Metropoli BBS
VIEWER: tetris.asm MODE: TEXT (ASCII)
;---------------------------------------------------------------------
; Tetris Deluxe v1.10 -- Tetris game, 4971 bytes
; Requires 286, EGA graphics (386 recommended)
;---------------------------------------------------------------------
; To construct TETRIS.DAT (TETRIS.FNT must exist):
;   GEN_PCS
;   GEN_FONT
;   COPY /B TETRIS.FN1+TETRIS.PCS TETRIS.DAT
;   DEL TETRIS.FN1
;   DEL TETRIS.PCS
;---------------------------------------------------------------------
; Design of font:
;   00          Blank
;   01          Fat chars - 1-wide chars ( I )
;   02-1D       Title chars
;   1E-2C       Countdown chars
;   2D-2F       Fat chars - 1-wide chars ( +-. )
;   30-39       Normal chars - numbers
;   41-5A       Normal chars - uppercase
;   5B-5F       Copyright message
;   60          50% gray
;   61-7A       Normal chars - lowercase
;   80-9F       3-D game pieces
;   A0-E5       Fat chars - 2-wide chars
;   E6-F4       Countdown chars
;   F8-FF       Inset box L-R-U-D-UL-UR-LL-LR
;---------------------------------------------------------------------

O           equ <offset>
B           equ <byte ptr>
W           equ <word ptr>
D           equ <dword ptr>

.Model Tiny
.186


;#####################################################################
; INITIALIZED DATA SEGMENT
;#####################################################################

.Data

;---------------------------------------------------------------------

BLOCK_FREQ  = 3000                  ;Random block freq.  | Divide these
PIECE_FREQ  = 8200                  ;Bonus piece freq.   | frequencies
DISP_FREQ   = 11000                 ;Disappearing freq.  | by round+8
LINE_FREQ   = 16400                 ;Advancing line freq.|

;---------------------------------------------------------------------

PIECE_SIZE  = 6                     ;Max size of piece
MAX_PIECES  = 37                    ;Number of pieces
PIECE_DATA  = 12 * PIECE_SIZE * MAX_PIECES

BOMB_PIECE  = 35                    ;Bonus pieces
ACID_PIECE  = 36

;---------------------------------------------------------------------

DFile$      db 'TETRIS.DAT',0
FError$     db 'Error reading TETRIS.DAT$'

Title$1     db  2, 3, 4, 5, 6, 7, 2, 3, 4, 8, 9,10,11,12,13,14,15,0
Title$2     db 16,17,18,19,20,21,16,17,18,22,23,24,25,26,27,28,29,0
Deluxe$     db 'D+E+L+U+X+E',0
Copyright$  db 91,92,93,94,95,'Tenie Remmel',0
Round$      db 'ROUND ',0           ;The space is for NextRound
LinesLeft$  db 'LINES LEFT',0
Score$      db 'SCORE',0
LinesX$     db ' '                  ;Also for NextRound
Lines$      db 'LINES',0

Menu1$      db '1 -- 7 PCS.',0      ;The initial menu
Menu2$      db '2 -- 14 PCS.',0
Menu3$      db '3 -- 21 PCS.',0
Menu4$      db '4 -- 28 PCS.',0
Menu5$      db '5 -- 35 PCS.',0
Menu6$      db 'S -- SOUND '
SoundOn     db 'Y',0
Menu7$      db 'G -- GRID    '
GridOn      db 'N',0
Menu8$      db 'R -- ROUND '
StRound     db '1',0
Menu9$      db 'Q -- QUIT',0

Bonus$      db 'BONUS',0
LowBonus$1  db ' BONUS FOR',0
LowBonus$2  db 'LOW PUZZLE',0

OVER_LEN    equ 7                   ;Length of game-over strings

GameOver$1  db 6Ch,4 dup(65h),69h,0 ;The game-over box
GameOver$2  db 6Ah,'GAME',6Ah,0
GameOver$3  db 6Ah,'OVER',6Ah,0
GameOver$4  db 66h,4 dup(65h),63h,0

HighScores$ db 'H I G H   S C O R E S',0
Name$       db 'NAME',0

;---------------------------------------------------------------------

BigChars    db 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46
            db 32, 45, 47, 32, 212,214,216,218,220,222,224,226
            db 228,230,32, 32, 32, 32, 32, 32, 32, 162,164,166
            db 168,170,172,174,176,1,  178,180,182,184,186,188
            db 190,192,194,196,198,200,202,204,206,208,210,32
            db 32, 32, 32, 32, 128,130,132,134,136,138,140,142
            db 144,146,148,150,152,154,156,158

Palette     db 0,1,2,3,4,5,38,7,56,9,18,27,36,45,54,63,0

Colors      db 06Eh,07Fh,05Dh,019h,02Ah,0C6h,04Ch
            db 06Fh,07Eh,05Eh,01Ah,02Bh,0CFh,04Dh
            db 01Bh,01Ch,01Dh,04Ah,04Bh,0CEh,02Eh
            db 08Ch,08Ah,08Dh,09Eh,02Fh,05Fh,09Fh
            db 087h,016h,05Bh,06Bh,07Bh,086h,08Bh
            db 03Ch,03Ah

LineVals    dw 0,50,150,400         ;Line scores
            dw 1000,2000,4000

PalVals     db 00h,00h,20h,04h      ;Normal pulsation data
            db 24h,24h,04h,20h

;-------------------------------------------------
; Round Types:  Bit 0-1 --  Initial setup
;               Bit 2   --  Appearing blocks, bonus pieces
;               Bit 3   --  Advancing lines
;               Bit 4   --  Disappearing blocks
;-------------------------------------------------

RoundType   db 00000b,00000b,00001b
            db 00010b,00011b,00100b
            db 00101b,10110b,01100b
            db 11101b,11110b,11100b
            db 12 dup(11100b)

RoundTimes  db 99,87,77,68,60,53    ;Delay times
            db 47,42,37,33,29,26
            db 23,21,19,17,15,14
            db 13,12,11,10, 9, 8

RoundLines  db 5, 8, 12,15,15,15    ;Num. of lines needed
            db 15,15,18,21,24,26    ;to complete each round
            db 28,30,32,34,36,38
            db 40,42,44,46,48,50

;-------------------------------------------------

BlockLists  dw O(BlockList0)        ;Pointers to block lists
            dw O(BlockList1)
            dw O(BlockList2)
            dw O(BlockList3)

BlockList1  dw 0000h,0100h,0200h    ;Block list for ??01 rounds
            dw 0300h,0400h,0500h
            dw 0600h,0700h,0009h
            dw 0109h,0209h,0309h
            dw 0409h,0509h,0609h
            dw 0709h,-1

BlockList2  dw 0006h,0001h,0108h    ;Block list for ??10 rounds
            dw 0103h,0205h,0307h
            dw 0306h,0302h,0409h
            dw 0404h,0400h,-1

BlockList3  dw 0000h,0001h,0003h    ;Block list for ??11 rounds
            dw 0004h,0005h,0006h
            dw 0008h,0009h,0101h
            dw 0102h,0104h,0105h
            dw 0107h,0108h,0202h
            dw 0203h,0206h,0207h
            dw 0303h,0304h,0305h
            dw 0306h,0404h,0405h
BlockList0  dw -1                   ;No blocks for ??00 rounds

;-------------------------------------------------

M_CHOICES   = 9                     ;Total num. of choices
M_NUMBERS   = 5                     ;Number of piece sets

Menu        dw O(Menu1$)            ;Menu string table
            dw O(Menu2$)
            dw O(Menu3$)
            dw O(Menu4$)
            dw O(Menu5$)
            dw O(Menu6$)
            dw O(Menu7$)
            dw O(Menu8$)
            dw O(Menu9$)


;#####################################################################
; UNINITIALIZED DATA SEGMENT
;#####################################################################

.Data?

;---------------------------------------------------------------------

ScoreOffs   dw ?                    ;Offset of scores in file

RandNum     dw ?,?                  ;Random number

PalCtr      dw ?                    ;Counter for pulsation

RectData    label word              ;Color sweep data
RectColor   db ?
RectCtr     db ?

TitleXY     label word              ;Title box position
TitleX      db ?
TitleY      db ?

Score       dw ?,?                  ;Score data
Round       dw ?
Lines       dw ?
LinesLeft   dw ?

DelayTime   dw ?                    ;Delay time in milliseconds
NumPieces   dw ?                    ;Number of pieces in use

PieceW      label word              ;Label for loading CX
Piece       db ?                    ;Piece data
PRot        db ?
NxPiece     db ?

BonusCtr    dw ?                    ;Countdown for bonus box


;#####################################################################
; CODE SEGMENT
;#####################################################################

.Code
Org 100h

;---------------------------------------------------------------------
; Main procedure
;---------------------------------------------------------------------

Main        Proc

            mov es,ds:[2Ch]         ;ES = environment
            xor di,di               ;Set up for scan
            mov cx,-1
            xor al,al

m_floop1:   repne scasb             ;Find a zero
            scasb                   ;Double zero?
            jne m_floop1            ;if not, loop back

            lea si,[di+2]           ;DS:SI = program name
            push es
            pop ds
            push cs                 ;ES = CS
            pop es

            mov di,O(FileName)      ;DI = filename buffer

m_floop2:   lodsb                   ;Copy byte
            stosb
            test al,al              ;Loop while not zero
            jnz m_floop2

            push cs                 ;DS = CS
            pop ds

            dec di                  ;DI = ptr. to null
            std                     ;Scan in reverse
            mov al,'\'              ;Find last slash
            repne scasb

            cld                     ;Restore dir-flag
            scasw                   ;Move past slash

            mov si,O(DFile$)        ;Copy 'TETRIS.DAT' onto
            mov cx,11               ;the end of the buffer
            rep movsb

;-------------------------------------------------

            mov di,O(BufEnd)        ;Clear the buffer
            mov cx,4096
            xor ax,ax
            rep stosw

            mov ax,3D00h            ;Open the data file
            mov dx,O(FileName)
            int 21h
            jc m_FError

            xchg bx,ax              ;BX = handle

            mov ah,3Fh              ;Read the data file
            mov cx,8192
            mov dx,O(BufEnd)
            int 21h
            jc m_FError
            xchg si,ax              ;SI = read size

            mov ah,3Eh              ;Close the file
            int 21h

            call Decode             ;Decode the file

