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

; Program Description

; Author: Chris Heng

; Description

;       Minimalistic, multiplayer pacman game.

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

DSEG SEGMENT

	; data segment

	; format: LABEL DATATYPE INITIAL_VALUE

	; e.g.

	; MESSAGE db 'HELLO WORLD!$'

	hex_tab         db '0123456789abcdef'

	prefix          db '0x$'

	curr_vmod       db ?

	new_vmod        db 12h



	BLACK           equ 00h

	BLUE            equ 01h

	GREEN           equ 02h

	CYAN            equ 03h

	RED             equ 04h

	MAGENTA         equ 05h

	BROWN           equ 06h

	GREY            equ 07h

	DARK_GREY       equ 08h

	LIGHT_BLUE      equ 09h

	LIGHT_GREEN     equ 0ah

	LIGHT_CYAN      equ 0bh

	LIGHT_RED       equ 0ch

	LIGHT_MAGENTA   equ 0dh

	YELLOW          equ 0eh

	WHITE           equ 0fh



	start_x         equ 40

	start_y         equ 40

	bsize           equ 35



	colors          db CYAN,BLUE,CYAN,BLUE,BLUE,BLUE,CYAN,BLUE,CYAN

			db BLUE,RED,BLUE,RED,RED,RED,BLUE,RED,BLUE

			db CYAN,RED,CYAN,BLUE,CYAN,BLUE,CYAN,RED,CYAN

			db BLUE,RED,BLUE,RED,RED,RED,BLUE,RED,BLUE

			db CYAN,BLUE,CYAN,BLUE,CYAN,BLUE,CYAN,BLUE,CYAN

			db BLUE,RED,BLUE,RED,RED,RED,BLUE,RED,BLUE

			db CYAN,RED,CYAN,BLUE,CYAN,BLUE,CYAN,RED,CYAN

			db BLUE,RED,BLUE,RED,RED,RED,BLUE,RED,BLUE

			db CYAN,BLUE,CYAN,BLUE,BLUE,BLUE,CYAN,BLUE,CYAN



	colors_db       db CYAN,BLUE,CYAN,BLUE,BLUE,BLUE,CYAN,BLUE,CYAN

			db BLUE,RED,BLUE,RED,RED,RED,BLUE,RED,BLUE

			db CYAN,RED,CYAN,BLUE,CYAN,BLUE,CYAN,RED,CYAN

			db BLUE,RED,BLUE,RED,RED,RED,BLUE,RED,BLUE

			db CYAN,BLUE,CYAN,BLUE,CYAN,BLUE,CYAN,BLUE,CYAN

			db BLUE,RED,BLUE,RED,RED,RED,BLUE,RED,BLUE

			db CYAN,RED,CYAN,BLUE,CYAN,BLUE,CYAN,RED,CYAN

			db BLUE,RED,BLUE,RED,RED,RED,BLUE,RED,BLUE

			db CYAN,BLUE,CYAN,BLUE,BLUE,BLUE,CYAN,BLUE,CYAN



	cur_x           dw ?

	cur_y           dw ?

	count           dw ?



	pac_loc         dw 76

	gob_loc         dw 4

	player_loc      dw ?

	player_color    db ?

	player_turn     dw ?

	player_choice   db 2

	opponent_choice db 2

	pac_moves       dw 0

	gob_moves       dw 0

	pac_pill        dw 0



	; player input keys

	right           equ 4dh

	left            equ 4bh

	up              equ 48h

	down            equ 50h

	esckey          equ 1bh



	player_key      db ?



	;messages

	msg0            db 'WORLD',39,'S SMALLEST PACMAN GAME $'

	msg1            db '(p)   Play as Pacman $'

	msg2            db '(g)   Play as Goblin $'

	msg3            db '(s)   Standalone game $'

	msg4            db '(n)   Network game $'

	msg5            db '(Esc) Exit $'



	msgchoice       db 'You are playing as: $'

	msgpac          db 'Pacman $'

	msggob          db 'Goblin $'

	msgmove1        db 'Your turn to move.                 $'

	msgmove2        db 'Your opponent',39,'s turn to move. $'

	

	msgwin          db 'Congratulations! You have won the game. $'

	msglose         db 'Oh no! You have lost the game. $'

	msgkey          db 'Press any key to return to the main menu... $'



	msgblank        db ' $'



	

	msgwait         db 'Waiting for connection... $'

	msgwait2        db 'Opponent has chosen: $'

	msgwait3        db 'Your choice: $'

	msglink         db 'Connection established. $'

	msgdisconnect   db 'Your opponent has disconnected! $'



	network         db 0



	THR             equ 03F8h

	MCR             equ 03FCh

	MSR             equ 03FEh

	LSR             equ 03FDh

	RBR             equ 03F8h





