/extra/space-invaders/space-invaders.factor

http://github.com/abeaumont/factor · Factor · 402 lines · 338 code · 61 blank · 3 comment · 35 complexity · 2beb1ded2979f5384e21bfe8e5296879 MD5 · raw file

  1. ! Copyright (C) 2006 Chris Double.
  2. ! See http://factorcode.org/license.txt for BSD license.
  3. !
  4. USING:
  5. accessors
  6. alien.c-types
  7. alien.data
  8. arrays
  9. byte-arrays
  10. calendar
  11. combinators
  12. cpu.8080
  13. cpu.8080.emulator
  14. io.files
  15. io.pathnames
  16. kernel
  17. locals
  18. math
  19. math.order
  20. openal
  21. openal.alut
  22. opengl.gl
  23. sequences
  24. ui
  25. ui.gadgets
  26. ui.gestures
  27. ui.render
  28. specialized-arrays
  29. ;
  30. QUALIFIED: threads
  31. QUALIFIED: system
  32. SPECIALIZED-ARRAY: uchar
  33. IN: space-invaders
  34. TUPLE: space-invaders < cpu port1 port2i port2o port3o port4lo port4hi port5o bitmap sounds looping? ;
  35. CONSTANT: game-width 224
  36. CONSTANT: game-height 256
  37. : make-opengl-bitmap ( -- array )
  38. game-height game-width 3 * * uchar <c-array> ;
  39. : bitmap-index ( point -- index )
  40. #! Point is a {x y}.
  41. first2 game-width 3 * * swap 3 * + ;
  42. :: set-bitmap-pixel ( bitmap point color -- )
  43. point bitmap-index :> index
  44. color first index bitmap set-nth
  45. color second index 1 + bitmap set-nth
  46. color third index 2 + bitmap set-nth ;
  47. : get-bitmap-pixel ( point array -- color )
  48. #! Point is a {x y}. color is a {r g b}
  49. [ bitmap-index ] dip
  50. [ nth ] 2keep
  51. [ [ 1 + ] dip nth ] 2keep
  52. [ 2 + ] dip nth 3array ;
  53. CONSTANT: SOUND-SHOT 0
  54. CONSTANT: SOUND-UFO 1
  55. CONSTANT: SOUND-BASE-HIT 2
  56. CONSTANT: SOUND-INVADER-HIT 3
  57. CONSTANT: SOUND-WALK1 4
  58. CONSTANT: SOUND-WALK2 5
  59. CONSTANT: SOUND-WALK3 6
  60. CONSTANT: SOUND-WALK4 7
  61. CONSTANT: SOUND-UFO-HIT 8
  62. : init-sound ( index cpu filename -- )
  63. absolute-path swapd [ sounds>> nth AL_BUFFER ] dip
  64. create-buffer-from-wav set-source-param ;
  65. : init-sounds ( cpu -- )
  66. init-openal
  67. [ 9 gen-sources swap sounds<< ] keep
  68. [ SOUND-SHOT "vocab:space-invaders/resources/Shot.wav" init-sound ] keep
  69. [ SOUND-UFO "vocab:space-invaders/resources/Ufo.wav" init-sound ] keep
  70. [ sounds>> SOUND-UFO swap nth AL_LOOPING AL_TRUE set-source-param ] keep
  71. [ SOUND-BASE-HIT "vocab:space-invaders/resources/BaseHit.wav" init-sound ] keep
  72. [ SOUND-INVADER-HIT "vocab:space-invaders/resources/InvHit.Wav" init-sound ] keep
  73. [ SOUND-WALK1 "vocab:space-invaders/resources/Walk1.wav" init-sound ] keep
  74. [ SOUND-WALK2 "vocab:space-invaders/resources/Walk2.wav" init-sound ] keep
  75. [ SOUND-WALK3 "vocab:space-invaders/resources/Walk3.wav" init-sound ] keep
  76. [ SOUND-WALK4 "vocab:space-invaders/resources/Walk4.wav" init-sound ] keep
  77. [ SOUND-UFO-HIT "vocab:space-invaders/resources/UfoHit.wav" init-sound ] keep
  78. f swap looping?<< ;
  79. : cpu-init ( cpu -- cpu )
  80. make-opengl-bitmap over bitmap<<
  81. [ init-sounds ] keep
  82. [ reset ] keep ;
  83. : <space-invaders> ( -- cpu )
  84. space-invaders new cpu-init ;
  85. : play-invaders-sound ( cpu sound -- )
  86. swap sounds>> nth source-play ;
  87. : stop-invaders-sound ( cpu sound -- )
  88. swap sounds>> nth source-stop ;
  89. : read-port1 ( cpu -- byte )
  90. #! Port 1 maps the keys for space invaders
  91. #! Bit 0 = coin slot
  92. #! Bit 1 = two players button
  93. #! Bit 2 = one player button
  94. #! Bit 4 = player one fire
  95. #! Bit 5 = player one left
  96. #! Bit 6 = player one right
  97. [ port1>> dup 0xFE bitand ] keep
  98. port1<< ;
  99. : read-port2 ( cpu -- byte )
  100. #! Port 2 maps player 2 controls and dip switches
  101. #! Bit 0,1 = number of ships
  102. #! Bit 2 = mode (1=easy, 0=hard)
  103. #! Bit 4 = player two fire
  104. #! Bit 5 = player two left
  105. #! Bit 6 = player two right
  106. #! Bit 7 = show or hide coin info
  107. [ port2i>> 0x8F bitand ] keep
  108. port1>> 0x70 bitand bitor ;
  109. : read-port3 ( cpu -- byte )
  110. #! Used to compute a special formula
  111. [ port4hi>> 8 shift ] keep
  112. [ port4lo>> bitor ] keep
  113. port2o>> shift -8 shift 0xFF bitand ;
  114. M: space-invaders read-port ( port cpu -- byte )
  115. #! Read a byte from the hardware port. 'port' should
  116. #! be an 8-bit value.
  117. swap {
  118. { 1 [ read-port1 ] }
  119. { 2 [ read-port2 ] }
  120. { 3 [ read-port3 ] }
  121. [ 2drop 0 ]
  122. } case ;
  123. : write-port2 ( value cpu -- )
  124. #! Setting this value affects the value read from port 3
  125. port2o<< ;
  126. :: bit-newly-set? ( old-value new-value bit -- bool )
  127. new-value bit bit? [ old-value bit bit? not ] dip and ;
  128. : port3-newly-set? ( new-value cpu bit -- bool )
  129. [ port3o>> swap ] dip bit-newly-set? ;
  130. : port5-newly-set? ( new-value cpu bit -- bool )
  131. [ port5o>> swap ] dip bit-newly-set? ;
  132. : write-port3 ( value cpu -- )
  133. #! Connected to the sound hardware
  134. #! Bit 0 = spaceship sound (looped)
  135. #! Bit 1 = Shot
  136. #! Bit 2 = Your ship hit
  137. #! Bit 3 = Invader hit
  138. #! Bit 4 = Extended play sound
  139. over 0 bit? over looping?>> not and [
  140. dup SOUND-UFO play-invaders-sound
  141. t over looping?<<
  142. ] when
  143. over 0 bit? not over looping?>> and [
  144. dup SOUND-UFO stop-invaders-sound
  145. f over looping?<<
  146. ] when
  147. 2dup 0 port3-newly-set? [ dup SOUND-UFO play-invaders-sound ] when
  148. 2dup 1 port3-newly-set? [ dup SOUND-SHOT play-invaders-sound ] when
  149. 2dup 2 port3-newly-set? [ dup SOUND-BASE-HIT play-invaders-sound ] when
  150. 2dup 3 port3-newly-set? [ dup SOUND-INVADER-HIT play-invaders-sound ] when
  151. port3o<< ;
  152. : write-port4 ( value cpu -- )
  153. #! Affects the value returned by reading port 3
  154. [ port4hi>> ] keep
  155. [ port4lo<< ] keep
  156. port4hi<< ;
  157. : write-port5 ( value cpu -- )
  158. #! Plays sounds
  159. #! Bit 0 = invaders sound 1
  160. #! Bit 1 = invaders sound 2
  161. #! Bit 2 = invaders sound 3
  162. #! Bit 3 = invaders sound 4
  163. #! Bit 4 = spaceship hit
  164. #! Bit 5 = amplifier enabled/disabled
  165. 2dup 0 port5-newly-set? [ dup SOUND-WALK1 play-invaders-sound ] when
  166. 2dup 1 port5-newly-set? [ dup SOUND-WALK2 play-invaders-sound ] when
  167. 2dup 2 port5-newly-set? [ dup SOUND-WALK3 play-invaders-sound ] when
  168. 2dup 3 port5-newly-set? [ dup SOUND-WALK4 play-invaders-sound ] when
  169. 2dup 4 port5-newly-set? [ dup SOUND-UFO-HIT play-invaders-sound ] when
  170. port5o<< ;
  171. M: space-invaders write-port ( value port cpu -- )
  172. #! Write a byte to the hardware port, where 'port' is
  173. #! an 8-bit value.
  174. swap {
  175. { 2 [ write-port2 ] }
  176. { 3 [ write-port3 ] }
  177. { 4 [ write-port4 ] }
  178. { 5 [ write-port5 ] }
  179. [ 3drop ]
  180. } case ;
  181. M: space-invaders reset ( cpu -- )
  182. dup call-next-method
  183. 0 >>port1
  184. 0 >>port2i
  185. 0 >>port2o
  186. 0 >>port3o
  187. 0 >>port4lo
  188. 0 >>port4hi
  189. 0 >>port5o
  190. drop ;
  191. : gui-step ( cpu -- )
  192. [ read-instruction ] keep ! n cpu
  193. over get-cycles over inc-cycles
  194. [ swap instructions nth call( cpu -- ) ] keep
  195. [ pc>> 0xFFFF bitand ] keep
  196. pc<< ;
  197. : gui-frame/2 ( cpu -- )
  198. [ gui-step ] keep
  199. [ cycles>> ] keep
  200. over 16667 < [ ! cycles cpu
  201. nip gui-frame/2
  202. ] [
  203. [ [ 16667 - ] dip cycles<< ] keep
  204. dup last-interrupt>> 0x10 = [
  205. 0x08 over last-interrupt<< 0x08 swap interrupt
  206. ] [
  207. 0x10 over last-interrupt<< 0x10 swap interrupt
  208. ] if
  209. ] if ;
  210. : gui-frame ( cpu -- )
  211. dup gui-frame/2 gui-frame/2 ;
  212. : coin-down ( cpu -- )
  213. [ port1>> 1 bitor ] keep port1<< ;
  214. : coin-up ( cpu -- )
  215. [ port1>> 255 1 - bitand ] keep port1<< ;
  216. : player1-down ( cpu -- )
  217. [ port1>> 4 bitor ] keep port1<< ;
  218. : player1-up ( cpu -- )
  219. [ port1>> 255 4 - bitand ] keep port1<< ;
  220. : player2-down ( cpu -- )
  221. [ port1>> 2 bitor ] keep port1<< ;
  222. : player2-up ( cpu -- )
  223. [ port1>> 255 2 - bitand ] keep port1<< ;
  224. : fire-down ( cpu -- )
  225. [ port1>> 0x10 bitor ] keep port1<< ;
  226. : fire-up ( cpu -- )
  227. [ port1>> 255 0x10 - bitand ] keep port1<< ;
  228. : left-down ( cpu -- )
  229. [ port1>> 0x20 bitor ] keep port1<< ;
  230. : left-up ( cpu -- )
  231. [ port1>> 255 0x20 - bitand ] keep port1<< ;
  232. : right-down ( cpu -- )
  233. [ port1>> 0x40 bitor ] keep port1<< ;
  234. : right-up ( cpu -- )
  235. [ port1>> 255 0x40 - bitand ] keep port1<< ;
  236. TUPLE: invaders-gadget < gadget cpu quit? windowed? ;
  237. invaders-gadget H{
  238. { T{ key-down f f "ESC" } [ t over quit?<< dup windowed?>> [ close-window ] [ drop ] if ] }
  239. { T{ key-down f f "BACKSPACE" } [ cpu>> coin-down ] }
  240. { T{ key-up f f "BACKSPACE" } [ cpu>> coin-up ] }
  241. { T{ key-down f f "1" } [ cpu>> player1-down ] }
  242. { T{ key-up f f "1" } [ cpu>> player1-up ] }
  243. { T{ key-down f f "2" } [ cpu>> player2-down ] }
  244. { T{ key-up f f "2" } [ cpu>> player2-up ] }
  245. { T{ key-down f f "UP" } [ cpu>> fire-down ] }
  246. { T{ key-up f f "UP" } [ cpu>> fire-up ] }
  247. { T{ key-down f f "LEFT" } [ cpu>> left-down ] }
  248. { T{ key-up f f "LEFT" } [ cpu>> left-up ] }
  249. { T{ key-down f f "RIGHT" } [ cpu>> right-down ] }
  250. { T{ key-up f f "RIGHT" } [ cpu>> right-up ] }
  251. } set-gestures
  252. : <invaders-gadget> ( cpu -- gadget )
  253. invaders-gadget new
  254. swap >>cpu
  255. f >>quit? ;
  256. M: invaders-gadget pref-dim* drop { 224 256 } ;
  257. M: invaders-gadget draw-gadget* ( gadget -- )
  258. 0 0 glRasterPos2i
  259. 1.0 -1.0 glPixelZoom
  260. [ 224 256 GL_RGB GL_UNSIGNED_BYTE ] dip
  261. cpu>> bitmap>> glDrawPixels ;
  262. CONSTANT: black { 0 0 0 }
  263. CONSTANT: white { 255 255 255 }
  264. CONSTANT: green { 0 255 0 }
  265. CONSTANT: red { 255 0 0 }
  266. : addr>xy ( addr -- point )
  267. #! Convert video RAM address to base X Y value. point is a {x y}.
  268. 0x2400 - ! n
  269. dup 0x1f bitand 8 * 255 swap - ! n y
  270. swap -5 shift swap 2array ;
  271. : plot-bitmap-pixel ( bitmap point color -- )
  272. #! point is a {x y}. color is a {r g b}.
  273. set-bitmap-pixel ;
  274. : get-point-color ( point -- color )
  275. #! Return the color to use for the given x/y position.
  276. first2
  277. {
  278. { [ dup 184 238 between? pick 0 223 between? and ] [ 2drop green ] }
  279. { [ dup 240 247 between? pick 16 133 between? and ] [ 2drop green ] }
  280. { [ dup 247 215 - 247 184 - between? pick 0 223 between? and ] [ 2drop red ] }
  281. [ 2drop white ]
  282. } cond ;
  283. : plot-bitmap-bits ( bitmap point byte bit -- )
  284. #! point is a {x y}.
  285. [ first2 ] 2dip
  286. dup swapd -1 * shift 1 bitand 0 =
  287. [ - 2array ] dip
  288. [ black ] [ dup get-point-color ] if
  289. plot-bitmap-pixel ;
  290. : do-bitmap-update ( bitmap value addr -- )
  291. addr>xy swap
  292. [ 0 plot-bitmap-bits ] 3keep
  293. [ 1 plot-bitmap-bits ] 3keep
  294. [ 2 plot-bitmap-bits ] 3keep
  295. [ 3 plot-bitmap-bits ] 3keep
  296. [ 4 plot-bitmap-bits ] 3keep
  297. [ 5 plot-bitmap-bits ] 3keep
  298. [ 6 plot-bitmap-bits ] 3keep
  299. 7 plot-bitmap-bits ;
  300. M: space-invaders update-video ( value addr cpu -- )
  301. over 0x2400 >= [
  302. bitmap>> -rot do-bitmap-update
  303. ] [
  304. 3drop
  305. ] if ;
  306. : sync-frame ( micros -- micros )
  307. #! Sleep until the time for the next frame arrives.
  308. 1000 60 / >fixnum + gmt timestamp>micros - dup 0 >
  309. [ milliseconds threads:sleep ] [ drop threads:yield ] if gmt timestamp>micros ;
  310. : invaders-process ( micros gadget -- )
  311. #! Run a space invaders gadget inside a
  312. #! concurrent process. Messages can be sent to
  313. #! signal key presses, etc.
  314. dup quit?>> [
  315. 2drop
  316. ] [
  317. [ sync-frame ] dip
  318. [ cpu>> gui-frame ] keep
  319. [ relayout-1 ] keep
  320. invaders-process
  321. ] if ;
  322. M: invaders-gadget graft* ( gadget -- )
  323. dup cpu>> init-sounds
  324. f over quit?<<
  325. [ gmt timestamp>micros swap invaders-process ] curry
  326. "Space invaders" threads:spawn drop ;
  327. M: invaders-gadget ungraft* ( gadget -- )
  328. t swap quit?<< ;
  329. : (run) ( title cpu rom-info -- )
  330. over load-rom* <invaders-gadget> t >>windowed? swap open-window ;
  331. CONSTANT: rom-info {
  332. { 0x0000 "invaders/invaders.h" }
  333. { 0x0800 "invaders/invaders.g" }
  334. { 0x1000 "invaders/invaders.f" }
  335. { 0x1800 "invaders/invaders.e" }
  336. }
  337. : run-invaders ( -- )
  338. [
  339. "Space Invaders" <space-invaders> rom-info (run)
  340. ] with-ui ;
  341. MAIN: run-invaders