1
0
mirror of https://github.com/jscrane/r65emu.git synced 2025-04-22 17:37:08 +00:00

Z80 bugfixes ()

This commit is contained in:
Stephen Crane 2025-03-14 14:03:59 +00:00 committed by GitHub
parent 6df4c8de9b
commit 862026462b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
49 changed files with 44402 additions and 469 deletions

@ -1,19 +1,24 @@
#ifndef __CPU_H__
#define __CPU_H__
#pragma once
#undef PC
#define O(o, e) case o: e(); break
#define A(o, e, a) case o: e(a); break
#define C(o) case o:
#define D(e) default: e(); break
#define E(op, expr) case op: expr; break
#define O(op, fn) E(op, fn())
#define A(op, e, a) E(op, e(a))
#define C(op) case op:
#define D(fn) default: fn(); break
#if defined(UNDOCUMENTED_OPS)
#define U(op, expr) case op: expr; break
#else
#define U(op, expr)
#endif
class CPU: public Checkpointable {
public:
virtual ~CPU() {}
virtual void run(unsigned instructions) =0;
virtual void reset() =0;
virtual void raise(uint8_t level) =0;
virtual char *status(char *buf, size_t n, bool hdr = false) =0;
virtual void checkpoint(Stream &s) = 0;
@ -30,4 +35,3 @@ protected:
Memory::address PC;
bool _halted;
};
#endif

37
src/debugging.h Normal file

@ -0,0 +1,37 @@
#pragma once
#define DEBUG_NONE 0x00000000
#define DEBUG_CPU 0x00000001
#define DEBUG_INI 0x00000002
#define DEBUG_PIA 0x00000004
#define DEBUG_VIA 0x00000008
#define DEBUG_DSP 0x00000010
#define DEBUG_EMU 0x00000020
#define DEBUG_MEM 0x00000040
#define DEBUG_ANY 0xffffffff
#if !defined(PRINTER)
#define PRINTER(x) Serial.x
#endif
#if !defined(DEBUGGING)
#define DEBUGGING DEBUG_NONE
#endif
#define _DBG(lvl, x) if (DEBUGGING & lvl) PRINTER(x)
#if DEBUGGING == DEBUG_NONE
#define DBG(x)
#define ERR(x)
#else
#define DBG(x) _DBG(DEBUG_ANY, x)
#define ERR(x) PRINTER(x)
#endif
#define DBG_CPU(x) _DBG(DEBUG_CPU, x)
#define DBG_INI(x) _DBG(DEBUG_INI, x)
#define DBG_PIA(x) _DBG(DEBUG_PIA, x)
#define DBG_VIA(x) _DBG(DEBUG_VIA, x)
#define DBG_DSP(x) _DBG(DEBUG_DSP, x)
#define DBG_EMU(x) _DBG(DEBUG_EMU, x)
#define DBG_MEM(x) _DBG(DEBUG_MEM, x)

@ -1,5 +1,4 @@
#ifndef __DISPLAY_H__
#define __DISPLAY_H__
#pragma once
typedef enum {
portrait, landscape, reverse_portrait, reverse_landscape
@ -79,5 +78,3 @@ private:
unsigned _oxs, _xoff, _yoff;
unsigned _w, _h;
};
#endif

@ -1,5 +1,4 @@
#ifndef __FILER_H__
#define __FILER_H__
#pragma once
class filer: virtual public serialio {
public:
@ -12,5 +11,3 @@ public:
virtual bool start() =0;
virtual void stop() =0;
};
#endif

@ -1,5 +1,4 @@
#ifndef __FLASH_FILER_H__
#define __FLASH_FILER_H__
#pragma once
#define MAX_FILES 5
@ -41,4 +40,3 @@ private:
const char *_programs;
uint8_t _current;
};
#endif

@ -29,35 +29,3 @@ void hardware_cancel_timer(int timer);
#if defined(__SPIRAM_H__) && defined(USE_SPIRAM)
extern class spiram sram;
#endif
#define DEBUG_NONE 0x00000000
#define DEBUG_CPU 0x00000001
#define DEBUG_INI 0x00000002
#define DEBUG_PIA 0x00000004
#define DEBUG_VIA 0x00000008
#define DEBUG_DSP 0x00000010
#define DEBUG_EMU 0x00000020
#define DEBUG_MEM 0x00000040
#define DEBUG_ANY 0xffffffff
#if !defined(DEBUGGING)
#define DEBUGGING DEBUG_NONE
#endif
#define _DBG(lvl, x) if (DEBUGGING & lvl) Serial.x
#if DEBUGGING == DEBUG_NONE
#define DBG(x)
#define ERR(x)
#else
#define DBG(x) _DBG(DEBUG_ANY, x)
#define ERR(x) Serial.x
#endif
#define DBG_CPU(x) _DBG(DEBUG_CPU, x)
#define DBG_INI(x) _DBG(DEBUG_INI, x)
#define DBG_PIA(x) _DBG(DEBUG_PIA, x)
#define DBG_VIA(x) _DBG(DEBUG_VIA, x)
#define DBG_DSP(x) _DBG(DEBUG_DSP, x)
#define DBG_EMU(x) _DBG(DEBUG_EMU, x)
#define DBG_MEM(x) _DBG(DEBUG_MEM, x)