DSEG ENDS



SSEG SEGMENT STACK

	; reserve space for stack

	db 256 DUP(?)

SSEG ENDS



CSEG SEGMENT

	; code segment

	ASSUME CS:CSEG, DS:DSEG, SS:SSEG

	

	; start is the entry procedure

	; you can name it using anything



PRINT_S macro   msg,X,Y         ;macro for printing strings

	push    ax

	push    dx

	mov     ah,2

	mov     dh,Y

	mov     dl,X

	int     10h

	mov     dx,offset msg

	mov     ah,09h

	int     21h

	pop     ax

	pop     dx

endm



PRINT_C macro   char,X,Y        ;macro for printing characters

	push    ax

	push    dx

	mov     ah,2

	mov     dh,Y

	mov     dl,X

	int     10h

	mov     dl,char

	mov     ah,02h

	int     21h

	pop     ax

	pop     dx

endm



drw_bx  macro   X1,Y1,colour

	local   nxt_rw,nxt_cl



	push    cx

	push    bx

	push    dx

	push    ax



	mov     dx,X1

nxt_rw: mov     cx,Y1



nxt_cl: mov     ah,0ch

	mov     al,colour

	mov     bh,0

	int     10h



	inc     cx

	mov     bx,Y1

	add     bx,bsize

	cmp     cx,bx

	jne     nxt_cl



	inc     dx

	mov     bx,X1

	add     bx,bsize

	cmp     dx,bx

	jne     nxt_rw



	pop     cx

	pop     bx

	pop     dx

	pop     ax

endm



drw_xy  macro   loc,colour

	local   col_loop,row_loop,drw_box

	push    cx

	push    bx

	push    dx

	push    ax



	mov     dx,0



	mov     ax,loc

	mov     bx,9

	idiv    bx

	mov     bx,ax



	mov     ax,start_y

	mov     cur_y,ax

	mov     ax,start_x

	mov     cur_x,ax

	

col_loop:

	cmp     dx,0

	je      row_loop

	add     cur_x,bsize+1

	dec     dx

	jnz     col_loop



row_loop:

	cmp     bx,0

	je      drw_box

	add     cur_y,bsize+1

	dec     bx

	jnz     row_loop



drw_box:

	drw_bx  cur_y,cur_x,colour



	pop     cx

	pop     bx

	pop     dx

	pop     ax

endm





drw_m   macro

	local   drw_nextcol,drw_nextrow,drw_exit

	push    cx

	push    bx

	push    dx

	push    ax



	sub     di,di

	sub     si,si



	mov     ax,start_y

	mov     cur_y,ax

	mov     ax,start_x

	mov     cur_x,ax

	mov     bx,0



drw_nextcol:

	drw_bx  cur_y,cur_x,colors[si]

	inc     si

	cmp     si,81

	je      drw_exit

	inc     di

	cmp     di,9

	je      drw_nextrow

	add     cur_x,bsize+1

	jmp     drw_nextcol



drw_nextrow:

	mov     ax,start_x

	mov     cur_x,ax

	add     cur_y,bsize+1

	mov     di,0

	jmp     drw_nextcol



drw_exit:



	pop     cx

	pop     bx

	pop     dx

	pop     ax

endm



init_connection macro player_input

	local   init_DSR,wait_DSR,init_CTS,wait_CTS,wait_abort,check_DSR,check_CTS



	push    ax

	push    dx



	call    cs:clear_screen

	PRINT_S msgwait,24,14



	mov     ah,0h

	mov     al,0E3h

	int     14h



init_DSR:

	mov     dx,MCR

	mov     al,01h

	out     dx,al



wait_DSR:

	mov     ah,01h

	int     16h

	jz      check_DSR



	mov     ah,00h

	int     16h

	cmp     al,esckey

	je      wait_abort



