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