📄 apcswrite.html
字号:
<!doctype html public "-//W3C//DTD HTML 3.2//EN"><html><head><title>Writing an APCS program</title><meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" /><meta http-equiv="content-language" content="en" /><meta name="resource-type" content="document"><meta name="copyright" content="This document copyright 2001 by Richard Murray. Use for non-profit and education purposes explicitly granted."><meta name="author" content="Richard Murray"><meta name="rating" content="general"></head><!-- /assembler/apcswrite.html --><!-- --><!-- (C) Copyright 2001 Richard Murray --><!-- Designed by Richard Murray --><!-- rmurray@heyrick.co.uk --><!-- --><body bgcolor="#f0f0f0" text="#000000" link="#0022dd" vlink="#002288"><table border = "0" width="100%"> <tr> <td align=center width=100> <img src="arm3.gif" width=79 height=78 align = middle> </td> <td> <h1 align="center"><font color="#800080">Writing an APCS program</font></h1> </td> <td align=center width=100> <img src="arm3.gif" width=79 height=78 align = middle> </td></table><p> <p>The <b>APCS</b>, or ARM Procedure Call Standard, provides a mechanism for writing tightlydefined routines in assembler. This may seem possibly wasteful when you consider the case ofwriting your own project entirely in assembler. However, modern applications tend to be of ahybrid fashion - the base written in a high level language, with speed-critical parts written inassembler. Then, APCS comes into its own.<p> <p> <p>In a DDE application, several directories are used:<ul> <li> <code>h</code><br> For header files <li> <code>o</code><br> For object files</ul>As <code>c</code> is used for C code, it stands to reason that <code>s</code> is used forassembler.<br>Lost?<br>Me too. I suspect 's' means source, from the days when real programmers shunned high levellanguages.<br>Whatever the reason, by convention the assembler code goes in a subdirectory called'<code>s</code>'.<p> <p> <p><h2>Registers</h2>Registers R0 to R3 are destructible. All the rest (save R14 (lr)) <i>must</i> be properlypreserved, if they are in any way altered by your code.<p>APCS defines the registers using different names to our usual R0 to R14. With the power of theassembler preprocessor, you can define R0 etc, but it is just as well to learn the APCS namesin case you are modifying code written by others.<center><table> <tr bgcolor="#cccccc" align="center"> <td colspan = 3>Register names</td> </tr> <tr bgcolor="#dddddd"> <td><code>Reg # </code></td> <td><code>APCS </code></td> <td>Meaning</td> <tr> <td><code>R0</code></td> <td><code>a1</code></td> <td>Working registers</td> </tr> <tr> <td><code>R1</code></td> <td><code>a2</code></td> <td align = center>"</td> </tr> <tr> <td><code>R2</code></td> <td><code>a3</code></td> <td align = center>"</td> </tr> <tr> <td><code>R3</code></td> <td><code>a4</code></td> <td align = center>"</td> </tr> <tr> <td><code>R4</code></td> <td><code>v1</code></td> <td>Must be preserved</td> </tr> <tr> <td><code>R5</code></td> <td><code>v2</code></td> <td align = center>"</td> </tr> <tr> <td><code>R6</code></td> <td><code>v3</code></td> <td align = center>"</td> </tr> <tr> <td><code>R7</code></td> <td><code>v4</code></td> <td align = center>"</td> </tr> <tr> <td><code>R8</code></td> <td><code>v5</code></td> <td align = center>"</td> </tr> <tr> <td><code>R9</code></td> <td><code>v6</code></td> <td align = center>"</td> </tr> <tr> <td><code>R10</code></td> <td><code>sl</code></td> <td>Stack Limit</td> </tr> <tr> <td><code>R11</code></td> <td><code>fp</code></td> <td>Frame Pointer</td> </tr> <tr> <td><code>R12</code></td> <td><code>ip</code></td> <td> </td> </tr> <tr> <td><code>R13</code></td> <td><code>sp</code></td> <td>Stack Pointer</td> </tr> <tr> <td><code>R14</code></td> <td><code>lr</code></td> <td>Link Register</td> </tr> <tr> <td><code>R15</code></td> <td><code>pc</code></td> <td>Program Counter</td> </tr></table></center><p> <p>These names are not defined by standard in Acorn's older <i>objasm</i>, though other assemblers(such as Nick Roberts' <i>ASM</i>, and the later (32 bit compatible) objasm) should define themfor you.<br>To define a register name in <i>objasm</i>, you use the <code>RN</code> directive, at the verystart of your program:<pre>a1 RN 0a2 RN 1a3 RN 2 ...etc...r13 RN 13sp RN 13r14 RN 14lr RN r14pc RN 15</pre>That example shows us two important things:<ol> <li> That registers can be multiply defined - you can have <i>both</i> 'r13' and 'sp'. <li> That registers can be defined from previously defined registers - 'lr' was defined from the setting of 'r14'.<br> (this is correct for <i>objasm</i>, other assemblers may not do this)</ol>Other assemblers may differ, like using a command such as "BINDREG", refer to theinstructions for your particular example.<p>You can set up a header file, if you don't want to do all of this in every module you write.Then, for <i>objasm</i>, you simply:<pre> GET h.regnames</pre>For <i>ASM</i>, you would use the INCLUDE directive. Other assemblers may vary.<p> <p> <p><h2>Areas</h2>The next thing that you must do is to define your area. This may be CODE or DATA, and canoptionally be READONLY.<br>Other areas exist, but are outside the scope of this document.<br>For our purposes, we shall be writing a program or function, so our area will be a READONLY CODEarea.<p>We define this with:<pre> AREA |main|, CODE, READONLY</pre>This sets up an area called "main". The area name must be enclosed in vertical bars,and it may be any valid identifier.<p>If we were linking our code with C, then we would call the area "|C$$code|". This isdescribed in mode detail in <a href="exa6.html">example 6</a>. You cannot call a function"main" when interworking with C, as by convention the initial function of a C programis main(). Likewise, you cannot call your function the same as an existing one.<p> <p> <p><h2>Entry point</h2>For a stand-alone program, your next directive would be:<pre> ENTRY</pre>to tell the assembler that this is where your program wishes to be entered. You can only have oneentry point per program.<p>When interworking with a high level language, you do not have an entry point. Instead, you havea collection of routines that are exported. Refer to <a href="exa6.html">example 6</a> for moreinformation.<br>Please note, there is quite a lot of stuff that should be set up for a stand-alone assemblerprogram, such as reading command line parameters and stack management. It gets even hairier ifyou wish to write a multitasking application entirely in assembler. Therefore, it is recommendedthat you write the basis of your program in C, and use assembler for the parts that require thespeed benefit. However, <a href="exa5.html">example 5</a> will describe a simple utility writtenentirely in assembler.<p> <p> <p><h2>Code</h2>Your code follows.<p>Now is the time to explain why the previous examples were indented eight spaces, except for theregister definitions.<p>In assembler code, there is a very simple syntax. Things on the left are counted as identifiers,while things that are indented are either instructions or directives.<br>Any line beginning with a semicolon is a comment, and a semicolon in a line of code means thatthe rest of the line is a comment.<br>You do <i>not</i> start labels with a period.Unlike BASIC, a colon does <i>not</i> start a new instruction. You can only have one instructionper line.<br>For example:<pre>r0 RN 0 AREA |main|, CODE, READONLY ENTRY ADR r0, title SWI &02 ; OS_Write0 SWI &10 ; OS_GetEnv SWI &02 ; OS_Write0 SWI &03 ; OS_NewLine SWI &11 ; OS_Exittitle = "This program was called with:", 10, 13, " ", 0 ALIGN END</pre>When run, the program outputs a short title, followed by the command line that started theprogram. For example:<table width="100%"><tr bgcolor="#000000"><td><pre><font color = "#ffffff">TaskWindow Server v0.01*catDir. IDEFS::Willow.$.Coding.Projects.Assembler.apcstest Option 00 (Off) CSD IDEFS::Willow.$.Coding.Projects.Assembler.apcstestLib. IDEFS::Buffy.$.libraryURD IDEFS::Stephanie.$o D/ s D/ test WR/*test -this -is a -testThis program was called with: test -this -is a -test*</font></pre></td></tr></table><p> <p> <p><h2>But... that doesn't work!</h2>If you are not using <i>objasm</i>, then chances are it won't work correctly. Each assembleravailable does the basic instruction set, and also some of the BASIC commands, such as ALIGN andDCD. However, specifics and directives are pretty much up to the author of the assembler.<br>As an example, many of the assemblers will do SWI name expansion, where <code>SWI"OS_Exit"</code> will read the SWI name and calculate the SWI number for you; and ARMmaker willgo as far as to allow you to specify output types (ABSOLUTE, MODULE, AOF, etc) which is veryuseful if you don't have a linker.<p>You may also like to <a href="apcsasm.html">refer to my opinions on various assemblers</a>.<p> <p> <p><h2>The examples</h2>The examples have been written for <i>objasm</i>.<p><dl> <dt> <a href="exa5.html">Example 5</a>: <dd> A simple example written entirely in assembler.</dl><dl> <dt> <a href="exa6.html">Example 6</a>: <dd> An example of combining assembler and C.</dl><p> <p> <p><h2>More...</h2>A full explanation of APCS and assemblers is out of the scope of this guide. Assuming that youare familiar with assembler, the instructions for <i>ASM</i> and <i>ARMmaker</i> are bothinteresting reads. These two documents should provide you with sufficient information to obtaingood results from your assembler, whichever you choose, and help you write productive code.<br>However, for complete instructions (should your require them), you would need to read the PRMsand the Acorn Assembler documentation.<p> <p> <p><h2>And finally...</h2>You will (by the time you've read the details for example 6), realise that I am overstressingthe following point:<br><i>Don't recode everything in assembler because you can.</i><br>Modern compilers aren't stupid. The Norcroft v4.00 compiler, apparently, doesn't generatetotally optimised code [source: lots of arguments on comp.sys.acorn.programmer over the years]but it does perform some optimisations. And, instruction for instruction, a compiler canprobabaly out-optimise many of you. I, for one, wouldn't want to take on a compiler against mycode.<p>But don't be discouraged. Assembler has its uses. Here are some examples:<ul> <li> A simple SWI wrapper, to remove the overheads of calling _kernel_swi() or similar.<br> DeskLib 2.30 took a simple WIMP command - <code>Wimp_ProcessKey</code> and implemented it in only four lines of assembler:<br><pre>MOV ip, lrSWI SWI_Wimp_ProcessKey + XOS_BitMOVVC a1, #0MOVS pc, ip ; <font color="red">(not 32-bit friendly)</font></pre> <br> <br> <li> Access to protected memory areas. One example is the podule area, which cannot be accessed from user mode code. <br> <br> <li> And those good old processor intensive functions, like playing with memory or pixels or anything with large amounts of bit shifting involved, or stuff that just doesn't compile well.<br> An interesting challenge would be to ask your compiler to generate a source listing rather than a compiled object file (for Norcroft C, use the <code>-S</code> flag). Load this file into your favourite editor, and read it. Can it be optimised? Is it overly peculiar (warning: compiler code can sometimes defy logic - trust it, it (usually!) knows what it is doing!).<br> This is also a good way to kickstart your own assembler code. If it is slow(ish), and doesn't call too many library functions, that you can knock up a 'working model' in C or Pascal (both Norcroft compilers will output ARM assembly code with the -S flag) and then you have a functional base to improve upon.</ul>I'm sure you can think of more examples that might benefit from being coded in assembler.<p>Please be sure to read '<a href = "opinion_04.html">Don't be over zealous</a>'.<hr size = "3"><a href="index.html#08">Return to assembler index</a><hr size = "3"><address>Copyright © 2001 Richard Murray</address></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -