Skip to content

Commit

Permalink
Read from the disk
Browse files Browse the repository at this point in the history
  • Loading branch information
taikiy committed Feb 9, 2023
1 parent 8912251 commit fc44421
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 35 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ all:
dd if=./message.txt >> ./boot.bin
dd if=/dev/zero bs=$(shell echo $$((512 - `stat message.txt | cut -d ' ' -f 8`))) count=1 >> ./boot.bin

run:
qemu-system-x86_64 -hda ./boot.bin

clean:
rm -rf ./boot.bin
57 changes: 25 additions & 32 deletions boot.asm
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,6 @@ times 33 db 0
init:
jmp 0x7c0:start

handle_zero:
mov ah, 0eh
mov al, 'A'
mov bx, 0x00
int 0x10
iret

handle_one:
mov ah, 0eh
mov al, 'B'
mov bx, 0x00
int 0x10
iret

start:
; setup the data segment
cli ; Disable interrupts. We don't want interrupts messing with registers
Expand All @@ -36,26 +22,29 @@ start:
mov sp, 0x7c00
sti ; Enables interrups

; Add a custom interrupt handler for Int0h (address 0x00).
; word[0x00] will use `ds` as the segment by default, which at this point of the code points at
; 0x7c0. We explicitly specify [ss:0x00], which points at 0x00, set in the code above.
mov word[ss:0x00], handle_zero ; First two bytes of RAM - offset
mov word[ss:0x02], 0x7c0 ; Second two bytes of RAM - segment

mov word[ss:0x04], handle_one
mov word[ss:0x06], 0x7c0
; Disk interrupt preparation
mov ah, 0x02 ; READ SECTOR command
mov al, 0x01 ; Read 1 sector
mov ch, 0x00 ; Cylinder number 0
mov cl, 0x02 ; Sector number 2
mov dh, 0x00 ; Head number 0
; We don't set DL. BIOS sets it to the booted disk
mov bx, buffer ; Data read will be buffered at ES:BX
int 0x13

int 0 ; same as mov ax, 0x00; div ax
int 1
jc error ; if the carry flag is set (error), jump

mov si, message ; SI = Source Index
mov si, buffer
call print

cld ; Clears direction flag
cli ; Disables interrupts
hlt ; This hangs the computer

; this hangs the computer
cli
hlt

error:
mov si, error_message
call print

print:
; setup INT10h params
Expand All @@ -75,8 +64,12 @@ print:
.done:
ret

message:
db 'Hello, World!', 0
error_message:
db 'Failed to load sector', 0

times 510 - ($ - $$) db 0 ; Pad the boot sector to 510 bytes
dw 0xAA55 ; Boot signature. 55AA (2 bytes) in the little-endian
dw 0xAA55 ; Boot signature. 55AA (2 bytes) in the little-endian

;; Everything under here is at the second sector from 0x7c00 (0x7e00)

buffer:
10 changes: 7 additions & 3 deletions doc/real_mode_development.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Real Mode Development

##
## Writing a bootloader

BIOS operates in _Real Mode_. Real mode (aka. read address mode) is an operating mode of all x86-compatible CPUs. All code in real mode is required to be 16 bits. Addresses in real mode correspond to real locations in memory. It uses a 20-bit _segmented memory_ address space (= 1MB of addressable memory) and unlimited direct software access to all addressable memory, I/O addresses and peripheral hardware. It does not provide memory protection, multitasking, or code privilege levels. [(wiki)](https://en.wikipedia.org/wiki/Real_mode)
Bootloader is a set of CPU instructions (usually written in assembly) that is loaded by the BIOS when a PC is booted. Bootloader's code sits at 0x7c00 when loaded into the memory, and must be 1-sector (= 512 bytes) long. The end of the bootloader code is marked by 2-byte signature `0x55AA`.

BIOS operates in _Real Mode_. Real Mode (aka. read address mode) is an operating mode available to all x86-compatible CPUs. All code in real mode is required to be 16 bits. Addresses in real mode correspond to real locations in memory. It uses a 20-bit _segmented memory_ address space (= 1MB of addressable memory) and unlimited direct software access to all addressable memory, I/O addresses and peripheral hardware. It does not provide memory protection, multitasking, or code privilege levels. [(wiki)](https://en.wikipedia.org/wiki/Real_mode)

## 1. Printing a string to the screen

Expand Down Expand Up @@ -117,10 +119,12 @@ To define a custom interrupt handler, we first define a routine with a label, an

We add [`message.txt`](../message.txt) file and append the content to `boot.bin` using `dd` command (see [`Makefile`](../Makefile)). When we start up a QEMU with `boot.bin`, it treats it as a hard disk.

Whenever CPU reads data from a hard disk, it must be one full block (512 bytes). We need to make sure that our message (starting from the second sector 0x200 because we use the first sector for our bootloader code), is padded with zeros until the end of the sector
Whenever CPU reads data from a hard disk, it must be one full sector (512 bytes). We need to make sure that our message (starting from the second sector 0x200 because we use the first sector for our bootloader code), is padded with zeros until the end of the sector

Disk access is done via [`Int13h/AH=02h`](http://www.ctyme.com/intr/rb-0607.htm).

Note how we created an empty label called `buffer` at the very end of the bootloader code. Because the bootloader code is 512 bytes, this label is not loaded into the memory. That doesn't mean we cannot use the memory pointed by this label. Since the label is at the end, it points to 0x7e00 = 0x7c00 (start of the bootloader) + 0x200 (512 bytes).

## Notes

- `lodsb` is one of x86 memory segmentation instructions. It uses DS (Data Segment) and SI (Source Index) registers. The real memory address is `DS * 16 + SI`.

0 comments on commit fc44421

Please sign in to comment.