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