-bus/a2bus: Added //SHH SYSTEME LANceGS Card. (#8264)

-machine/smc91c9x.cpp adjustments:
* Reset should disable promiscuous mode.
* RCR - handle promiscuous changes, adjust soft reset handling.
* EPH_STATUS should be LINK_OK by default (previous code wouldn't set LINK_OK unless RX_EN is set but LANceGS won't set RX_EN unless LINK_OK is set).
* B0_BANK bits 4-8 are unspecified in the documentation but LANceGS expects a 3 (used in Card detection logic).
* MIR values depend on device type and resets when the mmu resets.
* Reading/writing the data register needs to be aware of 8-bit I/O.
* Calculate FCS.
* Removed WMS OUI filtering hack.
This commit is contained in:
ksherlock 2021-07-08 21:38:52 -04:00 committed by GitHub
parent 6ffc98289c
commit a9fefd8363
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 280 additions and 28 deletions

View File

@ -2460,6 +2460,8 @@ if (BUSES["A2BUS"]~=null) then
MAME_DIR .. "src/devices/bus/a2bus/ezcgi.h",
MAME_DIR .. "src/devices/bus/a2bus/grappler.cpp",
MAME_DIR .. "src/devices/bus/a2bus/grappler.h",
MAME_DIR .. "src/devices/bus/a2bus/lancegs.cpp",
MAME_DIR .. "src/devices/bus/a2bus/lancegs.h",
MAME_DIR .. "src/devices/bus/a2bus/laser128.cpp",
MAME_DIR .. "src/devices/bus/a2bus/laser128.h",
MAME_DIR .. "src/devices/bus/a2bus/mouse.cpp",

View File

