From 7095b76a6c69b9e410ff4d9507bee977ee3c98a4 Mon Sep 17 00:00:00 2001 From: Vince Weaver Date: Fri, 4 Mar 2022 16:53:49 -0500 Subject: [PATCH] games: Lemm should probably be working on other things --- games/lemm/Makefile | 42 +++ games/lemm/decompress_fast_v2.s | 370 ++++++++++++++++++++++++++ games/lemm/graphics/Makefile | 48 ++++ games/lemm/graphics/level1.png | Bin 0 -> 28337 bytes games/lemm/graphics/level2.png | Bin 0 -> 7199 bytes games/lemm/graphics/level2_actual.png | Bin 0 -> 4729 bytes games/lemm/hardware.inc | 98 +++++++ games/lemm/hello.bas | 15 ++ games/lemm/lemm_test.s | 233 ++++++++++++++++ games/lemm/music/Lemmings5.ym | Bin 0 -> 1053 bytes games/lemm/music/Makefile | 72 +++++ games/lemm/wait_keypress.s | 5 + games/lemm/zp.inc | 200 ++++++++++++++ 13 files changed, 1083 insertions(+) create mode 100644 games/lemm/Makefile create mode 100644 games/lemm/decompress_fast_v2.s create mode 100644 games/lemm/graphics/Makefile create mode 100644 games/lemm/graphics/level1.png create mode 100644 games/lemm/graphics/level2.png create mode 100644 games/lemm/graphics/level2_actual.png create mode 100644 games/lemm/hardware.inc create mode 100644 games/lemm/hello.bas create mode 100644 games/lemm/lemm_test.s create mode 100644 games/lemm/music/Lemmings5.ym create mode 100644 games/lemm/music/Makefile create mode 100644 games/lemm/wait_keypress.s create mode 100644 games/lemm/zp.inc diff --git a/games/lemm/Makefile b/games/lemm/Makefile new file mode 100644 index 00000000..ccef4b20 --- /dev/null +++ b/games/lemm/Makefile @@ -0,0 +1,42 @@ +include ../../Makefile.inc + +DOS33 = ../../utils/dos33fs-utils/dos33 +DOS33_RAW = ../../utils/dos33fs-utils/dos33_raw +B2D = ../../utils/bmp2dhr/b2d +PNG2GR = ../../utils/gr-utils/png2gr +LZSA = ~/research/lzsa/lzsa/lzsa +TOKENIZE = ../../utils/asoft_basic-utils/tokenize_asoft +EMPTY_DISK = ../../empty_disk/empty.dsk + +all: lemm.dsk + +lemm.dsk: HELLO LEMM_TEST + cp $(EMPTY_DISK) lemm.dsk + $(DOS33) -y lemm.dsk SAVE A HELLO + $(DOS33) -y lemm.dsk BSAVE -a 0x4000 LEMM_TEST + + +### + +HELLO: hello.bas + $(TOKENIZE) < hello.bas > HELLO + +#### + +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 + ca65 -o lemm_test.o lemm_test.s -l lemm_test.lst + +#### + +graphics/graphics_test.inc: + cd graphics && make + +#### + +clean: + rm -f *~ *.o *.lst HELLO LEMM + diff --git a/games/lemm/decompress_fast_v2.s b/games/lemm/decompress_fast_v2.s new file mode 100644 index 00000000..fb2f24ad --- /dev/null +++ b/games/lemm/decompress_fast_v2.s @@ -0,0 +1,370 @@ +; note -- modified by Vince Weaver to assemble with ca65 +; in this case, A = page to decompress to +; getsrc_smc+1, getsrc_smc+2 is src location + +; ----------------------------------------------------------------------------- +; Decompress raw LZSA2 block. +; Create one with lzsa -r -f2 +; +; in: +; * LZSA_SRC_LO and LZSA_SRC_HI contain the compressed raw block address +; * LZSA_DST_LO and LZSA_DST_HI contain the destination buffer address +; +; out: +; * LZSA_DST_LO and LZSA_DST_HI contain the last decompressed byte address, +1 +; +; ----------------------------------------------------------------------------- +; Backward decompression is also supported, use lzsa -r -b -f2 +; To use it, also define BACKWARD_DECOMPRESS=1 before including this code! +; +; in: +; * LZSA_SRC_LO/LZSA_SRC_HI must contain the address of the last byte of compressed data +; * LZSA_DST_LO/LZSA_DST_HI must contain the address of the last byte of the destination buffer +; +; out: +; * LZSA_DST_LO/LZSA_DST_HI contain the last decompressed byte address, -1 +; +; ----------------------------------------------------------------------------- +; +; Copyright (C) 2019 Emmanuel Marty, Peter Ferrie +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not be +; misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source distribution. +; ----------------------------------------------------------------------------- + +;NIBCOUNT = $FC ; zero-page location for temp offset + +decompress_lzsa2_fast: + + sta LZSA_DST_HI + + ldy #$00 + sty LZSA_DST_LO + sty NIBCOUNT + +decode_token: + jsr getsrc ; read token byte: XYZ|LL|MMM + pha ; preserve token on stack + + and #$18 ; isolate literals count (LL) + beq no_literals ; skip if no literals to copy + cmp #$18 ; LITERALS_RUN_LEN_V2? + bcc prepare_copy_literals ; if less, count is directly embedded in token + + jsr getnibble ; get extra literals length nibble + ; add nibble to len from token + adc #$02 ; (LITERALS_RUN_LEN_V2) minus carry + cmp #$12 ; LITERALS_RUN_LEN_V2 + 15 ? + bcc prepare_copy_literals_direct ; if less, literals count is complete + + jsr getsrc ; get extra byte of variable literals count + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + sbc #$EE ; overflow? + jmp prepare_copy_literals_direct + +prepare_copy_literals_large: + ; handle 16 bits literals count + ; literals count = directly these 16 bits + jsr getlargesrc ; grab low 8 bits in X, high 8 bits in A + tay ; put high 8 bits in Y + bcs prepare_copy_literals_high ; (*same as JMP PREPARE_COPY_LITERALS_HIGH but shorter) + +prepare_copy_literals: + lsr ; shift literals count into place + lsr + lsr + +prepare_copy_literals_direct: + tax + bcs prepare_copy_literals_large ; if so, literals count is large + +prepare_copy_literals_high: + txa + beq copy_literals + iny + +copy_literals: + jsr getput ; copy one byte of literals + dex + bne copy_literals + dey + bne copy_literals + +no_literals: + pla ; retrieve token from stack + pha ; preserve token again + asl + bcs repmatch_or_large_offset ; 1YZ: rep-match or 13/16 bit offset + + asl ; 0YZ: 5 or 9 bit offset + bcs offset_9_bit + + ; 00Z: 5 bit offset + + ldx #$FF ; set offset bits 15-8 to 1 + + jsr getcombinedbits ; rotate Z bit into bit 0, read nibble for bits 4-1 + ora #$E0 ; set bits 7-5 to 1 + bne got_offset_lo ; go store low byte of match offset and prepare match + +offset_9_bit: ; 01Z: 9 bit offset + ;;asl ; shift Z (offset bit 8) in place + rol + rol + and #$01 + eor #$FF ; set offset bits 15-9 to 1 + bne got_offset_hi ; go store high byte, read low byte of match offset and prepare match + ; (*same as JMP GOT_OFFSET_HI but shorter) + +repmatch_or_large_offset: + asl ; 13 bit offset? + bcs repmatch_or_16bit ; handle rep-match or 16-bit offset if not + + ; 10Z: 13 bit offset + + jsr getcombinedbits ; rotate Z bit into bit 8, read nibble for bits 12-9 + adc #$DE ; set bits 15-13 to 1 and substract 2 (to substract 512) + bne got_offset_hi ; go store high byte, read low byte of match offset and prepare match + ; (*same as JMP GOT_OFFSET_HI but shorter) + +repmatch_or_16bit: ; rep-match or 16 bit offset + ;;ASL ; XYZ=111? + bmi rep_match ; reuse previous offset if so (rep-match) + + ; 110: handle 16 bit offset + jsr getsrc ; grab high 8 bits +got_offset_hi: + tax + jsr getsrc ; grab low 8 bits +got_offset_lo: + sta OFFSLO ; store low byte of match offset + stx OFFSHI ; store high byte of match offset + +rep_match: +.ifdef BACKWARD_DECOMPRESS + + ; Backward decompression - substract match offset + + sec ; add dest + match offset + lda putdst+1 ; low 8 bits +OFFSLO = *+1 + sbc #$AA + sta copy_match_loop+1 ; store back reference address + lda putdst+2 +OFFSHI = *+1 + sbc #$AA ; high 8 bits + sta copy_match_loop+2 ; store high 8 bits of address + sec + +.else + + ; Forward decompression - add match offset + + clc ; add dest + match offset + lda putdst+1 ; low 8 bits +OFFSLO = *+1 + adc #$AA + sta copy_match_loop+1 ; store back reference address +OFFSHI = *+1 + lda #$AA ; high 8 bits + adc putdst+2 + sta copy_match_loop+2 ; store high 8 bits of address +.endif + + pla ; retrieve token from stack again + and #$07 ; isolate match len (MMM) + adc #$01 ; add MIN_MATCH_SIZE_V2 and carry + cmp #$09 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2? + bcc prepare_copy_match ; if less, length is directly embedded in token + + jsr getnibble ; get extra match length nibble + ; add nibble to len from token + adc #$08 ; (MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2) minus carry + cmp #$18 ; MIN_MATCH_SIZE_V2 + MATCH_RUN_LEN_V2 + 15? + bcc prepare_copy_match ; if less, match length is complete + + jsr getsrc ; get extra byte of variable match length + ; the carry is always set by the CMP above + ; GETSRC doesn't change it + sbc #$E8 ; overflow? + +prepare_copy_match: + tax + bcc prepare_copy_match_y ; if not, the match length is complete + beq decompression_done ; if EOD code, bail + + ; Handle 16 bits match length + jsr getlargesrc ; grab low 8 bits in X, high 8 bits in A + tay ; put high 8 bits in Y + +prepare_copy_match_y: + txa + beq copy_match_loop + iny + +copy_match_loop: + lda $AAAA ; get one byte of backreference + jsr putdst ; copy to destination + +.ifdef BACKWARD_DECOMPRESS + + ; Backward decompression -- put backreference bytes backward + + lda copy_match_loop+1 + beq getmatch_adj_hi +getmatch_done: + dec copy_match_loop+1 + +.else + + ; Forward decompression -- put backreference bytes forward + + inc copy_match_loop+1 + beq getmatch_adj_hi +getmatch_done: + +.endif + + dex + bne copy_match_loop + dey + bne copy_match_loop + jmp decode_token + +.ifdef BACKWARD_DECOMPRESS + +getmatch_adj_hi: + dec copy_match_loop+2 + jmp getmatch_done + +.else + +getmatch_adj_hi: + inc copy_match_loop+2 + jmp getmatch_done +.endif + +getcombinedbits: + eor #$80 + asl + php + + jsr getnibble ; get nibble into bits 0-3 (for offset bits 1-4) + plp ; merge Z bit as the carry bit (for offset bit 0) +combinedbitz: + rol ; nibble -> bits 1-4; carry(!Z bit) -> bit 0 ; carry cleared +decompression_done: + rts + +getnibble: +NIBBLES = *+1 + lda #$AA + lsr NIBCOUNT + bcc need_nibbles + and #$0F ; isolate low 4 bits of nibble + rts + +need_nibbles: + inc NIBCOUNT + jsr getsrc ; get 2 nibbles + sta NIBBLES + lsr + lsr + lsr + lsr + sec + rts + +.ifdef BACKWARD_DECOMPRESS + + ; Backward decompression -- get and put bytes backward + +getput: + jsr getsrc +putdst: +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + sta $AAAA + lda putdst+1 + beq putdst_adj_hi + dec putdst+1 + rts + +putdst_adj_hi: + dec putdst+2 + dec putdst+1 + rts + +getlargesrc: + jsr getsrc ; grab low 8 bits + tax ; move to X + ; fall through grab high 8 bits + +getsrc: +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + lda $AAAA + pha + lda getsrc+1 + beq getsrc_adj_hi + dec getsrc+1 + pla + rts + +getsrc_adj_hi: + dec getsrc+2 + dec getsrc+1 + pla + rts + +.else + + ; Forward decompression -- get and put bytes forward + +getput: + jsr getsrc +putdst: +LZSA_DST_LO = *+1 +LZSA_DST_HI = *+2 + sta $AAAA + inc putdst+1 + beq putdst_adj_hi + rts + +putdst_adj_hi: + inc putdst+2 + rts + +getlargesrc: + jsr getsrc ; grab low 8 bits + tax ; move to X + ; fall through grab high 8 bits + +getsrc: +getsrc_smc: +LZSA_SRC_LO = *+1 +LZSA_SRC_HI = *+2 + lda $AAAA + inc getsrc+1 + beq getsrc_adj_hi + rts + +getsrc_adj_hi: + inc getsrc+2 + rts +.endif + diff --git a/games/lemm/graphics/Makefile b/games/lemm/graphics/Makefile new file mode 100644 index 00000000..16d27ece --- /dev/null +++ b/games/lemm/graphics/Makefile @@ -0,0 +1,48 @@ +PNG2RLE = ../../../utils/gr-utils/png2rle +PNG2GR = ../../../utils/gr-utils/png2gr +PNG2HGR = ../../../utils/hgr-utils/png2hgr +LZSA = ~/research/lzsa/lzsa/lzsa +B2D = ../../../utils/bmp2dhr/b2d + +all: graphics_test.inc + +### + +graphics_test.inc: \ + level1.lzsa \ + level2.lzsa \ + level2_actual.lzsa + echo "level1_lzsa: .incbin \"level1.lzsa\"" > graphics_test.inc + echo "level2_lzsa: .incbin \"level2.lzsa\"" >> graphics_test.inc + echo "level2_actual_lzsa: .incbin \"level2_actual.lzsa\"" >> graphics_test.inc + +### + +level1.lzsa: level1.hgr + $(LZSA) -r -f2 level1.hgr level1.lzsa + +level1.hgr: level1.png + $(PNG2HGR) level1.png > level1.hgr + +### + +level2.lzsa: level2.hgr + $(LZSA) -r -f2 level2.hgr level2.lzsa + +level2.hgr: level2.png + $(PNG2HGR) level2.png > level2.hgr + +### + +level2_actual.lzsa: level2_actual.hgr + $(LZSA) -r -f2 level2_actual.hgr level2_actual.lzsa + +level2_actual.hgr: level2_actual.png + $(PNG2HGR) level2_actual.png > level2_actual.hgr + +#### + +clean: + rm -f *~ graphics_test.inc \ + *.lzsa *.gr *.hgr + diff --git a/games/lemm/graphics/level1.png b/games/lemm/graphics/level1.png new file mode 100644 index 0000000000000000000000000000000000000000..3416f8c9f71188ce7a61d8d0a387247061cf9aca GIT binary patch literal 28337 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 literal 0 HcmV?d00001 diff --git a/games/lemm/graphics/level2_actual.png b/games/lemm/graphics/level2_actual.png new file mode 100644 index 0000000000000000000000000000000000000000..7e6df58f1f28bd2c63b612d6e4851bcf74a49709 GIT binary patch literal 4729 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 Hu0mjfQaj}o literal 0 HcmV?d00001 diff --git a/games/lemm/hardware.inc b/games/lemm/hardware.inc new file mode 100644 index 00000000..905b8dd8 --- /dev/null +++ b/games/lemm/hardware.inc @@ -0,0 +1,98 @@ +;; HARDWARE LOCATIONS + +KEYPRESS = $C000 +KEYRESET = $C010 + +;; SOFT SWITCHES +CLR80COL = $C000 ; PAGE0/PAGE1 normal +SET80COL = $C001 ; PAGE0/PAGE1 switches PAGE0 in Aux instead +EIGHTYCOLOFF = $C00C +EIGHTYCOLON = $C00D +TBCOLOR = $C022 ; IIgs text foreground / background colors +NEWVIDEO = $C029 ; IIgs graphics modes +SPEAKER = $C030 +CLOCKCTL = $C034 ; bits 0-3 are IIgs border color +SET_GR = $C050 +SET_TEXT = $C051 +FULLGR = $C052 +TEXTGR = $C053 +PAGE0 = $C054 +PAGE1 = $C055 +LORES = $C056 ; Enable LORES graphics +HIRES = $C057 ; Enable HIRES graphics +AN3 = $C05E ; Annunciator 3 + + + + + + + +PADDLE_BUTTON0 = $C061 +PADDL0 = $C064 +PTRIG = $C070 + +;; BASIC ROUTINES + +NORMAL = $F273 + +;; 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 +BELL = $FBDD ;; ring the bell +BASCALC = $FBC1 ;; +VTAB = $FC22 ;; VTAB to CV +HOME = $FC58 ;; Clear the text screen +WAIT = $FCA8 ;; delay 1/2(26+27A+5A^2) us +CROUT1 = $FD8B +SETINV = $FE80 ;; INVERSE +SETNORM = $FE84 ;; NORMAL +COUT = $FDED ;; output A to screen +COUT1 = $FDF0 ;; output A to screen + + + + + + + +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/games/lemm/hello.bas b/games/lemm/hello.bas new file mode 100644 index 00000000..6ce4ca9f --- /dev/null +++ b/games/lemm/hello.bas @@ -0,0 +1,15 @@ +5 HOME +10 PRINT "LOADING LEMM V0.01" +20 PRINT " LEMM PROOF-OF-CONCEPT DEMAKE" +30 PRINT:PRINT +70 PRINT "BASED ON LEMMINGS BY DMA DESIGN" +75 PRINT:PRINT +80 PRINT "APPLE II PORT: VINCE WEAVER" +90 PRINT "DISK CODE : QKUMBA" +94 PRINT "LZSA CODE : EMMANUEL MARTY" +95 PRINT +100 PRINT " ______" +110 PRINT " A \/\/\/ SOFTWARE PRODUCTION" +115 PRINT +120 PRINT " HTTP://WWW.DEATER.NET/WEAVE/VMWPROD" +130 PRINT CHR$(4)"CATALOG" diff --git a/games/lemm/lemm_test.s b/games/lemm/lemm_test.s new file mode 100644 index 00000000..658868b5 --- /dev/null +++ b/games/lemm/lemm_test.s @@ -0,0 +1,233 @@ +; Lemm Test + +; by deater (Vince Weaver) + +; Zero Page + .include "zp.inc" + .include "hardware.inc" + +lemm_test_start: + ;=================== + ; init screen + ;=================== + + jsr TEXT + jsr HOME + bit KEYRESET + + bit SET_GR + bit PAGE0 + bit HIRES + bit FULLGR + + ;=================== + ; machine workarounds + ;=================== + ; mostly IIgs + ;=================== + ; thanks to 4am who provided this code from Total Replay + + lda ROM_MACHINEID + cmp #$06 + bne not_a_iigs + sec + jsr $FE1F ; check for IIgs + bcs not_a_iigs + + ; gr/text page2 handling broken on early IIgs models + ; this enables the workaround + + jsr ROM_TEXT2COPY ; set alternate display mode on IIgs + cli ; enable VBL interrupts + + ; also 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 + +not_a_iigs: + + ;=================== + ; Load hires graphics + ;=================== +load_graphics_loop: + + lda #level1_lzsa + sta getsrc_smc+2 ; LZSA_SRC_HI + + lda #$20 + + jsr decompress_lzsa2_fast + + jsr wait_until_keypress + + lda #level2_lzsa + sta getsrc_smc+2 ; LZSA_SRC_HI + + lda #$20 + + jsr decompress_lzsa2_fast + + jsr wait_until_keypress + + lda #level2_actual_lzsa + sta getsrc_smc+2 ; LZSA_SRC_HI + + lda #$20 + + jsr decompress_lzsa2_fast + + jsr wait_until_keypress + + + + 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: + + + + + ;========================== + ; includes + ;========================== + +; .include "gr_pageflip.s" +; .include "gr_copy.s" +; .include "wait_a_bit.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 "init_vars.s" +; .include "graphics_title/title_graphics.inc" +; .include "lc_detect.s" + + + ; pt3 player +; .include "pt3_lib_core.s" +; .include "pt3_lib_init.s" +; .include "interrupt_handler.s" +; .include "pt3_lib_mockingboard_detect.s" +; .include "pt3_lib_mockingboard_setup.s" + +new_title: +.include "graphics/graphics_test.inc" + + + + + + + + + + +;PT3_LOC = theme_music + +;.align $100 +;theme_music: +;.incbin "audio/theme.pt3" + diff --git a/games/lemm/music/Lemmings5.ym b/games/lemm/music/Lemmings5.ym new file mode 100644 index 0000000000000000000000000000000000000000..eb177a111d681a0a3057729fe7ac900d41c705e4 GIT binary patch literal 1053 zcmV+&1mgQ24lQhGH7)xC002gi004=VO(q}!30X~FFfcYQSxuf00;Ott_qw#_``+_y zYPDjff!KU$dYCf;n8kR|HN$}gG+Z>p0^LQrj)8{2-3bnW#$aHzSSrZ4L}0cv4#G_Z z1q}rhT2vR__xpcR?`wZJD&N?!sCO853?>g?)&)7TIu@LU4GTol_6_g@d;{MA_rNS2 z2ckdRgXtrY_PwshNmq|)c#*>aOIOJC#AKZB;5} zx-z2%Y75<~YYH4VP@$DGvC)I`==Ya*ZysHrN0o^x_#3x{Ozd*K%#R;4GC5O;(AVor zQn4jp10OJ{n+M?7@$E3K?d{#Ww~LpT7A{#+HPXsfB&*%g*4EtSR&!rEG}y^<>C=}kikVA__1!rtb5ue2gZhwvt*NQ3%E6=e z@7C6pDuScH){g=GKLuZlBO}E@QT?Af=I7_;vb%dbJ8Y@&d0w|1wWIy-n7n$;82JZ- zdN6)IlJxIAex5V}>|m{aE&H99dad*w|PK0)U_>3Ic$jD1Y=nd^;A(FY5(0 z3pK9XSxx+!=)y35_Bro==051+Sc|2*)QZe|OEW@KcJ=k`n>CwaROwisD@l%j&l?|$ zN403uxo>lA4{?_NSNu4C*vIx*@9+u#E9I8Ut(b3KApAl7NI&#)PY3d|mg$X1xmmMX z$KAF4x;+wQPE%_^_yPR`=6*hB|LYt<_=E8W;t%;?{ulrN000Ltm;e9(00%L+00000 X0n8)-000000000001jjIe|o3@k%|Lp literal 0 HcmV?d00001 diff --git a/games/lemm/music/Makefile b/games/lemm/music/Makefile new file mode 100644 index 00000000..bd18bd43 --- /dev/null +++ b/games/lemm/music/Makefile @@ -0,0 +1,72 @@ +LZSA = ~/research/lzsa/lzsa/lzsa +YM5_TO_RAW = ~/research/vmw-meter.git/ay-3-8910/conversion_tools/ym5_to_raw + + + + +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 + +Lemmings5.raw: Lemmings5.ym + $(YM5_TO_RAW) ./Lemmings5.ym > Lemmings5.raw + + +lemmings5.part1: Lemmings5.raw + dd conv=notrunc if=Lemmings5.raw of=lemmings5.part1 bs=4096 count=1 seek=0 skip=0 + +lemmings5.part1.lzsa: lemmings5.part1 + $(LZSA) -r -f2 lemmings5.part1 lemmings5.part1.lzsa + + +lemmings5.part2: Lemmings5.raw + dd conv=notrunc if=Lemmings5.raw of=lemmings5.part2 bs=4096 count=1 seek=1 skip=1 + +lemmings5.part2.lzsa: lemmings5.part2 + $(LZSA) -r -f2 lemmings5.part2 lemmings5.part2.lzsa + + +lemmings5.part3: Lemmings5.raw + dd conv=notrunc if=Lemmings5.raw of=lemmings5.part3 bs=4096 count=1 seek=2 skip=2 + +lemmings5.part3.lzsa: lemmings5.part3 + $(LZSA) -r -f2 lemmings5.part3 lemmings5.part3.lzsa + + +lemmings5.part4: Lemmings5.raw + dd conv=notrunc if=Lemmings5.raw of=lemmings5.part4 bs=4096 count=1 seek=3 skip=3 + +lemmings5.part4.lzsa: lemmings5.part4 + $(LZSA) -r -f2 lemmings5.part4 lemmings5.part4.lzsa + + +lemmings5.part5: Lemmings5.raw + dd conv=notrunc if=Lemmings5.raw of=lemmings5.part5 bs=4096 count=1 seek=4 skip=4 + +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 + + +clean: + rm -f *.lzsa *.part1 *.part2 *.part3 *.part4 *.part5 *.part6 *.part7 *.part8 diff --git a/games/lemm/wait_keypress.s b/games/lemm/wait_keypress.s new file mode 100644 index 00000000..444d2074 --- /dev/null +++ b/games/lemm/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/games/lemm/zp.inc b/games/lemm/zp.inc new file mode 100644 index 00000000..79c4e7db --- /dev/null +++ b/games/lemm/zp.inc @@ -0,0 +1,200 @@ +;; Zero Page + +;; LZSA addresses +NIBCOUNT = $00 + +;; Zero page monitor routines addresses + +WNDLFT = $20 +WNDWDTH = $21 +WNDTOP = $22 +WNDBTM = $23 +CH = $24 +CV = $25 +GBASL = $26 +GBASH = $27 +BASL = $28 +BASH = $29 +H2 = $2C +X_LEFT = $2C +V2 = $2D +MASK = $2E +COLOR_MASK = $2F +COLOR = $30 + +SEEDL = $4e +SEEDH = $4f +XMAX = $50 + + + +; MIST zero page addresses + +FRAMEL = $60 +FRAMEH = $61 +;CURSOR_X = $62 +;CURSOR_Y = $63 +XPOS = $64 +YPOS = $65 +LOCATION_STRUCT_L = $66 +LOCATION_STRUCT_H = $67 +IN_SPECIAL = $68 +CURSOR_VISIBLE = $69 +IN_LEFT = $6A +IN_RIGHT = $6B +BTC_L = $6C +BTC_H = $6D + +; pt3 player registers +REGISTER_DUMP = $70 +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 +COPY_OFFSET = $7E +DECODER_STATE = $7F + +PATTERN_L = $7E +PATTERN_H = $7F + + +; note 70-7f also used by disk code + +; note: rest are up at $f0 + +; We have to save/restore the following values +; when loading/storing from disk + +WHICH_LOAD = $80 ; which file to load + +DUKE_XL = $81 +DUKE_X = $82 ; location of protagonist +DUKE_Y = $83 +DUKE_DIRECTION = $84 +DUKE_WALKING = $85 +DUKE_JUMPING = $86 + +LASER_OUT = $87 +LASER_X = $88 +LASER_Y = $89 +LASER_DIRECTION = $8A +TILEMAP_X = $8B +TILEMAP_Y = $8C + +DUKE_FOOT_OFFSET = $8D + +FIREPOWER = $8E +INVENTORY = $8F + INV_RED_KEY = $80 + INV_BLUE_KEY = $20 + INV_SHOE = $08 + INV_GRIP = $02 +HEALTH = $90 +SCORE0 = $91 +SCORE1 = $92 +SCORE2 = $93 +UPDATE_STATUS = $94 + +DUKE_FALLING = $95 +DUKE_SHOOTING = $96 +KICK_UP_DUST = $97 +SUPPRESS_WALK = $98 +ENEMY_DATAL = $99 +ENEMY_DATAH = $9A +DOOR_ACTIVATED = $9B +LASER_TILE = $9C +TILE_TEMP = $9D + +; done game puzzle state + + +WHICH_SLOT = $DA +JS_BUTTON_STATE = $DB +CURRENT_DISK = $DC +JOYSTICK_ENABLED= $DD +SOUND_STATUS = $DE + SOUND_DISABLED = $80 + SOUND_IN_LC = $01 ; $01 sound effects in language card + SOUND_MOCKINGBOARD = $02 ; mockingboard detected + +GRID_PAGE = $DF +ANIMATE_FRAME = $E0 +LEVEL_OVER = $E1 + GAME_OVER = $FF + NEXT_LEVEL = $01 +LOCATIONS_L = $E2 +LOCATIONS_H = $E3 + +; temp var per-world define +LONG_FRAME = $E4 ; nibel +CURRENT_DISPLAY = $E4 ; selena +LAST_PLAYED = $E4 ; selena + +DISP_PAGE = $ED ; ALL +DRAW_PAGE = $EE ; ALL + +; rest of pt3_player +MB_DETECTED = $EF +WHICH_CHUNK = $F0 +MB_CHUNK_OFFSET = $F1 +LOOP = $F4 +MB_VALUE = $F5 +MB_ADDR_L = $F6 +MB_ADDR_H = $F7 +DONE_PLAYING = $F8 +DONE_SONG = $F9 + +; rest of pt3_player +PT3_TEMP = $EF +ORNAMENT_L = $F0 +ORNAMENT_H = $F1 +SAMPLE_L = $F2 +SAMPLE_H = $F3 + + + + + + +TEMP = $FA +TEMPY = $FB +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 assemble with ca65 -- 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 = $70 + ldsizeh = $71 + namlo = $7b + namhi = $7c + step = $7d ; state for stepper motor + tmptrk = $7e ; temporary copy of current track + phase = $7f ; current phase for /seek +