The Spectrum was a popular 8-bit home computer from the early 1980s. Its graphics used a 256x192 pixel bitmap, each pixel is one bit. The colors were generated from a lower-resolution attribute map: each 8x8 tile of pixels had a foreground color and a background color. This was encoded as two three-bit color fields in each attribute byte. Hence the total storage for the display is:
- 6 KBytes for the graphic data (192 lines of 32 bytes each)
- 768 bytes for the color attribute map (24 lines of 32 bytes each)
Using the Gameduino's coprocessor, it is possible to convert from Spectrum video data to Gameduino background characters on-the-fly. As the raster moves down the screen the coprocessor converts each line of graphics.
In this demo sketch, the Arduino is loading various games' raw screen dumps into Gameduino memory. All of the graphics conversion work is being done by the Gameduino coprocessor.
#include <SPI.h>
#include <GD.h>
#include "spectrum.h"
#include "spectrum_data.h"
void setup()
{
GD.begin();
GD.microcode(spectrum_code, sizeof(spectrum_code));
GD.uncompress(0x7000, spectrum_tables);
// fill screen with TRANSPARENT
GD.fill(RAM_PIC, 0xff, 64 * 64);
GD.wr16(RAM_PAL + 8 * 255, TRANSPARENT);
GD.wr16(BG_COLOR, RGB(128, 0, 0)); // dark red border
// paint the 256x192 window as alternating lines of
// chars 0-31 and 32-63
for (byte y = 0; y < 24; y += 2) {
for (byte x = 0; x < 32; x++) {
GD.wr(RAM_PIC + 64 * (y + 6) + (9 + x), x);
GD.wr(RAM_PIC + 64 * (y + 7) + (9 + x), 32 + x);
}
}
}
#define PAUSE delay(3000)
void loop()
{
GD.uncompress(0x4000, screen_mm1); PAUSE;
GD.uncompress(0x4000, screen_mm2); PAUSE;
GD.uncompress(0x4000, screen_aa0); PAUSE;
GD.uncompress(0x4000, screen_aa1); PAUSE;
GD.uncompress(0x4000, screen_jp0); PAUSE;
GD.uncompress(0x4000, screen_jp1); PAUSE;
GD.uncompress(0x4000, screen_kl0); PAUSE;
GD.uncompress(0x4000, screen_kl1); PAUSE;
}
The hard work is all done in the coprocessor program. In the main loop, it waits for every 8th video line, then starts converting the next line of characters. It unpacks the pixel data and the attribute data into a line of 32 characters. Because it can reuse the characters on alternating lines, the total usage is only 64 characters.
The 6K Spectrum video data is loaded into Gameduino RAM starting at 0x4000 - the area normally used for sprites. In addition a lookup table at 0x7000 speeds up the color conversion.
start-microcode spectrum
\ Interface:
\ 4000-57FF Spectrum bitmap
\ 5800-5AFF Spectrum attributes
\ 7000 attribute lookup: 256 bytes. 64 colors of (paper, ink)
\ 7100 pixel stretch, 16 bytes.
: 1+ d# 1 + ;
: 0= d# 0 = ;
: 4* d# 4 * ;
: 64mod h# 3f and ;
: copy1 ( src dst -- src' dst' ) \ copy one byte
over c@
over c!
1+
;fallthru
: n1+ ( a b -- a+1 b )
swap 1+ swap ;
\ copy attrs for line y
\ dst is RAM_PAL or RAM_PAL+256
: attrcopy ( y -- )
dup 4* h# 5800 + swap ( src y )
h# 8 and d# 32 * RAM_PAL + ( src dst )
begin
over c@ 64mod 4* h# 7000 + swap \ fetch and lookup attribute
copy1 copy1 d# 4 + copy1 copy1 nip
n1+
dup h# ff and 0=
until
drop drop
;
: stretch! ( dst a -- dst' ) \ expand 4 bit graphic a, write to dst
h# f and
h# 7100 + c@
over c! 1+
;
: byte ( src dst -- src' dst' )
over c@ swap ( src a dst )
over d# 4 rshift stretch!
swap stretch! ( src dst' )
swap h# 100 + swap \ down 1 line in spectrum video memory
;
: byte4
byte byte byte byte ;
: pixelcopy ( y -- y )
dup 64mod 4* h# 4000 +
over h# c0 and d# 32 * + ( y src )
begin
dup
dup 64mod d# 16 * RAM_CHR +
byte4 byte4
drop drop
1+
dup h# 1f and 0=
until drop
;
\ Spectrum memory layout is a bit twisted
\ line 0 4000, 4001, 4002
\ 1 4100, 4101
\ ...
\ 8 4020
\ ...
\ 56 40e0, ... 40ff
\ ...
\ 63 47e0 47ff
\ 64 4800
\ 65 4900
\ ...
\ 191 57e0
\ at line 0 can start work on 4020
\ at line 8 can start work on 4040
\ at line 16 can start work on 4060
\ 56 4800
\
\ So in general, at line Y can start work on converting from:
\ 4000 + (((Y+8) & 38) * 4) + (((y+8) & c0) * 32)
: main
d# 0
begin
begin dup d# 48 + YLINE c@ = until
d# 8 + h# ff and
dup attrcopy
pixelcopy
again
;
end-microcode