check_DSR:

	mov     dx,MSR

	in      al,dx

	test    al,20h

	jz      wait_DSR



init_CTS:

	PRINT_S msglink,24,15

	mov     dx,MCR

	mov     al,03h

	out     dx,al



wait_CTS:

	mov     ah,01h

	int     16h

	jz      check_CTS



	mov     ah,00h

	int     16h

	cmp     al,esckey

	je      wait_abort



check_CTS:

	mov     dx,MSR

	in      al,dx

	test    al,10h

	jz      wait_CTS



	pop     ax

	pop     dx



	jmp     mainmenu



wait_abort:

	mov     dx,MCR

	mov     al,00h

	out     dx,al

	

	pop     ax

	pop     dx



	jmp     startmenu



endm





send_data       macro   data

	local   wait1

	push    dx

	push    ax



	mov     dx,LSR

wait1:

	in      al,dx

	test    al,20h

	jz      wait1



	mov     dx,THR

	mov     al,data

	out     dx,al

	pop     ax

	pop     dx

endm



receive_data    macro

	local   wait2

	push    dx

	push    ax

	mov     dx,LSR



wait2:

	in      al,dx

	test    al,1

	jz      wait2



	mov     dx,RBR

	in      al,dx

	mov     al,player_key

	pop     ax

	pop     dx

endm



initialize_player       macro

	local   init_me,init_pac,init_gob,init_message_turn,init_message_turn1

	cmp     player_turn,0

	je      init_me

	cmp     opponent_choice,0

	je      init_pac

	jmp     init_gob



init_me:

	cmp     player_choice,0

	je      init_pac

	jmp     init_gob



init_pac:

	mov     ax,pac_loc

	mov     player_loc,ax

	mov     player_color,YELLOW

	jmp     init_message_turn



init_gob:

	mov     ax,gob_loc

	mov     player_loc,ax

	mov     player_color,GREEN



init_message_turn:

	cmp     player_turn,0

	je      init_message_turn1

	PRINT_S msgmove2,50,11



init_message_turn1:

	PRINT_S msgmove1,50,11

endm    



START PROC FAR

	; initialization: initialize the DS to point to a proper segment

	MOV AX, DSEG

	MOV DS, AX

	

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

	; MAIN CODE

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



	mov     ah,0fh

	int     10h

	mov     curr_vmod,al



	mov     ah,00h

	mov     al,new_vmod

	int     10h



	mov     dx,MCR

	mov     al,00h

	out     dx,al



startmenu:



	call    cs:clear_screen

	PRINT_S msg0,28,10

	PRINT_S msg3,30,11

	PRINT_S msg4,30,12

	PRINT_S msg5,30,13



	;initialize

	mov     player_choice,2 

	mov     opponent_choice,2

	mov     network,0



check_startkey:

	mov     ah,01h

	int     16h

	jz      check_startkey

	

	mov     ah,00h

	int     16h

	

	cmp     al,esckey

	jne     check_startkey_next1

	jmp     exit



check_startkey_next1:

	cmp     al,73h

	jne     check_startkey_next2

	jmp     game_init_sa



check_startkey_next2:

	cmp     al,6Eh

	je      game_init_network

	jmp     check_startkey



game_init_network:

	mov     network,1

	init_connection



game_init_sa:

	mov     network,0

	jmp     mainmenu

	

mainmenu:



	call    cs:clear_screen

	PRINT_S msg0,28,10

	PRINT_S msg1,30,11

	PRINT_S msg2,30,12

	PRINT_S msg5,30,13



	PRINT_S msgwait2,30,15

	PRINT_S msgwait3,30,16

	

check_menukey:

	

	



check_opponent_choice:

	push	ax

	push	dx

	mov     dx,LSR

	in      al,dx

	test    al,1

	jz      check_opponent_choice_next



	mov     dx,RBR

	in      al,dx

	mov	player_key,al



;debug

	mov     ah,2

	mov     dx,0

	int     10h

	xor	ax,ax

	mov	al,player_key

	call	cs:output

;end debug



	pop	ax

	pop	dx



	mov     ah,opponent_choice

	cmp     ah,2

	jne     jmp_opponent_choice_esc



