Here, for reference, is the multi-stage bootloader code:
;;;
;;; two-stage.s
;;; Illustrates a two-stage loader, where the first stage invokes the BIOS
;;; to load the second stage.
;;;
bits 16
org 0x7c00
start:
origin: equ 0x7c00
blk_count: equ (end - loaded_code) / 512 + 1
; -----------------------------------------------------------------------------
; First stage loader
; Reset disk
mov ah, 0x0 ; Subfunction reset
mov dl, 0x80 ; Disk number
int 0x13
; Load blocks
mov ah, 0x42 ; Int 0x13, subfunction Extended Read
mov dl, 0x80 ; Drive num
mov si, disk_packet ; Packet address
int 0x13
jmp loaded_code
; ----------------------------------------------------------------------------
; Begin "pseudo-data" section
string: db "Hello, world!"
strlen: equ $-string
screen_addr: equ 0xb8000
align 2
disk_packet: db 0x10 ; Packet size
db 0 ; Reserved
dw blk_count ; Block count
dd loaded_code ; Addr. to load
dd 1 ; Starting block
; Pad remainder with 0 bytes
times 510 - ($ - $$) db 0
; Write boot signature at end
dw 0xaa55
; -----------------------------------------------------------------------------
; Begin second-stage loader
loaded_code:
; Set 80x25 text mode
mov ah, 0x0
mov al, 0x3
int 0x10
; Print text
mov si, 0 ; Memory index/cursor position
print:
; Print character
mov ah, 0x0a ; Subfunction = write char
mov al, byte [si + string]
mov bh, 0 ; Page = 0
mov cx, 1 ; Write count = 1
int 0x10
; Move cursor
inc si
mov ah, 0x02 ; Subfunction = set cursor pos.
mov bh, 0 ; Page = 0
mov dh, 0 ; Cursor row = 0
mov dx, si ; Cursor col = si
mov dh, 0
int 0x10
cmp si, strlen
jne print
; Infinite loop
forever: jmp forever
end:
; Pad so there's a good number of blocks used in the disk
times 1024 * 1024 db 0
Take the multi-stage bootloader from above (the one that just prints “Hello, World!” using BIOS calls) and modify it so that it reads a line of input and then prints a response. The BIOS calls dealing with the keyboard are all under interrupt 0x16. In particular,
Subfunction
ah = 0x01
checks for a keypress. It clearszf
if a keypress is available, and if so, setsal
to the ASCII code for the character.Subfunction
ah = 0
waits for a single keypress, returning its ASCII code inal
.
You’ll need to do this in a loop, until the Enter key (ASCII code = 13) is pressed, and then print a response containing the text entered. You’ll have to allocate a buffer to store the entered string, and you’ll want to limit the length of the string that can be entered, so that you don’t overrun the buffer. Here’s a sample transcript:
Enter your name: Andy
Hello, Andy!
The program should go into an infinite loop at the end.
Remember that your code is running in 16-bit mode; that means
You can use 32-bit registers (
eax
), but you only have six (eax,ebx,ecx,edx,edi,esi
) to really work with. Only the first four have byte-sized versions, though.You don’t have a stack (unless you setup the stack segment and
esp
!)Memory operands are severely restricted: only
bx
andsi/di
can be used as base/offset. No scale factor can be used.If you stick with BIOS, you don’t really have to worry about segments. If you want to write directly to the screen, you’ll have to use the
es
segment to do so (and make sure that its set up first!).
Don’t forget that you can connect to your machine from GDB; you just have to open another PuTTY session!