Created
September 22, 2023 23:51
-
-
Save keyboardspecialist/d4a3829fe885374aa596aff8b0e6da96 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%define BOCHS_HD_IMAGE ;define this when booting in Bochs as a hard drive image | |
%define CONSOLE_WIDTH 0x4E | |
%define RTC_DIVIDER 0xF | |
%define PADDLE_WIDTH 0x08 | |
%define PADDLE_SPEED 0x2 | |
BITS 16 ;we are running in real mode, so 16 bit only | |
[org 0x7C00] ;set origin address to 0x7C00. This is where BIOS drops us off in RAM | |
boot: | |
cli ;halt interrupts | |
xor ax, ax | |
mov ds, ax ;set data segment register | |
mov gs, ax ;set general-use segment register | |
mov ax, 0B800h | |
mov es, ax ;VGA color text data segment | |
mov ax, 07E0h | |
mov ss, ax ;set stack segment register | |
mov bp, ax ;set stack base pointer. bottom of stack | |
mov sp, 9C00h ;set stack pointer. top of stack | |
mov ax, word int70h_handler ;setup 70h interrupt for RTC timer | |
mov [gs:70h*4], ax ;set interrupt offset | |
mov [gs:70h*4+2], ds ;set interrupt segment | |
mov ax, word int09h_handler ;setup 09h for keyboard | |
mov [gs:09h*4], ax ;set interrupt offset | |
mov [gs:09h*4+2], ds ;set interrupt segment | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;; Init Routine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
init: | |
;fill with border chars | |
mov ax, 0x0FFE ;set ax to bg color | |
xor di, di ;clear di ;consider for space | |
mov cx, 80 * 25 ;set cx to size of console screen | |
rep stosw ;write ax to video memory cx times. mov [es:di++], ax | |
mov bx, 0x1901 | |
xor ax, ax | |
call fill_area | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;; Draw Bricks ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
mov bx, 0x0501 ;start at row 1, col 1 | |
mov ax, 0x03B2 ;char and attr to write | |
call fill_area | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;; RTC Init ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
mov al, 8Bh ;select reg B, disable NMI | |
out 70h, al | |
in al, 71h ;read B | |
or al, 40h ;OR 0x40 to set flag for start | |
push ax | |
mov ax, 8Bh ;select B | |
out 70h, al | |
pop ax | |
out 71h, al ;set B register to start | |
mov al, 8Ah ;select reg A and disable NMI | |
out 70h, al | |
in al, 71h ;read reg A value | |
and al, 11110000b ;keep top nibble received | |
or al, RTC_DIVIDER ;OR in divider in low nibble | |
push ax | |
mov al, 8Ah | |
out 70h, al ;select A | |
pop ax | |
out 71h, al ;write value to A | |
in al, 0A1h ;clear PIC 2 mask to ensure that IRQ 8 fires | |
xor al, al | |
out 0A1h, al | |
sti | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
jmp main_loop | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
fill_area: | |
.iter: | |
mov cx, CONSOLE_WIDTH ;brick row counter | |
call vga_get_char_offset ;get video offset | |
rep stosw ;write cx num bricks | |
dec bh | |
cmp bh, 0 ;loop | |
jnz .iter | |
ret | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;; Update Frame ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
update_frame: | |
mov al, byte [paddle_x] ;load paddle offsets | |
add al, byte [paddle_vec] ;add vector | |
cmp al, 1 ;test against left side | |
jge .testpaddlehigh ;bigger then test right side | |
mov al, 1 ;clip to 1 | |
jmp .storepaddlex ;store it | |
.testpaddlehigh: | |
cmp al, CONSOLE_WIDTH - PADDLE_WIDTH - 1;compare adjusted values for right edge | |
jle .storepaddlex ;nope just store it | |
mov al, CONSOLE_WIDTH - PADDLE_WIDTH - 1;clip to right side | |
.storepaddlex: | |
mov byte [paddle_x], al ;store the new x value | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
.checkballycollision: | |
mov bx, word [ball_x] ;load x,y at once | |
push bx ;save old position for later | |
add bh, byte [ball_vecy] ;add y pos + y vec | |
call vga_get_char_offset ;get video mem offset | |
mov ax, word [es:di] ;get char from memory offset | |
cmp al, 0 ;check if the cell is empty | |
je .storebally ;if it is, move there | |
neg byte [ball_vecy] ;if not, reverse direction | |
;this would be simpler with a paddle made of 1 char | |
cmp al, 205 ;check for paddle left side char | |
jz .paddlecollide ;if we hit, handle the collision | |
cmp al, 213 ;paddle middle char | |
jz .paddlecollide ;... | |
cmp al, 184 ;paddle right side char | |
jnz .ybrickcollision ;inverse test, if we hit just fall through | |
.paddlecollide: | |
mov dl, byte [ball_vecx] ;load ball x vec | |
cmp byte [paddle_vec], 0 ;check direction of paddle | |
jl .pvecn ;moving left ? | |
jg .pvec ;moving right ? | |
jmp .checkballxcollision ;no movement (only possible at start) | |
.pvecn: ;there's got to be a better way handling this | |
dec dl ;we are negative so decrement ball vec | |
cmp dl, -1 ;clip it to -1 | |
jg .donepaddle ;if greater, finish and store | |
mov dl, -1 ;if not, set to -1 | |
jmp .donepaddle ;store it | |
.pvec: ;paddle is positive | |
inc dl ;increment ball vec | |
cmp dl, 1 ;clipping to 1 this time | |
jl .donepaddle ;... | |
mov dl, 1 ;... | |
.donepaddle: | |
mov byte [ball_vecx], dl ;store new vector | |
jmp .checkballxcollision ;we hit something and reversed so just go on to check x direction | |
.ybrickcollision: | |
call brick_collider ;we possibly hit a brick so check and handle accordingly | |
jmp .checkballxcollision ;move on | |
.storebally: | |
mov byte [ball_y], bh ;cell was empty so we store the new y position | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
.checkballxcollision: | |
pop bx ;restore old ball x,y in case it changed above | |
push bx ;push it back onto the stack for later | |
cmp byte [ball_vecx], 0 ;if we have no x vector we can't hit anything | |
jz .skiptodraw ;no vec, just go draw | |
add bl, byte [ball_vecx] ;add vector to x pos | |
call vga_get_char_offset ;get new video offset | |
mov ax, [es:di] ;retrieve char there | |
cmp al, 0 ;is it empty? | |
je .storeballx ;if it is, move there | |
neg byte [ball_vecx] ;nope, reverse direction | |
.xbrickcollision: | |
call brick_collider ;we can only hit bricks on this axis, so handle if we did | |
jmp .skiptodraw ;no store, just draw | |
.storeballx: | |
mov byte [ball_x], bl ;we hit nothing and had a vector, so store | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;; Clear Scr ;;; | |
.skiptodraw: | |
xor ax, ax ;clear paddle row for redraw | |
mov bx, 0x1701 ;row 23, col 1 | |
mov cl, CONSOLE_WIDTH ;clear row | |
call vga_get_char_offset | |
rep stosw ;write blank chars to row | |
pop bx ;retrieve our original ball x,y | |
call vga_get_char_offset | |
mov [es:di], ax ;clear | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;; Draw Ball ;;; | |
mov bx, word [ball_x] ;get new ball x,y | |
mov ax, 0x0D07 ;ball char | |
call vga_get_char_offset | |
mov [es:di], ax ;write it | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;; Draw Paddle | |
mov bx, word [paddle_x] ;read as word to get row offset as well | |
mov ax, 0x07D5 ;left side char | |
call vga_get_char_offset | |
mov [es:di], ax ;write to video mem | |
add di, 2 ;increment video pointer | |
mov cl, PADDLE_WIDTH | |
mov al, 0xCD | |
rep stosw ;write middle bars to video mem | |
mov al, 0xB8 | |
mov [es:di], ax ;write right side char | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
.update_end: | |
ret | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;; Brick Collider ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
brick_collider: ;bx - bl - x, bh - y, al - char | |
cmp al, 178 ;chars 176-178 are brick chars. 178 - solid, 177 - 50%, 176 - 25% | |
jle .charlow ;if its less or equal, check low bound | |
jmp .skip ;greater, not a brick | |
.charlow: | |
cmp al, 176 ;check low bound | |
jl .skip ;lower, not a brick | |
push ax ;ax has our char and attr, save on stack | |
xor ax, ax ;clear ax | |
mov al, bl ;move ball x into al | |
mov dl, 3 ;set dl to 3. bricks are 3 wide. we need to divide ball x by 3 to get a brick offset | |
div dl ;do the divide | |
test ah, 0x3 ;check remainder. if its zero, we need to adjust or we will hit the brick to the right | |
jnz .noadjust ;not zero, dont adjust | |
dec al ;decrement so we collide with correct brick | |
.noadjust: | |
mul dl ;multiply our brick offset by brick size (3) | |
mov dl, al ;move offset into dl | |
;this gives us a brick offset that starts with the leftmost char of the brick | |
pop ax ;pop our char and attr off stack | |
push bx ;push our x/y onto stack so we don't clobber it | |
mov bl, dl ;vga_get_char_offset uses bx for x/y so transfer brick x offset to it | |
inc bl ;increment position since our bricks start at column 1 | |
mov cl, 3 ;we need to draw 3 chars | |
dec al ;decrement to more damage brick char | |
cmp al, 176 ;if we fall below lowest brick char, zero it out, its destroyed | |
jge .draw ;nope, not destroyed, just draw | |
xor al, al ;zero out char | |
.draw: | |
call vga_get_char_offset ;get brick mem offset | |
rep stosw ;draw | |
pop bx ;restore original ball x,y into bx | |
.skip: | |
ret | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
main_loop: | |
jmp main_loop | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;; VGA Offset ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
vga_get_char_offset: ;bx parm. bl - x, bh - y. ret - offset in di | |
push ax ;push ax and dx onto stack | |
push dx ;we don't want to clobber them | |
xor dx, dx | |
mov al, bh ;put y value into al | |
mul byte [console_w] ;y * row_width. mul requires register or memory operand | |
mov dl, bl ;put x value into dl | |
add ax, dx ;add x + y_row offset | |
mov di, ax ;move to di for our return value | |
shl di, 1 ;offset * 2, chars occupy 2 bytes | |
pop dx ;restore ax and dx | |
pop ax | |
ret | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;; RTC Timer Handler ;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
int70h_handler: | |
call update_frame | |
mov al, 0Ch ;select reg C | |
out 70h, al ;read RTC reg C. We have to do this to keep receiving interrupts | |
in al, 71h ;required dummy read. resets to reg D | |
mov al, 0x20 ;send EOI to allow interrupts continue | |
out 0xa0, al ;this int occurs on the slave PIC 2, so we need to notify it | |
out 0x20, al ;notify the master PIC 1, too | |
;this is fudged a bit since it doesn't reset. It'll trigger multiple times per 1 missed | |
;ball count reflects this | |
cmp byte [ball_y], 24 ;check if we missed the ball | |
jl .done ;nope, we're done | |
dec byte [balls_left] ;yep, decrement ball count | |
cmp byte [balls_left], 0 ;if we are at 0 we lose | |
jnz .done ;nope, we're done | |
.check: | |
in al, 64h ;out of balls, force reset. Read keyboard controller port | |
test al, 02h ;test system flag bit | |
jnz .check ;if its not clear, loop | |
mov al, 0FEh ;system reset command | |
out 64h, al ;send to controller | |
.done: | |
iret | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;; Keyboard handler ;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
int09h_handler: | |
in al, 60h ;read the key pressed | |
cmp al, 0x4B ;left arrow key | |
jnz .testright ;if not pressed check right key | |
mov byte [paddle_vec], -PADDLE_SPEED ;set negative direction vector | |
jmp .done | |
.testright: | |
cmp al, 0x4D ;right arrow key | |
jnz .done ;not pressed, exit | |
mov byte [paddle_vec], PADDLE_SPEED ;set positive direction vector | |
;;;;;;;;;;;;;;;;;;;;;;;;;;; Stuff we gotta do to make the keyboard interrupt happy | |
.done: | |
in al, 61h ;get keyboard control line value | |
mov ah, al ;save for later | |
or al, 80h ;set enable keyboard bit | |
out 61h, al ;write to port | |
xchg ah, al ;swap control port value into al | |
out 61h, al ;write to port | |
mov al, 20h ;send EOI to allow interrupts to continue | |
out 20h, al | |
iret | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
console_w db 80 ;console width | |
paddle_x db 36 ;current paddle x | |
db 23 ;hack to save 1 byte @ draw_paddle | |
paddle_vec db 0 ;paddle movement direction | |
ball_x db 40 ;current ball x | |
ball_y db 22 ;current ball y | |
ball_vecx db 0 ;vector in console chars | |
ball_vecy db -1 ;vector in console chars | |
block_cnt db 130 ;number of blocks left | |
balls_left db 0x20 | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
times 510-($-$$) db 0 ; Pad remainder of boot sector with 0s | |
dw 0xAA55 ; The standard PC boot signature | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
%ifdef BOCHS_HD_IMAGE | |
times 1024 * 1024 db 0 ;Bochs HD image padding. Set Cylinders/Sectors/Heads to 3 | |
%endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment