diff options
Diffstat (limited to 'src/uxn/uxn.c')
-rw-r--r-- | src/uxn/uxn.c | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/src/uxn/uxn.c b/src/uxn/uxn.c new file mode 100644 index 0000000..796a980 --- /dev/null +++ b/src/uxn/uxn.c | |||
@@ -0,0 +1,196 @@ | |||
1 | #include <stdio.h> | ||
2 | #include "uxn.h" | ||
3 | |||
4 | /* | ||
5 | Copyright (u) 2021 Devine Lu Linvega | ||
6 | |||
7 | Permission to use, copy, modify, and distribute this software for any | ||
8 | purpose with or without fee is hereby granted, provided that the above | ||
9 | copyright notice and this permission notice appear in all copies. | ||
10 | |||
11 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
12 | WITH REGARD TO THIS SOFTWARE. | ||
13 | */ | ||
14 | |||
15 | #pragma mark - Operations | ||
16 | |||
17 | /* clang-format off */ | ||
18 | void push8(Stack *s, Uint8 a) { if (s->ptr == 0xff) { s->error = 2; return; } s->dat[s->ptr++] = a; } | ||
19 | Uint8 pop8_keep(Stack *s) { if (s->kptr == 0) { s->error = 1; return 0; } return s->dat[--s->kptr]; } | ||
20 | Uint8 pop8_nokeep(Stack *s) { if (s->ptr == 0) { s->error = 1; return 0; } return s->dat[--s->ptr]; } | ||
21 | static Uint8 (*pop8)(Stack *s); | ||
22 | void mempoke8(Uint8 *m, Uint16 a, Uint8 b) { m[a] = b; } | ||
23 | Uint8 mempeek8(Uint8 *m, Uint16 a) { return m[a]; } | ||
24 | void devpoke8(Device *d, Uint8 a, Uint8 b) { d->dat[a & 0xf] = b; d->talk(d, a & 0x0f, 1); } | ||
25 | Uint8 devpeek8(Device *d, Uint8 a) { d->talk(d, a & 0x0f, 0); return d->dat[a & 0xf]; } | ||
26 | void push16(Stack *s, Uint16 a) { push8(s, a >> 8); push8(s, a); } | ||
27 | Uint16 pop16(Stack *s) { return pop8(s) + (pop8(s) << 8); } | ||
28 | void mempoke16(Uint8 *m, Uint16 a, Uint16 b) { mempoke8(m, a, b >> 8); mempoke8(m, a + 1, b); } | ||
29 | Uint16 mempeek16(Uint8 *m, Uint16 a) { return (mempeek8(m, a) << 8) + mempeek8(m, a + 1); } | ||
30 | void devpoke16(Device *d, Uint8 a, Uint16 b) { devpoke8(d, a, b >> 8); devpoke8(d, a + 1, b); } | ||
31 | Uint16 devpeek16(Device *d, Uint16 a) { return (devpeek8(d, a) << 8) + devpeek8(d, a + 1); } | ||
32 | /* Stack */ | ||
33 | void op_brk(Uxn *u) { u->ram.ptr = 0; } | ||
34 | void op_nop(Uxn *u) { (void)u; } | ||
35 | void op_lit(Uxn *u) { push8(u->src, mempeek8(u->ram.dat, u->ram.ptr++)); } | ||
36 | void op_pop(Uxn *u) { pop8(u->src); } | ||
37 | void op_dup(Uxn *u) { Uint8 a = pop8(u->src); push8(u->src, a); push8(u->src, a); } | ||
38 | void op_swp(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, a); push8(u->src, b); } | ||
39 | void op_ovr(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b); push8(u->src, a); push8(u->src, b); } | ||
40 | void op_rot(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src), c = pop8(u->src); push8(u->src, b); push8(u->src, a); push8(u->src, c); } | ||
41 | /* Logic */ | ||
42 | void op_equ(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b == a); } | ||
43 | void op_neq(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b != a); } | ||
44 | void op_gth(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b > a); } | ||
45 | void op_lth(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b < a); } | ||
46 | void op_jmp(Uxn *u) { Uint8 a = pop8(u->src); u->ram.ptr += (Sint8)a; } | ||
47 | void op_jnz(Uxn *u) { Uint8 a = pop8(u->src); if (pop8(u->src)) u->ram.ptr += (Sint8)a; } | ||
48 | void op_jsr(Uxn *u) { Uint8 a = pop8(u->src); push16(u->dst, u->ram.ptr); u->ram.ptr += (Sint8)a; } | ||
49 | void op_sth(Uxn *u) { Uint8 a = pop8(u->src); push8(u->dst, a); } | ||
50 | /* Memory */ | ||
51 | void op_pek(Uxn *u) { Uint8 a = pop8(u->src); push8(u->src, mempeek8(u->ram.dat, a)); } | ||
52 | void op_pok(Uxn *u) { Uint8 a = pop8(u->src); Uint8 b = pop8(u->src); mempoke8(u->ram.dat, a, b); } | ||
53 | void op_ldr(Uxn *u) { Uint8 a = pop8(u->src); push8(u->src, mempeek8(u->ram.dat, u->ram.ptr + (Sint8)a)); } | ||
54 | void op_str(Uxn *u) { Uint8 a = pop8(u->src); Uint8 b = pop8(u->src); mempoke8(u->ram.dat, u->ram.ptr + (Sint8)a, b); } | ||
55 | void op_lda(Uxn *u) { Uint16 a = pop16(u->src); push8(u->src, mempeek8(u->ram.dat, a)); } | ||
56 | void op_sta(Uxn *u) { Uint16 a = pop16(u->src); Uint8 b = pop8(u->src); mempoke8(u->ram.dat, a, b); } | ||
57 | void op_dei(Uxn *u) { Uint8 a = pop8(u->src); push8(u->src, devpeek8(&u->dev[a >> 4], a)); } | ||
58 | void op_deo(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); devpoke8(&u->dev[a >> 4], a, b); } | ||
59 | /* Arithmetic */ | ||
60 | void op_add(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b + a); } | ||
61 | void op_sub(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b - a); } | ||
62 | void op_mul(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b * a); } | ||
63 | void op_div(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b / a); } | ||
64 | void op_and(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b & a); } | ||
65 | void op_ora(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b | a); } | ||
66 | void op_eor(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b ^ a); } | ||
67 | void op_sft(Uxn *u) { Uint8 a = pop8(u->src), b = pop8(u->src); push8(u->src, b >> (a & 0x07) << ((a & 0x70) >> 4)); } | ||
68 | /* Stack */ | ||
69 | void op_lit16(Uxn *u) { push16(u->src, mempeek16(u->ram.dat, u->ram.ptr++)); u->ram.ptr++; } | ||
70 | void op_pop16(Uxn *u) { pop16(u->src); } | ||
71 | void op_dup16(Uxn *u) { Uint16 a = pop16(u->src); push16(u->src, a); push16(u->src, a); } | ||
72 | void op_swp16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, a); push16(u->src, b); } | ||
73 | void op_ovr16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b); push16(u->src, a); push16(u->src, b); } | ||
74 | void op_rot16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src), c = pop16(u->src); push16(u->src, b); push16(u->src, a); push16(u->src, c); } | ||
75 | /* Logic(16-bits) */ | ||
76 | void op_equ16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b == a); } | ||
77 | void op_neq16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b != a); } | ||
78 | void op_gth16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b > a); } | ||
79 | void op_lth16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push8(u->src, b < a); } | ||
80 | void op_jmp16(Uxn *u) { u->ram.ptr = pop16(u->src); } | ||
81 | void op_jnz16(Uxn *u) { Uint16 a = pop16(u->src); if (pop8(u->src)) u->ram.ptr = a; } | ||
82 | void op_jsr16(Uxn *u) { push16(u->dst, u->ram.ptr); u->ram.ptr = pop16(u->src); } | ||
83 | void op_sth16(Uxn *u) { Uint16 a = pop16(u->src); push16(u->dst, a); } | ||
84 | /* Memory(16-bits) */ | ||
85 | void op_pek16(Uxn *u) { Uint8 a = pop8(u->src); push16(u->src, mempeek16(u->ram.dat, a)); } | ||
86 | void op_pok16(Uxn *u) { Uint8 a = pop8(u->src); Uint16 b = pop16(u->src); mempoke16(u->ram.dat, a, b); } | ||
87 | void op_ldr16(Uxn *u) { Uint8 a = pop8(u->src); push16(u->src, mempeek16(u->ram.dat, u->ram.ptr + (Sint8)a)); } | ||
88 | void op_str16(Uxn *u) { Uint8 a = pop8(u->src); Uint16 b = pop16(u->src); mempoke16(u->ram.dat, u->ram.ptr + (Sint8)a, b); } | ||
89 | void op_lda16(Uxn *u) { Uint16 a = pop16(u->src); push16(u->src, mempeek16(u->ram.dat, a)); } | ||
90 | void op_sta16(Uxn *u) { Uint16 a = pop16(u->src); Uint16 b = pop16(u->src); mempoke16(u->ram.dat, a, b); } | ||
91 | void op_dei16(Uxn *u) { Uint8 a = pop8(u->src); push16(u->src, devpeek16(&u->dev[a >> 4], a)); } | ||
92 | void op_deo16(Uxn *u) { Uint8 a = pop8(u->src); Uint16 b = pop16(u->src); devpoke16(&u->dev[a >> 4], a, b); } | ||
93 | /* Arithmetic(16-bits) */ | ||
94 | void op_add16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b + a); } | ||
95 | void op_sub16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b - a); } | ||
96 | void op_mul16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b * a); } | ||
97 | void op_div16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b / a); } | ||
98 | void op_and16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b & a); } | ||
99 | void op_ora16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b | a); } | ||
100 | void op_eor16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b ^ a); } | ||
101 | void op_sft16(Uxn *u) { Uint16 a = pop16(u->src), b = pop16(u->src); push16(u->src, b >> (a & 0x000f) << ((a & 0x00f0) >> 4)); } | ||
102 | |||
103 | void (*ops[])(Uxn *u) = { | ||
104 | op_brk, op_lit, op_nop, op_pop, op_dup, op_swp, op_ovr, op_rot, | ||
105 | op_equ, op_neq, op_gth, op_lth, op_jmp, op_jnz, op_jsr, op_sth, | ||
106 | op_pek, op_pok, op_ldr, op_str, op_lda, op_sta, op_dei, op_deo, | ||
107 | op_add, op_sub, op_mul, op_div, op_and, op_ora, op_eor, op_sft, | ||
108 | /* 16-bit */ | ||
109 | op_brk, op_lit16, op_nop, op_pop16, op_dup16, op_swp16, op_ovr16, op_rot16, | ||
110 | op_equ16, op_neq16, op_gth16, op_lth16, op_jmp16, op_jnz16, op_jsr16, op_sth16, | ||
111 | op_pek16, op_pok16, op_ldr16, op_str16, op_lda16, op_sta16, op_dei16, op_deo16, | ||
112 | op_add16, op_sub16, op_mul16, op_div16, op_and16, op_ora16, op_eor16, op_sft16 | ||
113 | }; | ||
114 | |||
115 | /* clang-format on */ | ||
116 | |||
117 | #pragma mark - Core | ||
118 | |||
119 | int | ||
120 | haltuxn(Uxn *u, char *name, int id) | ||
121 | { | ||
122 | txt_printf("Halted: %s#%04x, at 0x%04x\n", name, id, u->ram.ptr); | ||
123 | u->ram.ptr = 0; | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | void | ||
128 | opcuxn(Uxn *u, Uint8 instr) | ||
129 | { | ||
130 | Uint8 op = instr & 0x3f, freturn = instr & 0x40, fkeep = instr & 0x80; | ||
131 | u->src = freturn ? &u->rst : &u->wst; | ||
132 | u->dst = freturn ? &u->wst : &u->rst; | ||
133 | if(fkeep) { | ||
134 | pop8 = pop8_keep; | ||
135 | u->src->kptr = u->src->ptr; | ||
136 | } else { | ||
137 | pop8 = pop8_nokeep; | ||
138 | } | ||
139 | (*ops[op])(u); | ||
140 | } | ||
141 | |||
142 | int | ||
143 | stepuxn(Uxn *u, Uint8 instr) | ||
144 | { | ||
145 | opcuxn(u, instr); | ||
146 | if(u->wst.error) | ||
147 | return haltuxn(u, u->wst.error == 1 ? "Working-stack underflow" : "Working-stack overflow", instr); | ||
148 | if(u->rst.error) | ||
149 | return haltuxn(u, u->rst.error == 1 ? "Return-stack underflow" : "Return-stack overflow", instr); | ||
150 | return 1; | ||
151 | } | ||
152 | |||
153 | int | ||
154 | evaluxn(Uxn *u, Uint16 vec) | ||
155 | { | ||
156 | u->ram.ptr = vec; | ||
157 | u->wst.error = 0; | ||
158 | u->rst.error = 0; | ||
159 | while(u->ram.ptr) | ||
160 | if(!stepuxn(u, u->ram.dat[u->ram.ptr++])) | ||
161 | return 0; | ||
162 | return 1; | ||
163 | } | ||
164 | |||
165 | int | ||
166 | bootuxn(Uxn *u) | ||
167 | { | ||
168 | size_t i; | ||
169 | char *cptr = (char *)u; | ||
170 | for(i = 0; i < sizeof(*u); i++) | ||
171 | cptr[i] = 0; | ||
172 | return 1; | ||
173 | } | ||
174 | |||
175 | int | ||
176 | loaduxn(Uxn *u, char *filepath) | ||
177 | { | ||
178 | FILE *f; | ||
179 | if(!(f = fopen(filepath, "rb"))) | ||
180 | return haltuxn(u, "Missing input rom.", 0); | ||
181 | fread(u->ram.dat + PAGE_PROGRAM, sizeof(u->ram.dat) - PAGE_PROGRAM, 1, f); | ||
182 | txt_printf("Uxn loaded[%s].\n", filepath); | ||
183 | return 1; | ||
184 | } | ||
185 | |||
186 | Device * | ||
187 | portuxn(Uxn *u, Uint8 id, char *name, void (*talkfn)(Device *d, Uint8 b0, Uint8 w)) | ||
188 | { | ||
189 | Device *d = &u->dev[id]; | ||
190 | d->addr = id * 0x10; | ||
191 | d->u = u; | ||
192 | d->mem = u->ram.dat; | ||
193 | d->talk = talkfn; | ||
194 | txt_printf("Device added #%02x: %s, at 0x%04x \n", id, name, d->addr); | ||
195 | return d; | ||
196 | } | ||