Download 0.2.0i at lexaloffle or via Humble, or for PocketCHIP.

Alright, let's do this! PICO-8's core specification is complete, and it appears to do what it says on the tin. So I'm calling it:

PICO-8 is in Beta!

The main purpose of 0.2 is to finish freezing the core of PICO-8 -- the api, cpu counting, specs, cart format, memory layout, program behavior, backwards and future-compatibility should no longer change.

Earlier attempts at settling on a fixed core in 0.1.11 and 0.1.12 failed because of technical issues creeping in and also some design decisions that just didn't sit right. It has only been due to the ongoing process of users like @Felice, @electricgryphon, @jobe, @freds72, @Eniko, @samhocevar, and many others prodding at the boundary of what PICO-8 can do -- and what it should do -- that all of those nooks and corners finally took shape. I'm really happy with the way the last pieces of PICO-8 have snapped together, and I think it has reached a point where it feels not only like it should never need to change, but that it never could have been any other way.

To make this happen required some jolting changes and a string of patches to get right, and the last few weeks PICO-8 has been in an uncomfortably liquid state. My apologies to everyone who was riding that bumpy update train (but thanks so much for the bug reports!). There might be one or two emergency patches in the next weeks, but I think any left-over quirks and design flaws will simply become part of the machine.

New Features and Changes

Character Set

PICO-8 now has a full 8-bit character set that can be accessed with CHR() to get a character by index, and ORD() to get the index from a character.

> PRINT(ORD("A")) 97 > PRINT(CHR(97)) A

All characters from 0..255 (0..15 are control characters and are not visible)

All of the new characters 16..255 can now be typed directly into the code editor. There are 3 modes that can be toggled on and off:

Katakana (ctrl-k) // type in romanji: ka ki ku ke ko

Hiragana (ctrl-j) // ditto

Puny Font (ctrl-p) // shift-letter gives you regular font

Additional characters can be accessed in the 2 kana modes with shift-0..9

SFX / Music Organiser

These can be accessed in the music editor, and give you a cart-wide view of all of the patterns or SFXes in a cart. They can be selected by shift-clicking, copied and pasted, or moved around (with ctrl-x, ctrl-v), and can also be used to visualize which SFXes are being used while music is playing.

Operators

Bitwise functions can now instead be expressed with operators. The function versions are still useful if you want nil arguments to default to 0, or just as as matter of style. But the operator versions are a little faster and often more token-efficient.

BAND(A,B) A & B BOR(A,B) A | B BXOR(A,B) A ^^ B SHL(A,B) A << B SHR(A,B) A >> B LSHR(A,B) A >>> B ROTL(A,B) A <<> B ROTR(A,B) A >>< B BNOT(A) ~A

There's also a handy integer divide, and operators to peek (but not poke)

FLR(A/B) A \ B PEEK(A) @A PEEK2(A) %A PEEK4(A) $A

Capacity Adjustments

CPU

Bitwise functions (BAND, BOR..) and PEEK functions are now a little more expensive. They can be replaced with operators counterparts to improve speed, but even they are not as fast as the 0.1.11 bitwise functions, especially when used in deeply nested expressions.

This change was necessary because I badly miscalculated how much real-world CPU load would be required to run the most bitwise-heavy carts. Lua functions cost a lot of (real) CPU compared to vm operators, and the result was carts that could completely obliterate a web-browser or real-world CPU on an older machine. This is a problem because a central goal of PICO-8 is to allow authors to forget about real-world CPUs across platforms, and just focus on the PICO-8 one.

Unfortunately, another central goal is to not mess with or break existing carts! So this was a hard choice to make. I've tried to balance this change somewhat with the introduction of native operators, tline(), and by adjusting the vm costs in a way that feels natural but also frees up some extra cycles. Along with bitwise and peek operators, the add and subtract vm instructions now also cost half as much as other vm instructions. So if you consider PICO-8 to be running at 8MHz, they cost 1 cycle per instruction, while most vm instructions cost 2.

CPU: Coroutines

Previous versions of PICO-8 handled CPU counting inside coroutines very badly. It was easy to accidentally (or intentionally) get 'free' cpu cycles when running a coroutine over a frame boundary, and in some versions the opposite could sometimes happen -- a coroutine or garbage collection would incorrectly yield the whole program causing unnecessary frame skipping. 0.2 contains a much cleaner implementation of cpu counting -- you can wrap anything in coresume(cocreate(function() ... end)), and get exactly the same result (minus the overhead of the wrapping). As a nice by-product, this has also made better STOP() / RESUME behaviour possible (see below).

Tokens and Code Compression

There is still a 8192 token limit (of course!), but negative numbers now count as a single token. This seemingly small fix, along with the new character set and bitwise operators, ultimately resulted in the code compression also improving. The result is that you can squeeze in around 10% more code.

If you want to peek behind the curtain, here's the story behind that:



The first version of PICO-8 had only a single limit for code side: 15360 characters. You can still see the remnants of this when you load a cartridge ("loaded foo.p8 (1049 chars)"). Soon after, tokens were introduced as the new limit, so that there was less incentive to bother minifying code except for really heavy carts. For this to work, the character limit was increased to 64k (so that you can get more than 2 characters per token), and the code became compressed so that it could still fit in the same 15360 byte block of a 32k cartridge. The idea was to introduce compression that was just good enough so that you'd normally hit the token limit before you hit the compressed size limit. It favored characters that were commonly used, and was intended to compress code rather than data. By virtue of being simple, it was also fast enough to compress up to 64k of code every key press, so that as you approach the compressed limit you can be altered as soon as you surpass it (which is still true). It held up pretty well, but over time, things changed. Token counting was adjusted to solve common problems, and generally allowed more code to fit within the limit. Carts included more data stuffed into the code section, often containing characters the compressor wasn't intended for. As a result, the compressed code size limit started to become as much of a pressing concern a the token limit. Carts packed to the brim would often use both to capacity. So, these 3 changes (in token counting, character set, and bitwise operators that cost less tokens), have put even more pressure on the compressor, and the old one just wasn't cutting it anymore. I really want to keep the token limit as the one that normally matters the most, and so better compression was in order. 0.2.0e features a code compressor that does about as well with any character subset, is decent at compressing byte-wise structured data stored in hex strings, and compresses around 10% better than previous versions.



