1 ! Boothead.s
- BIOS support for boot.c Author
: Kees J. Bot
4 ! This file contains the startup
and low level support for the secondary
5 ! boot program. It contains functions for disk
, tty
and keyboard I
/O
,
6 ! copying memory to arbitrary locations
, etc.
8 ! The primary bootstrap code supplies the following parameters in registers
:
10 ! es
:si
= Partition table entry if hard disk.
14 o32
= 0x66 ! This assembler doesn
't know 386 extensions
15 BOOTOFF = 0x7C00 ! 0x0000:BOOTOFF load a bootstrap here
16 LOADSEG = 0x1000 ! Where this code is loaded.
17 BUFFER = 0x0600 ! First free memory
18 PENTRYSIZE = 16 ! Partition table entry size.
19 a_flags = 2 ! From a.out.h, struct exec
24 A_SEP = 0x20 ! Separate I&D flag
25 K_I386 = 0x0001 ! Call Minix in 386 mode
26 K_RET = 0x0020 ! Returns to the monitor on reboot
27 K_INT86 = 0x0040 ! Requires generic INT support
28 K_MEML = 0x0080 ! Pass a list of free memory
30 DS_SELECTOR = 3*8 ! Kernel data selector
31 ES_SELECTOR = 4*8 ! Flat 4 Gb
32 SS_SELECTOR = 5*8 ! Monitor stack
33 CS_SELECTOR = 6*8 ! Kernel code
34 MCS_SELECTOR= 7*8 ! Monitor code
36 ESC = 0x1B ! Escape character
38 ! Imported variables and functions:
39 .extern _caddr, _daddr, _runsize, _edata, _end ! Runtime environment
40 .extern _device ! BIOS device number
41 .extern _rem_part ! To pass partition info
42 .extern _k_flags ! Special kernel flags
43 .extern _mem ! Free memory list
44 .extern _cdbooted ! Whether we booted from CD
45 .extern _cddevice ! Whether we booted from CD
50 ! Set segment registers and stack pointer using the programs own header!
51 ! The header is either 32 bytes (short form) or 48 bytes (long form). The
52 ! bootblock will jump to address 0x10030 in both cases, calling one of the
53 ! two jmpf instructions below.
55 ! CD bootblock jumps to address 0x10050 in both cases.
57 jmpf boot, LOADSEG+3 ! Set cs right (skipping long a.out header)
58 .space 11 ! jmpf + 11 = 16 bytes
59 jmpf boot, LOADSEG+2 ! Set cs right (skipping short a.out header)
60 .space 11 ! jmpf + 11 = 16 bytes
61 jmpf cdboot, LOADSEG+3
63 jmpf cdboot, LOADSEG+2
72 mov ds, ax ! ds = header
75 testb al, #A_SEP ! Separate I&D?
78 xchg ax, a_text ! No text
79 add a_data, ax ! Treat all text as data
81 mov ax, a_total ! Total nontext memory usage
82 and ax, #0xFFFE ! Round down to even
83 mov a_total, ax ! total - text = data + bss + heap + stack
84 cli ! Ignore interrupts while stack in limbo
85 mov sp, ax ! Set sp at the top of all that
87 mov ax, a_text ! Determine offset of ds above cs
92 mov ds, ax ! ds = cs + text / 16
95 push es ! Save es, we need it for the partition table
97 cld ! C compiler wants UP
101 mov di, #_edata ! Start of bss is at end of data
102 mov cx, #_end ! End of bss (begin of heap)
103 sub cx, di ! Number of bss bytes
104 shr cx, #1 ! Number of words
108 ! Copy primary boot parameters to variables. (Can do this now that bss is
109 ! cleared and may be written into).
111 mov _device, dx ! Boot device (probably 0x00 or 0x80)
112 mov _rem_part+0, si ! Remote partition table offset
113 pop _rem_part+2 ! and segment (saved es)
114 mov _cdbooted, bx ! Booted from CD? (bx set above)
116 ! Remember the current video mode for restoration on exit.
117 movb ah, #0x0F ! Get current video mode
119 andb al, #0x7F ! Mask off bit 7 (no blanking)
120 movb old_vid_mode, al
121 movb cur_vid_mode, al
123 ! Give C code access to the code segment, data segment and the size of this
137 mov ds, ax ! Back to the header once more
139 mov dx, a_total+2 ! dx:ax = data + bss + heap + stack
141 adc dx, a_text+2 ! dx:ax = text + data + bss + heap + stack
144 mov _runsize+2, dx ! 32 bit size of this process
146 ! Determine available memory as a list of (base,size) pairs as follows:
147 ! mem[0] = low memory, mem[1] = memory between 1M and 16M, mem[2] = memory
148 ! above 16M. Last two coalesced into mem[1] if adjacent.
149 mov di, #_mem ! di = memory list
150 int 0x12 ! Returns low memory size (in K) in ax
152 mov 4(di), ax ! mem[0].size = low memory size in bytes
155 cmp ax, #286 ! Only 286s and above have extended memory
157 cmp ax, #486 ! Assume 486s were the first to have >64M
158 jb small_ext ! (It helps to be paranoid when using the BIOS)
160 mov ax, #0xE801 ! Code for get memory size for >64M
161 int 0x15 ! ax = mem at 1M per 1K, bx = mem at 16M per 64K
164 movb ah, #0x88 ! Code for get extended memory size
165 clc ! Carry will stay clear if call exists
166 int 0x15 ! Returns size (in K) in ax for AT's
168 test ax
, ax
! An AT with no extended memory?
170 xor bx
, bx
! bx
= mem above
16M per
64K
= 0
172 mov cx
, ax
! cx
= copy of ext mem at
1M
173 mov
10(di
), #0x0010 ! mem[1].base = 0x00100000 (1M)
175 mov
12(di
), ax
! mem
[1].size = "ext mem at 1M" * 1024
178 jz no_ext
! No more ext mem above
16M?
179 cmp cx
, #15*1024 ! Chunks adjacent? (precisely 15M at 1M?)
181 mov
18(di
), #0x0100 ! mem[2].base = 0x01000000 (16M)
182 mov
22(di
), bx
! mem
[2].size = "ext mem at 16M" * 64K
185 add 14(di
), bx
! Add ext mem above
16M to mem below
16M
189 ! Time to switch to
a higher level language
(not much higher
)
192 ! void
..exit(int status)
193 ! Exit the monitor by rebooting the system.
194 .define _exit, __exit, ___exit ! Make various compilers happy
199 cmp 2(bx
), #0 ! Good exit status?
201 quit
: mov ax
, #any_key
204 xorb ah
, ah
! Read character from keyboard
206 reboot
: call dev_reset
208 int
0x19 ! Reboot the system
211 .ascii "\nHit any key to reboot\n\0"
214 ! u32_t mon2abs
(void
*ptr
)
215 ! Address in monitor data to absolute address.
220 mov dx
, ds
! Monitor data segment
223 ! u32_t vec2abs
(vector
*vec
)
224 ! 8086 interrupt vector to absolute address.
230 mov dx
, 2(bx
) ! dx
:ax vector
231 !jmp seg2abs
! Translate
233 seg2abs
: ! Translate dx
:ax to the
32 bit address dx-ax
238 shrb ch
, cl
! ch-dx
= dx
<< 4
240 adcb ch
, #0 ! ch-ax = ch-dx + ax
242 xorb dh
, dh
! dx-ax
= ch-ax
246 abs2seg
: ! Translate the
32 bit address dx-ax to dx
:ax
249 mov dx
, ax
! ch-dx
= dx-ax
250 and ax
, #0x000F ! Offset in ax
254 orb dh
, ch
! dx
= ch-dx
>> 4
258 ! void raw_copy
(u32_t dstaddr
, u32_t srcaddr
, u32_t count
)
259 ! Copy count bytes from srcaddr to dstaddr. Don
't do overlaps.
260 ! Also handles copying words to or from extended memory.
266 push di ! Save C variable registers
271 jcxz copydone ! Count is zero, end copy
274 bigcopy:mov cx, #0xFFF0 ! Don't copy more than about
64K at once
276 push cx
! Save copying count
279 cmp dx
, #0x0010 ! Copy to extended memory?
281 cmp 10(bp
), #0x0010 ! Copy from extended memory?
285 mov es
, dx
! es
:di
= dstaddr
290 mov ds
, dx
! ds
:si
= srcaddr
291 shr cx
, #1 ! Words to move
293 movs
! Do the word copy
294 adc cx
, cx
! One more byte?
296 movsb
! Do the byte copy
297 mov ax
, ss
! Restore ds
and es from the remaining ss
303 movb x_dst_desc+
4, dl
! Set base of destination segment
304 movb x_dst_desc+
7, dh
308 movb x_src_desc+
4, dl
! Set base of source segment
309 movb x_src_desc+
7, dh
310 mov si
, #x_gdt ! es:si = global descriptor table
311 shr cx
, #1 ! Words to move
312 movb ah
, #0x87 ! Code for extended memory move
315 pop cx
! Restore count
317 adc
6(bp
), #0 ! srcaddr += copycount
319 adc
10(bp
), #0 ! dstaddr += copycount
321 sbb
14(bp
), #0 ! count -= copycount
322 jmp copy
! and repeat
325 pop si
! Restore C variable registers
329 ! u16_t get_word
(u32_t addr
);
330 ! void put_word
(u32_t addr
, u16_t word
);
331 ! Read
or write
a 16 bits word at an arbitrary location.
332 .define _get_word, _put_word
336 mov ax
, (bx
) ! Word to get from addr
340 push
6(bx
) ! Word to store at addr
342 pop
(bx
) ! Store the word
349 mov ds
, dx
! ds
:bx
= addr
356 ! void relocate
(void
);
357 ! After the program has copied itself to
a safer place
, it needs to change
358 ! the segment registers. Caddr has already been set to the new location.
361 pop bx
! Return address
365 mov cx
, dx
! cx
= new code segment
366 mov ax
, cs
! Old code segment
367 sub ax
, cx
! ax
= -(new
- old
) = -Moving offset
370 mov ds
, dx
! ds
+= (new
- old
)
376 mov _daddr+
2, dx
! New data address
377 push cx
! New text segment
378 push bx
! Return offset of this function
381 ! void
*brk
(void
*addr
)
382 ! void
*sbrk
(size_t incr
)
383 ! Cannot fail implementations of brk
(2) and sbrk
(3), so we can use
384 ! malloc
(3). They reboot on stack collision instead of returning -1.
387 break
: .data2 _end ! A fake heap pointer
389 .define _brk, __brk, _sbrk, __sbrk
391 __brk
: ! __brk is for the standard C compiler
393 jmp sbrk
! break
= 0; return sbrk
(addr
);
396 mov ax
, break
! ax
= current break
397 sbrk
: push ax
! save it as future return value
398 mov bx
, sp
! Stack is now
: (retval
, retaddr
, incr
, ...)
399 add ax
, 4(bx
) ! ax
= break
+ increment
400 mov break
, ax
! Set new break
401 lea dx
, -1024(bx
) ! sp minus
a bit of breathing space
402 cmp dx
, ax
! Compare with the new break
403 jb heaperr
! Suffocating noises
404 lea dx
, -4096(bx
) ! A warning when heap+stack goes
< 4K
406 jae plenty
! No reason to complain
409 call _printf
! Warn about memory running low
411 movb memwarn
, #0 ! No more warnings
412 plenty
: pop ax
! Return old break
(0 for brk
)
414 heaperr
:mov ax
, #chmem
421 nomem
: .ascii "\nOut of%s\0"
422 memwarn
:.ascii "\nLow on"
423 chmem
: .ascii " memory, use chmem to increase the heap\n\0"
426 ! int dev_open
(void
);
427 ! Given the device
"_device" figure out if it exists
and what its number
428 ! of heads
and sectors may be. Return the BIOS error code on error
,
432 call dev_reset
! Optionally reset the disks
433 movb dev_state
, #0 ! State is "closed"
435 push di
! Save registers used by BIOS calls
436 movb dl
, _device
! The default device
439 cmpb dl
, #0x80 ! Floppy < 0x80, winchester >= 0x80
442 mov di
, #3 ! Three tries to init drive by reading sector 0
445 mov bx
, #BUFFER ! es:bx = scratch buffer
446 mov ax
, #0x0201 ! Read sector, #sectors = 1
447 mov cx
, #0x0001 ! Track 0, first sector
448 xorb dh
, dh
! Drive dl
, head
0
450 jnc finit0ok
! Sector
0 read ok?
451 cmpb ah
, #0x80 ! Disk timed out? (Floppy drive empty)
455 xorb ah
, ah
! Reset drive
458 jmp finit0
! Retry once more
, it may need to spin up
460 mov di
, #seclist ! List of per floppy type sectors/track
461 flast
: movb cl
, (di
) ! Sectors per track to test
462 cmpb cl
, #9 ! No need to do the last 720K/360K test
466 mov bx
, #BUFFER ! es:bx = scratch buffer
467 mov ax
, #0x0201 ! Read sector, #sectors = 1
468 xorb ch
, ch
! Track
0, last sector
469 xorb dh
, dh
! Drive dl
, head
0
471 jnc ftestok
! Sector cl read ok?
472 xorb ah
, ah
! Reset drive
475 inc di
! Try next sec
/track number
478 movb dh
, #2 ! Floppies have two sides
481 movb ah
, #0x08 ! Code for drive parameters
482 int
0x13 ! dl still contains drive
483 jc geoerr
! No such drive?
484 andb cl
, #0x3F ! cl = max sector number (1-origin)
485 incb dh
! dh
= 1 + max head number
(0-origin
)
488 movb cl
, #0x3F ! Think up geometry for CD's
491 movb sectors
, cl
! Sectors per track
492 movb al
, cl
! al
= sectors per track
493 mulb dh
! ax
= heads
* sectors
494 mov secspcyl
, ax
! Sectors per cylinder
= heads
* sectors
495 movb dev_state
, #1 ! Device state is "open"
496 xor ax
, ax
! Code for success
499 pop es
! Restore di
and es registers
502 xorb ah
, ah
! ax
= BIOS error code
506 .data1 18, 15, 9 ! 1.44M, 1.2M, and 360K/720K floppy sec/track
509 ! int dev_close
(void
);
510 ! Close the current device. Under the BIOS this does nothing much.
514 movb dev_state
, al
! State is
"closed"
517 ! Reset the disks if needed. Minix may have messed things up.
519 cmpb dev_state
, #0 ! Need reset if dev_state < 0
521 xorb ah
, ah
! Reset
(ah
= 0)
522 movb dl
, #0x80 ! All disks
524 movb dev_state
, #0 ! State is "closed"
527 ! int dev_boundary
(u32_t sector
);
528 ! True if
a sector is on
a boundary
, i.e. sector
% sectors
== 0.
529 .define _dev_boundary
533 mov ax
, 4(bx
) ! divide high half of sector number
535 mov ax
, 2(bx
) ! divide low half of sector number
536 div sectors
! dx
= sector
% sectors
537 sub dx
, #1 ! CF = dx == 0
538 sbb ax
, ax
! ax
= -CF
539 neg ax
! ax
= (sector
% sectors
) == 0
542 ! int biosreadsectors
(u32_t bufaddr
, u32_t sector
, u8_t count
)
543 ! int writesectors
(u32_t bufaddr
, u32_t sector
, u8_t count
)
544 ! Read
/write several sectors from
/to disk
or floppy. The buffer must
545 ! be between
64K boundaries
! Count must fit in
a byte. The external
546 ! variables _device
, sectors
and secspcyl describe the disk
and its
547 ! geometry. Returns
0 for success
, otherwise the BIOS error code.
549 .define _biosreadsectors, _writesectors
553 movb
13(bp
), #0x03 ! Code for a disk write
558 movb
13(bp
), #0x02 ! Code for a disk read
562 cmpb dev_state
, #0 ! Device state?
564 call _dev_open
! Initialize
571 mov es
, dx
! es
:bx
= bufaddr
572 mov di
, #3 ! Execute 3 resets on floppy error
575 mov di
, #1 ! But only 1 reset on hard disk error
576 nohd
: cmpb
12(bp
), #0 ! count equals zero?
579 mov dx
, 10(bp
) ! dx
:ax
= abs sector. Divide it by sectors
/cyl
580 cmp dx
, #[1024*255*63-255]>>16 ! Near 8G limit?
583 cmp si
, _cddevice
! Is it
a CD?
584 je bigdisk
! CD
's need extended read.
585 div secspcyl ! ax = cylinder, dx = sector within cylinder
586 xchg ax, dx ! ax = sector within cylinder, dx = cylinder
587 movb ch, dl ! ch = low 8 bits of cylinder
588 divb sectors ! al = head, ah = sector (0-origin)
589 xorb dl, dl ! About to shift bits 8-9 of cylinder into dl
591 shr dx, #1 ! dl[6..7] = high cylinder
592 orb dl, ah ! dl[0..5] = sector (0-origin)
593 movb cl, dl ! cl[0..5] = sector, cl[6..7] = high cyl
594 incb cl ! cl[0..5] = sector (1-origin)
595 movb dh, al ! dh = head
596 movb dl, _device ! dl = device to use
597 movb al, sectors ! Sectors per track - Sector number (0-origin)
598 subb al, ah ! = Sectors left on this track
599 cmpb al, 12(bp) ! Compare with # sectors to transfer
600 jbe doit ! Can't go past the end of
a cylinder?
601 movb al
, 12(bp
) ! 12(bp
) < sectors left on this track
602 doit
: movb ah
, 13(bp
) ! Code for disk read
(0x02) or write
(0x03)
603 push ax
! Save al
= sectors to read
604 int
0x13 ! call the BIOS to do the transfer
605 pop cx
! Restore al in cl
608 mov si
, #ext_rw ! si = extended read/write parameter packet
610 movb
2(si
), cl
! Fill in
# blocks to transfer
611 mov
4(si
), bx
! Buffer address
= es
:bx
613 mov
8(si
), ax
! Starting block number
= dx
:ax
615 movb dl
, _device
! dl
= device to use
616 mov ax
, #0x4000 ! This, or-ed with 0x02 or 0x03 becomes
617 orb ah
, 13(bp
) ! extended read
(0x4200) or write
(0x4300)
622 movb al
, cl
! Restore al
= sectors read
623 addb bh
, al
! bx
+= 2 * al
* 256 (add bytes transferred
)
624 addb bh
, al
! es
:bx
= where next sector is located
625 add 8(bp
), ax
! Update address by sectors transferred
626 adc
10(bp
), #0 ! Don't forget high word
627 subb
12(bp
), al
! Decrement sector count by sectors transferred
628 jnz more
! Not all sectors have been transferred
629 done
: xorb ah
, ah
! No error here
!
631 ioerr
: cmpb ah
, #0x80 ! Disk timed out? (Floppy drive empty)
633 cmpb ah
, #0x03 ! Disk write protected?
635 dec di
! Do we allow another reset?
636 jl finish
! No
, report the error
637 xorb ah
, ah
! Code for
a reset
(0)
639 jnc more
! Succesful reset
, try request again
641 xorb ah
, ah
! ax
= error number
649 ! Extended read
/write commands require
a parameter packet.
651 .data1 0x10 ! Length of extended r/w packet
653 .data2 0 ! Blocks to transfer (to be filled in)
654 .data2 0 ! Buffer address offset (tbfi)
655 .data2 0 ! Buffer address segment (tbfi)
656 .data4 0 ! Starting block number low 32 bits (tbfi)
657 .data4 0 ! Starting block number high 32 bits
661 ! Read
a character from the keyboard
, and check for an expired timer.
662 ! A carriage return is changed into
a linefeed for UNIX compatibility.
666 xchg ax
, unchar
! Ungotten character?
670 ! hlt
! Play dead until interrupted
(see pause
())
671 movb ah
, #0x01 ! Keyboard status
673 jz
0f
! Nothing typed
674 xorb ah
, ah
! Read character from keyboard
677 0: mov dx
, line
! Serial line?
680 add dx
, #5 ! Line Status Register
682 testb al
, #0x01 ! Data Ready?
685 !add dx
, 0 ! Receive Buffer Register
686 inb dx
! Get character
688 0: call _expired
! Timer expired?
691 mov ax
, #ESC ! Return ESC
694 cmpb al
, #0x0D ! Carriage return?
696 movb al
, #0x0A ! Change to linefeed
697 nocr
: cmpb al
, #ESC ! Escape typed?
699 inc escape
! Set flag
700 noesc
: xorb ah
, ah
! ax
= al
704 ! Return
a character to undo
a getch
().
713 ! True if ESC has been typed.
716 movb ah
, #0x01 ! Keyboard status
718 jz escflg
! Keypress?
719 cmpb al
, #ESC ! Escape typed?
721 xorb ah
, ah
! Discard the escape
723 inc escape
! Set flag
725 xchg ax
, escape
! Escape typed flag
729 ! Write
a character in teletype mode. The putk synonym is
730 ! for the kernel printf function that uses it.
731 ! Newlines are automatically preceded by
a carriage return.
733 .define _putch, _putk
736 movb al
, 2(bx
) ! al
= character to
be printed
737 testb al
, al
! Kernel printf adds
a null char to flush queue
739 cmpb al
, #0x0A ! al = newline?
742 call putc
! putc
('\r')
743 movb al
, #0x0A ! Restore the '\n' and print it
744 putc
: movb ah
, #0x0E ! Print character in teletype mode
745 mov bx
, #0x0001 ! Page 0, foreground color
747 mov bx
, line
! Serial line?
750 push ax
! Save character to print
751 call _get_tick
! Current clock tick counter
753 add cx
, #2 ! Don't want to see it count twice
754 1: lea dx
, 5(bx
) ! Line Status Register
756 testb al
, #0x20 ! Transmitter Holding Register Empty?
759 cmp ax
, cx
! Clock ticked more than once?
761 0: pop ax
! Restore character to print
762 mov dx
, bx
! Transmit Holding Register
763 outb dx
! Send character down the serial line
767 ! Wait for an interrupt using the HLT instruction. This either saves
768 ! power
, or tells an x86 emulator that nothing is happening right now.
774 ! void set_mode
(unsigned mode
);
775 ! void clear_screen
(void
);
776 ! Set video mode
/ clear the screen.
777 .define _set_mode, _clear_screen
780 mov ax
, 2(bx
) ! Video mode
782 je modeok
! Mode already as requested?
786 mov es
, ax
! es
= Vector segment
788 movb ch
, ah
! Copy of the special flags
789 andb ah
, #0x0F ! Test bits 8-11, clear special flags
790 jnz xvesa
! VESA extended mode?
791 int
0x10 ! Reset video
(ah
= 0)
793 xvesa
: mov bx
, ax
! bx
= extended mode
794 mov ax
, #0x4F02 ! Reset video
796 md_480
: ! Basic video mode is set
, now build on it
797 testb ch
, #0x20 ! 480 scan lines requested?
799 mov dx
, #0x3CC ! Get CRTC port
802 testb al
, #1 ! Mono or color?
805 0: mov ax
, #0x110C ! Vertical sync end (also unlocks CR0-7)
807 mov ax
, #0x060B ! Vertical total
809 mov ax
, #0x073E ! (Vertical) overflow
811 mov ax
, #0x10EA ! Vertical sync start
813 mov ax
, #0x12DF ! Vertical display end
815 mov ax
, #0x15E7 ! Vertical blank start
817 mov ax
, #0x1604 ! Vertical blank end
820 movb dl
, #0xCC ! Misc output register (read)
822 movb dl
, #0xC2 ! (write)
823 andb al
, #0x0D ! Preserve clock select bits and color bit
824 orb al
, #0xE2 ! Set correct sync polarity
826 pop dx
! Index register still in dx
828 testb ch
, #0x40 ! 9x14 point font requested?
830 mov ax
, #0x1111 ! Load ROM 9 by 14 font
831 xorb
bl, bl ! Load block
0
833 testb ch
, #0x20 ! 480 scan lines?
835 mov ax
, #0x12DB ! VGA vertical display end
837 eseg movb
0x0484, #33 ! Tell BIOS the last line number
839 testb ch
, #0x80 ! 8x8 point font requested?
841 mov ax
, #0x1112 ! Load ROM 8 by 8 font
842 xorb
bl, bl ! Load block
0
844 testb ch
, #0x20 ! 480 scan lines?
846 mov ax
, #0x12DF ! VGA vertical display end
848 eseg movb
0x0484, #59 ! Tell BIOS the last line number
850 xor dx
, dx
! dl
= column
= 0, dh
= row
= 0
852 movb ah
, #0x02 ! Set cursor position
858 ! Out to the usual
[index
, data
] port pair that are common for VGA devices
859 ! dx
= port
, ah
= index
, al
= data.
871 restore_video
: ! To restore the video mode on exit
878 ! void serial_init
(int line
)
879 ! Initialize copying console I
/O to
a serial line.
883 mov dx
, 2(bx
) ! Line number
885 test dx
, dx
! Off if line number
< 0
889 mov ds
, ax
! Vector
and BIOS data segment
890 mov bx
, dx
! Line number
891 shl bx
, #1 ! Word offset
892 mov bx
, 0x0400(bx
) ! I
/O port for the given line
894 mov line
, bx
! Remember I
/O port
897 test bx
, bx
! I
/O port must
be nonzero
899 mov ax
, #0x00E3 ! 9600 N-8-1
900 int
0x14 ! Initialize serial line dx
901 lea dx
, 4(bx
) ! Modem Control Register
902 movb al
, #0x0B ! DTR, RTS, OUT2
906 ! u32_t get_tick
(void
);
907 ! Return the current value of the clock tick counter. This counter
908 ! increments
18.2 times per second. Poll it to do delays. Does
not
909 ! work on the original PC
, but works on the PC
/XT.
913 xorb ah
, ah
! Code for get tick count
916 mov dx
, cx
! dx
:ax
= cx
:dx
= tick count
921 ! Functions used to obtain info about the hardware. Boot uses this information
922 ! itself
, but will also pass them on to
a pure
386 kernel
, because one can
't
923 ! make BIOS calls from protected mode. The video type could probably be
924 ! determined by the kernel too by looking at the hardware, but there is a small
925 ! chance on errors that the monitor allows you to correct by setting variables.
927 .define _get_bus ! returns type of system bus
928 .define _get_video ! returns type of display
930 ! u16_t get_bus(void)
931 ! Return type of system bus, in order: XT, AT, MCA.
934 xor dx, dx ! Assume XT
935 cmp ax, #286 ! An AT has at least a 286
938 movb ah, #0xC0 ! Code for get configuration
940 jc got_bus ! Carry clear and ah = 00 if supported
944 movb al, 5(bx) ! Load feature byte #1
946 testb al, #0x02 ! Test bit 1 - "bus is Micro Channel"
949 testb al, #0x40 ! Test bit 6 - "2nd 8259 installed"
955 mov ax, dx ! Return bus code
956 mov bus, ax ! Keep bus code, A20 handler likes to know
959 ! u16_t get_video(void)
960 ! Return type of display, in order: MDA, CGA, mono EGA, color EGA,
961 ! mono VGA, color VGA.
963 mov ax, #0x1A00 ! Function 1A returns display code
964 int 0x10 ! al = 1A if supported
966 jnz no_dc ! No display code function supported
969 cmpb bl, #5 ! Is it a monochrome EGA?
972 cmpb bl, #4 ! Is it a color EGA?
975 cmpb bl, #7 ! Is it a monochrome VGA?
978 cmpb bl, #8 ! Is it a color VGA?
981 no_dc: movb ah, #0x12 ! Get information about the EGA
984 cmpb bl, #0x10 ! Did it come back as 0x10? (No EGA)
988 cmpb bh, #1 ! Is it monochrome?
993 no_ega: int 0x11 ! Get bit pattern for equipment
994 and ax, #0x30 ! Isolate color/mono field
996 jz got_video ! Is it an MDA?
997 mov ax, #1 ! No it's CGA
1003 ! Functions to leave the boot monitor.
1004 .define _bootstrap ! Call another bootstrap
1005 .define _minix ! Call Minix
1007 ! void _bootstrap
(int device
, struct part_entry
*entry
)
1008 ! Call another bootstrap routine to boot MS-DOS for instance.
(No real
1009 ! need for that anymore
, now that you can format floppies under Minix
).
1010 ! The bootstrap must have been loaded at BOOTSEG from
"device".
1014 movb dl
, 2(bx
) ! Device to boot from
1015 mov si
, 4(bx
) ! ds
:si
= partition table entry
1017 mov es
, ax
! Vector segment
1018 mov di
, #BUFFER ! es:di = buffer in low core
1019 mov cx
, #PENTRYSIZE ! cx = size of partition table entry
1020 rep movsb
! Copy the entry to low core
1021 mov si
, #BUFFER ! es:si = partition table entry
1022 mov ds
, ax
! Some bootstraps need zero segment registers
1025 mov sp
, #BOOTOFF ! This should do it
1027 jmpf BOOTOFF
, 0 ! Back to where the BIOS loads the boot code
1029 ! void minix
(u32_t koff
, u32_t kcs
, u32_t kds
,
1030 ! char
*bootparams
, size_t paramsize
, u32_t aout
);
1034 mov bp
, sp
! Pointer to arguments
1036 mov dx
, #0x03F2 ! Floppy motor drive control bits
1037 movb al
, #0x0C ! Bits 4-7 for floppy 0-3 are off
1038 outb dx
! Kill the motors
1040 xor ax
, ax
! Vector
& BIOS data segments
1042 andb
0x043F, #0xF0 ! Clear diskette motor status bits of BIOS
1044 cli ! No more interruptions
1046 test _k_flags
, #K_I386 ! Switch to 386 mode?
1049 ! Call Minix in real mode.
1051 test _k_flags
, #K_MEML ! New memory arrangements?
1053 push
22(bp
) ! Address of
a.out headers
1056 push
18(bp
) ! # bytes of boot parameters
1057 push
16(bp
) ! Address of boot parameters
1059 test _k_flags
, #K_RET ! Can the kernel return?
1061 xor dx
, dx
! If little ext mem then monitor
not preserved
1063 cmp _mon_return
, ax
! Minix can return to the monitor?
1065 mov dx
, cs
! Monitor far return address
1067 0: push dx
! Push monitor far return address
or zero
1074 push dx
! Kernel code segment
1075 push
4(bp
) ! Kernel code offset
1079 mov ds
, dx
! Kernel data segment
1080 mov es
, dx
! Set es to kernel data too
1081 retf
! Make
a far call to the kernel
1083 ! Call Minix in
386 mode.
1085 cseg mov cs_real-
2, cs
! Patch CS
and DS into the instructions that
1086 cseg mov ds_real-
2, ds
! reload them when switching back to real mode
1087 .data1 0x0F,0x20,0xC0 ! mov eax, cr0
1088 orb al
, #0x01 ! Set PE (protection enable) bit
1090 mov msw
, ax
! Save as protected mode machine status word
1092 mov dx
, ds
! Monitor ds
1093 mov ax
, #p_gdt ! dx:ax = Global descriptor table
1095 mov p_gdt_desc+
2, ax
1096 movb p_gdt_desc+
4, dl
! Set base of global descriptor table
1099 mov dx
, 14(bp
) ! Kernel ds
(absolute address
)
1101 movb p_ds_desc+
4, dl
! Set base of kernel data segment
1103 mov dx
, ss
! Monitor ss
1104 xor ax
, ax
! dx
:ax
= Monitor stack segment
1105 call seg2abs
! Minix starts with the stack of the monitor
1107 movb p_ss_desc+
4, dl
1110 mov dx
, 10(bp
) ! Kernel cs
(absolute address
)
1112 movb p_cs_desc+
4, dl
1114 mov dx
, cs
! Monitor cs
1115 xor ax
, ax
! dx
:ax
= Monitor code segment
1117 mov p_mcs_desc+
2, ax
1118 movb p_mcs_desc+
4, dl
1121 test _k_flags
, #K_INT86 ! Generic INT86 support?
1123 push
#int86 ! Far address to INT86 support
1125 0: push
#bios13 ! Far address to BIOS int 13 support
1127 test _k_flags
, #K_MEML ! New memory arrangements?
1130 push
20(bp
) ! Address of
a.out headers
1133 push
18(bp
) ! 32 bit size of parameters on stack
1135 push
16(bp
) ! 32 bit address of parameters
(ss relative
)
1137 test _k_flags
, #K_RET ! Can the kernel return?
1140 push
#ret386 ! Monitor far return address
1146 push
4(bp
) ! 32 bit far address to kernel entry point
1148 call real2prot
! Switch to protected mode
1149 mov ax
, #DS_SELECTOR ! Kernel data
1151 mov ax
, #ES_SELECTOR ! Flat 4 Gb
1153 .data1 o32 ! Make a far call to the kernel
1156 ! Minix-
86 returns here on
a halt
or reboot.
1158 mov _reboot_code+
0, ax
1159 mov _reboot_code+
2, dx
! Return value
(obsolete method
)
1162 ! Minix-
386 returns here on
a halt
or reboot.
1165 mov _reboot_code
, ax
! Return value
(obsolete method
)
1166 call prot2real
! Switch to real mode
1169 mov sp
, bp
! Pop parameters
1170 sti
! Can take interrupts again
1172 call _get_video
! MDA
, CGA
, EGA
, ...
1173 movb dh
, #24 ! dh = row 24
1174 cmp ax
, #2 ! At least EGA?
1175 jb is25
! Otherwise
25 rows
1177 xor ax
, ax
! Vector
& BIOS data segments
1179 movb dh
, 0x0484 ! Number of rows on display minus one
1182 xorb dl
, dl
! dl
= column
0
1183 xorb bh
, bh
! Page
0
1184 movb ah
, #0x02 ! Set cursor position
1187 movb dev_state
, #-1 ! Minix may have upset the disks, must reset.
1188 call serial_init
! Likewise with our serial console
1196 movb ah
, #0x02 ! Get real-time clock time (from CMOS clock)
1198 jc tryclk
! Carry set
, not running
or being updated
1199 movb al
, ch
! ch
= hour in BCD
1200 call bcd
! al
= (al
>> 4) * 10 + (al
& 0x0F)
1201 mulb c60
! 60 minutes in an hour
1202 mov bx
, ax
! bx
= hour
* 60
1203 movb al
, cl
! cl
= minutes in BCD
1205 add bx
, ax
! bx
= hour
* 60 + minutes
1206 movb al
, dh
! dh
= seconds in BCD
1208 xchg ax
, bx
! ax
= hour
* 60 + minutes
, bx
= seconds
1209 mul c60
! dx-ax
= (hour
* 60 + minutes
) * 60
1211 adc dx
, #0 ! dx-bx = seconds since midnight
1216 add dx
, bx
! dx-ax
= dx-bx
* (0x1800B0 / (2*2*2*2*5))
1217 mov cx
, ax
! (0x1800B0 = ticks per day of BIOS clock
)
1222 div c1080
! cx-ax
= dx-ax
/ (24*60*60 / (2*2*2*2*5))
1223 mov dx
, ax
! cx-dx
= ticks since midnight
1224 movb ah
, #0x01 ! Set system time
1229 ret
! Return to monitor as if nothing much happened
1231 ! Transform BCD number in al to
a regular value in ax.
1235 .data1 0xD5,10 ! aad ! ax = (al >> 4) * 10 + (al & 0x0F)
1236 ret
! (BUG
: assembler messes up aad
& aam
!)
1238 ! Support function for Minix-
386 to make
a BIOS int
13 call
(disk I
/O
).
1242 sti
! Enable interrupts
1244 mov ax
, 8(bp
) ! Load parameters
1249 int
0x13 ! Make the BIOS call
1250 mov
8(bp
), ax
! Save results
1256 cli ! Disable interrupts
1258 mov ax
, #DS_SELECTOR ! Kernel data
1261 retf
! Return to the kernel
1263 ! Support function for Minix-
386 to make an
8086 interrupt call.
1270 mov es
, ax
! Vector
& BIOS data segments
1272 eseg mov
0x046C, ax
! Clear BIOS clock tick counter
1274 sti
! Enable interrupts
1276 movb al
, #0xCD ! INT instruction
1277 movb ah
, 8(bp
) ! Interrupt number?
1279 jnz
0f
! Nonzero if INT
, otherwise far call
1281 push
#intret+2 ! Far return address
1283 push
12(bp
) ! Far driver address
1284 mov ax
, #0x90CB ! RETF; NOP
1286 cseg
cmp ax
, intret
! Needs to
be changed?
1287 je
0f
! If
not then avoid
a huge I-cache stall
1288 cseg mov intret
, ax
! Patch
'INT n' or 'RETF; NOP' into code
1289 jmp
.+2 ! Clear instruction queue
1291 mov ds
, 16(bp
) ! Load parameters
1308 intret
: int
0xFF ! Do the interrupt
or far call
1310 .data1 o32 ! Save results
1316 pop
8+8(bp
) ! eflags
1334 cli ! Disable interrupts
1337 mov ds
, ax
! Vector
& BIOS data segments
1339 mov cx
, 0x046C ! Collect lost clock ticks in ecx
1342 mov ds
, ax
! Restore monitor ds
1344 mov ax
, #DS_SELECTOR ! Kernel data
1347 retf
! Return to the kernel
1349 ! Switch from real to protected mode.
1351 movb ah
, #0x02 ! Code for A20 enable
1354 lgdt p_gdt_desc
! Global descriptor table
1356 mov ax
, pdbr
! Load page directory base register
1357 .data1 0x0F,0x22,0xD8 ! mov cr3, eax
1358 .data1 0x0F,0x20,0xC0 ! mov eax, cr0
1360 xchg ax
, msw
! Exchange real mode msw for protected mode msw
1361 .data1 0x0F,0x22,0xC0 ! mov cr0, eax
1362 jmpf cs_prot
, MCS_SELECTOR
! Set code segment selector
1364 mov ax
, #SS_SELECTOR ! Set data selectors
1370 ! Switch from protected to real mode.
1372 lidt p_idt_desc
! Real mode interrupt vectors
1373 .data1 0x0F,0x20,0xD8 ! mov eax, cr3
1375 mov pdbr
, ax
! Save page directory base register
1376 .data1 0x0F,0x20,0xC0 ! mov eax, cr0
1378 xchg ax
, msw
! Exchange protected mode msw for real mode msw
1379 .data1 0x0F,0x22,0xC0 ! mov cr0, eax
1380 jmpf cs_real
, 0xDEAD ! Reload cs register
1384 mov ds
, ax
! Reload data segment registers
1388 xorb ah
, ah
! Code for A20 disable
1391 ! Enable
(ah
= 0x02) or disable
(ah
= 0x00) the A20 address line.
1393 cmp bus
, #2 ! PS/2 bus?
1396 movb al
, #0xD1 ! Tell keyboard that a command is coming
1399 movb al
, #0xDD ! 0xDD = A20 disable code if ah = 0x00
1400 orb al
, ah
! 0xDF = A20 enable code if ah
= 0x02
1403 movb al
, #0xFF ! Pulse output port
1405 call kb_wait
! Wait for the A20 line to settle down
1409 testb al
, #0x02 ! Keyboard input buffer full?
1410 jnz kb_wait
! If so
, wait
1413 gate_PS_A20
: ! The PS
/2 can twiddle A20 using port
A
1414 inb
0x92 ! Read port
A
1416 orb al
, ah
! Set A20 bit to the required state
1417 outb
0x92 ! Write port
A
1418 jmp
.+2 ! Small delay
1419 A20ok
: inb
0x92 ! Check port
A
1421 cmpb al
, ah
! A20 line settled down to the new state?
1422 jne A20ok
! If
not then wait
1425 ! void int15
(bios_env_t
*ep
)
1426 ! Do an
"INT 15" call
, primarily for APM
(Power Management
).
1429 push si
! Save callee-save register si
1432 mov ax
, (si
) ! ep-
>ax
1433 mov bx
, 2(si
) ! ep-
>bx
1434 mov cx
, 4(si
) ! ep-
>cx
1435 int
0x15 ! INT
0x15 BIOS call
1437 mov
(si
), ax
! ep-
>ax
1438 mov
2(si
), bx
! ep-
>bx
1439 mov
4(si
), cx
! ep-
>cx
1440 pop
6(si
) ! ep-
>flags
1444 ! void scan_keyboard
(void
)
1445 ! Read keyboard character. Needs to
be done in case one is waiting.
1446 .define _scan_keyboard
1458 .ascii "(null)\0" ! Just in case someone follows a null pointer
1460 c60
: .data2 60 ! Constants for MUL and DIV
1463 c19663
: .data2 19663
1465 ! Global descriptor tables.
1466 UNSET
= 0 ! Must
be computed
1468 ! For
"Extended Memory Block Move".
1472 .data2 0x0000, 0x0000
1473 .data1 0x00, 0x00, 0x00, 0x00
1475 ! Descriptor for this descriptor table
1477 .data1 UNSET, 0x00, 0x00, 0x00
1479 ! Source segment descriptor
1480 .data2 0xFFFF, UNSET
1481 .data1 UNSET, 0x92, 0x00, 0x00
1483 ! Destination segment descriptor
1484 .data2 0xFFFF, UNSET
1485 .data1 UNSET, 0x92, 0x00, 0x00
1487 ! BIOS segment descriptor
(scratch for int
0x15)
1489 .data1 UNSET, UNSET, UNSET, UNSET
1491 ! BIOS stack segment descriptor
(scratch for int
0x15)
1493 .data1 UNSET, UNSET, UNSET, UNSET
1495 ! Protected mode descriptor table.
1499 .data2 0x0000, 0x0000
1500 .data1 0x00, 0x00, 0x00, 0x00
1502 ! Descriptor for this descriptor table
1504 .data1 UNSET, 0x00, 0x00, 0x00
1506 ! Real mode interrupt descriptor table descriptor
1507 .data2 0x03FF, 0x0000
1508 .data1 0x00, 0x00, 0x00, 0x00
1510 ! Kernel data segment descriptor
(4 Gb flat
)
1511 .data2 0xFFFF, UNSET
1512 .data1 UNSET, 0x92, 0xCF, 0x00
1514 ! Physical memory descriptor
(4 Gb flat
)
1515 .data2 0xFFFF, 0x0000
1516 .data1 0x00, 0x92, 0xCF, 0x00
1518 ! Monitor data segment descriptor
(64 kb flat
)
1519 .data2 0xFFFF, UNSET
1520 .data1 UNSET, 0x92, 0x00, 0x00
1522 ! Kernel code segment descriptor
(4 Gb flat
)
1523 .data2 0xFFFF, UNSET
1524 .data1 UNSET, 0x9A, 0xCF, 0x00
1526 ! Monitor code segment descriptor
(64 kb flat
)
1527 .data2 0xFFFF, UNSET
1528 .data1 UNSET, 0x9A, 0x00, 0x00
1531 .comm old_vid_mode, 2 ! Video mode at startup
1532 .comm cur_vid_mode, 2 ! Current video mode
1533 .comm dev_state, 2 ! Device state: reset (-1), closed (0), open (1)
1534 .comm sectors, 2 ! # sectors of current device
1535 .comm secspcyl, 2 ! (Sectors * heads) of current device
1536 .comm msw, 4 ! Saved machine status word (cr0)
1537 .comm pdbr, 4 ! Saved page directory base register (cr3)
1538 .comm escape, 2 ! Escape typed?
1539 .comm bus, 2 ! Saved return value of _get_bus
1540 .comm unchar, 2 ! Char returned by ungetch(c)
1541 .comm line, 2 ! Serial line I/O port to copy console I/O to.