BytePusher

From Esolang
Jump to: navigation, search
BytePusher.gif

BytePusher is a minimalist virtual machine invented by user:Javamannen in 2010. You can think of it as a simplified CHIP-8 with bigger memory, bigger screen with colors, and sampled sound. BytePusher has a ByteByteJump one-instruction CPU. The barebones hardware abstraction layer provides only memory-mapped pixel and audiosample buffers, keystates and a fixed framerate. All higher-level stuff (text, sprites, tiles, scrolling, sound synthesis etc.) will have to be done in software.

Features[edit]

BytePusher is:

  • Simple: BytePusher takes minimalism to the extreme. A complete VM including graphics and sound can be written in less than 100 lines of C code.
  • Cross-platform: BytePusher's simple structure makes it very easy to write a VM for any OS (or even without an OS). It would also be simple to implement in actual hardware. All programs run cycle-identical and bit-identical across all platforms.
  • Persistent: The complete machine state can be saved to a file, and later be reproduced exactly.
  • Fixed-size: BytePusher's memory size, screen resolution and keyboard layout are fixed.
  • Fixed-speed: BytePusher is specified as a realtime system with fixed framerate, CPU speed and audio rate. It executes a fixed number of instructions per frame, which means that a BytePusher program can't "hog" the host CPU.
  • 100% sandboxed: A BytePusher VM has its own fixed-size "sandbox" memory, which it can never read or write outside of.

Intended uses[edit]

  • Games: BytePusher is well suited for implementation of 2D games.
  • Demos: BytePusher could serve as an "old-school" platform for the demoscene.
  • Education: Its simplicity and directness makes BytePusher an excellent tool for learning and experimentation.

Specifications[edit]

Framerate: 60 frames per second
CPU: ByteByteJump with 3-byte addresses
CPU speed: 65536 instructions per frame (3932160 instructions per second, ~3.93 MHz).
Byte ordering: Big-endian
Memory: 16 MiB RAM
Graphics: 256*256 pixels, 1 byte per pixel, 216 fixed colors
Sound: 8-bit mono, signed values. 256 samples per frame (15360 samples per second)
Keyboard: 16 keys, organized into 4 rows by 4 columns

Colors[edit]

Each pixel takes up 1 byte. This would theoretically allow for a maximum of 256 different colors, but BytePusher's fixed palette uses only the first 216 of these. Indices 216..255 are unused; they are set to black (#000000).

The 216 colors are organized into a 6*6*6 color cube (a.k.a the "websafe" palette). Each of the red, green and blue components can have an intensity value from 0 to 5. The formula to calculate a pixel's color value is: Red*36 + Green*6 + Blue. If the actual display device has 8-bit (00h-FFh) color components, we have to multiply each intensity value by 33h when blitting to the screen:

 0  1  2  3  4  5
00 33 66 99 CC FF

Keyboard layout[edit]

BytePusher's hex keyboard has the same layout as that of CHIP-8:

1 2 3 C
4 5 6 D
7 8 9 E
A 0 B F

Memory map[edit]

The memory-mapped I/O area of BytePusher consists of 8 (!) bytes at the start of memory. Multibyte values are stored with big-endian byte ordering.

Address Bytes Description
0 2 Keyboard state. Key X = bit X.
2 3 The program counter is fetched from this address at the beginning of each frame.
5 1 A value of ZZ means: pixel(XX, YY) is at address ZZYYXX.
6 2 A value of XXYY means: audio sample ZZ is at address XXYYZZ.

Outer loop[edit]

  1. Wait for the next timer tick (60 ticks are generated per second).
  2. Poll the keys and store their states as a 2-byte value at address 0.
  3. Fetch the 3-byte program counter from address 2, and execute exactly 65536 instructions.
  4. Send the 64-KiB pixeldata block designated by the byte value at address 5 to the display device. Send the 256-byte sampledata block designated by the 2-byte value at address 6 to the audio device.
  5. Go back to step 1.

A program must be finished with all its calculations for a frame before the next periodic reset of the program counter occurs. In essence this makes BytePusher a hard realtime system. When a program has finished its work for one frame, it should simply wait in a loop for the next reset. A loop counter can be used to measure CPU utilization.

