;...................................
;: (c) 1993 Rebellion Software Ltd :
;: Hand coded by R.C.Dibley - 1993 :
;:.................................:

;===============================================
;	ROUTINES FOR MOVEMENT AND CONTROL
;===============================================
.text

hinin	equ	-650	; height when inside car

Collision::

	tst.b	reverse.w
	bne	.out		; quick escape if in reverse - this can't happen currently so won't cause problems
	move.l	colhlt.w,d0	; get halt distance
	beq	.nowt		; if there's none, then skip onwards - check the bounce distance
	cmp.l	splim.w,d0	; check - not really necessary I think
	bpl.b	.nowt
	move.l	d0,splim.w	; change if necessary
	move.l	speed,d1
;	cmpi.w	#$1400,d1
;	bmi.b	.nocra
	bsr	lose_control
	move.l	d0,-(sp)
	bsr	crash_play
	move.l	(sp)+,d0
	clr.w	steer.w
	move.l	#steer_amounts,steer_address.w
	move.l	#KEY_S0H|ANY_JOY|PAUSEx,d1	; allows only * and # and pause and steer - no ABC
	and.l	d1,joy_cur.w
	and.l	d1,joy_edge.w	; kill all player keys for one frame, which allows speeds etc to change correctly

	trace.w	#'Ht'
	trace.w	colrot.w
	
.nocra:	cmp.l	colmin.w,d0	; check if its worth allowing bounces
	bmi	.std		; if not, get out - but do drop the speed !

; if got here then must have been a crash, so want to drop speed even if no bounce found

move.l	colmin.w,d1		; get bounce distance
	beq	.std		; if none - quit but still cut speed
	cmp.l	splim.w,d1	; check distance
	bpl	.std
	bra.b	.dbnc		; do bounce if required

; this bit only happens if there is no major crash


.nowt:	move.l	colmin.w,d1	; get bounce distance
	beq	.out		; if none - quit, without cutting speed because neither colmin nor colhlt has a value
	cmp.l	splim.w,d1	; check distance
	bpl	.out		; likewise but dropping out because car hasn't actually hit anything
.dbnc:	move.l	d1,splim.w

	trace.w	#'Bc'
	trace.w	colrot.w

	bsr	crash_play
	clr.w	steer.w
	move.l	#steer_amounts,steer_address.w
	tst.b	crash_flag	; see if any already under way
	seq.b	start_crash	; only start crash if crash is not already active
	bne	.inc		; and if crash IS already active, get out quick

.nowt2:	move.w	colrot.w,d0	; a value in the range -8 to +8
	move.b	#2,crash_flag.w
	move.w	d0,d1		; copy for offsetting
	bpl.b	.nnn
	neg.w	d1
	subq.b	#1,crash_flag.w
.nnn:	bclr	#0,d1		; make even, because sizes are now doubled
;	asl.w	#1,d1
	lea	AnGlE-*-2(PC),a0
	move.w	(a0,d1.w),d1
	tst.w	d0		; recheck sign
	bpl.b	.nng
	neg.w	d1
.nng:	trace.w	#"Ba"
	trace.w	d1
	sub.w	d1,6(a1)	; traceable
;	move.w	#0,viewmove;test
	move.w	#8,sidecount.w	; and this limits the effect to 8 frames

	tst.w	viewmove.w
	beq.b	.nov
	add.w	d1,Genstr+2
	bra.b	.con
.nov:	add.w	d1,swing_camera_angle.w		; test - aiming to make camera slowly follow car after crashes
	asl.w	#4,d1
	add.w	d1,swing_camera.w
	asr.w	#4,d1

.con:	asl.w	#3,d1
;	move.w	d1,sideways.w	; this bit means that bigger angles result in a bigger bounce back factor
	tst.w	d1
	bpl.b	.p5
	add.w	#-1000,d1
	bra.b	.p1
.p5:	add.w	#1000,d1
.p1:	move.w	d1,sideways.w

; now decide on which crash to use

	moveq	#0,d1
	move.l	speed.w,d2
	lea	SpeedTable-*-2(PC),a0

.slp:	cmp.l	(a0)+,d2
	blt.b	.sf
	addq.w	#1,d1		; increment speed band counter
	bra	.slp

.sf:	tst.w	d1		; check for low speeds (d1=0)
	bne.b	.nsk

	asr.w	sideways.w	; halve the movement

.nsk:	lea	ColTable-*-2(PC),a0
	asl.w	#5,d1
	adda.w	d1,a0

	tst.l	viewmove
	beq.b	.ok
	adda.w	#ColTable2-ColTable,a0
.ok:	tst.w	d0
	bpl.b	.nn
	neg.w	d0
.nn:	bclr	#0,d0
;	asl.w	d0
;	move.w	#'Cx',(a6)+
;	move.w	(a0,d0.w),(a6)+
;	move.w	d0,(a6)+
;	move.w	d1,(a6)+
	move.w	(a0,d0.w),crash_type.w
	bne.b	.std
	clr.b	start_crash.w
	clr.b	crash_flag.w
.std:
;	move.l	speed,d1
;	asr.l	#4,d1
;	sub.l	d1,speed
	trace.l	#"Slow"
	move.w	revs.w,d1
	move.w	colrot.w,d0
	bpl.b	.nna
	neg.w	d0		; typically 45 degrees gives $10 here
.nna:	add.w	#20,d0		; changes range to 16 to 48
	btst.b	#1,track_flags.w	; check for corner
	beq.b	.noc
	add.w	#16,d0		; surge in slow down values
.noc:	mulu	d0,d1
	asr.l	#6,d1		; aimed such that 45 degrees gives 1/2 revs
	sub.w	d1,revs.w
	bpl.b	.out
	clr.w	revs.w

.out:	moveq.l	#0,d1
	move.b	d1,coldir.w	; clear record of collision direction
	move.w	d1,colrot.w	; and bounce amount
	move.l	d1,colmin.w	; and distance to collision
	move.l	d1,colhlt.w	; and distance to halt
				; thereby guaranteeing that no accidental follow throughs occur
.ex:	rts

; code for dealin with collision during crash sequences
.inc:	move.w	colrot.w,d0
	move.w	d0,d1
	bpl.b	.nni
	neg.w	d1
.nni:	bclr	#0,d1
	lea	AnGlE-*-2(PC),a0
	move.w	(a0,d1.w),d1
	tst.w	d0
	bpl.b	.ngi
	neg.w	d1
.ngi:	trace.w	#'Bk'
	trace.w	d1
	sub.w	d1,car_direction.w
	sub.w	d1,6(a1)	; traceable

	tst.w	viewmove.w
	beq.b	.nvm
	add.w	d1,Genstr+2
	bra.b	.nsc
.nvm:	add.w	d1,swing_camera_angle.w		; test - aiming to make camera slowly follow car after crashes
	asl.w	#4,d1
	add.w	d1,swing_camera.w
	asr.w	#4,d1

