publication# 21642 rev: a amendment/ 0 issue date: november 1997 this document contains information on a product under development at advanced micro devices. the information is intended to help you evaluate this product. amd reserves the right to change or discontinue work on this product without notice. ASSEMBLING 16-bit code in a 32-bit code segment for windows ? ce bootstrap application note by dave tobias this application note explains how to assemble 16-bit code in a 32-bit code segment for windows ? ce bootstrap. this information is useful for those using a 32-bit microcontroller such as an lan?sc400 or lansc410 microcontroller. the standard build process that comes with the oem adaptation kit for windows ce requires that you use the assembler in flat, 32-bit mode. however, the cpu boots into 16-bit mode. microsoft ? compensates for this discrepancy by keeping the 16-bit code as short as possible and also by using macros to force the assembler into generating instructions for 16-bit mode. a good understanding of this workaround process is required to successfully add chipset-specific initialization code to the 16-bit code section. the execution unit of the cpu controls how the cpu processes a given opcode. for a 386 (or later) processor, the execution unit operates in either 16-bit mode or 32-bit mode. the execution unit considers instruction size as two elements: address size and data size. the execution unit allows individual control over these two elements on an instruction-by-instruction basis. when in real mode or v8086 mode, both the data and address, which will be manipulated by a particular opcode, default to being processed as 16-bit quantities because descriptors are not used in real or v8086 modes. when in protect mode, the default instruction size depends on the default (d) bit, which is found in all code segment descriptors, and which must be set up by the systems programmer. regardless of whether the default instruction size is 16 bit or 32 bit, the execution unit can always execute instructions using either 16-bit or 32-bit operands and/or addresses. in fact, a given opcode can be executed using any combination of operand size and data size via the use of override prefix bytes. to individually change the default address size and/or default data size of a single instruction, either one or two prefix bytes must precede the instruction. the two prefix bytes are 66h and 67h: n 66h changes the size of the instruction operand. n 67h changes the size of the instruction address. separating the size of the operand from the size of the address allows programmers to write instructions such as the following: mov ax, [ebx] in 16-bit mode, the assembler encodes the preceding instruction as 67 8b 03. this is because the address size is not the default 16 bits, so the assembler adds a 67h address prefix. in 32-bit mode, the assembler encodes the instruction as 66 8b 03. this is because the operand size is not the default 32 bits, so the assembler adds a 66h operand prefix. the assembler is designed to generate correct prefixes. the programmer defines the mode in which the assembler is operating by using the use16 or use32 keyword in a segment definition. note that simply providing the assembler with one of these directives does not control whether the cpu hardware will actually operate in 16-bit mode or 32-bit mode. this hardware setup must be done manually, and is beyond the scope of this application note. specifying a use16 or use32 segment directive simply tells the assembler to generate code as if the cpu hardware is in 16-bit mode or 32-bit mode, respectively. unfortunately, the assembler does not allow the programmer to switch back and forth between these assumptions in a single segment. nasm, the netwide assembler, does allow this switching via the use16 and use32 assembler pseudo-operation codes. (nasm is a free assembler available via the internet.) the "clean" way to approach this problem if the assembler does not support arbitrary switching between use16 and use32 is to generate two segments, a 16-bit segment and a 32-bit segment.
2 ASSEMBLING 16-bit code in a 32-bit code segment however, the linker and/or build process may not always support this approach. here are the steps required to modify and integrate working 16-bit code: 1. use macros to define the prefix bytes: opprefix macro db 66h endm addrprefixmacro db 67h endm 2. prefix all instructions that use 32-bit operands or addressing with opprefix and/or addrprefix , as appropriate. the assembler will not automatically insert the prefixes because it thinks it is in 32-bit mode, even though the cpu is in 16-bit mode. 3. change all 16-bit instructions into pseudo-32-bit instructions to keep the assembler from inserting unwanted opprefixes / addrprefixes . for example: out 22h,ax ---> out 22h,eax because the assembler thinks the cpu is in 32-bit mode, the assembler will assemble the out 22h,eax without a prefix, which the cpu (in 16-bit mode) will correctly interpret as out 22h,ax. 4. important! while performing step 3, watch for in- structions that have immediate 16-bit data. if you do something like the following example, you will get very bad results: mov ax,constvalue ---> mov eax,constvalue the assembler thinks it is in 32-bit mode, so it will make the constant in the instruction four bytes. the computer will interpret the last two of those bytes (00h 00h) as the first two bytes of the next instruction. there are several methods for working around this. one method is to create a macro: loadax macro value db 0b8h dw value endm another method is to code the instructions as 32-bit instructions: opprefix mov eax,constvalue of course, this does not work if you want to preserve the upper half of eax . if the upper half of eax needs to be preserved, you could do it in two instructions: opprefix and eax,0ffff0000h opprefix or eax,constvalue 5. check the assembly listing to ensure the following: C the instructions assembled as you expected them to. C all conditional jumps (e.g., jz , jnz , etc.) are as- sembled in their short form (single byte offset). if a conditional jump is assembled in its long form, the prefix byte will not be correct. for uncondi- tional jumps, the fact that the offset was extended by two bytes is immaterial (will not affect cpu op- eration). 6. strip all calls out of the code (replace with macros), or insert prefix instructions to ensure not only that the size of the offset is correct (the last two bytes of the four-byte offset are not treated as the next in- struction), but also that the calls and returns match in usage of the stack. 7. finally, use the disassembler in dos debug to dis- assemble and/or step through a portion of the code to make sure that it is doing what you expect it to do. trademarks copyright ? 1997 advanced micro devices, inc. all rights reserved. amd, the amd logo, and combinations thereof are trademarks of advanced micro devices, inc. lan is a trademark of advanced micro devices, inc. microsoft and windows are registered trademarks of microsoft corp. product names used in this publication are for identification purposes only and may be trademarks of their respective companies.
|