💾 Archived View for uscoffings.net › retro-computing › systems › TI994a › x99tape › x99tape_src › ta… captured on 2022-06-04 at 01:07:36.

View Raw

More Information

-=-=-=-=-=-=-

/*
	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;
}