#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include "psg.h"
struct psg_file * load_psg_file(char * filename)
{
FILE * f = NULL;
struct psg_file * file = NULL;
struct stat file_stat;
f
= fopen(filename
,"rb");
if( !f )
{
fprintf(stderr
,"Can't open file <%s>!\n",filename
);
goto ERROR;
}
if( fstat(fileno(f),&file_stat) )
{
fprintf(stderr
,"Can't fstat file <%s>!\n",filename
);
goto ERROR;
}
if( file_stat.st_size<=16 )
{
fprintf(stderr
,"File <%s> is too small!\n",filename
);
goto ERROR;
}
// allocate psg_file structure
file
= malloc(sizeof(struct psg_file
));
if( !file ) goto MEM_ERROR;
file->filename = NULL;
file->size = 0;
file->bytes = NULL;
if( !(file
->filename
= malloc(1+strlen(filename
))) ) goto MEM_ERROR
;
strcpy(file
->filename
, filename
);
file->size = file_stat.st_size;
if( !(file
->bytes
= malloc(file
->size
)) ) goto MEM_ERROR
;
// load into allocated area
if( file
->size
!= fread(file
->bytes
, 1, file
->size
, f
) )
{
fprintf(stderr
,"Can't load from file <%s>!\n",filename
);
goto ERROR;
}
return file;
/////////////////////////////////////////////////////////////////////
MEM_ERROR:
fprintf(stderr
,"%s: memory allocation error!\n",__PRETTY_FUNCTION__
);
ERROR:
free_psg_file(file);
}
void free_psg_file(struct psg_file * file)
{
if( !file ) return;
if( file
->filename
) free(file
->filename
);
if( file
->bytes
) free(file
->bytes
);
return;
}
int update_psg_regs(uint8_t * regs, struct psg_file * psg, size_t * position)
{ // updates regs[14] array with data from next PSG frame, if any.
// regs could be NULL (nothing is written then)
// psg_file * psg is a constant structure that contains data from psg file
// * position is current position into it, gets updated
//
// return values:
// (-1): end of psg reached
// 0: run again until non-zero.
// any>0: regs[14] array is valid for the given amount of frame periods
//
// function makes exit(1) if PSG is incorrect
//
// always start running with *position=0 !
//
if( !position )
{
fprintf(stderr
,"%s: position pointer given is NULL!\n",__PRETTY_FUNCTION__
);
}
if( !psg )
{
fprintf(stderr
,"%s: struct psg_file pointer given is NULL!\n",__PRETTY_FUNCTION__
);
}
if( !psg->bytes )
{
fprintf(stderr
,"%s: bytes pointer in struct psg_file is NULL!\n",__PRETTY_FUNCTION__
);
}
// initialization
if( *position<16 )
{
*position=16;
// init registers
if( regs )
{
for(int i=0;i<14;i++) regs[i]=0;
}
return 0; // rerun
}
// EOF check by size
if( *position >= psg->size )
{
return (-1);
}
// EOF check by end mark
if( psg->bytes[*position] == 0xFD )
{
return (-1);
}
// prepare regs array
if( regs ) regs[13]=0xFF; // 0xFF means "DON'T WRITE to AY/YM"
int frames=1;
int was_delimiter=0;
for(;;)
{
if( !(psg->bytes[*position] & 0xF0) )
{ // register contents
if( was_delimiter )
{ // parse next frame in next iteration
return frames;
}
uint8_t reg_num = psg->bytes[(*position)++];
if( *position >= psg->size )
{
fprintf(stderr
,"%s: error in PSG format at the offset 0x%lx!",__PRETTY_FUNCTION__
,(*position
)-1);
}
uint8_t reg_value = psg->bytes[(*position)++];
if( regs && reg_num<14 ) regs[reg_num]=reg_value;
if( *position >= psg->size ) return frames;
}
else if( psg->bytes[*position]==0xFD )
{ // end mark -- will be parsed on next iteration. *position is NOT incremented!
return frames;
}
else if( psg->bytes[*position]==0xFF )
{ // 1-frame delimiter
if( was_delimiter )
frames++;
else
was_delimiter = 1;
(*position)++;
if( *position >= psg->size ) return frames;
}
else if( psg->bytes[*position]==0xFE )
{ // pause in 4-frame increments
(*position)++;
if( *position >= psg->size )
{
fprintf(stderr
,"%s: error in PSG format at the offset 0x%lx!",__PRETTY_FUNCTION__
,(*position
)-1);
}
int num_frames = 4*(psg->bytes[*position]);
if( !was_delimiter )
{
num_frames--;
was_delimiter=1;
}
frames += num_frames;
(*position)++;
if( *position >= psg->size ) return frames;
}
else
{
fprintf(stderr
,"%s: error in PSG format at the offset 0x%lx!",__PRETTY_FUNCTION__
,*position
);
}
}
}
struct frame_list * build_psg_frames(struct psg_file * psg)
{
struct frame_list * head = NULL;
struct frame_list * local_head = NULL;
size_t position;
int frames_spent;
uint8_t regs[14];
// // first count number of frames
// frames = 0;
// position = 0;
// //
// while( (curr_frames=update_psg_regs(NULL,psg,&position)) >= 0 )
// frames +=curr_frames;
//
// build frame list
position = 0;
while( (frames_spent=update_psg_regs(regs,psg,&position)) >= 0 )
{
if( frames_spent==0 ) continue;
// create frame_ay structure
struct frame_ay
* ay_regs
= malloc(sizeof(struct frame_ay
));
if( !ay_regs ) goto MEM_ERROR;
ay_regs->base.type = FRAME_AY;
memcpy(ay_regs
->regs
,regs
,14);
// add frames_spent list elements pointing to this structure to the local list
//
for(int i=0;i<frames_spent;i++)
{
struct frame_list
* element
= malloc(sizeof(struct frame_list
));
if( !element ) goto MEM_ERROR;
element->frame = (struct frame_base *)ay_regs;
element->next = local_head;
local_head = element;
}
}
// reverse local_head-based list into global head-pointed list
if( local_head )
{
do
{
struct frame_list * old_next = local_head->next;
local_head->next = head;
head = local_head;
local_head = old_next;
} while( local_head );
}
else
{ // were no frames -- PSG format error
fprintf(stderr
,"%s: error in PSG format: no frames found!",__PRETTY_FUNCTION__
);
goto ERROR;
}
return head;
MEM_ERROR:
fprintf(stderr
,"%s: memory allocation error!\n",__PRETTY_FUNCTION__
);
ERROR:
free_psg_frames(local_head);
free_psg_frames(head);
}
void free_psg_frames(struct frame_list * head)
{
struct frame_base * oldframe = NULL;
struct frame_list * freeme;
while( head )
{
if( head->frame )
{
if( oldframe != head->frame )
{
oldframe = head->frame;
}
}
freeme = head;
head = head->next;
}
}