2 * Copyright (c) 1998 Robert Nordier
5 * Redistribution and use in source and binary forms are freely
6 * permitted provided that the above copyright notice and this
7 * paragraph and the following disclaimer are duplicated in all
10 * This software is provided "AS IS" and without any express or
11 * implied warranties, including, without limitation, the implied
12 * warranties of merchantability and fitness for a particular
23 .set MEM_BTX,0x1000 # Start of BTX memory
24 .set MEM_ESP0,0x1800 # Supervisor stack
25 .set MEM_BUF,0x1800 # Scratch buffer
26 .set MEM_ESPR,0x5e00 # Real mode stack
27 .set MEM_IDT,0x5e00 # IDT
28 .set MEM_TSS,0x5f98 # TSS
29 .set MEM_MAP,0x6000 # I/O bit map
30 .set MEM_TSS_END,0x7fff # End of TSS
31 .set MEM_ORG,0x9000 # BTX code
32 .set MEM_USR,0xa000 # Start of user memory
36 .set PAG_SIZ,0x1000 # Page size
37 .set PAG_CNT,0x1000 # Pages to map
41 .set PSL_RESERVED_DEFAULT,0x00000002
42 .set PSL_T,0x00000100 # Trap flag
43 .set PSL_I,0x00000200 # Interrupt enable flag
44 .set PSL_D,0x00000400 # String instruction direction
45 .set PSL_NT,0x00004000 # Nested task flag
46 .set PSL_VM,0x00020000 # Virtual 8086 mode flag
47 .set PSL_AC,0x00040000 # Alignment check flag
51 .set SEL_SCODE,0x8 # Supervisor code
52 .set SEL_SDATA,0x10 # Supervisor data
53 .set SEL_RCODE,0x18 # Real mode code
54 .set SEL_RDATA,0x20 # Real mode data
55 .set SEL_UCODE,0x28|3 # User code
56 .set SEL_UDATA,0x30|3 # User data
57 .set SEL_TSS,0x38 # TSS
59 * Task state segment fields.
61 .set TSS_ESP0,0x4 # PL 0 ESP
62 .set TSS_SS0,0x8 # PL 0 SS
63 .set TSS_MAP,0x66 # I/O bit map base
67 .set SYS_EXIT,0x0 # Exit
68 .set SYS_EXEC,0x1 # Exec
70 * Fields in V86 interface structure.
72 .set V86_CTL,0x0 # Control flags
73 .set V86_ADDR,0x4 # Int number/address
74 .set V86_ES,0x8 # V86 ES
75 .set V86_DS,0xc # V86 DS
76 .set V86_FS,0x10 # V86 FS
77 .set V86_GS,0x14 # V86 GS
81 .set V86F_ADDR,0x10000 # Segment:offset address
82 .set V86F_CALLF,0x20000 # Emulate far call
83 .set V86F_FLAGS,0x40000 # Return flags
85 * Dump format control bytes.
87 .set DMP_X16,0x1 # Word
88 .set DMP_X32,0x2 # Long
89 .set DMP_MEM,0x4 # Memory
90 .set DMP_EOL,0x8 # End of line
92 * Screen defaults and assumptions.
94 .set SCR_MAT,0x7 # Mode/attribute
95 .set SCR_COL,0x50 # Columns per row
96 .set SCR_ROW,0x19 # Rows per screen
98 * BIOS Data Area locations.
100 .set BDA_MEM,0x413 # Free memory
101 .set BDA_SCR,0x449 # Video mode
102 .set BDA_POS,0x450 # Cursor position
103 .set BDA_BOOT,0x472 # Boot howto flag
105 * Derivations, for brevity.
107 .set _ESP0H,MEM_ESP0>>0x8 # Byte 1 of ESP0
108 .set _TSSIO,MEM_MAP-MEM_TSS # TSS I/O base
109 .set _TSSLM,MEM_TSS_END-MEM_TSS # TSS limit
110 .set _IDTLM,MEM_TSS-MEM_IDT-1 # IDT limit
116 start: # Start of code
120 btx_hdr: .byte 0xeb # Machine ID
121 .byte 0xe # Header size
123 .byte 0x1 # Major version
124 .byte 0x2 # Minor version
125 .byte BTX_FLAGS # Flags
126 .word PAG_CNT-MEM_ORG>>0xc # Paging control
127 .word break-start # Text size
128 .long 0x0 # Entry address
130 * Initialization routine.
132 init: cli # Disable interrupts
133 xor %ax,%ax # Zero/segment
135 mov $MEM_ESP0,%sp # stack
136 mov %ax,%es # Address
143 mov $MEM_IDT,%di # Memory to initialize
144 mov $(MEM_ORG-MEM_IDT)/2,%cx # Words to zero
148 * Update real mode IDT for reflecting hardware interrupts.
150 mov $intr20,%bx # Address first handler
151 mov $0x10,%cx # Number of handlers
152 mov $0x20*4,%di # First real mode IDT entry
153 init.0: mov %bx,(%di) # Store IP
154 inc %di # Address next
157 add $4,%bx # Next handler
158 loop init.0 # Next IRQ
163 mov $idtctl,%si # Control string
164 init.1: lodsb # Get entry
166 xchg %ax,%cx # as word
167 jcxz init.4 # If done
169 xchg %ax,%dx # P:DPL:type
172 lodsw # Get handler offset
173 mov $SEL_SCODE,%dh # Segment selector
174 init.2: shr %bx # Handle this int?
176 mov %ax,(%di) # Set handler offset
177 mov %dh,0x2(%di) # and selector
178 mov %dl,0x5(%di) # Set P:DPL:type
179 add $0x4,%ax # Next handler
180 init.3: lea 0x8(%di),%di # Next entry
181 loop init.2 # Till set done
182 jmp init.1 # Continue
186 init.4: movb $_ESP0H,TSS_ESP0+1(%di) # Set ESP0
187 movb $SEL_SDATA,TSS_SS0(%di) # Set SS0
188 movb $_TSSIO,TSS_MAP(%di) # Set I/O bit map base
190 * Bring up the system.
192 mov $0x2820,%bx # Set protected mode
193 callw setpic # IRQ offsets
194 lidt idtdesc # Set IDT
195 lgdt gdtdesc # Set GDT
196 mov %cr0,%eax # Switch to protected
199 ljmp $SEL_SCODE,$init.8 # To 32-bit code
201 init.8: xorl %ecx,%ecx # Zero
202 movb $SEL_SDATA,%cl # To 32-bit
207 movb $SEL_TSS,%cl # Set task
209 movl $MEM_USR,%edx # User base address
210 movzwl %ss:BDA_MEM,%eax # Get free memory
211 shll $0xa,%eax # To bytes
212 subl $ARGSPACE,%eax # Less arg space
213 subl %edx,%eax # Less base
214 movb $SEL_UDATA,%cl # User data selector
217 push $0x202 # Set flags (IF set)
218 push $SEL_UCODE # Set CS
219 pushl btx_hdr+0xc # Set EIP
225 movb $0x7,%cl # Set remaining
226 init.9: push $0x0 # general
227 loop init.9 # registers
229 call sio_init # setup the serial console
231 popa # and initialize
232 popl %es # Initialize
240 exit: cli # Disable interrupts
241 movl $MEM_ESP0,%esp # Clear stack
245 movl %cr0,%eax # Get CR0
246 andl $~0x80000000,%eax # Disable
247 movl %eax,%cr0 # paging
248 xorl %ecx,%ecx # Zero
249 movl %ecx,%cr3 # Flush TLB
251 * Restore the GDT in case we caught a kernel trap.
253 lgdt %cs:gdtdesc # Set GDT
257 ljmpw $SEL_RCODE,$exit.1 # Reload CS
259 exit.1: mov $SEL_RDATA,%cl # 16-bit selector
260 mov %cx,%ss # Reload SS
262 mov %cx,%es # remaining
263 mov %cx,%fs # segment
264 mov %cx,%gs # registers
266 * To real-address mode.
269 mov %eax,%cr0 # real mode
270 ljmp $0x0,$exit.2 # Reload CS
271 exit.2: xor %ax,%ax # Real mode segment
272 mov %ax,%ss # Reload SS
273 mov %ax,%ds # Address data
274 mov $0x7008,%bx # Set real mode
275 callw setpic # IRQ offsets
276 lidt ivtdesc # Set IVT
278 * Reboot or await reset.
280 sti # Enable interrupts
281 testb $0x1,btx_hdr+0x7 # Reboot?
282 exit.3: jz exit.3 # No
283 movw $0x1234, BDA_BOOT # Do a warm boot
284 ljmp $0xf000,$0xfff0 # reboot the machine
286 * Set IRQ offsets by reprogramming 8259A PICs.
288 setpic: in $0x21,%al # Save master
290 in $0xa1,%al # Save slave
292 movb $0x11,%al # ICW1 to
293 outb %al,$0x20 # master,
294 outb %al,$0xa0 # slave
295 movb %bl,%al # ICW2 to
296 outb %al,$0x21 # master
297 movb %bh,%al # ICW2 to
298 outb %al,$0xa1 # slave
299 movb $0x4,%al # ICW3 to
300 outb %al,$0x21 # master
301 movb $0x2,%al # ICW3 to
302 outb %al,$0xa1 # slave
303 movb $0x1,%al # ICW4 to
304 outb %al,$0x21 # master,
305 outb %al,$0xa1 # slave
306 pop %ax # Restore slave
308 pop %ax # Restore master
313 * Exception jump table.
315 intx00: push $0x0 # Int 0x0: #DE
316 jmp ex_noc # Divide error
317 push $0x1 # Int 0x1: #DB
319 push $0x3 # Int 0x3: #BP
320 jmp ex_noc # Breakpoint
321 push $0x4 # Int 0x4: #OF
322 jmp ex_noc # Overflow
323 push $0x5 # Int 0x5: #BR
324 jmp ex_noc # BOUND range exceeded
325 push $0x6 # Int 0x6: #UD
326 jmp ex_noc # Invalid opcode
327 push $0x7 # Int 0x7: #NM
328 jmp ex_noc # Device not available
329 push $0x8 # Int 0x8: #DF
330 jmp except # Double fault
331 push $0xa # Int 0xa: #TS
332 jmp except # Invalid TSS
333 push $0xb # Int 0xb: #NP
334 jmp except # Segment not present
335 push $0xc # Int 0xc: #SS
336 jmp except # Stack segment fault
337 push $0xd # Int 0xd: #GP
338 jmp except # General protection
339 push $0xe # Int 0xe: #PF
340 jmp except # Page fault
341 intx10: push $0x10 # Int 0x10: #MF
342 jmp ex_noc # Floating-point error
344 * Save a zero error code.
346 ex_noc: pushl (%esp,1) # Duplicate int no
347 movb $0x0,0x4(%esp,1) # Fake error code
351 except: cld # String ops inc
359 cmpw $SEL_SCODE,0x44(%esp,1) # Supervisor mode?
362 jmp except.2 # Join common code
363 except.1: pushl 0x50(%esp,1) # Set SS
364 except.2: pushl 0x50(%esp,1) # Set ESP
365 push $SEL_SDATA # Set up
369 movl %esp,%ebx # Stack frame
370 movl $dmpfmt,%esi # Dump format string
371 movl $MEM_BUF,%edi # Buffer
375 call putstr # display
376 leal 0x18(%esp,1),%esp # Discard frame
380 cmpb $0x3,(%esp,1) # Breakpoint?
382 cmpb $0x1,(%esp,1) # Debug?
384 testl $PSL_T,0x10(%esp,1) # Trap flag set?
386 except.2a: jmp exit # Exit
387 except.3: leal 0x8(%esp,1),%esp # Discard err, int no
388 iret # From interrupt
391 * Reboot the machine by setting the reboot flag and exiting
393 reboot: orb $0x1,btx_hdr+0x7 # Set the reboot flag
394 jmp exit # Terminate BTX and reboot
397 * Protected Mode Hardware interrupt jump table.
399 intx20: push $0x8 # Int 0x20: IRQ0
400 jmp int_hw # V86 int 0x8
401 push $0x9 # Int 0x21: IRQ1
402 jmp int_hw # V86 int 0x9
403 push $0xa # Int 0x22: IRQ2
404 jmp int_hw # V86 int 0xa
405 push $0xb # Int 0x23: IRQ3
406 jmp int_hw # V86 int 0xb
407 push $0xc # Int 0x24: IRQ4
408 jmp int_hw # V86 int 0xc
409 push $0xd # Int 0x25: IRQ5
410 jmp int_hw # V86 int 0xd
411 push $0xe # Int 0x26: IRQ6
412 jmp int_hw # V86 int 0xe
413 push $0xf # Int 0x27: IRQ7
414 jmp int_hw # V86 int 0xf
415 push $0x70 # Int 0x28: IRQ8
416 jmp int_hw # V86 int 0x70
417 push $0x71 # Int 0x29: IRQ9
418 jmp int_hw # V86 int 0x71
419 push $0x72 # Int 0x2a: IRQ10
420 jmp int_hw # V86 int 0x72
421 push $0x73 # Int 0x2b: IRQ11
422 jmp int_hw # V86 int 0x73
423 push $0x74 # Int 0x2c: IRQ12
424 jmp int_hw # V86 int 0x74
425 push $0x75 # Int 0x2d: IRQ13
426 jmp int_hw # V86 int 0x75
427 push $0x76 # Int 0x2e: IRQ14
428 jmp int_hw # V86 int 0x76
429 push $0x77 # Int 0x2f: IRQ15
430 jmp int_hw # V86 int 0x77
433 * Invoke real mode interrupt/function call from user mode with arguments.
435 intx31: pushl $-1 # Dummy int no for btx_v86
437 * Invoke real mode interrupt/function call from protected mode.
439 * We place a trampoline on the user stack that will return to rret_tramp
440 * which will reenter protected mode and then finally return to the user
443 * Kernel frame %esi points to: Real mode stack frame at MEM_ESPR:
445 * -0x00 user %ss -0x04 kernel %esp (with full frame)
446 * -0x04 user %esp -0x08 btx_v86 pointer
447 * -0x08 user %eflags -0x0c flags (only used if interrupt)
448 * -0x0c user %cs -0x10 real mode CS:IP return trampoline
449 * -0x10 user %eip -0x12 real mode flags
450 * -0x14 int no -0x16 real mode CS:IP (target)
463 * -0x48 zero %eax (hardware int only)
464 * -0x4c zero %ecx (hardware int only)
465 * -0x50 zero %edx (hardware int only)
466 * -0x54 zero %ebx (hardware int only)
467 * -0x58 zero %esp (hardware int only)
468 * -0x5c zero %ebp (hardware int only)
469 * -0x60 zero %esi (hardware int only)
470 * -0x64 zero %edi (hardware int only)
471 * -0x68 zero %gs (hardware int only)
472 * -0x6c zero %fs (hardware int only)
473 * -0x70 zero %ds (hardware int only)
474 * -0x74 zero %es (hardware int only)
476 int_hw: cld # String ops inc
482 push $SEL_SDATA # Set up
486 leal 0x44(%esp,1),%esi # Base of frame
487 movl %esp,MEM_ESPR-0x04 # Save kernel stack pointer
488 movl -0x14(%esi),%eax # Get Int no
489 cmpl $-1,%eax # Hardware interrupt?
492 * v86 calls save the btx_v86 pointer on the real mode stack and read
493 * the address and flags from the btx_v86 structure. For interrupt
494 * handler invocations (VM86 INTx requests), disable interrupts,
495 * tracing, and alignment checking while the handler runs.
497 movl $MEM_USR,%ebx # User base
498 movl %ebx,%edx # address
499 addl -0x4(%esi),%ebx # User ESP
500 movl (%ebx),%ebp # btx_v86 pointer
501 addl %ebp,%edx # Flatten btx_v86 ptr
502 movl %edx,MEM_ESPR-0x08 # Save btx_v86 ptr
503 movl V86_ADDR(%edx),%eax # Get int no/address
504 movl V86_CTL(%edx),%edx # Get control flags
505 movl -0x08(%esi),%ebx # Save user flags in %ebx
506 testl $V86F_ADDR,%edx # Segment:offset?
508 andl $~(PSL_I|PSL_T|PSL_AC),%ebx # Disable interrupts, tracing,
509 # and alignment checking for
511 jmp intusr.3 # Skip hardware interrupt
513 * Hardware interrupts store a NULL btx_v86 pointer and use the
514 * address (interrupt number) from the stack with empty flags. Also,
515 * push a dummy frame of zeros onto the stack for all the general
516 * purpose and segment registers and clear %eflags. This gives the
517 * hardware interrupt handler a clean slate.
519 intusr.1: xorl %edx,%edx # Control flags
520 movl %edx,MEM_ESPR-0x08 # NULL btx_v86 ptr
521 movl $12,%ecx # Frame is 12 dwords
522 intusr.2: pushl $0x0 # Fill frame
523 loop intusr.2 # with zeros
524 movl $PSL_RESERVED_DEFAULT,%ebx # Set clean %eflags
526 * Look up real mode IDT entry for hardware interrupts and VM86 INTx
529 intusr.3: shll $0x2,%eax # Scale
530 movl (%eax),%eax # Load int vector
531 jmp intusr.5 # Skip CALLF test
533 * Panic if V86F_CALLF isn't set with V86F_ADDR.
535 intusr.4: testl $V86F_CALLF,%edx # Far call?
537 movl %edx,0x30(%esp,1) # Place VM86 flags in int no
538 movl $badvm86,%esi # Display bad
539 call putstr # VM86 call
544 popal # Restore gp regs
547 * %eax now holds the segment:offset of the function.
548 * %ebx now holds the %eflags to pass to real mode.
549 * %edx now holds the V86F_* flags.
551 intusr.5: movw %bx,MEM_ESPR-0x12 # Pass user flags to real mode
554 * If this is a v86 call, copy the seg regs out of the btx_v86 structure.
556 movl MEM_ESPR-0x08,%ecx # Get btx_v86 ptr
557 jecxz intusr.6 # Skip for hardware ints
558 leal -0x44(%esi),%edi # %edi => kernel stack seg regs
560 leal V86_ES(%ecx),%esi # %esi => btx_v86 seg regs
561 movl $4,%ecx # Copy seg regs
563 movsl # to kernel stack
565 intusr.6: movl -0x08(%esi),%ebx # Copy user flags to real
566 movl %ebx,MEM_ESPR-0x0c # mode return trampoline
567 movl $rret_tramp,%ebx # Set return trampoline
568 movl %ebx,MEM_ESPR-0x10 # CS:IP
569 movl %eax,MEM_ESPR-0x16 # Real mode target CS:IP
570 ljmpw $SEL_RCODE,$intusr.7 # Change to 16-bit segment
572 intusr.7: movl %cr0,%eax # Leave
574 movl %eax,%cr0 # mode
576 intusr.8: xorw %ax,%ax # Reset %ds
579 lidt ivtdesc # Set IVT
584 popal # Restore gp regs
585 movw $MEM_ESPR-0x16,%sp # Switch to real mode stack
586 iret # Call target routine
588 * For the return to real mode we setup a stack frame like this on the real
589 * mode stack. Note that callf calls won't pop off the flags, but we just
590 * ignore that by repositioning %sp to be just above the btx_v86 pointer
591 * so it is aligned. The stack is relative to MEM_ESPR.
609 rret_tramp: movw $MEM_ESPR-0x08,%sp # Reset stack pointer
610 pushal # Save gp regs
615 pushfl # Save %eflags
616 pushl $PSL_RESERVED_DEFAULT|PSL_D # Use clean %eflags with
617 popfl # string ops dec
618 xorw %ax,%ax # Reset seg
620 movw %ax,%es # (%ss is already 0)
621 lidt idtdesc # Set IDT
622 lgdt gdtdesc # Set GDT
623 mov %cr0,%eax # Switch to protected
626 ljmp $SEL_SCODE,$rret_tramp.1 # To 32-bit code
628 rret_tramp.1: xorl %ecx,%ecx # Zero
629 movb $SEL_SDATA,%cl # Setup
630 movw %cx,%ss # 32-bit
633 movl MEM_ESPR-0x04,%esp # Switch to kernel stack
634 leal 0x44(%esp,1),%esi # Base of frame
635 andb $~0x2,tss_desc+0x5 # Clear TSS busy
636 movb $SEL_TSS,%cl # Set task
639 * Now we are back in protected mode. The kernel stack frame set up
640 * before entering real mode is still intact. For hardware interrupts,
641 * leave the frame unchanged.
643 cmpl $0,MEM_ESPR-0x08 # Leave saved regs unchanged
644 jz rret_tramp.3 # for hardware ints
646 * For V86 calls, copy the registers off of the real mode stack onto
647 * the kernel stack as we want their updated values. Also, initialize
648 * the segment registers on the kernel stack.
650 * Note that the %esp in the kernel stack after this is garbage, but popa
651 * ignores it, so we don't have to fix it up.
653 leal -0x18(%esi),%edi # Kernel stack GP regs
655 movl $MEM_ESPR-0x0c,%esi # Real mode stack GP regs
656 movl $8,%ecx # Copy GP regs from
657 rep # real mode stack
658 movsl # to kernel stack
659 movl $SEL_UDATA,%eax # Selector for data seg regs
660 movl $4,%ecx # Initialize %ds,
664 * For V86 calls, copy the saved seg regs on the real mode stack back
665 * over to the btx_v86 structure. Also, conditionally update the
666 * saved eflags on the kernel stack based on the flags from the user.
668 movl MEM_ESPR-0x08,%ecx # Get btx_v86 ptr
669 leal V86_GS(%ecx),%edi # %edi => btx_v86 seg regs
670 leal MEM_ESPR-0x2c,%esi # %esi => real mode seg regs
671 xchgl %ecx,%edx # Save btx_v86 ptr
672 movl $4,%ecx # Copy seg regs
673 rep # from real mode stack
676 movl V86_CTL(%edx),%edx # Read V86 control flags
677 testl $V86F_FLAGS,%edx # User wants flags?
679 movl MEM_ESPR-0x3c,%eax # Read real mode flags
680 andl $~(PSL_T|PSL_NT),%eax # Clear unsafe flags
681 movw %ax,-0x08(%esi) # Update user flags (low 16)
683 * Return to the user task
685 rret_tramp.3: popl %es # Restore
689 popal # Restore gp regs
690 addl $4,%esp # Discard int no
691 iret # Return to user mode
696 intx30: cmpl $SYS_EXEC,%eax # Exec system call?
706 movl $MEM_USR,%eax # User base address
707 addl 0xc(%esp,1),%eax # Change to user
708 leal 0x4(%eax),%esp # stack
711 intx30.1: orb $0x1,%ss:btx_hdr+0x7 # Flag reboot
714 * Dump structure [EBX] to [EDI], using format string [ESI].
716 dump.0: stosb # Save char
717 dump: lodsb # Load char
718 testb %al,%al # End of string?
720 testb $0x80,%al # Control?
722 movb %al,%ch # Save control
723 movb $'=',%al # Append
728 addl %ebx,%esi # pointer
729 testb $DMP_X16,%ch # Dump word?
733 dump.1: testb $DMP_X32,%ch # Dump long?
737 dump.2: testb $DMP_MEM,%ch # Dump memory?
740 testl $PSL_VM,0x50(%ebx) # V86 mode?
742 verr 0x4(%esi) # Readable selector?
744 ldsl (%esi),%esi # Load pointer
745 jmp dump.4 # Join common code
746 dump.3: lodsl # Set offset
747 xchgl %eax,%edx # Save
749 shll $0x4,%eax # * 0x10
750 addl %edx,%eax # + offset
751 xchgl %eax,%esi # Set pointer
752 dump.4: movb $2,%dl # Num lines
753 dump.4a: movb $0x10,%cl # Bytes to dump
754 dump.5: lodsb # Get byte and
756 decb %cl # Keep count
758 movb $'-',%al # Separator
759 cmpb $0x8,%cl # Half way?
761 movb $' ',%al # Use space
762 dump.6: stosb # Save separator
763 jmp dump.5 # Continue
764 dump.6a: decb %dl # Keep count
766 movb $0xa,%al # Line feed
768 movb $7,%cl # Leading
769 movb $' ',%al # spaces
770 dump.6b: stosb # Dump
773 jmp dump.4a # Next line
774 dump.7: popl %ds # Restore
775 dump.8: popl %esi # Restore
776 movb $0xa,%al # Line feed
777 testb $DMP_EOL,%ch # End of line?
779 movb $' ',%al # Use spaces
781 dump.9: jmp dump.0 # Continue
782 dump.10: stosb # Terminate string
785 * Convert EAX, AX, or AL to hex, saving the result to [EDI].
787 hex32: pushl %eax # Save
788 shrl $0x10,%eax # Do upper
791 hex16: call hex16.1 # Do upper 8
792 hex16.1: xchgb %ah,%al # Save/restore
793 hex8: pushl %eax # Save
794 shrb $0x4,%al # Do upper
797 hex8.1: andb $0xf,%al # Get lower 4
798 cmpb $0xa,%al # Convert
799 sbbb $0x69,%al # to hex
801 orb $0x20,%al # To lower case
805 * Output zero-terminated string [ESI] to the console.
807 putstr.0: call putchr # Output char
808 putstr: lodsb # Load char
809 testb %al,%al # End of string?
813 .set SIO_PRT,SIOPRT # Base port
814 .set SIO_FMT,SIOFMT # 8N1
815 .set SIO_DIV,(115200/SIOSPD) # 115200 / SPD
820 sio_init: movw $SIO_PRT+0x3,%dx # Data format reg
821 movb $SIO_FMT|0x80,%al # Set format
822 outb %al,(%dx) # and DLAB
824 subb $0x3,%dl # Divisor latch reg
825 movw $SIO_DIV,%ax # Set
828 movb $SIO_FMT,%al # Clear
829 outb %al,(%dx) # DLAB
830 incl %edx # Modem control reg
831 movb $0x3,%al # Set RTS,
833 incl %edx # Line status reg
834 call sio_getc.1 # Get character
837 * int sio_flush(void)
839 sio_flush: xorl %eax,%eax # Return value
840 xorl %ecx,%ecx # Timeout
841 movb $0x80,%ch # counter
842 sio_flush.1: call sio_ischar # Check for character
843 jz sio_flush.2 # Till none
844 loop sio_flush.1 # or counter is zero
845 movb $1, %al # Exhausted all tries
846 sio_flush.2: ret # To caller
849 * void sio_putc(int c)
851 sio_putc: movw $SIO_PRT+0x5,%dx # Line status reg
852 xor %ecx,%ecx # Timeout
853 movb $0x40,%ch # counter
854 sio_putc.1: inb (%dx),%al # Transmitter
855 testb $0x20,%al # buffer empty?
856 loopz sio_putc.1 # No
857 jz sio_putc.2 # If timeout
858 movb 0x4(%esp,1),%al # Get character
859 subb $0x5,%dl # Transmitter hold reg
860 outb %al,(%dx) # Write character
861 sio_putc.2: ret $0x4 # To caller
866 sio_getc: call sio_ischar # Character available?
868 sio_getc.1: subb $0x5,%dl # Receiver buffer reg
869 inb (%dx),%al # Read character
873 * int sio_ischar(void)
875 sio_ischar: movw $SIO_PRT+0x5,%dx # Line status register
876 xorl %eax,%eax # Zero
877 inb (%dx),%al # Received data
878 andb $0x1,%al # ready?
882 * Output character AL to the serial console.
885 cmpb $10, %al # is it a newline?
886 jne putchr.1 # no?, then leave
887 push $13 # output a carriage
888 call sio_putc # return first
889 movb $10, %al # restore %al
890 putchr.1: pushl %eax # Push the character
892 call sio_putc # Output the character
897 * Output character AL to the console.
900 xorl %ecx,%ecx # Zero for loops
901 movb $SCR_MAT,%ah # Mode/attribute
902 movl $BDA_POS,%ebx # BDA pointer
903 movw (%ebx),%dx # Cursor position
904 movl $0xb8000,%edi # Regen buffer (color)
905 cmpb %ah,BDA_SCR-BDA_POS(%ebx) # Mono mode?
907 xorw %di,%di # Regen buffer (mono)
908 putchr.1: cmpb $0xa,%al # New line?
910 xchgl %eax,%ecx # Save char
911 movb $SCR_COL,%al # Columns per row
912 mulb %dh # * row position
913 addb %dl,%al # + column
914 adcb $0x0,%ah # position
916 xchgl %eax,%ecx # Swap char, offset
917 movw %ax,(%edi,%ecx,1) # Write attr:char
918 incl %edx # Bump cursor
919 cmpb $SCR_COL,%dl # Beyond row?
921 putchr.2: xorb %dl,%dl # Zero column
923 putchr.3: cmpb $SCR_ROW,%dh # Beyond screen?
925 leal 2*SCR_COL(%edi),%esi # New top line
926 movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move
929 movb $0x20,%al # Space
930 movb $SCR_COL,%cl # Columns to clear
933 movb $SCR_ROW-1,%dh # Bottom line
934 putchr.4: movw %dx,(%ebx) # Update position
941 * Real Mode Hardware interrupt jump table.
943 intr20: push $0x8 # Int 0x20: IRQ0
944 jmp int_hwr # V86 int 0x8
945 push $0x9 # Int 0x21: IRQ1
946 jmp int_hwr # V86 int 0x9
947 push $0xa # Int 0x22: IRQ2
948 jmp int_hwr # V86 int 0xa
949 push $0xb # Int 0x23: IRQ3
950 jmp int_hwr # V86 int 0xb
951 push $0xc # Int 0x24: IRQ4
952 jmp int_hwr # V86 int 0xc
953 push $0xd # Int 0x25: IRQ5
954 jmp int_hwr # V86 int 0xd
955 push $0xe # Int 0x26: IRQ6
956 jmp int_hwr # V86 int 0xe
957 push $0xf # Int 0x27: IRQ7
958 jmp int_hwr # V86 int 0xf
959 push $0x70 # Int 0x28: IRQ8
960 jmp int_hwr # V86 int 0x70
961 push $0x71 # Int 0x29: IRQ9
962 jmp int_hwr # V86 int 0x71
963 push $0x72 # Int 0x2a: IRQ10
964 jmp int_hwr # V86 int 0x72
965 push $0x73 # Int 0x2b: IRQ11
966 jmp int_hwr # V86 int 0x73
967 push $0x74 # Int 0x2c: IRQ12
968 jmp int_hwr # V86 int 0x74
969 push $0x75 # Int 0x2d: IRQ13
970 jmp int_hwr # V86 int 0x75
971 push $0x76 # Int 0x2e: IRQ14
972 jmp int_hwr # V86 int 0x76
973 push $0x77 # Int 0x2f: IRQ15
974 jmp int_hwr # V86 int 0x77
976 * Reflect hardware interrupts in real mode.
978 int_hwr: push %ax # Save
981 mov %sp,%bp # Address stack frame
982 xchg %bx,6(%bp) # Swap BX, int no
983 xor %ax,%ax # Set %ds:%bx to
984 shl $2,%bx # point to
985 mov %ax,%ds # IDT entry
986 mov (%bx),%ax # Load IP
987 mov 2(%bx),%bx # Load CS
988 xchg %ax,4(%bp) # Swap saved %ax,%bx with
989 xchg %bx,6(%bp) # CS:IP of handler
992 lret # Jump to handler
996 * Global descriptor table.
998 gdt: .word 0x0,0x0,0x0,0x0 # Null entry
999 .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE
1000 .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA
1001 .word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE
1002 .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
1003 .word 0xffff,MEM_USR,0xfa00,0xcf# SEL_UCODE
1004 .word 0xffff,MEM_USR,0xf200,0xcf# SEL_UDATA
1005 tss_desc: .word _TSSLM,MEM_TSS,0x8900,0x0 # SEL_TSS
1008 * Pseudo-descriptors.
1010 gdtdesc: .word gdt.1-gdt-1,gdt,0x0 # GDT
1011 idtdesc: .word _IDTLM,MEM_IDT,0x0 # IDT
1012 ivtdesc: .word 0x400-0x0-1,0x0,0x0 # IVT
1014 * IDT construction control string.
1016 idtctl: .byte 0x10, 0x8e # Int 0x0-0xf
1017 .word 0x7dfb,intx00 # (exceptions)
1018 .byte 0x10, 0x8e # Int 0x10
1019 .word 0x1, intx10 # (exception)
1020 .byte 0x10, 0x8e # Int 0x20-0x2f
1021 .word 0xffff,intx20 # (hardware)
1022 .byte 0x1, 0xee # int 0x30
1023 .word 0x1, intx30 # (system call)
1024 .byte 0x2, 0xee # Int 0x31-0x32
1025 .word 0x1, intx31 # (V86, null)
1026 .byte 0x0 # End of string
1028 * Dump format string.
1030 dmpfmt: .byte '\n' # "\n"
1031 .ascii "int" # "int="
1032 .byte 0x80|DMP_X32, 0x40 # "00000000 "
1033 .ascii "err" # "err="
1034 .byte 0x80|DMP_X32, 0x44 # "00000000 "
1035 .ascii "efl" # "efl="
1036 .byte 0x80|DMP_X32, 0x50 # "00000000 "
1037 .ascii "eip" # "eip="
1038 .byte 0x80|DMP_X32|DMP_EOL,0x48 # "00000000\n"
1039 .ascii "eax" # "eax="
1040 .byte 0x80|DMP_X32, 0x34 # "00000000 "
1041 .ascii "ebx" # "ebx="
1042 .byte 0x80|DMP_X32, 0x28 # "00000000 "
1043 .ascii "ecx" # "ecx="
1044 .byte 0x80|DMP_X32, 0x30 # "00000000 "
1045 .ascii "edx" # "edx="
1046 .byte 0x80|DMP_X32|DMP_EOL,0x2c # "00000000\n"
1047 .ascii "esi" # "esi="
1048 .byte 0x80|DMP_X32, 0x1c # "00000000 "
1049 .ascii "edi" # "edi="
1050 .byte 0x80|DMP_X32, 0x18 # "00000000 "
1051 .ascii "ebp" # "ebp="
1052 .byte 0x80|DMP_X32, 0x20 # "00000000 "
1053 .ascii "esp" # "esp="
1054 .byte 0x80|DMP_X32|DMP_EOL,0x0 # "00000000\n"
1056 .byte 0x80|DMP_X16, 0x4c # "0000 "
1058 .byte 0x80|DMP_X16, 0xc # "0000 "
1060 .byte 0x80|DMP_X16, 0x8 # "0000 "
1063 .byte 0x80|DMP_X16, 0x10 # "0000 "
1065 .byte 0x80|DMP_X16, 0x14 # "0000 "
1067 .byte 0x80|DMP_X16|DMP_EOL,0x4 # "0000\n"
1068 .ascii "cs:eip" # "cs:eip="
1069 .byte 0x80|DMP_MEM|DMP_EOL,0x48 # "00 00 ... 00 00\n"
1070 .ascii "ss:esp" # "ss:esp="
1071 .byte 0x80|DMP_MEM|DMP_EOL,0x0 # "00 00 ... 00 00\n"
1072 .asciz "BTX halted\n" # End
1074 * Bad VM86 call panic
1076 badvm86: .asciz "Invalid VM86 Request\n"
1079 * End of BTX memory.