-
Notifications
You must be signed in to change notification settings - Fork 0
/
cpu.c
248 lines (216 loc) · 5.57 KB
/
cpu.c
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "main.h"
#ifdef WIN32
#include <conio.h> // _kbhit()
#endif
static int *lv, *rv;
static int lvvirtual, rvvirtual;
static void push_inner(int reg)
{
if (SPVALUE < (STACKTOP - STACKELEMENTS))
{
printf("RUNTIME ERROR, STACK OVERFLOW\n");
exit(-1);
}
mem[SPVALUE] = mem[reg];
SPVALUE--; /* Decrement */
}
int dokbd(void)
{
#ifdef WIN32
if (_kbhit()) {
mem[INPORTC_LOCATION] = _getch();
if (mem[INPORTC_LOCATION] == '\r')
mem[INPORTC_LOCATION] = '\n'; // _getch only ever returns '\r'
return 1;
}
#else
if (posix_kbhit())
{
int test = getchar();
mem[INPORTC_LOCATION] = test;
return 1;
}
#endif
return 0;
}
/* Interrupt request, basically same as CALL, but to location specified in vector table based on interrupt number */
void inner_irq(int lam, int ram, int irqnum)
{
pushu(lam, ram); /* Push all user registers, (not PC, SP or R15!) */
push_inner(PCREGISTER); /* Push PC */
if (irqnum < 0 || irqnum > 15) {
printf("RUNTIME ERROR: Illegal IRQ number %d\n", irqnum);
exit(-1);
}
PCVALUE = mem[VECTOR_BASE + irqnum]; /* Set PC to dereference of vector table */
if (PCVALUE < VECTOR_COUNT || PCVALUE > MEMWORDS) {
printf("RUNTIME ERROR: Illegal PC value (%d) from IRQ (%d) deref (%d) in vector table\n", PCVALUE, irqnum, mem[VECTOR_BASE + irqnum]);
}
}
uint64_t runtime(void)
{
uint64_t cycles = 0;
int opcode;
int lam, ram;
PCVALUE = STARTPOS;
SPVALUE = STACKTOP; /* Empty stack */
while(!stoprun)
{
if ((cycles % 1000) == 0 && dokbd()) /* Raise an interrupt if keyboard hit */
{
PCVALUE = PCVALUE - 2; /* Pre-decrement PC twice, (because RET and then MAIN CODE is going to increment it!) */
inner_irq(0, 0, KEYPRESS_INTERRUPT);
}
opcode = PCTARGET & 0x000000FF; /* 0-7 (instruction code) */
lam = (PCTARGET & 0x0000FF00) >> 8; /* 8-15 (left hand addressing mode) */
ram = (PCTARGET & 0x00FF0000) >> 16; /* 16-23 (right hand addressing mode) */
if (opcode < INSTRUCTION_COUNT) {
setlvrv(lam, ram, opcode);
instructions[opcode].handler(lam, ram);
if (!absolute)
PCVALUE += (instructions[opcode].length);
absolute = 0;
}
else {
printf("Unknown operand at memory location %d\nExiting...", PCVALUE);
exit(-1);
}
cycles++;
}
return cycles;
}
/* TODO: Break into subroutine and caller with lv/rv and offset (1/2) */
void setlvrv(int lam, int ram, int opcode)
{
lvvirtual = 0;
rvvirtual = 0;
lv = (int *)0;
rv = (int *)0;
switch (lam)
{
case INS_DEREF:
if (PCTARGETPLUS1 > MEMWORDS) lvvirtual = PCTARGETPLUS1;
lv = &PCDEREFPLUS1;
break;
case INS_ADDRDEREF:
lv = &PCADDRESSDEREFPLUS1;
break;
case INS_IMMEDIATE:
lv = &PCTARGETPLUS1;
break;
}
if (instructions[opcode].length <= 2) return; /* Can't check RHS for 2-operand instructions! */
switch (ram)
{
case INS_DEREF:
if (PCTARGETPLUS2 > MEMWORDS) rvvirtual = PCTARGETPLUS2;
rv = &PCDEREFPLUS2;
break;
case INS_ADDRDEREF:
rv = &PCADDRESSDEREFPLUS2;
break;
case INS_IMMEDIATE:
rv = &PCTARGETPLUS2;
break;
}
}
/* INSTRUCTIONS */
void mov(int lam, int ram)
{
if (lvvirtual)
{
if (lvvirtual == OUTPORTC_LOCATION)
putchar((char)*rv);
else if (lvvirtual == OUTPORTI_LOCATION)
printf("%d", *rv);
}
else if (rvvirtual && rvvirtual == INPORTC_LOCATION)
{
*lv = mem[INPORTC_LOCATION];
}
else
*lv = *rv;
return;
}
void irq(int lam, int ram)
{
inner_irq(lam, ram, PCTARGETPLUS1);
absolute = 1; /* Required! */
}
void mod(int lam, int ram)
{
*lv = *lv % *rv;
return;
}
void cmp(int lam, int ram)
{
eq = (*lv == *rv);
c = (*lv >= *rv) ? 1 : 0;
return;
}
void jmp_inner(int lam, int ram, int flag)
{
if (flag) {
PCVALUE = PCTARGETPLUS1;
absolute=1;
}
}
static void pop_inner(int reg)
{
if (SPVALUE == STACKTOP)
{
printf("\n\n Can't POP without PUSH!\n\n");
exit(-1);
}
SPVALUE++; /* Increment */
mem[reg] = mem[SPVALUE]; /* Dereference SP */
}
void jeq(int lam, int ram) { jmp_inner(lam, ram, eq); }
void jnq(int lam, int ram) { jmp_inner(lam, ram, !eq); }
void jgt(int lam, int ram) { jmp_inner(lam, ram, (c && !eq)); }
void jlt(int lam, int ram) { jmp_inner(lam, ram, (!c && !eq)); }
void jmp(int lam, int ram) { jmp_inner(lam, ram, 1); }
void push(int lam, int ram) { push_inner(PCTARGETPLUS1); }
void pop(int lam, int ram) { pop_inner(PCTARGETPLUS1); }
void pushu(int lam, int ram) /* Push all user registers, (not PC, SP or R15 (return register)) */
{
int reg;
for (reg=2; reg < 15; reg++) {
push_inner(reg);
}
}
void popu(int lam, int ram) /* Pop all user registers, (not PC, SP or R15 (return register)) */
{
int reg;
for(reg=14; reg >= 2; reg--) {
pop_inner(reg);
}
}
void call(int lam, int ram)
{
pushu(lam, ram); /* Push all user registers, (not PC, SP or R15!) */
push_inner(PCREGISTER);
PCVALUE = PCTARGETPLUS1;
absolute = 1;
}
void ret(int lam, int ram)
{
pop_inner(PCREGISTER); /* Let runtime increment pc for us! */
popu(lam, ram); /* Pop all user registers (not PC, SP or R15!) */
PCVALUE = PCVALUE + 1; /* increment PC, runtime will increment again. (Ie skip *2* bytes, ie "CALL <loc>") */
}
void inc(int lam, int ram) { PCDEREFPLUS1 = PCDEREFPLUS1 + 1; }
void dec(int lam, int ram) { PCDEREFPLUS1 = PCDEREFPLUS1 - 1; }
void mul(int lam, int ram) { *lv = (*lv) * (*rv); } /* No overflow check! */
void add(int lam, int ram) { *lv = (*lv) + (*rv); } /* No overflow check! */
void sub(int lam, int ram) { *lv = (*lv) - (*rv); } /* No overflow check! */
void nop(int lam, int ram) {}
void hlt(int lam, int ram)
{
printf("\nGot HALT at location %d (%d)\n", PCREGISTER, mem[PCREGISTER]);
stoprun=1;
}