view src/main.c @ 33:eda4919d955f

Forgot to call controller_init
author Michael Pavone <pavone@retrodev.com>
date Sun, 03 Apr 2016 19:03:11 -0700
parents 78068060313a
children 6e7bfe83d2b0
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[48 * 1024];
uint8_t ram[16 * 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_VERTICAL,
	PORT_HORIZONTAL,
	PORT_VRAM_ADDRESS,
	PORT_VRAM_DATA
};

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 vertical_port_write(cpu *context, uint8_t port, uint16_t value)
{
	console *system = context->system;
	vdp_run(&system->video, context->cycles);
	system->video.vscroll = value;
}

uint16_t vertical_port_read(cpu *context, uint8_t port)
{
	console *system = context->system;
	vdp_run(&system->video, context->cycles);
	return system->video.vcounter;
}

void horizontal_port_write(cpu *context, uint8_t port, uint16_t value)
{
	console *system = context->system;
	vdp_run(&system->video, context->cycles);
	vdp_write_hscroll(&system->video, value);
}

uint16_t horizontal_port_read(cpu *context, uint8_t port)
{
	console *system = context->system;
	vdp_run(&system->video, context->cycles);
	return system->video.hcounter;
}

void address_port_write(cpu *context, uint8_t port, uint16_t value)
{
	console *system = context->system;
	vdp_run(&system->video, context->cycles);
	vdp_write_address(&system->video, value);
}

uint16_t address_port_read(cpu *context, uint8_t port)
{
	console *system = context->system;
	vdp_run(&system->video, context->cycles);
	return system->video.status;
}

void data_port_write(cpu *context, uint8_t port, uint16_t value)
{
	console *system = context->system;
	vdp_run(&system->video, context->cycles);
	vdp_write_data(&system->video, value);
}

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[] = {
	{rom, 0, sizeof(rom)-1, MEM_READ},
	{ram, sizeof(rom), sizeof(rom)-1+sizeof(ram), 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);
	}
	console context;
	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);
	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_VERTICAL].write = vertical_port_write;
	context.proc->port_handlers[PORT_VERTICAL].read = vertical_port_read;
	context.proc->port_handlers[PORT_HORIZONTAL].write = horizontal_port_write;
	context.proc->port_handlers[PORT_HORIZONTAL].read = horizontal_port_read;
	context.proc->port_handlers[PORT_VRAM_ADDRESS].write = address_port_write;
	context.proc->port_handlers[PORT_VRAM_ADDRESS].read = address_port_read;
	context.proc->port_handlers[PORT_VRAM_DATA].write = data_port_write;
	
	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;
}