├── .gitattributes ├── LICENSE ├── README.md ├── TODO ├── arduino_8080.ino ├── arduino_8080.ino.8080 ├── arduino_8080.ino.z80 ├── bootrom.h ├── config.h ├── disks └── cpm22.dsk ├── doc ├── README-building ├── README-config ├── README-cpm ├── README-hardware ├── nanoboard1.jpg ├── nanoboard2.jpg └── nanoboard3.jpg ├── iosim.h ├── memsim.h ├── roms ├── Makefile ├── bootrom.asm └── bootrom.lis ├── sd-fdc.h ├── simcore.h ├── src-examples ├── Makefile ├── README ├── banner.asm ├── banner.bin ├── banner.lis ├── basicex.bin ├── serial.asm ├── serial.bin ├── serial.lis ├── tb.asm ├── tb.bin ├── tb.lis ├── test8080.asm ├── test8080.bin └── test8080.lis └── srccpm2 ├── Makefile ├── bios.asm ├── bios.bin ├── bios.lis ├── boot.asm ├── boot.bin ├── boot.lis ├── cpm.bin └── putsys.c /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Udo Munk & Thomas Eberhardt 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Intel 8080 CPU emulation running on an Arduino Nano, derived from z80pack 2 | 3 | I managed to get the z80pack 8080 core squeezed into 32KB flash and 2 KB RAM 4 | of the Arduino Nano, I wasn't certain it would fit before trying. This is the 5 | smallest computer system I used so far for z80pack machines. 6 | 7 | All you need is an Arduino Nano board, some PC, an USB cable and the 8 | Arduino IDE for compiling the program und uploading it into the device. The 9 | builtin LED shows when the 8080 emulation stopped running for some reason. 10 | 11 | With 512 bytes RAM for the 8080 we can run very small programs only, but the 12 | first Altair users got their systems with 256 bytes and used it. Included 13 | are some 8080 example programs to show how. 14 | 15 | A video that shows it in action is available: https://youtu.be/wKuIKwkP6O8 16 | 17 | Udo Munk, April 2024 18 | 19 | <><><><><><><><><><><><><><><><><><><><><><><> 20 | 21 | The little 8080 computer looked promising, so in the next step we will 22 | provide more memory. For this the AVR CPU allows to put data in flash 23 | and get it out of the tiny RAM. Now this data needs to be accessed with 24 | functions provided by the runtime, which will cost some performance. 25 | Documentation is a bit spare about this, and others claim it costs a lot 26 | of performance. Our CPU core provides exact run time statistics, so I 27 | just have a look my self. 28 | 29 | It doesn't cost a lot of performance, about 10% slower, but for that we 30 | now got 2KB ROM and 512 Bytes RAM, a very good tradeoff. And with this 31 | "much" memory we now can run some more interesting 8080 programs, like 32 | Li-Chen Wang's famous TINY BASIC. 33 | 34 | Now with knowing this, we even can move more stuff into flash and free up 35 | some more RAM. For example the parity table, not every instruction 36 | computes parity, so that will reduce our performance less than another 37 | 10%, depends on the instructions a program uses. Acceptable and now we 38 | can increase RAM to 1 KB. 39 | Another large array we could move is the op-code jump table, but that 40 | will reduce our speed for every single op-code fetch, for now we leave 41 | this as is. To summarize, now we have: 42 | 43 | Intel 8080 CPU running at ca. 0.8 MHz 44 | UART for serial communication 45 | 2 KB ROM for program code @ 0000H - 07FFH 46 | 1 KB RAM for data @ 0800H - 0BFFH 47 | 48 | And all that on a tiny piece of hardware smaller than a 8080 CPU chip, 49 | leave alone a complete computer system. 50 | 51 | Udo Munk, May 2024 52 | 53 | <><><><><><><><><><><><><><><><><><><><><><><> 54 | 55 | For going further with our litte machine we need to use external memory. 56 | After much reading what is available I decided to use Adafruit FRAM SPI 57 | memory. These are said to be fast, writable like normal RAM without tearing 58 | it down like flash or EEPROM. And it even is persistent memory, which 59 | keeps contents without power. Also Adafruit provides a library to drive 60 | the FRAM via SPI, so we don't have to understand all the low level details 61 | of this. 62 | 63 | Today my modules arrived, soldered in the header for usage on a breadboard 64 | and connected it to the Arduino Nano. Adafruit also provides some example 65 | programs, so let's see if I got everything correct: 66 | 67 | 18:13:51.136 -> FRAM Size = 0x80000 68 | 18:13:51.136 -> Found SPI FRAM 69 | 18:13:52.646 -> SPI FRAM address size is 3 bytes. 70 | 18:13:52.680 -> SPI FRAM capacity appears to be.. 71 | 18:13:52.714 -> 524288 bytes 72 | 18:13:52.714 -> 512 kilobytes 73 | 18:13:52.755 -> 4096 kilobits 74 | 18:13:52.755 -> 4 megabits 75 | 76 | Looks good, and now we have a big 8080 computer system with: 77 | 78 | 8080 CPU running with 0.04 MHz 79 | UART for serial communication 80 | 64 KB RAM 81 | 82 | While the FRAM chips can be clocked up to 40 Mhz, with the Arduino Nano 83 | we are limited to SPI clock frequency 8 MHz, so we have to live with it. 84 | 85 | Now, we still have a copy of our code in flash, so instead of copying 86 | the code into the FRAM, we still can execute it from flash and use 87 | the FRAM only for data. This will give us the following: 88 | 89 | 8080 CPU running with 0.14 MHz 90 | UART for serial communication 91 | 2 KB ROM for program code @ 0000H - 07FFH 92 | 62 KB RAM for data @ 0800H - FFFFH 93 | 94 | Some final thoughts: 95 | 96 | I got a 512 MB FRAM module, just because one cannot have enough memory, right? 97 | Wrong, because for accessing a byte in this memory we must send it 3 bytes with 98 | the address. For a 64 KB module 2 byte addresses should improve performance by 99 | reducing the overhead. If someone is going to try this please tell us what you 100 | get. 101 | 102 | Udo Munk, May 2024 103 | 104 | <><><><><><><><><><><><><><><><><><><><><><><> 105 | 106 | Got some more stuff delivered today and after some soldering and wiring: 107 | 108 | Initializing SD card...Wiring is correct and a card is present. 109 | 110 | Card type: SDHC 111 | Clusters: 473504 112 | Blocks x Cluster: 64 113 | Total Blocks: 30304256 114 | 115 | Volume type is: FAT32 116 | Volume size (Kb): 15152128 117 | Volume size (Mb): 14797 118 | Volume size (Gb): 14.45 119 | 120 | Files found on the card (name, date and size in bytes): 121 | SYSTEM~1/ 2024-05-14 15:02:00 122 | WPSETT~1.DAT 2024-05-14 15:02:00 12 123 | INDEXE~1 2024-05-14 15:02:00 76 124 | DATEI1.TXT 2024-05-14 15:02:42 24 125 | DATEI2.TXT 2024-05-14 15:03:56 61 126 | 127 | Excellent, then let's see how to make use of this ... 128 | After adding the SD library and initializing the device we have: 129 | 130 | Sketch uses 30028 bytes (97%) of program storage space. Maximum is 30720 bytes. 131 | Global variables use 1555 bytes (75%) of dynamic memory, leaving 493 bytes for local variables. Maximum is 2048 bytes. 132 | 133 | Now it will get interesting, guess I'll need a while ;-) 134 | Actually it is even worse than I thought already, just from trying to open 135 | one file on the SD card: 136 | 137 | Sketch uses 32198 bytes (104%) of program storage space. Maximum is 30720 bytes. 138 | Global variables use 1581 bytes (77%) of dynamic memory, leaving 467 bytes for local variables. Maximum is 2048 bytes. 139 | Compilation error: text section exceeds available space in board 140 | 141 | Udo Munk, May 2024 142 | 143 | <><><><><><><><><><><><><><><><><><><><><><><> 144 | 145 | In this version I have replaced the CPU implementation with a code size 146 | optimized one from Thomas Eberhardt, everything else is the same as before. 147 | Hopefully this will free enough memory to make use of the SD drive now. 148 | 149 | Success, we do not compile any 8080 code into the application anymore, 150 | instead we read our code from a MicroSD drive, and we are not limited 151 | to 4 KB code size. However, free memory left is very very little, so 152 | this machine will always read /code80.bin from the MicroSD and then 153 | run the 8080 with whatever stuff you put in there. 154 | Anyway, we have the following machine now: 155 | 156 | 8080 CPU running with 0.04 MHz 157 | MITS Altair 88-SIO Rev. 1 for serial communication 158 | RAM @ 0000H - FEFFH 159 | ROM @ FF00H - FFFFH 160 | 32 GB floppy disk drive 161 | 162 | Udo Munk, May 2024 163 | 164 | <><><><><><><><><><><><><><><><><><><><><><><> 165 | 166 | Now that we have tweaked our CPU, the other component we can look at 167 | for code optimizations is the SD library. While this library is great, 168 | it includes lot's of stuff we don't need here, all we want is reading 169 | files with good old 8.3 filenames. 170 | 171 | Please install the following library in your Arduino IDE: 172 | 173 | SdFat - Adafruit Fork 174 | 175 | This one can be better customisized for our needs here, and with doing 176 | this we save a few KB code, that we can use to improve our application 177 | now :-) 178 | 179 | For example now we can implement a dialog that lets us enter the filename, 180 | without the need to copy it to a fixed name. On your MicroSD card create 181 | a directory CODE80 and copy all your 8080 binaries into this directory. 182 | Filename to enter is just the name, without extension, which must be .bin. 183 | 184 | Udo Munk, May 2024 185 | 186 | <><><><><><><><><><><><><><><><><><><><><><><> 187 | 188 | Now that we can load any code from the huge disk drive into the 8080 189 | computer system we have a problem, when the software isn't working, 190 | runs away and the whole machine seems to be stuck. 191 | For this I added an interrupt key, which will stop the 8080 from 192 | whatever it is doing. And then a register dump also is printed 193 | to the terminal, so that we might get an idea what it was doing. 194 | 195 | Udo Munk, May 2024 196 | 197 | <><><><><><><><><><><><><><><><><><><><><><><> 198 | 199 | 8080-SIM v1.6 200 | 201 | 1 - port 255 value: 0x22 202 | 2 - load file 203 | 3 - Disk 0: 204 | 4 - Disk 1: 205 | 5 - run machine 206 | 207 | Command: 3 208 | 209 | Filename: cpm22 210 | 1 - port 255 value: 0x22 211 | 2 - load file 212 | 3 - Disk 0: /DISKS80/cpm22.DSK 213 | 4 - Disk 1: 214 | 5 - run machine 215 | 216 | Command: 5 217 | 218 | 64K CP/M 2.2 VERS B01 219 | 220 | A>dir 221 | A: ASM COM : DDT COM : ED COM : MOVCPM COM 222 | A: DUMP COM : LOAD COM : PIP COM : STAT COM 223 | A: SYSGEN COM : XSUB COM : WM HLP : WM COM 224 | A: BYE COM : CLS COM : SURVEY MAC : SURVEY COM 225 | A: XDIR COM : SDIR COM : VIEW COM : SID COM 226 | A: MAC COM : MEMMAP COM : SUBMIT COM 227 | A> 228 | 229 | Long live the Intel 8080! 230 | 231 | Long live Digital Research CP/M! 232 | 233 | -------------------------------------- 234 | 235 | No one is going to believe this, but see your self ... 236 | Thomas just released a code size optimized Z80 CPU implementation, 237 | that still fits into the 32 KB flash memory of the Arduino Nano. 238 | This Z80 is tested on UNIX workstations inside the z80pack framework 239 | with code exercisers, to make sure it works the same as Zilog/Mostek 240 | silicon with regards to the documented behaviour. 241 | 242 | There are now two project files: 243 | 244 | arduino_8080.ino.8080 245 | arduino_8080.ino.z80 246 | 247 | and arduino_8080.ino is a link to one of them. Currently the 248 | project is setup to use the new Z80 CPU, but please be aware 249 | that Microsoft BASIC only runs on a 8080 CPU, will get stuck 250 | on a Z80. 251 | 252 | Of course now the comfort features from my TODO list won't fit 253 | anymore, but one can't have it all I guess. 254 | 255 | Long live the Zilog Z80 ! 256 | 257 | With this release we have implemented a real computer system 258 | with the following components: 259 | 260 | 8080 or Z80 CPU running with 0.04 MHz 261 | MITS Altair 88-SIO Rev. 1 for serial communication 262 | RAM @ 0000H - FEFFH 263 | ROM @ FF00H - FFFFH 264 | Floppy disk controller with two 8" floppy disk drives 265 | Huge 32 GB MicroSD drive, that stores floppy disk images, 266 | standalone programs and the system configuration 267 | 268 | This computer system certainly will not win any speed award, but it should 269 | receive some coolness award, because with a few tiny microelectronic circuits 270 | it substitutes a complete S100 mainframe from ca. 1980. 271 | 272 | And here a video that shows this system in action: https://youtu.be/DQ6wM0TVROI 273 | 274 | Udo Munk, May 2024 275 | 276 | <><><><><><><><><><><><><><><><><><><><><><><> 277 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Note to my self: 2 | 3 | when enough memory left in the end dump memory in config dialog 4 | when enough memory left in the end list files on the SD card 5 | -------------------------------------------------------------------------------- /arduino_8080.ino: -------------------------------------------------------------------------------- 1 | arduino_8080.ino.z80 -------------------------------------------------------------------------------- /arduino_8080.ino.8080: -------------------------------------------------------------------------------- 1 | // 2 | // Intel 8080 CPU emulation on an Arduino Nano 3 | // derived from Z80PACK 4 | // Copyright 2024, Udo Munk & Thomas Eberhardt 5 | // 6 | // History: 7 | // 04-MAY-2024 Release 1.0 implements a very basic 8080 system 8 | // 06-MAY-2024 Release 1.1 add support for a ROM in flash 9 | // 07-MAY-2024 Release 1.2 move 8080 memory into a FRAM 10 | // 13-MAY-2024 Release 1.2.1 can run MITS Altair BASIC 11 | // 17-MAY-2024 Release 1.3 switch to code minimized CPU from Thomas Eberhardt 12 | // 18-MAY-2024 Release 1.4 read 8080 code from a file on SD into FRAM 13 | // 20-MAY-2024 Release 1.5.1 added interrupt key and register dump for debugging 14 | // 22-MAY-2024 Release 1.6 boot from a disk image on SD 15 | // 13-JUN-2024 Release 1.6.1 power on jump into boot ROM 16 | // 17 | 18 | //#define FRAM_DEBUG 1 // enables FRAM debug messages 19 | //#define DEBUG // enables some application debug messages 20 | 21 | #include 22 | #include 23 | #include "Adafruit_FRAM_SPI.h" 24 | #include "SdFat.h" 25 | 26 | // unused analog pin to seed random generator 27 | #define UAP 7 28 | // chip select pin for FRAM (default) 29 | #define FRAM_CS 10 30 | // chip select pin for SD card 31 | #define SD_CS 9 32 | // interrupt pin, interrupt 0 is already used by the SPI stuff 33 | // so we use interrupt 1 at the D3 pin 34 | #define USERINT 3 35 | 36 | // data types for the 8080 CPU 37 | typedef uint8_t BYTE; 38 | typedef uint16_t WORD; 39 | 40 | // project includes 41 | #include "simcore.h" 42 | #include "memsim.h" 43 | #include "sd-fdc.h" 44 | #include "iosim.h" 45 | #include "config.h" 46 | 47 | #if _BYTE_ORDER == _LITTLE_ENDIAN 48 | struct cpu_reg { 49 | union { 50 | struct { 51 | BYTE l; 52 | BYTE h; 53 | }; 54 | WORD w; 55 | }; 56 | }; 57 | #elif _BYTE_ORDER == _BIG_ENDIAN 58 | struct cpu_reg { 59 | union { 60 | struct { 61 | BYTE h; 62 | BYTE l; 63 | }; 64 | WORD w; 65 | }; 66 | }; 67 | #else 68 | #error "Unsupported byte order" 69 | #endif 70 | 71 | // 8080 CPU state 72 | struct cpu_state { 73 | struct cpu_reg af; /* primary registers */ 74 | struct cpu_reg bc; 75 | struct cpu_reg de; 76 | struct cpu_reg hl; 77 | struct cpu_reg sp; /* stack pointer */ 78 | struct cpu_reg pc; /* program counter */ 79 | BYTE iff; /* interupt flags */ 80 | } cpu_state; 81 | 82 | // other global variables 83 | CPUState State = Running; // CPU state 84 | unsigned long tstates = 0; // executed T-states 85 | 86 | // Insure FAT16/FAT32 only 87 | SdFat32 SD; 88 | 89 | // Precomputed table for fast sign, zero and parity flag calculation 90 | #define _ 0 91 | #define S S_FLAG 92 | #define Z Z_FLAG 93 | #define P P_FLAG 94 | static const BYTE szp_flags[256] = { 95 | /*00*/ Z|P, _, _, P, _, P, P, _, 96 | /*08*/ _, P, P, _, P, _, _, P, 97 | /*10*/ _, P, P, _, P, _, _, P, 98 | /*18*/ P, _, _, P, _, P, P, _, 99 | /*20*/ _, P, P, _, P, _, _, P, 100 | /*28*/ P, _, _, P, _, P, P, _, 101 | /*30*/ P, _, _, P, _, P, P, _, 102 | /*38*/ _, P, P, _, P, _, _, P, 103 | /*40*/ _, P, P, _, P, _, _, P, 104 | /*48*/ P, _, _, P, _, P, P, _, 105 | /*50*/ P, _, _, P, _, P, P, _, 106 | /*58*/ _, P, P, _, P, _, _, P, 107 | /*60*/ P, _, _, P, _, P, P, _, 108 | /*68*/ _, P, P, _, P, _, _, P, 109 | /*70*/ _, P, P, _, P, _, _, P, 110 | /*78*/ P, _, _, P, _, P, P, _, 111 | /*80*/ S, S|P, S|P, S, S|P, S, S, S|P, 112 | /*88*/ S|P, S, S, S|P, S, S|P, S|P, S, 113 | /*90*/ S|P, S, S, S|P, S, S|P, S|P, S, 114 | /*98*/ S, S|P, S|P, S, S|P, S, S, S|P, 115 | /*a0*/ S|P, S, S, S|P, S, S|P, S|P, S, 116 | /*a8*/ S, S|P, S|P, S, S|P, S, S, S|P, 117 | /*b0*/ S, S|P, S|P, S, S|P, S, S, S|P, 118 | /*b8*/ S|P, S, S, S|P, S, S|P, S|P, S, 119 | /*c0*/ S|P, S, S, S|P, S, S|P, S|P, S, 120 | /*c8*/ S, S|P, S|P, S, S|P, S, S, S|P, 121 | /*d0*/ S, S|P, S|P, S, S|P, S, S, S|P, 122 | /*d8*/ S|P, S, S, S|P, S, S|P, S|P, S, 123 | /*e0*/ S, S|P, S|P, S, S|P, S, S, S|P, 124 | /*e8*/ S|P, S, S, S|P, S, S|P, S|P, S, 125 | /*f0*/ S|P, S, S, S|P, S, S|P, S|P, S, 126 | /*f8*/ S, S|P, S|P, S, S|P, S, S, S|P 127 | }; 128 | #undef _ 129 | #undef S 130 | #undef Z 131 | #undef P 132 | 133 | // This function initializes the CPU into the documented 134 | // state. Prevents assumptions about register contents 135 | // of a just powered CPU, like HL is always 0 in the 136 | // beginning, which is not the case with the silicon. 137 | static void init_cpu(struct cpu_state *cpu_state) 138 | { 139 | cpu_state->pc.w = 0xff00; // normally 0, power on jump to boot ROM 140 | cpu_state->sp.w = random(65535); 141 | cpu_state->af.w = random(65535); 142 | cpu_state->bc.w = random(65535); 143 | cpu_state->de.w = random(65535); 144 | cpu_state->hl.w = random(65535); 145 | } 146 | 147 | // This function builds the 8080 central processing unit. 148 | // The opcode where PC points to is fetched from the memory 149 | // and PC incremented by one. The opcode is then dispatched 150 | // to execute code, which emulates this 8080 opcode. 151 | // 152 | // For a description of how the arithmetic flags calculation works see: 153 | // http://emulators.com/docs/lazyoverflowdetect_final.pdf 154 | // 155 | // The formula for subtraction carry outs was determined by staring 156 | // intently at some truth tables. 157 | // 158 | // cout contains the carry outs for every bit 159 | 160 | void cpu_8080(struct cpu_state *cpu_state) 161 | { 162 | struct cpu_state cpu_regs; 163 | BYTE t, res, cout, P; 164 | 165 | struct cpu_reg w; /* working register */ 166 | 167 | #pragma push_macro("F") 168 | #undef F 169 | #pragma push_macro("SP") 170 | #undef SP 171 | #pragma push_macro("SPH") 172 | #undef SPH 173 | #pragma push_macro("SPL") 174 | #undef SPL 175 | #pragma push_macro("PC") 176 | #undef PC 177 | 178 | #define AF cpu_regs.af.w 179 | #define A cpu_regs.af.h 180 | #define F cpu_regs.af.l 181 | #define BC cpu_regs.bc.w 182 | #define B cpu_regs.bc.h 183 | #define C cpu_regs.bc.l 184 | #define DE cpu_regs.de.w 185 | #define D cpu_regs.de.h 186 | #define E cpu_regs.de.l 187 | #define HL cpu_regs.hl.w 188 | #define H cpu_regs.hl.h 189 | #define L cpu_regs.hl.l 190 | #define SP cpu_regs.sp.w 191 | #define SPH cpu_regs.sp.h 192 | #define SPL cpu_regs.sp.l 193 | #define PC cpu_regs.pc.w 194 | #define PCH cpu_regs.pc.h 195 | #define PCL cpu_regs.pc.l 196 | #define IFF cpu_regs.iff 197 | 198 | #define W w.w 199 | #define WH w.h 200 | #define WL w.l 201 | 202 | cpu_regs = *cpu_state; 203 | 204 | do { 205 | t = 4; /* minimum clock cycles for M1 */ 206 | 207 | switch (memrdr(PC++)) { /* execute next opcode */ 208 | 209 | case 0x00: /* NOP */ 210 | case 0x08: /* NOP* */ 211 | case 0x10: /* NOP* */ 212 | case 0x18: /* NOP* */ 213 | case 0x20: /* NOP* */ 214 | case 0x28: /* NOP* */ 215 | case 0x30: /* NOP* */ 216 | case 0x38: /* NOP* */ 217 | break; 218 | 219 | case 0x40: /* MOV B,B */ 220 | case 0x49: /* MOV C,C */ 221 | case 0x52: /* MOV D,D */ 222 | case 0x5b: /* MOV E,E */ 223 | case 0x64: /* MOV H,H */ 224 | case 0x6d: /* MOV L,L */ 225 | case 0x7f: /* MOV A,A */ 226 | t++; 227 | break; 228 | 229 | case 0x01: /* LXI B,nn */ 230 | C = memrdr(PC++); 231 | B = memrdr(PC++); 232 | t += 6; 233 | break; 234 | 235 | case 0x02: /* STAX B */ 236 | memwrt(BC, A); 237 | t += 3; 238 | break; 239 | 240 | case 0x03: /* INX B */ 241 | BC++; 242 | t++; 243 | break; 244 | 245 | case 0x04: /* INR B */ 246 | P = B; 247 | res = ++B; 248 | finish_inr: 249 | cout = (P & 1) | ((P | 1) & ~res); 250 | F = ((F & C_FLAG) | 251 | (((cout >> 3) & 1) << H_SHIFT) | 252 | szp_flags[res]); 253 | /* C_FLAG unchanged */ 254 | t++; 255 | break; 256 | 257 | case 0x05: /* DCR B */ 258 | P = B; 259 | res = --B; 260 | finish_dcr: 261 | cout = (~P & 1) | ((~P | 1) & res); 262 | F = ((F & C_FLAG) | 263 | (((cout >> 3) & 1) << H_SHIFT) | 264 | szp_flags[res]); 265 | F ^= H_FLAG; 266 | /* C_FLAG unchanged */ 267 | t++; 268 | break; 269 | 270 | case 0x06: /* MVI B,n */ 271 | B = memrdr(PC++); 272 | t += 3; 273 | break; 274 | 275 | case 0x07: /* RLC */ 276 | res = ((A & 0x80) >> 7) & 1; 277 | F = (F & ~C_FLAG) | (res << C_SHIFT); 278 | /* S_FLAG, Z_FLAG, H_FLAG, and P_FLAG unchanged */ 279 | A = (A << 1) | res; 280 | break; 281 | 282 | case 0x09: /* DAD B */ 283 | W = HL + BC; 284 | cout = (H & B) | ((H | B) & ~WH); 285 | finish_dad: 286 | F = (F & ~C_FLAG) | (((cout >> 7) & 1) << C_SHIFT); 287 | /* S_FLAG, Z_FLAG, H_FLAG, and P_FLAG unchanged */ 288 | H = WH; 289 | L = WL; 290 | t += 6; 291 | break; 292 | 293 | case 0x0a: /* LDAX B */ 294 | A = memrdr(BC); 295 | t += 3; 296 | break; 297 | 298 | case 0x0b: /* DCX B */ 299 | BC--; 300 | t++; 301 | break; 302 | 303 | case 0x0c: /* INR C */ 304 | P = C; 305 | res = ++C; 306 | goto finish_inr; 307 | 308 | case 0x0d: /* DCR C */ 309 | P = C; 310 | res = --C; 311 | goto finish_dcr; 312 | 313 | case 0x0e: /* MVI C,n */ 314 | C = memrdr(PC++); 315 | t += 3; 316 | break; 317 | 318 | case 0x0f: /* RRC */ 319 | res = A & 1; 320 | F = (F & ~C_FLAG) | (res << C_SHIFT); 321 | /* S_FLAG, Z_FLAG, H_FLAG, and P_FLAG unchanged */ 322 | A = (A >> 1) | (res << 7); 323 | break; 324 | 325 | case 0x11: /* LXI D,nn */ 326 | E = memrdr(PC++); 327 | D = memrdr(PC++); 328 | t += 6; 329 | break; 330 | 331 | case 0x12: /* STAX D */ 332 | memwrt(DE, A); 333 | t += 3; 334 | break; 335 | 336 | case 0x13: /* INX D */ 337 | DE++; 338 | t++; 339 | break; 340 | 341 | case 0x14: /* INR D */ 342 | P = D; 343 | res = ++D; 344 | goto finish_inr; 345 | 346 | case 0x15: /* DCR D */ 347 | P = D; 348 | res = --D; 349 | goto finish_dcr; 350 | 351 | case 0x16: /* MVI D,n */ 352 | D = memrdr(PC++); 353 | t += 3; 354 | break; 355 | 356 | case 0x17: /* RAL */ 357 | res = (F >> C_SHIFT) & 1; 358 | F = (F & ~C_FLAG) | ((((A & 0x80) >> 7) & 1) << C_SHIFT); 359 | /* S_FLAG, Z_FLAG, H_FLAG, and P_FLAG unchanged */ 360 | A = (A << 1) | res; 361 | break; 362 | 363 | case 0x19: /* DAD D */ 364 | W = HL + DE; 365 | cout = (H & D) | ((H | D) & ~WH); 366 | goto finish_dad; 367 | 368 | case 0x1a: /* LDAX D */ 369 | A = memrdr(DE); 370 | t += 3; 371 | break; 372 | 373 | case 0x1b: /* DCX D */ 374 | DE--; 375 | t++; 376 | break; 377 | 378 | case 0x1c: /* INR E */ 379 | P = E; 380 | res = ++E; 381 | goto finish_inr; 382 | 383 | case 0x1d: /* DCR E */ 384 | P = E; 385 | res = --E; 386 | goto finish_dcr; 387 | 388 | case 0x1e: /* MVI E,n */ 389 | E = memrdr(PC++); 390 | t += 3; 391 | break; 392 | 393 | case 0x1f: /* RAR */ 394 | res = (F >> C_SHIFT) & 1; 395 | F = (F & ~C_FLAG) | ((A & 1) << C_SHIFT); 396 | /* S_FLAG, Z_FLAG, H_FLAG, and P_FLAG unchanged */ 397 | A = (A >> 1) | (res << 7); 398 | break; 399 | 400 | case 0x21: /* LXI H,nn */ 401 | L = memrdr(PC++); 402 | H = memrdr(PC++); 403 | t += 6; 404 | break; 405 | 406 | case 0x22: /* SHLD nn */ 407 | WL = memrdr(PC++); 408 | WH = memrdr(PC++); 409 | memwrt(W, L); 410 | memwrt(W + 1, H); 411 | t += 12; 412 | break; 413 | 414 | case 0x23: /* INX H */ 415 | HL++; 416 | t++; 417 | break; 418 | 419 | case 0x24: /* INR H */ 420 | P = H; 421 | res = ++H; 422 | goto finish_inr; 423 | 424 | case 0x25: /* DCR H */ 425 | P = H; 426 | res = --H; 427 | goto finish_dcr; 428 | 429 | case 0x26: /* MVI H,n */ 430 | H = memrdr(PC++); 431 | t += 3; 432 | break; 433 | 434 | case 0x27: /* DAA */ 435 | P = 0; 436 | if (((A & 0xf) > 9) || (F & H_FLAG)) 437 | P |= 0x06; 438 | if ((A > 0x99) || (F & C_FLAG)) { 439 | F |= C_FLAG; 440 | P |= 0x60; 441 | } 442 | res = A + P; 443 | cout = (A & P) | ((A | P) & ~res); 444 | F = ((F & C_FLAG) | 445 | (((cout >> 3) & 1) << H_SHIFT) | 446 | szp_flags[res]); 447 | A = res; 448 | break; 449 | 450 | case 0x29: /* DAD H */ 451 | W = HL << 1; 452 | cout = H | (H & ~WH); 453 | goto finish_dad; 454 | 455 | case 0x2a: /* LHLD nn */ 456 | WL = memrdr(PC++); 457 | WH = memrdr(PC++); 458 | L = memrdr(W); 459 | H = memrdr(W + 1); 460 | t += 12; 461 | break; 462 | 463 | case 0x2b: /* DCX H */ 464 | HL--; 465 | t++; 466 | break; 467 | 468 | case 0x2c: /* INR L */ 469 | P = L; 470 | res = ++L; 471 | goto finish_inr; 472 | 473 | case 0x2d: /* DCR L */ 474 | P = L; 475 | res = --L; 476 | goto finish_dcr; 477 | 478 | case 0x2e: /* MVI L,n */ 479 | L = memrdr(PC++); 480 | t += 3; 481 | break; 482 | 483 | case 0x2f: /* CMA */ 484 | A = ~A; 485 | break; 486 | 487 | case 0x31: /* LXI SP,nn */ 488 | SPL = memrdr(PC++); 489 | SPH = memrdr(PC++); 490 | t += 6; 491 | break; 492 | 493 | case 0x32: /* STA nn */ 494 | WL = memrdr(PC++); 495 | WH = memrdr(PC++); 496 | memwrt(W, A); 497 | t += 9; 498 | break; 499 | 500 | case 0x33: /* INX SP */ 501 | SP++; 502 | t++; 503 | break; 504 | 505 | case 0x34: /* INR M */ 506 | P = memrdr(HL); 507 | res = P + 1; 508 | memwrt(HL, res); 509 | t += 5; 510 | goto finish_inr; 511 | 512 | case 0x35: /* DCR M */ 513 | P = memrdr(HL); 514 | res = P - 1; 515 | memwrt(HL, res); 516 | t += 5; 517 | goto finish_dcr; 518 | 519 | case 0x36: /* MVI M,n */ 520 | memwrt(HL, memrdr(PC++)); 521 | t += 6; 522 | break; 523 | 524 | case 0x37: /* STC */ 525 | F |= C_FLAG; 526 | /* S_FLAG, Z_FLAG, H_FLAG, and P_FLAG unchanged */ 527 | break; 528 | 529 | case 0x39: /* DAD SP */ 530 | W = HL + SP; 531 | cout = (H & SPH) | ((H | SPH) & ~WH); 532 | goto finish_dad; 533 | 534 | case 0x3a: /* LDA nn */ 535 | WL = memrdr(PC++); 536 | WH = memrdr(PC++); 537 | A = memrdr(W); 538 | t += 9; 539 | break; 540 | 541 | case 0x3b: /* DCX SP */ 542 | SP--; 543 | t++; 544 | break; 545 | 546 | case 0x3c: /* INR A */ 547 | P = A; 548 | res = ++A; 549 | goto finish_inr; 550 | 551 | case 0x3d: /* DCR A */ 552 | P = A; 553 | res = --A; 554 | goto finish_dcr; 555 | 556 | case 0x3e: /* MVI A,n */ 557 | A = memrdr(PC++); 558 | t += 3; 559 | break; 560 | 561 | case 0x3f: /* CMC */ 562 | F ^= C_FLAG; 563 | /* S_FLAG, Z_FLAG, H_FLAG, and P_FLAG unchanged */ 564 | break; 565 | 566 | case 0x41: /* MOV B,C */ 567 | B = C; 568 | t++; 569 | break; 570 | 571 | case 0x42: /* MOV B,D */ 572 | B = D; 573 | t++; 574 | break; 575 | 576 | case 0x43: /* MOV B,E */ 577 | B = E; 578 | t++; 579 | break; 580 | 581 | case 0x44: /* MOV B,H */ 582 | B = H; 583 | t++; 584 | break; 585 | 586 | case 0x45: /* MOV B,L */ 587 | B = L; 588 | t++; 589 | break; 590 | 591 | case 0x46: /* MOV B,M */ 592 | B = memrdr(HL); 593 | t += 3; 594 | break; 595 | 596 | case 0x47: /* MOV B,A */ 597 | B = A; 598 | t++; 599 | break; 600 | 601 | case 0x48: /* MOV C,B */ 602 | C = B; 603 | t++; 604 | break; 605 | 606 | case 0x4a: /* MOV C,D */ 607 | C = D; 608 | t++; 609 | break; 610 | 611 | case 0x4b: /* MOV C,E */ 612 | C = E; 613 | t++; 614 | break; 615 | 616 | case 0x4c: /* MOV C,H */ 617 | C = H; 618 | t++; 619 | break; 620 | 621 | case 0x4d: /* MOV C,L */ 622 | C = L; 623 | t++; 624 | break; 625 | 626 | case 0x4e: /* MOV C,M */ 627 | C = memrdr(HL); 628 | t += 3; 629 | break; 630 | 631 | case 0x4f: /* MOV C,A */ 632 | C = A; 633 | t++; 634 | break; 635 | 636 | case 0x50: /* MOV D,B */ 637 | D = B; 638 | t++; 639 | break; 640 | 641 | case 0x51: /* MOV D,C */ 642 | D = C; 643 | t++; 644 | break; 645 | 646 | case 0x53: /* MOV D,E */ 647 | D = E; 648 | t++; 649 | break; 650 | 651 | case 0x54: /* MOV D,H */ 652 | D = H; 653 | t++; 654 | break; 655 | 656 | case 0x55: /* MOV D,L */ 657 | D = L; 658 | t++; 659 | break; 660 | 661 | case 0x56: /* MOV D,M */ 662 | D = memrdr(HL); 663 | t += 3; 664 | break; 665 | 666 | case 0x57: /* MOV D,A */ 667 | D = A; 668 | t++; 669 | break; 670 | 671 | case 0x58: /* MOV E,B */ 672 | E = B; 673 | t++; 674 | break; 675 | 676 | case 0x59: /* MOV E,C */ 677 | E = C; 678 | t++; 679 | break; 680 | 681 | case 0x5a: /* MOV E,D */ 682 | E = D; 683 | t++; 684 | break; 685 | 686 | case 0x5c: /* MOV E,H */ 687 | E = H; 688 | t++; 689 | break; 690 | 691 | case 0x5d: /* MOV E,L */ 692 | E = L; 693 | t++; 694 | break; 695 | 696 | case 0x5e: /* MOV E,M */ 697 | E = memrdr(HL); 698 | t += 3; 699 | break; 700 | 701 | case 0x5f: /* MOV E,A */ 702 | E = A; 703 | t++; 704 | break; 705 | 706 | case 0x60: /* MOV H,B */ 707 | H = B; 708 | t++; 709 | break; 710 | 711 | case 0x61: /* MOV H,C */ 712 | H = C; 713 | t++; 714 | break; 715 | 716 | case 0x62: /* MOV H,D */ 717 | H = D; 718 | t++; 719 | break; 720 | 721 | case 0x63: /* MOV H,E */ 722 | H = E; 723 | t++; 724 | break; 725 | 726 | case 0x65: /* MOV H,L */ 727 | H = L; 728 | t++; 729 | break; 730 | 731 | case 0x66: /* MOV H,M */ 732 | H = memrdr(HL); 733 | t += 3; 734 | break; 735 | 736 | case 0x67: /* MOV H,A */ 737 | H = A; 738 | t++; 739 | break; 740 | 741 | case 0x68: /* MOV L,B */ 742 | L = B; 743 | t++; 744 | break; 745 | 746 | case 0x69: /* MOV L,C */ 747 | L = C; 748 | t++; 749 | break; 750 | 751 | case 0x6a: /* MOV L,D */ 752 | L = D; 753 | t++; 754 | break; 755 | 756 | case 0x6b: /* MOV L,E */ 757 | L = E; 758 | t++; 759 | break; 760 | 761 | case 0x6c: /* MOV L,H */ 762 | L = H; 763 | t++; 764 | break; 765 | 766 | case 0x6e: /* MOV L,M */ 767 | L = memrdr(HL); 768 | t += 3; 769 | break; 770 | 771 | case 0x6f: /* MOV L,A */ 772 | L = A; 773 | t++; 774 | break; 775 | 776 | case 0x70: /* MOV M,B */ 777 | memwrt(HL, B); 778 | t += 3; 779 | break; 780 | 781 | case 0x71: /* MOV M,C */ 782 | memwrt(HL, C); 783 | t += 3; 784 | break; 785 | 786 | case 0x72: /* MOV M,D */ 787 | memwrt(HL, D); 788 | t += 3; 789 | break; 790 | 791 | case 0x73: /* MOV M,E */ 792 | memwrt(HL, E); 793 | t += 3; 794 | break; 795 | 796 | case 0x74: /* MOV M,H */ 797 | memwrt(HL, H); 798 | t += 3; 799 | break; 800 | 801 | case 0x75: /* MOV M,L */ 802 | memwrt(HL, L); 803 | t += 3; 804 | break; 805 | 806 | case 0x76: /* HLT */ 807 | // cry and die, no interrupts yet 808 | State = Halted; 809 | t += 3; 810 | break; 811 | 812 | case 0x77: /* MOV M,A */ 813 | memwrt(HL, A); 814 | t += 3; 815 | break; 816 | 817 | case 0x78: /* MOV A,B */ 818 | A = B; 819 | t++; 820 | break; 821 | 822 | case 0x79: /* MOV A,C */ 823 | A = C; 824 | t++; 825 | break; 826 | 827 | case 0x7a: /* MOV A,D */ 828 | A = D; 829 | t++; 830 | break; 831 | 832 | case 0x7b: /* MOV A,E */ 833 | A = E; 834 | t++; 835 | break; 836 | 837 | case 0x7c: /* MOV A,H */ 838 | A = H; 839 | t++; 840 | break; 841 | 842 | case 0x7d: /* MOV A,L */ 843 | A = L; 844 | t++; 845 | break; 846 | 847 | case 0x7e: /* MOV A,M */ 848 | A = memrdr(HL); 849 | t += 3; 850 | break; 851 | 852 | case 0x80: /* ADD B */ 853 | P = B; 854 | res = 0; 855 | finish_add: 856 | res = A + P + res; 857 | cout = (A & P) | ((A | P) & ~res); 858 | F = ((((cout >> 7) & 1) << C_SHIFT) | 859 | (((cout >> 3) & 1) << H_SHIFT) | 860 | szp_flags[res]); 861 | A = res; 862 | break; 863 | 864 | case 0x81: /* ADD C */ 865 | P = C; 866 | res = 0; 867 | goto finish_add; 868 | 869 | case 0x82: /* ADD D */ 870 | P = D; 871 | res = 0; 872 | goto finish_add; 873 | 874 | case 0x83: /* ADD E */ 875 | P = E; 876 | res = 0; 877 | goto finish_add; 878 | 879 | case 0x84: /* ADD H */ 880 | P = H; 881 | res = 0; 882 | goto finish_add; 883 | 884 | case 0x85: /* ADD L */ 885 | P = L; 886 | res = 0; 887 | goto finish_add; 888 | 889 | case 0x86: /* ADD M */ 890 | P = memrdr(HL); 891 | res = 0; 892 | t += 3; 893 | goto finish_add; 894 | 895 | case 0x87: /* ADD A */ 896 | P = A; 897 | res = 0; 898 | goto finish_add; 899 | 900 | case 0x88: /* ADC B */ 901 | P = B; 902 | res = (F >> C_SHIFT) & 1; 903 | goto finish_add; 904 | 905 | case 0x89: /* ADC C */ 906 | P = C; 907 | res = (F >> C_SHIFT) & 1; 908 | goto finish_add; 909 | 910 | case 0x8a: /* ADC D */ 911 | P = D; 912 | res = (F >> C_SHIFT) & 1; 913 | goto finish_add; 914 | 915 | case 0x8b: /* ADC E */ 916 | P = E; 917 | res = (F >> C_SHIFT) & 1; 918 | goto finish_add; 919 | 920 | case 0x8c: /* ADC H */ 921 | P = H; 922 | res = (F >> C_SHIFT) & 1; 923 | goto finish_add; 924 | 925 | case 0x8d: /* ADC L */ 926 | P = L; 927 | res = (F >> C_SHIFT) & 1; 928 | goto finish_add; 929 | 930 | case 0x8e: /* ADC M */ 931 | P = memrdr(HL); 932 | res = (F >> C_SHIFT) & 1; 933 | t += 3; 934 | goto finish_add; 935 | 936 | case 0x8f: /* ADC A */ 937 | P = A; 938 | res = (F >> C_SHIFT) & 1; 939 | goto finish_add; 940 | 941 | case 0x90: /* SUB B */ 942 | P = B; 943 | res = 0; 944 | finish_sub: 945 | res = A - P - res; 946 | cout = (~A & P) | ((~A | P) & res); 947 | F = ((((cout >> 7) & 1) << C_SHIFT) | 948 | (((cout >> 3) & 1) << H_SHIFT) | 949 | szp_flags[res]); 950 | F ^= H_FLAG; 951 | A = res; 952 | break; 953 | 954 | case 0x91: /* SUB C */ 955 | P = C; 956 | res = 0; 957 | goto finish_sub; 958 | 959 | case 0x92: /* SUB D */ 960 | P = D; 961 | res = 0; 962 | goto finish_sub; 963 | 964 | case 0x93: /* SUB E */ 965 | P = E; 966 | res = 0; 967 | goto finish_sub; 968 | 969 | case 0x94: /* SUB H */ 970 | P = H; 971 | res = 0; 972 | goto finish_sub; 973 | 974 | case 0x95: /* SUB L */ 975 | P = L; 976 | res = 0; 977 | goto finish_sub; 978 | 979 | case 0x96: /* SUB M */ 980 | P = memrdr(HL); 981 | res = 0; 982 | t += 3; 983 | goto finish_sub; 984 | 985 | case 0x97: /* SUB A */ 986 | F = Z_FLAG | H_FLAG | P_FLAG; 987 | /* S_FLAG and C_FLAG cleared */ 988 | A = 0; 989 | break; 990 | 991 | case 0x98: /* SBB B */ 992 | P = B; 993 | res = (F >> C_SHIFT) & 1; 994 | goto finish_sub; 995 | 996 | case 0x99: /* SBB C */ 997 | P = C; 998 | res = (F >> C_SHIFT) & 1; 999 | goto finish_sub; 1000 | 1001 | case 0x9a: /* SBB D */ 1002 | P = D; 1003 | res = (F >> C_SHIFT) & 1; 1004 | goto finish_sub; 1005 | 1006 | case 0x9b: /* SBB E */ 1007 | P = E; 1008 | res = (F >> C_SHIFT) & 1; 1009 | goto finish_sub; 1010 | 1011 | case 0x9c: /* SBB H */ 1012 | P = H; 1013 | res = (F >> C_SHIFT) & 1; 1014 | goto finish_sub; 1015 | 1016 | case 0x9d: /* SBB L */ 1017 | P = L; 1018 | res = (F >> C_SHIFT) & 1; 1019 | goto finish_sub; 1020 | 1021 | case 0x9e: /* SBB M */ 1022 | P = memrdr(HL); 1023 | res = (F >> C_SHIFT) & 1; 1024 | t += 3; 1025 | goto finish_sub; 1026 | 1027 | case 0x9f: /* SBB A */ 1028 | P = A; 1029 | res = (F >> C_SHIFT) & 1; 1030 | goto finish_sub; 1031 | 1032 | case 0xa0: /* ANA B */ 1033 | P = B; 1034 | finish_ana: 1035 | res = A & P; 1036 | #ifdef AMD8080 1037 | F = szp_flags[res]; 1038 | /* H_FLAG and C_FLAG cleared */ 1039 | #else 1040 | F = (((((A | P) >> 3) & 1) << H_SHIFT) | 1041 | szp_flags[res]); 1042 | /* C_FLAG cleared */ 1043 | #endif 1044 | A = res; 1045 | break; 1046 | 1047 | case 0xa1: /* ANA C */ 1048 | P = C; 1049 | goto finish_ana; 1050 | 1051 | case 0xa2: /* ANA D */ 1052 | P = D; 1053 | goto finish_ana; 1054 | 1055 | case 0xa3: /* ANA E */ 1056 | P = E; 1057 | goto finish_ana; 1058 | 1059 | case 0xa4: /* ANA H */ 1060 | P = H; 1061 | goto finish_ana; 1062 | 1063 | case 0xa5: /* ANA L */ 1064 | P = L; 1065 | goto finish_ana; 1066 | 1067 | case 0xa6: /* ANA M */ 1068 | P = memrdr(HL); 1069 | t += 3; 1070 | goto finish_ana; 1071 | 1072 | case 0xa7: /* ANA A */ 1073 | P = A; 1074 | goto finish_ana; 1075 | 1076 | case 0xa8: /* XRA B */ 1077 | P = B; 1078 | finish_xra: 1079 | res = A ^ P; 1080 | F = szp_flags[res]; 1081 | /* H_FLAG and C_FLAG cleared */ 1082 | A = res; 1083 | break; 1084 | 1085 | case 0xa9: /* XRA C */ 1086 | P = C; 1087 | goto finish_xra; 1088 | 1089 | case 0xaa: /* XRA D */ 1090 | P = D; 1091 | goto finish_xra; 1092 | 1093 | case 0xab: /* XRA E */ 1094 | P = E; 1095 | goto finish_xra; 1096 | 1097 | case 0xac: /* XRA H */ 1098 | P = H; 1099 | goto finish_xra; 1100 | 1101 | case 0xad: /* XRA L */ 1102 | P = L; 1103 | goto finish_xra; 1104 | 1105 | case 0xae: /* XRA M */ 1106 | P = memrdr(HL); 1107 | t += 3; 1108 | goto finish_xra; 1109 | 1110 | case 0xaf: /* XRA A */ 1111 | F = Z_FLAG | P_FLAG; 1112 | /* S_FLAG, H_FLAG, and C_FLAG cleared */ 1113 | A = 0; 1114 | break; 1115 | 1116 | case 0xb0: /* ORA B */ 1117 | P = B; 1118 | finish_ora: 1119 | res = A | P; 1120 | F = szp_flags[res]; 1121 | /* H_FLAG and C_FLAG cleared */ 1122 | A = res; 1123 | break; 1124 | 1125 | case 0xb1: /* ORA C */ 1126 | P = C; 1127 | goto finish_ora; 1128 | 1129 | case 0xb2: /* ORA D */ 1130 | P = D; 1131 | goto finish_ora; 1132 | 1133 | case 0xb3: /* ORA E */ 1134 | P = E; 1135 | goto finish_ora; 1136 | 1137 | case 0xb4: /* ORA H */ 1138 | P = H; 1139 | goto finish_ora; 1140 | 1141 | case 0xb5: /* ORA L */ 1142 | P = L; 1143 | goto finish_ora; 1144 | 1145 | case 0xb6: /* ORA M */ 1146 | P = memrdr(HL); 1147 | t += 3; 1148 | goto finish_ora; 1149 | 1150 | case 0xb7: /* ORA A */ 1151 | F = szp_flags[A]; 1152 | /* H_FLAG and C_FLAG cleared */ 1153 | break; 1154 | 1155 | case 0xb8: /* CMP B */ 1156 | P = B; 1157 | finish_cmp: 1158 | res = A - P; 1159 | cout = (~A & P) | ((~A | P) & res); 1160 | F = ((((cout >> 7) & 1) << C_SHIFT) | 1161 | (((cout >> 3) & 1) << H_SHIFT) | 1162 | szp_flags[res]); 1163 | F ^= H_FLAG; 1164 | break; 1165 | 1166 | case 0xb9: /* CMP C */ 1167 | P = C; 1168 | goto finish_cmp; 1169 | 1170 | case 0xba: /* CMP D */ 1171 | P = D; 1172 | goto finish_cmp; 1173 | 1174 | case 0xbb: /* CMP E */ 1175 | P = E; 1176 | goto finish_cmp; 1177 | 1178 | case 0xbc: /* CMP H */ 1179 | P = H; 1180 | goto finish_cmp; 1181 | 1182 | case 0xbd: /* CMP L */ 1183 | P = L; 1184 | goto finish_cmp; 1185 | 1186 | case 0xbe: /* CMP M */ 1187 | P = memrdr(HL); 1188 | t += 3; 1189 | goto finish_cmp; 1190 | 1191 | case 0xbf: /* CMP A */ 1192 | F = Z_FLAG | H_FLAG | P_FLAG; 1193 | /* S_FLAG and C_FLAG cleared */ 1194 | break; 1195 | 1196 | case 0xc0: /* RNZ */ 1197 | res = !(F & Z_FLAG); 1198 | finish_retc: 1199 | t++; 1200 | if (res) 1201 | goto finish_ret; 1202 | break; 1203 | 1204 | case 0xc1: /* POP B */ 1205 | C = memrdr(SP++); 1206 | B = memrdr(SP++); 1207 | t += 6; 1208 | break; 1209 | 1210 | case 0xc2: /* JNZ nn */ 1211 | res = !(F & Z_FLAG); 1212 | finish_jmpc: 1213 | WL = memrdr(PC++); 1214 | WH = memrdr(PC++); 1215 | t += 6; 1216 | if (res) 1217 | PC = W; 1218 | break; 1219 | 1220 | case 0xc3: /* JMP nn */ 1221 | case 0xcb: /* JMP* nn */ 1222 | WL = memrdr(PC++); 1223 | WH = memrdr(PC); 1224 | t += 6; 1225 | PC = W; 1226 | break; 1227 | 1228 | case 0xc4: /* CNZ nn */ 1229 | res = !(F & Z_FLAG); 1230 | finish_callc: 1231 | WL = memrdr(PC++); 1232 | WH = memrdr(PC++); 1233 | t += 7; 1234 | if (res) 1235 | goto finish_call; 1236 | break; 1237 | 1238 | case 0xc5: /* PUSH B */ 1239 | memwrt(--SP, B); 1240 | memwrt(--SP, C); 1241 | t += 7; 1242 | break; 1243 | 1244 | case 0xc6: /* ADI n */ 1245 | P = memrdr(PC++); 1246 | res = 0; 1247 | t += 3; 1248 | goto finish_add; 1249 | 1250 | case 0xc7: /* RST 0 */ 1251 | W = 0; 1252 | t++; 1253 | goto finish_call; 1254 | 1255 | case 0xc8: /* RZ */ 1256 | res = F & Z_FLAG; 1257 | goto finish_retc; 1258 | 1259 | case 0xc9: /* RET */ 1260 | case 0xd9: /* RET* */ 1261 | finish_ret: 1262 | WL = memrdr(SP++); 1263 | WH = memrdr(SP++); 1264 | t += 6; 1265 | PC = W; 1266 | break; 1267 | 1268 | case 0xca: /* JZ nn */ 1269 | res = F & Z_FLAG; 1270 | goto finish_jmpc; 1271 | 1272 | case 0xcc: /* CZ nn */ 1273 | res = F & Z_FLAG; 1274 | goto finish_callc; 1275 | 1276 | case 0xcd: /* CALL nn */ 1277 | case 0xdd: /* CALL* nn */ 1278 | case 0xed: /* CALL* nn */ 1279 | case 0xfd: /* CALL* nn */ 1280 | WL = memrdr(PC++); 1281 | WH = memrdr(PC++); 1282 | t += 7; 1283 | finish_call: 1284 | memwrt(--SP, PCH); 1285 | memwrt(--SP, PCL); 1286 | t += 6; 1287 | PC = W; 1288 | break; 1289 | 1290 | case 0xce: /* ACI n */ 1291 | P = memrdr(PC++); 1292 | res = (F >> C_SHIFT) & 1; 1293 | t += 3; 1294 | goto finish_add; 1295 | 1296 | case 0xcf: /* RST 1 */ 1297 | W = 0x08; 1298 | t++; 1299 | goto finish_call; 1300 | 1301 | case 0xd0: /* RNC */ 1302 | res = !(F & C_FLAG); 1303 | goto finish_retc; 1304 | 1305 | case 0xd1: /* POP D */ 1306 | E = memrdr(SP++); 1307 | D = memrdr(SP++); 1308 | t += 6; 1309 | break; 1310 | 1311 | case 0xd2: /* JNC nn */ 1312 | res = !(F & C_FLAG); 1313 | goto finish_jmpc; 1314 | 1315 | case 0xd3: /* OUT n */ 1316 | P = memrdr(PC++); 1317 | io_out(P, P, A); 1318 | t += 6; 1319 | break; 1320 | 1321 | case 0xd4: /* CNC nn */ 1322 | res = !(F & C_FLAG); 1323 | goto finish_callc; 1324 | 1325 | case 0xd5: /* PUSH D */ 1326 | memwrt(--SP, D); 1327 | memwrt(--SP, E); 1328 | t += 7; 1329 | break; 1330 | 1331 | case 0xd6: /* SUI n */ 1332 | P = memrdr(PC++); 1333 | res = 0; 1334 | t += 3; 1335 | goto finish_sub; 1336 | 1337 | case 0xd7: /* RST 2 */ 1338 | W = 0x10; 1339 | t++; 1340 | goto finish_call; 1341 | 1342 | case 0xd8: /* RC */ 1343 | res = F & C_FLAG; 1344 | goto finish_retc; 1345 | 1346 | case 0xda: /* JC nn */ 1347 | res = F & C_FLAG; 1348 | goto finish_jmpc; 1349 | 1350 | case 0xdb: /* IN n */ 1351 | P = memrdr(PC++); 1352 | A = io_in(P, P); 1353 | t += 6; 1354 | break; 1355 | 1356 | case 0xdc: /* CC nn */ 1357 | res = F & C_FLAG; 1358 | goto finish_callc; 1359 | 1360 | case 0xde: /* SBI n */ 1361 | P = memrdr(PC++); 1362 | res = (F >> C_SHIFT) & 1; 1363 | t += 3; 1364 | goto finish_sub; 1365 | 1366 | case 0xdf: /* RST 3 */ 1367 | W = 0x18; 1368 | t++; 1369 | goto finish_call; 1370 | 1371 | case 0xe0: /* RPO */ 1372 | res = !(F & P_FLAG); 1373 | goto finish_retc; 1374 | 1375 | case 0xe1: /* POP H */ 1376 | L = memrdr(SP++); 1377 | H = memrdr(SP++); 1378 | t += 6; 1379 | break; 1380 | 1381 | case 0xe2: /* JPO nn */ 1382 | res = !(F & P_FLAG); 1383 | goto finish_jmpc; 1384 | 1385 | case 0xe3: /* XTHL */ 1386 | P = memrdr(SP); 1387 | memwrt(SP, L); 1388 | L = P; 1389 | P = memrdr(SP + 1); 1390 | memwrt(SP + 1, H); 1391 | H = P; 1392 | t += 14; 1393 | break; 1394 | 1395 | case 0xe4: /* CPO nn */ 1396 | res = !(F & P_FLAG); 1397 | goto finish_callc; 1398 | 1399 | case 0xe5: /* PUSH H */ 1400 | memwrt(--SP, H); 1401 | memwrt(--SP, L); 1402 | t += 7; 1403 | break; 1404 | 1405 | case 0xe6: /* ANI n */ 1406 | P = memrdr(PC++); 1407 | t += 3; 1408 | goto finish_ana; 1409 | 1410 | case 0xe7: /* RST 4 */ 1411 | W = 0x20; 1412 | t++; 1413 | goto finish_call; 1414 | 1415 | case 0xe8: /* RPE */ 1416 | res = F & P_FLAG; 1417 | goto finish_retc; 1418 | 1419 | case 0xe9: /* PCHL */ 1420 | PC = HL; 1421 | t++; 1422 | break; 1423 | 1424 | case 0xea: /* JPE nn */ 1425 | res = F & P_FLAG; 1426 | goto finish_jmpc; 1427 | 1428 | case 0xeb: /* XCHG */ 1429 | P = D; 1430 | D = H; 1431 | H = P; 1432 | P = E; 1433 | E = L; 1434 | L = P; 1435 | break; 1436 | 1437 | case 0xec: /* CPE nn */ 1438 | res = F & P_FLAG; 1439 | goto finish_callc; 1440 | 1441 | case 0xee: /* XRI n */ 1442 | P = memrdr(PC++); 1443 | t += 3; 1444 | goto finish_xra; 1445 | 1446 | case 0xef: /* RST 5 */ 1447 | W = 0x28; 1448 | t++; 1449 | goto finish_call; 1450 | 1451 | case 0xf0: /* RP */ 1452 | res = !(F & S_FLAG); 1453 | goto finish_retc; 1454 | 1455 | case 0xf1: /* POP PSW */ 1456 | F = memrdr(SP++); 1457 | A = memrdr(SP++); 1458 | t += 6; 1459 | break; 1460 | 1461 | case 0xf2: /* JP nn */ 1462 | res = !(F & S_FLAG); 1463 | goto finish_jmpc; 1464 | 1465 | case 0xf3: /* DI */ 1466 | IFF = 0; 1467 | break; 1468 | 1469 | case 0xf4: /* CP nn */ 1470 | res = !(F & S_FLAG); 1471 | goto finish_callc; 1472 | 1473 | case 0xf5: /* PUSH PSW */ 1474 | memwrt(--SP, A); 1475 | memwrt(--SP, ((F & ~(Y_FLAG | X_FLAG)) | N_FLAG)); 1476 | t += 7; 1477 | break; 1478 | 1479 | case 0xf6: /* ORI n */ 1480 | P = memrdr(PC++); 1481 | t += 3; 1482 | goto finish_ora; 1483 | 1484 | case 0xf7: /* RST 6 */ 1485 | W = 0x30; 1486 | t++; 1487 | goto finish_call; 1488 | 1489 | case 0xf8: /* RM */ 1490 | res = F & S_FLAG; 1491 | goto finish_retc; 1492 | 1493 | case 0xf9: /* SPHL */ 1494 | SP = HL; 1495 | t++; 1496 | break; 1497 | 1498 | case 0xfa: /* JM nn */ 1499 | res = F & S_FLAG; 1500 | goto finish_jmpc; 1501 | 1502 | case 0xfb: /* EI */ 1503 | IFF = 3; 1504 | // int_protection = 1; /* protect next instruction */ 1505 | break; 1506 | 1507 | case 0xfc: /* CM nn */ 1508 | res = F & S_FLAG; 1509 | goto finish_callc; 1510 | 1511 | case 0xfe: /* CPI n */ 1512 | P = memrdr(PC++); 1513 | t += 3; 1514 | goto finish_cmp; 1515 | 1516 | case 0xff: /* RST 7 */ 1517 | W = 0x38; 1518 | t++; 1519 | goto finish_call; 1520 | } 1521 | 1522 | tstates += t; // account for the executed instruction 1523 | 1524 | } while (State == Running); 1525 | 1526 | *cpu_state = cpu_regs; 1527 | 1528 | #undef W 1529 | #undef WH 1530 | #undef WL 1531 | 1532 | #undef AF 1533 | #undef A 1534 | #undef F 1535 | #undef BC 1536 | #undef B 1537 | #undef C 1538 | #undef DE 1539 | #undef D 1540 | #undef E 1541 | #undef HL 1542 | #undef H 1543 | #undef L 1544 | #undef SP 1545 | #undef SPH 1546 | #undef SPL 1547 | #undef PC 1548 | #undef PCH 1549 | #undef PCL 1550 | #undef IFF 1551 | 1552 | #pragma pop_macro("F") 1553 | #pragma pop_macro("SP") 1554 | #pragma pop_macro("SPH") 1555 | #pragma pop_macro("SPL") 1556 | #pragma pop_macro("PC") 1557 | } 1558 | 1559 | // interrupt handler for break switch, stops CPU 1560 | void user_int(void) 1561 | { 1562 | State = Interrupted; 1563 | } 1564 | 1565 | #ifdef DEBUG 1566 | // from the Arduino Memory Guide 1567 | void display_freeram() 1568 | { 1569 | Serial.print(F("SRAM left: ")); 1570 | Serial.println(freeRam()); 1571 | } 1572 | 1573 | int freeRam() 1574 | { 1575 | extern int __heap_start, *__brkval; 1576 | int v; 1577 | 1578 | return (int) &v - (__brkval == 0 ? 1579 | (int) &__heap_start : (int) __brkval); 1580 | } 1581 | #endif 1582 | 1583 | void setup() 1584 | { 1585 | // put your setup code here, to run once: 1586 | pinMode(USERINT, INPUT_PULLUP); 1587 | attachInterrupt(digitalPinToInterrupt(USERINT), user_int, FALLING); 1588 | 1589 | Serial.begin(9600); 1590 | while (!Serial) 1591 | ; // Wait for serial port to connect. Needed for native USB 1592 | 1593 | // if this analog pin is not wired the value is unpredictable 1594 | randomSeed(analogRead(UAP)); 1595 | 1596 | // make sure we run with highest possible SPI clock 1597 | SPI.setClockDivider(SPI_CLOCK_DIV2); 1598 | 1599 | if (!fram.begin()) { 1600 | Serial.println(F("No FRAM found")); 1601 | exit(1); 1602 | } 1603 | fram.writeEnable(true); // always leave write enabled 1604 | 1605 | if (!SD.begin(SD_CS)) { 1606 | Serial.println(F("SD failed")); 1607 | exit(1); 1608 | } 1609 | } 1610 | 1611 | void loop() 1612 | { 1613 | // put your main code here, to run repeatedly: 1614 | 1615 | // variables for measuring the run time 1616 | unsigned long start, stop; 1617 | 1618 | // we have no memory for a fancy banner 1619 | Serial.println(F("\f8080-SIM v1.6.1\n")); 1620 | 1621 | // initialize and configure all virtual hardware 1622 | init_cpu(&cpu_state); 1623 | init_memory(); 1624 | config(); 1625 | 1626 | // run the 8080 CPU with whatever code is in memory 1627 | State = Running; 1628 | start = millis(); 1629 | cpu_8080(&cpu_state); 1630 | stop = millis(); 1631 | 1632 | // tell us why it stopped running 1633 | Serial.println(); 1634 | Serial.print(F("CPU was ")); 1635 | if (State == Halted) 1636 | Serial.println(F("halted")); 1637 | else if (State == Interrupted) 1638 | Serial.println(F("interrupted")); 1639 | 1640 | // print register dump 1641 | Serial.println(F("PC\tA\tSZHPC\tB:C\tD:E\tH:L\tSP")); 1642 | Serial.print(cpu_state.pc.w, HEX); 1643 | Serial.print(F("\t")); 1644 | Serial.print(cpu_state.af.h, HEX); 1645 | Serial.print(F("\t")); 1646 | Serial.print(cpu_state.af.l & S_FLAG ? 1 : 0, DEC); 1647 | Serial.print(cpu_state.af.l & Z_FLAG ? 1 : 0, DEC); 1648 | Serial.print(cpu_state.af.l & H_FLAG ? 1 : 0, DEC); 1649 | Serial.print(cpu_state.af.l & P_FLAG ? 1 : 0, DEC); 1650 | Serial.print(cpu_state.af.l & C_FLAG ? 1 : 0, DEC); 1651 | Serial.print(F("\t")); 1652 | Serial.print(cpu_state.bc.h, HEX); 1653 | Serial.print(F(":")); 1654 | Serial.print(cpu_state.bc.l, HEX); 1655 | Serial.print(F("\t")); 1656 | Serial.print(cpu_state.de.h, HEX); 1657 | Serial.print(F(":")); 1658 | Serial.print(cpu_state.de.l, HEX); 1659 | Serial.print(F("\t")); 1660 | Serial.print(cpu_state.hl.h, HEX); 1661 | Serial.print(F(":")); 1662 | Serial.print(cpu_state.hl.l, HEX); 1663 | Serial.print(F("\t")); 1664 | Serial.print(cpu_state.sp.w, HEX); 1665 | Serial.println(); 1666 | 1667 | // print some execution statistics 1668 | Serial.println(); 1669 | Serial.print(F("8080 ran ")); 1670 | Serial.print(stop - start, DEC); 1671 | Serial.print(F(" ms and executed ")); 1672 | Serial.print(tstates, DEC); 1673 | Serial.println(F(" t-states")); 1674 | Serial.print(F("Clock frequency ")); 1675 | Serial.print(float(tstates) / float(stop - start) / 1000.0, 2); 1676 | Serial.println(F(" MHz")); 1677 | #ifdef DEBUG 1678 | display_freeram(); 1679 | #endif 1680 | Serial.println(); 1681 | Serial.flush(); 1682 | exit(0); 1683 | } 1684 | -------------------------------------------------------------------------------- /bootrom.h: -------------------------------------------------------------------------------- 1 | //build from source file bootrom.asm 2 | const PROGMEM unsigned char code[MEMSIZE] = { 3 | 0xf3, 0xaf, 0x32, 0x00, 0xf0, 0x32, 0x02, 0xf0, 0x32, 0x03, 0xf0, 0x3e, 4 | 0x01, 0x32, 0x01, 0xf0, 0x3e, 0x10, 0xd3, 0x04, 0x3e, 0x00, 0xd3, 0x04, 5 | 0x3e, 0xf0, 0xd3, 0x04, 0x3e, 0x20, 0xd3, 0x04, 0xdb, 0x04, 0xb7, 0xca, 6 | 0x00, 0x00, 0xfe, 0x02, 0xca, 0x00, 0x00, 0x76 7 | }; 8 | unsigned short code_length = 44; 9 | unsigned short code_load_addr = 0xff00; 10 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | // 2 | // Intel 8080 CPU emulation on an Arduino Nano 3 | // derived from Z80PACK 4 | // Copyright 2024, Udo Munk 5 | // 6 | // With this module the machine can be configured to run standalone 7 | // software loaded from a file. Also allows to mount/unmount disk 8 | // images with OS's and application software. 9 | // 10 | // History: 11 | // 19-MAY-2024 implemented configuration dialog 12 | // 21-MAY-2024 added mount/unmount of disk images 13 | // 14 | 15 | #define BS 0x08 // backspace 16 | #define DEL 0x7f // delete 17 | 18 | // read a config command line from the terminal 19 | void get_cmdline(char *buf, int len) 20 | { 21 | int i = 0; 22 | char c; 23 | 24 | for (;;) { 25 | while (!Serial.available()) 26 | ; 27 | c = Serial.read(); 28 | if ((c == BS) || (c == DEL)) { 29 | if (i >= 1) { 30 | Serial.write(BS); 31 | Serial.write(' '); 32 | Serial.write(BS); 33 | i--; 34 | } 35 | } else if (c != '\r') { 36 | if (i < len - 1) { 37 | buf[i++] = c; 38 | Serial.write(c); 39 | if (len == 2) 40 | break; 41 | } 42 | } else { 43 | break; 44 | } 45 | } 46 | buf[i] = '\0'; 47 | } 48 | 49 | // prompt for a filename 50 | static void prompt_fn(char *s) 51 | { 52 | Serial.print(F("Filename: ")); 53 | get_cmdline(s, 9); 54 | Serial.println(); 55 | } 56 | 57 | // disk already mounted 58 | static void mount_msg(void) 59 | { 60 | Serial.println(F("Disk already mounted")); 61 | } 62 | 63 | // configuration dialog for the machine 64 | void config(void) 65 | { 66 | const char *cfg = "/CONF80/CFG.TXT"; 67 | char s[10]; 68 | int go_flag = 0; 69 | 70 | // try to read config file 71 | if (sd_file.openExistingSFN(cfg)) { 72 | sd_file.read(&fp_value, 1); 73 | sd_file.read(&disks[0], 22); 74 | sd_file.read(&disks[1], 22); 75 | sd_file.close(); 76 | } 77 | 78 | while (!go_flag) { 79 | Serial.print(F("p - port 255: 0x")); 80 | Serial.println(fp_value, HEX); 81 | Serial.println(F("l - load file")); 82 | Serial.print(F("0 - Disk 0: ")); 83 | Serial.println(disks[0]); 84 | Serial.print(F("1 - Disk 1: ")); 85 | Serial.println(disks[1]); 86 | Serial.println(F("g - run machine\n")); 87 | Serial.print(F("Command: ")); 88 | 89 | get_cmdline(s, 2); 90 | Serial.println(F("\n")); 91 | 92 | switch (*s) { 93 | case 'p': 94 | again: 95 | Serial.print(F("Value: ")); 96 | get_cmdline(s, 3); 97 | Serial.println(F("\n")); 98 | if ((*s >= 'a') && (*s <= 'f')) 99 | *s = toupper(*s); 100 | if ((*(s + 1) >= 'a') && (*(s + 1) <= 'f')) 101 | *(s + 1) = toupper(*(s + 1)); 102 | if (!isxdigit(*s) || !isxdigit(*(s + 1))) { 103 | Serial.println(F("What?")); 104 | goto again; 105 | } 106 | fp_value = (*s <= '9') ? (*s - '0') : (*s - 'A' + 10); 107 | fp_value <<= 4; 108 | fp_value += (*(s + 1) <= '9') ? (*(s + 1) - '0') : 109 | (*(s + 1) - 'A' + 10); 110 | break; 111 | 112 | case 'l': 113 | prompt_fn(s); 114 | load_file(s); 115 | break; 116 | 117 | case '0': 118 | prompt_fn(s); 119 | if (strlen(s) == 0) { 120 | disks[0][0] = 0x0; 121 | Serial.println(); 122 | } else { 123 | mount_disk(0, s); 124 | if (!strcmp(disks[0], disks[1])) { 125 | mount_msg(); 126 | disks[0][0] = 0x0; 127 | Serial.println(); 128 | } 129 | } 130 | break; 131 | 132 | case '1': 133 | prompt_fn(s); 134 | if (strlen(s) == 0) { 135 | disks[1][0] = 0x0; 136 | Serial.println(); 137 | } else { 138 | mount_disk(1, s); 139 | if (!strcmp(disks[0], disks[1])) { 140 | mount_msg(); 141 | disks[1][0] = 0x0; 142 | Serial.println(); 143 | } 144 | } 145 | break; 146 | 147 | case 'g': 148 | go_flag = 1; 149 | break; 150 | 151 | default: 152 | break; 153 | } 154 | } 155 | 156 | // try to save config file 157 | if (sd_file.openExistingSFN(cfg)) { 158 | sd_file.write(&fp_value, 1); 159 | sd_file.write(&disks[0], 22); 160 | sd_file.write(&disks[1], 22); 161 | sd_file.close(); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /disks/cpm22.dsk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udo-munk/Arduino8080/f675ec03fe1c7fd80e3d3026e4b6d82740a2945e/disks/cpm22.dsk -------------------------------------------------------------------------------- /doc/README-building: -------------------------------------------------------------------------------- 1 | You need the Arduino IDE to compile the software and upload it 2 | into the Arduino Nano board. The software requires the following 3 | additional libraries, which need to get installed in the IDE: 4 | 5 | Adafruit BusIO 6 | Adafruit_FRAM_SPI 7 | SdFat - Adafruit Fork 8 | 9 | Preparing a MicroSD card: 10 | In the root directory of the card create these directories: 11 | CONF80 12 | CODE80 13 | DISKS80 14 | 15 | Into the CODE80 directory copy all the .bin files from src-examples. 16 | Into the DISKS80 directory copy the CP/M disk image from disks. 17 | In CONF80 create an empty text file CFG.TXT. 18 | 19 | I have tried to create the configuration file from the application, 20 | but this would pull in another 2 KB into the code segment, which we 21 | don't have when building the Z80 variant. So the directory and an 22 | empty file must be created once, from there on it will just be used 23 | to save and load the last configuration. 24 | -------------------------------------------------------------------------------- /doc/README-config: -------------------------------------------------------------------------------- 1 | When the system is powered on it displays a configuration dialog, 2 | that allows to setup and run the machine, either with stand-alone 3 | software, or boot an operating system from floppy disk. The dialog 4 | is very ineloquent and there is no builtin help, because of the 5 | limited memory resources of the Arduino Nano's ATmega328 MCU. So 6 | here a short introduction how to use it, the dialog looks like this: 7 | 8 | Z80-SIM v1.6.1 9 | 10 | p - port 255: 0x22 11 | l - load file 12 | 0 - Disk 0: 13 | 1 - Disk 1: 14 | g - run machine 15 | 16 | Command: 17 | 18 | 19 | With 'p' the value read from port 255 can be set. This is the hardware 20 | configuration that software can query, which components are in the 21 | machine. MITS Altair BASIC interpreter versions < 4.0 need a value 22 | of 0x00 and versions >= 4.0 a value of 0x22. To set the value enter 23 | two hex digits. 24 | 25 | With 'l' one can load a file with 8080/Z80 binary code into the memory. 26 | Enter only the name of the file, directory and extension will be added 27 | by the software. Unfortunately we cannot list directory contents because 28 | of the memory constrains, so keep directory listings on the PC you used 29 | to prepare the MicroSD card. 30 | 31 | With '0' one can mount/unmount disk images on disk drive 0. To mount 32 | one enter the filename, to unmount enter return only. If disk drive 0 33 | has a disk image mounted the machine will boot from this, no matter 34 | which program code was loaded before. A disk image in disk drive 0 35 | must contain a bootable operating system, like the included CP/M 2.2 36 | implementation for the machine. 37 | 38 | With '1' one can mount/unmount another disk image on disk drive 1 with 39 | some application software or data or whatever, same as with drive 0. 40 | 41 | With 'g' the 8080/Z80 CPU is started with code from disk drive 0, and 42 | if nothing mounted with whatever was loaded into the memory. 43 | -------------------------------------------------------------------------------- /doc/README-cpm: -------------------------------------------------------------------------------- 1 | The directory srccpm2 includes everything needed to put a customized 2 | CP/M 2.2 on the boot tracks of a disk image. A Makefile is included, 3 | that assembles boot loader and CBIOS with z80pack z80asm, and also 4 | builds the putsys utility, used to write the system onto the disk image. 5 | This is done with two commands executed in the directory: 6 | 7 | make 8 | ./putsys 9 | 10 | With that disks/cpm22.dsk is ready to be copied to a MicroSD card into 11 | a directory /DISKS80, which you need to create. Because I have done 12 | this already you can just copy disks/cpm22.dsk into /DISKS80 on the 13 | SD card. 14 | 15 | The CP/M 2.2 disk image includes everything that came with CP/M 2.2, 16 | plus a selection of some usefull tools. I also copied boot loader 17 | and CBIOS sources together with submit scripts onto the disk. CP/M 18 | was designed as self containing systems, means it has everything 19 | like editor, assembler, loader and so on, that it can be used to 20 | create a bootable disk. I always thought it is a good test for an 21 | operating system, when it can build and install it self on some disk. 22 | For this the filesystem must be stable, one needs working tools, so 23 | everything included that you can verify this. Please be aware that 24 | the machine is slow, this will need like 20 minutes or so, but 25 | great fun to watch it at doing serious work. On the booted CP/M 26 | system run these commands: 27 | 28 | submit sysgen64 29 | sysgen cpm64.sys 30 | 31 | Answer question about drive with 'a'. 32 | -------------------------------------------------------------------------------- /doc/README-hardware: -------------------------------------------------------------------------------- 1 | I really have no time to draw schematics with some CAD program. 2 | Also I think that is not necessary, the circuit is very simple and 3 | I found lots of online documentation, how to wire such devices with 4 | an Arduino Nano. 5 | However, I'll leave pictures here with my breadboard setup, which should 6 | show the wiring. 7 | 8 | nanoboard1: 9 | Shows how the Arduino Nano is wired with an Adafruit SPI FRAM module. 10 | Please note that I power the FRAM from the Nano stabilized 5V, 11 | they will also work with 3.3V, just make sure you connect the 12 | correct one to your board's power line. 13 | 14 | nanoboard2: 15 | An Adafruit MicroSD SPI module was added, make sure you get one 16 | that is designed for 5V, most are 3.3V. 17 | Connect both GND pins of the Arduino Nano board to the GND rails on 18 | your board. The MicoSD module can draw 100 mA, and all current drawn 19 | from the 5V must flow to GND. 20 | Then SPI runs with a clock frequency in the radio bands. And the 21 | breadboard wiring of course makes a nice HF antenna. I have added 22 | two capacitators with 10 uF and 100 nF between the + and - rails, 23 | to shortcut most of the generated HF. 24 | 25 | nanoboard3: 26 | I added a switch connected to GND and D3 at the Arduino Nano. Pressing 27 | the switch will cause an interrupt, which stops the 8080 CPU at next M1 28 | from whatever it is doing. 29 | This is optional and not really required for a functioning system. If the 30 | machine gets stuck with some broken software you can reset the Arduino 31 | board and restart all over. It just helps to analyze problems with buggy 32 | software. 33 | 34 | MAY 2024, Udo Munk 35 | -------------------------------------------------------------------------------- /doc/nanoboard1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udo-munk/Arduino8080/f675ec03fe1c7fd80e3d3026e4b6d82740a2945e/doc/nanoboard1.jpg -------------------------------------------------------------------------------- /doc/nanoboard2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udo-munk/Arduino8080/f675ec03fe1c7fd80e3d3026e4b6d82740a2945e/doc/nanoboard2.jpg -------------------------------------------------------------------------------- /doc/nanoboard3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udo-munk/Arduino8080/f675ec03fe1c7fd80e3d3026e4b6d82740a2945e/doc/nanoboard3.jpg -------------------------------------------------------------------------------- /iosim.h: -------------------------------------------------------------------------------- 1 | // 2 | // Intel 8080 CPU emulation on an Arduino Nano 3 | // derived from Z80PACK 4 | // Copyright 2024, Udo Munk 5 | // 6 | // This module implements the I/O functions for the 8080 CPU. 7 | // 8 | // History: 9 | // 04-MAY-2024 Release 1.0 implements a very basic 8080 system 10 | // 10-MAY-2024 added frontpanel port, handle reads to UART ignoring status 11 | // 21-MAY-2024 added FDC port 12 | // 13 | 14 | static BYTE sio_last; // last byte read from the UART 15 | BYTE fp_value = 0x22; // the value for port 255 read 16 | 17 | // I/O function port 0 read: 18 | // read status of the Arduino UART and return: 19 | // bit 0 = 0, character available for input from tty 20 | // bit 7 = 0, transmitter ready to write character to tty 21 | const static BYTE p000_in(void) 22 | { 23 | register BYTE stat = 0b10000001; // initially not ready 24 | 25 | if (Serial.available()) // check if there is input from tty 26 | stat &= 0b11111110; // if so flip status bit 27 | if (Serial.availableForWrite()) // check if output to tty is possible 28 | stat &= 0b01111111; // if so flip status bit 29 | 30 | return stat; 31 | } 32 | 33 | // I/O function port 1 read: 34 | // Read byte from Arduino UART. 35 | const static BYTE p001_in(void) 36 | { 37 | if (Serial.available()) 38 | sio_last = Serial.read(); 39 | return sio_last; 40 | } 41 | 42 | // This array contains function pointers for every input 43 | // I/O port (0 - 255), to do the required I/O. 44 | // To save memory we use the first 5 ports only. 45 | const static BYTE (*port_in[5]) (void) = { 46 | p000_in, // port 0 47 | p001_in, // port 1 48 | 0, // port 2 49 | 0, // port 3 50 | fdc_in // port 4 51 | }; 52 | 53 | // read a byte from 8080 CPU I/O 54 | BYTE io_in(BYTE addrl, BYTE addrh) 55 | { 56 | if ((addrl <= 4) && (*port_in[addrl] != 0)) // for now we use 0-4 57 | return (*port_in[addrl])(); 58 | else if (addrl == 255) // and 255, frontpanel port 59 | return fp_value; 60 | else 61 | return 0xff; // all other return 0xff 62 | } 63 | 64 | // I/O function port 1 write: 65 | // Write byte to Arduino UART. 66 | const static void p001_out(BYTE data) 67 | { 68 | Serial.write(data & 0x7f); // strip parity, some software won't 69 | } 70 | 71 | // This array contains function pointers for every output 72 | // I/O port (0 - 255), to do the required I/O. 73 | // To save memory we use the first 5 ports only. 74 | const static void (*port_out[5]) (BYTE) = { 75 | 0, // port 0 76 | p001_out, // port 1 77 | 0, // port 2 78 | 0, // port 3 79 | fdc_out // port 4 80 | }; 81 | 82 | // Write a byte to 8080 CPU I/O 83 | void io_out(BYTE addrl, BYTE addrh, BYTE data) 84 | { 85 | if ((addrl <= 4) && (*port_out[addrl] != 0)) // for now we use 0-4 86 | (*port_out[addrl]) (data); // and all other do nothing 87 | } 88 | -------------------------------------------------------------------------------- /memsim.h: -------------------------------------------------------------------------------- 1 | // 2 | // Intel 8080 CPU emulation on an Arduino Nano 3 | // derived from Z80PACK 4 | // Copyright 2024, Udo Munk 5 | // 6 | // This module implements the low level functions to access external 7 | // SPI memory. Supported is Adafruit FRAM, min 64 KByte for 8080 memory, 8 | // and Adafruit MicroSD for standalone programms and disk images. 9 | // 10 | // Uses Arduino libraries: 11 | // Adafruit BusIO 12 | // Adafruit_FRAM_SPI 13 | // SdFat - Adafruit Fork 14 | // 15 | // History: 16 | // 04-MAY-2024 Release 1.0 implements a very basic 8080 system 17 | // 06-MAY-2024 Release 1.1 add support for a ROM in flash 18 | // 07-MAY-2024 Release 1.2 move 8080 memory into a FRAM 19 | // 18-MAY-2024 Release 1.4 read 8080 code from a file on SD into FRAM 20 | // 19-MAY-2024 Release 1.5 use SdFat lib instead of SD lib to save memory 21 | // 21-MAY-2024 Release 1.6 added low level functions for disk images on SD 22 | // 13-JUN-2024 Release 1.6.1 use a real boot ROM for booting the machine 23 | // 24 | 25 | // 64 KB unbanked memory in FRAM 26 | // we want hardware SPI 27 | Adafruit_FRAM_SPI fram = Adafruit_FRAM_SPI(FRAM_CS); 28 | 29 | // boot ROM 30 | #define MEMSIZE 256 31 | #include "bootrom.h" 32 | 33 | // file handle, at any time we have only one file open 34 | FatFile sd_file; 35 | 36 | // transfer buffer for disk/memory transfers 37 | static BYTE dsk_buf[SEC_SZ]; 38 | 39 | // setup FRAM 40 | void init_memory(void) 41 | { 42 | uint32_t i; 43 | 44 | // copy the boot ROM into the top page of 8080 memory 45 | for (i = 0; i < 256; i++) 46 | fram.write8(0xff00 + i, pgm_read_byte_near(code + i)); 47 | } 48 | 49 | // read a byte from 8080 CPU memory at address addr 50 | static inline BYTE memrdr(WORD addr) 51 | { 52 | return fram.read8((uint32_t) addr); 53 | } 54 | 55 | // write a byte data into 8080 CPU RAM at address addr 56 | static inline void memwrt(WORD addr, BYTE data) 57 | { 58 | if (addr < 0xff00) // top memory page write protected, boot ROM 59 | fram.write8((uint32_t) addr, data); 60 | } 61 | 62 | #if 0 // for debugging, not enough memory left 63 | // memory dump 64 | void mem_dump(WORD addr) 65 | { 66 | int i, j; 67 | WORD a = addr; 68 | BYTE c; 69 | 70 | for (j = 1; j <= 16; j++) { 71 | for (i = 1; i <= 16; i++) { 72 | c = fram.read8(a++); 73 | if (c < 0x10) 74 | Serial.print(F("0")); 75 | Serial.print(c, HEX); 76 | Serial.print(F(" ")); 77 | } 78 | Serial.println(); 79 | } 80 | } 81 | #endif 82 | 83 | void complain(void) 84 | { 85 | Serial.println(F("File not found\n")); 86 | } 87 | 88 | // load a file 'name' into FRAM 89 | void load_file(char *name) 90 | { 91 | uint32_t i = 0; 92 | unsigned char c; 93 | char SFN[25]; 94 | 95 | strcpy(SFN, "/CODE80/"); 96 | strcat(SFN, name); 97 | strcat(SFN, ".BIN"); 98 | 99 | if (!sd_file.openExistingSFN(SFN)) { 100 | complain(); 101 | return; 102 | } 103 | 104 | while (sd_file.read(&c, 1)) 105 | fram.write8(i++, c); 106 | 107 | sd_file.close(); 108 | Serial.println(); 109 | } 110 | 111 | // mount a disk image 'name' on disk 'drive' 112 | void mount_disk(int8_t drive, char *name) 113 | { 114 | char SFN[22]; 115 | 116 | strcpy(SFN, "/DISKS80/"); 117 | strcat(SFN, name); 118 | strcat(SFN, ".DSK"); 119 | 120 | if (!sd_file.openExistingSFN(SFN)) { 121 | complain(); 122 | return; 123 | } 124 | 125 | sd_file.close(); 126 | strcpy(disks[drive], SFN); 127 | Serial.println(); 128 | } 129 | 130 | // prepare I/O for sector read and write routines 131 | BYTE prep_io(int8_t drive, int8_t track, int8_t sector, WORD addr) 132 | { 133 | uint32_t pos; 134 | 135 | // check if drive in range 136 | if ((drive < 0) || (drive > 1)) 137 | return FDC_STAT_DISK; 138 | 139 | // check if track and sector in range 140 | if (track > TRK) 141 | return FDC_STAT_TRACK; 142 | if ((sector < 1) || (sector > SPT)) 143 | return FDC_STAT_SEC; 144 | 145 | // check if DMA address in range 146 | if (addr > 0xff7f) 147 | return FDC_STAT_DMAADR; 148 | 149 | // check if disk in drive 150 | if (!strlen(disks[drive])) { 151 | return FDC_STAT_NODISK; 152 | } 153 | 154 | // open file with the disk image 155 | if (!sd_file.openExistingSFN(disks[drive])) { 156 | return FDC_STAT_NODISK; 157 | } 158 | 159 | // seek to track/sector 160 | pos = (((uint32_t) track * (uint32_t) SPT) + sector - 1) * SEC_SZ; 161 | if (!sd_file.seekSet(pos)) { 162 | sd_file.close(); 163 | return FDC_STAT_SEEK; 164 | } 165 | 166 | return FDC_STAT_OK; 167 | } 168 | 169 | // read from drive a sector on track into FRAM addr 170 | BYTE read_sec(int8_t drive, int8_t track, int8_t sector, WORD addr) 171 | { 172 | BYTE stat; 173 | 174 | // prepare for sector read 175 | if ((stat = prep_io(drive, track, sector, addr)) != FDC_STAT_OK) 176 | return stat; 177 | 178 | // read sector into FRAM 179 | if (sd_file.read(&dsk_buf[0], SEC_SZ) != SEC_SZ) { 180 | sd_file.close(); 181 | return FDC_STAT_READ; 182 | } 183 | sd_file.close(); 184 | if (!fram.write(addr, &dsk_buf[0], SEC_SZ)) { 185 | return FDC_STAT_DMA; 186 | } 187 | 188 | return FDC_STAT_OK; 189 | } 190 | 191 | // write to drive a sector on track from FRAM addr 192 | BYTE write_sec(int8_t drive, int8_t track, int8_t sector, WORD addr) 193 | { 194 | BYTE stat; 195 | 196 | // prepare for sector write 197 | if ((stat = prep_io(drive, track, sector, addr)) != FDC_STAT_OK) 198 | return stat; 199 | 200 | // write sector to disk image 201 | if (!fram.read(addr, &dsk_buf[0], SEC_SZ)) { 202 | sd_file.close(); 203 | return FDC_STAT_DMA; 204 | } 205 | if (sd_file.write(&dsk_buf[0], SEC_SZ) != SEC_SZ) { 206 | sd_file.close(); 207 | return FDC_STAT_WRITE; 208 | } 209 | 210 | sd_file.close(); 211 | return FDC_STAT_OK; 212 | } 213 | 214 | // get FDC command from FRAM 215 | void get_fdccmd(BYTE *cmd, WORD addr) 216 | { 217 | fram.read(addr, cmd, 4); 218 | } 219 | -------------------------------------------------------------------------------- /roms/Makefile: -------------------------------------------------------------------------------- 1 | Z80ASMDIR = ~/work/z80pack/z80asm 2 | Z80ASM = $(Z80ASMDIR)/z80asm 3 | Z80ASMFLAGS = -l -T -sn -p0 4 | 5 | all: ../bootrom.h 6 | 7 | ../bootrom.h: bootrom.asm 8 | $(Z80ASM) $(Z80ASMFLAGS) -fc -o../bootrom.h $< 9 | -------------------------------------------------------------------------------- /roms/bootrom.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; boot ROM for z80pack machines using SD-FDC 3 | ; use 8080 instructions only, so that it runs 4 | ; on all our CPU's 5 | ; 6 | ; Copyright (C) 2024 by Udo Munk 7 | ; 8 | ORG 0FF00H ;ROM in upper most memory page 9 | ; 10 | ; I/O ports 11 | ; 12 | FDC EQU 4 ;FDC port 13 | ; 14 | FDCMD EQU 0F000H ;command bytes for the FDC 15 | ; 16 | DI ;disable interrupts 17 | ; 18 | XOR A ;zero A 19 | LD (FDCMD),A ;track 0 20 | LD (FDCMD+2),A ;DMA address low 21 | LD (FDCMD+3),A ;DMA address high 22 | LD A,1 ;sector 1 23 | LD (FDCMD+1),A 24 | ; 25 | LD A,10H ;setup command for FDC 26 | OUT (FDC),A 27 | LD A,FDCMD AND 0FFH 28 | OUT (FDC),A 29 | LD A,FDCMD SHR 8 30 | OUT (FDC),A 31 | ; 32 | LD A,20H ;read sector 1 on track 0 from drive 0 33 | OUT (FDC),A 34 | IN A,(FDC) ;get FDC result 35 | OR A ;zero ? 36 | JP Z,0 ;if yes jump to boot code loaded @ 0 37 | ; 38 | CP 2 ;some problem, lets see, no disk in drive? 39 | JP Z,0 ;yes, so hopefully they loaded some code 40 | HALT ;if not there is some serious problem 41 | ; 42 | END ;of ROM 43 | -------------------------------------------------------------------------------- /roms/bootrom.lis: -------------------------------------------------------------------------------- 1 | Z80/8080-Macro-Assembler Release 2.0 2 | 3 | LOC OBJECT CODE LINE STMT SOURCE CODE 4 | 1 1 ; 5 | 2 2 ; boot ROM for z80pack machines using SD-FDC 6 | 3 3 ; use 8080 instructions only, so that it runs 7 | 4 4 ; on all our CPU's 8 | 5 5 ; 9 | 6 6 ; Copyright (C) 2024 by Udo Munk 10 | 7 7 ; 11 | 8 8 ORG 0FF00H ;ROM in upper most memory page 12 | 9 9 ; 13 | 10 10 ; I/O ports 14 | 11 11 ; 15 | 0004 = 12 12 FDC EQU 4 ;FDC port 16 | 13 13 ; 17 | f000 = 14 14 FDCMD EQU 0F000H ;command bytes for the FDC 18 | 15 15 ; 19 | ff00 f3 16 16 DI ;disable interrupts 20 | 17 17 ; 21 | ff01 af 18 18 XOR A ;zero A 22 | ff02 32 00 f0 19 19 LD (FDCMD),A ;track 0 23 | ff05 32 02 f0 20 20 LD (FDCMD+2),A ;DMA address low 24 | ff08 32 03 f0 21 21 LD (FDCMD+3),A ;DMA address high 25 | ff0b 3e 01 22 22 LD A,1 ;sector 1 26 | ff0d 32 01 f0 23 23 LD (FDCMD+1),A 27 | 24 24 ; 28 | ff10 3e 10 25 25 LD A,10H ;setup command for FDC 29 | ff12 d3 04 26 26 OUT (FDC),A 30 | ff14 3e 00 27 27 LD A,FDCMD AND 0FFH 31 | ff16 d3 04 28 28 OUT (FDC),A 32 | ff18 3e f0 29 29 LD A,FDCMD SHR 8 33 | ff1a d3 04 30 30 OUT (FDC),A 34 | 31 31 ; 35 | ff1c 3e 20 32 32 LD A,20H ;read sector 1 on track 0 from drive 0 36 | ff1e d3 04 33 33 OUT (FDC),A 37 | ff20 db 04 34 34 IN A,(FDC) ;get FDC result 38 | ff22 b7 35 35 OR A ;zero ? 39 | ff23 ca 00 00 36 36 JP Z,0 ;if yes jump to boot code loaded @ 0 40 | 37 37 ; 41 | ff26 fe 02 38 38 CP 2 ;some problem, lets see, no disk in drive? 42 | ff28 ca 00 00 39 39 JP Z,0 ;yes, so hopefully they loaded some code 43 | ff2b 76 40 40 HALT ;if not there is some serious problem 44 | 41 41 ; 45 | ff2c 42 42 END ;of ROM 46 | 47 | Symbol table 48 | 49 | FDC 0004 FDCMD f000 50 | -------------------------------------------------------------------------------- /sd-fdc.h: -------------------------------------------------------------------------------- 1 | // 2 | // Intel 8080 CPU emulation on an Arduino Nano 3 | // derived from Z80PACK 4 | // Copyright 2024, Udo Munk 5 | // 6 | // This FDC does not emulate any existing hardware, 7 | // special not these with some LSI chip, it is designed 8 | // for bare metal with disk images on MicroSD and such. 9 | // It needs one single I/O port, send command to the port, 10 | // read result what it did, done. It does DMA transfer of 11 | // a complete sector. It has a rock solid state engine, 12 | // that can not get stuck by some software sending random 13 | // data, is all ignored. It is platform independend, the 14 | // platform must provide the low level functions, which 15 | // depend on what storage hardware actually is used. 16 | // 17 | // History: 18 | // 23-MAY-2024 implemented FDC, CP/M boot code & CBIOS 19 | // 20 | 21 | // offsets in disk command descriptor 22 | #define DD_TRACK 0 // track number 23 | #define DD_SECTOR 1 // sector number 24 | #define DD_DMAL 2 // DMA address low 25 | #define DD_DMAH 3 // DMA address high 26 | 27 | // controller commands: MSB command, LSB drive number 28 | #define FDC_SETDD 0x10 // set address of disk descriptor from 29 | // following two OUT's 30 | #define FDC_READ 0x20 // read sector from disk 31 | #define FDC_WRITE 0x40 // write sector to disk 32 | 33 | static BYTE fdc_cmd[4]; // FDC command 34 | static BYTE fdc_state; // state of the FDC state machine 35 | static BYTE fdc_stat; // status of last FDC command 36 | static WORD fdc_cmd_addr; // address of the disk command 37 | static WORD fdc_dma_addr; // address for a DMA transfer 38 | 39 | // I/O out interface to the 8080 CPU 40 | const void fdc_out(BYTE data) 41 | { 42 | fdc_stat = FDC_STAT_OK; 43 | 44 | // FDC state machine 45 | switch (fdc_state) { 46 | case 0: // start of command phase 47 | 48 | // FDC command dispatcher, command in MSB, drive in LSB 49 | switch (data & 0xf0) { 50 | case FDC_SETDD: 51 | fdc_state++; 52 | break; 53 | 54 | case FDC_READ: 55 | get_fdccmd(&fdc_cmd[0], fdc_cmd_addr); 56 | fdc_dma_addr = fdc_cmd[DD_DMAL] + (fdc_cmd[DD_DMAH] << 8); 57 | fdc_stat = read_sec(data & 0x0f, fdc_cmd[DD_TRACK], fdc_cmd[DD_SECTOR], 58 | fdc_dma_addr); 59 | break; 60 | 61 | case FDC_WRITE: 62 | get_fdccmd(&fdc_cmd[0], fdc_cmd_addr); 63 | fdc_dma_addr = fdc_cmd[DD_DMAL] + (fdc_cmd[DD_DMAH] << 8); 64 | fdc_stat = write_sec(data & 0x0f, fdc_cmd[DD_TRACK], fdc_cmd[DD_SECTOR], 65 | fdc_dma_addr); 66 | break; 67 | 68 | default: 69 | break; 70 | } 71 | break; 72 | 73 | case 1: // LSB of disk command address 74 | fdc_cmd_addr = data; 75 | fdc_state++; 76 | break; 77 | 78 | case 2: // MSB of disk command address 79 | fdc_cmd_addr += data << 8; 80 | fdc_state = 0; 81 | break; 82 | 83 | default: 84 | break; 85 | } 86 | } 87 | 88 | // I/O in interface to the 8080 CPU 89 | const BYTE fdc_in(void) 90 | { 91 | return fdc_stat; 92 | } 93 | -------------------------------------------------------------------------------- /simcore.h: -------------------------------------------------------------------------------- 1 | // 2 | // Intel 8080 CPU emulation on an Arduino Nano 3 | // derived from Z80PACK 4 | // Copyright 2024, Udo Munk 5 | // 6 | // History: 7 | // 04-MAY-2024 Release 1.0, initial implementation 8 | // 17-MAY-2024 use X and Y as names for the undocumented flag bits 9 | // 22-MAY-2024 added disk definitions 10 | // 11 | 12 | // bit definitions for the 8080 CPU flags 13 | #define S_FLAG 128 14 | #define Z_FLAG 64 15 | #define Y_FLAG 32 16 | #define H_FLAG 16 17 | #define X_FLAG 8 18 | #define P_FLAG 4 19 | #define N_FLAG 2 20 | #define C_FLAG 1 21 | 22 | #define S_SHIFT 7 23 | #define Z_SHIFT 6 24 | #define Y_SHIFT 5 25 | #define H_SHIFT 4 26 | #define X_SHIFT 3 27 | #define P_SHIFT 2 28 | #define N_SHIFT 1 29 | #define C_SHIFT 0 30 | 31 | // possible states of the 8080 CPU 32 | enum CPUState { Running = 1, Halted = 2, Interrupted = 3 }; 33 | 34 | // floppy disk definition 35 | #define SEC_SZ 128 // sector size 36 | #define SPT 26 // sectors per track 37 | #define TRK 77 // number of tracks 38 | 39 | // path name for 2 disk images /DISKS80/filename.DSK 40 | char disks[2][22]; 41 | 42 | // FDC status codes 43 | #define FDC_STAT_OK 0 // command executed successfull 44 | #define FDC_STAT_DISK 1 // disk drive out of range 45 | #define FDC_STAT_NODISK 2 // disk file open error 46 | #define FDC_STAT_SEEK 3 // disk file seek error 47 | #define FDC_STAT_READ 4 // disk file read error 48 | #define FDC_STAT_WRITE 5 // disk file write error 49 | #define FDC_STAT_DMA 6 // DMA memory read/write error 50 | #define FDC_STAT_DMAADR 7 // DMA address out of range (cannot wrap 0xffff -> 0) 51 | #define FDC_STAT_TRACK 8 // track # > TRK 52 | #define FDC_STAT_SEC 9 // sector # < 1 or > SPT 53 | -------------------------------------------------------------------------------- /src-examples/Makefile: -------------------------------------------------------------------------------- 1 | all : serial.bin banner.bin tb.bin test8080.bin 2 | @echo 3 | @echo "Done." 4 | @echo 5 | 6 | clean : 7 | rm -f serial.bin banner.bin tb.bin test8080.bin 8 | 9 | serial.bin : serial.asm 10 | z80asm -8 -fb -l -sn -p0 $? 11 | 12 | banner.bin : banner.asm 13 | z80asm -8 -fb -l -sn -p0 $? 14 | 15 | tb.bin : tb.asm 16 | z80asm -8 -fb -l -sn -p0 -x $? 17 | 18 | test8080.bin: test8080.asm 19 | z80asm -8 -doncpm=0 -fb -l -sn -p0 $? 20 | -------------------------------------------------------------------------------- /src-examples/README: -------------------------------------------------------------------------------- 1 | This directory contains 8080 code examples for the Arduino 8080. 2 | I have assembled the sources with z80asm from z80pack, but you 3 | can use any 8080 assembler of course. 4 | 5 | With z80pack z80sim examples like these can be debugged under 6 | control of an In Circuit Emulator (debug probe), the serial 7 | port implemented in the latest release is identical. 8 | 9 | On your MicroSD card create a directory with the name CODE80 and 10 | copy the "example".bin files into it. When you run the machine 11 | you can select which file to load, just enter the filename 12 | without the directory and without the extension. 13 | 14 | The examples are: 15 | serial.bin - shows how to do serial I/O on the machine 16 | banner.bin - print a cool banner on the terminal and then halt CPU 17 | test8080.bin - Kelly Smith 8080 CPU test program 18 | tb.bin - Li-Chen Wang's famous TINY BASIC for 8080. 19 | basicex.bin - 16 KB Microsoft BASIC for Altair 8800 20 | -------------------------------------------------------------------------------- /src-examples/banner.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Example program for the Arduino 8080 3 | ; Print cool banner on the serial I/O port 4 | ; 5 | ; Udo Munk, April 2024 6 | ; 7 | 8 | .8080 ; we use Intel 8080 syntax 9 | ORG 0000H ; starting at memory location 0 10 | 11 | CR EQU 13 ; carriage return 12 | LF EQU 10 ; line feed 13 | 14 | CONST EQU 0 ; console status port 15 | CONDA EQU 1 ; console data port 16 | 17 | DI ; disable interrupts 18 | LXI SP,STACK ; set stack 19 | 20 | LXI D,BANNER ; DE points to the array with text to send 21 | MVI B,7 ; 7 lines to print 22 | LOOP CALL PRTLN ; print a line on console 23 | DCR B ; next one 24 | JNZ LOOP 25 | HLT ; done 26 | 27 | PRTLN IN CONST ; get console status into A 28 | RLC ; is output ready? 29 | JC PRTLN ; if not try again 30 | PRTLN1 LDAX D ; get next character into A 31 | INX D ; increment pointer to the text 32 | ORA A ; 0 termination? 33 | RZ ; if so we are done 34 | OUT CONDA ; output character in A to I/O port 1 (tty) 35 | JMP PRTLN1 ; and repeat until done 36 | 37 | BANNER DEFM " ##### ### ##### ### ##### ### # #" 38 | DEFB CR,LF,0 39 | DEFM "# # # # # # # # # # # ## ##" 40 | DEFB CR,LF,0 41 | DEFM "# # # # # # # # # # # # # #" 42 | DEFB CR,LF,0 43 | DEFM " ##### # # ##### # # ##### ##### # # # #" 44 | DEFB CR,LF,0 45 | DEFM "# # # # # # # # # # # #" 46 | DEFB CR,LF,0 47 | DEFM "# # # # # # # # # # # # #" 48 | DEFB CR,LF,0 49 | DEFM " ##### ### ##### ### ##### ### # #" 50 | DEFB CR,LF,0 51 | 52 | DS 20 ; space for the stack 53 | STACK 54 | 55 | END ; of program 56 | -------------------------------------------------------------------------------- /src-examples/banner.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udo-munk/Arduino8080/f675ec03fe1c7fd80e3d3026e4b6d82740a2945e/src-examples/banner.bin -------------------------------------------------------------------------------- /src-examples/banner.lis: -------------------------------------------------------------------------------- 1 | Z80/8080-Macro-Assembler Release 2.0 Mon May 20 09:27:10 2024 2 | 3 | LOC OBJECT CODE LINE STMT SOURCE CODE 4 | 1 1 ; 5 | 2 2 ; Example program for the Arduino 8080 6 | 3 3 ; Print cool banner on the serial I/O port 7 | 4 4 ; 8 | 5 5 ; Udo Munk, April 2024 9 | 6 6 ; 10 | 7 7 11 | 8 8 .8080 ; we use Intel 8080 syntax 12 | 9 9 ORG 0000H ; starting at memory location 0 13 | 10 10 14 | 000d = 11 11 CR EQU 13 ; carriage return 15 | 000a = 12 12 LF EQU 10 ; line feed 16 | 13 13 17 | 0000 = 14 14 CONST EQU 0 ; console status port 18 | 0001 = 15 15 CONDA EQU 1 ; console data port 19 | 16 16 20 | 0000 f3 17 17 DI ; disable interrupts 21 | 0001 31 09 02 18 18 LXI SP,STACK ; set stack 22 | 19 19 23 | 0004 11 20 00 20 20 LXI D,BANNER ; DE points to the array with text to send 24 | 0007 06 07 21 21 MVI B,7 ; 7 lines to print 25 | 0009 cd 11 00 22 22 LOOP CALL PRTLN ; print a line on console 26 | 000c 05 23 23 DCR B ; next one 27 | 000d c2 09 00 24 24 JNZ LOOP 28 | 0010 76 25 25 HLT ; done 29 | 26 26 30 | 0011 db 00 27 27 PRTLN IN CONST ; get console status into A 31 | 0013 07 28 28 RLC ; is output ready? 32 | 0014 da 11 00 29 29 JC PRTLN ; if not try again 33 | 0017 1a 30 30 PRTLN1 LDAX D ; get next character into A 34 | 0018 13 31 31 INX D ; increment pointer to the text 35 | 0019 b7 32 32 ORA A ; 0 termination? 36 | 001a c8 33 33 RZ ; if so we are done 37 | 001b d3 01 34 34 OUT CONDA ; output character in A to I/O port 1 (tty) 38 | 001d c3 17 00 35 35 JMP PRTLN1 ; and repeat until done 39 | 36 36 40 | 0020 20 23 23 23 37 37 BANNER DEFM " ##### ### ##### ### ##### ### # #" 41 | 0024 23 23 20 20 37 38 42 | 0028 20 20 23 23 37 39 43 | 002c 23 20 20 20 37 40 44 | 0030 20 20 23 23 37 41 45 | 0034 23 23 23 20 37 42 46 | 0038 20 20 20 23 37 43 47 | 003c 23 23 20 20 37 44 48 | 0040 20 20 20 20 37 45 49 | 0044 20 20 20 20 37 46 50 | 0048 20 20 23 23 37 47 51 | 004c 23 23 23 20 37 48 52 | 0050 20 20 20 23 37 49 53 | 0054 23 23 20 20 37 50 54 | 0058 20 23 20 20 37 51 55 | 005c 20 20 20 23 37 52 56 | 0060 0d 0a 00 38 53 DEFB CR,LF,0 57 | 0063 23 20 20 20 39 54 DEFM "# # # # # # # # # # # ## ##" 58 | 0067 20 20 23 20 39 55 59 | 006b 20 23 20 20 39 56 60 | 006f 20 23 20 20 39 57 61 | 0073 20 23 20 20 39 58 62 | 0077 20 20 20 23 39 59 63 | 007b 20 20 23 20 39 60 64 | 007f 20 20 23 20 39 61 65 | 0083 20 20 20 20 39 62 66 | 0087 20 20 20 20 39 63 67 | 008b 20 23 20 20 39 64 68 | 008f 20 20 20 23 39 65 69 | 0093 20 20 20 20 39 66 70 | 0097 23 20 20 20 39 67 71 | 009b 20 23 23 20 39 68 72 | 009f 20 20 23 23 39 69 73 | 00a3 0d 0a 00 40 70 DEFB CR,LF,0 74 | 00a6 23 20 20 20 41 71 DEFM "# # # # # # # # # # # # # #" 75 | 00aa 20 20 23 20 41 72 76 | 00ae 23 20 20 20 41 73 77 | 00b2 20 20 23 20 41 74 78 | 00b6 20 23 20 20 41 75 79 | 00ba 20 20 20 23 41 76 80 | 00be 20 23 20 20 41 77 81 | 00c2 20 20 20 23 41 78 82 | 00c6 20 20 20 20 41 79 83 | 00ca 20 20 20 20 41 80 84 | 00ce 20 23 20 20 41 81 85 | 00d2 20 20 20 20 41 82 86 | 00d6 20 20 20 20 41 83 87 | 00da 23 20 20 20 41 84 88 | 00de 20 23 20 23 41 85 89 | 00e2 20 23 20 23 41 86 90 | 00e6 0d 0a 00 42 87 DEFB CR,LF,0 91 | 00e9 20 23 23 23 43 88 DEFM " ##### # # ##### # # ##### ##### # # # #" 92 | 00ed 23 23 20 20 43 89 93 | 00f1 23 20 20 20 43 90 94 | 00f5 20 20 23 20 43 91 95 | 00f9 20 20 23 23 43 92 96 | 00fd 23 23 23 20 43 93 97 | 0101 20 23 20 20 43 94 98 | 0105 20 20 20 23 43 95 99 | 0109 20 20 23 23 43 96 100 | 010d 23 23 23 20 43 97 101 | 0111 20 20 23 23 43 98 102 | 0115 23 23 23 20 43 99 103 | 0119 20 20 20 20 43 100 104 | 011d 23 20 20 20 43 101 105 | 0121 20 23 20 20 43 102 106 | 0125 23 20 20 23 43 103 107 | 0129 0d 0a 00 44 104 DEFB CR,LF,0 108 | 012c 23 20 20 20 45 105 DEFM "# # # # # # # # # # # #" 109 | 0130 20 20 23 20 45 106 110 | 0134 23 20 20 20 45 107 111 | 0138 20 20 23 20 45 108 112 | 013c 20 23 20 20 45 109 113 | 0140 20 20 20 23 45 110 114 | 0144 20 23 20 20 45 111 115 | 0148 20 20 20 23 45 112 116 | 014c 20 20 20 20 45 113 117 | 0150 20 20 20 20 45 114 118 | 0154 20 20 20 20 45 115 119 | 0158 20 20 20 23 45 116 120 | 015c 20 20 20 20 45 117 121 | 0160 23 20 20 20 45 118 122 | 0164 20 23 20 20 45 119 123 | 0168 20 20 20 23 45 120 124 | 016c 0d 0a 00 46 121 DEFB CR,LF,0 125 | 016f 23 20 20 20 47 122 DEFM "# # # # # # # # # # # # #" 126 | 0173 20 20 23 20 47 123 127 | 0177 20 23 20 20 47 124 128 | 017b 20 23 20 20 47 125 129 | 017f 20 23 20 20 47 126 130 | 0183 20 20 20 23 47 127 131 | 0187 20 20 23 20 47 128 132 | 018b 20 20 23 20 47 129 133 | 018f 20 20 20 20 47 130 134 | 0193 20 20 20 20 47 131 135 | 0197 20 23 20 20 47 132 136 | 019b 20 20 20 23 47 133 137 | 019f 20 20 20 20 47 134 138 | 01a3 23 20 20 20 47 135 139 | 01a7 20 23 20 20 47 136 140 | 01ab 20 20 20 23 47 137 141 | 01af 0d 0a 00 48 138 DEFB CR,LF,0 142 | 01b2 20 23 23 23 49 139 DEFM " ##### ### ##### ### ##### ### # #" 143 | 01b6 23 23 20 20 49 140 144 | 01ba 20 20 23 23 49 141 145 | 01be 23 20 20 20 49 142 146 | 01c2 20 20 23 23 49 143 147 | 01c6 23 23 23 20 49 144 148 | 01ca 20 20 20 23 49 145 149 | 01ce 23 23 20 20 49 146 150 | 01d2 20 20 20 20 49 147 151 | 01d6 20 20 20 20 49 148 152 | 01da 20 20 23 23 49 149 153 | 01de 23 23 23 20 49 150 154 | 01e2 20 20 20 23 49 151 155 | 01e6 23 23 20 20 49 152 156 | 01ea 20 23 20 20 49 153 157 | 01ee 20 20 20 23 49 154 158 | 01f2 0d 0a 00 50 155 DEFB CR,LF,0 159 | 51 156 160 | 01f5 52 157 DS 20 ; space for the stack 161 | 0209 53 158 STACK 162 | 54 159 163 | 0209 55 160 END ; of program 164 | 165 | Symbol table 166 | 167 | BANNER 0020 CONDA 0001 CONST 0000 CR 000d LF 000a 168 | LOOP 0009 PRTLN 0011 PRTLN1 0017 STACK 0209 169 | -------------------------------------------------------------------------------- /src-examples/basicex.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udo-munk/Arduino8080/f675ec03fe1c7fd80e3d3026e4b6d82740a2945e/src-examples/basicex.bin -------------------------------------------------------------------------------- /src-examples/serial.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Example program for the Arduino 8080 3 | ; Do some serial I/O 4 | ; 5 | ; Udo Munk, April 2024 6 | ; 7 | 8 | .8080 ; we use 8080 syntax 9 | 10 | TTYSTA EQU 0 ; tty status port 11 | TTYDAT EQU 1 ; tty data port 12 | 13 | CR EQU 13 ; carriage return 14 | LF EQU 10 ; line feed 15 | 16 | ORG 0000H ; starting at memory location 0 17 | 18 | DI ; disable interrupts 19 | LXI SP,STACK ; set stack 20 | 21 | ; print instructions 22 | LXI H,TEXT ; HL points to text to send 23 | LOOP MOV A,M ; get next character into A 24 | ORA A ; is it 0 termination? 25 | JZ ECHO ; if yes done 26 | CALL OUTCH ; output character to tty 27 | INX H ; point to next character 28 | JMP LOOP ; and again 29 | 30 | ; now echo what is typed 31 | ECHO IN TTYSTA ; get tty status 32 | RRC ; is there any input? 33 | JC ECHO ; wait if not 34 | IN TTYDAT ; get character from tty into A 35 | CALL OUTCH ; echo it 36 | CPI 'X' ; is it a X? 37 | JZ DONE ; done if so 38 | CPI CR ; is it a carriage return? 39 | JNZ ECHO ; no, go on 40 | MVI A,LF ; else also send line feed 41 | CALL OUTCH 42 | JMP ECHO ; repeat 43 | 44 | DONE HLT ; done 45 | 46 | ; output character in A to tty 47 | OUTCH PUSH PSW ; save character in A 48 | OUTCH1 IN TTYSTA ; get tty status 49 | RLC ; output ready? 50 | JC OUTCH1 ; wait if not 51 | POP PSW ; get character back into A 52 | OUT TTYDAT ; send character to tty 53 | RET 54 | 55 | TEXT DEFM "Hello from the 8080 CPU" 56 | DEFB CR,LF 57 | DEFM "I will echo what you type, type X if done" 58 | DEFB CR,LF,0 59 | 60 | DS 20 ; space for the stack 61 | STACK 62 | 63 | END 64 | -------------------------------------------------------------------------------- /src-examples/serial.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udo-munk/Arduino8080/f675ec03fe1c7fd80e3d3026e4b6d82740a2945e/src-examples/serial.bin -------------------------------------------------------------------------------- /src-examples/serial.lis: -------------------------------------------------------------------------------- 1 | Z80/8080-Macro-Assembler Release 2.0 Mon May 20 09:27:10 2024 2 | 3 | LOC OBJECT CODE LINE STMT SOURCE CODE 4 | 1 1 ; 5 | 2 2 ; Example program for the Arduino 8080 6 | 3 3 ; Do some serial I/O 7 | 4 4 ; 8 | 5 5 ; Udo Munk, April 2024 9 | 6 6 ; 10 | 7 7 11 | 8 8 .8080 ; we use 8080 syntax 12 | 9 9 13 | 0000 = 10 10 TTYSTA EQU 0 ; tty status port 14 | 0001 = 11 11 TTYDAT EQU 1 ; tty data port 15 | 12 12 16 | 000d = 13 13 CR EQU 13 ; carriage return 17 | 000a = 14 14 LF EQU 10 ; line feed 18 | 15 15 19 | 16 16 ORG 0000H ; starting at memory location 0 20 | 17 17 21 | 0000 f3 18 18 DI ; disable interrupts 22 | 0001 31 95 00 19 19 LXI SP,STACK ; set stack 23 | 20 20 24 | 21 21 ; print instructions 25 | 0004 21 3c 00 22 22 LXI H,TEXT ; HL points to text to send 26 | 0007 7e 23 23 LOOP MOV A,M ; get next character into A 27 | 0008 b7 24 24 ORA A ; is it 0 termination? 28 | 0009 ca 13 00 25 25 JZ ECHO ; if yes done 29 | 000c cd 31 00 26 26 CALL OUTCH ; output character to tty 30 | 000f 23 27 27 INX H ; point to next character 31 | 0010 c3 07 00 28 28 JMP LOOP ; and again 32 | 29 29 33 | 30 30 ; now echo what is typed 34 | 0013 db 00 31 31 ECHO IN TTYSTA ; get tty status 35 | 0015 0f 32 32 RRC ; is there any input? 36 | 0016 da 13 00 33 33 JC ECHO ; wait if not 37 | 0019 db 01 34 34 IN TTYDAT ; get character from tty into A 38 | 001b cd 31 00 35 35 CALL OUTCH ; echo it 39 | 001e fe 58 36 36 CPI 'X' ; is it a X? 40 | 0020 ca 30 00 37 37 JZ DONE ; done if so 41 | 0023 fe 0d 38 38 CPI CR ; is it a carriage return? 42 | 0025 c2 13 00 39 39 JNZ ECHO ; no, go on 43 | 0028 3e 0a 40 40 MVI A,LF ; else also send line feed 44 | 002a cd 31 00 41 41 CALL OUTCH 45 | 002d c3 13 00 42 42 JMP ECHO ; repeat 46 | 43 43 47 | 0030 76 44 44 DONE HLT ; done 48 | 45 45 49 | 46 46 ; output character in A to tty 50 | 0031 f5 47 47 OUTCH PUSH PSW ; save character in A 51 | 0032 db 00 48 48 OUTCH1 IN TTYSTA ; get tty status 52 | 0034 07 49 49 RLC ; output ready? 53 | 0035 da 32 00 50 50 JC OUTCH1 ; wait if not 54 | 0038 f1 51 51 POP PSW ; get character back into A 55 | 0039 d3 01 52 52 OUT TTYDAT ; send character to tty 56 | 003b c9 53 53 RET 57 | 54 54 58 | 003c 48 65 6c 6c 55 55 TEXT DEFM "Hello from the 8080 CPU" 59 | 0040 6f 20 66 72 55 56 60 | 0044 6f 6d 20 74 55 57 61 | 0048 68 65 20 38 55 58 62 | 004c 30 38 30 20 55 59 63 | 0050 43 50 55 55 60 64 | 0053 0d 0a 56 61 DEFB CR,LF 65 | 0055 49 20 77 69 57 62 DEFM "I will echo what you type, type X if done" 66 | 0059 6c 6c 20 65 57 63 67 | 005d 63 68 6f 20 57 64 68 | 0061 77 68 61 74 57 65 69 | 0065 20 79 6f 75 57 66 70 | 0069 20 74 79 70 57 67 71 | 006d 65 2c 20 74 57 68 72 | 0071 79 70 65 20 57 69 73 | 0075 58 20 69 66 57 70 74 | 0079 20 64 6f 6e 57 71 75 | 007d 65 57 72 76 | 007e 0d 0a 00 58 73 DEFB CR,LF,0 77 | 59 74 78 | 0081 60 75 DS 20 ; space for the stack 79 | 0095 61 76 STACK 80 | 62 77 81 | 0095 63 78 END 82 | 83 | Symbol table 84 | 85 | CR 000d DONE 0030 ECHO 0013 LF 000a LOOP 0007 86 | OUTCH 0031 OUTCH1 0032 STACK 0095 TEXT 003c TTYDAT 0001 87 | TTYSTA 0000 88 | -------------------------------------------------------------------------------- /src-examples/tb.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udo-munk/Arduino8080/f675ec03fe1c7fd80e3d3026e4b6d82740a2945e/src-examples/tb.bin -------------------------------------------------------------------------------- /src-examples/test8080.asm: -------------------------------------------------------------------------------- 1 | ;*********************************************************************** 2 | ; MICROCOSM ASSOCIATES 8080/8085 CPU DIAGNOSTIC VERSION 1.0 (C) 1980 3 | ;*********************************************************************** 4 | ; 5 | ;DONATED TO THE "SIG/M" CP/M USER'S GROUP BY: 6 | ;KELLY SMITH, MICROCOSM ASSOCIATES 7 | ;3055 WACO AVENUE 8 | ;SIMI VALLEY, CALIFORNIA, 93065 9 | ;(805) 527-9321 (MODEM, CP/M-NET (TM)) 10 | ;(805) 527-0518 (VERBAL) 11 | ; 12 | ; 03-MAY-2024 Thomas Eberhardt Add z80pack CPU switching 13 | ; 18-MAY-2024 Thomas Eberhardt Add bare emulator/machine support 14 | ; 15 | 16 | IFNDEF ONCPM 17 | ONCPM EQU 1 ;1 = RUNS ON CPM, 0 = BARE EMULATOR/MACHINE 18 | ENDIF 19 | IOCHAR EQU 01H ;I/O PORT FOR CHARACTER OUTPUT (BARE) 20 | 21 | IF ONCPM 22 | ORG 00100H 23 | ELSE 24 | ORG 0H 25 | ENDIF 26 | 27 | START: XRA A 28 | STA SWFLAG ; CLEAR CPU SWITCHED FLAG 29 | DCR A 30 | JPE START1 ; EVEN PARITY -> IS 8080 31 | CALL SW8080 ; SWITCH TO 8080 32 | MVI A,0DH 33 | CALL PCHAR 34 | MVI A,0AH 35 | CALL PCHAR 36 | 37 | START1: LXI H, LOLZ 38 | CALL MSG 39 | JMP CPU ;JUMP TO 8080 CPU DIAGNOSTIC 40 | ; 41 | LOLZ: DB 'MICROCOSM ASSOCIATES 8080/8085' 42 | DB ' CPU DIAGNOSTIC VERSION 1.0 (C) 1980' 43 | DB 0dh, 0ah, 24h 44 | ; 45 | BDOS EQU 00005H ;BDOS ENTRY TO CP/M 46 | WBOOT: LDA SWFLAG ; CHECK CPU SWITCHED FLAG 47 | ORA A 48 | JZ WBOOT1 49 | MVI A,0DH 50 | CALL PCHAR 51 | MVI A,0AH 52 | CALL PCHAR 53 | CALL SWZ80 ; SWITCH BACK TO Z80 54 | WBOOT1: 55 | IF ONCPM 56 | JMP 0 57 | ELSE 58 | MVI A,0DH 59 | CALL PCHAR 60 | MVI A,0AH 61 | CALL PCHAR 62 | DI 63 | HLT 64 | ENDIF 65 | 66 | ; 67 | ;Z80PACK CPU SWITCHING ROUTINE 68 | ; 69 | SW8080: MVI A,16 70 | LXI H,S8080 71 | JMP DOSW 72 | SWZ80: MVI A,32 73 | LXI H,SZ80 74 | DOSW: MOV C,A ; SAVE CPU TYPE 75 | IN 0A0H ; CHECK IF HARDWARE CONTROL PORT IS UNLOCKED 76 | ORA A 77 | JZ DOSW1 78 | MVI A,0AAH ; UNLOCK HARDWARE CONTROL PORT 79 | OUT 0A0H 80 | DOSW1: MOV A,C ; SWITCH CPU TYPE 81 | OUT 0A0H 82 | STA SWFLAG ; SET CPU SWITCHED FLAG 83 | JMP MSG 84 | 85 | SWFLAG: DS 1 86 | SZ80: DB 'Switched to Z80 processor$' 87 | S8080: DB 'Switched to 8080 processor$' 88 | 89 | ; 90 | ;MESSAGE OUTPUT ROUTINE 91 | ; 92 | MSG: MOV A,M ; Get data 93 | CPI '$' ; End? 94 | RZ 95 | CALL PCHAR ; Output 96 | INX H ; Next 97 | JMP MSG ; Do all 98 | ; 99 | ; 100 | ;CHARACTER OUTPUT ROUTINE 101 | ; 102 | PCHAR: PUSH PSW 103 | PUSH D 104 | PUSH H 105 | IF ONCPM 106 | MOV E,A 107 | MVI C,2 108 | CALL BDOS 109 | ELSE 110 | OUT IOCHAR 111 | ENDIF 112 | POP H 113 | POP D 114 | POP PSW 115 | RET 116 | ; 117 | ; 118 | ; 119 | BYTEO: PUSH PSW 120 | CALL BYTO1 121 | MOV E,A 122 | CALL PCHAR 123 | POP PSW 124 | CALL BYTO2 125 | MOV E,A 126 | JMP PCHAR 127 | BYTO1: RRC 128 | RRC 129 | RRC 130 | RRC 131 | BYTO2: ANI 0FH 132 | CPI 0AH 133 | JM BYTO3 134 | ADI 7 135 | BYTO3: ADI 30H 136 | RET 137 | ; 138 | ; 139 | ; 140 | ;************************************************************ 141 | ; MESSAGE TABLE FOR OPERATIONAL CPU TEST 142 | ;************************************************************ 143 | ; 144 | OKCPU: DB 0DH,0AH 145 | DB 'CPU IS OPERATIONAL$' 146 | ; 147 | NGCPU: DB 0DH,0AH 148 | DB ' CPU HAS FAILED! ERROR EXIT=$' 149 | ; 150 | ; 151 | ; 152 | ;************************************************************ 153 | ; 8080/8085 CPU TEST/DIAGNOSTIC 154 | ;************************************************************ 155 | ; 156 | ;NOTE: (1) PROGRAM ASSUMES "CALL",AND "LXI SP" INSTRUCTIONS WORK! 157 | ; 158 | ; (2) INSTRUCTIONS NOT TESTED ARE "HLT","DI","EI", 159 | ; AND "RST 0" THRU "RST 7" 160 | ; 161 | ; 162 | ; 163 | ;TEST JUMP INSTRUCTIONS AND FLAGS 164 | ; 165 | CPU: LXI SP,STACK ;SET THE STACK POINTER 166 | ANI 0 ;INITIALIZE A REG. AND CLEAR ALL FLAGS 167 | JZ J010 ;TEST "JZ" 168 | CALL CPUER 169 | J010: JNC J020 ;TEST "JNC" 170 | CALL CPUER 171 | J020: JPE J030 ;TEST "JPE" 172 | CALL CPUER 173 | J030: JP J040 ;TEST "JP" 174 | CALL CPUER 175 | J040: JNZ J050 ;TEST "JNZ" 176 | JC J050 ;TEST "JC" 177 | JPO J050 ;TEST "JPO" 178 | JM J050 ;TEST "JM" 179 | JMP J060 ;TEST "JMP" (IT'S A LITTLE LATE,BUT WHAT THE HELL! 180 | J050: CALL CPUER 181 | J060: ADI 6 ;A=6,C=0,P=1,S=0,Z=0 182 | JNZ J070 ;TEST "JNZ" 183 | CALL CPUER 184 | J070: JC J080 ;TEST "JC" 185 | JPO J080 ;TEST "JPO" 186 | JP J090 ;TEST "JP" 187 | J080: CALL CPUER 188 | J090: ADI 070H ;A=76H,C=0,P=0,S=0,Z=0 189 | JPO J100 ;TEST "JPO" 190 | CALL CPUER 191 | J100: JM J110 ;TEST "JM" 192 | JZ J110 ;TEST "JZ" 193 | JNC J120 ;TEST "JNC" 194 | J110: CALL CPUER 195 | J120: ADI 081H ;A=F7H,C=0,P=0,S=1,Z=0 196 | JM J130 ;TEST "JM" 197 | CALL CPUER 198 | J130: JZ J140 ;TEST "JZ" 199 | JC J140 ;TEST "JC" 200 | JPO J150 ;TEST "JPO" 201 | J140: CALL CPUER 202 | J150: ADI 0FEH ;A=F5H,C=1,P=1,S=1,Z=0 203 | JC J160 ;TEST "JC" 204 | CALL CPUER 205 | J160: JZ J170 ;TEST "JZ" 206 | JPO J170 ;TEST "JPO" 207 | JM AIMM ;TEST "JM" 208 | J170: CALL CPUER 209 | ; 210 | ; 211 | ; 212 | ;TEST ACCUMULATOR IMMEDIATE INSTRUCTIONS 213 | ; 214 | AIMM: CPI 0 ;A=F5H,C=0,Z=0 215 | JC CPIE ;TEST "CPI" FOR RE-SET CARRY 216 | JZ CPIE ;TEST "CPI" FOR RE-SET ZERO 217 | CPI 0F5H ;A=F5H,C=0,Z=1 218 | JC CPIE ;TEST "CPI" FOR RE-SET CARRY ("ADI") 219 | JNZ CPIE ;TEST "CPI" FOR RE-SET ZERO 220 | CPI 0FFH ;A=F5H,C=1,Z=0 221 | JZ CPIE ;TEST "CPI" FOR RE-SET ZERO 222 | JC ACII ;TEST "CPI" FOR SET CARRY 223 | CPIE: CALL CPUER 224 | ACII: ACI 00AH ;A=F5H+0AH+CARRY(1)=0,C=1 225 | ACI 00AH ;A=0+0AH+CARRY(0)=0BH,C=0 226 | CPI 00BH 227 | JZ SUII ;TEST "ACI" 228 | CALL CPUER 229 | SUII: SUI 00CH ;A=FFH,C=0 230 | SUI 00FH ;A=F0H,C=1 231 | CPI 0F0H 232 | JZ SBII ;TEST "SUI" 233 | CALL CPUER 234 | SBII: SBI 0F1H ;A=F0H-0F1H-CARRY(0)=FFH,C=1 235 | SBI 00EH ;A=FFH-OEH-CARRY(1)=F0H,C=0 236 | CPI 0F0H 237 | JZ ANII ;TEST "SBI" 238 | CALL CPUER 239 | ANII: ANI 055H ;A=F0H55H=50H,C=0,P=1,S=0,Z=0 240 | CPI 050H 241 | JZ ORII ;TEST "ANI" 242 | CALL CPUER 243 | ORII: ORI 03AH ;A=50H3AH=7AH,C=0,P=0,S=0,Z=0 244 | CPI 07AH 245 | JZ XRII ;TEST "ORI" 246 | CALL CPUER 247 | XRII: XRI 00FH ;A=7AH0FH=75H,C=0,P=0,S=0,Z=0 248 | CPI 075H 249 | JZ C010 ;TEST "XRI" 250 | CALL CPUER 251 | ; 252 | ; 253 | ; 254 | ;TEST CALLS AND RETURNS 255 | ; 256 | C010: ANI 000H ;A=0,C=0,P=1,S=0,Z=1 257 | CC CPUER ;TEST "CC" 258 | CPO CPUER ;TEST "CPO" 259 | CM CPUER ;TEST "CM" 260 | CNZ CPUER ;TEST "CNZ" 261 | CPI 000H 262 | JZ C020 ;A=0,C=0,P=0,S=0,Z=1 263 | CALL CPUER 264 | C020: SUI 077H ;A=89H,C=1,P=0,S=1,Z=0 265 | CNC CPUER ;TEST "CNC" 266 | CPE CPUER ;TEST "CPE" 267 | CP CPUER ;TEST "CP" 268 | CZ CPUER ;TEST "CZ" 269 | CPI 089H 270 | JZ C030 ;TEST FOR "CALLS" TAKING BRANCH 271 | CALL CPUER 272 | C030: ANI 0FFH ;SET FLAGS BACK! 273 | CPO CPOI ;TEST "CPO" 274 | CPI 0D9H 275 | JZ MOVI ;TEST "CALL" SEQUENCE SUCCESS 276 | CALL CPUER 277 | CPOI: RPE ;TEST "RPE" 278 | ADI 010H ;A=99H,C=0,P=0,S=1,Z=0 279 | CPE CPEI ;TEST "CPE" 280 | ADI 002H ;A=D9H,C=0,P=0,S=1,Z=0 281 | RPO ;TEST "RPO" 282 | CALL CPUER 283 | CPEI: RPO ;TEST "RPO" 284 | ADI 020H ;A=B9H,C=0,P=0,S=1,Z=0 285 | CM CMI ;TEST "CM" 286 | ADI 004H ;A=D7H,C=0,P=1,S=1,Z=0 287 | RPE ;TEST "RPE" 288 | CALL CPUER 289 | CMI: RP ;TEST "RP" 290 | ADI 080H ;A=39H,C=1,P=1,S=0,Z=0 291 | CP TCPI ;TEST "CP" 292 | ADI 080H ;A=D3H,C=0,P=0,S=1,Z=0 293 | RM ;TEST "RM" 294 | CALL CPUER 295 | TCPI: RM ;TEST "RM" 296 | ADI 040H ;A=79H,C=0,P=0,S=0,Z=0 297 | CNC CNCI ;TEST "CNC" 298 | ADI 040H ;A=53H,C=0,P=1,S=0,Z=0 299 | RP ;TEST "RP" 300 | CALL CPUER 301 | CNCI: RC ;TEST "RC" 302 | ADI 08FH ;A=08H,C=1,P=0,S=0,Z=0 303 | CC CCI ;TEST "CC" 304 | SUI 002H ;A=13H,C=0,P=0,S=0,Z=0 305 | RNC ;TEST "RNC" 306 | CALL CPUER 307 | CCI: RNC ;TEST "RNC" 308 | ADI 0F7H ;A=FFH,C=0,P=1,S=1,Z=0 309 | CNZ CNZI ;TEST "CNZ" 310 | ADI 0FEH ;A=15H,C=1,P=0,S=0,Z=0 311 | RC ;TEST "RC" 312 | CALL CPUER 313 | CNZI: RZ ;TEST "RZ" 314 | ADI 001H ;A=00H,C=1,P=1,S=0,Z=1 315 | CZ CZI ;TEST "CZ" 316 | ADI 0D0H ;A=17H,C=1,P=1,S=0,Z=0 317 | RNZ ;TEST "RNZ" 318 | CALL CPUER 319 | CZI: RNZ ;TEST "RNZ" 320 | ADI 047H ;A=47H,C=0,P=1,S=0,Z=0 321 | CPI 047H ;A=47H,C=0,P=1,S=0,Z=1 322 | RZ ;TEST "RZ" 323 | CALL CPUER 324 | ; 325 | ; 326 | ; 327 | ;TEST "MOV","INR",AND "DCR" INSTRUCTIONS 328 | ; 329 | MOVI: MVI A,077H 330 | INR A 331 | MOV B,A 332 | INR B 333 | MOV C,B 334 | DCR C 335 | MOV D,C 336 | MOV E,D 337 | MOV H,E 338 | MOV L,H 339 | MOV A,L ;TEST "MOV" A,L,H,E,D,C,B,A 340 | DCR A 341 | MOV C,A 342 | MOV E,C 343 | MOV L,E 344 | MOV B,L 345 | MOV D,B 346 | MOV H,D 347 | MOV A,H ;TEST "MOV" A,H,D,B,L,E,C,A 348 | MOV D,A 349 | INR D 350 | MOV L,D 351 | MOV C,L 352 | INR C 353 | MOV H,C 354 | MOV B,H 355 | DCR B 356 | MOV E,B 357 | MOV A,E ;TEST "MOV" A,E,B,H,C,L,D,A 358 | MOV E,A 359 | INR E 360 | MOV B,E 361 | MOV H,B 362 | INR H 363 | MOV C,H 364 | MOV L,C 365 | MOV D,L 366 | DCR D 367 | MOV A,D ;TEST "MOV" A,D,L,C,H,B,E,A 368 | MOV H,A 369 | DCR H 370 | MOV D,H 371 | MOV B,D 372 | MOV L,B 373 | INR L 374 | MOV E,L 375 | DCR E 376 | MOV C,E 377 | MOV A,C ;TEST "MOV" A,C,E,L,B,D,H,A 378 | MOV L,A 379 | DCR L 380 | MOV H,L 381 | MOV E,H 382 | MOV D,E 383 | MOV C,D 384 | MOV B,C 385 | MOV A,B 386 | CPI 077H 387 | CNZ CPUER ;TEST "MOV" A,B,C,D,E,H,L,A 388 | ; 389 | ; 390 | ; 391 | ;TEST ARITHMETIC AND LOGIC INSTRUCTIONS 392 | ; 393 | XRA A 394 | MVI B,001H 395 | MVI C,003H 396 | MVI D,007H 397 | MVI E,00FH 398 | MVI H,01FH 399 | MVI L,03FH 400 | ADD B 401 | ADD C 402 | ADD D 403 | ADD E 404 | ADD H 405 | ADD L 406 | ADD A 407 | CPI 0F0H 408 | CNZ CPUER ;TEST "ADD" B,C,D,E,H,L,A 409 | SUB B 410 | SUB C 411 | SUB D 412 | SUB E 413 | SUB H 414 | SUB L 415 | CPI 078H 416 | CNZ CPUER ;TEST "SUB" B,C,D,E,H,L 417 | SUB A 418 | CNZ CPUER ;TEST "SUB" A 419 | MVI A,080H 420 | ADD A 421 | MVI B,001H 422 | MVI C,002H 423 | MVI D,003H 424 | MVI E,004H 425 | MVI H,005H 426 | MVI L,006H 427 | ADC B 428 | MVI B,080H 429 | ADD B 430 | ADD B 431 | ADC C 432 | ADD B 433 | ADD B 434 | ADC D 435 | ADD B 436 | ADD B 437 | ADC E 438 | ADD B 439 | ADD B 440 | ADC H 441 | ADD B 442 | ADD B 443 | ADC L 444 | ADD B 445 | ADD B 446 | ADC A 447 | CPI 037H 448 | CNZ CPUER ;TEST "ADC" B,C,D,E,H,L,A 449 | MVI A,080H 450 | ADD A 451 | MVI B,001H 452 | SBB B 453 | MVI B,0FFH 454 | ADD B 455 | SBB C 456 | ADD B 457 | SBB D 458 | ADD B 459 | SBB E 460 | ADD B 461 | SBB H 462 | ADD B 463 | SBB L 464 | CPI 0E0H 465 | CNZ CPUER ;TEST "SBB" B,C,D,E,H,L 466 | MVI A,080H 467 | ADD A 468 | SBB A 469 | CPI 0FFH 470 | CNZ CPUER ;TEST "SBB" A 471 | MVI A,0FFH 472 | MVI B,0FEH 473 | MVI C,0FCH 474 | MVI D,0EFH 475 | MVI E,07FH 476 | MVI H,0F4H 477 | MVI L,0BFH 478 | ANA A 479 | ANA C 480 | ANA D 481 | ANA E 482 | ANA H 483 | ANA L 484 | ANA A 485 | CPI 024H 486 | CNZ CPUER ;TEST "ANA" B,C,D,E,H,L,A 487 | XRA A 488 | MVI B,001H 489 | MVI C,002H 490 | MVI D,004H 491 | MVI E,008H 492 | MVI H,010H 493 | MVI L,020H 494 | ORA B 495 | ORA C 496 | ORA D 497 | ORA E 498 | ORA H 499 | ORA L 500 | ORA A 501 | CPI 03FH 502 | CNZ CPUER ;TEST "ORA" B,C,D,E,H,L,A 503 | MVI A,000H 504 | MVI H,08FH 505 | MVI L,04FH 506 | XRA B 507 | XRA C 508 | XRA D 509 | XRA E 510 | XRA H 511 | XRA L 512 | CPI 0CFH 513 | CNZ CPUER ;TEST "XRA" B,C,D,E,H,L 514 | XRA A 515 | CNZ CPUER ;TEST "XRA" A 516 | MVI B,044H 517 | MVI C,045H 518 | MVI D,046H 519 | MVI E,047H 520 | MVI H,HIGH TEMP0 ;HIGH BYTE OF TEST MEMORY LOCATION 521 | MVI L,LOW TEMP0 ;LOW BYTE OF TEST MEMORY LOCATION 522 | MOV M,B 523 | MVI B,000H 524 | MOV B,M 525 | MVI A,044H 526 | CMP B 527 | CNZ CPUER ;TEST "MOV" M,B AND B,M 528 | MOV M,D 529 | MVI D,000H 530 | MOV D,M 531 | MVI A,046H 532 | CMP D 533 | CNZ CPUER ;TEST "MOV" M,D AND D,M 534 | MOV M,E 535 | MVI E,000H 536 | MOV E,M 537 | MVI A,047H 538 | CMP E 539 | CNZ CPUER ;TEST "MOV" M,E AND E,M 540 | MOV M,H 541 | MVI H,HIGH TEMP0 542 | MVI L,LOW TEMP0 543 | MOV H,M 544 | MVI A,HIGH TEMP0 545 | CMP H 546 | CNZ CPUER ;TEST "MOV" M,H AND H,M 547 | MOV M,L 548 | MVI H,HIGH TEMP0 549 | MVI L,LOW TEMP0 550 | MOV L,M 551 | MVI A,LOW TEMP0 552 | CMP L 553 | CNZ CPUER ;TEST "MOV" M,L AND L,M 554 | MVI H,HIGH TEMP0 555 | MVI L,LOW TEMP0 556 | MVI A,032H 557 | MOV M,A 558 | CMP M 559 | CNZ CPUER ;TEST "MOV" M,A 560 | ADD M 561 | CPI 064H 562 | CNZ CPUER ;TEST "ADD" M 563 | XRA A 564 | MOV A,M 565 | CPI 032H 566 | CNZ CPUER ;TEST "MOV" A,M 567 | MVI H,HIGH TEMP0 568 | MVI L,LOW TEMP0 569 | MOV A,M 570 | SUB M 571 | CNZ CPUER ;TEST "SUB" M 572 | MVI A,080H 573 | ADD A 574 | ADC M 575 | CPI 033H 576 | CNZ CPUER ;TEST "ADC" M 577 | MVI A,080H 578 | ADD A 579 | SBB M 580 | CPI 0CDH 581 | CNZ CPUER ;TEST "SBB" M 582 | ANA M 583 | CNZ CPUER ;TEST "ANA" M 584 | MVI A,025H 585 | ORA M 586 | CPI 037H 587 | CNZ CPUER ;TEST "ORA" M 588 | XRA M 589 | CPI 005H 590 | CNZ CPUER ;TEST "XRA" M 591 | MVI M,055H 592 | INR M 593 | DCR M 594 | ADD M 595 | CPI 05AH 596 | CNZ CPUER ;TEST "INR","DCR",AND "MVI" M 597 | LXI B,12FFH 598 | LXI D,12FFH 599 | LXI H,12FFH 600 | INX B 601 | INX D 602 | INX H 603 | MVI A,013H 604 | CMP B 605 | CNZ CPUER ;TEST "LXI" AND "INX" B 606 | CMP D 607 | CNZ CPUER ;TEST "LXI" AND "INX" D 608 | CMP H 609 | CNZ CPUER ;TEST "LXI" AND "INX" H 610 | MVI A,000H 611 | CMP C 612 | CNZ CPUER ;TEST "LXI" AND "INX" B 613 | CMP E 614 | CNZ CPUER ;TEST "LXI" AND "INX" D 615 | CMP L 616 | CNZ CPUER ;TEST "LXI" AND "INX" H 617 | DCX B 618 | DCX D 619 | DCX H 620 | MVI A,012H 621 | CMP B 622 | CNZ CPUER ;TEST "DCX" B 623 | CMP D 624 | CNZ CPUER ;TEST "DCX" D 625 | CMP H 626 | CNZ CPUER ;TEST "DCX" H 627 | MVI A,0FFH 628 | CMP C 629 | CNZ CPUER ;TEST "DCX" B 630 | CMP E 631 | CNZ CPUER ;TEST "DCX" D 632 | CMP L 633 | CNZ CPUER ;TEST "DCX" H 634 | STA TEMP0 635 | XRA A 636 | LDA TEMP0 637 | CPI 0FFH 638 | CNZ CPUER ;TEST "LDA" AND "STA" 639 | LHLD TEMPP 640 | SHLD TEMP0 641 | LDA TEMPP 642 | MOV B,A 643 | LDA TEMP0 644 | CMP B 645 | CNZ CPUER ;TEST "LHLD" AND "SHLD" 646 | LDA TEMPP+1 647 | MOV B,A 648 | LDA TEMP0+1 649 | CMP B 650 | CNZ CPUER ;TEST "LHLD" AND "SHLD" 651 | MVI A,0AAH 652 | STA TEMP0 653 | MOV B,H 654 | MOV C,L 655 | XRA A 656 | LDAX B 657 | CPI 0AAH 658 | CNZ CPUER ;TEST "LDAX" B 659 | INR A 660 | STAX B 661 | LDA TEMP0 662 | CPI 0ABH 663 | CNZ CPUER ;TEST "STAX" B 664 | MVI A,077H 665 | STA TEMP0 666 | LHLD TEMPP 667 | LXI D,00000H 668 | XCHG 669 | XRA A 670 | LDAX D 671 | CPI 077H 672 | CNZ CPUER ;TEST "LDAX" D AND "XCHG" 673 | XRA A 674 | ADD H 675 | ADD L 676 | CNZ CPUER ;TEST "XCHG" 677 | MVI A,0CCH 678 | STAX D 679 | LDA TEMP0 680 | CPI 0CCH 681 | STAX D 682 | LDA TEMP0 683 | CPI 0CCH 684 | CNZ CPUER ;TEST "STAX" D 685 | LXI H,07777H 686 | DAD H 687 | MVI A,0EEH 688 | CMP H 689 | CNZ CPUER ;TEST "DAD" H 690 | CMP L 691 | CNZ CPUER ;TEST "DAD" H 692 | LXI H,05555H 693 | LXI B,0FFFFH 694 | DAD B 695 | MVI A,055H 696 | CNC CPUER ;TEST "DAD" B 697 | CMP H 698 | CNZ CPUER ;TEST "DAD" B 699 | MVI A,054H 700 | CMP L 701 | CNZ CPUER ;TEST "DAD" B 702 | LXI H,0AAAAH 703 | LXI D,03333H 704 | DAD D 705 | MVI A,0DDH 706 | CMP H 707 | CNZ CPUER ;TEST "DAD" D 708 | CMP L 709 | CNZ CPUER ;TEST "DAD" B 710 | STC 711 | CNC CPUER ;TEST "STC" 712 | CMC 713 | CC CPUER ;TEST "CMC 714 | MVI A,0AAH 715 | CMA 716 | CPI 055H 717 | CNZ CPUER ;TEST "CMA" 718 | ORA A ;RE-SET AUXILIARY CARRY 719 | DAA 720 | CPI 055H 721 | CNZ CPUER ;TEST "DAA" 722 | MVI A,088H 723 | ADD A 724 | DAA 725 | CPI 076H 726 | CNZ CPUER ;TEST "DAA" 727 | XRA A 728 | MVI A,0AAH 729 | DAA 730 | CNC CPUER ;TEST "DAA" 731 | CPI 010H 732 | CNZ CPUER ;TEST "DAA" 733 | XRA A 734 | MVI A,09AH 735 | DAA 736 | CNC CPUER ;TEST "DAA" 737 | CNZ CPUER ;TEST "DAA" 738 | STC 739 | MVI A,042H 740 | RLC 741 | CC CPUER ;TEST "RLC" FOR RE-SET CARRY 742 | RLC 743 | CNC CPUER ;TEST "RLC" FOR SET CARRY 744 | CPI 009H 745 | CNZ CPUER ;TEST "RLC" FOR ROTATION 746 | RRC 747 | CNC CPUER ;TEST "RRC" FOR SET CARRY 748 | RRC 749 | CPI 042H 750 | CNZ CPUER ;TEST "RRC" FOR ROTATION 751 | RAL 752 | RAL 753 | CNC CPUER ;TEST "RAL" FOR SET CARRY 754 | CPI 008H 755 | CNZ CPUER ;TEST "RAL" FOR ROTATION 756 | RAR 757 | RAR 758 | CC CPUER ;TEST "RAR" FOR RE-SET CARRY 759 | CPI 002H 760 | CNZ CPUER ;TEST "RAR" FOR ROTATION 761 | LXI B,01234H 762 | LXI D,0AAAAH 763 | LXI H,05555H 764 | XRA A 765 | PUSH B 766 | PUSH D 767 | PUSH H 768 | PUSH PSW 769 | LXI B,00000H 770 | LXI D,00000H 771 | LXI H,00000H 772 | MVI A,0C0H 773 | ADI 0F0H 774 | POP PSW 775 | POP H 776 | POP D 777 | POP B 778 | CC CPUER ;TEST "PUSH PSW" AND "POP PSW" 779 | CNZ CPUER ;TEST "PUSH PSW" AND "POP PSW" 780 | CPO CPUER ;TEST "PUSH PSW" AND "POP PSW" 781 | CM CPUER ;TEST "PUSH PSW" AND "POP PSW" 782 | MVI A,012H 783 | CMP B 784 | CNZ CPUER ;TEST "PUSH B" AND "POP B" 785 | MVI A,034H 786 | CMP C 787 | CNZ CPUER ;TEST "PUSH B" AND "POP B" 788 | MVI A,0AAH 789 | CMP D 790 | CNZ CPUER ;TEST "PUSH D" AND "POP D" 791 | CMP E 792 | CNZ CPUER ;TEST "PUSH D" AND "POP D" 793 | MVI A,055H 794 | CMP H 795 | CNZ CPUER ;TEST "PUSH H" AND "POP H" 796 | CMP L 797 | CNZ CPUER ;TEST "PUSH H" AND "POP H" 798 | LXI H,00000H 799 | DAD SP 800 | SHLD SAVSTK ;SAVE THE "OLD" STACK-POINTER! 801 | LXI SP,TEMP4 802 | DCX SP 803 | DCX SP 804 | INX SP 805 | DCX SP 806 | MVI A,055H 807 | STA TEMP2 808 | CMA 809 | STA TEMP3 810 | POP B 811 | CMP B 812 | CNZ CPUER ;TEST "LXI","DAD","INX",AND "DCX" SP 813 | CMA 814 | CMP C 815 | CNZ CPUER ;TEST "LXI","DAD","INX", AND "DCX" SP 816 | LXI H,TEMP4 817 | SPHL 818 | LXI H,07733H 819 | DCX SP 820 | DCX SP 821 | XTHL 822 | LDA TEMP3 823 | CPI 077H 824 | CNZ CPUER ;TEST "SPHL" AND "XTHL" 825 | LDA TEMP2 826 | CPI 033H 827 | CNZ CPUER ;TEST "SPHL" AND "XTHL" 828 | MVI A,055H 829 | CMP L 830 | CNZ CPUER ;TEST "SPHL" AND "XTHL" 831 | CMA 832 | CMP H 833 | CNZ CPUER ;TEST "SPHL" AND "XTHL" 834 | LHLD SAVSTK ;RESTORE THE "OLD" STACK-POINTER 835 | SPHL 836 | LXI H,CPUOK 837 | PCHL ;TEST "PCHL" 838 | ; 839 | ; 840 | ; 841 | CPUER: LXI H,NGCPU ;OUTPUT "CPU HAS FAILED ERROR EXIT=" TO CONSOLE 842 | CALL MSG 843 | XTHL 844 | MOV A,H 845 | CALL BYTEO ;SHOW ERROR EXIT ADDRESS HIGH BYTE 846 | MOV A,L 847 | CALL BYTEO ;SHOW ERROR EXIT ADDRESS LOW BYTE 848 | JMP WBOOT ;EXIT TO CP/M WARM BOOT 849 | ; 850 | ; 851 | ; 852 | CPUOK: LXI H,OKCPU ;OUTPUT "CPU IS OPERATIONAL" TO CONSOLE 853 | CALL MSG 854 | JMP WBOOT ;EXIT TO CP/M WARM BOOT 855 | ; 856 | ; 857 | ; 858 | TEMPP: DW TEMP0 ;POINTER USED TO TEST "LHLD","SHLD", 859 | ; AND "LDAX" INSTRUCTIONS 860 | ; 861 | TEMP0: DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS 862 | TEMP1: DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS 863 | TEMP2: DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS 864 | TEMP3: DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS 865 | TEMP4: DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS 866 | SAVSTK: DS 2 ;TEMPORARY STACK-POINTER STORAGE LOCATION 867 | ; 868 | ; 869 | ; 870 | STACK EQU TEMPP+256 ;DE-BUG STACK POINTER STORAGE AREA 871 | ; 872 | END 873 | 874 | -------------------------------------------------------------------------------- /src-examples/test8080.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udo-munk/Arduino8080/f675ec03fe1c7fd80e3d3026e4b6d82740a2945e/src-examples/test8080.bin -------------------------------------------------------------------------------- /src-examples/test8080.lis: -------------------------------------------------------------------------------- 1 | Z80/8080-Macro-Assembler Release 2.0 Sat May 18 14:29:37 2024 2 | 3 | LOC OBJECT CODE LINE STMT SOURCE CODE 4 | 1 1 ;*********************************************************************** 5 | 2 2 ; MICROCOSM ASSOCIATES 8080/8085 CPU DIAGNOSTIC VERSION 1.0 (C) 1980 6 | 3 3 ;*********************************************************************** 7 | 4 4 ; 8 | 5 5 ;DONATED TO THE "SIG/M" CP/M USER'S GROUP BY: 9 | 6 6 ;KELLY SMITH, MICROCOSM ASSOCIATES 10 | 7 7 ;3055 WACO AVENUE 11 | 8 8 ;SIMI VALLEY, CALIFORNIA, 93065 12 | 9 9 ;(805) 527-9321 (MODEM, CP/M-NET (TM)) 13 | 10 10 ;(805) 527-0518 (VERBAL) 14 | 11 11 ; 15 | 12 12 ; 03-MAY-2024 Thomas Eberhardt Add z80pack CPU switching 16 | 13 13 ; 18-MAY-2024 Thomas Eberhardt Add bare emulator/machine support 17 | 14 14 ; 18 | 15 15 19 | 16 16 IFNDEF ONCPM 20 | 17 17 ONCPM EQU 1 ;1 = RUNS ON CPM, 0 = BARE EMULATOR/MACHINE 21 | 18 18 ENDIF 22 | 0001 = 19 19 IOCHAR EQU 01H ;I/O PORT FOR CHARACTER OUTPUT (BARE) 23 | 20 20 24 | 21 21 IF ONCPM 25 | 22 22 ORG 00100H 26 | 23 23 ELSE 27 | 24 24 ORG 0H 28 | 25 25 ENDIF 29 | 26 26 30 | 0000 af 27 27 START: XRA A 31 | 0001 32 a5 00 28 28 STA SWFLAG ; CLEAR CPU SWITCHED FLAG 32 | 0004 3d 29 29 DCR A 33 | 0005 ea 15 00 30 30 JPE START1 ; EVEN PARITY -> IS 8080 34 | 0008 cd 84 00 31 31 CALL SW8080 ; SWITCH TO 8080 35 | 000b 3e 0d 32 32 MVI A,0DH 36 | 000d cd e6 00 33 33 CALL PCHAR 37 | 0010 3e 0a 34 34 MVI A,0AH 38 | 0012 cd e6 00 35 35 CALL PCHAR 39 | 36 36 40 | 0015 21 1e 00 37 37 START1: LXI H, LOLZ 41 | 0018 cd db 00 38 38 CALL MSG 42 | 001b c3 46 01 39 39 JMP CPU ;JUMP TO 8080 CPU DIAGNOSTIC 43 | 40 40 ; 44 | 001e 4d 49 43 52 41 41 LOLZ: DB 'MICROCOSM ASSOCIATES 8080/8085' 45 | 0022 4f 43 4f 53 41 42 46 | 0026 4d 20 41 53 41 43 47 | 002a 53 4f 43 49 41 44 48 | 002e 41 54 45 53 41 45 49 | 0032 20 38 30 38 41 46 50 | 0036 30 2f 38 30 41 47 51 | 003a 38 35 41 48 52 | 003c 20 43 50 55 42 49 DB ' CPU DIAGNOSTIC VERSION 1.0 (C) 1980' 53 | 0040 20 44 49 41 42 50 54 | 0044 47 4e 4f 53 42 51 55 | 0048 54 49 43 20 42 52 56 | 004c 56 45 52 53 42 53 57 | 0050 49 4f 4e 20 42 54 58 | 0054 31 2e 30 20 42 55 59 | 0058 20 28 43 29 42 56 60 | 005c 20 31 39 38 42 57 61 | 0060 30 42 58 62 | 0061 0d 0a 24 43 59 DB 0dh, 0ah, 24h 63 | 44 60 ; 64 | 0005 = 45 61 BDOS EQU 00005H ;BDOS ENTRY TO CP/M 65 | 0064 3a a5 00 46 62 WBOOT: LDA SWFLAG ; CHECK CPU SWITCHED FLAG 66 | 0067 b7 47 63 ORA A 67 | 0068 ca 78 00 48 64 JZ WBOOT1 68 | 006b 3e 0d 49 65 MVI A,0DH 69 | 006d cd e6 00 50 66 CALL PCHAR 70 | 0070 3e 0a 51 67 MVI A,0AH 71 | 0072 cd e6 00 52 68 CALL PCHAR 72 | 0075 cd 8c 00 53 69 CALL SWZ80 ; SWITCH BACK TO Z80 73 | 0078 54 70 WBOOT1: 74 | 55 71 IF ONCPM 75 | 56 72 JMP 0 76 | 57 73 ELSE 77 | 0078 3e 0d 58 74 MVI A,0DH 78 | 007a cd e6 00 59 75 CALL PCHAR 79 | 007d 3e 0a 60 76 MVI A,0AH 80 | 007f cd e6 00 61 77 CALL PCHAR 81 | 0082 f3 62 78 DI 82 | 0083 76 63 79 HLT 83 | 64 80 ENDIF 84 | 65 81 85 | 66 82 ; 86 | 67 83 ;Z80PACK CPU SWITCHING ROUTINE 87 | 68 84 ; 88 | 0084 3e 10 69 85 SW8080: MVI A,16 89 | 0086 21 c0 00 70 86 LXI H,S8080 90 | 0089 c3 91 00 71 87 JMP DOSW 91 | 008c 3e 20 72 88 SWZ80: MVI A,32 92 | 008e 21 a6 00 73 89 LXI H,SZ80 93 | 0091 4f 74 90 DOSW: MOV C,A ; SAVE CPU TYPE 94 | 0092 db a0 75 91 IN 0A0H ; CHECK IF HARDWARE CONTROL PORT IS UNLOCKED 95 | 0094 b7 76 92 ORA A 96 | 0095 ca 9c 00 77 93 JZ DOSW1 97 | 0098 3e aa 78 94 MVI A,0AAH ; UNLOCK HARDWARE CONTROL PORT 98 | 009a d3 a0 79 95 OUT 0A0H 99 | 009c 79 80 96 DOSW1: MOV A,C ; SWITCH CPU TYPE 100 | 009d d3 a0 81 97 OUT 0A0H 101 | 009f 32 a5 00 82 98 STA SWFLAG ; SET CPU SWITCHED FLAG 102 | 00a2 c3 db 00 83 99 JMP MSG 103 | 84 100 104 | 00a5 85 101 SWFLAG: DS 1 105 | 00a6 53 77 69 74 86 102 SZ80: DB 'Switched to Z80 processor$' 106 | 00aa 63 68 65 64 86 103 107 | 00ae 20 74 6f 20 86 104 108 | 00b2 5a 38 30 20 86 105 109 | 00b6 70 72 6f 63 86 106 110 | 00ba 65 73 73 6f 86 107 111 | 00be 72 24 86 108 112 | 00c0 53 77 69 74 87 109 S8080: DB 'Switched to 8080 processor$' 113 | 00c4 63 68 65 64 87 110 114 | 00c8 20 74 6f 20 87 111 115 | 00cc 38 30 38 30 87 112 116 | 00d0 20 70 72 6f 87 113 117 | 00d4 63 65 73 73 87 114 118 | 00d8 6f 72 24 87 115 119 | 88 116 120 | 89 117 ; 121 | 90 118 ;MESSAGE OUTPUT ROUTINE 122 | 91 119 ; 123 | 00db 7e 92 120 MSG: MOV A,M ; Get data 124 | 00dc fe 24 93 121 CPI '$' ; End? 125 | 00de c8 94 122 RZ 126 | 00df cd e6 00 95 123 CALL PCHAR ; Output 127 | 00e2 23 96 124 INX H ; Next 128 | 00e3 c3 db 00 97 125 JMP MSG ; Do all 129 | 98 126 ; 130 | 99 127 ; 131 | 100 128 ;CHARACTER OUTPUT ROUTINE 132 | 101 129 ; 133 | 00e6 f5 102 130 PCHAR: PUSH PSW 134 | 00e7 d5 103 131 PUSH D 135 | 00e8 e5 104 132 PUSH H 136 | 105 133 IF ONCPM 137 | 106 134 MOV E,A 138 | 107 135 MVI C,2 139 | 108 136 CALL BDOS 140 | 109 137 ELSE 141 | 00e9 d3 01 110 138 OUT IOCHAR 142 | 111 139 ENDIF 143 | 00eb e1 112 140 POP H 144 | 00ec d1 113 141 POP D 145 | 00ed f1 114 142 POP PSW 146 | 00ee c9 115 143 RET 147 | 116 144 ; 148 | 117 145 ; 149 | 118 146 ; 150 | 00ef f5 119 147 BYTEO: PUSH PSW 151 | 00f0 cd ff 00 120 148 CALL BYTO1 152 | 00f3 5f 121 149 MOV E,A 153 | 00f4 cd e6 00 122 150 CALL PCHAR 154 | 00f7 f1 123 151 POP PSW 155 | 00f8 cd 03 01 124 152 CALL BYTO2 156 | 00fb 5f 125 153 MOV E,A 157 | 00fc c3 e6 00 126 154 JMP PCHAR 158 | 00ff 0f 127 155 BYTO1: RRC 159 | 0100 0f 128 156 RRC 160 | 0101 0f 129 157 RRC 161 | 0102 0f 130 158 RRC 162 | 0103 e6 0f 131 159 BYTO2: ANI 0FH 163 | 0105 fe 0a 132 160 CPI 0AH 164 | 0107 fa 0c 01 133 161 JM BYTO3 165 | 010a c6 07 134 162 ADI 7 166 | 010c c6 30 135 163 BYTO3: ADI 30H 167 | 010e c9 136 164 RET 168 | 137 165 ; 169 | 138 166 ; 170 | 139 167 ; 171 | 140 168 ;************************************************************ 172 | 141 169 ; MESSAGE TABLE FOR OPERATIONAL CPU TEST 173 | 142 170 ;************************************************************ 174 | 143 171 ; 175 | 010f 0d 0a 144 172 OKCPU: DB 0DH,0AH 176 | 0111 43 50 55 20 145 173 DB 'CPU IS OPERATIONAL$' 177 | 0115 49 53 20 4f 145 174 178 | 0119 50 45 52 41 145 175 179 | 011d 54 49 4f 4e 145 176 180 | 0121 41 4c 24 145 177 181 | 146 178 ; 182 | 0124 0d 0a 147 179 NGCPU: DB 0DH,0AH 183 | 0126 20 43 50 55 148 180 DB ' CPU HAS FAILED! ERROR EXIT=$' 184 | 012a 20 48 41 53 148 181 185 | 012e 20 46 41 49 148 182 186 | 0132 4c 45 44 21 148 183 187 | 0136 20 20 20 20 148 184 188 | 013a 45 52 52 4f 148 185 189 | 013e 52 20 45 58 148 186 190 | 0142 49 54 3d 24 148 187 191 | 149 188 ; 192 | 150 189 ; 193 | 151 190 ; 194 | 152 191 ;************************************************************ 195 | 153 192 ; 8080/8085 CPU TEST/DIAGNOSTIC 196 | 154 193 ;************************************************************ 197 | 155 194 ; 198 | 156 195 ;NOTE: (1) PROGRAM ASSUMES "CALL",AND "LXI SP" INSTRUCTIONS WORK! 199 | 157 196 ; 200 | 158 197 ; (2) INSTRUCTIONS NOT TESTED ARE "HLT","DI","EI", 201 | 159 198 ; AND "RST 0" THRU "RST 7" 202 | 160 199 ; 203 | 161 200 ; 204 | 162 201 ; 205 | 163 202 ;TEST JUMP INSTRUCTIONS AND FLAGS 206 | 164 203 ; 207 | 0146 31 3f 07 165 204 CPU: LXI SP,STACK ;SET THE STACK POINTER 208 | 0149 e6 00 166 205 ANI 0 ;INITIALIZE A REG. AND CLEAR ALL FLAGS 209 | 014b ca 51 01 167 206 JZ J010 ;TEST "JZ" 210 | 014e cd 24 06 168 207 CALL CPUER 211 | 0151 d2 57 01 169 208 J010: JNC J020 ;TEST "JNC" 212 | 0154 cd 24 06 170 209 CALL CPUER 213 | 0157 ea 5d 01 171 210 J020: JPE J030 ;TEST "JPE" 214 | 015a cd 24 06 172 211 CALL CPUER 215 | 015d f2 63 01 173 212 J030: JP J040 ;TEST "JP" 216 | 0160 cd 24 06 174 213 CALL CPUER 217 | 0163 c2 72 01 175 214 J040: JNZ J050 ;TEST "JNZ" 218 | 0166 da 72 01 176 215 JC J050 ;TEST "JC" 219 | 0169 e2 72 01 177 216 JPO J050 ;TEST "JPO" 220 | 016c fa 72 01 178 217 JM J050 ;TEST "JM" 221 | 016f c3 75 01 179 218 JMP J060 ;TEST "JMP" (IT'S A LITTLE LATE,BUT WHAT THE HELL! 222 | 0172 cd 24 06 180 219 J050: CALL CPUER 223 | 0175 c6 06 181 220 J060: ADI 6 ;A=6,C=0,P=1,S=0,Z=0 224 | 0177 c2 7d 01 182 221 JNZ J070 ;TEST "JNZ" 225 | 017a cd 24 06 183 222 CALL CPUER 226 | 017d da 86 01 184 223 J070: JC J080 ;TEST "JC" 227 | 0180 e2 86 01 185 224 JPO J080 ;TEST "JPO" 228 | 0183 f2 89 01 186 225 JP J090 ;TEST "JP" 229 | 0186 cd 24 06 187 226 J080: CALL CPUER 230 | 0189 c6 70 188 227 J090: ADI 070H ;A=76H,C=0,P=0,S=0,Z=0 231 | 018b e2 91 01 189 228 JPO J100 ;TEST "JPO" 232 | 018e cd 24 06 190 229 CALL CPUER 233 | 0191 fa 9a 01 191 230 J100: JM J110 ;TEST "JM" 234 | 0194 ca 9a 01 192 231 JZ J110 ;TEST "JZ" 235 | 0197 d2 9d 01 193 232 JNC J120 ;TEST "JNC" 236 | 019a cd 24 06 194 233 J110: CALL CPUER 237 | 019d c6 81 195 234 J120: ADI 081H ;A=F7H,C=0,P=0,S=1,Z=0 238 | 019f fa a5 01 196 235 JM J130 ;TEST "JM" 239 | 01a2 cd 24 06 197 236 CALL CPUER 240 | 01a5 ca ae 01 198 237 J130: JZ J140 ;TEST "JZ" 241 | 01a8 da ae 01 199 238 JC J140 ;TEST "JC" 242 | 01ab e2 b1 01 200 239 JPO J150 ;TEST "JPO" 243 | 01ae cd 24 06 201 240 J140: CALL CPUER 244 | 01b1 c6 fe 202 241 J150: ADI 0FEH ;A=F5H,C=1,P=1,S=1,Z=0 245 | 01b3 da b9 01 203 242 JC J160 ;TEST "JC" 246 | 01b6 cd 24 06 204 243 CALL CPUER 247 | 01b9 ca c2 01 205 244 J160: JZ J170 ;TEST "JZ" 248 | 01bc e2 c2 01 206 245 JPO J170 ;TEST "JPO" 249 | 01bf fa c5 01 207 246 JM AIMM ;TEST "JM" 250 | 01c2 cd 24 06 208 247 J170: CALL CPUER 251 | 209 248 ; 252 | 210 249 ; 253 | 211 250 ; 254 | 212 251 ;TEST ACCUMULATOR IMMEDIATE INSTRUCTIONS 255 | 213 252 ; 256 | 01c5 fe 00 214 253 AIMM: CPI 0 ;A=F5H,C=0,Z=0 257 | 01c7 da dd 01 215 254 JC CPIE ;TEST "CPI" FOR RE-SET CARRY 258 | 01ca ca dd 01 216 255 JZ CPIE ;TEST "CPI" FOR RE-SET ZERO 259 | 01cd fe f5 217 256 CPI 0F5H ;A=F5H,C=0,Z=1 260 | 01cf da dd 01 218 257 JC CPIE ;TEST "CPI" FOR RE-SET CARRY ("ADI") 261 | 01d2 c2 dd 01 219 258 JNZ CPIE ;TEST "CPI" FOR RE-SET ZERO 262 | 01d5 fe ff 220 259 CPI 0FFH ;A=F5H,C=1,Z=0 263 | 01d7 ca dd 01 221 260 JZ CPIE ;TEST "CPI" FOR RE-SET ZERO 264 | 01da da e0 01 222 261 JC ACII ;TEST "CPI" FOR SET CARRY 265 | 01dd cd 24 06 223 262 CPIE: CALL CPUER 266 | 01e0 ce 0a 224 263 ACII: ACI 00AH ;A=F5H+0AH+CARRY(1)=0,C=1 267 | 01e2 ce 0a 225 264 ACI 00AH ;A=0+0AH+CARRY(0)=0BH,C=0 268 | 01e4 fe 0b 226 265 CPI 00BH 269 | 01e6 ca ec 01 227 266 JZ SUII ;TEST "ACI" 270 | 01e9 cd 24 06 228 267 CALL CPUER 271 | 01ec d6 0c 229 268 SUII: SUI 00CH ;A=FFH,C=0 272 | 01ee d6 0f 230 269 SUI 00FH ;A=F0H,C=1 273 | 01f0 fe f0 231 270 CPI 0F0H 274 | 01f2 ca f8 01 232 271 JZ SBII ;TEST "SUI" 275 | 01f5 cd 24 06 233 272 CALL CPUER 276 | 01f8 de f1 234 273 SBII: SBI 0F1H ;A=F0H-0F1H-CARRY(0)=FFH,C=1 277 | 01fa de 0e 235 274 SBI 00EH ;A=FFH-OEH-CARRY(1)=F0H,C=0 278 | 01fc fe f0 236 275 CPI 0F0H 279 | 01fe ca 04 02 237 276 JZ ANII ;TEST "SBI" 280 | 0201 cd 24 06 238 277 CALL CPUER 281 | 0204 e6 55 239 278 ANII: ANI 055H ;A=F0H55H=50H,C=0,P=1,S=0,Z=0 282 | 0206 fe 50 240 279 CPI 050H 283 | 0208 ca 0e 02 241 280 JZ ORII ;TEST "ANI" 284 | 020b cd 24 06 242 281 CALL CPUER 285 | 020e f6 3a 243 282 ORII: ORI 03AH ;A=50H3AH=7AH,C=0,P=0,S=0,Z=0 286 | 0210 fe 7a 244 283 CPI 07AH 287 | 0212 ca 18 02 245 284 JZ XRII ;TEST "ORI" 288 | 0215 cd 24 06 246 285 CALL CPUER 289 | 0218 ee 0f 247 286 XRII: XRI 00FH ;A=7AH0FH=75H,C=0,P=0,S=0,Z=0 290 | 021a fe 75 248 287 CPI 075H 291 | 021c ca 22 02 249 288 JZ C010 ;TEST "XRI" 292 | 021f cd 24 06 250 289 CALL CPUER 293 | 251 290 ; 294 | 252 291 ; 295 | 253 292 ; 296 | 254 293 ;TEST CALLS AND RETURNS 297 | 255 294 ; 298 | 0222 e6 00 256 295 C010: ANI 000H ;A=0,C=0,P=1,S=0,Z=1 299 | 0224 dc 24 06 257 296 CC CPUER ;TEST "CC" 300 | 0227 e4 24 06 258 297 CPO CPUER ;TEST "CPO" 301 | 022a fc 24 06 259 298 CM CPUER ;TEST "CM" 302 | 022d c4 24 06 260 299 CNZ CPUER ;TEST "CNZ" 303 | 0230 fe 00 261 300 CPI 000H 304 | 0232 ca 38 02 262 301 JZ C020 ;A=0,C=0,P=0,S=0,Z=1 305 | 0235 cd 24 06 263 302 CALL CPUER 306 | 0238 d6 77 264 303 C020: SUI 077H ;A=89H,C=1,P=0,S=1,Z=0 307 | 023a d4 24 06 265 304 CNC CPUER ;TEST "CNC" 308 | 023d ec 24 06 266 305 CPE CPUER ;TEST "CPE" 309 | 0240 f4 24 06 267 306 CP CPUER ;TEST "CP" 310 | 0243 cc 24 06 268 307 CZ CPUER ;TEST "CZ" 311 | 0246 fe 89 269 308 CPI 089H 312 | 0248 ca 4e 02 270 309 JZ C030 ;TEST FOR "CALLS" TAKING BRANCH 313 | 024b cd 24 06 271 310 CALL CPUER 314 | 024e e6 ff 272 311 C030: ANI 0FFH ;SET FLAGS BACK! 315 | 0250 e4 5b 02 273 312 CPO CPOI ;TEST "CPO" 316 | 0253 fe d9 274 313 CPI 0D9H 317 | 0255 ca b8 02 275 314 JZ MOVI ;TEST "CALL" SEQUENCE SUCCESS 318 | 0258 cd 24 06 276 315 CALL CPUER 319 | 025b e8 277 316 CPOI: RPE ;TEST "RPE" 320 | 025c c6 10 278 317 ADI 010H ;A=99H,C=0,P=0,S=1,Z=0 321 | 025e ec 67 02 279 318 CPE CPEI ;TEST "CPE" 322 | 0261 c6 02 280 319 ADI 002H ;A=D9H,C=0,P=0,S=1,Z=0 323 | 0263 e0 281 320 RPO ;TEST "RPO" 324 | 0264 cd 24 06 282 321 CALL CPUER 325 | 0267 e0 283 322 CPEI: RPO ;TEST "RPO" 326 | 0268 c6 20 284 323 ADI 020H ;A=B9H,C=0,P=0,S=1,Z=0 327 | 026a fc 73 02 285 324 CM CMI ;TEST "CM" 328 | 026d c6 04 286 325 ADI 004H ;A=D7H,C=0,P=1,S=1,Z=0 329 | 026f e8 287 326 RPE ;TEST "RPE" 330 | 0270 cd 24 06 288 327 CALL CPUER 331 | 0273 f0 289 328 CMI: RP ;TEST "RP" 332 | 0274 c6 80 290 329 ADI 080H ;A=39H,C=1,P=1,S=0,Z=0 333 | 0276 f4 7f 02 291 330 CP TCPI ;TEST "CP" 334 | 0279 c6 80 292 331 ADI 080H ;A=D3H,C=0,P=0,S=1,Z=0 335 | 027b f8 293 332 RM ;TEST "RM" 336 | 027c cd 24 06 294 333 CALL CPUER 337 | 027f f8 295 334 TCPI: RM ;TEST "RM" 338 | 0280 c6 40 296 335 ADI 040H ;A=79H,C=0,P=0,S=0,Z=0 339 | 0282 d4 8b 02 297 336 CNC CNCI ;TEST "CNC" 340 | 0285 c6 40 298 337 ADI 040H ;A=53H,C=0,P=1,S=0,Z=0 341 | 0287 f0 299 338 RP ;TEST "RP" 342 | 0288 cd 24 06 300 339 CALL CPUER 343 | 028b d8 301 340 CNCI: RC ;TEST "RC" 344 | 028c c6 8f 302 341 ADI 08FH ;A=08H,C=1,P=0,S=0,Z=0 345 | 028e dc 97 02 303 342 CC CCI ;TEST "CC" 346 | 0291 d6 02 304 343 SUI 002H ;A=13H,C=0,P=0,S=0,Z=0 347 | 0293 d0 305 344 RNC ;TEST "RNC" 348 | 0294 cd 24 06 306 345 CALL CPUER 349 | 0297 d0 307 346 CCI: RNC ;TEST "RNC" 350 | 0298 c6 f7 308 347 ADI 0F7H ;A=FFH,C=0,P=1,S=1,Z=0 351 | 029a c4 a3 02 309 348 CNZ CNZI ;TEST "CNZ" 352 | 029d c6 fe 310 349 ADI 0FEH ;A=15H,C=1,P=0,S=0,Z=0 353 | 029f d8 311 350 RC ;TEST "RC" 354 | 02a0 cd 24 06 312 351 CALL CPUER 355 | 02a3 c8 313 352 CNZI: RZ ;TEST "RZ" 356 | 02a4 c6 01 314 353 ADI 001H ;A=00H,C=1,P=1,S=0,Z=1 357 | 02a6 cc af 02 315 354 CZ CZI ;TEST "CZ" 358 | 02a9 c6 d0 316 355 ADI 0D0H ;A=17H,C=1,P=1,S=0,Z=0 359 | 02ab c0 317 356 RNZ ;TEST "RNZ" 360 | 02ac cd 24 06 318 357 CALL CPUER 361 | 02af c0 319 358 CZI: RNZ ;TEST "RNZ" 362 | 02b0 c6 47 320 359 ADI 047H ;A=47H,C=0,P=1,S=0,Z=0 363 | 02b2 fe 47 321 360 CPI 047H ;A=47H,C=0,P=1,S=0,Z=1 364 | 02b4 c8 322 361 RZ ;TEST "RZ" 365 | 02b5 cd 24 06 323 362 CALL CPUER 366 | 324 363 ; 367 | 325 364 ; 368 | 326 365 ; 369 | 327 366 ;TEST "MOV","INR",AND "DCR" INSTRUCTIONS 370 | 328 367 ; 371 | 02b8 3e 77 329 368 MOVI: MVI A,077H 372 | 02ba 3c 330 369 INR A 373 | 02bb 47 331 370 MOV B,A 374 | 02bc 04 332 371 INR B 375 | 02bd 48 333 372 MOV C,B 376 | 02be 0d 334 373 DCR C 377 | 02bf 51 335 374 MOV D,C 378 | 02c0 5a 336 375 MOV E,D 379 | 02c1 63 337 376 MOV H,E 380 | 02c2 6c 338 377 MOV L,H 381 | 02c3 7d 339 378 MOV A,L ;TEST "MOV" A,L,H,E,D,C,B,A 382 | 02c4 3d 340 379 DCR A 383 | 02c5 4f 341 380 MOV C,A 384 | 02c6 59 342 381 MOV E,C 385 | 02c7 6b 343 382 MOV L,E 386 | 02c8 45 344 383 MOV B,L 387 | 02c9 50 345 384 MOV D,B 388 | 02ca 62 346 385 MOV H,D 389 | 02cb 7c 347 386 MOV A,H ;TEST "MOV" A,H,D,B,L,E,C,A 390 | 02cc 57 348 387 MOV D,A 391 | 02cd 14 349 388 INR D 392 | 02ce 6a 350 389 MOV L,D 393 | 02cf 4d 351 390 MOV C,L 394 | 02d0 0c 352 391 INR C 395 | 02d1 61 353 392 MOV H,C 396 | 02d2 44 354 393 MOV B,H 397 | 02d3 05 355 394 DCR B 398 | 02d4 58 356 395 MOV E,B 399 | 02d5 7b 357 396 MOV A,E ;TEST "MOV" A,E,B,H,C,L,D,A 400 | 02d6 5f 358 397 MOV E,A 401 | 02d7 1c 359 398 INR E 402 | 02d8 43 360 399 MOV B,E 403 | 02d9 60 361 400 MOV H,B 404 | 02da 24 362 401 INR H 405 | 02db 4c 363 402 MOV C,H 406 | 02dc 69 364 403 MOV L,C 407 | 02dd 55 365 404 MOV D,L 408 | 02de 15 366 405 DCR D 409 | 02df 7a 367 406 MOV A,D ;TEST "MOV" A,D,L,C,H,B,E,A 410 | 02e0 67 368 407 MOV H,A 411 | 02e1 25 369 408 DCR H 412 | 02e2 54 370 409 MOV D,H 413 | 02e3 42 371 410 MOV B,D 414 | 02e4 68 372 411 MOV L,B 415 | 02e5 2c 373 412 INR L 416 | 02e6 5d 374 413 MOV E,L 417 | 02e7 1d 375 414 DCR E 418 | 02e8 4b 376 415 MOV C,E 419 | 02e9 79 377 416 MOV A,C ;TEST "MOV" A,C,E,L,B,D,H,A 420 | 02ea 6f 378 417 MOV L,A 421 | 02eb 2d 379 418 DCR L 422 | 02ec 65 380 419 MOV H,L 423 | 02ed 5c 381 420 MOV E,H 424 | 02ee 53 382 421 MOV D,E 425 | 02ef 4a 383 422 MOV C,D 426 | 02f0 41 384 423 MOV B,C 427 | 02f1 78 385 424 MOV A,B 428 | 02f2 fe 77 386 425 CPI 077H 429 | 02f4 c4 24 06 387 426 CNZ CPUER ;TEST "MOV" A,B,C,D,E,H,L,A 430 | 388 427 ; 431 | 389 428 ; 432 | 390 429 ; 433 | 391 430 ;TEST ARITHMETIC AND LOGIC INSTRUCTIONS 434 | 392 431 ; 435 | 02f7 af 393 432 XRA A 436 | 02f8 06 01 394 433 MVI B,001H 437 | 02fa 0e 03 395 434 MVI C,003H 438 | 02fc 16 07 396 435 MVI D,007H 439 | 02fe 1e 0f 397 436 MVI E,00FH 440 | 0300 26 1f 398 437 MVI H,01FH 441 | 0302 2e 3f 399 438 MVI L,03FH 442 | 0304 80 400 439 ADD B 443 | 0305 81 401 440 ADD C 444 | 0306 82 402 441 ADD D 445 | 0307 83 403 442 ADD E 446 | 0308 84 404 443 ADD H 447 | 0309 85 405 444 ADD L 448 | 030a 87 406 445 ADD A 449 | 030b fe f0 407 446 CPI 0F0H 450 | 030d c4 24 06 408 447 CNZ CPUER ;TEST "ADD" B,C,D,E,H,L,A 451 | 0310 90 409 448 SUB B 452 | 0311 91 410 449 SUB C 453 | 0312 92 411 450 SUB D 454 | 0313 93 412 451 SUB E 455 | 0314 94 413 452 SUB H 456 | 0315 95 414 453 SUB L 457 | 0316 fe 78 415 454 CPI 078H 458 | 0318 c4 24 06 416 455 CNZ CPUER ;TEST "SUB" B,C,D,E,H,L 459 | 031b 97 417 456 SUB A 460 | 031c c4 24 06 418 457 CNZ CPUER ;TEST "SUB" A 461 | 031f 3e 80 419 458 MVI A,080H 462 | 0321 87 420 459 ADD A 463 | 0322 06 01 421 460 MVI B,001H 464 | 0324 0e 02 422 461 MVI C,002H 465 | 0326 16 03 423 462 MVI D,003H 466 | 0328 1e 04 424 463 MVI E,004H 467 | 032a 26 05 425 464 MVI H,005H 468 | 032c 2e 06 426 465 MVI L,006H 469 | 032e 88 427 466 ADC B 470 | 032f 06 80 428 467 MVI B,080H 471 | 0331 80 429 468 ADD B 472 | 0332 80 430 469 ADD B 473 | 0333 89 431 470 ADC C 474 | 0334 80 432 471 ADD B 475 | 0335 80 433 472 ADD B 476 | 0336 8a 434 473 ADC D 477 | 0337 80 435 474 ADD B 478 | 0338 80 436 475 ADD B 479 | 0339 8b 437 476 ADC E 480 | 033a 80 438 477 ADD B 481 | 033b 80 439 478 ADD B 482 | 033c 8c 440 479 ADC H 483 | 033d 80 441 480 ADD B 484 | 033e 80 442 481 ADD B 485 | 033f 8d 443 482 ADC L 486 | 0340 80 444 483 ADD B 487 | 0341 80 445 484 ADD B 488 | 0342 8f 446 485 ADC A 489 | 0343 fe 37 447 486 CPI 037H 490 | 0345 c4 24 06 448 487 CNZ CPUER ;TEST "ADC" B,C,D,E,H,L,A 491 | 0348 3e 80 449 488 MVI A,080H 492 | 034a 87 450 489 ADD A 493 | 034b 06 01 451 490 MVI B,001H 494 | 034d 98 452 491 SBB B 495 | 034e 06 ff 453 492 MVI B,0FFH 496 | 0350 80 454 493 ADD B 497 | 0351 99 455 494 SBB C 498 | 0352 80 456 495 ADD B 499 | 0353 9a 457 496 SBB D 500 | 0354 80 458 497 ADD B 501 | 0355 9b 459 498 SBB E 502 | 0356 80 460 499 ADD B 503 | 0357 9c 461 500 SBB H 504 | 0358 80 462 501 ADD B 505 | 0359 9d 463 502 SBB L 506 | 035a fe e0 464 503 CPI 0E0H 507 | 035c c4 24 06 465 504 CNZ CPUER ;TEST "SBB" B,C,D,E,H,L 508 | 035f 3e 80 466 505 MVI A,080H 509 | 0361 87 467 506 ADD A 510 | 0362 9f 468 507 SBB A 511 | 0363 fe ff 469 508 CPI 0FFH 512 | 0365 c4 24 06 470 509 CNZ CPUER ;TEST "SBB" A 513 | 0368 3e ff 471 510 MVI A,0FFH 514 | 036a 06 fe 472 511 MVI B,0FEH 515 | 036c 0e fc 473 512 MVI C,0FCH 516 | 036e 16 ef 474 513 MVI D,0EFH 517 | 0370 1e 7f 475 514 MVI E,07FH 518 | 0372 26 f4 476 515 MVI H,0F4H 519 | 0374 2e bf 477 516 MVI L,0BFH 520 | 0376 a7 478 517 ANA A 521 | 0377 a1 479 518 ANA C 522 | 0378 a2 480 519 ANA D 523 | 0379 a3 481 520 ANA E 524 | 037a a4 482 521 ANA H 525 | 037b a5 483 522 ANA L 526 | 037c a7 484 523 ANA A 527 | 037d fe 24 485 524 CPI 024H 528 | 037f c4 24 06 486 525 CNZ CPUER ;TEST "ANA" B,C,D,E,H,L,A 529 | 0382 af 487 526 XRA A 530 | 0383 06 01 488 527 MVI B,001H 531 | 0385 0e 02 489 528 MVI C,002H 532 | 0387 16 04 490 529 MVI D,004H 533 | 0389 1e 08 491 530 MVI E,008H 534 | 038b 26 10 492 531 MVI H,010H 535 | 038d 2e 20 493 532 MVI L,020H 536 | 038f b0 494 533 ORA B 537 | 0390 b1 495 534 ORA C 538 | 0391 b2 496 535 ORA D 539 | 0392 b3 497 536 ORA E 540 | 0393 b4 498 537 ORA H 541 | 0394 b5 499 538 ORA L 542 | 0395 b7 500 539 ORA A 543 | 0396 fe 3f 501 540 CPI 03FH 544 | 0398 c4 24 06 502 541 CNZ CPUER ;TEST "ORA" B,C,D,E,H,L,A 545 | 039b 3e 00 503 542 MVI A,000H 546 | 039d 26 8f 504 543 MVI H,08FH 547 | 039f 2e 4f 505 544 MVI L,04FH 548 | 03a1 a8 506 545 XRA B 549 | 03a2 a9 507 546 XRA C 550 | 03a3 aa 508 547 XRA D 551 | 03a4 ab 509 548 XRA E 552 | 03a5 ac 510 549 XRA H 553 | 03a6 ad 511 550 XRA L 554 | 03a7 fe cf 512 551 CPI 0CFH 555 | 03a9 c4 24 06 513 552 CNZ CPUER ;TEST "XRA" B,C,D,E,H,L 556 | 03ac af 514 553 XRA A 557 | 03ad c4 24 06 515 554 CNZ CPUER ;TEST "XRA" A 558 | 03b0 06 44 516 555 MVI B,044H 559 | 03b2 0e 45 517 556 MVI C,045H 560 | 03b4 16 46 518 557 MVI D,046H 561 | 03b6 1e 47 519 558 MVI E,047H 562 | 03b8 26 06 520 559 MVI H,HIGH TEMP0 ;HIGH BYTE OF TEST MEMORY LOCATION 563 | 03ba 2e 41 521 560 MVI L,LOW TEMP0 ;LOW BYTE OF TEST MEMORY LOCATION 564 | 03bc 70 522 561 MOV M,B 565 | 03bd 06 00 523 562 MVI B,000H 566 | 03bf 46 524 563 MOV B,M 567 | 03c0 3e 44 525 564 MVI A,044H 568 | 03c2 b8 526 565 CMP B 569 | 03c3 c4 24 06 527 566 CNZ CPUER ;TEST "MOV" M,B AND B,M 570 | 03c6 72 528 567 MOV M,D 571 | 03c7 16 00 529 568 MVI D,000H 572 | 03c9 56 530 569 MOV D,M 573 | 03ca 3e 46 531 570 MVI A,046H 574 | 03cc ba 532 571 CMP D 575 | 03cd c4 24 06 533 572 CNZ CPUER ;TEST "MOV" M,D AND D,M 576 | 03d0 73 534 573 MOV M,E 577 | 03d1 1e 00 535 574 MVI E,000H 578 | 03d3 5e 536 575 MOV E,M 579 | 03d4 3e 47 537 576 MVI A,047H 580 | 03d6 bb 538 577 CMP E 581 | 03d7 c4 24 06 539 578 CNZ CPUER ;TEST "MOV" M,E AND E,M 582 | 03da 74 540 579 MOV M,H 583 | 03db 26 06 541 580 MVI H,HIGH TEMP0 584 | 03dd 2e 41 542 581 MVI L,LOW TEMP0 585 | 03df 66 543 582 MOV H,M 586 | 03e0 3e 06 544 583 MVI A,HIGH TEMP0 587 | 03e2 bc 545 584 CMP H 588 | 03e3 c4 24 06 546 585 CNZ CPUER ;TEST "MOV" M,H AND H,M 589 | 03e6 75 547 586 MOV M,L 590 | 03e7 26 06 548 587 MVI H,HIGH TEMP0 591 | 03e9 2e 41 549 588 MVI L,LOW TEMP0 592 | 03eb 6e 550 589 MOV L,M 593 | 03ec 3e 41 551 590 MVI A,LOW TEMP0 594 | 03ee bd 552 591 CMP L 595 | 03ef c4 24 06 553 592 CNZ CPUER ;TEST "MOV" M,L AND L,M 596 | 03f2 26 06 554 593 MVI H,HIGH TEMP0 597 | 03f4 2e 41 555 594 MVI L,LOW TEMP0 598 | 03f6 3e 32 556 595 MVI A,032H 599 | 03f8 77 557 596 MOV M,A 600 | 03f9 be 558 597 CMP M 601 | 03fa c4 24 06 559 598 CNZ CPUER ;TEST "MOV" M,A 602 | 03fd 86 560 599 ADD M 603 | 03fe fe 64 561 600 CPI 064H 604 | 0400 c4 24 06 562 601 CNZ CPUER ;TEST "ADD" M 605 | 0403 af 563 602 XRA A 606 | 0404 7e 564 603 MOV A,M 607 | 0405 fe 32 565 604 CPI 032H 608 | 0407 c4 24 06 566 605 CNZ CPUER ;TEST "MOV" A,M 609 | 040a 26 06 567 606 MVI H,HIGH TEMP0 610 | 040c 2e 41 568 607 MVI L,LOW TEMP0 611 | 040e 7e 569 608 MOV A,M 612 | 040f 96 570 609 SUB M 613 | 0410 c4 24 06 571 610 CNZ CPUER ;TEST "SUB" M 614 | 0413 3e 80 572 611 MVI A,080H 615 | 0415 87 573 612 ADD A 616 | 0416 8e 574 613 ADC M 617 | 0417 fe 33 575 614 CPI 033H 618 | 0419 c4 24 06 576 615 CNZ CPUER ;TEST "ADC" M 619 | 041c 3e 80 577 616 MVI A,080H 620 | 041e 87 578 617 ADD A 621 | 041f 9e 579 618 SBB M 622 | 0420 fe cd 580 619 CPI 0CDH 623 | 0422 c4 24 06 581 620 CNZ CPUER ;TEST "SBB" M 624 | 0425 a6 582 621 ANA M 625 | 0426 c4 24 06 583 622 CNZ CPUER ;TEST "ANA" M 626 | 0429 3e 25 584 623 MVI A,025H 627 | 042b b6 585 624 ORA M 628 | 042c fe 37 586 625 CPI 037H 629 | 042e c4 24 06 587 626 CNZ CPUER ;TEST "ORA" M 630 | 0431 ae 588 627 XRA M 631 | 0432 fe 05 589 628 CPI 005H 632 | 0434 c4 24 06 590 629 CNZ CPUER ;TEST "XRA" M 633 | 0437 36 55 591 630 MVI M,055H 634 | 0439 34 592 631 INR M 635 | 043a 35 593 632 DCR M 636 | 043b 86 594 633 ADD M 637 | 043c fe 5a 595 634 CPI 05AH 638 | 043e c4 24 06 596 635 CNZ CPUER ;TEST "INR","DCR",AND "MVI" M 639 | 0441 01 ff 12 597 636 LXI B,12FFH 640 | 0444 11 ff 12 598 637 LXI D,12FFH 641 | 0447 21 ff 12 599 638 LXI H,12FFH 642 | 044a 03 600 639 INX B 643 | 044b 13 601 640 INX D 644 | 044c 23 602 641 INX H 645 | 044d 3e 13 603 642 MVI A,013H 646 | 044f b8 604 643 CMP B 647 | 0450 c4 24 06 605 644 CNZ CPUER ;TEST "LXI" AND "INX" B 648 | 0453 ba 606 645 CMP D 649 | 0454 c4 24 06 607 646 CNZ CPUER ;TEST "LXI" AND "INX" D 650 | 0457 bc 608 647 CMP H 651 | 0458 c4 24 06 609 648 CNZ CPUER ;TEST "LXI" AND "INX" H 652 | 045b 3e 00 610 649 MVI A,000H 653 | 045d b9 611 650 CMP C 654 | 045e c4 24 06 612 651 CNZ CPUER ;TEST "LXI" AND "INX" B 655 | 0461 bb 613 652 CMP E 656 | 0462 c4 24 06 614 653 CNZ CPUER ;TEST "LXI" AND "INX" D 657 | 0465 bd 615 654 CMP L 658 | 0466 c4 24 06 616 655 CNZ CPUER ;TEST "LXI" AND "INX" H 659 | 0469 0b 617 656 DCX B 660 | 046a 1b 618 657 DCX D 661 | 046b 2b 619 658 DCX H 662 | 046c 3e 12 620 659 MVI A,012H 663 | 046e b8 621 660 CMP B 664 | 046f c4 24 06 622 661 CNZ CPUER ;TEST "DCX" B 665 | 0472 ba 623 662 CMP D 666 | 0473 c4 24 06 624 663 CNZ CPUER ;TEST "DCX" D 667 | 0476 bc 625 664 CMP H 668 | 0477 c4 24 06 626 665 CNZ CPUER ;TEST "DCX" H 669 | 047a 3e ff 627 666 MVI A,0FFH 670 | 047c b9 628 667 CMP C 671 | 047d c4 24 06 629 668 CNZ CPUER ;TEST "DCX" B 672 | 0480 bb 630 669 CMP E 673 | 0481 c4 24 06 631 670 CNZ CPUER ;TEST "DCX" D 674 | 0484 bd 632 671 CMP L 675 | 0485 c4 24 06 633 672 CNZ CPUER ;TEST "DCX" H 676 | 0488 32 41 06 634 673 STA TEMP0 677 | 048b af 635 674 XRA A 678 | 048c 3a 41 06 636 675 LDA TEMP0 679 | 048f fe ff 637 676 CPI 0FFH 680 | 0491 c4 24 06 638 677 CNZ CPUER ;TEST "LDA" AND "STA" 681 | 0494 2a 3f 06 639 678 LHLD TEMPP 682 | 0497 22 41 06 640 679 SHLD TEMP0 683 | 049a 3a 3f 06 641 680 LDA TEMPP 684 | 049d 47 642 681 MOV B,A 685 | 049e 3a 41 06 643 682 LDA TEMP0 686 | 04a1 b8 644 683 CMP B 687 | 04a2 c4 24 06 645 684 CNZ CPUER ;TEST "LHLD" AND "SHLD" 688 | 04a5 3a 40 06 646 685 LDA TEMPP+1 689 | 04a8 47 647 686 MOV B,A 690 | 04a9 3a 42 06 648 687 LDA TEMP0+1 691 | 04ac b8 649 688 CMP B 692 | 04ad c4 24 06 650 689 CNZ CPUER ;TEST "LHLD" AND "SHLD" 693 | 04b0 3e aa 651 690 MVI A,0AAH 694 | 04b2 32 41 06 652 691 STA TEMP0 695 | 04b5 44 653 692 MOV B,H 696 | 04b6 4d 654 693 MOV C,L 697 | 04b7 af 655 694 XRA A 698 | 04b8 0a 656 695 LDAX B 699 | 04b9 fe aa 657 696 CPI 0AAH 700 | 04bb c4 24 06 658 697 CNZ CPUER ;TEST "LDAX" B 701 | 04be 3c 659 698 INR A 702 | 04bf 02 660 699 STAX B 703 | 04c0 3a 41 06 661 700 LDA TEMP0 704 | 04c3 fe ab 662 701 CPI 0ABH 705 | 04c5 c4 24 06 663 702 CNZ CPUER ;TEST "STAX" B 706 | 04c8 3e 77 664 703 MVI A,077H 707 | 04ca 32 41 06 665 704 STA TEMP0 708 | 04cd 2a 3f 06 666 705 LHLD TEMPP 709 | 04d0 11 00 00 667 706 LXI D,00000H 710 | 04d3 eb 668 707 XCHG 711 | 04d4 af 669 708 XRA A 712 | 04d5 1a 670 709 LDAX D 713 | 04d6 fe 77 671 710 CPI 077H 714 | 04d8 c4 24 06 672 711 CNZ CPUER ;TEST "LDAX" D AND "XCHG" 715 | 04db af 673 712 XRA A 716 | 04dc 84 674 713 ADD H 717 | 04dd 85 675 714 ADD L 718 | 04de c4 24 06 676 715 CNZ CPUER ;TEST "XCHG" 719 | 04e1 3e cc 677 716 MVI A,0CCH 720 | 04e3 12 678 717 STAX D 721 | 04e4 3a 41 06 679 718 LDA TEMP0 722 | 04e7 fe cc 680 719 CPI 0CCH 723 | 04e9 12 681 720 STAX D 724 | 04ea 3a 41 06 682 721 LDA TEMP0 725 | 04ed fe cc 683 722 CPI 0CCH 726 | 04ef c4 24 06 684 723 CNZ CPUER ;TEST "STAX" D 727 | 04f2 21 77 77 685 724 LXI H,07777H 728 | 04f5 29 686 725 DAD H 729 | 04f6 3e ee 687 726 MVI A,0EEH 730 | 04f8 bc 688 727 CMP H 731 | 04f9 c4 24 06 689 728 CNZ CPUER ;TEST "DAD" H 732 | 04fc bd 690 729 CMP L 733 | 04fd c4 24 06 691 730 CNZ CPUER ;TEST "DAD" H 734 | 0500 21 55 55 692 731 LXI H,05555H 735 | 0503 01 ff ff 693 732 LXI B,0FFFFH 736 | 0506 09 694 733 DAD B 737 | 0507 3e 55 695 734 MVI A,055H 738 | 0509 d4 24 06 696 735 CNC CPUER ;TEST "DAD" B 739 | 050c bc 697 736 CMP H 740 | 050d c4 24 06 698 737 CNZ CPUER ;TEST "DAD" B 741 | 0510 3e 54 699 738 MVI A,054H 742 | 0512 bd 700 739 CMP L 743 | 0513 c4 24 06 701 740 CNZ CPUER ;TEST "DAD" B 744 | 0516 21 aa aa 702 741 LXI H,0AAAAH 745 | 0519 11 33 33 703 742 LXI D,03333H 746 | 051c 19 704 743 DAD D 747 | 051d 3e dd 705 744 MVI A,0DDH 748 | 051f bc 706 745 CMP H 749 | 0520 c4 24 06 707 746 CNZ CPUER ;TEST "DAD" D 750 | 0523 bd 708 747 CMP L 751 | 0524 c4 24 06 709 748 CNZ CPUER ;TEST "DAD" B 752 | 0527 37 710 749 STC 753 | 0528 d4 24 06 711 750 CNC CPUER ;TEST "STC" 754 | 052b 3f 712 751 CMC 755 | 052c dc 24 06 713 752 CC CPUER ;TEST "CMC 756 | 052f 3e aa 714 753 MVI A,0AAH 757 | 0531 2f 715 754 CMA 758 | 0532 fe 55 716 755 CPI 055H 759 | 0534 c4 24 06 717 756 CNZ CPUER ;TEST "CMA" 760 | 0537 b7 718 757 ORA A ;RE-SET AUXILIARY CARRY 761 | 0538 27 719 758 DAA 762 | 0539 fe 55 720 759 CPI 055H 763 | 053b c4 24 06 721 760 CNZ CPUER ;TEST "DAA" 764 | 053e 3e 88 722 761 MVI A,088H 765 | 0540 87 723 762 ADD A 766 | 0541 27 724 763 DAA 767 | 0542 fe 76 725 764 CPI 076H 768 | 0544 c4 24 06 726 765 CNZ CPUER ;TEST "DAA" 769 | 0547 af 727 766 XRA A 770 | 0548 3e aa 728 767 MVI A,0AAH 771 | 054a 27 729 768 DAA 772 | 054b d4 24 06 730 769 CNC CPUER ;TEST "DAA" 773 | 054e fe 10 731 770 CPI 010H 774 | 0550 c4 24 06 732 771 CNZ CPUER ;TEST "DAA" 775 | 0553 af 733 772 XRA A 776 | 0554 3e 9a 734 773 MVI A,09AH 777 | 0556 27 735 774 DAA 778 | 0557 d4 24 06 736 775 CNC CPUER ;TEST "DAA" 779 | 055a c4 24 06 737 776 CNZ CPUER ;TEST "DAA" 780 | 055d 37 738 777 STC 781 | 055e 3e 42 739 778 MVI A,042H 782 | 0560 07 740 779 RLC 783 | 0561 dc 24 06 741 780 CC CPUER ;TEST "RLC" FOR RE-SET CARRY 784 | 0564 07 742 781 RLC 785 | 0565 d4 24 06 743 782 CNC CPUER ;TEST "RLC" FOR SET CARRY 786 | 0568 fe 09 744 783 CPI 009H 787 | 056a c4 24 06 745 784 CNZ CPUER ;TEST "RLC" FOR ROTATION 788 | 056d 0f 746 785 RRC 789 | 056e d4 24 06 747 786 CNC CPUER ;TEST "RRC" FOR SET CARRY 790 | 0571 0f 748 787 RRC 791 | 0572 fe 42 749 788 CPI 042H 792 | 0574 c4 24 06 750 789 CNZ CPUER ;TEST "RRC" FOR ROTATION 793 | 0577 17 751 790 RAL 794 | 0578 17 752 791 RAL 795 | 0579 d4 24 06 753 792 CNC CPUER ;TEST "RAL" FOR SET CARRY 796 | 057c fe 08 754 793 CPI 008H 797 | 057e c4 24 06 755 794 CNZ CPUER ;TEST "RAL" FOR ROTATION 798 | 0581 1f 756 795 RAR 799 | 0582 1f 757 796 RAR 800 | 0583 dc 24 06 758 797 CC CPUER ;TEST "RAR" FOR RE-SET CARRY 801 | 0586 fe 02 759 798 CPI 002H 802 | 0588 c4 24 06 760 799 CNZ CPUER ;TEST "RAR" FOR ROTATION 803 | 058b 01 34 12 761 800 LXI B,01234H 804 | 058e 11 aa aa 762 801 LXI D,0AAAAH 805 | 0591 21 55 55 763 802 LXI H,05555H 806 | 0594 af 764 803 XRA A 807 | 0595 c5 765 804 PUSH B 808 | 0596 d5 766 805 PUSH D 809 | 0597 e5 767 806 PUSH H 810 | 0598 f5 768 807 PUSH PSW 811 | 0599 01 00 00 769 808 LXI B,00000H 812 | 059c 11 00 00 770 809 LXI D,00000H 813 | 059f 21 00 00 771 810 LXI H,00000H 814 | 05a2 3e c0 772 811 MVI A,0C0H 815 | 05a4 c6 f0 773 812 ADI 0F0H 816 | 05a6 f1 774 813 POP PSW 817 | 05a7 e1 775 814 POP H 818 | 05a8 d1 776 815 POP D 819 | 05a9 c1 777 816 POP B 820 | 05aa dc 24 06 778 817 CC CPUER ;TEST "PUSH PSW" AND "POP PSW" 821 | 05ad c4 24 06 779 818 CNZ CPUER ;TEST "PUSH PSW" AND "POP PSW" 822 | 05b0 e4 24 06 780 819 CPO CPUER ;TEST "PUSH PSW" AND "POP PSW" 823 | 05b3 fc 24 06 781 820 CM CPUER ;TEST "PUSH PSW" AND "POP PSW" 824 | 05b6 3e 12 782 821 MVI A,012H 825 | 05b8 b8 783 822 CMP B 826 | 05b9 c4 24 06 784 823 CNZ CPUER ;TEST "PUSH B" AND "POP B" 827 | 05bc 3e 34 785 824 MVI A,034H 828 | 05be b9 786 825 CMP C 829 | 05bf c4 24 06 787 826 CNZ CPUER ;TEST "PUSH B" AND "POP B" 830 | 05c2 3e aa 788 827 MVI A,0AAH 831 | 05c4 ba 789 828 CMP D 832 | 05c5 c4 24 06 790 829 CNZ CPUER ;TEST "PUSH D" AND "POP D" 833 | 05c8 bb 791 830 CMP E 834 | 05c9 c4 24 06 792 831 CNZ CPUER ;TEST "PUSH D" AND "POP D" 835 | 05cc 3e 55 793 832 MVI A,055H 836 | 05ce bc 794 833 CMP H 837 | 05cf c4 24 06 795 834 CNZ CPUER ;TEST "PUSH H" AND "POP H" 838 | 05d2 bd 796 835 CMP L 839 | 05d3 c4 24 06 797 836 CNZ CPUER ;TEST "PUSH H" AND "POP H" 840 | 05d6 21 00 00 798 837 LXI H,00000H 841 | 05d9 39 799 838 DAD SP 842 | 05da 22 46 06 800 839 SHLD SAVSTK ;SAVE THE "OLD" STACK-POINTER! 843 | 05dd 31 45 06 801 840 LXI SP,TEMP4 844 | 05e0 3b 802 841 DCX SP 845 | 05e1 3b 803 842 DCX SP 846 | 05e2 33 804 843 INX SP 847 | 05e3 3b 805 844 DCX SP 848 | 05e4 3e 55 806 845 MVI A,055H 849 | 05e6 32 43 06 807 846 STA TEMP2 850 | 05e9 2f 808 847 CMA 851 | 05ea 32 44 06 809 848 STA TEMP3 852 | 05ed c1 810 849 POP B 853 | 05ee b8 811 850 CMP B 854 | 05ef c4 24 06 812 851 CNZ CPUER ;TEST "LXI","DAD","INX",AND "DCX" SP 855 | 05f2 2f 813 852 CMA 856 | 05f3 b9 814 853 CMP C 857 | 05f4 c4 24 06 815 854 CNZ CPUER ;TEST "LXI","DAD","INX", AND "DCX" SP 858 | 05f7 21 45 06 816 855 LXI H,TEMP4 859 | 05fa f9 817 856 SPHL 860 | 05fb 21 33 77 818 857 LXI H,07733H 861 | 05fe 3b 819 858 DCX SP 862 | 05ff 3b 820 859 DCX SP 863 | 0600 e3 821 860 XTHL 864 | 0601 3a 44 06 822 861 LDA TEMP3 865 | 0604 fe 77 823 862 CPI 077H 866 | 0606 c4 24 06 824 863 CNZ CPUER ;TEST "SPHL" AND "XTHL" 867 | 0609 3a 43 06 825 864 LDA TEMP2 868 | 060c fe 33 826 865 CPI 033H 869 | 060e c4 24 06 827 866 CNZ CPUER ;TEST "SPHL" AND "XTHL" 870 | 0611 3e 55 828 867 MVI A,055H 871 | 0613 bd 829 868 CMP L 872 | 0614 c4 24 06 830 869 CNZ CPUER ;TEST "SPHL" AND "XTHL" 873 | 0617 2f 831 870 CMA 874 | 0618 bc 832 871 CMP H 875 | 0619 c4 24 06 833 872 CNZ CPUER ;TEST "SPHL" AND "XTHL" 876 | 061c 2a 46 06 834 873 LHLD SAVSTK ;RESTORE THE "OLD" STACK-POINTER 877 | 061f f9 835 874 SPHL 878 | 0620 21 36 06 836 875 LXI H,CPUOK 879 | 0623 e9 837 876 PCHL ;TEST "PCHL" 880 | 838 877 ; 881 | 839 878 ; 882 | 840 879 ; 883 | 0624 21 24 01 841 880 CPUER: LXI H,NGCPU ;OUTPUT "CPU HAS FAILED ERROR EXIT=" TO CONSOLE 884 | 0627 cd db 00 842 881 CALL MSG 885 | 062a e3 843 882 XTHL 886 | 062b 7c 844 883 MOV A,H 887 | 062c cd ef 00 845 884 CALL BYTEO ;SHOW ERROR EXIT ADDRESS HIGH BYTE 888 | 062f 7d 846 885 MOV A,L 889 | 0630 cd ef 00 847 886 CALL BYTEO ;SHOW ERROR EXIT ADDRESS LOW BYTE 890 | 0633 c3 64 00 848 887 JMP WBOOT ;EXIT TO CP/M WARM BOOT 891 | 849 888 ; 892 | 850 889 ; 893 | 851 890 ; 894 | 0636 21 0f 01 852 891 CPUOK: LXI H,OKCPU ;OUTPUT "CPU IS OPERATIONAL" TO CONSOLE 895 | 0639 cd db 00 853 892 CALL MSG 896 | 063c c3 64 00 854 893 JMP WBOOT ;EXIT TO CP/M WARM BOOT 897 | 855 894 ; 898 | 856 895 ; 899 | 857 896 ; 900 | 063f 41 06 858 897 TEMPP: DW TEMP0 ;POINTER USED TO TEST "LHLD","SHLD", 901 | 859 898 ; AND "LDAX" INSTRUCTIONS 902 | 860 899 ; 903 | 0641 861 900 TEMP0: DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS 904 | 0642 862 901 TEMP1: DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS 905 | 0643 863 902 TEMP2: DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS 906 | 0644 864 903 TEMP3: DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS 907 | 0645 865 904 TEMP4: DS 1 ;TEMPORARY STORAGE FOR CPU TEST MEMORY LOCATIONS 908 | 0646 866 905 SAVSTK: DS 2 ;TEMPORARY STACK-POINTER STORAGE LOCATION 909 | 867 906 ; 910 | 868 907 ; 911 | 869 908 ; 912 | 073f = 870 909 STACK EQU TEMPP+256 ;DE-BUG STACK POINTER STORAGE AREA 913 | 871 910 ; 914 | 0648 872 911 END 915 | 916 | Symbol table 917 | 918 | ACII 01e0 AIMM 01c5 ANII 0204 BDOS 0005* BYTEO 00ef 919 | BYTO1 00ff BYTO2 0103 BYTO3 010c C010 0222 C020 0238 920 | C030 024e CCI 0297 CMI 0273 CNCI 028b CNZI 02a3 921 | CPEI 0267 CPIE 01dd CPOI 025b CPU 0146 CPUER 0624 922 | CPUOK 0636 CZI 02af DOSW 0091 DOSW1 009c IOCHAR 0001 923 | J010 0151 J020 0157 J030 015d J040 0163 J050 0172 924 | J060 0175 J070 017d J080 0186 J090 0189 J100 0191 925 | J110 019a J120 019d J130 01a5 J140 01ae J150 01b1 926 | J160 01b9 J170 01c2 LOLZ 001e MOVI 02b8 MSG 00db 927 | NGCPU 0124 OKCPU 010f ONCPM 0000 ORII 020e PCHAR 00e6 928 | S8080 00c0 SAVSTK 0646 SBII 01f8 STACK 073f START 0000* 929 | START1 0015 SUII 01ec SW8080 0084 SWFLAG 00a5 SWZ80 008c 930 | SZ80 00a6 TCPI 027f TEMP0 0641 TEMP1 0642* TEMP2 0643 931 | TEMP3 0644 TEMP4 0645 TEMPP 063f WBOOT 0064 WBOOT1 0078 932 | XRII 0218 933 | -------------------------------------------------------------------------------- /srccpm2/Makefile: -------------------------------------------------------------------------------- 1 | CSTDS = -std=c99 -D_DEFAULT_SOURCE # -D_XOPEN_SOURCE=700L 2 | CWARNS= -Wall -Wextra -Wwrite-strings 3 | CFLAGS= -O $(CSTDS) $(CWARNS) 4 | LDFLAGS= -s 5 | 6 | Z80ASMDIR = ~/work/z80pack/z80asm 7 | Z80ASM = $(Z80ASMDIR)/z80asm 8 | Z80ASMFLAGS = -8 -l -T -sn -p0 9 | 10 | all: putsys bios.bin boot.bin 11 | 12 | putsys: putsys.c 13 | $(CC) $(CFLAGS) $(LDFLAGS) -o putsys putsys.c 14 | 15 | bios.bin: bios.asm $(Z80ASM) 16 | $(Z80ASM) $(Z80ASMFLAGS) -fb -x $< 17 | 18 | boot.bin: boot.asm $(Z80ASM) 19 | $(Z80ASM) $(Z80ASMFLAGS) -fb $< 20 | 21 | $(Z80ASM): FORCE 22 | $(MAKE) -C $(Z80ASMDIR) 23 | 24 | FORCE: 25 | 26 | install: 27 | 28 | uninstall: 29 | 30 | clean: 31 | rm -f putsys bios.bin bios.lis boot.bin boot.lis 32 | 33 | distclean: clean 34 | 35 | .PHONY: all FORCE install uninstall clean distclean 36 | -------------------------------------------------------------------------------- /srccpm2/bios.asm: -------------------------------------------------------------------------------- 1 | ; 8080 CBIOS for z80pack machines using SD-FDC 2 | ; 3 | ; Copyright (C) 2024 by Udo Munk 4 | ; 5 | MSIZE EQU 64 ;CP/M memory size in kilobytes 6 | ; 7 | ; "bias" is address offset from 3400H for memory systems 8 | ; than 16K (referred to as "b" throughout the text). 9 | ; 10 | BIAS EQU (MSIZE-20)*1024 11 | CCP EQU 3400H+BIAS ;base of ccp 12 | BDOS EQU CCP+806H ;base of bdos 13 | BIOS EQU CCP+1600H ;base of bios 14 | NSECTS EQU (BIOS-CCP)/128 ;warm start sector count 15 | CDISK EQU 0004H ;current disk number 0=A,...,15=P 16 | IOBYTE EQU 0003H ;Intel I/O byte 17 | FDCCMD EQU 0040H ;FDC command bytes 18 | DDTRK EQU 0 ;offset for track 19 | DDSEC EQU 1 ;offset for sector 20 | DDLDMA EQU 2 ;offset for DMA address low 21 | DDHDMA EQU 3 ;offset for DMA address high 22 | ; 23 | ; I/O ports 24 | ; 25 | CONSTA EQU 0 ;console status port 26 | CONDAT EQU 1 ;console data port 27 | FDC EQU 4 ;port for the FDC 28 | ; 29 | ORG BIOS ;origin of BIOS 30 | ; 31 | ; jump vector for individual subroutines 32 | ; 33 | JMP BOOT ;cold boot 34 | WBE JMP WBOOT ;warm start 35 | JMP CONST ;console status 36 | JMP CONIN ;console character in 37 | JMP CONOUT ;console character out 38 | JMP LIST ;list character out 39 | JMP PUNCH ;punch character out 40 | JMP READER ;reader character in 41 | JMP HOME ;move disk head to home position 42 | JMP SELDSK ;select disk drive 43 | JMP SETTRK ;set track number 44 | JMP SETSEC ;set sector number 45 | JMP SETDMA ;set dma address 46 | JMP READ ;read disk sector 47 | JMP WRITE ;write disk sector 48 | JMP LISTST ;list status 49 | JMP SECTRAN ;sector translate 50 | ; 51 | ; data tables 52 | ; 53 | SIGNON DB MSIZE / 10 + '0',MSIZE MOD 10 + '0' 54 | DB 'K CP/M 2.2 VERS B01',13,10,0 55 | BOOTERR DB 13,10,'BOOT ERROR',13,10,0 56 | ; 57 | ; disk parameter header for disk 0 58 | DPBASE DW TRANS,0000H 59 | DW 0000H,0000H 60 | DW DIRBF,DPBLK 61 | DW CHK00,ALL00 62 | ; disk parameter header for disk 1 63 | DW TRANS,0000H 64 | DW 0000H,0000H 65 | DW DIRBF,DPBLK 66 | DW CHK01,ALL01 67 | ; 68 | ; sector translate table for IBM 8" SD disks 69 | TRANS DB 1,7,13,19 ;sectors 1,2,3,4 70 | DB 25,5,11,17 ;sectors 5,6,7,8 71 | DB 23,3,9,15 ;sectors 9,10,11,12 72 | DB 21,2,8,14 ;sectors 13,14,15,16 73 | DB 20,26,6,12 ;sectors 17,18,19,20 74 | DB 18,24,4,10 ;sectors 21,22,23,24 75 | DB 16,22 ;sectors 25,26 76 | ; 77 | ; disk parameter block for IBM 8" SD disks 78 | DPBLK DW 26 ;sectors per track 79 | DB 3 ;block shift factor 80 | DB 7 ;block mask 81 | DB 0 ;extent mask 82 | DW 242 ;disk size-1 83 | DW 63 ;directory max 84 | DB 192 ;alloc 0 85 | DB 0 ;alloc 1 86 | DW 16 ;check size 87 | DW 2 ;track offset 88 | ; 89 | ; print a message to the console 90 | ; pointer to string in hl 91 | ; 92 | PRTMSG MOV A,M ;get next message byte 93 | ORA A ;is it zero? 94 | RZ ;yes, done 95 | MOV C,A ;no, print character on console 96 | CALL CONOUT 97 | INX H ;and do next 98 | JMP PRTMSG 99 | ; 100 | ; cold start 101 | ; 102 | BOOT LXI SP,80H ;use space below buffer for stack 103 | LXI H,SIGNON ;print signon 104 | CALL PRTMSG 105 | XRA A ;zero in the accumulator 106 | STA CDISK ;select disk drive 0 107 | STA DSKNO 108 | STA IOBYTE ;setup IOBYTE 109 | MVI A,10H ;setup FDC command 110 | OUT FDC 111 | MVI A,FDCCMD AND 0FFH 112 | OUT FDC 113 | MVI A,FDCCMD SHR 8 114 | OUT FDC 115 | STC ;flag for cold start 116 | CMC 117 | JMP GOCPM ;initialize and go to CP/M 118 | ; 119 | ; warm start 120 | ; 121 | WBOOT LXI SP,80H ;use space below buffer for stack 122 | MVI C,0 ;select disk 0 123 | CALL SELDSK 124 | CALL HOME ;go to track 0 125 | MVI B,NSECTS ;B counts # of sectors to load 126 | MVI C,0 ;C has the current track # 127 | MVI D,2 ;D has the next sector to load 128 | LXI H,CCP ;base of CP/M 129 | LOAD1 PUSH B ;save sector count and current track 130 | PUSH D ;save next sector to read 131 | PUSH H ;save DMA address 132 | MOV C,D ;get sector address to C 133 | CALL SETSEC ;set sector address 134 | POP B ;recall DMA address to BC 135 | PUSH B ;and replace on stack for later recall 136 | CALL SETDMA ;set DMA address from BC 137 | CALL READ ;read sector 138 | ORA A ;any errors? 139 | JZ LOAD2 ;no, continue 140 | LXI H,BOOTERR ;otherwise print message 141 | CALL PRTMSG 142 | HLT ;and halt the machine 143 | LOAD2 POP H ;recall DMA address 144 | LXI D,128 ;DMA = DMA + 128 145 | DAD D ;next DMA address now in HL 146 | POP D ;recall sector address 147 | POP B ;recall # of sectors remaining 148 | DCR B ;sectors = sectors - 1 149 | STC ;flag for warm start 150 | JZ GOCPM ;transfer to CP/M if all loaded 151 | INR D ;next sector 152 | MOV A,D ;sector = 27 ? 153 | CPI 27 154 | JC LOAD1 ;no, continue 155 | MVI D,1 ;else begin with sector 1 on next track 156 | INR C 157 | CALL SETTRK 158 | JMP LOAD1 ;for another sector 159 | GOCPM MVI A,0C3H ;C3 is a JMP instruction 160 | STA 0 ;for jmp to wboot 161 | LXI H,WBE ;WBOOT entry point 162 | SHLD 1 ;set address for JMP at 0 163 | STA 5 ;for JMP to BDOS 164 | LXI H,BDOS ;BDOS entry point 165 | SHLD 6 ;set address for JMP at 5 166 | LXI B,80H ;default dma address is 80H 167 | CALL SETDMA 168 | LDA CDISK ;get current disk number 169 | MOV C,A ;send to the CCP 170 | JC CCP+3 ;go to CCP warm start 171 | JMP CCP ;go to CCP cold start 172 | ; 173 | ; console status, return 0FFH if character ready, 00H if not 174 | ; 175 | CONST IN CONSTA ;get console status 176 | RRC ;test bit 0 177 | JC CONST1 ;not ready 178 | MVI A,0FFH ;ready, set flag 179 | RET 180 | CONST1 XRA A ;zero A 181 | RET 182 | ; 183 | ; console input character into register A 184 | ; 185 | CONIN IN CONSTA ;get console status 186 | RRC ;test bit 0 187 | JC CONIN ;not ready 188 | IN CONDAT ;get character from console 189 | RET 190 | ; 191 | ; console output 192 | ; 193 | CONOUT IN CONSTA ;get status 194 | RLC ;test bit 7 195 | JC CONOUT ;wait until transmitter ready 196 | MOV A,C ;get character into accumulator 197 | OUT CONDAT ;send to console 198 | RET 199 | ; 200 | ; printer status, return 0FFH if character ready, 00H if not 201 | ; 202 | LISTST XRA A ;we have no printer 203 | RET ;so never ready 204 | ; 205 | ; line printer output 206 | ; 207 | LIST RET ;we have no printer 208 | ; 209 | ; punch character from register C 210 | ; 211 | PUNCH RET ;we have no puncher 212 | ; 213 | ; read character into register A from reader 214 | ; 215 | READER MVI A,01AH ;we have no reader 216 | RET ;so return CTL-Z 217 | ; 218 | ; move to track 0 position on current disk 219 | ; 220 | HOME MVI C,0 ;select track 0 221 | JMP SETTRK 222 | ; 223 | ; select disk given by register C 224 | ; 225 | SELDSK LXI H,0 ;error return code 226 | MOV A,C ;get disk # to accumulator 227 | CPI 2 ;disk drive < 2 ? 228 | JC SEL1 229 | RET ;no, return with error 230 | SEL1 STA DSKNO ;save disk # 231 | MOV L,C ;HL = disk # 232 | DAD H ;*2 233 | DAD H ;*4 234 | DAD H ;*8 235 | DAD H ;*16 (size of each header) 236 | LXI D,DPBASE 237 | DAD D ;HL=.DPBASE(DISKNO*16) 238 | RET 239 | ; 240 | ; set track given by register C 241 | ; 242 | SETTRK MOV A,C ;get to accumulator 243 | STA FDCCMD+DDTRK ;set in FDC command 244 | RET 245 | ; 246 | ; set sector given by register C 247 | ; 248 | SETSEC MOV A,C ;get to accumulator 249 | STA FDCCMD+DDSEC ;set in FDC command 250 | RET 251 | ; 252 | ; set DMA address given by registers B and C 253 | ; 254 | SETDMA MOV A,C ;low order address 255 | STA FDCCMD+DDLDMA ;set in FDC command 256 | MOV A,B ;high order address 257 | STA FDCCMD+DDHDMA ;set in FDC command 258 | RET 259 | ; 260 | ; perform read operation 261 | ; 262 | READ LDA DSKNO ;get disk # 263 | ORI 20H ;mask in read command 264 | JMP DOIO ;do I/O operation 265 | ; 266 | ; perform write operation 267 | ; 268 | WRITE LDA DSKNO ;get disk # 269 | ORI 40H ;mask in write command 270 | JMP DOIO ;do I/O operation 271 | ; 272 | ; perform read/write I/O 273 | ; 274 | DOIO OUT FDC ;ask FDC to execute the command 275 | IN FDC ;get status from FDC 276 | RET 277 | ; 278 | ; translate the sector given by BC using 279 | ; the translation table given by DE 280 | ; 281 | SECTRAN XCHG ;HL=.TRANS 282 | DAD B ;HL=.TRANS(SECTOR) 283 | XCHG 284 | LDAX D 285 | MOV L,A ;L=TRANS(SECTOR) 286 | MVI H,0 ;HL=TRANS(SECTOR) 287 | RET ;with value in HL 288 | ; 289 | ; The remainder of the CBIOS is reserved uninitialized 290 | ; data area, and does not need to be part of the system 291 | ; memory image. The space must be available, however, 292 | ; between "BEGDAT" and "ENDDAT". 293 | ; 294 | BEGDAT EQU $ ;begin of data area 295 | ; 296 | DSKNO DS 1 ;selected disk 297 | ; 298 | DIRBF DS 128 ;scratch directory area 299 | ALL00 DS 31 ;allocation vector 0 300 | ALL01 DS 31 ;allocation vector 1 301 | CHK00 DS 16 ;check vector 0 302 | CHK01 DS 16 ;check vector 1 303 | ; 304 | ENDDAT EQU $ ;end of data area 305 | DATSIZ EQU $-BEGDAT ;size of data area 306 | ; 307 | END ;of CBIOS 308 | -------------------------------------------------------------------------------- /srccpm2/bios.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udo-munk/Arduino8080/f675ec03fe1c7fd80e3d3026e4b6d82740a2945e/srccpm2/bios.bin -------------------------------------------------------------------------------- /srccpm2/bios.lis: -------------------------------------------------------------------------------- 1 | Z80/8080-Macro-Assembler Release 2.0 2 | 3 | LOC OBJECT CODE LINE STMT SOURCE CODE 4 | 1 1 ; 8080 CBIOS for z80pack machines using SD-FDC 5 | 2 2 ; 6 | 3 3 ; Copyright (C) 2024 by Udo Munk 7 | 4 4 ; 8 | 0040 = 5 5 MSIZE EQU 64 ;CP/M memory size in kilobytes 9 | 6 6 ; 10 | 7 7 ; "bias" is address offset from 3400H for memory systems 11 | 8 8 ; than 16K (referred to as "b" throughout the text). 12 | 9 9 ; 13 | b000 = 10 10 BIAS EQU (MSIZE-20)*1024 14 | e400 = 11 11 CCP EQU 3400H+BIAS ;base of ccp 15 | ec06 = 12 12 BDOS EQU CCP+806H ;base of bdos 16 | fa00 = 13 13 BIOS EQU CCP+1600H ;base of bios 17 | 002c = 14 14 NSECTS EQU (BIOS-CCP)/128 ;warm start sector count 18 | 0004 = 15 15 CDISK EQU 0004H ;current disk number 0=A,...,15=P 19 | 0003 = 16 16 IOBYTE EQU 0003H ;Intel I/O byte 20 | 0040 = 17 17 FDCCMD EQU 0040H ;FDC command bytes 21 | 0000 = 18 18 DDTRK EQU 0 ;offset for track 22 | 0001 = 19 19 DDSEC EQU 1 ;offset for sector 23 | 0002 = 20 20 DDLDMA EQU 2 ;offset for DMA address low 24 | 0003 = 21 21 DDHDMA EQU 3 ;offset for DMA address high 25 | 22 22 ; 26 | 23 23 ; I/O ports 27 | 24 24 ; 28 | 0000 = 25 25 CONSTA EQU 0 ;console status port 29 | 0001 = 26 26 CONDAT EQU 1 ;console data port 30 | 0004 = 27 27 FDC EQU 4 ;port for the FDC 31 | 28 28 ; 32 | 29 29 ORG BIOS ;origin of BIOS 33 | 30 30 ; 34 | 31 31 ; jump vector for individual subroutines 35 | 32 32 ; 36 | fa00 c3 ae fa 33 33 JMP BOOT ;cold boot 37 | fa03 c3 d2 fa 34 34 WBE JMP WBOOT ;warm start 38 | fa06 c3 40 fb 35 35 JMP CONST ;console status 39 | fa09 c3 4b fb 36 36 JMP CONIN ;console character in 40 | fa0c c3 54 fb 37 37 JMP CONOUT ;console character out 41 | fa0f c3 60 fb 38 38 JMP LIST ;list character out 42 | fa12 c3 61 fb 39 39 JMP PUNCH ;punch character out 43 | fa15 c3 62 fb 40 40 JMP READER ;reader character in 44 | fa18 c3 65 fb 41 41 JMP HOME ;move disk head to home position 45 | fa1b c3 6a fb 42 42 JMP SELDSK ;select disk drive 46 | fa1e c3 81 fb 43 43 JMP SETTRK ;set track number 47 | fa21 c3 86 fb 44 44 JMP SETSEC ;set sector number 48 | fa24 c3 8b fb 45 45 JMP SETDMA ;set dma address 49 | fa27 c3 94 fb 46 46 JMP READ ;read disk sector 50 | fa2a c3 9c fb 47 47 JMP WRITE ;write disk sector 51 | fa2d c3 5e fb 48 48 JMP LISTST ;list status 52 | fa30 c3 a9 fb 49 49 JMP SECTRAN ;sector translate 53 | 50 50 ; 54 | 51 51 ; data tables 55 | 52 52 ; 56 | fa33 36 34 53 53 SIGNON DB MSIZE / 10 + '0',MSIZE MOD 10 + '0' 57 | fa35 4b 20 43 50 54 54 DB 'K CP/M 2.2 VERS B01',13,10,0 58 | fa39 2f 4d 20 32 54 55 59 | fa3d 2e 32 20 56 54 56 60 | fa41 45 52 53 20 54 57 61 | fa45 42 30 31 0d 54 58 62 | fa49 0a 00 54 59 63 | fa4b 0d 0a 42 4f 55 60 BOOTERR DB 13,10,'BOOT ERROR',13,10,0 64 | fa4f 4f 54 20 45 55 61 65 | fa53 52 52 4f 52 55 62 66 | fa57 0d 0a 00 55 63 67 | 56 64 ; 68 | 57 65 ; disk parameter header for disk 0 69 | fa5a 7a fa 00 00 58 66 DPBASE DW TRANS,0000H 70 | fa5e 00 00 00 00 59 67 DW 0000H,0000H 71 | fa62 b2 fb 94 fa 60 68 DW DIRBF,DPBLK 72 | fa66 70 fc 32 fc 61 69 DW CHK00,ALL00 73 | 62 70 ; disk parameter header for disk 1 74 | fa6a 7a fa 00 00 63 71 DW TRANS,0000H 75 | fa6e 00 00 00 00 64 72 DW 0000H,0000H 76 | fa72 b2 fb 94 fa 65 73 DW DIRBF,DPBLK 77 | fa76 80 fc 51 fc 66 74 DW CHK01,ALL01 78 | 67 75 ; 79 | 68 76 ; sector translate table for IBM 8" SD disks 80 | fa7a 01 07 0d 13 69 77 TRANS DB 1,7,13,19 ;sectors 1,2,3,4 81 | fa7e 19 05 0b 11 70 78 DB 25,5,11,17 ;sectors 5,6,7,8 82 | fa82 17 03 09 0f 71 79 DB 23,3,9,15 ;sectors 9,10,11,12 83 | fa86 15 02 08 0e 72 80 DB 21,2,8,14 ;sectors 13,14,15,16 84 | fa8a 14 1a 06 0c 73 81 DB 20,26,6,12 ;sectors 17,18,19,20 85 | fa8e 12 18 04 0a 74 82 DB 18,24,4,10 ;sectors 21,22,23,24 86 | fa92 10 16 75 83 DB 16,22 ;sectors 25,26 87 | 76 84 ; 88 | 77 85 ; disk parameter block for IBM 8" SD disks 89 | fa94 1a 00 78 86 DPBLK DW 26 ;sectors per track 90 | fa96 03 79 87 DB 3 ;block shift factor 91 | fa97 07 80 88 DB 7 ;block mask 92 | fa98 00 81 89 DB 0 ;extent mask 93 | fa99 f2 00 82 90 DW 242 ;disk size-1 94 | fa9b 3f 00 83 91 DW 63 ;directory max 95 | fa9d c0 84 92 DB 192 ;alloc 0 96 | fa9e 00 85 93 DB 0 ;alloc 1 97 | fa9f 10 00 86 94 DW 16 ;check size 98 | faa1 02 00 87 95 DW 2 ;track offset 99 | 88 96 ; 100 | 89 97 ; print a message to the console 101 | 90 98 ; pointer to string in hl 102 | 91 99 ; 103 | faa3 7e 92 100 PRTMSG MOV A,M ;get next message byte 104 | faa4 b7 93 101 ORA A ;is it zero? 105 | faa5 c8 94 102 RZ ;yes, done 106 | faa6 4f 95 103 MOV C,A ;no, print character on console 107 | faa7 cd 54 fb 96 104 CALL CONOUT 108 | faaa 23 97 105 INX H ;and do next 109 | faab c3 a3 fa 98 106 JMP PRTMSG 110 | 99 107 ; 111 | 100 108 ; cold start 112 | 101 109 ; 113 | faae 31 80 00 102 110 BOOT LXI SP,80H ;use space below buffer for stack 114 | fab1 21 33 fa 103 111 LXI H,SIGNON ;print signon 115 | fab4 cd a3 fa 104 112 CALL PRTMSG 116 | fab7 af 105 113 XRA A ;zero in the accumulator 117 | fab8 32 04 00 106 114 STA CDISK ;select disk drive 0 118 | fabb 32 b1 fb 107 115 STA DSKNO 119 | fabe 32 03 00 108 116 STA IOBYTE ;setup IOBYTE 120 | fac1 3e 10 109 117 MVI A,10H ;setup FDC command 121 | fac3 d3 04 110 118 OUT FDC 122 | fac5 3e 40 111 119 MVI A,FDCCMD AND 0FFH 123 | fac7 d3 04 112 120 OUT FDC 124 | fac9 3e 00 113 121 MVI A,FDCCMD SHR 8 125 | facb d3 04 114 122 OUT FDC 126 | facd 37 115 123 STC ;flag for cold start 127 | face 3f 116 124 CMC 128 | facf c3 1c fb 117 125 JMP GOCPM ;initialize and go to CP/M 129 | 118 126 ; 130 | 119 127 ; warm start 131 | 120 128 ; 132 | fad2 31 80 00 121 129 WBOOT LXI SP,80H ;use space below buffer for stack 133 | fad5 0e 00 122 130 MVI C,0 ;select disk 0 134 | fad7 cd 6a fb 123 131 CALL SELDSK 135 | fada cd 65 fb 124 132 CALL HOME ;go to track 0 136 | fadd 06 2c 125 133 MVI B,NSECTS ;B counts # of sectors to load 137 | fadf 0e 00 126 134 MVI C,0 ;C has the current track # 138 | fae1 16 02 127 135 MVI D,2 ;D has the next sector to load 139 | fae3 21 00 e4 128 136 LXI H,CCP ;base of CP/M 140 | fae6 c5 129 137 LOAD1 PUSH B ;save sector count and current track 141 | fae7 d5 130 138 PUSH D ;save next sector to read 142 | fae8 e5 131 139 PUSH H ;save DMA address 143 | fae9 4a 132 140 MOV C,D ;get sector address to C 144 | faea cd 86 fb 133 141 CALL SETSEC ;set sector address 145 | faed c1 134 142 POP B ;recall DMA address to BC 146 | faee c5 135 143 PUSH B ;and replace on stack for later recall 147 | faef cd 8b fb 136 144 CALL SETDMA ;set DMA address from BC 148 | faf2 cd 94 fb 137 145 CALL READ ;read sector 149 | faf5 b7 138 146 ORA A ;any errors? 150 | faf6 ca 00 fb 139 147 JZ LOAD2 ;no, continue 151 | faf9 21 4b fa 140 148 LXI H,BOOTERR ;otherwise print message 152 | fafc cd a3 fa 141 149 CALL PRTMSG 153 | faff 76 142 150 HLT ;and halt the machine 154 | fb00 e1 143 151 LOAD2 POP H ;recall DMA address 155 | fb01 11 80 00 144 152 LXI D,128 ;DMA = DMA + 128 156 | fb04 19 145 153 DAD D ;next DMA address now in HL 157 | fb05 d1 146 154 POP D ;recall sector address 158 | fb06 c1 147 155 POP B ;recall # of sectors remaining 159 | fb07 05 148 156 DCR B ;sectors = sectors - 1 160 | fb08 37 149 157 STC ;flag for warm start 161 | fb09 ca 1c fb 150 158 JZ GOCPM ;transfer to CP/M if all loaded 162 | fb0c 14 151 159 INR D ;next sector 163 | fb0d 7a 152 160 MOV A,D ;sector = 27 ? 164 | fb0e fe 1b 153 161 CPI 27 165 | fb10 da e6 fa 154 162 JC LOAD1 ;no, continue 166 | fb13 16 01 155 163 MVI D,1 ;else begin with sector 1 on next track 167 | fb15 0c 156 164 INR C 168 | fb16 cd 81 fb 157 165 CALL SETTRK 169 | fb19 c3 e6 fa 158 166 JMP LOAD1 ;for another sector 170 | fb1c 3e c3 159 167 GOCPM MVI A,0C3H ;C3 is a JMP instruction 171 | fb1e 32 00 00 160 168 STA 0 ;for jmp to wboot 172 | fb21 21 03 fa 161 169 LXI H,WBE ;WBOOT entry point 173 | fb24 22 01 00 162 170 SHLD 1 ;set address for JMP at 0 174 | fb27 32 05 00 163 171 STA 5 ;for JMP to BDOS 175 | fb2a 21 06 ec 164 172 LXI H,BDOS ;BDOS entry point 176 | fb2d 22 06 00 165 173 SHLD 6 ;set address for JMP at 5 177 | fb30 01 80 00 166 174 LXI B,80H ;default dma address is 80H 178 | fb33 cd 8b fb 167 175 CALL SETDMA 179 | fb36 3a 04 00 168 176 LDA CDISK ;get current disk number 180 | fb39 4f 169 177 MOV C,A ;send to the CCP 181 | fb3a da 03 e4 170 178 JC CCP+3 ;go to CCP warm start 182 | fb3d c3 00 e4 171 179 JMP CCP ;go to CCP cold start 183 | 172 180 ; 184 | 173 181 ; console status, return 0FFH if character ready, 00H if not 185 | 174 182 ; 186 | fb40 db 00 175 183 CONST IN CONSTA ;get console status 187 | fb42 0f 176 184 RRC ;test bit 0 188 | fb43 da 49 fb 177 185 JC CONST1 ;not ready 189 | fb46 3e ff 178 186 MVI A,0FFH ;ready, set flag 190 | fb48 c9 179 187 RET 191 | fb49 af 180 188 CONST1 XRA A ;zero A 192 | fb4a c9 181 189 RET 193 | 182 190 ; 194 | 183 191 ; console input character into register A 195 | 184 192 ; 196 | fb4b db 00 185 193 CONIN IN CONSTA ;get console status 197 | fb4d 0f 186 194 RRC ;test bit 0 198 | fb4e da 4b fb 187 195 JC CONIN ;not ready 199 | fb51 db 01 188 196 IN CONDAT ;get character from console 200 | fb53 c9 189 197 RET 201 | 190 198 ; 202 | 191 199 ; console output 203 | 192 200 ; 204 | fb54 db 00 193 201 CONOUT IN CONSTA ;get status 205 | fb56 07 194 202 RLC ;test bit 7 206 | fb57 da 54 fb 195 203 JC CONOUT ;wait until transmitter ready 207 | fb5a 79 196 204 MOV A,C ;get character into accumulator 208 | fb5b d3 01 197 205 OUT CONDAT ;send to console 209 | fb5d c9 198 206 RET 210 | 199 207 ; 211 | 200 208 ; printer status, return 0FFH if character ready, 00H if not 212 | 201 209 ; 213 | fb5e af 202 210 LISTST XRA A ;we have no printer 214 | fb5f c9 203 211 RET ;so never ready 215 | 204 212 ; 216 | 205 213 ; line printer output 217 | 206 214 ; 218 | fb60 c9 207 215 LIST RET ;we have no printer 219 | 208 216 ; 220 | 209 217 ; punch character from register C 221 | 210 218 ; 222 | fb61 c9 211 219 PUNCH RET ;we have no puncher 223 | 212 220 ; 224 | 213 221 ; read character into register A from reader 225 | 214 222 ; 226 | fb62 3e 1a 215 223 READER MVI A,01AH ;we have no reader 227 | fb64 c9 216 224 RET ;so return CTL-Z 228 | 217 225 ; 229 | 218 226 ; move to track 0 position on current disk 230 | 219 227 ; 231 | fb65 0e 00 220 228 HOME MVI C,0 ;select track 0 232 | fb67 c3 81 fb 221 229 JMP SETTRK 233 | 222 230 ; 234 | 223 231 ; select disk given by register C 235 | 224 232 ; 236 | fb6a 21 00 00 225 233 SELDSK LXI H,0 ;error return code 237 | fb6d 79 226 234 MOV A,C ;get disk # to accumulator 238 | fb6e fe 02 227 235 CPI 2 ;disk drive < 2 ? 239 | fb70 da 74 fb 228 236 JC SEL1 240 | fb73 c9 229 237 RET ;no, return with error 241 | fb74 32 b1 fb 230 238 SEL1 STA DSKNO ;save disk # 242 | fb77 69 231 239 MOV L,C ;HL = disk # 243 | fb78 29 232 240 DAD H ;*2 244 | fb79 29 233 241 DAD H ;*4 245 | fb7a 29 234 242 DAD H ;*8 246 | fb7b 29 235 243 DAD H ;*16 (size of each header) 247 | fb7c 11 5a fa 236 244 LXI D,DPBASE 248 | fb7f 19 237 245 DAD D ;HL=.DPBASE(DISKNO*16) 249 | fb80 c9 238 246 RET 250 | 239 247 ; 251 | 240 248 ; set track given by register C 252 | 241 249 ; 253 | fb81 79 242 250 SETTRK MOV A,C ;get to accumulator 254 | fb82 32 40 00 243 251 STA FDCCMD+DDTRK ;set in FDC command 255 | fb85 c9 244 252 RET 256 | 245 253 ; 257 | 246 254 ; set sector given by register C 258 | 247 255 ; 259 | fb86 79 248 256 SETSEC MOV A,C ;get to accumulator 260 | fb87 32 41 00 249 257 STA FDCCMD+DDSEC ;set in FDC command 261 | fb8a c9 250 258 RET 262 | 251 259 ; 263 | 252 260 ; set DMA address given by registers B and C 264 | 253 261 ; 265 | fb8b 79 254 262 SETDMA MOV A,C ;low order address 266 | fb8c 32 42 00 255 263 STA FDCCMD+DDLDMA ;set in FDC command 267 | fb8f 78 256 264 MOV A,B ;high order address 268 | fb90 32 43 00 257 265 STA FDCCMD+DDHDMA ;set in FDC command 269 | fb93 c9 258 266 RET 270 | 259 267 ; 271 | 260 268 ; perform read operation 272 | 261 269 ; 273 | fb94 3a b1 fb 262 270 READ LDA DSKNO ;get disk # 274 | fb97 f6 20 263 271 ORI 20H ;mask in read command 275 | fb99 c3 a4 fb 264 272 JMP DOIO ;do I/O operation 276 | 265 273 ; 277 | 266 274 ; perform write operation 278 | 267 275 ; 279 | fb9c 3a b1 fb 268 276 WRITE LDA DSKNO ;get disk # 280 | fb9f f6 40 269 277 ORI 40H ;mask in write command 281 | fba1 c3 a4 fb 270 278 JMP DOIO ;do I/O operation 282 | 271 279 ; 283 | 272 280 ; perform read/write I/O 284 | 273 281 ; 285 | fba4 d3 04 274 282 DOIO OUT FDC ;ask FDC to execute the command 286 | fba6 db 04 275 283 IN FDC ;get status from FDC 287 | fba8 c9 276 284 RET 288 | 277 285 ; 289 | 278 286 ; translate the sector given by BC using 290 | 279 287 ; the translation table given by DE 291 | 280 288 ; 292 | fba9 eb 281 289 SECTRAN XCHG ;HL=.TRANS 293 | fbaa 09 282 290 DAD B ;HL=.TRANS(SECTOR) 294 | fbab eb 283 291 XCHG 295 | fbac 1a 284 292 LDAX D 296 | fbad 6f 285 293 MOV L,A ;L=TRANS(SECTOR) 297 | fbae 26 00 286 294 MVI H,0 ;HL=TRANS(SECTOR) 298 | fbb0 c9 287 295 RET ;with value in HL 299 | 288 296 ; 300 | 289 297 ; The remainder of the CBIOS is reserved uninitialized 301 | 290 298 ; data area, and does not need to be part of the system 302 | 291 299 ; memory image. The space must be available, however, 303 | 292 300 ; between "BEGDAT" and "ENDDAT". 304 | 293 301 ; 305 | fbb1 = 294 302 BEGDAT EQU $ ;begin of data area 306 | 295 303 ; 307 | fbb1 296 304 DSKNO DS 1 ;selected disk 308 | 297 305 ; 309 | fbb2 298 306 DIRBF DS 128 ;scratch directory area 310 | fc32 299 307 ALL00 DS 31 ;allocation vector 0 311 | fc51 300 308 ALL01 DS 31 ;allocation vector 1 312 | fc70 301 309 CHK00 DS 16 ;check vector 0 313 | fc80 302 310 CHK01 DS 16 ;check vector 1 314 | 303 311 ; 315 | fc90 = 304 312 ENDDAT EQU $ ;end of data area 316 | 00df = 305 313 DATSIZ EQU $-BEGDAT ;size of data area 317 | 306 314 ; 318 | fc90 307 315 END ;of CBIOS 319 | 320 | Symbol table 321 | 322 | ALL00 fc32 ALL01 fc51 BDOS ec06 BEGDAT fbb1 BIAS b000 323 | BIOS fa00 BOOT faae BOOTERR fa4b CCP e400 CDISK 0004 324 | CHK00 fc70 CHK01 fc80 CONDAT 0001 CONIN fb4b CONOUT fb54 325 | CONST fb40 CONST1 fb49 CONSTA 0000 DATSIZ 00df* DDHDMA 0003 326 | DDLDMA 0002 DDSEC 0001 DDTRK 0000 DIRBF fbb2 DOIO fba4 327 | DPBASE fa5a DPBLK fa94 DSKNO fbb1 ENDDAT fc90* FDC 0004 328 | FDCCMD 0040 GOCPM fb1c HOME fb65 IOBYTE 0003 LIST fb60 329 | LISTST fb5e LOAD1 fae6 LOAD2 fb00 MSIZE 0040 NSECTS 002c 330 | PRTMSG faa3 PUNCH fb61 READ fb94 READER fb62 SECTRAN fba9 331 | SEL1 fb74 SELDSK fb6a SETDMA fb8b SETSEC fb86 SETTRK fb81 332 | SIGNON fa33 TRANS fa7a WBE fa03 WBOOT fad2 WRITE fb9c 333 | -------------------------------------------------------------------------------- /srccpm2/boot.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; CP/M 2.2 boot-loader for z80pack machines using SD-FDC 3 | ; 4 | ; Copyright (C) 2024 by Udo Munk 5 | ; 6 | ORG 0 ;mem base of boot 7 | ; 8 | MSIZE EQU 64 ;mem size in kbytes 9 | ; 10 | BIAS EQU (MSIZE-20)*1024 ;offset from 20k system 11 | CCP EQU 3400H+BIAS ;base of the ccp 12 | CPMB EQU BIAS+3400H ;start of CP/M 13 | BOOTE EQU CPMB+1600H ;cold boot entry point 14 | SECTS EQU 51 ;# of sectors to load (26 * 2 - 1) 15 | ; 16 | ; I/O ports 17 | ; 18 | FDC EQU 4 ;FDC port 19 | ; 20 | DI ;disable interrupts 21 | LXI SP,0FFH ;some space for stack 22 | MVI A,10H ;setup command for FDC 23 | OUT FDC 24 | MVI A,CMD AND 0FFH 25 | OUT FDC 26 | MVI A,CMD SHR 8 27 | OUT FDC 28 | LXI B,2 ;B=track 0, C=sector 2 29 | MVI D,SECTS ;D=# sectors to load 30 | ; 31 | ; load the next sector 32 | ; 33 | LSECT MVI A,20H ;tell FDC to read sector on drive 0 34 | OUT FDC 35 | IN FDC ;get result from FDC 36 | ORA A 37 | JZ BOOT1 38 | HLT ;read error, halt CPU 39 | BOOT1 DCR D ;SECTS=SECTS-1 40 | JZ BOOTE ;go to CP/M if all sectors done 41 | INR C ;sector = sector + 1 42 | MOV A,C 43 | CPI 27 ;last sector of track? 44 | JC BOOT2 ;no, do next sector 45 | MVI C,1 ;sector = 1 46 | INR B ;track = track + 1 47 | BOOT2 MOV A,B ;setup command 48 | STA CMD+0 ;save track 49 | MOV A,C 50 | STA CMD+1 ;save sector 51 | PUSH D 52 | LHLD CMD+2 ;get DMA address 53 | LXI D,80H ;and increase it by 128 54 | DAD D 55 | POP D 56 | SHLD CMD+2 ;set new dma address 57 | JMP LSECT ;for next sector 58 | ; 59 | ; command bytes for the FDC 60 | CMD DB 00H ;track 0 61 | DB 02H ;sector 2 62 | DB CPMB AND 0FFH ;DMA address low 63 | DB CPMB SHR 8 ;DMA address high 64 | 65 | END ;of boot loader 66 | -------------------------------------------------------------------------------- /srccpm2/boot.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udo-munk/Arduino8080/f675ec03fe1c7fd80e3d3026e4b6d82740a2945e/srccpm2/boot.bin -------------------------------------------------------------------------------- /srccpm2/boot.lis: -------------------------------------------------------------------------------- 1 | Z80/8080-Macro-Assembler Release 2.0 2 | 3 | LOC OBJECT CODE LINE STMT SOURCE CODE 4 | 1 1 ; 5 | 2 2 ; CP/M 2.2 boot-loader for z80pack machines using SD-FDC 6 | 3 3 ; 7 | 4 4 ; Copyright (C) 2024 by Udo Munk 8 | 5 5 ; 9 | 6 6 ORG 0 ;mem base of boot 10 | 7 7 ; 11 | 0040 = 8 8 MSIZE EQU 64 ;mem size in kbytes 12 | 9 9 ; 13 | b000 = 10 10 BIAS EQU (MSIZE-20)*1024 ;offset from 20k system 14 | e400 = 11 11 CCP EQU 3400H+BIAS ;base of the ccp 15 | e400 = 12 12 CPMB EQU BIAS+3400H ;start of CP/M 16 | fa00 = 13 13 BOOTE EQU CPMB+1600H ;cold boot entry point 17 | 0033 = 14 14 SECTS EQU 51 ;# of sectors to load (26 * 2 - 1) 18 | 15 15 ; 19 | 16 16 ; I/O ports 20 | 17 17 ; 21 | 0004 = 18 18 FDC EQU 4 ;FDC port 22 | 19 19 ; 23 | 0000 f3 20 20 DI ;disable interrupts 24 | 0001 31 ff 00 21 21 LXI SP,0FFH ;some space for stack 25 | 0004 3e 10 22 22 MVI A,10H ;setup command for FDC 26 | 0006 d3 04 23 23 OUT FDC 27 | 0008 3e 45 24 24 MVI A,CMD AND 0FFH 28 | 000a d3 04 25 25 OUT FDC 29 | 000c 3e 00 26 26 MVI A,CMD SHR 8 30 | 000e d3 04 27 27 OUT FDC 31 | 0010 01 02 00 28 28 LXI B,2 ;B=track 0, C=sector 2 32 | 0013 16 33 29 29 MVI D,SECTS ;D=# sectors to load 33 | 30 30 ; 34 | 31 31 ; load the next sector 35 | 32 32 ; 36 | 0015 3e 20 33 33 LSECT MVI A,20H ;tell FDC to read sector on drive 0 37 | 0017 d3 04 34 34 OUT FDC 38 | 0019 db 04 35 35 IN FDC ;get result from FDC 39 | 001b b7 36 36 ORA A 40 | 001c ca 20 00 37 37 JZ BOOT1 41 | 001f 76 38 38 HLT ;read error, halt CPU 42 | 0020 15 39 39 BOOT1 DCR D ;SECTS=SECTS-1 43 | 0021 ca 00 fa 40 40 JZ BOOTE ;go to CP/M if all sectors done 44 | 0024 0c 41 41 INR C ;sector = sector + 1 45 | 0025 79 42 42 MOV A,C 46 | 0026 fe 1b 43 43 CPI 27 ;last sector of track? 47 | 0028 da 2e 00 44 44 JC BOOT2 ;no, do next sector 48 | 002b 0e 01 45 45 MVI C,1 ;sector = 1 49 | 002d 04 46 46 INR B ;track = track + 1 50 | 002e 78 47 47 BOOT2 MOV A,B ;setup command 51 | 002f 32 45 00 48 48 STA CMD+0 ;save track 52 | 0032 79 49 49 MOV A,C 53 | 0033 32 46 00 50 50 STA CMD+1 ;save sector 54 | 0036 d5 51 51 PUSH D 55 | 0037 2a 47 00 52 52 LHLD CMD+2 ;get DMA address 56 | 003a 11 80 00 53 53 LXI D,80H ;and increase it by 128 57 | 003d 19 54 54 DAD D 58 | 003e d1 55 55 POP D 59 | 003f 22 47 00 56 56 SHLD CMD+2 ;set new dma address 60 | 0042 c3 15 00 57 57 JMP LSECT ;for next sector 61 | 58 58 ; 62 | 59 59 ; command bytes for the FDC 63 | 0045 00 60 60 CMD DB 00H ;track 0 64 | 0046 02 61 61 DB 02H ;sector 2 65 | 0047 00 62 62 DB CPMB AND 0FFH ;DMA address low 66 | 0048 e4 63 63 DB CPMB SHR 8 ;DMA address high 67 | 64 64 68 | 0049 65 65 END ;of boot loader 69 | 70 | Symbol table 71 | 72 | BIAS b000 BOOT1 0020 BOOT2 002e BOOTE fa00 CCP e400* 73 | CMD 0045 CPMB e400 FDC 0004 LSECT 0015 MSIZE 0040 74 | SECTS 0033 75 | -------------------------------------------------------------------------------- /srccpm2/cpm.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udo-munk/Arduino8080/f675ec03fe1c7fd80e3d3026e4b6d82740a2945e/srccpm2/cpm.bin -------------------------------------------------------------------------------- /srccpm2/putsys.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Write the CP/M system files to system tracks of drive A 3 | * 4 | * Copyright (C) 1988-2016 by Udo Munk 5 | * Copyright (C) 2024 by Thomas Eberhardt 6 | * 7 | * History: 8 | * 29-APR-1988 Development on TARGON/35 with AT&T Unix System V.3 9 | * 11-MAR-1993 comments in english and ported to COHERENT 4.0 10 | * 02-OCT-2006 modified to compile on modern POSIX OS's 11 | * 10-JAN-2014 lseek POSIX conformance 12 | * 03-APR-2016 disk drive name drivea.dsk 13 | * 27-APR-2024 improve error handling, use simple binary format 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | //#define DISK "../disks/drivea.dsk" 24 | #define DISK "../disks/cpm22.dsk" 25 | 26 | /* 27 | * This program writes the CP/M 2.2 OS from the following files 28 | * onto the system tracks of the boot disk (DISK): 29 | * 30 | * boot loader boot.bin 31 | * CCP cpm.bin 32 | * BDOS cpm.bin 33 | * BIOS bios.bin 34 | */ 35 | int main(void) 36 | { 37 | unsigned char sector[128]; 38 | register int i; 39 | int fd, drivea; 40 | ssize_t n; 41 | off_t o; 42 | 43 | /* open drive A for writing */ 44 | if ((drivea = open(DISK, O_WRONLY)) == -1) { 45 | perror(DISK); 46 | exit(EXIT_FAILURE); 47 | } 48 | 49 | /* open boot loader (boot.bin) for reading */ 50 | if ((fd = open("boot.bin", O_RDONLY)) == -1) { 51 | perror("boot.bin"); 52 | exit(EXIT_FAILURE); 53 | } 54 | /* read boot loader */ 55 | memset((char *) sector, 0, 128); 56 | if (read(fd, (char *) sector, 128) == -1) { 57 | perror("boot.bin"); 58 | exit(EXIT_FAILURE); 59 | } 60 | /* and write it to disk in drive A */ 61 | if ((n = write(drivea, (char *) sector, 128)) != 128) { 62 | fprintf(stderr, DISK ": %s\n", 63 | n == -1 ? strerror(errno) : "short write"); 64 | exit(EXIT_FAILURE); 65 | } 66 | close(fd); 67 | 68 | /* open CP/M system file (cpm.bin) for reading */ 69 | if ((fd = open("cpm.bin", O_RDONLY)) == -1) { 70 | perror("cpm.bin"); 71 | exit(EXIT_FAILURE); 72 | } 73 | /* position to CCP in cpm.bin, needed if created with e.g. SAVE */ 74 | if ((o = lseek(fd, 17 * 128L, SEEK_SET)) != 17 * 128L) { 75 | fprintf(stderr, "cpm.bin: %s\n", 76 | o == -1 ? strerror(errno) : "short file"); 77 | exit(EXIT_FAILURE); 78 | } 79 | /* read CCP and BDOS from cpm.bin and write them to disk in drive A */ 80 | for (i = 0; i < 44; i++) { 81 | if ((n = read(fd, (char *) sector, 128)) != 128) { 82 | fprintf(stderr, "cpm.bin: %s\n", 83 | n == -1 ? strerror(errno) : "short read"); 84 | exit(EXIT_FAILURE); 85 | } 86 | if ((n = write(drivea, (char *) sector, 128)) != 128) { 87 | fprintf(stderr, DISK ": %s\n", 88 | n == -1 ? strerror(errno) : "short write"); 89 | exit(EXIT_FAILURE); 90 | } 91 | } 92 | close(fd); 93 | 94 | /* open BIOS (bios.bin) for reading */ 95 | if ((fd = open("bios.bin", O_RDONLY)) == -1) { 96 | perror("bios.bin"); 97 | exit(EXIT_FAILURE); 98 | } 99 | /* read BIOS from bios.bin and write it to disk in drive A */ 100 | i = 0; 101 | while ((n = read(fd, (char *) sector, 128)) == 128) { 102 | if ((n = write(drivea, (char *) sector, 128)) != 128) { 103 | fprintf(stderr, DISK ": %s\n", 104 | n == -1 ? strerror(errno) : "short write"); 105 | exit(EXIT_FAILURE); 106 | } 107 | i++; 108 | if (i == 6) { 109 | fputs(DISK ": out of space (6 sectors written)", 110 | stderr); 111 | exit(EXIT_FAILURE); 112 | } 113 | } 114 | if (n == -1) { 115 | perror("bios.bin"); 116 | exit(EXIT_FAILURE); 117 | } else if (n > 0) { 118 | memset((char *) §or[n], 0, 128 - n); 119 | if ((n = write(drivea, (char *) sector, 128)) != 128) { 120 | fprintf(stderr, DISK ": %s\n", 121 | n == -1 ? strerror(errno) : "short write"); 122 | exit(EXIT_FAILURE); 123 | } 124 | } 125 | close(fd); 126 | 127 | close(drivea); 128 | return (EXIT_SUCCESS); 129 | } 130 | --------------------------------------------------------------------------------