	.title	MULTIM	Multi user game control
;+
;	if $$MQIX is non zero
;
;	if $$SOCCER is non zero
;-

	$$SOCCER= 0
	$$MQIX  = 1

.if ndf $$MQIX
	$$SOCCER= 1
	$$MQIX  = 0
.endc


	$dibdef
	$iodef
	$qiodef
	$secdef
	$jpidef

	.default displacement word

esc		= 27

snake		= 8		; number of snakes

;	meaning of event flags in cluster 2

flag$v_master	= 0		; set if a master snake exists
flag$v_read	= 1		; set if all snakes should read command
flag$v_update	= 2		; set if all snakes should update screen
flag$v_game	= 3		; set if game is in progress
flag$v_endofgame= 4		; set if we have reached the end of the game
flag$v_synch	= 5
flag$v_done	= 8		; set if operation (read,update) is complete

check_timer	= 13		; check timer id

flag$v_io	= 9		; event flag in cluster 0 (set on read completion)


	.psect	$rodata	nowrt, noexe, shr, pic, long

ttname_descr:
	.ascid	/TT/

mbxcnv:
	.ascid	/_MBA!UW:/	; convert mbx unit number to physical name

mbxbuf_descr:
	.word	mbxbuf_siz, 0
	.long	mbxbuf

dibbuf_descr:
	.word	dib$k_length, 0
	.long	dibbuf

	.align long
snake_desc_2:
.if ne $$SOCCER
	.ascid	/SOCCER_1/		; name of snake event flags
.endc
.if ne $$MQIX
	.ascid	/MQIX_1/
.endc

	.align	long
snake_map_name:
.if ne $$SOCCER
	.ascid	/SOCCER_DATA/
.endc
.if ne $$MQIX
	.ascid	/MQIX_DATA/
.endc

text = .
	.ascii	<esc>'<'		; enter ANSI mode
	.ascii	<esc>'(B'		; select ascii character set
	.ascii	<esc>'[2J'		; erase entire screen
	.ascii	<esc>'[1;1H'		; jump to top left corner
	.ascii	<10>			; linefeed
.if ne $$SOCCER
	.ascii	<esc>'#3                 SOCCER' ; double-height top half
	.ascii	<13><10>
	.ascii	<esc>'#4                 SOCCER' ; double-height bottom half
.endc
.if ne $$MQIX
	.ascii	<esc>'#3                 MULTI QIX' ; double-height top half
	.ascii	<13><10>
	.ascii	<esc>'#4                 MULTI QIX' ; double-height bottom half
.endc
	.ascii	<13><10><10>
	.ascii	<esc>'#6        Thank you for playing'
	.ascii	<13><10><10>
text_len = . - text
	.align	long
text_end_game:
	.long	2
	.long	text
	.address 10$
10$:	.long	text_len

text = .
	.ascii	<13><10><10>
	.ascii	'Game aborted because master '
	.ascii	'player'
	.ascii	' quitted'<13><10><10>
text_len = . - text
	.align	long
text_abort:
	.long	2
	.long	text
	.address 10$
10$:	.long	text_len

text = .
	.ascii	<esc> 'Y' <31+24> <31+1>	; col 1, row 24
	.ascii	<esc> 'G'			; exit graphics
	.ascii	<7> ' Please wait for next game ...'
	.ascii	<esc> 'F'			; enter graphics
text_len = . - text
	.align	long
text_wait:
	.long	2
	.long	text
	.address 10$
10$:	.long	text_len

	.align	long
username_jpi:
	.word	12, jpi$_username
	.address username_buf
	.address username_siz
	.long	0

	.align	long
start_wait:
	.long	-10000000*5, -1		; wait 5 seconds
second_1:
	.long	-10000000*1, -1		; wait 1 second
second_2:
	.long	-10000000*2, -1		; wait 2 seconds

.if ne $$SOCCER
update_wait:
	.long	-100000*40, -1		; wait 40/100 ths of a second
.endc

.if ne $$MQIX
update_wait:
	.long	-100000*30, -1		; wait 30/100 ths of a second
.endc

check_wait:
	.long	-10000000*5, -1		; wait 5 seconds for checking
