Skip to content
sverx edited this page Mar 19, 2015 · 19 revisions

Welcome to the devkitSMS wiki! How devkitSMS/SMSlib works. A small journey into functions and functionalities.

(short) Introduction

The SEGA Master System and its portable brother, the SEGA Game Gear, are 8-bit machines powered by two hearts. One heart is the Zilog Z80 processor, while the other is a custom graphic chip (derived from the Texas Instruments TMS9918) that handles everything that's on-screen: it's called the 'Video Display Processor', or VDP for short. Different Master System models can have different VDP revisions with different features, and the Game Gear one also has a few differences, but as long as we're concerned we can consider them all equal. There will be a special note when distinctions would be unavoidable.

The VDP works by redrawing the screen top to bottom 50 or 60 times a second, according to television standards. 192 lines of pixels will be on screen in any case, and each line (a scanline) will be 256 pixel wide, making the image resolution 256x192 pixel. The pixels will use colors from two 16-color palettes, one reserved for background tiles and one that can be used both for sprites and background tiles. This also means that all the sprites will share the same 16-color palette.

The background is made up of 16-color 8x8 pixel tiles. Both tiles and tilemap (which describes how tiles should be placed on-screen) need to be loaded into Video RAM (VRAM). The tilemap is a single 32x28 table where each entry contains information about which tile should be used, which palette, and if sprites are supposed to appear behind that tile or in front of it. VRAM size is 16 KB and it also contains the Sprite Attribute Table (SAT).

Sprites are made of 16-color 8x8 pixel tiles too, but they can be placed anywhere on screen. The VDP supports up to 64 of them at the same time, even if won't draw more than 8 of them on any given scanline. Their positions and images (the tiles used) are stored into SAT, which is again allocated into VRAM. VRAM is big enough to theoretically contain up to 512 tiles, but only tiles defined into either the 1st or the 2nd half of VRAM can be used.

More in-depth details on the VDP, along with very complete docs, can be found on this page at SMSPower! website.

devkitSMS/SMSlib

SMSlib is substantially a wrapper that will hide most of the hardware inner workings, making it easier for anyone to write programs on the Master System platform. It's meant to be used with devkitSMS, and simply using devkitSMS and including SMSlib.h in your code you'll automatically initialize the library. This means the whole hardware will be ready straight from the beginning. The ROM mappers prepared, the VDP internal registers initialized to common values, the vBlank and pause handlers both ready.

In detail, the VDP will be initialized to 'Mode 4', which is Master System own mode, with the features described in the Introduction. Background scrolling will be reset to 0 both X & Y direction, the SAT (256 bytes) allocated at end of VRAM, the tilemap (1.75 KB, also called 'Pattern Names Table', PNT) just before the SAT, so to have a contiguous 14 KB free space in VRAM, enough for 448 tiles, from the VRAM beginning on. The display (and the vBlank interrupt) will be off, since the VRAM needs first to be loaded with some content before displaying anything meaningful.

Let's start!

To load tiles into VRAM, you can use

SMS_loadTiles (void *src, unsigned int Tilefrom, unsigned int len)

and to load a tilemap you can use

SMS_loadTileMap (unsigned char x, unsigned char y, void *src, unsigned int len)

(if you've got an STM compressed tilemap, generated by Maxim's BMP2Tile, you can use)

SMS_loadSTMcompressedTileMap (unsigned char x, unsigned char y, unsigned char *src)

Then of course you need to load the palettes for your background and for your sprites eventually

SMS_loadBGPalette (void *palette)
SMS_loadSpritePalette (void *palette)

done that, you can finally turn on the screen, using the macro

SMS_displayOn()

of course you might want to redefine a single color. You could then use

SMS_setBGPaletteColor (unsigned char entry, unsigned char color)
SMS_setSpritePaletteColor (unsigned char entry, unsigned char color)

and the following functions will scroll the background horizontally and/or vertically (yes, the VDP can do that!)

SMS_setBGScrollX (int scrollX)
SMS_setBGScrollY (int scrollY)

How to sync with screen / a.k.a. "Your endless 'while' loop"

In your code you'll want to sync what's going on in the game with the screen refresh so that you actually make changes happen between one frame and the next one. This is generally achieved by a while loop that lasts forever and a function that waits for the screen draw phase end. So your program's main() will probably look like this:

