2 * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * Copyright (c) 1998 Robert Nordier
35 * All rights reserved.
37 * Redistribution and use in source and binary forms are freely
38 * permitted provided that the above copyright notice and this
39 * paragraph and the following disclaimer are duplicated in all
42 * This software is provided "AS IS" and without any express or
43 * implied warranties, including, without limitation, the implied
44 * warranties of merchantability and fitness for a particular
47 * $FreeBSD: src/sys/boot/i386/btx/btx/btx.s,v 1.32 2002/10/08 18:19:02 jhb Exp $
48 * $DragonFly: src/sys/boot/pc32/btx/btx/btx.S,v 1.8 2006/01/18 09:59:34 swildner Exp $
51 #include "../../bootasm.h"
56 .set PAG_SIZ,0x1000 # Page size
57 .set PAG_CNT,0x1000 # Pages to map
61 .set PSL_RESERVED_DEFAULT,0x00000002
62 .set PSL_T,0x00000100 # Trap flag
63 .set PSL_I,0x00000200 # Interrupt enable flag
64 .set PSL_D,0x00000400 # String instruction direction
65 .set PSL_NT,0x00004000 # Nested task flag
66 .set PSL_VM,0x00020000 # Virtual 8086 mode flag
67 .set PSL_AC,0x00040000 # Alignment check flag
72 .set SEL_SCODE,0x8 # Supervisor code
73 .set SEL_SDATA,0x10 # Supervisor data
74 .set SEL_RCODE,0x18 # Real mode code
75 .set SEL_RDATA,0x20 # Real mode data
76 .set SEL_UCODE,0x28|3 # User code
77 .set SEL_UDATA,0x30|3 # User data
78 .set SEL_TSS,0x38 # TSS
81 * Task state segment fields.
83 .set TSS_ESP0,0x4 # PL 0 ESP
84 .set TSS_SS0,0x8 # PL 0 SS
85 .set TSS_MAP,0x66 # I/O bit map base
90 .set SYS_EXIT,0x0 # Exit
91 .set SYS_EXEC,0x1 # Exec
94 * Fields in V86 interface structure.
96 .set V86_CTL,0x0 # Control flags
97 .set V86_ADDR,0x4 # Int number/address
98 .set V86_ES,0x8 # V86 ES
99 .set V86_DS,0xc # V86 DS
100 .set V86_FS,0x10 # V86 FS
101 .set V86_GS,0x14 # V86 GS
105 .set V86F_ADDR,0x10000 # Segment:offset address
106 .set V86F_CALLF,0x20000 # Emulate far call
107 .set V86F_FLAGS,0x40000 # Return flags
110 * Dump format control bytes.
112 .set DMP_X16,0x1 # Word
113 .set DMP_X32,0x2 # Long
114 .set DMP_MEM,0x4 # Memory
115 .set DMP_EOL,0x8 # End of line
118 * Screen defaults and assumptions.
120 .set SCR_MAT,0x7 # Mode/attribute
121 .set SCR_COL,0x50 # Columns per row
122 .set SCR_ROW,0x19 # Rows per screen
125 * Derivations, for brevity.
127 .set _ESP0H,MEM_BTX_ESP0>>0x8 # Byte 1 of ESP0
128 .set _TSSIO,MEM_BTX_MAP-MEM_BTX_TSS # TSS I/O base
129 .set _TSSLM,MEM_BTX_TSS_END-MEM_BTX_TSS # TSS limit
130 .set _IDTLM,MEM_BTX_TSS-MEM_BTX_IDT-1 # IDT limit
139 start: # Start of code
144 btx_hdr: .byte 0xeb # Machine ID
145 .byte 0xe # Header size
147 .byte 0x1 # Major version
148 .byte 0x2 # Minor version
149 .byte BTX_FLAGS # Flags
150 .word PAG_CNT-MEM_BTX_ORG>>0xc # Paging control
151 .word break-start # Text size
152 .long 0x0 # Entry address
155 * Initialization routine.
157 init: cli # Disable interrupts
158 xor %ax,%ax # Zero/segment
160 mov $MEM_BTX_ESP0,%sp # stack
161 mov %ax,%es # Address
169 mov $MEM_BTX_IDT,%di # Memory to initialize
170 mov $(MEM_BTX_ZEND-MEM_BTX_IDT)/2,%cx # Words to zero
175 * Update real mode IDT for reflecting hardware interrupts.
177 mov $intr20,%bx # Address first handler
178 mov $0x10,%cx # Number of handlers
179 mov $0x20*4,%di # First real mode IDT entry
180 init.0: mov %bx,(%di) # Store IP
181 inc %di # Address next
184 add $4,%bx # Next handler
185 loop init.0 # Next IRQ
190 mov $idtctl,%si # Control string
191 init.1: lodsb # Get entry
193 xchg %ax,%cx # as word
194 jcxz init.4 # If done
196 xchg %ax,%dx # P:DPL:type
199 lodsw # Get handler offset
200 mov $SEL_SCODE,%dh # Segment selector
201 init.2: shr %bx # Handle this int?
203 mov %ax,(%di) # Set handler offset
204 mov %dh,0x2(%di) # and selector
205 mov %dl,0x5(%di) # Set P:DPL:type
206 add $0x4,%ax # Next handler
207 init.3: lea 0x8(%di),%di # Next entry
208 loop init.2 # Till set done
209 jmp init.1 # Continue
214 init.4: movb $_ESP0H,TSS_ESP0+1(%di) # Set ESP0
215 movb $SEL_SDATA,TSS_SS0(%di) # Set SS0
216 movb $_TSSIO,TSS_MAP(%di) # Set I/O bit map base
218 * Bring up the system.
220 mov $0x2820,%bx # Set protected mode
221 callw setpic # IRQ offsets
222 lidt idtdesc # Set IDT
223 lgdt gdtdesc # Set GDT
224 mov %cr0,%eax # Switch to protected
227 ljmp $SEL_SCODE,$init.8 # To 32-bit code
229 init.8: xorl %ecx,%ecx # Zero
230 movb $SEL_SDATA,%cl # To 32-bit
236 movb $SEL_TSS,%cl # Set task
240 * BTX user area base of VM, for converting physical stack
241 * addresses to btx-client virtual stack addresses.
243 movl $MEM_BTX_USR,%edx
244 #if !defined(MEM_BTX_USR_STK)
246 * XXX We should NOT use BDA_MEM here. Use a fixed location
247 * instead. (%eax is a physical stack addr)
249 * (must match stack specified in btxldr)
251 movzwl %ss:BDA_MEM,%eax # Get free memory
252 decl %eax # Don't quite trust bios
253 shll $0xa,%eax # To bytes
256 * Use a fixed user stack instead of depending on BDA_MEM.
257 * %eax is a physical * stack address.
259 movl $MEM_BTX_USR_STK,%eax
261 subl $USR_ARGSPACE,%eax # Less arg space
262 subl %edx,%eax # Less base Phys->Virt
263 movb $SEL_UDATA,%cl # User data selector
265 pushl %eax # Set ESP (virtual address)
266 push $0x202 # Set flags (IF set)
267 push $SEL_UCODE # Set CS
268 pushl btx_hdr+0xc # Set EIP
273 pushl %edx # Set EAX (phys base addr of VM)
274 movb $0x7,%cl # Set remaining
275 init.9: push $0x0 # general
276 loop init.9 # registers
278 call sio_init # setup the serial console
280 popa # and initialize
281 popl %es # Initialize
290 exit: cli # Disable interrupts
291 movl $MEM_BTX_ESP0,%esp # Clear stack
296 movl %cr0,%eax # Get CR0
297 andl $~0x80000000,%eax # Disable
298 movl %eax,%cr0 # paging
299 xorl %ecx,%ecx # Zero
300 movl %ecx,%cr3 # Flush TLB
303 * Restore the GDT in case we caught a kernel trap.
305 lgdt gdtdesc # Set GDT
310 ljmpw $SEL_RCODE,$exit.1 # Reload CS
312 exit.1: mov $SEL_RDATA,%cl # 16-bit selector
313 mov %cx,%ss # Reload SS
315 mov %cx,%es # remaining
316 mov %cx,%fs # segment
317 mov %cx,%gs # registers
320 * To real-address mode.
323 mov %eax,%cr0 # real mode
324 ljmp $0x0,$exit.2 # Reload CS
325 exit.2: xor %ax,%ax # Real mode segment
326 mov %ax,%ss # Reload SS
327 mov %ax,%ds # Address data
328 mov $0x7008,%bx # Set real mode
329 callw setpic # IRQ offsets
330 lidt ivtdesc # Set IVT
333 * Reboot or await reset.
335 sti # Enable interrupts
336 testb $0x1,btx_hdr+0x7 # Reboot?
337 exit.3: jz exit.3 # No
338 movw $0x1234, BDA_BOOT # Do a warm boot
339 ljmp $0xf000,$0xfff0 # reboot the machine
342 * Set IRQ offsets by reprogramming 8259A PICs.
344 setpic: in $0x21,%al # Save master
346 in $0xa1,%al # Save slave
348 movb $0x11,%al # ICW1 to
349 outb %al,$0x20 # master,
350 outb %al,$0xa0 # slave
351 movb %bl,%al # ICW2 to
352 outb %al,$0x21 # master
353 movb %bh,%al # ICW2 to
354 outb %al,$0xa1 # slave
355 movb $0x4,%al # ICW3 to
356 outb %al,$0x21 # master
357 movb $0x2,%al # ICW3 to
358 outb %al,$0xa1 # slave
359 movb $0x1,%al # ICW4 to
360 outb %al,$0x21 # master,
361 outb %al,$0xa1 # slave
362 pop %ax # Restore slave
364 pop %ax # Restore master
370 * Exception jump table.
372 intx00: push $0x0 # Int 0x0: #DE
373 jmp ex_noc # Divide error
374 push $0x1 # Int 0x1: #DB
376 push $0x3 # Int 0x3: #BP
377 jmp ex_noc # Breakpoint
378 push $0x4 # Int 0x4: #OF
379 jmp ex_noc # Overflow
380 push $0x5 # Int 0x5: #BR
381 jmp ex_noc # BOUND range exceeded
382 push $0x6 # Int 0x6: #UD
383 jmp ex_noc # Invalid opcode
384 push $0x7 # Int 0x7: #NM
385 jmp ex_noc # Device not available
386 push $0x8 # Int 0x8: #DF
387 jmp except # Double fault
388 push $0xa # Int 0xa: #TS
389 jmp except # Invalid TSS
390 push $0xb # Int 0xb: #NP
391 jmp except # Segment not present
392 push $0xc # Int 0xc: #SS
393 jmp except # Stack segment fault
394 push $0xd # Int 0xd: #GP
395 jmp except # General protection
396 push $0xe # Int 0xe: #PF
397 jmp except # Page fault
398 intx10: push $0x10 # Int 0x10: #MF
399 jmp ex_noc # Floating-point error
402 * Save a zero error code.
404 ex_noc: pushl (%esp,1) # Duplicate int no
405 movb $0x0,0x4(%esp,1) # Fake error code
410 except: cld # String ops inc
418 cmpw $SEL_SCODE,0x44(%esp,1) # Supervisor mode?
421 jmp except.2 # Join common code
422 except.1: pushl 0x50(%esp,1) # Set SS
423 except.2: pushl 0x50(%esp,1) # Set ESP
424 push $SEL_SDATA # Set up
428 movl %esp,%ebx # Stack frame
429 movl $dmpfmt,%esi # Dump format string
430 movl $MEM_BTX_BUF,%edi # Buffer
434 call putstr # display
435 leal 0x18(%esp,1),%esp # Discard frame
439 cmpb $0x3,(%esp,1) # Breakpoint?
441 cmpb $0x1,(%esp,1) # Debug?
443 testl $PSL_T,0x10(%esp,1) # Trap flag set?
445 except.2a: jmp exit # Exit
446 except.3: leal 0x8(%esp,1),%esp # Discard err, int no
447 iret # From interrupt
450 * Reboot the machine by setting the reboot flag and exiting
452 reboot: orb $0x1,btx_hdr+0x7 # Set the reboot flag
453 jmp exit # Terminate BTX and reboot
456 * Protected Mode Hardware interrupt jump table.
458 intx20: push $0x8 # Int 0x20: IRQ0
459 jmp int_hw # V86 int 0x8
460 push $0x9 # Int 0x21: IRQ1
461 jmp int_hw # V86 int 0x9
462 push $0xa # Int 0x22: IRQ2
463 jmp int_hw # V86 int 0xa
464 push $0xb # Int 0x23: IRQ3
465 jmp int_hw # V86 int 0xb
466 push $0xc # Int 0x24: IRQ4
467 jmp int_hw # V86 int 0xc
468 push $0xd # Int 0x25: IRQ5
469 jmp int_hw # V86 int 0xd
470 push $0xe # Int 0x26: IRQ6
471 jmp int_hw # V86 int 0xe
472 push $0xf # Int 0x27: IRQ7
473 jmp int_hw # V86 int 0xf
474 push $0x70 # Int 0x28: IRQ8
475 jmp int_hw # V86 int 0x70
476 push $0x71 # Int 0x29: IRQ9
477 jmp int_hw # V86 int 0x71
478 push $0x72 # Int 0x2a: IRQ10
479 jmp int_hw # V86 int 0x72
480 push $0x73 # Int 0x2b: IRQ11
481 jmp int_hw # V86 int 0x73
482 push $0x74 # Int 0x2c: IRQ12
483 jmp int_hw # V86 int 0x74
484 push $0x75 # Int 0x2d: IRQ13
485 jmp int_hw # V86 int 0x75
486 push $0x76 # Int 0x2e: IRQ14
487 jmp int_hw # V86 int 0x76
488 push $0x77 # Int 0x2f: IRQ15
489 jmp int_hw # V86 int 0x77
492 * Invoke real mode interrupt/function call from user mode with arguments.
494 intx31: pushl $-1 # Dummy int no for btx_v86
497 * Invoke real mode interrupt/function call from protected mode.
499 * We place a trampoline on the user stack that will return to rret_tramp
500 * which will reenter protected mode and then finally return to the user
503 * Kernel frame %esi points to: Real mode stack frame at MEM_BTX_ESPR:
505 * -0x00 user %ss -0x04 kernel %esp (with full frame)
506 * -0x04 user %esp -0x08 btx_v86 pointer
507 * -0x08 user %eflags -0x0c flags (only used if interrupt)
508 * -0x0c user %cs -0x10 real mode CS:IP return trampoline
509 * -0x10 user %eip -0x12 real mode flags
510 * -0x14 int no -0x16 real mode CS:IP (target)
523 * -0x48 zero %eax (hardware int only)
524 * -0x4c zero %ecx (hardware int only)
525 * -0x50 zero %edx (hardware int only)
526 * -0x54 zero %ebx (hardware int only)
527 * -0x58 zero %esp (hardware int only)
528 * -0x5c zero %ebp (hardware int only)
529 * -0x60 zero %esi (hardware int only)
530 * -0x64 zero %edi (hardware int only)
531 * -0x68 zero %gs (hardware int only)
532 * -0x6c zero %fs (hardware int only)
533 * -0x70 zero %ds (hardware int only)
534 * -0x74 zero %es (hardware int only)
536 int_hw: cld # String ops inc
542 push $SEL_SDATA # Set up
546 leal 0x44(%esp,1),%esi # Base of frame
547 movl %esp,MEM_BTX_ESPR-0x04 # Save kernel stack pointer
548 movl -0x14(%esi),%eax # Get Int no
549 cmpl $-1,%eax # Hardware interrupt?
552 * v86 calls save the btx_v86 pointer on the real mode stack and read
553 * the address and flags from the btx_v86 structure. For interrupt
554 * handler invocations (VM86 INTx requests), disable interrupts,
555 * tracing, and alignment checking while the handler runs.
557 movl $MEM_BTX_USR,%ebx # User base
558 movl %ebx,%edx # address
559 addl -0x4(%esi),%ebx # User ESP
560 movl (%ebx),%ebp # btx_v86 pointer
561 addl %ebp,%edx # Flatten btx_v86 ptr
562 movl %edx,MEM_BTX_ESPR-0x08 # Save btx_v86 ptr
563 movl V86_ADDR(%edx),%eax # Get int no/address
564 movl V86_CTL(%edx),%edx # Get control flags
565 movl -0x08(%esi),%ebx # Save user flags in %ebx
566 testl $V86F_ADDR,%edx # Segment:offset?
568 andl $~(PSL_I|PSL_T|PSL_AC),%ebx # Disable interrupts, tracing,
569 # and alignment checking for
571 jmp intusr.3 # Skip hardware interrupt
573 * Hardware interrupts store a NULL btx_v86 pointer and use the
574 * address (interrupt number) from the stack with empty flags. Also,
575 * push a dummy frame of zeros onto the stack for all the general
576 * purpose and segment registers and clear %eflags. This gives the
577 * hardware interrupt handler a clean slate.
579 intusr.1: xorl %edx,%edx # Control flags
580 movl %edx,MEM_BTX_ESPR-0x08 # NULL btx_v86 ptr
581 movl $12,%ecx # Frame is 12 dwords
582 intusr.2: pushl $0x0 # Fill frame
583 loop intusr.2 # with zeros
584 movl $PSL_RESERVED_DEFAULT,%ebx # Set clean %eflags
586 * Look up real mode IDT entry for hardware interrupts and VM86 INTx
589 intusr.3: shll $0x2,%eax # Scale
590 movl (%eax),%eax # Load int vector
591 jmp intusr.5 # Skip CALLF test
593 * Panic if V86F_CALLF isn't set with V86F_ADDR.
595 intusr.4: testl $V86F_CALLF,%edx # Far call?
597 movl %edx,0x30(%esp,1) # Place VM86 flags in int no
598 movl $badvm86,%esi # Display bad
599 call putstr # VM86 call
604 popal # Restore gp regs
607 * %eax now holds the segment:offset of the function.
608 * %ebx now holds the %eflags to pass to real mode.
609 * %edx now holds the V86F_* flags.
611 intusr.5: movw %bx,MEM_BTX_ESPR-0x12 # Pass user flags to real mode
614 * If this is a v86 call, copy the seg regs out of the btx_v86 structure.
616 movl MEM_BTX_ESPR-0x08,%ecx # Get btx_v86 ptr
617 jecxz intusr.6 # Skip for hardware ints
618 leal -0x44(%esi),%edi # %edi => kernel stack seg regs
620 leal V86_ES(%ecx),%esi # %esi => btx_v86 seg regs
621 movl $4,%ecx # Copy seg regs
623 movsl # to kernel stack
625 intusr.6: movl -0x08(%esi),%ebx # Copy user flags to real
626 movl %ebx,MEM_BTX_ESPR-0x0c # mode return trampoline
627 movl $rret_tramp,%ebx # Set return trampoline
628 movl %ebx,MEM_BTX_ESPR-0x10 # CS:IP
629 movl %eax,MEM_BTX_ESPR-0x16 # Real mode target CS:IP
630 ljmpw $SEL_RCODE,$intusr.7 # Change to 16-bit segment
632 intusr.7: movl %cr0,%eax # Leave
634 movl %eax,%cr0 # mode
636 intusr.8: xorw %ax,%ax # Reset %ds
639 lidt ivtdesc # Set IVT
644 popal # Restore gp regs
645 movw $MEM_BTX_ESPR-0x16,%sp # Switch to real mode stack
646 iret # Call target routine
648 * For the return to real mode we setup a stack frame like this on the real
649 * mode stack. Note that callf calls won't pop off the flags, but we just
650 * ignore that by repositioning %sp to be just above the btx_v86 pointer
651 * so it is aligned. The stack is relative to MEM_BTX_ESPR.
669 rret_tramp: movw $MEM_BTX_ESPR-0x08,%sp # Reset stack pointer
670 pushal # Save gp regs
675 pushfl # Save %eflags
676 pushl $PSL_RESERVED_DEFAULT|PSL_D # Use clean %eflags with
677 popfl # string ops dec
678 xorw %ax,%ax # Reset seg
680 movw %ax,%es # (%ss is already 0)
681 lidt idtdesc # Set IDT
682 lgdt gdtdesc # Set GDT
683 mov %cr0,%eax # Switch to protected
686 ljmp $SEL_SCODE,$rret_tramp.1 # To 32-bit code
688 rret_tramp.1: xorl %ecx,%ecx # Zero
689 movb $SEL_SDATA,%cl # Setup
690 movw %cx,%ss # 32-bit
693 movl MEM_BTX_ESPR-0x04,%esp # Switch to kernel stack
694 leal 0x44(%esp,1),%esi # Base of frame
695 andb $~0x2,tss_desc+0x5 # Clear TSS busy
696 movb $SEL_TSS,%cl # Set task
699 * Now we are back in protected mode. The kernel stack frame set up
700 * before entering real mode is still intact. For hardware interrupts,
701 * leave the frame unchanged.
703 cmpl $0,MEM_BTX_ESPR-0x08 # Leave saved regs unchanged
704 jz rret_tramp.3 # for hardware ints
706 * For V86 calls, copy the registers off of the real mode stack onto
707 * the kernel stack as we want their updated values. Also, initialize
708 * the segment registers on the kernel stack.
710 * Note that the %esp in the kernel stack after this is garbage, but popa
711 * ignores it, so we don't have to fix it up.
713 leal -0x18(%esi),%edi # Kernel stack GP regs
715 movl $MEM_BTX_ESPR-0x0c,%esi # Real mode stack GP regs
716 movl $8,%ecx # Copy GP regs from
717 rep # real mode stack
718 movsl # to kernel stack
719 movl $SEL_UDATA,%eax # Selector for data seg regs
720 movl $4,%ecx # Initialize %ds,
724 * For V86 calls, copy the saved seg regs on the real mode stack back
725 * over to the btx_v86 structure. Also, conditionally update the
726 * saved eflags on the kernel stack based on the flags from the user.
728 movl MEM_BTX_ESPR-0x08,%ecx # Get btx_v86 ptr
729 leal V86_GS(%ecx),%edi # %edi => btx_v86 seg regs
730 leal MEM_BTX_ESPR-0x2c,%esi # %esi => real mode seg regs
731 xchgl %ecx,%edx # Save btx_v86 ptr
732 movl $4,%ecx # Copy seg regs
733 rep # from real mode stack
736 movl V86_CTL(%edx),%edx # Read V86 control flags
737 testl $V86F_FLAGS,%edx # User wants flags?
739 movl MEM_BTX_ESPR-0x3c,%eax # Read real mode flags
740 andl $~(PSL_T|PSL_NT),%eax # Clear unsafe flags
741 movw %ax,-0x08(%esi) # Update user flags (low 16)
743 * Return to the user task
745 rret_tramp.3: popl %es # Restore
749 popal # Restore gp regs
750 addl $4,%esp # Discard int no
751 iret # Return to user mode
756 intx30: cmpl $SYS_EXEC,%eax # Exec system call?
766 movl $MEM_BTX_USR,%eax # User base address
767 addl 0xc(%esp,1),%eax # Change to user
768 leal 0x4(%eax),%esp # stack
771 intx30.1: orb $0x1,%ss:btx_hdr+0x7 # Flag reboot
775 * Dump structure [EBX] to [EDI], using format string [ESI].
777 dump.0: stosb # Save char
778 dump: lodsb # Load char
779 testb %al,%al # End of string?
781 testb $0x80,%al # Control?
783 movb %al,%ch # Save control
784 movb $'=',%al # Append
789 addl %ebx,%esi # pointer
790 testb $DMP_X16,%ch # Dump word?
794 dump.1: testb $DMP_X32,%ch # Dump long?
798 dump.2: testb $DMP_MEM,%ch # Dump memory?
801 testl $PSL_VM,0x50(%ebx) # V86 mode?
803 verr 0x4(%esi) # Readable selector?
805 ldsl (%esi),%esi # Load pointer
806 jmp dump.4 # Join common code
807 dump.3: lodsl # Set offset
808 xchgl %eax,%edx # Save
810 shll $0x4,%eax # * 0x10
811 addl %edx,%eax # + offset
812 xchgl %eax,%esi # Set pointer
813 dump.4: movb $2,%dl # Num lines
814 dump.4a: movb $0x10,%cl # Bytes to dump
815 dump.5: lodsb # Get byte and
817 decb %cl # Keep count
819 movb $'-',%al # Separator
820 cmpb $0x8,%cl # Half way?
822 movb $' ',%al # Use space
823 dump.6: stosb # Save separator
824 jmp dump.5 # Continue
825 dump.6a: decb %dl # Keep count
827 movb $0xa,%al # Line feed
829 movb $7,%cl # Leading
830 movb $' ',%al # spaces
831 dump.6b: stosb # Dump
834 jmp dump.4a # Next line
835 dump.7: popl %ds # Restore
836 dump.8: popl %esi # Restore
837 movb $0xa,%al # Line feed
838 testb $DMP_EOL,%ch # End of line?
840 movb $' ',%al # Use spaces
842 dump.9: jmp dump.0 # Continue
843 dump.10: stosb # Terminate string
847 * Convert EAX, AX, or AL to hex, saving the result to [EDI].
849 hex32: pushl %eax # Save
850 shrl $0x10,%eax # Do upper
853 hex16: call hex16.1 # Do upper 8
854 hex16.1: xchgb %ah,%al # Save/restore
855 hex8: pushl %eax # Save
856 shrb $0x4,%al # Do upper
859 hex8.1: andb $0xf,%al # Get lower 4
860 cmpb $0xa,%al # Convert
861 sbbb $0x69,%al # to hex
863 orb $0x20,%al # To lower case
868 * Output zero-terminated string [ESI] to the console.
870 putstr.0: call putchr # Output char
871 putstr: lodsb # Load char
872 testb %al,%al # End of string?
876 .set SIO_PRT,SIOPRT # Base port
877 .set SIO_FMT,SIOFMT # 8N1
878 .set SIO_DIV,(115200/SIOSPD) # 115200 / SPD
881 * void sio_init(void)
883 sio_init: movw $SIO_PRT+0x3,%dx # Data format reg
884 movb $SIO_FMT|0x80,%al # Set format
885 outb %al,(%dx) # and DLAB
887 subb $0x3,%dl # Divisor latch reg
888 movw $SIO_DIV,%ax # Set
891 movb $SIO_FMT,%al # Clear
892 outb %al,(%dx) # DLAB
893 incl %edx # Modem control reg
894 movb $0x3,%al # Set RTS,
896 incl %edx # Line status reg
899 * void sio_flush(void)
901 sio_flush.0: call sio_getc.1 # Get character
902 sio_flush: call sio_ischar # Check for character
903 jnz sio_flush.0 # Till none
907 * void sio_putc(int c)
909 sio_putc: movw $SIO_PRT+0x5,%dx # Line status reg
910 xor %ecx,%ecx # Timeout
911 movb $0x40,%ch # counter
912 sio_putc.1: inb (%dx),%al # Transmitter
913 testb $0x20,%al # buffer empty?
914 loopz sio_putc.1 # No
915 jz sio_putc.2 # If timeout
916 movb 0x4(%esp,1),%al # Get character
917 subb $0x5,%dl # Transmitter hold reg
918 outb %al,(%dx) # Write character
919 sio_putc.2: ret $0x4 # To caller
924 sio_getc: call sio_ischar # Character available?
926 sio_getc.1: subb $0x5,%dl # Receiver buffer reg
927 inb (%dx),%al # Read character
931 * int sio_ischar(void)
933 sio_ischar: movw $SIO_PRT+0x5,%dx # Line status register
934 xorl %eax,%eax # Zero
935 inb (%dx),%al # Received data
936 andb $0x1,%al # ready?
940 * Output character AL to the serial console.
943 cmpb $10, %al # is it a newline?
944 jne putchr.1 # no?, then leave
945 push $13 # output a carriage
946 call sio_putc # return first
947 movb $10, %al # restore %al
948 putchr.1: pushl %eax # Push the character
950 call sio_putc # Output the character
955 * Output character AL to the console.
958 xorl %ecx,%ecx # Zero for loops
959 movb $SCR_MAT,%ah # Mode/attribute
960 movl $BDA_POS,%ebx # BDA pointer
961 movw (%ebx),%dx # Cursor position
962 movl $0xb8000,%edi # Regen buffer (color)
963 cmpb %ah,BDA_SCR-BDA_POS(%ebx) # Mono mode?
965 xorw %di,%di # Regen buffer (mono)
966 putchr.1: cmpb $0xa,%al # New line?
968 xchgl %eax,%ecx # Save char
969 movb $SCR_COL,%al # Columns per row
970 mulb %dh # * row position
971 addb %dl,%al # + column
972 adcb $0x0,%ah # position
974 xchgl %eax,%ecx # Swap char, offset
975 movw %ax,(%edi,%ecx,1) # Write attr:char
976 incl %edx # Bump cursor
977 cmpb $SCR_COL,%dl # Beyond row?
979 putchr.2: xorb %dl,%dl # Zero column
981 putchr.3: cmpb $SCR_ROW,%dh # Beyond screen?
983 leal 2*SCR_COL(%edi),%esi # New top line
984 movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move
987 movb $0x20,%al # Space
988 movb $SCR_COL,%cl # Columns to clear
991 movb $SCR_ROW-1,%dh # Bottom line
992 putchr.4: movw %dx,(%ebx) # Update position
999 * Real Mode Hardware interrupt jump table.
1001 intr20: push $0x8 # Int 0x20: IRQ0
1002 jmp int_hwr # V86 int 0x8
1003 push $0x9 # Int 0x21: IRQ1
1004 jmp int_hwr # V86 int 0x9
1005 push $0xa # Int 0x22: IRQ2
1006 jmp int_hwr # V86 int 0xa
1007 push $0xb # Int 0x23: IRQ3
1008 jmp int_hwr # V86 int 0xb
1009 push $0xc # Int 0x24: IRQ4
1010 jmp int_hwr # V86 int 0xc
1011 push $0xd # Int 0x25: IRQ5
1012 jmp int_hwr # V86 int 0xd
1013 push $0xe # Int 0x26: IRQ6
1014 jmp int_hwr # V86 int 0xe
1015 push $0xf # Int 0x27: IRQ7
1016 jmp int_hwr # V86 int 0xf
1017 push $0x70 # Int 0x28: IRQ8
1018 jmp int_hwr # V86 int 0x70
1019 push $0x71 # Int 0x29: IRQ9
1020 jmp int_hwr # V86 int 0x71
1021 push $0x72 # Int 0x2a: IRQ10
1022 jmp int_hwr # V86 int 0x72
1023 push $0x73 # Int 0x2b: IRQ11
1024 jmp int_hwr # V86 int 0x73
1025 push $0x74 # Int 0x2c: IRQ12
1026 jmp int_hwr # V86 int 0x74
1027 push $0x75 # Int 0x2d: IRQ13
1028 jmp int_hwr # V86 int 0x75
1029 push $0x76 # Int 0x2e: IRQ14
1030 jmp int_hwr # V86 int 0x76
1031 push $0x77 # Int 0x2f: IRQ15
1032 jmp int_hwr # V86 int 0x77
1034 * Reflect hardware interrupts in real mode.
1036 int_hwr: push %ax # Save
1039 mov %sp,%bp # Address stack frame
1040 xchg %bx,6(%bp) # Swap BX, int no
1041 xor %ax,%ax # Set %ds:%bx to
1042 shl $2,%bx # point to
1043 mov %ax,%ds # IDT entry
1044 mov (%bx),%ax # Load IP
1045 mov 2(%bx),%bx # Load CS
1046 xchg %ax,4(%bp) # Swap saved %ax,%bx with
1047 xchg %bx,6(%bp) # CS:IP of handler
1050 lret # Jump to handler
1053 * Global descriptor table.
1055 * 16: segment extent lsb
1056 * 24: segment base lsb
1062 * 4: segment extent msb
1064 * 1: 32 bit, else 16 bit
1065 * 1: limit granularity byte/page units
1067 * 8: segment base msb
1071 gdt: .word 0x0,0x0,0x0,0x0 # Null entry
1072 .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE
1073 .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA
1074 .word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE
1075 .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
1076 .word 0xffff,MEM_BTX_USR,0xfa00,0xcf# SEL_UCODE
1077 .word 0xffff,MEM_BTX_USR,0xf200,0xcf# SEL_UDATA
1078 tss_desc: .word _TSSLM,MEM_BTX_TSS,0x8900,0x0 # SEL_TSS
1081 * Pseudo-descriptors.
1083 gdtdesc: .word gdt.1-gdt-1,gdt,0x0 # GDT
1084 idtdesc: .word _IDTLM,MEM_BTX_IDT,0x0 # IDT
1085 ivtdesc: .word 0x400-0x0-1,0x0,0x0 # IVT
1088 * IDT construction control string.
1090 idtctl: .byte 0x10, 0x8e # Int 0x0-0xf
1091 .word 0x7dfb,intx00 # (exceptions)
1092 .byte 0x10, 0x8e # Int 0x10
1093 .word 0x1, intx10 # (exception)
1094 .byte 0x10, 0x8e # Int 0x20-0x2f
1095 .word 0xffff,intx20 # (hardware)
1096 .byte 0x1, 0xee # int 0x30
1097 .word 0x1, intx30 # (system call)
1098 .byte 0x2, 0xee # Int 0x31-0x32
1099 .word 0x1, intx31 # (V86, null)
1100 .byte 0x0 # End of string
1103 * Dump format string.
1105 dmpfmt: .byte '\n' # "\n"
1106 .ascii "int" # "int="
1107 .byte 0x80|DMP_X32, 0x40 # "00000000 "
1108 .ascii "err" # "err="
1109 .byte 0x80|DMP_X32, 0x44 # "00000000 "
1110 .ascii "efl" # "efl="
1111 .byte 0x80|DMP_X32, 0x50 # "00000000 "
1112 .ascii "eip" # "eip="
1113 .byte 0x80|DMP_X32|DMP_EOL,0x48 # "00000000\n"
1114 .ascii "eax" # "eax="
1115 .byte 0x80|DMP_X32, 0x34 # "00000000 "
1116 .ascii "ebx" # "ebx="
1117 .byte 0x80|DMP_X32, 0x28 # "00000000 "
1118 .ascii "ecx" # "ecx="
1119 .byte 0x80|DMP_X32, 0x30 # "00000000 "
1120 .ascii "edx" # "edx="
1121 .byte 0x80|DMP_X32|DMP_EOL,0x2c # "00000000\n"
1122 .ascii "esi" # "esi="
1123 .byte 0x80|DMP_X32, 0x1c # "00000000 "
1124 .ascii "edi" # "edi="
1125 .byte 0x80|DMP_X32, 0x18 # "00000000 "
1126 .ascii "ebp" # "ebp="
1127 .byte 0x80|DMP_X32, 0x20 # "00000000 "
1128 .ascii "esp" # "esp="
1129 .byte 0x80|DMP_X32|DMP_EOL,0x0 # "00000000\n"
1131 .byte 0x80|DMP_X16, 0x4c # "0000 "
1133 .byte 0x80|DMP_X16, 0xc # "0000 "
1135 .byte 0x80|DMP_X16, 0x8 # "0000 "
1138 .byte 0x80|DMP_X16, 0x10 # "0000 "
1140 .byte 0x80|DMP_X16, 0x14 # "0000 "
1142 .byte 0x80|DMP_X16|DMP_EOL,0x4 # "0000\n"
1143 .ascii "cs:eip" # "cs:eip="
1144 .byte 0x80|DMP_MEM|DMP_EOL,0x48 # "00 00 ... 00 00\n"
1145 .ascii "ss:esp" # "ss:esp="
1146 .byte 0x80|DMP_MEM|DMP_EOL,0x0 # "00 00 ... 00 00\n"
1147 .asciz "BTX halted\n" # End
1149 * Bad VM86 call panic
1151 badvm86: .asciz "Invalid VM86 Request\n"
1155 * End of BTX memory.