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:
parent
f2627107d5
commit
93b81c47b7
13 changed files with 804 additions and 287 deletions
File diff suppressed because one or more lines are too long
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
10
devices/pc/keyboard/README.md
Normal file
10
devices/pc/keyboard/README.md
Normal 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).
|
||||
408
devices/pc/keyboard/dasm42.c
Normal file
408
devices/pc/keyboard/dasm42.c
Normal 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."
|
||||
|
||||
*/
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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 || "");
|
||||
|
|
|
|||
Loading…
Reference in a new issue