1 ; vi:filetype=nasm ts=8 sts=8 sw=8:
2 ; second stage boot loader
18 mov al, [DRIVENO_ADDR]
29 ; enable A20 address line
32 ; load program into memory starting at 1MB
35 ; switch video mode, can't do that easily from protected mode
43 ; enter protected mode
47 ; inter-segment jump to set cs selector to segment 1
51 .pmode: ; set all data selectors to segment 2
70 gdt: ; 0: null segment
73 ; 1: code - base:0, lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:code/non-conf/rd
76 ; 2: data - base:0, lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:data/rw
82 ; trap gate 13: general protection fault
85 dw 8f00h ; type: trap, present, default
88 gpf_msg: db "GP fault "
105 ; use the same GDT as above, will use data seg: 2
132 mainsz_msg: db 'Program size: ',0
133 mainsz_msg2: db ' (',0
134 mainsz_msg3: db ' sectors)',10,0
143 mov dword [dest_ptr], LOADADDR
145 ; calculate first sector
149 ; add 1 to account for the boot sector
151 mov [first_sect], eax
153 ; calculate the first track (first_sect / sect_per_track)
154 movzx ecx, word [sect_per_track]
158 ; remainder is sector within track
170 ; calculate sector count
179 ; read a whole track into the buffer (or partial first track)
181 movzx ecx, word [sect_per_track]
186 ; copy to high memory
195 inc dword [cur_track]
196 ; other than the first track which might be partial, all the rest start from 0
197 mov dword [trk_sect], 0
203 ; the BIOS might have enabled interrupts
206 ; if we were loaded from floppy, turn all floppy motors off
207 mov bl, [DRIVENO_ADDR]
221 rdtrk_msg: db 'Reading track: ',0
222 rdcyl_msg: db ' - cyl: ',0
223 rdhead_msg: db ' head: ',0
224 rdsect_msg: db ' start sect: ',0
225 rdlast_msg: db ' ... ',0
226 rdok_msg: db 'OK',10,0
227 rdfail_msg: db 'failed',10,0
232 ; set es to the start of the destination buffer to allo readin in
233 ; full 64k chunks if necessary
239 mov word [read_retries], 3
250 ; calc cylinder (cur_track / num_heads) and head (cur_track % num_heads)
252 movzx ecx, word [num_heads]
269 ; cylinder low byte at ch and high bits at cl[7, 6]
283 ; start sector (1-based) in cl[0, 5]
289 ; number of sectors in al
291 ; call number (2) in ah
294 mov dl, [DRIVENO_ADDR]
298 ; abort after 3 attempts
299 dec word [read_retries]
302 ; error, reset controller and retry
316 ; reset es to 0 before returning
321 str_read_error: db 'Read error while reading track: ',0
324 mov esi, str_read_error
359 mov [ebx * 2 + edx], al
360 mov byte [ebx * 2 + edx + 1], 7
362 cmp dword [cursor_x], 80
370 ; expects string pointer in esi
380 ; expects number in eax
405 mov dword [cursor_x], 0
407 cmp dword [cursor_y], 25
414 ; clear with white-on-black spaces
428 DIV_9600 equ (115200 / 9600)
431 FIFO_ENABLE_CLEAR equ 07h
432 MCTL_DTR_RTS_OUT2 equ 0bh
433 LST_TREG_EMPTY equ 20h
450 ; clear and enable fifo
451 mov al, FIFO_ENABLE_CLEAR
455 mov al, MCTL_DTR_RTS_OUT2
470 ; wait until the transmit register is empty
473 and al, LST_TREG_EMPTY
482 ena20_msg: db 'A20 line enabled',13,0
500 ; CF = 1 if A20 test fails (not enabled)
504 mov dword [ebx], 0xbaadf00d
505 mov dword [edx], 0xaabbcc42
506 sub dword [ebx], 0xbaadf00d
509 ; enable A20 line through port 0x92 (fast A20)
511 mov esi, ena20_fast_msg
519 ena20_fast_msg: db 'Attempting fast A20 enable',10,0
521 ; enable A20 line through the keyboard controller
522 KBC_DATA_PORT equ 60h
524 KBC_STATUS_PORT equ 64h
525 KBC_CMD_RD_OUTPORT equ 0d0h
526 KBC_CMD_WR_OUTPORT equ 0d1h
528 KBC_STAT_OUT_RDY equ 01h
529 KBC_STAT_IN_FULL equ 02h
532 mov esi, ena20_kbd_msg
536 mov al, KBC_CMD_WR_OUTPORT
540 out KBC_DATA_PORT, al
543 ena20_kbd_msg: db 'Attempting KBD A20 enable',10,0
545 ; wait until the keyboard controller is ready to accept another byte
547 in al, KBC_STATUS_PORT
548 and al, KBC_STAT_IN_FULL
552 numbuf: times 16 db 0
555 ; this part is placed at the very end of all boot sections
558 ; buffer used by the track loader