comparison src/cpu.c @ 0:7e44f7d5810b

Initial commit. CPU working well enough for simple hello world program.
author Michael Pavone <pavone@retrodev.com>
date Tue, 22 Mar 2016 22:44:02 -0700
parents
children 6204c81e2933
comparison
equal deleted inserted replaced
-1:000000000000 0:7e44f7d5810b
1 #include <stdint.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include "cpu.h"
6
7 enum {
8 EXCEPTION_INTERRUPT_0,
9 EXCEPTION_INTERRUPT_1,
10 EXCEPTION_UNALIGNED_READ,
11 EXCEPTION_INVALID_INSTRUCTION
12 };
13
14 enum {
15 STATE_NEED_FETCH,
16 STATE_NORMAL,
17 STATE_EXCEPTION_START
18 };
19
20 #define STATUS_INT0_ENABLE 1
21 #define STATUS_INT1_ENABLE 2
22 #define FLAG_Z 4
23 #define FLAG_C 8
24 #define FLAG_N 16
25
26 #define REG_PC 14
27 #define REG_SR 15
28
29 cpu* alloc_cpu(uint32_t clock_divider, uint32_t num_regions, memory_region *regions)
30 {
31 size_t alloc_size = sizeof(cpu) + sizeof(memory_region) * num_regions;
32 cpu *context = malloc(alloc_size);
33 memset(context, 0, alloc_size);
34 context->clock_inc = clock_divider;
35 context->num_mem_regions = num_regions;
36 memcpy(context->mem_regions, regions, num_regions*sizeof(memory_region));
37
38 return context;
39 }
40
41 uint16_t cpu_read_16(cpu *context, uint16_t address)
42 {
43 context->cycles += context->clock_inc;
44 if (address & 1) {
45 context->exception = EXCEPTION_UNALIGNED_READ;
46 context->state = STATE_EXCEPTION_START;
47 return 0xFFFF;
48 }
49 memory_region *cur = context->mem_regions;
50 for (memory_region *end = cur + context->num_mem_regions; cur < end; cur++)
51 {
52 if (address >= cur->start && address <= cur->end && (cur->flags & MEM_READ)) {
53 return cur->base[address - cur->start] << 8 | cur->base[address - cur->start + 1];
54 }
55 }
56 return 0xFFFF;
57 }
58
59 uint8_t cpu_read_8(cpu *context, uint16_t address)
60 {
61 context->cycles += context->clock_inc;
62 memory_region *cur = context->mem_regions;
63 for (memory_region *end = cur + context->num_mem_regions; cur < end; cur++)
64 {
65 if (address >= cur->start && address <= cur->end && (cur->flags & MEM_READ)) {
66 return cur->base[address - cur->start];
67 }
68 }
69 return 0xFF;
70 }
71
72 void cpu_write_16(cpu *context, uint16_t address, uint16_t value)
73 {
74 context->cycles += context->clock_inc;
75 if (address & 1) {
76 context->exception = EXCEPTION_UNALIGNED_READ;
77 context->state = STATE_EXCEPTION_START;
78 return;
79 }
80 memory_region *cur = context->mem_regions;
81 for (memory_region *end = cur + context->num_mem_regions; cur < end; cur++)
82 {
83 if (address >= cur->start && address <= cur->end && (cur->flags & MEM_WRITE)) {
84 cur->base[address - cur->start] = value >> 8;
85 cur->base[address - cur->start + 1] = value;
86 break;
87 }
88 }
89 }
90
91 void cpu_write_8(cpu *context, uint16_t address, uint8_t value)
92 {
93 context->cycles += context->clock_inc;
94 memory_region *cur = context->mem_regions;
95 for (memory_region *end = cur + context->num_mem_regions; cur < end; cur++)
96 {
97 if (address >= cur->start && address <= cur->end && (cur->flags & MEM_WRITE)) {
98 cur->base[address - cur->start] = value;
99 break;
100 }
101 }
102 }
103
104 uint16_t cpu_read_port(cpu *context, uint8_t port)
105 {
106 port &= 0xF;
107 if (context->port_handlers[port].read) {
108 return context->port_handlers[port].read(context, port);
109 }
110 return 0xFFFF;
111 }
112
113 void cpu_write_port(cpu *context, uint8_t port, uint16_t value)
114 {
115 port &= 0xF;
116 if (context->port_handlers[port].write) {
117 context->port_handlers[port].write(context, port, value);
118 }
119 }
120
121 void fetch_instruction(cpu *context)
122 {
123 context->prefetch = cpu_read_16(context, context->regs[REG_PC]);
124 context->regs[REG_PC] += 2;
125 context->state = STATE_NORMAL;
126 }
127
128 void vector_fetch(cpu *context)
129 {
130 context->exception_pc = context->regs[REG_PC] - 2;
131 context->exception_sr = context->regs[REG_SR];
132 context->regs[REG_SR] &= ~(STATUS_INT0_ENABLE | STATUS_INT1_ENABLE);
133 context->regs[REG_PC] = cpu_read_16(context, context->vector_base + context->exception);
134 context->state = STATE_NEED_FETCH;
135 }
136
137 uint16_t sign_extend(uint16_t val)
138 {
139 if (val & 0x80) {
140 return val | 0xFF00;
141 }
142 return val;
143 }
144
145 void update_flags_arith(cpu *context, uint32_t result)
146 {
147 context->regs[REG_SR] &= ~(FLAG_N|FLAG_C|FLAG_Z);
148 if (!(result & 0xFFFF)) {
149 context->regs[REG_SR] |= FLAG_Z;
150 }
151 if (result &= 0x8000) {
152 context->regs[REG_SR] |= FLAG_N;
153 }
154 if (result &= 0x10000) {
155 context->regs[REG_SR] |= FLAG_C;
156 }
157 }
158
159 void update_flags_bitwise(cpu *context, uint32_t result)
160 {
161 context->regs[REG_SR] &= ~(FLAG_N|FLAG_Z);
162 if (!(result & 0xFFFF)) {
163 context->regs[REG_SR] |= FLAG_Z;
164 }
165 if (result &= 0x8000) {
166 context->regs[REG_SR] |= FLAG_N;
167 }
168 }
169
170 void run_bcc(cpu *context, uint8_t condition, uint8_t a, uint8_t b)
171 {
172
173 uint8_t doit = 0;
174 switch (condition)
175 {
176 case COND_ALWAYS:
177 doit = 1;
178 break;
179 case COND_NEVER:
180 break;
181 case COND_ZERO:
182 doit = context->regs[REG_SR] & FLAG_Z;
183 break;
184 case COND_NZERO:
185 doit = !(context->regs[REG_SR] & FLAG_Z);
186 break;
187 case COND_NEG:
188 doit = context->regs[REG_SR] & FLAG_N;
189 break;
190 case COND_POS:
191 doit = !(context->regs[REG_SR] & FLAG_N);
192 break;
193 case COND_CARRY:
194 doit = context->regs[REG_SR] & FLAG_C;
195 break;
196 case COND_NCARRY:
197 doit = context->regs[REG_SR] & FLAG_C;
198 break;
199 case COND_GREATER:
200 //not zero and not carry
201 doit = !(context->regs[REG_SR] & FLAG_Z) || !(context->regs[REG_SR] & FLAG_C);
202 break;
203 case COND_LEQ:
204 //zero or carry
205 doit = (context->regs[REG_SR] & FLAG_Z) || (context->regs[REG_SR] & FLAG_C);
206 break;
207 default:
208 context->exception = EXCEPTION_INVALID_INSTRUCTION;
209 context->state = STATE_EXCEPTION_START;
210 return;
211 }
212
213 if (doit) {
214 context->regs[REG_PC] += sign_extend(a << 4 | b) * 2;
215 context->state = STATE_NEED_FETCH;
216 }
217 }
218
219 uint16_t format_immediate_bitwise(uint16_t val)
220 {
221 if (val & 8) {
222 val |= 0xFFF0;
223 }
224 return val;
225 }
226
227 uint16_t format_immediate(uint16_t val)
228 {
229 val = format_immediate_bitwise(val);
230 if (!val) {
231 val = 8;
232 }
233 return val;
234 }
235
236 void run_single_reg(cpu *context, uint8_t dst, uint8_t op)
237 {
238 switch(op)
239 {
240 case RETI:
241 context->regs[dst] = context->exception_ur;
242 context->regs[REG_PC] = context->exception_pc;
243 context->regs[REG_SR] = context->exception_sr;
244 context->state = STATE_NEED_FETCH;
245 return;
246 case TRAP:
247 context->state = STATE_EXCEPTION_START;
248 context->exception = context->regs[dst];
249 return;
250 case TRAPI:
251 context->state = STATE_EXCEPTION_START;
252 context->exception = dst;
253 return;
254 case GETEPC:
255 context->regs[dst] = context->exception_pc;
256 break;
257 case SETEPC:
258 context->exception_pc = context->regs[dst];
259 break;
260 case GETESR:
261 context->regs[dst] = context->exception_sr;
262 break;
263 case SETESR:
264 context->exception_sr = context->regs[dst];
265 break;
266 case GETEUR:
267 context->regs[dst] = context->exception_ur;
268 break;
269 case SETEUR:
270 context->exception_ur = context->regs[dst];
271 break;
272 case GETENUM:
273 context->regs[dst] = context->exception;
274 break;
275 case SETENUM:
276 context->exception = context->regs[dst];
277 break;
278 default:
279 context->state = STATE_EXCEPTION_START;
280 context->exception = EXCEPTION_INVALID_INSTRUCTION;
281 return;
282 }
283 if (dst == REG_PC) {
284 context->state = STATE_NEED_FETCH;
285 }
286 }
287
288 void run_single_source(cpu *context, uint8_t dst, uint8_t a, uint8_t op)
289 {
290 uint32_t tmp;
291 uint8_t shift;
292 switch(op)
293 {
294 case MOVE:
295 context->regs[dst] = context->regs[a];
296 break;
297 case NEG:
298 tmp = -context->regs[a];
299 context->regs[dst] = tmp;
300 update_flags_arith(context, tmp);
301 break;
302 case NOT:
303 context->regs[dst] = ~context->regs[a];
304 update_flags_bitwise(context, context->regs[dst]);
305 break;
306 case CMP:
307 tmp = context->regs[dst] - context->regs[a];
308 update_flags_arith(context, tmp);
309 return;
310 case CALL:
311 context->regs[dst] = context->regs[REG_PC] - 2;
312 context->regs[REG_PC] = context->regs[a];
313 context->state = STATE_NEED_FETCH;
314 return;
315 case SWAP:
316 tmp = context->regs[dst];
317 context->regs[dst] = context->regs[a];
318 context->regs[a] = tmp;
319 if (a == REG_PC) {
320 context->state = STATE_NEED_FETCH;
321 return;
322 }
323 break;
324 case IN:
325 context->regs[dst] = cpu_read_port(context, context->regs[a]);
326 break;
327 case OUT:
328 cpu_write_port(context, context->regs[a], context->regs[dst]);
329 return;
330 case INI:
331 context->regs[dst] = cpu_read_port(context, a);
332 break;
333 case OUTI:
334 cpu_write_port(context, a, context->regs[dst]);
335 return;
336 case ADDI:
337 tmp = context->regs[dst] + format_immediate(a);
338 context->regs[dst] = tmp;
339 update_flags_arith(context, tmp);
340 break;
341 case ANDI:
342 context->regs[dst] = context->regs[dst] & format_immediate_bitwise(a);
343 update_flags_bitwise(context, context->regs[dst]);
344 break;
345 case ORI:
346 context->regs[dst] = context->regs[dst] | format_immediate_bitwise(a);
347 update_flags_bitwise(context, context->regs[dst]);
348 break;
349 case LSI:
350 shift = a & 7;
351 if (!shift) {
352 shift = 8;
353 }
354 if (a & 8) {
355 tmp = context->regs[dst] >> shift;
356 tmp |= (context->regs[dst] >> (shift - 1)) << 16 & 0x10000;
357 } else {
358 tmp = context->regs[dst] << (a & 7);
359 }
360 context->regs[dst] = tmp;
361 update_flags_arith(context, tmp);
362 break;
363 case ASRI:
364 shift = a;
365 if (!shift) {
366 shift = 16;
367 }
368 tmp = context->regs[dst];
369 if (tmp & 0x8000) {
370 tmp |= 0xFFFF0000;
371 }
372 tmp = tmp >> shift & 0xFFFF;
373 tmp |= (context->regs[dst] >> (context->regs[shift] - 1)) << 16 & 0x10000;
374 context->regs[dst] = tmp;
375 update_flags_arith(context, tmp);
376 break;
377 case SINGLE_REG:
378 run_single_reg(context, dst, a);
379 return;
380 }
381 if (dst == REG_PC) {
382 context->state = STATE_NEED_FETCH;
383 }
384 }
385
386 char * mnemonics[] = {
387 "ldim", "ldimh", "ld8", "ld16", "str8", "str16", "add", "adc", "and", "or", "xor", "lsl", "lsr", "asr", "bcc", "single"
388 };
389
390 void run_instruction(cpu *context)
391 {
392 uint16_t instruction = context->prefetch;
393 fetch_instruction(context);
394 uint8_t dst = instruction >> 12;
395 uint8_t a = instruction >> 8 & 0xF;
396 uint8_t b = instruction >> 4 & 0xF;
397 uint8_t op = instruction & 0xF;
398 uint32_t tmp;
399 switch (op)
400 {
401 case LDIM:
402 context->regs[dst] = sign_extend(a << 4 | b);
403 break;
404 case LDIMH:
405 context->regs[dst] &= 0xFF;
406 context->regs[dst] |= a << 12 | b << 8;
407 break;
408 case LD8:
409 context->regs[dst] = cpu_read_8(context, context->regs[a] + context->regs[b]);
410 break;
411 case LD16:
412 context->regs[dst] = cpu_read_16(context, context->regs[a] + context->regs[b]);
413 break;
414 case STR8:
415 cpu_write_8(context, context->regs[a] + context->regs[b], context->regs[dst]);
416 return;
417 case STR16:
418 cpu_write_16(context, context->regs[a] + context->regs[b], context->regs[dst]);
419 return;
420 case ADD:
421 tmp = context->regs[a] + context->regs[b];
422 context->regs[dst] = tmp;
423 update_flags_arith(context, tmp);
424 break;
425 case ADC:
426 tmp = context->regs[a] + context->regs[b] + (context->regs[REG_SR] & FLAG_C ? 1 : 0);
427 context->regs[dst] = tmp;
428 update_flags_arith(context, tmp);
429 break;
430 case AND:
431 context->regs[dst] = context->regs[a] & context->regs[b];
432 update_flags_bitwise(context, context->regs[dst]);
433 break;
434 case OR:
435 context->regs[dst] = context->regs[a] | context->regs[b];
436 update_flags_bitwise(context, context->regs[dst]);
437 break;
438 case XOR:
439 context->regs[dst] = context->regs[a] ^ context->regs[b];
440 update_flags_bitwise(context, context->regs[dst]);
441 break;
442 case LSL:
443 tmp = context->regs[a] << context->regs[b];
444 context->regs[dst] = tmp;
445 update_flags_arith(context, tmp);
446 break;
447 case LSR:
448 tmp = context->regs[a] >> context->regs[b];
449 tmp |= (context->regs[a] >> (context->regs[b] - 1)) << 16 & 0x10000;
450 context->regs[dst] = tmp;
451 update_flags_arith(context, tmp);
452 break;
453 case ASR:
454 tmp = context->regs[a];
455 if (tmp & 0x8000) {
456 tmp |= 0xFFFF0000;
457 }
458 tmp = tmp >> context->regs[b] & 0xFFFF;
459 tmp |= (context->regs[a] >> (context->regs[b] - 1)) << 16 & 0x10000;
460 context->regs[dst] = tmp;
461 update_flags_arith(context, tmp);
462 break;
463 case BCC:
464 run_bcc(context, dst, a, b);
465 return;
466 case SINGLE_SOURCE:
467 run_single_source(context, dst, a, b);
468 return;
469 }
470 if (dst == REG_PC) {
471 context->state = STATE_NEED_FETCH;
472 }
473 }
474
475 void run_cpu(cpu *context, uint32_t target_cycle)
476 {
477 while (context->cycles < target_cycle)
478 {
479 switch (context->state)
480 {
481 case STATE_NEED_FETCH:
482 fetch_instruction(context);
483 break;
484 case STATE_NORMAL:
485 run_instruction(context);
486 break;
487 case STATE_EXCEPTION_START:
488 vector_fetch(context);
489 break;
490 }
491 }
492 }