;-------------------------------------------------

            mov ax,1201h            ;Set EGA mode (if VGA)
            mov bl,30h
            int 10h
            mov ax,3                ;Set video mode 3
            int 10h

            mov ax,1100h            ;Load the custom font
            mov bx,0E00h            ;(Tetris pieces, etc.)
            mov cx,0100h
            xor dx,dx
            mov bp,O(Font)
            int 10h

            mov ax,1003h            ;Turn off blinking
            mov bh,1
            int 10h

            mov ax,1002h            ;Set new palette
            mov dx,O(Palette)
            int 10h

            mov ah,2                ;Put cursor off screen
            xor bh,bh
            mov dx,1900h
            int 10h

            mov ax,0305h            ;Speed up keyboard
            xor bx,bx
            int 16h

;-------------------------------------------------

            push 0B800h             ;ES = video memory
            pop es

            call Tetris             ;Call main game procedure

            mov ax,1202h            ;Set VGA mode (if VGA)
            mov bl,30h
            int 10h
            mov ax,3                ;Reset video mode
            int 10h

;-------------------------------------------------

            mov ax,3D01h            ;Open the data file
            mov dx,O(FileName)
            int 21h
            jnc m_FileOK

;-------------------------------------------------

m_FError:   mov ah,9                ;Output error string
            mov dx,O(FError$)
            jmp m_21ret             ;Return

;-------------------------------------------------

m_FileOK:   xchg bx,ax              ;BX = handle

            mov ax,4200h            ;Move file pointer
            xor cx,cx
            mov dx,ScoreOffs
            int 21h

            mov ah,40h              ;Write out scores, etc.
            mov cx,O(BufEnd)-O(SC_Score)
            mov dx,O(SC_Score)
            int 21h
            jc m_FError

            mov ah,3Eh              ;Close the file
m_21ret:    int 21h
            ret                     ;Return

Main        EndP

;---------------------------------------------------------------------
; Tetris -- Main game procedure
;---------------------------------------------------------------------

Tetris      Proc

t_BigLoop:  call Initialize         ;Initialize the game
            call StartInt           ;Start interrupt handler

            call Choose             ;Choose num. pieces or quit
            test ax,ax              ;Zero = quit
            jnz t_Cont0

            call StopInt            ;Stop interrupt handler
            ret                     ;Return

t_Cont0:    imul ax,7               ;Set piece count
            mov NumPieces,ax

;-------------------------------------------------

            mov t_Ctr,0             ;Ctr = 0

            mov ax,NumPieces        ;Get random piece
            call Rand
            mov PieceW,ax           ;Set piece word

            mov ax,NumPieces        ;Get random piece
            call Rand
            mov NxPiece,al          ;Set next piece

            mov t_XY,1505h          ;Set initial position

            mov bl,StRound          ;Set initial round
            and bx,0Fh
            dec bx
            mov Round,bx

            call NextRound          ;Set up round vars

            call ShowStatus         ;Show initial status
            call ShowNext
            jmp t_KeyDone           ;Go to show-piece code

;-------------------------------------------------

t_GenRand:  cwd                     ;Convert to frequency
            add bx,8
            div bx
            call Rand               ;Get random number
            test ax,ax              ;Test with zero
            ret                     ;Return

;-------------------------------------------------

t_PieceOn:  mov cx,PieceW           ;CX = piece
            mov bx,cx               ;Get color
            xor bh,bh
            mov al,Colors[bx]
t_PutPiece: mov bx,t_XY             ;Draw the piece
            call PutPiece
            ret                     ;Return

;-------------------------------------------------

t_PieceOff: mov cx,PieceW           ;Erase the piece
            xor al,al
            jmp t_PutPiece

;-------------------------------------------------

t_MainLoop: mov ax,DelayTime        ;Delay for current time
            call Delay
            inc t_Ctr               ;Increment modulo 4
            and t_Ctr,3

;-------------------------------------------------

            mov bx,Round            ;Get round type in BP
            mov bp,W RoundType[bx-1]

            test bp,4               ;Random blocks & pieces allowed?
            jz t_Bskip1

            mov ax,PIECE_FREQ       ;Piece frequency
            call t_GenRand          ;Get random number
            jnz t_Bskip0            ;Not zero, skip

            mov ax,2                ;Set next piece to be a
            call Rand               ; bonus piece (acid, bomb)
            add al,35
            mov NxPiece,al
            call ShowNext           ;Re-display next piece

t_Bskip0:   mov ax,BLOCK_FREQ       ;Block frequency
            call t_GenRand          ;Get random number
            jnz t_Bskip1            ;Not zero, skip

            mov bx,t_XY             ;BX = piece area
            call RandBlock          ;Generate random block

;-------------------------------------------------

t_Bskip1:   test bp,16              ;Disappearing blocks allowed?
            jz t_Bskip2

            mov ax,DISP_FREQ        ;Disp/column freq.
            call t_GenRand          ;Get random number
            jnz t_Bskip2            ;Not zero, skip

            mov bx,t_XY             ;BX = piece area
            call KillBlock          ;Remove a random block

;-------------------------------------------------

t_Bskip2:   test bp,8               ;Advancing lines allowed?
            jz t_KeyLoop

            mov ax,LINE_FREQ        ;Line frequency
            call t_GenRand          ;Get random number
            jnz t_KeyLoop           ;Not zero, skip

            call AdvanceLine        ;Advance a line
            inc t_Y                 ;Move piece up

;-------------------------------------------------

t_KeyLoop:  mov ah,1                ;Key pressed?
            int 16h
            jnz t_KeyHit
            jmp t_NoKey

t_KeyHit:   call t_PieceOff         ;Erase the piece

            xor ah,ah               ;Get the key
            int 16h
            mov al,ah               ;AL = scan code

            cmp al,4Bh              ;Left?
            je t_Left
            cmp al,4Dh              ;Right?
            je t_Right
            cmp al,50h              ;Down (Drop)?
            je t_Down
            cmp al,39h              ;Space (Rotate)?
            je t_Rotate
            cmp al,44h              ;F10 (Pause)?
            je t_Pause
            cmp al,01h              ;Q, Esc (Quit)?
            je t_j0
            cmp al,10h
            jne t_KDjmp             ;Get another key
t_j0:       jmp t_Done

;-------------------------------------------------

t_Left:     mov bx,t_XY             ;Fits at (x-1, y) ?
            mov cx,PieceW
            dec bx
            call Fits
            adc t_X,-1              ;Subtract 1 if NC
            jmp t_KeyDone

;-------------------------------------------------

t_Right:    mov bx,t_XY             ;Fits at (x+1, y) ?
            mov cx,PieceW
            inc bx
            call Fits
            sbb t_X,-1              ;Add 1 if NC
            jmp t_KeyDone

;-------------------------------------------------

t_Pause:    call t_PieceOn          ;Redraw the piece

            call FlushKey           ;Flush key buffer
            xor ah,ah               ;Wait for key
            int 16h
t_KDjmp:    jmp t_KeyDone

;-------------------------------------------------

t_Rotate:   mov bx,t_XY             ;Get piece data
            mov cx,PieceW

            inc ch                  ;Rotate once
            and ch,3

t_RotLoop:  call Fits               ;Piece fits?
            jnc t_RotDone
            jz t_KeyDone            ;No problem?
            jl t_Rot0               ;Off left = move right
            dec bx                  ;Move left
            jmp t_RotLoop
t_Rot0:     inc bx                  ;Move right
            jmp t_RotLoop

t_RotDone:  mov t_XY,bx             ;Write out data
            mov PieceW,cx
            jmp t_KeyDone

;-------------------------------------------------

t_Down:     mov bp,DelayTime        ;BP = drop delay
            shr bp,1
            add bp,20

            mov bx,t_XY             ;Get piece data
            mov cx,PieceW
            call FlushKey           ;Flush key buffer

t_DownLoop: dec bh                  ;Fits below?
            call Fits
            jc t_Lock               ;If not, lock this piece
            inc t_Drop              ;Increment drop time
            mov t_XY,bx             ;Save position

            call t_PieceOn          ;Draw the piece

            mov ax,bp               ;Delay
            call Delay

            inc bp                  ;Reduce delay 10%
            imul bp,29
            shr bp,5

            mov ah,1                ;Key pressed?
            int 16h
            jz t_DownCont

            cmp ah,50h              ;Another Down key?
            jne t_KeyJmp

t_DownCont: call t_PieceOff         ;Erase the piece
            jmp t_DownLoop          ;Loop back

;-------------------------------------------------

t_KeyDone:  call t_PieceOn          ;Redraw the piece
t_KeyJmp:   jmp t_KeyLoop

t_NoKey:    cmp t_Ctr,0             ;Counter not zero?
            jne t_j1                ;If so, don't drop

            call t_PieceOff         ;Erase the piece

            dec bh                  ;Can it move down?
            call Fits
            jc t_Lock               ;Jump if not

            mov t_XY,bx             ;Save position

            call t_PieceOn          ;Redraw the piece
t_j1:       jmp t_MainLoop          ;Loop back

;-------------------------------------------------

t_Lock:     call t_PieceOn          ;Redraw the piece

            mov bx,t_XY             ;Lock this piece
            mov al,t_Drop
            call LockPiece

            mov t_Drop,0            ;Reset drop time

            mov bx,1400h            ;BX = (0, 20)
            mov cx,10               ;10 blocks

t_LockLoop: call GetBlock           ;Check block
            test ah,ah              ;Quit if not blank
            jnz t_Done
            inc bx                  ;Next square
            loop t_LockLoop         ;Loop back

            mov ax,NumPieces        ;Get random piece
            call Rand
            xchg al,NxPiece         ;Set next piece
            mov PieceW,ax           ;Set current piece

            mov bx,1505h            ;Set initial position
            mov t_XY,bx

            call ShowStatus         ;Update screen info
            call ShowNext

            call t_PieceOn          ;Draw the new piece
            jmp t_MainLoop          ;Loop back

t_Done:     mov ax,1000             ;Delay 1 second
            call Delay

            call GameOver           ;Display 'Game Over'

;-------------------------------------------------

            call AddScore           ;Add to list if high enough
            call StopInt            ;Stop interrupt handler
            jmp t_BigLoop           ;Go again

;-------------------------------------------------

t_XY        label word              ;Local variables
t_X         db 0                    ;X, Y position
t_Y         db 0
t_Ctr       db 0                    ;Cycle counter
t_Drop      db 0                    ;Drop distance

Tetris      EndP

;---------------------------------------------------------------------
; AddScore -- Add the score to the list if high enough, show the list
;---------------------------------------------------------------------

