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