; on entry the following should be pre-set
;	a0	address of the track data
;
; during processing the following registers will be used

;d0	object data pointer / scratch
;d1-2	used for returning sine, and thereafter for retaining the inter-sectional offsets to x,z
;d3-d4	x,z of current object
;d5	current orientation
;d6	relative orientation holder
;d7	reverse flag at top, plus extras flags

Qcars		equ	6		; space allocation for 3d car definitions
twist_limit	equ	16		; keep things looking sensible

;=========================================================
;	ERROR HANDLING ROUTINES - FOR DEBUGGING
;=========================================================

error0:	message 'Undefined track command - fatal error.'
error1: message 'Incorrect stream code in track data.'
error2: message 'Bad initialisation code in track data.'
error3:	message 'Stream Overflow - fatal error.'
error4:	message 'Unmatched POP instruction.'
error5: message 'Illegal grid operation.'
error6:	message 'Output memory usage overflow.'
;=========================================================

Track::
	movem.l	d0-d7/a6,-(sp)		; stack all data registers
	move.l	a0,-(sp)		; for use very shortly

	move.l	#park_target,park_ad.w	; so that correct place is filled out
	movea.l	#Def3D-$20,a1		; address of the 3D object table
	movea.l	#Genstr,a2		; address of space to house generated list
	movea.w	#Stream,a3		; address of space to hold object stream counts to save on register over use
	movea.l	#FlagData,a4		; address of information on streams and object counts relating to each flag
;	movea.l	#track_table,a4		; a4 used for rock'n'roll data after 3d data initialisation process
					; a5 currently reserved for general scratch use
					; a6 free until actual 3d data starts being processed

tcount:	moveq.l	#0,d0			; d0 is for scratch
	moveq.l	#0,d1			; empty for holding flags - to allow counting barriers etc.
	moveq.l	#0,d5			; clear for word to long expansion

	move.l	d0,Pendx.w		; prepare pending move values to avoid initial jump
	move.l	d0,Pendz.w		; 
	move.l	d0,height_step.w
	move.l	d0,flying_ad.w		; make sure nothing gets moved unless its meant to !
	move.l	d0,fly_x.w
	move.l	d0,fly_z.w

	move.w	d0,Pendr.w		; similarly pending rotation
	move.w	d0,twist.w		; initial angle for track tilt
	move.w	d0,twist_inc.w		; initial increment level
	move.w	d0,twist_tgt.w		; initial target value
	move.w	d0,twdir.w		; 'direction' of twist
	move.w	d0,twdir_inc.w		; increment
	move.w	d0,twdir_tgt.w		; target
	move.w	d0,skip_qty.w
	move.w	d0,twist_segment.w

	move.w	d0,trail_step.w
	move.w	d0,count_down.w
	move.w	d0,sidecount.w
	move.w	d0,sideways.w
	move.w	d0,swing_camera_angle.w
	move.w	d0,swing_camera.w
	move.w	d0,lap_display.w
	move.w	#10000,track_width.w

	move.b	d0,view_limit.w
	move.b	d0,track_flags.w
	move.b	d0,reposition_count.w
	move.b	d0,drift_count.w
	st.b	dont_lap.w

	move.l	d0,(a3)+		; zero entire stream table even though may not all be used
	move.l	d0,(a3)+		; sub-surface stream
	move.l	d0,(a3)+		; surface stream (holds ground and carpet)
	move.l	d0,(a3)+		; track stream
	move.l	d0,(a3)+		; markings
	move.l	d0,(a3)+		; skids
	move.l	d0,(a3)+		; shadows
	move.l	d0,(a3)			; buildings
	lea	-28(a3),a3		; reset to start value

	move.w	(a0)+,d0		; use d0 for scratch space
	cmp.w	#START,d0		; check for standard initialisation code
	bne	error2

	lea	10(a0),a0		; skip next ten - just contains position data

loop1:	adda.w	skip_qty.w,a0
	clr.w	skip_qty.w
	moveq	#0,d0
	move.b	(a0)+,d0
	beq	cdone
	bclr	#EXTRAB,d0		; clear extras flag
	beq.b	.nexa
	addq.w	#2,skip_qty.w
	bsr	adjust_stream_counts

.nexa:	bclr	#REVB,d0		; ignore reverse - makes it +ve as well, so ext will work !
	cmp.w	#UNUSED,d0		; test for oversize
	bpl	error0

	movea.l	#Def3D-$20,a1
	cmp.w	#CORNER,d0
	bgt.b	.isnt
	movea.l	#DefTrack-$20,a1

.isnt:	ext.w	d0
	asl.w	#1,d0			; scale up for long values
.k:	movea.w	sublist-.k-4(PC,d0.w),a5
	lsr.w	#1,d0			; undo the scaling
base1:	jsr	-2(PC,a5)
	bra	loop1

sublist:
	entry	x_object,base1		;1
	entry	x_object,base1		;2
	entry	x_object,base1		;3
	entry	x_nothing,base1		;4
	entry	x_nothing,base1		;5
	entry	x_object,base1		;6
	entry	x_object,base1		;7
	entry	x_flagbits,base1	;8
	entry	x_flagbits,base1	;9
	entry	x_flagbits,base1	;10
	entry	x_flaginit,base1	;11
	entry	x_viewpoint,base1	;12
	entry	x_oldstuff,base1	;13
	entry	x_gridinit,base1	;14
	entry	x_gridinit,base1	; these don't occur, because grid init
	entry	x_gridinit,base1	; follows through until the end of the grid
	entry	x_gridinit,base1	; and continues from space after it
	entry	x_trigger,base1		;18
	entry	x_monitor,base1		;19
	entry	x_twist,base1		;20
	entry	x_twist,base1		;21
	entry	x_object,base1		;22
	entry	x_nothing,base1		;23
	entry	x_nothing,base1		;24
	entry	x_object,base1		;25
	entry	x_move,base1		;26
	entry	x_move,base1		;27 park
	entry	x_nothing,base1		;28
	entry	x_nothing,base1

x_twist:
	addq.w	#3,skip_qty.w		; makes it ignore all the values
	rts

x_oldstuff:	rts			; not used
x_viewpoint:	rts			; not used
x_trigger:	rts			; not used
x_monitor:	rts			; not used
x_nothing:	addq.w	#1,skip_qty.w	; causes simple ignore of an extra data byte
		rts
x_move:		addq.w	#7,skip_qty.w	; ignores 0,xoff,zoff,yrot
		rts


x_flagbits:
	moveq.l	#0,d2
	move.b	(a0)+,d2		; get flag number
	asl.w	#2,d2			; scale up for indexing
	moveq.l	#0,d3
	move.b	(a4,d2.w),d3		; get stream number
	asl.w	#2,d3
	moveq.l	#0,d4
	move.b	1(a4,d2.w),d4		; get increment for this flag
	lsr.w	#2,d2			; unscale flag number for bit operations

	cmp.b	#FLAGON,d0		; if not that just leaves flag setting commands
	bne.b	.non
	bset	d2,d1
	bne	.exit			; if already set then skip
