377 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Factor
		
	
	
		
			Executable File
		
	
			
		
		
	
	
			377 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Factor
		
	
	
		
			Executable File
		
	
! Copyright (C) 2006 Chris Double.
 | 
						|
! See http://factorcode.org/license.txt for BSD license.
 | 
						|
!
 | 
						|
USING: cpu.8080 cpu.8080.emulator openal math alien.c-types
 | 
						|
sequences kernel shuffle arrays io.files combinators ui.gestures
 | 
						|
ui.gadgets ui.render opengl.gl system match
 | 
						|
ui byte-arrays combinators.lib qualified ;
 | 
						|
QUALIFIED: threads
 | 
						|
IN: space-invaders
 | 
						|
 | 
						|
TUPLE: space-invaders port1 port2i port2o port3o port4lo port4hi port5o bitmap sounds looping? ;
 | 
						|
: game-width 224  ; inline
 | 
						|
: game-height 256 ; inline
 | 
						|
 | 
						|
: make-opengl-bitmap ( -- array )
 | 
						|
  game-height game-width 3 * * <byte-array> ;
 | 
						|
 | 
						|
: bitmap-index ( point -- index )
 | 
						|
  #! Point is a {x y}.
 | 
						|
  first2 game-width 3 * * swap 3 * + ;
 | 
						|
 | 
						|
: set-bitmap-pixel ( color point array -- )
 | 
						|
  #! 'color' is a {r g b}. Point is {x y}.
 | 
						|
  [ bitmap-index ] dip ! color index array
 | 
						|
  [ [ first ] 2dip set-uchar-nth ] 3keep
 | 
						|
  [ [ second ] 2dip [ 1 + ] dip set-uchar-nth ] 3keep
 | 
						|
  [ third ] 2dip [ 2 + ] dip set-uchar-nth ;
 | 
						|
 | 
						|
: get-bitmap-pixel ( point array -- color )
 | 
						|
  #! Point is a {x y}. color is a {r g b} 
 | 
						|
  [ bitmap-index ] dip
 | 
						|
  [ uint-nth ] 2keep
 | 
						|
  [ [ 1 + ] dip uchar-nth ] 2keep
 | 
						|
  [ 2 + ] dip uchar-nth 3array ;
 | 
						|
  
 | 
						|
: SOUND-SHOT         ( -- number ) 0 ;
 | 
						|
: SOUND-UFO          ( -- number ) 1 ;
 | 
						|
: SOUND-BASE-HIT     ( -- number ) 2 ;
 | 
						|
: SOUND-INVADER-HIT  ( -- number ) 3 ;
 | 
						|
: SOUND-WALK1        ( -- number ) 4 ;
 | 
						|
: SOUND-WALK2        ( -- number ) 5 ;
 | 
						|
: SOUND-WALK3        ( -- number ) 6 ;
 | 
						|
: SOUND-WALK4        ( -- number ) 7 ;
 | 
						|
: SOUND-UFO-HIT      ( -- number ) 8 ;
 | 
						|
 | 
						|
: init-sound ( index cpu filename  -- )
 | 
						|
  swapd >r space-invaders-sounds nth AL_BUFFER r> 
 | 
						|
  create-buffer-from-wav set-source-param ; 
 | 
						|
 | 
						|