while (true) {
  SMS_waitForVBlank();
  // you change the contents of the screen here...
  // ... then you do your game logic here
}

note: the function

SMS_waitForVBlank (void);

will work only when vBlank interrupt is on, which happens when you turn on the screen with SMSlib, otherwise it will turn into a endless (and hopeless) loop, and your program will be stuck.

Using sprites

SMSlib will handle sprite using a small temporary area so that it will be possible for you to define how sprites will appear in the next frame without changing the SAT contents (and thus affecting the sprite on screen in the current frame). So, in each frame, you have to declare you're going to list all the sprites one after another, front to back, with the function

SMS_initSprites (void)

each sprite can be added both using

SMS_addSprite (unsigned char x, unsigned char y, unsigned char tile)

which doesn't perform any clipping, or

SMS_addSpriteClipping (int x, int y, unsigned char tile)

which performs clipping (no sprite falling completely outside the viewport will be placed on screen) according to the viewport definition you can issue (and re-issue whenever you want) using the function

SMS_setClippingWindow (unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1)

note that both functions to add a sprite return a boolean. This indicates if the sprite has been added or not (clipped, no more sprites available)

Done with sprites, you've got to issue

SMS_finalizeSprites (void)

because we need to know we're not going to declare more sprites. All this, as said, will create a temporary copy of the SAT that you'll use the next frame. So you'll probably call the

SMS_copySpritestoSAT (void)

function in your while loop right after the vBlank starts.

Note: sprites can only use the tiles defined in either half of the VRAM. You can choose if you're going to use tiles 0-255 or tiles 256-511 (or, better, tiles from 256 to 447 since PNT and SAT are over tiles 448-511) using the function

SMS_useFirstHalfTilesforSprites (bool usefirsthalf)

that you would probably call just once in the beginning of your program. If you don't do that, sprites will be referring to tiles in the second part of VRAM.

Reading input

Attached to your Master System you've got one (or possibly two) controllers. The library will poll the state of the keys as long as the screen is on, and you can easily check the states of the keys by using the four functions

SMS_getKeysStatus (void)
SMS_getKeysPressed (void)
SMS_getKeysHeld (void)
SMS_getKeysReleased (void)

in detail: the first returns the current state of the keys on both controllers, the second returns which keys were pressed since last check, the third which keys are held pressed and the last one which keys that were previously pressed are now released. The returned value is a mask which you have to bit-compare against the defined constants that follow:

#define PORT_A_KEY_UP           0x0001
#define PORT_A_KEY_DOWN         0x0002
#define PORT_A_KEY_LEFT         0x0004
#define PORT_A_KEY_RIGHT        0x0008
#define PORT_A_KEY_1            0x0010
#define PORT_A_KEY_2            0x0020
#define PORT_A_KEY_START        PORT_A_KEY_1    /* handy alias */
#define PORT_B_KEY_UP           0x0040
#define PORT_B_KEY_DOWN         0x0080
#define PORT_B_KEY_LEFT         0x0100
#define PORT_B_KEY_RIGHT        0x0200
#define PORT_B_KEY_1            0x0400
#define PORT_B_KEY_2            0x0800
#define PORT_B_KEY_START        PORT_B_KEY_1    /* handy alias */

If you plan on reading a Genesis/MegaDrive pad connected to the first controller port (port A) you can also call the following functions to read the state of the additional keys, the keys which are present on a Genesis/MegaDrive pad that aren't present on the Master System pad (thus the 'A' key, the 'Start' key and the 'X','Y','Z' and 'Mode' key if you're using a so called "6-buttons" pad)

SMS_getMDKeysStatus (void)
SMS_getMDKeysPressed (void)
SMS_getMDKeysHeld (void)
SMS_getMDKeysReleased (void)

again, you have to bit-compare the returned values with the handy defines

#define PORT_A_MD_KEY_Z         0x0001
#define PORT_A_MD_KEY_Y         0x0002
#define PORT_A_MD_KEY_X         0x0004
#define PORT_A_MD_KEY_MODE      0x0008
#define PORT_A_MD_KEY_A         0x0010
#define PORT_A_MD_KEY_START     0x0020

(support for a second Genesis/MegaDrive pad in port B is still missing)

Clone this wiki locally