AddScore    Proc

            pusha                   ;Save all registers

            mov ax,Score[0]         ;DX:AX = score
            mov dx,Score[2]

            cmp dx,SC_Score[38]     ;Compare high word
            jb as_nope
            ja as_cont1

            cmp ax,SC_Score[36]     ;Compare low word
            ja as_cont1

as_nope:    mov ax,-1               ;Show scores
            call ShowScores

as_quit:    call FlushKey           ;Wait for key
            xor ah,ah
            int 16h

            popa                    ;Restore registers
            ret                     ;Return

;-------------------------------------------------

as_cont1:   mov bx,8                ;BX = score number

as_cloop:   imul si,bx,4            ;SI = pointer
            add si,O(SC_Score)

            cmp dx,[si+2]           ;Compare high word
            jb as_cdone
            ja as_copy1

            cmp ax,[si]             ;Compare low word
            jbe as_cdone

as_copy1:   mov si,bx               ;Copy score down
            mov di,bx
            inc di
            call CopyScore

            dec bx                  ;Next score
            jnl as_cloop            ;Loop back

;-------------------------------------------------

as_cdone:   inc bx                  ;BX = position

            imul si,bx,4            ;SI = pos * 4
            mov SC_Score[si],ax     ;Set score field
            mov SC_Score[si+2],dx

            shr si,1                ;SI = pos * 2
            mov ax,Lines            ;Set lines field
            mov SC_Lines[si],ax
            mov ax,Round            ;Set round field
            mov SC_Round[si],ax

            shl si,3                ;SI = ptr. to string
            add si,O(SC_Name)       
            xor ax,ax               ;Set name field to null string
            mov B [si],al

            xchg ax,bx              ;Show scores
            call ShowScores         ;BX now = 0

            call FlushKey           ;Flush keyboard

;-------------------------------------------------

            mov cx,0D0Bh            ;CX = position of string
            add ch,al

as_eloop:   pusha                   ;Save all registers

            mov dx,si               ;Get string length
            call StrLenF
            xchg dx,ax              ;DX = cursor position
            add dx,cx
            mov ah,2                ;Move cursor
            xor bh,bh
            int 10h

            mov bx,cx               ;Clear score area
            add cl,32
            mov al,20h
            call RectC

            mov dx,si               ;Output string
            call PutStrF

            popa                    ;Restore registers

;-------------------------------------------------

as_key:     xor ah,ah               ;Get a key
            int 16h

            test al,al              ;Zero, loop back
            jz as_key

            cmp al,08h              ;Backspace?
            je as_bksp
            cmp al,0Dh              ;Enter?
            je as_finish

            cmp al,20h              ;Space, hyphen OK
            je as_char
            cmp al,2Dh
            je as_char

            and al,0DFh             ;Make letters uppercase
            cmp al,'A'              ;Out of range?
            jb as_key
            cmp al,'Z'
            ja as_key

;-------------------------------------------------

as_char:    cmp bx,15               ;Out of space?
            jae as_key

            mov [si+bx],al          ;Add char to string
            inc bx
            mov B [si+bx],0
            jmp as_eloop            ;Loop back

;-------------------------------------------------

as_bksp:    test bx,bx              ;No chars?
            jz as_key

            dec bx                  ;Delete last char
            mov B [si+bx],0
            jmp as_eloop            ;Loop back

;-------------------------------------------------

as_finish:  mov ah,2                ;Put cursor off screen
            xor bh,bh
            mov dx,1900h
            int 10h

            jmp as_quit             ;Return

AddScore    EndP

;---------------------------------------------------------------------
; AdvanceLine -- Insert a line at the bottom of the well
;---------------------------------------------------------------------

AdvanceLine Proc

            pusha                   ;Save all registers

            mov dx,1500h            ;DX = initial pos.
            mov cx,10               ;10 columns

al_oloop:   mov bx,dx               ;BX = position
            lea ax,[bx+90]          ;AX = 360 + 4 X
            xor ah,ah               ; sound frequency
            shl ax,2

            call Sound              ;Set sound

al_iloop:   dec bh                  ;AX = (X, Y-1)
            call GetBlock
            inc bh                  ;(X, Y+1) = AX
            call PutBlock
            dec bh                  ;Next line
            jnz al_iloop            ;Loop back

            mov ax,4                ;3/4 probability
            call Rand               ;of a block here
            test ax,ax
            jz al_put

            mov ax,35               ;Get random number
            call Rand
            xchg si,ax              ;Convert to color
            mov ah,Colors[si]
            xor al,al               ;Type is 0
al_put:     call PutBlock           ;Put a block

            mov ax,15               ;Delay 15 msec.
            call Delay

            inc dx                  ;Next column
            loop al_oloop           ;Loop back

            call NoSound            ;Turn off sound
            popa                    ;Restore registers
            ret                     ;Return

AdvanceLine EndP

;---------------------------------------------------------------------
; Choose -- Display a menu and return the key pressed
;---------------------------------------------------------------------
; returns AX = selection, 0 thru 4 (0 = quit)

Choose      Proc

            pusha                   ;Save all registers

            mov bx,000Ah            ;Fill the well with
            mov cx,151Dh            ;a visible color
            mov ax,0F20h
            call RectB

ch_draw:    mov si,O(Menu)          ;SI = menu ptr.
            mov bx,030Bh            ;BX = position
            mov cx,M_CHOICES        ;Num. of choices

ch_loop:    lodsw                   ;DX = string ptr.
            xchg dx,ax
            call PutStrF            ;Display string
            add bh,2                ;Next line
            loop ch_loop            ;Loop back

ch_key:     xor ah,ah               ;Get a key
            int 16h

            cmp ah,1Fh              ;S, sound?
            jne ch_skip1

            xor SoundOn,17h         ;Flip sound flag
            jmp ch_draw             ;Redraw menu

ch_skip1:   cmp ah,22h              ;G, grid?
            jne ch_skip2

            xor GridOn,17h          ;Flip grid flag
            jmp ch_draw             ;Redraw menu

ch_skip2:   cmp ah,13h              ;R, round?
            jne ch_skip3

            mov al,StRound          ;AL = round
            and ax,0F7h             ;Increment round
            inc ax
            mov StRound,al          ;Set round
            jmp ch_draw             ;Redraw menu

ch_skip3:   dec ax                  ;'Q' = 51h --> 50h
            cmp ah,10h              ;Q, quit?
            je ch_cont

            sub al,'0'-1            ;Out of range?
            jbe ch_key
            cmp al,M_NUMBERS
            ja ch_key
     
ch_cont:    and ax,0Fh              ;Convert to number

            call ClearWell          ;Clear the well

            mov bp,sp               ;Set pushed AX
            mov [bp+14],ax
            popa                    ;Restore registers
            ret                     ;Return

Choose      EndP

;---------------------------------------------------------------------
; ClearBoxes -- Clear the score and count boxes
;---------------------------------------------------------------------

ClearBoxes  Proc

            pusha                   ;Save all registers

            mov bx,0D25h            ;Position of first score box
            mov cx,0D39h
            mov ax,8F20h
            mov dl,3                ;Increment is 3
            call cb_clear           ;Clear score boxes

            mov bx,0403h            ;Position of first count box
            mov cx,0505h
            mov ah,70h
            inc dx                  ;Increment is 4
            call cb_clear           ;Clear count boxes

            popa                    ;Restore registers
            ret                     ;Return

;-------------------------------------------------

cb_clear:   call RectB              ;Clear score/count boxes
            add bh,dl
            add ch,dl
            cmp ch,16h
            jle cb_clear
            ret                     ;Return

ClearBoxes  EndP

;---------------------------------------------------------------------
; ClearWell -- Clear the well
;---------------------------------------------------------------------

ClearWell   Proc

            pusha                   ;Save all registers

            mov bh,21               ;Initial position
            xor ax,ax               ;Cells are blank

cw_oloop:   mov bl,9                ;Start of row
cw_iloop:   call PutBlock           ;Clear this block
            dec bl                  ;Next block
            jnl cw_iloop            ;Loop back
            dec bh                  ;Next line
            jnl cw_oloop            ;Loop back

            popa                    ;Restore registers
            ret                     ;Return

ClearWell   EndP

;---------------------------------------------------------------------
; CopyScore -- Copy a score
;---------------------------------------------------------------------
; SI = source, DI = destination

CopyScore   Proc

            pusha                   ;Save all registers
            push es

            push ds                 ;ES = DS
            pop es

            add si,si               ;SI = src * 2
            add di,di               ;DI = dest * 2

            mov bx,O(SC_Lines)      ;Copy lines value
            mov ax,[bx+si]
            mov [bx+di],ax
            mov bx,O(SC_Round)      ;Copy round value
            mov ax,[bx+si]
            mov [bx+di],ax

            add si,si               ;SI = src * 4
            add di,di               ;DI = dest * 4

            mov bx,O(SC_Score)      ;Copy score value
            mov ax,[bx+si]
            mov [bx+di],ax
            mov ax,[bx+si+2]
            mov [bx+di+2],ax

            shl si,2                ;SI = src * 16
            shl di,2                ;DI = dest * 16
            mov bx,O(SC_Name)       ;BX = offset of names
            add si,bx               ;Adjust to offsets
            add di,bx
            mov cx,16               ;Copy name string
            rep movsb

            pop es                  ;Restore registers
            popa
            ret                     ;Return

CopyScore   EndP

;---------------------------------------------------------------------
; Crescendo -- Make a crescendo sound
;---------------------------------------------------------------------
; AX = start, BX = step, CX = count

Crescendo   Proc

            pusha                   ;Save all registers
            mov dx,20               ;DX = delay time

cr_loop:    call Sound              ;Set sound
            xchg ax,dx              ;Delay 20 msec.
            call Delay
            xchg ax,dx
            add ax,bx               ;Change tone
            loop cr_loop            ;Loop back

            call NoSound            ;Turn off speaker

            popa                    ;Restore registers
            ret                     ;Return

Crescendo   EndP

;---------------------------------------------------------------------
; CvtInt - Convert int to string
;---------------------------------------------------------------------
; AX = number, BX = string

CvtInt      Proc

            pusha                   ;Save all registers

            mov di,bx               ;DI = string pointer

            xor cx,cx               ;Zero CX
            mov si,10               ;SI = 10

ci_dloop:   xor dx,dx               ;Divide by 10
            div si
            add dl,30h              ;Convert to digit
            push dx                 ;Push digit
            inc cx
            test ax,ax              ;Loop back
            jnz ci_dloop

