|
|
1.1 root 1: /*
2: * Copyright (c) 1980 Regents of the University of California.
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms are permitted provided
6: * that: (1) source distributions retain this entire copyright notice and
7: * comment, and (2) distributions including binaries display the following
8: * acknowledgement: ``This product includes software developed by the
9: * University of California, Berkeley and its contributors'' in the
10: * documentation or other materials provided with the distribution and in
11: * all advertising materials mentioning features or use of this software.
12: * Neither the name of the University nor the names of its contributors may
13: * be used to endorse or promote products derived from this software without
14: * specific prior written permission.
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
16: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
17: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18: */
19:
20: #ifndef lint
21: static char sccsid[] = "@(#)mkioconf.c 5.14 (Berkeley) 6/1/90";
22: #endif /* not lint */
23:
24: #include <stdio.h>
25: #include "y.tab.h"
26: #include "config.h"
27:
28: /*
29: * build the ioconf.c file
30: */
31: char *qu();
32: char *intv();
33:
34: #if MACHINE_VAX
35: vax_ioconf()
36: {
37: register struct device *dp, *mp, *np;
38: register int uba_n, slave;
39: FILE *fp;
40:
41: fp = fopen(path("ioconf.c"), "w");
42: if (fp == 0) {
43: perror(path("ioconf.c"));
44: exit(1);
45: }
46: fprintf(fp, "#include \"machine/pte.h\"\n");
47: fprintf(fp, "#include \"../sys/param.h\"\n");
48: fprintf(fp, "#include \"../sys/buf.h\"\n");
49: fprintf(fp, "#include \"../sys/map.h\"\n");
50: fprintf(fp, "#include \"../sys/vm.h\"\n");
51: fprintf(fp, "\n");
52: fprintf(fp, "#include \"../vaxmba/mbavar.h\"\n");
53: fprintf(fp, "#include \"../vaxuba/ubavar.h\"\n\n");
54: fprintf(fp, "\n");
55: fprintf(fp, "#define C (caddr_t)\n\n");
56: /*
57: * First print the mba initialization structures
58: */
59: if (seen_mba) {
60: for (dp = dtab; dp != 0; dp = dp->d_next) {
61: mp = dp->d_conn;
62: if (mp == 0 || mp == TO_NEXUS ||
63: !eq(mp->d_name, "mba"))
64: continue;
65: fprintf(fp, "extern struct mba_driver %sdriver;\n",
66: dp->d_name);
67: }
68: fprintf(fp, "\nstruct mba_device mbdinit[] = {\n");
69: fprintf(fp, "\t/* Device, Unit, Mba, Drive, Dk */\n");
70: for (dp = dtab; dp != 0; dp = dp->d_next) {
71: mp = dp->d_conn;
72: if (dp->d_unit == QUES || mp == 0 ||
73: mp == TO_NEXUS || !eq(mp->d_name, "mba"))
74: continue;
75: if (dp->d_addr) {
76: printf("can't specify csr address on mba for %s%d\n",
77: dp->d_name, dp->d_unit);
78: continue;
79: }
80: if (dp->d_vec != 0) {
81: printf("can't specify vector for %s%d on mba\n",
82: dp->d_name, dp->d_unit);
83: continue;
84: }
85: if (dp->d_drive == UNKNOWN) {
86: printf("drive not specified for %s%d\n",
87: dp->d_name, dp->d_unit);
88: continue;
89: }
90: if (dp->d_slave != UNKNOWN) {
91: printf("can't specify slave number for %s%d\n",
92: dp->d_name, dp->d_unit);
93: continue;
94: }
95: fprintf(fp, "\t{ &%sdriver, %d, %s,",
96: dp->d_name, dp->d_unit, qu(mp->d_unit));
97: fprintf(fp, " %s, %d },\n",
98: qu(dp->d_drive), dp->d_dk);
99: }
100: fprintf(fp, "\t0\n};\n\n");
101: /*
102: * Print the mbsinit structure
103: * Driver Controller Unit Slave
104: */
105: fprintf(fp, "struct mba_slave mbsinit [] = {\n");
106: fprintf(fp, "\t/* Driver, Ctlr, Unit, Slave */\n");
107: for (dp = dtab; dp != 0; dp = dp->d_next) {
108: /*
109: * All slaves are connected to something which
110: * is connected to the massbus.
111: */
112: if ((mp = dp->d_conn) == 0 || mp == TO_NEXUS)
113: continue;
114: np = mp->d_conn;
115: if (np == 0 || np == TO_NEXUS ||
116: !eq(np->d_name, "mba"))
117: continue;
118: fprintf(fp, "\t{ &%sdriver, %s",
119: mp->d_name, qu(mp->d_unit));
120: fprintf(fp, ", %2d, %s },\n",
121: dp->d_unit, qu(dp->d_slave));
122: }
123: fprintf(fp, "\t0\n};\n\n");
124: }
125: /*
126: * Now generate interrupt vectors for the unibus
127: */
128: for (dp = dtab; dp != 0; dp = dp->d_next) {
129: if (dp->d_vec != 0) {
130: struct idlst *ip;
131: mp = dp->d_conn;
132: if (mp == 0 || mp == TO_NEXUS ||
133: (!eq(mp->d_name, "uba") && !eq(mp->d_name, "bi")))
134: continue;
135: fprintf(fp,
136: "extern struct uba_driver %sdriver;\n",
137: dp->d_name);
138: fprintf(fp, "extern ");
139: ip = dp->d_vec;
140: for (;;) {
141: fprintf(fp, "X%s%d()", ip->id, dp->d_unit);
142: ip = ip->id_next;
143: if (ip == 0)
144: break;
145: fprintf(fp, ", ");
146: }
147: fprintf(fp, ";\n");
148: fprintf(fp, "int\t (*%sint%d[])() = { ", dp->d_name,
149: dp->d_unit);
150: ip = dp->d_vec;
151: for (;;) {
152: fprintf(fp, "X%s%d", ip->id, dp->d_unit);
153: ip = ip->id_next;
154: if (ip == 0)
155: break;
156: fprintf(fp, ", ");
157: }
158: fprintf(fp, ", 0 } ;\n");
159: }
160: }
161: fprintf(fp, "\nstruct uba_ctlr ubminit[] = {\n");
162: fprintf(fp, "/*\t driver,\tctlr,\tubanum,\talive,\tintr,\taddr */\n");
163: for (dp = dtab; dp != 0; dp = dp->d_next) {
164: mp = dp->d_conn;
165: if (dp->d_type != CONTROLLER || mp == TO_NEXUS || mp == 0 ||
166: !eq(mp->d_name, "uba"))
167: continue;
168: if (dp->d_vec == 0) {
169: printf("must specify vector for %s%d\n",
170: dp->d_name, dp->d_unit);
171: continue;
172: }
173: if (dp->d_addr == 0) {
174: printf("must specify csr address for %s%d\n",
175: dp->d_name, dp->d_unit);
176: continue;
177: }
178: if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
179: printf("drives need their own entries; dont ");
180: printf("specify drive or slave for %s%d\n",
181: dp->d_name, dp->d_unit);
182: continue;
183: }
184: if (dp->d_flags) {
185: printf("controllers (e.g. %s%d) ",
186: dp->d_name, dp->d_unit);
187: printf("don't have flags, only devices do\n");
188: continue;
189: }
190: fprintf(fp,
191: "\t{ &%sdriver,\t%d,\t%s,\t0,\t%sint%d, C 0%o },\n",
192: dp->d_name, dp->d_unit, qu(mp->d_unit),
193: dp->d_name, dp->d_unit, dp->d_addr);
194: }
195: fprintf(fp, "\t0\n};\n");
196: /* unibus devices */
197: fprintf(fp, "\nstruct uba_device ubdinit[] = {\n");
198: fprintf(fp,
199: "\t/* driver, unit, ctlr, ubanum, slave, intr, addr, dk, flags*/\n");
200: for (dp = dtab; dp != 0; dp = dp->d_next) {
201: mp = dp->d_conn;
202: if (dp->d_unit == QUES || dp->d_type != DEVICE || mp == 0 ||
203: mp == TO_NEXUS || mp->d_type == MASTER ||
204: eq(mp->d_name, "mba"))
205: continue;
206: np = mp->d_conn;
207: if (np != 0 && np != TO_NEXUS && eq(np->d_name, "mba"))
208: continue;
209: np = 0;
210: if (eq(mp->d_name, "uba")) {
211: if (dp->d_vec == 0) {
212: printf("must specify vector for device %s%d\n",
213: dp->d_name, dp->d_unit);
214: continue;
215: }
216: if (dp->d_addr == 0) {
217: printf("must specify csr for device %s%d\n",
218: dp->d_name, dp->d_unit);
219: continue;
220: }
221: if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
222: printf("drives/slaves can be specified ");
223: printf("only for controllers, ");
224: printf("not for device %s%d\n",
225: dp->d_name, dp->d_unit);
226: continue;
227: }
228: uba_n = mp->d_unit;
229: slave = QUES;
230: } else {
231: if ((np = mp->d_conn) == 0) {
232: printf("%s%d isn't connected to anything ",
233: mp->d_name, mp->d_unit);
234: printf(", so %s%d is unattached\n",
235: dp->d_name, dp->d_unit);
236: continue;
237: }
238: uba_n = np->d_unit;
239: if (dp->d_drive == UNKNOWN) {
240: printf("must specify ``drive number'' ");
241: printf("for %s%d\n", dp->d_name, dp->d_unit);
242: continue;
243: }
244: /* NOTE THAT ON THE UNIBUS ``drive'' IS STORED IN */
245: /* ``SLAVE'' AND WE DON'T WANT A SLAVE SPECIFIED */
246: if (dp->d_slave != UNKNOWN) {
247: printf("slave numbers should be given only ");
248: printf("for massbus tapes, not for %s%d\n",
249: dp->d_name, dp->d_unit);
250: continue;
251: }
252: if (dp->d_vec != 0) {
253: printf("interrupt vectors should not be ");
254: printf("given for drive %s%d\n",
255: dp->d_name, dp->d_unit);
256: continue;
257: }
258: if (dp->d_addr != 0) {
259: printf("csr addresses should be given only ");
260: printf("on controllers, not on %s%d\n",
261: dp->d_name, dp->d_unit);
262: continue;
263: }
264: slave = dp->d_drive;
265: }
266: fprintf(fp, "\t{ &%sdriver, %2d, %s,",
267: eq(mp->d_name, "uba") ? dp->d_name : mp->d_name, dp->d_unit,
268: eq(mp->d_name, "uba") ? " -1" : qu(mp->d_unit));
269: fprintf(fp, " %s, %2d, %s, C 0%-6o, %d, 0x%x },\n",
270: qu(uba_n), slave, intv(dp), dp->d_addr, dp->d_dk,
271: dp->d_flags);
272: }
273: fprintf(fp, "\t0\n};\n");
274: (void) fclose(fp);
275: }
276: #endif
277:
278: #if MACHINE_TAHOE
279: tahoe_ioconf()
280: {
281: register struct device *dp, *mp, *np;
282: register int vba_n, slave;
283: FILE *fp;
284:
285: fp = fopen(path("ioconf.c"), "w");
286: if (fp == 0) {
287: perror(path("ioconf.c"));
288: exit(1);
289: }
290: fprintf(fp, "#include \"../sys/param.h\"\n");
291: fprintf(fp, "#include \"machine/pte.h\"\n");
292: fprintf(fp, "#include \"../sys/buf.h\"\n");
293: fprintf(fp, "#include \"../sys/map.h\"\n");
294: fprintf(fp, "\n");
295: fprintf(fp, "#include \"../tahoevba/vbavar.h\"\n");
296: fprintf(fp, "\n");
297: fprintf(fp, "#define C (caddr_t)\n\n");
298: /*
299: * Now generate interrupt vectors for the versabus
300: */
301: for (dp = dtab; dp != 0; dp = dp->d_next) {
302: mp = dp->d_conn;
303: if (mp == 0 || mp == TO_NEXUS || !eq(mp->d_name, "vba"))
304: continue;
305: if (dp->d_vec != 0) {
306: struct idlst *ip;
307: fprintf(fp,
308: "extern struct vba_driver %sdriver;\n",
309: dp->d_name);
310: fprintf(fp, "extern ");
311: ip = dp->d_vec;
312: for (;;) {
313: fprintf(fp, "X%s%d()", ip->id, dp->d_unit);
314: ip = ip->id_next;
315: if (ip == 0)
316: break;
317: fprintf(fp, ", ");
318: }
319: fprintf(fp, ";\n");
320: fprintf(fp, "int\t (*%sint%d[])() = { ", dp->d_name,
321: dp->d_unit);
322: ip = dp->d_vec;
323: for (;;) {
324: fprintf(fp, "X%s%d", ip->id, dp->d_unit);
325: ip = ip->id_next;
326: if (ip == 0)
327: break;
328: fprintf(fp, ", ");
329: }
330: fprintf(fp, ", 0 } ;\n");
331: } else if (dp->d_type == DRIVER) /* devices w/o interrupts */
332: fprintf(fp,
333: "extern struct vba_driver %sdriver;\n",
334: dp->d_name);
335: }
336: fprintf(fp, "\nstruct vba_ctlr vbminit[] = {\n");
337: fprintf(fp, "/*\t driver,\tctlr,\tvbanum,\talive,\tintr,\taddr */\n");
338: for (dp = dtab; dp != 0; dp = dp->d_next) {
339: mp = dp->d_conn;
340: if (dp->d_type != CONTROLLER || mp == TO_NEXUS || mp == 0 ||
341: !eq(mp->d_name, "vba"))
342: continue;
343: if (dp->d_vec == 0) {
344: printf("must specify vector for %s%d\n",
345: dp->d_name, dp->d_unit);
346: continue;
347: }
348: if (dp->d_addr == 0) {
349: printf("must specify csr address for %s%d\n",
350: dp->d_name, dp->d_unit);
351: continue;
352: }
353: if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
354: printf("drives need their own entries; dont ");
355: printf("specify drive or slave for %s%d\n",
356: dp->d_name, dp->d_unit);
357: continue;
358: }
359: if (dp->d_flags) {
360: printf("controllers (e.g. %s%d) ",
361: dp->d_name, dp->d_unit);
362: printf("don't have flags, only devices do\n");
363: continue;
364: }
365: fprintf(fp,
366: "\t{ &%sdriver,\t%d,\t%s,\t0,\t%sint%d, C 0x%x },\n",
367: dp->d_name, dp->d_unit, qu(mp->d_unit),
368: dp->d_name, dp->d_unit, dp->d_addr);
369: }
370: fprintf(fp, "\t0\n};\n");
371: /* versabus devices */
372: fprintf(fp, "\nstruct vba_device vbdinit[] = {\n");
373: fprintf(fp,
374: "\t/* driver, unit, ctlr, vbanum, slave, intr, addr, dk, flags*/\n");
375: for (dp = dtab; dp != 0; dp = dp->d_next) {
376: mp = dp->d_conn;
377: if (dp->d_unit == QUES || dp->d_type != DEVICE || mp == 0 ||
378: mp == TO_NEXUS || mp->d_type == MASTER ||
379: eq(mp->d_name, "mba"))
380: continue;
381: np = mp->d_conn;
382: if (np != 0 && np != TO_NEXUS && eq(np->d_name, "mba"))
383: continue;
384: np = 0;
385: if (eq(mp->d_name, "vba")) {
386: if (dp->d_vec == 0)
387: printf(
388: "Warning, no interrupt vector specified for device %s%d\n",
389: dp->d_name, dp->d_unit);
390: if (dp->d_addr == 0) {
391: printf("must specify csr for device %s%d\n",
392: dp->d_name, dp->d_unit);
393: continue;
394: }
395: if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
396: printf("drives/slaves can be specified ");
397: printf("only for controllers, ");
398: printf("not for device %s%d\n",
399: dp->d_name, dp->d_unit);
400: continue;
401: }
402: vba_n = mp->d_unit;
403: slave = QUES;
404: } else {
405: if ((np = mp->d_conn) == 0) {
406: printf("%s%d isn't connected to anything ",
407: mp->d_name, mp->d_unit);
408: printf(", so %s%d is unattached\n",
409: dp->d_name, dp->d_unit);
410: continue;
411: }
412: vba_n = np->d_unit;
413: if (dp->d_drive == UNKNOWN) {
414: printf("must specify ``drive number'' ");
415: printf("for %s%d\n", dp->d_name, dp->d_unit);
416: continue;
417: }
418: /* NOTE THAT ON THE UNIBUS ``drive'' IS STORED IN */
419: /* ``SLAVE'' AND WE DON'T WANT A SLAVE SPECIFIED */
420: if (dp->d_slave != UNKNOWN) {
421: printf("slave numbers should be given only ");
422: printf("for massbus tapes, not for %s%d\n",
423: dp->d_name, dp->d_unit);
424: continue;
425: }
426: if (dp->d_vec != 0) {
427: printf("interrupt vectors should not be ");
428: printf("given for drive %s%d\n",
429: dp->d_name, dp->d_unit);
430: continue;
431: }
432: if (dp->d_addr != 0) {
433: printf("csr addresses should be given only ");
434: printf("on controllers, not on %s%d\n",
435: dp->d_name, dp->d_unit);
436: continue;
437: }
438: slave = dp->d_drive;
439: }
440: fprintf(fp, "\t{ &%sdriver, %2d, %s,",
441: eq(mp->d_name, "vba") ? dp->d_name : mp->d_name, dp->d_unit,
442: eq(mp->d_name, "vba") ? " -1" : qu(mp->d_unit));
443: fprintf(fp, " %s, %2d, %s, C 0x%-6x, %d, 0x%x },\n",
444: qu(vba_n), slave, intv(dp), dp->d_addr, dp->d_dk,
445: dp->d_flags);
446: }
447: fprintf(fp, "\t0\n};\n");
448: (void) fclose(fp);
449: }
450: #endif
451:
452: #if MACHINE_HP300
453: hp300_ioconf()
454: {
455: register struct device *dp, *mp, *np;
456: register int hpib, slave;
457: FILE *fp;
458: extern char *wnum();
459:
460: fp = fopen(path("ioconf.c"), "w");
461: if (fp == 0) {
462: perror(path("ioconf.c"));
463: exit(1);
464: }
465: fprintf(fp, "#include \"machine/pte.h\"\n");
466: fprintf(fp, "#include \"../sys/param.h\"\n");
467: fprintf(fp, "#include \"../sys/buf.h\"\n");
468: fprintf(fp, "#include \"../sys/map.h\"\n");
469: fprintf(fp, "#include \"../sys/vm.h\"\n");
470: fprintf(fp, "\n");
471: fprintf(fp, "#include \"../hpdev/device.h\"\n\n");
472: fprintf(fp, "\n");
473: fprintf(fp, "#define C (caddr_t)\n");
474: fprintf(fp, "#define D (struct driver *)\n\n");
475: /*
476: * First print the hpib controller initialization structures
477: */
478: for (dp = dtab; dp != 0; dp = dp->d_next) {
479: mp = dp->d_conn;
480: if (dp->d_unit == QUES || mp == 0)
481: continue;
482: fprintf(fp, "extern struct driver %sdriver;\n", dp->d_name);
483: }
484: fprintf(fp, "\nstruct hp_ctlr hp_cinit[] = {\n");
485: fprintf(fp, "/*\tdriver,\t\tunit,\talive,\taddr,\tflags */\n");
486: for (dp = dtab; dp != 0; dp = dp->d_next) {
487: mp = dp->d_conn;
488: if (dp->d_unit == QUES ||
489: dp->d_type != MASTER && dp->d_type != CONTROLLER)
490: continue;
491: if (mp != TO_NEXUS) {
492: printf("%s%s must be attached to an sc (nexus)\n",
493: dp->d_name, wnum(dp->d_unit));
494: continue;
495: }
496: if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
497: printf("can't specify drive/slave for %s%s\n",
498: dp->d_name, wnum(dp->d_unit));
499: continue;
500: }
501: fprintf(fp,
502: "\t{ &%sdriver,\t%d,\t0,\tC 0x%x,\t0x%x },\n",
503: dp->d_name, dp->d_unit, dp->d_addr, dp->d_flags);
504: }
505: fprintf(fp, "\t0\n};\n");
506: /* devices */
507: fprintf(fp, "\nstruct hp_device hp_dinit[] = {\n");
508: fprintf(fp,
509: "/*driver,\tcdriver,\tunit,\tctlr,\tslave,\taddr,\tdk,\tflags*/\n");
510: for (dp = dtab; dp != 0; dp = dp->d_next) {
511: mp = dp->d_conn;
512: if (mp == 0 || dp->d_type != DEVICE || hpbadslave(mp, dp))
513: continue;
514: if (mp == TO_NEXUS) {
515: if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
516: printf("can't specify drive/slave for %s%s\n",
517: dp->d_name, wnum(dp->d_unit));
518: continue;
519: }
520: slave = QUES;
521: hpib = QUES;
522: } else {
523: if (dp->d_addr != 0) {
524: printf("can't specify sc for device %s%s\n",
525: dp->d_name, wnum(dp->d_unit));
526: continue;
527: }
528: if (mp->d_type == CONTROLLER) {
529: if (dp->d_drive == UNKNOWN) {
530: printf("must specify drive for %s%s\n",
531: dp->d_name, wnum(dp->d_unit));
532: continue;
533: }
534: slave = dp->d_drive;
535: } else {
536: if (dp->d_slave == UNKNOWN) {
537: printf("must specify slave for %s%s\n",
538: dp->d_name, wnum(dp->d_unit));
539: continue;
540: }
541: slave = dp->d_slave;
542: }
543: hpib = mp->d_unit;
544: }
545: fprintf(fp, "{ &%sdriver,\t", dp->d_name);
546: if (mp == TO_NEXUS)
547: fprintf(fp, "D 0x0,\t");
548: else
549: fprintf(fp, "&%sdriver,", mp->d_name);
550: fprintf(fp, "\t%d,\t%d,\t%d,\tC 0x%x,\t%d,\t0x%x },\n",
551: dp->d_unit, hpib, slave,
552: dp->d_addr, dp->d_dk, dp->d_flags);
553: }
554: fprintf(fp, "0\n};\n");
555: (void) fclose(fp);
556: }
557:
558: #define ishpibdev(n) (eq(n,"rd") || eq(n,"ct") || eq(n,"mt") || eq(n,"ppi"))
559: #define isscsidev(n) (eq(n,"sd") || eq(n,"st"))
560:
561: hpbadslave(mp, dp)
562: register struct device *dp, *mp;
563: {
564: extern char *wnum();
565:
566: if (mp == TO_NEXUS && ishpibdev(dp->d_name) ||
567: mp != TO_NEXUS && eq(mp->d_name, "hpib") &&
568: !ishpibdev(dp->d_name)) {
569: printf("%s%s must be attached to an hpib\n",
570: dp->d_name, wnum(dp->d_unit));
571: return (1);
572: }
573: if (mp == TO_NEXUS && isscsidev(dp->d_name) ||
574: mp != TO_NEXUS && eq(mp->d_name, "scsi") &&
575: !isscsidev(dp->d_name)) {
576: printf("%s%s must be attached to a scsi\n",
577: dp->d_name, wnum(dp->d_unit));
578: return (1);
579: }
580: return (0);
581: }
582:
583: char *
584: wnum(num)
585: {
586:
587: if (num == QUES || num == UNKNOWN)
588: return ("?");
589: (void) sprintf(errbuf, "%d", num);
590: return (errbuf);
591: }
592: #endif
593:
594: char *
595: intv(dev)
596: register struct device *dev;
597: {
598: static char buf[20];
599:
600: if (dev->d_vec == 0)
601: return (" 0");
602: (void) sprintf(buf, "%sint%d", dev->d_name, dev->d_unit);
603: return (buf);
604: }
605:
606: char *
607: qu(num)
608: {
609:
610: if (num == QUES)
611: return ("'?'");
612: if (num == UNKNOWN)
613: return (" -1");
614: (void) sprintf(errbuf, "%3d", num);
615: return (errbuf);
616: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.