|
|
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;
1.1.1.5 ! root 52: monitor_size = nnSize(42, 11);
1.1 root 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:
1.1.1.5 ! root 322: void
! 323: BitmapDevice::MonitorUpdate(TextScreen& monitor)
1.1 root 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:
364: // 全画面の更新フラグを立てる
365: void
366: BitmapDevice::Invalidate()
367: {
368: for (int i = 0; i < 1024; i++) {
369: atomic_dirty[i] = 1;
370: }
371: }
372:
373: // RFCNT への書き込み
374: void
375: BitmapDevice::WriteRFCNT(uint32 data)
376: {
377: // レジスタ値
378: rfcnt = data;
379:
380: // 水平オフセットを計算
381: // XXX マニュアルでは -8 まで指定できるように読めるが
382: // 指定する方法が分からない。
383: // XXX 193 以上を指定するとどうなるのか (とりあえずほっとく)
384: // 実機の挙動を目分量で見ると、右にはみ出た部分は
385: // 256 ラスタ先のラスタデータを描画しているようだ。
386: hscroll = ((rfcnt >> 16) & 0xff) + 8;
387: hscroll *= 4;
388:
389: // ラスタ(垂直)カウンタ
1.1.1.2 root 390: vscroll = (rfcnt + 26) & 0x3ff;
1.1 root 391:
392: // 全画面更新
393: Invalidate();
394:
395: putlog(1, "RFCNT <- $%08x (水平 %ddot, 垂直 %ddot)",
396: data, hscroll, vscroll);
397: }
398:
399: // 共通ビットマッププレーン バイト書き込み
400: void
401: BitmapDevice::WriteCommonBitmap8(uint32 addr, uint32 data)
402: {
403: uint32 offset = addr - 0xb1080000;
404: putlog(2, "共通プレーン書き込み $%08x <- $%02x", addr, data);
405: for (int i = 0; i < nplane; i++) {
406: if ((bmsel & (1 << i))) {
407: WritePlane8(i, offset, data);
408: }
409: }
410: }
411:
412: // 共通ビットマッププレーン ワード書き込み
413: void
414: BitmapDevice::WriteCommonBitmap16(uint32 addr, uint32 data)
415: {
416: uint32 offset = addr - 0xb1080000;
417: putlog(2, "共通プレーン書き込み $%08x <- $%04x", addr, data);
418: for (int i = 0; i < nplane; i++) {
419: if ((bmsel & (1 << i))) {
420: WritePlane16(i, offset, data);
421: }
422: }
423: }
424:
425: // 共通ビットマッププレーン ロング書き込み
426: void
427: BitmapDevice::WriteCommonBitmap32(uint32 addr, uint32 data)
428: {
429: uint32 offset = addr - 0xb1080000;
430: putlog(2, "共通プレーン書き込み $%08x <- $%08x", addr, data);
431: for (int i = 0; i < nplane; i++) {
432: if ((bmsel & (1 << i))) {
433: WritePlane32(i, offset, data);
434: }
435: }
436: }
437:
438: static inline uint32
439: RasterOp(uint rop, uint32 data, uint32 m)
440: {
441: switch (rop) {
442: case 0: // ZERO
443: data = 0;
444: break;
445: case 1: // AND1
446: data &= m;
447: break;
448: case 2: // AND2
449: data = ~data & m;
450: break;
451: case 3: // 使用不可
452: // 本当はここにシリアルモード変更コマンドがあるらしい
453: data = m;
454: break;
455: case 4: // AND3
456: data &= ~m;
457: break;
458: case 5: // THROUGH
459: break;
460: case 6: // EOR
461: data ^= m;
462: break;
463: case 7: // OR1
464: data |= m;
465: break;
466: case 8: // NOR
467: data = ~data & ~m;
468: break;
469: case 9: // ENOR
470: data = (data & m) | (~data & ~m);
471: break;
472: case 10: // INV1
473: data = ~data;
474: break;
475: case 11: // OR2
476: data = ~data | m;
477: break;
478: case 12: // INV2
479: data = ~m;
480: break;
481: case 13: // OR3
482: data |= ~m;
483: break;
484: case 14: // NAND
485: data = ~data | ~m;
486: break;
487: case 15: // ONE
488: data = 0xffffffff;
489: break;
490: }
491: return data;
492: }
493:
494: // ROP 文字列を返す
495: const char *
496: BitmapDevice::RopStr(uint rop)
497: {
498: static const char * ropstr[] = {
499: "ZERO",
500: "AND1",
501: "AND2",
502: "(3)",
503: "AND3",
504: "THRU", // THROUGH
505: "EOR",
506: "OR1",
507: "NOR",
508: "ENOR",
509: "INV1",
510: "OR2",
511: "INV2",
512: "OR3",
513: "NAND",
514: "ONE",
515: };
516: return ropstr[rop];
517: }
518:
519: // プレーン バイト書き込み
520: void
521: BitmapDevice::WritePlane8(uint plane, uint32 offset, uint32 data)
522: {
523: uint32 memoffset = plane * 0x40000 + offset;
524: uint32 m;
525: uint32 mask8;
526:
527: m = mem[HLB(memoffset)];
528: data = RasterOp(fcs[plane], data, m);
529: mask8 = mask[plane] >> (24 - (memoffset & 3) * 8);
530: mem[HLB(memoffset)] = (data & mask8) | (m & ~mask8);
531: atomic_dirty[offset / 256] = 1;
532: }
533:
534: // プレーン ワード書き込み
535: void
536: BitmapDevice::WritePlane16(uint plane, uint32 offset, uint32 data)
537: {
538: uint32 memoffset = plane * 0x40000 + offset;
539: uint32 m;
540: uint32 mask16;
541:
542: m = *(uint16 *)&mem[HLW(memoffset)];
543: data = RasterOp(fcs[plane], data, m);
544: if ((memoffset & 2) == 0) {
545: // 上位ワード
546: mask16 = mask[plane] >> 16;
547: } else {
548: // 下位ワード
549: mask16 = mask[plane];
550: }
551: *(uint16 *)&mem[HLW(memoffset)] = (data & mask16) | (m & ~mask16);
552: atomic_dirty[offset / 256] = 1;
553: }
554:
555: // プレーン ロングワード書き込み
556: void
557: BitmapDevice::WritePlane32(uint plane, uint32 offset, uint32 data)
558: {
559: uint32 memoffset = plane * 0x40000 + offset;
560: uint32 m;
561:
562: m = *(uint32 *)&mem[memoffset];
563: data = RasterOp(fcs[plane], data, m);
564: *(uint32 *)&mem[memoffset] = (data & mask[plane]) | (m & ~mask[plane]);
565: atomic_dirty[offset / 256] = 1;
566: }
1.1.1.3 root 567:
568:
569: // エミュレータによる画面出力
570: //
571: // VM 内からの操作ではなく、エミュレータ本体から画面を操作する
572: // エミュレーション ROM によるモニタコンソール用の機能。
573: // サービスを使いたい側 (EmuROM など) が任意の大きさの TextScreen を
574: // 用意し EmuConsoleOpen() をコールすると (現在のパレットのまま) 画面を
575: // クリアし、画面中央をこのテキスト VRAM の表示エリアとみなす。
576: // 以降は EmuConsoleUpdate() で TextScreen の内容に従って画面を更新し、
577: // 使用後は EmuConsoleClose() で再び画面をクリア (およびリソースの解放) を
578: // すること。
579:
580: // エミュレータによる出力開始
581: void
582: BitmapDevice::EmuConsoleOpen(TextScreen *t)
583: {
584: textscr = t;
585:
586: screen_w = textscr->GetCol();
587: screen_h = textscr->GetRow();
588: prev.reset(new uint16[screen_w * screen_h]);
589: for (int i = 0; i < screen_w * screen_h; i++) {
590: prev[i] = 0x0020;
591: }
592:
593: // ざっくりセンタリングする
594: origin_x = (1280 - screen_w * 12) / 2;
595: origin_y = (1024 - screen_h * 24) / 2;
596: // ただし横は 48ドット境界から始める
597: // こうすると1(,2)文字目の24ビットが必ずワード境界から始まる。
598: origin_x = (origin_x / 48) * 48;
599:
600: // 同時書き込み設定
601: bmsel = (1 << nplane) - 1;
602:
603: // 全画面をクリア
604: for (uint32 addr = 0xb1080000; addr < 0xb10c0000; addr += 4) {
605: WriteCommonBitmap32(addr, 0);
606: }
607: }
608:
609: // エミュレータによる出力終了
610: void
611: BitmapDevice::EmuConsoleClose()
612: {
613: // 全画面をクリア
614: for (uint32 addr = 0xb1080000; addr < 0xb10c0000; addr += 4) {
615: WriteCommonBitmap32(addr, 0);
616: }
617: // 同時書き込みも元に戻す
618: bmsel = 0;
619:
620: textscr = NULL;
621: prev.reset();
622: }
623:
624: // エミュレータによる出力
625: // ts が出力するテキスト情報、oldts は前回の情報。
626: void
627: BitmapDevice::EmuConsoleUpdate()
628: {
629: const uint16 *text = textscr->GetBuf();
630:
631: // 12ドットフォントなので2文字ずつ (24ビットずつ) 更新する
632: for (int y = 0; y < screen_h; y++) {
633: for (int x = 0; x < screen_w; x += 2) {
634: // 2文字分の文字と属性
635: uint32 t32 = *(const uint32 *)&text[y * screen_w + x];
636: uint32 p32 = *(const uint32 *)&prev[y * screen_w + x];
637: if (t32 == p32) {
638: continue;
639: }
640: // ここからは変更があったので出力
641: *(uint32 *)&prev[y * screen_w + x] = t32;
642: // 2文字分として直接32ビットキャストして取り出した t32 だと
643: // エンディアンによって上下が変わるので、ここでは改めて
644: // 順序通りに2回取り出す
645: uint32 ch0 = text[y * screen_w + x];
646: uint32 ch1 = text[y * screen_w + x + 1];
647: // その文字のフォントデータ上でのオフセット
1.1.1.4 root 648: const uint8 * const fo0 = BuiltinCGROM::Get12x24(ch0 & 0xff);
649: const uint8 * const fo1 = BuiltinCGROM::Get12x24(ch1 & 0xff);
1.1.1.3 root 650:
651: // 1,2文字目なら true、3,4文字目なら false
652: bool evenpair = ((x & 2) == 0);
653:
654: // ビットマップ上の書き始めアドレス
655: uint32 addr = 0xb1080000 +
656: ((origin_y + y * 24) * 256) +
657: ((origin_x + x * 12) / 8);
658:
659: for (int i = 0; i < 24; i++) {
660: // その文字のフォントデータ
1.1.1.4 root 661: uint32 fdat0 = be16toh(*(const uint16*)(fo0 + i * 2));
662: uint32 fdat1 = be16toh(*(const uint16*)(fo1 + i * 2));
1.1.1.3 root 663:
664: // 属性はとりあえず反転のみサポート
665: if ((ch0 & TA::On)) {
666: fdat0 = ~fdat0 & 0xfff0;
667: }
668: if ((ch1 & TA::On)) {
669: fdat1 = ~fdat1 & 0xfff0;
670: }
671:
672: // 1文字目と2文字目を組み合わせて 24bit にする
673: uint32 data = (fdat0 << 8) | (fdat1 >> 4);
674:
675: // 書き込みは一度に 3バイトずつになるので、
676: // 効率化のため上位下位ペアで書き込みパターンを変える。
677: //
678: // | chr1 | chr2 | chr3 | chr4 |
679: // +----+----+----+----+----+----+
680: // | +0 | +1 | +2 | +3 | +4 | +5 |
681: // +----+----+----+----+----+----+
682: // |<-.Word->|<.B>|<.B>|<-.Word->|
683: if (evenpair) {
684: WriteCommonBitmap16(addr, data >> 8);
685: WriteCommonBitmap8(addr + 2, data & 0xff);
686: } else {
687: WriteCommonBitmap8(addr, data >> 16);
688: WriteCommonBitmap16(addr + 1, data & 0xffff);
689: }
690:
691: addr += 256; // 1ライン進める
692: }
693: }
694: }
695: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.