.nsc:	asl.w	#3,d1
	tst.w	d1
	bpl.b	.p4
	add.w	#-1000,d1
	bra.b	.p2
.p4:	add.w	#1000,d1
.p2:	move.w	d1,sideways.w

	bra	.std		; allow speed decrease as well

.if 0
AnGlE:	dc.w	$10/2,$20/2,$30/2,$40/2,$50/2,$60/2,$70/2,$80/2
	dc.w	$8c/2,$98/2,$a4/2,$b0/2,$bc/2,$c8/2,$d4/2,$e0/2
	dc.w	$ec/2,$f8/2,$104/2,$110/2,$11c/2,$128/2,$134/2,$140/2
	dc.w	$140/2,$140/2,$140/2,$140/2,$140/2,$140/2,$140/2,$140/2,$140/2
.endif
.if 1	; old version - re-introduced to try to make bounces align properly - since they stopped (!?)
AnGlE:	dc.w	$10,$20,$30,$40,$50,$60,$70,$80
	dc.w	$8c,$98,$a4,$b0,$bc,$c8,$d4,$e0
	dc.w	$ec,$f8,$104,$110,$11c,$128,$134,$140
	dc.w	$140,$140,$140,$140,$140,$140,$140,$140,$140
.endif
.if 0	; old version based on using tan instead of angle
AnGlE:	dc.w	10,20,40,58,76,91,105,117,128,138	; offsets to be applied for various bounce angles
	dc.w	146,153,160,166,171,176,180,184,188
	dc.w	194,197,199,201,204,206,207,209,211
	dc.w	212,214,215,216,217,218,219,220,221
.endif

SpeedTable:
;	dc.l	150<<4	; = 20 mph
	dc.l	200<<4	; = 27 mph
	dc.l	300<<4	; = 40 mph
	dc.l	600<<4	; = 81 mph
	dc.l	1000<<4	; = 135 mph
	dc.l	1400<<4	; = 189 mph
	dc.l	1600<<4	; = 216 mph
	dc.l	20000<<4	; = 2700 mph just for guaranteed cut off


ColTable:
	dc.w	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
	dc.w	 0, 0, 0, 0, 6, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0
	dc.w	 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0
	dc.w	 0, 5, 5, 5, 9, 9, 8, 8, 8, 8,16,16,16,16, 0, 0
	dc.w	 4, 4, 4, 9, 9, 9, 9, 9,10,10,10,16,16,16, 0, 0
	dc.w	 9, 9, 9, 9,10,10,10,10,16,16,16,16,16,16, 0, 0

ColTable2:
	dc.w	  0,  0,  0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12
	dc.w	  0,  0, 13, 13, 13, 12, 12, 13, 13, 13, 12, 12, 12, 12, 12, 12
	dc.w	  0, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12
	dc.w	 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 12, 12
	dc.w	 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 12, 12
	dc.w	 14, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13, 12, 12


;descriptions :
; 0 nothing
; 1 45 degree twist away from wall
; 2 faster 67.5 degree spin away from wall
; 3 fast full spin round - high speeds only
; 4 sideways roll with no direction change
; 5 rock and roll al over the place
; 6 gentle rock from side to side
; 7 four wheel roll
; 8 another mad sideways foll
; 9 nose up spin and drop
; 10 roll nose over tail
; 16 sideways roll and spin ending 90 degrees from original direction 
;===========================================================================
.if 0
Sideskid::

;	rts;test

	movem.l	d0-d3,-(a7)
.if 0
	move.l	speed,d0
	subi.l	#$1000,d0
	bmi.b	.noskd
	move.w	zspd,d2
	moveq.l	#0,d3
	sub.w	xspd,d3		; (z,-x) is right turn outside normal
	move.w	rota.w,d1
	bpl.b	.nn
	neg.w	d1
	neg.w	d2
	neg.w	d3
.nn:	subi.w	#3,d1
	bpl.b	.skid
.noskd:	moveq.l	#0,d2
	moveq.l	#0,d3
	bra.b	.done
.skid:	mulu	d1,d0
	asr.l	#6,d0
	mulu	d0,d2
	mulu	d0,d3
	asl.l	#2,d2
	asl.l	#2,d3
	swap	d2
	swap	d3
	ext.l	d2
	ext.l	d3
.done:	move.l	d2,skidx.w
	move.l	d3,skidz.w
.endif

	move.l	speed,d0
	subi.l	#$1000,d0
	bpl.b	.skid
	moveq.l	#0,d0
	moveq.l	#0,d1
	bra.b	.done
.skid:	asr.l	#2,d0
	move.l	d0,d1
	muls	xspd.w,d0
	muls	zspd.w,d1
	asl.l	#2,d0
	asl.l	#2,d1
	swap	d0
	swap	d1
	ext.l	d0
	ext.l	d1
.done:	move.l	d0,skidx.w
	move.l	d1,skidz.w

.ex:	movem.l	(a7)+,d0-d3 
	rts
.endif
;=====================================

Extra_Object_Switch::
	movea.w	#reset_list+StrO,a0
;	bchg	#5,6(a0)		; off mirror
;	lea	$10(a0),a0		; next object
;	bchg	#5,6(a0)		; off mirror
;	lea	$10(a0),a0		; next object
;	bchg	#5,6(a0)		; off cockpit
;	lea	$10(a0),a0		; next object
	bchg	#5,6(a0)		; off steering
;	not.l	viewmove.w		; moved back into viewpoint switcher
	rts

Wheel_switch::
	movea.l	spinad.w,a0
	bchg	#6,13(a0)		; toggle real position of in car wheels
	bchg	#6,13+Struclen(a0)
	rts

;fade_remove::
;	movea.w	fade_address.w,a0
;	tst.w	(a0)			; check for being clear and not changing
;	bne.b	.putin
;	movea.w	#reset_list+fadeo,a0
;	bset	#5,$06(a0)
;	bset	#5,$16(a0)
;	rts
;.putin:	movea.w	#reset_list+fadeo,a0
;	bclr	#5,$06(a0)
;	bclr	#5,$16(a0)
;	rts
;	


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

Request_viewpoint::
	movem.l	d0/d1/a0,-(sp)
	tst.w	d1			; check for sensible values first
	ble.b	.exit			; forget any negatives straight away
	cmpi.w	#6,d1			; test against highest expected value
	bgt.b	.exit			; if its outside range, abandon

	andi.l	#$FFFEFFFE,joy_cur.w	; clear */# as these are used for special functions
	move.l	joy_edge.w,d0		; joy_edge is the place where actual key presses are picked up
	andi.l	#$FFF2FF32,d0		; clear * and # here as well in case, and remove all the numeric presses
	lea	key_decode-*-2(PC),a0
	move.b	(a0,d1.w),d1		; convert the number into a joypad bit
	bset	d1,d0
	move.l	d0,joy_edge.w

