mirror of
https://github.com/irmen/prog8.git
synced 2025-02-09 07:31:34 +00:00
added bonkram chunk to chunkfile example
This commit is contained in:
parent
390263a34e
commit
5268b05060
@ -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!
|
||||||
|
@ -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")
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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).
|
||||||
|
Loading…
x
Reference in New Issue
Block a user