: init-sounds ( cpu -- )
 | 
						|
  init-openal
 | 
						|
  [ 9 gen-sources swap set-space-invaders-sounds ] keep
 | 
						|
  [ SOUND-SHOT        "resource:extra/space-invaders/resources/Shot.wav" init-sound ] keep 
 | 
						|
  [ SOUND-UFO         "resource:extra/space-invaders/resources/Ufo.wav" init-sound ] keep 
 | 
						|
  [ space-invaders-sounds SOUND-UFO swap nth AL_LOOPING AL_TRUE set-source-param ] keep
 | 
						|
  [ SOUND-BASE-HIT    "resource:extra/space-invaders/resources/BaseHit.wav" init-sound ] keep 
 | 
						|
  [ SOUND-INVADER-HIT "resource:extra/space-invaders/resources/InvHit.wav" init-sound ] keep 
 | 
						|
  [ SOUND-WALK1       "resource:extra/space-invaders/resources/Walk1.wav" init-sound ] keep 
 | 
						|
  [ SOUND-WALK2       "resource:extra/space-invaders/resources/Walk2.wav" init-sound ] keep 
 | 
						|
  [ SOUND-WALK3       "resource:extra/space-invaders/resources/Walk3.wav" init-sound ] keep 
 | 
						|
  [ SOUND-WALK4       "resource:extra/space-invaders/resources/Walk4.wav" init-sound ] keep 
 | 
						|
  [ SOUND-UFO-HIT    "resource:extra/space-invaders/resources/UfoHit.wav" init-sound ] keep
 | 
						|
  f swap set-space-invaders-looping? ;
 | 
						|
 | 
						|
: <space-invaders> ( -- cpu )
 | 
						|
  <cpu> space-invaders construct-delegate
 | 
						|
  make-opengl-bitmap over set-space-invaders-bitmap
 | 
						|
  [ init-sounds ] keep
 | 
						|
  [ reset ] keep ;
 | 
						|
 | 
						|
: play-invaders-sound ( cpu sound -- )
 | 
						|
  swap space-invaders-sounds nth source-play ;
 | 
						|
 | 
						|
: stop-invaders-sound ( cpu sound -- )
 | 
						|
  swap space-invaders-sounds nth source-stop ;
 | 
						|
 | 
						|
: read-port1 ( cpu -- byte )
 | 
						|
  #! Port 1 maps the keys for space invaders
 | 
						|
  #! Bit 0 = coin slot
 | 
						|
  #! Bit 1 = two players button
 | 
						|
  #! Bit 2 = one player button
 | 
						|
  #! Bit 4 = player one fire
 | 
						|
  #! Bit 5 = player one left
 | 
						|
  #! Bit 6 = player one right
 | 
						|
  [ space-invaders-port1 dup HEX: FE bitand ] keep 
 | 
						|
 set-space-invaders-port1 ;
 | 
						|
 | 
						|
: read-port2 ( cpu -- byte )
 | 
						|
  #! Port 2 maps player 2 controls and dip switches
 | 
						|
  #! Bit 0,1 = number of ships
 | 
						|
  #! Bit 2   = mode (1=easy, 0=hard)
 | 
						|
  #! Bit 4   = player two fire
 | 
						|
  #! Bit 5   = player two left
 | 
						|
  #! Bit 6   = player two right
 | 
						|
  #! Bit 7   = show or hide coin info
 | 
						|
  [ space-invaders-port2i HEX: 8F bitand ] keep 
 | 
						|
  space-invaders-port1 HEX: 70 bitand bitor ;
 | 
						|
 | 
						|
: read-port3 ( cpu -- byte )
 | 
						|
  #! Used to compute a special formula
 | 
						|
  [ space-invaders-port4hi 8 shift ] keep 
 | 
						|
  [ space-invaders-port4lo bitor ] keep 
 | 
						|
  space-invaders-port2o shift -8 shift HEX: FF bitand ;
 | 
						|
 | 
						|
M: space-invaders read-port ( port cpu -- byte )
 | 
						|
  #! Read a byte from the hardware port. 'port' should
 | 
						|
  #! be an 8-bit value.
 | 
						|
  swap {
 | 
						|
    { 1 [ read-port1 ] }
 | 
						|
    { 2 [ read-port2 ] }
 | 
						|
    { 3 [ read-port3 ] }
 | 
						|
    [ 2drop 0 ]
 | 
						|
  } case ;
 | 
						|
 | 
						|
: write-port2 ( value cpu -- )
 | 
						|
  #! Setting this value affects the value read from port 3
 | 
						|
  set-space-invaders-port2o ;
 | 
						|
 | 
						|
