Modernized decocass_tape device. (nw)

This commit is contained in:
Ivan Vangelista 2013-08-06 15:23:27 +00:00
parent aa65080862
commit 1862219897
3 changed files with 208 additions and 256 deletions

View File

@ -932,7 +932,7 @@ READ8_MEMBER(decocass_state::decocass_e5xx_r)
/* E5x2-E5x3 and mirrors */
if (2 == (offset & E5XX_MASK))
{
UINT8 bot_eot = (tape_get_status_bits(m_cassette) >> 5) & 1;
UINT8 bot_eot = (m_cassette->get_status_bits() >> 5) & 1;
data =
(BIT(m_i8041_p1, 7) << 0) | /* D0 = P17 - REQ/ */
@ -942,7 +942,7 @@ READ8_MEMBER(decocass_state::decocass_e5xx_r)
((bot_eot) << 4) | /* D4 = BOT/EOT (direct from drive) */
(1 << 5) | /* D5 floating input */
(1 << 6) | /* D6 floating input */
(!tape_is_present(m_cassette) << 7); /* D7 = cassette present */
(!m_cassette->is_present() << 7); /* D7 = cassette present */
LOG(4,("%10s 6502-PC: %04x decocass_e5xx_r(%02x): $%02x <- STATUS (%s%s%s%s%s%s%s%s)\n",
space.machine().time().as_string(6),
@ -1468,7 +1468,7 @@ WRITE8_MEMBER(decocass_state::i8041_p1_w)
newspeed = (data & 0x04) ? -1 : -7;
else if ((data & 0x30) == 0x10)
newspeed = (data & 0x04) ? 1 : 7;
tape_change_speed(m_cassette, newspeed);
m_cassette->change_speed(newspeed);
}
m_i8041_p1 = data;
@ -1522,7 +1522,7 @@ READ8_MEMBER(decocass_state::i8041_p2_r)
{
UINT8 data;
data = (m_i8041_p2 & ~0xe0) | tape_get_status_bits(m_cassette);
data = (m_i8041_p2 & ~0xe0) | m_cassette->get_status_bits();
if (data != m_i8041_p2_read_latch)
{

View File

@ -8,7 +8,6 @@
#include "cpu/m6502/m6502.h"
#include "cpu/mcs48/mcs48.h"
#include "machine/decocass_tape.h"
#include "devlegcy.h"
#define LOG_CASSETTE_STATE 0
@ -16,57 +15,6 @@
CASSETTE DEVICE INTERFACE
***************************************************************************/
/* regions within the virtual tape */
enum tape_region
{
REGION_LEADER, /* in clear leader section */
REGION_LEADER_GAP, /* in gap between leader and BOT */
REGION_BOT, /* in BOT hole */
REGION_BOT_GAP, /* in gap between BOT hole and data */
REGION_DATA_BLOCK_0, /* in data block 0 */
REGION_DATA_BLOCK_255 = REGION_DATA_BLOCK_0 + 255,
REGION_EOT_GAP, /* in gap between data and EOT hole */
REGION_EOT, /* in EOT hole */
REGION_TRAILER_GAP, /* in gap between trailer and EOT */
REGION_TRAILER /* in clear trailer section */
};
/* bytes within a data block on a virtual tape */
enum tape_byte
{
BYTE_PRE_GAP_0, /* 34 bytes of gap, clock held to 0, no data */
BYTE_PRE_GAP_33 = BYTE_PRE_GAP_0 + 33,
BYTE_LEADIN, /* 1 leadin byte, clocked value 0x00 */
BYTE_HEADER, /* 1 header byte, clocked value 0xAA */
BYTE_DATA_0, /* 256 bytes of data, clocked */
BYTE_DATA_255 = BYTE_DATA_0 + 255,
BYTE_CRC16_MSB, /* 2 bytes of CRC, clocked MSB first, then LSB */
BYTE_CRC16_LSB,
BYTE_TRAILER, /* 1 trailer byte, clocked value 0xAA */
BYTE_LEADOUT, /* 1 leadout byte, clocked value 0x00 */
BYTE_LONGCLOCK, /* 1 longclock byte, clock held to 1, no data */
BYTE_POSTGAP_0, /* 34 bytes of gap, no clock, no data */
BYTE_POSTGAP_33 = BYTE_POSTGAP_0 + 33,
BYTE_BLOCK_TOTAL /* total number of bytes in block */
};
/* state of the tape */
struct tape_state
{
running_machine * machine; /* pointer back to the machine */
emu_timer * timer; /* timer for running the tape */
INT8 speed; /* speed: <-1=fast rewind, -1=reverse, 0=stopped, 1=normal, >1=fast forward */
tape_region region; /* current region */
tape_byte bytenum; /* byte number within a datablock */
UINT8 bitnum; /* bit number within a byte */
UINT32 clockpos; /* the current clock position of the tape */
UINT32 numclocks; /* total number of clocks on the entire tape */
UINT16 crc16[256]; /* CRC16 for each block */
};
/* number of tape clock pulses per second */
#define TAPE_CLOCKRATE 4800
#define TAPE_CLOCKS_PER_BIT 2
@ -104,20 +52,88 @@ struct tape_state
#define REGION_BOT_GAP_LEN_CLOCKS TAPE_MSEC_TO_CLOCKS(300) /* 300ms */
#define REGION_BOT_GAP_END_CLOCK (REGION_BOT_GAP_START_CLOCK+REGION_BOT_GAP_LEN_CLOCKS)
static UINT16 tape_crc16_byte(UINT16 crc, UINT8 data);
/*-------------------------------------------------
get_safe_token - makes sure that the passed
in device is, in fact, an IDE controller
-------------------------------------------------*/
const device_type DECOCASS_TAPE = &device_creator<decocass_tape_device>;
INLINE tape_state *get_safe_token(device_t *device)
decocass_tape_device::decocass_tape_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: device_t(mconfig, DECOCASS_TAPE, "DECO Cassette Tape", tag, owner, clock, "decocass_tape", __FILE__),
m_tape_timer(NULL),
m_speed(0),
m_bitnum(0),
m_clockpos(0),
m_numclocks(0)
{
assert(device != NULL);
assert(device->type() == DECOCASS_TAPE);
return (tape_state *)downcast<decocass_tape_device *>(device)->token();
for (int i = 0; i < 256; i++)
m_crc16[i] = 0;
}
//-------------------------------------------------
// device_config_complete - perform any
// operations now that the configuration is
// complete
//-------------------------------------------------
void decocass_tape_device::device_config_complete()
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void decocass_tape_device::device_start()
{
int curblock, offs, numblocks;
/* fetch the data pointer */
m_tape_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(decocass_tape_device::tape_clock_callback), this));
if (region() == NULL)
return;
UINT8 *regionbase = *region();
/* scan for the first non-empty block in the image */
for (offs = region()->bytes() - 1; offs >= 0; offs--)
if (regionbase[offs] != 0)
break;
numblocks = ((offs | 0xff) + 1) / 256;
assert(numblocks < ARRAY_LENGTH(m_crc16));
/* compute the total length */
m_numclocks = REGION_BOT_GAP_END_CLOCK + numblocks * BYTE_BLOCK_TOTAL * 16 + REGION_BOT_GAP_END_CLOCK;
/* compute CRCs for each block */
for (curblock = 0; curblock < numblocks; curblock++)
{
UINT16 crc = 0;
int testval;
/* first CRC the 256 bytes of data */
for (offs = 256 * curblock; offs < 256 * curblock + 256; offs++)
crc = tape_crc16_byte(crc, regionbase[offs]);
/* then find a pair of bytes that will bring the CRC to 0 (any better way than brute force?) */
for (testval = 0; testval < 0x10000; testval++)
if (tape_crc16_byte(tape_crc16_byte(crc, testval >> 8), testval) == 0)
break;
m_crc16[curblock] = testval;
}
/* register states */
save_item(NAME(m_speed));
save_item(NAME(m_bitnum));
save_item(NAME(m_clockpos));
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void decocass_tape_device::device_reset()
{
/* turn the tape off */
change_speed(0);
}
/*-------------------------------------------------
tape_crc16_byte - accumulate 8 bits worth of
@ -139,33 +155,32 @@ static UINT16 tape_crc16_byte(UINT16 crc, UINT8 data)
return crc;
}
/*-------------------------------------------------
tape_describe_state - create a string that
describes the state of the tape
-------------------------------------------------*/
static const char *tape_describe_state(tape_state *tape)
const char *decocass_tape_device::describe_state()
{
static char buffer[40];
char temprname[40];
const char *rname = temprname;
if (tape->region == REGION_LEADER)
if (m_region == REGION_LEADER)
rname = "LEAD";
else if (tape->region == REGION_LEADER_GAP)
else if (m_region == REGION_LEADER_GAP)
rname = "LGAP";
else if (tape->region == REGION_BOT)
else if (m_region == REGION_BOT)
rname = "BOT ";
else if (tape->region == REGION_BOT_GAP)
else if (m_region == REGION_BOT_GAP)
rname = "BGAP";
else if (tape->region == REGION_TRAILER)
else if (m_region == REGION_TRAILER)
rname = "TRLR";
else if (tape->region == REGION_TRAILER_GAP)
else if (m_region == REGION_TRAILER_GAP)
rname = "TGAP";
else if (tape->region == REGION_EOT)
else if (m_region == REGION_EOT)
rname = "EOT ";
else if (tape->region == REGION_EOT_GAP)
else if (m_region == REGION_EOT_GAP)
rname = "EGAP";
else
{
@ -173,39 +188,39 @@ static const char *tape_describe_state(tape_state *tape)
const char *bname = tempbname;
int clk;
if (tape->bytenum <= BYTE_PRE_GAP_33)
sprintf(tempbname, "PR%02d", tape->bytenum - BYTE_PRE_GAP_0);
else if (tape->bytenum == BYTE_LEADIN)
if (m_bytenum <= BYTE_PRE_GAP_33)
sprintf(tempbname, "PR%02d", m_bytenum - BYTE_PRE_GAP_0);
else if (m_bytenum == BYTE_LEADIN)
bname = "LDIN";
else if (tape->bytenum == BYTE_HEADER)
else if (m_bytenum == BYTE_HEADER)
bname = "HEAD";
else if (tape->bytenum <= BYTE_DATA_255)
sprintf(tempbname, "BY%02X", tape->bytenum - BYTE_DATA_0);
else if (tape->bytenum == BYTE_CRC16_MSB)
else if (m_bytenum <= BYTE_DATA_255)
sprintf(tempbname, "BY%02X", m_bytenum - BYTE_DATA_0);
else if (m_bytenum == BYTE_CRC16_MSB)
bname = "CRCM";
else if (tape->bytenum == BYTE_CRC16_LSB)
else if (m_bytenum == BYTE_CRC16_LSB)
bname = "CRCL";
else if (tape->bytenum == BYTE_TRAILER)
else if (m_bytenum == BYTE_TRAILER)
bname = "TRLR";
else if (tape->bytenum == BYTE_LEADOUT)
else if (m_bytenum == BYTE_LEADOUT)
bname = "LOUT";
else if (tape->bytenum == BYTE_LONGCLOCK)
else if (m_bytenum == BYTE_LONGCLOCK)
bname = "LONG";
else
sprintf(tempbname, "PO%02d", tape->bytenum - BYTE_POSTGAP_0);
sprintf(tempbname, "PO%02d", m_bytenum - BYTE_POSTGAP_0);
/* in the main data area, the clock alternates at the clock rate */
if (tape->bytenum >= BYTE_LEADIN && tape->bytenum <= BYTE_LEADOUT)
clk = ((UINT32)(tape->clockpos - REGION_BOT_GAP_END_CLOCK) & 1) ? 0 : 1;
else if (tape->bytenum == BYTE_LONGCLOCK)
if (m_bytenum >= BYTE_LEADIN && m_bytenum <= BYTE_LEADOUT)
clk = ((UINT32)(m_clockpos - REGION_BOT_GAP_END_CLOCK) & 1) ? 0 : 1;
else if (m_bytenum == BYTE_LONGCLOCK)
clk = 1;
else
clk = 0;
sprintf(temprname, "BL%02X.%4s.%d.%d", tape->region - REGION_DATA_BLOCK_0, bname, tape->bitnum, clk);
sprintf(temprname, "BL%02X.%4s.%d.%d", m_region - REGION_DATA_BLOCK_0, bname, m_bitnum, clk);
}
sprintf(buffer, "{%9d=%s}", tape->clockpos, rname);
sprintf(buffer, "{%9d=%s}", m_clockpos, rname);
return buffer;
}
@ -215,57 +230,54 @@ static const char *tape_describe_state(tape_state *tape)
to increment/decrement the tape location
-------------------------------------------------*/
static TIMER_CALLBACK( tape_clock_callback )
TIMER_CALLBACK_MEMBER( decocass_tape_device::tape_clock_callback )
{
device_t *device = (device_t *)ptr;
tape_state *tape = get_safe_token(device);
/* advance by one clock in the desired direction */
if (tape->speed < 0 && tape->clockpos > 0)
tape->clockpos--;
else if (tape->speed > 0 && tape->clockpos < tape->numclocks)
tape->clockpos++;
if (m_speed < 0 && m_clockpos > 0)
m_clockpos--;
else if (m_speed > 0 && m_clockpos < m_numclocks)
m_clockpos++;
/* look for states before the start of data */
if (tape->clockpos < REGION_LEADER_END_CLOCK)
tape->region = REGION_LEADER;
else if (tape->clockpos < REGION_LEADER_GAP_END_CLOCK)
tape->region = REGION_LEADER_GAP;
else if (tape->clockpos < REGION_BOT_END_CLOCK)
tape->region = REGION_BOT;
else if (tape->clockpos < REGION_BOT_GAP_END_CLOCK)
tape->region = REGION_BOT_GAP;
if (m_clockpos < REGION_LEADER_END_CLOCK)
m_region = REGION_LEADER;
else if (m_clockpos < REGION_LEADER_GAP_END_CLOCK)
m_region = REGION_LEADER_GAP;
else if (m_clockpos < REGION_BOT_END_CLOCK)
m_region = REGION_BOT;
else if (m_clockpos < REGION_BOT_GAP_END_CLOCK)
m_region = REGION_BOT_GAP;
/* look for states after the end of data */
else if (tape->clockpos >= tape->numclocks - REGION_LEADER_END_CLOCK)
tape->region = REGION_TRAILER;
else if (tape->clockpos >= tape->numclocks - REGION_LEADER_GAP_END_CLOCK)
tape->region = REGION_TRAILER_GAP;
else if (tape->clockpos >= tape->numclocks - REGION_BOT_END_CLOCK)
tape->region = REGION_EOT;
else if (tape->clockpos >= tape->numclocks - REGION_BOT_GAP_END_CLOCK)
tape->region = REGION_EOT_GAP;
else if (m_clockpos >= m_numclocks - REGION_LEADER_END_CLOCK)
m_region = REGION_TRAILER;
else if (m_clockpos >= m_numclocks - REGION_LEADER_GAP_END_CLOCK)
m_region = REGION_TRAILER_GAP;
else if (m_clockpos >= m_numclocks - REGION_BOT_END_CLOCK)
m_region = REGION_EOT;
else if (m_clockpos >= m_numclocks - REGION_BOT_GAP_END_CLOCK)
m_region = REGION_EOT_GAP;
/* everything else is data */
else
{
UINT32 dataclock = tape->clockpos - REGION_BOT_GAP_END_CLOCK;
UINT32 dataclock = m_clockpos - REGION_BOT_GAP_END_CLOCK;
/* compute the block number */
tape->region = (tape_region)(REGION_DATA_BLOCK_0 + dataclock / (TAPE_CLOCKS_PER_BYTE * BYTE_BLOCK_TOTAL));
dataclock -= (tape->region - REGION_DATA_BLOCK_0) * TAPE_CLOCKS_PER_BYTE * BYTE_BLOCK_TOTAL;
m_region = (tape_region)(REGION_DATA_BLOCK_0 + dataclock / (TAPE_CLOCKS_PER_BYTE * BYTE_BLOCK_TOTAL));
dataclock -= (m_region - REGION_DATA_BLOCK_0) * TAPE_CLOCKS_PER_BYTE * BYTE_BLOCK_TOTAL;
/* compute the byte within the block */
tape->bytenum = (tape_byte)(dataclock / TAPE_CLOCKS_PER_BYTE);
dataclock -= tape->bytenum * TAPE_CLOCKS_PER_BYTE;
m_bytenum = (tape_byte)(dataclock / TAPE_CLOCKS_PER_BYTE);
dataclock -= m_bytenum * TAPE_CLOCKS_PER_BYTE;
/* compute the bit within the byte */
tape->bitnum = dataclock / TAPE_CLOCKS_PER_BIT;
m_bitnum = dataclock / TAPE_CLOCKS_PER_BIT;
}
/* log */
if (LOG_CASSETTE_STATE)
tape_describe_state(tape);
describe_state();
}
@ -274,28 +286,27 @@ static TIMER_CALLBACK( tape_clock_callback )
bits from the tape
-------------------------------------------------*/
UINT8 tape_get_status_bits(device_t *device)
UINT8 decocass_tape_device::get_status_bits()
{
tape_state *tape = get_safe_token(device);
UINT8 tape_bits = 0;
/* bit 0x20 is the BOT/EOT signal, which is also set in the leader/trailer area */
if (tape->region == REGION_LEADER || tape->region == REGION_BOT || tape->region == REGION_EOT || tape->region == REGION_TRAILER)
if (m_region == REGION_LEADER || m_region == REGION_BOT || m_region == REGION_EOT || m_region == REGION_TRAILER)
tape_bits |= 0x20;
/* bit 0x40 is the clock, which is only valid in some areas of the data block */
/* bit 0x80 is the data, which is only valid in some areas of the data block */
if (tape->region >= REGION_DATA_BLOCK_0 && tape->region <= REGION_DATA_BLOCK_255)
if (m_region >= REGION_DATA_BLOCK_0 && m_region <= REGION_DATA_BLOCK_255)
{
int blocknum = tape->region - REGION_DATA_BLOCK_0;
int blocknum = m_region - REGION_DATA_BLOCK_0;
UINT8 byteval = 0x00;
/* in the main data area, the clock alternates at the clock rate */
if (tape->bytenum >= BYTE_LEADIN && tape->bytenum <= BYTE_LEADOUT)
tape_bits |= ((UINT32)(tape->clockpos - REGION_BOT_GAP_END_CLOCK) & 1) ? 0x00 : 0x40;
if (m_bytenum >= BYTE_LEADIN && m_bytenum <= BYTE_LEADOUT)
tape_bits |= ((UINT32)(m_clockpos - REGION_BOT_GAP_END_CLOCK) & 1) ? 0x00 : 0x40;
/* in the longclock area, the clock holds high */
else if (tape->bytenum == BYTE_LONGCLOCK)
else if (m_bytenum == BYTE_LONGCLOCK)
tape_bits |= 0x40;
/* everywhere else, the clock holds to 0 */
@ -303,23 +314,23 @@ UINT8 tape_get_status_bits(device_t *device)
;
/* lead-in and lead-out bytes are 0xAA */
if (tape->bytenum == BYTE_HEADER || tape->bytenum == BYTE_TRAILER)
if (m_bytenum == BYTE_HEADER || m_bytenum == BYTE_TRAILER)
byteval = 0xaa;
/* data block bytes are data */
else if (tape->bytenum >= BYTE_DATA_0 && tape->bytenum <= BYTE_DATA_255)
byteval = static_cast<UINT8 *>(*device->region())[blocknum * 256 + (tape->bytenum - BYTE_DATA_0)];
else if (m_bytenum >= BYTE_DATA_0 && m_bytenum <= BYTE_DATA_255)
byteval = static_cast<UINT8 *>(*region())[blocknum * 256 + (m_bytenum - BYTE_DATA_0)];
/* CRC MSB */
else if (tape->bytenum == BYTE_CRC16_MSB)
byteval = tape->crc16[blocknum] >> 8;
else if (m_bytenum == BYTE_CRC16_MSB)
byteval = m_crc16[blocknum] >> 8;
/* CRC LSB */
else if (tape->bytenum == BYTE_CRC16_LSB)
byteval = tape->crc16[blocknum];
else if (m_bytenum == BYTE_CRC16_LSB)
byteval = m_crc16[blocknum];
/* select the appropriate bit from the byte and move to the upper bit */
if ((byteval >> tape->bitnum) & 1)
if ((byteval >> m_bitnum) & 1)
tape_bits |= 0x80;
}
return tape_bits;
@ -331,9 +342,9 @@ UINT8 tape_get_status_bits(device_t *device)
present
-------------------------------------------------*/
UINT8 tape_is_present(device_t *device)
UINT8 decocass_tape_device::is_present()
{
return device->region() != NULL;
return region() != NULL;
}
@ -342,14 +353,13 @@ UINT8 tape_is_present(device_t *device)
playback
-------------------------------------------------*/
void tape_change_speed(device_t *device, INT8 newspeed)
void decocass_tape_device::change_speed(INT8 newspeed)
{
tape_state *tape = get_safe_token(device);
attotime newperiod;
INT8 absnewspeed;
/* do nothing if speed has not changed */
if (tape->speed == newspeed)
if (m_speed == newspeed)
return;
/* compute how fast to run the tape timer */
@ -360,107 +370,6 @@ void tape_change_speed(device_t *device, INT8 newspeed)
newperiod = attotime::from_hz(TAPE_CLOCKRATE * absnewspeed);
/* set the new speed */
tape->timer->adjust(newperiod, 0, newperiod);
tape->speed = newspeed;
}
/*-------------------------------------------------
device start callback
-------------------------------------------------*/
static DEVICE_START( decocass_tape )
{
tape_state *tape = get_safe_token(device);
int curblock, offs, numblocks;
/* validate some basic stuff */
assert(device != NULL);
assert(device->static_config() == NULL);
/* fetch the data pointer */
tape->timer = device->machine().scheduler().timer_alloc(FUNC(tape_clock_callback), (void *)device);
if (device->region() == NULL)
return;
UINT8 *regionbase = *device->region();
/* scan for the first non-empty block in the image */
for (offs = device->region()->bytes() - 1; offs >= 0; offs--)
if (regionbase[offs] != 0)
break;
numblocks = ((offs | 0xff) + 1) / 256;
assert(numblocks < ARRAY_LENGTH(tape->crc16));
/* compute the total length */
tape->numclocks = REGION_BOT_GAP_END_CLOCK + numblocks * BYTE_BLOCK_TOTAL * 16 + REGION_BOT_GAP_END_CLOCK;
/* compute CRCs for each block */
for (curblock = 0; curblock < numblocks; curblock++)
{
UINT16 crc = 0;
int testval;
/* first CRC the 256 bytes of data */
for (offs = 256 * curblock; offs < 256 * curblock + 256; offs++)
crc = tape_crc16_byte(crc, regionbase[offs]);
/* then find a pair of bytes that will bring the CRC to 0 (any better way than brute force?) */
for (testval = 0; testval < 0x10000; testval++)
if (tape_crc16_byte(tape_crc16_byte(crc, testval >> 8), testval) == 0)
break;
tape->crc16[curblock] = testval;
}
/* register states */
device->save_item(NAME(tape->speed));
device->save_item(NAME(tape->bitnum));
device->save_item(NAME(tape->clockpos));
}
/*-------------------------------------------------
device reset callback
-------------------------------------------------*/
static DEVICE_RESET( decocass_tape )
{
/* turn the tape off */
tape_change_speed(device, 0);
}
const device_type DECOCASS_TAPE = &device_creator<decocass_tape_device>;
decocass_tape_device::decocass_tape_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: device_t(mconfig, DECOCASS_TAPE, "DECO Cassette Tape", tag, owner, clock, "decocass_tape", __FILE__)
{
m_token = global_alloc_clear(tape_state);
}
//-------------------------------------------------
// device_config_complete - perform any
// operations now that the configuration is
// complete
//-------------------------------------------------
void decocass_tape_device::device_config_complete()
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void decocass_tape_device::device_start()
{
DEVICE_START_NAME( decocass_tape )(this);
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void decocass_tape_device::device_reset()
{
DEVICE_RESET_NAME( decocass_tape )(this);
m_tape_timer->adjust(newperiod, 0, newperiod);
m_speed = newspeed;
}

View File

@ -5,27 +5,70 @@ class decocass_tape_device : public device_t
{
public:
decocass_tape_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
~decocass_tape_device() { global_free(m_token); }
~decocass_tape_device() {}
// access to legacy token
void *token() const { assert(m_token != NULL); return m_token; }
UINT8 get_status_bits();
UINT8 is_present();
void change_speed(INT8 newspeed);
protected:
// device-level overrides
virtual void device_config_complete();
virtual void device_start();
virtual void device_reset();
private:
/* regions within the virtual tape */
enum tape_region
{
REGION_LEADER, /* in clear leader section */
REGION_LEADER_GAP, /* in gap between leader and BOT */
REGION_BOT, /* in BOT hole */
REGION_BOT_GAP, /* in gap between BOT hole and data */
REGION_DATA_BLOCK_0, /* in data block 0 */
REGION_DATA_BLOCK_255 = REGION_DATA_BLOCK_0 + 255,
REGION_EOT_GAP, /* in gap between data and EOT hole */
REGION_EOT, /* in EOT hole */
REGION_TRAILER_GAP, /* in gap between trailer and EOT */
REGION_TRAILER /* in clear trailer section */
};
/* bytes within a data block on a virtual tape */
enum tape_byte
{
BYTE_PRE_GAP_0, /* 34 bytes of gap, clock held to 0, no data */
BYTE_PRE_GAP_33 = BYTE_PRE_GAP_0 + 33,
BYTE_LEADIN, /* 1 leadin byte, clocked value 0x00 */
BYTE_HEADER, /* 1 header byte, clocked value 0xAA */
BYTE_DATA_0, /* 256 bytes of data, clocked */
BYTE_DATA_255 = BYTE_DATA_0 + 255,
BYTE_CRC16_MSB, /* 2 bytes of CRC, clocked MSB first, then LSB */
BYTE_CRC16_LSB,
BYTE_TRAILER, /* 1 trailer byte, clocked value 0xAA */
BYTE_LEADOUT, /* 1 leadout byte, clocked value 0x00 */
BYTE_LONGCLOCK, /* 1 longclock byte, clock held to 1, no data */
BYTE_POSTGAP_0, /* 34 bytes of gap, no clock, no data */
BYTE_POSTGAP_33 = BYTE_POSTGAP_0 + 33,
BYTE_BLOCK_TOTAL /* total number of bytes in block */
};
// internal state
void *m_token;
emu_timer * m_tape_timer; /* timer for running the tape */
INT8 m_speed; /* speed: <-1=fast rewind, -1=reverse, 0=stopped, 1=normal, >1=fast forward */
tape_region m_region; /* current region */
tape_byte m_bytenum; /* byte number within a datablock */
UINT8 m_bitnum; /* bit number within a byte */
UINT32 m_clockpos; /* the current clock position of the tape */
UINT32 m_numclocks; /* total number of clocks on the entire tape */
UINT16 m_crc16[256]; /* CRC16 for each block */
const char *describe_state();
TIMER_CALLBACK_MEMBER( tape_clock_callback );
};
extern const device_type DECOCASS_TAPE;
UINT8 tape_get_status_bits(device_t *device);
UINT8 tape_is_present(device_t *device);
void tape_change_speed(device_t *device, INT8 newspeed);
#define MCFG_DECOCASS_TAPE_ADD(_tag) \
MCFG_DEVICE_ADD(_tag, DECOCASS_TAPE, 0)