Also, and this is a little embarrassing, I found some unused space in the 32k cartridge format that has been sitting dormant since its creation in 2014. It has been given to the code section, which is now 0x3d00 bytes instead of 0x3c00.

TLINE

The tline() function ("Textured Line") is a mixture of line(), sspr(), and map().

You can use it to draw a line of pixels (same as line()), where each colour is sampled linearly from an arbitrary line on the map. It's not much use out of the box, but can be used as a low-level primitive for many purposes including polygon rendering, DOOM-style floors and walls, sprite rotation, map scaling, drawing gradients, customized gradients and fill pattern schemes. I've only played with it a little bit so far, but it's really fun, and I'm looking forward to seeing what it winds up being used for.

API Changes

RND(TBL)

Give rnd() a table as an argument, and it will return a random item in that table.

BTNP Custom Repeat Delays

From the manual:

Custom delays (in frames @ 30fps) can be set by poking the following memory addresses: POKE(0x5F5C, DELAY) -- set the initial delay before repeating. 255 means never repeat. POKE(0x5F5D, DELAY) -- set the repeating delay. In both cases, 0 can be used for the default behaviour (delays 15 and 4)

Fill Patterns Constants

Use the glyphs (shift-a..z) with fillp() to get some pre-defined fill patterns.

fillp(★) circfill(64,64,16,0x7) -- transparent white

They are defined with the transparency bit set. You can use flr(★) or ★\1 to get 2-colour patterns.

fillp(★\1) circfill(64,64,16,0x7c) -- white and blue

Demo Carts

Most of the demos have been updated, including Jelpi which now has a few more monsters and tilesets to play with! Use INSTALL_DEMOS to get the new versions. 0.2 also features 2 extra pre-installed games: 8 Legs to Love by @bridgs, and Embrace by @TRASEVOL_DOG. You can install them with INSTALL_GAMES.

Tabs and Tabs

Tab characters are now optionally visible (but off by default). You can turn them on in config.txt

Press shift-enter to automatically add an END and indent.

Also, there are 8 more code tabs. Click the right or left-most visible tab to scroll.

Shape Drawing Tools

Both the map and sprite editors now have circle, line, and rectangle drawing tools. Click the tool button to cycle through those 3 modes, and hold ctrl to toggle filled vs. outline circles and rectangles.

Map Tile Moving

It's now a little easier to move sprites around that are referenced by the map. In the map editor, select the sprites you'd like to move, use ctrl-x and ctrl-v to move them, and the map cell data will also be updated to avoid broken references. This operation applies to the selected region on the map (ctrl-a to select half, and ctrl-a again to select the whole map including shared memory).

This operation is a little tricky, because it adds items to both the spritesheet undo stack and the map undo stack, so you need to manually undo both if desired. Back up first!

Splore

Every time you launch a BBS cartridge, PICO-8 will now ping the server to check for a newer version and prompt you to update if it exists. You can turn this off in config.txt

There's also a 'search thread' option in splore's cart menu, which will be useful for long jam-style threads in the future. And is already great for browsing the tweetjam thread! (You can go to the search tab in splore, and search for "thread:tweetjam")

Exporters

HTML

The HTML exports now run a lot smoother on older machines, and with more reliable page formatting and mobile controls.

.zip File Output

A common problem when exporting cross-platform binaries, is that the machine you're generating files from doesn't necessarily support the file attributes needed to run programs on other operating system. This was especially problematic for Mac and Linux binaries exported from Windows, which had no way to store the executable bit (and so end-users would have to manually fix that). To get around this problem, the EXPORT command now produces ready-to-distribute .zip files, that store the needed file attributes when unzipped on any other operating system. As a bonus, you also don't need to bother manually zipping up each platform folder! There's currently no way to add other files (e.g. documentation) though, so in that case you might need to zip the .zip along with any other desired files.

Options menu

Binary exports now come with an OPTIONS menu that shows up when a cart is paused, and includes the same settings available in HTML exports (sound, fullscreen, controls).

Activity Log

Have you ever wondered how much time you've spent in PICO-8 editors or carts? Or which carts you've played the most? 0.2 now logs your activity to activity_log.txt (in the same folder as config.txt) once every 3 seconds (unless the PICO-8 is left idle for 30 seconds). There aren't any tools to process this data yet, but it is human-readable. I should clarify: this information is not transmitted anywhere! You can turn this off in config.txt (record_activity_log 0)

Frame Advance

PICO-8 can now be resumed from exactly the point that code stopped running. For example, if you put a STOP() in your code, and then type RESUME from the commandline, the program will continue as if the STOP() had not occurred. It's possible to type in commands before resuming to modify the state of the program though, which is useful for debugging.

A common debugging tool is to slow a game down and advancing frame by frame. You can do this by stopping suspending a program with escape, and then typing . and pressing enter. This will run the program until the next flip() call and then stop again. You can get subsequent frames in the same way, or just keep pressing enter after the first one. To add additional debugging behaviour, you can use stat(110), which returns true when running in frame-by-frame mode.

That's all for now -- I hope you enjoy 0.2 and I'll catch you soon!

-- zep

Full Changelog: (scroll down to 0.2.0 for the main changes)

