I was experimenting with generating three-color pattern dither (2x2 matricies), and this is what I came up with. It's obviously too slow to use without building a dithering table (you could fit 4:5:4 color in the cartridge ram with cstore/restore), but I wanted to throw this out in the world incase someone else wanted to play with it.
The more lua code I write in the pico-8, the more general purpose functions I wind up discovering. Here is my current utility library, I'll probably continue adding more as time progresses.
Quick sort
This one is pretty straight forward, you simply call it by providing a table and an optional comparator (for non-numeric lists), and it will in-place sort your table. It currently does not return anything, but it could me modified to return the table passed if you want to chain things.
-- Inplace quick sort function qsort(tbl, cmp, first, last) first = first or 1 last = last or #tbl cmp = cmp or function (x,y) return x - y end if (first >= last) then return end local pivot, pv, tail = first, tbl[pivot], last while pivot < tail do local tv = tbl[tail] -- Tail is smaller than the pivot -- Shuffle things around if ( cmp(pv, tv) > 0 ) then tbl[pivot], pivot = tv, pivot + 1 tbl[tail] = tbl[pivot] end tail = tail - 1 end tbl[pivot] = pv qsort(tbl, cmp, first, pivot - 1) qsort(tbl, cmp, pivot + 1, last) end |
So, one of my favorite effects from the olden days was the ability to do glinting / pseudo transparency. Sadly, there is no way to do this in the PICO-8 due to cycle timing and memory issues. I would love to see the PAL call replaced with something that accepts three colors rather than two.
something like: tpal c0 c1 c2, which would take all instances where c0 is drawn over c1 with c2. It would take 128 bytes of memory, but there is still plenty of headroom inside of the draw state structure to accommodate this.
I don't know if anyone else has done a write up on this, but there is actually a pretty solid way to get around most of the data limits on the system, and it's kinda gross, and doesn't with the webplayer. The nice thing is that it gives you a legit file system, and basically limitless binary data!
Here is a proof of concept, I created two files (this one is test.pb)
CLS() PRINT("") PRINT("") RELOAD (0x6000, 0x4300, 128) LOAD ("TEST2") RELOAD (0x6100, 0x4300, 128) FUNCTION _DRAW() END FUNCTION _UPDATE() END |
Test 2 contains nothing but garbage code that would crash the machine.
The rational behind this is that:
1) If LOAD trashes execution, it would crash when the new program runs
2) If LOAD aborts the current thread, it would simply return to the prompt with TEST2 loaded
3) If LOAD doesn't allow shared state, it would copy the same thing out of the code area of the cartridge
This test proves 3 things...
1) RUN performs LUA compilation, so LOADing garbage data doesn't harm execution state
2) The existing machine state is preserved after a LOAD call, while altering the loaded filename and data stored with in the cartridge
3) Storage space is essentially limitless.
Basic rundown of what happens with LOAD / RUN
LOAD: Sets the active filename for the cartridge, loads the 32k cartridge into the cartridge buffer, and loads the first 0x4300 bytes into memory (tiles, maps, sounds and patterns)
RUN: Disposes of existing Lua runtime state, discarding any global values, functions, etc, then parses and executes the code stored in the cartridge buffer.
This also means that whatever is in ram at 0x4300~0x7FFF is PRESERVED BETWEEN RUNS. The only downside here is that LOAD will print garbage to the screen, so write to this area after you LOAD, but before you RUN the LOADed cartridge.
So, if you want to get past the data storage limits, use a LOAD without running first.
If you want to get past the CODE limits, store persistent data at 0x4300~0x7FFF and then RUN.
So, I've been spending quite a bit of time these last few days trying to figure out the innerworkings of the PICO-8, especially the cartridge storage and I've basically got everything figured out at this point. One thing I thought might be helpful is letting everyone get a glimpse into the compression format that is used inside of the .PNG format, so you can avoid hitting that pesky 32kB code wall!
So, first I will start by breaking down the storage format for the current version of the .PNG. Note so far this has been a clean room RE, so if I'm duplicating anything that you already know, or has been described for you, just ignore me and move on.
Cartridge data is stored in the 2 lowest bits of a color channel, so each color is 8-bits worth of data (1 byte). The channels are ordered (MSB to LSB) ARGB (this is not raw uint8 data order from the image container, so keep that in mind). The images are 160*205, giving you a theoretical size of 32800, although only 32769 bytes are used, Everything past this is discarded.
0x0000~0x42FF: Ram initalizer (simply copied to RAM on load, see manual memory map for details) 0x4300~0x7FFF: Code (version 0 is ASCII encoded, version 1 is 0x8000: File version |