@ -1,5 +1,4 @@
#ifndef __HW_SERIAL_KBD_H__
#define __HW_SERIAL_KBD_H__
#pragma once
/*
* A serial keyboard based around the hardware serial port ("Serial")
@ -16,5 +15,3 @@ public:
private:
HardwareSerial &_serial;
};
#endif

@ -1,9 +1,15 @@
#if defined(ARDUINO)
#include <Arduino.h>
#else
#include <cstdint>
#include <cstdio>
#include <cstring>
#endif
#include <functional>
#include "memory.h"
#include "hardware.h"
#include "debugging.h"
#include "CPU.h"
#include "ports.h"
#include "i8080.h"
void i8080::run(unsigned clocks) {
@ -24,7 +30,7 @@ void i8080::reset() {
_halted = false;
}
void i8080::raise(uint8_t level) {
void i8080::raise(int level) {
if (flags.I) {
flags.I = 0;
_irq_pending = 0;

@ -1,5 +1,4 @@
#ifndef __I8080_H__
#define __I8080_H__
#pragma once
#undef sbi
#undef PC
@ -9,27 +8,35 @@ uint8_t parity(uint8_t);
class i8080: public CPU {
public:
i8080(Memory &m, PortDevice &d): CPU(m), _ports(d) {}
i8080(Memory &m): CPU(m) {}
void run(unsigned);
void reset();
void raise(uint8_t);
void raise(int);
char *status(char *buf, size_t n, bool hdr=false);
void checkpoint(Stream &);
void restore(Stream &);
inline uint8_t a() { return A; }
inline uint8_t b() { return B; }
inline uint8_t c() { return C; }
inline uint8_t d() { return D; }
inline uint8_t e() { return E; }
inline uint8_t h() { return H; }
inline uint8_t l() { return L; }
inline uint16_t bc() { return BC; }
inline uint16_t de() { return DE; }
inline uint16_t hl() { return HL; }
inline uint8_t sr() { return SR; }
void set_port_out_handler(std::function<void(uint16_t, uint8_t)> fn) {
port_out_handler = fn;
}
void set_port_in_handler(std::function<uint8_t(uint16_t)> fn) {
port_in_handler = fn;
}
inline uint8_t a() const { return A; }
inline uint8_t b() const { return B; }
inline uint8_t c() const { return C; }
inline uint8_t d() const { return D; }
inline uint8_t e() const { return E; }
inline uint8_t h() const { return H; }
inline uint8_t l() const { return L; }
inline uint16_t bc() const { return BC; }
inline uint16_t de() const { return DE; }
inline uint16_t hl() const { return HL; }
inline uint8_t sr() const { return SR; }
private:
uint8_t A;
@ -59,8 +66,18 @@ private:
} flags;
uint8_t SR;
};
uint8_t _irq_pending;
PortDevice &_ports;
int _irq_pending;
std::function<void(uint16_t, uint8_t)> port_out_handler;
std::function<uint8_t(uint16_t)> port_in_handler;
inline void _out(uint16_t p, uint8_t v) {
if (port_out_handler) port_out_handler(p, v);
}
inline uint8_t _in(uint16_t p) {
return port_in_handler? port_in_handler(p): 0;
}
void _op(uint8_t op);
@ -400,7 +417,7 @@ private:
inline void rnc() { _ret(!flags.C); }
inline void popd() { DE = _pop(); }
inline void jnc() { _jmp(!flags.C); }
inline void out() { _ports.out(_mem[PC++], A); }
inline void out() { _out(_mem[PC++], A); }
inline void cnc() { _call(!flags.C); }
inline void pushd() { _push(DE); }
inline void sui() { _sub(_mem[PC++]); }
@ -408,7 +425,7 @@ private:
inline void rc() { _ret(flags.C); }
inline void jc() { _jmp(flags.C); }
inline void in() { A = _ports.in(_mem[PC++]); }
inline void in() { A = _in(_mem[PC++]); }
inline void cc() { _call(flags.C); }
inline void sbi() { _sbc(_mem[PC++]); }
@ -446,5 +463,3 @@ private:
inline void cpi() { _cmp(_mem[PC++]); }
inline void rst7() { _push(PC); PC = 0x38; }
};
#endif

@ -1,5 +1,6 @@
#ifndef __MEMORY_H__
#define __MEMORY_H__
#pragma once
class Stream;
class Checkpointable {
public:
@ -41,7 +42,7 @@ public:
virtual Device *get (address at) const { return _pages[at/page_size]; }
Device &operator[] (address a) {
Device &operator[] (address a) const {
Device *d = get (a);
d->access (a);
return *d;
@ -51,4 +52,3 @@ public:
private:
Device *_pages[256];
};
#endif

@ -1,7 +1,7 @@
#include <Arduino.h>
#include <memory.h>
#include "hardware.h"
#include "memory.h"
#include "debugging.h"
#include "pia.h"
// see: https://github.com/mamedev/mame/blob/master/src/devices/machine/6821pia.cpp

@ -1,10 +0,0 @@
#ifndef __PORTS_H__
#define __PORTS_H__
class PortDevice {
public:
virtual void out(uint16_t p, uint8_t v) =0;
virtual uint8_t in(uint16_t p) =0;
};
#endif

@ -1,5 +1,4 @@
#ifndef __PROM_H__
#define __PROM_H__
#pragma once
class prom: public Memory::Device {
public:
@ -11,5 +10,3 @@ public:
private:
const uint8_t *_mem;
};
#endif

@ -1,5 +1,4 @@
#ifndef __PS2_SERIAL_KBD_H__
#define __PS2_SERIAL_KBD_H__
#pragma once
class ps2_serial_kbd: public serial_kbd {
public:
@ -7,5 +6,3 @@ public:
bool available();
void reset();
};
#endif

@ -1,9 +1,15 @@
#if defined(ARDUINO)
#include <Arduino.h>
#else
#include <cstdint>
#include <cstdio>
#include <cstring>
#endif
#include "memory.h"
#include "CPU.h"
#include "r6502.h"
#include "hardware.h"
#include "debugging.h"
void r6502::run(unsigned clocks) {
while (clocks--) {
@ -76,7 +82,7 @@ void r6502::restore(Stream &s)
#endif
}
void r6502::raise(uint8_t level) {
void r6502::raise(int level) {
if (level < 0)
nmi();
else if (!P.bits.I)

@ -1,13 +1,14 @@
#ifndef __R6502_H__
#define __R6502_H__
#pragma once
#undef PC
#undef cli
#undef sei
class Stream;
class r6502: public CPU {
public:
void raise(uint8_t);
void raise(int);
void reset();
void run(unsigned);
char *status(char *buf, size_t n, bool hdr=false);
@ -317,4 +318,3 @@ private:
inline void sbc_ax() { _sbc(_mem[_ax()]); }
inline void inc_ax() { _inc(_ax()); }
};
#endif

@ -20,3 +20,4 @@
#include "hw_serial_kbd.h"
#include "serial_dsp.h"
#include "hw_serial_dsp.h"
#include "debugging.h"

@ -1,5 +1,4 @@
#ifndef __RAM_H__
#define __RAM_H__
#pragma once
template<unsigned n = 1024>
class ram: public Memory::Device {
@ -19,4 +18,3 @@ public:
private:
uint8_t _mem[page_size];
};
#endif

@ -1,5 +1,4 @@
#ifndef __SD_FILER_H__
#define __SD_FILER_H__
#pragma once
class sd_filer: public filer {
public:
@ -22,4 +21,3 @@ public:
private:
const char *_programs;
};
#endif

@ -1,5 +1,4 @@
#ifndef __SERIAL_FILER_H__
#define __SERIAL_FILER_H__
#pragma once
// see https://playground.arduino.cc/Interfacing/LinuxTTY
// FIXME: do this in minicom config file
@ -24,4 +23,3 @@ private:
HardwareSerial &_serial;
unsigned _currsp;
};
#endif

@ -1,5 +1,4 @@
#ifndef __SERIALIO_H__
#define __SERIALIO_H__
#pragma once
#if !defined(SERIAL_5N1)
#define SERIAL_5N1 0x00
@ -43,4 +42,3 @@ public:
virtual uint8_t read() =0;
virtual bool more() { return false; }
};
#endif

@ -1,5 +1,4 @@
#ifndef __SOCKET_FILER_H__
#define __SOCKET_FILER_H__
#pragma once
class socket_filer: public filer {
public:
@ -21,4 +20,3 @@ public:
private:
const char *_hostname;
};
#endif

@ -1,5 +1,4 @@
#ifndef __SOUND_DAC_H__
#define __SOUND_DAC_H__
#pragma once
class DAC {
public:
@ -11,5 +10,3 @@ private:
volatile const uint8_t *_bytes;
volatile unsigned _size, _off;
};
#endif

@ -1,5 +1,4 @@
#ifndef __SPIRAM_H__
#define __SPIRAM_H__
#pragma once
class spiram: public Memory::Device {
public:
@ -12,5 +11,3 @@ public:
spiram(int bytes): Memory::Device(bytes) {}
void begin(uint8_t cs, int module);
};
#endif

Binary file not shown.

File diff suppressed because it is too large Load Diff

12
src/test6502/Makefile Normal file

@ -0,0 +1,12 @@
CPPFLAGS=-I.. -DNO_CHECKPOINT -D'PRINTER(x)=x'
#CPPFLAGS += -DDEBUGGING=0x01
CXXFLAGS=-g
test: test.o r6502.o memory.o
g++ -o $@ $+ $(LDLIBS)
%.o: ../%.cpp ../%.h
g++ $(CXXFLAGS) $(CPPFLAGS) -o $@ -c $<
clean:
rm -f test test.o r6502.o memory.o

65
src/test6502/test.cc Normal file

@ -0,0 +1,65 @@
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <unistd.h>
#include "memory.h"
#include "ram.h"
#include "CPU.h"
#include "r6502.h"
int load(const char *file, Memory &mem) {
int f = open(file, O_RDONLY);
if (f < 0) {
perror("open");
return -1;
}
for (int i = 0; i < 0x10000; i++) {
uint8_t c;
int n = read(f, &c, 1);
if (n == 0) {
fprintf(stderr, "short file: %d\n", i);
break;
}
mem[i] = c;
}
close(f);
return 0;
}
int main(int argc, char *argv[])
{
if (argc < 2) {
fprintf(stderr, "Usage: %s image.bin\n", argv[0]);
return -1;
}
Memory memory;
ram<65536> ram;
memory.put(ram, 0x0000);
if (0 > load(argv[1], memory))
return -1;
// initialise vectored start address
memory[0xfffc] = 0x00;
memory[0xfffd] = 0x04;
Memory::address opc = 0xfffc;
r6502 cpu(memory);
cpu.reset();
char buf[256];
while (true) {
cpu.run(1);
#if defined(DEBUGGING)
puts(cpu.status(buf, sizeof(buf)));
#endif
Memory::address pc = cpu.pc();
if (pc == opc)
break;
opc = pc;
}
puts(cpu.status(buf, sizeof(buf), true));
}

BIN
src/test8080/8080EXER.COM Normal file

Binary file not shown.

146
src/test8080/8080EXER.HEX Normal file

@ -0,0 +1,146 @@
:20010000C31301000000000000000000000000000000002A0600F911F60D0E09CDEA0D21CF
:200120003A017E23B6CA2F012BCDCE0AC3220111130E0E09CDEA0DC300006E01CE012E023F
:200140008E02EE024E03AE030E046E04CE042E058E05EE054E06AE060E076E07CE072E0871
:200160008E08EE084E09AE090E0A6E0A0000FF09000000A5C4C7C426D250A0EA586685C67E
:20018000DEC99B3000000000000000000021F80000000000000000000000000000000000D4
:2001A00000FFFFFFFFFFFFD700FFFF00000000646164203C622C642C682C73703E2E2E2E8E
:2001C0002E2E2E2E2E2E2E2E2E2E2E2E2E24FFC600000040913C7E677A6DDF615B290B1028
:2001E00066B2853800000000000000000000000000000000FF000000FF000000000000002C
:2002000000000000000000D700000000000000616C756F70206E6E2E2E2E2E2E2E2E2E2E4C
:200220002E2E2E2E2E2E2E2E2E2E2E2E2E24FF800000003EC53A574D4C030109E366A6D0CC
:200240003BBBAD3F00000000000000000000000000000000FF000000000000FF00000000BE
:20026000000000FFFFFFFFD700000000000000616C756F70203C622C632C642C652C682C5C
:200280006C2C6D2C613E2E2E2E2E2E2E2E24FF270000004121FA09601D59A55B8D7990042D
:2002A0008E9D2918000000000000000000000000000000D7FF0000000000000000000000FC
:2002C0000000000000000000000000000000003C6461612C636D612C7374632C636D633E4C
:2002E0002E2E2E2E2E2E2E2E2E2E2E2E2E24FF3C000000DF4AD8D598E52B8AB0A71B434448
:200300005A30D00100000000000000000000000000000000FF000000000000000000000083
:2003200000000000000000D7000000000000003C696E722C6463723E20612E2E2E2E2E2E29
:200340002E2E2E2E2E2E2E2E2E2E2E2E2E24FF0400000023D62D43617A8081865A851E86D2
:2003600058BB9B010000000000000000000000000000FF00000000000000000000000000CF
:2003800000000000000000D7000000000000003C696E722C6463723E20622E2E2E2E2E2EC8
:2003A0002E2E2E2E2E2E2E2E2E2E2E2E2E24FF0300000097CDAB44C98DE3E3CC11A4E802E7
:2003C000494D2A080000000000000000000000000021F8000000000000000000000000003C
:2003E00000000000000000D7000000000000003C696E782C6463783E20622E2E2E2E2E2E5C
:200400002E2E2E2E2E2E2E2E2E2E2E2E2E24FF0C00000089D735095B05859F278B08D29514
:200420000560060100000000000000000000000000FF000000000000000000000000000051
:2004400000000000000000D7000000000000003C696E722C6463723E20632E2E2E2E2E2E06
:200460002E2E2E2E2E2E2E2E2E2E2E2E2E24FF14000000EAA0BA5FFB651C98CC38BCDE4357
:200480005CBD0301000000000000000000000000FF00000000000000000000000000000040
:2004A00000000000000000D7000000000000003C696E722C6463723E20642E2E2E2E2E2EA5
:2004C0002E2E2E2E2E2E2E2E2E2E2E2E2E24FF130000002E341D13C928CA0A67992E3A923F
:2004E000F6549D08000000000000000000000021F8000000000000000000000000000000F4
:2005000000000000000000D7000000000000003C696E782C6463783E20642E2E2E2E2E2E38
:200520002E2E2E2E2E2E2E2E2E2E2E2E2E24FF1C0000002F600D4C0224F5E2F4A00AA113EF
:20054000322559010000000000000000000000FF00000000000000000000000000000000EB
:2005600000000000000000D7000000000000003C696E722C6463723E20652E2E2E2E2E2EE3
:200580002E2E2E2E2E2E2E2E2E2E2E2E2E24FF240000000615EBF2DDE82B26A6111ABC170C
:2005A0000618280100000000000000000000FF0000000000000000000000000000000000F5
:2005C00000000000000000D7000000000000003C696E722C6463723E20682E2E2E2E2E2E80
:2005E0002E2E2E2E2E2E2E2E2E2E2E2E2E24FF23000000F4C3A5076D1B044FC2E22A82577A
:20060000E0E1C30800000000000000000021F8000000000000000000000000000000000035
:2006200000000000000000D7000000000000003C696E782C6463783E20682E2E2E2E2E2E13
:200640002E2E2E2E2E2E2E2E2E2E2E2E2E24FF2C000000318020A5564309B4C1F4A2DFD122
:200660003CA23E01000000000000000000FF0000000000000000000000000000000000005E
:2006800000000000000000D7000000000000003C696E722C6463723E206C2E2E2E2E2E2EBB
:2006A0002E2E2E2E2E2E2E2E2E2E2E2E2E24FF3400000056B87C0C3EE503017E8758DA1584
:2006C0005C371F01000000FF00000000000000000000000000000000000000000000000068
:2006E00000000000000000D7000000000000003C696E722C6463723E206D2E2E2E2E2E2E5A
:200700002E2E2E2E2E2E2E2E2E2E2E2E2E24FF330000006F3482D469D1B6DE94A476F45371
:20072000025B8508000000000000000000000000000000000021F8000000000000000000B6
:2007400000000000000000D7000000000000003C696E782C6463783E2073702E2E2E2E2EA5
:200760002E2E2E2E2E2E2E2E2E2E2E2E2E24FF2A030100639830787720FEB1FAB9B8AB04CF
:20078000061560000000000000000000000000000000000000000000000000FFFF000000E0
:2007A0000000000000000000000000000000006C686C64206E6E6E6E2E2E2E2E2E2E2E2E4D
:2007C0002E2E2E2E2E2E2E2E2E2E2E2E2E24FF2203010003D07277537F723FEA6480E1107C
:2007E0002DE9350000000000000000000000000000000000000000000000000000000000AE
:2008000000FFFF00000000000000000000000073686C64206E6E6E6E2E2E2E2E2E2E2E2EE7
:200820002E2E2E2E2E2E2E2E2E2E2E2E2E24FF010000001C5C462DB98E7860B1740EB34608
:20084000D1CC30300000000000000000000000000000000000000000FFFF0000000000009D
:200860000000000000000000000000000000006C7869203C622C642C682C73703E2C6E6EF4
:200880006E6E2E2E2E2E2E2E2E2E2E2E2E24FF0A000000A8B32A1D8E7FAC4203010301C6EA
:2008A000B18EEF100000000000000000000000000000000000000000000000FF00000000FB
:2008C00000000000000000D7FF0000000000006C646178203C622C643E2E2E2E2E2E2E2ECB
:2008E0002E2E2E2E2E2E2E2E2E2E2E2E2E24FF0600000007C49DF43DD1390389DE55745350
:20090000C00955380000000000000000000000000000000000000000000000000000000081
:200920000000000000000000FF0000000000006D7669203C622C632C642C652C682C6C2CA6
:200940006D2C613E2C6E6E2E2E2E2E2E2E24FF40000000A47224A0AC610301C7828F719715
:200960008F8EEF3F0000000000000000000000000000000000000000000000FF000000002D
:20098000000000FFFFFFFFD7FF0000000000006D6F76203C62636465686C613E2C3C6263A9
:2009A0006465686C613E2E2E2E2E2E2E2E24FF3203010068FDECF4A04443B55306BACDD28D
:2009C0004FD81F080000000000000000000000000000000000000000000000FF00000000CA
:2009E00000000000000000D7FF000000000000737461206E6E6E6E202F206C6461206E6E65
:200A00006E6E2E2E2E2E2E2E2E2E2E2E2E24FF0700000092CB436D900A84C2530C0EF591F6
:200A2000EBFC401800000000000000000000000000000000FF000000000000000000000078
:200A400000000000000000D7000000000000003C726C632C7272632C72616C2C7261723EB5
:200A60002E2E2E2E2E2E2E2E2E2E2E2E2E24FF020000003B0C92B5FF6C9E9503010401C105
:200A800021E7BD180000000000000000000000000000000000000000000000FFFF0000007B
:200AA0000000000000000000FF00000000000073746178203C622C643E2E2E2E2E2E2E2EA9
:200AC0002E2E2E2E2E2E2E2E2E2E2E2E2E24E57E23666F7E327F0D23E51114001911DE0CC4
:200AE000CD4D0CE1E51128001911060DCD4D0C21060D3601E1E5114D0D0104007E12231307
:200B00000B78B1C2FC0A1103010110007E1223130B78B1C20C0B112C0019EB0E09CDEA0DC4
:200B2000CD8E0E3A4D0DFE76CA3A0BE6DFFEDDC2370B3A4E0DFE76C42E0DCD8D0CC4B10CA2
:200B4000E1CA760B113C0019CD4F0E11220ECA6D0B11290E0E09CDEA0DCDB50D11440E0E33
:200B600009CDEA0D21A20ECDB50D114C0E0E09CDEA0DE12323C9E53E0132EC0B32100C2156
:200B8000DE0C22ED0B21060D22110C0604E1E5114D0DCDA00B0610110301CDA00BC3230B97
:200BA000CDA90B2305C2A00BC9C5D5E54E111400197EFE00CACA0B06080FF53E00DCEF0B0A
:200BC000A90F4FF105C2B90B0608111400197EFE00CAE50B06080FF53E00DC130CA90F4FBE
:200BE000F105C2D60BE1D1791213C1C9000000C5E52AED0B4621EC0B7E4F0777FE01C20844
:200C00000C2AED0B2322ED0B78A1E1C1C83E01C9000000C5E52A110C4621100C7E4F07771F
:200C2000FE01C22C0C2A110C2322110C78A1E1C1C83E01C9F5C5D5E53600545D130B7E127E
:200C400023130B78B1C23E0CE1D1C1F1C9D5EB012800CD340CEB06140E0116005E7BA1CA8D
:200C6000630C1479074FFE01C25D0C2305C25C0C7AE6F80F0F0F6F26007AE6073C473E80E4
:200C80000705C2800CD1191114001977C9C5D5E521DE0C111400EB19EB347EFE00CAAC0CC2
:200CA000471AA0CAA80C3600C1D1E1C92313C3990CC5D5E521060D111400EB19EB7EB7CADF
:200CC000D90C471AA0C2D50C7807FE01C2D30C3600231377AFE1D1C1C92313C3BD0C0000DC
:200CE0000000000000000000000000000000000000000000000000000000000000000000F4
:200D00000000000000000000000000000000000000000000000000000000000000000000D3
:200D20000000000000000000000000000000F5C5D5E5F32100003922A90D310501E1E1E140
:200D4000D1C1F122970D2A1101F92A970D0000000022970D210000DA5E0D39C3600D393742
:200D600022A70D2A970D31A70DF5C5D5E5E5E52AA90DF9FB2A030122990D21A50D7EE6FFAC
:200D800077061011990D21A20E1A13CD660E05C2890DE1D1C1F1C900000000000000000046
:200DA00000000000000000000000007ECDC70D2305C2AB0DC9F5C5E506047ECDC70D2305B9
:200DC000C2BA0DE1C1F1C9F50F0F0F0FCDD00DF1F5C5D5E5E60FFE0ADADD0DC627C6305FEB
:200DE0000E02CDEA0DE1D1C1F1C9F5C5D5E5CD0500E1D1C1F1C93830383020696E7374725F
:200E0000756374696F6E206578657263697365720A0D24546573747320636F6D706C6574F4
:200E2000652420204F4B0A0D2420204552524F52202A2A2A2A206372632065787065637481
:200E400065643A2420666F756E643A240A0D24C5D5E511A20E06041ABEC2620E231305C245
:200E6000570EE1D1C1C9F5C5D5E5E511030019AE6F26002929EB21A60E19EBE10104001AF2
:200E8000A8467713230DC27F0EE1D1C1F1C9F5C5E521A20E3EFF0604772305C2980EE1C1CE
:200EA000F1C9000000000000000077073096EE0E612C990951BA076DC419706AF48FE96304
:200EC000A5359E6495A30EDB883279DCB8A4E0D5E91E97D2D98809B64C2B7EB17CBDE7B8E7
:200EE0002D0790BF1D911DB710646AB020F2F3B9714884BE41DE1ADAD47D6DDDE4EBF4D461
:200F0000B55183D385C7136C9856646BA8C0FD62F97A8A65C9EC14015C4F63066CD9FA0F98
:200F20003D638D080DF53B6E20C84C69105ED56041E4A26771723C03E4D14B04D447D20D43
:200F400085FDA50AB56B35B5A8FA42B2986CDBBBC9D6ACBCF94032D86CE345DF5C75DCD6E6
:200F60000DCFABD13D5926D930AC51DE003AC8D75180BFD0611621B4F4B556B3C423CFBAD2
:200F80009599B8BDA50F2802B89E5F058808C60CD9B2B10BE9242F6F7C8758684C11C16180
:200FA0001DABB6662D3D76DC419001DB710698D220BCEFD5102A71B1858906B6B51F9FBF0B
:200FC000E4A5E8B8D4337807C9A20F00F9349609A88EE10E98187F6A0DBB086D3D2D9164C2
:200FE0006C97E6635C016B6B51F41C6C6162856530D8F262004E6C0695ED1B01A57B820894
:20100000F4C1F50FC45765B0D9C612B7E9508BBEB8EAFCB9887C62DD1DDF15DA2D498CD3A3
:201020007CF3FBD44C654DB261583AB551CEA3BC0074D4BB30E24ADFA5413DD895D7A4D182
:20104000C46DD3D6F4FB4369E96A346ED9FCAD678846DA60B8D044042D7333031DE5AA0AD9
:201060004C5FDD0D7CC95005713C270241AABE0B1010C90C20865768B525206F85B3B9669D
:20108000D409CE61E49F5EDEF90E29D9C998B0D09822C7D7A8B459B33D172EB40D81B7BDA3
:2010A0005C3BC0BA6CADEDB883209ABFB3B603B6E20C74B1D29AEAD547399DD277AF04DB11
:2010C000261573DC1683E3630B1294643B840D6D6A3E7A6A5AA8E40ECF0B9309FF9D0A00BD
:2010E000AE277D079EB1F00F93448708A3D21E01F2686906C2FEF762575D806567CB196C1D
:2011000036716E6B06E7FED41B7689D32BE010DA7A5A67DD4ACCF9B9DF6F8EBEEFF917B77E
:20112000BE4360B08ED5D6D6A3E8A1D1937E38D8C2C44FDFF252D1BB67F1A6BC57673FB581
:2011400006DD48B2364BD80D2BDAAF0A1B4C36034AF641047A60DF60EFC3A867DF55316EBC
:201160008EEF4669BE79CB61B38CBC66831A256FD2A05268E236CC0C7795BB0B47032202F2
:2011800016B95505262FC5BA3BBEB2BD0B282BB45A925CB36A04C2D7FFA7B5D0CF312CD9A6
:2011A0009E8B5BDEAE1D9B64C2B0EC63F226756AA39C026D930A9C0906A9EB0E363F7207C5
:2011C00067850500571395BF4A82E2B87A147BB12BAE0CB61B3892D28E9BE5D5BE0D7CDCE8
:2011E000EFB70BDBDF2186D3D2D4F1D4E24268DDB3F81FDA836E81BE16CDF6B9265B6FB030
:2012000077E118B7477788085AE6FF0F6A7066063BCA11010B5C8F659EFFF862AE69616B79
:20122000FFD3166CCF45A00AE278D70DD2EE4E0483543903B3C2A7672661D06016F7496940
:20124000474D3E6E77DBAED16A4AD9D65ADC40DF0B6637D83BF0A9BCAE53DEBB9EC547B2BF
:20126000CF7F30B5FFE9BDBDF21CCABAC28A53B3933024B4A3A6BAD03605CDD7069354DEDD
:20128000572923D967BFB3667A2EC4614AB85D681B022A6F2B94B40BBE37C30C8EA15A0579
:0612A000DF1B2D02EF8DA3
:00000001FF
069354DEDD
:20128000572923D967BFB3667A2EC4614AB85D681B022A6F2B94B40BBE37C30C8EA15A0579
:0612A000DF1B2D02EF8DA3

1284
src/test8080/8080EXER.MAC Normal file

File diff suppressed because it is too large Load Diff

BIN
src/test8080/8080EXER.PNG Normal file

Binary file not shown.

After

(image error) Size: 55 KiB

BIN
src/test8080/8080EXM.COM Normal file

Binary file not shown.

BIN
src/test8080/8080PRE.COM Normal file

Binary file not shown.

35
src/test8080/8080PRE.HEX Normal file

@ -0,0 +1,35 @@
:200100003E01FE02CA0000FE01C20000C3110176FFCD1701C30000E17CFE01CA2101C30018
:20012000007DFE14CA2A01C30000319903F1C1D1E131A903E5D5C5F53AA103FE02C2000056
:200140003AA203FE04C200003AA303FE06C200003AA403FE08C200003AA503FE0AC2000001
:200160003AA603FE0CC200003AA703FE0EC200003AA803FE10C2000021A9037EFEA5C200B9
:200180000021AA037EFE3CC20000310005219501E5C9C300003EFFE60FFE0FC200003E5A20
:2001A000E60FFE0AC200000FFE05C200000FFE82C200000FFE41C200000FFEA0C2000021BB
:2001C0003412E5C178FE12C2000079FE34C20000210100E5F1DCDB01C35203E121D600E5F7
:2001E000F1D4E701C35203E121F501E5210100E5F1D8CD5203210202E521D600E5F1D0CDF7
:200200005203210100E5F1DA0D02CD520321D600E5F1D21802CD5203210400E5F1EC23029F
:20022000C35203E121D300E5F1E42F02C35203E1213D02E5210400E5F1E8CD5203214A0236
:20024000E521D300E5F1E0CD5203210400E5F1EA5502CD520321D300E5F1E26002CD52035F
:20026000214000E5F1CC6B02C35203E1219700E5F1C47702C35203E1218502E5214000E57E
:20028000F1C8CD5203219202E5219700E5F1C0CD5203214000E5F1CA9D02CD5203219700FF
:2002A000E5F1C2A802CD5203218000E5F1FCB302C35203E1215700E5F1F4BF02C35203E1BD
:2002C00021CD02E5218000E5F1F8CD520321DA02E5215700E5F1F0CD5203218000E5F1FA05
:2002E000E502CD5203215700E5F1F2F002CD520321F702E9CD52033EA506040F05C2FB02BC
:20030000FE5AC4520306103C05C20703FE6AC4520306002100002305C216037CFE01C4520D
:20032000037DFE00C452031132030E09CD0500C3000038303830205072656C696D696E61A3
:20034000727920746573747320636F6D706C65746524C12604780F0F0F0FE60F6F7ECD8AEA
:200360000378E60F6F7ECD8A03790F0F0F0FE60F6F7ECD8A0379E60F6F7ECD8A033E0DCD08
:200380008A033E0ACD8A03C30000F5C5D5E50E025FCD0500E1D1C1F1C9020406080A0C0E51
:2003A000100000000000000000A53C0000000000000000000000000000000000000000004C
:2003C00000000000000000000000000000000000000000000000000000000000000000001D
:2003E0000000000000000000000000000000000000000000000000000000000000000000FD
:2004000030313233343536373839616263646566000000000000000000000000000000007A
:200420000000000000000000000000000000000000000000000000000000000000000000BC
:2004400000000000000000000000000000000000000000000000000000000000000000009C
:2004600000000000000000000000000000000000000000000000000000000000000000007C
:2004800000000000000000000000000000000000000000000000000000000000000000005C
:2004A00000000000000000000000000000000000000000000000000000000000000000003C
:2004C00000000000000000000000000000000000000000000000000000000000000000001C
:2004E0000000000000000000000000000000000000000000000000000000000000000000FC
:00010001FE
0000000000000000000000000000000001C
:2004E000000000000000000000000000000000000000

BIN
src/test8080/CPUTEST.COM Normal file

Binary file not shown.

11
src/test8080/Makefile Normal file

@ -0,0 +1,11 @@
CPPFLAGS:=-I.. -DUNDOCUMENTED_OPS -DNO_CHECKPOINT -D'PRINTER(x)=x'
CXXFLAGS:=-g
test: test.o i8080.o memory.o
g++ -o $@ $+
%.o: ../%.cpp ../%.h
g++ $(CXXFLAGS) $(CPPFLAGS) -o $@ -c $<
clean:
rm -f test test.o memory.o i8080.o

18
src/test8080/README.md Normal file

@ -0,0 +1,18 @@
See:
* http://www.idb.me.uk/sunhillow/8080.html
* https://en.wikipedia.org/wiki/Intel_HEX
* http://altairclone.com/downloads/cpu_tests/+README.TXT
Files:
- 8080/8085 CPU Diagnostic, version 1.0, by Microcosm Associates (file
`TEST.COM`)
- Diagnostics II, version 1.2, CPU test by Supersoft Associates (file
`CPUTEST.COM`)
From this thread: http://compgroups.net/comp.os.cpm/8080-exerciser/947077
> I assume that is the difference in the state of the AC flag after
> subtract and decrement?
Seems that the half-carry flag was poorly understood --- probably in
the same way as the 6502's.

BIN
src/test8080/TEST.COM Normal file

Binary file not shown.

BIN
src/test8080/TST8080.COM Normal file

Binary file not shown.

111
src/test8080/test.cc Normal file

@ -0,0 +1,111 @@
#include <fcntl.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <unistd.h>
#include <functional>
#include "memory.h"
#include "CPU.h"
#include "i8080.h"
#include "ram.h"
// the test program calls BDOS CONOUT via the BDOS entry point at 05H
// http://www.mccm.hetlab.tk/millennium/milc/disk/topic_18.htm
// The function number is in register C: 2 for char output or 9 for string
int load_hex(Memory &memory, const char *file) {
FILE *f = fopen(file, "r");
if (!f) {
perror("fopen");
return -1;
}
char line[520];
int err = 0;
while (fgets(line, sizeof(line), f)) {
char *p = line;
unsigned n, t, a, a0, a1, x;
if (*p++ != ':')
break;
sscanf(p, "%02x%02x%02x%02x", &n, &a1, &a0, &t);
if (n == 0 || t == 1)
break;
a = a1 << 8 | a0;
p += 8;
int cs = n + t + a0 + a1;
for (int i = 0; i < n; i++) {
sscanf(p, "%02x", &x);
p += 2;
cs += x;
memory[a++] = x;
}
sscanf(p, "%02x", &x);
cs = (1 + ~(cs & 0xff)) & 0xff;
if (cs != x) {
printf("checksum error: %02x %02x\n", cs, x);
err = -1;
break;
}
}
fclose(f);
return err;
}
int load_com(Memory &memory, const char *file, unsigned short a) {
int fd = open(file, O_RDONLY);
if (fd < 0) {
perror("open");
return -1;
}
unsigned char c;
while (read(fd, &c, 1) == 1)
memory[a++] = c;
close(fd);
return 0;
}
int main(int argc, char *argv[])
{
if (argc < 2) {
fprintf(stderr, "Usage: %s image\n", argv[0]);
return -1;
}
Memory memory;
ram<65536> ram;
memory.put(ram, 0x0000);
if (0 > load_com(memory, argv[1], 0x100))
return -1;
i8080 cpu(memory);
cpu.set_port_out_handler([&cpu, &memory](uint16_t port, uint8_t a) {
if (port == 0) {
if (a == 2)
putchar(cpu.e());
else if (a == 9) {
char c;
uint16_t a = cpu.de();
while ((c = memory[a++]) != '$')
putchar(c);
putchar('\n');
}
}
});
cpu.reset();
cpu.run(256);
memory[0] = 0x76; // hlt
memory[5] = 0x79; // movac
memory[6] = 0xd3; // out
memory[7] = 0x00; // port 0
memory[8] = 0xd9; // ret
Memory::address opc = cpu.pc();
while (true) {
cpu.run(1);
Memory::address pc = cpu.pc();
if (pc == opc)
break;
opc = pc;
}
}

@ -3,6 +3,7 @@
#include "memory.h"
#include "via.h"
#include "hardware.h"
#include "debugging.h"
#define PORTB 0x00
#define PORTA 0x01

@ -1,20 +1,40 @@
#if defined(ARDUINO)
#include <Arduino.h>
#else
#include <cstdint>
#include <cstdio>
#include <cstring>
#define println(x) puts(x)
#endif
#include <functional>
#include "memory.h"
#include "hardware.h"
#include "ports.h"
#include "debugging.h"
#include "CPU.h"
#include "z80.h"
char *z80::status(char *buf, size_t n, bool hdr) {
#if DEBUGGING & DEBUG_CPU
uint8_t op = _mem[PC];
static bool first = true;
snprintf(buf, n,
"%s%04x %02x %04x %04x %04x %04x %04x %04x %04x %04x %04x %d%d%d "
"%04x %d%d%d%d%d%d%d%d",
hdr? "_pc_ op _af_ _bc_ _de_ _hl_ _af' _bc' _de' _hl' _ir_ imff _sp_ sz5h3pnc\r\n": "",
PC, op, AF, BC, DE, HL, AF_, BC_, DE_, HL_, IR, _im, _iff1, _iff2,
SP, flags.S, flags.Z, flags._5, flags.H, flags._3, flags.P, flags.N, flags.C);
"%s%04x %02x %d%d%d%d%d%d %02x %02x %d%d %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x ",
hdr || first? "PC A SZHPNC I R IFF BC DE HL A'F' B'C' D'E' H'L' IX IY SP OP\r\n": "",
PC, A, flags.S, flags.Z, flags.H, flags.P, flags.N, flags.C, I, R & 0x7f, _iff1, _iff2,
BC, DE, HL, AF_, BC_, DE_, HL_, IX, IY, SP);
first = false;
uint8_t op = _mem[PC], op1 = _mem[PC+1];
char obuf[16];
if (op == 0xdd || op == 0xfd) {
if (op1 == 0xcb)
snprintf(obuf, sizeof(obuf), "%02x cb %02x %02x", op, (uint8_t)_mem[PC+2], (uint8_t)_mem[PC+3]);
else
snprintf(obuf, sizeof(obuf), "%02x %02x", op, op1);
} else if (op == 0xed)
snprintf(obuf, sizeof(obuf), "ed %02x", op1);
else
snprintf(obuf, sizeof(obuf), "%02x", op);
strncat(buf, obuf, n);
#endif
return buf;
}
@ -39,7 +59,10 @@ void z80::checkpoint(Stream &s) {
s.write(_iff2);
s.write(_ts);
s.write(_halted);
s.write(_irq_pending);
s.write(_int_nmi);
s.write(_int_irq);
s.write(_int_prot);
s.write(_irq_data);
#endif
}
@ -63,7 +86,10 @@ void z80::restore(Stream &s) {
_iff2 = s.read();
_ts = s.read();
_halted = s.read();
_irq_pending = s.read();
_int_nmi = s.read();
_int_irq = s.read();
_int_prot = s.read();
_irq_data = s.read();
#endif
}
@ -76,49 +102,70 @@ uint8_t z80::_fetch_op() {
return op;
}
void z80::run(unsigned clocks) {
while (clocks--) {
if (_irq_pending)
void z80::run(unsigned instructions) {
while (instructions--) {
if (_int_nmi) {
_handle_nmi();
_int_nmi = false;
} else if (!_int_prot && _int_irq && _iff1) {
_handle_interrupt();
op(_fetch_op());
_int_irq = false;
}
if (_halted)
break;
_int_prot = false;
op(_fetch_op());
}
}
void z80::reset() {
AF = BC = DE = HL = PC = SP = 0;
AF_ = BC_ = DE_ = HL_ = IX = IY = 0;
I = R = 0;
_im = 0;
_iff1 = _iff2 = false;
_irq_pending = 0;
_iff1 = _iff2 = _int_nmi = _int_irq = _int_prot = false;
_irq_data = 0;
_ts = 0;
_halted = false;
}
void z80::_handle_nmi() {
DBG_CPU(println("NMI"));
_iff2 = _iff1;
_iff1 = false;
R++;
PC = 0x0066;
ts(11);
}
void z80::_handle_interrupt() {
if (_irq_pending < 0 || _iff1) {
if (_halted) {
_halted = false;
PC++;
}
_push(PC);
if (_irq_pending < 0) { // NMI
_iff2 = _iff1;
PC = 0x0066;
ts(11);
} else {
_iff1 = _iff2 = false;
R++;
if (_im == 0 || _im == 1)
PC = 0x0038;
else if (_im == 2)
PC = _rw(_irq_pending + (0x100 * I));
ts(7);
}
if (_halted) {
_halted = false;
PC++;
}
_irq_pending = 0;
_push(PC);
_iff1 = _iff2 = false;
R++;
if (_im == 0)
switch (_irq_data) {
case 0xc7: PC = 0x00; break;
case 0xcf: PC = 0x08; break;
case 0xd7: PC = 0x10; break;
case 0xdf: PC = 0x18; break;
case 0xe7: PC = 0x20; break;
case 0xef: PC = 0x28; break;
case 0xf7: PC = 0x30; break;
case 0xff: PC = 0x38; break;
}
else if (_im == 1)
PC = 0x0038;
else if (_im == 2)
PC = _rw(_irq_data + (0x100 * I));
ts(7);
DBG_CPU(printf("IM: %d PC: %04x\r\n", _im, PC));
}
void z80::daa() {
@ -154,284 +201,141 @@ void z80::_step_idx(EXT_OP f) {
}
void z80::_ddfd(uint16_t &ix, uint8_t &ixL, uint8_t &ixH, EXT_OP op) {
switch (_fetch_op()) {
case 0x09:
_add16(ix, BC);
break;
case 0x19:
_add16(ix, DE);
break;
case 0x21:
ix = _rwpc();
break;
case 0x22:
_swPC(ix);
break;
case 0x23:
ix++;
_mc(IR, 1); _mc(IR, 1);
break;
case 0x24:
_inc(ixH);
break;
case 0x25:
_dec(ixH);
break;
case 0x26:
ixH = _rb(PC++);
break;
case 0x29:
_add16(ix, ix);
break;
case 0x2a:
ix = _rwPC();
break;
case 0x2b:
ix--;
_mc(IR, 1); _mc(IR, 1);
break;
case 0x2c:
_inc(ixL);
break;
case 0x2d:
_dec(ixL);
break;
case 0x2e:
ixL = _rb(PC++);
break;
case 0x34:
_incO(ix);
break;
case 0x35:
_decO(ix);
break;
case 0x36:
_sbO(ix);
break;
case 0x39:
_add16(ix, SP);
break;
case 0x44:
B = ixH;
break;
case 0x45:
B = ixL;
break;
case 0x46:
B = _rbO(ix);
break;
case 0x4c:
C = ixH;
break;
case 0x4d:
C = ixL;
break;
case 0x4e:
C = _rbO(ix);
break;
case 0x54:
D = ixH;
break;
case 0x55:
D = ixL;
break;
case 0x56:
D = _rbO(ix);
break;
case 0x5c:
E = ixH;
break;
case 0x5d:
E = ixL;
break;
case 0x5e:
E = _rbO(ix);
break;
case 0x60:
ixH = B;
break;
case 0x61:
ixH = C;
break;
case 0x62:
ixH = D;
break;
case 0x63:
ixH = E;
break;
case 0x64:
break;
case 0x65:
ixH = ixL;
break;
case 0x66:
H = _rbO(ix);
break;
case 0x67:
ixH = A;
break;
case 0x68:
ixL = B;
break;
case 0x69:
ixL = C;
break;
case 0x6a:
ixL = D;
break;
case 0x6b:
ixL = E;
break;
case 0x6c:
ixL = ixH;
break;
case 0x6d:
break;
case 0x6e:
L = _rbO(ix);
break;
case 0x6f:
ixL = A;
break;
case 0x70:
_sbO(ix, B);
break;
case 0x71:
_sbO(ix, C);
break;
case 0x72:
_sbO(ix, D);
break;
case 0x73:
_sbO(ix, E);
break;
case 0x74:
_sbO(ix, H);
break;
case 0x75:
_sbO(ix, L);
break;
case 0x77:
_sbO(ix, A);
break;
case 0x7c:
A = ixH;
break;
case 0x7d:
A = ixL;
break;
case 0x7e:
A = _rbO(ix);
break;
case 0x84:
_add(ixH);
break;
case 0x85:
_add(ixL);
break;
case 0x86:
_add(_rbO(ix));
break;
case 0x8c:
_adc(ixH);
break;
case 0x8d:
_adc(ixL);
break;
case 0x8e:
_adc(_rbO(ix));
break;
case 0x94:
_sub(ixH);
break;
case 0x95:
_sub(ixL);
break;
case 0x96:
_sub(_rbO(ix));
break;
case 0x9c:
_sbc(ixH);
break;
case 0x9d:
_sbc(ixL);
break;
case 0x9e:
_sbc(_rbO(ix));
break;
case 0xa4:
_and(ixH);
break;
case 0xa5:
_and(ixL);
break;
case 0xa6:
_and(_rbO(ix));
break;
case 0xac:
_xor(ixH);
break;
case 0xad:
_xor(ixL);
break;
case 0xae:
_xor(_rbO(ix));
break;
case 0xb4:
_or(ixH);
break;
case 0xb5:
_or(ixL);
break;
case 0xb6:
_or(_rbO(ix));
break;
case 0xbc:
_cmp(ixH);
break;
case 0xbd:
_cmp(ixL);
break;
case 0xbe:
_cmp(_rbO(ix));
break;
case 0xcb:
_step_idx(op);
break;
case 0xe1:
ix = _pop();
break;
case 0xe3:
_exSP(ix);
break;
case 0xe5:
_mc(IR, 1);
_push(ix);
break;
case 0xe9:
PC = ix;
break;
case 0xf9:
_mc(IR, 1); _mc(IR, 1);
SP = ix;
break;
uint8_t o = _fetch_op();
switch (o) {
E(0x09, _add16(ix, BC));
E(0x19, _add16(ix, DE));
E(0x21, ix = _rwpc());
E(0x22, _swPC(ix));
E(0x23, ix++; _mc(IR, 1); _mc(IR, 1));
U(0x24, _inc(ixH));
U(0x25, _dec(ixH));
U(0x26, ixH = _rb(PC++));
E(0x29, _add16(ix, ix));
E(0x2a, ix = _rwPC());
E(0x2b, ix--; _mc(IR, 1); _mc(IR, 1));
U(0x2c, _inc(ixL));
U(0x2d, _dec(ixL));
U(0x2e, ixL = _rb(PC++));
E(0x34, _incO(ix));
E(0x35, _decO(ix));
E(0x36, _sbO(ix));
E(0x39, _add16(ix, SP));
U(0x44, B = ixH);
U(0x45, B = ixL);
E(0x46, B = _rbO(ix));
U(0x4c, C = ixH);
U(0x4d, C = ixL);
E(0x4e, C = _rbO(ix));
U(0x54, D = ixH);
U(0x55, D = ixL);
E(0x56, D = _rbO(ix));
U(0x5c, E = ixH);
U(0x5d, E = ixL);
E(0x5e, E = _rbO(ix));
U(0x60, ixH = B);
U(0x61, ixH = C);
U(0x62, ixH = D);
U(0x63, ixH = E);
U(0x64, /* FIXME */);
U(0x65, ixH = ixL);
E(0x66, H = _rbO(ix));
U(0x67, ixH = A);
U(0x68, ixL = B);
U(0x69, ixL = C);
U(0x6a, ixL = D);
U(0x6b, ixL = E);
U(0x6c, ixL = ixH);
U(0x6d, /* FIXME */);
E(0x6e, L = _rbO(ix));
U(0x6f, ixL = A);
E(0x70, _sbO(ix, B));
E(0x71, _sbO(ix, C));
E(0x72, _sbO(ix, D));
E(0x73, _sbO(ix, E));
E(0x74, _sbO(ix, H));
E(0x75, _sbO(ix, L));
E(0x77, _sbO(ix, A));
U(0x7c, A = ixH);
U(0x7d, A = ixL);
E(0x7e, A = _rbO(ix));
U(0x84, _add(ixH));
U(0x85, _add(ixL));
E(0x86, _add(_rbO(ix)));
U(0x8c, _adc(ixH));
U(0x8d, _adc(ixL));
E(0x8e, _adc(_rbO(ix)));
U(0x94, _sub(ixH));
U(0x95, _sub(ixL));
E(0x96, _sub(_rbO(ix)));
U(0x9c, _sbc(ixH));
U(0x9d, _sbc(ixL));
E(0x9e, _sbc(_rbO(ix)));
U(0xa4, _and(ixH));
U(0xa5, _and(ixL));
E(0xa6, _and(_rbO(ix)));
U(0xac, _xor(ixH));
U(0xad, _xor(ixL));
E(0xae, _xor(_rbO(ix)));
U(0xb4, _or(ixH));
U(0xb5, _or(ixL));
E(0xb6, _or(_rbO(ix)));
U(0xbc, _cmp(ixH));
U(0xbd, _cmp(ixL));
E(0xbe, _cmp(_rbO(ix)));
E(0xcb, _step_idx(op));
E(0xe1, ix = _pop());
E(0xe3, _exSP(ix));
E(0xe5, _mc(IR, 1); _push(ix));
E(0xe9, PC = ix);
E(0xf9, _mc(IR, 1); _mc(IR, 1); SP = ix);
default:
ERR(printf("unimplemented dd/fd op: %02x\r\n", o));
}
}
void z80::ed() {
switch (_fetch_op()) {
uint8_t op = _fetch_op();
switch (op) {
O(0x40, inB);
O(0x41, outB);
O(0x42, sbcBC);
O(0x43, ldPCbc);
C(0x44);
C(0x54);
C(0x64);
C(0x74);
C(0x4c);
C(0x54);
C(0x5c);
C(0x64);
C(0x6c);
C(0x74);
O(0x7c, neg);
C(0x45);
@ -507,6 +411,9 @@ void z80::ed() {
O(0xb9, cpdr);
O(0xba, indr);
O(0xbb, outdr);
default:
ERR(printf("unimplemented ed op: %02x\r\n", op));
}
}

124
src/z80.h

@ -1,22 +1,32 @@
#ifndef __Z80_H__
#define __Z80_H__
#pragma once
#undef sbi
#undef inch
#undef SP
class Stream;
class z80: public CPU {
public:
z80(Memory &m, PortDevice &ports): CPU(m), _ports(ports) {}
z80(Memory &m): CPU(m) {}
void run(unsigned);
void reset();
void raise(uint8_t level) { _irq_pending = level; }
char *status(char *buf, size_t n, bool hdr=false);
void nmi() { _int_nmi = true; }
void irq(uint8_t b) { _int_irq = true; _irq_data = b; }
char *status(char *buf, size_t n, bool hdr = false);
void checkpoint(Stream &);
void restore(Stream &);
void set_port_out_handler(std::function<void(uint16_t, uint8_t)> fn) {
port_out_handler = fn;
}
void set_port_in_handler(std::function<uint8_t(uint16_t)> fn) {
port_in_handler = fn;
}
inline uint8_t a() { return A; }
inline uint8_t b() { return B; }
inline uint8_t c() { return C; }
@ -69,6 +79,7 @@ public:
inline void reset_ts() { _ts = 0; }
private:
void _handle_nmi();
void _handle_interrupt();
void op(uint8_t);
@ -138,6 +149,9 @@ private:
uint8_t _im;
bool _iff1, _iff2;
bool _int_nmi, _int_irq, _int_prot;
uint8_t _irq_data;
union {
struct { uint8_t MPL, MPH; };
uint16_t _memptr;
@ -145,8 +159,16 @@ private:
unsigned long _ts;
uint8_t _irq_pending;
PortDevice &_ports;
std::function<void(uint16_t, uint8_t)> port_out_handler;
std::function<uint8_t(uint16_t)> port_in_handler;
inline void _out(uint16_t p, uint8_t v) {
if (port_out_handler) port_out_handler(p, v);
}
inline uint8_t _in(uint16_t p) {
return port_in_handler? port_in_handler(p): 0;
}
uint8_t parity(uint8_t);
@ -403,7 +425,12 @@ private:
inline void incb() { _inc(B); }
inline void decb() { _dec(B); }
inline void ldb() { B = _rb(PC++); }
inline void rlca() { flags.C = ((A & 0x80) >> 7); A = (A << 1) | flags.C; }
inline void rlca() {
flags.H = flags.N = 0;
flags.C = ((A & 0x80) >> 7);
A = (A << 1) | flags.C;
_35(A);
}
// 0x08
inline void exaf() { _exch(AF, AF_); }
@ -431,7 +458,9 @@ private:
inline void rla() {
uint8_t b = (A << 1) | flags.C;
flags.C = (A & 0x80) >> 7;
flags.H = flags.N = 0;
A = b;
_35(A);
}
// 0x18
@ -449,7 +478,9 @@ private:
inline void rra() {
uint8_t b = (A >> 1) | (flags.C << 7);
flags.C = (A & 1);
flags.H = flags.N = 0;
A = b;
_35(A);
}
// 0x20
@ -490,7 +521,17 @@ private:
inline void inca() { _inc(A); }
inline void deca() { _dec(A); }
inline void lda() { A = _rb(PC++); }
inline void ccf() { flags.H = flags.C; flags.C = flags.N = 0; _35(A); }
inline void ccf() {
if (flags.C) {
flags.C = 0;
flags.H = 1;
} else {
flags.H = 0;
flags.C = 1;
}
flags.N = 0;
_35(A);
}
// 0x40
inline void ldbb() {}
@ -702,7 +743,7 @@ private:
uint8_t b = _rb(PC++);
uint16_t p = b + (A << 8);
MPH = A; MPL = b+1;
_ports.out(p, A);
_out(p, A);
}
inline void callnc() { _call(!flags.C); }
inline void pushde() { _mc(IR, 1); _push(DE); }
@ -716,7 +757,7 @@ private:
inline void ina() {
uint8_t b = _rb(PC++);
uint16_t p = b + (A << 8);
A = _ports.in(p);
A = _in(p);
MPH = A; MPL = b+1;
}
inline void callc() { _call(flags.C); }
@ -737,14 +778,14 @@ private:
// 0xe8
inline uint8_t _inr() {
_memptr = BC+1;
uint8_t b = _ports.in(BC);
uint8_t b = _in(BC);
_szp35(b);
flags.N = flags.H = 0;
return b;
}
inline void _outr(uint8_t b) {
_memptr = BC+1;
_ports.out(BC, b);
_out(BC, b);
}
inline void retpe() { _ret(flags.P); }
@ -770,7 +811,7 @@ private:
inline void retm() { _ret(flags.S); }
inline void ldsphl() { _mc(IR, 1); _mc(IR, 1); SP = HL; }
inline void jpm() { _jmp(flags.S); }
inline void ei() { _iff1 = _iff2 = true; }
inline void ei() { _iff1 = _iff2 = _int_prot = true; }
inline void callm() { _call(flags.S); }
inline void fd() { _ddfd(IY, IYL, IYH, &z80::fdcb); }
inline void cp() { _cmp(_rb(PC++)); }
@ -1271,6 +1312,7 @@ private:
}
inline void cpi() {
uint8_t b = _rb(HL);
flags.H = ((b & 0x0f) > (A & 0x0f));
_mc(HL, 1); _mc(HL, 1); _mc(HL, 1);
_mc(HL, 1); _mc(HL, 1);
uint8_t c = A;
@ -1282,8 +1324,9 @@ private:
A = c;
if (flags.H) b--;
flags.C = f;
flags.N = 1;
flags.P = (BC != 0);
_35(b);
_sz35(b);
flags._5 = ((b & 0x02) != 0);
_memptr++;
}
@ -1327,17 +1370,23 @@ private:
}
inline void cpd_() {
uint8_t b = _rb(HL);
uint8_t c = A - b - flags.H;
flags.H = ((b & 0x0f) > (A & 0x0f));
_mc(HL, 1); _mc(HL, 1); _mc(HL, 1);
_mc(HL, 1); _mc(HL, 1);
uint8_t c = A;
uint8_t f = (flags.C != 0);
_sub(b);
HL--;
BC--;
b = A;
A = c;
if (flags.H) b--;
flags.C = f;
flags.N = 1;
flags.P = (BC != 0);
_sz35(c);
flags._5 = ((c & 0x02) != 0);
_sz35(b);
flags._5 = ((b & 0x02) != 0);
_memptr--;
// FIXME: flag H
}
inline void ind() {
_mc(IR, 1);
@ -1373,8 +1422,8 @@ private:
_mc(DE, 1);
b += A;
flags.P = (BC != 0);
_35(b);
flags._5 = ((b & 0x02) != 0);
flags._3 = (b & 0x08) != 0;
flags._5 = (b & 0x02) != 0;
flags.N = flags.H = 0;
if (BC) {
_mc(DE, 1); _mc(DE, 1); _mc(DE, 1);
@ -1398,10 +1447,10 @@ private:
flags.C = f;
flags.P = (BC != 0);
if (flags.H) b--;
_35(b);
flags._3 = ((b & 0x08) != 0);
flags._5 = ((b & 0x02) != 0);
_memptr++;
if (!flags.Z) {
if (!flags.Z && flags.P) {
_mc(HL, 1); _mc(HL, 1); _mc(HL, 1);
_mc(HL, 1); _mc(HL, 1);
PC -= 2;
@ -1414,7 +1463,7 @@ private:
uint8_t b = _inr();
_sb(HL, b);
B--;
uint8_t c = b + flags.C + 1;
uint8_t c = b + C + 1;
flags.N = (c & 0x80) != 0;
flags.C = flags.H = (c < b);
flags.P = parity((c & 0x07) ^ B);
@ -1465,17 +1514,22 @@ private:
}
inline void cpdr() {
uint8_t b = _rb(HL);
uint8_t c = A - b;
_mc(HL, 1); _mc(HL, 1); _mc(HL, 1);
_mc(HL, 1); _mc(HL, 1);
uint8_t c = A;
uint8_t f = (flags.C != 0);
_sub(b);
BC--;
flags.N = 1;
b -= A;
A = c;
flags.C = f;
flags.P = (BC != 0);
_sz35(c);
flags._5 = ((c & 0x02) != 0);
// FIXME: flag H
flags.N = 1;
if (flags.H) b--;
flags._3 = (b & 0x08) != 0;
flags._5 = (b & 0x02) != 0;
_memptr--;
if (BC) {
if (!flags.Z && flags.P) {
_mc(HL, 1); _mc(HL, 1); _mc(HL, 1);
_mc(HL, 1); _mc(HL, 1);
PC -= 2;
@ -1486,11 +1540,11 @@ private:
inline void indr() {
_mc(IR, 1);
uint8_t b = _inr();
_memptr = BC-1;
_sb(HL, b);
_memptr = BC-1;
B--;
uint8_t c = b + flags.C + 1;
flags.N = (c & 0x80) != 0;
uint8_t c = b + C - 1;
flags.N = (b & 0x80) != 0;
flags.C = flags.H = (c < b);
flags.P = parity((c & 0x07) ^ B);
_sz35(B);
@ -1519,8 +1573,8 @@ private:
PC -= 2;
}
}
// 0xDDCB extended instructions
// 0xDDCB extended instructions
inline uint16_t _rbIX(uint8_t &b, uint8_t o) {
uint16_t a = _ads(IX, o);
_memptr = a;
@ -2160,5 +2214,3 @@ private:
inline void set7IY(uint8_t o) { uint8_t b; _setIY(b, o, 0x80); }
inline void set7IYA(uint8_t o) { _setIY(A, o, 0x80); }
};
#endif

19
src/z80test/Makefile Normal file

@ -0,0 +1,19 @@
CPPFLAGS=-I.. -DDEBUGGING=0x41 -DUNDOCUMENTED_OPS -DNO_CHECKPOINT -D'PRINTER(x)=x'
CXXFLAGS=-g -fno-operator-names -Wall
all: z80test tests.me sum
z80test: z80test.o z80.o memory.o
g++ -o $@ $+ $(LDLIBS)
%.o: ../%.cpp ../%.h
g++ $(CXXFLAGS) $(CPPFLAGS) -o $@ -c $<
tests.me: z80test
./z80test tests.in > $@
sum: tests.me
md5sum $<
clean:
rm -f z80test *.o tests.me

27
src/z80test/README.md Normal file

@ -0,0 +1,27 @@
z80test
-------
A test harness for a z80 processor emulator; based on, and using tests from,
the excellent [FUSE 1.5.7](http://fuse-emulator.sourceforge.net/).
```bash
$ make
g++ -g -fno-operator-names -I.. -DDEBUGGING=0x41 -DUNDOCUMENTED_OPS -DNO_CHECKPOINT -D'PRINTER(x)=x' -c -o z80test.o z80test.cc
g++ -g -fno-operator-names -I.. -DDEBUGGING=0x41 -DUNDOCUMENTED_OPS -DNO_CHECKPOINT -D'PRINTER(x)=x' -o z80.o -c ../z80.cpp
g++ -g -fno-operator-names -I.. -DDEBUGGING=0x41 -DUNDOCUMENTED_OPS -DNO_CHECKPOINT -D'PRINTER(x)=x' -o memory.o -c ../memory.cpp
g++ -o z80test z80test.o z80.o memory.o
./z80test tests.in > tests.me
md5sum tests.me
75f83a24154998522cf1f6a60c0042ef tests.me
$ vi -d tests.expected tests.me
```
Flag Errors
===========
|test |exp |act |op |error |
|-------|-------|-------|---------------|-------|
|37_1 |ed |c5 |scf | E:5,3 |
|3f |58 |50 |ccf | E:3 |
|edb1_2 |87 |8f |cpir | E:3 |
|edb9_2 |a7 |8f |cpdr | E:5,3 |

18913
src/z80test/tests.expected Normal file

File diff suppressed because it is too large Load Diff

9153
src/z80test/tests.in Normal file

File diff suppressed because it is too large Load Diff

186
src/z80test/z80test.cc Normal file

@ -0,0 +1,186 @@
#include <stdio.h>
#include <stdint.h>
#include <setjmp.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <functional>
class Stream;
#include "memory.h"
#include "CPU.h"
#include "debugging.h"
#include "z80.h"
#include "ram.h"
class Ports {
public:
Ports(Memory &mem, z80 &cpu): _mem(mem), _cpu(cpu) {}
// not sure what this is all about, spectrum-specific stuff?
void pre(uint16_t p) const {
if ((p & 0xc000) == 0x4000)
printf("%5ld PC %04x\n", _cpu.ts(), p);
_cpu.ts(1);
}
void post(uint16_t p) const {
if (p & 0x0001) {
if ((p & 0xc000) == 0x4000) {
printf("%5ld PC %04x\n", _cpu.ts(), p); _cpu.ts(1);
printf("%5ld PC %04x\n", _cpu.ts(), p); _cpu.ts(1);
printf("%5ld PC %04x\n", _cpu.ts(), p); _cpu.ts(1);
} else
_cpu.ts(3);
} else {
printf("%5ld PC %04x\n", _cpu.ts(), p);
_cpu.ts(3);
}
}
void out(uint16_t port, uint8_t a) const {
pre(port);
printf("%5ld PW %04x %02x\n", _cpu.ts(), port, a);
post(port);
}
uint8_t in(uint16_t port) const {
uint8_t r = port >> 8;
pre(port);
printf("%5ld PR %04x %02x\n", _cpu.ts(), port, r);
post(port);
return r;
}
private:
Memory &_mem;
z80 &_cpu;
};
int read_test(FILE *f, z80 &z, Memory &m) {
unsigned af, bc, de, hl, af_, bc_, de_, hl_, ix, iy, sp, pc, memptr;
unsigned i, r, iff1, iff2, im;
unsigned halted, end_tstates2, address;
char test_name[80];
do {
if (!fgets(test_name, sizeof(test_name), f)) {
if (!feof(f))
fprintf(stderr, "reading test description: %s\n", strerror(errno));
return -1;
}
} while (test_name[0] == '\n');
printf("%s", test_name);
if (fscanf(f, "%x %x %x %x %x %x %x %x %x %x %x %x %x",
&af, &bc, &de, &hl, &af_, &bc_, &de_, &hl_, &ix, &iy,
&sp, &pc, &memptr) != 13)
{
fprintf(stderr, "first registers' line corrupt\n");
return -1;
}
z.af(af); z.bc(bc); z.de(de); z.hl(hl);
z.af_(af_); z.bc_(bc_); z.de_(de_); z.hl_(hl_);
z.ix(ix); z.iy(iy); z.sp(sp); z.pc(pc);
z.memptr(memptr);
if (fscanf(f, "%x %x %u %u %u %d %d", &i, &r, &iff1, &iff2, &im,
&halted, &end_tstates2 ) != 7)
{
fprintf(stderr, "second registers' line corrupt\n");
return -1;
}
z.i(i); z.r(r);
z.iff1(iff1); z.iff2(iff2);
z.im(im);
for (;;) {
if (fscanf(f, "%x", &address) != 1) {
fprintf(stderr, "no address found\n");
return -1;
}
if (address >= 0x10000) break;
for (;;) {
unsigned byte;
if (fscanf(f, "%x", &byte) != 1) {
fprintf(stderr, "no data byte found\n");
return -1;
}
if (byte >= 0x100) break;
m[address++] = byte;
}
}
return end_tstates2;
}
void dump_cpu_state(z80 &z) {
printf("%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x\n",
z.af(), z.bc(), z.de(), z.hl(), z.af_(), z.bc_(), z.de_(),
z.hl_(), z.ix(), z.iy(), z.sp(), z.pc(), z.memptr());
printf("%02x %02x %d %d %d %d %ld\n",
z.i(), z.r(), z.iff1(), z.iff2(), z.im(), z.halted(), z.ts());
}
void dump_memory_state(uint8_t b[], Memory &m) {
for (unsigned i = 0; i < 0x10000; i++) {
if (m[i] == b[i])
continue;
printf("%04x ", i);
for (; i < 0x10000 && m[i] != b[i]; i++)
printf("%02x ", (uint8_t)m[i]);
printf("-1\n");
}
printf("\n");
}
int main(int argc, char *argv[]) {
Memory memory;
ram<65536> ram;
memory.put(ram, 0x0000);
z80 cpu(memory);
Ports ports(memory, cpu);
cpu.set_port_out_handler([ports](uint16_t p, uint8_t v) { ports.out(p, v); });
cpu.set_port_in_handler([ports](uint16_t p) { return ports.in(p); });
cpu.reset();
FILE *fp = fopen(argv[1], "r");
if (!fp) {
perror("fopen");
return -1;
}
while (true) {
uint8_t backup[0x10000];
for (unsigned i = 0; i < 0x10000; ) {
memory[i++] = 0xde; memory[i++] = 0xad;
memory[i++] = 0xbe; memory[i++] = 0xef;
}
int end_ts = read_test(fp, cpu, memory);
if (0 > end_ts)
break;
for (unsigned i = 0; i < 0x10000; i++)
backup[i] = memory[i];
while (cpu.ts() < (uint32_t)end_ts)
cpu.run(1);
dump_cpu_state(cpu);
dump_memory_state(backup, memory);
cpu.reset();
}
fclose(fp);
}