|
|
1.1 root 1: //
2: // nono
1.1.1.4 ! root 3: // Copyright (C) 2020 nono project
! 4: // Licensed under nono-license.txt
1.1 root 5: //
6:
7: #include "bitmap.h"
8: #include "bt454.h"
1.1.1.3 root 9: #include "cgrom.h"
1.1 root 10:
11: //
12: // ビットマッププレーン
13: //
14: // Bitmap の VRAM はロングワード単位でホストバイトオーダ配置なので、
15: // バイトアクセス、ワードアクセス時は HLB(), HLW() マクロを使用のこと。
16: //
17:
18: // SX-9100 のほうはこう書いてあってこっちが正しいようだ。
19: // +00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
20: // b100 |RF| |BS| |共 通 B M P|プレーン#0 |
21: // b110 |プレーン#1 |プレーン#2 |プレーン#3 |プレーン#4 |
22: // b120 |プレーン#5 |プレーン#6 |プレーン#7 |FC| |
23: // b130 |F0| |F1| |F2| |F3| |
24: // b140 |F4| |F5| |F6| |F7| |
25: //
26: // SX-7100 のほうはこう書いてあるがこうはなっていないようだ。
27: // +00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
28: // b100 |RF| |BS| |共 通 B M P|FC| |
29: // b110 |プレーン#0 |プレーン#1 |プレーン#2 |プレーン#3 |
30: // b120 |プレーン#4 |プレーン#5 |プレーン#6 |プレーン#7 |
31: // b130 |F0| |F1| |F2| |F3| |
32: // b140 |F4| |F5| |F6| |F7| |
33: //
34: // RF: RFCNT (4バイト)
35: // BS: BMSEL (4バイト)
36: // FC: 共通ファンクションセット
37: // Fn: プレーン#n ファンクションセット
38: // 1区画が 64KB。空白のところは未調査。
1.1.1.3 root 39: // 未装着のプレーン(#4..#7とか)は 0xff が読めるようだ。
40: // ファンクションセットが 64バイトで折り返してるか未調査だが、
41: // b130'0000 も b133'0000 も 0xff が読めるのでこの範囲全域でミラーかも。
42: // b150'0000 以降(b1ff'ffffまで) は BUSERR のようだ。
43: //
1.1 root 44:
1.1.1.3 root 45: std::unique_ptr<BitmapDevice> gBitmap;
1.1 root 46:
47: BitmapDevice::BitmapDevice()
48: {
49: logname = "bitmap";
50: devname = "Bitmap";
51: devaddr = 0xb1000000;
52: monitor.Init(42, 11);
53:
54: // XXX とりあえず。実際は設定による
55: nplane = 1;
56: // プレーン数によってメモリを確保
1.1.1.3 root 57: mem.reset(new uint8 [0x40000 * nplane]);
1.1 root 58: }
59:
60: BitmapDevice::~BitmapDevice()
61: {
62: }
63:
64: void
65: BitmapDevice::ResetHard()
66: {
67: // 厳密にどの時点でリセットされるのかは知らないけど
68: bmsel = 0;
69:
70: // RFCNT の初期値は観測困難なので、とりあえず無視。
71:
72: for (int i = 0; i < MAX_PLANES; i++) {
73: fcs[i] = 5;
74: mask[i] = 0xffffffff;
75: }
76:
77: // XXX ここ?
78: Invalidate();
79: }
80:
81: uint64
82: BitmapDevice::Read8(uint32 addr)
83: {
84: if (addr < plane0base) {
85: // 書き込み専用
86: return 0xff;
87: }
88: if (addr < plane0base + 0x40000 * nplane) {
89: // プレーンの読み出し
90: return mem[HLB(addr - plane0base)];
91: }
92: // 残りは書き込み専用
93: return 0xff;
94: }
95:
96: uint64
97: BitmapDevice::Read16(uint32 addr)
98: {
99: if (addr < plane0base) {
100: // 書き込み専用
101: return 0xffff;
102: }
103: if (addr < plane0base + 0x40000 * nplane) {
104: // プレーンの読み出し
105: return *(uint16 *)&mem[HLW(addr - plane0base)];
106: }
107: // 残りは書き込み専用
108: return 0xffff;
109: }
110:
111: uint64
112: BitmapDevice::Read32(uint32 addr)
113: {
114: if (addr < plane0base) {
115: // 書き込み専用
116: return 0xffffffff;
117: }
118: if (addr < plane0base + 0x40000 * nplane) {
119: // プレーンの読み出し
120: return *(uint32 *)&mem[addr - plane0base];
121: }
122: // 残りは書き込み専用
123: return 0xffffffff;
124: }
125:
126: uint64
127: BitmapDevice::Write8(uint32 addr, uint32 data)
128: {
129: switch (addr) {
130: case 0xb1000000:
131: case 0xb1000001:
132: case 0xb1000002:
133: case 0xb1000003:
134: // このロングワード内へのバイトアクセスはロングワードにミラーされる
135: data = (data << 24) | (data << 16) | (data << 8) | data;
136: return Write32(addr & ~3, data);
137: case 0xb1040000:
138: case 0xb1040001:
139: case 0xb1040002:
140: case 0xb1040003:
141: // BMSEL はバイトアクセスできる?
142: putlog(0, "未実装バイト書き込み $%08x <- $%02x", addr, data);
143: return 0;
144: }
145:
146: if (addr < 0xb1080000) {
147: putlog(0, "未実装バイト書き込み $%08x <- $%02x", addr, data);
148: return 0;
149: }
150: if (addr < plane0base) {
151: // 共通ビットマッププレーン
152: WriteCommonBitmap8(addr, data);
153: return 0;
154: }
155:
156: if (addr < plane7end) {
157: // ビットマッププレーン
158: uint32 reladdr = addr - plane0base;
159: uint plane = reladdr / 0x40000;
160: uint32 offset = reladdr % 0x40000;
161:
162: if (plane < nplane) {
163: // ビットマッププレーンN
164: putlog(2, "プレーン%d書き込み $%08x <- $%02x", plane, addr, data);
165: WritePlane8(plane, offset, data);
166: return 0;
167: }
168: // HW 設定によっては存在しないプレーンへの書き込み
169: putlog(3, "不在プレーン%d書き込み $%08x <- $%02x",
170: plane, addr, data);
171: return 0;
172: }
173:
174: putlog(0, "未実装バイト書き込み $%08x <- $%02x", addr, data);
175: return 0;
176: }
177:
178: uint64
179: BitmapDevice::Write16(uint32 addr, uint32 data)
180: {
181: switch (addr) {
182: case 0xb1000000:
183: case 0xb1000002:
184: // このロングワード内へのワードアクセスはロングワードにミラーされる
185: data = (data << 16) | data;
186: return Write32(addr & ~3, data);
187: case 0xb1040000:
188: case 0xb1040002:
189: // BMSEL はワードアクセスできる?
190: putlog(0, "未実装ワード書き込み $%08x <- $%04x", addr, data);
191: return 0;
192: }
193:
194: if (addr < 0xb1080000) {
195: putlog(0, "未実装ワード書き込み $%08x <- $%04x", addr, data);
196: return 0;
197: }
198: if (addr < plane0base) {
199: // 共通ビットマッププレーン
200: WriteCommonBitmap16(addr, data);
201: return 0;
202: }
203:
204: if (addr < plane7end) {
205: // ビットマッププレーン
206: uint32 reladdr = addr - plane0base;
207: uint plane = reladdr / 0x40000;
208: uint32 offset = reladdr % 0x40000;
209:
210: if (plane < nplane) {
211: // ビットマッププレーンN
212: putlog(2, "プレーン%d書き込み $%08x <- $%04x", plane, addr, data);
213: WritePlane16(plane, offset, data);
214: return 0;
215: }
216: // HW 設定によっては存在しないプレーンへの書き込み
217: putlog(3, "不在プレーン%d書き込み $%08x <- $%04x",
218: plane, addr, data);
219: return 0;
220: }
221:
222: putlog(0, "未実装ワード書き込み $%08x <- $%04x", addr, data);
223: return 0;
224: }
225:
226: uint64
227: BitmapDevice::Write32(uint32 addr, uint32 data)
228: {
229: switch (addr) {
230: case 0xb1000000:
231: // RFCNT
232: WriteRFCNT(data);
233: return 0;
234:
235: case 0xb1040000:
236: // BMSEL
237: bmsel = data & 0xf;
238: putlog(1, "BMSEL <- $%08x", data);
239: return 0;
240: }
241:
242: if (addr < 0xb1080000) {
243: putlog(0, "未実装ロング書き込み $%08x <- $%08x", addr, data);
244: return 0;
245: }
246: if (addr < plane0base) {
247: // 共通ビットマッププレーン
248: WriteCommonBitmap32(addr, data);
249: return 0;
250: }
251:
252: if (addr < plane7end) {
253: // ビットマッププレーン
254: uint32 reladdr = addr - plane0base;
255: uint plane = reladdr / 0x40000;
256: uint32 offset = reladdr % 0x40000;
257:
258: if (plane < nplane) {
259: // ビットマッププレーンN
260: putlog(3, "プレーン%d書き込み $%08x <- $%08x", plane, addr, data);
261: WritePlane32(plane, offset, data);
262: return 0;
263: }
264: // HW 設定によっては存在しないプレーンへの書き込み
265: putlog(3, "不在プレーン%d書き込み $%08x <- $%08x",
266: plane, addr, data);
267: return 0;
268: }
269:
270: if (addr < 0xb1300000) {
271: // 共通ファンクションセット
272: uint rop = (addr & 0x3f) / 4;
273: putlog(2, "共通FCSet 書き込み $%08x <- $%08x (%s)",
274: addr, data, RopStr(rop));
275: for (int i = 0; i < nplane; i++) {
276: if ((bmsel & (1 << i))) {
277: fcs[i] = rop;
278: mask[i] = data;
279: }
280: }
281: return 0;
282: }
283:
284: if (addr < fcset7end) {
285: // ファンクションセット
286: int plane = (addr - 0xb1300000) / 0x40000;
287:
288: if (plane < nplane) {
289: // ファンクションセットN
290: uint rop = (addr & 0x3f) / 4;
291: putlog(2, "FCSet%d書き込み $%08x <- $%08x (%s)",
292: plane, addr, data, RopStr(rop));
293: fcs[plane] = rop;
294: mask[plane] = data;
295: return 0;
296: }
297: // HW 設定によっては存在しない FCSet への書き込み
298: putlog(3, "不在FCSet%d書き込み $%08x <- $%08x",
299: plane, addr, data);
300: return 0;
301: }
302:
303: putlog(0, "未実装ロング書き込み $%08x <- $%08x", addr, data);
304: return 0;
305: }
306:
307: uint64
308: BitmapDevice::Peek8(uint32 addr)
309: {
310: if (addr < plane0base) {
311: // 書き込み専用
312: return 0xff;
313: }
314: if (addr < plane0base + 0x40000 * nplane) {
315: // プレーンの読み出し
316: return mem[HLB(addr - plane0base)];
317: }
318: // 残りは書き込み専用
319: return 0xff;
320: }
321:
322: bool
323: BitmapDevice::MonitorUpdate()
324: {
325: int i;
326:
327: monitor.Clear();
328:
329: // 0 1 2 3 4
330: // 012345678901234567890123456789012345678901
331: // RFCNT: $12345678 (X=-123dot, Y=1023dot)
332: //
333: // BMSEL=$4 ROP Mask RGB RGB
334: // Plane#0 Sel THRU $ffffffff $0:RGB $8:RGB
335: monitor.Print(0, 0, "RFCNT: $%08x (X=%ddot, Y=%ddot)",
336: rfcnt, hscroll, vscroll);
337: monitor.Print(3, 2, "BMSEL=$%x ROP Mask", bmsel);
338:
339: for (i = 0; i < nplane; i++) {
340: monitor.Print(0, 3 + i, "Plane#%d %s %s $%08x",
341: i,
342: (bmsel & (1 << i)) ? "Sel" : "---",
343: RopStr(fcs[i]), mask[i]);
344: }
345: for (; i < MAX_PLANES; i++) {
346: monitor.Print(0, 3 + i, TA::Disable, "Plane#%d (Not installed)", i);
347: }
348:
349: // ついでなのでここで BT454 のパレットも表示。
350: // 012345
351: // $0:RGB
352: const uint8 *pal = gBT454->reg.color;
1.1.1.4 ! root 353: monitor.Puts(29, 1, "<BT454>");
! 354: monitor.Puts(32, 2, "RGB RGB");
1.1 root 355: for (i = 0; i < 16; i++) {
356: uint rgb =
357: ((pal[i * 3 + 0] & 0xf0) << 4) +
358: ((pal[i * 3 + 1] & 0xf0) ) +
359: ((pal[i * 3 + 2] & 0xf0) >> 4);
360: monitor.Print(29 + (i / 8) * 7, (i % 8) + 3, "$%x:%03x", i, rgb);
361: }
362:
363: return true;
364: }
365:
366: // 全画面の更新フラグを立てる
367: void
368: BitmapDevice::Invalidate()
369: {
370: for (int i = 0; i < 1024; i++) {
371: atomic_dirty[i] = 1;
372: }
373: }
374:
375: // RFCNT への書き込み
376: void
377: BitmapDevice::WriteRFCNT(uint32 data)
378: {
379: // レジスタ値
380: rfcnt = data;
381:
382: // 水平オフセットを計算
383: // XXX マニュアルでは -8 まで指定できるように読めるが
384: // 指定する方法が分からない。
385: // XXX 193 以上を指定するとどうなるのか (とりあえずほっとく)
386: // 実機の挙動を目分量で見ると、右にはみ出た部分は
387: // 256 ラスタ先のラスタデータを描画しているようだ。
388: hscroll = ((rfcnt >> 16) & 0xff) + 8;
389: hscroll *= 4;
390:
391: // ラスタ(垂直)カウンタ
1.1.1.2 root 392: vscroll = (rfcnt + 26) & 0x3ff;
1.1 root 393:
394: // 全画面更新
395: Invalidate();
396:
397: putlog(1, "RFCNT <- $%08x (水平 %ddot, 垂直 %ddot)",
398: data, hscroll, vscroll);
399: }
400:
401: // 共通ビットマッププレーン バイト書き込み
402: void
403: BitmapDevice::WriteCommonBitmap8(uint32 addr, uint32 data)
404: {
405: uint32 offset = addr - 0xb1080000;
406: putlog(2, "共通プレーン書き込み $%08x <- $%02x", addr, data);
407: for (int i = 0; i < nplane; i++) {
408: if ((bmsel & (1 << i))) {
409: WritePlane8(i, offset, data);
410: }
411: }
412: }
413:
414: // 共通ビットマッププレーン ワード書き込み
415: void
416: BitmapDevice::WriteCommonBitmap16(uint32 addr, uint32 data)
417: {
418: uint32 offset = addr - 0xb1080000;
419: putlog(2, "共通プレーン書き込み $%08x <- $%04x", addr, data);
420: for (int i = 0; i < nplane; i++) {
421: if ((bmsel & (1 << i))) {
422: WritePlane16(i, offset, data);
423: }
424: }
425: }
426:
427: // 共通ビットマッププレーン ロング書き込み
428: void
429: BitmapDevice::WriteCommonBitmap32(uint32 addr, uint32 data)
430: {
431: uint32 offset = addr - 0xb1080000;
432: putlog(2, "共通プレーン書き込み $%08x <- $%08x", addr, data);
433: for (int i = 0; i < nplane; i++) {
434: if ((bmsel & (1 << i))) {
435: WritePlane32(i, offset, data);
436: }
437: }
438: }
439:
440: static inline uint32
441: RasterOp(uint rop, uint32 data, uint32 m)
442: {
443: switch (rop) {
444: case 0: // ZERO
445: data = 0;
446: break;
447: case 1: // AND1
448: data &= m;
449: break;
450: case 2: // AND2
451: data = ~data & m;
452: break;
453: case 3: // 使用不可
454: // 本当はここにシリアルモード変更コマンドがあるらしい
455: data = m;
456: break;
457: case 4: // AND3
458: data &= ~m;
459: break;
460: case 5: // THROUGH
461: break;
462: case 6: // EOR
463: data ^= m;
464: break;
465: case 7: // OR1
466: data |= m;
467: break;
468: case 8: // NOR
469: data = ~data & ~m;
470: break;
471: case 9: // ENOR
472: data = (data & m) | (~data & ~m);
473: break;
474: case 10: // INV1
475: data = ~data;
476: break;
477: case 11: // OR2
478: data = ~data | m;
479: break;
480: case 12: // INV2
481: data = ~m;
482: break;
483: case 13: // OR3
484: data |= ~m;
485: break;
486: case 14: // NAND
487: data = ~data | ~m;
488: break;
489: case 15: // ONE
490: data = 0xffffffff;
491: break;
492: }
493: return data;
494: }
495:
496: // ROP 文字列を返す
497: const char *
498: BitmapDevice::RopStr(uint rop)
499: {
500: static const char * ropstr[] = {
501: "ZERO",
502: "AND1",
503: "AND2",
504: "(3)",
505: "AND3",
506: "THRU", // THROUGH
507: "EOR",
508: "OR1",
509: "NOR",
510: "ENOR",
511: "INV1",
512: "OR2",
513: "INV2",
514: "OR3",
515: "NAND",
516: "ONE",
517: };
518: return ropstr[rop];
519: }
520:
521: // プレーン バイト書き込み
522: void
523: BitmapDevice::WritePlane8(uint plane, uint32 offset, uint32 data)
524: {
525: uint32 memoffset = plane * 0x40000 + offset;
526: uint32 m;
527: uint32 mask8;
528:
529: m = mem[HLB(memoffset)];
530: data = RasterOp(fcs[plane], data, m);
531: mask8 = mask[plane] >> (24 - (memoffset & 3) * 8);
532: mem[HLB(memoffset)] = (data & mask8) | (m & ~mask8);
533: atomic_dirty[offset / 256] = 1;
534: }
535:
536: // プレーン ワード書き込み
537: void
538: BitmapDevice::WritePlane16(uint plane, uint32 offset, uint32 data)
539: {
540: uint32 memoffset = plane * 0x40000 + offset;
541: uint32 m;
542: uint32 mask16;
543:
544: m = *(uint16 *)&mem[HLW(memoffset)];
545: data = RasterOp(fcs[plane], data, m);
546: if ((memoffset & 2) == 0) {
547: // 上位ワード
548: mask16 = mask[plane] >> 16;
549: } else {
550: // 下位ワード
551: mask16 = mask[plane];
552: }
553: *(uint16 *)&mem[HLW(memoffset)] = (data & mask16) | (m & ~mask16);
554: atomic_dirty[offset / 256] = 1;
555: }
556:
557: // プレーン ロングワード書き込み
558: void
559: BitmapDevice::WritePlane32(uint plane, uint32 offset, uint32 data)
560: {
561: uint32 memoffset = plane * 0x40000 + offset;
562: uint32 m;
563:
564: m = *(uint32 *)&mem[memoffset];
565: data = RasterOp(fcs[plane], data, m);
566: *(uint32 *)&mem[memoffset] = (data & mask[plane]) | (m & ~mask[plane]);
567: atomic_dirty[offset / 256] = 1;
568: }
1.1.1.3 root 569:
570:
571: // エミュレータによる画面出力
572: //
573: // VM 内からの操作ではなく、エミュレータ本体から画面を操作する
574: // エミュレーション ROM によるモニタコンソール用の機能。
575: // サービスを使いたい側 (EmuROM など) が任意の大きさの TextScreen を
576: // 用意し EmuConsoleOpen() をコールすると (現在のパレットのまま) 画面を
577: // クリアし、画面中央をこのテキスト VRAM の表示エリアとみなす。
578: // 以降は EmuConsoleUpdate() で TextScreen の内容に従って画面を更新し、
579: // 使用後は EmuConsoleClose() で再び画面をクリア (およびリソースの解放) を
580: // すること。
581:
582: // エミュレータによる出力開始
583: void
584: BitmapDevice::EmuConsoleOpen(TextScreen *t)
585: {
586: textscr = t;
587:
588: screen_w = textscr->GetCol();
589: screen_h = textscr->GetRow();
590: prev.reset(new uint16[screen_w * screen_h]);
591: for (int i = 0; i < screen_w * screen_h; i++) {
592: prev[i] = 0x0020;
593: }
594:
595: // ざっくりセンタリングする
596: origin_x = (1280 - screen_w * 12) / 2;
597: origin_y = (1024 - screen_h * 24) / 2;
598: // ただし横は 48ドット境界から始める
599: // こうすると1(,2)文字目の24ビットが必ずワード境界から始まる。
600: origin_x = (origin_x / 48) * 48;
601:
602: // 同時書き込み設定
603: bmsel = (1 << nplane) - 1;
604:
605: // 全画面をクリア
606: for (uint32 addr = 0xb1080000; addr < 0xb10c0000; addr += 4) {
607: WriteCommonBitmap32(addr, 0);
608: }
609: }
610:
611: // エミュレータによる出力終了
612: void
613: BitmapDevice::EmuConsoleClose()
614: {
615: // 全画面をクリア
616: for (uint32 addr = 0xb1080000; addr < 0xb10c0000; addr += 4) {
617: WriteCommonBitmap32(addr, 0);
618: }
619: // 同時書き込みも元に戻す
620: bmsel = 0;
621:
622: textscr = NULL;
623: prev.reset();
624: }
625:
626: // エミュレータによる出力
627: // ts が出力するテキスト情報、oldts は前回の情報。
628: void
629: BitmapDevice::EmuConsoleUpdate()
630: {
631: const uint16 *text = textscr->GetBuf();
632:
633: // 12ドットフォントなので2文字ずつ (24ビットずつ) 更新する
634: for (int y = 0; y < screen_h; y++) {
635: for (int x = 0; x < screen_w; x += 2) {
636: // 2文字分の文字と属性
637: uint32 t32 = *(const uint32 *)&text[y * screen_w + x];
638: uint32 p32 = *(const uint32 *)&prev[y * screen_w + x];
639: if (t32 == p32) {
640: continue;
641: }
642: // ここからは変更があったので出力
643: *(uint32 *)&prev[y * screen_w + x] = t32;
644: // 2文字分として直接32ビットキャストして取り出した t32 だと
645: // エンディアンによって上下が変わるので、ここでは改めて
646: // 順序通りに2回取り出す
647: uint32 ch0 = text[y * screen_w + x];
648: uint32 ch1 = text[y * screen_w + x + 1];
649: // その文字のフォントデータ上でのオフセット
1.1.1.4 ! root 650: const uint8 * const fo0 = BuiltinCGROM::Get12x24(ch0 & 0xff);
! 651: const uint8 * const fo1 = BuiltinCGROM::Get12x24(ch1 & 0xff);
1.1.1.3 root 652:
653: // 1,2文字目なら true、3,4文字目なら false
654: bool evenpair = ((x & 2) == 0);
655:
656: // ビットマップ上の書き始めアドレス
657: uint32 addr = 0xb1080000 +
658: ((origin_y + y * 24) * 256) +
659: ((origin_x + x * 12) / 8);
660:
661: for (int i = 0; i < 24; i++) {
662: // その文字のフォントデータ
1.1.1.4 ! root 663: uint32 fdat0 = be16toh(*(const uint16*)(fo0 + i * 2));
! 664: uint32 fdat1 = be16toh(*(const uint16*)(fo1 + i * 2));
1.1.1.3 root 665:
666: // 属性はとりあえず反転のみサポート
667: if ((ch0 & TA::On)) {
668: fdat0 = ~fdat0 & 0xfff0;
669: }
670: if ((ch1 & TA::On)) {
671: fdat1 = ~fdat1 & 0xfff0;
672: }
673:
674: // 1文字目と2文字目を組み合わせて 24bit にする
675: uint32 data = (fdat0 << 8) | (fdat1 >> 4);
676:
677: // 書き込みは一度に 3バイトずつになるので、
678: // 効率化のため上位下位ペアで書き込みパターンを変える。
679: //
680: // | chr1 | chr2 | chr3 | chr4 |
681: // +----+----+----+----+----+----+
682: // | +0 | +1 | +2 | +3 | +4 | +5 |
683: // +----+----+----+----+----+----+
684: // |<-.Word->|<.B>|<.B>|<-.Word->|
685: if (evenpair) {
686: WriteCommonBitmap16(addr, data >> 8);
687: WriteCommonBitmap8(addr + 2, data & 0xff);
688: } else {
689: WriteCommonBitmap8(addr, data >> 16);
690: WriteCommonBitmap16(addr + 1, data & 0xffff);
691: }
692:
693: addr += 256; // 1ライン進める
694: }
695: }
696: }
697: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.