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

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

MapMode		equ	0	; 0 for display map and use blob
				; 1 for clear map and draw in grey

;=====================================================
;		MACROS
;=====================================================

.macro	trace value
.iif Trace_on > 0,	move\! \value,(a6)+

.if Trace_on = 2
	cmpa.l	#$78000,a6
	bge	dropout		; kill if overshot
.endif
.endm

.macro	border colour,override
.iif Borders=1, move.l #b_\colour,BORD1
.iif \?override&!Borders, move.l #b_\colour,BORD1
.endm

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

.macro	RunGPU	 program

	movea.w	#gpusem,a0
notc\~:	tst.l	(a0)
	bne.b	notc\~
	addq.l	#1,(a0)			; note addq.l to a known zero is same effect as move.l #1, but 2 bytes (not 6) and 20 ticks (not 28)
					; although moveq #1 + move.l would be quicker, use 4 bytes but need a register !!
	bclr	#0,G_CTRL+3		; halt GPU - note +3 to force last byte
	move.l	#G_RAM,G_PC		; set pc to start
	move.l	#\program*GPUM,gpustop
	move.l	#$11,G_CTRL		; start GPU

.endm

.macro	waitgpu
	movea.w	#gpusem,a0
.wait\~:tst.l	(a0)
	bne.b	.wait\~
.endm

;========================================
;         INCLUDES OF EQUATES
;========================================

.include 'jaguar.inc'
.include 'blit.inc'
.include 'memdefs.inc'
.include 'colours.inc'
.include 'joypad.inc'
.include 'redsound.inc'
.include 'msg.inc'
.include 'stream.inc'
.include 'fulsyn.def'

;=====================================================
;		MAIN PROGRAM
;=====================================================

.text
	movea.w	#Stack,a7
	bsr	start_from_scratch	; this includes converting to ROM image and reading eeprom contents
	init_sounds
	bsr	bootup_car_setup	; real once-only stuff - not done after *+#

restart_address::

.if finalver = 0	
	move.l	#Trace_table,a6		; 
.endif
	movea.w	#Stack,a7		; set stack pointer for safety sake, as may restart without proper cleardown
	move.l	#RLE,rlepoint.w
	bsr	general_start_up
.if use_sound = 1
	start_music ingreen
	move.w	#$5f,last_pitch.w	; this is to ensure correct pitch gets used each time, if the music is restarted
	tst.w	music_volume.w
	beq.b	.ki
	tst.b	music.w
	bne.b	.on
.ki:	jsr	StopMusic
.endif
.on:	tst.b	sound1st
	bne.b	nowt			; quick reset restart if not first time round
	bsr	init_car_setup		; only done first time round, because settings remain there-after - will be read from eeprom later
	moveq.l	#60,d1
	bsr	Wait_time		; one second delay from startup
nowt:	bsr	RedLine			; display redline text and rest of start up sequence
	bra.b	norrr

rerun:	bsr	Data_reloc		; re-compose the RAM segment if re-starting rather than re-booting
	bsr	Code_reloc		; and also the code in case it has been corrupted
	moveq	#2,d0
	bsr	Request_3d		; get the 3d object data for intro screens
	bsr	Variable_setup		; clear various variables and initialise some
	RunGPU	12
	bsr	Gen_setup
norrr:

	move.l	#$40000,d0		; to blank the main display area
	move.l	#obj_store,d1		; in full
	bsr	Blank

.skip:	move.l	#code_space-$802000,d0	; this is the difference between RAM code addresses and ROM ones
	jmp	2(PC,d0.l)		; will jump to code following this but in RAM version

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

Selt:	clr.w	counter.w
	bsr	Tournament_setup	; done before menu to ensure everything is ready
	movea.l	#Opt1,a3
	bsr	Menu			; run option screen(s)
	bsr	race_type_stuff		; make sure that anything depending on the race type is ready
	bsr	prepare_grid_allocations

	moveq.l	#0,d0
	move.b	track_no.w,d0		; for setting colour / light
	bsr	Track_init

	moveq.l	#0,d1
	move.b	weather.w,d1
	bsr	Weather_init

	movea.l	#cockpit,a0		; decode cockpit animations
	movea.l	#gf,a1
	movea.l	#gf1,a2
	move.b	car_colour.w,d0		; correct - using colour to determine palette
	asl.w	#8,d0
	adda.w	d0,a2
	bsr	rlpin

	move.b	#2,steering_tightness.w	; temporary fix for demo version

;	moveq.l	#10,d0
;	bsr	Frame_count

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

.if	MapMode = 0		; still required even though off screen
	move.l	#Mapdisp,a0
	moveq.l	#0,d0
	move.b	track_no.w,d0
	cmp.b	#10,d0		; limit track number to 2 for now
	ble.b	.ok
	moveq.l	#0,d0
.ok:	add.b	#16,d0		; gives 16+track no.
	border	white
	bsr	rldin		; unpack track map
	border	black

.endif
.if	MapMode = 1
	movea.l	#Mapdisp,a0
	move.l	#$1400-1,d0
	moveq.l	#0,d1
.lp:	move.l	d1,(a0)+	; clear map area
	dbra	d0,.lp

grid:	move.l	#Mapdisp+$80,a0	; centre of top row
	moveq	#9,d0
	move.w	#$f0ff,d1
.lp:	move.w	d1,(a0)
	lea	$800(a0),a0
	dbra	d0,.lp
	move.l	#Mapdisp+$2800,a0
	moveq	#15,d0
	move.w	#$00ff,d1
.lp1:	move.w	d1,(a0)
	lea	$10(a0),a0
	dbra	d0,.lp1

	move.w	#$ffff,Mapdisp+$2880

.endif
	; code for adjusting cockpit bitmap gaphics

	cmp.b	#1,weather.w		; test for rain
	bne.b	.nrain

	bsr	Darker

.nrain:	cmp.b	#3,weather.w
	bne.b	.okay			; or in dark

	bsr	Blacker

.okay:
Gamest:: .iif use_sound=1, stop_music

	cmp.b	#2,race_type.w
	beq.b	.skv

.ch:	moveq	#3,d0			; better get it right !
	move.w	d0,view_last.w
	move.b	d0,view_wants.w

.skv:	cmp.w	#1,view_last.w
	beq.b	.ch
	cmp.b	#1,view_wants.w
	beq.b	.ch

	moveq.l	#2,d0			; delay time
	move.w	d0,delay.w
	move.w	d0,program_area.w
	move.w	d0,perspective.w	; 2 is most extreme possible - 0 is more 'real'

	moveq	#1,d0
	move.b	d0,display_mode.w	; instruct to use blit for sky, not object
	bsr	Request_3d		; get main 3d object set into place
	bsr	Game_text_ntsc		; setup ntsc text if necessary
	move.l	#start_light,light_address.w
	bsr	init_light		; get light colours ready BEFORE drawing the first frame !

	moveq.l	#0,d0
	move.b	track_no.w,d0		; track number for track generator
	bsr	Twrite
	moveq	#0,d1
	move.b	weather.w,d1
	bsr	Prepare_bitmaps
	move.l	d1,d0
	bsr	Weather_cue		; must be run after Twrite

	subq.w	#1,pit_start.w		; make this work better
	subq.w	#1,pit_end.w		; 
	bsr	initialise_ai
	bsr	dennis_initialise

	clr.b	speedo.w		; get speedo going if its not
	bsr	Horizon_height		; make sure we don't get a grassy screen
	bsr	Obj_blank		; clear a screen (the offscreen)
	bsr 	Draw			; draw into it
	bchg.b	#3,screen+3.w		; flip screen number
