;---------------------------------------------------------------------
; 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