ci_ploop:   pop ax                  ;Pop digit
            mov [di],al             ;Store digit
            inc di
            loop ci_ploop           ;Loop back

            mov B [di],0            ;Add the null byte

            popa                    ;Restore registers
            ret                     ;Return

CvtInt      EndP

;---------------------------------------------------------------------
; CvtLong - Convert long to string
;---------------------------------------------------------------------
; DX:AX = number, BX = string

CvtLong     Proc

            pusha                   ;Save all registers

            mov di,bx               ;DI = string pointer

            xor cx,cx               ;Zero CX
            mov si,10               ;SI = 10

cl_loop:    xchg bx,ax              ;Divide DX:AX by SI
            xchg dx,ax              ;with remainder in BX
            xor dx,dx
            div si
            xchg bx,ax
            div si
            xchg dx,bx

            add bl,'0'              ;Convert to digit
            push bx                 ;Push digit
            inc cx
            mov bx,dx               ;Loop back
            or bx,ax
            jnz cl_loop

            jmp ci_ploop            ;Pop & store digits

CvtLong     EndP

;---------------------------------------------------------------------
; Decode -- Decode the file (from BufEnd) to the main buffer
;---------------------------------------------------------------------
; ES = DS

Decode      Proc

            pusha                   ;Save all registers

            mov si,O(BufEnd)        ;SI = in, DI = out
            mov di,O(Buffer)        ; for decoding the font

            xor cx,cx               ;Zero CX

de_loop1:   lodsb                   ;Get byte
            test al,al              ;Zero?
            jnz de_store

de_zero:    lodsb                   ;Load byte
            test al,al              ;Zero = quit
            jz de_cont

            xchg cl,al              ;CX = count, AL = 0

            rep                     ;Tricky...
de_store:   stosb                   ;Store byte
            jmp de_loop1            ;Loop back

de_cont:    mov cx,PIECE_DATA/2     ;CX = piece data size

de_loop2:   lodsb                   ;Get byte
            mov ah,al               ;Unpack to 2 bytes
            sar ax,4
            sar al,4
            stosw                   ;Store 2 bytes
            loop de_loop2           ;Loop back

            mov ax,si               ;Set score offset
            sub ax,O(BufEnd)
            mov ScoreOffs,ax

            mov cx,O(BufEnd)-O(SC_Score)
            rep movsb               ;Copy the high scores

            popa                    ;Restore registers
            ret                     ;Return

Decode      EndP

;---------------------------------------------------------------------
; Delay -- Millisecond resolution delay
;---------------------------------------------------------------------
; AX = time in milliseconds

Delay       Proc

            pusha                   ;Save all registers

            mov dx,1000             ;Multiply by 1000
            mul dx                  ;DX:AX = time in microseconds

            xchg ax,dx              ;CX:DX = time
            xchg ax,cx
            mov ah,86h              ;BIOS Delay Service
            int 15h

            popa                    ;Restore registers
            ret                     ;Return

Delay       EndP

;---------------------------------------------------------------------
; DelBlock -- Delete a block from the well, fix neighbors
;---------------------------------------------------------------------
; BL = X, BH = Y of block to delete

DelBlock    Proc

            pusha                   ;Save all registers

            call GetBlock           ;Can't delete steel blocks
            cmp ah,8Fh
            je db_done

            xor ax,ax               ;Kill this block
            call PutBlock

            dec bh                  ;Fix block below
            call GetBlock
            and al,0Dh
            call PutBlock

            add bh,2                ;Fix block above
            call GetBlock
            and al,07h
            call PutBlock

            dec bh                  ;Fix block at left
            dec bx
            call GetBlock
            and al,0Bh
            call PutBlock

            inc bx                  ;Fix block at right
            inc bx
            call GetBlock
            and al,0Eh
            call PutBlock

db_done:    popa                    ;Restore registers
            ret                     ;Return

DelBlock    EndP

;---------------------------------------------------------------------
; DelLine -- Delete a specific line from the well
;---------------------------------------------------------------------
; AX = line to delete

DelLine     Proc

            pusha                   ;Save all registers

            mov dh,al               ;DX = initial pos.
            xor dl,dl

            mov cx,10               ;10 columns

dl_oloop:   mov bx,dx               ;BX = position

            mov al,bh               ;AL = X + 4 Y
            shl al,2
            add al,bl
            add al,70               ;AX = 280 + 4 X + 16 Y
            xor ah,ah               ; sound frequency
            shl ax,2

            call Sound              ;Set sound

dl_iloop:   inc bh                  ;AX = (X, Y+1)
            call GetBlock
            dec bh                  ;(X, Y) = AX
            call PutBlock
            inc bh                  ;Next line
            cmp bh,22               ;Loop back
            jb dl_iloop

            mov bx,dx               ;Fix type on top
            call GetBlock
            and al,07h
            call PutBlock

            dec bh                  ;Fix type on bottom
            call GetBlock
            and al,0Dh
            call PutBlock

            mov ax,15               ;Delay 15 msec.
            call Delay

            inc dx                  ;Next column
            loop dl_oloop           ;Loop back

            call NoSound            ;Turn off sound

            popa                    ;Restore registers
            ret                     ;Return

DelLine     EndP

;---------------------------------------------------------------------
; DelLines -- Delete all completed lines from the well
;---------------------------------------------------------------------
; returns AX = num. of lines deleted

DelLines    Proc

            pusha                   ;Save all registers

            xor si,si               ;Zero SI
            mov ax,21               ;AX = top line

dls_loop:   call IsFull             ;Check for line
            jnc dls_skip
            call DelLine            ;Delete line
            inc si                  ;Increment counter
dls_skip:   dec ax                  ;Next line
            jnl dls_loop            ;Loop back

            mov bp,sp               ;Set pushed AX
            mov [bp+14],si
            popa                    ;Restore registers
            ret                     ;Return

DelLines    EndP

;---------------------------------------------------------------------
; Disp -- Display character and/or attribute
;---------------------------------------------------------------------
; BL = X, BH = Y, AL = char, AH = attr, displayed if nonzero

Disp        Proc

            pusha                   ;Save all registers

            xor cx,cx               ;Split X from Y
            xchg cl,bh
            imul di,cx,160          ;DI = offset
            add di,bx
            add di,bx

            test al,al              ;Set char if NZ
            jz $+4
            stosb
            dec di

            inc di                  ;Advance DI
            mov al,ah               ;AL = attr

            test al,al              ;Set attr if NZ
            jz $+3
            stosb

            popa                    ;Restore registers
            ret                     ;Return

Disp        EndP

;---------------------------------------------------------------------
; DrawBox - Draw an inset box using F8-FF charset
;---------------------------------------------------------------------
; BL = X1, BH = Y1, CL = X2, CH = Y2

DrawBox     Proc

            pusha                   ;Save all registers

            mov si,bx               ;Save BX, CX
            mov di,cx

            inc bh                  ;Draw left side
            dec ch
            push cx
            mov cl,bl
            mov ax,00F8h
            call RectC
            pop cx                  ;Draw right side
            mov bl,cl
            inc ax
            call RectC

            mov bx,si               ;Get old values
            mov cx,di
            inc bx                  ;Draw top side
            dec cx
            push cx
            mov ch,bh
            inc ax
            call RectC
            pop cx                  ;Draw bottom side
            mov bh,ch
            inc ax
            call RectC

            mov cx,si               ;CX = UL corner
            mov dx,di               ;DX = UR corner

            mov bx,cx               ;Draw UL corner
            inc ax
            call Disp
            mov bl,dl               ;Draw UR corner
            inc ax
            call Disp
            mov bx,cx               ;Draw LL corner
            mov bh,dh
            inc ax
            call Disp
            mov bx,dx               ;Draw LR corner
            inc ax
            call Disp

            popa                    ;Restore registers
            ret                     ;Return

DrawBox     EndP

;---------------------------------------------------------------------
; DrawTitle -- Draw title box
;---------------------------------------------------------------------
; BX = position of UL corner

DrawTitle   Proc

            pusha                   ;Save all registers

            mov cx,bx               ;Clear title area
            add cx,0312h
            mov ax,0F20h
            call RectB

            inc bx                  ;Display strings
            mov dx,O(Title$1)
            call PutStr
            inc bh
            mov dx,O(Title$2)
            call PutStr
            inc bh
            mov dx,O(Deluxe$)
            call PutStrF
            inc bh
            mov dx,O(Copyright$)
            call PutStr

            popa                    ;Restore registers
            ret                     ;Return

DrawTitle   EndP

;---------------------------------------------------------------------
; FindBlock -- Find a random position away from the piece
;---------------------------------------------------------------------
; DL = X, DH = Y of area to avoid
; returns BL = X, BH = Y, AH = color, AL = type

FindBlock   Proc

fb_loop:    mov ax,8                ;Generate random block
            call Rand
            inc ax
            mov bl,al
            mov ax,20
            call Rand
            mov bh,al

            mov al,dl               ;Compare to piece X
            sub al,bl
            add al,3
            cmp al,7
            jnb fb_done

            mov al,dh               ;Compare to piece Y
            sub al,bh               ;If it's within 3 of
            add al,3                ;both X and Y, skip it
            cmp al,7
            jb fb_loop

fb_done:    call GetBlock           ;AX = value of block
            ret                     ;Return

FindBlock   EndP

;---------------------------------------------------------------------
; Fits -- Check whether a piece fits
;---------------------------------------------------------------------
; BL = X, BH = Y, CL = piece, CH = rotation
; returns NC = fits, CY = doesn't fit:
; L = off left side, G = off right side, Z = hit something

Fits        Proc

            pusha                   ;Save all registers

            xor dx,dx               ;Separate piece data
            xchg dl,ch

            imul bp,dx,PIECE_SIZE   ;SI = piece offset
            imul si,cx,12*PIECE_SIZE
            lea si,Pieces[si+bp]

            mov cx,PIECE_SIZE       ;Num. of blocks

fi_loop:    push bx                 ;Save BX
            add bl,[si]             ;Add in offsets
            add bh,[si+4*PIECE_SIZE]
            call GetBlock           ;Check block
            pop bx                  ;Restore BX
            test ah,ah              ;Color nonzero?
            jnz fi_nope
            inc si                  ;Next block
            loop fi_loop            ;Loop back

fi_done:    popa                    ;Restore registers
            ret                     ;Return

fi_nope:    cmp ah,-2               ;Normal cases -> 0
            jne $+4                 ;Off left (-1) -> -1
            mov ah,1                ;Off right (-2) -> -2
            jae $+4
            xor ah,ah

            test ah,ah              ;Set flags
            stc                     ;Set carry flag
            jmp fi_done             ;Return

