├── .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 |
52 |
53 |
54 |
55 |
Game Overview
56 |
57 |
58 |
59 |
60 |
Difficult Level
61 |
62 |
63 |
64 |
65 |
Chat
66 |
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 |
--------------------------------------------------------------------------------