apple2: Add support for the BOOTI card [R. Belmont]

This commit is contained in:
arbee 2021-05-08 16:19:36 -04:00
parent 6704819c1f
commit fa5977301e
11 changed files with 1204 additions and 0 deletions

View File

@ -2416,6 +2416,8 @@ if (BUSES["A2BUS"]~=null) then
MAME_DIR .. "src/devices/bus/a2bus/agat7ram.h",
MAME_DIR .. "src/devices/bus/a2bus/agat840k_hle.cpp",
MAME_DIR .. "src/devices/bus/a2bus/agat840k_hle.h",
MAME_DIR .. "src/devices/bus/a2bus/booti.cpp",
MAME_DIR .. "src/devices/bus/a2bus/booti.h",
MAME_DIR .. "src/devices/bus/a2bus/byte8251.cpp",
MAME_DIR .. "src/devices/bus/a2bus/byte8251.h",
MAME_DIR .. "src/devices/bus/a2bus/ccs7710.cpp",

View File

@ -836,6 +836,18 @@ if (MACHINES["AT28C16"]~=null) then
}
end
---------------------------------------------------
--
--@src/devices/machine/at28c64b.h,MACHINES["AT28C64B"] = true
---------------------------------------------------
if (MACHINES["AT28C64B"]~=null) then
files {
MAME_DIR .. "src/devices/machine/at28c64b.cpp",
MAME_DIR .. "src/devices/machine/at28c64b.h",
}
end
---------------------------------------------------
--
--@src/devices/machine/at29x.h,MACHINES["AT29X"] = true
@ -981,6 +993,17 @@ if (MACHINES["CDP1879"]~=null) then
}
end
---------------------------------------------------
--
--@src/devices/machine/ch376.h,MACHINES["CH376"] = true
---------------------------------------------------
if (MACHINES["CH376"]~=null) then
files {
MAME_DIR .. "src/devices/machine/ch376.cpp",
MAME_DIR .. "src/devices/machine/ch376.h",
}
end
---------------------------------------------------
--

View File

@ -478,6 +478,7 @@ MACHINES["AMIGAFDC"] = true
MACHINES["ARM_IOMD"] = true
MACHINES["AT_KEYBC"] = true
MACHINES["AT28C16"] = true
MACHINES["AT28C64B"] = true
MACHINES["AT29X"] = true
MACHINES["AT45DBXX"] = true
MACHINES["ATAFLASH"] = true
@ -490,6 +491,7 @@ MACHINES["CDP1852"] = true
MACHINES["CDP1871"] = true
MACHINES["CDP1879"] = true
--MACHINES["CDU76S"] = true
MACHINES["CH376"] = true
MACHINES["CHESSMACHINE"] = true
MACHINES["CMOS40105"] = true
MACHINES["COM52C50"] = true

View File

