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
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
82 return new Argument(memoryMap.getWord(memoryBase + (Rn - NUM_REGISTER_ARGUMENTS) * 4));
83 } else {
84 return new Argument(cpu.R[Rn]);
85 }
86 }
87 }