diff --git a/devices/pc/bios/5170/1984-01-10.json b/devices/pc/bios/5170/1984-01-10.json index 196284586..35fd96d50 100644 --- a/devices/pc/bios/5170/1984-01-10.json +++ b/devices/pc/bios/5170/1984-01-10.json @@ -1023,4 +1023,4 @@ 0,0,0,0,-822083584,4683241,2112356352,-1243034839,1558760232,1089063717,49,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,14703594,791752944,942616625,-1375993804] -,"symbols":{"POST1":{"o":44},"POST2":{"o":3135},"D7_C":{"o":3278,"c":"Checkpoint 0x29"}}} \ No newline at end of file +,"symbols":{"POST1":{"o":44,"s":61440},"POST1_TEST01":{"o":166,"s":61440,"c":"TEST.01: 286 PROCESSOR TEST (REAL MODE)"},"POST1_TEST02":{"o":494,"s":61440,"c":"TEST.02: VERIFY CMOS SHUTDOWN BYTE"},"POST1_TEST03":{"o":531,"s":61440,"c":"TEST.03: ROS CHECKSUM TEST"},"POST1_TEST04":{"o":549,"s":61440,"c":"TEST.04: 8253 CHECK TIMER 1 (ALL BITS ON)"},"POST1_TEST05":{"o":604,"s":61440,"c":"TEST.05: 8253 CHECK TIMER 1 (ALL BITS OFF)"},"POST1_TEST06":{"o":635,"s":61440,"c":"TEST.06: 8237 DMA 0 INITIALIZATION"},"POST1_TEST07":{"o":710,"s":61440,"c":"TEST.07: 8237 DMA 1 INITIALIZATION"},"POST1_TEST08":{"o":827,"s":61440,"c":"TEST.08: DMA PAGE REGISTER TEST"},"POST1_TEST09":{"o":910,"s":61440,"c":"TEST.09: STORAGE REFRESH TEST"},"POST1_TEST10":{"o":934,"s":61440,"c":"TEST.10: 8042 TESTS"},"POST1_GETSW":{"o":989,"s":61440,"c":"GET SWITCH SETTINGS"},"POST1_TEST11":{"o":1062,"s":61440,"c":"TEST.11: BASE 64K R/W STORAGE TEST"},"POST1_SETMFG":{"o":1542,"s":61440,"c":"SET MFG_TST"},"POST1_TEST11A":{"o":1549,"s":61440,"c":"TEST.11A: VERIFY GDT/IDT INSTRUCTIONS"},"POST1_TEST12":{"o":1786,"s":61440,"c":"TEST.12: VERIFY CMOS CHECKSUM"},"POST1_TEST13":{"o":2018,"s":61440,"c":"TEST.13: PROTECTED MODE TEST"},"POST1_TEST13A":{"o":2231,"s":61440,"c":"TEST.13A: MEMORY SIZE TEST (ABOVE 1024K)"},"POST1_TEST14":{"o":2740,"s":61440,"c":"TEST.14: INITIALIZE CRT CONTROLLER"},"POST1_TEST15":{"o":2855,"s":61440,"c":"TEST.15: VIDEO LINE TEST"},"POST1_TEST16":{"o":2871,"s":61440,"c":"TEST.16: CRT INTERFACE LINES TEST"},"POST2":{"o":3135,"s":61440,"c":"TEST.17: 8259 PIC TEST"},"POST2_CP27":{"o":3223,"s":61440,"c":"CHECKPOINT 0x27"},"POST2_CP29":{"o":3282,"s":61440,"c":"CHECKPOINT 0x29"},"POST2_TEST18":{"o":3333,"s":61440,"c":"TEST.18: 8253 TIMER TEST (CHECKPOINT 0x2A)"},"POST2_ERR102":{"o":3366,"s":61440,"c":"DISPLAY 102 ERROR"},"POST2_CP2B":{"o":3376,"s":61440,"c":"CHECKPOINT 0x2B"},".263":{"o":3398,"s":61440,"a":"","c":"TIMER COUNTING TOO FAST, DISPLAY 102 ERROR"},"POST2_CP2C":{"o":3408,"s":61440,"c":"CHECKPOINT 0x2C"},"POST2_CP2D":{"o":3448,"s":61440,"c":"CHECKPOINT 0x2D"},"POST2_TEST19":{"o":3469,"s":61440,"c":"TEST.19: ADDITIONAL MEMORY TESTS (CHECKPOINT 0x2F)"},"POST2_TEST20":{"o":4503,"s":61440,"c":"TEST.20: ADDITIONAL PROTECTED-MODE TESTS"},"POST2_TEST21":{"o":4564,"s":61440,"c":"TEST.21: KEYBOARD TEST (CHECKPOINT 0x35)"},"POST2_CP36":{"o":4588,"s":61440,"c":"CHECKPOINT 0x36 (BEGIN KEYBOARD TEST)"},"POST2_CP37":{"o":4661,"s":61440,"c":"CHECKPOINT 0x37 (CHECK KBD_RESET SCAN CODE)"},"POST2_CP38":{"o":4670,"s":61440,"c":"CHECKPOINT 0x38 (RE-ENABLE KEYBOARD)"},"POST2_CP39":{"o":4689,"s":61440,"c":"CHECKPOINT 0x39 (STUCK KEYS)"},"POST2_CP3A":{"o":4756,"s":61440,"c":"CHECKPOINT 0x3A"},"POST2_RTCUP":{"o":5085,"s":61440,"c":"TEST CLOCK UPDATING"},"POST2_COMBOHF":{"o":5196,"s":61440,"c":"CHECK FOR COMBO HARD FILE/DISKETTE CARD"},"POST4":{"o":5971,"s":61440},"KBD_RESET":{"o":6098,"s":61440},"TEMP_ISR":{"o":6149,"s":61440},"HW_INT":{"o":6175,"s":61440},"NOT_SEC":{"o":6203,"s":61440},"SET_INTR_FLAG":{"o":6217,"s":61440},"POST5":{"o":6271,"s":61440},"EXC_13":{"o":6356,"s":61440,"c":"INT 13 (0x0D) GP FAULT HANDLER"},"SYS_32":{"o":6451,"s":61440,"c":"INT 32 (0x20) PROT-MODE TEST HANDLER"},"POST6":{"o":6556,"s":61440},"XMIT_8042":{"o":6949,"s":61440},"POST7":{"o":7213,"s":61440},"POST7_CPF1":{"o":7280,"s":61440,"c":"CHECKPOINT 0xF1"},"POST7_INT20":{"o":7288,"s":61440,"c":"TEST NORMAL SOFTWARE INTERRUPT"},"POST7_INT0D":{"o":7340,"s":61440,"c":"TEST GP FAULT (SEG LIMIT VIOLATION)"},"POST7_LLDT":{"o":7360,"s":61440,"c":"TEST LLDT"},"POST7_BOUND":{"o":7432,"s":61440,"c":"TEST BOUND"},"POST7_PUSHA":{"o":7539,"s":61440,"c":"TEST PUSHA/POPA"},"POST7_VERW":{"o":7623,"s":61440,"c":"TEST VERW/VERR"},"POST7_ARPL":{"o":7705,"s":61440,"c":"TEST ARPL"},"POST7_LAR":{"o":7751,"s":61440,"c":"TEST LAR/LSL"},"POST7_LOWMEG":{"o":7825,"s":61440,"c":"TEST WRITE TO 0x1B0000 VS. B000:0000"},"SYSINIT1":{"o":7962,"s":61440},"DISKETTE_IO_1":{"o":8357,"s":61440},"DISK_RETRY":{"o":8507,"s":61440},"DISK_RESET":{"o":8815,"s":61440},"DISK_STATUS":{"o":8881,"s":61440},"DISK_READ":{"o":8882,"s":61440},"DISK_VERF":{"o":8891,"s":61440},"DISK_FORMAT":{"o":8895,"s":61440},"DISK_WRITE":{"o":8933,"s":61440},"RW_OPN":{"o":8945,"s":61440},"GET_PARM":{"o":9255,"s":61440},"NEC_OUTPUT":{"o":9351,"s":61440},"SEEK":{"o":9409,"s":61440},"DMA_SETUP":{"o":9583,"s":61440},"CHK_STAT_2":{"o":9663,"s":61440},"WAIT_INT":{"o":9696,"s":61440},"DISK_INT_1":{"o":9742,"s":61440},"RESULTS":{"o":9765,"s":61440},"NUM_TRANS":{"o":9840,"s":61440},"READ_DSKCHNG":{"o":9897,"s":61440},"DISK_CHANGE":{"o":9963,"s":61440},"DISK_TYPE":{"o":10045,"s":61440},"FORMAT_SET":{"o":10162,"s":61440},"DSKETTE_SETUP":{"o":10262,"s":61440},"DISK_SETUP":{"o":10458,"s":61440},"DISK_IO":{"o":10865,"s":61440},"HD_INT":{"o":12196,"s":61440},"KEYBOARD_IO_1":{"o":12232,"s":61440},"KB_INT_1":{"o":12372,"s":61440},"K16":{"o":12457,"s":61440},"PRINTER_IO_1":{"o":13423,"s":61440},"RS232_IO_1":{"o":13557,"s":61440},"VIDEO_IO_1":{"o":13829,"s":61440},"SET_MODE":{"o":13902,"s":61440},"SET_CTYPE":{"o":14122,"s":61440},"SET_CPOS":{"o":14161,"s":61440},"READ_CURSOR":{"o":14203,"s":61440},"ACT_DISP_PAGE":{"o":14226,"s":61440},"SET_COLOR":{"o":14262,"s":61440},"VIDEO_STATE":{"o":14300,"s":61440},"SCROLL_UP":{"o":14335,"s":61440},"SCROLL_DOWN":{"o":14499,"s":61440},"READ_AC_CURRENT":{"o":14581,"s":61440},"WRITE_AC_CURRENT":{"o":14651,"s":61440},"WRITE_C_CURRENT":{"o":14702,"s":61440},"READ_DOT":{"o":14907,"s":61440},"WRITE_DOT":{"o":14924,"s":61440},"WRITE_TTY":{"o":15672,"s":61440},"READ_LPEN":{"o":15804,"s":61440},"MEMORY_SIZE_DETERMINE_1":{"o":15970,"s":61440},"EQUIPMENT_1":{"o":15980,"s":61440},"NMI_INT_1":{"o":15990,"s":61440},"SET_TOD":{"o":16175,"s":61440,"c":"CONVERT CMOS TIME TO TIMER TICKS"},"CASSETTE_IO_1":{"o":16354,"s":61440},"SHUT9":{"o":16978,"s":61440},"GATE_A20":{"o":17298,"s":61440},"TIME_OF_DAY_1":{"o":17500,"s":61440},"RTC_INT":{"o":17962,"s":61440},"TIMER_INT_1":{"o":18052,"s":61440},"PRINT_SCREEN_1":{"o":18124,"s":61440},"FILL":{"o":18258,"s":61440},"START":{"o":57435,"s":61440},"P_O_R":{"o":65520,"s":61440,"c":"POWER-ON RESET"}}} \ No newline at end of file diff --git a/devices/pc/bios/5170/1984-01-10.map b/devices/pc/bios/5170/1984-01-10.map index 54286b82d..e8d1395ad 100644 --- a/devices/pc/bios/5170/1984-01-10.map +++ b/devices/pc/bios/5170/1984-01-10.map @@ -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 diff --git a/devices/pc/bios/5170/README.md b/devices/pc/bios/5170/README.md index 687f2b546..c32d32882 100644 --- a/devices/pc/bios/5170/README.md +++ b/devices/pc/bios/5170/README.md @@ -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 diff --git a/devices/pc/keyboard/README.md b/devices/pc/keyboard/README.md new file mode 100644 index 000000000..5c4cf6ccd --- /dev/null +++ b/devices/pc/keyboard/README.md @@ -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). diff --git a/devices/pc/keyboard/dasm42.c b/devices/pc/keyboard/dasm42.c new file mode 100644 index 000000000..24484c771 --- /dev/null +++ b/devices/pc/keyboard/dasm42.c @@ -0,0 +1,408 @@ +#include // 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." + +*/ diff --git a/my_modules/filedump/lib/filedump.js b/my_modules/filedump/lib/filedump.js index 1af10e98b..da8652b97 100644 --- a/my_modules/filedump/lib/filedump.js +++ b/my_modules/filedump/lib/filedump.js @@ -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 diff --git a/my_modules/pcjs-client/lib/chipset.js b/my_modules/pcjs-client/lib/chipset.js index 2ea65d0e6..ce2837167 100644 --- a/my_modules/pcjs-client/lib/chipset.js +++ b/my_modules/pcjs-client/lib/chipset.js @@ -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) * diff --git a/my_modules/pcjs-client/lib/cpu.js b/my_modules/pcjs-client/lib/cpu.js index 8f3cc68e2..584e4c825 100644 --- a/my_modules/pcjs-client/lib/cpu.js +++ b/my_modules/pcjs-client/lib/cpu.js @@ -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() * diff --git a/my_modules/pcjs-client/lib/debugger.js b/my_modules/pcjs-client/lib/debugger.js index 3147bbf07..55e48dd8f 100644 --- a/my_modules/pcjs-client/lib/debugger.js +++ b/my_modules/pcjs-client/lib/debugger.js @@ -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; diff --git a/my_modules/pcjs-client/lib/keyboard.js b/my_modules/pcjs-client/lib/keyboard.js index db54a749c..985d65767 100644 --- a/my_modules/pcjs-client/lib/keyboard.js +++ b/my_modules/pcjs-client/lib/keyboard.js @@ -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); + } } } }; diff --git a/my_modules/pcjs-client/lib/x86cpu.js b/my_modules/pcjs-client/lib/x86cpu.js index d0ddfbfec..6eec8861a 100644 --- a/my_modules/pcjs-client/lib/x86cpu.js +++ b/my_modules/pcjs-client/lib/x86cpu.js @@ -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)); diff --git a/my_modules/pcjs-client/lib/x86opxx.js b/my_modules/pcjs-client/lib/x86opxx.js index 3462d79b9..5a5a62f51 100644 --- a/my_modules/pcjs-client/lib/x86opxx.js +++ b/my_modules/pcjs-client/lib/x86opxx.js @@ -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} diff --git a/my_modules/shared/lib/component.js b/my_modules/shared/lib/component.js index 38eff3b43..379208e2e 100644 --- a/my_modules/shared/lib/component.js +++ b/my_modules/shared/lib/component.js @@ -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