diff --git a/demos/vertical_scroll2/Makefile b/demos/vertical_scroll2/Makefile index 6756498f..d9579faa 100644 --- a/demos/vertical_scroll2/Makefile +++ b/demos/vertical_scroll2/Makefile @@ -28,7 +28,7 @@ HELLO: hello.bas SCROLLER: scroller.o ld65 -o SCROLLER scroller.o -C ../../linker_scripts/apple2_1000.inc -scroller.o: scroller.s gr_copy.s gr_unrle.s gr_unrle_large.s \ +scroller.o: scroller.s gr_copy.s \ gr_copy_large.s \ zp.inc hardware.inc spaceman.lzsa spaceman2.lzsa ca65 -o scroller.o scroller.s -l scroller.lst @@ -38,7 +38,7 @@ scroller.o: scroller.s gr_copy.s gr_unrle.s gr_unrle_large.s \ DSR_INTRO: dsr_intro.o ld65 -o DSR_INTRO dsr_intro.o -C ../../linker_scripts/apple2_1000.inc -dsr_intro.o: dsr_intro.s gr_copy.s gr_unrle.s gr_unrle_large.s \ +dsr_intro.o: dsr_intro.s gr_copy.s \ gr_copy_large.s \ zp.inc hardware.inc desire.lzsa desire2.lzsa ca65 -o dsr_intro.o dsr_intro.s -l dsr_intro.lst diff --git a/games/lemm/Makefile b/games/lemm/Makefile index ccef4b20..bb07b723 100644 --- a/games/lemm/Makefile +++ b/games/lemm/Makefile @@ -27,7 +27,8 @@ LEMM_TEST: lemm_test.o ld65 -o LEMM_TEST lemm_test.o -C ../../linker_scripts/apple2_4000.inc lemm_test.o: lemm_test.s zp.inc hardware.inc \ - graphics/graphics_test.inc + graphics/graphics_test.inc \ + interrupt_handler.s ca65 -o lemm_test.o lemm_test.s -l lemm_test.lst #### diff --git a/games/lemm/gr_offsets.s b/games/lemm/gr_offsets.s new file mode 100644 index 00000000..d3af91f7 --- /dev/null +++ b/games/lemm/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/games/lemm/hello.bas b/games/lemm/hello.bas index 6ce4ca9f..a8cf5748 100644 --- a/games/lemm/hello.bas +++ b/games/lemm/hello.bas @@ -12,4 +12,4 @@ 110 PRINT " A \/\/\/ SOFTWARE PRODUCTION" 115 PRINT 120 PRINT " HTTP://WWW.DEATER.NET/WEAVE/VMWPROD" -130 PRINT CHR$(4)"CATALOG" +130 PRINT CHR$(4)"BRUN LEMM_TEST" diff --git a/games/lemm/interrupt_handler.s b/games/lemm/interrupt_handler.s new file mode 100644 index 00000000..c42a0eee --- /dev/null +++ b/games/lemm/interrupt_handler.s @@ -0,0 +1,152 @@ + ;================================ + ;================================ + ; 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) + + +pt3_irq_smc1: + bit MOCK_6522_T1CL ; clear 6522 interrupt by reading T1C-L ; 4 + + lda DONE_PLAYING ; 3 + beq ym_play_music ; if song done, don't play music ; 3/2nt + jmp done_pt3_irq_handler ; 3 + ;============ + ; 13 + +ym_play_music: + + lda BASE_FRAME_L + sta CURRENT_FRAME_L + lda BASE_FRAME_H + sta CURRENT_FRAME_H + + ldx #0 +frame_loop: + ldy #0 + lda (CURRENT_FRAME_L),Y + jsr update_register + + clc + lda CURRENT_FRAME_H + adc #$4 + sta CURRENT_FRAME_H + + inx + cpx #12 + bne frame_loop + + + inc BASE_FRAME_L + bne not_oflo + + inc BASE_FRAME_H + lda BASE_FRAME_H + cmp #$D4 + bne not_oflo + + ; FIXME: handle out of data properly + lda #$D0 + sta BASE_FRAME_H + +not_oflo: + + + + jmp done_pt3_update_register + + + + +update_register: + +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 + + rts + +done_pt3_update_register: + +done_pt3_irq_handler: + + 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/games/lemm/lemm_test.s b/games/lemm/lemm_test.s index 658868b5..a9330fbe 100644 --- a/games/lemm/lemm_test.s +++ b/games/lemm/lemm_test.s @@ -7,18 +7,15 @@ .include "hardware.inc" lemm_test_start: - ;=================== - ; init screen - ;=================== - jsr TEXT - jsr HOME - bit KEYRESET + lda #0 + sta DRAW_PAGE - bit SET_GR - bit PAGE0 - bit HIRES - bit FULLGR + ;==================== + ; detect model + ;==================== + + jsr detect_appleii_model ;=================== ; machine workarounds @@ -55,6 +52,192 @@ lemm_test_start: not_a_iigs: + ;=================== + ; print config + ;=================== + + lda #config_string + sta OUTH + + jsr move_and_print + + ; print detected model + + lda APPLEII_MODEL + ora #$80 + sta $7d0+8 ; 23,8 + + ; if GS print the extra S + cmp #'G'|$80 + bne not_gs + lda #'S'|$80 + sta $7d0+9 + +not_gs: + + ;========================================= + ; detect if we have a language card (64k) + ; and load sound into it if possible + ;=================================== + + lda #0 + sta SOUND_STATUS ; clear out, sound enabled + + ;=========================================== + ; skip checks if open-apple being held down + + lda $C061 + and #$80 ; only bit 7 is affected + bne skip_all_checks ; rest is floating bus + + + jsr detect_language_card + bcs no_language_card + +yes_language_card: + ; update status + lda #'6'|$80 + sta $7d0+11 ; 23,11 + lda #'4'|$80 + sta $7d0+12 ; 23,12 + + ; update sound status + lda SOUND_STATUS + ora #SOUND_IN_LC + sta SOUND_STATUS + + jmp done_language_card + +no_language_card: + +done_language_card: + + ;=================================== + ; Detect Mockingboard + ;=================================== + +PT3_ENABLE_APPLE_IIC = 1 + + ; detect mockingboard + jsr mockingboard_detect + + bcc mockingboard_notfound + +mockingboard_found: + ; print detected location + + lda #'S'+$80 ; change NO to slot + sta $7d0+30 + + lda MB_ADDR_H ; $C4 = 4, want $B4 1100 -> 1011 + and #$87 + ora #$30 + + sta $7d0+31 ; 23,31 + + ; NOTE: in this game we need both language card && mockingboard + ; to enable mockingboard music + + lda SOUND_STATUS + and #SOUND_IN_LC + beq dont_enable_mc + + lda SOUND_STATUS + ora #SOUND_MOCKINGBOARD + sta SOUND_STATUS + +dont_enable_mc: + +mockingboard_notfound: + +skip_all_checks: + + + ;================================== + ; load music into the language card + ; into $D000 set 2 + ;================================== + + ; switch in language card + ; read/write RAM, $d000 bank 2 + + lda $C083 + lda $C083 + +; lda $C081 ; enable ROM +; lda $C081 ; enable write + + ; actually load it + + lda #lemm5_part1_lzsa + sta getsrc_smc+2 ; LZSA_SRC_HI + + lda #$d0 + + jsr decompress_lzsa2_fast + + lda #0 + sta DONE_PLAYING + sta BASE_FRAME_L + + lda #$D0 + sta BASE_FRAME_H + + lda #1 + sta LOOP + + jsr mockingboard_patch ; patch to work in slots other than 4? + + ;======================= + ; Set up 50Hz interrupt + ;======================== + + jsr mockingboard_init + jsr mockingboard_setup_interrupt + + +zurg: + ;============================ + ; Init the Mockingboard + ;============================ + + jsr reset_ay_both + jsr clear_ay_both + + + + ;======================= + ; start music + ;======================= + + + + + + + jsr wait_until_keypress + + cli + + ;=================== + ; init screen + ;=================== + +; jsr TEXT ; can't, swapped ROM out +; jsr HOME + bit KEYRESET + + bit SET_GR + bit PAGE0 + bit HIRES + bit FULLGR + + + ;=================== ; Load hires graphics ;=================== @@ -97,91 +280,6 @@ load_graphics_loop: jmp load_graphics_loop - ;=================================== - ; detect if we have a language card - ; and load sound into it if possible - ;=================================== - -; lda #0 -; sta SOUND_STATUS ; clear out, sound enabled - -; jsr detect_language_card -; bcs no_language_card - - ; update sound status -; lda SOUND_STATUS -; ora #SOUND_IN_LC -; sta SOUND_STATUS - - ; load sounds into LC - - ; read ram, write ram, use $d000 bank1 -; bit $C08B -; bit $C08B - -; lda #linking_noise_compressed -; sta getsrc_smc+2 - -; lda #$D0 ; decompress to $D000 - -; jsr decompress_lzsa2_fast - -;blah: - - ; read rom, nowrite, use $d000 bank1 -; bit $C08A - -no_language_card: - - ;=================================== - ; Setup Mockingboard - ;=================================== -; lda #0 -; sta DONE_PLAYING -; sta LOOP - - ; detect mockingboard -; jsr mockingboard_detect - -; bcc mockingboard_notfound - -mockingboard_found: -;; jsr mockingboard_patch ; patch to work in slots other than 4? - -; lda SOUND_STATUS -; ora #SOUND_MOCKINGBOARD -; sta SOUND_STATUS - - ;======================= - ; 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 - -; jmp done_setup_sound - - -mockingboard_notfound: - - -done_setup_sound: - @@ -192,21 +290,36 @@ done_setup_sound: ; .include "gr_pageflip.s" ; .include "gr_copy.s" ; .include "wait_a_bit.s" -; .include "gr_offsets.s" + .include "gr_offsets.s" .include "decompress_fast_v2.s" .include "wait_keypress.s" ; .include "print_help.s" ; .include "gr_fast_clear.s" -; .include "text_print.s" + .include "text_print.s" ; .include "init_vars.s" ; .include "graphics_title/title_graphics.inc" -; .include "lc_detect.s" + .include "lc_detect.s" ; pt3 player + +;.include "pt3_lib_mockingboard.inc" +.include "pt3_lib_detect_model.s" +.include "pt3_lib_mockingboard_detect.s" +.include "pt3_lib_mockingboard_setup.s" +.include "interrupt_handler.s" +.include "pt3_lib_mockingboard_patch.s" + + +config_string: +; 0123456789012345678901234567890123456789 +.byte 0,23,"APPLE II?, 48K, MOCKINGBOARD: NO, SSI: N",0 +; MOCKINGBOARD: NONE + + ; .include "pt3_lib_core.s" ; .include "pt3_lib_init.s" ; .include "interrupt_handler.s" @@ -219,15 +332,6 @@ new_title: - - - - - - -;PT3_LOC = theme_music - -;.align $100 -;theme_music: -;.incbin "audio/theme.pt3" +lemm5_part1_lzsa: +.incbin "music/lemm5.part1.lzsa" diff --git a/games/lemm/music/Makefile b/games/lemm/music/Makefile index bd18bd43..8558f5b6 100644 --- a/games/lemm/music/Makefile +++ b/games/lemm/music/Makefile @@ -1,71 +1,69 @@ LZSA = ~/research/lzsa/lzsa/lzsa YM5_TO_RAW = ~/research/vmw-meter.git/ay-3-8910/conversion_tools/ym5_to_raw +YM_TO_YM5 = ~/research/vmw-meter.git/ay-3-8910/conversion_tools/ym_to_ym5 +RAW_INTERLEAVE = ~/research/vmw-meter.git/ay-3-8910/conversion_tools/raw_interleave + +all: lemm5.part1.lzsa lemm5.part2.lzsa lemm5.part3.lzsa +# lemm5.part4.lzsa lemm5.part5.lzsa lemm5.part6.lzsa \ +# lemm5.part7.lzsa lemm5.part8.lzsa + +lemm5.raw: lemm5.ym + $(YM5_TO_RAW) ./lemm5.ym > lemm5.raw +lemm5.part1: lemm5.raw + $(RAW_INTERLEAVE) -m 11 -c 1024 ./lemm5 + +lemm5.part1.lzsa: lemm5.part1 + $(LZSA) -r -f2 lemm5.part1 lemm5.part1.lzsa + +#lemm5.part2: lemm5.raw +# dd conv=notrunc if=lemm5.raw of=lemm5.part2 bs=4096 count=1 seek=1 skip=1 + +lemm5.part2.lzsa: lemm5.part2 + $(LZSA) -r -f2 lemm5.part2 lemm5.part2.lzsa -all: lemmings5.part1.lzsa lemmings5.part2.lzsa lemmings5.part3.lzsa \ - lemmings5.part4.lzsa lemmings5.part5.lzsa lemmings5.part6.lzsa \ - lemmings5.part7.lzsa lemmings5.part8.lzsa +#lemm5.part3: lemm5.raw +# dd conv=notrunc if=lemm5.raw of=lemm5.part3 bs=4096 count=1 seek=2 skip=2 -Lemmings5.raw: Lemmings5.ym - $(YM5_TO_RAW) ./Lemmings5.ym > Lemmings5.raw +lemm5.part3.lzsa: lemm5.part3 + $(LZSA) -r -f2 lemm5.part3 lemm5.part3.lzsa -lemmings5.part1: Lemmings5.raw - dd conv=notrunc if=Lemmings5.raw of=lemmings5.part1 bs=4096 count=1 seek=0 skip=0 +#lemm5.part4: lemm5.raw +# dd conv=notrunc if=lemm5.raw of=lemm5.part4 bs=4096 count=1 seek=3 skip=3 -lemmings5.part1.lzsa: lemmings5.part1 - $(LZSA) -r -f2 lemmings5.part1 lemmings5.part1.lzsa +#lemm5.part4.lzsa: lemm5.part4 +# $(LZSA) -r -f2 lemm5.part4 lemm5.part4.lzsa -lemmings5.part2: Lemmings5.raw - dd conv=notrunc if=Lemmings5.raw of=lemmings5.part2 bs=4096 count=1 seek=1 skip=1 +#lemm5.part5: lemm5.raw +# dd conv=notrunc if=lemm5.raw of=lemm5.part5 bs=4096 count=1 seek=4 skip=4 -lemmings5.part2.lzsa: lemmings5.part2 - $(LZSA) -r -f2 lemmings5.part2 lemmings5.part2.lzsa +#lemm5.part5.lzsa: lemm5.part5 +# $(LZSA) -r -f2 lemm5.part5 lemm5.part5.lzsa -lemmings5.part3: Lemmings5.raw - dd conv=notrunc if=Lemmings5.raw of=lemmings5.part3 bs=4096 count=1 seek=2 skip=2 +#lemm5.part6: lemm5.raw +# dd conv=notrunc if=lemm5.raw of=lemm5.part6 bs=4096 count=1 seek=5 skip=5 -lemmings5.part3.lzsa: lemmings5.part3 - $(LZSA) -r -f2 lemmings5.part3 lemmings5.part3.lzsa +#lemm5.part6.lzsa: lemm5.part6 +# $(LZSA) -r -f2 lemm5.part1 lemm5.part1.lzsa -lemmings5.part4: Lemmings5.raw - dd conv=notrunc if=Lemmings5.raw of=lemmings5.part4 bs=4096 count=1 seek=3 skip=3 +#lemm5.part7: lemm5.raw +# dd conv=notrunc if=lemm5.raw of=lemm5.part7 bs=4096 count=1 seek=6 skip=6 -lemmings5.part4.lzsa: lemmings5.part4 - $(LZSA) -r -f2 lemmings5.part4 lemmings5.part4.lzsa +#lemm5.part7.lzsa: lemm5.part7 +# $(LZSA) -r -f2 lemm5.part7 lemm5.part7.lzsa -lemmings5.part5: Lemmings5.raw - dd conv=notrunc if=Lemmings5.raw of=lemmings5.part5 bs=4096 count=1 seek=4 skip=4 +#lemm5.part8: lemm5.raw +# dd conv=notrunc if=lemm5.raw of=lemm5.part8 bs=4096 count=1 seek=7 skip=7 -lemmings5.part5.lzsa: lemmings5.part5 - $(LZSA) -r -f2 lemmings5.part5 lemmings5.part5.lzsa - - -lemmings5.part6: Lemmings5.raw - dd conv=notrunc if=Lemmings5.raw of=lemmings5.part6 bs=4096 count=1 seek=5 skip=5 - -lemmings5.part6.lzsa: lemmings5.part6 - $(LZSA) -r -f2 lemmings5.part1 lemmings5.part1.lzsa - - -lemmings5.part7: Lemmings5.raw - dd conv=notrunc if=Lemmings5.raw of=lemmings5.part7 bs=4096 count=1 seek=6 skip=6 - -lemmings5.part7.lzsa: lemmings5.part7 - $(LZSA) -r -f2 lemmings5.part7 lemmings5.part7.lzsa - - -lemmings5.part8: Lemmings5.raw - dd conv=notrunc if=Lemmings5.raw of=lemmings5.part8 bs=4096 count=1 seek=7 skip=7 - -lemmings5.part8.lzsa: lemmings5.part8 - $(LZSA) -r -f2 lemmings5.part8 lemmings5.part8.lzsa +#lemm5.part8.lzsa: lemm5.part8 +# $(LZSA) -r -f2 lemm5.part8 lemm5.part8.lzsa clean: diff --git a/games/lemm/music/Lemmings5.ym b/games/lemm/music/lemm5.ym similarity index 100% rename from games/lemm/music/Lemmings5.ym rename to games/lemm/music/lemm5.ym diff --git a/games/lemm/pt3_lib_detect_model.s b/games/lemm/pt3_lib_detect_model.s new file mode 100644 index 00000000..31c1b8dc --- /dev/null +++ b/games/lemm/pt3_lib_detect_model.s @@ -0,0 +1,63 @@ + ;=========================== + ; Check Apple II model + ;=========================== + ; this is mostly for IIc support + ; as it does interrupts differently + + ; ' ' ($20) = Apple II + ; '+' ($2B) = Apple II+ + ; 'E' ($45) = Apple IIe + ; 'C' ($43) = Apple IIc + ; 'G' ($47) = Apple IIgs + + +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 + beq done_apple_detect + + lda #'+' + cpx #$EA + beq done_apple_detect + + ; TODO: check for J-plus or III? + + 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 + + ; assume GS? + + lda #'G' + bne done_apple_detect + +apple_iic: + lda #'C' + +done_apple_detect: + sta APPLEII_MODEL + rts diff --git a/games/lemm/pt3_lib_mockingboard.inc b/games/lemm/pt3_lib_mockingboard.inc new file mode 100644 index 00000000..80a8fdd1 --- /dev/null +++ b/games/lemm/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/games/lemm/pt3_lib_mockingboard_detect.s b/games/lemm/pt3_lib_mockingboard_detect.s new file mode 100644 index 00000000..cd36df72 --- /dev/null +++ b/games/lemm/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/games/lemm/pt3_lib_mockingboard_patch.s b/games/lemm/pt3_lib_mockingboard_patch.s new file mode 100644 index 00000000..89665c64 --- /dev/null +++ b/games/lemm/pt3_lib_mockingboard_patch.s @@ -0,0 +1,120 @@ + +;=================================================================== +; 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 + +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 + +;.ifdef PT3_ENABLE_APPLE_IIC +; lda APPLEII_MODEL +; cmp #'C' +; bne done_iic_hack + + ; bypass the firmware interrupt handler + ; should we do this on IIe too? probably faster + + ; first we have to copy the ROM to the language card +.if 0 + sei ; disable interrupts + +copy_rom_loop: + lda $c089 ; read ROM, write RAM1 + lda $c089 + + ldy #0 +read_rom_loop: + lda $D000,Y + sta $400,Y ; note this uses text page as + ; temporary data store + iny + bne read_rom_loop + + lda $c08B ; read/write RAM1 + lda $c08B ; + +write_rom_loop: + lda $400,Y + sta $D000,Y + iny + bne write_rom_loop + + inc read_rom_loop+2 + inc write_rom_loop+5 + bne copy_rom_loop +.endif + lda #interrupt_handler + sta $ffff + + + ; nop out the "lda $45" in the irq handler + lda #$EA + sta interrupt_smc + sta interrupt_smc+1 +;.endif +done_iic_hack: + + + ;========================= + ; 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/games/lemm/text_print.s b/games/lemm/text_print.s new file mode 100644 index 00000000..ed6cb40a --- /dev/null +++ b/games/lemm/text_print.s @@ -0,0 +1,145 @@ + + ;================================ + ; 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 + + +.if 0 + ;================================ + ; move and print a list of lines + ;================================ +move_and_print_list: + jsr move_and_print + ldy #0 + lda (OUTL),Y + bpl move_and_print_list + + rts + + + + ;================================ + ; move and print a list of lines + ;================================ +move_and_print_list_both_pages: + lda DRAW_PAGE + pha + + lda OUTL + pha + lda OUTH + pha + + lda #0 + sta DRAW_PAGE + + jsr move_and_print_list + + pla + sta OUTH + pla + sta OUTL + + lda #4 + sta DRAW_PAGE + + jsr move_and_print_list + + pla + sta DRAW_PAGE + + + rts + + + + ;======================= + ; print to both pages + ;======================= +print_both_pages: + lda DRAW_PAGE + pha + + lda OUTL + pha + lda OUTH + pha + + lda #0 + sta DRAW_PAGE + + jsr move_and_print + + pla + sta OUTH + pla + sta OUTL + + lda #4 + sta DRAW_PAGE + + jsr move_and_print + + pla + sta DRAW_PAGE + + + rts +.endif diff --git a/games/lemm/zp.inc b/games/lemm/zp.inc index 79c4e7db..efa9a29d 100644 --- a/games/lemm/zp.inc +++ b/games/lemm/zp.inc @@ -45,6 +45,13 @@ IN_RIGHT = $6B BTC_L = $6C BTC_H = $6D + +CURRENT_FRAME_L = $70 +CURRENT_FRAME_H = $71 +BASE_FRAME_L = $72 +BASE_FRAME_H = $73 + + ; pt3 player registers REGISTER_DUMP = $70 AY_REGISTERS = $70 @@ -90,7 +97,7 @@ LASER_OUT = $87 LASER_X = $88 LASER_Y = $89 LASER_DIRECTION = $8A -TILEMAP_X = $8B +APPLEII_MODEL = $8B TILEMAP_Y = $8C DUKE_FOOT_OFFSET = $8D