Z80 basic impl completed

This commit is contained in:
Роман Бойков 2026-03-01 17:45:53 +03:00
parent 76829b80cc
commit 610423f85a
21 changed files with 3121 additions and 285 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
*.bak
*.lst
*.tmp
.idea/

9
.idea/z80em.iml generated
View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

76
config/config.go Normal file
View File

@ -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 <file_path>.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
}
}

7
go.mod
View File

@ -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

14
go.sum Normal file
View File

@ -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=

85
logger/logger.go Normal file
View File

@ -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)
}
}
}

32
main.go
View File

@ -1,21 +1,29 @@
package main
import (
"fmt"
"z80em/config"
"z80em/logger"
"z80em/okean240"
)
//TIP <p>To run your code, right-click the code and select <b>Run</b>.</p> <p>Alternatively, click
// the <icon src="AllIcons.Actions.Execute"/> icon in the gutter and select the <b>Run</b> menu item from here.</p>
var Version = "v1.0.0"
var BuildTime = "2026-03-01"
func main() {
//TIP <p>Press <shortcut actionId="ShowIntentionActions"/> when your caret is at the underlined text
// to see how GoLand suggests fixing the warning.</p><p>Alternatively, if available, click the lightbulb to view possible fixes.</p>
s := "gopher"
fmt.Printf("Hello and welcome, %s!\n", s)
for i := 1; i <= 5; i++ {
//TIP <p>To start your debugging session, right-click your code in the editor and select the Debug option.</p> <p>We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.</p>
fmt.Println("i =", 100/i)
}
// 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()
}

BIN
okean240/CPM_r7.BIN Normal file

Binary file not shown.

BIN
okean240/MON_r6.BIN Normal file

Binary file not shown.

75
okean240/computer.go Normal file
View File

@ -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())
}
}

154
okean240/ioports.go Normal file
View File

@ -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

126
okean240/memory.go Normal file
View File

@ -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
}

4
okemu.yml Normal file
View File

@ -0,0 +1,4 @@
logFile: "okemu.log"
logLevel: "info"
monitorFile: "okean240/MON_r6.BIN"
cpmFile: "okean240/CPM_r7.BIN"

14
rebuild.sh Executable file
View File

@ -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:

101
z80em/constants.go Normal file
View File

@ -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,
}

View File

@ -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,
}

657
z80em/opcode.go Normal file
View File

@ -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
}

106
z80em/opcodeCB.go Normal file
View File

@ -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<<bitNumber) == 0
case 1:
z.Flags.Z = z.C&(1<<bitNumber) == 0
case 2:
z.Flags.Z = z.D&(1<<bitNumber) == 0
case 3:
z.Flags.Z = z.E&(1<<bitNumber) == 0
case 4:
z.Flags.Z = z.H&(1<<bitNumber) == 0
case 5:
z.Flags.Z = z.L&(1<<bitNumber) == 0
case 6:
z.Flags.Z = z.core.MemRead(z.hl())&(1<<bitNumber) == 0
default:
z.Flags.Z = z.A&(1<<bitNumber) == 0
}
z.Flags.N = false
z.Flags.H = true
z.Flags.P = z.Flags.Z
z.Flags.S = (bitNumber == 7) && !z.Flags.Z
// For the BIT n, (HL) instruction, the X and Y flags are obtained
// from what is apparently an internal temporary register used for
// some of the 16-bit arithmetic instructions.
// I haven't implemented that register here,
// so for now we'll set X and Y the same way for every BIT opcode,
// which means that they will usually be wrong for BIT n, (HL).
z.Flags.Y = (bitNumber == 5) && !z.Flags.Z
z.Flags.X = (bitNumber == 3) && !z.Flags.Z
} else if opcode < 0xC0 {
// RES instructions
negMask := byte(^(1 << bitNumber))
switch regCode {
case 0:
z.B &= negMask
case 1:
z.C &= negMask
case 2:
z.D &= negMask
case 3:
z.E &= negMask
case 4:
z.H &= negMask
case 5:
z.L &= negMask
case 6:
z.core.MemWrite(z.hl(), z.core.MemRead(z.hl())&negMask)
default:
z.A &= negMask
}
} else {
// SET instructions
mask := byte(1 << bitNumber)
switch regCode {
case 0:
z.B |= mask
case 1:
z.C |= mask
case 2:
z.D |= mask
case 3:
z.E |= mask
case 4:
z.H |= mask
case 5:
z.L |= mask
case 6:
z.core.MemWrite(z.hl(), z.core.MemRead(z.hl())|mask)
default:
z.A |= mask
}
}
z.CycleCounter += CycleCountsCb[opcode]
}