.set2:	add.w	d4,(a3,d3.w)		; add increment to stream increment
	rts

.non:	cmp.b	#FLAGOFF,d0
	bne.b	.noo
.clr:	bclr	d2,d1
	beq	.exit			; if already clear skip
.clr2:	sub.w	d4,(a3,d3.w)		; remove increment from stream increment
.exit:	rts

.noo:	cmp.b	#FLAGCHG,d0
	bne	error0

.chg:	bchg	d2,d1
	beq.b	.set2			; use set and clear code to set appropriately
	bra.b	.clr2


x_flaginit:
	movem.l	d7/a4,-(a7)		; stack because will destroy
	subq.l	#1,a0
	move.l	(a0)+,d7		; read flags (with instruction in high byte)
	exg.l	d1,d7			; since will want the 'new' flags in place
	move.l	d1,-(a7)		; stack
	eor.l	d1,d7			; changes required now marked as 1's - 0 = no change
	moveq.l	#LASTFG,d0		; counter for flags limit
.lbx:	lsr.l	#1,d7			; push a bit into carry
	bcc.b	.lb2			; loop back if no change needed
	moveq.l	#0,d3
	move.b	(a4)+,d3		; stream code
	asl.w	#2,d3			; scale up for indexing
	moveq.l	#0,d4
	move.b	(a4)+,d4		; increment value
	lsr.l	#1,d1			; check if is set in new settings
	bcs.b	.fad			; if it is then add
	neg.w	d4			; otherwise negate then add
.fad:	add.w	d4,(a3,d3.w)		; make correction to stream increment
	addq.l	#2,a4			; because table has 4 entries, not just two
.lb:	dbra	d0,.lbx			; head round requisite number of times	

	movem.l	(a7)+,d1/d7/a4
	rts

.lb2:	lsr.l	#1,d1			; because without the shift everything will fall apart
	addq.l	#4,a4
	bra	.lb

;===========================================================
; herein the object commands
;===========================================================
x_object:				; processes any command where the first byte is an object code
	cmp.b	#ADDHIGH,d0
	bne.b	.f3
	add.w	#10,skip_qty.w
.f3:	cmp.b	#ADDLONG,d0
	bne.b	.f0
	add.w	#10,skip_qty.w
.f0:	cmp.b	#ADDR,d0
	bne.b	.f1
	addq.w	#6,skip_qty.w
.f1:	cmp.b	#ADDO,d0
	bne.b	.f2
	addq.w	#6,skip_qty.w

.f2:	cmp.b	#ADDO,d0		; test for track type add rather than other objects
	bpl.b	.noex			; if not then extra pieces won't have to be added based on the flags

.extr:	cmp.b	#CORNER,d0		; test for the one special case - tight corners which need special consideration by side etc
	bne.b	.norm

	movem.l	d1/a3-a4,-(sp)		; push flags so that they can be destructively tested, and addresses so they can be used
	moveq.l	#2,d2			; offset pointer to flag data
	btst	#7,d0			; check for reverse
	beq.b	.fk
	moveq.l	#3,d2
.fk:	moveq.l	#9,d0			; flag counter
	moveq.l	#0,d4			; for increments being converted byte to word
;	subq.l	#4,a3
	subq.l	#4,a4

	and.l	#$FFFFFF,d1		; strip high stuff
.lz:	tst.l	d1
	beq.b	.nex			; avoid unnecessary case where all flags 0
;	addq.l	#4,a3
	addq.l	#4,a4
	lsr.l	d1			; push lowest bit into Carry
	bcc	.lz			; if bit clear move on to next bit

	moveq.l	#0,d3
	move.b	(a4),d3			; read in stream code
	asl.w	#2,d3
	move.b	(a4,d2.w),d4		; read in increment
	add.w	d4,2(a3,d3.w)		; apply increment to stream count
	bra	.lz

.nex:	movem.l	(sp)+,d1/a3-a4
	bra.b	.noex


.norm:	move.l	a3,-(sp)

	moveq.l	#7,d0			; stream counter - now increased
.lx:	move.w	(a3)+,d2		; get increment
	add.w	d2,(a3)+		; add increment to counter
	dbra	d0,.lx			; and loop round

	move.l	(sp)+,a3

.noex:	moveq.l	#0,d0
	move.b	(a0)+,d0		; get object number
	asl.w	#5,d0
	move.b	STREAM(a1,d0.w),d0	; get stream number
	beq	error1			; stream numbers start from 1
	ext.w	d0
	asl.w	#2,d0			; make stream number *4
	addq.w	#1,2(a3,d0.w)		; the 2 makes it add to the count rather than the increment value
	rts


;===============================================================

cdone::	move.l	(sp)+,a0		; recover address
	move.b	(a0)+,d0		; read command code
	cmp.b	#START>>8,d0		; test for start code
	bne	error2

	bsr	initrk			; initialise positional data, and setup the list management data

	movea.l	#track_table,a4		; a4 used for rock'n'roll data 
loop:	lea	jtable-*-2(PC),a6	; address of subroutine table - soon to be used
	moveq.l	#0,d0
	move.b	(a0)+,d0
	beq	exit			; now uses a routine for finishing off - which adds cars etc

	bclr	#REVL,d7		; zap the current reverse bit
	bclr	#REVB,d0		; test new one and clear
	beq.b	.norev
	bset	#REVL,d7
.norev:	bclr	#EXTRAB,d0		; test for 'extras' flag
	beq.b	.noext
	addq.w	#2,skip_qty.w
	tst.w	1(a0)			; test for zeroing (ie extras off)
	beq.b	.zap
	move.w	1(a0),left_obj.w	; note - writing a word to a byte to get two in one go
	bset	#EXTRAL,d7
	bra.b	.noext

.zap:	clr.w	left_obj.w
	bclr	#EXTRAL,d7

.noext:	asl.w	#1,d0			; multiply by 4 for address offset
	movea.w	(a6,d0.w),a3		; subroutine address

	cmp.w	#22<<1,d0		; test for new object entry code
	beq.b	.rob			; if is then read object data

	cmp.w	#25<<1,d0		; test for new object entry code
	beq.b	.rob			; if is then read object data

	cmp.w	#8<<1,d0
	bpl.b	base2			; not an object adding function, so no object code to read

.rob:	movea.l	#Def3D-$20,a1
	cmp.w	#CORNER<<1,d0
	bgt.b	.isnt
	movea.l	#DefTrack-$20,a1

.isnt:	moveq.l	#0,d0			; get pointer to object data block for use both here and in processing subroutine
	move.b	(a0)+,d0		; read object code
	asl.w	#5,d0			; scale up to skip blocks of 32 bytes

	moveq.l	#0,d1
	move.b	STREAM(a1,d0.w),d1	; get stream number
	asl.w	#2,d1
	movea.w	#WkSpc-4,a6		; -4 because there is no zero stream
	movea.l	(a6,d1.w),a2		; read current pointer value

base2:	jsr	-2(PC,a3)		; a3 will be free once jsr taken, because it isn't used again
	adda.w	skip_qty.w,a0
	clr.w	skip_qty.w
	bra	loop