@ -0,0 +1,171 @@
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/*********************************************************************
booti.cpp
Implementation of the BOOTI card
The BOOTI is an Apple II interface to the CH376 USB module.
The CH376 is intended for use with small microcontrollers (or,
you know, the 6502) to give them access to FAT-formatted
flash drives. See ch376.cpp for details.
C0n0: read/write data to CH376
C0n1: read status/write command to CH376
C0n4: $C800 ROM bank (0-3)
*********************************************************************/
#include "emu.h"
#include "booti.h"
/***************************************************************************
PARAMETERS
***************************************************************************/
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
DEFINE_DEVICE_TYPE(A2BUS_BOOTI, a2bus_booti_device, "a2booti", "Booti Card")
ROM_START( booti )
ROM_REGION(0x2000, "flash", 0)
ROM_LOAD( "bootifw09l.bin", 0x000000, 0x002000, CRC(be3f21ff) SHA1(f505ad4685cd44e4cce5b8d6d27b9c4fea159f11) )
ROM_END
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
//-------------------------------------------------
// device_add_mconfig - add device configuration
//-------------------------------------------------
void a2bus_booti_device::device_add_mconfig(machine_config &config)
{
AT28C64B(config, "flash", 0);
CH376(config, "ch376");
}
//-------------------------------------------------
// rom_region - device-specific ROM region
//-------------------------------------------------
const tiny_rom_entry *a2bus_booti_device::device_rom_region() const
{
return ROM_NAME( booti );
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
a2bus_booti_device::a2bus_booti_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, type, tag, owner, clock),
device_a2bus_card_interface(mconfig, *this),
m_flash(*this, "flash"),
m_ch376(*this, "ch376"),
m_rombank(0)
{
}
a2bus_booti_device::a2bus_booti_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
a2bus_booti_device(mconfig, A2BUS_BOOTI, tag, owner, clock)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void a2bus_booti_device::device_start()
{
save_item(NAME(m_rombank));
}
void a2bus_booti_device::device_reset()
{
m_rombank = 0;
}
/*-------------------------------------------------
read_c0nx - called for reads from this card's c0nx space
-------------------------------------------------*/
uint8_t a2bus_booti_device::read_c0nx(uint8_t offset)
{
switch (offset)
{
case 0:
case 1:
return m_ch376->read(offset);
case 4:
return m_rombank / 0x800;
}
return 0xff;
}
/*-------------------------------------------------
write_c0nx - called for writes to this card's c0nx space
-------------------------------------------------*/
void a2bus_booti_device::write_c0nx(uint8_t offset, uint8_t data)
{
switch (offset)
{
case 0:
case 1:
m_ch376->write(offset, data);
break;
case 4:
m_rombank = 0x800 * (data & 3);
break;
default:
printf("Write %02x to c0n%x (%s)\n", data, offset, machine().describe_context().c_str());
break;
}
}
/*-------------------------------------------------
read_cnxx - called for reads from this card's cnxx space
-------------------------------------------------*/
uint8_t a2bus_booti_device::read_cnxx(uint8_t offset)
{
// slot image at 0
return m_flash->read(offset);
}
void a2bus_booti_device::write_cnxx(uint8_t offset, uint8_t data)
{
}
/*-------------------------------------------------
read_c800 - called for reads from this card's c800 space
-------------------------------------------------*/
uint8_t a2bus_booti_device::read_c800(uint16_t offset)
{
return m_flash->read(offset + m_rombank);
}
/*-------------------------------------------------
write_c800 - called for writes to this card's c800 space
-------------------------------------------------*/
void a2bus_booti_device::write_c800(uint16_t offset, uint8_t data)
{
// the card appears not to pass writes to $CFFF to the EEPROM,
// otherwise there's a false read of the next opcode after a $CFFF write and we crash.
if (offset < 0x7ff)
{
m_flash->write(offset + m_rombank, data);
}
}

View File

@ -0,0 +1,58 @@
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/*********************************************************************
booti.h
Implementation of the Booti card
*********************************************************************/
#ifndef MAME_BUS_A2BUS_BOOTI_H
#define MAME_BUS_A2BUS_BOOTI_H
#pragma once
#include "a2bus.h"
#include "machine/at28c64b.h"
#include "machine/ch376.h"
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
class a2bus_booti_device:
public device_t,
public device_a2bus_card_interface
{
public:
// construction/destruction
a2bus_booti_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
protected:
a2bus_booti_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_add_mconfig(machine_config &config) override;
virtual const tiny_rom_entry *device_rom_region() const override;
// overrides of standard a2bus slot functions
virtual uint8_t read_c0nx(uint8_t offset) override;
virtual void write_c0nx(uint8_t offset, uint8_t data) override;
virtual uint8_t read_cnxx(uint8_t offset) override;
virtual void write_cnxx(uint8_t offset, uint8_t data) override;
virtual uint8_t read_c800(uint16_t offset) override;
virtual void write_c800(uint16_t offset, uint8_t data) override;
private:
required_device<at28c64b_device> m_flash;
required_device<ch376_device> m_ch376;
int m_rombank;
};
// device type definition
DECLARE_DEVICE_TYPE(A2BUS_BOOTI, a2bus_booti_device)
#endif // MAME_BUS_A2BUS_BOOTI_H

View File