opponent_choice_gob:

	cmp     player_key,67h

	jne     opponent_choice_pac

	mov     opponent_choice,1

	PRINT_S msggob,51,15

	mov     ah,player_choice

	cmp     ah,2

	jne     jmp_gameinit

	mov     player_turn,1

	jmp     check_menukey_next



jmp_opponent_choice_esc:

	jmp	opponent_choice_esc



check_opponent_choice_next:

	pop	ax

	pop	dx



jmp_check_menukey_next:

	jmp     check_menukey_next



opponent_choice_pac:

	cmp     player_key,70h

	jne     opponent_choice_esc

	;jne     jmp_check_menukey_next

	mov     opponent_choice,0

	PRINT_S msgpac,51,15

	mov     ah,player_choice

	cmp     ah,2

	jne     jmp_gameinit

	mov     player_turn,1

	jmp     check_menukey_next      



opponent_choice_esc:

        cmp     player_key,esckey

        jne     check_menukey_next

        PRINT_S msgdisconnect,30,18

        PRINT_S msgkey,30,19



press_anykey:

        mov     ah,01h

        int     16h

        jz      press_anykey

        mov     ah,00h

        int     16h

        jmp     startmenu



jmp_gameinit:

	jmp     game_init



check_menukey_next:

	mov     ah,01h

	int     16h

	jz      jmp_no_menukey



	mov     ah,00h

	int     16h



	cmp     al,esckey

	jne     check_player_choice

	mov	al,esckey

	mov	player_key,al

	send_data player_key

	jmp     exit



check_player_choice:

	mov     ah,player_choice

	cmp     ah,2

	je      check_menukey_pac



jmp_no_menukey:

	jmp     no_menukey



check_menukey_pac:

	cmp     al,70h

	jne     check_menukey_gob

	mov     ah,opponent_choice

	cmp     ah,0

	je      jmp_nomenukey

	mov     player_key,70h

	send_data player_key     

	mov     player_choice,0

	mov     ah,opponent_choice

	cmp     ah,1

	je      game_init

	mov     player_turn,0

	PRINT_S msgpac,43,16

	jmp     check_menukey



jmp_nomenukey:

	jmp     no_menukey



check_menukey_gob:

	cmp     al,67h

	jne     jmp_no_menukey

	mov     ah,opponent_choice

	cmp     ah,1

	je      jmp_no_menukey

	mov     player_key,67h

	send_data player_key

	mov     player_choice,1

	cmp     ah,0

	je      game_init

	mov     player_turn,0

	PRINT_S msggob,43,16

	jmp     check_menukey



no_menukey:     

	jmp     check_menukey



game_init:

	mov     pac_loc,76

	mov     gob_loc,4

	mov     pac_moves,0

	mov     gob_moves,0

	mov     pac_pill,0



	mov     bx,0



colors_init:

	mov     al,colors_db[bx]

	mov     colors[bx],al

	inc     bx

	cmp     bx,81

	jl      colors_init



	call    cs:clear_screen



	drw_m

	drw_xy  gob_loc,GREEN

	drw_xy  pac_loc,YELLOW



	cmp     player_choice,1

	je      message_init_gob

	jmp     message_init_pac



message_init_pac:

	PRINT_S msgchoice,50,10

	PRINT_S msgpac,70,10

	jmp     mainprog



message_init_gob:

	PRINT_S msgchoice,50,10

	PRINT_S msggob,70,10    

	jmp     mainprog



mainprog:

	;clear line

	;PRINT_S msgblank,50,11



	;check victory condition

	mov     ax,gob_loc

	cmp     ax,pac_loc

	je      gob_victory



	mov     ax,pac_pill

	cmp     ax,23

	je      pac_victory



	initialize_player



	cmp     player_turn,0

	je      jmp_check_key

	jmp     receive_key



jmp_check_key:

	jmp     check_key



gob_victory:

	cmp     player_choice,1

	je      message_victory

	cmp     player_choice,0

	je      message_defeat

	jmp     message_anykey



pac_victory:

	cmp     player_choice,0

	je      message_victory

	cmp     player_choice,1

	je      message_defeat

	jmp     message_anykey



message_victory:

	PRINT_S msgwin,10,25

	jmp     message_anykey



message_defeat:

	PRINT_S msglose,10,25

	jmp     message_anykey



message_anykey:

	PRINT_S msgkey,10,26

	jmp     press_any_key



