Space Invaders emulator in Factor

Chris Double — 2005-09-11 02:45:19

I've written a Space Invaders emulator in Factor. Screenshots here:

http://factor.modalwebserver.co.nz/factor_space_invaders.jpg
http://factor.modalwebserver.co.nz/factor_space_invaders2.jpg
http://factor.modalwebserver.co.nz/factor_space_invaders3.jpg

The code is in Factor CVS and in my darcs repository. I took a
declarative approach to emulating the 8080 CPU. Using parser combinators
I parsed a syntax for describing the instructions and generate the code
to do the emulation from that. So the instructions look like:

INSTRUCTION: DEC DE ; opcode 1B cycles 06
INSTRUCTION: INC E ; opcode 1C cycles 05
INSTRUCTION: DEC E ; opcode 1D cycles 05
INSTRUCTION: LD E,n ; opcode 1E cycles 07
INSTRUCTION: RRA ; opcode 1F cycles 04
INSTRUCTION: LD HL,nn ; opcode 21 cycles 10
INSTRUCTION: LD (nn),HL ; opcode 22 cycles 16

INSTRUCTION: is a parsing word that, at parse time, uses the parser
combinators to process the instruction body. It generates quoations for
the registers being used, etc and looks up an 'instruction family',
which contains a pattern of how that instruction should operate:

[[ "POP-RR" [ [ pop-sp ] keep $2 ] ]]
[[ "PUSH-RR" [ [ $1 ] keep push-sp ] ]]
[[ "INC-R" [ [ $1 ] keep [ inc-byte ] keep $2 ] ]]
[[ "DEC-R" [ [ $1 ] keep [ dec-byte ] keep $2 ] ]]
[[ "INC-RR" [ [ $1 ] keep [ inc-word ] keep $2 ] ]]
[[ "DEC-RR" [ [ $1 ] keep [ dec-word ] keep $2 ] ]]

The $1 and $2 in these patterns are replaced by the actual code to
retrieve and set values of registers that were produced by the parser
combinators upon parsing the code. This helped reduce the amount of code
I needed to write quite a bit.

The generic 8080 emulator is seperated from the space invaders specific
stuff:

TUPLE: cpu b c d e f h l a pc sp halted? last-interrupt cycles ram ;
GENERIC: reset ( cpu -- )
GENERIC: update-video ( value addr cpu -- )
GENERIC: read-port ( port cpu -- byte )
GENERIC: write-port ( value port cpu -- )

By creating a tuple that delegates to the 'cpu' tuple, and adding
methods to the generic functions you can create an emulator for any 8080
based device:

TUPLE: space-invaders port1 port2i port2o port3o port4lo port4hi port5o
;
C: space-invaders ( cpu -- cpu )
[ <cpu> swap set-delegate ] keep
[ reset ] keep ;
...etc...

I plan to add to the automatic emulation instruction generation to also
automatically generate an assembler and disassembler. There's some old
8080 code i'd like to try assembling and running (Forth, Basic, etc)
just to try the system out.

Overall I found Factor very easy to use for developing this. The main
issues i had we working out how the 8080 worked rather than problems
with using Factor or the concatenative approach.

Chris.
--
http://radio.weblogs.com/0102385