summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author arbee <rb6502@users.noreply.github.com>2021-05-08 16:19:36 -0400
committer arbee <rb6502@users.noreply.github.com>2021-05-08 16:19:36 -0400
commitfa5977301ec34f26bbaab3e1ac3eb9750ebde50d (patch)
tree209dd19822b38e649804b5057e9c920331703a7b
parent6704819c1f1a5512667da958664a0badd0439b64 (diff)
apple2: Add support for the BOOTI card [R. Belmont]
-rw-r--r--scripts/src/bus.lua2
-rw-r--r--scripts/src/machine.lua23
-rw-r--r--scripts/target/mame/mess.lua2
-rw-r--r--src/devices/bus/a2bus/booti.cpp171
-rw-r--r--src/devices/bus/a2bus/booti.h58
-rw-r--r--src/devices/machine/at28c64b.cpp279
-rw-r--r--src/devices/machine/at28c64b.h80
-rw-r--r--src/devices/machine/ch376.cpp505
-rw-r--r--src/devices/machine/ch376.h80
-rw-r--r--src/mame/drivers/apple2e.cpp2
-rw-r--r--src/mame/drivers/apple2gs.cpp2
11 files changed, 1204 insertions, 0 deletions
diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua
index 6b95615a32a..4cbf0266db2 100644
--- a/scripts/src/bus.lua
+++ b/scripts/src/bus.lua
@@ -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",
diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua
index 25d518e5065..8be9fe1d911 100644
--- a/scripts/src/machine.lua
+++ b/scripts/src/machine.lua
@@ -838,6 +838,18 @@ 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
---------------------------------------------------
--
diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua
index b40f73b2c46..f01652ddbd2 100644
--- a/scripts/target/mame/mess.lua
+++ b/scripts/target/mame/mess.lua
@@ -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
diff --git a/src/devices/bus/a2bus/booti.cpp b/src/devices/bus/a2bus/booti.cpp
new file mode 100644
index 00000000000..e365ebe2492
--- /dev/null
+++ b/src/devices/bus/a2bus/booti.cpp
@@ -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);
+ }
+}
diff --git a/src/devices/bus/a2bus/booti.h b/src/devices/bus/a2bus/booti.h
new file mode 100644
index 00000000000..571aa799e90
--- /dev/null
+++ b/src/devices/bus/a2bus/booti.h
@@ -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
diff --git a/src/devices/machine/at28c64b.cpp b/src/devices/machine/at28c64b.cpp
new file mode 100644
index 00000000000..194711228a1
--- /dev/null
+++ b/src/devices/machine/at28c64b.cpp
@@ -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;
+ }
+}
diff --git a/src/devices/machine/at28c64b.h b/src/devices/machine/at28c64b.h
new file mode 100644
index 00000000000..994625bf42d
--- /dev/null
+++ b/src/devices/machine/at28c64b.h
@@ -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
diff --git a/src/devices/machine/ch376.cpp b/src/devices/machine/ch376.cpp
new file mode 100644
index 00000000000..0b62e0a9076
--- /dev/null
+++ b/src/devices/machine/ch376.cpp
@@ -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;
+}
diff --git a/src/devices/machine/ch376.h b/src/devices/machine/ch376.h
new file mode 100644
index 00000000000..85af7069099
--- /dev/null
+++ b/src/devices/machine/ch376.h
@@ -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
diff --git a/src/mame/drivers/apple2e.cpp b/src/mame/drivers/apple2e.cpp
index 3a8dfa7a8f4..d2f6a86cd13 100644
--- a/src/mame/drivers/apple2e.cpp
+++ b/src/mame/drivers/apple2e.cpp
@@ -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)
diff --git a/src/mame/drivers/apple2gs.cpp b/src/mame/drivers/apple2gs.cpp
index ab228134e4e..490d5ee65be 100644
--- a/src/mame/drivers/apple2gs.cpp
+++ b/src/mame/drivers/apple2gs.cpp
@@ -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)