TITLE Tetris game include SYMBOLS.ASM ; Don't show them here fWidth equ 24 ; Ten cells wide fDepth equ 22 ; 20 Cells deep Initial_Speed equ 2400 ; Time per step Cgroup GROUP Code ; Name for code segments Dgroup GROUP Data ; Name for data segments Code SEGMENT PUBLIC 'Code' ASSUME CS:Cgroup,DS:Dgroup Start: JMP Start1 ; Avoid config data DB "(c) Simon Brattel",0; Config ID string DW 0 ; Ptr to config data Start1: MOV ax,StackSeg ; get Stack Segment Origin MOV SS,ax ; and initialize the SS register MOV SP,o TOS ; set SP to Top Of Stack MOV ax,Data ; get Data Segment Origin MOV DS,ax ; and initialize the DS register CALL Deallocate ; Lose extra memory CALL Init_VDU ; Set up the vdu drivers CALL Get_Windows ; Get some windows opened JC Exit ; Fail mov ah,b Wind_Data+1 ; First window call Set_Def_Wind ; MOV dx,o sFireUp ; Name it CALL PrintStr ; Play_Game: call Init_Score ; 0 mov dx,o sClear_NG ; Clear message call PrintStr ; call Play_Sheet ; Play a game MOV dx,o sNewGame ; Again ? CALL PrintStr ; NG_Lp: call Rand_32 ; Scramble the number call Get_Key ; Y/N ? or al,020H ; LC cmp al,"y" ; Yes ? je Play_Game ; Loop cmp al,"n" ; No ? jne NG_Lp ; No, loop CALL Close_Windows ; Close them again jmp Exit ; Leave ; Play one sheet Play_Sheet: mov ax,Initial_Speed ; Get the starting value mov Delay,ax ; Store it call Clear_Map ; Clear it PS_Lp: call Get_Shape ; Get the next shape call Test_Current ; Does it fit ? jc PS_End ; No, done PS_Lp1: call Player_Move ; Let the player try it call Show_Score ; Display it call Move_Down ; Step the shape down jnc PS_Lp1 ; Ok call Compress ; Remove any full rows mov ax,-2 ; Step the delay this much if dropped test b Drop_F,0FFh ; Dropped ? jnz PS_Drop ; Yes mov ax,-4 ; Worse if not dropped PS_Drop: add Delay,ax ; Step the delay jmp PS_Lp ; Loop PS_End: ret ; Done Exit: call Restore_Cursor ; Put the video mode back mov ah,Terminate ; Exit to DOS int Dos ; ; Move the shape down one line if possible, else draw and CF Move_Down: call Pause_Test ; Wait if required inc b sY ; Step down call Test_Current ; OK ? jnc MD_Done ; Yes, continue dec b sY ; Step back call Draw_Shape ; Place it here stc ; Fail MD_Done: ret ; ; Allow the player to move the block for a while Player_Move: mov cx,Delay ; Get the time shr cx,1 ; /8 shr cx,1 ; shr cx,1 ; shr cx,1 ; /8 shr cx,1 ; shr cx,1 ; PM_Lp: push cx ; Save the timer call PM_DoIt ; Move the block call Draw_Shape ; Place the object call Show_Map ; Put it on the screen call Undraw_Shape ; Remove it pop cx ; Restore test b Drop_F,0FFh ; Dropped ? jnz PM_Dropped ; Yes loop PM_Lp ; Loop jmp PM_Exit ; Done PM_Dropped: mov ax,0001h ; Score + 1 call Inc_Score ; PM_Exit: ret ; Done ; If key pressed, then move block PM_DoIt: call Test_Key ; Key pressed ? jz PM_DI_Done ; No, done call Rand_32 ; Scramble it call Get_Key ; Get the key cmp al,cEsc ; Done ? jz Exit ; Yes, fall out or al,20h ; force to lc mov bh,0 ; No angle change mov bl,-1 ; Left cmp al,"z" ; Step left je PM_DI_TryIt ; Yes mov bl,1 ; Right cmp al,"x" ; Step right je PM_DI_TryIt ; Yes mov bl,0 ; No X change mov bh,-1 ; Left cmp al,"n" ; Rotate left je PM_DI_TryIt ; Yes mov bh,1 ; Right cmp al,"m" ; Rotate right je PM_DI_TryIt ; Yes cmp al," " ; Drop ? jne PM_DI_Done ; No, done mov b Drop_F,1 ; Assert drop PM_DI_Done: ret ; Done ; Now move it if the new position and angle are OK PM_DI_TryIt: mov dl,sX ; Calc its X add dl,bl ; mov dh,sAngle ; Calc its angle add dh,bh ; and dh,03h ; Mask call Test_Shape_Fit ; Will it fit in this place ? jc PM_DI_Done ; No, ignore this mov sX,dl ; Move the object mov sAngle,dh ; jmp PM_DI_Done ; Done ; See if there are any complete lines and remove them if so Compress: mov di,o MAP_Start+((fDepth-1)*(fWidth+2)) ; Point at last line mov cx,fDepth ; Number of lines to try Comp_Lp: push cx ; Save count call Comp_Test ; Is this line full ? jnc Comp_1 ; No, skip call Comp_Crush ; Remove this line push di ; Save ptr call Show_MAP ; Show it pop di ; Restore Comp_Next: pop cx ; Do next loop Comp_Lp ; ret ; Done Comp_1: sub di,fWidth+2 ; Step back up jmp Comp_Next ; Do the next line ; Test if the line (di) is full Comp_Test: mov cx,fWidth ; This many wide mov bx,di ; Get ptr CT_Lp: test b [bx],0FFh ; Full ? jz CT_Fail ; No, fail inc bx ; Step ptr loop CT_Lp ; Loop stc ; Full ret ; CT_Fail: clc ; Not full ret ; ; Remove the line at [di] Comp_Crush: mov bx,di ; Get ptr CC_Lp: cmp bx,o MAP_Start ; Top line ? je CC_Clear ; Yes, clear line mov cx,fWidth ; Number to copy CC_Lp1: mov al,0-(fWidth+2)[bx] ; Read byte mov [bx],al ; Move it inc bx ; Step across loop CC_Lp1 ; Loop across line sub bx,(fWidth+2)+fwidth; Step up one line jmp CC_Lp ; Do next line CC_Clear: mov cx,fWidth ; Clear this line CCC_Lp: mov b [bx],0 ; Clear cell inc bx ; loop CCC_Lp ; Loop mov ax,0100h ; 10 for crush call Inc_Score ; ret ; Done ; Is the shape clear ? Test_Current: mov dl,sX ; X mov dh,sAngle ; Angle jmp Test_Shape_Fit ; See if OK ; Test if shape (cShape), at X (dl), Y (sY) and Angle (dh) ; Overlaps any other blocks (CF Set if so) Test_Shape_Fit: mov al,sShape ; Shape mov ah,dh ; Angle call Get_Shape_Ptr ; SI := Point at the shape's defn mov al,dl ; X mov ah,sY ; Y call Get_Shape_Addr ; DI := Point at MAP mov bx,1[si] ; First cell test b [di+bx],0FFh ; Cell used ? jnz TSF_Fail ; Yes mov bx,3[si] ; First cell test b [di+bx],0FFh ; Cell used ? jnz TSF_Fail ; Yes mov bx,5[si] ; First cell test b [di+bx],0FFh ; Cell used ? jnz TSF_Fail ; Yes mov bx,7[si] ; First cell test b [di+bx],0FFh ; Cell used ? jnz TSF_Fail ; Yes clc ; No overlap ret ; TSF_Fail: stc ; Overlap ret ; ; Put the shape into the map Draw_Shape: mov al,sShape ; Shape mov ah,sAngle ; Angle call Get_Shape_Ptr ; SI := Point at the shape's defn mov al,sX ; X mov ah,sY ; Y call Get_Shape_Addr ; DI := Point at MAP mov al,[si] ; Get colour jmp DS_Draw ; Show it ; Remove the shape from the map Undraw_Shape: mov al,sShape ; Shape mov ah,sAngle ; Angle call Get_Shape_Ptr ; SI := Point at the shape's defn mov al,sX ; X mov ah,sY ; Y call Get_Shape_Addr ; DI := Point at MAP mov al,0 ; Blank DS_Draw: mov bx,1[si] ; First cell mov [di+bx],al ; Write the cell mov bx,3[si] ; First cell mov [di+bx],al ; Write the cell mov bx,5[si] ; First cell mov [di+bx],al ; Write the cell mov bx,7[si] ; First cell mov [di+bx],al ; Write the cell clc ; No overlap ret ; ; Given al = shape, ah = angle : return si = ptr to defn of shape Get_Shape_Ptr: push ax ; Save push bx ; add al,al ; Shape * 4 add al,al ; add al,ah ; Shape * 4 + Angle mov ah,0 ; 16 bit mov bx,ax ; Save add ax,ax ; * 2 add ax,ax ; * 4 add ax,ax ; * 8 add ax,bx ; * 9 mov si,o Defn_Table ; Definition of shapes add si,ax ; Add offset pop bx ; pop ax ; ret ; Done ; Given al=X, ah=Y return ptr to MAP Get_Shape_Addr: push bx ; Save reg push ax ; mov bl,al ; X mov al,fWidth+2 ; Width of MAP imul ah ; Y*Width xchg ax,bx ; Swap them cbw ; x := 16 bit add ax,bx ; ax := x+(y*width) mov di,o MAP_Start ; Base add di,ax ; Full address pop ax ; Restore pop bx ; ret ; Done ; Get a number from 0 to 4 randomly Get_Shape: call Rand_32 ; Get us a random number and ax,07h ; Mask it jz Get_Shape ; 1..7 only dec al ; 0..6 mov sShape,al ; Store it mov b sX,fWidth/2 ; X mov b sY,1 ; Y mov b sAngle,0 ; Angle mov b Drop_F,0 ; Clear dropped flag ret ; Done ; Clear the playing area, should use STOSB (if I could remember the regs !) Clear_Map: mov si,o MAP ; Fill with 0 mov cx,fDepth+1 ; Length of data CM_Lp: push cx ; Save count mov b [si],0FFh ; Guard inc si ; mov al,0 ; Play area mov cx,fWidth ; Length call CM_Fill ; Fill it mov b [si],0FFh ; Guard inc si ; pop cx ; Loop loop CM_Lp ; mov al,0FFh ; Guard mov cx,fWidth+2 ; Length CM_Fill: mov [si],al ; Fill it inc si ; loop CM_Fill ; Loop ret ; ; Write data from (DS:SI) to screen Show_Map: push es ; Save segment register mov ah,b Wind_Data+3 ; Window descriptor call Test_Wind ; Get its address (bx) mov es,dx ; Segment of window data mov si,o MAP_Start ; Point at the map mov di,(((Wide/2)-fWidth)*2)+(Wide*2) ; Point at first char mov di,bx ; Window offset mov al," " ; Space char mov bx,fDepth ; 20 lines SM_Lp1: mov cx,fWidth ; 10 cells per line SM_Lp2: mov ah,[si] ; Read source inc si ; Step add ah,ah ; Align it add ah,ah ; add ah,ah ; add ah,ah ; and ah,70h ; Mask or ah,08h ; Bright mov es:[di],ax ; Write char + attr mov es:2[di],ax ; Twice add di,4 ; loop SM_Lp2 ; Loop X add si,2 ; Skip Guard bytes add di,Wide*2-fWidth*4 ; Step to next line dec bx ; Loop Y jnz SM_Lp1 ; call Redraw_All ; Show all the windows pop es ; Restore ret ; Done ; Add ax to score (done in BCD to make the display routines easier) Inc_Score: add al,Score ; Add score daa ; mov Score,al ; mov al,ah ; adc al,Score+1 ; daa ; mov Score+1,al ; mov al,0 ; adc al,Score+2 ; daa ; mov Score+2,al ; ret ; Done ; Zero score Init_Score: mov al,0 ; Clear score mov Score,al ; mov Score+1,al ; mov Score+2,al ; ret ; Done ; Put score onto display Show_Score: call Make_Score ; Into ASCII mov dx,o Score_Buffer ; String ; mov ah,b Wind_Data+2 ; Window number call PrintStr ; Show string ret ; Done ; Get into buffer as ASCII Make_Score: mov di,o Score_Buff ; Create string mov ah," " ; Zero suppress mov cx,3 ; 6 digit (3 bytes) mov si,o Score+2 ; MSB SS_Lp: mov al,[si] ; MSBs call SS_Byte ; dec si ; loop SS_Lp ; Loop cmp ah," " ; 0 ? jne SS_Done ; No, exit mov b -1[di],"0" ; At least 0 SS_Done: ret SS_Byte: mov bl,al ; Save byte shr al,1 ; Align shr al,1 ; shr al,1 ; shr al,1 ; call SSB_Digit ; Do one digit mov al,bl ; Restore SSB_Digit: and al,0Fh ; Mask jz SSBD_0 ; "0" mov ah,"0" ; Not leading now or al,ah ; Make ASCII mov [di],al ; Store inc di ; Step ret ; Done SSBD_0: mov [di],ah ; " " or "0" inc di ; ret ; Done Get_Windows: CALL Open_Windows ; Get some windows JC GW_No_Windows ; Fail mov ah,Old_Wind ; Close it call Close_Window ; call Redraw_Screen ; RET ; Done GW_No_Windows: MOV dx,o sNoWind ; Fail CALL PrintStr ; RET ; Done Open_Windows: CALL Get_Def_Wind ; Get the current one MOV Old_Wind,ah ; Save it MOV ah,0 ; Start with this one MOV cx,4 ; Need 4 MOV dl,0 ; Max priority PUSH es ; Save seg OW_Lp: PUSH cx ; Save it PUSH ax ; Save it PUSH dx ; Save max. priority CALL Test_Wind ; Open ? POP dx ; Restore JNZ OW_Open ; No, avoid POP ax ; Restore POP cx ; LOOP OW_Step ; Get another MOV di,o Wind_Data ; Point to data area MOV al,dl ; Highest found so far INC al ; Higher MOV Base_Priority,al ; Store it MOV dx,o Window_Data ; Editor window data MOV cx,4 ; Open dem windows OW_Lp2: MOV bx,dx ; Point at window MOV b wPriority[bx],al ; Store priority PUSH dx ; Save PUSH cx ; PUSH ax ; CALL Open_Window ; MOV [di],ah ; Save window number INC di ; POP ax ; POP cx ; POP dx ; JC OW_Fail ; Some error ADD dx,16 ; Point at next window INC al ; Step priority LOOP OW_Lp2 ; Loop CALL Redraw_Screen ; Show them all CLC ; Got 'em OW_Fail: POP es ; Restore RET ; Done OW_Open: MOV es,cx ; Data segment MOV bx,ax ; Data ptr MOV al,ES:wPriority[bx] ; Get it's priority CMP al,dl ; Higher priority ? JB OW_1 ; No, ignore MOV dl,al ; Get it OW_1: POP ax ; Restore POP cx ; OW_Step: INC ah ; Step CMP ah,nWindows ; Done ? JNE OW_Lp ; No, loop STC ; Fail, not enough POP es ; Restore RET ; Done Close_Windows: MOV di,o Wind_Data ; Point to data area MOV cx,4 ; CW_Lp: PUSH cx ; Save MOV ah,[di] ; Giz its number INC di ; CALL Close_Window ; POP cx ; LOOP CW_Lp ; Loop CALL Redraw_Screen ; Show it MOV ah,Old_Wind ; Restore CALL Set_Def_Wind ; RET ; Done ; Pause Pause_Test: MOV ah,2 ; Get shift status INT Kbd ; AND al,010h ; Caps shift jnz Pause_Test ; Wait if caps RET ; ; Random numbers Rand_32: push cx ; push bx ; call Rand_Inner ; Get next xor al,dh ; Scramble it a bit xor ah,dl ; pop bx ; Restore pop cx ; ret ; Done Rand_Inner: mov ax,Seed ; This is from Turbo Pascal mov bx,Seed+2 ; mov cx,ax ; mul w Nasty ; shl cx,1 ; shl cx,1 ; shl cx,1 ; add ch,cl ; add dx,cx ; add dx,bx ; shl bx,1 ; shl bx,1 ; add dx,bx ; add dh,bl ; mov cl,5 ; shl bx,cl ; add dh,bl ; add ax,1 ; adc dx,0 ; mov Seed,ax ; mov Seed+2,dx ; ret ; Code ENDS Data SEGMENT PUBLIC 'Data' ; Data seg ; Data for shapes xs equ 1 ; One cell in X ys equ (fWidth+2) ; One cell in Y Defn_Table db 4 ; 0 (bar) dw -xs,0,xs,2*xs ; db 4 ; dw -ys,0,ys,2*ys ; db 4 ; dw -2*xs,-xs,0,xs ; db 4 ; dw -2*ys,-ys,0,ys ; db 1 ; 1 (block) dw 0,xs,ys,ys+xs ; db 1 ; dw 0,-xs,ys,ys-xs ; db 1 ; dw 0,-xs,-ys,-ys-xs ; db 1 ; dw 0,xs,-ys,xs-ys ; db 6 ; 2 (point) dw 0,-xs,xs,ys ; db 6 ; dw 0,-xs,-ys,ys ; db 6 ; dw 0,-xs,xs,-ys ; db 6 ; dw 0,xs,-ys,ys ; db 7 ; 3 (L) dw -xs,0,xs,xs+ys ; db 7 ; dw -ys,0,ys,ys-xs ; db 7 ; dw -xs,0,xs,-xs-ys ; db 7 ; dw -ys,xs-ys,0,ys ; db 3 ; 4 (RL) dw -xs,0,xs,xs-ys ; db 3 ; dw -ys,0,ys,ys+xs ; db 3 ; dw -xs,0,xs,ys-xs ; db 3 ; dw -ys,-xs-ys,0,ys ; db 2 ; 5 (xx dw -xs,0,ys,ys+xs ; xx) db 2 ; dw -xs,0,-ys,ys-xs ; db 2 ; dw 0,xs,-xs-ys,-ys ; db 2 ; dw ys,0,xs,xs-ys ; db 5 ; 6 ( xx dw xs,0,ys,ys-xs ; xx ) db 5 ; dw -xs,0,-ys-xs,ys ; db 5 ; dw 0,-xs,xs-ys,-ys ; db 5 ; dw -ys,0,xs,xs+ys ; Nasty dw 33797 ; Nasty number Seed dw 1278,42 ; Random number seed Delay dw ? sX db ? ; X of shape sY db ? ; Y of shape sShape db ? ; Current shape sAngle db ? ; Angle Drop_F db ? ; Dropped flag Score db 3 dup (?) ; 6 Digits Window_Data DB 1,Wide DB 25,25 DB 1,Wide,1,2 DB 1,1 DB 00 DB 10h ; Attr DB 0Dh DB 07h DB 0Fh DB 0Eh ; 1 DB 1,((Wide/2)-3)-fWidth DB 1,25 DB 1,30,1,25 DB 1,1 DB 25 DB 00Dh ; Attr DB 0Dh DB 07h DB 0Fh DB 0Eh ; 2 DB Wide/2+2+fWidth,Wide DB 1,25 DB 1,30,1,25 DB 1,1 DB 00 DB 0Dh ; Attr DB 0Dh DB 07h DB 0Fh DB 0Eh ; 3 DB (Wide/2)-fWidth,((Wide/2)-1)+fWidth DB 2,23 DB 1,21,1,25 DB 1,1 DB 00 DB 07h ; Attr DB 0dh DB 0dh DB 0dh DB 0dh sNoWind DB cAttr3,"Not enough free windows",cCret,0 sFireUp DB cAttr0,"TETRIS V0.2",cCret db cCret db cAttr1,"SCORE :- ",0; For now sNewGame db cCur_Pos,20h,26h,cAttr0,"Play again ? (Y/N)",cCret,0 sClear_NG db cCur_Pos,20h,26h,cClear_Line,0 Old_Wind DB ? ; Initial window Wind_Data DB 8 dup (?) Base_Priority DB ? ; Priority of first window MAP DB (fWidth+2)+1 dup (?) ; Guard MAP_Start DB (fWidth+2)*(fDepth+2) dup (?); Play area Score_Buffer db cAttr2,cCur_Pos,2Ah,23h Score_Buff db 6 dup (?) db cAttr0,0 Data ENDS ; StackSeg SEGMENT STACK DW 100 dup (?) ; Some stack space TOS EQU THIS WORD ; StackSeg ENDS EXTRN Deallocate :NEAR EXTRN Init_VDU :NEAR EXTRN Restore_Cursor :NEAR EXTRN Open_Window :NEAR EXTRN Close_Window :NEAR EXTRN Redraw_Screen :NEAR EXTRN Redraw_All :NEAR EXTRN Redraw_Line :NEAR EXTRN PrintStr :NEAR EXTRN PrintStr_W :NEAR EXTRN PrintStrES :NEAR EXTRN PrintStrES_W :NEAR EXTRN Set_Def_Wind :NEAR EXTRN Get_Def_Wind :NEAR EXTRN Test_Wind :NEAR EXTRN Get_Key :NEAR EXTRN Get_Line :NEAR EXTRN Test_Key :NEAR EXTRN PrintAcc :NEAR EXTRN Print :NEAR EXTRN Print_W :NEAR EXTRN SkipSpc :NEAR EXTRN CaseConv :NEAR EXTRN WriteHex32 :NEAR EXTRN WriteHex16 :NEAR EXTRN WriteHex8 :NEAR EXTRN SignHex32 :NEAR EXTRN WriteDec32 :NEAR EXTRN SignDec32 :NEAR EXTRN Evaluate :NEAR EXTRN Eval :NEAR EXTRN Label_Char :NEAR EXTRN Alpha :NEAR END