press_any_key:

	mov     ah,01h

	int     16h

	jz      press_any_key

	mov     ah,00h

	int     16h

	jmp     mainmenu



receive_key:

	receive_data

	mov     ah,player_key

	cmp     ah,esckey

	jne     check_key_next0

	jmp     mainmenu



check_key_next0:

	cmp     ah,right

	je      jmp_moveright

	cmp     ah,left

	je      jmp_moveleft

	cmp     ah,up

	je      jmp_moveup

	cmp     ah,down

	je      jmp_movedown

	jmp     receive_key



jmp_moveright:

	jmp     move_right



jmp_moveleft:

	jmp     move_left



jmp_moveup:

	jmp     move_up



jmp_movedown:

	jmp     move_down



check_key:

	mov     ah,01h

	int     16h

	jz      no_key



	mov     ah,00h

	int     16h

	; int   21h

	; cmp   al,0ffh



	cmp     al,esckey

	jne     check_key_next1

	mov     player_key,al

	send_data player_key

	jmp     mainmenu



check_key_next1:

	cmp     ah,right

	je      moveright

	cmp     ah,left

	je      moveleft

	cmp     ah,up

	je      moveup

	cmp     ah,down

	je      movedown

	cmp     network,0

	je      check_key_next2

	jmp     no_key



check_key_next2:

	cmp     al,67h

	je      gobinit

	cmp     al,70h

	je      pacinit

	jmp     no_key

	

moveright:

	mov     player_key,ah

	jmp     move_right

moveleft:

	mov     player_key,ah

	jmp     move_left

moveup:

	mov     player_key,ah

	jmp     move_up

movedown:

	mov     player_key,ah

	jmp     move_down

gobinit:

	jmp     gob_init

pacinit:

	jmp     pac_init



gob_init:

	jmp     no_key

pac_init:

	jmp     no_key



no_key: jmp     check_key



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

	; END OF MAIN CODE

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

	; terminate the program normally



exit:   mov     ah,00h

	mov     al,curr_vmod

	int     10h



	mov     ah,4ch

	int     21h



move_right:

	mov     bx,player_loc

	cmp     colors[bx+1],RED

	jne     move_right_bound

	jmp     check_key

move_right_bound:

	mov     ax,player_loc

	inc     ax

	mov     bx,9

	mov     dx,0

	idiv    bx

	cmp     dx,0

	jne     move_right_draw

	jmp     check_key

move_right_draw:

	mov     dx,0

	mov     di,player_loc

	drw_xy  player_loc,colors[di]

	inc     player_loc

	drw_xy  player_loc,player_color

	jmp     post_move

	



move_left:

	mov     bx,player_loc

	cmp     colors[bx-1],RED

	jne     move_left_bound

	jmp     check_key

move_left_bound:

	mov     ax,player_loc

	inc     ax

	mov     bx,9

	mov     dx,0

	idiv    bx

	cmp     dx,1

	jne     move_left_draw

	jmp     check_key

move_left_draw: 

	mov     dx,0

	mov     di,player_loc

	drw_xy  player_loc,colors[di]

	dec     player_loc

	drw_xy  player_loc,player_color

	jmp     post_move



move_up:

	mov     bx,player_loc

	cmp     colors[bx-9],RED

	jne     move_up_bound

	jmp     check_key

move_up_bound:

	mov     ax,player_loc

	cmp     ax,8

	jg      move_up_draw

	jmp     check_key

move_up_draw:   

	mov     di,player_loc

	drw_xy  player_loc,colors[di]

	sub     player_loc,9

	drw_xy  player_loc,player_color

	jmp     post_move



move_down:

	mov     bx,player_loc

	cmp     colors[bx+9],RED

	jne     move_down_bound

	jmp     check_key

move_down_bound:

	mov     ax,player_loc

	cmp     ax,72

	jl      move_down_draw

	jmp     check_key

move_down_draw:         

	mov     di,player_loc

	drw_xy  player_loc,colors[di]

	add     player_loc,9

	drw_xy  player_loc,player_color

	jmp     post_move



post_move:

	cmp     network,1

	jne     post_move_next

	send_data player_key



post_move_next:

	cmp     player_choice,0

	je      post_pac_move



