312 lines
9.2 KiB
Text
312 lines
9.2 KiB
Text
## ============================= Memory Mapped Devices and Registers =============================
|
|
@group MemoryAddresses
|
|
MBR 0x1380
|
|
INT_VEC 0x1080
|
|
DISK_INTERFACE 0x1580
|
|
RAM 0x1740
|
|
CMOS 0x1000
|
|
VGA 0x1600
|
|
@end
|
|
|
|
@group CMOS_Settings
|
|
BOOT_DISK { MemoryAddresses.CMOS + 0x0010 }
|
|
@end
|
|
|
|
@group VGA_Registers
|
|
VIDEO_MODE { MemoryAddresses.VGA + 0x0000 }
|
|
SIGNAL { MemoryAddresses.VGA + 0x0003 }
|
|
TEXT_SINGLE_CHAR { MemoryAddresses.VGA + 0x0004 }
|
|
|
|
BUFF_START { MemoryAddresses.VGA + 0x00E0 }
|
|
@end
|
|
|
|
@group VGA_VideoModes
|
|
TEXT_SINGLE_COLOR 0x00
|
|
@end
|
|
|
|
@group Sig_VGA_Text_Single_Color
|
|
CONTINUE 0x00
|
|
PRINT_CHAR 0x02
|
|
STORE_CHAR 0x03
|
|
PRINT_FLUSH_BUFF 0x04
|
|
FLUSH_BUFF 0x05
|
|
PRINT_BUFF 0x06
|
|
|
|
REFRESH_SCREEN 0xE0
|
|
CLEAR_SCREEN 0xE1
|
|
@end
|
|
|
|
@define S_REG_1 0x07
|
|
@define S_REG_2 0x08
|
|
@define S_REG_3 0x09
|
|
## ===============================================================================================
|
|
|
|
.load 0x0000 ## BIOS is mapped to address 0x0000 in memory
|
|
.data
|
|
$_bios_name "DragonBIOS"
|
|
$_bios_version_number_maj 0x00, 0x00
|
|
$_bios_version_number_min 0x00, 0x03
|
|
|
|
## ============================= BIOS Program =============================
|
|
.code
|
|
movb [VGA_Registers.VIDEO_MODE], VGA_VideoModes.TEXT_SINGLE_COLOR ## Enable Text_Single_Color graphics mode
|
|
mov FL, 0b0000000000000001 ## Zero the FL Register and enable interrupts
|
|
movb [{MemoryAddresses.INT_VEC + (0x20 * 3)}], 0xFF ## Setting up int 0x20 handler
|
|
mov [{MemoryAddresses.INT_VEC + (0x20 * 3) + 1}], $_int_20_handler ## --
|
|
|
|
mov R10, 0x00 ## Setting up int 0x30 handler by using int 0x20 functionality
|
|
mov R9, $_int_30_handler ## -Passing the handler's address
|
|
mov R8, 0x30 ## -Passing the interrupt's code to setup
|
|
int 0x20 ## -Calling int 0x20 with 0x00 as parameter, to set new handler up
|
|
|
|
mov R10, 0x03
|
|
mov R9, $_bios_name
|
|
int 0x30
|
|
mov R10, 0x01
|
|
mov R9, 32
|
|
int 0x30
|
|
mov R10, 0x04
|
|
mov R9, [$_bios_version_number_maj]
|
|
int 0x30
|
|
mov R10, 0x01
|
|
mov R9, 46
|
|
int 0x30
|
|
mov R10, 0x04
|
|
mov R9, [$_bios_version_number_min]
|
|
int 0x30
|
|
mov R10, 0x02
|
|
int 0x30
|
|
|
|
## MBR Loading
|
|
and FL, 0b1111111111111110 ## Disable interrupts
|
|
mov FL, ACC
|
|
push 0
|
|
call $_load_mbr_data_block
|
|
or FL, 0b0000000000000001 ## Enable interrupts
|
|
mov FL, ACC
|
|
## ----
|
|
|
|
jmp MemoryAddresses.MBR ## Jump to start of MBR in memory
|
|
hlt ## Just in case somehow execution reaches this point
|
|
## ========================================================================
|
|
|
|
|
|
|
|
## ============================= BIOS Interrupt handler =============================
|
|
_int_20_handler:
|
|
mov ACC, R10
|
|
jeq $_int_20_disk_interface, 0x0010
|
|
jeq $_int_20_set_new_interrupt_handler, 0x0000
|
|
jeq $_int_20_clear_interrupt, 0x0001
|
|
jmp $_int_20_end
|
|
_int_20_disk_interface:
|
|
movb [{MemoryAddresses.DISK_INTERFACE + 0x1}], *R9 ## Mode
|
|
inc R9
|
|
movb [{MemoryAddresses.DISK_INTERFACE + 0x2}], *R9 ## Disk
|
|
inc R9
|
|
mov [{MemoryAddresses.DISK_INTERFACE + 0x3}], *R9 ## Sector
|
|
inc R9
|
|
inc R9
|
|
mov [{MemoryAddresses.DISK_INTERFACE + 0x5}], *R9 ## Address
|
|
inc R9
|
|
inc R9
|
|
mov [{MemoryAddresses.DISK_INTERFACE + 0x7}], *R9 ## Size
|
|
inc R9
|
|
inc R9
|
|
mov [{MemoryAddresses.DISK_INTERFACE + 0x9}], *R9 ## Memory Address
|
|
movb [MemoryAddresses.DISK_INTERFACE], 0x00 ## Signal set to "Start Operation"
|
|
jmp $_int_20_end
|
|
_int_20_set_new_interrupt_handler:
|
|
push R8 ## Interrupt Code
|
|
push R9 ## Handler Address
|
|
push 2
|
|
call $_set_interrupt_vector_entry
|
|
jmp $_int_20_end
|
|
_int_20_clear_interrupt:
|
|
push R9
|
|
push 0x0000
|
|
push 2
|
|
call $_set_interrupt_vector_entry
|
|
_int_20_end:
|
|
rti
|
|
## ==================================================================================
|
|
|
|
## ========================== BIOS Video Interrupt handler =========================
|
|
_int_30_handler:
|
|
mov ACC, R10
|
|
jeq $_int_30_direct_print_char_text_single, 0x0001
|
|
jeq $_int_30_direct_new_line_text_single, 0x0002
|
|
jeq $_int_30_direct_print_string_text_single, 0x0003
|
|
jeq $_int_30_direct_print_integer_text_single, 0x0004
|
|
jeq $_int_30_store_integer_text_single, 0x0005
|
|
jeq $_int_30_store_char_text_single, 0x0006
|
|
jeq $_int_30_print_buffer_no_flush_text_single, 0x0007
|
|
jeq $_int_30_print_buffer_and_flush_text_single, 0x0008
|
|
jeq $_int_30_flush_buffer_text_single, 0x0009
|
|
jeq $_int_30_store_string_text_single, 0x000A
|
|
jeq $_int_30_clear_screen, 0x00E0
|
|
jeq $_int_30_refresh_screen, 0x00E1
|
|
jmp $_int_30_end
|
|
|
|
|
|
_int_30_direct_print_char_text_single:
|
|
movb [VGA_Registers.TEXT_SINGLE_CHAR], R9
|
|
movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.PRINT_CHAR
|
|
jmp $_int_30_end
|
|
_int_30_direct_new_line_text_single:
|
|
movb [VGA_Registers.TEXT_SINGLE_CHAR], 10 ## New line char code
|
|
movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.STORE_CHAR
|
|
jmp $_int_30_end
|
|
_int_30_direct_print_string_text_single:
|
|
movb R1, *R9
|
|
movb [VGA_Registers.TEXT_SINGLE_CHAR], R1
|
|
movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.PRINT_CHAR
|
|
inc R9
|
|
movb ACC, *R9
|
|
jne $_int_30_direct_print_string_text_single, 0
|
|
jmp $_int_30_end
|
|
_int_30_direct_print_integer_text_single:
|
|
mov reg(S_REG_1), Sig_VGA_Text_Single_Color.PRINT_CHAR
|
|
push R9
|
|
push 1
|
|
call $_print_integer
|
|
jmp $_int_30_end
|
|
_int_30_store_integer_text_single:
|
|
mov reg(S_REG_1), Sig_VGA_Text_Single_Color.STORE_CHAR
|
|
push R9
|
|
push 1
|
|
call $_print_integer
|
|
jmp $_int_30_end
|
|
_int_30_store_char_text_single:
|
|
movb [VGA_Registers.TEXT_SINGLE_CHAR], R9
|
|
movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.STORE_CHAR
|
|
jmp $_int_30_end
|
|
_int_30_print_buffer_no_flush_text_single:
|
|
movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.PRINT_BUFF
|
|
_int_30_print_buffer_and_flush_text_single:
|
|
movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.PRINT_FLUSH_BUFF
|
|
jmp $_int_30_end
|
|
_int_30_flush_buffer_text_single:
|
|
movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.FLUSH_BUFF
|
|
jmp $_int_30_end
|
|
_int_30_store_string_text_single:
|
|
movb R1, *R9
|
|
movb [VGA_Registers.TEXT_SINGLE_CHAR], R1
|
|
movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.STORE_CHAR
|
|
inc R9
|
|
movb ACC, *R9
|
|
jne $_int_30_store_string_text_single, 0
|
|
jmp $_int_30_end
|
|
|
|
|
|
_int_30_clear_screen:
|
|
movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.CLEAR_SCREEN
|
|
jmp $_int_30_end
|
|
_int_30_refresh_screen:
|
|
movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.REFRESH_SCREEN
|
|
jmp $_int_30_end
|
|
_int_30_end:
|
|
rti
|
|
## ==================================================================================
|
|
|
|
|
|
_load_mbr_data_block:
|
|
movb [{MemoryAddresses.DISK_INTERFACE + 0x1}], 0x00 ## Mode = Read
|
|
movb R1, [CMOS_Settings.BOOT_DISK]
|
|
movb [{MemoryAddresses.DISK_INTERFACE + 0x2}], R1 ## Disk = Default
|
|
mov [{MemoryAddresses.DISK_INTERFACE + 0x3}],0x0000 ## Sector = 0x0000
|
|
mov [{MemoryAddresses.DISK_INTERFACE + 0x5}],0x0000 ## Address = 0x0000 (Start of Disk)
|
|
mov [{MemoryAddresses.DISK_INTERFACE + 0x7}], 512 ## Size = 512 (Size of MBR is 512 bytes)
|
|
mov [{MemoryAddresses.DISK_INTERFACE + 0x9}], MemoryAddresses.MBR ## MemoryAddress = MBR Address in memory
|
|
movb [MemoryAddresses.DISK_INTERFACE], 0x00 ## Signal = Start
|
|
_load_mbr_data_block_wait_loop:
|
|
mov ACC, [{MemoryAddresses.DISK_INTERFACE + 0xB}] ## Moving <Status> register into ACC
|
|
jne $_load_mbr_data_block_wait_loop, 0x00 ## If <Status> register not Free, loop around and wait
|
|
ret
|
|
|
|
_calc_interrupt_vector_address:
|
|
arg R1
|
|
mul R1, 0x03 ## Multiply the interrupt code by 3 for memory alignment
|
|
mov R1, ACC
|
|
add R1, MemoryAddresses.INT_VEC ## Add Interrupt Vector base address to Interrupt code
|
|
mov RV, ACC
|
|
ret
|
|
|
|
_set_interrupt_vector_entry:
|
|
arg R1
|
|
push R1
|
|
push 1
|
|
call $_calc_interrupt_vector_address
|
|
mov R1, RV
|
|
arg ACC
|
|
jeq $_set_interrupt_vector_entry_disable, 0x0000
|
|
movb *R1, 0xFF
|
|
jmp $_set_interrupt_vector_entry_end
|
|
_set_interrupt_vector_entry_disable:
|
|
movb *R1, 0x00
|
|
_set_interrupt_vector_entry_end:
|
|
inc R1
|
|
mov *R1, ACC
|
|
ret
|
|
|
|
|
|
_print_integer:
|
|
mov R1, 0
|
|
arg ACC
|
|
mov R3, 0 ## Used as boolean, 0 = positive, 1 = negative
|
|
jle $_print_integer_not_negative, 0
|
|
mov R3, 1
|
|
jeq $_print_integer_overflow_case, 0x8000
|
|
neg ACC
|
|
jmp $_print_integer_loop
|
|
_print_integer_overflow_case: ## Hardcoded for -32768 (0x8000)
|
|
push 56
|
|
push 54
|
|
push 55
|
|
push 50
|
|
push 51
|
|
mov R1, 5
|
|
jmp $_print_integer_loop_end
|
|
_print_integer_not_negative:
|
|
jne $_print_integer_loop, 0
|
|
push 48 ## '0' ASCII is 48
|
|
inc R1
|
|
jmp $_print_integer_loop_end
|
|
_print_integer_loop:
|
|
jeq $_print_integer_loop_end, 0
|
|
div ACC, 10
|
|
mov R2, ACC
|
|
add RV, 48 ## '0' ASCII is 48
|
|
push ACC
|
|
inc R1
|
|
mov ACC, R2
|
|
jmp $_print_integer_loop
|
|
_print_integer_loop_end:
|
|
mov ACC, R3
|
|
jne $_print_integer_no_sign, 1
|
|
push 45 ## '-' ASCII is 45
|
|
inc R1
|
|
_print_integer_no_sign:
|
|
pop R9
|
|
dec R1
|
|
movb [VGA_Registers.TEXT_SINGLE_CHAR], R9
|
|
movb [VGA_Registers.SIGNAL], reg(S_REG_1)
|
|
mov ACC, R1
|
|
jne $_print_integer_no_sign, 0
|
|
ret
|
|
|
|
|
|
_strlen:
|
|
arg R1
|
|
mov R2, 0
|
|
_strlen_loop:
|
|
movb ACC, *R1
|
|
inc R1
|
|
jeq $_strlen_loop_end, 0
|
|
inc R2
|
|
jmp $_strlen_loop
|
|
_strlen_loop_end:
|
|
mov RV, R2
|
|
ret
|
|
|
|
.fixed 4096, 0x00 ## BIOS Needs to be 4096 Bytes exactly
|