Mercurial > repos > simple16
view src/cpu.c @ 11:04d8efe7a1f0
Initial stab at video output and background color rendering. Fixed address decoding in address port write handler.
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sun, 27 Mar 2016 17:36:02 -0700 |
parents | 74a6d629b78f |
children | d8ae30286d17 |
line wrap: on
line source
#include <stdint.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include "cpu.h" enum { EXCEPTION_INTERRUPT_0, EXCEPTION_INTERRUPT_1, EXCEPTION_UNALIGNED_READ, EXCEPTION_INVALID_INSTRUCTION }; enum { STATE_NEED_FETCH, STATE_NORMAL, STATE_EXCEPTION_START }; #define STATUS_INT0_ENABLE 1 #define STATUS_INT1_ENABLE 2 #define FLAG_Z 4 #define FLAG_C 8 #define FLAG_N 16 cpu* alloc_cpu(uint32_t clock_divider, uint32_t num_regions, memory_region *regions) { size_t alloc_size = sizeof(cpu) + sizeof(memory_region) * num_regions; cpu *context = malloc(alloc_size); memset(context, 0, alloc_size); context->clock_inc = clock_divider; context->num_mem_regions = num_regions; memcpy(context->mem_regions, regions, num_regions*sizeof(memory_region)); return context; } uint16_t cpu_read_16(cpu *context, uint16_t address) { context->cycles += context->clock_inc; if (address & 1) { context->exception = EXCEPTION_UNALIGNED_READ; context->state = STATE_EXCEPTION_START; return 0xFFFF; } memory_region *cur = context->mem_regions; for (memory_region *end = cur + context->num_mem_regions; cur < end; cur++) { if (address >= cur->start && address <= cur->end && (cur->flags & MEM_READ)) { return cur->base[address - cur->start] << 8 | cur->base[address - cur->start + 1]; } } return 0xFFFF; } uint8_t cpu_read_8(cpu *context, uint16_t address) { context->cycles += context->clock_inc; memory_region *cur = context->mem_regions; for (memory_region *end = cur + context->num_mem_regions; cur < end; cur++) { if (address >= cur->start && address <= cur->end && (cur->flags & MEM_READ)) { return cur->base[address - cur->start]; } } return 0xFF; } void cpu_write_16(cpu *context, uint16_t address, uint16_t value) { context->cycles += context->clock_inc; if (address & 1) { context->exception = EXCEPTION_UNALIGNED_READ; context->state = STATE_EXCEPTION_START; return; } memory_region *cur = context->mem_regions; for (memory_region *end = cur + context->num_mem_regions; cur < end; cur++) { if (address >= cur->start && address <= cur->end && (cur->flags & MEM_WRITE)) { cur->base[address - cur->start] = value >> 8; cur->base[address - cur->start + 1] = value; break; } } } void cpu_write_8(cpu *context, uint16_t address, uint8_t value) { context->cycles += context->clock_inc; memory_region *cur = context->mem_regions; for (memory_region *end = cur + context->num_mem_regions; cur < end; cur++) { if (address >= cur->start && address <= cur->end && (cur->flags & MEM_WRITE)) { cur->base[address - cur->start] = value; break; } } } uint16_t cpu_read_port(cpu *context, uint8_t port) { port &= 0xF; if (context->port_handlers[port].read) { return context->port_handlers[port].read(context, port); } return 0xFFFF; } void cpu_write_port(cpu *context, uint8_t port, uint16_t value) { port &= 0xF; if (context->port_handlers[port].write) { context->port_handlers[port].write(context, port, value); } } void fetch_instruction(cpu *context) { context->prefetch = cpu_read_16(context, context->regs[REG_PC]); context->regs[REG_PC] += 2; context->state = STATE_NORMAL; } void vector_fetch(cpu *context) { context->exception_pc = context->regs[REG_PC] - 2; context->exception_sr = context->regs[REG_SR]; context->regs[REG_SR] &= ~(STATUS_INT0_ENABLE | STATUS_INT1_ENABLE); context->regs[REG_PC] = cpu_read_16(context, context->vector_base + context->exception); context->state = STATE_NEED_FETCH; } uint16_t sign_extend(uint16_t val) { if (val & 0x80) { return val | 0xFF00; } return val; } void update_flags_arith(cpu *context, uint32_t result) { context->regs[REG_SR] &= ~(FLAG_N|FLAG_C|FLAG_Z); if (!(result & 0xFFFF)) { context->regs[REG_SR] |= FLAG_Z; } if (result &= 0x8000) { context->regs[REG_SR] |= FLAG_N; } if (result &= 0x10000) { context->regs[REG_SR] |= FLAG_C; } } void update_flags_bitwise(cpu *context, uint32_t result) { context->regs[REG_SR] &= ~(FLAG_N|FLAG_Z); if (!(result & 0xFFFF)) { context->regs[REG_SR] |= FLAG_Z; } if (result &= 0x8000) { context->regs[REG_SR] |= FLAG_N; } } void run_bcc(cpu *context, uint8_t condition, uint8_t a, uint8_t b) { uint8_t doit = 0; switch (condition) { case COND_ALWAYS: doit = 1; break; case COND_NEVER: break; case COND_ZERO: doit = context->regs[REG_SR] & FLAG_Z; break; case COND_NZERO: doit = !(context->regs[REG_SR] & FLAG_Z); break; case COND_NEG: doit = context->regs[REG_SR] & FLAG_N; break; case COND_POS: doit = !(context->regs[REG_SR] & FLAG_N); break; case COND_CARRY: doit = context->regs[REG_SR] & FLAG_C; break; case COND_NCARRY: doit = context->regs[REG_SR] & FLAG_C; break; case COND_GREATER: //not zero and not carry doit = !(context->regs[REG_SR] & FLAG_Z) || !(context->regs[REG_SR] & FLAG_C); break; case COND_LEQ: //zero or carry doit = (context->regs[REG_SR] & FLAG_Z) || (context->regs[REG_SR] & FLAG_C); break; default: context->exception = EXCEPTION_INVALID_INSTRUCTION; context->state = STATE_EXCEPTION_START; return; } if (doit) { context->regs[REG_PC] += sign_extend(a << 4 | b) * 2; context->state = STATE_NEED_FETCH; } } uint16_t format_immediate(uint16_t val) { if (val & 8) { val |= 0xFFF0; } if (!val) { val = 8; } return val; } void run_single_reg(cpu *context, uint8_t dst, uint8_t op) { switch(op) { case RETI: context->regs[dst] = context->exception_ur; context->regs[REG_PC] = context->exception_pc; context->regs[REG_SR] = context->exception_sr; context->state = STATE_NEED_FETCH; return; case TRAP: context->state = STATE_EXCEPTION_START; context->exception = context->regs[dst]; return; case TRAPI: context->state = STATE_EXCEPTION_START; context->exception = dst; return; case GETEPC: context->regs[dst] = context->exception_pc; break; case SETEPC: context->exception_pc = context->regs[dst]; break; case GETESR: context->regs[dst] = context->exception_sr; break; case SETESR: context->exception_sr = context->regs[dst]; break; case GETEUR: context->regs[dst] = context->exception_ur; break; case SETEUR: context->exception_ur = context->regs[dst]; break; case GETENUM: context->regs[dst] = context->exception; break; case SETENUM: context->exception = context->regs[dst]; break; default: context->state = STATE_EXCEPTION_START; context->exception = EXCEPTION_INVALID_INSTRUCTION; return; } if (dst == REG_PC) { context->state = STATE_NEED_FETCH; } } void run_single_source(cpu *context, uint8_t dst, uint8_t a, uint8_t op) { uint32_t tmp; uint8_t shift; switch(op) { case MOVE: context->regs[dst] = context->regs[a]; break; case NEG: tmp = -context->regs[a]; context->regs[dst] = tmp; update_flags_arith(context, tmp); break; case NOT: context->regs[dst] = ~context->regs[a]; update_flags_bitwise(context, context->regs[dst]); break; case CMP: tmp = context->regs[dst] - context->regs[a]; update_flags_arith(context, tmp); return; case CALL: context->regs[dst] = context->regs[REG_PC] - 2; context->regs[REG_PC] = context->regs[a]; context->state = STATE_NEED_FETCH; return; case SWAP: tmp = context->regs[dst]; context->regs[dst] = context->regs[a]; context->regs[a] = tmp; if (a == REG_PC) { context->state = STATE_NEED_FETCH; return; } break; case IN: context->regs[dst] = cpu_read_port(context, context->regs[a]); break; case OUT: cpu_write_port(context, context->regs[a], context->regs[dst]); return; case INI: context->regs[dst] = cpu_read_port(context, a); break; case OUTI: cpu_write_port(context, a, context->regs[dst]); return; case ADDI: tmp = context->regs[dst] + format_immediate(a); context->regs[dst] = tmp; update_flags_arith(context, tmp); break; case ANDI: context->regs[dst] = context->regs[dst] & format_immediate(a); update_flags_bitwise(context, context->regs[dst]); break; case ORI: context->regs[dst] = context->regs[dst] | format_immediate(a); update_flags_bitwise(context, context->regs[dst]); break; case LSI: shift = a & 7; if (!shift) { shift = 8; } if (a & 8) { tmp = context->regs[dst] >> shift; tmp |= (context->regs[dst] >> (shift - 1)) << 16 & 0x10000; } else { tmp = context->regs[dst] << (a & 7); } context->regs[dst] = tmp; update_flags_arith(context, tmp); break; case CMPI: tmp = a; if (a & 8) { a |= 0xFFF0; } tmp = context->regs[dst] - a; update_flags_arith(context, tmp); break; case SINGLE_REG: run_single_reg(context, dst, a); return; } if (dst == REG_PC) { context->state = STATE_NEED_FETCH; } } char * mnemonics[] = { "ldim", "ldimh", "ld8", "ld16", "str8", "str16", "add", "adc", "and", "or", "xor", "lsl", "lsr", "asr", "bcc", "single" }; char * mnemonics_single_src[] = { "mov", "neg", "not", "cmp", "call", "swap", "in", "out", "ini", "outi", "addi", "andi", "ori", "lsi", "cmpi", "single reg" }; char * mnemonics_single_reg[] = { "reti", "trap", "trapi", "getepc", "setepc", "getesr", "setesr", "getenum", "setenum", "setuer", "getuer" }; void run_instruction(cpu *context) { uint16_t instruction = context->prefetch; fetch_instruction(context); uint8_t dst = instruction >> 12; uint8_t a = instruction >> 8 & 0xF; uint8_t b = instruction >> 4 & 0xF; uint8_t op = instruction & 0xF; uint32_t tmp; switch (op) { case LDIM: context->regs[dst] = sign_extend(a << 4 | b); break; case LDIMH: context->regs[dst] &= 0xFF; context->regs[dst] |= a << 12 | b << 8; break; case LD8: context->regs[dst] = cpu_read_8(context, context->regs[a] + context->regs[b]); break; case LD16: context->regs[dst] = cpu_read_16(context, context->regs[a] + context->regs[b]); break; case STR8: cpu_write_8(context, context->regs[a] + context->regs[b], context->regs[dst]); return; case STR16: cpu_write_16(context, context->regs[a] + context->regs[b], context->regs[dst]); return; case ADD: tmp = context->regs[a] + context->regs[b]; context->regs[dst] = tmp; update_flags_arith(context, tmp); break; case ADC: tmp = context->regs[a] + context->regs[b] + (context->regs[REG_SR] & FLAG_C ? 1 : 0); context->regs[dst] = tmp; update_flags_arith(context, tmp); break; case AND: context->regs[dst] = context->regs[a] & context->regs[b]; update_flags_bitwise(context, context->regs[dst]); break; case OR: context->regs[dst] = context->regs[a] | context->regs[b]; update_flags_bitwise(context, context->regs[dst]); break; case XOR: context->regs[dst] = context->regs[a] ^ context->regs[b]; update_flags_bitwise(context, context->regs[dst]); break; case LSL: tmp = context->regs[a] << context->regs[b]; context->regs[dst] = tmp; update_flags_arith(context, tmp); break; case LSR: tmp = context->regs[a] >> context->regs[b]; tmp |= (context->regs[a] >> (context->regs[b] - 1)) << 16 & 0x10000; context->regs[dst] = tmp; update_flags_arith(context, tmp); break; case ASR: tmp = context->regs[a]; if (tmp & 0x8000) { tmp |= 0xFFFF0000; } tmp = tmp >> context->regs[b] & 0xFFFF; tmp |= (context->regs[a] >> (context->regs[b] - 1)) << 16 & 0x10000; context->regs[dst] = tmp; update_flags_arith(context, tmp); break; case BCC: run_bcc(context, dst, a, b); return; case SINGLE_SOURCE: run_single_source(context, dst, a, b); return; } if (dst == REG_PC) { context->state = STATE_NEED_FETCH; } } void run_cpu(cpu *context, uint32_t target_cycle) { while (context->cycles < target_cycle) { switch (context->state) { case STATE_NEED_FETCH: fetch_instruction(context); break; case STATE_NORMAL: run_instruction(context); break; case STATE_EXCEPTION_START: vector_fetch(context); break; } } }