post_gob_move:

	inc     gob_moves

	mov     ax,player_loc

	mov     gob_loc,ax

	;mov     ah,2

	;mov     dx,0

	;int     10h

	;mov     ax,gob_moves

	;call    cs:output

	not     player_turn

	jmp     mainprog



post_pac_move:

	inc     pac_moves

	mov     bx,player_loc

	mov     pac_loc,bx

	;eat pills

	cmp     colors[bx],CYAN

	jne     post_pac_nopill

	mov     colors[bx],BLUE

	inc     pac_pill



post_pac_nopill:

	;mov     ah,2

	;mov     dx,0

	;int     10h

	;mov     ax,pac_pill

	;call    cs:output

	not     player_turn

	jmp     mainprog





	; end of `start' procedure

START ENDP



CLEAR_SCREEN PROC FAR

	PUSH    AX      ; save registers

	PUSH    DS       

	PUSH    BX       

	PUSH    CX      

	PUSH    DI      



	MOV     AX, 40h

	MOV     DS, AX  ; for getting screen parameters. 

	MOV     AH, 06h ; scroll up function id. 

	MOV     AL, 0   ; scroll all lines! 

	MOV     BH, 0   ; attribute for new lines. 

	MOV     CH, 0   ; upper row. 

	MOV     CL, 0   ; upper col. 

	MOV     DI, 84h ; rows on screen -1, 

	MOV     DH, [DI] ; lower row (byte). 

	MOV     DI, 4Ah ; columns on screen, 

	MOV     DL, [DI]

	DEC     DL      ; lower col. 

	INT     10h



	; set cursor position to top 

	; of the screen: 

	MOV     BH, 0   ; current page. 

	MOV     DL, 0   ; col. 

	MOV     DH, 0   ; row. 

	MOV     AH, 02

	INT     10h



	POP     DI      ; re-store registers... 

	POP     CX      ; 

	POP     BX      ; 

	POP     DS      ; 

	POP     AX      ; 



	RET

CLEAR_SCREEN ENDP



OUTPUT PROC FAR

	; This is the helper procedure for output



	; save all registers

	PUSH CX

	PUSH BX

	PUSH DX

	PUSH AX







	; output the prefix `0x'.

	; you can customize the prefix.

	MOV AH, 09H

	LEA DX, prefix

	INT 21H

	

	; 1. output higher 8-bit, which is in AH

	POP AX

	PUSH AX

	; 1.1 output first 4-bit

	MOV AL, AH              ; prepare to output higher 8-bit: copy it to AL

	MOV CH, AL              ; save a copy in CH

	MOV CL, 4               ; about to shift, 4 is the slots to shift

	SHR AL, CL              ; shift 4 slots to the right, first 4 bits remain

	MOV AH, 0               ; clear the AH, not useful any more

	MOV BX, AX              ; move AX to BX. BX is then used to check the table

	MOV DL, hex_tab[BX]     ; check the table now

	MOV AH, 02H             ; select the output function

	INT 21H         ; invoke



	; 1.2 output next 4-bit: lower 4-bit of AH

	MOV AL, CH              ; restore the saved copy from CH to AL

	AND AL, 00001111B ; mask the higher 4-bit out. We don’t need it

	MOV AH, 0               ; clear AH. 

	MOV BX, AX              ; mo AX to BX. BX is then used to check the table

	MOV DL, hex_tab[BX]     ; check the table now

	MOV AH, 02H             ; select the output function

	INT 21H         ; invoke



	; 2. output the lower 8-bit, which is in AL

	; we use the same method as that for outputting AH. 

; Comments are omitted

	POP AX

	PUSH AX



	MOV CH, AL ; save a copy

	MOV CL, 4  ; firstly highest 4-bit

	SHR AL, CL

	MOV AH, 0

	MOV BX, AX

	MOV DL, hex_tab[BX]

	MOV AH, 02H

	INT 21H



	MOV AL, CH              ; restore the saved copy

	AND AL, 00001111B       ; mask

	MOV AH, 0

	MOV BX, AX

	MOV DL, hex_tab[BX]

	MOV AH, 02H

	INT 21H

	

	; restore registers

	POP AX

	POP DX

	POP BX

	POP CX



	RET

OUTPUT ENDP

	

	; end of code segment

CSEG ENDS

; assign `start' as the entry point

END START



