View Javadoc

1   package com.tapina.robe.swi.clib;
2   
3   import com.tapina.robe.runtime.Environment;
4   import com.tapina.robe.runtime.MemoryMap;
5   import com.tapina.robe.runtime.CPU;
6   
7   /***
8    * This class represents a variable-length argument list.
9    * APCS passes the first four arguments in registers, then the remaining arguments in memory starting from the
10   * address containing in the fifth register.
11   * Alternatively, for calls which are passing on an existing varargs list, all arguments will be in memory.
12   */
13  public class VarArgs {
14      private static final int MAX_REGISTER = 3;
15      private static final int NUM_REGISTER_ARGUMENTS = MAX_REGISTER + 1;
16      private final CPU cpu;
17      private final MemoryMap memoryMap;
18      private final int registerOffset;
19      private final int memoryBase;
20  
21      public class Argument {
22          final int nativeArg;
23  
24          private Argument(int value) {
25              this.nativeArg = value;
26          }
27  
28          public int asInt() {
29              return nativeArg;
30          }
31  
32          public Integer asInteger() {
33              return new Integer(nativeArg);
34          }
35  
36          public String asString() {
37              return memoryMap.getString0(nativeArg);
38          }
39      }
40  
41      /***
42       * Construct a new VarArgs object under normal circumstances, where there are a known fixed number of arguments,
43       * and then an unknown variable number of arguments following.
44       * For example, <code>sprintf(char *str, char *format, ...)</code> has two known arguments.
45       * @param env Environment under which we are operating
46       * @param knownArgCount Number of known arguments
47       */
48      public VarArgs(Environment env, final int knownArgCount) {
49          this.cpu = env.getCpu();
50          this.memoryMap = env.getMemoryMap();
51          this.registerOffset = knownArgCount;
52          if (knownArgCount > NUM_REGISTER_ARGUMENTS) {
53              // Some of the known arguments are in memory too
54              this.memoryBase = cpu.R[4] + (knownArgCount - NUM_REGISTER_ARGUMENTS) * 4;
55          } else {
56              this.memoryBase = cpu.R[4];
57          }
58      }
59  
60      /***
61       * Construct a new VarArgs object where all arguments are in memory at a specified address.
62       * @param memoryMap memory map under which to obtain data
63       * @param address address of first value
64       */
65      public VarArgs(MemoryMap memoryMap, final int address) {
66          this.cpu = null;
67          this.memoryMap = memoryMap;
68          this.registerOffset = NUM_REGISTER_ARGUMENTS;
69          this.memoryBase = address;
70      }
71  
72      /***
73       * Get the specified argument from the variable argument list.
74       * The first argument has offset 0.
75       * @param offset Which argument to obtain.
76       * @return An object representing that argument.
77       */
78      public Argument getVarArg(int offset) {
79          final int Rn = registerOffset + offset;
80          if (Rn > MAX_REGISTER) {
81              // Fetch from memory
82              return new Argument(memoryMap.getWord(memoryBase + (Rn - NUM_REGISTER_ARGUMENTS) * 4));
83          } else {
84              return new Argument(cpu.R[Rn]);
85          }
86      }
87  }