Compare commits

...

5 Commits

Author SHA1 Message Date
giomba f81f087a13 fixed plain .prg version
actually it takes a "lot" of space more, but it is not a problem and it works,
anyhow a future version with a "fast" loader is required
2020-11-14 19:12:10 +01:00
giomba 55e0a550ea fixed font name 2020-11-14 17:40:30 +01:00
giomba 999844460a ported lzgmini_6502 for dasm, and embedded in this game's code 2020-10-04 20:58:08 +02:00
giomba bb17b78251 updated LICENSE 2020-10-04 17:59:30 +02:00
giomba fa2269afb8 compression routine for cartridge version, temporarily deprecates tape version 2020-10-04 17:51:35 +02:00
12 changed files with 387 additions and 112 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "liblzg"]
path = liblzg
url = https://github.com/mbitsnbites/liblzg.git

View File

@ -1 +0,0 @@
https://www.gnu.org/licenses/gpl-3.0.txt

6
LICENSE.md Normal file
View File

@ -0,0 +1,6 @@
# License
* [snake6502](https://git.giomba.it/giomba/snake6502) has been written by giomba and is released under the terms of the [GNU General Public License 3.0](https://www.gnu.org/licenses/gpl-3.0.txt)
* [liblzg](https://liblzg.bitsnbites.eu/) has been written by Marcus Geelnard and is released under the terms of the [zlib license](https://opensource.org/licenses/Zlib)

View File

@ -1,18 +1,31 @@
.POSIX:
ASM=$(wildcard src/*.asm)
RES=res.bin/amour.sid res.bin/levels.bin
ifeq "$(CARTRIDGE)" "1"
FORMAT:=3
else
FORMAT:=1
endif
RES=res.bin/amour.sid res.bin/levels.bin res.bin/unlzg.bin
.PHONY: debug env clean
bin/snake.prg: env $(ASM) $(RES) bin/explodefont
dasm src/main.asm -Isrc/ -DSYSTEM=64 -DDEBUG=$(DEBUG) -DVERBOSE=$(VERBOSE) -DCARTRIDGE=$(CARTRIDGE) -f$(FORMAT) -sbuild/symbols.txt -obin/snake.prg
bin/snake.bin: bin/snake.pack.lz
dasm src/cart.asm -Isrc/ -DVERBOSE=$(VERBOSE) -f3 -sbuild/cart.symbols.txt -obin/snake.bin
#bin/tape.prg: bin/snake.pack.lz.file
# dasm src/tape.asm -Isrc/ -DVERBOSE=$(VERBOSE) -f1 -sbuild/tape.sybols.txt -obin/tape.prg
#bin/snake.pack.lz.file: bin/snake.pack.lz
# echo -e "\x00\x80" > bin/snake.pack.lz.file
# cat bin/snake.pack.lz >> bin/snake.pack.lz.file
bin/snake.prg: bin/snake.pack
dasm src/prg.asm -Isrc/ -DVERBOSE=$(VERBOSE) -f1 -sbuild/prg.symbols.txt -obin/snake.prg
bin/snake.pack: env $(ASM) $(RES) bin/explodefont
dasm src/main.asm -Isrc/ -DSYSTEM=64 -DDEBUG=$(DEBUG) -DVERBOSE=$(VERBOSE) -DCARTRIDGE=$(CARTRIDGE) -f3 -sbuild/pack.symbols.txt -obin/snake.pack
bin/snake.pack.lz: bin/snake.pack liblzg/src/tools/lzg
liblzg/src/tools/lzg bin/snake.pack > bin/snake.pack.lz
liblzg/src/tools/lzg:
cd liblzg/src && make
clean:
rm -rf {build,bin,res.bin}
@ -29,6 +42,9 @@ res.bin/amour.sid:
res.bin/levels.bin: bin/level res.org/levels.txt
bin/level < res.org/levels.txt > res.bin/levels.bin
res.bin/unlzg.bin:
cp res.org/unlzg.bin res.bin/unlzg.bin
bin/level: util/rlevel.cpp
g++ -o bin/level util/rlevel.cpp

View File

@ -13,25 +13,37 @@ You need the GNU compiler collection and the [dasm](https://dasm-assembler.githu
```
$ make
```
Interesting targets:
* ```make bin/snake.bin``` produces .bin, ready to be burnt on an 8K EEPROM for making a cartridge (default)
* ```make bin/snake.prg``` produces .prg for the emulator, ready to be used on tape/disk
* ```make tape/disk``` (fastloader, to be done)
You can also define the following environment variables:
```$ DEBUG=1 make``` build with debugging artifacts
```$ VERBOSE=1 make``` output useful info during compilation
```$ CARTRIDGE=1 make``` produces an 8K bin ready to be burnt to an *PROM
## Developer docs
### Package
The whole program is assembled into a ```snake.pack``` binary blob with the following structure.
Absolute | Offset | Description
------------|-------------|------------
```$1000``` | ```$0000``` | load address
```$2800``` | ```$1800``` | entry point (start address)
### Memory map
Address | PRG | Description
----------------------|-------|------------
```$0000 - $0001``` | no | hardware
```$0002 - $00FF``` | no | zero page pointers
```$0100 - $07FF``` | no | *free ram*
```$0800 - $0FFF``` | yes | autostart (BASIC or cartridge) + Low Program Segment
```$1000 - $1FFF``` | yes | SID tune + Middle Program Segment
```$0100 - $01FF``` | no | stack page
```$0200 - $07FF``` | no | *free ram*
```$1000 - $1FFF``` | yes | SID tune
```$2000 - $27FF``` | yes | custom char
```$2800 - $xxxx``` | yes | High Program Segment (only needed part used)
```$2800 - $xxxx``` | yes | Program segment (only needed part used)
```$xxxx - $CCFF``` | no | *free ram*
```$CD00 - $CDFF``` | no | data segment (not-initialized vars)
```$CE00 - $CEFF``` | no | list X
@ -39,9 +51,14 @@ Address | PRG | Description
```$D000 - $DFFF``` | no | I/O
```$E000 - $FFFF``` | no | Kernal
Note: program (code) segments have been put in all possible free spots in order to squeeze the game into an 8K cartridge.
### Compression
```snake.pack``` is compressed into ```snake.pack.lz``` using [liblzg](https://github.com/mbitsnbites/liblzg), to save space in order to fit the game in a *PROM.
### Custom charset
### Decompression
```cart.asm``` is located at ```$8000``` (standard org address for C64 cartridges), and contains the decompression routine and the ```snake.pack.lz```. It decompresses ```snake.pack.lz``` back to ```$1000```, and jumps to its entry point at ```$2800```.
### Miscellanea
#### Custom charset
Index | Description
----------------|-------------
```$00 - $1F``` | A-Z (space first)
@ -50,8 +67,3 @@ Index | Description
```$50 - $5F``` | hex digits, reversed
```$60 - ``` | game tiles
### Cartridge
Cartridge version is at $8000 and simply copies itself back at $800.
Cartridge version can not be built with DEBUG=1 flag due to size constraints.

1
liblzg Submodule

@ -0,0 +1 @@
Subproject commit 182b56cb36843720f38eff2ec30db1deac4e85bd

BIN
res.org/unlzg.bin Normal file

Binary file not shown.

View File

@ -1,11 +1,15 @@
#if VERBOSE = 1
LASTINIT SET .
#endif
processor 6502
SEG.U zeropageSegment
org $02
INCLUDE "zeropage.asm"
SEG cartridgeSegment
org $8000
cartridge SUBROUTINE
WORD #$8009
WORD #$801a
WORD .coldstart
WORD .warmstart
; CBM80 in PETSCII (cartridge signature for autostart)
BYTE #$c3,#$c2,#$cd,#$38,#$30
@ -20,28 +24,35 @@ cartridge SUBROUTINE
cli
.warmstart:
; Copy cartridge content into proper memory location
ldx #$20
lda #$0
tay
; address of input compressed data
lda #<.lzpack
sta srcPointer
sta dstPointer
lda #>cartridgeStart
lda #>.lzpack
sta srcPointer + 1
lda #$08
sta dstPointer + 1
.loop:
lda (srcPointer),y
sta (dstPointer),y
iny
bne .loop
inc srcPointer + 1
inc dstPointer + 1
dex
bne .loop
jmp start
; address of output decompressed data
lda #$00
sta dstPointer
lda #$10
sta dstPointer + 1
jsr inflate
; jump to program entry
jmp $2800
; compressed pack
.lzpack:
INCBIN "bin/snake.pack.lz"
; decompression util
INCLUDE "lzgmini.asm"
#if VERBOSE = 1
ECHO "cart.asm @ ",LASTINIT,"len:",(. - LASTINIT)
#endif
ECHO "8k CARTRIDGE SIZE:",(. - $8000),"=",[(. - $8000)d]
ECHO "SPACE LEFT:",($9fff - .),"=",[($9fff - .)d]
#endif
; force filler for the *PROM
. = $9fff
BYTE #$ff

View File

@ -2564,5 +2564,5 @@ LASTINIT SET .
BYTE #%00000000
#if VERBOSE = 1
ECHO "tggs.asm @ ",LASTINIT,"len:",(. - LASTINIT)
#endif
ECHO "font.asm @ ",LASTINIT,"len:",(. - LASTINIT)
#endif

242
src/lzgmini.asm Normal file
View File

@ -0,0 +1,242 @@
inflate SUBROUTINE
.inEnd EQU 2
.offset EQU 4
.length EQU 6
.symbol EQU 25
.marker1 EQU 30
.marker2 EQU 31
.marker3 EQU 32
.marker4 EQU 33
.copy EQU 34
clc
ldy #10
lda (srcPointer),y
adc srcPointer
sta .inEnd
dey
lda (srcPointer),y
adc srcPointer + 1
sta .inEnd + 1
clc
lda .inEnd
adc #16
sta .inEnd
lda .inEnd + 1
adc #0
sta .inEnd + 1
; Get the marker symbols
ldy #16
lda (srcPointer),y
sta .marker1
iny
lda (srcPointer),y
sta .marker2
iny
lda (srcPointer),y
sta .marker3
iny
lda (srcPointer),y
sta .marker4
; Skip header + marker symbols (16 + 4 bytes)
clc
lda srcPointer
adc #20
sta srcPointer
lda srcPointer + 1
adc #0
sta srcPointer + 1
; Main decompression loop
ldy #0 ; Make sure that Y is zero
.mainloop:
lda srcPointer ; done?
cmp .inEnd
bne .notdone
lda srcPointer + 1
cmp .inEnd + 1
bne .notdone
rts
.notdone:
lda (srcPointer),y ; A = symbol
sta .symbol
sta $d020
inc srcPointer
bne .noinc1
inc srcPointer + 1
.noinc1:
cmp .marker1 ; Marker1?
beq .domarker1
cmp .marker2 ; Marker2?
beq .domarker2
cmp .marker3 ; Marker3?
beq .domarker3
cmp .marker4 ; Marker4?
beq .domarker4
.literal:
lda .symbol
sta (dstPointer),y ; Plain copy
inc dstPointer
bne .mainloop
inc dstPointer + 1
bne .mainloop
.domarker1:
jmp .domarker1b
; marker4 - "Near copy (incl. RLE)"
.domarker4:
lda (srcPointer),y
inc srcPointer
bne .noinc3
inc srcPointer + 1
.noinc3:
cmp #0
beq .literal ; Single occurance of the marker symbol (rare)
tax
lsr
lsr
lsr
lsr
lsr
sta .offset
inc .offset
lda #0
sta .offset + 1 ; offset = (b >> 5) + 1
txa
and #$1f
tax
lda .LZG_LENGTH_DECODE_LUT,x
sta .length ; length = .LZG_LENGTH_DECODE_LUT[b & 0x1f]
jmp .docopy
; marker3 - "Short copy"
.domarker3:
lda (srcPointer),y
inc srcPointer
bne .noinc4
inc srcPointer + 1
.noinc4:
cmp #0
beq .literal ; Single occurance of the marker symbol (rare)
tax
lsr
lsr
lsr
lsr
lsr
lsr
clc
adc #3
sta .length ; length = (b >> 6) + 3
txa
and #$3f
adc #8
sta .offset
lda #0
sta .offset + 1 ; offset = (b & 0x3f) + 8
beq .docopy
; marker2 - "Medium copy"
.domarker2:
lda (srcPointer),y
inc srcPointer
bne .noinc5
inc srcPointer + 1
.noinc5:
cmp #0
beq .literal ; Single occurance of the marker symbol (rare)
tax
lsr
lsr
lsr
lsr
lsr
sta .offset + 1
lda (srcPointer),y
inc srcPointer
bne .noinc6
inc srcPointer + 1
.noinc6:
clc
adc #8
sta .offset
bcc .noinc7
inc .offset + 1 ; offset = (((b & 0xe0) << 3) | b2) + 8
.noinc7:
txa
and #$1f
tax
lda .LZG_LENGTH_DECODE_LUT,x
sta .length ; length = .LZG_LENGTH_DECODE_LUT[b & 0x1f]
bne .docopy
.literal2:
jmp .literal
; marker1 - "Distant copy"
.domarker1b:
lda (srcPointer),y
inc srcPointer
bne .noinc8
inc srcPointer + 1
.noinc8:
cmp #0
beq .literal2 ; Single occurance of the marker symbol (rare)
and #$1f
tax
lda .LZG_LENGTH_DECODE_LUT,x
sta .length ; length = .LZG_LENGTH_DECODE_LUT[b & 0x1f]
lda (srcPointer),y
inc srcPointer
bne .noinc9
inc srcPointer + 1
.noinc9:
sta .offset + 1
lda (srcPointer),y
inc srcPointer
bne .noinc10
inc srcPointer + 1
.noinc10:
clc
adc #$08
sta .offset
lda .offset + 1
adc #$08
sta .offset + 1 ; offset = ((b2 << 8) | (*src++)) + 2056
; Copy corresponding data from history window
.docopy:
sec
lda dstPointer
sbc .offset
sta .copy
lda dstPointer + 1
sbc .offset + 1
sta .copy + 1
.loop1:
lda (.copy),y
sta (dstPointer),y
iny
cpy .length
bne .loop1
ldy #0 ; Make sure that Y is zero
clc
lda dstPointer
adc .length
sta dstPointer
bcc .noinc11
inc dstPointer + 1
.noinc11:
jmp .mainloop
; Lookup Table for decoding the copy length parameter
.LZG_LENGTH_DECODE_LUT
BYTE 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,35,48,72,128

View File

@ -20,34 +20,6 @@
ECHO "End of zeropage variables. Space left: ",($90 - .)
#endif
; Initialized segments
; ----------------------------------------------------------------------
SEG autostartSegment
#if CARTRIDGE = 0
org $801
INCLUDE "basic.asm" ; BASIC _MUST_ stay at this address
#else
org $800
INCLUDE "cart.asm"
#endif
INCLUDE "initdata.asm"
; Program "Segment" Low
; ----------------------------------------------------------------------
; You just have to fill this empty space, don't you think so? ;-)
INCLUDE "game.asm"
INCLUDE "gameover.asm"
INCLUDE "introreset.asm"
INCLUDE "program.asm"
INCLUDE "subroutines.asm"
INCLUDE "levels.asm"
INCLUDE "intro1.asm"
; Note: some code had to be included at an higher address
#if VERBOSE = 1
ECHO "End of Low Program Segment. Space left:",($1000 - .)
#endif
; SID tune (previously properly cleaned, see HVSC)
; ----------------------------------------------------------------------
SEG sidSegment
@ -55,47 +27,36 @@
sidtune:
INCBIN "../res.bin/amour.sid"
#if VERBOSE = 1
ECHO "End of SIDtune at ",.
#endif
INCLUDE "multicolor.asm"
INCLUDE "levelreset.asm"
INCLUDE "outro.asm"
#if VERBOSE = 1
ECHO "End of Middle Program Segment. Space left:",($2000 - .)
ECHO "End of SIDtune at ",.,"Space left:",($2000 - .)
#endif
; Font Data
; ----------------------------------------------------------------------
SEG tggsSegment
SEG fontSegment
org $2000
; This binary data that defines the font is exactly 2kB long ($800)
tggsFont:
INCLUDE "tggs.asm"
INCLUDE "font.asm"
; Program Segment High
; Program Segment
; ----------------------------------------------------------------------
SEG programSegment
org $2800
INCLUDE "program.asm"
INCLUDE "initdata.asm"
INCLUDE "game.asm"
INCLUDE "gameover.asm"
INCLUDE "introreset.asm"
INCLUDE "subroutines.asm"
INCLUDE "levels.asm"
INCLUDE "intro1.asm"
INCLUDE "multicolor.asm"
INCLUDE "levelreset.asm"
INCLUDE "outro.asm"
#if VERBOSE = 1
ECHO "End of High Program Segment at: ",.,"Space left:",($cd00 - .)
ECHO "End of program segment at:",.
ECHO "PACK SIZE:",(. - $1000),"=",[(. - $1000)d]
#endif
#if VERBOSE = 1
#if CARTRIDGE = 0
; +2 because of PRG header
ECHO "PRG size:",([. - $801 + 2]d),"dec"
#else
ECHO "BIN size:",([. - $800]d),"dec"
#endif
#endif
; Uninitialized segments
; ----------------------------------------------------------------------
; Cartridge locations
; -------------------
SEG.U cartridgeSegment
org $8000
cartridgeStart:
; Data variables
; -----------------
SEG.U dataSegment
@ -113,10 +74,8 @@ listX DS 256
listY DS 256
;
; coded during december 2017
; coded 2017, 2018, 2019, 2020
; by giomba -- giomba at glgprograms.it
; this software is free software and is distributed
; under the terms of GNU GPL v3 license
;
; vim: set expandtab tabstop=4 shiftwidth=4:

26
src/prg.asm Normal file
View File

@ -0,0 +1,26 @@
processor 6502
SEG.U
org $02
INCLUDE "zeropage.asm"
SEG autostart
org $801
autostartRoutine SUBROUTINE
; this is at $801
; and it MUST be exactly at this location in order to autostart
; 10 SYS2060 ($80c) BASIC autostart
BYTE #$0b,#$08,#$0a,#$00,#$9e,#$32,#$30,#$36,#$31,#$00,#$00,#$00
; this is at (2061 dec)=($80d)
; and it MUST be exactly after the above BASIC statement
. = $80d
jmp $2800
. = $1000
INCBIN "../bin/snake.pack"
#if VERBOSE = 1
ECHO "PRG SIZE:",(. - $801 + 2),"=",[(. - $801 + 2)d]
#endif