view src/cpu.c @ 6:74a6d629b78f

Added assembler. Removed hand-assembled version of hello world example
author Michael Pavone <pavone@retrodev.com>
date Sat, 26 Mar 2016 23:31:08 -0700
parents 6204c81e2933
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;
		}
	}
}