exit:	addq.l	#1,a0			; just in case anything tries to use the address afterwards
	
endtrk:		; routine to finish off the data streams

	movea.w	#WkSpc-4,a6
	movea.l	str_real*4(a6),a2	; read current pointer value for main 3d stream 
	move.l	a2,carsad.w		; position where data for first person car is stored
	movea.l	#Def3D-$20,a1		; don't want to try adding track pieces instead of cars

	move.l	xstart.w,d3		; put current position to the first track segment position
	move.l	zstart.w,d4		; z
	move.l	rstart.w,d5		; rotation

	bsr	move_back		; moves back a whole segment [20000 units]

	move.w	#Obcar,d0		; car 0
	add.b	grid_colour0.w,d0
	asl.w	#5,d0
	move.l	#-5000,d1
	move.l	#12500,d2
	moveq.l	#0,d6

	bsr	addrel2

	move.w	#Obf1shadow,d0
	asl.w	#5,d0
	bsr	addrel2
	lea	-32(a2),a2
	move.l	a2,shadad.w

	tst.b	real_num_drones.w
	beq	.alldone

;==============================================
	move.w	#Obcar,d0		; car 1
	add.b	grid_colour1.w,d0
	asl.w	#5,d0
	neg.l	d1
	sub.l	#5000,d2
	bsr	addrel2

	move.w	#Obf1shadow,d0
	asl.w	#5,d0
	bsr	addrel2

	cmp.b	#1,real_num_drones.w
	beq	.alldone

;==============================================
	move.w	#Obcar,d0		; car 2
	add.b	grid_colour2.w,d0
	asl.w	#5,d0
	neg.l	d1
	sub.l	#5000,d2
	bsr	addrel2

	move.w	#Obf1shadow,d0
	asl.w	#5,d0
	bsr	addrel2

	cmp.b	#2,real_num_drones.w
	beq.b	.alldone

;==============================================
	move.w	#Obcar,d0		; car 3
	add.b	grid_colour3.w,d0
	asl.w	#5,d0
	neg.l	d1
	sub.l	#5000,d2
	bsr	addrel2

	move.w	#Obf1shadow,d0
	asl.w	#5,d0
	bsr	addrel2

	cmp.b	#3,real_num_drones.w
	beq.b	.alldone

;==============================================
	move.w	#Obcar,d0		; car 4
	add.b	grid_colour4.w,d0
	asl.w	#5,d0
	neg.l	d1
	sub.l	#5000,d2
	bsr	addrel2

	move.w	#Obf1shadow,d0
	asl.w	#5,d0
	bsr	addrel2

	cmp.b	#4,real_num_drones.w
	beq.b	.alldone

;==============================================
	move.w	#Obcar,d0		; car 5
	add.b	grid_colour5.w,d0
	asl.w	#5,d0
	neg.l	d1
	sub.l	#5000,d2
	bsr	addrel2

	move.w	#Obf1shadow,d0
	asl.w	#5,d0
	bsr	addrel2

.alldone:
;===============================================
;	add sparks and any other anims which need to follow cars around

	move.w	#Obsmokeb,d0
	asl.w	#5,d0
	bsr	addrel2			; no need for positional settings as it will move as soon as it can	
	lea	-32(a2),a2
	move.l	a2,smokead.w

	move.w	#Obsparkb,d0
	asl.w	#5,d0
	bsr	addrel2			; no need for positional settings as it will move as soon as it can	
	lea	-32(a2),a2
	move.l	a2,sparkad.w

	move.w	#Obgrassb,d0
	asl.w	#5,d0
	bsr	addrel2			; no need for positional settings as it will move as soon as it can	
	lea	-32(a2),a2
	move.l	a2,grassad.w

;===============================================

	move.w	#Obwheel,d0		; note that the wheels can be placed anywhere, as they get moved to the car when needed
	asl.w	#5,d0
	move.l	#whljn-5000,d1
	move.w	#axldis+9500,d2
	move.w	#-$100,d6

	bsr	addrel2
	lea	-32(a2),a2
	move.l	#-212,16(a2)		; force up to right height
	bchg	#6,13(a2)		; toggle real position of in car wheels note use of 32 where Struclen would be strictly correct

	move.l	a2,spinad.w		; position at which to start rotation routine - for spinning wheels
					; this will have to be modified - otherwise can't use windmill
					; ideally need a list of spinning items. new entry for tdef !!
	move.l	#-5000,d1
	sub.l	#whljn,d1
	neg.w	d6

	bsr	addrel2
	lea	-32(a2),a2
	move.l	#-212,16(a2)		; force up to right height
	bchg	#6,13(a2)		; this makes them invisible initially

	lea	32(a2),a2
	move.l	#$ffff0000,(a2)+	; set spin stopper

;================================================
; write correct viewpoint into place

	move.l	carsad.w,a1		; a1 because thats where its used elsewhere
	moveq	#0,d0
	move.b	grid_position.w,d0
	asl.w	#5,d0
	adda.w	d0,a1			; point at appropriate car

	neg.w	d5			; put current angle into camera angle - because initially looking down car
	move.w	d5,Genstr+2

	movea.l	#view_table-8,a0	; points at viewpoint data table, less a bit, so that can just add on for any given view
	move.w	view_last.w,d1
	bne.b	.vok
	moveq	#3,d1
.vok:	asl.w	#2,d1
	adda.w	d1,a0

	move.w	(a0)+,d1
	ext.l	d1
	move.l	d1,Genstr+16		; set height
	move.w	(a0)+,trailer.w		; read distance and put in place

	bsr	Viewset			; this sets viewpoint x,z based on trailer and doesn't touch height



qtst:	movea.l	#Genstr+32-4,a6		; points to first list pointer (-4 is because I've reversed the order of data entries)
	movea.w	#Stream+4,a3
	moveq.l	#6,d0			; list counter for dbra loop - increased
	moveq.l	#0,d1			; blank for word-long expansion
	moveq.l	#0,d2			; counter for non-empty lists

.lp:	move.w	(a3)+,d1		; get stream count
	bne.b	.chk
	move.l	d1,(a6)			; zap address pointer to invalid data
	bra.b	.okx
.chk:	addq.w	#1,d2			; if stream exists, count it 
	cmp.w	(a3),d1			; check count against allowed space
	ble.b	.ok

	bsr	error3

.ok:	movea.l	(a6),a2			; get pointer to actual list - a2 is free now that all the lists have been written
	move.l	d1,(a2)			; write correct count into start of stream (if stream exists)
.okx:	addq.l	#8,a6			; move on to next list
	addq.l	#2,a3

	dbra	d0,.lp


	movea.l	WkSpc+24,a2		; read current pointer value of 7th stream
	cmp.l	#Sort_space-$1000,a2	; test against next thing which needs to be in place
	blt.b	.np
	bsr	error6