.exit:	movem.l	(sp)+,d0/d1/a0
	rts

key_decode::				; starting from key 0
	dc.b	4,19,7,3,18,6,2,17,5,1

Viewpoint::		; controlling the viewpoint - and making it fixed 
			; to or trailing the car
	move.l	viewmove.w,d1		; get status

	move.l	joy_cur.w,d0
	btst.l	#KEY_HASH,d0
	bne	.none			; # means not a normal function
	btst.l	#KEY_STAR,d0
	bne	.none			; * likewise
	move.l	joy_edge.w,d0
	btst.l	#KEY_1,d0		; 1 is for INSIDE view
	beq.b	.not1
	tst.l	viewmove.w
	bne.b	.not1			; if viewmove = -1, then already inside

	not.l	viewmove.w
	clr.b	request_delayed.w
	bsr	Wheel_switch
;	movea.l	#ObjDef2,a0
;	bsr	ObjSetup
;	not.l	viewmove.w

;	tst.b	crash_flag.w
;	beq.b	.ok

;	move.w	car_direction.w,6(a1)
;	clr.b	crash_flag.w
	


.ok:	move.l	12(a1),Genstr+12	; move viewpoint into car
	move.l	20(a1),Genstr+20
	move.l	#hinin,Genstr+16
	moveq	#0,d0
	move.w	d0,trailer.w
	moveq	#1,d0
	move.w	d0,view_last.w
	move.b	d0,view_wants.w		; to prevent view being screwed up when leaving a tunnel 
	move.w	6(a1),d0		; get spin
	neg.w	d0
	move.w	d0,Genstr+2		; and set to minus it
	move.w	curr_twist.w,d0
	sub.w	d0,10(a1)
	clr.w	curr_twist.w
	bra	in_cockpit		; start doing in-cockpit things

.not1:  and.l	#KEY_23456,d0		; check for any other keys
	beq	.none
	tst.l	viewmove.w
	beq.b	.not01			; if not leaving cockpit

	clr.b	request_delayed.w
	not.l	viewmove.w
	bsr	Wheel_switch
;	movea.l	#ObjDef2,a0
;	bsr	ObjSetup
;	not.l	viewmove.w
	bset	#31,d0			; flag for special ops

.not01:	moveq.l	#2,d1			; to get key number
	btst.l	#KEY_2,d0
	bne.b	.f1
	addq.l	#1,d1
	btst.l	#KEY_3,d0
	bne.b	.f1
	addq.l	#1,d1
	btst.l	#KEY_4,d0
	bne.b	.f1
	addq.l	#1,d1
	btst.l	#KEY_5,d0
	bne.b	.f1
	addq.l	#1,d1			; don't test 6 - has to be pressed !

.f1:	st.b	fast_fall.w		; register as a quick request when computer asks for it
	tas	comp_gen_view.w		; if its a computer request, don't adjust humans desires
	beq.b	.nc
	move.b	d1,view_wants.w
	clr.b	fast_fall.w

.nc:	move.b	view_limit.w,d0
	beq.b	.nf			; no fix if there's no special limit

	cmp.b	d1,d0			; test against limit
	bge.b	.nf			; if limit is big enough then don't change view number
	move.b	d0,d1			; allow highest permitted view when higher one requested

.nf:	bsr	read_view_d1		; converts d1 (view number) into d1,d2 (target height and distance)

	btst.l	#31,d0			; check in case its coming out of cockpit
	beq.b	.f2

	bsr	install_view
	bra	vcontrol

.f2:	bsr	drift_to_view

.none:	tst.l	viewmove.w
	bne	in_cockpit
	bra	vcontrol

reduc set 400

read_view_d1:
	move.w	d1,view_last.w
	asl.w	#2,d1
	lea	view_table-8-2-*(PC),a0
	adda.w	d1,a0
	move.w	(a0)+,d1		; read viewing height
	ext.l	d1			; extend
	move.w	(a0),d2			; read prespective modified distance
	rts

install_view:
	move.l	d1,Genstr+16
	move.w	d2,trailer.w
	move.b	#0,drift_count.w
	rts

drift_to_view:
	move.b	#16,drift_count.w
	sub.l	Genstr+16,d1	; height change
	sub.w	trailer.w,d2	; distance change

	tst.b	fast_fall.w
	beq.b	.ok		; check for quick change request
	asl.l	#1,d1		; double step size by doubling distance
	asl.w	#1,d2
	move.b	#8,drift_count.w; and use half the number of steps

.ok:	asr.l	#4,d1
	asr.w	#4,d2
	move.l	d1,height_step.w
	move.w	d2,trail_step.w
	rts

view_table::	; 5 * height,trail
	dc.w	-800,2200-reduc	; 2 
	dc.w	-1600,2725-reduc	; 3 
	dc.w	-3000,4600-reduc	; 4 
	dc.w	-3000,3600-reduc	; 5 
	dc.w	-5500,6500-reduc	; 6 
	dc.w	-400,3600-reduc		; finish - only available at end for panning camera

set_finish_viewpoint::
	move.w	view_last.w,-(sp)
	tst.l	viewmove.w		; see if the camera is in the car
	beq.b	.ok
	moveq	#5,d1			; if it is, force the viewpoint to view 5 instantly
	bsr	read_view_d1
	bsr	install_view
	not.l	viewmove.w		; and also release camera,
	clr.b	request_delayed.w	; and switch off overlay
	bsr	Wheel_switch
.ok:	moveq	#7,d1			; and set up a drift to the final viewpoint
	bsr	read_view_d1
	clr.b	fast_fall.w
	bsr	drift_to_view
	move.w	(sp)+,view_last.w
	rts

in_cockpit:
	; to get here implies that the view is in-cockpit, so apply apropriate calculations to the wheel co-ordinates
	movea.l	spinad.w,a2
	move.w	6(a1),d0	; get direction of car
	move.w	rota.w,d1	; amount of steering
	asl.w	#1,d1		; because a max of +/-32 would be 11 degrees - to small
	sub.w	d1,d0
	move.w	d0,d1
	add.w	#$100,d0
	sub.w	#$100,d1
	move.w	d0,6(a2)
	move.w	d1,6+Struclen(a2)	; set left and right rotations appropriately

	move.w	xspd.w,d0
	move.w	zspd.w,d1
	move.w	d0,d2
	move.w	d1,d3
	muls	axle_distance.w,d0	; = L xspd
	muls	axle_distance.w,d1	; = L zspd
	muls	#whljn,d2
	muls	#whljn,d3

