💾 Archived View for uscoffings.net › retro-computing › systems › TI994a › x99tape › x99tape_src › ta… captured on 2023-03-20 at 21:54:11.
View Raw
More Information
⬅️ Previous capture (2022-06-04)
-=-=-=-=-=-=-
/*
Portable TI99 tape encoder/decoder
tape_enc.c: Code to encode data into a new tape image
Raphael Nabet, 2002
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "common.h"
#include "my_int.h"
#include "tifiles.h"
#include "wave.h"
#include "tape_enc.h"
/*typedef struct ti_header_t
{
int block_len;
} ti_header_t;*/
enum
{
header_leader_len = 768,
block_leader_len = 8
};
static error_t write_tape_header(wave_file_out_t *dst, unsigned int block_len);
static error_t write_tape_block(wave_file_out_t *dst, const unsigned char buf[64]);
static error_t write_tape_byte(wave_file_out_t *dst, int data);
static error_t write_tape_bit(wave_file_out_t *dst, int data);
static int cur_state; /* 1 if tape output is currently < 0 */
/*
Bit Writing
Here is a 0 (assuming cassette line is initially low):
_____________________
| |
__| |__________
^ ^
start end
Here is a 1 (assuming cassette line is initially low):
__________ __________
| | |
__| |__________|
^ ^
flux change flux change
If the cassette line is initially high, read the diagrams upside down.
static const double default_data_rate = 1379.;
static int half_w = 16;
static void compute_parameters(double sample_rate)
{
double bit_width;
bit_width = sample_rate / default_data_rate;
half_w = ceil(bit_width/2);
}
/*
Write a complete tape
file_type: 0 for memory dump, 1 for program file
record_len: record lenght in bytes [1,192] (ignored if (file_type == 0))
file_len_in_records: file len in blocks (file_type == 0) or records (file_type == 1)
error_t write_tape(tifile_in_t *src, wave_file_out_t *dst)
{
error_t error;
int i;
unsigned char buf[192];
compute_parameters(dst->samplesPerSec);
if (src->type == ftype_data)
{
unsigned int record_len_in_blocks;
int j;
memset(buf, ' ', 192); /* don't ask */
/*if ((src->reclen <= 0) || (src->reclen > 192))
return invalid_parameters;*/
/* how many 64-byte blocks per record? */
record_len_in_blocks = (src->reclen+63) / 64;
for (i=0; /*1*/i<src->fixrecs; i++)
{
error = tifile_read(buf, src);
if (error)
return error;
if ((error = write_tape_header(dst, record_len_in_blocks)) != no_error)
return error;
fprintf(stderr, "writing record %d\n", i);
for (j=0; j<record_len_in_blocks; j++)
{
fprintf(stderr, "writing block %d\n", j);
error = write_tape_block(dst, buf+j*64);
if (error != no_error)
return error;
error = write_tape_block(dst, buf+j*64);
if (error != no_error)
return error;
}
/* add a 2 second trailer */
for (j=0; j<dst->samplesPerSec*2; j++)
{
error = write_wave_sample(dst, cur_state ? -1 : 1);
if (error)
return error;
}
}
}
else
{
if ((src->fixrecs <=0 ) || (src->fixrecs > 256))
return invalid_parameters;
if ((error = write_tape_header(dst, src->fixrecs)) != no_error)
return error;
for (i=0; i<src->fixrecs; i++)
{
error = tifile_read(buf, src);
if (error)
return error;
fprintf(stderr, "writing block %d\n", i);
error = write_tape_block(dst, buf);
if (error != no_error)
return error;
error = write_tape_block(dst, buf);
if (error != no_error)
return error;
}
/* add a 1/10 second trailer */
for (i=0; i<dst->samplesPerSec/10; i++)
{
error = write_wave_sample(dst, cur_state ? -1 : 1);
if (error)
return error;
}
}
return no_error;
}
/*
Write the tape leader, and the header wit the number of blocks to follow
block_len: number of data block to follow
static error_t write_tape_header(wave_file_out_t *dst, unsigned int block_len)
{
error_t error;
int byte;
int i;
if (block_len > 256)
{
return invalid_parameters;
}
if (block_len == 256)
byte = 0; /* TI99/4A tape write routine does support this */
else
byte = block_len;
cur_state = 0; /* I think */
/* write leader */
for (i=0; i<header_leader_len; i++)
{
error = write_tape_byte(dst, 0);
if (error)
return error;
}
/* write sync byte */
error = write_tape_byte(dst, 0xff);
if (error)
return error;
/* write len in blocks */
error = write_tape_byte(dst, byte);
if (error)
return error;
/* write second len */
error = write_tape_byte(dst, byte);
if (error)
return error;
return no_error;
}
/*
Write a data block to tape, including a short leader and the checksum
(must be called twice for each data chunk)
static error_t write_tape_block(wave_file_out_t *dst, const unsigned char data[64])
{
error_t error;
int i;
int checksum;
/* write leader */
for (i=0; i<block_leader_len; i++)
{
error = write_tape_byte(dst, 0);
if (error)
return error;
}
/* write sync byte */
error = write_tape_byte(dst, 0xff);
if (error)
return error;
/* write block data */
checksum = 0;
for (i=0; i<64; i++)
{
error = write_tape_byte(dst, data[i]);
if (error)
return error;
checksum = (checksum + data[i]) & 0xff;
}
/* write checksum */
error = write_tape_byte(dst, checksum);
if (error)
{
return error;
}
return no_error;
}
/*
Write one byte to tape
static error_t write_tape_byte(wave_file_out_t *dst, int data)
{
int i;
error_t error;
for (i=0; i<8; i++)
{
error = write_tape_bit(dst, (data & 0x80) ? 1 : 0);
if (error != no_error)
return error;
data <<= 1;
}
return no_error;
}
/*
Write a bit to tape
static error_t write_tape_bit(wave_file_out_t *dst, int data)
{
error_t error;
int i;
cur_state = ! cur_state;
for (i=0; i<half_w; i++)
{
error = write_wave_sample(dst, cur_state ? -1 : 1);
if (error)
return error;
}
if (data)
cur_state = ! cur_state;
for (i=0; i<half_w; i++)
{
error = write_wave_sample(dst, cur_state ? -1 : 1);
if (error)
return error;
}
return no_error;
}