483
z80em/opcodeDD.go Normal file
View File

@ -0,0 +1,483 @@
package z80em
var ddInstructions = []func(s *Z80Type){
// 0x09 : ADD IX, BC
0x09: func(s *Z80Type) {
s.doIxAdd(s.bc())
},
// 0x19 : ADD IX, DE
0x19: func(s *Z80Type) {
s.doIxAdd(s.de())
},
// 0x21 : LD IX, nn
0x21: func(s *Z80Type) {
s.IX = s.getAddr()
},
// 0x22 : LD (nn), IX
0x22: func(s *Z80Type) {
addr := s.getAddr()
s.core.MemWrite(addr, byte(s.IX&0x00ff))
s.core.MemWrite(addr+1, byte(s.IX>>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<<bitNumber) == 0
z.Flags.P = z.Flags.Z
z.Flags.S = (bitNumber == 7) && !z.Flags.Z
} else if opcode < 0xc0 {
// RES
value = z.core.MemRead(offset) & ^(1 << bitNumber)
z.core.MemWrite(offset, value)
} else {
// SET
value = z.core.MemRead(offset | (1 << bitNumber))
z.core.MemWrite(offset, value)
}
}
// This implements the undocumented shift, RES, and SET opcodes,
// which write their result to memory and also to an 8080 register.
if !bitTestOp {
value := byte(1)
switch opcode & 0x07 {
case 0:
z.B = value
case 1:
z.C = value
case 2:
z.D = value
case 3:
z.E = value
case 4:
z.H = value
case 5:
z.L = value
// 6 is the documented opcode, which doesn't set a register.
case 7:
z.A = value
}
}
z.CycleCounter += CycleCountsCb[opcode] + 8
}

426
z80em/opcodeED.go Normal file
View File