;	move.w	#$C000,d4
	asl.l	#2,d0
	asl.l	#2,d1
	asl.l	#2,d2
	asl.l	#2,d3
	swap	d0
	swap	d1
	swap	d2
	swap	d3
	ext.l	d0
	ext.l	d1
	ext.l	d2
	ext.l	d3

	move.l	12(a1),d4		; car x-co-ordinate
	add.l	d0,d4
	move.l	d4,d5
	sub.l	d3,d4
	add.l	d3,d5
	move.l	d4,12(a2)		; wheels x-coordinates
	move.l	d5,12+Struclen(a2)
	move.l	20(a1),d4		; z co-ordinate
	add.l	d1,d4
	move.l	d4,d5
	add.l	d2,d4
	sub.l	d2,d5
	move.l	d4,20(a2)		; wheels z-coordinates
	move.l	d5,20+Struclen(a2)
	rts	; 

vcontrol:	; since the movement being applied to the car 
;isn't being applied to the viewpoint, we need to deal with it here

;	tst.b	camera_lock_switch
;	beq	.exit
;	tst.b	static_view.w
;	bne	exit

	move.w	6(a1),d0	;test	; remove the overshoot
	add.w	swing_camera_angle.w,d0
	neg.w	d0		;test
	move.w	d0,Genstr+2	;test	; write out modified angle

	move.w	framect.w,d1

	tst.b	drift_count.w		; in case already moving viewpoint to another location
	beq.b	Viewset			; if not then skip this bit

	move.w	trailer.w,d0		; get current distance
	add.w	trail_step.w,d0		; add step amount
	move.w	d0,trailer.w		; write back
	move.l	Genstr+16,d0		; similarly for height
	add.l	height_step.w,d0
	move.l	d0,Genstr+16
	subq.b	#1,drift_count.w	; decrement count

Viewset::				; made into separately callable routine because need to initialise to an outside view
	move.w	Genstr+2,d0		; read direction camera is facing
	bsr	get_sine		; to work out the sine and cosine
	move.w	trailer.w,d0		; length to trail at
	neg.w	d0			; because we want to be behind the car
	muls	d0,d1			; convert sine / cosine into positional offset
	muls	d0,d2
	asl.l	#2,d1			; cut down by 14 bits
	asl.l	#2,d2			; by first pushing up a bit
	swap	d1			; then swapping
	swap	d2
	ext.l	d1			; make sure signs stay
	ext.l	d2

	add.l	12(a1),d1	; add in the current car positions
	add.l	20(a1),d2
	move.l	d1,Genstr+12
	move.l	d2,Genstr+20	; and write out to viewpoint

	rts	

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

Spark_follow::				; routine to make spark anim follow the car at a fixed distance behind
	behind set 1100			; distance to back of car and now also the smoke anim (in same place)
	movem.l	a2-a4,-(sp)		; and the grass !
	movea.l	sparkad.w,a2
	movea.l	smokead.w,a3
	movea.l	grassad.w,a4
	move.w	#-behind,d0
	move.w	d0,d1
	muls	xspd.w,d0
	muls	zspd.w,d1
	asl.l	#2,d0
	asl.l	#2,d1
	swap	d0
	swap	d1
	ext.l	d0
	ext.l	d1

	add.l	12(a1),d0
	add.l	20(a1),d1
	move.l	d0,12(a2)
	move.l	d0,12(a3)
	move.l	d0,12(a4)
	move.l	d1,20(a2)
	move.l	d1,20(a3)
	move.l	d1,20(a4)
	movem.l	(sp)+,a2-a4
	rts

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

Horizon_height::			; prepare value for use both in screen clear and object list
	move.w	Genstr,d1		; read current tilt value
	bmi.b	.ko
	moveq	#0,d1
.ko:	neg.w	d1			; negate
	asl.w	#1,d1			; double - for use in lookup
	cmp.w	#$9e,d1
	ble.b	.ok
	move.w	#$9e,d1
.ok:	movea.l	#hadj_tab,a0		; get table address - held in math.s
	move.w	(a0,d1),d1		; read horizon adjustment value
	neg.w	d1			; negate
	asl.w	#1,d1			; and double because screen is top to bottom and value of 2 per line
	add.w	#ScnSt+50,d1		; add standard horizon level
	move.w	d1,Horizon_h.w		; store for later use in several places

	bpl.b	.ok2
	clr.w	Horizon_h.w
.ok2:	rts

Error0:	message	"Fatal horizon height failure."

exob	set	16

Horizon::			; deal with shifting the horizon strip, one pixel per degree of y rotation
;	rts;test
				; which means blocks of 4 in the address (1 phrase = 4 pixels) and 2 in the pixel offset
.if 0
	tst.b	display_mode.w
	bne	.whl

	movea.w	#reset_list+Horz,a0	; for ease of use

	moveq.l	#0,d0
	move.w	Genstr+2,d0		; view direction - the only significant effector on the horizon
	asl.l	#1,d0			; double because two bytes per pixel
	move.l	d0,d1			; copy because need both an address offset and a pixel adjustment
	and.l	#$FF8,d0		; limit to 1023*4 - beyond that wraps around - and round off last bit(s) to keep phrase aligned
	and.l	#4,d1			; non-phrase adjustment is kept here for use in the pixel offset
	add.l	#bit_store,d0		; object address
	asl.l	#8,d0			; scale up for the object reset table entry
	moveq.l	#0,d2			; clear out in readiness
	move.w	2(a0),d2		; get the current entry in the object reset table (lower word only)
	and.w	#$7FF,d2		; remove excess data
	or.l	d2,d0
	move.l	d0,(a0)			; sets start address [for major offsets]

	move.w	8(a0),d0		; upper word of second phrase of first entry in object list - FIRSTPIX
	asl.w	#3,d1			; move up into correct place (value is now 0 or 32 - which is the bit at which to start !)
	and.w	#$0001,d0		; extract any previous setting - only one bit belongs to other data
	or.w	d1,d0			; modify to take new value
	move.w	d0,8(a0)		; write back in

	move.w	Horizon_h.w,d1		; get height adjustment
	asl.w	#3,d1
	move.w	#$c007,d0		; get mask
	and.w	6(a0),d0		; read from object definition - YPOS
	or.w	d1,d0			; put new value in place
	move.w	d0,6(a0)		; write adjusted version back
.endif

.whl:	tst.b	viewmove.w		; is the viewpoint mobile ?
	beq.b	.exit

; STEERING WHEEL AND WHEEL MOVEMENT

	movea.w	#reset_list+StrO,a0	; steering wheel display object
	move.w	rota.w,d0		; get the current amount of rotation 
	ext.l	d0			; make long
	addq.l	#1,d0			; make -1 go to zero - so no flicker
	bmi.b	.left			; skip if going left

	bset.b	#5,10(a0)		; set to display image reversed
	move.w	#$c10c,14(a0)		; and set new horizontal offset
	bra.b	.nomir

.left:	bclr.b	#5,10(a0)		; set to display image not reversed
	move.w	#$c034,14(a0)		; and reset horizontal offset

.nomir:	
;	move.w	wheel_rotate,d0;test	; get the current amount of rotation 	
	subq.l	#1,d0			; remove adjustment for flicker
	bpl.b	.nn			; need to make absolute
	neg.l	d0			; since negative, make +ve
