changeset 25:fb14515266f4

Implemented timer and timer interrupts. Added get/setvbr instructions. Fixed assembler bug. Moved mnemonics into a separate source file
author Michael Pavone <pavone@retrodev.com>
date Thu, 31 Mar 2016 23:25:52 -0700
parents 4c9dbfa30a66
children 083347ccd508
files Makefile simple_console.txt src/asm.c src/cpu.c src/cpu.h src/main.c src/mnemonics.c src/timer.c src/timer.h timer.s16
diffstat 10 files changed, 210 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Thu Mar 31 00:07:37 2016 -0700
+++ b/Makefile	Thu Mar 31 23:25:52 2016 -0700
@@ -18,10 +18,10 @@
 $(TARGETDIR) : 
 	mkdir $(TARGETDIR)
 
-$(TARGETDIR)/s16 : $(TARGETDIR)/main.o $(TARGETDIR)/cpu.o $(TARGETDIR)/vdp.o $(TARGETDIR)/audio.o $(TARGETDIR)/system_sdl.o
+$(TARGETDIR)/s16 : $(TARGETDIR)/main.o $(TARGETDIR)/cpu.o $(TARGETDIR)/vdp.o $(TARGETDIR)/audio.o $(TARGETDIR)/timer.o $(TARGETDIR)/system_sdl.o 
 	$(CC) -o $@ $^ $(LDFLAGS)
 	
-$(TARGETDIR)/asm : $(TARGETDIR)/asm.o $(TARGETDIR)/cpu.o
+$(TARGETDIR)/asm : $(TARGETDIR)/asm.o $(TARGETDIR)/mnemonics.o
 		$(CC) -o $@ $^ $(LDFLAGS)
 
 $(TARGETDIR)/%.o : src/%.c
--- a/simple_console.txt	Thu Mar 31 00:07:37 2016 -0700
+++ b/simple_console.txt	Thu Mar 31 23:25:52 2016 -0700
@@ -190,6 +190,7 @@
 CPU Clock Divider 20 (assuming 1 cycle/instruction, 5 for 4 cycles/instruction)
 Audio Timer Divider 34
 Audio Output Divider 544
+Interrupt Timer Divider 32
 
 Alternatively 13.056 Mhz clock and cut the dividers in half
 
--- a/src/asm.c	Thu Mar 31 00:07:37 2016 -0700
+++ b/src/asm.c	Thu Mar 31 23:25:52 2016 -0700
@@ -120,8 +120,8 @@
 		}
 		return ret;
 	}
-	index = find_string_arr(mnemonics_single_reg, mnemonic, SETENUM+1);
-	if (index > SETENUM) {
+	index = find_string_arr(mnemonics_single_reg, mnemonic, SETVBR+1);
+	if (index > SETVBR) {
 		ret.base = 0xFFFF;
 		return ret;
 	}
@@ -310,9 +310,11 @@
 	}
 	if (!strcmp(arg, "pc")) {
 		*inst |= REG_PC << arg_shift;
+		return 1;
 	}
 	if (!strcmp(arg, "sr")) {
 		*inst |= REG_SR << arg_shift;
+		return 1;
 	}
 	if (immed_min == immed_max) {
 		fprintf(stderr, "ERROR: Non-register argument %s where a register is required\n", arg);
--- a/src/cpu.c	Thu Mar 31 00:07:37 2016 -0700
+++ b/src/cpu.c	Thu Mar 31 23:25:52 2016 -0700
@@ -268,6 +268,12 @@
 	case SETENUM:
 		context->exception = context->regs[dst];
 		break;
+	case GETVBR:
+		context->regs[dst] = context->vector_base;
+		break;
+	case SETVBR:
+		context->vector_base = context->regs[dst];
+		break;
 	default:
 		context->state = STATE_EXCEPTION_START;
 		context->exception = EXCEPTION_INVALID_INSTRUCTION;
@@ -370,18 +376,6 @@
 	}
 }
 
