├── .gitignore ├── readme-assets ├── chat.png ├── startmenu.png ├── gameoverview.png └── gameoverview2.png ├── dosbox-x-generated1.conf ├── dosbox-x-generated2.conf ├── LICENSE ├── run.py ├── game.asm ├── README.md ├── bar.asm ├── Bricks.asm ├── ball.asm └── MainMenu.asm /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore all .EXE and .OBJ files (optional) 2 | /*.OBJ 3 | /*.EXE 4 | /*.COM 5 | 6 | .vscode/ -------------------------------------------------------------------------------- /readme-assets/chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salehahmed99/brick-breaker/HEAD/readme-assets/chat.png -------------------------------------------------------------------------------- /readme-assets/startmenu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salehahmed99/brick-breaker/HEAD/readme-assets/startmenu.png -------------------------------------------------------------------------------- /readme-assets/gameoverview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salehahmed99/brick-breaker/HEAD/readme-assets/gameoverview.png -------------------------------------------------------------------------------- /readme-assets/gameoverview2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salehahmed99/brick-breaker/HEAD/readme-assets/gameoverview2.png -------------------------------------------------------------------------------- /dosbox-x-generated1.conf: -------------------------------------------------------------------------------- 1 | 2 | [cpu] 3 | cycles = max 4 | [sdl] 5 | fullresolution=640x400 6 | windowresolution=640x400 7 | output=openglpp 8 | [autoexec] 9 | mount C E:\\Uni\\Year2\\FirstTerm\\MicroProcessors\\Assembly\\Assemblyx69 10 | C: 11 | tasm /m2 *.asm 12 | link MainMenu.obj Bricks.obj bar.obj ball.obj; 13 | 14 | MainMenu.exe 15 | [serial] 16 | serial1=directserial realport:COM1 17 | -------------------------------------------------------------------------------- /dosbox-x-generated2.conf: -------------------------------------------------------------------------------- 1 | 2 | [cpu] 3 | cycles = max 4 | [sdl] 5 | fullresolution=640x400 6 | windowresolution=640x400 7 | output=openglpp 8 | [autoexec] 9 | mount C E:\\Uni\\Year2\\FirstTerm\\MicroProcessors\\Assembly\\Assemblyx69 10 | C: 11 | tasm /m2 *.asm 12 | link MainMenu.obj Bricks.obj bar.obj ball.obj; 13 | 14 | MainMenu.exe 15 | [serial] 16 | serial1=directserial realport:COM2 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Saleh Ahmed 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | from time import sleep 4 | 5 | filedata = r""" 6 | [cpu] 7 | cycles = max 8 | [sdl] 9 | fullresolution=640x400 10 | windowresolution=640x400 11 | output=openglpp 12 | [autoexec] 13 | mount C E:\\Uni\\Year2\\FirstTerm\\MicroProcessors\\Assembly\\Assemblyx69 14 | C: 15 | tasm /m2 *.asm 16 | link MainMenu.obj Bricks.obj bar.obj ball.obj; 17 | """ 18 | 19 | filedata += "\nMainMenu.exe" 20 | 21 | filedata1 = ( 22 | filedata 23 | + r""" 24 | [serial] 25 | serial1=directserial realport:COM1 26 | """ 27 | ) 28 | 29 | filedata2 = ( 30 | filedata 31 | + r""" 32 | [serial] 33 | serial1=directserial realport:COM2 34 | """ 35 | ) 36 | 37 | with open("dosbox-x-generated1.conf", "w") as file: 38 | file.write(filedata1) 39 | 40 | with open("dosbox-x-generated2.conf", "w") as file: 41 | file.write(filedata2) 42 | 43 | prog1 = ["C:\\Program Files (x86)\\DOSBox-0.74-3\\DOSBox.exe", "-conf", "dosbox-x-generated1.conf"] 44 | prog2 = ["C:\\Program Files (x86)\\DOSBox-0.74-3\\DOSBox.exe", "-conf", "dosbox-x-generated2.conf"] 45 | 46 | subprocess.Popen(prog1) 47 | sleep(5) 48 | subprocess.Popen(prog2) -------------------------------------------------------------------------------- /game.asm: -------------------------------------------------------------------------------- 1 | CLEAR_SCREEN MACRO 2 | mov ax, 0600h 3 | mov bh, 07h 4 | mov cx, 0 5 | mov dx, 184fh 6 | int 10h 7 | ENDM 8 | 9 | ; NO NEED FOR THIS FILE RIGHT NOW ;;ANAS IBRAHEM 10 | .model small 11 | .stack 100h 12 | 13 | .data 14 | 15 | PUBLIC Game 16 | ; ball data 17 | EXTRN BALL_X:WORD, BALL_Y:WORD, BALL_SIZE:WORD, BALL_VELOCITY_X:WORD, BALL_VELOCITY_Y:WORD 18 | ; bar data 19 | EXTRN BAR_X:WORD, BAR1_Y:WORD,BAR2_Y:WORD, BAR_LENGTH:WORD, BAR_HEIGHT:WORD, BAR_SPEED:WORD, BAR_COLOR:BYTE 20 | ; brick data 21 | EXTRN BRICK_X:WORD, BRICK_Y:WORD, INITIAL_X:WORD, INITIAL_Y:WORD, NUM_BRICKS_PER_LINE:WORD, NUM_BRICKS_PER_COLUMN:WORD, BRICK_WIDTH:WORD, BRICK_HEIGHT:WORD, COLOR_BRICK:BYTE , Gap:WORD, BRICKS_STATUS:BYTE 22 | 23 | .code 24 | ;bar procedures 25 | EXTRN DRAW_BAR1:NEAR, CLEAR_BAR1:NEAR, DRAW_BAR2:NEAR , CLEAR_BAR2:NEAR ,WAIT_FOR_VSYNC:NEAR, HANDLE_BAR_INPUT:FAR 26 | 27 | ;ball procedures 28 | EXTRN DRAW_BALL:NEAR, CLEAR_BALL:NEAR, MOVE_BALL:NEAR , CHECK_TIME:NEAR , CHECK_COLLISION:NEAR 29 | 30 | ;brick procedures 31 | EXTRN DRAW_BRICK:NEAR, DRAW_BRICKS:NEAR 32 | 33 | Game PROC FAR 34 | ; mov ax, @data 35 | ; mov ds, ax 36 | CLEAR_SCREEN 37 | mov ax,13h 38 | int 10h 39 | 40 | ;draw initial screen 41 | call DRAW_BRICKS 42 | call DRAW_BAR1 43 | call DRAW_BAR2 44 | call DRAW_BALL 45 | 46 | ;main loop 47 | game_loop: 48 | call HANDLE_BAR_INPUT 49 | call CHECK_TIME ; Check timing first 50 | call MOVE_BALL ; Includes clear, update, collision, draw 51 | jmp game_loop 52 | 53 | exit: 54 | mov ax, 4c00h 55 | int 21h 56 | ret 57 | Game ENDP 58 | END Game -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Brick Breaker Game - Developed in Assembly Language 2 | 3 | ## 🎮 About The Game 4 | 5 | Welcome to the classic **Brick Breaker** game, developed using **Assembly Language**! This retro arcade game challenges you to break all the bricks with a bouncing ball while controlling a paddle. Get ready for some nostalgic fun and see how long you can last! 6 | 7 | The game features: 8 | - **Sleek 2D graphics** rendered using low-level Assembly instructions 9 | - **Simple controls** that test your reflexes and precision 10 | - **Increasing levels of difficulty** as you clear bricks 11 | - **Score tracking** to see how you rank as you progress 12 | - **Optimized performance** for minimal CPU usage 13 | 14 | Are you ready to break some bricks and beat your high score? Let's play! 15 | 16 | ## 🚀 Features 17 | 18 | - **Paddle Control**: Move the paddle left and right using keyboard input. 19 | - **Ball Bounce**: The ball bounces off walls, bricks, and the paddle to clear the board. 20 | - **Multiple Levels**: The difficulty increases as you progress, with more bricks and faster gameplay. 21 | - **Score Tracking**: Keep track of your progress and try to beat your highest score. 22 | - **Retro Vibes**: A nostalgic 80s arcade-style game, built with the power of Assembly language! 23 | 24 | ## 🖥️ How It Works 25 | 26 | This game is coded in **x86 Assembly language**. It uses **BIOS interrupts** to handle input, display graphics, and manage game logic. Through this project, we explore low-level programming concepts like: 27 | - Handling keyboard input directly 28 | - Implementing simple 2D graphics using pixel manipulation 29 | - Optimizing code for minimal CPU usage 30 | 31 | ## 🛠️ Technologies Used 32 | 33 | - **Assembly Language** (x86) 34 | - **BIOS Interrupts** for graphics rendering and input handling 35 | - **Basic Game Loop** for continuous gameplay 36 | 37 | ## ⚡ How To Play 38 | 39 | 1. **Start the Game**: Run the program and watch the opening screen appear. 40 | 2. **Control the Paddle**: Use the left and right arrow keys to move the paddle and catch the ball. 41 | 3. **Break the Bricks**: The ball bounces off the paddle and hits the bricks. Try to break them all! 42 | 4. **Beat the Levels**: With each level, the game becomes more challenging. Can you clear all the levels? 43 | 5. **Track Your Score**: The score is displayed at the top of the screen. How high can you go? 44 | 45 | ## 🖼️ Screenshots 46 | 47 |
48 | 49 |
50 |

Start Menu

51 | Image 1 52 |
53 | 54 |
55 |

Game Overview

56 | Image 2 57 |
58 | 59 |
60 |

Difficult Level

61 | Image 2 62 |
63 | 64 |
65 |

Chat

66 | Image 3 67 |
68 | 69 |
70 | 71 | 72 | --- 73 | 74 | ### Thank you for playing! Let the brick-breaking begin! 🧱🎯 75 | -------------------------------------------------------------------------------- /bar.asm: -------------------------------------------------------------------------------- 1 | .model small 2 | .stack 100h 3 | .data 4 | PUBLIC BAR1_X, BAR2_X , BAR1_Y,BAR2_Y, BAR_LENGTH, BAR_HEIGHT, BAR_SPEED, BAR1_COLOR , BAR2_COLOR 5 | BAR1_X dw 80 6 | BAR1_Y dw 192 7 | BAR2_Y dw 192 8 | BAR2_X dw 80 9 | 10 | BAR_LENGTH dw 60 ; to be decreased by levels 11 | BAR_HEIGHT dw 6 12 | BAR_SPEED dw 5 ;to be inccreased by levels 13 | BAR1_COLOR db 0fh 14 | BAR2_COLOR db 0eh 15 | .code 16 | 17 | PUBLIC DRAW_BAR1, CLEAR_BAR1, DRAW_BAR2 , CLEAR_BAR2, WAIT_FOR_VSYNC, HANDLE_BAR_INPUT 18 | 19 | WAIT_FOR_VSYNC PROC FAR 20 | push ax 21 | push dx 22 | mov dx, 3DAh ; VGA status port 23 | vsync_wait1: 24 | in al, dx 25 | test al, 8 ; Check vertical retrace 26 | jnz vsync_wait1 ; Wait if already in retrace 27 | vsync_wait2: 28 | in al, dx 29 | test al, 8 ; Wait for vertical retrace 30 | jz vsync_wait2 31 | pop dx 32 | pop ax 33 | ret 34 | WAIT_FOR_VSYNC ENDP 35 | 36 | DRAW_OR_CLEAR_BAR1 PROC FAR 37 | push ax 38 | push cx 39 | push dx 40 | mov BAR1_COLOR, al 41 | mov cx, BAR1_X ;col 42 | mov dx, BAR1_Y ;row 43 | draw_bar1_horizontal: 44 | mov ah, 0ch 45 | mov al, BAR1_COLOR 46 | int 10h 47 | inc cx 48 | mov ax, BAR1_X 49 | add ax, BAR_LENGTH 50 | cmp cx, ax ;next col 51 | jb draw_bar1_horizontal 52 | inc dx 53 | mov ax, BAR1_Y 54 | add ax, BAR_HEIGHT 55 | mov cx, BAR1_X ;return to starting column 56 | cmp dx, ax 57 | jb draw_bar1_horizontal ; move to next row 58 | pop dx 59 | pop cx 60 | pop ax 61 | ret 62 | DRAW_OR_CLEAR_BAR1 ENDP 63 | 64 | ;two mini procedures to draw and clear the bar 65 | 66 | DRAW_BAR1 PROC FAR 67 | push ax 68 | mov al, 0fh 69 | call far ptr DRAW_OR_CLEAR_BAR1 70 | pop ax 71 | ret 72 | DRAW_BAR1 ENDP 73 | 74 | CLEAR_BAR1 PROC FAR 75 | push ax 76 | mov al, 0h 77 | call far ptr DRAW_OR_CLEAR_BAR1 78 | pop ax 79 | ret 80 | CLEAR_BAR1 ENDP 81 | 82 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 83 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 84 | 85 | 86 | DRAW_OR_CLEAR_BAR2 PROC FAR 87 | push ax 88 | push cx 89 | push dx 90 | mov BAR2_COLOR, al 91 | mov cx, BAR2_X ;col 92 | mov dx, BAR2_Y ;row 93 | draw_bar2_horizontal: 94 | mov ah, 0ch 95 | mov al, BAR2_COLOR 96 | int 10h 97 | inc cx 98 | mov ax, BAR2_X 99 | add ax, BAR_LENGTH 100 | cmp cx, ax ;next col 101 | jb draw_bar2_horizontal 102 | inc dx 103 | mov ax, BAR2_Y 104 | add ax, BAR_HEIGHT 105 | mov cx, BAR2_X ;return to starting column 106 | cmp dx, ax 107 | jb draw_bar2_horizontal ; move to next row 108 | pop dx 109 | pop cx 110 | pop ax 111 | ret 112 | DRAW_OR_CLEAR_BAR2 ENDP 113 | 114 | ;two mini procedures to draw and clear the bar 115 | 116 | DRAW_BAR2 PROC FAR 117 | push ax 118 | mov al, 0eh 119 | call far ptr DRAW_OR_CLEAR_BAR2 120 | pop ax 121 | ret 122 | DRAW_BAR2 ENDP 123 | 124 | CLEAR_BAR2 PROC FAR 125 | push ax 126 | mov al, 0h 127 | call far ptr DRAW_OR_CLEAR_BAR2 128 | pop ax 129 | ret 130 | CLEAR_BAR2 ENDP 131 | 132 | 133 | 134 | 135 | HANDLE_BAR_INPUT PROC FAR 136 | ;check if the user pressed a key 137 | check_input: 138 | mov ah, 01h 139 | int 16h 140 | jnz skip_handle_input 141 | jmp handle_input_end;if no key pressed, continue the loop 142 | skip_handle_input: 143 | ; check which key was pressed 144 | mov ah, 00h 145 | int 16h 146 | ; use scan codes to check which key was pressed 147 | 148 | ; check space bar 149 | cmp ah, 39h 150 | je pause_game 151 | 152 | 153 | ; left and right arrow keys have scan codes 4b and 4d 154 | cmp ah, 4bh 155 | je move_left_bar1 156 | 157 | cmp ah, 4dh 158 | je move_right_bar1 159 | 160 | 161 | cmp al , 'a' 162 | je move_left_bar2 163 | 164 | cmp al ,'d' 165 | jne skip_move_right_bar2_1 166 | jmp move_right_bar2 167 | skip_move_right_bar2_1: 168 | jmp handle_input_end ;if any other key is pressed, continue the loop 169 | 170 | pause_game: 171 | mov ah, 02h 172 | mov bh, 0 173 | mov dh, 12 174 | mov dl, 15 175 | int 10h 176 | 177 | 178 | wait_resume: 179 | mov ah, 01h 180 | int 16h 181 | jz wait_resume 182 | 183 | jmp handle_input_end 184 | 185 | 186 | move_left_bar1: 187 | ;check if the bar is at the left edge of the screen 188 | mov ax, BAR1_X 189 | cmp ax, 0 190 | jg skip2_handle_input;if the bar is at the left edge, continue the loop 191 | jmp handle_input_end 192 | 193 | skip2_handle_input: 194 | ;call far ptr WAIT_FOR_VSYNC 195 | ;clear the bar 196 | call far ptr CLEAR_BAR1 197 | ;move the bar to the left 198 | mov ax, BAR1_X 199 | sub ax, BAR_SPEED 200 | mov BAR1_X, ax 201 | ;draw the bar 202 | call far ptr DRAW_BAR1 203 | call far ptr DRAW_BAR2 204 | jmp check_input 205 | 206 | move_right_bar1: 207 | ;check if the bar is at the right edge of the screen 208 | mov ax, BAR1_X 209 | add ax, BAR_LENGTH 210 | cmp ax, 319 211 | jge handle_input_end ;if the bar is at the right edge, continue the loop 212 | ;call far ptr WAIT_FOR_VSYNC 213 | ;clear the bar 214 | call far ptr CLEAR_BAR1 215 | ;move the bar to the right 216 | mov ax, BAR1_X 217 | add ax, BAR_SPEED 218 | mov BAR1_X, ax 219 | ;draw the bar 220 | call far ptr DRAW_BAR1 221 | call far ptr DRAW_BAR2 222 | jmp check_input 223 | 224 | 225 | 226 | 227 | move_left_bar2: 228 | ;check if the bar is at the left edge of the screen 229 | mov ax, BAR2_X 230 | cmp ax, 0 231 | jle handle_input_end;if the bar is at the left edge, continue the loop 232 | ;call far ptr WAIT_FOR_VSYNC 233 | ;clear the bar 234 | call far ptr CLEAR_BAR2 235 | ;move the bar to the left 236 | mov ax, BAR2_X 237 | sub ax, BAR_SPEED 238 | mov BAR2_X, ax 239 | ;draw the bar 240 | call far ptr DRAW_BAR2 241 | call far ptr DRAW_BAR1 242 | jmp check_input 243 | 244 | move_right_bar2: 245 | ;check if the bar is at the right edge of the screen 246 | mov ax, BAR2_X 247 | add ax, BAR_LENGTH 248 | cmp ax, 319 249 | jge handle_input_end ;if the bar is at the right edge, continue the loop 250 | ;call far ptr WAIT_FOR_VSYNC 251 | ;clear the bar 252 | call far ptr CLEAR_BAR2 253 | ;move the bar to the right 254 | mov ax, BAR2_X 255 | add ax, BAR_SPEED 256 | mov BAR2_X, ax 257 | ;draw the bar 258 | call far ptr DRAW_BAR2 259 | call far ptr DRAW_BAR1 260 | jmp check_input 261 | 262 | handle_input_end: 263 | ret 264 | HANDLE_BAR_INPUT ENDP 265 | end -------------------------------------------------------------------------------- /Bricks.asm: -------------------------------------------------------------------------------- 1 | .MODEL small 2 | .STACK 100h 3 | .data 4 | PUBLIC BRICK_X, BRICK_Y, INITIAL_X, INITIAL_Y, NUM_BRICKS_PER_LINE, NUM_BRICKS_PER_COLUMN, BRICK_WIDTH, BRICK_HEIGHT, COLOR_BRICK, Gap, BRICKS_STATUS , CURRENT_SCORE 5 | BRICK_X dw 0ah 6 | BRICK_Y dw 0ah 7 | INITIAL_X EQu 0ah 8 | INITIAL_Y EQu 0ah 9 | NUM_BRICKS_PER_LINE EQu 10 10 | NUM_BRICKS_PER_COLUMN EQu 4 11 | BRICK_WIDTH dw 1ah ; brick width 26 pixels 12 | BRICK_HEIGHT dw 0fh ; brick height 10 pixels 13 | COLOR_BRICK db 01h ; color of the brick 14 | Gap EQu 4 15 | BRICKS_STATUS db 10 dup(4) ; 40 bricks 16 | db 10 dup(3) 17 | db 2 dup(2) 18 | db 1 dup(5) 19 | db 1 dup(2) 20 | db 1 dup(6) 21 | db 2 dup(2) 22 | db 1 dup(5) 23 | db 2 dup(2) 24 | db 10 dup(1) 25 | 26 | 27 | CURRENT_SCORE db 0 28 | 29 | ;screen format | 10 26 4 26 4 ......26 10| each 26 is the brick and 4 is the gap between bricks and there is padding 10 pixels 30 | ; | 10 4 4 4 ...........4| 31 | ; | 10 26 4 26 4 ......26 10| 32 | .code 33 | 34 | 35 | PUBLIC DRAW_BRICK, DRAW_BRICKS, RESET_BRICKS_STATUS 36 | 37 | RESET_BRICKS_STATUS proc far 38 | push ax 39 | push cx 40 | push si 41 | 42 | mov cx, 10 ; First 3 rows (10 bricks * 3) 43 | mov si, offset BRICKS_STATUS 44 | mov al, 4 ; Strong bricks 45 | reset_red: 46 | mov [si], al 47 | inc si 48 | loop reset_red 49 | 50 | mov cx, 10 ; Last row 51 | mov al, 3 ; Weak bricks 52 | reset_yellow: 53 | mov [si], al 54 | inc si 55 | loop reset_yellow 56 | 57 | 58 | mov cx, 4 ; Last row 59 | mov al, 2 ; Weak bricks 60 | reset_green_left: 61 | mov [si], al 62 | inc si 63 | loop reset_green_left 64 | 65 | 66 | mov cx, 2 ; Last row 67 | mov al, 5 ; Weak bricks 68 | reset_magenta: 69 | mov [si], al 70 | inc si 71 | loop reset_magenta 72 | 73 | mov cx, 4 ; Last row 74 | mov al, 2 ; Weak bricks 75 | reset_green_right: 76 | mov [si], al 77 | inc si 78 | loop reset_green_right 79 | 80 | mov cx, 10 ; Last row 81 | mov al, 1 ; Weak bricks 82 | reset_blue: 83 | mov [si], al 84 | inc si 85 | loop reset_blue 86 | 87 | pop si 88 | pop cx 89 | pop ax 90 | ret 91 | RESET_BRICKS_STATUS endp 92 | 93 | 94 | DRAW_BRICK Proc FAR 95 | push ax 96 | push cx 97 | push dx 98 | 99 | mov cx , BRICK_X ;brick intial position x 100 | mov dx, BRICK_Y ;brick intial position x 101 | 102 | draw_horizontal: 103 | 104 | mov al,COLOR_BRICK ; color of the pixel 105 | mov ah,0ch ;draw pixel 106 | mov bh,00h 107 | int 10h ; excute the command 108 | 109 | 110 | inc cx ; cx = cx +1 111 | 112 | mov ax,cx ; cx - brick_x > brick_width then draw the next line 113 | sub ax,BRICK_X ; subtract the current_x with the inital_x to get the number of drawn pixels if greater than width go next line 114 | cmp ax,BRICK_WIDTH 115 | jng draw_horizontal ; if not greater complete drawing the line 116 | mov cx , BRICK_X ; if greater go for next line 117 | inc dx 118 | 119 | mov ax ,dx ; same as above but for the height 120 | sub ax,BRICK_Y 121 | cmp ax,BRICK_HEIGHT ;compare the current height with the brick_height if not greater then draw the line if not return and the brick is drawn 122 | jng draw_horizontal 123 | 124 | pop dx 125 | pop cx 126 | pop ax 127 | ret 128 | 129 | DRAW_BRICK endp 130 | 131 | DRAW_BRICKS proc FAR 132 | push ax 133 | push cx 134 | push dx 135 | 136 | ; mov cx, 0 ; Outer loop counter (rows) 137 | mov dx , 0 138 | outer_loop: 139 | ; push cx ; Save the outer loop counter 140 | 141 | mov cx, 0 ; Inner loop counter (bricks per row) 142 | inner_loop: 143 | 144 | ; Step 1: Calculate the LiFAR Index 145 | push dx 146 | mov ax, dx ; AX = row index (i) 147 | mov di , NUM_BRICKS_PER_LINE 148 | mul di ; AX = i * NUM_BRICKS_PER_LINE 149 | add ax, cx ; AX = i * NUM_BRICKS_PER_LINE + j 150 | 151 | ; Step 2: Compute the Address 152 | mov si, offset BRICKS_STATUS ; SI = base address of BRICKS_STATUS 153 | add si, ax ; SI = address of BRICKS_STATUS[i][j] 154 | 155 | pop dx 156 | 157 | ; Check brick status and set color 158 | mov bl, [si] ; Get brick status 159 | cmp bl , 6 160 | jne check_status_5 161 | mov COLOR_BRICK , 0ch 162 | jmp cont 163 | check_status_5: 164 | cmp bl , 5 165 | jne check_status_4 166 | mov COLOR_BRICK, 05h ; Magenta color 167 | jmp cont 168 | check_status_4: 169 | cmp bl ,4 ; Check if status is 4 170 | jne check_status_3 171 | mov COLOR_BRICK, 04h ; Red color 172 | jmp cont 173 | check_status_3: 174 | cmp bl, 3 ; Check if status is 3 175 | jne check_status_2 176 | mov COLOR_BRICK, 0eh ; yellow color 177 | jmp cont 178 | 179 | check_status_2: 180 | cmp bl, 2 ; Check if status is 2 181 | jne check_status_1 182 | mov COLOR_BRICK, 02h ; Green color 183 | jmp cont 184 | 185 | check_status_1: 186 | cmp bl, 1 ; Check if status is 1 187 | jne status_zero 188 | mov COLOR_BRICK, 01h ; Blue color 189 | jmp cont 190 | 191 | status_zero: 192 | mov COLOR_BRICK, 00h ; Black color 193 | 194 | cont: 195 | push bx 196 | call far ptr DRAW_BRICK ; Draw one brick 197 | pop bx 198 | 199 | next: 200 | 201 | mov ax, BRICK_X ; Move to the next brick horizontally 202 | add ax, BRICK_WIDTH ; Add the brick width 203 | add ax, Gap ; Add the horizontal gap 204 | mov BRICK_X, ax ; Update BRICK_X position 205 | inc cx 206 | cmp cx , NUM_BRICKS_PER_LINE 207 | jl inner_loop ; Continue drawing bricks in the row 208 | 209 | ; pop cx ; Restore the outer loop counter 210 | inc dx 211 | cmp dx , NUM_BRICKS_PER_COLUMN ; Move to the next row 212 | je done ; If all rows are drawn, exit 213 | 214 | mov ax, BRICK_Y ; Move to the next row verticall far ptry 215 | add ax, BRICK_HEIGHT ; Add the brick height 216 | add ax, Gap ; Add the vertical gap 217 | mov BRICK_Y, ax ; Update BRICK_Y position 218 | 219 | mov BRICK_X, INITIAL_X ; Reset BRICK_X to the initial position 220 | jmp outer_loop ; Continue drawing the next row 221 | 222 | 223 | 224 | done: 225 | mov BRICK_X, INITIAL_X ; Reset BRICK_X to the initial position 226 | mov BRICK_Y, INITIAL_Y ; Reset BRICK_Y to the initial position 227 | pop dx 228 | pop cx 229 | pop ax 230 | ret 231 | DRAW_BRICKS endp 232 | 233 | end -------------------------------------------------------------------------------- /ball.asm: -------------------------------------------------------------------------------- 1 | .model small 2 | .stack 100h 3 | .data 4 | PUBLIC PREV_TIME_STEP, BALL_X, BALL_Y, BALL_SIZE, BALL_VELOCITY_X, BALL_VELOCITY_Y 5 | PREV_TIME_STEP DB 0h 6 | BALL_X DW 0a0h 7 | BALL_Y DW 64h 8 | INITIAL_BALL_X EQu 0a0h 9 | INITIAL_BALL_Y EQu 64h 10 | BALL_SIZE DW 06h 11 | BALL_VELOCITY_X DW 06h 12 | BALL_VELOCITY_Y DW 03h 13 | INITIAL_BALL_VELOCITY_X EQu 06h 14 | INITIAL_BALL_VELOCITY_Y EQu 03h 15 | GAME_OVER_MSG db 'Game Over - Press any key to continue$' 16 | GAME_WIN_MSG db 'Congratulations! - Press any key to continue$' 17 | 18 | EXTRN BAR1_X:WORD,BAR2_X:WORD, BAR1_Y:WORD,BAR2_Y:WORD, BAR_LENGTH:WORD, BAR_HEIGHT:WORD , LIVES_COUNT:BYTE , READY1:BYTE , READY2:BYTE 19 | EXTRN NUM_BRICKS_PER_LINE:WORD, NUM_BRICKS_PER_COLUMN:WORD, BRICK_WIDTH:WORD, BRICK_HEIGHT:WORD, COLOR_BRICK:BYTE, BRICKS_STATUS:BYTE, INITIAL_X:WORD, INITIAL_Y:WORD, Gap:WORD ,CURRENT_SCORE:BYTE 20 | 21 | .CODE 22 | 23 | PUBLIC DRAW_BALL, CLEAR_BALL, MOVE_BALL , CHECK_TIME , CHECK_COLLISION 24 | EXTRN WAIT_FOR_VSYNC:FAR, DRAW_BRICKS:FAR , CLEAR_SCREEN_PROC:FAR , main:FAR, DRAW_BAR:FAR, RESET_BRICKS_STATUS:FAR , CLEAR_BAR1:FAR, CLEAR_BAR2:FAR , DRAW_BAR1:FAR, DRAW_BAR2:FAR 25 | 26 | 27 | DRAW_BALL PROC FAR 28 | 29 | mov cx , BALL_X ;set the initial x position of the ball 30 | mov dx , BALL_Y ;set the initial y position of the ball 31 | 32 | draw_horizontal: 33 | mov ah , 0ch ;draw pixel command 34 | mov al , 0bh ;set the color of the ball 35 | int 10h ;interrupt to draw the pixel 36 | inc cx ;increment x position 37 | mov ax , BALL_X 38 | add ax , BALL_SIZE 39 | cmp cx , ax ;check if x position is less than the (size of the ball + initial x position) 40 | jne draw_horizontal 41 | mov cx , BALL_X ;reset x position to initial x position 42 | inc dx ;increment y position 43 | mov ax , BALL_Y 44 | add ax , BALL_SIZE 45 | cmp dx , ax ;check if y position is less than the (size of the ball + initial y position) 46 | jne draw_horizontal 47 | 48 | RET 49 | DRAW_BALL ENDP 50 | 51 | CLEAR_BALL PROC FAR 52 | mov cx , BALL_X ;set the initial x position of the ball 53 | mov dx , BALL_Y ;set the initial y position of the ball 54 | 55 | clear_horizontal: 56 | mov ah , 0ch ;draw pixel command 57 | mov al , 0 ;set the color to black 58 | int 10h ;interrupt to draw the pixel 59 | inc cx ;increment x position 60 | mov ax , BALL_X 61 | add ax , BALL_SIZE 62 | cmp cx , ax ;check if x position is less than the (size of the ball + initial x position) 63 | jne clear_horizontal 64 | mov cx , BALL_X ;reset x position to initial x position 65 | inc dx ;increment y position 66 | mov ax , BALL_Y 67 | add ax , BALL_SIZE 68 | cmp dx , ax ;check if y position is less than the (size of the ball + initial y position) 69 | jne clear_horizontal 70 | 71 | RET 72 | CLEAR_BALL ENDP 73 | 74 | 75 | 76 | CHECK_TIME PROC FAR 77 | check_time_: 78 | mov ah , 2ch ; get the current time 79 | int 21h ; ch = hour , cl = minutes , dh = seconds , dl = 1/100 seconds 80 | 81 | cmp dl , PREV_TIME_STEP ; Compare current time step with previous time step 82 | ;je check_time_ 83 | 84 | mov PREV_TIME_STEP , dl ; Update previous time step 85 | ret 86 | CHECK_TIME ENDP 87 | 88 | 89 | 90 | MOVE_BALL PROC FAR 91 | ;call far ptr WAIT_FOR_VSYNC ; Sync with screen refresh 92 | call far ptr CLEAR_BALL ; Clear old position 93 | 94 | ; Update position 95 | mov ax, BALL_X 96 | add ax, BALL_VELOCITY_X 97 | mov BALL_X, ax 98 | 99 | mov ax, BALL_Y 100 | add ax, BALL_VELOCITY_Y 101 | mov BALL_Y, ax 102 | 103 | call far ptr CHECK_COLLISION ; Check for collision 104 | call far ptr DRAW_BALL ; Draw at new position 105 | ret 106 | MOVE_BALL ENDP 107 | 108 | 109 | CHECK_COLLISION PROC FAR 110 | push ax 111 | ; Check for collision with screen edges 112 | cmp BALL_X , 0 113 | jle collision_x_left ;check if x position is less than 0 114 | 115 | mov ax , BALL_X 116 | add ax , BALL_SIZE 117 | cmp ax , 320 118 | jge collision_x_right ;check if x position is greater than 320 119 | 120 | 121 | 122 | mov ax , BALL_Y 123 | add ax , BALL_SIZE 124 | cmp ax , 200 125 | jge collision_y_down ;check if y position is greater than 200 126 | 127 | 128 | cmp BALL_Y , 10 129 | jle collision_y_up ;check if y position is less than 0 130 | 131 | 132 | call far ptr CHECK_BAR1_COLLISION 133 | call far ptr CHECK_BAR2_COLLISION 134 | call far ptr CHECK_BRICKS_COLLISION 135 | 136 | pop ax 137 | ret 138 | 139 | 140 | collision_x_left: 141 | mov BALL_X , 0 ;set x position to 0 142 | neg BALL_VELOCITY_X ;negate the velocity 143 | pop ax 144 | ret 145 | 146 | collision_x_right: 147 | mov ax , 320 148 | sub ax , BALL_SIZE 149 | mov BALL_X , ax ;set x position to 320 - BALL_SIZE 150 | neg BALL_VELOCITY_X ;negate the velocity 151 | pop ax 152 | ret 153 | 154 | collision_y_down: 155 | dec LIVES_COUNT 156 | cmp LIVES_COUNT, 0 157 | jne continue_game 158 | jmp pause_game 159 | pop ax 160 | ret 161 | 162 | collision_y_up: 163 | mov BALL_Y , 10 ;set y position to 0 164 | neg BALL_VELOCITY_Y ;negate the velocity 165 | 166 | pop ax 167 | ret 168 | ; Stop ball 169 | pause_game: 170 | ; mov BALL_VELOCITY_X, 0 171 | ; mov BALL_VELOCITY_Y, 0 172 | ; Position cursor 173 | mov ah, 02h 174 | mov bh, 0 175 | mov dh, 12 176 | mov dl, 2 177 | int 10h 178 | 179 | ; Show game over message 180 | mov ah, 09h 181 | mov dx, offset GAME_OVER_MSG 182 | int 21h 183 | 184 | wait_key: 185 | ; Wait for keypress 186 | mov ah, 01h ; Check if key available 187 | int 16h 188 | jz wait_key ; If no key, keep waiting 189 | 190 | ; mov ah, 00h ; Get the key 191 | ; int 16h ; Clear key from buffer 192 | 193 | ; Now do reset sequence 194 | call far ptr CLEAR_SCREEN_PROC 195 | mov LIVES_COUNT, 3 196 | mov CURRENT_SCORE, 0 197 | mov BALL_X, INITIAL_BALL_X 198 | mov BALL_Y, INITIAL_BALL_Y 199 | mov READY1 , 0 200 | mov READY2 , 0 201 | mov BALL_VELOCITY_X, INITIAL_BALL_VELOCITY_X 202 | mov BALL_VELOCITY_Y, INITIAL_BALL_VELOCITY_Y 203 | 204 | call far ptr RESET_BRICKS_STATUS 205 | call far ptr DRAW_BRICKS 206 | jmp far ptr main 207 | 208 | continue_game: 209 | mov BALL_X, INITIAL_BALL_X ; Reset position for normal life loss 210 | mov BALL_Y, INITIAL_BALL_Y 211 | pop ax 212 | ret 213 | CHECK_COLLISION ENDP 214 | 215 | CHECK_BAR1_COLLISION PROC FAR 216 | push ax 217 | push bx 218 | ; Check if ball's bottom touches bar's top 219 | mov ax, BALL_Y 220 | add ax, BALL_SIZE ; Get ball's bottom edge 221 | cmp ax, BAR1_Y ; Compare with bar's top 222 | jl no_collision1 ; Ball is above bar 223 | 224 | ; Check horizontal overlap 225 | mov ax, BALL_X ; Ball's left edge 226 | add ax, BALL_SIZE ; Ball's right edge 227 | cmp ax, BAR1_X ; Compare with bar's left 228 | jl no_collision1 ; Ball is left of bar 229 | 230 | mov ax, BALL_X 231 | mov bx, BAR1_X 232 | add bx, BAR_LENGTH 233 | cmp ax, bx ; Compare with bar's right 234 | jg no_collision1 ; Ball is right of bar 235 | 236 | ; Collision detected - bounce ball 237 | neg BALL_VELOCITY_Y 238 | 239 | ;ensure the ball doesn't penetrate the bar 240 | mov ax, BAR1_Y 241 | sub ax, BALL_SIZE 242 | mov BALL_Y, ax 243 | call far ptr DRAW_BALL 244 | no_collision1: 245 | pop bx 246 | pop ax 247 | ret 248 | CHECK_BAR1_COLLISION ENDP 249 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 250 | 251 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 252 | CHECK_BAR2_COLLISION PROC FAR 253 | push ax 254 | push bx 255 | 256 | ; Only check if ball is moving down 257 | mov ax, BALL_VELOCITY_Y 258 | cmp ax, 0 259 | jl no_collision2 ; Skip if moving up 260 | 261 | ; Check if ball's bottom edge is at bar's top 262 | mov ax, BALL_Y 263 | add ax, BALL_SIZE ; Ball bottom edge 264 | cmp ax, BAR2_Y ; Compare with bar top 265 | jl no_collision2 ; Ball above bar 266 | 267 | ; Check if ball's top edge is past bar's bottom 268 | mov ax, BALL_Y 269 | mov bx, BAR2_Y 270 | add bx, BAR_HEIGHT 271 | cmp ax, bx ; Compare ball top with bar bottom 272 | jg no_collision2 ; Ball below bar 273 | 274 | ; Check horizontal overlap 275 | mov ax, BALL_X ; Ball's left edge 276 | add ax, BALL_SIZE ; Ball's right edge 277 | cmp ax, BAR2_X ; Compare with bar's left 278 | jl no_collision2 ; Ball is left of bar 279 | 280 | mov ax, BALL_X 281 | mov bx, BAR2_X 282 | add bx, BAR_LENGTH 283 | cmp ax, bx ; Compare with bar's right 284 | jg no_collision2 ; Ball is right of bar 285 | 286 | ; Collision detected - bounce ball 287 | neg BALL_VELOCITY_Y 288 | 289 | ; Position correction 290 | mov ax, BAR2_Y 291 | sub ax, BALL_SIZE 292 | mov BALL_Y, ax 293 | 294 | no_collision2: 295 | pop bx 296 | pop ax 297 | ret 298 | CHECK_BAR2_COLLISION ENDP 299 | 300 | 301 | CHECK_BRICKS_COLLISION PROC FAR 302 | push ax 303 | push bx 304 | push cx 305 | push dx 306 | push si 307 | 308 | ; Check ball position against brick zone 309 | mov ax, BALL_Y 310 | cmp ax, 10 ; INITIAL_Y 311 | jge skip_no_brick_collision ; Ball is above the brick zone 312 | jmp short_no_brick_collision 313 | 314 | skip_no_brick_collision: 315 | 316 | cmp ax, 80 ; Bottom of brick zone 317 | jle short skip_short_no_brick_collision 318 | jmp short_no_brick_collision ; Ball is below the brick zone 319 | skip_short_no_brick_collision: 320 | 321 | ; Get current brick position 322 | mov ax, BALL_Y ; Current position y of the ball 323 | sub ax, 10 ; Subtract INITIAL_Y to account for the first row 324 | mov bx, 19 ; Brick height + gap ,,, 15+4 325 | xor dx, dx 326 | div bx ; Divide by height of brick to determine row 327 | cmp ax, NUM_BRICKS_PER_COLUMN ; Check row bounds, we only want 0,1,2,3 328 | jl skip_no_brick_collision3 329 | jmp short_no_brick_collision ; Out of bounds 330 | 331 | skip_no_brick_collision3: 332 | mov cx, ax ; Save row 333 | 334 | ; Determine which brick in the row 335 | mov ax, BALL_X 336 | cmp ax , 10 337 | jl skip_short_no_brick_collision4 338 | sub ax, 10 ; Subtract INITIAL_X 339 | mov bx, 30 ; Brick width + gap 340 | xor dx, dx 341 | div bx ; Divide by width of brick to determine column 342 | cmp ax, NUM_BRICKS_PER_LINE ; Check column bounds, we want 0-9 343 | jl skip_short_no_brick_collision4 344 | jmp short_no_brick_collision ; Out of bounds 345 | 346 | skip_short_no_brick_collision4: 347 | 348 | ; We have the row and column of the brick 349 | ; Row * NUM_BRICKS_PER_LINE + column ,,,, here NUM_BRICKS_PER_LINE = 10 350 | ; Get brick index 351 | push ax ; Save column 352 | mov ax, cx ; Row 353 | mov bx, 010 ; NUM_BRICKS_PER_LINE 354 | mul bx ; Row * width 355 | pop bx ; Get column 356 | add ax, bx ; Final index 357 | 358 | ; Check and update brick 359 | mov si, offset BRICKS_STATUS 360 | add si, ax 361 | cmp byte ptr [si], 0 ; Brick already destroyed 362 | jne skip_no_brick_collision66 363 | jmp short_no_brick_collision ; No collision 364 | skip_no_brick_collision66: 365 | cmp byte ptr [si], 6 366 | jne check_magenta 367 | sub byte ptr [si] , 5 368 | push si 369 | dec si 370 | mov byte ptr [si] , 0 371 | add si , 2 372 | mov byte ptr [si] ,0 373 | pop si 374 | jmp decrement_once 375 | check_magenta: 376 | cmp byte ptr [si],5 377 | jne decrement_once 378 | inc LIVES_COUNT ; Destroy brick 379 | sub byte ptr [si] , 4 380 | decrement_once: 381 | dec byte ptr [si] ; Destroy brick 382 | cmp byte ptr [si], 0 383 | jne continue_ 384 | inc CURRENT_SCORE 385 | cmp CURRENT_SCORE, 10 386 | jne check_win 387 | call far ptr CLEAR_BAR1 388 | call far ptr CLEAR_BAR2 389 | sub BAR_LENGTH , 10 390 | call far ptr DRAW_BAR1 391 | call far ptr DRAW_BAR2 392 | 393 | check_win: 394 | cmp CURRENT_SCORE , 40 395 | jne continue_ 396 | jmp pause_game_2 397 | 398 | 399 | pause_game_2: 400 | ; mov BALL_VELOCITY_X, 0 401 | ; mov BALL_VELOCITY_Y, 0 402 | ; Position cursor 403 | mov ah, 02h 404 | mov bh, 0 405 | mov dh, 12 406 | mov dl, 2 407 | int 10h 408 | 409 | ; Show game over message 410 | mov ah, 09h 411 | mov dx, offset GAME_WIN_MSG 412 | int 21h 413 | 414 | wait_key_2: 415 | ; Wait for keypress 416 | mov ah, 01h ; Check if key available 417 | int 16h 418 | jz wait_key_2 ; If no key, keep waiting 419 | 420 | ; mov ah, 00h ; Get the key 421 | ; int 16h ; Clear key from buffer 422 | 423 | ; Now do reset sequence 424 | call far ptr CLEAR_SCREEN_PROC 425 | mov LIVES_COUNT, 3 426 | mov CURRENT_SCORE, 0 427 | mov BALL_X, INITIAL_BALL_X 428 | mov BALL_Y, INITIAL_BALL_Y 429 | mov READY1 , 0 430 | mov READY2 , 0 431 | mov BALL_VELOCITY_X, INITIAL_BALL_VELOCITY_X 432 | mov BALL_VELOCITY_Y, INITIAL_BALL_VELOCITY_Y 433 | 434 | call far ptr RESET_BRICKS_STATUS 435 | call far ptr DRAW_BRICKS 436 | jmp far ptr main 437 | 438 | 439 | continue_: 440 | ; Check collision type 441 | mov ax, BALL_VELOCITY_Y 442 | cmp ax, 0 443 | jl short_vertical_hit ; If moving up, handle as vertical hit 444 | 445 | ; Check for side collision 446 | mov ax, BALL_X 447 | add ax, BALL_SIZE ; Add ball size to current position 448 | mov bx, cx ; Get saved row 449 | push ax ; Save ball position 450 | mov ax, bx 451 | mov bx, 30 ; Brick width + gap 452 | mul bx ; Row * brick width 453 | add ax, INITIAL_X ; Add INITIAL_X 454 | mov bx, ax ; BX = brick left edge 455 | pop ax ; Restore ball position 456 | 457 | cmp ax, bx ; Compare with brick left edge 458 | jl short_side_hit ; Ball is to the left of the brick 459 | add bx, 26 ; Add BRICK_WIDTH 460 | cmp ax, bx ; Compare with brick right edge 461 | jg short_side_hit ; Ball is to the right of the brick 462 | 463 | short_vertical_hit: 464 | neg BALL_VELOCITY_Y ; Vertical bounce 465 | jmp short_hit_done ; Skip to hit done 466 | 467 | short_side_hit: 468 | neg BALL_VELOCITY_X ; Horizontal bounce 469 | 470 | short_hit_done: 471 | call far ptr DRAW_BRICKS ; Update display 472 | 473 | short_no_brick_collision: 474 | pop si 475 | pop dx 476 | pop cx 477 | pop bx 478 | pop ax 479 | ret 480 | CHECK_BRICKS_COLLISION ENDP 481 | 482 | 483 | ; CHECK_BRICKS_COLLISION PROC FAR 484 | ; push ax 485 | ; push bx 486 | ; push cx 487 | ; push dx 488 | ; push si 489 | 490 | ; ; Check ball position against brick zone 491 | ; mov ax, BALL_Y 492 | ; cmp ax, INITIAL_Y ; INITIAL_Y 493 | ; jl far ptr no_brick_collision 494 | 495 | ; cmp ax, 80 ; Bottom of brick zone 496 | ; jg far ptr no_brick_collision 497 | 498 | ; ; Get current brick position 499 | ; mov ax, BALL_Y ; Current Y position of the ball 500 | ; sub ax, INITIAL_Y ; Adjust for INITIAL_Y (top of brick zone) 501 | ; mov bx, 19 ; Brick height + gap (15 + 4) 502 | ; xor dx, dx ; Clear DX for division 503 | ; div bx ; AX / BX gives the row 504 | ; cmp ax, NUM_BRICKS_PER_COLUMN ; Only valid rows are 0, 1, 2, 3 505 | ; jge far ptr no_brick_collision ; If row >= 4, no collision 506 | ; mov cx, ax ; Save row index in CX 507 | 508 | ; ;now which brick in the row 509 | ; mov ax, BALL_X 510 | ; sub ax, INITIAL_X ; Subtract INITIAL_X 511 | ; mov bx, 30 ; Brick width + gap 512 | ; xor dx, dx 513 | ; div bx ; divide by the width of the brick 514 | ; cmp ax, NUM_BRICKS_PER_LINE ; Check column bounds, we want 0-9 515 | ; jge far ptr no_brick_collision 516 | 517 | ; ; we have the row and column of the brick 518 | ; ; row * NUM_BRICKS_PER_LINE + column ,,,, here NUM_BRICKS_PER_LINE = 10 519 | ; ; Get brick index 520 | ; push ax ; Save column 521 | ; mov ax, cx ; Row 522 | ; mov bx, NUM_BRICKS_PER_LINE ; NUM_BRICKS_PER_LINE 523 | ; mul bx ; Row * width 524 | ; pop bx ; Get column 525 | ; add ax, bx ; Final index 526 | 527 | ; ; Check and update brick 528 | ; mov si, offset BRICKS_STATUS 529 | ; add si, ax 530 | ; cmp byte ptr [si], 0 ;brick already destroyed 531 | ; je far ptr no_brick_collision 532 | ; dec byte ptr [si] ; Destroy brick,,, now we should draw again , msh 3arf azbotha 533 | ; cmp byte ptr [si], 0 534 | ; jne continue_: 535 | ; dec CURRENT_NUM_BRICKS 536 | ; continue_: 537 | 538 | ; ; Check collision type 539 | ; mov ax, BALL_VELOCITY_Y 540 | ; cmp ax, 0 541 | ; jl vertical_hit ; If moving up, handle as vertical hit 542 | 543 | ; ; Check for side collision 544 | ; mov ax, BALL_X 545 | ; add ax, BALL_SIZE 546 | ; mov bx, cx ; Get saved row 547 | ; push ax ; Save ball position 548 | ; mov ax, bx 549 | ; mov bx, 30 ; Brick width + gap 550 | ; mul bx ; Row * brick width 551 | ; add ax, INITIAL_X ; Add INITIAL_X 552 | ; mov bx, ax ; BX = brick left edge 553 | ; pop ax ; Restore ball position 554 | 555 | ; cmp ax, bx ; Compare with brick left edge 556 | ; jl side_hit 557 | ; add bx, BRICK_WIDTH ; Add BRICK_WIDTH 558 | ; cmp ax, bx ; Compare with brick right edge 559 | ; jg side_hit 560 | 561 | ; vertical_hit: 562 | ; neg BALL_VELOCITY_Y ; Vertical bounce 563 | ; jmp hit_done 564 | 565 | ; side_hit: 566 | ; neg BALL_VELOCITY_X ; Horizontal bounce 567 | 568 | ; hit_done: 569 | ; call far ptr DRAW_BRICKS ; Update display 570 | 571 | ; no_brick_collision: 572 | ; pop si 573 | ; pop dx 574 | ; pop cx 575 | ; pop bx 576 | ; pop ax 577 | ; ret 578 | ; CHECK_BRICKS_COLLISION ENDP 579 | 580 | ; end 581 | end -------------------------------------------------------------------------------- /MainMenu.asm: -------------------------------------------------------------------------------- 1 | ; Author: Anas Ibrahem 2 | ; Description: A simple main menu for a brick breaker 3 | ; ; 3 5 / 3 8 / 3 11 Cursor Positions 4 | ;; Updated By : Anas Ibrahem 24 / 12 / 2024 5 | CLEAR_SCREEN_GAME_MACRO MACRO ;; Not used in this file 6 | mov ax, 0600h ; Clear Screen 7 | mov bh, 07h 8 | mov cx, 0 9 | mov dx, 184fh 10 | int 10h 11 | ENDM CLEAR_SCREEN_GAME_MACRO 12 | 13 | CLEAR_SCREEN_MACRO MACRO 14 | mov ah, 0 15 | mov al, 3 16 | int 10h 17 | ENDM CLEAR_SCREEN_MACRO 18 | 19 | SAVE_CURSOR_SENDER_MACRO MACRO 20 | mov ah, 3h 21 | mov bh, 0h 22 | int 10h 23 | mov X_SENDER, dl 24 | mov Y_SENDER, dh 25 | ENDM SAVE_CURSOR_SENDER_MACRO 26 | 27 | SAVE_CURSOR_RECEIVER_MACRO MACRO 28 | mov ah, 3h 29 | mov bh, 0h 30 | int 10h 31 | mov X_RECEIVE, dl 32 | mov Y_RECEIVE, dh 33 | ENDM SAVE_CURSOR_RECEIVER_MACRO 34 | 35 | CLEAR_UPPER_MACRO MACRO 36 | mov ax, 060Dh 37 | mov bh, 03h 38 | mov ch, 0 39 | mov cl, 0 40 | mov dh, 12 41 | mov dl, 79 42 | int 10h 43 | ENDM CLEAR_UPPER_MACRO 44 | 45 | SCROLL_UPPER_MACRO MACRO 46 | mov ah, 6 47 | mov al, 1 48 | mov bh, 03h 49 | mov ch, 0 50 | mov cl, 0 51 | mov dh, 12 52 | mov dl, 79 53 | int 10h 54 | ENDM SCROLL_UPPER_MACRO 55 | 56 | SCROLL_LOWER_MACRO MACRO 57 | mov ah, 6 58 | mov al, 1 59 | mov bh, 30h 60 | mov ch, 13 61 | mov cl, 0 62 | mov dh, 24 63 | mov dl, 79 64 | int 10h 65 | ENDM SCROLL_LOWER_MACRO 66 | 67 | CLEAR_LOWER_MACRO MACRO 68 | mov ax, 060Ch 69 | mov bh, 30h 70 | mov ch, 13 71 | mov cl, 0 72 | mov dh, 24 73 | mov dl, 79 74 | int 10h 75 | ENDM CLEAR_LOWER_MACRO 76 | 77 | SET_CURSOR_MACRO MACRO x, y 78 | mov ah, 2 79 | mov bh, 0 80 | mov dl, x 81 | mov dh, y 82 | int 10h 83 | ENDM SET_CURSOR_MACRO 84 | 85 | ;----------------------------------------------------------------------------------------------------- 86 | .model small 87 | .data 88 | PUBLIC READY1 , READY2 89 | ; ball data 90 | EXTRN BALL_X:WORD, BALL_Y:WORD, BALL_SIZE:WORD, BALL_VELOCITY_X:WORD, BALL_VELOCITY_Y:WORD , PREV_TIME_STEP:BYTE 91 | ; bar data 92 | ; EXTRN BAR_X:WORD, BAR1_Y:WORD, BAR2_Y:WORD, BAR_LENGTH:WORD, BAR_HEIGHT:WORD, BAR_SPEED:WORD, BAR_COLOR:BYTE 93 | EXTRN BAR1_X:WORD, BAR2_X :WORD, BAR1_Y:WORD,BAR2_Y:WORD, BAR_LENGTH:WORD, BAR_HEIGHT:WORD, BAR_SPEED:WORD, BAR1_COLOR:BYTE , BAR2_COLOR:BYTE 94 | 95 | ; brick data 96 | EXTRN BRICK_X:WORD, BRICK_Y:WORD, INITIAL_X:WORD, INITIAL_Y:WORD, NUM_BRICKS_PER_LINE:WORD, NUM_BRICKS_PER_COLUMN:WORD, BRICK_WIDTH:WORD, BRICK_HEIGHT:WORD, COLOR_BRICK:BYTE, Gap:WORD, BRICKS_STATUS:BYTE, CURRENT_SCORE:WORD 97 | 98 | ; Main Menu Variables 99 | PUBLIC LIVES_COUNT 100 | TITLE_VARIABLE db "BRICKs BREAKER - MAIN MENU$" 101 | OPTION1_VARIABLE db "1. Start Game$" 102 | OPTION2_VARIABLE db "2. Chat$" 103 | OPTION3_VARIABLE db "3. Exit$" 104 | SCORE_MESSAGE db "SCORE: $" 105 | LIVES_MESSAGE db "LIVES: $" 106 | SELECTED_OPTION db 0 107 | NO_OF_OPTIONS db 2 108 | CLEAR db " $" 109 | KEY db 0 110 | HEART db 3 111 | READY_KEY db 'r' 112 | READY1 db 0 113 | READY2 db 0 114 | LIVES_COUNT db 4 115 | temp db 0 116 | ; Chat Variables 117 | VALUE db ? 118 | Y_SENDER db 0 119 | X_SENDER db 0 120 | X_RECEIVE db 0 121 | Y_RECEIVE db 0Dh 122 | .stack 100h 123 | .code 124 | ; bar procedures 125 | EXTRN DRAW_BAR:FAR, CLEAR_BAR:FAR, WAIT_FOR_VSYNC:FAR, HANDLE_BAR_INPUT:FAR 126 | ; ball procedures 127 | EXTRN DRAW_BALL:FAR, CLEAR_BALL:FAR, MOVE_BALL:FAR, CHECK_TIME:FAR, CHECK_COLLISION:FAR 128 | ; brick procedures 129 | EXTRN DRAW_BRICK:FAR, DRAW_BRICKS:FAR 130 | 131 | EXTRN DRAW_BAR1:FAR, CLEAR_BAR1:FAR, DRAW_BAR2:FAR , CLEAR_BAR2:FAR, WAIT_FOR_VSYNC:FAR 132 | 133 | PUBLIC main 134 | 135 | CLEAR_SCREEN_PROC proc far 136 | mov ah, 0 137 | mov al, 3 138 | int 10h 139 | ret 140 | CLEAR_SCREEN_PROC endp 141 | 142 | DISPLAY_TEXT_PROC proc far 143 | mov ah, 9 144 | int 21h 145 | ret 146 | DISPLAY_TEXT_PROC endp 147 | 148 | SET_CURSOR_PROC proc far 149 | mov ah, 2 150 | int 10h 151 | ret 152 | SET_CURSOR_PROC endp 153 | 154 | PRINT_HEART_PROC proc far 155 | push cx 156 | mov al, HEART 157 | mov ah, 9 158 | mov cx, 1h 159 | mov bl, 04h 160 | int 10h 161 | pop cx 162 | ret 163 | PRINT_HEART_PROC endp 164 | UPDATE_STATS_PROC proc far 165 | push ax 166 | push bx 167 | push cx 168 | push dx 169 | 170 | mov ax, CURRENT_SCORE 171 | ;; Convert to ascii 172 | aam 173 | add al, '0' 174 | add ah, '0' 175 | 176 | ; Print al 177 | mov temp, ah 178 | mov dl, 8 179 | mov dh, 0 180 | call far ptr SET_CURSOR_PROC 181 | mov ah, 9 182 | mov cx, 1h 183 | mov bl, 03h 184 | int 10h 185 | 186 | ; Print ah 187 | mov al, temp 188 | mov dl, 7 189 | mov dh, 0 190 | call far ptr SET_CURSOR_PROC 191 | mov ah, 9 192 | mov cx, 1h 193 | mov bl, 03h 194 | int 10h 195 | ; Print Lives 196 | mov al, LIVES_COUNT 197 | add al, '0' 198 | mov dl, 31 199 | mov dh, 0 200 | call far ptr SET_CURSOR_PROC 201 | mov ah, 9 202 | mov cx, 1h 203 | mov bl, 04h 204 | int 10h 205 | 206 | pop dx 207 | pop cx 208 | pop bx 209 | pop ax 210 | ret 211 | UPDATE_STATS_PROC ENDP 212 | 213 | 214 | DISPLAY_MENU_PROC proc far 215 | first_option: 216 | mov dl, 5 217 | mov dh, 5 218 | call far ptr SET_CURSOR_PROC 219 | mov dx, offset OPTION1_VARIABLE 220 | call far ptr DISPLAY_TEXT_PROC 221 | 222 | second_option: 223 | mov dl, 5 224 | mov dh, 8 225 | call far ptr SET_CURSOR_PROC 226 | mov dx, offset OPTION2_VARIABLE 227 | call far ptr DISPLAY_TEXT_PROC 228 | 229 | third_option: 230 | mov dl, 5 231 | mov dh, 11 232 | call far ptr SET_CURSOR_PROC 233 | mov dx, offset OPTION3_VARIABLE 234 | call far ptr DISPLAY_TEXT_PROC 235 | ret 236 | DISPLAY_MENU_PROC endp 237 | 238 | PRINT_CURSOR_PROC proc far 239 | ; Print the cursor selector 240 | ; Clear all selectors 241 | mov dl, 3 242 | mov dh, 5 243 | call far ptr SET_CURSOR_PROC 244 | mov dx, offset CLEAR 245 | call far ptr DISPLAY_TEXT_PROC 246 | 247 | mov dl, 3 248 | mov dh, 8 249 | call far ptr SET_CURSOR_PROC 250 | mov dx, offset CLEAR 251 | call far ptr DISPLAY_TEXT_PROC 252 | 253 | mov dl, 3 254 | mov dh, 11 255 | call far ptr SET_CURSOR_PROC 256 | mov dx, offset CLEAR 257 | call far ptr DISPLAY_TEXT_PROC 258 | 259 | cmp SELECTED_OPTION, 0 260 | je y_first_option 261 | cmp SELECTED_OPTION, 1 262 | je y_second_option 263 | cmp SELECTED_OPTION, 2 264 | je y_third_option 265 | 266 | y_first_option: 267 | mov dl, 3 268 | mov dh, 5 269 | jmp print_cursor 270 | 271 | y_second_option: 272 | mov dl, 3 273 | mov dh, 8 274 | jmp print_cursor 275 | 276 | y_third_option: 277 | mov dl, 3 278 | mov dh, 11 279 | jmp print_cursor 280 | 281 | print_cursor: 282 | call far ptr SET_CURSOR_PROC 283 | mov ah, 9 284 | mov al, '>' 285 | mov cx, 1h 286 | mov bl, 03h 287 | int 10h 288 | ret 289 | PRINT_CURSOR_PROC endp 290 | 291 | main proc far 292 | mov ax, @data 293 | mov ds, ax 294 | 295 | start_menu: 296 | mov ah, 0 297 | mov al, 13h 298 | int 10h 299 | 300 | ; call far ptr CLEAR_SCREEN_PROC ;NOT NEEDED 301 | print_start_menu: 302 | ; Set Cursor Center for Title 303 | mov dl, 7 304 | mov dh, 2 305 | call far ptr SET_CURSOR_PROC 306 | 307 | ; Display the title 308 | mov dx, offset TITLE_VARIABLE 309 | call far ptr DISPLAY_TEXT_PROC 310 | ; Display Menu 311 | call far ptr DISPLAY_MENU_PROC 312 | 313 | menu_loop: 314 | ; Read keyboard input for navigation 315 | call far ptr PRINT_CURSOR_PROC 316 | mov ah, 0 317 | int 16h 318 | cmp ah, 48h ; Arrow Up key 319 | je navigate_up 320 | cmp ah, 50h ; Arrow Down key 321 | je navigate_down 322 | cmp ah, 1Ch ; Enter key 323 | je select_option 324 | jmp menu_loop 325 | 326 | navigate_up: 327 | dec SELECTED_OPTION 328 | cmp SELECTED_OPTION, 0 329 | jl reset_to_bottom 330 | jmp menu_loop 331 | 332 | navigate_down: 333 | inc SELECTED_OPTION 334 | mov al, NO_OF_OPTIONS 335 | cmp SELECTED_OPTION, al 336 | jg reset_to_top 337 | jmp menu_loop 338 | 339 | reset_to_top: 340 | mov SELECTED_OPTION, 0 341 | jmp menu_loop 342 | 343 | reset_to_bottom: 344 | mov al, NO_OF_OPTIONS 345 | mov SELECTED_OPTION, al 346 | jmp menu_loop 347 | 348 | select_option: 349 | cmp SELECTED_OPTION, 0 350 | je start_game 351 | cmp SELECTED_OPTION, 1 352 | jne skip_option_1 353 | jmp far ptr show_chat 354 | skip_option_1: 355 | cmp SELECTED_OPTION, 2 356 | jne skip_option_2 357 | jmp far ptr exit_game 358 | skip_option_2: 359 | jmp menu_loop 360 | 361 | start_game: 362 | ; draw initial screen 363 | CLEAR_SCREEN_MACRO 364 | mov ax, 13h 365 | int 10h 366 | call far ptr DRAW_BRICKS 367 | call far ptr DRAW_BAR 368 | call far ptr DRAW_BALL 369 | 370 | ;; Set Cursor for Score 371 | mov dl, 1 372 | mov dh, 0 373 | call far ptr SET_CURSOR_PROC 374 | ;; Display Score Message 375 | mov dx, offset SCORE_MESSAGE 376 | call far ptr DISPLAY_TEXT_PROC 377 | 378 | mov dl, 25 379 | mov dh, 0 380 | call far ptr SET_CURSOR_PROC 381 | mov dx, offset LIVES_MESSAGE 382 | call far ptr DISPLAY_TEXT_PROC 383 | 384 | ;; INITIALIZATION of Communication 385 | mov dx, 3fbh ; Line control Register 386 | mov al, 10000000b 387 | out dx, al 388 | 389 | ; Set up the baud rate 390 | mov dx, 3f8h 391 | mov al, 0c0h 392 | out dx, al 393 | 394 | mov dx, 3f9h 395 | mov al, 00h 396 | out dx, al 397 | 398 | ; Set port configuration 399 | mov dx, 3fbh 400 | mov al, 00011111b 401 | out dx, al 402 | 403 | outer_check_loop: 404 | mov ah, 01h 405 | int 16h 406 | jz check_2nd_ready_loop 407 | cmp al , READY_KEY 408 | jne check_2nd_ready_loop 409 | mov READY1, 1 410 | ; Send ready Key 411 | mov dx, 3FDH ; Line Status Register 412 | In al, dx ; Read Line Status 413 | test al, 00100000b 414 | jz check_2nd_ready_loop ; Not empty 415 | mov dx, 3F8H ; Transmit data register 416 | mov al, READY_KEY ; put the key into al 417 | out dx, al ; sending the data 418 | check_2nd_ready_loop: 419 | mov dx, 3FDH ; Line Status Register 420 | in al, dx 421 | test al, 1 422 | jnz read_ready_from_second_player 423 | jmp test_ready 424 | read_ready_from_second_player: 425 | mov dx, 3F8H ; Receive data register 426 | In al, dx ; Read the data 427 | cmp al, READY_KEY ; 428 | jne test_ready 429 | mov READY2, 1 430 | test_ready: 431 | cmp READY1 , 1 432 | jne outer_check_loop 433 | cmp READY2 , 1 434 | jne outer_check_loop 435 | 436 | game_loop: 437 | ; Start Communication and logic 438 | mov ah, 1 ; Check if a key is pressed 439 | int 16h 440 | jnz first_player_press_key 441 | jmp far ptr second_player_receive ; jump to receiving mode 442 | first_player_press_key: 443 | mov ah,0 444 | int 16h 445 | 446 | mov KEY, al ; ascii code in al ; TODO add more options for keys like pause 447 | send_status_first_player: 448 | mov dx, 3FDH ; Line Status Register 449 | In al, dx ; Read Line Status 450 | test al, 00100000b 451 | jz second_player_receive ; Not empty 452 | 453 | mov dx, 3F8H ; Transmit data register 454 | mov al, KEY ; put the key into al 455 | out dx, al ; sending the data 456 | 457 | cmp al, 'a' ; left arrow key 458 | je move_left_bar1 459 | cmp al, 'd' ; right arrow key 460 | je move_right_bar1 461 | ;cmp al , 27 ; ckeck if the key was esc 462 | ;je start_menu_send 463 | jmp update_ball 464 | 465 | 466 | move_left_bar1: 467 | ; check if the bar is at the left edge of the screen 468 | mov ax, BAR1_X 469 | cmp ax, 0 470 | jg skip2_handle_input ; if the bar is at the left edge, continue the loop 471 | jmp update_ball 472 | 473 | 474 | skip2_handle_input: 475 | call far ptr WAIT_FOR_VSYNC 476 | ; clear the bar 477 | call far ptr CLEAR_BAR1 478 | ; move the bar to the left 479 | mov ax, BAR1_X 480 | sub ax, BAR_SPEED 481 | mov BAR1_X, ax 482 | ; draw the bar 483 | call far ptr DRAW_BAR1 484 | call far ptr DRAW_BAR2 485 | jmp second_player_receive 486 | 487 | 488 | move_right_bar1: 489 | ; check if the bar is at the right edge of the screen 490 | mov ax, BAR1_X 491 | add ax, BAR_LENGTH 492 | cmp ax, 319 493 | jl skip_game_loop1 494 | jmp update_ball ; if the bar is at the right edge, continue the loop 495 | skip_game_loop1: call far ptr WAIT_FOR_VSYNC 496 | ; clear the bar 497 | call far ptr CLEAR_BAR1 498 | ; move the bar to the right 499 | mov ax, BAR1_X 500 | add ax, BAR_SPEED 501 | mov BAR1_X, ax 502 | ; draw the bar 503 | call far ptr DRAW_BAR1 504 | call far ptr DRAW_BAR2 505 | jmp second_player_receive 506 | 507 | second_player_receive: 508 | mov dx, 3FDH ; Line Status Register 509 | in al, dx 510 | test al, 1 511 | jnz read_from_second_player 512 | jmp update_ball 513 | 514 | read_from_second_player: 515 | mov dx, 3F8H ; Receive data register 516 | In al, dx ; Read the data 517 | mov KEY, al ; put the key into al 518 | cmp al, 'a' ; left arrow key 519 | je move_left_bar2 520 | cmp al, 'd' ; right arrow key 521 | je move_right_bar2 522 | jmp update_ball 523 | 524 | 525 | move_left_bar2: 526 | ; check if the bar is at the left edge of the screen 527 | mov ax, BAR2_X 528 | cmp ax, 0 529 | jg skip_game_loop2 530 | jmp update_ball ; if the bar is at the left edge, continue the loop 531 | skip_game_loop2: call far ptr WAIT_FOR_VSYNC 532 | ; clear the bar 533 | call far ptr CLEAR_BAR2 534 | ; move the bar to the left 535 | mov ax, BAR2_X 536 | sub ax, BAR_SPEED 537 | mov BAR2_X, ax 538 | ; draw the bar 539 | call far ptr DRAW_BAR2 540 | call far ptr DRAW_BAR1 541 | jmp update_ball 542 | 543 | move_right_bar2: 544 | ; check if the bar is at the right edge of the screen 545 | mov ax, BAR2_X 546 | add ax, BAR_LENGTH 547 | cmp ax, 319 548 | jl skip_game_loop3 549 | jmp update_ball ; if the bar is at the right edge, continue the loop 550 | skip_game_loop3: call far ptr WAIT_FOR_VSYNC 551 | ; clear the bar 552 | call far ptr CLEAR_BAR2 553 | ; move the bar to the right 554 | mov ax, BAR2_X 555 | add ax, BAR_SPEED 556 | mov BAR2_X, ax 557 | ; draw the bar 558 | call far ptr DRAW_BAR2 559 | call far ptr DRAW_BAR1 560 | jmp update_ball 561 | 562 | update_ball: 563 | mov ah , 2ch ; get the current time 564 | int 21h ; ch = hour , cl = minutes , dh = seconds , dl = 1/100 seconds 565 | cmp dl , PREV_TIME_STEP ; Compare current time step with previous time step 566 | jne skip_game_loop_4 567 | jmp game_loop ; if equal then continue the loop 568 | skip_game_loop_4: 569 | mov PREV_TIME_STEP , dl ; Update previous time step 570 | call far ptr MOVE_BALL 571 | call far ptr UPDATE_STATS_PROC 572 | jmp game_loop ; stuck at game loop 573 | 574 | 575 | show_chat: 576 | call far ptr CLEAR_SCREEN_PROC 577 | mov dx, 3fbh ; Line control Register 578 | mov al, 10000000b 579 | out dx, al 580 | 581 | ; Set up the baud rate 582 | mov dx, 3f8h 583 | mov al, 0ch 584 | out dx, al 585 | 586 | mov dx, 3f9h 587 | mov al, 00h 588 | out dx, al 589 | 590 | ; Set port configuration 591 | mov dx, 3fbh 592 | mov al, 00011011b 593 | out dx, al 594 | 595 | ; SetUp the screen 596 | mov ah, 0 ; Ensure text mode 597 | mov al, 3 598 | int 10h 599 | 600 | CLEAR_UPPER_MACRO 601 | CLEAR_LOWER_MACRO 602 | 603 | mov dl, 0 604 | mov dh, 0 605 | call far ptr SET_CURSOR_PROC 606 | 607 | ; start sending and receiving 608 | call far ptr DETECT_CHAT_PROC 609 | 610 | DETECT_CHAT_PROC proc 611 | chat_loop: 612 | ; Check if there is a key pressed 613 | mov ah, 1 614 | int 16h 615 | jz short check_receive ; if not then jmp to check_receive // short to fix out of range 616 | 617 | check_receive: 618 | jmp far ptr receive ; jump to receiving mode 619 | jnz send ; if yes jmp to send mode 620 | 621 | send: 622 | mov ah, 0 ; clear the keyboard buffer 623 | int 16h 624 | mov VALUE, al ; ascii code in al 625 | cmp al, 0Dh ; IF ENTER 626 | jnz cont 627 | jz new_line_send 628 | 629 | new_line_send: 630 | cmp Y_SENDER, 12 ; check if out of range 631 | jz overflow 632 | jnz increment 633 | 634 | overflow: 635 | SCROLL_UPPER_MACRO 636 | jmp print 637 | 638 | increment: 639 | inc Y_SENDER ; if new line then go to the next line 640 | mov X_SENDER, 0 641 | 642 | cont: 643 | ; setting the cursor 644 | SET_CURSOR_MACRO X_SENDER, Y_SENDER 645 | cmp X_SENDER, 79 ; if the x goes to 79 the most right of screen check where is y 646 | jz checkY 647 | jnz print 648 | 649 | checkY: 650 | cmp Y_SENDER, 12 ; if y goes to the lower bound of the first half of the screen go to clear the upper half of screen 651 | jnz print 652 | SCROLL_UPPER_MACRO 653 | mov Y_SENDER, 12 654 | mov X_SENDER, 0 ; set the cursor manually to 0, Y 655 | SET_CURSOR_MACRO X_SENDER, Y_SENDER 656 | 657 | print: 658 | mov ah, 2 ; printing the char 659 | mov dl, VALUE 660 | int 21h 661 | 662 | send_status: 663 | mov dx, 3FDH ; Line Status Register 664 | again: 665 | in al, dx ; Read Line Status 666 | test al, 00100000b 667 | jz receive ; Not empty 668 | 669 | mov dx, 3F8H ; Transmit data register 670 | mov al, VALUE ; put the data into al 671 | out dx, al ; sending the data 672 | 673 | cmp al, 27 ; if the key was esc terminate the program and this check must be after the send is done 674 | jz start_menu_bridge 675 | SAVE_CURSOR_SENDER_MACRO ; we need to save the cursor here 676 | jmp chat_loop ; loop again 677 | 678 | start_menu_bridge: 679 | jmp start_menu 680 | 681 | send_bridge: 682 | jmp send 683 | 684 | receive: 685 | mov ah, 1 ; check if there is key pressed then go to the sending mode 686 | int 16h 687 | jnz send_bridge 688 | 689 | read_status: 690 | mov dx, 3FDH ; Line Status Register 691 | in al, dx 692 | test al, 1 693 | jz receive 694 | 695 | read: 696 | mov dx, 03F8H 697 | in al, dx 698 | mov VALUE, al ; check if the received data is sec key then terminate the program 699 | cmp VALUE, 27 ; if the key was esc exit chat 700 | jz start_menu_bridge 701 | 702 | cmp VALUE, 0Dh ; check if the key is enter 703 | jnz cont_receive 704 | jz new_line_receive 705 | 706 | new_line_receive: 707 | cmp Y_RECEIVE, 24 708 | jz overflow_receive 709 | jnz increment_receive 710 | 711 | overflow_receive: 712 | SCROLL_LOWER_MACRO 713 | jmp print_receive ; print the char 714 | 715 | increment_receive: 716 | inc Y_RECEIVE 717 | mov X_RECEIVE, 0 718 | 719 | cont_receive: 720 | SET_CURSOR_MACRO X_RECEIVE, Y_RECEIVE 721 | cmp X_RECEIVE, 79 ; if the x goes to 79 the most right of screen check where is y 722 | jz checkYR 723 | jnz print_receive 724 | 725 | checkYR: 726 | cmp Y_RECEIVE, 24 727 | jnz print_receive 728 | SCROLL_LOWER_MACRO 729 | mov Y_RECEIVE, 24 730 | mov X_RECEIVE, 0 731 | SET_CURSOR_MACRO X_RECEIVE, Y_RECEIVE 732 | 733 | print_receive: 734 | mov ah, 2 ; printing the char 735 | mov dl, VALUE 736 | int 21h 737 | SAVE_CURSOR_RECEIVER_MACRO ; we need to save the cursor here 738 | jmp chat_loop 739 | 740 | DETECT_CHAT_PROC endp 741 | 742 | exit_game: 743 | mov ah, 4ch 744 | int 21h 745 | ret 746 | 747 | main endp 748 | end main 749 | --------------------------------------------------------------------------------