.nn:	asr.l	#1,d0			; lose low bit - puts in range 0-15
	cmp.b	#13,d0			; test against maximum number of animation frames
	bmi.b	.nolim
	moveq.l	#13,d0			; adjust if over limit
.nolim:	swap	d0			; shift into upper half
	asr.l	#1,d0			; effect the multiply by $8000
	add.l	#steerwh,d0		; calculate new animation address
	asl.l	#8,d0			; shift up for writing into object list
	moveq.l	#0,d1
	move.w	2(a0),d1		; get phrase 1,high long,low word, of object
	and.w	#$7FF,d1		; remove current address
	or.l	d0,d1			; replace with new one
	move.l	d1,(a0)			; write back including high part into object list

	bsr	blit_cockpit

.exit:	rts
		
;===============================================================

;controls whilst paused - if pause has been pressed this is the only loop which is processed.

Pause::	move.l	joy_edge.w,d0
	move.w	effects_volume.w,old_eff_vol.w
	btst.l	#PAUSE,d0
	bne.b	.paws		; if pressed then don't exit
	rts

.paws:	movem.l	d0/d3-d5,-(sp)
	st.b	in_pause.w
	bsr	music_palette	; make sure this ready well in advance of being required !
	tst.l	VOLUME
	seq	fake_zero.w	; make sure silence occurs if it should
	move.l	VOLUME,d3	; this will have high word empty, so its ok
	swap	d3
	move.w	counter.w,d3
	move.l	key_mask.w,mask2.w
	moveq	#-1,d0
	move.l	d0,key_mask.w
	moveq	#0,d0
	move.l	d0,VOLUME
	jsr	StopMusic
	bsr	kill_low8	; zap the music channels in case they were being used for drones
	bsr	effect_save_kill
	move.l	race_timer.w,d5
;	bsr	show_pause
	movea.w	#reset_list+pauso,a0
	bclr	#5,6(a0)	; switch pause sprite on

.paw3:	bsr	sync		; wait a frame before re-testing
	bsr	readpad

	move.l	joy_edge.w,d0
	and.l	#(1<<OPTION)|(1<<FIRE_A)|(1<<FIRE_B),d0
	beq	.nopt

	movea.w	#reset_list+soundo,a0
	bclr	#5,6(a0)	; switch configure on
	bclr	#5,22(a0)	; and bar 1
	bclr	#5,38(a0)	; and bar 2
	bclr	#5,54(a0)	; and box

	movem.l	d0-d2/a1-a2,-(sp)
	swap	d3
	moveq	#0,d0
	move.w	d3,d0
	move.l	d0,VOLUME
	swap	d3

	move.l	joy_edge.w,d0	; B triggers fx, anything else gets music
	btst.l	#FIRE_B,d0
	beq.b	.mum

.fxm:	movea.w	#FX_ph,a1
	movea.w	#effects_volume,a2
	sfx	engine,$4fff,$3fff,8
	move.l	d0,engine.w
	st.b	sound_mods.w
	bsr	lite_1
	bra.b	.sm

.mum:	movea.w	#Music_ph,a1
	movea.w	#music_volume,a2
	clr.b	sound_mods.w
	bsr	lite_2
	tst.b	music.w
	beq.b	.sm
	tst.w	(a2)
	beq.b	.sm
	jsr	RestartMusic
	move.w	last_pitch.w,$f10002	

.sm:		
.lpfx:	bsr	readpad
	bsr	music_palette
	bsr	update_bars	; display correct values
	bsr	sync
	bsr	ResetKey	; allow reset to happen whilst in fx adjust
	bsr	FX		; allows 0 to switch sounds on/off
	bsr	check_n_change	; check for finish keys and change keys
	beq	.lpfx		; returns non-zero to declare finished

	clr.b	sound_mods.w
	move.l	VOLUME,d0
	swap	d3
	move.w	d0,d3
	swap	d3

	jsr	StopMusic	; should be off
	jsr	KillAll		; knock off all sounds, so they have to be re-started
	bsr	cart_refill

	movem.l	(sp)+,d0-d2/a1-a2

	movea.w	#reset_list+soundo,a0
	bset	#5,6(a0)	; switch configure off, since we must have either hit option (to return to pause)
	bset	#5,22(a0)	; and bar 1			or pause to return to the game
	bset	#5,38(a0)	; and bar 2
	bset	#5,54(a0)	; and box

	; don't have to check anything, cos if you pressed pause, it will still be registered,
	; and so the follow on code will drop you out

.nopt:	move.l	joy_cur.w,d0
	btst.l	#KEY_3,d0
	beq.b	.not13
	btst.l	#KEY_1,d0
	beq.b	.not13

	movea.w	#reset_list+pauso,a0
	bset	#5,6(a0)

.not13:	bsr	ResetKey	; allow reset to happen whilst in Pause
	move.l	joy_edge.w,d0
	btst.l	#PAUSE,d0	; otherwise wait until pressed again
	beq	.paw3

	movea.w	#reset_list+pauso,a0
	bset	#5,6(a0)

	move.w	d3,counter.w
	clr.w	d3
	swap	d3
	move.l	d3,VOLUME

	move.l	d5,race_timer.w
	move.l	mask2.w,key_mask.w
.noz:	tst.w	effects_volume.w
	beq.b	.nr		; no restore if zero, as they won't get updated ever !
	bsr	effects_restore

	tst.w	old_eff_vol.w
	bne.b	.nr			; if effects were zero, then have to restart any effects

	sfx	engine,$2fff,$3fff,8	; 8 bit sample
	move.l	d0,engine.w

.nr:	tst.b	music.w
	beq.b	.nomuc
	tst.w	music_volume.w
	beq.b	.nomuc
	jsr	RestartMusic
	move.w	last_pitch.w,$f10002
	bra.b	.ex
.nomuc:	bsr	start_drones	; make sure any drone car sounds restart at right volume as well
.ex:	clr.b	in_pause.w
	movem.l	(sp)+,d0/d3-d5
	rts

do_a_bar::
	cmp.w	#32,d0
	bgt.b	.nzr
	moveq	#0,d1
	move.l	d1,4(a0)
	bra.b	.nor
.nzr:	sub.w	#32,d0
	moveq	#-1,d1
	move.l	d1,(a0)+
.nor:	moveq	#-1,d1
	neg.w	d0
	add.w	#32,d0
	asl.l	d0,d1
	move.l	d1,(a0)
	rts

music_palette:
	moveq	#22,d1
	tst.b	music.w
	beq.b	.off
	move.w	#$f080,d0
	bra.b	.do
.off:	move.w	#$f040,d0
.do:	bsr	CLUT_write
	rts

update_bars:
	movea.w	#Music_ph,a0
	move.w	music_volume.w,d0
	bsr	do_a_bar

	movea.w	#FX_ph,a0
	move.w	effects_volume.w,d0
	bsr	do_a_bar

	rts

