ram - winnichenko/BLOB-87 GitHub Wiki

RAM & VRAM layout

The BLOB-87 has 1408kb of addressable "hardware" memory. This design should let you write the full stuffed cart to the floppy disk or a tiny flash drive.

.------------------------------------.
|          1408K RAM LAYOUT          |
|------------------------------------|
| ADDR  | INFO              | BYTES  |
|-------+-------------------+--------|
| 00000 | <VRAM>            | 65536  | Video RAM (see table below)
| 10000 | TILES             | 65536  | 2024 8x8 8bit sprites (tiles) - #0 to #2047
| 20000 | SPRITES           | 65536  | legacy partitioning, will be well organised
| 30000 | MAP               | 458752 | 512x448 map cells - holds index of sprite
| A0000 | MAP FLAGS         | 229376 | 512x448 map flags, 8bit number
| D8000 | GAMEPADS          | 4      | state for 4 gamepads
| D8004 | MOUSE             | 4      | mouse state X / Y / buttons / scroll
| D8008 | KEYBOARD          | 4      | keyboard state, codes for 4 pressed buttons
| D800C | SFX STATE         | 16     |
| D801C | SOUND REGISTERS   | 72     | ...
| D8064 | WAVEFORMS         | 256    | 16 waveforms, each 32 x 4bit values
| D8164 | SFX               | 4224   | Sound instruments
| D91E4 | MUSIC PATTERNS    | 11520  | ...
| DBEE4 | MUSIC TRACKS      | 408    | Songs/music traks
| DC07C | SOUND STATE       | 4      | ...
| DC080 | STEREO VOLUME     | 4      |
| DC084 | PERSISTENT MEMORY | 1024   | 256 32-bit values, energy independent cart memory
| DC484 | SPRITE FLAGS      | 2048   | 8bit number, each sprite's unique flag
| DCC84 | SYSTEM FONT       | 2048   | 256 8x8 1bit font (used by print)
| DD484 | ... CODE ...      | 600956 |
'-----------------------------------'

VRAM

.-----------------------------------.
|           64K VRAM LAYOUT         |
|-----------------------------------|
| ADDR  | INFO              | BYTES |
|-------+-------------------+-------|
| 00000 | SCREEN            | 57344 | 256x224 = 57344 8bit pixels
| 0E000 | PALETTE           | 192   | 64 x 24bit RGB color values
| 0E0C0 | PALETTE MAP       | 64    | 16 x 8bit color indexes (for palette swapping of individual sprites)
| 0E100 | BORDER COLOR      | 2(?)  | 8bit color value
| 0E102 | SCREEN OFFSET     | 2     | horz/vert screen offset [-128...+127]
| 0E104 | MOUSE CURSOR      | 1     | sprite to use for mouse pointer
| 0E105 | ... (reserved)    | 7931  | Reserved for future usage or will be depricated
'-----------------------------------'

Palette

PALETTE stores the currently active palette. Each entry in the palette includes a red, green, and blue component - allowing over 16 million colors (24-bit color).

.----------------------------------.
|              PALETTE             |
|----------------------------------|
| .-------------------------.      |
| | red component    | 8bit |      |
| | green component  | 8bit | x 64 | = 192 bytes
| | blue component   | 8bit |      |
| '-------------------------'      |
'----------------------------------'

Palette Map

PALETTE MAP controls how palette indexes are applied when writing to VRAM. This can be used to easily swap palette colours. The mapped colour index (not the original) is written to the SCREEN. With sprite-drawing functions (spr, map, textri), this color swap is done when reading the color from sprite memory.

transparent index parameters are applied on source colours, swapping out colour indices before remapping. (?) The colour index of 255 is always transparent and used in both Sprite and Map editors.

For an example see Swapping Palette Colors.

Mouse Cursor

MOUSE CURSOR specifies which cursor to use:

