From cf30f0e53650870aef97329c938f59171cf2186c Mon Sep 17 00:00:00 2001 From: Vince Weaver Date: Mon, 4 Dec 2023 22:17:22 -0500 Subject: [PATCH] xmas2023: initial checkin --- demos/xmas_2023/Makefile | 152 ++ demos/xmas_2023/generate_common.c | 77 + demos/xmas_2023/gr_fast_clear.s | 201 ++ demos/xmas_2023/gr_offsets.s | 5 + demos/xmas_2023/graphics/Makefile | 83 + demos/xmas_2023/graphics/tree01.png | Bin 0 -> 7288 bytes demos/xmas_2023/graphics/tree03.png | Bin 0 -> 7469 bytes demos/xmas_2023/graphics/tree05.png | Bin 0 -> 7372 bytes demos/xmas_2023/graphics/tree07.png | Bin 0 -> 7148 bytes demos/xmas_2023/graphics/tree09.png | Bin 0 -> 7076 bytes demos/xmas_2023/graphics/tree11.png | Bin 0 -> 7426 bytes demos/xmas_2023/graphics/tree13.png | Bin 0 -> 7440 bytes demos/xmas_2023/graphics/tree15.png | Bin 0 -> 7285 bytes demos/xmas_2023/gs_interrupt.s | 9 + demos/xmas_2023/hardware.inc | 101 + demos/xmas_2023/hardware_detect.s | 80 + demos/xmas_2023/hgr_table.s | 97 + demos/xmas_2023/interrupt_handler.s | 74 + demos/xmas_2023/irq_wait.s | 94 + demos/xmas_2023/lc_detect.s | 40 + demos/xmas_2023/music.s | 30 + .../music/Walking_In_The_Air_mA2E.pt3 | Bin 0 -> 2406 bytes demos/xmas_2023/pt3_lib_core.s | 2023 +++++++++++++++++ demos/xmas_2023/pt3_lib_detect_model.s | 95 + demos/xmas_2023/pt3_lib_init.s | 575 +++++ demos/xmas_2023/pt3_lib_irq_handler.s | 116 + demos/xmas_2023/pt3_lib_mockingboard.inc | 54 + demos/xmas_2023/pt3_lib_mockingboard_detect.s | 222 ++ demos/xmas_2023/pt3_lib_mockingboard_patch.s | 121 + demos/xmas_2023/pt3_lib_mockingboard_setup.s | 295 +++ demos/xmas_2023/qboot.inc | 8 + demos/xmas_2023/qboot_sector.s | 244 ++ demos/xmas_2023/qboot_stage2.s | 382 ++++ demos/xmas_2023/qload.s | 208 ++ demos/xmas_2023/start.s | 152 ++ demos/xmas_2023/text_print.s | 93 + demos/xmas_2023/wait.s | 18 + demos/xmas_2023/wait_a_bit.s | 37 + demos/xmas_2023/wait_keypress.s | 5 + demos/xmas_2023/xmas.s | 535 +++++ demos/xmas_2023/zp.inc | 282 +++ demos/xmas_2023/zx02_optim.s | 159 ++ 42 files changed, 6667 insertions(+) create mode 100644 demos/xmas_2023/Makefile create mode 100644 demos/xmas_2023/generate_common.c create mode 100644 demos/xmas_2023/gr_fast_clear.s create mode 100644 demos/xmas_2023/gr_offsets.s create mode 100644 demos/xmas_2023/graphics/Makefile create mode 100644 demos/xmas_2023/graphics/tree01.png create mode 100644 demos/xmas_2023/graphics/tree03.png create mode 100644 demos/xmas_2023/graphics/tree05.png create mode 100644 demos/xmas_2023/graphics/tree07.png create mode 100644 demos/xmas_2023/graphics/tree09.png create mode 100644 demos/xmas_2023/graphics/tree11.png create mode 100644 demos/xmas_2023/graphics/tree13.png create mode 100644 demos/xmas_2023/graphics/tree15.png create mode 100644 demos/xmas_2023/gs_interrupt.s create mode 100644 demos/xmas_2023/hardware.inc create mode 100644 demos/xmas_2023/hardware_detect.s create mode 100644 demos/xmas_2023/hgr_table.s create mode 100644 demos/xmas_2023/interrupt_handler.s create mode 100644 demos/xmas_2023/irq_wait.s create mode 100644 demos/xmas_2023/lc_detect.s create mode 100644 demos/xmas_2023/music.s create mode 100644 demos/xmas_2023/music/Walking_In_The_Air_mA2E.pt3 create mode 100644 demos/xmas_2023/pt3_lib_core.s create mode 100644 demos/xmas_2023/pt3_lib_detect_model.s create mode 100644 demos/xmas_2023/pt3_lib_init.s create mode 100644 demos/xmas_2023/pt3_lib_irq_handler.s create mode 100644 demos/xmas_2023/pt3_lib_mockingboard.inc create mode 100644 demos/xmas_2023/pt3_lib_mockingboard_detect.s create mode 100644 demos/xmas_2023/pt3_lib_mockingboard_patch.s create mode 100644 demos/xmas_2023/pt3_lib_mockingboard_setup.s create mode 100644 demos/xmas_2023/qboot.inc create mode 100644 demos/xmas_2023/qboot_sector.s create mode 100644 demos/xmas_2023/qboot_stage2.s create mode 100644 demos/xmas_2023/qload.s create mode 100644 demos/xmas_2023/start.s create mode 100644 demos/xmas_2023/text_print.s create mode 100644 demos/xmas_2023/wait.s create mode 100644 demos/xmas_2023/wait_a_bit.s create mode 100644 demos/xmas_2023/wait_keypress.s create mode 100644 demos/xmas_2023/xmas.s create mode 100644 demos/xmas_2023/zp.inc create mode 100644 demos/xmas_2023/zx02_optim.s diff --git a/demos/xmas_2023/Makefile b/demos/xmas_2023/Makefile new file mode 100644 index 00000000..75663c4e --- /dev/null +++ b/demos/xmas_2023/Makefile @@ -0,0 +1,152 @@ +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: xmas_2023.dsk + +xmas_2023.dsk: QBOOT QLOAD music.inc qload.inc \ + MUSIC \ + XMAS + cp $(EMPTY_DISK) xmas_2023.dsk + $(DOS33_RAW) second_d1.dsk 0 0 QBOOT 0 1 + $(DOS33_RAW) second_d1.dsk 0 2 QBOOT 1 1 + $(DOS33_RAW) second_d1.dsk 0 4 QBOOT 2 1 + $(DOS33_RAW) second_d1.dsk 1 0 QLOAD 0 0 +# $(DOS33_RAW) second_d1.dsk 2 0 MUSIC_INTRO 0 0 +# $(DOS33_RAW) second_d1.dsk 4 0 MUSIC 0 0 + $(DOS33_RAW) second_d1.dsk 12 0 XMAS 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 gr_fast_clear.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 \ + music/Walking_In_The_Air_mA2E.pt3 \ + 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 + +### + +XMAS: xmas.o + ld65 -o XMAS xmas.o -C $(LINKER_SCRIPTS)/apple2_8000.inc + +xmas.o: xmas.s \ + zp.inc hardware.inc qload.inc \ + graphics/tree01.gr.zx02 + ca65 -o xmas.o xmas.s -l xmas.lst + + +### + +graphics/tree01.gr.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 XMAS + rm -f qload.inc music.inc generate_common diff --git a/demos/xmas_2023/generate_common.c b/demos/xmas_2023/generate_common.c new file mode 100644 index 00000000..aa8e095c --- /dev/null +++ b/demos/xmas_2023/generate_common.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include + + + +static FILE *fff; + + +static void find_address(char *symbol_name, int routine_offset) { + + unsigned int addr=0; + char string[BUFSIZ],*result; + char temp_name[BUFSIZ]; + + strncpy(temp_name,symbol_name,BUFSIZ); + strncat(temp_name,":",2); + + while(1) { + + result=fgets(string,BUFSIZ,fff); + if (result==NULL) { + fprintf(stderr,"Error: %s not found!\n",symbol_name); + exit(-1); + } + + result=strstr(string,temp_name); + if (result!=NULL) { + string[6]=0; + sscanf(string,"%x",&addr); + break; + } + } + + + printf("%s\t=$%04x\n",symbol_name,addr+routine_offset); +} + +int main(int argc, char **argv) { + + int c; + char *filename; + char symbol[BUFSIZ]; + int routine_offset=0xd000; + + while ( (c=getopt(argc, argv, "a:s:") ) != -1) { + + switch(c) { + + case 'a': + routine_offset=strtol(optarg, NULL, 0); + break; + case 's': + strncpy(symbol,optarg,BUFSIZ-1); + break; + default: + fprintf(stderr,"Unknown option %c\n",c); + exit(-1); + break; + } + } + + filename=strdup(argv[optind]); + + fff=fopen(filename,"r"); + if (fff==NULL) { + fprintf(stderr,"ERROR! could not open %s\n",filename); + return -1; + } + + find_address(symbol,routine_offset); + + fclose(fff); + + return 0; +} diff --git a/demos/xmas_2023/gr_fast_clear.s b/demos/xmas_2023/gr_fast_clear.s new file mode 100644 index 00000000..8cc8d152 --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/gr_offsets.s b/demos/xmas_2023/gr_offsets.s new file mode 100644 index 00000000..d3af91f7 --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/graphics/Makefile b/demos/xmas_2023/graphics/Makefile new file mode 100644 index 00000000..25a4b62c --- /dev/null +++ b/demos/xmas_2023/graphics/Makefile @@ -0,0 +1,83 @@ +include ../../../Makefile.inc + +ZX02 = ~/research/6502_compression/zx02.git/build/zx02 -f +PNG_TO_HGR = ../../../utils/hgr-utils/png2hgr +PNG2GR = ../../../utils/gr-utils/png2gr + +all: tree01.gr.zx02 tree03.gr.zx02 \ + tree05.gr.zx02 tree07.gr.zx02 \ + tree09.gr.zx02 tree11.gr.zx02 \ + tree13.gr.zx02 tree15.gr.zx02 \ + + +#### + +tree01.gr.zx02: tree01.gr + $(ZX02) tree01.gr tree01.gr.zx02 + +tree01.gr: tree01.png + $(PNG2GR) tree01.png tree01.gr + +#### + +tree03.gr.zx02: tree03.gr + $(ZX02) tree03.gr tree03.gr.zx02 + +tree03.gr: tree03.png + $(PNG2GR) tree03.png tree03.gr + + +#### + +tree05.gr.zx02: tree05.gr + $(ZX02) tree05.gr tree05.gr.zx02 + +tree05.gr: tree05.png + $(PNG2GR) tree05.png tree05.gr + +#### + +tree07.gr.zx02: tree07.gr + $(ZX02) tree07.gr tree07.gr.zx02 + +tree07.gr: tree07.png + $(PNG2GR) tree07.png tree07.gr + + +#### + +tree09.gr.zx02: tree09.gr + $(ZX02) tree09.gr tree09.gr.zx02 + +tree09.gr: tree09.png + $(PNG2GR) tree09.png tree09.gr + +#### + +tree11.gr.zx02: tree11.gr + $(ZX02) tree11.gr tree11.gr.zx02 + +tree11.gr: tree11.png + $(PNG2GR) tree11.png tree11.gr + + +#### + +tree13.gr.zx02: tree13.gr + $(ZX02) tree13.gr tree13.gr.zx02 + +tree13.gr: tree13.png + $(PNG2GR) tree13.png tree13.gr + +#### + +tree15.gr.zx02: tree15.gr + $(ZX02) tree15.gr tree15.gr.zx02 + +tree15.gr: tree15.png + $(PNG2GR) tree15.png tree15.gr + +#### + +clean: + rm -f *~ *.zx02 diff --git a/demos/xmas_2023/graphics/tree01.png b/demos/xmas_2023/graphics/tree01.png new file mode 100644 index 0000000000000000000000000000000000000000..731ac750aed3e08b560a2b9f71b83319d153d325 GIT binary patch literal 7288 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 I(RV)oKX(l3TmS$7 literal 0 HcmV?d00001 diff --git a/demos/xmas_2023/graphics/tree03.png b/demos/xmas_2023/graphics/tree03.png new file mode 100644 index 0000000000000000000000000000000000000000..192524b595ee968e09afaf4b6c675531d1b7ad6a GIT binary patch literal 7469 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 literal 0 HcmV?d00001 diff --git a/demos/xmas_2023/graphics/tree07.png b/demos/xmas_2023/graphics/tree07.png new file mode 100644 index 0000000000000000000000000000000000000000..cc0bfaedfb8da303b2041181ad039885884ef43e GIT binary patch literal 7148 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 literal 0 HcmV?d00001 diff --git a/demos/xmas_2023/graphics/tree13.png b/demos/xmas_2023/graphics/tree13.png new file mode 100644 index 0000000000000000000000000000000000000000..db1284448ee0c3577bd062bf2c85dd6f90cfdb84 GIT binary patch literal 7440 zcmeHMdo)z-_aFC55u!vgh>*Fvja+jll1oHl%$#Au3^RjqS5j{%3Q;aYNK)ipxug`~ zEq6(zkXvDLNu~H5>h0}azwcVV^{(~%{@1K^&U4Ow_WnG3fA)T!b@rOGHrA#B{1W^S z2t>f#%*YlTZNLl3%K_d?_X#|PKsFDB*gMf}aY0Z&Duqb$0ig6?KL83aNJI#PF))CuaBBE&tjdYC-Iom6$FMI9E-|+_mSJY5o{`$elme@FySZL~+FVyw%!vH9a5|D@ z7jizQ3RSbX>vN&4JmT1&pS~Pv$Hy)OheZsmz*bk^geXc=2=LF+bDPTV&#m3b(%sI` zfgP*VQ&C;|*0lOTpm1(!A~k0IT_MA0b>P*eme8DwZj`)b_ICj{`+@U%yKv0s<>48G z`cn|7!vrxX!Z2M(z)X18pVw?s#fis=)--SB9&T5$3bZcD=e?wkz`{p9l5U{3o=AOXd@v z-!ct1UX)*1T|1V(jKvfh?4XGaU!3Up%bNI?)lb^D9LKmF4y(=;U!O{-%g@ zH>->2xAzJaEcXu4vplUV{!n8k@*t>vN3-I}<5iB-ozDM4xfd*|bXY;Gx8Qx{Si4hK z`7I-Xc5+fC%-`EuW!&%6N6M#Jp4p|rYh!ow7N;DBGjv{@dXutXO3sy?s!tlo;*9(n z@$JLCZOZ-HZLehadn|jO9qNiq%hry85B4)6mfR&i9*Yt!4UkK-zYcM*%+Jpnc@msw zB)f;p`BawW({%p4zA|A#amDt^%P*?O<>Yj#$1mO8c}MW_p|giWcJ!FEUUjNeXJMu} z4U19^yLi`~Q%|qxwJfPydSx19do(v*!bCDW9(5*Ot?g#{Q{CnayYY)ETwOk0z!T96 zmHo8R`H}88$-aT$RM((QHFKpCm+C}6dG%g%J!38Adg+$|u39mzWlx&yAKJKPokfKRZV6swIl5oVjxS z6H5d#`Ccs!(|e_kdaPXWSpaM+Vd2V=`Pf4bLTt$z;XUv%N8>tbWDyzHBRzd-?fBc1 zsk7-RE3~1A!)ml0T?HN`@Ws5Q{mUO#FYj%C>#E>{ zt-3)MZ+=%?3^ z?lBRZJR~MyZmR34dBbD!Qk#zN#GB5SM#HgK<~ z?akaHp7QrloZ&VtEds=2tU5Rsj_^j!Fc@)8<<9rv~z00nYFhRjN*OytQbE zgZXVoTzSO zvhSbn&dv(EqOzpfp5|L5!RN+0WV~`g{OhPyUv=kV_0{R77yi5B#@(u#l}7nblS>~% zl>PO2mpAW;qT1;}m2JE+y!vK)T22>{?MpS4B+SaFKEXZ`<%8HNTLrKe9l@p>^k^RDl-JAglU+!2}y`M$g z?m#E!#Vn3(zJ8O(9J-aIEPW(dQ=7w}&o1m!u!m5jbha5TJXg19bOsP_E_MYKCQluU zZL+3ale72~?A-X2i<(;UdKbE`uz{Jjt5LJ0yfLqH?EIsC!{(XfCvb;p7Moh66G5!@ zYoqD#!_kr~z3o?*UN=dQtBujt|{{F2$yUdzP6Dz~*e*Yn4SkgtKD*l_M_vvOrmkRw1*?MWd zEE%o$V-G#WkKe%yM4b>1!nr7&7qmfovA1r^hR(?yJ2)X=7ZsUa>hxHER;!sAz!y)D z6j4M#2~u^E(yz>L7bGFQ$5}vq|A`cl>**}>&WP(HQWfu=E8;6K^v|TTafDt^#tF4BTo&i}t0MGZA#!ku#%^u^Q>PXT#HjtQy#? zx%@Pi?8{C|3LN^(i!m?GL-YDnmR`UDx8j++H1_&}QBmr<7HakBhVC8ZLE{hY_i4NN z$6dzxM@Jcs+rCy2ZhCb5nn8)<;h8ZJ6IxrTmmcwYG%tH_`2NOn{P{C=^*|Mm+U-h=rOrpf@ zbM3k@Nnet~E$_h*m6Wibx$uAbia<)X0OdG4e?cHzfw(C(U z%3CxRquqGIpK(_zF7u<9MXUQs0@JBZD=FzDn>0$?S+F+4d}o$?_w^`dso7w)?8`-LhWVrb4c4}>x>AL=DTU2YA-{G^ilU?=TzTvcuPDEI=3OI?oTM>2b zvjYE+TF@CVp7-*&-6Y<-_2xaX)U?7Nb9pr+L~obc~%m z)SsO_9d+f^NsE5LIr=Mc;x()8oZ7@4i;#Mmiio3tSEvuW5qCqjaeU`T1Cguy=fjh| zXII=EUJ9M#y5{EZ8RxB)z-3={db7$3v}ZwRI?f%n`zg{S^@zvtv_$K@owrY^%c%Ec z)dCuCpuyc=pv+k=(~B8gpZz>+AF;G2W8wkNWC|mc*_5|aVW#E8cGtUtp9w(~V?;CF zz+h;D=$m$!aB{0*0S_fg>)|G{nyQ?s_+d!Y>}c$_ZES8xB^71zmls0JQ~NBEy4f`n zjRz_652kdNaQzBb_k5^-;})iXYS_L-Nxcqs0v>i-ILz4e0%h0ruT*y6%3cuzjS1tM zL(x$jwR?8xO;0KxFim!+1)8jkFmT)USEL#~wX&7w)sJ)9(xl{0@HG%TU?t{0>MU5O zX%qY@pJOeB?Tn(f-iM3c31#>1p`kbK(3nmFwUTqj@Hg1%5e0&l^ z=(6|}Wk}p65hlwqoXNV?egMwKO%^{m-y{=RT=d5BW{KKXe!cA?9R-Bjlz!pz>ETd( z7pXHPEq70}Ma!#D``9;5h1>=irjCx8N8%T2Qg3aUz~~tX`AIaEjGwc0KGi1NbAI$S z`!l}$d_Ep)h~X*aHxgepH;XQc$Z^VD%H8?Lu=>2KZmqnRto#Cn*RPSh1yJF7u{a-s z95!}(P;N0(rmIjb#8T1RIh!DWUMT+1r| z1!t3kgqUC!q-@O1J#)nE&`#bMndIk?#zPXbEp|IUr!rj22Or7aP5*YS=?E)hv)X2T zf$sQKV|kZEbQ=t1+XR4MaSccMab6dC8Qs{HYt=uU%ZtTNJ_coI6@}%9! zgR-Yim*)k~p{`Xy9(!2y+<(19c^ze7yWDt3b5i3(pie*~@A-Nz?&tT<)D#bIldMQC zgp6%NTLqR%N89V3mWNAMK^J)zQf8F7PwQEk0P;;T@#@weooPys?K)S_5uUG=Z4u-2 ze?DQIc&YH%(%iAK=~G)XN}D_uJr6|pM^zvV5?4Ol#Xa1_G~z(yI98wO7&JI+qEvO_ zGLzw1C+*Ked@mxi<8=@G6uBGgt7x}1(`6qt`YFoflb*B39fj9Vp2*cQcX)-1oA9x# z`=#?-&{G(iPvw5e(z_fVNt3zgA+jnUTTGjtZWFD9K-i*5hK4rghK7GNI$(Q~5q?3( ztWkgGV>i2eTV+9Rm661pz_Pel%|2z(oc-6uoCA>3-%ctBn8(KDzIOIw;Y+7qkK8PZ zE`st#MBTZksdUfm?3yJ%%tKpqwz(!Wp(Y6O{D#t%-c(N2A@%a9{tn(-Cn)srHT39k`p3(mg2@Pwm8J>KhM>ZV?h1p9?7b4 zDN%;B3!OvnSz*A{bM-oe~ICM^;jC$pZU_Z(rfqkcw)d4L$#TSkvP&@!Q!`Bb&OCb<#J%%3+?+wtQ9)Kr_ ztRp*Db6*xpBIw9Es#zhe{0spvl3559unV!a$A@_1u>@H?U4Ctb76{-A&~Z?PuMe4~ z#n6%6z|{hu*To1~=!Od2TSwN($_8pkp#o47^nfG1(WGG28>LTTZj0LVFb|dRFWT^ zL?J`hF>xN00J@H>EI1GS%RgT~E32RIWZDlFKt2!*oF4)OMa!6J0gnKD0AEm*2CjZ%~swxr-(^OYggQ=s5SR@vuj#AY?{sLuArqOX^ zJg^Q0g2PE54hl2?Fc>7v0{{(GJ#c6k4ud4ZuxKI@kHP~;JR1EA#6c(+fFj_vOej=e9GFg$FU}J{ z_>ny~Ce{hpGO#h%kwwG5JH8>Y@xjrFpn;C;0TMZY@wciy$rrGrkPH4fFN1Xo10i#NyWL6a?6igRy8CQUM&DLbaz*d~{^j zA)xD)Kc}t0?L@%QaYi^g0D>aX7%dc93yHD^A24cKNKF_Ly#Jz4A&`i{|Cjdq=7DN| zPq`V12Cg5xA^N_f>;V7oZ{J^iNE=%T3fJ<6@K7fZzvu5KI*mf42jQrI zfhWi#$Q4+g8(cvZHxi}vPhWz(fb}eZguzf~*x!U0(Z&CFI|Y!zub?2XSz2q57yuhBjLiXiV{jbZw`TMWlIxz4ks;dt zt)p?xoyxIxTst!}b1l0=bV<8o=p1#eV{L5>)Xz*f9R&`2er88#5C~ju{bFO}>ji>B z9=f@eG0((ic1}4yFQG<0Py~Lr8`zsQ&-9@98=yi);GYb8mhTtGB??(pbt;+kyE$to z3R||6#rM4>MYEC(7XP$2(7s@X=B+hgH|HwSNT4#6PK+u#XRo%(naZ?4Sv=%KI%{9+ z$6T3c;zCNLK2!qm--iAp8@MQFk|)CGDoK+%#-V>sOv%ZYy9QhzVs310RJ`B)CV<=0;tOgTV##(klmhvz&jAce+FhZorzP~N@U1Ukt$dbL3 zycBOFvL+Epi;~|%y}iBX_dVx#-gAE6|C)1Vp8L7(&-J;l>vLcCeddZVG1BGY5axhD zAYA%-T4vzW1pFd*vVy-AN4Pp55Ki5*7S;?iz!&OCr;#b{Bq+nrlLRF(DP#zQIXrsR zmM(dlOMBxs2jV;}zDIl)vh~Q)+ran}{R}tki@7;9EB$LHKS4F@#wTDG)@R2QzeE`q zp*20wYp+$`7FG7%Q4(BUJ^2XNvF4s>?02Z^tbmq#em~+`=VDbjz>m9kZ ziNK9!tFFMo2eOt6b@k2rS1a0Q!$0E)pB_s%*u=F4IqmdoN_qn==FZM~Q2QwZYrdl_ z)wRE>-|+owWBgU?%1}9xses^a<5-yA%%tGuS7VIIiUJzyXUg}4?~JvL8HJ9tn;8R7z*l~y6yB4H-(-S&hY9#D!olIV(%r8p?@4hu;41?T8gWA)y&vYRW_9B>cP~iKghO? z?}lyP*w-gbB*GN*lH_58f)Z05QIB`xPSMJP4=UADJl@NAJy0HU?Ct1|D%`JFQoSPI zu(;1>>{eHXzi8|1;{hWzdgnD~%3#jYB_9D%y!T18=DlKt`9nDO;rffi@E3z4)Mk2D zx^H04`><8xvD51CM$;Z{vvautIh`jH;=~gN^t~QH_sP>JCIf5{&@%s1B z#mCm&N6SX6{p1inLBp4t>z8hP-WWMw6IxAsr~Wc=F*wd^f&cyJaYmbD;BZaNB9%@R ze?lz@ROs>GFIE&wvcL4ZFj=+zc?ExrWPG~R`1{8Ov&9}yo&q9W>m`3l?Mf%_^>@!HmvNn zo!gNp+wz9d6jF)(?45;Y*Nbu0nW>gf`s4U=FonZOH2`s`$`t#?6Q8XD7kE21{piVU zLto3V`oNc?p=_&YPoX{cOv^YQ;X6z(_nM5*6?VY=MAOsj)EcU%=LOC21?m31_hOGf zx7m%_5HY{h^mH?MH~%9VMgjX~PS{)TzW$+Rw$8^>H1!!oGjF8wra#-!h{t>T^jO+e z@VC4L`>j`w>TUz>nm!3XAQ+oaO$%kG=J&aYS9r^PbeP+Bm@XwuL!IYQMeUS4W0PF!E$}_pvh6 zTzI?laSsZ~ZP#t$YH!CYS_-_)bNd27A>)OURS)H`E!L>0f6+DeFd)iHzIq$pXz!HCugT(1bn|dcadS<0-gVHjzw>qe^j^h+mWS3%{q_-$py6jcFI4v_RfHYN z=Y815s#IF`!Q{b*GrGarG5$kzY?-Qr>tS=Q64_k=@UDvcJgwKBR0pRhC0Y&;IOrYu zYWb39BOF;=xn%C)AF(hkUmr-x3H-gQ{K^UMZXj&#RGv0W($n&-Se1Hn6ZhEZ`06#e zj6mi0F<)B`TwDy)GQGg3S6xHosbcqX8aWR=e06{?4!rk{tJp*M=`5v2%L?)=+!22+ z$L;68-)l`Tt*N}{<0A9%>8KGV>(HKSJ(na{-cFjqT3Nj^2N@RvQN>kiw`4j0aDck1 z1-pdP>kv_8_B(}I=F@r395r>vB$nJPBXM5l9U_ig=Q2RKzw*|#3R!^ZI3pSu7w(oN z(5x0TEhhQ${Nn}j)7Xw!SRT*8r=PA5U*F9~YqEL$N<*+V4TEc^60TUS3(PNwylTRT zYgau^(Aq~mD1oeea;>UB+Z}&W=+EuiT2oXeH+QX}^ynRV&hD}2NJH7aZGb#e|C!(P zzChEm=6V>@-HO7**I`RBdr8L`btki0}gyLG--{@A|H1@E$DVYgsrh9 zyf@xh@U}>AU6TJ`cD%}o5liXlwUK8Ve7tIzOKESjl59sV>XH)Jgd#O=PsA)P-{kdE zo;KeVsy}&V=2oA~ln}S;VzNrou7Opf5gk;hH-L7%R3P=hq$uK;=nI~U4t%g%XxBoR zwcta6ZAxujlDq~N5(5kfW+4ex7oB6|vW_%fe0u0pTz$&|w^d!uLxeCz+Ote^NH6+n ze63{0T|3R?%z9w&&cW{CMk(29;ofyTWQX#uJDd#ih z)m^&Ru7)KPTV}k}E)=hKkZ{>U;)zq~&Fchg1EmJJKQFDeZwwQ{i6OfSIiEi0GQ&m> zDNHPCDw~L{rN|cj7L6K%I$PY~Kf2SeJZfT|EnjEP^Xg@^OsKhQPCrmL91JSZ(KV4b>TtYhE zIHmBsUO~caZD|z;e4CbhQEQM2;c4!dZVqWOu_F+6ixjfNfl3o5rc_uNCF^_Vu! zqwr-t6O)1Kmn7kiVf~!DjhM+OW1T~X-q_vju>;D_&RtGsW)U3EEA5yYvX8I~I=x%b z=W=TIoJRER_ME%dVgz*|7BTPJ`PlGf=Q=&RgiH5-92}n(#h+v)4;BfFCpR@#=JNE9 zc(Y<%jox*N*W&tw755qIJNq%X#ZBh9$|_#&4wpkuQBxA5|1@qv8!?JengOrVq#WfB z1Vw4&vUtab9Q!Tu6c)jI-i%FT#v*zulG5B7pEA8G)}Xu8Rcbw3sqB-#07E}NMy_o7 zr75PKDDL){aH25}f|bzGIuRG1t<0mC@89-LFI)>bfc}$Zf2cOQvYyi}HN|9|_}aRH z&M%f9#F_#{gMa_XO+veLQS(pc9_NQ~C&ttpt>loZ_xL^MoUGd0KGHB?pVDX9H&xmD z%;a#$BYhGq)F(~=$|apbJSQWmaMP!?;SBQS$Ra0kJ@=(~Hvj67uHsR9FaNo7>hBv# zWQnQ$YA+DzSeB$aod#hx$0n`rL=kk=;sTEhjit&4^oClKH1^Xqe>1eHsZO{c?cy3g zuPE=GC=_$opxn+sWvBJ5&`S%$ax3=b`aM~d#a}-gEXy5QHI)utT8Zp;Qw@@nV#f<} z$UV0^7lG!`=!dP1aOq4)DHO77w?ANbHvjVlfm7uI)Q4XSgrzUX1goDHEgF!dc&AA( zpFJfPgI^zk+kT8W^+t^AqL_Es2FpZ)kWTpoy_)w z9q1V?6zkZscO1m_4WC@k#d0#pr0*5 z?3%p)m(IG^=q|J$=1RKMOhhQ1yGOOe!XSp3qEz6DpZ4;LJJD$yU2Q{{dzRFQG<$4y z`I?azCq>S27i@dCOIDI|MB?*xtY@ z%pKG=Q{RZ142WjUw$B=6q`h(KqGUquI*%WFvo;TRc%Sr+Y!xNCjWE*E;cb=lA-G2r zx<=G1m^!ABVpp{NPUatq0>LkAp?79h2cLG$vBu|?p%q*1it`}M-`78CU_a~s`GY}W zvsisOy)9KtA_?sLm=v(Xvo*l0PgsC0nP+{Uh0uK@cfHFPYsa|-d zDr}1v4~{p@2pDt=!f;cCSsR)_2{bwhij~L8BjMUiiVqrglmn_lCzA1IS~}k;z&BNx z3xnZ_M<9HCedT>I@-(_L0;R01j6k9hXfzx|z`gva41fuzdP!_jeB;m}c{$N3o(u|& z3f<%c9BJMRRTvDMhyE3xho_<84|=NCcNIWA5KO=mfs#ieJUkFTdw4OleL#}$0sTi0 zFAK0yMwpSjXx?-ulC}?t%8>Y(g6QQSj6Aq3gQx-IbpCEB7nq^$;h9m^r>D9fa*lr zqyow1DIkvmS&4`uVHDv25~BdeDk^~#BxNNy76YJA%1%Tgl1TiC!kA70s}gYkIjT)6 zB1lDcB$0?p02Yox;!tp`k|O|jR6^n4C=`HJA_F*-qY|)1MRdaJ(C8ijxSbRaz?p>b zq&ja+YzmIoFws|qq2<2?zGX3S2N-0~K^0~|p?Wj_hFVZONahS+Q%#g25`|SpDWcJe zI201C@HfbkME3$Kag!5;l*iz-C<}0aQNW{7aHIkr`4gT-q>%moFZAZ&fvSAl zay^O{xW3<(>D!4iC!P8B_U+Z3vUQZ8(5*v(2b{j8;05@QzHJuB^=-<@1)w^Uz}w?{ zx&CFR{D)G&g4LqrsDObhkO3qdi$h`H0HCZ0mXv}5hD-*?NbEQ9ez1Gd$P8bAPSS7& zbp*8n%X3RBsPxuG$!ux%1MTZV+S~`|AKtMW=WKehl7n4#>Buv8+=Y4aBwhc+|XKwRY^}z-^l9QyQN9KlFV?LRm~kA7&jmyk_9w|AJcEdI#r z@#Oftz1j)34IMj32^yoO8Unl;Cn{PV7GI$^-fW?d&+g{v9;5f@D<`RY+d@b0u6WwG z&oQ3eecmD&-$dd{l}y)Y#A(!DIIY4JeQtDHs?Pqo1+mf1%pm&OMp|W>4k7;ows+Pz literal 0 HcmV?d00001 diff --git a/demos/xmas_2023/gs_interrupt.s b/demos/xmas_2023/gs_interrupt.s new file mode 100644 index 00000000..cec38027 --- /dev/null +++ b/demos/xmas_2023/gs_interrupt.s @@ -0,0 +1,9 @@ + +gs_interrupt_handler: + ; swap back in language card + + ; read/write RAM, use $d000 bank1 + bit $C083 + bit $C083 + + jmp interrupt_handler diff --git a/demos/xmas_2023/hardware.inc b/demos/xmas_2023/hardware.inc new file mode 100644 index 00000000..1cf29d20 --- /dev/null +++ b/demos/xmas_2023/hardware.inc @@ -0,0 +1,101 @@ +; HARDWARE LOCATIONS + +KEYPRESS = $C000 +KEYRESET = $C010 + +; SOFT SWITCHES +CLR80COL = $C000 ; PAGE1/PAGE1 normal +SET80COL = $C001 ; PAGE1/PAGE2 switches PAGE1 in Aux instead +EIGHTYCOLOFF = $C00C +EIGHTYCOLON = $C00D +TBCOLOR = $C022 ; IIgs text fg/bg colors +NEWVIDEO = $C029 ; IIgs graphics modes +SPEAKER = $C030 +CLOCKCTL = $C034 ; bits 0-3 are IIgs border color +CYAREG = $C036 ; iigs motor detect and clock speed +SET_GR = $C050 +SET_TEXT = $C051 +FULLGR = $C052 +TEXTGR = $C053 +PAGE1 = $C054 +PAGE2 = $C055 +LORES = $C056 ; Enable LORES graphics +HIRES = $C057 ; Enable HIRES graphics +AN3 = $C05E ; Annunciator 3 + +PADDLE_BUTTON0 = $C061 +PADDL0 = $C064 +PTRIG = $C070 + +; APPLESOFT BASIC ROUTINES + +NORMAL = $F273 +HGR2 = $F3D8 +HGR = $F3E2 +BKGND0 = $F3F4 ; clear current page to A +HPOSN = $F411 ; (Y,X),(A) (values stores in HGRX,XH,Y) +HPLOT0 = $F457 ; plot at (Y,X), (A) +COLOR_SHIFT = $F47E +HLINRL = $F530 ; (X,A),(Y) +HGLIN = $F53A ; line to (X,A),(Y) +COLORTBL = $F6F6 + +; MONITOR ROUTINES + +HLINE = $F819 ; HLINE Y,$2C at A +VLINE = $F828 ; VLINE A,$2D at Y +CLRSCR = $F832 ; Clear low-res screen +CLRTOP = $F836 ; clear only top of low-res screen +SETCOL = $F864 ; COLOR=A +ROM_TEXT2COPY = $F962 ; iigs +TEXT = $FB36 +TABV = $FB5B ; VTAB to A +ROM_MACHINEID = $FBB3 ; iigs +BASCALC = $FBC1 ; +BELL = $FBDD ; ring the bell +VTAB = $FC22 ; VTAB to CV +HOME = $FC58 ; Clear the text screen +WAIT = $FCA8 ; delay 1/2(26+27A+5A^2) us +SETINV = $FE80 ; INVERSE +SETNORM = $FE84 ; NORMAL +COUT = $FDED ; output A to screen +COUT1 = $FDF0 ; output A to screen + + + + + + + +COLOR_BLACK = 0 +COLOR_RED = 1 +COLOR_DARKBLUE = 2 +COLOR_PURPLE = 3 +COLOR_DARKGREEN = 4 +COLOR_GREY = 5 +COLOR_MEDIUMBLUE = 6 +COLOR_LIGHTBLUE = 7 +COLOR_BROWN = 8 +COLOR_ORANGE = 9 +COLOR_GREY2 = 10 +COLOR_PINK = 11 +COLOR_LIGHTGREEN = 12 +COLOR_YELLOW = 13 +COLOR_AQUA = 14 +COLOR_WHITE = 15 + +COLOR_BOTH_BLACK = $00 +COLOR_BOTH_RED = $11 +COLOR_BOTH_DARKBLUE = $22 +COLOR_BOTH_DARKGREEN = $44 +COLOR_BOTH_GREY = $55 +COLOR_BOTH_MEDIUMBLUE = $66 +COLOR_BOTH_LIGHTBLUE = $77 +COLOR_BOTH_BROWN = $88 +COLOR_BOTH_ORANGE = $99 +COLOR_BOTH_PINK = $BB +COLOR_BOTH_LIGHTGREEN = $CC +COLOR_BOTH_YELLOW = $DD +COLOR_BOTH_AQUA = $EE +COLOR_BOTH_WHITE = $FF + diff --git a/demos/xmas_2023/hardware_detect.s b/demos/xmas_2023/hardware_detect.s new file mode 100644 index 00000000..296878e3 --- /dev/null +++ b/demos/xmas_2023/hardware_detect.s @@ -0,0 +1,80 @@ +;==================== +; Hardware Detect +; called for disk1 and disk2 + +; simplified version that just detects model and mockingboard +; for the fake BIOS we do a bit more, but we do rely +; on this being run first + +hardware_detect: + + ;======================= + ; Hardware Detect Model + ;======================= + ; Yes Michaelangel007 I will eventually update linux_logo 6502 + + jsr detect_appleii_model + + lda APPLEII_MODEL + cmp #'g' + bne not_iigs + +is_a_iigs: + + ; enable 1MHz mode + ; see hw.accel.a in 4cade +setspeed: + lda CYAREG + and #$7f + sta CYAREG + + ; gr/text page2 handling broken on early IIgs models + ; this enables the workaround + + jsr ROM_TEXT2COPY ; set alternate display mode on IIgs + + + ; set background color to black instead of blue + lda NEWVIDEO + and #%00011111 ; bit 7 = 0 -> 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/xmas_2023/hgr_table.s b/demos/xmas_2023/hgr_table.s new file mode 100644 index 00000000..2a6d5a99 --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/interrupt_handler.s b/demos/xmas_2023/interrupt_handler.s new file mode 100644 index 00000000..f2d73df3 --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/irq_wait.s b/demos/xmas_2023/irq_wait.s new file mode 100644 index 00000000..96264c9f --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/lc_detect.s b/demos/xmas_2023/lc_detect.s new file mode 100644 index 00000000..3123a6ae --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/music.s b/demos/xmas_2023/music.s new file mode 100644 index 00000000..915da3e9 --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/music/Walking_In_The_Air_mA2E.pt3 b/demos/xmas_2023/music/Walking_In_The_Air_mA2E.pt3 new file mode 100644 index 0000000000000000000000000000000000000000..fd0030738b2550ba786226dc99bc30fdda1d4453 GIT binary patch literal 2406 zcmb_d-EZ4e6t^A6xkW=r8=wj#WF*koBnH}~@jM}r5Ij`sHfvSnHZyJ#H>t4~(pZtx z)JbHVbtz~P&wJos+n40= z=dL3cZ(RLIeoy{<-kz)-6+-GY?stykenA}fD~#v+XftQnC8U{iFP(p77JgoT0Wm5N3-PB*H^XM|Vi@rhM zqtOrxs%{jEhORQnVJR97uwYr;=t=ZA+JX7hl*2+a>N4fA9Jt)*I1tmJ7Cp}!#E8*0 z7N;?LF5T@##3MHfvEX_2I!10Eob5+@(QZWccA^jzVMB7U;1HK-EmtVyv^0}k4>qt^ zg?LSK5>bC9sOBEiY@TvXEIejOEM%%Y;edrdq2F3}fu0 zS9Y91vB>JhgUjS}emi(-X&dTEoYZQr3!&~qBq7*WDj1bd3u^}ur3mWca_|GnBhYqO zb>hN?6h?9gCMcrHQM^H11b``*OC>CpiSizVKxW7uV%{1|%F>(gU>lzsXY;(FqX;;V z$rUZDn9Hytvf{F0lg#S1dJT&%tx45|FCHfoIu)lfi8U;h;R~TsqEID~W*mYJXh}4q zkSd8bg)(i+wlFfmh2#;=Tae=cuxf}!s|)!L#|wQ*KFJ_UpxPQs6abw-eIMutdE8mj z^Lc&g4$DI`%zO7tL(7?^N~L7xNFMGwmNrQ7_rXc%H32K0`2iO16GYhY`@~O^%=(n( zU2<;1h6jtWLCeQd5YN`@So9|5>(rdE{h2v!ij@^;id*T7u4mG>NTHXqhOHN@FR@s$ zXraMFBgTw9gaHJFQ4J7X_-9dk_ZdDE0K#juu;j3ayEz+XY!HF&ElkRD^1E`};8)k6G`>hs64a`5>YWod%Y7M`=^$-i+HX5FX zQ62MJn|`BST`QTTwQ9WqCg4(0hgx=!)ecmpuc-T4b}y^#s!C5$yIS^1R(q@}JBqrk z{fu8AZbWZ*;yZ*~)ZgSo0a(v$DKMJz~28dc)$^05J@l(5BUdw!#F(T Gc + +; 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/xmas_2023/pt3_lib_detect_model.s b/demos/xmas_2023/pt3_lib_detect_model.s new file mode 100644 index 00000000..ac30fd4e --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/pt3_lib_init.s b/demos/xmas_2023/pt3_lib_init.s new file mode 100644 index 00000000..037b1ade --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/pt3_lib_irq_handler.s b/demos/xmas_2023/pt3_lib_irq_handler.s new file mode 100644 index 00000000..851a22bf --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/pt3_lib_mockingboard.inc b/demos/xmas_2023/pt3_lib_mockingboard.inc new file mode 100644 index 00000000..80a8fdd1 --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/pt3_lib_mockingboard_detect.s b/demos/xmas_2023/pt3_lib_mockingboard_detect.s new file mode 100644 index 00000000..544eb429 --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/pt3_lib_mockingboard_patch.s b/demos/xmas_2023/pt3_lib_mockingboard_patch.s new file mode 100644 index 00000000..728396c9 --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/qboot.inc b/demos/xmas_2023/qboot.inc new file mode 100644 index 00000000..65fbf7dd --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/qboot_sector.s b/demos/xmas_2023/qboot_sector.s new file mode 100644 index 00000000..2b3901b1 --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/qboot_stage2.s b/demos/xmas_2023/qboot_stage2.s new file mode 100644 index 00000000..6a38148f --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/qload.s b/demos/xmas_2023/qload.s new file mode 100644 index 00000000..5213ced7 --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/start.s b/demos/xmas_2023/start.s new file mode 100644 index 00000000..9df7c122 --- /dev/null +++ b/demos/xmas_2023/start.s @@ -0,0 +1,152 @@ +; XMAS 2023 + +; +; by deater (Vince Weaver) + + + +xmas_start: + jmp 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: + + + ;======================= + ;======================= + ; Load intro + ;======================= + ;======================= + + ; 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" + +disk_change_string: +; 0123456789012345678901234567890123456789 +;.byte 5,22,"INSERT DISK 2 AND PRESS ANY KEY",0 + +.include "pt3_lib_mockingboard_patch.s" + +.include "hardware_detect.s" diff --git a/demos/xmas_2023/text_print.s b/demos/xmas_2023/text_print.s new file mode 100644 index 00000000..34bae3db --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/wait.s b/demos/xmas_2023/wait.s new file mode 100644 index 00000000..12c4319c --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/wait_a_bit.s b/demos/xmas_2023/wait_a_bit.s new file mode 100644 index 00000000..b953ba4a --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/wait_keypress.s b/demos/xmas_2023/wait_keypress.s new file mode 100644 index 00000000..444d2074 --- /dev/null +++ b/demos/xmas_2023/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/xmas_2023/xmas.s b/demos/xmas_2023/xmas.s new file mode 100644 index 00000000..60c8bdfa --- /dev/null +++ b/demos/xmas_2023/xmas.s @@ -0,0 +1,535 @@ +; XMAS + +; PLASMAGORIA + +; based on original code by French Touch + +.include "hardware.inc" +.include "zp.inc" +.include "qload.inc" +.include "music.inc" + +; ============================================================================= +; ROUTINE MAIN +; ============================================================================= + +plasma_main: + + lda #$00 + sta DRAW_PAGE + sta clear_all_color+1 + + lda #$04 + sta DRAW_PAGE + jsr clear_all + + + bit PAGE2 ; set page 2 +; bit SET_TEXT ; set text + bit LORES ; set lo-res + + lda #0 + sta FRAME + + ; load image offscreen $6000 + + lda #mask1_data + sta zx_src_h+1 + lda #$60 + jsr zx02_full_decomp + + ; load image offscreen $6400 + + lda #mask2_data + sta zx_src_h+1 + lda #$64 + jsr zx02_full_decomp + + ; load image offscreen $6800 + + lda #mask3_data + sta zx_src_h+1 + lda #$68 + jsr zx02_full_decomp + + ; load image offscreen $6C00 + + lda #mask4_data + sta zx_src_h+1 + lda #$6C + jsr zx02_full_decomp + + ; load image offscreen $7000 + + lda #mask5_data + sta zx_src_h+1 + lda #$70 + jsr zx02_full_decomp + + ; load image offscreen $7400 + + lda #mask6_data + sta zx_src_h+1 + lda #$74 + jsr zx02_full_decomp + + ; load image offscreen $7800 + + lda #mask7_data + sta zx_src_h+1 + lda #$78 + jsr zx02_full_decomp + + ; load image offscreen $7C00 + + lda #mask8_data + sta zx_src_h+1 + lda #$7C + jsr zx02_full_decomp + + + + + ; remap the masks + ; $00->$00 + ; $11->$40 + ; $22->$80 + ; $44->$C0 + + + ldy #0 + sty OUTL + lda #$60 + sta OUTH +remap_mask: + lda (OUTL),Y + and #$7 + tax + lda remap_table,X + sta (OUTL),Y + dey + bne remap_mask + + inc OUTH + lda OUTH + cmp #$80 + bne remap_mask + + +step3: + + ; init + + lda #02 + sta COMPT2 + sta PARAM1 + sta PARAM2 + sta PARAM3 + sta PARAM4 + +bp3: + jsr precalc ; pre-calc + jsr display_normal ; display normal + jsr VBLANK + + lda #60 + jsr wait_for_pattern + bcc keep_making_plasma + + jmp done_plasmacube + + +keep_making_plasma: + + + inc COMPT1 + bne bp3 + dec COMPT2 + bne bp3 + + jmp step3 + + +; ============================================================================ +; Precalculate some values +; ROUTINES PRE CALCUL +; ============================================================================ +precalc: + lda PARAM1 ; self modify various parts + sta pc_off1+1 + lda PARAM2 + sta pc_off2+1 + lda PARAM3 + sta pc_off3+1 + lda PARAM4 + sta pc_off4+1 + + ; Table1(X) = sin1(PARAM1+X)+sin2(PARAM1+X) + ; Table2(X) = sin3(PARAM3+X)+sin1(PARAM4+X) + + ldx #$28 ; 40 +pc_b1: +pc_off1: + lda sin1 +pc_off2: + adc sin2 + sta Table1,X +pc_off3: + lda sin3 +pc_off4: + adc sin1 + sta Table2,X + + inc pc_off1+1 + inc pc_off2+1 + inc pc_off3+1 + inc pc_off4+1 + + dex + bpl pc_b1 + + inc PARAM1 + inc PARAM1 + dec PARAM2 + inc PARAM3 + dec PARAM4 + + rts + +; ============================================================================ +; Display Routines +; ROUTINES AFFICHAGES +; ============================================================================ + +; Display "Normal" +; AFFICHAGE "NORMAL" + +display_normal: + bit SET_GR ; gfx (lores) why needed? + + ldx #23 ; lines 0-23 lignes 0-23 + +display_line_loop: + lda gr_lookup_low,X ; setup pointers for line + sta GRLINE + lda gr_lookup_high,X + sta GRLINE+1 + + lda gr_lookup_low,X ; setup pointers for mask + sta INL + lda gr_lookup_high,X + clc +mask_src_smc: + adc #($70-$8) + sta INH + + + ldy #39 ; col 0-39 + + lda Table2,X ; setup base sine value for row + sta display_row_sin_smc+1 +display_col_loop: + lda Table1,Y ; load in column sine value +display_row_sin_smc: + adc #00 ; add in row value + + and #$3f + ora (INL),Y + + sta display_lookup_smc+1 ; patch in low byte of lookup +display_lookup_smc: + lda lores_colors_rgb ; attention: must be aligned + + sta (GRLINE),Y + dey + bpl display_col_loop + dex + bpl display_line_loop + + rts + + +VBLANK: + inc FRAME + + lda FRAME + lsr + lsr + lsr + lsr + and #$f + tax + lda mask_src_table,X + sta mask_src_smc+1 + + rts + +done_plasmacube: + + rts + +;.align 256 + +gr_lookup_low: + .byte $00,$80,$00,$80,$00,$80,$00,$80 + .byte $28,$A8,$28,$A8,$28,$A8,$28,$A8 + .byte $50,$D0,$50,$D0,$50,$D0,$50,$D0 + +gr_lookup_high: + .byte $08,$08,$09,$09,$0A,$0A,$0B,$0B + .byte $08,$08,$09,$09,$0A,$0A,$0B,$0B + .byte $08,$08,$09,$09,$0A,$0A,$0B,$0B + + + +.align 256 + + +; This appears to be roughly 47+32*sin(x)+16*sin(2x) +sin1: ; 256 +.byte $2E,$30,$32,$34,$35,$36,$38,$3A,$3C,$3C,$3E,$40,$41,$42,$44,$45,$47,$47,$49,$4A,$4B,$4C,$4D,$4E,$4F,$50,$51,$52,$53,$53,$54,$54 +.byte $55,$55,$56,$57,$57,$58,$58,$57,$58,$58,$58,$58,$58,$58,$58,$58,$58,$57,$57,$57,$56,$56,$55,$54,$55,$54,$53,$52,$52,$51,$50,$4F +.byte $4E,$4E,$4D,$4C,$4B,$4B,$4A,$49,$48,$47,$46,$45,$45,$44,$42,$42,$41,$41,$3F,$3F,$3D,$3D,$3C,$3B,$3B,$39,$39,$39,$38,$38,$37,$36 +.byte $36,$35,$35,$34,$34,$33,$32,$32,$32,$31,$31,$31,$30,$31,$30,$30,$30,$30,$2F,$2F,$30,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2E,$2F,$2F,$2F +.byte $2E,$2F,$2F,$2F,$2F,$2E,$2F,$2F,$2F,$2E,$2F,$2F,$2E,$2E,$2F,$2E,$2E,$2D,$2E,$2D,$2D,$2D,$2C,$2C,$2C,$2B,$2B,$2B,$2A,$2A,$29,$28 +.byte $28,$27,$27,$26,$26,$25,$25,$23,$23,$22,$21,$21,$20,$1F,$1F,$1D,$1D,$1C,$1B,$1A,$19,$19,$17,$16,$16,$15,$14,$13,$13,$12,$11,$10 +.byte $0F,$0F,$0E,$0D,$0C,$0C,$0B,$0A,$09,$09,$08,$08,$08,$07,$06,$07,$06,$06,$06,$06,$05,$06,$05,$05,$06,$05,$06,$06,$07,$07,$08,$08 +.byte $09,$09,$0A,$0B,$0B,$0C,$0C,$0D,$0F,$0F,$10,$12,$12,$14,$15,$16,$17,$19,$1A,$1B,$1D,$1E,$20,$21,$22,$24,$26,$27,$28,$2A,$2C,$2E + +; This appears to be roughly 47+32*sin(4x)+16*sin(3x) +sin2: ; 256 +.byte $2E,$33,$38,$3C,$40,$43,$47,$4B,$4E,$51,$54,$56,$59,$5A,$5C,$5D,$5D,$5E,$5E,$5D,$5C,$5A,$59,$57,$55,$53,$4F,$4C,$49,$46,$42,$3E +.byte $3A,$36,$32,$2E,$2A,$26,$23,$1F,$1C,$18,$15,$12,$10,$0E,$0C,$0A,$09,$08,$07,$07,$07,$07,$09,$0A,$0B,$0D,$0F,$11,$13,$16,$19,$1C +.byte $1F,$22,$26,$29,$2C,$2F,$32,$36,$38,$3B,$3E,$3F,$42,$44,$46,$47,$48,$49,$4B,$4B,$4B,$4A,$4A,$49,$49,$48,$46,$44,$43,$41,$3F,$3C +.byte $3A,$38,$35,$33,$30,$2E,$2C,$2A,$28,$26,$24,$22,$21,$20,$1F,$1F,$1E,$1E,$1D,$1D,$1E,$1E,$1F,$20,$21,$22,$24,$25,$27,$29,$2B,$2D +.byte $2E,$30,$33,$35,$37,$38,$3A,$3C,$3D,$3E,$3F,$3F,$40,$40,$41,$40,$40,$3F,$3F,$3E,$3D,$3B,$3A,$38,$36,$34,$31,$2F,$2D,$2B,$29,$25 +.byte $23,$21,$1F,$1D,$1B,$19,$18,$16,$15,$14,$14,$13,$13,$13,$13,$14,$16,$17,$18,$1A,$1C,$1D,$20,$23,$26,$28,$2C,$2E,$32,$35,$38,$3B +.byte $3E,$41,$45,$48,$4B,$4C,$4F,$51,$53,$54,$55,$55,$57,$57,$57,$56,$55,$53,$52,$50,$4E,$4B,$49,$45,$42,$3F,$3B,$37,$34,$30,$2C,$27 +.byte $23,$1F,$1C,$18,$14,$11,$0E,$0B,$09,$07,$05,$03,$02,$01,$00,$00,$01,$01,$02,$03,$05,$07,$0A,$0D,$10,$13,$17,$1A,$1E,$22,$26,$2A + +; This appears to be roughly 38+24*sin(3x)+16*sin(8x) +sin3: ; 256 +.byte $26,$2C,$31,$35,$39,$3D,$40,$42,$44,$45,$45,$46,$45,$43,$42,$40,$3C,$3A,$38,$36,$33,$31,$30,$2F,$2F,$2E,$2F,$2F,$30,$33,$33,$36 +.byte $37,$3A,$3C,$3C,$3E,$3E,$3D,$3D,$3B,$39,$36,$34,$30,$2B,$28,$23,$1D,$19,$14,$11,$0C,$09,$07,$04,$03,$03,$03,$03,$04,$07,$09,$0C +.byte $0F,$13,$16,$18,$1B,$1E,$20,$22,$22,$23,$24,$24,$23,$22,$21,$20,$1D,$1C,$1B,$1A,$19,$19,$19,$1A,$1C,$1E,$20,$23,$27,$2B,$2F,$33 +.byte $37,$3D,$40,$44,$47,$4A,$4C,$4D,$4E,$4E,$4D,$4C,$4A,$47,$45,$41,$3C,$39,$35,$32,$2E,$2B,$28,$26,$25,$23,$23,$22,$22,$24,$24,$25 +.byte $26,$29,$2A,$2A,$2B,$2C,$2B,$2B,$29,$28,$25,$23,$20,$1C,$19,$15,$10,$0D,$09,$07,$04,$02,$01,$00,$00,$00,$02,$03,$06,$0A,$0D,$11 +.byte $15,$1B,$1F,$23,$27,$2B,$2D,$30,$32,$33,$34,$35,$35,$33,$33,$32,$30,$2E,$2D,$2C,$2B,$2A,$2A,$2A,$2B,$2C,$2E,$30,$32,$36,$38,$3B +.byte $3E,$42,$45,$47,$49,$4B,$4B,$4B,$4A,$49,$47,$45,$42,$3D,$3A,$35,$30,$2B,$26,$22,$1E,$1A,$17,$14,$13,$11,$10,$10,$10,$12,$12,$14 +.byte $15,$18,$1A,$1B,$1D,$1E,$1F,$1F,$1F,$1F,$1E,$1D,$1B,$18,$16,$14,$10,$0E,$0C,$0B,$09,$08,$08,$09,$0A,$0C,$0E,$11,$14,$19,$1D,$22 + + +; Lookup table for colors +; Note the sine tables point roughly to the middle and go to the edges + + + +lores_colors_rgb: ; 256 + +; black +.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 +.byte $00,$00,$00,$00,$00,$00,$00,$00 +.byte $00,$00,$00,$00,$00,$00,$00,$00 + + +; red gradient +; $00, $11, $33, $BB, $FF, $BB, $33, $11 + +.byte $11,$11,$11,$11,$11,$11,$11,$11 +.byte $11,$11,$33,$33,$33,$33,$33,$33 +.byte $33,$33,$33,$33,$bb,$bb,$bb,$bb +.byte $bb,$bb,$bb,$bb,$bb,$bb,$ff,$ff +.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff +.byte $bb,$bb,$bb,$bb,$bb,$bb,$bb,$bb +.byte $bb,$bb,$33,$33,$33,$33,$33,$33 +.byte $33,$33,$33,$33,$11,$11,$11,$11 + +; red +.if 0 +.byte $11,$11,$11,$11,$11,$33,$33,$33 +.byte $33,$33,$bb,$bb,$bb,$bb,$bb,$ff +.byte $ff,$ff,$ff,$ff,$ff,$bb,$bb,$bb +.byte $bb,$bb,$33,$33,$33,$33,$33,$11 +.byte $11,$11,$11,$11,$33,$33,$33,$33 +.byte $bb,$bb,$bb,$bb,$bb,$ff,$ff,$ff +.byte $ff,$ff,$ff,$bb,$bb,$bb,$bb,$bb +.byte $33,$33,$33,$33,$33,$33,$11,$11 +.endif + +; $00, $22, $66, $77, $FF, $77, $66, $22 + +; 22 66 77 ff 77 66 + +; blue + + +.byte $22,$22,$22,$22,$22,$22,$22,$22 +.byte $22,$22,$22,$66,$66,$66,$66,$66 +.byte $66,$66,$66,$66,$66,$66,$77,$77 +.byte $77,$77,$77,$77,$77,$77,$77,$77 +.byte $77,$ff,$ff,$ff,$ff,$ff,$ff,$ff +.byte $ff,$ff,$ff,$ff,$77,$77,$77,$77 +.byte $77,$77,$77,$77,$77,$77,$77,$66 +.byte $66,$66,$66,$66,$66,$66,$66,$66 + + +.if 0 +.byte $22,$22,$22,$22,$22,$66,$66,$66 +.byte $66,$66,$77,$77,$77,$77,$77,$ff +.byte $ff,$ff,$ff,$ff,$ff,$77,$77,$77 +.byte $77,$77,$66,$66,$66,$66,$66,$22 +.byte $22,$22,$22,$22,$66,$66,$66,$66 +.byte $77,$77,$77,$77,$77,$ff,$ff,$ff +.byte $ff,$ff,$ff,$77,$77,$77,$77,$77 +.byte $66,$66,$66,$66,$66,$66,$22,$22 +.endif + + + +; $00, $44, $CC, $DD, $FF, $DD, $CC, $44 + +; green +.byte $44,$44,$44,$44,$44,$cc,$cc,$cc +.byte $cc,$cc,$dd,$dd,$dd,$dd,$dd,$ff +.byte $ff,$ff,$ff,$ff,$ff,$dd,$dd,$dd +.byte $dd,$dd,$cc,$cc,$cc,$cc,$cc,$44 +.byte $44,$44,$44,$44,$cc,$cc,$cc,$cc +.byte $dd,$dd,$dd,$dd,$dd,$ff,$ff,$ff +.byte $ff,$ff,$ff,$dd,$dd,$dd,$dd,$dd +.byte $cc,$cc,$cc,$cc,$cc,$cc,$44,$44 +.if 0 +.byte $00,$00,$00,$00,$00,$00,$00,$00 +.byte $44,$44,$44,$44,$44,$44,$44,$44 +.byte $CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC +.byte $DD,$DD,$DD,$DD,$DD,$DD,$DD,$DD +.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff +.byte $DD,$DD,$DD,$DD,$DD,$DD,$DD,$DD +.byte $CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC +.byte $44,$44,$44,$44,$44,$44,$44,$44 +.endif + +; This table has relatively fine color bands +.if 0 +lores_colors_fine: ; 256 +.byte $00,$00,$00,$00,$88,$88,$88,$88 +.byte $55,$55,$55,$55,$99,$99,$99,$99 +.byte $ff,$ff,$ff,$ff,$bb,$bb,$bb,$bb +.byte $33,$33,$33,$33,$22,$22,$22,$22 +.byte $66,$66,$66,$66,$77,$77,$77,$77 +.byte $44,$44,$44,$44,$cc,$cc,$cc,$cc +.byte $ee,$ee,$ee,$ee,$dd,$dd,$dd,$dd +.byte $99,$99,$99,$99,$11,$11,$11,$11 + +.byte $00,$00,$00,$00,$88,$88,$88,$88 +.byte $55,$55,$55,$55,$99,$99,$99,$99 +.byte $ff,$ff,$ff,$ff,$bb,$bb,$bb,$bb +.byte $33,$33,$33,$33,$22,$22,$22,$22 +.byte $66,$66,$66,$66,$77,$77,$77,$77 +.byte $44,$44,$44,$44,$cc,$cc,$cc,$cc +.byte $ee,$ee,$ee,$ee,$dd,$dd,$dd,$dd +.byte $99,$99,$99,$99,$11,$11,$11,$11 + +.byte $00,$00,$00,$00,$88,$88,$88,$88 +.byte $55,$55,$55,$55,$99,$99,$99,$99 +.byte $ff,$ff,$ff,$ff,$bb,$bb,$bb,$bb +.byte $33,$33,$33,$33,$22,$22,$22,$22 +.byte $66,$66,$66,$66,$77,$77,$77,$77 +.byte $44,$44,$44,$44,$cc,$cc,$cc,$cc +.byte $ee,$ee,$ee,$ee,$dd,$dd,$dd,$dd +.byte $99,$99,$99,$99,$11,$11,$11,$11 + +.byte $00,$00,$00,$00,$88,$88,$88,$88 +.byte $55,$55,$55,$55,$99,$99,$99,$99 +.byte $ff,$ff,$ff,$ff,$bb,$bb,$bb,$bb +.byte $33,$33,$33,$33,$22,$22,$22,$22 +.byte $66,$66,$66,$66,$77,$77,$77,$77 +.byte $44,$44,$44,$44,$cc,$cc,$cc,$cc +.byte $ee,$ee,$ee,$ee,$dd,$dd,$dd,$dd +.byte $99,$99,$99,$99,$11,$11,$11,$11 +.else +; This table has relatively wide color bands +lores_colors_wide: ; 256 +.byte $00,$00,$00,$00,$00,$00,$00,$00 +.byte $00,$00,$00,$00,$00,$00,$00,$00 +.byte $88,$88,$88,$88,$88,$88,$88,$88 +.byte $88,$88,$88,$88,$88,$88,$88,$88 +.byte $55,$55,$55,$55,$55,$55,$55,$55 +.byte $55,$55,$55,$55,$55,$55,$55,$55 +.byte $22,$22,$22,$22,$22,$22,$22,$22 +.byte $22,$22,$22,$22,$22,$22,$22,$22 +.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff +.byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff +.byte $bb,$bb,$bb,$bb,$bb,$bb,$bb,$bb +.byte $bb,$bb,$bb,$bb,$bb,$bb,$bb,$bb +.byte $33,$33,$33,$33,$33,$33,$33,$33 +.byte $33,$33,$33,$33,$33,$33,$33,$33 +.byte $22,$22,$22,$22,$22,$22,$22,$22 +.byte $22,$22,$22,$22,$22,$22,$22,$22 +.byte $66,$66,$66,$66,$66,$66,$66,$66 +.byte $66,$66,$66,$66,$66,$66,$66,$66 +.byte $77,$77,$77,$77,$77,$77,$77,$77 +.byte $77,$77,$77,$77,$77,$77,$77,$77 +.byte $44,$44,$44,$44,$44,$44,$44,$44 +.byte $44,$44,$44,$44,$44,$44,$44,$44 +.byte $cc,$cc,$cc,$cc,$cc,$cc,$cc,$cc +.byte $cc,$cc,$cc,$cc,$cc,$cc,$cc,$cc +.byte $ee,$ee,$ee,$ee,$ee,$ee,$ee,$ee +.byte $ee,$ee,$ee,$ee,$ee,$ee,$ee,$ee +.byte $dd,$dd,$dd,$dd,$dd,$dd,$dd,$dd +.byte $dd,$dd,$dd,$dd,$dd,$dd,$dd,$dd +.byte $99,$99,$99,$99,$99,$99,$99,$99 +.byte $99,$99,$99,$99,$99,$99,$99,$99 +.byte $11,$11,$11,$11,$11,$11,$11,$11 +.byte $11,$11,$11,$11,$11,$11,$11,$11 +.endif + +Table1 = $5000 +Table2 = $5000+64 + +remap_table: + .byte $00,$40,$80,$00,$C0 + +mask_src_table: + .byte $60-8,$64-8,$68-8,$6C-8,$70-8,$74-8,$78-8,$7C-8 + .byte $7C-8,$78-8,$74-8,$70-8,$6C-8,$68-8,$64-8,$60-8 + +.include "wait_keypress.s" +.include "irq_wait.s" + +mask1_data: +.incbin "graphics/tree01.gr.zx02" +mask2_data: +.incbin "graphics/tree03.gr.zx02" +mask3_data: +.incbin "graphics/tree05.gr.zx02" +mask4_data: +.incbin "graphics/tree07.gr.zx02" +mask5_data: +.incbin "graphics/tree09.gr.zx02" +mask6_data: +.incbin "graphics/tree11.gr.zx02" +mask7_data: +.incbin "graphics/tree13.gr.zx02" +mask8_data: +.incbin "graphics/tree15.gr.zx02" diff --git a/demos/xmas_2023/zp.inc b/demos/xmas_2023/zp.inc new file mode 100644 index 00000000..9c88fc81 --- /dev/null +++ b/demos/xmas_2023/zp.inc @@ -0,0 +1,282 @@ +;================== +;================== +; 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 +FRAME = $E8 +FRAMEL = $E8 +FRAMEH = $E9 +BTC_L = $EA ; audio +BTC_H = $EB ; audio +MASKL = $EC ; gr_putsprite_mask +MASKH = $ED + +;============================================== +; $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 + +; sierzoom +;XX = $F0 +XX_TH = $F1 +XX_TL = $F2 +;YY = $F3 +YY_TH = $F4 +YY_TL = $F5 +T_L = $F6 +T_H = $F7 +SAVED = $F8 + +BAR_X1 = $F0 +BAR_X2 = $F1 + +; spheres +BASE_SPRITEL = $F0 +BASE_SPRITEH = $F1 +CURRENT_SPRITEL = $F2 +CURRENT_SPRITEH = $F3 +XMISSION_COUNT = $F4 +REF1L = $F5 +REF1H = $F6 +REF2L = $F7 +REF2H = $F8 +REFCOUNT = $F9 + +; BIOS +STRING_COUNT = $F0 +LEAD0 = $F1 +SCROLL_OUT = $F2 +SCROLL_IN = $F3 +MEMCOUNT = $F4 +FAKE_KEY_COUNT = $F5 + +; OPENER +TICKER = $F1 +P2_OFFSET = $F2 + +; dots +MAX_DOTS = $F1 +Y_OFFSET = $F2 + +;============================================== +; $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/xmas_2023/zx02_optim.s b/demos/xmas_2023/zx02_optim.s new file mode 100644 index 00000000..5eebc2e0 --- /dev/null +++ b/demos/xmas_2023/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