blob: d6f60a36373e9a8d191b0161f0e0586ceaae8a56 [file] [log] [blame] [raw]
/*
YAPE - Yet Another Plus/4 Emulator
The program emulates the Commodore 264 family of 8 bit microcomputers
This program is free software, you are welcome to distribute it,
and/or modify it under certain conditions. For more information,
read 'Copying'.
(c) 2000, 2001, 2005 Attila Grósz
*/
#include "tape.h"
#include "tedmem.h"
#include "cpu.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned char tap_header[]={
'C','1','6','-','T','A','P','E','-','R','A','W',
0x02, // version -> 1 - whole wave 2 - wholeWave
0x02, // 0 - C64, 1 - VIC20, 2 - C16/+4
0x00, // Video standard ( 0= PAL, 1 =NTSC, 2 = NTSC2
0x00, // empty
// data length (4 byte) file size!!!!
// data
};
typedef struct _MTAP {
char string[12];
unsigned char version;
unsigned char platform;
unsigned char video;
unsigned char reserved;
} MTATAPBuffer , * pMTATAPBuffer;
static FILE *tapfile;
static MTATAPBuffer tap_header_read;
TAP::TAP()
{
TapeSoFar=0;
TapeFileSize=0;
TAPBuffer=NULL;
buttonPressed = motorOn = false;
lastCycle = 0;
edge = 0;
}
bool TAP::attach_tap()
{
char tmpname[14];
if ((tapfile=fopen(tapefilename,"rb"))) {
// load TAP file into buffer
fseek(tapfile, 0L, SEEK_END);
TapeFileSize=ftell(tapfile);
fseek(tapfile, 0L, SEEK_SET);
// allocate and load file
TAPBuffer=(unsigned char *) malloc(TapeFileSize);
fread(TAPBuffer,TapeFileSize,1,tapfile);
// initialise TAP image
inwave=false;
// setting platform
tap_header_read.version=TAPBuffer[0x0C];
// allocating space for type...
strncpy(tmpname,(const char *) TAPBuffer,12);
// determine the tYpe of tape file attached
if (!strncmp(tmpname,"C16-TAPE-RAW",12)) {
TapeSoFar=0x14; // offset to beginning of wave data
if (tap_header_read.version==1) {
wholeWave = 1;
} else {
wholeWave = 0;
}
} else { // if no match for MTAP, assume it is a sample
}
motorOn = buttonPressed = false;
// close the file, it's in the memory now...
fclose(tapfile);
return true;
}
else
return false;
}
bool TAP::create_tap()
{
int filesize = 0x00;
if (strlen(tapefilename)<5)
return false;
// initialise TAP image
if ((tapfile=fopen(tapefilename,"wb"))) {
// write the header
fwrite(&tap_header,sizeof(tap_header),1,(FILE *)tapfile);
fwrite(&filesize,4,1,(FILE *)tapfile);
return true;
}
else
return false;
}
bool TAP::detach_tap()
{
if ( tapfile!=NULL && TapeSoFar>0) {
fseek(tapfile, 0x10L, SEEK_SET);
fwrite(&TapeSoFar,sizeof(TapeSoFar),1,tapfile);
fseek(tapfile, 0L, SEEK_END);
}
if (TAPBuffer!=NULL) { // just to be sure....
free(TAPBuffer);
TAPBuffer=NULL;
}
if (tapfile!=NULL) {
fclose(tapfile);
tapfile=NULL;
}
TapeSoFar=0;
return false;
}
void TAP::rewind()
{
inwave=false;
TapeSoFar=0x14;
}
void TAP::changewave(bool wholewave)
{
if (!wholewave) {
tap_header[12]=2;
} else {
tap_header[12]=1;
}
}
inline void TAP::Advance(unsigned int elapsed)
{
while (elapsed--) {
if (inwave) {
if (tapeDelay-- == 0) {
//fprintf( stderr, "TAP unit timeout in cycle: %u\n", cycle + elapsed);
if (edge == 0x10)
ConvTAPUnitsToCycles();
else
tapeDelay = origTapeDelay;
edge ^= 0x10;
}
} else {
inwave=true;
ConvTAPUnitsToCycles();
edge = 0x00;
}
}
}
unsigned char TAP::ReadCSTIn(ClockCycle cycle)
{
int elapsed = int(cycle - lastCycle);
if (motorOn && TAPBuffer) {
Advance(elapsed);
lastCycle = cycle;
}
return edge;
}
inline ClockCycle TAP::ReadNextTapDelay()
{
if (!TAPBuffer)
return 0;
ClockCycle delay=TAPBuffer[TapeSoFar];
/* byte $00 is a pilot byte */
if (delay==0) {
delay=TAPBuffer[TapeSoFar+1];
delay+=TAPBuffer[TapeSoFar+2]<<8;
delay+=TAPBuffer[TapeSoFar+3]<<16;
TapeSoFar+=4;
/*fprintf( stderr, "Pilot byte %i.\n", delay);
fprintf( stderr, "Pilot offset: %i.\n", TapeSoFar);*/
} else {
delay <<= 3;
TapeSoFar+=1;
}
if (TapeSoFar>=TapeFileSize) {
motorOn = buttonPressed = false;
}
/* TAP units are measured in slow clock cycles */
return delay * 2;
}
void TAP::ConvTAPUnitsToCycles()
{
if (wholeWave) {
origTapeDelay = ReadNextTapDelay();
tapeDelay = origTapeDelay >> 1;
origTapeDelay -= tapeDelay;
} else {
tapeDelay = ReadNextTapDelay();
if (buttonPressed)
origTapeDelay = ReadNextTapDelay();
else
origTapeDelay = tapeDelay;
}
// printf( stderr, "New TAP delay value: %u\n", origTapeDelay);
}
void TAP::PressTapeButton(ClockCycle cycle)
{
if (!buttonPressed) {
lastCycle = cycle;
motorOn = buttonPressed = true;
}
}
void TAP::SetTapeMotor(ClockCycle cycle, unsigned int on)
{
int elapsed = int(cycle - lastCycle);
if (!on && motorOn)
Advance(elapsed);
lastCycle = cycle;
motorOn = (on != 0);
//fprintf( stderr, "Motor state: %i, in cycle %u\n", motorOn, cycle);
};