: bit-newly-set? ( old-value new-value bit -- bool )
 | 
						|
  tuck bit? >r bit? not r> and ;
 | 
						|
 | 
						|
: port3-newly-set? ( new-value cpu bit -- bool )
 | 
						|
  >r space-invaders-port3o swap r> bit-newly-set? ;
 | 
						|
 | 
						|
: port5-newly-set? ( new-value cpu bit -- bool )
 | 
						|
  >r space-invaders-port5o swap r> bit-newly-set? ;
 | 
						|
 | 
						|
: write-port3 ( value cpu -- )
 | 
						|
  #! Connected to the sound hardware
 | 
						|
  #! Bit 0 = spaceship sound (looped)
 | 
						|
  #! Bit 1 = Shot 
 | 
						|
  #! Bit 2 = Your ship hit
 | 
						|
  #! Bit 3 = Invader hit
 | 
						|
  #! Bit 4 = Extended play sound
 | 
						|
  over 0 bit? over space-invaders-looping? not and [ 
 | 
						|
    dup SOUND-UFO play-invaders-sound 
 | 
						|
    t over set-space-invaders-looping?
 | 
						|
  ] when 
 | 
						|
  over 0 bit? not over space-invaders-looping? and [ 
 | 
						|
    dup SOUND-UFO stop-invaders-sound 
 | 
						|
    f over set-space-invaders-looping?
 | 
						|
  ] when 
 | 
						|
  2dup 0 port3-newly-set? [ dup SOUND-UFO  play-invaders-sound ] when
 | 
						|
  2dup 1 port3-newly-set? [ dup SOUND-SHOT play-invaders-sound ] when
 | 
						|
  2dup 2 port3-newly-set? [ dup SOUND-BASE-HIT play-invaders-sound ] when
 | 
						|
  2dup 3 port3-newly-set? [ dup SOUND-INVADER-HIT play-invaders-sound ] when
 | 
						|
  set-space-invaders-port3o ;
 | 
						|
 | 
						|
: write-port4 ( value cpu -- )
 | 
						|
  #! Affects the value returned by reading port 3
 | 
						|
  [ space-invaders-port4hi ] keep 
 | 
						|
  [ set-space-invaders-port4lo ] keep 
 | 
						|
  set-space-invaders-port4hi ;
 | 
						|
 | 
						|
: write-port5 ( value cpu -- )
 | 
						|
  #! Plays sounds
 | 
						|
  #! Bit 0 = invaders sound 1
 | 
						|
  #! Bit 1 = invaders sound 2
 | 
						|
  #! Bit 2 = invaders sound 3
 | 
						|
  #! Bit 3 = invaders sound 4
 | 
						|
  #! Bit 4 = spaceship hit 
 | 
						|
  #! Bit 5 = amplifier enabled/disabled
 | 
						|
  2dup 0 port5-newly-set? [ dup SOUND-WALK1 play-invaders-sound ] when
 | 
						|
  2dup 1 port5-newly-set? [ dup SOUND-WALK2 play-invaders-sound ] when
 | 
						|
  2dup 2 port5-newly-set? [ dup SOUND-WALK3 play-invaders-sound ] when
 | 
						|
  2dup 3 port5-newly-set? [ dup SOUND-WALK4 play-invaders-sound ] when
 | 
						|
  2dup 4 port5-newly-set? [ dup SOUND-UFO-HIT play-invaders-sound ] when
 | 
						|
  set-space-invaders-port5o ;
 | 
						|
 | 
						|
M: space-invaders write-port ( value port cpu -- )
 | 
						|
  #! Write a byte to the hardware port, where 'port' is
 | 
						|
  #! an 8-bit value.  
 | 
						|
  swap {
 | 
						|
    { 2 [ write-port2 ] }
 | 
						|
    { 3 [ write-port3 ] }
 | 
						|
    { 4 [ write-port4 ] }
 | 
						|
    { 5 [ write-port5 ] }
 | 
						|
    [ 3drop ]
 | 
						|
  } case ;
 | 
						|
 | 
						|
