Skip to content

Instantly share code, notes, and snippets.

@Xom
Last active December 20, 2015 21:09
Show Gist options
  • Save Xom/6195195 to your computer and use it in GitHub Desktop.
Save Xom/6195195 to your computer and use it in GitHub Desktop.
pongvoll.asm Get binary from http://xomnom.com/pongvoll.zip
PONG VOLLEYBALL
Xomnom
https://gist.github.com/xomnom
target platform: DOSBox
screen guide:
grey - walls, ceiling, net
yellow - floors
white - balls
green - paddles
blue, red - projectiles
magenta - score
cyan - meter
objective:
Score five (or more) points to win the match.
Point(s) are awarded when opponent faults.
Letting a ball hit floor is a fault.
Letting paddle hit a stationary projectile is a fault.
New round starts after each fault.
universal controls:
escape - force-quit
P1 controls:
W - up
A - left
D - right
S - special
P2 controls:
up arrow - up
left arrow - left
right arrow - right
down arrow - special
special meter:
Meter charges when balls hit opponent's ceiling; overflow depletes.
Pressing "down" stops paddle if in motion. If already stationary,
if at least 30% meter and you haven't six already, subtracts that much
meter and deploys a projectile; otherwise activates doubling cube if
permitted. Single fault may be canceled with 60% meter.
projectiles:
Travels to opponent's side when deployed (blue); specials cannot be
activated by either player until projectile reaches
destination and becomes stationary (red).
Deliberately nameless; come up with your own nickname.
doubling cube:
Please refer to Wikipedia for explanation:
http://en.wikipedia.org/wiki/Backgammon#Doubling_cube
Crawford rule is in effect; "beaver", "raccoon", other rules aren't.
Extra balls appear at double and octuple stakes.
Stakes, projectiles, extra balls, etc. reset at new round.
collision physics:
Net, stationary paddle bounces balls à la Pong / Breakout.
Stationary projectile sends ball down.
Moving paddle bounces balls erratically (but always up).
hints:
Frame rate is a little under 20 FPS.
Only positive-edge keystrokes are processed; auto-repeat is
deliberately at default, non-useful setting to discourage
holding keys down.
Input is ignored during long beeps.
Spawn positions in new rounds depend upon prior positions.
Projectiles won't deploy if below top of net.
Doubling becomes a good idea with roughly three-to-one favorable odds.
Paddles can't chase balls downward, so be careful when venturing high.
I'm really paranoid about having missed something here.
PONG VOLLEYBALL
--
NOTES FROM 2013:
Xomnom
https://gist.github.com/xomnom
Pong Volleyball and this report on it were originally written for a class
project. Some personal information has been removed from these files.
'pongvoll.asm' is targeted at the Dewar Assembler, which AFAICT is only
used to teach CS at NYU, but is very close to MASM (Microsoft Assembler).
A binary is available at: http://xomnom.com/pongvoll.zip
You may also need DOSBox, depending on your system.
(see [C1] [PLATFORM])
Edit: I forgot to mention I was in my first year of college when I made this.
END NOTES FROM 2013.
--
[AA] [CONTENTS]
[AB] [FILES]
[AC] [MODULES]
[AD] [EXTERNALCODE]
[B1] [OVERVIEW]
[B2] [OBJECTIVE]
[C1] [PLATFORM]
[C2] [INSTRUCTIONS]
[C3] [PROBLEMS]
[D1] [TECHNICAL]
[EE] [POSTMORTEM]
[AB] [FILES]
devnotes.txt
> This report.
pongvoll.asm
> [AC] This file contains all of the game's source code.
readme.txt
> "This game is bloody complicated.
> You need to read the full instructions."
[AD] [EXTERNALCODE]
Several procedures were borrowed from Dewar's
"Demonstration color/graphics program"
> "Routine to play note on speaker"
> "Write Message Routine"
Code was also borrowed from 'printstr.asm'.
[B1] [OVERVIEW]
This game is a finished product and implements exactly what I had in mind, no
more and no less. That has been my intention from the moment I started
coding; therefore I coded with significant disregard for ease of refactoring.
[B2] [OBJECTIVE]
Prevent balls from colliding with your floor.
Prevent your paddle from colliding with projectiles.
Matches are to five points.
There is a Doubling Cube.
[C1] [PLATFORM]
DOSBox is defined as the target platform.
All modern computers should be capable;
likewise with the IBM-PCs DOSBox is designed to emulate.
[C2] [INSTRUCTIONS]
See README file; it and either the binary or the source are the only files
with which a hypothetical end-user would hypothetically be presented.
Escape to force-quit.
[C3] [PROBLEMS]
For gameplay hints, see README file.
It is possible for the erasure of a ball's or player's previous position
to cover another ball or for the former to disfigure the net; both glitches
are both incredibly rare and extremely inconsequential.
If both players attempt to activate specials during the same frame,
the outcome is determined by the vagaries of the keystroke buffer.
[D1] [TECHNICAL]
Since my code is poorly commented, I will give an overview of the structure
and point out anything interesting as I go. Note that after a certain point
I tried to avoid fanciness, even at the cost of more lines of code, due to
error-prone-ness in my fatigued state (I even had a fever a day or two ago;
if I'd written this yesterday it would've been whining, but somehow now it
feels like boasting).
From the top:
> code from 'printstr.asm'
Some perfunctory text to satisfy bureaucratic requirements, followed by a
stern warning to ignore said snippets, and RTFM. It is possible to quit out
at this message with Escape; if you weren't aware, you probably should've.
> variables
Arrays are uniformly DW for the sake of pointer loops.
> draw environment
What isn't in procedures, mostly never needs redrawing. There is a CEILP
procedure that redraws the half of the ceiling used for message text.
Oh, and mode is 320x200 4-bit color.
> process keystrokes
Pretty standard fare; I'm sure everyone has one of these loops.
Contained therein is all of the logic for activating specials:
> special activation
Giant collection of branching ifs. One of the many blocks of code that has to
be duplicated and tweaked for each side; I don't think there's a better way to
do it, though (what I would give for the (foo ? bar : baz) construction!).
> ball movement
90% collision code; pointer loop pretty much required, so I'm glad I wrote
this part early on. Basically, there's paddles and balls, and then there's
projectiles and the environment; the former collide with each of the other
three. Paddle-ball collision is handled by inside the pointer loop for ball
movement. Ball collision in general is handled in reverse order of priority
with respect to effect on direction of travel. Side effects are also handled
inside the loop.
> projectile movement
Only one projectile is allowed to be traveling across the screen at a time.
Some messy pointer loops are used to append to the list of non-traveling
projectile properties. I coded those after I mangled my sleep schedule, but
if my memory is reliable (I doubt it), it went surprisingly smoothly (unlike
everything else).
> paddle movement
Again, duplicate code per side. "; redraw is coded the lazy way". As long as
paddle isn't moving, no redraw, with added benefit of no "lazy way" flicker.
> win conditions
There is an egregiously named variable, LOCAL, which tracks how many loss
conditions are encountered during the progression of each frame. At the end
of the tally, only the difference between the sides matters; if only by one,
60% meter is burnt if available (surrender (doubling cube) is worth sixteen,
enough to guarantee round loss.
> quit game
Following the main loop, of course. Most of the logic is in procedure RESET.
> paint procedures
A separate one for each shape. Note the weird loops that draw two rows per
iteration. Behavior with regard to preserving registers is inconsistent
across shapes. Meter update, which can be caused by all sorts of things,
is responsible for calling the short beeps you hear during the game.
> doubling cube
Changes a bunch of stuff; really quite messy.
> reset (new round)
Changes even more stuff; even messier. On match conclusion, pops procedure
call off stack and jumps directly to QUIT.
> 'pg4.asm' procedures
I bet everyone has these, too.
[EE] [POSTMORTEM]
Continued from: [B1] [OVERVIEW]
Assembly makes other languages look a lot less scary.
I came up with the doubling cube before the projectiles.
A doubling cube is an ideal litmus test for non-trivial positional advantages.
I've never played Backgammon, but doubling cubes are just that awesome.
[EF] [ENDOFFILE]
; PONG VOLLEYBALL
;
; Xomnom
; https://gist.github.com/xomnom
;
; target platform: DOSBox
;
; source: printstr.asm
mov di, 0
help: mov al, rules[di] ;get next char
cmp al, 0
je okay
mov ah, 0eh
xor bh, bh
int 10h
inc di ;point to next char
jmp help
; await keystroke
okay: xor ah, ah
int 16h
cmp ah, 1
je ohno
jmp init ; skip variables
ohno: int 20h ; HALT!
; variables
rules db 0Dh, 0Ah, 'PONG VOLLEYBALL', 0Dh, 0Ah
db 'by Xomnom ', 0Dh, 0Ah, 0Dh, 0Ah
db 'http://git.io/VaJDfA', 0Dh, 0Ah
db 'Spring 2009 ', 0Dh, 0Ah, 0Dh, 0Ah
db 'target platform: DOSBox', 0Dh, 0Ah, 0Dh, 0Ah
db 'P1 controls:', 0Dh, 0Ah
db ' W: up A: left D: right', 0Dh, 0Ah
db ' S: special abilities', 0Dh, 0Ah, 0Dh, 0Ah
db 'P2 controls:', 0Dh, 0Ah
db ' layout as P1, but with arrow keys', 0Dh, 0Ah, 0Dh, 0Ah
db ' escape: quit game', 0Dh, 0Ah, 0Dh, 0Ah
db 'WARNING: This game is bloody complicated.', 0Dh, 0Ah
db ' You need to read the full instructions.'
db 0Dh, 0Ah, 0Dh, 0Ah, 'Press any key to start...', 0
weast db ' *anykey exits* P2 has won the match!', 0
wwest db ' P1 has won the match! *anykey exits*', 0
dwest db ' Doubling Cube! MOV=Double SPC=Drop', 0
deast db ' MOV=Double SPC=Drop Doubling Cube!', 0
lwest db ' P2 has won this round!', 0
least db ' P1 has won this round!', 0
crawf db 0
dcpms db 0 ; side: 1 = P1; 2 = P2
dcube db 1 ; 0 = max
westp db 0 ; P1 score
eastp db 0 ; P2 score
local db 128 ; auxiliary
westg db 64 ; P1 meter
eastg db 64 ; P2 meter
mined dw 0 ; deployed mine destination
minex dw 16 ; deployed mine current X
miney dw 152 ; deployed mine current Y
westn dw 0 ; count P2 mines on P1 side times two
eastn dw 0 ; count P1 mines on P2 side times two
wemix dw 6 dup(16) ; western mine X
wemiy dw 6 dup(16) ; western mine Y
eamix dw 6 dup(288) ; eastern mine X
eamiy dw 6 dup(16) ; eastern mine Y
balln dw 4 ; equal to balls onscreen times two
ballh dw 1,0,1,0,1,0 ; 0 = left
ballv dw 6 dup(0) ; 0 = up
ballx dw 16,300,16,300,16,300 ; ball Xs
bally dw 6 dup(156) ; ball Ys
westd db 0 ; 0 = stop; 1 = up
eastd db 0 ; 2 = left; 3 = right
westx dw 120 ; P1 X
westy dw 144 ; P1 Y
eastx dw 168 ; P2 X
easty dw 144 ; P2 Y
; init graphics mode
init: mov ax, 0Dh ; 320x200 4-bit color
int 10h ; video interrupt
; draw ceiling
xor bh, bh ; video page
mov ax, 0C07h ; grey pixels
xor dx, dx ; DX = row; CX = column
ceio: mov cx, 319 ; breadth; start outer
int 10h ; video interrupt
ceii: dec cx ; start inner
int 10h ; video interrupt
jnz ceii ; end inner
inc dx ;
cmp dx, 16 ; depth
jb ceio ; end outer
; draw walls
mov dx, 16
walo: mov cx, 15 ; start outer
int 10h ; pixel
wall: dec cx ; start left
int 10h ; pixel
jnz wall ; end left
mov cx, 304 ;
walr: int 10h ; pixel; start right
inc cx ;
cmp cx, 320 ;
jb walr ; end right
inc dx ;
cmp dx, 176 ;
jb walo ; end outer
; draw net
mov dx, 120
neto: mov cx, 152 ; start outer
neti: int 10h ; pixel; start inner
inc cx ;
cmp cx, 168 ;
jb neti ; end inner
inc dx ;
cmp dx, 176 ;
jb neto ; end outer
; draw floors
dec al ; yellow
mov dx, 160 ;
floo: mov cx, 16 ; start outer
flol: int 10h ; pixel; start left
inc cx ;
cmp cx, 152 ;
jb flol ; end left
mov cx, 168 ;
flor: int 10h ; pixel; start right
inc cx ;
cmp cx, 304 ;
jb flor ; end right
inc dx ;
cmp dx, 176 ;
jb floo ; end outer
; draw indicators
dec al ; magenta
mov cx, 12
mov dx, 180
call sboxp
mov cx, 284
call sboxp
call weggp
call eaggp
; START MAIN LOOP
; PROCESS KEYSTROKES
keyl: mov ah, 1 ; check keystrokes
int 16h ; keyboard interrupt
jnz keym ; if no more
jmp balm ; then break
keym: xor ah, ah ; get from buffer
int 16h ; keyboard interrupt
cmp ah, 11h ; 'W'
jne keya ; next
mov westd, 1 ; P1 up
jmp keyl ; loop
keya: cmp ah, 48h ; up
jne keyb ; next
mov eastd, 1 ; P2 up
jmp keyl ; loop
keyb: cmp ah, 1Eh ; 'A'
jne keyc ; next
mov westd, 2 ; P1 left
jmp keyl ; loop
keyc: cmp ah, 20h ; 'D'
jne keyd ; next
mov westd, 3 ; P1 right
jmp keyl ; loop
keyd: cmp ah, 4Bh ; left
jne keye ; next
mov eastd, 2 ; P2 left
jmp keyl ; loop
keye: cmp ah, 4Dh ; right
jne keyf ; next
mov eastd, 3 ; P2 right
jmp keyl ; loop
keyf: cmp ah, 1Fh ; 'S'
je klol
jmp keyg ; next
klol: cmp westd, 0 ; none
je klom
jmp keyi ; skip
; P1 special
klom: cmp mined, 0 ; one at a time
je klon
jmp keyi ; no dice
klon: cmp westg, 75 ; got meter?
jb kwwx
jmp kweg ; forthward!
kwwx: cmp dcpms, 2
jne kwww
jmp keyi
kwww: cmp crawf, 1 ; Crawford rule
jne kloo
jmp keyi
kloo: cmp dcube, 0
jne klop
jmp keyi
klop: mov dcpms, 2 ; kekeke
xor dx, dx
mov bl, 87h
mov si, offset dwest
call gr_msg
mov ax, 294 ; D
mov dx, 100
call note
kdcv: mov ah, 1 ; debuffer
int 16h
jz kdcw
cmp ah, 1 ; escape
jne komf
jmp quit ; abort
komf: xor ah, ah
int 16h
jmp kdcv ; done
kdcw: xor ah, ah ; keystroke
int 16h
cmp ah, 1 ; escape
jne komg
jmp quit ; abort
komg: cmp ah, 50h ; down
jne kdcx
add local, 16 ; drop
call ceilp ; erase
jmp keyi
kdcx: cmp ah, 48h ; up
je kdcy
cmp ah, 4Bh ; left
je kdcy
cmp ah, 4Dh ; right
je kdcy
jmp kdcw ; next
kdcy: call ceilp ; erase
call dbltk
jmp keyi
kweg: cmp westy, 113 ; minimum height
jnb keyi ; no dice
cmp eastn, 12 ; six at a time
jnb keyi ; no dice
sub westg, 75 ; 30% tension
call weggp ; update gauge
mov dx, westy ; copy Y
mov miney, dx ; paste Y
mov dx, westx ; copy X
add dx, 24 ; adjustment
mov minex, dx ; paste X
neg dx ; mirror
add dx, 312 ; across
mov mined, dx ; center
keyi: mov westd, 0 ; stop
jmp keyl ; loop
keyg: cmp ah, 50h ; down
je kbbq
jmp keyh ; next
kbbq: cmp eastd, 0 ; none
je kbbr
jmp keyj ; skip
; P2 special
kbbr: cmp mined, 0 ; one at a time
je kbbs
jmp keyj ; no dice
kbbs: cmp eastg, 75 ; got meter?
jb keef
jmp keag ; forthward!
keef: cmp dcpms, 1
jne kbbt
jmp keyj
kbbt: cmp dcube, 0
jne keee
jmp keyj
keee: cmp crawf, 1 ; Crawford rule
jne kbbu
jmp keyj
kbbu: mov dcpms, 1 ; kekeke
xor dx, dx
mov bl, 87h
mov si, offset deast
call gr_msg
mov ax, 294 ; D
mov dx, 100
call note
kdcd: mov ah, 1 ; debuffer
int 16h
jz kdce
cmp ah, 1 ; escape
jne kwte
jmp quit ; abort
kwte: xor ah, ah
int 16h
jmp kdcd ; done
kdce: xor ah, ah ; keystroke
int 16h
cmp ah, 1 ; escape
jne kwtf
jmp quit ; abort
kwtf: cmp ah, 1Fh ; 'S'
jne kdcf
sub local, 16 ; drop
call ceilp ; erase
jmp keyj
kdcf: cmp ah, 11h ; 'W'
je kdcg
cmp ah, 1Eh ; 'A'
je kdcg
cmp ah, 20h ; 'D'
je kdcg
jmp kdce ; next
kdcg: call ceilp ; erase
call dbltk
jmp keyj
keag: cmp easty, 113 ; minimum height
jnb keyj ; no dice
cmp westn, 12 ; six at a time
jnb keyj ; no dice
sub eastg, 75 ; 30% tension
call eaggp ; update gauge
mov dx, easty ; copy Y
mov miney, dx ; paste Y
mov dx, eastx ; copy X
mov minex, dx ; paste X
neg dx ; mirror
add dx, 312 ; across
mov mined, dx ; center
keyj: mov eastd, 0 ; stop
jmp keyl ; loop
keyh: cmp ah, 1 ; escape
je keyk ; short
jmp keyl ; long
keyk: jmp quit ; abort game
; BALL MOVEMENT
balm: xor bx, bx
; cleanup
balo: mov cx, ballx[bx] ; start outer
mov dx, bally[bx] ; graphical glitch
mov ax, 0C00h ; when newer position
call ballp ; of previous ball
; object collision
cmp cx, 160 ; mines
jnb bbls
jmp bals
bbls: cmp eastn, 0
jne bblt
jmp bclt
bblt: push bx
xor bx, bx
balu: cmp cx, eamix[bx] ; start loop
jb balv
cmp dx, eamiy[bx]
jb balv
mov ax, eamix[bx]
add ax, 8
cmp cx, ax
jnb balv
mov ax, eamiy[bx]
add ax, 8
cmp ax, dx
jb balv
push cx
push dx
mov cx, eamix[bx]
mov dx, eamiy[bx]
mov ax, 0C00h
call minep
dec al
push bx
sub eastn, 2
mov bx, eastn
mov cx, eamix[bx]
mov dx, eamiy[bx]
pop bx
mov eamix[bx], cx
mov eamiy[bx], dx
pop dx
pop cx
cmp bx, eastn
jb balu
balv: add bx, 2
cmp bx, eastn
jb balu ; end loop
pop bx
jmp balt
bals: cmp westn, 0
je bclt
push bx
xor bx, bx
bblu: cmp cx, wemix[bx] ; start loop
jb bblv
cmp dx, wemiy[bx]
jb bblv
mov ax, wemiy[bx]
add ax, 8
cmp dx, ax
jnb bblv
mov ax, wemix[bx]
add ax, 8
cmp cx, ax
jnb bblv
push cx
push dx
mov cx, wemix[bx]
mov dx, wemiy[bx]
mov ax, 0C00h
call minep
dec al
push bx
sub westn, 2
mov bx, westn
mov cx, wemix[bx]
mov dx, wemiy[bx]
pop bx
mov wemix[bx], cx
mov wemiy[bx], dx
pop dx
pop cx
cmp bx, westn
jb bblu
bblv: add bx, 2
cmp bx, westn
jb bblu ; end loop
pop bx
balt: cmp al, -1
jne bclt
mov ballv[bx], 1
bclt: mov ax, eastx ; P2
sub ax, 8
cmp ax, cx
jnb balc
add ax, 40
cmp ax, cx
jnb bomg
jmp balb
bomg: mov ax, easty
sub ax, 4
cmp ax, dx
jnb balc
add ax, 24
cmp ax, dx
jb balc
sub ax, 12
cmp ax, dx
jb bala
jmp bale
balc: cmp cx, 152 ; net
jb bald
cmp dx, 120
jb balb
cmp cx, 168
jnb balb
sub dx, 3
mov ax, 0C07h ; grey
call ballp ; redraw
cmp dx, 132
jnb bala
jmp bale
bald: mov ax, westx ; P1
sub ax, 8
cmp ax, cx
jnb balb
add ax, 40
cmp ax, cx
jb balb
mov ax, westy
sub ax, 4
cmp ax, dx
jnb balb
add ax, 24
cmp ax, dx
jb balb
sub ax, 12
cmp ax, dx
jnb bale
bala: neg ballh[bx]
inc ballh[bx]
bale: mov ballv[bx], 0
; bound collision
balb: cmp dx, 22 ; ceiling
jnb bali ;
mov ballv[bx], 1 ; down
cmp cx, 158
jb gaug
add westg, 11
call weggp
jmp balj
gaug: add eastg, 11
call eaggp
jmp balj
bali: cmp dx, 158 ; floor
jb balj ;
mov ballv[bx], 0 ; up
cmp cx, 158 ; loser
jb ball
add local, 2
ball: dec local
balj: cmp cx, 18 ; walll
jnb balk ;
mov ballh[bx], 1 ; right
jmp balz ;
balk: cmp cx, 298 ; wallr
jb balz ;
mov ballh[bx], 0 ; left
; movement
balz: cmp ballh[bx], 0 ; horizontal
jne balx ;
sub cx, 8 ;
balx: add cx, 4 ;
cmp ballv[bx], 0 ; vertical
jne baly ;
sub dx, 8 ;
baly: inc dx ;
mov ballx[bx], cx ; store X
mov bally[bx], dx ; store Y
; repaint
mov ax, 0C0Fh ; white pixels
call ballp ; paint ball
add bx, 2 ;
cmp bx, balln ;
jnb minm ; break
jmp balo ; end outer
; MINE MOVEMENT
minm: cmp mined, 0
jne minn
jmp wesm
minn: mov cx, minex
mov dx, miney
xor al, al
call minep
cmp cx, mined
jb mina
sub cx, 16
mina: add cx, 8
cmp mined, 160
jnb minb
cmp cx, mined
jb miny
jmp minz
miny: mov bx, westn
add westn, 2
mov mined, 0
mov al, 4
mov wemix[bx], cx
mov wemiy[bx], dx
mini: call minep ; start loop
sub bx, 2
jc minc ; break out
mov cx, wemix[bx]
mov dx, wemiy[bx]
jmp mini ; end loop
minc: mov bx, eastn
minj: sub bx, 2 ; start loop
jc wesm ; break out
mov cx, eamix[bx]
mov dx, eamiy[bx]
call minep
jmp minj ; end loop
minb: cmp cx, mined
jb minz
mov bx, eastn
add eastn, 2
mov mined, 0
mov al, 4
mov eamix[bx], cx
mov eamiy[bx], dx
mink: call minep ; start loop
sub bx, 2
jc mind ; break out
mov cx, eamix[bx]
mov dx, eamiy[bx]
jmp mink ; end loop
mind: mov bx, westn
minl: sub bx, 2 ; start loop
jc wesm ; break out
mov cx, wemix[bx]
mov dx, wemiy[bx]
call minep
jmp minl ; end loop
minz: inc al
call minep
mov minex, cx
; WEST MOVEMENT
wesm: mov cx, westx ; redraw is coded the lazy way
mov dx, westy ; will fix after features done
cmp westd, 0 ;
jne wesz ; if no move
jmp easm ; skip ahead
wesz: cmp westd, 2 ; left
jb wesu ;
je wesl ;
add cx, 16 ;
wesl: sub cx, 8 ;
add dx, 4 ;
cmp cx, 16 ; wall
jnb wesr ;
mov cx, 16 ;
mov westd, 0 ;
jmp wesd ;
wesr: cmp cx, 121 ; net
jb wesd ;
mov cx, 120 ;
mov westd, 0 ;
wesd: cmp dx, 145 ; floor
jb wesc
mov dx, 144
mov westd, 0
jmp wesc
wesu: sub dx, 8
cmp dx, 16
jnb wesc
mov dx, 16
mov westd, 0
; collision
wesc: cmp westn, 0
je wesp
push bx
xor bx, bx
welu: mov ax, wemiy[bx] ; start loop
add ax, 6
cmp ax, dx
jb welv
sub ax, 20
cmp ax, dx
jnb welv
mov ax, wemix[bx]
add ax, 6
cmp ax, cx
jb welv
sub ax, 36
cmp ax, cx
jnb welv
push cx
push dx
mov cx, wemix[bx]
mov dx, wemiy[bx]
mov ax, 0C00h
call minep
dec al
push bx
sub westn, 2
mov bx, westn
mov cx, wemix[bx]
mov dx, wemiy[bx]
pop bx
mov wemix[bx], cx
mov wemiy[bx], dx
pop dx
pop cx
cmp bx, westn
jb welu
welv: add bx, 2
cmp bx, westn
jb welu ; end loop
pop bx
cmp al, -1
jne wesp
dec local
; repaint
wesp: push cx
push dx
mov cx, westx
mov dx, westy ; graphical glitch
mov ax, 0C00h ; when newer position
call playp ; of previous ball
pop dx ;
pop cx ;
easm: mov ax, 0C02h ; green
call playp
sub dx, 16
mov westx, cx
mov westy, dx
; EAST MOVEMENT
mov cx, eastx ; redraw is coded the lazy way
mov dx, easty ; will fix after features done
cmp eastd, 0 ;
jne easz ; if no move
jmp zzzz ; skip ahead
easz: cmp eastd, 2 ; left
jb easu ;
je easl ;
add cx, 16 ;
easl: sub cx, 8 ;
add dx, 4 ;
cmp cx, 168 ; net
jnb easr ;
mov cx, 168 ;
mov eastd, 0 ;
jmp easd ;
easr: cmp cx, 273 ; wall
jb easd ;
mov cx, 272 ;
mov eastd, 0 ;
easd: cmp dx, 145 ; floor
jb easc
mov dx, 144
mov eastd, 0
jmp easc
easu: sub dx, 8
cmp dx, 16
jnb easc
mov dx, 16
mov eastd, 0
; collision
easc: cmp eastn, 0
je easp
push bx
xor bx, bx
ealu: mov ax, eamiy[bx] ; start loop
add ax, 6
cmp ax, dx
jb ealv
sub ax, 20
cmp ax, dx
jnb ealv
mov ax, eamix[bx]
add ax, 6
cmp ax, cx
jb ealv
sub ax, 36
cmp ax, cx
jnb ealv
push cx
push dx
mov cx, eamix[bx]
mov dx, eamiy[bx]
mov ax, 0C00h
call minep
dec al
push bx
sub eastn, 2
mov bx, eastn
mov cx, eamix[bx]
mov dx, eamiy[bx]
pop bx
mov eamix[bx], cx
mov eamiy[bx], dx
pop dx
pop cx
cmp bx, eastn
jb ealu
ealv: add bx, 2
cmp bx, eastn
jb ealu ; end loop
pop bx
cmp al, -1
jne easp
inc local
; repaint
easp: push cx
push dx
mov cx, eastx
mov dx, easty ; graphical glitch
mov ax, 0C00h ; when newer position
call playp ; of previous ball
pop dx ;
pop cx ;
zzzz: mov ax, 0C02h ; green
call playp
sub dx, 16
mov eastx, cx
mov easty, dx
; VICTORY
cmp local, 128
jne vhax
jmp zdbg
vhax: jb zwex
cmp local, 129
jne zeaz
cmp eastg, 150
jb zeaz
sub eastg, 150
call eaggp
dec local
jmp zdbg
zeaz: mov local, 128
xor dx, dx
mov bl, 87h
mov si, offset least
call gr_msg
mov ax, 262 ; C
mov dl, 200
call note
call ceilp
mov al, dcube
add westp, al
cmp al, 0
jne zeay
mov westp, 5
zeay: call reset
jmp zdbg
zwex: cmp local, 127
jne zwez
cmp westg, 150
jb zwez
sub westg, 150
call weggp
inc local
jmp zdbg
zwez: mov local, 128
xor dx, dx
mov bl, 87h
mov si, offset lwest
call gr_msg
mov ax, 262 ; C
mov dl, 200
call note
call ceilp
mov al, dcube
add eastp, al
cmp al, 0
jne zwey
mov eastp, 5
zwey: call reset
; FRAME DELAY
zdbg: xor ax, ax ; silence
mov dx, 5 ; 20 FPS (slower actually)
call note ; ZzZzZz
; END MAIN LOOP
jmp keyl ; main
; revert text mode
quit: mov ax, 2 ; text mode
int 10h ; video interrupt
int 20h ; HALT!
; 4x4 square (ball)
ballp proc ; AH = 0C0?h; ? = color
push bx ; DX = row; CX = column
xor bx, bx ; DX to increment +3
bapa: int 10h ; start inner
inc cx ;
inc bl ;
cmp bl, 4 ;
jb bapa ; end inner
inc dx ; next row
bapb: dec cx ; start inner
int 10h ;
inc bl ;
cmp bl, 8 ;
jb bapb ; end inner
inc dx ; next row
bapc: int 10h ; start inner
inc cx ;
inc bl ;
cmp bl, 12 ;
jb bapc ; end inner
inc dx ; next row
bapd: dec cx ; start inner
int 10h ;
inc bl ;
cmp bl, 16 ;
jb bapd ; end inner
pop bx
ret
ballp endp
; 8x8 square (mine)
minep proc ; AH = 0C0?h; ? = color
push bx ; DX = row
xor bx, bx ; CX = column
mipo: push bx ; clear bl; start outer
xor bl, bl ;
mipi: int 10h ; start inner
inc cx ;
inc bl ;
cmp bl, 8 ;
jb mipi ; end inner
inc dx ; next row
mipj: dec cx ; start inner
int 10h ;
inc bl ;
cmp bl, 16 ;
jb mipj ; end inner
pop bx ; fetch bl
inc dx ;
inc bl ;
cmp bl, 4 ;
jb mipo ; end outer
sub dx, 8
pop bx
ret
minep endp
; 32x16 rectangle (player)
playp proc ; AH = 0C0?h; ? = color
push bx ; DX = row; CX = column
xor bx, bx ; DX to increment +16
plpo: push bx ; clear bl; start outer
xor bl, bl ;
plpi: int 10h ; start inner
inc cx ;
inc bl ;
cmp bl, 32 ;
jb plpi ; end inner
inc dx ; next row
plpj: dec cx ; start inner
int 10h ;
inc bl ;
cmp bl, 64 ;
jb plpj ; end inner
pop bx ; fetch bl
inc dx ;
inc bl ;
cmp bl, 8 ;
jb plpo ; end outer
pop bx
ret
playp endp
; P1 meter
weggp proc
PUSH AX ; save registers
PUSH BX
PUSH CX
PUSH DX
mov ax, 330 ; E
mov dx, 3
call note
xor ah, ah
mov al, westg
mov bx, ax
mov cx, 3
shl bx, cl
add bx, ax
inc cl ; BH = 0
shr bx, cl ; BL = westg * 9/16
shl cl, 1 ; CX = 8
mov ax, 0C03h
wegg: sub bl, 1 ; start loop
jnc wegc
xor al, al
wegc: mov dx, 192
int 10h
inc dx
int 10h
inc dx
int 10h
inc dx
int 10h
inc cx
cmp cx, 153
jb wegg ; end loop
POP DX ; restore registers
POP CX
POP BX
POP AX
ret
weggp endp
; P2 meter
eaggp proc
PUSH AX ; save registers
PUSH BX
PUSH CX
PUSH DX
xor ah, ah
mov al, eastg
mov bx, ax
mov cx, 3
shl bx, cl
add bx, ax
inc cl ; BH = 0
shr bx, cl ; BL = eastg * 9/16
mov cx, 311 ; CX = 311
mov ax, 0C03h
eagg: sub bl, 1 ; start loop
jnc eagc
xor al, al
eagc: mov dx, 192
int 10h
inc dx
int 10h
inc dx
int 10h
inc dx
int 10h
dec cx
cmp cx, 168
jnb eagg ; end loop
mov ax, 330 ; E
mov dx, 3
call note
POP DX ; restore registers
POP CX
POP BX
POP AX
ret
eaggp endp
; 24x8 outline (stakes)
sboxp proc ; AH = 0C0?h; ? = color
push bx ; DX = row
xor bx, bx ; CX = column
sbpa: int 10h ; start loop
inc cx
inc bl
cmp bl, 23
jb sbpa ; end loop
sbpb: int 10h ; start loop
inc dx
inc bl
cmp bl, 30
jb sbpb ; end loop
sbpc: int 10h ; start loop
dec cx
inc bl
cmp bl, 53
jb sbpc ; end loop
sbpd: int 10h ; start loop
dec dx
inc bl
cmp bl, 60
jb sbpd ; end loop
pop bx
ret
sboxp endp
; 22x6 rectangle (score)
sfilp proc ; AH = 0C0?h; ? = color
push bx ; DX = row; CX = column
xor bx, bx ; DX to increment +6
sfpo: push bx ; clear bl; start outer
xor bl, bl ;
sfpi: int 10h ; start inner
inc cx ;
inc bl ;
cmp bl, 22 ;
jb sfpi ; end inner
inc dx ; next row
sfpj: dec cx ; start inner
int 10h ;
inc bl ;
cmp bl, 44 ;
jb sfpj ; end inner
pop bx ; fetch bl
inc dx ;
inc bl ;
cmp bl, 3 ;
jb sfpo ; end outer
pop bx
ret
sfilp endp
; erase message
ceilp proc
PUSH AX ; save registers
PUSH BX
PUSH CX
PUSH DX
xor bh, bh ; video page
mov ax, 0C07h ; grey pixels
xor dx, dx ; DX = row; CX = column
ceip: mov cx, 319 ; breadth; start outer
int 10h ; video interrupt
ceij: dec cx ; start inner
int 10h ; video interrupt
jnz ceij ; end inner
inc dx ;
cmp dx, 8 ; depth
jb ceip ; end outer
POP DX ; restore registers
POP CX
POP BX
POP AX
ret
ceilp endp
; doubling cube
dbltk proc
PUSH AX ; save registers
PUSH BX
PUSH CX
PUSH DX
cmp dcube, 1
jne dcta
add balln, 4
dcta: cmp dcube, 4
jne dctb
add balln, 4
dctb: shl dcube, 1
mov ax, 0C00h
xor bh, bh
mov bl, westp
add bl, dcube
push bx ; BH = 0
cmp bl, 5
jb dctc
mov dx, 164
mov cx, 72
call sboxp
dctc: mov al, 5
mov dx, 180
cmp bl, 4
jb dctd
mov cx, 132
call sboxp
dctd: cmp bl, 3
jb dcte
mov cx, 92
call sboxp
dcte: mov cx, 52
call sboxp
mov bl, eastp
add bl, dcube
pop dx
cmp bl, 5
jb dctg
cmp dx, 5
jb dctf
mov dcube, 0
dctf: mov dl, 164
mov cx, 224
xor al, al
call sboxp
mov al, 5
dctg: mov dl, 180
cmp bl, 4
jb dcth
mov cx, 164
call sboxp
dcth: cmp bl, 3
jb dcti
mov cx, 204
call sboxp
dcti: mov cx, 244
call sboxp
POP DX ; restore registers
POP CX
POP BX
POP AX
ret
dbltk endp
; new round
reset proc
PUSH AX ; save registers
PUSH BX
PUSH CX
PUSH DX
cmp crawf, 1
jne resa
inc crawf
resa: mov ax, 0C05h
xor bh, bh
mov bl, westp
cmp bl, 1
jnb reta
inc al
mov dx, 164
mov cx, 72
call sboxp
xor al, al
mov dl, 180
mov cl, 52
call sboxp
mov cl, 92
call sboxp
mov cl, 132
call sboxp
jmp resb
reta: mov cx, 13
mov dx, 181
call sfilp
cmp bl, 2
jnb retb
inc al
mov dx, 164
mov cx, 72
call sboxp
dec al
mov dl, 180
mov cl, 52
call sboxp
xor al, al
mov cl, 92
call sboxp
mov cl, 132
call sboxp
jmp resb
retb: mov cl, 53
mov dl, 181
call sfilp
cmp bl, 3
jnb retc
inc al
mov dx, 164
mov cx, 72
call sboxp
dec al
mov dl, 180
mov cl, 52
call sboxp
mov cl, 92
call sboxp
xor al, al
mov cl, 132
call sboxp
jmp resb
retc: mov cl, 93
mov dl, 181
call sfilp
cmp bl, 4
jnb retd
inc al
mov dl, 164
mov cl, 72
call sboxp
dec al
mov dl, 180
mov cl, 52
call sboxp
mov cl, 92
call sboxp
mov cl, 132
call sboxp
jmp resb
retd: mov cl, 133
mov dl, 181
call sfilp
cmp bl, 5
jnb retf
cmp crawf, 0
jne rete
inc crawf
rete: xor al, al
mov dl, 164
mov cl, 72
call sboxp
mov al, 5
mov dl, 180
mov cl, 52
call sboxp
mov cl, 92
call sboxp
mov cl, 132
call sboxp
jmp resb
retf: mov dl, 165
mov cl, 73
call sfilp
xor dl, dl
mov bl, 87h
mov si, offset wwest
call gr_msg
resf: mov ah, 1 ; debuffer
int 16h
jz resg
xor ah, ah
int 16h
jmp resf ; done
resg: xor ah, ah
int 16h
pop ax
pop ax
pop ax
pop ax
pop ax
jmp quit ; breaking out
resb: mov al, 5
mov bl, eastp
cmp bl, 1
jnb retg
inc al
mov dl, 164
mov cl, 224
call sboxp
xor al, al
mov dl, 180
mov cl, 244
call sboxp
mov cl, 204
call sboxp
mov cl, 164
call sboxp
jmp resc
retg: mov cx, 285
mov dl, 181
call sfilp
cmp bl, 2
jnb reth
inc al
mov dl, 164
mov cx, 224
call sboxp
dec al
mov dl, 180
mov cl, 244
call sboxp
xor al, al
mov cl, 204
call sboxp
mov cl, 164
call sboxp
jmp resc
reth: mov cx, 245
mov dl, 181
call sfilp
cmp bl, 3
jnb reti
inc al
mov dl, 164
mov cl, 224
call sboxp
dec al
mov dl, 180
mov cl, 244
call sboxp
mov cl, 204
call sboxp
xor al, al
mov cl, 164
call sboxp
jmp resc
reti: mov cl, 205
mov dl, 181
call sfilp
cmp bl, 4
jnb retj
inc al
mov dl, 164
mov cl, 224
call sboxp
dec al
mov dl, 180
mov cl, 244
call sboxp
mov cl, 204
call sboxp
mov cl, 164
call sboxp
jmp resc
retj: mov cl, 165
mov dl, 181
call sfilp
cmp bl, 5
jnb retl
cmp crawf, 0
jne retk
inc crawf
retk: xor al, al
mov dl, 164
mov cl, 224
call sboxp
mov al, 5
mov dl, 180
mov cl, 244
call sboxp
mov cl, 204
call sboxp
mov cl, 164
call sboxp
jmp resc
retl: mov dl, 165
mov cl, 225
call sfilp
xor dl, dl
mov bl, 87h
mov si, offset weast
call gr_msg
resd: mov ah, 1 ; debuffer
int 16h
jz rese
xor ah, ah
int 16h
jmp resd ; done
rese: xor ah, ah
int 16h
pop ax
pop ax
pop ax
pop ax
pop ax
jmp quit ; breaking out
resc: mov eastn, 0
mov westn, 0
mov balln, 4
mov ballv, 0
mov ballv[2], 0
mov ballv[4], 0
mov ballv[6], 0
mov ballv[8], 0
mov ballv[10], 0
mov westg, 64
mov eastg, 64
mov mined, 0
mov westd, 3
mov eastd, 2
mov ax, 0C00h
mov cx, ballx[4]
mov dx, bally[4]
call ballp
mov cx, ballx[6]
mov dx, bally[6]
call ballp
mov cx, ballx[8]
mov dx, bally[8]
call ballp
mov cx, ballx[10]
mov dx, bally[10]
call ballp
mov cx, minex
mov dx, miney
call minep
mov cx, wemix
mov dx, wemiy
call minep
mov cx, wemix[2]
mov dx, wemiy[2]
call minep
mov cx, wemix[4]
mov dx, wemiy[4]
call minep
mov cx, wemix[6]
mov dx, wemiy[6]
call minep
mov cx, wemix[8]
mov dx, wemiy[8]
call minep
mov cx, wemix[10]
mov dx, wemiy[10]
call minep
mov cx, eamix
mov dx, eamiy
call minep
mov cx, eamix[2]
mov dx, eamiy[2]
call minep
mov cx, eamix[4]
mov dx, eamiy[4]
call minep
mov cx, eamix[6]
mov dx, eamiy[6]
call minep
mov cx, eamix[8]
mov dx, eamiy[8]
call minep
mov cx, eamix[10]
mov dx, eamiy[10]
call minep
mov cx, westx
mov dx, westy
call playp
mov cx, eastx
mov dx, easty
call playp
mov westx, 120
mov westy, 144
mov eastx, 168
mov easty, 144
mov dcpms, 0
mov dcube, 1
resy: mov ah, 1 ; debuffer
int 16h
jz resz
xor ah, ah
int 16h
jmp resy ; done
resz: POP DX ; restore registers
POP CX
POP BX
POP AX
ret
reset endp
; source:
; Demonstration color/graphics program
; Copyright (C) 1984, Robert B. K. Dewar
; Routine to play note on speaker
; Definitions for timer gate control
CTRL EQU 61H ; timer gate control port
TIMR EQU 00000001B ; bit to turn timer on
SPKR EQU 00000010B ; bit to turn speaker on
; Definitions of input/output ports to access timer chip
TCTL EQU 043H ; port for timer control
TCTR EQU 042H ; port for timer count values
; Definitions of timer control values (to send to control port)
TSQW EQU 10110110B ; timer 2, 2 bytes, sq wave, binary
LATCH EQU 10000000B ; latch timer 2
; Define 32 bit value used to set timer frequency
FRHI EQU 0012H ; timer frequency high (1193180 / 256)
FRLO EQU 34DCH ; timer low (1193180 mod 256)
; AX = Frequency in Hz (32 - 32000)
; DX = Duration in units of 1/100 second
; Note: a frequency of zero, means rest (silence) for the indicated
; time, allowing this routine to be used simply as a timing delay.
NOTE PROC
PUSH AX ; save registers
PUSH BX
PUSH CX
PUSH DX
PUSH SI
MOV BX, AX ; save frequency in BX
MOV CX, DX ; save duration in CX
; We handle the rest (silence) case by using an arbitrary frequency to
; program the clock so that the normal approach for getting the right
; delay functions, but we will leave the speaker off in this case.
MOV SI, BX ; copy frequency to BX
OR BX, BX ; test zero frequency (rest)
JNZ NOT1 ; jump if not
MOV BX, 256 ; else reset to arbitrary non-zero
; Initialize timer and set desired frequency
NOT1: MOV AL, TSQW ; set timer 2 in square wave mode
OUT TCTL, AL
MOV DX, FRHI ; set DX:AX = 1193180 decimal
MOV AX, FRLO ; = clock frequency
DIV BX ; divide by desired frequency
OUT TCTR, AL ; output low order of divisor
MOV AL, AH ; output high order of divisor
OUT TCTR, AL
; Turn the timer on, and also the speaker (unless frequency 0 = rest)
IN AL, CTRL ; read current contents of control port
OR AL, TIMR ; turn timer on
OR SI, SI ; test zero frequency
JZ NOT2 ; skip if so (leave speaker off)
OR AL, SPKR ; else turn speaker on as well
; Compute number of clock cycles required at this frequency
NOT2: OUT CTRL, AL ; rewrite control port
XCHG AX, BX ; frequency to AX
MUL CX ; frequency times secs/100 to DX:AX
MOV CX, 100 ; divide by 100 to get number of beats
DIV CX
SHL AX, 1 ; times 2 because two clocks/beat
XCHG AX, CX ; count of clock cycles to CX
; Loop through clock cycles
NOT3: CALL RCTR ; read initial count
; Loop to wait for clock count to get reset. The count goes from the
; value we set down to 0, and then is reset back to the set value
NOT4: MOV DX, AX ; save previous count in DX
CALL RCTR ; read count again
CMP AX, DX ; compare new count : old count
JB NOT4 ; loop if new count is lower
LOOP NOT3 ; else reset, count down cycles
; Wait is complete, so turn off clock and return
IN AL, CTRL ; read current contents of port
AND AL, 0FFH-TIMR-SPKR ; reset timer/speaker control bits
; note that the above statement is an equation
OUT CTRL, AL ; rewrite control port
POP SI ; restore registers
POP DX
POP CX
POP BX
POP AX
RET ; return to caller
NOTE ENDP
; Routine to read count, returns current timer 2 count in AX
RCTR PROC
MOV AL, LATCH ; latch the counter
OUT TCTL, AL ; latch counter
IN AL, TCTR ; read lsb of count
MOV AH, AL
IN AL, TCTR ; read msb of count
XCHG AH, AL ; count is in AX
RET ; return to caller
RCTR ENDP
; Write Message Routine
; (dx) ; Cursor location (screen is 25 x 40)
; (bl) ; Color to use for message characters
; (si) ; Points to message (terminated by 00h)
gr_msg proc
push ax ; save registers
push bx
push si ; note: si,di,bp destroyed
push di ; by write TTY function
push bp
cld ; ensure auto-increment
xor bh, bh ; page zero for writes
mov ah, 2 ; set cursor as requested
int 10h
; Loop through characters of message
grmsg1: lodsb ; load next character
or al, al ; test terminating zero?
jz grmsg2 ; yes-done
push si ; no-save pointer
mov ah, 14 ; write char in TTY mode
int 10h
pop si ; restore character
jmp grmsg1 ; loop for next character
; Here with all characters written
grmsg2: pop bp ; restore registers
pop di
pop si
pop bx
pop ax
ret ;return to caller
gr_msg endp
; END OF FILE
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment