;---------------------------------------------------------------------
; Tetris Deluxe v1.00 -- Tetris game
; 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-1C Title chars
; 1E-2C Countdown chars
; 2D-7A Normal chars (mostly)
; 7B-7F Fat chars (16x14 char set)
; 80-A1 3-D game pieces
; A2-E7 Fat chars
; E8-F6 Countdown chars
; F8-FF Inset box L-R-U-D-UL-UR-LL-LR
;---------------------------------------------------------------------
.Model Tiny
.Code
.186
Jumps
Org 100h
Start: call Main ;Call main procedure
int 20h ;Exit to DOS
;---------------------------------------------------------------------
XDISP Macro B,A ;Display char
mov bx,B
ifnb <A>
if (A LE 0FFh)
mov al,A
else
mov ax,A
endif
endif
call Disp
EndM
XRECTA Macro B,C,A ;Draw attr. rectangle
mov bx,B
mov cx,C
ifnb <A>
mov al,A
endif
call RectA
EndM
XRECTB Macro B,C,A ;Draw full rectangle
mov bx,B
mov cx,C
ifnb <A>
mov ax,A
endif
call RectB
EndM
XRECTC Macro B,C,A ;Draw char rectangle
mov bx,B
mov cx,C
ifnb <A>
mov al,A
endif
call RectC
EndM
XBOX Macro B,C ;Draw inset box
mov bx,B
mov cx,C
call DrawBox
EndM
;---------------------------------------------------------------------
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.| by round+8
;---------------------------------------------------------------------
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 1, 2, 3, 4, 5, 6, 1, 2, 3, 7, 8, 9,10,11,12,13,14,0
Title$2 db 15,16,17,18,19,20,15,16,17,21,22,23,24,25,26,27,28,0
Deluxe$ db 'D+E+L+U+X+E',0
Copyright$ db 60,61,62,63,64,'Tenie Remmel',0
Round$ db 'ROUND ',0 ;The space is for NextLevel
LinesLeft$ db 'LINES LEFT',0
Score$ db 'SCORE',0
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
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, 92, 32, 32, 32, 32, 32, 32, 32, 32, 32, 123
db 125,93, 124,32, 212,214,216,218,220,222,224,226
db 228,230,126,127,32, 32, 32, 160,32, 162,164,166
db 168,170,172,174,176,91, 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
; Bit 5 -- Switching columns
;-------------------------------------------------
RoundType db 000000b,000000b,000001b
db 000010b,000011b,000100b
db 000101b,010110b,001100b
db 011101b,111110b,111100b
db 12 dup(111100b)
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 offset BlockList0 ;Pointers to block lists
dw offset BlockList1
dw offset BlockList2
dw offset 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 offset Menu1$ ;Menu string table
dw offset Menu2$
dw offset Menu3$
dw offset Menu4$
dw offset Menu5$
dw offset Menu6$
dw offset Menu7$
dw offset Menu8$
dw offset Menu9$
;---------------------------------------------------------------------
ScoreOffs dw 0 ;Offset of scores in file
RandNum dw 0,0 ;Random number
PalCtr dw 0 ;Counter for pulsation
RectColor db 0 ;Color sweep data
RectCtr db 0
TitleX db 39h ;Title box position
TitleY db 04h
Score dw 0,0 ;Score data
Round dw 0
Lines dw 0
LinesLeft dw 0
DelayTime dw 0 ;Delay time in milliseconds
NumPieces dw 0 ;Number of pieces in use
PieceW label word ;Label for loading CX
Piece db 0 ;Piece data
PRot db 0
NxPiece db 0
BonusCtr dw 0 ;Countdown for bonus box
;---------------------------------------------------------------------
; 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,offset 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,offset DFile$ ;Copy 'TETRIS.DAT' onto
mov cx,11 ;the end of the buffer
rep movsb
;-------------------------------------------------
mov di,offset BufEnd ;Clear the buffer
mov cx,4096
xor ax,ax
rep stosw
mov ax,3D00h ;Open the data file
mov dx,offset FileName
int 21h
jc m_FError
xchg bx,ax ;BX = handle
mov ah,3Fh ;Read the data file
mov cx,8192
mov dx,offset 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,offset Font
int 10h
mov ax,1003h ;Turn off blinking
mov bh,1
int 10h
mov ax,1002h ;Set new palette
mov dx,offset 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,offset FileName
int 21h
jc m_FError
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,offset BufEnd-offset SC_Score
mov dx,offset SC_Score
int 21h
jc m_FError
mov ah,3Eh ;Close the file
int 21h
ret ;Return
;-------------------------------------------------
m_FError: mov ah,9 ;Output error string
mov dx,offset FError$
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
jz t_Quit
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]
mov bx,[t_XY] ;Draw the piece
call PutPiece
ret ;Return
;-------------------------------------------------
t_PieceOff: mov cx,[PieceW] ;Erase the piece
mov bx,[t_XY]
xor al,al
call PutPiece
ret ;Return
;-------------------------------------------------
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,word ptr [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: mov bx,[Round] ;Advancing lines allowed?
test [RoundType+bx-1],8
jz t_Bskip3
mov ax,LINE_FREQ ;Line frequency
call t_GenRand ;Get random number
jnz t_Bskip3 ;Not zero, skip
call AdvanceLine ;Advance a line
inc [t_Y] ;Move piece up
;-------------------------------------------------
t_Bskip3: test bp,32 ;Switching columns allowed?
jz t_KeyLoop
mov ax,DISP_FREQ ;Disp/column frequency
call t_GenRand ;Get random number
jnz t_KeyLoop ;Not zero, skip
call t_PieceOff ;Erase the piece
call SwitchCols ;Switch two columns
call t_PieceOn ;Redraw the piece
;-------------------------------------------------
t_KeyLoop: mov ah,1 ;Key pressed?
int 16h
jz t_Cont1
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_KeyLoop ;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
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
mov si,0FFh ;AL = color
and si,cx
mov al,[Colors+si]
call PutPiece ;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_KeyLoop
t_DownCont: xor al,al ;Erase the piece
call PutPiece
jmp t_DownLoop ;Loop back
;-------------------------------------------------
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
;-------------------------------------------------
t_KeyDone: call t_PieceOn ;Redraw the piece
jmp t_KeyLoop
t_Cont1: 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
xchg cx,ax ;CX = piece
mov si,cx ;AL = color
mov al,[Colors+si]
call PutPiece ;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_Quit: call StopInt ;Stop interrupt handler
ret ;Return
;-------------------------------------------------
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 dx,[Score+2] ;DX:AX = score
mov ax,[Score]
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,offset 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 = pos * 16
xor al,al ;Set name field
mov [SC_Name+si],al ; to null string
mov ax,bx ;Show scores
call ShowScores
call FlushKey ;Flush keyboard
;-------------------------------------------------
add si,offset SC_Name ;SI = ptr. to string
xor bx,bx ;Zero BX
mov ch,al ;CX = position of string
add ch,13
mov cl,11
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
cmp al,60h ;Make letters uppercase
jb as_skip1
sub al,20h
as_skip1: 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 byte ptr [si+bx],0
jmp as_eloop ;Loop back
;-------------------------------------------------
as_bksp: test bx,bx ;No chars?
jz as_key
dec bx ;Delete last char
mov byte ptr [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,28 ;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
XRECTB 000Ah,151Dh,0F20h;Fill the well with
;a visible color
ch_draw: mov si,offset 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 ax,ax ;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: cmp ah,10h ;Q, quit?
je ch_zero
sub al,'0' ;Out of range?
jbe ch_key
cmp al,M_NUMBERS
ja ch_key
ch_cont: jmp $+4
ch_zero: xor al,al ;'Q' --> 0
mov si,0Fh ;Convert to number
and si,ax
XRECTB 000Ah,151Dh,80h ;Restore the well
mov bp,sp ;Set pushed AX
mov [bp+14],si
popa ;Restore registers
ret ;Return
Choose EndP
;---------------------------------------------------------------------
; ClearWell -- Clear the well
;---------------------------------------------------------------------
ClearWell Proc
pusha ;Save all registers
xor bh,bh ;Initial position
xor ax,ax ;Cells are blank
cw_oloop: xor bl,bl ;Start of row
cw_iloop: call PutBlock ;Clear this block
inc bx ;Next block
cmp bl,10 ;Loop back
jb cw_iloop
inc bh ;Next line
cmp bh,22 ;Loop back
jb cw_oloop
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 ax,[SC_Lines+si] ;Copy lines value
mov [SC_Lines+di],ax
mov ax,[SC_Round+si] ;Copy round value
mov [SC_Round+di],ax
add si,si ;SI = src * 4
add di,di ;DI = dest * 4
mov ax,[SC_Score+si] ;Copy score value
mov [SC_Score+di],ax
mov ax,[SC_Score+si+2]
mov [SC_Score+di+2],ax
shl si,2 ;SI = src * 16
shl di,2 ;DI = dest * 16
add si,offset SC_Name ;Adjust to offsets
add di,offset SC_Name
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 byte ptr [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
test dx,dx ;DX = 0, use CvtInt
jnz cl_long
call CvtInt ;Convert integer
jmp cl_done ;Return
cl_long: mov di,bx ;DI = string pointer
mov bx,10000 ;Divide by 10000
div bx
push dx ;Save DX
mov si,10 ;SI = 10
xor cx,cx ;Zero CX
cl_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 cl_dloop
cl_ploop: pop ax ;Pop digit
mov [di],al ;Store digit
inc di
loop cl_ploop ;Loop back
;-------------------------------------------------
pop ax ;Restore low data
mov cx,4 ;4 digits
cl_dloopb: xor dx,dx ;Zero DX
div si ;Divide by 10
add dl,30h ;Convert to digit
push dx ;Push digit
loop cl_dloopb ;Loop back
mov cx,4 ;4 digits
cl_ploopb: pop ax ;Pop digit
mov [di],al ;Store digit
inc di
loop cl_ploopb ;Loop back
mov byte ptr [di],0 ;Add the null byte
cl_done: popa ;Restore registers
ret ;Return
CvtLong EndP
;---------------------------------------------------------------------
; Decode -- Decode the file (from BufEnd) to the main buffer
;---------------------------------------------------------------------
; ES = DS
Decode Proc
pusha ;Save all registers
mov si,offset BufEnd ;SI = in, DI = out
mov di,offset 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,offset BufEnd
mov [ScoreOffs],ax
mov cx,offset BufEnd-offset 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
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
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,offset Title$1
call PutStr
inc bh
mov dx,offset Title$2
call PutStr
inc bh
mov dx,offset Deluxe$
call PutStrF
inc bh
mov dx,offset Copyright$
call PutStr
popa ;Restore registers
ret ;Return
DrawTitle 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 es ;Save registers
push ax
mov ax,40h ;ES = BIOS segment
mov es,ax
mov ax,[es:1Ch] ;Set head = tail
mov [es:1Ah],ax
pop ax ;Restore registers
pop es
ret ;Return
FlushKey EndP
;---------------------------------------------------------------------
; GameOver -- Draw the 'Game Over' box
;---------------------------------------------------------------------
GameOver Proc
pusha ;Save all registers
XRECTB 090Ch,0E1Bh,0F20h;Clear a rectangle
XRECTB 0A0Eh,0D19h,4F20h
mov bx,0A0Eh ;Display line 1
mov dx,offset GameOver$1
call PutStrF
inc bh ;Display line 2
mov dx,offset GameOver$2
call PutStrF
inc bh ;Display line 3
mov dx,offset GameOver$3
call PutStrF
inc bh ;Display line 4
mov dx,offset GameOver$4
call PutStrF
call FlushKey ;Wait for a key
xor ax,ax
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 cx
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
xor cx,cx ;Separate X from Y
xchg cl,bh
imul di,cx,-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 cx
pop bx
ret ;Return
gb_dec3: dec ah ; -3
gb_dec2: dec ah ; -2
gb_dec1: dec ah ; -1
jmp gb_done
GetBlock EndP
;---------------------------------------------------------------------
; Initialize -- Reset all variables, display initial screen
;---------------------------------------------------------------------
Initialize Proc
pusha ;Save all registers
xor ax,ax ;Init score, lines, etc.
mov [Score],ax
mov [Score+2],ax
mov [Lines],ax
mov [BonusCtr],ax ;Init timer counters
mov [PalCtr],ax
mov [RectColor],al
mov [RectCtr],al
call InitScreen ;Show the initial screen
call SRand ;Seed RNG
popa ;Restore registers
ret ;Return
Initialize EndP
;---------------------------------------------------------------------
; InitScreen -- Display the initial screen
;---------------------------------------------------------------------
InitScreen Proc
pusha ;Save all registers
XRECTB 0000h,184Fh,7F60h;Clear screen to 7/F dither
XRECTA 0000h,014Fh,00h ;Clear top 2 lines
call ClearWell ;Draw the well
XRECTC 0300h,1700h,94h ;Draw vertical 3-D sides
XRECTC 031Eh,151Eh
XRECTC 034Fh,174Fh,95h
XRECTC 0309h,1509h
XRECTC 1801h,184Eh,0FAh ;Draw horizontal 3-D sides
XRECTC 0201h,0208h,0FBh
XRECTC 021Fh,024Eh
xor ax,ax ;Draw 3-D corners
XDISP 0200h,98h
XDISP 021Eh
XDISP 0209h,93h
XDISP 024Fh
XDISP 1800h,8Ch
XDISP 184Fh,87h
XBOX 0338h,084Ch ;Draw title box
mov bx,0439h
call DrawTitle
mov [TitleX],39h ;Set title area
mov [TitleY],04h
;-------------------------------------------------
XRECTC 160Ah,161Dh,8Ah ;Draw bonus box
XRECTC 180Ah,181Dh
XRECTB 170Ah,171Dh,0E20h
xor ax,ax
XDISP 1609h,9Fh
XDISP 1709h,95h
XDISP 1809h,8Fh
XDISP 161Eh,9Eh
XDISP 171Eh,94h
XDISP 181Eh,8Eh
;-------------------------------------------------
XBOX 0302h,0606h ;Draw count boxes
XBOX 0702h,0A06h
XBOX 0B02h,0E06h
XBOX 0F02h,1206h
XBOX 1302h,1606h
XRECTB 0403h,0505h,7020h
XRECTB 0803h,0905h
XRECTB 0C03h,0D05h
XRECTB 1003h,1105h
XRECTB 1403h,1505h
;-------------------------------------------------
XBOX 0321h,0930h ;Draw next-piece box
XRECTB 0422h,082Fh,0080h
XBOX 0B24h,0E3Ah ;Draw score boxes
XBOX 0E24h,113Ah
XBOX 1124h,143Ah
XBOX 1424h,173Ah
XRECTB 0C25h,0D39h,8A20h
XRECTB 0F25h,1039h
XRECTB 1225h,1339h
XRECTB 1525h,1639h
mov bx,0E24h ; dividers...
mov cx,0E3Ah
call is_boxdiv
mov bx,1124h
mov cx,113Ah
call is_boxdiv
mov bx,1424h
mov cx,143Ah
call is_boxdiv
;-------------------------------------------------
mov bx,0C2Ah ;Draw score headers
mov dx,offset Round$
call PutStrF
mov bx,0F27h
mov dx,offset LinesLeft$
call PutStrF
mov bx,122Ah
mov dx,offset Score$
call PutStrF
mov bx,152Bh
mov dx,offset Lines$
call PutStrF
popa ;Restore registers
ret ;Return
;-------------------------------------------------
is_boxdiv: mov ax,008Ah ;Draw box divider
inc bx ;Changes AX, BX
dec cx
call RectC
dec bx
mov al,9Fh
call Disp
inc cx
mov bx,cx
dec ax
call Disp
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,500 ;500 max. iters
mov dx,bx ;DX = position
kb_BLoop: 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 kb_Bskip1
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 kb_Blb
;-------------------------------------------------
kb_Bskip1: call GetBlock ;Block empty?
test ah,ah
jnz kb_Bok
kb_Blb: loop kb_BLoop ;Loop back
jmp kb_Done ;No luck...
kb_Bok: call DelBlock ;Delete this block
kb_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],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,offset Bonus$
call PutStrF
mov bx,18 ;Show for 1 second
call ShowBonus
add [Score],ax ;Add into score
adc [Score+2],0
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
add [Score],bp ;Add to score
adc [Score+2],0
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,220 ;Make a random noise
call Rand
add ax,110
call Sound
mov ax,4 ;Delay 4 msec.
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
add [Score],ax
adc [Score+2],0
call NoSound ;Turn off speaker
jmp lp_cont ;Continue
LockPiece EndP
;---------------------------------------------------------------------
; LowBonus -- Add bonus for low puzzle
;---------------------------------------------------------------------
LowBonus Proc
pusha ;Save all registers
XRECTB 000Ah,011Dh,0320h;Clear top section
mov bx,000Bh ;Display header
mov dx,offset LowBonus$1
call PutStrF
inc bh
mov dx,offset LowBonus$2
call PutStrF
mov ax,1000 ;Delay 1 second
call Delay
mov bx,170Ah ;Show 'BONUS' string
mov dx,offset Bonus$
call PutStrF
XRECTA 000Ah,011Dh,0Fh ;Set color to white
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 bx,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 cx,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],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,20
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 ax,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 bx,[PalCtr] ;BX = palette counter
inc bx ;Increment modulo 8
and bx,07h
mov [PalCtr],bx ;Save new value
mov dx,03DAh ;DX = IS1 port
ni_vrt: in al,dx ;Wait for VRT
test al,8
jz ni_vrt
mov dx,03C0h ;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 bl,[RectCtr] ;Get rectangle info
mov al,[RectColor]
inc bl ;Increment counter
cmp bl,19 ;Check for wrap
jb ni_skip
xor bl,bl ;Wrap to start
inc al ;Increment color modulo 12
cmp al,6
jb $+4
xor al,al
ni_skip: mov [RectCtr],bl ;Save rectangle info
mov [RectColor],al
add al,9 ;AL = color
add bl,[TitleX] ;BX:CX = position
mov cl,bl
mov bh,[TitleY]
mov ch,bh
add ch,03h
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
mov [LinesLeft],0 ;Show with value 0
call ShowStatus
mov al,[StRound] ;First round, no bonus
sub ax,[Round]
cmp ax,31h
je nr_skip
call LowBonus ;Do the bonus sequence
call ClearWell ;Clear the well
nr_skip: inc [Round] ;Next round
mov bx,[Round] ;Increase speed
mov al,[RoundTimes+bx]
xor ah,ah
mov [DelayTime],ax
mov al,[RoundLines+bx-1];Set new LinesLeft
mov [LinesLeft],ax
;-------------------------------------------------
XRECTB 070Ah,0B1Dh,0F20h;Clear a rectangle
push ds ;ES = DS
pop es
mov si,offset Round$ ;Copy round string
mov di,offset BufEnd
mov cx,6
rep movsb
mov ax,[Round] ;Add the number
mov bx,offset BufEnd+6
call CvtInt
mov ax,[LinesLeft] ;Convert LinesLeft
mov bx,offset BufEnd+10
call CvtInt
mov di,offset BufEnd+13 ;DI = offset
cmp ax,10 ;Decrement if 1 digit
sbb di,0
mov byte ptr [di-1],' ' ;Add a space
mov si,offset Lines$ ;Copy lines string
mov cx,6
rep movsb
pop es ;Restore ES
mov bx,080Dh ;Display string 1
mov dx,offset BufEnd
call PutStrF
mov bx,0A0Dh ;Display string 2
mov dx,offset BufEnd+10
call PutStrF
call FlushKey ;Wait for a key
xor ax,ax
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
cmp ax,-1 ;Done?
je nr_done
xchg bx,ax ;Draw this block
mov ax,8F00h
call PutBlock
jmp nr_bloop ;Loop back
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,offset BigChars ;BX = table
pf_Loop: lodsb ;Load first byte
test al,al ;Found the null?
jz pf_Done
sub al,20h ;Scale to table
cmp al,50h ;Too big?
jb $+4
xor al,al ;If so, zero it
xlat ;Get font char
stosb ;Write to screen
inc di
test al,80h ;No second char?
jz pf_Loop
inc ax ;Get second half
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 bx ;Save registers
push cx
push dx
push ax ;Save AX
mov ax,[RandNum] ;CX:BX = RandNum * 0019660Dh + 10DCDh
mov dx,660Dh ;low * low
mul dx
mov cx,dx ;put it in CX:BX
mov bx,ax
imul ax,[RandNum],0019h ;low * high, high * low
imul dx,[RandNum+2],660Dh
add cx,ax ;add them in
add cx,dx
add bx,0DCDh ;add in the 10DCDh
adc cx,0001h
mov [RandNum],bx ;Save random number
mov [RandNum+2],cx
xchg ax,cx ;AX = low 15 bits of CX
shl ax,1 ; plus high bit of BX
rol bx,1
and bx,1
or ax,bx
pop bx ;BX = maximum
xor dx,dx ;Zero DX
test bx,bx ;Can't divide by zero
jz rn_skip
div bx ;Divide by BX
rn_skip: xchg ax,dx ;Result in AX
pop dx ;Restore registers
pop cx
pop bx
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
rb_BLoop: 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 rb_Bskip1
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 rb_Blb
;-------------------------------------------------
rb_Bskip1: call GetBlock ;Block not empty?
test ah,ah
jnz rb_Blb
dec bx ;Check (X-1, Y)
call GetBlock
inc bx
test ah,ah
jnz rb_Bok
inc bx ;Check (X+1, Y)
call GetBlock
dec bx
test ah,ah
jnz rb_Bok
dec bh ;Check (X, Y-1)
call GetBlock
inc bh
test ah,ah
jnz rb_Bok
inc bh ;Check (X, Y+1)
call GetBlock
dec bh
test ah,ah
jnz rb_Bok
;-------------------------------------------------
rb_Blb: loop rb_BLoop ;Loop back
jmp rb_Done ;No luck...
rb_Bok: mov ax,3800h ;Add this block
call PutBlock
rb_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 = offset
add bp,bx
add bp,bx
inc bp
push ax ;Save AX
xor ax,ax ;Split X2 from Y2
xchg al,ch
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 = offset
add bp,bx
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
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 = offset
add bp,bx
add bp,bx
push ax ;Save AX
xor ax,ax ;Split X2 from Y2
xchg al,ch
jmp RectX ;Draw the rectangle
RectC EndP
;---------------------------------------------------------------------
; RectX -- Drawing tail for RectA, RectC
;---------------------------------------------------------------------
; BX = X1, DX = Y1, CX = X2, AX = Y2, BP = offset, data byte on stack
RectX Proc
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
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
;---------------------------------------------------------------------
; ShowBonus -- Display a bonus value
;---------------------------------------------------------------------
; AX = value, BX = display time
ShowBonus Proc
pusha ;Save all registers
push bx ;Save BX
mov bx,offset 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
XRECTB 0422h,082Fh,80h ;Clear next-piece box
mov bl,[NxPiece] ;BX = piece
xor bh,bh
imul si,bx,12*PIECE_SIZE;SI = piece offset
add si,offset 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
XRECTB 0000h,184Fh,7F60h;Clear the screen
XBOX 011Eh,0632h ;Draw title box
mov bx,021Fh
call DrawTitle
mov [TitleX],1Fh ;Set title area
mov [TitleY],02h
mov [RectCtr],0
call StartInt ;Start interrupt handler
XRECTC 0100h,1700h,94h ;Draw vertical 3-D sides
XRECTC 014Fh,174Fh,95h
XRECTC 0001h,004Eh,0FBh ;Draw horizontal 3-D sides
XRECTC 1801h,184Eh,0FAh
xor ax,ax ;Draw 3-D corners
XDISP 0000h,98h
XDISP 004Fh,93h
XDISP 1800h,8Ch
XDISP 184Fh,87h
XBOX 0717h,0938h ;Draw 'High Scores' box
XRECTB 0818h,0837h,0E20h
mov bx,0819h ;Display string
mov dx,offset HighScores$
call PutStrF
XBOX 0A03h,174Ch ;Draw main box
XRECTB 0B04h,164Bh,0A20h
XRECTA 0B04h,0B4Bh,0Fh
;-------------------------------------------------
mov dx,offset Name$ ;Display header strings
mov bx,0B0Ch
call PutStr
mov dx,offset Score$
mov bl,34h
call PutStr
mov dx,offset Lines$
mov bl,3Eh
call PutStr
mov dx,offset Round$
mov bl,46h
call PutStr
;-------------------------------------------------
xor cx,cx ;Zero CX
sc_loop: mov ax,cx ;Put line number in buffer
inc ax
mov bx,offset 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,offset BufEnd
call PutStrF
;-------------------------------------------------
mov dx,cx ;DX = name string
shl dx,4
add dx,offset SC_Name
mov bl,11 ;Output string
call PutStrF
;-------------------------------------------------
mov si,cx ;Get score
shl si,2
mov ax,[SC_Score+si]
mov dx,[SC_Score+si+2]
mov bx,offset 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
;-------------------------------------------------
inc cx ;Next line
cmp cx,10 ;Loop back
jb sc_loop
pop ax ;Get highlight
test ax,ax ;-1 = none
jl sc_done
mov bh,13 ;BH, CH = line
add bh,al
mov ch,bh
mov bl,04h ;BL, CL = X position
mov cl,4Bh
mov al,0Eh ;Turn it yellow
call RectA
sc_done: popa ;Restore registers
ret ;Return
;-------------------------------------------------
sc_cvt: mov bx,offset 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
XRECTB 0D25h,0D39h,8F20h;Clear score boxes
XRECTB 1025h,1039h
XRECTB 1325h,1339h
XRECTB 1625h,1639h
XRECTB 0403h,0505h,7020h;Clear count boxes
XRECTB 0803h,0905h
XRECTB 0C03h,0D05h
XRECTB 1003h,1105h
XRECTB 1403h,1505h
;-------------------------------------------------
mov ax,[Round] ;Convert 'Round'
mov bx,offset BufEnd
call CvtInt
mov dx,bx ;Get length
call StrLen
mov bx,0D2Fh ;Display 'Round'
sub bl,al
call PutStrF
mov ax,[LinesLeft] ;Convert 'Lines Left'
mov bx,offset BufEnd
call CvtInt
mov dx,bx ;Get length
call StrLen
mov bx,102Fh ;Display 'Lines Left'
sub bl,al
call PutStrF
mov ax,[Score] ;Convert 'Score'
mov dx,[Score+2]
mov bx,offset BufEnd
call CvtLong
mov dx,bx ;Get length
call StrLen
mov bx,132Fh ;Display 'Score'
sub bl,al
call PutStrF
mov ax,[Lines] ;Convert 'Lines'
mov bx,offset BufEnd
call CvtInt
mov dx,bx ;Get length
call StrLen
mov bx,162Fh ;Display 'Lines'
sub bl,al
call PutStrF
;-------------------------------------------------
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
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
mov al,bl
out 42h,al
mov al,bh
out 42h,al
in al,61h ;Turn on speaker
or al,3
out 61h,al
sd_done: popa ;Restore registers
ret ;Return
Sound EndP
;---------------------------------------------------------------------
; SRand -- Seed random number generator
;---------------------------------------------------------------------
SRand Proc
push es ;Save registers
push ax
mov ax,40h ;ES = BIOS segment
mov es,ax
mov ax,[es:6Ch] ;Get low word
mov [RandNum],ax ;Save it
mov ax,[es:6Eh] ;Get high word
mov [RandNum+2],ax ;Save it
pop ax ;Restore registers
pop es
ret ;Return
SRand 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+2],es
mov [OldInt1C],bx
mov ah,25h ;Set new Int 1C
mov dx,offset 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
mov dx,[OldInt1C]
mov ds,[OldInt1C+2]
int 21h
pop ds ;Restore registers
popa
ret ;Return
StopInt EndP
;---------------------------------------------------------------------
; StrLen -- Get length of string
;---------------------------------------------------------------------
; DX = string, returns AX = length
StrLen Proc
push dx ;Save registers
push si
mov si,dx ;SI = string
xor dx,dx ;DX = 0
sl_loop: lodsb ;Get byte
test al,al ;Check for null
jz sl_done
inc dx ;Increment count
jmp sl_loop ;Loop back
sl_done: xchg ax,dx ;AX = length
pop si ;Restore registers
pop dx
ret ;Return
StrLen 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,offset BigChars ;BX = char table
sf_loop: lodsb ;Get byte
test al,al ;Check for null
jz sf_done
inc dx ;Increment count
sub al,20h ;Scale to table
xlat ;16-bit char?
test al,80h
jz sf_loop ;Jump if not
inc dx ;Increment count
jmp sf_loop ;Loop back
sf_done: xchg ax,dx ;AX = length
pop si ;Restore registers
pop dx
pop bx
ret ;Return
StrLenF EndP
;---------------------------------------------------------------------
; SwitchBlox -- Switch two blocks
;---------------------------------------------------------------------
; BX = block 1, DX = block 2
SwitchBlox Proc
pusha ;Save all registers
call GetBlock ;SI = (A)
xchg si,ax
xchg bx,dx ;AX = (B)
call GetBlock
xchg si,ax ;AX = (A)
and al,0Ah ;Kill L/R type
call PutBlock ;Set block B
xchg si,ax ;AX = (B)
and al,0Ah ;Kill L/R type
xchg bx,dx ;Set block A
call PutBlock
dec bx ;Fix block at left [1]
call GetBlock
and al,0Bh
call PutBlock
inc bx ;Fix block at right [1]
inc bx
call GetBlock
and al,0Eh
call PutBlock
mov bx,dx ;Fix block at left [2]
dec bx
call GetBlock
and al,0Bh
call PutBlock
inc bx ;Fix block at right [2]
inc bx
call GetBlock
and al,0Eh
call PutBlock
popa ;Restore registers
ret ;Return
SwitchBlox endp
;---------------------------------------------------------------------
; SwitchCols -- Switch two random columns
;---------------------------------------------------------------------
SwitchCols Proc
pusha ;Save all registers
mov ax,10 ;Get random columns
call Rand
xchg bx,ax
mov ax,10
call Rand
xchg dx,ax
xor bh,bh ;Start at line 0
xor dh,dh
mov cx,22 ;22 lines
sw_loop: call SwitchBlox ;Switch these blocks
inc bh ;Next line
inc dh
loop sw_loop ;Loop back
popa ;Restore registers
ret ;Return
SwitchCols EndP
;---------------------------------------------------------------------
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 Start