M: space-invaders reset ( cpu -- )
 | 
						|
  [ delegate reset ] keep
 | 
						|
  [ 0 swap set-space-invaders-port1 ] keep
 | 
						|
  [ 0 swap set-space-invaders-port2i ] keep
 | 
						|
  [ 0 swap set-space-invaders-port2o ] keep
 | 
						|
  [ 0 swap set-space-invaders-port3o ] keep
 | 
						|
  [ 0 swap set-space-invaders-port4lo ] keep
 | 
						|
  [ 0 swap set-space-invaders-port4hi ] keep
 | 
						|
  0 swap set-space-invaders-port5o ;
 | 
						|
 | 
						|
: gui-step ( cpu -- )
 | 
						|
  [ read-instruction ] keep ! n cpu
 | 
						|
  over get-cycles over inc-cycles
 | 
						|
  [ swap instructions case ] keep  
 | 
						|
  [ cpu-pc HEX: FFFF bitand ] keep 
 | 
						|
  set-cpu-pc ;
 | 
						|
 | 
						|
: gui-frame/2 ( cpu -- )
 | 
						|
  [ gui-step ] keep
 | 
						|
  [ cpu-cycles ] keep
 | 
						|
  over 16667 < [ ! cycles cpu
 | 
						|
    nip gui-frame/2
 | 
						|
  ] [
 | 
						|
    [ >r 16667 - r> set-cpu-cycles ] keep
 | 
						|
    dup cpu-last-interrupt HEX: 10 = [
 | 
						|
      HEX: 08 over set-cpu-last-interrupt HEX: 08 swap interrupt
 | 
						|
    ] [
 | 
						|
      HEX: 10 over set-cpu-last-interrupt HEX: 10 swap interrupt
 | 
						|
    ] if     
 | 
						|
  ] if ;
 | 
						|
 | 
						|
: gui-frame ( cpu -- )
 | 
						|
  dup gui-frame/2 gui-frame/2 ;
 | 
						|
 | 
						|
: coin-down ( cpu -- )
 | 
						|
  [ space-invaders-port1 1 bitor ] keep set-space-invaders-port1 ;
 | 
						|
 | 
						|
: coin-up ( cpu --  )
 | 
						|
  [ space-invaders-port1 255 1 - bitand ] keep set-space-invaders-port1 ;
 | 
						|
 | 
						|
: player1-down ( cpu -- )
 | 
						|
  [ space-invaders-port1 4 bitor ] keep set-space-invaders-port1 ;
 | 
						|
 | 
						|
: player1-up ( cpu -- )
 | 
						|
  [ space-invaders-port1 255 4 - bitand ] keep set-space-invaders-port1 ;
 | 
						|
 | 
						|
: player2-down ( cpu -- )
 | 
						|
  [ space-invaders-port1 2 bitor ] keep set-space-invaders-port1 ;
 | 
						|
 | 
						|
: player2-up ( cpu -- )
 | 
						|
  [ space-invaders-port1 255 2 - bitand ] keep set-space-invaders-port1 ;
 | 
						|
 | 
						|
: fire-down ( cpu -- )
 | 
						|
  [ space-invaders-port1 HEX: 10 bitor ] keep set-space-invaders-port1 ;
 | 
						|
 | 
						|
: fire-up ( cpu -- )
 | 
						|
  [ space-invaders-port1 255 HEX: 10 - bitand ] keep set-space-invaders-port1 ;
 | 
						|
 | 
						|
: left-down ( cpu -- )
 | 
						|
  [ space-invaders-port1 HEX: 20 bitor ] keep set-space-invaders-port1 ;
 | 
						|
 | 
						|
: left-up ( cpu -- )
 | 
						|
  [ space-invaders-port1 255 HEX: 20 - bitand ] keep set-space-invaders-port1 ;
 | 
						|
 | 
						|
: right-down ( cpu -- )
 | 
						|
  [ space-invaders-port1 HEX: 40 bitor ] keep set-space-invaders-port1 ;
 | 
						|
 | 
						|
