mame/src/lib/formats/mbee_cas.cpp
Miodrag Milanovic ddb290d5f6 NOTICE (TYPE NAME CONSOLIDATION)
Use standard uint64_t, uint32_t, uint16_t or uint8_t instead of UINT64, UINT32, UINT16 or UINT8
also use standard int64_t, int32_t, int16_t or int8_t instead of INT64, INT32, INT16 or INT8
2016-10-22 13:13:17 +02:00

262 lines
8.0 KiB
C++

// license:BSD-3-Clause
// copyright-holders:Robbbert
/********************************************************************
Support for Microbee cassette images
Microbee tapes consist of 3 sections
1. A leader of 63 zeroes
2. A header which contains the program name and other info
3. The main program
Each byte after conversion becomes a start bit, bit 0,1,etc to 7,
then 2 stop bits.
At 1200 baud, a high = 2 cycles of 2400Hz and a low = 1 cycle of 1200Hz
At 300 baud, a high = 8 cycles of 2400Hz and a low = 4 cycles of 1200Hz
The header bytes are arranged thus:
1 (SOH) 0x01
6 File name
1 file type (M=machine language, B=Basic)
2 length
2 load address
2 exec address
1 tape speed (0 = 300 baud; other = 1200 baud)
1 auto-start (0 = no)
1 unassigned byte
1 CRC byte
The header is always at 300 baud; the program will be at the
speed indicated by the speed byte.
By coincidence (or not), the header is the same format as that
of the Sorcerer and SOL-20. In these, the speed and auto-start
bytes are unassigned. The CRC uses the same algorithm.
The main program is broken into blocks of 256, with each block
having its own CRC byte.
Microbee tape and quickload formats:
BEE - straight binary dump to address 0900, no header. For Machine
Language programs.
BIN - the standard z80bin format.
COM - straight binary dump to address 0100, no header. For Machine
Language programs.
MWB - straight binary dump to address 08C0, no header. For BASIC
programs.
TAP - has an ID header of TAP_DGOS_BEE or MBEE, null terminated.
This is followed by the binary dump with the leader and CRC
bytes included.
********************************************************************/
#include <assert.h>
#include "mbee_cas.h"
#define WAVEENTRY_LOW -32768
#define WAVEENTRY_HIGH 32767
#define MBEE_WAV_FREQUENCY 9600
// image size
static int mbee_image_size;
static bool mbee_speed;
static int mbee_put_samples(int16_t *buffer, int sample_pos, int count, int level)
{
if (buffer)
{
for (int i=0; i<count; i++)
buffer[sample_pos + i] = level;
}
return count;
}
static int mbee_output_bit(int16_t *buffer, int sample_pos, bool bit)
{
int samples = 0;
if (mbee_speed)
{
if (bit)
{
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
}
else
{
samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_LOW);
samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_HIGH);
}
}
else
{
if (bit)
{
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
}
else
{
samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_LOW);
samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_HIGH);
samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_LOW);
samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_HIGH);
samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_LOW);
samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_HIGH);
samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_LOW);
samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_HIGH);
}
}
return samples;
}
static int mbee_output_byte(int16_t *buffer, int sample_pos, uint8_t byte)
{
int samples = 0;
uint8_t i;
/* start */
samples += mbee_output_bit (buffer, sample_pos + samples, 0);
/* data */
for (i = 0; i<8; i++)
samples += mbee_output_bit (buffer, sample_pos + samples, (byte >> i) & 1);
/* stop */
for (i = 0; i<2; i++)
samples += mbee_output_bit (buffer, sample_pos + samples, 1);
return samples;
}
static int mbee_handle_tap(int16_t *buffer, const uint8_t *bytes)
{
uint32_t sample_count = 0;
uint32_t byte_count = 0;
uint32_t i = 0;
bool temp_speed = 0;
uint8_t temp_blocks = 0;
uint16_t temp_size = 0;
// TAP file starts with a null-terminate ID string. We just skip this.
while (bytes[byte_count])
byte_count++;
// there can be a library of files, loop through them all
while (byte_count < mbee_image_size)
{
mbee_speed = 0;
// now output the leader
while ( (!bytes[byte_count]) && (byte_count < mbee_image_size) )
sample_count += mbee_output_byte(buffer, sample_count, bytes[byte_count++]);
// make sure SOH is where we expect
if (bytes[byte_count] != 1 )
break;
// store the size for later
temp_blocks = bytes[byte_count + 9];
temp_size = bytes[byte_count + 8] + temp_blocks*256;
// store the speed for later
temp_speed = (bytes[byte_count + 15]) ? 1 : 0;
/* header */
for (i=0; i<18; i++)
sample_count += mbee_output_byte(buffer, sample_count, bytes[byte_count++]);
// change speed
mbee_speed = temp_speed;
// calculate size of program including CRC bytes
temp_size = temp_size + temp_blocks + 1;
/* data */
for (i=0; i<temp_size; i++)
sample_count += mbee_output_byte(buffer, sample_count, bytes[byte_count++]);
}
return sample_count;
}
/*******************************************************************
Generate samples for the tape image
********************************************************************/
static int mbee_tap_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
{
return mbee_handle_tap(buffer, bytes);
}
/*******************************************************************
Calculate the number of samples needed for this tape image
********************************************************************/
static int mbee_tap_calculate_size_in_samples(const uint8_t *bytes, int length)
{
mbee_image_size = length;
return mbee_handle_tap(nullptr, bytes);
}
static const struct CassetteLegacyWaveFiller mbee_tap_config =
{
mbee_tap_fill_wave, /* fill_wave */
-1, /* chunk_size */
0, /* chunk_samples */
mbee_tap_calculate_size_in_samples, /* chunk_sample_calc */
MBEE_WAV_FREQUENCY, /* sample_frequency */
0, /* header_samples */
0 /* trailer_samples */
};
static cassette_image::error mbee_tap_identify(cassette_image *cassette, struct CassetteOptions *opts)
{
return cassette_legacy_identify(cassette, opts, &mbee_tap_config);
}
static cassette_image::error mbee_tap_load(cassette_image *cassette)
{
return cassette_legacy_construct(cassette, &mbee_tap_config);
}
static const struct CassetteFormat mbee_tap_image_format =
{
"tap",
mbee_tap_identify,
mbee_tap_load,
nullptr
};
CASSETTE_FORMATLIST_START(mbee_cassette_formats)
CASSETTE_FORMAT(mbee_tap_image_format)
CASSETTE_FORMATLIST_END