|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
1.1.1.2 root 8: // LUNA ビットマッププレーン
1.1 root 9: //
10:
11: // SX-9100 のほうはこう書いてあってこっちが正しいようだ。
12: // +00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
1.1.1.6 root 13: // b100 |RF|RF|RF|RF|BS| |共 通 B M P|プレーン#0 |
1.1 root 14: // b110 |プレーン#1 |プレーン#2 |プレーン#3 |プレーン#4 |
15: // b120 |プレーン#5 |プレーン#6 |プレーン#7 |FC| |
16: // b130 |F0| |F1| |F2| |F3| |
17: // b140 |F4| |F5| |F6| |F7| |
18: //
19: // SX-7100 のほうはこう書いてあるがこうはなっていないようだ。
20: // +00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
21: // b100 |RF| |BS| |共 通 B M P|FC| |
22: // b110 |プレーン#0 |プレーン#1 |プレーン#2 |プレーン#3 |
23: // b120 |プレーン#4 |プレーン#5 |プレーン#6 |プレーン#7 |
24: // b130 |F0| |F1| |F2| |F3| |
25: // b140 |F4| |F5| |F6| |F7| |
26: //
1.1.1.6 root 27: // RF: RFCNT (4バイト)。b100'0000 から b104'0000 の手前までこの1ポートのミラー。
28: // BS: BMSEL (4バイト)。こっちは未確認だがたぶん RFCNT と同じ?
1.1 root 29: // FC: 共通ファンクションセット
30: // Fn: プレーン#n ファンクションセット
31: // 1区画が 64KB。空白のところは未調査。
32: // 未装着のプレーン(#4..#7とか)は 0xff が読めるようだ。
33: // ファンクションセットが 64バイトで折り返してるか未調査だが、
34: // b130'0000 も b133'0000 も 0xff が読めるのでこの範囲全域でミラーかも。
35: // b150'0000 以降(b1ff'ffffまで) は BUSERR のようだ。
36:
1.1.1.2 root 37: // Lunafb の VRAM はロングワード単位でホストバイトオーダ配置なので、
38: // バイトアクセス、ワードアクセス時は HLB(), HLW() マクロを使用のこと。
39:
40: #include "lunafb.h"
1.1.1.4 root 41: #include "bt45x.h"
1.1.1.3 root 42: #include "builtinrom.h"
1.1.1.6 root 43: #include "busio.h"
1.1.1.7 root 44: #include "monitor.h"
1.1.1.2 root 45: #include "mpu.h"
1.1 root 46:
1.1.1.2 root 47: // コンストラクタ
1.1 root 48: LunafbDevice::LunafbDevice()
1.1.1.4 root 49: : inherited(2048, 1024)
1.1 root 50: {
1.1.1.4 root 51: SetName("Lunafb");
52: ClearAlias();
53: AddAlias("Lunafb");
1.1.1.2 root 54:
1.1.1.7 root 55: monitor = gMonitorManager->Regist(ID_MONITOR_LUNAVC, this);
56: monitor->func = ToMonitorCallback(&LunafbDevice::MonitorUpdate);
57: monitor->SetSize(39, 11);
1.1 root 58: }
59:
1.1.1.2 root 60: // デストラクタ
1.1 root 61: LunafbDevice::~LunafbDevice()
62: {
63: }
64:
1.1.1.4 root 65: // 初期化
1.1 root 66: bool
67: LunafbDevice::Init()
68: {
1.1.1.9 ! root 69: if (inherited::Init() == false) {
! 70: return false;
! 71: }
! 72:
1.1.1.4 root 73: auto bt45x = GetBT45xDevice();
74: nplane = bt45x->GetPlaneCount();
75:
1.1 root 76: plane_end = plane0base + 0x40000 * nplane;
1.1.1.3 root 77: fcset_end = fcset0base + 0x40000 * nplane;
1.1 root 78:
79: // プレーン数によってメモリを確保
1.1.1.9 ! root 80: size_t memsize = 0x40000 * nplane;
! 81: mem.reset(new(std::nothrow) uint8 [memsize]);
! 82: if ((bool)mem == false) {
! 83: warnx("Cannot allocate %zu bytes at %s", memsize, __method__);
! 84: return false;
! 85: }
1.1 root 86:
1.1.1.2 root 87: // ホストパレットを取得。
1.1.1.4 root 88: palette = &(bt45x->GetHostPalette())[0];
1.1 root 89:
1.1.1.5 root 90: // XXX とりあえず RAM と同じウェイトを入れておく (未調査)
91: wait = busdata::Wait(1 * mpu->GetClock_nsec());
92:
1.1 root 93: return true;
94: }
95:
1.1.1.2 root 96: // 電源オン/リセット
1.1 root 97: void
1.1.1.2 root 98: LunafbDevice::ResetHard(bool poweron)
1.1 root 99: {
1.1.1.5 root 100: inherited::ResetHard(poweron);
101:
1.1.1.2 root 102: if (poweron) {
103: // XXX 実際は不定値だと思うけど、観測手段がないのでとりあえず。
104: memset(&mem[0], 0xff, 0x40000 * nplane);
105: }
106:
1.1 root 107: // 厳密にどの時点でリセットされるのかは知らないけど
108: bmsel = 0;
109:
110: // XXX 初期値の観測は困難なので適当
111: rfcnt = 0;
1.1.1.2 root 112: xscroll = 0;
113: yscroll = 0;
1.1 root 114:
115: for (int i = 0; i < MAX_PLANES; i++) {
1.1.1.4 root 116: fcset[i] = ROP::THRU;
1.1.1.2 root 117: fcmask[i] = 0xffffffff;
1.1 root 118: }
119: }
120:
1.1.1.5 root 121: busdata
1.1.1.6 root 122: LunafbDevice::Read(busaddr addr)
1.1 root 123: {
1.1.1.6 root 124: uint32 paddr = addr.Addr();
1.1.1.5 root 125: busdata data;
1.1 root 126:
1.1.1.6 root 127: if (plane0base <= paddr && paddr < plane_end) {
1.1 root 128: // プレーンの読み出し
1.1.1.6 root 129: data = *(uint32 *)&mem[(paddr & ~3U) - plane0base];
1.1.1.5 root 130: } else {
131: // 残りは書き込み専用または不在
1.1.1.6 root 132: putlog(1, "Read not-readable $%08x", paddr);
133: data = 0xffffffff;
1.1 root 134: }
1.1.1.5 root 135: data |= wait;
1.1.1.6 root 136: data |= BusData::Size4;
1.1.1.5 root 137: return data;
1.1 root 138: }
139:
1.1.1.5 root 140: busdata
1.1.1.6 root 141: LunafbDevice::Write(busaddr addr, uint32 data)
1.1 root 142: {
1.1.1.6 root 143: uint32 paddr = addr.Addr();
1.1 root 144:
1.1.1.6 root 145: // RFCNT と BMSEL だけバスの処理が別。というよりバスの観点から言えば
146: // 本来別デバイスだが便宜上同じデバイスに同居している感じ。
147: if (paddr < 0xb1080000) {
148: return WriteReg(addr, data);
1.1 root 149: }
150:
1.1.1.6 root 151: uint32 reqsize = addr.GetSize();
152: // 3バイトアクセスは面倒なので回避して .B/.W/.L に整列させる。
153: uint32 datasize;
154: if (reqsize == 4 && (paddr & 3U) == 0) {
155: datasize = 4;
1.1.1.5 root 156: } else {
1.1.1.6 root 157: datasize = std::min(2 - (paddr & 1U), reqsize);
1.1 root 158: }
1.1.1.6 root 159: data >>= (reqsize - datasize) * 8;
1.1 root 160:
1.1.1.6 root 161: if (paddr < plane0base) {
1.1 root 162: // 共通ビットマッププレーン
1.1.1.6 root 163: if (datasize == 4) {
164: WriteCommonBitmap32(paddr, data);
165: } else if (datasize == 2) {
166: WriteCommonBitmap16(paddr, data);
167: } else {
168: WriteCommonBitmap8(paddr, data);
169: }
1.1 root 170:
1.1.1.6 root 171: } else if (__predict_true(paddr < plane_end)) {
1.1.1.3 root 172: // 存在するビットマッププレーン
1.1.1.6 root 173: uint32 reladdr = paddr - plane0base;
1.1 root 174: uint plane = reladdr / 0x40000;
175: uint32 offset = reladdr % 0x40000;
176:
1.1.1.6 root 177: putlog(3, "Plane%u $%08x <- $%0*x", plane, paddr, datasize * 2, data);
178:
179: if (datasize == 4) {
180: WritePlane32(plane, offset, data);
181: } else if (datasize == 2) {
182: WritePlane16(plane, offset, data);
183: } else {
184: WritePlane8(plane, offset, data);
185: }
1.1.1.3 root 186: SetDirty(offset);
187:
1.1.1.6 root 188: } else if (__predict_false(paddr < plane7end)) {
1.1.1.3 root 189: // HW 設定によっては存在しないプレーン
1.1.1.6 root 190: uint32 reladdr = paddr - plane0base;
1.1.1.3 root 191: uint plane = reladdr / 0x40000;
1.1.1.6 root 192: putlog(3, "Not-equipped Plane%u $%08x <- $%0*x",
193: plane, paddr, datasize * 2, data);
1.1 root 194:
1.1.1.6 root 195: } else if (paddr < 0xb1300000) {
196: // 共通ファンクションセット
197: // XXX ワード、バイトアクセスはどうなる?
198: uint rop = (paddr & 0x3f) / 4;
199: putlog(2, "Common FCSet $%08x <- $%0*x (%s)",
200: paddr, datasize * 2, data, RopStr(rop));
201: WriteCommonFCSet(rop, data);
1.1 root 202:
1.1.1.6 root 203: } else if (__predict_true(paddr < fcset_end)) {
204: // 存在するファンクションセット
205: // XXX ワード、バイトアクセスはどうなる?
206: uint plane = (paddr - 0xb1300000) / 0x40000;
207: uint rop = (paddr & 0x3f) / 4;
208: putlog(2, "FCSet%u $%08x <- $%0*x (%s)",
209: plane, paddr, datasize * 2, data, RopStr(rop));
210: fcset[plane] = rop;
211: fcmask[plane] = data;
1.1 root 212:
1.1.1.6 root 213: } else if (paddr < fcset7end) {
214: // HW 設定によっては存在しないファンクションセット
215: uint plane = (paddr - 0xb1300000) / 0x40000;
216: putlog(3, "Not-equipped FCSet%u $%08x <- $%0*x",
217: plane, paddr, datasize * 2, data);
1.1 root 218:
1.1.1.6 root 219: } else {
220: // 残りは全部空きのはずだが挙動未調査。
221: putlog(0, "$%08x <- $%0*x (NOT IMPLEMENTED)",
222: paddr, datasize * 2, data);
1.1 root 223:
1.1.1.3 root 224: }
225:
1.1.1.6 root 226: busdata r = wait;
227: r |= busdata::Size(datasize);
228: return r;
1.1 root 229: }
230:
1.1.1.5 root 231: busdata
1.1.1.6 root 232: LunafbDevice::WriteReg(busaddr addr, uint32 data)
1.1 root 233: {
1.1.1.6 root 234: // RFCNT レジスタ部分は BusIO_L, NPORT=1 相当。
235: //
236: // LUNA-88K PROM で `cw b1040000 0` した後、
237: // `cs b1040000 0100` (RFCNT の上位ワードに $0100 を書き込む) を実行する
238: // と、垂直位置だけが変わる。RFCNT は上位ワードの下位 8 ビットが水平
239: // スクロール位置、下位ワードの下位 10 ビットが垂直スクロール位置なので、
240: // ロングワード全体に $0100'0100 を $00ff'03ff でマスクした結果の
241: // $0000'0100 が書き込まれたことになる。
242: // m88200 のデータバスへの置き方と、このポートがロングワード固定で DBE*
243: // を細かくデコードしていないとすれば辻褄が合う。
244: // 同様に `cs b1040002 0020` を実行すると LUNA-88K 実機では垂直位置と
245: // ともに水平位置も変わる。
246: //
247: // BMSEL レジスタ部分は確認が難しいけどたぶん同じだと思うことにする。
248: data = BusIO::DataMultiplexer_Write_L(addr, data);
249:
250: if (addr.Addr() < 0xb1040000) {
1.1 root 251: // RFCNT
252: WriteRFCNT(data);
1.1.1.6 root 253: } else {
1.1 root 254: // BMSEL
1.1.1.6 root 255: bmsel = data & ((1 << nplane) - 1);
1.1 root 256: putlog(1, "BMSEL <- $%08x", data);
257: }
258:
1.1.1.6 root 259: busdata r = wait;
260: r |= BusData::Size4;
261: return r;
1.1 root 262: }
263:
1.1.1.5 root 264: busdata
1.1.1.6 root 265: LunafbDevice::Peek1(uint32 addr)
1.1 root 266: {
267: if (plane0base <= addr && addr < plane_end) {
268: // プレーンの読み出し
269: return mem[HLB(addr - plane0base)];
270: }
271: // 残りは書き込み専用
272: return 0xff;
273: }
274:
1.1.1.5 root 275: bool
1.1.1.6 root 276: LunafbDevice::Poke1(uint32 addr, uint32 data)
1.1.1.4 root 277: {
278: // 通常プレーンのみ越権書き込み可能とする?
279: if (plane0base <= addr && addr < plane_end) {
280: if ((int32)data >= 0) {
281: uint32 reladdr = addr - plane0base;
282: uint32 offset = reladdr % 0x40000;
283: // ROP の影響は受けずにメモリの内容を変更
284: mem[HLB(reladdr)] = data;
285: // この場合も画面を更新する
286: SetDirty(offset);
287: }
1.1.1.5 root 288: return true;
1.1.1.4 root 289: }
1.1.1.5 root 290: return false;
1.1.1.4 root 291: }
292:
1.1 root 293: void
294: LunafbDevice::MonitorUpdate(Monitor *, TextScreen& screen)
295: {
296: int i;
297:
298: screen.Clear();
299:
300: // 0 1 2 3 4
301: // 012345678901234567890123456789012345678901
302: // RFCNT: $12345678 (X=-123dot, Y=1023dot)
303: //
1.1.1.4 root 304: // BMSEL=$4 ROP Mask
305: // Plane#0 Sel THRU $ffffffff
306: screen.Print(0, 0, "RFCNT: $%08x (X=%4ddot, Y=%4ddot)",
1.1.1.2 root 307: rfcnt, xscroll, yscroll);
1.1.1.7 root 308: screen.Print(2, 2, "BMSEL=$%02x ROP Mask", bmsel);
1.1 root 309:
310: for (i = 0; i < nplane; i++) {
1.1.1.6 root 311: screen.Print(0, 3 + i, "Plane#%u %s %s $%08x",
1.1 root 312: i,
313: (bmsel & (1 << i)) ? "Sel" : "---",
1.1.1.2 root 314: RopStr(fcset[i]), fcmask[i]);
1.1 root 315: }
316: for (; i < MAX_PLANES; i++) {
1.1.1.6 root 317: screen.Print(0, 3 + i, TA::Disable, "Plane#%u (Not installed)", i);
1.1 root 318: }
319: }
320:
1.1.1.2 root 321: // offset の位置に対応する更新フラグを立てる。
1.1.1.4 root 322: // offset はプレーン先頭からのバイトオフセット。
1.1.1.2 root 323: inline void
324: LunafbDevice::SetDirty(uint32 offset)
325: {
1.1.1.4 root 326: // VRAM は横 2048 ドット(256バイト) x 縦 1024 ドット。
1.1.1.2 root 327: //
328: // 1 0
329: // 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0
330: // +-----------+-------+-------+-------+-----+
331: // VRAM | Y座標 (1024dot) | X (256byte) : 8bit|
332: // +-----------+-------+-------+-------+-----+
1.1.1.4 root 333: // : :
334: // +-------------------+
335: // Dirty | |
336: // +-------------------+
337: // 9 8 7 6 5 4 3 2 1 0
338:
339: // 更新フラグはラスタ単位
1.1.1.6 root 340: uint y = (offset >> 8);
1.1.1.7 root 341: dirty.line[y] = true;
1.1.1.4 root 342: }
343:
344: // X 方向にはみ出したときに対応する仮想画面の Y 座標を返す。
345: int
346: LunafbDevice::GetWrapY(int y) const
347: {
348: // Lunafb の VRAM は、はみ出すと 256 ラスタ先のラスタの先頭から
349: // 表示されるようだ。
350: return (y + 256) % 1024;
1.1 root 351: }
352:
353: // RFCNT への書き込み
354: void
355: LunafbDevice::WriteRFCNT(uint32 data)
356: {
357: // レジスタ値
358: rfcnt = data;
359:
1.1.1.4 root 360: // 水平オフセットを計算。
361: // レジスタに設定できる値は 0..255 で、何ピクセル目から表示を開始するかを
362: // 4 ピクセル単位で指定するもの。
363: // ただし、実際に表示されるのは、(4ピクセル単位で) 設定値 +9 の位置から
364: // (謎資料には 8 (というか -8) と書いてあるが実測は +9 のようだ)。
365: // 設定値が 0 の時 X=36 ピクセル目から 1315 ピクセル目までを表示。
366: // :
367: // 設定値が 183 の時 X=768 ピクセル目から 2047 ピクセル目までを表示。
368: // これが正常に表示できる範囲となる。
369: // 184 以降を指定すると、右のはみ出る部分 (2048 ピクセル目以降に相当) は
370: // 256 ラスタ先の X=0 からのデータが描画される (GetWrapY() 参照)。
371: // 表示開始位置は設定値に必ず +9 (*4 [pixel]) されるため、X=0..35 の
372: // ピクセルを (折り返しにより Y がずれた位置に見えることを除けば)
373: // まともに表示する方法はない。まじかよ。
374: xscroll = (rfcnt >> 16) & 0xff;
375: xscroll = (xscroll + 9) * 4;
1.1 root 376:
1.1.1.4 root 377: // ラスタ(垂直)カウンタ。
378: // XXX 26 は実際には CRTC2 によるパラメータ
1.1.1.2 root 379: yscroll = (rfcnt + 26) & 0x3ff;
1.1 root 380:
381: // 全画面更新
382: Invalidate();
383:
1.1.1.3 root 384: putlog(1, "RFCNT <- $%08x (H=%ddot, V=%ddot)", data, xscroll, yscroll);
1.1 root 385: }
386:
387: // 共通ビットマッププレーン バイト書き込み
388: void
389: LunafbDevice::WriteCommonBitmap8(uint32 addr, uint32 data)
390: {
391: uint32 offset = addr - 0xb1080000;
1.1.1.3 root 392: putlog(2, "Common Plane $%08x <- $%02x", addr, data);
1.1 root 393: for (int i = 0; i < nplane; i++) {
394: if ((bmsel & (1 << i))) {
395: WritePlane8(i, offset, data);
396: }
397: }
1.1.1.2 root 398: SetDirty(offset);
1.1 root 399: }
400:
401: // 共通ビットマッププレーン ワード書き込み
402: void
403: LunafbDevice::WriteCommonBitmap16(uint32 addr, uint32 data)
404: {
405: uint32 offset = addr - 0xb1080000;
1.1.1.3 root 406: putlog(2, "Common Plane $%08x <- $%04x", addr, data);
1.1 root 407: for (int i = 0; i < nplane; i++) {
408: if ((bmsel & (1 << i))) {
409: WritePlane16(i, offset, data);
410: }
411: }
1.1.1.2 root 412: SetDirty(offset);
1.1 root 413: }
414:
415: // 共通ビットマッププレーン ロング書き込み
416: void
417: LunafbDevice::WriteCommonBitmap32(uint32 addr, uint32 data)
418: {
419: uint32 offset = addr - 0xb1080000;
1.1.1.3 root 420: putlog(2, "Common Plane $%08x <- $%08x", addr, data);
1.1 root 421: for (int i = 0; i < nplane; i++) {
422: if ((bmsel & (1 << i))) {
423: WritePlane32(i, offset, data);
424: }
425: }
1.1.1.2 root 426: SetDirty(offset);
1.1 root 427: }
428:
1.1.1.2 root 429: // 共通ファンクションセット設定
430: void
431: LunafbDevice::WriteCommonFCSet(uint rop, uint32 data)
432: {
433: for (int i = 0; i < nplane; i++) {
434: if ((bmsel & (1 << i))) {
435: fcset[i] = rop;
436: fcmask[i] = data;
437: }
438: }
439: }
440:
441: // 現在の VRAM の値 vram と、書き込まれた値 data から
442: // 演算後 VRAM に書き戻すことになる値を返す。
443: // ここではマスクはまだ評価しない。
444: /*static*/ uint32
445: LunafbDevice::RasterOp(uint rop, uint32 vram, uint32 data)
1.1 root 446: {
447: switch (rop) {
1.1.1.2 root 448: case ROP::ZERO:
449: vram = 0;
1.1 root 450: break;
1.1.1.2 root 451: case ROP::AND1:
452: vram = vram & data;
1.1 root 453: break;
1.1.1.2 root 454: case ROP::AND2:
455: vram = vram & ~data;
1.1 root 456: break;
1.1.1.2 root 457: case ROP::cmd:
1.1.1.4 root 458: {
1.1.1.2 root 459: // 使用不可。本当はここにシリアルモード変更コマンドがあるらしい。
460:
461: // ログを出すためにはインスタンスが必要だが、普段は static 関数で
462: // 十分なので、ログを出す時だけグローバルインスタンスを参照する。
1.1.1.4 root 463: auto planevram = GetPlaneVRAMDevice();
464: if (planevram->loglevel >= 0) {
465: planevram->putlogn("ROP 3 is used");
1.1.1.2 root 466: }
1.1 root 467: break;
1.1.1.4 root 468: }
1.1.1.2 root 469: case ROP::AND3:
470: vram = ~vram & data;
1.1 root 471: break;
1.1.1.2 root 472: case ROP::THRU:
473: vram = data;
1.1 root 474: break;
1.1.1.2 root 475: case ROP::EOR:
476: vram = vram ^ data;
1.1 root 477: break;
1.1.1.2 root 478: case ROP::OR1:
479: vram = vram | data;
1.1 root 480: break;
1.1.1.2 root 481: case ROP::NOR:
482: vram = ~vram & ~data;
1.1 root 483: break;
1.1.1.2 root 484: case ROP::ENOR:
485: vram = (vram & data) | (~vram & ~data);
1.1 root 486: break;
1.1.1.2 root 487: case ROP::INV1:
488: vram = ~data;
1.1 root 489: break;
1.1.1.2 root 490: case ROP::OR2:
491: vram = vram | ~data;
1.1 root 492: break;
1.1.1.2 root 493: case ROP::INV2:
494: vram = ~vram;
1.1 root 495: break;
1.1.1.2 root 496: case ROP::OR3:
497: vram = ~vram | data;
1.1 root 498: break;
1.1.1.2 root 499: case ROP::NAND:
500: vram = ~vram | ~data;
1.1 root 501: break;
1.1.1.2 root 502: case ROP::ONE:
503: vram = 0xffffffff;
1.1 root 504: break;
1.1.1.2 root 505: default:
1.1.1.6 root 506: VMPANIC("corrupted rop=%u", rop);
1.1 root 507: }
1.1.1.2 root 508: return vram;
1.1 root 509: }
510:
511: // ROP 文字列を返す
512: const char *
513: LunafbDevice::RopStr(uint rop)
514: {
515: static const char * ropstr[] = {
516: "ZERO",
517: "AND1",
518: "AND2",
519: "(3)",
520: "AND3",
521: "THRU", // THROUGH
522: "EOR",
523: "OR1",
524: "NOR",
525: "ENOR",
526: "INV1",
527: "OR2",
528: "INV2",
529: "OR3",
530: "NAND",
531: "ONE",
532: };
533: return ropstr[rop];
534: }
535:
1.1.1.2 root 536: // 個別プレーンへのバイト書き込み
537: // (dirty は変更しない)
1.1 root 538: void
539: LunafbDevice::WritePlane8(uint plane, uint32 offset, uint32 data)
540: {
541: uint32 memoffset = plane * 0x40000 + offset;
1.1.1.2 root 542: uint32 vram;
543: uint32 mask;
1.1 root 544:
1.1.1.2 root 545: vram = mem[HLB(memoffset)];
546: data = RasterOp(fcset[plane], vram, data);
547: mask = fcmask[plane] >> (24 - (memoffset & 3) * 8);
548: mem[HLB(memoffset)] = (data & mask) | (vram & ~mask);
1.1 root 549: }
550:
1.1.1.2 root 551: // 個別プレーンへのワード書き込み
552: // (dirty は変更しない)
1.1 root 553: void
554: LunafbDevice::WritePlane16(uint plane, uint32 offset, uint32 data)
555: {
556: uint32 memoffset = plane * 0x40000 + offset;
1.1.1.2 root 557: uint32 vram;
558: uint32 mask;
1.1 root 559:
1.1.1.2 root 560: vram = *(uint16 *)&mem[HLW(memoffset)];
561: data = RasterOp(fcset[plane], vram, data);
1.1 root 562: if ((memoffset & 2) == 0) {
563: // 上位ワード
1.1.1.2 root 564: mask = fcmask[plane] >> 16;
1.1 root 565: } else {
566: // 下位ワード
1.1.1.2 root 567: mask = fcmask[plane];
1.1 root 568: }
1.1.1.2 root 569: *(uint16 *)&mem[HLW(memoffset)] = (data & mask) | (vram & ~mask);
1.1 root 570: }
571:
1.1.1.2 root 572: // 個別プレーンへのロングワード書き込み
573: // (dirty は変更しない)
1.1 root 574: void
575: LunafbDevice::WritePlane32(uint plane, uint32 offset, uint32 data)
576: {
577: uint32 memoffset = plane * 0x40000 + offset;
1.1.1.2 root 578: uint32 vram;
579: uint32 mask;
1.1 root 580:
1.1.1.2 root 581: vram = *(uint32 *)&mem[memoffset];
582: data = RasterOp(fcset[plane], vram, data);
583: mask = fcmask[plane];
584: *(uint32 *)&mem[memoffset] = (data & mask) | (vram & ~mask);
1.1 root 585: }
586:
1.1.1.4 root 587: // (x, y) 位置のピクセル情報を screen に描画。(ビットマップモニタの情報欄)
588: void
589: LunafbDevice::UpdateInfo(TextScreen& screen, int x, int y) const
590: {
591: screen.Clear();
592:
593: //0 1 2 3 4 5 6
594: //012345678901234567890123456789012345678901234567890123456789012345
595: //X=1024 Y=2024: (P0) $b10c0000: $00 (%00000000) ColorCode=$d
596:
597: screen.Puts(0, 0, "X= Y=");
598: for (int i = 0; i < nplane; i++) {
1.1.1.6 root 599: screen.Print(15, i, "(P%u)", i);
1.1.1.4 root 600: }
601:
602: // カーソルが範囲外ならここまで。
603: if (x < 0) {
604: return;
605: }
606:
607: screen.Print(2, 0, "%4d Y=%4d", x, y);
608:
609: // 1プレーン内のバイトオフセット
610: uint32 planeoffset = ((uint)y * 256) + ((uint)x / 8);
611:
612: uint cc = 0;
613: for (int i = 0; i < nplane; i++) {
614: // プレーン0先頭からのバイトオフセット
615: uint32 totaloffset = 0x40000 * i + planeoffset;
616:
617: uint8 data = mem[HLB(totaloffset)];
1.1.1.6 root 618: screen.Print(15, i, "(P%u) $%08x: $%02x (%%",
1.1.1.4 root 619: i, plane0base + totaloffset, data);
620:
621: // ビットパターン
622: for (int m = 0x80; m != 0; m >>= 1) {
623: screen.Putc((data & m) ? '1' : '0');
624: }
625: screen.Putc(')');
626:
627: // カーソル位置をハイライト
628: screen.Locate(37 + (x % 8), i);
629: screen.SetAttr(TA::Em);
630:
631: // カラーコードを計算
632: if ((data & (0x80U >> (x % 8))) != 0) {
633: cc |= (1U << i);
634: }
635: }
636:
637: screen.Print(47, 0, "ColorCode=$%x", cc);
638: }
639:
1.1 root 640:
641: // エミュレータによる画面出力
642: //
643: // VM 内からの操作ではなく、エミュレータ本体から画面を操作する
644: // エミュレーション ROM によるモニタコンソール用の機能。
1.1.1.2 root 645: // サービスを使いたい側 (EmuROM など) は EmuInitScreen() をコールすると
646: // (現在のパレットのまま) 画面をクリアする。
647: // 以降は EmuPutc() で文字を表示する。
1.1 root 648:
1.1.1.2 root 649: // エミュレータ出力用に画面を初期化する
650: void
651: LunafbDevice::EmuInitScreen()
652: {
1.1 root 653: // 同時書き込み設定
654: bmsel = (1 << nplane) - 1;
655:
1.1.1.2 root 656: // ROP を初期化
657: WriteCommonFCSet(ROP::THRU, 0xffffffff);
658:
1.1 root 659: // 全画面をクリア
1.1.1.2 root 660: EmuFill(0, 0, 2048, 1024, 0);
1.1.1.4 root 661:
662: // 書き込みはプレーン#0 だけ。
663: bmsel = 0x01;
1.1.1.2 root 664: }
665:
666: // 矩形領域を data で埋める。
667: void
668: LunafbDevice::EmuFill(uint x, uint y, uint w, uint h, uint32 data)
669: {
670: // ロングワードアラインしていないケースは対応しない
671: assert(x % 32 == 0);
672: assert(w % 32 == 0);
673:
674: for (uint py = y; py < y + h; py++) {
675: uint32 addr = 0xb1080000 + (x / 32) * 4 + py * 256;
676: for (uint px = x; px < x + w; px += 32) {
677: WriteCommonBitmap32(addr, data);
678: addr += 4;
679: }
1.1 root 680: }
681: }
682:
1.1.1.2 root 683: // ピクセル座標 x, y を左上として文字 c を出力。
684: // 文字が VRAM からはみ出ないようにすること。
1.1 root 685: void
1.1.1.2 root 686: LunafbDevice::EmuPutc(uint x, uint y, uint c)
1.1 root 687: {
1.1.1.2 root 688: EmuPutc(x, y, c, ROP::THRU);
689: }
690:
691: // ピクセル座標 x, y を左上として文字 c を出力。
692: // 文字が VRAM からはみ出ないようにすること。
693: void
1.1.1.6 root 694: LunafbDevice::EmuPutc(uint x, uint y, uint c, uint rop)
1.1.1.2 root 695: {
696: uint32 addr = 0xb1080000 + (x / 32) * 4 + y * 256;
697: uint32 bofs = x % 32;
1.1.1.5 root 698: int height = (c < 0x80) ? 24 : 12;
1.1.1.2 root 699:
700: // マスク設定。上から 12bit にマスクを置いといて..
701: union64 mask {};
702: mask.h = 0xfff00000;
703:
704: // うまい具合にシフト
705: mask.q >>= bofs;
706:
707: // その文字のフォントデータ上でのアドレス
1.1.1.5 root 708: const uint8 * const fontaddr = (c < 0x80)
709: ? Builtin::CGROM12x24(c)
710: : Builtin::CGROM12x12(c & 0x7f);
1.1.1.2 root 711:
1.1.1.5 root 712: for (int i = 0; i < height; i++) {
1.1.1.2 root 713: // その文字のフォントデータ。
714: // データは MSB 側 12bit に入っているのでこれを 64bit MSB 詰めにする
715: union64 data {};
716: data.h = be16toh(*(const uint16*)(fontaddr + i * 2)) << 16;
717:
718: // うまい具合にシフト
719: data.q >>= bofs;
720:
721: WriteCommonFCSet(rop, mask.h);
722: WriteCommonBitmap32(addr, data.h);
723: if (mask.l) {
724: WriteCommonFCSet(rop, mask.l);
725: WriteCommonBitmap32(addr + 4, data.l);
726: }
727: addr += 256; // 1ライン進める
1.1 root 728: }
729:
1.1.1.2 root 730: // ROP を元に戻す
731: WriteCommonFCSet(ROP::THRU, 0xffffffff);
1.1 root 732: }
733:
1.1.1.2 root 734: // 画面を縦スクロールさせる。上スクロールのみサポート。
1.1 root 735: void
1.1.1.2 root 736: LunafbDevice::EmuScrollY(uint dy, uint sy, uint h)
1.1 root 737: {
1.1.1.2 root 738: assert(dy < sy);
1.1 root 739:
1.1.1.2 root 740: uint32 da = dy * 256;
741: uint32 sa = sy * 256;
742:
743: for (uint y = 0; y < h; y++) {
744: for (int i = 0; i < nplane; i++) {
745: memcpy(&mem[da + i * 0x40000], &mem[sa + i * 0x40000], 160);
1.1 root 746: }
1.1.1.2 root 747: da += 256;
748: sa += 256;
1.1 root 749: }
1.1.1.2 root 750:
751: Invalidate();
1.1 root 752: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.