mirror of
https://github.com/UzixLS/zx-midiplayer.git
synced 2025-07-19 07:11:26 +03:00
refactor build; use rle
This commit is contained in:
23
Makefile
23
Makefile
@ -2,26 +2,21 @@ ifneq ($(wildcard .git),)
|
||||
VERSION := $(shell git describe --abbrev=6 --long --dirty --always --tags --first-parent | sed s/-/./)
|
||||
endif
|
||||
|
||||
export PATH:=/cygdrive/c/Hwdev/sjasmplus/:/cygdrive/e/Emulation/ZX Spectrum/Utils/fuse-utils/:/cygdrive/e/Emulation/ZX Spectrum/Emuls/Es.Pectrum/:${PATH}
|
||||
export PATH:=/cygdrive/c/Hwdev/sjasmplus/:/cygdrive/e/Emulation/ZX Spectrum/Emuls/Es.Pectrum/:${PATH}
|
||||
|
||||
SJOPTS = --fullpath --inc=resources/ -DVERSION=\"${VERSION}\"
|
||||
SJOPTS = --nologo --fullpath --outprefix=build/ -DVERSION=\"${VERSION}\"
|
||||
|
||||
.PHONY: all clean .FORCE
|
||||
.FORCE:
|
||||
.PHONY: all clean run
|
||||
|
||||
all: build/main.sna
|
||||
all:
|
||||
@mkdir -p build
|
||||
sjasmplus --msg=war --lst=build/main.lst --exp=build/main.exp --sld=build/main.sld ${SJOPTS} src/main.asm
|
||||
sjasmplus --msg=err --lst=build/build.lst ${SJOPTS} src/build.asm
|
||||
|
||||
clean:
|
||||
rm -rf build/ .tmp/
|
||||
|
||||
build/main.sna: src/main.asm .FORCE
|
||||
mkdir -p build
|
||||
sjasmplus --sld=build/main.sld --lst=build/main.lst --outprefix=build/ ${SJOPTS} $<
|
||||
|
||||
%.tzx: %.sna
|
||||
snap2tzx -o $@ $<
|
||||
|
||||
run: build/main.tzx
|
||||
EsPectrum $<
|
||||
run:
|
||||
EsPectrum build/main.tap
|
||||
|
||||
-include Makefile.local
|
||||
|
42
lua/incbin_pages.lua
Normal file
42
lua/incbin_pages.lua
Normal file
@ -0,0 +1,42 @@
|
||||
--[[
|
||||
Lua function providing "incbin" replacement which can arrange file into multiple pages
|
||||
for SjASMPlus (https://github.com/z00m128/sjasmplus)
|
||||
Author: Eugene Lozovoy
|
||||
|
||||
Parameters:
|
||||
1 file_name: name of file to open
|
||||
2 offset: positive value (optional)
|
||||
3 length: positive value (optional)
|
||||
4 baseaddr: destination address
|
||||
5 pages: which pages will be used
|
||||
6 pagesize: default 16Kb
|
||||
]]
|
||||
function incbin_pages(file_name, offset, length, baseaddr, pages, pagesize)
|
||||
pagesize = pagesize or 16*1024
|
||||
local f = io.open(file_name, "rb")
|
||||
if not f then
|
||||
sj.error("[incbin_pages]: cannot open file", file_name)
|
||||
return
|
||||
end
|
||||
filelength = f:seek("end")
|
||||
f:close()
|
||||
offset = offset or 0
|
||||
length = length or (filelength - offset)
|
||||
if (offset > filelength) or (length > filelength) or (offset+length > filelength) then
|
||||
sj.error("[incbin_rle]: file is too small", file_name)
|
||||
return
|
||||
end
|
||||
filepages = math.ceil(filelength / pagesize)
|
||||
if filepages > #pages then
|
||||
sj.error("[incbin_pages]: cannot fit file", file_name)
|
||||
return
|
||||
end
|
||||
local offsetend = offset + length
|
||||
for i = 1, filepages do
|
||||
local portion = math.min(pagesize, offsetend - offset)
|
||||
_pc(string.format("org 0x%x" ,baseaddr))
|
||||
_pc(string.format("page %u", pages[i]))
|
||||
_pc(string.format("incbin \"%s\",0x%x,0x%x", file_name, offset, portion))
|
||||
offset = offset + portion
|
||||
end
|
||||
end
|
134
lua/incbin_rle.lua
Normal file
134
lua/incbin_rle.lua
Normal file
@ -0,0 +1,134 @@
|
||||
--[[
|
||||
Lua function providing "incbin" replacement with RLE compression
|
||||
for SjASMPlus (https://github.com/z00m128/sjasmplus)
|
||||
Author: Eugene Lozovoy
|
||||
Original idea and Z80 code: cngsoft (https://www.cpcwiki.eu/forum/programming/realtime-rle-decoding-and-encoding/)
|
||||
|
||||
Z80 code:
|
||||
|
||||
; ENCODER: HL=^SOURCE,DE=^TARGET,IX=FULL_LENGTH; HL+=FULL_LENGTH,DE+=PAKD_LENGTH,IX=0,B=0,ACF!
|
||||
rle2pack_init ld b,0
|
||||
rle2pack_loop ld c,(hl)
|
||||
rle2pack_find ld a,xh
|
||||
or xl
|
||||
jr z,rle2pack_exit
|
||||
dec ix
|
||||
inc hl
|
||||
inc b
|
||||
jr z,rle2pack_over
|
||||
ld a,(hl)
|
||||
cp c
|
||||
jr z,rle2pack_find
|
||||
rle2pack_over call rle2pack_fill
|
||||
jr rle2pack_loop
|
||||
rle2pack_exit cp b
|
||||
call nz,rle2pack_fill
|
||||
; generate the end marker from the last byte!
|
||||
dec hl
|
||||
ld a,(hl)
|
||||
inc hl
|
||||
cpl
|
||||
jr rle2pack_exit_
|
||||
rle2pack_fill dec b
|
||||
ld a,c
|
||||
jr z,rle2pack_fill_
|
||||
rle2pack_exit_ ld (de),a
|
||||
inc de
|
||||
ld (de),a
|
||||
inc de
|
||||
dec b
|
||||
ld a,b
|
||||
rle2pack_fill_ ld (de),a
|
||||
inc de
|
||||
ld b,0
|
||||
ret
|
||||
|
||||
; DECODER: HL=^SOURCE,DE=^TARGET; HL+=PAKD_LENGTH,DE+=FULL_LENGTH,B!,AF!
|
||||
rle2upak_init ld b,1
|
||||
ld a,(hl)
|
||||
inc hl
|
||||
cp (hl)
|
||||
jr nz,rle2upak_fill
|
||||
inc hl
|
||||
ld b,(hl)
|
||||
inc hl
|
||||
inc b
|
||||
ret z
|
||||
inc b
|
||||
rle2upak_fill ld (de),a
|
||||
inc de
|
||||
djnz $-2
|
||||
jr rle2upak_init
|
||||
|
||||
The encoder is 49 bytes long and does almost 32 kB/s on average; the decoder fits in 19 bytes and runs twice as fast.
|
||||
Both support zero-length blocks thanks to the end marker, that also means that the decoder doesn't need to know the
|
||||
stream's length (packed or not) in advance.
|
||||
By turning all INC HL, DEC HL and INC DE into DEC HL, INC HL and DEC DE streams will be encoded and decoded in reverse.
|
||||
|
||||
The format itself is as follows: single bytes are encoded as themselves, double bytes as themselves plus a $00,
|
||||
and strings of three or more identical bytes (up to 256) become the first two bytes plus the length minus 2;
|
||||
the end-of-stream marker is a couple of identical bytes plus a $FF, and avoids clashing with previous single bytes
|
||||
(i.e. XX, XX XX $FF won't be misread as XX XX XX, $FF).
|
||||
|
||||
Best-case compression (all memory is made of strings of 256 identical bytes) is 3*length/256+3;
|
||||
worst-case compression (all memory is made of different couples of identical bytes, i.e. XX XX YY YY XX XX YY YY...) is 3*length/2+3.
|
||||
|
||||
Parameters:
|
||||
1 file_name: name of file to open
|
||||
2 offset: positive value (optional)
|
||||
3 length: positive value (optional)
|
||||
]]
|
||||
function incbin_rle(file_name, offset, length)
|
||||
local f = io.open(file_name, "rb")
|
||||
if not f then
|
||||
sj.error("[incbin_rle]: cannot open file", file_name)
|
||||
return
|
||||
end
|
||||
offset = offset or 0
|
||||
filelength = f:seek("end")
|
||||
length = (length or filelength-offset)
|
||||
if (offset > filelength) or (length > filelength) or (offset+length > filelength) then
|
||||
sj.error("[incbin_rle]: file is too small", file_name)
|
||||
return
|
||||
end
|
||||
|
||||
_pl(";; incbin_rle ;; file \"" .. file_name .. "\", offset \"" .. offset .. "\", length " .. length)
|
||||
f:seek("set", offset)
|
||||
local compressed = 0
|
||||
local prevbyte = nil
|
||||
local repeat_len = 0
|
||||
for i = 1, length do
|
||||
local char = f:read(1)
|
||||
local byte = string.byte(char)
|
||||
if byte == prevbyte and repeat_len == 0 then
|
||||
sj.add_byte(byte)
|
||||
compressed = compressed + 1
|
||||
repeat_len = 1
|
||||
elseif byte == prevbyte and repeat_len < 255 then
|
||||
repeat_len = repeat_len + 1
|
||||
elseif repeat_len > 0 then
|
||||
sj.add_byte(repeat_len-1)
|
||||
sj.add_byte(byte)
|
||||
compressed = compressed + 2
|
||||
repeat_len = 0
|
||||
else
|
||||
sj.add_byte(byte)
|
||||
compressed = compressed + 1
|
||||
end
|
||||
prevbyte = byte
|
||||
end
|
||||
if repeat_len > 0 then
|
||||
sj.add_byte(repeat_len-1)
|
||||
compressed = compressed + 1
|
||||
end
|
||||
sj.add_byte(255)
|
||||
sj.add_byte(255)
|
||||
sj.add_byte(255)
|
||||
compressed = compressed + 3
|
||||
f:close()
|
||||
_pl(";; incbin_rle ;; end of file \"" .. file_name .. "\"")
|
||||
-- if _c("__PASS__") == 3 then
|
||||
-- io.write(string.format("include data (rle): name=%s (%u bytes) Offset=%u Len=%u CompressedLen=%u\n",
|
||||
-- file_name, filelength, offset, length, compressed))
|
||||
-- end
|
||||
end
|
3
make.bat
3
make.bat
@ -3,5 +3,6 @@
|
||||
@timeout 5
|
||||
|
||||
@PATH=C:\Hwdev\sjasmplus\;%PATH%
|
||||
sjasmplus --outprefix=build/ --inc=resources/ src/main.asm
|
||||
sjasmplus --outprefix=build/ --exp=build/main.exp src/main.asm
|
||||
sjasmplus --outprefix=build/ src/build.asm
|
||||
@pause
|
||||
|
116
src/build.asm
Normal file
116
src/build.asm
Normal file
@ -0,0 +1,116 @@
|
||||
ASSERT __SJASMPLUS__ >= 0x011401 ; SjASMPlus 1.20.1
|
||||
DEVICE ZXSPECTRUM128
|
||||
OPT --syntax=abf
|
||||
|
||||
include "build/main.exp"
|
||||
includelua "lua/incbin_pages.lua"
|
||||
includelua "lua/incbin_rle.lua"
|
||||
|
||||
|
||||
; === SNA file ===
|
||||
lua allpass
|
||||
incbin_pages("res/play.scr", 0, nil, 0x4000, {0})
|
||||
incbin_pages("res/files.scr", 0, nil, 0xC000, {7})
|
||||
incbin_pages("build/main.bin", 0, nil, _c("begin"), {0})
|
||||
incbin_pages("res/test0.mid", 0, nil, 0xC000, {0,4,6,3})
|
||||
endlua
|
||||
page 0 : savesna "main.sna", main
|
||||
|
||||
|
||||
; === TAP file ===
|
||||
emptytap "main.tap"
|
||||
page 0 : savetap "main.tap", main
|
||||
|
||||
|
||||
; === TRD file ===
|
||||
org #5d3b
|
||||
boot_b:
|
||||
dw #0100, .end-$-4, #30fd,#000e,#b300,#005f,#f93a,#30c0,#000e,#5300,#005d,#ea3a
|
||||
.enter:
|
||||
di ;
|
||||
|
||||
ld hl, #8000 ;
|
||||
ld b, screen_sectors ;
|
||||
call .sub_load ;
|
||||
ld hl, #8000 ;
|
||||
ld de, #4000 ; screen1
|
||||
call .sub_unpack ;
|
||||
ld a, #17 ; screen2
|
||||
ld bc, #7ffd ;
|
||||
out (c), a ;
|
||||
ld de, #c000 ;
|
||||
call .sub_unpack ;
|
||||
|
||||
ld a, #10 ; code
|
||||
ld bc, #7ffd ;
|
||||
out (c), a ;
|
||||
ld hl, #c000 ;
|
||||
ld b, code_sectors ;
|
||||
call .sub_load ;
|
||||
ld hl, #c000 ;
|
||||
ld de, begin ;
|
||||
call .sub_unpack ;
|
||||
|
||||
ld hl, #c000 ;
|
||||
ld b, testmid_sectors ;
|
||||
call .sub_load ;
|
||||
|
||||
jp main ;
|
||||
|
||||
; IN - HL - destination address
|
||||
; IN - B - sectors count
|
||||
.sub_load:
|
||||
ld de, (#5cf4) ;
|
||||
ld c, #05 ;
|
||||
jp #3d13 ;
|
||||
|
||||
; IN - DE - destination
|
||||
; IN - HL - source
|
||||
; OUT - DE - pointer to next untouched byte at dest
|
||||
; OUT - HL - pointer to next byte after unpacked block
|
||||
.sub_unpack:
|
||||
ld b, 1 ;
|
||||
ld a, (hl) ;
|
||||
inc hl ;
|
||||
cp (hl) ;
|
||||
jr nz, .fill ;
|
||||
inc hl ;
|
||||
ld b, (hl) ;
|
||||
inc hl ;
|
||||
inc b ;
|
||||
ret z ;
|
||||
inc b ;
|
||||
.fill:
|
||||
ld (de), a ;
|
||||
inc de ;
|
||||
djnz .fill ;
|
||||
jp .sub_unpack ;
|
||||
|
||||
db #0d
|
||||
.end:
|
||||
|
||||
page 0
|
||||
emptytrd "main.trd", "ZXMIDI"
|
||||
savetrd "main.trd", "boot.B", boot_b, boot_b.end-boot_b
|
||||
|
||||
org 0
|
||||
lua allpass
|
||||
incbin_rle("res/play.scr")
|
||||
incbin_rle("res/files.scr")
|
||||
sj.insert_label("screen_sectors", math.ceil(sj.current_address/256))
|
||||
endlua
|
||||
savetrd "main.trd", &"boot.B", 0, $
|
||||
|
||||
org 0
|
||||
lua allpass
|
||||
incbin_rle("build/main.bin")
|
||||
sj.insert_label("code_sectors", math.ceil(sj.current_address/256))
|
||||
endlua
|
||||
savetrd "main.trd", &"boot.B", 0, $
|
||||
|
||||
org 0
|
||||
incbin "res/test1.mid"
|
||||
lua allpass
|
||||
sj.insert_label("testmid_sectors", math.ceil(sj.current_address/256))
|
||||
endlua
|
||||
savetrd "main.trd", &"boot.B", 0, $
|
76
src/main.asm
76
src/main.asm
@ -1,6 +1,6 @@
|
||||
ASSERT __SJASMPLUS__ >= 0x011401 ; SjASMPlus 1.20.1
|
||||
DEVICE ZXSPECTRUM128,stack_top
|
||||
OPT --syntax=F
|
||||
OPT --syntax=abf
|
||||
SLDOPT COMMENT WPMEM, LOGPOINT, ASSERTION
|
||||
|
||||
page 0
|
||||
@ -52,7 +52,7 @@ main:
|
||||
ld ix, string_title
|
||||
call print_string0
|
||||
|
||||
ld ix, testmid
|
||||
ld ix, #c000
|
||||
call smf_parse
|
||||
jr nz, loop
|
||||
call player_loop
|
||||
@ -84,8 +84,7 @@ builddate:
|
||||
db __DATE__, " ", __TIME__, 0
|
||||
db "Code end",0
|
||||
end:
|
||||
display "Program start: ",main
|
||||
display "Program end: ",$
|
||||
display "Code entrypoint=", main, " start=", begin, " end=",end, " len=", /d, end-begin
|
||||
|
||||
assert $ < stack_bottom
|
||||
org #BF00
|
||||
@ -94,68 +93,7 @@ stack_bottom:
|
||||
stack_top:
|
||||
|
||||
|
||||
|
||||
; === SNA file ===
|
||||
org #4000 : incbin "play.scr"
|
||||
org #C000,7 : incbin "files.scr"
|
||||
|
||||
org #C000,0
|
||||
testmid:
|
||||
; incbin "test0.mid",0 ; <= 16 Kb
|
||||
; incbin "test0.mid",0,#4000 : org #C000,4 : incbin "test0.mid",#4000 ; <= 32 Kb
|
||||
; incbin "test0.mid",0,#4000 : org #C000,4 : incbin "test0.mid",#4000,#4000 : org #C000,6 : incbin "test0.mid",#8000 ; <= 48 Kb
|
||||
incbin "test0.mid",0,#4000 : org #C000,4 : incbin "test0.mid",#4000,#4000 : org #C000,6 : incbin "test0.mid",#8000,#4000 : org #C000,3 : incbin "test0.mid",#C000 ; <= 64 Kb
|
||||
|
||||
page 0 : savesna "main.sna", main
|
||||
|
||||
|
||||
; === TRD file ===
|
||||
org #5d3b
|
||||
boot_b:
|
||||
dw #0100, .end-$-4, #30fd,#000e,#b300,#005f,#f93a,#30c0,#000e,#5300,#005d,#ea3a
|
||||
.enter:
|
||||
di
|
||||
|
||||
ld hl, #4000 ;
|
||||
ld b, 6912/256 ;
|
||||
call .sub_load ;
|
||||
|
||||
ld a, #17 ;
|
||||
ld bc, #7ffd ;
|
||||
out (c), a ;
|
||||
ld hl, #c000 ;
|
||||
ld b, 6912/256 ;
|
||||
call .sub_load ;
|
||||
|
||||
ld hl, begin ;
|
||||
ld b, (end-begin)/256+1 ;
|
||||
call .sub_load ;
|
||||
|
||||
ld a, #10 ;
|
||||
ld bc, #7ffd ;
|
||||
out (c), a ;
|
||||
ld hl, #c000 ;
|
||||
ld b, test1_mid_len/256+1 ; TODO correct len
|
||||
call .sub_load ;
|
||||
|
||||
jp main ;
|
||||
|
||||
; IN - HL - destination address
|
||||
; IN - B - sectors count
|
||||
.sub_load:
|
||||
ld de, (#5cf4) ;
|
||||
ld c, #05 ;
|
||||
jp #3d13 ;
|
||||
|
||||
db #0d
|
||||
.end:
|
||||
|
||||
emptytrd "main.trd", "ZXMIDI"
|
||||
page 0 : savetrd "main.trd", "boot.B", boot_b, boot_b.end-boot_b
|
||||
page 0 : savetrd "main.trd", &"boot.B", #4000, 6912
|
||||
page 7 : savetrd "main.trd", &"boot.B", #C000, 6912
|
||||
page 0 : savetrd "main.trd", &"boot.B", begin, end-begin
|
||||
|
||||
org 0 : incbin "test1.mid"
|
||||
test1_mid_len = $
|
||||
savetrd "main.trd", &"boot.B", 0, test1_mid_len
|
||||
export begin
|
||||
export end
|
||||
export main
|
||||
savebin "main.bin", begin, end-begin
|
||||
|
Reference in New Issue
Block a user