;****************** TETRIS.ASM -- TinyTetris v1.3 ; This is a Tetris game written in ASM. Ideal Jumps LEFT = 4Bh ;Key values RIGHT = 4Dh DOWN = 50h SPACE = 39h ENTERK = 1Ch ESCAPE = 01h BKSPACE = 0Eh F10 = 44h Model Tiny P186 CodeSeg Org 100h Start: jmp Main ;**************************** Static data for program CustomFont db 255,255,6 dup(213,234),192,128 db 255,254,6 dup(84,168),0,0 PieceStart dw offset Piece1,offset Piece2 dw offset Piece3,offset Piece4 dw offset Piece5,offset Piece6 dw offset Piece7 db 1 ;AND mask Piece1 db -1, 0, 1, 0, 2, 0 ; ÜÜÜÜ db 0,-1, 0, 1, 0, 2 db 3 ;AND mask Piece2 db 1,-1, 1, 0, -1, 0 ; ÛÜÜ db 1, 1, 0, 1, 0,-1 db -1, 1, -1, 0, 1, 0 db -1,-1, 0,-1, 0, 1 db 3 ;AND mask Piece3 db -1,-1, -1, 0, 1, 0 ; ÜÜÛ db 1,-1, 0,-1, 0, 1 db 1, 1, 1, 0, -1, 0 db -1, 1, 0, 1, 0,-1 db 3 ;AND mask Piece4 db 1, 0, 0,-1, -1, 0 ; ÜÛÜ db 0, 1, 1, 0, 0,-1 db -1, 0, 0, 1, 1, 0 db 0,-1, -1, 0, 0, 1 db 1 ;AND mask Piece5 db -1, 0, 0,-1, 1,-1 ; ÜÛß db 0, 1, -1, 0, -1,-1 db 1 ;AND mask Piece6 db 1, 0, 0,-1, -1,-1 ; ßÛÜ db 0,-1, -1, 0, -1, 1 db 0 ;AND mask Piece7 db 0,-1, -1,-1, -1, 0 ; ÛÛ TitleStr db '--- TinyTetris v1.3 ---',0 PlayAgain db 'Do you want to play again (Y/N) ?',0 YMHS db 'You made a High Score!',0 NameStr db 'Enter your name:',0 HiStr db 'TinyTetris High Scores',0 HST1 db 'Name',0 HST2 db 'Score Lines Level',0 FileName db 'TETRIS.SCO',0 OverStr db ' G A M E O V E R ',0 ScoreStr db 'Score:',0 LevelStr db 'Level:',0 LinesStr db 'Lines:',0 LeftStr db 'Lines Left:',0 ;**************************** Main program Proc Main mov ax,3 ;Set text mode int 10h mov ax,1114h ;Set 8x16 font int 10h mov ax,1110h ;Set font chars CE, CF to mov bx,1000h ;the 'block' image mov cx,2 mov dx,0CEh mov bp,offset CustomFont int 10h mov dx,03CCh ;Set 8 dot chars in al,dx and al,0F3h mov dx,03C2h out dx,al mov dx,03C4h mov ax,0101h out dx,ax mov ah,2 ;Turn off the cursor by xor bh,bh ;placing it off the screen mov dx,1E00h int 10h cld mov di,offset HiScores ;Zero out high scores mov cx,580 xor ax,ax rep stosw mov di,offset Spaces10 ;Set up space buffer mov cx,10 mov al,20h rep stosb xor al,al stosb mov cx,18 ;Set up top/bottom lines mov al,0DFh rep stosb xor al,al stosb mov cx,18 mov al,0DCh rep stosb xor ax,ax stosb push ax ;ES = 0 pop es mov ax,[es:046Ch] ;Seed random number with mov [RandNum],ax ;the BIOS time counter mov ax,[es:046Eh] mov [RandNum+2],ax push 0B800h ;ES = 0B800h (text video memory) pop es mov ax,3D00h ;Open scorefile mov dx,offset FileName int 21h jc GameLoop ;No scorefile, ignore xchg bx,ax ;Read hiscores mov ah,3Fh mov cx,1160 mov dx,offset HiScores int 21h mov ah,3Eh ;Close scorefile int 21h GameLoop: call Tetris ;Play game call AddHiScore ;Add in score, show scores call ClearScreen ;Clear screen mov cx,22 ;Print 'Play again?' string mov dx,12 ;in the middle of the screen mov si,offset PlayAgain mov al,9Fh ;Color = blinking white on blue call PutStr call FlushBuffer ;Flush key buffer PKeyLoop: xor ax,ax ;Get a key int 16h cmp ah,15h ;If it's a 'Y', then play again je GameLoop cmp ah,31h ;If it's an 'N', then quit jne PKeyLoop GameDone: mov ah,3Ch ;Create/truncate scorefile xor cx,cx mov dx,offset FileName int 21h xchg bx,ax ;Write hiscores mov ah,40h mov cx,1160 mov dx,offset HiScores int 21h mov ah,3Eh ;Close scorefile int 21h mov ax,3 ;Set text mode, restore font int 10h ret ;Exit EndP Main ;**************************** Tetris -- This is the actual game Proc Tetris pusha ;Save all registers ;****************** TETRIS Screen Setup call ClearScreen ;Clear the screen mov di,18 ;Start at (9, 0) mov cx,25 ;25 lines mov ax,7FB1h ;Color and character WellLoop: stosw ;Left side of well mov [word es:di+40],ax ;Right side add di,158 ;Next line loop WellLoop ;Loop back mov di,1668 ;Start at (34, 10) mov cx,9 ;9 lines mov al,0DBh ;AL = character SBoxLoop: stosb ;Left side of status box mov [es:di+33],al ;Right side add di,159 ;Next line loop SBoxLoop ;Loop back mov di,708 ;Start at (34, 4) mov cx,5 ;5 lines NBoxLoop: stosb ;Left side of next piece box mov [es:di+25],al ;Right side add di,159 ;Next line loop NBoxLoop ;Loop back mov cx,38 ;Print title string xor dx,dx mov si,offset TitleStr mov al,07h call PutStr mov cx,35 ;Print S-box top mov dx,10 mov si,offset BoxTop call PutStr mov dx,18 ;Print S-box bottom mov si,offset BoxBottom call PutStr mov cx,35 ;Print N-box top mov dx,4 mov si,offset BoxTop+4 call PutStr dec cx ;Print N-box bottom mov dx,9 dec si dec si call PutStr inc cx ;Print initial lines left inc cx mov dx,17 mov si,offset LeftStr call PutStr dec dx ;Print initial lines dec dx mov si,offset LinesStr call PutStr dec dx ;Print initial level dec dx mov si,offset LevelStr call PutStr dec dx ;Print initial score dec dx mov si,offset ScoreStr call PutStr ;****************** TETRIS Initialization xor ax,ax ;Initialize variables mov [Score],ax mov [Score+2],ax mov [Level],1 mov [Lines],ax mov [LinesLeft],5 mov [DelayTime],750 mov [Rotate],ax mov [X],4 mov [Y],24 call Rand7 mov [Piece],ax call Rand7 mov [NxPiece],ax ;****************** TETRIS Main Loop MainLoop: call ShowStatus ;Show status mov ax,[DelayTime] ;Delay specified time call Delay inc bp ;Ctr = (Ctr + 1) mod 4 and bp,3 ;****************** TETRIS Key Loop KeyLoop: mov ah,1 ;Check for keys int 16h jz NoKeys call LoadVals ;Erase current piece xor di,di call PutPiece xor ax,ax ;Get the key int 16h cmp ah,LEFT ;Left arrow? je KeyLeft cmp ah,RIGHT ;Right arrow? je KeyRight cmp ah,DOWN ;Down arrow? je KeyDown cmp ah,ENTERK ;Enter? je KeyDown cmp ah,SPACE ;Space? je KeySpace cmp ah,ESCAPE ;Escape? je KeyEsc cmp ah,F10 ;F10? je KeyF10 jmp KeyDone ;Not a recognized key KeyLeft: call LoadVals ;If it fits at (X - 1), dec cx call Fits jnc KeyDone mov [X],cx ;move it to (X - 1). jmp KeyDone KeyRight: call LoadVals ;If it fits at (X + 1), inc cx call Fits jnc KeyDone mov [X],cx ;move it to (X + 1). jmp KeyDone KeyDown: call LoadVals ;Load values mov si,dx ;Save old Y DownLoop: dec dx ;While it fits at (Y-1), call Fits ;decrement Y. jc DownLoop inc dx ;Move to where it last fit mov [Y],dx ;Save it in Y call PutPiece ;Display the piece mov ax,dx ;Lock using (Y + old Y) add ax,si call PieceDown ;Piece is down jmp KeyDone ;Done KeySpace: call LoadVals ;Load values inc ax ;Next rotation and ax,3 call Fits ;If it fits, jnc KeyDone mov [Rotate],ax ;update rotation value jmp KeyDone KeyEsc: call GameOver ;Done with game KeyF10: call LVPutPiece ;Show piece xor ax,ax ;Wait for a key int 16h KeyDone: call LVPutPiece ;Show piece jmp KeyLoop ;****************** TETRIS Piece Fall NoKeys: test bp,bp ;Only if counter is zero jne MainLoop call LoadVals ;Erase current piece xor di,di call PutPiece dec dx ;Check for fit at (Y - 1) call Fits jnc NoFit ;Jump if it doesn't fit mov [Y],dx ;Save new Y call LVPutPiece ;Show piece jmp MainLoop NoFit: call LVPutPiece ;Show piece mov ax,dx ;Lock using Y call PieceDown ;Piece is down call LVPutPiece ;Show piece jmp MainLoop ;Loop back ;****************** TETRIS Game Over GameOver: pop ax ;Pop junk-word mov cx,11 ;Print GO-top string mov dx,11 mov si,offset GOTop mov ax,04h call PutStr inc dx ;Print game-over message mov si,offset OverStr ;in blinking blue mov ax,0C9h call PutStr inc dx ;Print GO-bottom string mov si,offset GOBottom mov ax,04h call PutStr mov ax,3333 ;Delay 1/3 second call Delay call FlushBuffer ;Flush key buffer xor ax,ax ;Wait for a key int 16h popa ;Restore registers ret ;Return PieceDown: call LockPiece cmp dx,24 ;Too high, game over jge GameOver mov [Rotate],0 ;New piece, type (NxPiece, 0) mov ax,[NxPiece] mov [Piece],ax call Rand7 ;New next-piece mov [NxPiece],ax mov [X],4 ;Position (4, 24) mov [Y],24 call FlushBuffer ;Flush key buffer ret LoadVals: mov cx,[X] ;Load piece values mov dx,[Y] mov bx,[Piece] mov ax,[Rotate] mov di,bx inc di ret LVPutPiece: call LoadVals ;Load piece values jmp PutPiece EndP Tetris ;**************************** LockPiece -- Locks a piece in place Proc LockPiece pusha ;Save all registers push ax ;Save y-value mov ax,150 ;15 msecs mov bx,660 ;Sound at 660 call Sound call NoSound ;End of sound pop ax ;Get back y-value add ax,25 ; y + 25 mov dx,[Level] mul dx ; Level * (y + 25) imul ax,10 ; 10 * Level * (y + 25) cwd mov bx,25 ; (10 * Level * (y + 25)) / 25 div bx add [Score],ax ;add to score adc [Score+2],0 call DelLines ;Delete lines add [Lines],ax ;Adjust line counter sub [LinesLeft],ax mul ax ; (l ^ 3) mul ax add ax,4 ; (l ^ 3) + 4 mov bx,5 cwd div bx ; ((l ^ 3) + 4) / 5 imul ax,100 ;Line score value add [Score],ax ;add to score adc [Score+2],0 cmp [LinesLeft],0 ;Done with level? jg NotNew pusha ;Save all registers mov ax,200 ;20 msec mov bx,440 ;Start at 440 SndLoop1: call Sound ;Sound 440 to 880 add bx,20 cmp bx,880 jne SndLoop1 SndLoop2: call Sound ;Sound 880 to 660 sub bx,20 cmp bx,660 jne SndLoop2 SndLoop3: call Sound ;Sound 660 to 1100 add bx,20 cmp bx,1100 jne SndLoop3 call Sound ;Last sound call NoSound ;End of sound popa ;Restore registers mov [LinesLeft],0 ;LinesLeft = 0 call ShowStatus ;Show status call ClearWell ;Clear well imul ax,[Level],100 ;Score = Score + 100 * Level add [Score],ax adc [Score+2],0 inc [Level] ;Next level imul ax,[DelayTime],7 ;Reduce delay by 12% shr ax,3 mov [DelayTime],ax imul ax,[Level],2 ;LinesLeft = 6 + 2 * Level add ax,6 mov [LinesLeft],ax NotNew: call ShowStatus ;Show status popa ;Restore registers ret ;Return EndP LockPiece ;**************************** ShowStatus -- Display score, level, etc. Proc ShowStatus pusha ;Save all registers mov cx,43 ;Clear Score field mov dx,11 mov si,offset Spaces7 mov al,07h call PutStr inc dx ;Clear Level field inc dx call PutStr inc dx ;Clear Lines field inc dx call PutStr inc dx ;Clear Lines Left field inc dx add cx,5 add si,5 call PutStr mov si,offset Buffer ;Offset of buffer mov ax,[Score] ;Get decimal string for Score mov dx,[Score+2] mov di,si call Cvt32 mov cx,43 ;Print it at (43, 11) mov dx,11 mov al,0Ah call PutStr mov ax,[Level] ;Get decimal string for Level mov di,si call Cvt16 inc dx ;Print it at (43, 13) inc dx mov al,0Ah call PutStr mov ax,[Lines] ;Get decimal string for Lines mov di,si call Cvt16 inc dx ;Print it at (43, 15) inc dx mov al,0Ah call PutStr mov ax,[LinesLeft] ;Get decimal string for Lines Left mov di,si call Cvt16 mov cx,48 ;Print it at (48, 17) inc dx inc dx mov al,0Ah call PutStr mov si,offset Spaces10 ;Clear Next-Piece area mov cx,36 mov dx,5 mov al,07h call PutStr inc dx call PutStr inc dx call PutStr inc dx call PutStr mov bx,[NxPiece] mov bp,bx inc bp xor ax,ax mov cx,40 mov dx,6 add bx,bx mov si,[PieceStart+bx] ;SI = piece offset mov di,4 ;4 blocks xor ax,ax ;Start - (0, 0) SSBlkLoop: push cx dx ;Save position mov bl,ah ;Get offsets in AX, BX cbw xchg ax,bx cbw xchg ax,bx add ax,ax ;Add in offsets add cx,ax sub dx,bx mov ax,bp ;Color in AL call PutBlock ;Show block pop dx cx ;Restore position lodsw ;Load word dec di ;Loop back using DI jnz SSBlkLoop popa ;Restore registers ret ;Return EndP ShowStatus ;**************************** PutBlock -- Put block on screen Proc PutBlock ;Supply CX = x, DX = y, AL = color pusha ;Save all registers imul di,dx,160 ;DI = DX * 160 add di,cx ;DI = DX * 160 + CX * 2 add di,cx test al,al ;If zero, erase block jz IsZero mov ah,al ;AH = color shl ah,4 add ah,8 ;Foreground = color + 8 add ah,al mov al,0CEh ;Store first half stosw inc ax ;Store second half stosw popa ;Restore registers ret ;Return IsZero: mov ax,0720h ;Zero, store spaces stosw stosw popa ;Restore registers ret ;Return EndP PutBlock ;**************************** IsBlock -- Check for block Proc IsBlock ;Supply CX = x, DX = y ;Returns Carry = 1 if block pusha ;Save all registers add cx,cx add cx,10 ;Adjust to screen position neg dx add dx,24 imul di,dx,160 ;DI = DX * 160 add di,cx ;DI = DX * 160 + CX * 2 add di,cx mov al,[es:di] ;Load byte cmp al,0CEh ;If it's < 0CEh, jb NoBlock ;it isn't a block stc ;Set carry flag popa ;Restore registers ret ;Return NoBlock: clc ;Clear carry flag popa ;Restore registers ret ;Return EndP IsBlock ;**************************** PutPiece -- Put piece in well Proc PutPiece ;Supply CX = x, DX = y, BX = piece, AX = rotation, DI = color pusha ;Save all registers mov bp,di ;Color in BP add bx,bx mov si,[PieceStart+bx] ;SI = piece start and al,[si-1] ;AND mask imul ax,6 ;AX * 6 add si,ax ;SI = piece offset mov di,4 ;4 blocks xor ax,ax ;Start with (0, 0) BlockLoop: push cx dx ;Save position mov bl,ah ;Get offsets in AX, BX cbw xchg ax,bx cbw xchg ax,bx add cx,ax ;Add in offsets add dx,bx cmp cx,10 ;Out of well, don't show jae BlockNope cmp dx,25 jae BlockNope add cx,cx add cx,10 ;Adjust to screen position neg dx add dx,24 mov ax,bp ;Color in AL call PutBlock ;Show block BlockNope: pop dx cx ;Restore position lodsw ;Load word dec di ;Loop back using DI jnz BlockLoop popa ;Restore registers ret ;Return EndP PutPiece ;**************************** Fits -- Check whether piece fits Proc Fits ;Supply CX = x, DX = y, BX = piece, AX = rotation ;Returns: Carry = 1 if it fits, 0 if it doesn't. pusha ;Save all registers add bx,bx mov si,[PieceStart+bx] ;SI = piece start and al,[si-1] ;AND mask imul ax,6 ;AX * 6 add si,ax ;SI = piece offset mov di,4 ;4 blocks xor ax,ax ;Start with (0, 0) FitsLoop: push cx dx ;Save position mov bl,ah ;Get offsets in AX, BX cbw xchg ax,bx cbw xchg ax,bx add cx,ax ;Add in offsets add dx,bx cmp cx,10 ;Out of well, doesn't fit jae DoesntFit test dx,dx jl DoesntFit call IsBlock ;Check for block jc DoesntFit pop dx cx ;Restore position lodsw ;Load word dec di ;Loop back using DI jnz FitsLoop stc ;Set carry flag popa ;Restore registers ret ;Return DoesntFit: clc ;Clear carry flag pop dx cx ;Pop extra off stack popa ;Restore registers ret ;Return EndP Fits ;***************************** DelLine -- Delete line Proc DelLine ;Supply AX = y pusha ;Save all registers push ds ;Save DS push ax ;Save AX mov ax,250 ;25 msecs mov bx,440 ;Sound at 440 call Sound call Delay mov bx,660 ;Sound at 660 call Sound call Delay mov bx,880 ;Sound at 880 call Sound call Delay call NoSound ;End of sound pop ax ;Restore AX push es ;DS = ES pop ds neg ax ;Adjust for screen position add ax,24 xchg dx,ax imul di,dx,160 ;DI = DX * 160 add di,20 ;DI = DX * 160 + 20 mov si,di ;SI = previous line sub si,160 ScDnLoop: push si si ;Save offsets mov cx,20 ;Move line rep movsw pop si di ;DI = old SI, sub si,160 ;SI = old SI - 160 dec dx ;Loop back using DX jnz ScDnLoop pop ds popa ;Restore registers ret ;Return EndP DelLine ;***************************** DelLines -- Delete all completed lines Proc DelLines push cx ;Save CX xor cx,cx ;Zero CX mov ax,24 ;AX = 24 DelLoop: pusha ;Save all registers xchg dx,ax ;Y in DX mov cx,9 ;CX = 9 LChkLoop: call IsBlock ;Check for block jnc NotLine ;Not a line if no block dec cx ;Loop back using CX jns LChkLoop stc ;Set carry flag NotLine: popa ;Restore registers jnc $+6 ;Jump if not line call DelLine ;Delete line inc cx ;Increment counter dec ax ;Next line jns DelLoop ;Loop back xchg ax,cx ;Lines in AX pop cx ;Restore CX ret ;Return EndP DelLines ;**************************** ClearWell -- Clear the well Proc ClearWell pusha ;Save all registers mov di,20 ;Start at offset 20 mov dx,25 ;25 rows on screen mov ax,0720h ;Fill with spaces ClearWLoop: mov cx,20 ;Width of well rep stosw ;Clear line add di,120 ;Go to next line dec dx ;Loop back using DX jnz ClearWLoop popa ;Restore registers ret ;Return EndP ClearWell ;**************************** AddHiScore -- Add high score to list Proc AddHiScore pusha ;Save all registers push es ;Save ES push ds ;ES = DS pop es mov ax,[Score] ;DX:AX = Score mov dx,[Score+2] mov bx,offset HiScores+1102 ;Last Score field cmp dx,[bx+2] ;Make sure it's in the hiscores jb AddDone ja HSStart cmp ax,[bx] jbe AddDone HSStart: mov cx,19 HSCmpLoop: cmp dx,[bx+2] ;If Score <= HiScores[i].score, ja HSCmpLB ;then it is done jb HSCmpDone cmp ax,[bx] jbe HSCmpDone HSCmpLB: sub bx,58 ;Previous hiscore dec cx ;Loop back using CX jns HSCmpLoop HSCmpDone: inc cx ;Get the actual position mov dx,cx ;DX = CX mov bp,cx ;BP = CX mov cx,19 ;CX = 19 mov di,offset HiScores+1102 ;DI = HS #19 mov si,offset HiScores+1044 ;SI = HS #18 HSMovLoop: cmp cx,dx ;CX > DX, done je HSMovDone push cx si si ;Save offsets mov cx,29 ;Move hiscore rep movsw pop si di cx ;DI = old SI sub si,58 ;SI = old SI - 58 loop HSMovLoop HSMovDone: pop es ;Restore ES call ClearScreen ;Clear the screen mov cx,4 ;Print 'You made a High Score' mov dx,11 ;at (4, 11), in cyan on blue mov si,offset YMHS mov al,9Bh call PutStr xor cx,cx ;Print 'Enter your name:' mov dx,13 ;at (0, 13), in blue mov si,offset NameStr mov al,09h call PutStr mov bx,2114 ;Current position (17, 13) mov [word es:bx],0A5Fh ;Show pseudo-cursor xor si,si ;Cursor position 0 imul bp,58 ;BP = offset of hiscore add bp,offset HiScores HSKeyLoop: xor ax,ax ;Get a key int 16h cmp ah,BKSPACE ;Backspace? je HSKeyBksp cmp ah,LEFT ;Left-arrow? je HSKeyBksp cmp ah,ENTERK ;Enter? je HSKeyEnter cmp al,20h ;Is it a printable char? jb HSKeyLoop ;Ignore it if it isn't cmp al,7Eh ja HSKeyLoop cmp si,49 ;Already at limit? je HSKeyLoop mov ah,0Ah ;Color is green mov [es:bx],ax ;Show character mov [bp+si+8],al ;Put it in string inc si inc bx ;Advance cursor inc bx mov [word es:bx],0A5Fh ;Show pseudo-cursor jmp HSKeyLoop HSKeyBksp: test si,si ;Already at left? jz HSKeyLoop mov [byte es:bx],20h dec si dec bx ;Move cursor left dec bx mov [byte es:bx],5Fh ;Show pseudo-cursor jmp HSKeyLoop HSKeyEnter: mov [byte bp+si+8],0 ;Terminate string mov ax,[Score] ;Put in values mov [bp],ax mov ax,[Score+2] mov [bp+2],ax mov ax,[Lines] mov [bp+4],ax mov ax,[Level] mov [bp+6],ax push es ;Dummy AddDone: pop es ;****************** Show High Scores call ClearScreen ;Clear the screen mov cx,29 ;Display the title xor dx,dx mov si,offset HiStr mov al,0Fh call PutStr xor cx,cx ;Display HS 'table-top' mov dx,3 mov si,offset HST1 call PutStr mov cx,61 mov si,offset HST2 call PutStr mov bp,1 ;Counter is 1 mov bx,offset HiScores ;Offset of first Hiscore mov si,offset Buffer ;SI = buffer ShowLoop: mov ax,bp ;AX = counter mov di,si ;Convert into buffer call Cvt16 mov [word di],002Eh ;Add a period (like '15.') mov dx,bp ;DX = row add dx,4 mov cx,2 ;Column 2 mov al,09h ;in blue call PutStr push si mov si,bx ;Display name add si,8 mov cx,6 ;Column 6 mov al,0Ah ;in green call PutStr pop si push dx ;Save row mov ax,[bx] mov dx,[bx+2] mov di,si call Cvt32 ;Display score pop dx mov cx,66 ;Column 66 add cx,si ;on the right sub cx,di mov al,0Bh ;in cyan call PutStr mov ax,[bx+4] ;Display lines mov di,si call Cvt16 mov cx,72 ;Column 72 add cx,si ;on the right sub cx,di mov al,0Bh ;in cyan call PutStr mov ax,[bx+6] ;Display level mov di,si call Cvt16 mov cx,77 ;Column 77 add cx,si ;on the right sub cx,di mov al,0Bh ;in cyan call PutStr inc bp add bx,58 ;Next score cmp bp,20 ;Loop back jbe ShowLoop call FlushBuffer ;Flush key buffer xor ax,ax ;Wait for a key int 16h popa ;Restore registers ret ;Return EndP AddHiScore ;**************************** ClearScreen -- Clear the screen Proc ClearScreen pusha ;Save all registers xor di,di ;Zero DI mov ax,0720h ;Fill with spaces mov cx,32768 ;32768 words rep stosw ;Clear screen popa ;Restore registers ret ;Return EndP ClearScreen ;**************************** PutStr -- Print ASCIIZ string Proc PutStr ;Supply (CX, DX) = position, AL = color, DS:SI = string pusha ;Save all registers imul di,dx,160 ;DI = DX * 160 add di,cx ;DI = DX * 160 + CX * 2 add di,cx mov ah,al ;AH = color PutLoop: lodsb ;Load byte test al,al ;Quit if zero jz PutDone stosw ;Store word jmp PutLoop PutDone: popa ;Restore registers ret ;Return EndP PutStr ;**************************** FlushBuffer -- flush keyboard buffer Proc FlushBuffer push ds ;Save DS push 0 ;DS = 0 pop ds push [word 041Ah] ;Key tail = key head pop [word 041Ch] pop ds ;Restore DS ret ;Return EndP FlushBuffer ;**************************** Cvt16 -- Convert 16-bit binary to decimal Proc Cvt16 ;Supply AX = binary value, DS:DI = 5 bytes string space pusha ;Save registers pop di test ax,ax ;If zero, then just je U16Zero ;output a zero. xor cx,cx ;Zero CX mov si,10 ;SI = 10 U16DivLoop: xor dx,dx ;Zero DX div si ;Divide by 10 mov bl,dl ;Remainder in BL add bl,30h ;Convert to digit push bx ;Save digit inc cx ;Increment CX test ax,ax ;Zero now? jnz U16DivLoop ;Loop back if not U16PopLoop: pop ax ;Pop digit mov [di],al ;Store digit inc di loop U16PopLoop ;Loop back U16Ret: mov [byte di],0 ;Store ending null push di ;Restore registers popa ret ;Return U16Zero: mov [byte di],30h ;It was zero, so store a '0'. inc di jmp U16Ret EndP Cvt16 ;**************************** Cvt32 -- Convert 32-bit binary to decimal Proc Cvt32 ;Supply DX:AX = binary value, DS:DI = 5 bytes string space push ax bx dx ;Save all registers mov bx,10000 ;Divide it by 10000 div bx test ax,ax ;Don't do first part if it's zero jz Cvt32Less call Cvt16 ;Convert first part xchg ax,dx ;Remainder in AX call Cvt16B ;Convert last 4 Cvt32Done: pop dx bx ax ;Restore all registers ret ;Return Cvt32Less: xchg ax,dx call Cvt16 jmp Cvt32Done EndP Cvt32 ;**************************** Cvt16B -- Convert 16-bit binary to decimal Proc Cvt16B ;Supply AX = binary value, DS:DI = 5 bytes string space pusha ;Save registers pop di mov si,10 ;SI = 10 mov bp,4 mov cx,bp Z16DivLoop: xor dx,dx ;Zero DX div si ;Divide by 10 mov bl,dl ;Remainder in BL add bl,30h ;Convert to digit push bx ;Save digit dec bp ;Done? jnz Z16DivLoop ;Loop back if not Z16PopLoop: pop ax ;Pop digit mov [di],al ;Store digit inc di loop Z16PopLoop ;Loop back mov [byte di],0 ;Store ending null push di ;Restore registers popa ret ;Return EndP Cvt16B ;**************************** Rand7 -- Generate a random number from 1 to 7 Proc Rand7 mov ax,7 ;call Rand with value 7 call Rand ret EndP Rand7 ;**************************** Rand -- Generate a random number Proc Rand push bx cx dx ax ;Save registers, push AX mov ax,4E35h mul [RandNum] ;Here the Random Number is mov cx,dx ;multiplied by the value xchg bx,ax ;015A4E35h. This is one of imul ax,[RandNum],015Ah ;the optimal values for add cx,ax ;this type of random number. imul ax,[RandNum+2],4E35h add cx,ax add bx,1 ;Increment the number adc cx,0 mov [RandNum],bx ;Save random number mov [RandNum+2],cx xchg ax,cx ;Now the bits are re-arranged, shl ax,1 ;and the number is divided by and bx,1 ;the value originally in AX. add ax,bx pop bx xor dx,dx div bx xchg ax,dx ;Place result in AX pop dx cx bx ;Restore registers ret ;Return EndP Rand ;**************************** Sound -- Sound the speaker. Proc Sound ;Supply AX = delay, BX = frequency pusha ;Save all registers mov dx,12h ;BX = 1193180 / freq. mov ax,34DCh div bx mov bx,ax mov al,0B6h ;Set frequency out 43h,al mov ax,bx out 42h,al mov al,ah out 42h,al in al,61h ;Turn on speaker or al,3 out 61h,al popa ;Restore registers call Delay ;Do the delay ret ;Return EndP Sound ;**************************** NoSound -- Turn off the speaker. Proc NoSound push ax ;Save AX in al,61h ;Turn off speaker and al,0FCh out 61h,al pop ax ;Restore AX ret ;Return EndP NoSound ;**************************** Delay -- Delay. Proc Delay ;Supply AX = msecs * 10. pusha ;Save all registers mov dx,100 mul dx mov cx,dx xchg dx,ax ;CX:DX = time in microseconds mov ah,86h ;INT 15/86 = BIOS delay. int 15h popa ;Restore registers ret ;Return EndP Delay ;****************** Variables Spaces10 db 3 dup(?) Spaces7 db 8 dup(?) GOBottom db 2 dup(?) BoxTop db 17 dup(?) GOTop db 2 dup(?) BoxBottom db 17 dup(?) RandNum dw ?,? Score dw ?,? Lines dw ? Level dw ? DelayTime dw ? LinesLeft dw ? Piece dw ? Rotate dw ? NxPiece dw ? X dw ? Y dw ? HiScores dw 580 dup(?) Buffer: End Start