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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
#ifndef AHOY_C8ASM_PARSER_H
#define AHOY_C8ASM_PARSER_H
#include <stddef.h>
#include <stdint.h>
#include <mbstring.h>
struct tokens;
typedef enum {
I_ADD_I_VX,
I_ADD_VX_B,
I_ADD_VX_VY,
I_AND,
I_BCD,
I_CALL,
I_CLS,
I_DB,
I_DRW,
I_HEX,
I_JP_A,
I_JP_V0_A,
I_LD_DT,
I_LD_I,
I_LD_ST,
I_LD_VX_B,
I_LD_VX_DT,
I_LD_VX_K,
I_LD_VX_VY,
I_OR,
I_RET,
I_RND,
I_RSTR,
I_SE_VX_B,
I_SE_VX_VY,
I_SHL,
I_SHR,
I_SKNP,
I_SKP,
I_SNE_VX_B,
I_SNE_VX_VY,
I_STOR,
I_SUB,
I_SUBN,
I_SYS,
I_XOR,
} instrkind;
typedef enum {
R_V0,
R_V1,
R_V2,
R_V3,
R_V4,
R_V5,
R_V6,
R_V7,
R_V8,
R_V9,
R_VA,
R_VB,
R_VC,
R_VD,
R_VE,
R_VF,
R_I,
R_K,
R_DT,
R_ST,
} reg;
typedef enum {
D_INSTR,
D_LABEL,
} dirkind;
/* Arguments can always be represented by a uint16_t, however the parser is not
responsible for assigning addresses to labels. As a result an arg at this
stage can be either a uint16_t or the name of a label. */
struct raw_addr {
bool label;
union {
uint16_t val;
struct u8view sv;
};
};
struct instr {
instrkind kind;
/* The most arguments any instruction can take is 3, so it’s more efficient
to just store the arguments in a fixed-size array. The only exception is
the ‘db’ instruction which takes a variable-number of arguments, so in
that case we use a dynamic array. */
union {
struct raw_addr args[3];
struct {
uint8_t *buf;
size_t cap;
};
};
size_t len;
};
struct dir {
dirkind kind;
union {
struct u8view name;
struct instr instr;
};
};
struct ast {
struct dir *buf;
size_t len, cap;
};
struct ast parsefile(struct tokens);
#endif /* !AHOY_C8ASM_PARSER_H */
|