diff --git a/compiler/res/prog8lib/cx16/palette.p8 b/compiler/res/prog8lib/cx16/palette.p8 index b33b36af6..b5fe7bb44 100644 --- a/compiler/res/prog8lib/cx16/palette.p8 +++ b/compiler/res/prog8lib/cx16/palette.p8 @@ -5,55 +5,86 @@ palette { %option ignore_unused - uword vera_palette_ptr - sub set_color(ubyte index, uword color) { - vera_palette_ptr = $fa00+(index as uword * 2) - cx16.vpoke(1, vera_palette_ptr, lsb(color)) - vera_palette_ptr++ - cx16.vpoke(1, vera_palette_ptr, msb(color)) + cx16.vaddr(1, $fa00+(index as uword * 2), 0, 1) + cx16.VERA_DATA0 = lsb(color) + cx16.VERA_DATA0 = msb(color) + cx16.VERA_ADDR_H &= 1 } sub get_color(ubyte index) -> uword { - vera_palette_ptr = $fa00+(index as uword * 2) - return mkword(cx16.vpeek(1, vera_palette_ptr+1), cx16.vpeek(1, vera_palette_ptr)) + cx16.vaddr(1, $fa00+(index as uword * 2), 0, 1) + cx16.r0L = cx16.VERA_DATA0 + cx16.r0H = cx16.VERA_DATA0 + cx16.VERA_ADDR_H &= 1 + return cx16.r0 } - sub set_rgb_be(uword palette_ptr, uword num_colors) { + sub set_rgb_be(uword palette_ptr, uword num_colors, ubyte startindex) { ; 1 word per color entry, $0rgb in big endian format - vera_palette_ptr = $fa00 + cx16.vaddr(1, $fa00+(startindex as uword * 2), 0, 1) repeat num_colors { - cx16.vpoke(1, vera_palette_ptr+1, @(palette_ptr)) - palette_ptr++ - cx16.vpoke(1, vera_palette_ptr, @(palette_ptr)) - palette_ptr++ - vera_palette_ptr+=2 + cx16.VERA_DATA0 = @(palette_ptr+1) + cx16.VERA_DATA0 = @(palette_ptr) + palette_ptr += 2 } + cx16.VERA_ADDR_H &= 1 } - sub set_rgb(uword palette_words_ptr, uword num_colors) { + sub set_rgb(uword palette_words_ptr, uword num_colors, ubyte startindex) { ; 1 word per color entry (in little endian format as layed out in video memory, so $gb;$0r) - vera_palette_ptr = $fa00 - repeat num_colors*2 { - cx16.vpoke(1, vera_palette_ptr, @(palette_words_ptr)) + cx16.vaddr(1, $fa00+(startindex as uword * 2), 0, 1) + repeat num_colors { + cx16.VERA_DATA0 = @(palette_words_ptr) + palette_words_ptr++ + cx16.VERA_DATA0 = @(palette_words_ptr) palette_words_ptr++ - vera_palette_ptr++ } + cx16.VERA_ADDR_H &= 1 } - sub set_rgb8(uword palette_bytes_ptr, uword num_colors) { + sub set_rgb8(uword palette_bytes_ptr, uword num_colors, ubyte startindex) { ; 3 bytes per color entry, adjust color depth from 8 to 4 bits per channel. - vera_palette_ptr = $fa00 + cx16.vaddr(1, $fa00+(startindex as uword * 2), 0, 1) ubyte red ubyte greenblue repeat num_colors { cx16.r1 = color8to4(palette_bytes_ptr) palette_bytes_ptr+=3 - cx16.vpoke(1, vera_palette_ptr, cx16.r1H) ; $GB - vera_palette_ptr++ - cx16.vpoke(1, vera_palette_ptr, cx16.r1L) ; $0R - vera_palette_ptr++ + cx16.VERA_DATA0 = cx16.r1H ; $GB + cx16.VERA_DATA0 = cx16.r1L ; $0R } + cx16.VERA_ADDR_H &= 1 + } + + sub set_all_black() { + cx16.vaddr(1, $fa00, 0, 1) + repeat 256 { + cx16.VERA_DATA0 = 0 + cx16.VERA_DATA0 = 0 + } + cx16.VERA_ADDR_H &= 1 + } + + sub set_all_white() { + cx16.vaddr(1, $fa00, 0, 1) + repeat 256 { + cx16.VERA_DATA0 = $ff + cx16.VERA_DATA0 = $0f + } + cx16.VERA_ADDR_H &= 1 + } + + sub set_grayscale(ubyte startindex) { + ; set 16 consecutive colors to a grayscale gradient from black to white + cx16.vaddr(1, $fa00+(startindex as uword * 2), 0, 1) + cx16.r0L=0 + repeat 16 { + cx16.VERA_DATA0 = cx16.r0L + cx16.VERA_DATA0 = cx16.r0L + cx16.r0L += $11 + } + cx16.VERA_ADDR_H &= 1 } sub color8to4(uword colorpointer) -> uword { @@ -70,42 +101,6 @@ palette { return msb(channelvalue * $000f + 135) } - - sub set_monochrome(uword screencolorRGB, uword drawcolorRGB) { - vera_palette_ptr = $fa00 - cx16.vpoke(1, vera_palette_ptr, lsb(screencolorRGB)) ; G,B - vera_palette_ptr++ - cx16.vpoke(1, vera_palette_ptr, msb(screencolorRGB)) ; R - vera_palette_ptr++ - repeat 255 { - cx16.vpoke(1, vera_palette_ptr, lsb(drawcolorRGB)) ; G,B - vera_palette_ptr++ - cx16.vpoke(1, vera_palette_ptr, msb(drawcolorRGB)) ; R - vera_palette_ptr++ - } - } - - sub set_all_black() { - set_monochrome($000, $000) - } - - sub set_all_white() { - set_monochrome($fff, $fff) - } - - sub set_grayscale() { - ; set first 16 colors to a grayscale gradient from black to white - vera_palette_ptr = $fa00 - cx16.r2L=0 - repeat 16 { - cx16.vpoke(1, vera_palette_ptr, cx16.r2L) - vera_palette_ptr++ - cx16.vpoke(1, vera_palette_ptr, cx16.r2L) - vera_palette_ptr++ - cx16.r2L += $11 - } - } - sub fade_step_multi(ubyte startindex, ubyte endindex, uword target_rgb) -> bool { ; Perform one color fade step for multiple consecutive palette entries. ; startindex = palette index of first color to fade @@ -210,7 +205,7 @@ palette { $76e, ; 14 = light blue $bbb ; 15 = light grey ] - set_rgb(colors, len(colors)) + set_rgb(colors, len(colors), 0) } sub set_c64ntsc() { @@ -233,7 +228,7 @@ palette { $36f, ; 14 = light blue $ccc ; 15 = light grey ] - set_rgb(colors, len(colors)) + set_rgb(colors, len(colors), 0) } sub set_default16() { @@ -256,6 +251,6 @@ palette { $08f, ; 14 = light blue $bbb ; 15 = light grey ] - set_rgb(colors, len(colors)) + set_rgb(colors, len(colors), 0) } } diff --git a/compiler/test/TestCompilerOnExamples.kt b/compiler/test/TestCompilerOnExamples.kt index 521791e1a..e8a1e8fc5 100644 --- a/compiler/test/TestCompilerOnExamples.kt +++ b/compiler/test/TestCompilerOnExamples.kt @@ -106,7 +106,6 @@ class TestCompilerOnExamplesCx16: FunSpec({ val onlyCx16 = cartesianProduct( listOf( - "chunkedfile/demo", "vtui/testvtui", "pcmaudio/play-adpcm", "pcmaudio/stream-wav", diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 48e2bf69b..e293ae5a6 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -12,7 +12,6 @@ Future Things and Ideas - a syntax to access specific bits in a variable, to avoid manually shifts&ands, something like variable[4:8] ? (or something else this may be too similar to regular array indexing) - something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using '? -- on the C64: make the floating point routines @banked so that basic can be permanently banked out even if you use floats? But this will crash when the call is done from program code at $a000+ - Libraries: improve ability to create library files in prog8; for instance there's still stuff injected into the start of the start() routine AND there is separate setup logic going on before calling it. Make up our mind! Maybe all setup does need to be put into start() ? because the program cannot function correctly when the variables aren't initialized properly bss is not cleared etc. etc. Add a -library $xxxx command line option to preselect every setting that is required to make a library at $xxxx rather than a normal loadable and runnable program? diff --git a/examples/cx16/amiga.p8 b/examples/cx16/amiga.p8 index 14d52a4f8..edf0588f0 100644 --- a/examples/cx16/amiga.p8 +++ b/examples/cx16/amiga.p8 @@ -11,8 +11,7 @@ main { gfx_hires.graphics_mode() ; select 640*480 mode, 4 colors mouse.set_pointer_image() cx16.mouse_config(-1, 640/8, 240/8) - uword[4] amigacolors = [$aaa, $000, $fff, $68c] ; gray, black, white, lightblue - palette.set_rgb(amigacolors, len(amigacolors)) + palette.set_rgb([$aaa, $000, $fff, $68c], 4, 0) ; set Amiga's workbench 2.0 gray, black, white, lightblue colors cx16.VERA_DC_VSCALE = 64 ; have the vertical resolution so it is 640*240 - more or less Amiga's default non interlaced mode gfx_hires.text_charset(1) diff --git a/examples/cx16/chunkedfile/.gitignore b/examples/cx16/chunkedfile/.gitignore deleted file mode 100644 index 074df2070..000000000 --- a/examples/cx16/chunkedfile/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -testdata/*TITLESCREEN.* -*.mcf -*.asm -*.prg -*.vice-mon-list diff --git a/examples/cx16/chunkedfile/createmcf.py b/examples/cx16/chunkedfile/createmcf.py deleted file mode 100644 index d9c209f54..000000000 --- a/examples/cx16/chunkedfile/createmcf.py +++ /dev/null @@ -1,300 +0,0 @@ -import struct -from typing import Sequence - -# Chunk types: -# user types: 0 - 239 -# 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): - self.data = bytearray([CHUNK_IGNORE] * 256) - self.data[0:4] = struct.pack("ccBB", b'L', b'L', 1, 0) - self.index = 4 - - def __eq__(self, other) -> bool: - return isinstance(other, LoadList) and self.data == other.data - - def add_chunk(self, chunktype: int, size: int, bank: int = 0, address: int = 0) -> None: - if chunktype < 0 or chunktype > 255: - 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 > 255: - raise ValueError("bank must be 0 - 255") - if address < 0 or address > 65535: - raise ValueError("address must be 0 - 65535") - data = struct.pack(" 255: - raise IndexError("too many chunks") - self.data[self.index: self.index + len(data)] = data - self.index += len(data) - - def read(self, data: bytes) -> None: - if len(data) != 256: - raise ValueError("data must be 256 bytes long") - self.data[0:256] = data - self.index = 256 - - def parse(self) -> Sequence[tuple]: - if self.data[0] != ord('L') or self.data[1] != ord('L') or self.data[2] != 1: - raise ValueError("invalid loadlist identifier", self.data[:4]) - index = 4 - chunks = [] - while index < 256: - chunktype = self.data[index] - if chunktype == CHUNK_IGNORE: - index += 1 # pad byte - elif chunktype == CHUNK_EOF: - chunks.append((CHUNK_EOF, 0, 0, 0)) - return chunks - else: - size, bank, address = struct.unpack(" bool: - return self.index <= 4 - - -class MultiChunkFile: - def __init__(self): - self.chunks = [] - self.loadlist = LoadList() - self.datachunks = [] - - def read(self, filename: str) -> None: - num_loadlist = 0 - num_data = 0 - data_total = 0 - file_total = 0 - with open(filename, "rb") as inf: - while True: - data = inf.read(256) - if len(data) == 0: - break - loadlist = LoadList() - loadlist.read(data) - self.chunks.append(loadlist) - num_loadlist += 1 - file_total += len(data) - for chunk in loadlist.parse(): - if chunk[0] == CHUNK_EOF: - break - elif chunk[0] in ChunksWithData or chunk[0] < 240: - data = inf.read(chunk[1]) - self.chunks.append(data) - num_data += 1 - data_total += len(data) - file_total += len(data) - self.validate() - print("Read", filename) - print(f" {num_loadlist} loadlists, {num_data} data chunks") - print(f" total data size: {data_total} (${data_total:x})") - print(f" total file size: {file_total} (${file_total:x})") - - def write(self, filename: str) -> None: - self.flush_ll() - self.validate() - num_loadlist = 0 - num_data = 0 - data_total = 0 - file_total = 0 - with open(filename, "wb") as out: - for chunk in self.chunks: - if isinstance(chunk, LoadList): - num_loadlist += 1 - out.write(chunk.data) - file_total += len(chunk.data) - elif isinstance(chunk, bytes): - num_data += 1 - out.write(chunk) - file_total += len(chunk) - data_total += len(chunk) - else: - raise TypeError("invalid chunk type") - print("Written", filename) - print(f" {num_loadlist} loadlists, {num_data} data chunks") - print(f" total data size: {data_total} (${data_total:x})") - print(f" total file size: {file_total} (${file_total:x})") - - def validate(self) -> None: - if len(self.chunks) < 2: - raise ValueError("must be at least 2 chunks", self.chunks) - chunk_iter = iter(self.chunks) - eof_found = False - while not eof_found: - try: - chunk = next(chunk_iter) - except StopIteration: - raise ValueError("missing EOF chunk") - else: - if isinstance(chunk, LoadList): - for lc in chunk.parse(): - if lc[0] == CHUNK_EOF: - eof_found = True - break - elif lc[0] in ChunksWithData or lc[0] < 240: - size, bank, address = lc[1:] - data = next(chunk_iter) - if isinstance(data, bytes): - if len(data) != size: - raise ValueError("data chunk size mismatch") - else: - raise TypeError("expected data chunk") - else: - raise TypeError("expected LoadList chunk") - try: - next(chunk_iter) - except StopIteration: - pass - else: - raise ValueError("trailing chunks") - - 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: - 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") - self.add_chunk(CHUNK_SYSTEMRAM, bank, address, data[:chunksize]) - data = data[chunksize:] - address += chunksize - - 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, max 8K") - while data: - if address >= 0xc000: - address -= 0x2000 - bank += 1 - if bank >= 32: - raise ValueError("data too large for banked ram") - self.add_chunk(CHUNK_SYSTEMRAM, bank, address, data[:chunksize]) - data = data[chunksize:] - address += chunksize - - 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: - if address >= 65536: - address -= 65536 - bank += 1 - if bank >= 2: - raise ValueError("data too large for video ram") - self.add_chunk(CHUNK_VIDEORAM, bank, address, data[:chunksize]) - data = data[chunksize:] - address += chunksize - - def add_User(self, chunktype: int, data: bytes) -> None: - if chunktype < 0 or chunktype > 239: - raise ValueError("user chunk type must be 0 - 239") - if len(data) > 65535: - raise ValueError("data too large to fit in a single chunk") - self.add_chunk(chunktype, data=data) - - def add_Pause(self, code: int) -> None: - self.add_chunk_nodata(CHUNK_PAUSE, code) - - def add_EOF(self) -> None: - self.add_chunk_nodata(CHUNK_EOF, 0) - - def add_chunk(self, chunktype: int, bank: int = 0, address: int = 0, data: bytes = b"") -> None: - try: - self.loadlist.add_chunk(chunktype, len(data), bank, address) - except IndexError: - # loadlist is full, flush everything out and create a new one and put it in there. - self.flush_ll() - self.loadlist.add_chunk(chunktype, len(data), bank, address) - if data: - self.datachunks.append(bytes(data)) - - def add_chunk_nodata(self, chunktype: int, code: int = 0) -> None: - try: - self.loadlist.add_chunk(chunktype, code, 0, 0) - except IndexError: - # loadlist is full, flush everything out and create a new one and put it in there. - self.flush_ll() - self.loadlist.add_chunk(chunktype, code, 0, 0) - - def flush_ll(self) -> None: - if not self.loadlist.is_empty(): - self.chunks.append(self.loadlist) - self.chunks.extend(self.datachunks) - self.loadlist = LoadList() - self.datachunks.clear() - - -if __name__ == "__main__": - try: - bitmap1 = open("testdata/ME-TITLESCREEN.BIN", "rb").read() - bitmap2 = open("testdata/DS-TITLESCREEN.BIN", "rb").read() - 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. -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.""") - raise SystemExit - - program = bytearray(3333) - textdata = b"hello this is a demo text.... \x00" - - mcf = MultiChunkFile() - for _ in range(4): - mcf.add_User(42, bytearray(999)) - mcf.add_SystemRam(0, 0x4000, program) - mcf.add_BankedRam(10, 0xa000, textdata) - mcf.add_Pause(444) - for _ in range(4): - mcf.add_User(42, bytearray(999)) - mcf.add_VideoRam(1, 0xfa00, palette1) - mcf.add_VideoRam(0, 0, bitmap1) - mcf.add_Pause(333) - 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") - print("Verifying file...") - mcf2 = MultiChunkFile() - mcf2.read("demo.mcf") - assert mcf2.chunks == mcf.chunks diff --git a/examples/cx16/chunkedfile/demo.p8 b/examples/cx16/chunkedfile/demo.p8 deleted file mode 100644 index bc1d6a889..000000000 --- a/examples/cx16/chunkedfile/demo.p8 +++ /dev/null @@ -1,69 +0,0 @@ -%import diskio -%import textio -%import mcf - -%zeropage basicsafe - -main { - - sub start() { - uword duration - ubyte[256] bonkbuffer - - ;;diskio.fastmode(1) - set_screen() - 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() - if_cs { - break ; EOF reached, stop the streaming loop - } else { - ; PAUSE chunk encountered, code is in cx16.r0 - } - } - mcf.close() - } - - duration = cbm.RDTIM16() - cbm.CINT() - txt.print("done. ") - txt.print_uw(duration) - txt.print(" jiffies.\n") - } - - sub set_screen() { - ; 640x400 16 colors - cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1 - cx16.VERA_DC_HSCALE = 128 - cx16.VERA_DC_VSCALE = 128 - cx16.VERA_CTRL = %00000010 - cx16.VERA_DC_VSTART = 20 - cx16.VERA_DC_VSTOP = 400 /2 -1 + 20 ; clip off screen that overflows vram - cx16.VERA_L1_CONFIG = %00000110 ; 16 colors bitmap mode - cx16.VERA_L1_MAPBASE = 0 - cx16.VERA_L1_TILEBASE = %00000001 ; hires - } - - asmsub mcf_get_buffer(ubyte chunktype @A, uword size @XY) -> ubyte @A, uword @XY, bool @Pc { - %asm {{ - ldx #<$a000 - ldy #>$a000 - lda #10 - clc - rts - }} - } - - asmsub mcf_process_chunk() -> bool @Pc { - ; process the chunk that was loaded in the location returned by the previous call to mcf_get_buffer() - %asm {{ - clc - rts - }} - } -} diff --git a/examples/cx16/chunkedfile/mcf.p8 b/examples/cx16/chunkedfile/mcf.p8 deleted file mode 100644 index 701506a0a..000000000 --- a/examples/cx16/chunkedfile/mcf.p8 +++ /dev/null @@ -1,206 +0,0 @@ -%import syslib -%import strings - -; Streaming routine for MCF files (multipurpose chunk format): -; 1. call open() -; 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 - - sub open(str filename, ubyte drive, ubyte channel) -> bool { - file_channel = channel - cbm.SETNAM(strings.length(filename), filename) - cbm.SETLFS(channel, drive, 2) - void cbm.OPEN() - if_cc { - if cbm.READST()==0 { - void cbm.CHKIN(channel) - loadlist_ptr = loadlist_buf - needs_loadlist = true - return true - } - } - close() - return false - } - - sub close() { - cbm.CLRCHN() - cbm.CLOSE(file_channel) - } - - asmsub set_callbacks(uword getbuffer_routine @R0, uword processchunk_routine @R1) { - %asm {{ - lda cx16.r0 - ldy cx16.r0+1 - sta p8b_mcf.p8s_stream.getbuffer_call+1 - sty p8b_mcf.p8s_stream.getbuffer_call+2 - lda cx16.r1 - ldy cx16.r1+1 - sta p8b_mcf.p8s_stream.processchunk_call+1 - sty p8b_mcf.p8s_stream.processchunk_call+2 - rts - }} - } - - sub set_bonkbuffer(uword buffer) { - ; needs to be a buffer of 256 bytes (1 page) - bonkbuffer = buffer - } - - sub stream() { - repeat { - if needs_loadlist { - if not read_loadlist() { - sys.set_carry() - return - } - needs_loadlist = false - } - - while (loadlist_ptr-loadlist_buf)<256 { - when @(loadlist_ptr) { - 255 -> { - ; simply ignore this byte - loadlist_ptr++ - } - 254 -> { - ; End of File - close() - sys.set_carry() - return - } - 253 -> { - ; pause streaming - cx16.r0 = peekw(loadlist_ptr+1) - loadlist_ptr+=6 - sys.clear_carry() - return - } - 252 -> { - ; load into vram - blockload_vram(peekw(loadlist_ptr+1), peek(loadlist_ptr+3), peekw(loadlist_ptr+4)) - loadlist_ptr+=6 - } - 251 -> { - ; load into system ram - blockload_ram(peekw(loadlist_ptr+1), peek(loadlist_ptr+3), peekw(loadlist_ptr+4)) - 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 - } - else -> { - ; custom chunk - uword @shared chunksize = peekw(loadlist_ptr+1) - %asm {{ - lda (p8b_mcf.p8v_loadlist_ptr) - ldx p8v_chunksize - ldy p8v_chunksize+1 -getbuffer_call jsr $ffff ; modified - bcc + - rts ; fail - exit as if EOF -+ sta cx16.r1L - stx cx16.r0 - sty cx16.r0+1 - }} - blockload_ram(chunksize, cx16.r1L, cx16.r0) - loadlist_ptr += 6 - %asm {{ -processchunk_call jsr $ffff ; modified - bcc + - rts -+ - }} - } - } - } - needs_loadlist = true - } - } - - sub read_loadlist() -> bool { - blockload_ram(256, cx16.getrambank(), loadlist_buf) - if loadlist_buf[0]!=sc:'L' or loadlist_buf[1]!=sc:'L' or loadlist_buf[2]!=1 - return false ; header error - loadlist_ptr = loadlist_buf+4 - } - - sub blockload_vram(uword size, ubyte bank, uword address) { - cx16.VERA_CTRL = 0 - cx16.VERA_ADDR_L = lsb(address) - cx16.VERA_ADDR_M = msb(address) - cx16.VERA_ADDR_H = bank | %00010000 ; enable vera auto increment - while size!=0 { - size -= readblock(size, &cx16.VERA_DATA0, true) - } - } - - sub blockload_dummy(uword size) { - ubyte buffer - while size!=0 { - size -= readblock(size, &buffer, true) - } - } - - sub blockload_ram(uword size, ubyte bank, uword address) { - ubyte orig_ram_bank = cx16.getrambank() - cx16.rambank(bank) - cx16.r3 = address - while size!=0 { - cx16.r2 = readblock(size, cx16.r3, false) - size -= cx16.r2 - cx16.r3 += cx16.r2 - } - 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!=0 { - ubyte readsize = 255 - if msb(size)==0 - readsize = lsb(size) - void, 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 { - void, cx16.r0 = cx16.MACPTR(0, address, dontAdvance) ; read 512 bytes - return cx16.r0 - } - if msb(size)!=0 { - void, cx16.r0 = cx16.MACPTR(255, address, dontAdvance) ; read 255 bytes - return cx16.r0 - } - void, cx16.r0 = cx16.MACPTR(lsb(size), address, dontAdvance) ; read remaining number of bytes - return cx16.r0 - } -} diff --git a/examples/cx16/chunkedfile/readme.md b/examples/cx16/chunkedfile/readme.md deleted file mode 100644 index 1c4fd3baa..000000000 --- a/examples/cx16/chunkedfile/readme.md +++ /dev/null @@ -1,99 +0,0 @@ -# Multipurpose Chunked File - -## Goals - -- meant for the Commander X16 system, depends on working MACPTR kernal call for fast loading. -- single file, possibly megabytes in size, that contains various kinds of data to be loaded or streamed into different locations. -- simple file format with few special cases. -- no random access, only sequential reading/streaming. -- simple user code that doesn't have to bother with the I/O at all. -- a few chunk types that can be handled automatically to load data into system ram, banked ram, or video ram. -- custom chunk types for flexibility or extensibility. - -Theoretical optimal chunk size is 512 bytes but actual size may be different. (In practice there seems to be no significant speed impact) - -MCF files are meant to be created using a tool on PC, and only being read on the X16. -A Python tool is provided to create a demo MCF file. -A proof of concept Prog8 library module and example program is provided to consume that demo MCF file on the X16. - - -## File Format - -This is the MCF file format: - -| Chunk | size (bytes) | -|------------|--------------| -| LoadList | 256 | -| Data | 1 - 65535 | -| Data | 1 - 65535 | -| ... | | -| LoadList | 256 | -| Data | 1 - 65535 | -| Data | 1 - 65535 | -| ... | | - -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 - -This chunk is a list of what kinds of chunks occur in the file after it. -It starts with a small identification header: - -| data | meaning | -|---------|---------------------| -| 2 bytes | 'LL' (76, 76) | -| byte | version (1 for now) | -| byte | reserved (0) | - -Then a sequence of 1 or more chunk specs (6 bytes each), as long as it still fits in 256 bytes: - -| data | meaning | -|----------------------|--------------------------------------------| -| byte | chunk type | -| word (little-endian) | chunk size | -| byte | bank number (used for some chunk types) | -| word (little-endian) | memory address (used for some chunk types) | - -Total 6 bytes per occurrence. Any remaining unused bytes in the 256 bytes LoadList chunk are to be padded with byte 255. -If there are more chunks in the file than fit in a single loadlist, we simply add another loadlist block and continue there. -(The file only ends once an End Of File chunk type is encountered in a loadlist.) - - -### Chunk types - -| 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) - -When such a custom chunk type is encountered, a user routine is called to get the load address for the chunk data, -then the chunk is loaded into that buffer, and finally a user routine is called to process the chunk data. -The size can be zero, so that the chunk type acts like a simple notification flag for the main program to do something. - -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. - -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). - -These routines are provided to the streaming routine as callback addresses (ram bank number + address to call). -If any of these routines returns Carry set (error status) the streaming routine halts, otherwise it keeps on going. -The streaming continues until an End of File chunk type is encountered in the loadlist. diff --git a/examples/cx16/chunkedfile/testdata/readme.txt b/examples/cx16/chunkedfile/testdata/readme.txt deleted file mode 100644 index bec0e2865..000000000 --- a/examples/cx16/chunkedfile/testdata/readme.txt +++ /dev/null @@ -1,3 +0,0 @@ -You'll need to put the titlescreen data files from the 'musicdemo' project into this 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. diff --git a/examples/cx16/kefrenbars.p8 b/examples/cx16/kefrenbars.p8 index abc5d999f..bb8f8157d 100644 --- a/examples/cx16/kefrenbars.p8 +++ b/examples/cx16/kefrenbars.p8 @@ -21,7 +21,7 @@ main { $312, $211, $100 ] - palette.set_rgb(&colors, len(colors)) + palette.set_rgb(&colors, len(colors), 0) gfx_lores.graphics_mode() ; lores 256 colors cx16.VERA_DC_VSCALE = 0 ; display trick spoiler.......: stretch 1 line of display all the way to the bottom cx16.enable_irq_handlers(true) diff --git a/examples/cx16/showbmx.p8 b/examples/cx16/showbmx.p8 index ed944032f..37aed17f7 100644 --- a/examples/cx16/showbmx.p8 +++ b/examples/cx16/showbmx.p8 @@ -33,7 +33,7 @@ main { ; (you could do a soft fade-in effect with this for instance) bmx.palette_buffer_ptr = memory("palette", 512, 0) sys.memset(bmx.palette_buffer_ptr, 512, 0) - palette.set_rgb(bmx.palette_buffer_ptr, 256) + palette.set_rgb(bmx.palette_buffer_ptr, 256, 0) ; switch to bitmap screen mode and color depth: 320*240 void cx16.screen_mode($80, false) ; we're lazy and just use a kernal routine to set up the basics diff --git a/examples/test.p8 b/examples/test.p8 index 9e02c5d5a..6d7493a42 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,26 +1,51 @@ %import textio +%import palette %zeropage basicsafe +%option no_sysinit main { + uword[] @shared colors = [ + $000, ; 0 = black + $fff, ; 1 = white + $800, ; 2 = red + $afe, ; 3 = cyan + $c4c, ; 4 = purple + $0c5, ; 5 = green + $00a, ; 6 = blue + $ee7, ; 7 = yellow + $d85, ; 8 = orange + $640, ; 9 = brown + $f77, ; 10 = light red + $333, ; 11 = dark grey + $777, ; 12 = medium grey + $af6, ; 13 = light green + $08f, ; 14 = light blue + $bbb ; 15 = light grey + ] + + ubyte[] @shared colors_b = [ + $00, $00, ; 0 = black + $0f, $ff, ; 1 = white + $08, $00, ; 2 = red + $0a, $fe, ; 3 = cyan + $0c, $4c, ; 4 = purple + $00, $c5, ; 5 = green + $00, $0a, ; 6 = blue + $0e, $e7, ; 7 = yellow + $0d, $85, ; 8 = orange + $06, $40, ; 9 = brown + $0f, $77, ; 10 = light red + $03, $33, ; 11 = dark grey + $07, $77, ; 12 = medium grey + $0a, $f6, ; 13 = light green + $00, $8f, ; 14 = light blue + $0b, $bb ; 15 = light grey + ] + + sub start() { - if (cx16.r0 & 1) as bool == false - cx16.r1++ - else - cx16.r2++ - - if (cx16.r0 & 1) as bool == true - cx16.r1++ - else - cx16.r2++ - - if not((cx16.r0 & 1) as bool) - cx16.r1++ - else - cx16.r2++ - - if (cx16.r0 & 1) as bool - cx16.r1++ - else - cx16.r2++ + palette.set_grayscale(0) + sys.wait(60) + palette.set_rgb_be(colors_b, 16, 0) } } diff --git a/examples/vm/bsieve.p8 b/examples/vm/bsieve.p8 index 4b3e49f3d..1735382a4 100644 --- a/examples/vm/bsieve.p8 +++ b/examples/vm/bsieve.p8 @@ -1,7 +1,10 @@ -%import textio ; The "Byte Sieve" test. https://en.wikipedia.org/wiki/Byte_Sieve +%import textio +%zeropage basicsafe +%option no_sysinit + main { sub start() { @@ -24,8 +27,8 @@ main { flags_ptr[k] = 0 ; false k += prime } - txt.print_uw(prime) - txt.nl() + ; txt.print_uw(prime) + ; txt.nl() count++ } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0aaefbcaf..df97d72b8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME