|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // X68030 のメイン空間 ($000000-$FFFFFF) 担当
9: //
10:
1.1.1.3 root 11: // IODevice
12: // +-- MainbusBaseDevice (InitMainbus() と FC アクセスを持つ)
13: // | +-- MainbusDevice (これがメインバス、システムに1つ)
14: // | | +-- Mainbus24Device (上位8ビットがテーブルで表せるメインバス)
15: // | | | +-- LunaMainbus
16: // | | | | +-- Luna1Mainbus
17: // | | | | +-- Luna88kMainbus
18: // | | | +-- NewsMainbus
19: // | | +-- X68kMainbus
20: // | |
21: // | | +--------------+
22: // | +--| X68kIODevice | (FC を受け取るため)
23: // | +--------------+
24: // |
25: // +-- XPbusDevice
26:
1.1 root 27: #include "x68kio.h"
28: #include "adpcm.h"
29: #include "areaset.h"
1.1.1.3 root 30: #include "bankram.h"
1.1.1.6 ! root 31: #include "buserr.h"
1.1 root 32: #include "busio.h"
33: #include "config.h"
34: #include "crtc.h"
35: #include "dmac.h"
1.1.1.3 root 36: #include "extarea.h"
1.1 root 37: #include "fdc.h"
38: #include "gvram.h"
1.1.1.2 root 39: #include "mainbus.h"
1.1 root 40: #include "mainram.h"
41: #include "mfp.h"
1.1.1.5 root 42: #include "monitor.h"
1.1.1.3 root 43: #include "mpu.h"
1.1.1.2 root 44: #include "nereid.h"
1.1 root 45: #include "opm.h"
46: #include "pedec.h"
47: #include "pio.h"
48: #include "pluto.h"
49: #include "printer.h"
50: #include "romemu_x68k.h"
51: #include "romimg_x68k.h"
52: #include "rp5c15.h"
53: #include "scc.h"
54: #include "spc.h"
55: #include "sprite.h"
56: #include "sram.h"
57: #include "sysport.h"
58: #include "tvram.h"
59: #include "videoctlr.h"
1.1.1.3 root 60: #include "windrv.h"
61: #include <algorithm>
1.1 root 62:
63: // コンストラクタ
64: X68kIODevice::X68kIODevice()
65: : inherited(OBJ_X68KIO)
66: {
1.1.1.5 root 67: map_monitor = gMonitorManager->Regist(ID_SUBWIN_X68KIO, this);
68: map_monitor->func = ToMonitorCallback(&X68kIODevice::MonitorUpdate);
69: map_monitor->SetSize(73, 1 + 32);
70: map_monitor->SetMaxHeight(1 + 256);
1.1.1.2 root 71:
1.1.1.3 root 72: // エリアセットは実質ここが担当しているので、モニタもここ。
1.1.1.5 root 73: area_monitor = gMonitorManager->Regist(ID_MONITOR_AREASET, this);
74: area_monitor->func = ToMonitorCallback(&X68kIODevice::MonitorUpdateAreaset);
75: area_monitor->SetSize(32, 6);
1.1.1.3 root 76:
77: cut_fc2 = (gConfig->Find("x68k-cut-fc2").AsInt() != 0);
78:
1.1 root 79: #define ADD_DEVICE(NAME, ADDR, LEN) \
80: AddDevice(__CONCAT(p,NAME).get(), ADDR, LEN)
81:
82: #define N(NAME, NEW_CTOR, ADDR, LEN) do { \
83: NEWDV(NAME, NEW_CTOR); \
84: ADD_DEVICE(NAME, ADDR, LEN); \
85: } while (0)
86:
1.1.1.3 root 87: NEWDV(BusErr, new BusErrDevice());
88: NEWDV(MainRAM, new MainRAMDevice());
89: NEWDV(IPLROM0, new IPLROM0Device());
90:
1.1 root 91: // デバイスクラス作成。
92: // 初期化順の制約がない限りアドレス順。
93: // 開始アドレス 長さ
94: N(GVRAM, new GVRAMDevice(), 0xc00000,0x200000);
95: N(PlaneVRAM, new TVRAMDevice(), 0xe00000, 0x80000);
1.1.1.3 root 96: N(CRTC, new CRTCDevice(), 0xe80000, 0x2000);
1.1 root 97: N(VideoCtlr, new VideoCtlrDevice(), 0xe82000, 0x2000);
98: N(DMAC, new DMACDevice(), 0xe84000, 0x2000);
1.1.1.4 root 99: N(AreaSet, new BusIO_EB<AreaSetDevice>(), 0xe86000, 0x2000);
1.1 root 100: N(MFP, new BusIO_EB<MFPDevice>(), 0xe88000, 0x2000);
101: N(RTC, new BusIO_EB<RP5C15Device>(), 0xe8a000, 0x2000);
1.1.1.4 root 102: N(Printer, new BusIO_EB<PrinterDevice>(), 0xe8c000, 0x2000);
1.1 root 103: N(Sysport,new BusIO_FB<SysportDevice>(), 0xe8e000, 0x2000);
104: N(OPM, new BusIO_EB<OPMDevice>(), 0xe90000, 0x2000);
105: N(ADPCM, new BusIO_EB<ADPCMDevice>(), 0xe92000, 0x2000);
106: N(FDC, new BusIO_EB<FDCDevice>(), 0xe94000, 0x2000);
107: N(SPC, new BusIO_X68kSPC<SPCDevice>(), 0xe96000, 0x2000);
108: N(SCC, new BusIO_EB<SCCDevice>(), 0xe98000, 0x2000);
109: N(PPI, new BusIO_EB<PPIDevice>(), 0xe9a000, 0x2000);
110: N(PEDEC, new BusIO_EB<PEDECDevice>(), 0xe9c000, 0x2000);
1.1.1.3 root 111: // Windrv はデバイスは常に作成し、マップするかどうかは設定による。
1.1.1.4 root 112: NEWDV(Windrv, new BusIO_B<WindrvDevice>());
1.1.1.3 root 113: if (WindrvDevice::IsWindrv()) {
114: ADD_DEVICE(Windrv, 0xe9e000, 0x2000);
115: }
1.1 root 116: N(Pluto, new PlutoDevice(), 0xeac000, 0x2000);
1.1.1.3 root 117: N(ExtArea, new ExtAreaDevice(), 0xeae000, 0x2000);
1.1 root 118: N(Sprite, new SpriteDevice(), 0xeb0000, 0x10000);
1.1.1.2 root 119: // Nereid はこの時点ではデバイスの追加のみ。Init() 参照。
120: NEWDV(Nereid, new NereidDevice()); // 0xece000, 0x2000
1.1 root 121: N(SRAM, new SRAMDevice(), 0xed0000, 0x4000);
122: N(CGROM, new CGROMDevice(), 0xf00000, 0xc0000);
123: if (gConfig->Find("iplrom2-image").AsString().empty()) {
124: NEWDV(IPLROM2, new ROM30EmuDevice());
125: } else {
126: NEWDV(IPLROM2, new IPLROM2Device());
127: }
128: ADD_DEVICE(IPLROM2, 0xfc0000, 0x20000);
129: N(IPLROM1, new IPLROM1Device(), 0xfe0000, 0x20000);
130: }
131:
132: // デストラクタ
133: X68kIODevice::~X68kIODevice()
134: {
135: }
136:
1.1.1.3 root 137: // メインバスの初期化
1.1 root 138: bool
1.1.1.3 root 139: X68kIODevice::InitMainbus()
1.1 root 140: {
1.1.1.3 root 141: auto buserr = pBusErr.get();
142: auto mainram = dynamic_cast<MainRAMDevice*>(pMainRAM.get());
1.1 root 143:
1.1.1.3 root 144: // RAM は容量が確定した後のここで配置。
1.1 root 145: for (int i = 0; i < mainram->GetSize() / 8192; i++) {
1.1.1.3 root 146: sdevice[i] = mainram;
1.1 root 147: }
148:
1.1.1.2 root 149: // Nereid は1枚でもボードがある時だけメモリ空間に追加する。
150: // Nereid が1枚もない場合でもモニタやログ(この場合のログは起動時にどれが
151: // 有効とか無効と判断したことを表示するもの)のためデバイス自体は常に存在。
152: auto nereid = GetNereidDevice();
153: if (nereid->IsInstalled()) {
154: ADD_DEVICE(Nereid, 0xece000, 0x2000);
1.1.1.3 root 155:
156: // バンク領域もバンクメモリが有効な場合のみ追加する。
157: // #0 が $ee0000、
158: // #1 が $ef0000 固定にしている。
159: for (int i = 0; i < 2; i++) {
160: auto bank = gMainApp.FindObject<BankRAMDevice>(OBJ_BANKRAM(i));
161: if (bank != NULL && bank->GetSize() > 0) {
162: AddDevice(bank, 0xee0000 + i * 0x010000, 0x010000);
163: }
164: }
1.1.1.2 root 165: }
166:
1.1 root 167: // 必要なデバイスは全部埋めたので、この時点で nullptr なエントリを
168: // BusErr に差し替える。
1.1.1.3 root 169: for (auto&& d : sdevice) {
1.1 root 170: if (d == nullptr) {
171: d = buserr;
172: }
173: }
174:
1.1.1.3 root 175: // ユーザ空間にコピー。
176: // 00'0000 - BF'FFFF: S U : RAM
177: // C0'0000 - E7'FFFF: S - : GVRAM/TVRAM
178: // E8'0000 - EB'FFFF: S - : I/O 空間
179: // EC'0000 - EC'FFFF: S U : ユーザ I/O 空間
180: // ED'0000 - EF'FFFF: S - : I/O 空間
181: // F0'0000 - FF'FFFF: S - : ROM
182: SetUdevice(0x000000, 0xbfffff, true);
183: SetUdevice(0xc00000, 0xebffff, false);
184: SetUdevice(0xec0000, 0xecffff, true);
185: SetUdevice(0xed0000, 0xffffff, false);
186:
187: // ユーザ空間も nullptr をチェック。
188: assert(std::all_of(udevice.begin(), udevice.end(),
189: [](IODevice *d) { return d != nullptr; }));
190:
1.1 root 191: // デバッグ表示
192: if (0) {
1.1.1.3 root 193: for (int i = 1536; i < sdevice.size(); i++) {
1.1.1.4 root 194: printf("[%3u] $%06x ", i, i * 8192);
1.1.1.3 root 195: assert(sdevice[i]);
196: assert(udevice[i]);
197: printf("S:%-8s U:%s\n",
198: sdevice[i]->GetName().c_str(),
199: udevice[i]->GetName().c_str());
1.1 root 200: }
201: }
202:
203: return true;
204: }
205:
1.1.1.3 root 206: // アドレス start から end (を含む) までのユーザ空間を作成する。
207: // accessible true ならユーザアクセス可能 (スーパーバイザ空間をコピー)。
208: // false なら BusErr で埋める。
209: void
210: X68kIODevice::SetUdevice(uint32 startaddr, uint32 endaddr, bool accessible)
211: {
212: assert(startaddr < endaddr);
213: assert(((endaddr + 1) & 0x1fff) == 0);
1.1.1.4 root 214: uint start = startaddr / 8192;
215: uint end = (endaddr + 1) / 8192;
1.1.1.3 root 216:
217: // MPU の FC2 ピンカットの動作は本来 MPU 側を加工すべきだが、
218: // MPU アクセスのほうが圧倒的に多いので影響の少ない DMAC 側で処理する。
219:
220: // FC2 ピンカットする前の情報を DMAC に展開。
221: auto dmac = dynamic_cast<DMACDevice*>(pDMAC.get());
222: dmac->SetUdevice(start, end, accessible);
223:
224: // FC2 ピンカットなら全域がユーザアクセス可能。
225: if (cut_fc2) {
226: accessible = true;
227: }
228:
229: if (accessible) {
230: memcpy(&udevice[start], &sdevice[start],
231: (end - start) * sizeof(udevice[0]));
232: } else {
233: auto buserr = pBusErr.get();
234: for (int i = start; i < end; i++) {
235: udevice[i] = buserr;
236: }
237: }
238: }
239:
240: // デバイステーブル sdevice に dev を追加する。
1.1 root 241: void
242: X68kIODevice::AddDevice(IODevice *dev, uint devaddr, uint devlen)
243: {
1.1.1.4 root 244: uint idx = devaddr / 8192;
1.1 root 245: for (int i = idx; i < idx + (devlen + 8191) / 8192; i++) {
246: // 重複するケースは未対応
1.1.1.3 root 247: assertmsg(sdevice[i] == nullptr, "X68kIODevice.AddDevice '%s'",
248: sdevice[i]->GetName().c_str());
1.1 root 249:
1.1.1.3 root 250: sdevice[i] = dev;
1.1 root 251: }
252: }
253:
1.1.1.3 root 254: // アドレス super:addr に対応する IODevice* を返す。
1.1 root 255: // addr は事前に 24bit マスクしてあるもの。
256: inline IODevice *
1.1.1.3 root 257: X68kIODevice::Decoder(bool super, uint32 addr) const
1.1 root 258: {
259: uint idx = addr / 8192;
1.1.1.3 root 260: if (super) {
261: return sdevice[idx];
262: } else {
263: return udevice[idx];
264: }
1.1 root 265: }
266:
1.1.1.3 root 267: // 回路図より CIIN は以下の通り。
268: //
269: // FC1 ---------|
270: // ______ |OR ---o|& ____
271: // DSACK0 --o|>-| |&o---- CIIN
272: // __ |&
273: // AS -----------------o|&
274: //
275: // __ ___ ____
276: // AS FC1 DS0 : DS0 FC1|DS0 : CIIN
277: // L 0 H : L L : L (データ空間 & 16ビットポート)
278: // L 0 L : H H : H
279: // L 1 H : L H : H
280: // L 1 L : H H : H
281:
282: inline void
283: X68kIODevice::CheckCI(busaddr addr)
1.1 root 284: {
1.1.1.3 root 285: // 16ビットポートはメインメモリ、GVRAM、TVRAM、ROM を除いた部分なので
286: // 0xe80000 .. 0xefffff かな。
1.1.1.4 root 287: busaddr cimask = busaddr(0xf80000) | BusAddr::D;
288: busaddr data16 = busaddr(0xe80000) | BusAddr::D;
1.1.1.3 root 289: if ((addr.Get() & cimask.Get()) == data16.Get()) {
290: mpu->SetCI();
291: }
1.1 root 292: }
293:
1.1.1.3 root 294: busdata
1.1.1.4 root 295: X68kIODevice::Read(busaddr addr)
1.1 root 296: {
1.1.1.4 root 297: addr &= 0x00ffffff;
298: CheckCI(addr);
1.1.1.3 root 299:
1.1.1.4 root 300: IODevice *d = Decoder(addr.IsSuper(), addr.Addr());
301: busdata bd = d->Read(addr);
302: putlog(2, "Read $%06x -> $%0*x",
303: addr.Addr(), addr.GetSize() * 2, bd.Data());
1.1.1.3 root 304: return bd;
1.1 root 305: }
306:
1.1.1.3 root 307: busdata
1.1.1.4 root 308: X68kIODevice::Write(busaddr addr, uint32 data)
1.1 root 309: {
1.1.1.4 root 310: addr &= 0x00ffffff;
311: CheckCI(addr);
1.1.1.3 root 312:
1.1.1.4 root 313: IODevice *d = Decoder(addr.IsSuper(), addr.Addr());
314: putlog(2, "Write $%06x <- $%0*x", addr.Addr(), addr.GetSize() * 2, data);
315: return d->Write(addr, data);
1.1 root 316: }
317:
1.1.1.3 root 318: busdata
1.1.1.4 root 319: X68kIODevice::Peek1(uint32 addr)
1.1 root 320: {
1.1.1.4 root 321: addr &= 0x00ffffff;
1.1.1.3 root 322:
1.1.1.4 root 323: IODevice *d = Decoder(true, addr);
324: return d->Peek1(addr);
1.1.1.3 root 325: }
326:
1.1.1.4 root 327: bool
328: X68kIODevice::Poke1(uint32 addr, uint32 data)
1.1.1.3 root 329: {
1.1.1.4 root 330: addr &= 0x00ffffff;
1.1.1.3 root 331:
1.1.1.4 root 332: IODevice *d = Decoder(true, addr);
333: return d->Poke1(addr, data);
1.1 root 334: }
335:
1.1.1.2 root 336: void
337: X68kIODevice::MonitorUpdate(Monitor *, TextScreen& screen)
338: {
339: screen.Clear();
340:
341: // pos が表示開始位置(0-)
342: int pos = (int)screen.userdata;
343:
1.1.1.3 root 344: // ヘッダは常に描く
345: screen.Clear();
1.1.1.2 root 346: for (int i = 0; i < 8; i++) {
347: screen.Print(10 + i * 8, 0, "+$%04x", i * 0x2000);
348: }
349:
1.1.1.3 root 350: // 012345678901234567890
351: // $00'0000: 1234567
352:
1.1.1.4 root 353: uint idx = pos * 8;
1.1.1.3 root 354: for (int y = 1, end = screen.GetRow(); y < end; y++) {
355: screen.Print(0, y, "$%02x'0000:", (idx * 0x2000) >> 16);
1.1.1.2 root 356: for (int i = 0; i < 8; i++) {
1.1.1.3 root 357: auto sdev = sdevice[idx];
358: auto udev = udevice[idx];
359: auto pair = MainbusBaseDevice::FormatDevName(sdev);
360: TA attr;
361: if (sdev != udev) {
362: // スーパーバイザ空間は太字とかにして区別する。
363: attr = TA::Bold;
364: } else {
365: // 両方からアクセス可能 (かそもそもバスエラー)。
366: attr = pair.second;
367: }
1.1.1.2 root 368: const std::string& name = pair.first;
369: screen.Print(10 + i * 8, y, attr, "%s", name.c_str());
370: idx++;
371: }
372: }
373: }
374:
1.1 root 375: // ブートページを切り替える。
1.1.1.3 root 376: // X68kMainbus から呼ばれるが実際はこっちがメイン。
1.1 root 377: void
378: X68kIODevice::SwitchBootPage(bool isrom)
379: {
380: IODevice *ram;
381: IODevice *rom;
382:
1.1.1.3 root 383: if (gMainApp.human_mode) {
384: // Human モードではブートページを RAM に見せる。
385: // 今のところ。
386: return;
387: }
388:
389: // 共通処理
390: inherited::SwitchBootPage(isrom);
391:
1.1 root 392: if (isrom) {
393: // 0x000000 からの 64KB ( 8エントリ)、
394: // 0xfe0000 からの 128KB (16エントリ) のどちらも IPLROM0 が担当する。
395: ram = GetIPLROM0Device();
396: rom = GetIPLROM0Device();
397: } else {
398: // 0x000000 からの 64KB ( 8エントリ) に RAM を見せる。
399: // 0xfe0000 からの 128KB (16エントリ) に IPLROM1 を見せる。
400: ram = GetMainRAMDevice();
401: rom = GetIPLROM1Device();
402: }
403:
404: for (int i = 0; i < 8; i++) {
1.1.1.3 root 405: sdevice[i] = ram;
1.1 root 406: }
407: for (int i = 0; i < 16; i++) {
1.1.1.3 root 408: sdevice[sdevice.size() - 16 + i] = rom;
409: }
410: }
411:
412: // エリアセット設定
413: void
414: X68kIODevice::SetAreaset(uint8 arealimit_)
415: {
416: arealimit = arealimit_;
417:
418: // $000000 から (設定値 + 1) * 8KB をスーパーバイザ領域にする。
419: // $00 なら $000000 から $001FFF まで (8KB)。
420: // $ff なら $000000 から $01FFFF まで (2MB)。
421: // X68030 なのでメモリは最低でも 4MB あるので上限チェックは不要。
1.1.1.4 root 422: uint end = (arealimit + 1);
1.1.1.3 root 423: SetUdevice(0x00'0000, end * 8192 - 1, false);
424: SetUdevice(end * 8192, 0x20'0000 - 1, true);
425: }
426:
427: // 拡張エリアセット取得
428: uint8
1.1.1.4 root 429: X68kIODevice::GetExtarea(uint offset) const
1.1.1.3 root 430: {
1.1.1.4 root 431: if (offset < extarea.size()) {
1.1.1.3 root 432: return extarea[offset];
433: }
434: // ?
435: return 0xff;
436: }
437:
438: // 拡張エリアセット設定
439: void
1.1.1.4 root 440: X68kIODevice::SetExtarea(uint offset, uint8 data)
1.1.1.3 root 441: {
1.1.1.4 root 442: if (offset < extarea.size()) {
1.1.1.3 root 443: extarea[offset] = data;
444: }
445:
446: // [0] $200000 .. $3fffff (2MB)
447: // [1] $400000 .. $5fffff
448: // [2] $600000 .. $7fffff
449: // [3] $800000 .. $9fffff
450: // [4] $a00000 .. $bfffff
451: //
452: // +----+----+----+----+----+----+----+----+
453: // | S7 | S6 | S5 | S4 | S3 | S2 | S1 | S0 |
454: // +----+----+----+----+----+----+----+----+
455: //
456: // S0 は +$000000 〜 +$03ffff (256KB、32エントリ)
457: // S1 は +$040000 〜 +$07ffff
458: // S2 は +$080000 〜 +$0bffff
459: // S3 は +$0c0000 〜 +$0fffff
460: // S4 は +$100000 〜 +$13ffff
461: // S5 は +$140000 〜 +$17ffff
462: // S6 は +$180000 〜 +$1bffff
463: // S7 は +$1c0000 〜 +$1fffff
464: //
465: // %1 ならスーパーバイザ保護 (ユーザアクセス不可)。
466:
467: uint32 addr = (offset + 1) * 0x20'0000;
468: data = ~data;
469: for (int i = 0; i < 8; i++) {
470: bool u = (data & 1);
471: SetUdevice(addr, addr + 0x04'0000 - 1, u);
472:
473: data >>= 1;
474: addr += 0x04'0000;
475: }
476: }
477:
478: // エリアセットと拡張エリアセットのモニタ。
479: void
480: X68kIODevice::MonitorUpdateAreaset(Monitor *, TextScreen& screen)
481: {
1.1.1.4 root 482: uint areaend;
1.1.1.3 root 483:
484: screen.Clear();
485:
486: // エリアセット
487: areaend = (arealimit + 1) * 8192;
488: screen.Print(0, 0, "Areaset $%02x $000000-$%06x",
489: arealimit, areaend);
490:
491: // 拡張エリアセット
492: for (int i = 0; i < extarea.size(); i++) {
493: uint8 data = GetExtarea(i);
1.1.1.4 root 494: screen.Print(0, i + 1, "Extarea[%u] $%02x $%06x", i, data,
1.1.1.3 root 495: (i + 1) * 0x20'0000);
496: for (int j = 0; j < 8; j++) {
497: screen.Putc(24 + j, i + 1, (((int8)data < 0) ? 'S' : '-'));
498: data <<= 1;
499: }
1.1 root 500: }
501: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.