@ -0,0 +1,426 @@
package z80em
var edInstructions = []func(s *Z80Type){
// 0x40 : IN B, (C)
0x40: func(s *Z80Type) {
s.B = s.doIn((uint16(s.B) << 8) | uint16(s.C))
},
// 0x41 : OUT (C), B
0x41: func(s *Z80Type) {
s.core.IOWrite((uint16(s.B)<<8)|uint16(s.C), s.B)
},
// 0x42 : SBC HL, BC
0x42: func(s *Z80Type) {
s.doHlSbc(uint16(s.C) | (uint16(s.B) << 8))
},
// 0x43 : LD (nn), BC
0x43: func(s *Z80Type) {
addr := s.getAddr()
s.core.MemWrite(addr, s.C)
s.core.MemWrite(addr+1, s.B)
},
// 0x44 : NEG
0x44: func(s *Z80Type) {
s.doNeg()
},
// 0x45 : RETN
0x45: func(s *Z80Type) {
s.PC = s.PopWord() - 1
s.Iff1 = s.Iff2
},
// 0x46 : IM 0
0x46: func(s *Z80Type) {
s.IMode = 0
},
// 0x47 : LD I, A
0x47: func(s *Z80Type) {
s.I = s.A
},
// 0x48 : IN C, (C)
0x48: func(s *Z80Type) {
s.C = s.doIn(s.bc())
},
// 0x49 : OUT (C), C
0x49: func(s *Z80Type) {
s.core.IOWrite(s.bc(), s.C)
},
// 0x4a : ADC HL, BC
0x4A: func(s *Z80Type) {
s.doHlAdc(s.bc())
},
// 0x4b : LD BC, (nn)
0x4B: func(s *Z80Type) {
addr := s.getAddr()
s.C = s.core.MemRead(addr)
s.B = s.core.MemRead(addr + 1)
},
// 0x4c : NEG (Undocumented)
0x4C: func(s *Z80Type) {
s.doNeg()
},
// 0x4d : RETI
0x4D: func(s *Z80Type) {
s.PC = s.PopWord() - 1
},
// 0x4e : IM 0 (Undocumented)
0x4E: func(s *Z80Type) {
s.IMode = 0
},
// 0x4f : LD R, A
0x4F: func(s *Z80Type) {
s.R = s.A
},
// 0x50 : IN D, (C)
0x50: func(s *Z80Type) {
s.D = s.doIn(s.bc())
},
// 0x51 : OUT (C), D
0x51: func(s *Z80Type) {
s.core.IOWrite(s.bc(), s.D)
},
// 0x52 : SBC HL, DE
0x52: func(s *Z80Type) {
s.doHlSbc(s.de())
},
// 0x53 : LD (nn), DE
0x53: func(s *Z80Type) {
addr := s.getAddr()
s.core.MemWrite(addr, s.E)
s.core.MemWrite(addr+1, s.D)
},
// 0x54 : NEG (Undocumented)
0x54: func(s *Z80Type) {
s.doNeg()
},
// 0x55 : RETN
0x55: func(s *Z80Type) {
s.PC = s.PopWord() - 1
s.Iff1 = s.Iff2
},
// 0x56 : IM 1
0x56: func(s *Z80Type) {
s.IMode = 1
},
// 0x57 : LD A, I
0x57: func(s *Z80Type) {
s.A = s.I
s.Flags.S = s.A&0x80 != 0
s.Flags.Z = s.A == 0
s.Flags.H = false
s.Flags.P = s.Iff2 != 0
s.Flags.N = false
s.updateXYFlags(s.A)
},
// 0x58 : IN E, (C)
0x58: func(s *Z80Type) {
s.E = s.doIn(s.bc())
},
// 0x59 : OUT (C), E
0x59: func(s *Z80Type) {
s.core.IOWrite(s.bc(), s.E)
},
// 0x5a : ADC HL, DE
0x5A: func(s *Z80Type) {
s.doHlAdc(s.de())
},
// 0x5b : LD DE, (nn)
0x5B: func(s *Z80Type) {
addr := s.getAddr()
s.E = s.core.MemRead(addr)
s.D = s.core.MemRead(addr + 1)
},
// 0x5c : NEG (Undocumented)
0x5C: func(s *Z80Type) {
s.doNeg()
},
// 0x5d : RETN
0x5D: func(s *Z80Type) {
s.PC = s.PopWord() - 1
s.Iff1 = s.Iff2
},
// 0x5e : IM 2
0x5E: func(s *Z80Type) {
s.IMode = 2
},
// 0x5f : LD A, R
0x5F: func(s *Z80Type) {
s.A = s.R
s.Flags.S = s.A&0x80 != 0
s.Flags.Z = s.A == 0
s.Flags.H = false
s.Flags.P = s.Iff2 != 0
s.Flags.N = false
s.updateXYFlags(s.A)
},
// 0x60 : IN H, (C)
0x60: func(s *Z80Type) {
s.H = s.doIn(s.bc())
},
// 0x61 : OUT (C), H
0x61: func(s *Z80Type) {
s.core.IOWrite(s.bc(), s.H)
},
// 0x62 : SBC HL, HL
0x62: func(s *Z80Type) {
s.doHlSbc(s.hl())
},
// 0x63 : LD (nn), HL (Undocumented)
0x63: func(s *Z80Type) {
addr := s.getAddr()
s.core.MemWrite(addr, s.L)
s.core.MemWrite(addr+1, s.H)
},
// 0x64 : NEG (Undocumented)
0x64: func(s *Z80Type) {
s.doNeg()
},
// 0x65 : RETN
0x65: func(s *Z80Type) {
s.PC = s.PopWord() - 1
s.Iff1 = s.Iff2
},
// 0x66 : IM 0
0x66: func(s *Z80Type) {
s.IMode = 0
},
// 0x67 : RRD
0x67: func(s *Z80Type) {
hlValue := s.core.M1MemRead(s.hl())
temp1 := hlValue & 0x0f
temp2 := s.A & 0x0f
hlValue = ((hlValue & 0xf0) >> 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]
}
}

File diff suppressed because it is too large Load Diff