: right-up ( cpu -- )
 | 
						|
  [ space-invaders-port1 255 HEX: 40 - bitand ] keep set-space-invaders-port1 ;
 | 
						|
 | 
						|
 | 
						|
TUPLE: invaders-gadget cpu quit? ;
 | 
						|
 | 
						|
invaders-gadget H{
 | 
						|
    { T{ key-down f f "ESC" }    [ t swap set-invaders-gadget-quit? ] }
 | 
						|
    { T{ key-down f f "BACKSPACE" } [ invaders-gadget-cpu coin-down ] }
 | 
						|
    { T{ key-up   f f "BACKSPACE" } [ invaders-gadget-cpu coin-up ] }
 | 
						|
    { T{ key-down f f "1" }         [ invaders-gadget-cpu player1-down ] }
 | 
						|
    { T{ key-up   f f "1" }         [ invaders-gadget-cpu player1-up ] }
 | 
						|
    { T{ key-down f f "2" }         [ invaders-gadget-cpu player2-down ] }
 | 
						|
    { T{ key-up   f f "2" }         [ invaders-gadget-cpu player2-up ] }
 | 
						|
    { T{ key-down f f "UP" }        [ invaders-gadget-cpu fire-down ] }
 | 
						|
    { T{ key-up   f f "UP" }        [ invaders-gadget-cpu fire-up ] }
 | 
						|
    { T{ key-down f f "LEFT" }      [ invaders-gadget-cpu left-down ] }
 | 
						|
    { T{ key-up   f f "LEFT" }      [ invaders-gadget-cpu left-up ] }
 | 
						|
    { T{ key-down f f "RIGHT" }     [ invaders-gadget-cpu right-down ] }
 | 
						|
    { T{ key-up   f f "RIGHT" }     [ invaders-gadget-cpu right-up ] }
 | 
						|
  } set-gestures 
 | 
						|
 | 
						|
: <invaders-gadget> ( cpu -- gadget ) 
 | 
						|
  invaders-gadget construct-gadget
 | 
						|
  [ set-invaders-gadget-cpu ] keep
 | 
						|
  f over set-invaders-gadget-quit? ;
 | 
						|
 | 
						|
M: invaders-gadget pref-dim* drop { 224 256 0 } ;
 | 
						|
 | 
						|
M: invaders-gadget draw-gadget* ( gadget -- )
 | 
						|
  0 0 glRasterPos2i
 | 
						|
  1.0 -1.0 glPixelZoom
 | 
						|
  >r 224 256 GL_RGB GL_UNSIGNED_BYTE r>
 | 
						|
  invaders-gadget-cpu space-invaders-bitmap glDrawPixels ;
 | 
						|
 | 
						|
: black { 0 0 0 } ;
 | 
						|
: white { 255 255 255 } ;
 | 
						|
: green { 0 255 0 } ;
 | 
						|
: red   { 255 0 0 } ;
 | 
						|
 | 
						|
: addr>xy ( addr -- point )
 | 
						|
  #! Convert video RAM address to base X Y value. point is a {x y}.
 | 
						|
  HEX: 2400 - ! n
 | 
						|
  dup HEX: 1f bitand 8 * 255 swap - ! n y
 | 
						|
  swap -5 shift swap 2array ;
 | 
						|
 | 
						|
: plot-bitmap-pixel ( bitmap point color -- )
 | 
						|
  #! point is a {x y}. color is a {r g b}.
 | 
						|
  spin set-bitmap-pixel ;
 | 
						|
 | 
						|
: within ( n a b -- bool )
 | 
						|
  #! n >= a and n <= b
 | 
						|
  rot tuck swap <= >r swap >= r> and ;
 | 
						|
 | 
						|
