Stat

From Pico-8 Wiki
Jump to navigation Jump to search
stat( n )
Returns information about the current runtime environment.
n
The ID of the information to return. (See below.)

The stat() function returns information about the current runtime environment. Each kind of information has an ID (a number), described below.

You can display this metric on the game screen with print(), write it to the console with printh(), or perhaps use it some other way to adjust the behavior of your program.

Note that any ID values not listed below are unknown or have no defined meaning, returning 0.

{0…2} Memory and CPU usage

stat(0) returns the memory usage:

  • This number is in KiB. 1 would represent 1024 bytes allocated. The fractional portion is accurate, so 1.875 would represent 1920 bytes.
  • Accounts for all Lua data, such as variables and tables.
  • Ranges from 0 to 2048. (As of 0.1.11g, Lua is allowed up to 2 MiB of data.)
  • Does not include peek()/poke()-able RAM such as sprite or video RAM.
  • Does not include cartridge ROM.

stat(1) returns the total CPU usage:

  • Begins at 0 at the start of the first _update() after the most recent _draw().
  • Progresses towards 1 as it nears time for the next frame to begin. Therefore, 1 would represent 1/30s elapsed when using _update(), or 1/60s for _update60().
  • A value higher than 1 indicates that the program has gone over its processing budget for code and drawing this frame. Frames may be dropped to compensate, or overall execution may be slowed.

stat(2) returns the system CPU usage:

  • This number uses the same range and behavior as stat(1).
  • This number only increases while work is being done in unseen code. For instance, clearing the screen about 70 times would push stat(2) past 1.0. Almost all of the work would be done inside of calls to cls(), which is a system call. However, spending the whole frame calculating pi wouldn't budge stat(2), because it wouldn't involve any system calls.

{3} Current Display

stat(3) returns the current display being accessed (set by _map_display(n)), ranging from 0-3.

{4} Clipboard contents

The system clipboard contents can be found in stat(4), as a string.

See Clipboard for full details on usage and limitations.

{5} PICO-8 version

The running PICO-8 version ID can be found in stat(5), as a number.

This is the same number as the "version" ID in the .p8 file. (e.g. 33 for 0.2.3)

{6} Parameter string from a third-party load

When a cart calls load() to load another cart, it can provide an arbitrary string as the third argument. This string is accessible to the loaded cart by calling stat(6). If PICO-8 is run from the command line, the -p flag can be used to provide this argument instead.

If the load() call also included a breadcrumb string, the loaded cart can access this with stat(100).

{7} Frame rate

stat(7) returns the current frame rate, as the number of frames rendered per second.

Specifically, the frame rate is the number of times per second PICO-8 is able to call the game's _draw() function in the game loop (if provided), or the number of times the game calls flip(). When using the game loop, the expected frame rate is 30 frames per second (fps) with an _update() function or 60 fps with an _update60() function, but may be fewer if the draw or update functions take too long and PICO-8 skips _draw() calls to catch up. This makes frame rate an interesting measure of game performance for games that do a lot of calculation between frames.

{8} Target frame rate

stat(8) returns the intended frame rate of the system, currently either 30 or 60. See _update() for more details on how to choose one or the other.

{9} PICO-8 frame rate

stat(9) appears to return the number of times the PICO-8 application itself has updated in the last second. This will tend to be similar to the refresh rate of the physical display, but may vary with system load. It is independent of the current game's update rate. If you configure PICO-8 to show an FPS indicator, this is the value being shown.

{10} Unknown

stat(10) returns a number, 0 at the time of writing, whose purpose is unknown.

{11} Number of displays

stat(11) returns the number of enabled displays, ranging from 1-4.

{12…15} Pause menu location

stat(12) through stat(15) represents the coordinate position of the pause menu if the player were to pause the game at that moment, as the X and Y coordinates of the upper left corner and bottom right corner, respectively.

Various things determine the contents of the pause menu dynamically, including menuitem() calls, load() parameters, whether the game was loaded with Splore, and possible future extensions. PICO-8 calculates the size and position of the menu based on its contents.

{16…26} Sound and music status (deprecated)

0.2.4+: These are deprecated; use the more stat(46..56). stat(16..26) returns the same information as stat(46..56), but stat(16..26) are calculated with less precision.

Caution: There is a bug in stat(24) in PICO-8 versions prior to 0.1.12: If no music is currently playing, an ID of 0 will be returned instead of -1, but this does not mean music pattern 0 is actively playing. In affected versions, workarounds like checking stat(25) are necessary to detect whether music is actually playing.

{27} Unknown

stat(27) return values 0 at the time of writing, whose purposes are unknown.

{28} Raw keyboard

stat(28,scancode) returns true if key with scan code scancode is pressed. See SDL documentation (or SDL_scancode.h) for numeric values.

The following code tests all 256 raw keycodes:

