1 \ ******************************************************************
\r
5 \ * Spinning star globe
\r
7 \ * Change the speed of rotation with Z and X keys
\r
8 \ * Press Esc to quit
\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
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
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
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
27 \ ******************************************************************
\r
33 timerlength = 64*8*26
\r
34 debugrasters = FALSE
\r
37 \\ Define some zp locations
\r
53 \\ Set start address
\r
57 \ ******************************************************************
\r
58 \ * The entry point of the demo
\r
59 \ ******************************************************************
\r
63 \\ Set up hardware state and interrupts
\r
66 LDX #&FF:TXS ; reset stack
\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
90 \\ Set up CRTC for MODE 2
\r
100 \\ Set up video ULA for MODE 2
\r
105 \\ Set up palette for MODE 2
\r
118 LDA #0:STA angle:STA angle+1
\r
123 \\ Enable interrupts, ready to start the main loop
\r
127 \\ First we wait for 'vsync' so we are synchronised
\r
130 LDA vsync:BEQ initialwait:LDA #0:STA vsync
\r
132 \\ This is the main loop!
\r
136 \\ Plot every dot on the screen
\r
142 ; setup y pos ready for plot routine
\r
144 LDA doty,X:STA ypos
\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
166 LDA multtab2,X:SBC multtab1,Y:JMP donemult
\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
173 LDA multtab1,Y:SBC multtab2,X
\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
183 LDA xpos:AND #&FE:ASL A:ASL A
\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
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
203 ; loop to the next dot
\r
210 \\ Wait for VSync here
\r
214 LDA #&00 + PAL_magenta:STA &FE21
\r
217 LDA vsync:BEQ waitingforvsync
\r
218 CMP #2:BCS exit ; insist that it runs in a frame!
\r
221 \\ Now delete all the old dots.
\r
222 \\ We actually do this when the screen is still rasterising down..!
\r
226 LDY olddotaddrlo,X:STY write
\r
227 LDY olddotaddrhi,X:STY write+1
\r
230 DEY:BPL erasesamerow
\r
231 DEC write+1:DEC write+1:LDY #7:.erasesamerow
\r
237 LDA #&00 + PAL_red:STA &FE21
\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
255 \\ Exit - in the least graceful way possible :)
\r
262 \ ******************************************************************
\r
264 \ ******************************************************************
\r
267 LDA &FE4D:AND #2:BNE irqvsync
\r
269 LDA #&40:STA &FE4D:INC vsync
\r
271 LDA #&00 + PAL_blue:STA &FE21
\r
277 LDA #LO(timerlength):STA &FE44
\r
278 LDA #HI(timerlength):STA &FE45
\r
280 LDA #&00 + PAL_black:STA &FE21
\r
287 \ ******************************************************************
\r
288 \ * Colour table used by the plot code
\r
289 \ ******************************************************************
\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
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
338 EQUB &00 + PAL_black
\r
339 EQUB &10 + PAL_blue
\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
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
360 EQUB ABS(SIN(n/128*PI)) * 255
\r
364 \ ******************************************************************
\r
366 \ ******************************************************************
\r
371 EQUB (SIN(n/128*PI) + 1) / 2.0001 * 7 + 1
\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
407 \ ******************************************************************
\r
409 \ ******************************************************************
\r
411 ; contains the phase of this dot
\r
415 FOR n, 0, numdots-1
\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
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
434 ; contains the radius of the ball at this y position
\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
446 \ ******************************************************************
\r
447 \ * End address to be saved
\r
448 \ ******************************************************************
\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
463 .olddotaddrlo SKIP numdots
\r
466 .olddotaddrhi SKIP numdots
\r
469 .olddotaddry SKIP numdots
\r
473 \ ******************************************************************
\r
475 \ ******************************************************************
\r
477 SAVE "Code", start, end
\r