mirror of
https://github.com/romychs/Ocean-240.2-Emulator.git
synced 2026-04-21 11:03:21 +03:00
Compare commits
No commits in common. "master" and "v1.0.1" have entirely different histories.
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,5 @@
|
|||||||
*.lst
|
*.lst
|
||||||
*.tmp
|
*.tmp
|
||||||
*.sh
|
*.sh
|
||||||
*.log
|
|
||||||
.idea/
|
.idea/
|
||||||
src/.idea/
|
src/.idea/
|
||||||
Binary file not shown.
@ -1,5 +1,5 @@
|
|||||||
logFile: okemu.log
|
logFile: okemu.log
|
||||||
logLevel: debug
|
logLevel: info
|
||||||
monitorFile: rom/MON_r8_9c6c6546.mon
|
monitorFile: rom/MON_r8_9c6c6546.mon
|
||||||
cpmFile: rom/CPM_r8_bc0695e4.cpm
|
cpmFile: rom/CPM_r8_bc0695e4.cpm
|
||||||
fdc:
|
fdc:
|
||||||
|
|||||||
@ -3,9 +3,9 @@ package debug
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"okemu/debug/breakpoint"
|
"okemu/debug/breakpoint"
|
||||||
|
"okemu/z80"
|
||||||
|
"okemu/z80/dis"
|
||||||
|
|
||||||
"github.com/romychs/z80go"
|
|
||||||
"github.com/romychs/z80go/dis"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ type Debugger struct {
|
|||||||
cpuHistoryEnabled bool
|
cpuHistoryEnabled bool
|
||||||
cpuHistoryStarted bool
|
cpuHistoryStarted bool
|
||||||
cpuHistoryMaxSize int
|
cpuHistoryMaxSize int
|
||||||
cpuHistory []*z80go.CPU
|
cpuHistory []*z80.CPU
|
||||||
memBreakpoints [65536]byte
|
memBreakpoints [65536]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ func NewDebugger() *Debugger {
|
|||||||
cpuHistoryEnabled: false,
|
cpuHistoryEnabled: false,
|
||||||
cpuHistoryStarted: false,
|
cpuHistoryStarted: false,
|
||||||
cpuHistoryMaxSize: 0,
|
cpuHistoryMaxSize: 0,
|
||||||
cpuHistory: []*z80go.CPU{},
|
cpuHistory: []*z80.CPU{},
|
||||||
}
|
}
|
||||||
return &d
|
return &d
|
||||||
}
|
}
|
||||||
@ -85,14 +85,14 @@ func (d *Debugger) SetCpuHistoryMaxSize(size int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Debugger) CpuHistoryClear() {
|
func (d *Debugger) CpuHistoryClear() {
|
||||||
d.cpuHistory = make([]*z80go.CPU, 0)
|
d.cpuHistory = make([]*z80.CPU, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Debugger) CpuHistorySize() int {
|
func (d *Debugger) CpuHistorySize() int {
|
||||||
return len(d.cpuHistory)
|
return len(d.cpuHistory)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Debugger) CpuHistory(index int) *z80go.CPU {
|
func (d *Debugger) CpuHistory(index int) *z80.CPU {
|
||||||
if index >= 0 && index < len(d.cpuHistory) {
|
if index >= 0 && index < len(d.cpuHistory) {
|
||||||
return d.cpuHistory[index]
|
return d.cpuHistory[index]
|
||||||
}
|
}
|
||||||
@ -108,9 +108,9 @@ func (d *Debugger) SetCpuHistoryStarted(started bool) {
|
|||||||
d.cpuHistoryStarted = started
|
d.cpuHistoryStarted = started
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Debugger) SaveHistory(state *z80go.CPU) {
|
func (d *Debugger) SaveHistory(state *z80.CPU) {
|
||||||
if d.cpuHistoryEnabled && d.cpuHistoryMaxSize > 0 && d.cpuHistoryStarted {
|
if d.cpuHistoryEnabled && d.cpuHistoryMaxSize > 0 && d.cpuHistoryStarted {
|
||||||
d.cpuHistory = append([]*z80go.CPU{state}, d.cpuHistory...)
|
d.cpuHistory = append([]*z80.CPU{state}, d.cpuHistory...)
|
||||||
if len(d.cpuHistory) > d.cpuHistoryMaxSize {
|
if len(d.cpuHistory) > d.cpuHistoryMaxSize {
|
||||||
d.cpuHistory = d.cpuHistory[0 : d.cpuHistoryMaxSize-1]
|
d.cpuHistory = d.cpuHistory[0 : d.cpuHistoryMaxSize-1]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,20 +5,19 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"maps"
|
|
||||||
"net"
|
"net"
|
||||||
"okemu/config"
|
"okemu/config"
|
||||||
"okemu/debug"
|
"okemu/debug"
|
||||||
"okemu/debug/breakpoint"
|
"okemu/debug/breakpoint"
|
||||||
"okemu/okean240"
|
"okemu/okean240"
|
||||||
|
"okemu/z80"
|
||||||
|
"okemu/z80/dis"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"slices"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
//"okemu/logger"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/romychs/z80go"
|
|
||||||
"github.com/romychs/z80go/dis"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -76,7 +75,6 @@ var commandHandlers = map[string]CommandHandler{
|
|||||||
"hexdump": {(*ZRCP).handleHexDump, "Dumps memory at address, showing hex and ascii"},
|
"hexdump": {(*ZRCP).handleHexDump, "Dumps memory at address, showing hex and ascii"},
|
||||||
"load-binary": {(*ZRCP).handleLoadBinary, "Load binary file \"file\" at address \"addr\" with length \"len\", on the current memory zone"},
|
"load-binary": {(*ZRCP).handleLoadBinary, "Load binary file \"file\" at address \"addr\" with length \"len\", on the current memory zone"},
|
||||||
"quit": {(*ZRCP).handleEmptyHandler, "Closes connection"},
|
"quit": {(*ZRCP).handleEmptyHandler, "Closes connection"},
|
||||||
"reset-cpu": {(*ZRCP).handleResetCPU, "Resets CPU"},
|
|
||||||
"read-memory": {(*ZRCP).handleReadMemory, "Dumps memory at address"},
|
"read-memory": {(*ZRCP).handleReadMemory, "Dumps memory at address"},
|
||||||
"reset-tstates-partial": {(*ZRCP).handleResetTStatesPartial, "Resets the t-states partial counter"},
|
"reset-tstates-partial": {(*ZRCP).handleResetTStatesPartial, "Resets the t-states partial counter"},
|
||||||
"run": {(*ZRCP).handleRun, "Run cpu when on cpu step mode"},
|
"run": {(*ZRCP).handleRun, "Run cpu when on cpu step mode"},
|
||||||
@ -339,8 +337,6 @@ func (p *ZRCP) handleCPUHistory() (string, error) {
|
|||||||
return p.stateResponse(history), nil
|
return p.stateResponse(history), nil
|
||||||
}
|
}
|
||||||
return "", errors.New("ERROR: index out of range")
|
return "", errors.New("ERROR: index out of range")
|
||||||
case "get-size":
|
|
||||||
return strconv.Itoa(p.debugger.CpuHistorySize()), nil
|
|
||||||
case "ignrephalt", "ignrepldxr":
|
case "ignrephalt", "ignrepldxr":
|
||||||
// ignore
|
// ignore
|
||||||
default:
|
default:
|
||||||
@ -391,24 +387,24 @@ func (p *ZRCP) handleLoadBinary() (string, error) {
|
|||||||
// registersResponse Build string
|
// registersResponse Build string
|
||||||
// PC=%4x SP=%4x AF=%4x BC=%4x HL=%4x DE=%4x IX=%4x IY=%4x AF'=%4x BC'=%4x HL'=%4x DE'=%4x I=%2x
|
// PC=%4x SP=%4x AF=%4x BC=%4x HL=%4x DE=%4x IX=%4x IY=%4x AF'=%4x BC'=%4x HL'=%4x DE'=%4x I=%2x
|
||||||
// R=%2x F=%s F'=%s MEMPTR=%4x IM0 IFF-- VPS: 0 MMU=00000000000000000000000000000000
|
// R=%2x F=%s F'=%s MEMPTR=%4x IM0 IFF-- VPS: 0 MMU=00000000000000000000000000000000
|
||||||
func (p *ZRCP) registersResponse(state *z80go.CPU) string {
|
func (p *ZRCP) registersResponse(state *z80.CPU) string {
|
||||||
resp := fmt.Sprintf(getRegistersResponse,
|
resp := fmt.Sprintf(getRegistersResponse,
|
||||||
state.PC,
|
state.PC,
|
||||||
state.SP,
|
state.SP,
|
||||||
toW(state.A, state.Flags.AsByte()),
|
toW(state.A, state.Flags.GetFlags()),
|
||||||
toW(state.B, state.C),
|
toW(state.B, state.C),
|
||||||
toW(state.H, state.L),
|
toW(state.H, state.L),
|
||||||
toW(state.D, state.E),
|
toW(state.D, state.E),
|
||||||
state.IX,
|
state.IX,
|
||||||
state.IY,
|
state.IY,
|
||||||
toW(state.AAlt, state.FlagsAlt.AsByte()),
|
toW(state.AAlt, state.FlagsAlt.GetFlags()),
|
||||||
toW(state.BAlt, state.CAlt),
|
toW(state.BAlt, state.CAlt),
|
||||||
toW(state.HAlt, state.LAlt),
|
toW(state.HAlt, state.LAlt),
|
||||||
toW(state.DAlt, state.EAlt),
|
toW(state.DAlt, state.EAlt),
|
||||||
state.I,
|
state.I,
|
||||||
state.R,
|
state.R,
|
||||||
state.Flags.String(),
|
state.Flags.GetFlagsStr(),
|
||||||
state.FlagsAlt.String(),
|
state.FlagsAlt.GetFlagsStr(),
|
||||||
state.MemPtr,
|
state.MemPtr,
|
||||||
iifStr(state.Iff1, state.Iff2),
|
iifStr(state.Iff1, state.Iff2),
|
||||||
p.getMMU(),
|
p.getMMU(),
|
||||||
@ -430,17 +426,17 @@ func (p *ZRCP) getNBytes(addr uint16, n int) string {
|
|||||||
// stateResponse build string, represent history state
|
// stateResponse build string, represent history state
|
||||||
// PC=003a SP=ff46 AF=005c BC=174b HL=107f DE=0006 IX=ffff IY=5c3a AF'=0044 BC'=ffff HL'=ffff DE'=5cb9 I=3f R=78
|
// PC=003a SP=ff46 AF=005c BC=174b HL=107f DE=0006 IX=ffff IY=5c3a AF'=0044 BC'=ffff HL'=ffff DE'=5cb9 I=3f R=78
|
||||||
// IM0 IFF-- (PC)=2a785c23 (SP)=107f MMU=00000000000000000000000000000000
|
// IM0 IFF-- (PC)=2a785c23 (SP)=107f MMU=00000000000000000000000000000000
|
||||||
func (p *ZRCP) stateResponse(state *z80go.CPU) string {
|
func (p *ZRCP) stateResponse(state *z80.CPU) string {
|
||||||
resp := fmt.Sprintf(getStateResponse,
|
resp := fmt.Sprintf(getStateResponse,
|
||||||
state.PC,
|
state.PC,
|
||||||
state.SP,
|
state.SP,
|
||||||
toW(state.A, state.Flags.AsByte()),
|
toW(state.A, state.Flags.GetFlags()),
|
||||||
toW(state.B, state.C),
|
toW(state.B, state.C),
|
||||||
toW(state.H, state.L),
|
toW(state.H, state.L),
|
||||||
toW(state.D, state.E),
|
toW(state.D, state.E),
|
||||||
state.IX,
|
state.IX,
|
||||||
state.IY,
|
state.IY,
|
||||||
toW(state.AAlt, state.FlagsAlt.AsByte()),
|
toW(state.AAlt, state.FlagsAlt.GetFlags()),
|
||||||
toW(state.BAlt, state.CAlt),
|
toW(state.BAlt, state.CAlt),
|
||||||
toW(state.HAlt, state.LAlt),
|
toW(state.HAlt, state.LAlt),
|
||||||
toW(state.DAlt, state.EAlt),
|
toW(state.DAlt, state.EAlt),
|
||||||
@ -583,14 +579,9 @@ func (p *ZRCP) getExtendedStack() (string, error) {
|
|||||||
var resp strings.Builder
|
var resp strings.Builder
|
||||||
spEnd := sp - size*2
|
spEnd := sp - size*2
|
||||||
es, err := p.computer.ExtendedStack()
|
es, err := p.computer.ExtendedStack()
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
for i := sp; i > spEnd; i -= 2 {
|
for i := sp; i > spEnd; i -= 2 {
|
||||||
pvt, ok := es[i]
|
resp.WriteString(fmt.Sprintf("%04XH %s\n", p.computer.MemRead(i), PushValueTypeName[es[i]]))
|
||||||
if !ok {
|
|
||||||
pvt = z80go.PushValueTypeDefault
|
|
||||||
}
|
|
||||||
resp.WriteString(fmt.Sprintf("%04XH %s\n", p.computer.MemRead(i), PushValueTypeName[pvt]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Tracef("extended-stack get: %s", resp)
|
log.Tracef("extended-stack get: %s", resp)
|
||||||
@ -642,7 +633,6 @@ func (p *ZRCP) handleSetBreakpointPassCount() (string, error) {
|
|||||||
|
|
||||||
func (p *ZRCP) handleDisassemble() (string, error) {
|
func (p *ZRCP) handleDisassemble() (string, error) {
|
||||||
var addr uint16
|
var addr uint16
|
||||||
var size uint64
|
|
||||||
if len(p.params) == 0 {
|
if len(p.params) == 0 {
|
||||||
addr = p.computer.CPUState().PC
|
addr = p.computer.CPUState().PC
|
||||||
} else {
|
} else {
|
||||||
@ -651,17 +641,9 @@ func (p *ZRCP) handleDisassemble() (string, error) {
|
|||||||
if e != nil {
|
if e != nil {
|
||||||
return "", fmt.Errorf("error, illegal address: %s", p.params[0])
|
return "", fmt.Errorf("error, illegal address: %s", p.params[0])
|
||||||
}
|
}
|
||||||
if len(p.params) == 2 {
|
|
||||||
size, e = parseUint64(p.params[1])
|
|
||||||
if e != nil {
|
|
||||||
return "", fmt.Errorf("error, illegal size: %s", p.params[1])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
size = 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
res := p.disassembler.Disassm(addr)
|
res := p.disassembler.Disassm(addr)
|
||||||
log.Tracef("DISASSM[0x%04X, %d]: %s", addr, size, res)
|
log.Trace(res)
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -713,11 +695,6 @@ func (p *ZRCP) handleGetRegisters() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *ZRCP) handleHardResetCPU() (string, error) {
|
func (p *ZRCP) handleHardResetCPU() (string, error) {
|
||||||
p.computer.SetPendingCpuReset(true)
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ZRCP) handleResetCPU() (string, error) {
|
|
||||||
p.computer.Reset()
|
p.computer.Reset()
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
@ -955,10 +932,8 @@ func (p *ZRCP) handleGetMemBreakpoints() (string, error) {
|
|||||||
func (p *ZRCP) handleHelp() (string, error) {
|
func (p *ZRCP) handleHelp() (string, error) {
|
||||||
var res strings.Builder
|
var res strings.Builder
|
||||||
res.WriteString("Available commands:\n")
|
res.WriteString("Available commands:\n")
|
||||||
commands := slices.Collect(maps.Keys(commandHandlers))
|
for k, v := range commandHandlers {
|
||||||
slices.Sort(commands)
|
res.WriteString(fmt.Sprintf("%-*s%s\n", 24, k, v.desc))
|
||||||
for _, cmd := range commands {
|
|
||||||
res.WriteString(fmt.Sprintf("%-*s%s\n", 24, cmd, commandHandlers[cmd].desc))
|
|
||||||
}
|
}
|
||||||
res.WriteString("\nTotal commands: " + strconv.Itoa(len(commandHandlers)) + "\n")
|
res.WriteString("\nTotal commands: " + strconv.Itoa(len(commandHandlers)) + "\n")
|
||||||
return res.String(), nil
|
return res.String(), nil
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import (
|
|||||||
"okemu/config"
|
"okemu/config"
|
||||||
"okemu/okean240"
|
"okemu/okean240"
|
||||||
"okemu/okean240/fdc"
|
"okemu/okean240/fdc"
|
||||||
"os"
|
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/app"
|
"fyne.io/fyne/v2/app"
|
||||||
@ -15,14 +14,13 @@ import (
|
|||||||
"fyne.io/fyne/v2/dialog"
|
"fyne.io/fyne/v2/dialog"
|
||||||
"fyne.io/fyne/v2/driver/desktop"
|
"fyne.io/fyne/v2/driver/desktop"
|
||||||
"fyne.io/fyne/v2/layout"
|
"fyne.io/fyne/v2/layout"
|
||||||
"fyne.io/fyne/v2/storage"
|
|
||||||
"fyne.io/fyne/v2/theme"
|
"fyne.io/fyne/v2/theme"
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewMainWindow(computer *okean240.ComputerType, config *config.OkEmuConfig, title string) (*fyne.Window, *canvas.Raster, *widget.Label) {
|
func NewMainWindow(computer *okean240.ComputerType, config *config.OkEmuConfig) (*fyne.Window, *canvas.Raster, *widget.Label) {
|
||||||
emulatorApp := app.NewWithID("oceanemu-0001-0000-0001-8f33aba1fc0e")
|
emulatorApp := app.New()
|
||||||
w := emulatorApp.NewWindow(title)
|
w := emulatorApp.NewWindow("Океан 240.2")
|
||||||
|
|
||||||
// Handle all keys typed in main window canvas
|
// Handle all keys typed in main window canvas
|
||||||
w.Canvas().SetOnTypedKey(
|
w.Canvas().SetOnTypedKey(
|
||||||
@ -73,57 +71,24 @@ func NewMainWindow(computer *okean240.ComputerType, config *config.OkEmuConfig,
|
|||||||
return &w, raster, label
|
return &w, raster, label
|
||||||
}
|
}
|
||||||
|
|
||||||
var floppyDriveExt = []string{".okd", ".dat", ".bin", ".raw"}
|
|
||||||
|
|
||||||
func newOkdOpenDialog(drive byte, c *okean240.ComputerType, w fyne.Window, config *config.OkEmuConfig) *dialog.FileDialog {
|
|
||||||
fod := dialog.NewFileOpen(func(reader fyne.URIReadCloser, err error) {
|
|
||||||
// Read file list error occurred
|
|
||||||
if err != nil {
|
|
||||||
dialog.ShowError(err, w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// User Cancelled
|
|
||||||
if reader == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// User select Open button, load data
|
|
||||||
err = c.LoadFloppyData(drive, reader)
|
|
||||||
if err != nil {
|
|
||||||
dialog.ShowError(err, w)
|
|
||||||
}
|
|
||||||
_ = reader.Close()
|
|
||||||
}, w)
|
|
||||||
fod.SetFileName(config.FDC[drive].FloppyFile)
|
|
||||||
cwd, e := os.Getwd()
|
|
||||||
if e == nil {
|
|
||||||
uri, e := storage.ListerForURI(storage.NewFileURI(cwd))
|
|
||||||
if e == nil {
|
|
||||||
fod.SetLocation(uri)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fod.SetTitleText(fmt.Sprintf("Load floppy %s: image", string(rune(int(drive+66)))))
|
|
||||||
fod.SetFilter(storage.NewExtensionFileFilter(floppyDriveExt))
|
|
||||||
fod.Resize(fyne.NewSize(580, 500))
|
|
||||||
return fod
|
|
||||||
}
|
|
||||||
|
|
||||||
func newToolbar(c *okean240.ComputerType, w fyne.Window, a fyne.App, config *config.OkEmuConfig) *fyne.Container {
|
func newToolbar(c *okean240.ComputerType, w fyne.Window, a fyne.App, config *config.OkEmuConfig) *fyne.Container {
|
||||||
hBox := container.NewHBox()
|
hBox := container.NewHBox()
|
||||||
|
|
||||||
for d := 0; d < fdc.TotalDrives; d++ {
|
for d := 0; d < fdc.TotalDrives; d++ {
|
||||||
hBox.Add(widget.NewLabel(string(rune(66+d)) + ":"))
|
hBox.Add(widget.NewLabel(string(rune(66+d)) + ":"))
|
||||||
hBox.Add(widget.NewToolbar(
|
hBox.Add(widget.NewToolbar(
|
||||||
// Button Open
|
|
||||||
widget.NewToolbarAction(theme.FolderOpenIcon(), func() {
|
|
||||||
newOkdOpenDialog(byte(d), c, w, config).Show()
|
|
||||||
}),
|
|
||||||
// Button Save
|
|
||||||
widget.NewToolbarAction(theme.DocumentSaveIcon(), func() {
|
widget.NewToolbarAction(theme.DocumentSaveIcon(), func() {
|
||||||
err := c.SaveFloppy(byte(d))
|
err := c.SaveFloppy(byte(d))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dialog.ShowError(err, w)
|
dialog.ShowError(err, w)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
//widget.NewToolbarSpacer(),
|
||||||
|
widget.NewToolbarAction(theme.FolderOpenIcon(), func() {
|
||||||
|
err := c.LoadFloppy(byte(d))
|
||||||
|
if err != nil {
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
}
|
||||||
|
}),
|
||||||
))
|
))
|
||||||
hBox.Add(widget.NewSeparator())
|
hBox.Add(widget.NewSeparator())
|
||||||
}
|
}
|
||||||
@ -174,7 +139,7 @@ func newToolbar(c *okean240.ComputerType, w fyne.Window, a fyne.App, config *con
|
|||||||
}))
|
}))
|
||||||
hBox.Add(layout.NewSpacer())
|
hBox.Add(layout.NewSpacer())
|
||||||
hBox.Add(widget.NewButtonWithIcon("Reset", theme.MediaReplayIcon(), func() {
|
hBox.Add(widget.NewButtonWithIcon("Reset", theme.MediaReplayIcon(), func() {
|
||||||
c.SetPendingHardReset(true)
|
c.SetPendingReset(true)
|
||||||
//computer.Reset(conf)
|
//computer.Reset(conf)
|
||||||
}))
|
}))
|
||||||
return hBox
|
return hBox
|
||||||
|
|||||||
@ -37,7 +37,6 @@ require (
|
|||||||
github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect
|
github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
||||||
github.com/romychs/z80go v1.0.1 // indirect
|
|
||||||
github.com/rymdport/portal v0.4.2 // indirect
|
github.com/rymdport/portal v0.4.2 // indirect
|
||||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
|
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
|
||||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
|
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
|
||||||
|
|||||||
@ -70,10 +70,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/romychs/z80go v1.0.0 h1:umlM6U/JPt/qSAUhxu51wSK5a98gJZwhTu34Xj+5Ml8=
|
|
||||||
github.com/romychs/z80go v1.0.0/go.mod h1:bah4U8xv8qWfRJsrioP643bri+LDzKyC03E718YS9wI=
|
|
||||||
github.com/romychs/z80go v1.0.1 h1:rBYxvvdWHGsitZMXTheZGk6lB9VIepUUVWMxPVWimPY=
|
|
||||||
github.com/romychs/z80go v1.0.1/go.mod h1:bah4U8xv8qWfRJsrioP643bri+LDzKyC03E718YS9wI=
|
|
||||||
github.com/rymdport/portal v0.4.2 h1:7jKRSemwlTyVHHrTGgQg7gmNPJs88xkbKcIL3NlcmSU=
|
github.com/rymdport/portal v0.4.2 h1:7jKRSemwlTyVHHrTGgQg7gmNPJs88xkbKcIL3NlcmSU=
|
||||||
github.com/rymdport/portal v0.4.2/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
|
github.com/rymdport/portal v0.4.2/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
|
||||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||||
|
|||||||
@ -46,7 +46,10 @@ func InitLogging() {
|
|||||||
// FlushLogs Flush logs if logging to file
|
// FlushLogs Flush logs if logging to file
|
||||||
func FlushLogs() {
|
func FlushLogs() {
|
||||||
if logBuffer != nil {
|
if logBuffer != nil {
|
||||||
_ = logBuffer.Flush()
|
err := logBuffer.Flush()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
157
src/main.go
157
src/main.go
@ -11,6 +11,7 @@ import (
|
|||||||
"okemu/forms"
|
"okemu/forms"
|
||||||
"okemu/logger"
|
"okemu/logger"
|
||||||
"okemu/okean240"
|
"okemu/okean240"
|
||||||
|
"okemu/z80/dis"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@ -19,20 +20,13 @@ import (
|
|||||||
"fyne.io/fyne/v2/canvas"
|
"fyne.io/fyne/v2/canvas"
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
"github.com/loov/hrtime"
|
"github.com/loov/hrtime"
|
||||||
"github.com/romychs/z80go/dis"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var Version = "v1.0.3"
|
var Version = "v1.0.1"
|
||||||
var BuildTime = "2026-04-08"
|
var BuildTime = "2026-04-01"
|
||||||
|
|
||||||
const defaultTimerClkPeriod = 433
|
const defaultTimerClkPeriod = 430
|
||||||
const defaultCpuClkPeriod = 310
|
const defaultCpuClkPeriod = 311
|
||||||
|
|
||||||
const windowsTimerClkPeriod = 400
|
|
||||||
const windowsCpuClkPeriod = 300
|
|
||||||
|
|
||||||
const maxDelta = 3
|
|
||||||
const diffScale = 15.0
|
|
||||||
|
|
||||||
////go:embed hex/m80.hex
|
////go:embed hex/m80.hex
|
||||||
//var serialBytes []byte
|
//var serialBytes []byte
|
||||||
@ -40,9 +34,27 @@ const diffScale = 15.0
|
|||||||
////go:embed bin/jack.com
|
////go:embed bin/jack.com
|
||||||
//var ramBytes []byte
|
//var ramBytes []byte
|
||||||
|
|
||||||
|
var needReset = false
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
fmt.Printf("Starting Ocean-240.2 emulator %s build at %s\n", Version, BuildTime)
|
fmt.Printf("Starting Ocean-240.2 emulator %s build at %s\n", Version, BuildTime)
|
||||||
|
|
||||||
|
//f, err := os.Create("okemu.prof")
|
||||||
|
//if err != nil {
|
||||||
|
// log.Warn("Can not create prof file", err)
|
||||||
|
//}
|
||||||
|
//defer func(f *os.File) {
|
||||||
|
// err := f.Close()
|
||||||
|
// if err != nil {
|
||||||
|
// log.Warn("Can not close prof file", err)
|
||||||
|
// }
|
||||||
|
//}(f)
|
||||||
|
//if err := pprof.StartCPUProfile(f); err != nil {
|
||||||
|
// log.Warn("Can not start CPU profiling", err)
|
||||||
|
//}
|
||||||
|
//defer pprof.StopCPUProfile()
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
@ -55,24 +67,18 @@ func main() {
|
|||||||
conf := config.GetConfig()
|
conf := config.GetConfig()
|
||||||
|
|
||||||
// Reconfigure logging by config values
|
// Reconfigure logging by config values
|
||||||
logger.ReconfigureLogging(conf)
|
// logger.ReconfigureLogging(conf)
|
||||||
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
cpuClkPeriod.Store(windowsCpuClkPeriod)
|
|
||||||
timerClkPeriod.Store(windowsTimerClkPeriod)
|
|
||||||
} else {
|
|
||||||
cpuClkPeriod.Store(defaultCpuClkPeriod)
|
|
||||||
timerClkPeriod.Store(defaultTimerClkPeriod)
|
|
||||||
}
|
|
||||||
|
|
||||||
debugger := debug.NewDebugger()
|
debugger := debug.NewDebugger()
|
||||||
computer := okean240.NewComputer(conf, debugger)
|
computer := okean240.NewComputer(conf, debugger)
|
||||||
|
|
||||||
|
//computer.SetSerialBytes(serialBytes)
|
||||||
|
|
||||||
computer.AutoLoadFloppy()
|
computer.AutoLoadFloppy()
|
||||||
|
|
||||||
disassm := dis.NewDisassembler(computer)
|
disassm := dis.NewDisassembler(computer)
|
||||||
|
|
||||||
w, raster, label := forms.NewMainWindow(computer, conf, "Океан 240.2 "+Version)
|
w, raster, label := forms.NewMainWindow(computer, conf)
|
||||||
|
|
||||||
//dezog := dzrp.NewDZRP(conf, debugger, disassm, computer)
|
//dezog := dzrp.NewDZRP(conf, debugger, disassm, computer)
|
||||||
dezog := zrcp.NewZRCP(conf, debugger, disassm, computer)
|
dezog := zrcp.NewZRCP(conf, debugger, disassm, computer)
|
||||||
@ -87,7 +93,6 @@ func main() {
|
|||||||
|
|
||||||
(*w).ShowAndRun()
|
(*w).ShowAndRun()
|
||||||
computer.AutoSaveFloppy()
|
computer.AutoSaveFloppy()
|
||||||
logger.CloseLogs()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func screen(ctx context.Context, computer *okean240.ComputerType, raster *canvas.Raster, label *widget.Label) {
|
func screen(ctx context.Context, computer *okean240.ComputerType, raster *canvas.Raster, label *widget.Label) {
|
||||||
@ -108,20 +113,19 @@ func screen(ctx context.Context, computer *okean240.ComputerType, raster *canvas
|
|||||||
// redraw screen here
|
// redraw screen here
|
||||||
fyne.Do(func() {
|
fyne.Do(func() {
|
||||||
// status for every 50 frames
|
// status for every 50 frames
|
||||||
if frame%10 == 0 {
|
if frame%50 == 0 {
|
||||||
timeElapsed := hrtime.Since(timeStart)
|
timeElapsed := hrtime.Since(timeStart)
|
||||||
period := float64(timeElapsed.Nanoseconds()) / 1_000_000.0
|
period := float64(timeElapsed.Nanoseconds()) / 1_000_000.0
|
||||||
|
|
||||||
|
//cpuFreq = math.Round(float64(computer.Cycles()-pre)/period) / 1000.0
|
||||||
cpuFreq = math.Round(float64(cpuTicks.Load()-pre)/period) / 1000.0
|
cpuFreq = math.Round(float64(cpuTicks.Load()-pre)/period) / 1000.0
|
||||||
timerFreq = math.Round(float64(timerTicks.Load()-preTim)/period) / 1000.0
|
timerFreq = math.Round(float64(timerTicks.Load()-preTim)/period) / 1000.0
|
||||||
|
label.SetText(formatLabel(computer, cpuFreq, timerFreq))
|
||||||
|
|
||||||
adjustPeriods(computer, cpuFreq, timerFreq)
|
adjustPeriods(computer, cpuFreq, timerFreq)
|
||||||
|
|
||||||
if frame%50 == 0 {
|
//log.Debugf("Cpu clk period: %d, Timer clock period: %d, period: %1.3f", cpuClkPeriod.Load(), timerClkPeriod.Load(), period)
|
||||||
label.SetText(formatLabel(computer, cpuFreq, timerFreq))
|
//pre = computer.Cycles()
|
||||||
//log.Debugf("Cpu clk period: %d, Timer clock period: %d, frame time: %1.3fms", cpuClkPeriod.Load(), timerClkPeriod.Load(), period/5.0)
|
|
||||||
//logger.FlushLogs()
|
|
||||||
}
|
|
||||||
pre = cpuTicks.Load()
|
pre = cpuTicks.Load()
|
||||||
preTim = timerTicks.Load()
|
preTim = timerTicks.Load()
|
||||||
timeStart = hrtime.Now()
|
timeStart = hrtime.Now()
|
||||||
@ -137,46 +141,32 @@ func screen(ctx context.Context, computer *okean240.ComputerType, raster *canvas
|
|||||||
// adjustPeriods Adjust periods for CPU and Timer clock frequencies
|
// adjustPeriods Adjust periods for CPU and Timer clock frequencies
|
||||||
func adjustPeriods(c *okean240.ComputerType, cpuFreq float64, timerFreq float64) {
|
func adjustPeriods(c *okean240.ComputerType, cpuFreq float64, timerFreq float64) {
|
||||||
// adjust cpu clock if not full speed
|
// adjust cpu clock if not full speed
|
||||||
if !c.FullSpeed() {
|
if c.FullSpeed() {
|
||||||
calcPeriod(cpuFreq, okean240.CPUFrequency, okean240.CPUFrequencyHi, okean240.CPUFrequencyLow, &cpuClkPeriod)
|
if cpuFreq > okean240.CPUFrequencyHi && cpuClkPeriod.Load() < defaultCpuClkPeriod+defaultCpuClkPeriod/2 {
|
||||||
}
|
cpuClkPeriod.Add(1)
|
||||||
// adjust timerClock clock
|
} else if cpuFreq < okean240.CPUFrequencyLow && cpuClkPeriod.Load() > 3 {
|
||||||
calcPeriod(timerFreq, okean240.TimerFrequency, okean240.TimerFrequencyHi, okean240.TimerFrequencyLow, &timerClkPeriod)
|
cpuClkPeriod.Add(-1)
|
||||||
}
|
|
||||||
|
|
||||||
// calcPeriod calc new value period to adjust frequency of timer or CPU
|
|
||||||
func calcPeriod(curFreq float64, destFreq float64, hiLimit float64, loLimit float64, period *atomic.Int64) {
|
|
||||||
if curFreq > hiLimit && period.Load() < 2000 {
|
|
||||||
period.Add(calcDelta(curFreq, destFreq))
|
|
||||||
} else if curFreq < loLimit && period.Load() > 0 {
|
|
||||||
period.Add(-calcDelta(curFreq, destFreq))
|
|
||||||
if period.Load() < 0 {
|
|
||||||
period.Store(0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// adjust timerClock clock
|
||||||
|
if timerFreq > okean240.TimerFrequencyHi && timerClkPeriod.Load() < defaultTimerClkPeriod+defaultTimerClkPeriod/2 {
|
||||||
// calcDelta calculate step to change period
|
timerClkPeriod.Add(1)
|
||||||
func calcDelta(currentFreq float64, destFreq float64) int64 {
|
} else if timerFreq < okean240.TimerFrequencyLow && timerClkPeriod.Load() > 3 {
|
||||||
delta := int64(math.Round(math.Abs(destFreq-currentFreq) * diffScale))
|
timerClkPeriod.Add(-1)
|
||||||
if delta < 1 {
|
|
||||||
return 1
|
|
||||||
} else if delta > maxDelta {
|
|
||||||
return maxDelta
|
|
||||||
}
|
}
|
||||||
return delta
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatLabel(computer *okean240.ComputerType, freq float64, freqTim float64) string {
|
func formatLabel(computer *okean240.ComputerType, freq float64, freqTim float64) string {
|
||||||
return fmt.Sprintf("Screen size: %dx%d | Fcpu: %1.3fMHz | Ftmr: %1.3fMHz | Debugger: %s", // CP:%d TP:%d",
|
return fmt.Sprintf("Screen size: %dx%d | Fcpu: %1.3fMHz | Ftmr: %1.3fMHz | Debugger: %s", computer.ScreenWidth(), computer.ScreenHeight(), freq, freqTim, computer.DebuggerState())
|
||||||
computer.ScreenWidth(), computer.ScreenHeight(), freq, freqTim, computer.DebuggerState())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var timerTicks atomic.Uint64
|
var timerTicks atomic.Uint64
|
||||||
|
|
||||||
var timerClkPeriod atomic.Int64 // period in nanos for 1.5MHz frequency
|
var timerClkPeriod atomic.Int64 // period in nanos for 1.5MHz frequency
|
||||||
var cpuClkPeriod atomic.Int64 // period in nanos for 2.5MHz frequency
|
var cpuClkPeriod atomic.Int64 // period in nanos for 2.5MHz frequency
|
||||||
|
|
||||||
func timerClock(computer *okean240.ComputerType) {
|
func timerClock(computer *okean240.ComputerType) {
|
||||||
|
timerClkPeriod.Store(defaultTimerClkPeriod)
|
||||||
timeStart := hrtime.Now()
|
timeStart := hrtime.Now()
|
||||||
for {
|
for {
|
||||||
elapsed := hrtime.Since(timeStart)
|
elapsed := hrtime.Since(timeStart)
|
||||||
@ -193,6 +183,7 @@ func timerClock(computer *okean240.ComputerType) {
|
|||||||
var cpuTicks atomic.Uint64
|
var cpuTicks atomic.Uint64
|
||||||
|
|
||||||
func cpuClock(computer *okean240.ComputerType, dezog debug.DEZOG) {
|
func cpuClock(computer *okean240.ComputerType, dezog debug.DEZOG) {
|
||||||
|
cpuClkPeriod.Store(defaultCpuClkPeriod)
|
||||||
|
|
||||||
cpuTicks.Store(0)
|
cpuTicks.Store(0)
|
||||||
nextTick := uint64(0)
|
nextTick := uint64(0)
|
||||||
@ -203,54 +194,32 @@ func cpuClock(computer *okean240.ComputerType, dezog debug.DEZOG) {
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
elapsed := hrtime.Since(timeStart)
|
elapsed := hrtime.Since(timeStart)
|
||||||
if computer.FullSpeed() || int64(elapsed) >= cpuClkPeriod.Load() {
|
if int64(elapsed) >= cpuClkPeriod.Load() {
|
||||||
timeStart = hrtime.Now()
|
timeStart = hrtime.Now()
|
||||||
bp = 0
|
bp = 0
|
||||||
bpType = 0
|
bpType = 0
|
||||||
|
|
||||||
// 2.5MHz frequency
|
// 2.5MHz frequency
|
||||||
cpuTicks.Add(1)
|
cpuTicks.Add(1)
|
||||||
if !computer.PendingHardReset() && !computer.PendingCpuReset() {
|
if computer.FullSpeed() {
|
||||||
if computer.FullSpeed() {
|
// Max frequency
|
||||||
// Max frequency
|
_, bp, bpType = computer.Do()
|
||||||
_, bp, bpType = computer.Do()
|
} else if cpuTicks.Load() >= nextTick {
|
||||||
nextTick = cpuTicks.Load()
|
var t uint32
|
||||||
} else if cpuTicks.Load() >= nextTick {
|
t, bp, bpType = computer.Do()
|
||||||
var t uint32
|
nextTick = cpuTicks.Load() + uint64(t)
|
||||||
t, bp, bpType = computer.Do()
|
runtime.Gosched()
|
||||||
nextTick = cpuTicks.Load() + uint64(t)
|
}
|
||||||
runtime.Gosched()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Breakpoint hit
|
// Breakpoint hit
|
||||||
if bp > 0 || bpType != 0 {
|
if bp > 0 || bpType != 0 {
|
||||||
dezog.BreakpointHit(bp, bpType)
|
dezog.BreakpointHit(bp, bpType)
|
||||||
}
|
}
|
||||||
} else {
|
if needReset {
|
||||||
if computer.PendingHardReset() {
|
computer.Reset()
|
||||||
computer.HardReset()
|
needReset = false
|
||||||
} else {
|
|
||||||
computer.Reset()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//func initPerf() {
|
|
||||||
// f, err := os.Create("okemu.prof")
|
|
||||||
// if err != nil {
|
|
||||||
// log.Warn("Can not create prof file", err)
|
|
||||||
// }
|
|
||||||
// defer func(f *os.File) {
|
|
||||||
// err := f.Close()
|
|
||||||
// if err != nil {
|
|
||||||
// log.Warn("Can not close prof file", err)
|
|
||||||
// }
|
|
||||||
// }(f)
|
|
||||||
// if err := pprof.StartCPUProfile(f); err != nil {
|
|
||||||
// log.Warn("Can not start CPU profiling", err)
|
|
||||||
// }
|
|
||||||
// defer pprof.StopCPUProfile()
|
|
||||||
//}
|
|
||||||
|
|||||||
@ -15,25 +15,24 @@ import (
|
|||||||
"okemu/okean240/pic"
|
"okemu/okean240/pic"
|
||||||
"okemu/okean240/pit"
|
"okemu/okean240/pit"
|
||||||
"okemu/okean240/usart"
|
"okemu/okean240/usart"
|
||||||
|
"okemu/z80"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"okemu/z80/c99"
|
||||||
"github.com/romychs/z80go"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DefaultCPUFrequency = 2_500_000
|
const DefaultCPUFrequency = 2_500_000
|
||||||
const CPUFrequencyLow float64 = 2.48
|
const CPUFrequencyLow float64 = 2.47
|
||||||
const CPUFrequency float64 = 2.50
|
const CPUFrequencyHi float64 = 2.53
|
||||||
const CPUFrequencyHi float64 = 2.52
|
|
||||||
const TimerFrequencyLow float64 = 1.47
|
const TimerFrequencyLow float64 = 1.47
|
||||||
const TimerFrequency float64 = 1.50
|
|
||||||
const TimerFrequencyHi float64 = 1.53
|
const TimerFrequencyHi float64 = 1.53
|
||||||
|
|
||||||
type ComputerType struct {
|
type ComputerType struct {
|
||||||
cpu *z80go.CPU
|
cpu *c99.Z80
|
||||||
memory Memory
|
memory Memory
|
||||||
ioPorts [256]byte
|
ioPorts [256]byte
|
||||||
cycles uint64
|
cycles uint64
|
||||||
@ -54,17 +53,16 @@ type ComputerType struct {
|
|||||||
hShift byte
|
hShift byte
|
||||||
cpuFrequency uint32
|
cpuFrequency uint32
|
||||||
//
|
//
|
||||||
debugger *debug.Debugger
|
debugger *debug.Debugger
|
||||||
config *config.OkEmuConfig
|
config *config.OkEmuConfig
|
||||||
kbAck atomic.Bool
|
kbAck atomic.Bool
|
||||||
fullSpeed atomic.Bool
|
fullSpeed atomic.Bool
|
||||||
pendingCpuReset atomic.Bool
|
pendingReset atomic.Bool
|
||||||
pendingHardReset atomic.Bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Snapshot struct {
|
type Snapshot struct {
|
||||||
CPU *z80go.CPU `json:"cpu,omitempty"`
|
CPU *z80.CPU `json:"cpu,omitempty"`
|
||||||
Memory string `json:"memory,omitempty"`
|
Memory string `json:"memory,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const VRAMBlock0 = 3
|
const VRAMBlock0 = 3
|
||||||
@ -87,11 +85,11 @@ const VidColorBit = 0x40
|
|||||||
// SetCPUState(state *z80.CPU)
|
// SetCPUState(state *z80.CPU)
|
||||||
//}
|
//}
|
||||||
|
|
||||||
func (c *ComputerType) CPUState() *z80go.CPU {
|
func (c *ComputerType) CPUState() *z80.CPU {
|
||||||
return c.cpu.GetState()
|
return c.cpu.GetState()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ComputerType) SetCPUState(state *z80go.CPU) {
|
func (c *ComputerType) SetCPUState(state *z80.CPU) {
|
||||||
c.cpu.SetState(state)
|
c.cpu.SetState(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,28 +109,10 @@ func (c *ComputerType) MemWrite(addr uint16, val byte) {
|
|||||||
func NewComputer(cfg *config.OkEmuConfig, deb *debug.Debugger) *ComputerType {
|
func NewComputer(cfg *config.OkEmuConfig, deb *debug.Debugger) *ComputerType {
|
||||||
c := ComputerType{}
|
c := ComputerType{}
|
||||||
c.config = cfg
|
c.config = cfg
|
||||||
c.cpu = z80go.NewCPU(&c)
|
|
||||||
c.debugger = deb
|
|
||||||
|
|
||||||
c.HardReset()
|
|
||||||
|
|
||||||
return &c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset Only CPU reset.
|
|
||||||
func (c *ComputerType) Reset() {
|
|
||||||
// CPU
|
|
||||||
c.cpu.Reset()
|
|
||||||
c.cycles = 0
|
|
||||||
c.tstatesPartial = 0
|
|
||||||
c.pendingCpuReset.Store(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HardReset full computer reset, Use SetPendingHardReset flag to set flag before call this method
|
|
||||||
func (c *ComputerType) HardReset() {
|
|
||||||
c.cpu.Reset()
|
|
||||||
c.memory = Memory{}
|
c.memory = Memory{}
|
||||||
c.memory.Init(c.config.MonitorFile, c.config.CPMFile)
|
c.memory.Init(cfg.MonitorFile, cfg.CPMFile)
|
||||||
|
|
||||||
|
c.cpu = c99.New(&c)
|
||||||
|
|
||||||
c.cycles = 0
|
c.cycles = 0
|
||||||
c.tstatesPartial = 0
|
c.tstatesPartial = 0
|
||||||
@ -145,18 +125,24 @@ func (c *ComputerType) HardReset() {
|
|||||||
|
|
||||||
c.vShift = 0
|
c.vShift = 0
|
||||||
c.hShift = 0
|
c.hShift = 0
|
||||||
|
//c.aOffset = 0x100
|
||||||
|
|
||||||
c.pit = pit.New()
|
c.pit = pit.New()
|
||||||
c.kbAck.Store(false)
|
c.kbAck.Store(false)
|
||||||
c.usart = usart.New()
|
c.usart = usart.New()
|
||||||
c.pic = pic.NewI8259()
|
c.pic = pic.NewI8259()
|
||||||
c.fdc = fdc.NewFDC(c.config)
|
c.fdc = fdc.NewFDC(cfg)
|
||||||
|
|
||||||
c.cpuFrequency = DefaultCPUFrequency
|
c.cpuFrequency = DefaultCPUFrequency
|
||||||
|
c.debugger = deb
|
||||||
c.fullSpeed.Store(false)
|
c.fullSpeed.Store(false)
|
||||||
c.pendingHardReset.Store(false)
|
c.pendingReset.Store(false)
|
||||||
c.pendingCpuReset.Store(false)
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ComputerType) Reset() {
|
||||||
|
c.cpu.Reset()
|
||||||
|
c.cycles = 0
|
||||||
|
c.tstatesPartial = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ComputerType) getContext() map[string]interface{} {
|
func (c *ComputerType) getContext() map[string]interface{} {
|
||||||
@ -191,7 +177,7 @@ func (c *ComputerType) getContext() map[string]interface{} {
|
|||||||
ctx["BC"] = uint16(s.B)<<8 | uint16(s.C)
|
ctx["BC"] = uint16(s.B)<<8 | uint16(s.C)
|
||||||
ctx["DE"] = uint16(s.D)<<8 | uint16(s.E)
|
ctx["DE"] = uint16(s.D)<<8 | uint16(s.E)
|
||||||
ctx["HL"] = uint16(s.H)<<8 | uint16(s.L)
|
ctx["HL"] = uint16(s.H)<<8 | uint16(s.L)
|
||||||
ctx["AF"] = uint16(s.A)<<8 | uint16(s.Flags.AsByte())
|
ctx["AF"] = uint16(s.A)<<8 | uint16(s.Flags.GetFlags())
|
||||||
ctx["MEM"] = c.memory.MemRead
|
ctx["MEM"] = c.memory.MemRead
|
||||||
ctx["IO"] = c.IORead
|
ctx["IO"] = c.IORead
|
||||||
return ctx
|
return ctx
|
||||||
@ -332,6 +318,10 @@ func (c *ComputerType) TimerClk() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ComputerType) LoadFloppy(drive byte) error {
|
||||||
|
return c.fdc.LoadFloppy(drive)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ComputerType) SaveFloppy(drive byte) error {
|
func (c *ComputerType) SaveFloppy(drive byte) error {
|
||||||
return c.fdc.SaveFloppy(drive)
|
return c.fdc.SaveFloppy(drive)
|
||||||
}
|
}
|
||||||
@ -468,7 +458,7 @@ func (c *ComputerType) AutoSaveFloppy() {
|
|||||||
func (c *ComputerType) AutoLoadFloppy() {
|
func (c *ComputerType) AutoLoadFloppy() {
|
||||||
for drv := byte(0); drv < fdc.TotalDrives; drv++ {
|
for drv := byte(0); drv < fdc.TotalDrives; drv++ {
|
||||||
if c.config.FDC[drv].AutoLoad {
|
if c.config.FDC[drv].AutoLoad {
|
||||||
e := c.fdc.LoadFloppyFile(drv, c.config.FDC[drv].FloppyFile)
|
e := c.fdc.LoadFloppy(drv)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
log.Error(e)
|
log.Error(e)
|
||||||
}
|
}
|
||||||
@ -492,7 +482,7 @@ func (c *ComputerType) SetExtendedStack(enabled bool) {
|
|||||||
c.cpu.SetExtendedStack(enabled)
|
c.cpu.SetExtendedStack(enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ComputerType) ExtendedStack() (map[uint16]z80go.PushValueType, error) {
|
func (c *ComputerType) ExtendedStack() ([]byte, error) {
|
||||||
return c.cpu.ExtendedStack()
|
return c.cpu.ExtendedStack()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,22 +526,10 @@ func (c *ComputerType) FullSpeed() bool {
|
|||||||
return c.fullSpeed.Load()
|
return c.fullSpeed.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ComputerType) SetPendingHardReset(pending bool) {
|
func (c *ComputerType) SetPendingReset(pending bool) {
|
||||||
c.pendingHardReset.Store(pending)
|
c.pendingReset.Store(pending)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ComputerType) PendingHardReset() bool {
|
func (c *ComputerType) PendingReset() bool {
|
||||||
return c.pendingHardReset.Load()
|
return c.pendingReset.Load()
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ComputerType) SetPendingCpuReset(pending bool) {
|
|
||||||
c.pendingCpuReset.Store(pending)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ComputerType) PendingCpuReset() bool {
|
|
||||||
return c.pendingCpuReset.Load()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ComputerType) LoadFloppyData(drive byte, reader fyne.URIReadCloser) error {
|
|
||||||
return c.fdc.LoadFloppyData(drive, reader)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
|
||||||
"okemu/config"
|
"okemu/config"
|
||||||
"os"
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
@ -331,12 +330,12 @@ func (f *FloppyDriveController) Drq() byte {
|
|||||||
return f.drq
|
return f.drq
|
||||||
}
|
}
|
||||||
|
|
||||||
//func (f *FloppyDriveController) LoadFloppy(drive byte) error {
|
func (f *FloppyDriveController) LoadFloppy(drive byte) error {
|
||||||
// if drive < TotalDrives {
|
if drive < TotalDrives {
|
||||||
// return f.LoadConfiguredFloppy(drive)
|
return loadFloppy(&f.sectors[drive], f.config.FDC[drive].FloppyFile)
|
||||||
// }
|
}
|
||||||
// return errors.New("DriveNo " + strconv.Itoa(int(drive)) + " out of range")
|
return errors.New("DriveNo " + strconv.Itoa(int(drive)) + " out of range")
|
||||||
//}
|
}
|
||||||
|
|
||||||
func (f *FloppyDriveController) SaveFloppy(drive byte) error {
|
func (f *FloppyDriveController) SaveFloppy(drive byte) error {
|
||||||
if drive < TotalDrives {
|
if drive < TotalDrives {
|
||||||
@ -402,28 +401,8 @@ func (f *FloppyDriveController) Sector() byte {
|
|||||||
return f.sectorNo
|
return f.sectorNo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FloppyDriveController) LoadFloppyData(drive byte, reader io.ReadCloser) error {
|
// loadFloppy load floppy image to sector buffer from file
|
||||||
if drive >= TotalDrives {
|
func loadFloppy(sectors *[SizeInSectors]SectorType, fileName string) error {
|
||||||
return errors.New("Drive " + strconv.Itoa(int(drive+65)) + " out of range")
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
for sector := 0; sector < SizeInSectors; sector++ {
|
|
||||||
var n int
|
|
||||||
n, err = reader.Read(f.sectors[drive][sector])
|
|
||||||
if n != SectorSize {
|
|
||||||
log.Error("Load floppy error, sector size: %d <> %d", n, SectorSize)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Load floppy content failed:", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadFloppyFile load floppy image to sector buffer from file
|
|
||||||
func (f *FloppyDriveController) LoadFloppyFile(drive byte, fileName string) error {
|
|
||||||
//fileName := f.config.FDC[drive].FloppyFile
|
|
||||||
log.Debugf("Load Floppy content from file %s.", fileName)
|
log.Debugf("Load Floppy content from file %s.", fileName)
|
||||||
file, err := os.Open(fileName)
|
file, err := os.Open(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -437,7 +416,19 @@ func (f *FloppyDriveController) LoadFloppyFile(drive byte, fileName string) erro
|
|||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
}(file)
|
}(file)
|
||||||
return f.LoadFloppyData(drive, file)
|
|
||||||
|
for sector := 0; sector < SizeInSectors; sector++ {
|
||||||
|
var n int
|
||||||
|
n, err = file.Read(sectors[sector])
|
||||||
|
if n != SectorSize {
|
||||||
|
log.Error("Load floppy error, sector size: %d <> %d", n, SectorSize)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Load floppy content failed:", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// saveFloppy Save specified sectors to file with name fileName
|
// saveFloppy Save specified sectors to file with name fileName
|
||||||
|
|||||||
@ -17,7 +17,7 @@ func (c *ComputerType) IORead(port uint16) uint8 {
|
|||||||
irr := c.pic.IRR()
|
irr := c.pic.IRR()
|
||||||
// if irq from keyboard and no ACK applied, re-fire
|
// if irq from keyboard and no ACK applied, re-fire
|
||||||
if irr&RstKbdMask != 0 && !c.kbAck.Load() {
|
if irr&RstKbdMask != 0 && !c.kbAck.Load() {
|
||||||
log.Tracef("KBD IRQ REFIRE PC=%04X", c.cpu.PC)
|
log.Tracef("KBD IRQ REFIRE PC=%04X", c.cpu.PC())
|
||||||
c.pic.SetIRQ(RstKbdNo)
|
c.pic.SetIRQ(RstKbdNo)
|
||||||
}
|
}
|
||||||
return irr
|
return irr
|
||||||
@ -31,7 +31,7 @@ func (c *ComputerType) IORead(port uint16) uint8 {
|
|||||||
return c.usart.Receive()
|
return c.usart.Receive()
|
||||||
case KbdDd78pa:
|
case KbdDd78pa:
|
||||||
// Keyboard data
|
// Keyboard data
|
||||||
log.Tracef("KBD RD: %d, PC=%04X", c.ioPorts[KbdDd78pa], c.cpu.PC)
|
log.Tracef("KBD RD: %d, PC=%04X", c.ioPorts[KbdDd78pa], c.cpu.PC())
|
||||||
return c.ioPorts[KbdDd78pa]
|
return c.ioPorts[KbdDd78pa]
|
||||||
case KbdDd78pb:
|
case KbdDd78pb:
|
||||||
return c.ioPorts[KbdDd78pb]
|
return c.ioPorts[KbdDd78pb]
|
||||||
|
|||||||
58
src/z80/c99/constants.go
Normal file
58
src/z80/c99/constants.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package c99
|
||||||
|
|
||||||
|
var cycles00 = [256]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 cyclesED = [256]byte{
|
||||||
|
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||||
|
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||||
|
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||||
|
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||||
|
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, 8, 12, 12, 15, 20, 8, 14, 8, 8,
|
||||||
|
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||||
|
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||||
|
16, 16, 16, 16, 8, 8, 8, 8, 16, 16, 16, 16, 8, 8, 8, 8,
|
||||||
|
16, 16, 16, 16, 8, 8, 8, 8, 16, 16, 16, 16, 8, 8, 8, 8,
|
||||||
|
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||||
|
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||||
|
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||||
|
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||||
|
}
|
||||||
|
|
||||||
|
var cyclesDDFD = [256]byte{
|
||||||
|
4, 4, 4, 4, 4, 4, 4, 4, 4, 15, 4, 4, 4, 4, 4, 4,
|
||||||
|
4, 4, 4, 4, 4, 4, 4, 4, 4, 15, 4, 4, 4, 4, 4, 4,
|
||||||
|
4, 14, 20, 10, 8, 8, 11, 4, 4, 15, 20, 10, 8, 8, 11, 4,
|
||||||
|
4, 4, 4, 4, 23, 23, 19, 4, 4, 15, 4, 4, 4, 4, 4, 4,
|
||||||
|
4, 4, 4, 4, 8, 8, 19, 4, 4, 4, 4, 4, 8, 8, 19, 4,
|
||||||
|
4, 4, 4, 4, 8, 8, 19, 4, 4, 4, 4, 4, 8, 8, 19, 4,
|
||||||
|
8, 8, 8, 8, 8, 8, 19, 8, 8, 8, 8, 8, 8, 8, 19, 8,
|
||||||
|
19, 19, 19, 19, 19, 19, 4, 19, 4, 4, 4, 4, 8, 8, 19, 4,
|
||||||
|
4, 4, 4, 4, 8, 8, 19, 4, 4, 4, 4, 4, 8, 8, 19, 4,
|
||||||
|
4, 4, 4, 4, 8, 8, 19, 4, 4, 4, 4, 4, 8, 8, 19, 4,
|
||||||
|
4, 4, 4, 4, 8, 8, 19, 4, 4, 4, 4, 4, 8, 8, 19, 4,
|
||||||
|
4, 4, 4, 4, 8, 8, 19, 4, 4, 4, 4, 4, 8, 8, 19, 4,
|
||||||
|
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4,
|
||||||
|
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||||
|
4, 14, 4, 23, 4, 15, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4,
|
||||||
|
4, 4, 4, 4, 4, 4, 4, 4, 4, 10, 4, 4, 4, 4, 4, 4,
|
||||||
|
}
|
||||||
277
src/z80/c99/cpu.go
Normal file
277
src/z80/c99/cpu.go
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
package c99
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"okemu/z80"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PushValueTypeDefault = iota
|
||||||
|
PushValueTypeCall
|
||||||
|
PushValueTypeRst
|
||||||
|
PushValueTypePush
|
||||||
|
PushValueTypeMaskableInt
|
||||||
|
PushValueTypeNonMaskableInt
|
||||||
|
)
|
||||||
|
|
||||||
|
type Z80 struct {
|
||||||
|
|
||||||
|
// cycle count (t-states)
|
||||||
|
cycleCount uint32
|
||||||
|
|
||||||
|
// special purpose registers
|
||||||
|
pc, sp, ix, iy uint16
|
||||||
|
|
||||||
|
// "wz" register
|
||||||
|
memPtr uint16
|
||||||
|
|
||||||
|
// main registers
|
||||||
|
a, b, c, d, e, h, l byte
|
||||||
|
|
||||||
|
// alternate registers
|
||||||
|
a_, b_, c_, d_, e_, h_, l_, f_ byte
|
||||||
|
|
||||||
|
// interrupt vector, memory refresh
|
||||||
|
i, r byte
|
||||||
|
|
||||||
|
// flags: sign, zero, yf, half-carry, xf, parity/overflow, negative, carry
|
||||||
|
sf, zf, yf, hf, xf, pf, nf, cf bool
|
||||||
|
iffDelay byte
|
||||||
|
interruptMode byte
|
||||||
|
intData byte
|
||||||
|
iff1 bool
|
||||||
|
iff2 bool
|
||||||
|
isHalted bool
|
||||||
|
intPending bool
|
||||||
|
nmiPending bool
|
||||||
|
|
||||||
|
core z80.MemIoRW
|
||||||
|
memAccess map[uint16]byte
|
||||||
|
codeCoverageEnabled bool
|
||||||
|
codeCoverage map[uint16]bool
|
||||||
|
extendedStackEnabled bool
|
||||||
|
extendedStack [65536]uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
MemAccessRead = 1
|
||||||
|
MemAccessWrite = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// New initializes a Z80 instance and return pointer to it
|
||||||
|
func New(core z80.MemIoRW) *Z80 {
|
||||||
|
z := Z80{}
|
||||||
|
z.core = core
|
||||||
|
|
||||||
|
z.cycleCount = 0
|
||||||
|
|
||||||
|
z.pc = 0
|
||||||
|
z.sp = 0xFFFF
|
||||||
|
z.ix = 0
|
||||||
|
z.iy = 0
|
||||||
|
z.memPtr = 0
|
||||||
|
|
||||||
|
z.a = 0xFF
|
||||||
|
z.b = 0
|
||||||
|
z.c = 0
|
||||||
|
z.d = 0
|
||||||
|
z.e = 0
|
||||||
|
z.h = 0
|
||||||
|
z.l = 0
|
||||||
|
|
||||||
|
z.a_ = 0
|
||||||
|
z.b_ = 0
|
||||||
|
z.c_ = 0
|
||||||
|
z.d_ = 0
|
||||||
|
z.e_ = 0
|
||||||
|
z.h_ = 0
|
||||||
|
z.l_ = 0
|
||||||
|
z.f_ = 0
|
||||||
|
|
||||||
|
z.i = 0
|
||||||
|
z.r = 0
|
||||||
|
|
||||||
|
z.sf = true
|
||||||
|
z.zf = true
|
||||||
|
z.yf = true
|
||||||
|
z.hf = true
|
||||||
|
z.xf = true
|
||||||
|
z.pf = true
|
||||||
|
z.nf = true
|
||||||
|
z.cf = true
|
||||||
|
|
||||||
|
z.iffDelay = 0
|
||||||
|
z.interruptMode = 0
|
||||||
|
z.iff1 = false
|
||||||
|
z.iff2 = false
|
||||||
|
z.isHalted = false
|
||||||
|
z.intPending = false
|
||||||
|
z.nmiPending = false
|
||||||
|
z.intData = 0
|
||||||
|
z.codeCoverageEnabled = false
|
||||||
|
z.codeCoverage = make(map[uint16]bool)
|
||||||
|
return &z
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunInstruction executes the next instruction in memory + handles interrupts
|
||||||
|
func (z *Z80) RunInstruction() (uint32, *map[uint16]byte) {
|
||||||
|
z.memAccess = map[uint16]byte{}
|
||||||
|
if z.codeCoverageEnabled {
|
||||||
|
z.codeCoverage[z.pc] = true
|
||||||
|
}
|
||||||
|
pre := z.cycleCount
|
||||||
|
if z.isHalted {
|
||||||
|
z.execOpcode(0x00)
|
||||||
|
} else {
|
||||||
|
opcode := z.nextB()
|
||||||
|
z.execOpcode(opcode)
|
||||||
|
}
|
||||||
|
z.processInterrupts()
|
||||||
|
return z.cycleCount - pre, &z.memAccess
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) SetState(state *z80.CPU) {
|
||||||
|
z.cycleCount = 0
|
||||||
|
z.a = state.A
|
||||||
|
z.b = state.B
|
||||||
|
z.c = state.C
|
||||||
|
z.d = state.D
|
||||||
|
z.e = state.E
|
||||||
|
z.h = state.H
|
||||||
|
z.l = state.L
|
||||||
|
|
||||||
|
z.a_ = state.AAlt
|
||||||
|
z.b_ = state.BAlt
|
||||||
|
z.c_ = state.CAlt
|
||||||
|
z.d_ = state.DAlt
|
||||||
|
z.e_ = state.EAlt
|
||||||
|
z.h_ = state.HAlt
|
||||||
|
z.l_ = state.LAlt
|
||||||
|
|
||||||
|
z.pc = state.PC
|
||||||
|
z.sp = state.SP
|
||||||
|
z.ix = state.IX
|
||||||
|
z.iy = state.IY
|
||||||
|
z.i = state.I
|
||||||
|
z.r = state.R
|
||||||
|
z.memPtr = state.MemPtr
|
||||||
|
|
||||||
|
z.sf = state.Flags.S
|
||||||
|
z.zf = state.Flags.Z
|
||||||
|
z.yf = state.Flags.Y
|
||||||
|
z.hf = state.Flags.H
|
||||||
|
z.xf = state.Flags.X
|
||||||
|
z.pf = state.Flags.P
|
||||||
|
z.nf = state.Flags.N
|
||||||
|
z.cf = state.Flags.C
|
||||||
|
|
||||||
|
z.f_ = state.FlagsAlt.GetFlags()
|
||||||
|
|
||||||
|
//z.iff_delay = 0
|
||||||
|
z.interruptMode = state.IMode
|
||||||
|
z.iff1 = state.Iff1
|
||||||
|
z.iff2 = state.Iff2
|
||||||
|
z.isHalted = state.Halted
|
||||||
|
z.intPending = state.InterruptOccurred
|
||||||
|
z.nmiPending = false
|
||||||
|
z.intData = 0
|
||||||
|
}
|
||||||
|
func (z *Z80) GetState() *z80.CPU {
|
||||||
|
return &z80.CPU{
|
||||||
|
A: z.a,
|
||||||
|
B: z.b,
|
||||||
|
C: z.c,
|
||||||
|
D: z.d,
|
||||||
|
E: z.e,
|
||||||
|
H: z.h,
|
||||||
|
L: z.l,
|
||||||
|
AAlt: z.a_,
|
||||||
|
BAlt: z.b_,
|
||||||
|
CAlt: z.c_,
|
||||||
|
DAlt: z.d_,
|
||||||
|
EAlt: z.e_,
|
||||||
|
HAlt: z.h_,
|
||||||
|
LAlt: z.l_,
|
||||||
|
|
||||||
|
IX: z.ix,
|
||||||
|
IY: z.iy,
|
||||||
|
I: z.i,
|
||||||
|
R: z.r,
|
||||||
|
SP: z.sp,
|
||||||
|
PC: z.pc,
|
||||||
|
|
||||||
|
Flags: z.getFlags(),
|
||||||
|
FlagsAlt: z.getAltFlags(),
|
||||||
|
IMode: z.interruptMode,
|
||||||
|
Iff1: z.iff1,
|
||||||
|
Iff2: z.iff2,
|
||||||
|
Halted: z.isHalted,
|
||||||
|
DoDelayedDI: z.intPending,
|
||||||
|
DoDelayedEI: z.intPending,
|
||||||
|
CycleCount: z.cycleCount,
|
||||||
|
InterruptOccurred: z.intPending,
|
||||||
|
MemPtr: z.memPtr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) getFlags() z80.FlagsType {
|
||||||
|
return z80.FlagsType{
|
||||||
|
S: z.sf,
|
||||||
|
Z: z.zf,
|
||||||
|
Y: z.yf,
|
||||||
|
H: z.hf,
|
||||||
|
X: z.xf,
|
||||||
|
P: z.pf,
|
||||||
|
N: z.nf,
|
||||||
|
C: z.cf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) getAltFlags() z80.FlagsType {
|
||||||
|
return z80.FlagsType{
|
||||||
|
S: z.f_&0x80 != 0,
|
||||||
|
Z: z.f_&0x40 != 0,
|
||||||
|
Y: z.f_&0x20 != 0,
|
||||||
|
H: z.f_&0x10 != 0,
|
||||||
|
X: z.f_&0x08 != 0,
|
||||||
|
P: z.f_&0x04 != 0,
|
||||||
|
N: z.f_&0x02 != 0,
|
||||||
|
C: z.f_&0x01 != 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) PC() uint16 {
|
||||||
|
return z.pc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) ClearCodeCoverage() {
|
||||||
|
clear(z.codeCoverage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) SetCodeCoverage(enabled bool) {
|
||||||
|
z.codeCoverageEnabled = enabled
|
||||||
|
if !enabled {
|
||||||
|
clear(z.codeCoverage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) CodeCoverage() map[uint16]bool {
|
||||||
|
return z.codeCoverage
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) SetExtendedStack(enabled bool) {
|
||||||
|
z.extendedStackEnabled = enabled
|
||||||
|
if enabled {
|
||||||
|
for addr := 0; addr < 65536; addr++ {
|
||||||
|
z.extendedStack[addr] = PushValueTypeDefault
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) ExtendedStack() ([]byte, error) {
|
||||||
|
var err error
|
||||||
|
if !z.extendedStackEnabled {
|
||||||
|
err = errors.New("error, z80: ExtendedStack disabled")
|
||||||
|
}
|
||||||
|
return z.extendedStack[:], err
|
||||||
|
}
|
||||||
207
src/z80/c99/helper.go
Normal file
207
src/z80/c99/helper.go
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
package c99
|
||||||
|
|
||||||
|
import log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
func (z *Z80) rb(addr uint16) byte {
|
||||||
|
z.memAccess[addr] = MemAccessRead
|
||||||
|
return z.core.MemRead(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) wb(addr uint16, val byte) {
|
||||||
|
z.memAccess[addr] = MemAccessWrite
|
||||||
|
z.core.MemWrite(addr, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) rw(addr uint16) uint16 {
|
||||||
|
z.memAccess[addr] = MemAccessRead
|
||||||
|
z.memAccess[addr+1] = MemAccessRead
|
||||||
|
return (uint16(z.core.MemRead(addr+1)) << 8) | uint16(z.core.MemRead(addr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) ww(addr uint16, val uint16) {
|
||||||
|
z.memAccess[addr] = MemAccessWrite
|
||||||
|
z.memAccess[addr+1] = MemAccessWrite
|
||||||
|
z.core.MemWrite(addr, byte(val))
|
||||||
|
z.core.MemWrite(addr+1, byte(val>>8))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) pushW(val uint16) {
|
||||||
|
z.sp -= 2
|
||||||
|
z.ww(z.sp, val)
|
||||||
|
z.extendedStack[z.sp] = PushValueTypePush
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) popW() uint16 {
|
||||||
|
z.sp += 2
|
||||||
|
return z.rw(z.sp - 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) nextB() byte {
|
||||||
|
b := z.core.MemRead(z.pc)
|
||||||
|
z.pc++
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) nextW() uint16 {
|
||||||
|
w := (uint16(z.core.MemRead(z.pc+1)) << 8) | uint16(z.core.MemRead(z.pc))
|
||||||
|
z.pc += 2
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) bc() uint16 {
|
||||||
|
return (uint16(z.b) << 8) | uint16(z.c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) de() uint16 {
|
||||||
|
return (uint16(z.d) << 8) | uint16(z.e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) hl() uint16 {
|
||||||
|
return (uint16(z.h) << 8) | uint16(z.l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) setBC(val uint16) {
|
||||||
|
z.b = byte(val >> 8)
|
||||||
|
z.c = byte(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) setDE(val uint16) {
|
||||||
|
z.d = byte(val >> 8)
|
||||||
|
z.e = byte(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) setHL(val uint16) {
|
||||||
|
z.h = byte(val >> 8)
|
||||||
|
z.l = byte(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) f() byte {
|
||||||
|
val := byte(0)
|
||||||
|
if z.cf {
|
||||||
|
val |= 0x01
|
||||||
|
}
|
||||||
|
if z.nf {
|
||||||
|
val |= 0x02
|
||||||
|
}
|
||||||
|
if z.pf {
|
||||||
|
val |= 0x04
|
||||||
|
}
|
||||||
|
if z.xf {
|
||||||
|
val |= 0x08
|
||||||
|
}
|
||||||
|
if z.hf {
|
||||||
|
val |= 0x10
|
||||||
|
}
|
||||||
|
if z.yf {
|
||||||
|
val |= 0x20
|
||||||
|
}
|
||||||
|
if z.zf {
|
||||||
|
val |= 0x40
|
||||||
|
}
|
||||||
|
if z.sf {
|
||||||
|
val |= 0x80
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) setF(val byte) {
|
||||||
|
z.cf = val&1 != 0
|
||||||
|
z.nf = (val>>1)&1 != 0
|
||||||
|
z.pf = (val>>2)&1 != 0
|
||||||
|
z.xf = (val>>3)&1 != 0
|
||||||
|
z.hf = (val>>4)&1 != 0
|
||||||
|
z.yf = (val>>5)&1 != 0
|
||||||
|
z.zf = (val>>6)&1 != 0
|
||||||
|
z.sf = (val>>7)&1 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// increments R, keeping the highest byte intact
|
||||||
|
func (z *Z80) incR() {
|
||||||
|
z.r = (z.r & 0x80) | ((z.r + 1) & 0x7f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolToInt32(b bool) int32 {
|
||||||
|
if b {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns if there was a carry between bit "bit_no" and "bit_no - 1" when
|
||||||
|
// executing "a + b + cy"
|
||||||
|
func carry(bitNo int, a uint16, b uint16, cy bool) bool {
|
||||||
|
result := int32(a) + int32(b) + boolToInt32(cy)
|
||||||
|
carry := result ^ int32(a) ^ int32(b)
|
||||||
|
return (carry & (1 << bitNo)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// parity returns the parity of byte: 0 if odd, else 1
|
||||||
|
func parity(val byte) bool {
|
||||||
|
ones := byte(0)
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
ones += (val >> i) & 1
|
||||||
|
}
|
||||||
|
return (ones & 1) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateXY set undocumented 3rd (X) and 5th (Y) flags
|
||||||
|
func (z *Z80) updateXY(result byte) {
|
||||||
|
z.yf = result&0x20 != 0
|
||||||
|
z.xf = result&0x08 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) DebugOutput() {
|
||||||
|
log.Debugf("PC: %04X, AF: %04X, BC: %04X, DE: %04X, HL: %04X, SP: %04X, IX: %04X, IY: %04X, I: %02X, R: %02X",
|
||||||
|
z.pc, (uint16(z.a)<<8)|uint16(z.f()), z.bc(), z.de(), z.hl(), z.sp,
|
||||||
|
z.ix, z.iy, z.i, z.r)
|
||||||
|
|
||||||
|
log.Debugf("\t(%02X %02X %02X %02X), cycleCount: %d\n", z.rb(z.pc), z.rb(z.pc+1),
|
||||||
|
z.rb(z.pc+2), z.rb(z.pc+3), z.cycleCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *Z80) Reset() {
|
||||||
|
z.cycleCount = 0
|
||||||
|
z.pc = 0
|
||||||
|
z.sp = 0xFFFF
|
||||||
|
z.ix = 0
|
||||||
|
z.iy = 0
|
||||||
|
z.memPtr = 0
|
||||||
|
|
||||||
|
z.a = 0xFF
|
||||||
|
z.b = 0
|
||||||
|
z.c = 0
|
||||||
|
z.d = 0
|
||||||
|
z.e = 0
|
||||||
|
z.h = 0
|
||||||
|
z.l = 0
|
||||||
|
|
||||||
|
z.a_ = 0
|
||||||
|
z.b_ = 0
|
||||||
|
z.c_ = 0
|
||||||
|
z.d_ = 0
|
||||||
|
z.e_ = 0
|
||||||
|
z.h_ = 0
|
||||||
|
z.l_ = 0
|
||||||
|
z.f_ = 0
|
||||||
|
|
||||||
|
z.i = 0
|
||||||
|
z.r = 0
|
||||||
|
|
||||||
|
z.sf = true
|
||||||
|
z.zf = true
|
||||||
|
z.yf = true
|
||||||
|
z.hf = true
|
||||||
|
z.xf = true
|
||||||
|
z.pf = true
|
||||||
|
z.nf = true
|
||||||
|
z.cf = true
|
||||||
|
|
||||||
|
z.iffDelay = 0
|
||||||
|
z.interruptMode = 0
|
||||||
|
z.iff1 = false
|
||||||
|
z.iff2 = false
|
||||||
|
z.isHalted = false
|
||||||
|
z.intPending = false
|
||||||
|
z.nmiPending = false
|
||||||
|
z.intData = 0
|
||||||
|
}
|
||||||
1255
src/z80/c99/opcodes.go
Normal file
1255
src/z80/c99/opcodes.go
Normal file
File diff suppressed because it is too large
Load Diff
162
src/z80/c99/opcodesCB.go
Normal file
162
src/z80/c99/opcodesCB.go
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
package c99
|
||||||
|
|
||||||
|
import log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
// executes a CB opcode
|
||||||
|
func (z *Z80) execOpcodeCB(opcode byte) {
|
||||||
|
z.cycleCount += 8
|
||||||
|
z.incR()
|
||||||
|
|
||||||
|
// decoding instructions from http://z80.info/decoding.htm#cb
|
||||||
|
x_ := (opcode >> 6) & 3 // 0b11
|
||||||
|
y_ := (opcode >> 3) & 7 // 0b111
|
||||||
|
z_ := opcode & 7 // 0b111
|
||||||
|
|
||||||
|
var hl byte
|
||||||
|
v := byte(0)
|
||||||
|
reg := &v
|
||||||
|
switch z_ {
|
||||||
|
case 0:
|
||||||
|
reg = &z.b
|
||||||
|
case 1:
|
||||||
|
reg = &z.c
|
||||||
|
case 2:
|
||||||
|
reg = &z.d
|
||||||
|
case 3:
|
||||||
|
reg = &z.e
|
||||||
|
case 4:
|
||||||
|
reg = &z.h
|
||||||
|
case 5:
|
||||||
|
reg = &z.l
|
||||||
|
case 6:
|
||||||
|
hl = z.rb(z.hl())
|
||||||
|
reg = &hl
|
||||||
|
case 7:
|
||||||
|
reg = &z.a
|
||||||
|
}
|
||||||
|
|
||||||
|
switch x_ {
|
||||||
|
case 0:
|
||||||
|
// rot[y] r[z]
|
||||||
|
switch y_ {
|
||||||
|
case 0:
|
||||||
|
*reg = z.cbRlc(*reg)
|
||||||
|
case 1:
|
||||||
|
*reg = z.cbRrc(*reg)
|
||||||
|
case 2:
|
||||||
|
*reg = z.cbRl(*reg)
|
||||||
|
case 3:
|
||||||
|
*reg = z.cbRr(*reg)
|
||||||
|
case 4:
|
||||||
|
*reg = z.cbSla(*reg)
|
||||||
|
case 5:
|
||||||
|
*reg = z.cbSra(*reg)
|
||||||
|
case 6:
|
||||||
|
*reg = z.cbSll(*reg)
|
||||||
|
case 7:
|
||||||
|
*reg = z.cbSrl(*reg)
|
||||||
|
}
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
// BIT y, r[z]
|
||||||
|
z.cbBit(*reg, y_)
|
||||||
|
|
||||||
|
// in bit (hl), x/y flags are handled differently:
|
||||||
|
if z_ == 6 {
|
||||||
|
z.updateXY(byte(z.memPtr >> 8))
|
||||||
|
z.cycleCount += 4
|
||||||
|
}
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
*reg &= ^(1 << y_) // RES y, r[z]
|
||||||
|
case 3:
|
||||||
|
*reg |= 1 << y_ // SET y, r[z]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x_ == 0 || x_ == 2 || x_ == 3) && z_ == 6 {
|
||||||
|
z.cycleCount += 7
|
||||||
|
}
|
||||||
|
|
||||||
|
if reg == &hl {
|
||||||
|
z.wb(z.hl(), hl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// execOpcodeDcb executes a displaced CB opcode (DDCB or FDCB)
|
||||||
|
func (z *Z80) execOpcodeDcb(opcode byte, addr uint16) {
|
||||||
|
val := z.rb(addr)
|
||||||
|
result := byte(0)
|
||||||
|
|
||||||
|
// decoding instructions from http://z80.info/decoding.htm#ddcb
|
||||||
|
x_ := (opcode >> 6) & 3 // 0b11
|
||||||
|
y_ := (opcode >> 3) & 7 // 0b111
|
||||||
|
z_ := opcode & 7 // 0b111
|
||||||
|
|
||||||
|
switch x_ {
|
||||||
|
case 0:
|
||||||
|
// rot[y] (iz+d)
|
||||||
|
switch y_ {
|
||||||
|
case 0:
|
||||||
|
result = z.cbRlc(val)
|
||||||
|
case 1:
|
||||||
|
result = z.cbRrc(val)
|
||||||
|
case 2:
|
||||||
|
result = z.cbRl(val)
|
||||||
|
case 3:
|
||||||
|
result = z.cbRr(val)
|
||||||
|
case 4:
|
||||||
|
result = z.cbSla(val)
|
||||||
|
case 5:
|
||||||
|
result = z.cbSra(val)
|
||||||
|
case 6:
|
||||||
|
result = z.cbSll(val)
|
||||||
|
case 7:
|
||||||
|
result = z.cbSrl(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
// bit y,(iz+d)
|
||||||
|
result = z.cbBit(val, y_)
|
||||||
|
z.updateXY(byte(addr >> 8))
|
||||||
|
case 2:
|
||||||
|
result = val & ^(1 << y_) // res y, (iz+d)
|
||||||
|
case 3:
|
||||||
|
result = val | (1 << y_) // set y, (iz+d)
|
||||||
|
|
||||||
|
default:
|
||||||
|
log.Errorf("Unknown XYCB opcode: %02X\n", opcode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ld r[z], rot[y] (iz+d)
|
||||||
|
// ld r[z], res y,(iz+d)
|
||||||
|
// ld r[z], set y,(iz+d)
|
||||||
|
if x_ != 1 && z_ != 6 {
|
||||||
|
switch z_ {
|
||||||
|
case 0:
|
||||||
|
z.b = result
|
||||||
|
case 1:
|
||||||
|
z.c = result
|
||||||
|
case 2:
|
||||||
|
z.d = result
|
||||||
|
case 3:
|
||||||
|
z.e = result
|
||||||
|
case 4:
|
||||||
|
z.h = result
|
||||||
|
case 5:
|
||||||
|
z.l = result
|
||||||
|
// always false
|
||||||
|
//case 6:
|
||||||
|
// z.wb(z.hl(), result)
|
||||||
|
case 7:
|
||||||
|
z.a = result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if x_ == 1 {
|
||||||
|
// bit instructions take 20 cycles, others take 23
|
||||||
|
z.cycleCount += 20
|
||||||
|
} else {
|
||||||
|
z.wb(addr, result)
|
||||||
|
z.cycleCount += 23
|
||||||
|
}
|
||||||
|
}
|
||||||
206
src/z80/c99/opcodesDDFD.go
Normal file
206
src/z80/c99/opcodesDDFD.go
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
package c99
|
||||||
|
|
||||||
|
// executes a DD/FD opcode (IZ = IX or IY)
|
||||||
|
func (z *Z80) execOpcodeDDFD(opcode byte, iz *uint16) {
|
||||||
|
z.cycleCount += uint32(cyclesDDFD[opcode])
|
||||||
|
z.incR()
|
||||||
|
|
||||||
|
switch opcode {
|
||||||
|
case 0xE1:
|
||||||
|
*iz = z.popW() // pop iz
|
||||||
|
case 0xE5:
|
||||||
|
z.pushW(*iz) // push iz
|
||||||
|
case 0xE9:
|
||||||
|
// jp iz
|
||||||
|
z.pc = *iz
|
||||||
|
//z.jump(*iz)
|
||||||
|
case 0x09:
|
||||||
|
z.addIZ(iz, z.bc()) // add iz,bc
|
||||||
|
case 0x19:
|
||||||
|
z.addIZ(iz, z.de()) // add iz,de
|
||||||
|
case 0x29:
|
||||||
|
z.addIZ(iz, *iz) // add iz,iz
|
||||||
|
case 0x39:
|
||||||
|
z.addIZ(iz, z.sp) // add iz,sp
|
||||||
|
|
||||||
|
case 0x84:
|
||||||
|
z.a = z.addB(z.a, byte(*iz>>8), false) // add a,izh
|
||||||
|
case 0x85:
|
||||||
|
z.a = z.addB(z.a, byte(*iz), false) // add a,izl
|
||||||
|
case 0x8C:
|
||||||
|
z.a = z.addB(z.a, byte(*iz>>8), z.cf) // adc a,izh
|
||||||
|
case 0x8D:
|
||||||
|
z.a = z.addB(z.a, byte(*iz), z.cf) // adc a,izl
|
||||||
|
case 0x86:
|
||||||
|
z.a = z.addB(z.a, z.rb(z.displace(*iz, z.nextB())), false) // add a,(iz+*)
|
||||||
|
case 0x8E:
|
||||||
|
z.a = z.addB(z.a, z.rb(z.displace(*iz, z.nextB())), z.cf) // adc a,(iz+*)
|
||||||
|
case 0x96:
|
||||||
|
z.a = z.subB(z.a, z.rb(z.displace(*iz, z.nextB())), false) // sub (iz+*)
|
||||||
|
case 0x9E:
|
||||||
|
z.a = z.subB(z.a, z.rb(z.displace(*iz, z.nextB())), z.cf) // sbc (iz+*)
|
||||||
|
case 0x94:
|
||||||
|
z.a = z.subB(z.a, byte(*iz>>8), false) // sub izh
|
||||||
|
case 0x95:
|
||||||
|
z.a = z.subB(z.a, byte(*iz), false) // sub izl
|
||||||
|
case 0x9C:
|
||||||
|
z.a = z.subB(z.a, byte(*iz>>8), z.cf) // sbc izh
|
||||||
|
case 0x9D:
|
||||||
|
z.a = z.subB(z.a, byte(*iz), z.cf) // sbc izl
|
||||||
|
|
||||||
|
case 0xA6:
|
||||||
|
z.lAnd(z.rb(z.displace(*iz, z.nextB()))) // and (iz+*)
|
||||||
|
case 0xA4:
|
||||||
|
z.lAnd(byte(*iz >> 8)) // and izh
|
||||||
|
case 0xA5:
|
||||||
|
z.lAnd(byte(*iz)) // and izl
|
||||||
|
|
||||||
|
case 0xAE:
|
||||||
|
z.lXor(z.rb(z.displace(*iz, z.nextB()))) // xor (iz+*)
|
||||||
|
case 0xAC:
|
||||||
|
z.lXor(byte(*iz >> 8)) // xor izh
|
||||||
|
case 0xAD:
|
||||||
|
z.lXor(byte(*iz)) // xor izl
|
||||||
|
case 0xB6:
|
||||||
|
z.lOr(z.rb(z.displace(*iz, z.nextB()))) // or (iz+*)
|
||||||
|
case 0xB4:
|
||||||
|
z.lOr(byte(*iz >> 8)) // or izh
|
||||||
|
case 0xB5:
|
||||||
|
z.lOr(byte(*iz)) // or izl
|
||||||
|
case 0xBE:
|
||||||
|
z.cp(z.rb(z.displace(*iz, z.nextB()))) // cp (iz+*)
|
||||||
|
case 0xBC:
|
||||||
|
z.cp(byte(*iz >> 8)) // cp izh
|
||||||
|
case 0xBD:
|
||||||
|
z.cp(byte(*iz)) // cp izl
|
||||||
|
case 0x23:
|
||||||
|
*iz += 1 // inc iz
|
||||||
|
case 0x2B:
|
||||||
|
*iz -= 1 // dec iz
|
||||||
|
case 0x34:
|
||||||
|
// inc (iz+*)
|
||||||
|
addr := z.displace(*iz, z.nextB())
|
||||||
|
z.wb(addr, z.inc(z.rb(addr)))
|
||||||
|
case 0x35:
|
||||||
|
// dec (iz+*)
|
||||||
|
addr := z.displace(*iz, z.nextB())
|
||||||
|
z.wb(addr, z.dec(z.rb(addr)))
|
||||||
|
case 0x24:
|
||||||
|
*iz = (*iz & 0x00ff) | (uint16(z.inc(byte(*iz>>8))) << 8) // inc izh
|
||||||
|
case 0x25:
|
||||||
|
*iz = (*iz & 0x00ff) | (uint16(z.dec(byte(*iz>>8))) << 8) // dec izh
|
||||||
|
case 0x2C:
|
||||||
|
*iz = (*iz & 0xff00) | uint16(z.inc(byte(*iz))) // inc izl
|
||||||
|
case 0x2D:
|
||||||
|
*iz = (*iz & 0xff00) | uint16(z.dec(byte(*iz))) // dec izl
|
||||||
|
case 0x2A:
|
||||||
|
addr := z.nextW()
|
||||||
|
*iz = z.rw(addr) // ld iz,(**)
|
||||||
|
z.memPtr = addr + 1
|
||||||
|
case 0x22:
|
||||||
|
addr := z.nextW()
|
||||||
|
z.ww(addr, *iz) // ld (**),iz
|
||||||
|
z.memPtr = addr + 1
|
||||||
|
case 0x21:
|
||||||
|
*iz = z.nextW() // ld iz,**
|
||||||
|
case 0x36:
|
||||||
|
// ld (iz+*),*
|
||||||
|
addr := z.displace(*iz, z.nextB())
|
||||||
|
z.wb(addr, z.nextB())
|
||||||
|
case 0x70:
|
||||||
|
z.wb(z.displace(*iz, z.nextB()), z.b) // ld (iz+*),b
|
||||||
|
case 0x71:
|
||||||
|
z.wb(z.displace(*iz, z.nextB()), z.c) // ld (iz+*),c
|
||||||
|
case 0x72:
|
||||||
|
z.wb(z.displace(*iz, z.nextB()), z.d) // ld (iz+*),d
|
||||||
|
case 0x73:
|
||||||
|
z.wb(z.displace(*iz, z.nextB()), z.e) // ld (iz+*),e
|
||||||
|
case 0x74:
|
||||||
|
z.wb(z.displace(*iz, z.nextB()), z.h) // ld (iz+*),h
|
||||||
|
case 0x75:
|
||||||
|
z.wb(z.displace(*iz, z.nextB()), z.l) // ld (iz+*),l
|
||||||
|
case 0x77:
|
||||||
|
z.wb(z.displace(*iz, z.nextB()), z.a) // ld (iz+*),a
|
||||||
|
case 0x46:
|
||||||
|
z.b = z.rb(z.displace(*iz, z.nextB())) // ld b,(iz+*)
|
||||||
|
case 0x4E:
|
||||||
|
z.c = z.rb(z.displace(*iz, z.nextB())) // ld c,(iz+*)
|
||||||
|
case 0x56:
|
||||||
|
z.d = z.rb(z.displace(*iz, z.nextB())) // ld d,(iz+*)
|
||||||
|
case 0x5E:
|
||||||
|
z.e = z.rb(z.displace(*iz, z.nextB())) // ld e,(iz+*)
|
||||||
|
case 0x66:
|
||||||
|
z.h = z.rb(z.displace(*iz, z.nextB())) // ld h,(iz+*)
|
||||||
|
case 0x6E:
|
||||||
|
z.l = z.rb(z.displace(*iz, z.nextB())) // ld l,(iz+*)
|
||||||
|
case 0x7E:
|
||||||
|
z.a = z.rb(z.displace(*iz, z.nextB())) // ld a,(iz+*)
|
||||||
|
case 0x44:
|
||||||
|
z.b = byte(*iz >> 8) // ld b,izh
|
||||||
|
case 0x4C:
|
||||||
|
z.c = byte(*iz >> 8) // ld c,izh
|
||||||
|
case 0x54:
|
||||||
|
z.d = byte(*iz >> 8) // ld d,izh
|
||||||
|
case 0x5C:
|
||||||
|
z.e = byte(*iz >> 8) // ld e,izh
|
||||||
|
case 0x7C:
|
||||||
|
z.a = byte(*iz >> 8) // ld a,izh
|
||||||
|
case 0x45:
|
||||||
|
z.b = byte(*iz) // ld b,izl
|
||||||
|
case 0x4D:
|
||||||
|
z.c = byte(*iz) // ld c,izl
|
||||||
|
case 0x55:
|
||||||
|
z.d = byte(*iz) // ld d,izl
|
||||||
|
case 0x5D:
|
||||||
|
z.e = byte(*iz) // ld e,izl
|
||||||
|
case 0x7D:
|
||||||
|
z.a = byte(*iz) // ld a,izl
|
||||||
|
case 0x60:
|
||||||
|
*iz = (*iz & 0x00ff) | (uint16(z.b) << 8) // ld izh,b
|
||||||
|
case 0x61:
|
||||||
|
*iz = (*iz & 0x00ff) | (uint16(z.c) << 8) // ld izh,c
|
||||||
|
case 0x62:
|
||||||
|
*iz = (*iz & 0x00ff) | (uint16(z.d) << 8) // ld izh,d
|
||||||
|
case 0x63:
|
||||||
|
*iz = (*iz & 0x00ff) | (uint16(z.e) << 8) // ld izh,e
|
||||||
|
case 0x64: // ld izh,izh
|
||||||
|
case 0x65:
|
||||||
|
*iz = ((*iz & 0x00ff) << 8) | (*iz & 0x00ff) // ld izh,izl
|
||||||
|
case 0x67:
|
||||||
|
*iz = (uint16(z.a) << 8) | (*iz & 0x00ff) // ld izh,a
|
||||||
|
case 0x26:
|
||||||
|
*iz = (uint16(z.nextB()) << 8) | (*iz & 0x00ff) // ld izh,*
|
||||||
|
case 0x68:
|
||||||
|
*iz = (*iz & 0xff00) | uint16(z.b) // ld izl,b
|
||||||
|
case 0x69:
|
||||||
|
*iz = (*iz & 0xff00) | uint16(z.c) // ld izl,c
|
||||||
|
case 0x6A:
|
||||||
|
*iz = (*iz & 0xff00) | uint16(z.d) // ld izl,d
|
||||||
|
case 0x6B:
|
||||||
|
*iz = (*iz & 0xff00) | uint16(z.e) // ld izl,e
|
||||||
|
case 0x6C:
|
||||||
|
*iz = (*iz & 0xff00) | (*iz >> 8) // ld izl,izh
|
||||||
|
case 0x6D: // ld izl,izl
|
||||||
|
case 0x6F:
|
||||||
|
*iz = (*iz & 0xff00) | uint16(z.a) // ld izl,a
|
||||||
|
case 0x2E:
|
||||||
|
*iz = (*iz & 0xff00) | uint16(z.nextB()) // ld izl,*
|
||||||
|
case 0xF9:
|
||||||
|
z.sp = *iz // ld sp,iz
|
||||||
|
case 0xE3:
|
||||||
|
// ex (sp),iz
|
||||||
|
val := z.rw(z.sp)
|
||||||
|
z.ww(z.sp, *iz)
|
||||||
|
*iz = val
|
||||||
|
z.memPtr = val
|
||||||
|
case 0xCB:
|
||||||
|
addr := z.displace(*iz, z.nextB())
|
||||||
|
op := z.nextB()
|
||||||
|
z.execOpcodeDcb(op, addr)
|
||||||
|
default:
|
||||||
|
// any other FD/DD opcode behaves as a non-prefixed opcode:
|
||||||
|
z.execOpcode(opcode)
|
||||||
|
// R should not be incremented twice:
|
||||||
|
z.incR()
|
||||||
|
}
|
||||||
|
}
|
||||||
281
src/z80/c99/opcodesED.go
Normal file
281
src/z80/c99/opcodesED.go
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
package c99
|
||||||
|
|
||||||
|
import log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
// executes a ED opcode
|
||||||
|
func (z *Z80) execOpcodeED(opcode byte) {
|
||||||
|
z.cycleCount += uint32(cyclesED[opcode])
|
||||||
|
z.incR()
|
||||||
|
switch opcode {
|
||||||
|
case 0x47:
|
||||||
|
z.i = z.a // ld i,a
|
||||||
|
case 0x4F:
|
||||||
|
z.r = z.a // ld r,a
|
||||||
|
case 0x57:
|
||||||
|
// ld a,i
|
||||||
|
z.a = z.i
|
||||||
|
z.sf = z.a&0x80 != 0
|
||||||
|
z.zf = z.a == 0
|
||||||
|
z.hf = false
|
||||||
|
z.nf = false
|
||||||
|
z.pf = z.iff2
|
||||||
|
z.updateXY(z.a)
|
||||||
|
case 0x5F:
|
||||||
|
// ld a,r
|
||||||
|
z.a = z.r
|
||||||
|
z.sf = z.a&0x80 != 0
|
||||||
|
z.zf = z.a == 0
|
||||||
|
z.hf = false
|
||||||
|
z.nf = false
|
||||||
|
z.pf = z.iff2
|
||||||
|
case 0x45, 0x55, 0x5D, 0x65, 0x6D, 0x75, 0x7D:
|
||||||
|
// retn
|
||||||
|
z.iff1 = z.iff2
|
||||||
|
z.ret()
|
||||||
|
case 0x4D:
|
||||||
|
z.ret() // reti
|
||||||
|
|
||||||
|
case 0xA0:
|
||||||
|
z.ldi() // ldi
|
||||||
|
case 0xB0:
|
||||||
|
{
|
||||||
|
z.ldi()
|
||||||
|
|
||||||
|
if z.bc() != 0 {
|
||||||
|
z.pc -= 2
|
||||||
|
z.cycleCount += 5
|
||||||
|
z.memPtr = z.pc + 1
|
||||||
|
}
|
||||||
|
} // ldir
|
||||||
|
|
||||||
|
case 0xA8:
|
||||||
|
z.ldd() // ldd
|
||||||
|
case 0xB8:
|
||||||
|
{
|
||||||
|
z.ldd()
|
||||||
|
|
||||||
|
if z.bc() != 0 {
|
||||||
|
z.pc -= 2
|
||||||
|
z.cycleCount += 5
|
||||||
|
z.memPtr = z.pc + 1
|
||||||
|
}
|
||||||
|
} // lddr
|
||||||
|
|
||||||
|
case 0xA1:
|
||||||
|
z.cpi() // cpi
|
||||||
|
case 0xA9:
|
||||||
|
z.cpd() // cpd
|
||||||
|
case 0xB1:
|
||||||
|
// cpir
|
||||||
|
z.cpi()
|
||||||
|
if z.bc() != 0 && !z.zf {
|
||||||
|
z.pc -= 2
|
||||||
|
z.cycleCount += 5
|
||||||
|
z.memPtr = z.pc + 1
|
||||||
|
} else {
|
||||||
|
//z.mem_ptr++
|
||||||
|
}
|
||||||
|
//z.cpir()
|
||||||
|
case 0xB9:
|
||||||
|
// cpdr
|
||||||
|
z.cpd()
|
||||||
|
if z.bc() != 0 && !z.zf {
|
||||||
|
z.pc -= 2
|
||||||
|
z.cycleCount += 5
|
||||||
|
z.memPtr = z.pc + 1
|
||||||
|
} else {
|
||||||
|
//z.mem_ptr++
|
||||||
|
}
|
||||||
|
case 0x40:
|
||||||
|
z.inRC(&z.b) // in b, (c)
|
||||||
|
z.memPtr = z.bc() + 1
|
||||||
|
case 0x48:
|
||||||
|
z.memPtr = z.bc() + 1
|
||||||
|
z.inRC(&z.c) // in c, (c)
|
||||||
|
z.updateXY(z.c)
|
||||||
|
//case 0x4e:
|
||||||
|
// ld c,(iy+dd)
|
||||||
|
|
||||||
|
case 0x50:
|
||||||
|
z.inRC(&z.d) // in d, (c)
|
||||||
|
z.memPtr = z.bc() + 1
|
||||||
|
case 0x58:
|
||||||
|
// in e, (c)
|
||||||
|
z.inRC(&z.e)
|
||||||
|
z.memPtr = z.bc() + 1
|
||||||
|
z.updateXY(z.e)
|
||||||
|
case 0x60:
|
||||||
|
z.inRC(&z.h) // in h, (c)
|
||||||
|
z.memPtr = z.bc() + 1
|
||||||
|
case 0x68:
|
||||||
|
z.inRC(&z.l) // in l, (c)
|
||||||
|
z.memPtr = z.bc() + 1
|
||||||
|
z.updateXY(z.l)
|
||||||
|
case 0x70:
|
||||||
|
// in (c)
|
||||||
|
var val byte
|
||||||
|
z.inRC(&val)
|
||||||
|
z.memPtr = z.bc() + 1
|
||||||
|
case 0x78:
|
||||||
|
// in a, (c)
|
||||||
|
z.inRC(&z.a)
|
||||||
|
z.memPtr = z.bc() + 1
|
||||||
|
z.updateXY(z.a)
|
||||||
|
case 0xA2:
|
||||||
|
z.ini() // ini
|
||||||
|
case 0xB2:
|
||||||
|
// inir
|
||||||
|
z.ini()
|
||||||
|
if z.b > 0 {
|
||||||
|
z.pc -= 2
|
||||||
|
z.cycleCount += 5
|
||||||
|
}
|
||||||
|
case 0xAA:
|
||||||
|
// ind
|
||||||
|
z.ind()
|
||||||
|
case 0xBA:
|
||||||
|
// indr
|
||||||
|
z.ind()
|
||||||
|
if z.b > 0 {
|
||||||
|
z.pc -= 2
|
||||||
|
z.cycleCount += 5
|
||||||
|
}
|
||||||
|
case 0x41:
|
||||||
|
z.core.IOWrite(z.bc(), z.b) // out (c), b
|
||||||
|
z.memPtr = z.bc() + 1
|
||||||
|
case 0x49:
|
||||||
|
z.core.IOWrite(z.bc(), z.c) // out (c), c
|
||||||
|
z.memPtr = z.bc() + 1
|
||||||
|
case 0x51:
|
||||||
|
z.core.IOWrite(z.bc(), z.d) // out (c), d
|
||||||
|
z.memPtr = z.bc() + 1
|
||||||
|
case 0x59:
|
||||||
|
z.core.IOWrite(z.bc(), z.e) // out (c), e
|
||||||
|
z.memPtr = z.bc() + 1
|
||||||
|
case 0x61:
|
||||||
|
z.core.IOWrite(z.bc(), z.h) // out (c), h
|
||||||
|
z.memPtr = z.bc() + 1
|
||||||
|
case 0x69:
|
||||||
|
z.core.IOWrite(z.bc(), z.l) // out (c), l
|
||||||
|
z.memPtr = z.bc() + 1
|
||||||
|
case 0x71:
|
||||||
|
z.core.IOWrite(z.bc(), 0) // out (c), 0
|
||||||
|
z.memPtr = z.bc() + 1
|
||||||
|
case 0x79:
|
||||||
|
// out (c), a
|
||||||
|
z.core.IOWrite(z.bc(), z.a)
|
||||||
|
z.memPtr = z.bc() + 1
|
||||||
|
case 0xA3:
|
||||||
|
z.outi() // outi
|
||||||
|
case 0xB3:
|
||||||
|
// otir
|
||||||
|
z.outi()
|
||||||
|
if z.b > 0 {
|
||||||
|
z.pc -= 2
|
||||||
|
z.cycleCount += 5
|
||||||
|
}
|
||||||
|
case 0xAB:
|
||||||
|
z.outd() // outd
|
||||||
|
case 0xBB:
|
||||||
|
// otdr
|
||||||
|
z.outd()
|
||||||
|
if z.b > 0 {
|
||||||
|
z.cycleCount += 5
|
||||||
|
z.pc -= 2
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0x42:
|
||||||
|
z.sbcHL(z.bc()) // sbc hl,bc
|
||||||
|
case 0x52:
|
||||||
|
z.sbcHL(z.de()) // sbc hl,de
|
||||||
|
case 0x62:
|
||||||
|
z.sbcHL(z.hl()) // sbc hl,hl
|
||||||
|
case 0x72:
|
||||||
|
z.sbcHL(z.sp) // sbc hl,sp
|
||||||
|
case 0x4A:
|
||||||
|
z.adcHL(z.bc()) // adc hl,bc
|
||||||
|
case 0x5A:
|
||||||
|
z.adcHL(z.de()) // adc hl,de
|
||||||
|
case 0x6A:
|
||||||
|
z.adcHL(z.hl()) // adc hl,hl
|
||||||
|
case 0x7A:
|
||||||
|
z.adcHL(z.sp) // adc hl,sp
|
||||||
|
case 0x43:
|
||||||
|
// ld (**), bc
|
||||||
|
addr := z.nextW()
|
||||||
|
z.ww(addr, z.bc())
|
||||||
|
z.memPtr = addr + 1
|
||||||
|
case 0x53:
|
||||||
|
// ld (**), de
|
||||||
|
addr := z.nextW()
|
||||||
|
z.ww(addr, z.de())
|
||||||
|
z.memPtr = addr + 1
|
||||||
|
case 0x63:
|
||||||
|
// ld (**), hl
|
||||||
|
addr := z.nextW()
|
||||||
|
z.ww(addr, z.hl())
|
||||||
|
z.memPtr = addr + 1
|
||||||
|
case 0x73:
|
||||||
|
// ld (**), hl
|
||||||
|
addr := z.nextW()
|
||||||
|
z.ww(addr, z.sp)
|
||||||
|
z.memPtr = addr + 1
|
||||||
|
case 0x4B:
|
||||||
|
// ld bc, (**)
|
||||||
|
addr := z.nextW()
|
||||||
|
z.setBC(z.rw(addr))
|
||||||
|
z.memPtr = addr + 1
|
||||||
|
case 0x5B:
|
||||||
|
// ld de, (**)
|
||||||
|
addr := z.nextW()
|
||||||
|
z.setDE(z.rw(addr))
|
||||||
|
z.memPtr = addr + 1
|
||||||
|
case 0x6B:
|
||||||
|
// ld hl, (**)
|
||||||
|
addr := z.nextW()
|
||||||
|
z.setHL(z.rw(addr))
|
||||||
|
z.memPtr = addr + 1
|
||||||
|
case 0x7B:
|
||||||
|
// ld sp,(**)
|
||||||
|
addr := z.nextW()
|
||||||
|
z.sp = z.rw(addr)
|
||||||
|
z.memPtr = addr + 1
|
||||||
|
case 0x44, 0x54, 0x64, 0x74, 0x4C, 0x5C, 0x6C, 0x7C:
|
||||||
|
z.a = z.subB(0, z.a, false) // neg
|
||||||
|
case 0x46, 0x4e, 0x66, 0x6e:
|
||||||
|
z.interruptMode = 0 // im 0
|
||||||
|
case 0x56, 0x76:
|
||||||
|
z.interruptMode = 1 // im 1
|
||||||
|
case 0x5E, 0x7E:
|
||||||
|
z.interruptMode = 2 // im 2
|
||||||
|
case 0x67:
|
||||||
|
// rrd
|
||||||
|
a := z.a
|
||||||
|
val := z.rb(z.hl())
|
||||||
|
z.a = (a & 0xF0) | (val & 0xF)
|
||||||
|
z.wb(z.hl(), (val>>4)|(a<<4))
|
||||||
|
|
||||||
|
z.nf = false
|
||||||
|
z.hf = false
|
||||||
|
z.updateXY(z.a)
|
||||||
|
z.zf = z.a == 0
|
||||||
|
z.sf = z.a&0x80 != 0
|
||||||
|
z.pf = parity(z.a)
|
||||||
|
z.memPtr = z.hl() + 1
|
||||||
|
case 0x6F:
|
||||||
|
// rld
|
||||||
|
a := z.a
|
||||||
|
val := z.rb(z.hl())
|
||||||
|
z.a = (a & 0xF0) | (val >> 4)
|
||||||
|
z.wb(z.hl(), (val<<4)|(a&0xF))
|
||||||
|
z.nf = false
|
||||||
|
z.hf = false
|
||||||
|
z.updateXY(z.a)
|
||||||
|
z.zf = z.a == 0
|
||||||
|
z.sf = z.a&0x80 != 0
|
||||||
|
z.pf = parity(z.a)
|
||||||
|
z.memPtr = z.hl() + 1
|
||||||
|
default:
|
||||||
|
log.Errorf("Unknown ED opcode: %02X\n", opcode)
|
||||||
|
}
|
||||||
|
}
|
||||||
172
src/z80/cpu.go
Normal file
172
src/z80/cpu.go
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
package z80
|
||||||
|
|
||||||
|
type MemIoRW interface {
|
||||||
|
// M1MemRead Read byte from memory for specified address @ M1 cycle
|
||||||
|
M1MemRead(addr uint16) byte
|
||||||
|
// MemRead Read byte from memory for specified address
|
||||||
|
MemRead(addr uint16) byte
|
||||||
|
// MemWrite Write byte to memory to specified address
|
||||||
|
MemWrite(addr uint16, val byte)
|
||||||
|
// IORead Read byte from specified port
|
||||||
|
IORead(port uint16) byte
|
||||||
|
// IOWrite Write byte to specified port
|
||||||
|
IOWrite(port uint16, val byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CPUInterface interface {
|
||||||
|
// Reset CPU to initial state
|
||||||
|
Reset()
|
||||||
|
// RunInstruction Run single instruction, return number of CPU cycles
|
||||||
|
RunInstruction() uint32
|
||||||
|
// GetState Get current CPU state
|
||||||
|
GetState() *CPU
|
||||||
|
// SetState Set current CPU state
|
||||||
|
SetState(state *CPU)
|
||||||
|
// DebugOutput out current CPU state
|
||||||
|
DebugOutput()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlagsType - Processor flags
|
||||||
|
type FlagsType struct {
|
||||||
|
S bool `json:"s,omitempty"`
|
||||||
|
Z bool `json:"z,omitempty"`
|
||||||
|
Y bool `json:"y,omitempty"`
|
||||||
|
H bool `json:"h,omitempty"`
|
||||||
|
X bool `json:"x,omitempty"`
|
||||||
|
P bool `json:"p,omitempty"`
|
||||||
|
N bool `json:"n,omitempty"`
|
||||||
|
C bool `json:"c,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Z80CPU - Processor state
|
||||||
|
type CPU struct {
|
||||||
|
A byte `json:"a,omitempty"`
|
||||||
|
B byte `json:"b,omitempty"`
|
||||||
|
C byte `json:"c,omitempty"`
|
||||||
|
D byte `json:"d,omitempty"`
|
||||||
|
E byte `json:"e,omitempty"`
|
||||||
|
H byte `json:"h,omitempty"`
|
||||||
|
L byte `json:"l,omitempty"`
|
||||||
|
AAlt byte `json:"AAlt,omitempty"`
|
||||||
|
BAlt byte `json:"BAlt,omitempty"`
|
||||||
|
CAlt byte `json:"CAlt,omitempty"`
|
||||||
|
DAlt byte `json:"DAlt,omitempty"`
|
||||||
|
EAlt byte `json:"EAlt,omitempty"`
|
||||||
|
HAlt byte `json:"HAlt,omitempty"`
|
||||||
|
LAlt byte `json:"LAlt,omitempty"`
|
||||||
|
IX uint16 `json:"IX,omitempty"`
|
||||||
|
IY uint16 `json:"IY,omitempty"`
|
||||||
|
I byte `json:"i,omitempty"`
|
||||||
|
R byte `json:"r,omitempty"`
|
||||||
|
SP uint16 `json:"SP,omitempty"`
|
||||||
|
PC uint16 `json:"PC,omitempty"`
|
||||||
|
Flags FlagsType `json:"flags"`
|
||||||
|
FlagsAlt FlagsType `json:"flagsAlt"`
|
||||||
|
IMode byte `json:"IMode,omitempty"`
|
||||||
|
Iff1 bool `json:"iff1,omitempty"`
|
||||||
|
Iff2 bool `json:"iff2,omitempty"`
|
||||||
|
Halted bool `json:"halted,omitempty"`
|
||||||
|
DoDelayedDI bool `json:"doDelayedDI,omitempty"`
|
||||||
|
DoDelayedEI bool `json:"doDelayedEI,omitempty"`
|
||||||
|
CycleCount uint32 `json:"cycleCount,omitempty"`
|
||||||
|
InterruptOccurred bool `json:"interruptOccurred,omitempty"`
|
||||||
|
MemPtr uint16 `json:"memPtr,omitempty"`
|
||||||
|
//core MemIoRW
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagsType) GetFlags() byte {
|
||||||
|
var flags byte = 0
|
||||||
|
if f.S {
|
||||||
|
flags |= 0x80
|
||||||
|
}
|
||||||
|
if f.Z {
|
||||||
|
flags |= 0x40
|
||||||
|
}
|
||||||
|
if f.Y {
|
||||||
|
flags |= 0x20
|
||||||
|
}
|
||||||
|
if f.H {
|
||||||
|
flags |= 0x10
|
||||||
|
}
|
||||||
|
if f.X {
|
||||||
|
flags |= 0x08
|
||||||
|
}
|
||||||
|
if f.P {
|
||||||
|
flags |= 0x04
|
||||||
|
}
|
||||||
|
if f.N {
|
||||||
|
flags |= 0x02
|
||||||
|
}
|
||||||
|
if f.C {
|
||||||
|
flags |= 0x01
|
||||||
|
}
|
||||||
|
return flags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagsType) GetFlagsStr() string {
|
||||||
|
flags := []byte{'-', '-', '-', '-', '-', '-', '-', '-'}
|
||||||
|
if f.S {
|
||||||
|
flags[0] = 'S'
|
||||||
|
}
|
||||||
|
if f.Z {
|
||||||
|
flags[1] = 'Z'
|
||||||
|
}
|
||||||
|
if f.Y {
|
||||||
|
flags[2] = '5'
|
||||||
|
}
|
||||||
|
if f.H {
|
||||||
|
flags[3] = 'H'
|
||||||
|
}
|
||||||
|
if f.X {
|
||||||
|
flags[4] = '3'
|
||||||
|
}
|
||||||
|
if f.P {
|
||||||
|
flags[5] = 'P'
|
||||||
|
}
|
||||||
|
if f.N {
|
||||||
|
flags[6] = 'N'
|
||||||
|
}
|
||||||
|
if f.C {
|
||||||
|
flags[7] = 'C'
|
||||||
|
}
|
||||||
|
return string(flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *CPU) IIFStr() string {
|
||||||
|
flags := []byte{'-', '-'}
|
||||||
|
if z.Iff1 {
|
||||||
|
flags[0] = '1'
|
||||||
|
}
|
||||||
|
if z.Iff2 {
|
||||||
|
flags[1] = '2'
|
||||||
|
}
|
||||||
|
return string(flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFlags(f byte) FlagsType {
|
||||||
|
return FlagsType{
|
||||||
|
S: f&0x80 != 0,
|
||||||
|
Z: f&0x40 != 0,
|
||||||
|
Y: f&0x20 != 0,
|
||||||
|
H: f&0x10 != 0,
|
||||||
|
X: f&0x08 != 0,
|
||||||
|
P: f&0x04 != 0,
|
||||||
|
N: f&0x02 != 0,
|
||||||
|
C: f&0x01 != 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FlagsType) SetFlags(flags byte) {
|
||||||
|
f.S = flags&0x80 != 0
|
||||||
|
f.Z = flags&0x40 != 0
|
||||||
|
f.Y = flags&0x20 != 0
|
||||||
|
f.H = flags&0x10 != 0
|
||||||
|
f.X = flags&0x08 != 0
|
||||||
|
f.P = flags&0x04 != 0
|
||||||
|
f.N = flags&0x02 != 0
|
||||||
|
f.C = flags&0x01 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *CPU) GetPC() uint16 {
|
||||||
|
return z.PC
|
||||||
|
}
|
||||||
598
src/z80/cpu_test.go
Normal file
598
src/z80/cpu_test.go
Normal file
@ -0,0 +1,598 @@
|
|||||||
|
package z80_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
_ "embed"
|
||||||
|
"okemu/z80"
|
||||||
|
"okemu/z80/c99"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ScanNone int = iota
|
||||||
|
ScanDesc
|
||||||
|
ScanEvent
|
||||||
|
ScanRegs
|
||||||
|
ScanState
|
||||||
|
ScanMem
|
||||||
|
ScanEnd
|
||||||
|
)
|
||||||
|
|
||||||
|
type Registers struct {
|
||||||
|
AF uint16
|
||||||
|
BC uint16
|
||||||
|
DE uint16
|
||||||
|
HL uint16
|
||||||
|
AFa uint16
|
||||||
|
BCa uint16
|
||||||
|
DEa uint16
|
||||||
|
HLa uint16
|
||||||
|
IX uint16
|
||||||
|
IY uint16
|
||||||
|
SP uint16
|
||||||
|
PC uint16
|
||||||
|
MemPtr uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type State struct {
|
||||||
|
I byte
|
||||||
|
R byte
|
||||||
|
IFF1 bool
|
||||||
|
IFF2 bool
|
||||||
|
IM byte
|
||||||
|
isHalted bool
|
||||||
|
tStates uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemorySetup struct {
|
||||||
|
addr uint16
|
||||||
|
values []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type Event struct {
|
||||||
|
time uint16
|
||||||
|
typ string
|
||||||
|
addr uint16
|
||||||
|
data byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type Z80TestIn struct {
|
||||||
|
// description string
|
||||||
|
registers Registers
|
||||||
|
state State
|
||||||
|
memorySetup []MemorySetup
|
||||||
|
}
|
||||||
|
|
||||||
|
type Expect struct {
|
||||||
|
events []Event
|
||||||
|
registers Registers
|
||||||
|
state State
|
||||||
|
memory []MemorySetup
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:embed tests.in
|
||||||
|
var testIn []byte
|
||||||
|
|
||||||
|
//go:embed tests.expected
|
||||||
|
var testExpected []byte
|
||||||
|
|
||||||
|
type Computer struct {
|
||||||
|
cpu *c99.Z80
|
||||||
|
memory [65536]byte
|
||||||
|
ports [256]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var z80TestsIn map[string]Z80TestIn
|
||||||
|
var z80TestsExpected map[string]Expect
|
||||||
|
|
||||||
|
var computer Computer
|
||||||
|
|
||||||
|
var testNames []string
|
||||||
|
|
||||||
|
//var z80 *c99.Z80
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
z80TestsIn = make(map[string]Z80TestIn)
|
||||||
|
z80TestsExpected = make(map[string]Expect)
|
||||||
|
parseTestIn()
|
||||||
|
parseTestExpected()
|
||||||
|
for addr := 0; addr < 65535; addr++ {
|
||||||
|
computer.memory[addr] = 0x00
|
||||||
|
}
|
||||||
|
for addr := 0; addr < 255; addr++ {
|
||||||
|
computer.ports[addr] = 0
|
||||||
|
}
|
||||||
|
computer.cpu = c99.New(&computer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Computer) M1MemRead(addr uint16) byte {
|
||||||
|
return c.memory[addr]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Computer) MemRead(addr uint16) byte {
|
||||||
|
return c.memory[addr]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Computer) MemWrite(addr uint16, val byte) {
|
||||||
|
c.memory[addr] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Computer) IOWrite(port uint16, val byte) {
|
||||||
|
c.ports[port&0x00ff] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Computer) IORead(port uint16) byte {
|
||||||
|
return byte(port >> 8) //c.ports[port&0x00ff]
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTestExpected() {
|
||||||
|
exScanner := bufio.NewScanner(bytes.NewReader(testExpected))
|
||||||
|
scanState := ScanNone
|
||||||
|
|
||||||
|
testName := ""
|
||||||
|
var events []Event
|
||||||
|
registers := Registers{}
|
||||||
|
state := State{}
|
||||||
|
var memorySetup []MemorySetup
|
||||||
|
|
||||||
|
for exScanner.Scan() {
|
||||||
|
line := exScanner.Text()
|
||||||
|
if len(strings.TrimSpace(line)) == 0 {
|
||||||
|
if scanState == ScanMem {
|
||||||
|
scanState = ScanEnd
|
||||||
|
} else {
|
||||||
|
scanState = ScanNone
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ScanNone == scanState {
|
||||||
|
scanState = ScanDesc
|
||||||
|
} else if len(line) > 0 && line[0] == ' ' {
|
||||||
|
scanState = ScanEvent
|
||||||
|
}
|
||||||
|
//else {
|
||||||
|
// if scanState == ScanEvent {
|
||||||
|
// scanState = ScanRegs
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
switch scanState {
|
||||||
|
case ScanDesc:
|
||||||
|
testName = line
|
||||||
|
scanState = ScanRegs
|
||||||
|
case ScanEvent:
|
||||||
|
events = append([]Event{}, *parseEvent(line))
|
||||||
|
scanState = ScanRegs
|
||||||
|
case ScanRegs:
|
||||||
|
registers = *parseRegisters(line)
|
||||||
|
scanState = ScanState
|
||||||
|
case ScanState:
|
||||||
|
state = *parseState(line)
|
||||||
|
scanState = ScanMem
|
||||||
|
case ScanMem:
|
||||||
|
memorySetup = append(memorySetup, *parseMemory(line))
|
||||||
|
//scanState = ScanMem
|
||||||
|
case ScanEnd:
|
||||||
|
z80TestsExpected[testName] = Expect{
|
||||||
|
events: events,
|
||||||
|
registers: registers,
|
||||||
|
state: state,
|
||||||
|
memory: memorySetup,
|
||||||
|
}
|
||||||
|
events = []Event{}
|
||||||
|
memorySetup = []MemorySetup{}
|
||||||
|
scanState = ScanNone
|
||||||
|
default:
|
||||||
|
panic("unhandled default case")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTestIn() {
|
||||||
|
inScanner := bufio.NewScanner(bytes.NewReader(testIn))
|
||||||
|
scanState := ScanNone
|
||||||
|
testName := ""
|
||||||
|
registers := Registers{}
|
||||||
|
state := State{}
|
||||||
|
var memorySetup []MemorySetup
|
||||||
|
for inScanner.Scan() {
|
||||||
|
line := inScanner.Text()
|
||||||
|
if len(line) == 0 || strings.TrimSpace(line) == "" {
|
||||||
|
scanState = ScanNone
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ScanNone == scanState {
|
||||||
|
scanState = ScanDesc
|
||||||
|
} else if line == "-1" {
|
||||||
|
scanState = ScanEnd
|
||||||
|
}
|
||||||
|
switch scanState {
|
||||||
|
case ScanDesc:
|
||||||
|
testName = line
|
||||||
|
scanState = ScanRegs
|
||||||
|
case ScanRegs:
|
||||||
|
registers = *parseRegisters(line)
|
||||||
|
scanState = ScanState
|
||||||
|
case ScanState:
|
||||||
|
state = *parseState(line)
|
||||||
|
scanState = ScanMem
|
||||||
|
case ScanMem:
|
||||||
|
memorySetup = append(memorySetup, *parseMemory(line))
|
||||||
|
//scanState = ScanMem
|
||||||
|
case ScanEnd:
|
||||||
|
test := Z80TestIn{
|
||||||
|
registers: registers,
|
||||||
|
state: state,
|
||||||
|
memorySetup: memorySetup,
|
||||||
|
}
|
||||||
|
testNames = append(testNames, testName)
|
||||||
|
z80TestsIn[testName] = test
|
||||||
|
scanState = ScanNone
|
||||||
|
default:
|
||||||
|
panic("unhandled default case")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseEvent(event string) *Event {
|
||||||
|
e := Event{}
|
||||||
|
line := strings.TrimSpace(event)
|
||||||
|
//4 MR 0000 00
|
||||||
|
//012345678
|
||||||
|
if len(line) < 9 {
|
||||||
|
log.Errorf("Invalid event: %s", line)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
sp := strings.Index(line, " ")
|
||||||
|
if sp == -1 {
|
||||||
|
log.Errorf("Invalid event: %s", line)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
e.time = parseDecW(line[:sp])
|
||||||
|
e.typ = line[sp+1 : sp+3]
|
||||||
|
e.addr = parseHexW(line[sp+4 : sp+8])
|
||||||
|
if len(line) > sp+9 {
|
||||||
|
//println("Event: ", line)
|
||||||
|
|
||||||
|
e.data = parseHexB(line[sp+9:])
|
||||||
|
}
|
||||||
|
|
||||||
|
return &e
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseMemory(line string) *MemorySetup {
|
||||||
|
m := MemorySetup{}
|
||||||
|
//0000 00 -1
|
||||||
|
//0123456789
|
||||||
|
m.addr = parseHexW(line[0:4])
|
||||||
|
mem := line[5:]
|
||||||
|
for {
|
||||||
|
st := mem[:2]
|
||||||
|
if st == "-1" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
m.values = append(m.values, parseHexB(st))
|
||||||
|
mem = strings.TrimSpace(mem[2:])
|
||||||
|
}
|
||||||
|
return &m
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRegisters(line string) *Registers {
|
||||||
|
r := Registers{}
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if len(line) != 64 {
|
||||||
|
log.Errorf("Invalid register line: %s", line)
|
||||||
|
} else {
|
||||||
|
// 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
|
||||||
|
for ctr := 0; ctr < 13; ctr++ {
|
||||||
|
hexString := line[ctr*5 : ctr*5+4]
|
||||||
|
v, err := strconv.ParseUint(hexString, 16, 16)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Invalid register value: %s in line %s", hexString, line)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch ctr {
|
||||||
|
case 0:
|
||||||
|
r.AF = uint16(v)
|
||||||
|
case 1:
|
||||||
|
r.BC = uint16(v)
|
||||||
|
case 2:
|
||||||
|
r.DE = uint16(v)
|
||||||
|
case 3:
|
||||||
|
r.HL = uint16(v)
|
||||||
|
case 4:
|
||||||
|
r.AFa = uint16(v)
|
||||||
|
case 5:
|
||||||
|
r.BCa = uint16(v)
|
||||||
|
case 6:
|
||||||
|
r.DEa = uint16(v)
|
||||||
|
case 7:
|
||||||
|
r.HLa = uint16(v)
|
||||||
|
case 8:
|
||||||
|
r.IX = uint16(v)
|
||||||
|
case 9:
|
||||||
|
r.IY = uint16(v)
|
||||||
|
case 10:
|
||||||
|
r.SP = uint16(v)
|
||||||
|
case 11:
|
||||||
|
r.PC = uint16(v)
|
||||||
|
case 12:
|
||||||
|
r.MemPtr = uint16(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return &r
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseHexB(line string) byte {
|
||||||
|
v, err := strconv.ParseUint(line, 16, 8)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Invalid HexB value: %s", line)
|
||||||
|
}
|
||||||
|
return byte(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseHexW(line string) uint16 {
|
||||||
|
v, err := strconv.ParseUint(line, 16, 16)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Invalid HexW value: %s", line)
|
||||||
|
}
|
||||||
|
return uint16(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDecB(line string) byte {
|
||||||
|
v, err := strconv.ParseUint(line, 10, 8)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Invalid B value: %s", line)
|
||||||
|
}
|
||||||
|
return byte(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDecW(line string) uint16 {
|
||||||
|
v, err := strconv.ParseUint(line, 10, 16)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Invalid W value: %s", line)
|
||||||
|
}
|
||||||
|
return uint16(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseBoolB(line string) bool {
|
||||||
|
v, err := strconv.ParseUint(line, 10, 8)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Invalid state I value: %s", line)
|
||||||
|
}
|
||||||
|
return v != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseState(line string) *State {
|
||||||
|
s := State{}
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if len(line) < 15 {
|
||||||
|
log.Errorf("Invalid state line: %s", line)
|
||||||
|
} else {
|
||||||
|
//00 00 0 0 0 0 1
|
||||||
|
//0123456789012345678
|
||||||
|
s.I = parseHexB(line[0:2])
|
||||||
|
s.R = parseHexB(line[3:5])
|
||||||
|
s.IFF1 = parseBoolB(line[6:7])
|
||||||
|
s.IFF2 = parseBoolB(line[8:9])
|
||||||
|
s.IM = parseDecB(line[10:11])
|
||||||
|
s.isHalted = parseBoolB(line[12:13])
|
||||||
|
s.tStates = parseDecW(strings.TrimSpace(line[13:]))
|
||||||
|
}
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZ80Fuse(t *testing.T) {
|
||||||
|
t.Logf("Fuse-type Z80 emulator test")
|
||||||
|
computer.cpu.Reset()
|
||||||
|
for _, name := range testNames {
|
||||||
|
setComputerState(z80TestsIn[name])
|
||||||
|
exp, exists := z80TestsExpected[name]
|
||||||
|
if !exists {
|
||||||
|
t.Errorf("Expected values for test %s not exists!", name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cy := uint32(0)
|
||||||
|
for {
|
||||||
|
cy += computer.cpu.RunInstruction()
|
||||||
|
if cy >= uint32(exp.state.tStates) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkComputerState(t, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setComputerState(test Z80TestIn) {
|
||||||
|
state := z80.CPU{
|
||||||
|
A: byte(test.registers.AF >> 8),
|
||||||
|
B: byte(test.registers.BC >> 8),
|
||||||
|
C: byte(test.registers.BC),
|
||||||
|
D: byte(test.registers.DE >> 8),
|
||||||
|
E: byte(test.registers.DE),
|
||||||
|
H: byte(test.registers.HL >> 8),
|
||||||
|
L: byte(test.registers.HL),
|
||||||
|
AAlt: byte(test.registers.AFa >> 8),
|
||||||
|
BAlt: byte(test.registers.BCa >> 8),
|
||||||
|
CAlt: byte(test.registers.BCa),
|
||||||
|
DAlt: byte(test.registers.DEa >> 8),
|
||||||
|
EAlt: byte(test.registers.DEa),
|
||||||
|
HAlt: byte(test.registers.HLa >> 8),
|
||||||
|
LAlt: byte(test.registers.HLa),
|
||||||
|
IX: test.registers.IX,
|
||||||
|
IY: test.registers.IY,
|
||||||
|
I: test.state.I,
|
||||||
|
R: test.state.R,
|
||||||
|
SP: test.registers.SP,
|
||||||
|
PC: test.registers.PC,
|
||||||
|
Flags: z80.GetFlags(byte(test.registers.AF)),
|
||||||
|
FlagsAlt: z80.GetFlags(byte(test.registers.AFa)),
|
||||||
|
IMode: test.state.IM,
|
||||||
|
Iff1: test.state.IFF1,
|
||||||
|
Iff2: test.state.IFF2,
|
||||||
|
Halted: test.state.isHalted,
|
||||||
|
DoDelayedDI: false,
|
||||||
|
DoDelayedEI: false,
|
||||||
|
CycleCount: 0,
|
||||||
|
InterruptOccurred: false,
|
||||||
|
MemPtr: test.registers.MemPtr,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup CPU
|
||||||
|
computer.cpu.SetState(&state)
|
||||||
|
|
||||||
|
// Setup Memory
|
||||||
|
for _, ms := range test.memorySetup {
|
||||||
|
addr := ms.addr
|
||||||
|
for _, b := range ms.values {
|
||||||
|
computer.memory[addr] = b
|
||||||
|
addr++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func lo(w uint16) byte {
|
||||||
|
return byte(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hi(w uint16) byte {
|
||||||
|
return byte(w >> 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkComputerState(t *testing.T, name string) {
|
||||||
|
state := computer.cpu.GetState()
|
||||||
|
exp, exists := z80TestsExpected[name]
|
||||||
|
if !exists {
|
||||||
|
t.Errorf("Expected values for test %s not exists!", name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// A,B,C,D,E,H,L
|
||||||
|
if hi(exp.registers.AF) != state.A {
|
||||||
|
t.Errorf("%s: Expected A to be %x, got %x", name, hi(exp.registers.AF), state.A)
|
||||||
|
}
|
||||||
|
if hi(exp.registers.BC) != state.B {
|
||||||
|
t.Errorf("%s: Expected B to be %x, got %x", name, hi(exp.registers.BC), state.B)
|
||||||
|
computer.cpu.DebugOutput()
|
||||||
|
}
|
||||||
|
|
||||||
|
if lo(exp.registers.BC) != state.C {
|
||||||
|
t.Errorf("%s: Expected C to be %x, got %x", name, lo(exp.registers.BC), state.C)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hi(exp.registers.DE) != state.D {
|
||||||
|
t.Errorf("%s: Expected D to be %x, got %x", name, hi(exp.registers.DE), state.D)
|
||||||
|
}
|
||||||
|
|
||||||
|
if lo(exp.registers.DE) != state.E {
|
||||||
|
t.Errorf("%s: Expected E to be %x, got %x", name, lo(exp.registers.DE), state.E)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hi(exp.registers.HL) != state.H {
|
||||||
|
t.Errorf("%s: Expected H to be %x, got %x", name, hi(exp.registers.HL), state.H)
|
||||||
|
}
|
||||||
|
|
||||||
|
if lo(exp.registers.HL) != state.L {
|
||||||
|
t.Errorf("%s: Expected L to be %x, got %x", name, lo(exp.registers.BC), state.L)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alt A,B,C,D,E,H,L
|
||||||
|
if hi(exp.registers.AFa) != state.AAlt {
|
||||||
|
t.Errorf("%s: Expected A' to be %x, got %x", name, hi(exp.registers.AFa), state.AAlt)
|
||||||
|
}
|
||||||
|
if hi(exp.registers.BCa) != state.BAlt {
|
||||||
|
t.Errorf("%s: Expected B' to be %x, got %x", name, hi(exp.registers.BCa), state.BAlt)
|
||||||
|
}
|
||||||
|
|
||||||
|
if lo(exp.registers.BCa) != state.CAlt {
|
||||||
|
t.Errorf("%s: Expected C' to be %x, got %x", name, lo(exp.registers.BCa), state.CAlt)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hi(exp.registers.DEa) != state.DAlt {
|
||||||
|
t.Errorf("%s: Expected D' to be %x, got %x", name, hi(exp.registers.DEa), state.DAlt)
|
||||||
|
}
|
||||||
|
|
||||||
|
if lo(exp.registers.DEa) != state.EAlt {
|
||||||
|
t.Errorf("%s: Expected E' to be %x, got %x", name, lo(exp.registers.DEa), state.EAlt)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hi(exp.registers.HLa) != state.HAlt {
|
||||||
|
t.Errorf("%s: Expected H' to be %x, got %x", name, hi(exp.registers.HLa), state.HAlt)
|
||||||
|
}
|
||||||
|
|
||||||
|
if lo(exp.registers.HLa) != state.LAlt {
|
||||||
|
t.Errorf("%s: Expected L' to be %x, got %x", name, lo(exp.registers.BCa), state.LAlt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 16b regs PC, SP, Meme, R, I
|
||||||
|
|
||||||
|
if exp.registers.IX != state.IX {
|
||||||
|
t.Errorf("%s: Expected IX to be %x, got %x", name, exp.registers.IX, state.IX)
|
||||||
|
}
|
||||||
|
|
||||||
|
if exp.registers.IY != state.IY {
|
||||||
|
t.Errorf("%s: Expected IX to be %x, got %x", name, exp.registers.IX, state.IX)
|
||||||
|
}
|
||||||
|
|
||||||
|
if exp.registers.PC != state.PC {
|
||||||
|
t.Errorf("%s: Expected PC to be %x, got %x", name, exp.registers.PC, state.PC)
|
||||||
|
}
|
||||||
|
|
||||||
|
if exp.registers.SP != state.SP {
|
||||||
|
t.Errorf("%s: Expected SP to be %x, got %x", name, exp.registers.SP, state.SP)
|
||||||
|
}
|
||||||
|
|
||||||
|
if exp.registers.MemPtr != state.MemPtr {
|
||||||
|
t.Errorf("%s: Expected MemPtr to be %x, got %x", name, exp.registers.MemPtr, state.MemPtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// State
|
||||||
|
if exp.state.I != state.I {
|
||||||
|
t.Errorf("%s: Expected I to be %x, got %x", name, exp.state.I, state.I)
|
||||||
|
}
|
||||||
|
|
||||||
|
if exp.state.IM != state.IMode {
|
||||||
|
t.Errorf("%s: Expected IM to be %d, got %d", name, exp.state.IM, state.IMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if exp.state.IFF1 != state.Iff1 {
|
||||||
|
t.Errorf("%s: Expected IIF1 to be %t, got %t", name, exp.state.IFF1, state.Iff1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if exp.state.IFF2 != state.Iff2 {
|
||||||
|
t.Errorf("%s: Expected IIF2 to be %t, got %t", name, exp.state.IFF2, state.Iff2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if exp.state.isHalted != state.Halted {
|
||||||
|
t.Errorf("%s: Expected isHalted to be %t, got %t", name, exp.state.isHalted, state.Halted)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FLAGS
|
||||||
|
if lo(exp.registers.AF) != state.Flags.GetFlags() {
|
||||||
|
t.Errorf("%s: Expected Flags to be %08b, got %08b", name, lo(exp.registers.AF), state.Flags.GetFlags())
|
||||||
|
}
|
||||||
|
|
||||||
|
if lo(exp.registers.AFa) != state.FlagsAlt.GetFlags() {
|
||||||
|
t.Errorf("%s: Expected Flags' to be %08b, got %08b", name, lo(exp.registers.AFa), state.FlagsAlt.GetFlags())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check memory
|
||||||
|
for _, mExpect := range exp.memory {
|
||||||
|
addr := mExpect.addr
|
||||||
|
for _, b := range mExpect.values {
|
||||||
|
if computer.memory[addr] != b {
|
||||||
|
t.Errorf("%s: Expected memory[%x] to be %x, got %x", name, addr, b, computer.memory[addr])
|
||||||
|
}
|
||||||
|
addr++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
672
src/z80/dis/z80disasm.go
Normal file
672
src/z80/dis/z80disasm.go
Normal file
@ -0,0 +1,672 @@
|
|||||||
|
package dis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"okemu/z80"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Disassembler struct {
|
||||||
|
pc uint16
|
||||||
|
core z80.MemIoRW
|
||||||
|
}
|
||||||
|
|
||||||
|
type Disassembly interface {
|
||||||
|
Disassm(pc uint16) string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDisassembler(core z80.MemIoRW) *Disassembler {
|
||||||
|
d := Disassembler{
|
||||||
|
pc: 0,
|
||||||
|
core: core,
|
||||||
|
}
|
||||||
|
return &d
|
||||||
|
}
|
||||||
|
|
||||||
|
// opcode & 0x07
|
||||||
|
var operands = []string{"B", "C", "D", "E", "H", "L", "(HL)", "A"}
|
||||||
|
var aluOp = []string{"ADD A" + sep, "ADC A" + sep, "SUB ", "SBC A" + sep, "AND ", "XOR ", "OR ", "CP "}
|
||||||
|
|
||||||
|
const sep = ", "
|
||||||
|
|
||||||
|
func (d *Disassembler) jp(op, cond string) string {
|
||||||
|
addr := d.getW()
|
||||||
|
if cond != "" {
|
||||||
|
cond += sep
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s %s%s", op, cond, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Disassembler) jr(op, cond string) string {
|
||||||
|
addr := d.pc
|
||||||
|
offset := d.getByte()
|
||||||
|
if offset&0x80 != 0 {
|
||||||
|
addr += 0xFF00 | uint16(offset)
|
||||||
|
} else {
|
||||||
|
addr += d.pc + uint16(offset)
|
||||||
|
}
|
||||||
|
if cond != "" {
|
||||||
|
cond += sep
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s %s0x%04X", op, cond, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Disassembler) getByte() byte {
|
||||||
|
b := d.core.MemRead(d.pc)
|
||||||
|
d.pc++
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Disassembler) Disassm(pc uint16) string {
|
||||||
|
d.pc = pc
|
||||||
|
result := fmt.Sprintf(" %04X ", d.pc)
|
||||||
|
op := d.getByte()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
// == 00:0F
|
||||||
|
case op == 0x00:
|
||||||
|
result += "NOP"
|
||||||
|
case op == 0x01:
|
||||||
|
result += "LD BC" + sep + d.getW()
|
||||||
|
case op == 0x02:
|
||||||
|
result += "LD (BC)" + sep + "A"
|
||||||
|
case op == 0x03:
|
||||||
|
result += "INC BC"
|
||||||
|
case op == 0x04:
|
||||||
|
result += "INC B"
|
||||||
|
case op == 0x05:
|
||||||
|
result += "DEC B"
|
||||||
|
case op == 0x06:
|
||||||
|
result += "LD B" + sep + d.getB()
|
||||||
|
case op == 0x07:
|
||||||
|
result += "RLCA"
|
||||||
|
case op == 0x08:
|
||||||
|
result += "EX AF, AF'"
|
||||||
|
case op == 0x09:
|
||||||
|
result += "ADD HL" + sep + "BC"
|
||||||
|
case op == 0x0A:
|
||||||
|
result += "LD A" + sep + "(BC)"
|
||||||
|
case op == 0x0B:
|
||||||
|
result += "DEC BC"
|
||||||
|
case op == 0x0C:
|
||||||
|
result += "INC C"
|
||||||
|
case op == 0x0D:
|
||||||
|
result += "DEC C"
|
||||||
|
case op == 0x0E:
|
||||||
|
result += "LD C" + sep + d.getB()
|
||||||
|
case op == 0x0F:
|
||||||
|
result += "RRCA"
|
||||||
|
// 10:1F
|
||||||
|
case op == 0x10:
|
||||||
|
// DJNZ rel
|
||||||
|
result += d.jr("DJNZ", "")
|
||||||
|
case op == 0x11:
|
||||||
|
result += "LD DE" + sep + d.getW()
|
||||||
|
case op == 0x12:
|
||||||
|
result += "LD (DE)" + sep + "A"
|
||||||
|
case op == 0x13:
|
||||||
|
result += "INC DE"
|
||||||
|
case op == 0x14:
|
||||||
|
result += "INC D"
|
||||||
|
case op == 0x15:
|
||||||
|
result += "DEC D"
|
||||||
|
case op == 0x16:
|
||||||
|
result += "LD D" + sep + d.getB()
|
||||||
|
case op == 0x17:
|
||||||
|
result += "RLA"
|
||||||
|
case op == 0x18:
|
||||||
|
result += d.jr("JR", "")
|
||||||
|
case op == 0x19:
|
||||||
|
result += "ADD HL" + sep + "DE"
|
||||||
|
case op == 0x1A:
|
||||||
|
result += "LD A" + sep + "(DE)"
|
||||||
|
case op == 0x1B:
|
||||||
|
result += "DEC DE"
|
||||||
|
case op == 0x1C:
|
||||||
|
result += "INC E"
|
||||||
|
case op == 0x1D:
|
||||||
|
result += "DEC E"
|
||||||
|
case op == 0x1E:
|
||||||
|
result += "LD E" + sep + d.getB()
|
||||||
|
case op == 0x1F:
|
||||||
|
result += "RRA"
|
||||||
|
// == 20:2F
|
||||||
|
case op == 0x20:
|
||||||
|
result += d.jr("JR", "NZ")
|
||||||
|
case op == 0x21:
|
||||||
|
result += "LD HL" + sep + d.getW()
|
||||||
|
case op == 0x22:
|
||||||
|
// LD (nn),HL
|
||||||
|
result += "LD (" + d.getW() + ")" + sep + "HL"
|
||||||
|
case op == 0x23:
|
||||||
|
result += "INC HL"
|
||||||
|
case op == 0x24:
|
||||||
|
result += "INC H"
|
||||||
|
case op == 0x25:
|
||||||
|
result += "DEC H"
|
||||||
|
case op == 0x26:
|
||||||
|
result += "LD H" + sep + d.getB()
|
||||||
|
case op == 0x27:
|
||||||
|
result += "DAA"
|
||||||
|
case op == 0x28:
|
||||||
|
result += d.jr("JR", "Z")
|
||||||
|
case op == 0x29:
|
||||||
|
result += "ADD HL" + sep + "HL"
|
||||||
|
case op == 0x2A:
|
||||||
|
result += "LD HL" + sep + "(" + d.getW() + ")"
|
||||||
|
case op == 0x2B:
|
||||||
|
result += "DEC HL"
|
||||||
|
case op == 0x2C:
|
||||||
|
result += "INC L"
|
||||||
|
case op == 0x2D:
|
||||||
|
result += "DEC L"
|
||||||
|
case op == 0x2E:
|
||||||
|
result += "LD L" + sep + d.getB()
|
||||||
|
case op == 0x2F:
|
||||||
|
result += "CPL"
|
||||||
|
// == 30:3F
|
||||||
|
case op == 0x30:
|
||||||
|
result += d.jr("JR", "NC")
|
||||||
|
case op == 0x31:
|
||||||
|
result += "LD SP" + sep + d.getW()
|
||||||
|
case op == 0x32:
|
||||||
|
result += "LD (" + d.getW() + ")" + sep + "A"
|
||||||
|
case op == 0x33:
|
||||||
|
result += "INC SP"
|
||||||
|
case op == 0x34:
|
||||||
|
result += "INC (HL)"
|
||||||
|
case op == 0x35:
|
||||||
|
result += "DEC (HL)"
|
||||||
|
case op == 0x36:
|
||||||
|
result += "LD (HL)" + sep + d.getB()
|
||||||
|
case op == 0x37:
|
||||||
|
result += "SCF"
|
||||||
|
case op == 0x38:
|
||||||
|
result += d.jr("JR", "C")
|
||||||
|
case op == 0x39:
|
||||||
|
result += "ADD HL" + sep + "SP"
|
||||||
|
case op == 0x3A:
|
||||||
|
result += "LD A" + sep + "(" + d.getW() + ")"
|
||||||
|
case op == 0x3B:
|
||||||
|
result += "DEC SP"
|
||||||
|
case op == 0x3C:
|
||||||
|
result += "INC A"
|
||||||
|
case op == 0x3D:
|
||||||
|
result += "DEC A"
|
||||||
|
case op == 0x3E:
|
||||||
|
result += "LD A" + sep + d.getB()
|
||||||
|
case op == 0x3F:
|
||||||
|
result += "CCF"
|
||||||
|
case op == 0x76:
|
||||||
|
result += "HALT"
|
||||||
|
case op >= 0x40 && op <= 0x7F:
|
||||||
|
// LD op8, op8
|
||||||
|
result += "LD " + operands[(op>>3)&0x07] + sep + operands[op&0x07]
|
||||||
|
case op >= 0x80 && op <= 0xBF:
|
||||||
|
// ALU op8
|
||||||
|
result += aluOp[(op>>3)&0x07] + operands[op&0x07]
|
||||||
|
case op == 0xc0:
|
||||||
|
result += "RET NZ"
|
||||||
|
case op == 0xc1:
|
||||||
|
result += "POP BC"
|
||||||
|
case op == 0xc2:
|
||||||
|
result += d.jp("JP", "NZ")
|
||||||
|
case op == 0xc3:
|
||||||
|
result += d.jp("JP", "")
|
||||||
|
case op == 0xc4:
|
||||||
|
result += d.jp("CALL", "NZ")
|
||||||
|
case op == 0xc5:
|
||||||
|
result += "PUSH BC"
|
||||||
|
case op == 0xc6:
|
||||||
|
result += "ADD A" + sep + d.getB()
|
||||||
|
case op == 0xc7 || op == 0xd7 || op == 0xe7 || op == 0xf7 || op == 0xcf || op == 0xdf || op == 0xef || op == 0xff:
|
||||||
|
// RST nnH
|
||||||
|
result += fmt.Sprintf("RST %d%dH", (op>>4)&3, (op&1)*8)
|
||||||
|
case op == 0xc8:
|
||||||
|
result += "RET Z"
|
||||||
|
case op == 0xc9:
|
||||||
|
result += "RET"
|
||||||
|
case op == 0xca:
|
||||||
|
result += d.jp("JP", "Z")
|
||||||
|
case op == 0xcb:
|
||||||
|
result += d.opocodeCB()
|
||||||
|
case op == 0xcc:
|
||||||
|
result += d.jp("CALL", "Z")
|
||||||
|
case op == 0xcd:
|
||||||
|
result += d.jp("CALL", "")
|
||||||
|
case op == 0xce:
|
||||||
|
result += "ADC A" + sep + d.getB()
|
||||||
|
case op == 0xd0:
|
||||||
|
result += "RET NC"
|
||||||
|
case op == 0xd1:
|
||||||
|
result += "POP DE"
|
||||||
|
case op == 0xd2:
|
||||||
|
result += d.jp("JP", "NC")
|
||||||
|
case op == 0xd3:
|
||||||
|
result += "OUT (" + d.getB() + ")" + sep + "A"
|
||||||
|
case op == 0xd4:
|
||||||
|
result += d.jp("CALL", "NC")
|
||||||
|
case op == 0xd5:
|
||||||
|
result += "PUSH DE"
|
||||||
|
case op == 0xd6:
|
||||||
|
result += "SUB " + d.getB()
|
||||||
|
case op == 0xd8:
|
||||||
|
result += "RET C"
|
||||||
|
case op == 0xd9:
|
||||||
|
result += "EXX"
|
||||||
|
case op == 0xda:
|
||||||
|
result += d.jp("JP", "C")
|
||||||
|
case op == 0xdb:
|
||||||
|
result += "IN A" + sep + " (" + d.getB() + ")"
|
||||||
|
case op == 0xdc:
|
||||||
|
result += d.jp("CALL", "C")
|
||||||
|
case op == 0xdd:
|
||||||
|
result += d.opocodeDD(op)
|
||||||
|
case op == 0xde:
|
||||||
|
result += "SBC A" + sep + d.getB()
|
||||||
|
case op == 0xe0:
|
||||||
|
result += "RET PO"
|
||||||
|
case op == 0xe1:
|
||||||
|
result += "POP HL"
|
||||||
|
case op == 0xe2:
|
||||||
|
result += d.jp("JP", "PO")
|
||||||
|
case op == 0xe3:
|
||||||
|
result += "EX (SP)" + sep + "HL"
|
||||||
|
case op == 0xe4:
|
||||||
|
result += d.jp("CALL", "PO")
|
||||||
|
case op == 0xe5:
|
||||||
|
result += "PUSH HL"
|
||||||
|
case op == 0xe6:
|
||||||
|
result += "AND " + d.getB()
|
||||||
|
case op == 0xe8:
|
||||||
|
result += "RET PE"
|
||||||
|
case op == 0xe9:
|
||||||
|
result += "JP (HL)"
|
||||||
|
case op == 0xea:
|
||||||
|
result += d.jp("JP", "PE")
|
||||||
|
case op == 0xeb:
|
||||||
|
result += "EX DE" + sep + "HL"
|
||||||
|
case op == 0xec:
|
||||||
|
result += d.jp("CALL", "PE")
|
||||||
|
case op == 0xed:
|
||||||
|
result += d.opocodeED()
|
||||||
|
case op == 0xee:
|
||||||
|
result += "XOR " + d.getB()
|
||||||
|
case op == 0xf0:
|
||||||
|
result += "RET P"
|
||||||
|
case op == 0xf1:
|
||||||
|
result += "POP AF"
|
||||||
|
case op == 0xf2:
|
||||||
|
result += d.jp("JP", "P")
|
||||||
|
case op == 0xf3:
|
||||||
|
result += "DI"
|
||||||
|
case op == 0xf4:
|
||||||
|
result += d.jp("CALL", "P")
|
||||||
|
case op == 0xf5:
|
||||||
|
result += "PUSH AF"
|
||||||
|
case op == 0xf6:
|
||||||
|
result += "OR " + d.getB()
|
||||||
|
case op == 0xf8:
|
||||||
|
result += "RET M"
|
||||||
|
case op == 0xf9:
|
||||||
|
result += "LD SP" + sep + "HL"
|
||||||
|
case op == 0xfa:
|
||||||
|
result += d.jp("JP", "M")
|
||||||
|
case op == 0xfb:
|
||||||
|
result += "EI"
|
||||||
|
case op == 0xfc:
|
||||||
|
result += d.jp("CALL", "M")
|
||||||
|
case op == 0xfd:
|
||||||
|
result += d.opocodeDD(op)
|
||||||
|
case op == 0xfe:
|
||||||
|
result += "CP " + d.getB()
|
||||||
|
default:
|
||||||
|
// All unknown as DB
|
||||||
|
result += fmt.Sprintf("DB 0x%02X", op)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Disassembler) getW() string {
|
||||||
|
lo := d.core.MemRead(d.pc)
|
||||||
|
d.pc++
|
||||||
|
hi := d.core.MemRead(d.pc)
|
||||||
|
d.pc++
|
||||||
|
return fmt.Sprintf("0x%02X%02X", hi, lo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Disassembler) getB() string {
|
||||||
|
lo := d.core.MemRead(d.pc)
|
||||||
|
d.pc++
|
||||||
|
return fmt.Sprintf("0x%02X", lo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Disassembler) getRel() string {
|
||||||
|
offset := d.core.MemRead(d.pc)
|
||||||
|
var sign string
|
||||||
|
if int8(offset) < 0 {
|
||||||
|
sign = "-"
|
||||||
|
} else {
|
||||||
|
sign = "+"
|
||||||
|
}
|
||||||
|
return sign + fmt.Sprintf("0x%02X", offset&0x7F)
|
||||||
|
}
|
||||||
|
|
||||||
|
var shiftOps = []string{"RLC", "RRC", "RL", "RR", "SLA", "SRA", "SLL", "SRL"}
|
||||||
|
var bitOps = []string{"BIT", "RES", "SET"}
|
||||||
|
|
||||||
|
// opocodeCB disassemble Z80 Opcodes, with CB first byte
|
||||||
|
func (d *Disassembler) opocodeCB() string {
|
||||||
|
op := ""
|
||||||
|
opcode := d.getByte()
|
||||||
|
if opcode <= 0x3F {
|
||||||
|
op = shiftOps[opcode>>3&0x07] + operands[opcode&0x7]
|
||||||
|
} else {
|
||||||
|
op = shiftOps[(opcode>>6&0x03)-1] + operands[opcode&0x7]
|
||||||
|
}
|
||||||
|
return op
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Disassembler) opocodeDD(op byte) string {
|
||||||
|
opcode := d.getByte()
|
||||||
|
result := ""
|
||||||
|
switch opcode {
|
||||||
|
case 0x09:
|
||||||
|
result = "ADD ii" + sep + "BC"
|
||||||
|
case 0x19:
|
||||||
|
result = "ADD ii" + sep + "DE"
|
||||||
|
case 0x21:
|
||||||
|
result = "LD ii" + sep + d.getW()
|
||||||
|
case 0x22:
|
||||||
|
result = "LD (" + d.getW() + ")" + sep + "ii"
|
||||||
|
case 0x23:
|
||||||
|
result = "INC ii"
|
||||||
|
case 0x24:
|
||||||
|
result = "INC IXH"
|
||||||
|
case 0x25:
|
||||||
|
result = "DEC IXH"
|
||||||
|
case 0x26:
|
||||||
|
result = "LD IXH" + sep + "n"
|
||||||
|
case 0x29:
|
||||||
|
result = "ADD ii" + sep + "ii"
|
||||||
|
case 0x2A:
|
||||||
|
result = "LD ii" + sep + "(" + d.getW() + ")"
|
||||||
|
case 0x2B:
|
||||||
|
result = "DEC ii"
|
||||||
|
case 0x34:
|
||||||
|
result = "INC (ii" + d.getRel() + ")"
|
||||||
|
case 0x35:
|
||||||
|
result = "DEC (ii" + d.getRel() + ")"
|
||||||
|
case 0x36:
|
||||||
|
result = "LD (ii" + d.getRel() + ")" + sep + "n"
|
||||||
|
case 0x39:
|
||||||
|
result = "ADD ii" + sep + "SP"
|
||||||
|
case 0x46:
|
||||||
|
result = "LD B" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x4E:
|
||||||
|
result = "LD C" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x56:
|
||||||
|
result = "LD D" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x5E:
|
||||||
|
result = "LD E" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x66:
|
||||||
|
result = "LD H" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x6E:
|
||||||
|
result = "LD L" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x70:
|
||||||
|
result = "LD (ii" + d.getRel() + ")" + sep + "B"
|
||||||
|
case 0x71:
|
||||||
|
result = "LD (ii" + d.getRel() + ")" + sep + "C"
|
||||||
|
case 0x72:
|
||||||
|
result = "LD (ii" + d.getRel() + ")" + sep + "D"
|
||||||
|
case 0x73:
|
||||||
|
result = "LD (ii" + d.getRel() + ")" + sep + "E"
|
||||||
|
case 0x74:
|
||||||
|
result = "LD (ii" + d.getRel() + ")" + sep + "H"
|
||||||
|
case 0x75:
|
||||||
|
result = "LD (ii" + d.getRel() + ")" + sep + "L"
|
||||||
|
case 0x77:
|
||||||
|
result = "LD (ii" + d.getRel() + ")" + sep + "A"
|
||||||
|
case 0x7E:
|
||||||
|
result = "LD A" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x86:
|
||||||
|
result = "ADD A" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x8E:
|
||||||
|
result = "ADC A" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x96:
|
||||||
|
result = "SUB (ii" + d.getRel() + ")"
|
||||||
|
case 0x9E:
|
||||||
|
result = "SBC A" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0xA6:
|
||||||
|
result = "AND (ii" + d.getRel() + ")"
|
||||||
|
case 0xAE:
|
||||||
|
result = "XOR (ii" + d.getRel() + ")"
|
||||||
|
case 0xB6:
|
||||||
|
result = "OR (ii" + d.getRel() + ")"
|
||||||
|
case 0xBE:
|
||||||
|
result = "CP (ii" + d.getRel() + ")"
|
||||||
|
case 0xCB:
|
||||||
|
result = d.opocodeDDCB(op, opcode)
|
||||||
|
case 0xE1:
|
||||||
|
result = "POP ii"
|
||||||
|
case 0xE3:
|
||||||
|
result = "EX (SP)" + sep + "ii"
|
||||||
|
case 0xE5:
|
||||||
|
result = "PUSH ii"
|
||||||
|
case 0xE9:
|
||||||
|
result = "JP (ii)"
|
||||||
|
case 0xF9:
|
||||||
|
result = "LD SP" + sep + "ii"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("DB 0x%02X, 0x%02X", op, opcode)
|
||||||
|
}
|
||||||
|
|
||||||
|
reg := "IX"
|
||||||
|
if op == 0xFD {
|
||||||
|
reg = "IY"
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.ReplaceAll(result, "ii", reg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Disassembler) opocodeDDCB(op1 byte, op2 byte) string {
|
||||||
|
opcode := d.getByte()
|
||||||
|
result := ""
|
||||||
|
switch opcode {
|
||||||
|
case 0x06:
|
||||||
|
result = "RLC (ii" + d.getRel() + ")"
|
||||||
|
case 0x0E:
|
||||||
|
result = "RRC (ii" + d.getRel() + ")"
|
||||||
|
case 0x16:
|
||||||
|
result = "RL (ii" + d.getRel() + ")"
|
||||||
|
case 0x1E:
|
||||||
|
result = "RR (ii" + d.getRel() + ")"
|
||||||
|
case 0x26:
|
||||||
|
result = "SLA (ii" + d.getRel() + ")"
|
||||||
|
case 0x2E:
|
||||||
|
result = "SRA (ii" + d.getRel() + ")"
|
||||||
|
case 0x3E:
|
||||||
|
result = "SRL (ii" + d.getRel() + ")"
|
||||||
|
case 0x46:
|
||||||
|
result = "BIT 0" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x4E:
|
||||||
|
result = "BIT 1" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x56:
|
||||||
|
result = "BIT 2" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x5E:
|
||||||
|
result = "BIT 3" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x66:
|
||||||
|
result = "BIT 4" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x6E:
|
||||||
|
result = "BIT 5" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x76:
|
||||||
|
result = "BIT 6" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x7E:
|
||||||
|
result = "BIT 7" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x86:
|
||||||
|
result = "RES 0" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x8E:
|
||||||
|
result = "RES 1" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x96:
|
||||||
|
result = "RES 2" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0x9E:
|
||||||
|
result = "RES 3" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0xA6:
|
||||||
|
result = "RES 4" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0xAE:
|
||||||
|
result = "RES 5" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0xB6:
|
||||||
|
result = "RES 6" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0xBE:
|
||||||
|
result = "RES 7" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0xC6:
|
||||||
|
result = "SET 0" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0xCE:
|
||||||
|
result = "SET 1" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0xD6:
|
||||||
|
result = "SET 2" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0xDE:
|
||||||
|
result = "SET 3" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0xE6:
|
||||||
|
result = "SET 4" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0xEE:
|
||||||
|
result = "SET 5" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0xF6:
|
||||||
|
result = "SET 6" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
case 0xFE:
|
||||||
|
result = "SET 7" + sep + "(ii" + d.getRel() + ")"
|
||||||
|
default:
|
||||||
|
result = fmt.Sprintf("DB 0x%02X, 0x%02X, 0x%02X", op1, op2, opcode)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Disassembler) opocodeED() string {
|
||||||
|
opcode := d.getByte()
|
||||||
|
result := ""
|
||||||
|
switch opcode {
|
||||||
|
case 0x40:
|
||||||
|
result = "IN B" + sep + "(C)"
|
||||||
|
case 0x41:
|
||||||
|
result = "OUT (C)" + sep + "B"
|
||||||
|
case 0x42:
|
||||||
|
result = "SBC HL" + sep + "BC"
|
||||||
|
case 0x43:
|
||||||
|
result = "LD (" + d.getW() + ")" + sep + "BC"
|
||||||
|
case 0x44, 0x4C, 0x54, 0x5C, 0x64, 0x6C, 0x74, 0x7C:
|
||||||
|
result = "NEG"
|
||||||
|
case 0x45, 0x55, 0x5D, 0x65, 0x6D, 0x75, 0x7D:
|
||||||
|
result = "RETN"
|
||||||
|
case 0x46, 0x4E, 0x66, 0x6E:
|
||||||
|
result = "IM 0"
|
||||||
|
case 0x47:
|
||||||
|
result = "LD I" + sep + "A"
|
||||||
|
case 0x48:
|
||||||
|
result = "IN C" + sep + "(C)"
|
||||||
|
case 0x49:
|
||||||
|
result = "OUT (C)" + sep + "C"
|
||||||
|
case 0x4A:
|
||||||
|
result = "ADC HL" + sep + "BC"
|
||||||
|
case 0x4B:
|
||||||
|
result = "LD BC" + sep + "(" + d.getW() + ")"
|
||||||
|
case 0x4D:
|
||||||
|
result = "REТI"
|
||||||
|
case 0x4F:
|
||||||
|
result = "LD R" + sep + "A"
|
||||||
|
case 0x50:
|
||||||
|
result = "IN D" + sep + "(C)"
|
||||||
|
case 0x51:
|
||||||
|
result = "OUT (C)" + sep + "D"
|
||||||
|
case 0x52:
|
||||||
|
result = "SBC HL" + sep + "DE"
|
||||||
|
case 0x53:
|
||||||
|
result = "LD (nn)" + sep + "DE"
|
||||||
|
case 0x56, 0x76:
|
||||||
|
result = "IM 1"
|
||||||
|
case 0x57:
|
||||||
|
result = "LD A" + sep + "I"
|
||||||
|
case 0x58:
|
||||||
|
result = "IN E" + sep + "(C)"
|
||||||
|
case 0x59:
|
||||||
|
result = "OUT (C)" + sep + "E"
|
||||||
|
case 0x5A:
|
||||||
|
result = "ADC HL" + sep + "DE"
|
||||||
|
case 0x5B:
|
||||||
|
result = "LD DE" + sep + "(" + d.getW() + ")"
|
||||||
|
case 0x5E, 0x7E:
|
||||||
|
result = "IM 2"
|
||||||
|
case 0x5F:
|
||||||
|
result = "LD A" + sep + "R"
|
||||||
|
case 0x60:
|
||||||
|
result = "IN H" + sep + "(C)"
|
||||||
|
case 0x61:
|
||||||
|
result = "OUT (C)" + sep + "H"
|
||||||
|
case 0x62:
|
||||||
|
result = "SBC HL" + sep + "HL"
|
||||||
|
case 0x63:
|
||||||
|
result = "LD (nn)" + sep + "HL"
|
||||||
|
case 0x67:
|
||||||
|
result = "RRD"
|
||||||
|
case 0x68:
|
||||||
|
result = "IN L" + sep + " (C)"
|
||||||
|
case 0x69:
|
||||||
|
result = "OUT (C)" + sep + "L"
|
||||||
|
case 0x6A:
|
||||||
|
result = "ADC HL" + sep + " HL"
|
||||||
|
case 0x6B:
|
||||||
|
result = "LD HL" + sep + " (nn)"
|
||||||
|
case 0x6F:
|
||||||
|
result = "RLD"
|
||||||
|
case 0x70:
|
||||||
|
result = "INF"
|
||||||
|
case 0x71:
|
||||||
|
result = "OUT (C)" + sep + " 0"
|
||||||
|
case 0x72:
|
||||||
|
result = "SBC HL" + sep + "SP"
|
||||||
|
case 0x73:
|
||||||
|
result = "LD (nn)" + sep + "SP"
|
||||||
|
case 0x78:
|
||||||
|
result = "IN A" + sep + "(C)"
|
||||||
|
case 0x79:
|
||||||
|
result = "OUT (C)" + sep + "A"
|
||||||
|
case 0x7A:
|
||||||
|
result = "ADC HL" + sep + "SP"
|
||||||
|
case 0x7B:
|
||||||
|
result = "LD SP" + sep + "(" + d.getW() + ")"
|
||||||
|
case 0xA0:
|
||||||
|
result = "LDI"
|
||||||
|
case 0xA1:
|
||||||
|
result = "CPI"
|
||||||
|
case 0xA2:
|
||||||
|
result = "INI"
|
||||||
|
case 0xA3:
|
||||||
|
result = "OUTI"
|
||||||
|
case 0xA8:
|
||||||
|
result = "LDD"
|
||||||
|
case 0xA9:
|
||||||
|
result = "CPD"
|
||||||
|
case 0xAA:
|
||||||
|
result = "IND"
|
||||||
|
case 0xAB:
|
||||||
|
result = "OUTD"
|
||||||
|
case 0xB0:
|
||||||
|
result = "LDIR"
|
||||||
|
case 0xB1:
|
||||||
|
result = "CPIR"
|
||||||
|
case 0xB2:
|
||||||
|
result = "INIR"
|
||||||
|
case 0xB3:
|
||||||
|
result = "OTIR"
|
||||||
|
case 0xB8:
|
||||||
|
result = "LDDR"
|
||||||
|
case 0xB9:
|
||||||
|
result = "CPDR"
|
||||||
|
case 0xBA:
|
||||||
|
result = "INDR"
|
||||||
|
case 0xBB:
|
||||||
|
result = "OTDR"
|
||||||
|
default:
|
||||||
|
result = fmt.Sprintf("DB 0xED, 0x%02X", opcode)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
91
src/z80/dis/z80disasm_test.go
Normal file
91
src/z80/dis/z80disasm_test.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package dis
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var disasm *Disassembler
|
||||||
|
|
||||||
|
type TestComp struct {
|
||||||
|
memory [65536]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TestComp) M1MemRead(addr uint16) byte {
|
||||||
|
return t.memory[addr]
|
||||||
|
}
|
||||||
|
func (t *TestComp) MemRead(addr uint16) byte {
|
||||||
|
return t.memory[addr]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TestComp) MemWrite(addr uint16, val byte) {
|
||||||
|
t.memory[addr] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TestComp) IORead(port uint16) byte {
|
||||||
|
return byte(port >> 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TestComp) IOWrite(port uint16, val byte) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
var testComp *TestComp
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
testComp = &TestComp{}
|
||||||
|
for i := 0; i < 65536; i++ {
|
||||||
|
testComp.memory[i] = 0x3f
|
||||||
|
}
|
||||||
|
disasm = NewDisassembler(testComp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setMemory(addr uint16, value []byte) {
|
||||||
|
for i := 0; i < len(value); i++ {
|
||||||
|
testComp.memory[addr+uint16(i)] = value[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var test = []byte{0x31, 0x2c, 0x05, 0x11, 0x0e, 0x01, 0x0e, 0x09, 0xcd, 0x05, 0x00, 0xc3, 0x00, 0x00}
|
||||||
|
|
||||||
|
func Test_LD_SP_nn(t *testing.T) {
|
||||||
|
expected := " 0100 LD SP, 0x052C"
|
||||||
|
setMemory(0x100, test)
|
||||||
|
res := disasm.Disassm(0x100)
|
||||||
|
if res != expected {
|
||||||
|
t.Errorf("Error disasm LD SP, nn, result '%s', expected '%s'", res, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_LD_DE_nn(t *testing.T) {
|
||||||
|
expected := " 0103 LD DE, 0x010E"
|
||||||
|
setMemory(0x100, test)
|
||||||
|
res := disasm.Disassm(0x103)
|
||||||
|
if res != expected {
|
||||||
|
t.Errorf("Error disasm LD DE, nn, result '%s', expected '%s'", res, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_LD_C_n(t *testing.T) {
|
||||||
|
expected := " 0106 LD C, 0x09"
|
||||||
|
setMemory(0x100, test)
|
||||||
|
res := disasm.Disassm(0x106)
|
||||||
|
if res != expected {
|
||||||
|
t.Errorf("Error disasm LD C, n, result '%s', expected '%s'", res, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_CALL_nn(t *testing.T) {
|
||||||
|
expected := " 0108 CALL 0x0005"
|
||||||
|
setMemory(0x100, test)
|
||||||
|
res := disasm.Disassm(0x108)
|
||||||
|
if res != expected {
|
||||||
|
t.Errorf("Error disasm CALL nn, result '%s', expected '%s'", res, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_JP_nn(t *testing.T) {
|
||||||
|
expected := " 010B JP 0x0000"
|
||||||
|
setMemory(0x100, test)
|
||||||
|
res := disasm.Disassm(0x10b)
|
||||||
|
if res != expected {
|
||||||
|
t.Errorf("Error disasm JP nn, result '%s', expected '%s'", res, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
18913
src/z80/tests.expected
Normal file
18913
src/z80/tests.expected
Normal file
File diff suppressed because it is too large
Load Diff
9153
src/z80/tests.in
Normal file
9153
src/z80/tests.in
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user