@ -0,0 +1,279 @@
// license:BSD-3-Clause
// copyright-holders:smf, R. Belmont
/***************************************************************************
ATMEL AT28C64B
64K ( 8K x 8 ) Parallel EEPROM with flash-like in-band signalling
***************************************************************************/
#include "emu.h"
#include "machine/at28c64b.h"
static constexpr int AT28C64B_DATA_BYTES = 0x10000;
static constexpr int AT28C64B_ID_BYTES = 0x40;
static constexpr int AT28C64B_TOTAL_BYTES = AT28C64B_DATA_BYTES + AT28C64B_ID_BYTES;
static constexpr int AT28C64B_ID_OFFSET = 0x1fc0;
static constexpr int AT28C64B_SECTOR_SIZE = 0x40;
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
void at28c64b_device::at28c64b_map8(address_map &map)
{
map(0x00000, 0x1003f).ram();
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
// device type definition
DEFINE_DEVICE_TYPE(AT28C64B, at28c64b_device, "at28c64b", "AT28C64B 8Kx8 EEPROM")
//-------------------------------------------------
// at28c64b_device - constructor
//-------------------------------------------------
at28c64b_device::at28c64b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, AT28C64B, tag, owner, clock),
device_memory_interface(mconfig, *this),
device_nvram_interface(mconfig, *this),
m_space_config("at28c64b", ENDIANNESS_BIG, 8, 17, 0, address_map_constructor(FUNC(at28c64b_device::at28c64b_map8), this)),
m_a9_12v(0),
m_oe_12v(0),
m_last_write(-1),
m_state(0),
m_bytes_in_sector(0),
m_default_data(*this, DEVICE_SELF)
{
}
//-------------------------------------------------
// memory_space_config - return a description of
// any address spaces owned by this device
//-------------------------------------------------
device_memory_interface::space_config_vector at28c64b_device::memory_space_config() const
{
return space_config_vector {
std::make_pair(0, &m_space_config)
};
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void at28c64b_device::device_start()
{
m_write_timer = timer_alloc(0);
save_item( NAME(m_a9_12v) );
save_item( NAME(m_oe_12v) );
save_item( NAME(m_last_write) );
}
//-------------------------------------------------
// nvram_default - called to initialize NVRAM to
// its default state
//-------------------------------------------------
void at28c64b_device::nvram_default()
{
uint16_t default_value = 0xff;
for( offs_t offs = 0; offs < AT28C64B_TOTAL_BYTES; offs++ )
{
space(AS_PROGRAM).write_byte( offs, default_value );
}
/* populate from a memory region if present */
printf("checking for default\n");
if (m_default_data.found())
{
printf("Got default data\n");
for( offs_t offs = 0; offs < AT28C64B_DATA_BYTES; offs++ )
space(AS_PROGRAM).write_byte(offs, m_default_data[offs]);
}
}
//-------------------------------------------------
// nvram_read - called to read NVRAM from the
// .nv file
//-------------------------------------------------
void at28c64b_device::nvram_read( emu_file &file )
{
std::vector<uint8_t> buffer( AT28C64B_TOTAL_BYTES );
file.read( &buffer[0], AT28C64B_TOTAL_BYTES );
for( offs_t offs = 0; offs < AT28C64B_TOTAL_BYTES; offs++ )
{
space(AS_PROGRAM).write_byte( offs, buffer[ offs ] );
}
}
//-------------------------------------------------
// nvram_write - called to write NVRAM to the
// .nv file
//-------------------------------------------------
void at28c64b_device::nvram_write( emu_file &file )
{
std::vector<uint8_t> buffer ( AT28C64B_TOTAL_BYTES );
for( offs_t offs = 0; offs < AT28C64B_TOTAL_BYTES; offs++ )
{
buffer[ offs ] = space(AS_PROGRAM).read_byte( offs );
}
file.write( &buffer[0], AT28C64B_TOTAL_BYTES );
}
//**************************************************************************
// READ/WRITE HANDLERS
//**************************************************************************
void at28c64b_device::write(offs_t offset, uint8_t data)
{
logerror("%s: AT28C64B: write( %04x, %02x ) state %d last_write %d\n", machine().describe_context(), offset, data, m_state, m_last_write);
if (m_state == STATE_SECTOR_WRITE)
{
logerror("SECTOR WRITE: %02x @ %x\n", data, offset);
this->space(AS_PROGRAM).write_byte(offset, data);
m_last_write = data;
m_write_timer->adjust(attotime::from_usec(10));
m_bytes_in_sector--;
if (m_bytes_in_sector == 0)
{
m_state = STATE_WRITE_PROTECT;
}
return;
}
if( m_last_write >= 0 )
{
// logerror( "%s: AT28C64B: write( %04x, %02x ) busy\n", machine().describe_context(), offset, data );
}
else if( m_oe_12v )
{
// logerror( "%s: AT28C64B: write( %04x, %02x ) erase\n", machine().describe_context(), offset, data );
if( m_last_write < 0 )
{
for( offs_t offs = 0; offs < AT28C64B_TOTAL_BYTES; offs++ )
{
this->space(AS_PROGRAM).write_byte( offs, 0xff );
}
m_last_write = 0xff;
m_write_timer->adjust( attotime::from_usec( 10 ) );
}
}
else
{
if ((offset == 0x1555) && (data == 0xaa))
{
m_state = STATE_ID_1;
return;
}
if ((m_state == STATE_ID_1) && (offset == 0xaaa) && (data == 0x55))
{
m_state = STATE_ID_2;
return;
}
if ((m_state == STATE_ID_2) && (offset == 0x1555) && (data == 0xa0))
{
m_state = STATE_SECTOR_WRITE;
m_bytes_in_sector = AT28C64B_SECTOR_SIZE;
return;
}
if (m_state == STATE_WRITE_PROTECT)
{
logerror("%s: write %02x to %x while write protected\n", machine().describe_context(), data, offset);
return;
}
if ((m_a9_12v) && (offset >= AT28C64B_ID_OFFSET) && (offset < (AT28C64B_ID_OFFSET + AT28C64B_ID_BYTES)))
{
offset += AT28C64B_ID_BYTES;
}
if( m_last_write < 0 && this->space(AS_PROGRAM).read_byte( offset ) != data )
{
this->space(AS_PROGRAM).write_byte( offset, data );
m_last_write = data;
m_write_timer->adjust( attotime::from_usec( 10 ) );
}
}
}
uint8_t at28c64b_device::read(offs_t offset)
{
if( m_last_write >= 0 )
{
uint8_t data = m_last_write ^ 0x80;
// logerror( "%s: AT28C64B: read( %04x ) write status %02x\n", machine().describe_context(), offset, data );
return data;
}
else
{
if( m_a9_12v && offset >= AT28C64B_ID_OFFSET )
{
offset += AT28C64B_ID_BYTES;
}
uint8_t data = this->space(AS_PROGRAM).read_byte( offset );
// logerror( "%s: AT28C64B: read( %04x ) data %02x\n", machine().describe_context(), offset, data );
return data;
}
}
WRITE_LINE_MEMBER( at28c64b_device::set_a9_12v )
{
state &= 1;
if( m_a9_12v != state )
{
// logerror( "%s: AT28C64B: set_a9_12v( %d )\n", machine().describe_context(), state );
m_a9_12v = state;
}
}
WRITE_LINE_MEMBER( at28c64b_device::set_oe_12v )
{
state &= 1;
if( m_oe_12v != state )
{
// logerror( "%s: AT28C64B: set_oe_12v( %d )\n", machine().describe_context(), state );
m_oe_12v = state;
}
}
void at28c64b_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
switch( id )
{
case 0:
m_last_write = -1;
break;
}
}

