Examples

Since FIGnition is a real (though simple) computer, it can run real software and there are a number of example programs that can be run!

Contents:

 Page 1

Luna: A Lunar Lander

To begin with, here's a simple interactive version of Lunar Lander, called Luna. It's a bitmapped version of the game which was supposed to have been delivered to the BBC along with kit, but wasn't ready in time. It demonstrates that you can write a simple interactive bitmapped - i.e. sprite-based game and that the Forth system is capable of supporting 'real' development. Since this is the first available program for FIGnition I've supplied it as a listing too:

Your Lunar lander is heading for the moon as it drifts along; guide it down gently to the landing pad using space (SW7) to provide thrust if necessary. Your Lunar lander will crash if you hit the regolith or hit the landing pad at a velocity over 200! Be careful not to send the craft out of orbit!!!!!!!!!!

Graphics Demo

FIGnition is fast enough to handle a large number of moving graphic characters entirely in Forth. This simple demonstration moves a number of UDGs randomly around the screen while displaying the frame rate. 20 UDGs can be animated at 36 frames per second and up to 81 at least 10 frames per second. You run the demonstration with e.g. 20 gdem and then you can interactively increase or decrease the number of UDGs by pressing cursor right or left, and finally quit by pressing space.

A simple timeout command (note, we do a timeout by calculating timeout-clock<0, not timeout<clock since the subtraction followed by the 0< will in fact take care of overflow and wrap-around. The  maximum timeout ticks are limited to 32767 (about 10 minutes).

: setTimeout ( ticks -- timeout)

  clock i@ +

;

: timeout ( timeout -- boolean)

  dup clock i@ - 0<

;

Accessing the eeprom:

The AVR Microcontroller inside a FIGnition contains 512b (or 1024b) of non-volatile EEPROM memory. You can download data to it via USB using avrdude and read it by accessing the AVR's EEPROM registers. From Firmware 0.9.8 onwards you can write to it, using the 10 Kern vector and then read it back to another computer via USB and avrdude, which for the first time gives users the ability to transfer data in and out of a FIGnition.

$42 const eearh

$41 const eearl

$40 const eedr

$3F const eecr

2 const eepe

1 const eere

: eePrep

  begin

    eecr ic@ eepe and

  0= until

  dup 8 >> eearh ic!

  eearl ic!

;

 

: ec! ( value addr --)

  eePrep eedr ic!

  [ 10 kern , ]

;

: ec@ ( addr -- value)

  eePrep

  eere -2 eecr >port>

  drop eedr ic@

;

Drawing Circles!

The following few blocks uses a simple Bresenham Circle drawing algorithm to draw circles. You only really need the last 2 columns. It's quite quick, using roughly 100µs per point (a radius 23 circle can be drawn in <20ms).

Here's both a low resolution and high resolution demonstration of circ, the hi-res one being generated by running hiCirc.

 

Noughts And Crosses!

Here's a simple 'unbeatable' noughts and crosses game in only 5 screens and 554 bytes!

For the Computing History Museum's Hackers' Delight event I wrote a better version of Noughts and Crosses ( see below). It's 7.5Kb of source, and compiles into 2.9Kb of Forth (though 841b are just the definition headers, so the actual, compiled code is only 2.1Kb).

    

In this version of Noughts and Crosses I used 14 of the 16 UDGs to provide crisp, chunky 'X's (which took 2 UDGs) and decent 'O's.

The 'O' was the most tricky, I found that I could re-use the the inverse of the characters from the outer parts of the 'O's as the inner parts and thus do the work of 24 UDGs in only 12. The game gives you the option of going first or second and you place your move by using the cursor keys, then pressing enter. The game uses the conventional plot command to draw the winning lines. With large characters I could fill the screen Wargames-style :-) !

Line Drawing

Here's a simple block that provides line drawing!

