1 ; vi:filetype=nasm ts=8 sts=8 sw=8:
2 ; second stage boot loader
18 mov al, [DRIVENO_ADDR]
28 ; enable A20 address line
31 ; load program into memory starting at 1MB
34 ; switch video mode, can't do that easily from protected mode
42 ; enter protected mode
46 ; inter-segment jump to set cs selector to segment 1
50 .pmode: ; set all data selectors to segment 2
69 gdt: ; 0: null segment
72 ; 1: code - base:0, lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:code/non-conf/rd
75 ; 2: data - base:0, lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:data/rw
81 ; trap gate 13: general protection fault
84 dw 8f00h ; type: trap, present, default
87 gpf_msg: db "GP fault "
104 ; use the same GDT as above, will use data seg: 2
131 mainsz_msg: db 'Program size: ',0
132 mainsz_msg2: db ' (',0
133 mainsz_msg3: db ' sectors)',10,0
142 mov dword [dest_ptr], LOADADDR
144 ; calculate first sector
148 ; add 1 to account for the boot sector
150 mov [first_sect], eax
152 ; calculate the first track (first_sect / sect_per_track)
153 movzx ecx, word [sect_per_track]
157 ; remainder is sector within track
169 ; calculate sector count
178 ; read a whole track into the buffer (or partial first track)
180 movzx ecx, word [sect_per_track]
185 ; copy to high memory
194 inc dword [cur_track]
195 ; other than the first track which might be partial, all the rest start from 0
196 mov dword [trk_sect], 0
202 ; the BIOS might have enabled interrupts
205 ; if we were loaded from floppy, turn all floppy motors off
206 mov bl, [DRIVENO_ADDR]
220 rdtrk_msg: db 'Reading track: ',0
221 rdcyl_msg: db ' - cyl: ',0
222 rdhead_msg: db ' head: ',0
223 rdsect_msg: db ' start sect: ',0
224 rdlast_msg: db ' ... ',0
225 rdok_msg: db 'OK',10,0
226 rdfail_msg: db 'failed',10,0
231 ; set es to the start of the destination buffer to allo readin in
232 ; full 64k chunks if necessary
238 mov word [read_retries], 3
249 ; calc cylinder (cur_track / num_heads) and head (cur_track % num_heads)
251 movzx ecx, word [num_heads]
268 ; cylinder low byte at ch and high bits at cl[7, 6]
282 ; start sector (1-based) in cl[0, 5]
288 ; number of sectors in al
290 ; call number (2) in ah
293 mov dl, [DRIVENO_ADDR]
297 ; abort after 3 attempts
298 dec word [read_retries]
301 ; error, reset controller and retry
315 ; reset es to 0 before returning
320 str_read_error: db 'Read error while reading track: ',0
323 mov esi, str_read_error
358 mov [ebx * 2 + edx], al
359 mov byte [ebx * 2 + edx + 1], 7
361 cmp dword [cursor_x], 80
369 ; expects string pointer in esi
379 ; expects number in eax
404 mov dword [cursor_x], 0
406 cmp dword [cursor_y], 25
413 ; clear with white-on-black spaces
427 DIV_9600 equ (115200 / 9600)
430 FIFO_ENABLE_CLEAR equ 07h
431 MCTL_DTR_RTS_OUT2 equ 0bh
432 LST_TREG_EMPTY equ 20h
449 ; clear and enable fifo
450 mov al, FIFO_ENABLE_CLEAR
454 mov al, MCTL_DTR_RTS_OUT2
469 ; wait until the transmit register is empty
472 and al, LST_TREG_EMPTY
481 ena20_msg: db 'A20 line enabled',13,0
499 ; CF = 1 if A20 test fails (not enabled)
503 mov dword [ebx], 0xbaadf00d
504 mov dword [edx], 0xaabbcc42
505 sub dword [ebx], 0xbaadf00d
508 ; enable A20 line through port 0x92 (fast A20)
510 mov esi, ena20_fast_msg
518 ena20_fast_msg: db 'Attempting fast A20 enable',10,0
520 ; enable A20 line through the keyboard controller
521 KBC_DATA_PORT equ 60h
523 KBC_STATUS_PORT equ 64h
524 KBC_CMD_RD_OUTPORT equ 0d0h
525 KBC_CMD_WR_OUTPORT equ 0d1h
527 KBC_STAT_OUT_RDY equ 01h
528 KBC_STAT_IN_FULL equ 02h
531 mov esi, ena20_kbd_msg
535 mov al, KBC_CMD_WR_OUTPORT
539 out KBC_DATA_PORT, al
542 ena20_kbd_msg: db 'Attempting KBD A20 enable',10,0
544 ; wait until the keyboard controller is ready to accept another byte
546 in al, KBC_STATUS_PORT
547 and al, KBC_STAT_IN_FULL
554 ; this part is placed at the very end of all boot sections
557 ; buffer used by the track loader