function _init()
  poke(0x5f2d,1)
end
function _draw()
  cls()
  for y=0,15 do
    for x=0,15 do
      circfill(4+x*8,4+y*8,3,
          stat(28,x+y*16) and 7 or 1)
    end
  end
end

{29} Unknown

stat(29) return values 0 at the time of writing, whose purposes are unknown

{30…39} Mouse and Keyboard

From inside a cart, an experimental mode can be enabled for platforms that have mouse and keyboard attached:

-- enables mouse and keyboard via devkit mode
poke(0x5f2d, flags)
-- flags are a bitfield where:
--  0x1 enable
--  0x2 mouse buttons trigger btn(4)..btn(6)
--  0x4 pointer lock (use stat 38..39 to read movements)


Once enabled, stat(...) can be used to read mouse and keyboard values:

  • stat(30) -> a boolean value representing if a keypress has occurred
  • stat(31) -> string name of the keypress when stat(30) is true
    • note: stat(31) returns a second value, which seems to always be 0 (meaning unknown). This will cause problems if you try to call ord(stat(31)) directly, because ord takes 2 arguments
  • stat(32) -> mouse X
  • stat(33) -> mouse Y
  • stat(34) -> mouse buttons bitfield
    • 0x1: left button
    • 0x2: right button
    • 0x4: middle button
  • stat(35) -> number representing a horizontal mouse wheel or two-finger-swipe event.
    • The value returned is in pixels: swiping left returns a positive value, swiping right returns a negative value.
  • stat(36) -> number representing a mouse wheel event. Possible values are:
    • 1 roll up
    • 0 no roll
    • -1 roll down
  • stat(37) -> number ranging from 0-3 seeming to represent the mouse acceleration delta


The next two stat results will not return unless flag 0x4 has been set

  • stat(38) -> relative x movement in host desktop pixels
  • stat(39) -> relative y movement in host desktop pixels


Here's a helpful example function allowing you to dynamically set the appropriate mouse/keyboard flags for your Pico-8 project.

-- enable = enables devkit mode with mouse and keyboard
-- btn_emu = left,right,middle mouse buttons -> btn(❎),btn(🅾️),btn(6)
-- ptr_lock = enable pointer lock
function mkb_init(enable, btn_emu, ptr_lock)
  -- pass args as bitfield into hardware register
  poke(0x5f2d,(enable   and 1 or 0)
             |(btn_emu  and 2 or 0)
             |(ptr_lock and 4 or 0))
end

-- initialize the mouse/keyboard with desired flags
function _init()
  mkb_init(true,false,true) -- enabled, no button emulation, locked
end

When the user presses one or more keys, PICO-8 sets stat(30) to true, and then sets stat(31) to the next keypress to be read. When the application sees stat(30) is true and reads the keypress via stat(31), PICO-8 will then refresh the value of both stats, based on whether or not there are more keypresses remaining to be reported. This can be checked multiple times per frame to fully read all keys pressed since last frame.

An example of reading the keyboard:

function _update()
  while stat(30) do
    handle_keypress(stat(31))
  end
end

Some special keys can be detected using these standard Lua escape sequences:

Key Sequence Notes
Backspace "\b"
Enter "\r" By default, Enter and P bring up the pause menu. If either is detected, poke(0x5f30,1) can be used to temporarily suppress this action.
Esc "\27" or "\x1b" Only seen when the user is exiting the pause menu or halting execution. Neither action can be suppressed.
Tab "\t"

Modifier keys:

The Shift key is not reported separately. Instead, it changes the character that is returned from stat(31). For instance, if the player presses Shift+H, a double-wide heart character will be returned. Other modifier keys are invisible to PICO-8 programs.

Usage notes:

Conceptually, PICO-8 is a small game console, similar to an NES or GameBoy. It may be running on a machine without a keyboard or mouse connected, such as a game cabinet with only game controllers, or even a small system-on-a-chip implementation with integrated controls. For that reason, this is referred to as "devkit mode", intended for debugging and tools. It's not forbidden to write games that use this mode, but games that do may find a limited audience.

If PICO-8 is running in a context where mouse and keyboard cannot be safely relied on (for instance, on the BBS), it will display a notice that devkit mode has been enabled. This is so the user will not be confused if they cannot control the game. This notice will come up the first time a relevant stat() is read; this knowledge can be used to make sure the notice will not obscure something important at a bad time.

{46…56} Sound and music status

stat(46) through stat(56) return audio status. These are the newer versions (0.2.4+) of stat(16) through stat(26), which return the same data with less precision.

  • stat(46) through stat(49) return the index of the sound effect currently playing on the four channels, respectively.
    • If no sound is playing on the channel, stat() returns -1.
  • stat(50) through stat(53) return the note number (0 through 31) of the sound effect currently playing on the four channels, respectively.
    • If no sound is playing on the channel, stat() returns -1.
  • stat(54) is the music pattern ID currently being played, as a result of the most recent call to music().
  • stat(55) is the number of music patterns played since the most recent call to music().
  • stat(56) is the number of ticks (notes or rests) played on the current pattern.

