# HG changeset patch # User Michael Pavone # Date 1459491952 25200 # Node ID fb14515266f4eab30d35a62fc6b96e38d08ce8e0 # Parent 4c9dbfa30a6698da4996b0d0915ba924e3ecf9df Implemented timer and timer interrupts. Added get/setvbr instructions. Fixed assembler bug. Moved mnemonics into a separate source file diff -r 4c9dbfa30a66 -r fb14515266f4 Makefile --- 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 diff -r 4c9dbfa30a66 -r fb14515266f4 simple_console.txt --- 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 diff -r 4c9dbfa30a66 -r fb14515266f4 src/asm.c --- 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); diff -r 4c9dbfa30a66 -r fb14515266f4 src/cpu.c --- 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 diff -r 4c9dbfa30a66 -r fb14515266f4 src/cpu.h --- 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 { diff -r 4c9dbfa30a66 -r fb14515266f4 src/main.c --- 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; diff -r 4c9dbfa30a66 -r fb14515266f4 src/mnemonics.c --- /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" +}; diff -r 4c9dbfa30a66 -r fb14515266f4 src/timer.c --- /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 +#include +#include +#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; +} diff -r 4c9dbfa30a66 -r fb14515266f4 src/timer.h --- /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 diff -r 4c9dbfa30a66 -r fb14515266f4 timer.s16 --- /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