/* * Copyright (c) 1998 Robert Nordier * All rights reserved. * * Redistribution and use in source and binary forms are freely * permitted provided that the above copyright notice and this * paragraph and the following disclaimer are duplicated in all * such forms. * * This software is provided "AS IS" and without any express or * implied warranties, including, without limitation, the implied * warranties of merchantability and fitness for a particular * purpose. * * $FreeBSD$ */ #include /* * Memory layout. */ .set MEM_BTX,0x1000 # Start of BTX memory .set MEM_ESP0,0x1800 # Supervisor stack .set MEM_BUF,0x1800 # Scratch buffer .set MEM_ESPR,0x5e00 # Real mode stack .set MEM_IDT,0x5e00 # IDT .set MEM_TSS,0x5f98 # TSS .set MEM_MAP,0x6000 # I/O bit map .set MEM_TSS_END,0x7fff # End of TSS .set MEM_ORG,0x9000 # BTX code .set MEM_USR,0xa000 # Start of user memory /* * Paging control. */ .set PAG_SIZ,0x1000 # Page size .set PAG_CNT,0x1000 # Pages to map /* * Fields in %eflags. */ .set PSL_RESERVED_DEFAULT,0x00000002 .set PSL_T,0x00000100 # Trap flag .set PSL_I,0x00000200 # Interrupt enable flag .set PSL_D,0x00000400 # String instruction direction .set PSL_NT,0x00004000 # Nested task flag .set PSL_VM,0x00020000 # Virtual 8086 mode flag .set PSL_AC,0x00040000 # Alignment check flag /* * Segment selectors. */ .set SEL_SCODE,0x8 # Supervisor code .set SEL_SDATA,0x10 # Supervisor data .set SEL_RCODE,0x18 # Real mode code .set SEL_RDATA,0x20 # Real mode data .set SEL_UCODE,0x28|3 # User code .set SEL_UDATA,0x30|3 # User data .set SEL_TSS,0x38 # TSS /* * Task state segment fields. */ .set TSS_ESP0,0x4 # PL 0 ESP .set TSS_SS0,0x8 # PL 0 SS .set TSS_MAP,0x66 # I/O bit map base /* * System calls. */ .set SYS_EXIT,0x0 # Exit .set SYS_EXEC,0x1 # Exec /* * Fields in V86 interface structure. */ .set V86_CTL,0x0 # Control flags .set V86_ADDR,0x4 # Int number/address .set V86_ES,0x8 # V86 ES .set V86_DS,0xc # V86 DS .set V86_FS,0x10 # V86 FS .set V86_GS,0x14 # V86 GS /* * V86 control flags. */ .set V86F_ADDR,0x10000 # Segment:offset address .set V86F_CALLF,0x20000 # Emulate far call .set V86F_FLAGS,0x40000 # Return flags /* * Dump format control bytes. */ .set DMP_X16,0x1 # Word .set DMP_X32,0x2 # Long .set DMP_MEM,0x4 # Memory .set DMP_EOL,0x8 # End of line /* * Screen defaults and assumptions. */ .set SCR_MAT,0x7 # Mode/attribute .set SCR_COL,0x50 # Columns per row .set SCR_ROW,0x19 # Rows per screen /* * BIOS Data Area locations. */ .set BDA_MEM,0x413 # Free memory .set BDA_SCR,0x449 # Video mode .set BDA_POS,0x450 # Cursor position .set BDA_BOOT,0x472 # Boot howto flag /* * Derivations, for brevity. */ .set _ESP0H,MEM_ESP0>>0x8 # Byte 1 of ESP0 .set _TSSIO,MEM_MAP-MEM_TSS # TSS I/O base .set _TSSLM,MEM_TSS_END-MEM_TSS # TSS limit .set _IDTLM,MEM_TSS-MEM_IDT-1 # IDT limit /* * Code segment. */ .globl start .code16 start: # Start of code /* * BTX header. */ btx_hdr: .byte 0xeb # Machine ID .byte 0xe # Header size .ascii "BTX" # Magic .byte 0x1 # Major version .byte 0x2 # Minor version .byte BTX_FLAGS # Flags .word PAG_CNT-MEM_ORG>>0xc # Paging control .word break-start # Text size .long 0x0 # Entry address /* * Initialization routine. */ init: cli # Disable interrupts xor %ax,%ax # Zero/segment mov %ax,%ss # Set up mov $MEM_ESP0,%sp # stack mov %ax,%es # Address mov %ax,%ds # data pushl $0x2 # Clear popfl # flags /* * Initialize memory. */ mov $MEM_IDT,%di # Memory to initialize mov $(MEM_ORG-MEM_IDT)/2,%cx # Words to zero rep # Zero-fill stosw # memory /* * Update real mode IDT for reflecting hardware interrupts. */ mov $intr20,%bx # Address first handler mov $0x10,%cx # Number of handlers mov $0x20*4,%di # First real mode IDT entry init.0: mov %bx,(%di) # Store IP inc %di # Address next inc %di # entry stosw # Store CS add $4,%bx # Next handler loop init.0 # Next IRQ /* * Create IDT. */ mov $MEM_IDT,%di mov $idtctl,%si # Control string init.1: lodsb # Get entry cbw # count xchg %ax,%cx # as word jcxz init.4 # If done lodsb # Get segment xchg %ax,%dx # P:DPL:type lodsw # Get control xchg %ax,%bx # set lodsw # Get handler offset mov $SEL_SCODE,%dh # Segment selector init.2: shr %bx # Handle this int? jnc init.3 # No mov %ax,(%di) # Set handler offset mov %dh,0x2(%di) # and selector mov %dl,0x5(%di) # Set P:DPL:type add $0x4,%ax # Next handler init.3: lea 0x8(%di),%di # Next entry loop init.2 # Till set done jmp init.1 # Continue /* * Initialize TSS. */ init.4: movb $_ESP0H,TSS_ESP0+1(%di) # Set ESP0 movb $SEL_SDATA,TSS_SS0(%di) # Set SS0 movb $_TSSIO,TSS_MAP(%di) # Set I/O bit map base /* * Bring up the system. */ mov $0x2820,%bx # Set protected mode callw setpic # IRQ offsets lidt idtdesc # Set IDT lgdt gdtdesc # Set GDT mov %cr0,%eax # Switch to protected inc %ax # mode mov %eax,%cr0 # ljmp $SEL_SCODE,$init.8 # To 32-bit code .code32 init.8: xorl %ecx,%ecx # Zero movb $SEL_SDATA,%cl # To 32-bit movw %cx,%ss # stack /* * Launch user task. */ movb $SEL_TSS,%cl # Set task ltr %cx # register movl $MEM_USR,%edx # User base address movzwl %ss:BDA_MEM,%eax # Get free memory shll $0xa,%eax # To bytes subl $ARGSPACE,%eax # Less arg space subl %edx,%eax # Less base movb $SEL_UDATA,%cl # User data selector pushl %ecx # Set SS pushl %eax # Set ESP push $0x202 # Set flags (IF set) push $SEL_UCODE # Set CS pushl btx_hdr+0xc # Set EIP pushl %ecx # Set GS pushl %ecx # Set FS pushl %ecx # Set DS pushl %ecx # Set ES pushl %edx # Set EAX movb $0x7,%cl # Set remaining init.9: push $0x0 # general loop init.9 # registers #ifdef BTX_SERIAL call sio_init # setup the serial console #endif popa # and initialize popl %es # Initialize popl %ds # user popl %fs # segment popl %gs # registers iret # To user mode /* * Exit routine. */ exit: cli # Disable interrupts movl $MEM_ESP0,%esp # Clear stack /* * Turn off paging. */ movl %cr0,%eax # Get CR0 andl $~0x80000000,%eax # Disable movl %eax,%cr0 # paging xorl %ecx,%ecx # Zero movl %ecx,%cr3 # Flush TLB /* * Restore the GDT in case we caught a kernel trap. */ lgdt %cs:gdtdesc # Set GDT /* * To 16 bits. */ ljmpw $SEL_RCODE,$exit.1 # Reload CS .code16 exit.1: mov $SEL_RDATA,%cl # 16-bit selector mov %cx,%ss # Reload SS mov %cx,%ds # Load mov %cx,%es # remaining mov %cx,%fs # segment mov %cx,%gs # registers /* * To real-address mode. */ dec %ax # Switch to mov %eax,%cr0 # real mode ljmp $0x0,$exit.2 # Reload CS exit.2: xor %ax,%ax # Real mode segment mov %ax,%ss # Reload SS mov %ax,%ds # Address data mov $0x7008,%bx # Set real mode callw setpic # IRQ offsets lidt ivtdesc # Set IVT /* * Reboot or await reset. */ sti # Enable interrupts testb $0x1,btx_hdr+0x7 # Reboot? exit.3: jz exit.3 # No movw $0x1234, BDA_BOOT # Do a warm boot ljmp $0xf000,$0xfff0 # reboot the machine /* * Set IRQ offsets by reprogramming 8259A PICs. */ setpic: in $0x21,%al # Save master push %ax # IMR in $0xa1,%al # Save slave push %ax # IMR movb $0x11,%al # ICW1 to outb %al,$0x20 # master, outb %al,$0xa0 # slave movb %bl,%al # ICW2 to outb %al,$0x21 # master movb %bh,%al # ICW2 to outb %al,$0xa1 # slave movb $0x4,%al # ICW3 to outb %al,$0x21 # master movb $0x2,%al # ICW3 to outb %al,$0xa1 # slave movb $0x1,%al # ICW4 to outb %al,$0x21 # master, outb %al,$0xa1 # slave pop %ax # Restore slave outb %al,$0xa1 # IMR pop %ax # Restore master outb %al,$0x21 # IMR retw # To caller .code32 /* * Exception jump table. */ intx00: push $0x0 # Int 0x0: #DE jmp ex_noc # Divide error push $0x1 # Int 0x1: #DB jmp ex_noc # Debug push $0x3 # Int 0x3: #BP jmp ex_noc # Breakpoint push $0x4 # Int 0x4: #OF jmp ex_noc # Overflow push $0x5 # Int 0x5: #BR jmp ex_noc # BOUND range exceeded push $0x6 # Int 0x6: #UD jmp ex_noc # Invalid opcode push $0x7 # Int 0x7: #NM jmp ex_noc # Device not available push $0x8 # Int 0x8: #DF jmp except # Double fault push $0xa # Int 0xa: #TS jmp except # Invalid TSS push $0xb # Int 0xb: #NP jmp except # Segment not present push $0xc # Int 0xc: #SS jmp except # Stack segment fault push $0xd # Int 0xd: #GP jmp except # General protection push $0xe # Int 0xe: #PF jmp except # Page fault intx10: push $0x10 # Int 0x10: #MF jmp ex_noc # Floating-point error /* * Save a zero error code. */ ex_noc: pushl (%esp,1) # Duplicate int no movb $0x0,0x4(%esp,1) # Fake error code /* * Handle exception. */ except: cld # String ops inc pushl %ds # Save pushl %es # most pusha # registers pushl %gs # Set GS pushl %fs # Set FS pushl %ds # Set DS pushl %es # Set ES cmpw $SEL_SCODE,0x44(%esp,1) # Supervisor mode? jne except.1 # No pushl %ss # Set SS jmp except.2 # Join common code except.1: pushl 0x50(%esp,1) # Set SS except.2: pushl 0x50(%esp,1) # Set ESP push $SEL_SDATA # Set up popl %ds # to pushl %ds # address popl %es # data movl %esp,%ebx # Stack frame movl $dmpfmt,%esi # Dump format string movl $MEM_BUF,%edi # Buffer pushl %edi # Dump to call dump # buffer popl %esi # and call putstr # display leal 0x18(%esp,1),%esp # Discard frame popa # Restore popl %es # registers popl %ds # saved cmpb $0x3,(%esp,1) # Breakpoint? je except.3 # Yes cmpb $0x1,(%esp,1) # Debug? jne except.2a # No testl $PSL_T,0x10(%esp,1) # Trap flag set? jnz except.3 # Yes except.2a: jmp exit # Exit except.3: leal 0x8(%esp,1),%esp # Discard err, int no iret # From interrupt /* * Reboot the machine by setting the reboot flag and exiting */ reboot: orb $0x1,btx_hdr+0x7 # Set the reboot flag jmp exit # Terminate BTX and reboot /* * Protected Mode Hardware interrupt jump table. */ intx20: push $0x8 # Int 0x20: IRQ0 jmp int_hw # V86 int 0x8 push $0x9 # Int 0x21: IRQ1 jmp int_hw # V86 int 0x9 push $0xa # Int 0x22: IRQ2 jmp int_hw # V86 int 0xa push $0xb # Int 0x23: IRQ3 jmp int_hw # V86 int 0xb push $0xc # Int 0x24: IRQ4 jmp int_hw # V86 int 0xc push $0xd # Int 0x25: IRQ5 jmp int_hw # V86 int 0xd push $0xe # Int 0x26: IRQ6 jmp int_hw # V86 int 0xe push $0xf # Int 0x27: IRQ7 jmp int_hw # V86 int 0xf push $0x70 # Int 0x28: IRQ8 jmp int_hw # V86 int 0x70 push $0x71 # Int 0x29: IRQ9 jmp int_hw # V86 int 0x71 push $0x72 # Int 0x2a: IRQ10 jmp int_hw # V86 int 0x72 push $0x73 # Int 0x2b: IRQ11 jmp int_hw # V86 int 0x73 push $0x74 # Int 0x2c: IRQ12 jmp int_hw # V86 int 0x74 push $0x75 # Int 0x2d: IRQ13 jmp int_hw # V86 int 0x75 push $0x76 # Int 0x2e: IRQ14 jmp int_hw # V86 int 0x76 push $0x77 # Int 0x2f: IRQ15 jmp int_hw # V86 int 0x77 /* * Invoke real mode interrupt/function call from user mode with arguments. */ intx31: pushl $-1 # Dummy int no for btx_v86 /* * Invoke real mode interrupt/function call from protected mode. * * We place a trampoline on the user stack that will return to rret_tramp * which will reenter protected mode and then finally return to the user * client. * * Kernel frame %esi points to: Real mode stack frame at MEM_ESPR: * * -0x00 user %ss -0x04 kernel %esp (with full frame) * -0x04 user %esp -0x08 btx_v86 pointer * -0x08 user %eflags -0x0c flags (only used if interrupt) * -0x0c user %cs -0x10 real mode CS:IP return trampoline * -0x10 user %eip -0x12 real mode flags * -0x14 int no -0x16 real mode CS:IP (target) * -0x18 %eax * -0x1c %ecx * -0x20 %edx * -0x24 %ebx * -0x28 %esp * -0x2c %ebp * -0x30 %esi * -0x34 %edi * -0x38 %gs * -0x3c %fs * -0x40 %ds * -0x44 %es * -0x48 zero %eax (hardware int only) * -0x4c zero %ecx (hardware int only) * -0x50 zero %edx (hardware int only) * -0x54 zero %ebx (hardware int only) * -0x58 zero %esp (hardware int only) * -0x5c zero %ebp (hardware int only) * -0x60 zero %esi (hardware int only) * -0x64 zero %edi (hardware int only) * -0x68 zero %gs (hardware int only) * -0x6c zero %fs (hardware int only) * -0x70 zero %ds (hardware int only) * -0x74 zero %es (hardware int only) */ int_hw: cld # String ops inc pusha # Save gp regs pushl %gs # Save pushl %fs # seg pushl %ds # regs pushl %es push $SEL_SDATA # Set up popl %ds # to pushl %ds # address popl %es # data leal 0x44(%esp,1),%esi # Base of frame movl %esp,MEM_ESPR-0x04 # Save kernel stack pointer movl -0x14(%esi),%eax # Get Int no cmpl $-1,%eax # Hardware interrupt? jne intusr.1 # Yes /* * v86 calls save the btx_v86 pointer on the real mode stack and read * the address and flags from the btx_v86 structure. For interrupt * handler invocations (VM86 INTx requests), disable interrupts, * tracing, and alignment checking while the handler runs. */ movl $MEM_USR,%ebx # User base movl %ebx,%edx # address addl -0x4(%esi),%ebx # User ESP movl (%ebx),%ebp # btx_v86 pointer addl %ebp,%edx # Flatten btx_v86 ptr movl %edx,MEM_ESPR-0x08 # Save btx_v86 ptr movl V86_ADDR(%edx),%eax # Get int no/address movl V86_CTL(%edx),%edx # Get control flags movl -0x08(%esi),%ebx # Save user flags in %ebx testl $V86F_ADDR,%edx # Segment:offset? jnz intusr.4 # Yes andl $~(PSL_I|PSL_T|PSL_AC),%ebx # Disable interrupts, tracing, # and alignment checking for # interrupt handler jmp intusr.3 # Skip hardware interrupt /* * Hardware interrupts store a NULL btx_v86 pointer and use the * address (interrupt number) from the stack with empty flags. Also, * push a dummy frame of zeros onto the stack for all the general * purpose and segment registers and clear %eflags. This gives the * hardware interrupt handler a clean slate. */ intusr.1: xorl %edx,%edx # Control flags movl %edx,MEM_ESPR-0x08 # NULL btx_v86 ptr movl $12,%ecx # Frame is 12 dwords intusr.2: pushl $0x0 # Fill frame loop intusr.2 # with zeros movl $PSL_RESERVED_DEFAULT,%ebx # Set clean %eflags /* * Look up real mode IDT entry for hardware interrupts and VM86 INTx * requests. */ intusr.3: shll $0x2,%eax # Scale movl (%eax),%eax # Load int vector jmp intusr.5 # Skip CALLF test /* * Panic if V86F_CALLF isn't set with V86F_ADDR. */ intusr.4: testl $V86F_CALLF,%edx # Far call? jnz intusr.5 # Ok movl %edx,0x30(%esp,1) # Place VM86 flags in int no movl $badvm86,%esi # Display bad call putstr # VM86 call popl %es # Restore popl %ds # seg popl %fs # regs popl %gs popal # Restore gp regs jmp ex_noc # Panic /* * %eax now holds the segment:offset of the function. * %ebx now holds the %eflags to pass to real mode. * %edx now holds the V86F_* flags. */ intusr.5: movw %bx,MEM_ESPR-0x12 # Pass user flags to real mode # target /* * If this is a v86 call, copy the seg regs out of the btx_v86 structure. */ movl MEM_ESPR-0x08,%ecx # Get btx_v86 ptr jecxz intusr.6 # Skip for hardware ints leal -0x44(%esi),%edi # %edi => kernel stack seg regs pushl %esi # Save leal V86_ES(%ecx),%esi # %esi => btx_v86 seg regs movl $4,%ecx # Copy seg regs rep # from btx_v86 movsl # to kernel stack popl %esi # Restore intusr.6: movl -0x08(%esi),%ebx # Copy user flags to real movl %ebx,MEM_ESPR-0x0c # mode return trampoline movl $rret_tramp,%ebx # Set return trampoline movl %ebx,MEM_ESPR-0x10 # CS:IP movl %eax,MEM_ESPR-0x16 # Real mode target CS:IP ljmpw $SEL_RCODE,$intusr.7 # Change to 16-bit segment .code16 intusr.7: movl %cr0,%eax # Leave dec %al # protected movl %eax,%cr0 # mode ljmpw $0x0,$intusr.8 intusr.8: xorw %ax,%ax # Reset %ds movw %ax,%ds # and movw %ax,%ss # %ss lidt ivtdesc # Set IVT popl %es # Restore popl %ds # seg popl %fs # regs popl %gs popal # Restore gp regs movw $MEM_ESPR-0x16,%sp # Switch to real mode stack iret # Call target routine /* * For the return to real mode we setup a stack frame like this on the real * mode stack. Note that callf calls won't pop off the flags, but we just * ignore that by repositioning %sp to be just above the btx_v86 pointer * so it is aligned. The stack is relative to MEM_ESPR. * * -0x04 kernel %esp * -0x08 btx_v86 * -0x0c %eax * -0x10 %ecx * -0x14 %edx * -0x18 %ebx * -0x1c %esp * -0x20 %ebp * -0x24 %esi * -0x28 %edi * -0x2c %gs * -0x30 %fs * -0x34 %ds * -0x38 %es * -0x3c %eflags */ rret_tramp: movw $MEM_ESPR-0x08,%sp # Reset stack pointer pushal # Save gp regs pushl %gs # Save pushl %fs # seg pushl %ds # regs pushl %es pushfl # Save %eflags pushl $PSL_RESERVED_DEFAULT|PSL_D # Use clean %eflags with popfl # string ops dec xorw %ax,%ax # Reset seg movw %ax,%ds # regs movw %ax,%es # (%ss is already 0) lidt idtdesc # Set IDT lgdt gdtdesc # Set GDT mov %cr0,%eax # Switch to protected inc %ax # mode mov %eax,%cr0 # ljmp $SEL_SCODE,$rret_tramp.1 # To 32-bit code .code32 rret_tramp.1: xorl %ecx,%ecx # Zero movb $SEL_SDATA,%cl # Setup movw %cx,%ss # 32-bit movw %cx,%ds # seg movw %cx,%es # regs movl MEM_ESPR-0x04,%esp # Switch to kernel stack leal 0x44(%esp,1),%esi # Base of frame andb $~0x2,tss_desc+0x5 # Clear TSS busy movb $SEL_TSS,%cl # Set task ltr %cx # register /* * Now we are back in protected mode. The kernel stack frame set up * before entering real mode is still intact. For hardware interrupts, * leave the frame unchanged. */ cmpl $0,MEM_ESPR-0x08 # Leave saved regs unchanged jz rret_tramp.3 # for hardware ints /* * For V86 calls, copy the registers off of the real mode stack onto * the kernel stack as we want their updated values. Also, initialize * the segment registers on the kernel stack. * * Note that the %esp in the kernel stack after this is garbage, but popa * ignores it, so we don't have to fix it up. */ leal -0x18(%esi),%edi # Kernel stack GP regs pushl %esi # Save movl $MEM_ESPR-0x0c,%esi # Real mode stack GP regs movl $8,%ecx # Copy GP regs from rep # real mode stack movsl # to kernel stack movl $SEL_UDATA,%eax # Selector for data seg regs movl $4,%ecx # Initialize %ds, rep # %es, %fs, and stosl # %gs /* * For V86 calls, copy the saved seg regs on the real mode stack back * over to the btx_v86 structure. Also, conditionally update the * saved eflags on the kernel stack based on the flags from the user. */ movl MEM_ESPR-0x08,%ecx # Get btx_v86 ptr leal V86_GS(%ecx),%edi # %edi => btx_v86 seg regs leal MEM_ESPR-0x2c,%esi # %esi => real mode seg regs xchgl %ecx,%edx # Save btx_v86 ptr movl $4,%ecx # Copy seg regs rep # from real mode stack movsl # to btx_v86 popl %esi # Restore movl V86_CTL(%edx),%edx # Read V86 control flags testl $V86F_FLAGS,%edx # User wants flags? jz rret_tramp.3 # No movl MEM_ESPR-0x3c,%eax # Read real mode flags andl $~(PSL_T|PSL_NT),%eax # Clear unsafe flags movw %ax,-0x08(%esi) # Update user flags (low 16) /* * Return to the user task */ rret_tramp.3: popl %es # Restore popl %ds # seg popl %fs # regs popl %gs popal # Restore gp regs addl $4,%esp # Discard int no iret # Return to user mode /* * System Call. */ intx30: cmpl $SYS_EXEC,%eax # Exec system call? jne intx30.1 # No pushl %ss # Set up popl %es # all pushl %es # segment popl %ds # registers pushl %ds # for the popl %fs # program pushl %fs # we're popl %gs # invoking movl $MEM_USR,%eax # User base address addl 0xc(%esp,1),%eax # Change to user leal 0x4(%eax),%esp # stack popl %eax # Call call *%eax # program intx30.1: orb $0x1,%ss:btx_hdr+0x7 # Flag reboot jmp exit # Exit /* * Dump structure [EBX] to [EDI], using format string [ESI]. */ dump.0: stosb # Save char dump: lodsb # Load char testb %al,%al # End of string? jz dump.10 # Yes testb $0x80,%al # Control? jz dump.0 # No movb %al,%ch # Save control movb $'=',%al # Append stosb # '=' lodsb # Get offset pushl %esi # Save movsbl %al,%esi # To addl %ebx,%esi # pointer testb $DMP_X16,%ch # Dump word? jz dump.1 # No lodsw # Get and call hex16 # dump it dump.1: testb $DMP_X32,%ch # Dump long? jz dump.2 # No lodsl # Get and call hex32 # dump it dump.2: testb $DMP_MEM,%ch # Dump memory? jz dump.8 # No pushl %ds # Save testl $PSL_VM,0x50(%ebx) # V86 mode? jnz dump.3 # Yes verr 0x4(%esi) # Readable selector? jnz dump.3 # No ldsl (%esi),%esi # Load pointer jmp dump.4 # Join common code dump.3: lodsl # Set offset xchgl %eax,%edx # Save lodsl # Get segment shll $0x4,%eax # * 0x10 addl %edx,%eax # + offset xchgl %eax,%esi # Set pointer dump.4: movb $2,%dl # Num lines dump.4a: movb $0x10,%cl # Bytes to dump dump.5: lodsb # Get byte and call hex8 # dump it decb %cl # Keep count jz dump.6a # If done movb $'-',%al # Separator cmpb $0x8,%cl # Half way? je dump.6 # Yes movb $' ',%al # Use space dump.6: stosb # Save separator jmp dump.5 # Continue dump.6a: decb %dl # Keep count jz dump.7 # If done movb $0xa,%al # Line feed stosb # Save one movb $7,%cl # Leading movb $' ',%al # spaces dump.6b: stosb # Dump decb %cl # spaces jnz dump.6b jmp dump.4a # Next line dump.7: popl %ds # Restore dump.8: popl %esi # Restore movb $0xa,%al # Line feed testb $DMP_EOL,%ch # End of line? jnz dump.9 # Yes movb $' ',%al # Use spaces stosb # Save one dump.9: jmp dump.0 # Continue dump.10: stosb # Terminate string ret # To caller /* * Convert EAX, AX, or AL to hex, saving the result to [EDI]. */ hex32: pushl %eax # Save shrl $0x10,%eax # Do upper call hex16 # 16 popl %eax # Restore hex16: call hex16.1 # Do upper 8 hex16.1: xchgb %ah,%al # Save/restore hex8: pushl %eax # Save shrb $0x4,%al # Do upper call hex8.1 # 4 popl %eax # Restore hex8.1: andb $0xf,%al # Get lower 4 cmpb $0xa,%al # Convert sbbb $0x69,%al # to hex das # digit orb $0x20,%al # To lower case stosb # Save char ret # (Recursive) /* * Output zero-terminated string [ESI] to the console. */ putstr.0: call putchr # Output char putstr: lodsb # Load char testb %al,%al # End of string? jnz putstr.0 # No ret # To caller #ifdef BTX_SERIAL .set SIO_PRT,SIOPRT # Base port .set SIO_FMT,SIOFMT # 8N1 .set SIO_DIV,(115200/SIOSPD) # 115200 / SPD /* * int sio_init(void) */ sio_init: movw $SIO_PRT+0x3,%dx # Data format reg movb $SIO_FMT|0x80,%al # Set format outb %al,(%dx) # and DLAB pushl %edx # Save subb $0x3,%dl # Divisor latch reg movw $SIO_DIV,%ax # Set outw %ax,(%dx) # BPS popl %edx # Restore movb $SIO_FMT,%al # Clear outb %al,(%dx) # DLAB incl %edx # Modem control reg movb $0x3,%al # Set RTS, outb %al,(%dx) # DTR incl %edx # Line status reg call sio_getc.1 # Get character /* * int sio_flush(void) */ sio_flush: xorl %eax,%eax # Return value xorl %ecx,%ecx # Timeout movb $0x80,%ch # counter sio_flush.1: call sio_ischar # Check for character jz sio_flush.2 # Till none loop sio_flush.1 # or counter is zero movb $1, %al # Exhausted all tries sio_flush.2: ret # To caller /* * void sio_putc(int c) */ sio_putc: movw $SIO_PRT+0x5,%dx # Line status reg xor %ecx,%ecx # Timeout movb $0x40,%ch # counter sio_putc.1: inb (%dx),%al # Transmitter testb $0x20,%al # buffer empty? loopz sio_putc.1 # No jz sio_putc.2 # If timeout movb 0x4(%esp,1),%al # Get character subb $0x5,%dl # Transmitter hold reg outb %al,(%dx) # Write character sio_putc.2: ret $0x4 # To caller /* * int sio_getc(void) */ sio_getc: call sio_ischar # Character available? jz sio_getc # No sio_getc.1: subb $0x5,%dl # Receiver buffer reg inb (%dx),%al # Read character ret # To caller /* * int sio_ischar(void) */ sio_ischar: movw $SIO_PRT+0x5,%dx # Line status register xorl %eax,%eax # Zero inb (%dx),%al # Received data andb $0x1,%al # ready? ret # To caller /* * Output character AL to the serial console. */ putchr: pusha # Save cmpb $10, %al # is it a newline? jne putchr.1 # no?, then leave push $13 # output a carriage call sio_putc # return first movb $10, %al # restore %al putchr.1: pushl %eax # Push the character # onto the stack call sio_putc # Output the character popa # Restore ret # To caller #else /* * Output character AL to the console. */ putchr: pusha # Save xorl %ecx,%ecx # Zero for loops movb $SCR_MAT,%ah # Mode/attribute movl $BDA_POS,%ebx # BDA pointer movw (%ebx),%dx # Cursor position movl $0xb8000,%edi # Regen buffer (color) cmpb %ah,BDA_SCR-BDA_POS(%ebx) # Mono mode? jne putchr.1 # No xorw %di,%di # Regen buffer (mono) putchr.1: cmpb $0xa,%al # New line? je putchr.2 # Yes xchgl %eax,%ecx # Save char movb $SCR_COL,%al # Columns per row mulb %dh # * row position addb %dl,%al # + column adcb $0x0,%ah # position shll %eax # * 2 xchgl %eax,%ecx # Swap char, offset movw %ax,(%edi,%ecx,1) # Write attr:char incl %edx # Bump cursor cmpb $SCR_COL,%dl # Beyond row? jb putchr.3 # No putchr.2: xorb %dl,%dl # Zero column incb %dh # Bump row putchr.3: cmpb $SCR_ROW,%dh # Beyond screen? jb putchr.4 # No leal 2*SCR_COL(%edi),%esi # New top line movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move rep # Scroll movsl # screen movb $0x20,%al # Space movb $SCR_COL,%cl # Columns to clear rep # Clear stosw # line movb $SCR_ROW-1,%dh # Bottom line putchr.4: movw %dx,(%ebx) # Update position popa # Restore ret # To caller #endif .code16 /* * Real Mode Hardware interrupt jump table. */ intr20: push $0x8 # Int 0x20: IRQ0 jmp int_hwr # V86 int 0x8 push $0x9 # Int 0x21: IRQ1 jmp int_hwr # V86 int 0x9 push $0xa # Int 0x22: IRQ2 jmp int_hwr # V86 int 0xa push $0xb # Int 0x23: IRQ3 jmp int_hwr # V86 int 0xb push $0xc # Int 0x24: IRQ4 jmp int_hwr # V86 int 0xc push $0xd # Int 0x25: IRQ5 jmp int_hwr # V86 int 0xd push $0xe # Int 0x26: IRQ6 jmp int_hwr # V86 int 0xe push $0xf # Int 0x27: IRQ7 jmp int_hwr # V86 int 0xf push $0x70 # Int 0x28: IRQ8 jmp int_hwr # V86 int 0x70 push $0x71 # Int 0x29: IRQ9 jmp int_hwr # V86 int 0x71 push $0x72 # Int 0x2a: IRQ10 jmp int_hwr # V86 int 0x72 push $0x73 # Int 0x2b: IRQ11 jmp int_hwr # V86 int 0x73 push $0x74 # Int 0x2c: IRQ12 jmp int_hwr # V86 int 0x74 push $0x75 # Int 0x2d: IRQ13 jmp int_hwr # V86 int 0x75 push $0x76 # Int 0x2e: IRQ14 jmp int_hwr # V86 int 0x76 push $0x77 # Int 0x2f: IRQ15 jmp int_hwr # V86 int 0x77 /* * Reflect hardware interrupts in real mode. */ int_hwr: push %ax # Save push %ds # Save push %bp # Save mov %sp,%bp # Address stack frame xchg %bx,6(%bp) # Swap BX, int no xor %ax,%ax # Set %ds:%bx to shl $2,%bx # point to mov %ax,%ds # IDT entry mov (%bx),%ax # Load IP mov 2(%bx),%bx # Load CS xchg %ax,4(%bp) # Swap saved %ax,%bx with xchg %bx,6(%bp) # CS:IP of handler pop %bp # Restore pop %ds # Restore lret # Jump to handler .p2align 4 /* * Global descriptor table. */ gdt: .word 0x0,0x0,0x0,0x0 # Null entry .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA .word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA .word 0xffff,MEM_USR,0xfa00,0xcf# SEL_UCODE .word 0xffff,MEM_USR,0xf200,0xcf# SEL_UDATA tss_desc: .word _TSSLM,MEM_TSS,0x8900,0x0 # SEL_TSS gdt.1: /* * Pseudo-descriptors. */ gdtdesc: .word gdt.1-gdt-1,gdt,0x0 # GDT idtdesc: .word _IDTLM,MEM_IDT,0x0 # IDT ivtdesc: .word 0x400-0x0-1,0x0,0x0 # IVT /* * IDT construction control string. */ idtctl: .byte 0x10, 0x8e # Int 0x0-0xf .word 0x7dfb,intx00 # (exceptions) .byte 0x10, 0x8e # Int 0x10 .word 0x1, intx10 # (exception) .byte 0x10, 0x8e # Int 0x20-0x2f .word 0xffff,intx20 # (hardware) .byte 0x1, 0xee # int 0x30 .word 0x1, intx30 # (system call) .byte 0x2, 0xee # Int 0x31-0x32 .word 0x1, intx31 # (V86, null) .byte 0x0 # End of string /* * Dump format string. */ dmpfmt: .byte '\n' # "\n" .ascii "int" # "int=" .byte 0x80|DMP_X32, 0x40 # "00000000 " .ascii "err" # "err=" .byte 0x80|DMP_X32, 0x44 # "00000000 " .ascii "efl" # "efl=" .byte 0x80|DMP_X32, 0x50 # "00000000 " .ascii "eip" # "eip=" .byte 0x80|DMP_X32|DMP_EOL,0x48 # "00000000\n" .ascii "eax" # "eax=" .byte 0x80|DMP_X32, 0x34 # "00000000 " .ascii "ebx" # "ebx=" .byte 0x80|DMP_X32, 0x28 # "00000000 " .ascii "ecx" # "ecx=" .byte 0x80|DMP_X32, 0x30 # "00000000 " .ascii "edx" # "edx=" .byte 0x80|DMP_X32|DMP_EOL,0x2c # "00000000\n" .ascii "esi" # "esi=" .byte 0x80|DMP_X32, 0x1c # "00000000 " .ascii "edi" # "edi=" .byte 0x80|DMP_X32, 0x18 # "00000000 " .ascii "ebp" # "ebp=" .byte 0x80|DMP_X32, 0x20 # "00000000 " .ascii "esp" # "esp=" .byte 0x80|DMP_X32|DMP_EOL,0x0 # "00000000\n" .ascii "cs" # "cs=" .byte 0x80|DMP_X16, 0x4c # "0000 " .ascii "ds" # "ds=" .byte 0x80|DMP_X16, 0xc # "0000 " .ascii "es" # "es=" .byte 0x80|DMP_X16, 0x8 # "0000 " .ascii " " # " " .ascii "fs" # "fs=" .byte 0x80|DMP_X16, 0x10 # "0000 " .ascii "gs" # "gs=" .byte 0x80|DMP_X16, 0x14 # "0000 " .ascii "ss" # "ss=" .byte 0x80|DMP_X16|DMP_EOL,0x4 # "0000\n" .ascii "cs:eip" # "cs:eip=" .byte 0x80|DMP_MEM|DMP_EOL,0x48 # "00 00 ... 00 00\n" .ascii "ss:esp" # "ss:esp=" .byte 0x80|DMP_MEM|DMP_EOL,0x0 # "00 00 ... 00 00\n" .asciz "BTX halted\n" # End /* * Bad VM86 call panic */ badvm86: .asciz "Invalid VM86 Request\n" /* * End of BTX memory. */ .p2align 4 break: