mirror of
https://github.com/romychs/Ocean-240.2-Emulator.git
synced 2026-04-21 19:13:20 +03:00
Z80 basic impl completed
This commit is contained in:
parent
76829b80cc
commit
76312b1437
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
*.bak
|
||||
*.lst
|
||||
*.tmp
|
||||
.idea/
|
||||
|
||||
9
.idea/z80em.iml
generated
9
.idea/z80em.iml
generated
@ -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
76
config/config.go
Normal 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
7
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
|
||||
|
||||
14
go.sum
Normal file
14
go.sum
Normal 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
85
logger/logger.go
Normal 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
32
main.go
@ -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
BIN
okean240/CPM_r7.BIN
Normal file
Binary file not shown.
BIN
okean240/MON_r6.BIN
Normal file
BIN
okean240/MON_r6.BIN
Normal file
Binary file not shown.
75
okean240/computer.go
Normal file
75
okean240/computer.go
Normal 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
154
okean240/ioports.go
Normal 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
126
okean240/memory.go
Normal 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
4
okemu.yml
Normal 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
14
rebuild.sh
Executable 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
101
z80em/constants.go
Normal 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,
|
||||
}
|
||||
@ -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
657
z80em/opcode.go
Normal 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
106
z80em/opcodeCB.go
Normal 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
483
z80em/opcodeDD.go
Normal 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
426
z80em/opcodeED.go
Normal 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]
|
||||
}
|
||||
|
||||
}
|
||||
1007
z80em/z80em.go
1007
z80em/z80em.go
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user