Bump changelog
[debian-beebasm.git] / demo.6502
blob35da60a4a374c014aeeae81dd3a252d66f74404a
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 \\ Seed the random number generator so the build is repeatable\r
55 RANDOMIZE 42\r
58 \\ Set start address\r
60 ORG &1100\r
62 \ ******************************************************************\r
63 \ *     The entry point of the demo\r
64 \ ******************************************************************\r
66 .start\r
68         \\ Set up hardware state and interrupts\r
70         SEI\r
71         LDX #&FF:TXS                            ; reset stack\r
72         STX &FE44:STX &FE45\r
73         LDA #&7F:STA &FE4E                      ; disable all interrupts\r
74         STA &FE43                                       ; set keyboard data direction\r
75         LDA #&C2:STA &FE4E                      ; enable VSync and timer interrupt\r
76         LDA #&0F:STA &FE42                      ; set addressable latch for writing\r
77         LDA #3:STA &FE40                        ; keyboard write enable\r
78         LDA #0:STA &FE4B                        ; timer 1 one shot mode\r
79         LDA #LO(irq):STA &204\r
80         LDA #HI(irq):STA &205           ; set interrupt handler\r
82         \\ Clear the screen\r
84         LDX #&40\r
85         LDA #0\r
86         TAY\r
87 .clearloop\r
88         STA &4000,Y\r
89         INY\r
90         BNE clearloop\r
91         INC clearloop+2\r
92         DEX\r
93         BNE clearloop\r
95         \\ Set up CRTC for MODE 2\r
97         LDX #13\r
98 .crtcloop\r
99         STX &FE00\r
100         LDA crtcregs,X\r
101         STA &FE01\r
102         DEX\r
103         BPL crtcloop\r
105         \\ Set up video ULA for MODE 2\r
107         LDA #&F4\r
108         STA &FE20\r
110         \\ Set up palette for MODE 2\r
112         LDX #15\r
113 .palloop\r
114         LDA paldata,X\r
115         STA &FE21\r
116         ORA #&80\r
117         STA &FE21\r
118         DEX\r
119         BPL palloop\r
121         \\ Initialise vars\r
123         LDA #0:STA angle:STA angle+1\r
124         STA vsync\r
125         STA speed\r
126         LDA #1:STA speed+1\r
128         \\ Enable interrupts, ready to start the main loop\r
130         CLI\r
132         \\ First we wait for 'vsync' so we are synchronised\r
134 .initialwait\r
135         LDA vsync:BEQ initialwait:LDA #0:STA vsync\r
137         \\ This is the main loop!\r
139 .mainloop\r
141         \\ Plot every dot on the screen\r
143         LDX #0\r
144 .plotdotloop\r
145         STX counter\r
147         ; setup y pos ready for plot routine\r
149         LDA doty,X:STA ypos\r
151         ; get sin index\r
153         CLC:LDA dotx,X:ADC angle+1:TAY\r
154         CLC:ADC #64:STA behindflag\r
156         ; get colour from sin index\r
158         LDA coltable,Y:STA colour\r
160         ; perform sin(x) * radius\r
161         ; discussion of the multiplication method below in the table setup\r
163         SEC:LDA sintable,Y:STA temp:SBC dotr,X\r
164         BCS noneg:EOR #&FF:ADC #1:.noneg\r
165         CPY #128:TAY:BCS negativesine\r
167         CLC:LDA dotr,X:ADC temp:TAX\r
168         BCS morethan256:SEC\r
169         LDA multtab1,X:SBC multtab1,Y:JMP donemult\r
170         .morethan256\r
171         LDA multtab2,X:SBC multtab1,Y:JMP donemult\r
173         .negativesine\r
174         CLC:LDA dotr,X:ADC temp:TAX\r
175         BCS morethan256b:SEC\r
176         LDA multtab1,Y:SBC multtab1,X:JMP donemult\r
177         .morethan256b\r
178         LDA multtab1,Y:SBC multtab2,X\r
179         .donemult\r
181         CLC:ADC #64:STA xpos\r
183         ; routine to plot a dot\r
184         ; also we remember the calculated screen address in the dot tables\r
186         LDA ypos:LSR A:LSR A:AND #&FE\r
187         TAX\r
188         LDA xpos:AND #&FE:ASL A:ASL A\r
189         STA write\r
190         LDY counter:STA olddotaddrlo,Y\r
191         TXA:ADC #&40:STA write+1:STA olddotaddrhi,Y\r
192         LDA ypos:AND #7:STA olddotaddry,Y:TAY\r
193         LDA xpos:LSR A:LDA colour:ROL A:TAX\r
194         LDA colours,X\r
195         ORA (write),Y\r
196         STA (write),Y\r
197         BIT behindflag:BMI behind\r
199         ; if the dot is in front, we double its size\r
201         DEY:BPL samescreenrow\r
202         DEC write+1:DEC write+1:LDY #7:.samescreenrow\r
203         LDA colours,X\r
204         ORA (write),Y\r
205         STA (write),Y\r
206         .behind\r
208         ; loop to the next dot\r
210         LDX counter\r
211         INX:CPX #numdots\r
212         BEQ waitforvsync\r
213         JMP plotdotloop\r
215         \\ Wait for VSync here\r
217 .waitforvsync\r
218         IF debugrasters\r
219                 LDA #&00 + PAL_magenta:STA &FE21\r
220         ENDIF\r
221 .waitingforvsync\r
222         LDA vsync:BEQ waitingforvsync\r
223         CMP #2:BCS exit         ; insist that it runs in a frame!\r
224         LDA #0:STA vsync\r
226         \\ Now delete all the old dots.\r
227         \\ We actually do this when the screen is still rasterising down..!\r
229         TAX\r
230 .eraseloop\r
231         LDY olddotaddrlo,X:STY write\r
232         LDY olddotaddrhi,X:STY write+1\r
233         LDY olddotaddry,X\r
234         STA (write),Y\r
235         DEY:BPL erasesamerow\r
236         DEC write+1:DEC write+1:LDY #7:.erasesamerow\r
237         STA (write),Y\r
238         INX:CPX #numdots\r
239         BNE eraseloop\r
241         IF debugrasters\r
242                 LDA #&00 + PAL_red:STA &FE21\r
243         ENDIF\r
245         \\ Add to rotation\r
247         CLC:LDA angle:ADC speed:STA angle\r
248         LDA angle+1:ADC speed+1:STA angle+1\r
250         \\ Check keypresses\r
252         LDA #66:STA &FE4F:LDA &FE4F:BPL notx\r
253         CLC:LDA speed:ADC #16:STA speed:BCC notx:INC speed+1:.notx\r
254         LDA #97:STA &FE4F:LDA &FE4F:BPL notz\r
255         SEC:LDA speed:SBC #16:STA speed:BCS notz:DEC speed+1:.notz\r
256         LDA #112:STA &FE4F:LDA &FE4F:BMI exit\r
258         JMP mainloop\r
260         \\ Exit - in the least graceful way possible :)\r
262 .exit\r
263         JMP (&FFFC)\r
267 \ ******************************************************************\r
268 \ *     IRQ handler\r
269 \ ******************************************************************\r
271 .irq\r
272         LDA &FE4D:AND #2:BNE irqvsync\r
273 .irqtimer\r
274         LDA #&40:STA &FE4D:INC vsync\r
275         IF debugrasters\r
276                 LDA #&00 + PAL_blue:STA &FE21\r
277         ENDIF\r
278         LDA &FC\r
279         RTI\r
280 .irqvsync\r
281         STA &FE4D\r
282         LDA #LO(timerlength):STA &FE44\r
283         LDA #HI(timerlength):STA &FE45\r
284         IF debugrasters\r
285                 LDA #&00 + PAL_black:STA &FE21\r
286         ENDIF\r
287         LDA &FC\r
288         RTI\r
292 \ ******************************************************************\r
293 \ *     Colour table used by the plot code\r
294 \ ******************************************************************\r
296 .colours\r
297         EQUB &00, &00           ; black pixels\r
298         EQUB &02, &01           ; blue pixels\r
299         EQUB &08, &04           ; red pixels\r
300         EQUB &0A, &05           ; magenta pixels\r
301         EQUB &20, &10           ; green pixels\r
302         EQUB &22, &11           ; cyan pixels\r
303         EQUB &28, &14           ; yellow pixels\r
304         EQUB &2A, &15           ; white pixels\r
308 \ ******************************************************************\r
309 \ *     Values of CRTC regs for MODE 2\r
310 \ ******************************************************************\r
312 .crtcregs\r
313         EQUB 127                        ; R0  horizontal total\r
314         EQUB 64                         ; R1  horizontal displayed - shrunk a little\r
315         EQUB 91                         ; R2  horizontal position\r
316         EQUB 40                         ; R3  sync width\r
317         EQUB 38                         ; R4  vertical total\r
318         EQUB 0                          ; R5  vertical total adjust\r
319         EQUB 32                         ; R6  vertical displayed\r
320         EQUB 34                         ; R7  vertical position\r
321         EQUB 0                          ; R8  interlace\r
322         EQUB 7                          ; R9  scanlines per row\r
323         EQUB 32                         ; R10 cursor start\r
324         EQUB 8                          ; R11 cursor end\r
325         EQUB HI(&4000/8)        ; R12 screen start address, high\r
326         EQUB LO(&4000/8)        ; R13 screen start address, low\r
329 \ ******************************************************************\r
330 \ *     Values of palette regs for MODE 2\r
331 \ ******************************************************************\r
333 PAL_black       = (0 EOR 7)\r
334 PAL_blue        = (4 EOR 7)\r
335 PAL_red         = (1 EOR 7)\r
336 PAL_magenta = (5 EOR 7)\r
337 PAL_green       = (2 EOR 7)\r
338 PAL_cyan        = (6 EOR 7)\r
339 PAL_yellow      = (3 EOR 7)\r
340 PAL_white       = (7 EOR 7)\r
342 .paldata\r
343         EQUB &00 + PAL_black\r
344         EQUB &10 + PAL_blue\r
345         EQUB &20 + PAL_red\r
346         EQUB &30 + PAL_magenta\r
347         EQUB &40 + PAL_green\r
348         EQUB &50 + PAL_cyan\r
349         EQUB &60 + PAL_yellow\r
350         EQUB &70 + PAL_white\r
354 \ ******************************************************************\r
355 \ *     sin table\r
356 \ ******************************************************************\r
358 ; contains ABS sine values\r
359 ; we don't store the sign as it confuses the multiplication.\r
360 ; we can tell the sign very easily from whether the index is >128\r
362 ALIGN &100      ; so we don't incur page-crossed penalties\r
363 .sintable\r
364 FOR n, 0, 255\r
365         EQUB ABS(SIN(n/128*PI)) * 255\r
366 NEXT\r
369 \ ******************************************************************\r
370 \ *     colour table\r
371 \ ******************************************************************\r
373 ALIGN &100\r
374 .coltable\r
375 FOR n, 0, 255\r
376         EQUB (SIN(n/128*PI) + 1) / 2.0001 * 7 + 1\r
377 NEXT\r
380 \ ******************************************************************\r
381 \ *     multiplication tables\r
382 \ ******************************************************************\r
384 ; This is a very quick way to do multiplies, based on the fact that:\r
386 ;                                                       (a+b)^2  =  a^2 + b^2 + 2ab    (I)\r
387 ;                                                       (a-b)^2  =  a^2 + b^2 - 2ab    (II)\r
389 ; (I) minus (II) yields:        (a+b)^2 - (a-b)^2 = 4ab\r
391 ;                 or, rewritten:        ab = f(a+b) - f(a-b),\r
392 ;                                                                                       where f(x) = x^2 / 4\r
394 ; We build a table of f(x) here with x=0..511, and then can perform\r
395 ; 8-bit * 8-bit by 4 table lookups and a 16-bit subtract.\r
397 ; In this case, we will discard the low byte of the result, so we\r
398 ; only need the high bytes, and can do just 2 table lookups and a\r
399 ; simple 8-bit subtract.\r
401 ALIGN &100\r
402 .multtab1\r
403 FOR n, 0, 255\r
404         EQUB HI(n*n DIV 4)\r
405 NEXT\r
406 .multtab2\r
407 FOR n, 256, 511\r
408         EQUB HI(n*n DIV 4)\r
409 NEXT\r
412 \ ******************************************************************\r
413 \ *     dot tables\r
414 \ ******************************************************************\r
416 ; contains the phase of this dot\r
418 ALIGN &100\r
419 .dotx\r
420 FOR n, 0, numdots-1\r
421         EQUB RND(256)\r
422 NEXT\r
425 ; contains the y position of the dot\r
426 ; the dots are sorted by y positions, highest on screen first - this means we can do\r
427 ; 'raster chasing'!\r
428 ; the y positions are also biased so there are fewer at the poles, and more at the equator!\r
430 ALIGN &100\r
431 .doty\r
432 FOR n, 0, numdots-1\r
433         x = (n - numdots/2 + 0.5) / (numdots/2)\r
434         y = (x - SIN(x*PI) * 0.1) * radius\r
435         EQUB 128 + y\r
436 NEXT\r
439 ; contains the radius of the ball at this y position\r
441 ALIGN &100\r
442 .dotr\r
443 FOR n, 0, numdots-1\r
444         x = (n - numdots/2 + 0.5) / (numdots/2)\r
445         y = (x - SIN(x*PI) * 0.1) * radius\r
446         r = SQR(radius*radius - y*y) / 2\r
447         EQUB r\r
448 NEXT\r
451 \ ******************************************************************\r
452 \ *     End address to be saved\r
453 \ ******************************************************************\r
454 .end\r
458 \ ******************************************************************\r
459 \ *     Space reserved for tables but not initialised with anything\r
460 \ * Therefore these are not saved in the executable\r
461 \ ******************************************************************\r
463 ; these store the screen address of the last dot\r
464 ; at the end of the frame, we go through these tables, storing zeroes to\r
465 ; all these addresses in order to delete the last frame\r
467 ALIGN &100\r
468 .olddotaddrlo   SKIP numdots\r
470 ALIGN &100\r
471 .olddotaddrhi   SKIP numdots\r
473 ALIGN &100\r
474 .olddotaddry    SKIP numdots\r
478 \ ******************************************************************\r
479 \ *     Save the code\r
480 \ ******************************************************************\r
482 SAVE "Code", start, end\r