.wt:	tst.l	blitsem.w		
	bne.b	.wt
	bsr	Map_init		; set changes to object list so that only players marker flashes
	bsr	get_a1_a2		; make sure addresses are correct
	movea.l	#ObjDef2,a0
	bsr	ObjSetup		; switch on display

	bsr	Fade_find		; performs a sync, so removed one
;	bsr	sync
	tst.b	ntsc_type.w
	bne.b	.okn
	moveq	#16,d0
	move.w	#200+512,d1
	bsr	set_ypos
.okn:	bsr 	Horizon			; placed here because earlier the objects wouldn't exist
	bsr	Speedisp		; put correct numbers on display for speedo and gear
	bsr	Revdisp
 	bsr	Map			; put cars blobs in place
	bsr	Maprot
	border	mint
.if use_sound=1
	moveq	#0,d0
	move.b	track_no.w,d0		; for choosing music
	asl.w	#4,d0			; scale up
	movea.l	#Songlist,a1
	adda.l	d0,a1
	move.l	(a1)+,a0
	move.l	(a1)+,a5
	move.l	(a1)+,d4
	jsr	StartMusic	; why is this jsr ? because it has to be due to distance !
	move.w	(a1)+,d0
	move.w	d0,$f10002	; pitch hacker
	move.w	d0,last_pitch.w
	tst.w	music_volume.w
	beq.b	.ki
	tst.b	music.w
	bne.b	.sef
.ki:	jsr	StopMusic
.sef:
.endif
.if	MapMode = 1
	bsr	Predraw		; plot track points in advance 
.endif
	bsr	readpad
;	bsr	Unfade		; trigger removal of covers
	moveq.l	#0,d0
	move.w	d0,framemax.w
	move.w	d0,counter.w
	sfx	startyou,$7fff,$3fff,11
	move.l	d0,gentfx.w
;================================================================================

	moveq	#0,d0
	movea.w	#drone_engines,a0
	moveq	#4,d1
.lp:	move.l	d0,(a0)+
	dbra	d1,.lp

	move.w	#180,start_count.w
	bsr	Flightinit
	bsr	car_on_car		; just to get position data sorted out
	bsr	drone_sounds

Startloop:				; loop round re-drawing, allowing just viewpoint controls, pause etc
;	bsr	fade_remove
	bsr	readpad
	bsr	get_a1_a2
	bsr	Pause
	bsr	do_dennis_routines
	bsr	segment_effects		; +6	modify anything that needs it due to current segment status
	move.w	6(a1),d0		; 	sort out the cars direction 
	neg	d0			; 	NOTE uses MINUS the direction
	bsr	get_sine
	move.w	d1,xspd.w		; 	sine d0
	move.w	d2,zspd.w		; 	cos d0 
	move.l	d1,Gpu_xspd
	move.l	d2,Gpu_zspd
	move.l	#60000,splim.w

	bsr	Flightpath
	bsr	Viewpoint
	bsr	test_twist		; added to ensure camera doesn't jerk
	bsr	Shadow			; 2	move cars shadow so it follows car
	bsr	FX			; to allow music on/off etc
	bsr	Speedisp
	bsr	Revdisp
	bsr	ResetKey
	bsr	Display_routine
	bsr	get_a1_a2
	bsr	Maprot
	bsr	Mirrors
	bsr	Horizon
	tst.l	viewmove.w
	beq.b	.skf
	bsr	screen_flip

.skf:	bsr	Map				; moves things only - no blitting
	clr.l	race_timer.w
	clr.l	lap_start.w
	bsr	Timeout
	movea.w	#(reset_list+Rono+9),a0		; get address of rain object, 'first pixel' byte
	bsr	Rand1				; get random (word) number in d0
	bset.l	#0,d0				; make sure release is left on
	move.b	d0,(a0)	

	move.w	framect.w,d0
	sub.w	d0,start_count.w
	bpl	Startloop
	moveq	#11,d0
	bsr	check_channel		; compares correct piece of data to -4 for having finished
	bne	Startloop
	bsr	next_light
	bpl	Startloop
	bsr	start_race		; allows cars to start moving, including players

	cmpi.w	#1800,revs.w		; check for low revs
	blt.b	Gameloop
	move.w	#1800,revs.w		; allow only a tiny amount of revs to remain when starting

	tst.w	spark_count.w		; only trigger sparks if not already running
	bne.b	.spo
	addq.w	#1,spark_count.w
	addq.w	#1,smoke_count.w

.spo:	tst.l	skidfx.w
	bne.b	.mvg

	sfx	skid,$2fff		; trigger a brief skid noise as well
	move.l	d0,skidfx.w
	st	short_skid.w

.mvg:

Gameloop::
;	bsr	fade_remove		; takes it away when its clear, puts it back in when its not, by switching it in/out of object list
	bsr	Flightpath
	bsr	spark_cycle		; +	deal with animation cycle for sparks - moved here to allow first frame to show !!
	bsr	get_a1_a2		; 	prepare pointers
	bsr	copy_data		; 	moves current car positions to a store where they can be read later by collision stuff
	bsr	Collision		; 1	perform collision event duties
			; order change could be significant here !!!
	bsr	use_control		; 	deal with case where computer is controlling car
	trace.w	#"Sp"
	trace.w	speed+2.w
	bsr	readpad			; 	read joypad
	bsr	Pause			; 	check for Pause, and if pressed do the pause thang
	bsr	do_dennis_routines	; 10-20	all car control stuff
	bsr	front_wheel_roll	; +	make wheels roll in animated 3d
	bsr	Spark_follow		; +	move sparks to behind car
	bsr	segment_effects		; +6	modify anything that needs it due to current segment status
	bsr	off_track_test		; 	here because then track width is updated first
	bsr	AI			; c.100 note this screws up current segment, which is then reinstated
	bsr	car_on_car		; 6	test for collision between all types of car
	bsr	drone_sounds

	move.w	6(a1),d0		; 	sort out the cars direction 
	neg	d0			; 	NOTE uses MINUS the direction
	bsr	get_sine
	move.w	d1,xspd.w		; 	sine d0
	move.w	d2,zspd.w		; 	cos d0 
	move.l	d1,Gpu_xspd
	move.l	d2,Gpu_zspd
	move.l	#60000,splim.w

	bsr	Viewpoint		; 2	move the viewpoint either within car or chasing car
	bsr	test_twist		; 6	relies on a1 being correct : moved from earlier to ensure view changes are immediately acted upon
	bsr	Shadow			; 2	move cars shadow so it follows car

	bsr	FX			; this will be in every version, but the routine will be changed to remove debugging keys

	border	mint
	tst.b	speedo.w
	bne.b	nosp
	bsr	Speedisp		; 16
	bsr	Revdisp			; 2
