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
====
- 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: 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!

View File

@ -3,14 +3,17 @@ from typing import Sequence
# Chunk types:
# user types: 0 - 239
# reserved: 240 - 249
CHUNK_DUMMY = 250
# reserved: 240 - 248
CHUNK_DUMMY = 249
CHUNK_BONKRAM = 250
CHUNK_SYSTEMRAM = 251
CHUNK_VIDEORAM = 252
CHUNK_PAUSE = 253
CHUNK_EOF = 254
CHUNK_IGNORE = 255
ChunksWithData = {CHUNK_DUMMY, CHUNK_SYSTEMRAM, CHUNK_VIDEORAM, CHUNK_BONKRAM}
class LoadList:
def __init__(self):
@ -26,8 +29,8 @@ class LoadList:
raise ValueError("chunktype must be 0 - 255")
if size < 0 or size > 65535:
raise ValueError(f"size must be 0 - 65535 bytes")
if bank < 0 or bank > 31:
raise ValueError("bank must be 0 - 31")
if bank < 0 or bank > 255:
raise ValueError("bank must be 0 - 255")
if address < 0 or address > 65535:
raise ValueError("address must be 0 - 65535")
data = struct.pack("<BHBH", chunktype, size, bank, address)
@ -88,7 +91,7 @@ class MultiChunkFile:
for chunk in loadlist.parse():
if chunk[0] == CHUNK_EOF:
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])
self.chunks.append(data)
num_data += 1
@ -141,7 +144,7 @@ class MultiChunkFile:
if lc[0] == CHUNK_EOF:
eof_found = True
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:]
data = next(chunk_iter)
if isinstance(data, bytes):
@ -161,9 +164,11 @@ class MultiChunkFile:
def add_Dummy(self, size: int) -> None:
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:
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:
if address >= 65536:
raise ValueError("data too large for system ram")
@ -171,14 +176,14 @@ class MultiChunkFile:
data = data[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:
raise ValueError("use add_SystemRam instead to load chunks into normal system ram")
if chunksize>0x2000:
raise ValueError("chunksize too large for banked ram")
if chunksize > 0x2000:
raise ValueError("chunksize too large for banked ram, max 8K")
while data:
if address >= 0xc000:
address -= 0xc000
address -= 0x2000
bank += 1
if bank >= 32:
raise ValueError("data too large for banked ram")
@ -186,7 +191,24 @@ class MultiChunkFile:
data = data[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:
raise ValueError("bank for videoram must be 0 or 1")
while data:
@ -245,7 +267,7 @@ if __name__ == "__main__":
palette1 = open("testdata/ME-TITLESCREEN.PAL", "rb").read()
palette2 = open("testdata/DS-TITLESCREEN.PAL", "rb").read()
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.
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.""")
@ -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(0, 0, bitmap2)
mcf.add_Pause(222)
mcf.add_BonkRam(32, 0xc000, bytearray(32768))
mcf.add_Pause(111)
mcf.add_EOF()
mcf.write("demo.mcf")

View File

@ -7,11 +7,14 @@ main {
sub start() {
uword duration
ubyte[256] bonkbuffer
set_screen_mode()
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_bonkbuffer(bonkbuffer)
if mcf.open("demo.mcf", 8, 2) {
repeat {
mcf.stream()

View File

@ -3,14 +3,16 @@
; Streaming routine for MCF files (multipurpose chunk format):
; 1. call open()
; 2. set callbacks if needed, set_callbacks()
; 3. call stream() in a loop
; 4. call close() if you want to cleanup halfway through for some reason
; 2. set callbacks if needed; set_callbacks()
; 3. set bonk ram (cartridge ram) load buffer, if needed; set_bonkbuffer()
; 4. call stream() in a loop
; 5. call close() if you want to cleanup halfway through for some reason
mcf {
uword loadlist_buf = memory("loadlist", 256, 0)
uword @zp loadlist_ptr
uword bonkbuffer
bool needs_loadlist
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() {
repeat {
if needs_loadlist {
@ -89,6 +96,15 @@ mcf {
loadlist_ptr+=6
}
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
blockload_dummy(peekw(loadlist_ptr+1))
loadlist_ptr+=6
@ -158,6 +174,22 @@ processchunk_call jsr $ffff ; modified
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 {
if msb(size)>=2
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(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.
### LoadList chunk
### LoadList chunk
This chunk is a list of what kinds of chunks occur in the file after it.
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 |
|----------------------|--------------------------------------------|
| byte | chunk type |
| word (little-endian) | chunk size |
| word (little-endian) | chunk size |
| byte | bank number (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.)
### Chunk types
### Chunk types
| chunk type | meaning |
|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 0 - 239 | custom chunk types. See below. |
| 240 - 249 | 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. |
| 251 | system ram load: use banknumber + address to set the RAM bank and load address and loads the chunk there, then continue streaming. |
| 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. |
| 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. |
| 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. |
| chunk type | meaning |
|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 0 - 239 | custom chunk types. See below. |
| 240 - 248 | reserved for future system chunk types. |
| 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. |
| 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. |
| 251 | system ram load: use banknumber + address to set the RAM bank and load address and loads the chunk there, then continue streaming. |
| 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. |
| 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. |
| 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)
@ -85,10 +86,10 @@ The first routine has the following signature:
**get_buffer()**:
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:
**process_chunk()**:
Arguments: none (save them from the get_buffer call if needed).
Returns: Carry flag=success (set = fail, clear = ok).