;---------------------------------------------------------------------
; Tetris Deluxe v1.10 -- Tetris game, 4971 bytes
; Requires 286, EGA graphics (386 recommended)
;---------------------------------------------------------------------
; To construct TETRIS.DAT (TETRIS.FNT must exist):
; GEN_PCS
; GEN_FONT
; COPY /B TETRIS.FN1+TETRIS.PCS TETRIS.DAT
; DEL TETRIS.FN1
; DEL TETRIS.PCS
;---------------------------------------------------------------------
; Design of font:
; 00 Blank
; 01 Fat chars - 1-wide chars ( I )
; 02-1D Title chars
; 1E-2C Countdown chars
; 2D-2F Fat chars - 1-wide chars ( +-. )
; 30-39 Normal chars - numbers
; 41-5A Normal chars - uppercase
; 5B-5F Copyright message
; 60 50% gray
; 61-7A Normal chars - lowercase
; 80-9F 3-D game pieces
; A0-E5 Fat chars - 2-wide chars
; E6-F4 Countdown chars
; F8-FF Inset box L-R-U-D-UL-UR-LL-LR
;---------------------------------------------------------------------
O equ <offset>
B equ <byte ptr>
W equ <word ptr>
D equ <dword ptr>
.Model Tiny
.186
;#####################################################################
; INITIALIZED DATA SEGMENT
;#####################################################################
.Data
;---------------------------------------------------------------------
BLOCK_FREQ = 3000 ;Random block freq. | Divide these
PIECE_FREQ = 8200 ;Bonus piece freq. | frequencies
DISP_FREQ = 11000 ;Disappearing freq. | by round+8
LINE_FREQ = 16400 ;Advancing line freq.|
;---------------------------------------------------------------------
PIECE_SIZE = 6 ;Max size of piece
MAX_PIECES = 37 ;Number of pieces
PIECE_DATA = 12 * PIECE_SIZE * MAX_PIECES
BOMB_PIECE = 35 ;Bonus pieces
ACID_PIECE = 36
;---------------------------------------------------------------------
DFile$ db 'TETRIS.DAT',0
FError$ db 'Error reading TETRIS.DAT$'
Title$1 db 2, 3, 4, 5, 6, 7, 2, 3, 4, 8, 9,10,11,12,13,14,15,0
Title$2 db 16,17,18,19,20,21,16,17,18,22,23,24,25,26,27,28,29,0
Deluxe$ db 'D+E+L+U+X+E',0
Copyright$ db 91,92,93,94,95,'Tenie Remmel',0
Round$ db 'ROUND ',0 ;The space is for NextRound
LinesLeft$ db 'LINES LEFT',0
Score$ db 'SCORE',0
LinesX$ db ' ' ;Also for NextRound
Lines$ db 'LINES',0
Menu1$ db '1 -- 7 PCS.',0 ;The initial menu
Menu2$ db '2 -- 14 PCS.',0
Menu3$ db '3 -- 21 PCS.',0
Menu4$ db '4 -- 28 PCS.',0
Menu5$ db '5 -- 35 PCS.',0
Menu6$ db 'S -- SOUND '
SoundOn db 'Y',0
Menu7$ db 'G -- GRID '
GridOn db 'N',0
Menu8$ db 'R -- ROUND '
StRound db '1',0
Menu9$ db 'Q -- QUIT',0
Bonus$ db 'BONUS',0
LowBonus$1 db ' BONUS FOR',0
LowBonus$2 db 'LOW PUZZLE',0
OVER_LEN equ 7 ;Length of game-over strings
GameOver$1 db 6Ch,4 dup(65h),69h,0 ;The game-over box
GameOver$2 db 6Ah,'GAME',6Ah,0
GameOver$3 db 6Ah,'OVER',6Ah,0
GameOver$4 db 66h,4 dup(65h),63h,0
HighScores$ db 'H I G H S C O R E S',0
Name$ db 'NAME',0
;---------------------------------------------------------------------
BigChars db 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46
db 32, 45, 47, 32, 212,214,216,218,220,222,224,226
db 228,230,32, 32, 32, 32, 32, 32, 32, 162,164,166
db 168,170,172,174,176,1, 178,180,182,184,186,188
db 190,192,194,196,198,200,202,204,206,208,210,32
db 32, 32, 32, 32, 128,130,132,134,136,138,140,142
db 144,146,148,150,152,154,156,158
Palette db 0,1,2,3,4,5,38,7,56,9,18,27,36,45,54,63,0
Colors db 06Eh,07Fh,05Dh,019h,02Ah,0C6h,04Ch
db 06Fh,07Eh,05Eh,01Ah,02Bh,0CFh,04Dh
db 01Bh,01Ch,01Dh,04Ah,04Bh,0CEh,02Eh
db 08Ch,08Ah,08Dh,09Eh,02Fh,05Fh,09Fh
db 087h,016h,05Bh,06Bh,07Bh,086h,08Bh
db 03Ch,03Ah
LineVals dw 0,50,150,400 ;Line scores
dw 1000,2000,4000
PalVals db 00h,00h,20h,04h ;Normal pulsation data
db 24h,24h,04h,20h
;-------------------------------------------------
; Round Types: Bit 0-1 -- Initial setup
; Bit 2 -- Appearing blocks, bonus pieces
; Bit 3 -- Advancing lines
; Bit 4 -- Disappearing blocks
;-------------------------------------------------
RoundType db 00000b,00000b,00001b
db 00010b,00011b,00100b
db 00101b,10110b,01100b
db 11101b,11110b,11100b
db 12 dup(11100b)
RoundTimes db 99,87,77,68,60,53 ;Delay times
db 47,42,37,33,29,26
db 23,21,19,17,15,14
db 13,12,11,10, 9, 8
RoundLines db 5, 8, 12,15,15,15 ;Num. of lines needed
db 15,15,18,21,24,26 ;to complete each round
db 28,30,32,34,36,38
db 40,42,44,46,48,50
;-------------------------------------------------
BlockLists dw O(BlockList0) ;Pointers to block lists
dw O(BlockList1)
dw O(BlockList2)
dw O(BlockList3)
BlockList1 dw 0000h,0100h,0200h ;Block list for ??01 rounds
dw 0300h,0400h,0500h
dw 0600h,0700h,0009h
dw 0109h,0209h,0309h
dw 0409h,0509h,0609h
dw 0709h,-1
BlockList2 dw 0006h,0001h,0108h ;Block list for ??10 rounds
dw 0103h,0205h,0307h
dw 0306h,0302h,0409h
dw 0404h,0400h,-1
BlockList3 dw 0000h,0001h,0003h ;Block list for ??11 rounds
dw 0004h,0005h,0006h
dw 0008h,0009h,0101h
dw 0102h,0104h,0105h
dw 0107h,0108h,0202h
dw 0203h,0206h,0207h
dw 0303h,0304h,0305h
dw 0306h,0404h,0405h
BlockList0 dw -1 ;No blocks for ??00 rounds
;-------------------------------------------------
M_CHOICES = 9 ;Total num. of choices
M_NUMBERS = 5 ;Number of piece sets
Menu dw O(Menu1$) ;Menu string table
dw O(Menu2$)
dw O(Menu3$)
dw O(Menu4$)
dw O(Menu5$)
dw O(Menu6$)
dw O(Menu7$)
dw O(Menu8$)
dw O(Menu9$)
;#####################################################################
; UNINITIALIZED DATA SEGMENT
;#####################################################################
.Data?
;---------------------------------------------------------------------
ScoreOffs dw ? ;Offset of scores in file
RandNum dw ?,? ;Random number
PalCtr dw ? ;Counter for pulsation
RectData label word ;Color sweep data
RectColor db ?
RectCtr db ?
TitleXY label word ;Title box position
TitleX db ?
TitleY db ?
Score dw ?,? ;Score data
Round dw ?
Lines dw ?
LinesLeft dw ?
DelayTime dw ? ;Delay time in milliseconds
NumPieces dw ? ;Number of pieces in use
PieceW label word ;Label for loading CX
Piece db ? ;Piece data
PRot db ?
NxPiece db ?
BonusCtr dw ? ;Countdown for bonus box
;#####################################################################
; CODE SEGMENT
;#####################################################################
.Code
Org 100h
;---------------------------------------------------------------------
; Main procedure
;---------------------------------------------------------------------
Main Proc
mov es,ds:[2Ch] ;ES = environment
xor di,di ;Set up for scan
mov cx,-1
xor al,al
m_floop1: repne scasb ;Find a zero
scasb ;Double zero?
jne m_floop1 ;if not, loop back
lea si,[di+2] ;DS:SI = program name
push es
pop ds
push cs ;ES = CS
pop es
mov di,O(FileName) ;DI = filename buffer
m_floop2: lodsb ;Copy byte
stosb
test al,al ;Loop while not zero
jnz m_floop2
push cs ;DS = CS
pop ds
dec di ;DI = ptr. to null
std ;Scan in reverse
mov al,'\' ;Find last slash
repne scasb
cld ;Restore dir-flag
scasw ;Move past slash
mov si,O(DFile$) ;Copy 'TETRIS.DAT' onto
mov cx,11 ;the end of the buffer
rep movsb
;-------------------------------------------------
mov di,O(BufEnd) ;Clear the buffer
mov cx,4096
xor ax,ax
rep stosw
mov ax,3D00h ;Open the data file
mov dx,O(FileName)
int 21h
jc m_FError
xchg bx,ax ;BX = handle
mov ah,3Fh ;Read the data file
mov cx,8192
mov dx,O(BufEnd)
int 21h
jc m_FError
xchg si,ax ;SI = read size
mov ah,3Eh ;Close the file
int 21h
call Decode ;Decode the file
;-------------------------------------------------
mov ax,1201h ;Set EGA mode (if VGA)
mov bl,30h
int 10h
mov ax,3 ;Set video mode 3
int 10h
mov ax,1100h ;Load the custom font
mov bx,0E00h ;(Tetris pieces, etc.)
mov cx,0100h
xor dx,dx
mov bp,O(Font)
int 10h
mov ax,1003h ;Turn off blinking
mov bh,1
int 10h
mov ax,1002h ;Set new palette
mov dx,O(Palette)
int 10h
mov ah,2 ;Put cursor off screen
xor bh,bh
mov dx,1900h
int 10h
mov ax,0305h ;Speed up keyboard
xor bx,bx
int 16h
;-------------------------------------------------
push 0B800h ;ES = video memory
pop es
call Tetris ;Call main game procedure
mov ax,1202h ;Set VGA mode (if VGA)
mov bl,30h
int 10h
mov ax,3 ;Reset video mode
int 10h
;-------------------------------------------------
mov ax,3D01h ;Open the data file
mov dx,O(FileName)
int 21h
jnc m_FileOK
;-------------------------------------------------
m_FError: mov ah,9 ;Output error string
mov dx,O(FError$)
jmp m_21ret ;Return
;-------------------------------------------------
m_FileOK: xchg bx,ax ;BX = handle
mov ax,4200h ;Move file pointer
xor cx,cx
mov dx,ScoreOffs
int 21h
mov ah,40h ;Write out scores, etc.
mov cx,O(BufEnd)-O(SC_Score)
mov dx,O(SC_Score)
int 21h
jc m_FError
mov ah,3Eh ;Close the file
m_21ret: int 21h
ret ;Return
Main EndP
;---------------------------------------------------------------------
; Tetris -- Main game procedure
;---------------------------------------------------------------------
Tetris Proc
t_BigLoop: call Initialize ;Initialize the game
call StartInt ;Start interrupt handler
call Choose ;Choose num. pieces or quit
test ax,ax ;Zero = quit
jnz t_Cont0
call StopInt ;Stop interrupt handler
ret ;Return
t_Cont0: imul ax,7 ;Set piece count
mov NumPieces,ax
;-------------------------------------------------
mov t_Ctr,0 ;Ctr = 0
mov ax,NumPieces ;Get random piece
call Rand
mov PieceW,ax ;Set piece word
mov ax,NumPieces ;Get random piece
call Rand
mov NxPiece,al ;Set next piece
mov t_XY,1505h ;Set initial position
mov bl,StRound ;Set initial round
and bx,0Fh
dec bx
mov Round,bx
call NextRound ;Set up round vars
call ShowStatus ;Show initial status
call ShowNext
jmp t_KeyDone ;Go to show-piece code
;-------------------------------------------------
t_GenRand: cwd ;Convert to frequency
add bx,8
div bx
call Rand ;Get random number
test ax,ax ;Test with zero
ret ;Return
;-------------------------------------------------
t_PieceOn: mov cx,PieceW ;CX = piece
mov bx,cx ;Get color
xor bh,bh
mov al,Colors[bx]
t_PutPiece: mov bx,t_XY ;Draw the piece
call PutPiece
ret ;Return
;-------------------------------------------------
t_PieceOff: mov cx,PieceW ;Erase the piece
xor al,al
jmp t_PutPiece
;-------------------------------------------------
t_MainLoop: mov ax,DelayTime ;Delay for current time
call Delay
inc t_Ctr ;Increment modulo 4
and t_Ctr,3
;-------------------------------------------------
mov bx,Round ;Get round type in BP
mov bp,W RoundType[bx-1]
test bp,4 ;Random blocks & pieces allowed?
jz t_Bskip1
mov ax,PIECE_FREQ ;Piece frequency
call t_GenRand ;Get random number
jnz t_Bskip0 ;Not zero, skip
mov ax,2 ;Set next piece to be a
call Rand ; bonus piece (acid, bomb)
add al,35
mov NxPiece,al
call ShowNext ;Re-display next piece
t_Bskip0: mov ax,BLOCK_FREQ ;Block frequency
call t_GenRand ;Get random number
jnz t_Bskip1 ;Not zero, skip
mov bx,t_XY ;BX = piece area
call RandBlock ;Generate random block
;-------------------------------------------------
t_Bskip1: test bp,16 ;Disappearing blocks allowed?
jz t_Bskip2
mov ax,DISP_FREQ ;Disp/column freq.
call t_GenRand ;Get random number
jnz t_Bskip2 ;Not zero, skip
mov bx,t_XY ;BX = piece area
call KillBlock ;Remove a random block
;-------------------------------------------------
t_Bskip2: test bp,8 ;Advancing lines allowed?
jz t_KeyLoop
mov ax,LINE_FREQ ;Line frequency
call t_GenRand ;Get random number
jnz t_KeyLoop ;Not zero, skip
call AdvanceLine ;Advance a line
inc t_Y ;Move piece up
;-------------------------------------------------
t_KeyLoop: mov ah,1 ;Key pressed?
int 16h
jnz t_KeyHit
jmp t_NoKey
t_KeyHit: call t_PieceOff ;Erase the piece
xor ah,ah ;Get the key
int 16h
mov al,ah ;AL = scan code
cmp al,4Bh ;Left?
je t_Left
cmp al,4Dh ;Right?
je t_Right
cmp al,50h ;Down (Drop)?
je t_Down
cmp al,39h ;Space (Rotate)?
je t_Rotate
cmp al,44h ;F10 (Pause)?
je t_Pause
cmp al,01h ;Q, Esc (Quit)?
je t_j0
cmp al,10h
jne t_KDjmp ;Get another key
t_j0: jmp t_Done
;-------------------------------------------------
t_Left: mov bx,t_XY ;Fits at (x-1, y) ?
mov cx,PieceW
dec bx
call Fits
adc t_X,-1 ;Subtract 1 if NC
jmp t_KeyDone
;-------------------------------------------------
t_Right: mov bx,t_XY ;Fits at (x+1, y) ?
mov cx,PieceW
inc bx
call Fits
sbb t_X,-1 ;Add 1 if NC
jmp t_KeyDone
;-------------------------------------------------
t_Pause: call t_PieceOn ;Redraw the piece
call FlushKey ;Flush key buffer
xor ah,ah ;Wait for key
int 16h
t_KDjmp: jmp t_KeyDone
;-------------------------------------------------
t_Rotate: mov bx,t_XY ;Get piece data
mov cx,PieceW
inc ch ;Rotate once
and ch,3
t_RotLoop: call Fits ;Piece fits?
jnc t_RotDone
jz t_KeyDone ;No problem?
jl t_Rot0 ;Off left = move right
dec bx ;Move left
jmp t_RotLoop
t_Rot0: inc bx ;Move right
jmp t_RotLoop
t_RotDone: mov t_XY,bx ;Write out data
mov PieceW,cx
jmp t_KeyDone
;-------------------------------------------------
t_Down: mov bp,DelayTime ;BP = drop delay
shr bp,1
add bp,20
mov bx,t_XY ;Get piece data
mov cx,PieceW
call FlushKey ;Flush key buffer
t_DownLoop: dec bh ;Fits below?
call Fits
jc t_Lock ;If not, lock this piece
inc t_Drop ;Increment drop time
mov t_XY,bx ;Save position
call t_PieceOn ;Draw the piece
mov ax,bp ;Delay
call Delay
inc bp ;Reduce delay 10%
imul bp,29
shr bp,5
mov ah,1 ;Key pressed?
int 16h
jz t_DownCont
cmp ah,50h ;Another Down key?
jne t_KeyJmp
t_DownCont: call t_PieceOff ;Erase the piece
jmp t_DownLoop ;Loop back
;-------------------------------------------------
t_KeyDone: call t_PieceOn ;Redraw the piece
t_KeyJmp: jmp t_KeyLoop
t_NoKey: cmp t_Ctr,0 ;Counter not zero?
jne t_j1 ;If so, don't drop
call t_PieceOff ;Erase the piece
dec bh ;Can it move down?
call Fits
jc t_Lock ;Jump if not
mov t_XY,bx ;Save position
call t_PieceOn ;Redraw the piece
t_j1: jmp t_MainLoop ;Loop back
;-------------------------------------------------
t_Lock: call t_PieceOn ;Redraw the piece
mov bx,t_XY ;Lock this piece
mov al,t_Drop
call LockPiece
mov t_Drop,0 ;Reset drop time
mov bx,1400h ;BX = (0, 20)
mov cx,10 ;10 blocks
t_LockLoop: call GetBlock ;Check block
test ah,ah ;Quit if not blank
jnz t_Done
inc bx ;Next square
loop t_LockLoop ;Loop back
mov ax,NumPieces ;Get random piece
call Rand
xchg al,NxPiece ;Set next piece
mov PieceW,ax ;Set current piece
mov bx,1505h ;Set initial position
mov t_XY,bx
call ShowStatus ;Update screen info
call ShowNext
call t_PieceOn ;Draw the new piece
jmp t_MainLoop ;Loop back
t_Done: mov ax,1000 ;Delay 1 second
call Delay
call GameOver ;Display 'Game Over'
;-------------------------------------------------
call AddScore ;Add to list if high enough
call StopInt ;Stop interrupt handler
jmp t_BigLoop ;Go again
;-------------------------------------------------
t_XY label word ;Local variables
t_X db 0 ;X, Y position
t_Y db 0
t_Ctr db 0 ;Cycle counter
t_Drop db 0 ;Drop distance
Tetris EndP
;---------------------------------------------------------------------
; AddScore -- Add the score to the list if high enough, show the list
;---------------------------------------------------------------------
AddScore Proc
pusha ;Save all registers
mov ax,Score[0] ;DX:AX = score
mov dx,Score[2]
cmp dx,SC_Score[38] ;Compare high word
jb as_nope
ja as_cont1
cmp ax,SC_Score[36] ;Compare low word
ja as_cont1
as_nope: mov ax,-1 ;Show scores
call ShowScores
as_quit: call FlushKey ;Wait for key
xor ah,ah
int 16h
popa ;Restore registers
ret ;Return
;-------------------------------------------------
as_cont1: mov bx,8 ;BX = score number
as_cloop: imul si,bx,4 ;SI = pointer
add si,O(SC_Score)
cmp dx,[si+2] ;Compare high word
jb as_cdone
ja as_copy1
cmp ax,[si] ;Compare low word
jbe as_cdone
as_copy1: mov si,bx ;Copy score down
mov di,bx
inc di
call CopyScore
dec bx ;Next score
jnl as_cloop ;Loop back
;-------------------------------------------------
as_cdone: inc bx ;BX = position
imul si,bx,4 ;SI = pos * 4
mov SC_Score[si],ax ;Set score field
mov SC_Score[si+2],dx
shr si,1 ;SI = pos * 2
mov ax,Lines ;Set lines field
mov SC_Lines[si],ax
mov ax,Round ;Set round field
mov SC_Round[si],ax
shl si,3 ;SI = ptr. to string
add si,O(SC_Name)
xor ax,ax ;Set name field to null string
mov B [si],al
xchg ax,bx ;Show scores
call ShowScores ;BX now = 0
call FlushKey ;Flush keyboard
;-------------------------------------------------
mov cx,0D0Bh ;CX = position of string
add ch,al
as_eloop: pusha ;Save all registers
mov dx,si ;Get string length
call StrLenF
xchg dx,ax ;DX = cursor position
add dx,cx
mov ah,2 ;Move cursor
xor bh,bh
int 10h
mov bx,cx ;Clear score area
add cl,32
mov al,20h
call RectC
mov dx,si ;Output string
call PutStrF
popa ;Restore registers
;-------------------------------------------------
as_key: xor ah,ah ;Get a key
int 16h
test al,al ;Zero, loop back
jz as_key
cmp al,08h ;Backspace?
je as_bksp
cmp al,0Dh ;Enter?
je as_finish
cmp al,20h ;Space, hyphen OK
je as_char
cmp al,2Dh
je as_char
and al,0DFh ;Make letters uppercase
cmp al,'A' ;Out of range?
jb as_key
cmp al,'Z'
ja as_key
;-------------------------------------------------
as_char: cmp bx,15 ;Out of space?
jae as_key
mov [si+bx],al ;Add char to string
inc bx
mov B [si+bx],0
jmp as_eloop ;Loop back
;-------------------------------------------------
as_bksp: test bx,bx ;No chars?
jz as_key
dec bx ;Delete last char
mov B [si+bx],0
jmp as_eloop ;Loop back
;-------------------------------------------------
as_finish: mov ah,2 ;Put cursor off screen
xor bh,bh
mov dx,1900h
int 10h
jmp as_quit ;Return
AddScore EndP
;---------------------------------------------------------------------
; AdvanceLine -- Insert a line at the bottom of the well
;---------------------------------------------------------------------
AdvanceLine Proc
pusha ;Save all registers
mov dx,1500h ;DX = initial pos.
mov cx,10 ;10 columns
al_oloop: mov bx,dx ;BX = position
lea ax,[bx+90] ;AX = 360 + 4 X
xor ah,ah ; sound frequency
shl ax,2
call Sound ;Set sound
al_iloop: dec bh ;AX = (X, Y-1)
call GetBlock
inc bh ;(X, Y+1) = AX
call PutBlock
dec bh ;Next line
jnz al_iloop ;Loop back
mov ax,4 ;3/4 probability
call Rand ;of a block here
test ax,ax
jz al_put
mov ax,35 ;Get random number
call Rand
xchg si,ax ;Convert to color
mov ah,Colors[si]
xor al,al ;Type is 0
al_put: call PutBlock ;Put a block
mov ax,15 ;Delay 15 msec.
call Delay
inc dx ;Next column
loop al_oloop ;Loop back
call NoSound ;Turn off sound
popa ;Restore registers
ret ;Return
AdvanceLine EndP
;---------------------------------------------------------------------
; Choose -- Display a menu and return the key pressed
;---------------------------------------------------------------------
; returns AX = selection, 0 thru 4 (0 = quit)
Choose Proc
pusha ;Save all registers
mov bx,000Ah ;Fill the well with
mov cx,151Dh ;a visible color
mov ax,0F20h
call RectB
ch_draw: mov si,O(Menu) ;SI = menu ptr.
mov bx,030Bh ;BX = position
mov cx,M_CHOICES ;Num. of choices
ch_loop: lodsw ;DX = string ptr.
xchg dx,ax
call PutStrF ;Display string
add bh,2 ;Next line
loop ch_loop ;Loop back
ch_key: xor ah,ah ;Get a key
int 16h
cmp ah,1Fh ;S, sound?
jne ch_skip1
xor SoundOn,17h ;Flip sound flag
jmp ch_draw ;Redraw menu
ch_skip1: cmp ah,22h ;G, grid?
jne ch_skip2
xor GridOn,17h ;Flip grid flag
jmp ch_draw ;Redraw menu
ch_skip2: cmp ah,13h ;R, round?
jne ch_skip3
mov al,StRound ;AL = round
and ax,0F7h ;Increment round
inc ax
mov StRound,al ;Set round
jmp ch_draw ;Redraw menu
ch_skip3: dec ax ;'Q' = 51h --> 50h
cmp ah,10h ;Q, quit?
je ch_cont
sub al,'0'-1 ;Out of range?
jbe ch_key
cmp al,M_NUMBERS
ja ch_key
ch_cont: and ax,0Fh ;Convert to number
call ClearWell ;Clear the well
mov bp,sp ;Set pushed AX
mov [bp+14],ax
popa ;Restore registers
ret ;Return
Choose EndP
;---------------------------------------------------------------------
; ClearBoxes -- Clear the score and count boxes
;---------------------------------------------------------------------
ClearBoxes Proc
pusha ;Save all registers
mov bx,0D25h ;Position of first score box
mov cx,0D39h
mov ax,8F20h
mov dl,3 ;Increment is 3
call cb_clear ;Clear score boxes
mov bx,0403h ;Position of first count box
mov cx,0505h
mov ah,70h
inc dx ;Increment is 4
call cb_clear ;Clear count boxes
popa ;Restore registers
ret ;Return
;-------------------------------------------------
cb_clear: call RectB ;Clear score/count boxes
add bh,dl
add ch,dl
cmp ch,16h
jle cb_clear
ret ;Return
ClearBoxes EndP
;---------------------------------------------------------------------
; ClearWell -- Clear the well
;---------------------------------------------------------------------
ClearWell Proc
pusha ;Save all registers
mov bh,21 ;Initial position
xor ax,ax ;Cells are blank
cw_oloop: mov bl,9 ;Start of row
cw_iloop: call PutBlock ;Clear this block
dec bl ;Next block
jnl cw_iloop ;Loop back
dec bh ;Next line
jnl cw_oloop ;Loop back
popa ;Restore registers
ret ;Return
ClearWell EndP
;---------------------------------------------------------------------
; CopyScore -- Copy a score
;---------------------------------------------------------------------
; SI = source, DI = destination
CopyScore Proc
pusha ;Save all registers
push es
push ds ;ES = DS
pop es
add si,si ;SI = src * 2
add di,di ;DI = dest * 2
mov bx,O(SC_Lines) ;Copy lines value
mov ax,[bx+si]
mov [bx+di],ax
mov bx,O(SC_Round) ;Copy round value
mov ax,[bx+si]
mov [bx+di],ax
add si,si ;SI = src * 4
add di,di ;DI = dest * 4
mov bx,O(SC_Score) ;Copy score value
mov ax,[bx+si]
mov [bx+di],ax
mov ax,[bx+si+2]
mov [bx+di+2],ax
shl si,2 ;SI = src * 16
shl di,2 ;DI = dest * 16
mov bx,O(SC_Name) ;BX = offset of names
add si,bx ;Adjust to offsets
add di,bx
mov cx,16 ;Copy name string
rep movsb
pop es ;Restore registers
popa
ret ;Return
CopyScore EndP
;---------------------------------------------------------------------
; Crescendo -- Make a crescendo sound
;---------------------------------------------------------------------
; AX = start, BX = step, CX = count
Crescendo Proc
pusha ;Save all registers
mov dx,20 ;DX = delay time
cr_loop: call Sound ;Set sound
xchg ax,dx ;Delay 20 msec.
call Delay
xchg ax,dx
add ax,bx ;Change tone
loop cr_loop ;Loop back
call NoSound ;Turn off speaker
popa ;Restore registers
ret ;Return
Crescendo EndP
;---------------------------------------------------------------------
; CvtInt - Convert int to string
;---------------------------------------------------------------------
; AX = number, BX = string
CvtInt Proc
pusha ;Save all registers
mov di,bx ;DI = string pointer
xor cx,cx ;Zero CX
mov si,10 ;SI = 10
ci_dloop: xor dx,dx ;Divide by 10
div si
add dl,30h ;Convert to digit
push dx ;Push digit
inc cx
test ax,ax ;Loop back
jnz ci_dloop
ci_ploop: pop ax ;Pop digit
mov [di],al ;Store digit
inc di
loop ci_ploop ;Loop back
mov B [di],0 ;Add the null byte
popa ;Restore registers
ret ;Return
CvtInt EndP
;---------------------------------------------------------------------
; CvtLong - Convert long to string
;---------------------------------------------------------------------
; DX:AX = number, BX = string
CvtLong Proc
pusha ;Save all registers
mov di,bx ;DI = string pointer
xor cx,cx ;Zero CX
mov si,10 ;SI = 10
cl_loop: xchg bx,ax ;Divide DX:AX by SI
xchg dx,ax ;with remainder in BX
xor dx,dx
div si
xchg bx,ax
div si
xchg dx,bx
add bl,'0' ;Convert to digit
push bx ;Push digit
inc cx
mov bx,dx ;Loop back
or bx,ax
jnz cl_loop
jmp ci_ploop ;Pop & store digits
CvtLong EndP
;---------------------------------------------------------------------
; Decode -- Decode the file (from BufEnd) to the main buffer
;---------------------------------------------------------------------
; ES = DS
Decode Proc
pusha ;Save all registers
mov si,O(BufEnd) ;SI = in, DI = out
mov di,O(Buffer) ; for decoding the font
xor cx,cx ;Zero CX
de_loop1: lodsb ;Get byte
test al,al ;Zero?
jnz de_store
de_zero: lodsb ;Load byte
test al,al ;Zero = quit
jz de_cont
xchg cl,al ;CX = count, AL = 0
rep ;Tricky...
de_store: stosb ;Store byte
jmp de_loop1 ;Loop back
de_cont: mov cx,PIECE_DATA/2 ;CX = piece data size
de_loop2: lodsb ;Get byte
mov ah,al ;Unpack to 2 bytes
sar ax,4
sar al,4
stosw ;Store 2 bytes
loop de_loop2 ;Loop back
mov ax,si ;Set score offset
sub ax,O(BufEnd)
mov ScoreOffs,ax
mov cx,O(BufEnd)-O(SC_Score)
rep movsb ;Copy the high scores
popa ;Restore registers
ret ;Return
Decode EndP
;---------------------------------------------------------------------
; Delay -- Millisecond resolution delay
;---------------------------------------------------------------------
; AX = time in milliseconds
Delay Proc
pusha ;Save all registers
mov dx,1000 ;Multiply by 1000
mul dx ;DX:AX = time in microseconds
xchg ax,dx ;CX:DX = time
xchg ax,cx
mov ah,86h ;BIOS Delay Service
int 15h
popa ;Restore registers
ret ;Return
Delay EndP
;---------------------------------------------------------------------
; DelBlock -- Delete a block from the well, fix neighbors
;---------------------------------------------------------------------
; BL = X, BH = Y of block to delete
DelBlock Proc
pusha ;Save all registers
call GetBlock ;Can't delete steel blocks
cmp ah,8Fh
je db_done
xor ax,ax ;Kill this block
call PutBlock
dec bh ;Fix block below
call GetBlock
and al,0Dh
call PutBlock
add bh,2 ;Fix block above
call GetBlock
and al,07h
call PutBlock
dec bh ;Fix block at left
dec bx
call GetBlock
and al,0Bh
call PutBlock
inc bx ;Fix block at right
inc bx
call GetBlock
and al,0Eh
call PutBlock
db_done: popa ;Restore registers
ret ;Return
DelBlock EndP
;---------------------------------------------------------------------
; DelLine -- Delete a specific line from the well
;---------------------------------------------------------------------
; AX = line to delete
DelLine Proc
pusha ;Save all registers
mov dh,al ;DX = initial pos.
xor dl,dl
mov cx,10 ;10 columns
dl_oloop: mov bx,dx ;BX = position
mov al,bh ;AL = X + 4 Y
shl al,2
add al,bl
add al,70 ;AX = 280 + 4 X + 16 Y
xor ah,ah ; sound frequency
shl ax,2
call Sound ;Set sound
dl_iloop: inc bh ;AX = (X, Y+1)
call GetBlock
dec bh ;(X, Y) = AX
call PutBlock
inc bh ;Next line
cmp bh,22 ;Loop back
jb dl_iloop
mov bx,dx ;Fix type on top
call GetBlock
and al,07h
call PutBlock
dec bh ;Fix type on bottom
call GetBlock
and al,0Dh
call PutBlock
mov ax,15 ;Delay 15 msec.
call Delay
inc dx ;Next column
loop dl_oloop ;Loop back
call NoSound ;Turn off sound
popa ;Restore registers
ret ;Return
DelLine EndP
;---------------------------------------------------------------------
; DelLines -- Delete all completed lines from the well
;---------------------------------------------------------------------
; returns AX = num. of lines deleted
DelLines Proc
pusha ;Save all registers
xor si,si ;Zero SI
mov ax,21 ;AX = top line
dls_loop: call IsFull ;Check for line
jnc dls_skip
call DelLine ;Delete line
inc si ;Increment counter
dls_skip: dec ax ;Next line
jnl dls_loop ;Loop back
mov bp,sp ;Set pushed AX
mov [bp+14],si
popa ;Restore registers
ret ;Return
DelLines EndP
;---------------------------------------------------------------------
; Disp -- Display character and/or attribute
;---------------------------------------------------------------------
; BL = X, BH = Y, AL = char, AH = attr, displayed if nonzero
Disp Proc
pusha ;Save all registers
xor cx,cx ;Split X from Y
xchg cl,bh
imul di,cx,160 ;DI = offset
add di,bx
add di,bx
test al,al ;Set char if NZ
jz $+4
stosb
dec di
inc di ;Advance DI
mov al,ah ;AL = attr
test al,al ;Set attr if NZ
jz $+3
stosb
popa ;Restore registers
ret ;Return
Disp EndP
;---------------------------------------------------------------------
; DrawBox - Draw an inset box using F8-FF charset
;---------------------------------------------------------------------
; BL = X1, BH = Y1, CL = X2, CH = Y2
DrawBox Proc
pusha ;Save all registers
mov si,bx ;Save BX, CX
mov di,cx
inc bh ;Draw left side
dec ch
push cx
mov cl,bl
mov ax,00F8h
call RectC
pop cx ;Draw right side
mov bl,cl
inc ax
call RectC
mov bx,si ;Get old values
mov cx,di
inc bx ;Draw top side
dec cx
push cx
mov ch,bh
inc ax
call RectC
pop cx ;Draw bottom side
mov bh,ch
inc ax
call RectC
mov cx,si ;CX = UL corner
mov dx,di ;DX = UR corner
mov bx,cx ;Draw UL corner
inc ax
call Disp
mov bl,dl ;Draw UR corner
inc ax
call Disp
mov bx,cx ;Draw LL corner
mov bh,dh
inc ax
call Disp
mov bx,dx ;Draw LR corner
inc ax
call Disp
popa ;Restore registers
ret ;Return
DrawBox EndP
;---------------------------------------------------------------------
; DrawTitle -- Draw title box
;---------------------------------------------------------------------
; BX = position of UL corner
DrawTitle Proc
pusha ;Save all registers
mov cx,bx ;Clear title area
add cx,0312h
mov ax,0F20h
call RectB
inc bx ;Display strings
mov dx,O(Title$1)
call PutStr
inc bh
mov dx,O(Title$2)
call PutStr
inc bh
mov dx,O(Deluxe$)
call PutStrF
inc bh
mov dx,O(Copyright$)
call PutStr
popa ;Restore registers
ret ;Return
DrawTitle EndP
;---------------------------------------------------------------------
; FindBlock -- Find a random position away from the piece
;---------------------------------------------------------------------
; DL = X, DH = Y of area to avoid
; returns BL = X, BH = Y, AH = color, AL = type
FindBlock Proc
fb_loop: mov ax,8 ;Generate random block
call Rand
inc ax
mov bl,al
mov ax,20
call Rand
mov bh,al
mov al,dl ;Compare to piece X
sub al,bl
add al,3
cmp al,7
jnb fb_done
mov al,dh ;Compare to piece Y
sub al,bh ;If it's within 3 of
add al,3 ;both X and Y, skip it
cmp al,7
jb fb_loop
fb_done: call GetBlock ;AX = value of block
ret ;Return
FindBlock EndP
;---------------------------------------------------------------------
; Fits -- Check whether a piece fits
;---------------------------------------------------------------------
; BL = X, BH = Y, CL = piece, CH = rotation
; returns NC = fits, CY = doesn't fit:
; L = off left side, G = off right side, Z = hit something
Fits Proc
pusha ;Save all registers
xor dx,dx ;Separate piece data
xchg dl,ch
imul bp,dx,PIECE_SIZE ;SI = piece offset
imul si,cx,12*PIECE_SIZE
lea si,Pieces[si+bp]
mov cx,PIECE_SIZE ;Num. of blocks
fi_loop: push bx ;Save BX
add bl,[si] ;Add in offsets
add bh,[si+4*PIECE_SIZE]
call GetBlock ;Check block
pop bx ;Restore BX
test ah,ah ;Color nonzero?
jnz fi_nope
inc si ;Next block
loop fi_loop ;Loop back
fi_done: popa ;Restore registers
ret ;Return
fi_nope: cmp ah,-2 ;Normal cases -> 0
jne $+4 ;Off left (-1) -> -1
mov ah,1 ;Off right (-2) -> -2
jae $+4
xor ah,ah
test ah,ah ;Set flags
stc ;Set carry flag
jmp fi_done ;Return
Fits EndP
;---------------------------------------------------------------------
; FlushKey -- Flush the BIOS keyboard buffer
;---------------------------------------------------------------------
FlushKey Proc
push ds ;Save registers
push ax
push 40h ;DS = BIOS segment
pop ds
mov ax,ds:[1Ch] ;Set head = tail
mov ds:[1Ah],ax
pop ax ;Restore registers
pop ds
ret ;Return
FlushKey EndP
;---------------------------------------------------------------------
; GameOver -- Draw the 'Game Over' box
;---------------------------------------------------------------------
GameOver Proc
pusha ;Save all registers
mov bx,090Ch ;Clear a rectangle
mov cx,0E1Bh
mov ax,0F20h
call RectB
mov bx,0A0Eh
mov cx,0D19h
mov ah,4Fh
call RectB
mov dx,O(GameOver$1) ;DX = offset of line 1
mov cx,4 ;4 lines
go_loop: call PutStrF ;Display this line
inc bh ;Move to the next line
add dx,OVER_LEN
loop go_loop ;Loop back
call FlushKey ;Wait for a key
xor ah,ah
int 16h
popa ;Restore registers
ret ;Return
GameOver EndP
;---------------------------------------------------------------------
; GetBlock -- Return the status of a block in the well
;---------------------------------------------------------------------
; BL = X, BH = Y, returns AH = color, AL = type
; color: -1 = off left, -2 = off right, -3 = off bottom
GetBlock Proc
push bx ;Save registers
push di
xor ax,ax ;Zero AX
test bl,bl ;Out of range?
jl gb_dec1
cmp bl,9
jg gb_dec2
test bh,bh
jl gb_dec3
cmp bh,21
jg gb_done
xchg al,bh ;Separate X from Y
imul di,ax,-160 ;DI = offset
shl bx,2
lea di,[di+bx+0D34h]
mov ax,es:[di] ;AX = value
sub al,80h ;Fix type
shr al,1
cmp al,10h ;Patch for grid
jne gb_done ;??10h -> 0000h
xor ax,ax
gb_done: pop di ;Restore registers
pop bx
ret ;Return
gb_dec3: dec ax ; -3
gb_dec2: dec ax ; -2
gb_dec1: dec ax ; -1
xchg al,ah
jmp gb_done
GetBlock EndP
;---------------------------------------------------------------------
; Initialize -- Reset all variables, display initial screen
;---------------------------------------------------------------------
Initialize Proc
pusha ;Save all registers
push es
xor ax,ax ;Init score, lines, etc.
mov Score[0],ax
mov Score[2],ax
mov Lines,ax
mov BonusCtr,ax ;Init timer counters
mov PalCtr,ax
mov RectData,ax
call InitScreen ;Show the initial screen
mov es,ax ;ES = segment 0
mov ax,es:[046Ch] ;Get low word
mov RandNum[0],ax ;Save it
mov ax,es:[046Eh] ;Get high word
mov RandNum[2],ax ;Save it
pop es ;Restore registers
popa
ret ;Return
Initialize EndP
;---------------------------------------------------------------------
; InitScreen -- Display the initial screen
;---------------------------------------------------------------------
InitScreen Proc
pusha ;Save all registers
xor bx,bx ;Clear screen to 7/F dither
mov cx,184Fh
mov ax,7F60h
call RectB
mov ch,01h ;Clear top 2 lines
xor al,al
call RectA
call ClearWell ;Draw the well
mov bx,0300h ;Draw vertical 3-D sides
mov cx,1700h
mov al,94h
call RectC
mov cx,151Eh
mov bl,cl
call RectC
inc ax
mov cx,174Fh
mov bl,cl
call RectC
mov cx,1509h
mov bl,cl
call RectC
mov bx,1801h ;Draw horizontal 3-D sides
mov cx,184Eh
mov al,0FAh
call RectC
mov cx,0208h
mov bh,ch
inc ax
call RectC
mov bl,1Fh
mov cl,4Eh
call RectC
mov bx,0209h ;Draw 3-D corners
mov ax,93h
call Disp
mov bl,4Fh
call Disp
mov bl,1Eh
mov al,98h
call Disp
mov bl,00h
call Disp
mov bh,18h
mov al,8Ch
call Disp
mov bl,4Fh
mov al,87h
call Disp
mov bx,0338h ;Draw title box
mov cx,084Ch
call DrawBox
mov bx,0439h
call DrawTitle
mov TitleXY,bx ;Set title area
;-------------------------------------------------
mov bx,160Ah ;Draw bonus box
mov cx,161Dh
mov al,8Ah
call RectC
mov bh,18h
mov ch,bh
call RectC
mov bh,17h
mov ch,bh
mov ax,0E20h
call RectB
mov bx,1609h
mov ax,9Fh
call in_bdisp
mov bx,1709h
mov al,95h
call in_bdisp
mov bx,1809h
mov al,8Fh
call in_bdisp
;-------------------------------------------------
mov bx,0302h ;Position of first count box
mov cx,0606h
mov dl,4 ;Increment is 4
call in_cloop ;Draw outline of count boxes
call ClearBoxes ;Clear count boxes
;-------------------------------------------------
mov bx,0321h ;Draw next-piece box
mov cx,0930h
call DrawBox
mov bx,0422h
mov cx,082Fh
mov ax,0080h
call RectB
mov bx,0B24h ;Position of first score box
mov cx,0E3Ah
mov dl,3 ;Increment is 3
call in_cloop ;Draw outline of score boxes
mov bx,0C25h ;Position of first score box
mov cx,0D39h
mov ax,8A20h
in_sloop2: call RectB ;Fill inside of score boxes
call in_next
jle in_sloop2
mov bx,0E25h ;Position of first box divider
mov cx,0E39h
in_sloop3: pusha ;Save all registers
mov ax,008Ah ;Draw middle section
call RectC
dec bx ;Draw left end
mov al,9Fh
call Disp
mov bx,cx ;Draw right end
inc bx
dec ax
call Disp
popa ;Restore registers
call in_next ;Next box
jle in_sloop3 ;Loop back
;-------------------------------------------------
mov bx,0C2Ah ;Draw score headers
mov dx,O(Round$)
call PutStrF
mov bx,0F27h
mov dx,O(LinesLeft$)
call PutStrF
mov bx,122Ah
mov dx,O(Score$)
call PutStrF
mov bx,152Bh
mov dx,O(Lines$)
call PutStrF
popa ;Restore registers
ret ;Return
;-------------------------------------------------
in_bdisp: call Disp ;Display both sides of part of
mov bl,1Eh ;the bonus box
dec ax
call Disp
ret
;-------------------------------------------------
in_cloop: call DrawBox ;Draw outline of score/count boxes
call in_next
jle in_cloop
ret ;Return
;-------------------------------------------------
in_next: add bh,dl ;Move to the next box
add ch,dl
cmp ch,16h
ret
InitScreen EndP
;---------------------------------------------------------------------
; IsEmpty -- Check whether a line is empty
;---------------------------------------------------------------------
; AX = line to check, returns CY = yes, NC = no
IsEmpty Proc
pusha ;Save all registers
mov cx,10 ;10 blocks
mov bh,al ;BX = position
xor bl,bl
ie_loop: call GetBlock ;Check block
test ah,ah ;Quit if set
jnz ie_done
inc bx ;Next square
loop ie_loop ;Loop back
stc ;Set carry flag
ie_done: popa ;Restore registers
ret ;Return
IsEmpty EndP
;---------------------------------------------------------------------
; IsFull -- Check whether a line is full
;---------------------------------------------------------------------
; AX = line to check, returns CY = yes, NC = no
IsFull Proc
pusha ;Save all registers
mov cx,10 ;10 blocks
mov bh,al ;BX = position
xor bl,bl
if_loop: call GetBlock ;Check block
test ah,ah ;Quit if blank
jz if_done
inc bx ;Next square
loop if_loop ;Loop back
stc ;Set carry flag
if_done: popa ;Restore registers
ret ;Return
IsFull EndP
;---------------------------------------------------------------------
; KillBlock -- Remove a random block
;---------------------------------------------------------------------
; BL = X, BH = Y of area to avoid
KillBlock Proc
pusha ;Save all registers
mov cx,250 ;250 max. iters
mov dx,bx ;DX = position
kbk_loop: call FindBlock ;Generate random block
test ah,ah ;Block empty?
jnz kbk_found
loop kbk_loop ;Loop back
jmp kbk_done ;No luck...
kbk_found: call DelBlock ;Delete this block
kbk_Done: popa ;Restore registers
ret ;Return
KillBlock EndP
;---------------------------------------------------------------------
; LockPiece -- Lock a piece in the well
;---------------------------------------------------------------------
; AL = drop distance, BL = X position, BH = Y position
LockPiece Proc
pusha ;Save all registers
mov bp,bx ;BP = (X, Y)
mov di,bx ;SI = D
shr di,8 ;DI = Y
xor ah,ah
xchg si,ax
mov ax,40 ;AX = random freq.
call Rand
imul ax,10
add ax,440
mov bx,110 ;Make a noise
mov cx,3
call Crescendo
imul ax,Round,10 ;AX = 10 * Round
lea dx,[di+22] ;DX = Y + 22
mul dx ;Multiply
lea dx,[si+22] ;DX = 22 - Y + D
sub dx,di
mul dx ;Multiply
imul bx,di,-22 ;BX = 22 * (22 - Y)
add bx,22*22
div bx ;AX = score for piece
mov bx,9 ;Show for 1/2 second
call ShowBonus
add Score[0],ax ;Add into score
adc Score[2],0
call ShowStatus ;Update screen info
;-------------------------------------------------
cmp Piece,ACID_PIECE ;Special piece?
je lp_acid
cmp Piece,BOMB_PIECE
jne lp_cont
jmp lp_bomb
;-------------------------------------------------
lp_cont: call DelLines ;Delete all lines
test ax,ax ;No lines?
jz lp_skip1
push ax
xchg bx,ax ;AX = value of line
add bx,bx
mov ax,LineVals[bx]
mov dx,Round ;DX = multiplier
inc dx
shr dx,1
mul dx ;AX = line score
mov bx,170Ah ;Show 'BONUS' string
mov dx,O(Bonus$)
call PutStrF
mov bx,18 ;Show for 1 second
call ShowBonus
call lp_addin ;Add into score
pop ax ;Update line count
add Lines,ax
lp_skip1: call ShowStatus ;Update screen info
sub LinesLeft,ax ;No lines left?
jg lp_done
call NextRound ;Advance to the next round
lp_done: call ShowStatus ;Show status
call FlushKey ;Flush key buffer
popa ;Restore registers
ret ;Return
;-------------------------------------------------
lp_acid: mov dx,bp ;DX = position
sub dl,2
add dh,2
xor bp,bp ;Bonus = 0
lp_aloop: mov bx,dx ;Set up for inner loop
mov cx,5
lp_ailoop: call DelBlock ;Delete this block
mov ax,880 ;Make a random noise
call Rand
add ax,220
call Sound
mov ax,10 ;Delay 1/100 sec.
call Delay
inc bl ;Next block
loop lp_ailoop ;Loop back
imul ax,Round,5 ;Add in bonus
add bp,ax
mov ax,bp
mov bx,9 ;Display bonus
call ShowBonus
dec dh ;Next line
jnl lp_aloop ;Loop back
call lp_addin ;Add to score
call NoSound ;Turn off speaker
jmp lp_cont ;Continue
;-------------------------------------------------
lp_bomb: mov dx,bp ;DX = position
sub dl,3
add dh,3
mov si,7 ;7 lines
lp_bloop: mov bx,dx ;Set up for inner loop
mov cx,7
lp_biloop: call DelBlock ;Delete this block
mov ax,150 ;Make a random noise
call Rand
add ax,100
call Sound
mov al,4 ;Delay 4 msec. (AH = 0)
call Delay
inc bl ;Next block
loop lp_biloop ;Loop back
dec dh ;Next line
dec si ;Loop back
jnz lp_bloop
imul ax,Round,150 ;Add in bonus
call lp_addin
call NoSound ;Turn off speaker
jmp lp_cont ;Continue
;-------------------------------------------------
lp_addin: add Score[0],ax ;Add AX to score
adc Score[2],0
ret
LockPiece EndP
;---------------------------------------------------------------------
; LowBonus -- Add bonus for low puzzle
;---------------------------------------------------------------------
LowBonus Proc
pusha ;Save all registers
mov bx,000Ah ;Clear top section
mov cx,011Dh
mov ax,0320h
call RectB
inc bx ;Display header
mov dx,O(LowBonus$1)
call PutStrF
inc bh
mov dx,O(LowBonus$2)
call PutStrF
mov ax,1250 ;Delay 1.25 seconds
call Delay
mov bx,170Ah ;Show 'BONUS' string
mov dx,O(Bonus$)
call PutStrF
xor bh,bh ;Set color to white
mov al,0Fh
call RectA
mov di,19 ;Start with top line
xor si,si ;Score = 0
xor bp,bp ;Increment = 0
;-------------------------------------------------
lb_loop: mov ax,si ;Display bonus
xor bx,bx
call ShowBonus
imul ax,di,-20 ;Make a noise
add ax,840
mov bl,15
mov cx,10
call Crescendo
mov ax,di ;Is this line empty?
call IsEmpty
jnc lb_skip
;-------------------------------------------------
mov bh,al ;BX = position
mov bl,0
mov ax,3804h ;Pulsating color
call PutBlock ;Put first block
mov cl,8 ;8 blocks
inc ax ;Adjust type
lb_bloop: inc bx ;Put this block
call PutBlock
loop lb_bloop ;Loop back
inc bx ;Put last block
mov al,01h
call PutBlock
;-------------------------------------------------
imul dx,Round,10 ;Increment bonus
add bp,dx
add si,bp
dec di ;Next line
jmp lb_loop ;Loop back
;-------------------------------------------------
lb_skip: add Score[0],si ;Add to score
adc Score[2],0
mov ax,400 ;Delay 400 msec.
call Delay
mov ax,440 ;Make a noise
mov bx,20
mov cx,bx
call Crescendo
call ShowStatus ;Show status
mov BonusCtr,1 ;Reduce bonus timer
;-------------------------------------------------
lea dx,[di+1] ;DX = line
xchg dl,dh
lb_dloop: mov bx,dx ;Set up for loop
xor ax,ax
lb_diloop: call PutBlock ;Clear block
inc bx ;Next block
cmp bl,10 ;Loop back
jb lb_diloop
mov al,25 ;Delay 25 msec.
call Delay
inc dh ;Next line
cmp dh,20 ;Loop back
jb lb_dloop
popa ;Restore registers
ret ;Return
LowBonus EndP
;---------------------------------------------------------------------
; NewInt1C -- Timer tick handler
;---------------------------------------------------------------------
NewInt1C Proc
pushf ;Simulate an interrupt
db 09Ah ;CALL FAR opcode
OldInt1C dw ?,? ;Old INT 1C handler
pusha ;Save all registers
push ds
push es
push cs ;DS = CS
pop ds
push 0B800h ;ES = video memory
pop es
mov ax,PalCtr ;AX = palette counter
inc ax ;Increment modulo 8
and al,07h
mov PalCtr,ax ;Save new value
xchg bx,ax ;BX = palette counter
mov dx,03DAh ;DX = IS1 port
ni_vrt: in al,dx ;Wait for VRT
test al,8
jz ni_vrt
mov dl,0C0h ;DX = AC port
mov al,03h ;Setting color 3
out dx,al
call ni_ret ;Delay
mov al,PalVals[bx] ;Set color
out dx,al
call ni_ret ;Delay
mov al,20h ;Restart screen
out dx,al
;-------------------------------------------------
mov ax,RectData ;Get rectangle info
inc ah ;Increment counter
cmp ah,19 ;Check for wrap
jb ni_skip
xor ah,ah ;Wrap to start
inc ax ;Increment color modulo 6
cmp al,6
jb ni_skip
xor al,al
ni_skip: mov RectData,ax ;Save rectangle info
add al,9 ;AL = color
mov bx,TitleXY ;BX:CX = position
add bl,ah
mov cx,bx
add ch,3
call RectA ;Draw attr. rectangle
;-------------------------------------------------
cmp BonusCtr,0 ;Check bonus counter
je ni_done
dec BonusCtr ;Decrement bonus counter
jnz ni_done ;Zero?
mov bx,170Ah ;Clear the bonus box
mov cx,171Dh
mov ax,0E20h
call RectB
ni_done: pop es ;Restore registers
pop ds
popa
iret ;Interrupt return
ni_ret: ret ;null proc. for delay
NewInt1C EndP
;---------------------------------------------------------------------
; NextRound -- Advance to the next round
;---------------------------------------------------------------------
NextRound Proc
pusha ;Save all registers
push es
xor ax,ax ;Show with value 0
mov LinesLeft,ax
call ShowStatus
mov al,StRound ;First round, no bonus
sub al,B Round
cmp al,31h
je nr_skip
call LowBonus ;Do the bonus sequence
call ClearWell ;Clear the well
nr_skip: mov bx,Round ;BX = new round - 1
inc Round ;Next round
mov al,RoundTimes[bx] ;Increase speed (AH = 0 here)
mov DelayTime,ax
mov al,RoundLines[bx] ;Set new LinesLeft
mov LinesLeft,ax
;-------------------------------------------------
mov bx,070Ah ;Clear a rectangle
mov cx,0B1Dh
mov ax,0F20h
call RectB
push ds ;ES = DS
pop es
mov si,O(Round$) ;Copy round string
mov di,O(BufEnd)
mov cx,6
rep movsb
mov ax,Round ;Add the number
mov bx,O(BufEnd)+6
call CvtInt
mov ax,LinesLeft ;Convert LinesLeft
mov bx,O(BufEnd)+10
call CvtInt
mov di,O(BufEnd)+12 ;DI = offset
cmp ax,10 ;Decrement if 1 digit
sbb di,0
mov si,O(LinesX$) ;Copy lines string
mov cl,7
rep movsb
pop es ;Restore ES
mov bx,080Dh ;Display string 1
mov dx,O(BufEnd)
call PutStrF
mov bh,0Ah ;Display string 2
mov dx,O(BufEnd)+10
call PutStrF
call FlushKey ;Wait for a key
xor ah,ah
int 16h
;-------------------------------------------------
call ClearWell ;Clear the well
mov bx,Round ;BX = round type
mov bl,RoundType[bx-1]
and bx,3
add bx,bx ;SI = block list
mov si,BlockLists[bx]
nr_bloop: lodsw ;Load word
xchg bx,ax ;Draw this block
mov ax,8F00h
call PutBlock
test bx,bx ;Loop while not -1
jnl nr_bloop
nr_done: popa ;Restore registers
ret ;Return
NextRound EndP
;---------------------------------------------------------------------
; NoSound -- Turn off the speaker
;---------------------------------------------------------------------
NoSound Proc
push ax ;Save AX
in al,61h ;Turn off speaker
and al,0FCh
out 61h,al
pop ax ;Restore AX
ret ;Return
NoSound EndP
;---------------------------------------------------------------------
; PutBlock -- Display a block in the well
;---------------------------------------------------------------------
; BL = X, BH = Y, AH = color, AL = type
PutBlock Proc
pusha ;Save all registers
cmp bl,10 ;Out of range?
jae pb_done
cmp bh,22
jae pb_done
and al,0Fh ;Mask type to 0-15
cmp GridOn,'Y' ;If the grid is on and
jne pb_cont ;this piece is empty,
test ah,ah ;make it a grid block
jnz pb_cont
mov ax,0810h ;Dark gray, grid char
pb_cont: xor cx,cx ;Separate X from Y
xchg cl,bh
imul di,cx,-160 ;DI = offset
shl bx,2
lea di,[di+bx+0D34h]
add al,al ;AL = char
add al,80h
stosw ;Set first char
inc ax
stosw ;Set second char
pb_done: popa ;Restore registers
ret ;Return
PutBlock EndP
;---------------------------------------------------------------------
; PutPiece -- Display a Tetris piece in the well
;---------------------------------------------------------------------
; BL = X, BH = Y, CL = piece, CH = rotation, AL = color
PutPiece Proc
pusha ;Save all registers
xor dx,dx ;Separate piece data
xchg dl,ch
imul bp,dx,PIECE_SIZE ;SI = piece offset
imul si,cx,12*PIECE_SIZE
lea si,Pieces[si+bp]
mov cx,PIECE_SIZE ;Num. of blocks
mov ah,al ;AH = color
pp_loop: push bx ;Save BX
add bl,[si] ;Add in offsets
add bh,[si+4*PIECE_SIZE]
mov al,[si+8*PIECE_SIZE];AL = type
call PutBlock ;Display block
pop bx ;Restore BX
inc si ;Next block
loop pp_loop ;Loop back
popa ;Restore registers
ret ;Return
PutPiece EndP
;---------------------------------------------------------------------
; PutStr -- Display normal string
;---------------------------------------------------------------------
; BL = X, BH = Y, DX = ptr. to ASCIIZ string
PutStr Proc
pusha ;Save all registers
mov si,dx ;SI = string
xor cx,cx ;Split X from Y
xchg cl,bh
imul di,cx,160 ;DI = offset
add di,bx
add di,bx
ps_Loop: lodsb ;Load first byte
test al,al ;Found the null?
jz ps_Done
stosb ;Write to screen
inc di
jmp ps_Loop ;Loop back
ps_Done: popa ;Restore registers
ret ;Return
PutStr EndP
;---------------------------------------------------------------------
; PutStrF -- Display fat string
;---------------------------------------------------------------------
; BL = X, BH = Y, DX = ptr. to ASCIIZ string
PutStrF Proc
pusha ;Save all registers
mov si,dx ;SI = string
xor cx,cx ;Split X from Y
xchg cl,bh
imul di,cx,160 ;DI = offset
add di,bx
add di,bx
mov bx,O(BigChars)-20h ;BX = table
pf_Loop: lodsb ;Load first byte
test al,al ;Found the null?
jz pf_Done
xlat ;Get font char
stosb ;Write to screen
inc di
inc al ;Get second half
jns pf_Loop ;No second char?
stosb ;Write to screen
inc di
jmp pf_Loop ;Loop back
pf_Done: popa ;Restore registers
ret ;Return
PutStrF EndP
;---------------------------------------------------------------------
; Rand -- Generate random number
;---------------------------------------------------------------------
; AX (N) = range, returns AX = random number below N
Rand Proc
push cx ;Save registers
push dx
push ax ;Save AX
mov ax,RandNum[0] ;DX:AX = RandNum * 0019660Dh + 10DCDh
mov dx,660Dh ;low * low
mul dx
imul cx,RandNum[0],0019h;low * high, high * low
add dx,cx
imul cx,RandNum[2],660Dh
add dx,cx
add ax,0DCDh ;add in the 10DCDh
adc dx,0001h
mov RandNum[0],ax ;Save random number
mov RandNum[2],dx
xchg ax,dx ;AX = high word
xor dx,dx ;Zero DX
pop cx ;CX = range
div cx ;Divide by range
xchg ax,dx ;Result in AX
pop dx ;Restore registers
pop cx
ret ;Return
Rand EndP
;---------------------------------------------------------------------
; RandBlock -- Add a random block
;---------------------------------------------------------------------
; BL = X, BH = Y of area to avoid
RandBlock Proc
pusha ;Save all registers
mov cx,500 ;500 max. iters
mov dx,bx ;DX = position
rbk_loop: call FindBlock ;Generate random block
test ah,ah ;Block not empty?
jnz rbk_lb
dec bx ;Check (X-1, Y)
call GetBlock
inc bx
test ah,ah
jnz rbk_found
inc bx ;Check (X+1, Y)
call GetBlock
dec bx
test ah,ah
jnz rbk_found
dec bh ;Check (X, Y-1)
call GetBlock
inc bh
test ah,ah
jnz rbk_found
inc bh ;Check (X, Y+1)
call GetBlock
dec bh
test ah,ah
jnz rbk_found
rbk_lb: loop rbk_loop ;Loop back
jmp rbk_done ;No luck...
rbk_found: mov ax,3800h ;Add this block
call PutBlock
rbk_done: popa ;Restore registers
ret ;Return
RandBlock EndP
;---------------------------------------------------------------------
; RectA -- Rectangle of attributes
;---------------------------------------------------------------------
; BL = X1, BH = Y1, CL = X2, CH = Y2, AL = attr
RectA Proc
pusha ;Save all registers
xor dx,dx ;Split X1 from Y1
xchg dl,bh
imul bp,dx,160 ;BP = Y offset
inc bp
jmp RectX ;Draw the rectangle
RectA EndP
;---------------------------------------------------------------------
; RectB -- Rectangle of both chars and attrs
;---------------------------------------------------------------------
; BL = X1, BH = Y1, CL = X2, CH = Y2, AL = char, AH = attr
RectB Proc
pusha ;Save all registers
xor dx,dx ;Split X1 from Y1
xchg dl,bh
imul bp,dx,160 ;BP = Y offset
call RectY ;Calculate offset, distances
rb_loop: mov di,bp ;DI = offset
mov cx,bx ;CX = X size
rep stosw ;Write to screen
add bp,160 ;Next line
dec dx ;Loop back
jnl rb_loop
popa ;Restore registers
ret ;Return
RectB EndP
;---------------------------------------------------------------------
; RectC -- Rectangle of characters
;---------------------------------------------------------------------
; BL = X1, BH = Y1, CL = X2, CH = Y2, AL = char
RectC Proc
pusha ;Save all registers
xor dx,dx ;Split X1 from Y1
xchg dl,bh
imul bp,dx,160 ;BP = Y offset
jmp RectX ;Draw the rectangle
RectC EndP
;---------------------------------------------------------------------
; RectX -- Drawing tail for RectA, RectC
;---------------------------------------------------------------------
; BX = X1, DX = Y1, CL = X2, CH = Y2, BP = Y offset, AL = data byte
RectX Proc
call RectY ;Calculate offset, distances
rx_loop: mov di,bp ;DI = offset
mov cx,bx ;CX = X size
rx_iloop: stosb ;Write to screen
inc di
loop rx_iloop ;Loop back
add bp,160 ;Next line
dec dx ;Loop back
jnl rx_loop
popa ;Restore registers
ret ;Return
RectX EndP
;---------------------------------------------------------------------
; RectY -- Offset generator for RectB, RectX
;---------------------------------------------------------------------
; BX = X1, DX = Y1, CL = X2, CH = Y2, BP = Y offset, AL = data byte
; Returns DX = Ydist-1, BX = Xdist, BP = final offset, AL = data byte
RectY Proc
add bp,bx ;BP = final offset
add bp,bx
push ax ;Save AX
xor ax,ax ;Split X2 from Y2
xchg al,ch
sub cx,bx ;BX = X distance
mov bx,cx
inc bx
sub ax,dx ;DX = Y distance
xchg dx,ax
pop ax ;Get data byte
ret ;Return
RectY EndP
;---------------------------------------------------------------------
; ShowBonus -- Display a bonus value
;---------------------------------------------------------------------
; AX = value, BX = display time
ShowBonus Proc
pusha ;Save all registers
push bx ;Save BX
mov bx,O(BufEnd) ;Convert to string
call CvtInt
mov dx,bx ;Get length
call StrLenF
mov bx,171Eh ;Display in bonus box
sub bl,al
call PutStrF
mov cx,171Dh ;Set color to white
mov al,0Fh
call RectA
pop BonusCtr ;Set bonus time
popa ;Restore registers
ret ;Return
ShowBonus EndP
;---------------------------------------------------------------------
; ShowNext -- Show next piece
;---------------------------------------------------------------------
ShowNext Proc
pusha ;Save all registers
mov bx,0422h ;Clear next-piece box
mov cx,082Fh
mov ax,0080h
call RectB
mov bl,NxPiece ;BX = piece
xor bh,bh
imul si,bx,12*PIECE_SIZE;SI = piece offset
add si,O(Pieces)
mov ah,Colors[bx] ;BP = piece color
xchg bp,ax
mov cx,PIECE_SIZE ;Num. of blocks
sn_loop: mov al,[si] ;BX = X value
cbw
xchg bx,ax
mov al,[si+4*PIECE_SIZE];AX = Y value
cbw
imul di,ax,-160 ;DI = offset
shl bx,2
lea di,[di+bx+0410h]
mov ax,bp ;AH = color
mov al,[si+8*PIECE_SIZE];AL = type
and al,0Fh ;Mask to 0-15
add al,al ;Convert to char
add al,80h
stosw ;Write to screen
inc ax
stosw
inc si ;Next block
loop sn_loop ;Loop back
popa ;Restore registers
ret ;Return
ShowNext EndP
;---------------------------------------------------------------------
; ShowScores -- Display the high scores
;---------------------------------------------------------------------
; AX = score to highlight (-1 for none)
ShowScores Proc
pusha ;Save all registers
push ax ;Save AX
call StopInt ;Stop interrupt handler
xor bx,bx ;Clear the screen
mov cx,184Fh
mov ax,7F60h
call RectB
mov bx,011Eh ;Draw title box
mov cx,0632h
call DrawBox
mov bx,021Fh
call DrawTitle
mov TitleXY,bx ;Set title area
mov RectCtr,0
call StartInt ;Start interrupt handler
mov bx,0100h ;Draw vertical 3-D sides
mov cx,1700h
mov al,94h
call RectC
mov bl,4Fh
mov cl,4Fh
inc ax
call RectC
mov bx,0001h ;Draw horizontal 3-D sides
mov cx,004Eh
mov al,0FBh
call RectC
mov bh,18h
mov ch,4Eh
dec ax
call RectC
xor bx,bx ;Draw 3-D corners
mov al,98h
call Disp
mov bl,4Fh
mov al,93h
call Disp
mov bh,18h
mov al,87h
call Disp
mov bl,00h
mov al,8Ch
call Disp
mov bx,0717h ;Draw 'High Scores' box
mov cx,0938h
call DrawBox
mov bx,0818h
mov cx,0837h
mov ax,0E20h
call RectB
inc bx ;Display string
mov dx,O(HighScores$)
call PutStrF
mov bx,0A03h ;Draw main box
mov cx,174Ch
call DrawBox
mov bx,0B04h
mov cx,164Bh
mov ax,0A20h
call RectB
mov ch,0Bh
mov al,0Fh
call RectA
;-------------------------------------------------
mov dx,O(Name$) ;Display header strings
mov bx,0B0Ch
call PutStr
mov dx,O(Score$)
mov bl,34h
call PutStr
mov dx,O(Lines$)
mov bl,3Eh
call PutStr
mov dx,O(Round$)
mov bl,46h
call PutStr
;-------------------------------------------------
mov cx,9 ;10 lines
sc_loop: mov ax,cx ;Put line number in buffer
inc ax
mov bx,O(BufEnd)
call CvtInt
mov ax,002Eh ;Put period at end
cmp cx,9
sbb bx,-1
mov [bx+1],ax
mov bh,cl ;Output string
add bh,13
mov bl,5
mov dx,O(BufEnd)
call PutStrF
imul dx,cx,16 ;DX = name string
add dx,O(SC_Name)
mov bl,11 ;Output string
call PutStrF
;-------------------------------------------------
imul si,cx,4 ;Get score
mov ax,SC_Score[si]
mov dx,SC_Score[si+2]
mov bx,O(BufEnd) ;Convert score
call CvtLong
mov dx,bx
call StrLenF
mov bl,57 ;Display score
call sc_disp
shr si,1 ;Get lines
mov ax,SC_Lines[si]
call sc_cvt ;Convert lines
mov bl,67 ;Display lines
call sc_disp
mov ax,SC_Round[si] ;Get round
call sc_cvt ;Convert round
mov bl,75 ;Display round
call sc_disp
;-------------------------------------------------
dec cx ;Next line
jnl sc_loop ;Loop back
pop ax ;Get highlight
inc ax ;-1 = none
jz sc_done
mov bx,0C04h ;BX, CX = line
add bh,al
mov ch,bh
mov cl,4Bh
mov al,0Eh ;Turn it yellow
call RectA
sc_done: popa ;Restore registers
ret ;Return
;-------------------------------------------------
sc_cvt: mov bx,O(BufEnd) ;Convert to string
call CvtInt
mov dx,bx ;Get length of string
call StrLenF
ret ;Return
sc_disp: mov bh,cl ;Get position
add bh,13
sub bl,al
call PutStrF ;Display string
ret ;Return
ShowScores EndP
;---------------------------------------------------------------------
; ShowStatus -- Display score, round, etc.
;---------------------------------------------------------------------
ShowStatus Proc
pusha ;Save all registers
call ClearBoxes ;Clear score, count boxes
;-------------------------------------------------
mov ax,Round ;Convert 'Round'
mov bx,O(BufEnd)
call CvtInt
mov dx,bx ;DX = string
mov bx,0D2Fh ;Display 'Round'
call ss_disp
mov ax,LinesLeft ;Convert 'Lines Left'
mov bx,O(BufEnd)
call CvtInt
mov dx,bx ;DX = string
mov bx,102Fh ;Display 'Lines Left'
call ss_disp
mov ax,Score[0] ;Convert 'Score'
mov dx,Score[2]
mov bx,O(BufEnd)
call CvtLong
mov dx,bx ;DX = string
mov bx,132Fh ;Display 'Score'
call ss_disp
mov ax,Lines ;Convert 'Lines'
mov bx,O(BufEnd)
call CvtInt
mov dx,bx ;DX = string
mov bx,162Fh ;Display 'Lines'
call ss_disp
;-------------------------------------------------
mov ax,LinesLeft ;LinesLeft < 5?
cmp ax,5
ja ss_done
test ax,ax
jz ss_done
mov ah,0Ch ;AH = red on black color
mov bl,al ;AL = 3*num
add al,al
add al,bl
mov bh,6 ;BX = position
sub bh,bl
shl bh,2
mov bl,3
add al,1Bh ;Draw top half
call Disp
inc ax
inc bx
call Disp
inc ax
inc bx
call Disp
add bx,00FEh ;Go to LL corner
add al,0C8h ;Draw bottom half
call Disp
inc ax
inc bx
call Disp
inc ax
inc bx
call Disp
ss_done: popa ;Restore registers
ret ;Return
;-------------------------------------------------
ss_disp: call StrLenF ;Center the string
shr al,1
sub bl,al
call PutStrF ;Display the string
ret ;Return
ShowStatus EndP
;---------------------------------------------------------------------
; Sound -- Sound the speaker
;---------------------------------------------------------------------
; AX = frequency
Sound Proc
pusha ;Save registers
cmp SoundOn,'Y' ;Sound off?
jne sd_done
mov dx,12h ;BX = 1193180 / freq.
mov bx,34DCh
xchg bx,ax
div bx
xchg bx,ax
mov al,0B6h ;Set frequency
out 43h,al
xchg ax,bx
out 42h,al
mov al,ah
out 42h,al
in al,61h ;Turn on speaker
or al,3
out 61h,al
sd_done: popa ;Restore registers
ret ;Return
Sound EndP
;---------------------------------------------------------------------
; StartInt -- Start the interrupt handler
;---------------------------------------------------------------------
StartInt Proc
pusha ;Save all registers
push es
mov ax,351Ch ;Save old Int 1C
int 21h
mov OldInt1C[0],bx
mov OldInt1C[2],es
mov ah,25h ;Set new Int 1C
mov dx,O(NewInt1C)
int 21h
pop es ;Restore registers
popa
ret ;Return
StartInt EndP
;---------------------------------------------------------------------
; StopInt -- Stop the interrupt handler
;---------------------------------------------------------------------
StopInt Proc
pusha ;Save all registers
push ds
mov ax,251Ch ;Restore old Int 1C
lds dx,D OldInt1C
int 21h
pop ds ;Restore registers
popa
ret ;Return
StopInt EndP
;---------------------------------------------------------------------
; StrLenF -- Get length of fat string
;---------------------------------------------------------------------
; DX = string, returns AX = length
StrLenF Proc
push bx ;Save registers
push dx
push si
mov si,dx ;SI = string
xor dx,dx ;Zero DX
mov bx,O(BigChars)-20h ;BX = char table
sf_loop: lodsb ;Get byte
test al,al ;Check for null
jz sf_done
xlat ;16-bit char?
cmp al,80h ;Set carry if < 80h
sbb dx,-2 ;8-bit if < 80h, 16-bit otherwise
jmp sf_loop ;Loop back
sf_done: xchg ax,dx ;AX = length
pop si ;Restore registers
pop dx
pop bx
ret ;Return
StrLenF EndP
;#####################################################################
; UNINITIALIZED DATA SEGMENT
;#####################################################################
.Data?
;---------------------------------------------------------------------
FileName db 100 dup(?) ;Filename buffer
Buffer: ;Buffer for TETRIS.DAT
Font db 3584 dup(?) ;Font data
Pieces db PIECE_DATA dup(?) ;Piece data
SC_Score dw 20 dup(?) ;High-score data
SC_Lines dw 10 dup(?)
SC_Round dw 10 dup(?)
SC_Name db 160 dup(?)
BufEnd:
End Main