: qLine ( DX DY X Y n)

  0 do d>r d>r ( DX DY : X Y)

    2over dr> d+ ( DX DY X' : Y)

    2over dr> d+ ( DX DY X' Y')

    swap >r 2dup plot ( .. Xl Xh Yh /Xh Yh/ : Yl)

    r> swap ( .. Xl Xh Yl Yh)

  loop

  2drop 2drop 2drop 2drop

;

: /line ( dx dy max)

  swap 0< 1 or d>r ( dx : max -1|1 )

  $8000 swap dup abs ( $8000 dx |dx| : max -1|1 )

  dup r = if

    drop r> drop >r drop ( : dx -1|1)

    0 r> 0< 1 or

  else

    swap r> swap >r ( $8000 |dx| max : dx -1|1)

    u/ swap drop 0 ( FX : dx -1|1)

    r> d+-

  then

  0 r> ( DX : -1|1) ; ( -- DX DY)

; ( -- DX DY)

 

: line ( x y dx dy)  

  2swap 2dup plot d>r ( dx dy : x y)  

  over abs over abs ( dx dy |dx| |dy| : x y)  

  over max >r r -

  if ( dx dy : max x y)    

    r /line ( DX DY : max x y)  

  else    

    swap r /line 2swap ( DX DY : max x y)

  then ( WH:nxy)  

  r> 0 swap r> swap ( WH 0 x n : y)  

  0 swap r> swap qline ( WH 0 x 0 y n )

Using the standard Bresenham line-drawing algorithm would be slower on FIGnition so instead line gradients are represented as 16:16 bit fixed point values which are added to coordinates also represented as 16:16 fixed-point values. This replaces the earlier algorithm which used 8:8 bit fixed point values and now requires Firmware 1.0.0 or later to run. This line drawing algorithm plots lines at up to 8000 pixels per second, faster than most 80s 8-bit computers!

Life

I wrote a version of Life for the Jupiter-Ace in 2006. I wanted to see how easy it was to convert to FIGnition. It's not so hard and the result is listed here

The FIGnition version is only 1497b of compiled code (+4600b of data); is about 10x faster than the Ace version and generates a new generation every 2.95s. But the motivation wasn't just to prove FIGnition would be faster, but to encourage is other conversions of Jupiter-Ace programs by doing one myself.

Simple Character Graphics Engine

FIGnition is fast enough to support simple tiled graphics directly in Forth. Here's an easy double-buffered tiled graphics engine to make simple games easier to write.

The code is currently untested, so you should expect to see changes before anything is written using it, so for the moment this is a description of how it works. The display engine consists of a background image and a foreground image. The basic technique is to first draw the background contents using tile then use backTiles> to copy it to the foreground image. You then start drawing the foreground graphics using fore. When you've finished you'd use foreTiles> to copy the entire screen to video and then call backTiles> to restore the background image before drawing your new foreground graphics Foreground graphics are simple cdata tile maps, so if you have, say a 2x2 graphic image to be displayed at 10, 12 you'd define it with:

cdata myBlob 2 c, 3 c, 4 c, 5 c,

Then you can copy it using 10 12 >xy myBlob 2 2 fore .

The basic game is loop is thus:

clg ( clears the background)

generate-background-using-tile

backTiles>

begin

  generate-foreground-using-fore

  foreTiles>

  update-background-if-needed

  backTiles>

  inkey handle-keypresses

gameOver @ until

Because the background is restored on every loop the engine automatically provides flicker-free updates for your moving foreground images without you having to keep track of where the graphics had been and this works even if your foreground objects overlap. The definition xy@ provides some basic support for object detection.

The engine provides some niceties for scrolling games which you'd use as: backTiles> tilesUp or tilesDown or backTiles> tilesLeft or backTiles> tilesRight .  Scrolling and copying uses cmove for the updates which currently operates at roughly 19Kb/s for SRAM to SRAM copies and 31Kb/s for SRAM to internal RAM copies. Therefore foreTiles> takes 19.5ms and backTiles> 31ms => 50ms or 1/20th of a second = a maximum of 20 frames per second which is certainly playable though not stunningly fast. Scrolling up takes 19ms, left and right 21ms and down takes 39ms. Thus a horizontal scrolling game would take at least 71ms, and run at no more than 14 fps. By comparison, a Jupiter ace could shift RAM at over 150Kb/s (in machine code).  Future firmware updates will improve cmove and fill by at least 5x.

Debugger

Forth is powerful enough to support a debugger written in Forth itself! To use the debugger you'd type debug CommandToDebug and it must be a command you've written. The display shows the Debugging stack, followed by the Data stack, then the return stack and finally the memory stack. Pressing SW3 (Right) steps over each command; SW1 (Left) allows you to see the run screen; SW6 (Down) Steps into a definition and SW8 (Enter) Continues until the next breakpoint (which usually means it steps out of a definition).

The debugger can be downloaded from this page and includes both the .fth file and the .hex files created by T2E2. It occupies about 1.5Kb.

Mini Car Race

How small can a FIGnition game be? This extremely crude game, minirace is only 338 bytes long.

Minirace is simple, but it has all the elements of a game you need. It supports different speeds down to -1; it correctly handles the movement of the road so that it stays within bounds; it supports a UDG; it handles user input via inkey; it manages collision detection and a running score. The minirace.zip (which includes the .fth and .hex files) is in the attachments.

Chess

Here's the beginning of a simple chess game, a 2 player version. It generates a set of chess pieces; draws a board and allows 2 players to move pieces around the board.

To use it, load the program and then type chessica. Type a pair of coordinates e.g. if you want to move F1 to F5 (which is technically illegal as a first move) then type F, then 1, then <space>, then F then 5, then <enter> . Typing a letter or digit always changes the value in the correct column, <space> always switches between the from and to coordinates.

As you can see, it's pretty simple and only requires 614b, including graphics. It doesn't check for any illegal moves, nor can the computer play chess itself - that's for the future. 2Kb chess anyone ;-) ?

Brikky

Brikky is a simple breakout clone in only 8 pages of code, it uses the Audio Mod that's recently been developed to provide sound!

I also created a simple YouTube video for it (note: there's some strobing effects on it!).

Mazes!

I took Compute Magazine's! Maze generator and converted it for FIGnition. It's just a little 3 screen program.

FIGnition can generate 23x21 mazes in about 3s per maze which isn't  bad.

   

 Maze (by drawing pixels)

 Maze with thin walls.

With a little bit of 3d code we could generate a program for you to walk through a 3D maze, rather like the Jupiter Ace version.

How does it work? Well it's a pretty simple brute-force random maze generator. We have the maze array which stores 128's in empty locations and any other value represents a filled location in the maze. At each step it just picks a random direction to go in and if it's empty it calculates the new location and stores the direction in the new location in the maze. If it was filled it tries for the next direction in a clockwise sequence and if it gets back to the first direction it backtracks.

Backtracking is pretty simply too: by storing the direction in each new location we visit, it provides backtracking information, so to backtrack we simply retrieve the direction in the current maze location and move in the opposite direction (by subtracting rather than adding its displacement). Eventually we'll fill the entire screen with maze walls.

To set it up, we need to make sure the outer edges of the maze are no-go areas, which we do by filling them with 0s (not empty).

The clever part about FIGnition's algorithm is that we don't need special checks for when we backtrack to the start, instead we simply define the direction at our starting position (which is (1,1) ) as 1 (=right) and then set maze[0] to empty. Then when we backtrack from (1,1) we move left to (0,1) and do a search. The algorithm finds that to the right it's filled, as is the bottom, as is the left (which corresponds to the top, right corner of the maze). But then it finds that (0,0) is empty so it moves there and since the terminating

condition happens before the move is displayed, the maze generation stops.

The maze is plotted as though walls were the same width as the paths, but in fact since the walls aren't used in the maze calculation they can be replaced with any width walls, which is why a conversion to 3D is pretty trivial (you can convince yourself of this by considering the fact that all the walls are on even coordinates; which means you could change their thicknesses without changing the maze itself).

Maze generation is good for chase games, e.g. the classic ZX81 3D Monster Maze. The algorithm here only uses 1030b including 575b for the maze array itself. Here's the code itself: (wall would be better termed as 'empty').

As you can see, it's pretty simple and only requires 614b, including graphics. It doesn't check for any illegal moves, nor can the computer play chess itself - that's for the future, 2Kb chess anyone ;-) ?