.np:	move.l	d2,Genstr+24		; write list count into right place
	move.l	Genstr+20+(8*str_marking),d0	; read address of track markings list 
	move.l	d0,Dronea.w		; set up for use by drone cars
	moveq.l	#1,d0
	move.l	d0,Dronec.w

	sf	dronef.w		; set not to use drone car

	movea.l	Genstr+20+(8*str_track),a0		; address of track segment list
	move.w	2(a0),d0		; get number of segments
	subq.w	#1,d0			; make it actual highest segment number
	move.w	d0,final_segment.w	; store it



	movem.l	(sp)+,d0-d7/a6		; restore all data registers

	rts
;================================

move_back:
	exg	d0,d5
	bsr	get_sine
	exg	d0,d5

	move.w	#20000,d0
	muls	d0,d1
	muls	d0,d2

	asl.l	#2,d1
	asl.l	#2,d2
	swap	d1
	swap	d2
	ext.l	d1
	ext.l	d2			; perform quick 14 bit shift

	add.l	d1,d3			; apply backwards move
	sub.l	d2,d4
	rts


jtable:	entry	endtrk,base2		; 0
	entry	addtrk,base2		; 1
	entry	addtrk,base2		; 2
	entry	addtrk,base2		; 3
	entry	pushpos,base2		; 4
	entry	poppos,base2		; 5
	entry	addobj,base2		; 6
	entry	addrel,base2		; 7
	entry	setflg,base2		; 8
	entry	clrflg,base2		; 9
	entry	chgflg,base2		; 10
	entry	intflg,base2		; 11
	entry	error0,base2		; 12
	entry	error0,base2		; 13
	entry	grid_init,base2		; 14
	entry	grid_obj,base2		; 15
	entry	grid_line,base2		; 16
	entry	grid_skip,base2		; 17
	entry	trigger,base2		; 18
	entry	monitor,base2		; 19
	entry	twisttgt,base2		; 20
	entry	twistinc,base2		; 21
	entry	addlng,base2		; 22
	entry	viewset,base2		; 23
	entry	flagops,base2		; 24
	entry	addhigh,base2		; 25
	entry	mover,base2		; 26
	entry	parker,base2		; 27
	entry	flier,base2		; 28
	entry	pitend,base2		; 29

FlagData:			; in the form stream,quantity1,quantity2,quantity3
				; whereby qty1 will be added for every straight track piece added, 
				; whilst qty2 will be added for forward bends and
				; qty3 added for reversed bends

	dc.b	str_real,1,1,1		; all barriers to fit as one piece the track segment they are on
	dc.b	str_real,2,2,0		; all trees to be two per side of track except where this would be impossible
	dc.b	str_real,2,2,0		; so we perform a full object count at build time - which would be a good idea anyway
	dc.b	str_real,2,2,0		; same kind of thing for palms
	dc.b	0,0,0,0
	dc.b	0,0,0,0
	dc.b	0,0,0,0
	dc.b	0,0,0,0

	dc.b	str_real,1,1,1			; note this means having special barriers for corners and short straights but no special values
	dc.b	str_real,2,0,2			; and we are simply left with some extra space.
	dc.b	str_real,2,0,2
	dc.b	str_real,2,0,2
	dc.b	0,0,0,0
	dc.b	0,0,0,0
	dc.b	0,0,0,0
	dc.b	0,0,0,0

	dc.b	str_real,0,4,4		; chevrons will only occur on tight corners, and then always four per corner
	dc.b	str_marking,4,4,4	; four white lines for each track segment - less for small pieces but no matter - extras aren't a problem
	dc.b	str_marking,0,0,0	; no extras for dotted white lines - if anything, it should be less, but this is best left
	dc.b	str_coast,2,2,2		; currently assume carpet on both sides - hence 2 pieces - later may split by increasing to 8 bits per side

Streamx:			; extra count to add to each stream to ensure space
	dc.w	0		; underground - how likely am I ever to GENERATE underground objects ? never
	dc.w	0		; ground level (eg carpet)
	dc.w	0		; track similarly
	dc.w	100*Snail_trail	; and markings - for holding 'snail markers', to show the movement of the vehicle, enough for 10 seconds
	dc.w	40		; but skids may VERY well be increased during game
	dc.w	6		; shadows will be specially catered for
	dc.w	Qcars*8+20	; extra space for broken pieces and possibly extra vehicles (ambulance etc)

;=================================

initrk:	move.b	(a0)+,d0		; secondary check
	cmp.b	#START&$FF,d0		; test for correct start value (low byte)
	bne	error2

vinit:	movea.l	#Track_basic,a5		; purely to provide a standard set of start-up data - may be superceded
	moveq.l	#5,d0			; move 6 longs to cover the view direction, light vector, and view position
.lp:	move.l	(a5)+,(a2)+
	dbra	d0,.lp

linit:	moveq.l	#0,d1
	move.l	d1,(a2)+		; set up no. of lists with zero - will be corrected MUCH later when the 
					; space allocations have been performed and the data written in to place.

	lea.l	72(a2),a4		; add space to calculate first stream address - increased
	move.l	#Streamx,a5		; address of stream excess space allowances
	move.w	#WkSpc,a6		; address where to hold the increasing stream addresses - insufficient registers

	move.l	a3,-(sp)
	addq.l	#4,a3			; because stream incs are from 0, but there is no stream 0
	moveq.l	#6,d0			; six basic streams to loop through - increased
.lp:	move.l	a4,(a2)+		; actual address of stream in list table						NEW
	move.l	#$10008,(a2)+		; a basic legal depth cue value - will be written over later when weather is set	NEW
	addq.l	#4,a4			; skip one long - will contain the stream count
	move.l	a4,(a6)+		; address for use during building process
	move.w	(a5)+,d1		; get excess count
	clr.w	(a3)+			; put zero into stream increment
	add.w	(a3),d1			; get total count
	move.w	d1,(a3)+		; and update
	asl.w	#5,d1			; scale up by 32 - the structure length for all objects
	bne.b	.use			; if there is nothing in a stream we put a zero address in
	clr.l	-8(a2)			; before moving on as if nothing had happened
.use:	ext.l	d1	
	adda.l	d1,a4			; add space requirement to current address

	dbra	d0,.lp			; loop round for next stream

	clr.w	-4(a2)			; make last stream collidable

	move.l	(sp)+,a3		; recover actual stream data address
	move.l	a4,(a3)			; and put the end of data address into the spare space - in case I want it.

	move.l	(a0)+,d3		; initialise x
	move.l	(a0)+,d4		; and z
	moveq.l	#0,d5
	move.w	(a0)+,d5		; and direction
	moveq.l	#0,d7			; and flags
	move.l	d3,xstart.w		; set up for where the race starts from - temporary hack
	move.l	d4,zstart.w
	move.l	d5,rstart.w
	rts


addtrk:	add.l	Pendx.w,d3		; use pending moves from previous track piece
	add.l	Pendz.w,d4
	add.w	Pendr.w,d5		; and use any pending rotate
	clr.l	Pendx.w
	clr.l	Pendz.w
	clr.w	Pendr.w			; and clear pending rotate		

	tst.l	d7			; check for reverse flag - which is the sign bit of the long value
	bpl.b	.nr			; if not ...

	add.w	ROTE(a1,d0.w),d5	; append rotation if needed - in which case there won't be a pending rotate

