From bf0693a7c6f372676da684f4e752f3d910eb917d Mon Sep 17 00:00:00 2001 From: Vince Weaver Date: Thu, 11 Jan 2024 01:25:38 -0500 Subject: [PATCH] trogdor: probably ill-advised new demo --- demos/trogdor/Makefile | 149 ++ demos/trogdor/NOTES | 32 + demos/trogdor/gr_fast_clear.s | 201 ++ demos/trogdor/gr_offsets.s | 5 + demos/trogdor/graphics/Makefile | 116 + demos/trogdor/graphics/trog00_trogdor.png | Bin 0 -> 2280 bytes demos/trogdor/graphics/trog01_countryside.png | Bin 0 -> 5545 bytes demos/trogdor/graphics/trog02_countryside.png | Bin 0 -> 1816 bytes demos/trogdor/graphics/trog03_man.png | Bin 0 -> 2800 bytes demos/trogdor/graphics/trog04_dragonman.png | Bin 0 -> 2841 bytes demos/trogdor/graphics/trog05_zoom.png | Bin 0 -> 4326 bytes demos/trogdor/graphics/trog06_again.png | Bin 0 -> 2853 bytes demos/trogdor/graphics/trog07_peasant.png | Bin 0 -> 2020 bytes demos/trogdor/graphics/trog08_close1.png | Bin 0 -> 3616 bytes demos/trogdor/graphics/trog09_close.png | Bin 0 -> 4074 bytes demos/trogdor/graphics/trog10_peasants.png | Bin 0 -> 3754 bytes demos/trogdor/graphics/trog11_cottage.png | Bin 0 -> 3689 bytes demos/trogdor/graphics/trog_flames1.png | Bin 0 -> 1297 bytes demos/trogdor/graphics/trog_flames2.png | Bin 0 -> 9022 bytes demos/trogdor/graphics/trog_flames3.png | Bin 0 -> 1653 bytes demos/trogdor/gs_interrupt.s | 9 + demos/trogdor/hardware.inc | 101 + demos/trogdor/hardware_detect.s | 80 + demos/trogdor/hgr_clear_screen.s | 89 + demos/trogdor/hgr_page_flip.s | 18 + demos/trogdor/hgr_sprite_big.s | 205 ++ demos/trogdor/hgr_table.s | 97 + demos/trogdor/interrupt_handler.s | 74 + demos/trogdor/irq_wait.s | 94 + demos/trogdor/lc_detect.s | 40 + demos/trogdor/music.s | 30 + demos/trogdor/pt3_lib_core.s | 2023 +++++++++++++++++ demos/trogdor/pt3_lib_detect_model.s | 95 + demos/trogdor/pt3_lib_init.s | 575 +++++ demos/trogdor/pt3_lib_irq_handler.s | 116 + demos/trogdor/pt3_lib_mockingboard.inc | 54 + demos/trogdor/pt3_lib_mockingboard_detect.s | 222 ++ demos/trogdor/pt3_lib_mockingboard_patch.s | 121 + demos/trogdor/pt3_lib_mockingboard_setup.s | 295 +++ demos/trogdor/qboot.inc | 8 + demos/trogdor/qboot_sector.s | 244 ++ demos/trogdor/qboot_stage2.s | 382 ++++ demos/trogdor/qload.s | 208 ++ demos/trogdor/start.s | 197 ++ demos/trogdor/text_print.s | 93 + demos/trogdor/trogdor.s | 85 + demos/trogdor/wait.s | 18 + demos/trogdor/wait_a_bit.s | 37 + demos/trogdor/wait_keypress.s | 5 + demos/trogdor/zp.inc | 251 ++ demos/trogdor/zx02_optim.s | 159 ++ 51 files changed, 6528 insertions(+) create mode 100644 demos/trogdor/Makefile create mode 100644 demos/trogdor/NOTES create mode 100644 demos/trogdor/gr_fast_clear.s create mode 100644 demos/trogdor/gr_offsets.s create mode 100644 demos/trogdor/graphics/Makefile create mode 100644 demos/trogdor/graphics/trog00_trogdor.png create mode 100644 demos/trogdor/graphics/trog01_countryside.png create mode 100644 demos/trogdor/graphics/trog02_countryside.png create mode 100644 demos/trogdor/graphics/trog03_man.png create mode 100644 demos/trogdor/graphics/trog04_dragonman.png create mode 100644 demos/trogdor/graphics/trog05_zoom.png create mode 100644 demos/trogdor/graphics/trog06_again.png create mode 100644 demos/trogdor/graphics/trog07_peasant.png create mode 100644 demos/trogdor/graphics/trog08_close1.png create mode 100644 demos/trogdor/graphics/trog09_close.png create mode 100644 demos/trogdor/graphics/trog10_peasants.png create mode 100644 demos/trogdor/graphics/trog11_cottage.png create mode 100644 demos/trogdor/graphics/trog_flames1.png create mode 100644 demos/trogdor/graphics/trog_flames2.png create mode 100644 demos/trogdor/graphics/trog_flames3.png create mode 100644 demos/trogdor/gs_interrupt.s create mode 100644 demos/trogdor/hardware.inc create mode 100644 demos/trogdor/hardware_detect.s create mode 100644 demos/trogdor/hgr_clear_screen.s create mode 100644 demos/trogdor/hgr_page_flip.s create mode 100644 demos/trogdor/hgr_sprite_big.s create mode 100644 demos/trogdor/hgr_table.s create mode 100644 demos/trogdor/interrupt_handler.s create mode 100644 demos/trogdor/irq_wait.s create mode 100644 demos/trogdor/lc_detect.s create mode 100644 demos/trogdor/music.s create mode 100644 demos/trogdor/pt3_lib_core.s create mode 100644 demos/trogdor/pt3_lib_detect_model.s create mode 100644 demos/trogdor/pt3_lib_init.s create mode 100644 demos/trogdor/pt3_lib_irq_handler.s create mode 100644 demos/trogdor/pt3_lib_mockingboard.inc create mode 100644 demos/trogdor/pt3_lib_mockingboard_detect.s create mode 100644 demos/trogdor/pt3_lib_mockingboard_patch.s create mode 100644 demos/trogdor/pt3_lib_mockingboard_setup.s create mode 100644 demos/trogdor/qboot.inc create mode 100644 demos/trogdor/qboot_sector.s create mode 100644 demos/trogdor/qboot_stage2.s create mode 100644 demos/trogdor/qload.s create mode 100644 demos/trogdor/start.s create mode 100644 demos/trogdor/text_print.s create mode 100644 demos/trogdor/trogdor.s create mode 100644 demos/trogdor/wait.s create mode 100644 demos/trogdor/wait_a_bit.s create mode 100644 demos/trogdor/wait_keypress.s create mode 100644 demos/trogdor/zp.inc create mode 100644 demos/trogdor/zx02_optim.s diff --git a/demos/trogdor/Makefile b/demos/trogdor/Makefile new file mode 100644 index 00000000..2e762bb0 --- /dev/null +++ b/demos/trogdor/Makefile @@ -0,0 +1,149 @@ +include ../../Makefile.inc + +DOS33 = ../../utils/dos33fs-utils/dos33 +DOS33_RAW = ../../utils/dos33fs-utils/dos33_raw +EMPTY_DISK = ../../empty_disk/empty.dsk +TOKENIZE = ../../utils/asoft_basic-utils/tokenize_asoft +LINKER_SCRIPTS = ../../linker_scripts/ + +all: trogdor.dsk + +trogdor.dsk: QBOOT QLOAD music.inc qload.inc \ + MUSIC TROGDOR + cp $(EMPTY_DISK) trogdor.dsk + $(DOS33_RAW) trogdor.dsk 0 0 QBOOT 0 1 + $(DOS33_RAW) trogdor.dsk 0 2 QBOOT 1 1 + $(DOS33_RAW) trogdor.dsk 0 4 QBOOT 2 1 + $(DOS33_RAW) trogdor.dsk 1 0 QLOAD 0 0 + $(DOS33_RAW) trogdor.dsk 4 0 MUSIC 0 0 + $(DOS33_RAW) trogdor.dsk 12 0 TROGDOR 0 0 + + +#### + +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 + + +#### + +QLOAD: qload.o + ld65 -o QLOAD qload.o -C $(LINKER_SCRIPTS)/apple2_1200.inc + +qload.o: zp.inc hardware.inc music.inc qload.s \ + gr_offsets.s \ + wait.s wait_a_bit.s \ + lc_detect.s \ + text_print.s start.s \ + hgr_table.s gs_interrupt.s \ + zx02_optim.s wait_keypress.s hardware_detect.s \ + pt3_lib_detect_model.s pt3_lib_mockingboard_detect.s \ + pt3_lib_mockingboard_setup.s interrupt_handler.s \ + pt3_lib_mockingboard_patch.s + ca65 -o qload.o qload.s -l qload.lst + +#### + +QLOAD2: qload2.o + ld65 -o QLOAD2 qload2.o -C $(LINKER_SCRIPTS)/apple2_1200.inc + +qload2.o: qload2.s \ + zp.inc hardware.inc music2.inc \ + gr_offsets.s \ + wait.s wait_a_bit.s \ + lc_detect.s gr_fast_clear.s \ + text_print.s start2.s \ + hardware_detect.s \ + hgr_table.s \ + pt3_lib_detect_model.s pt3_lib_mockingboard_detect.s \ + pt3_lib_mockingboard_setup.s interrupt_handler.s \ + pt3_lib_mockingboard_patch.s + ca65 -o qload2.o qload2.s -l qload2.lst + + +#### + +qload.inc: generate_common QLOAD + ./generate_common -a 0x1200 -s load_file qload.lst > qload.inc + ./generate_common -a 0x1200 -s detect_appleii_model qload.lst >> qload.inc + ./generate_common -a 0x1200 -s clear_all qload.lst >> qload.inc + ./generate_common -a 0x1200 -s clear_all_color qload.lst >> qload.inc + ./generate_common -a 0x1200 -s set_normal qload.lst >> qload.inc + ./generate_common -a 0x1200 -s set_inverse qload.lst >> qload.inc + ./generate_common -a 0x1200 -s wait qload.lst >> qload.inc + ./generate_common -a 0x1200 -s wait_a_bit qload.lst >> qload.inc + ./generate_common -a 0x1200 -s move_and_print qload.lst >> qload.inc + ./generate_common -a 0x1200 -s detect_language_card qload.lst >> qload.inc + ./generate_common -a 0x1200 -s mockingboard_detect qload.lst >> qload.inc + ./generate_common -a 0x1200 -s clear_bottom qload.lst >> qload.inc + ./generate_common -a 0x1200 -s clear_bottoms qload.lst >> qload.inc + ./generate_common -a 0x1200 -s clear_screens qload.lst >> qload.inc + ./generate_common -a 0x1200 -s clear_top qload.lst >> qload.inc + ./generate_common -a 0x1200 -s zx02_full_decomp qload.lst >> qload.inc + ./generate_common -a 0x1200 -s zx_src_h qload.lst >> qload.inc + ./generate_common -a 0x1200 -s zx_src_l qload.lst >> qload.inc + +#### + +music.inc: generate_common MUSIC + ./generate_common -a 0xd000 -s pt3_init_song music.lst > music.inc + ./generate_common -a 0xd000 -s mockingboard_init music.lst >> music.inc + ./generate_common -a 0xd000 -s reset_ay_both music.lst >> music.inc + ./generate_common -a 0xd000 -s clear_ay_both music.lst >> music.inc + ./generate_common -a 0xd000 -s mockingboard_setup_interrupt music.lst >> music.inc + ./generate_common -a 0xd000 -s mockingboard_disable_interrupt music.lst >> music.inc + ./generate_common -a 0xd000 -s done_pt3_irq_handler music.lst >> music.inc + ./generate_common -a 0xd000 -s PT3_LOC music.lst >> music.inc + ./generate_common -a 0xd000 -s current_pattern_smc music.lst >> music.inc + ./generate_common -a 0xd000 -s pt3_set_pattern music.lst >> music.inc + ./generate_common -a 0xd000 -s mute_ay_both music.lst >> music.inc + ./generate_common -a 0xd000 -s unmute_ay_both music.lst >> music.inc + ./generate_common -a 0xd000 -s interrupt_handler music.lst >> music.inc + +#### + +MUSIC: music.o + ld65 -o MUSIC music.o -C $(LINKER_SCRIPTS)/apple2_d000.inc + +music.o: music.s zp.inc \ + pt3_lib_core.s \ + pt3_lib_mockingboard_detect.s \ + pt3_lib_mockingboard.inc \ + pt3_lib_init.s \ + pt3_lib_mockingboard_setup.s \ + pt3_lib_irq_handler.s + ca65 -o music.o music.s -l music.lst + +### + +TROGDOR: trogdor.o + ld65 -o TROGDOR trogdor.o -C $(LINKER_SCRIPTS)/apple2_8000.inc + +trogdor.o: trogdor.s \ + zp.inc hardware.inc qload.inc \ + graphics/trog00_trogdor.hgr.zx02 + ca65 -o trogdor.o trogdor.s -l trogdor.lst + + +### + +graphics/trog00_trogdor.hgr.zx02: + cd graphics && make + +### + +generate_common: generate_common.o + $(CC) $(LFLAGS) -o generate_common generate_common.o + +generate_common.o: generate_common.c + $(CC) $(CFLAGS) -c generate_common.c + +### + + +clean: + rm -f *~ *.o *.lst QBOOT QLOAD MUSIC TROGDOR + rm -f qload.inc music.inc generate_common diff --git a/demos/trogdor/NOTES b/demos/trogdor/NOTES new file mode 100644 index 00000000..c63c36fe --- /dev/null +++ b/demos/trogdor/NOTES @@ -0,0 +1,32 @@ +Crop to 635x472 + Levels: middle to 0.40 + Scale to: 280x208 + Levels: to 0.35 + Indexed, B+W, Floyd Steinberg + Crop to 280x192 with top at 8 pixels down + + + ++ zoom trogdor right to left ++ blank screen with flames ++ trogdor zoom up from bottom ++ village scroll from right to left ++ Man ++ Flames appear ++ Dragon man ++ Dragon ++ Zoom in/out ++ Scroll Down ++ Flames on Dragon Man again ++ Flip rapidly between dragon/man ++ Back to village ++ Flames over village ++ Burning peasant scrolls ++ dragon up/down ++ Lots of peasants ++ cottage scrolls up ++ back/forth dragon/cottage ++ cottags scrolls in upside down from top? with flames? ++ rapid cut between tilted man/peasant/few others + + diff --git a/demos/trogdor/gr_fast_clear.s b/demos/trogdor/gr_fast_clear.s new file mode 100644 index 00000000..8cc8d152 --- /dev/null +++ b/demos/trogdor/gr_fast_clear.s @@ -0,0 +1,201 @@ +clear_screens: + ;=================================== + ; Clear top/bottom of page 0 + ;=================================== + + 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 + + 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/trogdor/gr_offsets.s b/demos/trogdor/gr_offsets.s new file mode 100644 index 00000000..d3af91f7 --- /dev/null +++ b/demos/trogdor/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/trogdor/graphics/Makefile b/demos/trogdor/graphics/Makefile new file mode 100644 index 00000000..832fe6aa --- /dev/null +++ b/demos/trogdor/graphics/Makefile @@ -0,0 +1,116 @@ +include ../../../Makefile.inc + +ZX02 = ~/research/6502_compression/zx02.git/build/zx02 -f +PNG_TO_HGR = ../../../utils/hgr-utils/png2hgr +PNG2GR = ../../../utils/gr-utils/png2gr +HGR_SPRITE = ../../../utils/hgr-utils/hgr_make_sprite + +all: \ + trog00_trogdor.hgr.zx02 \ + trog01_countryside.hgr.zx02 \ + trog02_countryside.hgr.zx02 \ + trog03_man.hgr.zx02 \ + trog04_dragonman.hgr.zx02 \ + trog05_zoom.hgr.zx02 \ + trog07_peasant.hgr.zx02 \ + trog08_close1.hgr.zx02 \ + trog09_close.hgr.zx02 \ + trog10_peasants.hgr.zx02 \ + trog11_cottage.hgr.zx02 + +#### + +trog00_trogdor.hgr.zx02: trog00_trogdor.hgr + $(ZX02) trog00_trogdor.hgr trog00_trogdor.hgr.zx02 + +trog00_trogdor.hgr: trog00_trogdor.png + $(PNG_TO_HGR) trog00_trogdor.png > trog00_trogdor.hgr + +#### + +trog01_countryside.hgr.zx02: trog01_countryside.hgr + $(ZX02) trog01_countryside.hgr trog01_countryside.hgr.zx02 + +trog01_countryside.hgr: trog01_countryside.png + $(PNG_TO_HGR) trog01_countryside.png > trog01_countryside.hgr + +#### + +trog02_countryside.hgr.zx02: trog02_countryside.hgr + $(ZX02) trog02_countryside.hgr trog02_countryside.hgr.zx02 + +trog02_countryside.hgr: trog02_countryside.png + $(PNG_TO_HGR) trog02_countryside.png > trog02_countryside.hgr + + +#### + +trog03_man.hgr.zx02: trog03_man.hgr + $(ZX02) trog03_man.hgr trog03_man.hgr.zx02 + +trog03_man.hgr: trog03_man.png + $(PNG_TO_HGR) trog03_man.png > trog03_man.hgr + +#### + +trog04_dragonman.hgr.zx02: trog04_dragonman.hgr + $(ZX02) trog04_dragonman.hgr trog04_dragonman.hgr.zx02 + +trog04_dragonman.hgr: trog04_dragonman.png + $(PNG_TO_HGR) trog04_dragonman.png > trog04_dragonman.hgr + +#### + +trog05_zoom.hgr.zx02: trog05_zoom.hgr + $(ZX02) trog05_zoom.hgr trog05_zoom.hgr.zx02 + +trog05_zoom.hgr: trog05_zoom.png + $(PNG_TO_HGR) trog05_zoom.png > trog05_zoom.hgr + +#### + +trog07_peasant.hgr.zx02: trog07_peasant.hgr + $(ZX02) trog07_peasant.hgr trog07_peasant.hgr.zx02 + +trog07_peasant.hgr: trog07_peasant.png + $(PNG_TO_HGR) trog07_peasant.png > trog07_peasant.hgr + +#### + +trog08_close1.hgr.zx02: trog08_close1.hgr + $(ZX02) trog08_close1.hgr trog08_close1.hgr.zx02 + +trog08_close1.hgr: trog08_close1.png + $(PNG_TO_HGR) trog08_close1.png > trog08_close1.hgr + +#### + +trog09_close.hgr.zx02: trog09_close.hgr + $(ZX02) trog09_close.hgr trog09_close.hgr.zx02 + +trog09_close.hgr: trog09_close.png + $(PNG_TO_HGR) trog09_close.png > trog09_close.hgr + + +#### + +trog10_peasants.hgr.zx02: trog10_peasants.hgr + $(ZX02) trog10_peasants.hgr trog10_peasants.hgr.zx02 + +trog10_peasants.hgr: trog10_peasants.png + $(PNG_TO_HGR) trog10_peasants.png > trog10_peasants.hgr + +#### + +trog11_cottage.hgr.zx02: trog11_cottage.hgr + $(ZX02) trog11_cottage.hgr trog11_cottage.hgr.zx02 + +trog11_cottage.hgr: trog11_cottage.png + $(PNG_TO_HGR) trog11_cottage.png > trog11_cottage.hgr + + + +#### + +clean: + rm -f *~ *.zx02 diff --git a/demos/trogdor/graphics/trog00_trogdor.png b/demos/trogdor/graphics/trog00_trogdor.png new file mode 100644 index 0000000000000000000000000000000000000000..057bbf4a516ed5ac40b1794da986e1e4f5ceb95f GIT binary patch literal 2280 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 literal 0 HcmV?d00001 diff --git a/demos/trogdor/graphics/trog05_zoom.png b/demos/trogdor/graphics/trog05_zoom.png new file mode 100644 index 0000000000000000000000000000000000000000..4ca8f20935adcb79eee452ced2069bdd038389cd GIT binary patch literal 4326 zcmVEX>4Tx04R}tkv&MmKpe$iQ>9WW4hAgZkfC<6i;6hbDionYs1;guFdzMbCJjl7 zi=*ILaPVWX>fqw6tAnc`2!4RLx;QDiNQwVT3N2zhIPS;0dyl(!0N1K1n$?#CG~G6{ z>6DN!tO}u5gb~FEc*G^eGUh}v1K;s=j{sZm5-iLA+@E7mEm{f?5QvkCVcNtS#50?= z!FZpTS1OW9d`>)J(glehnJ#<$#<=V<$1-^{mz^i(iG^|x%RQ`AOpSP&IHqbk<#TzD zRmNM4wOT{z+>^a9Qq)(LnND*IX)Iz1A_T~2po$u7Bxu!1v5=+xl#hSZ^^4?^$yJ3y zjs?`AL2~_IfAG6ot28y~B}Gy||BK^%j02%vpxtzw?_n&jRlk{d+ ziyZ+&+rY(jSCjXE%N-#4WJspuNA(Ki)<;aecQ=JnRu$LRx*qpmVHz`-F9 zFH`oq&$|bDd;9lHr@tS%uX4j@<$mD+000tDOjJbx000}y0pv9P|NsBZTLBxI{*j+U z82|tP32;bRa{vGsOaK5FbOE2JKp+4B00(qQO+^Rj0SXcUB`Kq5g8%>k8FWQhbVF}# zZDnqB07G(RVRU6=Aa`kWXdp*PO;A^X4i^9b4zo!_K~#9!&0GnN+&By*P#-El|Nq;L zcuBHl`D|nx*=&;OcGEnIMaoicya?QQD+`2>zyBcDJ}0+?k-yi)_u@}*@J|r1%2Yr7 z&xB!BjLJdEPc-u<&1E|YSI=BhMQw>4p^l76C@%c`l) zqOU($SbpI%rJ5fXRinxwZbaa1&HUZ+wa}KLVN`#s^5b)#Q4hX&qkp-DqViz<2|U+K zO&!;)WJZb!U=%-LP5 z0{|EFUJH}vI|!Z%BWswT_xk6-=hA3tqL`^Fo_JuUzYod<%}~z5)hnb*&!=>hP?(c+Y5rIN3aX>yFqkU53sLY;GnBQRXxCT0EC4@C zqTd&I<}4812T;FlnSRCGmvO--Nl&2LklBcG`dq^#ZFa~_pf;EC6WSIbAj)ciqnJsew!>NPlTf)M^&9tcxmndYlw zr-=~xD?6u-Koy33<}+fDex+GP1}+zHt>C8r4#J3Hdi7vg=T(IdxS*P+X0n8bvg*1R z!q|6Ue<2Kle{0n-AC!!(o48kE+1su`zrU_ICTD&fC! z#66C8AlDn)oo3WVHPd_jKN5!X4pB@!=vlq%&15f(vZLOs3)~{OqnWbP3@>G)nJ|C1 zP&sX*Y{h$?gOoRQ;CdwsCdO~Z5!@Fh@SCz<-rL>C-U>rc5Ogd(#SaH~lkG=H@cu%W8cSLd)MSpMHo_Rj z*iwH=4@>%sdLH>s7!+$lQ0v3y)}l(%SUn8wiYS#>2AF=+efPaKG|3ce=Ei`cY+sOp z)-k9!K)pAbDPCJ>8Eb^FGJhepQ01vwrC;Z+Vs@G_*3|j!(xnt`bmAT^t(H}tZtTi; z!dSFu2Cg#M-_i1&1J9{;O7O92YybZ%pHT^F#nd==N@MFTgtcf$^*EjyVftG$8L(0_ zCCiPp4Gbc8HA<2svHTCVx(&P&Ci#P!`3jw78I)plCmgtUuESQy*>J>bVTxfEZPuEb zj`9SZ$;QfaER#Ta=QGV*o%d}AhsNpY>vjVnsoXvTnzjS})%!SJb1%tTE#uBe8?#aca)-f?~A)8Q^EUw?Jv zbxAXtXU0NcxC;otgOJKfnY%j^TCtRZ*TR%O(IIGhooS=wl0#P=#9{OJk0M`aMu%5T zA0dn|HioKh`fyhd=1?p?6NW(~#)UPnD8#h}3rT<1w`nJ^QCE?ZdFLHrFv14yW(?@C ztqd{!$fPNmCHWwn2yy=WCPYhG3J2A&|GBkT1IPexvK^zmbBn5Jq zZi%OPKAs8FMFKcV;+aJ;S8-ECTFI_FB9Lv+^td}fZ|Z_Bg~7ih^g{2g5aDF5!9ZXL zBxbJkL>P{KCmsLn@f*@uZ`DE092R@fB?H_8u3I$P{KdFg_NKBJPhAeuM2+%KJ)+S@ z@HB)`j`)rBGsUY0;@!n&ASHo>|Naa@L|Q8w-0M@6;*l*h?z$}?NRW=25sOY+dT;}l zBZlxu80)G%Q|P5rqFSnTj|J|$NWz#2YN94m1Ce7tHD_|=NEqghPO-hv46T^($I56^ z%UBobJE%5Fg|}Kk5q4o7+$@*Em?cPhcIz^)?dF*v8rBw77~x*1hJLxsc!VW-4{V~|08EcLljp>`S(vZ!cas)MVVe3SS!T%2KWzq4j%~EjW43s(1=iHLHFxuP+ zp=I5i_PgLzGZWD4EwpiW^q4EjoO3q5PQZcKX*0qc?g&oiO6kB4w!$C>zzO4g-eecB zWTAMJ^ZoQ*o;Fj>V6ZH1TXh^7bL>L9JJ%9myOl|6T#fL?;Y6i$mA*tw)fCARLnsB# znc5Q&aHE;c;T$he(Z+ishnk9K%K)u%?idK|90GmmCf_a>d%QGkXm=E8#%*KWE!P_; z4RmnZFwu3ck#6xlYiJ}-lF%6$cFS98Lmecwj%2ZkF@n|Ia)g&3`dwltjb|A{7%mml zf7O9ub3h!g_F_I>n7-94MBnL7l$x3ejTp1sXuQnekk&;nabt&qj7@GdZ10ikl-_`1 z7(l=s*z-|4d6VQ!v{Dz^vTp5mIczf6Xi%io?-* zynji$y0kfwO}ix6)}cRu*R9LT;6ql>OIBrO{vg03G>Zu!DQ@p>+t>5Z&N@*Z>{! zbO6n4TIO6j7cbzG!O5~|^;<2{2(_#*FE=aiDTW$ikQO32W2c=!3;@tHgEFvR9YX@2 z?OdEWHc-_sUPT~iXn9GtPFei4Fnv&uGBDmEZMq*vf=U3Ui$>a_K&`@Jn;KpQjMhPX zyMw&gQ<0u7NcyStj#M7JjPrwJ|D#=$BN|vc>@KO1ZI#&-!m+}rCpM&g#Z6)sH|~~* zgJ9+ntgfq<0G;MDcHdcla;onO&-YfIw>gJ>`RCBBLq=huh|U3awBooN+1N>gWIDV% zME+gcHos2Xg2~&@cp}(cMK{vS=2EVp(nud^3qK75djiIMi%1u=8RMLLvS8e!F^I#- zdP%A#aKUa1vukZS2>R&d!z(RtrF8`umO+A_F@QJQJY(tH?1Im<1MA$}*=7lASm{#u>cC1Eb#V$O!Wc8e;LA_W%HR&NI9vzuje^5oH8(*nZwj#-o*r3tJ4w7^E({ny zzlz6sRvlJ9Gp_7l6Pf8aa(_GWe&L`8%rsR=o3a?p|6YK-&rEN+2cqS?eZJ?(2WIk4 z?Wp!Mg#kp$PhT;^IUM)fLtBg=ET22gKGY62n)}sBeVfxW{Gn#lM~}k3Z3zb4!h313 zGSd-KX+(1|V9p8qc6fvz7#ObPQEu&SwBfFI?C9-Eq)3@&Kw!fl3rOI#P4Yv_&|f1fW8PT zY?9uJ{N7GPHv@n0P!V{bnWSP@6E^fao;DLP&RD(#SbE0bEc1;K?hmEQ6)0|i_zR@x zXF--;vzb1|LB5!gbs{v{8PCc=N1~i~4cp!vecr+^E0xeEI>*_Dn}M}c=eFg*?imrh z(?1=Bw%Pxj zeUne$mXq1tij0R}?%6k(j#p;#ZL_tZxH~)(Pj&JEKR?O93jK$ZCPxv)arP6(X0!?S zIpPN!N%4DVjuYfG`_KaR;WON)5UkKA}%dBa#*?Jz3=VtIl zo{@HjYfVJqXnon-SiTX)=Eu1B?yW4ec{AiaMsEkHE)lDkZJ)(ywbw2jM_RA$GEdKR za@^c{B@8YvT}-12`Mj^!n(@;EH=fhAKU)cheQ&)+Vm5jwHV@Z2Uu&kJr;Fq_2kpcv zqObAttuW>0qlIjJt`{fTI=r#Od@y#JIh$|}c|4=jBgg0MQ?oZ6bJRS%94}7K{lR-{3`8#2- zG1K$H{Sk@Vj!od^8SNhkBg3)Ff2J9)(p}w_eEX>4Tx04R}tkv&MmKpe$iQ>9WW4hAgZkfC<6i;6hbDionYs1;guFdzMbCJjl7 zi=*ILaPVWX>fqw6tAnc`2!4RLx;QDiNQwVT3N2zhIPS;0dyl(!0N1K1n$?#CG~G6{ z>6DN!tO}u5gb~FEc*G^eGUh}v1K;s=j{sZm5-iLA+@E7mEm{f?5QvkCVcNtS#50?= z!FZpTS1OW9d`>)J(glehnJ#<$#<=V<$1-^{mz^i(iG^|x%RQ`AOpSP&IHqbk<#TzD zRmNM4wOT{z+>^a9Qq)(LnND*IX)Iz1A_T~2po$u7Bxu!1v5=+xl#hSZ^^4?^$yJ3y zjs?`AL2~_IfAG6ot28y~B}Gy||BK^%j02%vpxtzw?_n&jRlk{d+ ziyZ+&+rY(jSCjXE%N-#4WJspuNA(Ki)<;aecQ=JnRu$LRx*qpmVHz`-F9 zFH`oq&$|bDd;9lHr@tS%uX4j@<$mD+000tDOjJbx000}y0pv9P|NsBZTLBxI{*j+U z82|tP32;bRa{vGsOaK5FbOE2JKp+4B00(qQO+^Rj0SXrtDv^0$tN;K28FWQhbVF}# zZDnqB07G(RVRU6=Aa`kWXdp*PO;A^X4i^9b2=YlpK~#9!?Oh3u<2Vc?P#-El|Nq-s zEJ}8KByL-hy})G7OuFL=Zx$(!r58QyADD4q@FFmHlVreuHgoWJjb!*L$?%`e9112s z{>7Qbfu_AT;X0c3-jsjf4PYkIrT_j8$sj|)D`i7k$V^#P2d{Qh6OL>Klf}RW+YFA9_GB?O*np2dd=`zR!4cT@zj4Y2 zylyg=)wCZ=CU2Uc*l5Y@i-ut|J;7`&a*_=8j2PK^*;g{$gfW{w{q|pT$g}*TtD3f8 zCYQnHqFG6}j^!j6+X=zB39xO`lnuBmn2i-o>X-zRRuRQh6RtbcNif8YWVXu7=8_*h z!E8-(x+eE>^8|$KYcsiA!b(&b40aXTS27b2tS^wwbT04f;CDU3 zIMo0zBBp0fuY6`mt9X;eTt9`>zgWSC*A*?O76WFiJqU*(Mf(N!{5bF{jB z7XhL*39Bt0Z%18kBQTzpbx{*T1~0Ty#62Y`aWhs*9$FVLP8dd9iNvT$g$iTEJ+C?~ z`&0DU3o}MTFy^f-b;T?hD*b5sXVsFD=WnM@p!yh#>Z=VHQ$F+yndlBNR;+BIIDmZd zBrws)*#$?p?{WbZ)nqm@T_-(YtA|7u<&Jkaf1maI0;3L2t)ls-_xAGB$yDRGtb(#P zD=k+$FrqJm26CmO;5kdN5Kz=rS+pzkOdht}PBL7QQf@O;C$P8(N^M?hPme~T^HpLr z0W)K915oK3IcgQ85uHL>wZQm`8Go0Izg&6)`4VK-D~``r^+TwOC70ti7|xP$JIJ9v zwIik>V5P2)Fx56^mD0oCgD(70oZn#M%ecw5g2p`VPB% z^ry|I6?<_9j5&QKDtr!Zb~G!dr-6-Zjt9?w1LLnoC#e+aamEhb-L>I)y;4UmOP;mi z?_e15l=55!+GBh4)FjwtStQyzoB8m6-EYsti7`M#jQ zlC92?6TiI)i>IZH`}L;Y`W9U0tD3K1%oekN7lHebf#TJrTthtq%{Oqd3h34sp zWR~QQ+YBnD_&3qCk<2pnL}Ipko8lL9(`SHV#NfTv3-kuF9;Az9hfqU|vQ{ zQGT|D^Ic7wde^l2X6i6kp%{H7vz|t>k})bJVHl<+IPtvc8^ymw<88yhV6*fQ!!88V zc#nks8@-_VP$V|Wwf9D$7G0hHuARj(LCZ+KdcT4AD2vJZ7_6Y<&XfW zKJ^001hH01iKmZ9D}y!hr~B{0Sir&?z@(z5SAb!R-|+@8h|PpoNJis#yhAc6iko*x zCKmS|my8EFYDM84YKkSA7l0|dd+8;TiRt8Pz!)C38GL5$_ekdMR%cr^+4Epb+EPva ze0NYc+@F_BP!!(&^F9%A6O0`C1zCH!1&1b`lU)umZX?i|3fiEt%iqMhHl*E}rzGR% zK^=Sw-UvijhJ0(Si&jbdDfD$S%~1#UO@?K7@yP*8l{Xwr?M5KU#L6T@M2wEWg`kpT zeDRIJ-KX&^gq9A(T1Sx&s-x}fS0ESO9w5RYyZelt6Wo1m+Y{qjfH|d;+_U*0RDDL77 zln`gxqe)>s81_VQRf+l-8{k37qBg`v<|4Xh%G$ChmjbOC-amf*#A=h#5awpFZT;EE37pgZT{! zhDwHU;x;~iIscaX7VU#^R7c6`yt-#lX}nR+O{0wopIv*$Y%Pj0CNn7*G0K}bS1je4 ze)5EI5G!8A2!gR(vtAUbM%~!9!Z6nst*X01r1=y=81&-R+n~T{0gf@BgpXy~$JdL3{2_kZ#HKfwG4SU&^y3K$aB00000NkvXXu0mjf DoMu;n literal 0 HcmV?d00001 diff --git a/demos/trogdor/graphics/trog07_peasant.png b/demos/trogdor/graphics/trog07_peasant.png new file mode 100644 index 0000000000000000000000000000000000000000..eb946cae155a7383d465a60b554a992c353fe547 GIT binary patch literal 2020 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 U6f)1TivR!s07*qoM6N<$f^ZQN?EnA( literal 0 HcmV?d00001 diff --git a/demos/trogdor/graphics/trog11_cottage.png b/demos/trogdor/graphics/trog11_cottage.png new file mode 100644 index 0000000000000000000000000000000000000000..7a72c82e2f3ca118d158c7ceaa1b7ba5d9652d43 GIT binary patch literal 3689 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 Hu0mjf)Cc|n literal 0 HcmV?d00001 diff --git a/demos/trogdor/graphics/trog_flames1.png b/demos/trogdor/graphics/trog_flames1.png new file mode 100644 index 0000000000000000000000000000000000000000..eeec4ce435435d2d50b4a59ab5fa845e2613b53f GIT binary patch literal 1297 zcmeAS@N?(olHy`uVBq!ia0y~yV3Yu|53n!;N!?wEoXJIEZ34_+Fi#mA=eXu`s>P2F7<*R(_yI;{|mDm9S*+oZYiFU%Q`7 zIvLvP)W>>dp<~hCg!&J=qgR`lzEpJ$a`@lx&MjKdpNRj1{2(pAc6dEq$8tiOIkJ z|NozjW|W@wZ{mDsNuVfafk$L91A~MQ5SK8{U*Vtt6l5>)^mS!_!N?^pr0UoH`WH}0 zvcxr_#5q4VH#M(>!MP|ku_QG`p**uBL&4qCHz2%`Pn>~)>7J*HV@SoVw==x^4jb?| zbAE7QD*C^>_v^_TvyE?d9MHMp+Nk%)E>K97{kr`x2ZJWTo&*+UiGv(&bYcE{a{Nr+ z;TuP7B|hyja@*qk=eL3#19P6Ia}7({`hCZW?`mu)cqYfgVRm}$e|Cw=TOn-im!Bl% zYct>QoRM%d=k3K7-`oo+ACKMp8v01%%u}!Tv$k&ZZmyfQE%8sJobTm6qp4cpm@{Tto>=1Jg&Pn8SZ+SFsmkJwzlyzm1%5Kx}8!^?$}_%Anfki%#@v${E=my z&BF#GxqA|OQ%W?ZZJX7wF^KJQFUQ=fLWzj1;+sM~E{{&d@UC}UUM8%zDy>kZhrw;* z6r-AWW6iD2M|vU`cTQSpKX>_@2F=82z4~XTdlo7A9(*wA!nNspKQN13m7H-=psy(R zjCy}k$1L80C9{q0UcBRd_Q|#fkzTWQx^CU2*=64Huxi#LA@MZh-X%AE7c1B3?0oT7 zRa;jvFZxVg)!kcHC!c$CG4}6P8}0dlc{gvTC%ii&EoP<{{sPTQpk*2?4ZR_K#6StW^Q*HftxIbpcG4+nA!qJCpdRc5{y1XgnNsP$8 zc51JYUVcx#d78)hLrKr}{>%+Ed6;xfKcQ}a*9pHGKci=_4~M?aSIONv+s4;Nd)=?w ztt&m=%=>Gvmvg!Lwm;9;No^Sm&~^QFx4G||r!VK~Uvm4r}NeiFlS0*Mr|C{P}a`#uA@^HVJhur*E;}%O*@&5;A2L?}9KbLh*2~7Y$ C$VpoO literal 0 HcmV?d00001 diff --git a/demos/trogdor/graphics/trog_flames2.png b/demos/trogdor/graphics/trog_flames2.png new file mode 100644 index 0000000000000000000000000000000000000000..f5e7eb2c70b4729134a36f2dcc39fdbedb96089e GIT binary patch literal 9022 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 G5dL3r-Wv!2 literal 0 HcmV?d00001 diff --git a/demos/trogdor/graphics/trog_flames3.png b/demos/trogdor/graphics/trog_flames3.png new file mode 100644 index 0000000000000000000000000000000000000000..bb21d5ee0161b207fdd71a9eb3918c8d251e4091 GIT binary patch literal 1653 zcmV-*28#KKP)EX>4Tx04R}tkv&MmKpe$iQ>9WW4hAgZkfC<6i;6hbDionYs1;guFdzMbCJjl7 zi=*ILaPVWX>fqw6tAnc`2!4RLx;QDiNQwVT3N2zhIPS;0dyl(!0N1K1n$?#CG~G6{ z>6DN!tO}u5gb~FEc*G^eGUh}v1K;s=j{sZm5-iLA+@E7mEm{f?5QvkCVcNtS#50?= z!FZpTS1OW9d`>)J(glehnJ#<$#<=V<$1-^{mz^i(iG^|x%RQ`AOpSP&IHqbk<#TzD zRmNM4wOT{z+>^a9Qq)(LnND*IX)Iz1A_T~2po$u7Bxu!1v5=+xl#hSZ^^4?^$yJ3y zjs?`AL2~_IfAG6ot28y~B}Gy||BK^%j02%vpxtzw?_n&jRlk{d+ ziyZ+&+rY(jSCjXE%N-#4WJspuNA(Ki)<;aecQ=JnRu$LRx*qpmVHz`-F9 zFH`oq&$|bDd;9lHr@tS%uX4j@<$mD+000tDOjJbx000}y0pv9P|NsBZTLBxI{*j+U z82|tP32;bRa{vGsOaK5FbOE2JKp+4B00(qQO+^Rj0SXrrEG|G|9{>OV8FWQhbVF}# zZDnqB07G(RVRU6=Aa`kWXdp*PO;A^X4i^9b1VTwfK~#9!?VDSUV=)Lsk!nLo-v8dl z2JCx#)UCKbm_iz|faIungCnERM;fMK8m3_yrePYUVH&1k8m3_yrePYUVH&1k8m3_y zrePYUVH&1k8m3_yCh4QvFhBf&l!8Gk^S#fsGCu`VEoDl{mTrJ+1BP|cUOE#%y@&>{cVkv|7?5}PZ>I4z7fdo-BXpM^4r zrhI(5;wHiDlgiFBzQkHJlcbDFlmnYr;;z9^2DXikJhKWy;E{X=TbcD6@Ul&T*>m=O z;^bZM=bDfL<1dO3%4m!$pGVvz7_lB);=PsSJbXU2=fN0Hf|h4Koq4hf<(K~nB3_ILsRxEXi+d#+ z0SQK9-Kno?5D$(1{oo%k=gTdcNHD&!e?r@i2j({5z=%Bte&yqf#dIDF^xbyK&CN8+ zfvwRzSuccP!NkFTd>OVmF#9oO&_MjUO;W}m9PDLL#1d(U^R3?bdG!k}Yi{xvEBa*M zXTeA6^za-8sXcH^ar-QIQw#EYVBPIF1l+b0QHy-Pc-NAp(O}@L$hk1~VmnyI-0#5 IIgs Apple II-compat video modes + ; bit 6 = 0 -> IIgs 128K memory map same as IIe + ; bit 5 = 0 -> IIgs DHGR is color, not mono + ; bits 0-4 unchanged + sta NEWVIDEO + lda #$F0 + sta TBCOLOR ; white text on black background + lda #$00 + sta CLOCKCTL ; black border + sta CLOCKCTL ; set twice for VidHD + + ; gs always swaps in RAM + lda #gs_interrupt_handler + sta $3FF + +not_iigs: + + + ;====================== + ; detect mockingboard + + lda #0 + sta SOUND_STATUS + +;PT3_ENABLE_APPLE_IIC = 1 ; we set this earlier + + jsr mockingboard_detect + bcc mockingboard_notfound + +mockingboard_found: + + lda SOUND_STATUS + ora #SOUND_MOCKINGBOARD + sta SOUND_STATUS + +mockingboard_notfound: + + rts + +.include "pt3_lib_mockingboard.inc" diff --git a/demos/trogdor/hgr_clear_screen.s b/demos/trogdor/hgr_clear_screen.s new file mode 100644 index 00000000..aa0805ab --- /dev/null +++ b/demos/trogdor/hgr_clear_screen.s @@ -0,0 +1,89 @@ +hgr_clear_screen: + lda DRAW_PAGE + beq hgr_page1_clearscreen + lda #0 + beq hgr_page2_clearscreen + +hgr_page1_clearscreen: + + ldy #0 +hgr_page1_cls_loop: + sta $2000,Y + sta $2100,Y + sta $2200,Y + sta $2300,Y + sta $2400,Y + sta $2500,Y + sta $2600,Y + sta $2700,Y + sta $2800,Y + sta $2900,Y + sta $2A00,Y + sta $2B00,Y + sta $2C00,Y + sta $2D00,Y + sta $2E00,Y + sta $2F00,Y + sta $3000,Y + sta $3100,Y + sta $3200,Y + sta $3300,Y + sta $3400,Y + sta $3500,Y + sta $3600,Y + sta $3700,Y + sta $3800,Y + sta $3900,Y + sta $3A00,Y + sta $3B00,Y + sta $3C00,Y + sta $3D00,Y + sta $3E00,Y + sta $3F00,Y + iny + bne hgr_page1_cls_loop + + rts + + +hgr_page2_clearscreen: + + ldy #0 +hgr_page2_cls_loop: + sta $4000,Y + sta $4100,Y + sta $4200,Y + sta $4300,Y + sta $4400,Y + sta $4500,Y + sta $4600,Y + sta $4700,Y + sta $4800,Y + sta $4900,Y + sta $4A00,Y + sta $4B00,Y + sta $4C00,Y + sta $4D00,Y + sta $4E00,Y + sta $4F00,Y + sta $5000,Y + sta $5100,Y + sta $5200,Y + sta $5300,Y + sta $5400,Y + sta $5500,Y + sta $5600,Y + sta $5700,Y + sta $5800,Y + sta $5900,Y + sta $5A00,Y + sta $5B00,Y + sta $5C00,Y + sta $5D00,Y + sta $5E00,Y + sta $5F00,Y + iny + bne hgr_page2_cls_loop + + rts + diff --git a/demos/trogdor/hgr_page_flip.s b/demos/trogdor/hgr_page_flip.s new file mode 100644 index 00000000..4a1487da --- /dev/null +++ b/demos/trogdor/hgr_page_flip.s @@ -0,0 +1,18 @@ + +hgr_page_flip: + lda DRAW_PAGE + beq flip_to_page1 + +flip_to_page2: + bit PAGE2 + lda #0 + beq done_hgr_page_flip ; bra + +flip_to_page1: + bit PAGE1 + lda #$20 + +done_hgr_page_flip: + sta DRAW_PAGE + + rts diff --git a/demos/trogdor/hgr_sprite_big.s b/demos/trogdor/hgr_sprite_big.s new file mode 100644 index 00000000..e25645eb --- /dev/null +++ b/demos/trogdor/hgr_sprite_big.s @@ -0,0 +1,205 @@ + ;=========================================== + ; hgr draw sprite (only at 7-bit boundaries) + ;=========================================== + ; can handle sprites bigger than a 256 byte page + + ; Note this is optimized for blue/orange sprites + ; it treats black0 as transparent + + ; SPRITE in INL/INH + ; Location at SPRITE_X SPRITE_Y + + ; xsize, ysize in first two bytes + + ; sprite AT INL/INH + + + ; orange = color5 1 101 0101 1 010 1010 + +hgr_draw_sprite_big: + lda SPRITE_X + ror + bcs hgr_draw_sprite_big_odd + +hgr_draw_sprite_big_even: + ldy #0 + lda (INL),Y ; load xsize + clc + adc SPRITE_X + sta big_sprite_width_end_smc+1 ; self modify for end of line + + iny ; load ysize + lda (INL),Y + sta big_sprite_ysize_smc+1 ; self modify + + ; point smc to sprite + lda INL ; 16-bit add + sta big_sprite_smc1+1 + lda INH + sta big_sprite_smc1+2 + + + ldx #0 ; X is pointer offset + stx CURRENT_ROW ; actual row + + ldx #2 + +hgr_big_sprite_yloop: + + lda CURRENT_ROW ; row + + clc + adc SPRITE_Y ; add in cursor_y + + ; calc GBASL/GBASH + + tay ; get output ROW into GBASL/H + lda hposn_low,Y + sta GBASL + lda hposn_high,Y + + clc + adc DRAW_PAGE + sta GBASH + + ldy SPRITE_X + +big_sprite_inner_loop: + + +big_sprite_smc1: + lda $f000,X ; load sprite data + beq big_sprite_transparent + sta (GBASL),Y ; store to screen + +big_sprite_transparent: + inx ; increment sprite offset + + ; if > 1 page + bne big_sprite_no_page_cross + inc big_sprite_smc1+2 + +big_sprite_no_page_cross: + iny ; increment output position + + +big_sprite_width_end_smc: + cpy #6 ; see if reached end of row + bne big_sprite_inner_loop ; if not, loop + + + inc CURRENT_ROW ; row + lda CURRENT_ROW ; row + +big_sprite_ysize_smc: + cmp #31 ; see if at end + bne hgr_big_sprite_yloop ; if not, loop + + rts + + + +hgr_draw_sprite_big_odd: + ldy #0 + lda (INL),Y ; load xsize + clc + adc SPRITE_X + sta osprite_width_end_smc+1 ; self modify for end of line + + iny ; load ysize + lda (INL),Y + sta osprite_ysize_smc+1 ; self modify + + ; point smc to sprite + lda INL ; 16-bit add + sta osprite_smc1+1 + lda INH + sta osprite_smc1+2 + + + ldx #0 ; X is pointer offset + stx CURRENT_ROW ; actual row + + ldx #2 + +ohgr_sprite_yloop: + + lda CURRENT_ROW ; row + + clc + adc SPRITE_Y ; add in cursor_y + + ; calc GBASL/GBASH + + tay ; get output ROW into GBASL/H + lda hposn_low,Y + sta GBASL + lda hposn_high,Y + + clc + adc DRAW_PAGE + sta GBASH + + ldy SPRITE_X + + clc + php ; store 0 carry on stack + +osprite_inner_loop: + + +osprite_smc1: + lda $f000,X ; load sprite data + + bne osprite_not_transparent + + ; we can't just skip if 0 because we might shift a bit in + ; from previous byte + + plp + bcs osprite_oops + clc + php + bcc osprite_transparent_done + +osprite_not_transparent: + plp ; restore carry from last +osprite_oops: + rol ; rotate in carry + asl ; one more time, bit6 in carry + php ; save on stack + sec ; assume blur/orange + ror ; rotate it back down + + sta (GBASL),Y ; store to screen + + +osprite_transparent_done: + inx ; increment sprite offset + + ; if > 1 page + bne osprite_no_page_cross + inc osprite_smc1+2 + +osprite_no_page_cross: + + iny ; increment output position + + + +osprite_width_end_smc: + cpy #6 ; see if reached end of row + bne osprite_inner_loop ; if not, loop + + + plp ; restore stack + + inc CURRENT_ROW ; row + lda CURRENT_ROW ; row + +osprite_ysize_smc: + cmp #31 ; see if at end + bne ohgr_sprite_yloop ; if not, loop + + rts + diff --git a/demos/trogdor/hgr_table.s b/demos/trogdor/hgr_table.s new file mode 100644 index 00000000..2a6d5a99 --- /dev/null +++ b/demos/trogdor/hgr_table.s @@ -0,0 +1,97 @@ +;div7_table = $b800 +;mod7_table = $b900 +;hposn_high = $ba00 +;hposn_low = $bb00 + + +hgr_make_tables: + + ;===================== + ; make /7 %7 tables + ;===================== + +hgr_make_7_tables: + + ldy #0 + lda #0 + ldx #0 +div7_loop: + sta div7_table,Y + + inx + cpx #7 + bne div7_not7 + + clc + adc #1 + ldx #0 +div7_not7: + iny + bne div7_loop + + + ldy #0 + lda #0 +mod7_loop: + sta mod7_table,Y + clc + adc #1 + cmp #7 + bne mod7_not7 + lda #0 +mod7_not7: + iny + bne mod7_loop + + + ; Hposn table + +; hposn_low, hposn_high will each be filled with $C0 bytes +; based on routine by John Brooks +; posted on comp.sys.apple2 on 2018-07-11 +; https://groups.google.com/d/msg/comp.sys.apple2/v2HOfHOmeNQ/zD76fJg_BAAJ +; clobbers A,X +; preserves Y + +; vmw note: version I was using based on applesoft HPOSN was ~64 bytes +; this one is 37 bytes + +build_hposn_tables: + ldx #0 +btmi: + txa + and #$F8 + bpl btpl1 + ora #5 +btpl1: + asl + bpl btpl2 + ora #5 +btpl2: + asl + asl + sta hposn_low, X + txa + and #7 + rol + asl hposn_low, X + rol + ora #$20 + sta hposn_high, X + inx + cpx #$C0 + bne btmi + +; go 16 beyond, which allows our text scrolling routine + + ldx #16 +extra_table_loop: + lda hposn_low,X + sta hposn_low+192,X + lda hposn_high,X + eor #$60 + sta hposn_high+192,X + dex + bpl extra_table_loop + + rts diff --git a/demos/trogdor/interrupt_handler.s b/demos/trogdor/interrupt_handler.s new file mode 100644 index 00000000..f2d73df3 --- /dev/null +++ b/demos/trogdor/interrupt_handler.s @@ -0,0 +1,74 @@ + ;================================ + ;================================ + ; 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) + + lda IRQ_COUNTDOWN + beq skip_irq_dec + dec IRQ_COUNTDOWN +skip_irq_dec: + + +.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/trogdor/irq_wait.s b/demos/trogdor/irq_wait.s new file mode 100644 index 00000000..96264c9f --- /dev/null +++ b/demos/trogdor/irq_wait.s @@ -0,0 +1,94 @@ + ;============================ + ; wait for music pattern + ; also check for keypress + ;============================ + ; pattern # in A +wait_for_pattern: + cmp current_pattern_smc+1 + bcc done_check_pattern_done ; blt + beq done_check_pattern_done ; ble + + lda KEYPRESS + bpl done_check_pattern_notdone + bit KEYRESET + jmp done_check_pattern_done + + ;============================ + ; setup timeout of A seconds + ;============================ +setup_timeout: + sta SECOND_COUNTDOWN + lda #0 + sta IRQ_COUNTDOWN + rts + + ;=========================== + ; countodown second timeout + ; also check for keypress + ;=========================== + ; carry set = done +check_timeout: + ; check keyboard first + lda KEYPRESS + bpl timeout_not_keypress + bit KEYRESET +; lda #0 ; reset, is this necessary? +; sta IRQ_COUNTDOWN +; sta SECOND_COUNTDOWN + jmp done_check_timeout_done + +timeout_not_keypress: + lda IRQ_COUNTDOWN + bne done_check_timeout_notdone +irq_countdown_zero: + lda SECOND_COUNTDOWN + beq done_check_timeout_done + + ; otherwise we need to decrement and update + dec SECOND_COUNTDOWN + lda #50 + sta IRQ_COUNTDOWN + +done_check_pattern_notdone: +done_check_timeout_notdone: + clc + rts + +done_check_pattern_done: +done_check_timeout_done: + sec + rts + + + ;========================== + ; busy wait A * 1 50Hz tick + ;========================== +wait_ticks: + sta IRQ_COUNTDOWN +wait_tick_loop: + lda IRQ_COUNTDOWN + bne wait_tick_loop +wait_tick_done: + rts + + + ;==================== + ; busy wait A seconds + ;==================== + ; exit early if key pressed + +wait_seconds: + tax + +wait_seconds_loop: + lda #50 ; wait 1s + jsr wait_ticks + + lda KEYPRESS + bmi wait_seconds_done + + dex + bpl wait_seconds_loop +wait_seconds_done: + bit KEYRESET + rts diff --git a/demos/trogdor/lc_detect.s b/demos/trogdor/lc_detect.s new file mode 100644 index 00000000..3123a6ae --- /dev/null +++ b/demos/trogdor/lc_detect.s @@ -0,0 +1,40 @@ +; Code from TotalReplay by 4am and qkumba + +;------------------------------------------------------------------------------ +; Has64K +; Checks whether computer has functioning language card (64K) +; +; in: none +; out: C clear if 64K detected +; C set if 64K not detected +; all other flags and registers clobbered +; ROM in memory (not LC RAM bank) +;------------------------------------------------------------------------------ + +detect_language_card: + + ; enable language card + ; READ_RAM1_WRITE_RAM1 + + bit $C08B + bit $C08B + + lda #$AA ; test #1 for $D0 page + sta $D000 + eor $D000 + bne no_lc + lsr $D000 ; test #2 for $D0 page + lda #$55 + eor $D000 + bne no_lc + clc + bcc done_detect + +no_lc: + sec + +done_detect: + ; READ_ROM_NO_WRITE + bit $C08A + + rts diff --git a/demos/trogdor/music.s b/demos/trogdor/music.s new file mode 100644 index 00000000..823db60b --- /dev/null +++ b/demos/trogdor/music.s @@ -0,0 +1,30 @@ +; music, music + +; by Vince `deater` Weaver vince@deater.net + +.include "hardware.inc" +.include "zp.inc" + +;.include "qload.inc" + +music_lib: + +PT3_ENABLE_APPLE_IIC = 1 + +; nop ; urgh to keep interrupt_handler from starting at $C4 + ; which broke auto-patcher + + ; pt3 player +; .include "pt3_lib_detect_model.s" + .include "pt3_lib_core.s" + .include "pt3_lib_init.s" + .include "pt3_lib_mockingboard_setup.s" + .include "interrupt_handler.s" + .include "pt3_lib_mockingboard_detect.s" + + +; only load one music track, self modify to make other + +.align $100 +PT3_LOC: +;.incbin "music/Walking_In_The_Air_mA2E.pt3" diff --git a/demos/trogdor/pt3_lib_core.s b/demos/trogdor/pt3_lib_core.s new file mode 100644 index 00000000..9a0c350e --- /dev/null +++ b/demos/trogdor/pt3_lib_core.s @@ -0,0 +1,2023 @@ +;=========================================== +; 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 + + ; BUG BUG BUG + ; pattern offset can be bigger than 128, and if we multiply + ; by two to get word size it will overflow + ; for example I have a .pt3 where pattern #48 ($30*3=$90) is used + +; 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 + + clc + sta PATTERN_L + adc PT3_LOC+PT3_PATTERN_LOC_L + php ; save carry as we might generate two + clc + adc PATTERN_L + sta PATTERN_L + + lda PT3_LOC+PT3_PATTERN_LOC_H ; 4 + adc #>PT3_LOC ; assume page boundary ; 2 + plp ; restore carry + adc #0 + sta PATTERN_H ; 3 + +; clc +; tya +; adc PATTERN_L +; adc PATTERN_L +; sta PATTERN_L +; lda #0 +; adc PATTERN_H +; sta PATTERN_H + + ldy #0 + + ; 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/trogdor/pt3_lib_detect_model.s b/demos/trogdor/pt3_lib_detect_model.s new file mode 100644 index 00000000..ac30fd4e --- /dev/null +++ b/demos/trogdor/pt3_lib_detect_model.s @@ -0,0 +1,95 @@ + ;=========================== + ; Check Apple II model + ;=========================== + ; this is mostly for IIc support + ; as it does interrupts differently + + ; some of this info from the document: + ; Apple II Family Identification Routines 2.2 + ; + + ; ' ' = Apple II + ; '+' = Apple II+ + ; 'e' = Apple IIe + ; 'c' = Apple IIc + ; 'g' = Apple IIgs + ; 'm' = mac L/C with board + ; 'j' = jplus + ; '3' = Apple III + +detect_appleii_model: + lda #' ' + + ldx $FBB3 + + ; II is $38 + ; J-plus is $C9 + ; II+ is $EA (so is III) + ; IIe and newer is $06 + + cpx #$38 ; ii + beq done_apple_detect + + + ; ii+ is EA FB1E=AD + ; iii is EA FB1E=8A 00 + + cpx #$EA + bne not_ii_iii +ii_or_iii: + + lda #'+' ; ii+/iii + + ldx $FB1E + cpx #$AD + beq done_apple_detect ; ii+ + + lda #'3' + bne done_apple_detect ; bra iii + +not_ii_iii: + lda #'j' ; jplus + cpx #$C9 + beq done_apple_detect + + + cpx #$06 + bne done_apple_detect + +apple_iie_or_newer: + + + + ldx $FBC0 ; $EA on a IIe + ; $E0 on a IIe enhanced + ; $00 on a IIc/IIc+ + + ; $FE1F = $60, IIgs + + beq apple_iic + + lda #'e' + cpx #$EA + beq done_apple_detect +; cpx #$E0 +; beq done_apple_detect + + ; should do something if not $E0 + + ; GS and IIe enhanced are the same, need to check + + sec ; set carry + jsr $FE1F + bcs done_apple_detect ;If carry then IIe enhanced + + ; get here we're a IIgs? + + lda #'g' + bne done_apple_detect + +apple_iic: + lda #'c' + +done_apple_detect: + sta APPLEII_MODEL + rts diff --git a/demos/trogdor/pt3_lib_init.s b/demos/trogdor/pt3_lib_init.s new file mode 100644 index 00000000..037b1ade --- /dev/null +++ b/demos/trogdor/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/trogdor/pt3_lib_irq_handler.s b/demos/trogdor/pt3_lib_irq_handler.s new file mode 100644 index 00000000..851a22bf --- /dev/null +++ b/demos/trogdor/pt3_lib_irq_handler.s @@ -0,0 +1,116 @@ + + +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 + +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/trogdor/pt3_lib_mockingboard.inc b/demos/trogdor/pt3_lib_mockingboard.inc new file mode 100644 index 00000000..80a8fdd1 --- /dev/null +++ b/demos/trogdor/pt3_lib_mockingboard.inc @@ -0,0 +1,54 @@ +; Mockingboad programming: +; + Has two 6522 I/O chips connected to two AY-3-8910 chips +; + Optionally has some speech chips controlled via the outport on the AY +; + Often in slot 4 +; TODO: how to auto-detect? +; References used: +; http://macgui.com/usenet/?group=2&id=8366 +; 6522 Data Sheet +; AY-3-8910 Data Sheet + +;======================== +; Mockingboard card +; Essentially two 6522s hooked to the Apple II bus +; Connected to AY-3-8910 chips +; PA0-PA7 on 6522 connected to DA0-DA7 on AY +; PB0 on 6522 connected to BC1 +; PB1 on 6522 connected to BDIR +; PB2 on 6522 connected to RESET + + +; left speaker +MOCK_6522_ORB1 = $C400 ; 6522 #1 port b data +MOCK_6522_ORA1 = $C401 ; 6522 #1 port a data +MOCK_6522_DDRB1 = $C402 ; 6522 #1 data direction port B +MOCK_6522_DDRA1 = $C403 ; 6522 #1 data direction port A +MOCK_6522_T1CL = $C404 ; 6522 #1 t1 low order latches +MOCK_6522_T1CH = $C405 ; 6522 #1 t1 high order counter +MOCK_6522_T1LL = $C406 ; 6522 #1 t1 low order latches +MOCK_6522_T1LH = $C407 ; 6522 #1 t1 high order latches +MOCK_6522_T2CL = $C408 ; 6522 #1 t2 low order latches +MOCK_6522_T2CH = $C409 ; 6522 #1 t2 high order counters +MOCK_6522_SR = $C40A ; 6522 #1 shift register +MOCK_6522_ACR = $C40B ; 6522 #1 auxilliary control register +MOCK_6522_PCR = $C40C ; 6522 #1 peripheral control register +MOCK_6522_IFR = $C40D ; 6522 #1 interrupt flag register +MOCK_6522_IER = $C40E ; 6522 #1 interrupt enable register +MOCK_6522_ORANH = $C40F ; 6522 #1 port a data no handshake + + +; right speaker +MOCK_6522_ORB2 = $C480 ; 6522 #2 port b data +MOCK_6522_ORA2 = $C481 ; 6522 #2 port a data +MOCK_6522_DDRB2 = $C482 ; 6522 #2 data direction port B +MOCK_6522_DDRA2 = $C483 ; 6522 #2 data direction port A + +; AY-3-8910 commands on port B +; RESET BDIR BC1 +MOCK_AY_RESET = $0 ; 0 0 0 +MOCK_AY_INACTIVE = $4 ; 1 0 0 +MOCK_AY_READ = $5 ; 1 0 1 +MOCK_AY_WRITE = $6 ; 1 1 0 +MOCK_AY_LATCH_ADDR = $7 ; 1 1 1 + + diff --git a/demos/trogdor/pt3_lib_mockingboard_detect.s b/demos/trogdor/pt3_lib_mockingboard_detect.s new file mode 100644 index 00000000..544eb429 --- /dev/null +++ b/demos/trogdor/pt3_lib_mockingboard_detect.s @@ -0,0 +1,222 @@ +;=================================================================== +; 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 +; A/Y clobbered +;------------------------------------------------------------------------------ + +mockingboard_detect: + + ; activate Mockingboard IIc + ; + the Mockingboard has to take over Slot#4 (IIc has no slots) + ; in theory any write to the firmware area in $C400 will + ; activate it, but that might not be fast enough when detecting + ; so writing $FF to $C403/$C404 is official way to enable + ; + Note this disables permanently the mouse firmware in $C400 + ; so "normal" interrupts are broken :( The hack to fix things + ; is to switch in RAM for $F000 and just replace the IRQ + ; vectors at $FFFE/$FFFF instead of $3FE/$3FF but that makes + ; it difficult if you actually wanted to use any + ; Applesoft/Monitor ROM routines + +.ifdef PT3_ENABLE_APPLE_IIC + lda APPLEII_MODEL + cmp #'c' + bne not_iic + + lda #$ff + + ; don't bother patching these, IIc mockingboard always slot 4 + + sta MOCK_6522_DDRA1 ; $C403 + sta MOCK_6522_T1CL ; $C404 +.endif + +not_iic: + 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 + + + +.if 0 + + + ;======================================= + ; Detect a Mockingboard card + ;======================================= + ; Based on code from the French Touch "Pure Noise" Demo + ; Attempts to time an instruction sequence with a 6522 + ; + ; If found, puts in bMB + ; MB_ADDRL:MB_ADDRH has address of Mockingboard + ; returns X=0 if not found, X=1 if found + +mockingboard_detect: + lda #0 + sta MB_ADDRL + +mb_detect_loop: ; self-modifying + lda #$07 ; we start in slot 7 ($C7) and go down to 0 ($C0) + ora #$C0 ; make it start with C + sta MB_ADDRH + ldy #04 ; $CX04 + ldx #02 ; 2 tries? +mb_check_cycle_loop: + lda (MB_ADDRL),Y ; timer 6522 (Low Order Counter) + ; count down + sta PT3_TEMP ; 3 cycles + lda (MB_ADDRL),Y ; + 5 cycles = 8 cycles + ; between the two accesses to the timer + sec + sbc PT3_TEMP ; subtract to see if we had 8 cycles + cmp #$f8 ; -8 + bne mb_not_in_this_slot + dex ; decrement, try one more time + bne mb_check_cycle_loop ; loop detection + inx ; Mockingboard found (X=1) +done_mb_detect: + ;stx bMB ; store result to bMB + rts ; return + +mb_not_in_this_slot: + dec mb_detect_loop+1 ; decrement the "slot" (self_modify) + bne mb_detect_loop ; loop down to one + ldx #00 + beq done_mb_detect + +;alternative MB detection from Nox Archaist +; lda #$04 +; sta MB_ADDRL +; ldx #$c7 +; +;find_mb: +; stx MB_ADDRH +; +; ;detect sound I +; +; sec +; ldy #$00 +; lda (MB_ADDRL), y +; sbc (MB_ADDRL), y +; cmp #$05 +; beq found_mb +; dex +; cpx #$c0 +; bne find_mb +; ldx #$00 ;no mockingboard found +; rts +; +;found_mb: +; ldx #$01 ;mockingboard found +; rts +; +; ;optionally detect sound II +; +; sec +; ldy #$80 +; lda (MB_ADDRL), y +; sbc (MB_ADDRL), y +; cmp #$05 +; beq found_mb + + + ;======================================= + ; Detect a Mockingboard card in Slot4 + ;======================================= + ; Based on code from the French Touch "Pure Noise" Demo + ; Attempts to time an instruction sequence with a 6522 + ; + ; MB_ADDRL:MB_ADDRH has address of Mockingboard + ; returns X=0 if not found, X=1 if found + +mockingboard_detect_slot4: + lda #0 + sta MB_ADDRL + +mb4_detect_loop: ; self-modifying + lda #$04 ; we're only looking in Slot 4 + ora #$C0 ; make it start with C + sta MB_ADDRH + ldy #04 ; $CX04 + ldx #02 ; 2 tries? +mb4_check_cycle_loop: + lda (MB_ADDRL),Y ; timer 6522 (Low Order Counter) + ; count down + sta PT3_TEMP ; 3 cycles + lda (MB_ADDRL),Y ; + 5 cycles = 8 cycles + ; between the two accesses to the timer + sec + sbc PT3_TEMP ; subtract to see if we had 8 cycles + cmp #$f8 ; -8 + bne mb4_not_in_this_slot + dex ; decrement, try one more time + bne mb4_check_cycle_loop ; loop detection + inx ; Mockingboard found (X=1) +done_mb4_detect: + rts ; return + +mb4_not_in_this_slot: + ldx #00 + beq done_mb4_detect + + + +.endif diff --git a/demos/trogdor/pt3_lib_mockingboard_patch.s b/demos/trogdor/pt3_lib_mockingboard_patch.s new file mode 100644 index 00000000..728396c9 --- /dev/null +++ b/demos/trogdor/pt3_lib_mockingboard_patch.s @@ -0,0 +1,121 @@ + +;=================================================================== +; 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: + + ; 16-bit increment + + inc 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 + + + + ;======================================= + ; mute AY -- just turn off all 3 channels + ; should silence the card + ; + ;======================================= +mute_ay_both: + ldx #7 ; + lda #$FF ; + sta MB_VALUE ; +mute_ay_left_loop: + jsr write_ay_both ; + + rts ; + + ;======================================= + ; unmute AY + ; restore to value we had before muting + ;======================================= +unmute_ay_both: + ldx #7 ; + lda ENABLE ; + sta MB_VALUE ; +unmute_ay_left_loop: + jsr write_ay_both ; + + rts ; + + + + +clear_ay_end: +;.assert >clear_ay_both = >clear_ay_end, error, "clear_ay_both crosses page" + + ;============================= + ; Setup + ;============================= +mockingboard_setup_interrupt: + + + ; for this game with things in language card including + ; irq handler, always force IIc mode (where RAM swapped in + ; and we put the irq handler address directly up at $FFFE) + + lda #interrupt_handler + sta $ffff + + ; note elsewhere we put gs_interrupt_handler in $3FE/$3FF + + ; nop out the "lda $45" since we are bypassing the ROM irq handler + ; that puts A in $45 + lda #$EA + sta interrupt_smc + sta interrupt_smc+1 + + + ;========================= + ; 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 + ;============================ + + + ; Note, on Apple II the clock isn't 1MHz but is actually closer to + ; roughly 1.023MHz, and every 65th clock is stretched (it's complicated) + + ; 4fe7 / 1.023e6 = .020s, 50Hz + ; 9c40 / 1.023e6 = .040s, 25Hz + ; 411a / 1.023e6 = .016s, 60Hz + + ; French Touch uses + ; 4e20 / 1.000e6 = .020s, 50Hz, which assumes 1MHz clock freq + + 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 +; lda #$20 +setup_irq_smc5: + sta MOCK_6522_T1CL ; write into low-order latch + lda #$4f +; lda #$4E +setup_irq_smc6: + sta MOCK_6522_T1CH ; write into high-order latch, + ; load both values into counter + ; clear interrupt and start counting + + rts + + + + ;============================= + ; Disable Interrupt + ;============================= +mockingboard_disable_interrupt: + + sei ; disable interrupts just in case + + lda #$40 ; Continuous interrupts, don't touch PB7 +disable_irq_smc1: + sta MOCK_6522_ACR ; ACR register + lda #$7F ; clear all interrupt flags +disable_irq_smc2: + sta MOCK_6522_IER ; IER register (interrupt enable) + + rts + diff --git a/demos/trogdor/qboot.inc b/demos/trogdor/qboot.inc new file mode 100644 index 00000000..65fbf7dd --- /dev/null +++ b/demos/trogdor/qboot.inc @@ -0,0 +1,8 @@ +seek = $1126 +driveon = $119D +driveoff = $1122 +load_new = $11AB +load_address=$11C4 +load_track=load_address+1 +load_sector=load_address+2 +load_length=load_address+3 diff --git a/demos/trogdor/qboot_sector.s b/demos/trogdor/qboot_sector.s new file mode 100644 index 00000000..2b3901b1 --- /dev/null +++ b/demos/trogdor/qboot_sector.s @@ -0,0 +1,244 @@ +; fast seek/multi-read +; copyright (c) Peter Ferrie 2015-16 + + ; Paramaters for loading QLOAD + + sectors = 14 ; user-defined + firsttrk = 1 ; user-defined, first track to read + firstsec = 0 ; user-defined, first sector to read + address = $12 ; user-defined + entry = $1200 ; 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 +; 3D = 1 + + ; 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 $10/$11 + + ; assume A=1 coming in here + + lsr ; check sector number + ; A=0, carry=1 + tay ; Y=0 + adc #$0f ; A=$10 (destintation) + + sta $27 ; set or update address as needed + cmp #$12 + ; 10 11 12 (1 1 1) + ; be, bf, c0 (1011 1011 1100) + ; 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 +; $1000 in this case +;*=$8fe + .byte $10, $00 + + +.include "qboot_stage2.s" diff --git a/demos/trogdor/qboot_stage2.s b/demos/trogdor/qboot_stage2.s new file mode 100644 index 00000000..6a38148f --- /dev/null +++ b/demos/trogdor/qboot_stage2.s @@ -0,0 +1,382 @@ +; 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 + +WAIT = norom_wait + +.org $1000 + +code_begin: + + .byte version + +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 + + + +; copy of ROM wait +; because we might disable ROM + +norom_wait: + sec +wait2: + pha +wait3: + sbc #$01 + bne wait3 + pla + sbc #$01 + bne wait2 + rts +wait_end: + diff --git a/demos/trogdor/qload.s b/demos/trogdor/qload.s new file mode 100644 index 00000000..5213ced7 --- /dev/null +++ b/demos/trogdor/qload.s @@ -0,0 +1,208 @@ +; Loader + +.include "zp.inc" +.include "hardware.inc" +.include "music.inc" + +;.include "common_defines.inc" +.include "qboot.inc" + +qload_start: + + ; init the write code +; lda WHICH_SLOT +; jsr popwr_init + + ; first time entry + ; start by loading text title + +; lda #0 ; load ZW engine +; sta WHICH_LOAD + + lda #1 + sta CURRENT_DISK ; current disk number + +; jsr load_file + + jmp xmas_start + +; jmp $2000 ; jump to ZW + + ;==================================== + ; loads file specified by WHICH_LOAD + ;==================================== +load_file: + ldx WHICH_LOAD + + 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 + + rts + + ;=================================================== + ;=================================================== + ; change disk + ;=================================================== + ;=================================================== + +change_disk: +.if 0 + ; 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 + ; offset 59 + ; disk1 = $0a + ; disk2 = $32 ('2') + ; disk3 = $33 ('3') + + lda $c59 + cmp #$0a + beq is_disk1 + cmp #$32 + beq is_disk2 + cmp #$33 + 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 ; MUSIC, XMAS + +load_address_array: + .byte $D0,$80 ; MUSIC, XMAS + +start_address: + .byte $D0,$80 ; MUSIC, XMAS + +track_array: + .byte 4,12 ; MUSIC, XMAS + +sector_array: + .byte 0,0 ; MUSIC, XMAS + +length_array: + .byte 32,32 ; MUSIC, XMAS + +PT3_ENABLE_APPLE_IIC = 1 + + .include "wait.s" + + .include "start.s" + + .include "lc_detect.s" + + .include "wait_a_bit.s" + .include "gr_fast_clear.s" + .include "text_print.s" + .include "gr_offsets.s" + + .include "pt3_lib_detect_model.s" + .include "pt3_lib_mockingboard_detect.s" + +mod7_table = $1c00 +div7_table = $1d00 +hposn_low = $1e00 +hposn_high = $1f00 + + .include "hgr_table.s" + +qload_end: + +.assert (>qload_end - >qload_start) < $e , error, "loader too big" diff --git a/demos/trogdor/start.s b/demos/trogdor/start.s new file mode 100644 index 00000000..34584de5 --- /dev/null +++ b/demos/trogdor/start.s @@ -0,0 +1,197 @@ +; XMAS 2023 + +; +; by deater (Vince Weaver) + + + +xmas_start: + + ;===================== + ; initializations + ;===================== + + jsr hardware_detect + + jsr hgr_make_tables + + + ;=================== + ; restart? + ;=================== +restart: + + lda #0 + sta DRAW_PAGE + + + ;================================== + ; load music into the language card + ; into $D000 set 1 + ;================================== + + ; read/write RAM, use $d000 bank1 + bit $C083 + bit $C083 + + lda #0 ; load MUSIC from disk + sta WHICH_LOAD + + jsr load_file + + lda #0 + sta DONE_PLAYING + + lda #1 + sta LOOP + + ; patch mockingboard + + lda SOUND_STATUS + beq skip_mbp1 + + jsr mockingboard_patch ; patch to work in slots other than 4? + +skip_mbp1: + + ;======================= + ; Set up 50Hz interrupt + ;======================== + + jsr mockingboard_init + jsr mockingboard_setup_interrupt + + ;============================ + ; Init the Mockingboard + ;============================ + + jsr reset_ay_both + jsr clear_ay_both + + ;================== + ; init song + ;================== + + jsr pt3_init_song + +dont_enable_mc: + +skip_all_checks: + + ;======================= + ;======================= + ; Print message + ;======================= + ;======================= + + ; print non-inverse + + jsr set_normal + + lda SOUND_STATUS + and #SOUND_MOCKINGBOARD + beq print_no_mock + +print_mock: + lda MB_ADDR_H + and #$7 + clc + adc #$B0 + sta mockingboard_string+29 + + lda #mockingboard_string + jmp done_set_message + +print_no_mock: + lda #no_mockingboard_string + +done_set_message: + sta OUTH + + ; print the text + + jsr move_and_print + + + + + ;======================= + ;======================= + ; Load xmas + ;======================= + ;======================= +load_xmas: + + ; load from disk + + lda #1 ; XMAS + sta WHICH_LOAD + jsr load_file + + ;======================= + ;======================= + ; Run intro + ;======================= + ;======================= + +; cli ; start music + + jsr $8000 + + + + +; bit PAGE1 ; be sure we're on PAGE1 + + ; clear text screen +; lda #$A0 +; sta clear_all_color+1 +; jsr clear_all + + ; switch to text/gr +; bit TEXTGR + + ; print non-inverse + +; jsr set_normal + + ; print messages +; lda #disk_change_string +; sta OUTH + + ; print the text + +; jsr move_and_print + +; bit KEYRESET ; just to be safe +; jsr wait_until_keypress + + +forever: + jmp forever + + + .include "wait_keypress.s" + .include "zx02_optim.s" + + .include "gs_interrupt.s" + +;.include "title.s" + + +; 0123456789012345678901234567890123456789 +mockingboard_string: +.byte 6,22,"MOCKINGBOARD DETECTED SLOT 4",0 + +no_mockingboard_string: +.byte 3,22,"NO MOCKINGBOARD, CONTINUING ANYWAY",0 + +.include "pt3_lib_mockingboard_patch.s" + +.include "hardware_detect.s" diff --git a/demos/trogdor/text_print.s b/demos/trogdor/text_print.s new file mode 100644 index 00000000..34bae3db --- /dev/null +++ b/demos/trogdor/text_print.s @@ -0,0 +1,93 @@ + + ;================================ + ; move_and_print + ;================================ + ; get X,Y from OUTL/OUTH + ; then print following string to that address + ; stop at NUL + ; convert to APPLE ASCII (or with 0x80) + ; leave OUTL/OUTH pointing to next string + +move_and_print: + ldy #0 + lda (OUTL),Y + sta CH + iny + lda (OUTL),Y + asl + tay + lda gr_offsets,Y ; lookup low-res memory address + clc + adc CH ; add in xpos + sta BASL ; store out low byte of addy + + lda gr_offsets+1,Y ; look up high byte + adc DRAW_PAGE ; + sta BASH ; and store it out + ; BASH:BASL now points at right place + + clc + lda OUTL + adc #2 + sta OUTL + lda OUTH + adc #0 + sta OUTH + + ;================================ + ; print_string + ;================================ + +print_string: + ldy #0 +print_string_loop: + lda (OUTL),Y + beq done_print_string +ps_smc1: + and #$3f ; make sure we are inverse + sta (BASL),Y + iny + bne print_string_loop +done_print_string: + iny + clc + tya + adc OUTL + sta OUTL + lda OUTH + adc #0 + sta OUTH + + rts + + ; set normal text +set_normal: + lda #$80 + sta ps_smc1+1 + + lda #09 ; ora + sta ps_smc1 + + rts + + ; restore inverse text +set_inverse: + lda #$29 + sta ps_smc1 + lda #$3f + sta ps_smc1+1 + + rts + + + ;================================ + ; move and print a list of lines + ;================================ + ; look for negative X meaning done +move_and_print_list: + jsr move_and_print + ldy #0 + lda (OUTL),Y + bpl move_and_print_list + + rts diff --git a/demos/trogdor/trogdor.s b/demos/trogdor/trogdor.s new file mode 100644 index 00000000..92b72803 --- /dev/null +++ b/demos/trogdor/trogdor.s @@ -0,0 +1,85 @@ +; TROGDOR 2024 + +.include "hardware.inc" +.include "zp.inc" +.include "qload.inc" +.include "music.inc" + + +trogdor_main: + + ;====================================== + ; init + ;====================================== + + lda #$00 + sta DRAW_PAGE + sta clear_all_color+1 + + lda #$04 + sta DRAW_PAGE + jsr clear_all + + ;====================================== + ; draw opening scene + ;====================================== + + lda #trog00_graphics + sta zx_src_h+1 + lda #$20 + jsr zx02_full_decomp + + bit SET_GR + bit HIRES + bit FULLGR + bit PAGE1 + + jsr wait_until_keypress + + ; second + + lda #trog03_graphics + sta zx_src_h+1 + lda #$20 + jsr zx02_full_decomp + + + +finished: + jmp finished + + + + ; start music + + lda SOUND_STATUS + and #SOUND_MOCKINGBOARD + beq no_music + cli ; enable sound +no_music: + + + ;0123456789012345678901234567890123456789 +merry_text: + .byte " MERRY CHRISTMAS!!! MERRY CHRISTMAS!!! ME" + + + + + + + +trog00_graphics: +.incbin "graphics/trog00_trogdor.hgr.zx02" + +trog03_graphics: +.incbin "graphics/trog03_man.hgr.zx02" + +.include "wait_keypress.s" +.include "irq_wait.s" + + diff --git a/demos/trogdor/wait.s b/demos/trogdor/wait.s new file mode 100644 index 00000000..12c4319c --- /dev/null +++ b/demos/trogdor/wait.s @@ -0,0 +1,18 @@ +; copy of ROM wait +; because we might disable ROM + + +wait: + sec +wait2: + pha +wait3: + sbc #$01 + bne wait3 + pla + sbc #$01 + bne wait2 + rts +wait_end: + +.assert (>wait_end - >wait) < 1 , error, "wait crosses page boundary" diff --git a/demos/trogdor/wait_a_bit.s b/demos/trogdor/wait_a_bit.s new file mode 100644 index 00000000..b953ba4a --- /dev/null +++ b/demos/trogdor/wait_a_bit.s @@ -0,0 +1,37 @@ + ;==================================== + ; wait for keypress or a few seconds + ;==================================== + ; A is length to wait + +wait_a_bit: + + bit KEYRESET + tax + +keyloop: + lda #200 ; delay a bit + jsr wait + + lda KEYPRESS + bmi done_keyloop + + dex + bne keyloop +; beq no_escape + +done_keyloop: + +; and #$7f +; cmp #27 +; bne no_escape + +; inc ESC_PRESSED +;no_escape: + + bit KEYRESET + + rts + + + + diff --git a/demos/trogdor/wait_keypress.s b/demos/trogdor/wait_keypress.s new file mode 100644 index 00000000..444d2074 --- /dev/null +++ b/demos/trogdor/wait_keypress.s @@ -0,0 +1,5 @@ +wait_until_keypress: + lda KEYPRESS ; 4 + bpl wait_until_keypress ; 3 + bit KEYRESET ; clear the keyboard buffer + rts ; 6 diff --git a/demos/trogdor/zp.inc b/demos/trogdor/zp.inc new file mode 100644 index 00000000..047ff949 --- /dev/null +++ b/demos/trogdor/zp.inc @@ -0,0 +1,251 @@ +;================== +;================== +; Zero Page Usage +;================== +;================== + +; ZX0 decompression addresses + +ZX0_src = $00 +ZX0_dst = $02 +offset = $04 +bitr = $06 +pntr = $07 +WHICH_LOAD = $09 +CURRENT_DISK = $0A + +; Zero page monitor routines addresses +; We don't use the monitor but we use some of these anyway + +WNDLFT = $20 +WNDWDTH = $21 +WNDTOP = $22 +WNDBTM = $23 +CH = $24 +CV = $25 +GBASL = $26 +GBASH = $27 +BASL = $28 +BASH = $29 +H2 = $2C +V2 = $2D +MASK = $2E +COLOR = $30 +;INVFLG = $32 + +;========================== +; $60-$6F unused currently +;========================== + + +;========================== +; $70-$7F for PT3 Player +;========================== + +AY_REGISTERS = $70 +A_FINE_TONE = $70 +A_COARSE_TONE = $71 +B_FINE_TONE = $72 +B_COARSE_TONE = $73 +C_FINE_TONE = $74 +C_COARSE_TONE = $75 +NOISE = $76 +ENABLE = $77 +PT3_MIXER_VAL = $77 +A_VOLUME = $78 +B_VOLUME = $79 +C_VOLUME = $7A +ENVELOPE_FINE = $7B +ENVELOPE_COARSE = $7C +ENVELOPE_SHAPE = $7D +PATTERN_L = $7E +PATTERN_H = $7F + +;============================ +; $80-$8D rest of pt3_player +;============================ + +PT3_TEMP = $80 +ORNAMENT_L = $81 +ORNAMENT_H = $82 +SAMPLE_L = $83 +SAMPLE_H = $84 +LOOP = $85 +MB_VALUE = $86 +MB_ADDR_L = $87 +MB_ADDR_H = $88 +DONE_PLAYING = $89 +DONE_SONG = $8A +APPLEII_MODEL = $8B +SOUND_STATUS = $8C + SOUND_DISABLED = $80 + SOUND_IN_LC = $01 ; $01 sound effects in language card + SOUND_MOCKINGBOARD = $02 ; mockingboard detected + +;============================= +; not sure why these are here +;============================= + +DISP_PAGE = $8D +DRAW_PAGE = $8E +TOTAL_RAM = $8F + +;============================= +; $90-$CF currently free +;============================= + +;============================= +; $D0-$D9 = hgr move +;============================= +HGR_X1 = $D0 +HGR_X2 = $D1 +HGR_Y1 = $D2 +HGR_Y2 = $D3 +HGR_DEST= $D4 +BOARD_COUNT = $D5 + + +WHICH_SLOT = $DA ; from boot sector + + +;============================================== +; $E0-$EF use for common things, don't re-use +;============================================== +IRQ_COUNTDOWN = $E0 +SECOND_COUNTDOWN= $E1 +COUNT = $E2 +XSAVE = $E3 +TEMPY = $E4 +XPOS = $E5 ; gr_plot +YPOS = $E6 ; gr_plot +COLOR_MASK = $E7 ; gr_plot +FRAMEL = $E8 +FRAMEH = $E9 +BTC_L = $EA ; audio +BTC_H = $EB ; audio +MASKL = $EC ; gr_putsprite_mask +MASKH = $ED +FRAME = $EE + +;============================================== +; $F0-$FB can re-use in each file +;============================================== + +; tunnel +XX = $F2 +MINUSXX = $F3 +YY = $F4 +MINUSYY = $F5 +D = $F6 +R = $F7 +CX = $F8 +CY = $F9 +RR = $FA + +; Credits +BACKUP_OUTL = $F2 +BACKUP_OUTH = $F3 + +; Nuts/ opener + +SPRITE_Y = $F2 +SPRITE_X = $F3 +CURRENT_ROW = $F4 + +; PLASMACUBE +OUT1 = $F0 +OUT1H = $F1 +OUT2 = $F2 +OUT2H = $F3 +COMPT1 = $F4 +COMPT2 = $F5 +PARAM1 = $F6 +PARAM2 = $F7 +PARAM3 = $F8 +PARAM4 = $F9 +GRLINE = $FA +GRLINEH = $FB + +; PLASMA + +; CUBE +SAVEX = $F3 +SAVEY = $F4 +SUM = $F5 + +; CIRCLES/DRAW_BOXES +COLOR2 = $F3 +X1 = $F4 +X2 = $F5 +Y1 = $F6 +Y2 = $F7 +SCENE_COUNT = $F8 +LAST_TYPE = $F9 + +; lens +LENS_X = $F0 +LENS_Y = $F1 +XADD = $F2 +YADD = $F3 + +; rotozoom +NUM1L = $F0 +NUM1H = $F1 +NUM2L = $F2 +NUM2H = $F3 +RESULT = $F4 ; F5,F6,F7 +SCALE_I = $F8 +SCALE_F = $F9 +ANGLE = $FA + +; credits +SCROLL_X = $F0 + +; polar +SCROLL_START = $F0 +YDEST = $F1 + + +; hgr_copy +HGR_COPY_Y1 = $F1 +HGR_COPY_Y2 = $F2 +FIRE_COLOR = $F3 + +SCROLL_LENGTH = $F4 +OFFSET = $F5 +TREE_COUNT = $F6 + + +;============================================== +; $FC-$FF we use for in/out pointers +;============================================== + +INL = $FC +INH = $FD +OUTL = $FE +OUTH = $FF + + +; read any file slot 6 version +; based on FASTLD6 and RTS copyright (c) Peter Ferrie 2011-2013,2018 + +; modified to assembled with ca64 -- vmw +; added code to patch it to run from current disk slot -- vmw + + + adrlo = $26 ; constant from boot prom + adrhi = $27 ; constant from boot prom + tmpsec = $3c ; constant from boot prom + reqsec = $3d ; constant from boot prom + sizelo = $44 + sizehi = $45 + secsize = $46 + + ldsizel = $f0 + ldsizeh = $f1 + namlo = $fb + namhi = $fc + step = $fd ; state for stepper motor + tmptrk = $fe ; temporary copy of current track + phase = $ff ; current phase for /seek + diff --git a/demos/trogdor/zx02_optim.s b/demos/trogdor/zx02_optim.s new file mode 100644 index 00000000..5eebc2e0 --- /dev/null +++ b/demos/trogdor/zx02_optim.s @@ -0,0 +1,159 @@ +; De-compressor for ZX02 files +; ---------------------------- +; +; Decompress ZX02 data (6502 optimized format), optimized for speed and size +; 138 bytes code, 58.0 cycles/byte in test file. +; +; Compress with: +; zx02 input.bin output.zx0 +; +; (c) 2022 DMSC +; Code under MIT license, see LICENSE file. + + +;ZP=$80 + +;offset = ZP+0 +;ZX0_src = ZP+2 +;ZX0_dst = ZP+4 +;bitr = ZP+6 +;pntr = ZP+7 + + ; Initial values for offset, source, destination and bitr +;zx0_ini_block: +; .byte $00, $00, comp_data, out_addr, $80 + +;-------------------------------------------------- +; Decompress ZX0 data (6502 optimized format) + +zx02_full_decomp: +; ; Get initialization block +; ldy #7 +; +;copy_init: lda zx0_ini_block-1, y +; sta offset-1, y +; dey +; bne copy_init + + + sta ZX0_dst+1 ; page to output to in A +zx_src_l: + ldy #$dd + sty ZX0_src +zx_src_h: + ldy #$dd + sty ZX0_src+1 + ldy #$80 + sty bitr + ldy #0 + sty offset + sty offset+1 + sty ZX0_dst ; always on even page + +; Decode literal: Ccopy next N bytes from compressed file +; Elias(length) byte[1] byte[2] ... byte[N] +decode_literal: + jsr get_elias + +cop0: lda (ZX0_src), y + inc ZX0_src + bne plus1 + inc ZX0_src+1 +plus1: sta (ZX0_dst),y + inc ZX0_dst + bne plus2 + inc ZX0_dst+1 +plus2: dex + bne cop0 + + asl bitr + bcs dzx0s_new_offset + +; Copy from last offset (repeat N bytes from last offset) +; Elias(length) + jsr get_elias +dzx0s_copy: + lda ZX0_dst + sbc offset ; C=0 from get_elias + sta pntr + lda ZX0_dst+1 + sbc offset+1 + sta pntr+1 + +cop1: + lda (pntr), y + inc pntr + bne plus3 + inc pntr+1 +plus3: sta (ZX0_dst),y + inc ZX0_dst + bne plus4 + inc ZX0_dst+1 +plus4: dex + bne cop1 + + asl bitr + bcc decode_literal + +; Copy from new offset (repeat N bytes from new offset) +; Elias(MSB(offset)) LSB(offset) Elias(length-1) +dzx0s_new_offset: + ; Read elias code for high part of offset + jsr get_elias + beq exit ; Read a 0, signals the end + ; Decrease and divide by 2 + dex + txa + lsr ; @ + sta offset+1 + + ; Get low part of offset, a literal 7 bits + lda (ZX0_src), y + inc ZX0_src + bne plus5 + inc ZX0_src+1 +plus5: + ; Divide by 2 + ror ; @ + sta offset + + ; And get the copy length. + ; Start elias reading with the bit already in carry: + ldx #1 + jsr elias_skip1 + + inx + bcc dzx0s_copy + +; Read an elias-gamma interlaced code. +; ------------------------------------ +get_elias: + ; Initialize return value to #1 + ldx #1 + bne elias_start + +elias_get: ; Read next data bit to result + asl bitr + rol ; @ + tax + +elias_start: + ; Get one bit + asl bitr + bne elias_skip1 + + ; Read new bit from stream + lda (ZX0_src), y + inc ZX0_src + bne plus6 + inc ZX0_src+1 +plus6: ;sec ; not needed, C=1 guaranteed from last bit + rol ;@ + sta bitr + +elias_skip1: + txa + bcs elias_get + ; Got ending bit, stop reading +exit: + rts