--- nono/vm/lunafb.cpp 2026/04/29 17:04:55 1.1 +++ nono/vm/lunafb.cpp 2026/04/29 17:05:37 1.1.1.8 @@ -4,21 +4,13 @@ // Licensed under nono-license.txt // -#include "lunafb.h" -#include "bt454.h" -#include "cgrom.h" -#include "mpu.h" - // -// ビットマッププレーン -// -// Lunafb の VRAM はロングワード単位でホストバイトオーダ配置なので、 -// バイトアクセス、ワードアクセス時は HLB(), HLW() マクロを使用のこと。 +// LUNA ビットマッププレーン // // SX-9100 のほうはこう書いてあってこっちが正しいようだ。 // +00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f -// b100 |RF| |BS| |共 通 B M P|プレーン#0 | +// b100 |RF|RF|RF|RF|BS| |共 通 B M P|プレーン#0 | // b110 |プレーン#1 |プレーン#2 |プレーン#3 |プレーン#4 | // b120 |プレーン#5 |プレーン#6 |プレーン#7 |FC| | // b130 |F0| |F1| |F2| |F3| | @@ -32,8 +24,8 @@ // b130 |F0| |F1| |F2| |F3| | // b140 |F4| |F5| |F6| |F7| | // -// RF: RFCNT (4バイト) -// BS: BMSEL (4バイト) +// RF: RFCNT (4バイト)。b100'0000 から b104'0000 の手前までこの1ポートのミラー。 +// BS: BMSEL (4バイト)。こっちは未確認だがたぶん RFCNT と同じ? // FC: 共通ファンクションセット // Fn: プレーン#n ファンクションセット // 1区画が 64KB。空白のところは未調査。 @@ -41,302 +33,227 @@ // ファンクションセットが 64バイトで折り返してるか未調査だが、 // b130'0000 も b133'0000 も 0xff が読めるのでこの範囲全域でミラーかも。 // b150'0000 以降(b1ff'ffffまで) は BUSERR のようだ。 -// -std::unique_ptr gLunafb; +// Lunafb の VRAM はロングワード単位でホストバイトオーダ配置なので、 +// バイトアクセス、ワードアクセス時は HLB(), HLW() マクロを使用のこと。 +#include "lunafb.h" +#include "bt45x.h" +#include "builtinrom.h" +#include "busio.h" +#include "monitor.h" +#include "mpu.h" + +// コンストラクタ LunafbDevice::LunafbDevice() - : inherited("Lunafb") + : inherited(2048, 1024) { - devaddr = 0xb1000000; + SetName("Lunafb"); + ClearAlias(); + AddAlias("Lunafb"); - monitor.func = (MonitorCallback_t)&LunafbDevice::MonitorUpdate; - monitor.SetSize(42, 11); - monitor.Regist(ID_MONITOR_LUNAFB); + monitor = gMonitorManager->Regist(ID_MONITOR_LUNAVC, this); + monitor->func = ToMonitorCallback(&LunafbDevice::MonitorUpdate); + monitor->SetSize(39, 11); } +// デストラクタ LunafbDevice::~LunafbDevice() { } +// 初期化 bool LunafbDevice::Init() { - // XXX とりあえず。実際は設定による - nplane = 1; + auto bt45x = GetBT45xDevice(); + nplane = bt45x->GetPlaneCount(); + plane_end = plane0base + 0x40000 * nplane; + fcset_end = fcset0base + 0x40000 * nplane; // プレーン数によってメモリを確保 mem.reset(new uint8 [0x40000 * nplane]); - return true; -} + // ホストパレットを取得。 + palette = &(bt45x->GetHostPalette())[0]; + + // XXX とりあえず RAM と同じウェイトを入れておく (未調査) + wait = busdata::Wait(1 * mpu->GetClock_nsec()); -bool -LunafbDevice::PowerOn() -{ - // XXX 実際は不定値だと思うけど、観測手段がないのでとりあえず。 - memset(&mem[0], 0xff, 0x40000 * nplane); return true; } +// 電源オン/リセット void -LunafbDevice::ResetHard() +LunafbDevice::ResetHard(bool poweron) { + inherited::ResetHard(poweron); + + if (poweron) { + // XXX 実際は不定値だと思うけど、観測手段がないのでとりあえず。 + memset(&mem[0], 0xff, 0x40000 * nplane); + } + // 厳密にどの時点でリセットされるのかは知らないけど bmsel = 0; // XXX 初期値の観測は困難なので適当 rfcnt = 0; - hscroll = 0; - vscroll = 0; + xscroll = 0; + yscroll = 0; for (int i = 0; i < MAX_PLANES; i++) { - fcs[i] = 5; - mask[i] = 0xffffffff; + fcset[i] = ROP::THRU; + fcmask[i] = 0xffffffff; } - - // XXX ここ? - Invalidate(); } -uint64 -LunafbDevice::Read8(uint32 addr) +busdata +LunafbDevice::Read(busaddr addr) { - // XXX とりあえず RAM と同じウェイトを入れておく (未調査) - gMPU->AddCycle(1); + uint32 paddr = addr.Addr(); + busdata data; - if (plane0base <= addr && addr < plane_end) { + if (plane0base <= paddr && paddr < plane_end) { // プレーンの読み出し - return mem[HLB(addr - plane0base)]; - } - // 残りは書き込み専用 - putlog(1, "%s: 未接続アドレス $%08x", __func__, addr); - return 0xff; -} - -uint64 -LunafbDevice::Read16(uint32 addr) -{ - // XXX とりあえず RAM と同じウェイトを入れておく (未調査) - gMPU->AddCycle(1); - - if (plane0base <= addr && addr < plane_end) { - // プレーンの読み出し - return *(uint16 *)&mem[HLW(addr - plane0base)]; + data = *(uint32 *)&mem[(paddr & ~3U) - plane0base]; + } else { + // 残りは書き込み専用または不在 + putlog(1, "Read not-readable $%08x", paddr); + data = 0xffffffff; } - // 残りは書き込み専用 - putlog(1, "%s: 未接続アドレス $%08x", __func__, addr); - return 0xffff; + data |= wait; + data |= BusData::Size4; + return data; } -uint64 -LunafbDevice::Read32(uint32 addr) +busdata +LunafbDevice::Write(busaddr addr, uint32 data) { - // XXX とりあえず RAM と同じウェイトを入れておく (未調査) - gMPU->AddCycle(1); + uint32 paddr = addr.Addr(); - if (plane0base <= addr && addr < plane_end) { - // プレーンの読み出し - return *(uint32 *)&mem[addr - plane0base]; + // RFCNT と BMSEL だけバスの処理が別。というよりバスの観点から言えば + // 本来別デバイスだが便宜上同じデバイスに同居している感じ。 + if (paddr < 0xb1080000) { + return WriteReg(addr, data); + } + + uint32 reqsize = addr.GetSize(); + // 3バイトアクセスは面倒なので回避して .B/.W/.L に整列させる。 + uint32 datasize; + if (reqsize == 4 && (paddr & 3U) == 0) { + datasize = 4; + } else { + datasize = std::min(2 - (paddr & 1U), reqsize); } - // 残りは書き込み専用 - putlog(1, "%s: 未接続アドレス $%08x", __func__, addr); - return 0xffffffff; -} + data >>= (reqsize - datasize) * 8; -uint64 -LunafbDevice::Write8(uint32 addr, uint32 data) -{ - // XXX とりあえず RAM と同じウェイトを入れておく (未調査) - gMPU->AddCycle(1); - - switch (addr) { - case 0xb1000000: - case 0xb1000001: - case 0xb1000002: - case 0xb1000003: - // このロングワード内へのバイトアクセスはロングワードにミラーされる - data = (data << 24) | (data << 16) | (data << 8) | data; - return Write32(addr & ~3, data); - case 0xb1040000: - case 0xb1040001: - case 0xb1040002: - case 0xb1040003: - // BMSEL はバイトアクセスできる? - putlog(0, "未実装バイト書き込み $%08x <- $%02x", addr, data); - return 0; - } - - if (addr < 0xb1080000) { - putlog(0, "未実装バイト書き込み $%08x <- $%02x", addr, data); - return 0; - } - if (addr < plane0base) { + if (paddr < plane0base) { // 共通ビットマッププレーン - WriteCommonBitmap8(addr, data); - return 0; - } + if (datasize == 4) { + WriteCommonBitmap32(paddr, data); + } else if (datasize == 2) { + WriteCommonBitmap16(paddr, data); + } else { + WriteCommonBitmap8(paddr, data); + } - if (addr < plane7end) { - // ビットマッププレーン - uint32 reladdr = addr - plane0base; + } else if (__predict_true(paddr < plane_end)) { + // 存在するビットマッププレーン + uint32 reladdr = paddr - plane0base; uint plane = reladdr / 0x40000; uint32 offset = reladdr % 0x40000; - if (plane < nplane) { - // ビットマッププレーンN - putlog(2, "プレーン%d書き込み $%08x <- $%02x", plane, addr, data); + putlog(3, "Plane%u $%08x <- $%0*x", plane, paddr, datasize * 2, data); + + if (datasize == 4) { + WritePlane32(plane, offset, data); + } else if (datasize == 2) { + WritePlane16(plane, offset, data); + } else { WritePlane8(plane, offset, data); - return 0; } - // HW 設定によっては存在しないプレーンへの書き込み - putlog(3, "不在プレーン%d書き込み $%08x <- $%02x", - plane, addr, data); - return 0; - } - - putlog(0, "未実装バイト書き込み $%08x <- $%02x", addr, data); - return 0; -} + SetDirty(offset); -uint64 -LunafbDevice::Write16(uint32 addr, uint32 data) -{ - // XXX とりあえず RAM と同じウェイトを入れておく (未調査) - gMPU->AddCycle(1); + } else if (__predict_false(paddr < plane7end)) { + // HW 設定によっては存在しないプレーン + uint32 reladdr = paddr - plane0base; + uint plane = reladdr / 0x40000; + putlog(3, "Not-equipped Plane%u $%08x <- $%0*x", + plane, paddr, datasize * 2, data); - switch (addr) { - case 0xb1000000: - case 0xb1000002: - // このロングワード内へのワードアクセスはロングワードにミラーされる - data = (data << 16) | data; - return Write32(addr & ~3, data); - case 0xb1040000: - case 0xb1040002: - // BMSEL はワードアクセスできる? - putlog(0, "未実装ワード書き込み $%08x <- $%04x", addr, data); - return 0; - } - - if (addr < 0xb1080000) { - putlog(0, "未実装ワード書き込み $%08x <- $%04x", addr, data); - return 0; - } - if (addr < plane0base) { - // 共通ビットマッププレーン - WriteCommonBitmap16(addr, data); - return 0; - } + } else if (paddr < 0xb1300000) { + // 共通ファンクションセット + // XXX ワード、バイトアクセスはどうなる? + uint rop = (paddr & 0x3f) / 4; + putlog(2, "Common FCSet $%08x <- $%0*x (%s)", + paddr, datasize * 2, data, RopStr(rop)); + WriteCommonFCSet(rop, data); + + } else if (__predict_true(paddr < fcset_end)) { + // 存在するファンクションセット + // XXX ワード、バイトアクセスはどうなる? + uint plane = (paddr - 0xb1300000) / 0x40000; + uint rop = (paddr & 0x3f) / 4; + putlog(2, "FCSet%u $%08x <- $%0*x (%s)", + plane, paddr, datasize * 2, data, RopStr(rop)); + fcset[plane] = rop; + fcmask[plane] = data; + + } else if (paddr < fcset7end) { + // HW 設定によっては存在しないファンクションセット + uint plane = (paddr - 0xb1300000) / 0x40000; + putlog(3, "Not-equipped FCSet%u $%08x <- $%0*x", + plane, paddr, datasize * 2, data); - if (addr < plane7end) { - // ビットマッププレーン - uint32 reladdr = addr - plane0base; - uint plane = reladdr / 0x40000; - uint32 offset = reladdr % 0x40000; + } else { + // 残りは全部空きのはずだが挙動未調査。 + putlog(0, "$%08x <- $%0*x (NOT IMPLEMENTED)", + paddr, datasize * 2, data); - if (plane < nplane) { - // ビットマッププレーンN - putlog(2, "プレーン%d書き込み $%08x <- $%04x", plane, addr, data); - WritePlane16(plane, offset, data); - return 0; - } - // HW 設定によっては存在しないプレーンへの書き込み - putlog(3, "不在プレーン%d書き込み $%08x <- $%04x", - plane, addr, data); - return 0; } - putlog(0, "未実装ワード書き込み $%08x <- $%04x", addr, data); - return 0; + busdata r = wait; + r |= busdata::Size(datasize); + return r; } -uint64 -LunafbDevice::Write32(uint32 addr, uint32 data) +busdata +LunafbDevice::WriteReg(busaddr addr, uint32 data) { - // XXX とりあえず RAM と同じウェイトを入れておく (未調査) - gMPU->AddCycle(1); + // RFCNT レジスタ部分は BusIO_L, NPORT=1 相当。 + // + // LUNA-88K PROM で `cw b1040000 0` した後、 + // `cs b1040000 0100` (RFCNT の上位ワードに $0100 を書き込む) を実行する + // と、垂直位置だけが変わる。RFCNT は上位ワードの下位 8 ビットが水平 + // スクロール位置、下位ワードの下位 10 ビットが垂直スクロール位置なので、 + // ロングワード全体に $0100'0100 を $00ff'03ff でマスクした結果の + // $0000'0100 が書き込まれたことになる。 + // m88200 のデータバスへの置き方と、このポートがロングワード固定で DBE* + // を細かくデコードしていないとすれば辻褄が合う。 + // 同様に `cs b1040002 0020` を実行すると LUNA-88K 実機では垂直位置と + // ともに水平位置も変わる。 + // + // BMSEL レジスタ部分は確認が難しいけどたぶん同じだと思うことにする。 + data = BusIO::DataMultiplexer_Write_L(addr, data); - switch (addr) { - case 0xb1000000: + if (addr.Addr() < 0xb1040000) { // RFCNT WriteRFCNT(data); - return 0; - - case 0xb1040000: + } else { // BMSEL - bmsel = data & 0xf; + bmsel = data & ((1 << nplane) - 1); putlog(1, "BMSEL <- $%08x", data); - return 0; - } - - if (addr < 0xb1080000) { - putlog(0, "未実装ロング書き込み $%08x <- $%08x", addr, data); - return 0; - } - if (addr < plane0base) { - // 共通ビットマッププレーン - WriteCommonBitmap32(addr, data); - return 0; - } - - if (addr < plane7end) { - // ビットマッププレーン - uint32 reladdr = addr - plane0base; - uint plane = reladdr / 0x40000; - uint32 offset = reladdr % 0x40000; - - if (plane < nplane) { - // ビットマッププレーンN - putlog(3, "プレーン%d書き込み $%08x <- $%08x", plane, addr, data); - WritePlane32(plane, offset, data); - return 0; - } - // HW 設定によっては存在しないプレーンへの書き込み - putlog(3, "不在プレーン%d書き込み $%08x <- $%08x", - plane, addr, data); - return 0; } - if (addr < 0xb1300000) { - // 共通ファンクションセット - uint rop = (addr & 0x3f) / 4; - putlog(2, "共通FCSet 書き込み $%08x <- $%08x (%s)", - addr, data, RopStr(rop)); - for (int i = 0; i < nplane; i++) { - if ((bmsel & (1 << i))) { - fcs[i] = rop; - mask[i] = data; - } - } - return 0; - } - - if (addr < fcset7end) { - // ファンクションセット - int plane = (addr - 0xb1300000) / 0x40000; - - if (plane < nplane) { - // ファンクションセットN - uint rop = (addr & 0x3f) / 4; - putlog(2, "FCSet%d書き込み $%08x <- $%08x (%s)", - plane, addr, data, RopStr(rop)); - fcs[plane] = rop; - mask[plane] = data; - return 0; - } - // HW 設定によっては存在しない FCSet への書き込み - putlog(3, "不在FCSet%d書き込み $%08x <- $%08x", - plane, addr, data); - return 0; - } - - putlog(0, "未実装ロング書き込み $%08x <- $%08x", addr, data); - return 0; + busdata r = wait; + r |= BusData::Size4; + return r; } -uint64 -LunafbDevice::Peek8(uint32 addr) +busdata +LunafbDevice::Peek1(uint32 addr) { if (plane0base <= addr && addr < plane_end) { // プレーンの読み出し @@ -346,6 +263,24 @@ LunafbDevice::Peek8(uint32 addr) return 0xff; } +bool +LunafbDevice::Poke1(uint32 addr, uint32 data) +{ + // 通常プレーンのみ越権書き込み可能とする? + if (plane0base <= addr && addr < plane_end) { + if ((int32)data >= 0) { + uint32 reladdr = addr - plane0base; + uint32 offset = reladdr % 0x40000; + // ROP の影響は受けずにメモリの内容を変更 + mem[HLB(reladdr)] = data; + // この場合も画面を更新する + SetDirty(offset); + } + return true; + } + return false; +} + void LunafbDevice::MonitorUpdate(Monitor *, TextScreen& screen) { @@ -357,44 +292,53 @@ LunafbDevice::MonitorUpdate(Monitor *, T // 012345678901234567890123456789012345678901 // RFCNT: $12345678 (X=-123dot, Y=1023dot) // - // BMSEL=$4 ROP Mask RGB RGB - // Plane#0 Sel THRU $ffffffff $0:RGB $8:RGB - screen.Print(0, 0, "RFCNT: $%08x (X=%ddot, Y=%ddot)", - rfcnt, hscroll, vscroll); - screen.Print(3, 2, "BMSEL=$%x ROP Mask", bmsel); + // BMSEL=$4 ROP Mask + // Plane#0 Sel THRU $ffffffff + screen.Print(0, 0, "RFCNT: $%08x (X=%4ddot, Y=%4ddot)", + rfcnt, xscroll, yscroll); + screen.Print(2, 2, "BMSEL=$%02x ROP Mask", bmsel); for (i = 0; i < nplane; i++) { - screen.Print(0, 3 + i, "Plane#%d %s %s $%08x", + screen.Print(0, 3 + i, "Plane#%u %s %s $%08x", i, (bmsel & (1 << i)) ? "Sel" : "---", - RopStr(fcs[i]), mask[i]); + RopStr(fcset[i]), fcmask[i]); } for (; i < MAX_PLANES; i++) { - screen.Print(0, 3 + i, TA::Disable, "Plane#%d (Not installed)", i); - } - - // ついでなのでここで BT454 のパレットも表示。 - // 012345 - // $0:RGB - const uint8 *pal = gBT454->reg.color; - screen.Puts(29, 1, ""); - screen.Puts(32, 2, "RGB RGB"); - for (i = 0; i < 16; i++) { - uint rgb = - ((pal[i * 3 + 0] & 0xf0) << 4) + - ((pal[i * 3 + 1] & 0xf0) ) + - ((pal[i * 3 + 2] & 0xf0) >> 4); - screen.Print(29 + (i / 8) * 7, (i % 8) + 3, "$%x:%03x", i, rgb); + screen.Print(0, 3 + i, TA::Disable, "Plane#%u (Not installed)", i); } } -// 全画面の更新フラグを立てる -void -LunafbDevice::Invalidate() +// offset の位置に対応する更新フラグを立てる。 +// offset はプレーン先頭からのバイトオフセット。 +inline void +LunafbDevice::SetDirty(uint32 offset) { - for (int i = 0; i < 1024; i++) { - atomic_dirty[i] = 1; - } + // VRAM は横 2048 ドット(256バイト) x 縦 1024 ドット。 + // + // 1 0 + // 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0 + // +-----------+-------+-------+-------+-----+ + // VRAM | Y座標 (1024dot) | X (256byte) : 8bit| + // +-----------+-------+-------+-------+-----+ + // : : + // +-------------------+ + // Dirty | | + // +-------------------+ + // 9 8 7 6 5 4 3 2 1 0 + + // 更新フラグはラスタ単位 + uint y = (offset >> 8); + dirty.line[y] = true; +} + +// X 方向にはみ出したときに対応する仮想画面の Y 座標を返す。 +int +LunafbDevice::GetWrapY(int y) const +{ + // Lunafb の VRAM は、はみ出すと 256 ラスタ先のラスタの先頭から + // 表示されるようだ。 + return (y + 256) % 1024; } // RFCNT への書き込み @@ -404,23 +348,31 @@ LunafbDevice::WriteRFCNT(uint32 data) // レジスタ値 rfcnt = data; - // 水平オフセットを計算 - // XXX マニュアルでは -8 まで指定できるように読めるが - // 指定する方法が分からない。 - // XXX 193 以上を指定するとどうなるのか (とりあえずほっとく) - // 実機の挙動を目分量で見ると、右にはみ出た部分は - // 256 ラスタ先のラスタデータを描画しているようだ。 - hscroll = ((rfcnt >> 16) & 0xff) + 8; - hscroll *= 4; - - // ラスタ(垂直)カウンタ - vscroll = (rfcnt + 26) & 0x3ff; + // 水平オフセットを計算。 + // レジスタに設定できる値は 0..255 で、何ピクセル目から表示を開始するかを + // 4 ピクセル単位で指定するもの。 + // ただし、実際に表示されるのは、(4ピクセル単位で) 設定値 +9 の位置から + // (謎資料には 8 (というか -8) と書いてあるが実測は +9 のようだ)。 + // 設定値が 0 の時 X=36 ピクセル目から 1315 ピクセル目までを表示。 + // : + // 設定値が 183 の時 X=768 ピクセル目から 2047 ピクセル目までを表示。 + // これが正常に表示できる範囲となる。 + // 184 以降を指定すると、右のはみ出る部分 (2048 ピクセル目以降に相当) は + // 256 ラスタ先の X=0 からのデータが描画される (GetWrapY() 参照)。 + // 表示開始位置は設定値に必ず +9 (*4 [pixel]) されるため、X=0..35 の + // ピクセルを (折り返しにより Y がずれた位置に見えることを除けば) + // まともに表示する方法はない。まじかよ。 + xscroll = (rfcnt >> 16) & 0xff; + xscroll = (xscroll + 9) * 4; + + // ラスタ(垂直)カウンタ。 + // XXX 26 は実際には CRTC2 によるパラメータ + yscroll = (rfcnt + 26) & 0x3ff; // 全画面更新 Invalidate(); - putlog(1, "RFCNT <- $%08x (水平 %ddot, 垂直 %ddot)", - data, hscroll, vscroll); + putlog(1, "RFCNT <- $%08x (H=%ddot, V=%ddot)", data, xscroll, yscroll); } // 共通ビットマッププレーン バイト書き込み @@ -428,12 +380,13 @@ void LunafbDevice::WriteCommonBitmap8(uint32 addr, uint32 data) { uint32 offset = addr - 0xb1080000; - putlog(2, "共通プレーン書き込み $%08x <- $%02x", addr, data); + putlog(2, "Common Plane $%08x <- $%02x", addr, data); for (int i = 0; i < nplane; i++) { if ((bmsel & (1 << i))) { WritePlane8(i, offset, data); } } + SetDirty(offset); } // 共通ビットマッププレーン ワード書き込み @@ -441,12 +394,13 @@ void LunafbDevice::WriteCommonBitmap16(uint32 addr, uint32 data) { uint32 offset = addr - 0xb1080000; - putlog(2, "共通プレーン書き込み $%08x <- $%04x", addr, data); + putlog(2, "Common Plane $%08x <- $%04x", addr, data); for (int i = 0; i < nplane; i++) { if ((bmsel & (1 << i))) { WritePlane16(i, offset, data); } } + SetDirty(offset); } // 共通ビットマッププレーン ロング書き込み @@ -454,68 +408,95 @@ void LunafbDevice::WriteCommonBitmap32(uint32 addr, uint32 data) { uint32 offset = addr - 0xb1080000; - putlog(2, "共通プレーン書き込み $%08x <- $%08x", addr, data); + putlog(2, "Common Plane $%08x <- $%08x", addr, data); for (int i = 0; i < nplane; i++) { if ((bmsel & (1 << i))) { WritePlane32(i, offset, data); } } + SetDirty(offset); } -static inline uint32 -RasterOp(uint rop, uint32 data, uint32 m) +// 共通ファンクションセット設定 +void +LunafbDevice::WriteCommonFCSet(uint rop, uint32 data) +{ + for (int i = 0; i < nplane; i++) { + if ((bmsel & (1 << i))) { + fcset[i] = rop; + fcmask[i] = data; + } + } +} + +// 現在の VRAM の値 vram と、書き込まれた値 data から +// 演算後 VRAM に書き戻すことになる値を返す。 +// ここではマスクはまだ評価しない。 +/*static*/ uint32 +LunafbDevice::RasterOp(uint rop, uint32 vram, uint32 data) { switch (rop) { - case 0: // ZERO - data = 0; + case ROP::ZERO: + vram = 0; break; - case 1: // AND1 - data &= m; + case ROP::AND1: + vram = vram & data; break; - case 2: // AND2 - data = ~data & m; + case ROP::AND2: + vram = vram & ~data; break; - case 3: // 使用不可 - // 本当はここにシリアルモード変更コマンドがあるらしい - data = m; + case ROP::cmd: + { + // 使用不可。本当はここにシリアルモード変更コマンドがあるらしい。 + + // ログを出すためにはインスタンスが必要だが、普段は static 関数で + // 十分なので、ログを出す時だけグローバルインスタンスを参照する。 + auto planevram = GetPlaneVRAMDevice(); + if (planevram->loglevel >= 0) { + planevram->putlogn("ROP 3 is used"); + } break; - case 4: // AND3 - data &= ~m; + } + case ROP::AND3: + vram = ~vram & data; break; - case 5: // THROUGH + case ROP::THRU: + vram = data; break; - case 6: // EOR - data ^= m; + case ROP::EOR: + vram = vram ^ data; break; - case 7: // OR1 - data |= m; + case ROP::OR1: + vram = vram | data; break; - case 8: // NOR - data = ~data & ~m; + case ROP::NOR: + vram = ~vram & ~data; break; - case 9: // ENOR - data = (data & m) | (~data & ~m); + case ROP::ENOR: + vram = (vram & data) | (~vram & ~data); break; - case 10: // INV1 - data = ~data; + case ROP::INV1: + vram = ~data; break; - case 11: // OR2 - data = ~data | m; + case ROP::OR2: + vram = vram | ~data; break; - case 12: // INV2 - data = ~m; + case ROP::INV2: + vram = ~vram; break; - case 13: // OR3 - data |= ~m; + case ROP::OR3: + vram = ~vram | data; break; - case 14: // NAND - data = ~data | ~m; + case ROP::NAND: + vram = ~vram | ~data; break; - case 15: // ONE - data = 0xffffffff; + case ROP::ONE: + vram = 0xffffffff; break; + default: + VMPANIC("corrupted rop=%u", rop); } - return data; + return vram; } // ROP 文字列を返す @@ -543,53 +524,108 @@ LunafbDevice::RopStr(uint rop) return ropstr[rop]; } -// プレーン バイト書き込み +// 個別プレーンへのバイト書き込み +// (dirty は変更しない) void LunafbDevice::WritePlane8(uint plane, uint32 offset, uint32 data) { uint32 memoffset = plane * 0x40000 + offset; - uint32 m; - uint32 mask8; + uint32 vram; + uint32 mask; - m = mem[HLB(memoffset)]; - data = RasterOp(fcs[plane], data, m); - mask8 = mask[plane] >> (24 - (memoffset & 3) * 8); - mem[HLB(memoffset)] = (data & mask8) | (m & ~mask8); - atomic_dirty[offset / 256] = 1; + vram = mem[HLB(memoffset)]; + data = RasterOp(fcset[plane], vram, data); + mask = fcmask[plane] >> (24 - (memoffset & 3) * 8); + mem[HLB(memoffset)] = (data & mask) | (vram & ~mask); } -// プレーン ワード書き込み +// 個別プレーンへのワード書き込み +// (dirty は変更しない) void LunafbDevice::WritePlane16(uint plane, uint32 offset, uint32 data) { uint32 memoffset = plane * 0x40000 + offset; - uint32 m; - uint32 mask16; + uint32 vram; + uint32 mask; - m = *(uint16 *)&mem[HLW(memoffset)]; - data = RasterOp(fcs[plane], data, m); + vram = *(uint16 *)&mem[HLW(memoffset)]; + data = RasterOp(fcset[plane], vram, data); if ((memoffset & 2) == 0) { // 上位ワード - mask16 = mask[plane] >> 16; + mask = fcmask[plane] >> 16; } else { // 下位ワード - mask16 = mask[plane]; + mask = fcmask[plane]; } - *(uint16 *)&mem[HLW(memoffset)] = (data & mask16) | (m & ~mask16); - atomic_dirty[offset / 256] = 1; + *(uint16 *)&mem[HLW(memoffset)] = (data & mask) | (vram & ~mask); } -// プレーン ロングワード書き込み +// 個別プレーンへのロングワード書き込み +// (dirty は変更しない) void LunafbDevice::WritePlane32(uint plane, uint32 offset, uint32 data) { uint32 memoffset = plane * 0x40000 + offset; - uint32 m; + uint32 vram; + uint32 mask; + + vram = *(uint32 *)&mem[memoffset]; + data = RasterOp(fcset[plane], vram, data); + mask = fcmask[plane]; + *(uint32 *)&mem[memoffset] = (data & mask) | (vram & ~mask); +} + +// (x, y) 位置のピクセル情報を screen に描画。(ビットマップモニタの情報欄) +void +LunafbDevice::UpdateInfo(TextScreen& screen, int x, int y) const +{ + screen.Clear(); - m = *(uint32 *)&mem[memoffset]; - data = RasterOp(fcs[plane], data, m); - *(uint32 *)&mem[memoffset] = (data & mask[plane]) | (m & ~mask[plane]); - atomic_dirty[offset / 256] = 1; + //0 1 2 3 4 5 6 + //012345678901234567890123456789012345678901234567890123456789012345 + //X=1024 Y=2024: (P0) $b10c0000: $00 (%00000000) ColorCode=$d + + screen.Puts(0, 0, "X= Y="); + for (int i = 0; i < nplane; i++) { + screen.Print(15, i, "(P%u)", i); + } + + // カーソルが範囲外ならここまで。 + if (x < 0) { + return; + } + + screen.Print(2, 0, "%4d Y=%4d", x, y); + + // 1プレーン内のバイトオフセット + uint32 planeoffset = ((uint)y * 256) + ((uint)x / 8); + + uint cc = 0; + for (int i = 0; i < nplane; i++) { + // プレーン0先頭からのバイトオフセット + uint32 totaloffset = 0x40000 * i + planeoffset; + + uint8 data = mem[HLB(totaloffset)]; + screen.Print(15, i, "(P%u) $%08x: $%02x (%%", + i, plane0base + totaloffset, data); + + // ビットパターン + for (int m = 0x80; m != 0; m >>= 1) { + screen.Putc((data & m) ? '1' : '0'); + } + screen.Putc(')'); + + // カーソル位置をハイライト + screen.Locate(37 + (x % 8), i); + screen.SetAttr(TA::Em); + + // カラーコードを計算 + if ((data & (0x80U >> (x % 8))) != 0) { + cc |= (1U << i); + } + } + + screen.Print(47, 0, "ColorCode=$%x", cc); } @@ -597,126 +633,111 @@ LunafbDevice::WritePlane32(uint plane, u // // VM 内からの操作ではなく、エミュレータ本体から画面を操作する // エミュレーション ROM によるモニタコンソール用の機能。 -// サービスを使いたい側 (EmuROM など) が任意の大きさの TextScreen を -// 用意し EmuConsoleOpen() をコールすると (現在のパレットのまま) 画面を -// クリアし、画面中央をこのテキスト VRAM の表示エリアとみなす。 -// 以降は EmuConsoleUpdate() で TextScreen の内容に従って画面を更新し、 -// 使用後は EmuConsoleClose() で再び画面をクリア (およびリソースの解放) を -// すること。 - -// エミュレータによる出力開始 -void -LunafbDevice::EmuConsoleOpen(TextScreen *t) -{ - textscr = t; - - screen_w = textscr->GetCol(); - screen_h = textscr->GetRow(); - prev.resize(screen_w * screen_h); - for (int i = 0; i < screen_w * screen_h; i++) { - prev[i] = 0x0020; - } - - // ざっくりセンタリングする - origin_x = (1280 - screen_w * 12) / 2; - origin_y = (1024 - screen_h * 24) / 2; - // ただし横は 48ドット境界から始める - // こうすると1(,2)文字目の24ビットが必ずワード境界から始まる。 - origin_x = (origin_x / 48) * 48; +// サービスを使いたい側 (EmuROM など) は EmuInitScreen() をコールすると +// (現在のパレットのまま) 画面をクリアする。 +// 以降は EmuPutc() で文字を表示する。 +// エミュレータ出力用に画面を初期化する +void +LunafbDevice::EmuInitScreen() +{ // 同時書き込み設定 bmsel = (1 << nplane) - 1; + // ROP を初期化 + WriteCommonFCSet(ROP::THRU, 0xffffffff); + // 全画面をクリア - for (uint32 addr = 0xb1080000; addr < 0xb10c0000; addr += 4) { - WriteCommonBitmap32(addr, 0); + EmuFill(0, 0, 2048, 1024, 0); + + // 書き込みはプレーン#0 だけ。 + bmsel = 0x01; +} + +// 矩形領域を data で埋める。 +void +LunafbDevice::EmuFill(uint x, uint y, uint w, uint h, uint32 data) +{ + // ロングワードアラインしていないケースは対応しない + assert(x % 32 == 0); + assert(w % 32 == 0); + + for (uint py = y; py < y + h; py++) { + uint32 addr = 0xb1080000 + (x / 32) * 4 + py * 256; + for (uint px = x; px < x + w; px += 32) { + WriteCommonBitmap32(addr, data); + addr += 4; + } } } -// エミュレータによる出力終了 +// ピクセル座標 x, y を左上として文字 c を出力。 +// 文字が VRAM からはみ出ないようにすること。 void -LunafbDevice::EmuConsoleClose() +LunafbDevice::EmuPutc(uint x, uint y, uint c) { - // 全画面をクリア - for (uint32 addr = 0xb1080000; addr < 0xb10c0000; addr += 4) { - WriteCommonBitmap32(addr, 0); + EmuPutc(x, y, c, ROP::THRU); +} + +// ピクセル座標 x, y を左上として文字 c を出力。 +// 文字が VRAM からはみ出ないようにすること。 +void +LunafbDevice::EmuPutc(uint x, uint y, uint c, uint rop) +{ + uint32 addr = 0xb1080000 + (x / 32) * 4 + y * 256; + uint32 bofs = x % 32; + int height = (c < 0x80) ? 24 : 12; + + // マスク設定。上から 12bit にマスクを置いといて.. + union64 mask {}; + mask.h = 0xfff00000; + + // うまい具合にシフト + mask.q >>= bofs; + + // その文字のフォントデータ上でのアドレス + const uint8 * const fontaddr = (c < 0x80) + ? Builtin::CGROM12x24(c) + : Builtin::CGROM12x12(c & 0x7f); + + for (int i = 0; i < height; i++) { + // その文字のフォントデータ。 + // データは MSB 側 12bit に入っているのでこれを 64bit MSB 詰めにする + union64 data {}; + data.h = be16toh(*(const uint16*)(fontaddr + i * 2)) << 16; + + // うまい具合にシフト + data.q >>= bofs; + + WriteCommonFCSet(rop, mask.h); + WriteCommonBitmap32(addr, data.h); + if (mask.l) { + WriteCommonFCSet(rop, mask.l); + WriteCommonBitmap32(addr + 4, data.l); + } + addr += 256; // 1ライン進める } - // 同時書き込みも元に戻す - bmsel = 0; - textscr = NULL; - prev.clear(); + // ROP を元に戻す + WriteCommonFCSet(ROP::THRU, 0xffffffff); } -// エミュレータによる出力 -// ts が出力するテキスト情報、oldts は前回の情報。 +// 画面を縦スクロールさせる。上スクロールのみサポート。 void -LunafbDevice::EmuConsoleUpdate() +LunafbDevice::EmuScrollY(uint dy, uint sy, uint h) { - auto& text = textscr->GetBuf(); - - // 12ドットフォントなので2文字ずつ (24ビットずつ) 更新する - for (int y = 0; y < screen_h; y++) { - for (int x = 0; x < screen_w; x += 2) { - // 2文字分の文字と属性 - uint32 t32 = *(const uint32 *)&text[y * screen_w + x]; - uint32 p32 = *(const uint32 *)&prev[y * screen_w + x]; - if (t32 == p32) { - continue; - } - // ここからは変更があったので出力 - *(uint32 *)&prev[y * screen_w + x] = t32; - // 2文字分として直接32ビットキャストして取り出した t32 だと - // エンディアンによって上下が変わるので、ここでは改めて - // 順序通りに2回取り出す - uint32 ch0 = text[y * screen_w + x]; - uint32 ch1 = text[y * screen_w + x + 1]; - // その文字のフォントデータ上でのオフセット - const uint8 * const fo0 = BuiltinCGROM::Get12x24(ch0 & 0xff); - const uint8 * const fo1 = BuiltinCGROM::Get12x24(ch1 & 0xff); - - // 1,2文字目なら true、3,4文字目なら false - bool evenpair = ((x & 2) == 0); - - // ビットマップ上の書き始めアドレス - uint32 addr = 0xb1080000 + - ((origin_y + y * 24) * 256) + - ((origin_x + x * 12) / 8); - - for (int i = 0; i < 24; i++) { - // その文字のフォントデータ - uint32 fdat0 = be16toh(*(const uint16*)(fo0 + i * 2)); - uint32 fdat1 = be16toh(*(const uint16*)(fo1 + i * 2)); - - // 属性はとりあえず反転のみサポート - if ((ch0 & TA::On)) { - fdat0 = ~fdat0 & 0xfff0; - } - if ((ch1 & TA::On)) { - fdat1 = ~fdat1 & 0xfff0; - } - - // 1文字目と2文字目を組み合わせて 24bit にする - uint32 data = (fdat0 << 8) | (fdat1 >> 4); - - // 書き込みは一度に 3バイトずつになるので、 - // 効率化のため上位下位ペアで書き込みパターンを変える。 - // - // | chr1 | chr2 | chr3 | chr4 | - // +----+----+----+----+----+----+ - // | +0 | +1 | +2 | +3 | +4 | +5 | - // +----+----+----+----+----+----+ - // |<-.Word->|<.B>|<.B>|<-.Word->| - if (evenpair) { - WriteCommonBitmap16(addr, data >> 8); - WriteCommonBitmap8(addr + 2, data & 0xff); - } else { - WriteCommonBitmap8(addr, data >> 16); - WriteCommonBitmap16(addr + 1, data & 0xffff); - } + assert(dy < sy); + + uint32 da = dy * 256; + uint32 sa = sy * 256; - addr += 256; // 1ライン進める - } + for (uint y = 0; y < h; y++) { + for (int i = 0; i < nplane; i++) { + memcpy(&mem[da + i * 0x40000], &mem[sa + i * 0x40000], 160); } + da += 256; + sa += 256; } + + Invalidate(); }