1 # pcboot - bootable PC demo/game kernel
2 # Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY, without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <https://www.gnu.org/licenses/>.
17 # this is the second-stage boot loader
21 .set main_load_addr, 0x100000
23 # make sure any BIOS call didn't re-enable interrupts
34 # enable A20 address line
37 # load the whole program into memory starting at 1MB
48 # enter protected mode for the first time
52 # inter-segment jump to set cs selector to segment 1
56 # set all data selectors to segment 2
82 gdt: # 0: null segment
85 # 1: code - base:0, lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:code/non-conf/rd
88 # 2: data - base:0, lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:data/rw
95 # trap gate 13: general protection fault
98 # type: trap, present, default
102 gpf_msg: .asciz "GP fault "
118 # use the same GDT above, will use data segment: 2
145 mainsz_msg: .asciz "Main program size: "
146 mainsz_msg2: .asciz " ("
147 mainsz_msg3: .asciz " sectors)\n"
156 movl $main_load_addr, dest_ptr
158 # calculate first sector
159 mov $_boot2_size, %eax
162 # add 1 to account for the boot sector
166 # calculate the first track (first_sect / sect_per_track)
167 movzxw sect_per_track, %ecx
171 # remainder is sector within track
174 mov $mainsz_msg, %esi
176 mov $_main_size, %eax
180 mov $mainsz_msg2, %esi
183 # calculate sector count
189 mov $mainsz_msg3, %esi
192 # read a whole track into the buffer (or partial first track)
194 movzxw sect_per_track, %ecx
204 # copy to high memory
214 # other than the first track which might be partial, all the rest start from 0
221 # the BIOS might have enabled interrupts
224 # just in case we were loaded from floppy, turn all floppy motors off
238 rdtrk_msg: .asciz "Reading track: "
239 rdcyl_msg: .asciz " - cyl: "
240 rdhead_msg: .asciz " head: "
241 rdsect_msg: .asciz " start sect: "
243 read_retries: .short 0
245 .set drive_number, 0x7bec
247 # set es to the start of the destination buffer to allow reading in
248 # full 64k chunks if necessary
254 movw $3, read_retries
265 # calc cylinder (cur_track / num_heads) and head (cur_track % num_heads)
267 movzxw num_heads, %ecx
275 mov $rdhead_msg, %esi
284 # cylinder low byte at ch and high bits at cl[7, 6]
291 mov $rdsect_msg, %esi
298 # start sector (1-based) in cl[0, 5]
304 # number of sectors in al
306 # call number (2) in ah
309 movb drive_number, %dl
313 # abort after 3 attempts
317 # error, reset controller and retry
329 # reset es to 0 before returning
334 str_read_error: .asciz "Read error while reading track: "
337 mov $str_read_error, %esi
350 # better print routines, since we're not constrainted by the 512b of
374 # this looks retarded. in nasm: [ebx * 2 + edx]
375 mov %al, (%edx, %ebx, 2)
376 movb $7, 1(%edx, %ebx, 2)
385 # expects string pointer in esi
395 # expects number in eax
400 mov $numbuf + 16, %esi
430 # move 80 * 24 lines from b80a0 -> b8000
435 # clear last line (b8f00)
450 .set UART_DATA, 0x3f8
451 .set UART_LSTAT, 0x3fd
452 .set LST_TREG_EMPTY, 0x20
465 # wait until the transmit register is empty
468 and $LST_TREG_EMPTY, %al
479 ena20_msg: .asciz "A20 line enabled\n"
490 # keep trying ... we can't do anything useful without A20 anyway
497 # CF = 1 if A20 test fails (not enabled)
501 movl $0xbaadf00d, (%ebx)
502 movl $0xaabbcc42, (%edx)
503 subl $0xbaadf00d, (%ebx)
506 # enable A20 line through port 0x92 (fast A20)
508 mov $ena20_fast_msg, %esi
516 ena20_fast_msg: .asciz "Attempting fast A20 enable\n"
519 # enable A20 line through the keyboard controller
520 .set KBC_DATA_PORT, 0x60
521 .set KBC_CMD_PORT, 0x64
522 .set KBC_STATUS_PORT, 0x64
523 .set KBC_CMD_RD_OUTPORT, 0xd0
524 .set KBC_CMD_WR_OUTPORT, 0xd1
526 .set KBC_STAT_OUT_RDY, 0x01
527 .set KBC_STAT_IN_FULL, 0x02
530 mov $ena20_kbd_msg, %esi
534 mov $KBC_CMD_WR_OUTPORT, %al
535 out %al, $KBC_CMD_PORT
538 out %al, $KBC_DATA_PORT
541 ena20_kbd_msg: .asciz "Attempting KBD A20 enable\n"
543 # wait until the keyboard controller is ready to accept another byte
545 in $KBC_STATUS_PORT, %al
546 and $KBC_STAT_IN_FULL, %al