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
47 ! Set segment registers and stack pointer using the programs own header!
48 ! The header is either 32 bytes (short form) or 48 bytes (long form). The
49 ! bootblock will jump to address 0x10030 in both cases, calling one of the
50 ! two jmpf instructions below.
52 jmpf boot, LOADSEG+3 ! Set cs right (skipping long a.out header)
53 .space 11 ! jmpf + 11 = 16 bytes
54 jmpf boot, LOADSEG+2 ! Set cs right (skipping short a.out header)
57 mov ds, ax ! ds = header
60 testb al, #A_SEP ! Separate I&D?
63 xchg ax, a_text ! No text
64 add a_data, ax ! Treat all text as data
66 mov ax, a_total ! Total nontext memory usage
67 and ax, #0xFFFE ! Round down to even
68 mov a_total, ax ! total - text = data + bss + heap + stack
69 cli ! Ignore interrupts while stack in limbo
70 mov sp, ax ! Set sp at the top of all that
72 mov ax, a_text ! Determine offset of ds above cs
77 mov ds, ax ! ds = cs + text / 16
80 push es ! Save es, we need it for the partition table
82 cld ! C compiler wants UP
86 mov di, #_edata ! Start of bss is at end of data
87 mov cx, #_end ! End of bss (begin of heap)
88 sub cx, di ! Number of bss bytes
89 shr cx, #1 ! Number of words
93 ! Copy primary boot parameters to variables. (Can do this now that bss is
94 ! cleared and may be written into).
96 mov _device, dx ! Boot device (probably 0x00 or 0x80)
97 mov _rem_part+0, si ! Remote partition table offset
98 pop _rem_part+2 ! and segment (saved es)
100 ! Remember the current video mode for restoration on exit.
101 movb ah, #0x0F ! Get current video mode
103 andb al, #0x7F ! Mask off bit 7 (no blanking)
104 movb old_vid_mode, al
105 movb cur_vid_mode, al
107 ! Give C code access to the code segment, data segment and the size of this
121 mov ds, ax ! Back to the header once more
123 mov dx, a_total+2 ! dx:ax = data + bss + heap + stack
125 adc dx, a_text+2 ! dx:ax = text + data + bss + heap + stack
128 mov _runsize+2, dx ! 32 bit size of this process
130 ! Determine available memory as a list of (base,size) pairs as follows:
131 ! mem[0] = low memory, mem[1] = memory between 1M and 16M, mem[2] = memory
132 ! above 16M. Last two coalesced into mem[1] if adjacent.
133 mov di, #_mem ! di = memory list
134 int 0x12 ! Returns low memory size (in K) in ax
136 mov 4(di), ax ! mem[0].size = low memory size in bytes
139 cmp ax, #286 ! Only 286s and above have extended memory
141 cmp ax, #486 ! Assume 486s were the first to have >64M
142 jb small_ext ! (It helps to be paranoid when using the BIOS)
144 mov ax, #0xE801 ! Code for get memory size for >64M
145 int 0x15 ! ax = mem at 1M per 1K, bx = mem at 16M per 64K
148 movb ah, #0x88 ! Code for get extended memory size
149 clc ! Carry will stay clear if call exists
150 int 0x15 ! Returns size (in K) in ax for AT's
152 test ax
, ax
! An AT with no extended memory?
154 xor bx
, bx
! bx
= mem above
16M per
64K
= 0
156 mov cx
, ax
! cx
= copy of ext mem at
1M
157 mov
10(di
), #0x0010 ! mem[1].base = 0x00100000 (1M)
159 mov
12(di
), ax
! mem
[1].size = "ext mem at 1M" * 1024
162 jz no_ext
! No more ext mem above
16M?
163 cmp cx
, #15*1024 ! Chunks adjacent? (precisely 15M at 1M?)
165 mov
18(di
), #0x0100 ! mem[2].base = 0x01000000 (16M)
166 mov
22(di
), bx
! mem
[2].size = "ext mem at 16M" * 64K
169 add 14(di
), bx
! Add ext mem above
16M to mem below
16M
173 ! Time to switch to
a higher level language
(not much higher
)
176 ! void
..exit(int status)
177 ! Exit the monitor by rebooting the system.
178 .define _exit, __exit, ___exit ! Make various compilers happy
183 cmp 2(bx
), #0 ! Good exit status?
185 quit
: mov ax
, #any_key
188 xorb ah
, ah
! Read character from keyboard
190 reboot
: call dev_reset
192 int
0x19 ! Reboot the system
195 .ascii "\nHit any key to reboot\n\0"
198 ! u32_t mon2abs
(void
*ptr
)
199 ! Address in monitor data to absolute address.
204 mov dx
, ds
! Monitor data segment
207 ! u32_t vec2abs
(vector
*vec
)
208 ! 8086 interrupt vector to absolute address.
214 mov dx
, 2(bx
) ! dx
:ax vector
215 !jmp seg2abs
! Translate
217 seg2abs
: ! Translate dx
:ax to the
32 bit address dx-ax
222 shrb ch
, cl
! ch-dx
= dx
<< 4
224 adcb ch
, #0 ! ch-ax = ch-dx + ax
226 xorb dh
, dh
! dx-ax
= ch-ax
230 abs2seg
: ! Translate the
32 bit address dx-ax to dx
:ax
233 mov dx
, ax
! ch-dx
= dx-ax
234 and ax
, #0x000F ! Offset in ax
238 orb dh
, ch
! dx
= ch-dx
>> 4
242 ! void raw_copy
(u32_t dstaddr
, u32_t srcaddr
, u32_t count
)
243 ! Copy count bytes from srcaddr to dstaddr. Don
't do overlaps.
244 ! Also handles copying words to or from extended memory.
250 push di ! Save C variable registers
255 jcxz copydone ! Count is zero, end copy
258 bigcopy:mov cx, #0xFFF0 ! Don't copy more than about
64K at once
260 push cx
! Save copying count
263 cmp dx
, #0x0010 ! Copy to extended memory?
265 cmp 10(bp
), #0x0010 ! Copy from extended memory?
269 mov es
, dx
! es
:di
= dstaddr
274 mov ds
, dx
! ds
:si
= srcaddr
275 shr cx
, #1 ! Words to move
277 movs
! Do the word copy
278 adc cx
, cx
! One more byte?
280 movsb
! Do the byte copy
281 mov ax
, ss
! Restore ds
and es from the remaining ss
287 movb x_dst_desc+
4, dl
! Set base of destination segment
291 movb x_src_desc+
4, dl
! Set base of source segment
292 mov si
, #x_gdt ! es:si = global descriptor table
293 shr cx
, #1 ! Words to move
294 movb ah
, #0x87 ! Code for extended memory move
297 pop cx
! Restore count
299 adc
6(bp
), #0 ! srcaddr += copycount
301 adc
10(bp
), #0 ! dstaddr += copycount
303 sbb
14(bp
), #0 ! count -= copycount
304 jmp copy
! and repeat
307 pop si
! Restore C variable registers
311 ! u16_t get_word
(u32_t addr
);
312 ! void put_word
(u32_t addr
, u16_t word
);
313 ! Read
or write
a 16 bits word at an arbitrary location.
314 .define _get_word, _put_word
318 mov ax
, (bx
) ! Word to get from addr
322 push
6(bx
) ! Word to store at addr
324 pop
(bx
) ! Store the word
331 mov ds
, dx
! ds
:bx
= addr
338 ! void relocate
(void
);
339 ! After the program has copied itself to
a safer place
, it needs to change
340 ! the segment registers. Caddr has already been set to the new location.
343 pop bx
! Return address
347 mov cx
, dx
! cx
= new code segment
348 mov ax
, cs
! Old code segment
349 sub ax
, cx
! ax
= -(new
- old
) = -Moving offset
352 mov ds
, dx
! ds
+= (new
- old
)
358 mov _daddr+
2, dx
! New data address
359 push cx
! New text segment
360 push bx
! Return offset of this function
363 ! void
*brk
(void
*addr
)
364 ! void
*sbrk
(size_t incr
)
365 ! Cannot fail implementations of brk
(2) and sbrk
(3), so we can use
366 ! malloc
(3). They reboot on stack collision instead of returning -1.
369 break
: .data2 _end ! A fake heap pointer
371 .define _brk, __brk, _sbrk, __sbrk
373 __brk
: ! __brk is for the standard C compiler
375 jmp sbrk
! break
= 0; return sbrk
(addr
);
378 mov ax
, break
! ax
= current break
379 sbrk
: push ax
! save it as future return value
380 mov bx
, sp
! Stack is now
: (retval
, retaddr
, incr
, ...)
381 add ax
, 4(bx
) ! ax
= break
+ increment
382 mov break
, ax
! Set new break
383 lea dx
, -1024(bx
) ! sp minus
a bit of breathing space
384 cmp dx
, ax
! Compare with the new break
385 jb heaperr
! Suffocating noises
386 lea dx
, -4096(bx
) ! A warning when heap+stack goes
< 4K
388 jae plenty
! No reason to complain
391 call _printf
! Warn about memory running low
393 movb memwarn
, #0 ! No more warnings
394 plenty
: pop ax
! Return old break
(0 for brk
)
396 heaperr
:mov ax
, #chmem
403 nomem
: .ascii "\nOut of%s\0"
404 memwarn
:.ascii "\nLow on"
405 chmem
: .ascii " memory, use chmem to increase the heap\n\0"
408 ! int dev_open
(void
);
409 ! Given the device
"_device" figure out if it exists
and what its number
410 ! of heads
and sectors may be. Return the BIOS error code on error
,
414 call dev_reset
! Optionally reset the disks
415 movb dev_state
, #0 ! State is "closed"
417 push di
! Save registers used by BIOS calls
418 movb dl
, _device
! The default device
419 cmpb dl
, #0x80 ! Floppy < 0x80, winchester >= 0x80
422 mov di
, #3 ! Three tries to init drive by reading sector 0
425 mov bx
, #BUFFER ! es:bx = scratch buffer
426 mov ax
, #0x0201 ! Read sector, #sectors = 1
427 mov cx
, #0x0001 ! Track 0, first sector
428 xorb dh
, dh
! Drive dl
, head
0
430 jnc finit0ok
! Sector
0 read ok?
431 cmpb ah
, #0x80 ! Disk timed out? (Floppy drive empty)
435 xorb ah
, ah
! Reset drive
438 jmp finit0
! Retry once more
, it may need to spin up
440 mov di
, #seclist ! List of per floppy type sectors/track
441 flast
: movb cl
, (di
) ! Sectors per track to test
442 cmpb cl
, #9 ! No need to do the last 720K/360K test
446 mov bx
, #BUFFER ! es:bx = scratch buffer
447 mov ax
, #0x0201 ! Read sector, #sectors = 1
448 xorb ch
, ch
! Track
0, last sector
449 xorb dh
, dh
! Drive dl
, head
0
451 jnc ftestok
! Sector cl read ok?
452 xorb ah
, ah
! Reset drive
455 inc di
! Try next sec
/track number
458 movb dh
, #2 ! Floppies have two sides
461 movb ah
, #0x08 ! Code for drive parameters
462 int
0x13 ! dl still contains drive
463 jc geoerr
! No such drive?
464 andb cl
, #0x3F ! cl = max sector number (1-origin)
465 incb dh
! dh
= 1 + max head number
(0-origin
)
467 movb sectors
, cl
! Sectors per track
468 movb al
, cl
! al
= sectors per track
469 mulb dh
! ax
= heads
* sectors
470 mov secspcyl
, ax
! Sectors per cylinder
= heads
* sectors
471 movb dev_state
, #1 ! Device state is "open"
472 xor ax
, ax
! Code for success
475 pop es
! Restore di
and es registers
478 xorb ah
, ah
! ax
= BIOS error code
482 .data1 18, 15, 9 ! 1.44M, 1.2M, and 360K/720K floppy sec/track
485 ! int dev_close
(void
);
486 ! Close the current device. Under the BIOS this does nothing much.
490 movb dev_state
, al
! State is
"closed"
493 ! Reset the disks if needed. Minix may have messed things up.
495 cmpb dev_state
, #0 ! Need reset if dev_state < 0
497 xorb ah
, ah
! Reset
(ah
= 0)
498 movb dl
, #0x80 ! All disks
500 movb dev_state
, #0 ! State is "closed"
503 ! int dev_boundary
(u32_t sector
);
504 ! True if
a sector is on
a boundary
, i.e. sector
% sectors
== 0.
505 .define _dev_boundary
509 mov ax
, 4(bx
) ! divide high half of sector number
511 mov ax
, 2(bx
) ! divide low half of sector number
512 div sectors
! dx
= sector
% sectors
513 sub dx
, #1 ! CF = dx == 0
514 sbb ax
, ax
! ax
= -CF
515 neg ax
! ax
= (sector
% sectors
) == 0
518 ! int readsectors
(u32_t bufaddr
, u32_t sector
, u8_t count
)
519 ! int writesectors
(u32_t bufaddr
, u32_t sector
, u8_t count
)
520 ! Read
/write several sectors from
/to disk
or floppy. The buffer must
521 ! be between
64K boundaries
! Count must fit in
a byte. The external
522 ! variables _device
, sectors
and secspcyl describe the disk
and its
523 ! geometry. Returns
0 for success
, otherwise the BIOS error code.
525 .define _readsectors, _writesectors
529 movb
13(bp
), #0x03 ! Code for a disk write
534 movb
13(bp
), #0x02 ! Code for a disk read
538 cmpb dev_state
, #0 ! Device state?
540 call _dev_open
! Initialize
547 mov es
, dx
! es
:bx
= bufaddr
548 mov di
, #3 ! Execute 3 resets on floppy error
551 mov di
, #1 ! But only 1 reset on hard disk error
552 nohd
: cmpb
12(bp
), #0 ! count equals zero?
555 mov dx
, 10(bp
) ! dx
:ax
= abs sector. Divide it by sectors
/cyl
556 cmp dx
, #[1024*255*63-255]>>16 ! Near 8G limit?
558 div secspcyl
! ax
= cylinder
, dx
= sector within cylinder
559 xchg ax
, dx
! ax
= sector within cylinder
, dx
= cylinder
560 movb ch
, dl
! ch
= low
8 bits of cylinder
561 divb sectors
! al
= head
, ah
= sector
(0-origin
)
562 xorb dl
, dl
! About to shift bits
8-9 of cylinder into dl
564 shr dx
, #1 ! dl[6..7] = high cylinder
565 orb dl
, ah
! dl
[0..5] = sector (0-origin)
566 movb cl
, dl
! cl
[0..5] = sector, cl[6..7] = high cyl
567 incb cl
! cl
[0..5] = sector (1-origin)
568 movb dh
, al
! dh
= head
569 movb dl
, _device
! dl
= device to use
570 movb al
, sectors
! Sectors per track
- Sector number
(0-origin
)
571 subb al
, ah
! = Sectors left on this track
572 cmpb al
, 12(bp
) ! Compare with
# sectors to transfer
573 jbe doit
! Can
't go past the end of a cylinder?
574 movb al, 12(bp) ! 12(bp) < sectors left on this track
575 doit: movb ah, 13(bp) ! Code for disk read (0x02) or write (0x03)
576 push ax ! Save al = sectors to read
577 int 0x13 ! call the BIOS to do the transfer
578 pop cx ! Restore al in cl
581 mov si, #ext_rw ! si = extended read/write parameter packet
583 movb 2(si), cl ! Fill in # blocks to transfer
584 mov 4(si), bx ! Buffer address = es:bx
586 mov 8(si), ax ! Starting block number = dx:ax
588 movb dl, _device ! dl = device to use
589 mov ax, #0x4000 ! This, or-ed with 0x02 or 0x03 becomes
590 orb ah, 13(bp) ! extended read (0x4200) or write (0x4300)
595 movb al, cl ! Restore al = sectors read
596 addb bh, al ! bx += 2 * al * 256 (add bytes transferred)
597 addb bh, al ! es:bx = where next sector is located
598 add 8(bp), ax ! Update address by sectors transferred
599 adc 10(bp), #0 ! Don't forget high word
600 subb
12(bp
), al
! Decrement sector count by sectors transferred
601 jnz more
! Not all sectors have been transferred
602 done
: xorb ah
, ah
! No error here
!
604 ioerr
: cmpb ah
, #0x80 ! Disk timed out? (Floppy drive empty)
606 cmpb ah
, #0x03 ! Disk write protected?
608 dec di
! Do we allow another reset?
609 jl finish
! No
, report the error
610 xorb ah
, ah
! Code for
a reset
(0)
612 jnc more
! Succesful reset
, try request again
614 xorb ah
, ah
! ax
= error number
622 ! Extended read
/write commands require
a parameter packet.
624 .data1 0x10 ! Length of extended r/w packet
626 .data2 0 ! Blocks to transfer (to be filled in)
627 .data2 0 ! Buffer address offset (tbfi)
628 .data2 0 ! Buffer address segment (tbfi)
629 .data4 0 ! Starting block number low 32 bits (tbfi)
630 .data4 0 ! Starting block number high 32 bits
634 ! Read
a character from the keyboard
, and check for an expired timer.
635 ! A carriage return is changed into
a linefeed for UNIX compatibility.
639 xchg ax
, unchar
! Ungotten character?
643 hlt
! Play dead until interrupted
(see pause
())
644 movb ah
, #0x01 ! Keyboard status
646 jz
0f
! Nothing typed
647 xorb ah
, ah
! Read character from keyboard
650 0: mov dx
, line
! Serial line?
653 add dx
, #5 ! Line Status Register
655 testb al
, #0x01 ! Data Ready?
658 !add dx
, 0 ! Receive Buffer Register
659 inb dx
! Get character
661 0: call _expired
! Timer expired?
664 mov ax
, #ESC ! Return ESC
667 cmpb al
, #0x0D ! Carriage return?
669 movb al
, #0x0A ! Change to linefeed
670 nocr
: cmpb al
, #ESC ! Escape typed?
672 inc escape
! Set flag
673 noesc
: xorb ah
, ah
! ax
= al
677 ! Return
a character to undo
a getch
().
686 ! True if ESC has been typed.
689 movb ah
, #0x01 ! Keyboard status
691 jz escflg
! Keypress?
692 cmpb al
, #ESC ! Escape typed?
694 xorb ah
, ah
! Discard the escape
696 inc escape
! Set flag
698 xchg ax
, escape
! Escape typed flag
702 ! Write
a character in teletype mode. The putk synonym is
703 ! for the kernel printf function that uses it.
704 ! Newlines are automatically preceded by
a carriage return.
706 .define _putch, _putk
709 movb al
, 2(bx
) ! al
= character to
be printed
710 testb al
, al
! Kernel printf adds
a null char to flush queue
712 cmpb al
, #0x0A ! al = newline?
715 call putc
! putc
('\r')
716 movb al
, #0x0A ! Restore the '\n' and print it
717 putc
: movb ah
, #0x0E ! Print character in teletype mode
718 mov bx
, #0x0001 ! Page 0, foreground color
720 mov bx
, line
! Serial line?
723 push ax
! Save character to print
724 call _get_tick
! Current clock tick counter
726 add cx
, #2 ! Don't want to see it count twice
727 1: lea dx
, 5(bx
) ! Line Status Register
729 testb al
, #0x20 ! Transmitter Holding Register Empty?
732 cmp ax
, cx
! Clock ticked more than once?
734 0: pop ax
! Restore character to print
735 mov dx
, bx
! Transmit Holding Register
736 outb dx
! Send character down the serial line
740 ! Wait for an interrupt using the HLT instruction. This either saves
741 ! power
, or tells an x86 emulator that nothing is happening right now.
747 ! void set_mode
(unsigned mode
);
748 ! void clear_screen
(void
);
749 ! Set video mode
/ clear the screen.
750 .define _set_mode, _clear_screen
753 mov ax
, 2(bx
) ! Video mode
755 je modeok
! Mode already as requested?
759 mov es
, ax
! es
= Vector segment
761 movb ch
, ah
! Copy of the special flags
762 andb ah
, #0x0F ! Test bits 8-11, clear special flags
763 jnz xvesa
! VESA extended mode?
764 int
0x10 ! Reset video
(ah
= 0)
766 xvesa
: mov bx
, ax
! bx
= extended mode
767 mov ax
, #0x4F02 ! Reset video
769 md_480
: ! Basic video mode is set
, now build on it
770 testb ch
, #0x20 ! 480 scan lines requested?
772 mov dx
, #0x3CC ! Get CRTC port
775 testb al
, #1 ! Mono or color?
778 0: mov ax
, #0x110C ! Vertical sync end (also unlocks CR0-7)
780 mov ax
, #0x060B ! Vertical total
782 mov ax
, #0x073E ! (Vertical) overflow
784 mov ax
, #0x10EA ! Vertical sync start
786 mov ax
, #0x12DF ! Vertical display end
788 mov ax
, #0x15E7 ! Vertical blank start
790 mov ax
, #0x1604 ! Vertical blank end
793 movb dl
, #0xCC ! Misc output register (read)
795 movb dl
, #0xC2 ! (write)
796 andb al
, #0x0D ! Preserve clock select bits and color bit
797 orb al
, #0xE2 ! Set correct sync polarity
799 pop dx
! Index register still in dx
801 testb ch
, #0x40 ! 9x14 point font requested?
803 mov ax
, #0x1111 ! Load ROM 9 by 14 font
804 xorb
bl, bl ! Load block
0
806 testb ch
, #0x20 ! 480 scan lines?
808 mov ax
, #0x12DB ! VGA vertical display end
810 eseg movb
0x0484, #33 ! Tell BIOS the last line number
812 testb ch
, #0x80 ! 8x8 point font requested?
814 mov ax
, #0x1112 ! Load ROM 8 by 8 font
815 xorb
bl, bl ! Load block
0
817 testb ch
, #0x20 ! 480 scan lines?
819 mov ax
, #0x12DF ! VGA vertical display end
821 eseg movb
0x0484, #59 ! Tell BIOS the last line number
823 xor dx
, dx
! dl
= column
= 0, dh
= row
= 0
825 movb ah
, #0x02 ! Set cursor position
831 ! Out to the usual
[index
, data
] port pair that are common for VGA devices
832 ! dx
= port
, ah
= index
, al
= data.
844 restore_video
: ! To restore the video mode on exit
851 ! void serial_init
(int line
)
852 ! Initialize copying console I
/O to
a serial line.
856 mov dx
, 2(bx
) ! Line number
858 test dx
, dx
! Off if line number
< 0
862 mov ds
, ax
! Vector
and BIOS data segment
863 mov bx
, dx
! Line number
864 shl bx
, #1 ! Word offset
865 mov bx
, 0x0400(bx
) ! I
/O port for the given line
867 mov line
, bx
! Remember I
/O port
870 test bx
, bx
! I
/O port must
be nonzero
872 mov ax
, #0x00E3 ! 9600 N-8-1
873 int
0x14 ! Initialize serial line dx
874 lea dx
, 4(bx
) ! Modem Control Register
875 movb al
, #0x0B ! DTR, RTS, OUT2
879 ! u32_t get_tick
(void
);
880 ! Return the current value of the clock tick counter. This counter
881 ! increments
18.2 times per second. Poll it to do delays. Does
not
882 ! work on the original PC
, but works on the PC
/XT.
886 xorb ah
, ah
! Code for get tick count
889 mov dx
, cx
! dx
:ax
= cx
:dx
= tick count
894 ! Functions used to obtain info about the hardware. Boot uses this information
895 ! itself
, but will also pass them on to
a pure
386 kernel
, because one can
't
896 ! make BIOS calls from protected mode. The video type could probably be
897 ! determined by the kernel too by looking at the hardware, but there is a small
898 ! chance on errors that the monitor allows you to correct by setting variables.
900 .define _get_bus ! returns type of system bus
901 .define _get_video ! returns type of display
903 ! u16_t get_bus(void)
904 ! Return type of system bus, in order: XT, AT, MCA.
907 xor dx, dx ! Assume XT
908 cmp ax, #286 ! An AT has at least a 286
911 movb ah, #0xC0 ! Code for get configuration
913 jc got_bus ! Carry clear and ah = 00 if supported
917 movb al, 5(bx) ! Load feature byte #1
919 testb al, #0x02 ! Test bit 1 - "bus is Micro Channel"
922 testb al, #0x40 ! Test bit 6 - "2nd 8259 installed"
928 mov ax, dx ! Return bus code
929 mov bus, ax ! Keep bus code, A20 handler likes to know
932 ! u16_t get_video(void)
933 ! Return type of display, in order: MDA, CGA, mono EGA, color EGA,
934 ! mono VGA, color VGA.
936 mov ax, #0x1A00 ! Function 1A returns display code
937 int 0x10 ! al = 1A if supported
939 jnz no_dc ! No display code function supported
942 cmpb bl, #5 ! Is it a monochrome EGA?
945 cmpb bl, #4 ! Is it a color EGA?
948 cmpb bl, #7 ! Is it a monochrome VGA?
951 cmpb bl, #8 ! Is it a color VGA?
954 no_dc: movb ah, #0x12 ! Get information about the EGA
957 cmpb bl, #0x10 ! Did it come back as 0x10? (No EGA)
961 cmpb bh, #1 ! Is it monochrome?
966 no_ega: int 0x11 ! Get bit pattern for equipment
967 and ax, #0x30 ! Isolate color/mono field
969 jz got_video ! Is it an MDA?
970 mov ax, #1 ! No it's CGA
976 ! Functions to leave the boot monitor.
977 .define _bootstrap ! Call another bootstrap
978 .define _minix ! Call Minix
980 ! void _bootstrap
(int device
, struct part_entry
*entry
)
981 ! Call another bootstrap routine to boot MS-DOS for instance.
(No real
982 ! need for that anymore
, now that you can format floppies under Minix
).
983 ! The bootstrap must have been loaded at BOOTSEG from
"device".
987 movb dl
, 2(bx
) ! Device to boot from
988 mov si
, 4(bx
) ! ds
:si
= partition table entry
990 mov es
, ax
! Vector segment
991 mov di
, #BUFFER ! es:di = buffer in low core
992 mov cx
, #PENTRYSIZE ! cx = size of partition table entry
993 rep movsb
! Copy the entry to low core
994 mov si
, #BUFFER ! es:si = partition table entry
995 mov ds
, ax
! Some bootstraps need zero segment registers
998 mov sp
, #BOOTOFF ! This should do it
1000 jmpf BOOTOFF
, 0 ! Back to where the BIOS loads the boot code
1002 ! void minix
(u32_t koff
, u32_t kcs
, u32_t kds
,
1003 ! char
*bootparams
, size_t paramsize
, u32_t aout
);
1007 mov bp
, sp
! Pointer to arguments
1009 mov dx
, #0x03F2 ! Floppy motor drive control bits
1010 movb al
, #0x0C ! Bits 4-7 for floppy 0-3 are off
1011 outb dx
! Kill the motors
1013 xor ax
, ax
! Vector
& BIOS data segments
1015 andb
0x043F, #0xF0 ! Clear diskette motor status bits of BIOS
1017 cli ! No more interruptions
1019 test _k_flags
, #K_I386 ! Switch to 386 mode?
1022 ! Call Minix in real mode.
1024 test _k_flags
, #K_MEML ! New memory arrangements?
1026 push
22(bp
) ! Address of
a.out headers
1029 push
18(bp
) ! # bytes of boot parameters
1030 push
16(bp
) ! Address of boot parameters
1032 test _k_flags
, #K_RET ! Can the kernel return?
1034 xor dx
, dx
! If little ext mem then monitor
not preserved
1036 cmp _mon_return
, ax
! Minix can return to the monitor?
1038 mov dx
, cs
! Monitor far return address
1040 0: push dx
! Push monitor far return address
or zero
1047 push dx
! Kernel code segment
1048 push
4(bp
) ! Kernel code offset
1052 mov ds
, dx
! Kernel data segment
1053 mov es
, dx
! Set es to kernel data too
1054 retf
! Make
a far call to the kernel
1056 ! Call Minix in
386 mode.
1058 cseg mov cs_real-
2, cs
! Patch CS
and DS into the instructions that
1059 cseg mov ds_real-
2, ds
! reload them when switching back to real mode
1060 .data1 0x0F,0x20,0xC0 ! mov eax, cr0
1061 orb al
, #0x01 ! Set PE (protection enable) bit
1063 mov msw
, ax
! Save as protected mode machine status word
1065 mov dx
, ds
! Monitor ds
1066 mov ax
, #p_gdt ! dx:ax = Global descriptor table
1068 mov p_gdt_desc+
2, ax
1069 movb p_gdt_desc+
4, dl
! Set base of global descriptor table
1072 mov dx
, 14(bp
) ! Kernel ds
(absolute address
)
1074 movb p_ds_desc+
4, dl
! Set base of kernel data segment
1076 mov dx
, ss
! Monitor ss
1077 xor ax
, ax
! dx
:ax
= Monitor stack segment
1078 call seg2abs
! Minix starts with the stack of the monitor
1080 movb p_ss_desc+
4, dl
1083 mov dx
, 10(bp
) ! Kernel cs
(absolute address
)
1085 movb p_cs_desc+
4, dl
1087 mov dx
, cs
! Monitor cs
1088 xor ax
, ax
! dx
:ax
= Monitor code segment
1090 mov p_mcs_desc+
2, ax
1091 movb p_mcs_desc+
4, dl
1094 test _k_flags
, #K_INT86 ! Generic INT86 support?
1096 push
#int86 ! Far address to INT86 support
1098 0: push
#bios13 ! Far address to BIOS int 13 support
1100 test _k_flags
, #K_MEML ! New memory arrangements?
1103 push
20(bp
) ! Address of
a.out headers
1106 push
18(bp
) ! 32 bit size of parameters on stack
1108 push
16(bp
) ! 32 bit address of parameters
(ss relative
)
1110 test _k_flags
, #K_RET ! Can the kernel return?
1113 push
#ret386 ! Monitor far return address
1119 push
4(bp
) ! 32 bit far address to kernel entry point
1121 call real2prot
! Switch to protected mode
1122 mov ax
, #DS_SELECTOR ! Kernel data
1124 mov ax
, #ES_SELECTOR ! Flat 4 Gb
1126 .data1 o32 ! Make a far call to the kernel
1129 ! Minix-
86 returns here on
a halt
or reboot.
1131 mov _reboot_code+
0, ax
1132 mov _reboot_code+
2, dx
! Return value
(obsolete method
)
1135 ! Minix-
386 returns here on
a halt
or reboot.
1138 mov _reboot_code
, ax
! Return value
(obsolete method
)
1139 call prot2real
! Switch to real mode
1142 mov sp
, bp
! Pop parameters
1143 sti
! Can take interrupts again
1145 call _get_video
! MDA
, CGA
, EGA
, ...
1146 movb dh
, #24 ! dh = row 24
1147 cmp ax
, #2 ! At least EGA?
1148 jb is25
! Otherwise
25 rows
1150 xor ax
, ax
! Vector
& BIOS data segments
1152 movb dh
, 0x0484 ! Number of rows on display minus one
1155 xorb dl
, dl
! dl
= column
0
1156 xorb bh
, bh
! Page
0
1157 movb ah
, #0x02 ! Set cursor position
1160 movb dev_state
, #-1 ! Minix may have upset the disks, must reset.
1161 call serial_init
! Likewise with our serial console
1169 movb ah
, #0x02 ! Get real-time clock time (from CMOS clock)
1171 jc tryclk
! Carry set
, not running
or being updated
1172 movb al
, ch
! ch
= hour in BCD
1173 call bcd
! al
= (al
>> 4) * 10 + (al
& 0x0F)
1174 mulb c60
! 60 minutes in an hour
1175 mov bx
, ax
! bx
= hour
* 60
1176 movb al
, cl
! cl
= minutes in BCD
1178 add bx
, ax
! bx
= hour
* 60 + minutes
1179 movb al
, dh
! dh
= seconds in BCD
1181 xchg ax
, bx
! ax
= hour
* 60 + minutes
, bx
= seconds
1182 mul c60
! dx-ax
= (hour
* 60 + minutes
) * 60
1184 adc dx
, #0 ! dx-bx = seconds since midnight
1189 add dx
, bx
! dx-ax
= dx-bx
* (0x1800B0 / (2*2*2*2*5))
1190 mov cx
, ax
! (0x1800B0 = ticks per day of BIOS clock
)
1195 div c1080
! cx-ax
= dx-ax
/ (24*60*60 / (2*2*2*2*5))
1196 mov dx
, ax
! cx-dx
= ticks since midnight
1197 movb ah
, #0x01 ! Set system time
1202 ret
! Return to monitor as if nothing much happened
1204 ! Transform BCD number in al to
a regular value in ax.
1208 .data1 0xD5,10 ! aad ! ax = (al >> 4) * 10 + (al & 0x0F)
1209 ret
! (BUG
: assembler messes up aad
& aam
!)
1211 ! Support function for Minix-
386 to make
a BIOS int
13 call
(disk I
/O
).
1215 sti
! Enable interrupts
1217 mov ax
, 8(bp
) ! Load parameters
1222 int
0x13 ! Make the BIOS call
1223 mov
8(bp
), ax
! Save results
1229 cli ! Disable interrupts
1231 mov ax
, #DS_SELECTOR ! Kernel data
1234 retf
! Return to the kernel
1236 ! Support function for Minix-
386 to make an
8086 interrupt call.
1243 mov es
, ax
! Vector
& BIOS data segments
1245 eseg mov
0x046C, ax
! Clear BIOS clock tick counter
1247 sti
! Enable interrupts
1249 movb al
, #0xCD ! INT instruction
1250 movb ah
, 8(bp
) ! Interrupt number?
1252 jnz
0f
! Nonzero if INT
, otherwise far call
1254 push
#intret+2 ! Far return address
1256 push
12(bp
) ! Far driver address
1257 mov ax
, #0x90CB ! RETF; NOP
1259 cseg
cmp ax
, intret
! Needs to
be changed?
1260 je
0f
! If
not then avoid
a huge I-cache stall
1261 cseg mov intret
, ax
! Patch
'INT n' or 'RETF; NOP' into code
1262 jmp
.+2 ! Clear instruction queue
1264 mov ds
, 16(bp
) ! Load parameters
1281 intret
: int
0xFF ! Do the interrupt
or far call
1283 .data1 o32 ! Save results
1289 pop
8+8(bp
) ! eflags
1307 cli ! Disable interrupts
1310 mov ds
, ax
! Vector
& BIOS data segments
1312 mov cx
, 0x046C ! Collect lost clock ticks in ecx
1315 mov ds
, ax
! Restore monitor ds
1317 mov ax
, #DS_SELECTOR ! Kernel data
1320 retf
! Return to the kernel
1322 ! Switch from real to protected mode.
1324 movb ah
, #0x02 ! Code for A20 enable
1327 lgdt p_gdt_desc
! Global descriptor table
1329 mov ax
, pdbr
! Load page directory base register
1330 .data1 0x0F,0x22,0xD8 ! mov cr3, eax
1331 .data1 0x0F,0x20,0xC0 ! mov eax, cr0
1333 xchg ax
, msw
! Exchange real mode msw for protected mode msw
1334 .data1 0x0F,0x22,0xC0 ! mov cr0, eax
1335 jmpf cs_prot
, MCS_SELECTOR
! Set code segment selector
1337 mov ax
, #SS_SELECTOR ! Set data selectors
1343 ! Switch from protected to real mode.
1345 lidt p_idt_desc
! Real mode interrupt vectors
1346 .data1 0x0F,0x20,0xD8 ! mov eax, cr3
1348 mov pdbr
, ax
! Save page directory base register
1349 .data1 0x0F,0x20,0xC0 ! mov eax, cr0
1351 xchg ax
, msw
! Exchange protected mode msw for real mode msw
1352 .data1 0x0F,0x22,0xC0 ! mov cr0, eax
1353 jmpf cs_real
, 0xDEAD ! Reload cs register
1357 mov ds
, ax
! Reload data segment registers
1361 xorb ah
, ah
! Code for A20 disable
1364 ! Enable
(ah
= 0x02) or disable
(ah
= 0x00) the A20 address line.
1366 cmp bus
, #2 ! PS/2 bus?
1369 movb al
, #0xD1 ! Tell keyboard that a command is coming
1372 movb al
, #0xDD ! 0xDD = A20 disable code if ah = 0x00
1373 orb al
, ah
! 0xDF = A20 enable code if ah
= 0x02
1376 movb al
, #0xFF ! Pulse output port
1378 call kb_wait
! Wait for the A20 line to settle down
1382 testb al
, #0x02 ! Keyboard input buffer full?
1383 jnz kb_wait
! If so
, wait
1386 gate_PS_A20
: ! The PS
/2 can twiddle A20 using port
A
1387 inb
0x92 ! Read port
A
1389 orb al
, ah
! Set A20 bit to the required state
1390 outb
0x92 ! Write port
A
1391 jmp
.+2 ! Small delay
1392 A20ok
: inb
0x92 ! Check port
A
1394 cmpb al
, ah
! A20 line settled down to the new state?
1395 jne A20ok
! If
not then wait
1398 ! void int15
(bios_env_t
*ep
)
1399 ! Do an
"INT 15" call
, primarily for APM
(Power Management
).
1402 push si
! Save callee-save register si
1405 mov ax
, (si
) ! ep-
>ax
1406 mov bx
, 2(si
) ! ep-
>bx
1407 mov cx
, 4(si
) ! ep-
>cx
1408 int
0x15 ! INT
0x15 BIOS call
1410 mov
(si
), ax
! ep-
>ax
1411 mov
2(si
), bx
! ep-
>bx
1412 mov
4(si
), cx
! ep-
>cx
1413 pop
6(si
) ! ep-
>flags
1417 ! void scan_keyboard
(void
)
1418 ! Read keyboard character. Needs to
be done in case one is waiting.
1419 .define _scan_keyboard
1431 .ascii "(null)\0" ! Just in case someone follows a null pointer
1433 c60
: .data2 60 ! Constants for MUL and DIV
1436 c19663
: .data2 19663
1438 ! Global descriptor tables.
1439 UNSET
= 0 ! Must
be computed
1441 ! For
"Extended Memory Block Move".
1445 .data2 0x0000, 0x0000
1446 .data1 0x00, 0x00, 0x00, 0x00
1448 ! Descriptor for this descriptor table
1450 .data1 UNSET, 0x00, 0x00, 0x00
1452 ! Source segment descriptor
1453 .data2 0xFFFF, UNSET
1454 .data1 UNSET, 0x92, 0x00, 0x00
1456 ! Destination segment descriptor
1457 .data2 0xFFFF, UNSET
1458 .data1 UNSET, 0x92, 0x00, 0x00
1460 ! BIOS segment descriptor
(scratch for int
0x15)
1462 .data1 UNSET, UNSET, UNSET, UNSET
1464 ! BIOS stack segment descriptor
(scratch for int
0x15)
1466 .data1 UNSET, UNSET, UNSET, UNSET
1468 ! Protected mode descriptor table.
1472 .data2 0x0000, 0x0000
1473 .data1 0x00, 0x00, 0x00, 0x00
1475 ! Descriptor for this descriptor table
1477 .data1 UNSET, 0x00, 0x00, 0x00
1479 ! Real mode interrupt descriptor table descriptor
1480 .data2 0x03FF, 0x0000
1481 .data1 0x00, 0x00, 0x00, 0x00
1483 ! Kernel data segment descriptor
(4 Gb flat
)
1484 .data2 0xFFFF, UNSET
1485 .data1 UNSET, 0x92, 0xCF, 0x00
1487 ! Physical memory descriptor
(4 Gb flat
)
1488 .data2 0xFFFF, 0x0000
1489 .data1 0x00, 0x92, 0xCF, 0x00
1491 ! Monitor data segment descriptor
(64 kb flat
)
1492 .data2 0xFFFF, UNSET
1493 .data1 UNSET, 0x92, 0x00, 0x00
1495 ! Kernel code segment descriptor
(4 Gb flat
)
1496 .data2 0xFFFF, UNSET
1497 .data1 UNSET, 0x9A, 0xCF, 0x00
1499 ! Monitor code segment descriptor
(64 kb flat
)
1500 .data2 0xFFFF, UNSET
1501 .data1 UNSET, 0x9A, 0x00, 0x00
1504 .comm old_vid_mode, 2 ! Video mode at startup
1505 .comm cur_vid_mode, 2 ! Current video mode
1506 .comm dev_state, 2 ! Device state: reset (-1), closed (0), open (1)
1507 .comm sectors, 2 ! # sectors of current device
1508 .comm secspcyl, 2 ! (Sectors * heads) of current device
1509 .comm msw, 4 ! Saved machine status word (cr0)
1510 .comm pdbr, 4 ! Saved page directory base register (cr3)
1511 .comm escape, 2 ! Escape typed?
1512 .comm bus, 2 ! Saved return value of _get_bus
1513 .comm unchar, 2 ! Char returned by ungetch(c)
1514 .comm line, 2 ! Serial line I/O port to copy console I/O to.