Fixed makefile for GCC (MinGW) builds.
[debian-beebasm.git] / demo.6502
blob499200f8b477b4e0bec75eceb6e6c9563abff008
1 \ ******************************************************************\r
2 \ *\r
3 \ *             BeebAsm demo\r
4 \ *\r
5 \ *             Spinning star globe\r
6 \ *\r
7 \ *             Change the speed of rotation with Z and X keys\r
8 \ *             Press Esc to quit\r
9 \ *\r
10 \ *             Try assembling the code with debugrasters = TRUE (line 20)\r
11 \ *             It shows how the frame is split up into various stages of\r
12 \ *             processing.\r
13 \ *\r
14 \ *             The red part is where we start to plot the dots - starting as\r
15 \ *             soon before the first screenline of the next frame is rastered\r
16 \ *             as possible.\r
17 \ *\r
18 \ *             The magenta part is a small loop where we wait for 'vsync'.\r
19 \ *             It's not actually vsync, but in fact a fixed time from actual\r
20 \ *             vsync (timed by timer 1) from which point we can start to erase\r
21 \ *             points from the top of the screen down.\r
22 \ *\r
23 \ *             The blue part is the time when we are erasing dots.  Because\r
24 \ *             the dots are sorted from top to bottom of screen, we can\r
25 \ *             overlap these updates with the actual screen rasterisation.\r
26 \ *\r
27 \ ******************************************************************\r
29 \\ Define globals\r
31 numdots                 = 160\r
32 radius                  = 100\r
33 timerlength             = 64*8*26\r
34 debugrasters    = FALSE\r
37 \\ Define some zp locations\r
39 ORG 0\r
41 .xpos                   SKIP 1\r
42 .ypos                   SKIP 1\r
43 .colour                 SKIP 1\r
44 .write                  SKIP 2\r
45 .vsync                  SKIP 1\r
46 .angle                  SKIP 2\r
47 .speed                  SKIP 2\r
48 .counter                SKIP 1\r
49 .temp                   SKIP 1\r
50 .behindflag             SKIP 1\r
53 \\ Set start address\r
55 ORG &1100\r
57 \ ******************************************************************\r
58 \ *     The entry point of the demo\r
59 \ ******************************************************************\r
61 .start\r
63         \\ Set up hardware state and interrupts\r
65         SEI\r
66         LDX #&FF:TXS                            ; reset stack\r
67         STX &FE44:STX &FE45\r
68         LDA #&7F:STA &FE4E                      ; disable all interrupts\r
69         STA &FE43                                       ; set keyboard data direction\r
70         LDA #&C2:STA &FE4E                      ; enable VSync and timer interrupt\r
71         LDA #&0F:STA &FE42                      ; set addressable latch for writing\r
72         LDA #3:STA &FE40                        ; keyboard write enable\r
73         LDA #0:STA &FE4B                        ; timer 1 one shot mode\r
74         LDA #LO(irq):STA &204\r
75         LDA #HI(irq):STA &205           ; set interrupt handler\r
77         \\ Clear the screen\r
79         LDX #&40\r
80         LDA #0\r
81         TAY\r
82 .clearloop\r
83         STA &4000,Y\r
84         INY\r
85         BNE clearloop\r
86         INC clearloop+2\r
87         DEX\r
88         BNE clearloop\r
90         \\ Set up CRTC for MODE 2\r
92         LDX #13\r
93 .crtcloop\r
94         STX &FE00\r
95         LDA crtcregs,X\r
96         STA &FE01\r
97         DEX\r
98         BPL crtcloop\r
100         \\ Set up video ULA for MODE 2\r
102         LDA #&F4\r
103         STA &FE20\r
105         \\ Set up palette for MODE 2\r
107         LDX #15\r
108 .palloop\r
109         LDA paldata,X\r
110         STA &FE21\r
111         ORA #&80\r
112         STA &FE21\r
113         DEX\r
114         BPL palloop\r
116         \\ Initialise vars\r
118         LDA #0:STA angle:STA angle+1\r
119         STA vsync\r
120         STA speed\r
121         LDA #1:STA speed+1\r
123         \\ Enable interrupts, ready to start the main loop\r
125         CLI\r
127         \\ First we wait for 'vsync' so we are synchronised\r
129 .initialwait\r
130         LDA vsync:BEQ initialwait:LDA #0:STA vsync\r
132         \\ This is the main loop!\r
134 .mainloop\r
136         \\ Plot every dot on the screen\r
138         LDX #0\r
139 .plotdotloop\r
140         STX counter\r
142         ; setup y pos ready for plot routine\r
144         LDA doty,X:STA ypos\r
146         ; get sin index\r
148         CLC:LDA dotx,X:ADC angle+1:TAY\r
149         CLC:ADC #64:STA behindflag\r
151         ; get colour from sin index\r
153         LDA coltable,Y:STA colour\r
155         ; perform sin(x) * radius\r
156         ; discussion of the multiplication method below in the table setup\r
158         SEC:LDA sintable,Y:STA temp:SBC dotr,X\r
159         BCS noneg:EOR #&FF:ADC #1:.noneg\r
160         CPY #128:TAY:BCS negativesine\r
162         CLC:LDA dotr,X:ADC temp:TAX\r
163         BCS morethan256:SEC\r
164         LDA multtab1,X:SBC multtab1,Y:JMP donemult\r
165         .morethan256\r
166         LDA multtab2,X:SBC multtab1,Y:JMP donemult\r
168         .negativesine\r
169         CLC:LDA dotr,X:ADC temp:TAX\r
170         BCS morethan256b:SEC\r
171         LDA multtab1,Y:SBC multtab1,X:JMP donemult\r
172         .morethan256b\r
173         LDA multtab1,Y:SBC multtab2,X\r
174         .donemult\r
176         CLC:ADC #64:STA xpos\r
178         ; routine to plot a dot\r
179         ; also we remember the calculated screen address in the dot tables\r
181         LDA ypos:LSR A:LSR A:AND #&FE\r
182         TAX\r
183         LDA xpos:AND #&FE:ASL A:ASL A\r
184         STA write\r
185         LDY counter:STA olddotaddrlo,Y\r
186         TXA:ADC #&40:STA write+1:STA olddotaddrhi,Y\r
187         LDA ypos:AND #7:STA olddotaddry,Y:TAY\r
188         LDA xpos:LSR A:LDA colour:ROL A:TAX\r
189         LDA colours,X\r
190         ORA (write),Y\r
191         STA (write),Y\r
192         BIT behindflag:BMI behind\r
194         ; if the dot is in front, we double its size\r
196         DEY:BPL samescreenrow\r
197         DEC write+1:DEC write+1:LDY #7:.samescreenrow\r
198         LDA colours,X\r
199         ORA (write),Y\r
200         STA (write),Y\r
201         .behind\r
203         ; loop to the next dot\r
205         LDX counter\r
206         INX:CPX #numdots\r
207         BEQ waitforvsync\r
208         JMP plotdotloop\r
210         \\ Wait for VSync here\r
212 .waitforvsync\r
213         IF debugrasters\r
214                 LDA #&00 + PAL_magenta:STA &FE21\r
215         ENDIF\r
216 .waitingforvsync\r
217         LDA vsync:BEQ waitingforvsync\r
218         CMP #2:BCS exit         ; insist that it runs in a frame!\r
219         LDA #0:STA vsync\r
221         \\ Now delete all the old dots.\r
222         \\ We actually do this when the screen is still rasterising down..!\r
224         TAX\r
225 .eraseloop\r
226         LDY olddotaddrlo,X:STY write\r
227         LDY olddotaddrhi,X:STY write+1\r
228         LDY olddotaddry,X\r
229         STA (write),Y\r
230         DEY:BPL erasesamerow\r
231         DEC write+1:DEC write+1:LDY #7:.erasesamerow\r
232         STA (write),Y\r
233         INX:CPX #numdots\r
234         BNE eraseloop\r
236         IF debugrasters\r
237                 LDA #&00 + PAL_red:STA &FE21\r
238         ENDIF\r
240         \\ Add to rotation\r
242         CLC:LDA angle:ADC speed:STA angle\r
243         LDA angle+1:ADC speed+1:STA angle+1\r
245         \\ Check keypresses\r
247         LDA #66:STA &FE4F:LDA &FE4F:BPL notx\r
248         CLC:LDA speed:ADC #16:STA speed:BCC notx:INC speed+1:.notx\r
249         LDA #97:STA &FE4F:LDA &FE4F:BPL notz\r
250         SEC:LDA speed:SBC #16:STA speed:BCS notz:DEC speed+1:.notz\r
251         LDA #112:STA &FE4F:LDA &FE4F:BMI exit\r
253         JMP mainloop\r
255         \\ Exit - in the least graceful way possible :)\r
257 .exit\r
258         JMP (&FFFC)\r
262 \ ******************************************************************\r
263 \ *     IRQ handler\r
264 \ ******************************************************************\r
266 .irq\r
267         LDA &FE4D:AND #2:BNE irqvsync\r
268 .irqtimer\r
269         LDA #&40:STA &FE4D:INC vsync\r
270         IF debugrasters\r
271                 LDA #&00 + PAL_blue:STA &FE21\r
272         ENDIF\r
273         LDA &FC\r
274         RTI\r
275 .irqvsync\r
276         STA &FE4D\r
277         LDA #LO(timerlength):STA &FE44\r
278         LDA #HI(timerlength):STA &FE45\r
279         IF debugrasters\r
280                 LDA #&00 + PAL_black:STA &FE21\r
281         ENDIF\r
282         LDA &FC\r
283         RTI\r
287 \ ******************************************************************\r
288 \ *     Colour table used by the plot code\r
289 \ ******************************************************************\r
291 .colours\r
292         EQUB &00, &00           ; black pixels\r
293         EQUB &02, &01           ; blue pixels\r
294         EQUB &08, &04           ; red pixels\r
295         EQUB &0A, &05           ; magenta pixels\r
296         EQUB &20, &10           ; green pixels\r
297         EQUB &22, &11           ; cyan pixels\r
298         EQUB &28, &14           ; yellow pixels\r
299         EQUB &2A, &15           ; white pixels\r
303 \ ******************************************************************\r
304 \ *     Values of CRTC regs for MODE 2\r
305 \ ******************************************************************\r
307 .crtcregs\r
308         EQUB 127                        ; R0  horizontal total\r
309         EQUB 64                         ; R1  horizontal displayed - shrunk a little\r
310         EQUB 91                         ; R2  horizontal position\r
311         EQUB 40                         ; R3  sync width\r
312         EQUB 38                         ; R4  vertical total\r
313         EQUB 0                          ; R5  vertical total adjust\r
314         EQUB 32                         ; R6  vertical displayed\r
315         EQUB 34                         ; R7  vertical position\r
316         EQUB 0                          ; R8  interlace\r
317         EQUB 7                          ; R9  scanlines per row\r
318         EQUB 32                         ; R10 cursor start\r
319         EQUB 8                          ; R11 cursor end\r
320         EQUB HI(&4000/8)        ; R12 screen start address, high\r
321         EQUB LO(&4000/8)        ; R13 screen start address, low\r
324 \ ******************************************************************\r
325 \ *     Values of palette regs for MODE 2\r
326 \ ******************************************************************\r
328 PAL_black       = (0 EOR 7)\r
329 PAL_blue        = (4 EOR 7)\r
330 PAL_red         = (1 EOR 7)\r
331 PAL_magenta = (5 EOR 7)\r
332 PAL_green       = (2 EOR 7)\r
333 PAL_cyan        = (6 EOR 7)\r
334 PAL_yellow      = (3 EOR 7)\r
335 PAL_white       = (7 EOR 7)\r
337 .paldata\r
338         EQUB &00 + PAL_black\r
339         EQUB &10 + PAL_blue\r
340         EQUB &20 + PAL_red\r
341         EQUB &30 + PAL_magenta\r
342         EQUB &40 + PAL_green\r
343         EQUB &50 + PAL_cyan\r
344         EQUB &60 + PAL_yellow\r
345         EQUB &70 + PAL_white\r
349 \ ******************************************************************\r
350 \ *     sin table\r
351 \ ******************************************************************\r
353 ; contains ABS sine values\r
354 ; we don't store the sign as it confuses the multiplication.\r
355 ; we can tell the sign very easily from whether the index is >128\r
357 ALIGN &100      ; so we don't incur page-crossed penalties\r
358 .sintable\r
359 FOR n, 0, 255\r
360         EQUB ABS(SIN(n/128*PI)) * 255\r
361 NEXT\r
364 \ ******************************************************************\r
365 \ *     colour table\r
366 \ ******************************************************************\r
368 ALIGN &100\r
369 .coltable\r
370 FOR n, 0, 255\r
371         EQUB (SIN(n/128*PI) + 1) / 2.0001 * 7 + 1\r
372 NEXT\r
375 \ ******************************************************************\r
376 \ *     multiplication tables\r
377 \ ******************************************************************\r
379 ; This is a very quick way to do multiplies, based on the fact that:\r
381 ;                                                       (a+b)^2  =  a^2 + b^2 + 2ab    (I)\r
382 ;                                                       (a-b)^2  =  a^2 + b^2 - 2ab    (II)\r
384 ; (I) minus (II) yields:        (a+b)^2 - (a-b)^2 = 4ab\r
386 ;                 or, rewritten:        ab = f(a+b) - f(a-b),\r
387 ;                                                                                       where f(x) = x^2 / 4\r
389 ; We build a table of f(x) here with x=0..511, and then can perform\r
390 ; 8-bit * 8-bit by 4 table lookups and a 16-bit subtract.\r
392 ; In this case, we will discard the low byte of the result, so we\r
393 ; only need the high bytes, and can do just 2 table lookups and a\r
394 ; simple 8-bit subtract.\r
396 ALIGN &100\r
397 .multtab1\r
398 FOR n, 0, 255\r
399         EQUB HI(n*n DIV 4)\r
400 NEXT\r
401 .multtab2\r
402 FOR n, 256, 511\r
403         EQUB HI(n*n DIV 4)\r
404 NEXT\r
407 \ ******************************************************************\r
408 \ *     dot tables\r
409 \ ******************************************************************\r
411 ; contains the phase of this dot\r
413 ALIGN &100\r
414 .dotx\r
415 FOR n, 0, numdots-1\r
416         EQUB RND(256)\r
417 NEXT\r
420 ; contains the y position of the dot\r
421 ; the dots are sorted by y positions, highest on screen first - this means we can do\r
422 ; 'raster chasing'!\r
423 ; the y positions are also biased so there are fewer at the poles, and more at the equator!\r
425 ALIGN &100\r
426 .doty\r
427 FOR n, 0, numdots-1\r
428         x = (n - numdots/2 + 0.5) / (numdots/2)\r
429         y = (x - SIN(x*PI) * 0.1) * radius\r
430         EQUB 128 + y\r
431 NEXT\r
434 ; contains the radius of the ball at this y position\r
436 ALIGN &100\r
437 .dotr\r
438 FOR n, 0, numdots-1\r
439         x = (n - numdots/2 + 0.5) / (numdots/2)\r
440         y = (x - SIN(x*PI) * 0.1) * radius\r
441         r = SQR(radius*radius - y*y) / 2\r
442         EQUB r\r
443 NEXT\r
446 \ ******************************************************************\r
447 \ *     End address to be saved\r
448 \ ******************************************************************\r
449 .end\r
453 \ ******************************************************************\r
454 \ *     Space reserved for tables but not initialised with anything\r
455 \ * Therefore these are not saved in the executable\r
456 \ ******************************************************************\r
458 ; these store the screen address of the last dot\r
459 ; at the end of the frame, we go through these tables, storing zeroes to\r
460 ; all these addresses in order to delete the last frame\r
462 ALIGN &100\r
463 .olddotaddrlo   SKIP numdots\r
465 ALIGN &100\r
466 .olddotaddrhi   SKIP numdots\r
468 ALIGN &100\r
469 .olddotaddry    SKIP numdots\r
473 \ ******************************************************************\r
474 \ *     Save the code\r
475 \ ******************************************************************\r
477 SAVE "Code", start, end\r