0 = Disabled
1...127 = Sprite (#257 to #383 in 4bpp mode)
128 = ARROW
129 = HAND
130 = IBEAM
131...255 = Reserved

Sprites

SPRITES: Each sprite is laid out sequentially in memory. Each sprite is 64 bytes long, so sprite #i starts at 0x4000+(64*i). Each byte in the sprite represents a pixel. BLOB-87 uses a 6bit colour palette, but each pixel is 8 bit in memory. The full 8bit with the palette of 256 colours will be supported in BLOB-89.

This block was removed

Each sprite could be accessed by index #0 to #2047 via spr function in code and also used in Map Editor.

Map

The MAP is a grid of 256x224 tiles. Each word (16bit) represents a single tile of the map(0-2047). Like the sprites, the map's tiles are laid out from left to right, top to bottom row. Convenience functions mset and mget are provided for working with map data.

Map Flags

The map flags are basically the extension of the sprite flags concept. Sprite flags are bind to the sprite itself, Map flags are bind to the map tiles. For example, if you have the wall on the map, you could give it a flag and it always stays on the wall whatever the sprite you use in that particular position.

Unlike the sprite flags, Map flags are accessed by an index 0-127 in the code and are drawn in the Map editor only as pre-designed icons. Map flags img 01 Map flags img 02 Map flags img 03

Mouse

.-------------------.
|       MOUSE       |
|-------------------|
| x position | 8bit |
| y position | 8bit |
| left       | 1bit |
| middle     | 1bit | = 4 bytes
| right      | 1bit |
| h scroll   | 6bit |
| v scroll   | 6bit |
| (reserved) | 1bit |
'-------------------'

The X and Y position values represent where in the TIC-80 screen the mouse is pointing to, but they may exceed the 240x136 range of the screen.

The H and V scroll values are used for the horizontal and vertical mouse wheel delta. These represent the amount of "steps" they had moved in the last frame. These are two's complement numbers, with their sign indicating which direction they're scrolling.

This data is most easily accessed using the mouse function.

Sound Registers

.-----------------------.
|     SOUND REGISTER    |
|-----------------------|
| frequency | 12bit     | x 4 channels = 72 bytes
| volume    | 4bit      |
| waveform  | 32 x 4bit |
'-----------------------'

The frequency value is converted to a period value (clamped to 10...4096) suited for a virtual 1.8 MHz clock. This period value produces an update frequency that is close to 32*frequency (for waveform envelopes) or 16*frequency (for noise output). At the update frequency, the 32-step counter pointing to the current waveform step will increment, or the noise LFSR will update.

Noise output occurs when the waveform is empty (zero-filled). This produces a pseudo-random binary output sequence, with a length of 32767 samples. The noise sequence is the same as the NES.

You can update the Sound Registers every tick (60 times per second) to generate sound independently from the built-in API functions. To read the frequency and volume, use the following code:

value = peek(0xFF9C+18*channel+1)<<8|peek(0xFF9C+18*channel)
frequency = (value&0x0fff)
volume = (value&0xf000)>>12

SFX

.--------------------------.
|           SFX            |
|--------------------------|
|                          |
| .-----------------.      |
| | volume   | 4bit |      |
| | wave     | 4bit | x 30 |
| | arpeggio | 4bit |      |
| | pitch    | 4bit |      |
| '-----------------'      |
|                          |
| .-----------------.      |
| | octave   | 3bit |      | x 64 = 4224 bytes
| | pitch16x | 1bit |      |
| | speed    | 3bit |      |
| | reverse  | 1bit |      |
| | note     | 4bit |      |
| | temp     | 4bit |      |
| '-----------------'      |
|                          |
| .--------------.         |
| |     LOOP     |         |
| |--------------| x 4     |
| | start | 4bit |         |
| | size  | 4bit |         |
| '--------------'         |
|                          |
'--------------------------'

Volume is inverted, so volumes 15...0 in the volume macro editor are treated as values 0...15 in these volume values.

The octave value is one lower than described in the editor. As such, values 0...7 correspond with SFX octaves 1...8.

The speed value and the pitch values are all two's complement numbers.

Note numbers 0...11 are mapped to C...B. Note numbers 12...15 are mapped to C...D# on the next higher octave, which is an "undocumented" SFX feature.

There are four loop fields, each with a start index and loop size. The order of the fields is: Wave, Volume, Arpeggio, Pitch

Music Patterns

The MUSIC PATTERNS stores the tracker pattern data.

.---------------------------.
|        MUSIC PATTERN      |
|---------------------------|
|                           |
| .------------------.      |
| |    PATTERN ROW   |      |
| |------------------|      |
| | note      | 4bit |      |
| | param1    | 4bit | x 64 | x 60 = 11520 bytes
| | param2    | 4bit |      |
| | command   | 3bit |      |
| | sfx       | 6bit |      |
| | octave    | 3bit |      |
| '------------------'      |
|                           |
'---------------------------'

Please also see the source for tic_track_row.

note describes which note to play.

0 = No Action
1 = Note Off
2,3 = Reserved (same as 0)
4...15 = Notes C...B

command is a SFX command (such as volume, slide, pitch, etc). See the source.

param1 and param2 are parameters for the various SFX commands.

sfx refers to the SFX id of the sound to use to play the given note.

One should be careful when setting/reading sfx with simple bit shifts (as it crosses a byte boundary). Bit offset 15 contains the high bit of sfx while bit offsets 16-20 contain the remaining bits. If done incorrectly you'll end up with the LSB in offset 15, instead of the MSB, which is incorrect. For example, SFX indexes 05 and 33 in the tracker come back as 10 and 3 when read incorrectly.

The octave value is one lower than described in the tracker. As such, values 0...7 correspond with octaves 1...8.

Music Tracks

The MUSIC TRACKS stores the track data.

.-----------------------------.
|         MUSIC TRACK         |
|-----------------------------|
|                             |
| .--------------------.      |
| | pattern ch0 | 6bit |      |
| | pattern ch1 | 6bit | x 16 |
| | pattern ch2 | 6bit |      |
| | pattern ch3 | 6bit |      |
| '--------------------'      | x 8 tracks = 408 bytes
|                             |
| .--------------.            |
| | tempo | 8bit |            |
| | rows  | 8bit |            |
| | speed | 8bit |            |
| '--------------'            |
|                             |
'-----------------------------'

The TEMPO (32..255) of the music track is 150 plus the value of tempo.

The number of ROWS (1..64) in the music track is 64 minus the value of rows.

The SPD (1..31) of the music track is 6 plus the value of speed.

Sound State

.-----------------.
|   SOUND STATE   |
|-----------------|
| track | 8bit    |
| frame | 8bit    |
| row   | 8bit    |
|-----------------|
|                 | = 4 bytes
| .-------------. |
| |    FLAGS    | |
| |-------------| |
| | loop | 1bit | |
| | ...  | 7bit | |
| '-------------' |
|                 |
'-----------------'

The Track value is 255 (-1) when no track is playing.

Sprite Flags

Stores the sprite flags in RAM. See fset and fget.

System Font

Stores the default system font which is used by print. A game can modify this font by simply updating this area of RAM.

  • 256 characters (default includes two fonts, the second in ASCII 128-255)
  • 1 bit, 8x8 pixel font (ie, 8 bytes per character)
The letter 'A'.

 Binary        Lit Pixels
00000100       .....*..
00001010       ....*.*.
00011111       ...*****
00010001       ...*...*
00010001       ...*...*
00000000       ........
00000000       ........
00000000       ........

See the font demo.

Persistent Memory

Every cartridge has 1 KB of persistent memory. This is useful for saving high-scores, level advancement or achievements. This memory can be updated directly using poke and automatically persists across restarts of your cartridge.

This memory can also be retrieved and updating using the pmem function. The pmem function divides this RAM into 256 individual 32-bit slots storing unsigned integers (from 0 to 4294967295).

⚠️ **GitHub.com Fallback** ⚠️