Updated the 5170 BIOS map, updated 8042 info, fixed some 8042 controller commands, made HLT work, and fixed the debugger's history buffer when executing a mix of real-mode and protected-mode instructions

This commit is contained in:
Jeff Parsons 2014-10-01 17:33:17 -07:00 committed by jeffpar
commit 93b81c47b7
13 changed files with 804 additions and 287 deletions

File diff suppressed because one or more lines are too long

View file

@ -1,8 +1,24 @@
F000:002C @ POST1
F000:00A6 @ POST1_TEST01 ; TEST.01: 286 PROCESSOR TEST (REAL MODE)
F000:01EE @ POST1_TEST02 ; TEST.02: VERIFY CMOS SHUTDOWN BYTE
F000:0213 @ POST1_TEST03 ; TEST.03: ROS CHECKSUM TEST
F000:0225 @ POST1_TEST04 ; TEST.04: 8253 CHECK TIMER 1 (ALL BITS ON)
F000:025C @ POST1_TEST05 ; TEST.05: 8253 CHECK TIMER 1 (ALL BITS OFF)
F000:027B @ POST1_TEST06 ; TEST.06: 8237 DMA 0 INITIALIZATION
F000:02C6 @ POST1_TEST07 ; TEST.07: 8237 DMA 1 INITIALIZATION
F000:033B @ POST1_TEST08 ; TEST.08: DMA PAGE REGISTER TEST
F000:038E @ POST1_TEST09 ; TEST.09: STORAGE REFRESH TEST
F000:03A6 @ POST1_TEST10 ; TEST.10: 8042 TESTS
F000:03DD @ POST1_GETSW ; GET SWITCH SETTINGS
F000:0426 @ POST1_TEST11 ; TEST.11: BASE 64K R/W STORAGE TEST
F000:0606 @ POST1_SETMFG ; SET MFG_TST
F000:060D @ POST1_TEST11A ; TEST.11A: VERIFY GDT/IDT INSTRUCTIONS
F000:06FA @ POST1_TEST12 ; TEST.12: VERIFY CMOS CHECKSUM
F000:07E2 @ POST1_TEST13 ; TEST.13: PROTECTED MODE TEST
F000:08B7 @ POST1_TEST13A ; TEST.13A: MEMORY SIZE TEST (ABOVE 1024K)
F000:0AB4 @ POST1_TEST14 ; TEST.14: INITIALIZE CRT CONTROLLER
F000:0B27 @ POST1_TEST15 ; TEST.15: VIDEO LINE TEST
F000:0B37 @ POST1_TEST16 ; TEST.16: CRT INTERFACE LINES TEST
0C3F +
F000:0000 @ POST2 ; TEST.17: 8259 PIC TEST
F000:0058 @ POST2_CP27 ; CHECKPOINT 0x27

View file

