.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 '<' ; enter ANSI mode .ascii '(B' ; select ascii character set .ascii '[2J' ; erase entire screen .ascii '[1;1H' ; jump to top left corner .ascii <10> ; linefeed .if ne $$SOCCER .ascii '#3 SOCCER' ; double-height top half .ascii <13><10> .ascii '#4 SOCCER' ; double-height bottom half .endc .if ne $$MQIX .ascii '#3 MULTI QIX' ; double-height top half .ascii <13><10> .ascii '#4 MULTI QIX' ; double-height bottom half .endc .ascii <13><10><10> .ascii '#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 'Y' <31+24> <31+1> ; col 1, row 24 .ascii 'G' ; exit graphics .ascii <7> ' Please wait for next game ...' .ascii '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 ? 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 ;+ ; 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 ;+ ; 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 ; 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 ;+ ; 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 ;+ ; 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 ;+ ; 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 ;+ ; 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 ;+ ; 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 ;+ ; 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