added bonkram chunk to chunkfile example

This commit is contained in:
Irmen de Jong 2023-09-25 22:14:03 +02:00
parent 390263a34e
commit 5268b05060
5 changed files with 92 additions and 32 deletions

View File

@ -1,6 +1,7 @@
TODO TODO
==== ====
- add a compiler switch to replace all calls to the math word mul routine on the X16 by the verafx call instead.
- [on branch: shortcircuit] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 .... - [on branch: shortcircuit] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 ....
- [on branch: ir-less-branch-opcodes] IR: reduce the number of branch instructions such as BEQ, BEQR, etc (gradually), replace with CMP(I) + status branch instruction - [on branch: ir-less-branch-opcodes] IR: reduce the number of branch instructions such as BEQ, BEQR, etc (gradually), replace with CMP(I) + status branch instruction
- IR: reduce amount of CMP/CMPI after instructions that set the status bits correctly (LOADs? INC? etc), but only after setting the status bits is verified! - IR: reduce amount of CMP/CMPI after instructions that set the status bits correctly (LOADs? INC? etc), but only after setting the status bits is verified!

View File

@ -3,14 +3,17 @@ from typing import Sequence
# Chunk types: # Chunk types:
# user types: 0 - 239 # user types: 0 - 239
# reserved: 240 - 249 # reserved: 240 - 248
CHUNK_DUMMY = 250 CHUNK_DUMMY = 249
CHUNK_BONKRAM = 250
CHUNK_SYSTEMRAM = 251 CHUNK_SYSTEMRAM = 251
CHUNK_VIDEORAM = 252 CHUNK_VIDEORAM = 252
CHUNK_PAUSE = 253 CHUNK_PAUSE = 253
CHUNK_EOF = 254 CHUNK_EOF = 254
CHUNK_IGNORE = 255 CHUNK_IGNORE = 255
ChunksWithData = {CHUNK_DUMMY, CHUNK_SYSTEMRAM, CHUNK_VIDEORAM, CHUNK_BONKRAM}
class LoadList: class LoadList:
def __init__(self): def __init__(self):
@ -26,8 +29,8 @@ class LoadList:
raise ValueError("chunktype must be 0 - 255") raise ValueError("chunktype must be 0 - 255")
if size < 0 or size > 65535: if size < 0 or size > 65535:
raise ValueError(f"size must be 0 - 65535 bytes") raise ValueError(f"size must be 0 - 65535 bytes")
if bank < 0 or bank > 31: if bank < 0 or bank > 255:
raise ValueError("bank must be 0 - 31") raise ValueError("bank must be 0 - 255")
if address < 0 or address > 65535: if address < 0 or address > 65535:
raise ValueError("address must be 0 - 65535") raise ValueError("address must be 0 - 65535")
data = struct.pack("<BHBH", chunktype, size, bank, address) data = struct.pack("<BHBH", chunktype, size, bank, address)
@ -88,7 +91,7 @@ class MultiChunkFile:
for chunk in loadlist.parse(): for chunk in loadlist.parse():
if chunk[0] == CHUNK_EOF: if chunk[0] == CHUNK_EOF:
break break
elif chunk[0] in (CHUNK_DUMMY, CHUNK_SYSTEMRAM, CHUNK_VIDEORAM) or chunk[0] < 240: elif chunk[0] in ChunksWithData or chunk[0] < 240:
data = inf.read(chunk[1]) data = inf.read(chunk[1])
self.chunks.append(data) self.chunks.append(data)
num_data += 1 num_data += 1
@ -141,7 +144,7 @@ class MultiChunkFile:
if lc[0] == CHUNK_EOF: if lc[0] == CHUNK_EOF:
eof_found = True eof_found = True
break break
elif lc[0] in (CHUNK_DUMMY, CHUNK_SYSTEMRAM, CHUNK_VIDEORAM) or lc[0] < 240: elif lc[0] in ChunksWithData or lc[0] < 240:
size, bank, address = lc[1:] size, bank, address = lc[1:]
data = next(chunk_iter) data = next(chunk_iter)
if isinstance(data, bytes): if isinstance(data, bytes):
@ -161,9 +164,11 @@ class MultiChunkFile:
def add_Dummy(self, size: int) -> None: def add_Dummy(self, size: int) -> None:
self.add_chunk(CHUNK_DUMMY, data=bytearray(size)) self.add_chunk(CHUNK_DUMMY, data=bytearray(size))
def add_SystemRam(self, bank: int, address: int, data: bytes, chunksize: int=0xfe00) -> None: def add_SystemRam(self, bank: int, address: int, data: bytes, chunksize: int = 0xfe00) -> None:
if address >= 0xa000 and address < 0xc000: if address >= 0xa000 and address < 0xc000:
raise ValueError("use add_BankedRam instead to load chunks into banked ram $a000-$c000") raise ValueError("use add_BankedRam instead to load chunks into banked ram $a000-$c000")
if bank < 0 or bank > 31:
raise ValueError("bank must be 0 - 31")
while data: while data:
if address >= 65536: if address >= 65536:
raise ValueError("data too large for system ram") raise ValueError("data too large for system ram")
@ -171,14 +176,14 @@ class MultiChunkFile:
data = data[chunksize:] data = data[chunksize:]
address += chunksize address += chunksize
def add_BankedRam(self, bank: int, address: int, data: bytes, chunksize: int=0x2000) -> None: def add_BankedRam(self, bank: int, address: int, data: bytes, chunksize: int = 0x2000) -> None:
if address < 0xa000 or address >= 0xc000: if address < 0xa000 or address >= 0xc000:
raise ValueError("use add_SystemRam instead to load chunks into normal system ram") raise ValueError("use add_SystemRam instead to load chunks into normal system ram")
if chunksize>0x2000: if chunksize > 0x2000:
raise ValueError("chunksize too large for banked ram") raise ValueError("chunksize too large for banked ram, max 8K")
while data: while data:
if address >= 0xc000: if address >= 0xc000:
address -= 0xc000 address -= 0x2000
bank += 1 bank += 1
if bank >= 32: if bank >= 32:
raise ValueError("data too large for banked ram") raise ValueError("data too large for banked ram")
@ -186,7 +191,24 @@ class MultiChunkFile:
data = data[chunksize:] data = data[chunksize:]
address += chunksize address += chunksize
def add_VideoRam(self, bank: int, address: int, data: bytes, chunksize: int=0xfe00) -> None: def add_BonkRam(self, bonk: int, address: int, data: bytes, chunksize: int = 0x4000) -> None:
if bonk < 32 or bonk > 255:
raise ValueError("bank for bonk ram (cartridge ram) must be 32 - 255")
if chunksize > 0x4000:
raise ValueError("chunksize too large for bonk ram (cartridge ram), max 16K")
if address < 0xc000 or address > 0xffff:
raise ValueError("use add_SystemRam instead to load chunks into normal system ram")
while data:
if address > 0xffff:
address -= 0x4000
bonk += 1
if bonk > 255:
raise ValueError("data too large for bonk ram (cartridge ram)")
self.add_chunk(CHUNK_BONKRAM, bonk, address, data[:chunksize])
data = data[chunksize:]
address += chunksize
def add_VideoRam(self, bank: int, address: int, data: bytes, chunksize: int = 0xfe00) -> None:
if bank < 0 or bank > 1: if bank < 0 or bank > 1:
raise ValueError("bank for videoram must be 0 or 1") raise ValueError("bank for videoram must be 0 or 1")
while data: while data:
@ -245,7 +267,7 @@ if __name__ == "__main__":
palette1 = open("testdata/ME-TITLESCREEN.PAL", "rb").read() palette1 = open("testdata/ME-TITLESCREEN.PAL", "rb").read()
palette2 = open("testdata/DS-TITLESCREEN.PAL", "rb").read() palette2 = open("testdata/DS-TITLESCREEN.PAL", "rb").read()
except IOError: except IOError:
print("""ERROR: cannot load the demo data files. print("""ERROR: cannot load the demo data files.
You'll need to put the titlescreen data files from the 'musicdemo' project into the testdata directory. You'll need to put the titlescreen data files from the 'musicdemo' project into the testdata directory.
The musicdemo is on github: https://github.com/irmen/cx16musicdemo The musicdemo is on github: https://github.com/irmen/cx16musicdemo
The four files are the two ME- and the two DS- TITLESCREEN.BIN and .PAL files, and are generated by running the makefile in that project.""") The four files are the two ME- and the two DS- TITLESCREEN.BIN and .PAL files, and are generated by running the makefile in that project.""")
@ -268,6 +290,7 @@ The four files are the two ME- and the two DS- TITLESCREEN.BIN and .PAL files, a
mcf.add_VideoRam(1, 0xfa00, palette2) mcf.add_VideoRam(1, 0xfa00, palette2)
mcf.add_VideoRam(0, 0, bitmap2) mcf.add_VideoRam(0, 0, bitmap2)
mcf.add_Pause(222) mcf.add_Pause(222)
mcf.add_BonkRam(32, 0xc000, bytearray(32768))
mcf.add_Pause(111) mcf.add_Pause(111)
mcf.add_EOF() mcf.add_EOF()
mcf.write("demo.mcf") mcf.write("demo.mcf")

