summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/pc98_cbus/lgy98.cpp
blob: 8ef1ad0a18f12e9856ee5b177e8f208ebc7bebce (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// license:BSD-3-Clause
// copyright-holders: Angelo Salese
/**************************************************************************************************

Melco/Buffalo LGY-98 NIC (NE2000 based)

LANMAN.DOS setup described here:
https://pokug.net/entry/2022/01/22/221536

TODO:
- Has no dipswitches on board, the port and INT lines are PnP configured thru LGYSETUP.
  Once LANMAN is setup it will try $00d0 base even if EEPROM says otherwise at CONFIG.SYS boot,
  and even if PROTOCOL.INI is explicitly  $10d0, definitely needs a BIOS.
  wpset 0xa294,2,w,wpdata==0xd0
- Wants CR bit 5 high for enough time on DMA transfers at CONFIG.SYS loading
- Untested beyond this.

**************************************************************************************************/

#include "emu.h"
#include "lgy98.h"

#include "multibyte.h"

DEFINE_DEVICE_TYPE(LGY98, lgy98_device, "lgy98", "Melco LGY-98 NIC interface")

lgy98_device::lgy98_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, LGY98, tag, owner, clock)
	, device_memory_interface(mconfig, *this)
	, device_pc98_cbus_slot_interface(mconfig, *this)
	, m_nic(*this, "nic")
	, m_eeprom(*this, "eeprom")
	, m_space_mem_config("internal_memory", ENDIANNESS_LITTLE, 8, 15, 0, address_map_constructor(FUNC(lgy98_device::memory_map), this))
{
}

void lgy98_device::device_add_mconfig(machine_config &config)
{
	// MX98902A, "8390 with built-in 10BaseT transceiver"
	DP8390D(config, m_nic, 20'000'000);
	m_nic->irq_callback().set([this](int state) { m_bus->int_w(0, state); });
	m_nic->mem_read_callback().set([this] (offs_t offset) { return space(0).read_byte(offset); });
	m_nic->mem_write_callback().set([this] (offs_t offset, u8 data) { space(0).write_byte(offset, data); });

	// ATMEL829 93C46 PC
	EEPROM_93C46_16BIT(config, m_eeprom);

	// unpopulated 2x 27C256 sockets
}

void lgy98_device::device_start()
{
	// NOTE: identical to isa/ne2000.cpp
	uint8_t mac[6];
	uint32_t num = machine().rand();
	memset(m_prom, 0x57, 16);
	mac[2] = 0x1b;
	put_u24be(mac+3, num);
	mac[0] = 0; mac[1] = 0;  // avoid gcc warning
	memcpy(m_prom, mac, 6);
	m_nic->set_mac(mac);
}

void lgy98_device::device_reset()
{
	// TODO: this clears the PROM (?)
	//memcpy(m_prom, &m_nic->get_mac()[0], 6);
}

device_memory_interface::space_config_vector lgy98_device::memory_space_config() const
{
	return space_config_vector{
		std::make_pair(0, &m_space_mem_config)
	};
}

ROM_START( lgy98 )
	// either 0x2000 or 0x4000 in size, internal to the Buffalo custom?
	ROM_REGION16_LE( 0x2000, "bios", ROMREGION_ERASEFF )
	ROM_LOAD("bios.bin", 0x0000, 0x2000, NO_DUMP )
ROM_END

const tiny_rom_entry *lgy98_device::device_rom_region() const
{
	return ROM_NAME( lgy98 );
}


void lgy98_device::remap(int space_id, offs_t start, offs_t end)
{
	// TODO: seems to employ an (undumped) ROM socket too
	if (space_id == AS_IO)
	{
		m_bus->install_device(0x0000, 0xffff, *this, &lgy98_device::io_map);
	}
}

void lgy98_device::io_map(address_map &map)
{
	// 0*d* ~ 7*d*
	// TODO: force to $00d0 for now
	const u16 base_io = 0x00d0;

	map(base_io, base_io + 0xf).rw(m_nic, FUNC(dp8390_device::cs_read), FUNC(dp8390_device::cs_write));

	// TODO: +0x18 reset (on reads?)

	map(base_io + 0x200, base_io + 0x201).rw(m_nic, FUNC(dp8390_device::remote_read), FUNC(dp8390_device::remote_write));

	// identification flags
	map(base_io + 0x30a, base_io + 0x30a).lr8(NAME([] () { return 0x00; }));
	map(base_io + 0x30b, base_io + 0x30b).lr8(NAME([] () { return 0x40; }));
	map(base_io + 0x30c, base_io + 0x30c).lr8(NAME([] () { return 0x26; }));
	map(base_io + 0x30d, base_io + 0x30d).lrw8(
		NAME([this] () {
			// TODO: bit 5 required low during ID sequence
			return 0x0a | (m_eeprom->do_read());
		}),
		NAME([this] (offs_t offset, u8 data) {
			// TODO: other bits
			m_eeprom->di_write(BIT(data, 1));
			m_eeprom->cs_write(BIT(data, 2));
			m_eeprom->clk_write(BIT(data, 3));
		})
	);
}

// NOTE: again identical to isa/ne2000.cpp
void lgy98_device::memory_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x001f).lr8(NAME([this] (offs_t offset) { return m_prom[offset >> 1]; }));
	map(0x4000, 0x7fff).ram();
}