.nr:	exg	d0,d5			; swap for the sine routine
	bsr	get_sine
	exg	d0,d5			; swap back into place

	movem.l	d1-d2,-(sp)		; stack for safe keeping

	bsr	update_twist

	tst.l	d7			; check for reverse flag
	bpl.b	.nr2			; if not ...

	addq.l	#4,d0			; skip inward direction

.nr2:	muls	DXIN(a1,d0.w),d1	; = x sin
	muls	DXIN(a1,d0.w),d2	; = x cos

	asl.l	#2,d1
	asl.l	#2,d2
	swap	d1
	swap	d2
	ext.l	d1
	ext.l	d2			; perform quick 14 bit shift

	add.l	d1,d4
	add.l	d2,d3

	movem.l	(sp),d1-d2		; recover multipliers

	muls	DZIN(a1,d0.w),d1	; z sin
	muls	DZIN(a1,d0.w),d2	; z cos

	asl.l	#2,d1
	asl.l	#2,d2
	swap	d1
	swap	d2
	ext.l	d1
	ext.l	d2			; perform quick 14 bit shift

	sub.l	d1,d3			; finalise movement from end of previous piece to centre of current piece
	add.l	d2,d4

	tst.l	d7
	bpl.b	.nx
	subq.l	#4,d0			; correct pointer early to avoid mess ups
	bra.b	.ny

.nx:	add.w	#$200,d5		; reverse direction just for output of non-reversed objects !

.ny:	clr.l	(a2)+			; no spin/rotation about x
	move.l	d5,(a2)+		; set rotation about y
	clr.l	(a2)+			; no spin about z

	move.l	d3,(a2)+		; x - placed now, before movement to end of this piece
	clr.l	(a2)+			; y
	move.l	d4,(a2)+		; z

; at this point, all positional data has been written, and the remaining calculations will be moving the current point
; on to the end of the object.  Thus this is where any extras need to be dealt with, and hence the remaining info for
; this object must be written out here and the addresses updated

	move.l	RADIUS(a1,d0.w),(a2)+	; radius
	move.l	OBJAD(a1,d0.w),(a2)+	; data pointer

	tst.l	d7
	bmi.b	.txq
	sub.w	#$200,d5		; reverse again makes only reversed objects reversed !

.txq:	move.l	d1,-(sp)
	moveq.l	#0,d1
	move.b	STREAM(a1,d0.w),d1	; get stream number
	asl.w	#2,d1			; scale up
	movea.w	#WkSpc-4,a6
	move.l	a2,(a6,d1.w)		; write new data stream address in to store
	movea.w	#Stream,a6
	addq.w	#1,(a6,d1.w)		; and also increment the actual count for this stream
	move.l	(sp)+,d1


	movea.l	EXTRA(a1,d0.w),a5	; get address of 'extra' data
	movea.l	#Def3D-$20,a1		; change from track objects to the other objects list
	bsr.b	addextra		; totally self contained routine for adding surrounding objects
	movea.l	#DefTrack-$20,a1

	tst.l	d7			; check for reverse flag
	bmi.b	.nr3 
	move.w	ROTE(a1,d0.w),Pendr.w	; set pending rotation if needed - ie if not set previously
	neg.w	Pendr.w			; reverse sign - rotation needs to come off ?!
	addq.l	#4,d0			; if not reversed then point at second pair
;	bra.b	.nr4

.nr3:;	add.w	#$200,d5		; return direction if reversed

.nr4:	movem.l	(sp),d1-d2		; recover multipliers again

	muls	DXIN(a1,d0.w),d1	; = x sin
	muls	DXIN(a1,d0.w),d2	; = x cos

	asl.l	#2,d1
	asl.l	#2,d2
	swap	d1
	swap	d2
	ext.l	d1
	ext.l	d2			; perform quick 14 bit shift

	add.l	d1,Pendz.w
	add.l	d2,Pendx.w

	movem.l	(sp)+,d1-d2		; recover multipliers

	muls	DZIN(a1,d0.w),d1	; z sin
	muls	DZIN(a1,d0.w),d2	; z cos

	asl.l	#2,d1
	asl.l	#2,d2
	swap	d1
	swap	d2
	ext.l	d1
	ext.l	d2			; perform quick 14 bit shift

	sub.l	d1,Pendx.w		; finalise movement from centre of current piece to end of current piece
	add.l	d2,Pendz.w

	rts


addextra:
	movem.l	d0-d5/d7/a0,-(sp)		; these are the critical registers - others shouldn't matter at all
					; a0 added to these, so it can be used to hold the index address for the original track object

	movea.l	#DefTrack-$20,a0
	move.w	#-$100,d6		; barrier direction offset

	tst.l	d7			; test for reverse flag
	bpl.b	.nr			; if not then skip next bit
	rol.w	#8,d7			; if 'tis then swap left and right flags
	neg.w	d6
;	addq.l	#1,d0			; and adjust for reading barrier code

.nr:	btst	#BARL,d7		; d7 holds all the flags - including reverse
	beq.b	.nobl

	btst	#EXTRAL,d7
	beq.b	.f1
	move.b	left_obj.w,d0
	bne.b	.f2			; if empty use normal one
	move.l	(sp),d0			; recover d0
.f1:	move.b	LBAR(a0,d0.w),d0	; read barrier code to use
.f2:	and.w	#$ff,d0
	asl.w	#5,d0
	bsr	addbar			; routine to insert barrier using addrel, at a position pointed to by (a5)
					; routine leaves a5 unchanged specifically to make this section easier to write

.nobl:	btst	#CARPET,d7
	beq.b	.nocl

	move.l	(sp),d0
	move.b	LTILE(a0,d0.w),d0
	beq.b	.nocl
	and.w	#$ff,d0
	asl.w	#5,d0
	bsr	addbar			; it may seem strange, but addbar will add anything, at the barrier position


.nocl:	move.l	(sp),d0			; recover d0
	addq.l	#4,a5			; move on from barrier pointer (left)
;	tst.l	d7			; test for reverse flag
;	bpl.b	.nr2			; if not then skip next bit

;	subq.l	#1,d0			; adjust for reading barrier code 

.nr2:	btst	#BARR,d7		; d7 holds all the flags - including reverse
	beq.b	.nobr

	neg.w	d6

	btst	#EXTRAL,d7
	beq.b	.f3
	move.b	right_obj.w,d0
	bne.b	.f4
	move.l	(sp),d0			; recover d0
.f3:	move.b	RBAR(a0,d0.w),d0	; read barrier code to use
.f4:	and.w	#$ff,d0
	asl.w	#5,d0
	bsr	addbar			; 

.nobr:	btst	#CARPET,d7
	beq.b	.nocr

	move.l	(sp),d0
	move.b	RTILE(a0,d0.w),d0
	beq.b	.nocr
	and.w	#$ff,d0
	asl.w	#5,d0
	bsr	addbar