-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;
@@ -471,17 +465,33 @@
 {
 	while (context->cycles < target_cycle)
 	{
-		switch (context->state)
+		context->current_target = target_cycle;
+		context->pending_interrupts = get_current_interrupts(context);
+		uint32_t int_cycle = next_interrupt_cycle(context, (~context->pending_interrupts) & 0x3);
+		if (int_cycle < context->current_target) {
+			context->current_target = int_cycle;
+		}
+		while (context->cycles < context->current_target)
 		{
-		case STATE_NEED_FETCH:
-			fetch_instruction(context);
-			break;
-		case STATE_NORMAL:
-			run_instruction(context);
-			break;
-		case STATE_EXCEPTION_START:
-			vector_fetch(context);
-			break;
+			switch (context->state)
+			{
+			case STATE_NEED_FETCH:
+				fetch_instruction(context);
+				break;
+			case STATE_NORMAL:
+				if (context->regs[REG_SR] & context->pending_interrupts) {
+					context->state = STATE_EXCEPTION_START;
+					context->exception = context->pending_interrupts & 1 ? EXCEPTION_INTERRUPT_0 : EXCEPTION_INTERRUPT_1;
+					context->pending_interrupts &= ~(1 << context->exception);
+					ack_interrupt(context, context->exception);
+				} else {
+					run_instruction(context);
+				}
+				break;
+			case STATE_EXCEPTION_START:
+				vector_fetch(context);
+				break;
+			}
 		}
 	}
 }
\ No newline at end of file
--- a/src/cpu.h	Thu Mar 31 00:07:37 2016 -0700
+++ b/src/cpu.h	Thu Mar 31 23:25:52 2016 -0700
@@ -28,6 +28,7 @@
 	void     *system;
 	uint32_t cycles;
 	uint32_t clock_inc;
+	uint32_t current_target;
 	uint32_t num_mem_regions;
 	uint16_t regs[16];
 	uint16_t exception;
@@ -39,6 +40,7 @@
 	uint16_t prefetch;
 	
 	uint8_t  state;
+	uint8_t  pending_interrupts;
 	
 	port_handler  port_handlers[16];
 	memory_region mem_regions[];
@@ -46,6 +48,11 @@
 
 cpu* alloc_cpu(uint32_t clock_divider, uint32_t num_regions, memory_region *regions);
 void run_cpu(cpu *context, uint32_t target_cycle);
+//To be implemented by system
+uint32_t next_interrupt_cycle(cpu *context, uint8_t mask);
+uint8_t get_current_interrupts(cpu *context);
+void ack_interrupt(cpu *context, int which);
+
 extern char * mnemonics[];
 extern char * mnemonics_single_src[];
 extern char * mnemonics_single_reg[];
@@ -99,7 +106,9 @@
 	GETEUR,
 	SETEUR,
 	GETENUM,
-	SETENUM
+	SETENUM,
+	GETVBR,
+	SETVBR
 };
 
 enum {
--- a/src/main.c	Thu Mar 31 00:07:37 2016 -0700
+++ b/src/main.c	Thu Mar 31 23:25:52 2016 -0700
@@ -5,6 +5,7 @@
 #include "cpu.h"
 #include "vdp.h"
 #include "audio.h"
+#include "timer.h"
 #include "system.h"
 
 #define CYCLES_PER_FRAME (832*262)
@@ -34,8 +35,9 @@
 
 typedef struct {
 	cpu   *proc;
+	audio *audio;
+	timer timer;
 	vdp   video;
-	audio *audio;
 } console;
 
 void debug_port_write(cpu *context, uint8_t port, uint16_t value)
@@ -111,6 +113,55 @@
 	audio_write_vol(system->audio, port - PORT_VOLUME_AB, value);
 }
 
+void timer_port_write(cpu *context, uint8_t port, uint16_t value)
+{
+	console *system = context->system;
+	timer_run(&system->timer, context->cycles);
+	timer_write(&system->timer, value);
+	uint32_t next_int = next_interrupt_cycle(context, (~context->pending_interrupts) & 3);
+	if (next_int < context->current_target) {
+		context->current_target = next_int;
+	}
+}
+
+uint32_t next_interrupt_cycle(cpu *context, uint8_t mask)
+{
+	console *system = context->system;
+	uint32_t next = 0xFFFFFFFF;
+	if (mask & 1) {
+		timer_run(&system->timer, context->cycles);
+		next = timer_next_interrupt(&system->timer);
+	}
+	if (mask & 2) {
+		vdp_run(&system->video, context->cycles);
+		//TODO: VBlank interrupt
+	}
+	return next;
+}
+
+uint8_t get_current_interrupts(cpu *context)
+{
+	console *system = context->system;
+	timer_run(&system->timer, context->cycles);
+	uint8_t bits = 0;
+	if (system->timer.pending) {
+		bits |= 1;
+	}
+	vdp_run(&system->video, context->cycles);
+	//TODO: VBlank interrupt
+	return bits;
+}
+
+void ack_interrupt(cpu *context, int which)
+{
+	console *system = context->system;
+	if (which == 0) {
+		timer_run(&system->timer, context->cycles);
+		system->timer.pending = 0;
+	}
+	//TODO: VBlank interrupt
+}
+
 memory_region regions[] = {
 	{rom, 0, sizeof(rom)-1, MEM_READ},
 	{ram, sizeof(rom), sizeof(rom)-1+sizeof(ram), MEM_READ},
@@ -123,9 +174,11 @@
 		run_cpu(context->proc, CYCLES_PER_FRAME);
 		audio_run(context->audio, CYCLES_PER_FRAME);
 		vdp_run(&context->video, CYCLES_PER_FRAME);
+		timer_run(&context->timer, CYCLES_PER_FRAME);
 		context->proc->cycles -= CYCLES_PER_FRAME;
 		context->video.cycles -= CYCLES_PER_FRAME;
 		context->audio->cycles -= CYCLES_PER_FRAME;
+		context->timer.cycles -= CYCLES_PER_FRAME;
 		system_poll_events();
 	}
 }