@ -1,4 +1,4 @@
From [http://minuszerodegrees.net/bios/BIOS_5170_10JAN84_6MHZ/README.TXT](http://minuszerodegrees.net/bios/BIOS_5170_10JAN84_6MHZ.zip):
From [http://minuszerodegrees.net/bios/BIOS_5170_10JAN84_6MHZ/README.TXT](http://minuszerodegrees.net/bios/):
This is the first BIOS for the IBM 5170.
It is dated 10JAN84.
@ -9,7 +9,7 @@ From [http://minuszerodegrees.net/bios/BIOS_5170_10JAN84_6MHZ/README.TXT](http:/
8 bit checksum of 6181028 = 36
8 bit checksum of 6181029 = CA
----
--
added = 00
There are two BIN files in this ZIP file:
@ -17,14 +17,17 @@ From [http://minuszerodegrees.net/bios/BIOS_5170_10JAN84_6MHZ/README.TXT](http:/
1. BIOS_5170_10JAN84_U27_6181028_27256_6MHZ.BIN --> Use this to create a U27 using a 27256 EPROM (rated at 150nS or faster)
2. BIOS_5170_10JAN84_U47_6181029_27256_6MHZ.BIN --> Use this to create a U47 using a 27256 EPROM (rated at 150nS or faster)
The JSON-encoded ROM image that PCjs uses ([1984-01-10.json]()) was created using the *FileDump* command-line *merge* option:
The JSON-encoded ROM image that PCjs uses was created using the *FileDump* command-line *merge* option:
filedump --file=BIOS_5170_10JAN84_U27_6181028_27256_6MHZ.BIN --merge=BIOS_5170_10JAN84_U47_6181029_27256_6MHZ.BIN --output=1984-01-10.json
filedump --file=http://static.pcjs.org/devices/pc/bios/5170/BIOS_5170_10JAN84_U27_6181028_27256_6MHZ.BIN --merge=http://static.pcjs.org/devices/pc/bios/5170/BIOS_5170_10JAN84_U47_6181029_27256_6MHZ.BIN --output=1984-01-10.json --overwrite
Since a MAP file ([1984-01-10.map]()) exists as well, it is automatically appended to the JSON file ([1984-01-10.json]())
when using a ROM input file (or JSON output file) with a matching filename.
Similarly, to create a binary ROM image (1984-01-10.rom), add *--format=rom* to the command-line. These operations can
only be performed using the *FileDump* command-line interface; the *FileDump* API does not support the *merge* option.
It is also possible to create a merged binary ROM image ([1984-01-10.rom](http://static.pcjs.org/devices/pc/bios/5170/1984-01-10.rom))
by adding *--format=rom* to the command-line (the default is *--format=json*).
A MAP file [1984-01-10.map]() exists as well, which is automatically appended to the JSON file when using a ROM input
file (or JSON output file) with a matching filename. For example:
These operations can only be performed using the *FileDump* command-line interface; the *FileDump* API does not support
either the *merge* option or the appending of MAP data. For the moment, the API can only dump unadorned ROM images; eg:
http://www.pcjs.org/api/v1/dump/?file=/devices/pc/bios/5170/1984-01-10.rom
http://www.pcjs.org/api/v1/dump/?file=http://static.pcjs.org/devices/pc/bios/5170/1984-01-10.rom

View file

@ -0,0 +1,10 @@
8042 Keyboard Controller Internals
---
The following documents were obtained from [halicery.com](http://halicery.com/):
- [8042_INTERN.TXT]()
- [8042_1503033.TXT]()
- [dasm42.c]()
Additional information (eg, undocumented 8042 commands) is also available from [OS/2 Museum](http://www.os2museum.com/wp/?p=589).

View file

@ -0,0 +1,408 @@
#include <stdio.h> // we use printf()
/*
Small Intel UPI-41/42 DISASSEMBLER
==================================
A. Tarpai 2010 (tarpai76 gmail com)
It was written to look at some 8042 ROM dump code.
You can use and modify it for any purpose.
I'm happy if you mention me, the author.
No warranties (what for?).
Usage:
------
Call dasm42() passing a pointer, an offset and number of bytes.
Uses 1 external: printf().
The disassembler is based on the book
"Microprocessor Peripherals UPI-41A/41AH/42/42AH User's Manual, INTEL CORPORATION, 1996"
*/
static int PC;
/* Operand Addressing Mode writers */
typedef void (*Tfop)(unsigned char *p);
static void fopJMP(unsigned char *p) // JMP and CALL: 11-bit absolute address (2K)
{
printf("$%04X", ((*p<<3)&0x700) | p[1]);
}
static void fopJ(unsigned char *p) // jumps: 8-bit in-page address
{
printf("$%04X", (PC&0xff00)|p[1]); // TODO!! Jump at page boundary?? PC or PC+2 here?
}
static void fopRx(unsigned char *p) // Register Direct (x=0-7)
{
printf("R%x", *p&7);
}
static void fopPx(unsigned char *p) // Port Direct (x=[1,2])
{
printf("P%x", *p&3);
}
static void fopRRx(unsigned char *p) // Indexed @R0 or @R1
{
printf("@R%x", *p&1);
}
static void fopA(unsigned char *p) // Accumulator
{
printf("A");
}
static void fopIMM(unsigned char *p) // Immediate 8-bit value
{
printf("#$%02X", p[1]);
}
typedef void (*Tfmnop)(unsigned char *p, Tfop fop1, Tfop fop2);
static void fopJB(unsigned char *p, Tfop fop1, Tfop fop2) // JBx is special (have to complete the mn)
{
printf("%x ", *p>>5);
fopJ(p);
}
static void fmnop2(unsigned char *p, Tfop fop1, Tfop fop2) // 2-operand instructions
{
printf(" ");
fop1(p);
printf(",");
fop2(p);
}
static void fmnop1(unsigned char *p, Tfop fop1, Tfop fop2) // 1-operand instructions
{
printf(" ");
fop1(p);
}
struct instr {
char *mn;
unsigned char opcd;
unsigned char opcdmsk;
char len;
Tfmnop fmnop;
Tfop fop1, fop2;
};
static struct instr instrs[] = {
{ "ADD", 0x68, 0xf8, 1, fmnop2, fopA, fopRx}, // ADD A,Rr Add Register Contents to Accumulator
{ "ADD", 0x60, 0xfe, 1, fmnop2, fopA, fopRRx}, // ADD A,@Rr Add Data Memory Contents to Accumulator
{ "ADD", 0x03, 0xff, 2, fmnop2, fopA, fopIMM}, // ADD A,Ýdata Add Immediate Data to Accumulator
{ "ADDC", 0x78, 0xf8, 1, fmnop2, fopA, fopRx}, // ADDC A,Rr Add Carry and Register Contents to Accumulator
{ "ADDC", 0x70, 0xfe, 1, fmnop2, fopA, fopRRx}, // ADDC A,@Rr Add Carry and Data Memory Contents to Accumulator
{ "ADDC", 0x13, 0xff, 2, fmnop2, fopA, fopIMM}, // Add Carry and Immediate Data to Accumulator
{ "ANL", 0x58, 0xf8, 1, fmnop2, fopA, fopRx}, // AND A,Rr Add Register Contents to Accumulator
{ "ANL", 0x50, 0xfe, 1, fmnop2, fopA, fopRRx}, // AND A,@Rr Add Data Memory Contents to Accumulator
{ "ANL", 0x53, 0xff, 2, fmnop2, fopA, fopIMM}, // AND A,Ýdata Add Immediate Data to Accumulator
{ "ANL", 0x98, 0xfc, 2, fmnop2, fopPx, fopIMM}, // ANL PP,Ýdata Logical AND PORT 1±2 With Immediate Mask
{ "ANLD", 0x9C, 0xfc, 1, fmnop2, fopPx, fopA}, // ANLD Pp,A Logical AND Port 4±7 With Accumulator Mask
{ "CALL", 0x14, 0x1f, 2, fmnop1, fopJMP},
{ "CLR A", 0x27, 0xff, 1, 0}, // CLR A
{ "CLR C", 0x97, 0xff, 1, 0}, // CLR C Clear Carry Bit
{ "CLR F1", 0xA5, 0xff, 1, 0}, // CLR F1 Clear Flag 1
{ "CLR F0", 0x85, 0xff, 1, 0}, // CLR F0 Clear Flag 0
{ "CPL A", 0x37, 0xff, 1, 0}, // CPL A Complement Accumulator
{ "CPL C", 0xA7, 0xff, 1, 0}, // CPL C Complement Carry Bit
{ "CPL F0", 0x95, 0xff, 1, 0}, // CPL F0 COMPLEMENT FLAG 0
{ "CPL F1", 0xB5, 0xff, 1, 0}, // CPL F1 COMPLEMENT FLAG 1
{ "DA A", 0x57, 0xff, 1, 0}, // DA A Decimal Adjust Accumulator
{ "DEC A", 0x07, 0xff, 1, 0}, // DEC A Decrement Accumulator
{ "DEC", 0xC8, 0xf8, 1, fmnop1, fopRx}, // DEC Rr Decrement Register
{ "DIS I", 0x15, 0xff, 1, 0}, // DIS I Disable IBF Interrupt
{ "DIS TCNTI", 0x35, 0xff, 1, 0}, // DIS TCNTI Disable Timer/Counter Interrupt
{ "DJNZ", 0xE8, 0xf8, 2, fmnop2, fopRx, fopJ}, // DJNZ Rr, address Decrement Register and Test
{ "EN DMA", 0xE5, 0xff, 1, 0}, // EN DMA Enable DMA Handshake Lines
{ "EN FLAGS", 0xF5, 0xff, 1, 0 }, // EN FLAGS Enable Master Interrupts
{ "EN I", 0x05, 0xff, 1, 0 }, // EN I Enable IBF Interrupt
{ "EN TCNTI", 0x25, 0xff, 1, 0}, // EN TCNTI Enable Timer/Counter Interrupt
{ "IN A,DBB", 0x22, 0xff, 1, 0}, // IN A,DBB Input Data Bus Buffer Contents to Accumulator
{ "IN", 0x08, 0xfc, 1, fmnop2, fopA, fopPx}, // IN A,Pp Input Port 1±2 Data to Accumulator
{ "INC A", 0x17, 0xff, 1, 0}, // INC A
{ "INC", 0x18, 0xf8, 1, fmnop1, fopRx}, // INC Rr Increment Register
{ "INC", 0x10, 0xfe, 1, fmnop1, fopRRx}, // INC @Rr Increment Data Memory Location
{ "JB", 0x12, 0x1f, 2, fopJB}, // JBb address Jump If Accumulator Bit is Set
{ "JC", 0xF6, 0xff, 2, fmnop1, fopJ }, //
{ "JF0", 0xB6, 0xff, 2, fmnop1, fopJ }, //
{ "JF1", 0x76, 0xff, 2, fmnop1, fopJ }, //
{ "JMP", 0x04, 0x1f, 2, fmnop1, fopJMP},
{ "JMPP @A", 0xB3, 0xff, 1, 0 }, // JMPP @A Indirect Jump Within Page
{ "JNC", 0xE6, 0xff, 2, fmnop1, fopJ }, //
{ "JNIBF", 0xD6, 0xff, 2, fmnop1, fopJ }, //
{ "JNT0", 0x26, 0xff, 2, fmnop1, fopJ }, // JNTO address Jump if TEST 0 is Low
{ "JNT1", 0x46, 0xff, 2, fmnop1, fopJ }, //
{ "JNZ", 0x96, 0xff, 2, fmnop1, fopJ }, //
{ "JOBF", 0x86, 0xff, 2, fmnop1, fopJ }, //
{ "JTF", 0x16, 0xff, 2, fmnop1, fopJ }, //
{ "JT0", 0x36, 0xff, 2, fmnop1, fopJ }, //
{ "JT1", 0x56, 0xff, 2, fmnop1, fopJ }, //
{ "JZ", 0xC6, 0xff, 2, fmnop1, fopJ }, //
{ "MOV", 0x23, 0xff, 2, fmnop2, fopA, fopIMM }, // MOV A,Ýdata Move Immediate Data to Accumulator
{ "MOV A,PSW", 0xC7, 0xff, 1, 0}, // MOV A,PSW Move PSW Contents to Accumulator
{ "MOV", 0xF8, 0xf8, 1, fmnop2, fopA, fopRx }, // MOV A,Rr Move Register Contents to Accumulator
{ "MOV", 0xF0, 0xfe, 1, fmnop2, fopA, fopRRx }, // MOV A,@Rr Move Data Memory Contents to Accumulator
{ "MOV A,T", 0x42, 0xff, 1, 0}, // MOV A,T Move Timer/Counter Contents to Accumulator
{ "MOV PSW,A", 0xD7, 0xff, 1, 0}, // MOV PSW,A Move Accumulator Contents to PSW
{ "MOV", 0xA8, 0xf8, 1, fmnop2, fopRx, fopA }, // MOV Rr,A Move Accumulator Contents to Register
{ "MOV", 0xB8, 0xf8, 2, fmnop2, fopRx, fopIMM }, // MOV Rr,Ýdata Move Immediate Data to Register
{ "MOV", 0xA0, 0xfe, 1, fmnop2, fopRRx, fopA }, // MOV @Rr,A Move Accumulator Contents to Data Memory
{ "MOV", 0xB0, 0xfe, 2, fmnop2, fopRRx, fopIMM }, // MOV @Rr,Ýdata Move Immediate Data to Data Memory
{ "MOV STS,A", 0x90, 0xff, 1, 0}, // MOV STS,A Move Accumulator Contents to STS Register
{ "MOV T,A", 0x62, 0xff, 1, 0}, // MOV T,A Move Accumulator Contents to Timer/Counter
{ "MOVD", 0x0C, 0xfc, 2, fmnop2, fopA, fopPx }, // MOVD A,Pp Move Port 4±7 Data to Accumulator
{ "MOVD", 0x3C, 0xfc, 2, fmnop2, fopPx, fopA }, // MOVD Pp,A Move Accumulator Data to Port 4, 5, 6 and 7
{ "MOVP A,@A", 0xA3, 0xff, 1, 0}, // MOVP A,@A Move Current Page Data to Accumulator
{ "MOVP3 A,@A", 0xE3, 0xff, 1, 0}, // MOVP3 A,@A Move Page 3 Data to Accumulator
{ "NOP", 0x00, 0xff, 1, 0},
{ "ORL", 0x48, 0xf8, 1, fmnop2, fopA, fopRx}, // ORL A,Rr Logical OR Accumulator With Register Mask
{ "ORL", 0x40, 0xfe, 1, fmnop2, fopA, fopRRx}, // ORL A,@Rr Logical OR Accumulator With Memory Mask
{ "ORL", 0x43, 0xff, 2, fmnop2, fopA, fopIMM}, // ORL A,ÝData Logical OR Accumulator With Immediate Mask
{ "ORL", 0x88, 0xfc, 2, fmnop2, fopPx, fopIMM}, // ORL Pp,Ýdata Logical OR Port 1±2 With Immediate Mask
{ "ORLD", 0x8C, 0xfc, 2, fmnop2, fopPx, fopA}, // ORLD Pp,A Logical OR Port 4±7 With Accumulator Mask
{ "OUT DBB,A", 0x02, 0xff, 1, 0}, // OUT DBB,A Output Accumulator Contents to Data Bus Buffer
{ "OUTL", 0x38, 0xfc, 1, fmnop2, fopPx, fopA }, // OUTL Pp,A Output Accumulator Data to Port 1 and 2
{ "RET", 0x83, 0xff, 1, 0}, // RET Return Without PSW Restore
{ "RETR", 0x93, 0xff, 1, 0}, // RET Return Without PSW Restore
{ "RL A", 0xE7, 0xff, 1, 0}, // RL A Rotate Left Without Carry
{ "RLC A", 0xF7, 0xff, 1, 0}, // RLC A Rotate Left Through Carry
{ "RR A", 0x77, 0xff, 1, 0}, // RR A Rotate Right Without Carry
{ "RRC A", 0x67, 0xff, 1, 0}, // RRC A Rotate Right Through Carry
{ "SEL RB0", 0xC5, 0xff, 1, 0}, // SEL RB0 Select Register Bank 0
{ "SEL RB1", 0xD5, 0xff, 1, 0}, // SEL RB1 Select Register Bank 1
{ "STOP TCNT", 0x65, 0xff, 1, 0}, // STOP TCNT Stop Timer/Event Counter
{ "STRT CNT", 0x45, 0xff, 1, 0}, // STRT CNT Start Event Counter
{ "STRT T", 0x55, 0xff, 1, 0}, // STRT T Start Timer
{ "SWAP A", 0x47, 0xff, 1, 0}, // SWAP A Swap Nibbles Within Accumulator
{ "XCH", 0x28, 0xf8, 1, fmnop2, fopA, fopRx}, // XCH ARr Exchange Accumulator-Register Contents
{ "XCH", 0x20, 0xfe, 1, fmnop2, fopA, fopRRx}, // XCH A,@Rr Exchange Accumulator and Data Memory Contents
{ "XCHD", 0x30, 0xfe, 1, fmnop2, fopA, fopRRx}, // XCHD A,@Rr Exchange Accumulator and Data Memory 4-bit Data
{ "XRL", 0xD8, 0xf8, 1, fmnop2, fopA, fopRx}, // XRL A,Rr Logical XOR Accumulator With Register Mask
{ "XRL", 0xD0, 0xfe, 1, fmnop2, fopA, fopRRx}, // XRL A,@Rr Logical XOR Accumulator With Memory Mask
{ "XRL", 0xD3, 0xff, 2, fmnop2, fopA, fopIMM}, // XRL A,Ýdata, Logical XOR Accumulator With Immediate Mask
{ "???", 0, 0, 1, 0} // All others (zero-mask will be true - if reached, len=1)
};
static int instr1(unsigned char *p)
{
struct instr *i= instrs;
for (; ; i++) {
if ((*p & i->opcdmsk) == i->opcd) {
printf("%04X: ", PC); // print Program Counter
PC+=i->len; // we increment PC as if CPU did for JMP instructions(?)
printf("%02X ", p[0]); // write 1 or 2 code bytes
if (i->len==2) printf("%02X ", p[1]);
else printf(" ");
printf("%s", i->mn); // write mnemonic
if (i->fmnop) i->fmnop(p, i->fop1, i->fop2); // write 0, 1 or 2 operands
printf("\n");
return i->len;
}
}
}
/* Extern.
Disassemble iNTEL UPI-41/42 machine code
of n bytes, from p + offset
*/
void dasm42(char *p, int offs, int n)
{
p+=offs;
PC=offs;
n+=offs;
for (; PC < n;) p+=instr1(p);
}
/********** NOTES ****************************************************
Addressing:
- implicite (in instr)
- register Rn (0..7)
- indexed @Rn (0..1)
- A
- # (0..$ff)
- addr, 1 byte + PC (0..$ffff)
--------------------
RAM (DATA) 256 bytes
--------------------
0..7 R0..R7 BANK0 (Bs in PSW)
8..23 STACK (8x16bit) .....
24..31 R0..R7 BANK1
...... to top: "RAM" .....
R0, R1 can be index register
----------------
ROM (PROGRAM) 2K
----------------
RESET: $0000
IBR INT: $0003
TIMER INT: $0007
-----------
PC - 10-bit (not 11?)
-----------
PC always points to next instruction.
----
JUMP
----
a.) absolute address: JMP $35E = aa a9 a8 0 0 1 0 0 a7 a6 a5 a4 a3 a2 a1 a0 (2-byte instr)
b.) "relative" (op -> PC-LO): JZ $addr = $C6 $op (2-byte instr)
// OK.. "If a conditional JUMP or indirect JUMP begins in location 255 of a page, it must reference a destination on the following page"
--------
PC-stack
--------
8x16 bits: call/int saves PSW[7..4]&PC[11..0]
-------------------------
PSW - program status word
-------------------------
7 6 5 4 | 3 | 2 1 0
C AC F0 Bs| - | SP
CALL: push PC&PSW[7..4]
RET: pop PC
RETR: pop PC&PSW[7..4]
On-chip oscillator
------------------
1 to 12.5 MHz
or external
-------------------
8-bit Timer/Counter
-------------------
Timer-mode
- increments on OSC + 32-prescale
- START T .. STOP TCNT
Counter-mode
- increments on falling edges on TEST1-pin
- START CNT .. STOP TCNT
- MOV T,A and MOV A,T for reading/writing
Timer/Counter OVERFLOW
----------------------
fe..ff..00
1. sets timer flag (TF) - then can be tested by JTF (which clears TF)
2. generates IRQ
3. EN/DIS TCNTI
4. if enabled: CALL $0007 happens
----------
INTERRUPTS
----------
IBF:
- higher pri
- EN/DIS I
- CS & RW triggers
Timer
- EN/DIS TCNTI
-
1. IRQ set + disable all interrupts
2. CALL 3 (IBF) or 7 (T)
3. entering ISR clears IRQ
4. ISR
5. RETR (re-enables interrupt)
HOST INTERRUPTS
---------------
EN FLAGS will allocate P24/P25 to OBF/_IBF (only RESET clears it)
"These interrupt outputs reflect the internal status of the OBF flag and the IBF inverted flag."
"Note, these outputs may be inhibited by writing a '0' to these pins. Reenabling interrupts is done by writing a '1' to these port pins."
==> so host cpu doesn't have to poll the same bits in STATUS REG (0x64); it can have it as interrupts (this is set in the code after RESET..)
------------
HOST CPU I/O
------------
!!! There are 3 registers in the UPI on the host side !!! (NB. host CPU writes into DBBIN..)
<--R-- STATUS <--- MOV STS,A + JF0,JF1,JOBF,JNIBF
HOST CPU I/O <--R-- DBBOUT <--- OUT DBB,A
--W--> DBBIN ---> IN A,DBB
RD WR A0
0 1 0 (0x60) Read DBBOUT register
0 1 1 (0x64) Read STATUS register
1 0 0 (0x60) Write DBBIN!
1 0 1 (0x64) Write DBBIN!
==> A0-pin simply latches into STATUS-F1-bit on host write, what the UPI can test by JF1 $xx.
-----------------
Data buffers: DBB
-----------------
DBBIN, DBBOUT
"When CS, A0 and RD are low, the contents of the DBBOUT register is placed on the three-state Data lines D0-D7 and the OBF flag is cleared."
"When CS and WR are low, the contents of the system data bus is latched into DBBIN. Also, the IBF flag is set and an interrupt is generated, if enabled."
--------------------
ST - status register
--------------------
= Bus buffer register status word.
ST7 ST6 ST5 ST4 | F1 F0 | IBF OBF
ST7-4: user defined, MOV STS,A writes them, UPI doesn't care more about it.
F0: user defined (JF0 $xx)
F1 = A0-pin (Command/Data)
"OBF Output Buffer Full: This flag is automatically set when the UPI-Microcomputer loads the DBBOUT register and is cleared when the master processor reads the data register."
"IBF Input Buffer Full: This flag is set when the master processor writes a character to the DBBIN register and is cleared when the UPI INputs the data register contents to its accumulator."
---------
I/O PORTS
---------
2 x 8-bit: P1 and P2
OUTL Pn,A
IN A,Pn
"To use a particular PORT pin as an input, a logic '1' must first be written to that pin."
*/

View file

@ -43,6 +43,11 @@ var DumpAPI = require("../../shared/lib/dumpapi");
/**
* FileDump()
*
* TODO: Consider adding a "map" option that allows the user to supply a MAP filename (via a "map" API parameter
* or a "--map" command-line option), which in turn triggers a call to loadMap(). Note that loadMap() will need
* to be a bit more general and use a worker function that calls either net.getFile() or fs.readFile(), similar
* to what our loadFile() function already does.
*
* @constructor
* @param {string|undefined} sFormat should be one of "json"|"data"|"hex"|"bytes"|"rom" (see the FORMAT constants)
* @param {boolean|string|undefined} fComments enables comments and other readability enhancements in the JSON output

View file

@ -555,75 +555,91 @@ ChipSet.PPI_SW.FDRIVE.SHIFT = 6;
* not clear whether that port is managed by the 8042 or independent circuitry.
*
* PPI_B on a MODEL_5170 is also bi-directional: at one point, the BIOS reads bit 5 (PPI_B.DISABLE_RW_MEM) to verify
* that it's alternating (the BIOS calls that bit "REFRESH_BIT").
* that it's alternating (the BIOS refers to it as "REFRESH_BIT").
*
* PPI_C and PPI_CTRL are neither documented nor 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.KBD_DATA = {}; // this.b8042OutBuff
ChipSet.KBD_DATA.PORT = 0x60;
ChipSet.KBD_DATA = { // this.b8042OutBuff
PORT: 0x60
};
ChipSet.KBD_DATA.CMD = {}; // this.b8042CmdData (KBD_DATA.CMD "data bytes" written to port 0x60, after writing a KBD_CMD byte to port 0x64)
ChipSet.KBD_DATA.CMD.PC_COMPAT = 0x40; // generate IBM PC-compatible scan codes
ChipSet.KBD_DATA.CMD.PC_MODE = 0x20;
ChipSet.KBD_DATA.CMD.NO_CLOCK = 0x10; // disable keyboard by driving "clock" line low
ChipSet.KBD_DATA.CMD.NO_INHIBIT = 0x08; // disable inhibit function
ChipSet.KBD_DATA.CMD.SYS_FLAG = 0x04; // this value is propagated to ChipSet.KBD_STATUS.SYS_FLAG
ChipSet.KBD_DATA.CMD.INT_ENABLE = 0x01; // generate an interrupt when the controller places data in the output buffer
ChipSet.KBD_DATA.CMD = { // this.b8042CmdData (KBD_DATA.CMD "data bytes" written to port 0x60, after writing a KBD_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.KBD_STATUS.SYS_FLAG
INT_ENABLE: 0x01 // generate an interrupt when the controller places data in the output buffer
};
ChipSet.KBD_DATA.SELF_TEST = {};
ChipSet.KBD_DATA.SELF_TEST.OK = 0x55;
ChipSet.KBD_DATA.SELF_TEST = {
OK: 0x55
};
ChipSet.KBD_DATA.INTF_TEST = {};
ChipSet.KBD_DATA.INTF_TEST.OK = 0x00;
ChipSet.KBD_DATA.INTF_TEST.CSLO = 0x01;
ChipSet.KBD_DATA.INTF_TEST.CSHI = 0x02;
ChipSet.KBD_DATA.INTF_TEST.DSLO = 0x03;
ChipSet.KBD_DATA.INTF_TEST.DSHI = 0x04;
ChipSet.KBD_DATA.INTF_TEST = { // result of ChipSet.KBD_CMD.INTF_TEST command (0xAB)
OK: 0x00, // no error
KBD_CLOCK_LO: 0x01, // keyboard clock line stuck low
KBD_CLOCK_HI: 0x02, // keyboard clock line stuck high
KBD_DATA_LO: 0x03, // keyboard data line stuck low
KBD_DATA_HI: 0x04 // keyboard data line stuck high
};
ChipSet.KBD_DATA.INPORT = {}; // this.b8042InPort
ChipSet.KBD_DATA.INPORT.EN256KB = 0x10; // enable 2nd 256Kb of system board RAM
ChipSet.KBD_DATA.INPORT.MFG_OFF = 0x20; // manufacturing jumper not installed
ChipSet.KBD_DATA.INPORT.MONO = 0x40; // monochrome monitor is primary display
ChipSet.KBD_DATA.INPORT.KBD_ON = 0x80; // keyboard unlocked
ChipSet.KBD_DATA.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
};
ChipSet.KBD_DATA.OUTPORT = {}; // this.b8042OutPort
ChipSet.KBD_DATA.OUTPORT.RESET = 0x01;
ChipSet.KBD_DATA.OUTPORT.A20 = 0x02;
ChipSet.KBD_DATA.OUTPORT.OBFULL = 0x10;
ChipSet.KBD_DATA.OUTPORT.IBEMPTY= 0x20;
ChipSet.KBD_DATA.OUTPORT.KBCLK = 0x40;
ChipSet.KBD_DATA.OUTPORT.KBDATA = 0x80;
ChipSet.KBD_DATA.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)
};
ChipSet.KBD_DATA.TESTPORT = {}; // generated "on the fly"
ChipSet.KBD_DATA.TESTPORT.CLOCK = 0x01;
ChipSet.KBD_DATA.TESTPORT.DATA = 0x02;
ChipSet.KBD_DATA.TESTPORT = { // generated "on the fly"
KBD_CLOCK: 0x01, // keyboard clock (input)
KBD_DATA: 0x02 // keyboard data (input)
};
ChipSet.KBD_CMD = {}; // this.b8042InBuff (on write to port 0x64, interpret this as a CMD)
ChipSet.KBD_CMD.PORT = 0x64;
ChipSet.KBD_CMD.READ_CMD = 0x20;
ChipSet.KBD_CMD.WRITE_CMD = 0x60; // followed by a command byte written to KBD_DATA.PORT (see KBD_DATA.CMD)
ChipSet.KBD_CMD.SELF_TEST = 0xAA; // self-test (KBD_DATA.SELF_TEST_OK is placed in the output buffer if no errors)
ChipSet.KBD_CMD.INTF_TEST = 0xAB; // interface test
ChipSet.KBD_CMD.DIAG_DUMP = 0xAC; // diagnostic dump
ChipSet.KBD_CMD.DISABLE_KBD = 0xAD; // disable keyboard
ChipSet.KBD_CMD.ENABLE_KBD = 0xAE; // enable keyboard
ChipSet.KBD_CMD.READ_INPORT = 0xC0; // read input port and place data in output buffer (use only if output buffer empty)
ChipSet.KBD_CMD.READ_OUTPORT = 0xD0; // read output port and place data in output buffer (use only if output buffer empty)
ChipSet.KBD_CMD.WRITE_OUTPORT = 0xD1; // next byte written to KBD_DATA.PORT (port 0x60) is placed in the output port (see KBD_DATA.OUTPUT)
ChipSet.KBD_CMD.READ_TEST = 0xE0;
ChipSet.KBD_CMD.PULSE_OUTPORT = 0xF0; // this is the 1st of 16 commands (0xF0-0xFF) that pulse bits 0-3 of the output port
ChipSet.KBD_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 KBD_DATA.PORT (see KBD_DATA.CMD)
SELF_TEST: 0xAA, // self-test (KBD_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 KBD_DATA.PORT (port 0x60) is placed in the output port (see KBD_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
};
ChipSet.KBD_STATUS = {}; // this.b8042Status (on read from port 0x64)
ChipSet.KBD_STATUS.PORT = 0x64;
ChipSet.KBD_STATUS.OUTBUFF_FULL = 0x01;
ChipSet.KBD_STATUS.INBUFF_FULL = 0x02; // set if the controller has received but not yet read data written to the input buffer (not normally set)
ChipSet.KBD_STATUS.SYS_FLAG = 0x04;
ChipSet.KBD_STATUS.CMD_FLAG = 0x08; // set on write to KBD_CMD (port 0x64), clear on write to KBD_DATA (port 0x60)
ChipSet.KBD_STATUS.NO_INHIBIT = 0x10;
ChipSet.KBD_STATUS.XMT_TIMEOUT = 0x20;
ChipSet.KBD_STATUS.RCV_TIMEOUT = 0x40;
ChipSet.KBD_STATUS.PARITY_ERR = 0x80; // last byte of data received had EVEN parity (ODD parity is normally expected)
ChipSet.KBD_STATUS.OUTBUFF_DELAY= 0x100;
ChipSet.KBD_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 KBD_CMD (port 0x64), clear on write to KBD_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)
@ -632,7 +648,7 @@ ChipSet.KBD_STATUS.OUTBUFF_DELAY= 0x100;
*
* 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
ChipSet.CMOS_ADDR = {}; // this.bCMOSAddr
ChipSet.CMOS_ADDR.PORT = 0x70;
ChipSet.CMOS_ADDR.RTC_SEC = 0x00;
ChipSet.CMOS_ADDR.RTC_SEC_ALRM = 0x01;
@ -966,17 +982,17 @@ ChipSet.prototype.reset = function()
*/
this.b8042Status = ChipSet.KBD_STATUS.NO_INHIBIT;
this.b8042InBuff = 0;
this.b8042CmdData = 0;
this.b8042CmdData = ChipSet.KBD_DATA.CMD.NO_CLOCK;
this.b8042OutBuff = 0;
/*
* TODO: Provide more control over these 8042 "Input Port" bits (eg, the keyboard lock)
*/
this.b8042InPort = ChipSet.KBD_DATA.INPORT.MFG_OFF | ChipSet.KBD_DATA.INPORT.KBD_ON;
if (this.getSWMemorySize() >= 512) this.b8042InPort |= ChipSet.KBD_DATA.INPORT.EN256KB;
if (this.getSWMemorySize() >= 512) this.b8042InPort |= ChipSet.KBD_DATA.INPORT.ENABLE_256KB;
if (this.getSW1VideoMonitor() == ChipSet.MONITOR.MONO) this.b8042InPort |= ChipSet.KBD_DATA.INPORT.MONO;
this.b8042OutPort = ChipSet.KBD_DATA.OUTPORT.A20;
this.b8042OutPort = ChipSet.KBD_DATA.OUTPORT.NO_RESET | ChipSet.KBD_DATA.OUTPORT.A20_ON;
this.bCMOSAddr = 0; // NMI is enabled, since the ChipSet.CMOS_ADDR.NMI_DISABLE bit is not set in bCMOSAddr
this.abCMOSData = new Array(ChipSet.CMOS_ADDR.TOTAL);
this.initRTCDate(this.sRTCDate);
@ -2563,7 +2579,7 @@ ChipSet.prototype.outPICH = function(iPIC, bOut, addrFrom)
*/
this.cpu.delayINTR();
/*
* Alas, we need an even longer delay for the MODEL_5170's "KBD_RESET" function, which must drop
* Alas, we need a longer delay for the MODEL_5170's "KBD_RESET" function (F000:17D2), which must drop
* into a loop and decrement CX at least once after unmasking the KBD IRQ. The "KBD_RESET" function on
* previous models could be handled with a 4-instruction delay provided by the Keyboard.resetDevice() call
* to setIRR(), but the MODEL_5170 needs a roughly 6-instruction delay after it unmasks the KBD IRQ.
@ -2659,14 +2675,14 @@ ChipSet.prototype.checkIMR = function(nIRQ)
/**
* getIRRVector()
*
* getIRRVector() is called by the CPU whenever PS_IF is set and OP_NOINTR is clear. Ordinarily, an immediate response would
* seem perfectly reasonable, but unfortunately, there are places in the ROM BIOS (eg, the "KBD_RESET" function @F000:E688)
* that enable interrupts but still expect nothing to happen for several more instructions.
* getIRRVector() is called by the CPU whenever PS_IF is set and OP_NOINTR is clear. Ordinarily, an immediate
* response would seem perfectly reasonable, but unfortunately, there are places in the original ROM BIOS like
* "KBD_RESET" (F000:E688) that enable interrupts but still expect nothing to happen for several more instructions.
*
* So, in addition to the two normal responses (an IDT vector #, or -1 indicating no pending interrupts), we must support
* a third response (-2) that basically means: don't change the CPU interrupt state, just keep calling until we return one
* of the first two responses. The number of times we delay our normal response is determined by the component that originally
* called setIRR with an optional delay parameter.
* So, in addition to the two normal responses (an IDT vector #, or -1 indicating no pending interrupts), we must
* support a third response (-2) that basically means: don't change the CPU interrupt state, just keep calling until
* we return one of the first two responses. The number of times we delay our normal response is determined by the
* component that originally called setIRR with an optional delay parameter.
*
* @this {ChipSet}
* @param {number} [iPIC]
@ -2863,7 +2879,7 @@ ChipSet.prototype.outTimer = function(iTimer, bOut, addrFrom)
ChipSet.prototype.inTimerCtrl = function(port, addrFrom)
{
this.messagePort(port, null, addrFrom, "TIMER_CTRL", ChipSet.MESSAGE_TIMER);
if (DEBUG) this.messageDebugger("Timer[CTRL]: Read-Back command not supported (yet)", ChipSet.MESSAGE_TIMER);
if (DEBUG) this.messageDebugger("TIMER_CTRL: Read-Back command not supported (yet)", ChipSet.MESSAGE_TIMER);
return null;
};
@ -3432,17 +3448,11 @@ ChipSet.prototype.outPPICtrl = function(port, bOut, addrFrom)
*/
ChipSet.prototype.in8042OutBuff = function(port, addrFrom)
{
this.messagePort(port, null, addrFrom, "8042_OUTBUFF", ChipSet.MESSAGE_CHIPSET, this.b8042OutBuff);
this.b8042Status &= ~ChipSet.KBD_STATUS.OUTBUFF_FULL;
var b = this.b8042OutBuff;
this.messagePort(port, null, addrFrom, "8042_OUTBUFF", ChipSet.MESSAGE_CHIPSET, b);
this.b8042Status &= ~(ChipSet.KBD_STATUS.OUTBUFF_FULL | ChipSet.KBD_STATUS.OUTBUFF_DELAY);
var bNext = this.kbd && this.kbd.readScanCode(true);
if (bNext) {
this.b8042OutBuff = bNext;
/*
* TODO: Determine why setting OUTBUFF_DELAY instead of OUTBUFF_FULL here causes "AA 301-Keyboard Error" during POST
*/
this.b8042Status |= ChipSet.KBD_STATUS.OUTBUFF_FULL;
}
if (bNext) this.set8042OutBuff(bNext);
return b;
};
@ -3472,20 +3482,7 @@ ChipSet.prototype.out8042InBuffData = function(port, bOut, addrFrom)
break;
case ChipSet.KBD_CMD.WRITE_OUTPORT:
this.b8042OutPort = bOut;
this.bus.setA20(!!(this.b8042OutPort & ChipSet.KBD_DATA.OUTPORT.A20));
if (!(this.b8042OutPort & ChipSet.KBD_DATA.OUTPORT.RESET)) {
/*
* Bit 0 of the 8042's output port is connected to RESET. Normally, it's "pulsed" with the
* KBD_CMD.PULSE_OUTPORT command, so if a RESET is detected via this command, we should try to
* determine if that's what the caller intended.
*/
if (DEBUG) {
this.messageDebugger("unexpected 8042 output port reset: " + str.toHexByte(this.b8042OutPort));
this.cpu.haltCPU();
}
this.cpu.resetRegs();
}
this.set8042OutPort(bOut);
break;
/*
@ -3531,8 +3528,8 @@ ChipSet.prototype.out8042InBuffData = function(port, bOut, addrFrom)
* F000:1B62 83E901 SUB CX,0001 ; EXIT WITH SUCCESS (CX != 0)
* F000:1B65 C3 RET
*
* But WAIT, the FUN doesn't end there. After this function returns, KBD_RESET waits for a Keyboard interrupt
* to occur, hoping for a 0xAA scan code as the Keyboard's final response. KBD_RESET also returns CX to the caller,
* But WAIT, the FUN doesn't end there. After this function returns, "KBD_RESET" waits for a Keyboard interrupt
* to occur, hoping for a 0xAA scan code as the Keyboard's final response. "KBD_RESET" also returns CX to the caller,
* and the caller ("TEST.21") assumes there was no interrupt if CX is zero.
*
* MOV AL,0FDH
@ -3552,13 +3549,8 @@ ChipSet.prototype.out8042InBuffData = function(port, bOut, addrFrom)
* CX can be zero not only if the loop exhausted it, but also if no looping was required!
*/
default:
if (this.kbd) {
var b = this.kbd.sendCmd(bOut);
if (b >= 0) {
this.b8042OutBuff = b;
this.b8042Status |= ChipSet.KBD_STATUS.OUTBUFF_DELAY;
}
}
this.b8042CmdData &= ~ChipSet.KBD_DATA.CMD.NO_CLOCK;
if (this.kbd) this.set8042OutBuff(this.kbd.sendCmd(bOut));
break;
}
}
@ -3589,9 +3581,9 @@ ChipSet.prototype.in8042Status = function(port, addrFrom)
* (which is outside the 0xff range of bits we return); when we see KBD_STATUS.OUTBUFF_DELAY,
* we clear it and set KBD_STATUS.OUTBUFF_FULL, which will be returned on the next read.
*
* This provides a single-poll delay, so that the aforementioned "flush" won't occur. If longer
* delays are needed down the road, we may need to set a delay count in the upper (hidden) bits
* of b8042Status, instead of using a single "OUTBUFF_DELAY" bit.
* This provides a single poll delay, so that the aforementioned "flush" won't toss our response.
* If longer delays are needed down the road, we may need to set a delay count in the upper (hidden)
* bits of b8042Status, instead of using a single "OUTBUFF_DELAY" bit.
*/
if (this.b8042Status & ChipSet.KBD_STATUS.OUTBUFF_DELAY) {
this.b8042Status |= ChipSet.KBD_STATUS.OUTBUFF_FULL;
@ -3630,37 +3622,41 @@ ChipSet.prototype.out8042InBuffCmd = function(port, bOut, addrFrom)
}
switch (this.b8042InBuff) {
/*
* No further action is required for this first group of commands; more data is expected via out8042InBuffData().
*/
case ChipSet.KBD_CMD.WRITE_CMD: // 0x60
case ChipSet.KBD_CMD.WRITE_OUTPORT: // 0xD1
/*
* No further action required for this first group of commands; more data is expected via out8042InBuffData()
*/
break;
case ChipSet.KBD_CMD.READ_INPORT: // 0xC0
this.b8042OutBuff = this.b8042InPort;
this.b8042Status |= ChipSet.KBD_STATUS.OUTBUFF_DELAY;
this.set8042OutBuff(this.b8042InPort);
break;
case ChipSet.KBD_CMD.DISABLE_KBD: // 0xAD
this.b8042CmdData |= ChipSet.KBD_DATA.CMD.NO_CLOCK;
if (DEBUG) this.messageDebugger("keyboard disabled", ChipSet.MESSAGE_KBD);
/*
* TODO: Determine where to honor KBD_DATA.CMD.NO_CLOCK; note that the MODEL_5170 BIOS calls "KBD_RESET" (F000:17D2)
* while the keyboard interface is disabled, yet we must still deliver the Keyboard's CMDRES.BATSUCCESS response code.
*/
break;
case ChipSet.KBD_CMD.ENABLE_KBD: // 0xAE
this.b8042CmdData &= ~ChipSet.KBD_DATA.CMD.NO_CLOCK;
if (DEBUG) this.messageDebugger("keyboard re-enabled", ChipSet.MESSAGE_KBD);
break;
case ChipSet.KBD_CMD.SELF_TEST: // 0xAA
this.b8042OutBuff = ChipSet.KBD_DATA.SELF_TEST.OK;
this.b8042Status |= ChipSet.KBD_STATUS.OUTBUFF_DELAY;
if (this.kbd) this.kbd.shiftScanCode(true);
this.b8042CmdData |= ChipSet.KBD_DATA.CMD.NO_CLOCK;
if (DEBUG) this.messageDebugger("keyboard disabled on reset", ChipSet.MESSAGE_KBD);
this.set8042OutBuff(ChipSet.KBD_DATA.SELF_TEST.OK);
this.set8042OutPort(ChipSet.KBD_DATA.OUTPORT.NO_RESET | ChipSet.KBD_DATA.OUTPORT.A20_ON);
break;
case ChipSet.KBD_CMD.READ_TEST: // 0xE0
/*
* TODO: Do we need to "OR" anything here for KBD_DATA.TESTPORT.DATA?
*/
this.b8042OutBuff = ((this.b8042CmdData & ChipSet.KBD_DATA.CMD.NO_CLOCK)? 0 : ChipSet.KBD_DATA.TESTPORT.CLOCK);
this.b8042Status |= ChipSet.KBD_STATUS.OUTBUFF_DELAY;
this.set8042OutBuff((this.b8042CmdData & ChipSet.KBD_DATA.CMD.NO_CLOCK)? 0 : ChipSet.KBD_DATA.TESTPORT.KBD_CLOCK);
break;
case ChipSet.KBD_CMD.PULSE_OUTPORT: // 0xF0-0xFF
@ -3675,12 +3671,53 @@ ChipSet.prototype.out8042InBuffCmd = function(port, bOut, addrFrom)
break;
default:
this.messageDebugger("unrecognized 8042 command: " + str.toHexByte(this.b8042InBuff));
this.cpu.haltCPU();
if (DEBUG && DEBUGGER && this.dbg) {
this.dbg.message("unrecognized 8042 command: " + str.toHexByte(this.b8042InBuff));
this.cpu.haltCPU();
}
break;
}
};
/**
* set8042OutBuff(b)
*
* @this {ChipSet}
* @param {number} b
*/
ChipSet.prototype.set8042OutBuff = function(b)
{
if (b >= 0) {
this.b8042OutBuff = b;
this.b8042Status &= ~ChipSet.KBD_STATUS.OUTBUFF_FULL;
this.b8042Status |= ChipSet.KBD_STATUS.OUTBUFF_DELAY;
}
};
/**
* set8042OutPort(b)
*
* @this {ChipSet}
* @param {number} b
*/
ChipSet.prototype.set8042OutPort = function(b)
{
this.b8042OutPort = b;
this.bus.setA20(!!(b & ChipSet.KBD_DATA.OUTPORT.A20_ON));
if (!(b & ChipSet.KBD_DATA.OUTPORT.NO_RESET)) {
/*
* Bit 0 of the 8042's output port is connected to RESET. Normally, it's "pulsed" with the
* KBD_CMD.PULSE_OUTPORT command, so if a RESET is detected via this command, we should try to
* determine if that's what the caller intended.
*/
if (DEBUG && DEBUGGER && this.dbg) {
this.dbg.message("unexpected 8042 output port reset: " + str.toHexByte(b));
this.cpu.haltCPU();
}
this.cpu.resetRegs();
}
};
/**
* inCMOSAddr(port, addrFrom)
*

View file

@ -250,7 +250,16 @@ CPU.prototype.powerUp = function(data, fRepower)
/*
* Give the Debugger a chance to do/print something once we've powered up (TODO: Review the necessity of this)
*/
if (DEBUGGER && this.dbg) this.dbg.init();
if (DEBUGGER && this.dbg) {
this.dbg.init();
} else {
/*
* TODO: Once we get rid of those nasty Component method overrides, this test will have to be revised as well
*/
if (Component.controlPrint) {
this.warning("No debugger detected");
}
}
}
this.fPowered = true;
if (!this.autoStart() && this.dbg) {
@ -1081,27 +1090,6 @@ CPU.prototype.updateCPU = function()
this.displayStatus();
};
/**
* waitCPU()
*
* Similar to haltCPU() with regard to how it resets various cycle countdown values, but the CPU
* remains in a "running" state, without yielding.
*
* TODO: This was originally used by opHLT(), but this seems rather pointless in hindsight, because
* this call will only end the current stepCPU() iteration; we'll immediately go back into stepCPU(),
* except that X86.INTFLAG.HALT will be set, so we won't execute any more instructions, not even opHLT(),
* until a hardware interrupt is acknowledged. However, it would still be nice if we could reduce CPU
* overhead while in a halted state.
*
* @this {CPU}
*
CPU.prototype.waitCPU = function()
{
this.nBurstCycles -= this.nStepCycles;
this.nStepCycles = 0; // this will break us out of stepCPU()
};
*/
/**
* yieldCPU()
*

View file

@ -132,14 +132,12 @@ function Debugger(parmsDbg)
this.clearBreakpoints();
/*
* Instead of pre-allocating these arrays, we wait until the reset() function is called.
* These arrays are updated in checkInstruction(), but the CPU will never actually call it
* unless checksEnabled() returns true, and that won't happen until one or more breakpoints
* have been set. This ensures that, by default, the CPU runs as fast as possible.
* Execution history is allocated by initHistory() whenever checksEnabled() conditions change.
* Execution history is updated whenever the CPU calls checkInstruction(), which will happen only
* when checksEnabled() returns true (eg, whenever one or more breakpoints have been set).
* This ensures that, by default, the CPU runs as fast as possible.
*/
this.iStepHistory = 0;
this.aStepHistory = [];
this.aaOpcodeFreqs = [];
this.initHistory();
/*
* Message categories supported by the messageEnabled() function and other assorted message
@ -516,22 +514,25 @@ if (DEBUGGER) {
* Based on the active CPU model, we make every effort to execute and disassemble this (and every other)
* opcode appropriately, by setting the opcode's entry in aaOpDescs accordingly. 0x0F defaults to the 8086
* entry: aOpDescPopCS.
*
* Note that we do NOT modify aaOpDescs directly; this.aaOpDescs is a reference to it if the processor
* is an 8086, otherwise we make a copy of the array and THEN modify it.
*/
Debugger.aOpDescPopCS = [Debugger.INS.POP, Debugger.TYPE_CS | Debugger.TYPE_OUT];
Debugger.aOpDescUndefined = [Debugger.INS.NONE, Debugger.TYPE_NONE];
Debugger.aOpDesc0F = [Debugger.INS.OP0F, Debugger.TYPE_WORD | Debugger.TYPE_BOTH];
/*
* The aaOpDescs array is indexed by opcode, and each element is a sub-array (aOpDesc)
* that describes the corresponding opcode. The sub-elements are as follows:
* The aaOpDescs array is indexed by opcode, and each element is a sub-array (aOpDesc) that describes
* the corresponding opcode. The sub-elements are as follows:
*
* [0]: {number} of the opcode name (see INS.*)
* [1]: {number} containing the destination operand descriptor bit(s)
* [2]: {number} containing the source operand descriptor bit(s)
*
* These sub-elements are all optional. If [0] is not present, the opcode is undefined;
* if [1] is not present (or contains zero), the opcode has no (or only implied) operands;
* and if [2] is not present, the opcode has only a single operand.
* These sub-elements are all optional. If [0] is not present, the opcode is undefined; if [1] is not
* present (or contains zero), the opcode has no (or only implied) operands; and if [2] is not present,
* the opcode has only a single operand.
*/
Debugger.aaOpDescs = [
/* 0x00 */ [Debugger.INS.ADD, Debugger.TYPE_MODRM | Debugger.TYPE_BYTE | Debugger.TYPE_BOTH, Debugger.TYPE_REG | Debugger.TYPE_BYTE | Debugger.TYPE_IN],
@ -1114,10 +1115,12 @@ if (DEBUGGER) {
this.hdc = cmp.getComponentByType("HDC");
if (MAXDEBUG) this.chipset = cmp.getComponentByType("ChipSet");
this.aaOpDescs = Debugger.aaOpDescs;
if (this.cpu.model >= X86.MODEL_80186) {
Debugger.aaOpDescs[0x0F] = Debugger.aOpDescUndefined;
this.aaOpDescs = Debugger.aaOpDescs.slice();
this.aaOpDescs[0x0F] = Debugger.aOpDescUndefined;
if (this.cpu.model >= X86.MODEL_80286) {
Debugger.aaOpDescs[0x0F] = Debugger.aOpDesc0F;
this.aaOpDescs[0x0F] = Debugger.aOpDesc0F;
}
}
@ -1459,7 +1462,47 @@ if (DEBUGGER) {
// this.doHelp();
this.println("Type ? for list of debugger commands");
};
/**
* initHistory()
*
* This function is intended to be called by the constructor, reset(), addBreakpoint(), findBreakpoint()
* and any other function that changes the checksEnabled() criteria used to decide whether checkInstruction()
* should be called.
*
* That is, if the history arrays need to be allocated and haven't already been allocated, then allocate them,
* and if the arrays are no longer needed, then deallocate them.
*
* @this {Debugger}
*/
Debugger.prototype.initHistory = function()
{
var i;
if (!this.checksEnabled()) {
this.iOpcodeHistory = 0;
this.aOpcodeHistory = [];
this.aaOpcodeCounts = [];
return;
}
if (!this.aOpcodeHistory || !this.aOpcodeHistory.length) {
this.aOpcodeHistory = new Array(10000);
for (i = 0; i < this.aOpcodeHistory.length; i++) {
/*
* Preallocate dummy Addr (Array) objects in every history slot, so that checkInstruction()
* doesn't need to call newAddr() on every instruction check.
*/
this.aOpcodeHistory[i] = [0, 0, 0];
}
this.iOpcodeHistory = 0;
}
if (!this.aaOpcodeCounts || !this.aaOpcodeCounts.length) {
this.aaOpcodeCounts = new Array(256);
for (i = 0; i < this.aaOpcodeCounts.length; i++) {
this.aaOpcodeCounts[i] = [i, 0];
}
}
};
/**
* runCPU(fOnClick)
*
@ -1625,19 +1668,7 @@ if (DEBUGGER) {
*/
Debugger.prototype.reset = function(fQuiet)
{
var i;
if (!this.aStepHistory.length) {
this.aStepHistory = new Array(10000);
}
for (i = 0; i < this.aStepHistory.length; i++) {
this.aStepHistory[i] = [];
}
if (!this.aaOpcodeFreqs.length) {
this.aaOpcodeFreqs = new Array(256);
}
for (i = 0; i < this.aaOpcodeFreqs.length; i++) {
this.aaOpcodeFreqs[i] = [i, 0];
}
this.initHistory();
this.cInstructions = 0;
this.nCycles = 0;
this.aAddrNextCode = this.newAddr(this.cpu.regIP, this.cpu.segCS.sel);
@ -1651,7 +1682,7 @@ if (DEBUGGER) {
this.clearTempBreakpoint();
if (!fQuiet) this.updateStatus();
};
/**
* save()
*
@ -1798,29 +1829,39 @@ if (DEBUGGER) {
* @this {Debugger}
* @param {number} addr
* @param {boolean} [fSkipBP] is true to skip breakpoint check
* @return {boolean} true to proceed, false to halt
* @return {boolean} true if breakpoint hit, false if not
*/
Debugger.prototype.checkInstruction = function(addr, fSkipBP)
{
var fBreak = false;
/*
* Assert that general-purpose register contents remain within their respective ranges;
* this isn't intended to be complete, just a spot-check.
*/
Component.assert(!(this.cpu.regAX & ~0xffff) && !(this.cpu.regBX & ~0xffff) && !(this.cpu.regCX & ~0xffff) && !(this.cpu.regDX & ~0xffff), "register out of bounds");
if (!fSkipBP && this.checkBreakpoint(addr, this.aBreakExec))
fBreak = true;
else {
this.cInstructions++;
var bOpcode = this.bus.getByteDirect(addr);
this.aaOpcodeFreqs[bOpcode][1]++;
this.aStepHistory[this.iStepHistory++] = this.newAddr(this.cpu.regIP, this.cpu.segCS.sel);
if (this.iStepHistory == this.aStepHistory.length) {
this.iStepHistory = 0;
}
if (!fSkipBP && this.checkBreakpoint(addr, this.aBreakExec)) {
return true;
}
return !fBreak;
this.cInstructions++;
var bOpcode = this.bus.getByteDirect(addr);
this.aaOpcodeCounts[bOpcode][1]++;
/*
* This is a good example of what NOT to do in a high-frequency function, and defeats
* the entire purpose of preallocating and preinitializing the history array in initHistory():
*
* this.aOpcodeHistory[this.iOpcodeHistory] = this.newAddr(this.cpu.regIP, this.cpu.segCS.sel, addr);
*
* As the name implies, newAddr() returns a new "Addr" (Array) object every time it's called.
*/
var a = this.aOpcodeHistory[this.iOpcodeHistory];
a[0] = this.cpu.regIP;
a[1] = this.cpu.segCS.sel;
a[2] = addr;
if (++this.iOpcodeHistory == this.aOpcodeHistory.length) this.iOpcodeHistory = 0;
return false;
};
/**
@ -1835,12 +1876,11 @@ if (DEBUGGER) {
*/
Debugger.prototype.checkMemoryRead = function(addr)
{
var fBreak = false;
if (this.checkBreakpoint(addr, this.aBreakRead)) {
this.cpu.haltCPU(true);
fBreak = true;
return true;
}
return fBreak;
return false;
};
/**
@ -1855,12 +1895,11 @@ if (DEBUGGER) {
*/
Debugger.prototype.checkMemoryWrite = function(addr)
{
var fBreak = false;
if (this.checkBreakpoint(addr, this.aBreakWrite)) {
this.cpu.haltCPU(true);
fBreak = true;
return true;
}
return fBreak;
return false;
};
/**
@ -2081,16 +2120,17 @@ if (DEBUGGER) {
};
/**
* newAddr(off, seg)
* newAddr(off, seg, addr)
*
* @this {Debugger}
* @param {number} off
* @param {number} seg
* @return {Array} containing [off, seg]
* @param {number} [addr] is the physical address, if known
* @return {Array} containing [off, seg, addr]
*/
Debugger.prototype.newAddr = function(off, seg)
Debugger.prototype.newAddr = function(off, seg, addr)
{
return [off, seg];
return [off, seg, addr];
};
/**
@ -2141,6 +2181,7 @@ if (DEBUGGER) {
this.bus.addMemoryBreakpoint(this.getAddr(aAddr), aBreak == this.aBreakWrite);
}
if (!fTemp) this.println("breakpoint enabled: " + this.hexAddr(aAddr) + " (" + aBreak[0] + ")");
this.initHistory();
return true;
}
return false;
@ -2169,6 +2210,7 @@ if (DEBUGGER) {
this.bus.removeMemoryBreakpoint(addr, aBreak == this.aBreakWrite);
}
if (!aAddrBreak[3]) this.println("breakpoint cleared: " + this.hexAddr(aAddrBreak) + " (" + aBreak[0] + ")");
this.initHistory();
break;
}
this.println("breakpoint exists: " + this.hexAddr(aAddrBreak) + " (" + aBreak[0] + ")");
@ -2309,7 +2351,7 @@ if (DEBUGGER) {
var aAddrIns = this.newAddr(aAddr[0], aAddr[1]);
var bOpcode = this.getByte(aAddr, 1);
var aOpDesc = Debugger.aaOpDescs[bOpcode];
var aOpDesc = this.aaOpDescs[bOpcode];
var iIns = aOpDesc[0];
var bModRM = -1;
@ -3399,10 +3441,10 @@ if (DEBUGGER) {
}
var i;
var cData = 0;
if (this.aaOpcodeFreqs) {
if (this.aaOpcodeCounts) {
if (sParm == "clear") {
for (i = 0; i < this.aaOpcodeFreqs.length; i++)
this.aaOpcodeFreqs[i] = [i, 0];
for (i = 0; i < this.aaOpcodeCounts.length; i++)
this.aaOpcodeCounts[i] = [i, 0];
this.println("frequency data cleared");
cData++;
}
@ -3411,15 +3453,15 @@ if (DEBUGGER) {
cData++;
}
else {
var aaSortedOpcodeFreqs = this.aaOpcodeFreqs.slice();
aaSortedOpcodeFreqs.sort(function(p, q) {
var aaSortedOpcodeCounts = this.aaOpcodeCounts.slice();
aaSortedOpcodeCounts.sort(function(p, q) {
return q[1] - p[1];
});
for (i = 0; i < aaSortedOpcodeFreqs.length; i++) {
var bOpcode = aaSortedOpcodeFreqs[i][0];
var cFreq = aaSortedOpcodeFreqs[i][1];
for (i = 0; i < aaSortedOpcodeCounts.length; i++) {
var bOpcode = aaSortedOpcodeCounts[i][0];
var cFreq = aaSortedOpcodeCounts[i][1];
if (cFreq) {
this.println((Debugger.asIns[Debugger.aaOpDescs[bOpcode][0]] + " ").substr(0, 5) + " (" + str.toHexByte(bOpcode) + "): " + cFreq + " times");
this.println((Debugger.asIns[this.aaOpDescs[bOpcode][0]] + " ").substr(0, 5) + " (" + str.toHexByte(bOpcode) + "): " + cFreq + " times");
cData++;
}
}
@ -3446,8 +3488,8 @@ if (DEBUGGER) {
}
var sMore = "";
var cLines = 10;
var iHistory = this.iStepHistory;
var aHistory = this.aStepHistory;
var iHistory = this.iOpcodeHistory;
var aHistory = this.aOpcodeHistory;
if (aHistory !== undefined) {
var n = (sCount === undefined? this.nextHistory : parseInt(sCount, 10));
if (isNaN(n))
@ -3470,14 +3512,14 @@ if (DEBUGGER) {
if (sCount !== undefined) {
this.println(n + " instructions earlier:");
}
while (cLines && iHistory != this.iStepHistory) {
while (cLines && iHistory != this.iOpcodeHistory) {
var aAddr = aHistory[iHistory];
if (!aAddr.length) break;
/*
* We must create a new aAddr from the address we obtained from aHistory, because
* it was a reference, not a copy, and we don't want getInstruction() modifying the original.
*/
aAddr = this.newAddr(aAddr[0], aAddr[1]);
aAddr = this.newAddr(aAddr[0], aAddr[1], aAddr[2]);
this.println(this.getInstruction(aAddr, "history", -n));
if (++iHistory == aHistory.length) iHistory = 0;
this.nextHistory = --n;

View file

@ -582,7 +582,7 @@ Keyboard.prototype.resetDevice = function()
* TODO: There's more to reset, like LED indicators, default type rate, and emptying the scan code buffer.
*/
this.messageDebugger("keyboard reset", true);
this.abScanBuffer = [0xAA];
this.abScanBuffer = [Keyboard.CMDRES.BATSUCCESS];
if (this.chipset) this.chipset.setIRR(ChipSet.IRQ.KBD, 4);
};
@ -638,6 +638,8 @@ Keyboard.prototype.sendCmd = function(bCmd)
b = Keyboard.CMDRES.ACK;
this.resetDevice();
break;
default:
break;
}
return b;
};
@ -648,7 +650,7 @@ Keyboard.prototype.sendCmd = function(bCmd)
* This is the ChipSet's interface for reading scan codes.
*
* @this {Keyboard}
* @param {boolean} [fShift]
* @param {boolean} [fShift] is used by the MODEL_5170 8042 Keyboard Controller (supersedes the old setEnable() interface)
* @return {number} next scan code, or 0 if none
*/
Keyboard.prototype.readScanCode = function(fShift)
@ -663,23 +665,32 @@ Keyboard.prototype.readScanCode = function(fShift)
};
/**
* shiftScanCode()
* shiftScanCode(fFlush)
*
* This is the ChipSet's interface to advance scan codes.
* This is the ChipSet's interface to advance (or flush) scan codes.
*
* @this {Keyboard}
* @param {boolean} [fFlush] is true to completely flush the keyboard buffer
*/
Keyboard.prototype.shiftScanCode = function()
Keyboard.prototype.shiftScanCode = function(fFlush)
{
if (this.abScanBuffer.length > 0) {
/*
* The keyboard interrupt service routine toggles the enable bit after reading a scan code, so
* presumably this is the proper point at which to shift the last scan code out, and then assert
* another interrupt if more scan codes exist.
*/
this.abScanBuffer.shift();
if (this.abScanBuffer.length > 0) {
if (this.chipset) this.chipset.setIRR(ChipSet.IRQ.KBD);
if (fFlush) {
/*
* This is now called after receipt of an 8042 self-test command, to ensure we don't
* overwrite the self-test response byte with left-over scan codes.
*/
this.abScanBuffer = [];
} else {
/*
* The keyboard interrupt service routine toggles the enable bit after reading a scan code, so
* presumably this is the proper point at which to shift the last scan code out, and then assert
* another interrupt if more scan codes exist.
*/
this.abScanBuffer.shift();
if (this.abScanBuffer.length > 0) {
if (this.chipset) this.chipset.setIRR(ChipSet.IRQ.KBD);
}
}
}
};

View file

@ -2248,7 +2248,7 @@ X86CPU.prototype.pushWord = function(w)
*
* ERRATA: I do recall that early revisions of the 8086/8088 failed to suppress hardware interrupts (and
* possibly also Trap acknowledgements) after an SS load, but that Intel corrected the problem at some point;
* however, I don't know exactly when that change was made or which IBM PC models may have been affected, if any.
* however, I don't know when that change was made or which IBM PC models may have been affected, if any.
* TODO: More research required.
*
* WARNING: There is also a priority consideration here. On the 8086/8088, hardware interrupts have higher
@ -2268,7 +2268,7 @@ X86CPU.prototype.checkINTR = function()
if (!(this.opFlags & X86.OPFLAG.NOINTR)) {
if ((this.intFlags & X86.INTFLAG.INTR) && (this.regPS & X86.PS.IF)) {
var nIDT = this.chipset.getIRRVector();
if (nIDT != -2) {
if (nIDT >= -1) {
this.intFlags &= ~X86.INTFLAG.INTR;
if (nIDT >= 0) {
this.intFlags &= ~X86.INTFLAG.HALT;
@ -2473,8 +2473,8 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
if (this.intFlags) {
if (this.checkINTR()) {
/*
* ASSERT: If it's never possible to have !nMinCycles WITHOUT the Debugger, then all
* we need to check is !nMinCycles.
* ASSERT: If it's never possible to have !nMinCycles WITHOUT the Debugger, then all we need
* to check is !nMinCycles.
*/
if (DEBUGGER && !nMinCycles) {
this.opFlags = 0;
@ -2483,25 +2483,28 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
}
if (this.intFlags & X86.INTFLAG.HALT) {
/*
* Even though we're technically "halted", we still need to keep the cycle count moving;
* otherwise the whole point of staying in the runCPU() loop (ie, to continue calling
* video.updateScreen() from runCPU(), as well as chipset.updateAllTimers() from stepCPU())
* is lost, because both those functions depend on movement in the cycle count.
*
* TODO: Another option here would be to decrement IP and execute the HLT repeatedly,
* but that had a surprisingly bad impact on performance; seems like a better idea would
* be to simply keep pretending that we just executed the remaining nStepCycles, and let
* runCPU() sleep for the remainder of the burst, so that we get some power savings.
* As discussed in opHLT(), the CPU is never REALLY halted by a HLT instruction; instead,
* opHLT() sets X86.INTFLAG.HALT, signalling to us that we're free to end the current burst
* AND that we should not execute any more instructions until checkINTR() indicates a hardware
* interrupt has been requested.
*
* One downside to this approach is that it *might* appear to the careful observer that we
* executed a full complement of instructions during bursts where X86.INTFLAG.HALT was set,
* when in fact we did not. However, the steady advance of the overall cycle count, and thus
* the steady series calls to stepCPU(), is needed to ensure that timer updates, video updates,
* etc, all continue to occur at the expected rates.
*
* If necessary, we can add another bookkeeping cycle counter (eg, one that keeps tracks of the
* number of cycles during which we did not actually execute any instructions).
*/
// this.advanceIP(-1);
this.nStepCycles -= 2;
this.nStepCycles = 0;
this.opFlags = 0;
continue;
break;
}
}
}
if (DEBUGGER && this.fDebugCheck && !this.dbg.checkInstruction(this.regEIP)) {
if (DEBUGGER && this.fDebugCheck && this.dbg.checkInstruction(this.regEIP)) {
this.haltCPU();
break;
}
@ -2524,19 +2527,19 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
if (DEBUG) {
/*
* Some opcode helpers are required to temporarily redirect getEAByte/getEAWord or setEAByte/setEAWord to null
* functions, effectively disabling a memory read that's unnecessary (or a memory write that could be destructive).
* However, they weren't originally required to restore those memory functions when they were done; we would
* simply reset all the memory functions here, after every single instruction.
* Some opcode helpers are required to temporarily redirect getEAByte/getEAWord or setEAByte/setEAWord
* to null functions, effectively disabling a memory read that's unnecessary (or a memory write that could
* be destructive). However, they weren't originally required to restore those memory functions when they
* were done; we would simply reset all the memory functions here, after every single instruction.
*
* That's no longer the case. Those opcode helpers (or their callers) are now required to restore the memory
* access functions to their defaults, so that we don't have to waste time resetting them here, on every instruction.
* The DEBUG-only verifyMemoryEnabled() simply confirms that everyone's doing their job.
* That's no longer the case. Those opcode helpers (or their callers) are now required to restore the
* memory access functions to their defaults, so that we don't have to waste time resetting them here, on
* every instruction. The DEBUG-only verifyMemoryEnabled() simply confirms that everyone's doing their job.
*/
this.verifyMemoryEnabled();
/*
* Make sure every instruction is assessing a cycle cost, and that the cost is a net positive.
* Make sure that every instruction is assessing a cycle cost, and that the cost is a net positive.
*/
if (this.nStepCycles >= this.nSnapCycles && !(this.opFlags & X86.OPFLAG.PREFIXES)) {
this.println("cycle miscount: " + (this.nSnapCycles - this.nStepCycles));

View file

@ -3192,45 +3192,34 @@ var X86OpXX = {
* @this {X86CPU}
*
* op=0xF4 (hlt)
*
* WARNING: Because other components "thrive" on the CPU's runCPU() loop notifications,
* (eg, the Video component's blinking elements, and the Chipset component's timers),
* we can't really stop. What we do instead is set INTFLAG.HALT and "wait" for INTFLAG.INTR
* to be set; since stepCPU() is already monitoring intFlags, this INTFLAG.HALT bit doesn't
* impact performance.
*
* All stepCPU() has to do when INTFLAG.HALT is set is advance the cycle count without
* advancing the program counter. That continues indefinitely until stepCPU() finally detects
* and acknowledges a INTFLAG.INTR notification, at which point INTFLAG.HALT is cleared.
*/
opHLT: function() {
/*
* The CPU is never REALLY halted by a HLT instruction; instead, by setting X86.INTFLAG.HALT,
* we are signalling to stepCPU() that it's free to end the current burst AND that it should not
* execute any more instructions until checkINTR() indicates a hardware interrupt is requested.
*/
this.intFlags |= X86.INTFLAG.HALT;
this.nStepCycles -= 2;
/*
* We halt the machine only if a Debugger is present AND Debugger checks are enabled (eg,
* one or more breakpoints are set, or the global DEBUG flag is set, etc), on the theory that
* whoever's using the Debugger might like to see halts; we also halt the machine if interrupts
* have been disabled, since that means it's dead in the water (we have no NMI generation
* mechanism at the moment).
*
* Otherwise, HLT is treated like any other instruction.
* If a Debugger is present AND Debugger checks are enabled (eg, one or more breakpoints are set,
* or the global DEBUG flag is set, etc), then we REALLY halt the CPU, on the theory that whoever's
* using the Debugger would like to see HLTs.
*/
if (DEBUGGER && this.dbg && this.dbg.checksEnabled(true)) {
this.advanceIP(-1); // this is purely for the Debugger's benefit, to show the HLT
this.haltCPU();
return;
}
/*
* We also REALLY halt the machine if interrupts have been disabled, since that means it's dead
* in the water (we have no NMI generation mechanism at the moment).
*/
if (!this.getIF()) {
if (DEBUGGER && this.dbg) this.advanceIP(-1);
this.haltCPU();
// return;
}
/*
* Per my discussion of waitCPU() in cpu.js, this seems rather pointless, so I don't call it anymore.
* If you re-enable this, make sure you re-enable the return statement above, too.
*
this.waitCPU();
*/
},
/**
* @this {X86CPU}

View file

@ -635,6 +635,11 @@ Component.prototype = {
if (!this.bindings[sBinding]) {
this.bindings[sBinding] = control;
control.value = ""; // this was added for Firefox (Safari automatically clears the <textarea> on a page reload, but Firefox does not)
/*
* TODO: Get rid of these Component method overrides, because they're going to cause issues
* if the day ever comes (and it WILL) that we want multiple machines on a single page with their
* own Control Panels.
*/
Component.println = (function(control) {
return function printControl(s, type) {
s = (type !== undefined? (type + ": ") : "") + (s || "");