beebasm

changeset 34:5aca9d5e4d02

Beginning of macro implementation Some small bugfixes and improvements to line parser Added some more error messages Renamed the demo source file extensions from .asm (too generic) to .6502 (better)
author RichTW <richtw1@gmail.com>
date Sun Mar 06 18:19:27 2011 +0100
parents 58954bd1a2d9
children 5fedd5b15bd7
files demo.6502 demo.asm relocdemo.6502 relocdemo.asm src/Makefile src/Makefile.inc src/VS2010/BeebAsm.vcxproj src/VS2010/BeebAsm.vcxproj.filters src/asmexception.h src/commands.cpp src/lineparser.cpp src/lineparser.h src/macro.cpp src/macro.h src/main.cpp src/sourcefile.cpp src/sourcefile.h src/tokens.h
diffstat 18 files changed, 1480 insertions(+), 1093 deletions(-) [+]
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/demo.6502	Sun Mar 06 18:19:27 2011 +0100
     1.3 @@ -0,0 +1,477 @@
     1.4 +\ ******************************************************************
     1.5 +\ *
     1.6 +\ *		BeebAsm demo
     1.7 +\ *
     1.8 +\ *		Spinning star globe
     1.9 +\ *
    1.10 +\ *		Change the speed of rotation with Z and X keys
    1.11 +\ *		Press Esc to quit
    1.12 +\ *
    1.13 +\ *		Try assembling the code with debugrasters = TRUE (line 20)
    1.14 +\ *		It shows how the frame is split up into various stages of
    1.15 +\ *		processing.
    1.16 +\ *
    1.17 +\ *		The red part is where we start to plot the dots - starting as
    1.18 +\ *		soon before the first screenline of the next frame is rastered
    1.19 +\ *		as possible.
    1.20 +\ *
    1.21 +\ *		The magenta part is a small loop where we wait for 'vsync'.
    1.22 +\ *		It's not actually vsync, but in fact a fixed time from actual
    1.23 +\ *		vsync (timed by timer 1) from which point we can start to erase
    1.24 +\ *		points from the top of the screen down.
    1.25 +\ *
    1.26 +\ *		The blue part is the time when we are erasing dots.  Because
    1.27 +\ *		the dots are sorted from top to bottom of screen, we can
    1.28 +\ *		overlap these updates with the actual screen rasterisation.
    1.29 +\ *
    1.30 +\ ******************************************************************
    1.31 +
    1.32 +\\ Define globals
    1.33 +
    1.34 +numdots			= 160
    1.35 +radius			= 100
    1.36 +timerlength		= 64*8*26
    1.37 +debugrasters	= FALSE
    1.38 +
    1.39 +
    1.40 +\\ Define some zp locations
    1.41 +
    1.42 +ORG 0
    1.43 +
    1.44 +.xpos			SKIP 1
    1.45 +.ypos			SKIP 1
    1.46 +.colour			SKIP 1
    1.47 +.write			SKIP 2
    1.48 +.vsync			SKIP 1
    1.49 +.angle			SKIP 2
    1.50 +.speed			SKIP 2
    1.51 +.counter		SKIP 1
    1.52 +.temp			SKIP 1
    1.53 +.behindflag		SKIP 1
    1.54 +
    1.55 +
    1.56 +\\ Set start address
    1.57 +
    1.58 +ORG &1100
    1.59 +
    1.60 +\ ******************************************************************
    1.61 +\ *	The entry point of the demo
    1.62 +\ ******************************************************************
    1.63 +
    1.64 +.start
    1.65 +
    1.66 +	\\ Set up hardware state and interrupts
    1.67 +
    1.68 +	SEI
    1.69 +	LDX #&FF:TXS				; reset stack
    1.70 +	STX &FE44:STX &FE45
    1.71 +	LDA #&7F:STA &FE4E			; disable all interrupts
    1.72 +	STA &FE43					; set keyboard data direction
    1.73 +	LDA #&C2:STA &FE4E			; enable VSync and timer interrupt
    1.74 +	LDA #&0F:STA &FE42			; set addressable latch for writing
    1.75 +	LDA #3:STA &FE40			; keyboard write enable
    1.76 +	LDA #0:STA &FE4B			; timer 1 one shot mode
    1.77 +	LDA #LO(irq):STA &204
    1.78 +	LDA #HI(irq):STA &205		; set interrupt handler
    1.79 +
    1.80 +	\\ Clear the screen
    1.81 +
    1.82 +	LDX #&40
    1.83 +	LDA #0
    1.84 +	TAY
    1.85 +.clearloop
    1.86 +	STA &4000,Y
    1.87 +	INY
    1.88 +	BNE clearloop
    1.89 +	INC clearloop+2
    1.90 +	DEX
    1.91 +	BNE clearloop
    1.92 +
    1.93 +	\\ Set up CRTC for MODE 2
    1.94 +
    1.95 +	LDX #13
    1.96 +.crtcloop
    1.97 +	STX &FE00
    1.98 +	LDA crtcregs,X
    1.99 +	STA &FE01
   1.100 +	DEX
   1.101 +	BPL crtcloop
   1.102 +
   1.103 +	\\ Set up video ULA for MODE 2
   1.104 +
   1.105 +	LDA #&F4
   1.106 +	STA &FE20
   1.107 +
   1.108 +	\\ Set up palette for MODE 2
   1.109 +
   1.110 +	LDX #15
   1.111 +.palloop
   1.112 +	LDA paldata,X
   1.113 +	STA &FE21
   1.114 +	ORA #&80
   1.115 +	STA &FE21
   1.116 +	DEX
   1.117 +	BPL palloop
   1.118 +
   1.119 +	\\ Initialise vars
   1.120 +
   1.121 +	LDA #0:STA angle:STA angle+1
   1.122 +	STA vsync
   1.123 +	STA speed
   1.124 +	LDA #1:STA speed+1
   1.125 +
   1.126 +	\\ Enable interrupts, ready to start the main loop
   1.127 +
   1.128 +	CLI
   1.129 +
   1.130 +	\\ First we wait for 'vsync' so we are synchronised
   1.131 +
   1.132 +.initialwait
   1.133 +	LDA vsync:BEQ initialwait:LDA #0:STA vsync
   1.134 +
   1.135 +	\\ This is the main loop!
   1.136 +
   1.137 +.mainloop
   1.138 +
   1.139 +	\\ Plot every dot on the screen
   1.140 +
   1.141 +	LDX #0
   1.142 +.plotdotloop
   1.143 +	STX counter
   1.144 +
   1.145 +	; setup y pos ready for plot routine
   1.146 +
   1.147 +	LDA doty,X:STA ypos
   1.148 +
   1.149 +	; get sin index
   1.150 +
   1.151 +	CLC:LDA dotx,X:ADC angle+1:TAY
   1.152 +	CLC:ADC #64:STA behindflag
   1.153 +
   1.154 +	; get colour from sin index
   1.155 +
   1.156 +	LDA coltable,Y:STA colour
   1.157 +
   1.158 +	; perform sin(x) * radius
   1.159 +	; discussion of the multiplication method below in the table setup
   1.160 +
   1.161 +	SEC:LDA sintable,Y:STA temp:SBC dotr,X
   1.162 +	BCS noneg:EOR #&FF:ADC #1:.noneg
   1.163 +	CPY #128:TAY:BCS negativesine
   1.164 +
   1.165 +	CLC:LDA dotr,X:ADC temp:TAX
   1.166 +	BCS morethan256:SEC
   1.167 +	LDA multtab1,X:SBC multtab1,Y:JMP donemult
   1.168 +	.morethan256
   1.169 +	LDA multtab2,X:SBC multtab1,Y:JMP donemult
   1.170 +
   1.171 +	.negativesine
   1.172 +	CLC:LDA dotr,X:ADC temp:TAX
   1.173 +	BCS morethan256b:SEC
   1.174 +	LDA multtab1,Y:SBC multtab1,X:JMP donemult
   1.175 +	.morethan256b
   1.176 +	LDA multtab1,Y:SBC multtab2,X
   1.177 +	.donemult
   1.178 +
   1.179 +	CLC:ADC #64:STA xpos
   1.180 +
   1.181 +	; routine to plot a dot
   1.182 +	; also we remember the calculated screen address in the dot tables
   1.183 +
   1.184 +	LDA ypos:LSR A:LSR A:AND #&FE
   1.185 +	TAX
   1.186 +	LDA xpos:AND #&FE:ASL A:ASL A
   1.187 +	STA write
   1.188 +	LDY counter:STA olddotaddrlo,Y
   1.189 +	TXA:ADC #&40:STA write+1:STA olddotaddrhi,Y
   1.190 +	LDA ypos:AND #7:STA olddotaddry,Y:TAY
   1.191 +	LDA xpos:LSR A:LDA colour:ROL A:TAX
   1.192 +	LDA colours,X
   1.193 +	ORA (write),Y
   1.194 +	STA (write),Y
   1.195 +	BIT behindflag:BMI behind
   1.196 +
   1.197 +	; if the dot is in front, we double its size
   1.198 +
   1.199 +	DEY:BPL samescreenrow
   1.200 +	DEC write+1:DEC write+1:LDY #7:.samescreenrow
   1.201 +	LDA colours,X
   1.202 +	ORA (write),Y
   1.203 +	STA (write),Y
   1.204 +	.behind
   1.205 +
   1.206 +	; loop to the next dot
   1.207 +
   1.208 +	LDX counter
   1.209 +	INX:CPX #numdots
   1.210 +	BEQ waitforvsync
   1.211 +	JMP plotdotloop
   1.212 +
   1.213 +	\\ Wait for VSync here
   1.214 +
   1.215 +.waitforvsync
   1.216 +	IF debugrasters
   1.217 +		LDA #&00 + PAL_magenta:STA &FE21
   1.218 +	ENDIF
   1.219 +.waitingforvsync
   1.220 +	LDA vsync:BEQ waitingforvsync
   1.221 +	CMP #2:BCS exit		; insist that it runs in a frame!
   1.222 +	LDA #0:STA vsync
   1.223 +
   1.224 +	\\ Now delete all the old dots.
   1.225 +	\\ We actually do this when the screen is still rasterising down..!
   1.226 +
   1.227 +	TAX
   1.228 +.eraseloop
   1.229 +	LDY olddotaddrlo,X:STY write
   1.230 +	LDY olddotaddrhi,X:STY write+1
   1.231 +	LDY olddotaddry,X
   1.232 +	STA (write),Y
   1.233 +	DEY:BPL erasesamerow
   1.234 +	DEC write+1:DEC write+1:LDY #7:.erasesamerow
   1.235 +	STA (write),Y
   1.236 +	INX:CPX #numdots
   1.237 +	BNE eraseloop
   1.238 +
   1.239 +	IF debugrasters
   1.240 +		LDA #&00 + PAL_red:STA &FE21
   1.241 +	ENDIF
   1.242 +
   1.243 +	\\ Add to rotation
   1.244 +
   1.245 +	CLC:LDA angle:ADC speed:STA angle
   1.246 +	LDA angle+1:ADC speed+1:STA angle+1
   1.247 +
   1.248 +	\\ Check keypresses
   1.249 +
   1.250 +	LDA #66:STA &FE4F:LDA &FE4F:BPL notx
   1.251 +	CLC:LDA speed:ADC #16:STA speed:BCC notx:INC speed+1:.notx
   1.252 +	LDA #97:STA &FE4F:LDA &FE4F:BPL notz
   1.253 +	SEC:LDA speed:SBC #16:STA speed:BCS notz:DEC speed+1:.notz
   1.254 +	LDA #112:STA &FE4F:LDA &FE4F:BMI exit
   1.255 +
   1.256 +	JMP mainloop
   1.257 +
   1.258 +	\\ Exit - in the least graceful way possible :)
   1.259 +
   1.260 +.exit
   1.261 +	JMP (&FFFC)
   1.262 +
   1.263 +
   1.264 +
   1.265 +\ ******************************************************************
   1.266 +\ *	IRQ handler
   1.267 +\ ******************************************************************
   1.268 +
   1.269 +.irq
   1.270 +	LDA &FE4D:AND #2:BNE irqvsync
   1.271 +.irqtimer
   1.272 +	LDA #&40:STA &FE4D:INC vsync
   1.273 +	IF debugrasters
   1.274 +		LDA #&00 + PAL_blue:STA &FE21
   1.275 +	ENDIF
   1.276 +	LDA &FC
   1.277 +	RTI
   1.278 +.irqvsync
   1.279 +	STA &FE4D
   1.280 +	LDA #LO(timerlength):STA &FE44
   1.281 +	LDA #HI(timerlength):STA &FE45
   1.282 +	IF debugrasters
   1.283 +		LDA #&00 + PAL_black:STA &FE21
   1.284 +	ENDIF
   1.285 +	LDA &FC
   1.286 +	RTI
   1.287 +
   1.288 +
   1.289 +
   1.290 +\ ******************************************************************
   1.291 +\ *	Colour table used by the plot code
   1.292 +\ ******************************************************************
   1.293 +
   1.294 +.colours
   1.295 +	EQUB &00, &00		; black pixels
   1.296 +	EQUB &02, &01		; blue pixels
   1.297 +	EQUB &08, &04		; red pixels
   1.298 +	EQUB &0A, &05		; magenta pixels
   1.299 +	EQUB &20, &10		; green pixels
   1.300 +	EQUB &22, &11		; cyan pixels
   1.301 +	EQUB &28, &14		; yellow pixels
   1.302 +	EQUB &2A, &15		; white pixels
   1.303 +
   1.304 +
   1.305 +
   1.306 +\ ******************************************************************
   1.307 +\ *	Values of CRTC regs for MODE 2
   1.308 +\ ******************************************************************
   1.309 +
   1.310 +.crtcregs
   1.311 +	EQUB 127			; R0  horizontal total
   1.312 +	EQUB 64				; R1  horizontal displayed - shrunk a little
   1.313 +	EQUB 91				; R2  horizontal position
   1.314 +	EQUB 40				; R3  sync width
   1.315 +	EQUB 38				; R4  vertical total
   1.316 +	EQUB 0				; R5  vertical total adjust
   1.317 +	EQUB 32				; R6  vertical displayed
   1.318 +	EQUB 34				; R7  vertical position
   1.319 +	EQUB 0				; R8  interlace
   1.320 +	EQUB 7				; R9  scanlines per row
   1.321 +	EQUB 32				; R10 cursor start
   1.322 +	EQUB 8				; R11 cursor end
   1.323 +	EQUB HI(&4000/8)	; R12 screen start address, high
   1.324 +	EQUB LO(&4000/8)	; R13 screen start address, low
   1.325 +
   1.326 +
   1.327 +\ ******************************************************************
   1.328 +\ *	Values of palette regs for MODE 2
   1.329 +\ ******************************************************************
   1.330 +
   1.331 +PAL_black	= (0 EOR 7)
   1.332 +PAL_blue	= (4 EOR 7)
   1.333 +PAL_red		= (1 EOR 7)
   1.334 +PAL_magenta = (5 EOR 7)
   1.335 +PAL_green	= (2 EOR 7)
   1.336 +PAL_cyan	= (6 EOR 7)
   1.337 +PAL_yellow	= (3 EOR 7)
   1.338 +PAL_white	= (7 EOR 7)
   1.339 +
   1.340 +.paldata
   1.341 +	EQUB &00 + PAL_black
   1.342 +	EQUB &10 + PAL_blue
   1.343 +	EQUB &20 + PAL_red
   1.344 +	EQUB &30 + PAL_magenta
   1.345 +	EQUB &40 + PAL_green
   1.346 +	EQUB &50 + PAL_cyan
   1.347 +	EQUB &60 + PAL_yellow
   1.348 +	EQUB &70 + PAL_white
   1.349 +
   1.350 +
   1.351 +
   1.352 +\ ******************************************************************
   1.353 +\ *	sin table
   1.354 +\ ******************************************************************
   1.355 +
   1.356 +; contains ABS sine values
   1.357 +; we don't store the sign as it confuses the multiplication.
   1.358 +; we can tell the sign very easily from whether the index is >128
   1.359 +
   1.360 +ALIGN &100	; so we don't incur page-crossed penalties
   1.361 +.sintable
   1.362 +FOR n, 0, 255
   1.363 +	EQUB ABS(SIN(n/128*PI)) * 255
   1.364 +NEXT
   1.365 +
   1.366 +
   1.367 +\ ******************************************************************
   1.368 +\ *	colour table
   1.369 +\ ******************************************************************
   1.370 +
   1.371 +ALIGN &100
   1.372 +.coltable
   1.373 +FOR n, 0, 255
   1.374 +	EQUB (SIN(n/128*PI) + 1) / 2.0001 * 7 + 1
   1.375 +NEXT
   1.376 +
   1.377 +
   1.378 +\ ******************************************************************
   1.379 +\ *	multiplication tables
   1.380 +\ ******************************************************************
   1.381 +
   1.382 +; This is a very quick way to do multiplies, based on the fact that:
   1.383 +;
   1.384 +;							(a+b)^2  =  a^2 + b^2 + 2ab    (I)
   1.385 +;							(a-b)^2  =  a^2 + b^2 - 2ab    (II)
   1.386 +;
   1.387 +; (I) minus (II) yields:	(a+b)^2 - (a-b)^2 = 4ab
   1.388 +;
   1.389 +; 		  or, rewritten:	ab = f(a+b) - f(a-b),
   1.390 +;											where f(x) = x^2 / 4
   1.391 +;
   1.392 +; We build a table of f(x) here with x=0..511, and then can perform
   1.393 +; 8-bit * 8-bit by 4 table lookups and a 16-bit subtract.
   1.394 +;
   1.395 +; In this case, we will discard the low byte of the result, so we
   1.396 +; only need the high bytes, and can do just 2 table lookups and a
   1.397 +; simple 8-bit subtract.
   1.398 +
   1.399 +ALIGN &100
   1.400 +.multtab1
   1.401 +FOR n, 0, 255
   1.402 +	EQUB HI(n*n DIV 4)
   1.403 +NEXT
   1.404 +.multtab2
   1.405 +FOR n, 256, 511
   1.406 +	EQUB HI(n*n DIV 4)
   1.407 +NEXT
   1.408 +
   1.409 +
   1.410 +\ ******************************************************************
   1.411 +\ *	dot tables
   1.412 +\ ******************************************************************
   1.413 +
   1.414 +; contains the phase of this dot
   1.415 +
   1.416 +ALIGN &100
   1.417 +.dotx
   1.418 +FOR n, 0, numdots-1
   1.419 +	EQUB RND(256)
   1.420 +NEXT
   1.421 +
   1.422 +
   1.423 +; contains the y position of the dot
   1.424 +; the dots are sorted by y positions, highest on screen first - this means we can do
   1.425 +; 'raster chasing'!
   1.426 +; the y positions are also biased so there are fewer at the poles, and more at the equator!
   1.427 +
   1.428 +ALIGN &100
   1.429 +.doty
   1.430 +FOR n, 0, numdots-1
   1.431 +	x = (n - numdots/2 + 0.5) / (numdots/2)
   1.432 +	y = (x - SIN(x*PI) * 0.1) * radius
   1.433 +	EQUB 128 + y
   1.434 +NEXT
   1.435 +
   1.436 +
   1.437 +; contains the radius of the ball at this y position
   1.438 +
   1.439 +ALIGN &100
   1.440 +.dotr
   1.441 +FOR n, 0, numdots-1
   1.442 +	x = (n - numdots/2 + 0.5) / (numdots/2)
   1.443 +	y = (x - SIN(x*PI) * 0.1) * radius
   1.444 +	r = SQR(radius*radius - y*y) / 2
   1.445 +	EQUB r
   1.446 +NEXT
   1.447 +
   1.448 +
   1.449 +\ ******************************************************************
   1.450 +\ *	End address to be saved
   1.451 +\ ******************************************************************
   1.452 +.end
   1.453 +
   1.454 +
   1.455 +
   1.456 +\ ******************************************************************
   1.457 +\ *	Space reserved for tables but not initialised with anything
   1.458 +\ * Therefore these are not saved in the executable
   1.459 +\ ******************************************************************
   1.460 +
   1.461 +; these store the screen address of the last dot
   1.462 +; at the end of the frame, we go through these tables, storing zeroes to
   1.463 +; all these addresses in order to delete the last frame
   1.464 +
   1.465 +ALIGN &100
   1.466 +.olddotaddrlo	SKIP numdots
   1.467 +
   1.468 +ALIGN &100
   1.469 +.olddotaddrhi	SKIP numdots
   1.470 +
   1.471 +ALIGN &100
   1.472 +.olddotaddry	SKIP numdots
   1.473 +
   1.474 +
   1.475 +
   1.476 +\ ******************************************************************
   1.477 +\ *	Save the code
   1.478 +\ ******************************************************************
   1.479 +
   1.480 +SAVE "Code", start, end
     2.1 --- a/demo.asm	Fri Mar 04 02:01:51 2011 +0000
     2.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.3 @@ -1,477 +0,0 @@
     2.4 -\ ******************************************************************
     2.5 -\ *
     2.6 -\ *		BeebAsm demo
     2.7 -\ *
     2.8 -\ *		Spinning star globe
     2.9 -\ *
    2.10 -\ *		Change the speed of rotation with Z and X keys
    2.11 -\ *		Press Esc to quit
    2.12 -\ *
    2.13 -\ *		Try assembling the code with debugrasters = TRUE (line 20)
    2.14 -\ *		It shows how the frame is split up into various stages of
    2.15 -\ *		processing.
    2.16 -\ *
    2.17 -\ *		The red part is where we start to plot the dots - starting as
    2.18 -\ *		soon before the first screenline of the next frame is rastered
    2.19 -\ *		as possible.
    2.20 -\ *
    2.21 -\ *		The magenta part is a small loop where we wait for 'vsync'.
    2.22 -\ *		It's not actually vsync, but in fact a fixed time from actual
    2.23 -\ *		vsync (timed by timer 1) from which point we can start to erase
    2.24 -\ *		points from the top of the screen down.
    2.25 -\ *
    2.26 -\ *		The blue part is the time when we are erasing dots.  Because
    2.27 -\ *		the dots are sorted from top to bottom of screen, we can
    2.28 -\ *		overlap these updates with the actual screen rasterisation.
    2.29 -\ *
    2.30 -\ ******************************************************************
    2.31 -
    2.32 -\\ Define globals
    2.33 -
    2.34 -numdots			= 160
    2.35 -radius			= 100
    2.36 -timerlength		= 64*8*26
    2.37 -debugrasters	= FALSE
    2.38 -
    2.39 -
    2.40 -\\ Define some zp locations
    2.41 -
    2.42 -ORG 0
    2.43 -
    2.44 -.xpos			SKIP 1
    2.45 -.ypos			SKIP 1
    2.46 -.colour			SKIP 1
    2.47 -.write			SKIP 2
    2.48 -.vsync			SKIP 1
    2.49 -.angle			SKIP 2
    2.50 -.speed			SKIP 2
    2.51 -.counter		SKIP 1
    2.52 -.temp			SKIP 1
    2.53 -.behindflag		SKIP 1
    2.54 -
    2.55 -
    2.56 -\\ Set start address
    2.57 -
    2.58 -ORG &1100
    2.59 -
    2.60 -\ ******************************************************************
    2.61 -\ *	The entry point of the demo
    2.62 -\ ******************************************************************
    2.63 -
    2.64 -.start
    2.65 -
    2.66 -	\\ Set up hardware state and interrupts
    2.67 -
    2.68 -	SEI
    2.69 -	LDX #&FF:TXS				; reset stack
    2.70 -	STX &FE44:STX &FE45
    2.71 -	LDA #&7F:STA &FE4E			; disable all interrupts
    2.72 -	STA &FE43					; set keyboard data direction
    2.73 -	LDA #&C2:STA &FE4E			; enable VSync and timer interrupt
    2.74 -	LDA #&0F:STA &FE42			; set addressable latch for writing
    2.75 -	LDA #3:STA &FE40			; keyboard write enable
    2.76 -	LDA #0:STA &FE4B			; timer 1 one shot mode
    2.77 -	LDA #LO(irq):STA &204
    2.78 -	LDA #HI(irq):STA &205		; set interrupt handler
    2.79 -
    2.80 -	\\ Clear the screen
    2.81 -
    2.82 -	LDX #&40
    2.83 -	LDA #0
    2.84 -	TAY
    2.85 -.clearloop
    2.86 -	STA &4000,Y
    2.87 -	INY
    2.88 -	BNE clearloop
    2.89 -	INC clearloop+2
    2.90 -	DEX
    2.91 -	BNE clearloop
    2.92 -
    2.93 -	\\ Set up CRTC for MODE 2
    2.94 -
    2.95 -	LDX #13
    2.96 -.crtcloop
    2.97 -	STX &FE00
    2.98 -	LDA crtcregs,X
    2.99 -	STA &FE01
   2.100 -	DEX
   2.101 -	BPL crtcloop
   2.102 -
   2.103 -	\\ Set up video ULA for MODE 2
   2.104 -
   2.105 -	LDA #&F4
   2.106 -	STA &FE20
   2.107 -
   2.108 -	\\ Set up palette for MODE 2
   2.109 -
   2.110 -	LDX #15
   2.111 -.palloop
   2.112 -	LDA paldata,X
   2.113 -	STA &FE21
   2.114 -	ORA #&80
   2.115 -	STA &FE21
   2.116 -	DEX
   2.117 -	BPL palloop
   2.118 -
   2.119 -	\\ Initialise vars
   2.120 -
   2.121 -	LDA #0:STA angle:STA angle+1
   2.122 -	STA vsync
   2.123 -	STA speed
   2.124 -	LDA #1:STA speed+1
   2.125 -
   2.126 -	\\ Enable interrupts, ready to start the main loop
   2.127 -
   2.128 -	CLI
   2.129 -
   2.130 -	\\ First we wait for 'vsync' so we are synchronised
   2.131 -
   2.132 -.initialwait
   2.133 -	LDA vsync:BEQ initialwait:LDA #0:STA vsync
   2.134 -
   2.135 -	\\ This is the main loop!
   2.136 -
   2.137 -.mainloop
   2.138 -
   2.139 -	\\ Plot every dot on the screen
   2.140 -
   2.141 -	LDX #0
   2.142 -.plotdotloop
   2.143 -	STX counter
   2.144 -
   2.145 -	; setup y pos ready for plot routine
   2.146 -
   2.147 -	LDA doty,X:STA ypos
   2.148 -
   2.149 -	; get sin index
   2.150 -
   2.151 -	CLC:LDA dotx,X:ADC angle+1:TAY
   2.152 -	CLC:ADC #64:STA behindflag
   2.153 -
   2.154 -	; get colour from sin index
   2.155 -
   2.156 -	LDA coltable,Y:STA colour
   2.157 -
   2.158 -	; perform sin(x) * radius
   2.159 -	; discussion of the multiplication method below in the table setup
   2.160 -
   2.161 -	SEC:LDA sintable,Y:STA temp:SBC dotr,X
   2.162 -	BCS noneg:EOR #&FF:ADC #1:.noneg
   2.163 -	CPY #128:TAY:BCS negativesine
   2.164 -
   2.165 -	CLC:LDA dotr,X:ADC temp:TAX
   2.166 -	BCS morethan256:SEC
   2.167 -	LDA multtab1,X:SBC multtab1,Y:JMP donemult
   2.168 -	.morethan256
   2.169 -	LDA multtab2,X:SBC multtab1,Y:JMP donemult
   2.170 -
   2.171 -	.negativesine
   2.172 -	CLC:LDA dotr,X:ADC temp:TAX
   2.173 -	BCS morethan256b:SEC
   2.174 -	LDA multtab1,Y:SBC multtab1,X:JMP donemult
   2.175 -	.morethan256b
   2.176 -	LDA multtab1,Y:SBC multtab2,X
   2.177 -	.donemult
   2.178 -
   2.179 -	CLC:ADC #64:STA xpos
   2.180 -
   2.181 -	; routine to plot a dot
   2.182 -	; also we remember the calculated screen address in the dot tables
   2.183 -
   2.184 -	LDA ypos:LSR A:LSR A:AND #&FE
   2.185 -	TAX
   2.186 -	LDA xpos:AND #&FE:ASL A:ASL A
   2.187 -	STA write
   2.188 -	LDY counter:STA olddotaddrlo,Y
   2.189 -	TXA:ADC #&40:STA write+1:STA olddotaddrhi,Y
   2.190 -	LDA ypos:AND #7:STA olddotaddry,Y:TAY
   2.191 -	LDA xpos:LSR A:LDA colour:ROL A:TAX
   2.192 -	LDA colours,X
   2.193 -	ORA (write),Y
   2.194 -	STA (write),Y
   2.195 -	BIT behindflag:BMI behind
   2.196 -
   2.197 -	; if the dot is in front, we double its size
   2.198 -
   2.199 -	DEY:BPL samescreenrow
   2.200 -	DEC write+1:DEC write+1:LDY #7:.samescreenrow
   2.201 -	LDA colours,X
   2.202 -	ORA (write),Y
   2.203 -	STA (write),Y
   2.204 -	.behind
   2.205 -
   2.206 -	; loop to the next dot
   2.207 -
   2.208 -	LDX counter
   2.209 -	INX:CPX #numdots
   2.210 -	BEQ waitforvsync
   2.211 -	JMP plotdotloop
   2.212 -
   2.213 -	\\ Wait for VSync here
   2.214 -
   2.215 -.waitforvsync
   2.216 -	IF debugrasters
   2.217 -		LDA #&00 + PAL_magenta:STA &FE21
   2.218 -	ENDIF
   2.219 -.waitingforvsync
   2.220 -	LDA vsync:BEQ waitingforvsync
   2.221 -	CMP #2:BCS exit		; insist that it runs in a frame!
   2.222 -	LDA #0:STA vsync
   2.223 -
   2.224 -	\\ Now delete all the old dots.
   2.225 -	\\ We actually do this when the screen is still rasterising down..!
   2.226 -
   2.227 -	TAX
   2.228 -.eraseloop
   2.229 -	LDY olddotaddrlo,X:STY write
   2.230 -	LDY olddotaddrhi,X:STY write+1
   2.231 -	LDY olddotaddry,X
   2.232 -	STA (write),Y
   2.233 -	DEY:BPL erasesamerow
   2.234 -	DEC write+1:DEC write+1:LDY #7:.erasesamerow
   2.235 -	STA (write),Y
   2.236 -	INX:CPX #numdots
   2.237 -	BNE eraseloop
   2.238 -
   2.239 -	IF debugrasters
   2.240 -		LDA #&00 + PAL_red:STA &FE21
   2.241 -	ENDIF
   2.242 -
   2.243 -	\\ Add to rotation
   2.244 -
   2.245 -	CLC:LDA angle:ADC speed:STA angle
   2.246 -	LDA angle+1:ADC speed+1:STA angle+1
   2.247 -
   2.248 -	\\ Check keypresses
   2.249 -
   2.250 -	LDA #66:STA &FE4F:LDA &FE4F:BPL notx
   2.251 -	CLC:LDA speed:ADC #16:STA speed:BCC notx:INC speed+1:.notx
   2.252 -	LDA #97:STA &FE4F:LDA &FE4F:BPL notz
   2.253 -	SEC:LDA speed:SBC #16:STA speed:BCS notz:DEC speed+1:.notz
   2.254 -	LDA #112:STA &FE4F:LDA &FE4F:BMI exit
   2.255 -
   2.256 -	JMP mainloop
   2.257 -
   2.258 -	\\ Exit - in the least graceful way possible :)
   2.259 -
   2.260 -.exit
   2.261 -	JMP (&FFFC)
   2.262 -
   2.263 -
   2.264 -
   2.265 -\ ******************************************************************
   2.266 -\ *	IRQ handler
   2.267 -\ ******************************************************************
   2.268 -
   2.269 -.irq
   2.270 -	LDA &FE4D:AND #2:BNE irqvsync
   2.271 -.irqtimer
   2.272 -	LDA #&40:STA &FE4D:INC vsync
   2.273 -	IF debugrasters
   2.274 -		LDA #&00 + PAL_blue:STA &FE21
   2.275 -	ENDIF
   2.276 -	LDA &FC
   2.277 -	RTI
   2.278 -.irqvsync
   2.279 -	STA &FE4D
   2.280 -	LDA #LO(timerlength):STA &FE44
   2.281 -	LDA #HI(timerlength):STA &FE45
   2.282 -	IF debugrasters
   2.283 -		LDA #&00 + PAL_black:STA &FE21
   2.284 -	ENDIF
   2.285 -	LDA &FC
   2.286 -	RTI
   2.287 -
   2.288 -
   2.289 -
   2.290 -\ ******************************************************************
   2.291 -\ *	Colour table used by the plot code
   2.292 -\ ******************************************************************
   2.293 -
   2.294 -.colours
   2.295 -	EQUB &00, &00		; black pixels
   2.296 -	EQUB &02, &01		; blue pixels
   2.297 -	EQUB &08, &04		; red pixels
   2.298 -	EQUB &0A, &05		; magenta pixels
   2.299 -	EQUB &20, &10		; green pixels
   2.300 -	EQUB &22, &11		; cyan pixels
   2.301 -	EQUB &28, &14		; yellow pixels
   2.302 -	EQUB &2A, &15		; white pixels
   2.303 -
   2.304 -
   2.305 -
   2.306 -\ ******************************************************************
   2.307 -\ *	Values of CRTC regs for MODE 2
   2.308 -\ ******************************************************************
   2.309 -
   2.310 -.crtcregs
   2.311 -	EQUB 127			; R0  horizontal total
   2.312 -	EQUB 64				; R1  horizontal displayed - shrunk a little
   2.313 -	EQUB 91				; R2  horizontal position
   2.314 -	EQUB 40				; R3  sync width
   2.315 -	EQUB 38				; R4  vertical total
   2.316 -	EQUB 0				; R5  vertical total adjust
   2.317 -	EQUB 32				; R6  vertical displayed
   2.318 -	EQUB 34				; R7  vertical position
   2.319 -	EQUB 0				; R8  interlace
   2.320 -	EQUB 7				; R9  scanlines per row
   2.321 -	EQUB 32				; R10 cursor start
   2.322 -	EQUB 8				; R11 cursor end
   2.323 -	EQUB HI(&4000/8)	; R12 screen start address, high
   2.324 -	EQUB LO(&4000/8)	; R13 screen start address, low
   2.325 -
   2.326 -
   2.327 -\ ******************************************************************
   2.328 -\ *	Values of palette regs for MODE 2
   2.329 -\ ******************************************************************
   2.330 -
   2.331 -PAL_black	= (0 EOR 7)
   2.332 -PAL_blue	= (4 EOR 7)
   2.333 -PAL_red		= (1 EOR 7)
   2.334 -PAL_magenta = (5 EOR 7)
   2.335 -PAL_green	= (2 EOR 7)
   2.336 -PAL_cyan	= (6 EOR 7)
   2.337 -PAL_yellow	= (3 EOR 7)
   2.338 -PAL_white	= (7 EOR 7)
   2.339 -
   2.340 -.paldata
   2.341 -	EQUB &00 + PAL_black
   2.342 -	EQUB &10 + PAL_blue
   2.343 -	EQUB &20 + PAL_red
   2.344 -	EQUB &30 + PAL_magenta
   2.345 -	EQUB &40 + PAL_green
   2.346 -	EQUB &50 + PAL_cyan
   2.347 -	EQUB &60 + PAL_yellow
   2.348 -	EQUB &70 + PAL_white
   2.349 -
   2.350 -
   2.351 -
   2.352 -\ ******************************************************************
   2.353 -\ *	sin table
   2.354 -\ ******************************************************************
   2.355 -
   2.356 -; contains ABS sine values
   2.357 -; we don't store the sign as it confuses the multiplication.
   2.358 -; we can tell the sign very easily from whether the index is >128
   2.359 -
   2.360 -ALIGN &100	; so we don't incur page-crossed penalties
   2.361 -.sintable
   2.362 -FOR n, 0, 255
   2.363 -	EQUB ABS(SIN(n/128*PI)) * 255
   2.364 -NEXT
   2.365 -
   2.366 -
   2.367 -\ ******************************************************************
   2.368 -\ *	colour table
   2.369 -\ ******************************************************************
   2.370 -
   2.371 -ALIGN &100
   2.372 -.coltable
   2.373 -FOR n, 0, 255
   2.374 -	EQUB (SIN(n/128*PI) + 1) / 2.0001 * 7 + 1
   2.375 -NEXT
   2.376 -
   2.377 -
   2.378 -\ ******************************************************************
   2.379 -\ *	multiplication tables
   2.380 -\ ******************************************************************
   2.381 -
   2.382 -; This is a very quick way to do multiplies, based on the fact that:
   2.383 -;
   2.384 -;							(a+b)^2  =  a^2 + b^2 + 2ab    (I)
   2.385 -;							(a-b)^2  =  a^2 + b^2 - 2ab    (II)
   2.386 -;
   2.387 -; (I) minus (II) yields:	(a+b)^2 - (a-b)^2 = 4ab
   2.388 -;
   2.389 -; 		  or, rewritten:	ab = f(a+b) - f(a-b),
   2.390 -;											where f(x) = x^2 / 4
   2.391 -;
   2.392 -; We build a table of f(x) here with x=0..511, and then can perform
   2.393 -; 8-bit * 8-bit by 4 table lookups and a 16-bit subtract.
   2.394 -;
   2.395 -; In this case, we will discard the low byte of the result, so we
   2.396 -; only need the high bytes, and can do just 2 table lookups and a
   2.397 -; simple 8-bit subtract.
   2.398 -
   2.399 -ALIGN &100
   2.400 -.multtab1
   2.401 -FOR n, 0, 255
   2.402 -	EQUB HI(n*n DIV 4)
   2.403 -NEXT
   2.404 -.multtab2
   2.405 -FOR n, 256, 511
   2.406 -	EQUB HI(n*n DIV 4)
   2.407 -NEXT
   2.408 -
   2.409 -
   2.410 -\ ******************************************************************
   2.411 -\ *	dot tables
   2.412 -\ ******************************************************************
   2.413 -
   2.414 -; contains the phase of this dot
   2.415 -
   2.416 -ALIGN &100
   2.417 -.dotx
   2.418 -FOR n, 0, numdots-1
   2.419 -	EQUB RND(256)
   2.420 -NEXT
   2.421 -
   2.422 -
   2.423 -; contains the y position of the dot
   2.424 -; the dots are sorted by y positions, highest on screen first - this means we can do
   2.425 -; 'raster chasing'!
   2.426 -; the y positions are also biased so there are fewer at the poles, and more at the equator!
   2.427 -
   2.428 -ALIGN &100
   2.429 -.doty
   2.430 -FOR n, 0, numdots-1
   2.431 -	x = (n - numdots/2 + 0.5) / (numdots/2)
   2.432 -	y = (x - SIN(x*PI) * 0.1) * radius
   2.433 -	EQUB 128 + y
   2.434 -NEXT
   2.435 -
   2.436 -
   2.437 -; contains the radius of the ball at this y position
   2.438 -
   2.439 -ALIGN &100
   2.440 -.dotr
   2.441 -FOR n, 0, numdots-1
   2.442 -	x = (n - numdots/2 + 0.5) / (numdots/2)
   2.443 -	y = (x - SIN(x*PI) * 0.1) * radius
   2.444 -	r = SQR(radius*radius - y*y) / 2
   2.445 -	EQUB r
   2.446 -NEXT
   2.447 -
   2.448 -
   2.449 -\ ******************************************************************
   2.450 -\ *	End address to be saved
   2.451 -\ ******************************************************************
   2.452 -.end
   2.453 -
   2.454 -
   2.455 -
   2.456 -\ ******************************************************************
   2.457 -\ *	Space reserved for tables but not initialised with anything
   2.458 -\ * Therefore these are not saved in the executable
   2.459 -\ ******************************************************************
   2.460 -
   2.461 -; these store the screen address of the last dot
   2.462 -; at the end of the frame, we go through these tables, storing zeroes to
   2.463 -; all these addresses in order to delete the last frame
   2.464 -
   2.465 -ALIGN &100
   2.466 -.olddotaddrlo	SKIP numdots
   2.467 -
   2.468 -ALIGN &100
   2.469 -.olddotaddrhi	SKIP numdots
   2.470 -
   2.471 -ALIGN &100
   2.472 -.olddotaddry	SKIP numdots
   2.473 -
   2.474 -
   2.475 -
   2.476 -\ ******************************************************************
   2.477 -\ *	Save the code
   2.478 -\ ******************************************************************
   2.479 -
   2.480 -SAVE "Code", start, end
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/relocdemo.6502	Sun Mar 06 18:19:27 2011 +0100
     3.3 @@ -0,0 +1,522 @@
     3.4 +\ ******************************************************************
     3.5 +\ *
     3.6 +\ *		Relocation demo
     3.7 +\ *
     3.8 +\ *		Simple demonstration of how to write self-relocating code
     3.9 +\ *		in BeebAsm, using the new 'reload address' feature of SAVE.
    3.10 +\ *
    3.11 +\ *		This uses the 'star globe' demo as a base.
    3.12 +\ *
    3.13 +\ ******************************************************************
    3.14 +
    3.15 +
    3.16 +\\ Define addresses
    3.17 +
    3.18 +NATIVE_ADDR		= &300		; address at which code will run
    3.19 +RELOAD_ADDR		= &1100		; address at which code will load
    3.20 +
    3.21 +OFFSET			= RELOAD_ADDR - NATIVE_ADDR
    3.22 +
    3.23 +
    3.24 +\\ Define globals
    3.25 +
    3.26 +numdots			= 160
    3.27 +radius			= 100
    3.28 +timerlength		= 64*8*26
    3.29 +debugrasters	= FALSE
    3.30 +
    3.31 +
    3.32 +\\ Define some zp locations
    3.33 +
    3.34 +ORG 0
    3.35 +
    3.36 +.xpos			SKIP 1
    3.37 +.ypos			SKIP 1
    3.38 +.colour			SKIP 1
    3.39 +.write			SKIP 2
    3.40 +.vsync			SKIP 1
    3.41 +.angle			SKIP 2
    3.42 +.speed			SKIP 2
    3.43 +.counter		SKIP 1
    3.44 +.temp			SKIP 1
    3.45 +.behindflag		SKIP 1
    3.46 +
    3.47 +
    3.48 +\\ Set start address
    3.49 +
    3.50 +ORG NATIVE_ADDR
    3.51 +
    3.52 +
    3.53 +\ ******************************************************************
    3.54 +\ *	The start of the demo 'proper', after it has been relocated
    3.55 +\ ******************************************************************
    3.56 +
    3.57 +.START
    3.58 +
    3.59 +	\\ Clear the screen
    3.60 +
    3.61 +	LDX #&40
    3.62 +	LDA #0
    3.63 +	TAY
    3.64 +.clearloop
    3.65 +	STA &4000,Y
    3.66 +	INY
    3.67 +	BNE clearloop
    3.68 +	INC clearloop+2
    3.69 +	DEX
    3.70 +	BNE clearloop
    3.71 +
    3.72 +	\\ Enable interrupts, ready to start the main loop
    3.73 +
    3.74 +	CLI
    3.75 +
    3.76 +	\\ First we wait for 'vsync' so we are synchronised
    3.77 +
    3.78 +.initialwait
    3.79 +	LDA vsync:BEQ initialwait:LDA #0:STA vsync
    3.80 +
    3.81 +	\\ Enable the screen
    3.82 +	
    3.83 +	LDA #6:STA &FE00:LDA #32:STA &FE01
    3.84 +
    3.85 +	\\ This is the main loop!
    3.86 +
    3.87 +.mainloop
    3.88 +
    3.89 +	\\ Plot every dot on the screen
    3.90 +
    3.91 +	LDX #0
    3.92 +.plotdotloop
    3.93 +	STX counter
    3.94 +
    3.95 +	; setup y pos ready for plot routine
    3.96 +
    3.97 +	LDA doty,X:STA ypos
    3.98 +
    3.99 +	; get sin index
   3.100 +
   3.101 +	CLC:LDA dotx,X:ADC angle+1:TAY
   3.102 +	CLC:ADC #64:STA behindflag
   3.103 +
   3.104 +	; get colour from sin index
   3.105 +
   3.106 +	LDA coltable,Y:STA colour
   3.107 +
   3.108 +	; perform sin(x) * radius
   3.109 +	; discussion of the multiplication method below in the table setup
   3.110 +
   3.111 +	SEC:LDA sintable,Y:STA temp:SBC dotr,X
   3.112 +	BCS noneg:EOR #&FF:ADC #1:.noneg
   3.113 +	CPY #128:TAY:BCS negativesine
   3.114 +
   3.115 +	CLC:LDA dotr,X:ADC temp:TAX
   3.116 +	BCS morethan256:SEC
   3.117 +	LDA multtab1,X:SBC multtab1,Y:JMP donemult
   3.118 +	.morethan256
   3.119 +	LDA multtab2,X:SBC multtab1,Y:JMP donemult
   3.120 +
   3.121 +	.negativesine
   3.122 +	CLC:LDA dotr,X:ADC temp:TAX
   3.123 +	BCS morethan256b:SEC
   3.124 +	LDA multtab1,Y:SBC multtab1,X:JMP donemult
   3.125 +	.morethan256b
   3.126 +	LDA multtab1,Y:SBC multtab2,X
   3.127 +	.donemult
   3.128 +
   3.129 +	CLC:ADC #64:STA xpos
   3.130 +
   3.131 +	; routine to plot a dot
   3.132 +	; also we remember the calculated screen address in the dot tables
   3.133 +
   3.134 +	LDA ypos:LSR A:LSR A:AND #&FE
   3.135 +	TAX
   3.136 +	LDA xpos:AND #&FE:ASL A:ASL A
   3.137 +	STA write
   3.138 +	LDY counter:STA olddotaddrlo,Y
   3.139 +	TXA:ADC #&40:STA write+1:STA olddotaddrhi,Y
   3.140 +	LDA ypos:AND #7:STA olddotaddry,Y:TAY
   3.141 +	LDA xpos:LSR A:LDA colour:ROL A:TAX
   3.142 +	LDA colours,X
   3.143 +	ORA (write),Y
   3.144 +	STA (write),Y
   3.145 +	BIT behindflag:BMI behind
   3.146 +
   3.147 +	; if the dot is in front, we double its size
   3.148 +
   3.149 +	DEY:BPL samescreenrow
   3.150 +	DEC write+1:DEC write+1:LDY #7:.samescreenrow
   3.151 +	LDA colours,X
   3.152 +	ORA (write),Y
   3.153 +	STA (write),Y
   3.154 +	.behind
   3.155 +
   3.156 +	; loop to the next dot
   3.157 +
   3.158 +	LDX counter
   3.159 +	INX:CPX #numdots
   3.160 +	BEQ waitforvsync
   3.161 +	JMP plotdotloop
   3.162 +
   3.163 +	\\ Wait for VSync here
   3.164 +
   3.165 +.waitforvsync
   3.166 +	IF debugrasters
   3.167 +		LDA #&00 + PAL_magenta:STA &FE21
   3.168 +	ENDIF
   3.169 +.waitingforvsync
   3.170 +	LDA vsync:BEQ waitingforvsync
   3.171 +	CMP #2:BCS exit		; insist that it runs in a frame!
   3.172 +	LDA #0:STA vsync
   3.173 +
   3.174 +	\\ Now delete all the old dots.
   3.175 +	\\ We actually do this when the screen is still rasterising down..!
   3.176 +
   3.177 +	TAX
   3.178 +.eraseloop
   3.179 +	LDY olddotaddrlo,X:STY write
   3.180 +	LDY olddotaddrhi,X:STY write+1
   3.181 +	LDY olddotaddry,X
   3.182 +	STA (write),Y
   3.183 +	DEY:BPL erasesamerow
   3.184 +	DEC write+1:DEC write+1:LDY #7:.erasesamerow
   3.185 +	STA (write),Y
   3.186 +	INX:CPX #numdots
   3.187 +	BNE eraseloop
   3.188 +
   3.189 +	IF debugrasters
   3.190 +		LDA #&00 + PAL_red:STA &FE21
   3.191 +	ENDIF
   3.192 +
   3.193 +	\\ Add to rotation
   3.194 +
   3.195 +	CLC:LDA angle:ADC speed:STA angle
   3.196 +	LDA angle+1:ADC speed+1:STA angle+1
   3.197 +
   3.198 +	\\ Check keypresses
   3.199 +
   3.200 +	LDA #66:STA &FE4F:LDA &FE4F:BPL notx
   3.201 +	CLC:LDA speed:ADC #16:STA speed:BCC notx:INC speed+1:.notx
   3.202 +	LDA #97:STA &FE4F:LDA &FE4F:BPL notz
   3.203 +	SEC:LDA speed:SBC #16:STA speed:BCS notz:DEC speed+1:.notz
   3.204 +	LDA #112:STA &FE4F:LDA &FE4F:BMI exit
   3.205 +
   3.206 +	JMP mainloop
   3.207 +
   3.208 +	\\ Exit - in the least graceful way possible :)
   3.209 +
   3.210 +.exit
   3.211 +	JMP (&FFFC)
   3.212 +
   3.213 +
   3.214 +
   3.215 +\ ******************************************************************
   3.216 +\ *	IRQ handler
   3.217 +\ ******************************************************************
   3.218 +
   3.219 +.irq
   3.220 +	LDA &FE4D:AND #2:BNE irqvsync
   3.221 +.irqtimer
   3.222 +	LDA #&40:STA &FE4D:INC vsync
   3.223 +	IF debugrasters
   3.224 +		LDA #&00 + PAL_blue:STA &FE21
   3.225 +	ENDIF
   3.226 +	LDA &FC
   3.227 +	RTI
   3.228 +.irqvsync
   3.229 +	STA &FE4D
   3.230 +	LDA #LO(timerlength):STA &FE44
   3.231 +	LDA #HI(timerlength):STA &FE45
   3.232 +	IF debugrasters
   3.233 +		LDA #&00 + PAL_black:STA &FE21
   3.234 +	ENDIF
   3.235 +	LDA &FC
   3.236 +	RTI
   3.237 +
   3.238 +
   3.239 +
   3.240 +\ ******************************************************************
   3.241 +\ *	Colour table used by the plot code
   3.242 +\ ******************************************************************
   3.243 +
   3.244 +.colours
   3.245 +	EQUB &00, &00		; black pixels
   3.246 +	EQUB &02, &01		; blue pixels
   3.247 +	EQUB &08, &04		; red pixels
   3.248 +	EQUB &0A, &05		; magenta pixels
   3.249 +	EQUB &20, &10		; green pixels
   3.250 +	EQUB &22, &11		; cyan pixels
   3.251 +	EQUB &28, &14		; yellow pixels
   3.252 +	EQUB &2A, &15		; white pixels
   3.253 +
   3.254 +
   3.255 +\ ******************************************************************
   3.256 +\ *	sin table
   3.257 +\ ******************************************************************
   3.258 +
   3.259 +; contains ABS sine values
   3.260 +; we don't store the sign as it confuses the multiplication.
   3.261 +; we can tell the sign very easily from whether the index is >128
   3.262 +
   3.263 +ALIGN &100	; so we don't incur page-crossed penalties
   3.264 +.sintable
   3.265 +FOR n, 0, 255
   3.266 +	EQUB ABS(SIN(n/128*PI)) * 255
   3.267 +NEXT
   3.268 +
   3.269 +
   3.270 +\ ******************************************************************
   3.271 +\ *	colour table
   3.272 +\ ******************************************************************
   3.273 +
   3.274 +ALIGN &100
   3.275 +.coltable
   3.276 +FOR n, 0, 255
   3.277 +	EQUB (SIN(n/128*PI) + 1) / 2.0001 * 7 + 1
   3.278 +NEXT
   3.279 +
   3.280 +
   3.281 +\ ******************************************************************
   3.282 +\ *	multiplication tables
   3.283 +\ ******************************************************************
   3.284 +
   3.285 +; This is a very quick way to do multiplies, based on the fact that:
   3.286 +;
   3.287 +;							(a+b)^2  =  a^2 + b^2 + 2ab    (I)
   3.288 +;							(a-b)^2  =  a^2 + b^2 - 2ab    (II)
   3.289 +;
   3.290 +; (I) minus (II) yields:	(a+b)^2 - (a-b)^2 = 4ab
   3.291 +;
   3.292 +; 		  or, rewritten:	ab = f(a+b) - f(a-b),
   3.293 +;											where f(x) = x^2 / 4
   3.294 +;
   3.295 +; We build a table of f(x) here with x=0..511, and then can perform
   3.296 +; 8-bit * 8-bit by 4 table lookups and a 16-bit subtract.
   3.297 +;
   3.298 +; In this case, we will discard the low byte of the result, so we
   3.299 +; only need the high bytes, and can do just 2 table lookups and a
   3.300 +; simple 8-bit subtract.
   3.301 +
   3.302 +ALIGN &100
   3.303 +.multtab1
   3.304 +FOR n, 0, 255
   3.305 +	EQUB HI(n*n DIV 4)
   3.306 +NEXT
   3.307 +.multtab2
   3.308 +FOR n, 256, 511
   3.309 +	EQUB HI(n*n DIV 4)
   3.310 +NEXT
   3.311 +
   3.312 +
   3.313 +\ ******************************************************************
   3.314 +\ *	dot tables
   3.315 +\ ******************************************************************
   3.316 +
   3.317 +; contains the phase of this dot
   3.318 +
   3.319 +ALIGN &100
   3.320 +.dotx
   3.321 +FOR n, 0, numdots-1
   3.322 +	EQUB RND(256)
   3.323 +NEXT
   3.324 +
   3.325 +
   3.326 +; contains the y position of the dot
   3.327 +; the dots are sorted by y positions, highest on screen first - this means we can do
   3.328 +; 'raster chasing'!
   3.329 +; the y positions are also biased so there are fewer at the poles, and more at the equator!
   3.330 +
   3.331 +ALIGN &100
   3.332 +.doty
   3.333 +FOR n, 0, numdots-1
   3.334 +	x = (n - numdots/2 + 0.5) / (numdots/2)
   3.335 +	y = (x - SIN(x*PI) * 0.1) * radius
   3.336 +	EQUB 128 + y
   3.337 +NEXT
   3.338 +
   3.339 +
   3.340 +; contains the radius of the ball at this y position
   3.341 +
   3.342 +ALIGN &100
   3.343 +.dotr
   3.344 +FOR n, 0, numdots-1
   3.345 +	x = (n - numdots/2 + 0.5) / (numdots/2)
   3.346 +	y = (x - SIN(x*PI) * 0.1) * radius
   3.347 +	r = SQR(radius*radius - y*y) / 2
   3.348 +	EQUB r
   3.349 +NEXT
   3.350 +
   3.351 +
   3.352 +\ ******************************************************************
   3.353 +\ *	This is the end of the main native block of code
   3.354 +\ ******************************************************************
   3.355 +.END
   3.356 +
   3.357 +
   3.358 +\ ******************************************************************
   3.359 +\ *	The entry point of the demo
   3.360 +\ * This relocates the code to its 'real' address, and can also
   3.361 +\ * do one-time initialisation, i.e. code we can chuck away afterwards.
   3.362 +\ *
   3.363 +\ * Since this is the relocation code, it has to go at the very end of
   3.364 +\ * the executable.
   3.365 +\
   3.366 +\ * This code will be running at its assemble address + OFFSET,
   3.367 +\ * so we have to patch up any absolute address references accordingly.
   3.368 +\ ******************************************************************
   3.369 +
   3.370 +ALIGN &100
   3.371 +.RELOC_START
   3.372 +
   3.373 +	\\ Set up hardware state and interrupts
   3.374 +
   3.375 +	SEI
   3.376 +	LDX #&FF:TXS				; reset stack
   3.377 +	STX &FE44:STX &FE45
   3.378 +	LDA #&7F:STA &FE4E			; disable all interrupts
   3.379 +	STA &FE43					; set keyboard data direction
   3.380 +	LDA #&C2:STA &FE4E			; enable VSync and timer interrupt
   3.381 +	LDA #&0F:STA &FE42			; set addressable latch for writing
   3.382 +	LDA #3:STA &FE40			; keyboard write enable
   3.383 +	LDA #0:STA &FE4B			; timer 1 one shot mode
   3.384 +	LDA #LO(irq):STA &204
   3.385 +	LDA #HI(irq):STA &205		; set interrupt handler
   3.386 +
   3.387 +	\\ Set up CRTC for MODE 2
   3.388 +
   3.389 +	LDX #13
   3.390 +.crtcloop
   3.391 +	STX &FE00
   3.392 +	LDA crtcregs + OFFSET,X		; PATCHED ADDRESS
   3.393 +	STA &FE01
   3.394 +	DEX
   3.395 +	BPL crtcloop
   3.396 +
   3.397 +	\\ Set up video ULA for MODE 2
   3.398 +
   3.399 +	LDA #&F4
   3.400 +	STA &FE20
   3.401 +
   3.402 +	\\ Set up palette for MODE 2
   3.403 +
   3.404 +	LDX #15
   3.405 +.palloop
   3.406 +	LDA paldata + OFFSET,X		; PATCHED ADDRESS
   3.407 +	STA &FE21
   3.408 +	ORA #&80
   3.409 +	STA &FE21
   3.410 +	DEX
   3.411 +	BPL palloop
   3.412 +
   3.413 +	\\ Initialise vars
   3.414 +
   3.415 +	LDA #0:STA angle:STA angle+1
   3.416 +	STA vsync
   3.417 +	STA speed
   3.418 +	LDA #1:STA speed+1
   3.419 +	
   3.420 +	\\ Relocate
   3.421 +	
   3.422 +	LDX #HI(RELOC_START-START)
   3.423 +	LDY #0
   3.424 +	.relocloop
   3.425 +	LDA RELOAD_ADDR,Y
   3.426 +	STA NATIVE_ADDR,Y
   3.427 +	INY
   3.428 +	BNE relocloop
   3.429 +	INC relocloop+OFFSET+2		; PATCHED ADDRESS
   3.430 +	INC relocloop+OFFSET+5		; PATCHED ADDRESS
   3.431 +	DEX
   3.432 +	BNE relocloop
   3.433 +	
   3.434 +	JMP START
   3.435 +
   3.436 +
   3.437 +\ ******************************************************************
   3.438 +\ *	Values of CRTC regs for MODE 2
   3.439 +\ ******************************************************************
   3.440 +
   3.441 +.crtcregs
   3.442 +	EQUB 127			; R0  horizontal total
   3.443 +	EQUB 64				; R1  horizontal displayed - shrunk a little
   3.444 +	EQUB 91				; R2  horizontal position
   3.445 +	EQUB 40				; R3  sync width
   3.446 +	EQUB 38				; R4  vertical total
   3.447 +	EQUB 0				; R5  vertical total adjust
   3.448 +	EQUB 0				; R6  vertical displayed
   3.449 +	EQUB 34				; R7  vertical position
   3.450 +	EQUB 0				; R8  interlace
   3.451 +	EQUB 7				; R9  scanlines per row
   3.452 +	EQUB 32				; R10 cursor start
   3.453 +	EQUB 8				; R11 cursor end
   3.454 +	EQUB HI(&4000/8)	; R12 screen start address, high
   3.455 +	EQUB LO(&4000/8)	; R13 screen start address, low
   3.456 +
   3.457 +
   3.458 +\ ******************************************************************
   3.459 +\ *	Values of palette regs for MODE 2
   3.460 +\ ******************************************************************
   3.461 +
   3.462 +PAL_black	= (0 EOR 7)
   3.463 +PAL_blue	= (4 EOR 7)
   3.464 +PAL_red		= (1 EOR 7)
   3.465 +PAL_magenta = (5 EOR 7)
   3.466 +PAL_green	= (2 EOR 7)
   3.467 +PAL_cyan	= (6 EOR 7)
   3.468 +PAL_yellow	= (3 EOR 7)
   3.469 +PAL_white	= (7 EOR 7)
   3.470 +
   3.471 +.paldata
   3.472 +	EQUB &00 + PAL_black
   3.473 +	EQUB &10 + PAL_blue
   3.474 +	EQUB &20 + PAL_red
   3.475 +	EQUB &30 + PAL_magenta
   3.476 +	EQUB &40 + PAL_green
   3.477 +	EQUB &50 + PAL_cyan
   3.478 +	EQUB &60 + PAL_yellow
   3.479 +	EQUB &70 + PAL_white
   3.480 +
   3.481 +
   3.482 +
   3.483 +
   3.484 +\ ******************************************************************
   3.485 +\ *	End address to be saved
   3.486 +\ ******************************************************************
   3.487 +.RELOC_END
   3.488 +
   3.489 +
   3.490 +\ ******************************************************************
   3.491 +\ *	Save the code, before the following data overlay clears it again
   3.492 +\ ******************************************************************
   3.493 +
   3.494 +SAVE "Code", START, RELOC_END, RELOC_START+OFFSET, RELOAD_ADDR
   3.495 +
   3.496 +
   3.497 +
   3.498 +
   3.499 +\ ******************************************************************
   3.500 +\ * Start a new overlay:
   3.501 +\ *
   3.502 +\ * This is overlapped with the relocation code above, because it
   3.503 +\ * will already have been thrown away by the time these tables are
   3.504 +\ * used.
   3.505 +\ *
   3.506 +\ * These tables are filled at run-time, hence we just define their
   3.507 +\ * addresses, we don't need to save anything.
   3.508 +\ ******************************************************************
   3.509 +
   3.510 +CLEAR END, RELOC_END
   3.511 +ORG END
   3.512 +
   3.513 +; these store the screen address of the last dot
   3.514 +; at the end of the frame, we go through these tables, storing zeroes to
   3.515 +; all these addresses in order to delete the last frame
   3.516 +
   3.517 +ALIGN &100
   3.518 +.olddotaddrlo	SKIP numdots
   3.519 +
   3.520 +ALIGN &100
   3.521 +.olddotaddrhi	SKIP numdots
   3.522 +
   3.523 +ALIGN &100
   3.524 +.olddotaddry	SKIP numdots
   3.525 +
     4.1 --- a/relocdemo.asm	Fri Mar 04 02:01:51 2011 +0000
     4.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.3 @@ -1,522 +0,0 @@
     4.4 -\ ******************************************************************
     4.5 -\ *
     4.6 -\ *		Relocation demo
     4.7 -\ *
     4.8 -\ *		Simple demonstration of how to write self-relocating code
     4.9 -\ *		in BeebAsm, using the new 'reload address' feature of SAVE.
    4.10 -\ *
    4.11 -\ *		This uses the 'star globe' demo as a base.
    4.12 -\ *
    4.13 -\ ******************************************************************
    4.14 -
    4.15 -
    4.16 -\\ Define addresses
    4.17 -
    4.18 -NATIVE_ADDR		= &300		; address at which code will run
    4.19 -RELOAD_ADDR		= &1100		; address at which code will load
    4.20 -
    4.21 -OFFSET			= RELOAD_ADDR - NATIVE_ADDR
    4.22 -
    4.23 -
    4.24 -\\ Define globals
    4.25 -
    4.26 -numdots			= 160
    4.27 -radius			= 100
    4.28 -timerlength		= 64*8*26
    4.29 -debugrasters	= FALSE
    4.30 -
    4.31 -
    4.32 -\\ Define some zp locations
    4.33 -
    4.34 -ORG 0
    4.35 -
    4.36 -.xpos			SKIP 1
    4.37 -.ypos			SKIP 1
    4.38 -.colour			SKIP 1
    4.39 -.write			SKIP 2
    4.40 -.vsync			SKIP 1
    4.41 -.angle			SKIP 2
    4.42 -.speed			SKIP 2
    4.43 -.counter		SKIP 1
    4.44 -.temp			SKIP 1
    4.45 -.behindflag		SKIP 1
    4.46 -
    4.47 -
    4.48 -\\ Set start address
    4.49 -
    4.50 -ORG NATIVE_ADDR
    4.51 -
    4.52 -
    4.53 -\ ******************************************************************
    4.54 -\ *	The start of the demo 'proper', after it has been relocated
    4.55 -\ ******************************************************************
    4.56 -
    4.57 -.START
    4.58 -
    4.59 -	\\ Clear the screen
    4.60 -
    4.61 -	LDX #&40
    4.62 -	LDA #0
    4.63 -	TAY
    4.64 -.clearloop
    4.65 -	STA &4000,Y
    4.66 -	INY
    4.67 -	BNE clearloop
    4.68 -	INC clearloop+2
    4.69 -	DEX
    4.70 -	BNE clearloop
    4.71 -
    4.72 -	\\ Enable interrupts, ready to start the main loop
    4.73 -
    4.74 -	CLI
    4.75 -
    4.76 -	\\ First we wait for 'vsync' so we are synchronised
    4.77 -
    4.78 -.initialwait
    4.79 -	LDA vsync:BEQ initialwait:LDA #0:STA vsync
    4.80 -
    4.81 -	\\ Enable the screen
    4.82 -	
    4.83 -	LDA #6:STA &FE00:LDA #32:STA &FE01
    4.84 -
    4.85 -	\\ This is the main loop!
    4.86 -
    4.87 -.mainloop
    4.88 -
    4.89 -	\\ Plot every dot on the screen
    4.90 -
    4.91 -	LDX #0
    4.92 -.plotdotloop
    4.93 -	STX counter
    4.94 -
    4.95 -	; setup y pos ready for plot routine
    4.96 -
    4.97 -	LDA doty,X:STA ypos
    4.98 -
    4.99 -	; get sin index
   4.100 -
   4.101 -	CLC:LDA dotx,X:ADC angle+1:TAY
   4.102 -	CLC:ADC #64:STA behindflag
   4.103 -
   4.104 -	; get colour from sin index
   4.105 -
   4.106 -	LDA coltable,Y:STA colour
   4.107 -
   4.108 -	; perform sin(x) * radius
   4.109 -	; discussion of the multiplication method below in the table setup
   4.110 -
   4.111 -	SEC:LDA sintable,Y:STA temp:SBC dotr,X
   4.112 -	BCS noneg:EOR #&FF:ADC #1:.noneg
   4.113 -	CPY #128:TAY:BCS negativesine
   4.114 -
   4.115 -	CLC:LDA dotr,X:ADC temp:TAX
   4.116 -	BCS morethan256:SEC
   4.117 -	LDA multtab1,X:SBC multtab1,Y:JMP donemult
   4.118 -	.morethan256
   4.119 -	LDA multtab2,X:SBC multtab1,Y:JMP donemult
   4.120 -
   4.121 -	.negativesine
   4.122 -	CLC:LDA dotr,X:ADC temp:TAX
   4.123 -	BCS morethan256b:SEC
   4.124 -	LDA multtab1,Y:SBC multtab1,X:JMP donemult
   4.125 -	.morethan256b
   4.126 -	LDA multtab1,Y:SBC multtab2,X
   4.127 -	.donemult
   4.128 -
   4.129 -	CLC:ADC #64:STA xpos
   4.130 -
   4.131 -	; routine to plot a dot
   4.132 -	; also we remember the calculated screen address in the dot tables
   4.133 -
   4.134 -	LDA ypos:LSR A:LSR A:AND #&FE
   4.135 -	TAX
   4.136 -	LDA xpos:AND #&FE:ASL A:ASL A
   4.137 -	STA write
   4.138 -	LDY counter:STA olddotaddrlo,Y
   4.139 -	TXA:ADC #&40:STA write+1:STA olddotaddrhi,Y
   4.140 -	LDA ypos:AND #7:STA olddotaddry,Y:TAY
   4.141 -	LDA xpos:LSR A:LDA colour:ROL A:TAX
   4.142 -	LDA colours,X
   4.143 -	ORA (write),Y
   4.144 -	STA (write),Y
   4.145 -	BIT behindflag:BMI behind
   4.146 -
   4.147 -	; if the dot is in front, we double its size
   4.148 -
   4.149 -	DEY:BPL samescreenrow
   4.150 -	DEC write+1:DEC write+1:LDY #7:.samescreenrow
   4.151 -	LDA colours,X
   4.152 -	ORA (write),Y
   4.153 -	STA (write),Y
   4.154 -	.behind
   4.155 -
   4.156 -	; loop to the next dot
   4.157 -
   4.158 -	LDX counter
   4.159 -	INX:CPX #numdots
   4.160 -	BEQ waitforvsync
   4.161 -	JMP plotdotloop
   4.162 -
   4.163 -	\\ Wait for VSync here
   4.164 -
   4.165 -.waitforvsync
   4.166 -	IF debugrasters
   4.167 -		LDA #&00 + PAL_magenta:STA &FE21
   4.168 -	ENDIF
   4.169 -.waitingforvsync
   4.170 -	LDA vsync:BEQ waitingforvsync
   4.171 -	CMP #2:BCS exit		; insist that it runs in a frame!
   4.172 -	LDA #0:STA vsync
   4.173 -
   4.174 -	\\ Now delete all the old dots.
   4.175 -	\\ We actually do this when the screen is still rasterising down..!
   4.176 -
   4.177 -	TAX
   4.178 -.eraseloop
   4.179 -	LDY olddotaddrlo,X:STY write
   4.180 -	LDY olddotaddrhi,X:STY write+1
   4.181 -	LDY olddotaddry,X
   4.182 -	STA (write),Y
   4.183 -	DEY:BPL erasesamerow
   4.184 -	DEC write+1:DEC write+1:LDY #7:.erasesamerow
   4.185 -	STA (write),Y
   4.186 -	INX:CPX #numdots
   4.187 -	BNE eraseloop
   4.188 -
   4.189 -	IF debugrasters
   4.190 -		LDA #&00 + PAL_red:STA &FE21
   4.191 -	ENDIF
   4.192 -
   4.193 -	\\ Add to rotation
   4.194 -
   4.195 -	CLC:LDA angle:ADC speed:STA angle
   4.196 -	LDA angle+1:ADC speed+1:STA angle+1
   4.197 -
   4.198 -	\\ Check keypresses
   4.199 -
   4.200 -	LDA #66:STA &FE4F:LDA &FE4F:BPL notx
   4.201 -	CLC:LDA speed:ADC #16:STA speed:BCC notx:INC speed+1:.notx
   4.202 -	LDA #97:STA &FE4F:LDA &FE4F:BPL notz
   4.203 -	SEC:LDA speed:SBC #16:STA speed:BCS notz:DEC speed+1:.notz
   4.204 -	LDA #112:STA &FE4F:LDA &FE4F:BMI exit
   4.205 -
   4.206 -	JMP mainloop
   4.207 -
   4.208 -	\\ Exit - in the least graceful way possible :)
   4.209 -
   4.210 -.exit
   4.211 -	JMP (&FFFC)
   4.212 -
   4.213 -
   4.214 -
   4.215 -\ ******************************************************************
   4.216 -\ *	IRQ handler
   4.217 -\ ******************************************************************
   4.218 -
   4.219 -.irq
   4.220 -	LDA &FE4D:AND #2:BNE irqvsync
   4.221 -.irqtimer
   4.222 -	LDA #&40:STA &FE4D:INC vsync
   4.223 -	IF debugrasters
   4.224 -		LDA #&00 + PAL_blue:STA &FE21
   4.225 -	ENDIF
   4.226 -	LDA &FC
   4.227 -	RTI
   4.228 -.irqvsync
   4.229 -	STA &FE4D
   4.230 -	LDA #LO(timerlength):STA &FE44
   4.231 -	LDA #HI(timerlength):STA &FE45
   4.232 -	IF debugrasters
   4.233 -		LDA #&00 + PAL_black:STA &FE21
   4.234 -	ENDIF
   4.235 -	LDA &FC
   4.236 -	RTI
   4.237 -
   4.238 -
   4.239 -
   4.240 -\ ******************************************************************
   4.241 -\ *	Colour table used by the plot code
   4.242 -\ ******************************************************************
   4.243 -
   4.244 -.colours
   4.245 -	EQUB &00, &00		; black pixels
   4.246 -	EQUB &02, &01		; blue pixels
   4.247 -	EQUB &08, &04		; red pixels
   4.248 -	EQUB &0A, &05		; magenta pixels
   4.249 -	EQUB &20, &10		; green pixels
   4.250 -	EQUB &22, &11		; cyan pixels
   4.251 -	EQUB &28, &14		; yellow pixels
   4.252 -	EQUB &2A, &15		; white pixels
   4.253 -
   4.254 -
   4.255 -\ ******************************************************************
   4.256 -\ *	sin table
   4.257 -\ ******************************************************************
   4.258 -
   4.259 -; contains ABS sine values
   4.260 -; we don't store the sign as it confuses the multiplication.
   4.261 -; we can tell the sign very easily from whether the index is >128
   4.262 -
   4.263 -ALIGN &100	; so we don't incur page-crossed penalties
   4.264 -.sintable
   4.265 -FOR n, 0, 255
   4.266 -	EQUB ABS(SIN(n/128*PI)) * 255
   4.267 -NEXT
   4.268 -
   4.269 -
   4.270 -\ ******************************************************************
   4.271 -\ *	colour table
   4.272 -\ ******************************************************************
   4.273 -
   4.274 -ALIGN &100
   4.275 -.coltable
   4.276 -FOR n, 0, 255
   4.277 -	EQUB (SIN(n/128*PI) + 1) / 2.0001 * 7 + 1
   4.278 -NEXT
   4.279 -
   4.280 -
   4.281 -\ ******************************************************************
   4.282 -\ *	multiplication tables
   4.283 -\ ******************************************************************
   4.284 -
   4.285 -; This is a very quick way to do multiplies, based on the fact that:
   4.286 -;
   4.287 -;							(a+b)^2  =  a^2 + b^2 + 2ab    (I)
   4.288 -;							(a-b)^2  =  a^2 + b^2 - 2ab    (II)
   4.289 -;
   4.290 -; (I) minus (II) yields:	(a+b)^2 - (a-b)^2 = 4ab
   4.291 -;
   4.292 -; 		  or, rewritten:	ab = f(a+b) - f(a-b),
   4.293 -;											where f(x) = x^2 / 4
   4.294 -;
   4.295 -; We build a table of f(x) here with x=0..511, and then can perform
   4.296 -; 8-bit * 8-bit by 4 table lookups and a 16-bit subtract.
   4.297 -;
   4.298 -; In this case, we will discard the low byte of the result, so we
   4.299 -; only need the high bytes, and can do just 2 table lookups and a
   4.300 -; simple 8-bit subtract.
   4.301 -
   4.302 -ALIGN &100
   4.303 -.multtab1
   4.304 -FOR n, 0, 255
   4.305 -	EQUB HI(n*n DIV 4)
   4.306 -NEXT
   4.307 -.multtab2
   4.308 -FOR n, 256, 511
   4.309 -	EQUB HI(n*n DIV 4)
   4.310 -NEXT
   4.311 -
   4.312 -
   4.313 -\ ******************************************************************
   4.314 -\ *	dot tables
   4.315 -\ ******************************************************************
   4.316 -
   4.317 -; contains the phase of this dot
   4.318 -
   4.319 -ALIGN &100
   4.320 -.dotx
   4.321 -FOR n, 0, numdots-1
   4.322 -	EQUB RND(256)
   4.323 -NEXT
   4.324 -
   4.325 -
   4.326 -; contains the y position of the dot
   4.327 -; the dots are sorted by y positions, highest on screen first - this means we can do
   4.328 -; 'raster chasing'!
   4.329 -; the y positions are also biased so there are fewer at the poles, and more at the equator!
   4.330 -
   4.331 -ALIGN &100
   4.332 -.doty
   4.333 -FOR n, 0, numdots-1
   4.334 -	x = (n - numdots/2 + 0.5) / (numdots/2)
   4.335 -	y = (x - SIN(x*PI) * 0.1) * radius
   4.336 -	EQUB 128 + y
   4.337 -NEXT
   4.338 -
   4.339 -
   4.340 -; contains the radius of the ball at this y position
   4.341 -
   4.342 -ALIGN &100
   4.343 -.dotr
   4.344 -FOR n, 0, numdots-1
   4.345 -	x = (n - numdots/2 + 0.5) / (numdots/2)
   4.346 -	y = (x - SIN(x*PI) * 0.1) * radius
   4.347 -	r = SQR(radius*radius - y*y) / 2
   4.348 -	EQUB r
   4.349 -NEXT
   4.350 -
   4.351 -
   4.352 -\ ******************************************************************
   4.353 -\ *	This is the end of the main native block of code
   4.354 -\ ******************************************************************
   4.355 -.END
   4.356 -
   4.357 -
   4.358 -\ ******************************************************************
   4.359 -\ *	The entry point of the demo
   4.360 -\ * This relocates the code to its 'real' address, and can also
   4.361 -\ * do one-time initialisation, i.e. code we can chuck away afterwards.
   4.362 -\ *
   4.363 -\ * Since this is the relocation code, it has to go at the very end of
   4.364 -\ * the executable.
   4.365 -\
   4.366 -\ * This code will be running at its assemble address + OFFSET,
   4.367 -\ * so we have to patch up any absolute address references accordingly.
   4.368 -\ ******************************************************************
   4.369 -
   4.370 -ALIGN &100
   4.371 -.RELOC_START
   4.372 -
   4.373 -	\\ Set up hardware state and interrupts
   4.374 -
   4.375 -	SEI
   4.376 -	LDX #&FF:TXS				; reset stack
   4.377 -	STX &FE44:STX &FE45
   4.378 -	LDA #&7F:STA &FE4E			; disable all interrupts
   4.379 -	STA &FE43					; set keyboard data direction
   4.380 -	LDA #&C2:STA &FE4E			; enable VSync and timer interrupt
   4.381 -	LDA #&0F:STA &FE42			; set addressable latch for writing
   4.382 -	LDA #3:STA &FE40			; keyboard write enable
   4.383 -	LDA #0:STA &FE4B			; timer 1 one shot mode
   4.384 -	LDA #LO(irq):STA &204
   4.385 -	LDA #HI(irq):STA &205		; set interrupt handler
   4.386 -
   4.387 -	\\ Set up CRTC for MODE 2
   4.388 -
   4.389 -	LDX #13
   4.390 -.crtcloop
   4.391 -	STX &FE00
   4.392 -	LDA crtcregs + OFFSET,X		; PATCHED ADDRESS
   4.393 -	STA &FE01
   4.394 -	DEX
   4.395 -	BPL crtcloop
   4.396 -
   4.397 -	\\ Set up video ULA for MODE 2
   4.398 -
   4.399 -	LDA #&F4
   4.400 -	STA &FE20
   4.401 -
   4.402 -	\\ Set up palette for MODE 2
   4.403 -
   4.404 -	LDX #15
   4.405 -.palloop
   4.406 -	LDA paldata + OFFSET,X		; PATCHED ADDRESS
   4.407 -	STA &FE21
   4.408 -	ORA #&80
   4.409 -	STA &FE21
   4.410 -	DEX
   4.411 -	BPL palloop
   4.412 -
   4.413 -	\\ Initialise vars
   4.414 -
   4.415 -	LDA #0:STA angle:STA angle+1
   4.416 -	STA vsync
   4.417 -	STA speed
   4.418 -	LDA #1:STA speed+1
   4.419 -	
   4.420 -	\\ Relocate
   4.421 -	
   4.422 -	LDX #HI(RELOC_START-START)
   4.423 -	LDY #0
   4.424 -	.relocloop
   4.425 -	LDA RELOAD_ADDR,Y
   4.426 -	STA NATIVE_ADDR,Y
   4.427 -	INY
   4.428 -	BNE relocloop
   4.429 -	INC relocloop+OFFSET+2		; PATCHED ADDRESS
   4.430 -	INC relocloop+OFFSET+5		; PATCHED ADDRESS
   4.431 -	DEX
   4.432 -	BNE relocloop
   4.433 -	
   4.434 -	JMP START
   4.435 -
   4.436 -
   4.437 -\ ******************************************************************
   4.438 -\ *	Values of CRTC regs for MODE 2
   4.439 -\ ******************************************************************
   4.440 -
   4.441 -.crtcregs
   4.442 -	EQUB 127			; R0  horizontal total
   4.443 -	EQUB 64				; R1  horizontal displayed - shrunk a little
   4.444 -	EQUB 91				; R2  horizontal position
   4.445 -	EQUB 40				; R3  sync width
   4.446 -	EQUB 38				; R4  vertical total
   4.447 -	EQUB 0				; R5  vertical total adjust
   4.448 -	EQUB 0				; R6  vertical displayed
   4.449 -	EQUB 34				; R7  vertical position
   4.450 -	EQUB 0				; R8  interlace
   4.451 -	EQUB 7				; R9  scanlines per row
   4.452 -	EQUB 32				; R10 cursor start
   4.453 -	EQUB 8				; R11 cursor end
   4.454 -	EQUB HI(&4000/8)	; R12 screen start address, high
   4.455 -	EQUB LO(&4000/8)	; R13 screen start address, low
   4.456 -
   4.457 -
   4.458 -\ ******************************************************************
   4.459 -\ *	Values of palette regs for MODE 2
   4.460 -\ ******************************************************************
   4.461 -
   4.462 -PAL_black	= (0 EOR 7)
   4.463 -PAL_blue	= (4 EOR 7)
   4.464 -PAL_red		= (1 EOR 7)
   4.465 -PAL_magenta = (5 EOR 7)
   4.466 -PAL_green	= (2 EOR 7)
   4.467 -PAL_cyan	= (6 EOR 7)
   4.468 -PAL_yellow	= (3 EOR 7)
   4.469 -PAL_white	= (7 EOR 7)
   4.470 -
   4.471 -.paldata
   4.472 -	EQUB &00 + PAL_black
   4.473 -	EQUB &10 + PAL_blue
   4.474 -	EQUB &20 + PAL_red
   4.475 -	EQUB &30 + PAL_magenta
   4.476 -	EQUB &40 + PAL_green
   4.477 -	EQUB &50 + PAL_cyan
   4.478 -	EQUB &60 + PAL_yellow
   4.479 -	EQUB &70 + PAL_white
   4.480 -
   4.481 -
   4.482 -
   4.483 -
   4.484 -\ ******************************************************************
   4.485 -\ *	End address to be saved
   4.486 -\ ******************************************************************
   4.487 -.RELOC_END
   4.488 -
   4.489 -
   4.490 -\ ******************************************************************
   4.491 -\ *	Save the code, before the following data overlay clears it again
   4.492 -\ ******************************************************************
   4.493 -
   4.494 -SAVE "Code", START, RELOC_END, RELOC_START+OFFSET, RELOAD_ADDR
   4.495 -
   4.496 -
   4.497 -
   4.498 -
   4.499 -\ ******************************************************************
   4.500 -\ * Start a new overlay:
   4.501 -\ *
   4.502 -\ * This is overlapped with the relocation code above, because it
   4.503 -\ * will already have been thrown away by the time these tables are
   4.504 -\ * used.
   4.505 -\ *
   4.506 -\ * These tables are filled at run-time, hence we just define their
   4.507 -\ * addresses, we don't need to save anything.
   4.508 -\ ******************************************************************
   4.509 -
   4.510 -CLEAR END, RELOC_END
   4.511 -ORG END
   4.512 -
   4.513 -; these store the screen address of the last dot
   4.514 -; at the end of the frame, we go through these tables, storing zeroes to
   4.515 -; all these addresses in order to delete the last frame
   4.516 -
   4.517 -ALIGN &100
   4.518 -.olddotaddrlo	SKIP numdots
   4.519 -
   4.520 -ALIGN &100
   4.521 -.olddotaddrhi	SKIP numdots
   4.522 -
   4.523 -ALIGN &100
   4.524 -.olddotaddry	SKIP numdots
   4.525 -
     5.1 --- a/src/Makefile	Fri Mar 04 02:01:51 2011 +0000
     5.2 +++ b/src/Makefile	Sun Mar 06 18:19:27 2011 +0100
     5.3 @@ -51,7 +51,8 @@
     5.4  
     5.5  # Parameters to the executable
     5.6  
     5.7 -PARAMS			:=		-i ../demo.asm -do ../demo.ssd -boot Code
     5.8 +#PARAMS			:=		-i ../demo.6502 -do ../demo.ssd -boot Code
     5.9 +PARAMS			:=		-i ../test.6502 -v
    5.10  
    5.11  
    5.12  
     6.1 --- a/src/Makefile.inc	Fri Mar 04 02:01:51 2011 +0000
     6.2 +++ b/src/Makefile.inc	Sun Mar 06 18:19:27 2011 +0100
     6.3 @@ -192,7 +192,7 @@
     6.4  code: deps objs $(TARGET)
     6.5  
     6.6  run:
     6.7 -	$(ECHO) Running ... $(TARGET)\n
     6.8 +	$(ECHO) Running ... $(TARGET)
     6.9  	$(VB)$(TARGET) $(PARAMS)
    6.10  
    6.11  
     7.1 --- a/src/VS2010/BeebAsm.vcxproj	Fri Mar 04 02:01:51 2011 +0000
     7.2 +++ b/src/VS2010/BeebAsm.vcxproj	Sun Mar 06 18:19:27 2011 +0100
     7.3 @@ -82,6 +82,7 @@
     7.4      <ClCompile Include="..\expression.cpp" />
     7.5      <ClCompile Include="..\globaldata.cpp" />
     7.6      <ClCompile Include="..\lineparser.cpp" />
     7.7 +    <ClCompile Include="..\macro.cpp" />
     7.8      <ClCompile Include="..\main.cpp" />
     7.9      <ClCompile Include="..\objectcode.cpp" />
    7.10      <ClCompile Include="..\sourcefile.cpp" />
    7.11 @@ -94,12 +95,12 @@
    7.12      <ClInclude Include="..\discimage.h" />
    7.13      <ClInclude Include="..\globaldata.h" />
    7.14      <ClInclude Include="..\lineparser.h" />
    7.15 +    <ClInclude Include="..\macro.h" />
    7.16      <ClInclude Include="..\main.h" />
    7.17      <ClInclude Include="..\objectcode.h" />
    7.18      <ClInclude Include="..\sourcefile.h" />
    7.19      <ClInclude Include="..\stringutils.h" />
    7.20      <ClInclude Include="..\symboltable.h" />
    7.21 -    <ClInclude Include="..\tokens.h" />
    7.22    </ItemGroup>
    7.23    <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
    7.24    <ImportGroup Label="ExtensionTargets">
     8.1 --- a/src/VS2010/BeebAsm.vcxproj.filters	Fri Mar 04 02:01:51 2011 +0000
     8.2 +++ b/src/VS2010/BeebAsm.vcxproj.filters	Sun Mar 06 18:19:27 2011 +0100
     8.3 @@ -54,6 +54,9 @@
     8.4      <ClCompile Include="..\BASIC.cpp">
     8.5        <Filter>Source Files</Filter>
     8.6      </ClCompile>
     8.7 +    <ClCompile Include="..\macro.cpp">
     8.8 +      <Filter>Source Files</Filter>
     8.9 +    </ClCompile>
    8.10    </ItemGroup>
    8.11    <ItemGroup>
    8.12      <ClInclude Include="..\asmexception.h">
    8.13 @@ -83,10 +86,10 @@
    8.14      <ClInclude Include="..\symboltable.h">
    8.15        <Filter>Header Files</Filter>
    8.16      </ClInclude>
    8.17 -    <ClInclude Include="..\tokens.h">
    8.18 +    <ClInclude Include="..\BASIC.h">
    8.19        <Filter>Header Files</Filter>
    8.20      </ClInclude>
    8.21 -    <ClInclude Include="..\BASIC.h">
    8.22 +    <ClInclude Include="..\macro.h">
    8.23        <Filter>Header Files</Filter>
    8.24      </ClInclude>
    8.25    </ItemGroup>
     9.1 --- a/src/asmexception.h	Fri Mar 04 02:01:51 2011 +0000
     9.2 +++ b/src/asmexception.h	Sun Mar 06 18:19:27 2011 +0100
     9.3 @@ -199,8 +199,12 @@
     9.4  DEFINE_SYNTAX_EXCEPTION( NoIndexedX, "X indexed mode does not exist for this instruction." );
     9.5  DEFINE_SYNTAX_EXCEPTION( NoIndexedY, "Y indexed mode does not exist for this instruction." );
     9.6  DEFINE_SYNTAX_EXCEPTION( LabelAlreadyDefined, "Symbol already defined." );
     9.7 -DEFINE_SYNTAX_EXCEPTION( InvalidSymbolName, "Invalid symbol name; must start with a letter and contain only numbers and underscore." );
     9.8 +DEFINE_SYNTAX_EXCEPTION( InvalidSymbolName, "Invalid symbol name; must start with a letter and contain only letters, numbers and underscore." );
     9.9  DEFINE_SYNTAX_EXCEPTION( SecondPassProblem, "Fatal error: the second assembler pass has generated different code to the first." );
    9.10 +DEFINE_SYNTAX_EXCEPTION( InvalidMacroName, "Invalid macro name; must start with a letter and contain only letters, numbers and underscore." );
    9.11 +DEFINE_SYNTAX_EXCEPTION( NoNestedMacros, "Cannot define one macro inside another." );
    9.12 +DEFINE_SYNTAX_EXCEPTION( EndMacroUnexpected, "ENDMACRO encountered without a matching MACRO directive." );
    9.13 +DEFINE_SYNTAX_EXCEPTION( DuplicateMacroName, "Macro name already defined." );
    9.14  
    9.15  // meta-language parsing exceptions
    9.16  DEFINE_SYNTAX_EXCEPTION( NextWithoutFor, "NEXT without FOR." );
    10.1 --- a/src/commands.cpp	Fri Mar 04 02:01:51 2011 +0000
    10.2 +++ b/src/commands.cpp	Sun Mar 06 18:19:27 2011 +0100
    10.3 @@ -70,7 +70,9 @@
    10.4  	{ "}",			&LineParser::HandleCloseBrace,			0 },
    10.5  	{ "MAPCHAR",	&LineParser::HandleMapChar,				0 },
    10.6  	{ "PUTFILE",	&LineParser::HandlePutFile,				0 },
    10.7 -	{ "PUTBASIC",	&LineParser::HandlePutBasic,			0 }
    10.8 +	{ "PUTBASIC",	&LineParser::HandlePutBasic,			0 },
    10.9 +	{ "MACRO",		&LineParser::HandleMacro,				&SourceFile::StartMacro },
   10.10 +	{ "ENDMACRO",	&LineParser::HandleEndMacro,			&SourceFile::EndMacro }
   10.11  };
   10.12  
   10.13  
   10.14 @@ -568,7 +570,7 @@
   10.15  	{
   10.16  		string filename( m_line.substr( m_column + 1, endQuotePos - m_column - 1 ) );
   10.17  
   10.18 -		if ( GlobalData::Instance().IsFirstPass() )
   10.19 +		if ( GlobalData::Instance().ShouldOutputAsm() )
   10.20  		{
   10.21  			cerr << "Including file " << filename << endl;
   10.22  		}
   10.23 @@ -1049,10 +1051,10 @@
   10.24  
   10.25  	// expect no more
   10.26  
   10.27 -	if ( AdvanceAndCheckEndOfStatement() )
   10.28 +	if ( m_line[ m_column ] == ',' )
   10.29  	{
   10.30 -		// found something else - wrong!
   10.31 -		throw AsmException_SyntaxError_InvalidCharacter( m_line, m_column );
   10.32 +		// Unexpected comma (remembering that an expression can validly end with a comma)
   10.33 +		throw AsmException_SyntaxError_UnexpectedComma( m_line, m_column );
   10.34  	}
   10.35  
   10.36  	// OK - do it
   10.37 @@ -1535,8 +1537,6 @@
   10.38  		inputFile.read( buffer, fileSize );
   10.39  		inputFile.close();
   10.40  
   10.41 -		cout << "Read file " << hostFilename << ": size " << fileSize << " bytes" << ": " << hex << start << " " << exec << endl;
   10.42 -
   10.43  		if ( GlobalData::Instance().UsesDiscImage() )
   10.44  		{
   10.45  			// disc image version of the save
   10.46 @@ -1656,3 +1656,96 @@
   10.47  
   10.48  }
   10.49  
   10.50 +
   10.51 +/*************************************************************************************************/
   10.52 +/**
   10.53 +	LineParser::HandleMacro()
   10.54 +*/
   10.55 +/*************************************************************************************************/
   10.56 +void LineParser::HandleMacro()
   10.57 +{
   10.58 +	if ( !AdvanceAndCheckEndOfStatement() )
   10.59 +	{
   10.60 +		throw AsmException_SyntaxError_EmptyExpression( m_line, m_column );
   10.61 +	}
   10.62 +
   10.63 +	string macroName;
   10.64 +
   10.65 +	if ( isalpha( m_line[ m_column ] ) || m_line[ m_column ] == '_' )
   10.66 +	{
   10.67 +		macroName = GetSymbolName();
   10.68 +
   10.69 +		if ( GlobalData::Instance().IsFirstPass() )
   10.70 +		{
   10.71 +			if ( MacroTable::Instance().Exists( macroName ) )
   10.72 +			{
   10.73 +				throw AsmException_SyntaxError_DuplicateMacroName( m_line, m_column );
   10.74 +			}
   10.75 +
   10.76 +			m_sourceFile->GetCurrentMacro()->SetName( macroName );
   10.77 +			cout << "MACRO '" << macroName << "'" << endl;
   10.78 +		}
   10.79 +	}
   10.80 +	else
   10.81 +	{
   10.82 +		throw AsmException_SyntaxError_InvalidMacroName( m_line, m_column );
   10.83 +	}
   10.84 +
   10.85 +	bool bExpectComma = false;
   10.86 +	bool bHasParameters = false;
   10.87 +
   10.88 +	while ( AdvanceAndCheckEndOfStatement() )
   10.89 +	{
   10.90 +		if ( bExpectComma )
   10.91 +		{
   10.92 +			if ( m_line[ m_column ] == ',' )
   10.93 +			{
   10.94 +				m_column++;
   10.95 +				bExpectComma = false;
   10.96 +			}
   10.97 +			else
   10.98 +			{
   10.99 +				throw AsmException_SyntaxError_MissingComma( m_line, m_column );
  10.100 +			}
  10.101 +		}
  10.102 +		else if ( isalpha( m_line[ m_column ] ) || m_line[ m_column ] == '_' )
  10.103 +		{
  10.104 +			string param = GetSymbolName();
  10.105 +
  10.106 +			if ( GlobalData::Instance().IsFirstPass() )
  10.107 +			{
  10.108 +				m_sourceFile->GetCurrentMacro()->AddParameter( param );
  10.109 +				cout << "  param: '" << param << "'" << endl;
  10.110 +			}
  10.111 +			bExpectComma = true;
  10.112 +			bHasParameters = true;
  10.113 +		}
  10.114 +		else
  10.115 +		{
  10.116 +			throw AsmException_SyntaxError_InvalidSymbolName( m_line, m_column );
  10.117 +		}
  10.118 +	}
  10.119 +
  10.120 +	if ( bHasParameters && !bExpectComma )
  10.121 +	{
  10.122 +		throw AsmException_SyntaxError_UnexpectedComma( m_line, m_column - 1 );
  10.123 +	}
  10.124 +
  10.125 +	m_sourceFile->SetCurrentIfCondition(false);
  10.126 +}
  10.127 +
  10.128 +
  10.129 +/*************************************************************************************************/
  10.130 +/**
  10.131 +	LineParser::HandleEndMacro()
  10.132 +*/
  10.133 +/*************************************************************************************************/
  10.134 +void LineParser::HandleEndMacro()
  10.135 +{
  10.136 +	if ( AdvanceAndCheckEndOfStatement() )
  10.137 +	{
  10.138 +		// found something
  10.139 +		throw AsmException_SyntaxError_InvalidCharacter( m_line, m_column );
  10.140 +	}
  10.141 +}
  10.142 +
    11.1 --- a/src/lineparser.cpp	Fri Mar 04 02:01:51 2011 +0000
    11.2 +++ b/src/lineparser.cpp	Sun Mar 06 18:19:27 2011 +0100
    11.3 @@ -22,6 +22,7 @@
    11.4  */
    11.5  /*************************************************************************************************/
    11.6  
    11.7 +#include <iostream>
    11.8  #include "lineparser.h"
    11.9  #include "asmexception.h"
   11.10  #include "stringutils.h"
   11.11 @@ -130,6 +131,7 @@
   11.12  		{
   11.13  			m_column = oldColumn;
   11.14  			SkipStatement();
   11.15 +
   11.16  			continue;
   11.17  		}
   11.18  
   11.19 @@ -210,35 +212,49 @@
   11.20  	bool bInQuotes = false;
   11.21  	bool bInSingleQuotes = false;
   11.22  
   11.23 -	while ( m_column < m_line.length() && ( bInQuotes || bInSingleQuotes || MoveToNextAtom( ":;\\" ) ) )
   11.24 +	int oldColumn = m_column;
   11.25 +
   11.26 +	if ( m_line[ m_column ] == '{' || m_line[ m_column ] == '}' || m_line[ m_column ] == ':' )
   11.27  	{
   11.28 -		if ( m_line[ m_column ] == '\"' && !bInSingleQuotes )
   11.29 -		{
   11.30 -			bInQuotes = !bInQuotes;
   11.31 -		}
   11.32 -		else if ( m_line[ m_column ] == '\'' )
   11.33 -		{
   11.34 -			if ( bInSingleQuotes )
   11.35 -			{
   11.36 -				bInSingleQuotes = false;
   11.37 -			}
   11.38 -			else if ( m_line[ m_column + 2 ] == '\'' && !bInQuotes )
   11.39 -			{
   11.40 -				bInSingleQuotes = true;
   11.41 -				m_column++;
   11.42 -			}
   11.43 -		}
   11.44 -
   11.45  		m_column++;
   11.46  	}
   11.47 -
   11.48 -	if ( m_line[ m_column ] == '\\' || m_line[ m_column ] == ';' )
   11.49 +	else if ( m_line[ m_column ] == '\\' || m_line[ m_column ] == ';' )
   11.50  	{
   11.51  		m_column = m_line.length();
   11.52  	}
   11.53 -	else if ( m_line[ m_column ] == ':' )
   11.54 +	else
   11.55  	{
   11.56 -		m_column++;
   11.57 +		while ( m_column < m_line.length() && ( bInQuotes || bInSingleQuotes || MoveToNextAtom( ":;\\{}" ) ) )
   11.58 +		{
   11.59 +			if ( m_line[ m_column ] == '\"' && !bInSingleQuotes )
   11.60 +			{
   11.61 +				bInQuotes = !bInQuotes;
   11.62 +			}
   11.63 +			else if ( m_line[ m_column ] == '\'' )
   11.64 +			{
   11.65 +				if ( bInSingleQuotes )
   11.66 +				{
   11.67 +					bInSingleQuotes = false;
   11.68 +				}
   11.69 +				else if ( m_line[ m_column + 2 ] == '\'' && !bInQuotes )
   11.70 +				{
   11.71 +					bInSingleQuotes = true;
   11.72 +					m_column++;
   11.73 +				}
   11.74 +			}
   11.75 +
   11.76 +			m_column++;
   11.77 +		}
   11.78 +	}
   11.79 +
   11.80 +	if ( m_sourceFile->GetCurrentMacro() != NULL &&
   11.81 +		 m_line[ oldColumn ] != ':' &&
   11.82 +		 m_line[ oldColumn ] != '\\' &&
   11.83 +		 m_line[ oldColumn ] != ';' )
   11.84 +	{
   11.85 +		string command = m_line.substr( oldColumn, m_column - oldColumn );
   11.86 +		m_sourceFile->GetCurrentMacro()->AddLine( command );
   11.87 +		cout << "   '" << command << "'" << endl;
   11.88  	}
   11.89  }
   11.90  
   11.91 @@ -378,7 +394,7 @@
   11.92  /*************************************************************************************************/
   11.93  bool LineParser::AdvanceAndCheckEndOfStatement()
   11.94  {
   11.95 -	return MoveToNextAtom( ";:\\" );
   11.96 +	return MoveToNextAtom( ";:\\{}" );
   11.97  }
   11.98  
   11.99  
  11.100 @@ -398,7 +414,7 @@
  11.101  /*************************************************************************************************/
  11.102  bool LineParser::AdvanceAndCheckEndOfSubStatement()
  11.103  {
  11.104 -	return MoveToNextAtom( ";:\\," );
  11.105 +	return MoveToNextAtom( ";:\\,{}" );
  11.106  }
  11.107  
  11.108  
    12.1 --- a/src/lineparser.h	Fri Mar 04 02:01:51 2011 +0000
    12.2 +++ b/src/lineparser.h	Sun Mar 06 18:19:27 2011 +0100
    12.3 @@ -46,7 +46,7 @@
    12.4  private:
    12.5  
    12.6  	typedef void ( LineParser::*TokenHandler )();
    12.7 -	typedef void ( SourceFile::*DirectiveHandler )( std::string line, int column );
    12.8 +	typedef void ( SourceFile::*DirectiveHandler )( const std::string& line, int column );
    12.9  
   12.10  	struct Token
   12.11  	{
   12.12 @@ -150,6 +150,8 @@
   12.13  	void			HandleMapChar();
   12.14  	void			HandlePutFile();
   12.15  	void			HandlePutBasic();
   12.16 +	void			HandleMacro();
   12.17 +	void			HandleEndMacro();
   12.18  
   12.19  	// expression evaluating methods
   12.20  
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/src/macro.cpp	Sun Mar 06 18:19:27 2011 +0100
    13.3 @@ -0,0 +1,143 @@
    13.4 +/*************************************************************************************************/
    13.5 +/**
    13.6 +	macro.cpp
    13.7 +
    13.8 +
    13.9 +	Copyright (C) Rich Talbot-Watkins 2007, 2008
   13.10 +
   13.11 +	This file is part of BeebAsm.
   13.12 +
   13.13 +	BeebAsm is free software: you can redistribute it and/or modify it under the terms of the GNU
   13.14 +	General Public License as published by the Free Software Foundation, either version 3 of the
   13.15 +	License, or (at your option) any later version.
   13.16 +
   13.17 +	BeebAsm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
   13.18 +	even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13.19 +	GNU General Public License for more details.
   13.20 +
   13.21 +	You should have received a copy of the GNU General Public License along with BeebAsm, as
   13.22 +	COPYING.txt.  If not, see <http://www.gnu.org/licenses/>.
   13.23 +*/
   13.24 +/*************************************************************************************************/
   13.25 +
   13.26 +#include <cmath>
   13.27 +#include <iostream>
   13.28 +
   13.29 +#include "macro.h"
   13.30 +
   13.31 +
   13.32 +using namespace std;
   13.33 +
   13.34 +
   13.35 +MacroTable* MacroTable::m_gInstance = NULL;
   13.36 +
   13.37 +
   13.38 +/*************************************************************************************************/
   13.39 +/**
   13.40 +	MacroTable::Create()
   13.41 +
   13.42 +	Creates the MacroTable singleton
   13.43 +*/
   13.44 +/*************************************************************************************************/
   13.45 +void MacroTable::Create()
   13.46 +{
   13.47 +	assert( m_gInstance == NULL );
   13.48 +
   13.49 +	m_gInstance = new MacroTable;
   13.50 +}
   13.51 +
   13.52 +
   13.53 +
   13.54 +/*************************************************************************************************/
   13.55 +/**
   13.56 +	MacroTable::Destroy()
   13.57 +
   13.58 +	Destroys the MacroTable singleton
   13.59 +*/
   13.60 +/*************************************************************************************************/
   13.61 +void MacroTable::Destroy()
   13.62 +{
   13.63 +	assert( m_gInstance != NULL );
   13.64 +
   13.65 +	delete m_gInstance;
   13.66 +	m_gInstance = NULL;
   13.67 +}
   13.68 +
   13.69 +
   13.70 +
   13.71 +/*************************************************************************************************/
   13.72 +/**
   13.73 +	MacroTable::MacroTable()
   13.74 +
   13.75 +	MacroTable constructor
   13.76 +*/
   13.77 +/*************************************************************************************************/
   13.78 +MacroTable::MacroTable()
   13.79 +{
   13.80 +}
   13.81 +
   13.82 +
   13.83 +
   13.84 +/*************************************************************************************************/
   13.85 +/**
   13.86 +	MacroTable::~MacroTable()
   13.87 +
   13.88 +	MacroTable destructor
   13.89 +*/
   13.90 +/*************************************************************************************************/
   13.91 +MacroTable::~MacroTable()
   13.92 +{
   13.93 +	for ( map< std::string, Macro* >::iterator it = m_map.begin(); it != m_map.end(); ++it )
   13.94 +	{
   13.95 +		delete it->second;
   13.96 +	}
   13.97 +}
   13.98 +
   13.99 +
  13.100 +/*************************************************************************************************/
  13.101 +/**
  13.102 +	MacroTable::Add()
  13.103 +
  13.104 +	Adds a new macro to the table
  13.105 +*/
  13.106 +/*************************************************************************************************/
  13.107 +void MacroTable::Add( Macro* macro )
  13.108 +{
  13.109 +	if ( macro != NULL )
  13.110 +	{
  13.111 +		m_map.insert( make_pair( macro->GetName(), macro ) );
  13.112 +	}
  13.113 +}
  13.114 +
  13.115 +
  13.116 +/*************************************************************************************************/
  13.117 +/**
  13.118 +	MacroTable::Exists()
  13.119 +
  13.120 +	Returns whether or not the named macro yet exists
  13.121 +*/
  13.122 +/*************************************************************************************************/
  13.123 +bool MacroTable::Exists( const string& name ) const
  13.124 +{
  13.125 +	return ( m_map.count( name ) > 0 );
  13.126 +}
  13.127 +
  13.128 +
  13.129 +/*************************************************************************************************/
  13.130 +/**
  13.131 +	MacroTable::Get()
  13.132 +
  13.133 +	Returns a reference to the named macro
  13.134 +*/
  13.135 +/*************************************************************************************************/
  13.136 +const Macro* MacroTable::Get( const string& name ) const
  13.137 +{
  13.138 +	if ( Exists( name ) )
  13.139 +	{
  13.140 +		return m_map.find( name )->second;
  13.141 +	}
  13.142 +	else
  13.143 +	{
  13.144 +		return NULL;
  13.145 +	}
  13.146 +}
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/src/macro.h	Sun Mar 06 18:19:27 2011 +0100
    14.3 @@ -0,0 +1,101 @@
    14.4 +/*************************************************************************************************/
    14.5 +/**
    14.6 +	macro.h
    14.7 +
    14.8 +
    14.9 +	Copyright (C) Rich Talbot-Watkins 2007 - 2011
   14.10 +
   14.11 +	This file is part of BeebAsm.
   14.12 +
   14.13 +	BeebAsm is free software: you can redistribute it and/or modify it under the terms of the GNU
   14.14 +	General Public License as published by the Free Software Foundation, either version 3 of the
   14.15 +	License, or (at your option) any later version.
   14.16 +
   14.17 +	BeebAsm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
   14.18 +	even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14.19 +	GNU General Public License for more details.
   14.20 +
   14.21 +	You should have received a copy of the GNU General Public License along with BeebAsm, as
   14.22 +	COPYING.txt.  If not, see <http://www.gnu.org/licenses/>.
   14.23 +*/
   14.24 +/*************************************************************************************************/
   14.25 +
   14.26 +#ifndef MACRO_H_
   14.27 +#define MACRO_H_
   14.28 +
   14.29 +#include <cassert>
   14.30 +#include <cstdlib>
   14.31 +#include <map>
   14.32 +#include <string>
   14.33 +#include <vector>
   14.34 +
   14.35 +
   14.36 +class Macro
   14.37 +{
   14.38 +public:
   14.39 +
   14.40 +	void SetName( const std::string& name )
   14.41 +	{
   14.42 +		m_name = name;
   14.43 +	}
   14.44 +
   14.45 +	void AddParameter( const std::string& param )
   14.46 +	{
   14.47 +		m_parameters.push_back( param );
   14.48 +	}
   14.49 +
   14.50 +	void AddLine( const std::string& line )
   14.51 +	{
   14.52 +		m_content.push_back( line );
   14.53 +	}
   14.54 +
   14.55 +	const std::string& GetName() const
   14.56 +	{
   14.57 +		return m_name;
   14.58 +	}
   14.59 +
   14.60 +	int GetNumberOfParameters() const
   14.61 +	{
   14.62 +		return m_parameters.size();
   14.63 +	}
   14.64 +
   14.65 +	const std::string& GetParameter( int i ) const
   14.66 +	{
   14.67 +		return m_parameters[ i ];
   14.68 +	}
   14.69 +
   14.70 +
   14.71 +private:
   14.72 +
   14.73 +	std::string						m_name;
   14.74 +	std::vector< std::string >		m_parameters;
   14.75 +	std::vector< std::string >		m_content;
   14.76 +};
   14.77 +
   14.78 +
   14.79 +
   14.80 +class MacroTable
   14.81 +{
   14.82 +public:
   14.83 +
   14.84 +	static void Create();
   14.85 +	static void Destroy();
   14.86 +	static inline MacroTable& Instance() { assert( m_gInstance != NULL ); return *m_gInstance; }
   14.87 +
   14.88 +	void Add( Macro* macro );
   14.89 +	bool Exists( const std::string& name ) const;
   14.90 +	const Macro* Get( const std::string& name ) const;
   14.91 +
   14.92 +private:
   14.93 +
   14.94 +	MacroTable();
   14.95 +	~MacroTable();
   14.96 +
   14.97 +	std::map< std::string, Macro* >	m_map;
   14.98 +
   14.99 +	static MacroTable*				m_gInstance;
  14.100 +};
  14.101 +
  14.102 +
  14.103 +
  14.104 +#endif // MACRO_H_
    15.1 --- a/src/main.cpp	Fri Mar 04 02:01:51 2011 +0000
    15.2 +++ b/src/main.cpp	Sun Mar 06 18:19:27 2011 +0100
    15.3 @@ -35,6 +35,7 @@
    15.4  #include "symboltable.h"
    15.5  #include "discimage.h"
    15.6  #include "BASIC.h"
    15.7 +#include "macro.h"
    15.8  
    15.9  
   15.10  using namespace std;
   15.11 @@ -201,6 +202,7 @@
   15.12  
   15.13  	SymbolTable::Create();
   15.14  	ObjectCode::Create();
   15.15 +	MacroTable::Create();
   15.16  	SetupBASICTables();
   15.17  
   15.18  	time_t randomSeed = time( NULL );
   15.19 @@ -244,6 +246,7 @@
   15.20  		cerr << "warning: no SAVE command in source file." << endl;
   15.21  	}
   15.22  
   15.23 +	MacroTable::Destroy();
   15.24  	ObjectCode::Destroy();
   15.25  	SymbolTable::Destroy();
   15.26  	GlobalData::Destroy();
    16.1 --- a/src/sourcefile.cpp	Fri Mar 04 02:01:51 2011 +0000
    16.2 +++ b/src/sourcefile.cpp	Sun Mar 06 18:19:27 2011 +0100
    16.3 @@ -50,7 +50,8 @@
    16.4  */
    16.5  /*************************************************************************************************/
    16.6  SourceFile::SourceFile( const char* pFilename )
    16.7 -	:	m_pFilename( pFilename ),
    16.8 +	:	m_currentMacro( NULL ),
    16.9 +		m_pFilename( pFilename ),
   16.10  		m_lineNumber( 1 ),
   16.11  		m_filePointer( 0 )
   16.12  {
   16.13 @@ -190,12 +191,12 @@
   16.14  	SourceFile::AddFor()
   16.15  */
   16.16  /*************************************************************************************************/
   16.17 -void SourceFile::AddFor( string varName,
   16.18 +void SourceFile::AddFor( const string& varName,
   16.19  						 double start,
   16.20  						 double end,
   16.21  						 double step,
   16.22  						 int filePtr,
   16.23 -						 string line,
   16.24 +						 const string& line,
   16.25  						 int column )
   16.26  {
   16.27  	if ( m_forStackPtr == MAX_FOR_LEVELS )
   16.28 @@ -232,7 +233,7 @@
   16.29  	Braces for scoping variables are just FORs in disguise...
   16.30  */
   16.31  /*************************************************************************************************/
   16.32 -void SourceFile::OpenBrace( string line, int column )
   16.33 +void SourceFile::OpenBrace( const string& line, int column )
   16.34  {
   16.35  	if ( m_forStackPtr == MAX_FOR_LEVELS )
   16.36  	{
   16.37 @@ -262,7 +263,7 @@
   16.38  	SourceFile::UpdateFor()
   16.39  */
   16.40  /*************************************************************************************************/
   16.41 -void SourceFile::UpdateFor( string line, int column )
   16.42 +void SourceFile::UpdateFor( const string& line, int column )
   16.43  {
   16.44  	if ( m_forStackPtr == 0 )
   16.45  	{
   16.46 @@ -306,7 +307,7 @@
   16.47  	Braces for scoping variables are just FORs in disguise...
   16.48  */
   16.49  /*************************************************************************************************/
   16.50 -void SourceFile::CloseBrace( string line, int column )
   16.51 +void SourceFile::CloseBrace( const string& line, int column )
   16.52  {
   16.53  	if ( m_forStackPtr == 0 )
   16.54  	{
   16.55 @@ -379,7 +380,7 @@
   16.56  	SourceFile::AddIfLevel()
   16.57  */
   16.58  /*************************************************************************************************/
   16.59 -void SourceFile::AddIfLevel( string line, int column )
   16.60 +void SourceFile::AddIfLevel( const string& line, int column )
   16.61  {
   16.62  	if ( m_ifStackPtr == MAX_IF_LEVELS )
   16.63  	{
   16.64 @@ -419,7 +420,7 @@
   16.65  	SourceFile::StartElse()
   16.66  */
   16.67  /*************************************************************************************************/
   16.68 -void SourceFile::StartElse( string line, int column )
   16.69 +void SourceFile::StartElse( const string& line, int column )
   16.70  {
   16.71  	if ( m_ifStack[ m_ifStackPtr - 1 ].m_hadElse )
   16.72  	{
   16.73 @@ -438,7 +439,7 @@
   16.74  	SourceFile::StartElif()
   16.75  */
   16.76  /*************************************************************************************************/
   16.77 -void SourceFile::StartElif( string line, int column )
   16.78 +void SourceFile::StartElif( const string& line, int column )
   16.79  {
   16.80  	if ( m_ifStack[ m_ifStackPtr - 1 ].m_hadElse )
   16.81  	{
   16.82 @@ -455,7 +456,7 @@
   16.83  	SourceFile::RemoveIfLevel()
   16.84  */
   16.85  /*************************************************************************************************/
   16.86 -void SourceFile::RemoveIfLevel( string line, int column )
   16.87 +void SourceFile::RemoveIfLevel( const string& line, int column )
   16.88  {
   16.89  	if ( m_ifStackPtr == 0 )
   16.90  	{
   16.91 @@ -464,3 +465,51 @@
   16.92  
   16.93  	m_ifStackPtr--;
   16.94  }
   16.95 +
   16.96 +
   16.97 +
   16.98 +/*************************************************************************************************/
   16.99 +/**
  16.100 +	SourceFile::StartMacro()
  16.101 +*/
  16.102 +/*************************************************************************************************/
  16.103 +void SourceFile::StartMacro( const string& line, int column )
  16.104 +{
  16.105 +	if ( GlobalData::Instance().IsFirstPass() )
  16.106 +	{
  16.107 +		if ( m_currentMacro == NULL )
  16.108 +		{
  16.109 +			m_currentMacro = new Macro();
  16.110 +		}
  16.111 +		else
  16.112 +		{
  16.113 +			throw AsmException_SyntaxError_NoNestedMacros( line, column );
  16.114 +		}
  16.115 +	}
  16.116 +
  16.117 +	AddIfLevel( line, column );
  16.118 +}
  16.119 +
  16.120 +
  16.121 +
  16.122 +/*************************************************************************************************/
  16.123 +/**
  16.124 +	SourceFile::EndMacro()
  16.125 +*/
  16.126 +/*************************************************************************************************/
  16.127 +void SourceFile::EndMacro( const string& line, int column )
  16.128 +{
  16.129 +	if ( GlobalData::Instance().IsFirstPass() &&
  16.130 +		 m_currentMacro == NULL )
  16.131 +	{
  16.132 +		throw AsmException_SyntaxError_EndMacroUnexpected( line, column - 8 );
  16.133 +	}
  16.134 +
  16.135 +	RemoveIfLevel( line, column );
  16.136 +
  16.137 +	if ( GlobalData::Instance().IsFirstPass() )
  16.138 +	{
  16.139 +		MacroTable::Instance().Add( m_currentMacro );
  16.140 +		m_currentMacro = NULL;
  16.141 +	}
  16.142 +}
    17.1 --- a/src/sourcefile.h	Fri Mar 04 02:01:51 2011 +0000
    17.2 +++ b/src/sourcefile.h	Sun Mar 06 18:19:27 2011 +0100
    17.3 @@ -26,6 +26,7 @@
    17.4  #include <fstream>
    17.5  #include <string>
    17.6  #include <vector>
    17.7 +#include "macro.h"
    17.8  
    17.9  
   17.10  class SourceFile
   17.11 @@ -85,32 +86,38 @@
   17.12  	int						m_ifStackPtr;
   17.13  	If						m_ifStack[ MAX_IF_LEVELS ];
   17.14  
   17.15 +	Macro*					m_currentMacro;
   17.16 +
   17.17 +
   17.18  public:
   17.19  
   17.20 -	void					OpenBrace( std::string line, int column );
   17.21 -	void					CloseBrace( std::string line, int column );
   17.22 +	void					OpenBrace( const std::string& line, int column );
   17.23 +	void					CloseBrace( const std::string& line, int column );
   17.24  
   17.25 -	void					AddFor( std::string varName,
   17.26 +	void					AddFor( const std::string& varName,
   17.27  									double start,
   17.28  									double end,
   17.29  									double step,
   17.30  									int filePtr,
   17.31 -									std::string line,
   17.32 +									const std::string& line,
   17.33  									int column );
   17.34  
   17.35 -	void					UpdateFor( std::string line, int column );
   17.36 +	void					UpdateFor( const std::string& line, int column );
   17.37  
   17.38  	inline int 				GetForLevel() const { return m_forStackPtr; }
   17.39 +	inline Macro*			GetCurrentMacro() { return m_currentMacro; }
   17.40  
   17.41  	std::string				GetSymbolNameSuffix( int level = -1 ) const;
   17.42  
   17.43  	bool					IsIfConditionTrue() const;
   17.44 -	void					AddIfLevel( std::string line, int column );
   17.45 +	void					AddIfLevel( const std::string& line, int column );
   17.46  	void					SetCurrentIfCondition( bool b );
   17.47 -	void					StartElse( std::string line, int column );
   17.48 -	void					StartElif( std::string line, int column );
   17.49 -	void					ToggleCurrentIfCondition( std::string line, int column );
   17.50 -	void					RemoveIfLevel( std::string line, int column );
   17.51 +	void					StartElse( const std::string& line, int column );
   17.52 +	void					StartElif( const std::string& line, int column );
   17.53 +	void					ToggleCurrentIfCondition( const std::string& line, int column );
   17.54 +	void					RemoveIfLevel( const std::string& line, int column );
   17.55 +	void					StartMacro( const std::string& line, int column );
   17.56 +	void					EndMacro( const std::string& line, int column );
   17.57  
   17.58  
   17.59  private:
    18.1 --- a/src/tokens.h	Fri Mar 04 02:01:51 2011 +0000
    18.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.3 @@ -1,36 +0,0 @@
    18.4 -/*************************************************************************************************/
    18.5 -/**
    18.6 -	tokens.h
    18.7 -
    18.8 -	All the data used by the assembler
    18.9 -*/
   18.10 -/*************************************************************************************************/
   18.11 -
   18.12 -// Token table
   18.13 -
   18.14 -LineParser::Token	LineParser::m_gaTokenTable[] =
   18.15 -{
   18.16 -	{ ".",			&LineParser::HandleDefineLabel },
   18.17 -	{ "\\",			&LineParser::HandleDefineComment },
   18.18 -	{ ";",			&LineParser::HandleDefineComment },
   18.19 -	{ ":",			&LineParser::HandleStatementSeparator },
   18.20 -	{ "PRINT",		&LineParser::HandlePrint },
   18.21 -	{ "ORG",		&LineParser::HandleOrg },
   18.22 -	{ "INCLUDE",	&LineParser::HandleInclude },
   18.23 -	{ "EQUB",		&LineParser::HandleEqub },
   18.24 -	{ "EQUS",		&LineParser::HandleEqub },
   18.25 -	{ "EQUW",		&LineParser::HandleEquw },
   18.26 -	{ "SAVE",		&LineParser::HandleSave },
   18.27 -	{ "FOR",		&LineParser::HandleFor },
   18.28 -	{ "NEXT",		&LineParser::HandleNext },
   18.29 -	{ "IF",			&LineParser::HandleIf },
   18.30 -	{ "ELSE",		&LineParser::HandleElse },
   18.31 -	{ "ENDIF",		&LineParser::HandleEndif },
   18.32 -	{ "ALIGN",		&LineParser::HandleAlign },
   18.33 -	{ "SKIP",		&LineParser::HandleSkip }
   18.34 -};
   18.35 -
   18.36 -
   18.37 -
   18.38 -
   18.39 -