@ -0,0 +1,178 @@
// license:BSD-3-Clause
// copyright-holders:Kelvin Sherlock
/*********************************************************************
lancegs.cpp
Apple II LANceGS Card
Kelvin Sherlock with assistance from Geoff Weiss
SMSC LAN 91c96 and 24c04 EEPROM
Bit 1 of C0nF switches between EEPROM (1) and Ethernet (0, default).
This bit is write only.
In EEPROM Mode:
C0n4 - Clock Low
C0n5 - Clock High
C0n6 - Data Low
C0n7 - Data High
C0n8 - Read Data
C0nF - toggle EEPROM/Ethernet
Known EEPROM locations. All strings high ascii, 0-terminated.
0x00: Identification string ("LANceGS Ethernet Card (c) 2000 Joachim Lange")
0x30: Revision string
0x40: Revision Code (1 byte)
0x48: Production date, yy m d (4 bytes)
0x4c: Service date, yy m d (4 bytes)
0x50: Serial Number (2 bytes)
0x60: MAC Address (6 bytes)
0x66: Destination Mac Address (6 bytes) *
0x80: IP Address (4 bytes) *
0x84: Destination IP Address (4 bytes) *
0x88: Netmask (4 bytes) *
0x8c: Gateway (4 bytes) *
* configurable with included software.
*********************************************************************/
#include "emu.h"
#include "lancegs.h"
/***************************************************************************
PARAMETERS
***************************************************************************/
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
DEFINE_DEVICE_TYPE(A2BUS_LANCEGS, a2bus_lancegs_device, "a2lancegs", "///SHH Systeme LANceGS")
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
a2bus_lancegs_device::a2bus_lancegs_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
a2bus_lancegs_device(mconfig, A2BUS_LANCEGS, tag, owner, clock)
{
}
a2bus_lancegs_device::a2bus_lancegs_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_netinf(*this, "smc91c96"),
m_i2cmem(*this, "i2cmem"),
m_shadow(false)
{
}
void a2bus_lancegs_device::device_add_mconfig(machine_config &config)
{
SMC91C96(config, m_netinf, 20_MHz_XTAL); // Datasheet fig 12.26, pg 122.
I2C_24C04(config, m_i2cmem, 0).set_address(0x80).set_e0(1);
m_netinf->irq_handler().set(FUNC(a2bus_lancegs_device::netinf_irq_w));
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void a2bus_lancegs_device::device_start()
{
save_item(NAME(m_shadow));
}
void a2bus_lancegs_device::device_reset()
{
m_shadow = false;
}
/*-------------------------------------------------
read_c0nx - called for reads from this card's c0nx space
-------------------------------------------------*/
uint8_t a2bus_lancegs_device::read_c0nx(uint8_t offset)
{
if (m_shadow) {
switch(offset) {
case 0x08: /* read data */
return 0xfe | m_i2cmem->read_sda();
break;
case 0x0f:
return 0x33;
break;
default:
return 0xff;
break;
}
} else {
const uint16_t mask = offset & 0x01 ? 0xff00: 0x00ff;
const uint16_t value = m_netinf->read(offset >> 1, mask);
return offset & 0x01 ? value >> 8 : value;
}
}
/*-------------------------------------------------
write_c0nx - called for writes to this card's c0nx space
-------------------------------------------------*/
void a2bus_lancegs_device::write_c0nx(uint8_t offset, uint8_t data)
{
if (offset == 0x0f) {
m_shadow = data & 0x01;
return;
}
if (m_shadow) {
switch(offset) {
case 0x04: /* set clock low */
m_i2cmem->write_scl(0);
break;
case 0x05: /* set clock high */
m_i2cmem->write_scl(1);
break;
case 0x06: /* set data low */
m_i2cmem->write_sda(0);
break;
case 0x07: /* set data high */
m_i2cmem->write_sda(1);
break;
default:
break;
}
} else {
const uint16_t mask = offset & 0x01 ? 0xff00: 0x00ff;
const uint16_t value = offset & 0x01 ? data << 8 : data;
m_netinf->write(offset >> 1, value, mask);
}
}
WRITE_LINE_MEMBER( a2bus_lancegs_device::netinf_irq_w )
{
if (state) {
raise_slot_irq();
} else {
lower_slot_irq();
}
}
ROM_START(lancegs)
ROM_REGION(0x0200, "i2cmem", 0)
ROM_LOAD("lancegs.nv", 0x0000, 0x0200, NO_DUMP)
ROM_END
const tiny_rom_entry *a2bus_lancegs_device::device_rom_region() const
{
return ROM_NAME(lancegs);
}

View File

@ -0,0 +1,52 @@
// license:BSD-3-Clause
// copyright-holders:Kelvin Sherlock
/*********************************************************************
lancegs.h
Apple II LANceGS Card
*********************************************************************/
#ifndef MAME_BUS_A2BUS_LANCEGS_H
#define MAME_BUS_A2BUS_LANCEGS_H
#include "a2bus.h"
#include "machine/smc91c9x.h"
#include "machine/i2cmem.h"
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
class a2bus_lancegs_device:
public device_t,
public device_a2bus_card_interface
{
public:
// construction/destruction
a2bus_lancegs_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
protected:
a2bus_lancegs_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
virtual void device_add_mconfig(machine_config &config) override;
virtual const tiny_rom_entry *device_rom_region() const override;
virtual void device_start() override;
virtual void device_reset() override;
virtual uint8_t read_c0nx(uint8_t offset) override;
virtual void write_c0nx(uint8_t offset, uint8_t data) override;
private:
required_device<smc91c96_device> m_netinf;
required_device<i2c_24c04_device> m_i2cmem;
bool m_shadow;
DECLARE_WRITE_LINE_MEMBER( netinf_irq_w );
};
// device type definition
DECLARE_DEVICE_TYPE(A2BUS_LANCEGS, a2bus_lancegs_device)
#endif // MAME_BUS_A2BUS_LANCEGS_H

View File

@ -71,7 +71,6 @@ smc91c9x_device::smc91c9x_device(const machine_config &mconfig, device_type type
}
const u8 smc91c9x_device::ETH_BROADCAST[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
const u8 smc91c9x_device::WMS_OUI[] = { 0x00, 0xA0, 0xAF };
//-------------------------------------------------
// device_start - device-specific startup
@ -95,12 +94,19 @@ void smc91c9x_device::device_start()
m_reg[B1_IA2_3] = 0x0000; m_regmask[B1_IA2_3] = 0xffff;
m_reg[B1_IA4_5] = 0x0000; m_regmask[B1_IA4_5] = 0xffff;
// Revision is set based on chip type
// Revision and MIR is based on chip type
m_regmask[B3_REVISION] = 0x0000;
m_regmask[B0_MIR] = 0x0000;
if (m_device_type == dev_type::SMC91C94)
{
m_reg[B3_REVISION] = 0x3345;
m_reg[B0_MIR] = 0x1212;
}
else if (m_device_type == dev_type::SMC91C96)
{
m_reg[B3_REVISION] = 0x3346;
m_reg[B0_MIR] = 0x1818;
}
else
fatalerror("device_start: Unknown device type\n");
@ -148,12 +154,11 @@ void smc91c9x_device::device_reset()
m_tx_retry_count = 0;
m_reg[B0_TCR] = 0x0000; m_regmask[B0_TCR] = 0x3d87;
m_reg[B0_EPH_STATUS] = 0x0000; m_regmask[B0_EPH_STATUS] = 0x0000;
m_reg[B0_EPH_STATUS] = 0x4000; m_regmask[B0_EPH_STATUS] = 0x0000;
m_reg[B0_RCR] = 0x0000; m_regmask[B0_RCR] = 0xc307;
m_reg[B0_COUNTER] = 0x0000; m_regmask[B0_COUNTER] = 0x0000;
m_reg[B0_MIR] = 0x1212; m_regmask[B0_MIR] = 0x0000;
m_reg[B0_MCR] = 0x3300; m_regmask[B0_MCR] = 0x00ff;
m_reg[B0_BANK] = 0x3300; m_regmask[B0_BANK] = 0x0007;
m_reg[B0_BANK] = 0x3330; m_regmask[B0_BANK] = 0x0007;
m_reg[B1_GENERAL_PURP] = 0x0000; m_regmask[B1_GENERAL_PURP] = 0xffff;
m_reg[B1_CONTROL] = 0x0100; m_regmask[B1_CONTROL] = 0x68e7;
@ -174,6 +179,8 @@ void smc91c9x_device::device_reset()
m_reg[B3_ERCV] = 0x331f; m_regmask[B3_ERCV] = 0x009f;
set_promisc(false);
update_ethernet_irq();
// Reset MMU
@ -186,6 +193,10 @@ void smc91c9x_device::mmu_reset()
m_alloc_rx = 0;
m_alloc_tx = 0;
// The register defaults to the MEMORY SIZE upon reset or upon the RESET MMU command.
m_reg[B0_MIR] &= 0xff;
m_reg[B0_MIR] |= m_reg[B0_MIR] << 8;
// Reset fifos.
reset_tx_fifos();
reset_completed_rx();
@ -394,14 +405,6 @@ int smc91c9x_device::recv_start_cb(u8 *buf, int length)
return 0;
}
// discard packets not from WMS
if (memcmp(WMS_OUI, &buf[6], 3))
{
LOGMASKED(LOG_RX, "received non-WMS packet OUI: %02x:%02x:%02x length %d discarded\n", buf[6], buf[7], buf[8], length);
return 0;
}
// Check for active transmission
if (m_tx_active)
{
@ -557,13 +560,14 @@ TIMER_CALLBACK_MEMBER(smc91c9x_device::tx_poll)
tx_buffer[length++] = 0x00;
// Add CRC
// TODO: Calculate CRC
if (1 && ((control & EBUF_CRC) || !(m_reg[B0_TCR] & NOCRC)))
if (((control & EBUF_CRC) || !(m_reg[B0_TCR] & NOCRC)))
{
tx_buffer[length++] = 0x11;
tx_buffer[length++] = 0x22;
tx_buffer[length++] = 0x33;
tx_buffer[length++] = 0x44;
u32 crc = util::crc32_creator::simple(tx_buffer + 4, length - 4);
tx_buffer[length++] = (crc >> 0) & 0xff;
tx_buffer[length++] = (crc >> 8) & 0xff;
tx_buffer[length++] = (crc >> 16) & 0xff;
tx_buffer[length++] = (crc >> 24) & 0xff;
}
// Remove status, length
@ -590,7 +594,7 @@ TIMER_CALLBACK_MEMBER(smc91c9x_device::tx_poll)
if (m_reg[B0_TCR] & (EPH_LOOP | LOOP))
send_complete_cb(length);
else
send(&tx_buffer[4], length);
send(&tx_buffer[4], length, 4);
}
}
@ -841,7 +845,9 @@ u16 smc91c9x_device::read(offs_t offset, u16 mem_mask)
else
buffer = &m_buffer[(m_reg[B2_PNR_ARR] & 0x1f) * ETHER_BUFFER_SIZE];
result = buffer[addr++];
result = 0;
if ( ACCESSING_BITS_0_7 )
result = buffer[addr++];
if ( ACCESSING_BITS_8_15 )
result |= buffer[addr++] << 8;
if ( m_reg[B2_POINTER] & 0x4000 )
@ -872,8 +878,10 @@ void smc91c9x_device::write(offs_t offset, u16 data, u16 mem_mask)
if (offset != B0_BANK && offset < sizeof(m_reg))
LOG("%s:smc91c9x_w(%s) = [%04X]<-%04X & (%04X & %04X)\n", machine().describe_context(), ethernet_regname[offset], offset, data, mem_mask , m_regmask[offset]);
const uint16_t old_reg = m_reg[offset];
mem_mask &= m_regmask[offset];
COMBINE_DATA(&m_reg[offset]);
const uint16_t new_reg = m_reg[offset];
/* handle it */
switch (offset)
@ -911,7 +919,7 @@ void smc91c9x_device::write(offs_t offset, u16 data, u16 mem_mask)
reset();
}
if ( !(data & RXEN) )
if ( (old_reg & RXEN) && !(new_reg & RXEN))
{
reset_completed_rx();
}
@ -921,6 +929,11 @@ void smc91c9x_device::write(offs_t offset, u16 data, u16 mem_mask)
m_reg[B0_EPH_STATUS] |= LINK_OK;
}
if ((old_reg ^ new_reg) & PRMS)
{
set_promisc(new_reg & PRMS);
}
if (VERBOSE & LOG_GENERAL)
{
if (data & SOFT_RST) LOG(" SOFT RST\n");
@ -951,14 +964,16 @@ void smc91c9x_device::write(offs_t offset, u16 data, u16 mem_mask)
break;
case B1_IA4_5:
set_promisc(m_reg[B0_RCR] & PRMS);
set_mac((char *)&m_reg[B1_IA0_1]);
if ( ACCESSING_BITS_8_15 )
{
set_promisc(m_reg[B0_RCR] & PRMS);
set_mac((char *)&m_reg[B1_IA0_1]);
}
break;
case B1_CONTROL: /* control register */
// Clearing LE_EN clears interrupt from LINK_OK status change
if (!(data & LE_ENABLE))
if ( (old_reg & LE_ENABLE) && !(new_reg & LE_ENABLE))
{
m_reg[B2_INTERRUPT] &= ~EINT_EPH;
update_ethernet_irq();
@ -1002,7 +1017,8 @@ void smc91c9x_device::write(offs_t offset, u16 data, u16 mem_mask)
else
buffer = &m_buffer[(m_reg[B2_PNR_ARR] & 0x1f) * ETHER_BUFFER_SIZE];
buffer[addr++] = data;
if ( ACCESSING_BITS_0_7 )
buffer[addr++] = data;
if ( ACCESSING_BITS_8_15 )
buffer[addr++] = data >> 8;
if ( m_reg[B2_POINTER] & AUTO_INCR)

View File

@ -215,9 +215,9 @@ private:
PTR = 0x07ff
};
static constexpr u32 FCS_RESIDUE = 0xdebb20e3;
static constexpr unsigned ETHER_BUFFER_SIZE = 256 * 6;
static const u8 ETH_BROADCAST[];
static const u8 WMS_OUI[];
// mmu

View File

@ -175,6 +175,7 @@ MIG RAM page 2 $CE02 is the speaker/slot bitfield and $CE03 is the paddle/accele
#include "bus/a2bus/ccs7710.h"
#include "bus/a2bus/ezcgi.h"
#include "bus/a2bus/grappler.h"
#include "bus/a2bus/lancegs.h"
#include "bus/a2bus/laser128.h"
#include "bus/a2bus/mouse.h"
#include "bus/a2bus/pc_xporter.h"
@ -4700,6 +4701,7 @@ static void apple2_cards(device_slot_interface &device)
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 */
device.option_add("lancegs", A2BUS_LANCEGS); /* ///SHH SYSTEME LANceGS Card */
}
static void apple2eaux_cards(device_slot_interface &device)

View File

@ -115,6 +115,7 @@
#include "bus/a2bus/ezcgi.h"
#include "bus/a2bus/grappler.h"
//#include "bus/a2bus/hostram.h"
#include "bus/a2bus/lancegs.h"
#include "bus/a2bus/laser128.h"
#include "bus/a2bus/mouse.h"
//#include "bus/a2bus/pc_xporter.h"
@ -4788,6 +4789,7 @@ static void apple2_cards(device_slot_interface &device)
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 */
device.option_add("lancegs", A2BUS_LANCEGS); /* ///SHH SYSTEME LANceGS Card */
}
void apple2gs_state::apple2gs(machine_config &config)