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
88 times 1944 db 0 ; enough space for 256 gates
90 gpf_msg: db "GP fault "
107 ; use the same GDT as above, will use data seg: 2
134 mainsz_msg: db 'Program size: ',0
135 mainsz_msg2: db ' (',0
136 mainsz_msg3: db ' sectors)',10,0
145 mov dword [dest_ptr], LOADADDR
147 ; calculate first sector
151 ; add 1 to account for the boot sector
153 mov [first_sect], eax
155 ; calculate the first track (first_sect / sect_per_track)
156 movzx ecx, word [sect_per_track]
160 ; remainder is sector within track
172 ; calculate sector count
181 ; read a whole track into the buffer (or partial first track)
183 movzx ecx, word [sect_per_track]
188 ; copy to high memory
197 inc dword [cur_track]
198 ; other than the first track which might be partial, all the rest start from 0
199 mov dword [trk_sect], 0
205 ; the BIOS might have enabled interrupts
208 ; if we were loaded from floppy, turn all floppy motors off
209 mov bl, [DRIVENO_ADDR]
223 rdtrk_msg: db 'Reading track: ',0
224 rdcyl_msg: db ' - cyl: ',0
225 rdhead_msg: db ' head: ',0
226 rdsect_msg: db ' start sect: ',0
227 rdlast_msg: db ' ... ',0
228 rdok_msg: db 'OK',10,0
229 rdfail_msg: db 'failed',10,0
234 ; set es to the start of the destination buffer to allo readin in
235 ; full 64k chunks if necessary
241 mov word [read_retries], 3
252 ; calc cylinder (cur_track / num_heads) and head (cur_track % num_heads)
254 movzx ecx, word [num_heads]
271 ; cylinder low byte at ch and high bits at cl[7, 6]
285 ; start sector (1-based) in cl[0, 5]
291 ; number of sectors in al
293 ; call number (2) in ah
296 mov dl, [DRIVENO_ADDR]
300 ; abort after 3 attempts
301 dec word [read_retries]
304 ; error, reset controller and retry
318 ; reset es to 0 before returning
323 str_read_error: db 'Read error while reading track: ',0
326 mov esi, str_read_error
361 mov [ebx * 2 + edx], al
362 mov byte [ebx * 2 + edx + 1], 7
364 cmp dword [cursor_x], 80
372 ; expects string pointer in esi
382 ; expects number in eax
407 mov dword [cursor_x], 0
409 cmp dword [cursor_y], 25
416 ; clear with white-on-black spaces
430 DIV_9600 equ (115200 / 9600)
433 FIFO_ENABLE_CLEAR equ 07h
434 MCTL_DTR_RTS_OUT2 equ 0bh
435 LST_TREG_EMPTY equ 20h
452 ; clear and enable fifo
453 mov al, FIFO_ENABLE_CLEAR
457 mov al, MCTL_DTR_RTS_OUT2
472 ; wait until the transmit register is empty
475 and al, LST_TREG_EMPTY
484 ena20_msg: db 'A20 line enabled',13,0
502 ; CF = 1 if A20 test fails (not enabled)
506 mov dword [ebx], 0xbaadf00d
507 mov dword [edx], 0xaabbcc42
508 sub dword [ebx], 0xbaadf00d
511 ; enable A20 line through port 0x92 (fast A20)
513 mov esi, ena20_fast_msg
521 ena20_fast_msg: db 'Attempting fast A20 enable',10,0
523 ; enable A20 line through the keyboard controller
524 KBC_DATA_PORT equ 60h
526 KBC_STATUS_PORT equ 64h
527 KBC_CMD_RD_OUTPORT equ 0d0h
528 KBC_CMD_WR_OUTPORT equ 0d1h
530 KBC_STAT_OUT_RDY equ 01h
531 KBC_STAT_IN_FULL equ 02h
534 mov esi, ena20_kbd_msg
538 mov al, KBC_CMD_WR_OUTPORT
542 out KBC_DATA_PORT, al
545 ena20_kbd_msg: db 'Attempting KBD A20 enable',10,0
547 ; wait until the keyboard controller is ready to accept another byte
549 in al, KBC_STATUS_PORT
550 and al, KBC_STAT_IN_FULL
554 numbuf: times 16 db 0
557 ; this part is placed at the very end of all boot sections
560 ; buffer used by the track loader