This repository has been archived by the owner on Dec 24, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 91
/
Copy pathchipset.js
4459 lines (4218 loc) · 188 KB
/
chipset.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* @fileoverview Implements the PCjs ChipSet component.
* @author <a href="mailto:[email protected]">Jeff Parsons</a>
* @version 1.0
* @suppress {missingProperties}
* Created 2012-Sep-14
*
* Copyright © 2012-2014 Jeff Parsons <[email protected]>
*
* This file is part of PCjs, which is part of the JavaScript Machines Project (aka JSMachines)
* at <http://jsmachines.net/> and <http://pcjs.org/>.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCjs. If not,
* see <http://www.gnu.org/licenses/gpl.html>.
*
* You are required to include the above copyright notice in every source code file of every
* copy or modified version of this work, and to display that copyright notice on every screen
* that loads or runs any version of this software (see Computer.sCopyright).
*
* Some PCjs files also attempt to load external resource files, such as character-image files,
* ROM files, and disk image files. Those external resource files are not considered part of the
* PCjs program for purposes of the GNU General Public License, and the author does not claim
* any copyright as to their contents.
*/
"use strict";
if (typeof module !== 'undefined') {
var str = require("../../shared/lib/strlib");
var usr = require("../../shared/lib/usrlib");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var State = require("./state");
}
/**
* ChipSet(parmsChipSet)
*
* The ChipSet component has the following component-specific (parmsChipSet) properties:
*
* model: 5150, 5160 or 5170 (should correspond to a ChipSet.MODEL_* constant)
* sw1: 8-character binary string representing the SW1 DIP switches (SW1[1-8])
* sw2: 8-character binary string representing the SW2 DIP switches (SW2[1-8]) (MODEL_5150 only)
* sound: true to enable (experimental) sound support (default); false to disable
* scaleTimers: true to divide timer cycle counts by the CPU's cycle multiplier (default is false)
* fdrives: 0-4 floppy drives (default is 2 if no sw1 value provided)
* monitor: none|tv|color|mono (default is mono if no sw1 value provided)
* rtcDate: optional RTC date to be used on resets; use the ISO 8601 format; eg: "2014-10-01T08:00:00-0700"
*
* The conventions used for the sw1 and sw2 strings are that the left-most character represents DIP switch [1],
* the right-most character represents DIP switch [8], and "1" means the DIP switch is ON and "0" means it is OFF.
*
* Internally, we convert the above strings into binary values that the 8255A PPI returns, where DIP switch [1]
* is bit 0 and DIP switch [8] is bit 7, and 0 indicates the switch is ON and 1 indicates it is OFF.
*
* For reference, here's how the SW1 and SW2 switches correspond to the internal 8255A PPI bit values:
*
* SW1[1] (bit 0) "0xxxxxxx" (1): IPL, "1xxxxxxx" (0): No IPL
* SW1[2] (bit 1) reserved
* SW1[3,4] (bits 3-2) "xx11xxxx" (00): 16Kb, "xx01xxxx" (01): 32Kb, "xx10xxxx" (10): 48Kb, "xx00xxxx" (11): 64Kb
* SW1[5,6] (bits 5-4) "xxxx11xx" (00): none, "xxxx01xx" (01): tv, "xxxx10xx" (10): color, "xxxx00xx" (11): mono
* SW1[7,8] (bits 7-6) "xxxxxx11" (00): 1 FD, "xxxxxx01" (01): 2 FD, "xxxxxx10" (10): 3 FD, "xxxxxx00" (11): 4 FD
*
* Note: FD refers to floppy drive, and IPL refers to an "Initial Program Load" floppy drive.
*
* SW2[1-4] (bits 3-0) "NNNNxxxx": number of 32Kb blocks of I/O expansion RAM present
*
* TODO: There are cryptic references to SW2[5] in the original (5150) TechRef, and apparently the 8255A PPI can
* be programmed to return it (which we support), but its purpose remains unclear to me (see PPI_B.ENABLE_SW2).
*
* For example, sw1="01110011" indicates that all SW1 DIP switches are ON, except for SW1[1], SW1[5] and SW1[6],
* which are OFF. Internally, the order of these bits must reversed (to 11001110) and then inverted (to 00110001)
* to yield the value that the 8255A PPI returns. Reading the final value right-to-left, 00110001 indicates an
* IPL floppy drive, 1X of RAM (where X is 16Kb on a MODEL_5150 and 64Kb on a MODEL_5160), MDA, and 1 floppy drive.
*
* WARNING: It is possible to set SW1 to indicate more memory than the RAM component has been configured to provide.
* This is a configuration error which will cause the machine to crash after reporting a "201" error code (memory
* test failure), which is presumably what a real machine would do if it was similarly misconfigured. Surprisingly,
* the BIOS forges ahead, setting SP to the top of the memory range indicated by SW1 (via INT 0x12), but the lack of
* a valid stack causes the system to crash after the next IRET. The BIOS should have either halted or modified
* the actual memory size to match the results of the memory test.
*
* This component provides support for many of the following components (except where a separate component is noted).
* This list is taken from p.1-8 ("System Unit") of the IBM 5160 (PC XT) Technical Reference Manual (as revised
* April 1983), only because I didn't see a similar listing in the original 5150 TechRef.
*
* Port(s) Description
* ------- -----------
* 000-00F DMA Chip 8237A-5 [see below]
* 020-021 Interrupt 8259A [see below]
* 040-043 Timer 8253-5 [see below]
* 060-063 PPI 8255A-5 [see below]
* 080-083 DMA Page Registers [see below]
* 0Ax [1] NMI Mask Register [see below]
* 0Cx Reserved
* 0Ex Reserved
* 200-20F Game Control
* 210-217 Expansion Unit
* 220-24F Reserved
* 278-27F Reserved
* 2F0-2F7 Reserved
* 2F8-2FF Asynchronous Communications (Secondary) [see the SerialPort component]
* 300-31F Prototype Card
* 320-32F Hard Drive Controller (XTC) [see the HDC component]
* 378-37F Printer
* 380-38C [2] SDLC Communications
* 380-389 [2] Binary Synchronous Communications (Secondary)
* 3A0-3A9 Binary Synchronous Communications (Primary)
* 3B0-3BF IBM Monochrome Display/Printer [see the Video component]
* 3C0-3CF Reserved
* 3D0-3DF Color/Graphics (Motorola 6845) [see the Video component]
* 3EO-3E7 Reserved
* 3FO-3F7 Floppy Drive Controller [see the FDC component]
* 3F8-3FF Asynchronous Communications (Primary) [see the SerialPort component]
*
* [1] At power-on time, NMI is masked off, perhaps because models 5150 and 5160 also tie coprocessor
* interrupts to NMI. Suppressing NMI by default seems odd, because that would also suppress memory
* parity errors. TODO: Determine whether "power-on time" refers to the initial power-on state of the
* NMI Mask Register or the state that the BIOS "POST" (Power-On Self-Test) sets.
*
* [2] These devices cannot be used together since their port addresses overlap.
*
* MODEL_5170 Description
* ---------- -----------
* 070 [3] CMOS Address ChipSet.CMOS.ADDR.PORT
* 071 CMOS Data ChipSet.CMOS.DATA.PORT
* 0F0 Coprocessor Clear Busy (output 0x00)
* 0F1 Coprocessor Reset (output 0x00)
* 1F0-1F7 Hard Drive Controller (ATC) [see the HDC component]
*
* [3] Port 0x70 doubles as the NMI Mask Register: output a CMOS address with bit 7 clear to enable NMI
* or with bit 7 set to disable NMI (apparently the inverse of the older NMI Mask Register at port 0xA0).
* Also, apparently unlike previous models, the MODEL_5170 POST leaves NMI enabled. And fortunately, the
* coprocessor interrupt line is no longer tied to NMI (it uses IRQ 13).
*
* @constructor
* @extends Component
* @param {Object} parmsChipSet
*/
function ChipSet(parmsChipSet)
{
Component.call(this, "ChipSet", parmsChipSet, ChipSet);
this.model = parmsChipSet['model'];
this.model = (this.model !== undefined? parseInt(this.model, 10) : ChipSet.MODEL_5150);
/*
* SW1 describes the number of floppy drives, the amount of base memory, the primary monitor type,
* and (on the MODEL_5160) whether or not a coprocessor is installed. If no SW1 settings are provided,
* we look for individual 'fdrives' and 'monitor' settings and build a default SW1 value.
*
* The defaults below select max memory, monochrome monitor (EGA monitor for MODEL_5170), and two floppies.
* Don't get too excited about "max memory" either: on a MODEL_5150, the max was 64Kb, and on a MODEL_5160,
* the max was 256Kb. However, the RAM component is free to install as much base memory as it likes,
* overriding the SW1 memory setting.
*
* Given that the ROM BIOS is hard-coded to load boot sectors @0000:7C00, the minimum amount of system RAM
* required to boot is therefore 32Kb. Whether that's actually enough to run any or all versions of PC-DOS is
* a separate question. FYI, with only 16Kb, the ROM BIOS will still try to boot, and fail miserably.
*/
this.sw1Init = 0;
var sw1 = parmsChipSet['sw1'];
if (sw1) {
this.sw1Init = this.parseSwitches(sw1, ChipSet.PPI_SW.MEMORY.X4 | ChipSet.PPI_SW.MONITOR.MONO);
} else {
var nDrives = parmsChipSet['fdrives'] || 2;
if (nDrives) {
this.sw1Init |= ChipSet.PPI_SW.FDRIVE.IPL;
nDrives--;
this.sw1Init |= ((nDrives & 0x3) << ChipSet.PPI_SW.FDRIVE.SHIFT);
}
var sMonitor = parmsChipSet['monitor'] || (this.model < ChipSet.MODEL_5170? "mono" : "ega");
if (sMonitor && ChipSet.aMonitorSwitches[sMonitor] !== undefined) {
this.sw1Init |= (ChipSet.aMonitorSwitches[sMonitor] << ChipSet.PPI_SW.MONITOR.SHIFT);
}
}
/*
* SW2 describes the number of 32Kb blocks of I/O expansion RAM that's present in the system. The MODEL_5150 ROM BIOS
* only checked/supported the first four switches, so the maximum amount of additional RAM specifiable was 15 * 32Kb,
* or 480Kb. With a maximum of 64Kb on the motherboard, the MODEL_5150 ROM BIOS could support a grand total of 544Kb.
*
* For MODEL_5160 (PC XT) and up, memory expansion cards had their own configuration switches, and the motherboard SW2
* switches for I/O expansion RAM were eliminated. Instead, the ROM BIOS scans the entire address space (up to 0xA0000)
* looking for additional memory. As a result, the only mechanism we provide for adding RAM (above the maximum of 256Kb
* supported on the motherboard) is the "size" parameter of the RAM component. NOTE: If you use the "size" parameter,
* you will not be able to dynamically alter the memory configuration; the RAM component will ignore any changes to SW1.
*/
this.sw2Init = this.parseSwitches(parmsChipSet['sw2'] || "11110000", 0);
/*
* The SW1 memory setting is actually just a multiplier: it's multiplied by 16Kb on a MODEL_5150, 64Kb otherwise.
*/
this.kbSW = (this.model == ChipSet.MODEL_5150? 16 : 64);
this.cDMACs = this.cPICs = 1;
if (this.model >= ChipSet.MODEL_5170) {
this.cDMACs = this.cPICs = 2;
}
this.fScaleTimers = parmsChipSet['scaleTimers'] || false;
this.sRTCDate = parmsChipSet['rtcDate'];
/*
* Here, I'm finally getting around to trying the Web Audio API. Fortunately, based on what little I know about
* sound generation, using the API to make the same noises as the IBM PC speaker should be straightforward.
*
* To start, we create an audio context, unless the 'sound' parameter has been explicitly set to false.
*
* From:
*
* http://developer.apple.com/library/safari/#documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/PlayingandSynthesizingSounds/PlayingandSynthesizingSounds.html
*
* "Similar to how HTML5 canvas requires a context on which lines and curves are drawn, Web Audio requires an audio context
* on which sounds are played and manipulated. This context will be the parent object of further audio objects to come....
* Your audio context is typically created when your page initializes and should be long-lived. You can play multiple sounds
* coming from multiple sources within the same context, so it is unnecessary to create more than one audio context per page."
*/
this.fSpeaker = false;
if (parmsChipSet['sound']) {
if (window && 'webkitAudioContext' in window) {
// noinspection JSPotentiallyInvalidConstructorUsage
this.contextAudio = new webkitAudioContext();
} else {
if (DEBUG) this.log("webkitAudioContext not available");
}
}
/*
* I used to defer ChipSet's reset() to powerUp(), which then gave us the option of doing either
* reset() OR restore(), instead of both. However, on MODEL_5170 machines, the initial CMOS data
* needs to be created earlier, so that when other components are initializing their state (eg, when
* HDC calls setCMOSDriveType() or RAM calls addCMOSMemory()), the CMOS will be ready to take their calls.
*/
this.reset();
this.setReady();
}
Component.subclass(Component, ChipSet);
/*
* Supported Models
*
* Unless otherwise noted, all BIOS references refer to the *original* BIOS released with each model
*/
ChipSet.MODEL_5150 = 5150; // used in reference to the 1st 5150 BIOS, dated Apr 24, 1981
ChipSet.MODEL_5160 = 5160; // used in reference to the 1st 5160 BIOS, dated Nov 8, 1982
ChipSet.MODEL_5170 = 5170; // used in reference to the 1st 5170 BIOS, dated Jan 10, 1984
/*
* The following are fake model numbers, used only to document issues/features of note in later BIOS revisions
*/
ChipSet.MODEL_5170_REV2 = 5170.2; // used in reference to the 2nd 5170 BIOS, dated Jun 10, 1985
ChipSet.MODEL_5170_REV3 = 5170.3; // used in reference to the 3rd 5170 BIOS, dated Nov 15, 1985
/*
* Values returned by ChipSet.getSWVideoMonitor()
*/
ChipSet.MONITOR = {
NONE: 0,
TV: 1, // Composite monitor (lower resolution; no support)
COLOR: 2, // Color Display (5153)
MONO: 3, // Monochrome Display (5151)
EGACOLOR: 4, // Enhanced Color Display (5154) in High-Res Mode
EGAEMULATION: 6 // Enhanced Color Display (5154) in Emulation Mode
};
/*
* Lookup table for converting ChipSet "monitor" values into the corresponding SW1 switch bits
* (they must be shifted left by ChipSet.PPI_SW.MONITOR.SHIFT before OR'ing them into sw1/sw1Init).
*/
ChipSet.aMonitorSwitches = {
"none": 0x0,
"tv": 0x1,
"color": 0x2,
"mono": 0x3,
"ega": 0x0
};
/*
* 8237A DMA Controller (DMAC) I/O ports
*
* MODEL_5150 and up uses DMA channel 0 for memory refresh cycles and channel 2 for the FDC
* MODEL_5160 and up uses DMA channel 3 for HDC transfers (XTC only)
* MODEL_5170 and up contain *two* DMA Controllers, which we refer to as DMA0 and DMA1; channel 4
* on DMA1 is used to "cascade" channels 0-3 from DMA0, so only channels 5-7 are available on DMA1
*
* QUESTION: Why does the MODEL_5150 ROM BIOS set the page register for channel 1 (port 0x83) to zero?
*
* For FDC DMA notes, refer to: http://wiki.osdev.org/ISA_DMA
* For general DMA notes, refer to: http://www.freebsd.org/doc/en/books/developers-handbook/dma.html
*/
ChipSet.DMA0 = {
INDEX: 0,
PORT: {
CH0_ADDR: 0x00, // OUT: starting address IN: current address
CH0_COUNT: 0x01, // OUT: starting word count IN: remaining word count
CH1_ADDR: 0x02, // OUT: starting address IN: current address
CH1_COUNT: 0x03, // OUT: starting word count IN: remaining word count
CH2_ADDR: 0x04, // OUT: starting address IN: current address
CH2_COUNT: 0x05, // OUT: starting word count IN: remaining word count
CH3_ADDR: 0x06, // OUT: starting address IN: current address
CH3_COUNT: 0x07, // OUT: starting word count IN: remaining word count
CMD_STATUS: 0x08, // OUT: command register IN: status register
REQUEST: 0x09,
MASK: 0x0A,
MODE: 0x0B,
CLEAR_FF: 0x0C,
MASTER_CLR: 0x0D,
CLEAR_MASK: 0x0E, // TODO: Provide handlers
ALL_MASK: 0x0F, // TODO: Provide handlers
CH2_PAGE: 0x81, // OUT: DMA channel 2 page register
CH3_PAGE: 0x82, // OUT: DMA channel 3 page register
CH1_PAGE: 0x83, // OUT: DMA channel 1 page register
CH0_PAGE: 0x87 // OUT: DMA channel 0 page register (unusable; See "The Inside Out" book, p.246)
}
};
ChipSet.DMA1 = {
INDEX: 1,
PORT: {
CH6_PAGE: 0x89, // OUT: DMA channel 6 page register (MODEL_5170)
CH7_PAGE: 0x8A, // OUT: DMA channel 7 page register (MODEL_5170)
CH5_PAGE: 0x8B, // OUT: DMA channel 5 page register (MODEL_5170)
CH4_PAGE: 0x8F, // OUT: DMA channel 4 page register (MODEL_5170; unusable; aka "refresh" page register?)
CH4_ADDR: 0xC0, // OUT: starting address IN: current address
CH4_COUNT: 0xC2, // OUT: starting word count IN: remaining word count
CH5_ADDR: 0xC4, // OUT: starting address IN: current address
CH5_COUNT: 0xC6, // OUT: starting word count IN: remaining word count
CH6_ADDR: 0xC8, // OUT: starting address IN: current address
CH6_COUNT: 0xCA, // OUT: starting word count IN: remaining word count
CH7_ADDR: 0xCC, // OUT: starting address IN: current address
CH7_COUNT: 0xCE, // OUT: starting word count IN: remaining word count
CMD_STATUS: 0xD0, // OUT: command register IN: status register
REQUEST: 0xD2,
MASK: 0xD4,
MODE: 0xD6,
CLEAR_FF: 0xD8,
MASTER_CLR: 0xDA,
CLEAR_MASK: 0xDC, // TODO: Provide handlers
ALL_MASK: 0xDE // TODO: Provide handlers
}
};
ChipSet.DMA_CMD = {
M2M_ENABLE: 0x01,
CH0HOLD_ENABLE: 0x02,
CTRL_DISABLE: 0x04,
COMP_TIMING: 0x08,
ROT_PRIORITY: 0x10,
EXT_WRITE_SEL: 0x20,
DREQ_ACTIVE_LO: 0x40,
DACK_ACTIVE_HI: 0x80
};
ChipSet.DMA_MASK = {
CHANNEL: 0x03,
CHANNEL_SET: 0x04
};
ChipSet.DMA_MODE = {
CHANNEL: 0x03,
XFER: 0x0C,
XFER_VERIFY: 0x00,
XFER_WRITE: 0x04,
XFER_READ: 0x08,
AUTOINIT: 0x10,
DECREMENT: 0x20,
MODE: 0xC0,
MODE_DEMAND: 0x00,
MODE_SINGLE: 0x40,
MODE_BLOCK: 0x80,
MODE_CASCADE: 0xC0
};
ChipSet.DMA_FDC = 0x02; // DMA channel assigned to the Floppy Drive Controller (FDC)
ChipSet.DMA_HDC = 0x03; // DMA channel assigned to the Hard Drive Controller (HDC; XTC only)
/*
* 8259A Programmable Interrupt Controller (PIC) I/O ports
*
* Internal registers:
*
* ICW1 Initialization Command Word 1 (sent to port ChipSet.PIC_LO)
* ICW2 Initialization Command Word 2 (sent to port ChipSet.PIC_HI)
* ICW3 Initialization Command Word 3 (sent to port ChipSet.PIC_HI)
* ICW4 Initialization Command Word 4 (sent to port ChipSet.PIC_HI)
* IMR Interrupt Mask Register
* IRR Interrupt Request Register
* ISR Interrupt Service Register
* IRLow (IR having lowest priority; IR+1 will have highest priority; default is 7)
*
* Note that ICW2 effectively contains the starting IDT vector number (ie, for IRQ 0),
* which must be multiplied by 4 to calculate the vector offset, since every vector is 4 bytes long.
*
* Also, since the low 3 bits of ICW2 are ignored in 8086/8088 mode (ie, they are effectively
* treated as zeros), this means that the starting IDT vector can only be a multiple of 8.
*
* So, if ICW2 is set to 0x08, the starting vector number (ie, for IRQ 0) will be 0x08, and the
* 4-byte address for the corresponding ISR will be located at offset 0x20 in the real-mode IDT.
*
* ICW4 is typically set to 0x09, indicating 8086 mode, non-automatic EOI, buffered/slave mode.
*
* QUESTION: Why did the original ROM BIOS choose buffered/slave over buffered/master? Did it simply
* not matter in pre-AT systems with only one PIC, or am I misreading something?
*
* TODO: Consider support for level-triggered PIC interrupts, even though the original IBM PCs
* (up through MODEL_5170) used only edge-triggered interrupts.
*/
ChipSet.PIC0 = { // all models: the "master" PIC
INDEX: 0,
PORT_LO: 0x20,
PORT_HI: 0x21
};
ChipSet.PIC1 = { // MODEL_5170 and up: the "slave" PIC
INDEX: 1,
PORT_LO: 0xA0,
PORT_HI: 0xA1
};
ChipSet.PIC_LO = { // ChipSet.PIC1.PORT_LO or ChipSet.PIC2.PORT_LO
ICW1: 0x10, // set means ICW1
ICW1_ICW4: 0x01, // ICW4 needed (otherwise ICW4 must be sent)
ICW1_SNGL: 0x02, // single PIC (and therefore no ICW3; otherwise there is another "cascaded" PIC)
ICW1_ADI: 0x04, // call address interval is 4 (otherwise 8; presumably ignored in 8086/8088 mode)
ICW1_LTIM: 0x08, // level-triggered interrupt mode (otherwise edge-triggered mode, which is what PCs use)
OCW2: 0x00, // bit 3 (PIC_LO.OCW3) and bit 4 (ChipSet.PIC_LO.ICW1) are clear in an OCW2 command byte
OCW2_IR_LVL: 0x07,
OCW2_OP_MASK: 0xE0, // of the following valid OCW2 operations, the first 4 are EOI commands (all have ChipSet.PIC_LO.OCW2_EOI set)
OCW2_EOI: 0x20, // non-specific EOI (end-of-interrupt)
OCW2_EOI_SPEC: 0x60, // specific EOI
OCW2_EOI_ROT: 0xA0, // rotate on non-specific EOI
OCW2_EOI_ROTSPEC: 0xE0, // rotate on specific EOI
OCW2_SET_ROTAUTO: 0x80, // set rotate in automatic EOI mode
OCW2_CLR_ROTAUTO: 0x00, // clear rotate in automatic EOI mode
OCW2_SET_PRI: 0xC0, // bits 0-2 specify the lowest priority interrupt
OCW3: 0x08, // bit 3 (PIC_LO.OCW3) is set and bit 4 (PIC_LO.ICW1) clear in an OCW3 command byte (bit 7 should be clear, too)
OCW3_READ_IRR: 0x02, // read IRR register
OCW3_READ_ISR: 0x03, // read ISR register
OCW3_READ_CMD: 0x03,
OCW3_POLL_CMD: 0x04, // poll
OCW3_SMM_RESET: 0x40, // special mask mode: reset
OCW3_SMM_SET: 0x60, // special mask mode: set
OCW3_SMM_CMD: 0x60
};
ChipSet.PIC_HI = { // ChipSet.PIC1.PORT_HI or ChipSet.PIC2.PORT_HI
ICW2_VECTOR: 0xF8, // starting vector number (bits 0-2 are effectively treated as zeros in 8086/8088 mode)
ICW4_8086: 0x01,
ICW4_AUTO_EOI: 0x02,
ICW4_MASTER: 0x04,
ICW4_BUFFERED: 0x08,
ICW4_FULLY_NESTED: 0x10,
OCW1_IMR: 0xFF
};
/*
* The priorities of IRQs 0-7 are normally high to low, unless the master PIC has been reprogrammed.
* Also, if a slave PIC is present, the priorities of IRQs 8-15 fall between the priorities of IRQs 1 and 3.
*
* As the MODEL_5170 TechRef states:
*
* "Interrupt requests are prioritized, with IRQ9 through IRQ12 and IRQ14 through IRQ15 having the
* highest priority (IRQ9 is the highest) and IRQ3 through IRQ7 having the lowest priority (IRQ7 is
* the lowest).
*
* Interrupt 13 (IRQ.COPROC) is used on the system board and is not available on the I/O channel.
* Interrupt 8 (IRQ.RTC) is used for the real-time clock."
*
* This priority scheme is a byproduct of IRQ8 through IRQ15 (slave PIC interrupts) being tied to IRQ2 of
* the master PIC. As a result, the two other system board interrupts, IRQ0 and IRQ1, continue to have the
* highest priority, by default.
*/
ChipSet.IRQ = {
TIMER0: 0x00,
KBD: 0x01,
SLAVE: 0x02,
COM2: 0x03,
COM1: 0x04,
XTC: 0x05, // MODEL_5160 uses this for its HDC; MODEL_5170 designates it for LPT2
FDC: 0x06,
LPT1: 0x07,
RTC: 0x08,
IRQ2: 0x09,
COPROC: 0x0D,
ATC: 0x0E // MODEL_5170 uses this for its HDC
};
/*
* 8253 Programmable Interval Timer (PIT) I/O ports
*/
ChipSet.TIMER0 = {
INDEX: 0,
PORT: 0x40 // used for time-of-day (prior to MODEL_5170)
};
ChipSet.TIMER1 = {
INDEX: 1,
PORT: 0x41 // used for memory refresh
};
ChipSet.TIMER2 = {
INDEX: 2,
PORT: 0x42 // used speaker tone generation
};
ChipSet.TIMER_CTRL = {
PORT: 0x43, // write-only control register (use the Read-Back command to get status)
BCD: 0x01,
MODE: 0x0E,
MODE0: 0x00, // interrupt on terminal count
MODE1: 0x02, // programmable one-shot
MODE2: 0x04, // rate generator
MODE3: 0x06, // square wave generator
MODE4: 0x08, // software-triggered strobe
MODE5: 0x0A, // hardware-triggered strobe
RW: 0x30,
RW_LATCH: 0x00,
RW_LSB: 0x10,
RW_MSB: 0x20,
RW_BOTH: 0x30,
SC: 0xC0,
SC_CTR0: 0x00,
SC_CTR1: 0x40,
SC_CTR2: 0x80,
SC_BACK: 0xC0
};
ChipSet.TIMER_TICKS_PER_SEC = 1193181;
/*
* 8255A Programmable Peripheral Interface (PPI) I/O ports, for Cassette/Speaker/Keyboard/SW1/etc
*
* Normally, 0x99 is written to PPI_CTRL.PORT, indicating that PPI_A.PORT and PPI_C.PORT are INPUT ports
* and PPI_B.PORT is an OUTPUT port.
*
* However, the MODEL_5160 ROM BIOS initially writes 0x89 instead, making PPI_A.PORT an OUTPUT port.
* I'm guessing that's just part of some "diagnostic mode", because all it writes to PPI_A.PORT are a series
* of "checkpoint" values (ie, 0x01, 0x02, and 0x03) before updating PPI_CTRL.PORT with the usual 0x99.
*/
ChipSet.PPI_A = { // this.bPPIA
PORT: 0x60 // INPUT: keyboard scan code (PPI_B.CLEAR_KBD must be clear)
};
ChipSet.PPI_B = { // this.bPPIB
PORT: 0x61, // OUTPUT (although it has to be treated as INPUT, too: the keyboard interrupt handler reads it, OR's PPI_B.CLEAR_KBD, writes it, and then rewrites the original read value)
CLK_TIMER2: 0x01, // ALL: set to enable clock to TIMER2
SPK_TIMER2: 0x02, // ALL: set to connect output of TIMER2 to speaker (MODEL_5150: clear for cassette)
ENABLE_SW2: 0x04, // MODEL_5150: set to enable SW2[1-4] through PPI_C.PORT, clear to enable SW2[5]; MODEL_5160: unused (there is no SW2 switch block on the MODEL_5160 motherboard)
CASS_MOTOR_OFF: 0x08, // MODEL_5150: cassette motor off
ENABLE_SW_HI: 0x08, // MODEL_5160: clear to read SW1[1-4], set to read SW1[5-8]
DISABLE_RW_MEM: 0x10, // ALL: clear to enable RAM parity check, set to disable
DISABLE_IO_CHK: 0x20, // ALL: clear to enable I/O channel check, set to disable
CLK_KBD: 0x40, // ALL: clear to force keyboard clock low
CLEAR_KBD: 0x80 // ALL: clear to enable keyboard scan codes (MODEL_5150: set to enable SW1 through PPI_A.PORT)
};
ChipSet.PPI_C = { // this.bPPIC
PORT: 0x62, // INPUT (see below)
SW: 0x0F, // MODEL_5150: SW2[1-4] or SW2[5], depending on whether PPI_B.ENABLE_SW2 is set or clear; MODEL_5160: SW1[1-4] or SW1[5-8], depending on whether PPI_B.ENABLE_SW_HI is clear or set
CASS_DATA_IN: 0x10,
TIMER2_OUT: 0x20,
IO_CHANNEL_CHK: 0x40, // used by NMI handler to detect I/O channel errors
RW_PARITY_CHK: 0x80 // used by NMI handler to detect R/W memory parity errors
};
ChipSet.PPI_CTRL = { // this.bPPICtrl
PORT: 0x63, // OUTPUT: initialized to 0x99, defining PPI_A and PPI_C as INPUT and PPI_B as OUTPUT
A_IN: 0x10,
B_IN: 0x02,
C_IN_LO: 0x01,
C_IN_HI: 0x08,
B_MODE: 0x04,
A_MODE: 0x60
};
/*
* On the MODEL_5150, the following PPI_SW bits are exposed through PPI_A.
*
* On the MODEL_5160, either the low or high 4 bits are exposed through PPI_C.SW, if PPI_B.ENABLE_SW_HI is clear or set.
*/
ChipSet.PPI_SW = {
FDRIVE: {
IPL: 0x01, // MODEL_5150: IPL ("Initial Program Load") floppy drive attached; MODEL_5160: "Loop on POST"
ONE: 0x00, // 1 floppy drive attached (or 0 drives if PPI_SW.FDRIVE_IPL is not set -- MODEL_5150 only)
TWO: 0x40, // 2 floppy drives attached
THREE: 0x80, // 3 floppy drives attached
FOUR: 0xC0, // 4 floppy drives attached
MASK: 0xC0,
SHIFT: 6
},
COPROC: 0x02, // MODEL_5150: reserved; MODEL_5160: coprocessor installed
MEMORY: { // MODEL_5150: "X" is 16Kb; MODEL_5160: "X" is 64Kb
X1: 0x00, // 16Kb or 64Kb
X2: 0x04, // 32Kb or 128Kb
X3: 0x08, // 48Kb or 192Kb
X4: 0x0C, // 64Kb or 256Kb
MASK: 0x0C,
SHIFT: 2
},
MONITOR: {
TV: 0x10,
COLOR: 0x20,
MONO: 0x30,
MASK: 0x30,
SHIFT: 4
}
};
/*
* 8042 Keyboard Controller I/O ports (MODEL_5170)
*
* On the MODEL_5170, port 0x60 is designated KBC.DATA rather than PPI_A, although the BIOS also refers to it
* as "PORT_A: 8042 KEYBOARD SCAN/DIAG OUTPUTS"). This is the 8042's output buffer and should be read only when
* KBC.STATUS.OUTBUFF_FULL is set.
*
* Similarly, port 0x61 is designated KBC.RWREG rather than PPI_B; the BIOS also refers to it as "PORT_B: 8042
* READ WRITE REGISTER", but it is not otherwise discussed in the MODEL_5170 TechRef's 8042 documentation.
* There are brief references to bits 0 and 1 (KBC.RWREG.CLK_TIMER2 and KBC.RWREG.SPK_TIMER2), and the BIOS sets
* bits 2-7 to "DISABLE PARITY CHECKERS" (principally KBC.RWREG.DISABLE_CHK, which are bits 2 and 3); why the BIOS
* also sets bits 4-7 (or if those bits are even settable) is unclear, since it uses 11111100B rather than defined
* constants.
*
* The bottom line: on a MODEL_5170, port 0x61 is still used for speaker control and parity checking, so we use
* the same register (bPPIB) but install different I/O handlers. It's also bi-directional: at one point, the BIOS
* reads KBC.RWREG.REFRESH_BIT (bit 4) to verify that it's alternating.
*
* PPI_C and PPI_CTRL don't seem to be documented or used by the MODEL_5170 BIOS, so I'm assuming they're obsolete.
*
* NOTE: For more information on the 8042 Controller, including information on undocumented commands, refer to the
* documents in /devices/pc/keyboard/, as well as the following websites:
*
* http://halicery.com/8042/8042_INTERN_TXT.htm
* http://www.os2museum.com/wp/?p=589 ("IBM PC/AT 8042 Keyboard Controller Commands")
*/
ChipSet.KBC = {
DATA: { // this.b8042OutBuff (PPI_A on previous models, still referred to as "PORT A" by the MODEL_5170 BIOS)
PORT: 0x60,
CMD: { // this.b8042CmdData (KBC.DATA.CMD "data bytes" written to port 0x60, after writing a KBC.CMD byte to port 0x64)
PC_COMPAT: 0x40, // generate IBM PC-compatible scan codes
PC_MODE: 0x20,
NO_CLOCK: 0x10, // disable keyboard by driving "clock" line low
NO_INHIBIT: 0x08, // disable inhibit function
SYS_FLAG: 0x04, // this value is propagated to ChipSet.KBC.STATUS.SYS_FLAG
INT_ENABLE: 0x01 // generate an interrupt when the controller places data in the output buffer
},
SELF_TEST: { // result of ChipSet.KBC.CMD.SELF_TEST command (0xAA)
OK: 0x55
},
INTF_TEST: { // result of ChipSet.KBC.CMD.INTF_TEST command (0xAB)
OK: 0x00, // no error
CLOCK_LO: 0x01, // keyboard clock line stuck low
CLOCK_HI: 0x02, // keyboard clock line stuck high
DATA_LO: 0x03, // keyboard data line stuck low
DATA_HI: 0x04 // keyboard data line stuck high
}
},
INPORT: { // this.b8042InPort
UNDEFINED: 0x0F, // undefined
ENABLE_256KB: 0x10, // enable 2nd 256Kb of system board RAM
MFG_OFF: 0x20, // manufacturing jumper not installed
MONO: 0x40, // monochrome monitor is primary display
KBD_ON: 0x80 // keyboard not inhibited
},
OUTPORT: { // this.b8042OutPort
NO_RESET: 0x01, // set by default
A20_ON: 0x02, // set by default
OUTBUFF_FULL: 0x10, // output buffer full
INBUFF_EMPTY: 0x20, // input buffer empty
KBD_CLOCK: 0x40, // keyboard clock (output)
KBD_DATA: 0x80 // keyboard data (output)
},
TESTPORT: { // generated "on the fly"
KBD_CLOCK: 0x01, // keyboard clock (input)
KBD_DATA: 0x02 // keyboard data (input)
},
RWREG: { // this.bPPIB (since CLK_TIMER2 and SPK_TIMER2 are in both PPI_B and KBC.RWREG)
PORT: 0x61,
CLK_TIMER2: 0x01, // set to enable clock to TIMER2
SPK_TIMER2: 0x02, // set to connect output of TIMER2 to speaker
DISABLE_CHK: 0x0C, // set these bits to disable I/O and RAM parity checks, clear them to enable checks
REFRESH_BIT: 0x10, // indicates memory refresh
IO_CHK: 0x40, // indicates I/O check
PARITY_CHK: 0x80, // indicates RAM parity check
PARITY_ERR: 0xC0
},
CMD: { // this.b8042InBuff (on write to port 0x64, interpret this as a CMD)
PORT: 0x64,
READ_CMD: 0x20,
WRITE_CMD: 0x60, // followed by a command byte written to KBC.DATA.PORT (see KBC.DATA.CMD)
SELF_TEST: 0xAA, // self-test (KBC.DATA.SELF_TEST.OK is placed in the output buffer if no errors)
INTF_TEST: 0xAB, // interface test
DIAG_DUMP: 0xAC, // diagnostic dump
DISABLE_KBD: 0xAD, // disable keyboard
ENABLE_KBD: 0xAE, // enable keyboard
READ_INPORT: 0xC0, // read input port and place data in output buffer (use only if output buffer empty)
READ_OUTPORT: 0xD0, // read output port and place data in output buffer (use only if output buffer empty)
WRITE_OUTPORT: 0xD1, // next byte written to KBC.DATA.PORT (port 0x60) is placed in the output port (see KBC.DATA.OUTPUT)
READ_TEST: 0xE0,
PULSE_OUTPORT: 0xF0 // this is the 1st of 16 commands (0xF0-0xFF) that pulse bits 0-3 of the output port
},
STATUS: { // this.b8042Status (on read from port 0x64)
PORT: 0x64,
OUTBUFF_FULL: 0x01,
INBUFF_FULL: 0x02, // set if the controller has received but not yet read data written to the input buffer (not normally set)
SYS_FLAG: 0x04,
CMD_FLAG: 0x08, // set on write to KBC.CMD (port 0x64), clear on write to KBC.DATA (port 0x60)
NO_INHIBIT: 0x10,
XMT_TIMEOUT: 0x20,
RCV_TIMEOUT: 0x40,
PARITY_ERR: 0x80, // last byte of data received had EVEN parity (ODD parity is normally expected)
OUTBUFF_DELAY: 0x100
}
};
/*
* MC146818A RTC/CMOS Ports (MODEL_5170)
*
* Write a CMOS address to ChipSet.CMOS.ADDR.PORT, then read/write data from/to ChipSet.CMOS.DATA.PORT.
*
* The ADDR port also controls NMI: write an address with bit 7 clear to enable NMI or set to disable NMI.
*/
ChipSet.CMOS = {
ADDR: { // this.bCMOSAddr
PORT: 0x70,
RTC_SEC: 0x00,
RTC_SEC_ALRM: 0x01,
RTC_MIN: 0x02,
RTC_MIN_ALRM: 0x03,
RTC_HOUR: 0x04,
RTC_HOUR_ALRM: 0x05,
RTC_WEEK_DAY: 0x06,
RTC_MONTH_DAY: 0x07,
RTC_MONTH: 0x08,
RTC_YEAR: 0x09,
RTC_STATUSA: 0x0A,
RTC_STATUSB: 0x0B,
RTC_STATUSC: 0x0C,
RTC_STATUSD: 0x0D,
DIAG: 0x0E,
SHUTDOWN: 0x0F,
FDRIVE: 0x10,
HDRIVE: 0x12,
EQUIP: 0x14,
BASEMEM_LO: 0x15,
BASEMEM_HI: 0x16, // the BASEMEM values indicate the total Kb of base memory, up to 0x280 (640Kb)
EXTMEM_LO: 0x17,
EXTMEM_HI: 0x18, // the EXTMEM values indicate the total Kb of extended memory, up to 0x3C00 (15Mb)
CHKSUM_HI: 0x2E,
CHKSUM_LO: 0x2F, // CMOS bytes included in the checksum calculation: 0x10-0x2D
EXTMEM2_LO: 0x30,
EXTMEM2_HI: 0x31,
CENTURY_DATE: 0x32, // BCD value for the current century (eg, 0x19 for 20th century, 0x20 for 21st century)
BOOT_INFO: 0x33, // 0x80 if 128Kb expansion memory installed, 0x40 if Setup Utility wants an initial setup message
MASK: 0x3F,
TOTAL: 0x40,
NMI_DISABLE: 0x80
},
DATA: { // this.abCMOSData
PORT: 0x71
},
STATUSA: { // abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSA]
UIP: 0x80, // bit 7: 1 indicates Update-In-Progress, 0 indicates date/time ready to read
DV: 0x70, // bits 6-4 (DV2-DV0) are programmed to 010 to select a 32.768Khz time base
RS: 0x0F // bits 3-0 (RS3-RS0) are programmed to 0110 to select a 976.562us interrupt rate
},
STATUSB: { // abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSB]
SET: 0x80, // bit 7: 1 to set any/all of the 14 time-bytes
PIE: 0x40, // bit 6: 1 for Periodic Interrupt Enable
AIE: 0x20, // bit 5: 1 for Alarm Interrupt Enable
UIE: 0x10, // bit 4: 1 for Update-Ended Interrupt Enable
SQWE: 0x08, // bit 3: 1 for Square Wave Enabled (as set by the STATUSA rate selection bits)
BINARY: 0x04, // bit 2: 1 for binary Date Mode, 0 for BCD Date Mode
HOUR24: 0x02, // bit 1: 1 for 24-hour mode, 0 for 12-hour mode
DST: 0x01 // bit 0: 1 for Daylight Savings Time enabled
},
STATUSC: { // abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSC] TODO: Does reading this register clear these interrupt conditions? (see F000:01C6 in the MODEL_5170 BIOS)
IRQF: 0x80, // bit 7
PF: 0x40, // bit 6: 1 indicates Periodic Interrupt
AF: 0x20, // bit 5: 1 indicates Alarm Interrupt
UF: 0x10, // bit 4: 1 indicates Update-Ended Interrupt
RESERVED: 0x0F
},
STATUSD: { // abCMOSData[ChipSet.CMOS.ADDR.RTC_STATUSD]
VRB: 0x80, // bit 7: 1 indicates Valid RAM Bit (0 implies power was and/or is lost)
RESERVED: 0x7F
},
DIAG: { // abCMOSData[ChipSet.CMOS.ADDR.DIAG]
RTCFAIL: 0x80, // bit 7: 1 indicates RTC lost power
CHKSUMFAIL: 0x40, // bit 6: 1 indicates bad CMOS checksum
CONFIGFAIL: 0x20, // bit 5: 1 indicates bad CMOS configuration info
MEMSIZEFAIL: 0x10, // bit 4: 1 indicates memory size miscompare
HDRIVEFAIL: 0x08, // bit 3: 1 indicates hard drive controller or drive init failure
TIMEFAIL: 0x04, // bit 2: 1 indicates time failure
RESERVED: 0x03
},
FDRIVE: { // abCMOSData[ChipSet.CMOS.ADDR.FDRIVE]
D0_MASK: 0xF0, // Drive 0 type in high nibble
D1_MASK: 0x0F, // Drive 1 type in lower nibble
NONE: 0, // no drive
DSDD: 1, // double-sided double-density drive (48 TPI, 40 tracks, 360Kb max)
DSHD: 2 // double-sided high-density drive (96 TPI, 80 tracks, 1.2Mb max)
},
/*
* HDRIVE types are defined by table in the HDC component, which uses setCMOSDriveType() to update the CMOS
*/
HDRIVE: { // abCMOSData[ChipSet.CMOS.ADDR.HDRIVE]
D0_MASK: 0xF0, // Drive 0 type in high nibble
D1_MASK: 0x0F // Drive 1 type in lower nibble
},
/*
* The CMOS equipment flags use the same format as the older PPI equipment flags
*/
EQUIP: { // abCMOSData[ChipSet.CMOS.ADDR.EQUIP]
MONITOR: ChipSet.PPI_SW.MONITOR, // PPI_SW.MONITOR.MASK == 0x30
COPROC: ChipSet.PPI_SW.COPROC, // PPI_SW.COPROC == 0x02
FDRIVE: ChipSet.PPI_SW.FDRIVE // PPI_SW.FDRIVE.IPL == 0x01 and PPI_SW.FDRIVE.MASK = 0xC0
}
};
/*
* Manufacturing Test Ports (MODEL_5170)
*
* The MODEL_5170 TechRef lists 0x80-0x9F as the range for DMA page registers, but that seems a bit
* overbroad; at one point, it says:
*
* "I/O address hex 080 is used as a diagnostic-checkpoint port or register.
* This port corresponds to a read/write register in the DMA page register (74LS6I2)."
*
* 0x80 is the neighborhood, but that particular port is not documented as a DMA page register.
* We'll refer to it as manufacturing port (see bMFGData). Be aware that the MODEL_5170 BIOS is littered
* with manufacturing test ("MFG_TST") code which, if enabled, writes to other DMA page registers,
* perhaps treating them as scratch registers.
*/
ChipSet.MFG = { // this.bMFGData
PORT: 0x80
};
/*
* NMI Mask Register (MODEL_5150 and MODEL_5160 only)
*/
ChipSet.NMI = { // this.bNMI
PORT: 0xA0,
ENABLE: 0x80,
DISABLE: 0x00
};
/*
* Coprocessor Control Registers (MODEL_5170)
*/
ChipSet.COPROC = { // TODO: Define a variable for this
PORT_CLEAR: 0xF0, // clear the coprocessor's "busy" state
PORT_RESET: 0xF1 // reset the coprocessor
};
/*
* ChipSet-related BIOS interrupts, functions, and other parameters
*/
ChipSet.BIOS = {
INT_RTC: 0x1A
};
/**
* @this {ChipSet}
* @param {string|null} sHTMLClass is the class of the HTML control (eg, "input", "output")
* @param {string|null} sHTMLType is the type of the HTML control (eg, "button", "list", "text", "submit", "textarea", "canvas")
* @param {string} sBinding is the value of the 'binding' parameter stored in the HTML control's "data-value" attribute (eg, "sw1")
* @param {Object} control is the HTML control DOM object (eg, HTMLButtonElement)
* @return {boolean} true if binding was successful, false if unrecognized binding request
*/
ChipSet.prototype.setBinding = function(sHTMLClass, sHTMLType, sBinding, control)
{
switch (sBinding) {
case "sw1":
this.bindings[sBinding] = control;
this.addSwitches(sBinding, control, 8, this.sw1Init, {
0: (this.model == ChipSet.MODEL_5150? "Bootable Floppy Drive" : "Loop on POST"),
1: (this.model == ChipSet.MODEL_5150? "Reserved" : "Coprocessor"),
2: "Base Memory Size", // up to 64Kb on a MODEL_5150, 256Kb on a MODEL_5160
4: "Monitor Type",
6: "Number of Floppy Drives"
});
return true;
case "sw2":
if (this.model == ChipSet.MODEL_5150) {
this.bindings[sBinding] = control;
this.addSwitches(sBinding, control, 8, this.sw2Init, {
0: "Expansion Memory Size", // up to 480Kb, which, when combined with 64Kb of MODEL_5150 base memory, gives a maximum of 544Kb
4: "Reserved"
});
return true;
}
break;
case "swdesc":
this.bindings[sBinding] = control;
return true;
default:
break;
}
return false;
};
/**
* initBus(cmp, bus, cpu, dbg)
*
* @this {ChipSet}
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {Debugger} dbg
*/
ChipSet.prototype.initBus = function(cmp, bus, cpu, dbg)
{
this.bus = bus;
this.cpu = cpu;
this.dbg = dbg;
this.cmp = cmp;
this.kbd = cmp.getComponentByType("Keyboard");
/*
* This divisor is invariant, so we calculate it as soon as we're able to query the CPU's base speed.
*/
this.nTicksDivisor = Math.round(cpu.getCyclesPerSecond() / ChipSet.TIMER_TICKS_PER_SEC);
bus.addPortInputTable(this, ChipSet.aPortInput);
bus.addPortOutputTable(this, ChipSet.aPortOutput);
if (this.model < ChipSet.MODEL_5170) {
bus.addPortInputTable(this, ChipSet.aPortInput5150);
bus.addPortOutputTable(this, ChipSet.aPortOutput5150);
} else {
bus.addPortInputTable(this, ChipSet.aPortInput5170);
bus.addPortOutputTable(this, ChipSet.aPortOutput5170);
}
if (DEBUGGER) {
if (dbg) {
var chipset = this;
dbg.messageInit(ChipSet);
dbg.messageDump(ChipSet.MESSAGE_PIC, function onDumpPIC()
{
chipset.dumpPIC();
});
dbg.messageDump(ChipSet.MESSAGE_TIMER, function onDumpTimer()
{
chipset.dumpTimer();
});
dbg.messageDump(ChipSet.MESSAGE_CMOS, function onDumpCMOS()
{
chipset.dumpCMOS();
});
}
cpu.addIntNotify(ChipSet.BIOS.INT_RTC, this, this.intBIOSRTC);
}
};
/**
* powerUp(data, fRepower)
*
* @this {ChipSet}
* @param {Object|null} data
* @param {boolean} [fRepower]
* @return {boolean} true if successful, false if failure
*/
ChipSet.prototype.powerUp = function(data, fRepower)
{
if (!fRepower) {
if (!data) {
this.reset(true);
} else {
if (!this.restore(data)) return false;
}
}
return true;
};
/**
* powerDown(fSave)
*
* @this {ChipSet}
* @param {boolean} fSave
* @return {Object|boolean}
*/
ChipSet.prototype.powerDown = function(fSave)
{
return fSave && this.save? this.save() : true;
};
/**
* reset(fSoft)
*
* @this {ChipSet}
* @param {boolean} [fSoft] is true if "soft" reset, otherwise "hard" reset (see below for details)
*/
ChipSet.prototype.reset = function(fSoft)