Alas, the simplicity really does set me back in one category -- persisting data.
It seems like looking at the manual and some carts, the main method is to write binary data to a memory address with peek/poke/memcpy/memset. As a JS guy, I've worked with plenty network protocols and IO between filesystems, databases, and APIs, but when it comes to working directly with binary data and using memory addresses, I'm kind of lost. I've rarely encountered a situation that required me to use bitwise operators or manipulate binary values directly.
That being said, I have looked around the BBS and attempted to find something to guide me on this quest to save the game state. I've seen a couple of threads (like this one) and downloaded a few carts like Shodo and Picodachi to see how it's done. Despite my best attempts to grok the concept, it's very difficult to understand the following:
1) Which memory addresses to use and how to prevent writing over previously written addresses. 0x5fc0-0x6000 seems like the sort of default range. But what are the intermediary addresses?
2) Determining the proper number of bytes required to save an arbitrary number/string/boolean and determining how many to read back.
3) How you'd go about using converting variables into binary before write and back again upon read.
4) If there's a feasible way to convert and store functions or tables as well.
I'd really appreciate any advice, tutorials, carts or code snippets that could help me wrap my head around how all this works in Picoland. Thanks! :)
Can you elaborate on what you're trying to do?
If you just want to store dynamic data in RAM during the run of the cart: values created by Lua code and stored in variables have their own 256 K memory region inaccessible to peek and poke. There is no need to serialize the Lua data structures for storage by poking into the addressable user RAM. Just keep the data structures in global variables.
The addressable RAM is useful for dynamically modifying, generating, or unpacking graphics, music, and sound data, and there is an additional memory region for arbitrary uses, such as to copy cart data out of graphics/sound memory for further manipulation. But unless the data is especially binary in nature (a collection of bytes), you might as well use the Lua memory and let the runtime environment manage the storage format. If you just want to use the cart data for its intended purposes, it's much easier to use the provided functions for drawing and playing sounds and music.
If you're actually asking about the 64-value (256-byte) cartdata region that persists between runs of the cart, there's not really a lot of space there for serializing big values. For small values, it's easiest to just define a compact storage format appropriate to your use case.
Answering your question more directly:
Numbers: Pico-8 number values are 16:16 fixed point values. You can use bit shifting and masking to isolate each of the four sets of 8 bits as separate byte values, then poke them into RAM. Reading would be the same process in reverse.
Strings: To go from bytes to characters, you can use a string containing every character in order and index into it. To go from characters to bytes, generate a reverse lookup table at runtime from the all-chars string that maps chars to indexes, then use that. Here's one version: https://www.lexaloffle.com/bbs/?tid=2420 I also use this technique somewhere in this text compression demo: https://www.lexaloffle.com/bbs/?tid=2776
Booleans: 1 = true and 0 = false is a standard encoding.
Packing table data into bytes is possible, but difficult to justify. Unpacking tables from string literals is a bit more useful because it uses fewer tokens than table literals, and has been attempted, e.g.: https://www.lexaloffle.com/bbs/?tid=2423
[Please log in to post a comment]