View File

@ -0,0 +1,80 @@
// license:BSD-3-Clause
// copyright-holders:smf
/***************************************************************************
ATMEL AT28C64B
64K ( 8K x 8 ) Parallel EEPROM
***************************************************************************/
#ifndef MAME_MACHINE_AT28C64B_H
#define MAME_MACHINE_AT28C64B_H
#pragma once
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> at28c64b_device
class at28c64b_device :
public device_t,
public device_memory_interface,
public device_nvram_interface
{
public:
// construction/destruction
at28c64b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
void write(offs_t offset, u8 data);
u8 read(offs_t offset);
protected:
// device-level overrides
virtual void device_start() override;
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
// device_memory_interface overrides
virtual space_config_vector memory_space_config() const override;
// device_nvram_interface overrides
virtual void nvram_default() override;
virtual void nvram_read(emu_file &file) override;
virtual void nvram_write(emu_file &file) override;
private:
// internal state
enum
{
STATE_IDLE,
STATE_ID_1,
STATE_ID_2,
STATE_SECTOR_WRITE,
STATE_WRITE_PROTECT
};
address_space_config m_space_config;
emu_timer *m_write_timer;
int m_a9_12v;
int m_oe_12v;
int m_last_write;
int m_state;
int m_bytes_in_sector;
optional_region_ptr<uint8_t> m_default_data;
// I/O operations
DECLARE_WRITE_LINE_MEMBER( set_a9_12v );
DECLARE_WRITE_LINE_MEMBER( set_oe_12v );
void at28c64b_map8(address_map &map);
};
// device type definition
DECLARE_DEVICE_TYPE(AT28C64B, at28c64b_device)
#endif // MAME_MACHINE_AT28C64B_H

View File

