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
83 ; trap gate 13: general protection fault
86 dw 8f00h ; type: trap, present, default
89 gpf_msg: db "GP fault "
106 ; use the same GDT as above, will use data seg: 2
133 mainsz_msg: db 'Program size: ',0
134 mainsz_msg2: db ' (',0
135 mainsz_msg3: db ' sectors)',10,0
144 mov dword [dest_ptr], LOADADDR
146 ; calculate first sector
150 ; add 1 to account for the boot sector
152 mov [first_sect], eax
154 ; calculate the first track (first_sect / sect_per_track)
155 movzx ecx, word [sect_per_track]
159 ; remainder is sector within track
171 ; calculate sector count
180 ; read a whole track into the buffer (or partial first track)
182 movzx ecx, word [sect_per_track]
187 ; copy to high memory
196 inc dword [cur_track]
197 ; other than the first track which might be partial, all the rest start from 0
198 mov dword [trk_sect], 0
204 ; the BIOS might have enabled interrupts
207 ; if we were loaded from floppy, turn all floppy motors off
208 mov bl, [DRIVENO_ADDR]
222 rdtrk_msg: db 'Reading track: ',0
223 rdcyl_msg: db ' - cyl: ',0
224 rdhead_msg: db ' head: ',0
225 rdsect_msg: db ' start sect: ',0
226 rdlast_msg: db ' ... ',0
227 rdok_msg: db 'OK',10,0
228 rdfail_msg: db 'failed',10,0
233 ; set es to the start of the destination buffer to allo readin in
234 ; full 64k chunks if necessary
240 mov word [read_retries], 3
251 ; calc cylinder (cur_track / num_heads) and head (cur_track % num_heads)
253 movzx ecx, word [num_heads]
270 ; cylinder low byte at ch and high bits at cl[7, 6]
284 ; start sector (1-based) in cl[0, 5]
290 ; number of sectors in al
292 ; call number (2) in ah
295 mov dl, [DRIVENO_ADDR]
299 ; abort after 3 attempts
300 dec word [read_retries]
303 ; error, reset controller and retry
317 ; reset es to 0 before returning
322 str_read_error: db 'Read error while reading track: ',0
325 mov esi, str_read_error
360 mov [ebx * 2 + edx], al
361 mov byte [ebx * 2 + edx + 1], 7
363 cmp dword [cursor_x], 80
371 ; expects string pointer in esi
381 ; expects number in eax
406 mov dword [cursor_x], 0
408 cmp dword [cursor_y], 25
415 ; clear with white-on-black spaces
429 DIV_9600 equ (115200 / 9600)
432 FIFO_ENABLE_CLEAR equ 07h
433 MCTL_DTR_RTS_OUT2 equ 0bh
434 LST_TREG_EMPTY equ 20h
451 ; clear and enable fifo
452 mov al, FIFO_ENABLE_CLEAR
456 mov al, MCTL_DTR_RTS_OUT2
471 ; wait until the transmit register is empty
474 and al, LST_TREG_EMPTY
483 ena20_msg: db 'A20 line enabled',13,0
501 ; CF = 1 if A20 test fails (not enabled)
505 mov dword [ebx], 0xbaadf00d
506 mov dword [edx], 0xaabbcc42
507 sub dword [ebx], 0xbaadf00d
510 ; enable A20 line through port 0x92 (fast A20)
512 mov esi, ena20_fast_msg
520 ena20_fast_msg: db 'Attempting fast A20 enable',10,0
522 ; enable A20 line through the keyboard controller
523 KBC_DATA_PORT equ 60h
525 KBC_STATUS_PORT equ 64h
526 KBC_CMD_RD_OUTPORT equ 0d0h
527 KBC_CMD_WR_OUTPORT equ 0d1h
529 KBC_STAT_OUT_RDY equ 01h
530 KBC_STAT_IN_FULL equ 02h
533 mov esi, ena20_kbd_msg
537 mov al, KBC_CMD_WR_OUTPORT
541 out KBC_DATA_PORT, al
544 ena20_kbd_msg: db 'Attempting KBD A20 enable',10,0
546 ; wait until the keyboard controller is ready to accept another byte
548 in al, KBC_STATUS_PORT
549 and al, KBC_STAT_IN_FULL
553 numbuf: times 16 db 0
556 ; this part is placed at the very end of all boot sections
559 ; buffer used by the track loader