valid_move:
	.long	^B101110100		; valid moves are 2,4,6,8 and 5!!
start_direction:
.if ne $$MQIX
	.byte	2, 8, 2, 8, 2, 8, 6, 4	; initial move directions for snake
.endc
;.if ne $$TANK
;	.byte	6, 4, 4, 6, 2, 8, 6, 4	;  for tank
;.endc
	.align	long
add_head_par:
	.long	1			; parameter list to Pascal routine
	.address move			; each players move
update_par:
	.long	2
	.address outbuf
	.address screen_len
update_par2:			; if we have died, then there is no head
	.long	2		; to change to a diamond, so write screen
	.address screen_buf	; update directly from global memory.
	.address screen_len

	.psect	$rwbuf	wrt, noexe, noshr, pic, page

mbxname_len = 16
mbxname:			; room to hold the physical mbx name
	.blkb	mbxname_len
mbxname_descr:
	.word	mbxname_len, 0
	.long	mbxname
mbxiosb:
	.long	0,0
mbxbuf_siz = 32
mbxbuf:
	.blkb	mbxbuf_siz

dibbuf:
	.blkb	dib$k_length

	.align	long
ttiosb:
	.long	0,0

save_bit:	.long

ttmode:		.blkq
ttmode_save:	.blkq

ttbuf_siz = 128
ttbuf:
	.blkb	ttbuf_siz
	.align	page

.if ne $$MQIX
trans_table:
	.blkb	256		; converts your number to diamond
.endc

outbuf_siz = 2048
outbuf::
	.blkb	outbuf_siz

map_range:
	.address share_data
	.address share_data+<512*3>
ret_range:
	.long	0, 0


	.psect	$sharedata wrt, noexe, shr, pic, page
share_data:

game_count:
	.long			; count of number of games played
master_flag:
	.long			; = 1 if we are master snake
abort:
	.long			; = 1 if all snakes should abort
player_bits:
	.long			; bit set if that snake is playing
players:
	.long			; bit set if that snake is reserved
other_players:
	.long			; used by master snake to wait for other
				; snakes to indicate operation completed
move_count:
	.long			; incremented every move.  Used for detecting
				; other snakes hanging the game
game_going:
	.long			; <> 0 if a game is going
you_just_died:
	.long			; bit I set if snake I just died
seed:
	.long			; random number seed
start_position:
	.blkl	snake		; position of starting (1-8)
;
;		5
;    1	+---------------+  3
;	|		|
;	|		|
;    7	|		|  8
;	|		|
;	|		|
;    4	+---------------+  2
;		6
;
score:
	.blkl	snake		; players' score
n_games:
	.blkl	snake		; # of games each player has played
wins:
	.blkl	snake		; # of wins for each player
player_pos:
	.blkl	snake		; starting position of each snake
	.align	quad
move:
.if ne $$SOCCER
	.blkw	snake		; each players move (word)
.endc
.if ne $$MQIX
	.blkb	snake		; each players move (word)
	.blkb	snake		; we had some problems overwritting name
.endc
name_size = 32
name:
	.blkb	name_size * snake ; each snakes name (32 chars long)
. = . + 512 - < . - share_data >
	.align	long
screen_len:
	.long			; # chars to be output
screen_buf:
	.blkb	2048		; buffer containing screen update
. = . + <512*6> - < . - share_data >


	.psect	$rwdata	wrt, noexe, noshr, pic, long

ttchan:
	.word
mbxchan:
	.word
data_ready:
	.word
master:
	.word			; = 1 if we are master snake
control_c_flag:
	.word			; non zero if ^C typed
dead:
	.word			; bit I set if snake I just died
	.align	long
cluster_2:
	.long
cluster_3:
	.long
player:
	.long
player_efn:			; my player efn in cluster 2
	.long
current_players:
	.long
chars_left:			; # of chars left in buffer
	.long
char_pointer:
	.long			; address of next character
last_move_count:
	.long
username_buf:
	.ascii	'            '	;	.blkb	12
username_siz:
	.long

outbuf_qio:
	$qio	func=io$_writevblk!io$m_noformat,-
		p1=outbuf
output_qio:
	$qio	func=io$_writevblk!io$m_noformat

