.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)
+}