Skip to content

Instantly share code, notes, and snippets.

@BrentFarris
Created October 15, 2023 01:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BrentFarris/6bc6765a40328815ea0003c3910a5dca to your computer and use it in GitHub Desktop.
Save BrentFarris/6bc6765a40328815ea0003c3910a5dca to your computer and use it in GitHub Desktop.
BITS 16 ; Instruct the system this is 16-bit code
; This is the entry point, nothing should happen before this
; other than setting the instruction size
main:
mov ax, 07C0h ; Setup 4KB stack space after this bootloader
add ax, 288 ; (4096+515) / 16 bytes (aligned) per paragraph
cli ; Disable interrupts (solvs old DOS bug)
mov ss, ax ; Assign current stack segment
mov sp, 4096 ; Setup our stack pointer
sti ; Enable interrupts (solvs old DOS bug)
mov ax, 07C0h ; 07C0h is where our program is located
mov ds, ax ; Set data segment to the load point of our program
call run ; Start the main loop
;------------------------------------------------------------------------------
; Constants
;------------------------------------------------------------------------------
s_hi db "Hello, World!", 0Dh, 0Ah, "- Brent", 0Dh, 0Ah, 00h
s_nl db 0Dh, 0Ah, 00h
;------------------------------------------------------------------------------
; The main loop of our program
;------------------------------------------------------------------------------
run:
mov si, s_hi ; Set our SI register to hello message pointer
call print ; Call our print subroutine
.loop:
call read_keyboard ; Call our keyboard input subroutine
je .loop ; Return to loop if no key was pressed
call ah_to_str ; A key was pressed, so let's print it!
mov si, s_nl ; Move the new line bytes into SI register
call print ; Print a new line for readability
jmp .loop ; Infinite loop to hold control of the computer
;------------------------------------------------------------------------------
; Reading keyboard input
;------------------------------------------------------------------------------
read_keyboard:
mov ah, 00h ; 00h is the get key and clear buffer function in BIOS
int 16h ; Call the BIOS interrupt for keyboard functions
test ah, ah ; AH will be 0 if no key was pressed, allow je after
ret ; Return to caller with ZF and AH set
;------------------------------------------------------------------------------
; Debug printing of bytes
;------------------------------------------------------------------------------
ah_to_str:
push ax ; Save the state of our AX register
push cx ; Save the state of our CX register
mov ch, 80h ; Set bit flag for AND as 10000000
mov al, ah ; Copy AH to AL so we can change AH
.ah_to_str_loop:
mov ah, al ; Restore original value of AH
and ah, ch ; Get the bit value for printing
test ah, ah ; Check to see if the bit is zero or 1
je .ah_to_str_zero ; The bit was 0 so jump to print 0
mov ah, 31h ; The bit was 1 so set AH to ascii 1
jmp .ah_to_str_print ; Jump to print the character
.ah_to_str_zero:
mov ah, 30h ; The bit was 0 so set AH to ascii 0
.ah_to_str_print:
call print_char ; Call our print_char routine below
SHR ch, 01h ; Shift CH right by 1 (0100000 first time)
cmp ch, 00h ; Check to see if we shifted all the way
jne .ah_to_str_loop ; If we haven't finished, return to loop
pop cx ; Restore the state of our CX register
pop ax ; Restore the state of our AX register
ret ; Return to caller location
print_char:
push ax ; Save the state of our AX register
push cx ; Save the CX register (due to int 10h clobbering)
mov al, ah ; AL is used for the character to print
mov ah, 0Eh ; Teletype BIOS interrupt function
mov bl, 0Fh ; Don't worry about me until the end of this guide
int 10h ; BIOS interrupt 10h (0x10 or 16 in decimal)
pop cx ; Restore the state of our CX register
pop ax ; Restore the state of our AX register
ret ; Return to caller location
;------------------------------------------------------------------------------
; Print string subroutine (null terminated string print)
;------------------------------------------------------------------------------
print:
push ax ; Save the current value of the AX register
mov ah, 0Eh ; Our first BIOS interrupt: Teletype output
mov bl, 0Fh ; Don't worry about me until the end of this guide
.repeat:
lodsb ; Load next character into AL register
cmp al, 00h ; Check if we are at end of string (0 = end of string)
je .done ; If AL is 0 then jump to done label
int 10h ; BIOS interrupt 10h (0x10 or 16 in decimal)
jmp .repeat ; Continue to next character in the string
.done:
pop ax ; Restore the value to the AX register
ret ; Return to caller location
;------------------------------------------------------------------------------
; Boot loaders are 512 bytes in size so pad the remaining bytes with 0
;------------------------------------------------------------------------------
times 510-($-$$) db 0 ; Pad (510 - current position) bytes of 0
dw 0xAA55 ; Boot sector code trailer
BITS 16 ; Instruct the system this is 16-bit code
; This is the entry point, nothing should happen before this
; other than setting the instruction size
main:
mov ax, 07C0h ; Setup 4KB stack space after this bootloader
add ax, 288 ; (4096+515) / 16 bytes (aligned) per paragraph
cli ; Disable interrupts (solvs old DOS bug)
mov ss, ax ; Assign current stack segment
mov sp, 4096 ; Setup our stack pointer
sti ; Enable interrupts (solvs old DOS bug)
mov ax, 07C0h ; 07C0h is where our program is located
mov ds, ax ; Set data segment to the load point of our program
call run ; Start the main loop
;------------------------------------------------------------------------------
; Constants
;------------------------------------------------------------------------------
s_hi db "Hello, World!", 0Dh, 0Ah, "- Brent", 0Dh, 0Ah, 00h
s_nl db 0Dh, 0Ah, 00h
;------------------------------------------------------------------------------
; The main loop of our program
;------------------------------------------------------------------------------
run:
call set_graphics ; Go into graphics mode
call plot_pixel ; Plot our white pixel on the screen
.loop:
call read_keyboard ; Call our keyboard input subroutine
je .loop ; Return to loop if no key was pressed
call ah_to_str ; A key was pressed, so let's print it!
mov si, s_nl ; Move the new line bytes into SI register
call print ; Print a new line for readability
jmp .loop ; Infinite loop to hold control of the computer
;------------------------------------------------------------------------------
; Set graphics mode
;------------------------------------------------------------------------------
set_graphics:
mov ah, 00h
mov al, 12h ; 640x480 VGA
int 10h
ret
;------------------------------------------------------------------------------
; Plot a pixel
;------------------------------------------------------------------------------
plot_pixel:
mov ah, 0Ch ; Write pixel function code
mov al, 0Fh ; Color (white)
mov cx, 0Fh ; X position
mov dx, 0Fh ; Y position
int 10h ; BIOS interrupt for screen functions
ret
;------------------------------------------------------------------------------
; Reading keyboard input
;------------------------------------------------------------------------------
read_keyboard:
mov ah, 00h ; 00h is the get key and clear buffer function in BIOS
int 16h ; Call the BIOS interrupt for keyboard functions
test ah, ah ; AH will be 0 if no key was pressed, allow je after
ret ; Return to caller with ZF and AH set
;------------------------------------------------------------------------------
; Debug printing of bytes
;------------------------------------------------------------------------------
ah_to_str:
push ax ; Save the state of our AX register
push cx ; Save the state of our CX register
mov ch, 80h ; Set bit flag for AND as 10000000
mov al, ah ; Copy AH to AL so we can change AH
.ah_to_str_loop:
mov ah, al ; Restore original value of AH
and ah, ch ; Get the bit value for printing
test ah, ah ; Check to see if the bit is zero or 1
je .ah_to_str_zero ; The bit was 0 so jump to print 0
mov ah, 31h ; The bit was 1 so set AH to ascii 1
jmp .ah_to_str_print ; Jump to print the character
.ah_to_str_zero:
mov ah, 30h ; The bit was 0 so set AH to ascii 0
.ah_to_str_print:
call print_char ; Call our print_char routine below
SHR ch, 01h ; Shift CH right by 1 (0100000 first time)
cmp ch, 00h ; Check to see if we shifted all the way
jne .ah_to_str_loop ; If we haven't finished, return to loop
pop cx ; Restore the state of our CX register
pop ax ; Restore the state of our AX register
ret ; Return to caller location
print_char:
push ax ; Save the state of our AX register
push cx ; Save the CX register (due to int 10h clobbering)
mov al, ah ; AL is used for the character to print
mov ah, 0Eh ; Teletype BIOS interrupt function
mov bl, 0Fh ; Don't worry about me until the end of this guide
int 10h ; BIOS interrupt 10h (0x10 or 16 in decimal)
pop cx ; Restore the state of our CX register
pop ax ; Restore the state of our AX register
ret ; Return to caller location
;------------------------------------------------------------------------------
; Print string subroutine (null terminated string print)
;------------------------------------------------------------------------------
print:
push ax ; Save the current value of the AX register
mov ah, 0Eh ; Our first BIOS interrupt: Teletype output
mov bl, 0Fh ; Don't worry about me until the end of this guide
.repeat:
lodsb ; Load next character into AL register
cmp al, 00h ; Check if we are at end of string (0 = end of string)
je .done ; If AL is 0 then jump to done label
int 10h ; BIOS interrupt 10h (0x10 or 16 in decimal)
jmp .repeat ; Continue to next character in the string
.done:
pop ax ; Restore the value to the AX register
ret ; Return to caller location
;------------------------------------------------------------------------------
; Boot loaders are 512 bytes in size so pad the remaining bytes with 0
;------------------------------------------------------------------------------
times 510-($-$$) db 0 ; Pad (510 - current position) bytes of 0
dw 0xAA55 ; Boot sector code trailer
BITS 16 ; Instruct the system this is 16-bit code
; This is the entry point, nothing should happen before this
; other than setting the instruction size
main:
mov ax, 07C0h ; Setup 4KB stack space after this bootloader
add ax, 288 ; (4096+515) / 16 bytes (aligned) per paragraph
cli ; Disable interrupts (solves old DOS bug)
mov ss, ax ; Assign current stack segment
mov sp, 4096 ; Setup our stack pointer
sti ; Enable interrupts (solvs old DOS bug)
mov ax, 07C0h ; 07C0h is where our program is located
mov ds, ax ; Set data segment to the load point of our program
call run ; Start the main loop
;------------------------------------------------------------------------------
; Constants
;------------------------------------------------------------------------------
s_hi db "Hello, World!", 0Dh, 0Ah, "- Brent", 0Dh, 0Ah, 00h
;------------------------------------------------------------------------------
; The main loop of our program
;------------------------------------------------------------------------------
run:
mov si, s_hi ; Set our si register to point to the hello message
call print ; Call our print subroutine to print the message
.loop:
jmp .loop ; Infinite loop to hold control of the computer
;------------------------------------------------------------------------------
; Print string subroutine (null terminated string print)
;------------------------------------------------------------------------------
print:
push ax ; Save the current value of the AX register
mov ah, 0Eh ; Our first BIOS interrupt: Teletype output
mov bl, 0Fh ; Don't worry about me until the end of this guide
.repeat:
lodsb ; Load next character into AL register
cmp al, 00h ; Check if we are at end of string (0 = end of string)
je .done ; If AL is 0 then jump to done label
int 10h ; BIOS interrupt 10h (0x10 or 16 in decimal)
jmp .repeat ; Continue to next character in the string
.done:
pop ax ; Restore the value to the AX register
ret ; Return to caller location
;------------------------------------------------------------------------------
; Boot loaders are 512 bytes in size so pad the remaining bytes with 0
;------------------------------------------------------------------------------
times 510-($-$$) db 0 ; Pad (510 - current position) bytes of 0
dw 0xAA55 ; Boot sector code trailer
print_char:
push ax ; Save the state of our AX register
push cx ; Save the CX register (due to int 10h clobbering)
mov al, ah ; AL is used for the character to print
mov bl, 01h ; BLUE TEXT!!!
mov ah, 0Eh ; Teletype BIOS interrupt function
int 10h ; BIOS interrupt 10h (0x10 or 16 in decimal)
pop cx ; Restore the state of our CX register
pop ax ; Restore the state of our AX register
ret ; Return to caller location
BITS 16 ; Instruct the system this is 16-bit code
; This is the entry point, nothing should happen before this
; other than setting the instruction size
main:
mov ax, 07C0h ; Setup 4KB stack space after this bootloader
add ax, 288 ; (4096+515) / 16 bytes (aligned) per paragraph
cli ; Disable interrupts (solvs old DOS bug)
mov ss, ax ; Assign current stack segment
mov sp, 4096 ; Setup our stack pointer
sti ; Enable interrupts (solvs old DOS bug)
mov ax, 07C0h ; 07C0h is where our program is located
mov ds, ax ; Set data segment to the load point of our program
call run ; Start the main loop
;------------------------------------------------------------------------------
; Constants
;------------------------------------------------------------------------------
s_hi db "Hello, World!", 0Dh, 0Ah, "- Brent", 0Dh, 0Ah, 00h
s_nl db 0Dh, 0Ah, 00h
;------------------------------------------------------------------------------
; The main loop of our program
;------------------------------------------------------------------------------
run:
mov si, s_hi ; Set our SI register to hello message pointer
call print ; Call our print subroutine
mov ah, 09h ; Set the AH register to 9 so we can print it
call ah_to_str ; This should output 00001001 to the screen
.loop:
jmp .loop ; Infinite loop to hold control of the computer
;------------------------------------------------------------------------------
; Debug printing of bytes
;------------------------------------------------------------------------------
ah_to_str:
push ax ; Save the state of our AX register
push cx ; Save the state of our CX register
mov ch, 80h ; Set bit flag for AND as 10000000
mov al, ah ; Copy AH to AL so we can change AH
.ah_to_str_loop:
mov ah, al ; Restore original value of AH
and ah, ch ; Get the bit value for printing
test ah, ah ; Check to see if the bit is zero or 1
je .ah_to_str_zero ; The bit was 0 so jump to print 0
mov ah, 31h ; The bit was 1 so set AH to ascii 1
jmp .ah_to_str_print ; Jump to print the character
.ah_to_str_zero:
mov ah, 30h ; The bit was 0 so set AH to ascii 0
.ah_to_str_print:
call print_char ; Call our print_char routine below
SHR ch, 01h ; Shift CH right by 1 (0100000 first time)
cmp ch, 00h ; Check to see if we shifted all the way
jne .ah_to_str_loop ; If we haven't finished, return to loop
pop cx ; Restore the state of our CX register
pop ax ; Restore the state of our AX register
ret ; Return to caller location
print_char:
push ax ; Save the state of our AX register
push cx ; Save the CX register (due to int 10h clobbering)
mov al, ah ; AL is used for the character to print
mov ah, 0Eh ; Teletype BIOS interrupt function
mov bl, 0Fh ; Don't worry about me until the end of this guide
int 10h ; BIOS interrupt 10h (0x10 or 16 in decimal)
pop cx ; Restore the state of our CX register
pop ax ; Restore the state of our AX register
ret ; Return to caller location
;------------------------------------------------------------------------------
; Print string subroutine (null terminated string print)
;------------------------------------------------------------------------------
print:
push ax ; Save the current value of the AX register
mov ah, 0Eh ; Our first BIOS interrupt: Teletype output
mov bl, 0Fh ; Don't worry about me until the end of this guide
.repeat:
lodsb ; Load next character into AL register
cmp al, 00h ; Check if we are at end of string (0 = end of string)
je .done ; If AL is 0 then jump to done label
int 10h ; BIOS interrupt 10h (0x10 or 16 in decimal)
jmp .repeat ; Continue to next character in the string
.done:
pop ax ; Restore the value to the AX register
ret ; Return to caller location
;------------------------------------------------------------------------------
; Boot loaders are 512 bytes in size so pad the remaining bytes with 0
;------------------------------------------------------------------------------
times 510-($-$$) db 0 ; Pad (510 - current position) bytes of 0
dw 0xAA55 ; Boot sector code trailer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment