## ============================= Memory Mapped Devices and Registers ============================= @define MBR_START_ADDRESS 0x1680 @define INT_VEC_START_ADDRESS 0x0480 @define DISK_INTERFACE_START_ADDRESS 0x1880 @define MEMORY_START_ADDRESS 0x1A40 @define CMOS_START_ADDRESS 0x0400 @define BIOS_VIDEO_START_ADDRESS 0x0780 @define BIOS_VIDEO_MEMORY_START_ADDRESS 0x07BC @define BIOS_VIDEO_MEMORY_END_ADDRESS 0x167F @define _BIOS_VIDEO_REG_CLEAR_COLOR {BIOS_VIDEO_START_ADDRESS + 0x0000} @define _BIOS_VIDEO_REG_SIGNAL {BIOS_VIDEO_START_ADDRESS + 0x0003} ## =============================================================================================== @define _CMOS_REG_BOOT_DISK {CMOS_START_ADDRESS + 0x0010} .load 0x0000 ## BIOS is mapped to address 0x0000 in memory .data $bios_version_number 0x00, 0x01 ## BIOS Version stored in memory ## ============================= BIOS Program ============================= .code mov FL, 0b0000000000000001 ## Zero the FL Register and enable interrupts movb [{INT_VEC_START_ADDRESS + (0x20 * 3)}], 0xFF ## Setting up int 0x20 handler mov [{INT_VEC_START_ADDRESS + (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 ## TODO: Probably move this code before interrupts are enabled ## to prevent an interrupt being fired while loading MBR ## data from disk ## ---- ## MBR Loading push 0 call $_load_mbr_data_block ## ---- jmp MBR_START_ADDRESS ## 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 [{DISK_INTERFACE_START_ADDRESS + 0x1}], *R9 ## Mode inc R9 movb [{DISK_INTERFACE_START_ADDRESS + 0x2}], *R9 ## Disk inc R9 mov [{DISK_INTERFACE_START_ADDRESS + 0x3}], *R9 ## Sector inc R9 inc R9 mov [{DISK_INTERFACE_START_ADDRESS + 0x5}], *R9 ## Address inc R9 inc R9 mov [{DISK_INTERFACE_START_ADDRESS + 0x7}], *R9 ## Size inc R9 inc R9 mov [{DISK_INTERFACE_START_ADDRESS + 0x9}], *R9 ## Memory Address movb [DISK_INTERFACE_START_ADDRESS], 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_clear_screen, 0x0000 jmp $_int_30_end _int_30_clear_screen: movb [_BIOS_VIDEO_REG_SIGNAL], 0x01 ## Signal = ClearScreen _int_30_end: rti ## ================================================================================== _load_mbr_data_block: movb [{DISK_INTERFACE_START_ADDRESS + 0x1}], 0x00 ## Mode = Read movb R1, [_CMOS_REG_BOOT_DISK] movb [{DISK_INTERFACE_START_ADDRESS + 0x2}], R1 ## Disk = Default mov [{DISK_INTERFACE_START_ADDRESS + 0x3}],0x0000 ## Sector = 0x0000 mov [{DISK_INTERFACE_START_ADDRESS + 0x5}],0x0000 ## Address = 0x0000 (Start of Disk) ##//TODO: currently set to 20 instead of 512, for debugging purposes mov [{DISK_INTERFACE_START_ADDRESS + 0x7}], 20 ## Size = 512 (Size of MBR is 512 bytes) mov [{DISK_INTERFACE_START_ADDRESS + 0x9}], MBR_START_ADDRESS ## MemoryAddress = MBR Address in memory movb [DISK_INTERFACE_START_ADDRESS], 0x00 ## Signal = Start _load_mbr_data_block_wait_loop: mov ACC, [{DISK_INTERFACE_START_ADDRESS + 0xB}] ## Moving register into ACC jne $_load_mbr_data_block_wait_loop, 0x00 ## If register not Free, loop around and wait ret _calc_interrupt_vector_address: mov R1, *PP mul R1, 0x03 ## Multiply the interrupt code by 3 for memory alignment mov R1, ACC add R1, INT_VEC_START_ADDRESS ## Add Interrupt Vector base address to Interrupt code mov RV, ACC ret _set_interrupt_vector_entry: mov R1, *PP push R1 push 1 call $_calc_interrupt_vector_address mov R1, RV dec PP dec PP mov ACC, *PP 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 .fixed 1024, 0x00 ## BIOS Needs to be 1024 Bytes exactly