check_n_change:
	cmpa.w	#effects_volume,a2
	bne.b	.noev
	mod_sfx	engine.w,#$88,#$4fff	; 8 bit sample - this has to be done every time, to ensure the volume updates instantly

.noev:	move.l	joy_edge.w,d0
	and.l	#(1<<JOY_UP)|(1<<JOY_DOWN),d0
	beq.b	.nosw

	cmpa.w	#music_volume,a2
	beq.b	.nomu
	movea.w	#music_volume,a2
	bsr	lite_2
	kill_sfx engine.w
	tst.b	music.w
	beq.b	.nosw
	jsr	RestartMusic
	move.w	last_pitch.w,$f10002
	clr.b	sound_mods.w
	bra.b	.nosw

.nomu:	movea.w	#effects_volume,a2
	jsr	StopMusic
	bsr	lite_1
	sfx	engine,$4fff,$3fff,8	; 8 bit sample - this has to be done just once, and then modified every frame
	st.b	sound_mods.w
	move.l	d0,engine.w

.nosw:	move.l	joy_edge.w,d0
	btst.l	#FIRE_A,d0
	beq.b	.nomus

	movea.w	#music_volume,a2
	kill_sfx engine.w
	bsr	lite_2
	tst.b	music.w
	beq.b	.nomus
	jsr	RestartMusic
	clr.b	sound_mods.w
	move.w	last_pitch.w,$f10002

.nomus:	btst.l	#FIRE_B,d0
	beq.b	.nofx

	movea.w	#effects_volume,a2
	jsr	StopMusic
	bsr	lite_1
	sfx	engine,$4fff,$3fff,8	; 8 bit sample - this has to be done just once, and then modified every frame
	st.b	sound_mods.w
	move.l	d0,engine.w

.nofx:	move.l	joy_cur.w,d0
	btst.l	#JOY_RIGHT,d0
	beq.b	.noad

	move.w	(a2),d1
	addq.w	#1,d1
	bmi.b	.noad
	cmp.w	#63,d1
	bgt.b	.noad
	move.w	d1,(a2)

	cmp.w	#1,d1
	bne.b	.noad			; this is what happens if go from zero to non-zero
	cmpa.w	#effects_volume,a2	; check if doing effects
	bne.b	.noad
	sfx	engine,$4fff,$3fff,8	; restart effect
	st.b	sound_mods.w
	move.l	d0,engine.w

.noad:	btst.l	#JOY_LEFT,d0
	beq.b	.nosu

	move.w	(a2),d1
	subq.w	#1,d1
	bmi.b	.nosu
	cmp.w	#63,d1
	bgt.b	.nosu
	move.w	d1,(a2)

.nosu:	moveq	#0,d0
	cmpa.w	#music_volume,a2
	bne.b	.mof
	move.w	music_volume.w,d0		; read music volume, because it has to be converted for the DSP
.mof:	bsr	convert_to_volume

.ex:	move.l	joy_edge.w,d0
	and.l	#(1<<PAUSE)|(1<<OPTION),d0
	rts

convert_to_volume::			; takes value in d0 (0-63) and converts it to a proper volume, and puts it in place
	tst.b	fake_zero.w
	beq.b	.nrm
	tst.w	d0
	beq.b	.ex
	tst.b	music.w
	beq.b	.ex
	jsr	RestartMusic
	move.w	last_pitch.w,$f10002
	clr.b	fake_zero.w

.nrm:	tst.w	d0
	beq.b	.kill
	bsr	do_real_volume
.ex:	rts

.kill:	jsr	StopMusic
	st.b	fake_zero.w
	rts

lite_1:	movem.l	d0/d1,-(sp)
	move.w	#hilio,d0
	move.w	#108,d1
	bsr	set_ypos
	movem.l	(sp)+,d0/d1
	rts

lite_2:	movem.l	d0/d1,-(sp)
	move.w	#hilio,d0
	move.w	#140,d1
	bsr	set_ypos
	movem.l	(sp)+,d0/d1
	rts

do_real_volume::
	movem.l	d0-d2/a0,-(sp)
	move.w	music_volume.w,d0
	move.w	d0,d2
	and.w	#6,d0			; trashes d0-d2-a0
	lea	volume_convert_table-*-2(PC),a0
	moveq	#0,d1
	move.w	(a0,d0),d1
	move.l	d2,d0
	asr.w	#3,d0				; reduces the 0-63 down to 0-7
	neg.w	d0				; turn into -7 to 0	
	addq.w	#7,d0				; and then into 0 to 7
	asr.w	d0,d1				; then shift the table value down by 0-7 - thus keeping volume sensible
	move.l	d1,VOLUME			; which unfortunately means the lowest 4 settings are 0, but what the hell.
	movem.l	(sp)+,d0-d2/a0
	rts

volume_convert_table:
	dc.w	$4c1a
	dc.w	$5a80
	dc.w	$6b9e
	dc.w	$7fff

ResetKey::
	movem.l	d0-d1,-(sp)
	move.l	joy_cur.w,d1
	move.w	resetct.w,d0
	beq.b	.new
	btst	#KEY_HASH,d1
	beq.b	.zero
	btst	#KEY_STAR,d1
	bne.b	.add
.zero:	moveq.l	#0,d0
	bra.b	.ex2
.new:	btst	#KEY_HASH,d1
	beq.b	.notr
	btst	#KEY_STAR,d1
	beq.b	.notr
.add:	add.w	framect.w,d0
	cmp.w	#10,d0
	bge.b	goreset
.ex2:	move.w	d0,resetct.w
.notr:	movem.l	(sp)+,d0-d1
	rts


goreset::
	bsr	VidOff		; need this to prevent crash bugs
.if use_sound=1
	tst.b	music.w
	beq.b	.ins
	stop_music
.ins:	kill_all
	clr.b	engine_on.w	; prevent drones playing outside game !
.endif

	jmp	restart_address

VarDis::		; switch between variables to display
		; choices required :	framect		video frames per actual frame
		;			trailer		distance of outside view from car
		;			polcnt		polygons drawn per frame
		;			polcnt2		polygons analysed per frame
		;			shapect		solid objects drawn per frame
		;			shapect2	solid objects analysed per frame

	move.l	joy_cur.w,d0		; require a # with it otherwise no change
	btst.l	#KEY_HASH,d0		
	beq.b	.out
	move.l	joy_edge.w,d0
	btst.l	#JOY_DOWN,d0		; 1 is down, 3 is up list
	beq.b	.not1
	moveq.l	#2,d0
	bra.b	.adr

.not1:	btst.l	#JOY_UP,d0
	beq.b	.out
	moveq.l	#-2,d0
.adr:	lea.l	Varlist-.adr-2(PC),a0	; makes a0 point to Varlist
	add.w	d0,disnum.w
	bpl.b	.ok			; if +ve then get address to test
	moveq.l	#listend-Varlist-2,d0	; otherwise, disnum should be pointing at last item in list
	move.w	d0,disnum.w
