summaryrefslogtreecommitdiff
path: root/src/Runtime/__va_arg.c
blob: fd0e182a14090e4fa73aa7187e831b17d4b552b5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
typedef struct {
  char gpr;
  char fpr;
  char reserved[2];
  char* input_arg_area;
  char* reg_save_area;
} va_list[1];

typedef enum {
  ARGPOINTER,
  WORD,
  DOUBLEWORD,
  REAL,
} _va_arg_type;

#define ALIGN(addr, amount) (((unsigned int)(addr) + ((amount)-1)) & ~((amount)-1))

void* __va_arg(va_list ap, _va_arg_type type) {
  char* addr;
  char* curGprPtr = &(ap->gpr);
  int curGpr = ap->gpr;
  int max = 8;
  int size = 4;
  int inc = 1;
  int even = 0;
  int fprOffset = 0;
  int regSize = 4;

  if (type == 3) {
    curGprPtr = &(ap->fpr);
    curGpr = ap->fpr;
    size = 8;
    fprOffset = 8 * 4;
    regSize = 8;
  }
  if (type == 2) {
    size = 8;
    max = max - 1;
    if (curGpr & 1)
      even = 1;
    inc = 2;
  }
  if (curGpr < max) {
    curGpr += even;
    addr = ap->reg_save_area + fprOffset + (curGpr * regSize);
    *curGprPtr = curGpr + inc;
  } else {
    *curGprPtr = 8;
    addr = ap->input_arg_area;
    addr = (char*)ALIGN(addr, size);
    ap->input_arg_area = addr + size;
  }
  if (type == ARGPOINTER)
    addr = *((char**)addr);

  return addr;
}