read_qio:
	$qio	func=io$_readvblk!io$m_noecho, -
		iosb=ttiosb, efn=flag$v_io, -
		p1=ttbuf, p2=1, -		; read 1 char with wait
		p4=term_blk			; say no terminators

;	$qio	func=io$_readvblk!io$m_timed!io$m_noecho, - ; !io$m_nofiltr,-
;		iosb=ttiosb,-
;		p1=ttbuf, p2=ttbuf_siz, p3=0	; wait time = 0

term_blk:
	.long	0, 0		; no terminators

exit_block:			; exit handler block
	.long
	.address snake_exit
	.long	1		; 1 argument
	.address 10$
10$:	.long			; exit reason


	.psect	$$code	nowrt, exe, shr, pic, long

	.entry	-
TTINIT, ^m<>
;+
; Create a mailbox.  Assign a channel to terminal with an associated mailbox.
;-
.if ne 0
	$crembx_s	chan=mbxchan, promsk=#^xFF00
	bsbw		error
	$getchn_s	chan=mbxchan, pribuf=dibbuf_descr
	bsbw		error
	$fao_s		ctrstr=mbxcnv, outbuf=mbxname_descr,-
			outlen=mbxname_descr, p1=dibbuf+dib$w_unit
.endc
	$assign_s	devnam=ttname_descr, chan=ttchan
;			mbxnam=mbxname_descr
	bsbw	error
	movw	ttchan, outbuf_qio+qio$_chan		;store channel #
	movw	ttchan, output_qio+qio$_chan		;store channel #
	movw	ttchan, read_qio+qio$_chan		;store channel #
	$qiow_s	func=#io$_setmode!io$m_ctrlcast, chan=ttchan,-
		p1=control_c
	ret

.if ne 0
	$qiow_s func=#io$_sensemode, chan=ttchan, -
		iosb=ttiosb, p1=ttmode	; get terminal characteristics
	bsbw	error
	movzwl	ttiosb, r0
	bsbw	error
	movq	ttmode, ttmode_save
	bbss	#tt$v_escape, ttmode+4, 80$	; want escape mode
80$:	$qiow_s func=#io$_setmode, chan=ttchan, p1=ttmode
	ret
.endc

	.entry	-
TT1CHAR,	^m<>
	clrb	ttbuf
	$qiow_s	func=#io$_readvblk!io$m_timed!io$m_noecho!io$m_nofiltr,-
		chan=ttchan, iosb=ttiosb,-
		p1=ttbuf, p2=#1, p3=#0	; wait time = 0
	cvtbl	ttbuf, r0
	cmpb	r0, #13			; is it <CR> ?
	bneq	100$
	clrb	data_ready
100$:	ret

TTREAD::
;	blbs	control_c_flag, 10$

	tstl	ttiosb			; did we read any characters ?
					; has read completed ?
	beql	100$			; br if no
	movzbl	ttbuf, r2		; get character before next read
	$qio_g	read_qio	 	; start read of another character
;
;	$qiow_s	func=#io$_writevblk,chan=ttchan,-	; debug write
;		p1=ttbuf, p2=ttiosb+2, p4=#^x1000

	movl	r2, r0			; copy character back into r0
	cmpb	r0, #^A/a/		; is it lowercase
	bgeq	50$			; br if yes
80$:
	cmpb	r0, #^A/ /
	beql	90$
	cmpb	r0, #^A/5/
	beql	90$
	rsb
90$:
	movb	#^X80, r0
	rsb
50$:
	bicb2	#^X20, r0		; make into uppercase
	brb	80$			; go check for "5", " "
100$:
	clrl	r0
	rsb


	.entry	-
MBXREAD,	^m<>
;+
; This is an AST routine which executes when the mailbox record has been read.
; The record itself is a status message which is assumed to say that
; unsolicited data is available at the terminal
;-
	blbc	mbxiosb, 100$		; on error, dont re-que read
;	we could have SS$_CANCEL or SS$_ABORT from the $CANCEL in the
;	exit handler
	movb	#1, data_ready		; indicate data is there
	bsbw	queue_mbxread		; queue another read request
100$:
	ret

