/* cpu_asm.s * * This file contains all assembly code for the Intel i386 implementation * of RTEMS. * * COPYRIGHT (c) 1989, 1990, 1991, 1992, 1993, 1994. * On-Line Applications Research Corporation (OAR). * All rights assigned to U.S. Government, 1994. * * This material may be reproduced by or for the U.S. Government pursuant * to the copyright license under the clause at DFARS 252.227-7013. This * notice must appear in all copies of this file and its derivatives. * * $Id$ */ #include /* * Format of i386 Register structure */ .set REG_EFLAGS, 0 .set REG_ESP, REG_EFLAGS + 4 .set REG_EBP, REG_ESP + 4 .set REG_EBX, REG_EBP + 4 .set REG_ESI, REG_EBX + 4 .set REG_EDI, REG_ESI + 4 .set SIZE_REGS, REG_EDI + 4 BEGIN_CODE /* * void _CPU_Context_switch( run_context, heir_context ) * * This routine performs a normal non-FP context. */ .align 2 PUBLIC (_CPU_Context_switch) .set RUNCONTEXT_ARG, 4 # save context argument .set HEIRCONTEXT_ARG, 8 # restore context argument SYM (_CPU_Context_switch): movl RUNCONTEXT_ARG(esp),eax # eax = running threads context pushf # push eflags popl REG_EFLAGS(eax) # save eflags movl esp,REG_ESP(eax) # save stack pointer movl ebp,REG_EBP(eax) # save base pointer movl ebx,REG_EBX(eax) # save ebx movl esi,REG_ESI(eax) # save source register movl edi,REG_EDI(eax) # save destination register movl HEIRCONTEXT_ARG(esp),eax # eax = heir threads context restore: pushl REG_EFLAGS(eax) # push eflags popf # restore eflags movl REG_ESP(eax),esp # restore stack pointer movl REG_EBP(eax),ebp # restore base pointer movl REG_EBX(eax),ebx # restore ebx movl REG_ESI(eax),esi # restore source register movl REG_EDI(eax),edi # restore destination register ret /* * NOTE: May be unnecessary to reload some registers. */ /* * void _CPU_Context_restore( new_context ) * * This routine performs a normal non-FP context. */ PUBLIC (_CPU_Context_restore) .set NEWCONTEXT_ARG, 4 # context to restore argument SYM (_CPU_Context_restore): movl NEWCONTEXT_ARG(esp),eax # eax = running threads context jmp restore /*PAGE * void _CPU_Context_save_fp_context( &fp_context_ptr ) * void _CPU_Context_restore_fp_context( &fp_context_ptr ) * * This section is used to context switch an i80287, i80387, * the built-in coprocessor or the i80486 or compatible. */ .set FPCONTEXT_ARG, 4 # FP context argument .align 2 PUBLIC (_CPU_Context_save_fp) SYM (_CPU_Context_save_fp): movl FPCONTEXT_ARG(esp),eax # eax = &ptr to FP context area movl (eax),eax # eax = FP context area fsave (eax) # save FP context ret .align 2 PUBLIC (_CPU_Context_restore_fp) SYM (_CPU_Context_restore_fp): movl FPCONTEXT_ARG(esp),eax # eax = &ptr to FP context area movl (eax),eax # eax = FP context area frstor (eax) # restore FP context ret /*PAGE * void _ISR_Handler() * * This routine provides the RTEMS interrupt management. * * NOTE: * Upon entry, the stack will contain a stack frame back to the * interrupted task. If dispatching is enabled, this is the * outer most interrupt, and (a context switch is necessary or * the current task has signals), then set up the stack to * transfer control to the interrupt dispatcher. */ .set SET_SEGMENT_REGISTERS_IN_INTERRUPT, 0 .set SAVED_REGS , 32 # space consumed by saved regs .set EIP_OFFSET , SAVED_REGS # offset of tasks eip .set CS_OFFSET , EIP_OFFSET+4 # offset of tasks code segment .set EFLAGS_OFFSET , CS_OFFSET+4 # offset of tasks eflags .align 2 PUBLIC (_ISR_Handler) SYM (_ISR_Handler): /* * Before this was point is reached the vectors unique * entry point did the following: * * 1. saved all registers with a "pusha" * 2. put the vector number in eax. * * BEGINNING OF ESTABLISH SEGMENTS * * WARNING: If an interrupt can occur when the segments are * not correct, then this is where we should establish * the segments. In addition to establishing the * segments, it may be necessary to establish a stack * in the current data area on the outermost interrupt. * * NOTE: If the previous values of the segment registers are * pushed, do not forget to adjust SAVED_REGS. * * NOTE: Make sure the exit code which restores these * when this type of code is needed. */ /***** ESTABLISH SEGMENTS CODE GOES HERE ******/ /* * END OF ESTABLISH SEGMENTS */ /* * Now switch stacks if necessary */ movl esp, edx # edx = previous stack pointer cmpl $0, SYM (_ISR_Nest_level) # is this the outermost interrupt? jne nested # No, then continue movl SYM (_CPU_Interrupt_stack_high), esp /* * We want to insure that the old stack pointer is on the * stack we will be on at the end of the ISR when we restore it. * By saving it on every interrupt, all we have to do is pop it * near the end of every interrupt. */ nested: pushl edx # save the previous stack pointer incl SYM (_ISR_Nest_level) # one nest level deeper incl SYM (_Thread_Dispatch_disable_level) # disable multitasking # EAX is preloaded with the vector number. push eax # push vector number mov SYM (_ISR_Vector_table) (,eax,4),eax # eax = Users handler call eax # invoke user ISR pop eax # eax = vector number decl SYM (_ISR_Nest_level) # one less ISR nest level # If interrupts are nested, # then dispatching is disabled decl SYM (_Thread_Dispatch_disable_level) # unnest multitasking # Is dispatch disabled jne exit # Yes, then exit cmpl $0, SYM (_Context_Switch_necessary) # Is task switch necessary? jne bframe # Yes, then build stack cmpl $0, SYM (_ISR_Signals_to_thread_executing) # signals sent to Run_thread # while in interrupt handler? je exit # No, exit bframe: cli # DISABLE INTERRUPTS!! popl esp # restore the stack pointer movl $0, SYM (_ISR_Signals_to_thread_executing) # push the isf for Isr_dispatch push EFLAGS_OFFSET(esp) # push tasks eflags push cs # cs of Isr_dispatch push $ SYM (_ISR_Dispatch) # entry point iret exit: cli # DISABLE INTERRUPTS!! popl esp # restore the stack pointer /* * BEGINNING OF DE-ESTABLISH SEGMENTS * * NOTE: Make sure there is code here if code is added to * load the segment registers. * */ /******* DE-ESTABLISH SEGMENTS CODE GOES HERE ********/ /* * END OF DE-ESTABLISH SEGMENTS */ popa # restore general registers iret /*PAGE * Distinct Interrupt Entry Points * * The following macro and the 256 instantiations of the macro * are necessary to determine which interrupt vector occurred. * The following macro allows a unique entry point to be defined * for each vector. * * NOTE: There are not spaces around the vector number argument * to the DISTINCT_INTERRUPT_ENTRY macro because m4 will * undesirably generate the symbol "_Isr_handler_ N" * instead of "_Isr_handler_N" like we want. */ #define DISTINCT_INTERRUPT_ENTRY(_vector) \ .align 16 ; \ PUBLIC (_ISR_Handler_ ## _vector ) ; \ SYM (_ISR_Handler_ ## _vector ): \ pusha ; \ xor eax, eax ; \ movb $ ## _vector, al ; \ jmp SYM (_ISR_Handler) ; DISTINCT_INTERRUPT_ENTRY(0) DISTINCT_INTERRUPT_ENTRY(1) DISTINCT_INTERRUPT_ENTRY(2) DISTINCT_INTERRUPT_ENTRY(3) DISTINCT_INTERRUPT_ENTRY(4) DISTINCT_INTERRUPT_ENTRY(5) DISTINCT_INTERRUPT_ENTRY(6) DISTINCT_INTERRUPT_ENTRY(7) DISTINCT_INTERRUPT_ENTRY(8) DISTINCT_INTERRUPT_ENTRY(9) DISTINCT_INTERRUPT_ENTRY(10) DISTINCT_INTERRUPT_ENTRY(11) DISTINCT_INTERRUPT_ENTRY(12) DISTINCT_INTERRUPT_ENTRY(13) DISTINCT_INTERRUPT_ENTRY(14) DISTINCT_INTERRUPT_ENTRY(15) DISTINCT_INTERRUPT_ENTRY(16) DISTINCT_INTERRUPT_ENTRY(17) DISTINCT_INTERRUPT_ENTRY(18) DISTINCT_INTERRUPT_ENTRY(19) DISTINCT_INTERRUPT_ENTRY(20) DISTINCT_INTERRUPT_ENTRY(21) DISTINCT_INTERRUPT_ENTRY(22) DISTINCT_INTERRUPT_ENTRY(23) DISTINCT_INTERRUPT_ENTRY(24) DISTINCT_INTERRUPT_ENTRY(25) DISTINCT_INTERRUPT_ENTRY(26) DISTINCT_INTERRUPT_ENTRY(27) DISTINCT_INTERRUPT_ENTRY(28) DISTINCT_INTERRUPT_ENTRY(29) DISTINCT_INTERRUPT_ENTRY(30) DISTINCT_INTERRUPT_ENTRY(31) DISTINCT_INTERRUPT_ENTRY(32) DISTINCT_INTERRUPT_ENTRY(33) DISTINCT_INTERRUPT_ENTRY(34) DISTINCT_INTERRUPT_ENTRY(35) DISTINCT_INTERRUPT_ENTRY(36) DISTINCT_INTERRUPT_ENTRY(37) DISTINCT_INTERRUPT_ENTRY(38) DISTINCT_INTERRUPT_ENTRY(39) DISTINCT_INTERRUPT_ENTRY(40) DISTINCT_INTERRUPT_ENTRY(41) DISTINCT_INTERRUPT_ENTRY(42) DISTINCT_INTERRUPT_ENTRY(43) DISTINCT_INTERRUPT_ENTRY(44) DISTINCT_INTERRUPT_ENTRY(45) DISTINCT_INTERRUPT_ENTRY(46) DISTINCT_INTERRUPT_ENTRY(47) DISTINCT_INTERRUPT_ENTRY(48) DISTINCT_INTERRUPT_ENTRY(49) DISTINCT_INTERRUPT_ENTRY(50) DISTINCT_INTERRUPT_ENTRY(51) DISTINCT_INTERRUPT_ENTRY(52) DISTINCT_INTERRUPT_ENTRY(53) DISTINCT_INTERRUPT_ENTRY(54) DISTINCT_INTERRUPT_ENTRY(55) DISTINCT_INTERRUPT_ENTRY(56) DISTINCT_INTERRUPT_ENTRY(57) DISTINCT_INTERRUPT_ENTRY(58) DISTINCT_INTERRUPT_ENTRY(59) DISTINCT_INTERRUPT_ENTRY(60) DISTINCT_INTERRUPT_ENTRY(61) DISTINCT_INTERRUPT_ENTRY(62) DISTINCT_INTERRUPT_ENTRY(63) DISTINCT_INTERRUPT_ENTRY(64) DISTINCT_INTERRUPT_ENTRY(65) DISTINCT_INTERRUPT_ENTRY(66) DISTINCT_INTERRUPT_ENTRY(67) DISTINCT_INTERRUPT_ENTRY(68) DISTINCT_INTERRUPT_ENTRY(69) DISTINCT_INTERRUPT_ENTRY(70) DISTINCT_INTERRUPT_ENTRY(71) DISTINCT_INTERRUPT_ENTRY(72) DISTINCT_INTERRUPT_ENTRY(73) DISTINCT_INTERRUPT_ENTRY(74) DISTINCT_INTERRUPT_ENTRY(75) DISTINCT_INTERRUPT_ENTRY(76) DISTINCT_INTERRUPT_ENTRY(77) DISTINCT_INTERRUPT_ENTRY(78) DISTINCT_INTERRUPT_ENTRY(79) DISTINCT_INTERRUPT_ENTRY(80) DISTINCT_INTERRUPT_ENTRY(81) DISTINCT_INTERRUPT_ENTRY(82) DISTINCT_INTERRUPT_ENTRY(83) DISTINCT_INTERRUPT_ENTRY(84) DISTINCT_INTERRUPT_ENTRY(85) DISTINCT_INTERRUPT_ENTRY(86) DISTINCT_INTERRUPT_ENTRY(87) DISTINCT_INTERRUPT_ENTRY(88) DISTINCT_INTERRUPT_ENTRY(89) DISTINCT_INTERRUPT_ENTRY(90) DISTINCT_INTERRUPT_ENTRY(91) DISTINCT_INTERRUPT_ENTRY(92) DISTINCT_INTERRUPT_ENTRY(93) DISTINCT_INTERRUPT_ENTRY(94) DISTINCT_INTERRUPT_ENTRY(95) DISTINCT_INTERRUPT_ENTRY(96) DISTINCT_INTERRUPT_ENTRY(97) DISTINCT_INTERRUPT_ENTRY(98) DISTINCT_INTERRUPT_ENTRY(99) DISTINCT_INTERRUPT_ENTRY(100) DISTINCT_INTERRUPT_ENTRY(101) DISTINCT_INTERRUPT_ENTRY(102) DISTINCT_INTERRUPT_ENTRY(103) DISTINCT_INTERRUPT_ENTRY(104) DISTINCT_INTERRUPT_ENTRY(105) DISTINCT_INTERRUPT_ENTRY(106) DISTINCT_INTERRUPT_ENTRY(107) DISTINCT_INTERRUPT_ENTRY(108) DISTINCT_INTERRUPT_ENTRY(109) DISTINCT_INTERRUPT_ENTRY(110) DISTINCT_INTERRUPT_ENTRY(111) DISTINCT_INTERRUPT_ENTRY(112) DISTINCT_INTERRUPT_ENTRY(113) DISTINCT_INTERRUPT_ENTRY(114) DISTINCT_INTERRUPT_ENTRY(115) DISTINCT_INTERRUPT_ENTRY(116) DISTINCT_INTERRUPT_ENTRY(117) DISTINCT_INTERRUPT_ENTRY(118) DISTINCT_INTERRUPT_ENTRY(119) DISTINCT_INTERRUPT_ENTRY(120) DISTINCT_INTERRUPT_ENTRY(121) DISTINCT_INTERRUPT_ENTRY(122) DISTINCT_INTERRUPT_ENTRY(123) DISTINCT_INTERRUPT_ENTRY(124) DISTINCT_INTERRUPT_ENTRY(125) DISTINCT_INTERRUPT_ENTRY(126) DISTINCT_INTERRUPT_ENTRY(127) DISTINCT_INTERRUPT_ENTRY(128) DISTINCT_INTERRUPT_ENTRY(129) DISTINCT_INTERRUPT_ENTRY(130) DISTINCT_INTERRUPT_ENTRY(131) DISTINCT_INTERRUPT_ENTRY(132) DISTINCT_INTERRUPT_ENTRY(133) DISTINCT_INTERRUPT_ENTRY(134) DISTINCT_INTERRUPT_ENTRY(135) DISTINCT_INTERRUPT_ENTRY(136) DISTINCT_INTERRUPT_ENTRY(137) DISTINCT_INTERRUPT_ENTRY(138) DISTINCT_INTERRUPT_ENTRY(139) DISTINCT_INTERRUPT_ENTRY(140) DISTINCT_INTERRUPT_ENTRY(141) DISTINCT_INTERRUPT_ENTRY(142) DISTINCT_INTERRUPT_ENTRY(143) DISTINCT_INTERRUPT_ENTRY(144) DISTINCT_INTERRUPT_ENTRY(145) DISTINCT_INTERRUPT_ENTRY(146) DISTINCT_INTERRUPT_ENTRY(147) DISTINCT_INTERRUPT_ENTRY(148) DISTINCT_INTERRUPT_ENTRY(149) DISTINCT_INTERRUPT_ENTRY(150) DISTINCT_INTERRUPT_ENTRY(151) DISTINCT_INTERRUPT_ENTRY(152) DISTINCT_INTERRUPT_ENTRY(153) DISTINCT_INTERRUPT_ENTRY(154) DISTINCT_INTERRUPT_ENTRY(155) DISTINCT_INTERRUPT_ENTRY(156) DISTINCT_INTERRUPT_ENTRY(157) DISTINCT_INTERRUPT_ENTRY(158) DISTINCT_INTERRUPT_ENTRY(159) DISTINCT_INTERRUPT_ENTRY(160) DISTINCT_INTERRUPT_ENTRY(161) DISTINCT_INTERRUPT_ENTRY(162) DISTINCT_INTERRUPT_ENTRY(163) DISTINCT_INTERRUPT_ENTRY(164) DISTINCT_INTERRUPT_ENTRY(165) DISTINCT_INTERRUPT_ENTRY(166) DISTINCT_INTERRUPT_ENTRY(167) DISTINCT_INTERRUPT_ENTRY(168) DISTINCT_INTERRUPT_ENTRY(169) DISTINCT_INTERRUPT_ENTRY(170) DISTINCT_INTERRUPT_ENTRY(171) DISTINCT_INTERRUPT_ENTRY(172) DISTINCT_INTERRUPT_ENTRY(173) DISTINCT_INTERRUPT_ENTRY(174) DISTINCT_INTERRUPT_ENTRY(175) DISTINCT_INTERRUPT_ENTRY(176) DISTINCT_INTERRUPT_ENTRY(177) DISTINCT_INTERRUPT_ENTRY(178) DISTINCT_INTERRUPT_ENTRY(179) DISTINCT_INTERRUPT_ENTRY(180) DISTINCT_INTERRUPT_ENTRY(181) DISTINCT_INTERRUPT_ENTRY(182) DISTINCT_INTERRUPT_ENTRY(183) DISTINCT_INTERRUPT_ENTRY(184) DISTINCT_INTERRUPT_ENTRY(185) DISTINCT_INTERRUPT_ENTRY(186) DISTINCT_INTERRUPT_ENTRY(187) DISTINCT_INTERRUPT_ENTRY(188) DISTINCT_INTERRUPT_ENTRY(189) DISTINCT_INTERRUPT_ENTRY(190) DISTINCT_INTERRUPT_ENTRY(191) DISTINCT_INTERRUPT_ENTRY(192) DISTINCT_INTERRUPT_ENTRY(193) DISTINCT_INTERRUPT_ENTRY(194) DISTINCT_INTERRUPT_ENTRY(195) DISTINCT_INTERRUPT_ENTRY(196) DISTINCT_INTERRUPT_ENTRY(197) DISTINCT_INTERRUPT_ENTRY(198) DISTINCT_INTERRUPT_ENTRY(199) DISTINCT_INTERRUPT_ENTRY(200) DISTINCT_INTERRUPT_ENTRY(201) DISTINCT_INTERRUPT_ENTRY(202) DISTINCT_INTERRUPT_ENTRY(203) DISTINCT_INTERRUPT_ENTRY(204) DISTINCT_INTERRUPT_ENTRY(205) DISTINCT_INTERRUPT_ENTRY(206) DISTINCT_INTERRUPT_ENTRY(207) DISTINCT_INTERRUPT_ENTRY(208) DISTINCT_INTERRUPT_ENTRY(209) DISTINCT_INTERRUPT_ENTRY(210) DISTINCT_INTERRUPT_ENTRY(211) DISTINCT_INTERRUPT_ENTRY(212) DISTINCT_INTERRUPT_ENTRY(213) DISTINCT_INTERRUPT_ENTRY(214) DISTINCT_INTERRUPT_ENTRY(215) DISTINCT_INTERRUPT_ENTRY(216) DISTINCT_INTERRUPT_ENTRY(217) DISTINCT_INTERRUPT_ENTRY(218) DISTINCT_INTERRUPT_ENTRY(219) DISTINCT_INTERRUPT_ENTRY(220) DISTINCT_INTERRUPT_ENTRY(221) DISTINCT_INTERRUPT_ENTRY(222) DISTINCT_INTERRUPT_ENTRY(223) DISTINCT_INTERRUPT_ENTRY(224) DISTINCT_INTERRUPT_ENTRY(225) DISTINCT_INTERRUPT_ENTRY(226) DISTINCT_INTERRUPT_ENTRY(227) DISTINCT_INTERRUPT_ENTRY(228) DISTINCT_INTERRUPT_ENTRY(229) DISTINCT_INTERRUPT_ENTRY(230) DISTINCT_INTERRUPT_ENTRY(231) DISTINCT_INTERRUPT_ENTRY(232) DISTINCT_INTERRUPT_ENTRY(233) DISTINCT_INTERRUPT_ENTRY(234) DISTINCT_INTERRUPT_ENTRY(235) DISTINCT_INTERRUPT_ENTRY(236) DISTINCT_INTERRUPT_ENTRY(237) DISTINCT_INTERRUPT_ENTRY(238) DISTINCT_INTERRUPT_ENTRY(239) DISTINCT_INTERRUPT_ENTRY(240) DISTINCT_INTERRUPT_ENTRY(241) DISTINCT_INTERRUPT_ENTRY(242) DISTINCT_INTERRUPT_ENTRY(243) DISTINCT_INTERRUPT_ENTRY(244) DISTINCT_INTERRUPT_ENTRY(245) DISTINCT_INTERRUPT_ENTRY(246) DISTINCT_INTERRUPT_ENTRY(247) DISTINCT_INTERRUPT_ENTRY(248) DISTINCT_INTERRUPT_ENTRY(249) DISTINCT_INTERRUPT_ENTRY(250) DISTINCT_INTERRUPT_ENTRY(251) DISTINCT_INTERRUPT_ENTRY(252) DISTINCT_INTERRUPT_ENTRY(253) DISTINCT_INTERRUPT_ENTRY(254) DISTINCT_INTERRUPT_ENTRY(255) /*PAGE * void _ISR_Dispatch() * * Entry point from the outermost interrupt service routine exit. * The current stack is the supervisor mode stack. */ PUBLIC (_ISR_Dispatch) SYM (_ISR_Dispatch): call SYM (_Thread_Dispatch) # invoke Dispatcher /* * BEGINNING OF DE-ESTABLISH SEGMENTS * * NOTE: Make sure there is code here if code is added to * load the segment registers. * */ /***** DE-ESTABLISH SEGMENTS CODE GOES HERE ****/ /* * END OF DE-ESTABLISH SEGMENTS */ popa # restore general registers iret # return to interrupted thread /*PAGE * * void i386_Install_idt( * unsigned32 source_offset, * unsigned16 destination_segment, * unsigned32 destination_offset * ); */ .align 2 PUBLIC (i386_Install_idt) .set INSTALL_IDT_SAVED_REGS, 8 .set SOURCE_OFFSET_ARG, INSTALL_IDT_SAVED_REGS + 4 .set DESTINATION_SEGMENT_ARG, INSTALL_IDT_SAVED_REGS + 8 .set DESTINATION_OFFSET_ARG, INSTALL_IDT_SAVED_REGS + 12 SYM (i386_Install_idt): push esi push edi movl SOURCE_OFFSET_ARG(esp),esi movl DESTINATION_OFFSET_ARG(esp),edi pushf # save flags cli # DISABLE INTERRUPTS!!! movw DESTINATION_SEGMENT_ARG+4(esp),ax push es # save es movw ax,es movsl # copy 1st half of IDT entry movsl # copy 2nd half of IDT entry pop es # restore es popf # ENABLE INTERRUPTS!!! pop edi pop esi ret /* * void *i386_Logical_to_physical( * rtems_unsigned16 segment, * void *address * ); * * Returns thirty-two bit physical address for segment:address. */ .set SEGMENT_ARG, 4 .set ADDRESS_ARG, 8 PUBLIC (i386_Logical_to_physical) SYM (i386_Logical_to_physical): xorl eax,eax # clear eax movzwl SEGMENT_ARG(esp),ecx # ecx = segment value movl $ SYM (_Global_descriptor_table),edx # edx = address of our GDT addl ecx,edx # edx = address of desired entry movb 7(edx),ah # ah = base 31:24 movb 4(edx),al # al = base 23:16 shll $16,eax # move ax into correct bits movw 2(edx),ax # ax = base 0:15 movl ADDRESS_ARG(esp),ecx # ecx = address to convert addl eax,ecx # ecx = physical address equivalent movl ecx,eax # eax = ecx ret /* * void *i386_Physical_to_logical( * rtems_unsigned16 segment, * void *address * ); * * Returns thirty-two bit physical address for segment:address. */ /* *.set SEGMENT_ARG, 4 *.set ADDRESS_ARG, 8 -- use sets from above */ PUBLIC (i386_Physical_to_logical) SYM (i386_Physical_to_logical): xorl eax,eax # clear eax movzwl SEGMENT_ARG(esp),ecx # ecx = segment value movl $ SYM (_Global_descriptor_table),edx # edx = address of our GDT addl ecx,edx # edx = address of desired entry movb 7(edx),ah # ah = base 31:24 movb 4(edx),al # al = base 23:16 shll $16,eax # move ax into correct bits movw 2(edx),ax # ax = base 0:15 movl ADDRESS_ARG(esp),ecx # ecx = address to convert subl eax,ecx # ecx = logical address equivalent movl ecx,eax # eax = ecx ret END_CODE END