summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus
diff options
context:
space:
mode:
author arbee <rb6502@users.noreply.github.com>2021-09-07 19:51:34 -0400
committer arbee <rb6502@users.noreply.github.com>2021-09-07 19:51:34 -0400
commitea8102906e6af8c17d29b06de9061e72245413a1 (patch)
treea9159572c3c6157772e7817d5dfd8420951644e9 /src/devices/bus
parent87ecae55e334225aa74e292a95a3380d29ace405 (diff)
apple2: support for the AppleIISD card [R. Belmont, Florian Reitz]
Diffstat (limited to 'src/devices/bus')
-rw-r--r--src/devices/bus/a2bus/a2sd.cpp280
-rw-r--r--src/devices/bus/a2bus/a2sd.h67
2 files changed, 347 insertions, 0 deletions
diff --git a/src/devices/bus/a2bus/a2sd.cpp b/src/devices/bus/a2bus/a2sd.cpp
new file mode 100644
index 00000000000..c5c9937caaf
--- /dev/null
+++ b/src/devices/bus/a2bus/a2sd.cpp
@@ -0,0 +1,280 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont
+/*********************************************************************
+
+ a2sd.cpp
+
+ Implementation of the AppleIISD card by Florian Reitz
+ https://github.com/freitz85/AppleIISd
+
+ AppleIISD has a Xilinx FPGA which implements a minimally hardware
+ assisted SPI interface, and the SD card is thus driven in SPI
+ mode rather than SD.
+
+ The SPI controller is fixed to SPI Mode 3 only.
+ (shift on falling CLK edges, shift-then-latch).
+
+ Firmware is contained in an Atmel 28C64B parallel EEPROM, which
+ has a Flash-style command set.
+
+*********************************************************************/
+
+#include "emu.h"
+#include "a2sd.h"
+
+#define LOG_GENERAL (1U << 0)
+#define LOG_SPI (1U << 1)
+
+//#define VERBOSE (LOG_COMMAND)
+#define LOG_OUTPUT_FUNC osd_printf_info
+
+#include "logmacro.h"
+
+/***************************************************************************
+ PARAMETERS
+***************************************************************************/
+
+static constexpr u8 C0N1_ECE = 0x04; // external clock: toggles between 500 kHz internal clock and 1/2 of the 7M A2 bus clock
+static constexpr u8 C0N1_FRX = 0x10; // fast recieve: when enabled, both reads and writes of the SPI data register start a shift cycle
+[[unused]] static constexpr u8 C0N1_BSY = 0x20;
+static constexpr u8 C0N1_TC = 0x80; // SPI transfer complete
+
+static constexpr u8 C0N3_CD = 0x40; // card detect
+static constexpr u8 C0N3_BIT_SS = 7;
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+DEFINE_DEVICE_TYPE(A2BUS_A2SD, a2bus_a2sd_device, "a2sd", "Apple II SD Card")
+
+ROM_START( a2sd )
+ ROM_REGION(0x2000, "flash", ROMREGION_ERASE00)
+ ROM_LOAD( "appleiisd.bin", 0x000000, 0x000800, CRC(e82eea8a) SHA1(7e0acef01e622eeed6f8e87893d07c701bbef016) )
+ROM_END
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+//-------------------------------------------------
+// device_add_mconfig - add device configuration
+//-------------------------------------------------
+void a2bus_a2sd_device::device_add_mconfig(machine_config &config)
+{
+ AT28C64B(config, m_flash, 0);
+
+ SPI_SDCARD(config, m_sdcard, 0);
+ m_sdcard->spi_miso_callback().set(FUNC(a2bus_a2sd_device::spi_miso_w));
+}
+
+//-------------------------------------------------
+// rom_region - device-specific ROM region
+//-------------------------------------------------
+
+const tiny_rom_entry *a2bus_a2sd_device::device_rom_region() const
+{
+ return ROM_NAME( a2sd );
+}
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+a2bus_a2sd_device::a2bus_a2sd_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_sdcard(*this, "sdcard"),
+ m_datain(0), m_in_latch(0), m_out_latch(0), m_c0n1(0), m_c0n3(0x80), m_in_bit(0), m_shift_count(0)
+{
+}
+
+a2bus_a2sd_device::a2bus_a2sd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ a2bus_a2sd_device(mconfig, A2BUS_A2SD, tag, owner, clock)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void a2bus_a2sd_device::device_start()
+{
+ m_shift_timer = timer_alloc(0);
+
+ save_item(NAME(m_datain));
+ save_item(NAME(m_in_latch));
+ save_item(NAME(m_out_latch));
+ save_item(NAME(m_c0n1));
+ save_item(NAME(m_c0n3));
+ save_item(NAME(m_in_bit));
+ save_item(NAME(m_shift_count));
+}
+
+void a2bus_a2sd_device::device_reset()
+{
+ m_shift_timer->adjust(attotime::never);
+ m_shift_count = 0;
+ m_sdcard->spi_clock_w(CLEAR_LINE);
+}
+
+void a2bus_a2sd_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ LOGMASKED(LOG_SPI, ">>>>>>> SHIFT %d (%c)\n", m_shift_count, (m_shift_count & 1) ? 'L' : 'S');
+ if (!(m_shift_count & 1))
+ {
+ if (m_shift_count < 16)
+ {
+ m_out_latch <<= 1;
+ }
+ m_in_latch <<= 1;
+ m_sdcard->spi_mosi_w(BIT(m_out_latch, 7));
+ m_sdcard->spi_clock_w(CLEAR_LINE);
+ }
+ else
+ {
+ m_in_latch &= ~0x01;
+ m_in_latch |= m_in_bit;
+ m_sdcard->spi_clock_w(ASSERT_LINE);
+
+ if (m_shift_count == 1)
+ {
+ m_datain = m_in_latch;
+ LOGMASKED(LOG_SPI, "SPI: got %02x (in latch %02x)\n", m_datain, m_in_latch);
+ }
+ }
+
+ m_shift_count--;
+ if (m_shift_count == 0)
+ {
+ m_shift_timer->adjust(attotime::never);
+ m_c0n1 |= C0N1_TC; // set TC
+ }
+}
+
+/*-------------------------------------------------
+ read_c0nx - called for reads from this card's c0nx space
+-------------------------------------------------*/
+
+u8 a2bus_a2sd_device::read_c0nx(u8 offset)
+{
+ switch (offset)
+ {
+ case 0:
+ m_c0n1 &= ~C0N1_TC; // clear TC
+ // if FRX is set, both reads and writes trigger a shift cycle
+ if (m_c0n1 & C0N1_FRX)
+ {
+ m_c0n1 &= ~C0N1_TC; // clear TC
+ m_shift_count = 16;
+ m_out_latch = 0xff;
+
+ if (m_c0n1 & C0N1_ECE)
+ {
+ m_shift_timer->adjust(attotime::from_hz(14.318181_MHz_XTAL / 4), 0, attotime::from_hz(14.318181_MHz_XTAL / 4));
+ }
+ else
+ {
+ m_shift_timer->adjust(attotime::from_hz(500_kHz_XTAL), 0, attotime::from_hz(500_kHz_XTAL));
+ }
+ }
+ return m_datain;
+
+ case 1:
+ return m_c0n1;
+
+ case 2:
+ return 0;
+
+ case 3:
+ m_c0n3 &= ~C0N3_CD;
+ m_c0n3 |= m_sdcard->get_card_present() ? 0 : C0N3_CD; // bit is set if no card is present
+ return m_c0n3;
+ }
+ return 0xff;
+}
+
+
+/*-------------------------------------------------
+ write_c0nx - called for writes to this card's c0nx space
+-------------------------------------------------*/
+
+void a2bus_a2sd_device::write_c0nx(u8 offset, u8 data)
+{
+ switch (offset)
+ {
+ case 0:
+ if (m_shift_count)
+ {
+ return;
+ }
+ LOGMASKED(LOG_SPI, "SPI sending %02x\n", data);
+ m_out_latch = data;
+ m_sdcard->spi_mosi_w(BIT(m_out_latch, 7));
+ m_c0n1 &= ~C0N1_TC; // clear TC
+ m_shift_count = 16;
+ // if ECE is set, clock is 3.58 MHz from the A2 bus, otherwise internally generated 500 kHz
+ if (m_c0n1 & C0N1_ECE)
+ {
+ m_shift_timer->adjust(attotime::from_hz(14.318181_MHz_XTAL / 4), 0, attotime::from_hz(14.318181_MHz_XTAL/4));
+ }
+ else
+ {
+ m_shift_timer->adjust(attotime::from_hz(500_kHz_XTAL), 0, attotime::from_hz(500_kHz_XTAL));
+ }
+ break;
+
+ case 1:
+ m_c0n1 &= 0xea;
+ m_c0n1 |= (data & 0x15);
+ break;
+
+ case 2:
+ break;
+
+ case 3:
+ m_c0n3 &= 0x9f;
+ m_c0n3 |= (data & 0x91);
+
+ m_sdcard->spi_ss_w(BIT(data, C0N3_BIT_SS));
+ LOGMASKED(LOG_GENERAL, "/SS is %x\n", BIT(data, C0N3_BIT_SS));
+ break;
+
+ default:
+ logerror("a2sd: 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
+-------------------------------------------------*/
+
+u8 a2bus_a2sd_device::read_cnxx(u8 offset)
+{
+ // slot image at 0
+ return m_flash->read(offset);
+}
+
+void a2bus_a2sd_device::write_cnxx(u8 offset, u8 data)
+{
+ m_flash->write(offset, data);
+}
+
+/*-------------------------------------------------
+ read_c800 - called for reads from this card's c800 space
+-------------------------------------------------*/
+
+u8 a2bus_a2sd_device::read_c800(u16 offset)
+{
+ return m_flash->read(offset + 0x100);
+}
+
+/*-------------------------------------------------
+ write_c800 - called for writes to this card's c800 space
+-------------------------------------------------*/
+void a2bus_a2sd_device::write_c800(u16 offset, u8 data)
+{
+ m_flash->write(offset + 0x100, data);
+}
diff --git a/src/devices/bus/a2bus/a2sd.h b/src/devices/bus/a2bus/a2sd.h
new file mode 100644
index 00000000000..c4d8b4c8f02
--- /dev/null
+++ b/src/devices/bus/a2bus/a2sd.h
@@ -0,0 +1,67 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont
+/*********************************************************************
+
+ a2sd.h
+
+ Implementation of the AppleIISD card
+
+*********************************************************************/
+
+#ifndef MAME_BUS_A2BUS_A2SD_H
+#define MAME_BUS_A2BUS_A2SD_H
+
+#pragma once
+
+#include "a2bus.h"
+#include "machine/at28c64b.h"
+#include "machine/spi_sdcard.h"
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+class a2bus_a2sd_device:
+ public device_t,
+ public device_a2bus_card_interface
+{
+public:
+ // construction/destruction
+ a2bus_a2sd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+protected:
+ a2bus_a2sd_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;
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
+
+ // overrides of standard a2bus slot functions
+ virtual u8 read_c0nx(u8 offset) override;
+ virtual void write_c0nx(u8 offset, u8 data) override;
+ virtual u8 read_cnxx(u8 offset) override;
+ virtual void write_cnxx(u8 offset, u8 data) override;
+ virtual u8 read_c800(uint16_t offset) override;
+ virtual void write_c800(uint16_t offset, u8 data) override;
+
+ // SPI 4-wire interface
+ WRITE_LINE_MEMBER(spi_miso_w) { m_in_bit = state; }
+
+private:
+ required_device<at28c64b_device> m_flash;
+ required_device<spi_sdcard_device> m_sdcard;
+
+ u8 m_datain, m_in_latch, m_out_latch;
+ u8 m_c0n1, m_c0n3;
+ int m_in_bit;
+
+ int m_shift_count;
+ emu_timer *m_shift_timer;
+};
+
+// device type definition
+DECLARE_DEVICE_TYPE(A2BUS_A2SD, a2bus_a2sd_device)
+
+#endif // MAME_BUS_A2BUS_A2SD_H