Fits        EndP

;---------------------------------------------------------------------
; FlushKey -- Flush the BIOS keyboard buffer
;---------------------------------------------------------------------

FlushKey    Proc

            push ds                 ;Save registers
            push ax

            push 40h                ;DS = BIOS segment
            pop ds

            mov ax,ds:[1Ch]         ;Set head = tail
            mov ds:[1Ah],ax

            pop ax                  ;Restore registers
            pop ds
            ret                     ;Return

FlushKey    EndP

;---------------------------------------------------------------------
; GameOver -- Draw the 'Game Over' box
;---------------------------------------------------------------------

GameOver    Proc

            pusha                   ;Save all registers

            mov bx,090Ch            ;Clear a rectangle
            mov cx,0E1Bh
            mov ax,0F20h
            call RectB
            mov bx,0A0Eh
            mov cx,0D19h
            mov ah,4Fh
            call RectB

            mov dx,O(GameOver$1)    ;DX = offset of line 1
            mov cx,4                ;4 lines

go_loop:    call PutStrF            ;Display this line
            inc bh                  ;Move to the next line
            add dx,OVER_LEN
            loop go_loop            ;Loop back

            call FlushKey           ;Wait for a key
            xor ah,ah
            int 16h

            popa                    ;Restore registers
            ret                     ;Return

GameOver    EndP

;---------------------------------------------------------------------
; GetBlock -- Return the status of a block in the well
;---------------------------------------------------------------------
; BL = X, BH = Y, returns AH = color, AL = type
; color: -1 = off left, -2 = off right, -3 = off bottom

GetBlock    Proc

            push bx                 ;Save registers
            push di

            xor ax,ax               ;Zero AX
            test bl,bl              ;Out of range?
            jl gb_dec1
            cmp bl,9
            jg gb_dec2
            test bh,bh
            jl gb_dec3
            cmp bh,21
            jg gb_done

            xchg al,bh              ;Separate X from Y
            imul di,ax,-160         ;DI = offset
            shl bx,2
            lea di,[di+bx+0D34h]

            mov ax,es:[di]          ;AX = value
            sub al,80h              ;Fix type
            shr al,1

            cmp al,10h              ;Patch for grid
            jne gb_done             ;??10h -> 0000h
            xor ax,ax

gb_done:    pop di                  ;Restore registers
            pop bx
            ret                     ;Return

gb_dec3:    dec ax                  ; -3
gb_dec2:    dec ax                  ; -2
gb_dec1:    dec ax                  ; -1
            xchg al,ah
            jmp gb_done

GetBlock    EndP

;---------------------------------------------------------------------
; Initialize -- Reset all variables, display initial screen
;---------------------------------------------------------------------

Initialize  Proc

            pusha                   ;Save all registers
            push es

            xor ax,ax               ;Init score, lines, etc.
            mov Score[0],ax
            mov Score[2],ax
            mov Lines,ax

            mov BonusCtr,ax         ;Init timer counters
            mov PalCtr,ax
            mov RectData,ax

            call InitScreen         ;Show the initial screen

            mov es,ax               ;ES = segment 0
            mov ax,es:[046Ch]       ;Get low word
            mov RandNum[0],ax       ;Save it
            mov ax,es:[046Eh]       ;Get high word
            mov RandNum[2],ax       ;Save it

            pop es                  ;Restore registers
            popa
            ret                     ;Return

Initialize  EndP

;---------------------------------------------------------------------
; InitScreen -- Display the initial screen
;---------------------------------------------------------------------

InitScreen  Proc

            pusha                   ;Save all registers

            xor bx,bx               ;Clear screen to 7/F dither
            mov cx,184Fh
            mov ax,7F60h
            call RectB
            mov ch,01h              ;Clear top 2 lines
            xor al,al
            call RectA
            call ClearWell          ;Draw the well

            mov bx,0300h            ;Draw vertical 3-D sides
            mov cx,1700h
            mov al,94h
            call RectC
            mov cx,151Eh
            mov bl,cl
            call RectC
            inc ax
            mov cx,174Fh
            mov bl,cl
            call RectC
            mov cx,1509h
            mov bl,cl
            call RectC

            mov bx,1801h            ;Draw horizontal 3-D sides
            mov cx,184Eh
            mov al,0FAh
            call RectC
            mov cx,0208h
            mov bh,ch
            inc ax
            call RectC
            mov bl,1Fh
            mov cl,4Eh
            call RectC

            mov bx,0209h            ;Draw 3-D corners
            mov ax,93h
            call Disp
            mov bl,4Fh
            call Disp
            mov bl,1Eh
            mov al,98h
            call Disp
            mov bl,00h
            call Disp
            mov bh,18h
            mov al,8Ch
            call Disp
            mov bl,4Fh
            mov al,87h
            call Disp

            mov bx,0338h            ;Draw title box
            mov cx,084Ch
            call DrawBox
            mov bx,0439h
            call DrawTitle
            mov TitleXY,bx         ;Set title area

;-------------------------------------------------

            mov bx,160Ah            ;Draw bonus box
            mov cx,161Dh
            mov al,8Ah
            call RectC
            mov bh,18h
            mov ch,bh
            call RectC
            mov bh,17h
            mov ch,bh
            mov ax,0E20h
            call RectB

            mov bx,1609h
            mov ax,9Fh
            call in_bdisp
            mov bx,1709h
            mov al,95h
            call in_bdisp
            mov bx,1809h
            mov al,8Fh
            call in_bdisp

;-------------------------------------------------

            mov bx,0302h            ;Position of first count box
            mov cx,0606h
            mov dl,4                ;Increment is 4
            call in_cloop           ;Draw outline of count boxes

            call ClearBoxes         ;Clear count boxes

;-------------------------------------------------

            mov bx,0321h            ;Draw next-piece box
            mov cx,0930h
            call DrawBox
            mov bx,0422h
            mov cx,082Fh
            mov ax,0080h
            call RectB

            mov bx,0B24h            ;Position of first score box
            mov cx,0E3Ah
            mov dl,3                ;Increment is 3
            call in_cloop           ;Draw outline of score boxes

            mov bx,0C25h            ;Position of first score box
            mov cx,0D39h
            mov ax,8A20h
in_sloop2:  call RectB              ;Fill inside of score boxes
            call in_next
            jle in_sloop2

            mov bx,0E25h            ;Position of first box divider
            mov cx,0E39h

in_sloop3:  pusha                   ;Save all registers
            mov ax,008Ah            ;Draw middle section
            call RectC
            dec bx                  ;Draw left end
            mov al,9Fh
            call Disp
            mov bx,cx               ;Draw right end
            inc bx   
            dec ax
            call Disp
            popa                    ;Restore registers

            call in_next            ;Next box
            jle in_sloop3           ;Loop back

;-------------------------------------------------

            mov bx,0C2Ah            ;Draw score headers
            mov dx,O(Round$)
            call PutStrF
            mov bx,0F27h
            mov dx,O(LinesLeft$)
            call PutStrF
            mov bx,122Ah
            mov dx,O(Score$)
            call PutStrF
            mov bx,152Bh
            mov dx,O(Lines$)
            call PutStrF

            popa                    ;Restore registers
            ret                     ;Return

;-------------------------------------------------

in_bdisp:   call Disp               ;Display both sides of part of
            mov bl,1Eh              ;the bonus box
            dec ax
            call Disp
            ret

;-------------------------------------------------

in_cloop:   call DrawBox            ;Draw outline of score/count boxes
            call in_next
            jle in_cloop
            ret                     ;Return

;-------------------------------------------------

in_next:    add bh,dl               ;Move to the next box
            add ch,dl
            cmp ch,16h
            ret

InitScreen  EndP

;---------------------------------------------------------------------
; IsEmpty -- Check whether a line is empty
;---------------------------------------------------------------------
; AX = line to check, returns CY = yes, NC = no

IsEmpty     Proc

            pusha                   ;Save all registers

            mov cx,10               ;10 blocks
            mov bh,al               ;BX = position
            xor bl,bl

ie_loop:    call GetBlock           ;Check block
            test ah,ah              ;Quit if set
            jnz ie_done
            inc bx                  ;Next square
            loop ie_loop            ;Loop back

            stc                     ;Set carry flag
ie_done:    popa                    ;Restore registers
            ret                     ;Return

IsEmpty     EndP

;---------------------------------------------------------------------
; IsFull -- Check whether a line is full
;---------------------------------------------------------------------
; AX = line to check, returns CY = yes, NC = no

IsFull      Proc

            pusha                   ;Save all registers

            mov cx,10               ;10 blocks
            mov bh,al               ;BX = position
            xor bl,bl

if_loop:    call GetBlock           ;Check block
            test ah,ah              ;Quit if blank
            jz if_done
            inc bx                  ;Next square
            loop if_loop            ;Loop back

            stc                     ;Set carry flag
if_done:    popa                    ;Restore registers
            ret                     ;Return

IsFull      EndP

;---------------------------------------------------------------------
; KillBlock -- Remove a random block
;---------------------------------------------------------------------
; BL = X, BH = Y of area to avoid

KillBlock   Proc

            pusha                   ;Save all registers

            mov cx,250              ;250 max. iters
            mov dx,bx               ;DX = position

kbk_loop:   call FindBlock          ;Generate random block
            test ah,ah              ;Block empty?
            jnz kbk_found

            loop kbk_loop           ;Loop back
            jmp kbk_done            ;No luck...

kbk_found:  call DelBlock           ;Delete this block

kbk_Done:   popa                    ;Restore registers
            ret                     ;Return

KillBlock   EndP

;---------------------------------------------------------------------
; LockPiece -- Lock a piece in the well
;---------------------------------------------------------------------
; AL = drop distance, BL = X position, BH = Y position

LockPiece   Proc

            pusha                   ;Save all registers

            mov bp,bx               ;BP = (X, Y)
            mov di,bx               ;SI = D
            shr di,8                ;DI = Y
            xor ah,ah
            xchg si,ax

            mov ax,40               ;AX = random freq.
            call Rand
            imul ax,10
            add ax,440

            mov bx,110              ;Make a noise
            mov cx,3
            call Crescendo

            imul ax,Round,10        ;AX = 10 * Round
            lea dx,[di+22]          ;DX = Y + 22
            mul dx                  ;Multiply
            lea dx,[si+22]          ;DX = 22 - Y + D
            sub dx,di
            mul dx                  ;Multiply

            imul bx,di,-22          ;BX = 22 * (22 - Y)
            add bx,22*22
            div bx                  ;AX = score for piece

            mov bx,9                ;Show for 1/2 second
            call ShowBonus

            add Score[0],ax         ;Add into score
            adc Score[2],0
            call ShowStatus         ;Update screen info