Inner loop[edit]

An instruction consists of 3 big-endian 24-bit addresses A,B,C stored consecutively in memory. The operation performed is: Copy 1 byte from A to B, then jump to C.

An instruction should be able to modify its own jump address before jumping. Thus the copy operation must be completed before the jump address is read.

Since the three address operands are limited to 24 bits,

  • We can never copy to/from an address past FFFFFFh, and
  • We can never jump to an instruction starting at an address greater than FFFFFFh.

However, since each instruction takes up exactly 9 bytes, jumping to an address ≥ FFFFF8h results in the CPU attempting to read past address FFFFFFh. In the worst case (if the instruction starts at FFFFFFh) the CPU will try to read 8 extra bytes. The way we deal with this is to simply add 8 extra padding bytes beyond FFFFFFh, all initialized to 0. A buggy application can then "crash" within the sandbox, without affecting the VM or the host machine in any way.

Here's an implementation in C of the inner loop:

unsigned char mem[0x1000008], *pc;
...
pc = mem + (mem[2]<<16 | mem[3]<<8 | mem[4]);
int i = 65536;
do {
  mem[pc[3]<<16 | pc[4]<<8 | pc[5]] =
  mem[pc[0]<<16 | pc[1]<<8 | pc[2]];
  pc = mem + (pc[6]<<16 | pc[7]<<8 | pc[8]);
} while (--i);

Since we read each address one byte at a time, this code works on both big-endian and little-endian host machines.

Persistence[edit]

A snapshot of the memory contents of a running BytePusher machine can be saved to a file. To ensure a consistent state, this must only be done in between frames (i.e. not while the inner loop is running). A saved snapshot file can later be loaded into RAM to recreate the exact same machine state.

File formats[edit]

Note that file formats are not part of the actual machine specification. Anyone is free to develop their own format and publish it here.

File extension Format description
".BytePusher" A ".BytePusher" file contains a snapshot of the memory contents of a BytePusher machine. Address X in memory is mapped to byte position X in the file. To decrease the filesize, any trailing zeros should be omitted from the file when saving. When loading a file which is less than the full 16 MiB, the upper part of RAM must be initialized to 0.
".BytePusherPage" A ".BytePusherPage" file contains a 256-byte lookup table, intended to reside on a 256-byte aligned page in memory.
".BytePusherBank" A ".BytePusherBank" file contains a 65536-byte lookup table, intended to reside in a 65536-byte aligned bank in memory.

Programs[edit]

Below is a list of all known BytePusher programs, with screenshots. If you've made your own program, please post a link here.

Palette Test.GIF Palette Test

This program shows the colors for all 256 possible pixel values (including the top 40 unused ones, which should show as black). The sourcecode is here.
Author: user:Javamannen

Scrolling Logo.GIF Scrolling Logo

This program shows a scrolling BytePusher logo, which wraps around the edges of the screen. The sourcecode is here.
Author: user:Javamannen

Keyboard Test.GIF Keyboard Test

This program shows the state of the sixteen keys. The sourcecode is here.
Author: user:Javamannen.

BytePusher Example Program Munching Squares.png Munching Squares

This program written in PUSHEM implements the munching squares display hack.
Author: User:Zzo38

Audio Test.GIF Audio Test

This program plays 8 bars of music in an endless loop, while displaying the changing waveform in the style of an oscilloscope. The sourcecode is here.
Author: user:Javamannen

SineScroller.GIF SineScroller

This program implements a multi-colored sine scroller. The sourcecode is here.
Author: user:Javamannen

Sprites.GIF Sprites

This program moves some sprites around in various patterns on top of a background image. The sourcecode is here.
Author: user:Javamannen

Invertloopsine.gif "Invert Loop" sine (from the Wayback Machine; retrieved on 25 August 2011)

This is an alternative audio test which unfortunately doesn't appear to catch a stuttering VM very well. The sourcecode is here (from the Wayback Machine; retrieved on 25 August 2011).
Author: Ben Russell

Machines[edit]

Below is a list of all known BytePusher (virtual or physical) machines. If you've made your own machine, please post a link here.

See also[edit]

External resources[edit]