.ok:	adda.w	disnum.w,a0
	move.w	(a0),d0
	bne.b	.stvar			; if non zero then is valid
.bdr:	lea.l	Varlist-.bdr-2(PC),a0	; otherwise, reset address
	move.w	d0,disnum.w		; and clear disnum
	move.w	(a0),d0
.stvar:	move.w	d0,disvar.w		; write out correct value for use by display routine
.out:	rts

Varlist::	dc.w	ZBase
		dc.w	axle_distance
;		dc.w	collision_test
		dc.w	roll_front
		dc.w	framect
		dc.w	trailer
;		dc.w	polcnt
;		dc.w	polcnt2
;		dc.w	shapect
;		dc.w	shapect2
;		dc.w	colnct
listend:	dc.w	0

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

FX::	movem.l	d0-d2/a0,-(sp)

.if 0
	move.l	joy_cur.w,d0
	btst.l	#KEY_HASH,d0		; require NO # with these
	bne	hash
	btst.l	#KEY_STAR,d0		; require a * instead
	beq	nostar
	move.l	joy_edge.w,d0
	btst.l	#KEY_1,d0		; key 1 to switch rain on and off
	beq.b	.nora
	move.l	CLUT+4,d0		; these not being converted to interrupt system as they are not part of game
	eori.b	#$E8,d0
	moveq	#4,d1
	bsr	CLUT_write		; place change into palette table

.nora:	btst.l	#KEY_7,d0		; *7 used for triggering sparks
	beq.b	.nosp
	addq.w	#1,spark_count.w

.nosp:	btst.l	#KEY_5,d0
	beq.b	.nobor
	move.w	#$180,d1
	move.w	d1,HDB1
	move.w	d1,HDB2

.nobor:	btst.l	#FIRE_A,d0
	beq.b	.noRAM
	move.l	16(a7),d1	; read return address
	cmp.l	#$802000,d1
	blt.b	.noRAM		; don't fix it if its already there

	sub.l	#$802000,d1
	add.l	#code_space,d1
	move.l	d1,16(a7)

.noRAM:	btst.l	#FIRE_C,d0
	beq.b	.noROM

	move.l	16(a7),d1	; read return address
	cmp.l	#$802000,d1
	bgt.b	.noROM		; don't fix it if its already there

	add.l	#$802000,d1
	sub.l	#code_space,d1
	move.l	d1,16(a7)

.noROM:
.if Trace_on=1
	btst.l	#OPTION,d0
	beq.b	.nomark

	trace.l	#'Mark'
	trace.l	#0
.endif
.nomark:
.exit:	movem.l	(sp)+,d0-d2/a0
	rts

HaltMessage:	message 'User Halt.'

nostar:;	move.l	joy_cur.w,d0
;	btst.l	#KEY_7,d0
;	beq.b	.nole
;	move.w	under_user.w,d1
;	subq.w	#1,d1
;	sub.w	#2500,understeer.w
;	cmp.b	#1,d1
;	bpl.b	.ok2
;	addq.w	#1,d1
;	add.w	#2500,understeer.w
;.ok2:	move.w	d1,under_user.w


.nole:	btst.l	#KEY_9,d0
	beq.b	.nori
	btst.l	#KEY_6,d0
	beq.b	.nostp
	bsr	HaltMessage
.nostp:;	move.w	under_user.w,d1
;	addq.w	#1,d1
;	add.w	#2500,understeer.w
;	cmpi.w	#7,d1	;23
;	bmi.b	.ok
;	subq.w	#1,d1
;	sub.w	#2500,understeer.w
;.ok:	move.w	d1,under_user.w
	
.nori:

.endif

.if use_sound=1
	move.l	joy_edge.w,d0
	btst.l	#KEY_0,d0
	beq.b	.exit

	tst.b	music.w
	beq.b	.puton

	clr.b	music.w
	jsr	StopMusic		; stop it allways, just in case
	tst.b	sound_mods.w
	bne.b	.exit			; but only mess with other stuff if sure

	tst.b	engine_on.w
	beq.b	.exit
	tst.b	in_pause.w
	beq.b	.exit
	jsr	start_drones
	bra.b	.exit

.puton:	st.b	music.w
	tst.b	sound_mods.w
	bne.b	.exit
	tst.l	music_volume.w
	beq.b	.exit

	jsr	RestartMusic
	move.w	last_pitch.w,$f10002

.endif

.exit:	movem.l	(sp)+,d0-d2/a0
	rts

.if 0
hash:			; these keys require a # with them

	move.l	joy_cur.w,d0
	btst.l	#KEY_0,d0
	beq.b	.nomde

	bchg	#7,display_mode.w
	beq.b	.new

.old:	movea.l	#ObjDef2,a0	; address of unpacked list (input)
	bra.b	.st
.new:	movea.l	#ObjDef7,a0
.st:	bsr	ObjSetup	


.nomde:	tst.b	drift_count.w	; make sure viewpoint isn't being moved
	bne	.exit
	move.w	program_area.w,d0
	cmpi.w	#1,d0
	bne	.exit
	move.l	joy_edge.w,d0
	btst.l	#KEY_1,d0	; #1,2,3 for the three perspectives
	beq.b	.n1
	move.w	#256,d1		; perspective value
	moveq.l	#0,d2		; perspective number
	bra.b	.apply

.n1:	btst.l	#KEY_2,d0	; #1,2,3 for the three perspectives
	beq.b	.n2
	move.w	#128,d1		; perspective value
	moveq.l	#1,d2		; perspective number
	bra.b	.apply

.n2:	btst.l	#KEY_3,d0	; #1,2,3 for the three perspectives
	beq.b	.exit
	move.w	#96,d1		; perspective value
	moveq.l	#2,d2		; perspective number

.apply:	move.w	d1,ZBase.w
	move.w	d2,perspective.w
	RunGPU	12	

.dis:	move.w	program_area.w,d0
	beq.b	.exit
	subq.w	#1,d0
	beq.b	.menu
	subq.w	#1,d0
	bne.b	.exit

	tst.l	viewmove.w
	bne.b	.exit

	move.w	view_last.w,d1
	asl.w	#3,d1
	lea	view_table-14-2-*(PC),a0
	adda.w	d1,a0
	asl.w	#1,d2
	adda.w	d2,a0
	move.w	(a0),trailer.w

.exit:	move.l	joy_edge.w,d0
	btst.l	#KEY_3,d0
	beq.b	.out

	tst.b	rotmode.w
	seq.b	rotmode.w

.out:	movem.l	(sp)+,d0-d2/a0
	rts

.menu:	lea	menu_dist_table-2-*(PC),a0
	asl.w	#2,d2
	adda.w	d2,a0
	move.l	(a0)+,Genstr+60		; this is a menu only code section
	movem.l	(sp)+,d0-d2/a0
	rts

