// license:BSD-3-Clause
// copyright-holders:Antoine Mine
/**********************************************************************
Copyright (C) Antoine Mine' 2006
Thomson 8-bit computers
**********************************************************************/
#include "emu.h"
#include "includes/thomson.h"
#include "machine/wd_fdc.h"
#define VERBOSE 0 /* 0, 1 or 2 */
#define PRINT(x) osd_printf_info x
#define LOG(x) do { if (VERBOSE > 0) logerror x; } while (0)
#define VLOG(x) do { if (VERBOSE > 1) logerror x; } while (0)
/******************* 3''1/2 & 5''1/4 disk format ********************/
/*
single density sector format:
data bytes
id field 00 6 byte synchro
FE 1 id field mark
1 track
1 side
1 sector
00 1 log sector size (0->128, 1->256, 2->512,...)
2 CRC (unemulated)
FF 12 spaces
data field 00 6 bytes synchro
FB 1 data field mark
E5 128 actual data (set to E5 when formatting)
2 CRC (unemulated)
FF 22 ? spaces
double density sector format:
data bytes
id field 00 12 bit synchro
A1 3 byte synchro
FE 1 id field mark
1 track
1 side
1 sector
01 1 log sector size (0->128, 1->256, 2->512,...)
2 CRC (unemulated)
4E/F7 22 spaces
data field 00 12 bit synchro
A1 3 bytes synchro
FB 1 data field mark
E5 256 actual data (set to E5 when formatting)
2 CRC (unemulated)
4E/F7 74 ? spaces
=> at most 392 bytes / sector
=> at most 6272 bytes / track
Notes:
- the BIOS puts 4E bytes as spaces after id and data fields
- some protected games expect F7 bytes (instead of 4E) after id fields
*/
#define THOM_SIZE_ID 32
#define THOM_SIZE_DATA_LO (128+80)
#define THOM_SIZE_DATA_HI (256+80)
#define THOM_SIZE_SYNCHRO 12
static int motor_on;
/* build an identifier, with header & space */
int thomson_state::thom_floppy_make_addr( chrn_id id, uint8_t* dst, int sector_size )
{
if ( sector_size == 128 )
{
/* single density */
memset( dst, 0x00, 6 ); /* synchro bytes */
dst[ 7 ] = 0xfe; /* address field mark */
dst[ 8 ] = id.C;
dst[ 9 ] = id.H;
dst[ 10 ] = id.N;
dst[ 11 ] = id.R;
dst[ 12 ] = 0; /* TODO: CRC */
dst[ 13 ] = 0; /* TODO: CRC */
memset( dst + 14, 0xff, 12 ); /* end mark */
return 36;
}
else
{
/* double density */
memset( dst, 0xa1, 3 ); /* synchro bytes */
dst[ 3 ] = 0xfe; /* address field mark */
dst[ 4 ] = id.C;
dst[ 5 ] = id.H;
dst[ 6 ] = id.N;
dst[ 7 ] = id.R;
dst[ 8 ] = 0; /* TODO: CRC */
dst[ 9 ] = 0; /* TODO: CRC */
memset( dst + 10, 0xf7, 22 ); /* end mark */
return 32;
}
}
/* build a sector, with header & space */
int thomson_state::thom_floppy_make_sector( legacy_floppy_image_device* img, chrn_id id, uint8_t* dst, int sector_size )
{
if ( sector_size == 128 )
{
/* single density */
memset( dst, 0x00, 6 ); /* synchro bytes */
dst[ 6 ] = 0xfb; /* data field mark */
img->floppy_drive_read_sector_data
( id.H, id.data_id, dst + 7, sector_size );
dst[ sector_size + 7 ] = 0; /* TODO: CRC */
dst[ sector_size + 8 ] = 0; /* TODO: CRC */
memset( dst + sector_size + 9, 0xff, 22 ); /* end mark */
return sector_size + 31;
}
else
{
/* double density */
memset( dst, 0xa1, 3 ); /* synchro bytes */
dst[ 3 ] = 0xfb; /* data field mark */
img->floppy_drive_read_sector_data
( id.H, id.data_id, dst + 4, sector_size );
dst[ sector_size + 4 ] = 0; /* TODO: CRC */
dst[ sector_size + 5 ] = 0; /* TODO: CRC */
memset( dst + sector_size + 6, 0xF7, 74 ); /* end mark */
return sector_size + 80;
}
}
/* build a whole track */
int thomson_state::thom_floppy_make_track( legacy_floppy_image_device* img, uint8_t* dst, int sector_size, int side )
{
uint8_t space = ( sector_size == 128 ) ? 0xff : 0;
uint8_t* org = dst;
chrn_id id;
int nb;
/* go to start of track */
while ( ! img->floppy_drive_get_flag_state( FLOPPY_DRIVE_INDEX ) )
{
if ( ! img->floppy_drive_get_next_id( side, &id ) )
return 0;
}
/* for each sector... */
for ( nb = 0; nb < 16; nb++ )
{
if ( ! img->floppy_drive_get_next_id( side, &id ) )
break;
memset( dst, space, THOM_SIZE_SYNCHRO ); dst += THOM_SIZE_SYNCHRO;
dst += thom_floppy_make_addr( id, dst, sector_size );
memset( dst, space, THOM_SIZE_SYNCHRO ); dst += THOM_SIZE_SYNCHRO;
dst += thom_floppy_make_sector( img, id, dst, sector_size );
if ( img->floppy_drive_get_flag_state( FLOPPY_DRIVE_INDEX ) )
break;
}
return dst - org;
}
/******************* QDD disk format ********************/
/*
sector format:
data bytes
id field 16 17 synchro
A5 1 id field mark
1 sector (1-400) hi byte
1 sector (1-400) low byte
1 check-sum (sum modulo 256 of 3 last bytes)
data field 16 10 synchro
5A 1 data field mark
FF 128 actual data (set to FF when formatting)
1 check-sum (sum modulo 256 of 129 last bytes)
there are 400 sectors numbered from 1
*/
#define THOM_QDD_SYNCH_DISK 100
#define THOM_QDD_SYNCH_ADDR 17
#define THOM_QDD_SYNCH_DATA 10
#define THOM_QDD_SIZE_ID ( 4 + THOM_QDD_SYNCH_ADDR )
#define THOM_QDD_SIZE_DATA ( 130 + THOM_QDD_SYNCH_DATA )
/* build an identifier, with header */
int thomson_state::thom_qdd_make_addr( int sector, uint8_t* dst )
{
dst[ 0 ] = 0xa5;
dst[ 1 ] = sector >> 8;
dst[ 2 ] = sector & 0xff;
dst[ 3 ] = dst[ 0 ] + dst[ 1 ] + dst[ 2 ];
return 4;
}
/* build a sector, with header */
int thomson_state::thom_qdd_make_sector( legacy_floppy_image_device* img, int sector, uint8_t* dst )
{
int i;
dst[ 0 ] = 0x5a;
img->floppy_drive_read_sector_data ( 0, sector, dst + 1, 128 );
dst[ 129 ] = 0;
for ( i = 0; i < 129; i++ )
dst[ 129 ] += dst[ i ];
return 130;
}
/* build a whole disk */
int thomson_state::thom_qdd_make_disk ( legacy_floppy_image_device* img, uint8_t* dst )
{
uint8_t* org = dst;
int i;
memset( dst, 0x16, THOM_QDD_SYNCH_DISK ); dst += THOM_QDD_SYNCH_DISK;
for ( i = 1; i <= 400; i++ )
{
memset( dst, 0x16, THOM_QDD_SYNCH_ADDR ); dst += THOM_QDD_SYNCH_ADDR;
dst += thom_qdd_make_addr( i, dst );
memset( dst, 0x16, THOM_QDD_SYNCH_DATA ); dst += THOM_QDD_SYNCH_DATA;
dst += thom_qdd_make_sector( img, i, dst );
}
memset( dst, 0x16, THOM_QDD_SYNCH_DISK ); dst += THOM_QDD_SYNCH_DISK;
return dst - org;
}
/*********************** CD 90-640 controller ************************/
/* 5''1/4 two-sided double-density
used in TO7, TO7/70, MO5 computers
based on a WD2793 (or lower ?) chip
*/
static uint8_t to7_5p14_select;
READ8_MEMBER( thomson_state::to7_5p14_r )
{
wd2793_device *fdc = machine().device<wd2793_device>("wd2793");
if ( offset < 4 )
return fdc->read(space, offset );
else if ( offset == 8 )
return to7_5p14_select;
else
logerror ( "%f $%04x to7_5p14_r: invalid read offset %i\n", machine().time().as_double(), m_maincpu->pc(), offset );
return 0;
}
WRITE8_MEMBER( thomson_state::to7_5p14_w )
{
wd2793_device *fdc = machine().device<wd2793_device>("wd2793");
if ( offset < 4 )
fdc->write(space, offset, data );
else if ( offset == 8 )
{
/* drive select */
floppy_image_device *floppy = nullptr;
if (BIT(data, 1)) floppy = fdc->subdevice<floppy_connector>("0")->get_device();
if (BIT(data, 2)) floppy = fdc->subdevice<floppy_connector>("1")->get_device();
fdc->set_floppy(floppy);
if (floppy)
{
thom_floppy_active( 0 );
floppy->mon_w(0);
floppy->ss_w(BIT(data, 0));
}
}
else
logerror ( "%f $%04x to7_5p14_w: invalid write offset %i (data=$%02X)\n",
machine().time().as_double(), m_maincpu->pc(), offset, data );
}
void thomson_state::to7_5p14_reset()
{
wd2793_device *fdc = machine().device<wd2793_device>("wd2793");
LOG(( "to7_5p14_reset: CD 90-640 controller\n" ));
fdc->reset();
}
void thomson_state::to7_5p14_init()
{
LOG(( "to7_5p14_init: CD 90-640 controller\n" ));
save_item(NAME(to7_5p14_select));
}
/*********************** CD 90-015 controller ************************/
/* 5''1/4 one-sided single-density (up to 4 one-sided drives)
used in TO7, TO7/70, MO5 computers
based on HD 46503 S chip, but we actually use a MC 6843 instead
(seems they are clone)
*/
static uint8_t to7_5p14sd_select;
READ8_MEMBER( thomson_state::to7_5p14sd_r )
{
if ( offset < 8 )
return m_mc6843->read( space, offset );
else if ( offset >= 8 && offset <= 9 )
return to7_5p14sd_select;
else
logerror ( "%f $%04x to7_5p14sd_r: invalid read offset %i\n", machine().time().as_double(), m_maincpu->pc(), offset );
return 0;
}
WRITE8_MEMBER( thomson_state::to7_5p14sd_w )
{
if ( offset < 8 )
m_mc6843->write( space, offset, data );
else if ( offset >= 8 && offset <= 9 )
{
/* drive select */
int drive = -1, side = 0;
if ( data & 1 )
{
drive = 0;
side = 0;
}
else if ( data & 2 )
{
drive = 1;
side = 1;
}
else if ( data & 4 )
{
drive = 2;
side = 0;
}
else if ( data & 8 )
{
drive = 3;
side = 1;
}
to7_5p14sd_select = data;
if ( drive != -1 )
{
thom_floppy_active( 0 );
m_mc6843->set_drive( drive );
m_mc6843->set_side( side );
LOG(( "%f $%04x to7_5p14sd_w: $%02X set drive=%i side=%i\n",
machine().time().as_double(), m_maincpu->pc(), data, drive, side ));
}
}
else
logerror ( "%f $%04x to7_5p14sd_w: invalid write offset %i (data=$%02X)\n",
machine().time().as_double(), m_maincpu->pc(), offset, data );
}
void thomson_state::to7_5p14_index_pulse_callback( device_t *controller,legacy_floppy_image_device *image, int state )
{
m_mc6843->set_index_pulse( state );
}
void thomson_state::to7_5p14sd_reset()
{
int i;
LOG(( "to7_5p14sd_reset: CD 90-015 controller\n" ));
for ( i = 0; i < floppy_get_count( machine() ); i++ )
{
legacy_floppy_image_device * img = floppy_get_device( machine(), i );
if (img) {
img->floppy_drive_set_ready_state( FLOPPY_DRIVE_READY, 0 );
img->floppy_drive_set_rpm( 300. );
img->floppy_drive_seek( - img->floppy_drive_get_current_track() );
}
}
}
void thomson_state::to7_5p14sd_init()
{
LOG(( "to7_5p14sd_init: CD 90-015 controller\n" ));
save_item(NAME(to7_5p14sd_select));
}
/*********************** CQ 90-028 controller ************************/
/* QDD 2''8 controller
used in TO7, TO7/70, MO5 computers
it is based on a MC6852 SSDA
Note: the MC6852 is only partially emulated, most features are not used in
the controller and are ignored.
*/
/* MC6852 status */
#define QDD_S_RDA 0x01 /* receiver data available */
#define QDD_S_TDRA 0x02 /* transitter data register available */
#define QDD_S_NDCD 0x04 /* data carrier detect, negated (unused) */
#define QDD_S_NCTS 0x08 /* clear-to-send, negated write-protect */
#define QDD_S_TUF 0x10 /* transmitter underflow (unused) */
#define QDD_S_OVR 0x20 /* receiver overrun (unused) */
#define QDD_S_PE 0x40 /* receiver parity error (unused) */
#define QDD_S_IRQ 0x80 /* interrupt request */
#define QDD_S_ERR (QDD_S_TUF | QDD_S_OVR | QDD_S_PE | QDD_S_NDCD | QDD_S_NCTS)
/* MC6852 control */
#define QDD_C1_RIE 0x20 /* interrupt on reveive */
#define QDD_C1_TIE 0x10 /* interrupt on transmit */
#define QDD_C1_CLRSYNC 0x08 /* clear receiver sync char (unused) */
#define QDD_C1_STRIPSYNC 0x04 /* strips sync from received (unused) */
#define QDD_C1_TRESET 0x02 /* transmitter reset */
#define QDD_C1_RRESET 0x01 /* receiver reset */
#define QDD_C2_EIE 0x80 /* interrupt on error */
#define QDD_C2_TSYNC 0x40 /* underflow = ff if 0 / sync if 1 (unused) */
#define QDD_C2_BLEN 0x04 /* transfer byte length (unused) */
#define QDD_C3_CLRTUF 0x08 /* clear underflow */
#define QDD_C3_CLRCTS 0x04 /* clear CTS */
#define QDD_C3_SYNCLEN 0x02 /* sync byte length (unused) */
#define QDD_C3_SYNCMODE 0x01 /* external / internal sync mode (unused) */
/* a track is actually the whole disk = 400 128-byte sectors + headers */
#define QDD_MAXBUF ( THOM_QDD_SIZE_ID + THOM_QDD_SIZE_DATA ) * 512
struct to7qdd_t
{
/* MC6852 registers */
uint8_t status;
uint8_t ctrl1;
uint8_t ctrl2;
uint8_t ctrl3;
/* extra registers */
uint8_t drive;
/* internal state */
uint8_t data[QDD_MAXBUF]; /* enough for a whole track */
uint32_t data_idx; /* byte position in track */
uint32_t start_idx; /* start of write position */
uint32_t data_size; /* track length */
uint8_t data_crc; /* checksum when writing */
uint8_t index_pulse; /* one pulse per track */
};
static to7qdd_t * to7qdd;
void thomson_state::to7_qdd_index_pulse_cb( device_t *controller,legacy_floppy_image_device *image, int state )
{
to7qdd->index_pulse = state;
if ( state )
{
/* rewind to disk start */
to7qdd->data_idx = 0;
to7qdd->start_idx = 0;
to7qdd->data_size = 0;
}
VLOG(( "%f to7_qdd_pulse_cb: state=%i\n", machine().time().as_double(), state ));
}
legacy_floppy_image_device * thomson_state::to7_qdd_image()
{
return floppy_get_device( machine(), 0 );
}
/* update MC6852 status register */
void thomson_state::to7_qdd_stat_update()
{
/* byte-ready */
to7qdd->status |= QDD_S_RDA | QDD_S_TDRA;
if ( ! to7qdd->drive )
to7qdd->status |= QDD_S_PE;
/* write-protect */
if (to7_qdd_image()->floppy_wpt_r() == CLEAR_LINE)
to7qdd->status |= QDD_S_NCTS;
/* sticky reset conditions */
if ( to7qdd->ctrl1 & QDD_C1_RRESET )
to7qdd->status &= ~(QDD_S_PE | QDD_S_RDA | QDD_S_OVR);
if ( to7qdd->ctrl1 & QDD_C1_TRESET )
to7qdd->status &= ~(QDD_S_TDRA | QDD_S_TUF);
/* irq update */
if ( ( (to7qdd->ctrl1 & QDD_C1_RIE) && !(to7qdd->status & QDD_S_RDA ) ) ||
( (to7qdd->ctrl1 & QDD_C1_TIE) && !(to7qdd->status & QDD_S_TDRA) ) ||
( (to7qdd->ctrl2 & QDD_C2_EIE) && !(to7qdd->status & QDD_S_ERR ) ) )
to7qdd->status &= ~QDD_S_IRQ;
if ( ( (to7qdd->ctrl1 & QDD_C1_RIE) && (to7qdd->status & QDD_S_RDA ) ) ||
( (to7qdd->ctrl1 & QDD_C1_TIE) && (to7qdd->status & QDD_S_TDRA) ) ||
( (to7qdd->ctrl2 & QDD_C2_EIE) && (to7qdd->status & QDD_S_ERR ) ) )
to7qdd->status |= QDD_S_IRQ;
}
uint8_t thomson_state::to7_qdd_read_byte()
{
uint8_t data;
/* rebuild disk if needed */
if ( !to7qdd->data_size )
{
to7qdd->data_size = thom_qdd_make_disk( to7_qdd_image(), to7qdd->data );
assert( to7qdd->data_idx < sizeof( to7qdd->data ) );
}
if ( to7qdd->data_idx >= to7qdd->data_size )
data = 0;
else
data = to7qdd->data[ to7qdd->data_idx ];
VLOG(( "%f $%04x to7_qdd_read_byte: RDATA off=%i/%i data=$%02X\n",
machine().time().as_double(), m_maincpu->pc(),
to7qdd->data_idx, to7qdd->data_size, data ));
to7qdd->data_idx++;
to7qdd->start_idx = to7qdd->data_idx;
return data;
}
/* This is quite complex: bytes are written one at a time by the CPU and we
must detect the following patterns:
* CPU write id field and data field => format
* CPU write data field after it has read an id field => sector write
*/
void thomson_state::to7_qdd_write_byte( uint8_t data )
{
int i;
/* rebuild disk if needed */
if ( !to7qdd->data_size )
{
to7qdd->data_size = thom_qdd_make_disk( to7_qdd_image(), to7qdd->data );
assert( to7qdd->data_idx < sizeof( to7qdd->data ) );
}
if ( ( to7qdd->start_idx != to7qdd->data_idx || /* field in construction */
data==0xA5 || data==0x5A ) && /* first byte of tentative field */
to7qdd->data_idx <to7qdd->data_size )
{
/* this is the first byte of the field */
if ( to7qdd->start_idx == to7qdd->data_idx )
to7qdd->data_crc = 0;
/* accumulate bytes */
to7qdd->data[ to7qdd->data_idx ] = data;
to7qdd->data_idx++;
VLOG (( "%f $%04x to7_qdd_write_byte: got $%02X offs=%i-%i\n",
machine().time().as_double(), m_maincpu->pc(), data,
to7qdd->start_idx, to7qdd->data_idx ));
/* end of tentative id field */
if ( to7qdd->data_idx == to7qdd->start_idx + 4 &&
to7qdd->data[ to7qdd->start_idx ] == 0xA5 &&
to7qdd->data[ to7qdd->start_idx + 3 ] == to7qdd->data_crc )
{
/* got an id field => format */
int sector = (int) to7qdd->data[ to7qdd->start_idx + 1 ] * 256 + (int) to7qdd->data[ to7qdd->start_idx + 2 ];
uint8_t filler = 0xff;
LOG(( "%f $%04x to7_qdd_write_byte: got id field for sector=%i\n",
machine().time().as_double(), m_maincpu->pc(), sector ));
to7_qdd_image()->floppy_drive_format_sector(
0, sector, 0, 0, sector, 128, filler );
thom_floppy_active( 1 );
to7qdd->start_idx = to7qdd->data_idx;
}
/* end of tentative data field */
else if ( to7qdd->data_idx == to7qdd->start_idx + 130 &&
to7qdd->data[ to7qdd->start_idx ] == 0x5A &&
to7qdd->data[ to7qdd->start_idx + 129 ] == to7qdd->data_crc )
{
/* look backwards for previous id field */
for ( i = to7qdd->start_idx - 3; i >= 0; i-- )
{
if ( to7qdd->data[ i ] == 0xA5 &&
( ( to7qdd->data[ i ] + to7qdd->data[ i + 1 ] +
to7qdd->data[ i + 2 ] ) & 0xff
) == to7qdd->data[ i + 3 ] )
break;
}
if ( i >= 0 )
{
/* got an id & a data field => write */
int sector = (int) to7qdd->data[ i + 1 ] * 256 + (int) to7qdd->data[ i + 2 ];
LOG(( "%f $%04x to7_qdd_write_byte: goto data field for sector=%i\n",
machine().time().as_double(), m_maincpu->pc(), sector ));
to7_qdd_image()->floppy_drive_write_sector_data( 0, sector, to7qdd->data + to7qdd->start_idx + 1, 128, 0 );
thom_floppy_active( 1 );
}
to7qdd->start_idx = to7qdd->data_idx;
}
else to7qdd->data_crc += data;
}
}
READ8_MEMBER( thomson_state::to7_qdd_r )
{
switch ( offset )
{
case 0: /* MC6852 status */
to7_qdd_stat_update();
VLOG(( "%f $%04x to7_qdd_r: STAT=$%02X irq=%i pe=%i ovr=%i und=%i tr=%i rd=%i ncts=%i\n",
machine().time().as_double(), m_maincpu->pc(), to7qdd->status,
to7qdd->status & QDD_S_IRQ ? 1 : 0,
to7qdd->status & QDD_S_PE ? 1 : 0,
to7qdd->status & QDD_S_OVR ? 1 : 0,
to7qdd->status & QDD_S_TUF ? 1 : 0,
to7qdd->status & QDD_S_TDRA ? 1 : 0,
to7qdd->status & QDD_S_RDA ? 1 : 0,
to7qdd->status & QDD_S_NCTS ? 1 : 0 ));
return to7qdd->status;
case 1: /* MC6852 data input => read byte from disk */
to7qdd->status &= ~(QDD_S_RDA | QDD_S_PE | QDD_S_OVR);
to7_qdd_stat_update();
return to7_qdd_read_byte();
case 8: /* floppy status */
{
uint8_t data = 0;
device_image_interface* img = dynamic_cast<device_image_interface *>(to7_qdd_image());
if ( ! img->exists() )
data |= 0x40; /* disk present */
if ( to7qdd->index_pulse )
data |= 0x80; /* disk start */
VLOG(( "%f $%04x to7_qdd_r: STATUS8 $%02X\n", machine().time().as_double(), m_maincpu->pc(), data ));
return data;
}
default:
logerror ( "%f $%04x to7_qdd_r: invalid read offset %i\n", machine().time().as_double(), m_maincpu->pc(), offset );
return 0;
}
}
WRITE8_MEMBER( thomson_state::to7_qdd_w )
{
switch ( offset )
{
case 0: /* MC6852 control 1 */
/* reset */
if ( data & QDD_C1_RRESET )
to7qdd->status &= ~(QDD_S_PE | QDD_S_RDA | QDD_S_OVR);
if ( data & QDD_C1_TRESET )
to7qdd->status &= ~(QDD_S_TDRA | QDD_S_TUF);
to7qdd->ctrl1 = ( data & ~(QDD_C1_RRESET | QDD_C1_TRESET) ) |( data & (QDD_C1_RRESET | QDD_C1_TRESET) & to7qdd->ctrl1 );
to7_qdd_stat_update();
VLOG(( "%f $%04x to7_qdd_w: CTRL1=$%02X reset=%c%c %s%sirq=%c%c\n",
machine().time().as_double(), m_maincpu->pc(), data,
data & QDD_C1_RRESET ? 'r' : '-', data & QDD_C1_TRESET ? 't' : '-',
data & QDD_C1_STRIPSYNC ? "strip-sync " : "",
data & QDD_C1_CLRSYNC ? "clear-sync " : "",
data & QDD_C1_RIE ? 'r' : '-',
data & QDD_C1_TIE ? 't' : '-' ));
break;
case 1:
switch ( to7qdd->ctrl1 >> 6 )
{
case 0: /* MC6852 control 2 */
{
#if 0
/* most of these are unused now */
static const int bit[8] = { 6, 6, 7, 8, 7, 7, 8, 8 };
static const int par[8] = { 2, 1, 0, 0, 2, 1, 2, 1 };
static const char *const parname[3] = { "none", "odd", "even" };
int bits, parity;
bits = bit[ (data >> 3) & 7 ];
parity = par[ (data >> 3) & 7 ];
to7_qdd_stat_update(machine());
VLOG(( "%f $%04x to7_qdd_w: CTRL2=$%02X bits=%i par=%s blen=%i under=%s%s\n",
machine().time().as_double(), m_maincpu->pc(), data,
bits, parname[ parity ], data & QDD_C2_BLEN ? 1 : 2,
data & QDD_C2_TSYNC ? "sync" : "ff",
data & QDD_C2_EIE ? "irq-err" : "" ));
#endif
to7qdd->ctrl2 = data;
break;
}
case 1: /* MC6852 control 3 */
to7qdd->ctrl3 = data;
/* reset just once each write, not sticky */
if ( data & QDD_C3_CLRTUF )
to7qdd->status &= ~QDD_S_TUF;
if ( data & QDD_C3_CLRCTS )
to7qdd->status &= ~QDD_S_NCTS;
to7_qdd_stat_update();
VLOG(( "%f $%04x to7_qdd_w: CTRL3=$%02X %s%ssync-len=%i sync-mode=%s\n",
machine().time().as_double(), m_maincpu->pc(), data,
data & QDD_C3_CLRTUF ? "clr-tuf " : "",
data & QDD_C3_CLRCTS ? "clr-cts " : "",
data & QDD_C3_SYNCLEN ? 1 : 2,
data & QDD_C3_SYNCMODE ? "ext" : "int" ));
break;
case 2: /* MC6852 sync code => write byte to disk */
to7_qdd_write_byte( data );
break;
case 3: /* MC6852 data out => does not seem to be used */
VLOG(( "%f $%04x to7_qdd_w: ignored WDATA=$%02X\n", machine().time().as_double(), m_maincpu->pc(), data ));
break;
}
break;
case 8: /* set drive */
to7qdd->drive = data;
VLOG(( "%f $%04x to7_qdd_w: DRIVE=$%02X\n", machine().time().as_double(), m_maincpu->pc(), data ));
break;
case 12: /* motor pulse ? */
thom_floppy_active( 0 );
VLOG(( "%f $%04x to7_qdd_w: MOTOR=$%02X\n", machine().time().as_double(), m_maincpu->pc(), data ));
break;
default:
logerror ( "%f $%04x to7_qdd_w: invalid write offset %i (data=$%02X)\n", machine().time().as_double(), m_maincpu->pc(), offset, data );
}
}
void thomson_state::to7_qdd_reset()
{
int i;
LOG(( "to7_qdd_reset: CQ 90-028 controller\n" ));
for ( i = 0; i < floppy_get_count( machine() ); i++ )
{
legacy_floppy_image_device * img = floppy_get_device( machine(), i );
if (img) {
img->floppy_drive_set_ready_state( FLOPPY_DRIVE_READY, 0 );
motor_on = CLEAR_LINE;
img->floppy_mon_w( motor_on);
/* pulse each time the whole-disk spiraling track ends */
/* at 90us per byte read, the disk can be read in 6s */
img->floppy_drive_set_rpm( 60. / 6. );
}
}
to7qdd->ctrl1 |= QDD_C1_TRESET | QDD_C1_RRESET; /* reset */
to7qdd->ctrl2 &= 0x7c; /* clear EIE, PC2-PC1 */
to7qdd->ctrl3 &= 0xfe; /* internal sync */
to7qdd->drive = 0;
to7_qdd_stat_update();
}
void thomson_state::to7_qdd_init()
{
LOG(( "to7_qdd_init: CQ 90-028 controller\n" ));
to7qdd = auto_alloc(machine(), to7qdd_t);
save_item(NAME(to7qdd->status));
save_item(NAME(to7qdd->ctrl1));
save_item(NAME(to7qdd->ctrl2));
save_item(NAME(to7qdd->ctrl3));
save_item(NAME(to7qdd->drive));
save_item(NAME(to7qdd->data_idx));
save_item(NAME(to7qdd->start_idx));
save_item(NAME(to7qdd->data_size));
save_item(NAME(to7qdd->data_crc));
save_item(NAME(to7qdd->index_pulse));
save_item(NAME(to7qdd->data));
}
/********************** THMFC1 controller *************************/
/* custom Thomson Gate-array for 3''1/2, 5''1/4 (double density only) & QDD
*/
/* types of high-level operations */
#define THMFC1_OP_RESET 0
#define THMFC1_OP_WRITE_SECT 1
#define THMFC1_OP_READ_ADDR 2
#define THMFC1_OP_READ_SECT 3
/* STAT0 flags */
#define THMFC1_STAT0_SYNCHRO 0x01 /* bit clock synchronized */
#define THMFC1_STAT0_BYTE_READY_OP 0x02 /* byte ready (high-level operation) */
#define THMFC1_STAT0_CRC_ERROR 0x04
#define THMFC1_STAT0_FINISHED 0x08
#define THMFC1_STAT0_FINISHING 0x10 /* (unemulated) */
#define THMFC1_STAT0_BYTE_READY_POL 0x80 /* polling mode */
/*#define THOM_MAXBUF (THOM_SIZE_ID+THOM_SIZE_DATA_HI+2*THOM_SIZE_SYNCHRO)*17*/
#define THOM_MAXBUF ( THOM_QDD_SIZE_ID + THOM_QDD_SIZE_DATA ) * 512
struct thmfc1_t
{
uint8_t op;
uint8_t sector; /* target sector, in [1,16] */
uint32_t sector_id;
uint8_t track; /* current track, in [0,79] */
uint8_t side; /* current side, 0 or 1 */
uint8_t drive; /* 0 to 3 */
uint16_t sector_size; /* 128 or 256 (512, 1024 not supported) */
uint8_t formatting;
uint8_t ipl; /* index pulse / QDD start */
uint8_t wsync; /* synchronization word */
uint8_t data[THOM_MAXBUF]; /* enough for a whole track */
uint32_t data_idx; /* reading / writing / formatting pos */
uint32_t data_size; /* bytes to read / write */
uint32_t data_finish; /* when to raise the finished flag */
uint32_t data_raw_idx; /* byte index for raw track reading */
uint32_t data_raw_size; /* size of track already cached in data */
uint8_t data_crc; /* check-sum of written data */
uint8_t stat0; /* status register */
};
static thmfc1_t * thmfc1;
static emu_timer* thmfc_floppy_cmd;
legacy_floppy_image_device * thomson_state::thmfc_floppy_image()
{
return floppy_get_device( machine(), thmfc1->drive );
}
int thomson_state::thmfc_floppy_is_qdd ( legacy_floppy_image_device *image )
{
if (image==nullptr) return 0;
if (!image->exists()) return 0;
return image->length()==51200; // idf QDD
}
void thomson_state::thmfc_floppy_index_pulse_cb ( device_t *controller,legacy_floppy_image_device *image, int state )
{
if ( image != thmfc_floppy_image())
return;
if ( thmfc_floppy_is_qdd(image))
{
/* pulse each time the whole-disk spiraling track ends */
image->floppy_drive_set_rpm( 16.92f /* 423/25 */ );
thmfc1->ipl = state;
if ( state )
{
thmfc1->data_raw_size = 0;
thmfc1->data_raw_idx = 0;
thmfc1->data_idx = 0;
}
}
else
{
image->floppy_drive_set_rpm( 300. );
thmfc1->ipl = state;
if ( state )
thmfc1->data_raw_idx = 0;
}
VLOG(( "%f thmfc_floppy_index_pulse_cb: state=%i\n", machine().time().as_double(), state ));
}
int thomson_state::thmfc_floppy_find_sector( chrn_id* dst )
{
legacy_floppy_image_device* img = thmfc_floppy_image();
chrn_id id;
int r = 0;
/* scan track, try 4 revolutions */
while ( r < 4 )
{
if ( img->floppy_drive_get_next_id( thmfc1->side, &id ) )
{
if ( id.C == thmfc1->track &&
id.R == thmfc1->sector &&
(128 << id.N) == thmfc1->sector_size
/* check side ? id.H == thmfc1->side */ )
{
if ( dst )
memcpy( dst, &id, sizeof( chrn_id ) );
thmfc1->stat0 = THMFC1_STAT0_BYTE_READY_POL;
LOG (( "thmfc_floppy_find_sector: sector found C=%i H=%i R=%i N=%i\n", id.C, id.H, id.R, id.N ));
return 1;
}
}
if ( img->floppy_drive_get_flag_state( FLOPPY_DRIVE_INDEX ) )
r++;
}
thmfc1->stat0 = THMFC1_STAT0_CRC_ERROR | THMFC1_STAT0_FINISHED;
LOG (( "thmfc_floppy_find_sector: sector not found drive=%s track=%i sector=%i\n", img->tag(), thmfc1->track, thmfc1->sector ));
return 0;
}
/* complete command (by read, write, or timeout) */
void thomson_state::thmfc_floppy_cmd_complete()
{
LOG (( "%f thmfc_floppy_cmd_complete_cb: cmd=%i off=%i/%i/%i\n",
machine().time().as_double(), thmfc1->op, thmfc1->data_idx,
thmfc1->data_finish - 1, thmfc1->data_size - 1 ));
if ( thmfc1->op == THMFC1_OP_WRITE_SECT )
{
legacy_floppy_image_device * img = thmfc_floppy_image();
img->floppy_drive_write_sector_data( thmfc1->side, thmfc1->sector_id, thmfc1->data + 3, thmfc1->data_size - 3, 0 );
thom_floppy_active( 1 );
}
thmfc1->op = THMFC1_OP_RESET;
thmfc1->stat0 |= THMFC1_STAT0_FINISHED;
thmfc1->data_idx = 0;
thmfc1->data_size = 0;
thmfc_floppy_cmd->adjust(attotime::never);
}
TIMER_CALLBACK_MEMBER( thomson_state::thmfc_floppy_cmd_complete_cb )
{
thmfc_floppy_cmd_complete();
}
/* intelligent read: show just one field, skip header */
uint8_t thomson_state::thmfc_floppy_read_byte()
{
uint8_t data = thmfc1->data[ thmfc1->data_idx ];
VLOG(( "%f $%04x thmfc_floppy_read_byte: off=%i/%i/%i data=$%02X\n",
machine().time().as_double(), m_maincpu->pc(),
thmfc1->data_idx, thmfc1->data_finish - 1, thmfc1->data_size - 1,
data ));
if ( thmfc1->data_idx >= thmfc1->data_size - 1 )
thmfc_floppy_cmd_complete();
else
thmfc1->data_idx++;
if ( thmfc1->data_idx >= thmfc1->data_finish )
thmfc1->stat0 |= THMFC1_STAT0_FINISHED;
return data;
}
/* dumb read: show whole track with field headers and gaps */
uint8_t thomson_state::thmfc_floppy_raw_read_byte()
{
uint8_t data;
/* rebuild track if needed */
if ( ! thmfc1->data_raw_size )
{
if ( thmfc_floppy_is_qdd(thmfc_floppy_image()))
/* QDD: track = whole disk */
thmfc1->data_raw_size = thom_qdd_make_disk ( thmfc_floppy_image(), thmfc1->data );
else
{
thmfc1->data_raw_idx = 0;
thmfc1->data_raw_size = thom_floppy_make_track( thmfc_floppy_image(), thmfc1->data,
thmfc1->sector_size, thmfc1->side );
}
assert( thmfc1->data_raw_size < sizeof( thmfc1->data ) );
}
if ( thmfc1->data_raw_idx >= thmfc1->data_raw_size )
data = 0;
else
data = thmfc1->data[ thmfc1->data_raw_idx ];
VLOG(( "%f $%04x thmfc_floppy_raw_read_byte: off=%i/%i data=$%02X\n",
machine().time().as_double(), m_maincpu->pc(),
thmfc1->data_raw_idx, thmfc1->data_raw_size, data ));
thmfc1->data_raw_idx++;
return data;
}
/* QDD writing / formating */
void thomson_state::thmfc_floppy_qdd_write_byte( uint8_t data )
{
int i;
if ( thmfc1->formatting &&
( thmfc1->data_idx || data==0xA5 || data==0x5A ) &&
thmfc1->data_raw_idx < THOM_MAXBUF )
{
if ( ! thmfc1->data_raw_size )
{
thmfc1->data_raw_size = thom_qdd_make_disk ( thmfc_floppy_image(), thmfc1->data );
assert( thmfc1->data_raw_size < sizeof( thmfc1->data ) );
}
/* accumulate bytes to form a field */
thmfc1->data[ thmfc1->data_raw_idx ] = data;
thmfc1->data_raw_idx++;
if ( ! thmfc1->data_idx )
{
/* start */
thmfc1->data_crc = 0;
thmfc1->data_idx = thmfc1->data_raw_idx;
}
VLOG (( "%f $%04x thmfc_floppy_qdd_write_byte: $%02X offs=%i-%i\n",
machine().time().as_double(), m_maincpu->pc(), data,
thmfc1->data_idx,thmfc1->data_raw_idx ));
if ( thmfc1->data_raw_idx == thmfc1->data_idx + 3 &&
thmfc1->data[ thmfc1->data_idx - 1 ] == 0xA5 &&
thmfc1->data[ thmfc1->data_idx + 2 ] == thmfc1->data_crc )
{
/* got an id field => format */
int sector = (int) thmfc1->data[ thmfc1->data_idx ] * 256 + (int) thmfc1->data[ thmfc1->data_idx + 1 ];
uint8_t filler = 0xff;
LOG(( "%f $%04x thmfc_floppy_qdd_write_byte: id field, sector=%i\n", machine().time().as_double(), m_maincpu->pc(), sector ));
thmfc_floppy_image()->floppy_drive_format_sector( 0, sector, 0, 0, sector, 128, filler );
thom_floppy_active( 1 );
thmfc1->data_idx = 0;
}
else if ( thmfc1->data_raw_idx == thmfc1->data_idx + 129 &&
thmfc1->data[ thmfc1->data_idx - 1 ] == 0x5A &&
thmfc1->data[ thmfc1->data_idx + 128 ] == thmfc1->data_crc )
{
/* look backwards for previous id field */
for ( i = thmfc1->data_idx - 4; i >= 0; i-- )
{
if ( thmfc1->data[ i ] == 0xA5 &&
( ( thmfc1->data[ i ] + thmfc1->data[ i + 1 ] +
thmfc1->data[ i + 2 ] ) & 0xff
) == thmfc1->data[ i + 3 ] )
break;
}
if ( i >= 0 )
{
/* got an id & a data field => write */
legacy_floppy_image_device * img = thmfc_floppy_image();
int sector = (int) thmfc1->data[ i + 1 ] * 256 +
(int) thmfc1->data[ i + 2 ];
LOG(( "%f $%04x thmfc_floppy_qdd_write_byte: data field, sector=%i\n",
machine().time().as_double(), m_maincpu->pc(), sector ));
img->floppy_drive_write_sector_data( 0, sector, thmfc1->data + thmfc1->data_idx, 128, 0 );
thom_floppy_active( 1 );
}
thmfc1->data_idx = 0;
}
else
thmfc1->data_crc += data;
}
else
{
thmfc1->data_raw_idx++;
VLOG (( "%f $%04x thmfc_floppy_qdd_write_byte: ignored $%02X\n", machine().time().as_double(), m_maincpu->pc(), data ));
}
}
/* intelligent writing */
void thomson_state::thmfc_floppy_write_byte( uint8_t data )
{
VLOG (( "%f $%04x thmfc_floppy_write_byte: off=%i/%i data=$%02X\n",
machine().time().as_double(), m_maincpu->pc(),
thmfc1->data_idx, thmfc1->data_size - 1, data ));
thmfc1->data_raw_size = 0;
thmfc1->data[ thmfc1->data_idx ] = data;
if ( thmfc1->data_idx >= thmfc1->data_size - 1 )
thmfc_floppy_cmd_complete();
else
thmfc1->data_idx++;
}
/* intelligent formatting */
void thomson_state::thmfc_floppy_format_byte( uint8_t data )
{
VLOG (( "%f $%04x thmfc_floppy_format_byte: $%02X\n", machine().time().as_double(), m_maincpu->pc(), data ));
thmfc1->data_raw_size = 0;
/* accumulate bytes to form an id field */
if ( thmfc1->data_idx || data==0xA1 )
{
static const uint8_t header[] = { 0xa1, 0xa1, 0xa1, 0xfe };
thmfc1->data[ thmfc1->data_idx ] = data;
thmfc1->data_idx++;
if ( thmfc1->data_idx > 11 )
{
if ( !memcmp ( thmfc1->data, header, sizeof( header ) ) )
{
/* got id field => format */
legacy_floppy_image_device * img = thmfc_floppy_image();
uint8_t track = thmfc1->data[4];
uint8_t side = thmfc1->data[5];
uint8_t sector = thmfc1->data[6];
uint8_t length = thmfc1->data[7]; /* actually, log length */
uint8_t filler = 0xe5; /* standard Thomson filler */
chrn_id id;
if ( thmfc_floppy_find_sector( &id ) )
{
img->floppy_drive_format_sector( side, thmfc1->sector_id, track, thmfc1->side, sector, length, filler );
thom_floppy_active( 1 );
}
}
thmfc1->data_idx = 0;
}
}
}
READ8_MEMBER( thomson_state::thmfc_floppy_r )
{
switch ( offset )
{
case 0: /* STAT0 */
thmfc1->stat0 ^= THMFC1_STAT0_SYNCHRO | THMFC1_STAT0_BYTE_READY_POL;
VLOG(( "%f $%04x thmfc_floppy_r: STAT0=$%02X\n", machine().time().as_double(), m_maincpu->pc(), thmfc1->stat0 ));
return thmfc1->stat0;
case 1: /* STAT1 */
{
uint8_t data = 0;
legacy_floppy_image_device * img = thmfc_floppy_image();
int flags = img->floppy_drive_get_flag_state(-1 );
if ( thmfc_floppy_is_qdd(img) )
{
if ( ! img->exists() )
data |= 0x40; /* disk present */
if ( ! thmfc1->ipl )
data |= 0x02; /* disk start */
data |= 0x08; /* connected */
}
else
{
if ( thmfc1->ipl )
data |= 0x40;
if ( img->exists() )
data |= 0x20; /* disk change (?) */
data |= !img->floppy_tk00_r() << 3;
if ( flags & FLOPPY_DRIVE_READY )
data |= 0x02;
}
if (!motor_on)
data |= 0x10;
if (!img->floppy_wpt_r())
data |= 0x04;
VLOG(( "%f $%04x thmfc_floppy_r: STAT1=$%02X\n", machine().time().as_double(), m_maincpu->pc(), data ));
return data;
}
case 3: /* RDATA */
if ( thmfc1->op == THMFC1_OP_READ_SECT || thmfc1->op == THMFC1_OP_READ_ADDR )
return thmfc_floppy_read_byte();
else
return thmfc_floppy_raw_read_byte();
case 6:
return 0;
case 8:
{
/* undocumented => emulate TO7 QDD controller ? */
uint8_t data = thmfc1->ipl << 7;
VLOG(( "%f $%04x thmfc_floppy_r: STAT8=$%02X\n", machine().time().as_double(), m_maincpu->pc(), data ));
return data;
}
default:
logerror ( "%f $%04x thmfc_floppy_r: invalid read offset %i\n", machine().time().as_double(), m_maincpu->pc(), offset );
return 0;
}
}
WRITE8_MEMBER( thomson_state::thmfc_floppy_w )
{
switch ( offset ) {
case 0: /* CMD0 */
{
int wsync = (data >> 4) & 1;
int qdd = thmfc_floppy_is_qdd(thmfc_floppy_image());
chrn_id id;
thmfc1->formatting = (data >> 2) & 1;
LOG (( "%f $%04x thmfc_floppy_w: CMD0=$%02X dens=%s wsync=%i dsync=%i fmt=%i op=%i\n",
machine().time().as_double(), m_maincpu->pc(), data,
(BIT(data, 5) ? "FM" : "MFM"),
wsync, (data >> 3) & 1,
thmfc1->formatting, data & 3 ));
/* abort previous command, if any */
thmfc1->op = THMFC1_OP_RESET;
thmfc_floppy_cmd->adjust(attotime::never);
switch ( data & 3 )
{
case THMFC1_OP_RESET:
thmfc1->stat0 = THMFC1_STAT0_FINISHED;
break;
case THMFC1_OP_WRITE_SECT:
if ( qdd )
logerror( "thmfc_floppy_w: smart operation 1 not supported for QDD\n" );
else if ( thmfc_floppy_find_sector( &id ) )
{
thmfc1->sector_id = id.data_id;
thmfc1->data_idx = 0;
thmfc1->data_size = thmfc1->sector_size + 3; /* A1 A1 FB <data> */
thmfc1->data_finish = thmfc1->sector_size + 3;
thmfc1->stat0 |= THMFC1_STAT0_BYTE_READY_OP;
thmfc1->op = THMFC1_OP_WRITE_SECT;
thmfc_floppy_cmd->adjust(attotime::from_msec( 10 ));
}
break;
case THMFC1_OP_READ_ADDR:
if ( qdd )
logerror( "thmfc_floppy_w: smart operation 2 not supported for QDD\n" );
else if ( thmfc_floppy_find_sector( &id ) )
{
thmfc1->data_size =
thom_floppy_make_addr( id, thmfc1->data, thmfc1->sector_size );
assert( thmfc1->data_size < sizeof( thmfc1->data ) );
thmfc1->data_finish = 10;
thmfc1->data_idx = 1;
thmfc1->stat0 |= THMFC1_STAT0_BYTE_READY_OP;
thmfc1->op = THMFC1_OP_READ_ADDR;
thmfc_floppy_cmd->adjust(attotime::from_msec( 1 ));
}
break;
case THMFC1_OP_READ_SECT:
if ( qdd )
logerror( "thmfc_floppy_w: smart operation 3 not supported for QDD\n" );
else if ( thmfc_floppy_find_sector( &id ) )
{
thmfc1->data_size = thom_floppy_make_sector
( thmfc_floppy_image(), id, thmfc1->data, thmfc1->sector_size );
assert( thmfc1->data_size < sizeof( thmfc1->data ) );
thmfc1->data_finish = thmfc1->sector_size + 4;
thmfc1->data_idx = 1;
thmfc1->stat0 |= THMFC1_STAT0_BYTE_READY_OP;
thmfc1->op = THMFC1_OP_READ_SECT;
thmfc_floppy_cmd->adjust(attotime::from_msec( 10 ));
}
break;
}
/* synchronize to word, if needed (QDD only) */
if ( wsync && qdd ) {
if ( ! thmfc1->data_raw_size )
thmfc1->data_raw_size = thom_qdd_make_disk ( thmfc_floppy_image(), thmfc1->data );
while ( thmfc1->data_raw_idx < thmfc1->data_raw_size &&
thmfc1->data[ thmfc1->data_raw_idx ] != thmfc1->wsync )
{
thmfc1->data_raw_idx++;
}
}
}
break;
case 1: /* CMD1 */
thmfc1->data_raw_size = 0;
thmfc1->sector_size = 128 << ( (data >> 5) & 3);
thmfc1->side = (data >> 4) & 1;
if ( thmfc1->sector_size > 256 )
{
logerror( "$%04x thmfc_floppy_w: sector size %i > 256 not handled\n",
m_maincpu->pc(), thmfc1->sector_size );
thmfc1->sector_size = 256;
}
LOG (( "%f $%04x thmfc_floppy_w: CMD1=$%02X sect-size=%i comp=%i head=%i\n",
machine().time().as_double(), m_maincpu->pc(), data,
thmfc1->sector_size, (data >> 1) & 7, thmfc1->side ));
break;
case 2: /* CMD2 */
{
legacy_floppy_image_device * img;
int seek = 0, motor;
thmfc1->drive = data & 2;
img = thmfc_floppy_image();
if ( thmfc_floppy_is_qdd(img))
{
motor = !(data & 0x40);
/* no side select & no seek for QDD */
}
else
{
if ( data & 0x10 )
seek = (data & 0x20) ? 1 : -1;
motor = (data >> 2) & 1;
thmfc1->drive |= 1 ^ ((data >> 6) & 1);
img = thmfc_floppy_image();
}
thom_floppy_active( 0 );
LOG (( "%f $%04x thmfc_floppy_w: CMD2=$%02X drv=%i step=%i motor=%i\n",
machine().time().as_double(), m_maincpu->pc(), data,
thmfc1->drive, seek, motor ));
if ( seek )
{
thmfc1->data_raw_size = 0;
img->floppy_drive_seek( seek );
}
/* in real life, to keep the motor running, it is sufficient to
set motor to 1 every few seconds.
instead of counting, we assume the motor is always running...
*/
motor_on = CLEAR_LINE /* motor */;
img->floppy_mon_w(motor_on);
}
break;
case 3: /* WDATA */
thmfc1->wsync = data;
if ( thmfc_floppy_is_qdd(thmfc_floppy_image()))
thmfc_floppy_qdd_write_byte( data );
else if ( thmfc1->op==THMFC1_OP_WRITE_SECT )
thmfc_floppy_write_byte( data );
else if ( thmfc1->formatting )
thmfc_floppy_format_byte( data );
else
{
/* TODO: implement other forms of raw track writing */
LOG (( "%f $%04x thmfc_floppy_w: ignored raw WDATA $%02X\n",
machine().time().as_double(), m_maincpu->pc(), data ));
}
break;
case 4: /* WCLK (unemulated) */
/* clock configuration: FF for data, 0A for synchro */
LOG (( "%f $%04x thmfc_floppy_w: WCLK=$%02X (%s)\n",
machine().time().as_double(), m_maincpu->pc(), data,
(data == 0xff) ? "data" : (data == 0x0A) ? "synchro" : "?" ));
break;
case 5: /* WSECT */
thmfc1->sector = data;
LOG (( "%f $%04x thmfc_floppy_w: WSECT=%i\n",
machine().time().as_double(), m_maincpu->pc(), data ));
break;
case 6: /* WTRCK */
thmfc1->track = data;
LOG (( "%f $%04x thmfc_floppy_w: WTRCK=%i (real=%i)\n",
machine().time().as_double(), m_maincpu->pc(), data,
thmfc_floppy_image()->floppy_drive_get_current_track()));
break;
case 7: /* WCELL */
/* precompensation (unemulated) */
LOG (( "%f $%04x thmfc_floppy_w: WCELL=$%02X\n",
machine().time().as_double(), m_maincpu->pc(), data ));
break;
default:
logerror ( "%f $%04x thmfc_floppy_w: invalid write offset %i (data=$%02X)\n",
machine().time().as_double(), m_maincpu->pc(), offset, data );
}
}
void thomson_state::thmfc_floppy_reset()
{
int i;
LOG(( "thmfc_floppy_reset: THMFC1 controller\n" ));
for ( i = 0; i < floppy_get_count( machine() ); i++ )
{
legacy_floppy_image_device * img = floppy_get_device( machine(), i );
if (img) {
img->floppy_drive_set_ready_state( FLOPPY_DRIVE_READY, 0 );
img->floppy_drive_seek( - img->floppy_drive_get_current_track() );
}
}
thmfc1->op = THMFC1_OP_RESET;
thmfc1->track = 0;
thmfc1->sector = 0;
thmfc1->side = 0;
thmfc1->drive = 0;
thmfc1->sector_size = 256;
thmfc1->formatting = 0;
thmfc1->stat0 = 0;
thmfc1->data_idx = 0;
thmfc1->data_size = 0;
thmfc1->data_raw_idx = 0;
thmfc1->data_raw_size = 0;
thmfc1->data_crc = 0;
thmfc1->wsync = 0;
thmfc_floppy_cmd->adjust(attotime::never);
}
void thomson_state::thmfc_floppy_init()
{
LOG(( "thmfc_floppy_init: THMFC1 controller\n" ));
thmfc1 = auto_alloc(machine(), thmfc1_t);
memset(thmfc1, 0, sizeof(thmfc1_t));
thmfc_floppy_cmd = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(thomson_state::thmfc_floppy_cmd_complete_cb),this));
save_item(NAME(thmfc1->op));
save_item(NAME(thmfc1->sector));
save_item(NAME(thmfc1->sector_id));
save_item(NAME(thmfc1->track));
save_item(NAME(thmfc1->side));
save_item(NAME(thmfc1->drive));
save_item(NAME(thmfc1->sector_size));
save_item(NAME(thmfc1->formatting));
save_item(NAME(thmfc1->ipl));
save_item(NAME(thmfc1->data_idx));
save_item(NAME(thmfc1->data_size));
save_item(NAME(thmfc1->data_finish));
save_item(NAME(thmfc1->stat0));
save_item(NAME(thmfc1->data_raw_idx));
save_item(NAME(thmfc1->data_raw_size));
save_item(NAME(thmfc1->data_crc));
save_item(NAME(thmfc1->wsync));
save_item(NAME(thmfc1->data));
}
/*********************** Network ************************/
/* The network extension is built as an external floppy controller.
It uses the same ROM and I/O space, and so, it is natural to have the
toplevel network emulation here!
*/
/* NOTE: This is work in progress!
For the moment, only hand-checks works: the TO7 can take the line, then
perform a DKBOOT request. We do not have the server emulated yet, so,
no way to answer the request.
*/
TIMER_CALLBACK_MEMBER( thomson_state::ans4 )
{
LOG(( "%f ans4\n", machine().time().as_double() ));
m_mc6854->set_cts( 0 );
}
TIMER_CALLBACK_MEMBER( thomson_state::ans3 )
{
LOG(( "%f ans3\n", machine().time().as_double() ));
m_mc6854->set_cts( 1 );
machine().scheduler().timer_set( attotime::from_usec( 100 ), timer_expired_delegate(FUNC(thomson_state::ans4),this));
}
TIMER_CALLBACK_MEMBER( thomson_state::ans2 )
{
LOG(( "%f ans2\n", machine().time().as_double() ));
m_mc6854->set_cts( 0 );
machine().scheduler().timer_set( attotime::from_usec( 100 ), timer_expired_delegate(FUNC(thomson_state::ans3),this));
}
TIMER_CALLBACK_MEMBER( thomson_state::ans )
{
LOG(( "%f ans\n", machine().time().as_double() ));
m_mc6854->set_cts( 1 );
machine().scheduler().timer_set( attotime::from_usec( 100 ), timer_expired_delegate(FUNC(thomson_state::ans2),this));
}
/* consigne DKBOOT
MO5 BASIC
$00 $00 $01 $00 $00 $00 $00 $00 $00 $00 $01 $00<$41 $00 $FF $20
$3D $4C $01 $60 $20 $3C $4F $01 $05 $20 $3F $9C $19 $25 $03 $11
$93 $15 $10 $25 $32 $8A $7E $FF $E1 $FD $E9 $41>$00 $00 $00 $00
$00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00
TO7/70 BASIC
$00 $00 $01 $00 $00 $00 $00 $00 $00 $00 $02 $00<$20 $42 $41 $53
$49 $43 $20 $4D $49 $43 $52 $4F $53 $4F $46 $54 $20 $31 $2E $30
$04 $00 $00 $00 $00 $00 $60 $FF $37 $9B $37 $9C>$00 $00 $00 $00
$00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00
TO7 BASIC
$00 $00 $01 $00 $00 $00 $00 $00 $00 $00 $00 $00<$20 $42 $41 $53
$49 $43 $20 $4D $49 $43 $52 $4F $53 $4F $46 $54 $20 $31 $2E $30
$04 $00 $00 $00 $00 $00 $60 $FF $37 $9B $37 $9C>$00 $00 $00 $00
$00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00
TO7 LOGO
$00 $00 $01 $00 $00 $00 $00 $00 $00 $00 $00 $00<$00 $00 $00 $00
$00 $20 $4C $4F $47 $4F $04 $00 $00 $00 $00 $00 $00 $00 $00 $00
$00 $00 $00 $00 $00 $00 $AA $FF $01 $16 $00 $C8>$00 $00 $00 $00
$00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00
*/
MC6854_OUT_FRAME_CB(thomson_state::to7_network_got_frame)
{
LOG(( "%f to7_network_got_frame:", machine().time().as_double() ));
for ( int i = 0; i < length; i++ )
LOG(( " $%02X", data[i] ));
LOG(( "\n" ));
if ( data[1] == 0xff )
{
LOG(( "to7_network_got_frame: %i phones %i\n", data[2], data[0] ));
machine().scheduler().timer_set( attotime::from_usec( 100 ), timer_expired_delegate(FUNC(thomson_state::ans), this));
m_mc6854->set_cts( 0 );
}
else if ( ! data[1] )
{
char name[33];
memcpy( name, data + 12, 32 );
name[32] = 0;
for (int i=0;i<32;i++)
{
if ( name[i]<32 || name[i]>=127 )
name[i]=' ';
}
LOG(( "to7_network_got_frame: DKBOOT system=%s appli=\"%s\"\n",
(data[10] == 0) ? "TO7" : (data[10] == 1) ? "MO5" :
(data[10] == 2) ? "TO7/70" : "?", name ));
}
}
void thomson_state::to7_network_init()
{
LOG(( "to7_network_init: NR 07-005 network extension\n" ));
logerror( "to7_network_init: network not handled!\n" );
}
void thomson_state::to7_network_reset()
{
LOG(( "to7_network_reset: NR 07-005 network extension\n" ));
m_mc6854->set_cts( 0 );
m_mc6854->set_cts( 1 );
}
READ8_MEMBER( thomson_state::to7_network_r )
{
if ( offset < 4 )
return m_mc6854->read( space, offset );
if ( offset == 8 )
{
/* network ID of the computer */
uint8_t id = m_io_fconfig->read() >> 3;
VLOG(( "%f $%04x to7_network_r: read id $%02X\n", machine().time().as_double(), m_maincpu->pc(), id ));
return id;
}
logerror( "%f $%04x to7_network_r: invalid read offset %i\n", machine().time().as_double(), m_maincpu->pc(), offset );
return 0;
}
WRITE8_MEMBER( thomson_state::to7_network_w )
{
if ( offset < 4 )
m_mc6854->write( space, offset, data );
else
{
logerror( "%f $%04x to7_network_w: invalid write offset %i (data=$%02X)\n",
machine().time().as_double(), m_maincpu->pc(), offset, data );
}
}
/*********************** TO7 dispatch ************************/
/* The TO7, TO7/70, MO5 and MO6 can use a variety of floppy controllers.
We use a PORT_CONFSETTING to chose the current controller, and dispatch
here.
NOTE: you need to reset the computer after changing the floppy controller!
The CD 90-351 controller seems similar to the THMFC1 gate-array
=> we reuse the THMFC code!
*/
uint8_t to7_controller_type;
uint8_t to7_floppy_bank;
void thomson_state::to7_floppy_init( void* base )
{
m_flopbank->configure_entries( 0, TO7_NB_FLOP_BANK, base, 0x800 );
save_item(NAME(to7_controller_type));
save_item(NAME(to7_floppy_bank));
to7_5p14sd_init();
to7_5p14_init();
to7_qdd_init();
thmfc_floppy_init();
to7_network_init();
}
void thomson_state::to7_floppy_reset()
{
to7_controller_type = (m_io_fconfig->read() ) & 7;
switch ( to7_controller_type )
{
case 1:
to7_floppy_bank = 1;
to7_5p14sd_reset();
break;
case 2:
to7_floppy_bank = 2;
to7_5p14_reset();
break;
case 3:
to7_floppy_bank = 3;
thmfc_floppy_reset();
break;
case 4:
to7_floppy_bank = 7;
to7_qdd_reset();
break;
case 5:
to7_floppy_bank = 8;
to7_network_reset();
break;
default:
to7_floppy_bank = 0;
break;
}
m_flopbank->set_entry( to7_floppy_bank );
}
READ8_MEMBER( thomson_state::to7_floppy_r )
{
switch ( to7_controller_type )
{
case 1:
return to7_5p14sd_r( space, offset, mem_mask );
case 2:
return to7_5p14_r( space, offset, mem_mask );
case 3:
return thmfc_floppy_r( space, offset );
case 4:
return to7_qdd_r( space, offset, mem_mask );
case 5:
return to7_network_r( space, offset, mem_mask );
}
return 0;
}
WRITE8_MEMBER( thomson_state::to7_floppy_w )
{
switch ( to7_controller_type )
{
case 1:
to7_5p14sd_w( space, offset, data, mem_mask );
return;
case 2:
to7_5p14_w( space, offset, data, mem_mask );
break;
case 3:
if ( offset == 8 )
{
to7_floppy_bank = 3 + (data & 3);
m_flopbank->set_entry( to7_floppy_bank );
VLOG (( "to7_floppy_w: set CD 90-351 ROM bank to %i\n", data & 3 ));
}
else
thmfc_floppy_w( space, offset, data );
break;
case 4:
to7_qdd_w( space, offset, data, mem_mask );
break;
case 5:
to7_network_w( space, offset, data, mem_mask );
break;
}
}
/*********************** TO9 ************************/
/* the internal controller is WD2793-based (so, similar to to7_5p14)
we also emulate external controllers
*/
void thomson_state::to9_floppy_init( void* int_base, void* ext_base )
{
to7_floppy_init( ext_base );
m_flopbank->configure_entry( TO7_NB_FLOP_BANK, int_base);
}
void thomson_state::to9_floppy_reset()
{
to7_floppy_reset();
if ( THOM_FLOPPY_EXT )
{
LOG(( "to9_floppy_reset: external controller\n" ));
}
else
{
LOG(( "to9_floppy_reset: internal controller\n" ));
to7_5p14_reset();
m_flopbank->set_entry( TO7_NB_FLOP_BANK );
}
}
READ8_MEMBER( thomson_state::to9_floppy_r )
{
if ( THOM_FLOPPY_EXT )
return to7_floppy_r( space, offset );
else
return to7_5p14_r( space, offset, mem_mask );
}
WRITE8_MEMBER( thomson_state::to9_floppy_w )
{
if ( THOM_FLOPPY_EXT )
to7_floppy_w( space, offset, data );
else
to7_5p14_w( space, offset, data, mem_mask );
}
void thomson_state::thomson_index_callback(legacy_floppy_image_device *device, int state)
{
switch ( to7_controller_type )
{
case 1:
to7_5p14_index_pulse_callback(nullptr, device, state);
break;
case 2:
break;
case 3:
thmfc_floppy_index_pulse_cb(nullptr, device, state);
break;
case 4:
to7_qdd_index_pulse_cb(nullptr, device, state);
break;
default:
break;
}
}