aboutsummaryrefslogtreecommitdiffstats
path: root/doc/latex/src/mixsize.tex
diff options
context:
space:
mode:
Diffstat (limited to 'doc/latex/src/mixsize.tex')
-rw-r--r--doc/latex/src/mixsize.tex185
1 files changed, 185 insertions, 0 deletions
diff --git a/doc/latex/src/mixsize.tex b/doc/latex/src/mixsize.tex
new file mode 100644
index 00000000..b8a35ea8
--- /dev/null
+++ b/doc/latex/src/mixsize.tex
@@ -0,0 +1,185 @@
+%
+% vim: ts=4 sw=4 et
+%
+\xchapter{mixsize}{Mixing 16 and 32 Bit Code}
+
+This chapter tries to cover some of the issues, largely related to
+unusual forms of addressing and jump instructions, encountered when
+writing operating system code such as protected-mode initialisation
+routines, which require code that operates in mixed segment sizes,
+such as code in a 16-bit segment trying to modify data in a 32-bit
+one, or jumps between different-size segments.
+
+\xsection{mixjump}{Mixed-Size Jumps}
+\index{jumps!mixed-size}
+\index{operating system, writing}
+\index{writing operating systems}
+
+The most common form of \textindex{mixed-size instruction} is the one
+used when writing a 32-bit OS: having done your setup in 16-bit mode,
+such as loading the kernel, you then have to boot it by switching into
+protected mode and jumping to the 32-bit kernel start address. In a
+fully 32-bit OS, this tends to be the \emph{only} mixed-size
+instruction you need, since everything before it can be done in pure
+16-bit code, and everything after it can be pure 32-bit.
+
+This jump must specify a 48-bit far address, since the target
+segment is a 32-bit one. However, it must be assembled in a 16-bit
+segment, so just coding, for example,
+
+\begin{lstlisting}
+jmp 0x1234:0x56789ABC ; wrong!
+\end{lstlisting}
+
+will not work, since the offset part of the address will be
+truncated to \code{0x9ABC} and the jump will be an ordinary 16-bit far
+one.
+
+The Linux kernel setup code gets round the inability of \code{as86} to
+generate the required instruction by coding it manually, using
+\code{DB} instructions. NASM can go one better than that, by actually
+generating the right instruction itself. Here's how to do it right:
+
+\begin{lstlisting}
+jmp dword 0x1234:0x56789ABC ; right
+\end{lstlisting}
+
+\indexcode{JMP DWORD}The \code{DWORD} prefix (strictly speaking,
+it should come \emph{after} the colon, since it is declaring the
+\emph{offset} field to be a doubleword; but NASM will accept either
+form, since both are unambiguous) forces the offset part to be treated
+as far, in the assumption that you are deliberately writing a jump from
+a 16-bit segment to a 32-bit one.
+
+You can do the reverse operation, jumping from a 32-bit segment to a
+16-bit one, by means of the \code{WORD} prefix:
+
+\begin{lstlisting}
+jmp word 0x8765:0x4321 ; 32 to 16 bit
+\end{lstlisting}
+
+If the \code{WORD} prefix is specified in 16-bit mode, or the
+\code{DWORD} prefix in 32-bit mode, they will be ignored, since each is
+explicitly forcing NASM into a mode it was in anyway.
+
+\xsection{mixaddr}{Addressing Between Different-Size Segments}
+\index{addressing!mixed-size}
+\index{mixed-size addressing}
+
+If your OS is mixed 16 and 32-bit, or if you are writing a DOS
+extender, you are likely to have to deal with some 16-bit segments
+and some 32-bit ones. At some point, you will probably end up
+writing code in a 16-bit segment which has to access data in a
+32-bit segment, or vice versa.
+
+If the data you are trying to access in a 32-bit segment lies within
+the first 64K of the segment, you may be able to get away with using
+an ordinary 16-bit addressing operation for the purpose; but sooner
+or later, you will want to do 32-bit addressing from 16-bit mode.
+
+The easiest way to do this is to make sure you use a register for
+the address, since any effective address containing a 32-bit
+register is forced to be a 32-bit address. So you can do
+
+\begin{lstlisting}
+mov eax,offset_into_32_bit_segment_specified_by_fs
+mov dword [fs:eax],0x11223344
+\end{lstlisting}
+
+This is fine, but slightly cumbersome (since it wastes an
+instruction and a register) if you already know the precise offset
+you are aiming at. The x86 architecture does allow 32-bit effective
+addresses to specify nothing but a 4-byte offset, so why shouldn't
+NASM be able to generate the best instruction for the purpose?
+
+It can. As in \nref{mixjump}, you need only prefix the address
+with the \code{DWORD} keyword, and it will be forced to be a 32-bit
+address:
+
+\begin{lstlisting}
+mov dword [fs:dword my_offset],0x11223344
+\end{lstlisting}
+
+Also as in \nref{mixjump}, NASM is not fussy about whether the
+\code{DWORD} prefix comes before or after the segment override, so
+arguably a nicer-looking way to code the above instruction is
+
+\begin{lstlisting}
+mov dword [dword fs:my_offset],0x11223344
+\end{lstlisting}
+
+Don't confuse the \code{DWORD} prefix \emph{outside} the square brackets,
+which controls the size of the data stored at the address, with the
+one \code{inside} the square brackets which controls the length of the
+address itself. The two can quite easily be different:
+
+\begin{lstlisting}
+mov word [dword 0x12345678],0x9ABC
+\end{lstlisting}
+
+This moves 16 bits of data to an address specified by a 32-bit
+offset.
+
+You can also specify \code{WORD} or \code{DWORD} prefixes along with the
+\code{FAR} prefix to indirect far jumps or calls. For example:
+
+\begin{lstlisting}
+call dword far [fs:word 0x4321]
+\end{lstlisting}
+
+This instruction contains an address specified by a 16-bit offset;
+it loads a 48-bit far pointer from that (16-bit segment and 32-bit
+offset), and calls that address.
+
+\xsection{mixother}{Other Mixed-Size Instructions}
+
+The other way you might want to access data might be using the
+string instructions (\code{LODSx}, \code{STOSx} and so on) or the
+\code{XLATB} instruction. These instructions, since they take no
+parameters, might seem to have no easy way to make them perform
+32-bit addressing when assembled in a 16-bit segment.
+
+This is the purpose of NASM's \codeindex{a16}, \codeindex{a32} and
+\codeindex{a64} prefixes. If you are coding \code{LODSB} in a 16-bit
+segment but it is supposed to be accessing a string in a 32-bit segment,
+you should load the desired address into \code{ESI} and then code
+
+\begin{lstlisting}
+a32 lodsb
+\end{lstlisting}
+
+The prefix forces the addressing size to 32 bits, meaning that
+\code{LODSB} loads from \code{[DS:ESI]} instead of \code{[DS:SI]}.
+To access a string in a 16-bit segment when coding in a 32-bit one,
+the corresponding \code{a16} prefix can be used.
+
+The \code{a16}, \code{a32} and \code{a64} prefixes can be applied to
+any instruction in NASM's instruction table, but most of them can
+generate all the useful forms without them. The prefixes are necessary
+only for instructions with implicit addressing: \code{CMPSx},
+\code{SCASx}, \code{LODSx}, \code{STOSx}, \code{MOVSx}, \code{INSx},
+\code{OUTSx}, and \code{XLATB}. Also, the various push and pop
+instructions (\code{PUSHA} and \code{POPF} as well as the more usual
+\code{PUSH} and \code{POP}) can accept \code{a16}, \code{a32} or
+\code{a64} prefixes to force a particular one of \code{SP}, \code{ESP} or
+\code{RSP} to be used as a stack pointer, in case the stack segment in
+use is a different size from the code segment.
+
+\code{PUSH} and \code{POP}, when applied to segment registers in 32-bit
+mode, also have the slightly odd behaviour that they push and pop 4
+bytes at a time, of which the top two are ignored and the bottom two
+give the value of the segment register being manipulated. To force
+the 16-bit behaviour of segment-register push and pop instructions,
+you can use the operand-size prefix \codeindex{o16}:
+
+\begin{lstlisting}
+o16 push ss
+o16 push ds
+\end{lstlisting}
+
+This code saves a doubleword of stack space by fitting two segment
+registers into the space which would normally be consumed by pushing
+one.
+
+(You can also use the \codeindex{o32} prefix to force the 32-bit behaviour
+when in 16-bit mode, but this seems less useful.)