;-------------------------------------------------

            cmp Piece,ACID_PIECE    ;Special piece?
            je lp_acid
            cmp Piece,BOMB_PIECE
            jne lp_cont
            jmp lp_bomb

;-------------------------------------------------

lp_cont:    call DelLines           ;Delete all lines
            test ax,ax              ;No lines?
            jz lp_skip1
            push ax

            xchg bx,ax              ;AX = value of line
            add bx,bx
            mov ax,LineVals[bx]

            mov dx,Round            ;DX = multiplier
            inc dx
            shr dx,1
            mul dx                  ;AX = line score

            mov bx,170Ah            ;Show 'BONUS' string
            mov dx,O(Bonus$)
            call PutStrF

            mov bx,18               ;Show for 1 second
            call ShowBonus

            call lp_addin           ;Add into score
            pop ax                  ;Update line count
            add Lines,ax

lp_skip1:   call ShowStatus         ;Update screen info

            sub LinesLeft,ax        ;No lines left?
            jg lp_done

            call NextRound          ;Advance to the next round

lp_done:    call ShowStatus         ;Show status
            call FlushKey           ;Flush key buffer
            popa                    ;Restore registers
            ret                     ;Return

;-------------------------------------------------

lp_acid:    mov dx,bp               ;DX = position
            sub dl,2
            add dh,2
            xor bp,bp               ;Bonus = 0

lp_aloop:   mov bx,dx               ;Set up for inner loop
            mov cx,5

lp_ailoop:  call DelBlock           ;Delete this block

            mov ax,880              ;Make a random noise
            call Rand
            add ax,220
            call Sound

            mov ax,10               ;Delay 1/100 sec.
            call Delay

            inc bl                  ;Next block
            loop lp_ailoop          ;Loop back

            imul ax,Round,5         ;Add in bonus
            add bp,ax
            mov ax,bp
            mov bx,9                ;Display bonus
            call ShowBonus

            dec dh                  ;Next line
            jnl lp_aloop            ;Loop back

            call lp_addin           ;Add to score

            call NoSound            ;Turn off speaker
            jmp lp_cont             ;Continue

;-------------------------------------------------

lp_bomb:    mov dx,bp               ;DX = position
            sub dl,3
            add dh,3

            mov si,7                ;7 lines

lp_bloop:   mov bx,dx               ;Set up for inner loop
            mov cx,7

lp_biloop:  call DelBlock           ;Delete this block

            mov ax,150              ;Make a random noise
            call Rand
            add ax,100
            call Sound

            mov al,4                ;Delay 4 msec. (AH = 0)
            call Delay

            inc bl                  ;Next block
            loop lp_biloop          ;Loop back

            dec dh                  ;Next line
            dec si                  ;Loop back
            jnz lp_bloop

            imul ax,Round,150       ;Add in bonus
            call lp_addin

            call NoSound            ;Turn off speaker
            jmp lp_cont             ;Continue

;-------------------------------------------------

lp_addin:   add Score[0],ax         ;Add AX to score
            adc Score[2],0
            ret

LockPiece   EndP

;---------------------------------------------------------------------
; LowBonus -- Add bonus for low puzzle
;---------------------------------------------------------------------

LowBonus    Proc

            pusha                   ;Save all registers

            mov bx,000Ah            ;Clear top section
            mov cx,011Dh
            mov ax,0320h
            call RectB

            inc bx                  ;Display header
            mov dx,O(LowBonus$1)
            call PutStrF
            inc bh
            mov dx,O(LowBonus$2)
            call PutStrF

            mov ax,1250             ;Delay 1.25 seconds
            call Delay

            mov bx,170Ah            ;Show 'BONUS' string
            mov dx,O(Bonus$)
            call PutStrF

            xor bh,bh               ;Set color to white
            mov al,0Fh
            call RectA

            mov di,19               ;Start with top line
            xor si,si               ;Score = 0
            xor bp,bp               ;Increment = 0

;-------------------------------------------------

lb_loop:    mov ax,si               ;Display bonus
            xor bx,bx
            call ShowBonus

            imul ax,di,-20          ;Make a noise
            add ax,840
            mov bl,15
            mov cx,10
            call Crescendo

            mov ax,di               ;Is this line empty?
            call IsEmpty
            jnc lb_skip

;-------------------------------------------------

            mov bh,al               ;BX = position
            mov bl,0
            mov ax,3804h            ;Pulsating color
            call PutBlock           ;Put first block

            mov cl,8                ;8 blocks
            inc ax                  ;Adjust type

lb_bloop:   inc bx                  ;Put this block
            call PutBlock
            loop lb_bloop           ;Loop back

            inc bx                  ;Put last block
            mov al,01h
            call PutBlock

;-------------------------------------------------

            imul dx,Round,10        ;Increment bonus
            add bp,dx
            add si,bp

            dec di                  ;Next line
            jmp lb_loop             ;Loop back

;-------------------------------------------------

lb_skip:    add Score[0],si         ;Add to score
            adc Score[2],0

            mov ax,400              ;Delay 400 msec.
            call Delay

            mov ax,440              ;Make a noise
            mov bx,20
            mov cx,bx
            call Crescendo

            call ShowStatus         ;Show status
            mov BonusCtr,1          ;Reduce bonus timer

;-------------------------------------------------

            lea dx,[di+1]           ;DX = line
            xchg dl,dh

lb_dloop:   mov bx,dx               ;Set up for loop
            xor ax,ax
lb_diloop:  call PutBlock           ;Clear block
            inc bx                  ;Next block
            cmp bl,10               ;Loop back
            jb lb_diloop

            mov al,25               ;Delay 25 msec.
            call Delay

            inc dh                  ;Next line
            cmp dh,20               ;Loop back
            jb lb_dloop

            popa                    ;Restore registers
            ret                     ;Return

LowBonus    EndP

;---------------------------------------------------------------------
; NewInt1C -- Timer tick handler
;---------------------------------------------------------------------

NewInt1C    Proc

            pushf                   ;Simulate an interrupt
            db 09Ah                 ;CALL FAR opcode
OldInt1C    dw ?,?                  ;Old INT 1C handler

            pusha                   ;Save all registers
            push ds
            push es

            push cs                 ;DS = CS
            pop ds
            push 0B800h             ;ES = video memory
            pop es

            mov ax,PalCtr           ;AX = palette counter
            inc ax                  ;Increment modulo 8
            and al,07h
            mov PalCtr,ax           ;Save new value
            xchg bx,ax              ;BX = palette counter

            mov dx,03DAh            ;DX = IS1 port
ni_vrt:     in al,dx                ;Wait for VRT
            test al,8
            jz ni_vrt

            mov dl,0C0h             ;DX = AC port
            mov al,03h              ;Setting color 3
            out dx,al
            call ni_ret             ;Delay
            mov al,PalVals[bx]      ;Set color
            out dx,al
            call ni_ret             ;Delay
            mov al,20h              ;Restart screen
            out dx,al

;-------------------------------------------------

            mov ax,RectData         ;Get rectangle info

            inc ah                  ;Increment counter
            cmp ah,19               ;Check for wrap
            jb ni_skip
            xor ah,ah               ;Wrap to start

            inc ax                  ;Increment color modulo 6
            cmp al,6
            jb ni_skip
            xor al,al

ni_skip:    mov RectData,ax         ;Save rectangle info

            add al,9                ;AL = color
            mov bx,TitleXY          ;BX:CX = position
            add bl,ah
            mov cx,bx
            add ch,3

            call RectA              ;Draw attr. rectangle

;-------------------------------------------------

            cmp BonusCtr,0          ;Check bonus counter
            je ni_done
            dec BonusCtr            ;Decrement bonus counter
            jnz ni_done             ;Zero?

            mov bx,170Ah            ;Clear the bonus box
            mov cx,171Dh
            mov ax,0E20h
            call RectB

ni_done:    pop es                  ;Restore registers
            pop ds
            popa
            iret                    ;Interrupt return

ni_ret:     ret                     ;null proc. for delay

NewInt1C    EndP

;---------------------------------------------------------------------
; NextRound -- Advance to the next round
;---------------------------------------------------------------------

NextRound   Proc

            pusha                   ;Save all registers
            push es

            xor ax,ax               ;Show with value 0
            mov LinesLeft,ax
            call ShowStatus

            mov al,StRound          ;First round, no bonus
            sub al,B Round
            cmp al,31h
            je nr_skip

            call LowBonus           ;Do the bonus sequence

            call ClearWell          ;Clear the well

nr_skip:    mov bx,Round            ;BX = new round - 1
            inc Round               ;Next round

            mov al,RoundTimes[bx]   ;Increase speed (AH = 0 here)
            mov DelayTime,ax

            mov al,RoundLines[bx]   ;Set new LinesLeft
            mov LinesLeft,ax

;-------------------------------------------------

            mov bx,070Ah            ;Clear a rectangle
            mov cx,0B1Dh
            mov ax,0F20h
            call RectB

            push ds                 ;ES = DS
            pop es

            mov si,O(Round$)        ;Copy round string
            mov di,O(BufEnd)
            mov cx,6
            rep movsb

            mov ax,Round            ;Add the number
            mov bx,O(BufEnd)+6
            call CvtInt

            mov ax,LinesLeft        ;Convert LinesLeft
            mov bx,O(BufEnd)+10
            call CvtInt

            mov di,O(BufEnd)+12     ;DI = offset
            cmp ax,10               ;Decrement if 1 digit
            sbb di,0

            mov si,O(LinesX$)       ;Copy lines string
            mov cl,7
            rep movsb

            pop es                  ;Restore ES

            mov bx,080Dh            ;Display string 1
            mov dx,O(BufEnd)
            call PutStrF

            mov bh,0Ah              ;Display string 2
            mov dx,O(BufEnd)+10
            call PutStrF

            call FlushKey           ;Wait for a key
            xor ah,ah
            int 16h

;-------------------------------------------------

            call ClearWell          ;Clear the well

            mov bx,Round            ;BX = round type
            mov bl,RoundType[bx-1]
            and bx,3
            add bx,bx               ;SI = block list
            mov si,BlockLists[bx]

nr_bloop:   lodsw                   ;Load word
            xchg bx,ax              ;Draw this block
            mov ax,8F00h
            call PutBlock
            test bx,bx              ;Loop while not -1
            jnl nr_bloop