QUEUE_MBXREAD:
	$qio_s	efn=#2, func=#io$_readvblk, chan=mbxchan, iosb=mbxiosb,-
		astadr=mbxread,-
		p1=mbxbuf, p2=#mbxbuf_siz
	blbc	r0, 100$
	rsb
100$:
	bsbw	error
	rsb

TTWRITE::
;+
;	bsbw	ttwrite
;	r3 contains length of buffer to write
;	the buffer is outbuf
;-
	movl	r3, outbuf_qio+qio$_p2		; store length of buffer
	$qiow_g	outbuf_qio
	blbc	r0, 100$
	rsb
100$:
	bsbw	error
	rsb


	.entry	-
snake_screen, ^m<r2,r3,r4,r5>
;+
;	CALL SNAKE_SCREEN( array, length )
;	BYTE ARRAY( LENGTH )
;	copies string to update screen into shared memory
;-
	movl	@8(ap), r0		; get length
	movl	r0, screen_len		; store length
	movc3	r0, @4(ap), screen_buf	; copy text
	ret

	.entry	-
snake_write, ^m<r2,r3>
;+
;	CALL SNAKE_WRITE( array, length )
;	BYTE ARRAY( LENGTH )
;	writes buffer to terminal in noformat mode
;-
	movl	4(ap), r3			; get address
	movl	@8(ap), r2			; get length
50$:
	movl	r2, r0				; copy length
	cmpw	r0, #512			; is it too big
	bleq	80$				; br if not
	movl	#512, r0
80$:
	movl	r3, output_qio+qio$_p1		; store address of buffer
	movl	r0, output_qio+qio$_p2		; store length of buffer
	addl2	r0, r3				; update address
	subl2	r0, r2				; update length
	$qiow_g	output_qio
	blbc	r0, 100$
	tstl	r2				; anything else to write ?
	bgtr	50$				; br if yes
	ret
100$:
	bsbw	error
	ret

	.entry	-
