Mercurial > repos > simple16
view src/main.c @ 49:5f30c4d18d79
Updated the HTML documentation a bit
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Tue, 30 Aug 2016 22:04:29 -0700 |
parents | 6e7bfe83d2b0 |
children |
line wrap: on
line source
#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "cpu.h" #include "vdp.h" #include "audio.h" #include "timer.h" #include "controller.h" #include "system.h" #define CYCLES_PER_FRAME (832*262) #define MASTER_CLOCK 13056000 uint8_t rom[4 * 1024 * 1024]; uint8_t ram[128 * 1024]; enum { PORT_CONTROLLER_1, PORT_CONTROLLER_2, RESERVED_1, RESERVED_2, PORT_FREQUENCY_A, PORT_FREQUENCY_B, PORT_FREQUENCY_C, PORT_FREQUENCY_D, PORT_VOLUME_AB, PORT_VOLUME_CD, PORT_TIMER, PORT_SERIAL, PORT_START_OFFSET, PORT_VIDEO_MODE, PORT_CRAM, RESERVED_3 }; typedef struct { cpu *proc; audio *audio; timer timer; controllers pads; vdp video; } console; void debug_port_write(cpu *context, uint8_t port, uint16_t value) { putchar(value); } uint16_t debug_port_read(cpu *context, uint8_t port) { return getchar(); } void offset_port_write(cpu *context, uint8_t port, uint16_t value) { console *system = context->system; vdp_run(&system->video, context->cycles); system->video.start_offset = value; } uint16_t offset_port_read(cpu *context, uint8_t port) { console *system = context->system; vdp_run(&system->video, context->cycles); return system->video.vcounter; } void mode_port_write(cpu *context, uint8_t port, uint16_t value) { console *system = context->system; vdp_run(&system->video, context->cycles); vdp_write_mode(&system->video, value); context->mem_regions[2].base = vdp_get_back_buffer(&system->video); } uint16_t mode_port_read(cpu *context, uint8_t port) { console *system = context->system; vdp_run(&system->video, context->cycles); return system->video.hcounter; } void cram_port_write(cpu *context, uint8_t port, uint16_t value) { console *system = context->system; vdp_run(&system->video, context->cycles); vdp_write_cram(&system->video, value); } uint16_t cram_port_read(cpu *context, uint8_t port) { console *system = context->system; vdp_run(&system->video, context->cycles); return system->video.status; } void frequency_port_write(cpu *context, uint8_t port, uint16_t value) { console *system = context->system; audio_run(system->audio, context->cycles); audio_write_freq(system->audio, port - PORT_FREQUENCY_A, value); } void volume_port_write(cpu *context, uint8_t port, uint16_t value) { console *system = context->system; audio_run(system->audio, context->cycles); 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; } } uint16_t controller_port_read(cpu *context, uint8_t port) { //process events so controller state is as fresh as possible system_poll_events(); console *system = context->system; return controller_read(&system->pads, port - PORT_CONTROLLER_1); } 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); uint32_t vnext = vdp_next_interrupt(&system->video); if (vnext < next) { next = vnext; } } 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); if (vdp_interrupt_pending(&system->video)) { bits |= 2; } 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; } else { vdp_ack_interrupt(&system->video); } } memory_region regions[] = { { .base = rom, .start = 0, .end = sizeof(rom)-1, .mask = 0x7FFFFF, .flags = MEM_READ }, { .base = ram, .start = sizeof(rom), .end = 0x4FFFFF, .mask = 0xFFFF, .flags = MEM_READ|MEM_WRITE }, { .base = NULL, .start = 0x700000, .end = 0x7FFFFF, .mask = 0xFFFF, .flags = MEM_READ|MEM_WRITE } }; void run_console(console *context) { for(;;) { 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(); } } int main(int argc, char **argv) { if (argc < 2) { fputs("usage: s16 FILE\n", stderr); return 1; } FILE *f = fopen(argv[1], "rb"); if (!f) { fprintf(stderr, "Failed to open %s for reading\n", argv[1]); return 1; } size_t read; if ((read = fread(rom, 1, sizeof(rom), f)) < sizeof(rom)) { memset(rom + read, 0xFF, sizeof(rom)-read); } fclose(f); console context; context.proc = alloc_cpu(2, sizeof(regions)/sizeof(memory_region), regions); context.proc->system = &context; vdp_init(&context.video, 2); context.proc->mem_regions[2].base = vdp_get_back_buffer(&context.video); timer_init(&context.timer, 16); controller_init(&context.pads); context.proc->port_handlers[PORT_CONTROLLER_1].read = controller_port_read; context.proc->port_handlers[PORT_CONTROLLER_2].read = controller_port_read; 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_START_OFFSET].write = offset_port_write; context.proc->port_handlers[PORT_START_OFFSET].read = offset_port_read; context.proc->port_handlers[PORT_VIDEO_MODE].write = mode_port_write; context.proc->port_handlers[PORT_VIDEO_MODE].read = mode_port_read; context.proc->port_handlers[PORT_CRAM].write = cram_port_write; context.proc->port_handlers[PORT_CRAM].read = cram_port_read; if (!system_init(640, 480, 48000)) { return 1; } context.audio = alloc_audio(MASTER_CLOCK, 17, system_sample_rate(), system_buffer_size()); run_console(&context); return 0; }