.nocr:	addq.l	#4,a5			; skip barrier pointer (right)
	btst	#TREEL0,d7
	beq.b	.notl			; if no first row of trees, then try palms instead

	move.w	#Obtree,d0
	btst	#CLOSE,d7
	beq.b	.ncll
	addq.w	#1,d0
.ncll:	asl.w	#5,d0
	bsr	addtree			; add a pair of trees 
	bra.b	.nopl			; since a first row is in, there can't be palms

.notl:	btst	#PALML,d7
	beq.b	.nopl

	move.w	#Obpalm,d0
	btst	#CLOSE,d7
	beq.b	.ncpl
	addq.w	#1,d0
.ncpl:	asl.w	#5,d0
	bsr	addtree			; add a pair of palms in the same way as adding a pair of trees

.nopl:	addq.l	#8,a5			; first row (left) either done or empty so now try second row
	btst	#TREEL1,d7
	beq.b	.notl2

	move.w	#Obtree,d0
	btst	#CLOSE,d7
	beq.b	.nclr
	addq.w	#1,d0
.nclr:	asl.w	#5,d0
	bsr	addtree

.notl2:	addq.l	#8,a5
	btst	#TREER0,d7
	beq.b	.notr			; if no first row of trees, then try palms instead

	move.w	#Obtree,d0
	btst	#CLOSE,d7
	beq.b	.ncll2
	addq.w	#1,d0
.ncll2:	asl.w	#5,d0
	bsr	addtree			; add a pair of trees 
	bra.b	.nopr			; since a first row is in, there can't be palms

.notr:	btst	#PALMR,d7
	beq.b	.nopr

	move.w	#Obpalm,d0
	btst	#CLOSE,d7
	beq.b	.ncpr
	addq.w	#1,d0
.ncpr:	asl.w	#5,d0
	bsr	addtree			; add a pair of palms in the same way as adding a pair of trees

.nopr:	addq.l	#8,a5
	btst	#TREER1,d7
	beq.b	.notr2

	move.w	#Obtree,d0
	btst	#CLOSE,d7
	beq.b	.nclr2
	addq.w	#1,d0
.nclr2:	asl.w	#5,d0
	bsr	addtree

.notr2:	addq.l	#8,a5

	move.l	(sp),d0			; recover d0 - note we aren't using + because there will be a full recovery in movem at the end
	tst.w	ROTE(a0,d0.w)		; check whether straight
	bne.b	.curve

	btst	#LINE,d7		; longline tested first, so it can overide short dots easily
	beq.b	.dot			; also note that it can only occur on straights, not corners - this may need to be changed

	move.w	#Obline,d0
	asl.w	#5,d0
	bsr	addlong
	bra.b	.exit

.dot:	btst	#DOT,d7
	beq.b	.exit

	move.w	#Obdot,d0
	asl.w	#5,d0
	bsr	addlines
	bra.b	.exit


;----------------------------------------------------------

.curve:	btst	#WARN,d7
	beq.b	.now

	clr.w	d6
	tst.l	d7			; check for reversed curve - in which case the angle of curvature needs to be taken from the rotation !
	bpl.b	.naa

	move.w	ROTE(a0,d0.w),d6
	neg.w	d6

.naa:	move.w	#Obsignr,d0
	tst.l	d7
	bpl.b	.nlt

	subq.w	#1,d0			; left turn arrow follows right turn
.nlt:	asl.w	#5,d0

	bsr	addchev	

.now:	lea	16(a5),a5		; skip 8 words for chevrons

	btst	#DOT,d7
	beq.b	.exit

	move.w	#Obdot,d0
	asl.w	#5,d0
	bsr	addline2
	
.exit:	movem.l	(sp)+,d0-d5/d7/a0
	rts

addrel2:movem.l	d1-d5,-(sp)

	movea.w	#WkSpcw,a3
	move.w	d1,(a3)+
	move.w	d2,(a3)+
	move.w	d6,(a3)
	bra.b	axd

addrel:	movem.l	d1-d5,-(sp)		; stack everything which is changeable

	movea.w	#WkSpcw,a3
	move.l	(a0)+,(a3)+
	move.w	(a0)+,(a3)

axd:	tst.w	d0
	beq	ext			; drop straight if non-existent object code
	moveq.l	#0,d1
	move.b	STREAM(a1,d0.w),d1	; get stream number
	asl.w	#2,d1
	movea.w	#WkSpc-4,a6		; -4 because there is no zero stream
	movea.l	(a6,d1.w),a2		; read current pointer value

	bsr	apply_moves		; new subroutine to allow use elsewhere

outp:	clr.l	(a2)+			; no spin/rotation about x
	move.l	d5,(a2)+		; set rotation about y
	clr.l	(a2)+			; no spin about z

	move.l	d3,(a2)+		; x - placed now, before movement to end of this piece
	clr.l	(a2)+			; y
	move.l	d4,(a2)+		; z

	move.l	RADIUS(a1,d0.w),(a2)+	; radius
	move.l	OBJAD(a1,d0.w),(a2)+	; data pointer

	moveq.l	#0,d1
	move.b	STREAM(a1,d0.w),d1		; get stream number (again)
	asl.w	#2,d1			; scale up
	move.l	a2,(a6,d1.w)		; write new data stream address in to store
	movea.w	#Stream,a6
	addq.w	#1,(a6,d1.w)		; and also increment the actual count for this stream

ext:	movem.l	(sp)+,d1-d5
ret:	rts

addhigh:				; special routine for adding objects which are off the ground
	bsr	addrel			; do normal object add
	move.l	(a0)+,-16(a2)		; then put the long y value into place in the 3d definition
	rts

addlng:	movem.l	d1-d5,-(sp)		; has to be non-relative due to not wanting to write a 32 bit rotator

	add.l	(a0)+,d3
	add.l	(a0)+,d4
	move.w	(a0)+,d5
	bra	outp

addobj:	movem.l	d1-d5,-(sp)		; stack everything which is changeable

	move.w	(a0)+,d0		; read short offset
	ext.l	d0			; extend because values in use are long
	add.l	d0,d3			; apply

	move.w	(a0)+,d0		; ditto for z
	ext.l	d0
	add.l	d0,d4

	move.w	(a0)+,d5
	bra	outp

addbar: tst.l	(a5)
	beq.b	ret			; zero,zero for barriers/trees etc is impossible - hence regard as a non-entry
	move.w	(a5),d1			; get x,z offsets from list
	move.w	2(a5),d2

flip:	tst.l	d7			; check for reverse
	
	bpl	addrel2			; addrel2 adds an object at d1,d2 position relative to current posn.

	neg.w	d1			; if reversed object then imagine will reverse positional offsets ?!
	neg.w	d2
	bra	addrel2

addtree:				; always add a pair of trees
	add.w	#$33,Treerot.w	
	move.w	Treerot.w,d6
	bsr	addbar

	addq.l	#4,a5
	add.w	#$33,Treerot.w	
	move.w	Treerot.w,d6
	bsr	addbar

	subq.l	#4,a5			; purely so that main routine can assume a5 unchanged
	rts

