Bump changelog
[debian-beebasm.git] / relocdemo.6502
blobdb379c303d4e480d4785eb03edf14e0292b5a401
1 \ ******************************************************************\r
2 \ *\r
3 \ *             Relocation demo\r
4 \ *\r
5 \ *             Simple demonstration of how to write self-relocating code\r
6 \ *             in BeebAsm, using the new 'reload address' feature of SAVE.\r
7 \ *\r
8 \ *             This uses the 'star globe' demo as a base.\r
9 \ *\r
10 \ ******************************************************************\r
13 \\ Define addresses\r
15 NATIVE_ADDR             = &300          ; address at which code will run\r
16 RELOAD_ADDR             = &1100         ; address at which code will load\r
18 OFFSET                  = RELOAD_ADDR - NATIVE_ADDR\r
21 \\ Define globals\r
23 numdots                 = 160\r
24 radius                  = 100\r
25 timerlength             = 64*8*26\r
26 debugrasters    = FALSE\r
29 \\ Define some zp locations\r
31 ORG 0\r
33 .xpos                   SKIP 1\r
34 .ypos                   SKIP 1\r
35 .colour                 SKIP 1\r
36 .write                  SKIP 2\r
37 .vsync                  SKIP 1\r
38 .angle                  SKIP 2\r
39 .speed                  SKIP 2\r
40 .counter                SKIP 1\r
41 .temp                   SKIP 1\r
42 .behindflag             SKIP 1\r
45 \\ Set start address\r
47 ORG NATIVE_ADDR\r
50 \ ******************************************************************\r
51 \ *     The start of the demo 'proper', after it has been relocated\r
52 \ ******************************************************************\r
54 .START\r
56         \\ Clear the screen\r
58         LDX #&40\r
59         LDA #0\r
60         TAY\r
61 .clearloop\r
62         STA &4000,Y\r
63         INY\r
64         BNE clearloop\r
65         INC clearloop+2\r
66         DEX\r
67         BNE clearloop\r
69         \\ Enable interrupts, ready to start the main loop\r
71         CLI\r
73         \\ First we wait for 'vsync' so we are synchronised\r
75 .initialwait\r
76         LDA vsync:BEQ initialwait:LDA #0:STA vsync\r
78         \\ Enable the screen\r
79         \r
80         LDA #6:STA &FE00:LDA #32:STA &FE01\r
82         \\ This is the main loop!\r
84 .mainloop\r
86         \\ Plot every dot on the screen\r
88         LDX #0\r
89 .plotdotloop\r
90         STX counter\r
92         ; setup y pos ready for plot routine\r
94         LDA doty,X:STA ypos\r
96         ; get sin index\r
98         CLC:LDA dotx,X:ADC angle+1:TAY\r
99         CLC:ADC #64:STA behindflag\r
101         ; get colour from sin index\r
103         LDA coltable,Y:STA colour\r
105         ; perform sin(x) * radius\r
106         ; discussion of the multiplication method below in the table setup\r
108         SEC:LDA sintable,Y:STA temp:SBC dotr,X\r
109         BCS noneg:EOR #&FF:ADC #1:.noneg\r
110         CPY #128:TAY:BCS negativesine\r
112         CLC:LDA dotr,X:ADC temp:TAX\r
113         BCS morethan256:SEC\r
114         LDA multtab1,X:SBC multtab1,Y:JMP donemult\r
115         .morethan256\r
116         LDA multtab2,X:SBC multtab1,Y:JMP donemult\r
118         .negativesine\r
119         CLC:LDA dotr,X:ADC temp:TAX\r
120         BCS morethan256b:SEC\r
121         LDA multtab1,Y:SBC multtab1,X:JMP donemult\r
122         .morethan256b\r
123         LDA multtab1,Y:SBC multtab2,X\r
124         .donemult\r
126         CLC:ADC #64:STA xpos\r
128         ; routine to plot a dot\r
129         ; also we remember the calculated screen address in the dot tables\r
131         LDA ypos:LSR A:LSR A:AND #&FE\r
132         TAX\r
133         LDA xpos:AND #&FE:ASL A:ASL A\r
134         STA write\r
135         LDY counter:STA olddotaddrlo,Y\r
136         TXA:ADC #&40:STA write+1:STA olddotaddrhi,Y\r
137         LDA ypos:AND #7:STA olddotaddry,Y:TAY\r
138         LDA xpos:LSR A:LDA colour:ROL A:TAX\r
139         LDA colours,X\r
140         ORA (write),Y\r
141         STA (write),Y\r
142         BIT behindflag:BMI behind\r
144         ; if the dot is in front, we double its size\r
146         DEY:BPL samescreenrow\r
147         DEC write+1:DEC write+1:LDY #7:.samescreenrow\r
148         LDA colours,X\r
149         ORA (write),Y\r
150         STA (write),Y\r
151         .behind\r
153         ; loop to the next dot\r
155         LDX counter\r
156         INX:CPX #numdots\r
157         BEQ waitforvsync\r
158         JMP plotdotloop\r
160         \\ Wait for VSync here\r
162 .waitforvsync\r
163         IF debugrasters\r
164                 LDA #&00 + PAL_magenta:STA &FE21\r
165         ENDIF\r
166 .waitingforvsync\r
167         LDA vsync:BEQ waitingforvsync\r
168         CMP #2:BCS exit         ; insist that it runs in a frame!\r
169         LDA #0:STA vsync\r
171         \\ Now delete all the old dots.\r
172         \\ We actually do this when the screen is still rasterising down..!\r
174         TAX\r
175 .eraseloop\r
176         LDY olddotaddrlo,X:STY write\r
177         LDY olddotaddrhi,X:STY write+1\r
178         LDY olddotaddry,X\r
179         STA (write),Y\r
180         DEY:BPL erasesamerow\r
181         DEC write+1:DEC write+1:LDY #7:.erasesamerow\r
182         STA (write),Y\r
183         INX:CPX #numdots\r
184         BNE eraseloop\r
186         IF debugrasters\r
187                 LDA #&00 + PAL_red:STA &FE21\r
188         ENDIF\r
190         \\ Add to rotation\r
192         CLC:LDA angle:ADC speed:STA angle\r
193         LDA angle+1:ADC speed+1:STA angle+1\r
195         \\ Check keypresses\r
197         LDA #66:STA &FE4F:LDA &FE4F:BPL notx\r
198         CLC:LDA speed:ADC #16:STA speed:BCC notx:INC speed+1:.notx\r
199         LDA #97:STA &FE4F:LDA &FE4F:BPL notz\r
200         SEC:LDA speed:SBC #16:STA speed:BCS notz:DEC speed+1:.notz\r
201         LDA #112:STA &FE4F:LDA &FE4F:BMI exit\r
203         JMP mainloop\r
205         \\ Exit - in the least graceful way possible :)\r
207 .exit\r
208         JMP (&FFFC)\r
212 \ ******************************************************************\r
213 \ *     IRQ handler\r
214 \ ******************************************************************\r
216 .irq\r
217         LDA &FE4D:AND #2:BNE irqvsync\r
218 .irqtimer\r
219         LDA #&40:STA &FE4D:INC vsync\r
220         IF debugrasters\r
221                 LDA #&00 + PAL_blue:STA &FE21\r
222         ENDIF\r
223         LDA &FC\r
224         RTI\r
225 .irqvsync\r
226         STA &FE4D\r
227         LDA #LO(timerlength):STA &FE44\r
228         LDA #HI(timerlength):STA &FE45\r
229         IF debugrasters\r
230                 LDA #&00 + PAL_black:STA &FE21\r
231         ENDIF\r
232         LDA &FC\r
233         RTI\r
237 \ ******************************************************************\r
238 \ *     Colour table used by the plot code\r
239 \ ******************************************************************\r
241 .colours\r
242         EQUB &00, &00           ; black pixels\r
243         EQUB &02, &01           ; blue pixels\r
244         EQUB &08, &04           ; red pixels\r
245         EQUB &0A, &05           ; magenta pixels\r
246         EQUB &20, &10           ; green pixels\r
247         EQUB &22, &11           ; cyan pixels\r
248         EQUB &28, &14           ; yellow pixels\r
249         EQUB &2A, &15           ; white pixels\r
252 \ ******************************************************************\r
253 \ *     sin table\r
254 \ ******************************************************************\r
256 ; contains ABS sine values\r
257 ; we don't store the sign as it confuses the multiplication.\r
258 ; we can tell the sign very easily from whether the index is >128\r
260 ALIGN &100      ; so we don't incur page-crossed penalties\r
261 .sintable\r
262 FOR n, 0, 255\r
263         EQUB ABS(SIN(n/128*PI)) * 255\r
264 NEXT\r
267 \ ******************************************************************\r
268 \ *     colour table\r
269 \ ******************************************************************\r
271 ALIGN &100\r
272 .coltable\r
273 FOR n, 0, 255\r
274         EQUB (SIN(n/128*PI) + 1) / 2.0001 * 7 + 1\r
275 NEXT\r
278 \ ******************************************************************\r
279 \ *     multiplication tables\r
280 \ ******************************************************************\r
282 ; This is a very quick way to do multiplies, based on the fact that:\r
284 ;                                                       (a+b)^2  =  a^2 + b^2 + 2ab    (I)\r
285 ;                                                       (a-b)^2  =  a^2 + b^2 - 2ab    (II)\r
287 ; (I) minus (II) yields:        (a+b)^2 - (a-b)^2 = 4ab\r
289 ;                 or, rewritten:        ab = f(a+b) - f(a-b),\r
290 ;                                                                                       where f(x) = x^2 / 4\r
292 ; We build a table of f(x) here with x=0..511, and then can perform\r
293 ; 8-bit * 8-bit by 4 table lookups and a 16-bit subtract.\r
295 ; In this case, we will discard the low byte of the result, so we\r
296 ; only need the high bytes, and can do just 2 table lookups and a\r
297 ; simple 8-bit subtract.\r
299 ALIGN &100\r
300 .multtab1\r
301 FOR n, 0, 255\r
302         EQUB HI(n*n DIV 4)\r
303 NEXT\r
304 .multtab2\r
305 FOR n, 256, 511\r
306         EQUB HI(n*n DIV 4)\r
307 NEXT\r
310 \ ******************************************************************\r
311 \ *     dot tables\r
312 \ ******************************************************************\r
314 ; contains the phase of this dot\r
316 ALIGN &100\r
317 .dotx\r
318 FOR n, 0, numdots-1\r
319         EQUB RND(256)\r
320 NEXT\r
323 ; contains the y position of the dot\r
324 ; the dots are sorted by y positions, highest on screen first - this means we can do\r
325 ; 'raster chasing'!\r
326 ; the y positions are also biased so there are fewer at the poles, and more at the equator!\r
328 ALIGN &100\r
329 .doty\r
330 FOR n, 0, numdots-1\r
331         x = (n - numdots/2 + 0.5) / (numdots/2)\r
332         y = (x - SIN(x*PI) * 0.1) * radius\r
333         EQUB 128 + y\r
334 NEXT\r
337 ; contains the radius of the ball at this y position\r
339 ALIGN &100\r
340 .dotr\r
341 FOR n, 0, numdots-1\r
342         x = (n - numdots/2 + 0.5) / (numdots/2)\r
343         y = (x - SIN(x*PI) * 0.1) * radius\r
344         r = SQR(radius*radius - y*y) / 2\r
345         EQUB r\r
346 NEXT\r
349 \ ******************************************************************\r
350 \ *     This is the end of the main native block of code\r
351 \ ******************************************************************\r
352 .END\r
355 \ ******************************************************************\r
356 \ *     The entry point of the demo\r
357 \ * This relocates the code to its 'real' address, and can also\r
358 \ * do one-time initialisation, i.e. code we can chuck away afterwards.\r
359 \ *\r
360 \ * Since this is the relocation code, it has to go at the very end of\r
361 \ * the executable.\r
363 \ * This code will be running at its assemble address + OFFSET,\r
364 \ * so we have to patch up any absolute address references accordingly.\r
365 \ ******************************************************************\r
367 ALIGN &100\r
368 .RELOC_START\r
370         \\ Set up hardware state and interrupts\r
372         SEI\r
373         LDX #&FF:TXS                            ; reset stack\r
374         STX &FE44:STX &FE45\r
375         LDA #&7F:STA &FE4E                      ; disable all interrupts\r
376         STA &FE43                                       ; set keyboard data direction\r
377         LDA #&C2:STA &FE4E                      ; enable VSync and timer interrupt\r
378         LDA #&0F:STA &FE42                      ; set addressable latch for writing\r
379         LDA #3:STA &FE40                        ; keyboard write enable\r
380         LDA #0:STA &FE4B                        ; timer 1 one shot mode\r
381         LDA #LO(irq):STA &204\r
382         LDA #HI(irq):STA &205           ; set interrupt handler\r
384         \\ Set up CRTC for MODE 2\r
386         LDX #13\r
387 .crtcloop\r
388         STX &FE00\r
389         LDA crtcregs + OFFSET,X         ; PATCHED ADDRESS\r
390         STA &FE01\r
391         DEX\r
392         BPL crtcloop\r
394         \\ Set up video ULA for MODE 2\r
396         LDA #&F4\r
397         STA &FE20\r
399         \\ Set up palette for MODE 2\r
401         LDX #15\r
402 .palloop\r
403         LDA paldata + OFFSET,X          ; PATCHED ADDRESS\r
404         STA &FE21\r
405         ORA #&80\r
406         STA &FE21\r
407         DEX\r
408         BPL palloop\r
410         \\ Initialise vars\r
412         LDA #0:STA angle:STA angle+1\r
413         STA vsync\r
414         STA speed\r
415         LDA #1:STA speed+1\r
416         \r
417         \\ Relocate\r
418         \r
419         LDX #HI(RELOC_START-START)\r
420         LDY #0\r
421         .relocloop\r
422         LDA RELOAD_ADDR,Y\r
423         STA NATIVE_ADDR,Y\r
424         INY\r
425         BNE relocloop\r
426         INC relocloop+OFFSET+2          ; PATCHED ADDRESS\r
427         INC relocloop+OFFSET+5          ; PATCHED ADDRESS\r
428         DEX\r
429         BNE relocloop\r
430         \r
431         JMP START\r
434 \ ******************************************************************\r
435 \ *     Values of CRTC regs for MODE 2\r
436 \ ******************************************************************\r
438 .crtcregs\r
439         EQUB 127                        ; R0  horizontal total\r
440         EQUB 64                         ; R1  horizontal displayed - shrunk a little\r
441         EQUB 91                         ; R2  horizontal position\r
442         EQUB 40                         ; R3  sync width\r
443         EQUB 38                         ; R4  vertical total\r
444         EQUB 0                          ; R5  vertical total adjust\r
445         EQUB 0                          ; R6  vertical displayed\r
446         EQUB 34                         ; R7  vertical position\r
447         EQUB 0                          ; R8  interlace\r
448         EQUB 7                          ; R9  scanlines per row\r
449         EQUB 32                         ; R10 cursor start\r
450         EQUB 8                          ; R11 cursor end\r
451         EQUB HI(&4000/8)        ; R12 screen start address, high\r
452         EQUB LO(&4000/8)        ; R13 screen start address, low\r
455 \ ******************************************************************\r
456 \ *     Values of palette regs for MODE 2\r
457 \ ******************************************************************\r
459 PAL_black       = (0 EOR 7)\r
460 PAL_blue        = (4 EOR 7)\r
461 PAL_red         = (1 EOR 7)\r
462 PAL_magenta = (5 EOR 7)\r
463 PAL_green       = (2 EOR 7)\r
464 PAL_cyan        = (6 EOR 7)\r
465 PAL_yellow      = (3 EOR 7)\r
466 PAL_white       = (7 EOR 7)\r
468 .paldata\r
469         EQUB &00 + PAL_black\r
470         EQUB &10 + PAL_blue\r
471         EQUB &20 + PAL_red\r
472         EQUB &30 + PAL_magenta\r
473         EQUB &40 + PAL_green\r
474         EQUB &50 + PAL_cyan\r
475         EQUB &60 + PAL_yellow\r
476         EQUB &70 + PAL_white\r
481 \ ******************************************************************\r
482 \ *     End address to be saved\r
483 \ ******************************************************************\r
484 .RELOC_END\r
487 \ ******************************************************************\r
488 \ *     Save the code, before the following data overlay clears it again\r
489 \ ******************************************************************\r
491 SAVE "Code", START, RELOC_END, RELOC_START+OFFSET, RELOAD_ADDR\r
496 \ ******************************************************************\r
497 \ * Start a new overlay:\r
498 \ *\r
499 \ * This is overlapped with the relocation code above, because it\r
500 \ * will already have been thrown away by the time these tables are\r
501 \ * used.\r
502 \ *\r
503 \ * These tables are filled at run-time, hence we just define their\r
504 \ * addresses, we don't need to save anything.\r
505 \ ******************************************************************\r
507 CLEAR END, RELOC_END\r
508 ORG END\r
510 ; these store the screen address of the last dot\r
511 ; at the end of the frame, we go through these tables, storing zeroes to\r
512 ; all these addresses in order to delete the last frame\r
514 ALIGN &100\r
515 .olddotaddrlo   SKIP numdots\r
517 ALIGN &100\r
518 .olddotaddrhi   SKIP numdots\r
520 ALIGN &100\r
521 .olddotaddry    SKIP numdots\r