427 lines
18 KiB
Plaintext
427 lines
18 KiB
Plaintext
PICO-8 Advanced Manual
|
||
By ReplayCoding
|
||
|
||
|
||
:: TOC
|
||
Video Modes
|
||
Palette Changes
|
||
Undocumented Functions
|
||
Memory Layout
|
||
Draw State
|
||
Hardware State
|
||
Javascript Export
|
||
PNG Export
|
||
Audio and SFX
|
||
Cart Format
|
||
BBS
|
||
|
||
|
||
-----------
|
||
:: Video modes
|
||
Pico-8 has multiple video modes, the default being 128x128. Different video modes can be enabled by setting a value at at 0x5F2C. The multiple modes are listed below
|
||
|
||
|
||
-- from https://neko250.github.io/pico8-api/
|
||
poke(0x5f2c, 0) -- standard, 128x128
|
||
poke(0x5f2c, 1) -- horizontal stretch, 64x128
|
||
poke(0x5f2c, 2) -- vertical stretch, 128x64
|
||
poke(0x5f2c, 3) -- zoomed, 64x64
|
||
poke(0x5f2c, 4) -- standard, 128x128
|
||
poke(0x5f2c, 5) -- mirror left half
|
||
poke(0x5f2c, 6) -- mirror top half
|
||
poke(0x5f2c, 7) -- mirror top-left quarter
|
||
|
||
|
||
:: Palette changes
|
||
The palette can be changed by modifying the PICO8.DAT file. Search for the hex string listed below and replace it with a palette of your own choosing. As far as I know, you can’t do this with exported carts.
|
||
|
||
|
||
-- Hex color palette
|
||
000000 1d2b53 7e2553 008751 ab5236 5f574f c2c3c7 fff1e8 ff004d ffa300 ffec27 00e436 29adff 83769c ff77a8 ffccaa
|
||
-- Actual colors with RGB
|
||
From https://www.romanzolotarev.com/pico-8-color-palette/
|
||
#000000 (0, 0, 0) black
|
||
#1D2B53 (29, 43, 83) dark-blue
|
||
#7E2553 (126, 37, 83) dark-purple
|
||
#008751 (0, 135, 81) dark-green
|
||
#AB5236 (171, 82, 54) brown
|
||
#5F574F (95, 87, 79) dark-gray
|
||
#C2C3C7 (194, 195, 199) light-gray
|
||
#FFF1E8 (255, 241, 232) white
|
||
#FF004D (255, 0, 77) red
|
||
#FFA300 (255, 163, 0) orange
|
||
#FFEC27 (255, 236, 39) yellow
|
||
#00E436 (0, 228, 54) green
|
||
#29ADFF (41, 173, 255) blue
|
||
#83769C (131, 118, 156) indigo
|
||
#FF77A8 (255, 119, 168) pink
|
||
#FFCCAA (255, 204, 170) peach
|
||
|
||
|
||
|
||
|
||
:: Undocumented functions
|
||
-- From http://pico-8.wikia.com/wiki/Category:Undocumented_API
|
||
holdframe() forces display not to update until flip() is called
|
||
_set_fps(framerate) sets the current framerate (15, 30, or 60 FPS)
|
||
_update_buttons() refreshes gamepad state
|
||
|
||
|
||
-- Other functions [ seem to do nothing ]
|
||
_startframe
|
||
_get_menu_item_selected
|
||
_update_framerate
|
||
_set_mainloop_exists
|
||
_mark_cpu
|
||
_set_paused
|
||
|
||
|
||
|
||
|
||
:: Memory layout
|
||
There are 32 kilobytes of addressable memory. Most of the the stuff is self-explanatory, but I will explain the draw and hardware state in the following sections.
|
||
-- Taken from the manual
|
||
0x0 gfx
|
||
0x1000 gfx2/map2 (shared)
|
||
0x2000 map
|
||
0x3000 gfx_props
|
||
0x3100 song
|
||
0x3200 sfx
|
||
0x4300 user data
|
||
0x5e00 persistent cart data (256 bytes)
|
||
0x5f00 draw state
|
||
0x5f40 hardware state
|
||
0x5f80 gpio pins (128 bytes)
|
||
0x6000 screen (8k)
|
||
|
||
|
||
:: Draw state
|
||
-- Taken from http://pico-8.wikia.com/wiki/Memory
|
||
0x5f00-0x5f1f: Palette filtering (pal(), palt()). 0x5f00-0x5f0f is palette #0 (the draw palette), and 0x5f10-0x5f1f is palette #1 (the screen palette). The index is the original color number, and bits 0-7 are the replacement color number. Bit 8 indicates whether the color should be treated as transparent (1=true).
|
||
|
||
|
||
0x5f20-0x5f23: Clipping region (clip()). The bounding box is described in four bytes, as coordinates from the top-left origin (0,0): 0x5f20=x1, 0x5f21=y1, 0x5f22=x2, 0x5f23=y2. Notice that this differs from the arguments to clip(): x2=x1+w, y2=y1+h.
|
||
|
||
|
||
0x5f24: (unknown, please update this if you know)
|
||
|
||
|
||
0x5f25: Pen color (color()). The bottom 4 bits are the current color index, from 0-15. The top 4 bits are typically 0 and unused, but when using fill patterns, they will be the secondary color index.
|
||
|
||
|
||
0x5f26-0x5f27: Print cursor (cursor()). 0x5f26=x, 0x5f27=y.
|
||
|
||
|
||
0x5f28-0x5f2b: Camera position (camera()). The x and y offsets are stored as 16-bit signed integers with the least significant byte first. 0x5f28-0x5f29=x offset, 0x5f2a-0x5f2b=y offset.
|
||
|
||
|
||
0x5f2c: Bit 2 (value 4) controls what this value does. If clear, bits 0 and 1 (values 1 and 2) control horizontal and vertical pixel-doubling. If bit 2 is set, they instead control screen mirroring, left-to-right and top-to-bottom, respectively. Thus, a value of 3 would produce a 64x64 screen, and a value of 7 would produce a 4-way kaleidoscope effect.
|
||
|
||
|
||
0x5f2d: If set to 1, devkit mode is enabled, allowing mouse coordinates and keyboard status to be checked via the stat() command.
|
||
|
||
|
||
0x5f2e: (undocumented, but confirmed by zep) If set to 1, causes the current palette scheme (see pal() and/or the 0x5f00-0x5f1f memory range, above) to persist instead of being reset at the end of the program.
|
||
|
||
|
||
0x5f30: (undocumented) If set to 1, suppresses the next attempt to bring up the pause menu. This can be done as late as discovering the pause button has been pressed this frame:
|
||
|
||
|
||
if(band(btn(),64)!=0) poke(0x5f30,1)
|
||
|
||
|
||
0x5f31-0x5f33: Fill pattern (fillp()). 0x5f31-0x5f32 contain the fill pattern as a 16-bit unsigned integer with the least significant byte first. 0x5f33's bit 0 is the related transparency bit.
|
||
|
||
|
||
0x5f34: If set to 1, changes the PICO-8 drawing API to accept color values that incorporate not just one or two colors, but optionally include information about both the fill pattern and transparency right in the color value. From the manual:
|
||
|
||
|
||
-- bit 0x1000.0000 means the non-colour bits should be observed
|
||
-- bit 0x0100.0000 transparency bit
|
||
-- bits 0x00FF.0000 are the usual colour bits
|
||
-- bits 0x0000.FFFF are interpreted as the fill pattern
|
||
|
||
|
||
The remaining values up to 0x5f3f otherwise appear to be either undocumented or reserved for future use.
|
||
|
||
|
||
:: Hardware state
|
||
-- Also from http://pico-8.wikia.com/wiki/Memory
|
||
These memory addresses represent or control various aspects of the hardware. These are undocumented.
|
||
0x5f40: Bitmask to half each audio channel's clock rate. Bit 0 (value 1) is for Channel 1, Bit 1 (value 2) is for Channel 2, and so on. The upper four bits are not used.
|
||
0x5f41: Bitmask to enable reverb for each channel. The bits have the same effect as 0x5f40.
|
||
0x5f42: Bitmask to distort the waveforms of each channel.
|
||
0x5f43: Bitmask to enable a lowpass filter for each channel.
|
||
The remaining values up to 0x5f7f are undocumented.
|
||
|
||
|
||
:: Javascript export -- html
|
||
When you export a cart in PICO-8 there are two variables that you can set: pico8_gpio and pico8_buttons. pico8_gpio is a list of 128 8-bit numbers. You can access it by peeking at 0x5f80 to 0x5fff. pico8_buttons is a little endian array of 8 binary numbers.
|
||
|
||
|
||
:: Javascript export -- js
|
||
The javascript file is an emscripten compiled program. The cart data is stored at _cartdat in compressed form. The compression algorithm is an LZ-like algorithm. A python program is at https://git.io/fx6Gx
|
||
|
||
|
||
:: PNG export
|
||
PICO-8 also allows you to export carts in a PNG format. The cart is stored in the two least significant bits of each color channel in the order ARGB (alpha, red, green, blue). The PNG is 160x205, so we have at most 32800 bytes (although only 32769 bytes are actually used.
|
||
-- Taken from https://www.lexaloffle.com/bbs/?tid=2400
|
||
|
||
|
||
|
||
0x0000~0x42FF: Ram initializer (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
|
||
|
||
|
||
|
||
|
||
|
||
This is all fairly straight forward and should not surprise anyone. If the version is 1, it means the code is likely compressed and will follow the following format:
|
||
|
||
|
||
|
||
0x0~0x3: the string ":c:\x00"
|
||
0x4~0x5: length of code (decompressed, big-endian)
|
||
0x6~0x7: always zero (could be used for compression scheme in the future?)
|
||
0x8+: Compressed data
|
||
|
||
|
||
|
||
|
||
|
||
Compressed data is an LZ like format, supporting a minimized character set so some text takes less space. It takes 8-bit codes to generate a chain of characters, this loops until the output data matches the value located in the header.
|
||
|
||
|
||
The codes are as follows
|
||
|
||
|
||
|
||
0x00 xx: "Extended" code. The following byte is copied to the output stream
|
||
0x01: Emit a new line
|
||
0x02~0x3B: Maps to: " 0123456789abcdefghijklmnopqrstuvwxyz!#%(){}[]<>+=/*:;.,~_"
|
||
0x3C~0xFF xx: Copy from buffer
|
||
|
||
|
||
|
||
|
||
|
||
The 'copy' call copies N bytes from -X bytes from the end of the current stream.
|
||
|
||
|
||
|
||
offset = (code - 0x3C) * 16 + (code2 & 0xF);
|
||
length = (code2 >> 4) + 2;
|
||
|
||
|
||
|
||
|
||
|
||
For a working example, this is what "Hello hello" would be encoded as.
|
||
|
||
|
||
|
||
// Header
|
||
"3a", "63", "3a", "0", "0", "b", "0", "0",
|
||
// Encoded text
|
||
"0", "48", // "H"
|
||
"11", "18", "18", "1b", // "ello"
|
||
"2", // [space]
|
||
"14", // "h"
|
||
"3c", "26" // "ello", 4 bytes copied from 6 bytes ago
|
||
|
||
|
||
|
||
|
||
|
||
Note: There are no capital letters in the short form encoding, so don't ever use them in your program. Also the maximum number of characters that can be copied from a previous point in the stream is 17 characters, so try to keep your variable names below this threshold. Interestingly enough, minus signs are also not supported by the base character set.
|
||
|
||
|
||
:: Audio and SFX
|
||
-- Taken from https://www.lexaloffle.com/bbs/?tid=2341
|
||
I didn't see this documented anywhere, and I had to work it out for my drumbeat cart, so I thought I'd save everyone else the trouble and publish what I know.
|
||
SFX:
|
||
A sound effect is stored in 68 bytes. Sfx 0 starts at address 0x3200, sfx 1 at address 0x3244, and so on.
|
||
The first 64 bytes (offset 0..63) store the 32 notes of the sound effect as described below.
|
||
Offset 64 is a byte that determines the editor mode: 0 for graph mode, nonzero for tracker mode. This has no effect on playback, just on the mode opened in the editor by default.
|
||
Offset 65 stores the playback speed. (Higher values correspond to slower playback, so "speed" isn't exactly the right name, but that is what is used in the editor.)
|
||
Offsets 66 and 67 store the loop begin and loop end times. A sound effect contains only 32 notes, but these times may be set to any byte value. All times past 32 simply play silence.
|
||
Note format:
|
||
A note is stored in 16 bits, two bytes little-endian.
|
||
Bits 0..5 store the pitch, ranging from 0 (C in octave 0) to 63 (D# in octave 5). The tracker mode editor can only enter pitches up to 60 (C 5), but it can display and play higher pitches.
|
||
Bits 6..8 store the instrument; the values are the same as in the tracker mode editor.
|
||
Bits 9..11 store the volume. Volume 0 corresponds to silence.
|
||
Bits 12..14 store the effect; the values are the same as in the tracker mode editor.
|
||
Bit 15 appears to be unused.
|
||
Example code:
|
||
|
||
|
||
|
||
|
||
|
||
|
||
-- This code is public domain, feel free to copy, use, and modify however you'd like
|
||
|
||
function make_note(pitch, instr, vol, effect)
|
||
return { pitch + 64*(inst%4) , 16*effect + 2*vol + flr(instr/4) } -- flr may be redundant when this is poke'd into memory
|
||
end
|
||
|
||
function get_note(sfx, time)
|
||
local addr = 0x3200 + 68*sfx + 2*time
|
||
return { peek(addr) , peek(addr + 1) }
|
||
end
|
||
|
||
function set_note(sfx, time, note)
|
||
local addr = 0x3200 + 68*sfx + 2*time
|
||
poke(addr, note[1])
|
||
poke(addr+1, note[2])
|
||
end
|
||
|
||
function get_speed(sfx)
|
||
return peek(0x3200 + 68*sfx + 65)
|
||
end
|
||
|
||
function set_speed(sfx, speed)
|
||
poke(0x3200 + 68*sfx + 65, speed)
|
||
end
|
||
|
||
function get_loop_start(sfx)
|
||
return peek(0x3200 + 68*sfx + 66)
|
||
end
|
||
|
||
function get_loop_end(sfx)
|
||
return peek(0x3200 + 68*sfx + 67)
|
||
end
|
||
|
||
function set_loop(sfx, start, end)
|
||
local addr = 0x3200 + 68*sfx
|
||
poke(addr + 66, start)
|
||
poke(addr + 67, end)
|
||
end
|
||
|
||
|
||
|
||
Music format:
|
||
A music pattern consists of four channels, each of which may have a sound effect played on it, plus some playback control flags.
|
||
The patterns are stored in four bytes, with pattern 0 at address 0x3100. Each byte describes one channel.
|
||
Bits 0..5 are the sfx to be played on the channel.
|
||
Bit 6 is mute. If it is 0, the channel is played; if it is 1, the channel is silenced.
|
||
Bit 7 contains the playback control flags:
|
||
Byte 0 bit 7 is the loop start flag
|
||
Byte 1 bit 7 is the loop end flag
|
||
Byte 2 bit 7 is the stop flag
|
||
Byte 3 bit 7 is unused
|
||
Playback control is as follows (I haven't tested all possibilities here):
|
||
When a pattern reaches the end, if the stop flag is set on this pattern, playback stops.
|
||
Otherwise, if the loop end flag is set on this pattern, move back until a pattern with the loop start flag set is found, and continue playback from there.
|
||
Otherwise, continue playback with the next pattern.
|
||
:: Cart format
|
||
The format of PICO-8 carts is quite simple. There are seven sections in total: __lua__; __gfx__; __gff__; __label__; __map__; __sfx__; __music__. These sections are described below:
|
||
|
||
|
||
-- header
|
||
Each cart begins with the following header:
|
||
pico-8 cartridge // http://www.pico-8.com
|
||
version [#]
|
||
|
||
|
||
|
||
|
||
-- __lua__
|
||
The __lua__ section contains the code that you write in the code editor. Glyphs may not show properly in an external code editor.
|
||
|
||
|
||
-- __gfx__
|
||
-- Taken from http://pico-8.wikia.com/wiki/P8FileFormat
|
||
The spritesheet section begins with the delimiter __gfx__.
|
||
The spritesheet is represented in the .p8 file as 128 lines of 128 hexadecimal digits. Each line is a pixel row, and each hexadecimal digit is a pixel color value, in pixel order.
|
||
This differs from the in-memory representation in that the 4-bit hexadecimal digits appear in pixel order in the .p8 file, while pairs are swapped (least significant nybble first) in memory. This allows you to identify and draw images using hex digits with a text editor, if you like.
|
||
A cart is allowed to use the bottommost 128 sprites as the bottommost 128x32 tiles of the map data. That is, if the cart calls map() with coordinates in that region, the data is read from the bottom of the spritesheet, and the map editor can view this memory either way.
|
||
In the .p8 file, this data is always saved in the __gfx__ section, even if the cart uses it as map data. Note that this is encoded as a linear series of 4-bit pixels, rather than a series of 8-bit bytes. Tools reading this section and converting to byte format should treat the first hex digit of each pair as the bottom 4 bits and the second digit as the top 4 bits.
|
||
|
||
|
||
-- __gff__
|
||
The __gff__ section contains the sprite flags. The flags are stored in two lines of 128 bytes in hexadecimal. The sprite flags are ordered MSB to LSB in the editor: red=1, orange=2, yellow=4, green=8, blue=16, purple=32, pink=64, and peach=128.
|
||
|
||
|
||
-- __label__
|
||
The label section stores the label used in the p8.png format. It’s format is identical to the spritesheet.
|
||
|
||
|
||
-- __map__
|
||
-- Taken from http://pico-8.wikia.com/wiki/P8FileFormat
|
||
The map section begins with the delimiter __map__.
|
||
Map data is stored in the .p8 file as 32 lines of 256 hexadecimal digits (128 bytes). Each pair of digits (most significant nybble first) is the sprite ID for a tile on the map, ordered left to right, top to bottom, for the first 32 rows of the map.
|
||
The map area is 128 tiles wide by 64 tiles high. Map memory describes the top 32 rows. If the cart author draws tiles in the bottom 32 rows, this is stored in the bottom of the __gfx__ section.
|
||
|
||
|
||
-- __sfx__
|
||
-- Taken from http://pico-8.wikia.com/wiki/P8FileFormat
|
||
Sound data is stored in the .p8 file as 64 lines of 168 hexadecimal digits (84 bytes, most significant nybble first), one line per sound effect (0-63).
|
||
|
||
|
||
The byte values (hex digit pairs, MSB) are as follows:
|
||
|
||
|
||
byte 0: The editor mode: 0 for pitch mode, 1 for note entry mode.
|
||
byte 1: The note duration, in multiples of 1/128 second.
|
||
byte 2: Loop range start, as a note number (0-63).
|
||
byte 3: Loop range end, as a note number (0-63).
|
||
bytes 4-84: 32 notes
|
||
|
||
|
||
Each note is represented by 20 bits = 5 nybbles = 5 hex digits. (Two notes use five bytes.) The nybbles are:
|
||
|
||
|
||
nybble 0-1: pitch (0-63): c-0 to d#-5, chromatic scale
|
||
nybble 2: waveform (0-F): 0 sine, 1 triangle, 2 sawtooth, 3 long square, 4 short square, 5 ringing, 6 noise, 7 ringing sine; 8-F are the custom waveforms corresponding to sound effects 0 through 7 (Pico-8 0.1.11 "version 11" and later)
|
||
nybble 3: volume (0-7)
|
||
nybble 4: effect (0-7): 0 none, 1 slide, 2 vibrato, 3 drop, 4 fade_in, 5 fade_out, 6 arp fast, 7 arp slow; arpeggio commands loop over groups of four notes at speed 2 (fast) and 4 (slow)
|
||
|
||
|
||
Note that this is very different from the in-memory layout for sound data.
|
||
|
||
|
||
-- __music__
|
||
Taken from http://pico-8.wikia.com/wiki/P8FileFormat (may be incorrect, please tell me is so)
|
||
The sound effects section begins with the delimiter __music__.
|
||
|
||
|
||
Music patterns are represented in the .p8 file as 64 lines, one for each pattern. Each line consists of a hex-encoded flags byte (two digits), a space, and four hex-encoded one-byte (MSB-first) sound effect IDs.
|
||
|
||
|
||
The flags byte has three flags on the lower bits (the higher five bits are unused):
|
||
|
||
|
||
0: begin pattern loop
|
||
1: end pattern loop
|
||
2: stop at end of pattern
|
||
|
||
|
||
The sound effect ID is in the range 0-63. If a channel is silent for a song pattern, its number is 64 + the channel number (0x41, 0x42, 0x43, or 0x44).
|
||
|
||
|
||
Note that this is different from the in-memory layout for music data.
|
||
|
||
|
||
:: BBS
|
||
In the following sections, you will find details about API endpoints used by the splore command in PICO-8. Some of them are internal and are not used as far as I know.
|
||
|
||
|
||
https://www.lexaloffle.com/bbs/snippet.php?cart_id=${cart_id}:
|
||
This will return a hex encoded P8.PNG file wrapped by a bbcode style tag called cart. If you add a "src" parameter with any value to the url, it will return the source code of the cart.
|
||
|
||
|
||
https://www.lexaloffle.com/bbs/cpost_lister3.php?max={$max_results}&cat=${bbs_area}&start_index=${start_index}&sub=${sub_section}&version=${version_of_asking_program}&search=${search}&cfil=1:
|
||
This will return the results of the splore command. It is returned as a PNG file. This PNG presumably contains the search results (No evidence here, just guessing).
|
||
|
||
|
||
https://www.lexaloffle.com/bimg/pi/pi${image_number}.png:
|
||
This will return one of the many default profile images. The are 32 different pictures starting from 0.
|
||
|
||
|
||
There are a couple others, but I will not document them here.
|
||
|
||
|
||
:: End
|
||
Ummm, what are you even supposed to write here anyway? Is "I hope you find this useful" good enough? Uhhhh..... |