;license:MIT ;(c) 2024 by 4am ; ; common routines used by graphic effects ; ; Note: launcher code can call these routines directly. However, ; graphic effects are assembled as separate targets and must call ; these routines indirectly via the vectors defined in constants.a, ; e.g. iBuildHGRTables instead of BuildHGRTables. ; ; Public functions: ; - WaitForKeyWithTimeout ; - BuildHGRTables ; - BuildHGRMirrorTables ; - BuildHGRMirrorCols ; - BuildDHGRMirrorCols ; - BuildHGRDitherMasks ; - BuildDHGRDitherMasks ; - BuildHGRSparseBitmasks1Bit ; - BuildDHGRSparseBitmasks1Bit ; - ReverseCoordinates1Bit ; - RippleCoordinates1Bit ; - RippleCoordinates1Bit2 ; - RippleCoordinates1Bit3 ; - RippleCoordinates1Bit4 ; - HGRPrecomputed1Bit ; - DHGRPrecomputed1Bit ; - BuildHGRSparseBitmasks2Bit ; - BuildDHGRSparseBitmasks2Bit ; - ReverseCoordinates2Bit ; - RippleCoordinates2Bit ; - HGRPrecomputed2Bit ; - DHGRPrecomputed2Bit ; - SetupPrecomputed3Bit ; - ReverseCoordinates3Bit ; - RippleCoordinates3Bit ; - HGRPrecomputed3Bit !source "src/fx/macros.hgr.a" !source "src/fx/macros.dhgr.a" !source "src/fx/macros.shr.a" !source "src/fx/macros.copybit.a" !source "src/fx/macros.misc.a" WaitForKeyWithTimeout ; in: A = timeout length (like standard $FCA8 wait routine) ; out: A clobbered (not always 0 if key is pressed, but also not the key pressed) ; X/Y preserved sec @wait1 pha @wait2 sbc #1 bne @wait2 pla bit KBD bmi @exit sbc #1 bne @wait1 @exit rts ; based on routine by John Brooks ; posted on comp.sys.apple2 on 2018-07-11 ; https://groups.google.com/d/msg/comp.sys.apple2/v2HOfHOmeNQ/zD76fJg_BAAJ BuildHGRTables ; out: populates tables at $0201 (hgrlo) and $0301 (hgrhi) ; A clobbered ; X=$C0 (important! some callers rely on this) ; Z=1 ; Y preserved ldx #0 - txa and #$F8 bpl + ora #5 + asl bpl + ora #5 + asl asl sta hgrlo, x txa and #7 rol asl hgrlo, x rol ora #$20 sta hgrhi, x inx cpx #$C0 bne - rts BuildHGRMirrorTables ldx #$C0 ldy #0 - tya and #$F8 bpl + ora #5 + asl bpl + ora #5 + asl asl sta hgrlomirror-1, x tya and #7 rol asl hgrlomirror-1, x rol ora #$20 sta hgr1himirror-1, x iny dex bne - rts BuildHGRMirrorCols ; in: none ; out: mirror_cols populated with lookup table to get $27-y for y in $00..$27 ; all registers and flags clobbered ldx #$27 ldy #$00 - tya sta mirror_cols, x iny dex bpl - rts BuildDHGRMirrorCols ; out: mirror_cols populated with lookup table to get $27-y for y in $00..$27 ; duplicated in both mainmem and auxmem ; X=0 ; Z=1 ldx #$28 ldy #$00 - tya sta mirror_cols-1, x sta $C005 sta mirror_cols-1, x sta $C004 iny dex bne - rts BuildHGRDitherMasks ldy #40 - lda #%10110011 sta dithermasks, y lda #%11100110 sta dithermasks+1, y lda #%11001100 sta dithermasks+2, y lda #%10011001 sta dithermasks+3, y dey dey dey dey bpl - ldy #43 lda #$FF - sta no_masks, y dey bpl - rts BuildDHGRDitherMasks ldy #40 - lda #%10011110 sta dithermasks, y lda #%11111000 sta dithermasks+1, y lda #%11100001 sta dithermasks+2, y lda #%10000111 sta dithermasks+3, y dey dey dey dey bpl - ldy #43 - lda #$FF sta no_masks, y dey bpl - sta $C005 ldy #40 - lda #%10001111 sta dithermasks, y lda #%10111100 sta dithermasks+1, y lda #%11110000 sta dithermasks+2, y lda #%11000011 sta dithermasks+3, y dey dey dey dey bpl - ldy #43 - lda #$FF sta no_masks, y dey bpl - sta $C004 rts BuildHGRSparseBitmasks1Bit lda #%10000001 sta copymasks1bit sta mirror_copymasks1bit+$C0 lda #%10000010 sta copymasks1bit+$20 sta mirror_copymasks1bit+$A0 lda #%10000100 sta copymasks1bit+$40 sta mirror_copymasks1bit+$80 lda #%10001000 sta copymasks1bit+$60 sta mirror_copymasks1bit+$60 lda #%10010000 sta copymasks1bit+$80 sta mirror_copymasks1bit+$40 lda #%10100000 sta copymasks1bit+$A0 sta mirror_copymasks1bit+$20 lda #%11000000 sta copymasks1bit+$C0 sta mirror_copymasks1bit rts BuildDHGRSparseBitmasks1Bit ; out: X=0 ldx #$00 txa - sta copymasks1bit, x sta $C005 sta copymasks1bit, x sta $C004 inx bne - ; X=0 lda #%00000111 sta copymasks1bit+$80 sta mirror_copymasks1bit+$40 lda #%00011000 sta copymasks1bit+$A0 sta mirror_copymasks1bit+$20 lda #%01100000 sta copymasks1bit+$C0 sta mirror_copymasks1bit sta $C005 lda #%10000011 sta copymasks1bit sta mirror_copymasks1bit+$C0 lda #%10001100 sta copymasks1bit+$20 sta mirror_copymasks1bit+$A0 lda #%10110000 sta copymasks1bit+$40 sta mirror_copymasks1bit+$80 lda #%11000000 sta copymasks1bit+$60 sta mirror_copymasks1bit+$60 sta $C004 rts ReverseCoordinates1Bit ldy #0 ; Coordinates1Bit sta $f1 lda #<(EndCoordinates1Bit - 2) sta $f2 lda #>(EndCoordinates1Bit - 2) sta $f3 clc !byte $24 - sec -- lda ($f0), y pha lda ($f2), y sta ($f0), y pla sta ($f2), y iny bcc - ldy #0 !byte $24 - clc inc $f0 bne + inc $f1 + lda $f1 eor #>(Coordinates1Bit + $1A40) bne + lda $f0 eor #<(Coordinates1Bit + $1A40) beq ++ + lda $f2 bne + dec $f3 + dec $f2 bcs - bcc -- ; always ++ rts !zone { RippleCoordinates1Bit4 lda #Coordinates1Bit sta $ed ldy #0 !byte $24 - sec lda ($ec), y pha lda $de, x sta ($ec), y pla sta $de, x inx iny bcc - dex dex dex dex bne -- dec $ee bne --- dec $ef bpl --- bmi exit ; always branches aslmod4 jsr aslmod aslmod3 jsr aslmod aslmod2 jsr aslmod aslmod cmp #$1A !if (>aslmod != >aslmod4) { !serious "aslmod entry points are not on the same page" } bcc + bne ++ cpy #$40 bcc + ++ iny + pha tya asl tay pla rol cmp #$34 bcc exit bne ++ cpy #$80 bcc exit ++ pha tya sbc #$80 tay pla sbc #$34 exit rts } !zone { HGRPrecomputed1Bit jsr BuildHGRTables jsr BuildHGRMirrorTables jsr BuildHGRMirrorCols jsr BuildHGRSparseBitmasks1Bit +COPY_TO_0 .start, .end jmp .InputLoop .start !pseudopc 0 { .Exit1Bit rts .InputLoop ldy #0 .input=*+1 ldx Coordinates1Bit ; first value: HGR row (only 0..95 will be in input array) bmi .Exit1Bit ; if > 127 then we're done +ROW_X_TO_BASE_ADDRESSES .src1, .src2, .dest1, .dest2 +ROW_X_TO_MIRROR_ADDRESSES .mirror_src1, .mirror_src2, .mirror_dest1, .mirror_dest2 inc .input lda (.input), y +HIGH_3_LOW_5 .input ; main 1x2 block in top-left quadrant .src1=*+1 lda $FDFD, y eor (<.dest1), y and copymasks1bit, x eor (<.dest1), y .dest1=*+1 sta $FDFD, y .src2=*+1 lda $FDFD, y eor (<.dest2), y and copymasks1bit, x eor (<.dest2), y .dest2=*+1 sta $FDFD, y ; corresponding 1x2 block in top-right quadrant (same row, opposite column) lda mirror_cols, y tay +COPY_BIT .src1, .dest1, mirror_copymasks1bit +COPY_BIT .src2, .dest2, mirror_copymasks1bit ; corresponding 1x2 block in bottom-right quadrant (opposite row, opposite column) .mirror_src1=*+1 lda $FDFD, y eor (<.mirror_dest1), y and mirror_copymasks1bit, x eor (<.mirror_dest1), y .mirror_dest1=*+1 sta $FDFD, y .mirror_src2=*+1 lda $FDFD, y eor (<.mirror_dest2), y and mirror_copymasks1bit, x eor (<.mirror_dest2), y .mirror_dest2=*+1 sta $FDFD, y ; corresponding 1x2 block in bottom-left quadrant (opposite row, original column) lda mirror_cols, y tay +COPY_BIT .mirror_src1, .mirror_dest1, copymasks1bit +COPY_BIT .mirror_src2, .mirror_dest2, copymasks1bit +INC_INPUT_AND_LOOP .input, .InputLoop } .end } !zone { DHGRPrecomputed1Bit jsr BuildDHGRSparseBitmasks1Bit jsr BuildHGRTables jsr BuildHGRMirrorTables jsr BuildDHGRMirrorCols +COPY_TO_0 .start, .end jmp .InputLoop .start !pseudopc 0 { .Exit1Bit rts .InputLoop ldy #0 .input=*+1 ldx Coordinates1Bit ; first value: HGR row (only 0..95 will be in input array) bmi .Exit1Bit ; if > 127 then we're done +ROW_X_TO_BASE_ADDRESSES .src1, .src2, .dest1, .dest2 +ROW_X_TO_MIRROR_ADDRESSES .mirror_src1, .mirror_src2, .mirror_dest1, .mirror_dest2 inc .input lda (.input), y +HIGH_3_LOW_5 .input sty <.y clc .bankloop lda copymasks1bit, x beq + sta <.copymask ; main 1x2 block in top-left quadrant .src1=*+1 lda $FDFD, y eor (<.dest1), y .copymask=*+1 and #$FD ; SMC eor (<.dest1), y .dest1=*+1 sta $FDFD, y .src2=*+1 lda $FDFD, y eor (<.dest2), y and <.copymask eor (<.dest2), y .dest2=*+1 sta $FDFD, y ; corresponding 1x2 block in bottom-left quadrant (opposite row, original column) +COPY_BIT_ZP .mirror_src1, .mirror_dest1, .copymask +COPY_BIT_ZP .mirror_src2, .mirror_dest2, .copymask + lda mirror_copymasks1bit, x ; exists at the same address in mainmem and auxmem but each is different beq + sta <.mirror_copymask ; corresponding 1x2 block in bottom-right quadrant (opposite row, opposite column) lda mirror_cols, y ; duplicated in mainmem and auxmem tay .mirror_src1=*+1 lda $FDFD, y eor (<.mirror_dest1), y .mirror_copymask=*+1 and #$FD ; SMC eor (<.mirror_dest1), y .mirror_dest1=*+1 sta $FDFD, y .mirror_src2=*+1 lda $FDFD, y eor (<.mirror_dest2), y and <.mirror_copymask eor (<.mirror_dest2), y .mirror_dest2=*+1 sta $FDFD, y ; corresponding 1x2 block in top-right quadrant (same row, opposite column) +COPY_BIT_ZP .src1, .dest1, .mirror_copymask +COPY_BIT_ZP .src2, .dest2, .mirror_copymask + bcs + sta $C003 sta $C005 .y=*+1 ldy #$FD sec bcs .bankloop + sta $C002 sta $C004 +INC_INPUT_AND_LOOP .input, .InputLoop } .end } BuildHGRSparseBitmasks2Bit lda #%10000011 sta copymasks2bit sta mirror_copymasks2bit+$E0 lda #%10001100 sta copymasks2bit+$20 sta mirror_copymasks2bit+$C0 lda #%10110000 sta copymasks2bit+$40 sta mirror_copymasks2bit+$A0 lda #%11000000 sta copymasks2bit+$60 sta mirror_copymasks2bit+$80 lda #%10000001 sta copymasks2bit+$80 sta mirror_copymasks2bit+$60 lda #%10000110 sta copymasks2bit+$A0 sta mirror_copymasks2bit+$40 lda #%10011000 sta copymasks2bit+$C0 sta mirror_copymasks2bit+$20 lda #%11100000 sta copymasks2bit+$E0 sta mirror_copymasks2bit rts BuildDHGRSparseBitmasks2Bit ldx #$00 txa - sta copymasks2bit, x sta $C005 sta copymasks2bit, x sta $C004 inx bne - lda #%10011111 sta copymasks2bit+$40 sta mirror_copymasks2bit+$A0 lda #%11100000 sta copymasks2bit+$60 sta mirror_copymasks2bit+$80 lda #%10000111 sta copymasks2bit+$C0 sta mirror_copymasks2bit+$20 lda #%11111000 sta copymasks2bit+$E0 sta mirror_copymasks2bit sta $C005 lda #%10001111 sta copymasks2bit sta mirror_copymasks2bit+$E0 lda #%11110000 sta copymasks2bit+$20 sta mirror_copymasks2bit+$C0 lda #%10000011 sta copymasks2bit+$80 sta mirror_copymasks2bit+$60 lda #%11111100 sta copymasks2bit+$A0 sta mirror_copymasks2bit+$40 sta $C004 rts ReverseCoordinates2Bit ldy #0 ; Coordinates2Bit sta $f1 lda #<(EndCoordinates2Bit - 2) sta $f2 lda #>(EndCoordinates2Bit - 2) sta $f3 ldx #$1E ; #$3C/2 clc !byte $24 - sec -- lda ($f0), y pha lda ($f2), y sta ($f0), y pla sta ($f2), y iny bcc - ldy #0 !byte $24 - clc inc $f0 bne + inc $f1 dex beq ++ + lda $f2 bne + dec $f3 + dec $f2 bcs - bcc -- ; always branches ++ rts RippleCoordinates2Bit ldy #0 ldx #$33 - lda @ptrtbl, x sta $c0, x dex bpl - lda #$9b sta $fe iny sty $ff ldx #6 - lda Coordinates2Bit + 1, x sta $7f, x lda Coordinates2Bit + 9, x sta $85, x lda Coordinates2Bit + 17, x sta $8b, x lda Coordinates2Bit + 65, x sta $9b, x dex bne - lda Coordinates2Bit + 28 sta $92 lda Coordinates2Bit + 29 sta $93 ldx #4 - lda Coordinates2Bit + 33, x sta $93, x lda Coordinates2Bit + 41, x sta $97, x lda Coordinates2Bit + 83, x sta $a1, x dex bne - ldx #2 - lda Coordinates2Bit + 125, x sta $a5, x lda Coordinates2Bit + 131, x sta $a7, x lda Coordinates2Bit + 139, x sta $a9, x lda Coordinates2Bit + 169, x sta $ab, x lda Coordinates2Bit + 237, x sta $ad, x lda Coordinates2Bit + 2193, x sta $af, x lda Coordinates2Bit + 6581, x sta $b1, x dex bne - --- ldx #$34 -- lda $be, x tay ora $bf, x beq + lda $bf, x jsr @aslmod sty $be, x sta $bf, x sty $fc clc adc #>Coordinates2Bit sta $fd ldy #0 !byte $24 - sec lda ($fc), y pha lda $7e, x sta ($fc), y pla sta $7e, x inx iny bcc - dex dex + dex dex bne -- ldy #1 lda $fe eor #<(411 - 2) beq + ldy #9 eor #<(411 - 2) xor <(411 - 136) bne ++ + - ldx @zerotbl, y sta $0, x sta $1, x dey bpl - ++ dec $fe bne --- dec $ff bpl --- bmi @exit ; always branches @aslmod jsr + + cmp #$1E bcc + iny + pha tya asl tay pla rol cmp #$3C bcc @exit sbc #$3C @exit rts @ptrtbl !word 2, 4, 6, 10, 12, 14, 18, 20 !word 22, 28, 34, 36, 42, 44, 66, 68 !word 70, 84, 86, 126, 132, 140, 170, 238 !word 2194, 6582 @zerotbl !byte $f0, $f2, $ca, $d2, $d8, $e0, $e2, $e6, $ea, $ee !zone { HGRPrecomputed2Bit jsr BuildHGRTables jsr BuildHGRMirrorCols jsr BuildHGRSparseBitmasks2Bit +COPY_TO_0 .start, .end jmp .InputLoop .start !pseudopc 0 { .Exit2Bit rts .InputLoop ldy #0 .input=*+1 ldx Coordinates2Bit ; first value: HGR row + 1 beq .Exit2Bit ; if 0 then we're done +ROW_X_TO_2BIT_BASE_ADDRESSES .src1, .src2, .dest1, .dest2 inc <.input lda (<.input), y +HIGH_3_LOW_5 .input ; main 2x2 block in left half .src1=*+1 lda $FDFD, y eor (<.dest1), y and copymasks2bit, x eor (<.dest1), y .dest1=*+1 sta $FDFD, y .src2=*+1 lda $FDFD, y eor (<.dest2), y and copymasks2bit, x eor (<.dest2), y .dest2=*+1 sta $FDFD, y ; corresponding 2x2 block in right half (same row, opposite column) lda mirror_cols, y tay +COPY_BIT .src1, .dest1, mirror_copymasks2bit +COPY_BIT .src2, .dest2, mirror_copymasks2bit inc <.input bne .InputLoop bit KBD bmi .Exit2Bit inc <.input+1 bne .InputLoop ; always branches } .end } !zone { DHGRPrecomputed2Bit jsr BuildDHGRSparseBitmasks2Bit jsr BuildHGRTables jsr BuildDHGRMirrorCols +COPY_TO_0 .start, .end jmp .InputLoop .start !pseudopc 0 { .Exit2Bit rts .InputLoop ldy #0 .input=*+1 ldx Coordinates2Bit ; first value: HGR row + 1 beq .Exit2Bit ; if 0 then we're done +ROW_X_TO_2BIT_BASE_ADDRESSES .src1, .src2, .dest1, .dest2 inc <.input lda (<.input), y +HIGH_3_LOW_5 .input ; main 2x2 block in left half clc - lda copymasks2bit, x beq + .src1=*+1 lda $FDFD, y eor (<.dest1), y and copymasks2bit, x eor (<.dest1), y .dest1=*+1 sta $FDFD, y .src2=*+1 lda $FDFD, y eor (<.dest2), y and copymasks2bit, x eor (<.dest2), y .dest2=*+1 sta $FDFD, y + bcs + sta $C003 sta $C005 sec bcs - + sta $C002 sta $C004 ; corresponding 2x2 block in right half (same row, opposite column) lda mirror_cols, y tay clc - lda mirror_copymasks2bit, x beq + +COPY_BIT .src1, .dest1, mirror_copymasks2bit +COPY_BIT .src2, .dest2, mirror_copymasks2bit + bcs + sta $C003 sta $C005 sec bcs - + sta $C002 sta $C004 inc <.input +LBNE .InputLoop bit $c000 bmi + inc <.input+1 jmp .InputLoop + rts } .end } SetupPrecomputed3Bit ; build regular HGR lookup tables, then split them jsr BuildHGRTables ldx #$BF ldy #$3F - lda hgrlo, x sta hgrlo3c, y sta hgrlo3c+$40, y lda hgrhi, x sta hgrhi3c, y sta hgrhi3c+$40, y dex lda hgrlo, x sta hgrlo3b, y sta hgrlo3b+$40, y lda hgrhi, x sta hgrhi3b, y sta hgrhi3b+$40, y dex lda hgrlo, x sta hgrlo3a, y sta hgrlo3a+$40, y lda hgrhi, x sta hgrhi3a, y sta hgrhi3a+$40, y dex dey bpl - ; build lookup table to get $20+y for y in $00..$07 ldx #$07 ldy #$27 - tya sta extra_cols-$20, y dey dex bpl - ; build sparse lookup tables for bitmasks lda #%10000011 sta copymasks3bit lda #%10001100 sta copymasks3bit+$20 lda #%10110000 sta copymasks3bit+$40 lda #%11000000 sta copymasks3bit+$60 lda #%10000001 sta copymasks3bit+$80 lda #%10000110 sta copymasks3bit+$A0 lda #%10011000 sta copymasks3bit+$C0 lda #%11100000 sta copymasks3bit+$E0 rts ReverseCoordinates3Bit ldy #0 ; Coordinates3Bit sta $f1 lda #<(EndCoordinates3Bit - 2) sta $f2 lda #>(EndCoordinates3Bit - 2) sta $f3 ldx #$28 ; #$50/2 clc !byte $24 - sec -- lda ($f0), y pha lda ($f2), y sta ($f0), y pla sta ($f2), y iny bcc - ldy #0 !byte $24 - clc inc $f0 bne + inc $f1 dex beq ++ + lda $f2 bne + dec $f3 + dec $f2 bcs - bcc -- ; always branches ++ rts RippleCoordinates3Bit ldx #$1B - lda @ripplezp, x sta $e0, x dex bpl - --- ldx #$0c -- ldy $ee, x lda $ef, x jsr @aslmod sty $ee, x sta $ef, x sty $ec clc adc #>Coordinates3Bit sta $ed ldy #0 !byte $24 - sec lda ($ec), y pha lda $de, x sta ($ec), y pla sta $de, x inx iny bcc - dex dex dex dex bne -- dec $ee bne --- dec $ef bpl --- bmi @exit ; always branches @aslmod jsr + + cmp #$28 bcc + iny + pha tya asl tay pla rol cmp #$50 bcc @exit sbc #$50 @exit rts @ripplezp !byte $1F,$F3,$20,$F3,$20,$14,$20,$D3 !byte $1E,$F3,$1F,$54,$00,$00,$AA,$06 !byte $02,$00,$04,$00,$06,$00,$0C,$00 !byte $16,$00,$1A,$00 !zone { HGRPrecomputed3Bit jsr SetupPrecomputed3Bit +COPY_TO_0 .start, .end jmp .InputLoop .start !pseudopc 0 { .Exit3Bit rts .InputLoop ldy #0 .input=*+1 lda Coordinates3Bit bmi .Exit3Bit ; if high bit is 1 then we're done cmp #$40 php tax +ROW_X_TO_3BIT_BASE_ADDRESSES .src1, .src2, .src3, .dest1, .dest2, .dest3 inc <.input lda (<.input), y and #%11100000 tax eor (<.input), y plp bcc + tay lda extra_cols, y + tay ; 2x3 block .src1=*+1 lda $FDFD, y eor (<.dest1), y and copymasks3bit, x eor (<.dest1), y .dest1=*+1 sta $FDFD, y .src2=*+1 lda $FDFD, y eor (<.dest2), y and copymasks3bit, x eor (<.dest2), y .dest2=*+1 sta $FDFD, y .src3=*+1 lda $FDFD, y eor (<.dest3), y and copymasks3bit, x eor (<.dest3), y .dest3=*+1 sta $FDFD, y inc <.input bne .InputLoop bit KBD bmi .Exit3Bit inc <.input+1 bne .InputLoop ; always branches } .end }