: get-point-color ( point -- color )
 | 
						|
  #! Return the color to use for the given x/y position.
 | 
						|
  first2
 | 
						|
  {
 | 
						|
    { [ dup 184 238 within pick 0 223 within and ] [ 2drop green ] }
 | 
						|
    { [ dup 240 247 within pick 16 133 within and ] [ 2drop green ] }
 | 
						|
    { [ dup 247 215 - 247 184 - within pick 0 223 within and ] [ 2drop red ] }
 | 
						|
    [ 2drop white ]
 | 
						|
  } cond ;
 | 
						|
 | 
						|
: plot-bitmap-bits ( bitmap point byte bit -- )
 | 
						|
  #! point is a {x y}.
 | 
						|
  [ first2 ] 2dip
 | 
						|
  dup swapd -1 * shift 1 bitand 0 =
 | 
						|
  [ - 2array ] dip
 | 
						|
  [ black ] [ dup get-point-color ] if
 | 
						|
  plot-bitmap-pixel ;
 | 
						|
 | 
						|
: do-bitmap-update ( bitmap value addr -- )
 | 
						|
  addr>xy swap 
 | 
						|
  [ 0 plot-bitmap-bits ] 3keep
 | 
						|
  [ 1 plot-bitmap-bits ] 3keep
 | 
						|
  [ 2 plot-bitmap-bits ] 3keep
 | 
						|
  [ 3 plot-bitmap-bits ] 3keep
 | 
						|
  [ 4 plot-bitmap-bits ] 3keep
 | 
						|
  [ 5 plot-bitmap-bits ] 3keep
 | 
						|
  [ 6 plot-bitmap-bits ] 3keep
 | 
						|
  7 plot-bitmap-bits ;
 | 
						|
 | 
						|
M: space-invaders update-video ( value addr cpu -- )  
 | 
						|
  over HEX: 2400 >= [
 | 
						|
    space-invaders-bitmap -rot do-bitmap-update
 | 
						|
  ] [
 | 
						|
    3drop
 | 
						|
  ] if ;
 | 
						|
 | 
						|
: sync-frame ( millis -- millis )
 | 
						|
  #! Sleep until the time for the next frame arrives.
 | 
						|
  1000 60 / >fixnum + millis - dup 0 >
 | 
						|
  [ threads:sleep ] [ drop threads:yield ] if millis ;
 | 
						|
 | 
						|
: invaders-process ( millis gadget -- )
 | 
						|
  #! Run a space invaders gadget inside a 
 | 
						|
  #! concurrent process. Messages can be sent to
 | 
						|
  #! signal key presses, etc.
 | 
						|
  dup invaders-gadget-quit? [
 | 
						|
    2drop
 | 
						|
  ] [
 | 
						|
    [ sync-frame ] dip
 | 
						|
    [ invaders-gadget-cpu gui-frame ] keep
 | 
						|
    [ relayout-1 ] keep
 | 
						|
    invaders-process 
 | 
						|
  ] if ;
 | 
						|
 | 
						|
M: invaders-gadget graft* ( gadget -- )
 | 
						|
  dup invaders-gadget-cpu init-sounds
 | 
						|
  f over set-invaders-gadget-quit?
 | 
						|
  [ millis swap invaders-process ] curry
 | 
						|
  "Space invaders" threads:spawn drop ;
 | 
						|
 | 
						|
M: invaders-gadget ungraft* ( gadget -- )
 | 
						|
 t swap set-invaders-gadget-quit? ;
 | 
						|
 | 
						|
: (run) ( title cpu rom-info -- )
 | 
						|
  over load-rom* <invaders-gadget> swap open-window ;
 | 
						|
 | 
						|
: run ( -- )  
 | 
						|
  "Space Invaders" <space-invaders> {
 | 
						|
    { HEX: 0000 "invaders/invaders.h" }
 | 
						|
    { HEX: 0800 "invaders/invaders.g" }
 | 
						|
    { HEX: 1000 "invaders/invaders.f" }
 | 
						|
    { HEX: 1800 "invaders/invaders.e" }
 | 
						|
  } [ (run) ] with-ui ;
 | 
						|
 | 
						|
MAIN: run
 |