@ -0,0 +1,505 @@
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************
ch376.h
"File manage and control chip CH376"
https://www.mpja.com/download/ch376ds1.pdf
This is a module intended to offload USB and USB mass storage
I/O from a small microcontroller or microprocessor.
It has 3 host interfaces:
1) A regular 8-bit bus with 8 data lines, an address line,
chip select, read enable, and write enable lines
2) An SPI bus interface (SCS, SCK, MOSI, MISO lines)
3) An asynchronous serial interface (TxD and RxD lines)
And 2 guest interfaces: USB 2.0 and a second 4-wire SPI bus for SD/MMC/TF cards.
The module can give high level directory/open/close/read/write access to
files on a FAT12, FAT16, or FAT32 filesystem.
***************************************************************************/
#include "emu.h"
#include "emuopts.h"
#include "machine/ch376.h"
#include "logmacro.h"
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
// device type definition
DEFINE_DEVICE_TYPE(CH376, ch376_device, "ch376", "CH376 USB/file manager module")
static constexpr u8 STATUS_USB_INT_SUCCESS = 0x14;
static constexpr u8 STATUS_USB_INT_CONNECT = 0x15;
static constexpr u8 STATUS_USB_INT_DISK_READ = 0x1d;
static constexpr u8 STATUS_USB_INT_DISK_WRITE = 0x1e;
static constexpr u8 STATUS_USB_INT_DISK_ERR = 0x1f;
static constexpr u8 STATUS_ERR_MISS_FILE = 0x42;
static constexpr u8 CMD_GET_IC_VER = 0x01;
static constexpr u8 CMD_GET_FILE_SIZE = 0x0c;
static constexpr u8 CMD_SET_USB_MODE = 0x15;
static constexpr u8 CMD_GET_STATUS = 0x22;
static constexpr u8 CMD_RD_USB_DATA0 = 0x27;
static constexpr u8 CMD_WR_REQ_DATA = 0x2d;
static constexpr u8 CMD_SET_FILE_NAME = 0x2f;
static constexpr u8 CMD_DISK_MOUNT = 0x31;
static constexpr u8 CMD_FILE_OPEN = 0x32;
static constexpr u8 CMD_FILE_ENUM_GO = 0x33;
static constexpr u8 CMD_FILE_CLOSE = 0x36;
static constexpr u8 CMD_BYTE_LOCATE = 0x39;
static constexpr u8 CMD_BYTE_READ = 0x3a;
static constexpr u8 CMD_BYTE_READ_GO = 0x3b;
static constexpr u8 CMD_BYTE_WRITE = 0x3c;
static constexpr u8 CMD_BYTE_WR_GO = 0x3d;
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// ch376_device - constructor
//-------------------------------------------------
ch376_device::ch376_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, CH376, tag, owner, clock),
m_state(0),
m_status(0),
m_int_status(0),
m_data(0),
m_last_command(0),
m_pointer(0),
m_file_pos(0),
m_dataPtr(0),
m_dataLen(0),
m_readLen(0),
m_cur_file_size(0)
{
std::fill_n(&m_file_name[0], sizeof(m_file_name), 0);
std::fill_n(&m_dataBuffer[0], sizeof(m_dataBuffer), 0);
m_current_directory.clear();
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void ch376_device::device_start()
{
save_item(NAME(m_state));
save_item(NAME(m_status));
save_item(NAME(m_int_status));
save_item(NAME(m_data));
save_item(NAME(m_file_name));
save_item(NAME(m_pointer));
save_item(NAME(m_dataPtr));
save_item(NAME(m_dataLen));
save_item(NAME(m_readLen));
m_directory.reset();
m_file.reset();
}
u8 ch376_device::read(offs_t offset)
{
if (offset & 1)
{
return m_status;
}
switch (m_state)
{
case STATE_GET_STATUS:
m_state = STATE_IDLE;
return m_int_status;
case STATE_WRITE_SIZE_REPLY:
m_state = STATE_READ_WRITE_DATA;
m_dataPtr = 0;
return m_data;
}
if ((m_dataLen > 0) && (m_dataPtr < m_dataLen))
{
return m_dataBuffer[m_dataPtr++];
}
return m_data;
}
void ch376_device::write(offs_t offset, u8 data)
{
if (offset & 1) // write command
{
//printf("CH376: write command %02x\n", data);
m_last_command = data;
switch (data)
{
case CMD_GET_IC_VER:
m_dataBuffer[0] = 0x41; //
m_dataPtr = 0;
m_dataLen = 1;
break;
case CMD_GET_FILE_SIZE:
m_state = STATE_GET_FILE_SIZE;
break;
case CMD_SET_USB_MODE:
m_state = STATE_USB_MODE_SET;
break;
case CMD_GET_STATUS:
m_state = STATE_GET_STATUS;
break;
case CMD_RD_USB_DATA0:
m_int_status = STATUS_USB_INT_DISK_READ;
break;
case CMD_WR_REQ_DATA:
{
int lenToWrite = (m_readLen >= 255) ? 255 : m_readLen;
m_data = (lenToWrite & 0xff);
m_readLen -= lenToWrite;
m_state = STATE_WRITE_SIZE_REPLY;
m_dataLen = lenToWrite;
}
break;
case CMD_SET_FILE_NAME:
m_state = STATE_SET_FILE_NAME;
m_pointer = 0;
break;
case CMD_DISK_MOUNT:
m_int_status = STATUS_USB_INT_CONNECT;
break;
case CMD_FILE_OPEN:
/*
CH376 allows paths with a leading / or \ to indicate the root of the SD card.
Directories are chained by an unusual method: start with the root dir (leading path separator),
then open each subdirectory without a leading path separator, and finally open the file without
a leading path separator.
The BOOTI card ignores directories entirely, so the implmentation here is not tested with that
scenario but it should work.
*/
if (m_file_name[strlen(m_file_name) - 1] == '*')
{
// it's a directory
// remove the wildcard
m_file_name[strlen(m_file_name) - 1] = '\0';
std::string tmpPath(machine().options().share_directory());
tmpPath.append(PATH_SEPARATOR);
if ((m_file_name[0] == '/') || (m_file_name[0] == '\\'))
{
tmpPath.append(&m_file_name[1]);
m_current_directory.clear();
m_current_directory = tmpPath;
}
else
{
tmpPath.append(m_file_name);
m_current_directory.append(PATH_SEPARATOR);
m_current_directory.append(tmpPath);
}
m_directory = osd::directory::open(tmpPath);
if (generateNextDirEntry())
{
m_int_status = STATUS_USB_INT_DISK_READ;
}
else
{
m_int_status = STATUS_USB_INT_DISK_ERR;
}
}
else
{
std::string tmpPath = m_current_directory;
u64 size;
// compose this name with the last directory opened, unless it starts with a path separator
tmpPath.append(PATH_SEPARATOR);
if ((m_file_name[0] == '/') || (m_file_name[0] == '\\'))
{
tmpPath.append(&m_file_name[1]);
}
else
{
tmpPath.append(m_file_name);
}
if (osd_file::open(tmpPath, OPEN_FLAG_READ|OPEN_FLAG_WRITE, m_file, size) == osd_file::error::NONE)
{
m_int_status = STATUS_USB_INT_SUCCESS;
m_cur_file_size = size & 0xffffffff;
}
else
{
m_int_status = STATUS_USB_INT_DISK_ERR;
m_cur_file_size = 0;
}
}
break;
case CMD_FILE_ENUM_GO:
if (generateNextDirEntry())
{
m_int_status = STATUS_USB_INT_DISK_READ;
}
else // end of directory
{
m_int_status = STATUS_ERR_MISS_FILE;
}
break;
case CMD_FILE_CLOSE:
m_directory.reset();
m_file.reset();
m_int_status = STATUS_USB_INT_DISK_READ;
break;
case CMD_BYTE_LOCATE:
m_state = STATE_SEEK;
break;
case CMD_BYTE_READ:
m_state = STATE_READ_SIZE;
break;
case CMD_BYTE_READ_GO:
if (m_readLen > 0)
{
doNextFileRead();
m_int_status = STATUS_USB_INT_DISK_READ;
}
else
{
m_dataBuffer[0] = 0;
m_dataLen = 0;
m_int_status = STATUS_USB_INT_SUCCESS;
}
break;
case CMD_BYTE_WRITE:
m_state = STATE_READ_SIZE;
break;
case CMD_BYTE_WR_GO:
if (m_dataPtr > 0)
{
u32 actual;
m_state = STATE_IDLE;
m_file->write(&m_dataBuffer[0], m_file_pos, m_dataLen, actual);
m_file_pos += m_dataLen;
if (m_readLen > 0)
{
m_int_status = STATUS_USB_INT_DISK_WRITE;
}
else
{
m_int_status = STATUS_USB_INT_SUCCESS;
}
}
else
{
m_int_status = STATUS_USB_INT_SUCCESS;
}
break;
default:
logerror("CH376: unhandled command %02x\n", data);
break;
}
}
else
{
switch (m_state)
{
case STATE_USB_MODE_SET:
m_state = STATE_IDLE;
break;
case STATE_SET_FILE_NAME:
m_file_name[m_pointer++] = data;
if (m_pointer == 14)
{
m_state = STATE_IDLE;
// is this shorter than 8.3?
if (m_file_name[8] == '\0')
{
int idx = 8;
while (m_file_name[idx] == '\0')
{
idx--;
}
idx++;
for (int i = 0; i < 5; i++)
{
m_file_name[idx + i] = m_file_name[9 + i];
}
}
}
break;
case STATE_SEEK:
case STATE_SEEK1:
case STATE_SEEK2:
m_file_pos >>= 8;
m_file_pos |= (data << 24);
m_state++;
break;
case STATE_SEEK3:
m_file_pos >>= 8;
m_file_pos |= (data << 24);
m_state = STATE_IDLE;
m_int_status = STATUS_USB_INT_SUCCESS;
break;
case STATE_READ_SIZE:
m_readLen = data;
m_state++;
break;
case STATE_READ_SIZE2:
m_readLen |= (data << 8);
m_state = STATE_IDLE;
m_int_status = STATUS_USB_INT_DISK_READ;
if (m_last_command == 0x3a)
{
doNextFileRead();
}
break;
case STATE_READ_WRITE_DATA:
m_dataBuffer[m_dataPtr++] = data;
break;
case STATE_GET_FILE_SIZE:
// the host must write 0x68 here; it's unclear what the real chip does if the value doesn't match.
// reply with the size of the currently open file.
m_dataBuffer[0] = m_cur_file_size & 0xff;
m_dataBuffer[1] = (m_cur_file_size >> 8) & 0xff;
m_dataBuffer[2] = (m_cur_file_size >> 16) & 0xff;
m_dataBuffer[3] = (m_cur_file_size >> 24) & 0xff;
m_dataPtr = 0;
m_dataLen = 4;
break;
}
}
}
bool ch376_device::generateNextDirEntry()
{
const osd::directory::entry *ourEntry;
m_dataPtr = 0;
m_dataLen = 0;
// no directory?
if (m_directory == nullptr)
{
return false;
}
// is there an entry?
if ((ourEntry = m_directory->read()) == nullptr)
{
return false;
}
std::fill_n(&m_dataBuffer[0], sizeof(m_dataBuffer), 0);
m_dataBuffer[0] = 32;
if (ourEntry->type == osd::directory::entry::entry_type::DIR)
{
strncpy(reinterpret_cast<char *>(&m_dataBuffer[1]), ourEntry->name, 8);
m_dataBuffer[0xc] == 0x10; // directory type
}
else if (ourEntry->type == osd::directory::entry::entry_type::FILE)
{
int dotIdx = 8;
for (int idx = (strlen(ourEntry->name) - 1); idx >= 0; idx--)
{
if (ourEntry->name[idx] == '.')
{
dotIdx = idx;
break;
}
}
int baseLen = std::min(8, dotIdx);
for (int idx = 0; idx < baseLen; idx++)
{
m_dataBuffer[idx + 1] = toupper(ourEntry->name[idx]);
if (ourEntry->name[idx] == '\0')
{
baseLen = idx;
break;
}
}
// copy the extension (up to 3 chars, if we found one)
if (ourEntry->name[dotIdx] == '.')
{
dotIdx++;
for (int idx = 0; idx < 3; idx++)
{
if ((idx + dotIdx) > strlen(ourEntry->name))
{
break;
}
m_dataBuffer[idx + 8 + 1] = toupper(ourEntry->name[idx + dotIdx]);
}
}
m_dataBuffer[0xc] = 0; // no attributes
if (ourEntry->size >= u64(0x100000000))
{
m_dataBuffer[0x1d] = 0xff;
m_dataBuffer[0x1e] = 0xff;
m_dataBuffer[0x1f] = 0xff;
m_dataBuffer[0x20] = 0xff;
}
else
{
m_dataBuffer[0x1d] = (ourEntry->size & 0xff);
m_dataBuffer[0x1e] = ((ourEntry->size >> 8) & 0xff);
m_dataBuffer[0x1f] = ((ourEntry->size >> 16) & 0xff);
m_dataBuffer[0x20] = ((ourEntry->size >> 24) & 0xff);
}
}
else // not a file or directory, recurse and hope the next one's better
{
return generateNextDirEntry();
}
m_dataLen = 32;
return true;
}
void ch376_device::doNextFileRead()
{
int lenToRead = (m_readLen >= 255) ? 255 : m_readLen;
u32 actualRead;
m_readLen -= lenToRead;
m_file->read(&m_dataBuffer[1], m_file_pos, lenToRead, actualRead);
m_dataBuffer[0] = (lenToRead & 0xff);
m_file_pos += actualRead;
m_dataLen = 256;
m_dataPtr = 0;
}

View File

@ -0,0 +1,80 @@
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************
ch376.h
"File manage and control chip CH376"
This is a module intended to offload USB and USB mass storage
I/O from a small microcontroller or microprocessor.
***************************************************************************/
#ifndef MAME_MACHINE_CH376_H
#define MAME_MACHINE_CH376_H
#pragma once
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> ch376_device
class ch376_device : public device_t
{
public:
// construction/destruction
ch376_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
u8 read(offs_t offset);
void write(offs_t offset, u8 data);
protected:
// device-level overrides
virtual void device_start() override;
private:
enum
{
STATE_IDLE = 0,
STATE_USB_MODE_SET,
STATE_GET_STATUS,
STATE_SET_FILE_NAME,
STATE_SEEK,
STATE_SEEK1,
STATE_SEEK2,
STATE_SEEK3,
STATE_READ_SIZE,
STATE_READ_SIZE2,
STATE_WRITE_SIZE_REPLY,
STATE_READ_WRITE_DATA,
STATE_GET_FILE_SIZE
};
bool generateNextDirEntry();
void doNextFileRead();
int m_state;
u8 m_status, m_int_status, m_data, m_last_command;
char m_file_name[16];
int m_pointer;
std::string m_current_directory;
osd::directory::ptr m_directory;
osd_file::ptr m_file;
u32 m_file_pos;
u8 m_dataBuffer[512];
int m_dataPtr, m_dataLen;
u16 m_readLen;
u32 m_cur_file_size;
};
// device type definition
DECLARE_DEVICE_TYPE(CH376, ch376_device)
#endif // MAME_MACHINE_CH376_H

View File

@ -168,6 +168,7 @@ MIG RAM page 2 $CE02 is the speaker/slot bitfield and $CE03 is the paddle/accele
#include "bus/a2bus/a2videoterm.h"
#include "bus/a2bus/a2vulcan.h"
#include "bus/a2bus/a2zipdrive.h"
#include "bus/a2bus/booti.h"
#include "bus/a2bus/byte8251.h"
#include "bus/a2bus/cmsscsi.h"
#include "bus/a2bus/computereyes2.h"
@ -4694,6 +4695,7 @@ static void apple2_cards(device_slot_interface &device)
device.option_add("sider1", A2BUS_SIDER1); /* Advanced Tech Systems / First Class Peripherals Sider 1 SASI card */
device.option_add("uniprint", A2BUS_UNIPRINT); /* Videx Uniprint parallel printer card */
device.option_add("ccs7710", A2BUS_CCS7710); /* California Computer Systems Model 7710 Asynchronous Serial Interface */
device.option_add("booti", A2BUS_BOOTI); /* Booti Card */
}
static void apple2eaux_cards(device_slot_interface &device)

View File

@ -124,6 +124,7 @@
#include "bus/a2bus/timemasterho.h"
#include "bus/a2bus/uniprint.h"
#include "bus/a2bus/uthernet.h"
#include "bus/a2bus/booti.h"
#include "bus/a2gameio/gameio.h"
@ -4771,6 +4772,7 @@ static void apple2_cards(device_slot_interface &device)
device.option_add("sider1", A2BUS_SIDER1); /* Advanced Tech Systems / First Class Peripherals Sider 1 SASI card */
device.option_add("uniprint", A2BUS_UNIPRINT); /* Videx Uniprint parallel printer card */
device.option_add("ccs7710", A2BUS_CCS7710); /* California Computer Systems Model 7710 Asynchronous Serial Interface */
device.option_add("booti", A2BUS_BOOTI); /* Booti Card */
}
void apple2gs_state::apple2gs(machine_config &config)