snake_dead, ^m<>
;+
;	CALL SNAKE_DEAD( player # )
;-
	subl3	#1, @4(ap), r0			; get # of snake who died
	bbss	r0, you_just_died, 100$		; set flag saying he died
100$:	ret



CANCELTYPEAHEAD::
	tstw	ttchan		; check channel is open
	beql	100$
	$qiow_s	func=#io$_readvblk!io$m_purge!io$m_timed,-
		chan=ttchan, -
		p1=ttbuf, p2=1, p3=0	; do read with 0 length buffer (p2)
100$:	ret				; return with status in r0

ERROR:
	blbs	r0, 100$
	pushl	r0
	calls	#1, G^lib$signal
100$:
	rsb

	.entry	-
control_c, ^m<>
	movb	#1, control_c_flag
	ret


	.page
	.entry	-
SNAKE_INIT, ^m<r2,r3,r4,r5>				; snake game
;+
;	I = SNAKE_INIT( player # , game )
;	returns I = 1 if you are master snake.
;	returns your player # as a integer
;	returns game = 1 if there is a game in progress
;-

	calls	#0, ttinit			; open terminal
;
	$ascefc_s efn=#64, name=snake_desc_2	; associate event flag cluster
	bsbw	error
;
;	$open	fab=snake_fab			; open section file
;	bsbw	error

	$deltva_s inadr=map_range		; delete memory were global
	bsbw	error				;  memory will be mapped
	$crmpsc_s inadr=map_range, flags=#sec$m_gbl!sec$m_wrt!sec$m_pagfil,-
		gsdnam=snake_map_name, - ; chan=snake_fab+fab$l_stv, 
		pagcnt=#4
	bsbw	error
	cmpl	r0, #ss$_created		; are we first to map section
	bneq	50$				; no
	movab	share_data+4, r3
	movc5	#0, (r3), #0, #512-4, (r3)	; clear everything except count
	$clref_s efn=#flag$v_game+64		; say not game
	movl	#39814571, seed			; init random n.g. seed
	movl	#snake, r0			; 8 snakes
20$:
	movl	r0, start_position-4[r0]	; init start position
	sobgtr	r0, 20$
50$:
	blbc	abort, 60$			; if not abort --> 60$
	callg	text_abort, snake_write
	$exit_s #1
60$:
;	$qio_g	read_qio	 	; start read of another character
;	the above line shifted into SNAKE_START

;	bsbw	queue_mbxread			; start terminal read
;
	bbss	#0, master_flag, 100$		; see if a master snake exists
			; this should be interlocked on a multi-processor
;+
; We will have to be the master snake
;-
	movb	#1, master			; indicate we are master snake
	$setef_s efn=#7				; set for first call
100$:
;
	clrl	r1				; start at player 0 (bit0=1)
150$:
	bbcs	r1, players, 200$		; see if this snake is free
	incl	r1				; go to next snake
	cmpl	r1, #snake			; have we checked all snakes?
	blss	150$				; no --> 150$
	mnegl	#1, r1				; player = -1 means none
200$:
	movl	r1, player			; store my snake number (0-7)
	movl	player, @4(ap)			;  and return it
500$:
	movzbl	game_going, @8(ap)		; return game going flag

	movl	r1, r3
	blss	600$				; no snakes available
	$getjpi_s itmlst=username_jpi		; get our username
	mull2	#name_size, r3			; get offset to start of name
	movc5	username_siz, username_buf, #^a/ /, #name_size, name(r3)
						; copy username
600$:
	$dclexh_s desblk=exit_block		; declare exit handler
	bsbw	error
;+
;	init translation table for converting all ascii chars for your
;	snake to a diamond
;-
	movab	trans_table, r3
	clrl	r4
700$:
	movb	r4, (r3)+			; store byte
	aoblss	#256, r4, 700$
	movl	player, r0			; get our player #
	blss	800$				; br if no players available
	movb	#^A/`/, trans_table+^A/1/+^X80(r0)	; convert us to diamond
	movab	trans_table+128, r3
	movb	#^A/1/, ^A/a/(r3)
	movb	#^A/2/, ^A/b/(r3)
	movb	#^A/3/, ^A/c/(r3)
	movb	#^A/4/, ^A/d/(r3)
	movb	#^A/5/, ^A/e/(r3)
	movb	#^A/6/, ^A/f/(r3)
	movb	#^A/7/, ^A/g/(r3)
	movb	#^A/8/, ^A/h/(r3)
	movb	#^A/ /, trans_table+^A/a/+^X80(r0)	; convert us to space
800$:

	movzbl	master, r0			; return master snake status
	ret

master_wait:
;+
; master snake has to wait some time for other snakes to start playing
; called from SNAKE_START
;-
	incl	game_count			; say another game being played
220$:	clrb	player_bits			; no other players
	bbss	player, player_bits, 400$	; say I am playing
400$:
	$clref_s efn=#flag$v_synch+64

.if ne $$MQIX
;+
;	randomise starting positions
;-
	moval	start_position, r4		; starting position numbers
	movl	#1, r2				; snake index (start at 1)
500$:
	pushal	seed				; random number seed
	calls	#1, G^mth$random		; random real in r0
	addl3	#1, r2, r3			; snake + 1
	cvtlf	r3, r3				; as real
	mulf2	r3, r0				; get snake to change pos with
	cvtfl	r0, r0
	movl	(r4)[r0], r1			; swap these positions
	movl	(r4)[r2], (r4)[r0]
	movl	r1, (r4)[r2]
	aobleq	#7, r2, 500$
;
	moval	start_position, r4
	movab	move, r3
	movl	#snake, r2			; number of snakes
600$:
	movl	(r4)+, r0			; get start position (1-8)
	movb	start_direction-1[r0], (r3)+	; copy start direction
	sobgtr	r2, 600$
.endc

.if ne $$SOCCER
	movab	move, r3
	clrq	(r3)+				; no move to start with
	clrq	(r3)+
.endc
;
	$setimr_s efn=#flag$v_game+64,- 
			daytim=second_1		; wait a time for other snakes
	$waitfr_s efn=#flag$v_game+64		; say that a game is going
	movb	#1, game_going			; say game going
	$clref_s efn=#flag$v_endofgame+64	; say not end of game
	$setef_s efn=#7				; sets event flag for first
						;  call to snake_wait
	$setimr_s efn=#flag$v_synch+64,-		
			 daytim=start_wait
	$waitfr_s efn=#flag$v_synch+64
	; this allows other snakes to set bit saying they are playing

	rsb

	.entry	-
SNAKE_START, ^m<r2,r3,r4>
;+
;	CALL SNAKE_START( PLAYERS , START_POSITION )
;	INTEGER PLAYERS, START_POSITION(8)
;	waits 5? seconds for other players to run game
;	The master snake is assumed to have waited some additional time
;	Returns PLAYERS, bit I <> 0 if that player is active
;	START_POSITION(I) is the starting location of snake I, (1-8)
;-
	$cancel_s chan=ttchan			; cancel the outstanding read

	blbc	master, 500$			; are we master snake ?
	bsbw	master_wait
	brb	800$
200$:
	$exit_s #1				; game aborted so stop
500$:
	$waitfr_s efn=#flag$v_game+64		; wait until a game starts
	blbs	abort, 200$			; if game stopped --> 200$
	bbss	player, player_bits, 600$	; say I am playing
600$:	$waitfr_s efn=#flag$v_synch+64		; synchronise
	blbs	abort, 200$			; if game stopped --> 200$
800$:
	movzbl	player_bits, r4			; get player bits
	ashl	#flag$v_done, r4, other_players ; used by master snake
	movl	r4, @4(ap)			; store player bits
	clrl	chars_left			; cancel type ahead
	clrb	data_ready			; make us do a read
;;	calls	#0, canceltypeahead
	$qio_g	read_qio	 	; start read of another character

.if ne $$MQIX
;	return starting positions
	moval	start_position, r0		; address of new positions
	movl	8(ap), r2			; address of where to put them
	movl	#snake, r1			; number of snakes
910$:
	movl	(r0)+, (r2)+
	sobgtr	r1, 910$
.endc

;	init starting directions
	movaw	move, r2			; address of where to put them
	movl	#snake, r1			; number of snakes
900$:
.if ne $$MQIX
	movb	#^A/9/, (r2)+			; 9 = invalid move
.endc

.if ne $$SOCCER
	movw	#5, (r2)+			; 5 = stop
.endc

	sobgtr	r1, 900$

	mnegl	#1, last_move_count		; invalidate last counter

	ret


SNAKE_WAIT::
;+
;	BSBW SNAKE_WAIT
; 	wait until we are told to read players command(s)
;-
	blbs	master, 200$			; are we master snake ?
	$waitfr_s efn=#flag$v_read+64		; if not then wait for flag
	rsb
200$:	; master snake waits and then sets flag for all players
	$cantim_s reqidt=#check_timer		; cancel checking timer
	$waitfr_s efn=#7			; wait for previous timer
	$setimr_s efn=#8, daytim=check_wait, -
		astadr=check_ast, reqidt=#check_timer ; set off checking timer
	$setimr_s efn=#7, daytim=update_wait
	$clref_s efn=#flag$v_update+64		; clear next flag to wait on
	movl	#flag$v_done+64, r2		; clear each players done flag
	$clref_s efn=r2				; player 0
	incl	r2
	$clref_s efn=r2
	incl	r2
	$clref_s efn=r2
	incl	r2
	$clref_s efn=r2
	incl	r2
	$clref_s efn=r2
	incl	r2
	$clref_s efn=r2
	incl	r2
	$clref_s efn=r2
	incl	r2
	$clref_s efn=r2				; player 7
;
	$clref_s efn=#flag$v_synch+64
	$setef_s efn=#flag$v_read+64		; tell everybody to do read
	rsb


SNAKE_READ::
;+
;	BSBW SNAKE_READ
;	read all users moves and store them into the byte array MOVES(*)
;-
	bsbw	ttread				; read users commands, if any
	movl	player, r1			; get our player number

.if ne $$SOCCER
	tstw	r0				; anything typed ?
	beql	10$				; br if no
	movw	r0, move[r1]			; store our move
.endc

.if ne $$MQIX
	tstb	r0				; anything typed ?
	beql	10$
	blss	5$
	movb	r0, move[r1]			; store our move
	brb	10$
5$:
	bisb2	r0, move[r1]			; r0 = ^X80 (set parity bit)
.endc

10$:
	addl3	#flag$v_done+64, player, r1
	$setef_s efn=r1				; say that read is complete
900$:
	blbc	master, 1000$
	$wfland_s efn=#64, mask=other_players	; wait for all players to read
	incl	move_count			; onto next move
	$clref_s efn=#flag$v_read+64		; clear next flag to wait on
	$setef_s efn=#flag$v_update+64		; tell everybody to update
	brb	1050$
1000$:
	$waitfr_s efn=#flag$v_update+64		; wait for all reads to complete
	blbs	master_flag, 1050$		; check for master snake OK
	movl	player, r1			; get our player number
	clrb	move(r1)			; store our move ( quit )
1050$:
	rsb


	.entry	-
SNAKE_PLAY, ^m<r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>
;+
;	called once at the start of the game.
;	I then call the Pascal routine ADD_HEAD to perform the moves.
;-
	blbs	master, 1000$		; master snake does all the work
100$:
	bsbw	snake_wait
	bsbw	snake_read
	$waitfr_s efn=#flag$v_synch+64	; wait until screen update there
	bsbw	snake_update		; update screen
	brb	100$

900$:
	clrb	game_going		; tell other snakes games finished
	$setef_s efn=#flag$v_synch+64	; wake other snakes up
	bsbb	snake_update		; write out last move
	ret

1000$:	; master snake moves every snake
	bsbw	snake_wait
	bsbw	snake_read
	callg	add_head_par, L^add_head ; call Pascal routine
					; returns 1 if game still going
	blbc	r0, 900$		; game has ended --> 900$
	$setef_s efn=#flag$v_synch+64	; wake other snakes up
	bsbb	snake_update		; update our screen
	brb	1000$


	.enable local_block
500$:
	$exit_s #1			; game aborted, so exit image

snake_update::
	blbs	abort, 500$
	blbs	dead, 80$		; if we are dead, then no head

.if ne $$SOCCER
	movc3	screen_len, screen_buf, outbuf	; copy update string
.endc

.if ne $$MQIX
;	replace your snake head with a diamond symbol
	movl	screen_len, r0		; get length of output string
	movtc	r0, screen_buf, #^A/ /, trans_table, r0, outbuf
.endc

;	movl	player, r2		; get my snake number
;	addw2	#^A/1/+^X80, r2		; get number with parity bit set
;	locc	r2, screen_len, outbuf
;	beql	50$			; could not find it !!!
;	movb	#^A/`/, (r1)		; change to diamond
50$:
	callg	update_par, snake_write
	blbc	game_going, 100$	; bit clear if game has finished
	bbsc	player, you_just_died, 60$ ; see if we just died
	rsb
60$:	movb	#1, dead		; say we are dead
	callg	text_wait, snake_write	; tell them to wait for next game_exit,
	rsb
80$:	; dont copy buffer if no head to update because we are dead
	callg	update_par2, snake_write
	blbc	game_going, 100$
	rsb
100$:
	$setimr_s efn=#6, daytim=second_1	; so we can see last move
	$waitfr_s efn=#6
	ret				; return from SNAKE_PLAY if end game
	.disable local_block


	.entry	-
CHECK_AST, ^m<r2,r3,r4>
;+
;	called when check_timer expires (2 seconds)
;	we should only get here if one of the other snakes has aborted
;	or ^S ed  .  Force the snake out of the game.
;-
	$readef_s efn=#64, state=cluster_2	; get done flags
	extzv	#flag$v_done, #snake, cluster_2, r2 ; get done flags
;	movb	other_players+1, r3		; get other players
	bicw3	r2, other_players+1, r3		; find players who have not
						; responded
	bicw2	r3, other_players+1		; and say they are dead
	clrl	r2				; snake 0
100$:	bbc	r2, r3, 200$
	clrb	move(r2)			; say snake has quitted
	addl3	#flag$v_done+64, r2, r0		; get event flag
	$setef_s efn=r0				; set event flag so I will
						; wake up on return from here
200$:	aoblss	#snake, r2, 100$		; for all 8 snakes

	ret


	.entry	-
SNAKE_GAME_END, ^m<>
;+
;	synchronizes the end of the game
;-
	clrb	dead			; we are not dead
	blbc	master, 500$		; if not master snake --> 500$
	$clref_s efn=#flag$v_game+64	; say game not in progress
	clrb	game_going		; and again
	$setimr_s efn=#flag$v_endofgame+64, daytim=second_2
	clrw	you_just_died		; reset died flags
500$:
	$waitfr_s efn=#flag$v_endofgame+64 ; wait for end of game
	blbs	abort, 800$		; if we should abort --> 800$
	ret
800$:	; we must abort. Probably because master snake stopped
	$exit_s #1


	.entry	-
SNAKE_EXIT, ^m<r2,r3,r4,r5>
;+
;	called as an exit handler
;-
;	$cancel_s chan=mbxchan		; cancel mailbox read

	movl	player, r3		; get my snake number
	blss	80$			; we never were playing
	clrb	move(r3)		; make next move a quit
	addl3	#flag$v_done, r3, r2	; get done bit
	bbcc	r2, other_players, 50$	; stop master snake from waiting for me
50$:	addl2	#64, r2			; make into event flag
	$setef_s efn=r2			; say input done
	bbcc	r3, players, 60$	; say this snake available
60$:
	clrl	score[r3]		; zero score
	clrl	n_games[r3]		; zero # of games played
80$:
	blbc	master, 100$		; are we master snake ?
	movb	#1, abort		; tell all other snakes to abort
	clrb	master_flag		; say no master snake
	$setef_s efn=#flag$v_read+64	; wake everybody up
	$setef_s efn=#flag$v_update+64
	$setef_s efn=#flag$v_endofgame+64
	$setef_s efn=#flag$v_synch+64
	$setef_s efn=#flag$v_game+64	; for people waiting for a game
100$:

;	$qiow_s func=#io$_setmode, chan=ttchan, p1=ttmode ;reset terminal

;	clear screen and put out of graphics mode
	callg	text_end_game, snake_write
	blbc	abort, 200$		; game is not being aborted --> 200$
	callg	text_abort, snake_write
200$:
	$deltva_s inadr=ret_range	; delete global section
;	$dassgn_s chan=snake_fab+fab$l_stv ; deassign channel

	ret

	
	.entry	-
NAME_SET, ^m<r2,r3,r4,r5>
;+
;	CALL NAME_SET( name )
;	set this players name
;-
	mull3	#name_size, player, r3	; get our player number (0-7)
	addl2	#13, r3			; skip username
	movc3	#name_size-13, @4(ap), name(r3) ; store name in shared memory
	ret

	.entry	-
NAME_GET, ^m<r2,r3,r4,r5>
;+
;	CALL NAME_GET( name , player # )
;	returns the name of specified player (1-8)
;-
	subl3	#1, @8(ap), r1		; get player number (0-7)
	mull2	#name_size, r1		; offset to this players name
	movc3	#name_size, name(r1), @4(ap) ; return players name
	ret

	.entry	-
SCORE_SET, ^m<>
;+
;	CALL SCORE_SET( player #, score , # games , # wins )
;-
player_arg = 4
score_arg = 8
games_arg = 12
wins_arg = 16
	subl3	#1, @player_arg(ap), r1		; get our player # (0-snake)
	movl	@score_arg(ap), score[r1]	; store score
	movl	@games_arg(ap), n_games[r1]
	movl	@wins_arg(ap), wins[r1]
	ret

	.entry	-
SCORE_GET, ^m<>
;+
;	CALL SCORE_GET( player , score , # games , # wins )
;-
;player_arg = 4
;score_arg = 8
;games_arg = 12
;wins_arg = 16
	subl3	#1, @player_arg(ap), r1		; get player # (0-snake)
	movl	score[r1], @score_arg(ap)	; return score
	movl	n_games[r1], @games_arg(ap)	; return # of games played
	movl	wins[r1], @wins_arg(ap)		; return # of wins
	ret

	.entry	-
snake_game_count, ^m<>
;+
;	CALL SNAKE_GAME_COUNT( # games )
;	returns # of games played (total)
;-
	movl	game_count, @4(ap)
	ret

	.end