addlong:				; add a long line, at position half way between two middle normal ones
	tst.l	(a5)
	beq.b	ret
	addq.w	#4,a5			; skip first two
	move.w	(a5)+,d1
	move.w	(a5)+,d2

	add.w	(a5)+,d1
	add.w	(a5)+,d2
	asr.w	#1,d1
	asr.w	#1,d2
	clr.w	d6

	bra	flip

addlines:				; add 4 dotted white lines (at most) in straight line
	clr.w	d6
	bsr	addbar
	addq.l	#4,a5
	bsr	addbar
	addq.l	#4,a5
	bsr	addbar
	addq.l	#4,a5
	bra	addbar

addline2:				; add 4 dotted lines around a curve
	move.w	4(a5),d6
	bsr	addbar
	addq.l	#6,a5
	move.w	4(a5),d6
	bsr	addbar
	addq.l	#6,a5
	move.w	4(a5),d6
	bsr	addbar
	addq.l	#6,a5
	move.w	4(a5),d6
	bra	addbar

addchev:				; chevrons are placed at corners, facing back the way you are going - so they need no rotation offset

	bsr	addbar
	addq.l	#4,a5
	bsr	addbar
	addq.l	#4,a5
	bsr	addbar
	addq.l	#4,a5
	bsr	addbar
	lea	-12(a5),a5
	rts

apply_moves:				; applies movement data which has been placed at a3 (a3 can be anywhere)
	subq.l	#4,a3

	exg	d0,d5			; swap for the sine routine
	bsr	get_sine
	exg	d0,d5			; swap back into place

	movem.l	d1-d2,-(sp)		; stack for safe keeping

	muls	(a3),d1			; transformation pt 1
	muls	(a3)+,d2		; doing stuff with x-off part

	asl.l	#2,d1
	asl.l	#2,d2
	swap	d1
	swap	d2
	ext.l	d1
	ext.l	d2			; perform quick 14 bit shift

	add.l	d1,d4
	add.l	d2,d3

	movem.l	(sp)+,d1-d2		; recover multipliers

	muls	(a3),d1			; transform part 2
	muls	(a3)+,d2		; now using z-off data

	asl.l	#2,d1
	asl.l	#2,d2
	swap	d1
	swap	d2
	ext.l	d1
	ext.l	d2			; perform quick 14 bit shift

	sub.l	d1,d3
	add.l	d2,d4

	add.w	(a3)+,d5		; apply rotation change
	rts

mover:				; note we don't stack the position data, because we WANT to lose it
	addq.l	#5,a0			; point beyond the zero,xoff,zoff values
	move.l	a0,a3			; point a3 which must read in the movement data, at the current data input stream
	bsr	apply_moves		; move the current pointer to the requested location (which is track relative)
	addq.l	#2,a0			; skip over the yrot value as well
	moveq	#0,d0
	move.l	d0,Pendx.w		; abandon any previous pending moves
	move.l	d0,Pendz.w
	move.w	d0,Pendr.w
	rts

parker:				; here we stack position data to allow return after messing about
	movem.l	d1-d5/a1,-(sp)
	movea.l	park_ad.w,a1
	addq.l	#5,a0
	move.l	a0,a3
	bsr	apply_moves
	move.l	d3,(a1)+
	move.l	d4,(a1)+
	move.l	a1,park_ad.w
	addq.l	#2,a0
	movem.l	(sp)+,d1-d5/a1
	rts

flier:				; relies on a2 being kept from previous bit, so that can just store the address
	lea	-32(a2),a2		; point back one object
	move.l	a2,flying_ad.w
	addq.l	#1,a0
	rts


pushpos:
	move.l	(sp)+,d0		; get rts address
	move.w	#$DEAD,-(sp)		; special marker
;	addq.w	#1,skip_qty.w		; skip unused byte - not required because code erroneously assumes the 0 is an object and pre-processes it
	move.w	Stream+12,pit_start.w
	movem.l	d0/d3-d5,-(sp)
	rts

pitend:
	move.w	Stream+12,pit_end.w
	addq.w	#1,skip_qty.w		; had to use skip here, because the code is not below 8, and so is known not to be object related
	rts


poppos:	movem.l	(sp)+,d0/d3-d5
	cmp.w	#$DEAD,(sp)+		; check for marker
	bne	error4			; abort if wrong
;	addq.w	#1,skip_qty.w		; skip unused byte - see above
	move.l	d0,-(sp)		; rts address
	rts

setflg:	move.b	(a0)+,d0
	bset	d0,d7
	rts

clrflg:	move.b	(a0)+,d0
	bclr	d0,d7
	rts

chgflg:	move.b	(a0)+,d0
	bchg	d0,d7
	rts

intflg:	moveq.l	#0,d7
	move.b	(a0)+,d7
	swap	d7
	move.w	(a0)+,d7
	rts

monitor:
trigger:
	message	'Undefined data entry used'


adjust_stream_counts:
	movem.l	d0-d1,-(sp)
	move.b	left_obj.w,d0
	move.b	1(a0),d1		; read code for 'new' left barrier
	cmp.b	d0,d1
	beq.b	.f1			; if no change

	move.b	d1,left_obj.w
	bsr.b	calculate_changes

.f1:	move.b	right_obj.w,d0
	move.b	2(a0),d1
	cmp.b	d0,d1
	beq.b	.f2

	move.b	d1,right_obj.w
	bsr.b	calculate_changes

.f2:	movem.l	(sp)+,d0-d1
	rts



calculate_changes:
	movea.l	#Def3D-$20,a1
	and.w	#$ff,d0
	asl.w	#5,d0			; scale up - 32 bytes per entry
	bne.b	.f2
	moveq	#str_real,d0		; zero entries mean standard barrier, which is stream 6
	bra.b	.f3
.f2:	move.b	STREAM(a1,d0.w),d0	; get stream code 0
	ext.w	d0

.f3:	and.w	#$ff,d1
	asl.w	#5,d1
	bne.b	.f4
	moveq	#str_real,d1		; zero entry again
	bra.b	.f5
.f4:	move.b	STREAM(a1,d1.w),d1	; get stream code 1
	ext.w	d1

.f5:	cmp.b	d0,d1			; if same stream then no change
	beq.b	.f6

	asl.w	#2,d0			; scale up because two words stored per stream
	asl.w	#2,d1

	subq.w	#1,(a3,d0.w)		; deduct one from stream 0
	addq.w	#1,(a3,d1.w)		; and add to stream 1

.f6:	rts
	
;-----------------------------------
x_gridinit:				; this is the pre-pass routine, and hence is not the same as grid_init, the main-pass routine
	move.b	(a0)+,d0
	lea	12(a0),a0		; skip the space allocated for start x,z (long) and step x,z (word)
	bra.b	.f2

.b2:	addq.l	#1,a0			; skip unread byte

.b1:	move.b	(a0)+,d0		; read a grid entry
	cmp.b	#GRID_S,d0		; test for skip entry - skip 0 is to be used for end-grid
	bne.b	.f1

	move.b	(a0)+,d0		; if skip test for end code
	beq	.exit
	bra	.b1			; otherwise restart