nosp:	bsr	ResetKey		; <1	deal with possibility of Atari Jaguar Restart keys (* and #)
	bsr	Horizon_height		; <1
	bsr	Display_routine		; n/a	main display (3d poly system) - includes switching screen to new one
	trace.l	gpudebug.w
	bsr	get_a1_a2		; 0	required for next bit
	bsr	Maprot			; 100	draw map - rotated - onto main screen
	bsr	Mirrors			; 0-50
	bsr	Horizon			; 1	does steering wheel object and cockpit blit
	bsr	Map			; 10	move markers on map
.iif	MapMode = 1, bsr Mapdraw
	tst.l	viewmove.w
	beq.b	.skf
	bsr	screen_flip

.skf:	tst.b	human_finished.w
	bne.b	.skt
	bsr	Timeout			; 10	update clock outputs
.skt:	bsr	pclip2			; 100	perform collision detection

; rain hack - to make less repetitive

	movea.w	#(reset_list+Rono+9),a0		; get address of rain object, 'first pixel' byte
	bsr	Rand1				; get random (word) number in d0
	bset.l	#0,d0				; make sure release is left on
	move.b	d0,(a0)	

	border	black,1
.tok:	tst.b	game_over.w
	beq	Gameloop			; and round again unless finished

	tas	play_out.w
	bne.b	.waitx

;	st.b	static_view.w
;	bsr	move_view_ahead
	move.w	#32,count_out.w			; total steps to produce a 180 swing - or just a delay if no special stuff
	tst.b	human_finished.w		; if player didn't finish, no special stuff
	beq.b	.qex
	cmp.b	#1,race_type.w			; check for free practice
	beq.b	.qex

	st.b	camera_lock_switch.w
	bsr	set_finish_viewpoint		; triggers movement of camera
	move.l	#~ANY_FIRE,key_mask.w		; disable primary controls
;	clr.l	key_mask.w			; when enabled this line stops the player from interacting after they cross the line
;	bsr	Start_fade

.waitx:	tst.b	human_finished.w		; if player didn't finish, no special stuff
	beq.b	.qex

	move.w	revs.w,d0
	asr.w	#3,d0
	sub.w	d0,revs.w

	cmp.b	#1,race_type.w			; check for free practice
	beq.b	.qex

	add.w	#$10,swing_camera_angle.w

;	move.w	framect.w,d0
.qex:	subq.w	#1,count_out.w
	bpl	Gameloop
;	tst.b	fade_over.w			; check for completion of play out sequence
;	bne	Gameloop			; keep going until it hits 0

; for now just drop through for test purposes

;==============================================
;drop out and re-run from select screens

.if use_sound=1
	kill_all			; halt any effects
	clr.b	engine_on.w
	start_music song0		; restart intro music as we head back into the menu
	move.w	#$5f,last_pitch.w
	tst.w	music_volume.w
	beq.b	.ki
	tst.b	music.w
	bne.b	.sks			; if music is switched off, kill it before it starts
.ki:	jsr	StopMusic
.endif

.sks:	bsr	GameOver		; performs all the sequences and prepares for next race if necessary
	
.restart:
.if use_sound = 1
	start_music ingreen		; restart intro music as we head back into the menu
	move.w	#$5f,last_pitch.w
	tst.w	music_volume.w
	beq.b	.ki2
	tst.b	music.w
	bne.b	.ins			; if music is switched off, kill it before it starts
.ki2:	jsr	StopMusic
.endif

.ins:	jmp	rerun


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

next_light::
	movea.l	light_address.w,a0
	move.w	(a0)+,start_count.w
	movea.w	#Palette+2,a1
	adda.w	(a0)+,a1
.lp:	move.w	(a0)+,d0
	beq.b	.out
	move.w	d0,(a1)+
	bne.b	.lp
.out:	bsr	play_from_a0
	move.l	a0,light_address.w
	tst.w	(a0)
	rts

init_light::
	movea.l	light_address.w,a0
	movea.w	#Palette+2,a1
.lp:	move.w	(a0)+,d0
	beq.b	.out
	move.w	d0,(a1)+
	bne.b	.lp
.out:	move.l	a0,light_address.w
	rts	

play_from_a0:
	move.l	(a0)+,d0		; note outside the stacked section !
.if	use_sound=1
	movem.l	a0-a4/d1-d4,-(sp)	; necessary to pass back a0 without corruption, but with change
	move.l	#$1fff,d2
	move.l	d2,d1
	moveq	#11,d3			; force to use the channel vacated by the voice sample
	jsr	DoEffect
	movem.l	(sp)+,a0-a4/d1-d4
.endif
	rts


.if 0
move_view_ahead::
	movem.l	d0-d3/a0,-(sp)

	move.w	xspd.w,d0	; this is sin<<14
	move.w	zspd.w,d1	; so to give speed<<3 distance, need to >>11
	move.w	speed+2.w,d2
	asr.w	#2,d2		;
	muls	#3,d2		; modified to 3/4 speed
	muls	d2,d0		;
	muls	d2,d1
	moveq	#11,d2
	asr.l	d2,d0
	asr.l	d2,d1
	bsr	get_a1_a2	; make sure a1 points at car
	add.l	12(a1),d0
	add.l	20(a1),d1	; gives final x/z positions in 2 seconds time (!)
	movea.l	#Genstr,a0
	move.l	d0,12(a0)
	move.l	#-128,16(a0)		; make it almost on road surface
	move.l	d1,20(a0)
	move.w	6(a1),d0	; get direction of car
	neg.w	d0
	add.w	#$200,d0
	move.w	d0,2(a0)

	movem.l	(sp)+,d0-d3/a0
	rts
.endif
.if MapMode = 1
Pixelplot:	; plots colour d2 at location d0,d1 on the map
	movem.l	d0-d1/a0,-(sp)
	asl.l	#4,d0		; x is scaled up one extra bit because the pixels are two bytes wide
	swap	d0		; scale down to give ratio where 8k = 1 pixel
	ext.l	d0
	add.l	#128,d0		; offset of 64 pixels to place 0,0 at centre of square
	and.b	#$FE,d0		; clear low bit
	neg.l	d1
	asl.l	#3,d1
	swap	d1
	ext.l	d1
	add.l	#40,d1		; y offset of 40 pixels = centre of square
	asl.l	#8,d1		; = 256*d1 - each line is 128 pixels = 256 bytes
	movea.l	#Mapdisp,a0
	add.l	d0,d1
	bmi.b	.noad
	adda.l	d1,a0
	move.w	d2,(a0)	; plot pixel
.noad:	movem.l	(sp)+,d0-d1/a0
	rts

Predraw:
	movem.l	d0-d3/a0,-(sp)
	movea.l	Genstr+20+(8*str_track),a0		; address of track list
	move.l	(a0)+,d3
	move.w	#$77f0,d2		; colour

.lp:	move.l	12(a0),d0
	move.l	20(a0),d1
	bsr	Pixelplot
	lea	$20(a0),a0
	dbra	d3,.lp

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

Mapdraw:	; directly plot a point at current car position, scaled down onto the map
	movem.l	d0-d2,-(a7)
	bsr	get_a1_a2
	move.l	12(a1),d0	; get x coordinate
	move.l	20(a1),d1	; get z co-ordinate
	move.w	#$7760,d2
	bsr	Pixelplot
	movem.l	(a7)+,d0-d2
	rts
.endif

Map::	movem.l	d0-d5/a0-a2,-(a7)
	cmp.b	#2,map_spin.w			; if off (2) then do nothing
	beq.b	.mex

	movea.w	#reset_list+blono,a0		; address of object data in object reset table
	movea.w	#grid_colour0,a2

	move.w	Genstr+2,d0
	bsr	get_sine		; for rotations
	move.l	d1,d3
	move.l	d2,d4

	movea.l	carsad.w,a1		; point at first vehicle
	moveq	#0,d5
	move.b	real_num_drones.w,d5

.bmv:	move.l	12(a1),d0		; get x coordinate
	move.l	20(a1),d1		; get z co-ordinate
	bsr	Rotateblob
	moveq	#0,d2
	move.b	(a2)+,d2		; read car colour - which corresponds to which blob to move
	cmp.b	car_colour.w,d2		; check players car colour against this one
	bne.b	.ok

	movem.l	d0-d1,-(sp)

.ok:	asl.w	#4,d2			; scale up to size of one object	
	bsr.b	Bput
	lea	32(a1),a1		; point at next car
	dbra	d5,.bmv

	moveq	#5,d5
	sub.b	real_num_drones.w,d5
	bra.b	.dbl

.boff:	moveq	#0,d2			; switch off all remaining car blobs
	move.b	(a2)+,d2
	asl.w	#4,d2
	bset	#5,6(a0,d2)
.dbl:	dbra	d5,.boff

	movem.l	(sp)+,d0-d1
	subq.l	#4,d0
	subq.l	#4,d1
	moveq	#6<<4,d2
	bsr	Bput			; put the ring around the players car


	movem.l	(a7)+,d0-d5/a0-a2
	rts

.mex:	movea.w	#reset_list+blono,a0	; switch off blobs if in mode 2 (off)
	moveq	#6,d0			; this #5 is intentionally not real-num-drones and now has one extra to switch off the ring
.lp0:	bset	#5,6(a0)		; off blob n
	lea	$10(a0),a0		; next object
	dbra	d0,.lp0
	movem.l	(a7)+,d0-d5/a0-a2
	rts


	; below is routine to move a general 'marker' to the right co-ordinates for the mapping system
zoff	equ	21+8-2-1+(maphi/2)+mapyout	; 21 for screen top , -2 for size, +48 for midpoint ?
xoff	equ	mapxout+(mapwidth/2)-2		; 160 for centre screen position, -2 for size

Bput:	move.l	d0,-(sp)		; retain x co-ordinate
;	neg.l	d1			; negate because down is +ve on screen
	add.l	#zoff,d1		; offset to allow for centre point at 0
	tst.b	ntsc_type.w
	bne.b	.ntsc
	add.l	#PALyoff/2,d1

.ntsc:	asl.l	#4,d1			; scale up for fitting into object defn
	move.w	#$C003,d0		; prepare mask for object data
	and.w	6(a0,d2.w),d0		; gives first word without ypos in it
	and.w	#$3ffc,d1
	or.w	d1,d0			; put ypos in
	move.w	d0,6(a0,d2.w)		; store into obj list

	move.l	(sp)+,d1	; recover x co-ordinate
	add.l	#xoff,d1	; offset
	move.w	#$E000,d0	; prepare mask
	and.w	14(a0,d2.w),d0	; get data
	and.w	#$1fff,d1
	or.w	d1,d0		; put x in
	move.w	d0,14(a0,d2.w)	; write out

	rts


Rotateblob:			; rotates d0,d1 by d3,d4 without messing ANY other registers
	movem.l	d2-d4,-(sp)

	asl.l	#3,d1		; shift down by 13
	swap	d1		; the quick way 
	ext.l	d1		; because values need to be scaled
	asl.l	#3,d0
	swap	d0
	ext.l	d0

	tst.b	map_spin.w	; only rotate if in mode 0
	beq.b	.m2

.m1:	neg.l	d1
	subq.l	#8,d1		; adjustment because straight blit is higher than rotated one
	bra.b	.ex

.m2:	move.l	d0,d2		; copy -x into d2
	muls	d4,d2		; = xcos
	muls	d1,d4		; = zcos
	muls	d3,d0		; = xsin
	neg.l	d0		; = -xsin
	sub.l	d4,d0		; = new y coordinate
	muls	d3,d1		; = zsin
	sub.l	d1,d2		; = xcos - zsin = new x co-ordinate
	move.l	d0,d1		; put in place
	move.l	d2,d0		; --
	asl.l	#2,d0
	asl.l	#2,d1
	swap	d0
	swap	d1
	ext.l	d0
	ext.l	d1
.ex:	movem.l	(sp)+,d2-d4
	rts

	

Map_init:
	movem.l	a0/d0,-(sp)
	movea.l	#BLOB,a0
	move.b	car_colour.w,d0		; correct, because the object numbers are the colours right now
	ext.w	d0
	mulu	#20,d0
	adda.w	d0,a0		; points at players cars object def'n

	move.l	#Banim,(a0)
	bset	#6,8(a0)	; makes it animated rather than bitmap

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

.if 0		; old display checker
check_obs:
	movea.w	#reset_list+$20,a0
	movea.l	#obj_data,a1
	moveq	#3,d0	
.lp:	cmp.l	(a0)+,(a1)+
	bne.b	.go
	dbra	d0,.lp
	rts
.go:	movea.l	#ObjDef2,a0
	bsr	ObjSetup
	rts
.endif


Display_routine::			; this routine is the main display thang for polygon data

	movem.l	d0-d7/a0-a5,-(a7)	; almost everything stacked because then anything can call it

	tst.b	frame_change.w
	beq.b	.nosyn
	bsr	sync
.nosyn:	move.w	delay.w,d0
	bsr	floop
	move.w	counter.w,d0
	move.w	d0,framect.w
	cmp.w	framemax.w,d0
	bmi.b	.no_ch
	move.w	d0,framemax.w
.no_ch:	clr.w	counter.w
	addq.l	#1,dfct.w

	bsr	colhack

	cmpi.w	#10,framect.w
	bmi.b	.ok
	move.w	#10,framect.w
.ok:

	bsr	Obj_blank
	clr.w	collision_test.w	; clear prior to any use of counter
;	clr.l	polcnt.w
;	clr.l	shapect.w

	bsr.b	Spinner 		; routine to apply changes to the amount of spin in each direction.
	bsr	Draw			; the new improved plotting routine
bbw:	tst.l	blitsem.w		
	bne.b	bbw

	tst.l	viewmove.w
	bne.b	.ex			; don't immediately update the screen if view is in cockpit
	bsr	screen_flip		; to allow easier mods

.ex:	movem.l	(a7)+,d0-d7/a0-a5

	rts

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

screen_flip:
	tas	request_delayed.w
	bne.b	.ok
	bsr	Extra_Object_Switch
.ok:	bchg.b	#3,screen+3.w		; flip screen number
	st.b	frame_change.w		; sets a flag which will be cleared by frame interrupt, allowing instant continuation where reasonable
	rts

get_a1_a2::
	move.l	d0,-(sp)
	movea.l	carsad.w,a1		; for getting at the car data
	moveq	#0,d0
	move.b	grid_position.w,d0
	asl.w	#5,d0
	adda.w	d0,a1
	movea.l	spinad.w,a2
	move.l	(sp)+,d0
	rts

Spinner::		; updates all the "rotation" values by adding the "spin" to them

	movem.l	d0-d1/a0,-(a7)

	movea.l	spinad.w,a0

spinloop:
	move.w	(a0)+,d0
	bmi.b	.done
	add.w	d0,(a0)+	; x done

	move.w	(a0)+,d0
	add.w	d0,(a0)+	; y done

	move.w	(a0)+,d0
	add.w	d0,(a0)+	; z done

	lea	20(a0),a0		; skip coordinates, radius, and pointer to structure data
	bra.b	spinloop

.done:	movem.l	(a7)+,d0-d1/a0
	rts



Speedisp::		; not stacking - think don't need to
;	bsr	do_dennis_mph
;	and.l	#$ffff,d0
	move.l	speed.w,d0	; get speed in mm per frame - max $8000 in 12.4 format
	asr.w	#1,d0		; half temporarily whilst faking high speed
	tst.b	units.w		; zero for mph, otherwise km/h
	bne.b	.kmh
	move.w	#553,d1		; mph = 553/2048 * mm per frame	**** note changed to 2mm per unit ****

	bra.b	.use

.kmh:	move.w	#895,d1		; kmh = 1.6 * mph = 884.7 / 2048 * mm/frame

.use:	asr.l	#1,d0		; max $4000 = actual speed *8 - multipliable as well
	mulu	d1,d0		; giving mph *4*4096 = *16K !
	asl.l	#2,d0
	swap	d0		; quick 16 bit shift
	ext.l	d0 		; and quick data clear

;	moveq	#0,d0
;	move.w	ai_blocks+22,d0

;	movem.l	d0/a0,-(sp)
;	swap	d0
;	asr.l	#7,d0		; Chris's sound stuff - hacked in to be removed later
;	move.l	engine.w,a0
;	lea	16(a0),a0
;	add.l	#$8000,d0
;	move.l	d0,(a0)
;	movem.l	(sp)+,d0/a0

;	move.l	gpudebug.w,d0	; temporary
;	move.w	framemax.w,d0	; test
;	move.w	framect.w,d0
;	move.w	hit_type.w,d0
;	moveq	#$f,d1
;	and.w	d0,d1
;	asr.w	#4,d0
;	muls	#10,d0
;	or	d1,d0
	move.l	#spono,d1	; points to object number in list
	moveq.l	#3,d2		; number of digits
	bsr	Countout

	tst.b	human_finished.w	; don't change lap count or position once finished
	bne.b	.skip

	move.w	lap_display.w,d0
	addq.w	#1,d0
	move.l	#lapono,d1
	moveq	#2,d2
	bsr	Countout

	movea.l	#posit_block-4,a0
	moveq	#0,d0
	move.b	grid_position.w,d1
.lp:	addq.l	#4,a0
	addq.w	#1,d0
	cmp.b	(a0),d1
	bne.b	.lp

	move.l	#pono,d1	; position digit
	moveq.l	#1,d2
	bsr	Countout

	move.w	num_players.w,d0
	add.b	real_num_drones.w,d0

	move.l	#nono,d1
	moveq.l	#1,d2
	bsr	Countout

.skip:	move.w	gear_1,d0
;	move.w	framect.w,d0
;	move.w	steering_user.w,d0
;	move.w	under_user.w,d0	; temp
;	sub.w	#28,d0		; to make displayable
	move.l	#gono,d1	; points to object number in list
	moveq.l	#1,d2		; number of digits
	bsr	Countout

	rts

; temporarily disabled due to too many objects in object list
;	movea.w	disvar.w,a0	; get address of variable to display
;	moveq.l	#0,d0
;	move.w	(a0),d0		; get the variables value
;	move.l	#poono,d1
;	moveq.l	#4,d2
;	bra.b	Countout	; note run through into subroutine, thus avoiding bsr.
				; DO NOT PUT MORE CODE HERE OR SPLIT !

.if	0	; new version of countout is in hexcon.s
Countout::
	data_size = 256


	movea.w	#reset_list,a0	; 
	adda.w	d1,a0		; step over objects as necessary
;	tst.b	viewmove.w	; check whether overlays are in place
;	bne.b	.ttl
;	lea	-64(a0),a0	; deal with possibility of extra objects

.ttl:	sub.w	#10000,d0
	bhi.b	.ttl
	add.w	#10000,d0	; reduce if outside limit
	cmp.b	#4,d2
	bmi.b	.oflow		; if less than 4, then only display 3 digits 

	moveq.l	#0,d1
.thou:	add.l	#data_size,d1		; generate address
	sub.w	#1000,d0	; reduce whilst generating address
	bhi.b	.thou
	add.w	#1000,d0	; correct for overun

.thout:	tst.l	d1
	sne	leading_zero.w
	bne.b	.ok
	add.l	#data_size*10,d1
.ok:	add.l	#charcs-data_size,d1
	asl.l	#8,d1
	moveq.l	#0,d2
	move.w	2(a0),d2
	and.w	#$7ff,d2
	or.l	d1,d2
	move.l	d2,(a0)+
	lea.l	12(a0),a0
	bra.b	.cdwn

.oflow:	sub.w	#1000,d0	; reduce by 1000 
	bpl.b	.oflow
	add.w	#1000,d0	; until need to go back
	cmp.b	#3,d2		; check for 3 digit display
	bmi.b	.oflow2

.cdwn:	moveq.l	#0,d1
.hund:	cmp.w	#100,d0
	bmi.b	.hout
	sub.w	#100,d0
	add.l	#data_size,d1
	bra.b	.hund

.hout:	tst.l	d1
	beq.b	.ze2
	st	leading_zero.w
.ze2:	tst.b	leading_zero.w
	bne.b	.ok2
	add.l	#data_size*10,d1
.ok2:	add.l	#charcs,d1	; address of start of numbers
	asl.l	#8,d1
	moveq.l	#0,d2
	move.w	2(a0),d2
	and.w	#$7ff,d2
	or.l	d1,d2
	move.l	d2,(a0)+
	lea.l	12(a0),a0
	bra.b	.cdwn2

.oflow2:
	sub.w	#100,d0
	bpl.b	.oflow2
	add.w	#100,d0
	cmp.b	#2,d2		; check for 2 digit display
	bmi.b	.oflow3

.cdwn2:	moveq.l	#0,d1
.tens:	cmp.w	#10,d0
	bmi.b	.tout
	sub.w	#10,d0
	add.l	#data_size,d1
	bra.b	.tens

.tout:	tst.l	d1
	beq.b	.ze3
	st	leading_zero.w
.ze3:	tst.b	leading_zero.w
	bne.b	.ok3
	add.l	#data_size*10,d1
.ok3:	add.l	#charcs,d1	; address of start of numbers
	asl.l	#8,d1
	moveq.l	#0,d2
	move.w	2(a0),d2
	and.w	#$7ff,d2
	or.l	d1,d2
	move.l	d2,(a0)+
	lea.l	12(a0),a0
	bra.b	.cdwn3

.oflow3:
	sub.w	#10,d0
	bpl.b	.oflow3
	add.w	#10,d0

.cdwn3:	move.l	#charcs,d1	; address of start of numbers
.units:

;	mulu	#data_size,d0	; one method

	asl.l	#8,d0	; shift up by 8 = *256
	add.l	d0,d1

.uout:	asl.l	#8,d1
	moveq.l	#0,d2
	move.w	2(a0),d2
	and.w	#$7ff,d2
	or.l	d1,d2
	move.l	d2,(a0)+

	rts
.endif

GPUWrite::		; transfer program from end of all this to the GPU at GPU_RAM start 

;	moveq	#8,d0
;	move.l	d0,G_CTRL		; make sure it isn't running
	
 	move.l	#G_RAM,a0		; base of GPU local RAM
	move.l	#gp,a1			; where the GPU code will be tacked on
	move.l	#gpu_over2,d0		; end of first full overlay
	sub.l	a1,d0
	asr.l	#2,d0			; convert to longwords
getgpu: move.l	(a1)+,(a0)+	
	dbra	d0,getgpu		; copy the code in
	clr.l	Gpu_flag

.if	1
	move.l	#$deadc0de,d0
	movea.l	#G_ENDRAM,a1
.x:	move.l	d0,(a0)+		; fill GPU RAM with emptiness
	cmpa.l	a1,a0
	bmi	.x
.endif
	moveq	#0,d0
	move.l	d0,G_CTRL
	moveq	#1,d0
	move.l	d0,gpusem.w		; flag for testing finish
	move.l	#G_RAM,G_PC		; set pc to start
	move.l	#GPUM,gpustop		; program 1 = initialisation
	move.l	d0,G_CTRL		; start GPU

	waitgpu
	st	gpu_ready.w

	rts

.if 0
Tilt:	move.l	d0,-(sp)	; make world tilt down in one direction
	move.w	Genstr+2,d0	; get direction
	asr.w	#2,d0
	tst.b	d0
	bmi.b	.nn		; because could be $80 - the un-negatable !
	neg.b	d0
.nn:	ext.w	d0
	asr.w	#3,d0
	subq.w	#4,d0
	move.w	d0,Genstr
	move.l	(sp)+,d0
	rts
.endif
Stack_zap::
	link	a6,#0
	move.w	#$400,d0

.lp:	move.l	#$DEADC0DE,-(sp)
	dbra	d0,.lp

	unlk	a6
	rts

bootup_car_setup:			; things to setup when not just resetting
	moveq	#0,d0
	move.b	d0,weather.w
	move.b	d0,foil_type.w
	move.b	d0,tyre_type.w
	move.b	d0,race_type.w
	move.b	#5,num_drones.w		; not kept in cart

	bsr	read_configuration	; reads all key configs and other settings
	bmi.b	.bad			; returns -1 if failed on any specifics, in which case default them all
	tst.b	valid_eeprom.w
	bne.b	.ok			; also use defaults if no valid data was found originally
.bad:	bsr.b	init_default
.ok:	bsr	read_fame		; reads names 'n times, correctly, even if the general config was screwed, and they had to be replaced
	rts

init_car_setup:				; things to do when resetting or booting
	moveq	#2,d0
	move.b	d0,steering_tightness.w
	move.w	#30,steering_user.w
	move.w	#3,under_user.w
	clr.b	tourn_track.w	; even when resetting, tournament has to restart

	rts

init_default::
	moveq.l	#1,d0
	move.b	d0,tach_set.w
	move.b	d0,music.w

	moveq.l	#0,d0
	move.b	d0,gear_type.w
	move.b	d0,car_colour.w
	move.b	d0,map_spin.w
	move.b	d0,key_config.w
	move.b	d0,race_type.w

	move.b	#4,track_no.w
	move.b	#5,num_drones.w
	move.b	#10,qty_laps.w

	move.w	#54,music_volume.w
	move.w	#40,effects_volume.w
	rts

Revdisp:
	movem.l	d0-d3/a0,-(sp)

	moveq.l	#12,d1		; counter
	movea.l	#reset_list,a0	; object list address
	move.w	#revno,d0	; object number of first block
	adda.w	d0,a0		; add on
	move.w	revs.w,d0	; display amount
	tst.b	tach_set.w
	beq	.dark
	sub.w	#1500,d0	; less start value
	bra.b	.light

.lpl:	bclr.b	#5,6(a0)	; clear bit which stops display of an object
	lea	16(a0),a0	; move on
	sub.w	#1000,d0	; reduce rev count
.light:	dbmi	d1,.lpl

	bpl.b	.exit
	bra.b	.dark

.lpg:	bset.b	#5,6(a0)	; set that bit for the remainder of objects
	lea	16(a0),a0
.dark:	dbra	d1,.lpg

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


.if 0
Revdisp:
	movem.l	d0-d1/a0,-(sp)

	moveq.l	#11,d1
	movea.l	#rev_shadow,a0
	move.w	revs.w,d0
	sub.w	#1600,d0	; minimum for revs to show

.lpl:	sub.w	#1000,d0
	smi.b	(a0)+
	dbra	d1,.lpl

	clr.l	(a0)		; make absolutely sure the right hand bit is empty

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

segment_effects::
	movem.l	d0-d3,-(sp)
	st	comp_gen_view.w		; ensures that only here can the flag for this be used, and that it is reset every time round
	moveq	#0,d1
	move.w	twist_segment.w,d0	;current_segment.w,d0	; get segment number
	asl.w	#3,d0
	movea.l	#track_table,a2
	move.b	6(a2,d0.w),d1		; maximum view number
	cmp.b	view_limit.w,d1		; test for already set to same value
	beq.b	.ok

	tst.b	view_limit.w
	beq.b	.nosp			; no special treatment for first time 
	tst.b	d1
	beq.b	.nosp			; or for changing to free choice

	move.b	d1,view_limit.w
	cmp.b	view_wants.w,d1		; check whether can get desired view
	blt.b	.take			; use new limit view if not
	move.b	view_wants.w,d1	
.take:	cmp.b	view_last+1.w,d1	; check in case already in use
	beq.b	.ok
	bra.b	.vout

.nosp:	move.b	d1,view_limit.w
	beq.b	.rele			; release if 0 (and not already released)
	cmp.b	view_last+1.w,d1	; check if last requested one (and hence current one) was too high
	bge.b	.ok

	move.b	view_last+1.w,view_wants.w; hang on to old one for recover
	bra.b	.vout

.rele:	move.b	view_wants.w,d1		; read view that was previously held or previously requested
.vout:	clr.b	comp_gen_view.w
	bsr	Request_viewpoint

.ok:	move.b	7(a2,d0.w),d1		; read flags as they are now
	cmp.b	track_flags.w,d1	; test for change
	beq.b	.out			; no changes means do nothing

	move.b	track_flags.w,d0	; get old flags
	eor.b	d1,d0			; get changes
	move.b	d1,track_flags.w	; write out new version before it gets forgotten

.b0:	btst	#0,d0
	beq.b	.b1			; if no change then out

	cmp.b	#1,weather.w		; check for rain - nothing to do if off
	bne.b	.b1

	exg	d0,d2
	moveq	#0,d0
	btst	#0,d1			; test new flags to see whether to set or clear rain
	bne.b	.roff			; bit set means no rain - in tunnel
	move.w	#raincol,d0
.roff:	moveq	#6,d1
	bsr	CLUT_write
	exg	d0,d2			; for safety to ensure that d0 is available for further uses
.b1:	btst	#1,d0			; no uses for bits 4-7	bit 1 used for turns (for general slowdown)
	beq.b	.b3			; bit 2 for tight turns for drones, bit 3 for wide track (for off road test)
	btst	#1,track_flags.w
	beq.b	.redu
	add.w	#2500,track_width.w
	bra.b	.b3
.redu:	sub.w	#2500,track_width.w

.b3:	btst	#3,d0			; check for change to bit 3
	beq.b	.b2
	btst	#3,track_flags.w
	beq.b	.cut
	add.w	#5000,track_width.w
	bra.b	.b2
.cut:	sub.w	#5000,track_width.w

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

prepare_grid_allocations::

	movea.w	#posit_block,a0
	moveq	#0,d0
	moveq	#0,d1
	moveq	#5,d2
.lp0:	move.b	d1,(a0)+
	move.b	d0,(a0)+
	move.w	d0,(a0)+
	addq.w	#1,d1
	dbra	d2,.lp0

	cmp.b	#1,race_type.w
	beq.b	.first				; allocate colours in the free practive to ensure the right car gets switched off

.more:	tst.b	tourn_track.w
	beq.b	.first
	cmp.b	#0,race_type.w
	bne.b	.tourn

.first:	move.b	car_colour.w,grid_colour0.w
	moveq	#5,d2
;	move.b	#real_num_drones.w,d2		; was used, but now using #5 to allocate all colours, and allow easy switching off of extra blobs
	bra.b	.dlp
.lp:	move.w	#6,d1
	bsr	Randx
	movea.w	#grid_colour0,a0
	moveq	#5,d1
;	move.b	real_num_drones.w,d1		; again, was used but now fixed to do all 6 cars - note these two should be the same ... and weren't
	sub.b	d2,d1		; gives number of colours already defined
	bra.b	.dlp2
.lp2:	cmp.b	(a0)+,d0
	beq.b	.lp		; if same found do again
.dlp2:	dbra	d1,.lp2
	move.b	d0,(a0)		; out it
.dlp:	dbra	d2,.lp
	rts

.tourn:	movea.w	#pos1_colour,a0
	movea.w	#grid_colour0,a1

	moveq	#5,d0
.tlp:	move.b	(a0)+,(a1)+
	dbra	d0,.tlp

	rts


race_type_stuff::
	movem.l	d0/d1/a0,-(sp)
	moveq	#0,d0
	cmp.b	#1,race_type.w		; 1 is free practice
	bne.b	.real
	move.b	d0,real_num_drones.w
	move.b	d0,tourn_track.w
	move.l	d0,points_colour0.w
	move.w	d0,points_colour4.w
	bra.b	.unral
.real:	move.b	num_drones.w,real_num_drones.w

.unral:	tst.b	tourn_track.w
	beq.b	.first			; first stage behaves like a single race
	cmp.b	#2,race_type.w
	beq.b	.tour
.first:	move.b	d0,grid_position.w
	move.b	d0,tourn_track.w
	move.l	d0,points_colour0.w
	move.w	d0,points_colour4.w
	movea.w	#time_colour0,a0
	moveq	#11,d1			; clear twelve long entries
.lp:	move.l	d0,(a0)+
	dbra	d1,.lp
	movem.l	(sp)+,d0/d1/a0
	rts

.tour:	move.b	last_result.w,grid_position.w
	subq.b	#1,grid_position.w

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

car_on_car::
	movem.l	d0-d4/a0-a4,-(sp)

	move.l	#3000,d4
	moveq	#0,d2
	move.b	real_num_drones.w,d2	; loop counter
	addq.w	#1,d2
	movea.l	carsad.w,a0
	movea.w	#drone_dis,a4
	bra.b	.lpd
.lp:	move.w	d2,d0
	asl.w	#5,d0
	lea	(a0,d0.w),a2		; point at d2 car
	move.w	d2,d3
	bra.b	.lpd2

.lp2:	move.w	d3,d0
	asl.w	#5,d0
	lea	(a0,d0.w),a1		; point at d3 car
	move.l	12(a2),d0
	sub.l	12(a1),d0
	bpl.b	.nn1
	neg.l	d0
.nn1:	move.l	20(a2),d1
	sub.l	20(a1),d1
	bpl.b	.nn2
	neg.l	d1
.nn2:	cmp.l	d0,d1
	bgt.b	.nex
	exg	d0,d1
.nex:	asr.l	#1,d0
	add.l	d0,d1			; this is the approx. distance from car2 to car3

	clr.b	human_crash.w
	cmp.b	grid_position.w,d2
	beq.b	.use
	cmp.b	grid_position.w,d3
	bne.b	.nou
.use:	st.b	human_crash.w
	move.l	(a4),d0
	move.l	d1,(a4)+
	sub.l	d1,d0
	move.l	d0,(a4)+		; generates a value for approach speed (+ve is coming in, -ve is going away)

.nou:	cmp.l	d4,d1
	bgt.b	.lpd2

	bsr	test_both		; once definite that cars are 'close' then test there PREVIOUS relative positions
.lpd2:	dbra	d3,.lp2
.lpd:	dbra	d2,.lp

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

test_both::
	movem.l	d0-d5/a0-a3,-(sp)
	move.l	carsad.w,d0
	movea.w	#cars_copy,a0
	sub.l	d0,a1
	sub.l	d0,a2
	add.l	a0,a1
	add.l	a0,a2
	move.l	12(a1),d4
	move.l	20(a1),d5
	sub.l	12(a2),d4		; generate dx,dz (from d2 to d3)
	sub.l	20(a2),d5
	movea.w	#ai_blocks,a0

	moveq	#0,d1
	bsr	rotate_test		; returns result for d2 on d3 in d1.b, and doesn't mess anything up
	rol.b	#4,d1
	exg	a1,a2			; swap cars addresses
	exg	d2,d3			; and the car numbers
	neg.l	d4
	neg.l	d5
	bsr	rotate_test
	exg	a1,a2
	exg	d2,d3

	move.w	d1,hit_type.w
	asl.w	#5,d2
	asl.w	#5,d3

	tst.b	d1
	bne.b	.nhc
	bsr	head_on
	bra.b	.cex			; exit with collision stuff

.nhc:	moveq	#$f,d0
	and.b	d1,d0
	beq.b	.just1			; only one number, in upper nibble, so car d3 is hitting stuff - swap 'em
	moveq	#$f0,d0
	and.b	d1,d0
	beq.b	.just2			; only one number, in lower nibble

	cmp.b	#$13,d1
	beq.b	.nudge
	cmp.b	#$31,d1
	bne.b	.exit			; exit without collision stuff

	exg	d2,d3			; if the hit is 31, then it is the opposite to what I want
	exg	a1,a2			; where I can just turn the first car left and the other car right (after swapping directions)

.nudge:	bsr	swap_dirs		; simplest possible response to a side to side collision
	bra.b	.cex

.just1:	exg	d2,d3			; swap d2,d3 to make the car which gets bashed correct
	ror.b	#4,d1
.just2:	cmp.b	#1,d1
	beq.b	.sid
	cmp.b	#3,d1
	bne.b	.nosi
.sid:	bsr	side_hit
	bra.b	.cex

.nosi:	cmp.b	#2,d1
	bne.b	.half2			; if type is not 20,02,03,30,01,10, then its an impossible one (
	bsr	shunt			; now we know that car d2 hit car d3 

.cex:	tst.b	human_crash.w
	beq.b	.halfem		; if no human involved

	bsr	crash_play
	bra.b	.exit

.half2:	
.halfem:asr.w	VELOC(a0,d2)

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

rotate_test:				; rotate d4,d5 round to relative to car in d2, and check direction
	movem.l	d0/d2-d7,-(sp)
	move.l	d1,d7
	move.w	6(a2),d0		; get angle to apply
	bsr	get_sine
	move.l	d4,d6			; x
	move.l	d5,d3			; z
	muls	d2,d6			; x cos
	muls	d1,d3			; - z sin
	add.l	d6,d3			; new x value (this is <<14 but doesn't matter anyway)
	muls	d1,d4			; x sin
	muls	d2,d5			; z cos
	sub.l	d5,d4			; - new z value

	cmp.l	d3,d4
	blt.b	.n1
	bset	#1,d7

.n1:	tst.l	d3
	bpl.b	.nn1
	neg.l	d3
.nn1:	tst.l	d4
	bpl.b	.nn2
	neg.l	d4
.nn2:	cmp.l	d3,d4
	bgt.b	.n2
	bset	#0,d7
.n2:	move.l	d7,d1

	movem.l	(sp)+,d0/d2-d7
	rts

side_hit:				; this is where car in d2 drove into the side of car in d3 but I can't be bothered, so it goes in as a head on
head_on::
	moveq	#$f,d0
	and.b	STATS(a0,d2),d0
	cmp.b	#human,d0
	bne.b	.noc2
.doc:	tst.b	crash_flag.w		; cannot crash if already in a crash as it screws up
	bne.b	.noc
	cmp.w	#$800,speed+2.w
	bgt.b	.con
	tst.w	VELOC(a0,d3)
	beq.b	.noc
.con:	move.b	#2,crash_flag.w
	st.b	start_crash.w
	move.w	#10,crash_type.w
	tst.l	viewmove.w
	beq.b	.noc
	move.w	#13,crash_type.w
.noc:	asr.w	revs.w		; halve revs = halve speed
	rts

.noc2:	moveq	#$f,d0
	and.b	STATS(a0,d3),d0
	cmp.b	#human,d0
	beq.b	.doc
	rts

swap_dirs::				; no need to consider what type of vehicle - just swap both the ai V_DIR and the 6(a0,x) directions
	move.w	V_DIR(a0,d2),d0
	move.w	V_DIR(a0,d3),d1
	add.w	#$20,d1
	sub.w	#$20,d0
	move.w	d1,V_DIR(a0,d2)
	move.w	d0,V_DIR(a0,d3)

.if 0
	moveq	#$f,d0
	moveq	#$f,d1
	and.b	STATS(a0,d2),d0
	and.b	STATS(a0,d3),d1
	cmp.b	#human,d0
	beq.b	.nsl1
	clr.w	VELOC(a0,d2)
.nsl1:	cmp.b	#human,d1
	beq.b	.nsl2
	clr.w	VELOC(a0,d3)
.nsl2:
.endif
	movea.l	carsad.w,a0
	move.w	6(a0,d2),d0
	move.w	6(a0,d3),d1
	add.w	#$20,d1
	sub.w	#$20,d0
	move.w	d1,6(a0,d2)
	move.w	d0,6(a0,d3)

	tst.l	viewmove.w
	beq.b	.ok
	movea.l	carsad.w,a0

	moveq	#0,d0
	move.b	grid_position.w,d0
	asl.w	#5,d0
	move.w	6(a0,d0),d0
	neg.w	d0
	move.w	d0,Genstr+2

.ok:	movea.w	#ai_blocks,a0		; return address as it was
	rts

shunt::					; where car d2 hits car d3 up the back
	moveq	#$f,d0
	and.b	STATS(a0,d2.w),d0	; type of back car
	cmp.b	#human,d0
	bne.b	.stopd2			; if drone at back then stop him
	exg	d2,d3
	bsr	read_speed
	exg	d2,d3
	sub.l	#2000,d0		; take about 20mph off
	bpl.b	.ws
	moveq	#0,d0
.ws:	bsr	write_speed
	bra.b	.car2			; don't bother 'read speed' for human, as won't need write speed

.stopd2:bsr	read_speed		; needed in case other car has to be pushed forward
	move.l	d0,d1
	moveq	#$f,d0
	and.b	STATS(a0,d2.w),d0
	cmp.b	#racing,d0
	bne.b	.car2
	clr.w	VELOC(a0,d2.w)
	
.car2:	exg	d2,d3
	moveq	#$f,d0
	and.b	STATS(a0,d2.w),d0	; type of front car
	cmp.b	#human,d0
	bne.b	.nutin			; if drone at back then stop him

	move.l	d1,d0
	bsr	write_speed		; if front car is human then gain speed

.nutin:	exg	d2,d3
	rts


read_speed:
	moveq	#$f,d0
	and.b	STATS(a0,d2.w),d0
	cmp.b	#human,d0
	bne.b	.drone
	move.l	speed.w,d0
	rts
.drone:	move.w	VELOC(a0,d2.w),d0
	mulu	SKILL(a0,d2.w),d0
	asr.l	#2,d0
	rts

write_speed:
	movem.l	d1-d2,-(sp)
	moveq	#$f,d1
	and.b	STATS(a0,d2.w),d1
	cmp.b	#human,d1
	bne.b	.drone
	move.l	speed.w,d1	; get current speed so we can work out revs
	move.l	d0,speed.w
	mulu	revs.w,d0
	bne.b	.ok
.nr:	move.l	speed.w,d0
	move.l	d0,d1
	asr.l	#1,d1
	add.l	d1,d0
	bra.b	.wo
.ok:	tst.l	d1
	beq	.nr		; ZERO PROTECTION
	divu	d1,d0
.wo:	move.w	d0,revs.w
	movem.l	(sp)+,d1-d2
	rts

.drone:	divu	SKILL(a0,d2.w),d0		; SKILL cannot be zero, so safe
	asl.w	#2,d0
	move.w	d0,VELOC(a0,d2.w)
	movem.l	(sp)+,d1-d2
	rts



half_one:				; this no longer stops car, but cuts by half
	asl.w	#5,d2		; start off with car in d2
	moveq	#$f,d0
	and.b	STATS(a0,d2.w),d0
	cmp.b	#human,d0
	bne.b	.drone
	clr.l	speed.w
	clr.w	revs.w
	bra.b	.exit
.drone:	clr.w	VELOC(a0,d2.w)
.exit:	rts

check_channel::
	movem.l	a1/d0-d1,-(sp)
	movea.l	#TABLESTART,a1
	muls	#80,d0
	moveq	#-4,d1
	cmp.l	(a1,d0),d1
	bne.b	.still
	bsr	start_engines
	moveq	#0,d0			; to force eq
.still:	movem.l	(sp)+,a1/d0-d1
	rts



start_engines::				; extend eventually to deal with the no music case
	sfx	engine,$2fff,$3fff,8	; 8 bit sample
	move.l	d0,engine.w
;	sfx	tst2,$16ff,$3fff,9	; FM synth sound
;	move.l	d0,engine2.w
	st.b	engine_on.w
start_drones::
	tst.b	real_num_drones.w
	beq	.exit
	movea.w	#drone_engines,a0
	sfx	engine,$ff,$3fff,10	; one drone car always plays
	move.l	d0,(a0)+
	tst.b	music
	bne	.exit

	sfx	engine,$ff,$3fff,1
	move.l	d0,(a0)+
	cmp.b	#2,real_num_drones.w
	beq.b	.exit
	sfx	engine,$ff,$3fff,2
	move.l	d0,(a0)+
	cmp.b	#3,real_num_drones.w
	beq.b	.exit
	sfx	engine,$ff,$3fff,3
	move.l	d0,(a0)+
	cmp.b	#4,real_num_drones.w
	beq.b	.exit
	sfx	engine,$ff,$3fff,4
	move.l	d0,(a0)+

.exit:	rts

copy_data::
	movem.l	a0-a1,-(sp)
	movea.l	carsad.w,a0
	movea.w	#cars_copy,a1
	moveq	#47,d0
.lp:	move.l	(a0)+,(a1)+
	dbra	d0,.lp

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

doppler:
	sub.l	#70000,d1
	bpl.b	.zero
	neg.l	d1
	asr.l	#2,d1

	asr.l	#8,d2
	asr.l	#2,d2
	add.l	#$70,d2
	rts
.zero:	moveq	#0,d1
	rts


deal_with_first::			; plays (or switches off) based on contents of d1/d2
	bsr	doppler		; convert distance (d1) and speed (d2) into volume (d1) and pitch (d2)
	tst.l	d1
	bne.b	.okv		; if audible then don't switch off !
	move.l	(a3)+,d0
	beq.b	.exit
	kill_sfx		; zaps it
	clr.l	-4(a3)		; and zap its id number
.exit:	rts

.okv:	move.l	(a3)+,d0		; get id number
	bne.b	.oka			; if its already playing then don't restart it

	movem.l	a0-a4/d1-d4,-(sp)
	move.l	#$3fff,d2
	move.l	#FX_engine,d0
	jsr	DoEffect
	movem.l	(sp)+,a0-a4/d1-d4
	move.l	d0,-4(a3)

.oka:	exg.l	d2,d1
	mod_sfx				; applies pitch change
	rts

drone_sounds::
	tst.b	real_num_drones.w
	beq.b	.ex
	tst.b	engine_on.w
	bne.b	.eo
.ex:	rts

.eo:	movea.w	#drone_engines,a3
	tst.b	music.w
	beq.b	.allon

	movem.l	d0-d4/a0-a1,-(sp)
	movea.w	#drone_dis,a0
	moveq	#0,d0
	move.b	real_num_drones,d0
	
	move.l	(a0)+,d1
	move.l	(a0)+,d2

	subq.l	#2,d0
	bmi.b	.ok
	bra.b	.nn

.lp:	move.l	(a0)+,d3
	move.l	(a0)+,d4

	cmp.l	d3,d1
	ble.b	.nn

	move.l	d3,d1
	move.l	d4,d2

.nn:	dbra	d0,.lp

.ok:	moveq	#10,d3			; make d3 the channel number
	bsr	deal_with_first
	movem.l	(sp)+,d0-d4/a0-a1
	rts

.allon:	movem.l	d0-d4/a0-a1,-(sp)
	movea.w	#drone_dis,a0

	move.l	(a0)+,d1
	move.l	(a0)+,d2
	moveq	#10,d3
	bsr	deal_with_first

	moveq	#0,d4
	move.b	real_num_drones.w,d4
	beq.b	.exit
	subq.b	#1,d4
	bra.b	.dlp

.lp2:	move.l	(a0)+,d1
	move.l	(a0)+,d2
	moveq	#5,d3
	sub.l	d4,d3
	bsr	deal_with_first
.dlp:	dbra	d4,.lp2

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


;.include 'debug.s'		; only required if stop button fails, and internal self debugging required
.include 'hexcon.s'
.include 'ai.inc'



