|
|
nono 1.7.0
//
// nono
// Copyright (C) 2023 nono project
// Licensed under nono-license.txt
//
//
// Nereid イーサネット (RTL8019AS)
//
// ローカル側のアドレス空間は以下の配置のようだ。
//
// 0000-3fff: PROM (32 バイトでミラー)
// 4000-7fff: バッファ RAM (16KB)
// 8000-bfff: バスか何かの都合で多少再現性のある不定データが読めるようだ
// c000-ffff: バッファ RAM (16KB) のミラーのようだ
// PROM 領域(32バイト)をバイトで読み出すと以下のように読める。
// 0000: 00 00 50 50 C2 C2 10 10 YY YY ZZ ZZ FF FF FF FF
// 0010: FF FF FF FF FF FF FF FF FF FF FF FF 57 57 57 57
//
// 同領域をワードで読み出すと以下のように読める。
// 0000: 00FF 50FF C2FF 10FF YYFF ZZFF FFFF FFFF
// 0010: FFFF FFFF FFFF FFFF FFFF FFFF 57FF 57FF
//
// MAC アドレスは 00:50:C2:10:YY:ZZ。
// $001c あたりは 'B'($42) か 'W'($57) で、バイトかワードかを示すらしい。
// メモ。電源オン時のレジスタ値ダンプ:
// (1回目)
// Page0: 21 00 FF 9F 13 00 00 EF FF FF 50 70 16 00 00 00
// Page1: 61 00 05 02 02 10 64 F7 01 B0 91 20 01 20 60 02
// Page2: A1 80 00 FF 00 FF FF FF FF FF FF FF D1 E0 C6 80
// Page3: E1 30 00 00 90 00 10 FF 00 FE FF FD FF FE FF FF
// (2回目)
// Page0: 21 00 FF BF 13 00 00 EF FF FF 50 70 16 00 00 00
// Page1: 61 00 01 00 02 00 44 F7 01 B0 91 20 00 20 20 02
// Page2: A1 80 00 FF 00 FF FF FF FF FF FF FF D1 E0 86 80
// Page3: E1 30 00 00 90 00 10 FF 00 FE FF FD FF FE FF FF
#include "rtl8019as.h"
#include "event.h"
#include "interrupt.h"
#include "monitor.h"
#include "scheduler.h"
// コンストラクタ
RTL8019ASDevice::RTL8019ASDevice(uint n, int vector_)
: inherited(OBJ_ETHERNET(n))
{
SetName(string_format("RTL8019AS#%u", n));
ClearAlias();
AddAlias(string_format("RTL8019AS%u", n));
AddAlias(string_format("Net%u", n));
intr_vector = vector_;
monitor = gMonitorManager->Regist(ID_MONITOR_RTL8019AS(n), this);
monitor->SetCallback(&RTL8019ASDevice::MonitorScreen);
monitor->SetSize(71, 19);
}
// デストラクタ
RTL8019ASDevice::~RTL8019ASDevice()
{
}
// 初期化
bool
RTL8019ASDevice::Init()
{
uint n = GetId() - OBJ_ETHERNET0;
hostnet->SetPortName(string_format("Nereid#%u", n));
// MAC アドレスを取得
MacAddr macaddr;
if (inherited::GetConfigMacAddr(n, &macaddr, false) == false) {
// エラーメッセージ表示済み
return false;
}
// PROM 領域 (実質16バイト) を作成
memset(&prom[0], 0xff, prom.size());
macaddr.ExportTo(&prom[0]);
prom[0xe] = 'W';
prom[0xf] = 'W';
interrupt = GetInterruptDevice();
// ホストからの受信コールバックを登録
scheduler->ConnectMessage(MessageID::HOSTNET_RX(n), this,
ToMessageCallback(&RTL8019ASDevice::RxMessage));
auto evman = GetEventManager();
tx_event = evman->Regist(this,
ToEventCallback(&RTL8019ASDevice::TXEvent),
string_format("RTL8019AS%u TX", n));
rx_event = evman->Regist(this,
ToEventCallback(&RTL8019ASDevice::RXEvent),
string_format("RTL8019AS%u RX", n));
txled_event = evman->Regist(this,
ToEventCallback(&RTL8019ASDevice::TXLedEvent),
string_format("RTL8019AS%u TX LED", n));
rxled_event = evman->Regist(this,
ToEventCallback(&RTL8019ASDevice::RXLedEvent),
string_format("RTL8019AS%u RX LED", n));
return true;
}
// リセット
void
RTL8019ASDevice::ResetHard(bool poweron)
{
if (poweron) {
// 電源オン時の不定値は適当。ハードリセットの影響を受けない。
// いずれにしても極力ゼロじゃない値にしておく。
local_addr = 0xff00;
boundary_addr = 0xcf00;
current_page_addr = 0xfc00;
remote_addr = 0xffff;
// TSR/RSR は初回送信/受信時まで不定 (DP83901A.pdf)
tsr = 0xcc;
rsr = 0xcc;
// PSTART は電源オンでだいたいこれが読める?
// ハードリセットの影響を受けない。
pstart_addr = 0x8000;
// RCR は電源オン時だいたい $d1 な気がする
monitor_mode = false;
promisc_mode = true;
accept_mcast = false;
accept_bcast = false;
accept_short = false;
accept_error = true;
// TCR は電源オン時だいたい $e0 な気がする
tcr = 0x00;
// DCR は電源オン時 $86 になることが多い気がする
dcr = RTL8019::DCR_BOS;
// XXX カウンタがリセットでどうなるかは未調査
for (auto& c : error_counter) {
c = 0;
}
}
// 処理中は全部破棄
scheduler->StopEvent(tx_event);
scheduler->StopEvent(rx_event);
tx_in_progress = false;
rx_packet.Clear();
// LED は電源オンで点灯。リセットでも点灯に戻りそうな気はする。
tx_led = true;
rx_led = true;
scheduler->StopEvent(txled_event);
scheduler->StopEvent(rxled_event);
// ハードリセットで、TPSR, RCR は変化しない。
// CR は初期化される。
pagesel = 0;
dmamode = DMAMode::Complete;
// PSTOP は 0 になるように見える。
pstop_addr = 0x0000;
// TCR.LB は 0 (Normal Operation) になる。それ以外は変化しない。
tcr &= ~RTL8019::TCR_LB_MASK;
// DCR は LAS=1 になる。それ以外は変化しない。
dcr |= RTL8019::DCR_LAS;
// ISR は、電源オン後にクリアしろと書いてあるのでおそらく不定。
// 実際 $ef が読めがち。
intr_stat = 0xef;
intr_mask = 0x00;
ChangeState(State::Stop);
// Page3 の RTL8019AS 部分はだいたいリセットされそう?
config_write_enable = false;
verid = 0;
irq_enable = true;
brom_force_disable = false;
// XXX 初期値は?
mediatype = RTL8019::MediaType_10BaseT_Auto;
ChangeInterrupt();
}
// リセットポートによるソフトリセット
void
RTL8019ASDevice::Reset()
{
// リセットといっても実質ほぼ Stop じゃないかと思う。
// 実際ざっと試した限りレジスタの値は変化しなかった。
ChangeState(State::Stop);
}
/*static*/ inline uint32
RTL8019ASDevice::Decoder(uint32 addr)
{
return addr & 0x3f;
}
busdata
RTL8019ASDevice::Read(busaddr addr)
{
busdata data;
uint32 paddr = addr.Addr();
uint32 reqsize = addr.GetSize();
uint32 datasize = std::min(2 - (paddr & 1), reqsize);
uint32 offset = Decoder(paddr) >> 1;
if (offset < 0x10) {
// レジスタ部は、上位にレジスタ、下位は $ff が読める。
// ワードで読んでも上位にレジスタ、下位は $ff なので、
// レジスタと $ff のバイト配置とも言えるかも。
if ((paddr & 1) == 0) {
uint32 rn = pagesel + offset;
data = ReadReg(rn);
} else {
data = 0xff;
}
data |= BusData::Size1;
} else if (offset < 0x18) {
// DMA 部。
if (datasize == 1) {
data = ReadDMA8(paddr & 1);
data |= BusData::Size1;
} else {
data = ReadDMA16();
data |= BusData::Size2;
}
} else {
// リセットポート
putlog(1, "Reset");
Reset();
data = 0xffff >> ((2 - datasize) * 8);
data |= busdata::Size(datasize);
}
return data;
}
busdata
RTL8019ASDevice::Write(busaddr addr, uint32 data)
{
busdata r;
uint32 paddr = addr.Addr();
uint32 reqsize = addr.GetSize();
uint32 datasize = std::min(2 - (paddr & 1), reqsize);
uint32 offset = Decoder(paddr) >> 1;
data >>= (reqsize - datasize) * 8;
if (offset < 0x10) {
// レジスタ部は、上位がレジスタ、下位は無視される(バスエラーではない)。
// ワードで書いても、上位がレジスタ、下位は無視されるので、
// レジスタと N.C. のバイト配置とも言えるかも。
if ((paddr & 1) == 0) {
uint32 rn = pagesel + offset;
WriteReg(rn, data);
} else {
}
r |= BusData::Size1;
} else if (offset < 0x18) {
// DMA 部。
if (datasize == 1) {
WriteDMA8(data);
r |= BusData::Size1;
} else {
WriteDMA16(data);
r |= BusData::Size2;
}
} else {
putlog(1, "Reset");
Reset();
r |= busdata::Size(datasize);
}
return r;
}
busdata
RTL8019ASDevice::Peek1(uint32 addr)
{
uint32 offset = Decoder(addr);
if (offset < 0x10 * 2) {
if (offset % 2 == 0) {
uint32 n = pagesel + offset / 2;
return PeekReg(n);
} else {
return 0xff;
}
} else if (offset < 0x18 * 2) {
return PeekDMA(offset & 1);
} else {
// XXX ?
return 0xff;
}
}
void
RTL8019ASDevice::MonitorScreen(Monitor *, TextScreen& screen)
{
uint32 val;
const int x = 6;
const int x2 = 59;
int y;
screen.Clear();
y = 0;
// 0 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890123456789
// CFG0 $xx 12345 12345 12345 12345 12345 12345 12345 12345
val = PeekCR();
screen.Puts(0, y, "CR:");
screen.Print(x, y, "$%02x", val);
static const char * const cr_bits[] = {
"", "", "", "", "", "TXP", "STA", "STP"
};
MonitorReg(screen, x + 4, y, val, cr_bits);
screen.Print(x + 4, y, "PageSel=%x", val >> 6);
uint rd = (val >> 3) & 7;
screen.Print(x + 16, y, "RD=%x(%s)", rd, GetDMAName(rd));
y++;
val = PeekISR();
screen.Puts(0, y, "ISR:");
screen.Print(x, y, "$%02x", val);
static const char * const isr_bits[] = {
"RST", "RDC", "CNT", "OVW", "TXE", "RXE", "PTX", "PRX"
};
MonitorReg(screen, x + 4, y, val, isr_bits);
y++;
val = PeekIMR();
screen.Puts(0, y, "IMR:");
screen.Print(x, y, "$%02x", val);
static const char * const imr_bits[] = {
"-1", "RDCE", "CNTE", "OVWE", "TXEE", "RXEE", "PTXE", "PRXE"
};
MonitorReg(screen, x + 4, y, val, imr_bits);
y++;
val = PeekDCR();
screen.Puts(0, y, "DCR:");
screen.Print(x, y, "$%02x", val);
static const char * const dcr_bits[] = {
"-1", "FT1", "FT0", "ARM", "LS", "LAS", "(BOS)", "WTS"
};
MonitorReg(screen, x + 4, y, val, dcr_bits);
y++;
val = PeekTCR();
screen.Puts(0, y, "TCR:");
screen.Print(x, y, "$%02x", val);
static const char * const tcr_bits[] = {
"-1", "-1", "-1", "OFST", "ATD", "LB1", "LB0", "CRC"
};
MonitorReg(screen, x + 4, y, val, tcr_bits);
y++;
val = PeekTSR();
screen.Puts(0, y, "TSR:");
screen.Print(x, y, "$%02x", val);
static const char * const tsr_bits[] = {
"OWC", "CDH", "-0", "CRS", "ABT", "COL", "-1", "PTX"
};
MonitorReg(screen, x + 4, y, val, tsr_bits);
y++;
val = PeekRCR();
screen.Puts(0, y, "RCR:");
screen.Print(x, y, "$%02x", val);
static const char * const rcr_bits[] = {
"-1", "-1", "MON", "PRO", "AM", "AB", "AR", "SEP"
};
MonitorReg(screen, x + 4, y, val, rcr_bits);
y++;
val = PeekRSR();
screen.Puts(0, y, "RSR:");
screen.Print(x, y, "$%02x", val);
static const char * const rsr_bits[] = {
"DFR", "DIS", "PHY", "MPA", "-0", "FAE", "CRC", "PRX"
};
MonitorReg(screen, x + 4, y, val, rsr_bits);
y++;
// MAC アドレスは PAR0 が左側 (ビッグエンディアンぽい感じ)。
screen.Print(0, y, "PAR0-5: %s", par.ToString(':').c_str());
// マルチキャストフィルタは MAR0 が右側 (リトルエンディアンぽい感じ)。
// MAR7-0: $00000000'00000000
screen.Print(x + 23, y, "MAR7-0: $%08x'%08x",
(uint32)(mar >> 32), (uint32)mar);
y++;
y++;
val = Peek9346CR();
screen.Puts(0, y, "9346:");
screen.Print(x, y, "$%02x", val);
static const char * const cr9346_bits[] = {
"EEM1", "EEM0", "-1", "-1", "EECS", "EESK", "EEDI", "EEDO"
};
MonitorReg(screen, x + 4, y, val, cr9346_bits);
uint eem = val >> 6;
y++;
val = PeekCONFIG0();
screen.Puts(0, y, "CFG0:");
screen.Print(x, y, "$%02x", val);
static const char * const config0_bits[] = {
"VID1", "VID0", "AUI", "PNPJP", "JP", "BNC", "-0", "-0"
};
MonitorReg(screen, x + 4, y, val, config0_bits);
y++;
val = PeekCONFIG1();
screen.Puts(0, y, "CFG1:");
screen.Print(x, y, "$%02x", val);
static const char * const config1_bits[] = {
"IRQEN", "IRQS2", "IRQS1", "IRQS0", "IOS3", "IOS2", "IOS1", "IOS0"
};
MonitorReg(screen, x + 4, y, val, config1_bits);
y++;
val = PeekCONFIG2();
screen.Puts(0, y, "CFG2:");
screen.Print(x, y, "$%02x", val);
static const char * const config2_bits[] = {
"PL1", "PL0", "BSELB", "BS4", "BS3", "BS2", "BS1", "BS0"
};
MonitorReg(screen, x + 4, y, val, config2_bits);
y++;
val = PeekCONFIG3();
screen.Puts(0, y, "CFG3:");
screen.Print(x, y, "$%02x", val);
static const char * const config3_bits[] = {
"PNP", "FUDUP", "LEDS1", "LEDS0", "-0", "SLEEP", "PWRDN", "ACTB"
};
MonitorReg(screen, x + 4, y, val, config3_bits);
y++;
val = PeekCONFIG4();
screen.Puts(0, y, "CFG4:");
screen.Print(x, y, "$%02x", val);
static const char * const config4_bits[] = {
"-1", "-1", "-1", "-1", "-1", "-1", "-1", "IOMS"
};
MonitorReg(screen, x + 4, y, val, config4_bits);
y++;
// x+ 1 2 3 4
// 01234567890123456789012345678901234567890123456789
// EEM=0(AutoLoad) Ver=3(8019A/AS) PL=0(10baseTAuto)
static const char * const eem_str[] = {
"Normal",
"AutoLoad",
"Program",
"WriteEn",
};
screen.Print(x, y, "EEM=%u(%s)", eem, eem_str[eem]);
static const char * const verid_str[] = {
"8019A/AS",
"?",
"?",
"8019",
};
screen.Print(x + 16, y, "VID=%u(%s)", verid, verid_str[verid]);
static const char * const mediatype_str[] = {
"10baseTAuto",
"10baseT",
"10base5",
"10base2",
};
screen.Print(x + 33, y, "PL=%u(%s)", mediatype, mediatype_str[mediatype]);
y++;
// LED は、RTL8019AS の LED ピンではなく Nereid ボード上の LED で表す。
// Nereid ボードを上 (実装面側) から見るとこのような位置関係と結線に
// なっているため(素子は省略)、RTL8019AS の LED0,1,2 (LINK, RX, TX) と
// Nereid 基板上の LE1,2,3 (LINK, TX, RX) は並び順が違うことに注意。
//
// LINK TX RX
// (Orange) (Red) (Green)
// +------+
// +------[*]---| RJ45 |---[*]-[*]---------[USB]---+
// | LE1 | +------+ LE2| |LE3 |
// | | | |
// | +----------------|---|---+
// | | | |
// | +------+---+---+----+
// | RTL8019AS | 63 62 61 |
// | | LED2 LED1 LED0 |
// | | (TX) (RX) (LINK) |
//
y++;
screen.Puts(0, y, "LED:");
screen.Puts(x, y, TA::ReverseOrange, "LED1");
screen.Puts(x + 4, y, "(LINK)");
screen.Puts(x + 11, y, tx_led ? TA::ReverseRed : TA::Off, "LED2");
screen.Puts(x + 15, y, "(TX)");
screen.Puts(x + 20, y, rx_led ? TA::ReverseGreen : TA::Off, "LED3");
screen.Puts(x + 24, y, "(RX)");
// 右カラム
y = 0;
screen.Puts(x2, y++, "RSAR");
screen.Print(x2 - 1, y++, "(CRDA) =$%04x", PeekCRDA());
screen.Print(x2, y++, "RBCR =$%04x", remote_count);
y++;
//screen.Puts(x2 + 10, y, TA::Disable, "00");
screen.Print(x2, y++, "TPSR =$%02x", transmit_page_start_addr >> 8);
screen.Print(x2, y++, "TBCR =$%04x", transmit_byte_count);
y++;
//screen.Puts(x2 + 10, y, TA::Disable, "00");
screen.Print(x2, y++, "PSTART=$%02x", PeekPSTART());
//screen.Puts(x2 + 10, y, TA::Disable, "00");
screen.Print(x2, y++, "PSTOP =$%02x", PeekPSTOP());
//screen.Puts(x2 + 10, y, TA::Disable, "00");
screen.Print(x2, y++, "BNRY =$%02x", PeekBNRY());
//screen.Puts(x2 + 10, y, TA::Disable, "00");
screen.Print(x2, y++, "CURR =$%02x", PeekCURR());
y++;
screen.Print(x2, y++, "CLDA =$%04x", PeekCLDA());
y++;
screen.Print(x2, y++, "NCR = $%02x", PeekNCR());
screen.Print(x2, y++, "CNTR0 = $%02x", PeekCNTR(0));
screen.Print(x2, y++, "CNTR1 = $%02x", PeekCNTR(1));
screen.Print(x2, y++, "CNTR2 = $%02x", PeekCNTR(2));
}
// レジスタをビットごとに表示する。MonitorScreen の下請け。
// "-" で始まっていたら Disable 色にし、次の文字から表示。
void
RTL8019ASDevice::MonitorReg(TextScreen& screen,
int x, int y, uint32 reg, const char * const *names)
{
for (int i = 0; i < 8; i++) {
if (names[i][0] == '-') {
screen.Puts(x + i * 6, y, TA::Disable, names[i] + 1);
} else {
bool b = reg & (1U << (7 - i));
screen.Puts(x + i * 6, y, TA::OnOff(b), names[i]);
}
}
}
// レジスタ読み出し (rn はページ番号込み)
uint32
RTL8019ASDevice::ReadReg(uint32 rn)
{
uint32 data;
switch (rn) {
case RTL8019::CR: // CR は全ページで同じものが見える
case RTL8019::CR_1:
case RTL8019::CR_2:
case RTL8019::CR_3:
data = PeekCR();
break;
case RTL8019::CLDA0:
data = PeekCLDA() & 0xff;
break;
case RTL8019::CLDA1:
data = PeekCLDA() >> 8;
break;
case RTL8019::BNRY:
data = PeekBNRY();
break;
case RTL8019::TSR:
data = PeekTSR();
break;
case RTL8019::NCR:
data = PeekNCR();
break;
case RTL8019::FIFO:
// それっぽい値は返すけど未実装
data = PeekFIFO();
putlog(0, "Read %s -> $%02x (NOT IMPLEMENTED)",
RegNameR(rn).c_str(), data);
return data;
case RTL8019::ISR:
data = PeekISR();
break;
case RTL8019::CRDA0:
data = PeekCRDA() & 0xff;
break;
case RTL8019::CRDA1:
data = PeekCRDA() >> 8;
break;
case RTL8019::ID0:
data = Peek8019ID0();
break;
case RTL8019::ID1:
data = Peek8019ID1();
break;
case RTL8019::RSR:
data = PeekRSR();
break;
case RTL8019::CNTR0:
case RTL8019::CNTR1:
case RTL8019::CNTR2:
{
// 読み出しでリセットされる
uint n = rn - RTL8019::CNTR0;
data = PeekCNTR(n);
error_counter[n] = 0;
break;
}
case RTL8019::PAR0
... RTL8019::PAR5:
data = PeekPAR(rn - RTL8019::PAR0);
break;
case RTL8019::CURR:
data = PeekCURR();
break;
case RTL8019::MAR0
... RTL8019::MAR7:
data = PeekMAR(rn - RTL8019::MAR0);
break;
case RTL8019::PSTART_2:
data = PeekPSTART();
break;
case RTL8019::PSTOP_2:
data = PeekPSTOP();
break;
case RTL8019::TPSR_2:
data = PeekTPSR();
break;
case RTL8019::RCR_2:
data = PeekRCR();
break;
case RTL8019::TCR_2:
data = PeekTCR();
break;
case RTL8019::DCR_2:
data = PeekDCR();
break;
case RTL8019::IMR_2:
data = PeekIMR();
break;
case RTL8019::CR9346:
data = Peek9346CR();
break;
case RTL8019::BPAGE:
data = PeekBPAGE();
break;
case RTL8019::CONFIG0:
data = PeekCONFIG0();
break;
case RTL8019::CONFIG1:
data = PeekCONFIG1();
break;
case RTL8019::CONFIG2:
data = PeekCONFIG2();
break;
case RTL8019::CONFIG3:
data = PeekCONFIG3();
break;
case RTL8019::CSNSAV:
data = PeekCSNSAV();
break;
case 0x39:
data = PeekReg39();
break;
case RTL8019::INTR:
data = PeekINTR();
break;
case RTL8019::CONFIG4:
data = PeekCONFIG4();
break;
case 0x23:
case 0x25 ... 0x2b:
case 0x37:
case 0x3a:
case 0x3c:
case 0x3e:
case 0x3f:
data = 0xff;
break;
default:
putlog(0, "Read %s (NOT IMPLEMENTED)", RegNameR(rn).c_str());
return 0xff;
}
putlog(2, "%s -> $%02x", RegNameR(rn).c_str(), data);
return data;
}
// DMA ポートのバイト読み出し
uint32
RTL8019ASDevice::ReadDMA8(uint32 a0)
{
uint32 data;
// DMA モードが Read でない時に読み込むと、
// 現在の CRDA が読める (がカウンタは変化しない) ような気がする。
uint32 prev_addr = remote_addr;
if (remote_addr < 0x4000) {
// PROM 領域。
// バイトアクセスだと偶数バイト、奇数バイトは同じものが見える。
data = ReadPROM(remote_addr);
} else if ((remote_addr & 0x4000)) {
// RAM 領域。
if (word_transfer || (remote_addr & 1) == 0) {
// WTS==1 か偶数番地なら普通に読み出せる。
// たぶんワードで読み出せてるがそのうち上位 (LE でいう下位)
// だけが見えてる状態だと思う。
uint16 addr = remote_addr & 0xfffe;
data = ReadRAM(addr);
} else {
data = 0;
}
} else {
// 未実装領域。実機ではバスの残像か何かが読めているようだが。
data = 0xff;
}
PostDMA();
if (__predict_false(loglevel >= 2)) {
ReadDMAlog(prev_addr, data, 2);
}
return (uint8)data;
}
// DMA ポートのワード読み出し
uint32
RTL8019ASDevice::ReadDMA16()
{
uint32 data;
// DMA モードが Read でない時に読み込むと、
// 現在の CRDA が読める (がカウンタは変化しない) ような気がする。
// RTL8019AS は基本 LE で、Nereid 上でデータバスがクロスされてるはず
//
// 680x0 RTL8019AS
// +---------------------+ +----------------------------------+
// レジスタ データバス データバス (レジスタ) 内蔵メモリ
// +- D15-8: $86 <- - D15-8: $dd <-+ +00: $86
// $86dd <-+ × +- $dd86 <- +01: $dd
// +- D7-0 : $dd <- - D7-0 : $86 <-+
uint32 prev_addr = remote_addr;
if (remote_addr < 0x4000) {
// PROM 領域。
// ワードアクセスだと上位(偶数番地)にデータ、下位(奇数番地)は $ff。
data = ReadPROM(remote_addr) << 8;
data |= 0xff;
} else if ((remote_addr & 0x4000)) {
// RAM 領域。
// 偶数番地からのアクセスはワード値を返す。
// WTS==1 なら、奇数番地でも A0 をマスクしたようにワード値を返す。
// WTS==0 で奇数番地ならおかしなことになる (0000 とか ffff が読める?)
// ようだ。
if (word_transfer || (remote_addr & 1) == 0) {
uint16 addr = remote_addr & 0xfffe;
data = ReadRAM(addr) << 8;
data |= ReadRAM(addr + 1);
} else {
data = 0;
}
} else {
// 未実装領域。実機ではバスの残像か何かが読めているようだが。
data = 0xffff;
}
PostDMA();
if (__predict_false(loglevel >= 2)) {
ReadDMAlog(prev_addr, data, 4);
}
return (uint16)data;
}
// DMA 読み出しのログ。
// loglevel >= 3 なら、すべて表示。
// loglevel == 2 なら、受信ヘッダの読み出しと転送完了時のサマリーのみ表示。
// 受信ヘッダは、実際のバッファ上のデータが受信ヘッダかどうかではなく、
// ページ先頭から4バイトを読み出そうとしているものをヘッダの読み出しだと
// 思って表示する。ゲスト OS がヘッダだと思って読んだ(であろう)ところが
// どう読めたかが見たいので。
// この関数自体は loglevel >= 2 で呼ばれる。
void
RTL8019ASDevice::ReadDMAlog(uint32 prev_addr, uint32 data, int width)
{
// ページ先頭から 4 バイトの転送ならヘッダ読み込みだと思ってみる
bool header =
((remote_start_addr & 0x00ff) == 0) && (remote_start_count == 4);
if (loglevel >= 3 || header) {
std::string databuf = strhex(data, width);
if ((int32)data < 0) {
putlogn("DMA[$%04x] -> $%s (No Data)", prev_addr, databuf.c_str());
} else {
putlogn("DMA[$%04x] -> $%s", prev_addr, databuf.c_str());
}
}
if (__predict_false(remote_count == 0)) {
putlogn("DMA %u bytes transferred", remote_start_count);
}
}
// レジスタ書き込み (rn はページ番号込み)
void
RTL8019ASDevice::WriteReg(uint32 rn, uint32 data)
{
switch (rn) {
case RTL8019::CR: // CR は全ページで同じものが見える
case RTL8019::CR_1:
case RTL8019::CR_2:
case RTL8019::CR_3:
WriteCR(data);
break;
case RTL8019::PSTART:
WritePSTART(data);
break;
case RTL8019::PSTOP:
WritePSTOP(data);
break;
case RTL8019::BNRY:
WriteBNRY(data);
break;
case RTL8019::TPSR:
WriteTPSR(data);
break;
case RTL8019::TBCR0:
WriteTBCR0(data);
break;
case RTL8019::TBCR1:
WriteTBCR1(data);
break;
case RTL8019::ISR:
WriteISR(data);
break;
case RTL8019::RSAR0:
WriteRSAR0(data);
break;
case RTL8019::RSAR1:
WriteRSAR1(data);
break;
case RTL8019::RBCR0:
WriteRBCR0(data);
break;
case RTL8019::RBCR1:
WriteRBCR1(data);
break;
case RTL8019::RCR:
WriteRCR(data);
break;
case RTL8019::TCR:
WriteTCR(data);
break;
case RTL8019::DCR:
WriteDCR(data);
break;
case RTL8019::IMR:
WriteIMR(data);
break;
case RTL8019::PAR0
... RTL8019::PAR5:
WritePAR(rn - RTL8019::PAR0, data);
break;
case RTL8019::CURR:
WriteCURR(data);
break;
case RTL8019::MAR0
... RTL8019::MAR7:
WriteMAR(rn - RTL8019::MAR0, data);
break;
case RTL8019::CR9346:
Write9346CR(data);
break;
case RTL8019::CONFIG0:
WriteCONFIG0(data);
break;
case RTL8019::CONFIG1:
WriteCONFIG1(data);
break;
case RTL8019::CONFIG2:
WriteCONFIG2(data);
break;
case RTL8019::CONFIG3:
WriteCONFIG3(data);
break;
default:
putlog(0, "%s <- $%02x (NOT IMPLEMENTED)",
RegNameW(rn).c_str(), data);
break;
}
}
// CR レジスタ書き込み
void
RTL8019ASDevice::WriteCR(uint32 data)
{
std::string msg;
uint32 newpage;
uint32 newmode;
bool newstp;
State newstate;
// ページ
newpage = (data >> 2) & 0x30;
if (newpage != pagesel) {
pagesel = newpage;
if (loglevel >= 2) {
msg = string_format(" Page=%x", pagesel >> 4);
}
}
// DMA モード
newmode = (data >> 3) & 7;
if ((DMAMode)newmode != dmamode) {
dmamode = (DMAMode)newmode;
if (loglevel >= 2) {
msg += " DMA=";
msg += GetDMAName(newmode);
}
}
// TXP ビット
if (loglevel >= 2) {
if (tx_in_progress == false && (data & RTL8019::CR_TXP)) {
msg += " TXP";
}
}
// STP ビット
//
// 現行 STP書き込み
// Normal 0 : 何もしない
// InProg 0 : どうする?
// Stop 0 : -> Normal へ
// Normal 1 : -> InProg へ
// InProg 1 : 何もしない
// Stop 1 : 何もしない
newstp = (data & RTL8019::CR_STP);
newstate = state;
if (state == State::Normal) {
if (newstp) {
// Normal 中に STP がセットされたらストップ状態へ移行。
newstate = State::Stop;
}
} else {
if (newstp == false) {
// Stop 中に STP がクリアされたら通常状態へ移行。
// Progress もこっち?
newstate = State::Normal;
}
}
if (loglevel >= 2) {
if (newstate != state) {
msg += string_format(" STP=%u", newstp);
}
}
// 表示内容が揃ったところでログ表示
if (loglevel >= 2) {
if (msg.empty()) {
putlogn("CR <- $%02x", data);
} else {
putlogn("CR <- $%02x (%s)", data, msg.c_str() + 1);
}
}
if (dmamode == DMAMode::RemoteRead || dmamode == DMAMode::RemoteWrite) {
// ログ用に DMA 開始時の初期値を保存
remote_start_addr = remote_addr;
remote_start_count = remote_count;
} else if (__predict_false(dmamode == DMAMode::SendPacket)) {
putlog(0, "CR: SendPacket Command (NOT IMPLEMENTED)");
}
// RTL8019AS では STA ビットは何もせず状態を保持してるだけ?
cr_sta = (data & RTL8019::CR_STA);
// XXX
// NetBSD の sys/dev/ic/ne2000.c::detect_ne2000() は CR.STP を下げて、
// ISR.RST が立つまで100回チェックしている。
// RTL8019AS.pdf (DP83901A.pdf も) には STP を下げると ISR.RST もクリア
// されると書いてあるので、このループは成立しないはず。
// そして 100回試しても RST が立たなかったら検出失敗に goto するところは
// (rev1.2 の時点から) if 0 されている (そりゃそれだと動かないからね…)。
// 他の互換チップのことは分からんけど、
// STP を下げて RST が下がるのを待つ、の間違いじゃないのかなあ。
// XXX TXP と STP 同時にセットするとどうなる?
// STP を立てられた時に送受信処理中でなければすぐにリセット。
// 処理中のものがあればそれが終わり次第そっちで変更するので
// ここでは対応不要。
if (newstate != state) {
ChangeState(newstate);
}
// 送信開始
if ((data & RTL8019::CR_TXP)) {
Transmit();
}
}
// PSTART レジスタ書き込み
void
RTL8019ASDevice::WritePSTART(uint32 data)
{
putlog(2, "PSTART <- $%02x", data);
pstart_addr = data << 8;
}
// PSTOP レジスタ書き込み
void
RTL8019ASDevice::WritePSTOP(uint32 data)
{
putlog(2, "PSTOP <- $%02x", data);
pstop_addr = data << 8;
}
// BNRY レジスタ書き込み
void
RTL8019ASDevice::WriteBNRY(uint32 data)
{
putlog(2, "BNRY <- $%02x", data);
boundary_addr = data << 8;
}
// TPSR レジスタ書き込み
void
RTL8019ASDevice::WriteTPSR(uint32 data)
{
putlog(2, "TPSR <- $%02x", data);
transmit_page_start_addr = data << 8;
}
// TBCR0 レジスタ書き込み
void
RTL8019ASDevice::WriteTBCR0(uint32 data)
{
transmit_byte_count &= 0xff00;
transmit_byte_count |= data;
putlog(2, "TBCR0 <- $%02x (TBCR=$%04x)", data, transmit_byte_count);
}
// TBCR1 レジスタ書き込み
void
RTL8019ASDevice::WriteTBCR1(uint32 data)
{
transmit_byte_count &= 0x00ff;
transmit_byte_count |= data << 8;
putlog(2, "TBCR1 <- $%02x (TBCR=$%04x)", data, transmit_byte_count);
}
// ISR レジスタ書き込み
void
RTL8019ASDevice::WriteISR(uint32 data)
{
// '1' が書き込まれたビットをクリア
intr_stat &= ~data;
// bit7 (RST) は関係ないので落としておく
intr_stat &= ~0x80;
putlog(2, "ISR <- $%02x (ISR=$%02x)", data, intr_stat);
ChangeInterrupt();
}
// RSAR0 レジスタ書き込み
void
RTL8019ASDevice::WriteRSAR0(uint32 data)
{
// RSAR への書き込みは実質 CRDA への書き込みで、両者は別のレジスタでは
// なく、よくあるリードとライトで名前が違うだけのように見える。
// 実際 RSAR=$4000 から $10 バイト転送後の CRDA は $4010 になっており
// ここから RSAR を書き込まずにもう一度 RBCR=$10 転送すると $4010 から
// の $10 バイトが読める。
remote_addr &= 0xff00;
remote_addr |= data;
putlog(2, "RSAR0 <- $%02x (CRDA=$%04x)", data, remote_addr);
}
// RSAR1 レジスタ書き込み
void
RTL8019ASDevice::WriteRSAR1(uint32 data)
{
remote_addr &= 0x00ff;
remote_addr |= data << 8;
putlog(2, "RSAR1 <- $%02x (CRDA=$%04x)", data, remote_addr);
}
// RBCR0 レジスタ書き込み
void
RTL8019ASDevice::WriteRBCR0(uint32 data)
{
remote_count &= 0xff00;
remote_count |= data;
putlog(2, "RBCR0 <- $%02x (RBCR=$%04x)", data, remote_count);
}
// RBCR1 レジスタ書き込み
void
RTL8019ASDevice::WriteRBCR1(uint32 data)
{
remote_count &= 0x00ff;
remote_count |= data << 8;
putlog(2, "RBCR1 <- $%02x (RBCR=$%04x)", data, remote_count);
}
// RCR レジスタ書き込み
void
RTL8019ASDevice::WriteRCR(uint32 data)
{
putlog(2, "RCR <- $%02x", data);
monitor_mode = (data & RTL8019::RCR_MON);
promisc_mode = (data & RTL8019::RCR_PRO);
accept_mcast = (data & RTL8019::RCR_AM);
accept_bcast = (data & RTL8019::RCR_AB);
accept_short = (data & RTL8019::RCR_AR);
accept_error = (data & RTL8019::RCR_SEP);
}
// TCR レジスタ書き込み
void
RTL8019ASDevice::WriteTCR(uint32 data)
{
// OFST, ATD, LB1, CRC すべて未実装。
// ループバック(LB1:0)は 0(通常), 1(内部) のみ認めておく?
if ((data & 0x1d) != 0) {
putlog(0, "TCR <- $%02x (NOT IMPLEMENTED)", data);
} else {
putlog(2, "TCR <- $%02x", data);
}
tcr = data;
}
// DCR レジスタ書き込み
void
RTL8019ASDevice::WriteDCR(uint32 data)
{
// FT は NE2000 用なので不要らしい。
// LAS (ロングワード転送?) は RTL8019AS がサポートしてないらしい。
// BOS (バイトオーダ選択) は RTL8019AS がサポートしてない。
// ARM, LS(==0) はエミュレータ的未サポート。
std::string msg;
if ((data & RTL8019::DCR_ARM) != 0) {
msg += "ARM=1";
}
if ((data & RTL8019::DCR_LS) == 0) {
msg += "LS=0";
}
if (msg.empty() == false) {
putlog(0, "DCR <- $%02x (%s NOT IMPLEMENTED)", data, msg.c_str());
} else {
putlog(2, "DCR <- $%02x", data);
}
dcr = data & 0xfe;
// XXX WTS の設定と DMA ポートをアクセスする際のバス幅が一致してない
// ケースは未対応。
word_transfer = (data & RTL8019::DCR_WTS);
}
// IMR レジスタ書き込み
void
RTL8019ASDevice::WriteIMR(uint32 data)
{
putlog(2, "IMR <- $%02x", data);
intr_mask = data & ~0x80;
ChangeInterrupt();
}
// PAR0..5 レジスタ書き込み
void
RTL8019ASDevice::WritePAR(uint n, uint32 data)
{
par[n] = data;
if (__predict_false(loglevel >= 3)) {
putlogn("PAR%u <- $%02x", n, data);
} else {
if (__predict_false(n == 5)) {
putlog(2, "PAR <- %02x:%02x:%02x:%02x:%02x:%02x",
par[0], par[1], par[2], par[3], par[4], par[5]);
}
}
}
// CURR レジスタ書き込み
void
RTL8019ASDevice::WriteCURR(uint32 data)
{
putlog(2, "CURR <- $%02x", data);
current_page_addr = data << 8;
local_addr = current_page_addr;
}
// MAR0..7 レジスタ書き込み
void
RTL8019ASDevice::WriteMAR(uint n, uint32 data)
{
mar &= ~(0xffULL << (n * 8));
mar |= (uint64)data << (n * 8);
if (__predict_false(loglevel >= 3)) {
putlogn("MAR%u <- $%02x", n, data);
} else {
if (__predict_false(n == 7)) {
putlog(2, "MAR <- $%08x'%08x", (uint32)(mar >> 32), (uint32)mar);
}
}
}
// 9346CR レジスタ書き込み
void
RTL8019ASDevice::Write9346CR(uint32 data)
{
uint32 eem = (data >> 6) & 3;
if (eem == 3) {
// EEM = %11 で CONFIG1-3 への書き込みを許可。
config_write_enable = true;
} else {
config_write_enable = false;
}
if (eem == 1 || eem == 2) {
putlog(0, "9346CR <- $%02x EEM=%u (NOT IMPLEMENTED)", data, eem);
return;
}
if ((data & 0x0f) != 0) {
putlog(0, "9346CR <- $%02x (NOT IMPLEMENTED)", data);
return;
}
putlog(2, "9346CR <- $%02x", data);
}
void
RTL8019ASDevice::WriteCONFIG0(uint32 data)
{
if (config_write_enable == false) {
putlog(1, "CONFIG0 <- $%02x: Config write is disabled", data);
return;
}
// CONFIG0 は元々読み出し専用レジスタで、このうち VERID (2bit) で
// RTL8019 のモデル情報が読み出せる構造だったっぽい。
// %11 なら RTL8019
// %00 なら RTL8019A
// で、RTL8019AS ではこの VERID フィールドだけ R/W に変えて、
// 設定によって RTL8019(無印) としても振る舞えるようにした(?)っぽい。
// ほんまかいな。
verid = data >> 6;
if (verid != 0) {
putlog(2, "CONFIG0 <- $%02x VERID=%x (NOT IMPLEMENTED)", data, verid);
} else {
putlog(2, "CONFIG0 <- $%02x", data);
}
}
void
RTL8019ASDevice::WriteCONFIG1(uint32 data)
{
if (config_write_enable == false) {
putlog(1, "CONFIG1 <- $%02x: Config write is disabled", data);
return;
}
putlog(2, "CONFIG1 <- $%02x", data);
irq_enable = (data & RTL8019::CONFIG1_IRQEN);
ChangeInterrupt();
}
void
RTL8019ASDevice::WriteCONFIG2(uint32 data)
{
if (config_write_enable == false) {
putlog(1, "CONFIG2 <- $%02x: Config write is disabled", data);
return;
}
mediatype = (data >> 6);
// どのみち BS4-0 で BROM Disabled なので、BSELB の影響は受けない。
// 値だけ記憶しておく。
brom_force_disable = (data & RTL8019::CONFIG2_BSELB);
}
void
RTL8019ASDevice::WriteCONFIG3(uint32 data)
{
if (config_write_enable == false) {
putlog(1, "CONFIG3 <- $%02x: Config write is disabled", data);
return;
}
if ((data & 0x06) != 0) {
// SLEEP, PWRDN はサポートしていない
putlog(0, "CONFIG3 <- $%02x (NOT IMPLEMENTED)", data);
} else {
putlog(2, "CONFIG3 <- $%02x", data);
}
config3 = data;
}
// DMA ポートへのバイト書き込み
void
RTL8019ASDevice::WriteDMA8(uint32 data)
{
// DMA モードが Write でない時に書き込むとどうなる?
if (dmamode != DMAMode::RemoteWrite) {
putlog(0, "DMA <- $%02x but not RemoteWrite (NOT IMPLEMENTED)", data);
return;
}
uint16 prev_addr = remote_addr;
bool written;
if ((remote_addr & 0x4000)) {
// RAM 領域。
if (word_transfer || (remote_addr & 1) == 0) {
// WTS==1 か偶数番地なら書き込めそう。未確認。
uint16 addr = remote_addr & 0xfffe;
WriteRAM(addr, data);
} else {
// 未確認。
}
written = true;
} else {
// 書き込み不可。たぶん何も起きない。
written = false;
}
PostDMA();
if (__predict_false(loglevel >= 1)) {
if (written) {
putlog(3, "DMA[$%04x] <- $%02x", prev_addr, data);
} else {
putlogn("DMA[$%04x] <- $%02x (Not writable)", prev_addr, data);
}
if (__predict_false(remote_count == 0)) {
putlog(2, "DMA %u bytes transferred", remote_start_count);
}
}
}
// DMA ポートへのワード書き込み
void
RTL8019ASDevice::WriteDMA16(uint32 data)
{
// DMA モードが Write でない時に書き込むとどうなる?
if (dmamode != DMAMode::RemoteWrite) {
putlog(0, "DMA <- $%04x but not RemoteWrite (NOT IMPLEMENTED)", data);
return;
}
// RTL8019AS は基本 LE で、Nereid 上でデータバスがクロスされてるはず
//
// 680x0 RTL8019AS
// +---------------------+ +----------------------------------+
// レジスタ データバス データバス (レジスタ) 内蔵メモリ
// +-> D15-8: $86 - -> D15-8: $dd -+ +00: $86
// $86dd -+ × +-> $dd86 -> +01: $dd
// +-> D7-0 : $dd - -> D7-0 : $86 -+
uint16 prev_addr = remote_addr;
bool written;
if ((remote_addr & 0x4000)) {
// RAM 領域。
// 書き込みは確認していないが Read 側からの推測で、
// 偶数番地からのアクセスは書き込めるはず。
// WTS==1 なら奇数番地でも A0 をマスクしたように振る舞うはず。
// WTS==0 で奇数番地ならおかしなことになりそう。
if (word_transfer || (remote_addr & 1) == 0) {
uint16 addr = remote_addr & 0xfffe;
WriteRAM(addr, data >> 8);
WriteRAM(addr + 1, data & 0xff);
} else {
// XXX 何が書けるかは未調査。
}
written = true;
} else {
// 書き込み不可。たぶん何も起きない。
written = false;
}
PostDMA();
if (__predict_false(loglevel >= 1)) {
if (written) {
putlog(3, "DMA[$%04x] <- $%04x", prev_addr, data);
} else {
putlogn("DMA[$%04x] <- $%04x (Not writable)", prev_addr, data);
}
if (__predict_false(remote_count == 0)) {
putlog(2, "DMA %u bytes transferred", remote_start_count);
}
}
}
// DMA 転送後の共通処理。
// アドレスをインクリメント、カウントをデクリメント。
void
RTL8019ASDevice::PostDMA()
{
// DCR.WTS==0(バイト転送) なら1回、
// DCR.WTS==1(ワード転送) なら2回カウンタを進める。
// その都度 0 チェックは行う。
// カウンタが 0 でも、1回今の remote_addr からの読み出しは出来て、
// ただしそれ以上は進まないので、ここにカウンタのチェックがあるっぽい。
//
// 実機でも、
// 1. RSAR=$4000、RBCR=$0008 をセットして CR <- RemoteDMA
// -> $4000 から 8バイト読み出せて CRDA=$4008
// 2. CR <- RemoteDMA
// -> $4008 から 1ワード(1回)読み出せて CRDA=$4008
// 3. RBCR=$0008 をセットして CR <- RemoteDMA
// -> $4008 から 8バイト読み出せる。
// となる。
int n;
if (word_transfer) {
n = 2;
} else {
n = 1;
}
for (int i = 0; remote_count > 0 && i < n; i++) {
remote_count--;
remote_addr++;
if (__predict_false(remote_addr == pstop_addr)) {
remote_addr = pstart_addr;
}
}
if (__predict_false(remote_count == 0)) {
dmamode = DMAMode::Complete;
intr_stat |= RTL8019::ISR_RDC;
ChangeInterrupt();
}
}
uint32
RTL8019ASDevice::PeekReg(uint32 rn) const
{
switch (rn) {
case RTL8019::CR: // CR は全ページで同じものが見える
case RTL8019::CR_1:
case RTL8019::CR_2:
case RTL8019::CR_3:
return PeekCR();
case RTL8019::CLDA0:
return PeekCLDA() & 0xff;
case RTL8019::CLDA1:
return PeekCLDA() >> 8;
case RTL8019::BNRY:
return PeekBNRY();
case RTL8019::TSR:
return PeekTSR();
case RTL8019::NCR:
return PeekNCR();
case RTL8019::FIFO:
return PeekFIFO();
case RTL8019::ISR:
return PeekISR();
case RTL8019::CRDA0:
return PeekCRDA() & 0xff;
case RTL8019::CRDA1:
return PeekCRDA() >> 8;
case RTL8019::ID0:
return Peek8019ID0();
case RTL8019::ID1:
return Peek8019ID1();
case RTL8019::RSR:
return PeekRSR();
case RTL8019::CNTR0:
return PeekCNTR(0);
case RTL8019::CNTR1:
return PeekCNTR(1);
case RTL8019::CNTR2:
return PeekCNTR(2);
case RTL8019::PAR0
... RTL8019::PAR5:
return PeekPAR(rn - RTL8019::PAR0);
case RTL8019::CURR:
return PeekCURR();
case RTL8019::MAR0
... RTL8019::MAR7:
return PeekMAR(rn - RTL8019::MAR0);
case RTL8019::PSTART_2:
return PeekPSTART();
case RTL8019::PSTOP_2:
return PeekPSTOP();
case RTL8019::TPSR_2:
return PeekTPSR();
case RTL8019::RCR_2:
return PeekRCR();
case RTL8019::TCR_2:
return PeekTCR();
case RTL8019::DCR_2:
return PeekDCR();
case RTL8019::IMR_2:
return PeekIMR();
case RTL8019::CR9346:
return Peek9346CR();
case RTL8019::BPAGE:
return PeekBPAGE();
case RTL8019::CONFIG0:
return PeekCONFIG0();
case RTL8019::CONFIG1:
return PeekCONFIG1();
case RTL8019::CONFIG2:
return PeekCONFIG2();
case RTL8019::CONFIG3:
return PeekCONFIG3();
case RTL8019::CSNSAV:
return PeekCSNSAV();
case 0x39:
return PeekReg39();
case RTL8019::INTR:
return PeekINTR();
case RTL8019::CONFIG4:
return PeekCONFIG4();
}
return 0xff;
}
uint32
RTL8019ASDevice::PeekCR() const
{
uint32 data;
// PS
data = pagesel << 2;
// RD
switch (dmamode) {
case DMAMode::NotAllowed:
break;
case DMAMode::RemoteRead:
data |= RTL8019::CR_RD_READ;
break;
case DMAMode::RemoteWrite:
data |= RTL8019::CR_RD_WRITE;
break;
case DMAMode::SendPacket:
data |= RTL8019::CR_RD_SEND;
break;
case DMAMode::Complete:
data |= 0x20;
break;
}
// TXP
if (tx_in_progress) {
data |= RTL8019::CR_TXP;
}
// STA
data |= cr_sta;
// STP
if (state == State::Stop) {
data |= RTL8019::CR_STP;
}
return data;
}
uint32
RTL8019ASDevice::PeekCLDA() const
{
return local_addr;
}
uint32
RTL8019ASDevice::PeekBNRY() const
{
return boundary_addr >> 8;
}
uint32
RTL8019ASDevice::PeekTSR() const
{
uint32 data = tsr;
// bit5 は %0、bit1 は %1 らしい。
data &= ~0x20;
data |= 0x02;
return data;
}
uint32
RTL8019ASDevice::PeekNCR() const
{
// コリジョンカウンタは不要なので実装していない
return 0;
}
uint32
RTL8019ASDevice::PeekFIFO() const
{
// 未実装だが $00 が読めているようだ
return 0;
}
uint32
RTL8019ASDevice::PeekISR() const
{
uint32 data = intr_stat;
// RST は、Stop 状態とバッファフルの論理和?
if (state == State::Stop || local_addr == boundary_addr) {
data |= RTL8019::ISR_RST;
}
return data;
}
uint32
RTL8019ASDevice::PeekCRDA() const
{
return remote_addr;
}
uint32
RTL8019ASDevice::Peek8019ID0() const
{
return 0x50;
}
uint32
RTL8019ASDevice::Peek8019ID1() const
{
return 0x70;
}
uint32
RTL8019ASDevice::PeekRSR() const
{
uint32 data;
data = rsr & ~0x48;
if (monitor_mode) {
data |= RTL8019::RSR_DIS;
}
return data;
}
uint32
RTL8019ASDevice::PeekCNTR(int n) const
{
return error_counter[n];
}
uint32
RTL8019ASDevice::PeekPAR(int n) const
{
return par[n];
}
uint32
RTL8019ASDevice::PeekCURR() const
{
return current_page_addr >> 8;
}
uint32
RTL8019ASDevice::PeekMAR(int n) const
{
return (mar >> (n * 8)) & 0xff;
}
uint32
RTL8019ASDevice::PeekPSTART() const
{
return pstart_addr >> 8;
}
uint32
RTL8019ASDevice::PeekPSTOP() const
{
return pstop_addr >> 8;
}
uint32
RTL8019ASDevice::PeekTPSR() const
{
return transmit_page_start_addr >> 8;
}
uint32
RTL8019ASDevice::PeekRCR() const
{
uint32 data = 0xc0;
if (monitor_mode) data |= RTL8019::RCR_MON;
if (promisc_mode) data |= RTL8019::RCR_PRO;
if (accept_mcast) data |= RTL8019::RCR_AM;
if (accept_bcast) data |= RTL8019::RCR_AB;
if (accept_short) data |= RTL8019::RCR_AR;
if (accept_error) data |= RTL8019::RCR_SEP;
return data;
}
uint32
RTL8019ASDevice::PeekTCR() const
{
return 0xe0 | tcr;
}
uint32
RTL8019ASDevice::PeekDCR() const
{
uint32 data;
data = 0x80 | dcr;
if (word_transfer) data |= RTL8019::DCR_WTS;
return data;
}
uint32
RTL8019ASDevice::PeekIMR() const
{
return intr_mask | 0x80;
}
uint32
RTL8019ASDevice::Peek9346CR() const
{
uint32 data = 0x30;
// EEM は $0 か $3 のみサポート
if (config_write_enable) {
data |= 0xc0;
}
return data;
}
uint32
RTL8019ASDevice::PeekBPAGE() const
{
// 使わなそうだし、サポートもしていない
return 0;
}
uint32
RTL8019ASDevice::PeekCONFIG0() const
{
// XXX 値確認
return (verid << 6);
}
uint32
RTL8019ASDevice::PeekCONFIG1() const
{
uint32 data;
// IRQS=INT1、IOS=300H
data = 0x10;
if (irq_enable) data |= RTL8019::CONFIG1_IRQEN;
return data;
}
uint32
RTL8019ASDevice::PeekCONFIG2() const
{
uint32 data;
data = mediatype << 6;
if (brom_force_disable) {
data |= RTL8019::CONFIG2_BSELB;
}
// BS4-0 は 0
return data;
}
uint32
RTL8019ASDevice::PeekCONFIG3() const
{
uint32 data;
data = (config3 & 0x06);
// LEDS1=0, LEDS0=1
data |= 0x10;
return data;
}
uint32
RTL8019ASDevice::PeekCSNSAV() const
{
// サポートしていない
return 0;
}
uint32
RTL8019ASDevice::PeekReg39() const
{
// データシートには何もないと書いてあるが $fe が読めるようだ
return 0xfe;
}
uint32
RTL8019ASDevice::PeekCONFIG4() const
{
// IOMS=0
return 0xfe;
}
uint32
RTL8019ASDevice::PeekINTR() const
{
// INT1 以外は pull up?
uint32 data = 0xfd;
// 正論理でええんやろうか
if (interrupt->GetINT(this)) {
data |= 0x02;
}
return data;
}
uint32
RTL8019ASDevice::PeekDMA(uint32 a0) const
{
// DMA モードが Read でない時には何を見せる?
if (dmamode != DMAMode::RemoteRead) {
if (a0 == 0) {
return 0xcc;
} else {
return 0xdd;
}
}
// このポートは実際にはバイトアクセスかワードアクセスかによって
// (か、もしくは DCR.WTS ビットによって?)、
// 読み出せる値が変わるが、実用上はワード転送がメインだろうと思うので、
// 正しくワードアクセスされたと仮定した時の値を表示しておく。
if (remote_addr < 0x4000) {
if (a0 == 0) {
return ReadPROM(remote_addr);
} else {
return 0xff;
}
} else if ((remote_addr & 0x4000)) {
uint32 addr = remote_addr & ~1;
return ReadRAM(addr + a0);
} else {
return 0xff;
}
}
// PROM 領域をバイトで読み出す。
// addr は内蔵メモリ空間でのアドレス。
uint32
RTL8019ASDevice::ReadPROM(uint32 addr) const
{
// PROM 領域は 32 バイト(16ワード) でミラー。
uint32 offset = (addr >> 1) & 0x0f;
assert(offset < prom.size());
return prom[offset];
}
// 内蔵 RAM を (副作用なく) バイトで読み出す。
// addr は内蔵メモリ空間でのアドレス。
// 送信パケット用の読み出しにも使われる。
inline uint32
RTL8019ASDevice::ReadRAM(uint32 addr) const
{
uint32 offset = addr & 0x3fff;
assert(offset < buffer.size());
return buffer[offset];
}
// 内蔵 RAM に (副作用なく) バイトで書き込む。
// addr は内蔵メモリ空間でのアドレス。
// 受信パケットの書き込みにも使われる。
inline void
RTL8019ASDevice::WriteRAM(uint32 addr, uint32 data)
{
uint32 offset = addr & 0x3fff;
assert(offset < buffer.size());
buffer[offset] = data;
}
// 動作状態が Stop か StopInProgress なら true を返す
bool
RTL8019ASDevice::IsStopOrInProgress() const
{
return (state != State::Normal);
}
// 動作状態を newstate に変更を試みる。
// newstate には Normal か Stop を指定すること
// (その結果 StopInProgress になるかも知れない)。
void
RTL8019ASDevice::ChangeState(State newstate)
{
if (newstate == state) {
return;
}
switch (newstate) {
case State::Normal:
hostnet->EnableRx(true);
break;
case State::Stop:
if (tx_in_progress || rx_packet.Length() != 0) {
// 処理中があれば、処理が終わるのを待つ。
// (送信または受信処理完了時に Stop にする)
newstate = State::StopInProgress;
} else {
// ホストの受信を停止する
hostnet->EnableRx(false);
}
break;
default:
break;
}
state = newstate;
}
// 送信開始。
void
RTL8019ASDevice::Transmit()
{
// Stop なら送(受)信は不可。
// InProgress でも新たな送信は行わない
// (受信完了を待ってるところのかもしれないので)。
if (IsStopOrInProgress()) {
putlog(1, "Transmit not allowed due to CR.STP=1");
return;
}
// 送信開始
putlog(2, "Transmit TPSR=$%02x TBCR=$%04x",
transmit_page_start_addr >> 8, transmit_byte_count);
tx_packet.Clear();
tx_in_progress = true;
// TSR は転送開始時にクリア (DP83901A.pdf)
tsr = 0;
// 転送イベントを起こすだけ。
tx_event->time = 0;
scheduler->StartEvent(tx_event);
// LED イベントは止まっていれば動かす。
if (txled_event->IsRunning() == false) {
txled_event->time = 0;
scheduler->StartEvent(txled_event);
}
}
// 転送イベント。
// TXP=1 で呼ばれて、ページごとにパケットにコピーする。
// TBCR で指定された長さを全部コピーしたらホストに送信。
void
RTL8019ASDevice::TXEvent(Event *ev)
{
// 今回処理する分
uint count = std::min((uint)transmit_byte_count, 256u);
if (count == 0 && tx_packet.Length() != 0) {
// 前回でコピーが完了していればここで送信。
putlog(3, "TXEvent Send %u bytes", tx_packet.Length());
hostnet->Tx(tx_packet);
// 送信完了。
tx_in_progress = false;
tsr |= RTL8019::TSR_PTX;
intr_stat |= RTL8019::ISR_PTX;
ChangeInterrupt();
// ストップ待ちなら、ストップ状態に移行を試みる
if (state == State::StopInProgress) {
ChangeState(State::Stop);
}
return;
}
// このページをパケットにコピー
uint32 addr = transmit_page_start_addr;
for (uint32 end = addr + count; addr < end; addr++) {
tx_packet.Append((uint8)ReadRAM(addr));
}
putlog(4, "TXEvent Copy %u bytes at page $%02x",
count, transmit_page_start_addr >> 8);
// XXX TPSR, TBCR は更新されるのかどうか。
// とりあえず内部で必要なので使いまわしておく。
transmit_page_start_addr += 256;
transmit_byte_count -= count;
// XXX 折り返し
// 転送にかかった時間を模しておく。
// 時間は適当。10Mbps なので 1バイトあたり 1usec とする。
tx_event->time = usec_to_tsec(count);
scheduler->StartEvent(tx_event);
}
// パケット受信通知。HostNet から (EthernetDevice 経由で) 呼ばれる。
void
RTL8019ASDevice::RxMessage(MessageID msgid, uint32 arg)
{
// 受信イベントが止まっていれば動かす。
// 受信イベントがすでに動いていれば、1パケット受信完了後に
// ホストキューを確認するので、ここでは何もしなくてよい。
if (rx_event->IsRunning() == false) {
rx_event->time = 0;
scheduler->StartEvent(rx_event);
}
}
// 受信イベント。
//
// rx_packet の length, remain によって状態マシンになっている。
//
// len == 0 : 受信中でない。ホストキューから読み出せる
// len != 0, remain == len : 受信パケットのコピー (1ページ目)。ヘッダも出力
// len != 0, remain < len : 受信パケットのコピー (2ページ目以降)
// len != 0, remain == 0 : 受信パケットのコピー完了直後。
void
RTL8019ASDevice::RXEvent(Event *ev)
{
uint remain;
if (rx_packet.Length() == 0) {
// パケットが空の時に呼ばれたら、
// ホストキューから1パケット取り出してみる。
if (hostnet->Rx(&rx_packet) == false) {
// 取り出せなくなったら、ここでイベントを停止。
rx_packet.Clear();
return;
}
// 取り出したらそのまま処理する。
// ここがパケット受信処理開始なので RSR をクリア。
rsr = 0;
// 受信 LED イベントが止まっていれば動かす。
// 取り出して、破棄するかもしれないより前。
if (rxled_event->IsRunning() == false) {
rxled_event->time = 0;
scheduler->StartEvent(rxled_event);
}
// 受信無効中ならカウンタを上げてから、パケットを破棄。
if (monitor_mode) {
rsr |= RTL8019::RSR_MPA;
rsr &= ~RTL8019::RSR_PRX; // 念のため
IncCounter(2);
intr_stat |= RTL8019::ISR_RXE;
ChangeInterrupt();
goto discard;
}
}
assert(rx_packet.Length() != 0);
remain = rx_packet.GetRemain();
if (remain != 0) {
// 実際に書き込む前に、これから書き込むページ(local_addr) が
// BNRY (ホストがまだ読んでない受信済みページ に追いついていたら、
// 受信バッファオーバーフロー。
if (local_addr == boundary_addr) {
putlog(2, "Receive ring overflow: CURR=$%02x BNRY=$%02x",
local_addr >> 8, boundary_addr >> 8);
rsr |= RTL8019::RSR_MPA;
rsr &= ~RTL8019::RSR_PRX; // 念のため
IncCounter(2);
// RST は自動的にセットされる
intr_stat |= RTL8019::ISR_OVW | RTL8019::ISR_RXE;
ChangeInterrupt();
goto discard;
}
if (remain == rx_packet.Length()) {
// 初回処理
RXHead();
}
RXCopy(ev, remain);
} else {
RXDone(ev);
}
return;
discard:
// パケットを破棄する。
// この場合も、ホストキューから次のパケットを引き取る作業は必要。
// XXX 間隔は適当
rx_packet.Clear();
ev->time = 1_usec;
scheduler->RestartEvent(ev);
}
// パケット受信の初回。
// 4バイトの受信ヘッダをバッファに書き込む。
void
RTL8019ASDevice::RXHead()
{
// 先に、このパケットの次のページ位置を求めて覚えておく。
next_page_addr = local_addr + 4 + rx_packet.Length();
next_page_addr = roundup(next_page_addr, 256);
if (next_page_addr >= pstop_addr) {
next_page_addr = pstart_addr + (next_page_addr - pstop_addr);
}
// 受信ヘッダは転送完了後に書き込むため、空けておく。
// XXX ゼロクリアするかどうか
local_addr += 4;
}
// 受信パケットをバッファにコピー。
// 1ページ埋めるかパケットをコピーし終えたら次へ。
// (初回もヘッダ書き込みに続いて呼ばれる)
void
RTL8019ASDevice::RXCopy(Event *ev, uint remain)
{
// パケットをこのページにコピー。
// 今回処理する分はパケットの残りとページの残りの小さいほう。
uint count = std::min(remain, 0x100U - (local_addr & 0xff));
putlog(4, "RXEvent Copy %u bytes at page $%02x", count, local_addr >> 8);
for (uint32 end = local_addr + count; local_addr < end; ) {
WriteRAM(local_addr++, rx_packet.Read());
}
// パケットの最後に到達したらポインタだけここで次ページへ。
// PSTOP 折り返し処理の前にやっておくため。
// (パケットの受信完了処理自体は次のループの先頭で行う)
if (rx_packet.GetRemain() == 0) {
local_addr = roundup(local_addr, 256);
}
// PSTOP に到達したら PSTART に折り返す。
if (local_addr >= pstop_addr) {
local_addr = pstart_addr;
}
// 転送にかかった時間を模しておく。
// 時間は適当。10Mbps なので 1バイトあたり 1usec とする。
ev->time = usec_to_tsec(count);
scheduler->RestartEvent(ev);
}
// 受信パケットをバッファにコピーし終えた次のターンで呼ばれる。
// 完了処理をして、ストップ待ちならストップ状態に移行。
// ストップ待ちでなければ再び RXEvent() イベントを呼び、
// 次パケットの処理を継続する。
void
RTL8019ASDevice::RXDone(Event *ev)
{
// 前回でコピーが完了していればここで通知。
uint length = rx_packet.Length();
putlog(3, "RXDone Received %u bytes", length);
// 受信ヘッダに書き込む前に RSR を更新
rsr |= RTL8019::RSR_PRX;
if ((rx_packet.Peek(0) & 0x01)) {
rsr |= RTL8019::RSR_PHY;
}
// 念のためエラービットをクリア (DP83901A.pdf)
rsr &= ~(RTL8019::RSR_MPA |
RTL8019::RSR_FAE |
RTL8019::RSR_CRC);
// XXX Receive Status には、正常受信時は RSR を書き込む。
// エラー時はまだ見てない (DP83901A.pdf)
// 受信ヘッダを書き込む。
// このパケット受信中は CURR は更新しないので、これが使える。
//
// メモリ上のバイト順で次の通り。
// +0: Receive Status
// +1: 次の受信ページ
// +2: 受信バイト長(L)
// +3: 受信バイト長(H)
//
// ワード (Little Endian) 表記:
// D15 D8 D7 D0
// +0: 次の受信ページ Receive Status
// +2: 受信バイト長(H) 受信バイト長(L)
uint16 addr = current_page_addr;
WriteRAM(addr++, rsr);
WriteRAM(addr++, next_page_addr >> 8);
WriteRAM(addr++, length & 0xff);
WriteRAM(addr++, length >> 8);
// CURR を次受信ページに更新。残り転送バイトが 0 になった時点で
// local_addr は次ページ先頭に繰り上げてある。
current_page_addr = local_addr;
rx_packet.Clear();
// 受信完了。
intr_stat |= RTL8019::ISR_PRX;
ChangeInterrupt();
if (state == State::StopInProgress) {
// ストップ待ちなら、ストップ状態に移行を試みる
ChangeState(State::Stop);
} else {
// そうでなければ、次の受信を試す。
// (rx_packet.Length() == 0 で RXEvent() を呼ぶとパケット受信となる)
// どのくらいか待つ? 値は適当
ev->time = 100_usec;
scheduler->RestartEvent(ev);
}
}
// この宛先アドレスを受信するかどうか。
// これは HostNet スレッドで呼ばれる。
int
RTL8019ASDevice::HWAddrFilter(const MacAddr& dstaddr) const
{
// RTL8019AS.pdf では詳細不明だが、
// DP83901A.pdf には、完全なプロミスキャスにするには AB, AM, PRO 全部
// 立ててさらにマルチキャストハッシュを全部 1 にしろ、と注釈があるので、
// - PRO は自分宛て以外のユニキャストを受け取るかどうか、
// - AM は、ハッシュで accept になったマルチキャストを受け取るか
// マルチキャストを一切受け取らないか、
// - AB はブロードキャストを受け取るかどうか、
// の独立したビットのようだ。
if (dstaddr.IsUnicast()) {
if (promisc_mode == false) {
if (dstaddr != par) {
return HPF_DROP_UNICAST;
}
}
} else {
if (dstaddr.IsBroadcast()) {
if (accept_bcast == false) {
// XXX 選択肢がまだない
return HPF_DROP_MULTICAST;
}
} else {
if (accept_mcast == false) {
return HPF_DROP_MULTICAST;
}
// マルチキャスト
//
// RTL8019AS のマルチキャストフィルタ(ハッシュ) は宛先 MAC
// アドレスの CRC (32ビット) のうち上位6ビットを pos として、
// MAR の下から pos ビット目が立っていれば受理する、というもの。
//
// 31 30 29 28 27 26 25 24 0
// +---+---+---+---+---+---+---+---+-- --+
// CRC32(dstaddr) = | p5| p4| p3| p2| p1| p0| X X | .. |
// +---+---+---+---+---+---+---+---+-- --+
//
// +---+---+---+---+---+---+
// pos = | p5| p4| p3| p2| p1| p0|
// +---+---+---+---+---+---+
uint32 crc = EthernetDevice::CRC32(dstaddr);
uint pos = crc >> 26;
bool mcast_pass = (mar & (1ULL << pos));
putmsg(2, "mcast %02x:%02x:%02x:%02x:%02x:%02x pos=%u %s",
dstaddr[0], dstaddr[1], dstaddr[2],
dstaddr[3], dstaddr[4], dstaddr[5],
pos, (mcast_pass ? "pass" : "drop"));
if (mcast_pass == false) {
return HPF_DROP_MULTICAST;
}
}
}
return HPF_PASS;
}
// 送信 LED イベント
void
RTL8019ASDevice::TXLedEvent(Event *ev)
{
if (tx_led == false) {
// 消灯中だったら、次は 6msec 点灯
tx_led = true;
ev->time = 6_msec;
scheduler->RestartEvent(ev);
} else {
// 点灯中だったら
// - 送信中なら次は 100msec 消灯
// - 送信中でなければここで終わり
if (tx_in_progress) {
tx_led = false;
ev->time = 100_msec;
scheduler->RestartEvent(ev);
}
}
}
// 受信 LED イベント
void
RTL8019ASDevice::RXLedEvent(Event *ev)
{
if (rx_led == false) {
// 消灯中だったら、次は 6msec 点灯
rx_led = true;
ev->time = 6_msec;
scheduler->RestartEvent(ev);
} else {
// 点灯中だったら
// - 受信中なら次は 100msec 消灯
// - 受信中でなければここで終わり
if (rx_packet.Length() != 0) {
rx_led = false;
ev->time = 100_msec;
scheduler->RestartEvent(ev);
}
}
}
// 実際のボードの割り込みは、SL811HS/T もいるので NereidBoard が一旦受けて
// いると思うが、それを忠実に真似すると
// RTL8019AS -> NereidBoard -> X68030Interrupt のようなカスケードになり、
// それはちょっと手間なので、ここでは NereidBoard の DIPSW で決まる割り込み
// ベクタの値だけ NereidBoard から事前に受け取っておいて、あとは
// InterruptDevice と直接やりとりする。
// 割り込み信号線の状態を変える。
// intr_stat、intr_mask、irq_enable が更新されたら呼ぶこと。
void
RTL8019ASDevice::ChangeInterrupt()
{
if (irq_enable) {
bool irq = ((intr_stat & intr_mask) != 0);
interrupt->ChangeINT(this, irq);
} else {
// IRQEN が 0 だと出力がハイインピーダンスになる。
interrupt->AssertINT(this);
}
}
// 割り込みアクノリッジ
busdata
RTL8019ASDevice::InterruptAcknowledge()
{
return intr_vector;
}
// エラーカウンタをインクリメントする。
void
RTL8019ASDevice::IncCounter(int n)
{
error_counter[n]++;
// XXX DP83901A はカウンタが $c0 になったらカウントとめるらしい。
// RTL8019AS/AX88196B には記述なし。要動作確認。
// いずれかカウンタの MSB が立ったら割り込み
uint32 c = error_counter[0] | error_counter[1] | error_counter[2];
if ((c & 0x80) != 0) {
intr_stat |= RTL8019::ISR_CNT;
} else {
// 書いてはないけど、たぶん OR したのが繋がってるだけだとしたら
// 最上位ビットがいずれも立たなくなったら、CNT も勝手に下がるはず?
intr_stat &= ~RTL8019::ISR_CNT;
}
ChangeInterrupt();
}
// 読み込み時のレジスタ名を返す (rn はページ番号込み)
/*static*/ std::string
RTL8019ASDevice::RegNameR(int rn)
{
if (regnames[rn]) {
return std::string(regnames[rn]);
} else {
return string_format("(%02x)", rn);
}
}
// 書き込み時のレジスタ名を返す (rn はページ番号込み)
/*static*/ std::string
RTL8019ASDevice::RegNameW(int rn)
{
int page = rn >> 4;
int n = rn & 0xf;
// Page1 は R/W 共通
if (page == 1) {
return RegNameR(rn);
}
// Page2 は ReadOnly レジスタなので名前はない。
if (page == 2) {
return string_format("%02x", rn);
}
// Page0, Page3 のレジスタ名はオフセットしたところに置いてある。
int offset = (page == 0) ? 0x40 : 0x50;
if (regnames[offset + n]) {
return std::string(regnames[offset + n]);
} else {
return string_format("(%02x)", rn);
}
}
/*static*/ const char * const
RTL8019ASDevice::regnames[] = {
"CR", // 0 Page0(Read側)
"CLDA0", // 1
"CLDA1", // 2
"BNRY", // 3
"TSR", // 4
"NCR", // 5
"FIFO", // 6
"ISR", // 7
"CRDA0", // 8
"CRDA1", // 9
"8019ID0", // a
"8019ID1", // b
"RSR", // c
"CNTR0", // d
"CNTR1", // e
"CNTR2", // f
"CR", // 0 Page1(R/W共通)
"PAR0", // 1
"PAR1", // 2
"PAR2", // 3
"PAR3", // 4
"PAR4", // 5
"PAR5", // 6
"CURR", // 7
"MAR0", // 8
"MAR1", // 9
"MAR2", // a
"MAR3", // b
"MAR4", // c
"MAR5", // d
"MAR6", // e
"MAR7", // f
"CR", // 0 Page2(Read Only)
"PSTART", // 1
"PSTOP", // 2
NULL, // 3
"TPSR", // 4
NULL, // 5
NULL, // 6
NULL, // 7
NULL, // 8
NULL, // 9
NULL, // a
NULL, // b
"RCR", // c
"TCR", // d
"DCR", // e
"IMR", // f
"CR", // 0 Page3(Read側)
"9346CR", // 1
"BPAGE", // 2
"CONFIG0", // 3
"CONFIG1", // 4
"CONFIG2", // 5
"CONFIG3", // 6
NULL, // 7
"CSNSAV", // 8
NULL, // 9
NULL, // a
"INTR", // b
NULL, // c
"CONFIG4", // d
NULL, // e
NULL, // f
"CR", // 0 Page0(Write側)
"PSTART", // 1
"PSTOP", // 2
"BNRY", // 3
"TPSR", // 4
"TBCR0", // 5
"TBCR1", // 6
"ISR", // 7
"RSAR0", // 8
"RSAR1", // 9
"RBCR0", // a
"RBCR1", // b
"RCR", // c
"TCR", // d
"DCR", // e
"IMR", // f
"CR", // 0 Page3(Write側)
"9346CR", // 1
"BPAGE", // 2
"CONFIG0", // 3
"CONFIG1", // 4
"CONFIG2", // 5
"CONFIG3", // 6
"TEST", // 7
NULL, // 8
"HLTCLK", // 9
NULL, // a
NULL, // b
"FMWP", // c
NULL, // d
NULL, // e
NULL, // f
};
// CR_RD (DMAMode) の名前を返す
/*static*/ const char *
RTL8019ASDevice::GetDMAName(uint32 mode)
{
static const char * const rd_str[] = {
// 01234567890
"NotAllowed",
"RemoteRead",
"RemoteWrite",
"SendPacket",
"Complt/Abrt",
};
if (mode > 4) {
return rd_str[4];
} else {
return rd_str[mode];
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.