@@ -150,12 +203,14 @@
 	context.proc = alloc_cpu(10, sizeof(regions)/sizeof(memory_region), regions);
 	context.proc->system = &context;
 	vdp_init(&context.video, 2);
+	timer_init(&context.timer, 16);
 	context.proc->port_handlers[PORT_FREQUENCY_A].write = frequency_port_write;
 	context.proc->port_handlers[PORT_FREQUENCY_B].write = frequency_port_write;
 	context.proc->port_handlers[PORT_FREQUENCY_C].write = frequency_port_write;
 	context.proc->port_handlers[PORT_FREQUENCY_D].write = frequency_port_write;
 	context.proc->port_handlers[PORT_VOLUME_AB].write = volume_port_write;
 	context.proc->port_handlers[PORT_VOLUME_CD].write = volume_port_write;
+	context.proc->port_handlers[PORT_TIMER].write = timer_port_write;
 	context.proc->port_handlers[PORT_SERIAL].write = debug_port_write;
 	context.proc->port_handlers[PORT_SERIAL].read = debug_port_read;
 	context.proc->port_handlers[PORT_VERTICAL].write = vertical_port_write;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/mnemonics.c	Thu Mar 31 23:25:52 2016 -0700
@@ -0,0 +1,12 @@
+
+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", "getvbr", "setvbr"
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/timer.c	Thu Mar 31 23:25:52 2016 -0700
@@ -0,0 +1,43 @@
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+#include "timer.h"
+
+void timer_init(timer *context, uint32_t clock_div)
+{
+	memset(context, 0, sizeof(timer));
+	context->clock_inc = clock_div;
+}
+
+void timer_run(timer *context, uint32_t target)
+{
+	while (context->cycles < target)
+	{
+		if (context->current) {
+			context->current--;
+			if (!context->current) {
+				context->pending = 1;
+			}
+		} else {
+			context->current = context->load;
+		}
+		context->cycles += context->clock_inc;
+	}
+}
+
+uint32_t timer_next_interrupt(timer *context)
+{
+	if (context->pending) {
+		return 0;
+	}
+	if (context->current) {
+		return context->cycles + context->current * context->clock_inc;
+	}
+	return UINT_MAX;
+}
+
+void timer_write(timer *context, uint16_t value)
+{
+	context->load = context->current = value;
+	context->pending = 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/timer.h	Thu Mar 31 23:25:52 2016 -0700
@@ -0,0 +1,18 @@
+#ifndef TIMER_H_
+#define TIMER_H_
+
+typedef struct {
+	uint32_t cycles;
+	uint32_t clock_inc;
+	
+	uint16_t load;
+	uint16_t current;
+	uint8_t  pending;
+} timer;
+
+void timer_init(timer *context, uint32_t clock_div);
+void timer_run(timer *context, uint32_t target);
+uint32_t timer_next_interrupt(timer *context);
+void timer_write(timer *context, uint16_t value);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/timer.s16	Thu Mar 31 23:25:52 2016 -0700
@@ -0,0 +1,32 @@
+	ldim vectors, r0
+	setvbr r0
+	;current color value
+	ldim 0, r0
+	;color increment
+	ldim $11, r3
+	ldimh $1, r3
+	;Palette RAM address
+	ldim 0, r1
+	ldimh $FF, r1
+	;enable interrupt
+	ori 1, sr
+	;Timer Value
+	ldim $FF, r2
+	ldimh $FF, r2
+	outi $A, r2
+wait
+	bra wait
+	;shouldn't get here, disable timer so it's clear something broke
+	ldim 0, r2
+	outi $A, r2
+	bra wait
+
+vectors:
+	dc.w timer_handler
+	
+timer_handler
+	outi $E, r1
+	outi $F, r0
+	add r3, r0, r0
+	reti r4
+	
\ No newline at end of file