.f1:	cmp.b	#GRID_L,d0		; grid-line code requires no changes
	beq	.b2

	cmp.b	#GRID_O,d0		; only other (legal) possibility is grid-object
	beq	.f3

	cmp.b	#ADDR,d0		; additional possibility - normal oject on top of grid
	bne	error5

	move.b	(a0)+,d0
	addq.l	#4,a0			; skip excess bytes
	bra.b	.f2

.f3:	move.b	(a0)+,d0
.f2:	asl.w	#5,d0
	move.b	STREAM(a1,d0.w),d0	; get stream number
	beq	error1			; ensure stream no. legal
	ext.w	d0
	asl.w	#2,d0			; make stream number *4
	addq.w	#1,2(a3,d0.w)		; the 2 makes it add to the count rather than the increment value
	addq.l	#2,a0			; skip rotation value
	bra	.b1

.exit:	rts

grid_init:
;	move.l	(sp)+,d0		; steal rts address
;	movem.l	d0/d3-d5,-(sp)		; stack position/rotation, and the old rts address below it to allow correct return
	movem.l	d3-d5,general_heap.w
	move.b	(a0)+,d0		; object number to add

	move.l	(a0)+,d3		; new x
	move.l	(a0)+,d4		; new z
	move.w	(a0)+,d5		; new rotation
	move.w	(a0)+,xstep.w
	move.w	(a0)+,zstep.w
	move.l	d3,xinit.w
	bra.b	gobj

grid_obj:
	move.b	(a0)+,d0		; get object to add
	move.w	(a0)+,d5		; update rotation

gobj:	asl.w	#5,d0
	moveq.l	#0,d1			; clear relative offsets
	moveq.l	#0,d2
	moveq.l	#0,d6			; and relative rotation
	movea.l	#Def3D-$20,a1
	bsr	addrel2

	move.w	xstep.w,d1
	add.l	d1,d3

	rts

grid_line:
	move.l	xinit.w,d3		; reset x
	moveq.l	#0,d0
	move.w	zstep.w,d0
	add.l	d0,d4			; update z
	moveq.l	#0,d0
	move.b	(a0)+,d0		; get skip qty
	muls	xstep.w,d0
	add.l	d0,d3
	rts

grid_skip:
	moveq.l	#0,d0
	move.b	(a0)+,d0		; get skip qty
	beq.b	.f1
	muls	xstep.w,d0
	add.l	d0,d3
	rts

.f1:
	movem.l	general_heap.w,d3-d5
;	movem.l	(sp)+,d0/d3-d5
;	move.l	d0,-(sp)
	rts

update_twist:
	movem.l	d0-d3,-(sp)

	tst.w	ROTE(a1,d0.w)		; check for corners ie non zero turns - done here whilst values exits
	beq.b	.nil
	bset	#TURNING,track_flags.w
	bra.b	.twi
.nil:	bclr	#TURNING,track_flags.w

.twi:	move.w	twist_inc.w,d1		; read increment -
	beq.b	.ntc			; if none then quit

	move.w	twist.w,d0		; read current twist qty
	move.w	twist_tgt.w,d2		; read stopping point
	add.w	d1,d0			; apply increment
	cmp.w	d0,d2			; test for stop
	beq.b	.stop			; if so ...
	move.w	#twist_limit,d3		; read limit size
	cmp.w	d3,d0			; check
	bmi.b	.nth			; skip if not too high
	move.w	d3,twist.w		; otherwise, set to limit
	clr.w	twist_inc.w		; and stop further moves
	bra.b	.exit
.nth:	neg.w	d3			; now test lower limit
	cmp.w	d3,d0			; test
	bpl.b	.ntl			; branch if not too low
	move.w	d3,twist.w		; otherwise, set to limit
	clr.w	twist_inc.w		; and stop further moves
	bra.b	.exit

.stop:	move.w	d2,twist.w
	clr.w	twist_inc.w
	bra.b	.ntc

.ntl:	move.w	d0,twist.w

.ntc:	move.w	twdir_inc.w,d1		; read increment -
	beq.b	.ndc			; if none then quit

	move.w	twdir.w,d0		; read current twist qty
	move.w	twdir_tgt.w,d2		; read stopping point
	add.w	d1,d0			; apply increment
	sub.w	d0,d2			; get distance from stop point
	bpl.b	.nn			; make distance absolute
	neg.w	d2
.nn:	tst.w	d1			; make increment absolute
	bpl.b	.nn2
	neg.w	d1
.nn2:	cmp.w	d1,d2			; test 
	bpl.b	.nohit

.stop2:	clr.w	twdir_inc.w		; and stop further moves
	move.w	twdir_tgt.w,twdir.w
	bra.b	.exit
.nohit:	move.w	d0,twdir.w

.ndc:
.exit:	move.w	twist.w,(a4)+		; twist quantity
	move.w	twdir.w,(a4)+		; twist direction
	move.w	d5,(a4)+		; true track piece direction (always forward)
	move.b	view_limit.w,(a4)+
	move.b	track_flags.w,(a4)+	; note that cornering flag is entered at start of routine

	movem.l	(sp)+,d0-d3
	rts


twisttgt:
	move.b	(a0)+,d0		; read target twist quantity
	ext.w	d0
	move.w	d0,twist_tgt.w
	moveq.l	#1,d1			; standard step speed
	sub.w	twist.w,d0		; get total required change
	bgt.b	.nn
	blt.b	.ng
	moveq.l	#0,d1
.ng:	neg.w	d1
.nn:	move.w	d1,twist_inc.w

angle:	move.w	(a0)+,d0		; read target direction
	move.w	d0,twdir_tgt.w
	moveq.l	#16,d1			; standard step speed
	sub.w	twdir.w,d0
	asl.w	#6,d0			; which fixes to the 'true' sign
	bgt.b	.nn
	blt.b	.ng
	moveq.l	#0,d1
.ng:	neg.w	d1
.nn:	move.w	d1,twdir_inc.w

	rts

twistinc:
	move.b	(a0)+,d0
	ext.w	d0
	move.w	d0,twist_inc.w

	move.w	(a0)+,twdir_inc.w
	rts

viewset:move.b	(a0)+,view_limit.w 	; shortest subroutine so far !
	rts

flagops:move.b	(a0)+,d0
	move.b	d0,d1
	lsr.b	#4,d1		; op type
	bne.b	.nz

	bclr	d0,track_flags.w
	rts

.nz:	subq.b	#1,d1
	bne.b	.ns

	bset	d0,track_flags.w
	rts

.ns:	bchg	d0,track_flags.w
	rts


.end

notes on what is used in the second pass loop and subroutines :

d0 is used for command codes and then object codes once the command is decoded
d7 holds flags

a0 holds the command input stream address
a4 holds track segment data table address - twist values, direction, flags 
a6 holds subroutine table start address, and hence is free outside the main loop
a3 holds the subroutine address and so is also free outside main loop
a1 holds pointer to object data structure
a2 holds pointer to current position in current output stream
a5 holds pointer to extra data block when required (this applies to track segments only)

