view vgm.c @ 1925:039553703c20

Don't apply address and cd register changes to the 'live' registers until pending flag is cleared, but do preserve the upper address bits in the latch. Fixes regression in Overdrive 2 while preserving fix to Mona in 344 bytes
author Michael Pavone <pavone@retrodev.com>
date Mon, 13 Apr 2020 20:43:25 -0700
parents 508522f08e4d
children a9449608d0b0
line wrap: on
line source

#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include "vgm.h"

vgm_writer *vgm_write_open(char *filename, uint32_t rate, uint32_t clock, uint32_t cycle)
{
	FILE *f = fopen(filename, "wb");
	if (!f) {
		return NULL;
	}
	vgm_writer *writer = calloc(sizeof(vgm_writer), 1);
	memcpy(writer->header.ident, "Vgm ", 4);
	writer->header.version = 0x150;
	writer->header.data_offset = sizeof(writer->header) - offsetof(vgm_header, data_offset);
	writer->header.rate = rate;
	writer->f = f;
	if (1 != fwrite(&writer->header, sizeof(writer->header), 1, f)) {
		free(writer);
		fclose(f);
		return NULL;
	}
	writer->master_clock = clock;
	writer->last_cycle = cycle;
	
	return writer;
}

void vgm_sn76489_init(vgm_writer *writer, uint32_t clock, uint16_t feedback, uint8_t shift_reg_size, uint8_t flags)
{
	if (flags && writer->header.version < 0x151) {
		writer->header.version = 0x151;
	}
	writer->header.sn76489_clk = clock,
	writer->header.sn76489_fb = feedback;
	writer->header.sn76489_shift = shift_reg_size;
	writer->header.sn76489_flags = flags;
}

static void wait_commands(vgm_writer *writer, uint32_t delta)
{
	if (!delta) {
		return;
	}
	if (delta <= 0x10) {
		fputc(CMD_WAIT_SHORT + (delta - 1), writer->f);
	} else if (delta >= 735 && delta <= (735 + 0x10)) {
		fputc(CMD_WAIT_60, writer->f);
		wait_commands(writer, delta - 735);
	} else if (delta >= 882 && delta <= (882 + 0x10)) {
		fputc(CMD_WAIT_50, writer->f);
		wait_commands(writer, delta - 882);
	} else if (delta > 0xFFFF) {
		uint8_t cmd[3] = {CMD_WAIT, 0xFF, 0xFF};
		fwrite(cmd, 1, sizeof(cmd), writer->f);
		wait_commands(writer, delta - 0xFFFF);
	} else {
		uint8_t cmd[3] = {CMD_WAIT, delta, delta >> 8};
		fwrite(cmd, 1, sizeof(cmd), writer->f);
	}
}

static void add_wait(vgm_writer *writer, uint32_t cycle)
{
	uint64_t delta = cycle - writer->last_cycle;
	delta *= (uint64_t)44100;
	delta /= (uint64_t)writer->master_clock;
	
	uint32_t mclks_per_sample = writer->master_clock / 44100;
	writer->last_cycle += delta * mclks_per_sample; 
	writer->header.num_samples += delta;
	wait_commands(writer, delta);
}

void vgm_sn76489_write(vgm_writer *writer, uint32_t cycle, uint8_t value)
{
	add_wait(writer, cycle);
	uint8_t cmd[2] = {CMD_PSG, value};
	fwrite(cmd, 1, sizeof(cmd), writer->f);
}

void vgm_ym2612_init(vgm_writer *writer, uint32_t clock)
{
	writer->header.ym2612_clk = clock;
}

void vgm_ym2612_part1_write(vgm_writer *writer, uint32_t cycle, uint8_t reg, uint8_t value)
{
	add_wait(writer, cycle);
	uint8_t cmd[3] = {CMD_YM2612_0, reg, value};
	fwrite(cmd, 1, sizeof(cmd), writer->f);
}

void vgm_ym2612_part2_write(vgm_writer *writer, uint32_t cycle, uint8_t reg, uint8_t value)
{
	add_wait(writer, cycle);
	uint8_t cmd[3] = {CMD_YM2612_1, reg, value};
	fwrite(cmd, 1, sizeof(cmd), writer->f);
}

void vgm_adjust_cycles(vgm_writer *writer, uint32_t deduction)
{
	if (deduction > writer->last_cycle) {
		writer->last_cycle = 0;
	} else {
		writer->last_cycle -= deduction;
	}
}

void vgm_close(vgm_writer *writer)
{
	writer->header.eof_offset = ftell(writer->f) - offsetof(vgm_header, eof_offset);
	fseek(writer->f, SEEK_SET, 0);
	fwrite(&writer->header, sizeof(writer->header), 1, writer->f);
	fclose(writer->f);
	free(writer);
}