diff --git a/docs/source/todo.rst b/docs/source/todo.rst index f3f353415..ce8b7e183 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -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! diff --git a/examples/cx16/chunkedfile/createmcf.py b/examples/cx16/chunkedfile/createmcf.py index 0cea4a5be..d9c209f54 100644 --- a/examples/cx16/chunkedfile/createmcf.py +++ b/examples/cx16/chunkedfile/createmcf.py @@ -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(" 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") diff --git a/examples/cx16/chunkedfile/demo.p8 b/examples/cx16/chunkedfile/demo.p8 index 7254baeef..2a91fa327 100644 --- a/examples/cx16/chunkedfile/demo.p8 +++ b/examples/cx16/chunkedfile/demo.p8 @@ -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() diff --git a/examples/cx16/chunkedfile/mcf.p8 b/examples/cx16/chunkedfile/mcf.p8 index 305173596..6b198ff5f 100644 --- a/examples/cx16/chunkedfile/mcf.p8 +++ b/examples/cx16/chunkedfile/mcf.p8 @@ -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 } -} \ No newline at end of file +} diff --git a/examples/cx16/chunkedfile/readme.md b/examples/cx16/chunkedfile/readme.md index 17f13559e..d6c19107b 100644 --- a/examples/cx16/chunkedfile/readme.md +++ b/examples/cx16/chunkedfile/readme.md @@ -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).