diff --git a/.gitignore b/.gitignore index cb0b961..76f9d8a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.bak *.lst *.tmp +.idea/ diff --git a/.idea/z80em.iml b/.idea/z80em.iml deleted file mode 100644 index 5e764c4..0000000 --- a/.idea/z80em.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..1b049f0 --- /dev/null +++ b/config/config.go @@ -0,0 +1,76 @@ +package config + +import ( + "os" + + log "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" +) + +const defaultMonitorFile = "okean240/MON_r6.BIN" +const defaultCPMFile = "okean240/CPM_r7.BIN" + +type OkEmuConfig struct { + LogFile string `yaml:"logFile"` + LogLevel string `yaml:"logLevel"` + MonitorFile string `yaml:"monitorFile"` + CPMFile string `yaml:"cpmFile"` +} + +var config *OkEmuConfig + +//func usage() { +// fmt.Printf("Use: %s --config .yml\n", filepath.Base(os.Args[0])) +// os.Exit(2) +//} + +func GetConfig() *OkEmuConfig { + return config +} + +func LoadConfig() { + //args := os.Args + //if len(args) != 3 { + // usage() + //} + // + //if args[1] != "--config" { + // usage() + //} + // confFile := args[2] + confFile := "okemu.yml" + + conf := OkEmuConfig{} + data, err := os.ReadFile(confFile) + if err == nil { + err := yaml.Unmarshal(data, &conf) + if err != nil { + log.Panicf("Config file error: %v", err) + } + setDefaultConf(&conf) + checkConfig(&conf) + } else { + log.Panicf("Can not open config file: %v", err) + } + + config = &conf +} + +func checkConfig(conf *OkEmuConfig) { + +} + +func setDefaultConf(conf *OkEmuConfig) { + if conf.LogLevel == "" { + conf.LogLevel = "info" + } + if conf.LogFile == "" { + conf.LogFile = "okemu.log" + } + if conf.MonitorFile == "" { + conf.MonitorFile = defaultMonitorFile + } + if conf.CPMFile == "" { + conf.CPMFile = defaultCPMFile + } +} diff --git a/go.mod b/go.mod index 332cb7d..1ec5960 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,10 @@ module z80em go 1.25 + +require ( + github.com/sirupsen/logrus v1.9.4 + gopkg.in/yaml.v3 v3.0.1 +) + +require golang.org/x/sys v0.13.0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..63fc67a --- /dev/null +++ b/go.sum @@ -0,0 +1,14 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/logger/logger.go b/logger/logger.go new file mode 100644 index 0000000..f3fd979 --- /dev/null +++ b/logger/logger.go @@ -0,0 +1,85 @@ +package logger + +import ( + "bufio" + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + "syscall" + + log "github.com/sirupsen/logrus" +) + +var logFile *os.File = nil +var logBuffer *bufio.Writer = nil + +type LogFormatter struct { +} + +func (f *LogFormatter) Format(entry *log.Entry) ([]byte, error) { + caller := "-" + if entry.HasCaller() { + caller = filepath.Base(entry.Caller.File) + ":" + strconv.Itoa(entry.Caller.Line) + } + lvl := strings.ToUpper(entry.Level.String()) + if len(lvl) > 0 { + lvl = lvl[0:1] + } else { + lvl = "-" + } + logMessage := fmt.Sprintf("%s[%s][%s]: %s\n", entry.Time.Format("2006-01-02T15:04:05"), lvl, caller, entry.Message) //time.RFC3339 + + return []byte(logMessage), nil +} + +// InitLogging Initialise logging format and level +func InitLogging() { + log.SetFormatter(new(LogFormatter)) + log.SetLevel(log.DebugLevel) + log.SetReportCaller(true) + log.SetOutput(os.Stdout) +} + +// FlushLogs Flush logs if logging to file +func FlushLogs() { + if logBuffer != nil { + err := logBuffer.Flush() + if err != nil { + return + } + } +} + +// ReconfigureLogging Reconfigure logging by config values +func ReconfigureLogging(config *config.ServiceConfig) { + // redirect logging if log file specified + if len(config.LogFile) > 0 { + var err error + logFile, err = os.OpenFile(config.LogFile, os.O_WRONLY|syscall.O_CREAT|syscall.O_APPEND, 0664) + if err != nil { + log.Fatalf("Can not access to log file: %s\n%v", config.LogFile, err) + } + logBuffer = bufio.NewWriter(logFile) + log.SetOutput(logBuffer) + } + if len(config.LogLevel) > 0 { + level, err := log.ParseLevel(config.LogLevel) + if err != nil { + log.Fatalf("Can not parse log level: %s\n%v", config.LogLevel, err) + } + log.SetLevel(level) + } +} + +// CloseLogs If log output is file, flush and close it +func CloseLogs() { + FlushLogs() + if logFile != nil { + err := logFile.Close() + if err != nil { + log.Warnf("Can not close log file: %s\n%v", logFile.Name(), err) + } + } +} diff --git a/main.go b/main.go index df6b8ab..c9271b4 100644 --- a/main.go +++ b/main.go @@ -1,21 +1,29 @@ package main import ( - "fmt" + "z80em/config" + "z80em/logger" + "z80em/okean240" ) -//TIP

To run your code, right-click the code and select Run.

Alternatively, click -// the icon in the gutter and select the Run menu item from here.