Notes from zep on precision:

  • stat(16..26): "Note that the row number may not be very precise depending on what is going on with the host operating system's audio mixer. Also in the case of the web player which needs to have a large mixing buffer, the result is often slightly earlier that what is audible." - zep
  • stat(46..56): "It's still not perfect, but requires less to work to get pretty good sync going." - zep

{57…71} Unknown

  • stat(57..63) -> 0?
  • stat(64..71) -> nil?

{80…85,90…95} Time of day

A PICO-8 cart can access the current time of day, in the host system's local time zone:

  • stat(90) -> year, e.g. 2018
  • stat(91) -> month of year, 1-12
  • stat(92) -> day of month, 1-31
  • stat(93) -> hour of day, 0-23
  • stat(94) -> minute of hour, 0-59
  • stat(95) -> second, 0-61 (usually 0-59, see note below)

A corresponding set of values, but in UTC time, are available at stat(80) through stat(85).

Note: PICO-8 and/or Lua appear to be using the standard C tm_time interface to provide these values, and as such, you should be aware that the current second is allowed to go past 59, to 60 or 61, in the event of leap seconds occurring.


{99} Garbage Collection (Raw)

While stat(0) performs garbage collection in order to obtain a meaningful result, stat(99) returns the raw value of the same.

{100…102} BBS information

The following are not officially documented but appear to be related to BBS functionality:

  • stat(100) -> Returns breadcrumb label.
  • stat(101) -> For BBS carts, stat(101) returns the current BBS ID. (per zep on Twitter)
  • stat(102) -> For BBS carts, returns site (e.g. "www.lexaloffle.com"). For local carts, returns 0.

{103…107} Unknown

The following are mostly unknown, but may be related to BBS functionality, the PCM audio channel, or something else entirely:

  • stat(103) -> Unknown, returns some kind of SHA1 hash (known to be affected by cart contents)
  • stat(104) -> Unknown, returns false in limited testing.
  • stat(105) -> Unknown, returns 0 in limited testing.
  • stat(106) -> Unknown, returns some kind of SHA1 hash. (known to be affected by cart contents & load parameter)
  • stat(107) -> Unknown, returns 0 in limited testing.

(If you know more details about any of these, please update this section.)

{108…109} 5kHz PCM Audio

By writing to the undocumented 0x808 serial port, you can output PCM audio samples.

Specifically, up to 2048 bytes of unsigned 8-bit 5512.5 Hz audio can be buffered by using serial(0x808,address,length); more info here.

  • stat(108) -> Returns the number of PCM sample bytes currently buffered for output. Ranges from 0 to 2047.
  • stat(109) -> Returns the PICO-8 app's audio buffer size divided by 4. Generally, the PCM buffer must be filled by at least stat(109)-stat(108) samples to reduce CPU usage as much as possible while resulting in little amounts of crackling.

{110} Frame-by-frame mode flag

This stat is zero under normal conditions, and non-zero when PICO-8 is in frame-by-frame mode.

Frame-by-frame mode is engaged by using "." at the command prompt, which effectively calls resume to continue execution and then stop() before the next frame begins. It may be used repeatedly to advance a game frame-by-frame.

This is an example of how stat(110) might be used to insert extra breakpoints before the next frame:

function _update()
  
  if(stat(110)>0) stop("before move: " .. player.x .. "," .. player.y)
  move_player()
  if(stat(110)>0) stop("after move: " .. player.x .. "," .. player.y)
  
end

{111…119} Unknown

stat(111…119) return a number, 0 at the time of writing, whose purpose is unknown.

{120...121} Bytestream Availability

Boolean values used with the GPIO functionality of Pico-8. stat(120) and stat(121) will return a value of false unless data is available to be transferred between Pico-8 and the host operating system. They are not useful in many situations, such as while running carts on the BBS or running exported carts.

To obtain the bytestreams necessary to make use of these two results, you would use the serial() function.

  • stat(120) -> returns true when data is available for a dropped file (obtained with serial() at channel 0x800)
  • stat(121) -> returns true when data is available for a dropped image (obtained with serial() at channel 0x802)

{122…???} Unknown

  • stat(122) -> Returns false. Possibly related to serial() bytestreams.
  • stat(124) -> Returns current path.

(If you know more details about any of these, please update this section.)

Examples

function _update()
  -- game update code...
end

function _draw()
  cls()

  -- game draw code...

  print('mem:'..stat(0), 0, 0, 7)
  print('cpu:'..stat(1), 0, 6, 7)
end

See also