menu_dist_table:
	dc.l	$a00,$700,$680
.endif
.if 0
read_digit:			; takes d0 input (key presses) and outputs d1 = N if any digit pressed or -1 elsewise
.endif	

lose_control:
	move.l	d0,-(sp)
	tas	out_of_control.w	; temporarily allow changes from multiple 'halts'
;	bne.b	slowly
border	red
	bsr	find_near_to_car	; find an appropriate point near to car
border	black
	move.l	#RESET_STEER|(1<<KEY_0)|PAUSEx,key_mask.w	; prevent user having control - and thus Dennis' code from messing things up !
;	move.w	#10,count_down.w	; no count down now - instant bounce back onto track
	move.b	#8,reposition_count.w

	move.l	xstart.w,d0
	sub.l	12(a1),d0
	tst.b	god.w
	bne.b	.onx
	tst.l	d0
	bpl.b	.sub1
	add.l	#2000,d0
.sub1:	sub.l	#1000,d0
	asr.l	#1,d0			; step size changed from 1/8 to 1/4, because going to use shrinking drift
.onx:	move.l	d0,xstep.w		; previously used by track generator and hence free now

	move.l	zstart.w,d0
	sub.l	20(a1),d0
	tst.b	god.w
	bne.b	.onz
	tst.l	d0
	bpl.b	.sub2
	add.l	#2000,d0
.sub2:	sub.l	#1000,d0
	asr.l	#1,d0
.onz:	move.l	d0,zstep.w		; previously used by track generator and hence free now

.exit:	move.l	(sp)+,d0
	rts

slowly:	tst.w	colrot.w
	bpl.b	.p5
	move.w	#-500,d1
	bra.b	.p1
.p5:	move.w	#500,d1
.p1:	move.w	d1,sideways.w
	move.w	#8,sidecount.w
.exit:	move.l	(sp)+,d0
	rts

use_control::
	tst.b	out_of_control.w	; check first
	beq.b	.exit
	border	-1;,1
;	move.w	count_down.w,d0		; removed to make instant response
;	sub.w	framect.w,d0
;	bmi.b	.trigger
;	move.w	d0,count_down.w
;.exit:	rts
.trigger:
	border	sand;,1
	subq.b	#1,reposition_count.w
	blt.b	.release
	move.l	xstep.w,d0
	add.l	d0,12(a1)
	and.l	viewmove.w,d0
	add.l	d0,Genstr+12
	tst.b	god.w
	bne.b	.sk1
	asr.l	#1,d0
.sk1:	move.l	d0,xstep.w

	move.l	zstep.w,d0
	add.l	d0,20(a1)
	and.l	viewmove.w,d0
	add.l	d0,Genstr+20
	tst.b	god.w
	bne.b	.sk2
	asr.l	#1,d0
.sk2:	move.l	d0,zstep.w
.exit:	rts
.release:
	tst.b	crash_flag.w
	bne.b	.ex
	clr.b	out_of_control.w
	clr.b	god.w
	moveq.l	#-1,d0
	move.l	d0,key_mask.w		; allow user control again
.ex:	rts


Shadow::
	movem.l	d0/a0-a1,-(sp)

	moveq	#num_cars-1,d0
	movea.l	shadad.w,a0		; read address of cars shadow object
	movea.l	carsad.w,a1

.lp:	move.w	6(a1),6(a0)		; make car and shadow direction and x and z the same
	move.l	12(a1),12(a0)
	move.l	20(a1),20(a0)
	lea	$20(a0),a0
	lea	$20(a1),a1
	dbra	d0,.lp

	movem.l	(sp)+,d0/a0-a1
	rts

Zap_shadow::
	movea.l	shadad.w,a0
	moveq	#0,d0
	move.b	grid_position.w,d0
	asl.w	#5,d0
	move.w	#$200,10(a0,d0)		; turn upside down
	rts

Unzap_shadow::
	movea.l	shadad.w,a0
	moveq	#0,d0
	move.b	grid_position.w,d0
	asl.w	#5,d0
	clr.w	10(a0,d0)
	rts

flight_dir:
	move.l	12(a0),d3	; = x
	move.l	20(a0),d4	; = z
	bsr	get_atan	; find out current angle from here to centre
	add.w	#400-256,d0
	neg.w	d0
	move.w	d0,6(a0)
	neg.w	d0
	add.w	#512,d0

	bsr	get_sine
	move.w	flyspeed.w,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
	move.l	d1,fly_x.w
	move.l	d2,fly_z.w
	rts

Flightinit::
	move.l	flying_ad.w,d0
	beq.b	.exit
	move.l	d0,a0
	bsr	flight_dir
.exit:	rts


Flightpath::
	movem.l	d0-d1/a0,-(sp)
	move.l	flying_ad.w,d0
	beq.b	.exit

	move.l	d0,a0
	moveq	#40,d1			; theoretically once every 100 times = once every 10 seconds. (on average)
	bsr	Randx
	bne.b	.mov
	bsr	flight_dir
	bra.b	.ad

.mov:	move.l	fly_x.w,d1
	move.l	fly_z.w,d2

.ad:	add.l	d1,12(a0)
	add.l	d2,20(a0)

.exit:	movem.l	(sp)+,d0-d1/a0
	rts

kill_low8:
	movem.l	d0-d1/a0,-(sp)
	movea.l	#TABLESTART,a0
	moveq	#7,d0
	moveq	#-4,d1
.lp0:	move.l	d1,(a0)
	lea	80(a0),a0
	dbra	d0,.lp0
	movem.l	(sp)+,d0-d1/a0
	rts

effect_save_kill:
	movem.l	d0-d2/a0-a1,-(sp)
	movea.l	#TABLESTART+(8*80),a0	; point at voice 8
	movea.w	#effects_copy,a1

	moveq	#-4,d2
	moveq	#3,d0			; 4 voices
.lp0:	moveq	#18,d1			; 20 longs of data, less first one
	move.l	(a0),(a1)+
	move.l	d2,(a0)+
.lp1:	move.l	(a0)+,(a1)+
	dbra	d1,.lp1
	dbra	d0,.lp0
	movem.l	(sp)+,d0-d2/a0-a1
	rts

effects_restore:
	movem.l	d0-d2/a0-a1,-(sp)
	movea.l	#TABLESTART+(8*80),a0
	movea.w	#effects_copy,a1

	moveq	#-4,d2
	moveq	#3,d0
.lp0:	moveq	#18,d1
	move.l	d2,(a0)+
	addq.l	#4,a1
.lp1:	move.l	(a1)+,(a0)+
	dbra	d1,.lp1
	move.l	-80(a1),-80(a0)
	dbra	d0,.lp0
	movem.l	(sp)+,d0-d2/a0-a1
	rts

crash_play::
	moveq	#11,d0
	bsr	check_channel
	bne.b	.exit			; no play if already on
	sfx	crash,$7fff,$3fff,11
.exit:	rts
