diff --git a/demos/outline2021/128b/Makefile b/demos/outline2021/128b/Makefile new file mode 100644 index 00000000..8e21f441 --- /dev/null +++ b/demos/outline2021/128b/Makefile @@ -0,0 +1,100 @@ +include ../../../Makefile.inc + +DOS33 = ../../../utils/dos33fs-utils/dos33 +TOKENIZE = ../../../utils/asoft_basic-utils/tokenize_asoft +LINKER_SCRIPTS = ../../../linker_scripts +EMPTY_DISK = ../../../empty_disk/empty.dsk + +all: xdraw.dsk xdraw128.dsk + +submit: xdraw128.zip + +xdraw128_submit.zip: XDRAW128 xdraw128.s file_id.diz xdraw128.dsk + mkdir -p outline2021_xdraw128 + cp XDRAW128 ./outline2021_xdraw128 + cp xdraw128.s ./outline2021_xdraw128 + cp file_id.diz ./outline2021_xdraw128 + cp xdraw128.dsk ./outline2021_xdraw128 + zip -r xdraw128.zip outline2021_xdraw128 + mkdir -p outline2021_xdraw128_submit + cp xdraw128_720p.mp4 ./outline2021_xdraw128_submit + cp xdraw128.zip ./outline2021_xdraw128_submit + zip -r xdraw128_submit.zip outline2021_xdraw128_submit + +xdraw.dsk: HELLO TINY_XDRAW TINY_COOL SPIN.BAS XDRAW128 + cp $(EMPTY_DISK) xdraw.dsk + $(DOS33) -y xdraw.dsk SAVE A HELLO + $(DOS33) -y xdraw.dsk SAVE A SPIN.BAS + $(DOS33) -y xdraw.dsk BSAVE -a 0xC00 TINY_XDRAW + $(DOS33) -y xdraw.dsk BSAVE -a 0xC00 TINY_COOL + $(DOS33) -y xdraw.dsk BSAVE -a 0xC00 XDRAW128 + +xdraw128.dsk: HELLO_COMPO XDRAW128 + cp $(EMPTY_DISK) xdraw128.dsk + $(DOS33) -y xdraw128.dsk SAVE A HELLO_COMPO HELLO + $(DOS33) -y xdraw128.dsk BSAVE -a 0xC00 XDRAW128 + $(DOS33) -y -x xdraw128.dsk SAVE T HELLO "*** ***" + $(DOS33) -y -x xdraw128.dsk SAVE T HELLO " *** ***" + $(DOS33) -y -x xdraw128.dsk SAVE T HELLO " *** ***" + $(DOS33) -y -x xdraw128.dsk SAVE T HELLO " ***" + $(DOS33) -y -x xdraw128.dsk SAVE T HELLO " *** ***" + $(DOS33) -y -x xdraw128.dsk SAVE T HELLO " *** ***" + $(DOS33) -y -x xdraw128.dsk SAVE T HELLO "*** ***" + $(DOS33) -y -x xdraw128.dsk SAVE I HELLO "XDRAW XTRAVAGANZA" + $(DOS33) -y -x xdraw128.dsk SAVE I HELLO "/----------------\\" + $(DOS33) -y -x xdraw128.dsk SAVE I HELLO " 128 BYTE DEMO" + $(DOS33) -y -x xdraw128.dsk SAVE I HELLO " OUTLINE 2021" + $(DOS33) -y -x xdraw128.dsk SAVE I HELLO "\================/" + $(DOS33) -y -x xdraw128.dsk SAVE I HELLO " DEATER / DSR" + + +### + +HELLO: hello.bas + $(TOKENIZE) < hello.bas > HELLO + +### + +HELLO_COMPO: hello_compo.bas + $(TOKENIZE) < hello_compo.bas > HELLO_COMPO + + +### + +SPIN.BAS: spin.bas + $(TOKENIZE) < spin.bas > SPIN.BAS + + +### + +TINY_XDRAW: tiny_xdraw.o + ld65 -o TINY_XDRAW tiny_xdraw.o -C $(LINKER_SCRIPTS)/apple2_c00.inc + +tiny_xdraw.o: tiny_xdraw.s + ca65 -o tiny_xdraw.o tiny_xdraw.s -l tiny_xdraw.lst + + +### + +TINY_COOL: tiny_cool.o +# ld65 -o TINY_COOL tiny_cool.o -C apple2_e7_zp.inc + ld65 -o TINY_COOL tiny_cool.o -C $(LINKER_SCRIPTS)/apple2_c00.inc + +tiny_cool.o: tiny_cool.s + ca65 -o tiny_cool.o tiny_cool.s -l tiny_cool.lst + +### + +XDRAW128: xdraw128.o + ld65 -o XDRAW128 xdraw128.o -C $(LINKER_SCRIPTS)/apple2_c00.inc + +xdraw128.o: xdraw128.s + ca65 -o xdraw128.o xdraw128.s -l xdraw128.lst + + + +### + +clean: + rm -f *~ *.o *.lst HELLO TINY_XDRAW TINY_COOL XDRAW128 + diff --git a/demos/outline2021/128b/apple2_e7_zp.inc b/demos/outline2021/128b/apple2_e7_zp.inc new file mode 100644 index 00000000..f615f797 --- /dev/null +++ b/demos/outline2021/128b/apple2_e7_zp.inc @@ -0,0 +1,12 @@ +MEMORY { + ZP: start = $E7, size = $90, type = rw; + RAM: start = $E7, size = $8E00, file = %O; +} + +SEGMENTS { +#CODE: load = RAM, type = ro; +#RODATA: load = RAM, type = ro; +#DATA: load = RAM, type = rw; +#BSS: load = RAM, type = bss, define = yes; +ZEROPAGE: load = ZP, type = ro; +} diff --git a/demos/outline2021/128b/file_id.diz b/demos/outline2021/128b/file_id.diz new file mode 100644 index 00000000..a725a8b9 --- /dev/null +++ b/demos/outline2021/128b/file_id.diz @@ -0,0 +1,5 @@ +Xdraw Xtravaganza +- +Hi-res Xdraw Pattern Megademo +128-byte Intro for Apple II, Outline 2021 +by Deater / dSr diff --git a/demos/outline2021/128b/hello.bas b/demos/outline2021/128b/hello.bas new file mode 100644 index 00000000..41cf3582 --- /dev/null +++ b/demos/outline2021/128b/hello.bas @@ -0,0 +1,3 @@ +5 HOME +20 PRINT CHR$(4)"CATALOG" + diff --git a/demos/outline2021/128b/spin.bas b/demos/outline2021/128b/spin.bas new file mode 100644 index 00000000..62e50ceb --- /dev/null +++ b/demos/outline2021/128b/spin.bas @@ -0,0 +1,9 @@ +1 POKE 768,64:POKE 232,0:POKE 233,3 +5 FOR I=0 TO 31:POKE 770+(I*2),4:POKE 771+(I*2),205+I +8 POKE 770+64+(I*2),4:POKE 771+64+(I*2),205+I:NEXT +10 HGR2:SCALE=1 +40 FOR I=1 TO 64 +45 ROT=I-1 +50 XDRAW I AT 140,96 +60 NEXT I +70 GOTO 40 diff --git a/demos/outline2021/128b/tiny_cool.s b/demos/outline2021/128b/tiny_cool.s new file mode 100644 index 00000000..3e835fcd --- /dev/null +++ b/demos/outline2021/128b/tiny_cool.s @@ -0,0 +1,59 @@ +; How does this work? +; seems mostly to be using $0004 - $FF04 as a shape table +; It's because we are passing ROT as the low byte of shape table + + +; zero page locations +HGR_SHAPE = $1A +A5H = $45 +XREG = $46 +YREG = $47 +HGR_SCALE = $E7 +HGR_ROTATION = $F9 +FRAME = $FC +XPOS = $FD +YPOS = $FF + +; ROM calls +HGR2 = $F3D8 +HPOSN = $F411 +XDRAW0 = $F65D ; shape table in Y:X +RESTORE = $FF3F + +;.zeropage + +tiny_tiny: + + jsr HGR2 ; Hi-res graphics, no text at bottom + ; Y=0, A=0 after this call + + lda #1 + sta HGR_SCALE + +tiny_loop: + ; setup X and Y co-ords + ldy #0 ; Y always 0 + ldx #140 + lda #96 + jsr HPOSN ; X= (y,x) Y=(a) + + ldx #$4 +rot_smc: + lda #0 ; ROT=0 + pha + + and #$1f + clc + adc #208 + tay ; ldy #>shape_table + + pla +zurg: + jsr XDRAW0 ; XDRAW 1 AT X,Y + ; Both A and X are 0 at exit + + inc rot_smc+1 + jmp tiny_loop + +; d0..ff +; 208 .. 255 = 48 diff --git a/demos/outline2021/128b/tiny_xdraw.s b/demos/outline2021/128b/tiny_xdraw.s new file mode 100644 index 00000000..dadab272 --- /dev/null +++ b/demos/outline2021/128b/tiny_xdraw.s @@ -0,0 +1,115 @@ +; Tiny Xdraw + +; repeatedly draws an image from an Apple II shape table + +; can arbitrarily point to any memory location as a source of these +; some look amazing but depend on random machine state +; to be deterministic you should probably stick to +; $E7-$F0 (the program itself) +; $D000-$FFFF (the ROMs) +; shapetables are a bit complicated to explain here, but they are a +; series of bytes ending with a $00 +; (note if you point to a zero, it will be interpreted as an +; action not an end) +; each byte specifies up to 3 actions, DRAW + UP DOWN LEFT RIGHT or +; NODRAW + UP DOWN LEFT RIGHT +; It is vector scaling with SCALE we hardcode to $20 and rotation +; which gets set to 0 after the first iteration, (which is +; why the first shape has arbitrary rotation and gets left) + +; we are xdrawing so it will XOR with the current pixels on the screen + + ; NUP=0 UP=4 zz yyy xxx , does xxx yyy zz + ; NRT=1 RT=5 + ; NDN=2 DN=6 + ; NLT=3 LT=7 + +; zero page locations +HGR_SHAPE = $1A +HGR_SHAPE2 = $1B +HGR_BITS = $1C +GBASL = $26 +GBASH = $27 +A5H = $45 +XREG = $46 +YREG = $47 + ; C0-CF should be clear + ; D0-DF?? D0-D5 = HGR scratch? +HGR_DX = $D0 ; HGLIN +HGR_DX2 = $D1 ; HGLIN +HGR_DY = $D2 ; HGLIN +HGR_QUADRANT = $D3 +HGR_E = $D4 +HGR_E2 = $D5 +HGR_X = $E0 +HGR_X2 = $E1 +HGR_Y = $E2 +HGR_COLOR = $E4 +HGR_HORIZ = $E5 +HGR_SCALE = $E7 +HGR_SHAPE_TABLE = $E8 +HGR_SHAPE_TABLE2= $E9 +HGR_COLLISIONS = $EA +HGR_ROTATION = $F9 +FRAME = $FC +XPOS = $FD +YPOS = $FF + +; ROM calls +HGR2 = $F3D8 +HGR = $F3E2 +HPOSN = $F411 +XDRAW0 = $F65D +XDRAW1 = $F661 +RESTORE = $FF3F + +;.zeropage +;.globalzp rot_smc + +tiny_xdraw: + + lda #$20 + sta HGR_SCALE ; can get rid of if load in zero page + ldx #0 + + jsr HGR2 ; Hi-res, full screen ; 3 + ; Y=0, A=0 after this call + + ; we load at $E7 which is HGR_SCALE, so HGR_SCALE gets + ; the value of the above JSR instruction ($20) + + + ; A and Y are 0 here. + ; X is left behind by the boot process? + + txa + jsr HPOSN ; set screen position to X= (y,x) Y=(a) + ; saves X,Y,A to zero page + ; after Y= orig X/7 + ; A and X are ?? +tiny_loop: + + ; values for shape table + ; Y X + ; 00 E7 = neat + ; 00 EB = OK + ; 00 EF = good + ; F0 01 = cool, let's go with it + + ldx #$01 ; point to bottom byte of shape address + ldy #$f0 ; point to top byte of shape address + + ; ROT in A + + ; this will be 0 2nd time through loop, arbitrary otherwise + lda #0 ; ROT=0 + jsr XDRAW0 ; XDRAW 1 AT X,Y + ; Both A and X are 0 at exit + ; Z flag set on exit + ; Y varies + + beq tiny_loop ; bra + + + + diff --git a/demos/outline2021/128b/xdraw128.s b/demos/outline2021/128b/xdraw128.s new file mode 100644 index 00000000..c805b1b6 --- /dev/null +++ b/demos/outline2021/128b/xdraw128.s @@ -0,0 +1,228 @@ +; XDRAW128 +; 128b intro for outline 2021 +; +; by Vince `deater` Weaver +; dSr + + +; things to try +; clear to "other" black would mean blue/orange instead of green/purple +; offset starting point by one to change color +; would love a DSR logo in the middle of the circle but that would +; cost ~20 bytes + + +; goal is 128 +; 142: first round +; 141: massive re-write for common XDRAW +; 137: shave a few bytes off on first half (no clc, note X is 0 after xdraw) +; 131: last part using common XDRAW +; 130: add center_y0 + +; zero page locations +HGR_SHAPE = $1A +HGR_SHAPE2 = $1B +HGR_BITS = $1C +GBASL = $26 +GBASH = $27 +A5H = $45 +XREG = $46 +YREG = $47 +HGR_SCALE = $E7 +HGR_ROT = $F9 +FRAME = $FC +XPOS = $FD +YPOS = $FF + +; ROM calls +HGR2 = $F3D8 +HCLR = $F3F2 ; clear to 0 +HCLR2 = $F3F4 ; clear to A +BKGND = $F3F6 ; clear to HGR_BITS +HPOSN = $F411 +XDRAW0 = $F65D ; shape table in Y:X +RESTORE = $FF3F + +xdraw128: + + ;===================== + ; setup graphics mode + ;===================== + + + jsr HGR2 ; Hi-res graphics, no text at bottom + ; Y=0, A=0 after this call + + + sta FRAME ; set frame to 0 + sta HGR_ROT ; rotation 0 + + ;====================== + ; do circle intro + ; put dSr logo in it? + ; that would be like 16 more bytes :( + ;====================== + +circle: + + lda #40 + sta HGR_SCALE + +circle_loop: + inc HGR_ROT + + jsr center + + ldx #shape_table + + jsr common_xdraw + + bne circle_loop ; we dec frame in common_xdraw + + + + ;=================================== + ; Lightning + ;=================================== + + + +lightning: + + ; in theory X is 0 here? + +; lda #1 +; sta HGR_SCALE + + inx + stx HGR_SCALE + + +lightning_loop: + + jsr center + + ldx #$4 ; we use $D004 - $F004 as shape tables + + inc HGR_ROT ; rotate + + lda HGR_ROT ; ROT value + ora #$d0 ; set to either $DX or $FX ??? + tay + + +; lda HGR_ROT ; ROT value +; and #$1f ; wrap to 32 +;; clc +; adc #208 ; $D0 +; ora #$d0 +; tay ; set high shape table to (ROT%32)+208 + + jsr common_xdraw + + bne lightning_loop + + + ;=================================== + ; Tiny Xdraw + ;=================================== + +tiny_xdraw: + + + + lda #$20 + sta HGR_SCALE ; can get rid of if load in zero page + + ; in theory X=0 on entry + +; ldx #0 + txa +; tay + jsr center_y0 ; start at co-ords 0,0 + + + + +tiny_loop: + ; F0 01 = cool, let's go with it + ldx #$01 ; point to bottom byte of shape address + ldy #$f0 ; point to top byte of shape address + + ; ROT should be 0? + + jsr common_xdraw + bne tiny_loop + + + + ;=================================== + ; More Xdraw + ;=================================== + +more_xdraw: + + +outer_more_loop: + + lda #64 + sta FRAME + +color_smc: + lda #$80 + jsr HCLR2 + +more_loop: + +more_smc: + ldx #$4d ; point to bottom byte of shape address + ldy #$d3 ; point to top byte of shape address + + jsr common_xdraw + bne more_loop + + lda color_smc+1 ; 3+2+3 + eor #$80 ; if zp =2+2+2+2 + sta color_smc+1 + + inc more_smc+1 ; move to next pattern + lda more_smc+1 + cmp #$60 + bcc outer_more_loop + + inc HGR_ROT ; increase rotation + + lda #$4d ; reset shapetable pointer + sta more_smc+1 + + bne outer_more_loop ; bra + + + + ; 9 + 9 if inlined (18) + ; 3 + 3 + 10 as function (16) +center: + ; setup X and Y co-ords + ldx #140 + lda #96 +center_y0: + ldy #0 ; Y always 0 + jsr HPOSN ; X= (y,x) Y=(a) + rts + + +common_xdraw: + + lda HGR_ROT ; rotation + jsr XDRAW0 ; XDRAW 1 AT X,Y + ; Both A and X are 0 at exit + ; Z flag set on exit + + dec FRAME + + rts + +shape_table: + .byte $3A,$DB,$0 ; shape data accidentally found at addr $0004 + diff --git a/demos/outline2021/demo/Makefile b/demos/outline2021/demo/Makefile new file mode 100644 index 00000000..28e27c16 --- /dev/null +++ b/demos/outline2021/demo/Makefile @@ -0,0 +1,64 @@ +include ../../../Makefile.inc + +DOS33 = ../../../utils/dos33fs-utils/dos33 +DOS33_RAW = ../../../utils/dos33fs-utils/dos33_raw +TOKENIZE = ../../../utils/asoft_basic-utils/tokenize_asoft +LINKER_SCRIPTS = ../../../linker_scripts +EMPTY_DISK = ../../../empty_disk + +all: outline2021.dsk + +#outline2021.dsk: HELLO QBOOT QLOAD OUTLINE +# cp $(EMPTY_DISK)/empty.dsk outline2021.dsk +# $(DOS33) -y outline2021.dsk SAVE A HELLO +# $(DOS33) -y outline2021.dsk BSAVE -a 0x6000 OUTLINE + +outline2021.dsk: QBOOT QLOAD OUTLINE + cp $(EMPTY_DISK)/empty.dsk outline2021.dsk + $(DOS33_RAW) outline2021.dsk 0 0 QBOOT 0 1 + $(DOS33_RAW) outline2021.dsk 0 2 QBOOT 1 1 + $(DOS33_RAW) outline2021.dsk 0 4 QBOOT 2 1 + $(DOS33_RAW) outline2021.dsk 1 0 QLOAD 0 14 + $(DOS33_RAW) outline2021.dsk 2 0 OUTLINE 0 80 + + + + +#### + +QLOAD: qload.o + ld65 -o QLOAD qload.o -C $(LINKER_SCRIPTS)/apple2_1600.inc + +qload.o: qload.s qboot.inc + ca65 -o qload.o qload.s -l qload.lst + +#### + +QBOOT: qboot_sector.o + ld65 -o QBOOT qboot_sector.o -C $(LINKER_SCRIPTS)/apple2_800.inc + +qboot_sector.o: qboot_sector.s qboot_stage2.s + ca65 -o qboot_sector.o qboot_sector.s -l qboot_sector.lst + + + +### + +#HELLO: hello.bas +# $(TOKENIZE) < hello.bas > HELLO + +### + +OUTLINE: outline.o + ld65 -o OUTLINE outline.o -C $(LINKER_SCRIPTS)/apple2_6000.inc + +outline.o: outline.s zp.inc \ + shimmer.s a2_inside.s drops.s wires.s \ + flying_dir.inc tfv_flying.s flying_mode7.s credits.s + ca65 -o outline.o outline.s -l outline.lst + +### + +clean: + rm -f *~ *.o *.lst HELLO OUTLINE + diff --git a/demos/outline2021/demo/NOTES b/demos/outline2021/demo/NOTES new file mode 100644 index 00000000..34f68250 --- /dev/null +++ b/demos/outline2021/demo/NOTES @@ -0,0 +1,8 @@ +Memory Usage: + +$200 +$1000-$10FF wires_lookup +$2000-$3FFF hires page 1 +$4000-$6000 hires page 2 (24k) +$6000-????? code +$B600-$BDFF multiply tables (2k) diff --git a/demos/outline2021/demo/a2_inside.s b/demos/outline2021/demo/a2_inside.s new file mode 100644 index 00000000..c9c428f3 --- /dev/null +++ b/demos/outline2021/demo/a2_inside.s @@ -0,0 +1,192 @@ +; Apple II self portrait using Boxes + + ;================================ + ; Clear screen and setup graphics + ;================================ +a2_inside: + + jsr SETGR ; set lo-res 40x40 mode + bit PAGE0 + bit LORES + bit FULLGR ; make it 40x48 + +draw_box_loop: + + ; get color/Y0 + jsr load_byte + tax ; Y0 is in X + + tya ; check for end + + bmi end + + + jsr load_byte ; Y1 + sta Y1 + + jsr load_byte ; X0 + sta X0 + + tya + lsr + lsr + sta COLOR + + + jsr load_byte ; X1 + sta H2 + + tya + and #$C0 + ora COLOR + + lsr + lsr + lsr + lsr + + jsr SETCOL + + +inner_loop: + + ;; HLINE Y,H2 at A + ;; X left alone, carry set on exit + ;; H2 left alone + ;; Y and A trashed + + ldy X0 + txa + jsr HLINE + + cpx Y1 + inx + bcc inner_loop + bcs draw_box_loop + + + ;========================= + ; draw the demo + ;========================= + ; screen is from (11,6) - (20,23) + ; so size is 9,17? +end: + + lda #128 + sta FRAME + + + ; pause a bit at beginning + jsr WAIT + +sier_loop: + + lda #100 ; Wait a bit, we're too fast + jsr WAIT + + inc FRAME ; increment frame + + ldx #17 ; YY + +sier_yloop: + + lda #9 ; XX + sta XX + +sier_xloop: + + txa ; get YY + clc + adc FRAME ; and add in FRAME + + and XX ; and it with XX + + bne black + lda FRAME ; color is based on frame + lsr ; only update every 16 lines? + lsr + lsr + lsr + bne not_zero ; but no color 0 (would be all black) + lda #3 ; how about purple instead + +not_zero: + + .byte $2C ; bit trick +black: + lda #$00 + + jsr SETCOL ; set top/bottom nibble same color + + lda XX ; offset XX to tiny screen + clc + adc #11 + tay ; put into Y + + txa ; offset YY to tiny screen + clc + adc #6 ; put into A + + jsr PLOT ; PLOT AT Y,A + + dec XX + bpl sier_xloop + + dex + bpl sier_yloop + + lda FRAME + bne sier_loop + rts + + ;========================= + ; load byte routine + ;========================= + +load_byte: + inc load_byte_smc+1 + bne load_byte_nowrap + inc load_byte_smc+2 + +load_byte_nowrap: + ; so no need to wrap +load_byte_smc: + lda box_data-1 + tay + and #$3f + rts + + + ; 4 6 6 6 6 +box_data: + .byte $00,$2F,$C0,$E7 + .byte $01,$2B,$0A,$9B + .byte $28,$29,$43,$D4 + .byte $24,$27,$43,$D6 + .byte $20,$23,$45,$D7 + .byte $1C,$1F,$48,$D8 + .byte $23,$26,$07,$8E + .byte $24,$27,$08,$92 + .byte $1F,$1F,$0D,$92 + .byte $2A,$2B,$43,$54 + .byte $2C,$2D,$46,$53 + .byte $2C,$2D,$14,$97 + .byte $08,$16,$1C,$9C + .byte $02,$1A,$49,$D8 + .byte $04,$18,$0A,$95 + .byte $06,$17,$0B,$14 + .byte $15,$29,$22,$A2 + .byte $13,$28,$22,$A4 + .byte $13,$14,$5C,$63 + .byte $15,$16,$5B,$61 + .byte $17,$2B,$59,$E1 + .byte $18,$20,$1A,$20 + .byte $22,$2A,$1A,$20 + .byte $1C,$1C,$5B,$60 + .byte $26,$26,$5B,$60 + .byte $1F,$20,$DF,$1F + .byte $29,$2A,$DF,$1F + .byte $19,$1E,$5D,$5E + .byte $23,$28,$5D,$5E + .byte $02,$03,$17,$D7 + .byte $FF diff --git a/demos/outline2021/demo/c00_scrn_offsets.s b/demos/outline2021/demo/c00_scrn_offsets.s new file mode 100644 index 00000000..02a5f7dc --- /dev/null +++ b/demos/outline2021/demo/c00_scrn_offsets.s @@ -0,0 +1,16 @@ +common_offsets_l: + .byte <$c00,<$c80,<$d00,<$d80,<$e00,<$e80,<$f00,<$f80 + .byte <$c28,<$ca8,<$d28,<$da8,<$e28,<$ea8,<$f28,<$fa8 + .byte <$c50,<$cd0,<$d50,<$dd0,<$e50,<$ed0,<$f50,<$fd0 + +scrn_c00_offsets_h: + .byte >$c00,>$c80,>$d00,>$d80,>$e00,>$e80,>$f00,>$f80 + .byte >$c28,>$ca8,>$d28,>$da8,>$e28,>$ea8,>$f28,>$fa8 + .byte >$c50,>$cd0,>$d50,>$dd0,>$e50,>$ed0,>$f50,>$fd0 + + +gr_400_offsets_h: + .byte >$400,>$480,>$500,>$580,>$600,>$680,>$700,>$780 + .byte >$428,>$4a8,>$528,>$5a8,>$628,>$6a8,>$728,>$7a8 + .byte >$450,>$4d0,>$550,>$5d0,>$650,>$6d0,>$750,>$7d0 + diff --git a/demos/outline2021/demo/credits.s b/demos/outline2021/demo/credits.s new file mode 100644 index 00000000..0db2b9cc --- /dev/null +++ b/demos/outline2021/demo/credits.s @@ -0,0 +1,504 @@ + +; Roughly based on "Entropy" by Dave McKellar of Toronto +; A Two-line BASIC program Found on Beagle Brother's Apple Mechanic Disk +; +; It is XORing vector squares across the screen. Randomly the size +; is changed while doing this. + + +; 24001 ROT=0:FOR I=1 TO 15: READ A,B: POKE A,B: NEXT: DATA +; 232,252,233,29,7676,1,7678,4,7679,0,7680,18,7681,63, +; 7682,36,7683,36,7684,45,7685,45,7686,54,7687,54,7688,63, +; 7689,0 +; 24002 FOR I=1 TO 99: HGR2: FOR E=.08 TO .15 STEP .01: +; FOR Y=4 to 189 STEP 6: FOR X=4 to 278 STEP 6: +; SCALE=(RND(1)E) THEN SCALE = 1 +; if E=.15 IF RND<38 +; If (RND(1)E) THEN SCALE = 1 + + + + +credits: + + jsr HGR ; Hi-res graphics, no text at bottom + ; Y=0, A=0 after this call + + jsr clear_bottom + + sta LOGO_OFFSET + sta FRAME + + lda #1 ; default is 1 + sta HGR_SCALE + + ldy #60 ; FOR Y=60 to 102 STEP 6 +logo_yloop: + + ldx #34 ; FOR X=32 to 248 STEP 6 +logo_xloop: + + stx XREG ; save X + sty YREG ; save Y + + ; setup X and Y co-ords + tya ; YPOS into A + ldy #0 ; XHIGH always 0 + + jsr HPOSN ; X= (y,x) Y=(a) + + ldx LOGO_OFFSET + asl desire_boxes,X + bcc skip_xdraw + + ldx #shape_table + lda #0 ; ROT=0 + + jsr XDRAW0 ; XDRAW 1 AT X,Y + ; Both A and X are 0 at exit + +skip_xdraw: + jsr RESTORE ; restore FLAGS/X/Y/A + ; we saved X/Y earlier + +logo_nextx: ; NEXT X + inc FRAME + lda FRAME + and #$7 + bne no_inc_offset + + inc LOGO_OFFSET + +no_inc_offset: + txa + clc ; 1 + adc #6 ; x+=6 ; 2 + tax + + ;cmp #248 + cmp #18 ; this is 272? + bne logo_xloop + +logo_nexty: + +; inc LOGO_OFFSET + + clc + tya + adc #6 ; y+=6 + tay + cpy #102 + bne logo_yloop ; if so, loop + + + ;======================================= + ; delay a few seconds + ;======================================= + + ldx #150 + jsr long_wait + + lda #0 + sta FRAME + sta FRAMEH + + ;====================================== + ; do the effect + ;====================================== + +eloop: + ldy #0 ; Y=0 to 180 STEP 6 +yloop: + + ldx #0 + stx XHIGH + + ldx #4 ; FOR X=4 to 278 STEP 6 +xloop: + + lda #1 ; default is 1 + sta HGR_SCALE + + cpy #54 + bcc random_scale ; blt + cpy #108 + bcc done_scale ; bge + + +random_scale: + jsr random16 + cmp #20 + bcs done_scale ; bge + + lda SEEDL + bmi done_scale + + inc HGR_SCALE + +done_scale: + + stx XREG ; save X + sty YREG ; save Y + + ; setup X and Y co-ords + tya ; YPOS into A + ldy XHIGH ; Y always 0 + ; XPOS already in X + + jsr HPOSN ; X= (y,x) Y=(a) + + + ldx #shape_table + lda #0 ; ROT=0 + + jsr XDRAW0 ; XDRAW 1 AT X,Y + ; Both A and X are 0 at exit + + jsr text_credits + + jsr RESTORE ; restore FLAGS/X/Y/A + ; we saved X/Y earlier + +nextx: ; NEXT X + + ; starting at 4 so hit 256, overflow to high bit + ; finally end at 280 which is 24? + + txa + clc ; 1 + adc #6 ; x+=6 ; 2 + tax + beq xwrap + cmp #24 + bne xloop + beq nexty +xwrap: + inc XHIGH + jmp xloop + +nexty: + ; carry always set if we get here? + + clc + tya + adc #6 ; y+=6 + tay + cpy #156 + bne yloop ; if so, loop + beq eloop + +shape_table: + .byte 18,63,36,36,45,45,54,54,63,0 ; shape data (a square) + + +; 280/6= 48 roughly ; 36*6 wide = 216, 280-216/2=32 to 248 +; 160/6 = 26 roughly ; 42 high, 160-42/2 = 118/2=59, say 60? + +;0123456701234567012345670123456701234567 +; # # +; # +; # #### #### # # ### #### +; ##### # # # # ## # # +; # # ###### ### # # ###### +; # # # # # # # +; #### #### #### # # #### +;0123456701234567012345670123456701234567 +desire_boxes: +.byte $02,$00,$04,$00,$00 +.byte $02,$00,$00,$00,$00 +.byte $02,$78,$F5,$73,$C0 +.byte $3E,$85,$05,$84,$20 +.byte $42,$FC,$E5,$07,$E0 +.byte $42,$80,$15,$04,$00 +.byte $3C,$79,$E5,$03,$C0 + + +;setup_text_credits: +; +; ; clear bottom of page2 and set split +; bit TEXTGR +; +; ldx #39 +; lda #' '|$80 +;clear_bottom_loop: +; sta $A50,X +; sta $AD0,X +; sta $B50,X +; sta $BD0,X +; dex +; bpl clear_bottom_loop + + ; set "done" + +; lda #DONE +; sta command + + ; clear time + +; lda #0 +; sta seconds +; sta ticks + +; rts + + + + + ;====================================== + ;====================================== + ; display credits + ;====================================== + ;====================================== + +text_credits: + + ; display music bars + + ; a bar + + lda A_VOLUME + lsr + lsr + sta draw_a_bar_left_loop+1 + lda #3 + sec + sbc draw_a_bar_left_loop+1 + sta draw_a_bar_right_loop+1 + + ldx #4 + lda #' '|$80 +draw_a_bar_left_loop: + cpx #$4 + bne skip_al_bar + eor #$80 +skip_al_bar: + sta $650,X ; A50 + dex + bpl draw_a_bar_left_loop + + ldx #4 + lda #' ' +draw_a_bar_right_loop: + cpx #$4 + bne skip_ar_bar + eor #$80 +skip_ar_bar: + sta $650+35,X ; A50 + dex + bpl draw_a_bar_right_loop + + ; b bar + + lda B_VOLUME + lsr + lsr + sta draw_b_bar_left_loop+1 + lda #3 + sec + sbc draw_b_bar_left_loop+1 + sta draw_b_bar_right_loop+1 + + ldx #4 + lda #' '|$80 +draw_b_bar_left_loop: + cpx #$4 + bne skip_bl_bar + eor #$80 +skip_bl_bar: + sta $6D0,X ; $AD0 + dex + bpl draw_b_bar_left_loop + + ldx #4 + lda #' ' +draw_b_bar_right_loop: + cpx #$4 + bne skip_br_bar + eor #$80 +skip_br_bar: + sta $6D0+35,X ; $AD0 + dex + bpl draw_b_bar_right_loop + + ; c + + lda C_VOLUME + lsr + lsr + sta draw_c_bar_left_loop+1 + lda #3 + sec + sbc draw_c_bar_left_loop+1 + sta draw_c_bar_right_loop+1 + ldx #4 + lda #' '|$80 +draw_c_bar_left_loop: + cpx #$4 + bne skip_cl_bar + eor #$80 +skip_cl_bar: + sta $750,X ; $B50 + dex + bpl draw_c_bar_left_loop + + ldx #4 + lda #' ' +draw_c_bar_right_loop: + cpx #$4 + bne skip_cr_bar + eor #$80 +skip_cr_bar: + sta $750+35,X ; $B50 + dex + bpl draw_c_bar_right_loop + + ;================ + ; update frames + ;================ + + inc FRAME + lda FRAME + cmp #100 + bne not_fifty + + lda #0 + sta FRAME + inc FRAMEH +not_fifty: + + ;================ + ; write credits + ;================ + +actual_credits: + lda FRAME + cmp #25 + bne done_credits + + lda FRAMEH + + ; increment on multiples of 4 seconds + + and #$7 + beq next_credit + bne done_credits + +next_credit: + + ;======================== + ; write the credits + +write_credits: + lda which_credit + cmp #7 + beq done_credits + + ldx #4 +outer_credit_loop: + + ; X is proper line + ; point to start of proper output line + + lda credits_address,X + sta credits_address_smc+1 + lda credits_address+1,X + sta credits_address_smc+2 + + ; load proper input location + + lda which_credit + asl + tay + + txa + asl + asl + asl ; *16 (already *2) + clc + adc credits_table,Y + sta write_credit_1_loop+1 + lda credits_table+1,Y + adc #0 + sta write_credit_1_loop+2 + + ldy #0 +write_credit_1_loop: + lda $dede,Y + ora #$80 +credits_address_smc: + sta $dede,Y + iny + cpy #16 + bne write_credit_1_loop + +done_credit1_loop: + dex + dex + bpl outer_credit_loop + + + inc which_credit + +done_credits: + rts + +credits_address: + .word $650+12 + .word $6d0+12 + .word $750+12 + +credits_table: + .word credits1 + .word credits2 + .word credits3 + .word credits4 + .word credits5 + .word credits6 + .word credits7 + + +credits1: + .byte " CODE: " + .byte " " + .byte " DEATER " + +credits2: + .byte " MUSIC: " + .byte " " + .byte " MAZE " + +credits3: + .byte " EFFECTS: " + .byte " J. WARWICK " + .byte " D. MCKELLAR " + +credits4: + .byte " MAGIC: " + .byte " QKUMBA " + .byte " 4 A.M. " + +credits5: + .byte " GREETS: " + .byte " FRENCH TOUCH " + .byte " IMPHOBIA " + +credits6: + .byte " GROUIK " + .byte " FENARINARSA " + .byte " WIZ21 " + +credits7: + .byte " APPLE ][ " + .byte " " + .byte " FOREVER " + +which_credit: + .byte $0 diff --git a/demos/outline2021/demo/decompress_fast_v2.s b/demos/outline2021/demo/decompress_fast_v2.s new file mode 100644 index 00000000..fb2f24ad --- /dev/null +++ b/demos/outline2021/demo/decompress_fast_v2.s @@ -0,0 +1,370 @@ +; note -- modified by Vince Weaver to assemble with ca65 +; in this case, A = page to decompress to +; getsrc_smc+1, getsrc_smc+2 is src location + +; ----------------------------------------------------------------------------- +; Decompress raw LZSA2 block. +; Create one with lzsa -r -f2 +; +; in: +; * LZSA_SRC_LO and LZSA_SRC_HI contain the compressed raw block address +; * LZSA_DST_LO and LZSA_DST_HI contain the destination buffer address +; +; out: +; * LZSA_DST_LO and LZSA_DST_HI contain the last decompressed byte address, +1 +; +; ----------------------------------------------------------------------------- +; Backward decompression is also supported, use lzsa -r -b -f2 +; To use it, also define BACKWARD_DECOMPRESS=1 before including this code! +; +; in: +; * LZSA_SRC_LO/LZSA_SRC_HI must contain the address of the last byte of compressed data +; * LZSA_DST_LO/LZSA_DST_HI must contain the address of the last byte of the destination buffer +; +; out: +; * LZSA_DST_LO/LZSA_DST_HI contain the last decompressed byte address, -1 +; +; ----------------------------------------------------------------------------- +; +; Copyright (C) 2019 Emmanuel Marty, Peter Ferrie +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +; ----------------------------------------------------------------------------- + +;NIBCOUNT = $FC ; zero-page location for temp offset + +decompress_lzsa2_fast: + + sta LZSA_DST_HI + + ldy #$00 + sty LZSA_DST_LO + sty NIBCOUNT + +decode_token: + jsr getsrc ; read token byte: XYZ|LL|MMM + pha ; preserve token on stack + + and #$18 ; isolate literals count (LL) + beq no_literals ; skip if no literals to copy + cmp #$18 ; LITERALS_RUN_LEN_V2? + bcc prepare_copy_literals ; if less, count is directly embedded in token + + jsr getnibble ; get extra literals length nibble + ; add nibble to len from token + adc #$02 ; (LITERALS_RUN_LEN_V2) minus carry + cmp #$12 ; LITERALS_RUN_LEN_V2 + 15 ? + bcc prepare_copy_literals_direct ; if less, literals count is complete + + jsr getsrc ; get extra byte of variable literals count + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + sbc #$EE ; overflow? + jmp prepare_copy_literals_direct + +prepare_copy_literals_large: + ; handle 16 bits literals count + ; literals count = directly these 16 bits + jsr getlargesrc ; grab low 8 bits in X, high 8 bits in A + tay ; put high 8 bits in Y + bcs prepare_copy_literals_high ; (*same as JMP PREPARE_COPY_LITERALS_HIGH but shorter) + +prepare_copy_literals: + lsr ; shift literals count into place + lsr + lsr + +prepare_copy_literals_direct: + tax + bcs prepare_copy_literals_large ; if so, literals count is large + +prepare_copy_literals_high: + txa + beq copy_literals + iny + +copy_literals: + jsr getput ; copy one byte of literals + dex + bne copy_literals + dey + bne copy_literals + +no_literals: + pla ; retrieve token from stack + pha ; preserve token again + asl + bcs repmatch_or_large_offset ; 1YZ: rep-match or 13/16 bit offset + + asl ; 0YZ: 5 or 9 bit offset + bcs offset_9_bit + + ; 00Z: 5 bit offset + + ldx #$FF ; set offset bits 15-8 to 1 + + jsr getcombinedbits ; rotate Z bit into bit 0, read nibble for bits 4-1 + ora #$E0 ; set bits 7-5 to 1 + bne got_offset_lo ; go store low byte of match offset and prepare match + +offset_9_bit: ; 01Z: 9 bit offset + ;;asl ; shift Z (offset bit 8) in place + rol + rol + and #$01 + eor #$FF ; set offset bits 15-9 to 1 + bne got_offset_hi ; go store high byte, read low byte of match offset and prepare match + ; (*same as JMP GOT_OFFSET_HI but shorter) + +repmatch_or_large_offset: + asl ; 13 bit offset? + bcs repmatch_or_16bit ; handle rep-match or 16-bit offset if not + + ; 10Z: 13 bit offset + + jsr getcombinedbits ; rotate Z bit into bit 8, read nibble for bits 12-9 + adc #$DE ; set bits 15-13 to 1 and substract 2 (to substract 512) + bne got_offset_hi ; go store high byte, read low byte of match offset and prepare match + ; (*same as JMP GOT_OFFSET_HI but shorter) + +repmatch_or_16bit: ; rep-match or 16 bit offset + ;;ASL ; XYZ=111? + bmi rep_match ; reuse previous offset if so (rep-match) + + ; 110: handle 16 bit offset + jsr getsrc ; grab high 8 bits +got_offset_hi: + tax + jsr getsrc ; grab low 8 bits +got_offset_lo: + sta OFFSLO ; store low byte of match offset + stx OFFSHI ; store high byte of match offset + +rep_match: +.ifdef BACKWARD_DECOMPRESS + + ; Backward decompression - substract match offset + + sec ; add dest + match offset + lda putdst+1 ; low 8 bits +OFFSLO = *+1 + sbc #$AA + sta copy_match_loop+1 ; store back reference address + lda putdst+2 +OFFSHI = *+1 + sbc #$AA ; high 8 bits + sta copy_match_loop+2 ; store high 8 bits of address + sec + +.else + + ; Forward decompression - add match offset + + clc ; add dest + match offset + lda putdst+1 ; low 8 bits +OFFSLO = *+1 + adc #$AA + sta copy_match_loop+1 ; store back reference address +OFFSHI = *+1 + lda #$AA ; high 8 bits + adc putdst+2 + sta copy_match_loop+2 ; store high 8 bits of address +.endif + + pla ; retrieve token from stack again + and #$07 ; isolate match len (MMM) + adc #$01 ; add MIN_MATCH_SIZE_V2 and carry + cmp #$09 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2? + bcc prepare_copy_match ; if less, length is directly embedded in token + + jsr getnibble ; get extra match length nibble + ; add nibble to len from token + adc #$08 ; (MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2) minus carry + cmp #$18 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15? + bcc prepare_copy_match ; if less, match length is complete + + jsr getsrc ; get extra byte of variable match length + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + sbc #$E8 ; overflow? + +prepare_copy_match: + tax + bcc prepare_copy_match_y ; if not, the match length is complete + beq decompression_done ; if EOD code, bail + + ; Handle 16 bits match length + jsr getlargesrc ; grab low 8 bits in X, high 8 bits in A + tay ; put high 8 bits in Y + +prepare_copy_match_y: + txa + beq copy_match_loop + iny + +copy_match_loop: + lda $AAAA ; get one byte of backreference + jsr putdst ; copy to destination + +.ifdef BACKWARD_DECOMPRESS + + ; Backward decompression -- put backreference bytes backward + + lda copy_match_loop+1 + beq getmatch_adj_hi +getmatch_done: + dec copy_match_loop+1 + +.else + + ; Forward decompression -- put backreference bytes forward + + inc copy_match_loop+1 + beq getmatch_adj_hi +getmatch_done: + +.endif + + dex + bne copy_match_loop + dey + bne copy_match_loop + jmp decode_token + +.ifdef BACKWARD_DECOMPRESS + +getmatch_adj_hi: + dec copy_match_loop+2 + jmp getmatch_done + +.else + +getmatch_adj_hi: + inc copy_match_loop+2 + jmp getmatch_done +.endif + +getcombinedbits: + eor #$80 + asl + php + + jsr getnibble ; get nibble into bits 0-3 (for offset bits 1-4) + plp ; merge Z bit as the carry bit (for offset bit 0) +combinedbitz: + rol ; nibble -> bits 1-4; carry(!Z bit) -> bit 0 ; carry cleared +decompression_done: + rts + +getnibble: +NIBBLES = *+1 + lda #$AA + lsr NIBCOUNT + bcc need_nibbles + and #$0F ; isolate low 4 bits of nibble + rts + +need_nibbles: + inc NIBCOUNT + jsr getsrc ; get 2 nibbles + sta NIBBLES + lsr + lsr + lsr + lsr + sec + rts + +.ifdef BACKWARD_DECOMPRESS + + ; Backward decompression -- get and put bytes backward + +getput: + jsr getsrc +putdst: +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + sta $AAAA + lda putdst+1 + beq putdst_adj_hi + dec putdst+1 + rts + +putdst_adj_hi: + dec putdst+2 + dec putdst+1 + rts + +getlargesrc: + jsr getsrc ; grab low 8 bits + tax ; move to X + ; fall through grab high 8 bits + +getsrc: +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + lda $AAAA + pha + lda getsrc+1 + beq getsrc_adj_hi + dec getsrc+1 + pla + rts + +getsrc_adj_hi: + dec getsrc+2 + dec getsrc+1 + pla + rts + +.else + + ; Forward decompression -- get and put bytes forward + +getput: + jsr getsrc +putdst: +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + sta $AAAA + inc putdst+1 + beq putdst_adj_hi + rts + +putdst_adj_hi: + inc putdst+2 + rts + +getlargesrc: + jsr getsrc ; grab low 8 bits + tax ; move to X + ; fall through grab high 8 bits + +getsrc: +getsrc_smc: +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + lda $AAAA + inc getsrc+1 + beq getsrc_adj_hi + rts + +getsrc_adj_hi: + inc getsrc+2 + rts +.endif + diff --git a/demos/outline2021/demo/drops.s b/demos/outline2021/demo/drops.s new file mode 100644 index 00000000..8a03dc73 --- /dev/null +++ b/demos/outline2021/demo/drops.s @@ -0,0 +1,207 @@ +; water drops + +; based roughly on +; https://github.com/seban-slt/Atari8BitBot/blob/master/ASM/water/water.m65 + +; for each pixel + +; C +; A V B +; D +; +; calculate color as NEW_V = (A+B+C+D)/2 - OLD_V +; then flip buffers + + +.if 0 + + +; zero page + +GBASH = $27 +MASK = $2E +COLOR = $30 +SEEDL = $4E + +FRAME = $F8 +XX = $F9 +DROPL = $FA +DROPH = $FB +BUF1L = $FC +BUF1H = $FD +BUF2L = $FE +BUF2H = $FF + +; soft switches +FULLGR = $C052 +LORES = $C056 ; Enable LORES graphics + + +; ROM routines +HGR = $F3E2 +HGR2 = $F3D8 +PLOT = $F800 ;; PLOT AT Y,A +PLOT1 = $F80E ;; PLOT at (GBASL),Y (need MASK to be $0f or $f0) + +.endif + + ;================================ + ; Clear screen and setup graphics + ;================================ +drops: + jsr HGR ; clear $2000-$4000 to zero + ; A is $00 after this + ; Y is $00 + + bit FULLGR ; full page + bit LORES ; switch to LORES + +drops_outer: + + ; in all but first loop X is $FF on arrival + +; inx + stx BUF1L + stx BUF2L + + ;================================= + ; handle new frame + ;================================= + + inc FRAME + lda FRAME + tay ; save frame in Y + + ; alternate $20/$28 in BUF1H/BUF2H + + and #$1 + asl + asl + asl ; A now 0 or 8 + + ora #$20 + sta BUF1H + eor #$8 + sta BUF2H + + ; check if we add new raindrop + + tya ; reload FRAME + and #$3 ; only drop every 4 frames + bne no_drop + + ; fake random number generator by reading ROM + + lda $E000,Y ; based on FRAME + + ; buffer is 40x48 = roughly 2k? + ; so random top bits = 0..7 + + sta DROPL + and #$7 + ora #$20 + sta DROPH + + lda #31 ; $1f value for drop + + tay ; cheat and draw drop at offset 31 to reuse value + + sta (DROPL),Y ; draw at offset 31 + iny + sta (DROPL),Y ; draw at offset 32 + + ldy #71 + sta (DROPL),Y ; draw at offset 71 (y+1) + iny + sta (DROPL),Y ; draw at offset 72 + +no_drop: + + + ldx #47 ; load 47 into YY + + + ;================================= + ; yloop + ;================================= + +drops_yloop: + + ; reset XX to 39 + + lda #39 ; XX + sta XX + + tay + txa ; YY into A + + ; plot 39,YY + jsr PLOT ; PLOT Y,A, setting up MASK and putting addr in GBASL/H + + + ;================================= + ; xloop + ;================================= + +drops_xloop: + + clc + ldy #1 + lda (BUF1L),Y + ldy #81 + adc (BUF1L),Y + ldy #40 + adc (BUF1L),Y + ldy #42 + adc (BUF1L),Y + lsr + dey + +; sec + sbc (BUF2L),Y + bpl done_calc + eor #$ff +done_calc: + sta (BUF2L),Y + + inc BUF1L + inc BUF2L + bne no_oflo + + inc BUF1H + inc BUF2H + +no_oflo: + + ; adjust color + + lsr + lsr + and #$3 + tay + lda colors,Y + sta COLOR + + ldy XX + jsr PLOT1 ; PLOT AT (GBASL),Y + + dec XX + bpl drops_xloop + + dex ; YY + bpl drops_yloop + +weird_outer: + + bmi drops_outer ; small enough now! + +colors: +.byte $22,$66,$77,$ff + +;colors: +;.byte $00,$22,$66,$EE,$77,$ff,$ff,$ff + +; 0 2 6 e 7 f f f +; 0000 0010 0110 1110 0111 1111 1111 1111 +; 0 1 2 3 4 5 6 7 + diff --git a/demos/outline2021/demo/fakepal.s b/demos/outline2021/demo/fakepal.s new file mode 100644 index 00000000..d6653b26 --- /dev/null +++ b/demos/outline2021/demo/fakepal.s @@ -0,0 +1,237 @@ +; A 123-byte Apple II Lo-res Fake Palette Rotation Demo +; The Apple II has no Palette rotation hardware, so we fake it + +; For Lovebyte 2021 + +; by Vince `deater` Weaver (vince@deater.net) / dSr +; with some help from qkumba + +; 151 -- original +; 137 -- optimize generation +; 136 -- align lookup table so we can index it easier +; 130 -- optimize indexing of lookup +; 126 -- run loops backaward +; 124 -- notice X already 0 before plot +; 131 -- use GBASCALC. much faster, but 7 bytes larger +; 129 -- run loop backwards +; 128 -- set color ourselves +; 127 -- overlap color lookup with sine table +; 119 -- forgot to comment out unused +; 121 -- make it use full screen (40x48) + +; 149 -- add page flipping +; 144 -- optimize a bit +; 141 -- smc DRAW_PAGE +; 139 -- from qkumba, remove php/plp +; 138 -- from qkumba, remove SAVEX +; 133 -- run from zero page +; 132 -- make lookup 8*sin+7 +; 131 -- re-arrange sine table +; 128 -- call into PLOT for MASK seting + +; urgh lovebyte wants 124 byte (counts header) + +; 127 -- base YY<<16 by adding smc, not by shifting +; 125 -- realize that the top byte wraps so no need to and +; 124 -- re-arrange code to make an CLC unnecessary +; 123 -- qkumba noticed we can use the $FF offset directly in page flip + + +; zero page +;GBASL = $26 +;GBASH = $27 +;MASK = $2E +;COLOR = $30 +;CTEMP = $68 +YY = $69 + +; soft-switches +;FULLGR = $C052 +;PAGE1 = $C054 + +; ROM routines +;PLOT1 = $F80E ;; PLOT at (GBASL),Y (need MASK to be $0f or $f0) +GBASCALC= $F847 ;; take Y-coord/2 in A, put address in GBASL/H ( a trashed, C clear) +;SETCOL = $F864 ;; COLOR=A*17 +;SETGR = $FB40 + + +;.zeropage +;.globalzp colorlookup,plot_lookup_smc,draw_page_smc,frame_smc,sinetable + + + ;================================ + ; Clear screen and setup graphics + ;================================ +plasma: + + jsr SETGR ; set lo-res 40x40 mode + bit FULLGR ; make it 40x48 + + + +; color = ( 8.0 + 8*sin(x) + 8.0 + 8*sin(y) )/2 +; becomes +; color = ( 16 + (sintable[xx&0xf]) + (sintable[yy&0xf])) / 2; + + ; we only create a 16x16 texture, which we pattern across 40x48 screen + + + ; I've tried re-optimizing this about 10 different ways + ; and it never ends up shorter + +create_lookup: + ldx #15 +create_yloop: + ldy #15 +create_xloop: + sec + lda sinetable,X + adc sinetable,Y ; 15+sin(x)+sin(y) + lsr +lookup_smc: + sta lookup ; always starts at $d00 + inc lookup_smc+1 + + dey + bpl create_xloop + + dex + bpl create_yloop + + ; X and Y both $FF + +create_lookup_done: + +forever_loop: + +cycle_colors: + + ; cycle colors + ; instead of advancing entire frame, do slightly slower route + ; instead now and just incrememnting the frame and doing the + ; adjustment at plot time. + + ; increment frame + + inc frame_smc+1 + + ; set/flip pages + ; we want to flip pages and then draw to the offscreen one + +flip_pages: + +; ldy #0 + +; iny ; y is $FF, make it 0 + + lda draw_page_smc+1 ; DRAW_PAGE + bne done_page + dey +done_page: +; ldx PAGE1,Y ; set display page to PAGE1 or PAGE2 + + ldx $BF56,Y ; PAGE1 - $FF + + eor #$4 ; flip draw page between $400/$800 + sta draw_page_smc+1 ; DRAW_PAGE + + + ; plot current frame + ; scan whole 40x48 screen and plot each point based on + ; lookup table colors +plot_frame: + + ldx #47 ; YY=47 (count backwards) +plot_yloop: + + txa ; get YY into A + pha ; save X for later + lsr ; call actually wants Ycoord/2 + + php ; save C flag for mask handling + + ; ugh can't use PLOT trick as it always will draw something + ; to PAGE1 even if we don't want to + + jsr GBASCALC ; point GBASL/H to address in (A is ycoord/2) + ; after, A is GBASL, C is clear + + lda GBASH ; adjust to be PAGE1/PAGE2 ($400 or $800) +draw_page_smc: + adc #0 + sta GBASH + + ; increment YY in top nibble of lookup for (yy<<16)+xx + ; clc from above, C always 0 + lda plot_lookup_smc+1 + adc #$10 ; no need to mask as it will oflo and be ignored + sta plot_lookup_smc+1 + + ;========== + + ldy #39 ; XX = 39 (countdown) + + ; sets MASK by calling into middle of PLOT routine + ; by Y being 39 draw in a spot that gets over-written + + plp + jsr $f806 + +plot_xloop: + + tya ; get XX & 0x0f + and #$f + tax + +plot_lookup_smc: + lda lookup,X ; load lookup, (YY*16)+XX + + clc +frame_smc: + adc #$00 ; add in frame + + and #$f + lsr ; we actually only have 8 colors + + tax + + lda colorlookup,X ; lookup color + + + sta COLOR ; each nibble should be same + + jsr PLOT1 ; plot at GBASL,Y (x co-ord goes in Y) + + dey + bpl plot_xloop + + pla ; restore YY + tax + dex + bpl plot_yloop + bmi forever_loop + +colorlookup: + +; blue +.byte $55,$22,$66,$77,$ff,$77,$55 ; ,$00 shared w sin table + + +sinetable: +; this is actually (8*sin(x))+7 +; re-arranged so starts with $00 for colorlookup overlap +.byte $00,$FF +HACK: ; use the $0200 here for (HACK),Y addressing? + ; in the end no way to get Y set properly +.byte $00,$02,$04 +.byte $07,$0A,$0C,$0E,$0F,$0E,$0C,$0A +.byte $07,$04,$02 + + +; make lookup happen at page boundary + +lookup = $200 + +;.org $200 +;lookup: diff --git a/demos/outline2021/demo/flying_dir.inc b/demos/outline2021/demo/flying_dir.inc new file mode 100644 index 00000000..e4e1a38e --- /dev/null +++ b/demos/outline2021/demo/flying_dir.inc @@ -0,0 +1,25 @@ +island_flying_directions: + .byte $2,$00 ; 2 frames, do nothing + .byte $1,'Z' ; start moving forward + .byte $10,$00 ; 16 frames, do nothing + .byte $3,'D' ; 3 frames, turn right + .byte $1,'Z' ; move faster + .byte $8,$00 ; 8 frames, do nothing + .byte $2,'D' ; 2 frames, turn left + .byte $8,$00 ; 8 frames, do nothing + .byte $3,'A' ; 3 frames, turn left + .byte $1,'Z' ; speedup + .byte $8,$00 ; 8 frames, do nothing + .byte $6,'S' ; 6 frames down + .byte $6,$00 ; 6 frames do nothing + .byte $3,'A' ; 3 frames left + .byte $3,'D' ; 3 frames right + .byte $2,$00 ; 2 frames nothing + .byte $1,'D' ; 1 frame right + .byte $2,$00 ; 2 frames nothing + .byte $8,'D' ; 8 frame right + .byte $1,'Z' ; 8 frames up + .byte $6,'W' ; 2 speedup + .byte $a,$00 ; 10 nothing + .byte $3,'S' ; 3 down + .byte $1,'Q' ; quit diff --git a/demos/outline2021/demo/flying_mode7.s b/demos/outline2021/demo/flying_mode7.s new file mode 100644 index 00000000..0db5a6d4 --- /dev/null +++ b/demos/outline2021/demo/flying_mode7.s @@ -0,0 +1,685 @@ +;=========================== +; Draw the Mode7 Background +;=========================== + +; opening screen, original code +; $2d070 cycles = 184,432 = 5.4 fps +; $2da70 cycles (added in 2 cycle cpx) a00 = 2560, yes, 32*40=1280 +; $2aec3 cycles (update inner loop) = 175,811 = 5.7 fps +; $29af5 cycles (move things around) = 170,741 = 5.85 fps +; $29988 cycles (save another cycle) = 170,393 = 5.86 fps +; $29968 cycles (align lookup tables) +; $28FA8 cycles (remove clc) = 167,848 = 5.95 fps + +; TODO: +; could save cycle running inner X loop backwards, but would +; have to redo a lot of the math to start at right of screen +; could save avg of 2 cycles on inner X loop if we have special +; case odd vs even lines + + +; flying_loop -> check_done 2040 - 214f 2E +; +; check_done -> draw_background 214f - 219b 2E +; +; draw_background -> check_over_water 219b - 219e 298ad +; +; check_over_water -> no_splash 219e - 21d0 5b +; +; no_splash -> done_flying_loop 21d0 - 229b aa4 + + + + +draw_background_mode7: + + ; setup initial odd/even color mask + lda #$f0 ; 2 + sta COLOR_MASK ; 3 + + ; start Y at 8 (below horizon line) + lda #8 ; 2 + sta SCREEN_Y ; 3 + ;============= + ; 10 + +screeny_loop: + and #$fe ; be sure SCREEN_Y used later is even ; 2 + tay ; put in Y for lookup table later ; 2 + + lda COLOR_MASK ; flip mask for odd/even row plotting ; 3 + eor #$ff ; 2 + sta COLOR_MASK ; 3 + sta mask_label+1 ; setup self-modifying code ; 4 + + bpl odd_branch ; smc for even/odd line ; 2nt/3 + lda #$1d ; ora abs,X opcode is $1d ; 2 + bne ok_branch ; bra ; 3 +odd_branch: + lda #$2c ; bit is $2c ; 2 +ok_branch: + sta innersmc1 ; actually update ora/bit ; 4 + ;============ + ; ?27 + +setup_gr_addr: + lda gr_offsets,Y ; lookup low-res memory row address ; 4 + sta innersmc1+1 ; smc low addr ; 4 + sta innersmc2+1 ; smc low addr ; 4 + + lda gr_offsets+1,Y ; load high part of address ; 4 + clc ; clear carry for add ; 2 + adc DRAW_PAGE ; add in draw page offset ; 3 + sta innersmc1+2 ; smc high addr ; 4 + sta innersmc2+2 ; smc high addr ; 4 + + ;============= + ; 29 + +calc_horizontal_scale: + + ; Calculate the horizontal scale using a lookup table + + ; horizontal_scale.i *ALWAYS* = 0 + + ; unsigned char horizontal_lookup[7][32]; + ;horizontal_scale.f= + ; horizontal_lookup[space_z.i&0xf][(screen_y-8)/2]; + ; horizontal_lookup[(space_z<<5)+(screen_y-8)] + + + clc ; 2 + lda SCREEN_Y ; 3 +spacez_shifted: + adc #0 ; self-modify, loads (spacez<<5)-8 ; 2 + tay ; 2 + lda horizontal_lookup,Y ; 4 + sta NUM1L ; HORIZ_SCALE_F is input to next mul ; 3 + ;============ + ; 16 + +; mul2 + ; calculate the distance of the line we are drawing + ; fixed_mul(&horizontal_scale,&scale,&distance); + lda #0 ; HORIZ_SCALE_I is always zero ; 2 + sta NUM1H ; 3 + ; NUM1L was set to HORIZ_SCALE_F previously ; + lda #CONST_SCALE_I ; SCALE_I ; 2 + sta NUM2H ; 3 + lda #CONST_SCALE_F ; SCALE_F ; 2 + sta NUM2L ; 3 + sec ; don't reuse previous settings ; 2 + jsr multiply ; 6 + stx DISTANCE_I ; 2 + sta DISTANCE_F ; 2 + ;========== + ; 27 + + ; calculate the dx and dy to add to points in space + ; we add to the starting values on each row to get the next + ; space values + + ; dx.i=fixed_sin[(angle+8)&0xf].i // -sin() + lda ANGLE ; 3 + clc ; 2 + adc #8 ; 2 + and #$f ; 2 + asl ; 2 + tay ; 2 + lda fixed_sin,Y ; load integer half ; 4 + sta NUM2H ; use as source in upcomnig mul ; 3 + + + ; dx.f=fixed_sin[(angle+8)&0xf].f; // -sin() + iny ; point to float half ; 2 + lda fixed_sin,Y ; load it from lookup table ; 4 + sta NUM2L ; use as source in upcoming mul ; 3 + ;========== + ; 29 + +;mul3 + ; fixed_mul(&dx,&horizontal_scale,&dx); + + ; DX_I:DX_F already set in NUM2H:NUM2L + clc ; reuse HORIZ_SCALE in NUM1 ; 2 + jsr multiply ; 6 + stx DX_I ; 3 + sta DX_F ; 3 + ;========== + ; 14 + + ; dy.i=fixed_sin[(angle+4)&0xf].i; // cos() + + lda ANGLE ; 3 + clc ; 2 + adc #4 ; 2 + and #$f ; 2 + asl ; 2 + tay ; 2 + lda fixed_sin,Y ; load integer half ; 4 + sta NUM2H ; use as source in upcoming mul ; 3 + + ; dy.f=fixed_sin[(angle+4)&0xf].f; // cos() + + iny ; point to float half ; 2 + lda fixed_sin,Y ; load from lookup table ; 4 + sta NUM2L ; use as source in upcoming mul ; 3 + ;========== + ; 29 +;mul4 + ; fixed_mul(&dy,&horizontal_scale,&dy); + + ; DY_I:DY_F already in NUM2H:NUM2L + clc ; reuse horiz_scale in num1 ; 2 + jsr multiply ; 6 + stx DY_I ; 3 + sta DY_F ; 3 + ;========== + ; 14 + + ;================================= + ; calculate the starting position + ;================================= + + ; fixed_add(&distance,&factor,&space_x); + clc ; fixed_add(&distance,&factor,&space_y); ; 2 + lda DISTANCE_F ; 3 + adc FACTOR_F ; 3 + sta SPACEY_F ; 3 + sta SPACEX_F ; 3 + + lda DISTANCE_I ; 3 + adc FACTOR_I ; 3 + sta SPACEY_I ; 3 + sta SPACEX_I ; 3 + ;========== + ; 26 + + + ; temp.i=fixed_sin[(angle+4)&0xf].i; // cos() + + lda ANGLE ; 3 + clc ; 2 + adc #4 ; 2 + and #$f ; 2 + asl ; 2 + tay ; 2 + lda fixed_sin,Y ; 4 + sta NUM2H ; store as source for next mul ; 3 + + + ; temp.f=fixed_sin[(angle+4)&0xf].f; // cos() + iny ; 2 + lda fixed_sin,Y ; 4 + sta NUM2L ; store as source for next mul ; 3 + ;========== + ; 29 + +; mul5 + ; fixed_mul(&space_x,&temp,&space_x); + lda SPACEX_I ; 3 + sta NUM1H ; 3 + lda SPACEX_F ; 3 + sta NUM1L ; 3 + ; NUM2H:NUM2L already set above + sec ; don't reuse previous NUM1 ; 2 + jsr multiply ; 6 + ; SPACEX_I in X ; + ; SPACEX_F in A ; + ;========== + ; 20 + + ; fixed_add(&space_x,&cx,&space_x); + clc ; 2 + ; SPACEX_F still in A ; + adc CX_F ; 3 + sta SPACEX_F ; 3 + txa ; SPACEX_I was in X ; 2 + adc CX_I ; 3 + sta SPACEX_I ; 3 + ;=========== + ; 16 + + + ; temp.i=fixed_sin[angle&0xf].i; // sin() + lda ANGLE ; 3 + and #$f ; 2 + asl ; 2 + tay ; 2 + lda fixed_sin,Y ; 4 + sta NUM2H ; store for next mul ; 3 + + ; fixed_temp.f=fixed_sin[angle&0xf].f; // sin() + iny ; 2 + lda fixed_sin,Y ; 4 + sta NUM2L ; store for next mul ; 3 + ;========== + ; 25 + +;mul6 + ; fixed_mul(&space_y,&fixed_temp,&space_y); + lda SPACEY_I ; 3 + sta NUM1H ; 3 + lda SPACEY_F ; 3 + sta NUM1L ; 3 + ; NUM2H:NUM2L already set + sec ; don't reuse previous num1 ; 2 + jsr multiply ; 6 + ; SPACEY_I in X ; + ; SPACEY_F in A ; + ;========== + ; 20 + + ; fixed_add(&space_y,&cy,&space_y); + clc ; 2 + ; SPACEY_F in A + adc CY_F ; 3 + sta SPACEY_F ; 3 + txa ; SPACEY_I in X ; 2 + adc CY_I ; 3 + sta SPACEY_I ; 3 + ;========== + ; 16 +; mul7 + ; fixed_mul(&lowres_half,&dx,&temp); + lda #CONST_LOWRES_HALF_I ; 2 + sta NUM1H ; 3 + lda #CONST_LOWRES_HALF_F ; 2 + sta NUM1L ; 3 + lda DX_I ; 3 + sta NUM2H ; 3 + sta dxi_label+1 ; for self modify ; 4 + lda DX_F ; 3 + sta dxf_label+1 ; for self modify ; 4 + sta NUM2L ; 3 + sec ; don't reuse previous num1 ; 2 + jsr multiply ; 6 + ; TEMP_I in X ; + ; TEMP_F in A ; + ;========== + ; 38 + + + ; fixed_add(&space_x,&temp,&space_x); + clc ; 2 + ; TEMP_F in A + adc SPACEX_F ; 3 + sta SPACEX_F ; 3 + txa ; TEMP_I in X ; 2 + adc SPACEX_I ; 3 + sta SPACEX_I ; 3 + ;========== + ; 16 + +;mul8 + ; fixed_mul(&fixed_temp,&dy,&fixed_temp); + lda DY_I ; 3 + sta NUM2H ; 3 + sta dyi_label+1 ; for self modify ; 4 + lda DY_F ; 3 + sta NUM2L ; 3 + sta dyf_label+1 ; for self modify ; 4 + clc ; reuse CONST_LOWRES_HALF from last time ; 2 + jsr multiply ; 6 + ; TEMP_I in X + ; TEMP_F in A + ;========== + ; 28 + + ; fixed_add(&space_y,&temp,&space_y); + clc ; 2 + ; TEMP_F in A + adc SPACEY_F ; 3 + sta SPACEY_F ; 3 + + txa ; TEMP_I in X ; 2 + adc SPACEY_I ; 3 + sta SPACEY_I ; 3 + + ;========== + ; 16 + + + ldx #0 ; was SCREEN_X ; 2 + ;========== + ; 2 + ;=================================================== + ; SCREEN_X LOOP!!!! + ; every cycle in here counts for 32*40=1280 cycles + ;=================================================== +screenx_loop: + + +nomatch: + ;==================================== + ; do a full lookup, takes much longer + ; used to be a separate function but we inlined it here + + ;==================== + ; lookup_map + ;==================== + ; finds value in space_x.i,space_y.i + ; returns color in A + ; CLOBBERS: A,Y + + ; island is 8x8 + ; map is 64x64 but anything not island is ocean + + lda SPACEX_I ; 3 + sta spacex_label+1 ; self modifying code, LAST_SPACEX_I ; 4 + and #CONST_MAP_MASK_X ; wrap at 64 ; 2 + sta SPACEX_I ; store i patch out ; 3 + tay ; copy to Y for later ; 2 + + lda SPACEY_I ; 3 + sta spacey_label+1 ; self modifying code, LAST_SPACEY_I ; 4 + and #CONST_MAP_MASK_Y ; wrap to 64x64 grid ; 2 + sta SPACEY_I ; 3 + + asl ; 2 + asl ; 2 + asl ; multiply by 8 ; 2 + clc ; 2 + adc SPACEX_I ; add in X value ; 3 + ; only valid if x<8 and y<8 + + ;============ + ; 37 + + ; SPACEX_I is n y + cpy #$8 ; 2 + bcs ocean_color ; bge 8 ; 2nt/3 + ldy SPACEY_I ; 3 + cpy #$8 ; 2 + bcs ocean_color ; bge 8 ; 2nt/3 + ;============= + ; ?? + + ;============== + ; lookup island + ; A is spacey<<3+spacex +island_color: + tay ; 2 + lda flying_map,Y ; load from array ; 4 + jmp update_cache ; 3 + ;============ + ; 11 + + ;============= + ; lookup ocean + ; A is spacey<<3+spacex +ocean_color: + and #$1f ; 2 + tay ; 2 + lda water_map,Y ; the color of the sea ; 4 + ;=========== + ; 8 + + ; store cached value + ; note: this does seem faster than storing in zero page +update_cache: + sta map_color_label+1 ; self-modifying ; 4 + + ;=========== + ; 4 +match: + +mask_label: + ; color is in A + + ; this is f0 or 0f depending on odd/even row + and #0 ; COLOR_MASK (self modifying) ; 2 + + ; this is ora or bit depending on odd/even +innersmc1: + ora $400,X ; we're odd, or the bottom in ; 4 +innersmc2: + sta $400,X ; plot double height pixel ; 5 + + ;============ + ; 11 + + ;=================================== + ; incremement column, see if done + + + inx ; increment SCREEN_X ; 2 + cpx #40 ; 2 + beq done_screenx_loop ; branch until we've done 40 ; 2nt/3 + ;============= + ; 6/7 + + ;======================================= + ; advance to the next position in space + + ; fixed_add(&space_x,&dx,&space_x); + + ; state of carry here? cpx #40 should always be less than so cc? + +; clc ; 2 + lda SPACEX_F ; 3 +dxf_label: + adc #0 ; self modifying, is DX_F ; 2 + sta SPACEX_F ; 3 + + lda SPACEX_I ; 3 +dxi_label: + adc #0 ; self modifying, is DX_I ; 2 + sta SPACEX_I ; 3 + tay ; save for later ; 2 + ;========== + ; 20 + + ; fixed_add(&space_y,&dy,&space_y); + + clc ; 2 + lda SPACEY_F ; 3 +dyf_label: + adc #0 ; self modifyig, is DY_F ; 2 + sta SPACEY_F ; 3 + lda SPACEY_I ; 3 +dyi_label: + adc #0 ; self mofidying is DY_I ; 2 + sta SPACEY_I ; 3 + ;============ + ; 18 + + ; cache color and return if same as last time + ; SPACEY_I is in A from above +spacey_label: + cmp #0 ; self modify, LAST_SPACEY_I ; 2 + bne nomatch ; 2nt/3 + + ; SPACEX_I is in Y +spacex_label: + cpy #0 ; self modify, LAST_SPACEX_I ; 2 + bne nomatch ; 2nt/3 +map_color_label: + lda #0 ; self modify, LAST_MAP_COLOR ; 2 + jmp match ; 3 + ;============ + ; ?? + + ;======================== + ; get here at end of loop + ;========================= + +done_screenx_loop: + inc SCREEN_Y ; 5 + lda SCREEN_Y ; 3 + cmp #40 ; LOWRES height ; 2 + beq done_screeny ; 2nt/3 + jmp screeny_loop ; too far to branch ; 3 + ;============= + ; 15 +done_screeny: + rts ; 6 + + + + + + + + + + + + + ;==================== + ; lookup_map + ;==================== + ; finds value in space_x.i,space_y.i + ; returns color in A + ; CLOBBERS: A,Y + ; this is used to check if above water or grass + ; the high-performance per-pixel version has been inlined +lookup_map: + lda SPACEX_I ; 3 + and #CONST_MAP_MASK_X ; 2 + sta SPACEX_I ; 3 + tay ; 2 + + lda SPACEY_I ; 3 + and #CONST_MAP_MASK_Y ; wrap to 64x64 grid ; 2 + sta SPACEY_I ; 3 + + asl ; 2 + asl ; 2 + asl ; multiply by 8 ; 2 + clc ; 2 + adc SPACEX_I ; add in X value ; 3 + ; only valid if x<8 and y<8 + + ; SPACEX_I is in y + cpy #$8 ; 2 + ;============ + ; 31 + + bcs ocean_color_outline ; bgt 8 ;^2nt/3 + ldy SPACEY_I ; 3 + cpy #$8 ; 2 + bcs ocean_color_outline ; bgt 8 ; 2nt/3 + + tay ; 2 + lda flying_map,Y ; load from array ; 4 + + bcc update_cache_outline ; 3 + +ocean_color_outline: + and #$1f ; 2 + tay ; 2 + lda water_map,Y ; the color of the sea ; 4 + +update_cache_outline: + rts ; 6 + + + + ;====================================== + ; draw sky + ;====================================== + ; Only draw sky if necessary + ; (at start, or if we have switched to text, we never overwrite it) +draw_sky: + ; 6 + ; Draw Sky on both pages + ; lines 0..6 + + + lda #COLOR_BOTH_MEDIUMBLUE ; MEDIUMBLUE color ; 2 + ldx #39 + +sky_loop: ; draw line across screen + sta $400,X + sta $480,X + sta $500,X + sta $800,X + sta $880,X + sta $900,X + + dex + bpl sky_loop + + ; Draw Hazy Horizon + + lda #$56 ; Horizon is blue/grey ; 2 + ldx #39 +horizon_loop: ; draw line across screen + sta $580,X + sta $980,X + + dex + bpl horizon_loop + + rts + + + +;horizontal_lookup_20: +; .byte $0C,$0A,$09,$08,$07,$06,$05,$05,$04,$04,$04,$04,$03,$03,$03,$03 +; .byte $26,$20,$1B,$18,$15,$13,$11,$10,$0E,$0D,$0C,$0C,$0B,$0A,$0A,$09 +; .byte $40,$35,$2D,$28,$23,$20,$1D,$1A,$18,$16,$15,$14,$12,$11,$10,$10 +; .byte $59,$4A,$40,$38,$31,$2C,$28,$25,$22,$20,$1D,$1C,$1A,$18,$17,$16 +; .byte $73,$60,$52,$48,$40,$39,$34,$30,$2C,$29,$26,$24,$21,$20,$1E,$1C +; .byte $8C,$75,$64,$58,$4E,$46,$40,$3A,$36,$32,$2E,$2C,$29,$27,$25,$23 +; .byte $A6,$8A,$76,$68,$5C,$53,$4B,$45,$40,$3B,$37,$34,$30,$2E,$2B,$29 + + ; we can guarantee 4 cycle indexed reads if we page-align this + ; it's 16*14 bytes +.align 256 +horizontal_lookup: + .byte $0C,$0B,$0A,$09,$09,$08,$08,$07,$07,$06,$06,$06,$05,$05,$05,$05 + .byte $04,$04,$04,$04,$04,$04,$04,$03,$03,$03,$03,$03,$03,$03,$03,$03 + .byte $26,$22,$20,$1D,$1B,$19,$18,$16,$15,$14,$13,$12,$11,$10,$10,$0F + .byte $0E,$0E,$0D,$0D,$0C,$0C,$0C,$0B,$0B,$0A,$0A,$0A,$0A,$09,$09,$09 + .byte $40,$3A,$35,$31,$2D,$2A,$28,$25,$23,$21,$20,$1E,$1D,$1B,$1A,$19 + .byte $18,$17,$16,$16,$15,$14,$14,$13,$12,$12,$11,$11,$10,$10,$10,$0F + .byte $59,$51,$4A,$44,$40,$3B,$38,$34,$31,$2F,$2C,$2A,$28,$26,$25,$23 + .byte $22,$21,$20,$1E,$1D,$1C,$1C,$1B,$1A,$19,$18,$18,$17,$16,$16,$15 + .byte $73,$68,$60,$58,$52,$4C,$48,$43,$40,$3C,$39,$36,$34,$32,$30,$2E + .byte $2C,$2A,$29,$27,$26,$25,$24,$22,$21,$20,$20,$1F,$1E,$1D,$1C,$1C + .byte $8C,$80,$75,$6C,$64,$5D,$58,$52,$4E,$4A,$46,$43,$40,$3D,$3A,$38 + .byte $36,$34,$32,$30,$2E,$2D,$2C,$2A,$29,$28,$27,$26,$25,$24,$23,$22 + .byte $A6,$97,$8A,$80,$76,$6E,$68,$61,$5C,$57,$53,$4F,$4B,$48,$45,$42 + .byte $40,$3D,$3B,$39,$37,$35,$34,$32,$30,$2F,$2E,$2C,$2B,$2A,$29,$28 + +; 8.8 fixed point +; should we store as two arrays, one I one F? +; 32 bytes +fixed_sin: + .byte $00,$00 ; 0.000000=00.00 + .byte $00,$61 ; 0.382683=00.61 + .byte $00,$b5 ; 0.707107=00.b5 + .byte $00,$ec ; 0.923880=00.ec + .byte $01,$00 ; 1.000000=01.00 + .byte $00,$ec ; 0.923880=00.ec + .byte $00,$b5 ; 0.707107=00.b5 + .byte $00,$61 ; 0.382683=00.61 + .byte $00,$00 ; 0.000000=00.00 + .byte $ff,$9f ; -0.382683=ff.9f + .byte $ff,$4b ; -0.707107=ff.4b + .byte $ff,$14 ; -0.923880=ff.14 + .byte $ff,$00 ; -1.000000=ff.00 + .byte $ff,$14 ; -0.923880=ff.14 + .byte $ff,$4b ; -0.707107=ff.4b + .byte $ff,$9f ; -0.382683=ff.9f + +; 32 bytes +fixed_sin_scale: + .byte $00,$00 + .byte $00,$0c + .byte $00,$16 + .byte $00,$1d + .byte $00,$20 + .byte $00,$1d + .byte $00,$16 + .byte $00,$0c + .byte $00,$00 + .byte $ff,$f4 + .byte $ff,$ea + .byte $ff,$e3 + .byte $ff,$e0 + .byte $ff,$e3 + .byte $ff,$ea + .byte $ff,$f4 diff --git a/demos/outline2021/demo/flying_sprites.inc b/demos/outline2021/demo/flying_sprites.inc new file mode 100644 index 00000000..3c68fe72 --- /dev/null +++ b/demos/outline2021/demo/flying_sprites.inc @@ -0,0 +1,58 @@ +;================ +; Ship Sprites +;================ + +splash_forward: + .byte $7,$2 + .byte $00,$ee,$00,$00,$00,$ee,$00 + .byte $ee,$00,$00,$00,$00,$00,$ee + +splash_right: + .byte $7,$2 + .byte $00,$00,$00,$00,$00,$ee,$00 + .byte $00,$00,$00,$00,$00,$00,$ee + + +splash_left: + .byte $7,$2 + .byte $00,$ee,$00,$00,$00,$00,$00 + .byte $ee,$00,$00,$00,$00,$00,$00 + +shadow_forward: + .byte $3,$2 + .byte $00,$aa,$00 + .byte $a0,$aa,$a0 + +shadow_right: + .byte $3,$2 + .byte $a0,$00,$aa + .byte $00,$0a,$a0 + +shadow_left: + .byte $3,$2 + .byte $aa,$00,$a0 + .byte $a0,$0a,$00 + +ship_forward: + .byte $9,$5 + .byte $00,$00,$00,$00,$ff,$00,$00,$00,$00 + .byte $00,$00,$00,$66,$ff,$66,$00,$00,$00 + .byte $00,$00,$70,$2f,$12,$2f,$70,$00,$00 + .byte $f0,$f7,$f7,$f2,$d9,$f2,$f7,$f7,$f0 + .byte $00,$00,$00,$00,$0d,$00,$00,$00,$00 + +ship_right: + .byte $9,$5 + .byte $00,$00,$00,$00,$00,$60,$60,$f0,$00 + .byte $00,$f0,$70,$70,$f6,$f6,$6f,$66,$00 + .byte $00,$07,$ff,$2f,$12,$27,$f6,$00,$00 + .byte $00,$00,$00,$dd,$d9,$f2,$77,$00,$00 + .byte $00,$00,$00,$00,$00,$0f,$ff,$70,$00 + +ship_left: + .byte $9,$5 + .byte $00,$f0,$60,$60,$00,$00,$00,$00,$00 + .byte $00,$66,$6f,$f6,$f6,$70,$70,$f0,$00 + .byte $00,$00,$f6,$27,$12,$2f,$ff,$07,$00 + .byte $00,$00,$77,$f2,$d9,$dd,$00,$00,$00 + .byte $00,$70,$ff,$0f,$00,$00,$00,$00,$00 diff --git a/demos/outline2021/demo/gr_fast_clear.s b/demos/outline2021/demo/gr_fast_clear.s new file mode 100644 index 00000000..a86eb105 --- /dev/null +++ b/demos/outline2021/demo/gr_fast_clear.s @@ -0,0 +1,207 @@ +clear_screens: + ;=================================== + ; Clear top/bottom of page 0 + ;=================================== + + lda DRAW_PAGE + pha + + lda #$0 + sta DRAW_PAGE + jsr clear_top + jsr clear_bottom + + ;=================================== + ; Clear top/bottom of page 1 + ;=================================== + + lda #$4 + sta DRAW_PAGE + jsr clear_top + jsr clear_bottom + + pla + sta DRAW_PAGE + + rts + + + + + ;========================================================= + ; clear_top + ;========================================================= + ; clear DRAW_PAGE + ; original = 14,558 cycles(?) 15ms, 70Hz + ; OPTIMIZED MAX (page0,48rows): 45*120+4+6 = 5410 = 5.4ms 185Hz + ; (pageX,40rows): 50*120+4+6 = 6010 = 6.0ms 166Hz + ; 50*120+4+6+37 = 6055 = 6.0ms 166Hz +clear_top: + lda #0 ; 2 +clear_top_a: + sta COLOR ; 3 + clc ; 2 + lda DRAW_PAGE ; 3 + + adc #4 ; 2 + sta __ctf+2 ; 3 + sta __ctf+5 ; 3 + adc #1 ; 2 + sta __ctf+8 ; 3 + sta __ctf+11 ; 3 + adc #1 ; 2 + sta __ctf2+2 ; 3 + sta __ctf2+5 ; 3 + adc #1 ; 2 + sta __ctf2+8 ; 3 + sta __ctf2+11 ; 3 + + + ldy #120 ; 2 + lda COLOR ; 3 +clear_top_fast_loop: +__ctf: + sta $400,Y ; 5 + sta $480,Y ; 5 + sta $500,Y ; 5 + sta $580,Y ; 5 + + cpy #80 ; 2 + bpl no_draw_bottom ; 2nt/3 +__ctf2: + sta $600,Y ; 5 + sta $680,Y ; 5 + sta $700,Y ; 5 + sta $780,Y ; 5 +no_draw_bottom: + + dey ; 2 + bpl clear_top_fast_loop ; 2nt/3 + + rts ; 6 + + + + + ;========================================================= + ; clear_bottom + ;========================================================= + ; clear bottom of draw page + +clear_bottom: + clc ; 2 + lda DRAW_PAGE ; 3 + + adc #6 ; 2 + sta __cbf2+2 ; 3 + sta __cbf2+5 ; 3 + adc #1 ; 2 + sta __cbf2+8 ; 3 + sta __cbf2+11 ; 3 + + + ldy #120 ; 2 + lda #$a0 ; Normal Space ; 2 +clear_bottom_fast_loop: +__cbf2: + sta $600,Y ; 5 + sta $680,Y ; 5 + sta $700,Y ; 5 + sta $780,Y ; 5 + + dey ; 2 + cpy #80 ; 2 + bpl clear_bottom_fast_loop ; 2nt/3 + + rts ; 6 + + +;clear_screens_notext: + ;=================================== + ; Clear top/bottom of page 0 + ;=================================== + +; lda #$0 +; sta DRAW_PAGE +; jsr clear_all + + ;=================================== + ; Clear top/bottom of page 1 + ;=================================== + +; lda #$4 +; sta DRAW_PAGE +; jsr clear_all + +; rts + + +clear_bottoms: + + lda DRAW_PAGE + pha + + ;=================================== + ; Clear bottom of page 0 + ;=================================== + + lda #$0 + sta DRAW_PAGE + jsr clear_bottom + + ;=================================== + ; Clear bottom of page 1 + ;=================================== + + lda #$4 + sta DRAW_PAGE + jsr clear_bottom + + pla + sta DRAW_PAGE + + rts + + + ;========================================================= + ; clear_all + ;========================================================= + ; clear 48 rows + +clear_all: + clc ; 2 + lda DRAW_PAGE ; 3 + + adc #4 ; 2 + sta __caf+2 ; 3 + sta __caf+5 ; 3 + adc #1 ; 2 + sta __caf+8 ; 3 + sta __caf+11 ; 3 + adc #1 ; 2 + sta __caf2+2 ; 3 + sta __caf2+5 ; 3 + adc #1 ; 2 + sta __caf2+8 ; 3 + sta __caf2+11 ; 3 + + + ldy #120 ; 2 +clear_all_color: + lda #' '|$80 ; 2 +clear_all_fast_loop: +__caf: + sta $400,Y ; 5 + sta $480,Y ; 5 + sta $500,Y ; 5 + sta $580,Y ; 5 +__caf2: + sta $600,Y ; 5 + sta $680,Y ; 5 + sta $700,Y ; 5 + sta $780,Y ; 5 + + dey ; 2 + bpl clear_all_fast_loop ; 2nt/3 + + rts ; 6 diff --git a/demos/outline2021/demo/gr_offsets.s b/demos/outline2021/demo/gr_offsets.s new file mode 100644 index 00000000..d3af91f7 --- /dev/null +++ b/demos/outline2021/demo/gr_offsets.s @@ -0,0 +1,5 @@ +gr_offsets: + .word $400,$480,$500,$580,$600,$680,$700,$780 + .word $428,$4a8,$528,$5a8,$628,$6a8,$728,$7a8 + .word $450,$4d0,$550,$5d0,$650,$6d0,$750,$7d0 + diff --git a/demos/outline2021/demo/gr_pageflip.s b/demos/outline2021/demo/gr_pageflip.s new file mode 100644 index 00000000..f199f5be --- /dev/null +++ b/demos/outline2021/demo/gr_pageflip.s @@ -0,0 +1,24 @@ + ;========== + ; page_flip + ;========== + +page_flip: + lda DISP_PAGE ; 3 + beq page_flip_show_1 ; 2nt/3 +page_flip_show_0: + bit PAGE0 ; 4 + lda #4 ; 2 + sta DRAW_PAGE ; DRAW_PAGE=1 ; 3 + lda #0 ; 2 + sta DISP_PAGE ; DISP_PAGE=0 ; 3 + rts ; 6 +page_flip_show_1: + bit PAGE1 ; 4 + sta DRAW_PAGE ; DRAW_PAGE=0 ; 3 + lda #1 ; 2 + sta DISP_PAGE ; DISP_PAGE=1 ; 3 + rts ; 6 + ;==================== + ; DISP_PAGE=0 26 + ; DISP_PAGE=1 24 + diff --git a/demos/outline2021/demo/gr_putsprite.s b/demos/outline2021/demo/gr_putsprite.s new file mode 100644 index 00000000..6aecf782 --- /dev/null +++ b/demos/outline2021/demo/gr_putsprite.s @@ -0,0 +1,101 @@ + ;============================================= + ; put_sprite + ;============================================= + ; Sprite to display in INH,INL + ; Location is XPOS,YPOS + ; Note, only works if YPOS is multiple of two? + +put_sprite: + + ldy #0 ; byte 0 is xsize ; 2 + lda (INL),Y ; 5 + sta CH ; xsize is in CH ; 3 + iny ; 2 + + lda (INL),Y ; byte 1 is ysize ; 5 + sta CV ; ysize is in CV ; 3 + iny ; 2 + + lda YPOS ; make a copy of ypos ; 3 + sta TEMPY ; as we modify it ; 3 + ;=========== + ; 28 +put_sprite_loop: + sty TEMP ; save sprite pointer ; 3 + + ldy TEMPY ; 3 + lda gr_offsets,Y ; lookup low-res memory address ; 5 + clc ; 2 + adc XPOS ; add in xpos ; 3 + sta OUTL ; store out low byte of addy ; 3 + lda gr_offsets+1,Y ; look up high byte ; 5 + adc DRAW_PAGE ; ; 3 + sta OUTH ; and store it out ; 3 + ldy TEMP ; restore sprite pointer ; 3 + + ; OUTH:OUTL now points at right place + + ldx CH ; load xsize into x ; 3 + ;=========== + ; 36 +put_sprite_pixel: + lda (INL),Y ; get sprite colors ; 5 + iny ; increment sprite pointer ; 2 + + sty TEMP ; save sprite pointer ; 3 + ldy #$0 ; 2 + + ; check if completely transparent + ; if so, skip + + cmp #$0 ; if all zero, transparent ; 2 + beq put_sprite_done_draw ; don't draw it ; 2nt/3 + ; FIXME: use BIT? ;============== + ; 17 + + sta COLOR ; save color for later ; 3 + + ; check if top pixel transparent + + and #$f0 ; check if top nibble zero ; 2 + bne put_sprite_bottom ; if not skip ahead ; 2nt/3 + + lda #$f0 ; setup mask ; 2 + sta MASK ; 3 + bmi put_sprite_mask ; 2nt/3 + +put_sprite_bottom: + lda COLOR ; re-load color ; 3 + and #$0f ; check if bottom nibble zero ; 2 + bne put_sprite_all ; if not, skip ahead ; 2nt/3 + lda #$0f ; 2 + sta MASK ; setup mask ; 3 + +put_sprite_mask: + lda (OUTL),Y ; get color at output ; 5 + and MASK ; mask off unneeded part ; 3 + ora COLOR ; or the color in ; 3 + sta (OUTL),Y ; store it back ; 5 + + jmp put_sprite_done_draw ; we are done ; 3 + +put_sprite_all: + lda COLOR ; load color ; 3 + sta (OUTL),Y ; and write it out ; 5 + + +put_sprite_done_draw: + + ldy TEMP ; restore sprite pointer ; 3 + + inc OUTL ; increment output pointer ; 5 + dex ; decrement x counter ; 2 + bne put_sprite_pixel ; if not done, keep looping ; 2nt/3 + + inc TEMPY ; each line has two y vars ; 5 + inc TEMPY ; 5 + dec CV ; decemenet total y count ; 5 + bne put_sprite_loop ; loop if not done ; 2nt/3 + + rts ; return ; 6 + diff --git a/demos/outline2021/demo/hardware.inc b/demos/outline2021/demo/hardware.inc new file mode 100644 index 00000000..6e5d846b --- /dev/null +++ b/demos/outline2021/demo/hardware.inc @@ -0,0 +1,95 @@ +;; HARDWARE LOCATIONS + +KEYPRESS = $C000 +KEYRESET = $C010 + +;; SOFT SWITCHES +CLR80COL = $C000 ; PAGE0/PAGE1 normal +SET80COL = $C001 ; PAGE0/PAGE1 switches PAGE0 in Aux instead +EIGHTYCOLOFF = $C00C +EIGHTYCOLON = $C00D +SPEAKER = $C030 +SET_GR = $C050 +SET_TEXT = $C051 +FULLGR = $C052 +TEXTGR = $C053 +PAGE0 = $C054 +PAGE1 = $C055 +LORES = $C056 ; Enable LORES graphics +HIRES = $C057 ; Enable HIRES graphics +AN3 = $C05E ; Annunciator 3 + +PADDLE_BUTTON0 = $C061 +PADDL0 = $C064 +PTRIG = $C070 + +;; BASIC ROM ROUTINES + +NORMAL = $F273 +HGR2 = $F3D8 +HGR = $F3E2 +HCLR = $F3F2 +HPOSN = $F411 +XDRAW0 = $F65D + +;; MONITOR ROUTINES + +PLOT = $F800 ; PLOT AT Y,A +PLOT1 = $F80E ; PLOT at (GBASL),Y (need MASK to be $0f or $f0) +HLINE = $F819 ; HLINE Y,$2C at A +VLINE = $F828 ; VLINE A,$2D at Y +CLRSCR = $F832 ; Clear low-res screen +CLRTOP = $F836 ; clear only top of low-res screen +SETGR = $FB40 ; GR +SETCOL = $F864 ; COLOR=A +TEXT = $FB36 +TABV = $FB5B ; VTAB to A +BELL = $FBDD ; ring the bell +BASCALC = $FBC1 ; +VTAB = $FC22 ; VTAB to CV +HOME = $FC58 ; Clear the text screen +WAIT = $FCA8 ; delay 1/2(26+27A+5A^2) us +CROUT1 = $FD8B +SETINV = $FE80 ; INVERSE +SETNORM = $FE84 ; NORMAL +COUT = $FDED ; output A to screen +COUT1 = $FDF0 ; output A to screen +RESTORE = $FF3F + + + + + + +COLOR_BLACK = 0 +COLOR_RED = 1 +COLOR_DARKBLUE = 2 +COLOR_PURPLE = 3 +COLOR_DARKGREEN = 4 +COLOR_GREY = 5 +COLOR_MEDIUMBLUE = 6 +COLOR_LIGHTBLUE = 7 +COLOR_BROWN = 8 +COLOR_ORANGE = 9 +COLOR_GREY2 = 10 +COLOR_PINK = 11 +COLOR_LIGHTGREEN = 12 +COLOR_YELLOW = 13 +COLOR_AQUA = 14 +COLOR_WHITE = 15 + +COLOR_BOTH_BLACK = $00 +COLOR_BOTH_RED = $11 +COLOR_BOTH_DARKBLUE = $22 +COLOR_BOTH_DARKGREEN = $44 +COLOR_BOTH_GREY = $55 +COLOR_BOTH_MEDIUMBLUE = $66 +COLOR_BOTH_LIGHTBLUE = $77 +COLOR_BOTH_BROWN = $88 +COLOR_BOTH_ORANGE = $99 +COLOR_BOTH_PINK = $BB +COLOR_BOTH_LIGHTGREEN = $CC +COLOR_BOTH_YELLOW = $DD +COLOR_BOTH_AQUA = $EE +COLOR_BOTH_WHITE = $FF + diff --git a/demos/outline2021/demo/interrupt_handler.s b/demos/outline2021/demo/interrupt_handler.s new file mode 100644 index 00000000..2a766283 --- /dev/null +++ b/demos/outline2021/demo/interrupt_handler.s @@ -0,0 +1,69 @@ + ;================================ + ;================================ + ; mockingboard interrupt handler + ;================================ + ;================================ + ; On Apple II/6502 the interrupt handler jumps to address in 0xfffe + ; This is in the ROM, which saves the registers + ; on older IIe it saved A to $45 (which could mess with DISK II) + ; newer IIe doesn't do that. + ; It then calculates if it is a BRK or not (which trashes A) + ; Then it sets up the stack like an interrupt and calls 0x3fe + + ; Note: the IIc is much more complicated + ; its firmware tries to decode the proper source + ; based on various things, including screen hole values + ; we bypass that by switching out ROM and replacing the + ; $fffe vector with this, but that does mean we have + ; to be sure status flag and accumulator set properly + +interrupt_handler: + php ; save status flags + cld ; clear decimal mode + pha ; save A ; 3 + ; A is saved in $45 by firmware + txa + pha ; save X + tya + pha ; save Y + + + +; inc $0404 ; debug (flashes char onscreen) + + +.include "pt3_lib_irq_handler.s" + + jmp exit_interrupt + + ;================================= + ; Finally done with this interrupt + ;================================= + +quiet_exit: + stx DONE_PLAYING + jsr clear_ay_both + + ldx #$ff ; also mute the channel + stx AY_REGISTERS+7 ; just in case + + +exit_interrupt: + + pla + tay ; restore Y + pla + tax ; restore X + pla ; restore a ; 4 + + ; on II+/IIe (but not IIc) we need to do this? +interrupt_smc: + lda $45 ; restore A + plp + + rti ; return from interrupt ; 6 + + ;============ + ; typical + ; ???? cycles + diff --git a/demos/outline2021/demo/long_wait.s b/demos/outline2021/demo/long_wait.s new file mode 100644 index 00000000..558296dc --- /dev/null +++ b/demos/outline2021/demo/long_wait.s @@ -0,0 +1,11 @@ + ;===================== + ; long(er) wait + ; waits approximately X*10 ms + ; X=100 1s + ; X=4 = 40ms= 1/25s +long_wait: + lda #60 + jsr WAIT ; delay + dex + bne long_wait + rts diff --git a/demos/outline2021/demo/multiply_fast.s b/demos/outline2021/demo/multiply_fast.s new file mode 100644 index 00000000..b6f39511 --- /dev/null +++ b/demos/outline2021/demo/multiply_fast.s @@ -0,0 +1,355 @@ +; Fast mutiply + + +; Note for our purposes we only care about 8.8 x 8.8 fixed point +; with 8.8 result, which means we only care about the middle two bytes +; of the 32 bit result. So we disable generation of the high and low byte +; to save some cycles. + +; +; The old routine took around 700 cycles for a 16bitx16bit=32bit mutiply +; This routine, at an expense of 2kB of looku tables, takes around 250 +; If you reuse a term the next time this drops closer to 200 + +; This routine was described by Stephen Judd and found +; in The Fridge and in the C=Hacking magazine +; http://codebase64.org/doku.php?id=base:seriously_fast_multiplication + +; The key thing to note is that +; (a+b)^2 (a-b)^2 +; a*b = ------- - -------- +; 4 4 +; So if you have tables of the squares of 0..511 you can lookup and subtract +; instead of multiplying. + +; Table generation: I:0..511 +; square1_lo = <((I*I)/4) +; square1_hi = >((I*I)/4) +; square2_lo = <(((I-255)*(I-255))/4) +; square2_hi = >(((I-255)*(I-255))/4) + +; Note: DOS3.3 starts at $9600 +; I/O starts at $c000 + +square1_lo = $B600 +square1_hi = $B800 +square2_lo = $BA00 +square2_hi = $BC00 + + +; for(i=0;i<512;i++) { +; square1_lo[i]=((i*i)/4)&0xff; +; square1_hi[i]=(((i*i)/4)>>8)&0xff; +; square2_lo[i]=( ((i-255)*(i-255))/4)&0xff; +; square2_hi[i]=(( ((i-255)*(i-255))/4)>>8)&0xff; +; } + + + ; note, don't run these more than once? + ; why not? oh, smc that we don't reset + +init_multiply_tables: + + ; Build the add tables + + ldx #$00 + txa + .byte $c9 ; CMP #immediate - skip TYA and clear carry flag +lb1: tya + adc #$00 ; 0 +ml1: sta square1_hi,x ; square1_hi[0]=0 + tay ; y=0 + cmp #$40 ; subtract 64 and update flags (c=0) + txa ; a=0 + ror ; rotate +ml9: adc #$00 ; add 0 + sta ml9+1 ; update add value + inx ; x=1 +ml0: sta square1_lo,x ; square1_lo[0]=1 + bne lb1 ; if not zero, loop + inc ml0+2 ; increment values + inc ml1+2 ; increment values + clc ; c=0 + iny ; y=1 + bne lb1 ; loop + + ; Build the subtract tables based on the existing one + + ldx #$00 + ldy #$ff +second_table: + lda square1_hi+1,x + sta square2_hi+$100,x + lda square1_hi,x + sta square2_hi,y + lda square1_lo+1,x + sta square2_lo+$100,x + lda square1_lo,x + sta square2_lo,y + dey + inx + bne second_table + + + rts + + +; Fast 16x16 bit unsigned multiplication, 32-bit result +; Input: NUM1H:NUM1L * NUM2H:NUM2L +; Result: RESULT3:RESULT2:RESULT1:RESULT0 +; +; Does self-modifying code to hard-code NUM1H:NUM1L into the code +; carry=0: re-use previous NUM1H:NUM1L +; carry=1: reload NUM1H:NUM1L (58 cycles slower) +; +; clobbered: RESULT, X, A, C +; Allocation setup: T1,T2 and RESULT preferably on Zero-page. +; +; NUM1H (x_i), NUM1L (x_f) +; NUM2H (y_i), NUM2L (y_f) + +; NUM1L * NUM2L = AAaa +; NUM1L * NUM2H = BBbb +; NUM1H * NUM2L = CCcc +; NUM1H * NUM2H = DDdd +; +; AAaa +; BBbb +; CCcc +; + DDdd +; ---------- +; RESULT + +;fixed_16x16_mul_unsigned: + +multiply: + + bcc num1_same_as_last_time ; 2nt/3 + + ;============================ + ; Set up self-modifying code + ; this changes the code to be hard-coded to multiply by NUM1H:NUM1L + ;============================ + + lda NUM1L ; load the low byte ; 3 + sta sm1a+1 ; 3 + sta sm3a+1 ; 3 + sta sm5a+1 ; 3 + sta sm7a+1 ; 3 + eor #$ff ; invert the bits for subtracting ; 2 + sta sm2a+1 ; 3 + sta sm4a+1 ; 3 + sta sm6a+1 ; 3 + sta sm8a+1 ; 3 + lda NUM1H ; load the high byte ; 3 + sta sm1b+1 ; 3 + sta sm3b+1 ; 3 + sta sm5b+1 ; 3 +; sta sm7b+1 ; + eor #$ff ; invert the bits for subtractin ; 2 + sta sm2b+1 ; 3 + sta sm4b+1 ; 3 + sta sm6b+1 ; 3 +; sta sm8b+1 ; + ;=========== + ; 52 + +num1_same_as_last_time: + + ;========================== + ; Perform NUM1L * NUM2L = AAaa + ;========================== + + ldx NUM2L ; (low le) ; 3 + sec ; 2 +sm1a: + lda square1_lo,x ; 4 +sm2a: + sbc square2_lo,x ; 4 + + ; a is _aa + +; sta RESULT+0 ; + +sm3a: + lda square1_hi,x ; 4 +sm4a: + sbc square2_hi,x ; 4 + ; a is _AA + sta _AA+1 ; 3 + ;=========== + ; 24 + + ; Perform NUM1H * NUM2L = CCcc + sec ; 2 +sm1b: + lda square1_lo,x ; 4 +sm2b: + sbc square2_lo,x ; 4 + ; a is _cc + sta _cc+1 ; 3 +sm3b: + lda square1_hi,x ; 4 +sm4b: + sbc square2_hi,x ; 4 + ; a is _CC + sta _CC+1 ; 3 + ;=========== + ; 24 + + ;========================== + ; Perform NUM1L * NUM2H = BBbb + ;========================== + ldx NUM2H ; 3 + sec ; 2 +sm5a: + lda square1_lo,x ; 4 +sm6a: + sbc square2_lo,x ; 4 + ; a is _bb + sta _bb+1 ; 3 + +sm7a: + lda square1_hi,x ; 4 +sm8a: + sbc square2_hi,x ; 4 + ; a is _BB + sta _BB+1 ; 3 + ;=========== + ; 27 + + ;========================== + ; Perform NUM1H * NUM2H = DDdd + ;========================== + sec ; 2 +sm5b: + lda square1_lo,x ; 4 +sm6b: + sbc square2_lo,x ; 4 + ; a is _dd + sta _dd+1 ; 3 +;sm7b: +; lda square1_hi,x ; +;sm8b: +; sbc square2_hi,x ; + ; a = _DD +; sta RESULT+3 ; + ;=========== + ; 13 + + ;=========================================== + ; Add the separate multiplications together + ;=========================================== + + clc ; 2 +_AA: + lda #0 ; loading _AA ; 2 +_bb: + adc #0 ; adding in _bb ; 2 + sta RESULT+1 ; 3 + ;========== + ; 9 + ; product[2]=_BB+_CC+c + +_BB: + lda #0 ; loading _BB ; 2 +_CC: + adc #0 ; adding in _CC ; 2 + sta RESULT+2 ; 3 + ;=========== + ; 7 + + ; product[3]=_DD+c + +; bcc dd_no_carry1 ; +; inc RESULT+3 ; + clc ; 2 + ;============= + ; 2 +dd_no_carry1: + + ; product[1]=_AA+_bb+_cc + +_cc: + lda #0 ; load _cc ; 2 + adc RESULT+1 ; 3 + sta RESULT+1 ; 3 + + ; product[2]=_BB+_CC+_dd+c + +_dd: + lda #0 ; load _dd ; 2 + adc RESULT+2 ; 3 + sta RESULT+2 ; 3 + + ;=========== + ; 16 + ; product[3]=_DD+c + + +; bcc dd_no_carry2 ; +; inc RESULT+3 ; + + ;============= + ; 0 + +dd_no_carry2: + +; *z_i=product[1]; +; *z_f=product[0]; + +; rts ; 6 + + + ;================= + ; Signed multiply + ;================= + +;multiply: + +; jsr fixed_16x16_mul_unsigned ; 6 + + lda NUM1H ; x_i ; 3 + ;=========== + ; 12 + + + bpl x_positive ;^3/2nt + + sec ; 2 + lda RESULT+2 ; 3 + sbc NUM2L ; 3 + sta RESULT+2 ; 3 +; lda RESULT+3 ; +; sbc NUM2H ; +; sta RESULT+3 ; + ;============ + ; 10 + +x_positive: + + lda NUM2H ; y_i ; 3 + ;============ + ; ; 6 + + bpl y_positive ;^3/2nt + + + sec ; 2 + lda RESULT+2 ; 3 + sbc NUM1L ; 3 + sta RESULT+2 ; 3 +; lda RESULT+3 ; +; sbc NUM1H ; +; sta RESULT+3 ; + ;=========== + ; 10 + +y_positive: + ldx RESULT+2 ; *z_i=product[2]; ; 3 + lda RESULT+1 ; *z_f=product[1]; ; 3 + + rts ; 6 + ;========== + ; 12 + diff --git a/demos/outline2021/demo/outline.s b/demos/outline2021/demo/outline.s new file mode 100644 index 00000000..af29cad5 --- /dev/null +++ b/demos/outline2021/demo/outline.s @@ -0,0 +1,113 @@ +; Outline 2021? + +; by deater (Vince Weaver) + +; Zero Page + .include "zp.inc" + .include "hardware.inc" + +outline_demo: + + ;========================= + ; init the multiply tables + + ; Initialize the 2kB of multiply lookup tables + jsr init_multiply_tables + + ;=================== + ; PT3 player Setup + + lda #0 + sta DONE_PLAYING + lda #1 + sta LOOP + + jsr mockingboard_detect + bcc mockingboard_not_found +setup_interrupt: + jsr mockingboard_init + jsr mockingboard_setup_interrupt + jsr reset_ay_both + jsr clear_ay_both + jsr pt3_init_song + +start_interrupts: + + cli + +mockingboard_not_found: + + ;=================== + ; init screen + ;=================== + + jsr TEXT + jsr SETGR + jsr HOME +; bit SET_GR +; bit TEXTGR + bit KEYRESET + + ;=================== + ; init vars + ;=================== + + ;============================= + ; Title screen + ;============================= + + jsr shimmer + + ;============================= + ; a2 plasma + ;============================= + +; jsr a2_inside + +; jsr plasma + +; jsr drops + + jsr wires + +; jsr mode7_flying + + ;============================= + ; Credits + ;============================= + + jsr credits + +forever: + jmp forever + + +.include "pt3_lib_core.s" +.include "pt3_lib_init.s" +.include "pt3_lib_mockingboard_setup.s" +.include "interrupt_handler.s" +; if you're self patching, detect has to be after interrupt_handler.s +.include "pt3_lib_mockingboard_detect.s" + +.include "shimmer.s" +.include "a2_inside.s" +.include "fakepal.s" +.include "tfv_flying.s" +.include "drops.s" +.include "wires.s" +.include "credits.s" + +.include "gr_putsprite.s" +.include "gr_pageflip.s" +.include "flying_mode7.s" +.include "multiply_fast.s" +.include "gr_fast_clear.s" +.include "gr_offsets.s" + +.include "long_wait.s" +.include "random16.s" + +PT3_LOC = song +.align $100 +song: +.incbin "mAZE_-_Apple_snapple_Outline.pt3" diff --git a/demos/outline2021/demo/pt3_lib_core.s b/demos/outline2021/demo/pt3_lib_core.s new file mode 100644 index 00000000..1118b5ae --- /dev/null +++ b/demos/outline2021/demo/pt3_lib_core.s @@ -0,0 +1,1993 @@ +;=========================================== +; Library to decode Vortex Tracker PT3 files +; in 6502 assembly for Apple ][ Mockingboard +; +; by Vince Weaver + +; Roughly based on the Formats.pas Pascal code from Ay_Emul + +; Size Optimization -- Mem+Code (pt3_lib_end-note_a) +; + 3407 bytes -- original working implementation +; + 3302 bytes -- autogenerate the volume tables +; + 3297 bytes -- remove some un-needed bytes from struct +; + 3262 bytes -- combine some duplicated code in $1X/$BX env setting +; + 3253 bytes -- remove unnecessary variable +; + 3203 bytes -- combine common code in note decoder +; + 2937 bytes -- qkumba first pass +; + 2879 bytes -- qkumba second pass +; + 2839 bytes -- mask note command in common code +; + 2832 bytes -- combine $D0 and $E0 decode +; + 2816 bytes -- eliminate "decode_done" variable (2.75k) +; + 2817 bytes -- eliminate pt3_version. Slighly faster but also bigger +; + 2828 bytes -- fix some correctness issues +; + 2776 bytes -- init vars with loop (slower, but more correct and smaller) +; + 2739 bytes -- qkumba's crazy SMC everywhere patch +; + 2418+143 = 2561 bytes -- move NOTE structs to page0 +; + 2423+143 = 2566 bytes -- fix vibrato code +; + 2554+143 = 2697 bytes -- generate all four tone tables +; + 2537+143 = 2680 bytes -- inline GetNoteFreq + +; TODO +; move some of these flags to be bits rather than bytes? +; enabled could be bit 6 or 7 for fast checking +; NOTE_ENABLED,ENVELOPE_ENABLED,SIMPLE_GLISS,ENV_SLIDING,AMP_SLIDING? + +; Header offsets + +PT3_VERSION = $0D +PT3_HEADER_FREQUENCY = $63 +PT3_SPEED = $64 +PT3_LOOP = $66 +PT3_PATTERN_LOC_L = $67 +PT3_PATTERN_LOC_H = $68 +PT3_SAMPLE_LOC_L = $69 +PT3_SAMPLE_LOC_H = $6A +PT3_ORNAMENT_LOC_L = $A9 +PT3_ORNAMENT_LOC_H = $AA +PT3_PATTERN_TABLE = $C9 + +; Use memset to set things to 0? + +NOTE_VOLUME =0 +NOTE_TONE_SLIDING_L =1 +NOTE_TONE_SLIDING_H =2 +NOTE_ENABLED =3 +NOTE_ENVELOPE_ENABLED =4 +NOTE_SAMPLE_POINTER_L =5 +NOTE_SAMPLE_POINTER_H =6 +NOTE_SAMPLE_LOOP =7 +NOTE_SAMPLE_LENGTH =8 +NOTE_TONE_L =9 +NOTE_TONE_H =10 +NOTE_AMPLITUDE =11 +NOTE_NOTE =12 +NOTE_LEN =13 +NOTE_LEN_COUNT =14 +NOTE_ADDR_L =15 +NOTE_ADDR_H =16 +NOTE_ORNAMENT_POINTER_L =17 +NOTE_ORNAMENT_POINTER_H =18 +NOTE_ORNAMENT_LOOP =19 +NOTE_ORNAMENT_LENGTH =20 +NOTE_ONOFF =21 +NOTE_TONE_ACCUMULATOR_L =22 +NOTE_TONE_ACCUMULATOR_H =23 +NOTE_TONE_SLIDE_COUNT =24 +NOTE_ORNAMENT_POSITION =25 +NOTE_SAMPLE_POSITION =26 +NOTE_ENVELOPE_SLIDING =27 +NOTE_NOISE_SLIDING =28 +NOTE_AMPLITUDE_SLIDING =29 +NOTE_ONOFF_DELAY =30 ;ordering of DELAYs is hard-coded now +NOTE_OFFON_DELAY =31 ;ordering of DELAYs is hard-coded now +NOTE_TONE_SLIDE_STEP_L =32 +NOTE_TONE_SLIDE_STEP_H =33 +NOTE_TONE_SLIDE_DELAY =34 +NOTE_SIMPLE_GLISS =35 +NOTE_SLIDE_TO_NOTE =36 +NOTE_TONE_DELTA_L =37 +NOTE_TONE_DELTA_H =38 +NOTE_TONE_SLIDE_TO_STEP =39 + +NOTE_STRUCT_SIZE=40 + +.ifdef PT3_USE_ZERO_PAGE +note_a = $80 +note_b = $80+(NOTE_STRUCT_SIZE*1) +note_c = $80+(NOTE_STRUCT_SIZE*2) + +begin_vars=$80 +end_vars=$80+(NOTE_STRUCT_SIZE*3) + + +.else ; !PT3_USE_ZERO_PAGE +begin_vars: + +note_a: ; reset? + + .byte $0 ; NOTE_VOLUME ; 0 ; Y + .byte $0 ; NOTE_TONE_SLIDING_L ; 1 ; Y + .byte $0 ; NOTE_TONE_SLIDING_H ; 2 ; Y + .byte $0 ; NOTE_ENABLED ; 3 ; Y + .byte $0 ; NOTE_ENVELOPE_ENABLED ; 4 ; Y + .byte $0 ; NOTE_SAMPLE_POINTER_L ; 5 ; Y + .byte $0 ; NOTE_SAMPLE_POINTER_H ; 6 ; Y + .byte $0 ; NOTE_SAMPLE_LOOP ; 7 ; Y + .byte $0 ; NOTE_SAMPLE_LENGTH ; 8 ; Y + .byte $0 ; NOTE_TONE_L ; 9 + .byte $0 ; NOTE_TONE_H ; 10 + .byte $0 ; NOTE_AMPLITUDE ; 11 + .byte $0 ; NOTE_NOTE ; 12 + .byte $0 ; NOTE_LEN ; 13 + .byte $0 ; NOTE_LEN_COUNT ; 14 + .byte $0 ; NOTE_ADDR_L ; 15 + .byte $0 ; NOTE_ADDR_H ; 16 + .byte $0 ; NOTE_ORNAMENT_POINTER_L ; 17 ; Y + .byte $0 ; NOTE_ORNAMENT_POINTER_H ; 18 ; Y + .byte $0 ; NOTE_ORNAMENT_LOOP ; 19 ; Y + .byte $0 ; NOTE_ORNAMENT_LENGTH ; 20 ; Y + .byte $0 ; NOTE_ONOFF ; 21 + .byte $0 ; NOTE_TONE_ACCUMULATOR_L ; 22 + .byte $0 ; NOTE_TONE_ACCUMULATOR_H ; 23 + .byte $0 ; NOTE_TONE_SLIDE_COUNT ; 24 + .byte $0 ; NOTE_ORNAMENT_POSITION ; 25 ; Y + .byte $0 ; NOTE_SAMPLE_POSITION ; 26 ; Y + .byte $0 ; NOTE_ENVELOPE_SLIDING ; 27 + .byte $0 ; NOTE_NOISE_SLIDING ; 28 + .byte $0 ; NOTE_AMPLITUDE_SLIDING ; 29 + .byte $0 ; NOTE_ONOFF_DELAY ; 30 + .byte $0 ; NOTE_OFFON_DELAY ; 31 + .byte $0 ; NOTE_TONE_SLIDE_STEP_L ; 32 + .byte $0 ; NOTE_TONE_SLIDE_STEP_H ; 33 + .byte $0 ; NOTE_TONE_SLIDE_DELAY ; 34 + .byte $0 ; NOTE_SIMPLE_GLISS ; 35 + .byte $0 ; NOTE_SLIDE_TO_NOTE ; 36 + .byte $0 ; NOTE_TONE_DELTA_L ; 37 + .byte $0 ; NOTE_TONE_DELTA_H ; 38 + .byte $0 ; NOTE_TONE_SLIDE_TO_STEP ; 39 + +note_b: + .byte $0 ; NOTE_VOLUME + .byte $0 ; NOTE_TONE_SLIDING_L + .byte $0 ; NOTE_TONE_SLIDING_H + .byte $0 ; NOTE_ENABLED + .byte $0 ; NOTE_ENVELOPE_ENABLED + .byte $0 ; NOTE_SAMPLE_POINTER_L + .byte $0 ; NOTE_SAMPLE_POINTER_H + .byte $0 ; NOTE_SAMPLE_LOOP + .byte $0 ; NOTE_SAMPLE_LENGTH + .byte $0 ; NOTE_TONE_L + .byte $0 ; NOTE_TONE_H + .byte $0 ; NOTE_AMPLITUDE + .byte $0 ; NOTE_NOTE + .byte $0 ; NOTE_LEN + .byte $0 ; NOTE_LEN_COUNT + .byte $0 ; NOTE_ADDR_L + .byte $0 ; NOTE_ADDR_H + .byte $0 ; NOTE_ORNAMENT_POINTER_L + .byte $0 ; NOTE_ORNAMENT_POINTER_H + .byte $0 ; NOTE_ORNAMENT_LOOP + .byte $0 ; NOTE_ORNAMENT_LENGTH + .byte $0 ; NOTE_ONOFF + .byte $0 ; NOTE_TONE_ACCUMULATOR_L + .byte $0 ; NOTE_TONE_ACCUMULATOR_H + .byte $0 ; NOTE_TONE_SLIDE_COUNT + .byte $0 ; NOTE_ORNAMENT_POSITION + .byte $0 ; NOTE_SAMPLE_POSITION + .byte $0 ; NOTE_ENVELOPE_SLIDING + .byte $0 ; NOTE_NOISE_SLIDING + .byte $0 ; NOTE_AMPLITUDE_SLIDING + .byte $0 ; NOTE_ONOFF_DELAY + .byte $0 ; NOTE_OFFON_DELAY + .byte $0 ; NOTE_TONE_SLIDE_STEP_L + .byte $0 ; NOTE_TONE_SLIDE_STEP_H + .byte $0 ; NOTE_TONE_SLIDE_DELAY + .byte $0 ; NOTE_SIMPLE_GLISS + .byte $0 ; NOTE_SLIDE_TO_NOTE + .byte $0 ; NOTE_TONE_DELTA_L + .byte $0 ; NOTE_TONE_DELTA_H + .byte $0 ; NOTE_TONE_SLIDE_TO_STEP + +note_c: + .byte $0 ; NOTE_VOLUME + .byte $0 ; NOTE_TONE_SLIDING_L + .byte $0 ; NOTE_TONE_SLIDING_H + .byte $0 ; NOTE_ENABLED + .byte $0 ; NOTE_ENVELOPE_ENABLED + .byte $0 ; NOTE_SAMPLE_POINTER_L + .byte $0 ; NOTE_SAMPLE_POINTER_H + .byte $0 ; NOTE_SAMPLE_LOOP + .byte $0 ; NOTE_SAMPLE_LENGTH + .byte $0 ; NOTE_TONE_L + .byte $0 ; NOTE_TONE_H + .byte $0 ; NOTE_AMPLITUDE + .byte $0 ; NOTE_NOTE + .byte $0 ; NOTE_LEN + .byte $0 ; NOTE_LEN_COUNT + .byte $0 ; NOTE_ADDR_L + .byte $0 ; NOTE_ADDR_H + .byte $0 ; NOTE_ORNAMENT_POINTER_L + .byte $0 ; NOTE_ORNAMENT_POINTER_H + .byte $0 ; NOTE_ORNAMENT_LOOP + .byte $0 ; NOTE_ORNAMENT_LENGTH + .byte $0 ; NOTE_ONOFF + .byte $0 ; NOTE_TONE_ACCUMULATOR_L + .byte $0 ; NOTE_TONE_ACCUMULATOR_H + .byte $0 ; NOTE_TONE_SLIDE_COUNT + .byte $0 ; NOTE_ORNAMENT_POSITION + .byte $0 ; NOTE_SAMPLE_POSITION + .byte $0 ; NOTE_ENVELOPE_SLIDING + .byte $0 ; NOTE_NOISE_SLIDING + .byte $0 ; NOTE_AMPLITUDE_SLIDING + .byte $0 ; NOTE_ONOFF_DELAY + .byte $0 ; NOTE_OFFON_DELAY + .byte $0 ; NOTE_TONE_SLIDE_STEP_L + .byte $0 ; NOTE_TONE_SLIDE_STEP_H + .byte $0 ; NOTE_TONE_SLIDE_DELAY + .byte $0 ; NOTE_SIMPLE_GLISS + .byte $0 ; NOTE_SLIDE_TO_NOTE + .byte $0 ; NOTE_TONE_DELTA_L + .byte $0 ; NOTE_TONE_DELTA_H + .byte $0 ; NOTE_TONE_SLIDE_TO_STEP +end_vars: +.endif + +load_ornament0_sample1: + lda #0 ; 2 + jsr load_ornament ; 6 + ; fall through + + ;=========================== + ; Load Sample + ;=========================== + ; sample in A + ; which note offset in X + + ; Sample table pointers are 16-bits little endian + ; There are 32 of these pointers starting at $6a:$69 + ; Our sample starts at address (A*2)+that pointer + ; We point SAMPLE_H:SAMPLE_L to this + ; then we load the length/data values + ; and then leave SAMPLE_H:SAMPLE_L pointing to begnning of + ; the sample data + + ; Optimization: + ; see comments on ornament setting + +load_sample1: + lda #1 ; 2 + +load_sample: + + sty PT3_TEMP ; 3 + + ;pt3->ornament_patterns[i]= + ; (pt3->data[0x6a+(i*2)]<<8)|pt3->data[0x69+(i*2)]; + + asl ; A*2 ; 2 + tay ; 2 + + ; Set the initial sample pointer + ; a->sample_pointer=pt3->sample_patterns[a->sample]; + + lda PT3_LOC+PT3_SAMPLE_LOC_L,Y ; 4+ + sta SAMPLE_L ; 3 + + lda PT3_LOC+PT3_SAMPLE_LOC_L+1,Y ; 4+ + + ; assume pt3 file is at page boundary + adc #>PT3_LOC ; 2 + sta SAMPLE_H ; 3 + + ; Set the loop value + ; a->sample_loop=pt3->data[a->sample_pointer]; + + ldy #0 ; 2 + lda (SAMPLE_L),Y ; 5+ + sta note_a+NOTE_SAMPLE_LOOP,X ; 5 + + ; Set the length value + ; a->sample_length=pt3->data[a->sample_pointer]; + + iny ; 2 + lda (SAMPLE_L),Y ; 5+ + sta note_a+NOTE_SAMPLE_LENGTH,X ; 5 + + ; Set pointer to beginning of samples + + lda SAMPLE_L ; 3 + adc #$2 ; 2 + sta note_a+NOTE_SAMPLE_POINTER_L,X ; 5 + lda SAMPLE_H ; 3 + adc #$0 ; 2 + sta note_a+NOTE_SAMPLE_POINTER_H,X ; 5 + + ldy PT3_TEMP ; 3 + + rts ; 6 + ;============ + ; 76 + + + ;=========================== + ; Load Ornament + ;=========================== + ; ornament value in A + ; note offset in X + + ; Ornament table pointers are 16-bits little endian + ; There are 16 of these pointers starting at $aa:$a9 + ; Our ornament starts at address (A*2)+that pointer + ; We point ORNAMENT_H:ORNAMENT_L to this + ; then we load the length/data values + ; and then leave ORNAMENT_H:ORNAMENT_L pointing to begnning of + ; the ornament data + + ; Optimization: + ; Loop and length only used once, can be located negative + ; from the pointer, but 6502 doesn't make addressing like that + ; easy. Can't self modify as channels A/B/C have own copies + ; of the var. + +load_ornament: + + sty PT3_TEMP ; save Y value ; 3 + + ;pt3->ornament_patterns[i]= + ; (pt3->data[0xaa+(i*2)]<<8)|pt3->data[0xa9+(i*2)]; + + asl ; A*2 ; 2 + tay ; 2 + + ; a->ornament_pointer=pt3->ornament_patterns[a->ornament]; + + lda PT3_LOC+PT3_ORNAMENT_LOC_L,Y ; 4+ + sta ORNAMENT_L ; 3 + + lda PT3_LOC+PT3_ORNAMENT_LOC_L+1,Y ; 4+ + + ; we're assuming PT3 is loaded to a page boundary + + adc #>PT3_LOC ; 2 + sta ORNAMENT_H ; 3 + + lda #0 ; 2 + sta note_a+NOTE_ORNAMENT_POSITION,X ; 5 + + tay ; 2 + + ; Set the loop value + ; a->ornament_loop=pt3->data[a->ornament_pointer]; + lda (ORNAMENT_L),Y ; 5+ + sta note_a+NOTE_ORNAMENT_LOOP,X ; 5 + + ; Set the length value + ; a->ornament_length=pt3->data[a->ornament_pointer]; + iny ; 2 + lda (ORNAMENT_L),Y ; 5+ + sta note_a+NOTE_ORNAMENT_LENGTH,X ; 5 + + ; Set the pointer to the value past the length + + lda ORNAMENT_L ; 3 + adc #$2 ; 2 + sta note_a+NOTE_ORNAMENT_POINTER_L,X ; 5 + lda ORNAMENT_H ; 3 + adc #$0 ; 2 + sta note_a+NOTE_ORNAMENT_POINTER_H,X ; 5 + + ldy PT3_TEMP ; restore Y value ; 3 + + rts ; 6 + + ;============ + ; 83 + + + + ;===================================== + ; Calculate Note + ;===================================== + ; note offset in X + +calculate_note: + + lda note_a+NOTE_ENABLED,X ; 4+ + bne note_enabled ; 2/3 + + sta note_a+NOTE_AMPLITUDE,X ; 5 + jmp done_note ; 3 + +note_enabled: + + lda note_a+NOTE_SAMPLE_POINTER_H,X ; 4+ + sta SAMPLE_H ; 3 + lda note_a+NOTE_SAMPLE_POINTER_L,X ; 4+ + sta SAMPLE_L ; 3 + + lda note_a+NOTE_ORNAMENT_POINTER_H,X ; 4+ + sta ORNAMENT_H ; 3 + lda note_a+NOTE_ORNAMENT_POINTER_L,X ; 4+ + sta ORNAMENT_L ; 3 + + + lda note_a+NOTE_SAMPLE_POSITION,X ; 4+ + asl ; 2 + asl ; 2 + tay ; 2 + + ; b0 = pt3->data[a->sample_pointer + a->sample_position * 4]; + lda (SAMPLE_L),Y ; 5+ + sta sample_b0_smc+1 ; 4 + + ; b1 = pt3->data[a->sample_pointer + a->sample_position * 4 + 1]; + iny ; 2 + lda (SAMPLE_L),Y ; 5+ + sta sample_b1_smc+1 ; 4 + + ; a->tone = pt3->data[a->sample_pointer + a->sample_position*4+2]; + ; a->tone+=(pt3->data[a->sample_pointer + a->sample_position*4+3])<<8; + ; a->tone += a->tone_accumulator; + iny ; 2 + lda (SAMPLE_L),Y ; 5+ + adc note_a+NOTE_TONE_ACCUMULATOR_L,X ; 4+ + sta note_a+NOTE_TONE_L,X ; 4 + + iny ; 2 + lda (SAMPLE_L),Y ; 5+ + adc note_a+NOTE_TONE_ACCUMULATOR_H,X ; 4+ + sta note_a+NOTE_TONE_H,X ; 4 + + ;============================= + ; Accumulate tone if set + ; (if sample_b1 & $40) + + bit sample_b1_smc+1 + bvc no_accum ; (so, if b1&0x40 is zero, skip it) + + sta note_a+NOTE_TONE_ACCUMULATOR_H,X + lda note_a+NOTE_TONE_L,X ; tone_accumulator=tone + sta note_a+NOTE_TONE_ACCUMULATOR_L,X + +no_accum: + + ;============================ + ; Calculate tone + ; j = a->note + (pt3->data[a->ornament_pointer + a->ornament_position] + clc ;;can be removed if ADC ACCUMULATOR_H cannot overflow + ldy note_a+NOTE_ORNAMENT_POSITION,X + lda (ORNAMENT_L),Y + adc note_a+NOTE_NOTE,X + + ; if (j < 0) j = 0; + bpl note_not_negative + lda #0 + + ; if (j > 95) j = 95; +note_not_negative: + cmp #96 + bcc note_not_too_high ; blt + + lda #95 + +note_not_too_high: + + ; w = GetNoteFreq(j,pt3->frequency_table); + + tay ; for GetNoteFreq later + + ; a->tone = (a->tone + a->tone_sliding + w) & 0xfff; + + clc + lda note_a+NOTE_TONE_SLIDING_L,X + adc note_a+NOTE_TONE_L,X + sta temp_word_l1_smc+1 + + lda note_a+NOTE_TONE_H,X + adc note_a+NOTE_TONE_SLIDING_H,X + sta temp_word_h1_smc+1 + + clc ;;can be removed if ADC SLIDING_H cannot overflow +temp_word_l1_smc: + lda #$d1 + adc NoteTable_low,Y ; GetNoteFreq + sta note_a+NOTE_TONE_L,X +temp_word_h1_smc: + lda #$d1 + adc NoteTable_high,Y ; GetNoteFreq + and #$0f + sta note_a+NOTE_TONE_H,X + + ;===================== + ; handle tone sliding + + lda note_a+NOTE_TONE_SLIDE_COUNT,X + bmi no_tone_sliding ; if (a->tone_slide_count > 0) { + beq no_tone_sliding + + dec note_a+NOTE_TONE_SLIDE_COUNT,X ; a->tone_slide_count--; + bne no_tone_sliding ; if (a->tone_slide_count==0) { + + + ; a->tone_sliding+=a->tone_slide_step + clc ;;can be removed if ADC freq_h cannot overflow + lda note_a+NOTE_TONE_SLIDING_L,X + adc note_a+NOTE_TONE_SLIDE_STEP_L,X + sta note_a+NOTE_TONE_SLIDING_L,X + tay ; save NOTE_TONE_SLIDING_L in y + lda note_a+NOTE_TONE_SLIDING_H,X + adc note_a+NOTE_TONE_SLIDE_STEP_H,X + sta note_a+NOTE_TONE_SLIDING_H,X + + ; a->tone_slide_count = a->tone_slide_delay; + lda note_a+NOTE_TONE_SLIDE_DELAY,X + sta note_a+NOTE_TONE_SLIDE_COUNT,X + + lda note_a+NOTE_SIMPLE_GLISS,X + bne no_tone_sliding ; if (!a->simplegliss) { + + ; FIXME: do these need to be signed compares? + +check1: + lda note_a+NOTE_TONE_SLIDE_STEP_H,X + bpl check2 ; if ( ((a->tone_slide_step < 0) && + + ; (a->tone_sliding <= a->tone_delta) || + + ; 16 bit signed compare + tya ; y has NOTE_TONE_SLIDING_L + cmp note_a+NOTE_TONE_DELTA_L,X ; NUM1-NUM2 + lda note_a+NOTE_TONE_SLIDING_H,X + sbc note_a+NOTE_TONE_DELTA_H,X + bvc sc_loser1 ; N eor V + eor #$80 +sc_loser1: + bmi slide_to_note ; then A (signed) < NUM (signed) and BMI will branch + + ; equals case + tya ; y has NOTE_TONE_SLIDING_L + cmp note_a+NOTE_TONE_DELTA_L,X + bne check2 + lda note_a+NOTE_TONE_SLIDING_H,X + cmp note_a+NOTE_TONE_DELTA_H,X + beq slide_to_note + +check2: + lda note_a+NOTE_TONE_SLIDE_STEP_H,X + bmi no_tone_sliding ; ((a->tone_slide_step >= 0) && + + ; (a->tone_sliding >= a->tone_delta) + + ; 16 bit signed compare + tya ; y has NOTE_TONE_SLIDING_L + cmp note_a+NOTE_TONE_DELTA_L,X ; num1-num2 + lda note_a+NOTE_TONE_SLIDING_H,X + sbc note_a+NOTE_TONE_DELTA_H,X + bvc sc_loser2 ; N eor V + eor #$80 +sc_loser2: + bmi no_tone_sliding ; then A (signed) < NUM (signed) and BMI will branch + +slide_to_note: + ; a->note = a->slide_to_note; + lda note_a+NOTE_SLIDE_TO_NOTE,X + sta note_a+NOTE_NOTE,X + lda #0 + sta note_a+NOTE_TONE_SLIDE_COUNT,X + sta note_a+NOTE_TONE_SLIDING_L,X + sta note_a+NOTE_TONE_SLIDING_H,X + + +no_tone_sliding: + + ;========================= + ; Calculate the amplitude + ;========================= +calc_amplitude: + ; get base value from the sample (bottom 4 bits of sample_b1) + +sample_b1_smc: + lda #$d1 ; a->amplitude= (b1 & 0xf); + and #$f + + ;======================================== + ; if b0 top bit is set, it means sliding + + ; adjust amplitude sliding + + bit sample_b0_smc+1 ; if ((b0 & 0x80)!=0) { + bpl done_amp_sliding ; so if top bit not set, skip + tay + + ;================================ + ; if top bits 0b11 then slide up + ; if top bits 0b10 then slide down + + ; if ((b0 & 0x40)!=0) { + lda note_a+NOTE_AMPLITUDE_SLIDING,X + sec + bvc amp_slide_down + +amp_slide_up: + ; if (a->amplitude_sliding < 15) { + ; a pain to do signed compares + sbc #15 + bvc asu_signed + eor #$80 +asu_signed: + bpl done_amp_sliding ; skip if A>=15 + inc note_a+NOTE_AMPLITUDE_SLIDING,X ; a->amplitude_sliding++; + bne done_amp_sliding_y + +amp_slide_down: + ; if (a->amplitude_sliding > -15) { + ; a pain to do signed compares + sbc #$f1 ; -15 + bvc asd_signed + eor #$80 +asd_signed: + bmi done_amp_sliding ; if A < -15, skip subtract + + dec note_a+NOTE_AMPLITUDE_SLIDING,X ; a->amplitude_sliding--; + +done_amp_sliding_y: + tya + +done_amp_sliding: + + ; a->amplitude+=a->amplitude_sliding; + clc + adc note_a+NOTE_AMPLITUDE_SLIDING,X + + ; clamp amplitude to 0 - 15 + +check_amp_lo: + bmi write_clamp_amplitude + +check_amp_hi: + cmp #16 + bcc write_amplitude ; blt + lda #15 + .byte $2C +write_clamp_amplitude: + lda #0 +write_amplitude: + sta note_amp_smc+1 + +done_clamp_amplitude: + + ; We generate the proper table at runtime now + ; so always in Volume Table + ; a->amplitude = PT3VolumeTable_33_34[a->volume][a->amplitude]; + ; a->amplitude = PT3VolumeTable_35[a->volume][a->amplitude]; + + lda note_a+NOTE_VOLUME,X ; 4+ + asl ; 2 + asl ; 2 + asl ; 2 + asl ; 2 +note_amp_smc: + ora #$d1 ; 4+ + + tay ; 2 + lda VolumeTable,Y ; 4+ + sta note_a+NOTE_AMPLITUDE,X ; 5 + +done_table: + + +check_envelope_enable: + ; Bottom bit of b0 indicates our sample has envelope + ; Also make sure envelopes are enabled + + + ; if (((b0 & 0x1) == 0) && ( a->envelope_enabled)) { +sample_b0_smc: + lda #$d1 + lsr + tay + bcs envelope_slide + + lda note_a+NOTE_ENVELOPE_ENABLED,X + beq envelope_slide + + + ; Bit 4 of the per-channel AY-3-8910 amplitude specifies + ; envelope enabled + + lda note_a+NOTE_AMPLITUDE,X ; a->amplitude |= 16; + ora #$10 + sta note_a+NOTE_AMPLITUDE,X + + +envelope_slide: + + ; Envelope slide + ; If b1 top bits are 10 or 11 + + lda sample_b0_smc+1 + asl + asl + asl ; b0 bit 5 to carry flag + lda #$20 + bit sample_b1_smc+1 ; b1 bit 7 to sign flag, bit 5 to zero flag + php + bpl else_noise_slide ; if ((b1 & 0x80) != 0) { + tya + ora #$f0 + bcs envelope_slide_down ; if ((b0 & 0x20) == 0) { + +envelope_slide_up: + ; j = ((b0>>1)&0xF) + a->envelope_sliding; + and #$0f + clc + +envelope_slide_down: + + ; j = ((b0>>1)|0xF0) + a->envelope_sliding + adc note_a+NOTE_ENVELOPE_SLIDING,X + sta e_slide_amount_smc+1 ; j + +envelope_slide_done: + + plp + beq last_envelope ; if (( b1 & 0x20) != 0) { + + ; a->envelope_sliding = j; + sta note_a+NOTE_ENVELOPE_SLIDING,X + +last_envelope: + + ; pt3->envelope_add+=j; + + clc +e_slide_amount_smc: + lda #$d1 + adc pt3_envelope_add_smc+1 + sta pt3_envelope_add_smc+1 + + jmp noise_slide_done ; skip else + +else_noise_slide: + ; Noise slide + ; else { + + ; pt3->noise_add = (b0>>1) + a->noise_sliding; + tya + clc + adc note_a+NOTE_NOISE_SLIDING,X + sta pt3_noise_add_smc+1 + + plp + beq noise_slide_done ; if ((b1 & 0x20) != 0) { + + ; noise_sliding = pt3_noise_add + sta note_a+NOTE_NOISE_SLIDING,X + +noise_slide_done: + ;====================== + ; set mixer + + lda sample_b1_smc+1 ; pt3->mixer_value = ((b1 >>1) & 0x48) | pt3->mixer_value; + lsr + and #$48 + + ora PT3_MIXER_VAL ; 3 + sta PT3_MIXER_VAL ; 3 + + + ;======================== + ; increment sample position + + inc note_a+NOTE_SAMPLE_POSITION,X ; a->sample_position++; + + lda note_a+NOTE_SAMPLE_POSITION,X + cmp note_a+NOTE_SAMPLE_LENGTH,X + + bcc sample_pos_ok ; blt + + lda note_a+NOTE_SAMPLE_LOOP,X + sta note_a+NOTE_SAMPLE_POSITION,X + +sample_pos_ok: + + ;======================== + ; increment ornament position + + inc note_a+NOTE_ORNAMENT_POSITION,X ; a->ornament_position++; + lda note_a+NOTE_ORNAMENT_POSITION,X + cmp note_a+NOTE_ORNAMENT_LENGTH,X + + bcc ornament_pos_ok ; blt + + lda note_a+NOTE_ORNAMENT_LOOP,X + sta note_a+NOTE_ORNAMENT_POSITION,X +ornament_pos_ok: + + +done_note: + ; set mixer value + ; this is a bit complex (from original code) + ; after 3 calls it is set up properly + lsr PT3_MIXER_VAL + +handle_onoff: + ldy note_a+NOTE_ONOFF,X ;if (a->onoff>0) { + beq done_onoff + + dey ; a->onoff--; + + bne put_offon ; if (a->onoff==0) { + lda note_a+NOTE_ENABLED,X + eor #$1 ; toggle note_enabled + sta note_a+NOTE_ENABLED,X + + beq do_offon +do_onoff: + ldy note_a+NOTE_ONOFF_DELAY,X ; if (a->enabled) a->onoff=a->onoff_delay; + jmp put_offon +do_offon: + ldy note_a+NOTE_OFFON_DELAY,X ; else a->onoff=a->offon_delay; +put_offon: +.ifdef PT3_USE_ZERO_PAGE + sty note_a+NOTE_ONOFF,X +.else + lda note_a+NOTE_ONOFF,X + tay +.endif + +done_onoff: + + rts ; 6 + + + + + + + ;===================================== + ; Decode Note + ;===================================== + ; X points to the note offset + + ; Note! These timings are out of date (FIXME) + ; Timings (from ===>) + ; 00: 14+30 + ; 0X: 14+15 + ; 10: 14+5 +124 + ; 1X: 14+5 +193 + ; 2X/3X: 14+5 +17 + ; 4X: 14+5+5 + 111 + ; 5X-BX: 14+5+5+ 102 + ; CX: + ; DX/EX: + ; FX: + +stop_decoding: + + ; we are still running, decrement and early return + dec note_a+NOTE_LEN_COUNT,X ; 7 + rts ; 6 + + ;===================================== + ; Decode Line + ;===================================== + +pt3_decode_line: + ; decode_note(&pt3->a,&(pt3->a_addr),pt3); + ldx #(NOTE_STRUCT_SIZE*0) + jsr decode_note + + ; decode_note(&pt3->b,&(pt3->b_addr),pt3); + ldx #(NOTE_STRUCT_SIZE*1) + jsr decode_note + + ; decode_note(&pt3->c,&(pt3->c_addr),pt3); + ldx #(NOTE_STRUCT_SIZE*2) + ;;jsr decode_note ; fall through + +; if (pt3->a.all_done && pt3->b.all_done && pt3->c.all_done) { +; return 1; +; } + +decode_note: + + ; Init vars + + ldy #0 ; 2 + sty spec_command_smc+1 ; 4 + + ; Skip decode if note still running + lda note_a+NOTE_LEN_COUNT,X ; 4+ + cmp #2 ; 2 + bcs stop_decoding ; blt, assume not negative ; 2/3 + +keep_decoding: + + lda note_a+NOTE_NOTE,X ; store prev note ; 4+ + sta prev_note_smc+1 ; 4 + + lda note_a+NOTE_TONE_SLIDING_H,X ; store prev sliding ; 4+ + sta prev_sliding_h_smc+1 ; 4 + lda note_a+NOTE_TONE_SLIDING_L,X ; 4+ + sta prev_sliding_l_smc+1 ; 4 + + + ;============ + ; 24 + +note_decode_loop: + lda note_a+NOTE_LEN,X ; re-up length count ; 4+ + sta note_a+NOTE_LEN_COUNT,X ; 5 + + lda note_a+NOTE_ADDR_L,X ; 4+ + sta PATTERN_L ; 3 + lda note_a+NOTE_ADDR_H,X ; 4+ + sta PATTERN_H ; 3 +;===> + ; get next value + lda (PATTERN_L),Y ; 5+ + sta note_command_smc+1 ; save termporarily ; 4 + and #$0f ; 2 + sta note_command_bottom_smc+1 ; 4 + +note_command_smc: + lda #$d1 ; 2 + + ; FIXME: use a jump table?? + ; further reflection, that would require 32-bytes of addresses + ; in addition to needing X or Y to index the jump table. hmmm + + and #$f0 ; 2 + + ; cmp #$00 + bne decode_case_1X ; 2/3 + ;============= + ; 14 + +decode_case_0X: + ;============================== + ; $0X set special effect + ;============================== + ; -1 +note_command_bottom_smc: + lda #$d1 ; 2 + + ; we can always store spec as 0 means no spec + + ; FIXME: what if multiple spec commands? + ; Doesn't seem to happen in practice + ; But AY_emul has code to handle it + + sta spec_command_smc+1 ; 4 + + bne decode_case_0X_not_zero ; 2/3 + ;============= + ; 8 + ; 00 case + ; means end of pattern + ; -1 + sta note_a+NOTE_LEN_COUNT,X ; len_count=0; ; 5 + + dec pt3_pattern_done_smc+1 ; 6 + + jmp note_done_decoding ; 3 + +decode_case_1X: + ;============================== + ; $1X -- Set Envelope Type + ;============================== + + cmp #$10 ; 2 + bne decode_case_2X ; 2/3 + ;============ + ; 5 + + ; -1 + lda note_command_bottom_smc+1 ; 4 + bne decode_case_not_10 ; 3 + +decode_case_10: + ; 10 case - disable ; -1 + sta note_a+NOTE_ENVELOPE_ENABLED,X ; A is 0 ; 5 + beq decode_case_1x_common ; branch always ; 3 + +decode_case_not_10: + ; -1 + jsr set_envelope ; 6+64 + +decode_case_1x_common: + + iny ; 2 + lda (PATTERN_L),Y ; 5+ + lsr ; 2 + jsr load_sample ; 6+86 + + lda #0 ; 2 + sta note_a+NOTE_ORNAMENT_POSITION,X ; ornament_position=0 ; 5 + +decode_case_0X_not_zero: + + jmp done_decode_loop ; 3 + +decode_case_2X: +decode_case_3X: + ;============================== + ; $2X/$3X set noise period + ;============================== + + cmp #$40 ; 2 + bcs decode_case_4X ; branch greater/equal ; 3 + ; -1 + lda note_command_smc+1 ; 4 + adc #$e0 ; same as subtract $20 ; 2 + sta pt3_noise_period_smc+1 ; 3 + + jmp done_decode_loop ; 3 + ;=========== + ; 16 + +decode_case_4X: + ;============================== + ; $4X -- set ornament + ;============================== +; cmp #$40 ; already set ; + bne decode_case_5X ; 3 + ; -1 + lda note_command_bottom_smc+1; set ornament to bottom nibble; 4 + jsr load_ornament ; 6+93 + + jmp done_decode_loop ; 3 + ;============ + ; 110 + +decode_case_5X: + ;============================== + ; $5X-$AX set note + ;============================== + cmp #$B0 ; 2 + bcs decode_case_bX ; branch greater/equal ; 3 + + ; -1 + lda note_command_smc+1 ; 4 + adc #$b0 ; 2 + sta note_a+NOTE_NOTE,X ; note=(current_val-0x50); ; 5 + + jsr reset_note ; 6+69 + + lda #1 ; 2 + sta note_a+NOTE_ENABLED,X ; enabled=1 ; 5 + + + bne note_done_decoding ; 3 + +decode_case_bX: + ;============================================ + ; $BX -- note length or envelope manipulation + ;============================================ +; cmp #$b0 ; already set from before + bne decode_case_cX ; 3 + ; -1 + lda note_command_bottom_smc+1 ; 4 + beq decode_case_b0 ; 3 + ; -1 + sbc #1 ; envelope_type=(current_val&0xf)-1; ; 2 + bne decode_case_bx_higher ; 3 + +decode_case_b1: + ; Set Length + + ; get next byte + iny ; 2 + lda (PATTERN_L),Y ; 5 + + sta note_a+NOTE_LEN,X ; 5 + sta note_a+NOTE_LEN_COUNT,X ; 5 + bcs done_decode_loop ; branch always ; 3 + +decode_case_b0: + ; Disable envelope + sta note_a+NOTE_ENVELOPE_ENABLED,X ; 5 + sta note_a+NOTE_ORNAMENT_POSITION,X ; 5 + beq done_decode_loop ; 3 + + +decode_case_bx_higher: + + jsr set_envelope ; 6+64 + + bcs done_decode_loop ; branch always ; 3 + +decode_case_cX: + ;============================== + ; $CX -- set volume + ;============================== + cmp #$c0 ; check top nibble $C ; 2 + bne decode_case_dX ; 3 + ; -1 + lda note_command_bottom_smc+1 ; 4 + bne decode_case_cx_not_c0 ; 3 + ; -1 +decode_case_c0: + ; special case $C0 means shut down the note + + sta note_a+NOTE_ENABLED,X ; enabled=0 ; 5 + + jsr reset_note ; 6+69 + + beq note_done_decoding ; branch always ; 3 + +decode_case_cx_not_c0: + sta note_a+NOTE_VOLUME,X ; volume=current_val&0xf; 5 + bne done_decode_loop ; branch always ; 3 + +decode_case_dX: + ;============================== + ; $DX/$EX -- change sample + ;============================== + ; D0 = special case (end note) + ; D1-EF = set sample to (value - $D0) + + cmp #$f0 ; check top nibble $D/$E ; 2 + beq decode_case_fX ; 3 + ; -1 + + lda note_command_smc+1 ; 4 + sec ; 2 + sbc #$d0 ; 2 + beq note_done_decoding ; 3 + +decode_case_not_d0: + ; -1 + + jsr load_sample ; load sample in bottom nybble ; 6+?? + + bcc done_decode_loop; branch always ; 3 + + ;======================== + ; d0 case means end note +;decode_case_d0: +; jmp note_done_decoding + + + ;============================== + ; $FX - change ornament/sample + ;============================== +decode_case_fX: + ; disable envelope + lda #0 ; 2 + sta note_a+NOTE_ENVELOPE_ENABLED,X ; 5 + + ; Set ornament to low byte of command + lda note_command_bottom_smc+1 ; 4 + jsr load_ornament ; ornament to load in A ; 6+? + + ; Get next byte + iny ; point to next byte ; 2 + lda (PATTERN_L),Y ; 5 + + ; Set sample to value/2 + lsr ; divide by two ; 2 + jsr load_sample ; sample to load in A ; 6+? + + ; fallthrough + +done_decode_loop: + + iny ; point to next byte ; 2 + + jmp note_decode_loop ; 3 + +note_done_decoding: + + iny ; point to next byte ; 2 + + ;================================= + ; handle effects + ;================================= + ; Note, the AYemul code has code to make sure these are applied + ; In the same order they appear. We don't bother? +handle_effects: + +spec_command_smc: + lda #$d1 ; 2 + + ;============================== + ; Effect #1 -- Tone Down + ;============================== +effect_1: + cmp #$1 ; 2 + bne effect_2 ; 3 + ; -1 + sta note_a+NOTE_SIMPLE_GLISS,X ; 5 + lsr ; 2 + sta note_a+NOTE_ONOFF,X ; 5 + + lda (PATTERN_L),Y ; load byte, set as slide delay ; 5 + iny ; 2 + + sta note_a+NOTE_TONE_SLIDE_DELAY,X ; 5 + sta note_a+NOTE_TONE_SLIDE_COUNT,X ; 5 + + lda (PATTERN_L),Y ; load byte, set as slide step low ; 5 + iny ; 2 + sta note_a+NOTE_TONE_SLIDE_STEP_L,X ; 5 + + lda (PATTERN_L),Y ; load byte, set as slide step high ; 5 + iny ; 2 + sta note_a+NOTE_TONE_SLIDE_STEP_H,X ; 5 + + jmp no_effect ; 3 + + ;============================== + ; Effect #2 -- Portamento + ;============================== +effect_2: + cmp #$2 ; 2 + beq effect_2_small ; 3 + ; -1 + jmp effect_3 ; 3 +effect_2_small: ; FIXME: make smaller + lda #0 ; 2 + sta note_a+NOTE_SIMPLE_GLISS,X ; 5 + sta note_a+NOTE_ONOFF,X ; 5 + + lda (PATTERN_L),Y ; load byte, set as delay ; 5 + iny ; 2 + + sta note_a+NOTE_TONE_SLIDE_DELAY,X ; 5 + sta note_a+NOTE_TONE_SLIDE_COUNT,X ; 5 + + iny ; 2 + iny ; 2 + iny ; 2 + + lda (PATTERN_L),Y ; load byte, set as slide_step high ; 5 + php ; 3 + + ; 16-bit absolute value + bpl slide_step_positive1 ; 3 + ;-1 + eor #$ff ; 2 + +slide_step_positive1: + sta note_a+NOTE_TONE_SLIDE_STEP_H,X ; 5 + dey ; 2 + lda (PATTERN_L),Y ; load byte, set as slide_step low ; 5 + plp ; 4 + clc ; 2 + bpl slide_step_positive2 ; 3 + ;-1 + eor #$ff ; 2 + sec ; 2 + +slide_step_positive2: + adc #$0 ; 2 + sta note_a+NOTE_TONE_SLIDE_STEP_L,X ; 5 + bcc skip_step_inc1 ; 3 + inc note_a+NOTE_TONE_SLIDE_STEP_H,X ; 7 +skip_step_inc1: + + + iny ; moved here as it messed with flags ; 2 + iny ; 2 + + +; a->tone_delta=GetNoteFreq(a->note,pt3)- +; GetNoteFreq(prev_note,pt3); + + sty PT3_TEMP ; save Y +prev_note_smc: + ldy #$d1 + lda NoteTable_low,Y ; GetNoteFreq + sta temp_word_l2_smc+1 + lda NoteTable_high,Y ; GetNoteFreq + sta temp_word_h2_smc+1 + + ldy note_a+NOTE_NOTE,X + lda NoteTable_low,Y ; GetNoteFreq + + sec +temp_word_l2_smc: + sbc #$d1 + sta note_a+NOTE_TONE_DELTA_L,X + lda NoteTable_high,Y ; GetNoteFreq +temp_word_h2_smc: + sbc #$d1 + sta note_a+NOTE_TONE_DELTA_H,X + + ; a->slide_to_note=a->note; + lda note_a+NOTE_NOTE,X + sta note_a+NOTE_SLIDE_TO_NOTE,X + + ldy PT3_TEMP ; restore Y + + ; a->note=prev_note; + lda prev_note_smc+1 + sta note_a+NOTE_NOTE,X + + ; implement file version 6 and above slide behavior + ; this is done by SMC at song init time +version_smc: + jmp weird_version ; (JMP to BIT via smc) ; 3 + +prev_sliding_l_smc: + lda #$d1 + sta note_a+NOTE_TONE_SLIDING_L,X +prev_sliding_h_smc: + lda #$d1 + sta note_a+NOTE_TONE_SLIDING_H,X + +weird_version: + + ; annoying 16-bit subtract, only care if negative + ; if ((a->tone_delta - a->tone_sliding) < 0) { + sec + lda note_a+NOTE_TONE_DELTA_L,X + sbc note_a+NOTE_TONE_SLIDING_L,X + lda note_a+NOTE_TONE_DELTA_H,X + sbc note_a+NOTE_TONE_SLIDING_H,X + bpl no_effect + + ; a->tone_slide_step = -a->tone_slide_step; + + lda note_a+NOTE_TONE_SLIDE_STEP_L,X + eor #$ff + clc + adc #$1 + sta note_a+NOTE_TONE_SLIDE_STEP_L,X + lda note_a+NOTE_TONE_SLIDE_STEP_H,X + eor #$ff + adc #$0 + sta note_a+NOTE_TONE_SLIDE_STEP_H,X + + jmp no_effect + + ;============================== + ; Effect #3 -- Sample Position + ;============================== +effect_3: + cmp #$3 + bne effect_4 + + lda (PATTERN_L),Y ; load byte, set as sample position + iny + sta note_a+NOTE_SAMPLE_POSITION,X + + bne no_effect ; branch always + + ;============================== + ; Effect #4 -- Ornament Position + ;============================== +effect_4: + cmp #$4 + bne effect_5 + + lda (PATTERN_L),Y ; load byte, set as ornament position + iny + sta note_a+NOTE_ORNAMENT_POSITION,X + + bne no_effect ; branch always + + ;============================== + ; Effect #5 -- Vibrato + ;============================== +effect_5: + cmp #$5 + bne effect_8 + + lda (PATTERN_L),Y ; load byte, set as onoff delay + iny + sta note_a+NOTE_ONOFF_DELAY,X + sta note_a+NOTE_ONOFF,X + + lda (PATTERN_L),Y ; load byte, set as offon delay + iny + sta note_a+NOTE_OFFON_DELAY,X + + lda #0 + sta note_a+NOTE_TONE_SLIDE_COUNT,X + sta note_a+NOTE_TONE_SLIDING_L,X + sta note_a+NOTE_TONE_SLIDING_H,X + + beq no_effect ; branch always + + ;============================== + ; Effect #8 -- Envelope Down + ;============================== +effect_8: + cmp #$8 + bne effect_9 + + ; delay + lda (PATTERN_L),Y ; load byte, set as speed + iny + sta pt3_envelope_delay_smc+1 + sta pt3_envelope_delay_orig_smc+1 + + ; low value + lda (PATTERN_L),Y ; load byte, set as low + iny + sta pt3_envelope_slide_add_l_smc+1 + + ; high value + lda (PATTERN_L),Y ; load byte, set as high + iny + sta pt3_envelope_slide_add_h_smc+1 + + bne no_effect ; branch always + + ;============================== + ; Effect #9 -- Set Speed + ;============================== +effect_9: + cmp #$9 + bne no_effect + + lda (PATTERN_L),Y ; load byte, set as speed + iny + sta pt3_speed_smc+1 + +no_effect: + + ;================================ + ; add y into the address pointer + + clc + tya + adc note_a+NOTE_ADDR_L,X + sta note_a+NOTE_ADDR_L,X + lda #0 + adc note_a+NOTE_ADDR_H,X + sta note_a+NOTE_ADDR_H,X + sta PATTERN_H + + rts + + + ;======================================= + ; Set Envelope + ;======================================= + ; pulls out common code from $1X and $BX + ; commands + + ; A = new envelope type + +set_envelope: + + sta pt3_envelope_type_smc+1 ; 4 + +; give fake old to force update? maybe only needed if printing? +; pt3->envelope_type_old=0x78; + + lda #$78 ; 2 + sta pt3_envelope_type_old_smc+1 ; 4 + + ; get next byte + iny ; 2 + lda (PATTERN_L),Y ; 5+ + sta pt3_envelope_period_h_smc+1 ; 4 + + iny ; 2 + lda (PATTERN_L),Y ; 5+ + sta pt3_envelope_period_l_smc+1 ; 4 + + lda #1 ; 2 + sta note_a+NOTE_ENVELOPE_ENABLED,X ; envelope_enabled=1 ; 5 + lsr ; 2 + sta note_a+NOTE_ORNAMENT_POSITION,X ; ornament_position=0 ; 5 + sta pt3_envelope_delay_smc+1 ; envelope_delay=0 ; 4 + sta pt3_envelope_slide_l_smc+1 ; envelope_slide=0 ; 4 + sta pt3_envelope_slide_h_smc+1 ; 4 + + rts ; 6 + ;=========== + ; 64 + + ;======================== + ; reset note + ;======================== + ; common code from the decode note code + +reset_note: + lda #0 ; 2 + sta note_a+NOTE_SAMPLE_POSITION,X ; sample_position=0 ; 5 + sta note_a+NOTE_AMPLITUDE_SLIDING,X ; amplitude_sliding=0 ; 5 + sta note_a+NOTE_NOISE_SLIDING,X ; noise_sliding=0 ; 5 + sta note_a+NOTE_ENVELOPE_SLIDING,X ; envelope_sliding=0 ; 5 + sta note_a+NOTE_ORNAMENT_POSITION,X ; ornament_position=0 ; 5 + sta note_a+NOTE_TONE_SLIDE_COUNT,X ; tone_slide_count=0 ; 5 + sta note_a+NOTE_TONE_SLIDING_L,X ; tone_sliding=0 ; 5 + sta note_a+NOTE_TONE_SLIDING_H,X ; 5 + sta note_a+NOTE_TONE_ACCUMULATOR_L,X ; tone_accumulator=0 ; 5 + sta note_a+NOTE_TONE_ACCUMULATOR_H,X ; 5 + sta note_a+NOTE_ONOFF,X ; onoff=0; ; 5 + + rts ; 6 + ;============ + ; 69 + + + + + + + ;===================================== + ; Set Pattern + ;===================================== + ; FIXME: inline this? we do call it from outside + ; in the player note length code + +is_done: + ; done with song, set it to non-zero + sta DONE_SONG ; 3 + rts ; 6 + +pt3_set_pattern: + + ; Lookup current pattern in pattern table +current_pattern_smc: + ldy #$d1 ; 2 + lda PT3_LOC+PT3_PATTERN_TABLE,Y ; 4+ + + ; if value is $FF we are at the end of the song + cmp #$ff ; 2 + beq is_done ; 2/3 + + ;============ + ; 20 if end + +not_done: + + ; set up the three pattern address pointers + + asl ; mul pattern offset by two, as word sized ; 2 + tay ; 2 + + ; point PATTERN_H/PATTERN_L to the pattern address table + + clc ; 2 + lda PT3_LOC+PT3_PATTERN_LOC_L ; 4 + sta PATTERN_L ; 3 + lda PT3_LOC+PT3_PATTERN_LOC_H ; 4 + adc #>PT3_LOC ; assume page boundary ; 2 + sta PATTERN_H ; 3 + + ; First 16-bits points to the Channel A address + lda (PATTERN_L),Y ; 5+ + sta note_a+NOTE_ADDR_L ; 4 + iny ; 2 + lda (PATTERN_L),Y ; 5+ + adc #>PT3_LOC ; assume page boundary ; 2 + sta note_a+NOTE_ADDR_H ; 4 + iny ; 2 + + ; Next 16-bits points to the Channel B address + lda (PATTERN_L),Y ; 5+ + sta note_b+NOTE_ADDR_L ; 4 + iny ; 2 + lda (PATTERN_L),Y ; 5+ + adc #>PT3_LOC ; assume page boundary ; 2 + sta note_b+NOTE_ADDR_H ; 4 + iny ; 2 + + ; Next 16-bits points to the Channel C address + lda (PATTERN_L),Y ; 5+ + sta note_c+NOTE_ADDR_L ; 4 + iny ; 2 + lda (PATTERN_L),Y ; 5+ + adc #>PT3_LOC ; assume page boundary ; 2 + sta note_c+NOTE_ADDR_H ; 4 + + ; clear out the noise channel + lda #0 ; 2 + sta pt3_noise_period_smc+1 ; 4 + + ; Set all three channels as active + ; FIXME: num_channels, may need to be 6 if doing 6-channel pt3? + lda #3 ; 2 + sta pt3_pattern_done_smc+1 ; 4 + + rts ; 6 + + + + ;===================================== + ; pt3 make frame + ;===================================== + ; update pattern or line if necessary + ; then calculate the values for the next frame + + ;========================== + ; pattern done early! + +early_end: + inc current_pattern_smc+1 ; increment pattern ; 6 + sta current_line_smc+1 ; 4 + sta current_subframe_smc+1 ; 4 + +check_subframe: + lda current_subframe_smc+1 ; 4 + bne pattern_good ; 2/3 + + ; load a new pattern in + jsr pt3_set_pattern ;6+? + + lda DONE_SONG ; 3 + beq pattern_good ; 2/3 + rts ; 6 + +pt3_make_frame: + + ; see if we need a new pattern + ; we do if line==0 and subframe==0 + ; allow fallthrough where possible +current_line_smc: + lda #$d1 ; 2 + beq check_subframe ; 2/3 + +pattern_good: + + ; see if we need a new line + +current_subframe_smc: + lda #$d1 ; 2 + bne line_good ; 2/3 + + ; decode a new line + jsr pt3_decode_line ; 6+? + + ; check if pattern done early +pt3_pattern_done_smc: + lda #$d1 ; 2 + beq early_end ; 2/3 + +line_good: + + ; Increment everything + + inc current_subframe_smc+1 ; subframe++ ; 6 + lda current_subframe_smc+1 ; 4 + + ; if we hit pt3_speed, move to next +pt3_speed_smc: + eor #$d1 ; 2 + bne do_frame ; 2/3 + +next_line: + sta current_subframe_smc+1 ; reset subframe to 0 ; 4 + + inc current_line_smc+1 ; and increment line ; 6 + lda current_line_smc+1 ; 4 + + eor #64 ; always end at 64. ; 2 + bne do_frame ; is this always needed? ; 2/3 + +next_pattern: + sta current_line_smc+1 ; reset line to 0 ; 4 + + inc current_pattern_smc+1 ; increment pattern ; 6 + +do_frame: + ; AY-3-8910 register summary + ; + ; R0/R1 = A period low/high + ; R2/R3 = B period low/high + ; R4/R5 = C period low/high + ; R6 = Noise period + ; R7 = Enable XX Noise=!CBA Tone=!CBA + ; R8/R9/R10 = Channel A/B/C amplitude M3210, M=envelope enable + ; R11/R12 = Envelope Period low/high + ; R13 = Envelope Shape, 0xff means don't write + ; R14/R15 = I/O (ignored) + + ldx #0 ; needed ; 2 + stx PT3_MIXER_VAL ; 3 + stx pt3_envelope_add_smc+1 ; 4 + + ;;ldx #(NOTE_STRUCT_SIZE*0) ; Note A ; 2 + jsr calculate_note ; 6+? + ldx #(NOTE_STRUCT_SIZE*1) ; Note B ; 2 + jsr calculate_note ; 6+? + ldx #(NOTE_STRUCT_SIZE*2) ; Note C ; 2 + jsr calculate_note ; 6+? + +convert_177_smc1: + sec ; 2 + + ; Load up the Frequency Registers + + lda note_a+NOTE_TONE_L ; Note A Period L ; 4 + sta AY_REGISTERS+0 ; into R0 ; 3 + + lda note_a+NOTE_TONE_H ; Note A Period H ; 4 + sta AY_REGISTERS+1 ; into R1 ; 3 + lda note_a+NOTE_TONE_L ; Note A Period L ; 4 + bcc no_scale_a ; 2/3 + + ; Convert from 1.77MHz to 1MHz by multiplying by 9/16 + + ; conversion costs 100 cycles! + + ; first multiply by 8 + asl ; 2 + rol AY_REGISTERS+1 ; 5 + asl ; 2 + rol AY_REGISTERS+1 ; 5 + asl ; 2 + rol AY_REGISTERS+1 ; 5 + + ; add in original to get 9 + clc ; 2 + adc note_a+NOTE_TONE_L ; 4 + sta AY_REGISTERS+0 ; 3 + lda note_a+NOTE_TONE_H ; 4 + adc AY_REGISTERS+1 ; 3 + + ; divide by 16 to get proper value + ror ; 2 + ror AY_REGISTERS+0 ; 5 + ror ; 2 + ror AY_REGISTERS+0 ; 5 + ror ; 2 + ror AY_REGISTERS+0 ; 5 + ror ; 2 + ror AY_REGISTERS+0 ; 5 + and #$0f ; 2 + sta AY_REGISTERS+1 ; 3 + +no_scale_a: + +convert_177_smc2: + sec ; 2 + + lda note_b+NOTE_TONE_L ; Note B Period L ; 4 + sta AY_REGISTERS+2 ; into R2 ; 3 + + lda note_b+NOTE_TONE_H ; Note B Period H ; 4 + sta AY_REGISTERS+3 ; into R3 ; 3 + lda note_b+NOTE_TONE_L ; Note B Period L ; 4 + bcc no_scale_b ; 2/3 + + ; Convert from 1.77MHz to 1MHz by multiplying by 9/16 + + ; first multiply by 8 + asl ; 2 + rol AY_REGISTERS+3 ; 5 + asl ; 2 + rol AY_REGISTERS+3 ; 5 + asl ; 2 + rol AY_REGISTERS+3 ; 5 + + ; add in original to get 9 + clc ; 2 + adc note_b+NOTE_TONE_L ; 4 + sta AY_REGISTERS+2 ; 3 + lda note_b+NOTE_TONE_H ; 4 + adc AY_REGISTERS+3 ; 3 + + ; divide by 16 to get proper value + ror ; 2 + ror AY_REGISTERS+2 ; 5 + ror ; 2 + ror AY_REGISTERS+2 ; 5 + ror ; 2 + ror AY_REGISTERS+2 ; 5 + ror ; 2 + ror AY_REGISTERS+2 ; 5 + and #$0f ; 2 + sta AY_REGISTERS+3 ; 3 + +no_scale_b: + +convert_177_smc3: + sec ; 2 + + lda note_c+NOTE_TONE_L ; Note C Period L ; 4 + sta AY_REGISTERS+4 ; into R4 ; 3 + lda note_c+NOTE_TONE_H ; Note C Period H ; 4 + sta AY_REGISTERS+5 ; into R5 ; 3 + lda note_c+NOTE_TONE_L ; Note C Period L ; 4 + bcc no_scale_c ; 2/3 + + ; Convert from 1.77MHz to 1MHz by multiplying by 9/16 + + ; first multiply by 8 + asl ; 2 + rol AY_REGISTERS+5 ; 5 + asl ; 2 + rol AY_REGISTERS+5 ; 5 + asl ; 2 + rol AY_REGISTERS+5 ; 5 + + ; add in original to get 9 + clc ; 2 + adc note_c+NOTE_TONE_L ; 4 + sta AY_REGISTERS+4 ; 3 + lda note_c+NOTE_TONE_H ; 4 + adc AY_REGISTERS+5 ; 3 + + ; divide by 16 to get proper value + ror ; 2 + ror AY_REGISTERS+4 ; 5 + ror ; 2 + ror AY_REGISTERS+4 ; 5 + ror ; 2 + ror AY_REGISTERS+4 ; 5 + ror ; 2 + ror AY_REGISTERS+4 ; 5 + and #$0f ; 2 + sta AY_REGISTERS+5 ; 3 + +no_scale_c: + + + ; Noise + ; frame[6]= (pt3->noise_period+pt3->noise_add)&0x1f; + clc ; 2 +pt3_noise_period_smc: + lda #$d1 ; 2 +pt3_noise_add_smc: + adc #$d1 ; 2 + and #$1f ; 2 + sta AY_REGISTERS+6 ; 3 + +convert_177_smc4: + sec ; 2 + bcc no_scale_n ; 2/3 + + ; Convert from 1.77MHz to 1MHz by multiplying by 9/16 + + ; first multiply by 8 + asl ; 2 + asl ; 2 + asl ; 2 + + ; add in original to get 9 + adc AY_REGISTERS+6 ; 3 + + ; divide by 16 to get proper value + ror ; 2 + ror ; 2 + ror ; 2 + ror ; 2 + and #$1f ; 2 + +no_scale_n: + sta AY_REGISTERS+6 ; 3 + + ;======================= + ; Mixer + + ; PT3_MIXER_VAL is already in AY_REGISTERS+7 + + ;======================= + ; Amplitudes + + lda note_a+NOTE_AMPLITUDE ; 3 + sta AY_REGISTERS+8 ; 3 + lda note_b+NOTE_AMPLITUDE ; 3 + sta AY_REGISTERS+9 ; 3 + lda note_c+NOTE_AMPLITUDE ; 3 + sta AY_REGISTERS+10 ; 3 + + ;====================================== + ; Envelope period + ; result=period+add+slide (16-bits) + clc ; 2 +pt3_envelope_period_l_smc: + lda #$d1 ; 2 +pt3_envelope_add_smc: + adc #$d1 ; 2 + tay ; 2 +pt3_envelope_period_h_smc: + lda #$d1 ; 2 + adc #0 ; 2 + tax ; 2 + + clc ; 2 + tya ; 2 +pt3_envelope_slide_l_smc: + adc #$d1 ; 2 + sta AY_REGISTERS+11 ; 3 + txa ; 2 +pt3_envelope_slide_h_smc: + adc #$d1 ; 2 + sta AY_REGISTERS+12 ; 3 + +convert_177_smc5: + sec + bcc no_scale_e ; 2/3 + + ; Convert from 1.77MHz to 1MHz by multiplying by 9/16 + + tay ; 2 + ; first multiply by 8 + lda AY_REGISTERS+11 ; 3 + asl ; 2 + rol AY_REGISTERS+12 ; 5 + asl ; 2 + rol AY_REGISTERS+12 ; 5 + asl ; 2 + rol AY_REGISTERS+12 ; 5 + + ; add in original to get 9 + clc ; 2 + adc AY_REGISTERS+11 ; 3 + sta AY_REGISTERS+11 ; 3 + tya ; 2 + adc AY_REGISTERS+12 ; 3 + + ; divide by 16 to get proper value + ror ; 2 + ror AY_REGISTERS+11 ; 5 + ror ; 2 + ror AY_REGISTERS+11 ; 5 + ror ; 2 + ror AY_REGISTERS+11 ; 5 + ror ; 2 + ror AY_REGISTERS+11 ; 5 + and #$0f ; 2 + sta AY_REGISTERS+12 ; 3 + +no_scale_e: + + ;======================== + ; Envelope shape + +pt3_envelope_type_smc: + lda #$d1 ; 2 +pt3_envelope_type_old_smc: + cmp #$d1 ; 2 + sta pt3_envelope_type_old_smc+1; copy old to new ; 4 + bne envelope_diff ; 2/3 +envelope_same: + lda #$ff ; if same, store $ff ; 2 +envelope_diff: + sta AY_REGISTERS+13 ; 3 + + + + ;============================== + ; end-of-frame envelope update + ;============================== + +pt3_envelope_delay_smc: + lda #$d1 ; 2 + beq done_do_frame ; assume can't be negative? ; 2/3 + ; do this if envelope_delay>0 + dec pt3_envelope_delay_smc+1 ; 6 + bne done_do_frame ; 2/3 + ; only do if we hit 0 +pt3_envelope_delay_orig_smc: + lda #$d1 ; reset envelope delay ; 2 + sta pt3_envelope_delay_smc+1 ; 4 + + clc ; 16-bit add ; 2 + lda pt3_envelope_slide_l_smc+1 ; 4 +pt3_envelope_slide_add_l_smc: + adc #$d1 ; 2 + sta pt3_envelope_slide_l_smc+1 ; 4 + lda pt3_envelope_slide_h_smc+1 ; 4 +pt3_envelope_slide_add_h_smc: + adc #$d1 ; 2 + sta pt3_envelope_slide_h_smc+1 ; 4 + +done_do_frame: + + rts ; 6 + + +; note, you might have slightly better performance if these are aligned +; so that loads don't have to cross page boundaries + +NoteTable_high: + .res 96,0 +NoteTable_low: + .res 96,0 + +VolumeTable: + .res 256,0 + + +pt3_lib_end: diff --git a/demos/outline2021/demo/pt3_lib_init.s b/demos/outline2021/demo/pt3_lib_init.s new file mode 100644 index 00000000..037b1ade --- /dev/null +++ b/demos/outline2021/demo/pt3_lib_init.s @@ -0,0 +1,575 @@ +; pt3_lib_init.s + +; Initialize a song + +; this is done before song starts playing so it is not +; as performance / timing critical + + ;==================================== + ; pt3_init_song + ;==================================== + ; +pt3_init_song: + + lda #$0 + sta DONE_SONG ; 3 + ldx #(end_vars-begin_vars) +zero_song_structs_loop: + dex + sta note_a,X + bne zero_song_structs_loop + + sta pt3_noise_period_smc+1 ; 4 + sta pt3_noise_add_smc+1 ; 4 + + sta pt3_envelope_period_l_smc+1 ; 4 + sta pt3_envelope_period_h_smc+1 ; 4 + sta pt3_envelope_slide_l_smc+1 ; 4 + sta pt3_envelope_slide_h_smc+1 ; 4 + sta pt3_envelope_slide_add_l_smc+1 ; 4 + sta pt3_envelope_slide_add_h_smc+1 ; 4 + sta pt3_envelope_add_smc+1 ; 4 + sta pt3_envelope_type_smc+1 ; 4 + sta pt3_envelope_type_old_smc+1 ; 4 + sta pt3_envelope_delay_smc+1 ; 4 + sta pt3_envelope_delay_orig_smc+1 ; 4 + + sta PT3_MIXER_VAL ; 3 + + sta current_pattern_smc+1 ; 4 + sta current_line_smc+1 ; 4 + sta current_subframe_smc+1 ; 4 + + lda #$f ; 2 + sta note_a+NOTE_VOLUME ; 4 + sta note_b+NOTE_VOLUME ; 4 + sta note_c+NOTE_VOLUME ; 4 + + ; default ornament/sample in A + ; X is zero coming in here + ;ldx #(NOTE_STRUCT_SIZE*0) ; 2 + jsr load_ornament0_sample1 ; 6+93 + + ; default ornament/sample in B + ldx #(NOTE_STRUCT_SIZE*1) ; 2 + jsr load_ornament0_sample1 ; 6+93 + + ; default ornament/sample in C + ldx #(NOTE_STRUCT_SIZE*2) ; 2 + jsr load_ornament0_sample1 ; 6+93 + + ;======================= + ; load default speed + + lda PT3_LOC+PT3_SPEED ; 4 + sta pt3_speed_smc+1 ; 4 + + ;======================= + ; load loop + + lda PT3_LOC+PT3_LOOP ; 4 + sta pt3_loop_smc+1 ; 4 + + + ;======================== + ;======================== + ; set up note/freq table + ; this saves some space and makes things marginally faster longrun + ;======================== + ;======================== + ; note (heh) that there are separate tables if version 3.3 + ; but we are going to assume we are only going to be playing + ; newer 3.4+ version files so only need the newer tables + + ldx PT3_LOC+PT3_HEADER_FREQUENCY ; 4 + beq use_freq_table_0 + dex + beq use_freq_table_1 + dex + beq use_freq_table_2 + ; fallthrough (freq table 3) + +use_freq_table_3: + ;================================================= + ; Create Table #3, v4+, "PT3NoteTable_REAL_34_35" + ;================================================= + + ldy #11 ; !2 +freq_table_3_copy_loop: + ; note, high lookup almost same as 2v4, just need to adjust one value + + lda base2_v4_high,Y ; !3 + sta NoteTable_high,Y ; !3 + lda base3_low,Y ; !3 + sta NoteTable_low,Y ; !3 + dey ; !1 + bpl freq_table_3_copy_loop ; !2 + + dec NoteTable_high ; adjust to right value + + jsr NoteTablePropogate ; !3 + + lda #table3_v4_adjust + sta note_table_adjust_smc+2 + + jsr NoteTableAdjust + + jmp done_set_freq_table + + + +use_freq_table_2: + ;================================================= + ; Create Table #2, v4+, "PT3NoteTable_ASM_34_35" + ;================================================= + + ldy #11 +freq_table_2_copy_loop: + lda base2_v4_high,Y + sta NoteTable_high,Y + lda base2_v4_low,Y + sta NoteTable_low,Y + dey + bpl freq_table_2_copy_loop + + jsr NoteTablePropogate ; !3 + + lda #table2_v4_adjust + sta note_table_adjust_smc+2 + + jsr NoteTableAdjust + + jmp done_set_freq_table + +use_freq_table_1: + ;================================================= + ; Create Table #1, "PT3NoteTable_ST" + ;================================================= + + ldy #11 +freq_table_1_copy_loop: + lda base1_high,Y + sta NoteTable_high,Y + lda base1_low,Y + sta NoteTable_low,Y + dey + bpl freq_table_1_copy_loop + + jsr NoteTablePropogate ; !3 + + ; last adjustments + lda #$FD ; Tone[23]=$3FD + sta NoteTable_low+23 + dec NoteTable_low+46 ; Tone[46]-=1; + + + jmp done_set_freq_table + + +use_freq_table_0: + ;================================================= + ; Create Table #0, "PT3NoteTable_PT_34_35" + ;================================================= + + ldy #11 +freq_table_0_copy_loop: + lda base0_v4_high,Y + sta NoteTable_high,Y + lda base0_v4_low,Y + sta NoteTable_low,Y + dey + bpl freq_table_0_copy_loop + + jsr NoteTablePropogate ; !3 + + lda #table0_v4_adjust + sta note_table_adjust_smc+2 + + jsr NoteTableAdjust + + +done_set_freq_table: + + + ;====================== + ; calculate version + ldx #6 ; 2 + lda PT3_LOC+PT3_VERSION ; 4 + sec ; 2 + sbc #'0' ; 2 + cmp #9 ; 2 + bcs not_ascii_number ; bge ; 2/3 + tax ; 2 + +not_ascii_number: + + ; adjust version<6 SMC code in the slide code + + ; FIXME: I am sure there's a more clever way to do this + + lda #$2C ; BIT ; 2 + cpx #$6 ; 2 + bcs version_greater_than_or_equal_6 ; bgt ; 3 + ; less than 6, jump + ; also carry is known to be clear + adc #$20 ; BIT->JMP 2C->4C ; 2 +version_greater_than_or_equal_6: + sta version_smc ; 4 + +pick_volume_table: + + ;======================= + ; Pick which volume number, based on version + + ; if (PlParams.PT3.PT3_Version <= 4) + + cpx #5 ; 2 + + ; carry clear = 3.3/3.4 table + ; carry set = 3.5 table + + ;========================== + ; VolTableCreator + ;========================== + ; Creates the appropriate volume table + ; based on z80 code by Ivan Roshin ZXAYHOBETA/VTII10bG.asm + ; + + ; Called with carry==0 for 3.3/3.4 table + ; Called with carry==1 for 3.5 table + + ; 177f-1932 = 435 bytes, not that much better than 512 of lookup + + +VolTableCreator: + + ; Init initial variables + lda #$0 + sta z80_d_smc+1 + ldy #$11 + + ; Set up self modify + + ldx #$2A ; ROL for self-modify + bcs vol_type_35 + +vol_type_33: + + ; For older table, we set initial conditions a bit + ; different + + dey + tya + + ldx #$ea ; NOP for self modify + +vol_type_35: + sty z80_l_smc+1 ; l=16 or 17 + sta z80_e_smc+1 ; e=16 or 0 + stx vol_smc ; set the self-modify code + + ldy #16 ; skip first row, all zeros + ldx #16 ; c=16 +vol_outer: + clc ; add HL,DE +z80_l_smc: + lda #$d1 +z80_e_smc: + adc #$d1 + sta z80_e_smc+1 + lda #0 +z80_d_smc: + adc #$d1 + sta z80_d_smc+1 ; carry is important + + ; sbc hl,hl + lda #0 + adc #$ff + eor #$ff + +vol_write: + sta z80_h_smc+1 + pha + +vol_inner: + pla + pha + +vol_smc: + nop ; nop or ROL depending + +z80_h_smc: + lda #$d1 + + adc #$0 ; a=a+carry; + + sta VolumeTable,Y + iny + + pla ; add HL,DE + adc z80_e_smc+1 + pha + lda z80_h_smc+1 + adc z80_d_smc+1 + sta z80_h_smc+1 + + inx ; inc C + txa ; a=c + and #$f + bne vol_inner + + + pla + + lda z80_e_smc+1 ; a=e + cmp #$77 + bne vol_m3 + + inc z80_e_smc+1 + +vol_m3: + txa ; a=c + bne vol_outer + +vol_done: + rts + + + ;========================================= + ; copy note table seed to proper location + ;========================================= + +; faster inlined + +;NoteTableCopy: + +; ldy #11 ; !2 +;note_table_copy_loop: +;ntc_smc1: +; lda base1_high,Y ; !3 +; sta NoteTable_high,Y ; !3 +;ntc_smc2: +; lda base1_low,Y ; !3 +; sta NoteTable_low,Y ; !3 +; dey ; !1 +; bpl note_table_copy_loop ; !2 +; rts ; !1 + + + ;========================================== + ; propogate the freq down, dividing by two + ;========================================== +NoteTablePropogate: + + ldy #0 +note_table_propogate_loop: + clc + lda NoteTable_high,Y + ror + sta NoteTable_high+12,Y + + lda NoteTable_low,Y + ror + sta NoteTable_low+12,Y + + iny + cpy #84 + bne note_table_propogate_loop + + rts + + + ;================================================ + ; propogation isn't enough, various values + ; are often off by one, so adjust using a bitmask + ;================================================ +NoteTableAdjust: + + ldx #0 +note_table_adjust_outer: + +note_table_adjust_smc: + lda table0_v4_adjust,X + sta PT3_TEMP + + ; reset smc + lda #NoteTable_low + sta ntl_smc+2 + + + ldy #7 +note_table_adjust_inner: + ror PT3_TEMP + bcc note_table_skip_adjust + +ntl_smc: + inc NoteTable_low,X + +note_table_skip_adjust: + clc + lda #12 + adc ntl_smc+1 + sta ntl_smc+1 + lda #0 + adc ntl_smc+2 ; unnecessary if aligned + sta ntl_smc+2 + +skip_adjust_done: + dey + bpl note_table_adjust_inner + + inx + cpx #12 + bne note_table_adjust_outer + + rts + + +;base0_v3_high: +;.byte $0C,$0B,$0A,$0A,$09,$09,$08,$08,$07,$07,$06,$06 +;base0_v3_low: +;.byte $21,$73,$CE,$33,$A0,$16,$93,$18,$A4,$36,$CE,$6D + +; note: same as base0_v3_high +base0_v4_high: +.byte $0C,$0B,$0A,$0A,$09,$09,$08,$08,$07,$07,$06,$06 +base0_v4_low: +.byte $22,$73,$CF,$33,$A1,$17,$94,$19,$A4,$37,$CF,$6D + +base1_high: +.byte $0E,$0E,$0D,$0C,$0B,$0B,$0A,$09,$09,$08,$08,$07 +base1_low: +.byte $F8,$10,$60,$80,$D8,$28,$88,$F0,$60,$E0,$58,$E0 + +;base2_v3_high: +;.byte $0D,$0C,$0B,$0B,$0A,$09,$09,$08,$08,$07,$07,$07 +;base2_v3_low: +;.byte $3E,$80,$CC,$22,$82,$EC,$5C,$D6,$58,$E0,$6E,$04 + +; note almost same as above +base2_v4_high: +.byte $0D,$0C,$0B,$0A,$0A,$09,$09,$08,$08,$07,$07,$06 +base2_v4_low: +.byte $10,$55,$A4,$FC,$5F,$CA,$3D,$B8,$3B,$C5,$55,$EC + +; note almost same as above +;base3_high: +;.byte $0C,$0C,$0B,$0A,$0A,$09,$09,$08,$08,$07,$07,$06 +base3_low: +.byte $DA,$22,$73,$CF,$33,$A1,$17,$94,$19,$A4,$37,$CF + + +; Adjustment factors +table0_v4_adjust: +.byte $40,$e6,$9c,$66,$40,$2c,$20,$30,$48,$6c,$1c,$5a + +table2_v4_adjust: +.byte $20,$a8,$40,$f8,$bc,$90,$78,$70,$74,$08,$2a,$50 + +table3_v4_adjust: +.byte $B4,$40,$e6,$9c,$66,$40,$2c,$20,$30,$48,$6c,$1c + + +; Table #1 of Pro Tracker 3.3x - 3.5x +;PT3NoteTable_ST_high: +;.byte $0E,$0E,$0D,$0C,$0B,$0B,$0A,$09 +;.byte $09,$08,$08,$07,$07,$07,$06,$06 +;.byte $05,$05,$05,$04,$04,$04,$04,$03 +;.byte $03,$03,$03,$03,$02,$02,$02,$02 +;.byte $02,$02,$02,$01,$01,$01,$01,$01 +;.byte $01,$01,$01,$01,$01,$01,$01,$00 +;.byte $00,$00,$00,$00,$00,$00,$00,$00 +;.byte $00,$00,$00,$00,$00,$00,$00,$00 +;.byte $00,$00,$00,$00,$00,$00,$00,$00 +;.byte $00,$00,$00,$00,$00,$00,$00,$00 +;.byte $00,$00,$00,$00,$00,$00,$00,$00 +;.byte $00,$00,$00,$00,$00,$00,$00,$00 + +;PT3NoteTable_ST_low: +;.byte $F8,$10,$60,$80,$D8,$28,$88,$F0 +;.byte $60,$E0,$58,$E0,$7C,$08,$B0,$40 +;.byte $EC,$94,$44,$F8,$B0,$70,$2C,$FD +;.byte $BE,$84,$58,$20,$F6,$CA,$A2,$7C +;.byte $58,$38,$16,$F8,$DF,$C2,$AC,$90 +;.byte $7B,$65,$51,$3E,$2C,$1C,$0A,$FC +;.byte $EF,$E1,$D6,$C8,$BD,$B2,$A8,$9F +;.byte $96,$8E,$85,$7E,$77,$70,$6B,$64 +;.byte $5E,$59,$54,$4F,$4B,$47,$42,$3F +;.byte $3B,$38,$35,$32,$2F,$2C,$2A,$27 +;.byte $25,$23,$21,$1F,$1D,$1C,$1A,$19 +;.byte $17,$16,$15,$13,$12,$11,$10,$0F + + +; Table #2 of Pro Tracker 3.4x - 3.5x +;PT3NoteTable_ASM_34_35_high: +;.byte $0D,$0C,$0B,$0A,$0A,$09,$09,$08 +;.byte $08,$07,$07,$06,$06,$06,$05,$05 +;.byte $05,$04,$04,$04,$04,$03,$03,$03 +;.byte $03,$03,$02,$02,$02,$02,$02,$02 +;.byte $02,$01,$01,$01,$01,$01,$01,$01 +;.byte $01,$01,$01,$01,$01,$00,$00,$00 +;.byte $00,$00,$00,$00,$00,$00,$00,$00 +;.byte $00,$00,$00,$00,$00,$00,$00,$00 +;.byte $00,$00,$00,$00,$00,$00,$00,$00 +;.byte $00,$00,$00,$00,$00,$00,$00,$00 +;.byte $00,$00,$00,$00,$00,$00,$00,$00 +;.byte $00,$00,$00,$00,$00,$00,$00,$00 + +;PT3NoteTable_ASM_34_35_low: +;.byte $10,$55,$A4,$FC,$5F,$CA,$3D,$B8 +;.byte $3B,$C5,$55,$EC,$88,$2A,$D2,$7E +;.byte $2F,$E5,$9E,$5C,$1D,$E2,$AB,$76 +;.byte $44,$15,$E9,$BF,$98,$72,$4F,$2E +;.byte $0F,$F1,$D5,$BB,$A2,$8B,$74,$60 +;.byte $4C,$39,$28,$17,$07,$F9,$EB,$DD +;.byte $D1,$C5,$BA,$B0,$A6,$9D,$94,$8C +;.byte $84,$7C,$75,$6F,$69,$63,$5D,$58 +;.byte $53,$4E,$4A,$46,$42,$3E,$3B,$37 +;.byte $34,$31,$2F,$2C,$29,$27,$25,$23 +;.byte $21,$1F,$1D,$1C,$1A,$19,$17,$16 +;.byte $15,$14,$12,$11,$10,$0F,$0E,$0D + + +;PT3VolumeTable_33_34: +;.byte $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0 +;.byte $0,$0,$0,$0,$0,$0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1 +;.byte $0,$0,$0,$0,$0,$0,$1,$1,$1,$1,$1,$2,$2,$2,$2,$2 +;.byte $0,$0,$0,$0,$1,$1,$1,$1,$2,$2,$2,$2,$3,$3,$3,$3 +;.byte $0,$0,$0,$0,$1,$1,$1,$2,$2,$2,$3,$3,$3,$4,$4,$4 +;.byte $0,$0,$0,$1,$1,$1,$2,$2,$3,$3,$3,$4,$4,$4,$5,$5 +;.byte $0,$0,$0,$1,$1,$2,$2,$3,$3,$3,$4,$4,$5,$5,$6,$6 +;.byte $0,$0,$1,$1,$2,$2,$3,$3,$4,$4,$5,$5,$6,$6,$7,$7 +;.byte $0,$0,$1,$1,$2,$2,$3,$3,$4,$5,$5,$6,$6,$7,$7,$8 +;.byte $0,$0,$1,$1,$2,$3,$3,$4,$5,$5,$6,$6,$7,$8,$8,$9 +;.byte $0,$0,$1,$2,$2,$3,$4,$4,$5,$6,$6,$7,$8,$8,$9,$A +;.byte $0,$0,$1,$2,$3,$3,$4,$5,$6,$6,$7,$8,$9,$9,$A,$B +;.byte $0,$0,$1,$2,$3,$4,$4,$5,$6,$7,$8,$8,$9,$A,$B,$C +;.byte $0,$0,$1,$2,$3,$4,$5,$6,$7,$7,$8,$9,$A,$B,$C,$D +;.byte $0,$0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$A,$B,$C,$D,$E +;.byte $0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$A,$B,$C,$D,$E,$F + +;PT3VolumeTable_35: +;.byte $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0 +;.byte $0,$0,$0,$0,$0,$0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1 +;.byte $0,$0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1,$2,$2,$2,$2 +;.byte $0,$0,$0,$1,$1,$1,$1,$1,$2,$2,$2,$2,$2,$3,$3,$3 +;.byte $0,$0,$1,$1,$1,$1,$2,$2,$2,$2,$3,$3,$3,$3,$4,$4 +;.byte $0,$0,$1,$1,$1,$2,$2,$2,$3,$3,$3,$4,$4,$4,$5,$5 +;.byte $0,$0,$1,$1,$2,$2,$2,$3,$3,$4,$4,$4,$5,$5,$6,$6 +;.byte $0,$0,$1,$1,$2,$2,$3,$3,$4,$4,$5,$5,$6,$6,$7,$7 +;.byte $0,$1,$1,$2,$2,$3,$3,$4,$4,$5,$5,$6,$6,$7,$7,$8 +;.byte $0,$1,$1,$2,$2,$3,$4,$4,$5,$5,$6,$7,$7,$8,$8,$9 +;.byte $0,$1,$1,$2,$3,$3,$4,$5,$5,$6,$7,$7,$8,$9,$9,$A +;.byte $0,$1,$1,$2,$3,$4,$4,$5,$6,$7,$7,$8,$9,$A,$A,$B +;.byte $0,$1,$2,$2,$3,$4,$5,$6,$6,$7,$8,$9,$A,$A,$B,$C +;.byte $0,$1,$2,$3,$3,$4,$5,$6,$7,$8,$9,$A,$A,$B,$C,$D +;.byte $0,$1,$2,$3,$4,$5,$6,$7,$7,$8,$9,$A,$B,$C,$D,$E +;.byte $0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$A,$B,$C,$D,$E,$F diff --git a/demos/outline2021/demo/pt3_lib_irq_handler.s b/demos/outline2021/demo/pt3_lib_irq_handler.s new file mode 100644 index 00000000..0de5309a --- /dev/null +++ b/demos/outline2021/demo/pt3_lib_irq_handler.s @@ -0,0 +1,117 @@ + + +pt3_irq_handler: + +pt3_irq_smc1: + bit MOCK_6522_T1CL ; clear 6522 interrupt by reading T1C-L ; 4 + + lda DONE_PLAYING ; 3 + beq pt3_play_music ; if song done, don't play music ; 3/2nt + jmp done_pt3_irq_handler ; 3 + ;============ + ; 13 + +pt3_play_music: + + ; decode a frame of music + + jsr pt3_make_frame + + ; handle song over condition + lda DONE_SONG + beq mb_write_frame ; if not done, continue + + lda LOOP ; see if looping + beq move_to_next + +pt3_loop_smc: + lda #$d1 ; looping, move to loop location + ; non-zero to avoid the temptation + ; to merge with following lda #$0 + sta current_pattern_smc+1 + lda #$0 + sta current_line_smc+1 + sta current_subframe_smc+1 + sta DONE_SONG ; undo the next song + +; beq done_pt3_irq_handler ; branch always + beq pt3_play_music ; branch always + +move_to_next: + ; same as "press right" + ldx #$20 + jmp quiet_exit + + ;====================================== + ; Write frames to Mockingboard + ;====================================== + ; for speed could merge this into + ; the decode code + +mb_write_frame: + + + tax ; set up reg count ; 2 + ;============ + ; 2 + + ;================================== + ; loop through the 14 registers + ; reading the value, then write out + ;================================== + +mb_write_loop: + lda AY_REGISTERS,X ; load register value ; 4 + + ; special case R13. If it is 0xff, then don't update + ; otherwise might spuriously reset the envelope settings + + cpx #13 ; 2 + bne mb_not_13 ; 3/2nt + cmp #$ff ; 2 + beq mb_skip_13 ; 3/2nt + ;============ + ; typ 5 +mb_not_13: + + + ; address +pt3_irq_smc2: + stx MOCK_6522_ORA1 ; put address on PA1 ; 4 + stx MOCK_6522_ORA2 ; put address on PA2 ; 4 + ldy #MOCK_AY_LATCH_ADDR ; latch_address for PB1 ; 2 +pt3_irq_smc3: + sty MOCK_6522_ORB1 ; latch_address on PB1 ; 4 + sty MOCK_6522_ORB2 ; latch_address on PB2 ; 4 + ldy #MOCK_AY_INACTIVE ; go inactive ; 2 +pt3_irq_smc4: + sty MOCK_6522_ORB1 ; 4 + sty MOCK_6522_ORB2 ; 4 + + ; value +pt3_irq_smc5: + sta MOCK_6522_ORA1 ; put value on PA1 ; 4 + sta MOCK_6522_ORA2 ; put value on PA2 ; 4 + lda #MOCK_AY_WRITE ; ; 2 +pt3_irq_smc6: + sta MOCK_6522_ORB1 ; write on PB1 ; 4 + sty MOCK_6522_ORB1 ; 4 +pt3_irq_smc7: + sta MOCK_6522_ORB2 ; write on PB2 ; 4 + sty MOCK_6522_ORB2 ; 4 + ;=========== + ; 56 +mb_no_write: + inx ; point to next register ; 2 + cpx #14 ; if 14 we're done ; 2 + bmi mb_write_loop ; otherwise, loop ; 3/2nt + ;============ + ; 7 +mb_skip_13: + + + ;================================= + ; Finally done with this interrupt + ;================================= + +done_pt3_irq_handler: diff --git a/demos/outline2021/demo/pt3_lib_mockingboard_detect.s b/demos/outline2021/demo/pt3_lib_mockingboard_detect.s new file mode 100644 index 00000000..706d9407 --- /dev/null +++ b/demos/outline2021/demo/pt3_lib_mockingboard_detect.s @@ -0,0 +1,314 @@ +;=================================================================== +; code to detect mockingboard +;=================================================================== +; this isn't always easy +; my inclination is to just assume slot #4 but that isn't always realistic + +; code below based on "hw.mockingboard.a" from "Total Replay" + +;license:MIT +; By Andrew Roughan +; in the style of 4am for Total Replay +; +; Mockingboard support functions +; + +;------------------------------------------------------------------------------ +; HasMockingboard +; detect Mockingboard card by searching for 6522 timers across all slots +; access 6522 timers with deterministic cycle counts +; +; based on prior art in Mockingboard Developers Toolkit +; with optimisation from deater/french touch +; also takes into account FastChip //e clock difference +; +; in: none +; accelerators should be off +; out: C set if Mockingboard found in any slot +; if card was found, X = #$Cn where n is the slot number of the card +; C clear if no Mockingboard found +; other flags clobbered +; zp $65-$67 clobbered +; A/Y clobbered +;------------------------------------------------------------------------------ + +mockingboard_detect: + lda #$00 + sta MB_ADDR_L + ldx #$C7 ; start at slot #7 +mb_slot_loop: + stx MB_ADDR_H + ldy #$04 ; 6522 #1 $Cx04 + jsr mb_timer_check + bne mb_next_slot + ldy #$84 ; 6522 #2 $Cx84 + jsr mb_timer_check + bne mb_next_slot +mb_found: + sec ; found + rts + +mb_next_slot: + dex + cpx #$C0 + bne mb_slot_loop + + clc ; not found + rts + +mb_timer_check: + lda (MB_ADDR_L),Y ; read 6522 timer low byte + sta MB_VALUE + lda (MB_ADDR_L),Y ; second time + sec + sbc MB_VALUE + cmp #$F8 ; looking for (-)8 cycles between reads + beq mb_timer_check_done + cmp #$F7 ; FastChip //e clock is different +mb_timer_check_done: + rts + + + +;=================================================================== +; code to patch mockingboard if not in slot#4 +;=================================================================== +; this is the brute force version, we have to patch 39 locations +; see further below if you want to try a smaller, more dangerous, patch + +.if 0 +mockingboard_patch: + + lda MB_ADDR_H + + sta pt3_irq_smc1+2 ; 1 + + sta pt3_irq_smc2+2 ; 2 + sta pt3_irq_smc2+5 ; 3 + + sta pt3_irq_smc3+2 ; 4 + sta pt3_irq_smc3+5 ; 5 + + sta pt3_irq_smc4+2 ; 6 + sta pt3_irq_smc4+5 ; 7 + + sta pt3_irq_smc5+2 ; 8 + sta pt3_irq_smc5+5 ; 9 + + sta pt3_irq_smc6+2 ; 10 + sta pt3_irq_smc6+5 ; 11 + + sta pt3_irq_smc7+2 ; 12 + sta pt3_irq_smc7+5 ; 13 + + sta mock_init_smc1+2 ; 14 + sta mock_init_smc1+5 ; 15 + + sta mock_init_smc2+2 ; 16 + sta mock_init_smc2+5 ; 17 + + sta reset_ay_smc1+2 ; 18 + sta reset_ay_smc2+2 ; 19 + sta reset_ay_smc3+2 ; 20 + sta reset_ay_smc4+2 ; 21 + + sta write_ay_smc1+2 ; 22 + sta write_ay_smc1+5 ; 23 + + sta write_ay_smc2+2 ; 24 + sta write_ay_smc2+5 ; 25 + + sta write_ay_smc3+2 ; 26 + sta write_ay_smc3+5 ; 27 + + sta write_ay_smc4+2 ; 28 + sta write_ay_smc4+5 ; 29 + + sta write_ay_smc5+2 ; 30 + sta write_ay_smc5+5 ; 31 + + sta write_ay_smc6+2 ; 32 + sta write_ay_smc6+5 ; 33 + + sta setup_irq_smc1+2 ; 34 + sta setup_irq_smc2+2 ; 35 + sta setup_irq_smc3+2 ; 36 + sta setup_irq_smc4+2 ; 37 + sta setup_irq_smc5+2 ; 38 + sta setup_irq_smc6+2 ; 39 + + rts +.endif + +;=================================================================== +; dangerous code to patch mockingboard if not in slot#4 +;=================================================================== +; this code patches any $C4 value to the proper slot# if not slot4 +; this can be dangerous, it might over-write other important values +; that should be $C4 + +; safer ways to do this: +; only do this if 2 bytes after a LDA/STA/LDX/STX +; count total and if not 39 then print error message + +mockingboard_patch: + ; from mockingboard_init $1BBF + ; to done_pt3_irq_handler $1D85 + + ldx MB_ADDR_H + ldy #0 + + lda #mockingboard_init + sta MB_ADDR_H + +mb_patch_loop: + lda (MB_ADDR_L),Y + cmp #$C4 + bne mb_patch_nomatch + + txa + sta (MB_ADDR_L),Y +mb_patch_nomatch: + + inc MB_ADDR_L + lda MB_ADDR_L + bne mb_patch_oflo + inc MB_ADDR_H + +mb_patch_oflo: + lda MB_ADDR_H + cmp #>done_pt3_irq_handler + bne mb_patch_loop + lda MB_ADDR_L + cmp # Latch Address -> Inactive -> Write Data -> Inactive + + ;========================================= + ; Write Right/Left to save value AY-3-8910 + ;========================================= + ; register in X + ; value in MB_VALUE + +write_ay_both: + ; address + +write_ay_smc1: + stx MOCK_6522_ORA1 ; put address on PA1 ; 4 + stx MOCK_6522_ORA2 ; put address on PA2 ; 4 + lda #MOCK_AY_LATCH_ADDR ; latch_address on PB1 ; 2 +write_ay_smc2: + sta MOCK_6522_ORB1 ; latch_address on PB1 ; 4 + sta MOCK_6522_ORB2 ; latch_address on PB2 ; 4 + ldy #MOCK_AY_INACTIVE ; go inactive ; 2 +write_ay_smc3: + sty MOCK_6522_ORB1 ; 4 + sty MOCK_6522_ORB2 ; 4 + ;=========== + ; 28 + ; value + lda MB_VALUE ; 3 +write_ay_smc4: + sta MOCK_6522_ORA1 ; put value on PA1 ; 4 + sta MOCK_6522_ORA2 ; put value on PA2 ; 4 + lda #MOCK_AY_WRITE ; ; 2 +write_ay_smc5: + sta MOCK_6522_ORB1 ; write on PB1 ; 4 + sta MOCK_6522_ORB2 ; write on PB2 ; 4 +write_ay_smc6: + sty MOCK_6522_ORB1 ; 4 + sty MOCK_6522_ORB2 ; 4 + ;=========== + ; 29 + + rts ; 6 + ;=========== + ; 63 +write_ay_both_end: +;.assert >write_ay_both = >write_ay_both_end, error, "write_ay_both crosses page" + + ;======================================= + ; clear ay -- clear all 14 AY registers + ; should silence the card + ;======================================= + ; 7+(74*14)+5=1048 +clear_ay_both: + ldx #13 ; 2 + lda #0 ; 2 + sta MB_VALUE ; 3 +clear_ay_left_loop: + jsr write_ay_both ; 6+63 + dex ; 2 + bpl clear_ay_left_loop ; 3 + ; -1 + rts ; 6 + +clear_ay_end: +;.assert >clear_ay_both = >clear_ay_end, error, "clear_ay_both crosses page" + + ;============================= + ; Setup + ;============================= +mockingboard_setup_interrupt: + + ;=========================== + ; Check for Apple IIc + ;=========================== + ; it does interrupts differently + + lda $FBB3 ; IIe and newer is $06 + cmp #6 + beq apple_iie_or_newer + + jmp done_apple_detect +apple_iie_or_newer: + lda $FBC0 ; 0 on a IIc + bne done_apple_detect +apple_iic: + ; activate IIc mockingboard? + ; this might only be necessary to allow detection + ; I get the impression the Mockingboard 4c activates + ; when you access any of the 6522 ports in Slot 4 + lda #$ff + + ; don't bother patching these, IIc mockingboard always slot 4? + + sta MOCK_6522_DDRA1 + sta MOCK_6522_T1CL + + ; bypass the firmware interrupt handler + ; should we do this on IIe too? probably faster + + sei ; disable interrupts + lda $c08b ; disable ROM (enable language card) + lda $c08b + lda #interrupt_handler + sta $ffff + + lda #$EA ; nop out the "lda $45" in the irq hand + sta interrupt_smc + sta interrupt_smc+1 + +done_apple_detect: + + + ;========================= + ; Setup Interrupt Handler + ;========================= + ; Vector address goes to 0x3fe/0x3ff + ; FIXME: should chain any existing handler + + lda #interrupt_handler + sta $03ff + + ;============================ + ; Enable 50Hz clock on 6522 + ;============================ + + ; 4fe7 / 1e6 = .020s, 50Hz + + ; 9c40 / 1e6 = .040s, 25Hz + ; 411a / 1e6 = .016s, 60Hz + + sei ; disable interrupts just in case + + lda #$40 ; Continuous interrupts, don't touch PB7 +setup_irq_smc1: + sta MOCK_6522_ACR ; ACR register + lda #$7F ; clear all interrupt flags +setup_irq_smc2: + sta MOCK_6522_IER ; IER register (interrupt enable) + + lda #$C0 +setup_irq_smc3: + sta MOCK_6522_IFR ; IFR: 1100, enable interrupt on timer one oflow +setup_irq_smc4: + sta MOCK_6522_IER ; IER: 1100, enable timer one interrupt + + lda #$E7 +setup_irq_smc5: + sta MOCK_6522_T1CL ; write into low-order latch + lda #$4f +setup_irq_smc6: + sta MOCK_6522_T1CH ; write into high-order latch, + ; load both values into counter + ; clear interrupt and start counting + + rts diff --git a/demos/outline2021/demo/qboot.inc b/demos/outline2021/demo/qboot.inc new file mode 100644 index 00000000..83f4f0a3 --- /dev/null +++ b/demos/outline2021/demo/qboot.inc @@ -0,0 +1,8 @@ +seek = $1526 +driveon = $159D +driveoff = $1522 +load_new = $15AB +load_address=$15C4 +load_track=load_address+1 +load_sector=load_address+2 +load_length=load_address+3 diff --git a/demos/outline2021/demo/qboot_sector.s b/demos/outline2021/demo/qboot_sector.s new file mode 100644 index 00000000..af83f95b --- /dev/null +++ b/demos/outline2021/demo/qboot_sector.s @@ -0,0 +1,248 @@ +; fast seek/multi-read +; copyright (c) Peter Ferrie 2015-16 + + ; Paramaters for loading QLOAD + ; want to load it at $1600 + + sectors = 14 ; user-defined + firsttrk = 1 ; user-defined, first track to read + firstsec = 0 ; user-defined, first sector to read + address = $16 ; user-defined + entry = $1600 ; user-defined + version = 1 + + ;memory usage: + ;256 bytes ($200-2ff) static table + grouped = $200 + + ; stay away from interrupt vectors at $3fe !!! + + ;106 bytes ($300-369) static table + preshift = $300 + zvalue = $fd ; only during init + znibble = $fe ; only during init + zmask = $ff ; only during init + + WHICH_SLOT = $DA + +; $26/$27 sector read location (ROM) +; $3D sector number (ROM) + + +; at entry (at least on AppleWin) A=1, X=60 (slot<<4), Y=0 +; qkumba says cffa cards leave Y at $10 +; 26/27 = 00/09 (memory destination) +; 3D = 1 (sector) + + ; For Disk II booting, the firmware loads track0/sector0 + ; to $800 and then jumps to $801 + +.org $800 + .byte 1 ; number of sectors for ROM to load + +boot_entry: + ; this code loads two sectors up to $14/$15 + + ; it's full of qkumba magic so be careful + + ; in theory A=1 here on boot + ; A=3 second time we get called after loading $14 + ; A=5 third time we get called after loading $15 + + lsr ; check sector number + ; A=0, C=1 A=1,C=1 A=2,C=1 + tay ; Y=0 Y=1 Y=2 + adc #$13 ; A=$14 A=$15 A=$16 + sta $27 ; set destination for read to $1400 + + cmp #$16 + ; OLD 10 11 12 (1 1 1) + ; OLD be, bf, c0 (1011 1011 1100) + ; OLD so if hit $c000 we are done + + beq done_load_2 ; branch if loaded 2 + + inc $3d ; increment sector (faster to find) + + ; call to the read routine in proper slot + ; using rts to jump indirect to + ; $CX5C + + ; this routine reads sector in $3D on track in $41 + ; to address in $26/$27 + ; when it's done it jumps back to $801 + stx WHICH_SLOT ; save for later + + txa ; x is slot# << 4 + lsr + lsr + lsr + lsr + ora #$c0 ; slot to PROM base + pha + lda #$5b ;read-1 + pha + rts ; return used to call $CX5C in disk II ROM + +done_load_2: + + ; patch self modifying code for Q6L read + + txa + ora #$8c ; slot to Q6L + ; Q6L? + ; if slot 6, after this A is $EC + + ; Y should be 2 here +patch_loop: + iny + ldx patchtbl-3, Y + sta code_begin, X ; replace placeholders with Q6L + ; BE02 = EC? lda c0ec + ; so sets to c08c (Q6L) + + bne patch_loop + + ; patch self-modifying code for turning motor off + + and #$f8 ; MOTOROFF (c088) -> c0e8 + sta slotpatch7+1 + + ; patch self-modifying code for turning motor on + clc + adc #1 ; MOTORON (c089) -> c0e9 + sta slotpatch9+1 + + ; patch self-modifying code for phase off + + eor #9 ; PHASEOFF (c080) + sta slotpatch8+1 + + ldx #$3f + stx zmask + inx + ldy #$7f + + bne skip_ahead ; branch always + + ; pad with zeros until $839 + ; $839 is the entry point + ; adjusts address at $8FE to be entry point + ; jumps to boot 2 +;.res $839-* + +; lda #>(entry-1) +; pha +; lda #<(entry-1) +; pha +; jsr preread +; jmp $1000 ; stage2 entry point + +patchtbl: + .byte <(slotpatch1+1), <(slotpatch2+1), <(slotpatch3+1) + .byte <(slotpatch4+1), <(slotpatch5+1), <(slotpatch6+1) +indextbl: ;the 0 also terminates the patchtbl list! + .byte 0, 2, 1, 3 + + + ;construct denibbilisation table + ;pre-shifted for interleave read + +skip_ahead: +loopaa: + sty znibble + tya + asl + bit znibble + beq loopz + ora znibble + eor #$ff + and #$7e +loopa: + bcs loopz + lsr + bne loopa + dex + txa + asl + asl + sta preshift-$16, Y +loopz: + dey + bne loopaa + + ;construct 2-bit group table + + sty zvalue +loopbb: + lsr zmask + lsr zmask +loopb: + lda indextbl, X + sta grouped, Y + inc zvalue + lda zvalue + and zmask + bne loopy + inx + txa + and #3 + tax +loopy: + iny + iny + iny + iny + cpy #3 + bcs loopb + iny + cpy #3 + bcc loopbb + lda #>(entry-1) + pha + lda #<(entry-1) + pha + jsr preread + + ; seek backward support +; sty startsec+1 +; sta tmpadr+1 +; stx total+1 + + jmp seekread + +preread: + +;copy post-read if necessary +;push post-read address here +; pla +; tax +; pla +; tay +; lda #>(postread-1) +; pha +; lda #<(postread-1) +; pha +; tya +; pha +; txa +; pha + + lda #<(firsttrk*2) + sta phase+1 + ldx #sectors + lda #address + ldy #firstsec + rts + +end_code: + +.res $8fe-* + +; traditionally, entry point to jump to at end of loading +; $1400 in this case +;*=$8fe + .byte $14, $00 + + +.include "qboot_stage2.s" diff --git a/demos/outline2021/demo/qboot_stage2.s b/demos/outline2021/demo/qboot_stage2.s new file mode 100644 index 00000000..c27f5df8 --- /dev/null +++ b/demos/outline2021/demo/qboot_stage2.s @@ -0,0 +1,364 @@ +; the following lives on sectors $0E and $0D +; why? +; request sector 2 and 4, and the interleave is + +; beneath apple dos (3-23) +; Physical (firmware) : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +; DOS33 mapping : 0, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 15 + + +; Beneath Apple DOS +; p86 (dos reference) +; + +WAIT = $FCA8 ;; delay 1/2(26+27A+5A^2) us + +.org $1400 + +code_begin: + + .byte version ; note this also gets over-written as a + ; side-effect of the patching code + +readnib: +slotpatch1: ; smc + lda $c0d1 ; gets set to C08C (Q6L) read + bpl readnib + rts + + ;fill address array for one track +seekread: + sty startsec+1 + sta tmpadr+1 + stx total+1 + +inittrk: + sec + lda #$10 + sbc startsec+1 + cmp total+1 + bcs it_skip + + tax + +it_skip: + stx partial1 + stx partial2 + jsr seek + +startsec: + ldy #$d1 + +tmpadr: +tmpadr_loop: + lda #$d1 + sta addrtbl, y + inc tmpadr+1 + iny + dec partial1 + bne tmpadr_loop + + ;==================================== + ; read a sector + ;==================================== + ; first address field + ;==================================== + ; starts with $D5 $AA $96 + ; then XX YY volume + ; then XX YY track + ; then XX YY sector + ; then XX YY checksum + ; then ends with $DE $AA $EB + ;==================================== + ; data field + ;==================================== + ; starts with $D5 $AA $AD + ; 342 bytes of data + ; XX checksum + ; ends with $DE $AA $EB +read: + +outer_read: + jsr readnib +inner_read: + cmp #$d5 ; look for $D5 part of addr field + bne outer_read + + jsr readnib ; look for $D5 $AA + cmp #$aa + bne inner_read + + ; look for $D5 $AA $AD + + tay ; we need Y=#$AA later + jsr readnib + eor #$ad ; zero A if match + beq check_mode + + ; if not #$AD, then #$96 is assumed + ; so in address field + + ldy #2 ; volume, track, sector +another: + jsr readnib + rol ; set carry + sta sector+1 + jsr readnib + and sector+1 + dey + bpl another + + tay + ldx addrtbl, Y ; fetch corresponding address + beq read ; done? + + sta sector+1 ; store index for later + + stx adrpatch1+2 + stx adrpatch8+2 + stx adrpatch2+2 + stx adrpatch3+2 + stx adrpatch5+2 + stx adrpatch7+2 + + inx + stx adrpatch9+2 + dex + + dex + stx adrpatch4+2 + stx adrpatch6+2 + + ldy #$fe + +loop2: +adrpatch1: + lda $d102, Y + pha + iny + bne loop2 + +branch_read: + bcs read ; branch always + +check_mode: + cpx #0 + beq read ; loop if not expecting #$AD + +loop33: + sta tmpval+1 ; zero rolling checksum +slotpatch2: +loop4: + ldx $c0d1 + bpl loop4 + lda preshift-$96, X +adrpatch2: + sta $d102, Y ; store 2-bit array + +tmpval: + eor #$d1 + iny + bne loop33 + ldy #$aa +slotpatch3: +loop5: + ldx $c0d1 + bpl loop5 + eor preshift-$96, X +adrpatch3: + ldx $d102, Y ; bit2tbl + eor grouped+2, X ; first 86 nibbles use group bits 0-1 +adrpatch4: + sta $d156, y + iny + bne loop5 + and #$fc + ldy #$aa +slotpatch4: +loop6: + ldx $c0d1 + bpl loop6 + eor preshift-$96, X +adrpatch5: + ldx $d102, Y ; bit2tbl + eor grouped+1, X ; second 86 nibbles use group bits 2-3 +adrpatch6: + sta $d1ac, Y + iny + bne loop6 + and #$fc + ldx #$ac +slotpatch5: +loop7: + ldy $c0d1 + bpl loop7 + eor preshift-$96, Y +adrpatch7: + ldy $d100, X ; bit2tbl + eor grouped, Y ; last 84 nibbles use group bits 4-5 +adrpatch8: + sta $d100, x + inx + bne loop7 + and #$fc +slotpatch6: +loop8: + ldy $c0d1 + bpl loop8 + eor preshift-$96, Y + cmp #1 ; carry = !zero + ldy #1 +loop9: + pla +adrpatch9: + sta $d100, Y + dey + bpl loop9 +branch_read2: + bcs branch_read ; branch if checksum failure + +sector: + ldy #$d1 + txa + sta addrtbl, Y ; zero corresponding address + dec total+1 + dec partial2 ; adjust remaining count + ; (faster than looping over array) + sec + bne branch_read2 ; read all requested sectors in one track + + sta startsec+1 ; this was missing from original code + ; leading to trouble on wrap around + ; it not starting at sector0 +total: + ldx #$d1 + beq driveoff + inc phase+1 + inc phase+1 ; update current track + jmp inittrk + +driveoff: +slotpatch7: + lda $c0d1 + +seekret: + rts + +seek: + ldx #0 + stx step+1 +copy_cur: +curtrk: + lda #0 + sta tmpval+1 + sec +phase: + sbc #$d1 + beq seekret + + ; if seek backwards + bcs sback + + eor #$ff + inc curtrk+1 + + bcc ssback +sback: + adc #$fe + dec curtrk+1 +ssback: + cmp step+1 + bcc loop10 +step: + lda #$d1 +loop10: + cmp #8 + bcs loop11 + tay + sec +loop11: + lda curtrk+1 + ldx step1, Y + bne loop12 +loopmmm: + clc + lda tmpval+1 + ldx step2, Y +loop12: + stx sector+1 + and #3 + rol + tax +slotpatch8: + sta $c0d1, X +loopmm: + ldx #$13 +loopm: + dex + bne loopm + dec sector+1 + bne loopmm + lsr + bcs loopmmm + inc step+1 + bne copy_cur + +step1: .byte 1, $30, $28, $24, $20, $1e, $1d, $1c +step2: .byte $70, $2c, $26, $22, $1f, $1e, $1d, $1c +addrtbl: .res 16 + +partial1: .byte $00 +partial2: .byte $00 +code_end: + + + +;========================== +; enable drive motor +;========================== + +driveon: + +slotpatch9: + lda $c0d1 + + ; wait 1s + + ldx #6 +wait_1s: + lda #255 + jsr WAIT + dex + bne wait_1s + + rts + +load_new: + + jsr driveon + + lda load_track + asl ; track to start*2 + sta phase+1 + + lda load_sector + tay ; sector to start + + lda load_length ; length + tax + + lda load_address ; address to load + + jsr seekread + + rts + +load_address: + .byte $00 +load_track: + .byte $00 +load_sector: + .byte $00 +load_length: + .byte $00 + diff --git a/demos/outline2021/demo/qload.s b/demos/outline2021/demo/qload.s new file mode 100644 index 00000000..299cdea7 --- /dev/null +++ b/demos/outline2021/demo/qload.s @@ -0,0 +1,173 @@ +; Loader for ootw + +;.include "../zp.inc" +;.include "../hardware.inc" + +;.include "common_defines.inc" +.include "qboot.inc" + +qload_start: + + jsr load_file + + ; always enter at $6000 + + jmp $6000 + + ;==================================== + ; loads file specified by WHICH_LOAD + ;==================================== +load_file: + ldx #0 + +; lda which_disk_array,X +; cmp CURRENT_DISK +; bne change_disk + +load_file_no_diskcheck: + lda load_address_array,X + sta load_address + + lda track_array,X + sta load_track + + lda sector_array,X + sta load_sector + + lda length_array,X + sta load_length + + jsr load_new ; FIXME: tail call + + rts + + ;=================================================== + ;=================================================== + ; change disk + ;=================================================== + ;=================================================== +.if 0 +change_disk: + + ; turn off disk drive light + + jsr driveoff + + jsr TEXT + jsr HOME + + lda #error_string + sta OUTH + + ldx WHICH_LOAD + lda which_disk_array,X + clc + adc #48 + + ldy #19 + sta (OUTL),Y + + ldy #0 + +quick_print: + lda (OUTL),Y + beq quick_print_done + jsr COUT1 + iny + jmp quick_print + +quick_print_done: + +fnf_keypress: + lda KEYPRESS + bpl fnf_keypress + bit KEYRESET + + ;============================================== + ; actually verify proper disk is there + ; read T0:S0 and verify proper disk + + lda WHICH_LOAD + pha + + ldx #LOAD_FIRST_SECTOR ; load track 0 sector 0 + stx WHICH_LOAD + + jsr load_file_no_diskcheck + + pla + sta WHICH_LOAD + tax + + ; first sector now in $c00 + ; disk1 = $01 + ; disk2 = $02 + ; disk3 = $03 + + lda $ca5 + cmp #$01 + beq is_disk1 + cmp #$02 + beq is_disk2 + cmp #$03 + beq is_disk3 + bne change_disk ; unknown disk + +is_disk1: + lda #1 + bne disk_compare + +is_disk2: + lda #2 + bne disk_compare + +is_disk3: + lda #3 + +disk_compare: + cmp which_disk_array,X + bne change_disk ; disk mismatch + + ;============================================== + ; all good, retry original load + + jsr HOME + + ldx WHICH_LOAD + lda which_disk_array,X + sta CURRENT_DISK + + jmp load_file + +; offset for disk number is 19 +error_string: +.byte "PLEASE INSERT DISK 1, PRESS RETURN",0 +.endif + +which_disk_array: + .byte 1,1,1,1 ; + + +load_address_array: + .byte $60 ; + +track_array: + .byte 2 ; + + +sector_array: + .byte 0 ; + + +length_array: + .byte 80 ; + + + + ; include common libraries... + +qload_end: + +.assert (>qload_end - >qload_start) < $e , error, "loader too big" diff --git a/demos/outline2021/demo/random16.s b/demos/outline2021/demo/random16.s new file mode 100644 index 00000000..ce4df66c --- /dev/null +++ b/demos/outline2021/demo/random16.s @@ -0,0 +1,60 @@ +; 16-bit 6502 Random Number Generator + +; Linear feedback shift register PRNG by White Flame +; http://codebase64.org/doku.php?id=base:small_fast_16-bit_prng + +; The Apple II KEYIN routine increments this field +; while waiting for keypress + +;SEEDL = $4E +;SEEDH = $4F + +XOR_MAGIC = $7657 ; "vW" + + ;============================= + ; random16 + ;============================= + ; takes: + ; not 0, cc = 5+ = 27 + ; not 0, cs = 5+12+19 = 36 + ; $0000 = 5+7+19 = 31 + ; $8000 = 5+6+14 = 25 + ; $XX00 = 5+6+7+19 = 37 +random16: + + lda SEEDL ; 3 + beq lowZero ; $0000 and $8000 are special values ; 2 + + asl SEEDL ; Do a normal shift ; 5 + lda SEEDH ; 3 + rol ; 2 + bcc noEor ; 2 + +doEor: + ; high byte is in A + + + eor #>XOR_MAGIC ; 2 + sta SEEDH ; 3 + lda SEEDL ; 3 + eor #shape_dsr + + lda #0 ; set rotation + + jsr XDRAW0 ; XDRAW 1 AT X,Y + ; Both A and X are 0 at exit + + + inc FRAME + lda FRAME + and #$7 + beq reverse + +add_x: + lda XPOS + clc + adc DIRECTION + sta XPOS + jmp x_loop + +reverse: + lda DIRECTION ; switch direction + eor #$ff + sec + adc #0 + sta DIRECTION + + lda YPOS + clc + adc #16 + sta YPOS + cmp #190 + bne y_loop + + ;===================================== + ; shimmer + ;===================================== + + +do_shimmer: + lda #16 + sta FRAME + + +do_shimmer_y: + + ldx #0 +do_shimmer_x: + +blargh: + lda $4000,X + eor #$80 +blargh2: + sta $4000,X + inx + bne do_shimmer_x + + + inc blargh+2 + inc blargh2+2 + + lda blargh+2 + cmp #$60 + bne do_shimmer_y + + lda #$40 + sta blargh+2 + sta blargh2+2 + + dec FRAME + bne do_shimmer_y + + rts + +shape_dsr: +.byte $2d,$36,$ff,$3f +.byte $24,$ad,$22,$24,$94,$21,$2c,$4d +.byte $91,$3f,$36,$00 diff --git a/demos/outline2021/demo/sier_hgr.s b/demos/outline2021/demo/sier_hgr.s new file mode 100644 index 00000000..8aff6ec8 --- /dev/null +++ b/demos/outline2021/demo/sier_hgr.s @@ -0,0 +1,374 @@ +; sierpinski-like demo +; based on the code from Hellmood's Memories demo + +; by Vince `deater` Weaver + +; the simple sierpinski you more or less just plot +; X AND Y + +; Hellmood's you plot something more or less like +; COLOR = ( (Y-(X*T)) & (X+(Y*T) ) & 0xf0 +; where T is an incrementing frame value + +; to get speed on 6502/Apple II we change the multiplies to +; a series of 16-bit 8.8 fixed point adds + +; TODO: +; HPLOT: roughly 30s / frame +; MOVERIGHT: roughly 14s / frame +; MOVERIGHT NO COLORSHIFT:roughly 11s / frame +; MOVERIGHT MOVEDOWN roughly 11s / frame +; INLINE HPLOT roughly 9s / frame +; INLINE EVERYTHING roughly 7s / frame +; XT/YT lookup tables roughly 6s / frame +; only write 1/7 of time roughly 3s / frame +; only draw 128 lines roughly 2s / frame + +; zero page + +HGR_BITS = $1C +GBASL = $26 +GBASH = $27 +BASL = $28 +BASH = $29 +MASK = $2E +COLOR = $30 +HGR_HMASK = $30 +HGR_X = $E0 +HGR_Y = $E2 +HGR_COLOR = $E4 +HGR_HORIZ = $E5 +HGR_SCALE = $E7 + +DRAW_PAGE = $04 +FRAME = $05 +SEVEN = $06 +NEXTCOL = $07 +XX_TH = $08 +XX_TL = $09 +YY = $0A +YY_TH = $0B +YY_TL = $0C +T_L = $0D +T_H = $0E +SAVED = $0F + +; Soft switches +FULLGR = $C052 +PAGE1 = $C054 +PAGE2 = $C055 + +; ROM routines +HGR = $F3E2 +HGR2 = $F3D8 +HPOSN = $F411 +HPLOT0 = $F457 +HPLOT1 = $F45A ; skip the HPOSN call +COLOR_SHIFT = $F47E ; shift color for odd/even Y (0..7 or 7..13) +MOVE_RIGHT = $F48A ; move next plot location one to the right +MOVE_DOWN = $F504 ; clc at f504, needed? + ; f505 is applesoft entry point but assumes c? + ; move next plot location one to the right + + ; note moveright/movedown respect HGR_PAGE + +XDRAW0 = $F65D +COLORTBL = $F6F6 +BASCALC = $FBC1 +HOME = $FC58 +CLREOLZ = $FC9E ; clear (BASL),Y to end of line + +XT_LOOKUP_TABLE = $1000 +YT_LOOKUP_TABLE = $1100 + +;.zeropage +;.globalzp T_L,T_H + + ;================================ + ; Clear screen and setup graphics + ;================================ +sier: + jsr HGR ; returns with A=0 + + sta T_L ; start with multiplier 0 + sta T_H + + + ;============================= + +sier_outer: + + ldx #24 + jsr xdraw_desire ; draw desire + + ldx #216 + jsr xdraw_desire ; draw desire + + + ldx #0 ; get X 0 for later + stx YY ; YY starts at 0 + + ; create XX_T and YY_T lookup tables + + stx XX_TL ; always start at 0 + stx XX_TH + + ; calc XX*T + ; only really care about XX_TH +xt_table_loop: + clc ; 2 + lda XX_TL ; 3 + adc T_L ; 2 + sta XX_TL ; 3 + + lda XX_TH ; 3 + adc T_H ; 3 + sta XX_TH ; 3 + + sta YT_LOOKUP_TABLE,X ; 5 + + eor #$ff ; negate, as we subtract ; 2 + sta XT_LOOKUP_TABLE,X ; 5 + inx ; 2 + bne xt_table_loop ; 3/2 + + + ; inc T +; clc + lda T_L +speed_smc: + adc #2 + sta T_L + bcc no_carry + inc T_H +no_carry: + + ; speed up the zoom as it goes + inc speed_smc+1 + + ; set initial position on screen at line 32 + + lda #$0 ; + sta GBASL + + lda #$20 ; start on page2 line 32 ($4200) + sta GBASH + +sier_yloop: + + ; GBASH is in A here + +; lda GBASH ; update output pointer + sta gb_smc+2 + + lda GBASL ; adjust so centered + clc + adc #10 + sta gb_smc+1 + + ; YY*T only needs to be updated once per line + ; so do it here and then self-modify into place + + ldx YY ; 3 + stx add_yy_smc+1 ; 4 + lda YT_LOOKUP_TABLE,X ; 4 + sta yy_th_smc+1 ; 4 + + ; reset XX to 0 + + ldx #0 ; XX + +seven_loop: + ldy #7 ; apple ii 7 pixels per byte + +sier_xloop: + + ; want (YY-(XX*T)) & (XX+(YY*T) + + + ; SAVED = XX+(Y*T) + clc ; needed for proper colors ; 2 + txa ; XX ; 2 +yy_th_smc: + adc #$dd ; 2 + sta SAVED ; 3 + + lda XT_LOOKUP_TABLE,X ; ~(XX*T) ; 4 + ; calc (YY-XX*T) + sec ; 2 +add_yy_smc: + adc #$dd ; 2 + + ; want (YY-(XX*T)) & (XX+(YY*T) + + and SAVED ; 3 + ;============ + ; 20 + + clc ; 2 + beq black ; 2/3 +white: + sec ; 2 +black: + ;===== + ; 5/6 + + + ror NEXTCOL ; rotate in next bit ; 5 + + inx ; increment x ; 2 + + dey ; dec seven count ; 2 + bne sier_xloop ; 2/3 + + ;=========================================================== + + + lda NEXTCOL ; sign extend top bit, ; 3 + cmp #$80 ; matches earlier cool colors ; 2 + ror ; 2 + +gb_smc: + sta $4000 ; write to hi-res display ; 4 + inc gb_smc+1 ; increase GBASL ; 6 + + cpx #124 ; 2 + bcc seven_loop ; 3/2 + + + ;================= + ; total roughly ??? + ; full screen each inner cycle is done 256*192 = 49152 + ; apple II cyles/frame = 17,030 + ; 1FPS = 1,021,800 + + + ;================================== + + jsr MOVE_DOWN ; ROM routine to skip the next line + ; as this is non-trivial on Apple II + ; X/Y left alone + ; returns with GBASH in A + + inc YY ; repeat until YY=128 + bpl sier_yloop + +;flip_pages: +; TODO if frame rate ever gets fast enough + + + ldx #24 + jsr xdraw_desire ; erase desire + + ldx #216 + jsr xdraw_desire ; erase desire + + inc FRAME + + + +;=============== + ; clear screen + + jsr HOME + +; ldx #24 +;clear_screen_loop: + ; txa + ; jsr BASCALC ; A is BASL at end + ; lda BASH + ; clc +; adc DRAW_PAGE + ; sta BASH + + ; ldy #0 + ; jsr CLREOLZ + ; dex +; cpx #20 +; bne clear_screen_loop + + + + ldy #39 +text_loop: + + tya ; get YY to print at +; clc +; adc FRAME + and #$f + tax + + lda cosine,X ; get cosine value + jsr BASCALC ; convert to BASL/BASH + +; lda BASH ; add so is proper page + ; clc + ; adc DRAW_PAGE + ; sta BASH + + tya ; lookup char to print + clc + adc FRAME + and #$f + tax + +; lda apple,X + lda $FB09,X ; 8 bytes of apple II + cpx #8 + bcc blah2 +; ora #$80 + lda #$a0 +blah2: + sta (BASL),Y ; print it + + dey ; loop + bpl text_loop + + + + jmp sier_outer ; branch always + + +cosine: + .byte 23,23,23,22,22,21,20,20,20,20,20,21,21,22,23,23 + +shape_dsr: +.byte $2d,$36,$ff,$3f +.byte $24,$ad,$22,$24,$94,$21,$2c,$4d +.byte $91,$3f,$36,$00 + + + ;======================= + ; xdraw desire + ;======================= + +xdraw_desire: + + + ; setup X and Y co-ords + + ldy #0 ; XPOSH always 0 for us + lda #20 + jsr HPOSN ; X= (y,x) Y=(a) + + + lda #3 + sta HGR_SCALE + +shape_smc: + ldx #shape_dsr ; code fits in one page so this doesn't change + +rot_smc: + lda FRAME ; set rotation + and #$f ; necessary? + asl + asl + + ; for scale 2 only 8 positions + +blah: + jmp XDRAW0 ; XDRAW 1 AT X,Y + ; Both A and X are 0 at exit + diff --git a/demos/outline2021/demo/tfv_flying.s b/demos/outline2021/demo/tfv_flying.s new file mode 100644 index 00000000..06be9ee2 --- /dev/null +++ b/demos/outline2021/demo/tfv_flying.s @@ -0,0 +1,653 @@ +; Mode-7 Flying code + + +;=========== +; CONSTANTS +;=========== +CONST_SHIPX = 15 +CONST_TILE_W = 64 +CONST_TILE_H = 64 +CONST_MAP_MASK_X = (CONST_TILE_W - 1) +CONST_MAP_MASK_Y = (CONST_TILE_H - 1) +CONST_LOWRES_W = 40 +CONST_LOWRES_H = 40 +CONST_BETA_I = $ff +CONST_BETA_F = $80 +CONST_SCALE_I = $14 +CONST_SCALE_F = $00 +CONST_LOWRES_HALF_I = $ec ; -(LOWRES_W/2) +CONST_LOWRES_HALF_F = $00 + +mode7_flying: + + ;=================== + ; Clear screen/pages + ;=================== + + bit PAGE0 + bit LORES ; Lo-res graphics + bit TEXTGR ; mixed gr/text mode + bit SET_GR ; set graphics + + + jsr clear_screens + lda #0 + sta DISP_PAGE + lda #4 + sta DRAW_PAGE + + ;=============== + ; Init Variables + ;=============== + lda #20 + sta SHIPY + lda #0 + sta TURNING + sta ANGLE + sta SPACEX_I + sta SPACEY_I + sta CX_I + sta CX_F + sta CY_I + sta CY_F + sta DRAW_SPLASH + sta SPEED + sta SPLASH_COUNT + sta KEY_COUNT + sta KEY_OFFSET + + lda #1 ; slightly off North for better view of island + sta ANGLE + + jsr draw_sky + + lda #4 ; starts out at 4.5 altitude + sta SPACEZ_I + lda #$80 + sta SPACEZ_F + + jsr update_z_factor + +flying_loop: + + lda SPLASH_COUNT ; 3 + beq flying_keyboard ; 2nt/3 + dec SPLASH_COUNT ; decrement splash count ; 5 + +flying_keyboard: + +; jsr get_keypress ; get keypress ; 6 + + lda KEY_COUNT + bne done_key + + ldy KEY_OFFSET + lda island_flying_directions,Y + sta KEY_COUNT + iny + + lda island_flying_directions,Y + sta LASTKEY + inc KEY_OFFSET + inc KEY_OFFSET + +done_key: + dec KEY_COUNT + lda LASTKEY + +; cmp #0 +; bne key_was_pressed +; +; jmp check_done + +;key_was_pressed: + +; lda LASTKEY ; 3 + + cmp #('Q') ; if quit, then return + bne skipskip + rts + +skipskip: + + cmp #'W' ; 2 + bne flying_check_down ; 3/2nt + + ;=========== + ; UP PRESSED + ;=========== + + lda SHIPY + cmp #17 + bcc flying_check_down ; bgt, if shipy>16 + dec SHIPY + dec SHIPY ; move ship up + inc SPACEZ_I ; incement height + jsr update_z_factor + lda #0 + sta SPLASH_COUNT + jmp check_done + +flying_check_down: + cmp #'S' + bne flying_check_left + + ;============= + ; DOWN PRESSED + ;============= + + lda SHIPY + cmp #28 + bcs splashy ; ble, if shipy < 28 + inc SHIPY + inc SHIPY ; move ship down + dec SPACEZ_I ; decrement height + jsr update_z_factor + bcc done_flying_down + +splashy: + lda #10 + sta SPLASH_COUNT +done_flying_down: + jmp check_done + +flying_check_left: + cmp #'A' + bne flying_check_right + + ;============= + ; LEFT PRESSED + ;============= + + lda TURNING + bmi turn_left + beq turn_left + + lda #$0 + sta TURNING + jmp check_done + +turn_left: + lda #253 ; -3 + sta TURNING + + dec ANGLE + jmp check_done + +flying_check_right: + cmp #'D' + bne check_speedup + + ;============== + ; RIGHT PRESSED + ;============== + + lda TURNING ;; FIXME: optimize me + bpl turn_right + lda #0 + sta TURNING + jmp check_done + +turn_right: + lda #3 + sta TURNING + + inc ANGLE + jmp check_done + +check_speedup: + cmp #'Z' + bne check_speeddown + + ;========= + ; SPEED UP + ;========= + lda #$8 + cmp SPEED + beq skip_speedup + inc SPEED +skip_speedup: + jmp check_done + +check_speeddown: + cmp #'X' + bne check_brake + + ;=========== + ; SPEED DOWN + ;=========== + + lda SPEED + beq skip_speeddown + dec SPEED +skip_speeddown: + jmp check_done + +check_brake: + cmp #' ' + bne check_land + + ;============ + ; BRAKE + ;============ + lda #$0 + sta SPEED + jmp check_done + +check_land: + cmp #13 + bne check_help + + ;===== + ; LAND + ;===== + + ; finds value in space_x.i,space_y.i + ; returns color in A + lda CX_I + sta SPACEX_I + lda CY_I + sta SPACEY_I + + jsr lookup_map + + cmp #COLOR_BOTH_LIGHTGREEN + bne must_land_on_grass + +landing_loop: + + jsr draw_background_mode7 + + ; Draw Shadow + lda #>shadow_forward + sta INH + lda #ship_forward + sta INH + lda #splash_forward ; 2 + sta INH ; 3 + lda #shadow_forward ; 2 + sta INH ; 3 + lda #ship_forward ; 2 + sta INH ; 3 + lda #splash_right ; 2 + sta INH ; 3 + lda #shadow_right ; 2 + sta INH ; 3 + lda #ship_right ; 2 + sta INH ; 3 + lda #splash_left ; 2 + sta INH ; 3 + lda #shadow_left ; 2 + sta INH ; 3 + lda #ship_left ; 2 + sta INH ; 3 + lda #