+var Version = "v1.0.0" +var BuildTime = "2026-03-01" func main() { - //TIP

Press when your caret is at the underlined text - // to see how GoLand suggests fixing the warning.

Alternatively, if available, click the lightbulb to view possible fixes.

- s := "gopher" - fmt.Printf("Hello and welcome, %s!\n", s) - for i := 1; i <= 5; i++ { - //TIP

To start your debugging session, right-click your code in the editor and select the Debug option.

We have set one breakpoint - // for you, but you can always add more by pressing .

- fmt.Println("i =", 100/i) - } -} \ No newline at end of file + // base log init + logger.InitLogging() + + // load config yml file + config.LoadConfig() + + conf := config.GetConfig() + + // Reconfigure logging by config values + logger.ReconfigureLogging(conf) + + println("Init computer") + computer := okean240.New(config) + println("Run computer") + computer.Run() +} diff --git a/okean240/CPM_r7.BIN b/okean240/CPM_r7.BIN new file mode 100644 index 0000000..ba16f63 Binary files /dev/null and b/okean240/CPM_r7.BIN differ diff --git a/okean240/MON_r6.BIN b/okean240/MON_r6.BIN new file mode 100644 index 0000000..c6b7a69 Binary files /dev/null and b/okean240/MON_r6.BIN differ diff --git a/okean240/computer.go b/okean240/computer.go new file mode 100644 index 0000000..6be0e2e --- /dev/null +++ b/okean240/computer.go @@ -0,0 +1,75 @@ +package okean240 + +import ( + "z80em/config" + "z80em/z80em" + + log "github.com/sirupsen/logrus" +) + +type ComputerType struct { + cpu z80em.Z80Type + memory Memory + ioPorts [256]byte + cycles uint64 + dd17EnableOut bool +} + +type ComputerInterface interface { + //Init(rom0 string, rom1 string) + Run() +} + +func (c *ComputerType) M1MemRead(addr uint16) byte { + return c.memory.M1MemRead(addr) +} + +func (c *ComputerType) MemRead(addr uint16) byte { + return c.memory.MemRead(addr) +} + +func (c *ComputerType) MemWrite(addr uint16, val byte) { + c.memory.MemWrite(addr, val) +} + +func (c *ComputerType) IORead(port uint16) byte { + return c.ioPorts[port] +} + +func (c *ComputerType) IOWrite(port uint16, val byte) { + c.ioPorts[byte(port&0x00ff)] = val + switch byte(port & 0x00ff) { + case SYS_DD17PB: + + if c.dd17EnableOut { + c.memory.Configure(val) + } + case SYS_DD17CTR: + c.dd17EnableOut = val == 0x80 + case KBD_DD78CTR: + + } +} + +// New Builds new computer +func New(cfg config.OkEmuConfig) *ComputerType { + c := ComputerType{} + c.memory = Memory{} + c.memory.Init(cfg.MonitorFile, cfg.CPMFile) + + c.cpu = *z80em.New(&c) + + c.cycles = 0 + c.dd17EnableOut = false + return &c +} + +func (c *ComputerType) Run() { + c.cpu.Reset() + for { + state := c.cpu.GetState() + log.Infof("%d - [%x]: %x\n", c.cycles, state.PC, c.MemRead(state.PC)) + c.cycles += uint64(c.cpu.RunInstruction()) + + } +} diff --git a/okean240/ioports.go b/okean240/ioports.go new file mode 100644 index 0000000..6882b7a --- /dev/null +++ b/okean240/ioports.go @@ -0,0 +1,154 @@ +package okean240 + +/* + * Ocean-240.2 + * Computer with FDC variant. + * IO Ports definitions + * + * By Romych 2026-03-01 + */ + +/* + * КР580ВВ55 DD79 USER PORT + */ + +// USR_DD79PA User port A +const USR_DD79PA = 0x00 + +// USR_DD79PB User port B +const USR_DD79PB = 0x01 + +// USR_DD79PC User port C +const USR_DD79PC = 0x02 + +// USR_DD79CTR Config +const USR_DD79CTR = 0x03 // Config: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +// Set bit: [0][xxx][bbb][0|1] + +/* + * КР1818ВГ93 FDC Controller + */ + +// FDC_CMD FDC Command +const FDC_CMD = 0x20 + +// FDC_TRACK FDC Track No +const FDC_TRACK = 0x21 + +// FDC_SECT FDC Sector +const FDC_SECT = 0x22 + +// FDC_DATA FDC Data +const FDC_DATA = 0x23 + +// FDC_WAIT FDC Wait +const FDC_WAIT = 0x24 + +/* + * Floppy Controller port + */ + +// FLOPPY Floppy Controller port +const FLOPPY = 0x25 // WR: 5-SSEN, 4-#DDEN, 3-INIT, 2-DRSEL, 1-MOT1, 0-MOT0 +// RD: 7-MOTST, 6-SSEL, 5,4-x , 3-DRSEL, 2-MOT1, 1-MOT0, 0-INT + +/* + * КР580ВВ55 DD78 Keyboard + */ + +// KBD_DD78PA Port A - Keyboard Data +const KBD_DD78PA = 0x40 + +// KBD_DD78PB Port B - JST3,SHFT,CTRL,ACK,TAPE5,TAPE4,GK,GC +const KBD_DD78PB = 0x41 + +// KBD_DD78PC Port C - [PC7:5],[KBD_ACK],[PC3:0] +const KBD_DD78PC = 0x42 + +// KBD_DD78CTR Control port +const KBD_DD78CTR = 0x43 // Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] +// Set bit: [0][xxx][bbb][0|1] + +/* + * КР580ВИ53 DD70 + */ + +// TMR_DD70C1 Timer counter 1 +const TMR_DD70C1 = 0x60 + +// TMR_DD70C2 Timer counter 2 +const TMR_DD70C2 = 0x61 + +// TMR_DD70C3 Timer counter 3 +const TMR_DD70C3 = 0x62 + +/* + TMR_DD70CTR + Timer config: [sc1,sc0][rl1,rl0][m2,m1,m0][bcd] + sc - timer, rl=01-LSB, 10-MSB, 11-LSB+MSB + mode 000 - int on fin, + 001 - one shot, + x10 - rate gen, + x11-sq wave +*/ +const TMR_DD70CTR = 0x63 + +/* + * Programmable Interrupt controller PIC KR580VV59 + */ + +// PIC_DD75RS RS Port +const PIC_DD75RS = 0x80 + +// PIC_DD75RM RM Port +const PIC_DD75RM = 0x81 + +/* + * КР580ВВ51 DD72 + */ + +// UART_DD72RD Serial data +const UART_DD72RD = 0xA0 + +// UART_DD72RR Serial status [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY] +const UART_DD72RR = 0xA1 + +/* + * КР580ВВ55 DD17 System port + */ + +// Port A - VShift[8..1] Vertical shift +const SYS_DD17PA = 0xC0 + +// Port B - Memory mapper [ROM14,13][REST][ENROM-][A18,17,16][32k] +const SYS_DD17PB = 0xC1 + +// Port C - HShift[HS5..1,SB3..1] Horisontal shift +const SYS_DD17PC = 0xC2 + +/* + * SYS_DD17CTR + * Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] + * Set bit: [0][xxx][bbb][0|1] + */ +const SYS_DD17CTR = 0xC3 + +/* + * КР580ВВ55 DD67 + */ + +// LPT_DD67PA Port A - Printer Data +const LPT_DD67PA = 0xE0 + +// VID_DD67PB Port B - Video control [VSU,C/M,FL3:1,COL3:1] +const VID_DD67PB = 0xE1 + +// DD67PC Port C - [USER3:1, STB-LP, BELL, TAPE3:1] +const DD67PC = 0xE2 + +/* + * DD67CTR + * Сonfig: [1][ma1,ma0][0-aO|1-aI],[0-chO,1-chI],[mb],[0-bO|1-bI],[0-clO,1-clI] + * Set bit: [0][xxx][bbb][0|1] + */ +const DD67CTR = 0xE3 diff --git a/okean240/memory.go b/okean240/memory.go new file mode 100644 index 0000000..baa7fd7 --- /dev/null +++ b/okean240/memory.go @@ -0,0 +1,126 @@ +package okean240 + +import ( + "os" + + log "github.com/sirupsen/logrus" +) + +const RamBlockSize = 16 * 1024 // 16Kb +const RamSize = 512 * 1024 // 512kb (16xRU7) or 128k (16xRU5) +const RamBlocks = RamSize / RamBlockSize // 32 Ram blocks for 512k, 8 for 128k +const RamDefaultInitPattern = 0x3f +const RamWindows = 4 + +const RSTBit = 0x20 +const ROMDisBit = 0x10 +const AccessHiBit = 0x01 +const ExtRamAddrBits = 0x0E + +const ( + WindowNo0 = iota + WindowNo1 + WindowNo2 + WindowNo3 +) + +type RamBlock struct { + memory [RamBlockSize]byte +} + +type Memory struct { + allMemory [RamBlocks]RamBlock + memoryWindow [RamWindows]RamBlock + rom0 RamBlock // monitor + monitor + rom1 RamBlock // cpm + monitor + config byte +} + +type MemoryInterface interface { + // Init - Initialize memory at "computer start" + Init(rom0 string, rom1 string) + // Configure - Set memory configuration + Configure(value byte) + // M1MemRead Read byte from memoryWindow for specified address + M1MemRead(addr uint16) byte + // MemRead Read byte from memoryWindow for specified address + MemRead(addr uint16) byte + // MemWrite Write byte to memoryWindow to specified address + MemWrite(addr uint16, val byte) +} + +func (m *Memory) Init(monFile string, cmpFile string) { + + // empty RAM + for block := range m.allMemory { + rb := RamBlock{} + for addr := 0; addr < RamBlockSize; addr++ { + rb.memory[addr] = RamDefaultInitPattern + } + m.allMemory[block] = rb + } + + // Load ROM files and init ROM0,1 + // Read the entire file into a byte slice + rom0bin, err := os.ReadFile(monFile) + if err != nil { + log.Fatal(err) + } + rom1bin, err := os.ReadFile(cmpFile) + if err != nil { + log.Fatal(err) + } + m.rom0 = RamBlock{} + m.rom1 = RamBlock{} + half := RamBlockSize / 2 + for i := 0; i < half; i++ { + // mon+mon + m.rom0.memory[i] = rom0bin[i] + m.rom0.memory[i+half] = rom0bin[i] + // cp/m + mon + m.rom1.memory[i] = rom1bin[i] + m.rom1.memory[i+half] = rom0bin[i] + } + // Config mem with RST pin Hi + m.Configure(RSTBit) +} + +// Configure - Configure memoryWindow windows +func (m *Memory) Configure(value byte) { + m.config = value + if m.config&RSTBit != 0 { + // RST bit set just after System RESET + // All memoryWindow windows points to ROM0 (monitor) + for i := 0; i < RamWindows; i++ { + m.memoryWindow[i] = m.rom0 + } + } else { + // Map RAM blocks to windows + sp := (m.config & ExtRamAddrBits) << 1 // 0,4,8,12 + for i := byte(0); i < RamWindows; i++ { + m.memoryWindow[i] = m.allMemory[sp+i] + } + // Map two hi windows to low windows in 32k flag set + if m.config&AccessHiBit == 0 { + m.memoryWindow[WindowNo0] = m.memoryWindow[WindowNo2] + m.memoryWindow[WindowNo1] = m.memoryWindow[WindowNo3] + } + // If ROM enabled, map ROM to last window + if m.config&ROMDisBit == 0 { + // If ROM enabled, CP/M + Mon at window 3 [0xC000:0xFFFF] + m.memoryWindow[WindowNo3] = m.rom1 + } + } +} + +func (m *Memory) M1MemRead(addr uint16) byte { + return m.memoryWindow[addr>>14].memory[addr&0x3fff] +} + +func (m *Memory) MemRead(addr uint16) byte { + return m.memoryWindow[addr>>14].memory[addr&0x3fff] +} + +func (m *Memory) MemWrite(addr uint16, val byte) { + m.memoryWindow[addr>>14].memory[addr&0x3fff] = val +} diff --git a/okemu.yml b/okemu.yml new file mode 100644 index 0000000..2e711a1 --- /dev/null +++ b/okemu.yml @@ -0,0 +1,4 @@ +logFile: "okemu.log" +logLevel: "info" +monitorFile: "okean240/MON_r6.BIN" +cpmFile: "okean240/CPM_r7.BIN" diff --git a/rebuild.sh b/rebuild.sh new file mode 100755 index 0000000..84c7b84 --- /dev/null +++ b/rebuild.sh @@ -0,0 +1,14 @@ +#! /bin/bash + +echo "Build OkEmu" +export GOROOT=/home/roma/projects/go/1.25.7/ +export PATH=/home/roma/projects/go/1.25.7/bin/:$PATH + +version=$(git describe --tags HEAD) +#commit=$(git rev-parse HEAD) +timestamp=$(date +%Y-%m-%d' '%H:%M:%S) + +go build -ldflags "-X 'main.Version=$version' -X 'main.BuildTime=$timestamp'" . + +#echo "Copy to kubelogtst01" +#scp stash boykovra@kubelogtst01: diff --git a/z80em/constants.go b/z80em/constants.go new file mode 100644 index 0000000..fe99814 --- /dev/null +++ b/z80em/constants.go @@ -0,0 +1,101 @@ +package z80em + +const OpHalt = 0x76 +const OpLdBB = 0x40 +const OpAddAB = 0x80 +const OpRetNz = 0xc0 + +var CycleCounts = []byte{ + 4, 10, 7, 6, 4, 4, 7, 4, 4, 11, 7, 6, 4, 4, 7, 4, + 8, 10, 7, 6, 4, 4, 7, 4, 12, 11, 7, 6, 4, 4, 7, 4, + 7, 10, 16, 6, 4, 4, 7, 4, 7, 11, 16, 6, 4, 4, 7, 4, + 7, 10, 13, 6, 11, 11, 10, 4, 7, 11, 13, 6, 4, 4, 7, 4, + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, + 7, 7, 7, 7, 7, 7, 4, 7, 4, 4, 4, 4, 4, 4, 7, 4, + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, + 5, 10, 10, 10, 10, 11, 7, 11, 5, 10, 10, 0, 10, 17, 7, 11, + 5, 10, 10, 11, 10, 11, 7, 11, 5, 4, 10, 11, 10, 0, 7, 11, + 5, 10, 10, 19, 10, 11, 7, 11, 5, 4, 10, 4, 10, 0, 7, 11, + 5, 10, 10, 4, 10, 11, 7, 11, 5, 6, 10, 4, 10, 0, 7, 11, +} + +var CycleCountsCb = []byte{ + 8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, + 8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, + 8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, + 8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, + 8, 8, 8, 8, 8, 8, 12, 8, 8, 8, 8, 8, 8, 8, 12, 8, + 8, 8, 8, 8, 8, 8, 12, 8, 8, 8, 8, 8, 8, 8, 12, 8, + 8, 8, 8, 8, 8, 8, 12, 8, 8, 8, 8, 8, 8, 8, 12, 8, + 8, 8, 8, 8, 8, 8, 12, 8, 8, 8, 8, 8, 8, 8, 12, 8, + 8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, + 8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, + 8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, + 8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, + 8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, + 8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, + 8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, + 8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, +} + +var CycleCountsDd = []byte{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, + 0, 14, 20, 10, 8, 8, 11, 0, 0, 15, 20, 10, 8, 8, 11, 0, + 0, 0, 0, 0, 23, 23, 19, 0, 0, 15, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 8, 19, 0, 0, 0, 0, 0, 8, 8, 19, 0, + 0, 0, 0, 0, 8, 8, 19, 0, 0, 0, 0, 0, 8, 8, 19, 0, + 8, 8, 8, 8, 8, 8, 19, 8, 8, 8, 8, 8, 8, 8, 19, 8, + 19, 19, 19, 19, 19, 19, 0, 19, 0, 0, 0, 0, 8, 8, 19, 0, + 0, 0, 0, 0, 8, 8, 19, 0, 0, 0, 0, 0, 8, 8, 19, 0, + 0, 0, 0, 0, 8, 8, 19, 0, 0, 0, 0, 0, 8, 8, 19, 0, + 0, 0, 0, 0, 8, 8, 19, 0, 0, 0, 0, 0, 8, 8, 19, 0, + 0, 0, 0, 0, 8, 8, 19, 0, 0, 0, 0, 0, 8, 8, 19, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 14, 0, 23, 0, 15, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, +} + +var CycleCountsEd = []byte{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 12, 12, 15, 20, 8, 14, 8, 9, 12, 12, 15, 20, 8, 14, 8, 9, + 12, 12, 15, 20, 8, 14, 8, 9, 12, 12, 15, 20, 8, 14, 8, 9, + 12, 12, 15, 20, 8, 14, 8, 18, 12, 12, 15, 20, 8, 14, 8, 18, + 12, 12, 15, 20, 8, 14, 8, 0, 12, 12, 15, 20, 8, 14, 8, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16, 16, 16, 16, 0, 0, 0, 0, 16, 16, 16, 16, 0, 0, 0, 0, + 16, 16, 16, 16, 0, 0, 0, 0, 16, 16, 16, 16, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +} + +var ParityBits = []bool{ + true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, + false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, + false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, + true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, + false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, + true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, + true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, + false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, + false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, + true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, + true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, + false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, + true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, + false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, + false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, + true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, +} diff --git a/z80em/core.go b/z80em/core.go deleted file mode 100644 index 4609f89..0000000 --- a/z80em/core.go +++ /dev/null @@ -1,27 +0,0 @@ -package z80em - -const OP_HALT = 0x76 -const OP_LD_B_B = 0x40 -const OP_LD_A_A = 0x7f -const OP_ADD_A_B = 0x80 -const OP_RET_NZ = 0xc0 - -var CYCLE_COUNTS = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} -var PARITY_BITS = []bool{ - true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, - false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, - false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, - true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, - false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, - true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, - true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, - false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, - false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, - true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, - true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, - false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, - true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, - false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, - false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, - true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, -} diff --git a/z80em/opcode.go b/z80em/opcode.go new file mode 100644 index 0000000..6f98089 --- /dev/null +++ b/z80em/opcode.go @@ -0,0 +1,657 @@ +package z80em + +var instructions = []func(s *Z80Type){ + // NOP + 0x00: func(s *Z80Type) { + // NOP + }, + // LD BC, nn + 0x01: func(s *Z80Type) { + s.PC++ + s.C = s.core.MemRead(s.PC) + s.PC++ + s.B = s.core.MemRead(s.PC) + }, + // LD (BC), A + 0x02: func(s *Z80Type) { + s.core.MemWrite(s.bc(), s.A) + }, + // 0x03 : INC BC + 0x03: func(s *Z80Type) { + s.incBc() + }, + // 0x04 : INC B + 0x04: func(s *Z80Type) { + s.B = s.doInc(s.B) + }, + // 0x05 : DEC B + 0x05: func(s *Z80Type) { + s.B = s.doDec(s.B) + }, + // 0x06 : LD B, n + 0x06: func(s *Z80Type) { + s.PC++ + s.B = s.core.MemRead(s.PC) + }, + // 0x07 : RLCA + 0x07: func(s *Z80Type) { + // This instruction is implemented as a special case of the + // more general Z80-specific RLC instruction. + // Specifially, RLCA is a version of RLC A that affects fewer flags. + // The same applies to RRCA, RLA, and RRA. + tempS := s.Flags.S + tempZ := s.Flags.Z + tempP := s.Flags.P + s.A = s.doRlc(s.A) + s.Flags.S = tempS + s.Flags.Z = tempZ + s.Flags.P = tempP + }, + // 0x08 : EX AF, AF' + 0x08: func(s *Z80Type) { + s.A, s.APrime = s.APrime, s.A + temp := s.getFlagsRegister() + s.setFlagsRegister(s.getFlagsPrimeRegister()) + s.setFlagsPrimeRegister(temp) + }, + // 0x09 : ADD HL, BC + 0x09: func(s *Z80Type) { + s.doHlAdd(s.bc()) + }, + // 0x0a : LD A, (BC) + 0x0A: func(s *Z80Type) { + s.A = s.core.MemRead(s.bc()) + }, + // 0x0b : DEC BC + 0x0B: func(s *Z80Type) { + s.decBc() + }, + // 0x0c : INC C + 0x0C: func(s *Z80Type) { + s.C = s.doInc(s.C) + }, + // 0x0d : DEC C + 0x0D: func(s *Z80Type) { + s.C = s.doDec(s.C) + }, + // 0x0e : LD C, n + 0x0E: func(s *Z80Type) { + s.PC++ + s.C = s.core.MemRead(s.PC) + }, + // 0x0f : RRCA + 0x0F: func(s *Z80Type) { + tempS := s.Flags.S + tempZ := s.Flags.Z + tempP := s.Flags.P + s.A = s.doRrc(s.A) + s.Flags.S = tempS + s.Flags.Z = tempZ + s.Flags.P = tempP + }, + // 0x10 : DJNZ nn + 0x10: func(s *Z80Type) { + s.B-- + s.doConditionalRelativeJump(s.B != 0) + }, + // 0x11 : LD DE, nn + 0x11: func(s *Z80Type) { + s.PC++ + s.E = s.core.MemRead(s.PC) + s.PC++ + s.D = s.core.MemRead(s.PC) + }, + // 0x12 : LD (DE), A + 0x12: func(s *Z80Type) { + s.core.MemWrite(s.de(), s.A) + }, + // 0x13 : INC DE + 0x13: func(s *Z80Type) { + s.incDe() + }, + // 0x14 : INC D + 0x14: func(s *Z80Type) { + s.D = s.doInc(s.D) + }, + // 0x15 : DEC D + 0x15: func(s *Z80Type) { + s.D = s.doDec(s.D) + }, + // 0x16 : LD D, n + 0x16: func(s *Z80Type) { + s.PC++ + s.D = s.core.MemRead(s.PC) + }, + // 0x17 : RLA + 0x17: func(s *Z80Type) { + tempS := s.Flags.S + tempZ := s.Flags.Z + tempP := s.Flags.P + s.A = s.doRl(s.A) + s.Flags.S = tempS + s.Flags.Z = tempZ + s.Flags.P = tempP + }, + // 0x18 : JR n + 0x18: func(s *Z80Type) { + var o = int8(s.core.MemRead(s.PC + 1)) + if o > 0 { + s.PC += uint16(o) + } else { + s.PC -= uint16(-o) + } + s.PC++ + }, + // 0x19 : ADD HL, DE + 0x19: func(s *Z80Type) { + s.doHlAdd(s.de()) + }, + // 0x1a : LD A, (DE) + 0x1A: func(s *Z80Type) { + s.A = s.core.MemRead(s.de()) + }, + // 0x1b : DEC DE + 0x1B: func(s *Z80Type) { + s.decDe() + }, + // 0x1c : INC E + 0x1C: func(s *Z80Type) { + s.E = s.doInc(s.E) + }, + // 0x1d : DEC E + 0x1D: func(s *Z80Type) { + s.E = s.doDec(s.E) + }, + // 0x1e : LD E, n + 0x1E: func(s *Z80Type) { + s.PC++ + s.E = s.core.MemRead(s.PC) + }, + // 0x1f : RRA + 0x1F: func(s *Z80Type) { + tempS := s.Flags.S + tempZ := s.Flags.Z + tempP := s.Flags.P + s.A = s.doRr(s.A) + s.Flags.S = tempS + s.Flags.Z = tempZ + s.Flags.P = tempP + }, + // 0x20 : JR NZ, n + 0x20: func(s *Z80Type) { + s.doConditionalRelativeJump(!s.Flags.Z) + }, + // 0x21 : LD HL, nn + 0x21: func(s *Z80Type) { + s.PC++ + s.L = s.core.MemRead(s.PC) + s.PC++ + s.H = s.core.MemRead(s.PC) + }, + // 0x22 : LD (nn), HL + 0x22: func(s *Z80Type) { + addr := s.getAddr() + s.core.MemWrite(addr, s.L) + s.core.MemWrite(addr+1, s.H) + }, + // 0x23 : INC HL + 0x23: func(s *Z80Type) { + s.incHl() + }, + // 0x24 : INC H + 0x24: func(s *Z80Type) { + s.H = s.doInc(s.H) + }, + // 0x25 : DEC H + 0x25: func(s *Z80Type) { + s.H = s.doDec(s.H) + }, + // 0x26 : LD H, n + 0x26: func(s *Z80Type) { + s.PC++ + s.H = s.core.MemRead(s.PC) + }, + // 0x27 : DAA + 0x27: func(s *Z80Type) { + temp := s.A + if !s.Flags.N { + if s.Flags.H || ((s.A & 0x0f) > 9) { + temp += 0x06 + } + if s.Flags.C || (s.A > 0x99) { + temp += 0x60 + } + } else { + if s.Flags.H || ((s.A & 0x0f) > 9) { + temp -= 0x06 + } + if s.Flags.C || (s.A > 0x99) { + temp -= 0x60 + } + } + + s.Flags.S = (temp & 0x80) != 0 + s.Flags.Z = temp == 0 + s.Flags.H = ((s.A & 0x10) ^ (temp & 0x10)) != 0 + s.Flags.P = ParityBits[temp] + // DAA never clears the carry flag if it was already set, + // but it is able to set the carry flag if it was clear. + // Don't ask me, I don't know. + // Note also that we check for a BCD carry, instead of the usual. + s.Flags.C = s.Flags.C || (s.A > 0x99) + s.A = temp + s.updateXYFlags(s.A) + }, + // 0x28 : JR Z, n + 0x28: func(s *Z80Type) { + s.doConditionalRelativeJump(s.Flags.Z) + }, + // 0x29 : ADD HL, HL + 0x29: func(s *Z80Type) { + s.doHlAdd(s.hl()) + }, + // 0x2a : LD HL, (nn) + 0x2A: func(s *Z80Type) { + addr := s.getAddr() + s.L = s.core.MemRead(addr) + s.H = s.core.MemRead(addr + 1) + }, + // 0x2b : DEC HL + 0x2B: func(s *Z80Type) { + s.decHl() + }, + // 0x2c : INC L + 0x2C: func(s *Z80Type) { + s.L = s.doInc(s.L) + }, + // 0x2d : DEC L + 0x2D: func(s *Z80Type) { + s.L = s.doDec(s.L) + }, + // 0x2e : LD L, n + 0x2E: func(s *Z80Type) { + s.PC++ + s.L = s.core.MemRead(s.PC) + }, + // 0x2f : CPL + 0x2F: func(s *Z80Type) { + s.A = ^s.A + s.Flags.N = true + s.Flags.H = true + s.updateXYFlags(s.A) + }, + // 0x30 : JR NC, n + 0x30: func(s *Z80Type) { + s.doConditionalRelativeJump(!s.Flags.C) + }, + // 0x31 : LD SP, nn + 0x31: func(s *Z80Type) { + s.PC++ + lo := s.core.MemRead(s.PC) + s.PC++ + s.SP = (uint16(s.core.MemRead(s.PC)) << 8) | uint16(lo) + }, + // 0x32 : LD (nn), A + 0x32: func(s *Z80Type) { + s.core.MemWrite(s.getAddr(), s.A) + }, + // 0x33 : INC SP + 0x33: func(s *Z80Type) { + s.SP++ + }, + // 0x34 : INC (HL) + 0x34: func(s *Z80Type) { + s.core.MemWrite(s.hl(), s.doInc(s.core.MemRead(s.hl()))) + }, + // 0x35 : DEC (HL) + 0x35: func(s *Z80Type) { + s.core.MemWrite(s.hl(), s.doDec(s.core.MemRead(s.hl()))) + }, + // 0x36 : LD (HL), n + 0x36: func(s *Z80Type) { + s.PC++ + s.core.MemWrite(s.hl(), s.core.MemRead(s.PC)) + }, + // 0x37 : SCF + 0x37: func(s *Z80Type) { + s.Flags.N = false + s.Flags.H = false + s.Flags.C = true + s.updateXYFlags(s.A) + }, + // 0x38 : JR C, n + 0x38: func(s *Z80Type) { + s.doConditionalRelativeJump(s.Flags.C) + }, + // 0x39 : ADD HL, SP + 0x39: func(s *Z80Type) { + s.doHlAdd(s.SP) + }, + // 0x3a : LD A, (nn) + 0x3A: func(s *Z80Type) { + s.A = s.core.MemRead(s.getAddr()) + }, + // 0x3b : DEC SP + 0x3B: func(s *Z80Type) { + s.SP-- + }, + // 0x3c : INC A + 0x3C: func(s *Z80Type) { + s.A = s.doInc(s.A) + }, + // 0x3d : DEC A + 0x3D: func(s *Z80Type) { + s.A = s.doDec(s.A) + }, + // 0x3e : LD A, n + 0x3E: func(s *Z80Type) { + s.PC++ + s.A = s.core.MemRead(s.PC) + }, + // 0x3f : CCF + 0x3F: func(s *Z80Type) { + s.Flags.N = false + s.Flags.H = s.Flags.C + s.Flags.C = !s.Flags.C + s.updateXYFlags(s.A) + }, + // 0xc0 : RET NZ + 0xC0: func(s *Z80Type) { + s.doConditionalReturn(!s.Flags.Z) + }, + // 0xc1 : POP BC + 0xC1: func(s *Z80Type) { + result := s.PopWord() + s.C = byte(result & 0xff) + s.B = byte((result & 0xff00) >> 8) + }, + // 0xc2 : JP NZ, nn + 0xC2: func(s *Z80Type) { + s.doConditionalAbsoluteJump(!s.Flags.Z) + }, + // 0xc3 : JP nn + 0xC3: func(s *Z80Type) { + s.PC = uint16(s.core.MemRead(s.PC+1)) | (uint16(s.core.MemRead(s.PC+2)) << 8) + s.PC-- + }, + // 0xc4 : CALL NZ, nn + 0xC4: func(s *Z80Type) { + s.doConditionalCall(!s.Flags.Z) + }, + // 0xc5 : PUSH BC + 0xC5: func(s *Z80Type) { + s.pushWord((uint16(s.B) << 8) | uint16(s.C)) + }, + // 0xc6 : ADD A, n + 0xC6: func(s *Z80Type) { + s.PC++ + s.doAdd(s.core.MemRead(s.PC)) + }, + // 0xc7 : RST 00h + 0xC7: func(s *Z80Type) { + s.doReset(0x0000) + }, + // 0xc8 : RET Z + 0xC8: func(s *Z80Type) { + s.doConditionalReturn(s.Flags.Z) + }, + // 0xc9 : RET + 0xC9: func(s *Z80Type) { + s.PC = s.PopWord() - 1 + }, + // 0xca : JP Z, nn + 0xCA: func(s *Z80Type) { + s.doConditionalAbsoluteJump(s.Flags.Z) + }, + // 0xcb : CB Prefix + 0xCB: func(s *Z80Type) { + s.opcodeCB() + }, + // 0xcc : CALL Z, nn + 0xCC: func(s *Z80Type) { + s.doConditionalCall(s.Flags.Z) + }, + // 0xcd : CALL nn + 0xCD: func(s *Z80Type) { + s.pushWord(s.PC + 3) + s.PC = uint16(s.core.MemRead(s.PC+1)) | (uint16(s.core.MemRead(s.PC+2)) << 8) + s.PC-- + }, + // 0xce : ADC A, n + 0xCE: func(s *Z80Type) { + s.PC++ + s.doAdc(s.core.MemRead(s.PC)) + }, + // 0xcf : RST 08h + 0xCF: func(s *Z80Type) { + s.doReset(0x0008) + }, + // 0xd0 : RET NC + 0xD0: func(s *Z80Type) { + s.doConditionalReturn(!s.Flags.C) + }, + // 0xd1 : POP DE + 0xD1: func(s *Z80Type) { + result := s.PopWord() + s.E = byte(result & 0xff) + s.D = byte((result & 0xff00) >> 8) + }, + // 0xd2 : JP NC, nn + 0xD2: func(s *Z80Type) { + s.doConditionalAbsoluteJump(!s.Flags.C) + }, + // 0xd3 : OUT (n), A + 0xD3: func(s *Z80Type) { + s.PC++ + s.core.IOWrite((uint16(s.A)<<8)|uint16(s.core.MemRead(s.PC)), s.A) + }, + // 0xd4 : CALL NC, nn + 0xD4: func(s *Z80Type) { + s.doConditionalCall(!s.Flags.C) + }, + // 0xd5 : PUSH DE + 0xD5: func(s *Z80Type) { + s.pushWord((uint16(s.D) << 8) | uint16(s.E)) + }, + // 0xd6 : SUB n + 0xD6: func(s *Z80Type) { + s.PC++ + s.doSub(s.core.MemRead(s.PC)) + }, + // 0xd7 : RST 10h + 0xD7: func(s *Z80Type) { + s.doReset(0x0010) + }, + // 0xd8 : RET C + 0xD8: func(s *Z80Type) { + s.doConditionalReturn(s.Flags.C) + }, + // 0xd9 : EXX + 0xD9: func(s *Z80Type) { + s.B, s.BPrime = s.BPrime, s.B + s.C, s.CPrime = s.CPrime, s.C + s.D, s.DPrime = s.DPrime, s.D + s.E, s.EPrime = s.EPrime, s.E + s.H, s.HPrime = s.HPrime, s.H + s.L, s.LPrime = s.LPrime, s.L + }, + // 0xda : JP C, nn + 0xDA: func(s *Z80Type) { + s.doConditionalAbsoluteJump(s.Flags.C) + }, + // 0xdb : IN A, (n) + 0xDB: func(s *Z80Type) { + s.PC++ + s.A = s.core.IORead((uint16(s.A) << 8) | uint16(s.core.MemRead(s.PC))) + }, + // 0xdc : CALL C, nn + 0xDC: func(s *Z80Type) { + s.doConditionalCall(s.Flags.C) + }, + // 0xdd : DD Prefix (IX instructions) + 0xDD: func(s *Z80Type) { + s.opcodeDD() + }, + // 0xde : SBC n + 0xDE: func(s *Z80Type) { + s.PC++ + s.doSbc(s.core.MemRead(s.PC)) + }, + // 0xdf : RST 18h + 0xDF: func(s *Z80Type) { + s.doReset(0x0018) + }, + // 0xe0 : RET PO + 0xE0: func(s *Z80Type) { + s.doConditionalReturn(!s.Flags.P) + }, + // 0xe1 : POP HL + 0xE1: func(s *Z80Type) { + result := s.PopWord() + s.L = byte(result & 0xff) + s.H = byte((result & 0xff00) >> 8) + }, + // 0xe2 : JP PO, (nn) + 0xE2: func(s *Z80Type) { + s.doConditionalAbsoluteJump(!s.Flags.P) + }, + // 0xe3 : EX (SP), HL + 0xE3: func(s *Z80Type) { + temp := s.core.MemRead(s.SP) + s.core.MemWrite(s.SP, s.L) + s.L = temp + temp = s.core.MemRead(s.SP + 1) + s.core.MemWrite(s.SP+1, s.H) + s.H = temp + }, + // 0xe4 : CALL PO, nn + 0xE4: func(s *Z80Type) { + s.doConditionalCall(!s.Flags.P) + }, + // 0xe5 : PUSH HL + 0xE5: func(s *Z80Type) { + s.pushWord((uint16(s.H) << 8) | uint16(s.L)) + }, + // 0xe6 : AND n + 0xE6: func(s *Z80Type) { + s.PC++ + s.doAnd(s.core.MemRead(s.PC)) + }, + // 0xe7 : RST 20h + 0xE7: func(s *Z80Type) { + s.doReset(0x0020) + }, + // 0xe8 : RET PE + 0xE8: func(s *Z80Type) { + s.doConditionalReturn(s.Flags.P) + }, + // 0xe9 : JP (HL) + 0xE9: func(s *Z80Type) { + s.PC = uint16(s.H)<<8 | uint16(s.L) + s.PC-- + }, + // 0xea : JP PE, nn + 0xEA: func(s *Z80Type) { + s.doConditionalAbsoluteJump(s.Flags.P) + }, + // 0xeb : EX DE, HL + 0xEB: func(s *Z80Type) { + s.D, s.H = s.H, s.D + s.E, s.L = s.L, s.E + }, + // 0xec : CALL PE, nn + 0xEC: func(s *Z80Type) { + s.doConditionalCall(s.Flags.P) + }, + // 0xed : ED Prefix + 0xED: func(s *Z80Type) { + s.opcodeED() + }, + // 0xee : XOR n + 0xEE: func(s *Z80Type) { + s.PC++ + s.doXor(s.core.MemRead(s.PC)) + }, + // 0xef : RST 28h + 0xEF: func(s *Z80Type) { + s.doReset(0x0028) + }, + // 0xf0 : RET P + 0xF0: func(s *Z80Type) { + s.doConditionalReturn(!s.Flags.S) + }, + // 0xf1 : POP AF + 0xF1: func(s *Z80Type) { + var result = s.PopWord() + s.setFlagsRegister(byte(result & 0xff)) + s.A = byte((result & 0xff00) >> 8) + }, + // 0xf2 : JP P, nn + 0xF2: func(s *Z80Type) { + s.doConditionalAbsoluteJump(!s.Flags.S) + }, + // 0xf3 : DI + 0xF3: func(s *Z80Type) { + // DI doesn't actually take effect until after the next instruction. + s.DoDelayedDI = true + }, + // 0xf4 : CALL P, nn + 0xF4: func(s *Z80Type) { + s.doConditionalCall(!s.Flags.S) + }, + // 0xf5 : PUSH AF + 0xF5: func(s *Z80Type) { + s.pushWord(uint16(s.getFlagsRegister()) | (uint16(s.A) << 8)) + }, + // 0xf6 : OR n + 0xF6: func(s *Z80Type) { + s.PC++ + s.doOr(s.core.MemRead(s.PC)) + }, + // 0xf7 : RST 30h + 0xF7: func(s *Z80Type) { + s.doReset(0x0030) + }, + // 0xf8 : RET M + 0xF8: func(s *Z80Type) { + s.doConditionalReturn(s.Flags.S) + }, + // 0xf9 : LD SP, HL + 0xF9: func(s *Z80Type) { + s.SP = uint16(s.H)<<8 | uint16(s.L) + }, + // 0xfa : JP M, nn + 0xFA: func(s *Z80Type) { + s.doConditionalAbsoluteJump(s.Flags.S) + }, + // 0xfb : EI + 0xFB: func(s *Z80Type) { + // EI doesn't actually take effect until after the next instruction. + s.DoDelayedEI = true + }, + // 0xfc : CALL M, nn + 0xFC: func(s *Z80Type) { + s.doConditionalCall(s.Flags.S) + }, + // 0xfd : FD Prefix (IY instructions) + 0xFD: func(s *Z80Type) { + s.opcodeFD() + }, + // 0xfe : CP n + 0xFE: func(s *Z80Type) { + s.PC++ + s.doCp(s.core.MemRead(s.PC)) + }, + // 0xff : RST 38h + 0xFF: func(s *Z80Type) { + s.doReset(0x0038) + }, +} + +func (z *Z80Type) getAddr() uint16 { + z.PC++ + addr := uint16(z.core.MemRead(z.PC)) + z.PC++ + addr |= uint16(z.core.MemRead(z.PC)) << 8 + return addr +} diff --git a/z80em/opcodeCB.go b/z80em/opcodeCB.go new file mode 100644 index 0000000..3f33156 --- /dev/null +++ b/z80em/opcodeCB.go @@ -0,0 +1,106 @@ +package z80em + +func (z *Z80Type) opcodeCB() { + z.R = (z.R & 0x80) | (((z.R & 0x7f) + 1) & 0x7f) + z.PC++ + opcode := z.core.M1MemRead(z.PC) + bitNumber := (opcode & 0x38) >> 3 + regCode := opcode & 0x07 + if opcode < 0x40 { + // Shift/rotate instructions + opArray := []OpShift{z.doRlc, z.doRrc, z.doRl, z.doRr, z.doSla, z.doSra, z.doSll, z.doSrl} + switch regCode { + case 0: + z.B = opArray[bitNumber](z.B) + case 1: + z.C = opArray[bitNumber](z.C) + case 2: + z.D = opArray[bitNumber](z.D) + case 3: + z.E = opArray[bitNumber](z.E) + case 4: + z.H = opArray[bitNumber](z.H) + case 5: + z.L = opArray[bitNumber](z.L) + case 6: + z.core.MemWrite(z.hl(), opArray[bitNumber](z.core.MemRead(z.hl()))) + default: + z.A = opArray[bitNumber](z.A) + } + } else if opcode < 0x80 { + // BIT instructions + switch regCode { + case 0: + z.Flags.Z = z.B&(1<>8)) + }, + // 0x23 : INC IX + 0x23: func(s *Z80Type) { + s.IX++ + }, + // 0x24 : INC IXH (Undocumented) + 0x24: func(s *Z80Type) { + s.IX = (uint16(s.doInc(byte(s.IX>>8))) << 8) | (s.IX & 0x00ff) + }, + // 0x25 : DEC IXH (Undocumented) + 0x25: func(s *Z80Type) { + s.IX = (uint16(s.doDec(byte(s.IX>>8))) << 8) | (s.IX & 0x00ff) + }, + // 0x26 : LD IXH, n (Undocumented) + 0x26: func(s *Z80Type) { + s.PC++ + s.IX = (uint16(s.core.MemRead(s.PC)) << 8) | (s.IX & 0x00ff) + }, + // 0x29 : ADD IX, IX + 0x29: func(s *Z80Type) { + s.doIxAdd(s.IX) + }, + // 0x2a : LD IX, (nn) + 0x2A: func(s *Z80Type) { + addr := s.getAddr() + s.IX = (uint16(s.core.MemRead(addr)) << 8) | uint16(s.core.MemRead(addr+1)) + }, + // 0x2b : DEC IX + 0x2B: func(s *Z80Type) { + s.IX-- + }, + // 0x2c : INC IXL (Undocumented) + 0x2C: func(s *Z80Type) { + s.IX = (uint16(s.doInc(byte(s.IX & 0x00ff)))) | (s.IX & 0xff00) + }, + // 0x2d : DEC IXL (Undocumented) + 0x2D: func(s *Z80Type) { + s.IX = (uint16(s.doDec(byte(s.IX & 0x00ff)))) | (s.IX & 0xff00) + }, + // 0x2e : LD IXL, n (Undocumented) + 0x2E: func(s *Z80Type) { + s.PC++ + s.IX = (uint16(s.core.MemRead(s.PC))) | (s.IX & 0xff00) + }, + // 0x34 : INC (IX+n) + 0x34: func(s *Z80Type) { + offset := s.getOffset(s.IX) + value := s.core.MemRead(offset) + s.core.MemWrite(offset, s.doInc(value)) + }, + // 0x35 : DEC (IX+n) + 0x35: func(s *Z80Type) { + offset := s.getOffset(s.IX) + value := s.core.MemRead(offset) + s.core.MemWrite(offset, s.doDec(value)) + }, + // 0x36 : LD (IX+n), n + 0x36: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.PC++ + s.core.MemWrite(offset, s.core.MemRead(s.PC)) + }, + // 0x39 : ADD IX, SP + 0x39: func(s *Z80Type) { + s.doIxAdd(s.SP) + }, + // 0x44 : LD B, IXH (Undocumented) + 0x44: func(s *Z80Type) { + s.B = byte(s.IX >> 8) + }, + // 0x45 : LD B, IXL (Undocumented) + 0x45: func(s *Z80Type) { + s.B = byte(s.IX & 0x00ff) + }, + // 0x46 : LD B, (IX+n) + 0x46: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.B = s.core.MemRead(offset) + }, + // 0x4c : LD C, IXH (Undocumented) + 0x4C: func(s *Z80Type) { + s.C = byte(s.IX >> 8) + }, + // 0x4d : LD C, IXL (Undocumented) + 0x4D: func(s *Z80Type) { + s.C = byte(s.IX & 0x00ff) + }, + // 0x4e : LD C, (IX+n) + 0x4E: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.C = s.core.MemRead(offset) + }, + // 0x54 : LD D, IXH (Undocumented) + 0x54: func(s *Z80Type) { + s.D = byte(s.IX >> 8) + }, + // 0x55 : LD D, IXL (Undocumented) + 0x55: func(s *Z80Type) { + s.D = byte(s.IX & 0x00ff) + }, + // 0x56 : LD D, (IX+n) + 0x56: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.D = s.core.MemRead(offset) + }, + // 0x5d : LD E, IXL (Undocumented) + 0x5D: func(s *Z80Type) { + s.E = byte(s.IX & 0x00ff) + }, + // 0x5e : LD E, (IX+n) + 0x5E: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.E = s.core.MemRead(offset) + }, + // 0x60 : LD IXH, B (Undocumented) + 0x60: func(s *Z80Type) { + s.IX = uint16(s.B)<<8 | s.IX&0x00ff + }, + // 0x61 : LD IXH, C (Undocumented) + 0x61: func(s *Z80Type) { + s.IX = uint16(s.C)<<8 | s.IX&0x00ff + }, + // 0x62 : LD IXH, D (Undocumented) + 0x62: func(s *Z80Type) { + s.IX = uint16(s.D)<<8 | s.IX&0x00ff + }, + // 0x63 : LD IXH, E (Undocumented) + 0x63: func(s *Z80Type) { + s.IX = uint16(s.E)<<8 | s.IX&0x00ff + }, + // 0x64 : LD IXH, IXH (Undocumented) + 0x64: func(s *Z80Type) { + // NOP + }, + // 0x65 : LD IXH, IXL (Undocumented) + 0x65: func(s *Z80Type) { + s.IX = (s.IX << 8) | (s.IX & 0x00ff) + }, + // 0x66 : LD H, (IX+n) + 0x66: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.H = s.core.MemRead(offset) + }, + // 0x67 : LD IXH, A (Undocumented) + 0x67: func(s *Z80Type) { + s.IX = (uint16(s.A) << 8) | (s.IX & 0x00ff) + }, + // 0x68 : LD IXL, B (Undocumented) + 0x68: func(s *Z80Type) { + s.IX = (s.IX & 0xff00) | uint16(s.B) + }, + // 0x69 : LD IXL, C (Undocumented) + 0x69: func(s *Z80Type) { + s.IX = (s.IX & 0xff00) | uint16(s.C) + }, + // 0x6a : LD IXL, D (Undocumented) + 0x6a: func(s *Z80Type) { + s.IX = (s.IX & 0xff00) | uint16(s.D) + }, + // 0x6b : LD IXL, E (Undocumented) + 0x6b: func(s *Z80Type) { + s.IX = (s.IX & 0xff00) | uint16(s.E) + }, + // 0x6c : LD IXL, IXH (Undocumented) + 0x6c: func(s *Z80Type) { + s.IX = (s.IX >> 8) | (s.IX & 0xff00) + }, + // 0x6d : LD IXL, IXL (Undocumented) + 0x6d: func(s *Z80Type) { + // NOP + }, + // 0x6e : LD L, (IX+n) + 0x6e: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.L = s.core.MemRead(offset) + }, + // 0x6f : LD IXL, A (Undocumented) + 0x6f: func(s *Z80Type) { + s.IX = uint16(s.A) | (s.IX & 0xff00) + }, + // 0x70 : LD (IX+n), B + 0x70: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.core.MemWrite(offset, s.B) + }, + // 0x71 : LD (IX+n), C + 0x71: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.core.MemWrite(offset, s.C) + }, + // 0x72 : LD (IX+n), D + 0x72: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.core.MemWrite(offset, s.D) + }, + // 0x73 : LD (IX+n), E + 0x73: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.core.MemWrite(offset, s.E) + }, + // 0x74 : LD (IX+n), H + 0x74: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.core.MemWrite(offset, s.H) + }, + // 0x75 : LD (IX+n), L + 0x75: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.core.MemWrite(offset, s.L) + }, + // 0x77 : LD (IX+n), A + 0x77: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.core.MemWrite(offset, s.A) + }, + // 0x7c : LD A, IXH (Undocumented) + 0x7C: func(s *Z80Type) { + s.A = byte(s.IX >> 8) + }, + // 0x7d : LD A, IXL (Undocumented) + 0x7D: func(s *Z80Type) { + s.A = byte(s.IX & 0x00ff) + }, + // 0x7e : LD A, (IX+n) + 0x7E: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.A = s.core.MemRead(offset) + }, + // 0x84 : ADD A, IXH (Undocumented) + 0x84: func(s *Z80Type) { + s.doAdd(byte(s.IX >> 8)) + }, + // 0x85 : ADD A, IXL (Undocumented) + 0x85: func(s *Z80Type) { + s.doAdd(byte(s.IX & 0x00ff)) + }, + // 0x86 : ADD A, (IX+n) + 0x86: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.doAdd(s.core.MemRead(offset)) + }, + // 0x8c : ADC A, IXH (Undocumented) + 0x8C: func(s *Z80Type) { + s.doAdc(byte(s.IX >> 8)) + }, + // 0x8d : ADC A, IXL (Undocumented) + 0x8D: func(s *Z80Type) { + s.doAdc(byte(s.IX & 0x00ff)) + }, + // 0x8e : ADC A, (IX+n) + 0x8E: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.doAdc(s.core.MemRead(offset)) + }, + // 0x94 : SUB IXH (Undocumented) + 0x94: func(s *Z80Type) { + s.doSub(byte(s.IX >> 8)) + }, + // 0x95 : SUB IXL (Undocumented) + 0x95: func(s *Z80Type) { + s.doSub(byte(s.IX & 0x00ff)) + }, + // 0x96 : SUB A, (IX+n) + 0x96: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.doSub(s.core.MemRead(offset)) + }, + // 0x9c : SBC IXH (Undocumented) + 0x9C: func(s *Z80Type) { + s.doSbc(byte(s.IX >> 8)) + }, + // 0x9d : SBC IXL (Undocumented) + 0x9D: func(s *Z80Type) { + s.doSbc(byte(s.IX & 0x00ff)) + }, + // 0x9e : SBC A, (IX+n) + 0x9E: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.doSbc(s.core.MemRead(offset)) + }, + // 0xa4 : AND IXH (Undocumented) + 0xA4: func(s *Z80Type) { + s.doAnd(byte(s.IX >> 8)) + }, + // 0xa5 : AND IXL (Undocumented) + 0xA5: func(s *Z80Type) { + s.doAnd(byte(s.IX & 0x00ff)) + }, + // 0xa6 : AND A, (IX+n) + 0xA6: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.doAnd(s.core.MemRead(offset)) + }, + // 0xac : XOR IXH (Undocumented) + 0xAC: func(s *Z80Type) { + s.doXor(byte(s.IX >> 8)) + }, + // 0xad : XOR IXL (Undocumented) + 0xAD: func(s *Z80Type) { + s.doXor(byte(s.IX & 0x00ff)) + }, + // 0xae : XOR A, (IX+n) + 0xAE: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.doXor(s.core.MemRead(offset)) + }, + // 0xb4 : OR IXH (Undocumented) + 0xB4: func(s *Z80Type) { + s.doOr(byte(s.IX >> 8)) + }, + // 0xb5 : OR IXL (Undocumented) + 0xB5: func(s *Z80Type) { + s.doOr(byte(s.IX & 0x00ff)) + }, + // 0xb6 : OR A, (IX+n) + 0xB6: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.doOr(s.core.MemRead(offset)) + }, + // 0xbc : CP IXH (Undocumented) + 0xBC: func(s *Z80Type) { + s.doCp(byte(s.IX >> 8)) + }, + // 0xbd : CP IXL (Undocumented) + 0xBD: func(s *Z80Type) { + s.doCp(byte(s.IX & 0x00ff)) + }, + // 0xbe : CP A, (IX+n) + 0xBE: func(s *Z80Type) { + offset := s.getOffset(s.IX) + s.doCp(s.core.MemRead(offset)) + }, + // 0xcb : CB Prefix (IX bit instructions) + 0xCB: func(s *Z80Type) { + s.opcodeDDCB() + }, + // 0xe1 : POP IX + 0xE1: func(s *Z80Type) { + s.IX = s.PopWord() + }, + // 0xe3 : EX (SP), IX + 0xE3: func(s *Z80Type) { + temp := s.IX + s.IX = uint16(s.core.MemRead(s.SP)) + s.IX |= uint16(s.core.MemRead(s.SP+1)) << 8 + s.core.MemWrite(s.SP, byte(temp&0x00ff)) + s.core.MemWrite(s.SP+1, byte(temp>>8)) + }, + // 0xe5 : PUSH IX + 0xE5: func(s *Z80Type) { + s.pushWord(s.IX) + }, + // 0xe9 : JP (IX) + 0xE9: func(s *Z80Type) { + s.PC = s.IX - 1 + }, + // 0xf9 : LD SP, IX + 0xf9: func(s *Z80Type) { + s.SP = s.IX + }, +} + +// ===================================================== +func (z *Z80Type) getOffset(reg uint16) uint16 { + z.PC++ + offset := z.core.MemRead(z.PC) + if offset < 0 { + reg -= uint16(-offset) + } else { + reg += uint16(offset) + } + return reg +} + +func (z *Z80Type) opcodeDD() { + z.R = (z.R & 0x80) | (((z.R & 0x7f) + 1) & 0x7f) + z.PC++ + opcode := z.core.M1MemRead(z.PC) + + fun := ddInstructions[opcode] + if fun != nil { + //func = func.bind(this); + fun(z) + z.CycleCounter += CycleCountsDd[opcode] + } else { + // Apparently if a DD opcode doesn't exist, + // it gets treated as an unprefixed opcode. + // What we'll do to handle that is just back up the + // program counter, so that this byte gets decoded + // as a normal instruction. + z.PC-- + // And we'll add in the cycle count for a NOP. + z.CycleCounter += CycleCounts[0] + } +} + +func (z *Z80Type) opcodeDDCB() { + offset := z.getOffset(z.IX) + z.PC++ + + opcode := z.core.M1MemRead(z.PC) + value := byte(0) + bitTestOp := false + // As with the "normal" CB prefix, we implement the DDCB prefix + // by decoding the opcode directly, rather than using a table. + if opcode < 0x40 { + // Shift and rotate instructions. + + ddcbFunctions := []OpShift{z.doRlc, z.doRrc, z.doRl, z.doRr, z.doSla, z.doSra, z.doSll, z.doSrl} + + // Most of the opcodes in this range are not valid, + // so we map this opcode onto one of the ones that is. + + fun := ddcbFunctions[(opcode&0x38)>>3] + value = fun(z.core.MemRead(offset)) + z.core.MemWrite(offset, value) + } else { + bitNumber := (opcode & 0x38) >> 3 + + if opcode < 0x80 { + // BIT + bitTestOp = true + z.Flags.N = false + z.Flags.H = true + z.Flags.Z = z.core.MemRead(offset)&(1<> 4) | (temp2 << 4) + s.A = (s.A & 0xf0) | temp1 + s.core.MemWrite(s.hl(), hlValue) + s.Flags.S = s.A&0x80 != 0 + s.Flags.Z = s.A == 0 + s.Flags.H = false + s.Flags.P = ParityBits[s.A] + s.Flags.N = false + s.updateXYFlags(s.A) + + }, + // 0x68 : IN L, (C) + 0x68: func(s *Z80Type) { + s.L = s.doIn(s.bc()) + }, + // 0x69 : OUT (C), L + 0x69: func(s *Z80Type) { + s.core.IOWrite(s.bc(), s.L) + }, + // 0x6a : ADC HL, HL + 0x6A: func(s *Z80Type) { + s.doHlAdc(s.hl()) + }, + // 0x6b : LD HL, (nn) (Undocumented) + 0x6B: func(s *Z80Type) { + addr := s.getAddr() + s.L = s.core.MemRead(addr) + s.H = s.core.MemRead(addr + 1) + }, + // 0x6C : NEG (Undocumented) + 0x6C: func(s *Z80Type) { + s.doNeg() + }, + // 0x6D : RETN + 0x6D: func(s *Z80Type) { + s.PC = s.PopWord() - 1 + s.Iff1 = s.Iff2 + }, + // 0x6E : IM 0 + 0x6E: func(s *Z80Type) { + s.IMode = 0 + }, + // 0x6f : RLD + 0x6F: func(s *Z80Type) { + hlValue := s.core.MemRead(s.hl()) + temp1 := hlValue & 0xf0 + temp2 := s.A & 0x0f + hlValue = ((hlValue & 0x0f) << 4) | temp2 + s.A = (s.A & 0xf0) | (temp1 >> 4) + s.core.MemWrite(s.hl(), hlValue) + + s.Flags.S = s.A&0x80 != 0 + s.Flags.Z = s.A == 0 + s.Flags.H = false + s.Flags.P = ParityBits[s.A] + s.Flags.N = false + s.updateXYFlags(s.A) + }, + // 0x70 : INF + 0x70: func(s *Z80Type) { + s.doIn(s.bc()) + }, + // 0x71 : OUT (C), 0 (Undocumented) + 0x71: func(s *Z80Type) { + s.core.IOWrite(s.bc(), 0) + }, + // 0x72 : SBC HL, SP + 0x72: func(s *Z80Type) { + s.doHlSbc(s.SP) + }, + // 0x73 : LD (nn), SP + 0x73: func(s *Z80Type) { + addr := s.getAddr() + s.core.MemWrite(addr, byte(s.SP&0x00ff)) + s.core.MemWrite(addr+1, byte(s.SP>>8)) + }, + // 0x74 : NEG (Undocumented) + 0x74: func(s *Z80Type) { + s.doNeg() + }, + // 0x75 : RETN + 0x75: func(s *Z80Type) { + s.PC = s.PopWord() - 1 + s.Iff1 = s.Iff2 + }, + // 0x76 : IM 1 + 0x76: func(s *Z80Type) { + s.IMode = 1 + }, + // 0x78 : IN A, (C) + 0x78: func(s *Z80Type) { + s.A = s.core.IORead(s.bc()) + }, + // 0x79 : OUT (C), A + 0x79: func(s *Z80Type) { + s.core.IOWrite(s.bc(), s.A) + }, + // 0x7a : ADC HL, SP + 0x7A: func(s *Z80Type) { + s.doHlAdc(s.SP) + }, + // 0x7b : LD SP, (nn) + 0x7B: func(s *Z80Type) { + addr := s.getAddr() + s.SP = uint16(s.core.MemRead(addr)) + s.SP |= uint16(s.core.MemRead(addr+1)) << 8 + }, + // 0x7c : NEG (Undocumented) + 0x7C: func(s *Z80Type) { + s.doNeg() + }, + // 0x7d : RETN + 0x7D: func(s *Z80Type) { + s.PC = s.PopWord() - 1 + s.Iff1 = s.Iff2 + }, + // 0x7e : IM 2 + 0x7E: func(s *Z80Type) { + s.IMode = 2 + }, + // 0xa0 : LDI + 0xA0: func(s *Z80Type) { + s.doLdi() + }, + // 0xa1 : CPI + 0xA1: func(s *Z80Type) { + s.doCpi() + }, + // 0xa2 : INI + 0xA2: func(s *Z80Type) { + s.doIni() + }, + // 0xa3 : OUTI + 0xA3: func(s *Z80Type) { + s.doOuti() + }, + // 0xa8 : LDD + 0xA8: func(s *Z80Type) { + s.doLdd() + }, + // 0xa9 : CPD + 0xA9: func(s *Z80Type) { + s.doCpd() + }, + // 0xaa : IND + 0xAA: func(s *Z80Type) { + s.doInd() + }, + // 0xab : OUTD + 0xAB: func(s *Z80Type) { + s.doOutd() + }, + // 0xb0 : LDIR + 0xB0: func(s *Z80Type) { + s.doLdi() + if (s.B | s.C) != 0 { + s.CycleCounter += 5 + s.PC -= 2 + } + }, + // 0xb1 : CPIR + 0xB1: func(s *Z80Type) { + s.doCpi() + if !s.Flags.Z && (s.B|s.C) != 0 { + s.CycleCounter += 5 + s.PC -= 2 + } + }, + // 0xb2 : INIR + 0xB2: func(s *Z80Type) { + s.doIni() + if s.B != 0 { + s.CycleCounter += 5 + s.PC -= 2 + } + }, + // 0xb3 : OTIR + 0xB3: func(s *Z80Type) { + s.doOuti() + if s.B != 0 { + s.CycleCounter += 5 + s.PC -= 2 + } + }, + // 0xb8 : LDDR + 0xB8: func(s *Z80Type) { + s.doLdd() + if (s.B | s.C) != 0 { + s.CycleCounter += 5 + s.PC -= 2 + } + }, + // 0xb9 : CPDR + 0xB9: func(s *Z80Type) { + s.doCpd() + if !s.Flags.Z && (s.B|s.C) != 0 { + s.CycleCounter += 5 + s.PC -= 2 + } + }, + // 0xba : INDR + 0xBA: func(s *Z80Type) { + s.doInd() + if s.B != 0 { + s.CycleCounter += 5 + s.PC -= 2 + } + }, + // 0xbb : OTDR + 0xBB: func(s *Z80Type) { + s.doOutd() + if s.B != 0 { + s.CycleCounter += 5 + s.PC -= 2 + } + }, +} + +func (z *Z80Type) opcodeED() { + z.R = (z.R & 0x80) | (((z.R & 0x7f) + 1) & 0x7f) + z.PC++ + + opcode := z.core.M1MemRead(z.PC) + + fun := edInstructions[opcode] + if fun != nil { + fun(z) + z.CycleCounter += CycleCountsEd[opcode] + } else { + z.PC-- + z.CycleCounter += CycleCounts[0] + } + +} diff --git a/z80em/z80em.go b/z80em/z80em.go index 516e595..3a2220b 100644 --- a/z80em/z80em.go +++ b/z80em/z80em.go @@ -1,5 +1,7 @@ package z80em +import "fmt" + const SpDefault uint16 = 0xdff0 // FlagsType - Processor flags @@ -14,8 +16,8 @@ type FlagsType struct { C bool } -// StateType - Processor state -type StateType struct { +// Z80Type - Processor state +type Z80Type struct { A byte B byte C byte @@ -30,8 +32,8 @@ type StateType struct { EPrime byte HPrime byte LPrime byte - IX byte - IY byte + IX uint16 + IY uint16 I byte R byte SP uint16 @@ -63,108 +65,104 @@ type MemIoRW interface { IOWrite(port uint16, val byte) } -type Z80em interface { +type CPUInterface interface { // Reset CPU to initial state Reset() // RunInstruction Run single instruction, return number of CPU cycles RunInstruction() byte // GetState Get current CPU state - GetState() *StateType + GetState() *Z80Type // SetState Set current CPU state - SetState(state *StateType) - // setFlagsRegister Set value for CPU flags by specified byte [7:0] = SZYHXPNC - setFlagsRegister(flags byte) - - decodeInstruction(opcode byte) byte - pushWord(pc uint16) + SetState(state *Z80Type) } -func (s StateType) Reset() { - s.A = 0 - s.R = 0 - s.SP = SpDefault - s.PC = 0 - s.setFlagsRegister(0) +func (z *Z80Type) Reset() { + z.A = 0 + z.R = 0 + z.SP = SpDefault + z.PC = 0 + z.setFlagsRegister(0) // Interrupts disabled - s.IMode = 0 - s.Iff1 = 0 - s.Iff2 = 0 - s.interruptOccurred = false + z.IMode = 0 + z.Iff1 = 0 + z.Iff2 = 0 + z.interruptOccurred = false // Start not halted - s.Halted = false - s.DoDelayedDI = false - s.DoDelayedEI = false + z.Halted = false + z.DoDelayedDI = false + z.DoDelayedEI = false // no cycles - s.CycleCounter = 0 + z.CycleCounter = 0 + fmt.Println("CPUInterface State Reset") } -func (s StateType) GetState() *StateType { - return &StateType{ - A: s.A, - B: s.B, - C: s.C, - D: s.D, - E: s.E, - H: s.H, - L: s.L, - APrime: s.APrime, - BPrime: s.BPrime, - CPrime: s.CPrime, - DPrime: s.DPrime, - EPrime: s.EPrime, - HPrime: s.HPrime, - IX: s.IX, - IY: s.IY, - R: s.R, - SP: s.SP, - PC: s.PC, - Flags: s.Flags, - FlagsPrime: s.FlagsPrime, - IMode: s.IMode, - Iff1: s.Iff1, - Iff2: s.Iff2, - Halted: s.Halted, - DoDelayedDI: s.DoDelayedDI, - DoDelayedEI: s.DoDelayedEI, - CycleCounter: s.CycleCounter, +func (z *Z80Type) GetState() *Z80Type { + return &Z80Type{ + A: z.A, + B: z.B, + C: z.C, + D: z.D, + E: z.E, + H: z.H, + L: z.L, + APrime: z.APrime, + BPrime: z.BPrime, + CPrime: z.CPrime, + DPrime: z.DPrime, + EPrime: z.EPrime, + HPrime: z.HPrime, + IX: z.IX, + IY: z.IY, + R: z.R, + SP: z.SP, + PC: z.PC, + Flags: z.Flags, + FlagsPrime: z.FlagsPrime, + IMode: z.IMode, + Iff1: z.Iff1, + Iff2: z.Iff2, + Halted: z.Halted, + DoDelayedDI: z.DoDelayedDI, + DoDelayedEI: z.DoDelayedEI, + CycleCounter: z.CycleCounter, } } -func (s StateType) SetState(state *StateType) { - s.A = state.A - s.B = state.B - s.C = state.C - s.D = state.D - s.E = state.E - s.H = state.H - s.L = state.L - s.APrime = state.APrime - s.BPrime = state.BPrime - s.CPrime = state.CPrime - s.DPrime = state.DPrime - s.EPrime = state.EPrime - s.HPrime = state.HPrime - s.IX = state.IX - s.IY = state.IY - s.I = state.I - s.R = state.R - s.SP = state.SP - s.PC = state.PC - s.Flags = state.Flags - s.FlagsPrime = state.FlagsPrime - s.IMode = state.IMode - s.Iff1 = state.Iff1 - s.Iff2 = state.Iff2 - s.Halted = state.Halted - s.DoDelayedDI = state.DoDelayedDI - s.DoDelayedEI = state.DoDelayedEI - s.CycleCounter = state.CycleCounter +func (z *Z80Type) SetState(state *Z80Type) { + z.A = state.A + z.B = state.B + z.C = state.C + z.D = state.D + z.E = state.E + z.H = state.H + z.L = state.L + z.APrime = state.APrime + z.BPrime = state.BPrime + z.CPrime = state.CPrime + z.DPrime = state.DPrime + z.EPrime = state.EPrime + z.HPrime = state.HPrime + z.IX = state.IX + z.IY = state.IY + z.I = state.I + z.R = state.R + z.SP = state.SP + z.PC = state.PC + z.Flags = state.Flags + z.FlagsPrime = state.FlagsPrime + z.IMode = state.IMode + z.Iff1 = state.Iff1 + z.Iff2 = state.Iff2 + z.Halted = state.Halted + z.DoDelayedDI = state.DoDelayedDI + z.DoDelayedEI = state.DoDelayedEI + z.CycleCounter = state.CycleCounter } // New Create new -func New(memIoRW MemIoRW) *StateType { - return &StateType{ +func New(memIoRW MemIoRW) *Z80Type { + return &Z80Type{ A: 0, B: 0, C: 0, @@ -199,55 +197,55 @@ func New(memIoRW MemIoRW) *StateType { } } -func (s StateType) RunInstruction() byte { +func (z *Z80Type) RunInstruction() byte { // R is incremented at the start of every instruction cycle, // before the instruction actually runs. // The high bit of R is not affected by this increment, // it can only be changed using the LD R, A instruction. // Note: also a HALT does increment the R register. - s.R = (s.R & 0x80) | (((s.R & 0x7f) + 1) & 0x7f) + z.R = (z.R & 0x80) | (((z.R & 0x7f) + 1) & 0x7f) - if !s.Halted { + if !z.Halted { // If the previous instruction was a DI or an EI, // we'll need to disable or enable interrupts // after whatever instruction we're about to run is finished. doingDelayedDi := false doingDelayedEi := false - if s.DoDelayedDI { - s.DoDelayedDI = false + if z.DoDelayedDI { + z.DoDelayedDI = false doingDelayedDi = true - } else if s.DoDelayedEI { - s.DoDelayedEI = false + } else if z.DoDelayedEI { + z.DoDelayedEI = false doingDelayedEi = true } // Read the byte at the PC and run the instruction it encodes. - opcode := s.core.M1MemRead(s.PC) - s.decodeInstruction(opcode) + opcode := z.core.M1MemRead(z.PC) + z.decodeInstruction(opcode) // HALT does not increase the PC - if !s.Halted { - s.PC++ + if !z.Halted { + z.PC++ } // Actually do the delayed interrupt disable/enable if we have one. if doingDelayedDi { - s.Iff1 = 0 - s.Iff2 = 0 + z.Iff1 = 0 + z.Iff2 = 0 } else if doingDelayedEi { - s.Iff1 = 1 - s.Iff2 = 1 + z.Iff1 = 1 + z.Iff2 = 1 } // And finally clear out the cycle counter for the next instruction // before returning it to the emulator core. - cycleCounter := s.CycleCounter - s.CycleCounter = 0 + cycleCounter := z.CycleCounter + z.CycleCounter = 0 return cycleCounter } else { // HALTED // During HALT, NOPs are executed which is 4T - s.core.M1MemRead(s.PC) // HALT does a normal M1 fetch to keep the memory refresh active. The result is ignored (NOP). + z.core.M1MemRead(z.PC) // HALT does a normal M1 fetch to keep the memory refresh active. The result is ignored (NOP). return 4 } } @@ -256,207 +254,179 @@ func (s StateType) RunInstruction() byte { // // nonMaskable - true if this is a non-maskable interrupt // data - the value to be placed on the data bus, if needed -func (s StateType) interrupt(nonMaskable bool, data byte) { +func (z *Z80Type) interrupt(nonMaskable bool, data byte) { if nonMaskable { // An interrupt, if halted, does increase the PC - if s.Halted { - s.PC++ + if z.Halted { + z.PC++ } // The high bit of R is not affected by this increment, // it can only be changed using the LD R, A instruction. - s.R = (s.R & 0x80) | (((s.R & 0x7f) + 1) & 0x7f) + z.R = (z.R & 0x80) | (((z.R & 0x7f) + 1) & 0x7f) // Non-maskable interrupts are always handled the same way; // clear IFF1 and then do a CALL 0x0066. // Also, all interrupts reset the HALT state. - s.Halted = false - s.Iff2 = s.Iff1 - s.Iff1 = 0 - s.pushWord(s.PC) - s.PC = 0x66 + z.Halted = false + z.Iff2 = z.Iff1 + z.Iff1 = 0 + z.pushWord(z.PC) + z.PC = 0x66 - s.CycleCounter += 11 - } else if s.Iff1 != 0 { + z.CycleCounter += 11 + } else if z.Iff1 != 0 { // An interrupt, if halted, does increase the PC - if s.Halted { - s.PC++ + if z.Halted { + z.PC++ } // The high bit of R is not affected by this increment, // it can only be changed using the LD R,A instruction. - s.R = (s.R & 0x80) | (((s.R & 0x7f) + 1) & 0x7f) + z.R = (z.R & 0x80) | (((z.R & 0x7f) + 1) & 0x7f) - s.Halted = false - s.Iff1 = 0 - s.Iff2 = 0 + z.Halted = false + z.Iff1 = 0 + z.Iff2 = 0 - if s.IMode == 0 { + if z.IMode == 0 { // In the 8080-compatible interrupt mode, // decode the content of the data bus as an instruction and run it. - // it's probably a RST instruction, which pushes (PC+1) onto the stack + // it'z probably a RST instruction, which pushes (PC+1) onto the stack // so we should decrement PC before we decode the instruction - s.PC-- - s.decodeInstruction(data) - s.PC++ // increment PC upon return - s.CycleCounter += 2 - } else if s.IMode == 1 { + z.PC-- + z.decodeInstruction(data) + z.PC++ // increment PC upon return + z.CycleCounter += 2 + } else if z.IMode == 1 { // Mode 1 is always just RST 0x38. - s.pushWord(s.PC) - s.PC = 0x0038 - s.CycleCounter += 13 - } else if s.IMode == 2 { + z.pushWord(z.PC) + z.PC = 0x0038 + z.CycleCounter += 13 + } else if z.IMode == 2 { // Mode 2 uses the value on the data bus as in index // into the vector table pointer to by the I register. - s.pushWord(s.PC) + z.pushWord(z.PC) // The Z80 manual says that this address must be 2-byte aligned, // but it doesn't appear that this is actually the case on the hardware, // so we don't attempt to enforce that here. - vectorAddress := (uint16(s.I) << 8) | uint16(data) - s.PC = uint16(s.core.MemRead(vectorAddress)) | (uint16(s.core.MemRead(vectorAddress+1)) << 8) - s.CycleCounter += 19 + vectorAddress := (uint16(z.I) << 8) | uint16(data) + z.PC = uint16(z.core.MemRead(vectorAddress)) | (uint16(z.core.MemRead(vectorAddress+1)) << 8) + z.CycleCounter += 19 // A "notification" is generated so that the calling program can break on it. - s.interruptOccurred = true + z.interruptOccurred = true } } } -func (s StateType) pushWord(pc uint16) { - // TODO: Implement - panic("not yet implemented") +func (z *Z80Type) pushWord(operand uint16) { + z.SP-- + z.core.MemWrite(z.SP, byte(operand>>8)) + z.SP-- + z.core.MemWrite(z.SP, byte(operand&0x00ff)) } -func (s StateType) getOperand(opcode byte) byte { +func (z *Z80Type) getOperand(opcode byte) byte { switch opcode & 0x07 { case 0: - return s.B + return z.B case 1: - return s.C + return z.C case 2: - return s.D + return z.D case 3: - return s.E + return z.E case 4: - return s.H + return z.H case 5: - return s.L + return z.L case 6: - return s.core.MemRead(uint16(s.H)<<8 | uint16(s.L)) + return z.core.MemRead(uint16(z.H)<<8 | uint16(z.L)) default: - return s.A + return z.A } } -func (s StateType) decodeInstruction(opcode byte) { +func (z *Z80Type) decodeInstruction(opcode byte) { // Handle HALT right up front, because it fouls up our LD decoding // by falling where LD (HL), (HL) ought to be. - if opcode == OP_HALT { - s.Halted = true - } else if opcode >= OP_LD_B_B && opcode < OP_ADD_A_B { + if opcode == OpHalt { + z.Halted = true + } else if opcode >= OpLdBB && opcode < OpAddAB { // This entire range is all 8-bit register loads. // Get the operand and assign it to the correct destination. - s.load8bit(opcode, s.getOperand(opcode)) - } else if (opcode >= OP_ADD_A_B) && (opcode < OP_RET_NZ) { + z.load8bit(opcode, z.getOperand(opcode)) + } else if (opcode >= OpAddAB) && (opcode < OpRetNz) { // These are the 8-bit register ALU instructions. // We'll get the operand and then use this "jump table" // to call the correct utility function for the instruction. - s.alu8bit(opcode, s.getOperand(opcode)) + z.alu8bit(opcode, z.getOperand(opcode)) } else { // This is one of the less formulaic instructions; // we'll get the specific function for it from our array. - s.otherInstructions(opcode) + fun := instructions[opcode] + fun(z) + //z.otherInstructions(opcode) } - s.CycleCounter += CYCLE_COUNTS[opcode] + z.CycleCounter += CycleCounts[opcode] } -func (s StateType) load8bit(opcode byte, operand byte) { +func (z *Z80Type) load8bit(opcode byte, operand byte) { switch (opcode & 0x38) >> 3 { case 0: - s.B = operand + z.B = operand case 1: - s.C = operand + z.C = operand case 2: - s.D = operand + z.D = operand case 3: - s.E = operand + z.E = operand case 4: - s.H = operand + z.H = operand case 5: - s.L = operand + z.L = operand case 6: - s.core.MemWrite(uint16(s.H)<<8|uint16(s.L), operand) + z.core.MemWrite(uint16(z.H)<<8|uint16(z.L), operand) default: - s.A = operand + z.A = operand } } -func (s StateType) alu8bit(opcode byte, operand byte) { +func (z *Z80Type) alu8bit(opcode byte, operand byte) { switch (opcode & 0x38) >> 3 { case 0: - s.doAdd(operand) + z.doAdd(operand) case 1: - s.doAdc(operand) + z.doAdc(operand) case 2: - s.doSub(operand) + z.doSub(operand) case 3: - s.doSbc(operand) + z.doSbc(operand) case 4: - s.doAnd(operand) + z.doAnd(operand) case 5: - s.doXor(operand) + z.doXor(operand) case 6: - s.doOr(operand) + z.doOr(operand) default: - s.doCp(operand) + z.doCp(operand) } } -func (s StateType) doAdd(operand byte) { - -} - -func (s StateType) doAdc(operand byte) { - -} - -func (s StateType) doSub(operand byte) { - -} - -func (s StateType) doSbc(operand byte) { - -} - -func (s StateType) doAnd(operand byte) { - -} - -func (s StateType) doXor(operand byte) { - -} - -func (s StateType) doOr(operand byte) { - -} - -func (s StateType) doCp(operand byte) { - -} - -func (s StateType) otherInstructions(opcode byte) { +func (z *Z80Type) otherInstructions(opcode byte) { } // getFlagsRegister return whole F register -func (s StateType) getFlagsRegister() byte { - return getFlags(&s.Flags) +func (z *Z80Type) getFlagsRegister() byte { + return getFlags(&z.Flags) } // getFlagsRegister return whole F' register -func (s StateType) getFlagsPrimeRegister() byte { - return getFlags(&s.FlagsPrime) +func (z *Z80Type) getFlagsPrimeRegister() byte { + return getFlags(&z.FlagsPrime) } func getFlags(f *FlagsType) byte { @@ -495,12 +465,12 @@ func getFlags(f *FlagsType) byte { } -func (s StateType) setFlagsRegister(flags byte) { - setFlags(flags, &s.Flags) +func (z *Z80Type) setFlagsRegister(flags byte) { + setFlags(flags, &z.Flags) } -func (s StateType) setFlagsPrimeRegister(flags byte) { - setFlags(flags, &s.FlagsPrime) +func (z *Z80Type) setFlagsPrimeRegister(flags byte) { + setFlags(flags, &z.FlagsPrime) } func setFlags(flags byte, f *FlagsType) { @@ -514,37 +484,602 @@ func setFlags(flags byte, f *FlagsType) { f.C = flags&0x01 != 0 } -func (s StateType) updateXYFlags(result byte) { +func (z *Z80Type) updateXYFlags(result byte) { // Most of the time, the undocumented flags // (sometimes called X and Y, or 3 and 5), // take their values from the corresponding bits // of the result of the instruction, // or from some other related value. // This is a utility function to set those flags based on those bits. - s.Flags.Y = (result&0x20)>>5 != 0 - s.Flags.X = (result&0x08)>>3 != 0 + z.Flags.Y = (result&0x20)>>5 != 0 + z.Flags.X = (result&0x08)>>3 != 0 } func getParity(value byte) bool { - return PARITY_BITS[value] + return ParityBits[value] } -func (s StateType) PushWord(operand uint16) { - // Pretty obvious what this function does; given a 16-bit value, - // decrement the stack pointer, write the high byte to the new - // stack pointer location, then repeat for the low byte. - s.SP-- - s.core.MemWrite(s.SP, byte((operand&0xff00)>>8)) - s.SP-- - s.core.MemWrite(s.SP, byte(operand&0x00ff)) +// PushWord - Decrement stack pointer and put specified word value to stack +func (z *Z80Type) PushWord(operand uint16) { + z.SP-- + z.core.MemWrite(z.SP, byte((operand&0xff00)>>8)) + z.SP-- + z.core.MemWrite(z.SP, byte(operand&0x00ff)) } -func (s StateType) PopWord() uint16 { - // Again, not complicated; read a byte off the top of the stack, - // increment the stack pointer, rinse and repeat. - result := uint16(s.core.MemRead(s.SP)) - s.SP++ - result |= uint16(s.core.MemRead(s.SP)) << 8 - s.SP++ +// PopWord - Get word value from top of stack and increment stack pointer +func (z *Z80Type) PopWord() uint16 { + result := uint16(z.core.MemRead(z.SP)) + z.SP++ + result |= uint16(z.core.MemRead(z.SP)) << 8 + z.SP++ return result } + +// doConditionalAbsoluteJump - Implements the JP [condition],nn instructions. +func (z *Z80Type) doConditionalAbsoluteJump(condition bool) { + if condition { + // We're taking this jump, so write the new PC, + // and then decrement the thing we just wrote, + // because the instruction decoder increments the PC + // unconditionally at the end of every instruction, + // and we need to counteract that so we end up at the jump target. + // TODO: Check for increment CycleCounter + z.PC = uint16(z.core.MemRead(z.PC+1)) | (uint16(z.core.MemRead(z.PC+2)) << 8) + z.PC-- + } else { + // We're not taking this jump, just move the PC past the operand. + z.PC += 2 + } +} + +// doConditionalRelativeJump - Implements the JR [condition],nn instructions. +func (z *Z80Type) doConditionalRelativeJump(condition bool) { + if condition { + // We need a few more cycles to actually take the jump. + z.CycleCounter += 5 + // Calculate the offset specified by our operand. + offset := z.core.MemRead(z.PC + 1) + + // Add the offset to the PC, also skipping past this instruction. + if offset < 0 { + z.PC = z.PC - uint16(-offset) + } else { + z.PC = z.PC + uint16(offset) + } + + } + z.PC++ +} + +// doConditionalCall - Implements CALL [condition],nn instructions. +func (z *Z80Type) doConditionalCall(condition bool) { + if condition { + z.CycleCounter += 7 + z.PushWord(z.PC + 3) + z.PC = uint16(z.core.MemRead(z.PC+1)) | (uint16(z.core.MemRead(z.PC+2)) << 8) + z.PC-- + } else { + z.PC += 2 + } +} + +func (z *Z80Type) doConditionalReturn(condition bool) { + if condition { + z.CycleCounter += 6 + z.PC = z.PopWord() - 1 + } +} + +// doReset - Implements RST [address] instructions. +func (z *Z80Type) doReset(address uint16) { + z.PushWord(z.PC + 1) + z.PC = address - 1 +} + +func (z *Z80Type) setBaseFlags(operand byte, result uint16) { + z.Flags.C = result > 0x00ff + z.Flags.S = result&0x80 != 0 + z.Flags.Z = result&0xff == 0 + z.Flags.H = (((operand & 0x0f) + (z.A & 0x0f)) & 0x10) != 0 + // An overflow has happened if the sign bits of the accumulator and the operand + // don't match the sign bit of the result value. + z.Flags.P = ((z.A & 0x80) == (operand & 0x80)) && (z.A&0x80 != byte(result&0x80)) +} + +func (z *Z80Type) doAdd(operand byte) { + // This is the ADD A, [operand] instructions. + // We'll do the literal addition, which includes any overflow, + // so that we can more easily figure out whether we had + // an overflow or a carry and set the flags accordingly. + var result = uint16(z.A) + uint16(operand) + z.A = byte(result & 0xff) + z.setBaseFlags(operand, result) + z.Flags.N = false + z.updateXYFlags(z.A) +} + +func (z *Z80Type) doAdc(operand byte) { + var result = uint16(z.A) + uint16(operand) + if z.Flags.C { + result++ + } + z.A = byte(result & 0xff) + z.setBaseFlags(operand, result) + z.Flags.N = false + z.updateXYFlags(z.A) +} + +func (z *Z80Type) doSub(operand byte) { + var result = uint16(z.A) - uint16(operand) + z.A = byte(result & 0xff) + z.setBaseFlags(operand, result) + z.Flags.N = true + z.updateXYFlags(z.A) +} + +func (z *Z80Type) doSbc(operand byte) { + var result = uint16(z.A) - uint16(operand) + if z.Flags.C { + result-- + } + z.A = byte(result & 0xff) + z.setBaseFlags(operand, result) + z.Flags.N = true + z.updateXYFlags(z.A) +} + +func (z *Z80Type) setLogicFlags() { + z.Flags.S = z.A&0x80 != 0 + z.Flags.Z = z.A == 0 + z.Flags.H = true + z.Flags.P = ParityBits[z.A] + z.Flags.N = false + z.Flags.C = false +} + +func (z *Z80Type) doAnd(operand byte) { + z.A &= operand + z.setLogicFlags() + z.updateXYFlags(z.A) +} + +func (z *Z80Type) doXor(operand byte) { + z.A ^= operand + z.setLogicFlags() + z.updateXYFlags(z.A) +} + +func (z *Z80Type) doOr(operand byte) { + z.A |= operand + z.setLogicFlags() + z.updateXYFlags(z.A) +} + +func (z *Z80Type) doCp(operand byte) { + tmp := z.A + z.doSub(operand) + z.A = tmp + z.updateXYFlags(operand) +} + +func (z *Z80Type) doInc(operand byte) byte { + var result = uint16(operand) + 1 + r8 := byte(result & 0xff) + z.Flags.S = r8&0x80 > 0 + z.Flags.Z = r8 == 0 + z.Flags.H = (operand & 0x0f) == 0x0f + // It'z a good deal easier to detect overflow for an increment/decrement. + z.Flags.P = operand == 0x7f + z.Flags.N = false + z.updateXYFlags(r8) + return r8 +} + +func (z *Z80Type) doDec(operand byte) byte { + var result = uint16(operand) - 1 + r8 := byte(result & 0xff) + z.Flags.S = r8&0x80 > 0 + z.Flags.Z = r8 == 0 + z.Flags.H = (operand & 0x0f) == 0x00 + z.Flags.P = operand == 0x80 + z.Flags.N = true + z.updateXYFlags(r8) + return r8 +} + +func (z *Z80Type) doHlAdd(operand uint16) { + // The HL arithmetic instructions are the same as the A ones, + // just with twice as many bits happening. + hl := uint16(z.L) | (uint16(z.H) << 8) + result := uint32(hl) + uint32(operand) + z.Flags.N = false + z.Flags.C = result > 0xffff + z.Flags.H = ((hl&0x0fff)+(operand&0x0fff))&0x1000 > 0 + + z.L = byte(result & 0xff) + z.H = byte((result & 0xff00) >> 8) + + z.updateXYFlags(z.H) +} + +func (z *Z80Type) doHlAdc(operand uint16) { + if z.Flags.C { + operand++ + } + + hl := uint16(z.L) | (uint16(z.H) << 8) + result := uint32(hl) + uint32(operand) + + z.Flags.S = (result & 0x8000) != 0 + z.Flags.Z = result&0xffff == 0 + z.Flags.H = (((hl & 0x0fff) + (operand & 0x0fff)) & 0x1000) != 0 + z.Flags.P = ((hl & 0x8000) == (operand & 0x8000)) && (uint16(result&0x8000) != (hl & 0x8000)) + z.Flags.N = false + z.Flags.C = result > 0xffff + + z.L = byte(result & 0xff) + z.H = byte((result & 0xff00) >> 8) + + z.updateXYFlags(z.H) +} + +func (z *Z80Type) doHlSbc(operand uint16) { + if z.Flags.C { + operand++ + } + + hl := uint16(z.L) | (uint16(z.H) << 8) + result := uint32(hl) - uint32(operand) + + z.Flags.S = (result & 0x8000) != 0 + z.Flags.Z = result&0xffff == 0 + z.Flags.H = (((hl & 0x0fff) - (operand & 0x0fff)) & 0x1000) != 0 + z.Flags.P = ((hl & 0x8000) != (operand & 0x8000)) && (uint16(result&0x8000) != (hl & 0x8000)) + z.Flags.N = true + z.Flags.C = result > 0xffff + + z.L = byte(result & 0xff) + z.H = byte((result & 0xff00) >> 8) + + z.updateXYFlags(z.H) +} + +func (z *Z80Type) doIn(port uint16) byte { + var result = z.core.IORead(port) + + z.Flags.S = result&0x80 != 0 + z.Flags.Z = result == 0 + z.Flags.H = false + z.Flags.P = ParityBits[result] + z.Flags.N = false + z.updateXYFlags(result) + return result +} + +func (z *Z80Type) doNeg() { + // This instruction is defined to not alter the register if it === 0x80. + a := int8(z.A) + if z.A != 0x80 { + // This is a signed operation, so convert A to a signed value. + z.A = byte(-a) + } + z.Flags.S = z.A&0x80 != 0 + z.Flags.Z = z.A == 0 + z.Flags.H = ((-a) & 0x0f) > 0 + z.Flags.P = z.A == 0x80 + z.Flags.N = true + z.Flags.C = z.A != 0 + z.updateXYFlags(z.A) +} + +func (z *Z80Type) doLdi() { + // Copy the value that we're supposed to copy. + hl := uint16(z.L) | (uint16(z.H) << 8) + de := uint16(z.E) | (uint16(z.D) << 8) + readValue := z.core.MemRead(hl) + z.core.MemWrite(de, readValue) + + z.incDe() + z.incHl() + z.decBc() + + z.Flags.H = false + z.Flags.P = (z.C | z.B) != 0 + z.Flags.N = false + z.Flags.Y = ((z.A+readValue)&0x02)>>1 != 0 + z.Flags.X = ((z.A+readValue)&0x08)>>3 != 0 +} + +func (z *Z80Type) doCpi() { + tempCarry := z.Flags.C + hl := uint16(z.L) | (uint16(z.H) << 8) + readValue := z.core.MemRead(hl) + z.doCp(readValue) + + z.Flags.C = tempCarry + var fh byte = 0 + if z.Flags.H { + fh = 1 + } + z.Flags.Y = ((z.A-readValue-fh)&0x02)>>1 != 0 + z.Flags.X = ((z.A-readValue-fh)&0x08)>>3 != 0 + + z.incHl() + z.decBc() + + z.Flags.P = (z.B | z.C) != 0 +} + +func (z *Z80Type) doIni() { + hl := (uint16(z.H) << 8) | uint16(z.L) + bc := (uint16(z.B) << 8) | uint16(z.C) + z.core.MemWrite(hl, z.core.IORead(bc)) + + z.incHl() + + z.B = z.doDec(z.B) + z.Flags.N = true +} + +func (z *Z80Type) doOuti() { + // Zilog pseudo code is wrong, see: https://github.com/maziac/z80-instruction-set/pull/10 + z.B = z.doDec(z.B) + hl := (uint16(z.H) << 8) | uint16(z.L) + bc := (uint16(z.B) << 8) | uint16(z.C) + z.core.IOWrite(bc, z.core.MemRead(hl)) + + z.incHl() + + z.Flags.N = true +} + +func (z *Z80Type) doLdd() { + z.Flags.N = false + z.Flags.H = false + + hl := (uint16(z.H) << 8) | uint16(z.L) + de := (uint16(z.D) << 8) | uint16(z.E) + + readValue := z.core.MemRead(hl) + z.core.MemWrite(de, readValue) + + z.decDe() + z.decHl() + z.decBc() + + z.Flags.P = (z.C | z.B) != 0 + z.Flags.Y = ((z.A+readValue)&0x02)>>1 != 0 + z.Flags.X = ((z.A+readValue)&0x08)>>3 != 0 +} + +func (z *Z80Type) doCpd() { + tempCarry := z.Flags.C + hl := uint16(z.L) | (uint16(z.H) << 8) + readValue := z.core.MemRead(hl) + z.doCp(readValue) + + z.Flags.C = tempCarry + + var fh byte = 0 + if z.Flags.H { + fh = 1 + } + + z.Flags.Y = ((z.A-readValue-fh)&0x02)>>1 != 0 + z.Flags.X = ((z.A-readValue-fh)&0x08)>>3 != 0 + + z.decHl() + z.decBc() + + z.Flags.P = (z.B | z.C) != 0 +} + +func (z *Z80Type) doInd() { + hl := (uint16(z.H) << 8) | uint16(z.L) + bc := (uint16(z.B) << 8) | uint16(z.C) + z.core.MemWrite(hl, z.core.IORead(bc)) + z.decHl() + z.B = z.doDec(z.B) + z.Flags.N = true +} + +func (z *Z80Type) doOutd() { + // Zilog pseudo code is wrong, see: https://github.com/maziac/z80-instruction-set/pull/10 + z.B = z.doDec(z.B) + hl := (uint16(z.H) << 8) | uint16(z.L) + bc := (uint16(z.B) << 8) | uint16(z.C) + z.core.IOWrite(bc, z.core.MemRead(hl)) + z.decHl() + z.Flags.N = true +} + +type OpShift func(operand byte) byte + +func (z *Z80Type) doRlc(operand byte) byte { + z.Flags.C = operand&0x80 != 0 + var fc byte = 0 + if z.Flags.C { + fc = 1 + } + operand = (operand << 1) | fc + z.setShiftFlags(operand) + return operand +} + +func (z *Z80Type) doRrc(operand byte) byte { + z.Flags.C = operand&1 != 0 + var fc byte = 0 + if z.Flags.C { + fc = 0x80 + } + operand = ((operand >> 1) & 0x7f) | fc + z.setShiftFlags(operand) + return operand +} + +func (z *Z80Type) doRl(operand byte) byte { + var fc byte = 0 + if z.Flags.C { + fc = 1 + } + z.Flags.C = operand&0x80 != 0 + operand = (operand << 1) | fc + z.setShiftFlags(operand) + return operand +} + +func (z *Z80Type) doRr(operand byte) byte { + var fc byte = 0 + if z.Flags.C { + fc = 0x80 + } + z.Flags.C = operand&1 != 0 + operand = ((operand >> 1) & 0x7f) | fc + z.setShiftFlags(operand) + return operand +} + +func (z *Z80Type) doSla(operand byte) byte { + z.Flags.C = operand&0x80 != 0 + operand = operand << 1 + z.setShiftFlags(operand) + return operand +} + +func (z *Z80Type) doSra(operand byte) byte { + z.Flags.C = operand&1 != 0 + operand = ((operand >> 1) & 0x7f) | (operand & 0x80) + z.setShiftFlags(operand) + return operand +} + +func (z *Z80Type) doSll(operand byte) byte { + z.Flags.C = operand&0x80 != 0 + operand = (operand << 1) | 1 + z.setShiftFlags(operand) + return operand +} + +func (z *Z80Type) doSrl(operand byte) byte { + z.Flags.C = operand&1 != 0 + operand = (operand >> 1) & 0x7f + z.setShiftFlags(operand) + z.Flags.S = false + return operand +} + +func (z *Z80Type) doIxAdd(operand uint16) { + z.Flags.N = false + result := uint32(z.IX) + uint32(operand) + z.Flags.C = result > 0xffff + z.Flags.H = (((z.IX & 0xfff) + (operand & 0xfff)) & 0x1000) != 0 + + z.updateXYFlags(byte((result & 0xff00) >> 8)) + z.IX = uint16(result & 0xffff) +} + +func (z *Z80Type) setShiftFlags(operand byte) { + z.Flags.N = false + z.Flags.H = false + z.Flags.Z = operand == 0 + z.Flags.P = ParityBits[operand] + z.Flags.S = (operand & 0x80) != 0 + z.updateXYFlags(operand) +} + +//type Operation func() + +func (z *Z80Type) opcodeFD() { + z.R = (z.R & 0x80) | (((z.R & 0x7f) + 1) & 0x7f) + z.PC++ + opcode := z.core.M1MemRead(z.PC) + fun := ddInstructions[opcode] + if fun != nil { + // Rather than copy and paste all the IX instructions into IY instructions, + // what we'll do is sneakily copy IY into IX, run the IX instruction, + // and then copy the result into IY and restore the old IX. + var temp = z.IX + z.IX = z.IY + fun(z) + z.IY = z.IX + z.IX = temp + z.CycleCounter += CycleCountsDd[opcode] + } else { + z.PC-- + z.CycleCounter += CycleCounts[0] + } +} + +// ============== get register pairs + +func (z *Z80Type) bc() uint16 { + return (uint16(z.B) << 8) | uint16(z.C) +} + +func (z *Z80Type) de() uint16 { + return (uint16(z.D) << 8) | uint16(z.E) +} + +func (z *Z80Type) hl() uint16 { + return (uint16(z.H) << 8) | uint16(z.L) +} + +// ============ helper fn + +func (z *Z80Type) incBc() { + z.changeBc(+1) +} + +func (z *Z80Type) decBc() { + z.changeBc(-1) +} + +func (z *Z80Type) incHl() { + z.changeHl(+1) +} + +func (z *Z80Type) decHl() { + z.changeHl(-1) +} + +func (z *Z80Type) incDe() { + z.changeDe(+1) +} + +func (z *Z80Type) decDe() { + z.changeDe(-1) +} + +func (z *Z80Type) changeHl(val int8) { + hl := (uint16(z.H) << 8) | uint16(z.L) + if val < 0 { + hl-- + } else { + hl++ + } + z.L = byte(hl & 0xff) + z.H = byte((hl & 0xff00) >> 8) +} + +func (z *Z80Type) changeDe(val int8) { + de := (uint16(z.D) << 8) | uint16(z.E) + if val < 0 { + de-- + } else { + de++ + } + z.E = byte(de & 0xff) + z.D = byte((de & 0xff00) >> 8) +} + +func (z *Z80Type) changeBc(val int8) { + bc := (uint16(z.B) << 8) | uint16(z.C) + if val < 0 { + bc-- + } else { + bc++ + } + z.C = byte(bc & 0x00ff) + z.B = byte((bc & 0xff00) >> 8) +}