/* psg.c: recording AY chip output to .psg files
Copyright (c) 2003-2016 Matthew Westcott, Philip Kendall
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Author contact information:
E-mail: philip-fuse@shadowmagic.org.uk
*/
#include "config.h"
#include <stdio.h>
#include "infrastructure/startup_manager.h"
#include "psg.h"
#include "ui/ui.h"
/* Are we currently recording a .psg file? */
int psg_recording;
static libspectrum_byte psg_register_values[ AY_REGISTERS ];
/* booleans to indicate the registers written to in this frame */
static int psg_registers_written[ AY_REGISTERS ];
/* number of prior frames with no AY events */
static int psg_empty_frame_count;
static FILE *psg_file;
static int write_frame_separator( void );
static int
psg_init( void *context )
{
psg_recording = 0;
return 0;
}
int
psg_start_recording( const char *filename )
{
int i;
if( psg_recording ) return 1;
psg_file = fopen( filename, "wb" );
if( psg_file == NULL ) {
ui_error( UI_ERROR_ERROR, "unable to open PSG file for writing" );
return 1;
}
/* write PSG file header */
if( fprintf( psg_file, "PSG\x1a" ) < 0 ) {
ui_error( UI_ERROR_ERROR, "unable to write PSG file header" );
return 1;
}
for( i = 0; i < 12; i++ ) putc( 0, psg_file );
/* begin with no registers written */
for( i = 0; i < AY_REGISTERS; i++ ) psg_registers_written[i] = 0;
psg_empty_frame_count = 1;
psg_recording = 1;
return 0;
}
int
psg_stop_recording( void )
{
if( !psg_recording ) return 1;
/* flush final frame */
psg_frame();
/* end file with the appropriate end-frame marker */
write_frame_separator();
fclose( psg_file );
psg_recording = 0;
return 0;
}
static int
write_frame_separator( void )
{
while( psg_empty_frame_count >= 4 ) {
int count;
count = psg_empty_frame_count / 4;
if( count > 0xff ) count = 0xff;
putc( 0xfe, psg_file );
putc( count, psg_file );
psg_empty_frame_count -= 4 * count;
}
for( ; psg_empty_frame_count; psg_empty_frame_count-- )
putc( 0xff, psg_file );
return 0;
}
int
psg_frame( void )
{
int i;
int ay_updated;
if( !psg_recording ) return 0;
/* check if any AY sound events have happened this frame */
ay_updated = 0;
for( i = 0; i < 14 && !ay_updated; i++ )
ay_updated = psg_registers_written[i];
if( ay_updated ) {
write_frame_separator();
for( i = 0; i < 14; i++ ) {
if( psg_registers_written[i] ) {
putc( i, psg_file );
putc( psg_register_values[i], psg_file );
}
}
psg_empty_frame_count = 1;
} else {
/* AY not updated */
psg_empty_frame_count++;
}
for( i = 0; i < AY_REGISTERS; i++ ) psg_registers_written[i] = 0;
return 0;
}
int
psg_write_register( libspectrum_byte reg, libspectrum_byte value )
{
if( psg_registers_written[reg] ) return 0;
psg_register_values[reg] = value;
psg_registers_written[reg] = 1;
return 0;
}
static void
psg_end( void )
{
if( psg_recording ) psg_stop_recording();
}
void
psg_register_startup( void )
{
startup_manager_module dependencies[] = { STARTUP_MANAGER_MODULE_SETUID };
startup_manager_register( STARTUP_MANAGER_MODULE_PSG, dependencies,
ARRAY_SIZE( dependencies ), psg_init, NULL,
psg_end );
}