View File

@ -7,11 +7,14 @@ main {
sub start() { sub start() {
uword duration uword duration
ubyte[256] bonkbuffer
set_screen_mode() set_screen_mode()
cbm.SETTIM(0,0,0) cbm.SETTIM(0,0,0)
mcf.set_callbacks(mcf_get_buffer, mcf_process_chunk) ; not needed if the stream has no custom chunk types mcf.set_callbacks(mcf_get_buffer, mcf_process_chunk) ; not needed if the stream has no custom chunk types
mcf.set_bonkbuffer(bonkbuffer)
if mcf.open("demo.mcf", 8, 2) { if mcf.open("demo.mcf", 8, 2) {
repeat { repeat {
mcf.stream() mcf.stream()

View File

@ -3,14 +3,16 @@
; Streaming routine for MCF files (multipurpose chunk format): ; Streaming routine for MCF files (multipurpose chunk format):
; 1. call open() ; 1. call open()
; 2. set callbacks if needed, set_callbacks() ; 2. set callbacks if needed; set_callbacks()
; 3. call stream() in a loop ; 3. set bonk ram (cartridge ram) load buffer, if needed; set_bonkbuffer()
; 4. call close() if you want to cleanup halfway through for some reason ; 4. call stream() in a loop
; 5. call close() if you want to cleanup halfway through for some reason
mcf { mcf {
uword loadlist_buf = memory("loadlist", 256, 0) uword loadlist_buf = memory("loadlist", 256, 0)
uword @zp loadlist_ptr uword @zp loadlist_ptr
uword bonkbuffer
bool needs_loadlist bool needs_loadlist
ubyte file_channel ubyte file_channel
@ -49,6 +51,11 @@ mcf {
}} }}
} }
sub set_bonkbuffer(uword buffer) {
; needs to be a buffer of 256 bytes (1 page)
bonkbuffer = buffer
}
sub stream() { sub stream() {
repeat { repeat {
if needs_loadlist { if needs_loadlist {
@ -89,6 +96,15 @@ mcf {
loadlist_ptr+=6 loadlist_ptr+=6
} }
250 -> { 250 -> {
; bonk ram (cartridge ram)
; This cannot use MACPTR (because the kernal rom isn't banked in)
; so we have to load it into a buffer and copy it manually.
; Because this will be rarely used, the buffer is not allocated here to save memory, and instead
; the user has to set it with the config routine when the program wants to use this chunk type.
blockload_bonkram(peekw(loadlist_ptr+1), peek(loadlist_ptr+3), peekw(loadlist_ptr+4))
loadlist_ptr+=6
}
249 -> {
; dummy chunk ; dummy chunk
blockload_dummy(peekw(loadlist_ptr+1)) blockload_dummy(peekw(loadlist_ptr+1))
loadlist_ptr+=6 loadlist_ptr+=6
@ -158,6 +174,22 @@ processchunk_call jsr $ffff ; modified
cx16.rambank(orig_ram_bank) cx16.rambank(orig_ram_bank)
} }
sub blockload_bonkram(uword size, ubyte bonk, uword address) {
ubyte orig_rom_bank = cx16.getrombank()
cx16.r3 = address
while size {
ubyte readsize = 255
if size < 255
readsize = lsb(size)
cx16.r2 = cx16.macptr(readsize, bonkbuffer, false) ; can't macptr directly to bonk ram
cx16.rombank(bonk)
sys.memcopy(bonkbuffer, cx16.r3, cx16.r2) ; copy to bonk ram
cx16.rombank(orig_rom_bank)
size -= cx16.r2
cx16.r3 += cx16.r2
}
}
sub readblock(uword size, uword address, bool dontAdvance) -> uword { sub readblock(uword size, uword address, bool dontAdvance) -> uword {
if msb(size)>=2 if msb(size)>=2
return cx16.macptr(0, address, dontAdvance) ; read 512 bytes return cx16.macptr(0, address, dontAdvance) ; read 512 bytes
@ -165,4 +197,4 @@ processchunk_call jsr $ffff ; modified
return cx16.macptr(255, address, dontAdvance) ; read 255 bytes return cx16.macptr(255, address, dontAdvance) ; read 255 bytes
return cx16.macptr(lsb(size), address, dontAdvance) ; read remaining number of bytes return cx16.macptr(lsb(size), address, dontAdvance) ; read remaining number of bytes
} }
} }

View File

@ -36,7 +36,7 @@ and so on.
There is no limit to the number of chunks and the size of the file, as long as it fits on the disk. There is no limit to the number of chunks and the size of the file, as long as it fits on the disk.
### LoadList chunk ### LoadList chunk
This chunk is a list of what kinds of chunks occur in the file after it. This chunk is a list of what kinds of chunks occur in the file after it.
It starts with a small identification header: It starts with a small identification header:
@ -52,7 +52,7 @@ Then a sequence of 1 or more chunk specs (6 bytes each), as long as it still fit
| data | meaning | | data | meaning |
|----------------------|--------------------------------------------| |----------------------|--------------------------------------------|
| byte | chunk type | | byte | chunk type |
| word (little-endian) | chunk size | | word (little-endian) | chunk size |
| byte | bank number (used for some chunk types) | | byte | bank number (used for some chunk types) |
| word (little-endian) | memory address (used for some chunk types) | | word (little-endian) | memory address (used for some chunk types) |
@ -61,18 +61,19 @@ If there are more chunks in the file than fit in a single loadlist, we simply ad
(The file only ends once an End Of File chunk type is encountered in a loadlist.) (The file only ends once an End Of File chunk type is encountered in a loadlist.)
### Chunk types ### Chunk types
| chunk type | meaning | | chunk type | meaning |
|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 0 - 239 | custom chunk types. See below. | | 0 - 239 | custom chunk types. See below. |
| 240 - 249 | reserved for future system chunk types. | | 240 - 248 | reserved for future system chunk types. |
| 250 | dummy chunk: read a chunk of the specified number of bytes but don't do anything with it. Useful to realign the file I/O on disk block size. | | 249 | dummy chunk: read a chunk of the specified number of bytes but don't do anything with it. Useful to realign the file I/O on disk block size. |
| 251 | system ram load: use banknumber + address to set the RAM bank and load address and loads the chunk there, then continue streaming. | | 250 | bonk ram load: use banknumber + address to set the Cartridge RAM ('bonk' RAM) bank and load address and loads the chunk there, then continue streaming. Note this is slower than other types of ram. Rquires 1 page of user chosen buffer area. |
| 252 | video ram load: use banknumber + address to set the Vera VRAM bank (hi byte) and load address (mid+lo byte) and loads the chunk into video ram there, then continue streaming. | | 251 | system ram load: use banknumber + address to set the RAM bank and load address and loads the chunk there, then continue streaming. |
| 253 | pause streaming. Returns from stream routine with pause status: Carry=clear. And reg.r0=size. until perhaps the program calls the stream routine again to resume. | | 252 | video ram load: use banknumber + address to set the Vera VRAM bank (hi byte) and load address (mid+lo byte) and loads the chunk into video ram there, then continue streaming. |
| 254 | end of file. Closes the file and stops streaming: returns from stream routine with exit status: Carry=set. | | 253 | pause streaming. Returns from stream routine with pause status: Carry=clear. And reg.r0=size. until perhaps the program calls the stream routine again to resume. |
| 255 | ignore this byte. Used to pad out the loadlist chunk to 256 bytes. | | 254 | end of file. Closes the file and stops streaming: returns from stream routine with exit status: Carry=set. |
| 255 | ignore this byte. Used to pad out the loadlist chunk to 256 bytes. |
### Custom chunk types (0 - 239) ### Custom chunk types (0 - 239)
@ -85,10 +86,10 @@ The first routine has the following signature:
**get_buffer()**: **get_buffer()**:
Arguments: reg.A = chunk type, reg.XY = chunksize. Arguments: reg.A = chunk type, reg.XY = chunksize.
Returns: Carry flag=success (set = fail, clear = ok), ram bank in reg.A, memory address in reg.XY. Returns: Carry flag=success (set = fail, clear = ok), ram bank in reg.A, memory address in reg.XY.
The second routine has the following signature: The second routine has the following signature:
**process_chunk()**: **process_chunk()**:
Arguments: none (save them from the get_buffer call if needed). Arguments: none (save them from the get_buffer call if needed).
Returns: Carry flag=success (set = fail, clear = ok). Returns: Carry flag=success (set = fail, clear = ok).