nr_done:    popa                    ;Restore registers
            ret                     ;Return

NextRound   EndP

;---------------------------------------------------------------------
; NoSound -- Turn off the speaker
;---------------------------------------------------------------------

NoSound     Proc

            push ax                 ;Save AX

            in al,61h               ;Turn off speaker
            and al,0FCh
            out 61h,al

            pop ax                  ;Restore AX
            ret                     ;Return

NoSound     EndP

;---------------------------------------------------------------------
; PutBlock -- Display a block in the well
;---------------------------------------------------------------------
; BL = X, BH = Y, AH = color, AL = type

PutBlock    Proc

            pusha                   ;Save all registers

            cmp bl,10               ;Out of range?
            jae pb_done
            cmp bh,22
            jae pb_done

            and al,0Fh              ;Mask type to 0-15

            cmp GridOn,'Y'          ;If the grid is on and
            jne pb_cont             ;this piece is empty,
            test ah,ah              ;make it a grid block
            jnz pb_cont

            mov ax,0810h            ;Dark gray, grid char

pb_cont:    xor cx,cx               ;Separate X from Y
            xchg cl,bh

            imul di,cx,-160         ;DI = offset
            shl bx,2
            lea di,[di+bx+0D34h]

            add al,al               ;AL = char
            add al,80h
            stosw                   ;Set first char
            inc ax
            stosw                   ;Set second char

pb_done:    popa                    ;Restore registers
            ret                     ;Return

PutBlock    EndP

;---------------------------------------------------------------------
; PutPiece -- Display a Tetris piece in the well
;---------------------------------------------------------------------
; BL = X, BH = Y, CL = piece, CH = rotation, AL = color

PutPiece    Proc

            pusha                   ;Save all registers

            xor dx,dx               ;Separate piece data
            xchg dl,ch

            imul bp,dx,PIECE_SIZE   ;SI = piece offset
            imul si,cx,12*PIECE_SIZE
            lea si,Pieces[si+bp]

            mov cx,PIECE_SIZE       ;Num. of blocks
            mov ah,al               ;AH = color

pp_loop:    push bx                 ;Save BX
            add bl,[si]             ;Add in offsets
            add bh,[si+4*PIECE_SIZE]
            mov al,[si+8*PIECE_SIZE];AL = type
            call PutBlock           ;Display block
            pop bx                  ;Restore BX
            inc si                  ;Next block
            loop pp_loop            ;Loop back

            popa                    ;Restore registers
            ret                     ;Return

PutPiece    EndP

;---------------------------------------------------------------------
; PutStr -- Display normal string
;---------------------------------------------------------------------
; BL = X, BH = Y, DX = ptr. to ASCIIZ string

PutStr      Proc

            pusha                   ;Save all registers

            mov si,dx               ;SI = string
            xor cx,cx               ;Split X from Y
            xchg cl,bh
            imul di,cx,160          ;DI = offset
            add di,bx
            add di,bx

ps_Loop:    lodsb                   ;Load first byte
            test al,al              ;Found the null?
            jz ps_Done

            stosb                   ;Write to screen
            inc di
            jmp ps_Loop             ;Loop back

ps_Done:    popa                    ;Restore registers
            ret                     ;Return

PutStr      EndP

;---------------------------------------------------------------------
; PutStrF -- Display fat string
;---------------------------------------------------------------------
; BL = X, BH = Y, DX = ptr. to ASCIIZ string

PutStrF     Proc

            pusha                   ;Save all registers

            mov si,dx               ;SI = string
            xor cx,cx               ;Split X from Y
            xchg cl,bh
            imul di,cx,160          ;DI = offset
            add di,bx
            add di,bx
            mov bx,O(BigChars)-20h  ;BX = table

pf_Loop:    lodsb                   ;Load first byte
            test al,al              ;Found the null?
            jz pf_Done

            xlat                    ;Get font char
            stosb                   ;Write to screen
            inc di

            inc al                  ;Get second half
            jns pf_Loop             ;No second char?
            stosb                   ;Write to screen
            inc di

            jmp pf_Loop             ;Loop back

pf_Done:    popa                    ;Restore registers
            ret                     ;Return

PutStrF     EndP

;---------------------------------------------------------------------
; Rand -- Generate random number
;---------------------------------------------------------------------
; AX (N) = range, returns AX = random number below N

Rand        Proc

            push cx                 ;Save registers
            push dx

            push ax                 ;Save AX
            mov ax,RandNum[0]       ;DX:AX = RandNum * 0019660Dh + 10DCDh
            mov dx,660Dh            ;low * low
            mul dx
            imul cx,RandNum[0],0019h;low * high, high * low
            add dx,cx               
            imul cx,RandNum[2],660Dh
            add dx,cx
            add ax,0DCDh            ;add in the 10DCDh
            adc dx,0001h

            mov RandNum[0],ax       ;Save random number
            mov RandNum[2],dx

            xchg ax,dx              ;AX = high word
            xor dx,dx               ;Zero DX
            pop cx                  ;CX = range
            div cx                  ;Divide by range
            xchg ax,dx              ;Result in AX

            pop dx                  ;Restore registers
            pop cx
            ret                     ;Return

Rand        EndP

;---------------------------------------------------------------------
; RandBlock -- Add a random block
;---------------------------------------------------------------------
; BL = X, BH = Y of area to avoid

RandBlock   Proc

            pusha                   ;Save all registers

            mov cx,500              ;500 max. iters
            mov dx,bx               ;DX = position

rbk_loop:   call FindBlock          ;Generate random block
            test ah,ah              ;Block not empty?
            jnz rbk_lb

            dec bx                  ;Check (X-1, Y)
            call GetBlock
            inc bx
            test ah,ah
            jnz rbk_found

            inc bx                  ;Check (X+1, Y)
            call GetBlock
            dec bx
            test ah,ah
            jnz rbk_found

            dec bh                  ;Check (X, Y-1)
            call GetBlock
            inc bh
            test ah,ah
            jnz rbk_found

            inc bh                  ;Check (X, Y+1)
            call GetBlock
            dec bh
            test ah,ah
            jnz rbk_found

rbk_lb:     loop rbk_loop           ;Loop back
            jmp rbk_done            ;No luck...

rbk_found:  mov ax,3800h            ;Add this block
            call PutBlock

rbk_done:   popa                    ;Restore registers
            ret                     ;Return

RandBlock   EndP

;---------------------------------------------------------------------
; RectA -- Rectangle of attributes
;---------------------------------------------------------------------
; BL = X1, BH = Y1, CL = X2, CH = Y2, AL = attr

RectA       Proc

            pusha                   ;Save all registers

            xor dx,dx               ;Split X1 from Y1
            xchg dl,bh
            imul bp,dx,160          ;BP = Y offset
            inc bp

            jmp RectX               ;Draw the rectangle

RectA       EndP

;---------------------------------------------------------------------
; RectB -- Rectangle of both chars and attrs
;---------------------------------------------------------------------
; BL = X1, BH = Y1, CL = X2, CH = Y2, AL = char, AH = attr

RectB       Proc

            pusha                   ;Save all registers

            xor dx,dx               ;Split X1 from Y1
            xchg dl,bh
            imul bp,dx,160          ;BP = Y offset

            call RectY              ;Calculate offset, distances

rb_loop:    mov di,bp               ;DI = offset
            mov cx,bx               ;CX = X size
            rep stosw               ;Write to screen

            add bp,160              ;Next line
            dec dx                  ;Loop back
            jnl rb_loop

            popa                    ;Restore registers
            ret                     ;Return

RectB       EndP

;---------------------------------------------------------------------
; RectC -- Rectangle of characters
;---------------------------------------------------------------------
; BL = X1, BH = Y1, CL = X2, CH = Y2, AL = char

RectC       Proc

            pusha                   ;Save all registers

            xor dx,dx               ;Split X1 from Y1
            xchg dl,bh
            imul bp,dx,160          ;BP = Y offset

            jmp RectX               ;Draw the rectangle

RectC       EndP

;---------------------------------------------------------------------
; RectX -- Drawing tail for RectA, RectC
;---------------------------------------------------------------------
; BX = X1, DX = Y1, CL = X2, CH = Y2, BP = Y offset, AL = data byte

RectX       Proc

            call RectY              ;Calculate offset, distances

rx_loop:    mov di,bp               ;DI = offset
            mov cx,bx               ;CX = X size

rx_iloop:   stosb                   ;Write to screen
            inc di
            loop rx_iloop           ;Loop back

            add bp,160              ;Next line
            dec dx                  ;Loop back
            jnl rx_loop

            popa                    ;Restore registers
            ret                     ;Return

RectX       EndP

;---------------------------------------------------------------------
; RectY -- Offset generator for RectB, RectX
;---------------------------------------------------------------------
; BX = X1, DX = Y1, CL = X2, CH = Y2, BP = Y offset, AL = data byte
; Returns DX = Ydist-1, BX = Xdist, BP = final offset, AL = data byte

RectY       Proc

            add bp,bx               ;BP = final offset
            add bp,bx

            push ax                 ;Save AX
            xor ax,ax               ;Split X2 from Y2
            xchg al,ch

            sub cx,bx               ;BX = X distance
            mov bx,cx
            inc bx
            sub ax,dx               ;DX = Y distance
            xchg dx,ax

            pop ax                  ;Get data byte
            ret                     ;Return

RectY       EndP

;---------------------------------------------------------------------
; ShowBonus -- Display a bonus value
;---------------------------------------------------------------------
; AX = value, BX = display time

ShowBonus   Proc

            pusha                   ;Save all registers

            push bx                 ;Save BX
            mov bx,O(BufEnd)        ;Convert to string
            call CvtInt
            mov dx,bx               ;Get length
            call StrLenF
            mov bx,171Eh            ;Display in bonus box
            sub bl,al
            call PutStrF

            mov cx,171Dh            ;Set color to white
            mov al,0Fh
            call RectA

            pop BonusCtr            ;Set bonus time
            popa                    ;Restore registers
            ret                     ;Return

ShowBonus   EndP

;---------------------------------------------------------------------
; ShowNext -- Show next piece
;---------------------------------------------------------------------

ShowNext    Proc

            pusha                   ;Save all registers

            mov bx,0422h            ;Clear next-piece box
            mov cx,082Fh
            mov ax,0080h
            call RectB

            mov bl,NxPiece          ;BX = piece
            xor bh,bh

            imul si,bx,12*PIECE_SIZE;SI = piece offset
            add si,O(Pieces)

            mov ah,Colors[bx]       ;BP = piece color
            xchg bp,ax
            mov cx,PIECE_SIZE       ;Num. of blocks

