Home
Luke's Weblog - A Farewell to Pong [entries|archive|friends|userinfo]
Luke Gorrie

[ website | My Website ]
[ userinfo | livejournal userinfo ]
[ archive | journal archive ]

A Farewell to Pong [Nov. 15th, 2007|07:43 pm]
Previous Entry Add to Memories Tell a Friend Next Entry
[Tags|, ]

I recently tried to rewrite the MacHack PONG in the XO firmware. I'd just read Thinking Forth so I didn't like the code in PONG.FTH: I wanted there to be one screenful of code for each important section (showing the score, taking keyboard input, etc). Then I'd be comfortable with writing the two-line feature that Mitch Bradley had suggested adding.

What really happened is that I had a lot of fun, learned a lot of Forth, didn't finish the game, and made absolutely no useful contribution to the PONG on the XO. Not too bad as far as failures go. :-)

Anyway I'm an old Amiga guy so I wanted the game logic to execute in lock-step with the screen refresh, and since the vertical blanking period is very short I used double-buffering to avoid flicker:

\ PONGDBUF.FTH: Simple double-buffer support.

: db-fill-rectangle ( col x y w h -- )
   rot screen-height + -rot  ( col x y' w h -- )
   fill-rectangle
;
: wait-vblank ( -- )
   gp-wait-ready
   0 0 wh!  0 0 dst!  0 0 src!
   h# 6000.00cc ropmode!
   h# 400 blt!
;
: db-blit ( -- )
   wait-vblank
   0 screen-height  0 0  screen-width screen-height 20 - gp-move
;
And with lots of effort I did eventually manage to draw the score using only a screen of code, though I reckon it should have been simpler and more direct:
\ PONGSCOR.FTH: Text drawing
decimal
h# ffff constant fg
h# 0000 constant bg

" ****.   *.****.****.*  *.****.*   .****.****.****."
" *  *.   *.   *.   *.*  *.*   .*   .   *.*  *.*  *." $cat2
" *  *.   *.   *.   *.*  *.*   .*   .   *.*  *.*  *." $cat2
" *  *.   *.****.****.****.****.****.   *.****.****." $cat2
" *  *.   *.*   .   *.   *.   *.*  *.   *.*  *.   *." $cat2
" *  *.   *.*   .   *.   *.   *.*  *.   *.*  *.   *." $cat2
" ****.   *.****.****.   *.****.****.   *.****.   *." $cat2
" --------------------------------------------------" $cat2 drop
   constant drawing-instructions

variable ip variable xloc variable yloc

: fill-box ( color -- ) xloc @ yloc @ 10 10 db-fill-rectangle ;
: draw-digit ( x y digit )
   10 mod  5 *  drawing-instructions +  ip !
   yloc !   xloc !
   begin   ( Empty )
      ip @ c@ dup case
         ascii * of  1 ip +!  fg fill-box  10 xloc +!  endof
         bl      of  1 ip +!  bg fill-box  10 xloc +!  endof
         ascii . of 46 ip +!  -40 xloc +!  10 yloc +!  endof
      endcase
   ascii - =  until
;
: draw-score ( x y score )
   >r 2dup swap 50 + swap       ( x y x' y r: score )
   r@      draw-digit           ( x y      r: score )
   r> 10 / draw-digit
;
hex
Drawing the board, bats, and ball was no problem at all:
\ PONGDRAW.FTH: Drawing non-text graphics
decimal

: units ( n -- n ) 20 * ;
: unit  ( n -- n ) units ;

: draw-ball ( x y -- ) fg -rot 1 unit 1 unit  db-fill-rectangle ;
: draw-bat  ( x y -- ) fg -rot 1 unit 6 units db-fill-rectangle ;
: draw-bat1 ( y -- )                 1 unit swap draw-bat ;
: draw-bat2 ( y -- ) screen-width 2 units - swap draw-bat ;
: draw-score1 ( score -- )                100 40 rot draw-score ;
: draw-score2 ( score -- ) screen-width 200 - 40 rot draw-score ;
: draw-centerline ( -- )
   fg  screen-width 10 - 2 /  40  10 screen-height 80 -  db-fill-rectangle
;
: draw-boundary ( y -- ) >r  fg 0 r> screen-width 20 db-fill-rectangle ;
: draw-boundaries ( -- ) 0 draw-boundary  screen-height 20 - draw-boundary ;

: fill-screen ( color -- ) 0 0 screen-width screen-height db-fill-rectangle ;
: draw-screen ( bat1-y bat2-y ball-x ball-y score1 score2 -- )
   bg fill-screen  draw-boundaries  draw-centerline
   draw-score2 draw-score1
   draw-ball draw-bat2 draw-bat1
;
: test-draw-screen ( -- ) 200 500 800 500 72 48 draw-screen ;

hex
The keyboard code fits in one screen but it's the one part I mostly borrowed from the original:
\ PONGKDB.FTH: Keyboard input
decimal
0 value key_esc        0 value key_off
0 value key_left_down  0 value key_left_up
0 value key_right_down 0 value key_right_up

: initkeys    ( -- ) " stdin @ iselect  ' get-scan  0 alarm  iunselect" eval ;
: restorekeys ( -- ) " stdin @ iselect  ' get-scan 10 alarm  iunselect" eval ;
: clear-key-states ( -- )
   false to key_esc         false to key_off
   false to key_left_down   false to key_left_up
   false to key_right_down  false to key_right_up
;
0 value e0-seen?
: set-key-states ( down? station )
   dup . dup 1 = if abort" quit" then  case
      h# 65 of  e0-seen? if to key_right_up   else to key_left_up   then  endof
      h# 66 of  e0-seen? if to key_right_down else to key_left_down then  endof
      h# 69 of  to key_esc        endof   \ lower left game button
      h# 2a of  to key_left_up    endof   \ shift-left
      h# 5b of  to key_left_down  endof   \ hand-left
      h# 36 of  to key_right_up   endof   \ shift-right
      h# 5c of  to key_right_down endof   \ hand-right
      h# 5d of  to key_esc        endof   \ square
      h#  1 of  to key_off        endof   \ ESC scancode
   endcase
;
: scan-keyboard ( -- )
   clear-key-states
   begin " get-data?" stdin @ $call-method  while   ( scancode )
      dup h# e0 =  if
         drop  true to e0-seen?
      else
         set-key-states  0 to e0-seen?
      then
   repeat
;
hex
And then the game logic itself -- well, that's where I stopped, because I got stuck chasing a bug that somehow locked up the keyboard soon after the game starts:
\ PONGGAME.FTH: Game state
decimal

variable bat1-y variable bat2-y variable ball-x variable ball-y
variable ball-dx variable ball-dy
42 value score1 0 value score2
: step-ball ( -- ) ball-dx @ ball-x +!  ball-dy @ ball-y +! ;

: game-draw ( -- )
   bat1-y @ bat2-y @ ball-x @ ball-y @ score1 score2 draw-screen
;
: game-loop ( -- )
   begin
      scan-keyboard
      key_left_up    if -10 bat1-y +! then
      key_left_down  if  10 bat1-y +! then
      key_right_up   if -10 bat2-y +! then
      key_right_down if  10 bat2-y +!  ascii x emit then
\      3 ball-x +!  1 ball-y +!
      game-draw  db-blit
      random d# 10000 mod 0= if 1 to key_off then
   key_off  until
;

: pong-display-test ( -- )
   d# 100 0 do  test-draw-screen db-blit  loop
;
: pong-game ( -- )
   initkeys  game-loop  restorekeys
;

hex
There it ends, and I'll never finish it now that I'm having more fun programming in Etoys. So it goes!
LinkReply

Comments:
From: (Anonymous)
2007-11-15 07:27 pm (UTC)

(Link)

That's awesome! How do you get the code back and forth between files and Open Firmware?
[User Picture]From: [info]lukego
2007-11-16 04:57 am (UTC)

(Link)

I develop the code directly in the firmware. I updated the OLPC Wiki (http://wiki.laptop.org/go/Firmware_hobby_hacking) with details of how to do this, let me know if you need any help!

NB: It didn't take that long to learn to touch-type on the XO.

Edited at 2007-11-16 05:38 am (UTC)
[User Picture]From: [info]kragen
2007-11-18 06:58 am (UTC)

(Link)

Thanks! Right now I don't have an XO handy, but maybe I'll get one eventually. Would be nice to have a lightweight laptop I could comfortably read in the sun.
From: [info]marcosc.pip.verisignlabs.com
2007-11-16 05:25 am (UTC)

Getting a XO

(Link)

Heya Luke,
I managed to hook myself up with a green machine... should be arriving in a few weeks:)Going to try to build a reference implementation of the W3C's Widgets 1.0 spec on it:)
Kind regards,
Marcos
[User Picture]From: [info]lukego
2007-11-16 06:01 am (UTC)

Re: Getting a XO

(Link)

Howdy Marcos, long time no see!
I'm sure you'll have a lot of fun with the XO. :-)
[User Picture]From: [info]darius
2007-11-16 06:58 am (UTC)

(Link)

I think I'd go with a cross between your approach and theirs for the score-drawing, like:

: draw0
X X X X $
X ~ ~ X $
X ~ ~ X $
X ~ ~ X $
X ~ ~ X $
X X X X $ ;

with suitable definitions of X etc. (And too bad about fitting on one screen.)

Incidentally I had trouble with the paypal page for ordering an XO, hmph! I'm going to wait till next week and try again, I think.
[User Picture]From: [info]lukego
2007-11-16 09:02 am (UTC)

(Link)

I already extended the rows/columns to make "screens" easier to work with. Now we just need to add multi-column support to the editor. :-)
[User Picture]From: [info]darius
2007-11-16 01:15 pm (UTC)

(Link)

That'd do it. :-)

I really must try out OFW on my iBook sometime -- all I've ever done so far is boot into it and go "yup, it's there".
From: (Anonymous)
2007-11-18 06:36 am (UTC)

(Link)

It has some handy graphics words at least on the OLPC. One of Mako's OLPCs had a broken OS install and so didn't do anything but run OFW, so in my first hour playing with it I wrote a program that bounced a green rectangle around the screen. Unfortunately I accidentally made it run forever, and I couldn't figure out how to interrupt it...
[User Picture]From: [info]lukego
2007-11-18 12:12 pm (UTC)

(Link)

I use the power button for interrupt since rebooting the firmware is so fast. :-)
From: (Anonymous)
2007-11-16 05:51 pm (UTC)

(Link)

I haven't read http://hack.org/~mc/software/ofpong.forth but it sounds similar.
From: (Anonymous)
2007-11-25 04:35 pm (UTC)

testing this one...

(Link)

Very interesting... as always! Cheers from -Switzerland-.