JMP INIT BALLC EQU 0EH ;SET THE BALL'S COLOR TO YELLOW ON BLACK BACKGROUND GRAVITY EQU -1 ;GRAVITY CONSTANT UPKEY EQU 72 ;VARIOUS KEY CONSTANTS DOWNKEY EQU 80 LEFTKEY EQU 75 RIGHTKEY EQU 77 RETKEY EQU 13 FORWARD EQU '.' BACKWARD EQU ',' TANK1COLOR EQU 04H ;REFERENCED BY PAINT ROUTINES FOR TANK COLORS TANK2COLOR EQU 01H TANK1POS DB 3 ;THE MIDDLE POSITION OF EACH TANK TANK2POS DB 72 ;USED TO PAINT, CHECK FOR HITS, EVERYTHING... TANK1SCORE DB 0 TANK2SCORE DB 0 FIRSTGO DB 0 ;DON'T PAINT THE FIRST PLACE THE BALL IS IN (INSIDE THE TANK) MOVED DB 0 ;CHECKS TO SEE IF A TANK HAS MOVED IN A TURN BROW DB 23 ;CURRENT LINE OF BALL (0 = NO BALL) BCOL DB 3 ;CURRENT COL OF BALL VXBALL DB +5 ;VERTICAL DIRECTION (-1=UP, +1=DOWN) VYBALL DB +5 ;HORIZ DIRECTION (-1=LEFT, +1=RIGHT) PWRBU1 DB +5 ;STORES THE POWER OF EACH TANK FOR THE NEXT TURN PWRBU2 DB +5 WIND DB +0 ;SETS THE WIND CONSTANT ANGLE DW 45 ;VARIABLE FOR THE ANGLE OF THE SHOT ANGLEBU1 DW +45 ;STORES THE ANGLE OF EACH TANK FOR THE NEXT TURN ANGLEBU2 DW +45 TURN DB 1 ;KEEPS TRACK OF 1 OR 2'S TURN (1 STARTS) GAMEOVER DB 0 ;GAME OVER? 1=YES, 0=NO NUMSTORE DB 0,0,0 ;STORES THE 3 DIGIT NUMBERS FOR PASCII WINNER DB 0 ;STORES THE WINNER OF THE GAME ;DATABASE OF ANGLES 0-90 FOR SINE (COSINE BACKWARDS) SINE DB 000,001,003,005,007,009,010,012,014 DB 016,017,019,021,023,024,026,028,029 DB 031,033,034,036,037,039,041,042,044 DB 045,047,048,050,052,053,054,056,057 DB 059,060,062,063,064,066,067,068,069 DB 071,072,073,074,075,077,077,079,080 DB 081,082,083,084,085,086,087,087,088 DB 089,090,091,091,092,093,093,094,095 DB 095,096,096,097,097,097,098,098,098 DB 099,099,099,099,099,100,100,100,100 HITMSG DB 'THATS A HIT!',0 SCORE DB 'SCORE: ',0 DEG DB 'DEG:',0 PWR DB 'PWR:',0 TITL DB '[][][] [] []\ [] [] //',0 TITL2 DB ' [] [][] []\\[] []<<',0 TITL3 DB ' [] [] [][] \[] [] \\',0 TITL4 DB ' ',0 TITL5 DB 'Version 4.29 by Max Sobell, NYU',0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; This is ugly, but its the formatting for the splash screen ;; each 0 represents a carriage return and ' ', 0 represents ;; a blank line. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SPLASHMSG DB '< -- This is a tank. ',0 db ' ',0 db 'The UP and DOWN arrow keys ' db 'change the angle of a shot.',0 db ' ',0 db 'The LEFT and RIGHT arrow keys ' db 'change the power of a shot.', 0 db ' ',0 db 'When you are ready, press ENTER ' db 'to fire. ',0 db ' ',0 db 'Each turn, you can move forwards ' db 'or backwards' db 'one space', 0 db 'Use "," for L and "." for R',0 db ' ',0 db 'The objective is to hit the ' db 'other tank 3 times', 0 db 'before they can hit you 3 times.',0 db 'When you hit them, ' db 'your tank will move forward ' db '5 spaces.', 0 db ' ',0 db 'Press any key to begin playing! ' db '"q" anytime quits.',1 ;FUNERAL MARCH, TAKEN FROM DEWAR GAME ;modified for double-time BEEPP DW 15,600,6,0 dw 0 FUNR1 DW 37,262,6,0 DW 0 FUNR2 DW 37,262,6,0 DW 26,262,6,0 DW 7,262,6,0 DW 37,262,6,0 DW 13,311,6,0 DW 7,294,6,0 DW 26,294,6,0 DW 7,262,6,0 DW 26,262,6,0 DW 7,247,6,0 DW 37,262,6,0 DW 0 ;END MARCH FROM DEWAR ONEWON db 'Tank 1 won the game!',0 TWOWON db 'Tank 2 won the game!',0 AGAIN db 'Play again? y/n',0 ;BEGIN CODE INIT: CALL CLEANUP ;clear the screen of the CLI CALL SPLASH ;display the splash (intro) screen INIT1: MOV TANK1POs, 3 ;THE MIDDLE POSITION OF EACH TANK MOV TANK2POS, 72 ;USED TO PAINT, CHECK FOR HITS, EVERYTHING... MOV TANK1SCORE, 0 MOV TANK2SCORE, 0 MOV GAMEOVER, 0 MOV TURN, 1 MOV BROW, 23 ;CURRENT LINE OF BALL (0 = NO BALL) MOV BCOL, 3 MOV MOVED, 0 INIT2: CALL CLEANUP CALL PRINTPWR CALL PRINTDEGS MOV AH, 11 ;SET COLOR MOV BH, 0 ;SET BACKGROUND MOV BL, 1 ;CHOOSE BLUE INT 10H CALL SETVALUES ;get user inputs (up/down,left/right for angle and power) CALL SETANGLES ;and then take the sin and cos of the power to shoot the ball off at MAININIT: MOV BROW, 23 ;put the ball on the grass (almost bottom row) CMP TURN, 1 ;see whose turn it is (1 or 2) JNE SETBALL2 ;if its not one's, its two's MOV AL, TANK1POS ;put the ball at tank 1's area JMP SETBALL SETBALL2: MOV AL, TANK2POS ;if its not one's turn to fire, the ball goes to two SETBALL: ADD AL, 2 ;put the ball in the middle of the tank MOV BCOL, AL ;now place the ball MAIN: MOV DH,BROW ;POINT TO BALL POSITION MOV DL,BCOL MOV AL,2 ;BALL IS ASCII 2 (HAPPY FACE) MOV BL,BALLC ;SET PROPER COLOR MOV CX,1 CMP FIRSTGO, 0 ;DON'T PAINT THE BALL THE FIRST TIME WE SHOOT JE SKIPPAINT CALL PAINT ;DISPLAY BALL AT CURRENT POSITION CALL DELAY ;WAIT FOR A MOMENT SKIPPAINT: INC FIRSTGO ;paint the ball next time (no longer the first time) CMP TURN, 1 ;whose turn is it? JNE MOVE2 CALL MOVEBALL1 ;move the ball according to x + y velocities JMP CHK MOVE2: CALL MOVEBALL2 CHK: CMP BROW, 22 ;has the ball landed naturally? JA HIT ;if yes, go into the hit routine (landing) JMP MAIN ;otherwise move it again HIT: MOV FIRSTGO, 0 ;RESET (since it will be a new shot next time) MOV DH,BROW ;paint the ball a final time in its landing place MOV DL,BCOL MOV AL,2 MOV BL,BALLC-2 ;paint the ball RED this time MOV CX,1 CALL PAINT ;DISPLAY BALL AT CURRENT POSITION MOV CH, 5 ;delay 5x the normal length lea si, beepp call tune DLOOP: CALL DELAY DEC CH JNZ DLOOP CALL CHECKIFHIT ;see if the ball hit anyone CMP GAMEOVER, 1 ;if the game is over, end it (someone has reached 3 hits) JE FIN CMP TURN, 1 ;change turns JNE CHANGETO1 INC TURN JMP TWOTURN CHANGETO1: DEC TURN PUSH AX PUSH BX MOV AL, PWRBU1 ;restore the 1's backups from last turn MOV VXBALL, AL MOV VYBALL, AL MOV BX, ANGLEBU1 MOV ANGLE, BX POP BX POP AX JMP INIT2 ;and go again (initialize) TWOTURN: PUSH AX PUSH BX MOV AL, PWRBU2 MOV VXBALL, AL MOV VYBALL, AL MOV BX, ANGLEBU2 MOV ANGLE, BX POP BX POP AX JMP INIT2 FIN: LEA SI,FUNR2 ;PLAY FUNERAL MARCH AT THE END OF THE GAME CALL TUNE ;SINCE SOMEONE HIT 3 TIMES, CALL EXIT JMP XIT ;AND RETURN TO DOS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; THIS PROCEDURE MOVES THE BALL BASED ON WIND, ETC AND STORES IT IN THE BCOL ;; AND BROW VARIABLES (DB) TO BE PAINTED IN THE MAIN METHOD ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MOVEBALL1 PROC PUSH AX PUSH BX MOV BL, WIND ;ACCOUNT FOR THE WIND ADD VXBALL, BL ;ADD IT TO THE X VELOCITY MOV AL, VXBALL MOV AH, VYBALL MOV BL, BCOL ;CHECK WE'RE NOT GOING PAST THE END ADD BL, AL CMP BL, 79 JA DONTMOVEX ADD BCOL, AL ;ADD X VELOCITY TO THE COLUMNS JMP CHECKY DONTMOVEX: MOV BCOL, 79 MOV BROW, 24 JMP DONEMOVING CHECKY: SUB BROW, AH MOV BL, GRAVITY ADD VYBALL, BL DONEMOVING: POP BX POP AX RET MOVEBALL1 ENDP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; OR MOVE THE BALL FOR TANK 2 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MOVEBALL2 PROC PUSH AX PUSH BX MOV BL, WIND ;ACCOUNT FOR THE WIND SUB VXBALL, BL ;ADD IT TO THE X VELOCITY MOV AL, VXBALL MOV AH, VYBALL MOV BL, BCOL ;CHECK WE'RE NOT GOING PAST THE END SUB BL, AL CMP BL, 80 JA DONTMOVEX2 SUB BCOL, AL ;ADD X VELOCITY TO THE COLUMNS JMP CHECKY2 DONTMOVEX2: MOV BCOL, 0 MOV BROW, 24 JMP DONEMOVING2 CHECKY2: SUB BROW, AH MOV BL, GRAVITY ADD VYBALL, BL DONEMOVING2: POP BX POP AX RET MOVEBALL2 ENDP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; THIS PROCEDURE ASSIGNS X AND Y VALUES TO THE BALL BASED ON SINE/COSINE ;; VALUES FROM THE DW "SINE". BY TAKING THE SINE AND COSINE OF THE SAME ;; ANGLE AND SPLITTING IT UP INTO X AND Y COMPONENTS, I CAN CREATE A VECTOR ;; PATH FOR THE BALL BASED ON AN ANGLE. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SETANGLES PROC PUSH AX PUSH CX PUSH SI MOV SI, ANGLE ;get the angle that the user input MOV AL, SINE[SI] ;get the sine value of that angle MOV AH, 0 MOV CL, VYBALL ;get the ball's input y velocity (same as x for now) MOV CH, 0 MUL CX ;velocity * sin(angle) MOV CX, 100 DIV CX ;and now divide by 100 to round off to integers (accuracy loss since no floating point here) MOV VYBALL, AL ;now record the answer MOV AX, 90 ;convert sine to cosine SUB AX, SI ;by subtracting the angle from 90 degrees (phase shift) MOV SI, AX MOV AL, SINE[SI] ;get the effective cosine of the angle MOV AH, 0 MOV CL, VXBALL MOV CH, 0 MUL CX MOV CX, 100 DIV CX ;do the same mults and divs as with the y velocity MOV VXBALL, AL ;and move the result into the x velocity POP SI POP CX POP AX RET SETANGLES ENDP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; THIS CHECKS FOR KEY INPUTS AND SETS THE ANGLE/VELOCITY AND MOVES THE ;; TANKS EACH TURN, CHECKING FOR NECESSARY CONSTRAINTS (NOT OFF THE SCREEN ;; AND NOT TOUCHING EACH OTHER). ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SETVALUES: ;print the current angle CALL PMSGS SETVALUES1: ;now get more keyboard input MOV AH, 0 INT 16H CHECKDONE: CMP AL, 0DH ;check for a shot (enter) JNE CHECKENDP JMP DONE ;if yes, then this proc is over, shoot the ball CHECKENDP: CMP AL, 'q' JNE CHECKF JMP XIT CHECKF: CMP AL, FORWARD ;check for forward JNE CHECKB ;if not, check for backwards... CMP MOVED, 1 ;has the player already moved once this turn? JE SETVALUES1 ;if yes, get more input CMP TURN, 1 ;whose turn is it? i.e., which tank should move? JNE F2 ;if its not one's, its two's INC TANK1POS ;move tank 1 forwards one space MOV BL, TANK1POS ;now do some checks -- MOV BH, TANK2POS ;is it face to face with tank 2? SUB BH, BL CMP BH, 6 JAE FOK DEC TANK1POS ;if it is, move it back again... (undo) JMP SETVALUES1 ;that move doesn't count -- get more input FOK: CALL REPAINT ;finally we get some good input -- repaint the canvas MOV MOVED, 1 ;and that was the move for the turn.... JMP SETVALUES1 F2: INC TANK2POS ;same stuff for tank 2, but if we're moving tank 2 to the right... MOV BH, TANK2POS ;we have to check if its against the wall, not if its next to tank 1 CMP BH, 75 JBE FOK2 DEC TANK2POS JMP SETVALUES1 FOK2: CALL REPAINT ;if its good input, that's the move, and repaint MOV MOVED, 1 JMP SETVALUES1 CHECKB: CMP AL, BACKWARD ;same comments as above, but visa versa for tank 1 and 2 on the checks... JNE CHECKRIGHT CMP MOVED, 1 JE SETVALUES1 CMP TURN, 1 JNE B2 DEC TANK1POS MOV BH, TANK1POS ADD BH, 1 CMP BH, 1 JAE BOK INC TANK1POS JMP SETVALUES1 BOK: CALL REPAINT MOV MOVED, 1 JMP SETVALUES1 B2: DEC TANK2POS MOV BL, TANK1POS MOV BH, TANK2POS SUB BH, BL CMP BH, 6 JAE BOK2 INC TANK2POS JMP SETVALUES1 BOK2: CALL REPAINT MOV MOVED, 1 JMP SETVALUES1 CHECKRIGHT: CMP AH, RIGHTKEY ;standard stuff -- check for power increase or decrease JNE CHECKLEFT ;if not this key, check for the next one INC VXBALL ;increase both x and y velocities (for sine and cosine) INC VYBALL CALL PRINTPWR ;and reprint the power CHECKLEFT: CMP AH, LEFTKEY ;same here, but decrease JNE CHECKUP DEC VXBALL DEC VYBALL CALL PRINTPWR CHECKUP: CMP AH, UPKEY ;check for degree increase JNE CHECKDOWN ADD ANGLE, 5 ;increase by FIVE (because we don't have floating point... CALL PRINTDEGS CHECKDOWN: CMP AH, DOWNKEY JNE GOAGAIN SUB ANGLE, 5 CALL PRINTDEGS GOAGAIN: JMP SETVALUES1 DONE: MOV AL, VXBALL ;KEEP THE POWER IN A DB TO RESTORE NEXT TURN MOV BX, ANGLE CMP TURN, 1 ;IS IT 1'S TURN OR 2'S TURN? JNE SET2 MOV PWRBU1, AL MOV ANGLEBU1, BX MOV MOVED, 0 JMP ENDSET SET2: MOV PWRBU2, AL MOV ANGLEBU2, BX MOV MOVED, 0 ENDSET: RET ; ; THIS PROCEDURE PRINTS "deg: XX" IN THE UPPER LEFT ; PRINTDEGS PROC PUSH SI PUSH DX PUSH AX cmp turn, 1 jne setfor2 MOV DH, 0 MOV DL, 0 CALL SETPOS ;set the cursor in the upper left for tank 1 jmp degp setfor2: MOV DH, 0 MOV DL, 60 CALL SETPOS ;set the cursor in the upper right for tank 2 degp: MOV SI, 0 DEGP1: MOV AH, 0EH ;prepare to print "DEG:" in front of the degrees MOV AL, DEG[SI] INT 10H INC SI CMP AL, 0 JNE DEGP1 MOV AX, ANGLE ;now print the angle where the cursor is CMP AL, 90 JBE ANGLEPRINT MOV ANGLE, 90 MOV AX, ANGLE ANGLEPRINT: CALL PASCII ;at this point, should display "DEG: Xx" POP AX POP DX POP SI RET PRINTDEGS ENDP ; ; THIS PROCEDURE PRINTS "pwr: XX" IN THE UPPER LEFT, OVER 10 ; ;now print the current power a row down PRINTPWR PROC PUSH SI PUSH DX PUSH AX CMP TURN, 1 JNE SETFOR2P MOV DH, 0 MOV DL, 10 CALL SETPOS ;SET THE CURSOR IN THE UPPER LEFT FOR TANK 1 JMP PWRP SETFOR2P: MOV DH, 0 MOV DL, 70 CALL SETPOS ;SET THE CURSOR IN THE UPPER RIGHT FOR TANK 2 PWRP: MOV SI, 0 PWRP1: MOV AH, 0EH MOV AL, PWR[SI] INT 10H INC SI CMP AL, 0 JNE PWRP1 MOV AL, VXBALL MOV AH, 0 CALL PASCII POP AX POP DX POP SI RET PRINTPWR ENDP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; THIS IS THE PROCEDURE WHICH CHECKS IF A TANK WAS HIT LAST SHOT AND ;; DEALS WITH INCREASING SCORES AND PRINTING OUT "THAT'S A HIT!" WITH ;; A BEEP. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CHECKIFHIT PROC PUSH AX PUSH BX PUSH SI MOV SI, 0 CMP BROW, 23 JA HITOVER ;THE PLAYER SHOT THE BALL OFF THE SCREEN -- THIS IS THE DUMPSTER LOCATION MOV AL, BCOL SUB AL, TANK2POS ;CHECK IF TANK 2 IS HIT CMP AL, 5 JB PRINTHIT1 MOV AL, BCOL SUB AL, TANK1POS ;OTHERWISE CHECK IF TANK 1 IS HIT CMP AL, 5 JB PRINTHIT2 JMP HITOVER ;IF NEITHER IS HIT, RETURN PRINTHIT1: MOV DH, 2 MOV DL, 0 ;UNDER TANK 1'S SCORE CALL SETPOS ADD TANK1POS, 5 INC TANK1SCORE CMP TANK1SCORE, 3 JNE HITPLOOP MOV GAMEOVER, 1 MOV WINNER, 1 JMP HITPLOOP PRINTHIT2: MOV DH, 2 MOV DL, 60 ;UNDER TANK 2'S SCORE CALL SETPOS SUB TANK2POS, 5 INC TANK2SCORE CMP TANK2SCORE, 3 JNE HITPLOOP MOV GAMEOVER, 1 MOV WINNER, 2 HITPLOOP: LEA SI, HITMSG CALL PRINTSTR MOV DL, 5 ;DELAY COUNTER DLP: CALL DELAY DEC DL JNZ DLP LEA SI,FUNR1 ;PLAY A BEEP CALL TUNE HITOVER: POP SI POP BX POP AX RET CHECKIFHIT ENDP ;THIS PROCEDURE IS CALLED AFTER SOMEONE WINS EXIT PROC CALL CLEANUP CMP WINNER, 1 JA TWOWINS LEA SI, ONEWON CALL PRINTSTR JMP AGAINMSG TWOWINS: LEA SI, TWOWON CALL PRINTSTR AGAINMSG: MOV DL, 0 MOV DH, 1 CALL SETPOS LEA SI, AGAIN CALL PRINTSTR GETINPUT: MOV AH, 0 INT 16H CHECKAGAIN: CMP AL, 'y' JNE CHECKFINISH JMP INIT1 CHECKFINISH: CMP AL, 'n' JE ENDPROG CALL DELAY JMP GETINPUT ENDPROG: JMP XIT EXIT ENDP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; THIS IS THE PAINT PROCEDURE WHICH PAINTS CX ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; REPAINT PROC CALL CLEANUP ;CLEAN UP THE SCREEN (DELETE EVERYTHING) CALL PMSGS ;PRINT THE SCORE, TANKS, GRASS CALL PRINTDEGS ;PRINT THE CURRENT DEGREES (BOTH TANKS) CALL PRINTPWR ;PRINT THE CURRENT POWER (BOTH TANKS) RET REPAINT ENDP ; PAINT PROC PUSH AX ; SAVE REGISTERS PUSH BX CMP CX,0 ; SKIP IF NO CHARS TO PAINT JE PAINT1 CALL SETPOS ; SET CURSOR POSITION MOV BH,0 ; WRITE THE CHARACTERS MOV AH,9 INT 10H ; ; HERE WITH CHARACTERS PAINTED ; PAINT1: POP BX ; RESTORE REGISTERS POP AX RET ; RETURN TO CALLER PAINT ENDP SETPOS PROC PUSH AX ; SAVE REGISTERS PUSH BX MOV BH,0 ; SET CURSOR MOV AH,2 INT 10H POP BX ; RESTORE REGISTERS POP AX RET ; RETURN TO CALLER SETPOS ENDP ;TO PRINT MESSAGES TO SCREEN and repaint the canvas PMSGS PROC PUSH AX PUSH BX PUSH SI MOV DH, 1 MOV DL, 0 ;set the score field for tank 1 in the upper left LEA SI, SCORE ;print "SCORE:" CALL PRINTSTR MOV AL, TANK1SCORE ;and print the score right after it CALL PASCII ;ascii value of the score MOV DH, 1 MOV DL, 59 ;reset for tank 2's score (one back from where it should be) lea si, score ;bc there is a 0 in the video buffer from before call printstr mov al, tank2score call pascii PNTGRASS: ;This program puts 1 row of green ='s on the screen in the bottom rows ; ; first place the cursor ; mov dh, 24 mov dl, 0 ;BOTTOM ROW mov ah, 2 ;set cursor mov bh, 0 ;video page 0 int 10h ; mov al, ' ' mov cx, 80 ;one row mov bl, 22h ;green on black mov bh, 0 ;video page 0 mov ah, 9 ;write char w/attrib int 10h ;NOW PRINT TANK 1'S BOTTOM ROW mov dh, 23 mov dl, TANK1POS ;BOTTOM ROW mov ah, 2 ;set cursor mov bh, 0 ;video page 0 int 10h MOV AL, 'X' ;PRINT TANK 1 MOV CX, 5 ;5 WIDE MOV BL, tank1color ;RED ON BLACK MOV BH, 0 MOV AH, 9 INT 10H ;NOW PRINT TANK 1'S MIDDLE ROW mov dh, 22 mov dl, TANK1POS ;BOTTOM ROW add dl, 1 mov ah, 2 ;set cursor mov bh, 0 ;video page 0 int 10h MOV AL, 'X' ;PRINT TANK 1 MOV CX, 3 ;5 WIDE MOV BL, tank1color ;RED ON BLACK MOV BH, 0 MOV AH, 9 INT 10H ;now print tank 1's top row mov dh, 22 mov dl, tank1pos add dl, 2 mov ah, 2 mov bh, 0 int 10h mov al, 'O' mov cx, 1 mov bl, tank1color mov bh, 0 mov ah, 9 int 10h mov dh, 21 mov dl, tank1pos add dl, 3 mov ah, 2 mov bh, 0 int 10h mov al, '/' mov cx, 1 mov bl, tank1color mov bh, 0 mov ah, 9 int 10h ;NOW PRINT TANK 2'S BOTTOM ROW mov dh, 23 mov dl, TANK2POS ;BOTTOM ROW mov ah, 2 ;set cursor mov bh, 0 ;video page 0 int 10h MOV AL, 'X' ;PRINT TANK 1 MOV CX, 5 ;5 WIDE MOV BL, tank2color ;RED ON BLACK MOV BH, 0 MOV AH, 9 INT 10H ;NOW PRINT TANK 2'S MIDDLE ROW mov dh, 22 mov dl, TANK2POS ;BOTTOM ROW add dl, 1 mov ah, 2 ;set cursor mov bh, 0 ;video page 0 int 10h MOV AL, 'X' ;PRINT TANK 1 MOV CX, 3 ;5 WIDE MOV BL, tank2color ;RED ON BLACK MOV BH, 0 MOV AH, 9 INT 10H ;now print tank 1's top row mov dh, 22 mov dl, tank2pos add dl, 2 mov ah, 2 mov bh, 0 int 10h mov al, 'O' mov cx, 1 mov bl, tank2color mov bh, 0 mov ah, 9 int 10h mov dh, 21 mov dl, tank2pos add dl, 1 mov ah, 2 mov bh, 0 int 10h mov al, '\' mov cx, 1 mov bl, tank2color mov bh, 0 mov ah, 9 int 10h POP SI POP BX POP AX RET PMSGS ENDP ; ; This proc is from Dewar -- it clears the screen (prints all black on black spaces) ; CLEANUP PROC MOV DX,0 ;reset screen to normal color MOV CX,25*80 MOV BL,07H MOV AL,' ' CALL PAINT MOV AX, 4C00H RET CLEANUP ENDP ;from dewar, cleanup routine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; this procedure paints the welcome screen, wait until a key ;; is pressed, then returns ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PRINTSTR PROC ;Prints ASCII-z string pointed to by the SI register to the screen ;Credit to Dov push ax push bx push si ; call setpos ;save space, call within the printstr method mov bh, 0 ;video page 0 mov bl, 05H and al, 0 int 10h mov ah, 14 ;write teletype ; pLoop: mov al, [si] cmp al, 0 je pDone ; int 10h ;print character inc si jmp pLoop ; pDone: pop si pop bx pop ax ret PRINTSTR ENDP ; SPLASH PROC PUSH AX PUSH BX PUSH CX PUSH DX PUSH SI MOV DH, 1 MOV DL, 20 LEA SI, TITL CALL PRINTSTR INC DH LEA SI, TITL2 CALL PRINTSTR INC DH LEA SI, TITL3 CALL PRINTSTR INC DH LEA SI, TITL4 CALL PRINTSTR INC DH LEA SI, TITL5 CALL PRINTSTR ;PRINT AN EXAMPLE TANK'S BOTTOM ROW MOV DH, 9 MOV DL, TANK1POS ;BOTTOM ROW MOV AH, 2 ;SET CURSOR MOV BH, 0 ;VIDEO PAGE 0 INT 10H MOV AL, 'X' ;PRINT TANK 1 MOV CX, 5 ;5 WIDE MOV BL, TANK1COLOR ;RED ON BLACK MOV BH, 0 MOV AH, 9 INT 10H ;NOW PRINT TANK'S MIDDLE ROW DEC DH INC DL MOV AH, 2 ;SET CURSOR MOV BH, 0 ;VIDEO PAGE 0 INT 10H MOV AL, 'X' ;PRINT TANK 1 MOV CX, 3 ;5 WIDE MOV BL, TANK1COLOR ;RED ON BLACK MOV BH, 0 MOV AH, 9 INT 10H ;NOW PRINT TANK'S TOP ROW MOV DH, 8 MOV DL, TANK1POS ADD DL, 2 MOV AH, 2 MOV BH, 0 INT 10H MOV AL, 'O' MOV CX, 1 MOV BL, TANK1COLOR MOV BH, 0 MOV AH, 9 INT 10H MOV DH, 7 MOV DL, TANK1POS ADD DL, 3 MOV AH, 2 MOV BH, 0 INT 10H MOV AL, '/' MOV CX, 1 MOV BL, TANK1COLOR MOV BH, 0 MOV AH, 9 INT 10H MOV DH, 7 MOV DL, 12 CALL SETPOS MOV SI, 0 MOV CL, 0 ;INIT THE DELAY MOV AH, 0EH SPLASHPRNT: MOV AL, SPLASHMSG[SI] CMP AL, 1 ;CHECK IF WE'RE DONE JE WAITAMIN ;wait for key to be pressed INC SI INT 10H CMP AL, 0 ;CHECK IF ITS A NEW LINE ONLY JNE SPLASHPRNT INC DH MOV DL, 12 CALL SETPOS JMP SPLASHPRNT WAITAMIN: CALL DELAY MOV AH, 1 ; CHECK IF KEY PRESSED - MEANS QUIT INT 16H JNZ GETKEY JMP WAITAMIN GETKEY: MOV AH, 0 ; KEY WAS PRESSED -- READ IT INT 16H ; AND THROW IT AWAY! CALL CLEANUP POP SI POP DX POP CX POP BX POP AX RET SPLASH ENDP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; THE FOLLOWING CODE WAS TAKEN DIRECTLY FROM THE BOOK ;; TO CONVERT A NUMBER INTO ASCII CODE FOR PRINTING: ;; ;; START BOOK CODE ;; PRINTS ASCII NUMBERS FROM AL ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PASCII: ; MOV AL, BYTE PTR 1100H[DI] ;GET INPUT BINARY VALUE PUSH SI PUSH BX PUSH DI MOV BL, '0' ;INITIALIZE RESULT OF D1 MOV DI, 0 MOV AH, 0EH ;GET READY FOR PRINTING ;LOOP THROUGH TO COMPUTE THE HUNDREDS DIGIT HNL: CMP AL, 100 ;ANOTHER SUBTRACTION NEEDED? JB HNC ;JUMP IF NOT SUB AL, 100 ;ELSE SUBTRACT 100 INC BL ;BUMP HUNDREDS DIGIT JMP HNL ;AND LOOP BACK ;NOW STORE 100'S AND COMPUTE THE TENS DIGIT FROM THE REMAINDER HNC: MOV NUMSTORE, BL MOV BL, '0' ;LOOP TO COMPUTE TENS DIGIT BY SUBTRACTING 10 TNL: CMP AL, 10 ;ANOTHER SUB NEEDED? JB TNC ;JUMP IF NOT SUB AL, 10 ;ELSE SUBTRACT 10 INC BL ;BUMP 10'S DIGIT JMP TNL ;AND LOOP BACK ;STORE TENS, THEN STORE REMAINDER AND WE'RE DONE TNC: MOV SI, 1 MOV NUMSTORE[SI], BL ;STORE TENS DIGIT ADD AL, 30H ;CONVERT UNITS TO ASCII INC SI MOV NUMSTORE[SI], AL ;STORE UNITS DIGIT INC DI ;GET READY FOR NEXT ITERATION MOV BH, 0 ;AND PRINTING DEC SI DEC SI MOV AL, NUMSTORE[SI] ;PRINT THE HUNDREDS CMP AL, '0' ;IS AL 0? JNE PH ;IF NOT, PRINT IT MOV AL, ' ' ;IF IT IS, PRINT A SPACE.. PH: INT 10H INC SI MOV AL, NUMSTORE[SI] ;PRINT THE TENS CMP AL, '0' ;SAME PROCESS -- IS 10'S A 0?? JNE PT MOV AL, ' ' PT: INT 10H INC SI MOV AL, NUMSTORE[SI] ;PRINT THE ONES INT 10H POP DI POP BX POP SI RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; THIS PROCEDURE IS JUST TO KEEP THE CPU BUSY FOR A WHILE and play music ;; ALL credit to Dewar ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Routine to play note on speaker ; ; (AX) Frequency in Hz (32 - 32000) ; (DX) Duration in units of 1/100 second ; CALL NOTE ; ; Note: a frequency of zero, means rest (silence) for the indicated ; time, allowing this routine to be used simply as a timing delay. ; ; Definitions for timer gate control ; CTRL EQU 61H ; timer gate control port TIMR EQU 00000001B ; bit to turn timer on SPKR EQU 00000010B ; bit to turn speaker on ; ; Definitions of input/output ports to access timer chip ; TCTL EQU 043H ; port for timer control TCTR EQU 042H ; port for timer count values ; ; Definitions of timer control values (to send to control port) ; TSQW EQU 10110110B ; timer 2, 2 bytes, sq wave, binary LATCH EQU 10000000B ; latch timer 2 ; ; Define 32 bit value used to set timer frequency ; FRHI EQU 0012H ; timer frequency high (1193180 / 256) FRLO EQU 34DCH ; timer low (1193180 mod 256) ; DELAY PROC PUSH AX ; save registers PUSH DX MOV DH,25 ; get cursor off screen area MOV DL,0 ; (cleaner appearence) CALL SETPOS SUB AX,AX ; zero frequency for rest MOV DX,6 ; delay of 0.06 secs is reasonable CALL NOTE ; execute delay POP DX ; restore registers POP AX RET ; return to caller DELAY ENDP RCTR PROC MOV AL,LATCH ; latch the counter OUT TCTL,AL ; latch counter IN AL,TCTR ; read lsb of count MOV AH,AL IN AL,TCTR ; read msb of count XCHG AH,AL ; count is in AX RET ; return to caller RCTR ENDP NOTE PROC PUSH AX ; save registers PUSH BX PUSH CX PUSH DX PUSH SI MOV BX,AX ; save frequency in BX MOV CX,DX ; save duration in CX ; ; We handle the rest (silence) case by using an arbitrary frequency to ; program the clock so that the normal approach for getting the right ; delay functions, but we will leave the speaker off in this case. ; MOV SI,BX ; copy frequency to BX OR BX,BX ; test zero frequency (rest) JNZ NOT1 ; jump if not MOV BX,256 ; else reset to arbitrary non-zero ; ; Initialize timer and set desired frequency ; NOT1: MOV AL,TSQW ; set timer 2 in square wave mode OUT TCTL,AL MOV DX,FRHI ; set DX:AX = 1193180 decimal MOV AX,FRLO ; = clock frequency DIV BX ; divide by desired frequency OUT TCTR,AL ; output low order of divisor MOV AL,AH ; output high order of divisor OUT TCTR,AL ; ; Turn the timer on, and also the speaker (unless frequency 0 = rest) ; IN AL,CTRL ; read current contents of control port OR AL,TIMR ; turn timer on OR SI,SI ; test zero frequency JZ NOT2 ; skip if so (leave speaker off) OR AL,SPKR ; else turn speaker on as well ; ; Compute number of clock cycles required at this frequency ; NOT2: OUT CTRL,AL ; rewrite control port XCHG AX,BX ; frequency to AX MUL CX ; frequency times secs/100 to DX:AX MOV CX,100 ; divide by 100 to get number of beats DIV CX SHL AX,1 ; times 2 because two clocks/beat XCHG AX,CX ; count of clock cycles to CX ; ; Loop through clock cycles ; NOT3: CALL RCTR ; read initial count ; ; Loop to wait for clock count to get reset. The count goes from the ; value we set down to 0, and then is reset back to the set value ; NOT4: MOV DX,AX ; save previous count in DX CALL RCTR ; read count again CMP AX,DX ; compare new count : old count JB NOT4 ; loop if new count is lower LOOP NOT3 ; else reset, count down cycles ; ; Wait is complete, so turn off clock and return ; IN AL,CTRL ; read current contents of port AND AL,0FFH-TIMR-SPKR ; reset timer/speaker control bits ; note that the above statement is an equation OUT CTRL,AL ; rewrite control port POP SI ; restore registers POP DX POP CX POP BX POP AX RET ; return to caller NOTE ENDP ; ; Routine to play tune on speaker ; ; (SI) Pointer to tune list ; CALL TUNE ; ; The tune list is a series of word pairs. The first word is the ; duration in units of 1/100th of a second, and the second word ; is the frequency. An entry with zero duration ends the tune list ; and a zero frequency is a rest (period of silence). ; TUNE PROC PUSH AX ; save registers PUSH DX ; ; Loop through notes of the tune ; TUN1: LODSW ; load duration OR AX,AX ; test zero duration ending the list JZ TUN2 ; if so, end of tune ; ; Play next note ; XCHG AX,DX ; put duration in DX LODSW ; load frequency CALL NOTE ; play the note JMP TUN1 ; and loop back ; ; Here at end of tune ; TUN2: POP DX ; restore registers POP AX RET ; return to caller TUNE ENDP ; ;END DEWAR CREDIT ; XIT: call cleanup INT 20H END