sn_loop:    mov al,[si]             ;BX = X value
            cbw
            xchg bx,ax
            mov al,[si+4*PIECE_SIZE];AX = Y value
            cbw

            imul di,ax,-160         ;DI = offset
            shl bx,2
            lea di,[di+bx+0410h]

            mov ax,bp               ;AH = color
            mov al,[si+8*PIECE_SIZE];AL = type
            and al,0Fh              ;Mask to 0-15
            add al,al               ;Convert to char
            add al,80h

            stosw                   ;Write to screen
            inc ax
            stosw

            inc si                  ;Next block
            loop sn_loop            ;Loop back

            popa                    ;Restore registers
            ret                     ;Return

ShowNext    EndP

;---------------------------------------------------------------------
; ShowScores -- Display the high scores
;---------------------------------------------------------------------
; AX = score to highlight (-1 for none)

ShowScores  Proc

            pusha                   ;Save all registers

            push ax                 ;Save AX
            call StopInt            ;Stop interrupt handler

            xor bx,bx               ;Clear the screen
            mov cx,184Fh
            mov ax,7F60h
            call RectB

            mov bx,011Eh            ;Draw title box
            mov cx,0632h
            call DrawBox

            mov bx,021Fh
            call DrawTitle
            mov TitleXY,bx          ;Set title area
            mov RectCtr,0

            call StartInt           ;Start interrupt handler

            mov bx,0100h            ;Draw vertical 3-D sides
            mov cx,1700h
            mov al,94h
            call RectC
            mov bl,4Fh
            mov cl,4Fh
            inc ax
            call RectC

            mov bx,0001h            ;Draw horizontal 3-D sides
            mov cx,004Eh
            mov al,0FBh
            call RectC
            mov bh,18h
            mov ch,4Eh
            dec ax
            call RectC

            xor bx,bx               ;Draw 3-D corners
            mov al,98h
            call Disp
            mov bl,4Fh
            mov al,93h
            call Disp
            mov bh,18h
            mov al,87h
            call Disp
            mov bl,00h
            mov al,8Ch
            call Disp

            mov bx,0717h            ;Draw 'High Scores' box
            mov cx,0938h
            call DrawBox
            mov bx,0818h
            mov cx,0837h
            mov ax,0E20h
            call RectB
            inc bx                  ;Display string
            mov dx,O(HighScores$)
            call PutStrF

            mov bx,0A03h            ;Draw main box
            mov cx,174Ch
            call DrawBox
            mov bx,0B04h
            mov cx,164Bh
            mov ax,0A20h
            call RectB
            mov ch,0Bh
            mov al,0Fh
            call RectA

;-------------------------------------------------

            mov dx,O(Name$)         ;Display header strings
            mov bx,0B0Ch
            call PutStr
            mov dx,O(Score$)
            mov bl,34h
            call PutStr
            mov dx,O(Lines$)
            mov bl,3Eh
            call PutStr
            mov dx,O(Round$)
            mov bl,46h
            call PutStr

;-------------------------------------------------

            mov cx,9                ;10 lines

sc_loop:    mov ax,cx               ;Put line number in buffer
            inc ax
            mov bx,O(BufEnd)
            call CvtInt

            mov ax,002Eh            ;Put period at end
            cmp cx,9
            sbb bx,-1
            mov [bx+1],ax

            mov bh,cl               ;Output string
            add bh,13
            mov bl,5
            mov dx,O(BufEnd)
            call PutStrF

            imul dx,cx,16           ;DX = name string
            add dx,O(SC_Name)

            mov bl,11               ;Output string
            call PutStrF

;-------------------------------------------------

            imul si,cx,4            ;Get score
            mov ax,SC_Score[si]
            mov dx,SC_Score[si+2]
            mov bx,O(BufEnd)        ;Convert score
            call CvtLong
            mov dx,bx
            call StrLenF
            mov bl,57               ;Display score
            call sc_disp

            shr si,1                ;Get lines
            mov ax,SC_Lines[si]
            call sc_cvt             ;Convert lines
            mov bl,67               ;Display lines
            call sc_disp

            mov ax,SC_Round[si]     ;Get round
            call sc_cvt             ;Convert round
            mov bl,75               ;Display round
            call sc_disp

;-------------------------------------------------

            dec cx                  ;Next line
            jnl sc_loop             ;Loop back

            pop ax                  ;Get highlight
            inc ax                  ;-1 = none
            jz sc_done

            mov bx,0C04h            ;BX, CX = line
            add bh,al
            mov ch,bh
            mov cl,4Bh
            mov al,0Eh              ;Turn it yellow
            call RectA

sc_done:    popa                    ;Restore registers
            ret                     ;Return

;-------------------------------------------------

sc_cvt:     mov bx,O(BufEnd)        ;Convert to string
            call CvtInt
            mov dx,bx               ;Get length of string
            call StrLenF
            ret                     ;Return

sc_disp:    mov bh,cl               ;Get position
            add bh,13
            sub bl,al
            call PutStrF            ;Display string
            ret                     ;Return

ShowScores  EndP

;---------------------------------------------------------------------
; ShowStatus -- Display score, round, etc.
;---------------------------------------------------------------------

ShowStatus  Proc

            pusha                   ;Save all registers

            call ClearBoxes         ;Clear score, count boxes

;-------------------------------------------------

            mov ax,Round            ;Convert 'Round'
            mov bx,O(BufEnd)
            call CvtInt
            mov dx,bx               ;DX = string
            mov bx,0D2Fh            ;Display 'Round'
            call ss_disp

            mov ax,LinesLeft        ;Convert 'Lines Left'
            mov bx,O(BufEnd)
            call CvtInt
            mov dx,bx               ;DX = string
            mov bx,102Fh            ;Display 'Lines Left'
            call ss_disp

            mov ax,Score[0]         ;Convert 'Score'
            mov dx,Score[2]
            mov bx,O(BufEnd)
            call CvtLong
            mov dx,bx               ;DX = string
            mov bx,132Fh            ;Display 'Score'
            call ss_disp

            mov ax,Lines            ;Convert 'Lines'
            mov bx,O(BufEnd)
            call CvtInt
            mov dx,bx               ;DX = string
            mov bx,162Fh            ;Display 'Lines'
            call ss_disp

;-------------------------------------------------

            mov ax,LinesLeft        ;LinesLeft < 5?
            cmp ax,5
            ja ss_done
            test ax,ax
            jz ss_done

            mov ah,0Ch              ;AH = red on black color
            mov bl,al               ;AL = 3*num
            add al,al
            add al,bl

            mov bh,6                ;BX = position
            sub bh,bl
            shl bh,2
            mov bl,3

            add al,1Bh              ;Draw top half
            call Disp
            inc ax
            inc bx
            call Disp
            inc ax
            inc bx
            call Disp

            add bx,00FEh            ;Go to LL corner
            add al,0C8h             ;Draw bottom half
            call Disp
            inc ax
            inc bx
            call Disp
            inc ax
            inc bx
            call Disp

ss_done:    popa                    ;Restore registers
            ret                     ;Return

;-------------------------------------------------

ss_disp:    call StrLenF            ;Center the string
            shr al,1
            sub bl,al
            call PutStrF            ;Display the string
            ret                     ;Return

ShowStatus  EndP

;---------------------------------------------------------------------
; Sound -- Sound the speaker
;---------------------------------------------------------------------
; AX = frequency

Sound       Proc

            pusha                   ;Save registers
            cmp SoundOn,'Y'         ;Sound off?
            jne sd_done

            mov dx,12h              ;BX = 1193180 / freq.
            mov bx,34DCh
            xchg bx,ax
            div bx
            xchg bx,ax

            mov al,0B6h             ;Set frequency
            out 43h,al
            xchg ax,bx
            out 42h,al
            mov al,ah
            out 42h,al

            in al,61h               ;Turn on speaker
            or al,3
            out 61h,al

sd_done:    popa                    ;Restore registers
            ret                     ;Return

Sound       EndP

;---------------------------------------------------------------------
; StartInt -- Start the interrupt handler
;---------------------------------------------------------------------

StartInt    Proc

            pusha                   ;Save all registers
            push es

            mov ax,351Ch            ;Save old Int 1C
            int 21h
            mov OldInt1C[0],bx
            mov OldInt1C[2],es

            mov ah,25h              ;Set new Int 1C
            mov dx,O(NewInt1C)
            int 21h

            pop es                  ;Restore registers
            popa
            ret                     ;Return

StartInt    EndP

;---------------------------------------------------------------------
; StopInt -- Stop the interrupt handler
;---------------------------------------------------------------------

StopInt     Proc

            pusha                   ;Save all registers
            push ds

            mov ax,251Ch            ;Restore old Int 1C
            lds dx,D OldInt1C
            int 21h

            pop ds                  ;Restore registers
            popa
            ret                     ;Return

StopInt     EndP

;---------------------------------------------------------------------
; StrLenF -- Get length of fat string
;---------------------------------------------------------------------
; DX = string, returns AX = length

StrLenF     Proc

            push bx                 ;Save registers
            push dx
            push si

            mov si,dx               ;SI = string
            xor dx,dx               ;Zero DX
            mov bx,O(BigChars)-20h  ;BX = char table

sf_loop:    lodsb                   ;Get byte
            test al,al              ;Check for null
            jz sf_done

            xlat                    ;16-bit char?
            cmp al,80h              ;Set carry if < 80h
            sbb dx,-2               ;8-bit if < 80h, 16-bit otherwise
            jmp sf_loop             ;Loop back

sf_done:    xchg ax,dx              ;AX = length

            pop si                  ;Restore registers
            pop dx
            pop bx
            ret                     ;Return

StrLenF     EndP


;#####################################################################
; UNINITIALIZED DATA SEGMENT
;#####################################################################

.Data?

;---------------------------------------------------------------------

FileName    db 100 dup(?)           ;Filename buffer

Buffer:                             ;Buffer for TETRIS.DAT

Font        db 3584 dup(?)          ;Font data

Pieces      db PIECE_DATA dup(?)    ;Piece data

SC_Score    dw 20 dup(?)            ;High-score data
SC_Lines    dw 10 dup(?)
SC_Round    dw 10 dup(?)
SC_Name     db 160 dup(?)

BufEnd:

End Main
[ RETURN TO DIRECTORY ]