📄 nasmdoc7.htm
字号:
multiplying the desired array index, 3, by the size of the array element,2.) The sizes of the C base types in 16-bit compilers are: 1 for<code><nobr>char</nobr></code>, 2 for <code><nobr>short</nobr></code> and<code><nobr>int</nobr></code>, 4 for <code><nobr>long</nobr></code> and<code><nobr>float</nobr></code>, and 8 for<code><nobr>double</nobr></code>.<p>To access a C data structure, you need to know the offset from the baseof the structure to the field you are interested in. You can either do thisby converting the C structure definition into a NASM structure definition(using <code><nobr>STRUC</nobr></code>), or by calculating the one offsetand using just that.<p>To do either of these, you should read your C compiler's manual to findout how it organises data structures. NASM gives no special alignment tostructure members in its own <code><nobr>STRUC</nobr></code> macro, so youhave to specify alignment yourself if the C compiler generates it.Typically, you might find that a structure like<p><pre>struct { char c; int i; } foo;</pre><p>might be four bytes long rather than three, since the<code><nobr>int</nobr></code> field would be aligned to a two-byteboundary. However, this sort of feature tends to be a configurable optionin the C compiler, either using command-line options or<code><nobr>#pragma</nobr></code> lines, so you have to find out how yourown compiler does it.<h4><a name="section-7.4.5">7.4.5 <code><nobr>c16.mac</nobr></code>: Helper Macros for the 16-bit C Interface</a></h4><p>Included in the NASM archives, in the <code><nobr>misc</nobr></code>directory, is a file <code><nobr>c16.mac</nobr></code> of macros. Itdefines three macros: <code><nobr>proc</nobr></code>,<code><nobr>arg</nobr></code> and <code><nobr>endproc</nobr></code>. Theseare intended to be used for C-style procedure definitions, and theyautomate a lot of the work involved in keeping track of the callingconvention.<p>(An alternative, TASM compatible form of <code><nobr>arg</nobr></code>is also now built into NASM's preprocessor. See<a href="nasmdoc4.html#section-4.9">section 4.9</a> for details.)<p>An example of an assembly function using the macro set is given here:<p><pre>proc _nearproc %$i arg %$j arg mov ax,[bp + %$i] mov bx,[bp + %$j] add ax,[bx] endproc</pre><p>This defines <code><nobr>_nearproc</nobr></code> to be a proceduretaking two arguments, the first (<code><nobr>i</nobr></code>) an integerand the second (<code><nobr>j</nobr></code>) a pointer to an integer. Itreturns <code><nobr>i + *j</nobr></code>.<p>Note that the <code><nobr>arg</nobr></code> macro has an<code><nobr>EQU</nobr></code> as the first line of its expansion, and sincethe label before the macro call gets prepended to the first line of theexpanded macro, the <code><nobr>EQU</nobr></code> works, defining<code><nobr>%$i</nobr></code> to be an offset from<code><nobr>BP</nobr></code>. A context-local variable is used, local tothe context pushed by the <code><nobr>proc</nobr></code> macro and poppedby the <code><nobr>endproc</nobr></code> macro, so that the same argumentname can be used in later procedures. Of course, you don't <em>have</em> todo that.<p>The macro set produces code for near functions (tiny, small andcompact-model code) by default. You can have it generate far functions(medium, large and huge-model code) by means of coding<code><nobr>%define FARCODE</nobr></code>. This changes the kind of returninstruction generated by <code><nobr>endproc</nobr></code>, and alsochanges the starting point for the argument offsets. The macro set containsno intrinsic dependency on whether data pointers are far or not.<p><code><nobr>arg</nobr></code> can take an optional parameter, giving thesize of the argument. If no size is given, 2 is assumed, since it is likelythat many function parameters will be of type<code><nobr>int</nobr></code>.<p>The large-model equivalent of the above function would look like this:<p><pre>%define FARCODE proc _farproc %$i arg %$j arg 4 mov ax,[bp + %$i] mov bx,[bp + %$j] mov es,[bp + %$j + 2] add ax,[bx] endproc</pre><p>This makes use of the argument to the <code><nobr>arg</nobr></code>macro to define a parameter of size 4, because <code><nobr>j</nobr></code>is now a far pointer. When we load from <code><nobr>j</nobr></code>, wemust load a segment and an offset.<h3><a name="section-7.5">7.5 Interfacing to Borland Pascal Programs</a></h3><p>Interfacing to Borland Pascal programs is similar in concept tointerfacing to 16-bit C programs. The differences are:<ul><li>The leading underscore required for interfacing to C programs is notrequired for Pascal.<li>The memory model is always large: functions are far, data pointers arefar, and no data item can be more than 64K long. (Actually, some functionsare near, but only those functions that are local to a Pascal unit andnever called from outside it. All assembly functions that Pascal calls, andall Pascal functions that assembly routines are able to call, are far.)However, all static data declared in a Pascal program goes into the defaultdata segment, which is the one whose segment address will be in<code><nobr>DS</nobr></code> when control is passed to your assembly code.The only things that do not live in the default data segment are localvariables (they live in the stack segment) and dynamically allocatedvariables. All data <em>pointers</em>, however, are far.<li>The function calling convention is different - described below.<li>Some data types, such as strings, are stored differently.<li>There are restrictions on the segment names you are allowed to use -Borland Pascal will ignore code or data declared in a segment it doesn'tlike the name of. The restrictions are described below.</ul><h4><a name="section-7.5.1">7.5.1 The Pascal Calling Convention</a></h4><p>The 16-bit Pascal calling convention is as follows. In the followingdescription, the words <em>caller</em> and <em>callee</em> are used todenote the function doing the calling and the function which gets called.<ul><li>The caller pushes the function's parameters on the stack, one afteranother, in normal order (left to right, so that the first argumentspecified to the function is pushed first).<li>The caller then executes a far <code><nobr>CALL</nobr></code>instruction to pass control to the callee.<li>The callee receives control, and typically (although this is notactually necessary, in functions which do not need to access theirparameters) starts by saving the value of <code><nobr>SP</nobr></code> in<code><nobr>BP</nobr></code> so as to be able to use<code><nobr>BP</nobr></code> as a base pointer to find its parameters onthe stack. However, the caller was probably doing this too, so part of thecalling convention states that <code><nobr>BP</nobr></code> must bepreserved by any function. Hence the callee, if it is going to set up<code><nobr>BP</nobr></code> as a frame pointer, must push the previousvalue first.<li>The callee may then access its parameters relative to<code><nobr>BP</nobr></code>. The word at <code><nobr>[BP]</nobr></code>holds the previous value of <code><nobr>BP</nobr></code> as it was pushed.The next word, at <code><nobr>[BP+2]</nobr></code>, holds the offset partof the return address, and the next one at <code><nobr>[BP+4]</nobr></code>the segment part. The parameters begin at <code><nobr>[BP+6]</nobr></code>.The rightmost parameter of the function, since it was pushed last, isaccessible at this offset from <code><nobr>BP</nobr></code>; the othersfollow, at successively greater offsets.<li>The callee may also wish to decrease <code><nobr>SP</nobr></code>further, so as to allocate space on the stack for local variables, whichwill then be accessible at negative offsets from<code><nobr>BP</nobr></code>.<li>The callee, if it wishes to return a value to the caller, should leavethe value in <code><nobr>AL</nobr></code>, <code><nobr>AX</nobr></code> or<code><nobr>DX:AX</nobr></code> depending on the size of the value.Floating-point results are returned in <code><nobr>ST0</nobr></code>.Results of type <code><nobr>Real</nobr></code> (Borland's own customfloating-point data type, not handled directly by the FPU) are returned in<code><nobr>DX:BX:AX</nobr></code>. To return a result of type<code><nobr>String</nobr></code>, the caller pushes a pointer to atemporary string before pushing the parameters, and the callee places thereturned string value at that location. The pointer is not a parameter, andshould not be removed from the stack by the <code><nobr>RETF</nobr></code>instruction.<li>Once the callee has finished processing, it restores<code><nobr>SP</nobr></code> from <code><nobr>BP</nobr></code> if it hadallocated local stack space, then pops the previous value of<code><nobr>BP</nobr></code>, and returns via<code><nobr>RETF</nobr></code>. It uses the form of<code><nobr>RETF</nobr></code> with an immediate parameter, giving thenumber of bytes taken up by the parameters on the stack. This causes theparameters to be removed from the stack as a side effect of the returninstruction.<li>When the caller regains control from the callee, the functionparameters have already been removed from the stack, so it needs to donothing further.</ul><p>Thus, you would define a function in Pascal style, taking two<code><nobr>Integer</nobr></code>-type parameters, in the following way:<p><pre>global myfunc myfunc: push bp mov bp,sp sub sp,0x40 ; 64 bytes of local stack space mov bx,[bp+8] ; first parameter to function mov bx,[bp+6] ; second parameter to function ; some more code mov sp,bp ; undo "sub sp,0x40" above pop bp retf 4 ; total size of params is 4</pre><p>At the other end of the process, to call a Pascal function from yourassembly code, you would do something like this:<p><pre>extern SomeFunc ; and then, further down... push word seg mystring ; Now push the segment, and... push word mystring ; ... offset of "mystring" push word [myint] ; one of my variables call far SomeFunc</pre><p>This is equivalent to the Pascal code<p><pre>procedure SomeFunc(String: PChar; Int: Integer); SomeFunc(@mystring, myint);</pre><h4><a name="section-7.5.2">7.5.2 Borland Pascal Segment Name Restrictions</a></h4><p>Since Borland Pascal's internal unit file format is completely differentfrom <code><nobr>OBJ</nobr></code>, it only makes a very sketchy job ofactually reading and understanding the various information contained in areal <code><nobr>OBJ</nobr></code> file when it links that in. Therefore anobject file intended to be linked to a Pascal program must obey a number ofrestrictions:<ul><li>Procedures and functions must be in a segment whose name is either<code><nobr>CODE</nobr></code>, <code><nobr>CSEG</nobr></code>, orsomething ending in <code><nobr>_TEXT</nobr></code>.<li>Initialised data must be in a segment whose name is either<code><nobr>CONST</nobr></code> or something ending in<code><nobr>_DATA</nobr></code>.<li>Uninitialised data must be in a segment whose name is either<code><nobr>DATA</nobr></code>, <code><nobr>DSEG</nobr></code>, orsomething ending in <code><nobr>_BSS</nobr></code>.<li>Any other segments in the object file are completely ignored.<code><nobr>GROUP</nobr></code> directives and segment attributes are alsoignored.</ul><h4><a name="section-7.5.3">7.5.3 Using <code><nobr>c16.mac</nobr></code> With Pascal Programs</a></h4><p>The <code><nobr>c16.mac</nobr></code> macro package, described in<a href="#section-7.4.5">section 7.4.5</a>, can also be used to simplifywriting functions to be called from Pascal programs, if you code<code><nobr>%define PASCAL</nobr></code>. This definition ensures thatfunctions are far (it implies <code><nobr>FARCODE</nobr></code>), and alsocauses procedure return instructions to be generated with an operand.<p>Defining <code><nobr>PASCAL</nobr></code> does not change the code whichcalculates the argument offsets; you must declare your function's argumentsin reverse order. For example:<p><pre>%define PASCAL proc _pascalproc %$j arg 4 %$i arg mov ax,[bp + %$i] mov bx,[bp + %$j] mov es,[bp + %$j + 2] add ax,[bx] endproc</pre><p>This defines the same routine, conceptually, as the example in<a href="#section-7.4.5">section 7.4.5</a>: it defines a function takingtwo arguments, an integer and a pointer to an integer, which returns thesum of the integer and the contents of the pointer. The only differencebetween this code and the large-model C version is that<code><nobr>PASCAL</nobr></code> is defined instead of<code><nobr>FARCODE</nobr></code>, and that the arguments are declared inreverse order.<p align=center><a href="nasmdoc8.html">Next Chapter</a> |<a href="nasmdoc6.html">Previous Chapter</a> |<a href="nasmdoc0.html">Contents</a> |<a href="nasmdoci.html">Index</a></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -