📄 startmenu.s
字号:
/* * Converted to use Eric Biederman's _real_call routine which frees * the 16 bit code from running in the same 64kB segment as the * 32 bit code's stack. *//* #defines because ljmp wants a number, probably gas bug *//* .equ KERN_CODE_SEG,_pmcs-_gdt */#define KERN_CODE_SEG 0x08 .equ KERN_DATA_SEG,_pmds-_gdt/* .equ REAL_CODE_SEG,_rmcs-_gdt */#define REAL_CODE_SEG 0x18 .equ REAL_DATA_SEG,_rmds-_gdt .equ CR0_PE,1#ifdef GAS291#define DATA32 data32;#define ADDR32 addr32;#define LJMPI(x) ljmp x#else#define DATA32 data32#define ADDR32 addr32/* newer GAS295 require #define LJMPI(x) ljmp *x */#define LJMPI(x) ljmp x#endif#define DO_REAL_CALL pushl $10f; pushl $20f-10f; call _real_call; .section ".text16"; 10: .code16#define DO_REAL_RETURN ret; 20: .code32; .previous .section ".text" .section ".text16","ax",@progbits .previous .code32 .arch i386/* * NOTE: if you write a subroutine that is called from C code (gcc/egcs), * then you only have to take care of %ebx, %esi, %edi and %ebp. These * registers must not be altered under any circumstance. All other registers * may be clobbered without any negative side effects. If you don't follow * this rule then you'll run into strange effects that only occur on some * gcc versions (because the register allocator may use different registers). * * All the data32 prefixes for the ljmp instructions are necessary, because * the assembler emits code with a relocation address of 0. This means that * all destinations are initially negative, which the assembler doesn't grok, * because for some reason negative numbers don't fit into 16 bits. The addr32 * prefixes are there for the same reasons, because otherwise the memory * references are only 16 bit wide. Theoretically they are all superfluous. *//**************************************************************************START - Where all the fun begins....**************************************************************************//* this must be the first thing in the file because we enter from the top */ .global _start_start:/* We have to use our own GDT when running in our segment because the old GDT will have the wrong descriptors for the real code segments */ sgdt gdtsave /* save old GDT */ lgdt gdtarg /* load ours */ /* reload the segment registers */ movl $KERN_DATA_SEG,%eax movl %eax,%ds movl %eax,%es movl %eax,%ss movl %eax,%fs movl %eax,%gs /* flush prefetch queue, and reload %cs:%eip */ ljmp $KERN_CODE_SEG,$1f1: /* save the stack pointer and call the routine */ movl %esp,%eax movl %eax,initsp movl $RELOC+0x20000,%esp /* change stack */ pushl 12(%eax) /* replicate args on new stack */ pushl 8(%eax) pushl 4(%eax) call menu_exit:/* we reset sp to the location just before entering first instead of relying on the return from menu because exit could have been called from anywhere */ movl initsp,%ebx movl %ebx,%esp lgdt gdtsave /* restore old GDT */ ret .globl exitexit: movl 4(%esp),%eax jmp _exit/**************************************************************************SET_SEG_BASE - Set the base address of a segment registerStolen from Etherboot 5.1. With thanks to Eric Biederman**************************************************************************/ /* .globl set_seg_base */set_seg_base: /* Low half of the gdt base */ movl 4(%esp), %eax shll $16, %eax /* High half of the gdt base */ movl 4(%esp), %ecx shrl $16, %ecx andl $0xff, %ecx movl 4(%esp), %edx andl $0xff000000, %edx orl %edx, %ecx movl 8(%esp), %edx /* Fixup the code segment */ andl $0x0000ffff, 0(%edx) orl %eax , 0(%edx) andl $0x00ffff00, 4(%edx) orl %ecx , 4(%edx) /* Fixup the data segment */ andl $0x0000ffff, 8(%edx) orl %eax , 8(%edx) andl $0x00ffff00, 12(%edx) orl %ecx , 12(%edx) ret/**************************************************************************_REAL_CALL - Run some code in real mode.Stolen from Etherboot 5.1. With thanks to Eric Biederman**************************************************************************/ /* MAX_REAL_MODE_STACK is carefully tuned to work * with the stack bottom at 0x7c00 while not chancing * overwriting data below 0x500. */#define MAX_REAL_MODE_STACK 29696#define RADDR(sym) (((sym) - _end16) + MAX_REAL_MODE_STACK) .balign 4 /* .globl real_mode_stack */real_mode_stack: .long 0x7c00 /* Put the stack just below the dos load address */real_stack_top: .long 0_save_esp: .long 0 /* .globl _real_call */_real_call: /* Save the original %esp value */ movl %esp, _save_esp /* Save the temporary registers I use */ pushl $0 pushl %ebx pushl %ecx pushl %edx pushl %esi pushl %edi pushl %ebp /* Load up the registers */ movl 32(%esp), %ecx /* The 16bit code len */ movl 36(%esp), %esi /* The 16bit code start */ movl virt_offset, %ebp /* The virtual offset */ /* stack top = phys_to_virt(real_mode_stack - MAX_REAL_MODE_STACK) */ movl real_mode_stack, %ebx /* The stack top */ subl $MAX_REAL_MODE_STACK, %ebx movl %ebx, real_stack_top subl %ebp, %ebx /* Save the real mode stack top */ movl %ebx, 24(%esp) /* Compute where the copied code goes */ leal RADDR(__real_call)(%ebx), %edi subl %ecx, %edi andl $0xfffffffc, %edi /* 4 byte aligned */ /* Remember where the code is executed */ movl %edi, %eax subl %ebx, %eax movw %ax, real_ip /* Copy the user code onto the real mode stack */ rep movsb /* Copy the trampoline onto the stack */ movl $__real_call, %esi movl $_end16 - __real_call, %ecx leal RADDR(__real_call)(%ebx), %edi rep movsb /* Fixup real_gdtarg */ leal _gdt(%ebp), %eax movl %eax, RADDR(real_gdtarg +2)(%ebx) /* Fixup the gdt */ pushl $_rmcs leal 0(%ebx, %ebp), %eax pushl %eax call set_seg_base addl $8, %esp /* Restore the saved registers */ popl %ebp popl %edi popl %esi popl %edx popl %ecx popl %ebx /* And switch stacks */ popl %esp movzwl RADDR(real_ip)(%esp), %eax addl %eax, %esp /* Setup for jump to real mode */ movl real_stack_top, %eax shrl $4, %eax pushw %ax pushw $RADDR(real16) /* Switch stack from %esp 32bit virtual to %sp 16bit physical */ addl virt_offset, %esp subl real_stack_top, %esp /* Jump to 16bit code */ ljmp $REAL_CODE_SEG, $RADDR(code16) /* jump to a 16 bit segment */_real_call_ret: /* reload segment registers */ movl $KERN_DATA_SEG,%eax movl %eax,%ds movl %eax,%es movl %eax,%ss movl %eax,%fs movl %eax,%gs /* Restore the stack */ movl _save_esp, %esp /* Restore the direction flag */ cld /* Get the real mode stack pointer */ movl real_stack_top, %eax subl virt_offset, %eax pushl %eax movzwl RADDR(real_sp)(%eax), %eax addl 0(%esp), %eax addl $4, %esp /* Return to my caller */ ret $8 .balign 16__real_call:real_sp: .word 0real_ip: .word 0real_gdtarg: .word _gdt_end - _gdt - 1 /* limit */ .long _gdt /* addr */ .code16code16: /* Load 16bit segment descriptors to force 16bit segment limit */ movw $REAL_DATA_SEG, %ax movw %ax,%ds movw %ax,%ss movw %ax,%es movw %ax,%fs movw %ax,%gs /* clear the PE bit of CR0 */ movl %cr0,%eax andb $0!CR0_PE,%al movl %eax,%cr0 /* make intersegment jmp to flush the processor pipeline * and reload %cs:%eip (to clear upper 16 bits of %eip). */ lretreal16: /* we are in real mode now * set up the real mode segment registers : %ds, $ss, %es */ movw %cs, %ax movw %ax, %ss movw %ax, %ds movw %ax, %fs movw %ax, %gs /* Enable interrupts */ sti /* Call the user supplied code */ call *RADDR(real_ip) /* Save the stack pointer */ /* Reload %ds */ movw %cs, %ax movw %ax, %ds movw %sp, RADDR(real_sp) /* Disable interrupts */ cli /* Switch back to protected mode */ cs DATA32 lgdt RADDR(real_gdtarg) movl %cr0, %eax orb $CR0_PE, %al movl %eax, %cr0 /* turn on protected mode */ /* flush prefetch queue, and reload %cs:%eip */ DATA32 ljmp $KERN_CODE_SEG, $_real_call_ret .code32__end16: .balign 16_end16: .code32/**************************************************************************CURRTICKS - Get TimeUse direct memory access to BIOS variables, longword 0040:006C (tickstoday) and byte 0040:0070 (midnight crossover flag) instead of callingtimeofday BIOS interrupt.**************************************************************************/ .globl currtickscurrticks: pushl %ebp pushl %ebx pushl %esi pushl %edi DO_REAL_CALL DO_REAL_RETURN movl virt_offset,%ebp negl %ebp movl 0x46C(%ebp), %eax movb 0x470(%ebp), %bl cmpb $0, %bl je notmidnite movb $0, 0x470(%ebp) /* clear the flag */ addl $0x1800b0,days /* 0x1800b0 ticks per day */notmidnite: addl days,%eax popl %edi popl %esi popl %ebx popl %ebp ret/**************************************************************************console_cls()BIOS call "INT 10H Function 0Fh" to get current video mode Call with %ah = 0x0f Returns %al = (video mode) %bh = (page number)BIOS call "INT 10H Function 00h" to set the video mode (clears screen) Call with %ah = 0x00 %al = (video mode)**************************************************************************/ .globl console_clsconsole_cls: pushl %ebx pushl %esi pushl %edi DO_REAL_CALL movb $0xf, %ah int $0x10 /* Get Current Video mode */ xorb %ah, %ah int $0x10 /* Set Video mode (clears screen) */ DO_REAL_RETURN popl %edi popl %esi popl %ebx ret/**************************************************************************console_nocursor()BIOS call "INT 10H Function 01h" to set cursor type Call with %ah = 0x01 %ch = cursor starting scanline %cl = cursor ending scanline**************************************************************************/ .globl console_nocursorconsole_nocursor: pushl %ebx pushl %esi pushl %edi DO_REAL_CALL movw $0x2000, %cx movb $0x1, %ah int $0x10 DO_REAL_RETURN popl %edi popl %esi popl %ebx ret/**************************************************************************console_getxy()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -