From 4179eeb19f26851b5f3d235ac979193846ebddc8 Mon Sep 17 00:00:00 2001 From: nj7p Date: Sat, 11 Apr 2015 15:00:04 -0700 Subject: [PATCH] MDS-800: Initial simulator commit --- MDS-800/MDS-800/Mds-800.cfg | 44 + MDS-800/MDS-800/Mds-800.txt | 211 + MDS-800/MDS-800/Mds-800.vsd | Bin 0 -> 57856 bytes MDS-800/MDS-800/functional description.vsd | Bin 0 -> 57344 bytes MDS-800/MDS-800/mds-800_sys.c | 81 + MDS-800/MDS-800/system_defs.h | 84 + MDS-800/System_80-10/System_80-10.txt | 211 + MDS-800/System_80-10/System_80-10.vsd | Bin 0 -> 57856 bytes .../System_80-10/functional description.vsd | Bin 0 -> 57344 bytes MDS-800/System_80-10/system_80_10.cfg | 41 + MDS-800/System_80-10/system_80_10_sys.c | 78 + MDS-800/System_80-10/system_defs.h | 81 + MDS-800/common/i8008.c | 2002 +++++++ MDS-800/common/i8080.c | 1438 +++++ MDS-800/common/i8088.c | 4746 +++++++++++++++++ MDS-800/common/i8251.c | 252 + MDS-800/common/i8255.c | 459 ++ MDS-800/common/i8259.c | 304 ++ MDS-800/common/i8273.c | 252 + MDS-800/common/i8274.c | 351 ++ MDS-800/common/iSBC80-10.c | 143 + MDS-800/common/iSBC80-20.c | 149 + MDS-800/common/iSBC80-30.c | 149 + MDS-800/common/ieprom.c | 201 + MDS-800/common/ijedec.c | 486 ++ MDS-800/common/iram16.c | 203 + MDS-800/common/iram8.c | 168 + MDS-800/common/isbc064.c | 202 + MDS-800/common/isbc064b.c | 256 + MDS-800/common/isbc064x.c | 205 + MDS-800/common/isbc208.c | 1699 ++++++ MDS-800/common/multibus.c | 288 + MDS-800/common/pata.c | 193 + MDS-800/makefile | 1382 +++++ 34 files changed, 16359 insertions(+) create mode 100644 MDS-800/MDS-800/Mds-800.cfg create mode 100644 MDS-800/MDS-800/Mds-800.txt create mode 100644 MDS-800/MDS-800/Mds-800.vsd create mode 100644 MDS-800/MDS-800/functional description.vsd create mode 100644 MDS-800/MDS-800/mds-800_sys.c create mode 100644 MDS-800/MDS-800/system_defs.h create mode 100644 MDS-800/System_80-10/System_80-10.txt create mode 100644 MDS-800/System_80-10/System_80-10.vsd create mode 100644 MDS-800/System_80-10/functional description.vsd create mode 100644 MDS-800/System_80-10/system_80_10.cfg create mode 100644 MDS-800/System_80-10/system_80_10_sys.c create mode 100644 MDS-800/System_80-10/system_defs.h create mode 100644 MDS-800/common/i8008.c create mode 100644 MDS-800/common/i8080.c create mode 100644 MDS-800/common/i8088.c create mode 100644 MDS-800/common/i8251.c create mode 100644 MDS-800/common/i8255.c create mode 100644 MDS-800/common/i8259.c create mode 100644 MDS-800/common/i8273.c create mode 100644 MDS-800/common/i8274.c create mode 100644 MDS-800/common/iSBC80-10.c create mode 100644 MDS-800/common/iSBC80-20.c create mode 100644 MDS-800/common/iSBC80-30.c create mode 100644 MDS-800/common/ieprom.c create mode 100644 MDS-800/common/ijedec.c create mode 100644 MDS-800/common/iram16.c create mode 100644 MDS-800/common/iram8.c create mode 100644 MDS-800/common/isbc064.c create mode 100644 MDS-800/common/isbc064b.c create mode 100644 MDS-800/common/isbc064x.c create mode 100644 MDS-800/common/isbc208.c create mode 100644 MDS-800/common/multibus.c create mode 100644 MDS-800/common/pata.c create mode 100644 MDS-800/makefile diff --git a/MDS-800/MDS-800/Mds-800.cfg b/MDS-800/MDS-800/Mds-800.cfg new file mode 100644 index 00000000..3db17a13 --- /dev/null +++ b/MDS-800/MDS-800/Mds-800.cfg @@ -0,0 +1,44 @@ +/* mds-800.cfg: Intel mds-800 simulator definitions + + This file holds the configuration for the mds-800 + boards I/O and Memory. + + Copyright (c) 2015, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + William A. Beech BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + 16 Dec 12 - Original file + 08 Apr 15 - Modified to be mds-800.cfg file to set base and size. + Changed tabs to spaces + +*/ + +/* set the base I/O address for the iSBC 208 */ +#define SBC208_BASE 0x40 + +/* configure interrupt request line */ +#define SBC208_INT INT_1 + +/* set the base and size for the iSBC 064 */ +#define SBC064_BASE 0x0000 +#define SBC064_SIZE 0x10000 + diff --git a/MDS-800/MDS-800/Mds-800.txt b/MDS-800/MDS-800/Mds-800.txt new file mode 100644 index 00000000..e63bdd6e --- /dev/null +++ b/MDS-800/MDS-800/Mds-800.txt @@ -0,0 +1,211 @@ +Altair 8800 Simulator +===================== + +1. Background. + + The MITS (Micro Instrumentation and Telemetry Systems) Altair 8800 +was announced on the January 1975 cover of Popular Electronics, which +boasted you could buy and build this powerful computer kit for only $397. +The kit consisted at that time of only the parts to build a case, power +supply, card cage (18 slots), CPU card, and memory card with 256 *bytes* of +memory. Still, thousands were ordered within the first few months after the +announcement, starting the personal computer revolution as we know it today. + + Many laugh at the small size of the that first kit, noting there +were no peripherals and the 256 byte memory size. But the computer was an +open system, and by 1977 MITS and many other small startups had added many +expansion cards to make the Altair quite a respectable little computer. The +"Altair Bus" that made this possible was soon called the S-100 Bus, later +adopted as an industry standard, and eventually became the IEE-696 Bus. + +2. Hardware + + We are simulating a fairly "loaded" Altair 8800 from about 1977, +with the following configuration: + + device simulates + name(s) + + CPU Altair 8800 with Intel 8080 CPU board, 62KB + of RAM, 2K of EPROM with start boot ROM. + 2SIO MITS 88-2SIO Dual Serial Interface Board. Port 1 + is assumed to be connected to a serial "glass + TTY" that is your terminal running the Simulator. + PTR Paper Tape Reader attached to port 2 of the + 2SIO board. + PTP Paper Tape Punch attached to port 2 of the + 2SIO board. This also doubles as a printer + port. + DSK MITS 88-DISK Floppy Disk controller with up + to eight drives. + +2.1 CPU + + We have 2 CPU options that were not present on the original +machine but are useful in the simulator. We also allow you to select +memory sizes, but be aware that some sample software requires the full +64K (i.e. CP/M) and the MITS Disk Basic and Altair DOS require about +a minimum of 24K. + + SET CPU 8080 Simulates the 8080 CPU (normal) + SET CPU Z80 Simulates the later Z80 CPU [At the present time + this is not fully implemented and is not to be + trusted with real Z80 software] + SET CPU ITRAP Causes the simulator to halt if an invalid 8080 + Opcode is detected. + SET CPU NOITRAP Does not stop on an invalid Opcode. This is + how the real 8080 works. + SET CPU 4K + SET CPU 8K + SET CPU 12K + SET CPU 16K + ...... + SET CPU 64K All these set various CPU memory configurations. + The 2K EPROM at the high end of memory is always + present and will always boot. + +The BOOT EPROM card starts at address 177400. Jumping to this address +will always boot drive 0 of the floppy controller. If no valid bootable +software is present there the machine crashes. This is historically +accurate behavior. + +The real 8080, on receiving a HLT (Halt) instruction, freezes the processor +and only an interrupt or CPU hardware reset will restore it. The simulator +is alot nicer, it will halt but send you back to the simulator command line. + +CPU Registers include the following: + + name size comments + + PC 16 The Program Counter + A 8 The accumulator + BC 16 The BC register pair. Register B is the high + 8 bits, C is the lower 8 bits + DE 16 The DE register pair. D is the top 8 bits, E is + the bottom. + HL 16 The HL register pair. H is top, L is bottom. + C 1 Carry flag. + Z 1 Zero Flag. + AC 1 Auxillary Carry flag. + P 1 Parity flag. + S 1 Sign flag. + SR 16 The front panel switches. + BREAK 16 Breakpoint address (377777 to disable). + WRU 8 The interrupt character. This starts as 005 + (ctrl-E) but some Altair software uses this + keystroke so best to change this to something + exotic such as 035 (which is Ctl-]). + + +2.2 The Serial I/O Card (2SIO) + + This simple programmed I/O device provides 2 serial ports to the +outside world, which could be hardware jumpered to support RS-232 plugs or a +TTY current loop interface. The standard I/O addresses assigned by MITS +was 20-21 (octal) for the first port, and 22-23 (octal) for the second. +We follow this standard in the Simulator. + + The simulator directs I/O to/from the first port to the screen. The +second port reads from an attachable "tape reader" file on input, and writes +to an attachable "punch file" on output. These files are considered a +simple stream of 8-bit bytes. + +2.3 The 88-DISK controller. + + The MITS 88-DISK is a simple programmed I/O interface to the MITS +8-inch floppy drive, which was basically a Pertec FD-400 with a power +supply and buffer board builtin. The controller supports neither interrupts +nor DMA, so floppy access required the sustained attention of the CPU. +The standard I/O addresses were 10, 11, and 12 (octal), and we follow the +standard. Details on controlling this hardware are in the altair_dsk.c +source file. + + +3. Sample Software + + Running an Altair in 1977 you would be running either MITS Disk +Extended BASIC, or the brand new and sexy CP/M Operating System from Digital +Research. Or possibly, you ordered Altair DOS back when it was promised in +1975, and are still waiting for it to be delivered in early 1977. + + We have samples of all three for you to check out. We can't go into +the details of how they work, but we'll give you a few hints. + + +3.1 CP/M Version 2.2 + + This version is my own port of the standard CP/M to the Altair. +There were some "official" versions but I don't have them. None were +endorsed or sold by MITS to my knowledge, however. + To boot CP/M: + + sim> attach dsk0 altcpm.dsk + sim> go 177400 + 62K CP/M VERSION 2.2 (ALTAIR 8800) + A>DIR + + CP/M feels like DOS, sort of. DIR will work. I have included all +the standard CP/M utilities, plus a few common public-domain ones. I also +include the sources to the customized BIOS and some other small programs. +TYPE will print an ASCII file. DUMP will dump a binary one. LS is a better +DIR than DIR. ASM will assemble .ASM files to Hex, LOAD will "load" them to +binary format (.COM). ED is a simple editor, #A command will bring the +source file to the buffer, T command will "type" lines, L will move lines, +E exits the editor. 20L20T will move down 20 lines, and type 20. Very +DECish. DDT is the debugger, SUBMIT is a batch-type command processor. +A sample batch file that will assemble and write out the bootable CP/M +image (on drive A) is "SYSGEN.SUB". To run it, type "SUBMIT SYSGEN". + + +3.2 MITS Disk Extended BASIC Version 4.1 + + This was the commonly used software for serious users of the Altair +computer. It is a powerful (but slow) BASIC with some extended commands to +allow it to access and manage the disk. There was no operating system it +ran under. To boot: + + sim> attach dsk0 mbasic.dsk + sim> go 177400 + + MEMORY SIZE? [return] + LINEPRINTER? C [return] + HIGHEST DISK NUMBER? 0 [return] (3 here = 4 drive system) + NUMBER OF FILES? 3 [return] + NUMBER OF RANDOM FILES? 2 [return] + + 44297 BYTES FREE + ALTAIR BASIC REV. 4.1 + [DISK EXTENDED VERSION] + COPYRIGHT 1977 BY MITS INC. + OK + mount 0 + OK + files + + +3.3 Altair DOS Version 1.0 + + This was long promised but not delivered until it was almost +irrelevant. A short attempted tour will reveal it to be a dog, far inferior +to CP/M. To boot: + + sim> attach dsk0 altdos.dsk + sim> go 177400 + + MEMORY SIZE? 64 [return] + INTERRUPTS? N [return] + HIGHEST DISK NUMBER? 0 [return] (3 here = 4 drive system) + HOW MANY DISK FILES? 3 [return] + HOW MANY RANDOM FILES? 2 [return] + + 056769 BYTES AVAILABLE + DOS MONITOR VER 1.0 + COPYRIGHT 1977 BY MITS INC + .mnt 0 + + .dir 0 + + + + + diff --git a/MDS-800/MDS-800/Mds-800.vsd b/MDS-800/MDS-800/Mds-800.vsd new file mode 100644 index 0000000000000000000000000000000000000000..a0acb99788780d6b16b69daea981108df167ac76 GIT binary patch literal 57856 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 literal 0 HcmV?d00001 diff --git a/MDS-800/MDS-800/functional description.vsd b/MDS-800/MDS-800/functional description.vsd new file mode 100644 index 0000000000000000000000000000000000000000..a6fd1978eea897b22f4dca8afffec52ba8eb58c5 GIT binary patch literal 57344 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 literal 0 HcmV?d00001 diff --git a/MDS-800/MDS-800/mds-800_sys.c b/MDS-800/MDS-800/mds-800_sys.c new file mode 100644 index 00000000..fa346ccb --- /dev/null +++ b/MDS-800/MDS-800/mds-800_sys.c @@ -0,0 +1,81 @@ +/* system_80_10_sys.c: multibus system interface + + Copyright (c) 2010, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + William A. Beech BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + ?? ??? 10 - Original file. + 16 Dec 12 - Modified to use isbc_80_10.cfg file to set base and size. + 08 Apr 15 - Modified to use mds-800.cfg file to set base and size. + Changed tabs to spaces + +*/ + +#include "system_defs.h" + +extern DEVICE i8080_dev; +extern REG i8080_reg[]; +extern DEVICE i8251_dev; +extern DEVICE i8255_dev; +extern DEVICE EPROM_dev; +extern DEVICE RAM_dev; +extern DEVICE multibus_dev; +extern DEVICE isbc208_dev; +extern DEVICE isbc064_dev; + +/* SCP data structures + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words needed for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages +*/ + +char sim_name[] = "Intel MDS-800"; + +REG *sim_PC = &i8080_reg[0]; + +int32 sim_emax = 4; + +DEVICE *sim_devices[] = { + &i8080_dev, + &EPROM_dev, + &RAM_dev, + &i8251_dev, + &i8255_dev, + &multibus_dev, + &isbc064_dev, + &isbc208_dev, + NULL +}; + +const char *sim_stop_messages[] = { + "Unknown error", + "Unknown I/O Instruction", + "HALT instruction", + "Breakpoint", + "Invalid Opcode", + "Invalid Memory", + "XACK Error" +}; + diff --git a/MDS-800/MDS-800/system_defs.h b/MDS-800/MDS-800/system_defs.h new file mode 100644 index 00000000..4892355c --- /dev/null +++ b/MDS-800/MDS-800/system_defs.h @@ -0,0 +1,84 @@ +/* system_defs.h: Intel iSBC simulator definitions + + Copyright (c) 2010, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + William A. Beech BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + ?? ??? 10 - Original file. + 16 Dec 12 - Modified to use isbc_80_10.cfg file to set base and size. + 08 Apr 15 - Modified to use mds-800.cfg file to set base and size. + Changed tabs to spaces + +*/ + +#include +#include +#include "mds-800.cfg" /* Intel System 80/10 configuration */ +#include "sim_defs.h" /* simulator defns */ + +/* multibus interrupt definitions */ + +#define INT_0 0x01 +#define INT_1 0x02 +#define INT_2 0x04 +#define INT_3 0x08 +#define INT_4 0x10 +#define INT_5 0x20 +#define INT_6 0x40 +#define INT_7 0x80 + +/* CPU interrupts definitions */ + +#define INT_R 0x200 +#define I75 0x40 +#define I65 0x20 +#define I55 0x10 + +/* Memory */ + +#define MAXMEMSIZE 0x10000 /* 8080 max memory size */ +#define MEMSIZE (i8080_unit.capac) /* 8080 actual memory size */ +#define ADDRMASK (MAXMEMSIZE - 1) /* 8080 address mask */ +#define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) + +/* debug definitions */ + +#define DEBUG_flow 0x0001 +#define DEBUG_read 0x0002 +#define DEBUG_write 0x0004 +#define DEBUG_level1 0x0008 +#define DEBUG_level2 0x0010 +#define DEBUG_reg 0x0020 +#define DEBUG_asm 0x0040 +#define DEBUG_xack 0x0080 +#define DEBUG_all 0xFFFF + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_OPCODE 4 /* Invalid Opcode */ +#define STOP_IO 5 /* I/O error */ +#define STOP_MEM 6 /* Memory error */ +#define STOP_XACK 7 /* XACK error */ + diff --git a/MDS-800/System_80-10/System_80-10.txt b/MDS-800/System_80-10/System_80-10.txt new file mode 100644 index 00000000..e63bdd6e --- /dev/null +++ b/MDS-800/System_80-10/System_80-10.txt @@ -0,0 +1,211 @@ +Altair 8800 Simulator +===================== + +1. Background. + + The MITS (Micro Instrumentation and Telemetry Systems) Altair 8800 +was announced on the January 1975 cover of Popular Electronics, which +boasted you could buy and build this powerful computer kit for only $397. +The kit consisted at that time of only the parts to build a case, power +supply, card cage (18 slots), CPU card, and memory card with 256 *bytes* of +memory. Still, thousands were ordered within the first few months after the +announcement, starting the personal computer revolution as we know it today. + + Many laugh at the small size of the that first kit, noting there +were no peripherals and the 256 byte memory size. But the computer was an +open system, and by 1977 MITS and many other small startups had added many +expansion cards to make the Altair quite a respectable little computer. The +"Altair Bus" that made this possible was soon called the S-100 Bus, later +adopted as an industry standard, and eventually became the IEE-696 Bus. + +2. Hardware + + We are simulating a fairly "loaded" Altair 8800 from about 1977, +with the following configuration: + + device simulates + name(s) + + CPU Altair 8800 with Intel 8080 CPU board, 62KB + of RAM, 2K of EPROM with start boot ROM. + 2SIO MITS 88-2SIO Dual Serial Interface Board. Port 1 + is assumed to be connected to a serial "glass + TTY" that is your terminal running the Simulator. + PTR Paper Tape Reader attached to port 2 of the + 2SIO board. + PTP Paper Tape Punch attached to port 2 of the + 2SIO board. This also doubles as a printer + port. + DSK MITS 88-DISK Floppy Disk controller with up + to eight drives. + +2.1 CPU + + We have 2 CPU options that were not present on the original +machine but are useful in the simulator. We also allow you to select +memory sizes, but be aware that some sample software requires the full +64K (i.e. CP/M) and the MITS Disk Basic and Altair DOS require about +a minimum of 24K. + + SET CPU 8080 Simulates the 8080 CPU (normal) + SET CPU Z80 Simulates the later Z80 CPU [At the present time + this is not fully implemented and is not to be + trusted with real Z80 software] + SET CPU ITRAP Causes the simulator to halt if an invalid 8080 + Opcode is detected. + SET CPU NOITRAP Does not stop on an invalid Opcode. This is + how the real 8080 works. + SET CPU 4K + SET CPU 8K + SET CPU 12K + SET CPU 16K + ...... + SET CPU 64K All these set various CPU memory configurations. + The 2K EPROM at the high end of memory is always + present and will always boot. + +The BOOT EPROM card starts at address 177400. Jumping to this address +will always boot drive 0 of the floppy controller. If no valid bootable +software is present there the machine crashes. This is historically +accurate behavior. + +The real 8080, on receiving a HLT (Halt) instruction, freezes the processor +and only an interrupt or CPU hardware reset will restore it. The simulator +is alot nicer, it will halt but send you back to the simulator command line. + +CPU Registers include the following: + + name size comments + + PC 16 The Program Counter + A 8 The accumulator + BC 16 The BC register pair. Register B is the high + 8 bits, C is the lower 8 bits + DE 16 The DE register pair. D is the top 8 bits, E is + the bottom. + HL 16 The HL register pair. H is top, L is bottom. + C 1 Carry flag. + Z 1 Zero Flag. + AC 1 Auxillary Carry flag. + P 1 Parity flag. + S 1 Sign flag. + SR 16 The front panel switches. + BREAK 16 Breakpoint address (377777 to disable). + WRU 8 The interrupt character. This starts as 005 + (ctrl-E) but some Altair software uses this + keystroke so best to change this to something + exotic such as 035 (which is Ctl-]). + + +2.2 The Serial I/O Card (2SIO) + + This simple programmed I/O device provides 2 serial ports to the +outside world, which could be hardware jumpered to support RS-232 plugs or a +TTY current loop interface. The standard I/O addresses assigned by MITS +was 20-21 (octal) for the first port, and 22-23 (octal) for the second. +We follow this standard in the Simulator. + + The simulator directs I/O to/from the first port to the screen. The +second port reads from an attachable "tape reader" file on input, and writes +to an attachable "punch file" on output. These files are considered a +simple stream of 8-bit bytes. + +2.3 The 88-DISK controller. + + The MITS 88-DISK is a simple programmed I/O interface to the MITS +8-inch floppy drive, which was basically a Pertec FD-400 with a power +supply and buffer board builtin. The controller supports neither interrupts +nor DMA, so floppy access required the sustained attention of the CPU. +The standard I/O addresses were 10, 11, and 12 (octal), and we follow the +standard. Details on controlling this hardware are in the altair_dsk.c +source file. + + +3. Sample Software + + Running an Altair in 1977 you would be running either MITS Disk +Extended BASIC, or the brand new and sexy CP/M Operating System from Digital +Research. Or possibly, you ordered Altair DOS back when it was promised in +1975, and are still waiting for it to be delivered in early 1977. + + We have samples of all three for you to check out. We can't go into +the details of how they work, but we'll give you a few hints. + + +3.1 CP/M Version 2.2 + + This version is my own port of the standard CP/M to the Altair. +There were some "official" versions but I don't have them. None were +endorsed or sold by MITS to my knowledge, however. + To boot CP/M: + + sim> attach dsk0 altcpm.dsk + sim> go 177400 + 62K CP/M VERSION 2.2 (ALTAIR 8800) + A>DIR + + CP/M feels like DOS, sort of. DIR will work. I have included all +the standard CP/M utilities, plus a few common public-domain ones. I also +include the sources to the customized BIOS and some other small programs. +TYPE will print an ASCII file. DUMP will dump a binary one. LS is a better +DIR than DIR. ASM will assemble .ASM files to Hex, LOAD will "load" them to +binary format (.COM). ED is a simple editor, #A command will bring the +source file to the buffer, T command will "type" lines, L will move lines, +E exits the editor. 20L20T will move down 20 lines, and type 20. Very +DECish. DDT is the debugger, SUBMIT is a batch-type command processor. +A sample batch file that will assemble and write out the bootable CP/M +image (on drive A) is "SYSGEN.SUB". To run it, type "SUBMIT SYSGEN". + + +3.2 MITS Disk Extended BASIC Version 4.1 + + This was the commonly used software for serious users of the Altair +computer. It is a powerful (but slow) BASIC with some extended commands to +allow it to access and manage the disk. There was no operating system it +ran under. To boot: + + sim> attach dsk0 mbasic.dsk + sim> go 177400 + + MEMORY SIZE? [return] + LINEPRINTER? C [return] + HIGHEST DISK NUMBER? 0 [return] (3 here = 4 drive system) + NUMBER OF FILES? 3 [return] + NUMBER OF RANDOM FILES? 2 [return] + + 44297 BYTES FREE + ALTAIR BASIC REV. 4.1 + [DISK EXTENDED VERSION] + COPYRIGHT 1977 BY MITS INC. + OK + mount 0 + OK + files + + +3.3 Altair DOS Version 1.0 + + This was long promised but not delivered until it was almost +irrelevant. A short attempted tour will reveal it to be a dog, far inferior +to CP/M. To boot: + + sim> attach dsk0 altdos.dsk + sim> go 177400 + + MEMORY SIZE? 64 [return] + INTERRUPTS? N [return] + HIGHEST DISK NUMBER? 0 [return] (3 here = 4 drive system) + HOW MANY DISK FILES? 3 [return] + HOW MANY RANDOM FILES? 2 [return] + + 056769 BYTES AVAILABLE + DOS MONITOR VER 1.0 + COPYRIGHT 1977 BY MITS INC + .mnt 0 + + .dir 0 + + + + + diff --git a/MDS-800/System_80-10/System_80-10.vsd b/MDS-800/System_80-10/System_80-10.vsd new file mode 100644 index 0000000000000000000000000000000000000000..7f4a87f5e1bf3e8376296fde8c24359e909055c6 GIT binary patch literal 57856 zcmeEv2S8Kj*Y|U824R^21Qj&_WQbrGg0m(;aAN|3Tf-iThUuu<1ObZ^k>RKo6s)rX zj@H@Wz)=Ub&bDemR1~a900l(2-?^aFzgp}2+P7cd@AJaReeSu>IL~>;`90&_d%28V zzPY$-1$yHcf+(n!&`0#w_!zkHSJy0rXy7J!tpq`kYzh!KKOO&zG|&RFlI8d5`2SM_ zen<{~35bup(QG)!q4{J}{@{ZPy};|5`;dUI`il?o`yqb*tLcA8oc|R+|C`hQJIkN6 z4W?b&EksGX0*Ae>N7lK%&zJpj5hHC%$xA~r9z~&eAUW~^3PFiz77A;tiLZzJb9Xm{ zW2FA6;DtQ@xz9iDxPWuyqV{VV8rPQQEELpM_eVn7;UGyI$PonXfs}0aA3@)8*+crI z4SZ$$-tzDc1_OA`()Js)xAOg8lc)d7{F8Ynk9Q>Z7K@a3;!FLvhicc~D_cOePd)HM zp7r5u0AvVc1Y``<35W><5CE}(I6x*qra)#uTp)9x&Olv&x&m1Mbpz@S)B}hI#0Rnj zvI4RO>Iq~6WD8^mWDnE}$N{J~kRy;2P#+)xkTVb|qbr=SUNC=@6RC>$sPC=w_NXa>+splG03 zKxBQC1Ce?E*yE4(_g}5~AOIXzfCyigp`H8JzhqtbyN>@(`hQp2WIX?y(B;_?Z2x3G^{V~<9d+$>J$-WjJ2cP=7K8f7s18c}zjlz}=@?mq{_`L?__d4I zHukY#Wv*qQ-i}Rs$o`e~2Co2veh`SXFK}w>6Une--$&Y+1R&DZD1k_uk_ANi-3Ub5 zp8_D#_Ur~C?aqE6(ncKxG6J%PNXhu_LFFdx4xHMKH{GP)fIj2;@WpYnNCZr_2QmgS zE%NxYa}N-LY%^qAkp@MCIk_NbGA_Tckc6Pw5z)|}k2QtszV7_cu!y*@Fn*{!F&NnL zcsa@UYP;fxM~BUeatexzlg~x%{Q0wkW<`bYL*%n(LsarO=<&&UxS(15@Tlmp_^HsS;-Kw?C-CLr;ZY%BQ`=82u2ad1 zHx;V+$e~`cfIj2L`9tuIHuZXXEa3ZeL^D1rK1$w}mS~-A+xUjWj#mLBl07`xhDcqm z0D5(h_>#nfua50tWYeJK@V$ND7KScB*jH_u?EBg>N_r-h3K={F5k`@bzIIer(owf$ z8eq?nfJH~P;ZrXd#RQ?bfFWnM;Yrf_7$8T8|JCuPETrsHf%xdcD=%%|`6ralL zLpAm9@b{^_{vGA;p;CM*uMgGKzr)|B^7?m_!-q=op7JUyD^t97sMYGXBh_d$9bOhJ zSTJqcw2sR^bEK!Iza0;7R4Ub<1$mthQoS?y8_f6QD^Mo$89j2r+ywX^GI$_F{zh21 z1YvkRdbIl)HdNfDbLV#Vdw%uw^qicWUKbVI6DCZ6upP<$S#a z!;^>b4+;HQzJ`1@xZTh4)WxnY&h75^{C4Wp$-%+ljeN;uGIMkD_Iyp9G@<=pGG^3p z@1gMP?<;A~DR_`dByZ>9ZO#YgYx}-p&>{@DCow`uMz^i?=x+pD#;3|LpOjMLWMy zES%rj+`OX-fzCnIL&ZYxq2k0?c?X|Pr@b>@9rX|b2Fs>Jg@u44V4N@c>nrUjufMk* zLV0ny+?g|HzENJ5mX_VRbxT_K`Pb{R!h)t78ymMP7Jfn14!!I_!=UyKCOGsNjCXcH z9ouKkz6kCh`-$d^^nD4wJY*wE&{9`yD znJ-AXxVU)7jvdRFFJHKD;V-}Z@-f2vjbZyfh71`3pa^>MoQ#zziiIJvY2ZGzcTfAja^ZY1YVf~p^QKK} zvpV=-lr5_STj|>g!V1fh2 zBk$}F0h0Y$5x>s=z&_{w`AYs`L3{uFf%)RFSunJG^)CQO5C5*tf>#{!7K5QTefsp* zMFsa)Uw!4?x38V8&HIZRJjBT5e_2@I;r;pQ!1EuNFAIyV?-?n#-=fiIFf@4043f@1 zefr=yu9!dfoxy7=Z@2S7p1*he^*81V@?KC-@P^KzcBQ7Kf|CF8ShZ@^8)4sh3Guv> z4@}U^$CMXTV9+!C?#$2o`Fv?dLC|QgHgUpkQ>pZwse>JS(00IGIKR`+`+jvW7o>t_1vRikyqK7nzYGf=z*|R<_c;ERd_i%5Er6nVXZwOFosPl+M|*ie zZ~yJL-@cx&*Q>|t{yQ2MF#pN+)!V@#A|wdR9AJLH#($}EI-UOW&p-3|d>9?Po-Y_v z|7BS4@Q<}G_r7lJ>IFy--T3ROZg;;?Ua#lhVWDxg|F`r1C)yXxF9FnfLkS59 z2_5AH`C_rijqC3}=rmeZR@NKwl$Vz~J3GHWVEf&nLx;W*_MMmD`JH@V{$1tudJ%y8 zL(1#*QvRRr58v6opm%y*$wo%69&mb{`=4&8w?~c~c|)21x7ydA;ixyngUgPgB{*Ol z4aeJl-p^@A8tw6b?S8-KKfC*$diaJx$<57uXC#m`9017IuU`+v_uqp(?~M6lnD5CK zMB6@J(XPNhOQrp8dv-bs>(4y@WBm8z3rYbxE;wNN;@>kN^MRUqPrg9;{~rB+V0JpZ zy(eECe%|lM&(CkKgl+9i1*!lC^mx!?faCpP|L5+0e7^qYWc!$yKjrIVwD-@4{ZwB6 zyv{!+#ZTq+G1~j*!+t8Se_rPwlj5iH`WWr~^I<=g*FUfGk4f=Ud3}ub{`s(<%Ilxk z`NyRAsk}Z$d;fgcPv!N`>-=L<{8U~aqrHDV?5Fbj=XL%uDSj%ikI~*gANEsu{qs8i zm=vMB$amL~@2DgXa(mM&cN^o=&;Jt|XnT7h-`?M+7vC!`Xl~T(2xIbf-*6bVzau>b zYb*Doxoapd7QlNC;k|?8`>qk%L?>TC$X~aK9uqkLS(mHk+Jl>Xf8#5D3h-~61|+t> zu^QeB2=^}oZ@?J0*jK#YQ>t|o{-au`s>z6WR!h`1YxS^39a^fa)^VvyF5<&G#FYdp zDJenZ@*KK&@gmx{Z=VKj*s#G8DN#5P9*%r`e2^OfFAnY6wJUl^JZV7RpQ}V8>*kj< z)mH0@N-L4x@4Cj8TJn|Te|3C_pMb74TB25~U!(2zurBX;y&khh`%L%X>8PrD>cASU zSB(}!{_fbrI%TbHBy$S0R#)Eaf<1T|SzT4ns?koY(c;KIwR4SDq<)cwWx*Y6&w9`u zr4N->qowbx(dyM`8C|IcHCjWsFsjiS*JwM{XqgZ`v$9ce77xI%ChP=O1@GPs#?ZCa z?i|dTi(7Lk!E52o;MKa+5^A+>eJjhv`E%1f!z(_wP7(e<$)}&e@G&M?STc^La&65# zSuW-Z4rOXS-PVK}jN6#fvbovxY_485mvM-#;OLLE+{vxhEja~oQBER}30qG0F)`{H zYQpa&EpMi1gh{d~>Q z*c}|5YD~34+>7(&S*q5|gk>!d2z^X0_3v9QXG0W@lw)>+ch#ar z_@h;Kj$t-8z$lw*oXzc&&1J#`E1S#C=5n&RCStRZ1(zXHZglIrO)YZ#q&{`XIFYsI zz-KxfO$b7*GCwRV01pcYpo9fb!vbhw0raqdB)u?czU08zUnP&dYA}2pE3l9)^J9kv zaKNKUSb%9*fLT}oH!R?qd00T_f`BgB+^)f+^{IEqSv&&ebfXPAmvR+SWxGp!%uv4a zCe?;!!W!aR5EVP9(hvDR@~ia2&PL#8BPfsj7FX-G-J-@u4)HCSQLX#FYga}C%Y@I* zrwbJMbZZ7fteCDCX2-YTCdWv(xa}~{@2=oba~9Hebo-JX%vd|@M$A_0oQ3+Mauyoo zEHvCwb6$()zMY#rUkSN$Grx!>rh8*y4Z@qO1(xYMtsa+q+`ebUcEk99 zOPlXs_-0=2lfosrXkG5k+@rada?5jTbE%tqnvGx;S`{cbtRvrSF=6jK+KaOm940>= zHQfQ{Yi89)xjT=V?>yR>?{#tO7LzUxXGEbnW)@J^-Lkpev$;LQW@qcEbPM5S;mWey z)1#(%S)PO!Nwc!UPGXA({&Zli`%ee9xuc&Bd`~)3jhy!`GjdM%^6P1)XU#U+%itW) z=Nt$y;2bdYJ;1W&vaPujIKg^DP1ZCr`Z~&#->OyWpVXD=r)oXT-KL^7XEEa&*m(@Q zjyvDQ3>+U&93N0`U}vx)m6)~kH_W**@PhUmZX;ExucD=eSJmIos%-S%ctfejvO^c0FKr1Zy|sttu5xA>F_rYR z@b0G6DUw+d#ZaYS5Ldcd<0>d9);eMSzP1IC;Koj3=WoT7q#+owF+xBrNvTQ;|E zHrIVklC6i|Gh34Z!-Ad%&5#D%uhcx%$eOc*8~bm(;n-jJ8{N|F30^n3VahWM`^?|B z;Z|X9-dy2Q;a5V|xBY*o7bZ9u(H!*L@015%j7NUcRh%($q;^{J{qrEpnNAOuF*h;; zpQi;kCPr<>{6?$~yHk->;dLz7AI-OH457!Y5YS^zSA6XxYwnp<(dTJoL#U(J-rO!2 z%L{696kGO+wb^Tv8Qj>1-;#2=Wd2<1wbt3V_jSkO>gPeJsgX|Z$Ug}+is zkI{%-d4qU9Jid$an5;3_z5FxBNe^EOk%#GRhC8uEf<@iNm5 zXKL4np%mJD!6DfuL80LHF_&#lS71GV9zHd@&(qTt6_a9ZOoYfr*iSf8I7K*1s1Rld z^M&Yuu)FLRVU6%BzfLyWWy2I`Oq^%p^!fK?XPrPRqO0EM!u_2y=O{eo{_=47yy#Y! z&*whf=$`D_?M!S>C1tnJI^BKA+`2)M*$SB?A4l#>7J8+wz*ARHQ0kZK0%q$H1w$2Q zWxvXf2~e5ssER4NYhE|SK~1A7rr)PhKAWwhol;y^JXAbaB+u%4Bh*!6PoOTPMKcsB z1M@7fCC~6+^HTW@IeH{-mK!VTg{E8i>8?u8!lJI3KIxI^vFSMOU_{xI+YO9F!pVYO zjULD*zw3I*xcSKOo9{~#>n(6|EC{1nsb;J@wV5SlZMgrr>WBFjIGR*6?mG)yj8EGC ztLBE}k%ZM6=V!;JUdwc=A8lPCCdLtAMBdzH+LjCTol9v}=Vq+yk59IemK_>%qQPeR z-B}s4GL#$c`ea|YJIiuZ#hBw|E2v1FK0y(s$T@S)F%G+xadtntWhp!ths9w5xe;qe zRV=AsoT#wCVmApE=Vs-8ll67O^W<40k_*OKJlvnuZ|qFrsbYlP%3ZW1yz*MB+483r zm7gbX3sg3Fbj|LTK2#%0KUg@b=Od-zZ=a{HNdG#$C_SlQv~@U6?54Zr;4^zY@wgZV-+u*`sam2Now?vt7hI!Bos=9aR0Ox}|#Z!)kP{=4bZF z#pCWB<(0WyTKZsgncqIc5`Cqa#;!c_q=-vo4hUB6(H{5C3;3N$FrP}sSS>)tGE;$z zZ0H1}l*$yESb?YT+)RggWxNWW=xmzkEIM#}ee*`&1INqCD$450Xli7x?xh~2_EF2! zvFc=Xwi<0yA5x!J-&SkXn8rk7qe1;NewrzoS(-pWh9+NgK(q0T<`)fomE}I_#P3d! zc&rEDv4<@hCNc5bhz??Mt_@elT_n(VOdUaCo|W4% z2QW`TOF(jF5;K#zmDx`*Sn(TkqX6v@uzA)zciwQ`B;HKkA|CRPZ07CbmGW*V9x0l6 z#sb8HXRC$^0tDM7lXI5~_NFUOr5_hu5u7YNaePP8Q%4-pgyzCt!tL9K2`36?2p0;K ztAu&Ny~0z%>%xb^Zpv?8>`-cz&WBANt1eml%v?E?Bk87`=0Ew&@m+Z?mwcHCzO!wL z!V2MgF$weiCiE(b`q@t!=~PtZpI;r6$@i+-x^vp}Ba;`!ocHf!r5ceiedPql*v#{J zJEu)5_uN?$Fl={_+a+I?LM3&WC{1uG3R@tZE&OixzEW)84^y^oidG0$e`Lb`T z`~*dbHbtQ)PQ=Xi^CJ%JRe2@h)IGnc;*ht`iE$@lKHuF7l5KTVWY89=;+7-NUPTeU zC-O?YH{-+Ufks7TYfk2s_ElsIX9RL?m7O@V?qY}{W0HPJfY3MZvrE2aRCS{N0%?#< zQ9O#7C^d`gRW!SI(fkQzyQbd0kF2&925FVHeztymnXAl-DA~DLI5RHgiP}y+KrU(( zGk2UgpSSXcRDA#bm1z=cs3ys)+mWAEN<_KEl9Q6iYmzF-naX}UPBJ|wN=rjxS4S+6 zjw(%%VMw2{^2XApwV8?-`QaB^lV(!7mt|v@axm1{d@%o-w|7 zrawN`@IWovCC^fn$t&b_a+<+xhrxt3M}0u-6p+X z`p9(WDe1G)73mr2@L7prZDR!r3=1CCg+H%H6kTm6CP7r|q&(yDW^&jN3B!gO?J@hd zVFQc`SW@+iN!TQESO6mhPsV{Ym5nJ&@c{9|I>s!~F5?p6F`8=R!@7LvBIpbCD;oif zYj+4AFe{#8PjxVy`aU5Qu$g2lg~bv*czR(3Z!(ukoy?od!%nVwRC{(Q+#Io$!Z5uY zE8NZdfs0vag_wtFfSx6n`H)lK$(X*CGVpFwD<9XVwm8%E%%7!URF|cgMW6}vVRAGN zzQq(rz{iT98loUo+xz9CNAev9pZ^q|T>%vm0X;`=EdKJd8HV${2k}Dex zwo+(YOdr;5j#^4h6nrUw(NAUL#Dz4wg(S9d4(R22pp?FI zxs7>+#Spa<;9FnKT6t4rXkG0# zcyB%%HR@uX)`m5sJ~diCWH!;Cf=sblz0l``Hnd5*HWxv;*zl|n67kp^k3+~T3Cn;Bj%>_(aS zP-sdiZ>~oreGQGZ&GlMU_K3E+L$9d^!C^26wS@MlwpPbBex^&|8Q*Jw`r$?sW7WFI z8CCU+8g2g}xIa{&&3yHXY1lMufIeQMrTF8FXF9#A`mZV*pRUBeBCAoOP{o8H*x@s@ zhi7QzLSGO4vMChqN{pJg8U;pE<>f+JE0se_%tAkQ>f)J!;rf^u!>Ck!jE3nk@GNYE z$!eg9V)&`n=^vL$9_2{n&X3~jAC>@L~%E6Mn)?!TnS$w z7%hSUYW_;7gx3#IMz+*y33e;Br@P_Ol^B5q3ZdVJ(tr;U#sLdTLMV#}Y6`pks#74JgP3;BFU z`*@{Q*iL_bm?MAA8h&U3A3loEDp-jJa>n=JE8^i}T;ovi41PgcSi{G} z+)=+kq~CXoWwkCZ17wZF&UWir1_S>B9Il)m2n?;i(x)2QX@vvyja*f$$VZfH&4gp< z{R~(zOkd*3`0Nk`Fatq?1`;y}Nzi~SjI0NGOdjY#Hd&z$HFYzi#rhD%wGEvJ0G)LE z42LGW8VkT@_KP0Ea2|pOL}DlsyW8|k=cd1c`LM3L<%6gGS=d=NR)8-X#qd95P>eq3 z52Po#;K+azJW3C>Zu7+WoV~*+41AeBW}tMXEa&{>>4Pt4{oeVqX6LD4NNW}?zeP=*J;r&xe3$&_G!z(g+h*8; z4a2k%)8|qZdnPdW3fK8Y?o)>=9?w`IpQli=EeJ)8)|pdPpX3sTJ|lj+KI%nR=MhVY zo!wiLHZYnalsJ`!MPgypl5~T9)w*-bDjV4nPk%ACkapW@|NdX?>&E#f zN71QbtjQ&edTr@vG>7QV$WCceKdQ%?^u;PWM56HJtB-mO^my>pvM$V8{HXR(CmT%v z+LGvj*KXEmp^c{0Xg@;@P6&TgJ2z#u@F`3O5k@f4w5gt$JvlevUOqxCSW7*8FKtx% zm_i@zWt+QA^s4${tu@-7&iRxOV?-b^hR}6n4Xj(Y*H8*Np(AKijn)%Ev-c`IyOQ!` z8u~nleyT7}Nr_}2=m|v4sCSKaW0yQibS(q91>v>2p20a3<6H*XOp#*WV%h!MdJMg~ z`bV|worsE8>o~?#O0~|M+LcsAIT4$z>g5~!&)cs9os%PIeSzTKU!`TJer?i8DeVrAGT_( zjlnBhH3{3wZZ}q63BG;HSV`V#tggH?R;6I9wh|t>O$5Os8i`h3qv8dWvak_8eyBw% z#4HCh))kBw7x6eq_(IsIP_+sh2|`FkDx#Sn4imUG{aDIiEDgTUCd3)JUlNP8m=O75 zOW>PrOl@i!MM)5hRDz-c`%1sqt&mf ze+rf_R16;%3J{McZQmK=`Z8L18SVDHGTLRK)I&dw(&7ZhDG}OiU`rx&K-7D|IJ%*i zXl%+h?82oeo!BzeYPAxhTXp7iUasW0v&~oH=J4t}%Chck`eYN#j$_4{Y0CZXr3vL% z(rDkLnUDrFvr8YapJQlk29yS-noh;TO;`v!%(=or{WCBd$`LND)Z!cmE8{-ojce*&Q~==#f^3&F2c1+R&|3oC9{N zlbjx!s|5BvQ%J+w@4{efcVpD`9=9)~^wiMS!{`bdMvh~s?(7bGW;D=;Hg=s2e4&Z7 zXoBMgBWfAHFjoK?FkH&P)4SMcH=0qlcB%A0>AVDv{Wsk#tZtf9mzwe{BvmF>S2(m> z(=1kImtg}(v$*w!7nuHdP9V{4`O$n;yX6PNe+2Pol9pdz>beU#n?XrQeK2WA zA3_9{zeyCX=hrCS5!Wp0>l>sejcO9j2B@Yl1OIS(+A&XHE@5&;WO(08Zcm=D$C&QN{o zo#9~a^)9kDnXSYO6UKI7*Jza^WONdt-dg9z(Bn$Q?#UhD`@t! z!Pey50nE9y;ef8ZHXNWHdT0Ed@!MPU%UuBm>|j_Awss}jU;v;1-)!y*I0O8dJd=JUad4xTl5XhW-KN`GL&tadGENO6)!9n-hYu z4KY^|$GTUtt3Fr6d}eSXKjvDqqW@#RE%DT3tc2QaNvWCHkik70*|=u#(#L-IP%COG zb$oi{(v#i;7OkwG%U_!wr8G{z7Vk#$80@gs11*gm=!y^BsMx@{lGr2cr*t%s{y4pL z#roJQiBrOyQd5>Fc&RnM2YZx0N~eC4+!}{{pW1E7Zd$%?)#s>eZR^4(x!2>Av`WQ5 zdaFXTIafT-cq>S@6(C99ZPXK6o@a%2PY`2gg2Je!W~Kx52hIArveEuH)of3e$rf=Puv0>%HtsL6D;6;YjeqgNn&Q#7gcMKO2G40o zG<*Z6Ec2S}sB(?|bQ1 zh9~G&gF(ZdjidWpScajd=zy~Ch+B@s3ypuAxrsTAGdSWEG`Y9BtajJA%0>skZT_pp z*mn3E>$-n*|JZ#0v00|}q8GK=lj8uJY`uVZnzQzxW}Dp0HI(StUo*=g^1i zYFD`CQ`TIUqoFh@2C(K;hy8N#O(T_R?(WuXLI!1%lI1?}- z$j_Z^I*>bng9*8qfMZD|=YS;8lYUQ*v%9|{yY@5)xq(5zDy2ZuMR$D<@gJKZ4in-$q$Kv;YwIa$H2P*fM&91+rT@7jafdd zgMqhwv%px`UY&wq;4Nz#c^A6Ez}xB7$a^6@(+o9nYD~dgwGF-Lt(w}+#HfZcSg5Un z_xGJS363w1!-t~F3Gy3)fNcQUfCI1%K%1-9@MU0L4$Nwv?*vYiFi^0@hk;cq)ksD00fQ7LdomU@^vVW%FILD4G8cMgSi|f zVG4YHS}6L7!b}6vk^gK=Mp>MXXp`_2Qe2OD{a*lp$ktn~DsL_qK6pyBO%Yg>tBAIi z3k{d{s^vZ>Zasa8mGs)C2Rz%r{xhAnj%`fW)O%U$!}I{1E(E*}^GgT*KuY#7yY$E8 z08aIVN^Xrk)4he2uD4?)Z5a)r>NC{x3cWU{GJ1~sHWpgH(nsZ@pfVL4c z8iMU+bB1s*z(yp9YDpZz3Z#%o>Il7cEC)+89M3i$HN~8ST9Y{b9IA{Rs~5wW$ys4S zQ3Al~-P#k7k~5b&kh_QT-4G6XH6byJ|CmN=z1ThAD#5|)Y85Du-<^d{>@mtoIMtkHjhtVgebEazdbfvk%x zKLA;qAUP6b)oL$`e4|`q`wgNk4d(<3iMII_m|ju0&94B;Lbuyd)?BG83UE%Add!vj z$fY5(q-z0XMM%RF{lb0Yu0~0>_y#3PqaviU;ZwPDrTp%q=wnj;tcd~f$Nj>xrD3zA zbFN4qg&vSbIJZNrRym2W$jO!GyQF;3KV1A^{UGseRrg2zyk!3^3Dq5;F}U zArti_W*WjC(gv9jiJ77SGfnp;A=6n`T%Vjm!LU<$-G+@~TUZ9BdIfSUta`@*T0$09 zX%?Jq?Qm&>^GU|doiJR;53~fh#O0u%l333`Hp36#LaPIN0ggS1$4ZpUt!xJ0wRfCj ztbuLHjswa;;)iPSi{kaaDdIh3?>0rWLMZTl;R#E;II5=P&p`Lf40WpQ$!UFVZMcMgs_I%hY{l|M0k-fl(~D|=vO$`;v%XIu;_8+&w* z^XJEM*<7{{zTJVX4_g{`j$$y5u3pn zV^7gr@A~%7n0I^5m@-7C+|Qgmbi}&3dpHgp{Mc{@4rS9^ElU1&?w6SiS4JS$XHT|KWQKa#3HBQNNjz@wxPbB`p3-AIBlFw2oimZTOjkxxd%GI3kGhjkj<|+R-!-SW zvxF$-PS0fy+^`ml=#@aja#a=2kGh{e=HSBrwdge%XPLjr|0W6J`W=TUCZ*F$l$!YH zGuoh8S>=_D6)CZmXM&w80L+=g98+)Y(SS*LGHMiow&w(D;IbfkR)MmHk`A~GU>WK?q(*zxGLMoeN6WB9 zp1RtN?)j7-;CvdVISRPsm%)iCmgkPocqN`3M7UO~EVFlLM${B{5y80Jx%35Umsi@#485YBVIj zVXRkAP!tXIdWwdI1G#*}<5QK$i9#$|=x!8a-arJ7#P4u1c*_uPJQ5OWVi8Qr1Y#IK zCaf`e9;N`?gEa(^u!m06^>=e46aavd5rsme1CY_H18hVg6q=<3?aNh2@5?UbS_$($$fa#wQCW^L3^P9ABE!t`h7S>Q3OaVhQcP9mf z5n33tMp6*pp+OtTL@{pEC_VJNg&B|fyqX=Nwjmxba?>63`Q&;CLsKsz5sy9qna^nL zkO#Ks4Q{V@J>P&l+2k&}woP_!w68v|Y;@HpCx=82>vlB?Pa12l(p4v^<+IPz%2iSi z1H9WxT#*RdA_EhRSS+Fq(9o4)SkD%BGv21xyTmOD?YP;RR8VohcgAa0f*km9=Pa0>wf z>}OWk3u|l?rN4vNWDJW%b#>yiG{H0!2V4L2O5A8LnL}E-F?Ob`3yb*UJ*eD?CQdGi zJX;n^*@w@b;Yv+(U25KUUFXTJv>C3n#HFld1FT+7H9fNq7(*Yy_#PAZOm;!}o{S3| zItfg0ajPCo{&+aIrlUaic&Iw@L>pl6Mtz!5h=1E+jJ1Yb^$~l))J;p04JU9AiC^Z% zp!tKHVR^_OO-Mj|p_faz$fuVmF=Vi_ythb>!b3g%0>p6>MN7p&p`xhqqQu#5B7V3C zCWZ<`0sN^W;fZW9m$jJXxk^f=Qyin13uP9{(EDti+VN`0=XSCtdvd&gE24EikR0pv8EW59eYpGT2Q7SoI z5vZn(5YIO9SovfeMeq0obT#M?BXqNH*U9EqpMGi%Y-E3PofxaEJf2tl%aFFjKwMnSa_SMu^>+t6K zs`}BE&{x=YqBgY}LegGcaw%p`2t5T$Dt5xJcFv{f^ZL=Nb=WOf@?k(|HW^LZ@mF!B zK;R&VE1*5D{cdPxKRVtPRRHHvEu*F$<|9nor^?$U+xki}FxWWvUc*b#9@XO5bDRe9 z736QG>@I{jVGr?8CralDxs*fwNUIMESy-Eh<{;W1+n(PqqK*IwseKSKaA?bImK*BS zmRE?$6yllPp3jb77(2EdBKS>_zMBT#kV`ltl2;58Qr;sp`C#^TcSr5{lg;b0?QXy0N;~@Qn zf^641{E(chz`6S$950Yo)4?IbXWyyO+Sh1z-XbcVQ>(!aRn-HW+aHvpqKHgj)v!i8 zt{Jv+X}!d2DYvV8BR=3XOZ;efj*y}cKrUB+TVaG_L=Gk21{M}Kj-%fT$)Th<-au%j z02kJ1$H){pl(CTHoEoj26nPEDlF*A(sE{PWp_ot$F*5J(JN~E*f9~scCAnY4tg;an zt?m*BDK0n_Jt7QH<1^u-+Eb>Y9*=6%xFQUimsYErfT0L`1QyxKfytWAjA1Tj-fbex zDchI>sPq!p#i59TW{sonHQE+NRsClKhMp75o;Z#)T78_&v}PiA=5XdD=1k@yW;$~- z^Zv8j`0Z9N4C?VvL<~ezkrb8<8ssSA777BxLGaNqFeuTeAq>Sogbf7qkgPw{m|i!L z&tsGratn{YgThh5LzFFHvBIgGu$#!di%TjXq-RH6TDO4eju<8u%Si^FTk{7_GJ!ZPGrttE@ZA^<}nZ6YcRmdn?Yoab|`^f zimU-^)-EyusHsh44eYFG7g+^OrCsFe$EZVOhll7jksDlIiG16wO=K?14>fopZ!Z=| zjMT3rMv92HHOwP53|o`lDe!}P4Y=~b(@wNgm;tOoqM@YBM4O&L=GJySgUm#mosq|6G?Gt3i=yowa0%pkHC3mdl!UEA2!P$Fxz zMU>25_$}gL9n}OZtwU>#_OUXXGT0f_ps`Q`EOCGCjvC6c-l$kWKV`n7filQth<@KL zJ191fC1{_3>_MSs=`aeF8ej%}cCFUAMjQCBZsGHKT`fYFx}ayeIZ&0`tL~%P2nwQ_4@TotG1_Y8ahOhK+~*CY?c`9O@XQ6h1cL<2hSK_{V#Z#AS%NI) zS6NikYF$Pu9^gvf>ZH{PzjxYgAKwKlqp%L9Vul<&Hb&*%#;CIr4OxNw2Ty6O)mD9* z@p>!Tb1PcnO++1#GKjKvDs$2k)GL2ED-}pt+%sA3+)l?jNv_MC%Axp5U@hV0gL5d zQ~%r&HqThVBQrJHY-r}t0wWL@p>T#(=lvrh!4V0J zXgEicj2Ljl0E6aoax~2y#Fz(+dB9i*=Y^z?)!uOk`jz_Ae+8{wQMR_-5>2aW>ogI)#Vlf%yqWOOdjW8p{|D0URDa1`sY+0Yqh zXCz|mG-t81*j~);FYYf6^<}5^755dV`m(Dnt1bJB*=Z}pE5ub8`?=+FOA%Z?(LK>s zTe7E$r;5kGWuQ1vyfTO#DUK9B(XrE_#nIyDmh2dD41{8*rHRwTl^A=Wcp-RWr>z#R z7Ke)2x3F7Sq?o;qA!dMgwyhg`mKQrsDOQSUF6ig=!w&F)pB{butmj9mrKIIV+g zkys?&C}w+!y~KH93cE{<_Sek?;%Q;O7ZQb3wyIhuzed$#hlHQ z%E+2iTui|vF4&x63g^Yb{-Q`Rj$n$wu4vKhEk$ip1a?IS$SH#UV!%wgR($PVgH5W! zz16ytpr_CPw1}FB%^8(KVFBEtiqk9MT47f~7#8M!+YRiMhmrv<;9=Xcq!Bg*C zhkD`fq8w{ekt?-aC<-<-VeKuII~$sceB;k89vqUAX+KXq$~EEqg*B8?$~wbTXm?Pn zo!L1aKW7NT;xJdbAsZJZ@12*~$k-@;emcjx)c_x-PhD{33k&RFVsynp93E0)##h25 zN~mwYQd0?M+=Qi%JH04fmT4~)ZxL|rKU>b|Y*y*J?~JVmT7_B7;j?|JPSHd)yZH+> z0RdDqO~vzAhN-=H9Y~)W#-e-pO}wIhShs$>QE8RjL%Cb9n4NsX?rDxqzvjcI5q)o| zR^`eFB@E5_wH~{GAFxyHAe!{PXfAk=-5_~{YKc>{Qet)U1fp=5NZd?2J5`*ty2yLv z`jTC+YfRP;4L}|OQN);2NRM5Kl~aFW`W}hRDDoa5)}tW*t}dy*slH~eDdH^w0fXK5 z?G+YzH?F#>H5Qt~T9e9qh|5D&qCXnqu3u(R=2Rvu^DPT0>qH#;hYUH9Gst!;`sOPJft2e0&)#$j|gt)J6 zQ0r^XXq+^{2WWupE@8;;1Nx+Ba-Lw4LmE^dybbTT#)yeR8{&(nqiy*kh&jw?;^=6h zZOM7>uL)E{9FmCa)r3GsVfGx|bpmo?3YjBhCev<7Qkkddd33ud)PIrIIjGO}qC-s7 z{v-*;GvW0sKK67Uta9>xjyk3@iw$*|#e2Tr&<3sI zR438Vlgh5iB5_IBkY}odABRnLG_*xqD0ryLzRg&}TwS~+rEw1X%TpYAFZYFlQ=DO8 zn<|DPmtc3+WIc}WmbXzj@lnjapB7!{dpYrnN73ak@`QQxQtLgH891JJOdHsv zueKlT_Pn&1k{IfIGd19HMJl?ww7en}t6uuk;|gV_Rl{8BrNz|5%Px7;^_kXWR-tcX2W3CXe9NB5_Ni7fdq$WUI0d`9D_#1@N6M$j(JZ+_o+0OR4Q&ppPs?xo zg4{OO2VG@r<# za?Lk--_?ryV0hBl{jZ|2_U-C)-CJW4KtC)*0Sg=w0Bg<}e* z7k;HeiwkF4Y$@DdcuI4#a5nEnA$rK0&vR9UYUXI1c%Q4%2=uiIsZXd*^B$-gRaf>- zB9LXNsc=x)YJsdQwk)~KLV&iFy%3x)^A~E$z7v?J{eHrGsoe*w$EpL>aq1LxjvD2M zFSI!(Ij_Doau(L~Q`ZXgRE;Nu+Hf`Ynt_@znxO)IfFMGUrTIp4u*&?T=9&goY3gHn zzVu`~oK=s9C*ph?q95V4GG9E#6Q3SFEf~etPEY!rxG(&gC?ZY}R|)ihXe11n-I#rt zBBmb$4@cp!)oBhhjk$rjoB0FtG85fp)-&n6&b(!^!Mw4&Kpxs8OyM2pZRZ{4UEtl} zC8Q%<@B}u__ZQ5NEtI*XD+Oqkyg)#goE2P1e=KMfTrH+gmtz!rw=RW!Ldb_GP$N7= z(f$y{;zIHvit@r-Bgyj9{$71X`XMm|`G_UsBw>=dlBJTbBxw8fsP!i#S0!!%Jl|Ha zO)6@Y44Y^nbCL;VzOoUylXHurbJxpu$qqh1+_Tfm@n5H3ek_|FOHZPQ(5{0XXxA~? zb;0aa`-aIE%Bu>kPAhK}%&yDH&nY@9|5c72%Uk6vg_WYOVwhr_9_>1snLxY#<%z?w zDT${QHx<*sA0?s*2uYypeQB5hn3e`K!WJ^Hj@J8&$hTAVyQE>W0drwB^7E%NCW9 zKN?}#wd_~HkTPjmkGcR$gPx+Pv5k+6j(<1Ov?>3h_fdmhbk+1*r(gUu{juhvvmAM= z{Ud%8R5%@uO?*02vq*!|HD4(9X-YLWG>I%~rwOaTtpGE+(jTuE+pJ zZHchWvw^2Cun;&2gaTi|?YL4KzE}EULCDhXNkx{1I|N2&(4wSsg2MFQ1%%)S*=5;X znf87B_wi0%-0W#mOr^*nuEc#N0tW((mnI;{}J)uG^vei6G zd9uP=KBa7Srd*DE)s4(`yzk}f)#72BW?sGSoN{A;;&R0OT#WK0q`w9o$Q-9|ypwW$ zm^wpY+V76}1eiSiE+)RLJo<3V1U~XQ95bhi6>>NRT~w$QIt3-&EZr`>xne^q3L++? zE1MQ2r6v_;e3O*A0@hvo)>%Q#0zU+9{*vsObr$i!$y*{f$sNYyTxGW5zcHx#x+J*KZ^BMWYs{<3gP~ae{ zG6d$8&6HbCUB7yWvita^YiZTf3^<}kBD)hlYq`T3KN4S zv#;E4QNfIbans#E40VpZg70xDwKwsc_it7n^sf`15$rBXpx&t3^rMt$*Lry*Q)9?zqD~_GT|^vTzl{HI8VX zb{C2!z_dHvxvbgdC#t)pYSPXfr-q0(q$-1t25lWb{{V%OTpT!|WLnc*c@RAPcJ=a> zQ30F3b(v5S$h=fJ`*2Li&Vw=XqcIZ>$3)eXNA5ou^9}#EtC#b;Oeo2$j{GjEzT%gw zm*KHD{4y(oYXlNAIKb5`*os6ioJgH z_9LDLw5p4+o&^P#Ui@?MrQ)6&zyHFgdf$sO>aQyK&zN%cQ&p=vCgVURvMx&quy`;q zAT_C${Wan3FREo9A@XHvLPyBQ^}o>%B@}%7-9TL}8``p=Jx$7F^hlh62D+KOpeFv{=wwQN-vQEfzs>4wCSY?C<(9)+Xe_x$1- zuZq{i8|CZH5{&fYo`z3Yt_r|(!F8DnVp%O~HY}bbm}!N<>i`7Vj%Y#b_CCcG$9gS@ zFB4P<>I5&JtQ#cs5z2(I!ek*oTewYlXvm$*c68ew2k#F<=1n&HzZ?If@Rsn25J@-^ z)KlUi86lZ0iIxzPmPt0kEc4a{v4#vfm1)%J$q_TReYicsweu zpcm)J0*~bHI(#7PTFcWs5@&8HH2$@B)co1McG*5`cI4Sb8sDRbm@bw0_Tq{oCIe36 zu6HQ|PGe}mY0p}UW!SZK3Q2O2;=~st?D6qQ_<=`rHT2>Osn@C(TrXl@%8Q>VLfrb4 z5WFa`#$OA->>K9Kl*H1!F4rzwT|+y~ekr%QhV}mCvDG!|phVtKHBL*ck$V3^eH=32 zH1$QW-G1lDMsdZNvXCZexbo_?tfwWq``H(oD^F9u(PFCm51WXK_b(7r zW_lk7r1b(e>6S4W$!Hb9|PF4n7aqrcvlTe?xtQqNLkxy5dC-qX2W zqYeF9Jkoy^!M6~JzDSA{bZZcb7A=nbwS?goR>gEb8Xx{c5>{3d znsRCLvtVk(RnJ@bS418()}pJ9H+_DJxY&JOnRM~qzM1bmef2i$ZI*0TvuaY>#7%{h}qvjbEIC19?Rxa5K|T!d)5y5%R*!; zWm{p3TTJF~%4KGCGSuhY`M#vgP;M({r&lxujlcSM<+N*$S7MJ>8in1PxrgPO)HF~@<>%rrd^P@jj zWM*rtEL3QCV?l+9=~YFQqESITrJ?Fl1ruTWW6+$ zM5{U_2=@E;B9m`8bI$`M6aa3prcu59dsD| z7&^^CO+jpHqfzJB43igAt%c2eOtte-!1?#3jJPFY7qs$GO6rV@&x4kTGsip+DyZnK z%Qr%ZpR{VNQ(oZ>oAfw~Lvc^bww2W?3a)Gm2Lg(_80avHK#TV=CKyq{jL=@FJS{W5KH+i z-sDzLLF5fL7u|>Tw7GKH1}&}Zx-;S{N+K_!!dtUkhxUfK?QoTtRw+4fWYoA8n8zLn z&6R5=$1HO9O|>*vZp*|1aBKY#{m4zlkjRVnv{z@B(>q&b_s+5XZ6q(!thlKuySz>R zTwo!ywBDI{Z$kQB9DmJh4>ZUPSXoKUU=K5Lv!+~3WE$iK5l z5W|V_1cwOHeCEDf7d>-d*JtaTy~GLPGI5WnB~S~YK{ulhrH`dgqyJ2gq@yJIF8Ve8 zMfxpz4gD=0F^~~s5aZU=p1C6dL(DkLIM2Awc*1za=wcvTWX)QjF_txK z;{x|NtSusxz}mr*u<}?}Syik?*8A~V_DMoR1CE2hh2zbc%Rx&xYdIMiLl0!I_oOk!jq=q3lyi5D7*;fb z62SvOgP>E8p(h$3vQe-_w&iBF*@+ACKSKRfs9 zmJ?fYHyNmJy4dyF*KktnW&OzAlXnkb?`H2VV)@}srj-_1i}s|u4w3Fjy;+RoISQTA z&lYA*u5#GYv?p!D;Y6v?!Za(#0v9`5&&%?pITvY9&tiMhuM}UcGR(Ytt@a99U2(li z?@eyvu}w%ZBDO%1iP3O!<2kyBFk7yHI6E?xstmb)RFK#uATibuu+-nh#ct zcv=vOJ-&Ic;@BpQRU{AJx2w}I(^#9m`+BF)arg6jjVd{mQ{Br$t~Qjlb?VS-+CyrH zdO~v5YPtp8KSQyvobHbuqpO}HIp$(l-OJ^%+2{gJ>Q_Nl*N^ECg2 zM%Co4xv|@GqqpV;eZE}ub^Y>LxHvO6(Y*ATEag>~jJSygW)94(F*){Exn{4|rz3)sbrF-WNo8gr)b3Z$# zGCZY59f-H_sizX$ACG%cbfP!6+>DEK=hd2VvES(PUYl_>;^?DzG*8VqTs>yqoismh zjha-n#OGL&9yx#n#~sq2457FdwvxY+r^qIHIXQPZ-5Y%*H}IzrOv01k63_VY1WG0j z64YpYpKzGcLd;?yGkWrhF~G49V6j$^K;J<}OIP-EBqPQY(a5Rk0`+W0!}Y1|D~IaA zo>_+rno5In6Gi7Oqbq!uKT+#wo0yeaI?uTxnhP`xBAG~^A`s;*+mUDH(|3c zKi5GX^}L6x%pCi+O$l+<_8pgz5E%DI1~smi=6NR;)9^^MyN78}2NPaa?X~S^BsgOx z$ieW^VH*CChw1{HhZkVzpm1RZtY}K}?9Sqj!)4CE3+>x3W*0B z=+K_}+7&75qiBDso;Kdkg0cT*hMB&2O^p1g{+yDV#er9$7)$-Mwe=_6=VlI8IrjZ_ zaERAE{1+C<^Gi2XTrQQhP9Z~1`Yp^B&b_$sqnDhE<4^no@yIIyy;G_2-zzt(eTvGR ziAM$!o`}m&vRbj(DtMz6f2&pcoXu7dq;T;@tDlRiXM4f$k4L5z&Q5Id3wYv{3#*tH z*^&2W3-u%e4y+4xmU!*%b)t$A$X~9Qg-JFf@rhl|M^#I&qz6JRj=6S)--ikJ*0; zYvHJ2%}ZDd$RBt&cFR$=mcjX62Io7& zCFd1hk~xx}J-pjXIcTfo#pGel%;sv$fjo3>Xf~QbCa|K(ja}}HnT$}zDtr5om$h4H#lFoc#=dkK%ehKtH#DM`HRlZ)B#+I|T;oJR@VX@eC3$c%YMI-*$^)`Vh_9e<@zJoW4KAFCN zjw~1_=wkZzCAoB=XwTqhbTmgK+iAoI61g!b^RTH1t!F$Mo)wnE(B<4_Y~6_(PzOVo zwNfHJGl6yTS23&pH9E_30yE)@pHn5hI1_XD%n!pS>AA8;!x=MY0uG&w^emsj3FP?m z(MHa0&M}UDUVm=1>@i1tN#k0SmY*fb8P{<#kIs80L!LY?Z>VIfWSZn4Ps%G^fa#Y1 z-0{%#SUb-%@?>70V4Z?8%aDRHte~vNI6ZMWAFHp~x&k$1h2@^&qe6Z;zmDI=*Any< z*bBz3LIq`<*n%>ifZ`QN7vu=gCBbdMGXcdb!jeFbiW^9tNhiRP5%g-Cw(UyOCNb7v- zv4(J(e8+Hh)S$)>?PE>*lxiz6mrLeLubBF2E722EzYm6%x-YYUKGI))uZ7g<#bys_ z?N!e0Z#K_tXW7A`dDZaryX3xhZQzwS4M%E}%yImjX)3JrH;Z>Z)qHS~j`zhCy<2f- zHw`vB6z_GNjS=ja`POMlj8@K$o@Y}Hv2u)NBNS&GcC|}j1?HSfMjHaLk64;hQ}GAG zQ_aKgypumVisSRGRfbqE;biZZHik;XJk@;4FrrAukhW1dq;<5C=Bl!56gY)bR-N+s z)@x!CnEvc&%lXzrnz0N|{hVnxdRJ*cBCoJpGe-YUR-Hb+a-3VC&jqW_)8lPirH-p` z(d(e9+41#Y)lmkXRtH=%>bB|(bIh7@EU1flT1}0Kd{nh!hi_<(L2R;bG)kW%_Ki4c zusGfK47+;vFvr6dr`2hT8xqr$0Z)cG!l0Pq8o91Pc!7Rnz>szYz1b|QK1~UO;vyY} z+PHMneKRm-MTGvwTS>tFa2nqW}e!63=`)*|ItLz;EspaYFBhcz^QBA=?l zVN5(z)UNtWoXC8B)s|$&^*2Kvart>S$^#hF;^Lzc7$*gAvM7&nl~KiLWV~neVp+0i z{!zVMSbqE-uA-eR1K=uh^WS{Ut@k~*_^9eJ=-rlP7S1u{4B?EK9v?N`6D{7okh6j# zphpNC3$k9gX$kG$S| zI-kk+gk!_}aDH$5_^4yD1N_@p;-gRr{{g>&-^te#3=oVIum#9l9wP85img~DSavP8 z;#UE>B`*||3+e=I0xeNrk-cc-xNR^!-@d9hQxqx7SS9*Jv|n^u6ji);+%}_r5r3FP zJi_n_=NZcCG5t&%hvig8T9YPyQ(PNYfql`t946yS6*NX24$JYk1`->IizKR;={i*s zS@I?i$M+Ub_oSUK#Z2mQjc7JPNov}z6trJ*T9Pq&Z*i_?*(9D4D!#Y4$n%3lUpi3g zDD{xelA5Z%71yzQ@%D7UXgnb|E0gVwbQ%K8{@1;pm^<4 zLkEj-)~`xM7TSibrQ2pai66JkBI1v;j_Yb(+VAS{%Nj;_c+z;v1o33d$i^02b3N&9 z7~KtN8%+@3*8E*{3%}pk_~uE&q=eM?=9i{T5 zz^i3drRL{$C;WJvwgKr{uH%J1j;URul1dH4*?xV=TgC#yHm-88J| zb)!eUD*33~V(ReonASqOZ3|V6HH&wZ*I-xZ87~FROi9*IjyBByw0Crl^XIL`mujX%=cT* zNMZQ!&e`umR zfG`gk&ll<^+?sdI$HM7c*{dcCCk$N^{9$}FsI`dBE<6%4RtH zGkxMvPMA9~niS{bD7L8EIx*zvs}jF4&6Yp2ZqxQMDJvDaS5?)V&z+oD|5-9ef9^TM z9I#U<)WKSt`}AEPJY~YQ7aNp%I=pjuSMo`1$Z9Nled=|VX3Gg`!wgY7X2HC;+4=OV z70H4WBV^6_waATQRB|$);XFFp0oxU<{_=+bI~&%WLC2@;m!8i0#dF5+3(_0Xr&9D< z`dMlqvyr*Tyk&D`2inj<_YZN$In=%jUzR9Klc71P0@+PjwQRCtfnvF0jU2hkedM|F z64-pRqG!q{($Q?NY26{V)73;SovDx$MWgAR^o4ZlRKX;nBeEWS{&5^L2)j|EJ!(-L z6FsRKu2d*|J9Ha2YW9bN2IVZ(79vS_2VrZq- z8$7PrZ1A9MdmN3QbHmQLRi-8#ckOFS_G>A%24i?)U5tVEIl`kf5j&cR4&=^l^2J4? zgAUieU6{LlLdJ!LBcCTOu%R8KrwDPqVddo~OsCjCZV0r0eG|GPMla;l*4~S3P@QvI ze&n1Ra&t1pD$}hAXTpn^+xj6672RLfk!ZcyeLcw~!U;olY9E&-a&{Ob3UpcB zuiYo%XK#zmYFXf7t5|`hsgGduPvNU%mjtXWSV$#5Gl3T|Z_XK~9Z4qBX=j|Ud7@h9 zpQ0=@s{|PdMd5-g@8Ya0)5DrK!Llx3Voo{qxfQrk=o zpGgR1_UhMnMUy(nYKze?FN8PEf2mJ)tChxOuPm3{OPGR?=}#>e!i4T8JetijJ$t9J z<6<%bhiNn%z8iGVymRk%>1Abn-(TwY9fbq3`n1+rrDFXSyrmBFE*O?%6BXH_uV+c$}9?gMgG1$K?)VUi(Mh-9=+&bE1ll!lFFMiWI+OxN1xuL7?yUNy#jaGr! zp2SESvu0ta_$}*lhn{Dtc&KMxIwn-^&Y5Y7EzP_eWG=Q6>PToboQ7E%&-0^2&gSX8 zut1NfA^0M4kSD51zeXvf} zV? z`)TMTT9IBJT2LOFRvtYZ&G{^hFiTl{u>3;N>FPD=!xr}f)0V$Wv?r8L)~HkVbwnn; zvz8RySZHZFMY3@BRc@o7;R%{~Cu2d{2l*sbXHZIUt#eUSs>!DHyGq4k6>pwerqu}x z#8PptUn}=haEZU8w!lK*J)Y!ovCBloQsJ4cwaydy@9+7)kKuRSs>NipIu7wO*BooV zD~?XTD?$8S4H82_PdY$4Qp%S4OHqh)rF5%wjjR2IWVg8u*(=?Pu1lXtUrD>97n}b) zO7tBpJ9PMEK8{C=r(6m&4-Ay;mu-}7q3;_&i|h>2Ib(X?Oh(5m>6x;8S*c9;NY*6# zDC;e!UoQ-DkRu$8TwvBeEq^fI$iy>$FfvJskS7%HU?<6U$?pXom$QGr-5GTEX0-U3 zTqS?}SR3gp1}YpC9*S9tFvV)cHU$F9siH_xsd%Ayr_fRLQ=t*632#@8F61mzt$!0O z*5;ujD*J$F@hw%2>MdB+jYu?zEFdS5^GW`Mb>vf??5M#BGLL7mq_Nrl)2v2Pm(Yn_ zw8}3jLd*4#$)@9656z5D{(Rmt-pDokbj6k%Uy!3=F5213V*e4lNHBoS-o%U=unP7} zHabP!VGWH9ZFG#gob6W1#N?d(Cp2zQtdWM9B2gpmx4V&n^Wb!xX6049U(ia;rO}#k zCc?OQi_*H|T!-C^GEwXxao%5Z*3_qBx z+zs@6yf=TEyI}9E(rwWO+RlCVm+db*I!?_}1=?-5OTsjVZ&O&?hZd|kK2ytCgN0rN z&(mQ0se*O~39Wr+1y2v$9ke?_wfuQ#GLMTSytz-X^bTSxhL%R7sCd1?z|Tvgu|4rA z;yVGs={mz!#!6yVsnN;h~b3J4nRJ}Y2ZyJsvcH~G8y_My4U z?SnYZw-yc3E^^Vf54Ao>^3z&3IP0)!^jOOwf-!=rf`BMiUZ}uo)jFTD6hW5YPN3yQ z!L9Y^m7q&Nh^$40Poh|9vKkcgn4bCD_Xwgrk$o8OV#7 zT&`!y?wY9z8c-U3?Cz!fDo=-9<&Wncx~_4#re$Oha7sA!jeh>i4MVpWFKa8V(Ar^9 z)7U{(x_2*NLe-QZ0YT_mkT5S`S+MQ3ptb?L#r!vT=5_r(^1zj&p^IR0TSsxS+V9c=~Wea5iraX*>~?^LmHQGhH3&$9}4^fU6xW5#k=;jp>NoitpLFkDn)=T@`XzR#jM;t9f zheYb3@Lk+XF6|RvOcQ+gc(Z*pX$VuO#{fT_#Mt3ZXYvfV<61n=9nd@}=o}5Ep0fmr zkD=Ae*o{%T z#?fFT7)*cRVKd&`WYWOUutjFI%giI~sqHbL?fIg>xShi8?J=zi)7>@;%x2+fCGH1_ zNq>&*XEbSGy4fUi?BZEmZPeL98*1$2!vFeA4mmbkk`RTb&bXhG%IGFoRaX;WDzuj{7vik&xuOnlE?6Qj9X zzbQ+n*}dhkv6H1UGo#tabBOhkosiILY`@1_ox1XsJvLR}T)EeI&59Z4Wqf?;AyCDfFuRi1Tz%2I3Qdv+jU6|>?*p*r}onm;6RPN6!Y z(lv7NWwkn^TC4o9#fkUqwDoR@1N@$7m8Z~FK8k!(#uju<%V>`fB&^iG=sEzVOJY)* z-?~T_IBzI_<9;i%z*sAKxa+}KXXY|EI5w!!>G&@k@5A0LnhZ7uS-0PW6YGC^cyaV+>;|BY?qY zBr;GMBb!mcxXGwyz}c)J*Bg+L%k>5umJ91EX)^jsn#@tXWPM=q6evpJ2o(6UIANUC zoNb&jbaaN3PoGGC!C|P}Rh`6$093@E!F$0UJ{v4`y!*U*9-6?{<@e_=vEJid?r7Xf8YKh{}z8G<1OFdnMpR*OJEu4t;`)61{+yaSqgWJ6xkI4nIcjUb*#v(<2G zTBI769h%K$!(a=J zE+AZRrm=ga9^CC5VhRp(;1gi~x2}(n>Tg{iH?k9PjubY{!Q<1@fz~Dk-^Fu1zK_78 z@Wo@%j1v93i>eiMu^~+RJqIs@^d9iN$7Asa=R(Sj?q4ORoB>TBxf6_Nf4LA+DHcNb zdQpW@4wuwr3L)f7D&FRV&=hBc?xUL&ax)#D03uL6q-?~3zth5Y;|OvE2S0Genh1Y4 z0>b)+0MLDLnca>~;NCU#WveN;-RRel;O0azPIJKua*>;2ixz5Kj$xEYPx)S@fxeP(SRx|U8;%gtYO z26_yp`8i-RJqt|-+9Z1ogYm>rG?$1l1|p&ai#)81-f9g zURoNOG=EHE5JqJJ9BpvSg^UN+zeQkb&0JPP8WDpCHv(QHq)BRGG)!BI2IR$~zQvvc zE_~pw8jeCb+A?Em$i9R<_#{%%(I`DCORsLZ1YhK;AwL-qHUuK=QLU$HH7e3&K$-0I zE{LQ=0ZG1!lHD!JstB~aT^)y3)R3`8#Iw(MQ;TS*R%2TShBPSWTrJ>wz-D4S(o;!% z5eXITmZ)^vdo{O)G=mlc$_141;gfu*S!3HO3;}lyjixzBhmwyTn*(DJX< zl+sY{N7dR#5sp-19r83*wQj?~MYvDK!1-|KSSG5{!XJjlmYz9Gnfs8v8?mHNMt=hpc8^v^}gKEfu$`77a z2yVw17*Phas+(GH9j%^V zSx5jk8`KG;8P}~TI2>I0(5f!{mj5?_e7YM>5eo5vM?4-iLmH~#K(~|%8bU=npj#NY zu%e+>pj^Eu?fRnUR9*a$Wl)EDC4zcT1&LtwNTW)Eg7{ym{-q*RL2z$@Jr0VB`+9Gw zVral$(Nl~S`K6feyAxH+G(-=gRH_5m*9?iBwgiq0z7)gg(fIFV@<9q9_&~88>gC-H zOjQR*R{zkz;I_+$I}!pKO9mk;G=tKhRXwT;lgpM0%Y*t)%^i29j^go%(plT0Wn<$)I;Bp z7g##(0SL^lUu0eeNGu?K<=lqSaVa3wG}}|JwSc?@QXOaS3Y2V(lb|3E}f4ZzVIYM21M z5ub#P)H~=Sc#~7vdq6VKbD-6M?BQ_-5>XIP?NFE%>X=FO(-0^MTLsy|lnpVcJ;!x0 zlXm+!cBrxFo?l=*^@jq|(6i6^`<3xtg}{Bql<8|@F@R{_@m)knVn zpVHcY5}Wxi#r|3`h(X;bc#Z}I_*X4u`lY3M+6W>7ml`5hgKGMMU~@p-KB^-~HKh>$ zMU#*EFQS8(|IH1B=wo`;1pl!>P6(sA-rwlJSA_;XC`#x4CdSvsggl(TE=B%rOse|- zoEKH&e~tl_{EsW>j4;RlE;`j7|LOgN+@1bj+^@~&{BN5N@}N4|e_AB8HmEN2fFCYu z$}EG$6HuajyBP3|3e-hSa??m5?#+iO7ruLSBO!py283E|`E(;|0fBR_-y!FgCF4Rs zsHwCk&Mt_P{X;%A5a&D~Hs9h*ytNB|1W3PcNC+U!fKU?l?m zZ^$)3CjWrE0A%qG$OD%wdPQIG=Xs;5Ptae9L*`ge<({$5hLcaf_($ z^Er}{jQ<44kZ-AGWhLW_02%QOIR&k;6A%MHdRp}wAcuZHMnKMI0QpP8S%8%PfNX@E zTL2mME$6imr++kp8SQI}Z9kZU{{+ZiwWv9jiZA&AF*%)z@B0DSm?Xq+079*;-Fq|v z;@tTm&JifI62A=Q8t$#}#M$V5Pre?T^Z zJf{Kqt6rA@srdnE1Eg2%_i>g%K4Ss-OXgjGEc^i(0XBs7KcqSUGM@(IuUZ@dB<~00 z6d>n+K+XYj=^FwK**ziA;1dSN?z}YN6D+9T__B22yB>sI5cDBXc8@*~2ndv}7(y_D zU<|!R!V1fJLpZ`1U z=mnCwfRyLfd~fIj@c)^Mk}w4Bbny=oCR~HZJJ7m|t?B~}?H#Qc(gK66rUpE)g5@^6 zRc%v4x)5rL!b?3gmq9n(~@Di$_`2pCT)J?JaxuPBbNka-3x8Sr9( zBx*%6sIjfnKh0pkYI;+Pbz@uK9q2<}W3Uyk1>!Y{)Cs!&9wVR^x?4YUHUr2Ia( z|C==sf@JWQig+jhCBQiu%_qC^A9j{73UscyKXT)n@#2rf{UdSSH~&A9=KI9?U(WwO zDSuKon0Ig25EbneIJ|Q`D!SdDni+fnBXvr}Yeh01#i0}+8S)33fzr?%6s>QGo$vhT z{vHU&$oi*36!QGfWBz)@1H77#+JDo~IDMXTP=vnikAl2ofRkk4BLeyZ$yvg$xBkZG z4Ed8f@Qv>KO@!YGU_e-+r2PWzH|2gmeg?cQKUsFt{Z4kj2_pHO_`3ev?`p5VH@blA zp9TJDTL)B~s|P%j`Z z5D&-($QH;B$R5Z6$Pvg1$Qh_NkPA>BAXgwapuRwSAa@{=M^89=0rdm&2J!*w4>SO1 zAkZM7!9c!1Lx6??34nw^A|OAYVL<*s0YGG<9RV~FXcSN&&}g7BKtVubfy6+h2!nw_ zfW`rZ0)+vM2bush5oi+7WS}WPQ-P)dO$U+!g#$$ZMFPzLiUNuTiUEoRnh6vKGz(}p zP(08aAhJD@<^E5&U;E$twtQd$oGl>2&V00ozWYeFh5wZMck=&FK!=g-P?XLZdl)OeR-nfIV1um`vaK* znHTB)*|`@mLG~H4uSg?|$CAsOZ?_=xAP)EG-g( zWhpW;++Y8T7ZV>nFU~C@Ia!vD+Qa83M9hhs!J8pVNPwhd$q30zATN@ClJ__HrGTI0 zm8`or;Xx58snN+P`fxHnStlfqq;m%%<1Pdu>m>__yni(i8JDbA@;7;ntbeIwl)rS$ zsIjB{MB@VVS4a&n9Zv>}#)Jlhj1`3jj1>h1jMLvv7F`+|Jtr|fA~kxH|F3l~f(vAt zqN zl>B~QCEH9Y5ZQ(lKxBI(+Z3rw$TmgllWjm`yVU}bn2~IVWMAzHXA(!+!}*W&RWgmp zxcGQD{iZFE`IC-pV?v-AgQg}X%VLuw=I~Kf$N)j8saW+snOehpDAPAr*hD_kzH+`BM`}74RBa@X11r$PpWF7v4yGKQe z?G(VCqX3JJ(&JNqn8ie(bij}adOS(S9|IIW_SZY#=7q$%BxdEI3%^D}3KA~AcE9?e zc>1s?I64E7Wvl{#O#0uXgXw1h?coso5ALtme*ZNN^yU1orT*6^{Goh*y$t>g2Os43 zXKL@SzxRXu{(2ew84f_#nSO zQ+t2?y&vTF*UR9~aPUEXf2Q{S`g=df@2{7^pW)zL@+&Vd*GGAMaOd;?cy20{s`I@x z+8djA=g{ByB@&5Rt$r&PV53VH(uDm^ClQM~->Z+`Ip8gSadGjq$rBr&J%;~Vm(KIJ z+_xh9i=RrRa_A7>_Iu$7S#|jTnI|DG_Ft5s(+HKC6`#<>0JBt z>3pv~e&>L<{GH|ZU8NG!?}7T=OcLUDLM1{CUO9gXJaAl|z2DPgFnB&+TL2+&S9>kP z5vm>ncW&AAUFEl@zdhO!{zv2o_kzqI*>=x=RDRvNb=$gi>sxhj`SN8~*DZSCDG@3~ z*vFV4izao#pq=@e}H?y+S(@+_!Jv_U+qu?b=mVR(9dSg^qCW zK;+-X?;PPj;{T79@3$P4mX^YJTT)VzpPvt<`E7*%cF2FZeE)6s-hyVTY69!LkoyU=OE!AG*o&D`GAp|_{C6D)w{GRfAFMJKWz~Big0`!Ai zE(ibna`)%s|Ch_xuHk8Jm;oc>u&{IT)@fCM=FPW}E?CjZ|2@2?m?6K2IK7S8|v z^zrvCdgRCvXxnGboayM$|6k^R?>oNyw*R&FIXD)qTK?IZ z0yx6|oVeJI@G*fSXf&Eo?^rBW=OT8L6+ECg!L<+0oz-pUIPLyFHh#9(^`9@FwRQLQ ze={?ViHQlD%?1w`#yY~SEG_M9ZJ}adP6>qs&u>|NuaN(K`8wNQmS-=1>px4ruye?u z{yrc-cXxNN68+s`+B(db?3Wzdhj~}dE8zjMvMTlwKvhf zjr=qk0-DlW|2a7-PY+?oI08+jqn~wnz+Thg2Ty9^oN3dh{&nTEw6Ms`%!K?qyA2yQ z4Cueq$??_DAuXN%UHdfm>UE)`d`5XG0SO2f4gg zZ~JZJ_w0W-j`!%%qrLHT9AQMqFsgLQ*(3X(?cGtWShxi|TQ}WYo?X|y`%fL^drN*> zH+=>}=(f$Dca|TFpL9AMMoxQsdq}^tq6dHHO8sYk7??Vj@;%E3@=H%oe=F^xq9Plf zg3bEY%Ic<>*PV_x_Hcv{=)vtC@WW92r}4|9M~^x;0PuskdDY5ieEzra@9Mf2{=wtovUT9V#@@Zx zL5A)Bjt&kTe(*TIalXETJBNR^apQ08*N`dLecPk)`F(wSyy0kL!*g}*1Id7=BZarj zr%)?k82aZ9?4QH^{n{&T^TxIR0-v?d+}kJS?dgHR6Pg(;jldE7U6Dg8t^0KgjQIE}(a?_d$N| zsGz_3V@GbcOls)r@?vP7_)ipZ|Fwom#i8ch4-WmE@dL3p^kXoqBX#pbZD8PMrTP? zSi+kKyCDUEDk>@vxvY*ZUc87795|ps>({ThK?)Q@#KfSWpdjQ$z>7n>ckhlK6VDpa zSLdqHsOR%5n(JzGdrws(gI{z_t#w^V&fnqwNSv_lwOXQ1Ygnu8@%VYk%X$N5t+qk; z@I~OGdg`EBt$(c+L!sW-wdRDD=VyzZOq1jz)wLhcNf@Y3BIM2($8S{ z7_$N_6Gu}m9WDG=9#(P=Wojwi(Tp02JDAf7Eeq*|mIj5Ej3aD0$8eO*4$B(dl9P}Y zr4k9v*fM&MnTdUr8Lzjbs)eEzbmtcI7;JUG+}`2@Cq&Mn*Ft&*%Uv1ET?=Zu_T(64 zI2zkIZZg$4osGrM#!{YyEUwXQy-iJ;IW)Lp zR*mkf?%f%UEHfUjl+Krz((M=wk$k#*xD(I8GBZ)K*=xI1X-_$aTC|Y1y~ii?NXDAs zHxswm6)iLjELvz(w9t6FRgLbO`_z{;x_xuww1TB(OEb)dh*NxfTIG4wnpb<{F7sAU zmb+4yyV91se##%Y!+KXpjqcmWz-=(gT!hbwo|MD0KFUg(hAj>oDJ;khuF)x^87Du*=*L$<%}?@$if; zk;U11D&1OeNw88?d^&K7zfC2)NSaj`U5PCobotO4@5_g_dZWvSz9K!@Chq$`HF3}J z53#o}uw$F-XK)S~at?(VaSj;=A7a^Avh6GcVIW zriQ?>hoz4mP|6-s8=`48VtkQaxF1e!#?qbI6o)s0-&1le4$% zz_uw32M&Ji>Q!jjuh7yvFT>F%q`}c_;P8l-5wj#=52`gUG}4yB$ff}sZ@LZ;{6x3$ zdWJur+&HBH!-j+gH{LGq!%Y_~6KoK$b`SW4UY_b=LUS?j{<$g)V>}6&uH=l7A+_6* zuUWfrnIygxPnhlJbm|bH))G~ z!QH+uW;RB-ikz*SBC(Q)W>=9-?<9x)4*8KyeR-`}rz_^C+pV!H!~<@)?yGqjk)1u$ z%{vo@H7+@>WgR}5IJe+#Fdn&dUERxwCC8mdv}P^cH)r3BpO_M9i}HA`?I>1X_M{x4%LIh?+W*E9JWb^%h8x*zqIM|A4t!-L9Ga{1)vKL zcFdkD_mhRnVr28;+dLMezu4%V>Dl8nu z6%>^H$=a|4T^fIw{H*jx={J0&atypXMR(o*mMEe*@b2^nRLYP99qpw2hWxSor95*^ z_nT3k8fOA^JGFO~JZn&iHMXPykF+`^yD39YWGymNdA-1V3opl0;a9%5dwx*P%$%eg z9CtCH>?!U6N+Pwgtap#@9sE&qQ~X5CYD*4rVpFf@d)1G&s}K?6h-jiDy@j^IU_U6##%ow2; z+vZT(^)cXzZeJ0AEU@mQ=2uiAYv1y*<>6Z_?x9HeV-)!eMK;>qZ?m}n6^cBEBCFd3 z&6LPdC~`jfD6&G(OpPSa+~*4WM`lOzqXg$XmHx+qY)T}v)}G(D?$y_ez8$-^n?u<- zJb0b>!Z-U*<+W-BDj28D?h)OXDC=Iyyq-T2ie$?O99OVMJ3a{CmvP;35%UC7KA&}1 z`JM8%^4Yhm(Ye|y?8?RC?jPf-JicG{aI`AqfN_PP!b0OzHM3G^N#qZVRP50n4=4%y zg-I}9h{xD|giNL8d=Kfc2}mK4$~8%RKf$@#F7s5}yIkSfrNXo5(D8LG8-ouWSE=r* zo~vkTWToz{9;^;hOVvs0Om(3eZB-vppI6^eYt)#=Oyi(I{WT$)DVjN&aDJ|)RC7qP z@r>pN4Pd?ps0*(rLE^DqfX5!UYM8{t-LdNrAjSQMTf;Ypey8K^3pX5ovJkq_}=(Yk0;3?1!!4rM-r3^N8GwB2NW3tQ`NzYDM)((Oqo9-pQfk zO}^7DJsxfpFTI$nkmgO4|AHcQD6*+ciY5?o#79J$g?-sZVh^F5OY82E)bpktKWR;j zBKabLzGUhM3iGVYi8+vY5_$rXF*BI?%q`6Ra$osR%#D1shtKBPalN@CxRbcExr?~S zN4$x9fP0F2Q~pHW!Zqb1F5iPcj336|CZ1fpoWDOuaWdyP|0=)o)QRKUGhVpjh$gTS z^cHN}He4`KFiWsdpjag+5$qS76xVAUBs1OiJ_;?ZiOA>qKiaZm+Vrm{FG@Tg z+Qn8mGIjdO39d=`=Sy}>n^fhuqatkht_ZL1gIRK=#ATu+)opL|N0J1=m%9#}!VY{p zMeA1)*6LB>z2o~}_8nyie{Y(@-l!8N5)(o~h$H)z{uwxR&rix^6cBV`+=;{myLv;m zZLacM+9GB0a^%;0Z*1_1l2ZYj@DcQIlfA0E%92z4pofi6$B*I~D3QC+Pu}JQ{H=)no`4dz-r`~yhY`2t0Xcdkjjv+j$r_`3H z*s)14J2~r_+DSH0CTtTix1Ts)vht=x^x(nOX<}-WCd0qS*Oym{g~j{CmExJ##gD{i zs{3!RWcp2%oSKoeI`$(;;Hgw8h72hyZ!T+IlP^z{9euSWV>YFysu26W2*W;Z2*W>a zpnTjw{kVY!KjTaNlNgXGVNBzdO%1tDV| zk)s90D~dI8EXORzA*X-Ns2ukxIdgL4Ik`FTS&8BLxdH{I1&^P{ysSqQU0oL@LDc9Z zT+^x+a@sHxrVX{)Z=Chh2ACDFBn)j6-?VOX!~9Z`U>EmWOUzm;zqQ^ zbBU^Sztd)b7Sj7$e8x#@eNxAJ4sX2Iz&vp+ApxI3m6po1WI8hkG2z|$F9~ABQfdxC zXg^wkCg95nnpVnGBr!9Yh0LwYBh2&6>y#A+=pNAubE)>X;uEnju}p=}jHE?3Yg1aa z5h#(Un*iVXYSGG?o1&iAZH4#dvr&^S@kL#93+h{|<&j?}3#A})Y))^qfY3%YYu7}g z`CNKzqt=BM5z0mru@piZPiSYfXf5KHIFE)W*q{a2AdGyOKe#!O)lH9;kDpfw>g(Gm z)G8&ZkjBcaA1ixU;8lVil-Z93=9H?IdQ{QR*i_q6uT>U~)VCdaZ9Om!lR;=Dv?q0S zI<{$pE{kh=zY*Gp7fpoK=w{A(RL`i@4j77uLIc{wQ@@&qO~VEn;X;4Kz^%hZ>y$a_3NdEa4G9ODxcGIcFkgIVY0iyqpuy z3Bt1i89k3syiHq>$w~}Yz!wNc3t@sb+++@d*O}Pa9`-7j_?O}E%k3fG*lvR% z)!0cZ7-(qXsa!>;!>7wE1Y_v^jaV>EU*g9Ya)bhyfgnKxi5UbWXh7B`c7uE-5Aq?q ztRRS*y@}CkcZA}pM<;whCq0JX&}CO+VfgI+@k1HzL-DYg7@CRQYi`ha8LnVHe%{mO z;fv4$>?|8A!#@pVgdQ>4hZgV#(K9@7WW<(eQ@yf&&yUn6cE&sr^b0p=jW1iJiZ z^+dwt;;{ShcBodYwI05g7ML@pJV<-V;a)TSQT_0?TJ6;SrPvHpcngXML+A#wgCO6T z`B+&O^fd~s)%qdm_WtE(S7Ohmp#>529_1wpY$gN2NFZ!MeQLD_uasc%bqwScf!FD# z_AbIqiy3GWMS|_d3J2&%41?$OPwLn^5EZY{aZIU{8l4rjJGD;tKctx~Iy6%m^OVJ` z5E#dcm)S;+=B3)qFt_;!UA4x+=#8$Lgl%EBE2|Cs-M>**;@?$PS6?fuQ=qK25I)7t z1i>Ymh&FDM{1r%9&;%bp)FLHfR)HGp4$8|CaXCovO3)-%wh5XDLO?}IqJ=B7=iGP7c-_$)LPzY&Wumq<{y1T7`jU?|%cG54ZP2;*uAqKIhS{|_3H z{P<;BYKxXfXrV%zwbMv-h+HZ3gl270)bj>9trr%BQYiShI2H*Xs!21%=WRvZc)r*x z9d9t28S+A#Xo!3<=jYmJJCx2tasBZ)G~y#HdZWI#L9rwB zeku<-hw!z^bN;Q$0~>0s*6>mN3($N~BKWvam}oqy`_7owt7ug!+MW9<+9iR+$8ag7 z)eV$W8uZ!l)-)J^sP`jrbkiWs)SPYH&61*UV@pw+?MjSp+m+LGxq{=(wpxi>!MklJ zpY~+aC!1kT99zz8bIUJZt59AwjrPS-Gg5)(ck2uKa|}(dKxv?==~O(%jD@hHoU0r( zAQy9>d~HcPWqpo=sVpCJimq}nRxU!3V@&&8{A+T(PB#bbMhnW8Zq+_0hnvcA z{-TGq?JXr0IRSvD#ynvP8ZTKLw1}zaDoS5P^Amq44D=;(tXcQ6}KFC;& z^^~F$KEcjza{tg48$+RD8SALOS1G9~Z}{4^mepymh}v%YgX&M=gcI$WAI(>`YkpAt zUnAaZQu7;1Ja-~@3y_o~2$Rg{O9(;pHw$A7LYhR|lUs!Sf+Gwham~U6fNF*k(EKQV zhgiZRMy8AliFT4i*AT+F(UPbU60nWmY1jOi1Qfqs^AC~qHI)$Un%@ptf#x@GA800- zE2+_?0T!NM=uN?~Aga&L!wjiEj{tRVaFO+y#Y)ULb!<0wtyVEA#hd*!#H4O2m_#>G zPt@of4F%By2N@KYqb2oQpk{~o+L5*cu({}QKzFVl2dIbrJpSkL+gc5)JOKu5XW04L zc@laU04N~1#{@eP1>i9N0;u*^+0S&?%y3Y7>Q`6U=qfvlS;X8%n}#{Fp44eYl7}zs zlT17+#{nFe?CY`)1xXsH7_zp({3Ce=h$FTiLB;)(j8-Hcv@`!E8Gt~mvzM>Ewaxkg zVO6E!%$~`~=gcac{pg!gBeC^~SJTFNSF;~2kS7i?x>=fdy+uCYX~^ajY9>}e?Xl#P zg~d=``zAK7@m=;b1RrKg&8CjenYpYoVBn&a_36AdIdKZpoa-rGG#_7=Ek0;j(jZTK z*hcw!&egPD(U)`3Ao|mswiWA=uBJ_icFWFMBIjn;1|RNq>PZgui_ErU?5pe^OLoyp zgC8wGsx@s3pB3LoR?w>DgXnE?;ih8IJku?}*%p8#{kBq1Y<^h~)iYIuor#F1p0Y3> zXgGMz=haQl$Eg;33e3(Zh6}~fV`IjCg;AfT`HhXP5^Ukn?nf?~Jhu$5peoFzXptfw z32D5G7xMOvzAd2m(boG-L&6d3Ija2Yj)CXQ#>Y999H3#yKIsme?i?@#Dg#2>;NY9` z%YiA_Ar9TPdcT5h`^$d1t?>!EtuNHDU(@IT);7_oIX+DFC2`wzM7imAvp-`_1t8Q`u+!nf8gl&Vrv7QG<4@fEv9a~`TEPPd`tsDo~WZQ%C#`5;5l2a`8 z75$3zL^~=8nR4ma&*K4=-07!$6+?S+H^7i{WAH3;qH!N48kgE98kgE98ie(I<$z0Z zl+XA+mjpd5D1si;min$QU(_{FV8!lNAanRNkoj&o02!+{KxQsc@I!3QyzgKZh9Mdc zwBq@}(m=M=E?7VTB-09$++71ou~sv9wl!3bWZK-Ylc)2sU<&{;%GW@~oCqwySn$yE z$KOWK?E%TGk|F9#7{hgSCkK{dc{gNe7)^o!tm$!8UV>eR&j$C4z|n5(KQxK1detl;ExZ^sE&c#+9*W!I;`$X&Dub_AM}A}L`U7uG1WKvo%}9jlN_1Ao z#JdrIW~QHh;yr_n**ty@6L04hzNw(SIYq$4TdJRVmwUp*+wINFdm%mF0yT4L%|Tu1 zr{457P2DCUu#tR}^%15@fj^0Uly7m!k|!j?Z5jg)UQ=OM!J1nt(|ui%Su0623x$8!&JD4FC|?d#jXH zEmeYtF9^Mfz`9CF=zA`7T-uwS`;xf*;x$$>(3=PR^kBb1r+v;grEBW_?F_*@K&J}; z?}L5m(630z8LUgc8V7J{Ff?*ItU>o1taPItD`{0Uglf)EtL_@;p^~3VE=JjinQg}~ zjwPEj$=u2kYy-57n8{FV7n?Jbg8?=oK~!tnP*ymFL{eYV+s1ORG~@AX)4(ZKB-EP3 z3FT0w>?DIk&TP&KGl~KLR-ZO|KuYeG)IpYeIA0Fspf?7IIlQMdTHD2Td&JFdBo52# zy11)?6S##VORtjW9=&@22{d+nRo}p)A%|$Y@AZlBk7G_d$ zUbiU&>1qZYQT0Gz-PrN0jhG^33;R<~6Y6STfLJ7?nu(cs1kpyG?_a?>=CyGhguDS+ zFG-Mfk%M0akX2$U0#~y=cx|CT5x04&~6Q&2O=8b{qcE%ntn{ z$a>NVWVsLT1hOu&LI7la2HBAyt4@1K7#!!3)PFE-Sqvv!KG;NKA0@wK&P<;D|IyT&yGkK9!p;;q?^8eL=Ul)@U8cDh~?ZRJk}jB zlOF>B)761v1{vG}#Pk|45wBNU&JnLyTTq5|ve!bNg+8r@EI$BDFWV6l37EJU?RJw0 z>`C3HfSJydm}w{pnW(QZ(@^$MJ!C>8W{L;QG(DJvOlLiDL(+tTVJ8iG3=d>m+k|KP zhjXlL`y>NeLe{oRtvOrU;nI5dO2(}nFkL7Ow*k0h$w3hnNq*sMMhL)#HW&6sIQA?B zt57hvuo*nhKFO|0MvhtAKXO`pY_9oO?~5N@Q_(6Oq`PqH<%s@?i<#vr8rCP%H)VJF zzQyLp<{tB&=Mbhpi&|4mu|ZQ(OrXfe8^#1K1g zIZAfQtoYn($uOr<40Tz(JAEO3Z0?k3`cBO!y|ya$W%k{9KGSc9Z}PFZyJ()gi8=Fj zF$!3PgYqk06e(!vgJknQ;`Tm91%CwD65^8kj`Y@4_LWm%%aCe=QPw76_n zjv*~BM%7KddM1bRk}TPl>>zxb3)>L3H0%guFplG)_piyPDRKKmVKA18j&5h;Jp*nD zQr(5&2{k%eYRK4=^tOA!19IoxnL9>>=#&TflZTC5o4$wR!oj~8;liPOmaavayVF0( zXLvHgErSAMB~da-%xuZg;N;wpu=G$Ay;BmEE`e{j21LY3qB+qm`;d3T%>&NUb9gGx z(}}+4ugz0UQ*D-=s+gtz^aMK(f0lw9JuRa=OQG~y$H?EE-ZdYoRXP<#9pGugKITnA zIpR7tedpYLUB$#c%jw0;LF?CGvAt7iSh4c%%VXZBzj5*4{kXRijI$~IEc6x$NAnsMq3CZ=%G#B_c&AhTYtk=d?tb%TlBwL8yX%TWsn%)0EZ2rQp4Z+l9+i3y?|8(fNQ z>j6r7Zo7%8Rx9<7%g1^#(DPbtqu#=F0dSd`%>`f+ql%Rwt>TB&#ebtZl)W4fiaU@(I9t0 z{S+pEMGavwtZDe{(=Tdh1(o!frbdtINegK)73bnKTVzc2eNrb&V@zYpnAJ@d+p3#l z)=^++OJjRdE@FHZhNZD-_$%XdgnF*S!wrWyvKA$5V(C{X99b`tYqf3pEQDj>{^jf< zj?qT$9@6Hs4&Ua+6>vZ5LM~0LqtL)uKGMjtw(Je+DFW29l9;ZL16)zci8hLyc=gg4 zH5rrN-!-TwDDuX719@ZPp<*85@~8^rMj;k0^fpPfY9zu(;Xhkq1QE_1j|7C8SOi9y za0~;;gf(T(!{mT_u*L`y_Rxvv1H8NlIRKzcM4=El0AviF12&=%a?LV=_Q@(_@X03x zmXk>s$eGPQuf_oF$V*TTDqgpmN#yK6#W=C9c^cYIC(tVNm4UYHgaIXo&}MpJs?1EU z#Yb>6YzfMz!WOPHEc-mR8WD?&5w;zDi%i%!*PNT9CcN^!1g@4^!^U+;(92Rw6l>c? zus@kchqo$gAI!@~^y#eI=u-lh%e9$kIYD(~X>wK&R4S6MAWSnqLkEf0CAHd6SW$^X zL<$(+XCN60OCYq-E!xypZE~{~wpFDW;B%NL70f>~m?+*3&2QEww`yxRSaf4uBn1>v z`VI<;CbTeTok>AFmqzVOCQ9_8#u=cOt;`hE_l=uvRV2i!7!vZM-TtsGLSS+fi8;_-lq@iTk`e#t#MFV3Fsp-ZznX@h|;*IyAT23@` z^GM@5vRI0~JoYS4YMSRVtA1;{PWGhD@}#9LV|_Z%_O+?0!7gkJeI(;6j2|@F1C{zQ zE^z21Fu_G_24MX0ac|2(;q39ybW#XCU4|8*uRAOcg`x?mh$ry(h!F<$7N*Vcb(i%K%1}&{Ur3lJd7^NcC?ZN2H(r>Q;3ee6 z2*EH^CJf_E9htJtFS?H~+Sff`?onZsmoQLRqbplS?GDx&*jbt1*Z`Lr9jxg%)5$fR1U8Rc(+Qw0HLcMtt8Oa5_EaFS;MV8>k(e{+ zE{7e99F`Y550_O@rLnA&-bu4>GuGNAuB5Jpl?_QW^|GwQ2^1}?Y^=mLg&IQOnHjIb zLBrOC6Eo(?ddoLrb3>|;r&GUH+pzw~8+MMoau9n>{BnY{f4@kaV(#7cOjod&<>qsW z#{%2ZaL>xgJk}=fh_tCJR$c+NWwVf7vM_j%dnuEJfP9(b&oS<3XN00_ zwNWrFI=5GbYIMTegsyHyAL}Aaf!)%to50wP+CNnfCvfD3W{1>TZ9h$YjSg?Ae^fu( z2F41kX{*2Y>rMa{Z-_gAjaCPDNNM#2Eno5JZMtgOExH@#_fjcP_%Z z0SmvTLxW{F-`ikQfNcR?{@{;)#ccrOq3{Qzdva*QnczjD8}hJPOGPBpn<#iF|D6+zr1s5CJc5`Q3!(8Cu^ zf+uw+*+Ya+>Xx#E7<4f09<2^TvCar|vMtYrIh~ovT+F=JOjuF2GED|jD_|dod>V9Y z9QCZ#wlW^o4tf?AkXQFFpG#Wub`;($D)K`Yaqj4iyzt3ZT;86#FP@`orku9?y zb2xJ%a~5+Ua}~3MdH8;#5mwa#Txzt#2=tmu4XChoE(w52dM?XhJI!k@kKV>RxcrXF z`jyK?o*i5^PDOr3tPtdnS_1~K^h~1uZCov;f zY(&FIJ_&tw0-tT|)d_qO`sxHecf6@ilFyy5tCQsO_Uq~-`D|>jPN+?_2#f%uu0dT& zp)%3<0jzO5(e;h`E-({je4|HEZWE86Q_Vn$x|m^vVhuLL9U(DThKJeUsb})A6)*ds zGCuwG**wf$Rr`H$6a!I}>f0E!=7xZ0FvP-alo zl14tdkwI@n$X@^CMpv-4nW$gg!fugUwC><_kDuqAGBjXOV1W^gW;Q}cbH~0G6@ozn zER>G$z{9A~7WHc-uLbh+7hw=hro7J!9cx$SaJ>6U}3 zD6DMEm}9`kexh35ft@_YtZ?4&hc9StHMT8yy)Esf?J!!}EkqqSn8WD0y4Y?c4I+C| zE0hk?d@jWfopfvd&}jow!iqf}eCETWSR4`*_D~~FYBbfnL1(e+jzNL?mBVxs1B+Xj zOBx0MntVhB)U3^p7#q+m#`me@11c`HT38d~=*{MdHYLw=FKw!^1vXhW)?m|zi_p7a zpyR?mCXX;17z=8(PWomL2G?M*!R}E#%*7ZnHg+rn_Q(efyF^6}hy9T;yK=O##bN(j zSmo#z8U&<)=OlQXMU|tSMF&S>^op^raCV2^?r=s^Z7N4kwK+I42F@{XMnbqEge#A* z%F&O=Bzz&j7Xn7Yc_f_C81Re%PmN9GXbBk*3IU5nCt{ilTzqz9L_dt-FXlQZ!OD)rLJr zG)80_W5ceosj(4?*d?M8k?kXl{nF;8%}5bg)1K*UYi!tAAj?n@J5`h_vYjYm&lAlP zCEBnTiWZ7&pXt~NkwWwmB8jp@wk0C=M$tx5HO8iS&^&Ax*sxcKR*14}*sDdWMYgFT zcA2OQ?q=V{Zex)PMQnzM0e7<>WwCOg+mMN}N z4ItMl>PbVbqx#i0urtLJlamR*efuaYZ1OS8Zy$y8N+?_?!U+VH02pc$zD?MxHwDD+ zO`Z%0QYZp!q-&e8A2W(mezd##Bi%TS7g^_gpV_% zj=1``hzh2hG8c2kO<4A{%h^+(<~t|aZ02(wG%ROyy?ZrS zex^>{LPK|dT5|T~&66~f+FiVnny@hYyXw0ylNjdCBG)?xcp8iD6Ed+_{rEX-)Z$>d zkC4n~7k@EZeDnE(A_q##lGCQ><5OCtC&QgEwxG08mt8pIq)a244Ja*%4;^G>2gtRl37AC71y6vj+*sXAzzIGDMb z9#4-mLjzv%UIq6}-J8u^1FD4iHS;1<&7|y0E0oKaT=aQdTJF^dcaMSG(+6)_Oy;6^ z?#JBKJqLHZYf$DC*%r^@(6E3Y(bQg+5E251WmZugCcXT!GzB!+QyL&X{VwLIN zq)=EV=iW)5Y&*EO_elO^Ta;@@p)}=onZa9vHRT;n2^?0kF?sD1-vgg5y09^=d$G^n zjUSf?;_10|t+_XGJS}Z?c#XYQuz}XLelaD@-MuXP(vIEPsAO5y?riMKWlv}BR!D6t z)2ZthQ`07v%)GQrYIkxjZT(_eT5<`xv}`;7YyL&PnxA4*X(8vMTz;vOj!zL-2%H21 z1)~LM+K}a`se&bf<%_lnymsf!L)Qi2JBx?63B1Z}#r?$Ya)qBfRK8Fgx~f=wQCc}F z3au!=CtfVQmE8N2-b-&Kdo4!xE9ufnN*AfGbgcA#d9qZIB`uQnR2`Mdm6Mp-2}VZE zA)c7RgCet#kvqWAXxTK`Q~FJdEOnl&^ammrRYa7qkIBA|sv1ux^nr^5%VYuuRT(_%@}4ZGR=@+(Y~p9I$f_qpxrV_Z~;d(IxwZ+m^A- zuI4#3U}fw9nNF-5!x}KwFx`Nhz@?jixe#+G0ZwRH&W4=bIm(>NIn^uh;+6)6+vjze z=N?j}cIO_BzL#QyZd&9Omlc=xsk9hd9A2DUjJ_?tRD7>ER6{TCTJBOFt3+eVCt4?z zE6P`E%E~8l&z7Sr+!@@qa(9iN#)>;!nM^F5rM#e02CXRP?o(!~`l90f4n)}V5v@(O z1L|;rxd0ie7V_Ox0#&fefS;!NL>0bHHB<17YBT??3WdHLUX9e@aHOZQ+M^-tSO_~E5RFUKn zd?m#^=JtK_7%8sPFxup-m99RxtB-x%l)AG?D2iwlEG5n;oH$yH(#6Zf8^pWCsW(1acTN0Ii~_FH+^GY>C2R)L4F`*jl-_@7|sFq+QAn8=;8ulpW7bFDu_rzPsFF&ADe0Rtgs{kaC>=lP9W0doh=u_oJR6bFTJcXF2CfhJoy+SA3 zxT>D=B`OrDa(^Cc)5AU?$*8&4@w1}}9Geea4EWxt%jN0MPQSvdr$5E+_{k=yV`6{d zKX*eHl1wZXX~>4MT7F7%Q-hvpS~R9akidf&MuZX5xoAF&n|-=&BV42f{jL&(7{yL! zFb$=y%%MyP^BZw0Q#i%?2=@f{FmqzBXQ!V&V4`fUVb^CH_%Z8z`f+*N8Uz&B3|ab2 z<~>Zz-8Wqh--_a{;&Ns4=7?n1xL;{)+iI*wfw3VK%sD9eY+}SRp%L<9!iITac*=S5&LQ+t3p6TJa9Ceew6=Dsi2dFGrTr!>vo_4U-0|T%r=Bjw-b3 z`%WmaCHY>ibWXgmt9~ImfAX_h=Dx2_qg+hix<%V3>-F(>)Zm;>}vm0)>e{plP z{9bHhF+7TpGFgMpwpV&O6-%Mzinj zqi|QYoQTW$yg4UhO@`6x9CXp?ux@Wo4RI~!VNRpUP|dPd_bnC{hivE;QJhkoT`X+e zbXZrMf0DkZ;3OS8NoSlb{;^m+fRpx+XUXL-%aKjFS9w7Bgz~uZkIGk;7nh@b<(1{v z%O91WU{DX9q?;>|qjG>UP&rkZpj0UHlx0fvrSh!uN99vxo06saUe!;9hN~v3W~mkk zR;fN0>{p#sp=*N2Dto36(?H;^&eMdd(U+Qe>Ui-+^&WMF`k?r}8a1dHn(i7`%}~v~ zSROb20m_sv(GcPtnnLkbd6lN_5zViqDvUx{5KhEEVl;uK5yO{UfANJZk2n=~SXZIL zjE@lK8`Tj%5NvrX!DI?~Vo$NZcmgwy+2Gl8;^(xcF4G@xVXj)QjwsHrolD(QP&*f^ zovUL~xKGy(VWXD(5leQ z&K<%R^PMC3Dg0~~yL{IkN3(YD(GC7%{!4zUyqCaT;3pU@MKOZXAxGxs3bM&-xWU>qmNcuN}Z*Hq+_JhrHN9`Vl;YX zLiK7@xzt2;Rk~T(IDP7h2$NZWnp>+sy|Pq29q) z5*;_Kxc^Mnh#`eKT)4o?|B6jq9VR0(h!cww)e$VmO`CKl8uk#B^S^vd$iUh7Nd>iXX)eC9c+ncGx>(pAdQRcEX+ervgN;ceu8u%v0eL;6 zGN{MTF3yk3D2@&|68P}3XEM>gP9qRbfOVSbEY&!R%Tq0El>0u#4lc2I6|Km3J(4~C zav_Cs&?bDd`?TOIvIuxw(zRl9;O>mw9-H06nT}TzJTA@n8l^g2N=6erF2!9Lc4_7V z);Zo!t`()-c5HUfU+0){*yabZx?~vKy{13lH0{cl4jfUwYM# zi&pck?=H=?%KZEaie4L~coL6xt}oOo9W(?Hm4P1Rx@>aXB+6(kxZ&nk8mgpvmLK)Q z?j;75k5^quvdTmsl~->`Ii2VIC?XJRP*Y9{12HpAivUJJ%^9x&d?$1vi}pp{Xi_4e-?ise-*!kzn_mz@+XYC$FJuf&b}JV5!efmk6@%=vLIgYv0$~p ztN5S*(XVN8FZ+Hw&hYZ|+-HKr8&Jz~&X#Wug+1UaMaq{s(MLbc95r9{-T!OuTfm`O z`?sGpgOChDIxs_0qNY*E-qnnf&J8kXcNjyGl5tk4W+?5{-VGI<8$=Er#uUn~_EMqa zZqoUn#we*&Gs32%_5Btdw(r~Te&6@IuK)FYtLw?QJ?F#vt>?F%`yR`Kb1Mq&D!jLt z7Sd8LQuJ#2Q_~Mq^tW7qA6WX~y|)%%=SsyIetxj7a8N|z z6^9+qntN>)2btGS(W(fX;vv)kWh?XqHO?}vD5~`4no_i~ylzb?_S=T?H)~3@;^_Nu z3iyAfa1NCBgCewieT<6o`{#@il*f&_Sy&M4AI#thSKOZBp&w;Cb?AV#;q_V$tD2&* zi77Xd?qodne!3AguX~XGAY-u1INw+*@nhM*hmegDee@W7-*zfR zx%_GWybVf95vN2otVScH$jTLVMSEU7K##rY_?d?Vo{m*-?pmlOrdZfE{aChelLnvo zz*{}+F2`1Va+XJ9cI0k#dE642QoO!t!b6u!TXPy*+6uSkV915Q$6Batr=C#zZ}m_& z-OJj^E2^2|{x0&(UH$SvFB}}UTr_7^g{OW*WO#U_{#i;Y^`)qCJ?bm5{DqJ4vDL|< zq>j4^+dDZK{_Bx4OF9t0QTG}42Ve5_%&vu+`qwO;iE zYTga`u&6e&QcK1xnKD#h94+nSc6g0k;G0Fk4Y8j)0BK3Dse7#Ys^qjB0 zS&ukJ6Ris#;phZ&EPc|^{&Vvz{AGwV=5OWh-Wi7+m*(a=F2yj%rN1Ay-q1?#i^mg8 z9J%B3@DMx!PsK0c$KZQuP!0a=+# z-%ET`A~_)+d&L|0o;UmOr%boHW4jc~!GI})*G47=#*M821Lpplb+%bs&?mowi%*gc ztPR-h6I=9AYl+~J_D`3vbkpgn@(U)R(bUZ+^l#huu~9{iJ@3f+(aY{Wn6kO96=2JL zvu50)+9^H8KYfyvKi=y8l z!=;$^t;OxRj+GWqk{)L#Wa(0-GEkjO8;yLwJwJbcHadO$0%@>b1g6T@#1vIq8#vXE zJ^$mgplHifc|cOy>6lG|vFD#8O^q#DciE{cI?P1XeXL;Qo;t<0hMHXs_>JX{HU^qo zyH;&Awi}(lic0mcZVP-WUi31Dv{v^b(sE{A6|kO)18MwjkDP@jWU$wwSaS_ntS6eV zM_FOZ7#g>_#}R3V20j(@cG%k`JV)5f{>zhn%G0j5|ffzISu%CMc=dOe9doC{vWX zl!eMOwO!UXZd0tyFtN?ot@g;*sLT4tcSXituez+eH{5EJsV-3eroOIzs@A9}geigS zi1EZsVj&Shh>7h)F2N`xE)$Q4H*nb6x64|>N5g3+7Ij(Aq48-wJZIEHbRH#C(?&08 ziZU32+J+9uu&19`(YBx|?sQzJS(dJJkhGOQY42|XSH~8LdAdJU89z09(`Z%uVSux@ z;c8lW4wdpju-&*28xtgnrldqux59z3t?}!82D2W83}+gszcw%X(-==O^aWv%0ly1M#N@ccu+u)WM;!Z*6R zugIG?cQ$=t_Vb|W4U@5F*}vt$#>o|SPFvI0Y|zfSF%4bzz7Z0W{lK?r&Mm>M5Hk_# zo6uJ{%6XX3H3EzC61s+<`SGI)*YVtCuT7cxd^2+yT7SG>+%DOt<<-V+*{_A4gj#<{ z8Ef`*6&y<+C{YwD&Bb;ZsMJyZC}jMZ`SE^!nk3x2AOv^I8;`#p9B>NPD#C;Emt2%> zKjT$6W>-qI?qR2}rvg%Y@GOzDpDWb__d(y$k%JE|*kg zlRC1mxm~KSWxr#?t2p2gab396xj%8kx$C(ojhn+Q;Z}1WaCMZQxFeN_)M@bG`|+3Y z*YGc?Mln$i-)qz*6)8&dhX0vwBt(5=MhZ{iHRf{Rb%r2mgAi?R6(nT|x$H{eJz-1C z0Bi9k)5iPl_q*1&9lug`q(EBuRHUoN^PdXj*=*nShWvG>Ewq{F3d(kE2P zI^NJX21Dv2q%F4D(vwnDCA}}rVRlNZnb%~mndvmdlWpd|cT154@e5_O{FQv9lr_t! zxHW$`UjEY)yg-4L<0&_q=u%vc>)-gqet{dXFVE)^#NvzIqQJ4(6}0)bT0 zn|(ldQc-l=-{$7E87!k%XQSxXEF(b(L%*>CoDQg2kwQ097Si36vy?u`KRBau$y()u z)E`(yHN`4x_V!j(teV8Ol?Jg%Z|QD!5!?4HTQ6w!_2vERCv~K}OMj`uweSdzrWC8j zs4!ny{panJ;ArZ{OzdMO=hWn(Gs^orb4A)IyjRzk`#j3L-ez_=v-sA*tgNOQj+;|S zU5Z!WQIBpn@6jA;+^>wXyqiUM9y(E2hv#9J3*WrM^I*}{$tc=!Mn^gIg#%kYW@-(5 z-#Df3@h+{NhW@vrGHE+RSi!6_>0{|RI2K19w6P) z8NTbf@rNq61}$F3I_q&|jgco_U)D=sXRmwRSTDU@rMFS*nf4{O8|OzWLM=F)kSV%N z&blrYjQEGk4ixpLZOWZ$>CXv2a*T$S{0=(dL3mU%9rnSa3EsZ&fMvE;U&mc(h_sS-k))5I>)AGiT=R zY}U~Vejp93uY)*(Ddq)_5(`;kf2KIBpe}6LC~+Ug;|Vtp4Ln+LcSmShQj6;0gqvx= zyZco;a&6g3^^5CvRkiAXcQ3L_TU5qH9jBRZpP^aIJ#2Ba%GmBu;P!yhBO4z~V!y4* znySI!C@Xk=CKmuiY=*;vpV`5@HPWD>B|(cv|~d`gX4#>+=rX zr+)o%pZZ}>{e~Z3LrWoXOP-~AE}AN5N;{JS6NC`Gue zM)pkBChLO_#2rpQ^<%J0p85s){AL_bI?6I^&JK>MzmmR~iZAh%yERM{oNcC?d&l7^ z4WyfHa1eSLwPr&LvUc5uqb&RcUWxm2U*d_}(+VjUdD@2j*_{`M1^pj@sFRPNojK{-^NuZ)P$y{fELzI(XDT-s0FE`8uL3jH9&s1JOw z12X+!v9mfNB0yatUHb|b{PNoAcOr5;;-eWa7_l_C*NlF{$XD00uc;BAtyW`1fLuNG zcbr8$DMxy&NA8+B>_6jtBZ_ochI$OF}+AYzy0MpOqm+>$2e~8+ws-H)_ zx3SrmdJm)C>npSrGM8DQJ?8v-i*NOKc{U683ei#F@4}nHdSRkiHM z_gZ6c4i-=Tp_qpz<2}x8(zkuj#k7i{V%Ffu&GbAWAKZaAOhbcM7K+*I35vOjh@qFP z*V=N%N5$$zT6dWJ(l7guso2Oi!z)#NuT;V7+fOF*of_v@w2Zy~_79eFiV}?pox`3t zF-awF`&BJf%hjeApA9MA zFM&=lE16zNuFhqWR_5K*K`dm$y6$ep?pJwz@&tC6D3C3VX0wD-ggoIcMWLdPp#R8< zDuXLk9doQ+K3DvCm2us@&+X1k^E;iHSZC&#r)c8_C+<2W#r|<-?!7CSW%tnI%*)D0 z$~Vf-N+Y$6dYl@0ssmnjW`;lS%v`Hp&E3jH2e_55&vJX5)>{1aQq<#Q)EDmKC-bZJ zX7f>$f=;{7x1e?MuPNxl`BJn(x=tD@TrIr6cen7P=&TT}zF|l|DRQER9+wD1>FY&k zD?LZ_SXnK)Bi*3;7gV=?!9>)KfZ8>7X>4sN5#alAhS7cmCjRKb@E27t(RX`127K{i{qSoYD`&<M)2`1rA9Ntvk#xN1a6RfcP&AH81RpW9$A&arI1liu_iS;X3@@7{sfet*P?It#^ z$bVs%PWnykrP@s=y~$heVLj|yPfwxYPInV94##gw-<#(%4E$#LiF7Jv0`vCafCc?; z!lC!Jz(B3RV)xyVpUt97w&*O6d3}I&;Pr^zx7K`Qb6TsO*~V+1&d>)podxUUEJ33; zMq!E9X!|YW~#Fw-2 zY#DhrenFbTE2C!e{0w~NpuOWftv`f_=Q$qrFo7hA>2nfY-~ zHtQhxT)~VUK@pGALUwz_K1hppK>knD!uESDt553`AA4NIIW+L#m>u)&l74ISeptl; zfx4u)NEBh0&wnt%!WrH<`Ry3i=-t=2==BJuY93qqt1MQ4?k3%#w)<2y@LFdH?#Y<; zgEjbic=n$q5L;(^qZaQ?NolqZ!|o4%%f7}80Pm(0y+jVFh^J-tQI~Hc5iSDMN)acVWNYwo)-iRktkhsC|+7!8gGEdSIfoM zS5LWLAKR5~p0ecLXAuNi{5jk~t#vP||McjE(Unfko?wz@&x zuGRz7PmChigf|gP#1TIedkA!dI7cW5712Uy(+1G&X_Oyna3<j*df7dT^VT;lU)=hHty*muTrlT#1~$-9hMnioCOC$95@$|x!2J2z;{k{ zKH8rTAB3%Wu~$LLO?e6`%1y14r(nOOzLBSB4UDTUzOzj`&fVxv>crd0m*cfA+o%$D zn&h&^gp5LcQe)>`8+(QQOaCjr>)rR8Sr6Sh`;3Wnx{VJO%hML1zHwZZ*^&{9Dg?AR zAJO&!Xu5E@bC57LVs^-{!q||^cm(_9kR`GRTc7;>W^S_V<7lVZZ!-Gw&F2?p4;Q(K zz+b#*%^uSNF3V5yzHo?La7M-K5UxCyx8O`>{0=`2&WSD9j4#YvfDd;_Jca*(Q%?IY zc_Ax36H~ZyS8ennbFH0jwb9s0g=`*IzTfN>K4RN~M10m#!&3oHHuGkOBl z!7<`_)^oz|a2TatOKLd@VJP0@))-{}+6Wc1_BvVdJ)Xq!rt^RDTgVbGWEK0AdG9ov zru8r+OWCpaIghP|wW-URVQhm=`Ocuf5GI*CHr9Ei-m|Sg8WS>L-G1XT?;r1M8jv|) zzgev7xfaWGR^_&5<9F4y8*(_i>$jP3IKR(y@Ae(fm>jh;OkDV^vAE$Z+ir_HMpaP% z+*l)_rJvbswd-LGG!FO&Qj`zZ*@t$I-95mAONyY*td$W@p}RvciMs4SMZ4_Vgr#u|G zp%Te2whjVUoy-Ak5_SQcfdveesg z3m$qd1`Kf9BiFxj$=u1=!pV8u&jD3)tABie+wMik?70)a?1@$v%|<99!ICPG*Pot&@!Y0kmC4UUYBP?b7S_c>we_9}aa=*Y^u%OYvuH8>z1 zqJhVqCYBxrW6)^UhVk-(iYh<5{hQBKy?p(s)#K!g{E&Ef(;QTNu}f;RUo#3Gf;ctk zLJ-a}RPB-!<)7RTp{m(H+1h|@dEs^FcX!j~czH$5`YRQ>6bhAsRnNOX$)*s}R*wrD z@%z59d*WcsH(j$jxBhcWC6*iR$qlfAmG0Dbn0>6 z`@wMvcByI2IWv6dUQSF2)qP0w66gMNIfWR@eGOl}$RV11?itEy%9%+|XC)NFbB~NX zPdkYb63B=>K7j$=`}~_>_|0V{o(#Ux*{`$c%(qHK8TsTj}HdUlB3%*>V}H{;X|gX+&!4Syb?`hl%m2v0rPYJ!MR&ClMS zSCMyeMNTn(0avdpknu1S!e4X7|3T`8x3V0oPPZYr&f!5X@h;bC_{^)`Q{z==a6`P> z;<-fq8~3D|JeDNxIhI5xSj4rqSwvm(;?FlDiDY6YahMRezka~wD~MO#132kaDisY@ zwS3SE)uVaPM$yIyCx=g=L}ttVL)I=`CZr|`(M;MxS_Dl@+fK`+mC-Ku84@bL2eg&Z zT1zW$FN{YPLFYCsj6Zk5gl=o>lIB216X^-XKJ+Dnv*qyv2dg&GDZA+plKe~Qm*@}a zP4q50qA{(RW0_N#{>(6DBJ)=!@OtFTi_E*s#?g$;p;9w88a{Ebs#rRQefz**73rHr zyV!;7GweUuf!t;`m20t0Y{%Uoi}ahv{YiGAD9wE&CoeaRdxC3u;=a2>xz5w4=vo)@<}J-MD${hPdJx+2lLU*It@UoAL@4xaU?x zJjyICj`1+}Fz5aP?>O&sZd|`{LHo|t%~7}W00X7;hA@n96+EIWJGx+BjPI|f(MKUAbIsLB6z@F1ft2XCXF3L>)}Vh`S+0|>+c)e zk6zblJ*Qm>0_ z4S8`Ov?}v4c?f9vz~>%*UN0)$KP4xrZ;o~On9b6^66VMh3n6^H$ihf|A$gfWXg#(a zt8oHKCWPDQ3Lv$q8JG)Hfh0|Gx+}5~cyAg&fX@b;BoFxiEFerMrw0L``{J>>xj(>i z8u7Bl4EolfmH=`ON$=qSL5R}W%C*t;u%ifg?%2zg_su*SLCn=oTdr5vX-_hIY(NNv zG60bRx?1aKTlHTz^Ugl&a4@IvUcJ|o^*g@SkglWq>iv~eU8~7<6z@?Ou0K$F6lMBI zEu)FF5h#Eb)*pm-5(mm@UTL%(S_#N8gYe)~bBeY1N185k5Yv%~x|TL+LZB1e5kbnY z2!NiZWB|ns1QmmkQizt2{P#I1Y+fy){6x4=U$L3m?HD_797gG@qoqyp!L)2KG81jc zWF8_`1JNxAZpqo&C^8il{r2!OTM42?FWDL?(JgS4L3 ze~==?079BRp-s`I>X3HD7JJ*>yBZC6Zi!eRDGH#;AVD1hqYo+xj5eSnqnLJ$pq8*w zedGip7!)_kvyMJgT^(E9uS4V4Gi4%Rj<6sm+09hcqIuR~I9?mWbmkx*tY>QUR1g#m zqoHN7h!xf|HISAa+==>t2K|I@dCoz6vM!)u4fDsAoIv7XATg{p>01n6B!XduSp!8* zC%Xt#LF{O+-hYYGGyKXBhyse(tC1X{k)5GT1g+C9(a`UpZ6QDmG#9EQPxw012n}jSUnk7JnrUAqt0xRSJsPF~ z6h=<;+Zc2os@tKDu6vLrfIJ6e*f->jZU&~e z41w(S*Eq3&OajF18}g}729^NG#Baz(KuQ3a@(nQnq`giP-A_atG&iAxb z3y}MOOzTGAKLpbmev5<21f~W)x526d<47vEh(h3oCZmfI!xS znA6~Q5KSlVn$hhVFkYTWNWl8(C+LKkOlChBlEDl|!DRNp_Jl+v1hOzB#z5`!$aT2{ zioy~gTQIi}101So9azC`o?g2Ki|+XgJXQHXfkgK=4ZwZCH57%nLpg2m1OFu_uY?KR2}h%G|NLj?fBv&ekMhDl6)^svb8v${_w-4x>a~BV z9t&Y({yxUn2C(!H720pt-9%ER9(eBLP==y55Yq|y@SfC!bOkkL^2X8zv|8v1a##sa ziUfNTg4!8qY1ey@kc97Rf?z*NFjnjQ#sO{t|mf7&_lCXbx)|3!4NFaL|^ zPJdtBuU$X(-*!FJknFbqv`FYfnCavM{p&^A%xc*AkY5Y%^~d%qHF zE_|NajSK;V64A3&n%0es17tuiq$GSR<_HLBOMBwXhB)4Ue9xyA;s^jCtx!*#=U3os zAORWl4bi%~6FUG12#kD*V+%+*AeP?{4?yk#LjQ&=0i>-LvJntdVXrvJVr(oRIGk zf9;L6fDGw{_(IN(fP61&Eg*AyAsZp*NI>kr<(vy~_5i~8hJ-^)%K`b`((t3(v2%bh zzs1P~aV5OLY#?!d{5O1al(7W84|G-+tv&50HnAV5)R0%UdT>BT6-a9 z0WpvMYw6Dc8Q%*rg>CvzfPAmdY(P?bAsYcX-3u`gZp9jVcK$d<(ptHN}7X^O8jkP;3ZTP7Jp$`OI2zn6oA@qen zu1Zqn#}EPyf)NB`2qqBvLm=H4q`QLjoeY3LuI7Od$khX{%DdMLxk{`ckZXeh!5RWN zpN2xPg+Q)P@(GWet0N$g^JpXl2MFYBC7(S0Zh22?h5zTQg6?)c{YfxrE& z*3*8_dfF{ZfW~zDe|$-;r@Y=;Po$YAyX3E$R^(Qo^>g#*HgF;3f&>-68L|K$7N}WC z4M3*5(-nPg?pBL3(jvQJCYi}!F0#x1XD<$9zXZHXWZNdgXP^Js;{Gpk=Rlf`;6Ceu z8oI#eo*MRYpY{6X|4lzmg_`Y$eiW?!tCtSI|8Fj`hG(H4BYM>^u@asJVb+~)(d>WG z*4~049q_VhYr*p^xLZS8G&Z%wIGTo(UQ&K)??8u*4W(m9;h$yT&KN=5t2}8#yY!4B z8d3Rs@X7KQ$54AtKIJ4;Axfz`bL;*w-MUZpPrK@lEZ^Gxr)gs=_-RXI3)3)hlST-e z7Y8(;u~iKo+E?!l%)AHR>U!Ddne}FqH%;9$*nh&>G5<@ z$EsDj9miI|oh=DABM!*6vGv0aqamy4uiocbH?|J^6p_#|le3XkIC_k3wm^)>Mra%X=3+VA o{6@YrN4|)y#-RV^ja}u!7;fXyq?tX1K}}peeVBl}BlW)i7wSeHApigX literal 0 HcmV?d00001 diff --git a/MDS-800/System_80-10/system_80_10.cfg b/MDS-800/System_80-10/system_80_10.cfg new file mode 100644 index 00000000..d38d3529 --- /dev/null +++ b/MDS-800/System_80-10/system_80_10.cfg @@ -0,0 +1,41 @@ +/* system_80_10.cfg: Intel System 80/10 simulator definitions + + This file holds the configuration for the System 80/10 + boards I/O and Memory. + + Copyright (c) 2010, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + William A. Beech BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + 16 Dec 12 - Original file +*/ + +/* set the base I/O address for the iSBC 208 */ +#define SBC208_BASE 0x40 + +/* configure interrupt request line */ +#define SBC208_INT INT_1 + +/* set the base and size for the iSBC 064 */ +#define SBC064_BASE 0x0000 +#define SBC064_SIZE 0x10000 + diff --git a/MDS-800/System_80-10/system_80_10_sys.c b/MDS-800/System_80-10/system_80_10_sys.c new file mode 100644 index 00000000..21128265 --- /dev/null +++ b/MDS-800/System_80-10/system_80_10_sys.c @@ -0,0 +1,78 @@ +/* system_80_10_sys.c: multibus system interface + + Copyright (c) 2010, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + William A. Beech BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + ?? ??? 10 - Original file. + 16 Dec 12 - Modified to use isbc_80_10.cfg file to set base and size. +*/ + +#include "system_defs.h" + +extern DEVICE i8080_dev; +extern REG i8080_reg[]; +extern DEVICE i8251_dev; +extern DEVICE i8255_dev; +extern DEVICE EPROM_dev; +extern DEVICE RAM_dev; +extern DEVICE multibus_dev; +extern DEVICE isbc208_dev; +extern DEVICE isbc064_dev; + +/* SCP data structures + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words needed for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages +*/ + +char sim_name[] = "Intel System 80/10"; + +REG *sim_PC = &i8080_reg[0]; + +int32 sim_emax = 4; + +DEVICE *sim_devices[] = { + &i8080_dev, + &EPROM_dev, + &RAM_dev, + &i8251_dev, + &i8255_dev, + &multibus_dev, + &isbc064_dev, + &isbc208_dev, + NULL +}; + +const char *sim_stop_messages[] = { + "Unknown error", + "Unknown I/O Instruction", + "HALT instruction", + "Breakpoint", + "Invalid Opcode", + "Invalid Memory", + "XACK Error" +}; + diff --git a/MDS-800/System_80-10/system_defs.h b/MDS-800/System_80-10/system_defs.h new file mode 100644 index 00000000..9ee659da --- /dev/null +++ b/MDS-800/System_80-10/system_defs.h @@ -0,0 +1,81 @@ +/* system_defs.h: Intel iSBC simulator definitions + + Copyright (c) 2010, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + William A. Beech BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + ?? ??? 10 - Original file. + 16 Dec 12 - Modified to use isbc_80_10.cfg file to set base and size. +*/ + +#include +#include +#include "system_80_10.cfg" /* Intel System 80/10 configuration */ +#include "sim_defs.h" /* simulator defns */ + +/* multibus interrupt definitions */ + +#define INT_0 0x01 +#define INT_1 0x02 +#define INT_2 0x04 +#define INT_3 0x08 +#define INT_4 0x10 +#define INT_5 0x20 +#define INT_6 0x40 +#define INT_7 0x80 + +/* CPU interrupts definitions */ + +#define INT_R 0x200 +#define I75 0x40 +#define I65 0x20 +#define I55 0x10 + +/* Memory */ + +#define MAXMEMSIZE 0x10000 /* 8080 max memory size */ +#define MEMSIZE (i8080_unit.capac) /* 8080 actual memory size */ +#define ADDRMASK (MAXMEMSIZE - 1) /* 8080 address mask */ +#define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) + +/* debug definitions */ + +#define DEBUG_flow 0x0001 +#define DEBUG_read 0x0002 +#define DEBUG_write 0x0004 +#define DEBUG_level1 0x0008 +#define DEBUG_level2 0x0010 +#define DEBUG_reg 0x0020 +#define DEBUG_asm 0x0040 +#define DEBUG_xack 0x0080 +#define DEBUG_all 0xFFFF + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_OPCODE 4 /* Invalid Opcode */ +#define STOP_IO 5 /* I/O error */ +#define STOP_MEM 6 /* Memory error */ +#define STOP_XACK 7 /* XACK error */ + diff --git a/MDS-800/common/i8008.c b/MDS-800/common/i8008.c new file mode 100644 index 00000000..6a840d90 --- /dev/null +++ b/MDS-800/common/i8008.c @@ -0,0 +1,2002 @@ +/* i8008.c: Intel 8008 CPU simulator + + Copyright (c) 2011, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + cpu 8008 CPU + + The register state for the 8008 CPU is: + + A<0:7> Accumulator + B<0:7> B Register + C<0:7> C Register + D<0:7> D Register + E<0:7> E Register + H<0:7> H Register + L<0:7> L Register + PC<0:13> Program counter + + The 8008 is an 8-bit CPU, which uses 14-bit registers to address + up to 16KB of memory. + + The 57 basic instructions come in 1, 2, and 3-byte flavors. + + This routine is the instruction decode routine for the 8008. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HLT instruction + I/O error in I/O simulator + Invalid OP code (if ITRAP is set on CPU) + + 2. Interrupts. + There are 8 possible levels of interrupt, and in effect they + do a hardware CALL instruction to one of 8 possible low + memory addresses. + + 3. Non-existent memory. On the 8008, reads to non-existent memory + return 0FFh, and writes are ignored. In the simulator, the + largest possible memory is instantiated and initialized to zero. + Thus, only writes need be checked against actual memory size. + + 4. Adding I/O devices. These modules must be modified: + + 15 Feb 15 - Original file. + +*/ + +#include "system_defs.h" + +#define UNIT_V_OPSTOP (UNIT_V_UF) /* Stop on Invalid OP? */ +#define UNIT_OPSTOP (1 << UNIT_V_OPSTOP) +#define UNIT_V_TRACE (UNIT_V_UF+1) /* Trace switch */ +#define UNIT_TRACE (1 << UNIT_V_TRACE) + +/* register masks */ +#define BYTE_R 0xFF +#define WORD_R14 0x3FFF + +/* storage for the rest of the registers */ +uint32 A = 0; /* accumulator */ +uint32 B = 0; /* B register */ +uint32 C = 0; /* C register */ +uint32 D = 0; /* D register */ +uint32 E = 0; /* E register */ +uint32 H = 0; /* H register */ +uint32 L = 0; /* L register */ +uint32 CF = 0; /* C - carry flag */ +uint32 PF = 0; /* P - parity flag */ +uint32 ZF = 0; /* Z - zero flag */ +uint32 SF = 0; /* S - sign flag */ +uint32 SP = 0; /* stack frame pointer */ +uint32 saved_PC = 0; /* program counter */ +uint32 int_req = 0; /* Interrupt request */ + +int32 PCX; /* External view of PC */ +int32 PC; +UNIT *uptr; + +/* function prototypes */ +void store_m(unit32 val); +unit32 fetch_m(void); +void set_cpuint(int32 int_num); +void dumpregs(void); +int32 fetch_byte(int32 flag); +int32 fetch_word(void); +uint16 pop_word(void); +void push_word(uint16 val); +void setflag4(int32 reg); +void setlogical(int32 reg); +void setinc(int32 reg); +int32 getreg(int32 reg); +void putreg(int32 reg, int32 val); +int32 getpair(int32 reg); +int32 getpush(int32 reg); +void putpush(int32 reg, int32 data); +void putpair(int32 reg, int32 val); +void parity(int32 reg); +int32 cond(int32 con); +t_stat i8008_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat i8008_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat i8008_reset (DEVICE *dptr); + +/* external function prototypes */ + +extern t_stat i8008_reset (DEVICE *dptr); +extern int32 get_mbyte(int32 addr); +extern int32 get_mword(int32 addr); +extern void put_mbyte(int32 addr, int32 val); +extern void put_mword(int32 addr, int32 val); +extern int32 sim_int_char; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ + + +struct idev { + int32 (*routine)(); +}; + +/* This is the I/O configuration table. There are 256 possible +device addresses, if a device is plugged to a port it's routine +address is here, 'nulldev' means no device is available +*/ + +extern struct idev dev_table[]; + +/* CPU data structures + + i8008_dev CPU device descriptor + i8008_unit CPU unit descriptor + i8008_reg CPU register list + i8008_mod CPU modifiers list +*/ + +UNIT i8008_unit = { UDATA (NULL, 0, 65535) }; /* default 8008 */ + +REG i8008_reg[] = { + { HRDATA (PC, saved_PC, 16) }, /* must be first for sim_PC */ + { HRDATA (A, A, 8) }, + { HRDATA (B, B, 8) }, + { HRDATA (C, C, 8) }, + { HRDATA (D, D, 8) }, + { HRDATA (E, E, 8) }, + { HRDATA (H, H, 8) }, + { HRDATA (L, L, 8) }, + { HRDATA (CF, CF, 1) }, + { HRDATA (PF, PF, 1) }, + { HRDATA (ZF, SF, 1) }, + { HRDATA (SF, SF, 1) }, + { HRDATA (INTR, int_req, 32) }, + { HRDATA (WRU, sim_int_char, 8) }, + { NULL } +}; + +MTAB i8008_mod[] = { + { UNIT_OPSTOP, 0, "ITRAP", "ITRAP", NULL }, + { UNIT_OPSTOP, UNIT_OPSTOP, "NOITRAP", "NOITRAP", NULL }, + { UNIT_TRACE, 0, "NOTRACE", "NOTRACE", NULL }, + { UNIT_TRACE, UNIT_TRACE, "TRACE", "TRACE", NULL }, + { 0 } +}; + +DEBTAB i8008_debug[] = { + { "ALL", DEBUG_all }, + { "FLOW", DEBUG_flow }, + { "READ", DEBUG_read }, + { "WRITE", DEBUG_write }, + { "LEV1", DEBUG_level1 }, + { "LEV2", DEBUG_level2 }, + { "REG", DEBUG_reg }, + { "ASM", DEBUG_asm }, + { NULL } +}; + +DEVICE i8008_dev = { + "CPU", //name + &i8008_unit, //units + i8008_reg, //registers + i8008_mod, //modifiers + 1, //numunits + 16, //aradix + 16, //awidth + 1, //aincr + 16, //dradix + 8, //dwidth + &i8008_ex, //examine + &i8008_dep, //deposit +// &i8008_reset, //reset + NULL, //reset + NULL, //boot + NULL, //attach + NULL, //detach + NULL, //ctxt + DEV_DEBUG, //flags + 0, //dctrl + i8008_debug, //debflags + NULL, //msize + NULL //lname +}; + +/* tables for the disassembler */ +char *opcode[] = { +"*HLT", "*HLT", "RLC", "RFC", /* 0x00 */ +"ADI ", "RST 0", "LAI ,", "RET", +"INB", "DCB", "RRC", "RFZ", +"ACI ", "RST 1", "LBI ", "*RET", +"INC", "DCC", "RAL", "RFS", /* 0x10 */ +"SUI ", "RST 2", "LCI ", "*RET", +"IND", "DCD", "RAR", "RFP", +"SBI ", "RST 3", "LDI ", "*RET", +"INE", "DCE", "???", "RTC", /* 0x20 */ +"NDI ", "RST 4", "LEI ", "*RET", +"INH", "DCH", "???", "RTZ", +"XRI ", "RST 5", "LHI ", "*RET", +"INL", "DCL", "???", "RTS", /* 0x30 */ +"ORI ", "RST 6", "LLI ", "*RET", +"???", "???", "???", "RTP", +"CPI ", "RST 7", "LMI ", "*RET", +"JFC ", "INP ", "CFC ", "INP ", /* 0x40 */ +"JMP ", "INP ", "CAL ", "INP ", +"JFZ ", "INP ", "CFZ ", "INP ", +"*JMP ", "INP ", "*CAL ", "INP ", +"JFS", "OUT ", "CFS ", "OUT ", /* 0x50 */ +"*JMP ", "OUT ", "*CAL ", "OUT ", +"JFP ", "OUT ", "CFP ", "OUT ", +"*JMP ", "OUT ", "*CAL ", "OUT ", +"JTC ", "OUT ", "CTC ", "OUT ", /* 0x60 */ +"*JMP ", "OUT ", "*CAL ", "OUT ", +"JTZ ", "OUT ", "CTZ", "OUT ", +"*JMP ", "OUT ", "*CAL", "OUT ", +"JTS ", "OUT ", "CTS ", "OUT ", /* 0x70 */ +"*JMP ", "OUT ", "*CAL ", "OUT ", +"JTP ", "OUT ", "CTP", "OUT ", +"*JMP ", "OUT ", "*CAL ", "OUT ", +"ADA", "ADB", "ADC", "ADD", /* 0x80 */ +"ADE", "ADH", "ADL", "ADM", +"ACA", "ACB", "ACC", "ACD", +"ACE", "ACH", "ACL", "ACM", +"SUA", "SUB", "SUC", "SUD", /* 0x90 */ +"SUE", "SUH", "SUL", "SUM", +"SBA", "SBB", "SBC", "SBD", +"SBE", "SBH", "SBL", "SBM", +"NDA", "NDB", "NDC", "NDD", /* 0xA0 */ +"NDE", "NDH", "NDL", "NDM", +"XRA", "XRB", "XRC", "XRD", +"XRE", "XRH", "XRL", "XRM", +"ORA", "ORB", "ORC", "ORD", /* 0xB0 */ +"ORE", "ORH", "ORL", "ORM", +"CPA", "CPB", "CPC", "CPD", +"CPE", "CPH", "CPL", "CPM", +"NOP", "LAB", "LAC", "LAD", /* 0xC0 */ +"LAE", "LAH", "LAL", "LAM", +"LBA", "LBB", "LBC", "LBD", +"LBE", "LBH ", "LBL", "LBM", +"LCA", "LCB", "LCC", "LCD", /* 0xD0 */ +"LCE", "LCH", "LCL", "LCM", +"LDA", "LDB", "LDC", "LDD", +"LDE", "LDH", "LDL", "LDM", +"LEA", "LEB", "LEC", "LED", /* 0xE0 */ +"LEE", "LEH", "LEL", "LEM", +"LHA", "LHB", "LHC", "LHD", +"LHE", "LHH", "LHL", "LHM", +"LLA", "LLB", "LLC", "LLD", /* 0xF0 */ +"LLE", "LLH", "LLL", "LLM", +"LMA", "LMB", "LMC", "LMD", +"LME", "LMH", "LML", "HLT", + }; + +int32 oplen[256] = { +/* + 0 1 2 3 4 5 6 7 8 9 A B C D E F */ + 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, /* 0X */ + 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, /* 1X */ + 1, 1, 0, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, /* 2X */ + 1, 1, 0, 1, 2, 1, 2, 1, 0, 0, 0, 1, 2, 1, 2, 1, /* 3X */ + 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, /* 4X */ + 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, /* 5X */ + 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, /* 6X */ + 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, /* 7X */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 8X */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 9X */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* AX */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* BX */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* CX */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* DX */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* EX */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* FX */ +}; + +uint16 stack_frame[7]; /* processor stack frame */ + +void set_cpuint(int32 int_num) +{ + int_req |= int_num; +} + +/* instruction simulator */ +int32 sim_instr (void) +{ + extern int32 sim_interval; + uint32 IR, OP, DAR, reason, hi, lo, i, adr, val; + + PC = saved_PC & WORD_R14; /* load local PC */ + reason = 0; + + uptr = i8008_dev.units; + + /* Main instruction fetch/decode loop */ + + while (reason == 0) { /* loop until halted */ + +// if (PC == 0x1000) { /* turn on debugging */ +// i8008_dev.dctrl = DEBUG_asm + DEBUG_reg; +// reason = STOP_HALT; +// } + if (i8008_dev.dctrl & DEBUG_reg) { + dumpregs(); + printf("\n"); + } + + if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event()) + break; + } + sim_interval--; /* countdown clock */ + + if (int_req > 0) { /* interrupt? */ +// printf("\ni8008: int_req=%04X", int_req); + ; + } else { /* 8008 */ + if (IE) { /* enabled? */ + push_word(PC); /* do an RST 7 */ + PC = 0x0038; + int_req &= ~INT_R; +// printf("\ni8008: int_req=%04X", int_req); + } + } /* end interrupt */ + + if (sim_brk_summ && + sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + + PCX = PC; + + if (uptr->flags & UNIT_TRACE) { + dumpregs(); + printf("\n"); + } + IR = OP = fetch_byte(0); /* instruction fetch */ + + if (OP == 0x00 || OP == 0x01 || OP == 0xFF) { /* HLT Instruction*/ + reason = STOP_HALT; + PC--; + continue; + } + + /* The Big Instruction Decode Switch */ + + switch (IR) { + + case 0x02: /* RLC */ + if (A & 0x80) + CF = 1; + else + CF = 0; + A = (A << 1) & 0xFF; + if (CF) + A |= 0x01; + A &= BYTE_R; + break; + + case 0x03: /* RFC */ + if (CF) + ; + else + PC = pop_word(); + break; + + case 0x04: /* ADI */ + A += fetch_byte(1); + setflag3(A); + A &= BYTE_R; + break; + + case 0x05: /* RST 0 */ + case 0x0D: /* RST 1 */ + case 0x15: /* RST 2 */ + case 0x1D: /* RST 3 */ + case 0x25: /* RST 4 */ + case 0x2D: /* RST 5 */ + case 0x35: /* RST 6 */ + case 0x3D: /* RST 7 */ + val = fetch_byte(); + push_word(PC); + PC = val << 3; + break; + + case 0x06: /* LAI */ + A = fetch_byte(1); + A &= BYTE_R; + break; + + case 0x07: /* RET */ + PC = pop_word(); + break; + + case 0x08: /* INB */ + B++; + setflag3(B); + B &= BYTE_R; + break; + + case 0x09: /* DCB */ + B--; + setflag3(B); + B &= BYTE_R; + break; + + case 0x0A: /* RRC */ + if (A & 0x01) + CF = 1; + else + CF = 0; + A = (A >> 1) & 0xFF; + if (CF) + A |= 0x80; + A &= BYTE_R; + break; + + case 0x0B: /* RFZ */ + if (ZF) + ; + else + PC = pop_word(); + break; + + case 0x0C: /* ACI */ + A += fetch_byte(1); + if (CF) + A++; + setflag3(A); + A &= BYTE_R; + break; + + case 0x0E: /* LBI */ + B = fetch_byte(1); + B &= BYTE_R; + break; + + case 0x0F: /* *RET */ + PC = pop_word(); + break; + + case 0x10: /* INC */ + C++; + setflag3(C); + C &= BYTE_R; + break; + + case 0x11: /* DCC */ + C--; + setflag3(C); + C &= BYTE_R; + break; + + case 0x12: /* RAL */ + if (A & 0x80) + CF = 1; + else + CF = 0; + A = (A << 1) & 0xFF; + if (CF) + A |= 0x01; + A &= BYTE_R; + break; + + case 0x13: /* RFS */ + if (SF) + ; + else + PC = pop_word(); + break; + + case 0x14: /* SUI */ + A -= fetch_byte(1); + setflag3(A); + A &= BYTE_R; + break; + + case 0x16: /* LCI */ + C = fetch_byte(1); + C &= BYTE_R; + break; + + case 0x17: /* *RET */ + PC = pop_word(); + break; + + case 0x18: /* IND */ + D++; + setflag3(D); + D &= BYTE_R; + break; + + case 0x19: /* DCD */ + D--; + setflag3(D); + D &= BYTE_R; + break; + + case 0x1A: /* RAR */ + if (A & 0x01) + CF = 1; + else + CF = 0; + A = (A >> 1) & 0xFF; + if (CF) + A |= 0x80; + A &= BYTE_R; + break; + + case 0x1B: /* RFP */ + if (PF) + ; + else + PC = pop_word(); + break; + + case 0x1C: /* SBI */ + A -= fetch_byte(1); + if (CF) + A--; + setflag3(A); + A &= BYTE_R; + break; + + case 0x1E: /* LDI */ + D = fetch_byte(1); + D &= BYTE_R; + break; + + case 0x1F: /* *RET */ + PC = pop_word(); + break; + + case 0x20: /* INE */ + E++; + setflag3(E); + E &= BYTE_R; + break; + + case 0x21: /* DCE */ + E--; + setflag3(E); + E &= BYTE_R; + break; + + case 0x23: /* RTC */ + if (CF) + PC = pop_word(); + break; + + case 0x24: /* NDI */ + A &= fetch_byte(1); + setflag3(A); + A &= BYTE_R; + break; + + case 0x26: /* LEI */ + E = fetch_byte(1); + E &= BYTE_R; + break; + + case 0x27: /* *RET */ + PC = pop_word(); + break; + + case 0x28: /* INH */ + H++; + setflag3(H); + H &= BYTE_R; + break; + + case 0x29: /* DCH */ + H--; + setflag3(H); + H &= BYTE_R; + break; + + case 0x2B: /* RTZ */ + if (ZF) + PC = pop_word(); + break; + + case 0x2C: /* XRI */ + A ^= fetch_byte(1); + setflag3(A); + break; + + case 0x2E: /* LHI */ + H = fetch_byte(1); + H &= BYTE_R; + break; + + case 0x2F: /* *RET */ + PC = pop_word(); + break; + + case 0x30: /* INL */ + L++; + setflag3(L); + L &= BYTE_R; + break; + + case 0x31: /* DCL */ + L--; + setflag3(L); + L &= BYTE_R; + break; + + case 0x33: /* RTS */ + if (SF) + PC = pop_word(); + break; + + case 0x34: /* ORI */ + A |= fetch_byte(1); + setflag3(A); + A &= BYTE_R; + break; + + case 0x36: /* LLI */ + L = fetch_byte(1); + L &= BYTE_R; + break; + + case 0x37: /* *RET */ + PC = pop_word(); + break; + + case 0x3B: /* RTP */ + if (PF) + PC = pop_word(); + break; + + case 0x3C: /* CPI */ + DAR = A; + DAR -= fetch_byte(1); + setflag3(DAR); + break; + + case 0x3E: /* LMI */ + val = fetch_byte(1); + store_m(val); + break; + + case 0x3F: /* *RET */ + PC = pop_word(); + break; + + case 0x40: /* JFC */ + DAR = fetch_word(); + if (CF) + ; + else + PC = DAR; + break; + + case 0x41: /* INP 0 */ + case 0x43: /* INP 1 */ + case 0x45: /* INP 2 */ + case 0x47: /* INP 3 */ + case 0x49: /* INP 4 */ + case 0x4B: /* INP 5 */ + case 0x4D: /* INP 6 */ + case 0x4F: /* INP 7 */ + /**** fix me! */ + break; + + case 0x42: /* CFC */ + adr = fetch_word(); + if (CF) + ; + else { + push_word(PC); + PC = adr; + } + break; + + case 0x44: /* JMP */ + PC = fetch_word(); + break; + + case 0x46: /* CAL */ + adr = fetch_word(); + push_word(PC); + PC = adr; + break; + + case 0x48: /* JFZ */ + DAR = fetch_word(); + if (ZF) + ; + else + PC = DAR; + break; + + case 0x4A: /* CFZ */ + adr = fetch_word(); + if (ZF) + ; + else { + push_word(PC); + PC = adr; + } + break; + + case 0x4C: /* *JMP */ + PC = fetch_word(); + break; + + case 0x4E: /* *CAL */ + adr = fetch_word(); + push_word(PC); + PC = adr; + break; + + case 0x50: /* JFS */ + DAR = fetch_word(); + if (SF) + ; + else + PC = DAR; + break; + + case 0x51: /* OUT 8 */ + case 0x53: /* OUT 9 */ + case 0x55: /* OUT 10 */ + case 0x57: /* OUT 11 */ + case 0x59: /* OUT 12 */ + case 0x5B: /* OUT 13 */ + case 0x5D: /* OUT 14 */ + case 0x5E: /* OUT 15 */ + case 0x61: /* OUT 16 */ + case 0x63: /* OUT 17 */ + case 0x65: /* OUT 18 */ + case 0x67: /* OUT 19 */ + case 0x69: /* OUT 20 */ + case 0x6B: /* OUT 21 */ + case 0x6D: /* OUT 22 */ + case 0x6E: /* OUT 23 */ + case 0x71: /* OUT 24 */ + case 0x73: /* OUT 25 */ + case 0x75: /* OUT 26 */ + case 0x77: /* OUT 27 */ + case 0x79: /* OUT 28 */ + case 0x7B: /* OUT 29 */ + case 0x7D: /* OUT 30 */ + case 0x7E: /* OUT 31 */ + /**** fix me! */ + break; + + case 0x52: /* CFS */ + adr = fetch_word(); + if (SF) + ; + else { + push_word(PC); + PC = adr; + } + break; + + case 0x54: /* *JMP */ + PC = fetch_word(); + break; + + case 0x56: /* *CAL */ + adr = fetch_word(); + push_word(PC); + PC = adr; + break; + + case 0x58: /* JFP */ + DAR = fetch_word(); + if (PF) + ; + else + PC = DAR; + break; + + case 0x5A: /* CFP */ + adr = fetch_word(); + if (PF) + ; + else { + push_word(PC); + PC = adr; + } + break; + + case 0x5C: /* *JMP */ + PC = fetch_word(); + break; + + case 0x5E: /* *CAL */ + adr = fetch_word(); + push_word(PC); + PC = adr; + break; + + case 0x60: /* JTC */ + DAR = fetch_word(); + if (CF) + PC = DAR; + break; + + case 0x62: /* CTC */ + adr = fetch_word(); + if (CF) { + push_word(PC); + PC = adr; + } + break; + + case 0x64: /* *JMP */ + PC = fetch_word(); + break; + + case 0x66: /* *CAL */ + adr = fetch_word(); + push_word(PC); + PC = adr; + break; + + case 0x68: /* JTZ */ + DAR = fetch_word(); + if (ZF) + PC = DAR; + break; + + case 0x6A: /* CTZ */ + adr = fetch_word(); + if (ZF) { + push_word(PC); + PC = adr; + } + break; + + case 0x6C: /* *JMP */ + PC = fetch_word(); + break; + + case 0x6E: /* *CAL */ + adr = fetch_word(); + push_word(PC); + PC = adr; + break; + + case 0x70: /* JTS */ + DAR = fetch_word(); + if (SF) + PC = DAR; + break; + + case 0x72: /* CTS */ + adr = fetch_word(); + if (SF) { + push_word(PC); + PC = adr; + } + break; + + case 0x74: /* *JMP */ + PC = fetch_word(); + break; + + case 0x76: /* *CAL */ + adr = fetch_word(); + push_word(PC); + PC = adr; + break; + + case 0x78: /* JTP */ + DAR = fetch_word(); + if (PF) + PC = DAR; + break; + + case 0x7A: /* CTP */ + adr = fetch_word(); + if (PF) { + push_word(PC); + PC = adr; + } + break; + + case 0x7C: /* *JMP */ + PC = fetch_word(); + break; + + case 0x7E: /* *CAL */ + adr = fetch_word(); + push_word(PC); + PC = adr; + break; + + case 0x80: /* ADA */ + A += A; + setflag4(A); + A &= BYTE_R; + break; + + case 0x81: /* ADB */ + A += B; + setflag4(A); + A &= BYTE_R; + break; + + case 0x82: /* ADC */ + A += C; + setflag4(A); + A &= BYTE_R; + break; + + case 0x83: /* ADD */ + A += D; + setflag4(A); + A &= BYTE_R; + break; + + case 0x84: /* ADE */ + A += E; + setflag4(A); + A &= BYTE_R; + break; + + case 0x85: /* ADH */ + A += H; + setflag4(A); + A &= BYTE_R; + break; + + case 0x86: /* ADL */ + A += L; + setflag4(A); + A &= BYTE_R; + break; + + case 0x87: /* ADM */ + A += fetch_m(); + setflag4(A); + A &= BYTE_R; + break; + + case 0x88: /* ACA */ + A += A; + if (CF) + A++; + setflag4(A); + A &= BYTE_R; + break; + + case 0x89: /* ACB */ + A += B; + if (CF) + A++; + setflag4(A); + A &= BYTE_R; + break; + + case 0x8A: /* ACC */ + A += C; + if (CF) + A++; + setflag4(A); + A &= BYTE_R; + break; + + case 0x8B: /* ACD */ + A += D; + if (CF) + A++; + setflag4(A); + A &= BYTE_R; + break; + + case 0x8C: /* ACE */ + A += E; + if (CF) + A++; + setflag4(A); + A &= BYTE_R; + break; + + case 0x8D: /* ACH */ + A += H; + if (CF) + A++; + setflag4(A); + A &= BYTE_R; + break; + + case 0x8E: /* ACL */ + A += L; + if (CF) + A++; + setflag4(A); + A &= BYTE_R; + break; + + case 0x8F: /* ACM */ + A += fetch_m(); + if (CF) + A++; + setflag4(A); + A &= BYTE_R; + break; + + case 0x90: /* SUA */ + A -= A; + setflag4(A); + A &= BYTE_R; + break; + + case 0x91: /* SUB */ + A -= B; + setflag4(A); + A &= BYTE_R; + break; + + case 0x92: /* SUC */ + A -= C; + setflag4(A); + A &= BYTE_R; + break; + + case 0x93: /* SUD */ + A -= D; + setflag4(A); + A &= BYTE_R; + break; + + case 0x94: /* SUE */ + A -= E; + setflag4(A); + A &= BYTE_R; + break; + + case 0x95: /* SUH */ + A -= H; + setflag4(A); + A &= BYTE_R; + break; + + case 0x96: /* SUL */ + A -= L; + setflag4(A); + A &= BYTE_R; + break; + + case 0x97: /* SUM */ + A -= fetch_m(); + setflag4(A); + A &= BYTE_R; + break; + + case 0x98: /* SBA */ + A -= A; + if (CF) + A--; + setflag4(A); + A &= BYTE_R; + break; + + case 0x99: /* SBB */ + A -= B; + if (CF) + A--; + setflag4(A); + A &= BYTE_R; + break; + + case 0x9A: /* SBC */ + A -= C; + if (CF) + A--; + setflag4(A); + A &= BYTE_R; + break; + + case 0x9B: /* SBD */ + A -= D; + if (CF) + A--; + setflag4(A); + A &= BYTE_R; + break; + + case 0x9C: /* SBE */ + A -= E; + if (CF) + A--; + setflag4(A); + A &= BYTE_R; + break; + + case 0x9D: /* SBH */ + A -= H; + if (CF) + A--; + setflag4(A); + A &= BYTE_R; + break; + + case 0x9E: /* SBL */ + A -= L; + if (CF) + A--; + setflag4(A); + A &= BYTE_R; + break; + + case 0x9F: /* SBM */ + A -= fetch_m(); + if (CF) + A - ; + setflag4(A); + A &= BYTE_R; + break; + + case 0xA0: /* NDA */ + A &= A; + setflag4(A); + A &= BYTE_R; + break; + + case 0xA1: /* NDB */ + A &= B; + setflag4(A); + A &= BYTE_R; + break; + + case 0xA2: /* NDC */ + A &= C; + setflag4(A); + A &= BYTE_R; + break; + + case 0xA3: /* NDD */ + A &= D; + setflag4(A); + A &= BYTE_R; + break; + + case 0xA4: /* NDE */ + A &= E; + setflag4(A); + A &= BYTE_R; + break; + + case 0xA5: /* NDH */ + A &= H; + setflag4(A); + A &= BYTE_R; + break; + + case 0xA6: /* NDL */ + A &= L; + setflag4(A); + A &= BYTE_R; + break; + + case 0xA7: /* NDM */ + A &= fetch_m(); + setflag4(A); + A &= BYTE_R; + break; + + case 0xA8: /* XRA */ + A ^= A; + setflag4(A); + A &= BYTE_R; + break; + + case 0xA9: /* XRB */ + A ^= B; + setflag4(A); + A &= BYTE_R; + break; + + case 0xAA: /* XRC */ + A ^= C; + setflag4(A); + A &= BYTE_R; + break; + + case 0xAB: /* XRD */ + A ^= D; + setflag4(A); + A &= BYTE_R; + break; + + case 0xAC: /* XRE */ + A ^= E; + setflag4(A); + A &= BYTE_R; + break; + + case 0xAD: /* XRH */ + A ^= H; + setflag4(A); + A &= BYTE_R; + break; + + case 0xAE: /* XRL */ + A ^= L; + setflag4(A); + A &= BYTE_R; + break; + + case 0xAF: /* XRM */ + A |= fetch_m(); + setflag4(A); + A &= BYTE_R; + break; + + case 0xB0: /* ORA */ + A |= A; + setflag4(A); + A &= BYTE_R; + break; + + case 0xB1: /* ORB */ + A |= B; + setflag4(A); + A &= BYTE_R; + break; + + case 0xB2: /* ORC */ + A |= C; + setflag4(A); + A &= BYTE_R; + break; + + case 0xB3: /* ORD */ + A |= D; + setflag4(A); + A &= BYTE_R; + break; + + case 0xB4: /* ORE */ + A |= E; + setflag4(A); + A &= BYTE_R; + break; + + case 0xB5: /* ORH */ + A |= H; + setflag4(A); + A &= BYTE_R; + break; + + case 0xB6: /* ORL */ + A |= L; + setflag4(A); + A &= BYTE_R; + break; + + case 0xB7: /* ORM */ + A |= fetch_m(); + setflag4(A); + A &= BYTE_R; + break; + + case 0xB8: /* CPA */ + DAR -= A; + setflag4(DAR); + A &= BYTE_R; + break; + + case 0xB9: /* CPB */ + DAR -= B; + setflag4(DAR); + A &= BYTE_R; + break; + + case 0xBA: /* CPC */ + DAR -= C; + setflag4(DAR); + A &= BYTE_R; + break; + + case 0xBB: /* CPD */ + DAR -= D; + setflag4(DAR); + A &= BYTE_R; + break; + + case 0xBC: /* CPE */ + DAR -= E; + setflag4(DAR); + A &= BYTE_R; + break; + + case 0xBD: /* CPH */ + DAR -= H; + setflag4(DAR); + A &= BYTE_R; + break; + + case 0xBE: /* CPL */ + DAR -= L; + setflag4(DAR); + A &= BYTE_R; + break; + + case 0xBF: /* CPM */ + DAR -= fetch_m(); + setflag4(DAR); + A &= BYTE_R; + break; + + case 0xC0: /* NOP */ + break; + + case 0xC1: /* LAB */ + A = B; + A &= BYTE_R; + break; + + case 0xC2: /* LAC */ + A = C; + A &= BYTE_R; + break; + + case 0xC3: /* LAD */ + A = D; + A &= BYTE_R; + break; + + case 0xC4: /* LAE */ + A = E; + A &= BYTE_R; + break; + + case 0xC5: /* LAH */ + A = H; + A &= BYTE_R; + break; + + case 0xC6: /* LAL */ + A = L; + A &= BYTE_R; + break; + + case 0xC7: /* LAM */ + A = FETCH_M(); + A &= BYTE_R; + break; + + case 0xC8: /* LBA */ + B = A; + B &= BYTE_R; + break; + + case 0xC9: /* LBB */ + B = B; + B &= BYTE_R; + break; + + case 0xCA: /* LBC */ + B = C; + B &= BYTE_R; + break; + + case 0xCB: /* LBD */ + B = D; + B &= BYTE_R; + break; + + case 0xCC: /* LBE */ + B = E; + B &= BYTE_R; + break; + + case 0xCD: /* LBH */ + B = H; + B &= BYTE_R; + break; + + case 0xCE: /* LBL */ + B = L; + B &= BYTE_R; + break; + + case 0xCF: /* LBM */ + B = FETCH_M(); + B &= BYTE_R; + break; + + case 0xD0: /* LCA */ + C = A; + C &= BYTE_R; + break; + + case 0xD1: /* LCB */ + C = B; + C &= BYTE_R; + break; + + case 0xD2: /* LCC */ + C = C; + C &= BYTE_R; + break; + + case 0xD3: /* LCD */ + C = D; + C &= BYTE_R; + break; + + case 0xD4: /* LCE */ + C = E; + C &= BYTE_R; + break; + + case 0xD5: /* LCH */ + C = H; + C &= BYTE_R; + break; + + case 0xD6: /* LCL */ + C = L; + C &= BYTE_R; + break; + + case 0xD7: /* LCM */ + C = FETCH_M(); + C &= BYTE_R; + break; + + case 0xD8: /* LDA */ + D = A; + D &= BYTE_R; + break; + + case 0xD9: /* LDB */ + D = B; + D &= BYTE_R; + break; + + case 0xDA: /* LDC */ + D = C; + D &= BYTE_R; + break; + + case 0xDB: /* LDD */ + D = D; + D &= BYTE_R; + break; + + case 0xDC: /* LDE */ + D = E; + D &= BYTE_R; + break; + + case 0xDD: /* LDH */ + D = H; + D &= BYTE_R; + break; + + case 0xDE: /* LDL */ + D = L; + D &= BYTE_R; + break; + + case 0xDF: /* LDM */ + D = FETCH_M(); + D &= BYTE_R; + break; + + case 0xE0: /* LEA */ + E = A; + E &= BYTE_R; + break; + + case 0xE1: /* LEB */ + E = B; + E &= BYTE_R; + break; + + case 0xE2: /* LEC */ + E = C; + E &= BYTE_R; + break; + + case 0xE3: /* LED */ + E = D; + E &= BYTE_R; + break; + + case 0xE4: /* LEE */ + E = E; + E &= BYTE_R; + break; + + case 0xE5: /* LEH */ + E = H; + E &= BYTE_R; + break; + + case 0xE6: /* LEL */ + E = L; + E &= BYTE_R; + break; + + case 0xE7: /* LEM */ + E = FETCH_M(); + E &= BYTE_R; + break; + + case 0xE8: /* LHA */ + H = A; + H &= BYTE_R; + break; + + case 0xE9: /* LHB */ + H = B; + H &= BYTE_R; + break; + + case 0xEA: /* LHC */ + H = C; + H &= BYTE_R; + break; + + case 0xEB: /* LHD */ + H = D; + H &= BYTE_R; + break; + + case 0xEC: /* LHE */ + H = E; + H &= BYTE_R; + break; + + case 0xED: /* LHH */ + H = H; + H &= BYTE_R; + break; + + case 0xEE: /* LHL */ + H = L; + H &= BYTE_R; + break; + + case 0xEF: /* LHM */ + H = FETCH_M(); + H &= BYTE_R; + break; + + case 0xF0: /* LLA */ + L = A; + L &= BYTE_R; + break; + + case 0xF1: /* LLB */ + L = B; + L &= BYTE_R; + break; + + case 0xF2: /* LLC */ + L = C; + L &= BYTE_R; + break; + + case 0xF3: /* LLD */ + L = D; + L &= BYTE_R; + break; + + case 0xF4: /* LLE */ + L = E; + L &= BYTE_R; + break; + + case 0xF5: /* LLH */ + L = H; + L &= BYTE_R; + break; + + case 0xF6: /* LLL */ + L = L; + L &= BYTE_R; + break; + + case 0xF7: /* LLM */ + L = FETCH_M(); + L &= BYTE_R; + break; + + case 0xF8: /* LMA */ + store_m(A); + break; + + case 0xF9: /* LMB */ + store_m(B); + break; + + case 0xFA: /* LMC */ + store_m(C); + break; + + case 0xFB: /* LMD */ + store_m(D); + break; + + case 0xFC: /* LME */ + store_m(E); + break; + + case 0xFD: /* LMH */ + store_m(H); + break; + + case 0xFE: /* LML */ + store_m(L); + break; + + case 0xFF: /* LMM */ + val = FETCH_M(); + store_m(val); + break; + + default: /* undefined opcode */ + if (i8008_unit.flags & UNIT_OPSTOP) { + reason = STOP_OPCODE; + PC--; + } + break; + } + } + +/* Simulation halted */ + + saved_PC = PC; + return reason; +} + +/* store byte to (HL) */ +void store_m(uint32 val) +{ + DAR = (H << 8) + L; + DAR &= WORD_R14; + ret get_mword(DAR); +} + +/* get byte from (HL) */ +uint32 fetch_m(void) +{ + DAR = (H << 8) + L; + DAR &= WORD_R14; + put_mword(DAR, val); +} + +/* dump the registers */ +void dumpregs(void) +{ + printf(" A=%02X B=%02X C=%02X D=%04X E=%02X H=%04X L=%02X\n", + A, B, C, D, E, H, L); + printf(" CF=%d ZF=%d SF=%d PF=%d\n", + CF, ZF, SF, PF); +} + +/* fetch an instruction or byte */ +int32 fetch_byte(int32 flag) +{ + uint32 val; + + val = get_mbyte(PC) & 0xFF; /* fetch byte */ + if (i8008_dev.dctrl & DEBUG_asm || uptr->flags & UNIT_TRACE) { /* display source code */ + switch (flag) { + case 0: /* opcode fetch */ + printf("OP=%02X %04X %s", val, PC, opcode[val]); + break; + case 1: /* byte operand fetch */ + printf("0%02XH", val); + break; + } + } + PC = (PC + 1) & ADDRMASK; /* increment PC */ + val &= BYTE_R; + return val; +} + +/* fetch a word */ +int32 fetch_word(void) +{ + uint16 val; + + val = get_mbyte(PC) & BYTE_R; /* fetch low byte */ + val |= get_mbyte(PC + 1) << 8; /* fetch high byte */ + if (i8008_dev.dctrl & DEBUG_asm || uptr->flags & UNIT_TRACE) /* display source code */ + printf("0%04XH", val); + PC = (PC + 2) & ADDRMASK; /* increment PC */ + val &= WORD_R14; + return val; +} + +/* push a word to the stack frame */ +void push_word(uint16 val) +{ + stack_frame[SP] = val; + SP++; + if (SP == 8) + SP = 0; +} + +/* pop a word from the stack frame */ +uint16 pop_word(void) +{ + SP--; + if (SP < 0) + SP = 7; + return stack_frame[SP]; +} + + +/* Set the arry, ign, ero and verflow flags following + an operation on 'reg'. +*/ + +void setflag4(int32 reg) +{ + if (reg & 0x100) + CF = 1; + else + CF = 0; + if (reg & 0x80) + SF = 0; + else + SF = 1; + if ((reg & BYTE_R) == 0) + ZF = 1; + else + ZF = 0; + parity(reg); +} + +/* Set the arry, ign and ero flags following + an operation on 'reg'. +*/ + +void setflag3(int32 reg) +{ + CF = 0; + if (reg & 0x80) + SF = 0; + else + SF = 1; + if ((reg & BYTE_R) == 0) + ZF = 1; + else + ZF = 0; + parity(reg); +} + +/* Set the Parity (PF) flag based on parity of 'reg', i.e., number +of bits on even: PF=1, else PF=0 +*/ + +void parity(int32 reg) +{ + int32 bc = 0; + + if (reg & 0x01) bc++; + if (reg & 0x02) bc++; + if (reg & 0x04) bc++; + if (reg & 0x08) bc++; + if (reg & 0x10) bc++; + if (reg & 0x20) bc++; + if (reg & 0x40) bc++; + if (reg & 0x80) bc++; + if (bc & 0x01) + PF = 0; + else + PF = 1; +} + + + +/* Reset routine */ + +t_stat i8008_reset (DEVICE *dptr) +{ + int i; + + CF = SF = ZF = PF = 0; + saved_PC = 0; + int_req = 0; + for (i = 0; i < 7; i++) + stack_frame[i] = 0; + sim_brk_types = sim_brk_dflt = SWMASK ('E'); + printf(" 8008: Reset\n"); + return SCPE_OK; +} + +/* Memory examine */ + +t_stat i8008_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ + if (addr >= MEMSIZE) + return SCPE_NXM; + if (vptr != NULL) + *vptr = get_mbyte(addr); + return SCPE_OK; +} + +/* Memory deposit */ + +t_stat i8008_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ + if (addr >= MEMSIZE) + return SCPE_NXM; + put_mbyte(addr, val); + return SCPE_OK; +} + +/* This is the binary loader. The input file is considered to be + a string of literal bytes with no special format. The load + starts at the current value of the PC. +*/ + +int32 sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ + int32 i, addr = 0, cnt = 0; + + if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; + addr = saved_PC; + while ((i = getc (fileref)) != EOF) { + put_mbyte(addr, i); + addr++; + cnt++; + } /* end while */ + printf ("%d Bytes loaded.\n", cnt); + return (SCPE_OK); +} + +/* Symbolic output + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + status = error code +*/ + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ + int32 cflag, c1, c2, inst, adr; + + cflag = (uptr == NULL) || (uptr == &i8008_unit); + c1 = (val[0] >> 8) & 0x7F; + c2 = val[0] & 0x7F; + if (sw & SWMASK ('A')) { + fprintf (of, (c2 < 0x20)? "<%02X>": "%c", c2); + return SCPE_OK; + } + if (sw & SWMASK ('C')) { + fprintf (of, (c1 < 0x20)? "<%02X>": "%c", c1); + fprintf (of, (c2 < 0x20)? "<%02X>": "%c", c2); + return SCPE_OK; + } + if (!(sw & SWMASK ('M'))) return SCPE_ARG; + inst = val[0]; + fprintf (of, "%s", opcode[inst]); + if (oplen[inst] == 2) { + if (strchr(opcode[inst], ' ') != NULL) + fprintf (of, ","); + else fprintf (of, " "); + fprintf (of, "%h", val[1]); + } + if (oplen[inst] == 3) { + adr = val[1] & 0xFF; + adr |= (val[2] << 8) & 0xff00; + if (strchr(opcode[inst], ' ') != NULL) + fprintf (of, ","); + else fprintf (of, " "); + fprintf (of, "%h", adr); + } + return -(oplen[inst] - 1); +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ + int32 cflag, i = 0, j, r; + char gbuf[CBUFSIZE]; + + cflag = (uptr == NULL) || (uptr == &i8008_unit); + while (isspace (*cptr)) cptr++; /* absorb spaces */ + if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (uint32) cptr[0]; + return SCPE_OK; + } + if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = ((uint32) cptr[0] << 8) + (uint32) cptr[1]; + return SCPE_OK; + } + +/* An instruction: get opcode (all characters until null, comma, + or numeric (including spaces). +*/ + + while (1) { + if (*cptr == ',' || *cptr == '\0' || + isdigit(*cptr)) + break; + gbuf[i] = toupper(*cptr); + cptr++; + i++; + } + +/* Allow for RST which has numeric as part of opcode */ + + if (toupper(gbuf[0]) == 'R' && + toupper(gbuf[1]) == 'S' && + toupper(gbuf[2]) == 'T') { + gbuf[i] = toupper(*cptr); + cptr++; + i++; + } + +/* kill trailing spaces if any */ + gbuf[i] = '\0'; + for (j = i - 1; gbuf[j] == ' '; j--) { + gbuf[j] = '\0'; + } + +/* find opcode in table */ + for (j = 0; j < 256; j++) { + if (strcmp(gbuf, opcode[j]) == 0) + break; + } + if (j > 255) /* not found */ + return SCPE_ARG; + + val[0] = j; /* store opcode */ + if (oplen[j] < 2) /* if 1-byter we are done */ + return SCPE_OK; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, 0); /* get address */ + sscanf(gbuf, "%o", &r); + if (oplen[j] == 2) { + val[1] = r & 0xFF; + return (-1); + } + val[1] = r & 0xFF; + val[2] = (r >> 8) & 0xFF; + return (-2); +} diff --git a/MDS-800/common/i8080.c b/MDS-800/common/i8080.c new file mode 100644 index 00000000..890fbc6a --- /dev/null +++ b/MDS-800/common/i8080.c @@ -0,0 +1,1438 @@ +/* i8080.c: Intel 8080/8085 CPU simulator + + Copyright (c) 1997-2005, Charles E. Owen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + CHARLES E. OWEN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. + + This software was modified by Bill Beech, Nov 2010, to allow emulation of Intel + iSBC Single Board Computers. + + Copyright (c) 2011, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + cpu 8080 CPU + + 08 Oct 02 RMS Tied off spurious compiler warnings + 23 Nov 10 WAB Modified for iSBC emulation + 04 Dec 12 WAB Added 8080 interrupts + 14 Dec 12 WAB Added 8085 interrupts + + The register state for the 8080 CPU is: + + A<0:7> Accumulator + BC<0:15> BC Register Pair + DE<0:15> DE Register Pair + HL<0:15> HL Register Pair + PSW<0:7> Program Status Word (Flags) + PC<0:15> Program counter + SP<0:15> Stack Pointer + + The 8080 is an 8-bit CPU, which uses 16-bit registers to address + up to 64KB of memory. + + The 78 basic instructions come in 1, 2, and 3-byte flavors. + + This routine is the instruction decode routine for the 8080. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + I/O error in I/O simulator + Invalid OP code (if ITRAP is set on CPU) + + 2. Interrupts. + There are 8 possible levels of interrupt, and in effect they + do a hardware CALL instruction to one of 8 possible low + memory addresses. + + 3. Non-existent memory. On the 8080, reads to non-existent memory + return 0FFh, and writes are ignored. In the simulator, the + largest possible memory is instantiated and initialized to zero. + Thus, only writes need be checked against actual memory size. + + 4. Adding I/O devices. These modules must be modified: + + altair_cpu.c add I/O service routines to dev_table + altair_sys.c add pointer to data structures in sim_devices + + ?? ??? 11 - Original file. + 16 Dec 12 - Modified to use isbc_80_10.cfg file to set base and size. + 20 Dec 12 - Modified for basic interrupt function. + 03 Mar 13 - Added trace function. + 04 Mar 13 - Modified all instructions to truncate the affected register + at the end of the routine. + 17 Mar 13 - Modified to enable/disable trace based on start and stop + addresses. + +*/ + +#include "system_defs.h" + +#define UNIT_V_OPSTOP (UNIT_V_UF) /* Stop on Invalid OP? */ +#define UNIT_OPSTOP (1 << UNIT_V_OPSTOP) +#define UNIT_V_8085 (UNIT_V_UF+1) /* 8080/8085 switch */ +#define UNIT_8085 (1 << UNIT_V_8085) +#define UNIT_V_TRACE (UNIT_V_UF+2) /* Trace switch */ +#define UNIT_TRACE (1 << UNIT_V_TRACE) + +/* Flag values to set proper positions in PSW */ +#define CF 0x01 +#define PF 0x04 +#define AF 0x10 +#define ZF 0x40 +#define SF 0x80 + +/* Macros to handle the flags in the PSW + 8080 has bit #1 always set. This is (not well) documented behavior. */ +#define PSW_ALWAYS_ON (0x02) /* for 8080 */ +#define PSW_MSK (CF|PF|AF|ZF|SF) +#define TOGGLE_FLAG(FLAG) (PSW ^= FLAG) +#define SET_FLAG(FLAG) (PSW |= FLAG) +#define CLR_FLAG(FLAG) (PSW &= ~FLAG) +#define GET_FLAG(FLAG) (PSW & FLAG) +#define COND_SET_FLAG(COND,FLAG) \ + if (COND) SET_FLAG(FLAG); else CLR_FLAG(FLAG) + +#define SET_XACK(VAL) (xack = VAL) +#define GET_XACK(FLAG) (xack &= FLAG) + +/* values for IM bits */ +#define ITRAP 0x100 +#define SID 0x80 +#define SOD 0x80 +#define SDE 0x40 +#define R75 0x10 +#define IE 0x08 +#define MSE 0x08 +#define M75 0x04 +#define M65 0x02 +#define M55 0x01 + +/* register masks */ +#define BYTE_R 0xFF +#define WORD_R 0xFFFF + +/* storage for the rest of the registers */ +uint32 PSW = 0; /* program status word */ +uint32 A = 0; /* accumulator */ +uint32 BC = 0; /* BC register pair */ +uint32 DE = 0; /* DE register pair */ +uint32 HL = 0; /* HL register pair */ +uint32 SP = 0; /* Stack pointer */ +uint32 saved_PC = 0; /* program counter */ +uint32 IM = 0; /* Interrupt Mask Register */ +uint32 xack = 0; /* XACK signal */ +uint32 int_req = 0; /* Interrupt request */ + +int32 PCX; /* External view of PC */ +int32 PC; +UNIT *uptr; + +/* function prototypes */ +void set_cpuint(int32 int_num); +void dumpregs(void); +int32 fetch_byte(int32 flag); +int32 fetch_word(void); +uint16 pop_word(void); +void push_word(uint16 val); +void setarith(int32 reg); +void setlogical(int32 reg); +void setinc(int32 reg); +int32 getreg(int32 reg); +void putreg(int32 reg, int32 val); +int32 getpair(int32 reg); +int32 getpush(int32 reg); +void putpush(int32 reg, int32 data); +void putpair(int32 reg, int32 val); +void parity(int32 reg); +int32 cond(int32 con); +t_stat i8080_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat i8080_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat i8080_reset (DEVICE *dptr); + +/* external function prototypes */ + +extern t_stat i8080_reset (DEVICE *dptr); +extern int32 get_mbyte(int32 addr); +extern int32 get_mword(int32 addr); +extern void put_mbyte(int32 addr, int32 val); +extern void put_mword(int32 addr, int32 val); +extern int32 sim_int_char; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ + + +struct idev { + int32 (*routine)(); +}; + +/* This is the I/O configuration table. There are 256 possible +device addresses, if a device is plugged to a port it's routine +address is here, 'nulldev' means no device is available +*/ + +extern struct idev dev_table[]; + +/* CPU data structures + + i8080_dev CPU device descriptor + i8080_unit CPU unit descriptor + i8080_reg CPU register list + i8080_mod CPU modifiers list +*/ + +UNIT i8080_unit = { UDATA (NULL, 0, 65535) }; /* default 8080 */ + +REG i8080_reg[] = { + { HRDATA (PC, saved_PC, 16) }, /* must be first for sim_PC */ + { HRDATA (PSW, PSW, 8) }, + { HRDATA (A, A, 8) }, + { HRDATA (BC, BC, 16) }, + { HRDATA (DE, DE, 16) }, + { HRDATA (HL, HL, 16) }, + { HRDATA (SP, SP, 16) }, + { HRDATA (IM, IM, 8) }, + { HRDATA (XACK, xack, 8) }, + { HRDATA (INTR, int_req, 32) }, + { HRDATA (WRU, sim_int_char, 8) }, + { NULL } +}; + +MTAB i8080_mod[] = { + { UNIT_8085, 0, "8080", "8080", NULL }, + { UNIT_8085, UNIT_8085, "8085", "8085", NULL }, + { UNIT_OPSTOP, 0, "ITRAP", "ITRAP", NULL }, + { UNIT_OPSTOP, UNIT_OPSTOP, "NOITRAP", "NOITRAP", NULL }, + { UNIT_TRACE, 0, "NOTRACE", "NOTRACE", NULL }, + { UNIT_TRACE, UNIT_TRACE, "TRACE", "TRACE", NULL }, + { 0 } +}; + +DEBTAB i8080_debug[] = { + { "ALL", DEBUG_all }, + { "FLOW", DEBUG_flow }, + { "READ", DEBUG_read }, + { "WRITE", DEBUG_write }, + { "LEV1", DEBUG_level1 }, + { "LEV2", DEBUG_level2 }, + { "REG", DEBUG_reg }, + { "ASM", DEBUG_asm }, + { NULL } +}; + +DEVICE i8080_dev = { + "CPU", //name + &i8080_unit, //units + i8080_reg, //registers + i8080_mod, //modifiers + 1, //numunits + 16, //aradix + 16, //awidth + 1, //aincr + 16, //dradix + 8, //dwidth + &i8080_ex, //examine + &i8080_dep, //deposit +// &i8080_reset, //reset + NULL, //reset + NULL, //boot + NULL, //attach + NULL, //detach + NULL, //ctxt + DEV_DEBUG, //flags + 0, //dctrl + i8080_debug, //debflags + NULL, //msize + NULL //lname +}; + +/* tables for the disassembler */ +char *opcode[] = { +"NOP", "LXI B,", "STAX B", "INX B", /* 0x00 */ +"INR B", "DCR B", "MVI B,", "RLC", +"???", "DAD B", "LDAX B", "DCX B", +"INR C", "DCR C", "MVI C,", "RRC", +"???", "LXI D,", "STAX D", "INX D", /* 0x10 */ +"INR D", "DCR D", "MVI D,", "RAL", +"???", "DAD D", "LDAX D", "DCX D", +"INR E", "DCR E", "MVI E,", "RAR", +"RIM", "LXI H,", "SHLD ", "INX H", /* 0x20 */ +"INR H", "DCR H", "MVI H,", "DAA", +"???", "DAD H", "LHLD ", "DCX H", +"INR L", "DCR L", "MVI L", "CMA", +"SIM", "LXI SP,", "STA ", "INX SP", /* 0x30 */ +"INR M", "DCR M", "MVI M,", "STC", +"???", "DAD SP", "LDA ", "DCX SP", +"INR A", "DCR A", "MVI A,", "CMC", +"MOV B,B", "MOV B,C", "MOV B,D", "MOV B,E", /* 0x40 */ +"MOV B,H", "MOV B,L", "MOV B,M", "MOV B,A", +"MOV C,B", "MOV C,C", "MOV C,D", "MOV C,E", +"MOV C,H", "MOV C,L", "MOV C,M", "MOV C,A", +"MOV D,B", "MOV D,C", "MOV D,D", "MOV D,E", /* 0x50 */ +"MOV D,H", "MOV D,L", "MOV D,M", "MOV D,A", +"MOV E,B", "MOV E,C", "MOV E,D", "MOV E,E", +"MOV E,H", "MOV E,L", "MOV E,M", "MOV E,A", +"MOV H,B", "MOV H,C", "MOV H,D", "MOV H,E", /* 0x60 */ +"MOV H,H", "MOV H,L", "MOV H,M", "MOV H,A", +"MOV L,B", "MOV L,C", "MOV L,D", "MOV L,E", +"MOV L,H", "MOV L,L", "MOV L,M", "MOV L,A", +"MOV M,B", "MOV M,C", "MOV M,D", "MOV M,E", /* 0x70 */ +"MOV M,H", "MOV M,L", "HLT", "MOV M,A", +"MOV A,B", "MOV A,C", "MOV A,D", "MOV A,E", +"MOV A,H", "MOV A,L", "MOV A,M", "MOV A,A", +"ADD B", "ADD C", "ADD D", "ADD E", /* 0x80 */ +"ADD H", "ADD L", "ADD M", "ADD A", +"ADC B", "ADC C", "ADC D", "ADC E", +"ADC H", "ADC L", "ADC M", "ADC A", +"SUB B", "SUB C", "SUB D", "SUB E", /* 0x90 */ +"SUB H", "SUB L", "SUB M", "SUB A", +"SBB B", "SBB C", "SBB D", "SBB E", +"SBB H", "SBB L", "SBB M", "SBB A", +"ANA B", "ANA C", "ANA D", "ANA E", /* 0xA0 */ +"ANA H", "ANA L", "ANA M", "ANA A", +"XRA B", "XRA C", "XRA D", "XRA E", +"XRA H", "XRA L", "XRA M", "XRA A", +"ORA B", "ORA C", "ORA D", "ORA E", /* 0xB0 */ +"ORA H", "ORA L", "ORA M", "ORA A", +"CMP B", "CMP C", "CMP D", "CMP E", +"CMP H", "CMP L", "CMP M", "CMP A", +"RNZ", "POP B", "JNZ ", "JMP ", /* 0xC0 */ +"CNZ ", "PUSH B", "ADI ", "RST 0", +"RZ", "RET", "JZ ", "???", +"CZ ", "CALL ", "ACI ", "RST 1", +"RNC", "POP D", "JNC ", "OUT ", /* 0xD0 */ +"CNC ", "PUSH D", "SUI ", "RST 2", +"RC", "???", "JC ", "IN ", +"CC ", "???", "SBI ", "RST 3", +"RPO", "POP H", "JPO ", "XTHL", /* 0xE0 */ +"CPO ", "PUSH H", "ANI ", "RST 4", +"RPE", "PCHL", "JPE ", "XCHG", +"CPE ", "???", "XRI ", "RST 5", +"RP", "POP PSW", "JP ", "DI", /* 0xF0 */ +"CP ", "PUSH PSW", "ORI ", "RST 6", +"RM", "SPHL", "JM ", "EI", +"CM ", "???", "CPI ", "RST 7", + }; + +int32 oplen[256] = { +1,3,1,1,1,1,2,1,0,1,1,1,1,1,2,1, +0,3,1,1,1,1,2,1,0,1,1,1,1,1,2,1, +1,3,3,1,1,1,2,1,0,1,3,1,1,1,2,1, +1,3,3,1,1,1,2,1,0,1,3,1,1,1,2,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,3,3,3,1,2,1,1,1,3,0,3,3,2,1, +1,1,3,2,3,1,2,1,1,0,3,2,3,0,2,1, +1,1,3,1,3,1,2,1,1,1,3,1,3,0,2,1, +1,1,3,1,3,1,2,1,1,1,3,1,3,0,2,1 }; + +void set_cpuint(int32 int_num) +{ + int_req |= int_num; +} + +//FILE *fpd; + +/* instruction simulator */ +int32 sim_instr (void) +{ + extern int32 sim_interval; + uint32 IR, OP, DAR, reason, hi, lo, i, adr; + + PC = saved_PC & WORD_R; /* load local PC */ + reason = 0; + + uptr = i8080_dev.units; + if (i8080_dev.dctrl & DEBUG_flow) { + if (uptr->flags & UNIT_8085) + printf("CPU = 8085\n"); + else + printf("CPU = 8080\n"); + } + /* Main instruction fetch/decode loop */ + + while (reason == 0) { /* loop until halted */ + +// if (PC == 0x1000) { /* turn on debugging */ +// i8080_dev.dctrl = DEBUG_asm + DEBUG_reg; +// reason = STOP_HALT; +// } + if (i8080_dev.dctrl & DEBUG_reg) { + dumpregs(); + printf("\n"); + } + + if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event()) + break; + } + sim_interval--; /* countdown clock */ + + if (int_req > 0) { /* interrupt? */ +// printf("\ni8080: int_req=%04X IM=%04X", int_req, IM); + if (uptr->flags & UNIT_8085) { /* 8085 */ + if (int_req & ITRAP) { /* int */ + push_word(PC); + PC = 0x0024; + int_req &= ~ITRAP; + } else if (IM & IE) { + if (int_req & I75 && IM & M75) { /* int 7.5 */ + push_word(PC); + PC = 0x003C; + int_req &= ~I75; + } else if (int_req & I65 && IM & M65) { /* int 6.5 */ + push_word(PC); + PC = 0x0034; + int_req &= ~I65; + } else if (int_req & I55 && IM & M55) { /* int 5.5 */ + push_word(PC); + PC = 0x002C; + int_req &= ~I55; + } else if (int_req & INT_R) { /* intr */ + push_word(PC); /* do an RST 7 */ + PC = 0x0038; + int_req &= ~INT_R; + } + } + } else { /* 8080 */ + if (IM & IE) { /* enabled? */ + push_word(PC); /* do an RST 7 */ + PC = 0x0038; + int_req &= ~INT_R; +// printf("\ni8080: int_req=%04X", int_req); + } + } + } /* end interrupt */ + + if (sim_brk_summ && + sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + + PCX = PC; + +// fprintf(fpd, "%04X\n", PC); +// fflush(fpd); + + if (uptr->flags & UNIT_TRACE) { + dumpregs(); + printf("\n"); + } + IR = OP = fetch_byte(0); /* instruction fetch */ + + if (GET_XACK(1) == 0) { /* no XACK for instruction fetch */ + reason = STOP_XACK; + printf("Stopped for XACK-1 PC=%04X\n", --PC); + continue; + } + + if (OP == 0x76) { /* HLT Instruction*/ + reason = STOP_HALT; + PC--; + continue; + } + + /* Handle below all operations which refer to registers or + register pairs. After that, a large switch statement + takes care of all other opcodes */ + + if ((OP & 0xC0) == 0x40) { /* MOV */ + DAR = getreg(OP & 0x07); + putreg((OP >> 3) & 0x07, DAR); + goto loop_end; + } + + if ((OP & 0xC7) == 0x06) { /* MVI */ + putreg((OP >> 3) & 0x07, fetch_byte(1)); + goto loop_end; + } + + if ((OP & 0xCF) == 0x01) { /* LXI */ + DAR = fetch_word(); + putpair((OP >> 4) & 0x03, DAR); + goto loop_end; + } + + if ((OP & 0xEF) == 0x0A) { /* LDAX */ + DAR = getpair((OP >> 4) & 0x03); + putreg(7, get_mbyte(DAR)); + goto loop_end; + } + + if ((OP & 0xEF) == 0x02) { /* STAX */ + DAR = getpair((OP >> 4) & 0x03); + put_mbyte(DAR, getreg(7)); + goto loop_end; + } + + if ((OP & 0xF8) == 0xB8) { /* CMP */ + DAR = A & 0xFF; + DAR -= getreg(OP & 0x07); + setarith(DAR); + DAR &= BYTE_R; + goto loop_end; + } + + if ((OP & 0xC7) == 0xC2) { /* JMP */ + adr = fetch_word(); + if (cond((OP >> 3) & 0x07)) + PC = adr; + goto loop_end; + } + + if ((OP & 0xC7) == 0xC4) { /* CALL */ + adr = fetch_word(); + if (cond((OP >> 3) & 0x07)) { + push_word(PC); + PC = adr; + } + goto loop_end; + } + + if ((OP & 0xC7) == 0xC0) { /* RET */ + if (cond((OP >> 3) & 0x07)) { + PC = pop_word(); + } + goto loop_end; + } + + if ((OP & 0xC7) == 0xC7) { /* RST */ + push_word(PC); + PC = OP & 0x38; + goto loop_end; + } + + if ((OP & 0xCF) == 0xC5) { /* PUSH */ + DAR = getpush((OP >> 4) & 0x03); + push_word(DAR); + goto loop_end; + } + + if ((OP & 0xCF) == 0xC1) { /* POP */ + DAR = pop_word(); + putpush((OP >> 4) & 0x03, DAR); + goto loop_end; + } + + if ((OP & 0xF8) == 0x80) { /* ADD */ + A += getreg(OP & 0x07); + setarith(A); + A &= BYTE_R; + goto loop_end; + } + + if ((OP & 0xF8) == 0x88) { /* ADC */ + A += getreg(OP & 0x07); + if (GET_FLAG(CF)) + A++; + setarith(A); + A &= BYTE_R; + goto loop_end; + } + + if ((OP & 0xF8) == 0x90) { /* SUB */ + A -= getreg(OP & 0x07); + setarith(A); + A &= BYTE_R; + goto loop_end; + } + + if ((OP & 0xF8) == 0x98) { /* SBB */ + A -= getreg(OP & 0x07); + if (GET_FLAG(CF)) + A--; + setarith(A); + A &= BYTE_R; + goto loop_end; + } + + if ((OP & 0xC7) == 0x04) { /* INR */ + DAR = getreg((OP >> 3) & 0x07); + DAR++; + setinc(DAR); +// DAR &= BYTE_R; + putreg((OP >> 3) & 0x07, DAR); + goto loop_end; + } + + if ((OP & 0xC7) == 0x05) { /* DCR */ + DAR = getreg((OP >> 3) & 0x07); + DAR--; + setinc(DAR); +// DAR &= BYTE_R; + putreg((OP >> 3) & 0x07, DAR); + goto loop_end; + } + + if ((OP & 0xCF) == 0x03) { /* INX */ + DAR = getpair((OP >> 4) & 0x03); + DAR++; +// DAR &= WORD_R; + putpair((OP >> 4) & 0x03, DAR); + goto loop_end; + } + + if ((OP & 0xCF) == 0x0B) { /* DCX */ + DAR = getpair((OP >> 4) & 0x03); + DAR--; +// DAR &= WORD_R; + putpair((OP >> 4) & 0x03, DAR); + goto loop_end; + } + + if ((OP & 0xCF) == 0x09) { /* DAD */ + HL += getpair((OP >> 4) & 0x03); + COND_SET_FLAG(HL & 0x10000, CF); + HL &= WORD_R; + goto loop_end; + } + + if ((OP & 0xF8) == 0xA0) { /* ANA */ + A &= getreg(OP & 0x07); + setlogical(A); + goto loop_end; + } + + if ((OP & 0xF8) == 0xA8) { /* XRA */ + A ^= getreg(OP & 0x07); + setlogical(A); + A &= BYTE_R; + goto loop_end; + } + + if ((OP & 0xF8) == 0xB0) { /* ORA */ + A |= getreg(OP & 0x07); + setlogical(A); + goto loop_end; + } + + /* The Big Instruction Decode Switch */ + + switch (IR) { + + /* 8085 instructions only */ + case 0x20: /* RIM */ + if (i8080_unit.flags & UNIT_8085) { /* 8085 */ + A = IM; + } else { /* 8080 */ + reason = STOP_OPCODE; + PC--; + } + break; + + case 0x30: /* SIM */ + if (i8080_unit.flags & UNIT_8085) { /* 8085 */ + if (A & MSE) { + IM &= 0xF8; + IM |= A & 0x07; + } + if (A & I75) { /* reset RST 7.5 FF */ + } + } else { /* 8080 */ + reason = STOP_OPCODE; + PC--; + } + break; + + /* Logical instructions */ + + case 0xFE: /* CPI */ + DAR = A; + DAR -= fetch_byte(1); +// DAR &= BYTE_R; + setarith(DAR); + break; + + case 0xE6: /* ANI */ + A &= fetch_byte(1); + setlogical(A); + break; + + case 0xEE: /* XRI */ + A ^= fetch_byte(1); +// DAR &= BYTE_R; + setlogical(A); + break; + + case 0xF6: /* ORI */ + A |= fetch_byte(1); + setlogical(A); + break; + + /* Jump instructions */ + + case 0xC3: /* JMP */ + PC = fetch_word(); + break; + + case 0xE9: /* PCHL */ + PC = HL; + break; + + case 0xCD: /* CALL */ + adr = fetch_word(); + push_word(PC); + PC = adr; + break; + + case 0xC9: /* RET */ + PC = pop_word(); + break; + + /* Data Transfer Group */ + + case 0x32: /* STA */ + DAR = fetch_word(); + DAR &= WORD_R; + put_mbyte(DAR, A); + break; + + case 0x3A: /* LDA */ + DAR = fetch_word(); + DAR &= WORD_R; + A = get_mbyte(DAR); + break; + + case 0x22: /* SHLD */ + DAR = fetch_word(); + DAR &= WORD_R; + put_mword(DAR, HL); + break; + + case 0x2A: /* LHLD */ + DAR = fetch_word(); + DAR &= WORD_R; + HL = get_mword(DAR); + break; + + case 0xEB: /* XCHG */ + DAR = HL; + HL = DE; + HL &= WORD_R; + DE = DAR; + DE &= WORD_R; + break; + + /* Arithmetic Group */ + + case 0xC6: /* ADI */ + A += fetch_byte(1); + setarith(A); + A &= BYTE_R; + break; + + case 0xCE: /* ACI */ + A += fetch_byte(1); + if (GET_FLAG(CF)) + A++; + setarith(A); + A &= BYTE_R; + break; + + case 0xD6: /* SUI */ + A -= fetch_byte(1); + setarith(A); + A &= BYTE_R; + break; + + case 0xDE: /* SBI */ + A -= fetch_byte(1); + if (GET_FLAG(CF)) + A--; + setarith(A); + A &= BYTE_R; + break; + + case 0x27: /* DAA */ + DAR = A & 0x0F; + if (DAR > 9 || GET_FLAG(AF)) { + DAR += 6; + A &= 0xF0; + A |= DAR & 0x0F; + COND_SET_FLAG(DAR & 0x10, AF); + } + DAR = (A >> 4) & 0x0F; + if (DAR > 9 || GET_FLAG(AF)) { + DAR += 6; + if (GET_FLAG(AF)) DAR++; + A &= 0x0F; + A |= (DAR << 4); + } + COND_SET_FLAG(DAR & 0x10, CF); + COND_SET_FLAG(A & 0x80, SF); + COND_SET_FLAG((A & 0xFF) == 0, ZF); + parity(A); + break; + + case 0x07: /* RLC */ + COND_SET_FLAG(A & 0x80, CF); + A = (A << 1) & 0xFF; + if (GET_FLAG(CF)) + A |= 0x01; + A &= BYTE_R; + break; + + case 0x0F: /* RRC */ + COND_SET_FLAG(A & 0x01, CF); + A = (A >> 1) & 0xFF; + if (GET_FLAG(CF)) + A |= 0x80; + A &= BYTE_R; + break; + + case 0x17: /* RAL */ + DAR = GET_FLAG(CF); + COND_SET_FLAG(A & 0x80, CF); + A = (A << 1) & 0xFF; + if (DAR) + A |= 0x01; + A &= BYTE_R; + break; + + case 0x1F: /* RAR */ + DAR = GET_FLAG(CF); + COND_SET_FLAG(A & 0x01, CF); + A = (A >> 1) & 0xFF; + if (DAR) + A |= 0x80; + A &= BYTE_R; + break; + + case 0x2F: /* CMA */ + A = ~A; + A &= BYTE_R; + break; + + case 0x3F: /* CMC */ + TOGGLE_FLAG(CF); + break; + + case 0x37: /* STC */ + SET_FLAG(CF); + break; + + /* Stack, I/O & Machine Control Group */ + + case 0x00: /* NOP */ + break; + + case 0xE3: /* XTHL */ + DAR = pop_word(); + push_word(HL); + HL = DAR; + HL &= WORD_R; + break; + + case 0xF9: /* SPHL */ + SP = HL; + break; + + case 0xFB: /* EI */ + IM |= IE; +// printf("\nEI: pc=%04X", PC - 1); + break; + + case 0xF3: /* DI */ + IM &= ~IE; +// printf("\nDI: pc=%04X", PC - 1); + break; + + case 0xDB: /* IN */ + DAR = fetch_byte(1); + A = dev_table[DAR].routine(0, 0); + A &= BYTE_R; + break; + + case 0xD3: /* OUT */ + DAR = fetch_byte(1); + dev_table[DAR].routine(1, A); + break; + + default: /* undefined opcode */ + if (i8080_unit.flags & UNIT_OPSTOP) { + reason = STOP_OPCODE; + PC--; + } + break; + } +loop_end: + if (GET_XACK(1) == 0) { /* no XACK for instruction fetch */ + reason = STOP_XACK; + printf("Stopped for XACK-2 PC=%04X\n", --PC); + continue; + } + + } + +/* Simulation halted */ + + saved_PC = PC; +// fclose(fpd); + return reason; +} + +/* dump the registers */ +void dumpregs(void) +{ + printf(" A=%02X BC=%04X DE=%04X HL=%04X SP=%04X IM=%02X XACK=%d\n", + A, BC, DE, HL, SP, IM, xack); + printf(" CF=%d ZF=%d AF=%d SF=%d PF=%d\n", + GET_FLAG(CF) ? 1 : 0, + GET_FLAG(ZF) ? 1 : 0, + GET_FLAG(AF) ? 1 : 0, + GET_FLAG(SF) ? 1 : 0, + GET_FLAG(PF) ? 1 : 0); +} +/* fetch an instruction or byte */ +int32 fetch_byte(int32 flag) +{ + uint32 val; + + val = get_mbyte(PC) & 0xFF; /* fetch byte */ + if (i8080_dev.dctrl & DEBUG_asm || uptr->flags & UNIT_TRACE) { /* display source code */ + switch (flag) { + case 0: /* opcode fetch */ + printf("OP=%02X %04X %s", val, PC, opcode[val]); + break; + case 1: /* byte operand fetch */ + printf("0%02XH", val); + break; + } + } + PC = (PC + 1) & ADDRMASK; /* increment PC */ + val &= BYTE_R; + return val; +} + +/* fetch a word */ +int32 fetch_word(void) +{ + uint16 val; + + val = get_mbyte(PC) & BYTE_R; /* fetch low byte */ + val |= get_mbyte(PC + 1) << 8; /* fetch high byte */ + if (i8080_dev.dctrl & DEBUG_asm || uptr->flags & UNIT_TRACE) /* display source code */ + printf("0%04XH", val); + PC = (PC + 2) & ADDRMASK; /* increment PC */ + val &= WORD_R; + return val; +} + +/* push a word to the stack */ +void push_word(uint16 val) +{ + SP--; + put_mbyte(SP, (val >> 8)); + SP--; + put_mbyte(SP, val & 0xFF); +} + +/* pop a word from the stack */ +uint16 pop_word(void) +{ + register uint16 res; + + res = get_mbyte(SP); + SP++; + res |= get_mbyte(SP) << 8; + SP++; + return res; +} + +/* Test an 8080 flag condition and return 1 if true, 0 if false */ +int32 cond(int32 con) +{ + switch (con) { + case 0: /* NZ */ + if (GET_FLAG(ZF) == 0) return 1; + break; + case 1: /* Z */ + if (GET_FLAG(ZF)) return 1; + break; + case 2: /* NC */ + if (GET_FLAG(CF) == 0) return 1; + break; + case 3: /* C */ + if (GET_FLAG(CF)) return 1; + break; + case 4: /* PO */ + if (GET_FLAG(PF) == 0) return 1; + break; + case 5: /* PE */ + if (GET_FLAG(PF)) return 1; + break; + case 6: /* P */ + if (GET_FLAG(SF) == 0) return 1; + break; + case 7: /* M */ + if (GET_FLAG(SF)) return 1; + break; + default: + break; + } + return 0; +} + +/* Set the arry, ign, ero and

arity flags following + an arithmetic operation on 'reg'. +*/ + +void setarith(int32 reg) +{ + COND_SET_FLAG(reg & 0x100, CF); + COND_SET_FLAG(reg & 0x80, SF); + COND_SET_FLAG((reg & BYTE_R) == 0, ZF); + CLR_FLAG(AF); + parity(reg); +} + +/* Set the arry, ign, ero amd

arity flags following + a logical (bitwise) operation on 'reg'. +*/ + +void setlogical(int32 reg) +{ + CLR_FLAG(CF); + COND_SET_FLAG(reg & 0x80, SF); + COND_SET_FLAG((reg & BYTE_R) == 0, ZF); + CLR_FLAG(AF); + parity(reg); +} + +/* Set the Parity (P) flag based on parity of 'reg', i.e., number + of bits on even: P=0200000, else P=0 +*/ + +void parity(int32 reg) +{ + int32 bc = 0; + + if (reg & 0x01) bc++; + if (reg & 0x02) bc++; + if (reg & 0x04) bc++; + if (reg & 0x08) bc++; + if (reg & 0x10) bc++; + if (reg & 0x20) bc++; + if (reg & 0x40) bc++; + if (reg & 0x80) bc++; + if (bc & 0x01) + CLR_FLAG(PF); + else + SET_FLAG(PF); +} + +/* Set the ign, ero amd

arity flags following + an INR/DCR operation on 'reg'. +*/ + +void setinc(int32 reg) +{ + COND_SET_FLAG(reg & 0x80, SF); + COND_SET_FLAG((reg & BYTE_R) == 0, ZF); + parity(reg); +} + +/* Get an 8080 register and return it */ +int32 getreg(int32 reg) +{ + switch (reg) { + case 0: /* reg B */ + // printf("reg=%04X BC=%04X ret=%04X\n", + // reg, BC, (BC >>8) & 0xff); + return ((BC >>8) & BYTE_R); + case 1: /* reg C */ + return (BC & BYTE_R); + case 2: /* reg D */ + return ((DE >>8) & BYTE_R); + case 3: /* reg E */ + return (DE & BYTE_R); + case 4: /* reg H */ + return ((HL >>8) & BYTE_R); + case 5: /* reg L */ + return (HL & BYTE_R); + case 6: /* reg M */ + return (get_mbyte(HL)); + case 7: /* reg A */ + return (A); + default: + break; + } + return 0; +} + +/* Put a value into an 8-bit 8080 register from memory */ +void putreg(int32 reg, int32 val) +{ + switch (reg) { + case 0: /* reg B */ +// printf("reg=%04X val=%04X\n", reg, val); + BC = BC & BYTE_R; +// printf("BC&0x00ff=%04X val<<8=%04X\n", BC, val<<8); + BC = BC | (val <<8); + break; + case 1: /* reg C */ + BC = BC & 0xFF00; + BC = BC | val; + break; + case 2: /* reg D */ + DE = DE & BYTE_R; + DE = DE | (val <<8); + break; + case 3: /* reg E */ + DE = DE & 0xFF00; + DE = DE | val; + break; + case 4: /* reg H */ + HL = HL & BYTE_R; + HL = HL | (val <<8); + break; + case 5: /* reg L */ + HL = HL & 0xFF00; + HL = HL | val; + break; + case 6: /* reg M */ + put_mbyte(HL, val); + break; + case 7: /* reg A */ + A = val & BYTE_R; + default: + break; + } +} + +/* Return the value of a selected register pair */ +int32 getpair(int32 reg) +{ + switch (reg) { + case 0: /* reg BC */ + return (BC); + case 1: /* reg DE */ + return (DE); + case 2: /* reg HL */ + return (HL); + case 3: /* reg SP */ + return (SP); + default: + break; + } + return 0; +} + +/* Return the value of a selected register pair, in PUSH + format where 3 means A & flags, not SP */ +int32 getpush(int32 reg) +{ + int32 stat; + + switch (reg) { + case 0: /* reg BC */ + return (BC); + case 1: /* reg DE */ + return (DE); + case 2: /* reg HL */ + return (HL); + case 3: /* reg (A << 8) | PSW */ + stat = A << 8 | PSW; + return (stat); + default: + break; + } + return 0; +} + + +/* Place data into the indicated register pair, in PUSH + format where 3 means A& flags, not SP */ +void putpush(int32 reg, int32 data) +{ + switch (reg) { + case 0: /* reg BC */ + BC = data; + break; + case 1: /* reg DE */ + DE = data; + break; + case 2: /* reg HL */ + HL = data; + break; + case 3: /* reg (A << 8) | PSW */ + A = (data >> 8) & BYTE_R; + PSW = data & BYTE_R; + break; + default: + break; + } +} + + +/* Put a value into an 8080 register pair */ +void putpair(int32 reg, int32 val) +{ + switch (reg) { + case 0: /* reg BC */ + BC = val; + break; + case 1: /* reg DE */ + DE = val; + break; + case 2: /* reg HL */ + HL = val; + break; + case 3: /* reg SP */ + SP = val; + break; + default: + break; + } +} + +/* Reset routine */ + +t_stat i8080_reset (DEVICE *dptr) +{ + PSW = PSW_ALWAYS_ON; + CLR_FLAG(CF); + CLR_FLAG(ZF); + saved_PC = 0; + int_req = 0; + IM = 0; + sim_brk_types = sim_brk_dflt = SWMASK ('E'); + printf(" 8080: Reset\n"); +// fpd = fopen("trace.txt", "w"); + return SCPE_OK; +} + +/* Memory examine */ + +t_stat i8080_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ + if (addr >= MEMSIZE) + return SCPE_NXM; + if (vptr != NULL) + *vptr = get_mbyte(addr); + return SCPE_OK; +} + +/* Memory deposit */ + +t_stat i8080_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ + if (addr >= MEMSIZE) + return SCPE_NXM; + put_mbyte(addr, val); + return SCPE_OK; +} + +/* This is the binary loader. The input file is considered to be + a string of literal bytes with no special format. The load + starts at the current value of the PC. +*/ + +int32 sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ + int32 i, addr = 0, cnt = 0; + + if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; + addr = saved_PC; + while ((i = getc (fileref)) != EOF) { + put_mbyte(addr, i); + addr++; + cnt++; + } /* end while */ + printf ("%d Bytes loaded.\n", cnt); + return (SCPE_OK); +} + +/* Symbolic output + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + status = error code +*/ + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ + int32 cflag, c1, c2, inst, adr; + + cflag = (uptr == NULL) || (uptr == &i8080_unit); + c1 = (val[0] >> 8) & 0x7F; + c2 = val[0] & 0x7F; + if (sw & SWMASK ('A')) { + fprintf (of, (c2 < 0x20)? "<%02X>": "%c", c2); + return SCPE_OK; + } + if (sw & SWMASK ('C')) { + fprintf (of, (c1 < 0x20)? "<%02X>": "%c", c1); + fprintf (of, (c2 < 0x20)? "<%02X>": "%c", c2); + return SCPE_OK; + } + if (!(sw & SWMASK ('M'))) return SCPE_ARG; + inst = val[0]; + fprintf (of, "%s", opcode[inst]); + if (oplen[inst] == 2) { + if (strchr(opcode[inst], ' ') != NULL) + fprintf (of, ","); + else fprintf (of, " "); + fprintf (of, "%h", val[1]); + } + if (oplen[inst] == 3) { + adr = val[1] & 0xFF; + adr |= (val[2] << 8) & 0xff00; + if (strchr(opcode[inst], ' ') != NULL) + fprintf (of, ","); + else fprintf (of, " "); + fprintf (of, "%h", adr); + } + return -(oplen[inst] - 1); +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ + int32 cflag, i = 0, j, r; + char gbuf[CBUFSIZE]; + + cflag = (uptr == NULL) || (uptr == &i8080_unit); + while (isspace (*cptr)) cptr++; /* absorb spaces */ + if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (uint32) cptr[0]; + return SCPE_OK; + } + if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = ((uint32) cptr[0] << 8) + (uint32) cptr[1]; + return SCPE_OK; + } + +/* An instruction: get opcode (all characters until null, comma, + or numeric (including spaces). +*/ + + while (1) { + if (*cptr == ',' || *cptr == '\0' || + isdigit(*cptr)) + break; + gbuf[i] = toupper(*cptr); + cptr++; + i++; + } + +/* Allow for RST which has numeric as part of opcode */ + + if (toupper(gbuf[0]) == 'R' && + toupper(gbuf[1]) == 'S' && + toupper(gbuf[2]) == 'T') { + gbuf[i] = toupper(*cptr); + cptr++; + i++; + } + +/* Allow for 'MOV' which is only opcode that has comma in it. */ + + if (toupper(gbuf[0]) == 'M' && + toupper(gbuf[1]) == 'O' && + toupper(gbuf[2]) == 'V') { + gbuf[i] = toupper(*cptr); + cptr++; + i++; + gbuf[i] = toupper(*cptr); + cptr++; + i++; + } + +/* kill trailing spaces if any */ + gbuf[i] = '\0'; + for (j = i - 1; gbuf[j] == ' '; j--) { + gbuf[j] = '\0'; + } + +/* find opcode in table */ + for (j = 0; j < 256; j++) { + if (strcmp(gbuf, opcode[j]) == 0) + break; + } + if (j > 255) /* not found */ + return SCPE_ARG; + + val[0] = j; /* store opcode */ + if (oplen[j] < 2) /* if 1-byter we are done */ + return SCPE_OK; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, 0); /* get address */ + sscanf(gbuf, "%o", &r); + if (oplen[j] == 2) { + val[1] = r & 0xFF; + return (-1); + } + val[1] = r & 0xFF; + val[2] = (r >> 8) & 0xFF; + return (-2); +} diff --git a/MDS-800/common/i8088.c b/MDS-800/common/i8088.c new file mode 100644 index 00000000..3a7bf3bd --- /dev/null +++ b/MDS-800/common/i8088.c @@ -0,0 +1,4746 @@ +/* i8088.c: Intel 8086/8088 CPU simulator + + Copyright (C) 1991 Jim Hudgens + + The file is part of GDE. + + GDE is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + GDE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GDE; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + This software was modified by Bill Beech, Mar 2011, from the software GDE + of Jim Hudgens as provided with the SIMH AltairZ80 emulation package. + I modified it to allow emulation of Intel iSBC Single Board Computers. + + Copyright (c) 2011, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + cpu 8088 CPU + + 17 Mar 11 WAB Original code + + The register state for the 8088 CPU is: + + AX<0:15> AH/AL Register Pair + BX<0:15> BH/BL Register Pair + CX<0:15> CH/CL Register Pair + DX<0:15> DH/DL Register Pair + SI<0:15> Source Index Register + DI<0:15> Destination Index Register + BP<0:15> Base Pointer + SP<0:15> Stack Pointer + CS<0:15> Code Segment Register + DS<0:15> Date Segment Register + SS<0:15> Stack Segment Register + ES<0:15> Extra Segment Register + IP<0:15> Program Counter + + PSW<0:15> Program Status Word - Contains the following flags: + + AF Auxillary Flag + CF Carry Flag + OF Overflow Flag + SF Sign Flag + PF Parity Flag + ZF Zero Flag + DF Direction Flag + IF Interrupt Enable Flag + TF Trap Flag + + in bit positions: + 15 8 7 0 + |--|--|--|--|OF|DF|IF|TF|SF|ZF|--|AF|--|PF|--|CF| + + The 8088 is an 8-bit CPU, which uses 16-bit offset and segment registers + in combination with a dedicated adder to address up to 1MB of memory directly. + The offset register is added to the segment register shifted left 4 places + to obtain the 20-bit address. + + The CPU utilizes two separate processing units - the Execution Unit (EU) and + the Bus Interface Unit (BIU). The EU executes instructions. The BIU fetches + instructions, reads operands and writes results. The two units can operate + independently of one another and are able, under most circumstances, to + extensively overlap instruction fetch with execution. + + The BIUs of the 8086 and 8088 are functionally identical, but are implemented + differently to match the structure and performance characteristics of their + respective buses. + + The almost 300 instructions come in 1, 2, 3, 4, 5, 6 and 7-byte flavors. + + This routine is the simulator for the 8088. It is called from the + simulator control program to execute instructions in simulated memory, + starting at the simulated IP. It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + I/O error in I/O simulator + Invalid OP code (if ITRAP is set on CPU) + + 2. Interrupts. + There are 256 possible levels of interrupt, and in effect they + do a hardware CALL instruction to one of 256 possible low + memory addresses. + + 3. Non-existent memory. On the 8088, reads to non-existent memory + return 0FFh, and writes are ignored. + + Some algorithms were pulled from the GDE Dos/IP Emulator by Jim Hudgens + +*/ +/* + This algorithm was pulled from the GDE Dos/IP Emulator by Jim Hudgens + +CARRY CHAIN CALCULATION. + This represents a somewhat expensive calculation which is + apparently required to emulate the setting of the OF and + AF flag. The latter is not so important, but the former is. + The overflow flag is the XOR of the top two bits of the + carry chain for an addition (similar for subtraction). + Since we do not want to simulate the addition in a bitwise + manner, we try to calculate the carry chain given the + two operands and the result. + + So, given the following table, which represents the + addition of two bits, we can derive a formula for + the carry chain. + + a b cin r cout + 0 0 0 0 0 + 0 0 1 1 0 + 0 1 0 1 0 + 0 1 1 0 1 + 1 0 0 1 0 + 1 0 1 0 1 + 1 1 0 0 1 + 1 1 1 1 1 + + Construction of table for cout: + + ab + r \ 00 01 11 10 + |------------------ + 0 | 0 1 1 1 + 1 | 0 0 1 0 + + By inspection, one gets: cc = ab + r'(a + b) + + That represents alot of operations, but NO CHOICE.... + +BORROW CHAIN CALCULATION. + The following table represents the + subtraction of two bits, from which we can derive a formula for + the borrow chain. + + a b bin r bout + 0 0 0 0 0 + 0 0 1 1 1 + 0 1 0 1 1 + 0 1 1 0 1 + 1 0 0 1 0 + 1 0 1 0 0 + 1 1 0 0 0 + 1 1 1 1 1 + + Construction of table for cout: + + ab + r \ 00 01 11 10 + |------------------ + 0 | 0 1 0 0 + 1 | 1 1 1 0 + + By inspection, one gets: bc = a'b + r(a' + b) + + Segment register selection and overrides are handled as follows: + If there is a segment override, the register number is stored + in seg_ovr. If there is no override, seg_ovr is zero. Seg_ovr + is set to zero after each instruction except segment override + instructions. + + Get_ea sets the value of seg_reg to the override if present + otherwise to the default value for the registers used in the + effective address calculation. + + The get/put_smword/byte routines use the register number in + seg_reg to obtain the segment value to calculate the absolute + memory address for the operation. + */ + +#include +#include "multibus_defs.h" + +#define UNIT_V_OPSTOP (UNIT_V_UF) /* Stop on Invalid OP? */ +#define UNIT_OPSTOP (1 << UNIT_V_OPSTOP) +#define UNIT_V_CHIP (UNIT_V_UF+1) /* 8088 or 8086 */ +#define UNIT_CHIP (1 << UNIT_V_CHIP) + +/* Flag values to set proper positions in PSW */ +#define CF 0x0001 +#define PF 0x0004 +#define AF 0x0010 +#define ZF 0x0040 +#define SF 0x0080 +#define TF 0x0100 +#define IF 0x0200 +#define DF 0x0400 +#define OF 0x0800 + +/* Macros to handle the flags in the PSW + 8088 has top 4 bits of the flags set to 1 + also, bit#1 is set. This is (not well) documented behavior. */ +#define PSW_ALWAYS_ON (0xF002) /* for 8086 */ +#define PSW_MSK (CF|PF|AF|ZF|SF|TF|IF|DF|OF) +#define TOGGLE_FLAG(FLAG) (PSW ^= FLAG) +#define SET_FLAG(FLAG) (PSW |= FLAG) +#define CLR_FLAG(FLAG) (PSW &= ~FLAG) +#define GET_FLAG(FLAG) (PSW & FLAG) +#define CONDITIONAL_SET_FLAG(COND,FLAG) \ + if (COND) SET_FLAG(FLAG); else CLR_FLAG(FLAG) + +/* union of byte and word registers */ +union { + uint8 b[2]; /* two bytes */ + uint16 w; /* one word */ +} A, B, C, D; /* storage for AX, BX, CX and DX */ + +/* macros for byte registers */ +#define AH (A.b[1]) +#define AL (A.b[0]) +#define BH (B.b[1]) +#define BL (B.b[0]) +#define CH (C.b[1]) +#define CL (C.b[0]) +#define DH (D.b[1]) +#define DL (D.b[0]) + +/* macros for word registers */ +#define AX (A.w) +#define BX (B.w) +#define CX (C.w) +#define DX (D.w) + +/* macros for handling IP and SP */ +#define INC_IP1 (++IP & ADDRMASK20) /* increment IP one byte */ +#define INC_IP2 ((IP += 2) & ADDRMASK20) /* increment IP two bytes */ + +/* storage for the rest of the registers */ +int32 DI; /* Source Index Register */ +int32 SI; /* Destination Index Register */ +int32 BP; /* Base Pointer Register */ +int32 SP; /* Stack Pointer Register */ +int32 CS; /* Code Segment Register */ +int32 DS; /* Data Segment Register */ +int32 SS; /* Stack Segment Register */ +int32 ES; /* Extra Segment Register */ +int32 IP; /* Program Counter */ +int32 PSW; /* Program Status Word (Flags) */ +int32 saved_PC = 0; /* saved program counter */ +int32 int_req = 0; /* Interrupt request */ +int32 chip = 0; /* 0 = 8088 chip, 1 = 8086 chip */ +#define CHIP_8088 0 /* processor types */ +#define CHIP_8086 1 +#define CHIP_80188 2 +#define CHIP_80186 3 +#define CHIP_80286 4 + +int32 seg_ovr = 0; /* segment override register */ +int32 seg_reg = 0; /* segment register to use for EA */ +#define SEG_NONE 0 /* segmenr override register values */ +#define SEG_CS 8 +#define SEG_DS 9 +#define SEG_ES 10 +#define SEG_SS 11 + +int32 PCX; /* External view of IP */ + +/* handle prefix instructions */ +uint32 sysmode = 0; /* prefix flags */ +#define SYSMODE_SEG_DS_SS 0x01 +#define SYSMODE_SEGOVR_CS 0x02 +#define SYSMODE_SEGOVR_DS 0x04 +#define SYSMODE_SEGOVR_ES 0x08 +#define SYSMODE_SEGOVR_SS 0x10 +#define SYSMODE_SEGMASK (SYSMODE_SEG_DS_SS | SYSMODE_SEGOVR_CS | \ + SYSMODE_SEGOVR_DS | SYSMODE_SEGOVR_ES | SYSMODE_SEGOVR_SS) +#define SYSMODE_PREFIX_REPE 0x20 +#define SYSMODE_PREFIX_REPNE 0x40 + +/* function prototypes */ +int32 sign_ext(int32 val); +int32 fetch_byte(int32 flag); +int32 fetch_word(void); +int32 parity(int32 val); +void i86_intr_raise(uint8 num); +uint32 get_rbyte(uint32 reg); +uint32 get_rword(uint32 reg); +void put_rbyte(uint32 reg, uint32 val); +void put_rword(uint32 reg, uint32 val); +uint32 get_ea(uint32 mrr); +void set_segreg(uint32 reg); +void get_mrr_dec(uint32 mrr, uint32 *mod, uint32 *reg, uint32 *rm); + +/* emulator primitives */ +uint8 aad_word(uint16 d); +uint16 aam_word(uint8 d); +uint8 adc_byte(uint8 d, uint8 s); +uint16 adc_word(uint16 d, uint16 s); +uint8 add_byte(uint8 d, uint8 s); +uint16 add_word(uint16 d, uint16 s); +uint8 and_byte(uint8 d, uint8 s); +uint16 cmp_word(uint16 d, uint16 s); +uint8 cmp_byte(uint8 d, uint8 s); +uint16 and_word(uint16 d, uint16 s); +uint8 dec_byte(uint8 d); +uint16 dec_word(uint16 d); +void div_byte(uint8 s); +void div_word(uint16 s); +void idiv_byte(uint8 s); +void idiv_word(uint16 s); +void imul_byte(uint8 s); +void imul_word(uint16 s); +uint8 inc_byte(uint8 d); +uint16 inc_word(uint16 d); +void mul_byte(uint8 s); +void mul_word(uint16 s); +uint8 neg_byte(uint8 s); +uint16 neg_word(uint16 s); +uint8 not_byte(uint8 s); +uint16 not_word(uint16 s); +uint8 or_byte(uint8 d, uint8 s); +uint16 or_word(uint16 d, uint16 s); +void push_word(uint16 val); +uint16 pop_word(void); +uint8 rcl_byte(uint8 d, uint8 s); +uint16 rcl_word(uint16 d, uint16 s); +uint8 rcr_byte(uint8 d, uint8 s); +uint16 rcr_word(uint16 d, uint16 s); +uint8 rol_byte(uint8 d, uint8 s); +uint16 rol_word(uint16 d, uint16 s); +uint8 ror_byte(uint8 d, uint8 s); +uint16 ror_word(uint16 d, uint16 s); +uint8 shl_byte(uint8 d, uint8 s); +uint16 shl_word(uint16 d, uint16 s); +uint8 shr_byte(uint8 d, uint8 s); +uint16 shr_word(uint16 d, uint16 s); +uint8 sar_byte(uint8 d, uint8 s); +uint16 sar_word(uint16 d, uint16 s); +uint8 sbb_byte(uint8 d, uint8 s); +uint16 sbb_word(uint16 d, uint16 s); +uint8 sub_byte(uint8 d, uint8 s); +uint16 sub_word(uint16 d, uint16 s); +void test_byte(uint8 d, uint8 s); +void test_word(uint16 d, uint16 s); +uint8 xor_byte(uint8 d, uint8 s); +uint16 xor_word(uint16 d, uint16 s); +int32 get_smbyte(int32 segreg, int32 addr); +int32 get_smword(int32 segreg, int32 addr); +void put_smbyte(int32 segreg, int32 addr, int32 val); +void put_smword(int32 segreg, int32 addr, int32 val); + +/* simulator routines */ +int32 sim_instr(void); +t_stat i8088_reset (DEVICE *dptr); +t_stat i8088_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat i8088_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); + +/* external references */ +//extern t_stat i8088_reset (DEVICE *dptr); + +/* Multibus memory read and write absolute address routines */ +extern int32 get_mbyte(int32 addr); +extern int32 get_mword(int32 addr); +extern void put_mbyte(int32 addr, int32 val); +extern void put_mword(int32 addr, int32 val); + +extern int32 sim_int_char; +extern int32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ + +/* This is the I/O configuration table. There are 65536 possible +device addresses, if a device is plugged to a port it's routine +address is here, 'nulldev' means no device is available +*/ + +struct idev { + int32 (*routine)(); +}; +extern struct idev dev_table[]; + +/* 8088 CPU data structures + + i8088_dev CPU device descriptor + i8088_unit CPU unit descriptor + i8088_reg CPU register list + i8088_mod CPU modifiers list +*/ + +UNIT i8088_unit = { UDATA (NULL, 0, 0) }; + +REG i8088_reg[] = { + { HRDATA (IP, saved_PC, 16) }, /* must be first for sim_PC */ + { HRDATA (AX, AX, 16) }, + { HRDATA (BX, BX, 16) }, + { HRDATA (CX, CX, 16) }, + { HRDATA (DX, DX, 16) }, + { HRDATA (DI, DI, 16) }, + { HRDATA (SI, SI, 16) }, + { HRDATA (BP, BP, 16) }, + { HRDATA (SP, SP, 16) }, + { HRDATA (CS, CS, 16) }, + { HRDATA (DS, DS, 16) }, + { HRDATA (SS, SS, 16) }, + { HRDATA (ES, ES, 16) }, + { HRDATA (PSW, PSW, 16) }, + { HRDATA (WRU, sim_int_char, 8) }, + { NULL } +}; + +MTAB i8088_mod[] = { + { UNIT_CHIP, UNIT_CHIP, "8086", "8086", NULL }, + { UNIT_CHIP, 0, "8088", "8088", NULL }, + { UNIT_OPSTOP, UNIT_OPSTOP, "ITRAP", "ITRAP", NULL }, + { UNIT_OPSTOP, 0, "NOITRAP", "NOITRAP", NULL }, + { 0 } +}; + +DEBTAB i8088_debug[] = { + { "ALL", DEBUG_all }, + { "FLOW", DEBUG_flow }, + { "READ", DEBUG_read }, + { "WRITE", DEBUG_write }, + { "LEV1", DEBUG_level1 }, + { "LEV2", DEBUG_level2 }, + { "REG", DEBUG_reg }, + { "ASM", DEBUG_asm }, + { NULL } +}; + +DEVICE i8088_dev = { + "CPU", //name + &i8088_unit, //units + i8088_reg, //registers + i8088_mod, //modifiers + 1, //numunits + 16, //aradix + 20, //awidth + 1, //aincr + 16, //dradix + 8, //dwidth + &i8088_ex, //examine + &i8088_dep, //deposit + &i8088_reset, //reset + NULL, //boot + NULL, //attach + NULL, //detach + NULL, //ctxt + DEV_DEBUG, //flags + 0, //dctrl + i8088_debug, //debflags + NULL, //msize + NULL //lname +}; + +uint8 xor_3_tab[] = { 0, 1, 1, 0 }; + +int32 IP; + +static const char *opcode[] = { +"ADD ", "ADD ", "ADD ", "ADD ", /* 0x00 */ +"ADD AL,", "ADD AX,", "PUSH ES", "POP ES", +"OR ", "OR ", "OR ", "OR ", +"OR AL,", "OR AX,", "PUSH CS", "???", +"ADC ", "ADC ", "ADC ", "ADC ", /* 0x10 */ +"ADC AL,", "ADC AX,", "PUSH SS", "RPOP SS", +"SBB ", "SBB ", "SBB ", "SBB ", +"SBB AL,", "SBB AX,", "PUSH DS", "POP DS", +"AND ", "AND ", "AND ", "AND ", /* 0x20 */ +"AND AL,", "AND AX,", "ES:", "DAA", +"SUB ", "SUB ", "SUB ", "SUB ", +"SUB AL,", "SUB AX,", "CS:", "DAS", +"XOR ", "XOR ", "XOR ", "XOR ", /* 0x30 */ +"XOR AL,", "XOR AX,", "SS:", "AAA", +"CMP ", "CMP ", "CMP ", "CMP ", +"CMP AL,", "CMP AX,", "DS:", "AAS", +"INC AX", "INC CX", "INC DX", "INC BX", /* 0x40 */ +"INC SP", "INC BP", "INC SI", "INC DI", +"DEC AX", "DEC CX", "DEC DX", "DEC BX", +"DEC SP", "DEC BP", "DEC SI", "DEC DI", +"PUSH AX", "PUSH CX", "PUSH DX", "PUSH BX", /* 0x50 */ +"PUSH SP", "PUSH BP", "PUSH SI", "PUSH DI", +"POP AX", "POP CX", "POP DX", "POP BX", +"POP SP", "POP BP", "POP SI", "POP DI", +"???", "???", "???", "???", /* 0x60 */ +"???", "???", "???", "???", +"PUSH ", "IMUL ", "PUSH ", "IMUL ", +"INSB", "INSW", "OUTSB", "OUTSW", +"JO ", "JNO ", "JC ", "JNC", /* 0x70 */ +"JZ ", "JNZ ", "JNA ", "JA", +"JS ", "JNS ", "JP ", "JNP ", +"JL ", "JNL ", "JLE ", "JNLE", +"???", "???", "???", "???", /* 0x80 */ +"TEST ", "TEST ", "XCHG ", "XCHG ", +"MOV ", "MOV ", "MOV ", "MOV ", +"MOV ", "LEA ", "MOV ", "POP ", +"NOP", "XCHG AX,CX", "XCHG AX,DX", "XCHG AX,BX",/* 0x90 */ +"XCHG AX,SP", "XCHG AX,BP", "XCHG AX,SI", "XCHG AX,DI", +"CBW", "CWD", "CALL ", "WAIT", +"PUSHF", "POPF", "SAHF", "LAHF", +"MOV AL,", "MOV AX,", "MOV ", "MOV ", /* 0xA0 */ +"MOVSB", "MOVSW", "CMPSB", "CMPSW", +"TEST AL,", "TEST AX,", "STOSB", "STOSW", +"LODSB", "LODSW", "SCASB", "SCASW", +"MOV AL,", "MOV CL,", "MOV DL,", "MOV BL,", /* 0xB0 */ +"MOV AH,", "MOV CH,", "MOV DH,", "MOV BH,", +"MOV AX,", "MOV CX,", "MOV DX,", "MOV BX,", +"MOV SP,", "MOV BP,", "MOV SI,", "MOV DI," +" ", " ", "RET ", "RET ", /* 0xC0 */ +"LES ", "LDS ", "MOV ", "MOV ", +"???", "???", "RET ", "RET", +"INT 3", "INT ", "INTO", "IRET", +" ", " ", " ", " ", /* 0xD0 */ +"AAM", "AAD", "???", "XLATB", +"ESC ", "ESC ", "ESC ", "ESC ", +"ESC ", "ESC ", "ESC ", "ESC ", +"LOOPNZ ", "LOOPZ ", "LOOP", "JCXZ", /* 0xE0 */ +"IN AL,", "IN AX,", "OUT ", "OUT ", +"CALL ", "JMP ", "JMP ", "JMP ", +"IN AL,DX", "IN AX,DX", "OUT DX,AL", "OUT DX,AX", +"LOCK", "???", "REPNZ", "REPZ", /* 0xF0 */ +"HLT", "CMC", " ", " ", +"CLC", "STC", "CLI", "STI", +"CLD", "STD", "???", "???" + }; + +int32 oplen[256] = { +1,3,1,1,1,1,2,1,0,1,1,1,1,1,2,1, +0,3,1,1,1,1,2,1,0,1,1,1,1,1,2,1, +0,3,3,1,1,1,2,1,0,1,3,1,1,1,2,1, +0,3,3,1,1,1,2,1,0,1,3,1,1,1,2,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,3,3,3,1,2,1,1,1,3,0,3,3,2,1, +1,1,3,2,3,1,2,1,1,0,3,2,3,0,2,1, +1,1,3,1,3,1,2,1,1,1,3,1,3,0,2,1, +1,1,3,1,3,1,2,1,1,1,3,1,3,0,2,1 +}; + +int32 sim_instr (void) +{ + extern int32 sim_interval; + int32 IR, OP, DAR, reason, hi, lo, carry, i, adr; + int32 MRR, REG, EA, MOD, RM, DISP, VAL, DATA, OFF, SEG, INC, VAL1; + + IP = saved_PC & ADDRMASK16; /* load local IP */ + reason = 0; /* clear stop reason */ + + /* Main instruction fetch/decode loop */ + + while (reason == 0) { /* loop until halted */ + if (i8088_dev.dctrl & DEBUG_asm) + printf("\n"); + if (i8088_dev.dctrl & DEBUG_reg) { + printf("Regs: AX=%04X BX=%04X CX=%04X DX=%04X SP=%04X BP=%04X SI=%04X DI=%04X IP=%04X\n", + AX, BX, CX, DX, SP, BP, SI, DI, IP); + printf("Segs: CS=%04X DS=%04X ES=%04X SS=%04X ", CS, DS, ES, SS); + printf("Flags: %04X\n", PSW); + } + + if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; + } + sim_interval--; /* countdown clock */ + + if (int_req > 0) { /* interrupt? */ + + /* 8088 interrupts not implemented yet. */ + + } /* end interrupt */ + + if (sim_brk_summ && + sim_brk_test (IP, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + + + PCX = IP; + IR = OP = fetch_byte(0); /* fetch instruction */ + + + + /* Handle below all operations which refer to registers or + register pairs. After that, a large switch statement + takes care of all other opcodes */ + + /* data transfer instructions */ + + /* arithmetic instructions */ + + /* bit manipulation instructions */ + /* string manipulation instructions */ + /* control transfer instructions */ + /* processor control instructions */ + /* The Big Instruction Decode Switch */ + + switch (IR) { + + /* data transfer instructions */ + /* arithmetic instructions */ + + case 0x00: /* ADD byte - REG = REG + (EA) */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = add_byte(get_rbyte(REG), get_smbyte(seg_reg, EA)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } else { /* RM is second register */ + VAL = add_byte(get_rbyte(REG), get_rbyte(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x01: /* ADD word - (EA) = (EA) + REG */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = add_word(get_rword(REG), get_smword(seg_reg, EA)); /* do operation */ + put_smword(seg_reg, EA, VAL); /* store result */ + } else { /* RM is second register */ + VAL = add_word(get_rword(REG), get_rword(RM)); /* do operation */ + put_rword(REG, VAL); /* store result */ + } + break; + + case 0x02: /* ADD byte - REG = REG + (EA) */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = add_byte(get_rbyte(REG), get_smbyte(seg_reg, EA)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } else { /* RM is second register */ + VAL = add_byte(get_rbyte(REG), get_rbyte(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x03: /* ADD word - (EA) = (EA) + REG */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = adc_word(get_rword(REG), get_smword(seg_reg, EA)); /* do operation */ + put_smbyte(seg_reg, EA, VAL); /* store result */ + } else { /* RM is second register */ + VAL = adc_word(get_rword(REG), get_rword(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x04: /* ADD byte - AL = AL + DATA */ + DATA = fetch_byte(1); + VAL = add_byte(AL, DATA); /* do operation */ + AL = VAL; /* store result */ + break; + + case 0x05: /* ADD word - (EA) = (EA) + REG */ + DATA = fetch_word(); + VAL = add_word(AX, DATA); /* do operation */ + AX = VAL; /* store result */ + break; + + case 0x06: /* PUSH ES */ + push_word(ES); + break; + + case 0x07: /* POP ES */ + ES = pop_word(); + break; + + case 0x08: /* OR byte - REG = REG OR (EA) */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = or_byte(get_rbyte(REG), get_smbyte(seg_reg, EA)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } else { /* RM is second register */ + VAL = or_byte(get_rbyte(REG), get_rbyte(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x09: /* OR word - (EA) = (EA) OR REG */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = or_word(get_rword(REG), get_smword(seg_reg, EA)); /* do operation */ + put_smword(seg_reg, EA, VAL); /* store result */ + } else { /* RM is second register */ + VAL = or_word(get_rword(REG), get_rword(RM)); /* do operation */ + put_rword(REG, VAL); /* store result */ + } + break; + + case 0x0A: /* OR byte - REG = REG OR (EA) */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = or_byte(get_rbyte(REG), get_smbyte(seg_reg, EA)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } else { /* RM is second register */ + VAL = or_byte(get_rbyte(REG), get_rbyte(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x0B: /* OR word - (EA) = (EA) OR REG */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = or_word(get_rword(REG), get_smword(seg_reg, EA)); /* do operation */ + put_smbyte(seg_reg, EA, VAL); /* store result */ + } else { /* RM is second register */ + VAL = or_word(get_rword(REG), get_rword(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x0C: /* OR byte - AL = AL OR DATA */ + DATA = fetch_byte(1); + VAL = or_byte(AL, DATA); /* do operation */ + AL = VAL; /* store result */ + break; + + case 0x0D: /* OR word - (EA) = (EA) OR REG */ + DATA = fetch_word(); + VAL = or_word(AX, DATA); /* do operation */ + AX = VAL; /* store result */ + break; + + case 0x0E: /* PUSH CS */ + push_word(CS); + break; + + /* 0x0F - Not implemented on 8086/8088 */ + + case 0x10: /* ADC byte - REG = REG + (EA) + CF */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = adc_byte(get_rbyte(REG), get_smbyte(seg_reg, EA)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } else { /* RM is second register */ + VAL = adc_byte(get_rbyte(REG), get_rbyte(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x11: /* ADC word - (EA) = (EA) + REG + CF */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = adc_word(get_rword(REG), get_smword(seg_reg, EA)); /* do operation */ + put_smword(seg_reg, EA, VAL); /* store result */ + } else { /* RM is second register */ + VAL = adc_word(get_rword(REG), get_rword(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x12: /* ADC byte - REG = REG + (EA) + CF */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = adc_byte(get_rbyte(REG), get_smbyte(seg_reg, EA)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } else { /* RM is second register */ + VAL = adc_byte(get_rbyte(REG), get_rbyte(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x13: /* ADC word - (EA) = (EA) + REG + CF */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = adc_word(get_rword(REG), get_smword(seg_reg, EA)); /* do operation */ + put_smbyte(seg_reg, EA, VAL); /* store result */ + } else { /* RM is second register */ + VAL = adc_word(get_rword(REG), get_rword(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x14: /* ADC byte - AL = AL + DATA + CF */ + DATA = fetch_byte(1); + VAL = adc_byte(AL, DATA); /* do operation */ + AL = VAL; /* store result */ + break; + + case 0x15: /* ADC word - (EA) = (EA) + REG + CF */ + DATA = fetch_word(); + VAL = adc_word(AX, DATA); /* do operation */ + AX = VAL; /* store result */ + break; + + case 0x16: /* PUSH SS */ + push_word(SS); + break; + + case 0x17: /* POP SS */ + SS = pop_word(); + break; + + case 0x18: /* SBB byte - REG = REG - (EA) - CF */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = sbb_byte(get_rbyte(REG), get_smbyte(seg_reg, EA)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } else { /* RM is second register */ + VAL = sbb_byte(get_rbyte(REG), get_rbyte(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x19: /* SBB word - (EA) = (EA) - REG - CF */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = sbb_word(get_rword(REG), get_smword(seg_reg, EA)); /* do operation */ + put_smword(seg_reg, EA, VAL); /* store result */ + } else { /* RM is second register */ + VAL = sbb_word(get_rword(REG), get_rword(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x1A: /* SBB byte - REG = REG - (EA) - CF */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = sbb_byte(get_rbyte(REG), get_smbyte(seg_reg, EA)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } else { /* RM is second register */ + VAL = sbb_byte(get_rbyte(REG), get_rbyte(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x1B: /* SBB word - (EA) = (EA) - REG - CF */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = sbb_word(get_rword(REG), get_smword(seg_reg, EA)); /* do operation */ + put_smbyte(seg_reg, EA, VAL); /* store result */ + } else { /* RM is second register */ + VAL = sbb_word(get_rword(REG), get_rword(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x1C: /* SBB byte - AL = AL - DATA - CF */ + DATA = fetch_byte(1); + VAL = sbb_byte(AL, DATA); /* do operation */ + AL = VAL; /* store result */ + break; + + case 0x1D: /* SBB word - (EA) = (EA) - REG - CF */ + DATA = fetch_word(); + VAL = sbb_word(AX, DATA); /* do operation */ + AX = VAL; /* store result */ + break; + + case 0x1E: /* PUSH DS */ + push_word(DS); + break; + + case 0x1F: /* POP DS */ + DS = pop_word(); + break; + + case 0x20: /* AND byte - REG = REG AND (EA) */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = and_byte(get_rbyte(REG), get_smbyte(seg_reg, EA)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } else { /* RM is second register */ + VAL = and_byte(get_rbyte(REG), get_rbyte(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x21: /* AND word - (EA) = (EA) AND REG */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = and_word(get_rword(REG), get_smword(seg_reg, EA)); /* do operation */ + put_smword(seg_reg, EA, VAL); /* store result */ + } else { /* RM is second register */ + VAL = and_word(get_rword(REG), get_rword(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x22: /* AND byte - REG = REG AND (EA) */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = and_byte(get_rbyte(REG), get_smbyte(seg_reg, EA)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } else { /* RM is second register */ + VAL = and_byte(get_rbyte(REG), get_rbyte(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x23: /* AND word - (EA) = (EA) AND REG */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = and_word(get_rword(REG), get_smword(seg_reg, EA)); /* do operation */ + put_smbyte(seg_reg, EA, VAL); /* store result */ + } else { /* RM is second register */ + VAL = and_word(get_rword(REG), get_rword(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x24: /* AND byte - AL = AL AND DATA */ + DATA = fetch_byte(1); + VAL = and_byte(AL, DATA); /* do operation */ + AL = VAL; /* store result */ + break; + + case 0x25: /* AND word - (EA) = (EA) AND REG */ + DATA = fetch_word(); + VAL = and_word(AX, DATA); /* do operation */ + AX = VAL; /* store result */ + break; + + case 0x26: /* ES: - segment override prefix */ + seg_ovr = SEG_ES; + sysmode |= SYSMODE_SEGOVR_ES; + break; + + case 0x27: /* DAA */ + if (((AL & 0xF) > 9) || GET_FLAG(AF)) { + AL += 6; + SET_FLAG(AF); + } + if ((AL > 0x9F) || GET_FLAG(CF)) { + AL += 0x60; + SET_FLAG(CF); + } + break; + + case 0x28: /* SUB byte - REG = REG - (EA) */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = sub_byte(get_rbyte(REG), get_smbyte(seg_reg, EA)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } else { /* RM is second register */ + VAL = sub_byte(get_rbyte(REG), get_rbyte(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x29: /* SUB word - (EA) = (EA) - REG */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = sub_word(get_rword(REG), get_smword(seg_reg, EA)); /* do operation */ + put_smword(seg_reg, EA, VAL); /* store result */ + } else { /* RM is second register */ + VAL = sub_word(get_rword(REG), get_rword(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x2A: /* SUB byte - REG = REG - (EA) */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = sub_byte(get_rbyte(REG), get_smbyte(seg_reg, EA)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } else { /* RM is second register */ + VAL = sub_byte(get_rbyte(REG), get_rbyte(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x2B: /* SUB word - (EA) = (EA) - REG */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = sub_word(get_rword(REG), get_smword(seg_reg, EA)); /* do operation */ + put_smbyte(seg_reg, EA, VAL); /* store result */ + } else { /* RM is second register */ + VAL = sub_word(get_rword(REG), get_rword(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x2C: /* SUB byte - AL = AL - DATA */ + DATA = fetch_byte(1); + VAL = sub_byte(AL, DATA); /* do operation */ + AL = VAL; /* store result */ + break; + + case 0x2D: /* SUB word - (EA) = (EA) - REG */ + DATA = fetch_word(); + VAL = sub_word(AX, DATA); /* do operation */ + AX = VAL; /* store result */ + break; + + case 0x2E: /* DS: - segment override prefix */ + seg_ovr = SEG_DS; + sysmode |= SYSMODE_SEGOVR_DS; + break; + + case 0x2F: /* DAS */ + if (((AL & 0xF) > 9) || GET_FLAG(AF)) { + AL -= 6; + SET_FLAG(AF); + } + if ((AL > 0x9F) || GET_FLAG(CF)) { + AL -= 0x60; + SET_FLAG(CF); + } + break; + + case 0x30: /* XOR byte - REG = REG XOR (EA) */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = xor_byte(get_rbyte(REG), get_smbyte(seg_reg, EA)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } else { /* RM is second register */ + VAL = xor_byte(get_rbyte(REG), get_rbyte(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x31: /* XOR word - (EA) = (EA) XOR REG */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = xor_word(get_rword(REG), get_smword(seg_reg, EA)); /* do operation */ + put_smword(seg_reg, EA, VAL); /* store result */ + } else { /* RM is second register */ + VAL = xor_word(get_rword(REG), get_rword(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x32: /* XOR byte - REG = REG XOR (EA) */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = xor_byte(get_rbyte(REG), get_smbyte(seg_reg, EA)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } else { /* RM is second register */ + VAL = xor_byte(get_rbyte(REG), get_rbyte(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x33: /* XOR word - (EA) = (EA) XOR REG */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = xor_word(get_rword(REG), get_smword(seg_reg, EA)); /* do operation */ + put_smbyte(seg_reg, EA, VAL); /* store result */ + } else { /* RM is second register */ + VAL = xor_word(get_rword(REG), get_rword(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x34: /* XOR byte - AL = AL XOR DATA */ + DATA = fetch_byte(1); + VAL = xor_byte(AL, DATA); /* do operation */ + AL = VAL; /* store result */ + break; + + case 0x35: /* XOR word - (EA) = (EA) XOR REG */ + DATA = fetch_word(); + VAL = xor_word(AX, DATA); /* do operation */ + AX = VAL; /* store result */ + break; + + case 0x36: /* SS: - segment override prefix */ + seg_ovr = SEG_SS; + sysmode |= SYSMODE_SEGOVR_SS; + break; + + case 0x37: /* AAA */ + if (((AL & 0xF) > 9) || GET_FLAG(AF)) { + AL += 6; + AH++; + SET_FLAG(AF); + } + CONDITIONAL_SET_FLAG(GET_FLAG(AF), CF); + AL &= 0xF; + break; + + case 0x38: /* CMP byte - CMP (REG, (EA)) */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = xor_byte(get_rbyte(REG), get_smbyte(seg_reg, EA)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } else { /* RM is second register */ + VAL = xor_byte(get_rbyte(REG), get_rbyte(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x39: /* CMP word - CMP ((EA), REG) */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = xor_word(get_rword(REG), get_smword(seg_reg, EA)); /* do operation */ + put_smword(seg_reg, EA, VAL); /* store result */ + } else { /* RM is second register */ + VAL = xor_word(get_rword(REG), get_rword(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x3A: /* CMP byte - CMP (REG, (EA)) */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = xor_byte(get_rbyte(REG), get_smbyte(seg_reg, EA)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } else { /* RM is second register */ + VAL = xor_byte(get_rbyte(REG), get_rbyte(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x3B: /* CMP word - CMP ((EA), REG) */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = xor_word(get_rword(REG), get_smword(seg_reg, EA)); /* do operation */ + put_smbyte(seg_reg, EA, VAL); /* store result */ + } else { /* RM is second register */ + VAL = xor_word(get_rword(REG), get_rword(RM)); /* do operation */ + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x3C: /* CMP byte - CMP (AL, DATA) */ + DATA = fetch_byte(1); + VAL = xor_byte(AL, DATA); /* do operation */ + AL = VAL; /* store result */ + break; + + case 0x3D: /* CMP word - CMP ((EA), REG) */ + DATA = fetch_word(); + VAL = xor_word(AX, DATA); /* do operation */ + AX = VAL; /* store result */ + break; + + case 0x3E: /* DS: - segment override prefix */ + seg_ovr = SEG_DS; + sysmode |= SYSMODE_SEGOVR_DS; + break; + + case 0x3F: /* AAS */ + if (((AL & 0xF) > 9) || GET_FLAG(AF)) { + AL -= 6; + AH--; + SET_FLAG(AF); + } + CONDITIONAL_SET_FLAG(GET_FLAG(AF), CF); + AL &= 0xF; + break; + + case 0x40: /* INC AX */ + AX = inc_word(AX); + break; + + case 0x41: /* INC CX */ + CX = inc_word(CX); + break; + + case 0x42: /* INC DX */ + DX = inc_word(DX); + break; + + case 0x43: /* INC BX */ + BX = inc_word(BX); + break; + + case 0x44: /* INC SP */ + SP = inc_word(SP); + break; + + case 0x45: /* INC BP */ + BP = inc_word(BP); + break; + + case 0x46: /* INC SI */ + SI = inc_word(SI); + break; + + case 0x47: /* INC DI */ + DI = inc_word(DI); + break; + + case 0x48: /* DEC AX */ + AX = dec_word(AX); + break; + + case 0x49: /* DEC CX */ + CX = dec_word(CX); + break; + + case 0x4A: /* DEC DX */ + DX = dec_word(DX); + break; + + case 0x4B: /* DEC BX */ + BX = dec_word(BX); + break; + + case 0x4C: /* DEC SP */ + SP = dec_word(SP); + break; + + case 0x4D: /* DEC BP */ + BP = dec_word(BP); + break; + + case 0x4E: /* DEC SI */ + SI = dec_word(SI); + break; + + case 0x4F: /* DEC DI */ + DI = dec_word(DI); + break; + + case 0x50: /* PUSH AX */ + push_word(AX); + break; + + case 0x51: /* PUSH CX */ + push_word(CX); + break; + + case 0x52: /* PUSH DX */ + push_word(DX); + break; + + case 0x53: /* PUSH BX */ + push_word(BX); + break; + + case 0x54: /* PUSH SP */ + push_word(SP); + break; + + case 0x55: /* PUSH BP */ + push_word(BP); + break; + + case 0x56: /* PUSH SI */ + push_word(SI); + break; + + case 0x57: /* PUSH DI */ + push_word(DI); + break; + + case 0x58: /* POP AX */ + AX = pop_word(); + break; + + case 0x59: /* POP CX */ + CX = pop_word(); + break; + + case 0x5A: /* POP DX */ + DX = pop_word(); + break; + + case 0x5B: /* POP BX */ + BX = pop_word(); + break; + + case 0x5C: /* POP SP */ + SP = pop_word(); + break; + + case 0x5D: /* POP BP */ + BP = pop_word(); + break; + + case 0x5E: /* POP SI */ + SI = pop_word(); + break; + + case 0x5F: /* POP DI */ + DI = pop_word(); + break; + + /* 0x60 - 0x6F - Not implemented on 8086/8088 */ + + case 0x70: /* JO short label */ + /* jump to byte offset if overflow flag is set */ + OFF = sign_ext(fetch_byte(1)); + EA = (IP + OFF) & ADDRMASK16; + if (GET_FLAG(OF)) + IP = EA; + break; + + case 0x71: /* JNO short label */ + /* jump to byte offset if overflow flag is clear */ + OFF = sign_ext(fetch_byte(1)); + EA = (IP + OFF) & ADDRMASK16; + if (!GET_FLAG(OF)) + IP = EA; + break; + + case 0x72: /* JB/JNAE/JC short label */ + /* jump to byte offset if carry flag is set. */ + OFF = sign_ext(fetch_byte(1)); + EA = (IP + OFF) & ADDRMASK16; + if (GET_FLAG(CF)) + IP = EA; + break; + + case 0x73: /* JNB/JAE/JNC short label */ + /* jump to byte offset if carry flsg is clear */ + OFF = sign_ext(fetch_byte(1)); + EA = (IP + OFF) & ADDRMASK16; + if (!GET_FLAG(CF)) + IP = EA; + break; + + case 0x74: /* JE/JZ short label */ + /* jump to byte offset if zero flag is set */ + OFF = sign_ext(fetch_byte(1)); + EA = (IP + OFF) & ADDRMASK16; + if (GET_FLAG(ZF)) + IP = EA; + break; + + case 0x75: /* JNE/JNZ short label */ + /* jump to byte offset if zero flag is clear */ + OFF = sign_ext(fetch_byte(1)); + EA = (IP + OFF) & ADDRMASK16; + if (!GET_FLAG(ZF)) + IP = EA; + break; + + case 0x76: /* JBE/JNA short label */ + /* jump to byte offset if carry flag is set or if the zero + flag is set. */ + OFF = sign_ext(fetch_byte(1)); + EA = (IP + OFF) & ADDRMASK16; + if (GET_FLAG(CF) || GET_FLAG(ZF)) + IP = EA; + break; + + case 0x77: /* JNBE/JA short label */ + /* jump to byte offset if carry flag is clear and if the zero + flag is clear */ + OFF = sign_ext(fetch_byte(1)); + EA = (IP + OFF) & ADDRMASK16; + if (!(GET_FLAG(CF) || GET_FLAG(ZF))) + IP = EA; + break; + + case 0x78: /* JS short label */ + /* jump to byte offset if sign flag is set */ + OFF = sign_ext(fetch_byte(1)); + EA = (IP + OFF) & ADDRMASK16; + if (GET_FLAG(SF)) + IP = EA; + break; + + case 0x79: /* JNS short label */ + /* jump to byte offset if sign flag is clear */ + OFF = sign_ext(fetch_byte(1)); + EA = (IP + OFF) & ADDRMASK16; + if (!GET_FLAG(SF)) + IP = EA; + break; + + case 0x7A: /* JP/JPE short label */ + /* jump to byte offset if parity flag is set (even) */ + OFF = sign_ext(fetch_byte(1)); + EA = (IP + OFF) & ADDRMASK16; + if (GET_FLAG(PF)) + IP = EA; + break; + + case 0x7B: /* JNP/JPO short label */ + /* jump to byte offset if parity flsg is clear (odd) */ + OFF = sign_ext(fetch_byte(1)); + EA = (IP + OFF) & ADDRMASK16; + if (!GET_FLAG(PF)) + IP = EA; + break; + + case 0x7C: /* JL/JNGE short label */ + /* jump to byte offset if sign flag not equal to overflow flag. */ + OFF = sign_ext(fetch_byte(1)); + EA = (IP + OFF) & ADDRMASK16; + if ((GET_FLAG(SF) != 0) ^ (GET_FLAG(OF) != 0)) + IP = EA; + break; + + case 0x7D: /* JNL/JGE short label */ + /* jump to byte offset if sign flag equal to overflow flag. */ + OFF = sign_ext(fetch_byte(1)); + EA = (IP + OFF) & ADDRMASK16; + if ((GET_FLAG(SF) != 0) == (GET_FLAG(OF) != 0)) + IP = EA; + break; + + case 0x7E: /* JLE/JNG short label */ + /* jump to byte offset if sign flag not equal to overflow flag + or the zero flag is set */ + OFF = sign_ext(fetch_byte(1)); + EA = (IP + OFF) & ADDRMASK16; + if (((GET_FLAG(SF) != 0) ^ (GET_FLAG(OF) != 0)) || GET_FLAG(ZF)) + IP = EA; + break; + + case 0x7F: /* JNLE/JG short label */ + /* jump to byte offset if sign flag equal to overflow flag. + and the zero flag is clear*/ + OFF = sign_ext(fetch_byte(1)); + EA = (IP + OFF) & ADDRMASK16; + if (((GET_FLAG(SF) != 0) == (GET_FLAG(OF) != 0)) || !GET_FLAG(ZF)) + IP = EA; + break; + + case 0x80: /* byte operands */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + DATA = fetch_byte(1); /* must be done after DISP is collected */ + switch(REG) { + case 0: + VAL = add_byte(get_smbyte(seg_reg, EA), DATA); /* ADD mem8, immed8 */ + break; + case 1: + VAL = or_byte(get_smbyte(seg_reg, EA), DATA); /* OR mem8, immed8 */ + break; + case 2: + VAL = adc_byte(get_smbyte(seg_reg, EA), DATA); /* ADC mem8, immed8 */ + break; + case 3: + VAL = sbb_byte(get_smbyte(seg_reg, EA), DATA); /* SBB mem8, immed8 */ + break; + case 4: + VAL = and_byte(get_smbyte(seg_reg, EA), DATA); /* AND mem8, immed8 */ + break; + case 5: + VAL = sub_byte(get_smbyte(seg_reg, EA), DATA); /* SUB mem8, immed8 */ + break; + case 6: + VAL = xor_byte(get_smbyte(seg_reg, EA), DATA); /* XOR mem8, immed8 */ + break; + case 7: + VAL = cmp_byte(get_smbyte(seg_reg, EA), DATA); /* CMP mem8, immed8 */ + break; + } + put_rbyte(EA, VAL); /* store result */ + } else { /* RM is second register */ + switch(REG) { + case 0: + VAL = add_byte(get_rbyte(RM), DATA); /* ADD REG8, immed8 */ + break; + case 1: + VAL = or_byte(get_rbyte(RM), DATA); /* OR REG8, immed8 */ + break; + case 2: + VAL = adc_byte(get_rbyte(RM), DATA); /* ADC REG8, immed8 */ + break; + case 3: + VAL = sbb_byte(get_rbyte(RM), DATA); /* SBB REG8, immed8 */ + break; + case 4: + VAL = and_byte(get_rbyte(RM), DATA); /* AND REG8, immed8 */ + break; + case 5: + VAL = sub_byte(get_rbyte(RM), DATA); /* SUB REG8, immed8 */ + break; + case 6: + VAL = xor_byte(get_rbyte(RM), DATA); /* XOR REG8, immed8 */ + break; + case 7: + VAL = cmp_byte(get_rbyte(RM), DATA); /* CMP REG8, immed8 */ + break; + } + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x81: /* word operands */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + DATA = fetch_byte(1) << 8; /* must be done after DISP is collected */ + DATA |= fetch_byte(1); + switch(REG) { + case 0: + VAL = add_word(get_smword(seg_reg, EA), DATA); /* ADD mem16, immed16 */ + break; + case 1: + VAL = or_word(get_smword(seg_reg, EA), DATA); /* OR mem16, immed16 */ + break; + case 2: + VAL = adc_word(get_smword(seg_reg, EA), DATA); /* ADC mem16, immed16 */ + break; + case 3: + VAL = sbb_word(get_smword(seg_reg, EA), DATA); /* SBB mem16, immed16 */ + break; + case 4: + VAL = and_word(get_smword(seg_reg, EA), DATA); /* AND mem16, immed16 */ + break; + case 5: + VAL = sub_word(get_smword(seg_reg, EA), DATA); /* SUB mem16, immed16 */ + break; + case 6: + VAL = xor_word(get_smword(seg_reg, EA), DATA); /* XOR mem16, immed16 */ + break; + case 7: + VAL = cmp_word(get_smword(seg_reg, EA), DATA); /* CMP mem16, immed16 */ + break; + } + put_rword(EA, VAL); /* store result */ + } else { /* RM is second register */ + switch(REG) { + case 0: + VAL = add_word(get_rword(RM), DATA); /* ADD reg16, immed16 */ + break; + case 1: + VAL = or_word(get_rword(RM), DATA); /* OR reg16, immed16 */ + break; + case 2: + VAL = adc_word(get_rword(RM), DATA); /* ADC reg16, immed16 */ + break; + case 3: + VAL = sbb_word(get_rword(RM), DATA); /* SBB reg16, immed16 */ + break; + case 4: + VAL = and_word(get_rword(RM), DATA); /* AND reg16, immed16 */ + break; + case 5: + VAL = sub_word(get_rword(RM), DATA); /* SUB reg16, immed16 */ + break; + case 6: + VAL = xor_word(get_rword(RM), DATA); /* XOR reg16, immed16 */ + break; + case 7: + VAL = cmp_word(get_rword(RM), DATA); /* CMP reg16, immed16 */ + break; + } + put_rword(RM, VAL); /* store result */ + } + break; + + case 0x82: /* byte operands */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + DATA = fetch_byte(1); /* must be done after DISP is collected */ + switch(REG) { + case 0: + VAL = add_byte(get_smbyte(seg_reg, EA), DATA); /* ADD mem8, immed8 */ + break; + case 2: + VAL = adc_byte(get_smbyte(seg_reg, EA), DATA); /* ADC mem8, immed8 */ + break; + case 3: + VAL = sbb_byte(get_smbyte(seg_reg, EA), DATA); /* SBB mem8, immed8 */ + break; + case 5: + VAL = sub_byte(get_smbyte(seg_reg, EA), DATA); /* SUB mem8, immed8 */ + break; + case 7: + VAL = cmp_byte(get_smbyte(seg_reg, EA), DATA); /* CMP mem8, immed8 */ + break; + default: /* bad opcodes */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + put_rbyte(EA, VAL); /* store result */ + } else { /* RM is second register */ + switch(REG) { + case 0: + VAL = add_byte(get_rbyte(RM), DATA); /* ADD reg8, immed8 */ + break; + case 2: + VAL = adc_byte(get_rbyte(RM), DATA); /* ADC reg8, immed8 */ + break; + case 3: + VAL = sbb_byte(get_rbyte(RM), DATA); /* SBB reg8, immed8 */ + break; + case 5: + VAL = sub_byte(get_rbyte(RM), DATA); /* SUB reg8, immed8 */ + break; + case 7: + VAL = cmp_byte(get_rbyte(RM), DATA); /* CMP reg8, immed8 */ + break; + default: /* bad opcodes */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + put_rbyte(REG, VAL); /* store result */ + } + break; + + case 0x83: /* word operands */ + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + DATA = fetch_byte(1) << 8; /* must be done after DISP is collected */ + if (DATA & 0x80) + DATA |= 0xFF00; + else + DATA &= 0xFF; + switch(REG) { + case 0: + VAL = add_word(get_smword(seg_reg, EA), DATA); /* ADD mem16, immed8-SX */ + break; + case 2: + VAL = adc_word(get_smword(seg_reg, EA), DATA); /* ADC mem16, immed8-SX */ + break; + case 3: + VAL = sbb_word(get_smword(seg_reg, EA), DATA); /* SBB mem16, immed8-SX */ + break; + case 5: + VAL = sub_word(get_smword(seg_reg, EA), DATA); /* SUB mem16, immed8-SX */ + break; + case 7: + VAL = cmp_word(get_smword(seg_reg, EA), DATA); /* CMP mem16, immed8-SX */ + break; + default: /* bad opcodes */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + put_rword(EA, VAL); /* store result */ + } else { /* RM is second register */ + switch(REG) { + case 0: + VAL = add_word(get_rword(RM), DATA); /* ADD reg16, immed8-SX */ + break; + case 2: + VAL = adc_word(get_rword(RM), DATA); /* ADC reg16, immed8-SX */ + break; + case 3: + VAL = sbb_word(get_rword(RM), DATA); /* SBB reg16, immed8-SX */ + break; + case 5: + VAL = sub_word(get_rword(RM), DATA); /* CUB reg16, immed8-SX */ + break; + case 7: + VAL = cmp_word(get_rword(RM), DATA); /* CMP reg16, immed8-SX */ + break; + default: /* bad opcodes */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + put_rword(RM, VAL); /* store result */ + } + break; + + case 0x84: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + test_byte(get_smbyte(seg_reg, EA),get_rbyte(REG)); /* TEST mem8, reg8 */ + } else { + test_byte(get_rbyte(REG),get_rbyte(RM)); /* TEST reg8, reg8 */ + } + break; + + case 0x85: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + test_word(get_smword(seg_reg, EA),get_rword(REG)); /* TEST mem16, reg16 */ + } else { + test_word(get_rword(REG),get_rword(RM)); /* TEST reg16, reg16 */ + } + break; + + case 0x86: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = get_rbyte(REG);/* XCHG mem8, reg8 */ + put_rbyte(REG, get_smbyte(seg_reg, EA)); + put_smbyte(seg_reg, EA, VAL); + } else { + VAL = get_rbyte(RM);/* XCHG reg8, reg8 */ + put_rbyte(RM, get_rbyte(REG)); + put_rbyte(REG, VAL); + } + break; + + case 0x87: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + VAL = get_rword(REG);/* XCHG mem16, reg16 */ + put_rword(REG, get_smword(seg_reg, EA)); + put_smword(seg_reg, EA, VAL); + } else { + VAL = get_rword(RM);/* XCHG reg16, reg16 */ + put_rword(RM, get_rword(REG)); + put_rword(REG, VAL); + } + break; + + case 0x88: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + put_smbyte(seg_reg, EA, get_rbyte(REG)); /* MOV mem8, reg8 */ + } else + put_rbyte(REG, get_rbyte(RM)); /* MOV reg8, reg8 */ + break; + + case 0x89: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + put_smword(seg_reg, EA, get_rword(REG)); /* MOV mem16, reg16 */ + } else + put_rword(REG, get_rword(RM)); /* MOV reg16, reg16 */ + break; + + case 0x8A: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + put_rbyte(REG, get_smbyte(seg_reg, EA)); /* MOV reg8, mem8 */ + } else + put_rbyte(REG, get_rbyte(RM)); /* MOV reg8, reg8 */ + break; + + case 0x8B: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + put_rword(REG, get_smword(seg_reg, EA)); /* MOV reg16, mem16 */ + } else + put_rword(REG, get_rword(RM)); /* MOV reg16, reg16 */ + break; + + case 0x8C: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + switch(REG) { + case 0: /* MOV mem16, ES */ + put_smword(seg_reg, EA, ES); + break; + case 1: /* MOV mem16, CS */ + put_smword(seg_reg, EA, CS); + break; + case 2: /* MOV mem16, SS */ + put_smword(seg_reg, EA, SS); + break; + case 3: /* MOV mem16, DS */ + put_smword(seg_reg, EA, DS); + break; + default: /* bad opcodes */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + } else { + switch(REG) { + case 0: /* MOV reg16, ES */ + put_rword(RM, ES); + break; + case 1: /* MOV reg16, CS */ + put_rword(RM, CS); + break; + case 2: /* MOV reg16, SS */ + put_rword(RM, SS); + break; + case 3: /* MOV reg16, DS */ + put_rword(RM, DS); + break; + default: /* bad opcodes */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + } + break; + + case 0x8D: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + put_rword(REG, EA); /* LEA reg16, mem16 */ + } else { + reason = STOP_OPCODE; + IP -= 2; + } + break; + + case 0x8E: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + switch(REG) { + case 0: /* MOV ES, mem16 */ + ES = get_smword(seg_reg, EA); + break; + case 1: /* MOV CS, mem16 */ + CS = get_smword(seg_reg, EA); + break; + case 2: /* MOV SS, mem16 */ + SS = get_smword(seg_reg, EA); + break; + case 3: /* MOV DS, mem16 */ + DS = get_smword(seg_reg, EA); + break; + default: /* bad opcodes */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + } else { + switch(REG) { + case 0: /* MOV ES, reg16 */ + ES = get_rword(RM); + break; + case 1: /* MOV CS, reg16 */ + CS = get_rword(RM); + break; + case 2: /* MOV SS, reg16 */ + SS = get_rword(RM); + break; + case 3: /* MOV DS, reg16 */ + DS = get_rword(RM); + break; + default: /* bad opcodes */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + } + break; + + case 0x8f: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + put_smword(seg_reg, EA, pop_word()); + } else { + reason = STOP_OPCODE; + IP -= 2; + } + break; + + case 0x90: /* NOP */ + break; + + case 0x91: /* XCHG AX, CX */ + VAL = AX; + AX = CX; + CX = VAL; + break; + + case 0x92: /* XCHG AX, DX */ + VAL = AX; + AX = DX; + DX = VAL; + break; + + case 0x93: /* XCHG AX, BX */ + VAL = AX; + AX = BX; + BX = VAL; + break; + + case 0x94: /* XCHG AX, SP */ + VAL = AX; + AX = SP; + SP = VAL; + break; + + case 0x95: /* XCHG AX, BP */ + VAL = AX; + AX = BP; + BP = VAL; + break; + + case 0x96: /* XCHG AX, SI */ + VAL = AX; + AX = SI; + SI = VAL; + break; + + case 0x97: /* XCHG AX, DI */ + VAL = AX; + AX = DI; + DI = VAL; + break; + + case 0x98: /* cbw */ + if (AL & 0x80) + AH = 0xFF; + else + AH = 0; + break; + + case 0x99: /* cbw */ + if (AX & 0x8000) + DX = 0xffff; + else + DX = 0x0; + break; + + case 0x9A: /* CALL FAR proc */ + OFF = fetch_word(); /* do operation */ + SEG = fetch_word(); + push_word(CS); + CS = SEG; + push_word(IP); + IP = OFF; + break; + + case 0x9B: /* WAIT */ + break; + + case 0x9C: /* PUSHF */ + VAL = PSW; + VAL &= PSW_MSK; + VAL |= PSW_ALWAYS_ON; + push_word(VAL); + break; + + case 0x9D: /* POPF */ + PSW = pop_word(); + break; + + case 0x9E: /* SAHF */ + PSW &= 0xFFFFFF00; + PSW |= AH; + break; + + case 0x9F: /* LAHF */ + AH = PSW & 0xFF; + AH |= 0x2; + break; + + case 0xA0: /* MOV AL, mem8 */ + OFF = fetch_word(); + set_segreg(SEG_DS); /* to allow segment override */ + AL = get_smbyte(seg_reg, OFF); + break; + + case 0xA1: /* MOV AX, mem16 */ + OFF = fetch_word(); + set_segreg(SEG_DS); /* to allow segment override */ + AX = get_smword(seg_reg, OFF); + break; + + case 0xA2: /* MOV mem8, AL */ + OFF = fetch_word(); + set_segreg(SEG_DS); /* to allow segment override */ + put_smbyte(seg_reg, OFF, AL); + break; + + case 0xA3: /* MOV mem16, AX */ + OFF = fetch_word(); + set_segreg(SEG_DS); /* to allow segment override */ + put_smword(seg_reg, OFF, AX); + break; + + case 0xA4: /* MOVS dest-str8, src-str8 */ + if (GET_FLAG(DF)) /* down */ + INC = -1; + else + INC = 1; + while (CX != 0) { + VAL = get_smbyte(seg_reg, SI); + put_smbyte(ES, DI, VAL); + CX--; + SI += INC; + DI += INC; + } + break; + + case 0xA5: /* MOVS dest-str16, src-str16 */ + if (GET_FLAG(DF)) /* down */ + INC = -2; + else + INC = 2; + while (CX != 0) { + VAL = get_smword(seg_reg, SI); + put_smword(ES, DI, VAL); + CX--; + SI += INC; + DI += INC; + } + break; + + case 0xA6: /* CMPS dest-str8, src-str8 */ + if (GET_FLAG(DF)) /* down */ + INC = -1; + else + INC = 1; + while (CX != 0) { + VAL = get_smbyte(seg_reg, SI); + VAL1 = get_smbyte(ES, DI); + cmp_byte(VAL, VAL1); + CX--; + SI += INC; + DI += INC; + if (GET_FLAG(ZF) == 0) + break; + } + break; + + case 0xA7: /* CMPS dest-str16, src-str16 */ + if (GET_FLAG(DF)) /* down */ + INC = -2; + else + INC = 2; + while (CX != 0) { + VAL = get_smword(seg_reg, SI); + VAL1 = get_smword(ES, DI); + cmp_word(VAL, VAL1); + CX--; + SI += INC; + DI += INC; + if (GET_FLAG(ZF) == 0) + break; + } + break; + + case 0xA8: /* TEST AL, immed8 */ + VAL = fetch_byte(1); + test_byte(AL, VAL); + break; + + case 0xA9: /* TEST AX, immed8 */ + VAL = fetch_word(); + test_word(AX, VAL); + break; + + case 0xAA: /* STOS dest-str8 */ + if (GET_FLAG(DF)) /* down */ + INC = -1; + else + INC = 1; + if (sysmode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { + while (CX != 0) { + put_smbyte(ES, DI, AL); + CX--; + DI += INC; + } + sysmode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } else { + put_smbyte(ES, DI, AL); + DI += INC; + } + break; + + case 0xAB: /* STOS dest-str16 */ + if (GET_FLAG(DF)) /* down */ + INC = -1; + else + INC = 1; + if (sysmode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { + while (CX != 0) { + put_smword(ES, DI, AX); + CX--; + DI += INC; + } + sysmode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } else { + put_smword(ES, DI, AL); + DI += INC; + } + break; + + case 0xAC: /* LODS dest-str8 */ + if (GET_FLAG(DF)) /* down */ + INC = -1; + else + INC = 1; + set_segreg(SEG_DS); /* allow overrides */ + if (sysmode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { + while (CX != 0) { + AL = get_smbyte(seg_reg, SI); + CX--; + SI += INC; + } + sysmode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } else { + AL = get_smbyte(seg_reg, SI); + SI += INC; + } + break; + + case 0xAD: /* LODS dest-str16 */ + if (GET_FLAG(DF)) /* down */ + INC = -1; + else + INC = 1; + set_segreg(SEG_DS); /* allow overrides */ + if (sysmode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { + while (CX != 0) { + AX = get_smword(seg_reg, SI); + CX--; + SI += INC; + } + sysmode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } else { + AX = get_smword(seg_reg, SI); + SI += INC; + } + break; + + case 0xAE: /* SCAS dest-str8 */ + if (GET_FLAG(DF)) /* down */ + INC = -1; + else + INC = 1; + if (sysmode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { + while (CX != 0) { + VAL = get_smbyte(ES, DI); + cmp_byte(AL, VAL); + CX--; + DI += INC; + if (GET_FLAG(ZF) == 0) + break; + } + sysmode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } else { + VAL = get_smbyte(ES, DI); + cmp_byte(AL, VAL); + DI += INC; + } + break; + + case 0xAF: /* SCAS dest-str16 */ + if (GET_FLAG(DF)) /* down */ + INC = -2; + else + INC = 2; + if (sysmode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) { + while (CX != 0) { + VAL = get_smword(ES, DI); + cmp_word(AX, VAL); + CX--; + DI += INC; + if (GET_FLAG(ZF) == 0) + break; + } + sysmode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } else { + VAL = get_smword(ES, DI); + cmp_byte(AL, VAL); + DI += INC; + } + break; + + case 0xB0: /* MOV AL,immed8 */ + AL = fetch_byte(1); + break; + + case 0xB1: /* MOV CL,immed8 */ + CL = fetch_byte(1); + break; + + case 0xB2: /* MOV DL,immed8 */ + DL = fetch_byte(1); + break; + + case 0xB3: /* MOV BL,immed8 */ + BL = fetch_byte(1); + break; + + case 0xB4: /* MOV AH,immed8 */ + AH = fetch_byte(1); + break; + + case 0xB5: /* MOV CH,immed8 */ + CH = fetch_byte(1); + break; + + case 0xB6: /* MOV DH,immed8 */ + DH = fetch_byte(1); + break; + + case 0xB7: /* MOV BH,immed8 */ + BH = fetch_byte(1); + break; + + case 0xB8: /* MOV AX,immed16 */ + AX = fetch_word(); + break; + + case 0xB9: /* MOV CX,immed16 */ + CX = fetch_word(); + break; + + case 0xBA: /* MOV DX,immed16 */ + DX = fetch_word(); + break; + + case 0xBB: /* MOV BX,immed16 */ + BX = fetch_word(); + break; + + case 0xBC: /* MOV SP,immed16 */ + SP = fetch_word(); + break; + + case 0xBD: /* MOV BP,immed16 */ + BP = fetch_word(); + break; + + case 0xBE: /* MOV SI,immed16 */ + SI = fetch_word(); + break; + + case 0xBF: /* MOV DI,immed16 */ + DI = fetch_word(); + break; + + /* 0xC0 - 0xC1 - Not implemented on 8086/8088 */ + + case 0xC2: /* RET immed16 (intrasegment) */ + OFF = fetch_word(); + IP = pop_word(); + SP += OFF; + break; + + case 0xC3: /* RET (intrasegment) */ + IP = pop_word(); + break; + + case 0xC4: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + put_rword(REG, get_smword(seg_reg, EA)); /* LES mem16 */ + ES = get_smword(seg_reg, EA + 2); + } else { +// put_rword(REG, get_rword(RM)); /* LES reg16 */ +// ES = get_rword(RM) + 2; + /* not defined for 8086 */ + reason = STOP_OPCODE; + IP -= 2; + } + break; + + case 0xC5: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + put_rword(REG, get_smword(seg_reg, EA)); /* LDS mem16 */ + DS = get_smword(seg_reg, EA + 2); + } else { +// put_rword(REG, get_rword(RM)); /* LDS reg16 */ +// DS = get_rword(RM) + 2; + /* not defined for 8086 */ + reason = STOP_OPCODE; + IP -= 2; + } + break; + + case 0xC6: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (REG) { /* has to be 0 */ + reason = STOP_OPCODE; + IP -= 2; + } + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + DATA = fetch_byte(1); /* has to be after DISP */ + put_smbyte(seg_reg, EA, DATA); /* MOV mem8, immed8 */ + } else { + put_rbyte(RM, DATA); /* MOV reg8, immed8 */ + } + break; + + case 0xC7: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (REG) { /* has to be 0 */ + reason = STOP_OPCODE; + IP -= 2; + } + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + DATA = get_mword(IP); /* has to be after DISP */ + put_smword(seg_reg, EA, DATA); /* MOV mem16, immed16 */ + } else { + put_rword(RM, DATA); /* MOV reg16, immed16 */ + } + break; + + /* 0xC8 - 0xC9 - Not implemented on 8086/8088 */ + + case 0xCA: /* RET immed16 (intersegment) */ + OFF = fetch_word(); + IP = pop_word(); + CS = pop_word(); + SP += OFF; + break; + + case 0xCB: /* RET (intersegment) */ + IP = pop_word(); + CS = pop_word(); + break; + + case 0xCC: /* INT 3 */ + push_word(PSW); + CLR_FLAG(IF); + CLR_FLAG(TF); + push_word(CS); + push_word(IP); + CS = get_mword(14); + IP = get_mword(12); + break; + + case 0xCD: /* INT immed8 */ + OFF = fetch_byte(1); + push_word(PSW); + CLR_FLAG(IF); + CLR_FLAG(TF); + push_word(CS); + push_word(IP); + CS = get_mword((OFF * 4) + 2); + IP = get_mword(OFF * 4); + break; + + case 0xCE: /* INTO */ + push_word(PSW); + CLR_FLAG(IF); + CLR_FLAG(TF); + push_word(CS); + push_word(IP); + CS = get_mword(18); + IP = get_mword(16); + break; + + case 0xCF: /* IRET */ + IP = pop_word(); + CS = pop_word(); + PSW = pop_word(); + break; + + case 0xD0: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + switch(REG) { + case 0: + VAL = rol_byte(get_smbyte(seg_reg, EA), 1); /* ROL mem8, 1 */ + break; + case 1: + VAL = ror_byte(get_smbyte(seg_reg, EA), 1); /* ROR mem8, 1 */ + break; + case 2: + VAL = rcl_byte(get_smbyte(seg_reg, EA), 1); /* RCL mem8, 1 */ + break; + case 3: + VAL = rcr_byte(get_smbyte(seg_reg, EA), 1); /* RCR mem8, 1 */ + break; + case 4: + VAL = shl_byte(get_smbyte(seg_reg, EA), 1); /* SAL/SHL mem8, 1 */ + break; + case 5: + VAL = shr_byte(get_smbyte(seg_reg, EA), 1); /* SHR mem8, 1 */ + break; + case 7: + VAL = sar_byte(get_smbyte(seg_reg, EA), 1); /* SAR mem8, 1 */ + break; + default: /* bad opcodes */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + put_rbyte(EA, VAL); /* store result */ + } else { /* RM is second register */ + switch(REG) { + case 0: + VAL = rol_byte(get_rbyte(RM), 1); /* RCL reg8, 1 */ + break; + case 1: + VAL = ror_byte(get_rbyte(RM), 1); /* ROR reg8, 1 */ + break; + case 2: + VAL = rcl_byte(get_rbyte(RM), 1); /* RCL reg8, 1 */ + break; + case 3: + VAL = rcr_byte(get_rbyte(RM), 1); /* RCR reg8, 1 */ + break; + case 4: + VAL = shl_byte(get_rbyte(RM), 1); /* SHL/SAL reg8, 1*/ + break; + case 5: + VAL = shr_byte(get_rbyte(RM), 1); /* SHR reg8, 1 */ + break; + case 7: + VAL = sar_byte(get_rbyte(RM), 1); /* SAR reg8, 1 */ + break; + default: /* bad opcodes */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + put_rbyte(RM, VAL); /* store result */ + } + break; + + case 0xD1: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + switch(REG) { + case 0: + VAL = rol_word(get_smword(seg_reg, EA), 1); /* ROL mem16, 1 */ + break; + case 1: + VAL = ror_word(get_smword(seg_reg, EA), 1); /* ROR mem16, 1 */ + break; + case 2: + VAL = rcl_word(get_smword(seg_reg, EA), 1); /* RCL mem16, 1 */ + break; + case 3: + VAL = rcr_word(get_smword(seg_reg, EA), 1); /* RCR mem16, 1 */ + break; + case 4: + VAL = shl_word(get_smword(seg_reg, EA), 1); /* SAL/SHL mem16, 1 */ + break; + case 5: + VAL = shr_word(get_smword(seg_reg, EA), 1); /* SHR mem16, 1 */ + break; + case 7: + VAL = sar_word(get_smword(seg_reg, EA), 1); /* SAR mem16, 1 */ + break; + default: /* bad opcodes */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + put_rword(EA, VAL); /* store result */ + } else { /* RM is second register */ + switch(REG) { + case 0: + VAL = rol_word(get_rword(RM), 1); /* RCL reg16, 1 */ + break; + case 1: + VAL = ror_word(get_rword(RM), 1); /* ROR reg16, 1 */ + break; + case 2: + VAL = rcl_word(get_rword(RM), 1); /* RCL reg16, 1 */ + break; + case 3: + VAL = rcr_word(get_rword(RM), 1); /* RCR reg16, 1 */ + break; + case 4: + VAL = shl_word(get_rword(RM), 1); /* SHL/SAL reg16, 1 */ + break; + case 5: + VAL = shr_word(get_rword(RM), 1); /* SHR reg16, 1 */ + break; + case 7: + VAL = sar_word(get_rword(RM), 1); /* SAR reg16, 1 */ + break; + default: /* bad opcodes */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + put_rbyte(RM, VAL); /* store result */ + } + break; + + case 0xD2: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + switch(REG) { + case 0: + VAL = rol_byte(get_smbyte(seg_reg, EA), CL); /* ROL mem8, CL */ + break; + case 1: + VAL = ror_byte(get_smbyte(seg_reg, EA), CL); /* ROR mem8, CL */ + break; + case 2: + VAL = rcl_byte(get_smbyte(seg_reg, EA), CL); /* RCL mem8, CL */ + break; + case 3: + VAL = rcr_byte(get_smbyte(seg_reg, EA), CL); /* RCR mem8, CL */ + break; + case 4: + VAL = shl_byte(get_smbyte(seg_reg, EA), CL); /* SAL/SHL mem8, CL */ + break; + case 5: + VAL = shr_byte(get_smbyte(seg_reg, EA), CL); /* SHR mem8, CL */ + break; + case 7: + VAL = sar_byte(get_smbyte(seg_reg, EA), CL); /* SAR mem8, CL */ + break; + default: /* bad opcodes */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + put_rbyte(EA, VAL); /* store result */ + } else { /* RM is second register */ + switch(REG) { + case 0: + VAL = rol_byte(get_rbyte(RM), CL); /* RCL reg8, CL */ + break; + case 1: + VAL = ror_byte(get_rbyte(RM), CL); /* ROR reg8, CL */ + break; + case 2: + VAL = rcl_byte(get_rbyte(RM), CL); /* RCL reg8, CL */ + break; + case 3: + VAL = rcr_byte(get_rbyte(RM), CL); /* RCR reg8, CL */ + break; + case 4: + VAL = shl_byte(get_rbyte(RM), CL); /* SHL/SAL reg8, CL*/ + break; + case 5: + VAL = shr_byte(get_rbyte(RM), CL); /* SHR reg8, CL */ + break; + case 7: + VAL = sar_byte(get_rbyte(RM), CL); /* SAR reg8, CL */ + break; + default: /* bad opcodes */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + put_rbyte(RM, VAL); /* store result */ + } + break; + + case 0xD3: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + switch(REG) { + case 0: + VAL = rol_word(get_smword(seg_reg, EA), CL); /* ROL mem16, CL */ + break; + case 1: + VAL = ror_word(get_smword(seg_reg, EA), CL); /* ROR mem16, CL */ + break; + case 2: + VAL = rcl_word(get_smword(seg_reg, EA), CL); /* RCL mem16, CL */ + break; + case 3: + VAL = rcr_word(get_smword(seg_reg, EA), CL); /* RCR mem16, CL */ + break; + case 4: + VAL = shl_word(get_smword(seg_reg, EA), CL); /* SAL/SHL mem16, CL */ + break; + case 5: + VAL = shr_word(get_smword(seg_reg, EA), CL); /* SHR mem16, CL */ + break; + case 7: + VAL = sar_word(get_smword(seg_reg, EA), CL); /* SAR mem16, CL */ + break; + default: /* bad opcodes */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + put_rword(EA, VAL); /* store result */ + } else { /* RM is second register */ + switch(REG) { + case 0: + VAL = rol_word(get_rword(RM), CL); /* RCL reg16, CL */ + break; + case 1: + VAL = ror_word(get_rword(RM), CL); /* ROR reg16, CL */ + break; + case 2: + VAL = rcl_word(get_rword(RM), CL); /* RCL reg16, CL */ + break; + case 3: + VAL = rcr_word(get_rword(RM), CL); /* RCR reg16, CL */ + break; + case 4: + VAL = shl_word(get_rword(RM), CL); /* SHL/SAL reg16, CL */ + break; + case 5: + VAL = shr_word(get_rword(RM), CL); /* SHR reg16, CL */ + break; + case 7: + VAL = sar_word(get_rword(RM), CL); /* SAR reg16, CL */ + break; + default: /* bad opcodes */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + put_rbyte(RM, VAL); /* store result */ + } + break; + + case 0xD4: /* AAM */ + VAL = fetch_word(); + if (VAL != 10) { + reason = STOP_OPCODE; + IP -= 2; + } + /* note the type change here --- returning AL and AH in AX. */ + AX = aam_word(AL); + break; + + case 0xD5: /* AAD */ + VAL = fetch_word(); + if (VAL != 10) { + reason = STOP_OPCODE; + IP -= 2; + } + AX = aad_word(AX); + break; + + /* 0xD6 - Not implemented on 8086/8088 */ + + case 0xD7: /* XLAT */ + OFF = BX + (uint8)AL; + AL = get_smbyte(SEG_CS, OFF); + break; + + case 0xD8: /* ESC */ + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: + /* for now, do nothing, NOP for 8088 */ + break; + + case 0xE0: /* LOOPNE label */ + OFF = fetch_byte(1); + OFF += (int16)IP; + CX -= 1; + if (CX != 0 && !GET_FLAG(ZF)) /* CX != 0 and !ZF */ + IP = OFF; + break; + + case 0xE1: /* LOOPE label */ + OFF = fetch_byte(1); + OFF += (int16)IP; + CX -= 1; + if (CX != 0 && GET_FLAG(ZF)) /* CX != 0 and ZF */ + IP = OFF; + break; + + case 0xE2: /* LOOP label */ + OFF = fetch_byte(1); + OFF += (int16)IP; + CX -= 1; + if (CX != 0) /* CX != 0 */ + IP = OFF; + break; + + case 0xE3: /* JCXZ label */ + OFF = fetch_byte(1); + OFF += (int16)IP; + if (CX == 0) /* CX != 0 */ + IP = OFF; + break; + + case 0xE4: /* IN AL,immed8 */ + OFF = fetch_byte(1); + AL = dev_table[OFF].routine(0, 0); + break; + + case 0xE5: /* IN AX,immed8 */ + OFF = fetch_byte(1); + AH = dev_table[OFF].routine(0, 0); + AL = dev_table[OFF+1].routine(0, 0); + break; + + case 0xE6: /* OUT AL,immed8 */ + OFF = fetch_byte(1); + dev_table[OFF].routine(1, AL); + break; + + case 0xE7: /* OUT AX,immed8 */ + OFF = fetch_byte(1); + dev_table[OFF].routine(1, AH); + dev_table[OFF+1].routine(1, AL); + break; + + case 0xE8: /* CALL NEAR proc */ + OFF = fetch_word(); + push_word(IP); + IP = (OFF + IP) & ADDRMASK16; + break; + + case 0xE9: /* JMP NEAR label */ + OFF = fetch_word(); + IP = (OFF + IP) & ADDRMASK16; + break; + + case 0xEA: /* JMP FAR label */ + OFF = fetch_word(); + SEG = fetch_word(); + CS = SEG; + IP = OFF; + break; + + case 0xEB: /* JMP short-label */ + OFF = fetch_byte(1); + if (OFF & 0x80) /* if negative, sign extend */ + OFF |= 0XFF00; + IP = (IP + OFF) & ADDRMASK16; + break; + + case 0xEC: /* IN AL,DX */ + AL = dev_table[DX].routine(0, 0); + break; + + case 0xED: /* IN AX,DX */ + AH = dev_table[DX].routine(0, 0); + AL = dev_table[DX+1].routine(0, 0); + break; + + case 0xEE: /* OUT AL,DX */ + dev_table[DX].routine(1, AL); + break; + + case 0xEF: /* OUT AX,DX */ + dev_table[DX].routine(1, AH); + dev_table[DX+1].routine(1, AL); + break; + + case 0xF0: /* LOCK */ + /* do nothing for now */ + break; + + /* 0xF1 - Not implemented on 8086/8088 */ + + case 0xF2: /* REPNE/REPNZ */ + sysmode |= SYSMODE_PREFIX_REPNE; + break; + + case 0xF3: /* REP/REPE/REPZ */ + sysmode |= SYSMODE_PREFIX_REPE; + break; + + case 0xF4: /* HLT */ + reason = STOP_HALT; + IP--; + break; + + case 0xF5: /* CMC */ + TOGGLE_FLAG(CF); + break; + + case 0xF6: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + switch(REG) { + case 0: /* TEST mem8, immed8 */ + DATA = fetch_byte(1); + test_byte(get_smbyte(seg_reg, EA), DATA); + break; + case 2: /* NOT mem8 */ + VAL = not_byte(get_smbyte(seg_reg, EA)); + put_smbyte(seg_reg, EA, VAL); /* store result */ + break; + case 3: /* NEG mem8 */ + VAL = neg_byte(get_smbyte(seg_reg, EA)); + put_smbyte(seg_reg, EA, VAL); /* store result */ + break; + case 4: /* MUL mem8 */ + mul_byte(get_smbyte(seg_reg, EA)); + break; + case 5: /* IMUL mem8 */ + imul_byte(get_smbyte(seg_reg, EA)); + break; + case 6: /* DIV mem8 */ + div_byte(get_smbyte(seg_reg, EA)); + break; + case 7: /* IDIV mem8 */ + idiv_byte(get_smbyte(seg_reg, EA)); + break; + default: /* bad opcode */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + } else { /* RM is second register */ + switch(REG) { + case 0: /* TEST reg8, immed8 */ + DATA = fetch_byte(1); + test_byte(get_rbyte(RM), DATA); + break; + case 2: /* NOT reg8 */ + VAL = not_byte(get_rbyte(RM)); + put_rbyte(RM, VAL); /* store result */ + break; + case 3: /* NEG reg8 */ + VAL = neg_byte(get_rbyte(RM)); + put_rbyte(RM, VAL); /* store result */ + break; + case 4: /* MUL reg8 */ + mul_byte(get_rbyte(RM)); + break; + case 5: /* IMUL reg8 */ + imul_byte(get_rbyte(RM)); + break; + case 6: /* DIV reg8 */ + div_byte(get_rbyte(RM)); + break; + case 7: /* IDIV reg8 */ + idiv_byte(get_rbyte(RM)); + break; + default: /* bad opcode */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + put_rbyte(RM, VAL); /* store result */ + } + break; + + case 0xF7: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + switch(REG) { + case 0: /* TEST mem16, immed16 */ + DATA = fetch_word(); + test_word(get_smword(seg_reg, EA), DATA); + break; + case 2: /* NOT mem16 */ + VAL = not_word(get_smword(seg_reg, EA)); + put_smword(seg_reg, EA, VAL); /* store result */ + break; + case 3: /* NEG mem16 */ + VAL = neg_word(get_smword(seg_reg, EA)); + put_smword(seg_reg, EA, VAL); /* store result */ + break; + case 4: /* MUL mem16 */ + mul_word(get_smword(seg_reg, EA)); + break; + case 5: /* IMUL mem16 */ + imul_word(get_smword(seg_reg, EA)); + break; + case 6: /* DIV mem16 */ + div_word(get_smword(seg_reg, EA)); + break; + case 7: /* IDIV mem16 */ + idiv_word(get_smword(seg_reg, EA)); + break; + default: /* bad opcode */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + } else { /* RM is second register */ + switch(REG) { + case 0: /* TEST reg16, immed16 */ + DATA = fetch_word(); + test_word(get_rword(RM), DATA); + break; + case 2: /* NOT reg16 */ + VAL = not_word(get_rword(RM)); + put_rword(RM, VAL); /* store result */ + break; + case 3: /* NEG reg16 */ + VAL = neg_word(get_rword(RM)); + put_rword(RM, VAL); /* store result */ + break; + case 4: /* MUL reg16 */ + mul_word(get_rword(RM)); + break; + case 5: /* IMUL reg16 */ + imul_word(get_rword(RM)); + break; + case 6: /* DIV reg16 */ + div_word(get_rword(RM)); + break; + case 7: /* IDIV reg16 */ + idiv_word(get_rword(RM)); + break; + default: /* bad opcode */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + put_rbyte(RM, VAL); /* store result */ + } + break; + + case 0xF8: /* CLC */ + CLR_FLAG(CF); + break; + + case 0xF9: /* STC */ + SET_FLAG(CF); + break; + + case 0xFA: /* CLI */ + CLR_FLAG(IF); + break; + + case 0xFB: /* STI */ + SET_FLAG(IF); + break; + + case 0xFC: /* CLD */ + CLR_FLAG(DF); + break; + + case 0xFD: /* STD */ + SET_FLAG(DF); + break; + + case 0xFE: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + switch(REG) { + case 0: /* INC mem16 */ + VAL = inc_byte(get_smbyte(seg_reg, EA)); /* do operation */ + put_smbyte(seg_reg, EA, VAL); /* store result */ + break; + case 1: /* DEC mem16 */ + VAL = dec_byte(get_smbyte(seg_reg, EA)); /* do operation */ + put_smbyte(seg_reg, EA, VAL); /* store result */ + break; + default: /* bad opcodes */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + } else { /* RM is second register */ + switch(REG) { + case 0: + VAL = inc_byte(get_rbyte(RM)); /* do operation */ + put_rbyte(RM, VAL); /* store result */ + break; + case 1: + VAL = dec_byte(get_rbyte(RM)); /* do operation */ + put_rbyte(RM, VAL); /* store result */ + break; + default: /* bad opcodes */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + put_rbyte(RM, VAL); /* store result */ + } + break; + + case 0xFF: + MRR = fetch_byte(1); + get_mrr_dec(MRR, &MOD, ®, &RM); + if (MOD != 0x3) { /* based, indexed, or based indexed addressing */ + EA = get_ea(MRR); /* get effective address */ + switch(REG) { + case 0: /* INC mem16 */ + VAL = inc_word(get_smword(seg_reg, EA)); /* do operation */ + put_smword(seg_reg, EA, VAL); /* store result */ + break; + case 1: /* DEC mem16 */ + VAL = dec_word(get_smword(seg_reg, EA)); /* do operation */ + put_smword(seg_reg, EA, VAL); /* store result */ + break; + case 2: /* CALL NEAR mem16 */ + OFF = get_smword(SEG_CS, EA); /* do operation */ + push_word(IP); + IP = OFF; + break; + case 3: /* CALL FAR mem16 */ + OFF = get_smword(SEG_CS, EA); /* do operation */ + SEG = get_smword(SEG_CS, EA + 2); + push_word(CS); + CS = SEG; + push_word(IP); + IP = OFF; + break; + case 4: /* JMP NEAR mem16 */ + OFF = get_smword(SEG_CS, EA); /* do operation */ + IP = OFF; + break; + case 5: /* JMP FAR mem16 */ + OFF = get_smword(SEG_CS, EA); /* do operation */ + SEG = get_smword(SEG_CS, EA + 2); + CS = SEG; + IP = OFF; + break; + case 6: /* PUSH mem16 */ + VAL = get_smword(seg_reg, EA); /* do operation */ + push_word(VAL); + break; + case 7: /* bad opcode */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + } else { /* RM is second register */ + switch(REG) { + case 2: /* CALL NEAR reg16 */ + OFF = get_rword(RM); /* do operation */ + push_word(IP); + IP = OFF; + break; + case 4: /* JMP NEAR reg16 */ + OFF = get_rword(RM); /* do operation */ + IP = OFF; + break; + default: /* bad opcode */ + reason = STOP_OPCODE; + IP -= 2; + break; + } + put_rbyte(RM, VAL); /* store result */ + } + break; + + + default: +// if (i8088_unit.flags & UNIT_OPSTOP) { + reason = STOP_OPCODE; + IP--; +// } + break; + } + /* not segment override */ + if ((IR == 0x26) || (IR == 0x2E) || (IR == 0x36) || (IR == 0x3E)) { + seg_ovr = SEG_NONE; /* clear segment override */ + sysmode &= 0x0000001E; /* clear flags */ + sysmode |= 0x00000001; + } + } + +/* Simulation halted */ + + saved_PC = IP; + return reason; +} + +/* emulation subfunctions */ + +int32 sign_ext(int32 val) +{ + int32 res; + + res = val; + if (val & 0x80) + res |= 0xFF00; + return res; +} + + +int32 fetch_byte(int32 flag) +{ + uint8 val; + + val = get_smbyte(SEG_CS, IP) & 0xFF; /* fetch byte */ + if (i8088_dev.dctrl & DEBUG_asm) { /* display source code */ + switch (flag) { + case 0: /* opcode fetch */ +// printf("%04X:%04X %s", CS, IP, opcode[val]); + printf("%04X:%04X %02X", CS, IP, val); + break; + case 1: /* byte operand fetch */ + printf(" %02X", val); + break; + } + } + IP = INC_IP1; /* increment IP */ + return val; +} + +int32 fetch_word(void) +{ + uint16 val; + + val = get_smbyte(SEG_CS, IP) & 0xFF; /* fetch low byte */ + val |= get_smbyte(SEG_CS, IP + 1) << 8; /* fetch high byte */ + if (i8088_dev.dctrl & DEBUG_asm) +// printf("0%04XH", val); + printf(" %04X", val); + IP = INC_IP2; /* increment IP */ + return val; +} + +/* calculate parity on a 8- or 16-bit value */ + +int32 parity(int32 val) +{ + int32 bc = 0; + + if (val & 0x0001) bc++; + if (val & 0x0002) bc++; + if (val & 0x0004) bc++; + if (val & 0x0008) bc++; + if (val & 0x0010) bc++; + if (val & 0x0020) bc++; + if (val & 0x0040) bc++; + if (val & 0x0080) bc++; + if (val & 0x0100) bc++; + if (val & 0x0200) bc++; + if (val & 0x0400) bc++; + if (val & 0x0800) bc++; + if (val & 0x1000) bc++; + if (val & 0x2000) bc++; + if (val & 0x4000) bc++; + if (val & 0x8000) bc++; + return bc & 1; +} + +void i86_intr_raise(uint8 num) +{ + /* do nothing for now */ +} + +/* return byte register */ + +uint32 get_rbyte(uint32 reg) +{ + uint32 val; + + switch(reg) { + case 0: val = AL; break; + case 1: val = CL; break; + case 2: val = DL; break; + case 3: val = BL; break; + case 4: val = AH; break; + case 5: val = CH; break; + case 6: val = DH; break; + case 7: val = BH; break; + } + return val; +} + +/* return word register - added segment registers as 8-11 */ + +uint32 get_rword(uint32 reg) +{ + uint32 val; + + switch(reg) { + case 0: val = AX; break; + case 1: val = CX; break; + case 2: val = DX; break; + case 3: val = BX; break; + case 4: val = SP; break; + case 5: val = BP; break; + case 6: val = SI; break; + case 7: val = DI; break; + case 8: val = CS; break; + case 9: val = DS; break; + case 10: val = ES; break; + case 11: val = SS; break; + } + return val; +} + +/* set byte register */ + +void put_rbyte(uint32 reg, uint32 val) +{ + val &= 0xFF; /* force byte */ + switch(reg){ + case 0: AL = val; break; + case 1: CL = val; break; + case 2: DL = val; break; + case 3: BL = val; break; + case 4: AH = val; break; + case 5: CH = val; break; + case 6: DH = val; break; + case 7: BH = val; break; + } +} + +/* set word register */ + +void put_rword(uint32 reg, uint32 val) +{ + val &= 0xFFFF; /* force word */ + switch(reg){ + case 0: AX = val; break; + case 1: CX = val; break; + case 2: DX = val; break; + case 3: BX = val; break; + case 4: SP = val; break; + case 5: BP = val; break; + case 6: SI = val; break; + case 7: DI = val; break; + } +} + +/* set seg_reg as required for EA */ + +void set_segreg(uint32 reg) +{ + if (seg_ovr) + seg_reg = seg_ovr; + else + seg_ovr = reg; +} + +/* return effective address from mrr - also set seg_reg */ + +uint32 get_ea(uint32 mrr) +{ + uint32 MOD, REG, RM, DISP, EA; + + get_mrr_dec(mrr, &MOD, ®, &RM); + switch(MOD) { + case 0: /* DISP = 0 */ + DISP = 0; + switch(RM) { + case 0: + EA = BX + SI; + set_segreg(SEG_DS); + break; + case 1: + EA = BX + DI; + set_segreg(SEG_DS); + break; + case 2: + EA = BP + SI; + set_segreg(SEG_SS); + break; + case 3: + EA = BP + DI; + set_segreg(SEG_SS); + break; + case 4: + EA = SI; + set_segreg(SEG_DS); + break; + case 5: + EA = DI; + set_segreg(SEG_ES); + break; + case 6: + DISP = fetch_word(); + EA = DISP; + set_segreg(SEG_DS); + break; + case 7: + EA = BX; + set_segreg(SEG_DS); + break; + } + break; + case 1: /* DISP is byte */ + DISP = fetch_byte(1); + switch(RM) { + case 0: + EA = BX + SI + DISP; + set_segreg(SEG_DS); + break; + case 1: + EA = BX + DI + DISP; + set_segreg(SEG_DS); + break; + case 2: + EA = BP + SI + DISP; + set_segreg(SEG_SS); + break; + case 3: + EA = BP + DI + DISP; + set_segreg(SEG_SS); + break; + case 4: + EA = SI + DISP; + set_segreg(SEG_DS); + break; + case 5: + EA = DI + DISP; + set_segreg(SEG_ES); + break; + case 6: + EA = BP + DISP; + set_segreg(SEG_SS); + break; + case 7: + EA = BX + DISP; + set_segreg(SEG_DS); + break; + } + break; + case 2: /* DISP is word */ + DISP = fetch_word(); + switch(RM) { + case 0: + EA = BX + SI + DISP; + set_segreg(SEG_DS); + break; + case 1: + EA = BX + DI + DISP; + set_segreg(SEG_DS); + break; + case 2: + EA = BP + SI + DISP; + set_segreg(SEG_SS); + break; + case 3: + EA = BP + DI + DISP; + set_segreg(SEG_SS); + break; + case 4: + EA = SI + DISP; + set_segreg(SEG_DS); + break; + case 5: + EA = DI + DISP; + set_segreg(SEG_ES); + break; + case 6: + EA = BP + DISP; + set_segreg(SEG_SS); + break; + case 7: + EA = BX + DISP; + set_segreg(SEG_SS); + break; + } + break; + case 3: /* RM is register field */ + break; + } + if (i8088_dev.dctrl & DEBUG_level1) + printf("get_ea: MRR=%02X MOD=%02X REG=%02X R/M=%02X DISP=%04X EA=%04X\n", + mrr, MOD, REG, RM, DISP, EA); + return EA; +} +/* return mod, reg and rm field from mrr */ + +void get_mrr_dec(uint32 mrr, uint32 *mod, uint32 *reg, uint32 *rm) +{ + *mod = (mrr >> 6) & 0x3; + *reg = (mrr >> 3) & 0x7; + *rm = mrr & 0x7; +} + +/* + Most of the primitive algorythms were pulled from the GDE Dos/IP Emulator by Jim Hudgens +*/ + +/* aad primitive */ +uint8 aad_word(uint16 d) +{ + uint16 VAL; + uint8 HI, LOW; + + HI = (d >> 8) & 0xFF; + LOW = d & 0xFF; + VAL = LOW + 10 * HI; + CONDITIONAL_SET_FLAG(VAL & 0x80, SF); + CONDITIONAL_SET_FLAG(VAL == 0, ZF); + CONDITIONAL_SET_FLAG(parity(VAL & 0xFF), PF); + return (uint8) VAL; +} + +/* aam primitive */ +uint16 aam_word(uint8 d) +{ + uint16 VAL, HI; + + HI = d / 10; + VAL = d % 10; + VAL |= (HI << 8); + CONDITIONAL_SET_FLAG(VAL & 0x80, SF); + CONDITIONAL_SET_FLAG(VAL == 0, ZF); + CONDITIONAL_SET_FLAG(parity(VAL & 0xFF), PF); + return VAL; +} + +/* add with carry byte primitive */ +uint8 adc_byte(uint8 d, uint8 s) +{ + register uint16 res; + register uint16 cc; + + if (GET_FLAG(CF)) + res = 1 + d + s; + else + res = d + s; + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG(res & 0x0100, CF); + CONDITIONAL_SET_FLAG((res & 0xff) == 0, ZF); + CONDITIONAL_SET_FLAG(res & 0x80, SF); + CONDITIONAL_SET_FLAG(parity(res & 0xff), PF); + /* calculate the carry chain SEE NOTE AT TOP.*/ + cc = (s & d) | ((~res) & (s | d)); + /* set the flags based on the carry chain */ + CONDITIONAL_SET_FLAG(xor_3_tab[(cc >> 6) & 0x3], OF); + CONDITIONAL_SET_FLAG(cc & 0x08, AF); + return (uint8) res; +} + +/* add with carry word primitive */ +uint16 adc_word(uint16 d, uint16 s) +{ + register uint32 res; + register uint32 cc; + + if (GET_FLAG(CF)) + res = 1 + d + s; + else + res = d + s; + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG(res & 0x10000, CF); + CONDITIONAL_SET_FLAG((res & 0xFFFF) == 0, ZF); + CONDITIONAL_SET_FLAG(res & 0x8000, SF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + /* calculate the carry chain SEE NOTE AT TOP.*/ + cc = (s & d) | ((~res) & (s | d)); + /* set the flags based on the carry chain */ + CONDITIONAL_SET_FLAG(xor_3_tab[(cc >> 14) & 0x3], OF); + CONDITIONAL_SET_FLAG(cc & 0x08, AF); + return res; +} + +/* add byte primitive */ +uint8 add_byte(uint8 d, uint8 s) +{ + register uint16 res; + register uint16 cc; + + res = d + s; + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG(res & 0x0100, CF); + CONDITIONAL_SET_FLAG((res & 0xFF) == 0, ZF); + CONDITIONAL_SET_FLAG(res & 0x80, SF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + /* calculate the carry chain SEE NOTE AT TOP.*/ + cc = (s & d) | ((~res) & (s | d)); + /* set the flags based on the carry chain */ + CONDITIONAL_SET_FLAG(xor_3_tab[(cc >> 6) & 0x3], OF); + CONDITIONAL_SET_FLAG(cc & 0x08, AF); + return (uint8) res; +} + +/* add word primitive */ +uint16 add_word(uint16 d, uint16 s) +{ + register uint32 res; + register uint32 cc; + + res = d + s; + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG(res & 0x10000, CF); + CONDITIONAL_SET_FLAG((res & 0xFFFF) == 0, ZF); + CONDITIONAL_SET_FLAG(res & 0x8000, SF); + CONDITIONAL_SET_FLAG(parity(res & 0xff), PF); + /* calculate the carry chain SEE NOTE AT TOP.*/ + cc = (s & d) | ((~res) & (s | d)); + /* set the flags based on the carry chain */ + CONDITIONAL_SET_FLAG(xor_3_tab[(cc >> 14) & 0x3], OF); + CONDITIONAL_SET_FLAG(cc & 0x08, AF); + return res; +} + +/* and byte primitive */ +uint8 and_byte(uint8 d, uint8 s) +{ + register uint8 res; + res = d & s; + + /* clear flags */ + CLR_FLAG(OF); + CLR_FLAG(CF); + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG(res & 0x80, SF); + CONDITIONAL_SET_FLAG(res == 0, ZF); + CONDITIONAL_SET_FLAG(parity(res), PF); + return res; +} + +/* and word primitive */ +uint16 and_word(uint16 d, uint16 s) +{ + register uint16 res; + res = d & s; + + /* clear flags */ + CLR_FLAG(OF); + CLR_FLAG(CF); + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG(res & 0x8000, SF); + CONDITIONAL_SET_FLAG(res == 0, ZF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + return res; +} + +/* cmp byte primitive */ +uint8 cmp_byte(uint8 d, uint8 s) +{ + register uint32 res; + register uint32 bc; + + res = d - s; + /* clear flags */ + CLR_FLAG(CF); + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG(res & 0x80, SF); + CONDITIONAL_SET_FLAG((res & 0xFF)==0, ZF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + /* calculate the borrow chain. See note at top */ + bc= (res&(~d|s))|(~d&s); + /* set flags based on borrow chain */ + CONDITIONAL_SET_FLAG(bc & 0x80, CF); + CONDITIONAL_SET_FLAG(xor_3_tab[(bc >> 6) & 0x3], OF); + CONDITIONAL_SET_FLAG(bc & 0x8, AF); + return d; /* long story why this is needed. Look at opcode + 0x80 in ops.c, for an idea why this is necessary.*/ +} + +/* cmp word primitive */ +uint16 cmp_word(uint16 d, uint16 s) +{ + register uint32 res; + register uint32 bc; + + res = d - s; + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG(res & 0x8000, SF); + CONDITIONAL_SET_FLAG((res & 0xFFFF) == 0, ZF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + /* calculate the borrow chain. See note at top */ + bc = (res & (~d | s)) | (~d &s); + /* set flags based on borrow chain */ + CONDITIONAL_SET_FLAG(bc & 0x8000, CF); + CONDITIONAL_SET_FLAG(xor_3_tab[(bc >> 14) & 0x3], OF); + CONDITIONAL_SET_FLAG(bc & 0x8, AF); + return d; /* long story why this is needed. Look at opcode + 0x80 in ops.c, for an idea why this is necessary.*/ +} + +/* dec byte primitive */ +uint8 dec_byte(uint8 d) +{ + register uint32 res; + register uint32 bc; + + res = d - 1; + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG(res & 0x80, SF); + CONDITIONAL_SET_FLAG((res & 0xff)==0, ZF); + CONDITIONAL_SET_FLAG(parity(res & 0xff), PF); + /* calculate the borrow chain. See note at top */ + /* based on sub_byte, uses s=1. */ + bc = (res & (~d | 1)) | (~d & 1); + /* carry flag unchanged */ + /* set flags based on borrow chain */ + CONDITIONAL_SET_FLAG(xor_3_tab[(bc >> 6) & 0x3], OF); + CONDITIONAL_SET_FLAG(bc & 0x8, AF); + return res; +} + +/* dec word primitive */ +uint16 dec_word(uint16 d) +{ + register uint32 res; + register uint32 bc; + + res = d - 1; + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG(res & 0x8000, SF); + CONDITIONAL_SET_FLAG((res & 0xffff) == 0, ZF); + CONDITIONAL_SET_FLAG(parity(res & 0xff), PF); + /* calculate the borrow chain. See note at top */ + /* based on the sub_byte routine, with s==1 */ + bc = (res & (~d | 1)) | (~d & 1); + /* carry flag unchanged */ + /* set flags based on borrow chain */ + CONDITIONAL_SET_FLAG(xor_3_tab[(bc >> 14) & 0x3], OF); + CONDITIONAL_SET_FLAG(bc & 0x8, AF); + return res; +} + +/* div byte primitive */ +void div_byte(uint8 s) +{ + uint32 dvd, dvs, div, mod; + + dvs = s; + dvd = AX; + if (s == 0) { + i86_intr_raise(0); + return; + } + div = dvd / dvs; + mod = dvd % dvs; + if (abs(div) > 0xFF) { + i86_intr_raise(0); + return; + } + /* Undef --- Can't hurt */ + CLR_FLAG(SF); + CONDITIONAL_SET_FLAG(div == 0, ZF); + AL = (uint8)div; + AH = (uint8)mod; +} + +/* div word primitive */ +void div_word(uint16 s) +{ + uint32 dvd, dvs, div, mod; + + dvd = DX; + dvd = (dvd << 16) | AX; + dvs = s; + if (dvs == 0) { + i86_intr_raise(0); + return; + } + div = dvd / dvs; + mod = dvd % dvs; + if (abs(div) > 0xFFFF) { + i86_intr_raise(0); + return; + } + /* Undef --- Can't hurt */ + CLR_FLAG(SF); + CONDITIONAL_SET_FLAG(div == 0, ZF); + AX = div; + DX = mod; +} + +/* idiv byte primitive */ +void idiv_byte(uint8 s) +{ + int32 dvd, div, mod; + + dvd = (int16)AX; + if (s == 0) { + i86_intr_raise(0); + return; + } + div = dvd / (int8)s; + mod = dvd % (int8)s; + if (abs(div) > 0x7F) { + i86_intr_raise(0); + return; + } + /* Undef --- Can't hurt */ + CONDITIONAL_SET_FLAG(div & 0x80, SF); + CONDITIONAL_SET_FLAG(div == 0, ZF); + AL = (int8)div; + AH = (int8)mod; +} + +/* idiv word primitive */ +void idiv_word(uint16 s) +{ + int32 dvd, dvs, div, mod; + + dvd = DX; + dvd = (dvd << 16) | AX; + if (s == 0) { + i86_intr_raise(0); + return; + } + dvs = (int16)s; + div = dvd / dvs; + mod = dvd % dvs; + if (abs(div) > 0x7FFF) { + i86_intr_raise(0); + return; + } + /* Undef --- Can't hurt */ + CONDITIONAL_SET_FLAG(div & 0x8000, SF); + CONDITIONAL_SET_FLAG(div == 0, ZF); + AX = div; + DX = mod; +} + +/* imul byte primitive */ +void imul_byte(uint8 s) +{ + int16 res; + + res = (int8)AL * (int8)s; + AX = res; + /* Undef --- Can't hurt */ + CONDITIONAL_SET_FLAG(res & 0x8000, SF); + CONDITIONAL_SET_FLAG(res == 0, ZF); + if ( AH == 0 || AH == 0xFF) { + CLR_FLAG(CF); + CLR_FLAG(OF); + } else { + SET_FLAG(CF); + SET_FLAG(OF); + } +} + +/* imul word primitive */ +void imul_word(uint16 s) +{ + int32 res; + + res = (int16)AX * (int16)s; + AX = res & 0xFFFF; + DX = (res >> 16) & 0xFFFF; + /* Undef --- Can't hurt */ + CONDITIONAL_SET_FLAG(res & 0x80000000, SF); + CONDITIONAL_SET_FLAG(res == 0, ZF); + if (DX == 0 || DX == 0xFFFF) { + CLR_FLAG(CF); + CLR_FLAG(OF); + } else { + SET_FLAG(CF); + SET_FLAG(OF); + } +} + +/* inc byte primitive */ +uint8 inc_byte(uint8 d) +{ + register uint32 res; + register uint32 cc; + + res = d + 1; + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG((res & 0xFF) == 0, ZF); + CONDITIONAL_SET_FLAG(res & 0x80, SF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + /* calculate the carry chain SEE NOTE AT TOP.*/ + cc = ((1 & d) | (~res)) & (1 | d); + /* set the flags based on the carry chain */ + CONDITIONAL_SET_FLAG(xor_3_tab[(cc >> 6) & 0x3], OF); + CONDITIONAL_SET_FLAG(cc & 0x8, AF); + return res; +} + +/* inc word primitive */ +uint16 inc_word(uint16 d) +{ + register uint32 res; + register uint32 cc; + + res = d + 1; + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG((res & 0xFFFF) == 0, ZF); + CONDITIONAL_SET_FLAG(res & 0x8000, SF); + CONDITIONAL_SET_FLAG(parity(res & 0xff), PF); + /* calculate the carry chain SEE NOTE AT TOP.*/ + cc = (1 & d) | ((~res) & (1 | d)); + /* set the flags based on the carry chain */ + CONDITIONAL_SET_FLAG(xor_3_tab[(cc >> 14) & 0x3], OF); + CONDITIONAL_SET_FLAG(cc & 0x8, AF); + return res ; +} + +/* mul byte primitive */ +void mul_byte(uint8 s) +{ + uint16 res; + + res = AL * s; + AX = res; + /* Undef --- Can't hurt */ + CLR_FLAG(SF); + CONDITIONAL_SET_FLAG(res == 0, ZF); + if (AH == 0) { + CLR_FLAG(CF); + CLR_FLAG(OF); + } else { + SET_FLAG(CF); + SET_FLAG(OF); + } +} + +/* mul word primitive */ +void mul_word(uint16 s) +{ + uint32 res; + + res = AX * s; + /* Undef --- Can't hurt */ + CLR_FLAG(SF); + CONDITIONAL_SET_FLAG(res == 0, ZF); + AX = res & 0xFFFF; + DX = (res >> 16) & 0xFFFF; + if (DX == 0) { + CLR_FLAG(CF); + CLR_FLAG(OF); + } else { + SET_FLAG(CF); + SET_FLAG(OF); + } +} + +/* neg byte primitive */ +uint8 neg_byte(uint8 s) +{ + register uint8 res; + register uint8 bc; + + CONDITIONAL_SET_FLAG(s != 0, CF); + res = -s; + CONDITIONAL_SET_FLAG((res & 0xff) == 0, ZF); + CONDITIONAL_SET_FLAG(res & 0x80, SF); + CONDITIONAL_SET_FLAG(parity(res), PF); + /* calculate the borrow chain --- modified such that d=0. */ + bc= res | s; + CONDITIONAL_SET_FLAG(xor_3_tab[(bc >> 6) & 0x3], OF); + CONDITIONAL_SET_FLAG(bc & 0x8, AF); + return res; +} + +/* neg word primitive */ +uint16 neg_word(uint16 s) +{ + register uint16 res; + register uint16 bc; + + CONDITIONAL_SET_FLAG(s != 0, CF); + res = -s; + CONDITIONAL_SET_FLAG((res & 0xFFFF) == 0, ZF); + CONDITIONAL_SET_FLAG(res & 0x8000, SF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + /* calculate the borrow chain --- modified such that d=0 */ + bc= res | s; + CONDITIONAL_SET_FLAG(xor_3_tab[(bc >> 6) & 0x3], OF); + CONDITIONAL_SET_FLAG(bc & 0x8, AF); + return res; +} + +/* not byte primitive */ +uint8 not_byte(uint8 s) +{ + return ~s; +} + +/* not word primitive */ +uint16 not_word(uint16 s) +{ + return ~s; +} + +/* or byte primitive */ +uint8 or_byte(uint8 d, uint8 s) +{ + register uint8 res; + + res = d | s; + /* clear flags */ + CLR_FLAG(OF); + CLR_FLAG(CF); + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG(res & 0x80, SF); + CONDITIONAL_SET_FLAG(res == 0, ZF); + CONDITIONAL_SET_FLAG(parity(res), PF); + return res; +} + +/* or word primitive */ +uint16 or_word(uint16 d, uint16 s) +{ + register uint16 res; + + res = d | s; + /* clear flags */ + CLR_FLAG(OF); + CLR_FLAG(CF); + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG(res & 0x8000, SF); + CONDITIONAL_SET_FLAG(res == 0, ZF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + return res; +} + +/* push word primitive */ +void push_word(uint16 val) +{ + SP--; + put_smbyte(SS, SP, val >> 8); + SP--; + put_smbyte(SS, SP, val & 0xFF); +} + +/* pop word primitive */ +uint16 pop_word(void) +{ + register uint16 res; + + res = get_smbyte(SS, SP); + SP++; + res |= (get_smbyte(SS, SP) << 8); + SP++; + return res; +} + +/* rcl byte primitive */ +uint8 rcl_byte(uint8 d, uint8 s) +{ + register uint32 res, cnt, mask, cf; + + res = d; + if ((cnt = s % 9)) + { + cf = (d >> (8-cnt)) & 0x1; + res = (d << cnt) & 0xFF; + mask = (1<<(cnt-1)) - 1; + res |= (d >> (9-cnt)) & mask; + if (GET_FLAG(CF)) + res |= 1 << (cnt-1); + CONDITIONAL_SET_FLAG(cf, CF); + CONDITIONAL_SET_FLAG(cnt == 1 && xor_3_tab[cf + ((res >> 6) & 0x2)], OF); + } + return res & 0xFF; +} + +/* rcl word primitive */ +uint16 rcl_word(uint16 d, uint16 s) +{ + register uint32 res, cnt, mask, cf; + + res = d; + if ((cnt = s % 17)) + { + cf = (d >> (16-cnt)) & 0x1; + res = (d << cnt) & 0xFFFF; + mask = (1<<(cnt-1)) - 1; + res |= (d >> (17-cnt)) & mask; + if (GET_FLAG(CF)) + res |= 1 << (cnt-1); + CONDITIONAL_SET_FLAG(cf, CF); + CONDITIONAL_SET_FLAG(cnt == 1 && xor_3_tab[cf + ((res >> 14) & 0x2)], OF); + } + return res & 0xFFFF; +} + +/* rcr byte primitive */ +uint8 rcr_byte(uint8 d, uint8 s) +{ + uint8 res, cnt; + uint8 mask, cf, ocf = 0; + + res = d; + + if ((cnt = s % 9)) { + if (cnt == 1) { + cf = d & 0x1; + ocf = GET_FLAG(CF) != 0; + } else + cf = (d >> (cnt - 1)) & 0x1; + mask = (1 <<( 8 - cnt)) - 1; + res = (d >> cnt) & mask; + res |= (d << (9-cnt)); + if (GET_FLAG(CF)) + res |= 1 << (8 - cnt); + CONDITIONAL_SET_FLAG(cf, CF); + if (cnt == 1) + CONDITIONAL_SET_FLAG(xor_3_tab[ocf + ((d >> 6) & 0x2)], OF); + } + return res; +} + +/* rcr word primitive */ +uint16 rcr_word(uint16 d, uint16 s) +{ + uint16 res, cnt; + uint16 mask, cf, ocf = 0; + + res = d; + if ((cnt = s % 17)) { + if (cnt == 1) { + cf = d & 0x1; + ocf = GET_FLAG(CF) != 0; + } else + cf = (d >> (cnt-1)) & 0x1; + mask = (1 <<( 16 - cnt)) - 1; + res = (d >> cnt) & mask; + res |= (d << (17 - cnt)); + if (GET_FLAG(CF)) + res |= 1 << (16 - cnt); + CONDITIONAL_SET_FLAG(cf, CF); + if (cnt == 1) + CONDITIONAL_SET_FLAG(xor_3_tab[ocf + ((d >> 14) & 0x2)], OF); + } + return res; +} + +/* rol byte primitive */ +uint8 rol_byte(uint8 d, uint8 s) +{ + register uint32 res, cnt, mask; + + res =d; + + if ((cnt = s % 8)) { + res = (d << cnt); + mask = (1 << cnt) - 1; + res |= (d >> (8-cnt)) & mask; + CONDITIONAL_SET_FLAG(res & 0x1, CF); + CONDITIONAL_SET_FLAG(cnt == 1 && xor_3_tab[(res & 0x1) + ((res >> 6) & 0x2)], OF); + } + return res & 0xFF; +} + +/* rol word primitive */ +uint16 rol_word(uint16 d, uint16 s) +{ + register uint32 res, cnt, mask; + + res = d; + if ((cnt = s % 16)) { + res = (d << cnt); + mask = (1 << cnt) - 1; + res |= (d >> (16 - cnt)) & mask; + CONDITIONAL_SET_FLAG(res & 0x1, CF); + CONDITIONAL_SET_FLAG(cnt == 1 && xor_3_tab[(res & 0x1) + ((res >> 14) & 0x2)], OF); + } + return res&0xFFFF; +} + +/* ror byte primitive */ +uint8 ror_byte(uint8 d, uint8 s) +{ + register uint32 res, cnt, mask; + + res = d; + if ((cnt = s % 8)) { + res = (d << (8-cnt)); + mask = (1 << (8-cnt)) - 1; + res |= (d >> (cnt)) & mask; + CONDITIONAL_SET_FLAG(res & 0x80, CF); + CONDITIONAL_SET_FLAG(cnt == 1 && xor_3_tab[(res >> 6) & 0x3], OF); + } + return res & 0xFF; +} + +/* ror word primitive */ +uint16 ror_word(uint16 d, uint16 s) +{ + register uint32 res, cnt, mask; + + res = d; + if ((cnt = s % 16)) { + res = (d << (16-cnt)); + mask = (1 << (16-cnt)) - 1; + res |= (d >> (cnt)) & mask; + CONDITIONAL_SET_FLAG(res & 0x8000, CF); + CONDITIONAL_SET_FLAG(cnt == 1 && xor_3_tab[(res >> 14) & 0x3], OF); + } + return res & 0xFFFF; +} + +/* shl byte primitive */ +uint8 shl_byte(uint8 d, uint8 s) +{ + uint32 cnt, res, cf; + + if (s < 8) { + cnt = s % 8; + if (cnt > 0) { + res = d << cnt; + cf = d & (1 << (8 - cnt)); + CONDITIONAL_SET_FLAG(cf, CF); + CONDITIONAL_SET_FLAG((res & 0xFF)==0, ZF); + CONDITIONAL_SET_FLAG(res & 0x80, SF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + } else + res = (uint8) d; + if (cnt == 1) + CONDITIONAL_SET_FLAG((((res & 0x80) == 0x80) ^ + (GET_FLAG( CF) != 0)), OF); + else + CLR_FLAG(OF); + } else { + res = 0; + CONDITIONAL_SET_FLAG((s == 8) && (d & 1), CF); + CLR_FLAG(OF); + CLR_FLAG(SF); + CLR_FLAG(PF); + SET_FLAG(ZF); + } + return res & 0xFF; +} + +/* shl word primitive */ +uint16 shl_word(uint16 d, uint16 s) +{ + uint32 cnt, res, cf; + + if (s < 16) { + cnt = s % 16; + if (cnt > 0) { + res = d << cnt; + cf = d & (1<<(16-cnt)); + CONDITIONAL_SET_FLAG(cf, CF); + CONDITIONAL_SET_FLAG((res & 0xFFFF) == 0, ZF); + CONDITIONAL_SET_FLAG(res & 0x8000, SF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + } else + res = (uint16) d; + if (cnt == 1) + CONDITIONAL_SET_FLAG((((res & 0x8000) == 0x8000) ^ + (GET_FLAG(CF) != 0)), OF); + else + CLR_FLAG(OF); + } else { + res = 0; + CONDITIONAL_SET_FLAG((s == 16) && (d & 1), CF); + CLR_FLAG(OF); + SET_FLAG(ZF); + CLR_FLAG(SF); + CLR_FLAG(PF); + } + return res & 0xFFFF; +} + +/* shr byte primitive */ +uint8 shr_byte(uint8 d, uint8 s) +{ + uint32 cnt, res, cf, mask; + + if (s < 8) { + cnt = s % 8; + if (cnt > 0) { + mask = (1 << (8 - cnt)) - 1; + cf = d & (1 << (cnt - 1)); + res = (d >> cnt) & mask; + CONDITIONAL_SET_FLAG(cf, CF); + CONDITIONAL_SET_FLAG((res & 0xFF) == 0, ZF); + CONDITIONAL_SET_FLAG(res & 0x80, SF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + } else + res = (uint8) d; + if (cnt == 1) + CONDITIONAL_SET_FLAG(xor_3_tab[(res >> 6) & 0x3], OF); + else + CLR_FLAG(OF); + } else { + res = 0; + CONDITIONAL_SET_FLAG((s == 8) && (d & 0x80), CF); + CLR_FLAG(OF); + SET_FLAG(ZF); + CLR_FLAG(SF); + CLR_FLAG(PF); + } + return res & 0xFF; +} + +/* shr word primitive */ +uint16 shr_word(uint16 d, uint16 s) +{ + uint32 cnt, res, cf, mask; + + res = d; + if (s < 16) { + cnt = s % 16; + if (cnt > 0) { + mask = (1 << (16 - cnt)) - 1; + cf = d & (1 << (cnt - 1)); + res = (d >> cnt) & mask; + CONDITIONAL_SET_FLAG(cf, CF); + CONDITIONAL_SET_FLAG((res & 0xFFFF) == 0, ZF); + CONDITIONAL_SET_FLAG(res & 0x8000, SF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + } else + res = d; + if (cnt == 1) + CONDITIONAL_SET_FLAG(xor_3_tab[(res >> 14) & 0x3], OF); + else + CLR_FLAG(OF); + } else { + res = 0; + CONDITIONAL_SET_FLAG((s == 16) && (d & 0x8000), CF); + CLR_FLAG(OF); + SET_FLAG(ZF); + CLR_FLAG(SF); + CLR_FLAG(PF); + } + return res & 0xFFFF; +} + +/* sar byte primitive */ +uint8 sar_byte(uint8 d, uint8 s) +{ + uint32 cnt, res, cf, mask, sf; + + res = d; + sf = d & 0x80; + cnt = s % 8; + if (cnt > 0 && cnt < 8) { + mask = (1 << (8 - cnt)) - 1; + cf = d & (1 << (cnt -1 )); + res = (d >> cnt) & mask; + CONDITIONAL_SET_FLAG(cf, CF); + if (sf) + res |= ~mask; + CONDITIONAL_SET_FLAG((res & 0xFF)==0, ZF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + CONDITIONAL_SET_FLAG(res & 0x80, SF); + } else if (cnt >= 8) { + if (sf) { + res = 0xFF; + SET_FLAG(CF); + CLR_FLAG(ZF); + SET_FLAG(SF); + SET_FLAG(PF); + } else { + res = 0; + CLR_FLAG(CF); + SET_FLAG(ZF); + CLR_FLAG(SF); + CLR_FLAG(PF); + } + } + return res & 0xFF; +} + +/* sar word primitive */ +uint16 sar_word(uint16 d, uint16 s) +{ + uint32 cnt, res, cf, mask, sf; + + sf = d & 0x8000; + cnt = s % 16; + res = d; + if (cnt > 0 && cnt < 16) { + mask = (1 << (16 - cnt)) - 1; + cf = d & (1 << (cnt - 1)); + res = (d >> cnt) & mask; + CONDITIONAL_SET_FLAG(cf, CF); + if (sf) + res |= ~mask; + CONDITIONAL_SET_FLAG((res & 0xFFFF) == 0, ZF); + CONDITIONAL_SET_FLAG(res & 0x8000, SF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + } else if (cnt >= 16) { + if (sf) { + res = 0xFFFF; + SET_FLAG(CF); + CLR_FLAG(ZF); + SET_FLAG(SF); + SET_FLAG(PF); + } else { + res = 0; + CLR_FLAG(CF); + SET_FLAG(ZF); + CLR_FLAG(SF); + CLR_FLAG(PF); + } + } + return res & 0xFFFF; +} + +/* sbb byte primitive */ +uint8 sbb_byte(uint8 d, uint8 s) +{ + register uint32 res; + register uint32 bc; + + if (GET_FLAG(CF)) + res = d - s - 1; + else + res = d - s; + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG(res & 0x80, SF); + CONDITIONAL_SET_FLAG((res & 0xFF) == 0, ZF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + /* calculate the borrow chain. See note at top */ + bc= (res&(~d|s))|(~d&s); + /* set flags based on borrow chain */ + CONDITIONAL_SET_FLAG(bc & 0x80, CF); + CONDITIONAL_SET_FLAG(xor_3_tab[(bc >> 6) & 0x3], OF); + CONDITIONAL_SET_FLAG(bc & 0x8, AF); +// return res & 0x0FF; + return (uint8) res; +} + +/* sbb word primitive */ +uint16 sbb_word(uint16 d, uint16 s) +{ + register uint32 res; + register uint32 bc; + + if (GET_FLAG(CF)) + res = d - s - 1; + else + res = d - s; + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG(res & 0x8000, SF); + CONDITIONAL_SET_FLAG((res & 0xFFFF) == 0, ZF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + /* calculate the borrow chain. See note at top */ + bc= (res&(~d|s))|(~d&s); + /* set flags based on borrow chain */ + CONDITIONAL_SET_FLAG(bc & 0x8000, CF); + CONDITIONAL_SET_FLAG(xor_3_tab[(bc >> 14) & 0x3], OF); + CONDITIONAL_SET_FLAG(bc & 0x8, AF); +// return res & 0xFFFF; + return (uint16) res; +} + +/* sub byte primitive */ +uint8 sub_byte(uint8 d, uint8 s) +{ + register uint32 res; + register uint32 bc; + + res = d - s; + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG(res & 0x80, SF); + CONDITIONAL_SET_FLAG((res & 0xFF) == 0, ZF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + /* calculate the borrow chain. See note at top */ + bc= (res&(~d|s))|(~d&s); + /* set flags based on borrow chain */ + CONDITIONAL_SET_FLAG(bc & 0x80, CF); + CONDITIONAL_SET_FLAG(xor_3_tab[(bc >> 6) & 0x3], OF); + CONDITIONAL_SET_FLAG(bc & 0x8, AF); +// return res & 0xff; + return (uint8) res; +} + +/* sub word primitive */ +uint16 sub_word(uint16 d, uint16 s) +{ + register uint32 res; + register uint32 bc; + + res = d - s; + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG(res &0x8000, SF); + CONDITIONAL_SET_FLAG((res &0xFFFF) == 0, ZF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + /* calculate the borrow chain. See note at top */ + bc= (res&(~d|s))|(~d&s); + /* set flags based on borrow chain */ + CONDITIONAL_SET_FLAG(bc & 0x8000, CF); + CONDITIONAL_SET_FLAG(xor_3_tab[(bc >> 14) & 0x3], OF); + CONDITIONAL_SET_FLAG(bc & 0x8, AF); +// return res & 0xffff; + return (uint16) res; +} + +/* test byte primitive */ +void test_byte(uint8 d, uint8 s) +{ + register uint32 res; + + res = d & s; + CLR_FLAG(OF); + CONDITIONAL_SET_FLAG(res & 0x80, SF); + CONDITIONAL_SET_FLAG(res == 0, ZF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + /* AF == dont care*/ + CLR_FLAG(CF); +} + +/* test word primitive */ +void test_word(uint16 d, uint16 s) +{ + register uint32 res; + + res = d & s; + CLR_FLAG(OF); + CONDITIONAL_SET_FLAG(res & 0x8000, SF); + CONDITIONAL_SET_FLAG(res == 0, ZF); + CONDITIONAL_SET_FLAG(parity(res & 0xff), PF); + /* AF == dont care*/ + CLR_FLAG(CF); +} + +/* xor byte primitive */ +uint8 xor_byte(uint8 d, uint8 s) +{ + register uint8 res; + res = d ^ s; + + /* clear flags */ + CLR_FLAG(OF); + CLR_FLAG(CF); + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG(res & 0x80, SF); + CONDITIONAL_SET_FLAG(res == 0, ZF); + CONDITIONAL_SET_FLAG(parity(res), PF); + return res; +} + +/* xor word primitive */ +uint16 xor_word(uint16 d, uint16 s) +{ + register uint16 res; + + res = d ^ s; + /* clear flags */ + CLR_FLAG(OF); + CLR_FLAG(CF); + /* set the flags based on the result */ + CONDITIONAL_SET_FLAG(res & 0x8000, SF); + CONDITIONAL_SET_FLAG(res == 0, ZF); + CONDITIONAL_SET_FLAG(parity(res & 0xFF), PF); + return res; +} + +/* memory routines. These use the segment register (segreg) value and offset + (addr) to calculate the proper source or destination memory address */ + +/* get a byte from memory */ + +int32 get_smbyte(int32 segreg, int32 addr) +{ + int32 abs_addr, val; + + abs_addr = addr + (get_rword(segreg) << 4); + val = get_mbyte(abs_addr); +// printf("get_smbyte: seg=%04X addr=%04X abs_addr=%08X get_mbyte=%02X\n", +// get_rword(segreg), addr, abs_addr, val); + return val; +} + +/* get a word from memory using addr and segment register */ + +int32 get_smword(int32 segreg, int32 addr) +{ + int32 val; + + val = get_smbyte(segreg, addr); + val |= (get_smbyte(segreg, addr+1) << 8); + return val; +} + +/* put a byte to memory using addr and segment register */ + +void put_smbyte(int32 segreg, int32 addr, int32 val) +{ + int32 abs_addr; + + abs_addr = addr + (get_rword(segreg) << 4); + put_mbyte(abs_addr, val); +} + +/* put a word to memory using addr and segment register */ + +void put_smword(int32 segreg, int32 addr, int32 val) +{ + put_smbyte(segreg, addr, val); + put_smbyte(segreg, addr+1, val << 8); +} + +/* Reset routine using addr and segment register */ + +t_stat i8088_reset (DEVICE *dptr) +{ + PSW = 0; + CS = 0xFFFF; + DS = 0; + SS = 0; + ES = 0; + saved_PC = 0; + int_req = 0; + sim_brk_types = sim_brk_dflt = SWMASK ('E'); + printf(" 8088 Reset\n"); + return SCPE_OK; +} + +/* Memory examine */ + +t_stat i8088_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ + if (addr >= MAXMEMSIZE20) + return SCPE_NXM; + if (vptr != NULL) + *vptr = get_mbyte(addr); + return SCPE_OK; +} + +/* Memory deposit */ + +t_stat i8088_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ + if (addr >= MAXMEMSIZE20) + return SCPE_NXM; + put_mbyte(addr, val); + return SCPE_OK; +} + +/* This is the binary loader. The input file is considered to be + a string of literal bytes with no special format. The load + starts at the current value of the PC. +*/ + +int32 sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ + int32 i, addr = 0, cnt = 0; + + if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; + addr = saved_PC; + while ((i = getc (fileref)) != EOF) { + put_mbyte(addr, i); + addr++; + cnt++; + } /* end while */ + printf ("%d Bytes loaded.\n", cnt); + return (SCPE_OK); +} + +/* Symbolic output + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + status = error code +*/ + +int32 fprint_sym (FILE *of, int32 addr, uint32 *val, + UNIT *uptr, int32 sw) +{ + int32 cflag, c1, c2, inst, adr; + + cflag = (uptr == NULL) || (uptr == &i8088_unit); + c1 = (val[0] >> 8) & 0177; + c2 = val[0] & 0177; + if (sw & SWMASK ('A')) { + fprintf (of, (c2 < 040)? "<%02X>": "%c", c2); + return SCPE_OK; + } + if (sw & SWMASK ('C')) { + fprintf (of, (c1 < 040)? "<%02X>": "%c", c1); + fprintf (of, (c2 < 040)? "<%02X>": "%c", c2); + return SCPE_OK; + } + if (!(sw & SWMASK ('M'))) return SCPE_ARG; + inst = val[0]; + fprintf (of, "%s", opcode[inst]); + if (oplen[inst] == 2) { + if (strchr(opcode[inst], ' ') != NULL) + fprintf (of, ","); + else fprintf (of, " "); + fprintf (of, "%h", val[1]); + } + if (oplen[inst] == 3) { + adr = val[1] & 0xFF; + adr |= (val[2] << 8) & 0xff00; + if (strchr(opcode[inst], ' ') != NULL) + fprintf (of, ","); + else fprintf (of, " "); + fprintf (of, "%h", adr); + } + return -(oplen[inst] - 1); +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +int32 parse_sym (char *cptr, int32 addr, UNIT *uptr, uint32 *val, int32 sw) +{ + int32 cflag, i = 0, j, r; + char gbuf[CBUFSIZE]; + + cflag = (uptr == NULL) || (uptr == &i8088_unit); + while (isspace (*cptr)) cptr++; /* absorb spaces */ + if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (uint32) cptr[0]; + return SCPE_OK; + } + if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = ((uint32) cptr[0] << 8) + (uint32) cptr[1]; + return SCPE_OK; + } + +/* An instruction: get opcode (all characters until null, comma, + or numeric (including spaces). +*/ + + while (1) { + if (*cptr == ',' || *cptr == '\0' || + isdigit(*cptr)) + break; + gbuf[i] = toupper(*cptr); + cptr++; + i++; + } + +/* Allow for RST which has numeric as part of opcode */ + + if (toupper(gbuf[0]) == 'R' && + toupper(gbuf[1]) == 'S' && + toupper(gbuf[2]) == 'T') { + gbuf[i] = toupper(*cptr); + cptr++; + i++; + } + +/* Allow for 'MOV' which is only opcode that has comma in it. */ + + if (toupper(gbuf[0]) == 'M' && + toupper(gbuf[1]) == 'O' && + toupper(gbuf[2]) == 'V') { + gbuf[i] = toupper(*cptr); + cptr++; + i++; + gbuf[i] = toupper(*cptr); + cptr++; + i++; + } + +/* kill trailing spaces if any */ + gbuf[i] = '\0'; + for (j = i - 1; gbuf[j] == ' '; j--) { + gbuf[j] = '\0'; + } + +/* find opcode in table */ + for (j = 0; j < 256; j++) { + if (strcmp(gbuf, opcode[j]) == 0) + break; + } + if (j > 255) /* not found */ + return SCPE_ARG; + + val[0] = j; /* store opcode */ + if (oplen[j] < 2) /* if 1-byter we are done */ + return SCPE_OK; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, 0); /* get address */ + sscanf(gbuf, "%o", &r); + if (oplen[j] == 2) { + val[1] = r & 0xFF; + return (-1); + } + val[1] = r & 0xFF; + val[2] = (r >> 8) & 0xFF; + return (-2); +} diff --git a/MDS-800/common/i8251.c b/MDS-800/common/i8251.c new file mode 100644 index 00000000..b1092b37 --- /dev/null +++ b/MDS-800/common/i8251.c @@ -0,0 +1,252 @@ +/* i8251.c: Intel i8251 UART adapter + + Copyright (c) 2010, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + These functions support a simulated i8251 interface device on an iSBC. + The device had one physical I/O port which could be connected + to any serial I/O device that would connect to a current loop, + RS232, or TTY interface. Available baud rates were jumper + selectable for each port from 110 to 9600. + + All I/O is via programmed I/O. The i8251 has a status port + and a data port. + + The simulated device does not support synchronous mode. The simulated device + supports a select from I/O space and one address line. The data port is at the + lower address and the status/command port is at the higher. + + A write to the status port can select some options for the device: + + Asynchronous Mode Instruction + 7 6 5 4 3 2 1 0 + +---+---+---+---+---+---+---+---+ + | S2 S1 EP PEN L2 L1 B2 B1| + +---+---+---+---+---+---+---+---+ + + Baud Rate Factor + B2 0 1 0 1 + B1 0 0 1 1 + sync 1X 16X 64X + mode + + Character Length + L2 0 1 0 1 + L1 0 0 1 1 + 5 6 7 8 + bits bits bits bits + + EP - A 1 in this bit position selects even parity. + PEN - A 1 in this bit position enables parity. + + Number of Stop Bits + S2 0 1 0 1 + S1 0 0 1 1 + invalid 1 1.5 2 + bit bits bits + + Command Instruction Format + 7 6 5 4 3 2 1 0 + +---+---+---+---+---+---+---+---+ + | EH IR RTS ER SBRK RxE DTR TxE| + +---+---+---+---+---+---+---+---+ + + TxE - A 1 in this bit position enables transmit. + DTR - A 1 in this bit position forces *DTR to zero. + RxE - A 1 in this bit position enables receive. + SBRK - A 1 in this bit position forces TxD to zero. + ER - A 1 in this bit position resets the error bits + RTS - A 1 in this bit position forces *RTS to zero. + IR - A 1 in this bit position returns the 8251 to Mode Instruction Format. + EH - A 1 in this bit position enables search for sync characters. + + A read of the status port gets the port status: + + Status Read Format + 7 6 5 4 3 2 1 0 + +---+---+---+---+---+---+---+---+ + |DSR SD FE OE PE TxE RxR TxR| + +---+---+---+---+---+---+---+---+ + + TxR - A 1 in this bit position signals transmit ready to receive a character. + RxR - A 1 in this bit position signals receiver has a character. + TxE - A 1 in this bit position signals transmitter has no more characters to transmit. + PE - A 1 in this bit signals a parity error. + OE - A 1 in this bit signals an transmit overrun error. + FE - A 1 in this bit signals a framing error. + SD - A 1 in this bit position returns the 8251 to Mode Instruction Format. + DSR - A 1 in this bit position signals *DSR is at zero. + + A read from the data port gets the typed character, a write + to the data port writes the character to the device. + + ?? ??? 10 - Original file. + 16 Dec 12 - Modified to use isbc_80_10.cfg file to set base and size. +*/ + +#include "system_defs.h" + +#define UNIT_V_ANSI (UNIT_V_UF + 0) /* ANSI mode */ +#define UNIT_ANSI (1 << UNIT_V_ANSI) + +#define TXR 0x01 +#define RXR 0x02 +#define TXE 0x04 +#define SD 0x40 + +extern int32 reg_dev(int32 (*routine)(), int32 port); + +/* function prototypes */ + +t_stat i8251_svc (UNIT *uptr); +t_stat i8251_reset (DEVICE *dptr, int32 base); +int32 i8251s(int32 io, int32 data); +int32 i8251d(int32 io, int32 data); +void i8251_reset1(void); +/* i8251 Standard I/O Data Structures */ + +UNIT i8251_unit = { + UDATA (&i8251_svc, 0, 0), KBD_POLL_WAIT +}; + +REG i8251_reg[] = { + { HRDATA (DATA, i8251_unit.buf, 8) }, + { HRDATA (STAT, i8251_unit.u3, 8) }, + { HRDATA (MODE, i8251_unit.u4, 8) }, + { HRDATA (CMD, i8251_unit.u5, 8) }, + { NULL } +}; + +MTAB i8251_mod[] = { + { UNIT_ANSI, 0, "TTY", "TTY", NULL }, + { UNIT_ANSI, UNIT_ANSI, "ANSI", "ANSI", NULL }, + { 0 } +}; + +DEVICE i8251_dev = { + "8251", //name + &i8251_unit, //units + i8251_reg, //registers + i8251_mod, //modifiers + 1, //numunits + 10, //aradix + 31, //awidth + 1, //aincr + 16, //dradix + 8, //dwidth + NULL, //examine + NULL, //deposit +// &i8251_reset, //reset + NULL, //reset + NULL, //boot + NULL, //attach + NULL, //detach + NULL, //ctxt + 0, //flags + 0, //dctrl + NULL, //debflags + NULL, //msize + NULL //lname +}; + +/* Service routines to handle simulator functions */ + +/* i8251_svc - actually gets char & places in buffer */ + +t_stat i8251_svc (UNIT *uptr) +{ + int32 temp; + + sim_activate (&i8251_unit, i8251_unit.wait); /* continue poll */ + if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) + return temp; /* no char or error? */ + i8251_unit.buf = temp & 0xFF; /* Save char */ + i8251_unit.u3 |= RXR; /* Set status */ + + /* Do any special character handling here */ + + i8251_unit.pos++; + return SCPE_OK; +} + +/* Reset routine */ + +t_stat i8251_reset (DEVICE *dptr, int32 base) +{ + reg_dev(i8251d, base); + reg_dev(i8251s, base + 1); + reg_dev(i8251d, base + 2); + reg_dev(i8251s, base + 3); + i8251_reset1(); + printf(" 8251: Registered at %02X\n", base); + sim_activate (&i8251_unit, i8251_unit.wait); /* activate unit */ + return SCPE_OK; +} + +/* I/O instruction handlers, called from the CPU module when an + IN or OUT instruction is issued. +*/ + +int32 i8251s(int32 io, int32 data) +{ +// printf("\nio=%d data=%04X\n", io, data); + if (io == 0) { /* read status port */ + return i8251_unit.u3; + } else { /* write status port */ + if (i8251_unit.u6) { /* if mode, set cmd */ + i8251_unit.u5 = data; + printf("8251: Command Instruction=%02X\n", data); + if (data & SD) /* reset port! */ + i8251_reset1(); + } else { /* set mode */ + i8251_unit.u4 = data; + printf("8251: Mode Instruction=%02X\n", data); + i8251_unit.u6 = 1; /* set cmd received */ + } + return (0); + } +} + +int32 i8251d(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + i8251_unit.u3 &= ~RXR; + return (i8251_unit.buf); + } else { /* write data port */ + sim_putchar(data); + } + return 0; +} + +void i8251_reset1(void) +{ + i8251_unit.u3 = TXR + TXE; /* status */ + i8251_unit.u4 = 0; /* mode instruction */ + i8251_unit.u5 = 0; /* command instruction */ + i8251_unit.u6 = 0; + i8251_unit.buf = 0; + i8251_unit.pos = 0; + printf(" 8251: Reset\n"); +} + +/* end of i8251.c */ diff --git a/MDS-800/common/i8255.c b/MDS-800/common/i8255.c new file mode 100644 index 00000000..b0b3e79b --- /dev/null +++ b/MDS-800/common/i8255.c @@ -0,0 +1,459 @@ +/* i8255.c: Intel i8255 PIO adapter + + Copyright (c) 2010, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + These functions support a simulated i8255 interface device on an iSBC. + The device has threee physical 8-bit I/O ports which could be connected + to any parallel I/O device. + + All I/O is via programmed I/O. The i8255 has a control port (PIOS) + and three data ports (PIOA, PIOB, and PIOC). + + The simulated device supports a select from I/O space and two address lines. + The data ports are at the lower addresses and the control port is at + the highest. + + A write to the control port can configure the device: + + Control Word + +---+---+---+---+---+---+---+---+ + | D7 D6 D5 D4 D3 D2 D1 D0| + +---+---+---+---+---+---+---+---+ + + Group B + D0 Port C (lower) 1-Input, 0-Output + D1 Port B 1-Input, 0-Output + D2 Mode Selection 0-Mode 0, 1-Mode 1 + + Group A + D3 Port C (upper) 1-Input, 0-Output + D4 Port A 1-Input, 0-Output + D5-6 Mode Selection 00-Mode 0, 01-Mode 1, 1X-Mode 2 + + D7 Mode Set Flag 1=Active, 0=Bit Set + + Mode 0 - Basic Input/Output + Mode 1 - Strobed Input/Output + Mode 2 - Bidirectional Bus + + Bit Set - D7=0, D3:1 select port C bit, D0 1=set, 0=reset + + A read to the data ports gets the current port value, a write + to the data ports writes the character to the device. + + *** Need to modify so that multiple devices can be registered and + used. + + ?? ??? 10 - Original file. + 16 Dec 12 - Modified to use isbc_80_10.cfg file to set base and size. +*/ + +#include "system_defs.h" /* system header in system dir */ +#define i8255_DEV 4 /* number of devices */ + +/* function prototypes */ + +int32 i8255s0(int32 io, int32 data); +int32 i8255a0(int32 io, int32 data); +int32 i8255b0(int32 io, int32 data); +int32 i8255c0(int32 io, int32 data); +int32 i8255s1(int32 io, int32 data); +int32 i8255a1(int32 io, int32 data); +int32 i8255b1(int32 io, int32 data); +int32 i8255c1(int32 io, int32 data); +int32 i8255s2(int32 io, int32 data); +int32 i8255a2(int32 io, int32 data); +int32 i8255b2(int32 io, int32 data); +int32 i8255c2(int32 io, int32 data); +int32 i8255s3(int32 io, int32 data); +int32 i8255a3(int32 io, int32 data); +int32 i8255b3(int32 io, int32 data); +int32 i8255c3(int32 io, int32 data); +t_stat i8255_reset (DEVICE *dptr, int32 base); + +/* external function prototypes */ + +extern int32 reg_dev(int32 (*routine)(), int32 port); + +/* globals */ + +int32 i8255_cnt = 0; +uint8 i8255_base[i8255_DEV]; + +/* i8255 Standard I/O Data Structures */ + +UNIT i8255_unit[] = { + { UDATA (0, 0, 0) }, + { UDATA (0, 0, 0) }, + { UDATA (0, 0, 0) }, + { UDATA (0, 0, 0) } +}; + +DEBTAB i8255_debug[] = { + { "ALL", DEBUG_all }, + { "FLOW", DEBUG_flow }, + { "READ", DEBUG_read }, + { "WRITE", DEBUG_write }, + { "XACK", DEBUG_xack }, + { "LEV1", DEBUG_level1 }, + { "LEV2", DEBUG_level2 }, + { NULL } +}; + +REG i8255_reg[] = { + { HRDATA (CONTROL0, i8255_unit[0].u3, 8) }, + { HRDATA (PORTA0, i8255_unit[0].u4, 8) }, + { HRDATA (PORTB0, i8255_unit[0].u5, 8) }, + { HRDATA (PORTC0, i8255_unit[0].u6, 8) }, + { HRDATA (CONTROL1, i8255_unit[1].u3, 8) }, + { HRDATA (PORTA1, i8255_unit[1].u4, 8) }, + { HRDATA (PORTB1, i8255_unit[1].u5, 8) }, + { HRDATA (PORTC1, i8255_unit[1].u6, 8) }, + { HRDATA (CONTROL1, i8255_unit[2].u3, 8) }, + { HRDATA (PORTA1, i8255_unit[2].u4, 8) }, + { HRDATA (PORTB1, i8255_unit[2].u5, 8) }, + { HRDATA (PORTC1, i8255_unit[2].u6, 8) }, + { HRDATA (CONTROL1, i8255_unit[3].u3, 8) }, + { HRDATA (PORTA1, i8255_unit[3].u4, 8) }, + { HRDATA (PORTB1, i8255_unit[3].u5, 8) }, + { HRDATA (PORTC1, i8255_unit[3].u6, 8) }, + { NULL } +}; + +DEVICE i8255_dev = { + "8255", //name + i8255_unit, //units + i8255_reg, //registers + NULL, //modifiers + 1, //numunits + 16, //aradix + 32, //awidth + 1, //aincr + 16, //dradix + 8, //dwidth + NULL, //examine + NULL, //deposit +// &i8255_reset, //reset + NULL, //reset + NULL, //boot + NULL, //attach + NULL, //detach + NULL, //ctxt + 0, //flags + 0, //dctrl + i8255_debug, //debflags + NULL, //msize + NULL //lname +}; + +/* I/O instruction handlers, called from the CPU module when an + IN or OUT instruction is issued. +*/ + +int32 i8255s0(int32 io, int32 data) +{ + int32 bit; + + if (io == 0) { /* read status port */ + return i8255_unit[0].u3; + } else { /* write status port */ + if (data & 0x80) { /* mode instruction */ + i8255_unit[0].u3 = data; + printf("8255-0: Mode Instruction=%02X\n", data); + if (data & 0x64) + printf(" Mode 1 and 2 not yet implemented\n"); + } else { /* bit set */ + bit = (data & 0x0E) >> 1; /* get bit number */ + if (data & 0x01) { /* set bit */ + i8255_unit[0].u6 |= (0x01 << bit); + } else { /* reset bit */ + i8255_unit[0].u6 &= ~(0x01 << bit); + } + } + } + return 0; +} + +int32 i8255a0(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + return (i8255_unit[0].u4); + } else { /* write data port */ + i8255_unit[0].u4 = data; + printf("8255-0: Port A = %02X\n", data); + } + return 0; +} + +int32 i8255b0(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + return (i8255_unit[0].u5); + } else { /* write data port */ + i8255_unit[0].u5 = data; + printf("8255-0: Port B = %02X\n", data); + } + return 0; +} + +int32 i8255c0(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + return (i8255_unit[0].u6); + } else { /* write data port */ + i8255_unit[0].u6 = data; + printf("8255-0: Port C = %02X\n", data); + } + return 0; +} + +int32 i8255s1(int32 io, int32 data) +{ + int32 bit; + + if (io == 0) { /* read status port */ + return i8255_unit[1].u3; + } else { /* write status port */ + if (data & 0x80) { /* mode instruction */ + i8255_unit[1].u3 = data; + printf("8255-1: Mode Instruction=%02X\n", data); + if (data & 0x64) + printf(" Mode 1 and 2 not yet implemented\n"); + } else { /* bit set */ + bit = (data & 0x0E) >> 1; /* get bit number */ + if (data & 0x01) { /* set bit */ + i8255_unit[1].u6 |= (0x01 << bit); + } else { /* reset bit */ + i8255_unit[1].u6 &= ~(0x01 << bit); + } + } + } + return 0; +} + +int32 i8255a1(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + return (i8255_unit[1].u4); + } else { /* write data port */ + i8255_unit[1].u4 = data; + printf("8255-1: Port A = %02X\n", data); + } + return 0; +} + +int32 i8255b1(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + return (i8255_unit[1].u5); + } else { /* write data port */ + i8255_unit[1].u5 = data; + printf("8255-1: Port B = %02X\n", data); + } + return 0; +} + +int32 i8255c1(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + return (i8255_unit[1].u6); + } else { /* write data port */ + i8255_unit[1].u6 = data; + printf("8255-1: Port C = %02X\n", data); + } + return 0; +} + +int32 i8255s2(int32 io, int32 data) +{ + int32 bit; + + if (io == 0) { /* read status port */ + return i8255_unit[2].u3; + } else { /* write status port */ + if (data & 0x80) { /* mode instruction */ + i8255_unit[2].u3 = data; + printf("8255-2: Mode Instruction=%02X\n", data); + if (data & 0x64) + printf(" Mode 1 and 2 not yet implemented\n"); + } else { /* bit set */ + bit = (data & 0x0E) >> 1; /* get bit number */ + if (data & 0x01) { /* set bit */ + i8255_unit[2].u6 |= (0x01 << bit); + } else { /* reset bit */ + i8255_unit[2].u6 &= ~(0x01 << bit); + } + } + } + return 0; +} + +int32 i8255a2(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + return (i8255_unit[2].u4); + } else { /* write data port */ + i8255_unit[2].u4 = data; + printf("8255-2: Port A = %02X\n", data); + } + return 0; +} + +int32 i8255b2(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + return (i8255_unit[2].u5); + } else { /* write data port */ + i8255_unit[2].u5 = data; + printf("8255-2: Port B = %02X\n", data); + } + return 0; +} + +int32 i8255c2(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + return (i8255_unit[2].u6); + } else { /* write data port */ + i8255_unit[2].u6 = data; + printf("8255-2: Port C = %02X\n", data); + } + return 0; +} + +int32 i8255s3(int32 io, int32 data) +{ + int32 bit; + + if (io == 0) { /* read status port */ + return i8255_unit[3].u3; + } else { /* write status port */ + if (data & 0x80) { /* mode instruction */ + i8255_unit[3].u3 = data; + printf("8255-3: Mode Instruction=%02X\n", data); + if (data & 0x64) + printf("\n Mode 1 and 2 not yet implemented\n"); + } else { /* bit set */ + bit = (data & 0x0E) >> 1; /* get bit number */ + if (data & 0x01) { /* set bit */ + i8255_unit[3].u6 |= (0x01 << bit); + } else { /* reset bit */ + i8255_unit[3].u6 &= ~(0x01 << bit); + } + } + } + return 0; +} + +int32 i8255a3(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + return (i8255_unit[3].u4); + } else { /* write data port */ + i8255_unit[3].u4 = data; + printf("8255-3: Port A = %02X\n", data); + } + return 0; +} + +int32 i8255b3(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + return (i8255_unit[3].u5); + } else { /* write data port */ + i8255_unit[3].u5 = data; + printf("8255-3: Port B = %02X\n", data); + } + return 0; +} + +int32 i8255c3(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + return (i8255_unit[3].u6); + } else { /* write data port */ + i8255_unit[3].u6 = data; + printf("8255-3: Port C = %02X\n", data); + } + return 0; +} + +/* Reset routine */ + +t_stat i8255_reset (DEVICE *dptr, int32 base) +{ + switch (i8255_cnt) { + case 0: + reg_dev(i8255a0, base); + reg_dev(i8255b0, base + 1); + reg_dev(i8255c0, base + 2); + reg_dev(i8255s0, base + 3); + i8255_unit[0].u3 = 0x9B; /* control */ + i8255_unit[0].u4 = 0xFF; /* Port A */ + i8255_unit[0].u5 = 0xFF; /* Port B */ + i8255_unit[0].u6 = 0xFF; /* Port C */ + printf(" 8255-0: Reset\n"); + break; + case 1: + reg_dev(i8255a1, base); + reg_dev(i8255b1, base + 1); + reg_dev(i8255c1, base + 2); + reg_dev(i8255s1, base + 3); + i8255_unit[1].u3 = 0x9B; /* control */ + i8255_unit[1].u4 = 0xFF; /* Port A */ + i8255_unit[1].u5 = 0xFF; /* Port B */ + i8255_unit[1].u6 = 0xFF; /* Port C */ + printf(" 8255-1: Reset\n"); + break; + case 2: + reg_dev(i8255a2, base); + reg_dev(i8255b2, base + 1); + reg_dev(i8255c2, base + 2); + reg_dev(i8255s2, base + 3); + i8255_unit[2].u3 = 0x9B; /* control */ + i8255_unit[2].u4 = 0xFF; /* Port A */ + i8255_unit[2].u5 = 0xFF; /* Port B */ + i8255_unit[2].u6 = 0xFF; /* Port C */ + printf(" 8255-2: Reset\n"); + break; + case 3: + reg_dev(i8255a3, base); + reg_dev(i8255b3, base + 1); + reg_dev(i8255c3, base + 2); + reg_dev(i8255s3, base + 3); + i8255_unit[3].u3 = 0x9B; /* control */ + i8255_unit[3].u4 = 0xFF; /* Port A */ + i8255_unit[3].u5 = 0xFF; /* Port B */ + i8255_unit[3].u6 = 0xFF; /* Port C */ + printf(" 8255-3: Reset\n"); + break; + default: + printf(" 8255: Bad device\n"); + } + printf(" 8255-%d: Registered at %02X\n", i8255_cnt, base); + i8255_cnt++; + return SCPE_OK; +} + +/* end of i8255.c */ diff --git a/MDS-800/common/i8259.c b/MDS-800/common/i8259.c new file mode 100644 index 00000000..8b319261 --- /dev/null +++ b/MDS-800/common/i8259.c @@ -0,0 +1,304 @@ +/* i8259.c: Intel i8259 PIC adapter + + Copyright (c) 2010, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + These functions support a simulated i8259 interface device on an iSBC. + 24 Jan 13 - Original file. +*/ + +#include "system_defs.h" /* system header in system dir */ +#define i8259_DEV 2 /* number of devices */ + +/* function prototypes */ + +int32 i8259a0(int32 io, int32 data); +int32 i8259b0(int32 io, int32 data); +int32 i8259a1(int32 io, int32 data); +int32 i8259b1(int32 io, int32 data); +void i8259_dump(int32 dev); +t_stat i8259_reset (DEVICE *dptr, int32 base); + +/* external function prototypes */ + +extern int32 reg_dev(int32 (*routine)(), int32 port); + +/* globals */ + +int32 i8259_cnt = 0; +uint8 i8259_base[i8259_DEV]; +uint8 i8259_icw1[i8259_DEV]; +uint8 i8259_icw2[i8259_DEV]; +uint8 i8259_icw3[i8259_DEV]; +uint8 i8259_icw4[i8259_DEV]; +uint8 i8259_ocw1[i8259_DEV]; +uint8 i8259_ocw2[i8259_DEV]; +uint8 i8259_ocw3[i8259_DEV]; +int32 icw_num0 = 1, icw_num1 = 1; + +/* i8255 Standard I/O Data Structures */ + +UNIT i8259_unit[] = { + { UDATA (0, 0, 0) }, + { UDATA (0, 0, 0) } +}; + +DEBTAB i8259_debug[] = { + { "ALL", DEBUG_all }, + { "FLOW", DEBUG_flow }, + { "READ", DEBUG_read }, + { "WRITE", DEBUG_write }, + { "XACK", DEBUG_xack }, + { "LEV1", DEBUG_level1 }, + { "LEV2", DEBUG_level2 }, + { NULL } +}; + +REG i8259_reg[] = { + { HRDATA (IRR0, i8259_unit[0].u3, 8) }, + { HRDATA (ISR0, i8259_unit[0].u4, 8) }, + { HRDATA (IMR0, i8259_unit[0].u5, 8) }, + { HRDATA (IRR1, i8259_unit[1].u3, 8) }, + { HRDATA (ISR1, i8259_unit[1].u4, 8) }, + { HRDATA (IMR1, i8259_unit[1].u5, 8) }, + { NULL } +}; + +DEVICE i8259_dev = { + "8259", //name + i8259_unit, //units + i8259_reg, //registers + NULL, //modifiers + 1, //numunits + 16, //aradix + 32, //awidth + 1, //aincr + 16, //dradix + 8, //dwidth + NULL, //examine + NULL, //deposit +// &i8259_reset, //reset + NULL, //reset + NULL, //boot + NULL, //attach + NULL, //detach + NULL, //ctxt + 0, //flags + 0, //dctrl + i8259_debug, //debflags + NULL, //msize + NULL //lname +}; + +/* I/O instruction handlers, called from the CPU module when an + IN or OUT instruction is issued. +*/ + +int32 i8259a0(int32 io, int32 data) +{ + int32 bit; + + if (io == 0) { /* read data port */ + if ((i8259_ocw3[0] & 0x03) == 0x02) + return (i8259_unit[0].u3); /* IRR */ + if ((i8259_ocw3[0] & 0x03) == 0x03) + return (i8259_unit[0].u4); /* ISR */ + } else { /* write data port */ + if (data & 0x10) { + icw_num0 = 1; + } + if (icw_num0 == 1) { + i8259_icw1[0] = data; /* ICW1 */ + i8259_unit[0].u5 = 0x00; /* clear IMR */ + i8259_ocw3[0] = 0x02; /* clear OCW3, Sel IRR */ + } else { + switch (data & 0x18) { + case 0: /* OCW2 */ + i8259_ocw2[0] = data; + break; + case 8: /* OCW3 */ + i8259_ocw3[0] = data; + break; + default: + printf("8259b-0: OCW Error %02X\n", data); + break; + } + } + printf("8259a-0: data = %02X\n", data); + icw_num0++; /* step ICW number */ + } + i8259_dump(0); + return 0; +} + +int32 i8259b0(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + return (i8259_unit[0].u5); /* IMR */ + } else { /* write data port */ + if (icw_num0 >= 2 && icw_num0 < 5) { /* ICW mode */ + switch (icw_num0) { + case 2: /* ICW2 */ + i8259_icw2[0] = data; + break; + case 3: /* ICW3 */ + i8259_icw3[0] = data; + break; + case 4: /* ICW4 */ + if (i8259_icw1[0] & 0x01) + i8259_icw4[0] = data; + else + printf("8259b-0: ICW4 not enabled - data=%02X\n", data); + break; + default: + printf("8259b-0: ICW Error %02X\n", data); + break; + } + icw_num0++; + } else { + i8259_ocw1[0] = data; /* OCW0 */ + } + } + i8259_dump(0); + return 0; +} + +int32 i8259a1(int32 io, int32 data) +{ + int32 bit; + + if (io == 0) { /* read data port */ + if ((i8259_ocw3[1] & 0x03) == 0x02) + return (i8259_unit[1].u3); /* IRR */ + if ((i8259_ocw3[1] & 0x03) == 0x03) + return (i8259_unit[1].u4); /* ISR */ + } else { /* write data port */ + if (data & 0x10) { + icw_num1 = 1; + } + if (icw_num1 == 1) { + i8259_icw1[1] = data; /* ICW1 */ + i8259_unit[1].u5 = 0x00; /* clear IMR */ + i8259_ocw3[1] = 0x02; /* clear OCW3, Sel IRR */ + } else { + switch (data & 0x18) { + case 0: /* OCW2 */ + i8259_ocw2[1] = data; + break; + case 8: /* OCW3 */ + i8259_ocw3[1] = data; + break; + default: + printf("8259b-1: OCW Error %02X\n", data); + break; + } + } + printf("8259a-1: data = %02X\n", data); + icw_num1++; /* step ICW number */ + } + i8259_dump(1); + return 0; +} + +int32 i8259b1(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + return (i8259_unit[1].u5); /* IMR */ + } else { /* write data port */ + if (icw_num1 >= 2 && icw_num1 < 5) { /* ICW mode */ + switch (icw_num1) { + case 2: /* ICW2 */ + i8259_icw2[1] = data; + break; + case 3: /* ICW3 */ + i8259_icw3[1] = data; + break; + case 4: /* ICW4 */ + if (i8259_icw1[1] & 0x01) + i8259_icw4[1] = data; + else + printf("8259b-1: ICW4 not enabled - data=%02X\n", data); + break; + default: + printf("8259b-1: ICW Error %02X\n", data); + break; + } + icw_num1++; + } else { + i8259_ocw1[1] = data; /* OCW0 */ + } + } + i8259_dump(1); + return 0; +} + +void i8259_dump(int32 dev) +{ + printf("Device %d\n", dev); + printf(" IRR = %02X\n", i8259_unit[dev].u3); + printf(" ISR = %02X\n", i8259_unit[dev].u4); + printf(" IMR = %02X\n", i8259_unit[dev].u5); + printf(" ICW1 = %02X\n", i8259_icw1[dev]); + printf(" ICW2 = %02X\n", i8259_icw2[dev]); + printf(" ICW3 = %02X\n", i8259_icw3[dev]); + printf(" ICW4 = %02X\n", i8259_icw4[dev]); + printf(" OCW1 = %02X\n", i8259_ocw1[dev]); + printf(" OCW2 = %02X\n", i8259_ocw2[dev]); + printf(" OCW3 = %02X\n", i8259_ocw3[dev]); +} + +/* Reset routine */ + +t_stat i8259_reset (DEVICE *dptr, int32 base) +{ + switch (i8259_cnt) { + case 0: + reg_dev(i8259a0, base); + reg_dev(i8259b0, base + 1); + reg_dev(i8259a0, base + 2); + reg_dev(i8259b0, base + 3); + i8259_unit[0].u3 = 0x00; /* IRR */ + i8259_unit[0].u4 = 0x00; /* ISR */ + i8259_unit[0].u5 = 0x00; /* IMR */ + printf(" 8259-0: Reset\n"); + break; + case 1: + reg_dev(i8259a1, base); + reg_dev(i8259b1, base + 1); + reg_dev(i8259a1, base + 2); + reg_dev(i8259b1, base + 3); + i8259_unit[1].u3 = 0x00; /* IRR */ + i8259_unit[1].u4 = 0x00; /* ISR */ + i8259_unit[1].u5 = 0x00; /* IMR */ + printf(" 8259-1: Reset\n"); + break; + default: + printf(" 8259: Bad device\n"); + break; + } + printf(" 8259-%d: Registered at %02X\n", i8259_cnt, base); + i8259_cnt++; + return SCPE_OK; +} + +/* end of i8259.c */ diff --git a/MDS-800/common/i8273.c b/MDS-800/common/i8273.c new file mode 100644 index 00000000..c51e3066 --- /dev/null +++ b/MDS-800/common/i8273.c @@ -0,0 +1,252 @@ +/* i8273.c: Intel i8273 UART adapter + + Copyright (c) 2011, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + These functions support a simulated i8273 interface device on an iSBC. + The device had one physical I/O port which could be connected + to any serial I/O device that would connect to a current loop, + RS232, or TTY interface. Available baud rates were jumper + selectable for each port from 110 to 9600. + + All I/O is via programmed I/O. The i8273 has a status port + and a data port. + + The simulated device does not support synchronous mode. The simulated device + supports a select from I/O space and one address line. The data port is at the + lower address and the status/command port is at the higher. + + A write to the status port can select some options for the device: + + Asynchronous Mode Instruction + +---+---+---+---+---+---+---+---+ + | S2 S1 EP PEN L2 L1 B2 B1| + +---+---+---+---+---+---+---+---+ + + Baud Rate Factor + B2 0 1 0 1 + B1 0 0 1 1 + sync 1X 16X 64X + mode + + Character Length + L2 0 1 0 1 + L1 0 0 1 1 + 5 6 7 8 + bits bits bits bits + + EP - A 1 in this bit position selects even parity. + PEN - A 1 in this bit position enables parity. + + Number of Stop Bits + S2 0 1 0 1 + S1 0 0 1 1 + invalid 1 1.5 2 + bit bits bits + + Command Instruction Format + +---+---+---+---+---+---+---+---+ + | EH IR RTS ER SBRK RxE DTR TxE| + +---+---+---+---+---+---+---+---+ + + TxE - A 1 in this bit position enables transmit. + DTR - A 1 in this bit position forces *DTR to zero. + RxE - A 1 in this bit position enables receive. + SBRK - A 1 in this bit position forces TxD to zero. + ER - A 1 in this bit position resets the error bits + RTS - A 1 in this bit position forces *RTS to zero. + IR - A 1 in this bit position returns the 8251 to Mode Instruction Format. + EH - A 1 in this bit position enables search for sync characters. + + A read of the status port gets the port status: + + Status Read Format + +---+---+---+---+---+---+---+---+ + |DSR SD FE OE PE TxE RxR TxR| + +---+---+---+---+---+---+---+---+ + + TxR - A 1 in this bit position signals transmit ready to receive a character. + RxR - A 1 in this bit position signals receiver has a character. + TxE - A 1 in this bit position signals transmitter has no more characters to transmit. + PE - A 1 in this bit signals a parity error. + OE - A 1 in this bit signals an transmit overrun error. + FE - A 1 in this bit signals a framing error. + SD - A 1 in this bit position returns the 8251 to Mode Instruction Format. + DSR - A 1 in this bit position signals *DSR is at zero. + + A read to the data port gets the buffered character, a write + to the data port writes the character to the device. + +*/ + +#include + +#include "multibus_defs.h" + +#define UNIT_V_ANSI (UNIT_V_UF + 0) /* ANSI mode */ +#define UNIT_ANSI (1 << UNIT_V_ANSI) + +uint8 + wr0 = 0, /* command register */ + wr1 = 0, /* enable register */ + wr2 = 0, /* CH A mode register */ + /* CH B interrups vector */ + wr3 = 0, /* configuration register 1 */ + wr4 = 0, /* configuration register 2 */ + wr5 = 0, /* configuration register 3 */ + wr6 = 0, /* sync low byte */ + wr7 = 0, /* sync high byte */ + rr0 = 0, /* status register */ + rr1 = 0, /* error register */ + rr2 = 0; /* read interrupt vector */ + +/* function prototypes */ + +t_stat i8273_reset (DEVICE *dptr); + +/* i8273 Standard I/O Data Structures */ + +UNIT i8273_unit = { UDATA (NULL, 0, 0), KBD_POLL_WAIT }; + +REG i8273_reg[] = { + { HRDATA (WR0, wr0, 8) }, + { HRDATA (WR1, wr1, 8) }, + { HRDATA (WR2, wr2, 8) }, + { HRDATA (WR3, wr3, 8) }, + { HRDATA (WR4, wr4, 8) }, + { HRDATA (WR5, wr5, 8) }, + { HRDATA (WR6, wr6, 8) }, + { HRDATA (WR7, wr7, 8) }, + { HRDATA (RR0, rr0, 8) }, + { HRDATA (RR0, rr1, 8) }, + { HRDATA (RR0, rr2, 8) }, + { NULL } +}; +MTAB i8273_mod[] = { + { UNIT_ANSI, 0, "TTY", "TTY", NULL }, + { UNIT_ANSI, UNIT_ANSI, "ANSI", "ANSI", NULL }, + { 0 } +}; + +DEBTAB i8273_debug[] = { + { "ALL", DEBUG_all }, + { "FLOW", DEBUG_flow }, + { "READ", DEBUG_read }, + { "WRITE", DEBUG_write }, + { "LEV1", DEBUG_level1 }, + { "LEV2", DEBUG_level2 }, + { NULL } +}; + +DEVICE i8273_dev = { + "8251", //name + &i8273_unit, //units + i8273_reg, //registers + i8273_mod, //modifiers + 1, //numunits + 16, //aradix + 32, //awidth + 1, //aincr + 16, //dradix + 8, //dwidth + NULL, //examine + NULL, //deposit + i8273_reset, //reset + NULL, //boot + NULL, //attach + NULL, //detach + NULL, //ctxt + DEV_DEBUG, //flags + 0, //dctrl + i8273_debug, //debflags + NULL, //msize + NULL //lname +}; + +/* Service routines to handle simulator functions */ + +/* Reset routine */ + +t_stat i8273_reset (DEVICE *dptr) +{ + wr0 = 0; /* command register */ + wr1 = 0; /* enable register */ + wr2 = 0; /* CH A mode register */ + /* CH B interrups vector */ + wr3 = 0; /* configuration register 1 */ + wr4 = 0; /* configuration register 2 */ + wr5 = 0; /* configuration register 3 */ + wr6 = 0; /* sync low byte */ + wr7 = 0; /* sync high byte */ + rr0 = 0; /* status register */ + rr1 = 0; /* error register */ + rr2 = 0; /* read interrupt vector */ + printf(" 8273 Reset\n"); + return SCPE_OK; +} + +/* I/O instruction handlers, called from the CPU module when an + IN or OUT instruction is issued. + + Each function is passed an 'io' flag, where 0 means a read from + the port, and 1 means a write to the port. On input, the actual + input is passed as the return value, on output, 'data' is written + to the device. +*/ + +int32 i8273s(int32 io, int32 data) +{ + if (io == 0) { /* read status port */ + return i8273_unit.u3; + } else { /* write status port */ + if (data == 0x40) { /* reset port! */ + i8273_unit.u3 = 0x05; /* status */ + i8273_unit.u4 = 0; /* mode instruction */ + i8273_unit.u5 = 0; /* command instruction */ + i8273_unit.u6 = 0; + i8273_unit.buf = 0; + i8273_unit.pos = 0; + printf("8273 Reset\n"); + } else if (i8273_unit.u6) { + i8273_unit.u5 = data; + printf("8273 Command Instruction=%02X\n", data); + } else { + i8273_unit.u4 = data; + printf("8273 Mode Instruction=%02X\n", data); + i8273_unit.u6++; + } + return (0); + } +} + +int32 i8273d(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + i8273_unit.u3 &= 0xFD; + return (i8273_unit.buf); + } else { /* write data port */ + sim_putchar(data); + } + return 0; +} + diff --git a/MDS-800/common/i8274.c b/MDS-800/common/i8274.c new file mode 100644 index 00000000..a6749a02 --- /dev/null +++ b/MDS-800/common/i8274.c @@ -0,0 +1,351 @@ +/* i8274.c: Intel i8274 MPSC adapter + + Copyright (c) 2011, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + These functions support a simulated i8274 interface device on an iSBC. + The device had two physical I/O ports which could be connected + to any serial I/O device that would connect to an RS232 interface. + + All I/O is via programmed I/O. The i8274 has a status port + and a data port. + + The simulated device does not support synchronous mode. The simulated device + supports a select from I/O space and two address lines. The data port is at the + lower address and the status/command port is at the higher address for each + channel. + + Minimum simulation is provided for this device. Channel A is used as a + console port for the iSBC-88/45 + + A write to the status port can select some options for the device: + + Asynchronous Mode Instruction + +---+---+---+---+---+---+---+---+ + | S2 S1 EP PEN L2 L1 B2 B1| + +---+---+---+---+---+---+---+---+ + + Baud Rate Factor + B2 0 1 0 1 + B1 0 0 1 1 + sync 1X 16X 64X + mode + + Character Length + L2 0 1 0 1 + L1 0 0 1 1 + 5 6 7 8 + bits bits bits bits + + EP - A 1 in this bit position selects even parity. + PEN - A 1 in this bit position enables parity. + + Number of Stop Bits + S2 0 1 0 1 + S1 0 0 1 1 + invalid 1 1.5 2 + bit bits bits + + Command Instruction Format + +---+---+---+---+---+---+---+---+ + | EH IR RTS ER SBRK RxE DTR TxE| + +---+---+---+---+---+---+---+---+ + + TxE - A 1 in this bit position enables transmit. + DTR - A 1 in this bit position forces *DTR to zero. + RxE - A 1 in this bit position enables receive. + SBRK - A 1 in this bit position forces TxD to zero. + ER - A 1 in this bit position resets the error bits + RTS - A 1 in this bit position forces *RTS to zero. + IR - A 1 in this bit position returns the 8251 to Mode Instruction Format. + EH - A 1 in this bit position enables search for sync characters. + + A read of the status port gets the port status: + + Status Read Format + +---+---+---+---+---+---+---+---+ + |DSR SD FE OE PE TxE RxR TxR| + +---+---+---+---+---+---+---+---+ + + TxR - A 1 in this bit position signals transmit ready to receive a character. + RxR - A 1 in this bit position signals receiver has a character. + TxE - A 1 in this bit position signals transmitter has no more characters to transmit. + PE - A 1 in this bit signals a parity error. + OE - A 1 in this bit signals an transmit overrun error. + FE - A 1 in this bit signals a framing error. + SD - A 1 in this bit position returns the 8251 to Mode Instruction Format. + DSR - A 1 in this bit position signals *DSR is at zero. + + A read to the data port gets the buffered character, a write + to the data port writes the character to the device. + +*/ + +#include + +#include "multibus_defs.h" + +#define UNIT_V_ANSI (UNIT_V_UF + 0) /* ANSI mode */ +#define UNIT_ANSI (1 << UNIT_V_ANSI) + +/* register definitions */ +/* channel A */ +uint8 wr0a = 0, /* command register */ + wr1a = 0, /* enable register */ + wr2a = 0, /* mode register */ + wr3a = 0, /* configuration register 1 */ + wr4a = 0, /* configuration register 2 */ + wr5a = 0, /* configuration register 3 */ + wr6a = 0, /* sync low byte */ + wr7a = 0, /* sync high byte */ + rr0a = 0, /* status register */ + rr1a = 0, /* error register */ + rr2a = 0; /* read interrupt vector */ +/* channel B */ +uint8 wr0b = 0, /* command register */ + wr1b = 0, /* enable register */ + wr2b = 0, /* CH B interrups vector */ + wr3b = 0, /* configuration register 1 */ + wr4b = 0, /* configuration register 2 */ + wr5b = 0, /* configuration register 3 */ + wr6b = 0, /* sync low byte */ + wr7b = 0, /* sync high byte */ + rr0b = 0, /* status register */ + rr1b = 0, /* error register */ + rr2b = 0; /* read interrupt vector */ + +/* function prototypes */ + +t_stat i8274_svc (UNIT *uptr); +t_stat i8274_reset (DEVICE *dptr); +int32 i8274As(int32 io, int32 data); +int32 i8274Ad(int32 io, int32 data); +int32 i8274Bs(int32 io, int32 data); +int32 i8274Bd(int32 io, int32 data); + +/* i8274 Standard I/O Data Structures */ + +UNIT i8274_unit = { UDATA (NULL, 0, 0), KBD_POLL_WAIT }; + +REG i8274_reg[] = { + { HRDATA (WR0A, wr0a, 8) }, + { HRDATA (WR1A, wr1a, 8) }, + { HRDATA (WR2A, wr2a, 8) }, + { HRDATA (WR3A, wr3a, 8) }, + { HRDATA (WR4A, wr4a, 8) }, + { HRDATA (WR5A, wr5a, 8) }, + { HRDATA (WR6A, wr6a, 8) }, + { HRDATA (WR7A, wr7a, 8) }, + { HRDATA (RR0A, rr0a, 8) }, + { HRDATA (RR0A, rr1a, 8) }, + { HRDATA (RR0A, rr2a, 8) }, + { HRDATA (WR0B, wr0b, 8) }, + { HRDATA (WR1B, wr1b, 8) }, + { HRDATA (WR2B, wr2b, 8) }, + { HRDATA (WR3B, wr3b, 8) }, + { HRDATA (WR4B, wr4b, 8) }, + { HRDATA (WR5B, wr5b, 8) }, + { HRDATA (WR6B, wr6b, 8) }, + { HRDATA (WR7B, wr7b, 8) }, + { HRDATA (RR0B, rr0b, 8) }, + { HRDATA (RR0B, rr1b, 8) }, + { HRDATA (RR0B, rr2b, 8) }, + { NULL } +}; +MTAB i8274_mod[] = { + { UNIT_ANSI, 0, "TTY", "TTY", NULL }, + { UNIT_ANSI, UNIT_ANSI, "ANSI", "ANSI", NULL }, + { 0 } +}; + +DEBTAB i8274_debug[] = { + { "ALL", DEBUG_all }, + { "FLOW", DEBUG_flow }, + { "READ", DEBUG_read }, + { "WRITE", DEBUG_write }, + { "LEV1", DEBUG_level1 }, + { "LEV2", DEBUG_level2 }, + { NULL } +}; + +DEVICE i8274_dev = { + "8274", //name + &i8274_unit, //units + i8274_reg, //registers + i8274_mod, //modifiers + 1, //numunits + 16, //aradix + 32, //awidth + 1, //aincr + 16, //dradix + 8, //dwidth + NULL, //examine + NULL, //deposit + i8274_reset, //reset + NULL, //boot + NULL, //attach + NULL, //detach + NULL, //ctxt + DEV_DEBUG, //flags + 0, //dctrl + i8274_debug, //debflags + NULL, //msize + NULL //lname +}; + +/* Service routines to handle simulator functions */ + +/* service routine - actually gets char & places in buffer in CH A*/ + +t_stat i8274_svc (UNIT *uptr) +{ + int32 temp; + + sim_activate (&i8274_unit, i8274_unit.wait); /* continue poll */ + if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) + return temp; /* no char or error? */ + i8274_unit.buf = temp & 0xFF; /* Save char */ + rr0a |= 0x01; /* Set rx char ready */ + + /* Do any special character handling here */ + + i8274_unit.pos++; + return SCPE_OK; +} + +/* Reset routine */ + +t_stat i8274_reset (DEVICE *dptr) +{ + wr0a = wr1a = wr2a = wr3a = wr4a = wr5a = wr6a = wr7a = rr0a = rr1a = rr2a = 0; + wr0b = wr1b = wr2b = wr3b = wr4b = wr5b = wr6b = wr7b = rr0b = rr1b = rr2b = 0; + printf(" 8274 Reset\n"); + return SCPE_OK; +} + +/* I/O instruction handlers, called from the CPU module when an + IN or OUT instruction is issued. + + Each function is passed an 'io' flag, where 0 means a read from + the port, and 1 means a write to the port. On input, the actual + input is passed as the return value, on output, 'data' is written + to the device. + + The 8274 contains 2 separate channels, A and B. +*/ + +/* channel A command/status */ +int32 i8274As(int32 io, int32 data) +{ + if (io == 0) { /* read status port */ + switch(wr0a & 0x7) { + case 0: /* rr0a */ + return rr0a; + case 1: /* rr1a */ + return rr1a; + case 2: /* rr1a */ + return rr2a; + } + return 0; /* bad register select */ + } else { /* write status port */ + switch(wr0a & 0x7) { + case 0: /* wr0a */ + wr0a = data; + if ((wr0a & 0x38) == 0x18) { /* channel reset */ + wr0a = wr1a = wr2a = wr3a = wr4a = wr5a = 0; + wr6a = wr7a = rr0a = rr1a = rr2a = 0; + printf("8274 Channel A reset\n"); + } + break; + case 1: /* wr1a */ + wr1a = data; + break; + case 2: /* wr2a */ + wr2a = data; + break; + case 3: /* wr3a */ + wr3a = data; + break; + case 4: /* wr4a */ + wr4a = data; + break; + case 5: /* wr5a */ + wr5a = data; + break; + case 6: /* wr6a */ + wr6a = data; + break; + case 7: /* wr7a */ + wr7a = data; + break; + } + printf("8274 Command WR%dA=%02X\n", wr0a & 0x7, data); + return 0; + } +} + +/* channel A data */ +int32 i8274Ad(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + rr0a &= 0xFE; + return (i8274_unit.buf); + } else { /* write data port */ + sim_putchar(data); + } + return 0; +} + +/* channel B command/status */ +int32 i8274Bs(int32 io, int32 data) +{ + if (io == 0) { /* read status port */ + return i8274_unit.u3; + } else { /* write status port */ + if (data == 0x40) { /* reset port! */ + printf("8274 Reset\n"); + } else if (i8274_unit.u6) { + i8274_unit.u5 = data; + printf("8274 Command Instruction=%02X\n", data); + } else { + i8274_unit.u4 = data; + printf("8274 Mode Instruction=%02X\n", data); + i8274_unit.u6++; + } + return (0); + } +} + +/* channel B data */ +int32 i8274Bd(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + i8274_unit.u3 &= 0xFD; + return (i8274_unit.buf); + } else { /* write data port */ + sim_putchar(data); + } + return 0; +} + +/* end of i8274.c */ \ No newline at end of file diff --git a/MDS-800/common/iSBC80-10.c b/MDS-800/common/iSBC80-10.c new file mode 100644 index 00000000..27a4a796 --- /dev/null +++ b/MDS-800/common/iSBC80-10.c @@ -0,0 +1,143 @@ +/* iSBC80-10.c: Intel iSBC 80/10 Processor simulator + + Copyright (c) 2010, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + This software was written by Bill Beech, Dec 2010, to allow emulation of Multibus + Computer Systems. + + ?? ??? 10 - Original file. +*/ + +#include "system_defs.h" + +/* set the base I/O address for the first 8255 */ +#define I8255_BASE_0 0xE4 + +/* set the base I/O address for the second 8255 */ +#define I8255_BASE_1 0xE8 + +/* set the base I/O address for the 8251 */ +#define I8251_BASE 0xEC + +/* set the base and size for the EPROM on the iSBC 80/10 */ +#define ROM_SIZE 0x1000 + +/* set the base and size for the RAM on the iSBC 80/10 */ +#define RAM_BASE 0x3C00 +#define RAM_SIZE 0x0400 + +/* set INTR for CPU */ +#define INTR INT_1 + +/* function prototypes */ + +int32 get_mbyte(int32 addr); +int32 get_mword(int32 addr); +void put_mbyte(int32 addr, int32 val); +void put_mword(int32 addr, int32 val); +t_stat SBC_reset (DEVICE *dptr); + +/* external function prototypes */ + +extern t_stat i8080_reset (DEVICE *dptr); /* reset the 8080 emulator */ +extern int32 multibus_get_mbyte(int32 addr); +extern void multibus_put_mbyte(int32 addr, int32 val); +extern int32 EPROM_get_mbyte(int32 addr); +extern int32 RAM_get_mbyte(int32 addr); +extern void RAM_put_mbyte(int32 addr, int32 val); +extern UNIT i8255_unit; +extern UNIT EPROM_unit; +extern UNIT RAM_unit; +extern t_stat i8255_reset (DEVICE *dptr, int32 base); +extern t_stat i8251_reset (DEVICE *dptr, int32 base); +extern t_stat pata_reset (DEVICE *dptr, int32 base); +extern t_stat EPROM_reset (DEVICE *dptr, int32 size); +extern t_stat RAM_reset (DEVICE *dptr, int32 base, int32 size); + +/* SBC reset routine */ + +t_stat SBC_reset (DEVICE *dptr) +{ + printf("Initializing iSBC-80/10:\n"); + i8080_reset (NULL); + i8255_reset (NULL, I8255_BASE_0); + i8255_reset (NULL, I8255_BASE_1); + i8251_reset (NULL, I8251_BASE); + EPROM_reset (NULL, ROM_SIZE); + RAM_reset (NULL, RAM_BASE, RAM_SIZE); + return SCPE_OK; +} + +/* get a byte from memory - handle RAM, ROM, I/O, and Multibus memory */ + +int32 get_mbyte(int32 addr) +{ + int32 val, org, len; + + /* if local EPROM handle it */ + if ((i8255_unit.u5 & 0x01) && (addr >= EPROM_unit.u3) && (addr < (EPROM_unit.u3 + EPROM_unit.capac))) { + return EPROM_get_mbyte(addr); + } /* if local RAM handle it */ + if ((i8255_unit.u5 & 0x02) && (addr >= RAM_unit.u3) && (addr < (RAM_unit.u3 + RAM_unit.capac))) { + return RAM_get_mbyte(addr); + } /* otherwise, try the multibus */ + return multibus_get_mbyte(addr); +} + +/* get a word from memory */ + +int32 get_mword(int32 addr) +{ + int32 val; + + val = get_mbyte(addr); + val |= (get_mbyte(addr+1) << 8); + return val; +} + +/* put a byte to memory - handle RAM, ROM, I/O, and Multibus memory */ + +void put_mbyte(int32 addr, int32 val) +{ + /* if local EPROM handle it */ + if ((i8255_unit.u5 & 0x01) && (addr >= EPROM_unit.u3) && (addr <= (EPROM_unit.u3 + EPROM_unit.capac))) { + printf("Write to R/O memory address %04X - ignored\n", addr); + return; + } /* if local RAM handle it */ + if ((i8255_unit.u5 & 0x02) && (addr >= RAM_unit.u3) && (addr <= (RAM_unit.u3 + RAM_unit.capac))) { + RAM_put_mbyte(addr, val); + return; + } /* otherwise, try the multibus */ + multibus_put_mbyte(addr, val); +} + +/* put a word to memory */ + +void put_mword(int32 addr, int32 val) +{ + put_mbyte(addr, val); + put_mbyte(addr+1, val >> 8); +} + +/* end of iSBC80-10.c */ diff --git a/MDS-800/common/iSBC80-20.c b/MDS-800/common/iSBC80-20.c new file mode 100644 index 00000000..b9af2024 --- /dev/null +++ b/MDS-800/common/iSBC80-20.c @@ -0,0 +1,149 @@ +/* iSBC80-20.c: Intel iSBC 80/30 Processor simulator + + Copyright (c) 2010, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + This software was written by Bill Beech, Dec 2010, to allow emulation of Multibus + Computer Systems. + + ?? ??? 10 - Original file. +*/ + +#include "system_defs.h" + +/* set the base I/O address for the 8259 */ +#define I8259_BASE 0xD8 + +/* set the base I/O address for the first 8255 */ +#define I8255_BASE_0 0xE4 + +/* set the base I/O address for the second 8255 */ +#define I8255_BASE_1 0xE8 + +/* set the base I/O address for the 8251 */ +#define I8251_BASE 0xEC + +/* set the base and size for the EPROM on the iSBC 80/20 */ +#define ROM_SIZE 0x1000 + +/* set the base and size for the RAM on the iSBC 80/20 */ +#define RAM_BASE 0x3C00 +#define RAM_SIZE 0x0400 + +/* set INTR for CPU */ +#define INTR INT_1 + +/* function prototypes */ + +int32 get_mbyte(int32 addr); +int32 get_mword(int32 addr); +void put_mbyte(int32 addr, int32 val); +void put_mword(int32 addr, int32 val); +t_stat i80_10_reset (DEVICE *dptr); + +/* external function prototypes */ + +extern t_stat i8080_reset (DEVICE *dptr); /* reset the 8080 emulator */ +extern int32 multibus_get_mbyte(int32 addr); +extern void multibus_put_mbyte(int32 addr, int32 val); +extern int32 EPROM_get_mbyte(int32 addr); +extern int32 RAM_get_mbyte(int32 addr); +extern void RAM_put_mbyte(int32 addr, int32 val); +extern UNIT i8255_unit; +extern UNIT EPROM_unit; +extern UNIT RAM_unit; +extern t_stat i8251_reset (DEVICE *dptr, int32 base); +extern t_stat i8255_reset (DEVICE *dptr, int32 base); +extern t_stat i8259_reset (DEVICE *dptr, int32 base); +extern t_stat pata_reset (DEVICE *dptr, int32 base); +extern t_stat EPROM_reset (DEVICE *dptr, int32 size); +extern t_stat RAM_reset (DEVICE *dptr, int32 base, int32 size); + +/* CPU reset routine + put here to cause a reset of the entire iSBC system */ + +t_stat SBC_reset (DEVICE *dptr) +{ + printf("Initializing iSBC-80/20\n"); + i8080_reset(NULL); + i8259_reset(NULL, I8259_BASE); + i8255_reset(NULL, I8255_BASE_0); + i8255_reset(NULL, I8255_BASE_1); + i8251_reset(NULL, I8251_BASE); + EPROM_reset(NULL, ROM_SIZE); + RAM_reset(NULL, RAM_BASE, RAM_SIZE); + return SCPE_OK; +} + +/* get a byte from memory - handle RAM, ROM and Multibus memory */ + +int32 get_mbyte(int32 addr) +{ + int32 val, org, len; + + /* if local EPROM handle it */ + if ((i8255_unit.u6 & 0x01) && (addr >= EPROM_unit.u3) && (addr < (EPROM_unit.u3 + EPROM_unit.capac))) { + return EPROM_get_mbyte(addr); + } /* if local RAM handle it */ + if ((i8255_unit.u6 & 0x02) && (addr >= RAM_unit.u3) && (addr < (RAM_unit.u3 + RAM_unit.capac))) { + return RAM_get_mbyte(addr); + } /* otherwise, try the multibus */ + return multibus_get_mbyte(addr); +} + +/* get a word from memory */ + +int32 get_mword(int32 addr) +{ + int32 val; + + val = get_mbyte(addr); + val |= (get_mbyte(addr+1) << 8); + return val; +} + +/* put a byte to memory - handle RAM, ROM and Multibus memory */ + +void put_mbyte(int32 addr, int32 val) +{ + /* if local EPROM handle it */ + if ((i8255_unit.u6 & 0x01) && (addr >= EPROM_unit.u3) && (addr <= (EPROM_unit.u3 + EPROM_unit.capac))) { + printf("Write to R/O memory address %04X - ignored\n", addr); + return; + } /* if local RAM handle it */ + if ((i8255_unit.u6 & 0x02) && (addr >= RAM_unit.u3) && (addr <= (RAM_unit.u3 + RAM_unit.capac))) { + RAM_put_mbyte(addr, val); + return; + } /* otherwise, try the multibus */ + multibus_put_mbyte(addr, val); +} + +/* put a word to memory */ + +void put_mword(int32 addr, int32 val) +{ + put_mbyte(addr, val); + put_mbyte(addr+1, val >> 8); +} + +/* end of iSBC80-10.c */ diff --git a/MDS-800/common/iSBC80-30.c b/MDS-800/common/iSBC80-30.c new file mode 100644 index 00000000..a48d72d6 --- /dev/null +++ b/MDS-800/common/iSBC80-30.c @@ -0,0 +1,149 @@ +/* iSBC80-30.c: Intel iSBC 80/30 Processor simulator + + Copyright (c) 2010, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + This software was written by Bill Beech, Dec 2010, to allow emulation of Multibus + Computer Systems. + + ?? ??? 10 - Original file. +*/ + +#include "system_defs.h" + +/* set the base I/O address for the 8259 */ +#define I8259_BASE 0xD8 + +/* set the base I/O address for the first 8255 */ +#define I8255_BASE_0 0xE4 + +/* set the base I/O address for the second 8255 */ +#define I8255_BASE_1 0xE8 + +/* set the base I/O address for the 8251 */ +#define I8251_BASE 0xEC + +/* set the base and size for the EPROM on the iSBC 80/20 */ +#define ROM_SIZE 0x1000 + +/* set the base and size for the RAM on the iSBC 80/20 */ +#define RAM_BASE 0x3C00 +#define RAM_SIZE 0x0400 + +/* set INTR for CPU */ +#define INTR INT_1 + +/* function prototypes */ + +int32 get_mbyte(int32 addr); +int32 get_mword(int32 addr); +void put_mbyte(int32 addr, int32 val); +void put_mword(int32 addr, int32 val); +t_stat i80_10_reset (DEVICE *dptr); + +/* external function prototypes */ + +extern t_stat i8080_reset (DEVICE *dptr); /* reset the 8080 emulator */ +extern int32 multibus_get_mbyte(int32 addr); +extern void multibus_put_mbyte(int32 addr, int32 val); +extern int32 EPROM_get_mbyte(int32 addr); +extern int32 RAM_get_mbyte(int32 addr); +extern void RAM_put_mbyte(int32 addr, int32 val); +extern UNIT i8255_unit; +extern UNIT EPROM_unit; +extern UNIT RAM_unit; +extern t_stat i8251_reset (DEVICE *dptr, int32 base); +extern t_stat i8255_reset (DEVICE *dptr, int32 base); +extern t_stat i8259_reset (DEVICE *dptr, int32 base); +extern t_stat pata_reset (DEVICE *dptr, int32 base); +extern t_stat EPROM_reset (DEVICE *dptr, int32 size); +extern t_stat RAM_reset (DEVICE *dptr, int32 base, int32 size); + +/* CPU reset routine + put here to cause a reset of the entire iSBC system */ + +t_stat SBC_reset (DEVICE *dptr) +{ + printf("Initializing iSBC-80/20\n"); + i8080_reset(NULL); + i8259_reset(NULL, I8259_BASE); + i8255_reset(NULL, I8255_BASE_0); + i8255_reset(NULL, I8255_BASE_1); + i8251_reset(NULL, I8251_BASE); + EPROM_reset(NULL, ROM_SIZE); + RAM_reset(NULL, RAM_BASE, RAM_SIZE); + return SCPE_OK; +} + +/* get a byte from memory - handle RAM, ROM and Multibus memory */ + +int32 get_mbyte(int32 addr) +{ + int32 val, org, len; + + /* if local EPROM handle it */ + if ((i8255_unit.u6 & 0x01) && (addr >= EPROM_unit.u3) && (addr < (EPROM_unit.u3 + EPROM_unit.capac))) { + return EPROM_get_mbyte(addr); + } /* if local RAM handle it */ + if ((i8255_unit.u6 & 0x02) && (addr >= RAM_unit.u3) && (addr < (RAM_unit.u3 + RAM_unit.capac))) { + return RAM_get_mbyte(addr); + } /* otherwise, try the multibus */ + return multibus_get_mbyte(addr); +} + +/* get a word from memory */ + +int32 get_mword(int32 addr) +{ + int32 val; + + val = get_mbyte(addr); + val |= (get_mbyte(addr+1) << 8); + return val; +} + +/* put a byte to memory - handle RAM, ROM and Multibus memory */ + +void put_mbyte(int32 addr, int32 val) +{ + /* if local EPROM handle it */ + if ((i8255_unit.u6 & 0x01) && (addr >= EPROM_unit.u3) && (addr <= (EPROM_unit.u3 + EPROM_unit.capac))) { + printf("Write to R/O memory address %04X - ignored\n", addr); + return; + } /* if local RAM handle it */ + if ((i8255_unit.u6 & 0x02) && (addr >= RAM_unit.u3) && (addr <= (RAM_unit.u3 + RAM_unit.capac))) { + RAM_put_mbyte(addr, val); + return; + } /* otherwise, try the multibus */ + multibus_put_mbyte(addr, val); +} + +/* put a word to memory */ + +void put_mword(int32 addr, int32 val) +{ + put_mbyte(addr, val); + put_mbyte(addr+1, val >> 8); +} + +/* end of iSBC80-10.c */ diff --git a/MDS-800/common/ieprom.c b/MDS-800/common/ieprom.c new file mode 100644 index 00000000..6555f24f --- /dev/null +++ b/MDS-800/common/ieprom.c @@ -0,0 +1,201 @@ +/* iEPROM.c: Intel EPROM simulator for 8-bit SBCs + + Copyright (c) 2010, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + These functions support a simulated i2732 EPROM device on an iSBC. This + allows the attachment of the device to a binary file containing the EPROM + code. + + Unit will support a single 2708, 2716, 2732 and 2764 EPROM type. + + ?? ??? 10 - Original file. + 16 Dec 12 - Modified to use isbc_80_10.cfg file to set base and size. +*/ + +#include "system_defs.h" + +#define SET_XACK(VAL) (xack = VAL) + +/* function prototypes */ + +t_stat EPROM_attach (UNIT *uptr, char *cptr); +t_stat EPROM_reset (DEVICE *dptr, int32 size); +int32 EPROM_get_mbyte(int32 addr); + +extern UNIT i8255_unit; +extern uint8 xack; /* XACK signal */ + +/* SIMH EPROM Standard I/O Data Structures */ + +UNIT EPROM_unit = { + UDATA (NULL, UNIT_ATTABLE+UNIT_BINK+UNIT_ROABLE+UNIT_RO, 0), 0 +}; + +DEBTAB EPROM_debug[] = { + { "ALL", DEBUG_all }, + { "FLOW", DEBUG_flow }, + { "READ", DEBUG_read }, + { "WRITE", DEBUG_write }, + { "XACK", DEBUG_xack }, + { "LEV1", DEBUG_level1 }, + { "LEV2", DEBUG_level2 }, + { NULL } +}; + +DEVICE EPROM_dev = { + "EPROM", //name + &EPROM_unit, //units + NULL, //registers + NULL, //modifiers + 1, //numunits + 16, //aradix + 32, //awidth + 1, //aincr + 16, //dradix + 8, //dwidth + NULL, //examine + NULL, //deposit +// &EPROM_reset, //reset + NULL, //reset + NULL, //boot + &EPROM_attach, //attach + NULL, //detach + NULL, //ctxt + DEV_DEBUG, //flags + 0, //dctrl + EPROM_debug, //debflags + NULL, //msize + NULL //lname +}; + +/* global variables */ + +/* EPROM functions */ + +/* EPROM attach */ + +t_stat EPROM_attach (UNIT *uptr, char *cptr) +{ + int j, c; + FILE *fp; + t_stat r; + + if (EPROM_dev.dctrl & DEBUG_flow) + printf("EPROM_attach: cptr=%s\n", cptr); + if ((r = attach_unit (uptr, cptr)) != SCPE_OK) { + if (EPROM_dev.dctrl & DEBUG_flow) + printf("EPROM_attach: Error\n"); + return r; + } + if (EPROM_dev.dctrl & DEBUG_read) + printf("\tAllocate buffer\n"); + if (EPROM_unit.filebuf == NULL) { /* no buffer allocated */ + EPROM_unit.filebuf = malloc(EPROM_unit.capac); /* allocate EPROM buffer */ + if (EPROM_unit.filebuf == NULL) { + if (EPROM_dev.dctrl & DEBUG_flow) + printf("EPROM_attach: Malloc error\n"); + return SCPE_MEM; + } + } + if (EPROM_dev.dctrl & DEBUG_read) + printf("\tOpen file %s\n", EPROM_unit.filename); + fp = fopen(EPROM_unit.filename, "rb"); /* open EPROM file */ + if (fp == NULL) { + printf("EPROM: Unable to open ROM file %s\n", EPROM_unit.filename); + printf("\tNo ROM image loaded!!!\n"); + return SCPE_OK; + } + if (EPROM_dev.dctrl & DEBUG_read) + printf("\tRead file\n"); + j = 0; /* load EPROM file */ + c = fgetc(fp); + while (c != EOF) { + *(uint8 *)(EPROM_unit.filebuf + j++) = c & 0xFF; + c = fgetc(fp); + if (j >= EPROM_unit.capac) { + printf("\tImage is too large - Load truncated!!!\n"); + break; + } + } + if (EPROM_dev.dctrl & DEBUG_read) + printf("\tClose file\n"); + fclose(fp); + printf("EPROM: %d bytes of ROM image %s loaded\n", j, EPROM_unit.filename); + if (EPROM_dev.dctrl & DEBUG_flow) + printf("EPROM_attach: Done\n"); + return SCPE_OK; +} + +/* EPROM reset */ + +t_stat EPROM_reset (DEVICE *dptr, int32 size) +{ + t_stat r; + +// if (EPROM_dev.dctrl & DEBUG_flow) /* entry message */ + printf(" EPROM_reset: base=0000 size=%04X\n", size); + if ((EPROM_unit.flags & UNIT_ATT) == 0) { /* if unattached */ + EPROM_unit.capac = size; /* set EPROM size to 0 */ + if (EPROM_dev.dctrl & DEBUG_flow) /* exit message */ + printf("Done1\n"); +// printf(" EPROM: Available [%04X-%04XH]\n", +// 0, EPROM_unit.capac - 1); + return SCPE_OK; + } + if ((EPROM_unit.flags & UNIT_ATT) == 0) { + printf("EPROM: No file attached\n"); + } + if (EPROM_dev.dctrl & DEBUG_flow) /* exit message */ + printf("Done2\n"); + return SCPE_OK; +} + +/* get a byte from memory */ + +int32 EPROM_get_mbyte(int32 addr) +{ + int32 val; + + if (i8255_unit.u6 & 0x01) { /* EPROM enabled */ + if (EPROM_dev.dctrl & DEBUG_read) + printf("EPROM_get_mbyte: addr=%04X\n", addr); + if ((addr >= 0) && (addr < EPROM_unit.capac)) { + SET_XACK(1); /* good memory address */ + if (EPROM_dev.dctrl & DEBUG_xack) + printf("EPROM_get_mbyte: Set XACK for %04X\n", addr); + val = *(uint8 *)(EPROM_unit.filebuf + addr); + if (EPROM_dev.dctrl & DEBUG_read) + printf(" val=%04X\n", val); + return (val & 0xFF); + } + if (EPROM_dev.dctrl & DEBUG_read) + printf(" EPROM Disabled\n"); + return 0xFF; + } + if (EPROM_dev.dctrl & DEBUG_read) + printf(" Out of range\n"); + return 0xFF; +} + +/* end of iEPROM.c */ diff --git a/MDS-800/common/ijedec.c b/MDS-800/common/ijedec.c new file mode 100644 index 00000000..ffc2ffbe --- /dev/null +++ b/MDS-800/common/ijedec.c @@ -0,0 +1,486 @@ +/* iJEDEC.c: Intel JEDEC Universal Site simulator for SBCs + + Copyright (c) 2011, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + These functions support a simulated i2732 JEDEC device on an iSBC. This + allows the attachment of the device to a binary file containing the JEDEC + code. + + Unit will support 8, 16 and 32 KB EPROMs as well as 8 and 16 KB static + RAMs in the JEDEC sockets. Units must be configured for 8KB for 8KB + SRAM and 32KB for 32KB SRAM. If configured for 16KB, SRAM cannot be + configured. Size is set by configuring the top JEDEC site for an EPROM. + Size and spacing for the other JEDEC units is derived from the top JEDEC + site configuration. Changing the top JEDEC site will clear the + configuration of all other JEDEC sites. The JEDEC driver can be set for + either 8- or 16bit data access. + + The top JEDEC site can only be configured to contain an EPROM. + It contains the reset address for the 8088, 8086, 80188, 80186, + and 80286. + + For illustration 8-bit mode - 4 Sites - configured for 8KB chips + + +--------+ 0xFFFFF + | | + | jedec3 | Only ROM + | | + +--------+ 0xFE000 + + +--------+ 0xFDFFF + | | + | jedec2 | RAM/ROM + | | + +--------+ 0xFC000 + + +--------+ 0xFBFFF + | | + | jedec1 | RAM/ROM + | | + +--------+ 0xFA000 + + +--------+ 0xF9FFF + | | + | jedec0 | RAM/ROM + | | + +--------+ 0xF8000 + + For illustration 16-bit mode - 4 Sites - configured for 8KB chips + + Odd data byte Even data byte + High data byte Low data byte + +--------+ 0xFFFFF +--------+ 0xFFFFE + | | | | + | jedec3 | Only ROM | jedec2 | Only ROM + | | | | + +--------+ 0xFC001 +--------+ 0xFC000 + + +--------+ 0xFBFFF +--------+ 0xFBFFE + | | | | + | jedec3 | RAM/ROM | jedec2 | RAM/ROM + | | | | + +--------+ 0xF8001 +--------+ 0xF8000 + + uptr->filename - ROM image file attached to unit + uptr->capac - unit capacity in bytes + uptr->u3 - unit base address + uptr->u4 - unit device type {none|8krom|16krom|32krom|8kram|32kram} + uptr->u5 - unit flags - ROM or RAM, 8 or 16BIT (top unit only) + uptr->u6 - unit number +*/ + +#include + +#include "multibus_defs.h" + +#define JEDEC_NUM 4 + +#define UNIT_V_DMODE (UNIT_V_UF) /* data bus mode */ +#define UNIT_DMODE (1 << UNIT_V_DMODE) +#define UNIT_V_MSIZE (UNIT_V_UF+1) /* Memory Size */ +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) +#define UNIT_NONE 0 /* No device */ +#define UNIT_8KROM 1 /* 8KB ROM */ +#define UNIT_16KROM 2 /* 16KB ROM */ +#define UNIT_32KROM 3 /* 32KB ROM */ +#define UNIT_8KRAM 4 /* 8KB RAM */ +#define UNIT_32KRAM 5 /* 32KB RAM */ + +#define RAM 0x00000001 +#define D16BIT 0x00000002 + +/* function prototypes */ + +t_stat JEDEC_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat JEDEC_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat JEDEC_attach (UNIT *uptr, char *cptr); +t_stat JEDEC_reset (DEVICE *dptr); +int32 JEDEC_get_mbyte(int32 addr); +void JEDEC_put_mbyte(int32 addr, int32 val); + +/* SIMH JEDEC Standard I/O Data Structures */ + +UNIT JEDEC_unit[] = { + { UDATA (NULL, UNIT_ATTABLE+UNIT_BINK+UNIT_ROABLE+UNIT_RO, 0),0 }, + { UDATA (NULL, UNIT_ATTABLE+UNIT_BINK+UNIT_ROABLE+UNIT_RO, 0),0 }, + { UDATA (NULL, UNIT_ATTABLE+UNIT_BINK+UNIT_ROABLE+UNIT_RO, 0),0 }, + { UDATA (NULL, UNIT_ATTABLE+UNIT_BINK+UNIT_ROABLE+UNIT_RO, 0),0 } +}; + +MTAB JEDEC_mod[] = { + { UNIT_DMODE, 0, "8-Bit", "8B", &JEDEC_set_mode }, + { UNIT_DMODE, UNIT_DMODE, "16-Bit", "16B", &JEDEC_set_mode }, + { UNIT_MSIZE, UNIT_NONE, "Not configured", "NONE", &JEDEC_set_size }, + { UNIT_MSIZE, UNIT_8KROM, "8KB ROM", "8KROM", &JEDEC_set_size }, + { UNIT_MSIZE, UNIT_16KROM, "16KB ROM", "16KROM", &JEDEC_set_size }, + { UNIT_MSIZE, UNIT_32KROM, "32KB ROM", "32KROM", &JEDEC_set_size }, + { UNIT_MSIZE, UNIT_8KRAM, "8KB RAM", "8KRAM", &JEDEC_set_size }, + { UNIT_MSIZE, UNIT_32KRAM, "32KB RAM", "32KRAM", &JEDEC_set_size }, + { 0 } +}; + +DEBTAB JEDEC_debug[] = { + { "ALL", DEBUG_all }, + { "FLOW", DEBUG_flow }, + { "READ", DEBUG_read }, + { "WRITE", DEBUG_write }, + { "LEV1", DEBUG_level1 }, + { "LEV2", DEBUG_level2 }, + { NULL } +}; + +DEVICE JEDEC_dev = { + "JEDEC", //name + JEDEC_unit, //units + NULL, //registers + JEDEC_mod, //modifiers + JEDEC_NUM, //numunits + 16, //aradix + 32, //awidth + 1, //aincr + 16, //dradix + 8, //dwidth + NULL, //examine + NULL, //deposit + &JEDEC_reset, //reset + NULL, //boot + &JEDEC_attach, //attach + NULL, //detach + NULL, //ctxt + DEV_DEBUG, //flags + 0, //dctrl + JEDEC_debug, //debflags + NULL, //msize + NULL //lname +}; + +/* global variables */ + +uint8 *JEDEC_buf[JEDEC_NUM] = { /* JEDEC buffer pointers */ + NULL, + NULL, + NULL, + NULL +}; + +/* JEDEC functions */ + +/* JEDEC attach - force JEDEC reset at completion */ + +t_stat JEDEC_attach (UNIT *uptr, char *cptr) +{ + t_stat r; + + if (JEDEC_dev.dctrl & DEBUG_flow) + printf("\tJEDEC_attach: Entered with cptr=%s\n", cptr); + if ((r = attach_unit (uptr, cptr)) != SCPE_OK) { + if (JEDEC_dev.dctrl & DEBUG_flow) + printf("\tJEDEC_attach: Error\n"); + return r; + } + if (JEDEC_dev.dctrl & DEBUG_flow) + printf("\tJEDEC_attach: Done\n"); + return (JEDEC_reset (NULL)); +} + +/* JEDEC set mode = 8- or 16-bit data bus */ + +t_stat JEDEC_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + UNIT *uptr1; + + if (JEDEC_dev.dctrl & DEBUG_flow) + printf("\tJEDEC_set_mode: Entered with val=%08XH, unit=%d\n", val, uptr->u6); + uptr1 = JEDEC_dev.units + JEDEC_NUM - 1; /* top unit holds this configuration */ + if (val) { /* 16-bit mode */ + uptr1->u5 |= D16BIT; + } else { /* 8-bit mode */ + uptr1->u5 &= ~D16BIT; + } + if (JEDEC_dev.dctrl & DEBUG_flow) + printf("JEDEC%d->u5=%08XH\n", JEDEC_NUM - 1, uptr1->u5); + printf("\tJEDEC_set_mode: Done\n"); +} + +/* JEDEC set type = none, 8krom, 16krom, 32krom, 8kram or 32kram */ + +t_stat JEDEC_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + uint32 i, basadr; + UNIT *uptr1; + + if (JEDEC_dev.dctrl & DEBUG_flow) + printf("\tJEDEC_set_size: Entered with val=%d, unit=%d\n", val, uptr->u6); + uptr1 = JEDEC_dev.units + JEDEC_NUM - 1; /* top unit holds u5 configuration */ + uptr->u4 = val; + switch(val) { + case UNIT_NONE: + uptr->capac = 0; + uptr->u5 &= ~RAM; /* ROM */ + if (uptr->u6 == JEDEC_NUM - 1) {/* top unit ? */ + uptr->u3 = 0; /* base address */ + printf("JEDEC site size set to 8KB\n"); + for (i = 0; i < JEDEC_NUM-1; i++) { /* clear all units but last unit */ + uptr1 = JEDEC_dev.units + i; + uptr1->capac = 0; + } + } + break; + case UNIT_8KROM: + uptr->capac = 0x2000; + uptr1->u5 &= ~RAM; /* ROM */ + basadr = 0x100000 - (uptr->capac * JEDEC_NUM); + printf("JEDEC site base address = %06XH\n", basadr); + if (uptr->u6 == JEDEC_NUM - 1) {/* top unit ? */ + uptr->u3 = basadr + (uptr->capac * uptr->u6); /* base address */ + printf("JEDEC site size set to 8KB\n"); + for (i = 0; i < JEDEC_NUM-1; i++) { /* clear all units but last unit */ + uptr1 = JEDEC_dev.units + i; + uptr1->capac = 0; + } + } else { + if (uptr1->capac != uptr->capac) { + uptr->capac = 0; + printf("JEDEC site size precludes use of this device\n"); + } + } + break; + case UNIT_16KROM: + uptr->capac = 0x4000; + uptr1->u5 &= ~RAM; /* ROM */ + basadr = 0x100000 - (uptr->capac * JEDEC_NUM); + printf("JEDEC site base address = %06XH\n", basadr); + if (uptr->u6 == JEDEC_NUM - 1) {/* top unit ? */ + uptr->u3 = basadr + (uptr->capac * uptr->u6); /* base address */ + printf("JEDEC site size set to 16KB\n"); + for (i = 0; i < JEDEC_NUM-1; i++) { /* clear all units but last unit */ + uptr1 = JEDEC_dev.units + i; + uptr1->capac = 0; + } + } else { + if (uptr1->capac != uptr->capac) { + uptr->capac = 0; + printf("JEDEC site size precludes use of this device\n"); + } + } + break; + case UNIT_32KROM: + uptr->capac = 0x8000; + uptr1->u5 &= ~RAM; /* ROM */ + basadr = 0x100000 - (uptr->capac * JEDEC_NUM); + printf("JEDEC site base address = %06XH\n", basadr); + if (uptr->u6 == JEDEC_NUM - 1) {/* top unit ? */ + uptr->u3 = basadr + (uptr->capac * uptr->u6); /* base address */ + printf("JEDEC site size set to 32KB\n"); + for (i = 0; i < JEDEC_NUM-1; i++) { /* clear all units but last unit */ + uptr1 = JEDEC_dev.units + i; + uptr1->capac = 0; + } + } else { + if (uptr1->capac != uptr->capac) { + uptr->capac = 0; + printf("JEDEC site size precludes use of this device\n"); + } + } + break; + case UNIT_8KRAM: + uptr->capac = 0x2000; + if (uptr->u6 == JEDEC_NUM - 1) {/* top unit ? */ + printf("JEDEC%d cannot be SRAM\n", uptr->u6); + } else { + if (uptr1->capac != uptr->capac) { + uptr->capac = 0; + printf("JEDEC site size precludes use of this device\n"); + } else { + uptr->u5 |= RAM; /* RAM */ + } + } + break; + case UNIT_32KRAM: + uptr->capac = 0x8000; + if (uptr->u6 == JEDEC_NUM - 1) {/* top unit ? */ + printf("JEDEC%d cannot be SRAM\n", uptr->u6); + } else { + if (uptr1->capac != uptr->capac) { + uptr->capac = 0; + printf("JEDEC site size precludes use of this device\n"); + } else { + uptr->u5 |= RAM; /* RAM */ + } + } + break; + default: + if (JEDEC_dev.dctrl & DEBUG_flow) + printf("\tJEDEC_set_size: Error\n"); + return SCPE_ARG; + } + if (JEDEC_buf[uptr->u6]) { /* any change requires a new buffer */ + free (JEDEC_buf[uptr->u6]); + JEDEC_buf[uptr->u6] = NULL; + } + if (JEDEC_dev.dctrl & DEBUG_flow) { + printf("\tJEDEC%d->capac=%04XH\n", uptr->u6, uptr->capac); + printf("\tJEDEC%d->u3[Base addr]=%06XH\n", uptr->u6, uptr->u3); + printf("\tJEDEC%d->u4[val]=%06XH\n", uptr->u6, uptr->u4); + printf("\tJEDEC%d->u5[Flags]=%06XH\n", uptr->u6, uptr->u5); + printf("\tJEDEC%d->u6[unit #]=%06XH\n", uptr->u6, uptr->u6); + uptr1 = JEDEC_dev.units + JEDEC_NUM - 1; /* top unit holds u5 configuration */ + printf("\tJEDEC%d->u5[Flags]=%06XH\n", JEDEC_NUM - 1, uptr1->u5); + printf("\tJEDEC_set_size: Done\n"); + } + return SCPE_OK; +} + +/* JEDEC reset */ + +t_stat JEDEC_reset (DEVICE *dptr) +{ + int32 i, j, c; + FILE *fp; + t_stat r; + UNIT *uptr; + static int flag = 1; + + if (JEDEC_dev.dctrl & DEBUG_flow) + printf("\tJEDEC_reset: Entered\n"); + for (i = 0; i < JEDEC_NUM; i++) { /* handle all umits */ + uptr = JEDEC_dev.units + i; + if (uptr->capac == 0) { /* if not configured */ + printf(" JEDEC%d: Not configured\n", i); + if (flag) { + printf(" ALL: \"set JEDEC3 None | 8krom | 16krom | 32krom | 8kram | 32kram\"\n"); + printf(" EPROM: \"att JEDEC3 \"\n"); + flag = 0; + } + uptr->capac = 0; + /* assume 8KB in base address calculation */ + uptr->u3 = 0xF8000 + (0x2000 * i); /* base address */ + uptr->u4 = 0; /* None */ + uptr->u5 = 0; /* RO */ + uptr->u6 = i; /* unit number - only set here! */ + } + if (uptr->capac) { /* if configured */ + printf(" JEDEC%d: Initializing %2XKB %s [%04X-%04XH]\n", + i, + uptr->capac / 0x400, + uptr->u5 ? "Ram" : "Rom", + uptr->u3, + uptr->u3 + uptr->capac - 1); + if (JEDEC_buf[uptr->u6] == NULL) {/* no buffer allocated */ + JEDEC_buf[uptr->u6] = malloc(uptr->capac); + if (JEDEC_buf[uptr->u6] == NULL) { + if (JEDEC_dev.dctrl & DEBUG_flow) + printf("\tJEDEC_reset: Malloc error\n"); + return SCPE_MEM; + } + } + if ((uptr->u5 & 0x0001) == 0) { /* ROM - load file */ + fp = fopen(uptr->filename, "rb"); + if (fp == NULL) { + printf("\tUnable to open ROM file %s\n", uptr->filename); + printf("\tNo ROM image loaded!!!\n"); + } else { + j = 0; + c = fgetc(fp); + while (c != EOF) { + *(JEDEC_buf[uptr->u6] + j++) = c & 0xFF; + c = fgetc(fp); + if (j >= JEDEC_unit[uptr->u6].capac) { + printf("\tImage is too large - Load truncated!!!\n"); + break; + } + } + fclose(fp); + printf("\t%d bytes of ROM image %s loaded\n", j, uptr->filename); + } + } + } + } + if (JEDEC_dev.dctrl & DEBUG_flow) + printf("\tJEDEC_reset: Done\n"); + return SCPE_OK; +} + +/* I/O instruction handlers, called from the CPU module when an + JEDEC memory read or write is issued. + + Need to fix for hi/low memory operations +*/ + +/* get a byte from memory */ + +int32 JEDEC_get_mbyte(int32 addr) +{ + int32 i, val, org, len; + UNIT *uptr; + + if (JEDEC_dev.dctrl & DEBUG_read) + printf("\tJEDEC_get_mbyte: Entered\n"); + for (i = 0; i < JEDEC_NUM; i++) { /* test all umits for address */ + uptr = JEDEC_dev.units + i; + org = uptr->u3; + len = uptr->capac - 1; + if ((addr >= org) && (addr <= org + len)) { + if (JEDEC_dev.dctrl & DEBUG_read) + printf("\tJEDEC%d Addr=%06XH Org=%06XH Len=%06XH\n", i, addr, org, len); + val = *(JEDEC_buf[uptr->u6] + (addr - org)); + if (JEDEC_dev.dctrl & DEBUG_read) + printf("\tJEDEC_get_mbyte: Exit with [%0XH]\n", val & 0xFF); + return (val & 0xFF); + } + } + if (JEDEC_dev.dctrl & DEBUG_read) + printf("\tJEDEC_get_mbyte: Exit - Out of range\n", addr); + return 0xFF; +} + +/* put a byte into memory */ + +void JEDEC_put_mbyte(int32 addr, int32 val) +{ + int32 i, org, len, type; + UNIT *uptr; + + if (JEDEC_dev.dctrl & DEBUG_write) + printf("\tJEDEC_put_mbyte: Entered\n"); + for (i = 0; i < JEDEC_NUM; i++) { /* test all umits for address */ + uptr = JEDEC_dev.units + i; + org = uptr->u3; + len = uptr->capac - 1; + if ((addr >= org) && (addr < org + len)) { + if (JEDEC_dev.dctrl & DEBUG_write) + printf("\tJEDEC%d Org=%06XH Len=%06XH\n", i, org, len); + if (uptr->u5 & RAM) { /* can't write to ROM */ + *(JEDEC_buf[uptr->u6] + (addr - org)) = val & 0xFF; + if (JEDEC_dev.dctrl & DEBUG_write) + printf("\tJEDEC_put_mbyte: Exit with [%06XH]=%02XH\n", addr, val); + } else + printf("\tJEDEC_put_mbyte: Write to ROM ignored\n"); + } + } + if (JEDEC_dev.dctrl & DEBUG_write) + printf("\tJEDEC_put_mbyte: Exit - Out of range\n"); +} + +/* end of iJEDEC.c */ diff --git a/MDS-800/common/iram16.c b/MDS-800/common/iram16.c new file mode 100644 index 00000000..7b663e47 --- /dev/null +++ b/MDS-800/common/iram16.c @@ -0,0 +1,203 @@ +/* iram.c: Intel RAM simulator for 16-bit SBCs + + Copyright (c) 2011, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + William A. Beech BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + These functions support a simulated RAM device in low memory on an iSBC. These + SBCs do not have the capability to switch off this RAM. In most cases a portion + of the RAM is dual-ported so it also appears in the multibus memory map at + a configurable location. + + Unit will support 16K SRAM sizes. + +*/ + +#include + +#include "multibus_defs.h" + +#define UNIT_V_RSIZE (UNIT_V_UF) /* RAM Size */ +#define UNIT_RSIZE (0x1 << UNIT_V_RSIZE) +#define UNIT_NONE (0) /* No unit */ +#define UNIT_16K (1) /* 16KB */ + +/* function prototypes */ + +t_stat RAM_svc (UNIT *uptr); +t_stat RAM_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat RAM_reset (DEVICE *dptr); +int32 RAM_get_mbyte(int32 addr); +void RAM_put_mbyte(int32 addr, int32 val); + +/* SIMH RAM Standard I/O Data Structures */ + +UNIT RAM_unit = { UDATA (NULL, UNIT_BINK, 0), KBD_POLL_WAIT }; + +MTAB RAM_mod[] = { + { UNIT_RSIZE, UNIT_NONE, "None", "none", &RAM_set_size }, + { UNIT_RSIZE, UNIT_16K, "16KB", "16KB", &RAM_set_size }, + { 0 } +}; + +DEBTAB RAM_debug[] = { + { "ALL", DEBUG_all }, + { "FLOW", DEBUG_flow }, + { "READ", DEBUG_read }, + { "WRITE", DEBUG_write }, + { "LEV1", DEBUG_level1 }, + { "LEV2", DEBUG_level2 }, + { NULL } +}; + +DEVICE RAM_dev = { + "RAM", //name + &RAM_unit, //units + NULL, //registers + RAM_mod, //modifiers + 1, //numunits + 16, //aradix + 32, //awidth + 1, //aincr + 16, //dradix + 8, //dwidth + NULL, //examine + NULL, //deposit + &RAM_reset, //reset + NULL, //boot + NULL, //attach + NULL, //detach + NULL, //ctxt + DEV_DEBUG, //flags + 0, //dctrl + RAM_debug, //debflags + NULL, //msize + NULL //lname +}; + +/* global variables */ + +uint8 *RAM_buf = NULL; /* RAM buffer pointer */ + +/* RAM functions */ + +/* RAM set size = none or 16KB */ + +t_stat RAM_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + if (RAM_dev.dctrl & DEBUG_flow) + printf("RAM_set_size: val=%d\n", val); + if ((val < UNIT_NONE) || (val > UNIT_16K)) { + if (RAM_dev.dctrl & DEBUG_flow) + printf("RAM_set_size: Size error\n"); + return SCPE_ARG; + } + RAM_unit.capac = 0x4000 * val; /* set size */ + RAM_unit.u3 = 0x0000; /* base is 0 */ + RAM_unit.u4 = val; /* save val */ + if (RAM_buf) { /* if changed, allocate new buffer */ + free (RAM_buf); + RAM_buf = NULL; + } + if (RAM_dev.dctrl & DEBUG_flow) + printf("RAM_set_size: Done\n"); + return (RAM_reset (NULL)); /* force reset after reconfig */ +} + +/* RAM reset */ + +t_stat RAM_reset (DEVICE *dptr) +{ + int j; + FILE *fp; + + if (RAM_dev.dctrl & DEBUG_flow) + printf("RAM_reset: \n"); + if (RAM_unit.capac == 0) { /* if undefined */ + printf(" RAM: defaulted for 16KB\n"); + printf(" \"set RAM 16KB\"\n"); + RAM_unit.capac = 0x4000; + RAM_unit.u3 = 0; + RAM_unit.u4 = 1; + } + printf(" RAM: Initializing [%04X-%04XH]\n", + RAM_unit.u3, + RAM_unit.u3 + RAM_unit.capac - 1); + if (RAM_buf == NULL) { /* no buffer allocated */ + RAM_buf = malloc(RAM_unit.capac); + if (RAM_buf == NULL) { + if (RAM_dev.dctrl & DEBUG_flow) + printf("RAM_reset: Malloc error\n"); + return SCPE_MEM; + } + } + if (RAM_dev.dctrl & DEBUG_flow) + printf("RAM_reset: Done\n"); + return SCPE_OK; +} + +/* I/O instruction handlers, called from the CPU module when an + RAM memory read or write is issued. +*/ + +/* get a byte from memory */ + +int32 RAM_get_mbyte(int32 addr) +{ + int32 val, org, len; + + org = RAM_unit.u3; + len = RAM_unit.capac - 1; + if (RAM_dev.dctrl & DEBUG_read) + printf("RAM_get_mbyte: addr=%04X", addr); + if ((addr >= org) && (addr <= org + len)) { + val = *(RAM_buf + (addr - org)); + if (RAM_dev.dctrl & DEBUG_read) + printf(" val=%04X\n", val); + return (val & 0xFF); + } + if (RAM_dev.dctrl & DEBUG_read) + printf(" Out of range\n", addr); + return 0xFF; +} + +/* put a byte to memory */ + +void RAM_put_mbyte(int32 addr, int32 val) +{ + int32 org, len; + + org = RAM_unit.u3; + len = RAM_unit.capac - 1; + if (RAM_dev.dctrl & DEBUG_write) + printf("RAM_put_mbyte: addr=%04X, val=%02X", addr, val); + if ((addr >= org) && (addr < org + len)) { + *(RAM_buf + (addr - org)) = val & 0xFF; + if (RAM_dev.dctrl & DEBUG_write) + printf("\n"); + return; + } + if (RAM_dev.dctrl & DEBUG_write) + printf(" Out of range\n", val); +} + +/* end of iram.c */ diff --git a/MDS-800/common/iram8.c b/MDS-800/common/iram8.c new file mode 100644 index 00000000..46d2a4b4 --- /dev/null +++ b/MDS-800/common/iram8.c @@ -0,0 +1,168 @@ +/* iRAM8.c: Intel RAM simulator for 8-bit SBCs + + Copyright (c) 2011, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + These functions support a simulated i8111 or 8102 RAM device on an iSBC. + + ?? ??? 11 - Original file. + 16 Dec 12 - Modified to use isbc_80_10.cfg file to set base and size. +*/ + +#include "system_defs.h" + +#define SET_XACK(VAL) (xack = VAL) + +/* function prototypes */ + +t_stat RAM_svc (UNIT *uptr); +t_stat RAM_reset (DEVICE *dptr, int32 base, int32 size); +int32 RAM_get_mbyte(int32 addr); +void RAM_put_mbyte(int32 addr, int32 val); + +extern UNIT i8255_unit; +extern uint8 xack; /* XACK signal */ + +/* SIMH RAM Standard I/O Data Structures */ + +UNIT RAM_unit = { UDATA (NULL, UNIT_BINK, 0), KBD_POLL_WAIT }; + +DEBTAB RAM_debug[] = { + { "ALL", DEBUG_all }, + { "FLOW", DEBUG_flow }, + { "READ", DEBUG_read }, + { "WRITE", DEBUG_write }, + { "XACK", DEBUG_xack }, + { "LEV1", DEBUG_level1 }, + { "LEV2", DEBUG_level2 }, + { NULL } +}; + +DEVICE RAM_dev = { + "RAM", //name + &RAM_unit, //units + NULL, //registers + NULL, //modifiers + 1, //numunits + 16, //aradix + 32, //awidth + 1, //aincr + 16, //dradix + 8, //dwidth + NULL, //examine + NULL, //deposit +// &RAM_reset, //reset + NULL, //reset + NULL, //boot + NULL, //attach + NULL, //detach + NULL, //ctxt + DEV_DEBUG, //flags + 0, //dctrl + RAM_debug, //debflags + NULL, //msize + NULL //lname +}; + +/* global variables */ + +/* RAM functions */ + +/* RAM reset */ + +t_stat RAM_reset (DEVICE *dptr, int32 base, int32 size) +{ +// if (RAM_dev.dctrl & DEBUG_flow) + printf(" RAM_reset: base=%04X size=%04X\n", base, size-1); + if (RAM_unit.capac == 0) { /* if undefined */ + RAM_unit.capac = size; + RAM_unit.u3 = base; + } + if (RAM_unit.filebuf == NULL) { /* no buffer allocated */ + RAM_unit.filebuf = malloc(RAM_unit.capac); + if (RAM_unit.filebuf == NULL) { + if (RAM_dev.dctrl & DEBUG_flow) + printf("RAM_set_size: Malloc error\n"); + return SCPE_MEM; + } + } +// printf(" RAM: Available [%04X-%04XH]\n", +// RAM_unit.u3, +// RAM_unit.u3 + RAM_unit.capac - 1); + if (RAM_dev.dctrl & DEBUG_flow) + printf("RAM_reset: Done\n"); + return SCPE_OK; +} + +/* get a byte from memory */ + +int32 RAM_get_mbyte(int32 addr) +{ + int32 val; + + if (i8255_unit.u6 & 0x02) { /* enable RAM */ + if (RAM_dev.dctrl & DEBUG_read) + printf("RAM_get_mbyte: addr=%04X\n", addr); + if ((addr >= RAM_unit.u3) && (addr < (RAM_unit.u3 + RAM_unit.capac))) { + SET_XACK(1); /* good memory address */ + if (RAM_dev.dctrl & DEBUG_xack) + printf("RAM_get_mbyte: Set XACK for %04X\n", addr); + val = *(uint8 *)(RAM_unit.filebuf + (addr - RAM_unit.u3)); + if (RAM_dev.dctrl & DEBUG_read) + printf(" val=%04X\n", val); + return (val & 0xFF); + } + if (RAM_dev.dctrl & DEBUG_read) + printf(" RAM disabled\n"); + return 0xFF; + } + if (RAM_dev.dctrl & DEBUG_read) + printf(" Out of range\n"); + return 0xFF; +} + +/* put a byte to memory */ + +void RAM_put_mbyte(int32 addr, int32 val) +{ + if (i8255_unit.u6 & 0x02) { /* enable RAM */ + if (RAM_dev.dctrl & DEBUG_write) + printf("RAM_put_mbyte: addr=%04X, val=%02X\n", addr, val); + if ((addr >= RAM_unit.u3) && (addr < RAM_unit.u3 + RAM_unit.capac)) { + SET_XACK(1); /* good memory address */ + if (RAM_dev.dctrl & DEBUG_xack) + printf("RAM_put_mbyte: Set XACK for %04X\n", addr); + *(uint8 *)(RAM_unit.filebuf + (addr - RAM_unit.u3)) = val & 0xFF; + if (RAM_dev.dctrl & DEBUG_write) + printf("\n"); + return; + } + if (RAM_dev.dctrl & DEBUG_write) + printf(" RAM disabled\n"); + return; + } + if (RAM_dev.dctrl & DEBUG_write) + printf(" Out of range\n"); +} + +/* end of iRAM8.c */ diff --git a/MDS-800/common/isbc064.c b/MDS-800/common/isbc064.c new file mode 100644 index 00000000..4e7c18ca --- /dev/null +++ b/MDS-800/common/isbc064.c @@ -0,0 +1,202 @@ +/* isbc064.c: Intel iSBC064 64K Byte Memory Card + + Copyright (c) 2011, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + These functions support a simulated isbc016, isbc032, isbc048 and isbc064 + memory card on an Intel multibus system. + + ?? ??? 11 - Original file. + 16 Dec 12 - Modified to use system_80_10.cfg file to set base and size. + +*/ + +#include "system_defs.h" + +#define SET_XACK(VAL) (xack = VAL) + +/* prototypes */ + +t_stat isbc064_reset (DEVICE *dptr); +int32 isbc064_get_mbyte(int32 addr); +int32 isbc064_get_mword(int32 addr); +void isbc064_put_mbyte(int32 addr, int32 val); +void isbc064_put_mword(int32 addr, int32 val); + +extern uint8 xack; /* XACK signal */ + +/* isbc064 Standard I/O Data Structures */ + +UNIT isbc064_unit = { + UDATA (NULL, UNIT_FIX+UNIT_DISABLE+UNIT_BINK, 65536), KBD_POLL_WAIT +}; + +DEBTAB isbc064_debug[] = { + { "ALL", DEBUG_all }, + { "FLOW", DEBUG_flow }, + { "READ", DEBUG_read }, + { "WRITE", DEBUG_write }, + { "XACK", DEBUG_xack }, + { "LEV1", DEBUG_level1 }, + { "LEV2", DEBUG_level2 }, + { NULL } +}; + +DEVICE isbc064_dev = { + "SBC064", //name + &isbc064_unit, //units + NULL, //registers + NULL, //modifiers + 1, //numunits + 16, //aradix + 8, //awidth + 1, //aincr + 16, //dradix + 8, //dwidth + NULL, //examine + NULL, //deposite + &isbc064_reset, //reset + NULL, //boot + NULL, //attach + NULL, //detach + NULL, //ctxt + DEV_DEBUG+DEV_DISABLE+DEV_DIS, //flags + 0, //dctrl + isbc064_debug, //debflags + NULL, //msize + NULL //lname +}; + +/* iSBC064 globals */ + +/* Reset routine */ + +t_stat isbc064_reset (DEVICE *dptr) +{ + if (isbc064_dev.dctrl & DEBUG_flow) + printf("isbc064_reset: "); + if ((isbc064_dev.flags & DEV_DIS) == 0) { + isbc064_unit.capac = SBC064_SIZE; + isbc064_unit.u3 = SBC064_BASE; + printf("iSBC 064: Available[%04X-%04XH]\n", + isbc064_unit.u3, + isbc064_unit.u3 + isbc064_unit.capac - 1); + } + if (isbc064_unit.filebuf == NULL) { + isbc064_unit.filebuf = malloc(isbc064_unit.capac); + if (isbc064_unit.filebuf == NULL) { + if (isbc064_dev.dctrl & DEBUG_flow) + printf("isbc064_reset: Malloc error\n"); + return SCPE_MEM; + } + } + if (isbc064_dev.dctrl & DEBUG_flow) + printf("isbc064_reset: Done\n"); + return SCPE_OK; +} + +/* get a byte from memory */ + +int32 isbc064_get_mbyte(int32 addr) +{ + int32 val, org, len; + int i = 0; + + if ((isbc064_dev.flags & DEV_DIS) == 0) { + org = isbc064_unit.u3; + len = isbc064_unit.capac; + if (isbc064_dev.dctrl & DEBUG_read) + printf("isbc064_get_mbyte: addr=%04X", addr); + if (isbc064_dev.dctrl & DEBUG_read) + printf("isbc064_put_mbyte: org=%04X, len=%04X\n", org, len); + if ((addr >= org) && (addr < (org + len))) { + SET_XACK(1); /* good memory address */ + if (isbc064_dev.dctrl & DEBUG_xack) + printf("isbc064_get_mbyte: Set XACK for %04X\n", addr); + val = *(uint8 *)(isbc064_unit.filebuf + (addr - org)); + if (isbc064_dev.dctrl & DEBUG_read) + printf(" val=%04X\n", val); + return (val & 0xFF); + } else { + if (isbc064_dev.dctrl & DEBUG_read) + printf(" Out of range\n"); + return 0xFF; /* multibus has active high pullups */ + } + } + if (isbc064_dev.dctrl & DEBUG_read) + printf(" Disabled\n"); + return 0xFF; /* multibus has active high pullups */ +} + +/* get a word from memory */ + +int32 isbc064_get_mword(int32 addr) +{ + int32 val; + + val = isbc064_get_mbyte(addr); + val |= (isbc064_get_mbyte(addr+1) << 8); + return val; +} + +/* put a byte into memory */ + +void isbc064_put_mbyte(int32 addr, int32 val) +{ + int32 org, len; + int i = 0; + + if ((isbc064_dev.flags & DEV_DIS) == 0) { + org = isbc064_unit.u3; + len = isbc064_unit.capac; + if (isbc064_dev.dctrl & DEBUG_write) + printf("isbc064_put_mbyte: addr=%04X, val=%02X\n", addr, val); + if (isbc064_dev.dctrl & DEBUG_write) + printf("isbc064_put_mbyte: org=%04X, len=%04X\n", org, len); + if ((addr >= org) && (addr < (org + len))) { + SET_XACK(1); /* good memory address */ + if (isbc064_dev.dctrl & DEBUG_xack) + printf("isbc064_put_mbyte: Set XACK for %04X\n", addr); + *(uint8 *)(isbc064_unit.filebuf + (addr - org)) = val & 0xFF; + if (isbc064_dev.dctrl & DEBUG_xack) + printf("isbc064_put_mbyte: Return\n"); + return; + } else { + if (isbc064_dev.dctrl & DEBUG_write) + printf(" Out of range\n"); + return; + } + } + if (isbc064_dev.dctrl & DEBUG_write) + printf("isbc064_put_mbyte: Disabled\n"); +} + +/* put a word into memory */ + +void isbc064_put_mword(int32 addr, int32 val) +{ + isbc064_put_mbyte(addr, val); + isbc064_put_mbyte(addr+1, val << 8); +} + +/* end of isbc064.c */ diff --git a/MDS-800/common/isbc064b.c b/MDS-800/common/isbc064b.c new file mode 100644 index 00000000..c23c5e6a --- /dev/null +++ b/MDS-800/common/isbc064b.c @@ -0,0 +1,256 @@ +/* isbc064.c: Intel iSBC064 64K Byte Memory Card + + Copyright (c) 2011, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + These functions support a simulated isbc016, isbc032, isbc048 and isbc064 memory card + on an Intel multibus system. + + */ + +#include + +#include "multibus_defs.h" + +#define UNIT_V_MSIZE (UNIT_V_UF) /* Memory Size */ +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) +#define UNIT_V_MBASE (UNIT_V_UF+1) /* Memory Base */ +#define UNIT_MBASE (1 << UNIT_V_MBASE) + +/* prototypes */ + +t_stat isbc064_reset (DEVICE *dptr); +t_stat isbc064_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat isbc064_set_base (UNIT *uptr, int32 val, char *cptr, void *desc); +int32 isbc064_get_mbyte(int32 addr); +int32 isbc064_get_mword(int32 addr); +void isbc064_put_mbyte(int32 addr, int32 val); +void isbc064_put_mword(int32 addr, int32 val); + +/* isbc064 Standard I/O Data Structures */ + +UNIT isbc064_unit = { UDATA (NULL, UNIT_FIX+UNIT_DISABLE+UNIT_BINK, 65536), KBD_POLL_WAIT }; + +MTAB isbc064_mod[] = { + { UNIT_MSIZE, 16384, NULL, "16K", &isbc064_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &isbc064_set_size }, + { UNIT_MSIZE, 49152, NULL, "48K", &isbc064_set_size }, + { UNIT_MSIZE, 65535, NULL, "64K", &isbc064_set_size }, + { UNIT_MBASE, 0, NULL, "B0K", &isbc064_set_base }, + { UNIT_MBASE, 16384, NULL, "B16K", &isbc064_set_base }, + { UNIT_MBASE, 32768, NULL, "B32K", &isbc064_set_base }, + { UNIT_MBASE, 49152, NULL, "B48K", &isbc064_set_base }, + { 0 } +}; + +DEBTAB isbc064_debug[] = { + { "ALL", DEBUG_all }, + { "FLOW", DEBUG_flow }, + { "READ", DEBUG_read }, + { "WRITE", DEBUG_write }, + { "LEV1", DEBUG_level1 }, + { "LEV2", DEBUG_level2 }, + { NULL } +}; + +DEVICE isbc064_dev = { + "SBC064", //name + &isbc064_unit, //units + NULL, //registers + isbc064_mod, //modifiers + 1, //numunits + 16, //aradix + 8, //awidth + 1, //aincr + 16, //dradix + 8, //dwidth + NULL, //examine + NULL, //deposite + &isbc064_reset, //reset + NULL, //boot + NULL, //attach + NULL, //detach + NULL, //ctxt + DEV_DEBUG+DEV_DISABLE+DEV_DIS, //flags + 0, //dctrl + isbc064_debug, //debflags + NULL, //msize + NULL //lname +}; + +/* iSBC064 globals */ + +uint8 *MB_buf = NULL; //pointer to memory buffer + +/* Set memory size routine */ + +t_stat isbc064_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + int32 mc = 0; + uint32 i; + + if (isbc064_dev.dctrl & DEBUG_flow) + printf("isbc064_set_size: val=%04X\n", val); + if ((val <= 0) || (val > MAXMEMSIZE)) { + if (isbc064_dev.dctrl & DEBUG_flow) + printf("isbc064_set_size: Memory size error\n"); + return SCPE_ARG; + } + isbc064_unit.capac = val; + for (i = isbc064_unit.capac; i < MAXMEMSIZE; i++) + isbc064_put_mbyte(i, 0); + isbc064_unit.capac = val; + isbc064_unit.u3 = 0; + if (MB_buf) { + free (MB_buf); + MB_buf = NULL; + } + if (isbc064_dev.dctrl & DEBUG_flow) + printf("isbc064_set_size: Done\n"); + return SCPE_OK; +} + +/* Set memory base address routine */ + +t_stat isbc064_set_base (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + if (isbc064_dev.dctrl & DEBUG_flow) + printf("isbc064_set_base: val=%04X\n", val); + if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0)) { + if (isbc064_dev.dctrl & DEBUG_flow) + printf("isbc064_set_base: Base address error\n"); + return SCPE_ARG; + } + isbc064_unit.u3 = val; + if (MB_buf) { + free (MB_buf); + MB_buf = NULL; + } + if (isbc064_dev.dctrl & DEBUG_flow) + printf("isbc064_set_base: Done\n"); + return (isbc064_reset (NULL)); +} + +/* Reset routine */ + +t_stat isbc064_reset (DEVICE *dptr) +{ + if (isbc064_dev.dctrl & DEBUG_flow) + printf("isbc064_reset: \n"); + if ((isbc064_dev.flags & DEV_DIS) == 0) { + printf("Initializing %s [%04X-%04XH]\n", "iSBC-064", + isbc064_unit.u3, + isbc064_unit.u3 + isbc064_unit.capac - 1); + if (MB_buf == NULL) { + MB_buf = malloc(isbc064_unit.capac); + if (MB_buf == NULL) { + if (isbc064_dev.dctrl & DEBUG_flow) + printf("isbc064_reset: Malloc error\n"); + return SCPE_MEM; + } + } + } + if (isbc064_dev.dctrl & DEBUG_flow) + printf("isbc064_reset: Done\n"); + return SCPE_OK; +} + +/* I/O instruction handlers, called from the CPU module when an + external memory read or write is issued. +*/ + +/* get a byte from memory */ + +int32 isbc064_get_mbyte(int32 addr) +{ + int32 val, org, len; + int i = 0; + + if ((isbc064_dev.flags & DEV_DIS) == 0) { + org = isbc064_unit.u3; + len = isbc064_unit.capac - 1; + if (isbc064_dev.dctrl & DEBUG_read) + printf("isbc064_get_mbyte: addr=%04X", addr); + if ((addr >= org) && (addr <= org + len)) { + val = *(MB_buf + (addr - org)); + if (isbc064_dev.dctrl & DEBUG_read) + printf(" val=%04X\n", val); + return (val & 0xFF); + } else { + if (isbc064_dev.dctrl & DEBUG_read) + printf(" Out of range\n"); + return 0xFF; /* multibus has active high pullups */ + } + } + if (isbc064_dev.dctrl & DEBUG_read) + printf(" Disabled\n"); + return 0xFF; /* multibus has active high pullups */ +} + +/* get a word from memory */ + +int32 isbc064_get_mword(int32 addr) +{ + int32 val; + + val = isbc064_get_mbyte(addr); + val |= (isbc064_get_mbyte(addr+1) << 8); + return val; +} + +/* put a byte into memory */ + +void isbc064_put_mbyte(int32 addr, int32 val) +{ + int32 org, len, type; + int i = 0; + + if ((isbc064_dev.flags & DEV_DIS) == 0) { + org = isbc064_unit.u3; + len = isbc064_unit.capac - 1; + if (isbc064_dev.dctrl & DEBUG_write) + printf("isbc064_put_mbyte: addr=%04X, val=%02X", addr, val); + if ((addr >= org) && (addr < org + len)) { + *(MB_buf + (addr - org)) = val & 0xFF; + if (isbc064_dev.dctrl & DEBUG_write) + printf("\n"); + return; + } else { + if (isbc064_dev.dctrl & DEBUG_write) + printf(" Out of range\n"); + return; + } + } + if (isbc064_dev.dctrl & DEBUG_write) + printf("isbc064_put_mbyte: Disabled\n"); +} + +/* put a word into memory */ + +void isbc064_put_mword(int32 addr, int32 val) +{ + isbc064_put_mbyte(addr, val); + isbc064_put_mbyte(addr+1, val << 8); +} + +/* end of isbc064.c */ diff --git a/MDS-800/common/isbc064x.c b/MDS-800/common/isbc064x.c new file mode 100644 index 00000000..2685ec5f --- /dev/null +++ b/MDS-800/common/isbc064x.c @@ -0,0 +1,205 @@ +/* isbc064.c: Intel iSBC064 64K Byte Memory Card + + Copyright (c) 2011, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + These functions support a simulated isbc016, isbc032, isbc048 and isbc064 + memory card on an Intel multibus system. + + ?? ??? 11 - Original file. + 16 Dec 12 - Modified to use system_80_10.cfg file to set base and size. + +*/ + +#include +#include "multibus_defs.h" + +#define SET_XACK(VAL) (XACK = VAL) + +/* prototypes */ + +t_stat isbc064_reset (DEVICE *dptr); +int32 isbc064_get_mbyte(int32 addr); +int32 isbc064_get_mword(int32 addr); +void isbc064_put_mbyte(int32 addr, int32 val); +void isbc064_put_mword(int32 addr, int32 val); + +extern uint8 XACK; /* XACK signal */ + +/* isbc064 Standard I/O Data Structures */ + +UNIT isbc064_unit = { + UDATA (NULL, UNIT_FIX+UNIT_DISABLE+UNIT_BINK, 65536), KBD_POLL_WAIT +}; + +DEBTAB isbc064_debug[] = { + { "ALL", DEBUG_all }, + { "FLOW", DEBUG_flow }, + { "READ", DEBUG_read }, + { "WRITE", DEBUG_write }, + { "XACK", DEBUG_xack }, + { "LEV1", DEBUG_level1 }, + { "LEV2", DEBUG_level2 }, + { NULL } +}; + +DEVICE isbc064_dev = { + "SBC064", //name + &isbc064_unit, //units + NULL, //registers + NULL, //modifiers + 1, //numunits + 16, //aradix + 8, //awidth + 1, //aincr + 16, //dradix + 8, //dwidth + NULL, //examine + NULL, //deposite + &isbc064_reset, //reset + NULL, //boot + NULL, //attach + NULL, //detach + NULL, //ctxt + DEV_DEBUG+DEV_DISABLE+DEV_DIS, //flags + 0, //dctrl + isbc064_debug, //debflags + NULL, //msize + NULL //lname +}; + +/* iSBC064 globals */ + +/* Reset routine */ + +t_stat isbc064_reset (DEVICE *dptr) +{ + if (isbc064_dev.dctrl & DEBUG_flow) + printf("isbc064_reset: \n"); + if ((isbc064_dev.flags & DEV_DIS) == 0) { + if (isbc064_dev.dctrl & DEBUG_flow) + printf("isbc064_reset: Size=%04X\n", isbc064_unit.capac - 1); + if (isbc064_dev.dctrl & DEBUG_flow) + printf("isbc064_reset: Base address=%04X\n", isbc064_unit.u3); + printf("iSBC 064: Available[%04X-%04XH]\n", + isbc064_unit.u3, + isbc064_unit.u3 + isbc064_unit.capac - 1); + } + if (isbc064_unit.filebuf == NULL) { + isbc064_unit.filebuf = malloc(isbc064_unit.capac); + if (isbc064_unit.filebuf == NULL) { + if (isbc064_dev.dctrl & DEBUG_flow) + printf("isbc064_reset: Malloc error\n"); + return SCPE_MEM; + } + } + if (isbc064_dev.dctrl & DEBUG_flow) + printf("isbc064_reset: Done\n"); + return SCPE_OK; +} + +/* get a byte from memory */ + +int32 isbc064_get_mbyte(int32 addr) +{ + int32 val, org, len; + int i = 0; + + if ((isbc064_dev.flags & DEV_DIS) == 0) { + org = isbc064_unit.u3; + len = isbc064_unit.capac; + if (isbc064_dev.dctrl & DEBUG_read) + printf("isbc064_get_mbyte: addr=%04X", addr); + if (isbc064_dev.dctrl & DEBUG_read) + printf("isbc064_put_mbyte: org=%04X, len=%04X\n", org, len); + if ((addr >= org) && (addr < (org + len))) { + SET_XACK(1); /* good memory address */ + if (isbc064_dev.dctrl & DEBUG_xack) + printf("isbc064_get_mbyte: Set XACK for %04X\n", addr); + val = *(uint8 *)(isbc064_unit.filebuf + (addr - org)); + if (isbc064_dev.dctrl & DEBUG_read) + printf(" val=%04X\n", val); + return (val & 0xFF); + } else { + if (isbc064_dev.dctrl & DEBUG_read) + printf(" Out of range\n"); + return 0xFF; /* multibus has active high pullups */ + } + } + if (isbc064_dev.dctrl & DEBUG_read) + printf(" Disabled\n"); + return 0xFF; /* multibus has active high pullups */ +} + +/* get a word from memory */ + +int32 isbc064_get_mword(int32 addr) +{ + int32 val; + + val = isbc064_get_mbyte(addr); + val |= (isbc064_get_mbyte(addr+1) << 8); + return val; +} + +/* put a byte into memory */ + +void isbc064_put_mbyte(int32 addr, int32 val) +{ + int32 org, len; + int i = 0; + + if ((isbc064_dev.flags & DEV_DIS) == 0) { + org = isbc064_unit.u3; + len = isbc064_unit.capac; + if (isbc064_dev.dctrl & DEBUG_write) + printf("isbc064_put_mbyte: addr=%04X, val=%02X\n", addr, val); + if (isbc064_dev.dctrl & DEBUG_write) + printf("isbc064_put_mbyte: org=%04X, len=%04X\n", org, len); + if ((addr >= org) && (addr < (org + len))) { + SET_XACK(1); /* good memory address */ + if (isbc064_dev.dctrl & DEBUG_xack) + printf("isbc064_put_mbyte: Set XACK for %04X\n", addr); + *(uint8 *)(isbc064_unit.filebuf + (addr - org)) = val & 0xFF; + if (isbc064_dev.dctrl & DEBUG_xack) + printf("isbc064_put_mbyte: Return\n"); + return; + } else { + if (isbc064_dev.dctrl & DEBUG_write) + printf(" Out of range\n"); + return; + } + } + if (isbc064_dev.dctrl & DEBUG_write) + printf("isbc064_put_mbyte: Disabled\n"); +} + +/* put a word into memory */ + +void isbc064_put_mword(int32 addr, int32 val) +{ + isbc064_put_mbyte(addr, val); + isbc064_put_mbyte(addr+1, val << 8); +} + +/* end of isbc064.c */ diff --git a/MDS-800/common/isbc208.c b/MDS-800/common/isbc208.c new file mode 100644 index 00000000..349820bc --- /dev/null +++ b/MDS-800/common/isbc208.c @@ -0,0 +1,1699 @@ +/* isbc208.c: Intel iSBC208 Floppy Disk adapter + + Copyright (c) 2011, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + These functions support a simulated iSBC208 interface to 4 each 8-, 5 1/4-, or + 3 1/2-inch floppy disk drives. Commands are setup with programmed I/O to the + simulated ports of an i8237 DMA controller and an i8272 FDC. Data transfer + to/from the simulated disks is performed directly with the multibus memory. + + The iSBC-208 can be configured for 8- or 16-bit addresses. It defaults to 8-bit + addresses for the 8080/8085 processors. It can be configured for I/O port + addresses with 3-bits (8-bit address) or 11-bits (16-bit address). Default is + 3-bits set to 0. This defines the port offset to be used to determine the actual + port address. Bus priority can be configured for parallel or serial mode. Default is + serial. The multibus interface interrupt can be configured for interrupt 0-7. + Default is none. Since all channel registers in the i8237 are 16-bit, transfers + are done as two 8-bit operations, low- then high-byte. + + Port addressing is as follows (Port offset = 0): + + Port Mode Command Function + + 00 Write Load DMAC Channel 0 Base and Current Address Regsiters + Read Read DMAC Channel 0 Current Address Register + 01 Write Load DMAC Channel 0 Base and Current Word Count Registers + Read Read DMAC Channel 0 Current Word Count Register + 04 Write Load DMAC Channel 2 Base and Current Address Regsiters + Read Read DMAC Channel 2 Current Address Register + 05 Write Load DMAC Channel 2 Base and Current Word Count Registers + Read Read DMAC Channel 2 Current Word Count Register + 06 Write Load DMAC Channel 3 Base and Current Address Regsiters + Read Read DMAC Channel 3 Current Address Register + 07 Write Load DMAC Channel 3 Base and Current Word Count Registers + Read Read DMAC Channel 3 Current Word Count Register + 08 Write Load DMAC Command Register + Read Read DMAC Status Register + 09 Write Load DMAC Request Register + OA Write Set/Reset DMAC Mask Register + OB Write Load DMAC Mode Register + OC Write Clear DMAC First/Last Flip-Flop + 0D Write DMAC Master Clear + OF Write Load DMAC Mask Register + 10 Read Read FDC Status Register + 11 Write Load FDC Data Register + Read Read FDC Data Register + 12 Write Load Controller Auxiliary Port + Read Poll Interrupt Status + 13 Write Controller Reset + 14 Write Load Controller Low-Byte Segment Address Register + 15 Write Load Controller High-Byte Segment Address Register + 20-2F Read/Write Reserved for iSBX Multimodule Board + + Register usage is defined in the following paragraphs. + + Read/Write DMAC Address Registers + + Used to simultaneously load a channel's current-address register and base-address + register with the memory address of the first byte to be transferred. (The Channel + 0 current/base address register must be loaded prior to initiating a diskette read + or write operation.) Since each channel's address registers are 16 bits in length + (64K address range), two "write address register" commands must be executed in + order to load the complete current/base address registers for any channel. + + Read/Write DMAC Word Count Registers + + The Write DMAC Word Count Register command is used to simultaneously load a + channel's current and base word-count registers with the number of bytes + to be transferred during a subsequent DMA operation. Since the word-count + registers are 16-bits in length, two commands must be executed to load both + halves of the registers. + + Write DMAC Command Register + + The Write DMAC Command Register command loads an 8-bit byte into the + DMAC's command register to define the operating characteristics of the + DMAC. The functions of the individual bits in the command register are + defined in the following diagram. Note that only two bits within the + register are applicable to the controller; the remaining bits select + functions that are not supported and, accordingly, must always be set + to zero. + + 7 6 5 4 3 2 1 0 + +---+---+---+---+---+---+---+---+ + | 0 0 0 0 0 0 | + +---+---+---+---+---+---+---+---+ + | | + | +---------- 0 CONTROLLER ENABLE + | 1 CONTROLLER DISABLE + | + +------------------ 0 FIXED PRIORITY + 1 ROTATING PRIORITY + + Read DMAC Status Register Command + + The Read DMAC Status Register command accesses an 8-bit status byte that + identifies the DMA channels that have reached terminal count or that + have a pending DMA request. + + 7 6 5 4 3 2 1 0 + +---+---+---+---+---+---+---+---+ + | 0 0 | + +---+---+---+---+---+---+---+---+ + | | | | | | + | | | | | +-- CHANNEL 0 TC + | | | | +---------- CHANNEL 2 TC + | | | +-------------- CHANNEL 3 TC + | | +------------------ CHANNEL 0 DMA REQUEST + | +-------------------------- CHANNEL 2 DMA REQUEST + +------------------------------ CHANNEL 3 DMA REQUEST + + Write DMAC Request Register + + The data byte associated with the Write DMAC Request Register command + sets or resets a channel's associated request bit within the DMAC's + internal 4-bit request register. + + 7 6 5 4 3 2 1 0 + +---+---+---+---+---+---+---+---+ + | X X X X X | + +---+---+---+---+---+---+---+---+ + | | | + | +---+-- 00 SELECT CHANNEL 0 + | 01 SELECT CHANNEL 1 + | 10 SELECT CHANNEL 2 + | 11 SELECT CHANNEL 3 + | + +---------- 0 RESET REQUEST BIT + 1 SET REQUEST BIT + + Set/Reset DMAC Mask Register + + Prior to a DREQ-initiated DMA transfer, the channel's mask bit must + be reset to enable recognition of the DREQ input. When the transfer + is complete (terminal count reached or external EOP applied) and + the channel is not programmed to autoinitialize, the channel's + mask bit is automatically set (disabling DREQ) and must be reset + prior to a subsequent DMA transfer. All four bits of the mask + register are set (disabling the DREQ inputs) by a DMAC master + clear or controller reset. Additionally, all four bits can be + set/reset by a single Write DMAC Mask Register command. + + + 7 6 5 4 3 2 1 0 + +---+---+---+---+---+---+---+---+ + | X X X X X | + +---+---+---+---+---+---+---+---+ + | | | + | +---+-- 00 SELECT CHANNEL 0 + | 01 SELECT CHANNEL 1 + | 10 SELECT CHANNEL 2 + | 11 SELECT CHANNEL 3 + | + +---------- 0 RESET REQUEST BIT + 1 SET REQUEST BIT + + Write DMAC Mode Register + + The Write DMAC Mode Register command is used to define the + operating mode characteristics for each DMA channel. Each + channel has an internal 6-bit mode register; the high-order + six bits of the associated data byte are written into the + mode register addressed by the two low-order bits. + + + 7 6 5 4 3 2 1 0 + +---+---+---+---+---+---+---+---+ + | | + +---+---+---+---+---+---+---+---+ + | | | | | | | | + | | | | | | +---+-- 00 SELECT CHANNEL 0 + | | | | | | 01 SELECT CHANNEL 1 + | | | | | | 10 SELECT CHANNEL 2 + | | | | | | 11 SELECT CHANNEL 3 + | | | | | | + | | | | +---+---------- 00 VERIFY TRANSFER + | | | | 01 WRITE TRANSFER + | | | | 10 READ TRANSFER + | | | | + | | | +------------------ 0 AUTOINITIALIZE DISABLE + | | | 1 AUTOINITIALIZE ENABLE + | | | + | | +---------------------- 0 ADDRESS INCREMENT + | | 1 ADDRESS DECREMENT + | | + +---+-------------------------- 00 DEMAND MODE + 01 SINGLE MODE + 10 BLOCK MODE + + Clear DMAC First/Last Flip-Flop + + The Clear DMAC First/Last Flip-Flop command initializes + the DMAC's internal first/last flip-flop so that the + next byte written to or re~d from the 16-bit address + or word-count registers is the low-order byte. The + flip-flop is toggled with each register access so that + a second register read or write command accesses the + high-order byte. + + DMAC Master Clear + + The DMAC Master Clear command clears the DMAC's command, status, + request, and temporary registers to zero, initializes the + first/last flip-flop, and sets the four channel mask bits in + the mask register to disable all DMA requests (i.e., the DMAC + is placed in an idle state). + + Write DMAC Mask Register + + The Write DMAC Mask Register command allows all four bits of the + DMAC's mask register to be written with a single command. + + 7 6 5 4 3 2 1 0 + +---+---+---+---+---+---+---+---+ + | X X X X X | + +---+---+---+---+---+---+---+---+ + | | | + | | +-- 0 CLEAR CHANNEL 0 MASK BIT + | | 1 SET CHANNEL 0 MASK BIT + | | + | +---------- 0 CLEAR CHANNEL 2 MASK BIT + | 1 SET CHANNEL 2 MASK BIT + | + +-------------- 0 CLEAR CHANNEL 3 MASK BIT + 1 SET CHANNEL 3 MASK BIT + + Read FDC Status Register + + The Read FDC Status Register command accesses the FDC's main + status register. The individual status register bits are as + follows: + + 7 6 5 4 3 2 1 0 + +---+---+---+---+---+---+---+---+ + | | + +---+---+---+---+---+---+---+---+ + | | | | | | | | + | | | | | | | +-- FDD 0 BUSY + | | | | | | +------ FDD 1 BUSY + | | | | | +---------- FDD 2 BUSY + | | | | +-------------- FDD 3 BUSY + | | | +------------------ FDC BUSY + | | +---------------------- NON-DMA MODE + | +-------------------------- DATA INPUT/OUTPUT + +------------------------------ REQUEST FOR MASTER + + Read/Write FDC Data Register + + The Read and Write FDC Data Register commands are used to write + command and parameter bytes to the FDC in order to specify the + operation to be performed (referred to as the "command phase") + and to read status bytes from the FDC following the operation + (referred to as the "result phase"). During the command and + result phases, the 8-bit data register is actually a series of + 8-bit registers in a stack. Each register is accessed in + sequence; the number of registers accessed and the individual + register contents are defined by the specific disk command. + + Write Controller Auxiliary Port + + The Write Controller Auxiliary Port command is used to set or + clear individual bits within the controller's auxiliary port. + The four low-order port bits are dedicated to auxiliary drive + control functions (jumper links are required to connect the + desired port bit to an available pin on the drive interface + connectors). The most common application for these bits is + the "Motor-On" control function for mini-sized drives. + + 7 6 5 4 3 2 1 0 + +---+---+---+---+---+---+---+---+ + | | + +---+---+---+---+---+---+---+---+ + | | | | | | | | + | | | | +---+---+---+-- DRIVE CONTROL + | | | +------------------ ADDR 20 + | | +---------------------- ADDR 21 + | +-------------------------- ADDR 22 + +------------------------------ ADDR 23 + + Poll Interrupt Status + + The Poll Interrupt Status command presents the interrupt + status of the controller and the two interrupt status + lines dedicated to the iSBX Multimodule board. + 7 6 5 4 3 2 1 0 + +---+---+---+---+---+---+---+---+ + | X X X X X | + +---+---+---+---+---+---+---+---+ + | | | + | | +-- CONTROLLER INTERRUPT + | +------ MULTIMODULE BOARD INTERRUPT 0 + +---------- MULTIMODULE BOARD INTERRUPT 1 + + Controller Reset + + The Controller Reset command is the software reset for the + controller. This command clears the controller's auxiliary + port and segment address register, provides a reset signal + to the iSBX Multimodule board and initializes the bus + controller (releases the bus), the DMAC (clears the internal + registers and masks the DREQ inputs), and the FDC (places + the FDC in an idle state and disables the output control + lines to the diskette drive). + + Write Controller Low- And High-Byte Segment Address Registers + + The Write Controller Low- and High-Byte Address Registers + commands are required when the controller uses 20-bit + addressing (memory address range from 0 to OFFFFFH). These + commands are issued prior to initiating a diskette read or + write operation to specify the 16-bit segment address. + + FDC Commands + + The 8272/D765 is capable of performing 15 different + commands. Each command is initiated by a multibyte transfer + from the processor, and the result after execution of the + command may also be a multibyte transfer back to the processor. + Because of this multibyte interchange of information between + the FDC and the processor, it is convenient to consider each + command as consisting of three phases: + + Command Phase: The FDC receives all information required to + perform a particular operation from the processor. + + Execution Phase: The FDC performs the operation it was + instructed to do. + + Result Phase: After completion of the operation, status + and other housekeeping information are made available + to the processor. + + Not all the FDC commands are supported by this emulation. Only the subset + of commands required to build an operable CP/M BIOS are supported. They are: + + Read - Read specified data from the selected FDD. + + Write - Write specified data to the selected FDD. + + Seek - Move the R/W head to the specified cylinder on the specified FDD. + + Specify - Set the characteristics for all the FDDs. + + Sense Interrupt - Sense change in FDD Ready line or and of Seek/Recalibrate + command. + + Sense Drive - Returns status of all the FDDs. + + Recalibrate - Move the R/W head to cylinder 0 on the specified FDD. + + Format Track - Format the current track on the specified FDD. + + Read ID - Reads the first address mark it finds. + + Simulated Floppy Disk Drives + + The units in this device simulate an 8- or 5 1/4- or 3 1/2 inch drives. The + drives can emulate SSSD, SSDD, and DSDD. Drives can be attached to files up + to 1.44MB in size. Drive configuration is selected when a disk is logged onto + the system. An identity sector or identity byte contains information to + configure the OS drivers for the type of drive to emulate. + + uptr->u3 - + uptr->u4 - + uptr->u5 - + uptr->u6 - unit number (0-FDD_NUM) + + ?? ??? 11 - Original file. + 16 Dec 12 - Modified to use system_80_10.cfg file to set I/O base address. +*/ + +#include "system_defs.h" + +#define UNIT_V_WPMODE (UNIT_V_UF) /* Write protect */ +#define UNIT_WPMODE (1 << UNIT_V_WPMODE) + +/* master status register definitions */ +#define RQM 0x80 /* Request for master */ +#define DIO 0x40 /* Data I/O Direction 0=W, 1=R */ +#define NDM 0x20 /* Non-DMA mode */ +#define CB 0x10 /* FDC busy */ +#define D3B 0x08 /* FDD 3 busy */` +#define D2B 0x04 /* FDD 2 busy */` +#define D1B 0x02 /* FDD 1 busy */` +#define D0B 0x01 /* FDD 0 busy */` + +/* status register 0 definitions */ +#define IC 0xC0 /* Interrupt code */ +#define IC_NORM 0x00 /* normal completion */ +#define IC_ABNORM 0x40 /* abnormal completion */ +#define IC_INVC 0x80 /* invalid command */ +#define IC_RC 0xC0 /* drive not ready */ +#define SE 0x20 /* Seek end */ +#define EC 0x10 /* Equipment check */ +#define NR 0x08 /* Not ready */ +#define HD 0x04 /* Head selected */ +#define US 0x03 /* Unit selected */ +#define US_0 0x00 /* Unit 0 */ +#define US_1 0x01 /* Unit 1 */ +#define US_2 0x02 /* Unit 2 */ +#define US_3 0x03 /* Unit 3 */ + +/* status register 1 definitions */ +#define EN 0x80 /* End of cylinder */ +#define DE 0x20 /* Data error */ +#define OR 0x10 /* Overrun */ +#define ND 0x04 /* No data */ +#define NW 0x02 /* Not writable */ +#define MA 0x01 /* Missing address mark */ + +/* status register 2 definitions */ +#define CM 0x40 /* Control mark */ +#define DD 0x20 /* Data error in data field */ +#define WC 0x10 /* Wrong cylinder */ +#define BC 0x02 /* Bad cylinder */ +#define MD 0x01 /* Missing address mark in data field */ + +/* status register 3/fddst definitions */ +#define FT 0x80 /* Fault */ +#define WP 0x40 /* Write protect */ +#define RDY 0x20 /* Ready */ +#define T0 0x10 /* Track 0 */ +#define TS 0x08 /* Two sided */ +//#define HD 0x04 /* Head selected */ +//#define US 0x03 /* Unit selected */ + +/* FDC command definitions */ +#define READTRK 0x02 +#define SPEC 0x03 +#define SENDRV 0x04 +#define WRITE 0x05 +#define READ 0x06 +#define HOME 0x07 +#define SENINT 0x08 +#define WRITEDEL 0x09 +#define READID 0x0A +#define READDEL 0x0C +#define FMTTRK 0x0D +#define SEEK 0x0F +#define SCANEQ 0x11 +#define SCANLOEQ 0x19 +#define SCANHIEQ 0x1D + +#define FDD_NUM 4 + +/* internal function prototypes */ + +t_stat isbc208_svc (UNIT *uptr); +t_stat isbc208_reset (DEVICE *dptr); +void isbc208_reset1 (void); +t_stat isbc208_attach (UNIT *uptr, char *cptr); +t_stat isbc208_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); +int32 isbc208_r0(int32 io, int32 data); +int32 isbc208_r1(int32 io, int32 data); +int32 isbc208_r2(int32 io, int32 data); +int32 isbc208_r3(int32 io, int32 data); +int32 isbc208_r4(int32 io, int32 data); +int32 isbc208_r5(int32 io, int32 data); +int32 isbc208_r6(int32 io, int32 data); +int32 isbc208_r7(int32 io, int32 data); +int32 isbc208_r8(int32 io, int32 data); +int32 isbc208_r9(int32 io, int32 data); +int32 isbc208_rA(int32 io, int32 data); +int32 isbc208_rB(int32 io, int32 data); +int32 isbc208_rC(int32 io, int32 data); +int32 isbc208_rD(int32 io, int32 data); +int32 isbc208_rE(int32 io, int32 data); +int32 isbc208_rF(int32 io, int32 data); +int32 isbc208_r10(int32 io, int32 data); +int32 isbc208_r11(int32 io, int32 data); +int32 isbc208_r12(int32 io, int32 data); +int32 isbc208_r13(int32 io, int32 data); +int32 isbc208_r14(int32 io, int32 data); +int32 isbc208_r15(int32 io, int32 data); + +/* external function prototypes */ + +extern void set_irq(int32 int_num); +extern void clr_irq(int32 int_num); +extern int32 reg_dev(int32 (*routine)(), int32 port); +extern void multibus_put_mbyte(int32 addr, int32 val); +extern int32 multibus_get_mbyte(int32 addr); + +/* 8237 physical register definitions */ +uint16 i8237_r0; // 8237 ch 0 address register +uint16 i8237_r1; // 8237 ch 0 count register +uint16 i8237_r2; // 8237 ch 1 address register +uint16 i8237_r3; // 8237 ch 1 count register +uint16 i8237_r4; // 8237 ch 2 address register +uint16 i8237_r5; // 8237 ch 2 count register +uint16 i8237_r6; // 8237 ch 3 address register +uint16 i8237_r7; // 8237 ch 3 count register +uint8 i8237_r8; // 8237 status register +uint8 i8237_r9; // 8237 command register +uint8 i8237_rA; // 8237 mode register +uint8 i8237_rB; // 8237 mask register +uint8 i8237_rC; // 8237 request register +uint8 i8237_rD; // 8237 first/last ff + +/* 8272 physical register definitions */ +/* 8272 command register stack*/ +uint8 i8272_w0; // MT+MFM+SK+command +uint8 i8272_w1; // HDS [HDS=H << 2] + DS1 + DS0 +uint8 i8272_w2; // cylinder # (0-XX) +uint8 i8272_w3; // head # (0 or 1) +uint8 i8272_w4; // sector # (1-XX) +uint8 i8272_w5; // number of bytes (128 << N) +uint8 i8272_w6; // End of track (last sector # on cylinder) +uint8 i8272_w7; // Gap length +uint8 i8272_w8; // Data length (when N=0, size to read or write) + +/* 8272 status register stack */ +uint8 i8272_msr; // main status +uint8 i8272_r0; // ST 0 +uint8 i8272_r1; // ST 1 +uint8 i8272_r2; // ST 2 +uint8 i8272_r3; // ST 3 + +/* iSBC-208 physical register definitions */ +uint16 isbc208_sr; // isbc-208 segment register +uint8 isbc208_i; // iSBC-208 interrupt register +uint8 isbc208_a; // iSBC-208 auxillary port register + +/* data obtained from analyzing command registers/attached file length */ +int32 wsp = 0, rsp = 0; // indexes to write and read stacks (8272 data) +int32 cyl; // current cylinder +int32 hed; // current head [ h << 2] +int32 h; // current head +int32 sec; // current sector +int32 drv; // current drive +uint8 cmd, pcmd; // current command +int32 secn; // N 0-128, 1-256, etc +int32 spt; // sectors per track +int32 ssize; // sector size (128 << N) + +uint8 *isbc208_buf[FDD_NUM] = { /* FDD buffer pointers */ + NULL, + NULL, + NULL, + NULL +}; + +int32 fddst[FDD_NUM] = { // in ST3 format + 0, // status of FDD 0 + 0, // status of FDD 1 + 0, // status of FDD 2 + 0 // status of FDD 3 +}; + +int8 maxcyl[FDD_NUM] = { + 0, // last cylinder + 1 of FDD 0 + 0, // last cylinder + 1 of FDD 1 + 0, // last cylinder + 1 of FDD 2 + 0 // last cylinder + 1 of FDD 3 +}; + +/* isbc208 Standard SIMH Device Data Structures - 4 units */ +UNIT isbc208_unit[] = { + { UDATA (&isbc208_svc, UNIT_ATTABLE+UNIT_DISABLE, 0), 20 }, + { UDATA (&isbc208_svc, UNIT_ATTABLE+UNIT_DISABLE, 0), 20 }, + { UDATA (&isbc208_svc, UNIT_ATTABLE+UNIT_DISABLE, 0), 20 }, + { UDATA (&isbc208_svc, UNIT_ATTABLE+UNIT_DISABLE, 0), 20 } +}; + +REG isbc208_reg[] = { + { HRDATA (CH0ADR, i8237_r0, 16) }, + { HRDATA (CH0CNT, i8237_r1, 16) }, + { HRDATA (CH1ADR, i8237_r2, 16) }, + { HRDATA (CH1CNT, i8237_r3, 16) }, + { HRDATA (CH2ADR, i8237_r4, 16) }, + { HRDATA (CH2CNT, i8237_r5, 16) }, + { HRDATA (CH3ADR, i8237_r6, 16) }, + { HRDATA (CH3CNT, i8237_r7, 16) }, + { HRDATA (STAT37, i8237_r8, 8) }, + { HRDATA (CMD37, i8237_r9, 8) }, + { HRDATA (MODE, i8237_rA, 8) }, + { HRDATA (MASK, i8237_rB, 8) }, + { HRDATA (REQ, i8237_rC, 8) }, + { HRDATA (FF, i8237_rD, 8) }, + { HRDATA (STAT72, i8272_msr, 8) }, + { HRDATA (STAT720, i8272_r0, 8) }, + { HRDATA (STAT721, i8272_r1, 8) }, + { HRDATA (STAT722, i8272_r2, 8) }, + { HRDATA (STAT723, i8272_r3, 8) }, + { HRDATA (CMD720, i8272_w0, 8) }, + { HRDATA (CMD721, i8272_w1, 8) }, + { HRDATA (CMD722, i8272_w2, 8) }, + { HRDATA (CMD723, i8272_w3, 8) }, + { HRDATA (CMD724, i8272_w4, 8) }, + { HRDATA (CMD725, i8272_w5, 8) }, + { HRDATA (CMD726, i8272_w6, 8) }, + { HRDATA (CMD727, i8272_w7, 8) }, + { HRDATA (CMD728, i8272_w8, 8) }, + { HRDATA (FDD0, fddst[0], 8) }, + { HRDATA (FDD1, fddst[1], 8) }, + { HRDATA (FDD2, fddst[2], 8) }, + { HRDATA (FDD3, fddst[3], 8) }, + { HRDATA (SEGREG, isbc208_sr, 8) }, + { HRDATA (AUX, isbc208_a, 8) }, + { HRDATA (INT, isbc208_i, 8) }, + { NULL } +}; + +MTAB isbc208_mod[] = { + { UNIT_WPMODE, 0, "RW", "RW", &isbc208_set_mode }, + { UNIT_WPMODE, UNIT_WPMODE, "WP", "WP", &isbc208_set_mode }, + { 0 } +}; + +DEBTAB isbc208_debug[] = { + { "ALL", DEBUG_all }, + { "FLOW", DEBUG_flow }, + { "READ", DEBUG_read }, + { "WRITE", DEBUG_write }, + { "LEV1", DEBUG_level1 }, + { "LEV2", DEBUG_level2 }, + { "REG", DEBUG_reg }, + { NULL } +}; + +DEVICE isbc208_dev = { + "SBC208", //name + isbc208_unit, //units + isbc208_reg, //registers + isbc208_mod, //modifiers + FDD_NUM, //numunits + 16, //aradix + 32, //awidth + 1, //aincr + 16, //dradix + 8, //dwidth + NULL, //examine + NULL, //deposit + &isbc208_reset, //deposit + NULL, //boot + &isbc208_attach, //attach + NULL, //detach + NULL, //ctxt + DEV_DEBUG+DEV_DISABLE+DEV_DIS, //flags + 0, //dctrl +// DEBUG_flow + DEBUG_read + DEBUG_write, //dctrl + isbc208_debug, //debflags + NULL, //msize + NULL //lname +}; + +/* Service routines to handle simulator functions */ + +/* service routine - actually does the simulated disk I/O */ + +t_stat isbc208_svc (UNIT *uptr) +{ + int32 i, imgadr, data; + int c; + int32 bpt, bpc; + FILE *fp; + + if ((i8272_msr & CB) && cmd && (uptr->u6 == drv)) { /* execution phase */ + if (isbc208_dev.dctrl & DEBUG_flow) + printf("isbc208_svc: Entered execution phase\n"); + switch (cmd) { + case READ: /* 0x06 */ +// printf("READ-e: fddst=%02X", fddst[uptr->u6]); + h = i8272_w3; // h = 0 or 1 + hed = i8272_w3 << 2; // hed = 0 or 4 [h << 2] + sec = i8272_w4; // sector number (1-XX) + secn = i8272_w5; // N (0-5) + spt = i8272_w6; // sectors/track + ssize = 128 << secn; // size of sector (bytes) + bpt = ssize * spt; // bytes/track + bpc = bpt * 2; // bytes/cylinder +// printf(" d=%d h=%d c=%d s=%d\n", drv, h, cyl, sec); + if (isbc208_dev.dctrl & DEBUG_flow) { + printf("isbc208_svc: FDC read: h=%d, hed=%d, sec=%d, secn=%d, spt=%d, ssize=%04X, bpt=%04X, bpc=%04X\n", + h, hed, sec, secn, spt, ssize, bpt, bpc); + printf("isbc208_svc: FDC read: d=%d h=%d c=%d s=%d N=%d spt=%d fddst=%02X\n", + drv, h, cyl, sec, secn, spt, fddst[uptr->u6]); + printf("\nFDC read of d=%d h=%d c=%d s=%d", + drv, h, cyl, sec); + } + if ((fddst[uptr->u6] & RDY) == 0) { // drive not ready + i8272_r0 = IC_ABNORM + NR + hed + drv; /* command done - Not ready error*/ + i8272_r3 = fddst[uptr->u6]; + i8272_msr |= (RQM + DIO + CB); /* enter result phase */ + if (isbc208_dev.dctrl & DEBUG_flow) + printf("isbc208_svc: FDC read: Not Ready\n"); + } else { // get image addr for this d, h, c, s + imgadr = (cyl * bpc) + (h * bpt) + ((sec - 1) * ssize); + if (isbc208_dev.dctrl & DEBUG_flow) + printf("isbc208_svc: FDC read: DMA addr=%04X cnt=%04X imgadr=%04X\n", + i8237_r0, i8237_r1, imgadr); + for (i=0; i<=i8237_r1; i++) { /* copy selected sector to memory */ + data = *(isbc208_buf[uptr->u6] + (imgadr + i)); + multibus_put_mbyte(i8237_r0 + i, data); + } +//*** need to step return results IAW table 3-11 in 143078-001 + i8272_w4 = ++sec; /* next sector */ + i8272_r0 = hed + drv; /* command done - no error */ + i8272_r3 = fddst[uptr->u6]; + } + i8272_r1 = 0; + i8272_r2 = 0; + i8272_w2 = cyl; /* generate a current address mark */ + i8272_w3 = h; + if (i8272_w4 > i8272_w6) { // beyond last sector of track? + i8272_w4 = 1; // yes, set to sector 1; + if (h) { // on head one? + i8272_w2++; // yes, step cylinder + h = 0; // back to head 0 + } + } + i8272_w5 = secn; + i8272_msr |= (RQM + DIO + CB); /* enter result phase */ + rsp = wsp = 0; /* reset indexes */ + set_irq(SBC208_INT); /* set interrupt */ +// printf("READ-x: fddst=%02X\n", fddst[uptr->u6]); + break; + case WRITE: /* 0x05 */ +// printf("WRITE-e: fddst=%02X\n", fddst[uptr->u6]); + h = i8272_w3; // h = 0 or 1 + hed = i8272_w3 << 2; // hed = 0 or 4 [h << 2] + sec = i8272_w4; // sector number (1-XX) + secn = i8272_w5; // N (0-5) + spt = i8272_w6; // sectors/track + ssize = 128 << secn; // size of sector (bytes) + bpt = ssize * spt; // bytes/track + bpc = bpt * 2; // bytes/cylinder + if (isbc208_dev.dctrl & DEBUG_flow) + printf("isbc208_svc: FDC write: hed=%d, sec=%d, secn=%d, spt=%d, ssize=%04X, bpt=%04X, bpc=%04X\n", + hed, sec, secn, spt, ssize, bpt, bpc); + if (isbc208_dev.dctrl & DEBUG_flow) + printf("isbc208_svc: FDC write: d=%d h=%d c=%d s=%d N=%d spt=%d fddst=%02X\n", + drv, h, cyl, sec, secn, spt, fddst[uptr->u6]); + i8272_r1 = 0; // clear ST1 + i8272_r2 = 0; // clear ST2 + if ((fddst[uptr->u6] & RDY) == 0) { + i8272_r0 = IC_ABNORM + NR + hed + drv; /* Not ready error*/ + i8272_r3 = fddst[uptr->u6]; + i8272_msr |= (RQM + DIO + CB); /* enter result phase */ + if (isbc208_dev.dctrl & DEBUG_flow) + printf("isbc208_svc: FDC write: Not Ready\n"); +// } else if (fddst[uptr->u6] & WP) { +// i8272_r0 = IC_ABNORM + hed + drv; /* write protect error*/ +// i8272_r1 = NW; // set not writable in ST1 +// i8272_r3 = fddst[uptr->u6] + WP; +// i8272_msr |= (RQM + DIO + CB); /* enter result phase */ +// printf("\nWrite Protected fddst[%d]=%02X\n", uptr->u6, fddst[uptr->u6]); +// if (isbc208_dev.dctrl & DEBUG_flow) +// printf("isbc208_svc: FDC write: Write Protected\n"); + } else { // get image addr for this d, h, c, s + imgadr = (cyl * bpc) + (h * bpt) + ((sec - 1) * ssize); + if (isbc208_dev.dctrl & DEBUG_flow) + printf("isbc208_svc: FDC write: DMA adr=%04X cnt=%04X imgadr=%04X\n", + i8237_r0, i8237_r1, imgadr); + for (i=0; i<=i8237_r1; i++) { /* copy selected memory to image */ + data = multibus_get_mbyte(i8237_r0 + i); + *(isbc208_buf[uptr->u6] + (imgadr + i)) = data; + } + //*** quick fix. Needs more thought! + fp = fopen(uptr->filename, "wb"); // write out modified image + for (i=0; icapac; i++) { + c = *(isbc208_buf[uptr->u6] + i) & 0xFF; + fputc(c, fp); + } + fclose(fp); +//*** need to step return results IAW table 3-11 in 143078-001 + i8272_w2 = cyl; /* generate a current address mark */ + i8272_w3 = hed >> 2; + i8272_w4 = ++sec; /* next sector */ + i8272_w5 = secn; + i8272_r0 = hed + drv; /* command done - no error */ + i8272_r3 = fddst[uptr->u6]; + i8272_msr |= (RQM + DIO + CB); /* enter result phase */ + } + rsp = wsp = 0; /* reset indexes */ + set_irq(SBC208_INT); /* set interrupt */ +// printf("WRITE-x: fddst=%02X\n", fddst[uptr->u6]); + break; + case FMTTRK: /* 0x0D */ + if ((fddst[uptr->u6] & RDY) == 0) { + i8272_r0 = IC_ABNORM + NR + hed + drv; /* Not ready error*/ + i8272_msr |= (RQM + DIO + CB); /* enter result phase */ + if (isbc208_dev.dctrl & DEBUG_flow) + printf("isbc208_svc: Not Ready\n"); + } else if (fddst[uptr->u6] & WP) { + i8272_r0 = IC_ABNORM + hed + drv; /* write protect error*/ + i8272_r3 = fddst[uptr->u6] + WP; + i8272_msr |= (RQM + DIO + CB); /* enter result phase */ + if (isbc208_dev.dctrl & DEBUG_flow) + printf("isbc208_svc: Write Protected\n"); + } else { + ; /* do nothing for now */ + i8272_msr |= (RQM + DIO + CB); /* enter result phase */ + } + rsp = wsp = 0; /* reset indexes */ + set_irq(SBC208_INT); /* set interrupt */ + break; + case SENINT: /* 0x08 */ + i8272_msr |= (RQM + DIO + CB); /* enter result phase */ + i8272_r0 = hed + drv; /* command done - no error */ + i8272_r1 = 0; + i8272_r2 = 0; + rsp = wsp = 0; /* reset indexes */ + clr_irq(SBC208_INT); /* clear interrupt */ + break; + case SENDRV: /* 0x04 */ + if (isbc208_dev.dctrl & DEBUG_flow) + printf("isbc208_svc: FDC sense drive of disk=%d fddst=%02X\n", drv, fddst[uptr->u6]); + i8272_msr |= (RQM + DIO + CB); /* enter result phase */ + i8272_r0 = hed + drv; /* command done - no error */ + i8272_r1 = 0; + i8272_r2 = 0; + i8272_r3 = fddst[drv]; /* drv status */ + rsp = wsp = 0; /* reset indexes */ + break; + case HOME: /* 0x07 */ +// printf("HOME-e: fddst=%02X\n", fddst[uptr->u6]); + if (isbc208_dev.dctrl & DEBUG_flow) + printf("isbc208_svc: FDC home: disk=%d fddst=%02X\n", drv, fddst[uptr->u6]); + if ((fddst[uptr->u6] & RDY) == 0) { + i8272_r0 = IC_ABNORM + NR + hed + drv; /* Not ready error*/ + i8272_r3 = fddst[uptr->u6]; + if (isbc208_dev.dctrl & DEBUG_flow) + printf("isbc208_svc: Not Ready\n"); + } else { + cyl = 0; /* now on cylinder 0 */ + fddst[drv] |= T0; /* set status flag */ + i8272_r0 = SE + hed + drv; /* seek end - no error */ + } + i8272_r1 = 0; + i8272_r2 = 0; + i8272_msr &= ~(RQM + DIO + CB + hed + drv); /* execution phase done*/ + i8272_msr |= RQM; /* enter COMMAND phase */ + rsp = wsp = 0; /* reset indexes */ + set_irq(SBC208_INT); /* set interrupt */ +// printf("HOME-x: fddst=%02X\n", fddst[uptr->u6]); + break; + case SPEC: /* 0x03 */ + fddst[0] |= TS; //*** bad, bad, bad! + fddst[1] |= TS; + fddst[2] |= TS; + fddst[3] |= TS; +// printf("SPEC-e: fddst[%d]=%02X\n", uptr->u6, fddst[uptr->u6]); + if (isbc208_dev.dctrl & DEBUG_flow) + printf("isbc208_svc: FDC specify SRT=%d ms HUT=%d ms HLT=%d ms \n", + 16 - (drv >> 4), 16 * (drv & 0x0f), i8272_w2 & 0xfe); + i8272_r0 = hed + drv; /* command done - no error */ + i8272_r1 = 0; + i8272_r2 = 0; + i8272_msr &= ~(RQM + DIO + CB); /* execution phase done*/ + i8272_msr = 0; // force 0 for now, where does 0x07 come from? + i8272_msr |= RQM; /* enter command phase */ + rsp = wsp = 0; /* reset indexes */ +// printf("SPEC-x: fddst[%d]=%02X\n", uptr->u6, fddst[uptr->u6]); + break; + case READID: /* 0x0A */ + if ((fddst[uptr->u6] & RDY) == 0) { + i8272_r0 = IC_RC + NR + hed + drv; /* Not ready error*/ + i8272_r3 = fddst[uptr->u6]; + if (isbc208_dev.dctrl & DEBUG_flow) + printf("isbc208_svc: Not Ready\n"); + } else { + i8272_w2 = cyl; /* generate a valid address mark */ + i8272_w3 = hed >> 2; + i8272_w4 = 1; /* always sector 1 */ + i8272_w5 = secn; + i8272_r0 = hed + drv; /* command done - no error */ + i8272_msr &= ~(RQM + DIO + CB); /* execution phase done*/ + i8272_msr |= RQM; /* enter command phase */ + } + i8272_r1 = 0; + i8272_r2 = 0; + rsp = wsp = 0; /* reset indexes */ + break; + case SEEK: /* 0x0F */ +// printf("SEEK-e: fddst=%02X\n", fddst[uptr->u6]); + if (isbc208_dev.dctrl & DEBUG_flow) + printf("isbc208_svc: FDC seek: disk=%d cyl=%d fddst=%02X\n", drv, i8272_w2, fddst[uptr->u6]); + if ((fddst[uptr->u6] & RDY) == 0) { /* Not ready? */ + i8272_r0 = IC_ABNORM + NR + hed + drv; /* error*/ + i8272_r3 = fddst[uptr->u6]; + if (isbc208_dev.dctrl & DEBUG_flow) + printf("isbc208_svc: FDC seek: Not Ready\n"); + } else if (i8272_w2 >= maxcyl[uptr->u6]) { + i8272_r0 = IC_ABNORM + RDY + hed + drv; /* seek error*/ + if (isbc208_dev.dctrl & DEBUG_flow) + printf("isbc208_svc: FDC seek: Invalid Cylinder %d\n", i8272_w2); + } else { + i8272_r0 |= SE + hed + drv; /* command done - no error */ + cyl = i8272_w2; /* new cylinder number */ + if (cyl == 0) { /* if cyl 0, set flag */ + fddst[drv] |= T0; /* set T0 status flag */ + i8272_r3 |= T0; + } else { + fddst[drv] &= ~T0; /* clear T0 status flag */ + i8272_r3 &= ~T0; + } + } + i8272_r1 = 0; + i8272_r2 = 0; + i8272_msr &= ~(RQM + DIO + CB + hed + drv); /* execution phase done*/ + i8272_msr |= RQM; /* enter command phase */ + rsp = wsp = 0; /* reset indexes */ +// set_irq(SBC208_INT); /* set interrupt */ +// printf("SEEK-x: fddst=%02X\n", fddst[uptr->u6]); + break; + default: + i8272_msr &= ~(RQM + DIO + CB); /* execution phase done*/ + i8272_msr |= RQM; /* enter command phase */ + i8272_r0 = IC_INVC + hed + drv; /* set bad command error */ + i8272_r1 = 0; + i8272_r2 = 0; + rsp = wsp = 0; /* reset indexes */ + break; + } + pcmd = cmd; /* save for result phase */ + cmd = 0; /* reset command */ + if (isbc208_dev.dctrl & DEBUG_flow) + printf("isbc208_svc: Exit: RSLT0 msr=%02X ST0=%02X ST1=%02X ST2=%02X ST3=%02X\n", + i8272_msr, i8272_r0, i8272_r1, i8272_r2, i8272_r3); + } + sim_activate (&isbc208_unit[uptr->u6], isbc208_unit[uptr->u6].wait); + return SCPE_OK; +} + +// read/write FDC data register +int32 isbc208_r11(int32 io, int32 data) +{ + if (io == 0) { /* read FDC data register */ + wsp = 0; /* clear write stack index */ + switch (rsp) { /* read from next stack register */ + case 0: + if (isbc208_dev.dctrl & DEBUG_reg) { + printf("i8272_r1 read as %02X\n", i8272_r1); + printf("i8272_r3 read as %02X\n", i8272_r3); + } + rsp++; /* step read stack index */ + clr_irq(SBC208_INT); /* clear interrupt */ + if (pcmd == SENDRV) { + i8272_msr = RQM; /* result phase SENDRV done */ + return i8272_r1; // SENDRV return ST1 + } + return i8272_r0; /* ST0 */ + case 1: + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8272_r2 read as %02X\n", i8272_r2); + rsp++; /* step read stack index */ + if (pcmd == SENINT) { + i8272_msr = RQM; /* result phase SENINT done */ + return cyl; // SENINT return current cylinder + } + return i8272_r1; /* ST1 */ + case 2: + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8272_r3 read as %02X\n", i8272_r3); + rsp++; /* step read stack index */ + return i8272_r2; /* ST2 */ + case 3: + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8272_w2 read as %02X\n", i8272_w2); + rsp++; /* step read stack index */ + return i8272_w2; /* C - cylinder */ + case 4: + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8272_w3 read as %02X\n", i8272_w3); + rsp++; /* step read stack index */ + return i8272_w3; /* H - head */ + case 5: + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8272_w4 read as %02X\n", i8272_w4); + rsp++; /* step read stack index */ + return i8272_w4; /* R - sector */ + case 6: + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8272_w5 read as %02X\n", i8272_w5); + i8272_msr = RQM; /* result phase ALL OTHERS done */ + return i8272_w5; /* N - sector size*/ + } + } else { /* write FDC data register */ + rsp = 0; /* clear read stack index */ + switch (wsp) { /* write to next stack register */ + case 0: + i8272_w0 = data; /* rws = MT + MFM + SK + cmd */ + cmd = data & 0x1F; /* save the current command */ + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8272_w0 set to %02X\n", data); + if (cmd == SENINT) { + i8272_msr = CB; /* command phase SENINT done */ + return 0; + } + wsp++; /* step write stack index */ + break; + case 1: + i8272_w1 = data; /* rws = hed + drv */ + if (cmd != SPEC) + drv = data & 0x03; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8272_w1 set to %02X\n", data); + if (cmd == HOME || cmd == SENDRV || cmd == READID) { + i8272_msr = CB + hed + drv; /* command phase HOME, READID and SENDRV done */ + return 0; + } + wsp++; /* step write stack index */ + break; + case 2: + i8272_w2 = data; /* rws = C */ + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8272_w2 set to %02X\n", data); + if (cmd == SPEC || cmd == SEEK) { + i8272_msr = CB + hed + drv; /* command phase SPECIFY and SEEK done */ + return 0; + } + wsp++; /* step write stack index */ + break; + case 3: + i8272_w3 = data; /* rw = H */ + hed = data; + wsp++; /* step write stack index */ + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8272_w3 set to %02X\n", data); + break; + case 4: + i8272_w4 = data; /* rw = R */ + sec = data; + wsp++; /* step write stack index */ + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8272_w4 set to %02X\n", data); + break; + case 5: + i8272_w5 = data; /* rw = N */ + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8272_w5 set to %02X\n", data); + if (cmd == FMTTRK) { + i8272_msr = CB + hed + drv; /* command phase FMTTRK done */ + return 0; + } + wsp++; /* step write stack index */ + break; + case 6: + i8272_w6 = data; /* rw = last sector number */ + wsp++; /* step write stack index */ + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8272_w6 set to %02X\n", data); + break; + case 7: + i8272_w7 = data; /* rw = gap length */ + wsp++; /* step write stack index */ + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8272_w7 set to %02X\n", data); + break; + case 8: + i8272_w8 = data; /* rw = bytes to transfer */ + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8272_w8 set to %02X\n", data); + if (cmd == READ || cmd == WRITE) + i8272_msr = CB + hed + drv; /* command phase all others done */ + break; + } + return 0; + } +} + +/* Reset routine */ + +t_stat isbc208_reset (DEVICE *dptr) +{ + reg_dev(isbc208_r0, SBC208_BASE + 0); + reg_dev(isbc208_r1, SBC208_BASE + 1); + reg_dev(isbc208_r2, SBC208_BASE + 2); + reg_dev(isbc208_r3, SBC208_BASE + 3); + reg_dev(isbc208_r4, SBC208_BASE + 4); + reg_dev(isbc208_r5, SBC208_BASE + 5); + reg_dev(isbc208_r6, SBC208_BASE + 6); + reg_dev(isbc208_r7, SBC208_BASE + 7); + reg_dev(isbc208_r8, SBC208_BASE + 8); + reg_dev(isbc208_r9, SBC208_BASE + 9); + reg_dev(isbc208_rA, SBC208_BASE + 10); + reg_dev(isbc208_rB, SBC208_BASE + 11); + reg_dev(isbc208_rC, SBC208_BASE + 12); + reg_dev(isbc208_rD, SBC208_BASE + 13); + reg_dev(isbc208_rE, SBC208_BASE + 14); + reg_dev(isbc208_rF, SBC208_BASE + 15); + reg_dev(isbc208_r10, SBC208_BASE + 16); + reg_dev(isbc208_r11, SBC208_BASE + 17); + reg_dev(isbc208_r12, SBC208_BASE + 18); + reg_dev(isbc208_r13, SBC208_BASE + 19); + reg_dev(isbc208_r14, SBC208_BASE + 20); + reg_dev(isbc208_r15, SBC208_BASE + 21); + if ((isbc208_dev.flags & DEV_DIS) == 0) + isbc208_reset1(); + return SCPE_OK; +} + +void isbc208_reset1 (void) +{ + int32 i; + UNIT *uptr; + static int flag = 1; + + if (flag) printf("iSBC 208: Initializing\n"); + for (i = 0; i < FDD_NUM; i++) { /* handle all units */ + uptr = isbc208_dev.units + i; + if (uptr->capac == 0) { /* if not configured */ +// printf(" SBC208%d: Not configured\n", i); +// if (flag) { +// printf(" ALL: \"set isbc208 en\"\n"); +// printf(" EPROM: \"att isbc2080 \"\n"); +// flag = 0; +// } + uptr->capac = 0; /* initialize unit */ + uptr->u3 = 0; + uptr->u4 = 0; + uptr->u5 = 0; + uptr->u6 = i; /* unit number - only set here! */ + fddst[i] = WP + T0 + i; /* initial drive status */ + uptr->flags |= UNIT_WPMODE; /* set WP in unit flags */ + sim_activate (&isbc208_unit[uptr->u6], isbc208_unit[uptr->u6].wait); + } else { + fddst[i] = RDY + WP + T0 + i; /* initial attach drive status */ +// printf(" SBC208%d: Configured, Attached to %s\n", i, uptr->filename); + } + } + i8237_r8 = 0; /* status */ + i8237_r9 = 0; /* command */ + i8237_rB = 0x0F; /* mask */ + i8237_rC = 0; /* request */ + i8237_rD = 0; /* first/last FF */ + i8272_msr = RQM; /* 8272 ready for start of command */ + rsp = wsp = 0; /* reset indexes */ + cmd = 0; /* clear command */ + if (flag) { + printf(" 8237 Reset\n"); + printf(" 8272 Reset\n"); + } + flag = 0; +} + +/* isbc208 attach - attach an .IMG file to a FDD */ + +t_stat isbc208_attach (UNIT *uptr, char *cptr) +{ + t_stat r; + FILE *fp; + int32 i, c = 0; + long flen; + + if (isbc208_dev.dctrl & DEBUG_flow) + printf(" isbc208_attach: Entered with cptr=%s\n", cptr); + if ((r = attach_unit (uptr, cptr)) != SCPE_OK) { + printf(" isbc208_attach: Attach error\n"); + return r; + } + fp = fopen(uptr->filename, "rb"); + if (fp == NULL) { + printf(" Unable to open disk img file %s\n", uptr->filename); + printf(" No disk image loaded!!!\n"); + } else { + printf("iSBC 208: Attach\n"); + fseek(fp, 0, SEEK_END); /* size disk image */ + flen = ftell(fp); + fseek(fp, 0, SEEK_SET); + if (isbc208_buf[uptr->u6] == NULL) { /* no buffer allocated */ + isbc208_buf[uptr->u6] = malloc(flen); + if (isbc208_buf[uptr->u6] == NULL) { + printf(" iSBC208_attach: Malloc error\n"); + return SCPE_MEM; + } + } + uptr->capac = flen; + i = 0; + c = fgetc(fp); // copy disk image into buffer + while (c != EOF) { + *(isbc208_buf[uptr->u6] + i++) = c & 0xFF; + c = fgetc(fp); + } + fclose(fp); + fddst[uptr->u6] |= RDY; /* set unit ready */ + if (flen == 368640) { /* 5" 360K DSDD */ + maxcyl[uptr->u6] = 40; + fddst[uptr->u6] |= TS; // two sided + } + else if (flen == 737280) { /* 5" 720K DSQD / 3.5" 720K DSDD */ + maxcyl[uptr->u6] = 80; + fddst[uptr->u6] |= TS; // two sided + } + else if (flen == 1228800) { /* 5" 1.2M DSHD */ + maxcyl[uptr->u6] = 80; + fddst[uptr->u6] |= TS; // two sided + } + else if (flen == 1474560) { /* 3.5" 1.44M DSHD */ + maxcyl[uptr->u6] = 80; + fddst[uptr->u6] |= TS; // two sided + } + printf(" Drive-%d: %d bytes of disk image %s loaded, fddst=%02X\n", + uptr->u6, i, uptr->filename, fddst[uptr->u6]); + } + if (isbc208_dev.dctrl & DEBUG_flow) + printf(" iSBC208_attach: Done\n"); + return SCPE_OK; +} + +/* isbc208 set mode = 8- or 16-bit data bus */ + +t_stat isbc208_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + UNIT *uptr1; + + if (isbc208_dev.dctrl & DEBUG_flow) + printf(" isbc208_set_mode: Entered with val=%08XH uptr->flags=%08X\n", + val, uptr->flags); + if (val & UNIT_WPMODE) { /* write protect */ + fddst[uptr->u6] |= WP; + uptr->flags |= val; + } else { /* read write */ + fddst[uptr->u6] &= ~WP; + uptr->flags &= ~val; + } +// printf("fddst[%d]=%02XH uptr->flags=%08X\n", uptr->u6, fddst[uptr->u6], uptr->flags); + if (isbc208_dev.dctrl & DEBUG_flow) + printf(" isbc208_set_mode: Done\n"); + return SCPE_OK; +} + +/* I/O instruction handlers, called from the CPU module when an + IN or OUT instruction is issued. + + Each function is passed an 'io' flag, where 0 means a read from + the port, and 1 means a write to the port. On input, the actual + input is passed as the return value, on output, 'data' is written + to the device. +*/ + +int32 isbc208_r0(int32 io, int32 data) +{ + if (io == 0) { /* read current address CH 0 */ + if (i8237_rD) { /* high byte */ + i8237_rD = 0; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r0(H) read as %04X\n", i8237_r0); + return (i8237_r0 >> 8); + } else { /* low byte */ + i8237_rD++; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r0(L) read as %04X\n", i8237_r0); + return (i8237_r0 & 0xFF); + } + } else { /* write base & current address CH 0 */ + if (i8237_rD) { /* high byte */ + i8237_rD = 0; + i8237_r0 |= (data << 8); + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r0(H) set to %04X\n", i8237_r0); + } else { /* low byte */ + i8237_rD++; + i8237_r0 = data & 0xFF; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r0(L) set to %04X\n", i8237_r0); + } + return 0; + } +} + +int32 isbc208_r1(int32 io, int32 data) +{ + if (io == 0) { /* read current word count CH 0 */ + if (i8237_rD) { /* high byte */ + i8237_rD = 0; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r1(H) read as %04X\n", i8237_r1); + return (i8237_r1 >> 8); + } else { /* low byte */ + i8237_rD++; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r1(L) read as %04X\n", i8237_r1); + return (i8237_r1 & 0xFF); + } + } else { /* write base & current address CH 0 */ + if (i8237_rD) { /* high byte */ + i8237_rD = 0; + i8237_r1 |= (data << 8); + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r1(H) set to %04X\n", i8237_r1); + } else { /* low byte */ + i8237_rD++; + i8237_r1 = data & 0xFF; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r1(L) set to %04X\n", i8237_r1); + } + return 0; + } +} + +int32 isbc208_r2(int32 io, int32 data) +{ + if (io == 0) { /* read current address CH 1 */ + if (i8237_rD) { /* high byte */ + i8237_rD = 0; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r2(H) read as %04X\n", i8237_r2); + return (i8237_r2 >> 8); + } else { /* low byte */ + i8237_rD++; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r2(L) read as %04X\n", i8237_r2); + return (i8237_r2 & 0xFF); + } + } else { /* write base & current address CH 1 */ + if (i8237_rD) { /* high byte */ + i8237_rD = 0; + i8237_r2 |= (data << 8); + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r2(H) set to %04X\n", i8237_r2); + } else { /* low byte */ + i8237_rD++; + i8237_r2 = data & 0xFF; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r2(L) set to %04X\n", i8237_r2); + } + return 0; + } +} + +int32 isbc208_r3(int32 io, int32 data) +{ + if (io == 0) { /* read current word count CH 1 */ + if (i8237_rD) { /* high byte */ + i8237_rD = 0; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r3(H) read as %04X\n", i8237_r3); + return (i8237_r3 >> 8); + } else { /* low byte */ + i8237_rD++; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r3(L) read as %04X\n", i8237_r3); + return (i8237_r3 & 0xFF); + } + } else { /* write base & current address CH 1 */ + if (i8237_rD) { /* high byte */ + i8237_rD = 0; + i8237_r3 |= (data << 8); + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r3(H) set to %04X\n", i8237_r3); + } else { /* low byte */ + i8237_rD++; + i8237_r3 = data & 0xFF; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r3(L) set to %04X\n", i8237_r3); + } + return 0; + } +} + +int32 isbc208_r4(int32 io, int32 data) +{ + if (io == 0) { /* read current address CH 2 */ + if (i8237_rD) { /* high byte */ + i8237_rD = 0; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r4(H) read as %04X\n", i8237_r4); + return (i8237_r4 >> 8); + } else { /* low byte */ + i8237_rD++; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r4(L) read as %04X\n", i8237_r4); + return (i8237_r4 & 0xFF); + } + } else { /* write base & current address CH 2 */ + if (i8237_rD) { /* high byte */ + i8237_rD = 0; + i8237_r4 |= (data << 8); + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r4(H) set to %04X\n", i8237_r4); + } else { /* low byte */ + i8237_rD++; + i8237_r4 = data & 0xFF; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r4(L) set to %04X\n", i8237_r4); + } + return 0; + } +} + +int32 isbc208_r5(int32 io, int32 data) +{ + if (io == 0) { /* read current word count CH 2 */ + if (i8237_rD) { /* high byte */ + i8237_rD = 0; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r5(H) read as %04X\n", i8237_r5); + return (i8237_r5 >> 8); + } else { /* low byte */ + i8237_rD++; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r5(L) read as %04X\n", i8237_r5); + return (i8237_r5 & 0xFF); + } + } else { /* write base & current address CH 2 */ + if (i8237_rD) { /* high byte */ + i8237_rD = 0; + i8237_r5 |= (data << 8); + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r5(H) set to %04X\n", i8237_r5); + } else { /* low byte */ + i8237_rD++; + i8237_r5 = data & 0xFF; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r5(L) set to %04X\n", i8237_r5); + } + return 0; + } +} + +int32 isbc208_r6(int32 io, int32 data) +{ + if (io == 0) { /* read current address CH 3 */ + if (i8237_rD) { /* high byte */ + i8237_rD = 0; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r6(H) read as %04X\n", i8237_r6); + return (i8237_r6 >> 8); + } else { /* low byte */ + i8237_rD++; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r6(L) read as %04X\n", i8237_r6); + return (i8237_r6 & 0xFF); + } + } else { /* write base & current address CH 3 */ + if (i8237_rD) { /* high byte */ + i8237_rD = 0; + i8237_r6 |= (data << 8); + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r6(H) set to %04X\n", i8237_r6); + } else { /* low byte */ + i8237_rD++; + i8237_r6 = data & 0xFF; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r6(L) set to %04X\n", i8237_r6); + } + return 0; + } +} + +int32 isbc208_r7(int32 io, int32 data) +{ + if (io == 0) { /* read current word count CH 3 */ + if (i8237_rD) { /* high byte */ + i8237_rD = 0; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r7(H) read as %04X\n", i8237_r7); + return (i8237_r7 >> 8); + } else { /* low byte */ + i8237_rD++; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r7(L) read as %04X\n", i8237_r7); + return (i8237_r7 & 0xFF); + } + } else { /* write base & current address CH 3 */ + if (i8237_rD) { /* high byte */ + i8237_rD = 0; + i8237_r7 |= (data << 8); + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r7(H) set to %04X\n", i8237_r7); + } else { /* low byte */ + i8237_rD++; + i8237_r7 = data & 0xFF; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r7(L) set to %04X\n", i8237_r7); + } + return 0; + } +} + +int32 isbc208_r8(int32 io, int32 data) +{ + if (io == 0) { /* read status register */ + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r8 (status) read as %02X\n", i8237_r8); + return (i8237_r8); + } else { /* write command register */ + i8237_r9 = data & 0xFF; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_r9 (command) set to %02X\n", i8237_r9); + return 0; + } +} + +int32 isbc208_r9(int32 io, int32 data) +{ + if (io == 0) { + if (isbc208_dev.dctrl & DEBUG_reg) + printf("Illegal read of isbc208_r9\n"); + return 0; + } else { /* write request register */ + i8237_rC = data & 0xFF; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_rC (request) set to %02X\n", i8237_rC); + return 0; + } +} + +int32 isbc208_rA(int32 io, int32 data) +{ + if (io == 0) { + if (isbc208_dev.dctrl & DEBUG_reg) + printf("Illegal read of isbc208_rA\n"); + return 0; + } else { /* write single mask register */ + switch(data & 0x03) { + case 0: + if (data & 0x04) + i8237_rB |= 1; + else + i8237_rB &= ~1; + break; + case 1: + if (data & 0x04) + i8237_rB |= 2; + else + i8237_rB &= ~2; + break; + case 2: + if (data & 0x04) + i8237_rB |= 4; + else + i8237_rB &= ~4; + break; + case 3: + if (data & 0x04) + i8237_rB |= 8; + else + i8237_rB &= ~8; + break; + } + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_rB (mask) set to %02X\n", i8237_rB); + return 0; + } +} + +int32 isbc208_rB(int32 io, int32 data) +{ + if (io == 0) { + if (isbc208_dev.dctrl & DEBUG_reg) + printf("Illegal read of isbc208_rB\n"); + return 0; + } else { /* write mode register */ + i8237_rA = data & 0xFF; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_rA (mode) set to %02X\n", i8237_rA); + return 0; + } +} + +int32 isbc208_rC(int32 io, int32 data) +{ + if (io == 0) { + if (isbc208_dev.dctrl & DEBUG_reg) + printf("Illegal read of isbc208_rC\n"); + return 0; + } else { /* clear byte pointer FF */ + i8237_rD = 0; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_rD (FF) cleared\n"); + return 0; + } +} + +int32 isbc208_rD(int32 io, int32 data) +{ + if (io == 0) { /* read temporary register */ + if (isbc208_dev.dctrl & DEBUG_reg) + printf("Illegal read of isbc208_rD\n"); + return 0; + } else { /* master clear */ + isbc208_reset1(); + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237 master clear\n"); + return 0; + } +} + +int32 isbc208_rE(int32 io, int32 data) +{ + if (io == 0) { + if (isbc208_dev.dctrl & DEBUG_reg) + printf("Illegal read of isbc208_rE\n"); + return 0; + } else { /* clear mask register */ + i8237_rB = 0; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_rB (mask) cleared\n"); + return 0; + } +} + +int32 isbc208_rF(int32 io, int32 data) +{ + if (io == 0) { + if (isbc208_dev.dctrl & DEBUG_reg) + printf("Illegal read of isbc208_rF\n"); + return 0; + } else { /* write all mask register bits */ + i8237_rB = data & 0x0F; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8237_rB (mask) set to %02X\n", i8237_rB); + return 0; + } +} + +int32 isbc208_r10(int32 io, int32 data) +{ + if (io == 0) { /* read FDC status register */ + if (isbc208_dev.dctrl & DEBUG_reg) + printf("i8272_msr read as %02X\n", i8272_msr); + return i8272_msr; + } else { + if (isbc208_dev.dctrl & DEBUG_reg) + printf("Illegal write to isbc208_r10\n"); + return 0; + } +} + +int32 isbc208_r12(int32 io, int32 data) +{ + if (io == 0) { /* read interrupt status */ + if (isbc208_dev.dctrl & DEBUG_reg) + printf("isbc208_r12 read as %02X\n", isbc208_i); + return (isbc208_i); + } else { /* write controller auxillary port */ + isbc208_a = data & 0xFF; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("isbc208_r12 set to %02X\n", isbc208_a); + return 0; + } +} + +int32 isbc208_r13(int32 io, int32 data) +{ + if (io == 0) { + if (isbc208_dev.dctrl & DEBUG_reg) + printf("Illegal read of isbc208_r13\n"); + return 0; + } else { /* reset controller */ + isbc208_reset1(); + if (isbc208_dev.dctrl & DEBUG_reg) + printf("isbc208_r13 controller reset\n"); + return 0; + } +} + +int32 isbc208_r14(int32 io, int32 data) +{ + if (io == 0) { + if (isbc208_dev.dctrl & DEBUG_reg) + printf("Illegal read of isbc208_r14\n"); + return 0; + } else { /* Low-Byte Segment Address Register */ + isbc208_sr = data & 0xFF; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("isbc208_sr(L) set to %02X\n", data & 0xFF); + return 0; + } +} + +int32 isbc208_r15(int32 io, int32 data) +{ + if (io == 0) { + if (isbc208_dev.dctrl & DEBUG_reg) + printf("Illegal read of isbc208_r15\n"); + return 0; + } else { /* High-Byte Segment Address Register */ + isbc208_sr |= data << 8; + if (isbc208_dev.dctrl & DEBUG_reg) + printf("isbc208_sr(H) set to %02X\n", data); + return 0; + } +} + +/* end of isbc208.c */ diff --git a/MDS-800/common/multibus.c b/MDS-800/common/multibus.c new file mode 100644 index 00000000..6821103a --- /dev/null +++ b/MDS-800/common/multibus.c @@ -0,0 +1,288 @@ +/* multibus.c: Multibus I simulator + + Copyright (c) 2010, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + This software was written by Bill Beech, Dec 2010, to allow emulation of Multibus + Computer Systems. + + ?? ??? 10 - Original file. + 16 Dec 12 - Modified to use isbc_80_10.cfg file to set base and size. +*/ + +#include "system_defs.h" + +#define SET_XACK(VAL) (xack = VAL) + +int32 mbirq = 0; /* set no multibus interrupts */ + +/* function prototypes */ + +t_stat multibus_svc(UNIT *uptr); +t_stat multibus_reset(DEVICE *dptr); +void set_irq(int32 int_num); +void clr_irq(int32 int_num); +int32 nulldev(int32 io, int32 data); +int32 reg_dev(int32 (*routine)(), int32 port); +t_stat multibus_reset (DEVICE *dptr); +int32 multibus_get_mbyte(int32 addr); +int32 multibus_get_mword(int32 addr); +void multibus_put_mbyte(int32 addr, int32 val); +void multibus_put_mword(int32 addr, int32 val); + +/* external function prototypes */ + +extern t_stat SBC_reset(DEVICE *dptr); /* reset the iSBC80/10 emulator */ +extern int32 isbc064_get_mbyte(int32 addr); +extern void isbc064_put_mbyte(int32 addr, int32 val); +extern void set_cpuint(int32 int_num); +extern t_stat SBC_reset (DEVICE *dptr); +extern t_stat isbc064_reset (DEVICE *dptr); +extern t_stat isbc208_reset (DEVICE *dptr); + +/* external globals */ + +extern uint8 xack; /* XACK signal */ +extern int32 int_req; /* i8080 INT signal */ + +/* multibus Standard SIMH Device Data Structures */ + +UNIT multibus_unit = { + UDATA (&multibus_svc, 0, 0), 20 +}; + +REG multibus_reg[] = { + { HRDATA (MBIRQ, mbirq, 32) }, + { HRDATA (XACK, xack, 8) } +}; + +DEBTAB multibus_debug[] = { + { "ALL", DEBUG_all }, + { "FLOW", DEBUG_flow }, + { "READ", DEBUG_read }, + { "WRITE", DEBUG_write }, + { "LEV1", DEBUG_level1 }, + { "LEV2", DEBUG_level2 }, + { NULL } +}; + +DEVICE multibus_dev = { + "MBIRQ", //name + &multibus_unit, //units + multibus_reg, //registers + NULL, //modifiers + 1, //numunits + 16, //aradix + 32, //awidth + 1, //aincr + 16, //dradix + 8, //dwidth + NULL, //examine + NULL, //deposit + &multibus_reset, //reset + NULL, //boot + NULL, //attach + NULL, //detach + NULL, //ctxt + DEV_DEBUG, //flags + 0, //dctrl + multibus_debug, //debflags + NULL, //msize + NULL //lname +}; + +/* Service routines to handle simulator functions */ + +/* service routine - actually does the simulated interrupts */ + +t_stat multibus_svc(UNIT *uptr) +{ + switch (mbirq) { + case INT_1: + set_cpuint(INT_R); + clr_irq(SBC208_INT); /***** bad, bad, bad! */ +// printf("multibus_svc: mbirq=%04X int_req=%04X\n", mbirq, int_req); + break; + default: +// printf("multibus_svc: default mbirq=%04X\n", mbirq); + break; + } + sim_activate (&multibus_unit, multibus_unit.wait); /* continue poll */ +} + +/* Reset routine */ + +t_stat multibus_reset(DEVICE *dptr) +{ + SBC_reset(NULL); + isbc064_reset(NULL); + isbc208_reset(NULL); + printf(" Multibus: Reset\n"); + sim_activate (&multibus_unit, multibus_unit.wait); /* activate unit */ + return SCPE_OK; +} + +void set_irq(int32 int_num) +{ + mbirq |= int_num; +// printf("set_irq: int_num=%04X mbirq=%04X\n", int_num, mbirq); +} + +void clr_irq(int32 int_num) +{ + mbirq &= ~int_num; +// printf("clr_irq: int_num=%04X mbirq=%04X\n", int_num, mbirq); +} + +/* This is the I/O configuration table. There are 256 possible +device addresses, if a device is plugged to a port it's routine +address is here, 'nulldev' means no device is available +*/ +struct idev { + int32 (*routine)(); +}; + +struct idev dev_table[256] = { +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 000H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 004H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 008H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 00CH */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 010H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 014H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 018H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 01CH */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 020H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 024H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 028H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 02CH */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 030H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 034H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 038H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 03CH */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 040H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 044H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 048H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 04CH */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 050H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 054H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 058H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 05CH */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 060H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 064H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 068H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 06CH */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 070H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 074H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 078H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 07CH */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 080H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 084H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 088H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 08CH */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 090H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 094H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 098H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 09CH */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0A0H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0A4H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0A8H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0A0H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0B0H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0B4H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0B8H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0B0H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0C0H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0C4H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0C8H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0CCH */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0D0H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0D4H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0D8H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0DCH */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0E0H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0E4H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0E8H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0ECH */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0F0H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0F4H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0F8H */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev} /* 0FCH */ +}; + +int32 nulldev(int32 flag, int32 data) +{ + SET_XACK(0); /* set no XACK */ + if (flag == 0) /* if we got here, no valid I/O device */ + return (0xFF); + return 0; +} + +int32 reg_dev(int32 (*routine)(), int32 port) +{ + if (dev_table[port].routine != &nulldev) { /* port already assigned */ +// printf("Multibus: I/O Port %02X is already assigned\n", port); + } else { +// printf("Port %02X is assigned\n", port); + dev_table[port].routine = routine; + } +} + +/* get a byte from memory */ + +int32 multibus_get_mbyte(int32 addr) +{ + SET_XACK(0); /* set no XACK */ +// printf("multibus_get_mbyte: Cleared XACK for %04X\n", addr); + return isbc064_get_mbyte(addr); +} + +/* get a word from memory */ + +int32 multibus_get_mword(int32 addr) +{ + int32 val; + + val = multibus_get_mbyte(addr); + val |= (multibus_get_mbyte(addr+1) << 8); + return val; +} + +/* put a byte to memory */ + +void multibus_put_mbyte(int32 addr, int32 val) +{ + SET_XACK(0); /* set no XACK */ +// printf("multibus_put_mbyte: Cleared XACK for %04X\n", addr); + isbc064_put_mbyte(addr, val); +// printf("multibus_put_mbyte: Done XACK=%dX\n", XACK); +} + +/* put a word to memory */ + +void multibus_put_mword(int32 addr, int32 val) +{ + multibus_put_mbyte(addr, val); + multibus_put_mbyte(addr+1, val << 8); +} + + diff --git a/MDS-800/common/pata.c b/MDS-800/common/pata.c new file mode 100644 index 00000000..5648bf5c --- /dev/null +++ b/MDS-800/common/pata.c @@ -0,0 +1,193 @@ +/* i8255.c: Intel i8255 PIO adapter + + Copyright (c) 2010, William A. Beech + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + WILLIAM A. BEECH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of William A. Beech shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from William A. Beech. + + These functions support a simulated i8255 interface device on an iSBC. + The device has threee physical 8-bit I/O ports which could be connected + to any parallel I/O device. + + All I/O is via programmed I/O. The i8255 has a control port (PIOS) + and three data ports (PIOA, PIOB, and PIOC). + + The simulated device supports a select from I/O space and two address lines. + The data ports are at the lower addresses and the control port is at + the highest. + + A write to the control port can configure the device: + + Control Word + +---+---+---+---+---+---+---+---+ + | D7 D6 D5 D4 D3 D2 D1 D0| + +---+---+---+---+---+---+---+---+ + + Group B + D0 Port C (lower) 1-Input, 0-Output + D1 Port B 1-Input, 0-Output + D2 Mode Selection 0-Mode 0, 1-Mode 1 + + Group A + D3 Port C (upper) 1-Input, 0-Output + D4 Port A 1-Input, 0-Output + D5-6 Mode Selection 00-Mode 0, 01-Mode 1, 1X-Mode 2 + + D7 Mode Set Flag 1=Active, 0=Bit Set + + Mode 0 - Basic Input/Output + Mode 1 - Strobed Input/Output + Mode 2 - Bidirectional Bus + + Bit Set - D7=0, D3:1 select port C bit, D0 1=set, 0=reset + + A read to the data ports gets the current port value, a write + to the data ports writes the character to the device. + + ?? ??? 10 - Original file. + 16 Dec 12 - Modified to use isbc_80_10.cfg file to set base and size. +*/ + +#include "system_defs.h" + +extern int32 reg_dev(int32 (*routine)(), int32 port); + +/* function prototypes */ + +t_stat pata_reset (DEVICE *dptr, int32 base); + +/* i8255 Standard I/O Data Structures */ + +UNIT pata_unit[] = { + { UDATA (0, 0, 0) } +}; + +REG pata_reg[] = { + { HRDATA (CONTROL0, pata_unit[0].u3, 8) }, + { HRDATA (PORTA0, pata_unit[0].u4, 8) }, + { HRDATA (PORTB0, pata_unit[0].u5, 8) }, + { HRDATA (PORTC0, pata_unit[0].u6, 8) }, + { NULL } +}; + +DEVICE pata_dev = { + "PATA", //name + pata_unit, //units + pata_reg, //registers + NULL, //modifiers + 1, //numunits + 16, //aradix + 32, //awidth + 1, //aincr + 16, //dradix + 8, //dwidth + NULL, //examine + NULL, //deposit +// &pata_reset, //reset + NULL, //reset + NULL, //boot + NULL, //attach + NULL, //detach + NULL, //ctxt + 0, //flags + 0, //dctrl + NULL, //debflags + NULL, //msize + NULL //lname +}; + +/* I/O instruction handlers, called from the CPU module when an + IN or OUT instruction is issued. +*/ + +int32 patas(int32 io, int32 data) +{ + int32 bit; + + if (io == 0) { /* read status port */ + return pata_unit[0].u3; + } else { /* write status port */ + if (data & 0x80) { /* mode instruction */ + pata_unit[0].u3 = data; + printf("PATA: 8255 Mode Instruction=%02X\n", data); + if (data & 0x64) + printf(" Mode 1 and 2 not yet implemented\n"); + } else { /* bit set */ + bit = (data & 0x0E) >> 1; /* get bit number */ + if (data & 0x01) { /* set bit */ + pata_unit[0].u6 |= (0x01 << bit); + } else { /* reset bit */ + pata_unit[0].u6 &= ~(0x01 << bit); + } + } + } + return 0; +} + +int32 pataa(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + return (pata_unit[0].u4); + } else { /* write data port */ + pata_unit[0].u4 = data; + printf("PATA: 8255 Port A = %02X\n", data); + } + return 0; +} + +int32 patab(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + return (pata_unit[0].u5); + } else { /* write data port */ + pata_unit[0].u5 = data; + printf("PATA: 8255 Port B = %02X\n", data); + } + return 0; +} + +int32 patac(int32 io, int32 data) +{ + if (io == 0) { /* read data port */ + return (pata_unit[0].u6); + } else { /* write data port */ + pata_unit[0].u6 = data; + printf("PATA: 8255 Port C = %02X\n", data); + } + return 0; +} + +/* Reset routine */ + +t_stat pata_reset (DEVICE *dptr, int32 base) +{ + pata_unit[0].u3 = 0x9B; /* control */ + pata_unit[0].u4 = 0xFF; /* Port A */ + pata_unit[0].u5 = 0xFF; /* Port B */ + pata_unit[0].u6 = 0xFF; /* Port C */ + reg_dev(pataa, base); + reg_dev(patab, base + 1); + reg_dev(patac, base + 2); + reg_dev(patas, base + 3); + printf(" PATA: Reset\n"); + return SCPE_OK; +} + diff --git a/MDS-800/makefile b/MDS-800/makefile new file mode 100644 index 00000000..4e65edcd --- /dev/null +++ b/MDS-800/makefile @@ -0,0 +1,1382 @@ +# +# This GNU make makefile has been tested on: +# Linux (x86 & Sparc & PPC) +# OS X +# Solaris (x86 & Sparc) (gcc and Sun C) +# OpenBSD +# NetBSD +# FreeBSD +# HP-UX +# AIX +# Windows (MinGW & cygwin) +# Linux x86 targeting Android (using agcc script) +# +# Android targeted builds should invoke GNU make with GCC=agcc on +# the command line. +# +# In general, the logic below will detect and build with the available +# features which the host build environment provides. +# +# Dynamic loading of libpcap is the preferred default behavior if pcap.h +# is available at build time. Support to statically linking against libpcap +# is deprecated and may be removed in the future. Static linking against +# libpcap can be enabled if GNU make is invoked with USE_NETWORK=1 on the +# command line. +# +# Some platforms may not have vendor supplied libpcap available. HP-UX is +# one such example. The packages which are available for this platform +# install include files and libraries in user specified directories. In +# order for this makefile to locate where these components may have been +# installed, gmake should be invoked with LPATH=/usr/lib:/usr/local/lib +# defined (adjusted as needed depending on where they may be installed). +# +# The default build will build compiler optimized binaries. +# If debugging is desired, then GNU make can be invoked with +# DEBUG=1 on the command line. +# +# OSX and other environments may have the LLVM (clang) compiler +# installed. If you want to build with the clang compiler, invoke +# make with GCC=clang. +# +# Internal ROM support can be disabled if GNU make is invoked with +# DONT_USE_ROMS=1 on the command line. +# +# Asynchronous I/O support can be disabled if GNU make is invoked with +# NOASYNCH=1 on the command line. +# +# For linting (or other code analyzers) make may be invoked similar to: +# +# make GCC=cppcheck CC_OUTSPEC= LDFLAGS= CFLAGS_G="--enable=all --template=gcc" CC_STD=--std=c99 +# +# CC Command (and platform available options). (Poor man's autoconf) +# +ifeq (old,$(shell gmake --version /dev/null 2>&1 | grep 'GNU Make' | awk '{ if ($$3 < "3.81") {print "old"} }')) + GMAKE_VERSION = $(shell gmake --version /dev/null 2>&1 | grep 'GNU Make' | awk '{ print $$3 }') + $(warning *** Warning *** GNU Make Version $(GMAKE_VERSION) is too old to) + $(warning *** Warning *** fully process this makefile) +endif +# building the pdp11, or any vax simulator could use networking support +BUILD_SINGLE := $(MAKECMDGOALS) $(BLANK_SUFFIX) +ifneq (,$(or $(findstring pdp11,$(MAKECMDGOALS)),$(findstring vax,$(MAKECMDGOALS)),$(findstring all,$(MAKECMDGOALS)))) + NETWORK_USEFUL = true + ifneq (,$(or $(findstring microvax1,$(MAKECMDGOALS)),$(findstring microvax2,$(MAKECMDGOALS)))) + VIDEO_USEFUL = true + endif + ifneq (,$(findstring all,$(MAKECMDGOALS))$(word 2,$(MAKECMDGOALS))) + BUILD_MULTIPLE = s + VIDEO_USEFUL = true + endif +else + ifeq ($(MAKECMDGOALS),) + # default target is all + NETWORK_USEFUL = true + VIDEO_USEFUL = true + BUILD_MULTIPLE = s + BUILD_SINGLE := all $(BUILD_SINGLE) + endif +endif +ifeq ($(WIN32),) #*nix Environments (&& cygwin) + ifeq ($(GCC),) + GCC = gcc + endif + OSTYPE = $(shell uname) + # OSNAME is used in messages to indicate the source of libpcap components + OSNAME = $(OSTYPE) + ifeq (SunOS,$(OSTYPE)) + TEST = /bin/test + else + TEST = test + endif + ifeq (CYGWIN,$(findstring CYGWIN,$(OSTYPE))) # uname returns CYGWIN_NT-n.n-ver + OSTYPE = cygwin + OSNAME = windows-build + endif + ifeq (,$(shell $(GCC) -v /dev/null 2>&1 | grep 'clang')) + GCC_VERSION = $(shell $(GCC) -v /dev/null 2>&1 | grep 'gcc version' | awk '{ print $$3 }') + COMPILER_NAME = GCC Version: $(GCC_VERSION) + ifeq (,$(GCC_VERSION)) + ifeq (SunOS,$(OSTYPE)) + ifneq (,$(shell $(GCC) -V 2>&1 | grep 'Sun C')) + SUNC_VERSION = $(shell $(GCC) -V 2>&1 | grep 'Sun C') + COMPILER_NAME = $(wordlist 2,10,$(SUNC_VERSION)) + endif + endif + ifeq (HP-UX,$(OSTYPE)) + ifneq (,$(shell what `which $(firstword $(GCC)) 2>&1`| grep -i compiler)) + COMPILER_NAME = $(strip $(shell what `which $(firstword $(GCC)) 2>&1` | grep -i compiler)) + endif + endif + endif + else + ifeq (Apple,$(shell $(GCC) -v /dev/null 2>&1 | grep 'Apple' | awk '{ print $$1 }')) + COMPILER_NAME = $(shell $(GCC) -v /dev/null 2>&1 | grep 'Apple' | awk '{ print $$1 " " $$2 " " $$3 " " $$4 }') + CLANG_VERSION = $(word 4,$(COMPILER_NAME)) + else + COMPILER_NAME = $(shell $(GCC) -v /dev/null 2>&1 | grep 'clang version' | awk '{ print $$1 " " $$2 " " $$3 }') + CLANG_VERSION = $(word 3,$(COMPILER_NAME)) + ifeq (,$(findstring .,$(CLANG_VERSION))) + COMPILER_NAME = $(shell $(GCC) -v /dev/null 2>&1 | grep 'clang version' | awk '{ print $$1 " " $$2 " " $$3 " " $$4 }') + CLANG_VERSION = $(word 4,$(COMPILER_NAME)) + endif + endif + endif + ifeq (git-repo,$(shell if $(TEST) -d ./.git; then echo git-repo; fi)) + ifeq (need-hooks,$(shell if $(TEST) ! -e ./.git/hooks/post-checkout; then echo need-hooks; fi)) + $(info *** Installing git hooks in local repository ***) + GIT_HOOKS += $(shell /bin/cp './Visual Studio Projects/git-hooks/post-commit' ./.git/hooks/) + GIT_HOOKS += $(shell /bin/cp './Visual Studio Projects/git-hooks/post-checkout' ./.git/hooks/) + GIT_HOOKS += $(shell /bin/cp './Visual Studio Projects/git-hooks/post-merge' ./.git/hooks/) + GIT_HOOKS += $(shell ./.git/hooks/post-checkout) + ifneq (,$(strip $(GIT_HOOKS))) + $(info *** Warning - Error installing git hooks *** $(GIT_HOOKS)) + endif + endif + endif + LTO_EXCLUDE_VERSIONS = + PCAPLIB = pcap + ifeq (agcc,$(findstring agcc,$(GCC))) # Android target build? + OS_CCDEFS = -D_GNU_SOURCE + ifeq (,$(NOASYNCH)) + OS_CCDEFS += -DSIM_ASYNCH_IO + endif + OS_LDFLAGS = -lm + else # Non-Android Builds + INCPATH:=/usr/include + LIBPATH:=/usr/lib + OS_CCDEFS = -D_GNU_SOURCE + GCC_OPTIMIZERS_CMD = $(GCC) -v --help 2>&1 + GCC_WARNINGS_CMD = $(GCC) -v --help 2>&1 + LD_ELF = $(shell echo | $(GCC) -E -dM - | grep __ELF__) + ifeq (Darwin,$(OSTYPE)) + OSNAME = OSX + LIBEXT = dylib + INCPATH:=$(shell $(GCC) -x c -v -E /dev/null 2>&1 | grep -A 10 '> search starts here' | grep '^ ' | grep -v 'framework directory' | tr -d '\n') + ifeq (incopt,$(shell if $(TEST) -d /opt/local/include; then echo incopt; fi)) + INCPATH += /opt/local/include + OS_CCDEFS += -I/opt/local/include + endif + ifeq (libopt,$(shell if $(TEST) -d /opt/local/lib; then echo libopt; fi)) + LIBPATH += /opt/local/lib + OS_LDFLAGS += -L/opt/local/lib + endif + ifeq (libXt,$(shell if $(TEST) -d /usr/X11/lib; then echo libXt; fi)) + LIBPATH += /usr/X11/lib + OS_LDFLAGS += -L/usr/X11/lib + endif + else + ifeq (Linux,$(OSTYPE)) + LIBPATH := $(sort $(foreach lib,$(shell /sbin/ldconfig -p | grep ' => /' | sed 's/^.* => //'),$(dir $(lib)))) + LIBEXT = so + else + ifeq (SunOS,$(OSTYPE)) + OSNAME = Solaris + LIBPATH := $(shell crle | grep 'Default Library Path' | awk '{ print $$5 }' | sed 's/:/ /g') + LIBEXT = so + OS_LDFLAGS += -lsocket -lnsl + ifeq (incsfw,$(shell if $(TEST) -d /opt/sfw/include; then echo incsfw; fi)) + INCPATH += /opt/sfw/include + OS_CCDEFS += -I/opt/sfw/include + endif + ifeq (libsfw,$(shell if $(TEST) -d /opt/sfw/lib; then echo libsfw; fi)) + LIBPATH += /opt/sfw/lib + OS_LDFLAGS += -L/opt/sfw/lib -R/opt/sfw/lib + endif + OS_CCDEFS += -D_LARGEFILE_SOURCE + else + ifeq (cygwin,$(OSTYPE)) + # use 0readme_ethernet.txt documented Windows pcap build components + INCPATH += ../windows-build/winpcap/WpdPack/include + LIBPATH += ../windows-build/winpcap/WpdPack/lib + PCAPLIB = wpcap + LIBEXT = a + else + ifneq (,$(findstring AIX,$(OSTYPE))) + OS_LDFLAGS += -lm -lrt + ifeq (incopt,$(shell if $(TEST) -d /opt/freeware/include; then echo incopt; fi)) + INCPATH += /opt/freeware/include + OS_CCDEFS += -I/opt/freeware/include + endif + ifeq (libopt,$(shell if $(TEST) -d /opt/freeware/lib; then echo libopt; fi)) + LIBPATH += /opt/freeware/lib + OS_LDFLAGS += -L/opt/freeware/lib + endif + else + ifeq (,$(findstring NetBSD,$(OSTYPE))) + ifneq (no ldconfig,$(findstring no ldconfig,$(shell which ldconfig 2>&1))) + LDSEARCH :=$(shell ldconfig -r | grep 'search directories' | awk '{print $$3}' | sed 's/:/ /g') + endif + ifneq (,$(LDSEARCH)) + LIBPATH := $(LDSEARCH) + else + ifeq (,$(strip $(LPATH))) + $(info *** Warning ***) + $(info *** Warning *** The library search path on your $(OSTYPE) platform can't be) + $(info *** Warning *** determined. This should be resolved before you can expect) + $(info *** Warning *** to have fully working simulators.) + $(info *** Warning ***) + $(info *** Warning *** You can specify your library paths via the LPATH environment) + $(info *** Warning *** variable.) + $(info *** Warning ***) + else + LIBPATH = $(subst :, ,$(LPATH)) + OS_LDFLAGS += $(patsubst %,-L%,$(LIBPATH)) + endif + endif + endif + endif + ifeq (usrpkglib,$(shell if $(TEST) -d /usr/pkg/lib; then echo usrpkglib; fi)) + LIBPATH += /usr/pkg/lib + INCPATH += /usr/pkg/include + OS_LDFLAGS += -L/usr/pkg/lib -R/usr/pkg/lib + endif + ifeq (/usr/local/lib,$(findstring /usr/local/lib,$(LIBPATH))) + INCPATH += /usr/local/include + OS_CCDEFS += -I/usr/local/include + endif + ifneq (,$(findstring NetBSD,$(OSTYPE))$(findstring FreeBSD,$(OSTYPE))$(findstring AIX,$(OSTYPE))) + LIBEXT = so + else + ifeq (HP-UX,$(OSTYPE)) + ifeq (ia64,$(shell uname -m)) + LIBEXT = so + else + LIBEXT = sl + endif + OS_CCDEFS += -D_HPUX_SOURCE -D_LARGEFILE64_SOURCE + OS_LDFLAGS += -Wl,+b: + NO_LTO = 1 + else + LIBEXT = a + endif + endif + endif + endif + endif + endif + # Some gcc versions don't support LTO, so only use LTO when the compiler is known to support it + ifeq (,$(NO_LTO)) + ifneq (,$(GCC_VERSION)) + ifeq (,$(shell $(GCC) -v /dev/null 2>&1 | grep '\-\-enable-lto')) + LTO_EXCLUDE_VERSIONS += $(GCC_VERSION) + endif + endif + endif + endif + $(info lib paths are: $(LIBPATH)) + $(info include paths are: $(INCPATH)) + find_lib = $(strip $(firstword $(foreach dir,$(strip $(LIBPATH)),$(wildcard $(dir)/lib$(1).$(LIBEXT))))) + find_include = $(strip $(firstword $(foreach dir,$(strip $(INCPATH)),$(wildcard $(dir)/$(1).h)))) + ifneq (,$(call find_lib,m)) + OS_LDFLAGS += -lm + $(info using libm: $(call find_lib,m)) + endif + ifneq (,$(call find_lib,rt)) + OS_LDFLAGS += -lrt + $(info using librt: $(call find_lib,rt)) + endif + ifneq (,$(call find_include,pthread)) + ifneq (,$(call find_lib,pthread)) + OS_CCDEFS += -DUSE_READER_THREAD + ifeq (,$(NOASYNCH)) + OS_CCDEFS += -DSIM_ASYNCH_IO + endif + OS_LDFLAGS += -lpthread + $(info using libpthread: $(call find_lib,pthread) $(call find_include,pthread)) + else + LIBEXTSAVE := $(LIBEXT) + LIBEXT = a + ifneq (,$(call find_lib,pthread)) + OS_CCDEFS += -DUSE_READER_THREAD + ifeq (,$(NOASYNCH)) + OS_CCDEFS += -DSIM_ASYNCH_IO + endif + OS_LDFLAGS += -lpthread + $(info using libpthread: $(call find_lib,pthread) $(call find_include,pthread)) + endif + LIBEXT = $(LIBEXTSAVE) + endif + endif + ifneq (,$(call find_include,dlfcn)) + ifneq (,$(call find_lib,dl)) + OS_CCDEFS += -DHAVE_DLOPEN=$(LIBEXT) + OS_LDFLAGS += -ldl + $(info using libdl: $(call find_lib,dl) $(call find_include,dlfcn)) + else + ifneq (,$(findstring BSD,$(OSTYPE))$(findstring AIX,$(OSTYPE))) + OS_CCDEFS += -DHAVE_DLOPEN=so + $(info using libdl: $(call find_include,dlfcn)) + else + ifneq (,$(call find_lib,dld)) + OS_CCDEFS += -DHAVE_DLOPEN=$(LIBEXT) + OS_LDFLAGS += -ldld + $(info using libdld: $(call find_lib,dld) $(call find_include,dlfcn)) + endif + endif + endif + endif + ifneq (,$(call find_include,glob)) + OS_CCDEFS += -DHAVE_GLOB + else + ifneq (,$(call find_include,fnmatch)) + OS_CCDEFS += -DHAVE_FNMATCH + endif + endif + ifneq (,$(VIDEO_USEFUL)) + ifneq (,$(call find_include,SDL2/SDL)) + ifneq (,$(call find_lib,SDL2)) + OS_CCDEFS += -DHAVE_LIBSDL -I$(dir $(call find_include,SDL2/SDL)) + OS_LDFLAGS += -lSDL2 + VIDEO_FEATURES = - video capabilities provided by libSDL2 (Simple Directmedia Layer) + $(info using libSDL2: $(call find_lib,SDL2) $(call find_include,SDL2/SDL)) + ifeq (Darwin,$(OSTYPE)) + OS_LDFLAGS += -lobjc -framework cocoa + endif + endif + else + ifneq (,$(call find_include,SDL/SDL)) + ifneq (,$(call find_lib,SDL)) + OS_CCDEFS += -DHAVE_LIBSDL -I$(dir $(call find_include,SDL/SDL)) + OS_LDFLAGS += -lSDL + VIDEO_FEATURES = - video capabilities provided by libSDL (Simple Directmedia Layer) + $(info using libSDL: $(call find_lib,SDL) $(call find_include,SDL/SDL)) + ifeq (Darwin,$(OSTYPE)) + OS_LDFLAGS += -lobjc -framework cocoa + endif + endif + endif + endif + ifeq (,$(findstring HAVE_LIBSDL,$(OS_CCDEFS))) + $(info *** Warning ***) + $(info *** Warning *** The simulator$(BUILD_MULTIPLE) you are building could provide more) + $(info *** Warning *** functionality if video support were available on your system.) + $(info *** Warning *** Install the development components of libSDL packaged by your) + $(info *** Warning *** operating system distribution and rebuild your simulator to) + $(info *** Warning *** enable this extra functionality.) + $(info *** Warning ***) + endif + endif + ifneq (,$(NETWORK_USEFUL)) + ifneq (,$(call find_include,pcap)) + ifneq (,$(shell grep 'pcap/pcap.h' $(call find_include,pcap) | grep include)) + PCAP_H_PATH = $(dir $(call find_include,pcap))pcap/pcap.h + else + PCAP_H_PATH = $(call find_include,pcap) + endif + ifneq (,$(shell grep pcap_compile $(PCAP_H_PATH) | grep const)) + BPF_CONST_STRING = -DBPF_CONST_STRING + endif + NETWORK_CCDEFS += -DHAVE_PCAP_NETWORK -I$(dir $(call find_include,pcap)) $(BPF_CONST_STRING) + NETWORK_LAN_FEATURES += PCAP + ifneq (,$(call find_lib,$(PCAPLIB))) + ifneq ($(USE_NETWORK),) # Network support specified on the GNU make command line + NETWORK_CCDEFS += -DUSE_NETWORK + $(info *** Warning ***) + $(info *** Warning *** Statically linking against libpcap is provides no measurable) + $(info *** Warning *** benefits over dynamically linking libpcap.) + $(info *** Warning ***) + $(info *** Warning *** Support for linking this way is currently deprecated and may be removed) + $(info *** Warning *** in the future.) + $(info *** Warning ***) + ifeq (cygwin,$(OSTYPE)) + # cygwin has no ldconfig so explicitly specify pcap object library + NETWORK_LDFLAGS = -L$(dir $(call find_lib,$(PCAPLIB))) -Wl,-R,$(dir $(call find_lib,$(PCAPLIB))) -l$(PCAPLIB) + else + NETWORK_LDFLAGS = -l$(PCAPLIB) + endif + $(info using libpcap: $(call find_lib,$(PCAPLIB)) $(call find_include,pcap)) + NETWORK_FEATURES = - static networking support using $(OSNAME) provided libpcap components + else # default build uses dynamic libpcap + NETWORK_CCDEFS += -DUSE_SHARED + $(info using libpcap: $(call find_include,pcap)) + NETWORK_FEATURES = - dynamic networking support using $(OSNAME) provided libpcap components + endif + else + LIBEXTSAVE := $(LIBEXT) + LIBEXT = a + ifneq (,$(call find_lib,$(PCAPLIB))) + NETWORK_CCDEFS += -DUSE_NETWORK + NETWORK_LDFLAGS := -L$(dir $(call find_lib,$(PCAPLIB))) -l$(PCAPLIB) + NETWORK_FEATURES = - static networking support using $(OSNAME) provided libpcap components + $(info using libpcap: $(call find_lib,$(PCAPLIB)) $(call find_include,pcap)) + endif + LIBEXT = $(LIBEXTSAVE) + endif + else + # On non-Linux platforms, we'll still try to provide deprecated support for libpcap in /usr/local + INCPATHSAVE := $(INCPATH) + ifeq (,$(findstring Linux,$(OSTYPE))) + # Look for package built from tcpdump.org sources with default install target (or cygwin winpcap) + INCPATH += /usr/local/include + PCAP_H_FOUND = $(call find_include,pcap) + endif + ifneq (,$(strip $(PCAP_H_FOUND))) + ifneq (,$(shell grep 'pcap/pcap.h' $(call find_include,pcap) | grep include)) + PCAP_H_PATH = $(dir $(call find_include,pcap))pcap/pcap.h + else + PCAP_H_PATH = $(call find_include,pcap) + endif + ifneq (,$(shell grep pcap_compile $(PCAP_H_PATH) | grep const)) + BPF_CONST_STRING = -DBPF_CONST_STRING + endif + LIBEXTSAVE := $(LIBEXT) + # first check if binary - shared objects are available/installed in the linker known search paths + ifneq (,$(call find_lib,$(PCAPLIB))) + NETWORK_CCDEFS = -DUSE_SHARED -I$(dir $(call find_include,pcap)) $(BPF_CONST_STRING) + NETWORK_FEATURES = - dynamic networking support using libpcap components from www.tcpdump.org and locally installed libpcap.$(LIBEXT) + $(info using libpcap: $(call find_include,pcap)) + else + LIBPATH += /usr/local/lib + LIBEXT = a + ifneq (,$(call find_lib,$(PCAPLIB))) + $(info using libpcap: $(call find_lib,$(PCAPLIB)) $(call find_include,pcap)) + ifeq (cygwin,$(OSTYPE)) + NETWORK_CCDEFS = -DUSE_NETWORK -DHAVE_PCAP_NETWORK -I$(dir $(call find_include,pcap)) $(BPF_CONST_STRING) + NETWORK_LDFLAGS = -L$(dir $(call find_lib,$(PCAPLIB))) -Wl,-R,$(dir $(call find_lib,$(PCAPLIB))) -l$(PCAPLIB) + NETWORK_FEATURES = - static networking support using libpcap components located in the cygwin directories + else + NETWORK_CCDEFS := -DUSE_NETWORK -DHAVE_PCAP_NETWORK -isystem -I$(dir $(call find_include,pcap)) $(BPF_CONST_STRING) $(call find_lib,$(PCAPLIB)) + NETWORK_FEATURES = - networking support using libpcap components from www.tcpdump.org + $(info *** Warning ***) + $(info *** Warning *** $(BUILD_SINGLE)Simulator$(BUILD_MULTIPLE) being built with networking support using) + $(info *** Warning *** libpcap components from www.tcpdump.org.) + $(info *** Warning *** Some users have had problems using the www.tcpdump.org libpcap) + $(info *** Warning *** components for simh networking. For best results, with) + $(info *** Warning *** simh networking, it is recommended that you install the) + $(info *** Warning *** libpcap-dev (or libpcap-devel) package from your $(OSNAME) distribution) + $(info *** Warning ***) + $(info *** Warning *** Building with the components manually installed from www.tcpdump.org) + $(info *** Warning *** is officially deprecated. Attempting to do so is unsupported.) + $(info *** Warning ***) + endif + else + $(error using libpcap: $(call find_include,pcap) missing $(PCAPLIB).$(LIBEXT)) + endif + NETWORK_LAN_FEATURES += PCAP + endif + LIBEXT = $(LIBEXTSAVE) + else + INCPATH = $(INCPATHSAVE) + $(info *** Warning ***) + $(info *** Warning *** $(BUILD_SINGLE)Simulator$(BUILD_MULTIPLE) are being built WITHOUT) + $(info *** Warning *** libpcap networking support) + $(info *** Warning ***) + $(info *** Warning *** To build simulator(s) with libpcap networking support you) + $(info *** Warning *** should read 0readme_ethernet.txt and follow the instructions) + $(info *** Warning *** regarding the needed libpcap development components for your) + $(info *** Warning *** $(OSTYPE) platform) + $(info *** Warning ***) + endif + endif + # Consider other network connections + ifneq (,$(call find_lib,vdeplug)) + # libvdeplug requires the use of the OS provided libpcap + ifeq (,$(findstring usr/local,$(NETWORK_CCDEFS))) + ifneq (,$(call find_include,libvdeplug)) + # Provide support for vde networking + NETWORK_CCDEFS += -DHAVE_VDE_NETWORK + NETWORK_LAN_FEATURES += VDE + ifeq (,$(findstring USE_NETWORK,$(NETWORK_CCDEFS))$(findstring USE_SHARED,$(NETWORK_CCDEFS))) + NETWORK_CCDEFS += -DUSE_NETWORK + endif + ifeq (Darwin,$(OSTYPE)) + NETWORK_LDFLAGS += -lvdeplug -L$(dir $(call find_lib,vdeplug)) + else + NETWORK_LDFLAGS += -lvdeplug -Wl,-R,$(dir $(call find_lib,vdeplug)) -L$(dir $(call find_lib,vdeplug)) + endif + $(info using libvdeplug: $(call find_lib,vdeplug) $(call find_include,libvdeplug)) + endif + endif + endif + ifeq (,$(findstring HAVE_VDE_NETWORK,$(NETWORK_CCDEFS))) + # Support is available on Linux for libvdeplug. Advise on its usage + ifneq (,$(findstring Linux,$(OSTYPE))) + ifneq (,$(findstring USE_NETWORK,$(NETWORK_CCDEFS))$(findstring USE_SHARED,$(NETWORK_CCDEFS))) + $(info *** Warning ***) + $(info *** Warning *** $(BUILD_SINGLE)Simulator$(BUILD_MULTIPLE) are being built with) + $(info *** Warning *** minimal libpcap networking support) + $(info *** Warning ***) + endif + $(info *** Warning *** Simulators on your $(OSNAME) platform can also be built with) + $(info *** Warning *** extended LAN Ethernet networking support by using VDE Ethernet.) + $(info *** Warning ***) + $(info *** Warning *** To build simulator(s) with extended networking support you) + $(info *** Warning *** should read 0readme_ethernet.txt and follow the instructions) + $(info *** Warning *** regarding the needed libvdeplug components for your $(OSNAME)) + $(info *** Warning *** platform) + $(info *** Warning ***) + endif + endif + ifneq (,$(call find_include,linux/if_tun)) + # Provide support for Tap networking on Linux + NETWORK_CCDEFS += -DHAVE_TAP_NETWORK + NETWORK_LAN_FEATURES += TAP + ifeq (,$(findstring USE_NETWORK,$(NETWORK_CCDEFS))$(findstring USE_SHARED,$(NETWORK_CCDEFS))) + NETWORK_CCDEFS += -DUSE_NETWORK + endif + endif + ifeq (bsdtuntap,$(shell if $(TEST) -e /usr/include/net/if_tun.h -o -e /Library/Extensions/tap.kext; then echo bsdtuntap; fi)) + # Provide support for Tap networking on BSD platforms (including OS X) + NETWORK_CCDEFS += -DHAVE_TAP_NETWORK -DHAVE_BSDTUNTAP + NETWORK_LAN_FEATURES += TAP + ifeq (,$(findstring USE_NETWORK,$(NETWORK_CCDEFS))$(findstring USE_SHARED,$(NETWORK_CCDEFS))) + NETWORK_CCDEFS += -DUSE_NETWORK + endif + endif + ifeq (,$(findstring USE_NETWORK,$(NETWORK_CCDEFS))$(findstring USE_SHARED,$(NETWORK_CCDEFS))$(findstring HAVE_VDE_NETWORK,$(NETWORK_CCDEFS))) + NETWORK_CCDEFS += -DUSE_NETWORK + NETWORK_FEATURES = - WITHOUT Local LAN networking support + $(info *** Warning ***) + $(info *** Warning *** $(BUILD_SINGLE)Simulator$(BUILD_MULTIPLE) are being built WITHOUT LAN networking support) + $(info *** Warning ***) + $(info *** Warning *** To build simulator(s) with networking support you should read) + $(info *** Warning *** 0readme_ethernet.txt and follow the instructions regarding the) + $(info *** Warning *** needed libpcap components for your $(OSTYPE) platform) + $(info *** Warning ***) + endif + NETWORK_OPT = $(NETWORK_CCDEFS) + endif + ifneq (binexists,$(shell if $(TEST) -e BIN; then echo binexists; fi)) + MKDIRBIN = mkdir -p BIN + endif + ifeq (commit-id-exists,$(shell if $(TEST) -e .git-commit-id; then echo commit-id-exists; fi)) + GIT_COMMIT_ID=$(shell cat .git-commit-id) + else + ifeq (,$(shell grep 'define SIM_GIT_COMMIT_ID' sim_rev.h | grep 'Format:')) + GIT_COMMIT_ID=$(shell grep 'define SIM_GIT_COMMIT_ID' sim_rev.h | awk '{ print $$3 }') + endif + endif +else + #Win32 Environments (via MinGW32) + GCC = gcc + ifeq (XP,$(findstring XP,$(shell ver))) + GCC_Path := C:\MinGW\bin\ + else + GCC_Path := $(dir $(shell where gcc.exe)) + endif + ifeq (rename-build-support,$(shell if exist ..\windows-build-windows-build echo rename-build-support)) + FIXED_BUILD := $(shell move ..\windows-build-windows-build ..\windows-build >NUL) + endif + GCC_VERSION = $(word 3,$(shell $(GCC) --version)) + COMPILER_NAME = GCC Version: $(GCC_VERSION) + LTO_EXCLUDE_VERSIONS = 4.5.2 + ifeq (pthreads,$(shell if exist ..\windows-build\pthreads\Pre-built.2\include\pthread.h echo pthreads)) + PTHREADS_CCDEFS = -DUSE_READER_THREAD -DPTW32_STATIC_LIB -I../windows-build/pthreads/Pre-built.2/include + ifeq (,$(NOASYNCH)) + PTHREADS_CCDEFS += -DSIM_ASYNCH_IO + endif + PTHREADS_LDFLAGS = -lpthreadGC2 -L..\windows-build\pthreads\Pre-built.2\lib + else + ifeq (pthreads,$(shell if exist $(dir $(GCC_Path))..\include\pthread.h echo pthreads)) + PTHREADS_CCDEFS = -DUSE_READER_THREAD + ifeq (,$(NOASYNCH)) + PTHREADS_CCDEFS += -DSIM_ASYNCH_IO + endif + PTHREADS_LDFLAGS = -lpthread + endif + endif + ifeq (pcap,$(shell if exist ..\windows-build\winpcap\Wpdpack\include\pcap.h echo pcap)) + NETWORK_LDFLAGS = + NETWORK_OPT = -DUSE_SHARED -I../windows-build/winpcap/Wpdpack/include -I$(GCC_Path)..\include\ddk + NETWORK_FEATURES = - dynamic networking support using windows-build provided libpcap components + else + ifeq (pcap,$(shell if exist $(dir $(GCC_Path))..\include\pcap.h echo pcap)) + NETWORK_LDFLAGS = + NETWORK_OPT = -DUSE_SHARED -I$(GCC_Path)..\include\ddk + NETWORK_FEATURES = - dynamic networking support using libpcap components found in the MinGW directories + endif + endif + ifneq (,$(VIDEO_USEFUL)) + ifeq (libSDL,$(shell if exist ..\windows-build\libSDL\SDL2-2.0.0\include\SDL.h echo libSDL)) + OS_CCDEFS += -DHAVE_LIBSDL -I..\windows-build\libSDL\SDL2-2.0.0\include + OS_LDFLAGS += -lSDL2 -L..\windows-build\libSDL\SDL2-2.0.0\lib + VIDEO_FEATURES = - video capabilities provided by libSDL2 (Simple Directmedia Layer) + else + $(info ***********************************************************************) + $(info ***********************************************************************) + $(info ** This build could produce simulators with video capabilities. **) + $(info ** However, the required files to achieve this can't be found on **) + $(info ** this system. Download the file: **) + $(info ** https://github.com/simh/windows-build/archive/windows-build.zip **) + $(info ** Refer to the file: **) + $(info ** "Visual Studio Projects\0ReadMe_Projects.txt" for where to place **) + $(info ** the 'windows-build' folder extracted from that zip file. **) + $(info ***********************************************************************) + $(info ***********************************************************************) + $(info .) + endif + endif + OS_CCDEFS += -fms-extensions $(PTHREADS_CCDEFS) + OS_LDFLAGS += -lm -lwsock32 -lwinmm $(PTHREADS_LDFLAGS) + EXE = .exe + ifneq (binexists,$(shell if exist BIN echo binexists)) + MKDIRBIN = if not exist BIN mkdir BIN + endif + ifneq ($(USE_NETWORK),) + NETWORK_OPT += -DUSE_SHARED + endif + ifneq (,$(shell if exist .git-commit-id type .git-commit-id)) + GIT_COMMIT_ID=$(shell if exist .git-commit-id type .git-commit-id) + else + ifeq (,$(shell findstr /C:"define SIM_GIT_COMMIT_ID" sim_rev.h | findstr Format)) + GIT_COMMIT_ID=$(shell for /F "tokens=3" %%i in ("$(shell findstr /C:"define SIM_GIT_COMMIT_ID" sim_rev.h)") do echo %%i) + endif + endif +endif +ifneq (,$(GIT_COMMIT_ID)) + CFLAGS_GIT = -DSIM_GIT_COMMIT_ID=$(GIT_COMMIT_ID) +endif +ifneq ($(DEBUG),) + CFLAGS_G = -g -ggdb -g3 + CFLAGS_O = -O0 + BUILD_FEATURES = - debugging support +else + ifneq (clang,$(findstring clang,$(COMPILER_NAME))) + CFLAGS_O = -O2 + else + ifeq (Darwin,$(OSTYPE)) + CFLAGS_O += -O4 -fno-strict-overflow -flto -fwhole-program + else + CFLAGS_O := -O2 -fno-strict-overflow + endif + endif + LDFLAGS_O = + GCC_MAJOR_VERSION = $(firstword $(subst ., ,$(GCC_VERSION))) + ifneq (3,$(GCC_MAJOR_VERSION)) + ifeq (,$(GCC_OPTIMIZERS_CMD)) + GCC_OPTIMIZERS_CMD = $(GCC) --help=optimizers + endif + GCC_OPTIMIZERS = $(shell $(GCC_OPTIMIZERS_CMD)) + endif + ifneq (,$(findstring $(GCC_VERSION),$(LTO_EXCLUDE_VERSIONS))) + NO_LTO = 1 + endif + ifneq (,$(findstring -finline-functions,$(GCC_OPTIMIZERS))) + CFLAGS_O += -finline-functions + endif + ifneq (,$(findstring -fgcse-after-reload,$(GCC_OPTIMIZERS))) + CFLAGS_O += -fgcse-after-reload + endif + ifneq (,$(findstring -fpredictive-commoning,$(GCC_OPTIMIZERS))) + CFLAGS_O += -fpredictive-commoning + endif + ifneq (,$(findstring -fipa-cp-clone,$(GCC_OPTIMIZERS))) + CFLAGS_O += -fipa-cp-clone + endif + ifneq (,$(findstring -funsafe-loop-optimizations,$(GCC_OPTIMIZERS))) + CFLAGS_O += -fno-unsafe-loop-optimizations + endif + ifneq (,$(findstring -fstrict-overflow,$(GCC_OPTIMIZERS))) + CFLAGS_O += -fno-strict-overflow + endif + ifeq (,$(NO_LTO)) + ifneq (,$(findstring -flto,$(GCC_OPTIMIZERS))) + CFLAGS_O += -flto -fwhole-program + LDFLAGS_O += -flto -fwhole-program + endif + endif + BUILD_FEATURES = - compiler optimizations and no debugging support +endif +ifneq (3,$(GCC_MAJOR_VERSION)) + ifeq (,$(GCC_WARNINGS_CMD)) + GCC_WARNINGS_CMD = $(GCC) --help=warnings + endif + ifneq (,$(findstring -Wunused-result,$(shell $(GCC_WARNINGS_CMD)))) + CFLAGS_O += -Wno-unused-result + endif +endif +ifneq (clean,$(MAKECMDGOALS)) + BUILD_FEATURES := $(BUILD_FEATURES). $(COMPILER_NAME) + $(info ***) + $(info *** $(BUILD_SINGLE)Simulator$(BUILD_MULTIPLE) being built with:) + $(info *** $(BUILD_FEATURES).) + ifneq (,$(NETWORK_FEATURES)) + $(info *** $(NETWORK_FEATURES).) + endif + ifneq (,$(NETWORK_LAN_FEATURES)) + $(info *** - Local LAN packet transports: $(NETWORK_LAN_FEATURES)) + endif + ifneq (,$(VIDEO_FEATURES)) + $(info *** $(VIDEO_FEATURES).) + endif + ifneq (,$(GIT_COMMIT_ID)) + $(info ***) + $(info *** git commit id is $(GIT_COMMIT_ID).) + endif + $(info ***) +endif +ifneq ($(DONT_USE_ROMS),) + ROMS_OPT = -DDONT_USE_INTERNAL_ROM +else + BUILD_ROMS = ${BIN}BuildROMs${EXE} +endif +ifneq ($(DONT_USE_READER_THREAD),) + NETWORK_OPT += -DDONT_USE_READER_THREAD +endif + +ifeq (HP-UX,$(OSTYPE)) + CC_STD = -std=gnu99 +else + ifeq (,$(SUNC_VERSION)) + CC_STD = -std=c99 + endif +endif +CC_OUTSPEC = -o $@ +CC := $(GCC) $(CC_STD) -U__STRICT_ANSI__ $(CFLAGS_G) $(CFLAGS_O) $(CFLAGS_GIT) -DSIM_COMPILER="$(COMPILER_NAME)" -I . $(OS_CCDEFS) $(ROMS_OPT) +LDFLAGS := $(OS_LDFLAGS) $(NETWORK_LDFLAGS) $(LDFLAGS_O) + +# +# Common Libraries +# +BIN = BIN/ +SIM = scp.c sim_console.c sim_fio.c sim_timer.c sim_sock.c \ + sim_tmxr.c sim_ether.c sim_tape.c sim_disk.c sim_serial.c \ + sim_video.c + +DISPLAYD = display +ifeq ($(WIN32),) + ifeq (x11,$(shell if $(TEST) -e /usr/include/X11/Intrinsic.h ; then echo x11; fi)) + DISPLAYL = ${DISPLAYD}/display.c $(DISPLAYD)/x11.c + DISPLAYVT = ${DISPLAYD}/vt11.c + DISPLAY_OPT = -DUSE_DISPLAY -I/usr/X11/include -lXt -lX11 -lm + else + DISPLAYL = + DISPLAYVT = + DISPLAY_OPT = + endif +else + DISPLAYL = ${DISPLAYD}/display.c $(DISPLAYD)/win32.c + DISPLAYVT = ${DISPLAYD}/vt11.c + DISPLAY_OPT = -DUSE_DISPLAY -lgdi32 +endif + +# +# Emulator source files and compile time options +# +PDP1D = PDP1 +PDP1 = ${PDP1D}/pdp1_lp.c ${PDP1D}/pdp1_cpu.c ${PDP1D}/pdp1_stddev.c \ + ${PDP1D}/pdp1_sys.c ${PDP1D}/pdp1_dt.c ${PDP1D}/pdp1_drm.c \ + ${PDP1D}/pdp1_clk.c ${PDP1D}/pdp1_dcs.c ${PDP1D}/pdp1_dpy.c ${DISPLAYL} +PDP1_OPT = -I ${PDP1D} $(DISPLAY_OPT) + + +NOVAD = NOVA +NOVA = ${NOVAD}/nova_sys.c ${NOVAD}/nova_cpu.c ${NOVAD}/nova_dkp.c \ + ${NOVAD}/nova_dsk.c ${NOVAD}/nova_lp.c ${NOVAD}/nova_mta.c \ + ${NOVAD}/nova_plt.c ${NOVAD}/nova_pt.c ${NOVAD}/nova_clk.c \ + ${NOVAD}/nova_tt.c ${NOVAD}/nova_tt1.c ${NOVAD}/nova_qty.c +NOVA_OPT = -I ${NOVAD} + + +ECLIPSE = ${NOVAD}/eclipse_cpu.c ${NOVAD}/eclipse_tt.c ${NOVAD}/nova_sys.c \ + ${NOVAD}/nova_dkp.c ${NOVAD}/nova_dsk.c ${NOVAD}/nova_lp.c \ + ${NOVAD}/nova_mta.c ${NOVAD}/nova_plt.c ${NOVAD}/nova_pt.c \ + ${NOVAD}/nova_clk.c ${NOVAD}/nova_tt1.c ${NOVAD}/nova_qty.c +ECLIPSE_OPT = -I ${NOVAD} -DECLIPSE + + +PDP18BD = PDP18B +PDP18B = ${PDP18BD}/pdp18b_dt.c ${PDP18BD}/pdp18b_drm.c ${PDP18BD}/pdp18b_cpu.c \ + ${PDP18BD}/pdp18b_lp.c ${PDP18BD}/pdp18b_mt.c ${PDP18BD}/pdp18b_rf.c \ + ${PDP18BD}/pdp18b_rp.c ${PDP18BD}/pdp18b_stddev.c ${PDP18BD}/pdp18b_sys.c \ + ${PDP18BD}/pdp18b_rb.c ${PDP18BD}/pdp18b_tt1.c ${PDP18BD}/pdp18b_fpp.c +PDP4_OPT = -DPDP4 -I ${PDP18BD} +PDP7_OPT = -DPDP7 -I ${PDP18BD} +PDP9_OPT = -DPDP9 -I ${PDP18BD} +PDP15_OPT = -DPDP15 -I ${PDP18BD} + + +PDP11D = PDP11 +PDP11 = ${PDP11D}/pdp11_fp.c ${PDP11D}/pdp11_cpu.c ${PDP11D}/pdp11_dz.c \ + ${PDP11D}/pdp11_cis.c ${PDP11D}/pdp11_lp.c ${PDP11D}/pdp11_rk.c \ + ${PDP11D}/pdp11_rl.c ${PDP11D}/pdp11_rp.c ${PDP11D}/pdp11_rx.c \ + ${PDP11D}/pdp11_stddev.c ${PDP11D}/pdp11_sys.c ${PDP11D}/pdp11_tc.c \ + ${PDP11D}/pdp11_tm.c ${PDP11D}/pdp11_ts.c ${PDP11D}/pdp11_io.c \ + ${PDP11D}/pdp11_rq.c ${PDP11D}/pdp11_tq.c ${PDP11D}/pdp11_pclk.c \ + ${PDP11D}/pdp11_ry.c ${PDP11D}/pdp11_pt.c ${PDP11D}/pdp11_hk.c \ + ${PDP11D}/pdp11_xq.c ${PDP11D}/pdp11_xu.c ${PDP11D}/pdp11_vh.c \ + ${PDP11D}/pdp11_rh.c ${PDP11D}/pdp11_tu.c ${PDP11D}/pdp11_cpumod.c \ + ${PDP11D}/pdp11_cr.c ${PDP11D}/pdp11_rf.c ${PDP11D}/pdp11_dl.c \ + ${PDP11D}/pdp11_ta.c ${PDP11D}/pdp11_rc.c ${PDP11D}/pdp11_kg.c \ + ${PDP11D}/pdp11_ke.c ${PDP11D}/pdp11_dc.c ${PDP11D}/pdp11_dmc.c \ + ${PDP11D}/pdp11_kmc.c ${PDP11D}/pdp11_dup.c ${PDP11D}/pdp11_rs.c \ + ${PDP11D}/pdp11_vt.c ${PDP11D}/pdp11_io_lib.c $(DISPLAYL) $(DISPLAYVT) +PDP11_OPT = -DVM_PDP11 -I ${PDP11D} ${NETWORK_OPT} $(DISPLAY_OPT) + + +VAXD = VAX +VAX = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c ${VAXD}/vax_io.c \ + ${VAXD}/vax_cis.c ${VAXD}/vax_octa.c ${VAXD}/vax_cmode.c \ + ${VAXD}/vax_mmu.c ${VAXD}/vax_stddev.c ${VAXD}/vax_sysdev.c \ + ${VAXD}/vax_sys.c ${VAXD}/vax_syscm.c ${VAXD}/vax_syslist.c \ + ${PDP11D}/pdp11_rl.c ${PDP11D}/pdp11_rq.c ${PDP11D}/pdp11_ts.c \ + ${PDP11D}/pdp11_dz.c ${PDP11D}/pdp11_lp.c ${PDP11D}/pdp11_tq.c \ + ${PDP11D}/pdp11_xq.c ${PDP11D}/pdp11_vh.c ${PDP11D}/pdp11_cr.c \ + ${PDP11D}/pdp11_io_lib.c +VAX_OPT = -DVM_VAX -DUSE_INT64 -DUSE_ADDR64 -I ${VAXD} -I ${PDP11D} ${NETWORK_OPT} + + +VAX610 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ + ${VAXD}/vax_cis.c ${VAXD}/vax_octa.c ${VAXD}/vax_cmode.c \ + ${VAXD}/vax_mmu.c ${VAXD}/vax_sys.c ${VAXD}/vax_syscm.c \ + ${VAXD}/vax610_stddev.c ${VAXD}/vax610_sysdev.c ${VAXD}/vax610_io.c \ + ${VAXD}/vax610_syslist.c ${VAXD}/vax610_mem.c ${VAXD}/vax_vc.c \ + ${VAXD}/vax_lk.c ${VAXD}/vax_vs.c ${VAXD}/vax_2681.c \ + ${PDP11D}/pdp11_rl.c ${PDP11D}/pdp11_rq.c ${PDP11D}/pdp11_ts.c \ + ${PDP11D}/pdp11_dz.c ${PDP11D}/pdp11_lp.c ${PDP11D}/pdp11_tq.c \ + ${PDP11D}/pdp11_xq.c ${PDP11D}/pdp11_vh.c ${PDP11D}/pdp11_cr.c \ + ${PDP11D}/pdp11_io_lib.c +VAX610_OPT = -DVM_VAX -DVAX_610 -DUSE_INT64 -DUSE_ADDR64 -DUSE_SIM_VIDEO -I ${VAXD} -I ${PDP11D} ${NETWORK_OPT} + +VAX630 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ + ${VAXD}/vax_cis.c ${VAXD}/vax_octa.c ${VAXD}/vax_cmode.c \ + ${VAXD}/vax_mmu.c ${VAXD}/vax_sys.c ${VAXD}/vax_syscm.c \ + ${VAXD}/vax_watch.c ${VAXD}/vax630_stddev.c ${VAXD}/vax630_sysdev.c \ + ${VAXD}/vax630_io.c ${VAXD}/vax630_syslist.c ${VAXD}/vax_vc.c \ + ${VAXD}/vax_lk.c ${VAXD}/vax_vs.c ${VAXD}/vax_2681.c \ + ${PDP11D}/pdp11_rl.c ${PDP11D}/pdp11_rq.c ${PDP11D}/pdp11_ts.c \ + ${PDP11D}/pdp11_dz.c ${PDP11D}/pdp11_lp.c ${PDP11D}/pdp11_tq.c \ + ${PDP11D}/pdp11_xq.c ${PDP11D}/pdp11_vh.c ${PDP11D}/pdp11_cr.c \ + ${PDP11D}/pdp11_io_lib.c +VAX620_OPT = -DVM_VAX -DVAX_620 -DUSE_INT64 -DUSE_ADDR64 -I ${VAXD} -I ${PDP11D} ${NETWORK_OPT} +VAX630_OPT = -DVM_VAX -DVAX_630 -DUSE_INT64 -DUSE_ADDR64 -DUSE_SIM_VIDEO -I ${VAXD} -I ${PDP11D} ${NETWORK_OPT} + + +VAX730 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ + ${VAXD}/vax_cis.c ${VAXD}/vax_octa.c ${VAXD}/vax_cmode.c \ + ${VAXD}/vax_mmu.c ${VAXD}/vax_sys.c ${VAXD}/vax_syscm.c \ + ${VAXD}/vax730_stddev.c ${VAXD}/vax730_sys.c \ + ${VAXD}/vax730_mem.c ${VAXD}/vax730_uba.c ${VAXD}/vax730_rb.c \ + ${VAXD}/vax730_syslist.c \ + ${PDP11D}/pdp11_rl.c ${PDP11D}/pdp11_rq.c ${PDP11D}/pdp11_ts.c \ + ${PDP11D}/pdp11_dz.c ${PDP11D}/pdp11_lp.c ${PDP11D}/pdp11_tq.c \ + ${PDP11D}/pdp11_xu.c ${PDP11D}/pdp11_ry.c ${PDP11D}/pdp11_cr.c \ + ${PDP11D}/pdp11_hk.c ${PDP11D}/pdp11_vh.c ${PDP11D}/pdp11_dmc.c \ + ${PDP11D}/pdp11_dup.c ${PDP11D}/pdp11_io_lib.c +VAX730_OPT = -DVM_VAX -DVAX_730 -DUSE_INT64 -DUSE_ADDR64 -I VAX -I ${PDP11D} ${NETWORK_OPT} + + +VAX750 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ + ${VAXD}/vax_cis.c ${VAXD}/vax_octa.c ${VAXD}/vax_cmode.c \ + ${VAXD}/vax_mmu.c ${VAXD}/vax_sys.c ${VAXD}/vax_syscm.c \ + ${VAXD}/vax750_stddev.c ${VAXD}/vax750_cmi.c \ + ${VAXD}/vax750_mem.c ${VAXD}/vax750_uba.c ${VAXD}/vax7x0_mba.c \ + ${VAXD}/vax750_syslist.c \ + ${PDP11D}/pdp11_rl.c ${PDP11D}/pdp11_rq.c ${PDP11D}/pdp11_ts.c \ + ${PDP11D}/pdp11_dz.c ${PDP11D}/pdp11_lp.c ${PDP11D}/pdp11_tq.c \ + ${PDP11D}/pdp11_xu.c ${PDP11D}/pdp11_ry.c ${PDP11D}/pdp11_cr.c \ + ${PDP11D}/pdp11_hk.c ${PDP11D}/pdp11_rp.c ${PDP11D}/pdp11_tu.c \ + ${PDP11D}/pdp11_vh.c ${PDP11D}/pdp11_dmc.c ${PDP11D}/pdp11_dup.c \ + ${PDP11D}/pdp11_io_lib.c +VAX750_OPT = -DVM_VAX -DVAX_750 -DUSE_INT64 -DUSE_ADDR64 -I VAX -I ${PDP11D} ${NETWORK_OPT} + + +VAX780 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ + ${VAXD}/vax_cis.c ${VAXD}/vax_octa.c ${VAXD}/vax_cmode.c \ + ${VAXD}/vax_mmu.c ${VAXD}/vax_sys.c ${VAXD}/vax_syscm.c \ + ${VAXD}/vax780_stddev.c ${VAXD}/vax780_sbi.c \ + ${VAXD}/vax780_mem.c ${VAXD}/vax780_uba.c ${VAXD}/vax7x0_mba.c \ + ${VAXD}/vax780_fload.c ${VAXD}/vax780_syslist.c \ + ${PDP11D}/pdp11_rl.c ${PDP11D}/pdp11_rq.c ${PDP11D}/pdp11_ts.c \ + ${PDP11D}/pdp11_dz.c ${PDP11D}/pdp11_lp.c ${PDP11D}/pdp11_tq.c \ + ${PDP11D}/pdp11_xu.c ${PDP11D}/pdp11_ry.c ${PDP11D}/pdp11_cr.c \ + ${PDP11D}/pdp11_rp.c ${PDP11D}/pdp11_tu.c ${PDP11D}/pdp11_hk.c \ + ${PDP11D}/pdp11_vh.c ${PDP11D}/pdp11_dmc.c ${PDP11D}/pdp11_dup.c \ + ${PDP11D}/pdp11_io_lib.c +VAX780_OPT = -DVM_VAX -DVAX_780 -DUSE_INT64 -DUSE_ADDR64 -I VAX -I ${PDP11D} ${NETWORK_OPT} + + +VAX8600 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ + ${VAXD}/vax_cis.c ${VAXD}/vax_octa.c ${VAXD}/vax_cmode.c \ + ${VAXD}/vax_mmu.c ${VAXD}/vax_sys.c ${VAXD}/vax_syscm.c \ + ${VAXD}/vax860_stddev.c ${VAXD}/vax860_sbia.c \ + ${VAXD}/vax860_abus.c ${VAXD}/vax780_uba.c ${VAXD}/vax7x0_mba.c \ + ${VAXD}/vax860_syslist.c \ + ${PDP11D}/pdp11_rl.c ${PDP11D}/pdp11_rq.c ${PDP11D}/pdp11_ts.c \ + ${PDP11D}/pdp11_dz.c ${PDP11D}/pdp11_lp.c ${PDP11D}/pdp11_tq.c \ + ${PDP11D}/pdp11_xu.c ${PDP11D}/pdp11_ry.c ${PDP11D}/pdp11_cr.c \ + ${PDP11D}/pdp11_rp.c ${PDP11D}/pdp11_tu.c ${PDP11D}/pdp11_hk.c \ + ${PDP11D}/pdp11_vh.c ${PDP11D}/pdp11_dmc.c ${PDP11D}/pdp11_dup.c \ + ${PDP11D}/pdp11_io_lib.c +VAX8600_OPT = -DVM_VAX -DVAX_860 -DUSE_INT64 -DUSE_ADDR64 -I VAX -I ${PDP11D} ${NETWORK_OPT} + + +PDP10D = PDP10 +PDP10 = ${PDP10D}/pdp10_fe.c ${PDP11D}/pdp11_dz.c ${PDP10D}/pdp10_cpu.c \ + ${PDP10D}/pdp10_ksio.c ${PDP10D}/pdp10_lp20.c ${PDP10D}/pdp10_mdfp.c \ + ${PDP10D}/pdp10_pag.c ${PDP10D}/pdp10_rp.c ${PDP10D}/pdp10_sys.c \ + ${PDP10D}/pdp10_tim.c ${PDP10D}/pdp10_tu.c ${PDP10D}/pdp10_xtnd.c \ + ${PDP11D}/pdp11_pt.c ${PDP11D}/pdp11_ry.c ${PDP11D}/pdp11_cr.c \ + ${PDP11D}/pdp11_dup.c ${PDP11D}/pdp11_dmc.c ${PDP11D}/pdp11_kmc.c +PDP10_OPT = -DVM_PDP10 -DUSE_INT64 -I ${PDP10D} -I ${PDP11D} + + +PDP8D = PDP8 +PDP8 = ${PDP8D}/pdp8_cpu.c ${PDP8D}/pdp8_clk.c ${PDP8D}/pdp8_df.c \ + ${PDP8D}/pdp8_dt.c ${PDP8D}/pdp8_lp.c ${PDP8D}/pdp8_mt.c \ + ${PDP8D}/pdp8_pt.c ${PDP8D}/pdp8_rf.c ${PDP8D}/pdp8_rk.c \ + ${PDP8D}/pdp8_rx.c ${PDP8D}/pdp8_sys.c ${PDP8D}/pdp8_tt.c \ + ${PDP8D}/pdp8_ttx.c ${PDP8D}/pdp8_rl.c ${PDP8D}/pdp8_tsc.c \ + ${PDP8D}/pdp8_td.c ${PDP8D}/pdp8_ct.c ${PDP8D}/pdp8_fpp.c +PDP8_OPT = -I ${PDP8D} + + +H316D = H316 +H316 = ${H316D}/h316_stddev.c ${H316D}/h316_lp.c ${H316D}/h316_cpu.c \ + ${H316D}/h316_sys.c ${H316D}/h316_mt.c ${H316D}/h316_fhd.c \ + ${H316D}/h316_dp.c ${H316D}/h316_rtc.c ${H316D}/h316_imp.c \ + ${H316D}/h316_hi.c ${H316D}/h316_mi.c ${H316D}/h316_udp.c +H316_OPT = -I ${H316D} -D VM_IMPTIP + + +HP2100D = HP2100 +HP2100 = ${HP2100D}/hp2100_stddev.c ${HP2100D}/hp2100_dp.c ${HP2100D}/hp2100_dq.c \ + ${HP2100D}/hp2100_dr.c ${HP2100D}/hp2100_lps.c ${HP2100D}/hp2100_ms.c \ + ${HP2100D}/hp2100_mt.c ${HP2100D}/hp2100_mux.c ${HP2100D}/hp2100_cpu.c \ + ${HP2100D}/hp2100_fp.c ${HP2100D}/hp2100_sys.c ${HP2100D}/hp2100_lpt.c \ + ${HP2100D}/hp2100_ipl.c ${HP2100D}/hp2100_ds.c ${HP2100D}/hp2100_cpu0.c \ + ${HP2100D}/hp2100_cpu1.c ${HP2100D}/hp2100_cpu2.c ${HP2100D}/hp2100_cpu3.c \ + ${HP2100D}/hp2100_cpu4.c ${HP2100D}/hp2100_cpu5.c ${HP2100D}/hp2100_cpu6.c \ + ${HP2100D}/hp2100_cpu7.c ${HP2100D}/hp2100_fp1.c ${HP2100D}/hp2100_baci.c \ + ${HP2100D}/hp2100_mpx.c ${HP2100D}/hp2100_pif.c ${HP2100D}/hp2100_di.c \ + ${HP2100D}/hp2100_di_da.c ${HP2100D}/hp_disclib.c +HP2100_OPT = -DHAVE_INT64 -I ${HP2100D} + + +I1401D = I1401 +I1401 = ${I1401D}/i1401_lp.c ${I1401D}/i1401_cpu.c ${I1401D}/i1401_iq.c \ + ${I1401D}/i1401_cd.c ${I1401D}/i1401_mt.c ${I1401D}/i1401_dp.c \ + ${I1401D}/i1401_sys.c +I1401_OPT = -I ${I1401D} + + +I1620D = I1620 +I1620 = ${I1620D}/i1620_cd.c ${I1620D}/i1620_dp.c ${I1620D}/i1620_pt.c \ + ${I1620D}/i1620_tty.c ${I1620D}/i1620_cpu.c ${I1620D}/i1620_lp.c \ + ${I1620D}/i1620_fp.c ${I1620D}/i1620_sys.c +I1620_OPT = -I ${I1620D} + + +I7094D = I7094 +I7094 = ${I7094D}/i7094_cpu.c ${I7094D}/i7094_cpu1.c ${I7094D}/i7094_io.c \ + ${I7094D}/i7094_cd.c ${I7094D}/i7094_clk.c ${I7094D}/i7094_com.c \ + ${I7094D}/i7094_drm.c ${I7094D}/i7094_dsk.c ${I7094D}/i7094_sys.c \ + ${I7094D}/i7094_lp.c ${I7094D}/i7094_mt.c ${I7094D}/i7094_binloader.c +I7094_OPT = -DUSE_INT64 -I ${I7094D} + + +IBM1130D = Ibm1130 +IBM1130 = ${IBM1130D}/ibm1130_cpu.c ${IBM1130D}/ibm1130_cr.c \ + ${IBM1130D}/ibm1130_disk.c ${IBM1130D}/ibm1130_stddev.c \ + ${IBM1130D}/ibm1130_sys.c ${IBM1130D}/ibm1130_gdu.c \ + ${IBM1130D}/ibm1130_gui.c ${IBM1130D}/ibm1130_prt.c \ + ${IBM1130D}/ibm1130_fmt.c ${IBM1130D}/ibm1130_ptrp.c \ + ${IBM1130D}/ibm1130_plot.c ${IBM1130D}/ibm1130_sca.c \ + ${IBM1130D}/ibm1130_t2741.c +IBM1130_OPT = -I ${IBM1130D} +ifneq ($(WIN32),) +IBM1130_OPT += -DGUI_SUPPORT -lgdi32 +endif + + +ID16D = Interdata +ID16 = ${ID16D}/id16_cpu.c ${ID16D}/id16_sys.c ${ID16D}/id_dp.c \ + ${ID16D}/id_fd.c ${ID16D}/id_fp.c ${ID16D}/id_idc.c ${ID16D}/id_io.c \ + ${ID16D}/id_lp.c ${ID16D}/id_mt.c ${ID16D}/id_pas.c ${ID16D}/id_pt.c \ + ${ID16D}/id_tt.c ${ID16D}/id_uvc.c ${ID16D}/id16_dboot.c ${ID16D}/id_ttp.c +ID16_OPT = -I ${ID16D} + + +ID32D = Interdata +ID32 = ${ID32D}/id32_cpu.c ${ID32D}/id32_sys.c ${ID32D}/id_dp.c \ + ${ID32D}/id_fd.c ${ID32D}/id_fp.c ${ID32D}/id_idc.c ${ID32D}/id_io.c \ + ${ID32D}/id_lp.c ${ID32D}/id_mt.c ${ID32D}/id_pas.c ${ID32D}/id_pt.c \ + ${ID32D}/id_tt.c ${ID32D}/id_uvc.c ${ID32D}/id32_dboot.c ${ID32D}/id_ttp.c +ID32_OPT = -I ${ID32D} + + +S3D = S3 +S3 = ${S3D}/s3_cd.c ${S3D}/s3_cpu.c ${S3D}/s3_disk.c ${S3D}/s3_lp.c \ + ${S3D}/s3_pkb.c ${S3D}/s3_sys.c +S3_OPT = -I ${S3D} + + +ALTAIRD = ALTAIR +ALTAIR = ${ALTAIRD}/altair_sio.c ${ALTAIRD}/altair_cpu.c ${ALTAIRD}/altair_dsk.c \ + ${ALTAIRD}/altair_sys.c +ALTAIR_OPT = -I ${ALTAIRD} + + +ALTAIRZ80D = AltairZ80 +ALTAIRZ80 = ${ALTAIRZ80D}/altairz80_cpu.c ${ALTAIRZ80D}/altairz80_cpu_nommu.c \ + ${ALTAIRZ80D}/altairz80_dsk.c ${ALTAIRZ80D}/disasm.c \ + ${ALTAIRZ80D}/altairz80_sio.c ${ALTAIRZ80D}/altairz80_sys.c \ + ${ALTAIRZ80D}/altairz80_hdsk.c ${ALTAIRZ80D}/altairz80_net.c \ + ${ALTAIRZ80D}/flashwriter2.c ${ALTAIRZ80D}/i86_decode.c \ + ${ALTAIRZ80D}/i86_ops.c ${ALTAIRZ80D}/i86_prim_ops.c \ + ${ALTAIRZ80D}/i8272.c ${ALTAIRZ80D}/insnsd.c ${ALTAIRZ80D}/altairz80_mhdsk.c \ + ${ALTAIRZ80D}/mfdc.c ${ALTAIRZ80D}/n8vem.c ${ALTAIRZ80D}/vfdhd.c \ + ${ALTAIRZ80D}/s100_disk1a.c ${ALTAIRZ80D}/s100_disk2.c ${ALTAIRZ80D}/s100_disk3.c \ + ${ALTAIRZ80D}/s100_fif.c ${ALTAIRZ80D}/s100_mdriveh.c \ + ${ALTAIRZ80D}/s100_mdsad.c ${ALTAIRZ80D}/s100_selchan.c \ + ${ALTAIRZ80D}/s100_ss1.c ${ALTAIRZ80D}/s100_64fdc.c \ + ${ALTAIRZ80D}/s100_scp300f.c ${ALTAIRZ80D}/sim_imd.c \ + ${ALTAIRZ80D}/wd179x.c ${ALTAIRZ80D}/s100_hdc1001.c \ + ${ALTAIRZ80D}/s100_if3.c ${ALTAIRZ80D}/s100_adcs6.c \ + ${ALTAIRZ80D}/m68kcpu.c ${ALTAIRZ80D}/m68kdasm.c \ + ${ALTAIRZ80D}/m68kopac.c ${ALTAIRZ80D}/m68kopdm.c \ + ${ALTAIRZ80D}/m68kopnz.c ${ALTAIRZ80D}/m68kops.c ${ALTAIRZ80D}/m68ksim.c +ALTAIRZ80_OPT = -I ${ALTAIRZ80D} + + +GRID = GRI +GRI = ${GRID}/gri_cpu.c ${GRID}/gri_stddev.c ${GRID}/gri_sys.c +GRI_OPT = -I ${GRID} + + +LGPD = LGP +LGP = ${LGPD}/lgp_cpu.c ${LGPD}/lgp_stddev.c ${LGPD}/lgp_sys.c +LGP_OPT = -I ${LGPD} + + +SDSD = SDS +SDS = ${SDSD}/sds_cpu.c ${SDSD}/sds_drm.c ${SDSD}/sds_dsk.c ${SDSD}/sds_io.c \ + ${SDSD}/sds_lp.c ${SDSD}/sds_mt.c ${SDSD}/sds_mux.c ${SDSD}/sds_rad.c \ + ${SDSD}/sds_stddev.c ${SDSD}/sds_sys.c +SDS_OPT = -I ${SDSD} + +SWTP6800D = swtp6800/swtp6800 +SWTP6800C = swtp6800/common +SWTP6800MP-A = ${SWTP6800C}/mp-a.c ${SWTP6800C}/m6800.c ${SWTP6800C}/m6810.c \ + ${SWTP6800C}/bootrom.c ${SWTP6800C}/dc-4.c ${SWTP6800C}/mp-s.c ${SWTP6800D}/mp-a_sys.c \ + ${SWTP6800C}/mp-b2.c ${SWTP6800C}/mp-8m.c +SWTP6800MP-A2 = ${SWTP6800C}/mp-a2.c ${SWTP6800C}/m6800.c ${SWTP6800C}/m6810.c \ + ${SWTP6800C}/bootrom.c ${SWTP6800C}/dc-4.c ${SWTP6800C}/mp-s.c ${SWTP6800D}/mp-a2_sys.c \ + ${SWTP6800C}/mp-b2.c ${SWTP6800C}/mp-8m.c ${SWTP6800C}/i2716.c +SWTP6800_OPT = -I ${SWTP6800D} + +TX0D = TX-0 +TX0 = ${TX0D}/tx0_cpu.c ${TX0D}/tx0_dpy.c ${TX0D}/tx0_stddev.c \ + ${TX0D}/tx0_sys.c ${TX0D}/tx0_sys_orig.c ${DISPLAYL} +TX0_OPT = -I ${TX0D} $(DISPLAY_OPT) + + +SSEMD = SSEM +SSEM = ${SSEMD}/ssem_cpu.c ${SSEMD}/ssem_sys.c +SSEM_OPT = -I ${SSEMD} + +System_80-10D = Intel/System_80-10 +iCOMMOND = Intel/common +System_80-10 = ${iCOMMOND}/i8251.c ${iCOMMOND}/i8255.c ${iCOMMOND}/i8080.c \ + ${iCOMMOND}/multibus.c ${iCOMMOND}/iSBC80-10.c \ + ${System_80-10D}/system_80_10_sys.c ${iCOMMOND}/isbc208.c \ + ${iCOMMOND}/isbc064.c ${iCOMMOND}/ieprom.c ${iCOMMOND}/iram8.c +System_80-10_OPT = -I ${System_80-10D} + + +System_80-20D = Intel/System_80-20 +iCOMMOND = Intel/common +System_80-20 = ${iCOMMOND}/i8251.c ${iCOMMOND}/i8255.c ${iCOMMOND}/i8080.c \ + ${iCOMMOND}/multibus.c ${iCOMMOND}/iSBC80-20.c \ + ${System_80-20D}/system_80_20_sys.c ${iCOMMOND}/isbc208.c \ + ${iCOMMOND}/isbc064.c ${iCOMMOND}/ieprom.c ${iCOMMOND}/iram8.c \ + ${iCOMMOND}/i8259.c +System_80-20_OPT = -I ${System_80-20D} + + +System_80-30D = Intel/System_80-30 +iCOMMOND = Intel/common +System_80-30 = ${iCOMMOND}/i8251.c ${iCOMMOND}/i8255.c ${iCOMMOND}/i8080.c \ + ${iCOMMOND}/multibus.c ${iCOMMOND}/iSBC80-20.c \ + ${System_80-30D}/system_80_30_sys.c ${iCOMMOND}/isbc208.c \ + ${iCOMMOND}/isbc064.c ${iCOMMOND}/ieprom.c ${iCOMMOND}/iram8.c \ + ${iCOMMOND}/i8259.c +System_80-30_OPT = -I ${System_80-30D} + + +# +# Build everything +# +#ALL = pdp1 pdp4 pdp7 pdp8 pdp9 pdp15 pdp11 pdp10 +# vax microvax3900 microvax1 rtvax1000 microvax2 vax730 vax750 vax780 vax8600 +# nova eclipse hp2100 i1401 i1620 s3 altair altairz80 gri +## swtp6800mp-a swtp6800mp-a2 tx-0 ssem +ALL = System_80-10 + +all : ${ALL} + +clean : +ifeq ($(WIN32),) + ${RM} -r ${BIN} +else + if exist BIN\*.exe del /q BIN\*.exe + if exist BIN rmdir BIN +endif + +${BIN}BuildROMs${EXE} : + ${MKDIRBIN} +ifeq (agcc,$(findstring agcc,$(firstword $(CC)))) + gcc $(wordlist 2,1000,${CC}) sim_BuildROMs.c $(CC_OUTSPEC) +else + ${CC} sim_BuildROMs.c $(CC_OUTSPEC) +endif +ifeq ($(WIN32),) + $@ + ${RM} $@ + ifeq (Darwin,$(OSTYPE)) # remove Xcode's debugging symbols folder too + ${RM} -rf $@.dSYM + endif +else + $(@D)\$(@F) + del $(@D)\$(@F) +endif + +# +# Individual builds +# +System_80-10 : ${BIN}System_80-10${EXE} + +${BIN}System_80-10${EXE} : ${System_80-10} ${SIM} + ${MKDIRBIN} + ${CC} ${System_80-10} ${SIM} ${System_80-10_OPT} -o $@ ${LDFLAGS} + +System_80-20 : ${BIN}System_80-20${EXE} + +${BIN}System_80-20${EXE} : ${System_80-20} ${SIM} + ${MKDIRBIN} + ${CC} ${System_80-20} ${SIM} ${System_80-20_OPT} -o $@ ${LDFLAGS} + +System_80-30 : ${BIN}System_80-30${EXE} + +${BIN}System_80-30${EXE} : ${System_80-30} ${SIM} + ${MKDIRBIN} + ${CC} ${System_80-30} ${SIM} ${System_80-30_OPT} -o $@ ${LDFLAGS} + +pdp1 : ${BIN}pdp1${EXE} + +${BIN}pdp1${EXE} : ${PDP1} ${SIM} + ${MKDIRBIN} + ${CC} ${PDP1} ${SIM} ${PDP1_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +pdp4 : ${BIN}pdp4${EXE} + +${BIN}pdp4${EXE} : ${PDP18B} ${SIM} + ${MKDIRBIN} + ${CC} ${PDP18B} ${SIM} ${PDP4_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +pdp7 : ${BIN}pdp7${EXE} + +${BIN}pdp7${EXE} : ${PDP18B} ${SIM} + ${MKDIRBIN} + ${CC} ${PDP18B} ${SIM} ${PDP7_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +pdp8 : ${BIN}pdp8${EXE} + +${BIN}pdp8${EXE} : ${PDP8} ${SIM} + ${MKDIRBIN} + ${CC} ${PDP8} ${SIM} ${PDP8_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +pdp9 : ${BIN}pdp9${EXE} + +${BIN}pdp9${EXE} : ${PDP18B} ${SIM} + ${MKDIRBIN} + ${CC} ${PDP18B} ${SIM} ${PDP9_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +pdp15 : ${BIN}pdp15${EXE} + +${BIN}pdp15${EXE} : ${PDP18B} ${SIM} + ${MKDIRBIN} + ${CC} ${PDP18B} ${SIM} ${PDP15_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +pdp10 : ${BIN}pdp10${EXE} + +${BIN}pdp10${EXE} : ${PDP10} ${SIM} + ${MKDIRBIN} + ${CC} ${PDP10} ${SIM} ${PDP10_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +pdp11 : ${BIN}pdp11${EXE} + +${BIN}pdp11${EXE} : ${PDP11} ${SIM} + ${MKDIRBIN} + ${CC} ${PDP11} ${SIM} ${PDP11_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +vax : microvax3900 + +microvax3900 : ${BIN}microvax3900${EXE} + +${BIN}microvax3900${EXE} : ${VAX} ${SIM} ${BUILD_ROMS} + ${MKDIRBIN} + ${CC} ${VAX} ${SIM} ${VAX_OPT} $(CC_OUTSPEC) ${LDFLAGS} +ifeq ($(WIN32),) + cp ${BIN}microvax3900${EXE} ${BIN}vax${EXE} +else + copy $(@D)\microvax3900${EXE} $(@D)\vax${EXE} +endif + +microvax1 : ${BIN}microvax1${EXE} + +${BIN}microvax1${EXE} : ${VAX610} ${SIM} ${BUILD_ROMS} + ${MKDIRBIN} + ${CC} ${VAX610} ${SIM} ${VAX610_OPT} -o $@ ${LDFLAGS} + +rtvax1000 : ${BIN}rtvax1000${EXE} + +${BIN}rtvax1000${EXE} : ${VAX630} ${SIM} ${BUILD_ROMS} + ${MKDIRBIN} + ${CC} ${VAX630} ${SIM} ${VAX620_OPT} -o $@ ${LDFLAGS} + +microvax2 : ${BIN}microvax2${EXE} + +${BIN}microvax2${EXE} : ${VAX630} ${SIM} ${BUILD_ROMS} + ${MKDIRBIN} + ${CC} ${VAX630} ${SIM} ${VAX630_OPT} -o $@ ${LDFLAGS} + +vax730 : ${BIN}vax730${EXE} + +${BIN}vax730${EXE} : ${VAX730} ${SIM} ${BUILD_ROMS} + ${MKDIRBIN} + ${CC} ${VAX730} ${SIM} ${VAX730_OPT} -o $@ ${LDFLAGS} + +vax750 : ${BIN}vax750${EXE} + +${BIN}vax750${EXE} : ${VAX750} ${SIM} ${BUILD_ROMS} + ${MKDIRBIN} + ${CC} ${VAX750} ${SIM} ${VAX750_OPT} -o $@ ${LDFLAGS} + +vax780 : ${BIN}vax780${EXE} + +${BIN}vax780${EXE} : ${VAX780} ${SIM} ${BUILD_ROMS} + ${MKDIRBIN} + ${CC} ${VAX780} ${SIM} ${VAX780_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +vax8600 : ${BIN}vax8600${EXE} + +${BIN}vax8600${EXE} : ${VAX8600} ${SIM} ${BUILD_ROMS} + ${MKDIRBIN} + ${CC} ${VAX8600} ${SIM} ${VAX8600_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +nova : ${BIN}nova${EXE} + +${BIN}nova${EXE} : ${NOVA} ${SIM} + ${MKDIRBIN} + ${CC} ${NOVA} ${SIM} ${NOVA_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +eclipse : ${BIN}eclipse${EXE} + +${BIN}eclipse${EXE} : ${ECLIPSE} ${SIM} + ${MKDIRBIN} + ${CC} ${ECLIPSE} ${SIM} ${ECLIPSE_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +h316 : ${BIN}h316${EXE} + +${BIN}h316${EXE} : ${H316} ${SIM} + ${MKDIRBIN} + ${CC} ${H316} ${SIM} ${H316_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +hp2100 : ${BIN}hp2100${EXE} + +${BIN}hp2100${EXE} : ${HP2100} ${SIM} + ${MKDIRBIN} + ${CC} ${HP2100} ${SIM} ${HP2100_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +i1401 : ${BIN}i1401${EXE} + +${BIN}i1401${EXE} : ${I1401} ${SIM} + ${MKDIRBIN} + ${CC} ${I1401} ${SIM} ${I1401_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +i1620 : ${BIN}i1620${EXE} + +${BIN}i1620${EXE} : ${I1620} ${SIM} + ${MKDIRBIN} + ${CC} ${I1620} ${SIM} ${I1620_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +i7094 : ${BIN}i7094${EXE} + +${BIN}i7094${EXE} : ${I7094} ${SIM} + ${MKDIRBIN} + ${CC} ${I7094} ${SIM} ${I7094_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +ibm1130 : ${BIN}ibm1130${EXE} + +${BIN}ibm1130${EXE} : ${IBM1130} + ${MKDIRBIN} +ifneq ($(WIN32),) + windres ${IBM1130D}/ibm1130.rc $(BIN)ibm1130.o + ${CC} ${IBM1130} ${SIM} ${IBM1130_OPT} $(BIN)ibm1130.o $(CC_OUTSPEC) ${LDFLAGS} + del BIN\ibm1130.o +else + ${CC} ${IBM1130} ${SIM} ${IBM1130_OPT} $(CC_OUTSPEC) ${LDFLAGS} +endif + +s3 : ${BIN}s3${EXE} + +${BIN}s3${EXE} : ${S3} ${SIM} + ${MKDIRBIN} + ${CC} ${S3} ${SIM} ${S3_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +altair : ${BIN}altair${EXE} + +${BIN}altair${EXE} : ${ALTAIR} ${SIM} + ${MKDIRBIN} + ${CC} ${ALTAIR} ${SIM} ${ALTAIR_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +altairz80 : ${BIN}altairz80${EXE} + +${BIN}altairz80${EXE} : ${ALTAIRZ80} ${SIM} + ${MKDIRBIN} + ${CC} ${ALTAIRZ80} ${SIM} ${ALTAIRZ80_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +gri : ${BIN}gri${EXE} + +${BIN}gri${EXE} : ${GRI} ${SIM} + ${MKDIRBIN} + ${CC} ${GRI} ${SIM} ${GRI_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +lgp : ${BIN}lgp${EXE} + +${BIN}lgp${EXE} : ${LGP} ${SIM} + ${MKDIRBIN} + ${CC} ${LGP} ${SIM} ${LGP_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +id16 : ${BIN}id16${EXE} + +${BIN}id16${EXE} : ${ID16} ${SIM} + ${MKDIRBIN} + ${CC} ${ID16} ${SIM} ${ID16_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +id32 : ${BIN}id32${EXE} + +${BIN}id32${EXE} : ${ID32} ${SIM} + ${MKDIRBIN} + ${CC} ${ID32} ${SIM} ${ID32_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +sds : ${BIN}sds${EXE} + +${BIN}sds${EXE} : ${SDS} ${SIM} + ${MKDIRBIN} + ${CC} ${SDS} ${SIM} ${SDS_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +swtp6800mp-a : ${BIN}swtp6800mp-a${EXE} + +${BIN}swtp6800mp-a${EXE} : ${SWTP6800MP-A} ${SIM} ${BUILD_ROMS} + ${MKDIRBIN} + ${CC} ${SWTP6800MP-A} ${SIM} ${SWTP6800_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +swtp6800mp-a2 : ${BIN}swtp6800mp-a2${EXE} + +${BIN}swtp6800mp-a2${EXE} : ${SWTP6800MP-A2} ${SIM} ${BUILD_ROMS} + ${MKDIRBIN} + ${CC} ${SWTP6800MP-A2} ${SIM} ${SWTP6800_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +tx-0 : ${BIN}tx-0${EXE} + +${BIN}tx-0${EXE} : ${TX0} ${SIM} + ${MKDIRBIN} + ${CC} ${TX0} ${SIM} ${TX0_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +ssem : ${BIN}ssem${EXE} + +${BIN}ssem${EXE} : ${SSEM} ${SIM} + ${MKDIRBIN} + ${CC} ${SSEM} ${SIM} ${SSEM_OPT} $(CC_OUTSPEC) ${LDFLAGS} +