Debugging SMS/GG programs isn't always easy. Not all emulators have debuggers, and you can't easily output debug messages. The idea of the SDSC Debug Console Specification is to reserve two unused I/O-ports and output data to them, which will be visible on a debug console of the emulator. One port ($FC) is a control port, while the other one ($FD) serves for data output. Both ports are write- only. Also, because these ports reside in the joystick port range ($C0-$FF), the joystick ports need to be disabled before access to the SDSC Debug Console ports is permitted. The joystick ports are disabled by SETTING bit 2 of the I/O Control Port (port $3E). RESETTING bit 2 will (re-)enable the joystick ports. (Please see Charles MacDonald's "SMS/GG hardware notes (2002/11/12)" in the S8-Dev Technical Documents section for more information about the I/O Control Port.)

The debug console is a ASCII text window with a size of 80x25 characters. It may have more than 25 rows, but the last 25 are always the active ones and the others may only be accessed using a scroll bar. The characters can be coloured with 16 standard colours for both foreground and background with an attribute byte. There's a current attribute byte that's used for all following characters you output. There's also a current cursor position, which defines where the characters are inserted.

If you send data to the data port, it will be inserted as an ASCII character and with the current attribute at the current cursor position. Then the cursor column will be increased by 1, and if it reaches a column of 80, the cursor column will be reset to 0 and the cursor row will be increased by 1. If the cursor row reaches 25, it will be decreased to 24 and the whole text will be scrolled up one row.

For now, the standard characters are only supported from 32 to 127 because the others from 128 to 255 are country specific. Also, only 2 of the control characters from 0 to 31 are supported:

10: Line feed

It will reset the cursor column to 0 and the cursor row will be increased by 1. Again, if the cursor row reaches 25, it will be decreased to 24 and the whole text will be scrolled up one row.

13: Carriage return

It will reset the cursor column to 0.

The control port supports the following commands:

1: Suspend emulation

2: Clear the debug console

Move the cursor to 0,0 and reset the control and data port state. The attribute bytes of all characters are set to the current one and all the characters are set to space.

3: Set attribute

It will set the current attribute to the followed attribute byte.

4: Move cursor

First followed by the row byte, then by the column byte. The row is mod'ed with 25, the column is mod'ed with 80. (ie. if you use a column more than 79, the cursor won't be moved to the next row)

All other codes ($00, $05-$FF) must be reserved for future use. At startup or reset of the emulator, the attribute byte is set to 15 and command 2 is automatically executed.

Both the control port and the data port have an internal state variable. The control port state is set to a specific value if you send a "Set attribute"- or a "Move cursor"-command to the control port. This state means that the control port is now expecting other bytes, like the attribute byte, or the row and column bytes to complete the command. After sending these bytes both states are reset to the normal state.

Because it's hard to output numbers as text using a Z80, especially in decimal, the debug console supports a system to output numbers. To use this system, you have to send a "Format specifier" to the data port, which looks like this:

'%'[width]<fmt><data type><parameter>

Width (Optional)

The width of the field in decimal. The data is right adjusted in this field. If the data has too many characters to fit in the width specified, then the left-most portion is truncated for numbers; the right-most portion is truncated for strings. If the width is larger than the data, then spaces are added as padding for the signed decimal, unsigned decimal, ASCII, and String formats. Zeros (0) are added as padding for the hexadecimal and binary formats. If no width is specified, then the width of the field becomes the minimum width required to fully represent the data.

For numeric data, this means all leading zeros are removed, (e.g., the binary value 00101011 would have a width of 6 and would be represented as 101011). The maximum allowed width is 256.


The format in which the number is displayed. Can be one of the following:

Signed decimal
Unsigned decimal
Hexadecimal using 'abcdef'
Hexadecimal using 'ABCDEF'
ASCII character
Zero-terminated String

Data type:

Can be one of the following:

Memory word at the address specified by a 16-bit parameter
Memory byte at the address specified by a 16-bit parameter
VRAM word at the address specified by a 16-bit parameter (the address is automatically ANDed with 0x3FFF)
VRAM byte at the address specified by a 16-bit parameter (the address is automatically ANDed with 0x3FFF)
Processor register specified by a 8-bit parameter:
  • $00 or 'b': B
  • $01 or 'c': C
  • $02 or 'd': D
  • $03 or 'e': E
  • $04 or 'h': H
  • $05 or 'l': L
  • $06 or 'f': F
  • $07 or 'a' A
  • $08 or 'p': PC
  • $09 or 's': SP
  • $0A or 'x': IX
  • $0B or 'y': IY
  • $0C or 'B': BC
  • $0D or 'D': DE
  • $0E or 'H': HL
  • $0F or 'A': AF
  • $10 or 'r': R
  • $11 or 'i': I
  • $12: BC'
  • $13: DE'
  • $14: HL'
  • $15: AF'
  • All other codes are reserved. The size depends on the register.
VDP register or palette entry specified by a 8-bit parameter, which may have the following values:
  • 0x00 to 0x0F: VDP registers from 0x0 to 0xF
  • 0x10 to 0x2F: Palette entries from 0x00 to 0x1F (8-bit on SMS, 16 bit on GG)

All parts of the format specifier are in ASCII, except the parameter, which is a 8-bit or 16-bit (LSB) value depending on the data type.

The data port's internal state variable is used to indicate which part of the format specifier is sent. It is reset to the normal state if you clear the console or after the format specifier is finished.

In case of the ASCII-format ('a') or zero-terminated string ('s'), the data type must be either 'mb' or 'vb'. Also, the width does not only specify the width of the field, but also how many bytes are displayed. All other formats display only one byte or word. If a hexadecimal or binary number does not fill the whole field, the rest of it is filled with '0's.

Note that the format and the data type are case sensitive. The format is always one byte, while the data type is always two bytes.

The '%' character at the beginning will not be visible on the debug console. If you want to output a '%' you have to send two '%'s directly after each other.

After completion of a format specifier, the number is displayed as if normal characters would be sent to the data port.

The attribute byte, which defines the colours, has the following format:

7-4Background colour
3-0Foreground colour

There are 16 possible colours for both background and foreground which are taken from the following palette:

1Dark blue
2Dark green
3Dark cyan
4Dark red
5Dark magenta
6Dark yellow
7Dark gray
8Light gray
9Light blue
10Light green
11Light cyan
12Light red
13Light magenta
14Light yellow

You can choose the RGB values yourself. If you can't or don't want, you don't have to support the attribute byte at all.

It's possible to generate errors by sending wrong bytes to the debug ports. Such errors be handled. You can choose yourself how you handle them. For example, you can add a status bar, which displays an error message. Or you can insert something like "[ERROR]" in the debug console. In case of an error, the state of the port the wrong byte has been sent to, is reset to the normal state.

The following errors can be handled:

Return to top