ZEsarUX ZRCP Partial debug protocol implementation

This commit is contained in:
Роман Бойков 2026-03-14 00:02:00 +03:00
parent b70b02a4be
commit b200f269a0
18 changed files with 992 additions and 314 deletions

View File

@ -9,6 +9,7 @@ import (
const defaultMonitorFile = "rom/MON_v5.bin" const defaultMonitorFile = "rom/MON_v5.bin"
const defaultCPMFile = "rom/CPM_v5.bin" const defaultCPMFile = "rom/CPM_v5.bin"
const DefaultDebufPort = 10000
type OkEmuConfig struct { type OkEmuConfig struct {
LogFile string `yaml:"logFile"` LogFile string `yaml:"logFile"`
@ -17,6 +18,8 @@ type OkEmuConfig struct {
CPMFile string `yaml:"cpmFile"` CPMFile string `yaml:"cpmFile"`
FloppyB string `yaml:"floppyB"` FloppyB string `yaml:"floppyB"`
FloppyC string `yaml:"floppyC"` FloppyC string `yaml:"floppyC"`
Host string `yaml:"host"`
Port int `yaml:"port"`
} }
var config *OkEmuConfig var config *OkEmuConfig
@ -59,7 +62,9 @@ func LoadConfig() {
} }
func checkConfig(conf *OkEmuConfig) { func checkConfig(conf *OkEmuConfig) {
if conf.Host == "" {
conf.Host = "localhost"
}
} }
func setDefaultConf(conf *OkEmuConfig) { func setDefaultConf(conf *OkEmuConfig) {
@ -75,4 +80,8 @@ func setDefaultConf(conf *OkEmuConfig) {
if conf.CPMFile == "" { if conf.CPMFile == "" {
conf.CPMFile = defaultCPMFile conf.CPMFile = defaultCPMFile
} }
if conf.Port < 80 || conf.Port > 65535 {
log.Infof("Port %d incorrect, using default: %d", conf.Port, DefaultDebufPort)
conf.Port = DefaultDebufPort
}
} }

469
debuger/listener.go Normal file
View File

@ -0,0 +1,469 @@
package debuger
import (
"bufio"
"fmt"
"io"
"net"
"okemu/config"
"okemu/okean240"
"os"
"strings"
//"okemu/logger"
"strconv"
log "github.com/sirupsen/logrus"
)
const welcomeMessage = "Welcome to ZEsarUX remote command protocol (ZRCP)\nWrite help for available commands\n\ncommand> "
const emptyResponse = "\ncommand> "
const aboutResponse = "ZEsarUX remote command protocol"
const getVersionResponse = "12.1"
const getRegistersResponse = "PC=%04x SP=%04x AF=%04x BC=%04x HL=%04x DE=%04x IX=%04x IY=%04x AF'=%04x BC'=%04x HL'=%04x DE'=%04x I=%02x R=%02x F=%s F'=%s MEMPTR=%04x IM0 IFF%s VPS: 0 MMU=00000000000000000000000000000000"
const inCpuStepResponse = "\ncommand@cpu-step> "
const getMachineResponse = "64K RAM, no ZX\n"
const respErrorLoading = "ERROR loading file"
const quitResponse = "Sayonara baby\n"
// Receive messages, split to strings and parse
func handleConnection(c net.Conn) {
reader := bufio.NewReader(c)
writer := bufio.NewWriter(c)
if !writeWelcomeMessage(writer) {
return
}
activeWriter = writer
for {
str, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
break
} else {
log.Errorf("TCP error: %v", err)
return
}
}
if !HandleCommand(str, writer) {
log.Debug("Closing connection")
writeResponseMessage(writer, quitResponse)
break
}
//byteBuffer.WriteByte(b)
}
activeWriter = nil
//log.Trace("TCP Connection closed")
err := c.Close()
if err != nil {
log.Warnf("Can not close socket: %v", err)
}
}
var activeWriter *bufio.Writer = nil
func writeWelcomeMessage(writer *bufio.Writer) bool {
return writeResponseMessage(writer, welcomeMessage)
}
//command@cpu-step
func writeResponseMessage(writer *bufio.Writer, message string) bool {
prompt := emptyResponse
if computer.IsStepMode() {
prompt = inCpuStepResponse
}
_, err := writer.WriteString(message + prompt)
if err != nil {
log.Errorf("TCP error: %v", err)
return false
}
err = writer.Flush()
if err != nil {
log.Errorf("TCP error: %v", err)
return false
}
return true
}
var computer *okean240.ComputerType
// SetupTcpHandler Setup TCP listener, handle connections
func SetupTcpHandler(config *config.OkEmuConfig, comp *okean240.ComputerType) {
port := config.Host + ":" + strconv.Itoa(config.Port)
computer = comp
log.Infof("Serve TCP connections on %s", port)
l, err := net.Listen("tcp4", port)
if err != nil {
fmt.Println(err)
return
}
defer func(l net.Listener) {
err := l.Close()
if err != nil {
log.Warnf("Error closing listener connection %v", err)
}
}(l)
for {
c, err := l.Accept()
if err != nil {
log.Errorf("Accept connection: %v", err)
return
}
go handleConnection(c)
}
}
// HandleCommand HandleLogLine Parse log line(s) and send it to redis
func HandleCommand(str string, writer *bufio.Writer) bool {
quit := false
str = strings.TrimSpace(str)
if str == "" {
return false
}
log.Debugf("Command: '%s'", str)
pos := strings.Index(str, " ")
cmd := str
params := ""
if pos > 1 {
cmd = str[:pos]
params = strings.TrimSpace(str[pos+1:])
}
switch cmd {
case "cpu-step":
computer.Do()
writeResponseMessage(writer, " "+fmt.Sprintf("%04X", computer.GetCPUState().PC))
case "run":
_, e := writer.WriteString("Running until a breakpoint, key press or data sent, menu opening or other event\n")
if e != nil {
log.Warnf("Error writing to buffer: %v", e)
}
e = writer.Flush()
if e != nil {
log.Warnf("Error flushing the buffer: %v", e)
}
computer.SetRunMode(true)
case "get-tstates-partial":
writeResponseMessage(writer, strconv.FormatUint(computer.Cycles(), 10))
case "close-all-menus":
writeResponseMessage(writer, "")
case "about":
writeResponseMessage(writer, aboutResponse)
case "get-version":
writeResponseMessage(writer, getVersionResponse)
case "get-registers":
writeResponseMessage(writer, registersResponse())
case "set-register":
writeResponseMessage(writer, setRegister(params))
case "hard-reset-cpu":
computer.Reset()
writeResponseMessage(writer, "")
case "enter-cpu-step":
computer.SetStepMode(true)
writeResponseMessage(writer, "")
case "exit-cpu-step":
computer.SetStepMode(false)
writeResponseMessage(writer, "")
case "set-debug-settings":
log.Debugf("Set debug settings to %s", params)
writeResponseMessage(writer, "")
case "get-current-machine":
writeResponseMessage(writer, getMachineResponse)
case "clear-membreakpoints":
computer.ClearMemBreakpoints()
writeResponseMessage(writer, "")
case "enable-breakpoints":
computer.SetBreakpointsEnabled(true)
writeResponseMessage(writer, "")
case "disable-breakpoints":
computer.SetBreakpointsEnabled(false)
writeResponseMessage(writer, "")
case "enable-breakpoint":
writeResponseMessage(writer, setBreakpointState(params, true))
case "disable-breakpoint":
writeResponseMessage(writer, setBreakpointState(params, false))
case "get-cpu-frequency":
writeResponseMessage(writer, strconv.Itoa(int(computer.CPUFrequency())))
case "set-breakpoint":
// 1 PC=0010Bh
writeResponseMessage(writer, setBreakpoint(params))
case "cpu-code-coverage":
//"enabled no"
writeResponseMessage(writer, "")
case "cpu-history":
// "enabled yes"
// "set-max-size 1000"
// "clear"
// "started yes"
// "ignrephalt yes"
// "ignrepldxr yes"
writeResponseMessage(writer, "")
case "extended-stack":
// "enabled no"
// "enabled yes"
if strings.HasPrefix(params, "get") {
writeResponseMessage(writer, getExtendedStack(params))
} else {
writeResponseMessage(writer, "")
}
case "load-binary":
writeResponseMessage(writer, loadBinary(params))
case "read-memory":
writeResponseMessage(writer, readMemory(params))
case "quit":
quit = true
default:
log.Debugf("Unhandled Command: %s", str)
writeResponseMessage(writer, "")
}
return !quit
}
func loadBinary(param string) string {
params := strings.Split(param, " ")
if len(params) < 2 {
log.Errorf("Invalid load parameters: %s", param)
return respErrorLoading
}
fn := strings.TrimSpace(params[0])
if strings.HasPrefix(fn, "\"") {
fn = fn[1:]
}
if strings.HasSuffix(fn, "\"") && len(fn) > 1 {
fn = fn[:len(fn)-1]
}
offset, e := strconv.Atoi(params[1])
length := 0
if e != nil || offset < 0 || offset > 65535 || len(fn) == 0 {
log.Errorf("Invalid load parameters: %s", param)
return respErrorLoading
}
if len(params) > 2 {
length, e = strconv.Atoi(params[1])
if e != nil {
length = 0
}
}
data, err := os.ReadFile(fn)
if err != nil {
log.Errorf("Error reading file: %v", err)
return respErrorLoading
}
if length != 0 && len(data) < length {
log.Errorf("File too short. Expected %d bytes, got %d", len(data), length)
return respErrorLoading
}
if length == 0 {
length = len(data)
}
// Loaded Ok, move file to memory
for addr := uint16(0); addr < uint16(length); addr++ {
computer.MemWrite(addr+uint16(offset), data[addr])
}
return ""
}
func toW(hi, lo byte) uint16 {
return uint16(lo) | (uint16(hi) << 8)
}
func iifStr(iif1, iif2 bool) string {
flags := []byte{'-', '-'}
if iif1 {
flags[0] = '1'
}
if iif2 {
flags[1] = '2'
}
return string(flags)
}
// 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
// R=%2x F=%s F'=%s MEMPTR=%4x IM0 IFF-- VPS: 0 MMU=00000000000000000000000000000000
func registersResponse() string {
state := computer.GetCPUState()
resp := fmt.Sprintf(getRegistersResponse,
state.PC,
state.SP,
toW(state.A, state.Flags.GetFlags()),
toW(state.B, state.C),
toW(state.H, state.L),
toW(state.D, state.E),
state.IX,
state.IY,
toW(state.AAlt, state.FlagsAlt.GetFlags()),
toW(state.BAlt, state.CAlt),
toW(state.HAlt, state.LAlt),
toW(state.DAlt, state.EAlt),
state.I,
state.R,
state.Flags.GetFlagsStr(),
state.FlagsAlt.GetFlagsStr(),
state.MemPtr,
iifStr(state.Iff1, state.Iff2),
)
log.Debug(resp)
return resp
}
func setRegister(param string) string {
state := computer.GetCPUState()
params := strings.Split(param, "=")
if len(params) != 2 {
log.Errorf("Invalid set register parameter: %s", param)
return registersResponse()
}
val, e := strconv.Atoi(params[1])
if e != nil {
log.Errorf("Invalid set register parameter value: %s", params[1])
return registersResponse()
}
switch params[0] {
case "SP":
state.SP = uint16(val)
case "PC":
state.PC = uint16(val)
case "IX":
state.IX = uint16(val)
case "IY":
state.IY = uint16(val)
case "A":
state.A = uint8(val)
case "B":
state.B = uint8(val)
case "C":
state.C = uint8(val)
case "D":
state.D = uint8(val)
case "E":
state.E = uint8(val)
case "H":
state.H = uint8(val)
case "L":
state.L = uint8(val)
case "I":
state.I = uint8(val)
case "R":
state.R = uint8(val)
default:
log.Errorf("Unsupported set register parameter: %s", param)
}
computer.SetCPUState(state)
return registersResponse()
}
func readMemory(param string) string {
params := strings.Split(param, " ")
if len(params) != 2 {
log.Errorf("Invalid read memory parameter: %s", param)
return registersResponse()
}
offset, e := strconv.Atoi(params[0])
if e != nil {
log.Errorf("Invalid read memory parameter offset: %s", params[0])
}
size, e := strconv.Atoi(params[1])
if e != nil {
log.Errorf("Invalid read memory parameter size: %s", params[1])
}
resp := ""
for i := 0; i < size; i++ {
resp += fmt.Sprintf("%02X", computer.MemRead(uint16(offset)+uint16(i)))
}
log.Tracef("ReadMemory[%d,%d]:\n%s", offset, size, resp)
return resp
}
func getExtendedStack(param string) string {
params := strings.Split(param, " ")
if len(params) < 2 {
log.Errorf("Will be 2 or 3 params: %s", param)
return ""
}
size, err := strconv.Atoi(params[1])
if err != nil || size < 0 || size > 65636 {
log.Errorf("Invalid size param: %s", param)
}
sp := computer.GetCPUState().SP
if len(params) == 3 {
psp, err := strconv.ParseUint(params[2], 10, 16)
if err != nil {
log.Errorf("Invalid SP param: %s", params[2])
} else {
sp = uint16(psp)
}
}
resp := ""
spEnd := sp - uint16(size*2)
for i := sp; i > spEnd; i -= 2 {
resp += fmt.Sprintf("%04XH default\n", computer.MemRead(i))
}
log.Debugf("Stack[%d,%d]:\n%s", sp, size, resp)
return resp
}
func setBreakpointState(param string, enable bool) string {
no, e := strconv.Atoi(param)
if e != nil {
log.Errorf("Invalid breakpoint parameter: %s", param)
return ""
}
if enable && !computer.IsBreakpointsEnabled() {
return "Error. You must enable breakpoints first"
}
computer.SetBreakpointEnabled(uint16(no), enable)
return ""
}
func setBreakpoint(param string) string {
// 1 PC=0010Bh
params := strings.Split(param, " ")
if len(params) != 2 {
log.Errorf("Invalid set breakpoint parameters: %s", param)
return ""
}
no, e := strconv.Atoi(params[0])
if e != nil || no > okean240.MaxBreakpoints || no < 1 {
log.Errorf("Invalid breakpoint number: %s", params[0])
return ""
}
regv := strings.Split(params[1], "=")
if len(regv) != 2 {
log.Errorf("Invalid breakpoint parameter: %s", params[1])
return ""
}
addr, e := strconv.ParseUint(strings.TrimSuffix(regv[1], "h"), 16, 32)
if e != nil || addr < 0 || addr >= 65535 {
log.Errorf("Invalid breakpoint address: %s", regv[1])
return ""
}
if regv[0] == "PC" {
computer.SetBreakpoint(uint16(no), uint16(addr))
} else {
log.Errorf("Unsupported BP: %s", params[1])
}
return ""
}
func BreakpointHit(no uint16) {
if activeWriter != nil {
pc := computer.GetCPUState().PC
rep := fmt.Sprintf("Breakpoint fired: PC=%XH\n %04X NOP", pc, pc)
log.Debug(rep)
writeResponseMessage(activeWriter, rep)
}
}

74
dezog/protocol.go Normal file
View File

@ -0,0 +1,74 @@
package dezog
// Command - DZRP protocol command
type Command struct {
length uint32 // Length of the payload data. (little endian)
sequence uint8 // Sequence number, 1-255. Increased with each command
command uint8 // Command ID
data []byte // Payload
}
// Response - response on DZRP protocol command
type Response struct {
length uint32 // Length of the following data beginning with the sequence number. (little endian)
sequence uint8 // Sequence number, same as command.
data []byte // Payload
}
// Notification - message from emulator to DEZOG
type Notification struct {
seqNo uint8 // Instead of Seq No.
command uint8 // NTF_PAUSE = 1
breakReason uint8 // Break reason: 0 = no reason (e.g. a step-over), 1 = manual break, 2 = breakpoint hit,
// 3 = watchpoint hit read access, 4 = watchpoint hit write access, 255 = some other reason:
//the reason string might have useful information for the user
address uint16 // Breakpoint or watchpoint address.
bank uint8 // The bank+1 of the breakpoint or watchpoint address.
reason string // Null-terminated break reason string. Might in theory have almost 2^32 byte length.
// In practice, it will be normally less than 256. If reason string is empty it will contain at
// least a 0.
}
const NTF_PAUSE = 1
// Notification, Break reasons
const (
BR_REASON_MANUAL = 1
BR_REASON_BP_HIT = 2
BR_REASON_WP_HIT_R = 3
BR_REASON_WP_HIT_W = 4
BR_REASON_OTHER = 255
)
// DEZOG Commands to emulator
const (
CMD_INIT = 1
CMD_CLOSE = 2
CMD_GET_REGISTERS = 3
CMD_SET_REGISTER = 4
CMD_WRITE_BANK = 5
CMD_CONTINUE = 6
CMD_PAUSE = 7
CMD_READ_MEM = 8
CMD_WRITE_MEM = 9
CMD_SET_SLOT = 10
CMD_GET_TBBLUE_REG = 11
CMD_SET_BORDER = 12
CMD_SET_BREAKPOINTS = 13
CMD_RESTORE_MEM = 14
CMD_LOOPBACK = 15
CMD_GET_SPRITES_PALETTE = 16
CMD_GET_SPRITES_CLIP_WINDOW_AND_CONTROL = 17
CMD_GET_SPRITES = 18
CMD_GET_SPRITE_PATTERNS = 19
CMD_READ_PORT = 20
CMD_WRITE_PORT = 21
CMD_EXEC_ASM = 22
CMD_INTERRUPT_ON_OFF = 23
CMD_ADD_BREAKPOINT = 40
CMD_REMOVE_BREAKPOINT = 41
CMD_ADD_WATCHPOINT = 42
CMD_REMOVE_WATCHPOINT = 43
CMD_READ_STATE = 50
CMD_WRITE_STATE = 51
)

6
go.mod
View File

@ -3,12 +3,13 @@ module okemu
go 1.25 go 1.25
require ( require (
fyne.io/fyne/v2 v2.7.3
github.com/howeyc/crc16 v0.0.0-20171223171357-2b2a61e366a6
github.com/sirupsen/logrus v1.9.4 github.com/sirupsen/logrus v1.9.4
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
fyne.io/fyne/v2 v2.7.3 // indirect
fyne.io/systray v1.12.0 // indirect fyne.io/systray v1.12.0 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect github.com/BurntSushi/toml v1.5.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
@ -17,18 +18,17 @@ require (
github.com/fyne-io/gl-js v0.2.0 // indirect github.com/fyne-io/gl-js v0.2.0 // indirect
github.com/fyne-io/glfw-js v0.3.0 // indirect github.com/fyne-io/glfw-js v0.3.0 // indirect
github.com/fyne-io/image v0.1.1 // indirect github.com/fyne-io/image v0.1.1 // indirect
github.com/fyne-io/mobile v0.1.2 // indirect
github.com/fyne-io/oksvg v0.2.0 // indirect github.com/fyne-io/oksvg v0.2.0 // indirect
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect
github.com/go-text/render v0.2.0 // indirect github.com/go-text/render v0.2.0 // indirect
github.com/go-text/typesetting v0.3.3 // indirect github.com/go-text/typesetting v0.3.3 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff // indirect
github.com/hack-pad/go-indexeddb v0.3.2 // indirect github.com/hack-pad/go-indexeddb v0.3.2 // indirect
github.com/hack-pad/safejs v0.1.0 // indirect github.com/hack-pad/safejs v0.1.0 // indirect
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
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

82
go.sum
View File

@ -1,20 +1,16 @@
fyne.io/fyne v1.4.3 h1:356CnXCiYrrfaLGsB7qLK3c6ktzyh8WR05v/2RBu51I=
fyne.io/fyne v1.4.3/go.mod h1:8kiPBNSDmuplxs9WnKCkaWYqbcXFy0DeAzwa6PBO9Z8=
fyne.io/fyne/v2 v2.7.3 h1:xBT/iYbdnNHONWO38fZMBrVBiJG8rV/Jypmy4tVfRWE= fyne.io/fyne/v2 v2.7.3 h1:xBT/iYbdnNHONWO38fZMBrVBiJG8rV/Jypmy4tVfRWE=
fyne.io/fyne/v2 v2.7.3/go.mod h1:gu+dlIcZWSzKZmnrY8Fbnj2Hirabv2ek+AKsfQ2bBlw= fyne.io/fyne/v2 v2.7.3/go.mod h1:gu+dlIcZWSzKZmnrY8Fbnj2Hirabv2ek+AKsfQ2bBlw=
fyne.io/systray v1.12.0 h1:CA1Kk0e2zwFlxtc02L3QFSiIbxJ/P0n582YrZHT7aTM= fyne.io/systray v1.12.0 h1:CA1Kk0e2zwFlxtc02L3QFSiIbxJ/P0n582YrZHT7aTM=
fyne.io/systray v1.12.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs= fyne.io/systray v1.12.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
github.com/fredbi/uri v1.1.1 h1:xZHJC08GZNIUhbP5ImTHnt5Ya0T8FI2VAwI/37kh2Ko= github.com/fredbi/uri v1.1.1 h1:xZHJC08GZNIUhbP5ImTHnt5Ya0T8FI2VAwI/37kh2Ko=
github.com/fredbi/uri v1.1.1/go.mod h1:4+DZQ5zBjEwQCDmXW5JdIjz0PUA+yJbvtBv+u+adr5o= github.com/fredbi/uri v1.1.1/go.mod h1:4+DZQ5zBjEwQCDmXW5JdIjz0PUA+yJbvtBv+u+adr5o=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fyne-io/gl-js v0.2.0 h1:+EXMLVEa18EfkXBVKhifYB6OGs3HwKO3lUElA0LlAjs= github.com/fyne-io/gl-js v0.2.0 h1:+EXMLVEa18EfkXBVKhifYB6OGs3HwKO3lUElA0LlAjs=
@ -23,116 +19,66 @@ github.com/fyne-io/glfw-js v0.3.0 h1:d8k2+Y7l+zy2pc7wlGRyPfTgZoqDf3AI4G+2zOWhWUk
github.com/fyne-io/glfw-js v0.3.0/go.mod h1:Ri6te7rdZtBgBpxLW19uBpp3Dl6K9K/bRaYdJ22G8Jk= github.com/fyne-io/glfw-js v0.3.0/go.mod h1:Ri6te7rdZtBgBpxLW19uBpp3Dl6K9K/bRaYdJ22G8Jk=
github.com/fyne-io/image v0.1.1 h1:WH0z4H7qfvNUw5l4p3bC1q70sa5+YWVt6HCj7y4VNyA= github.com/fyne-io/image v0.1.1 h1:WH0z4H7qfvNUw5l4p3bC1q70sa5+YWVt6HCj7y4VNyA=
github.com/fyne-io/image v0.1.1/go.mod h1:xrfYBh6yspc+KjkgdZU/ifUC9sPA5Iv7WYUBzQKK7JM= github.com/fyne-io/image v0.1.1/go.mod h1:xrfYBh6yspc+KjkgdZU/ifUC9sPA5Iv7WYUBzQKK7JM=
github.com/fyne-io/mobile v0.1.2 h1:0HaXDtOOwyOTn3Umi0uKVCOgJtfX73c6unC4U8i5VZU=
github.com/fyne-io/mobile v0.1.2/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY=
github.com/fyne-io/oksvg v0.2.0 h1:mxcGU2dx6nwjJsSA9PCYZDuoAcsZ/OuJlvg/Q9Njfo8= github.com/fyne-io/oksvg v0.2.0 h1:mxcGU2dx6nwjJsSA9PCYZDuoAcsZ/OuJlvg/Q9Njfo8=
github.com/fyne-io/oksvg v0.2.0/go.mod h1:dJ9oEkPiWhnTFNCmRgEze+YNprJF7YRbpjgpWS4kzoI= github.com/fyne-io/oksvg v0.2.0/go.mod h1:dJ9oEkPiWhnTFNCmRgEze+YNprJF7YRbpjgpWS4kzoI=
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw=
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw2H/zl9nrd3eCZfYV+NfQA= github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw2H/zl9nrd3eCZfYV+NfQA=
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200625191551-73d3c3675aa3 h1:q521PfSp5/z6/sD9FZZOWj4d1MLmfQW8PkRnI9M6PCE=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200625191551-73d3c3675aa3/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc= github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc=
github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU= github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU=
github.com/go-text/typesetting v0.3.3 h1:ihGNJU9KzdK2QRDy1Bm7FT5RFQoYb+3n3EIhI/4eaQc= github.com/go-text/typesetting v0.3.3 h1:ihGNJU9KzdK2QRDy1Bm7FT5RFQoYb+3n3EIhI/4eaQc=
github.com/go-text/typesetting v0.3.3/go.mod h1:vIRUT25mLQaSh4C8H/lIsKppQz/Gdb8Pu/tNwpi52ts= github.com/go-text/typesetting v0.3.3/go.mod h1:vIRUT25mLQaSh4C8H/lIsKppQz/Gdb8Pu/tNwpi52ts=
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= github.com/go-text/typesetting-utils v0.0.0-20250618110550-c820a94c77b8 h1:4KCscI9qYWMGTuz6BpJtbUSRzcBrUSSE0ENMJbNSrFs=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/go-text/typesetting-utils v0.0.0-20250618110550-c820a94c77b8/go.mod h1:3/62I4La/HBRX9TcTpBj4eipLiwzf+vhI+7whTc9V7o=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y=
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/hack-pad/go-indexeddb v0.3.2 h1:DTqeJJYc1usa45Q5r52t01KhvlSN02+Oq+tQbSBI91A= github.com/hack-pad/go-indexeddb v0.3.2 h1:DTqeJJYc1usa45Q5r52t01KhvlSN02+Oq+tQbSBI91A=
github.com/hack-pad/go-indexeddb v0.3.2/go.mod h1:QvfTevpDVlkfomY498LhstjwbPW6QC4VC/lxYb0Kom0= github.com/hack-pad/go-indexeddb v0.3.2/go.mod h1:QvfTevpDVlkfomY498LhstjwbPW6QC4VC/lxYb0Kom0=
github.com/hack-pad/safejs v0.1.0 h1:qPS6vjreAqh2amUqj4WNG1zIw7qlRQJ9K10eDKMCnE8= github.com/hack-pad/safejs v0.1.0 h1:qPS6vjreAqh2amUqj4WNG1zIw7qlRQJ9K10eDKMCnE8=
github.com/hack-pad/safejs v0.1.0/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio= github.com/hack-pad/safejs v0.1.0/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc= github.com/howeyc/crc16 v0.0.0-20171223171357-2b2a61e366a6 h1:IIVxLyDUYErC950b8kecjoqDet8P5S4lcVRUOM6rdkU=
github.com/howeyc/crc16 v0.0.0-20171223171357-2b2a61e366a6/go.mod h1:JslaLRrzGsOKJgFEPBP65Whn+rdwDQSk0I0MCRFe2Zw=
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade h1:FmusiCI1wHw+XQbvL9M+1r/C3SPqKrmBaIOYwVfQoDE= github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade h1:FmusiCI1wHw+XQbvL9M+1r/C3SPqKrmBaIOYwVfQoDE=
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o= github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M= github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M=
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw= github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lucor/goinfo v0.0.0-20200401173949-526b5363a13a/go.mod h1:ORP3/rB5IsulLEBwQZCJyyV6niqmI7P4EWSmkug+1Ng=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/nicksnyder/go-i18n/v2 v2.5.1 h1:IxtPxYsR9Gp60cGXjfuR/llTqV8aYMsC472zD0D1vHk= github.com/nicksnyder/go-i18n/v2 v2.5.1 h1:IxtPxYsR9Gp60cGXjfuR/llTqV8aYMsC472zD0D1vHk=
github.com/nicksnyder/go-i18n/v2 v2.5.1/go.mod h1:DrhgsSDZxoAfvVrBVLXoxZn/pN5TXqaDbq7ju94viiQ= github.com/nicksnyder/go-i18n/v2 v2.5.1/go.mod h1:DrhgsSDZxoAfvVrBVLXoxZn/pN5TXqaDbq7ju94viiQ=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 h1:HunZiaEKNGVdhTRQOVpMmj5MQnGnv+e8uZNu3xFLgyM=
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE= github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q= github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 h1:m59mIOBO4kfcNCEzJNy71UkeF4XIx2EVmL9KLwDQdmM=
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ= github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE= github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ= golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8= golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190808195139-e713427fea3f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200328031815-3db5fc6bac03/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

44
main.go
View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"image/color" "image/color"
"okemu/config" "okemu/config"
"okemu/debuger"
"okemu/logger" "okemu/logger"
"okemu/okean240" "okemu/okean240"
"sync/atomic" "sync/atomic"
@ -21,11 +22,14 @@ import (
var Version = "v1.0.0" var Version = "v1.0.0"
var BuildTime = "2026-03-01" var BuildTime = "2026-03-01"
//go:embed hex/format.hex //go:embed hex/m80.hex
var serialBytes []byte var serialBytes []byte
//go:embed bin/FORMAT.COM //go:embed bin/main.com
var ramBytes []byte var ramBytes1 []byte
//go:embed bin/PLOT.BAS
var ramBytes2 []byte
var needReset = false var needReset = false
var fullSpeed atomic.Bool var fullSpeed atomic.Bool
@ -53,6 +57,8 @@ func main() {
go emulator(computer) go emulator(computer)
go screen(computer, raster, label) go screen(computer, raster, label)
go debuger.SetupTcpHandler(conf, computer)
(*w).ShowAndRun() (*w).ShowAndRun()
} }
@ -106,14 +112,22 @@ func mainWindow(computer *okean240.ComputerType) (*fyne.Window, *canvas.Raster,
widget.NewButton("Save Floppy", func() { widget.NewButton("Save Floppy", func() {
computer.SaveFloppy() computer.SaveFloppy()
}), }),
widget.NewButton("RUN", func() { widget.NewButton("RUN1", func() {
computer.SetRamBytes(ramBytes) computer.SetRamBytes(ramBytes1)
}),
widget.NewButton("RUN2", func() {
computer.SetRamBytes(ramBytes2)
}), }),
widget.NewButton("DUMP", func() { widget.NewButton("DUMP", func() {
computer.Dump(0x100, 15000) computer.Dump(0x100, 15000)
}), }),
widget.NewCheck("Full speed", func(b bool) { widget.NewCheck("Full speed", func(b bool) {
fullSpeed.Store(b) fullSpeed.Store(b)
if b {
computer.SetCPUFrequency(50_000_000)
} else {
computer.SetCPUFrequency(2_500_000)
}
}), }),
widget.NewSeparator(), widget.NewSeparator(),
widget.NewButton("Reset", func() { widget.NewButton("Reset", func() {
@ -170,14 +184,22 @@ func emulator(computer *okean240.ComputerType) {
// 1.5 MHz // 1.5 MHz
computer.TimerClk() computer.TimerClk()
} }
if fullSpeed.Load() { if !computer.IsStepMode() || computer.IsRunMode() {
computer.Do() var bp uint16 = 0
} else { if fullSpeed.Load() {
if ticks >= nextClock { _, bp = computer.Do()
nextClock = ticks + uint64(computer.Do())*ticksPerTact } else {
if ticks >= nextClock {
var t uint32
t, bp = computer.Do()
nextClock = ticks + uint64(t)*ticksPerTact
}
}
// Breakpoint hit
if bp > 0 {
debuger.BreakpointHit(bp)
} }
} }
//computer.Do()
if needReset { if needReset {
computer.Reset() computer.Reset()
needReset = false needReset = false

View File

@ -9,6 +9,7 @@ import (
"okemu/okean240/pic" "okemu/okean240/pic"
"okemu/okean240/pit" "okemu/okean240/pit"
"okemu/okean240/usart" "okemu/okean240/usart"
"okemu/z80"
"os" "os"
//"okemu/z80" //"okemu/z80"
@ -19,6 +20,13 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
const DefaultCPUFrequency = 2_500_000
type Breakpoint struct {
addr uint16
enabled bool
}
type ComputerType struct { type ComputerType struct {
cpu *c99.Z80 cpu *c99.Z80
memory Memory memory Memory
@ -38,9 +46,15 @@ type ComputerType struct {
kbdBuffer []byte kbdBuffer []byte
vShift byte vShift byte
hShift byte hShift byte
stepMode bool
runMode bool
bpEnabled bool
breakpoints [MaxBreakpoints]Breakpoint
//aOffset uint16 //aOffset uint16
cpuFrequency uint32
} }
const MaxBreakpoints = 256
const VRAMBlock0 = 3 const VRAMBlock0 = 3
const VRAMBlock1 = 7 const VRAMBlock1 = 7
const VidVsuBit = 0x80 const VidVsuBit = 0x80
@ -57,9 +71,69 @@ type ComputerInterface interface {
PutCtrlKey(shortcut fyne.Shortcut) PutCtrlKey(shortcut fyne.Shortcut)
SaveFloppy() SaveFloppy()
LoadFloppy() LoadFloppy()
CPUState() *z80.Z80CPU
SetCPUState(state *z80.Z80CPU)
StepMode() bool
SetStepMode(step bool)
ClearMemBreakpoints()
SetBreakpointsEnabled(enabled bool)
IsBreakpoint() bool
//Dump(start uint16, length uint16) //Dump(start uint16, length uint16)
} }
func (c *ComputerType) SetBreakpointsEnabled(enabled bool) {
c.bpEnabled = enabled
}
func (c *ComputerType) IsBreakpointsEnabled() bool {
return c.bpEnabled
}
func (c *ComputerType) SetBreakpoint(no uint16, addr uint16) {
if no > 0 && no <= MaxBreakpoints {
c.breakpoints[no-1].addr = addr
log.Debugf("BP[%d] SET AT PC=%04X", no, addr)
} else {
log.Warnf("Breakpoint number %d out or range!", no)
}
}
func (c *ComputerType) SetBreakpointEnabled(no uint16, enabled bool) {
if no <= MaxBreakpoints && no > 0 {
c.breakpoints[no-1].enabled = enabled
} else {
log.Warnf("Breakpoint number %d out or range!", no)
}
}
func (c *ComputerType) IsBreakpointEnabled(no uint16) bool {
if no <= MaxBreakpoints && no > 0 {
return c.breakpoints[no-1].enabled
}
log.Warnf("Breakpoint number %d out or range!", no)
return false
}
func (c *ComputerType) ClearMemBreakpoints() {
log.Warnf("Clearing memory bpEnabled unimplemented!")
}
func (c *ComputerType) SetStepMode(step bool) {
c.stepMode = step
}
func (c *ComputerType) IsStepMode() bool {
return c.stepMode
}
func (c *ComputerType) GetCPUState() *z80.Z80CPU {
return c.cpu.GetState()
}
func (c *ComputerType) SetCPUState(state *z80.Z80CPU) {
c.cpu.SetState(state)
}
func (c *ComputerType) M1MemRead(addr uint16) byte { func (c *ComputerType) M1MemRead(addr uint16) byte {
return c.memory.M1MemRead(addr) return c.memory.M1MemRead(addr)
} }
@ -96,7 +170,14 @@ func New(cfg *config.OkEmuConfig) *ComputerType {
c.usart = usart.New() c.usart = usart.New()
c.pic = pic.New() c.pic = pic.New()
c.fdc = fdc.New(cfg) c.fdc = fdc.New(cfg)
c.cpuFrequency = DefaultCPUFrequency
c.bpEnabled = false
c.breakpoints = [256]Breakpoint{}
for i := range c.breakpoints {
c.breakpoints[i] = Breakpoint{}
c.breakpoints[i].enabled = false
c.breakpoints[i].addr = 0
}
return &c return &c
} }
@ -114,14 +195,32 @@ func (c *ComputerType) Reset() {
} }
func (c *ComputerType) Do() uint32 { func (c *ComputerType) SetRunMode(run bool) {
c.runMode = run
}
func (c *ComputerType) IsRunMode() bool {
return c.runMode
}
func (c *ComputerType) Do() (uint32, uint16) {
// check breakpoints
if c.bpEnabled && c.runMode {
for no, bp := range c.breakpoints {
if bp.enabled && bp.addr == c.cpu.GetState().PC {
c.runMode = false
return 0, uint16(no + 1)
}
}
}
ticks := c.cpu.RunInstruction() ticks := c.cpu.RunInstruction()
c.cycles += uint64(ticks) c.cycles += uint64(ticks)
//pc := c.cpu.GetState().PC //pc := c.cpu.GetState().PC
//if pc >= 0xfea3 && pc <= 0xff25 { //if pc >= 0xfea3 && pc <= 0xff25 {
// c.cpu.DebugOutput() // c.cpu.DebugOutput()
//} //}
return ticks return ticks, 0
} }
func (c *ComputerType) GetPixel(x uint16, y uint16) color.RGBA { func (c *ComputerType) GetPixel(x uint16, y uint16) color.RGBA {
@ -272,3 +371,11 @@ func (c *ComputerType) Dump(start uint16, length uint16) {
log.Debug("Memory dump saved successfully") log.Debug("Memory dump saved successfully")
} }
} }
func (c *ComputerType) CPUFrequency() uint32 {
return c.cpuFrequency
}
func (c *ComputerType) SetCPUFrequency(frequency uint32) {
c.cpuFrequency = frequency
}

View File

@ -106,7 +106,7 @@ const RstTimerNo = 4
// PicDd75a Port A (a0=0) // PicDd75a Port A (a0=0)
const PicDd75a = 0x80 const PicDd75a = 0x80
// PIC_DD75B Port B (a0=1) // PicDd75b Port B (a0=1)
const PicDd75b = 0x81 const PicDd75b = 0x81
/* /*
@ -116,7 +116,7 @@ const PicDd75b = 0x81
// UartDd72rd Serial data // UartDd72rd Serial data
const UartDd72rd = 0xA0 const UartDd72rd = 0xA0
// UartDd72rr Serial status [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY] // UartDd72rr Serial enabled [RST,RQ_RX,RST_ERR,PAUSE,RX_EN,RX_RDY,TX_RDY]
const UartDd72rr = 0xA1 const UartDd72rr = 0xA1
/* /*

View File

@ -15,6 +15,7 @@ import (
"slices" "slices"
"strconv" "strconv"
"github.com/howeyc/crc16"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@ -38,18 +39,21 @@ const (
// FDC Commands // FDC Commands
const ( const (
CmdRestore byte = 0x0 CmdRestore = 0x0
CmdSeek byte = 0x1 CmdSeek = 0x1
CmdStep byte = 0x2 CmdStep = 0x2
CmdStepIn byte = 0x5 CmdStepIn = 0x5
CmdStepOut byte = 0x7 CmdStepOut = 0x7
CmdReadSector byte = 0x8 CmdReadSector = 0x8
CmdReadSectorMulti byte = 0x9 CmdReadSectorMulti = 0x9
CmdWriteSector byte = 0xa CmdWriteSector = 0xa
CmdWriteTrack byte = 0xf CmdReadAddress = 0xc
CmdNoCommand byte = 0xff CmdWriteTrack = 0xf
CmdNoCommand = 0xff
) )
var interleave = []byte{1, 8, 6, 4, 2, 9, 7, 5, 3}
const ( const (
StatusTR0 = 0x04 // TR0 - Head at track 0 StatusTR0 = 0x04 // TR0 - Head at track 0
StatusRNF = 0x10 // RNF - Record not found StatusRNF = 0x10 // RNF - Record not found
@ -100,7 +104,10 @@ type FloppyDriveControllerInterface interface {
Sector() byte Sector() byte
} }
//var slicer = []uint16{1, 8, 6, 4, 2, 9, 7, 5, 3}
func getSectorNo(side byte, track byte, sector byte) uint16 { func getSectorNo(side byte, track byte, sector byte) uint16 {
//return (uint16(track)*18 + uint16(side)*9 + slicer[sector-1] - 1)
return uint16(side)*SectorsPerSide + uint16(track)*SectorPerTrack + uint16(sector) - 1 return uint16(side)*SectorsPerSide + uint16(track)*SectorPerTrack + uint16(sector) - 1
} }
@ -124,6 +131,12 @@ func (f *FloppyDriveController) GetFloppy() byte {
return floppy return floppy
} }
var crcTable *crc16.Table
func init() {
crcTable = crc16.MakeTable(0xffff)
}
func (f *FloppyDriveController) SetCmd(value byte) { func (f *FloppyDriveController) SetCmd(value byte) {
f.lastCmd = value >> 4 f.lastCmd = value >> 4
switch f.lastCmd { switch f.lastCmd {
@ -183,11 +196,21 @@ func (f *FloppyDriveController) SetCmd(value byte) {
f.drq = 0 f.drq = 0
f.status = StatusRNF f.status = StatusRNF
} }
case CmdReadAddress:
log.Tracef("CMD ReadAddress %d", value)
f.trackBuffer = []byte{f.trackNo, f.sideNo, f.sectorNo, 2}
checksum := crc16.Checksum(f.trackBuffer, crcTable)
f.trackBuffer = append(f.trackBuffer, byte(checksum))
f.trackBuffer = append(f.trackBuffer, byte(checksum>>8))
f.drq = 1
f.status = 0x0
case CmdWriteTrack: case CmdWriteTrack:
log.Tracef("CMD Write Track %x", f.trackNo) log.Tracef("CMD Write Track %x", f.trackNo)
f.drq = 1
f.status = 0x00 f.status = 0x00
f.trackBuffer = []byte{} f.trackBuffer = []byte{}
f.drq = 1
default: default:
log.Errorf("Unknown CMD: %x VAL: %x", f.lastCmd, value&0xf) log.Errorf("Unknown CMD: %x VAL: %x", f.lastCmd, value&0xf)
} }
@ -201,7 +224,7 @@ func (f *FloppyDriveController) SetTrackNo(value byte) {
//log.Tracef("FDC Track: %d", value) //log.Tracef("FDC Track: %d", value)
if value > TracksCount { if value > TracksCount {
f.status |= 0x10 /// RNF f.status |= 0x10 /// RNF
log.Error("Track not found!") log.Errorf("Track %d not found!", value)
} else { } else {
f.trackNo = value f.trackNo = value
} }
@ -282,7 +305,7 @@ func (f *FloppyDriveController) writeTrack() {
func (f *FloppyDriveController) Data() byte { func (f *FloppyDriveController) Data() byte {
switch f.lastCmd { switch f.lastCmd {
case CmdReadSector, CmdReadSectorMulti: case CmdReadSector, CmdReadSectorMulti, CmdReadAddress:
if len(f.trackBuffer) > 0 { if len(f.trackBuffer) > 0 {
f.drq = 1 f.drq = 1
f.data = f.trackBuffer[0] f.data = f.trackBuffer[0]

View File

@ -1,6 +1,8 @@
logFile: "okemu.log" logFile: "okemu.log"
logLevel: "info" logLevel: "info"
monitorFile: "rom/MON_r8_9c6c6546.bin" monitorFile: "rom/MON_r8_9c6c6546.bin"
# cpmFile: "rom/CPM_r7_b89a7e16.bin"
cpmFile: "rom/CPM_r8_bc0695e4.bin" cpmFile: "rom/CPM_r8_bc0695e4.bin"
floppyB: "floppy/floppyB.okd" floppyB: "floppy/floppyB.okd"
floppyC: "floppy/floppyC.okd" floppyC: "floppy/floppyC.okd"
port: 10001

Binary file not shown.

View File

@ -128,7 +128,7 @@ func carry(bitNo int, a uint16, b uint16, cy bool) bool {
return (carry & (1 << bitNo)) != 0 return (carry & (1 << bitNo)) != 0
} }
// returns the parity of byte: 0 if number of 1 bits in `val` is odd, else 1 // parity returns the parity of byte: 0 if odd, else 1
func parity(val byte) bool { func parity(val byte) bool {
ones := byte(0) ones := byte(0)
for i := 0; i < 8; i++ { for i := 0; i < 8; i++ {

View File

@ -9,7 +9,7 @@ func (z *Z80) jump(addr uint16) {
} }
// jumps to next word in memory if condition is true // jumps to next word in memory if condition is true
func (z *Z80) cond_jump(condition bool) { func (z *Z80) condJump(condition bool) {
addr := z.nextW() addr := z.nextW()
if condition { if condition {
z.jump(addr) z.jump(addr)
@ -25,7 +25,7 @@ func (z *Z80) call(addr uint16) {
} }
// calls to next word in memory if condition is true // calls to next word in memory if condition is true
func (z *Z80) cond_call(condition bool) { func (z *Z80) condCall(condition bool) {
addr := z.nextW() addr := z.nextW()
if condition { if condition {
z.call(addr) z.call(addr)
@ -41,7 +41,7 @@ func (z *Z80) ret() {
} }
// returns from subroutine if condition is true // returns from subroutine if condition is true
func (z *Z80) cond_ret(condition bool) { func (z *Z80) condRet(condition bool) {
if condition { if condition {
z.ret() z.ret()
z.cycleCount += 6 z.cycleCount += 6
@ -57,7 +57,7 @@ func (z *Z80) jr(offset byte) {
z.memPtr = z.pc z.memPtr = z.pc
} }
func (z *Z80) cond_jr(condition bool) { func (z *Z80) condJr(condition bool) {
b := z.nextB() b := z.nextB()
if condition { if condition {
z.jr(b) z.jr(b)
@ -73,7 +73,7 @@ func bToByte(cond bool) byte {
} }
// ADD Byte: adds two bytes together // ADD Byte: adds two bytes together
func (z *Z80) addb(a byte, b byte, cy bool) byte { func (z *Z80) addB(a byte, b byte, cy bool) byte {
result := a + b + bToByte(cy) result := a + b + bToByte(cy)
z.sf = result&0x80 != 0 z.sf = result&0x80 != 0
z.zf = result == 0 z.zf = result == 0
@ -86,8 +86,8 @@ func (z *Z80) addb(a byte, b byte, cy bool) byte {
} }
// SUBtract Byte: subtracts two bytes (with optional carry) // SUBtract Byte: subtracts two bytes (with optional carry)
func (z *Z80) subb(a byte, b byte, cy bool) byte { func (z *Z80) subB(a byte, b byte, cy bool) byte {
val := z.addb(a, ^b, !cy) val := z.addB(a, ^b, !cy)
z.cf = !z.cf z.cf = !z.cf
z.hf = !z.hf z.hf = !z.hf
z.nf = true z.nf = true
@ -95,9 +95,9 @@ func (z *Z80) subb(a byte, b byte, cy bool) byte {
} }
// ADD Word: adds two words together // ADD Word: adds two words together
func (z *Z80) addw(a uint16, b uint16, cy bool) uint16 { func (z *Z80) addW(a uint16, b uint16, cy bool) uint16 {
lsb := z.addb(byte(a), byte(b), cy) lsb := z.addB(byte(a), byte(b), cy)
msb := z.addb(byte(a>>8), byte(b>>8), z.cf) msb := z.addB(byte(a>>8), byte(b>>8), z.cf)
result := (uint16(msb) << 8) | uint16(lsb) result := (uint16(msb) << 8) | uint16(lsb)
z.zf = result == 0 z.zf = result == 0
z.memPtr = a + 1 z.memPtr = a + 1
@ -105,9 +105,9 @@ func (z *Z80) addw(a uint16, b uint16, cy bool) uint16 {
} }
// SUBtract Word: subtracts two words (with optional carry) // SUBtract Word: subtracts two words (with optional carry)
func (z *Z80) subw(a uint16, b uint16, cy bool) uint16 { func (z *Z80) subW(a uint16, b uint16, cy bool) uint16 {
lsb := z.subb(byte(a), byte(b), cy) lsb := z.subB(byte(a), byte(b), cy)
msb := z.subb(byte(a>>8), byte(b>>8), z.cf) msb := z.subB(byte(a>>8), byte(b>>8), z.cf)
result := (uint16(msb) << 8) | uint16(lsb) result := (uint16(msb) << 8) | uint16(lsb)
z.zf = result == 0 z.zf = result == 0
z.memPtr = a + 1 z.memPtr = a + 1
@ -115,11 +115,11 @@ func (z *Z80) subw(a uint16, b uint16, cy bool) uint16 {
} }
// Adds a word to HL // Adds a word to HL
func (z *Z80) addhl(val uint16) { func (z *Z80) addHL(val uint16) {
sf := z.sf sf := z.sf
zf := z.zf zf := z.zf
pf := z.pf pf := z.pf
result := z.addw(z.hl(), val, false) result := z.addW(z.hl(), val, false)
z.setHL(result) z.setHL(result)
z.sf = sf z.sf = sf
z.zf = zf z.zf = zf
@ -127,28 +127,28 @@ func (z *Z80) addhl(val uint16) {
} }
// adds a word to IX or IY // adds a word to IX or IY
func (z *Z80) addiz(reg *uint16, val uint16) { func (z *Z80) addIZ(reg *uint16, val uint16) {
sf := z.sf sf := z.sf
zf := z.zf zf := z.zf
pf := z.pf pf := z.pf
result := z.addw(*reg, val, false) result := z.addW(*reg, val, false)
*reg = result *reg = result
z.sf = sf z.sf = sf
z.zf = zf z.zf = zf
z.pf = pf z.pf = pf
} }
// adchl adds a word (+ carry) to HL // adcHL adds a word (+ carry) to HL
func (z *Z80) adchl(val uint16) { func (z *Z80) adcHL(val uint16) {
result := z.addw(z.hl(), val, z.cf) result := z.addW(z.hl(), val, z.cf)
z.sf = result&0x8000 != 0 z.sf = result&0x8000 != 0
z.zf = result == 0 z.zf = result == 0
z.setHL(result) z.setHL(result)
} }
// sbchl subtracts a word (+ carry) to HL // sbcHL subtracts a word (+ carry) to HL
func (z *Z80) sbchl(val uint16) { func (z *Z80) sbcHL(val uint16) {
result := z.subw(z.hl(), val, z.cf) result := z.subW(z.hl(), val, z.cf)
z.sf = result&0x8000 != 0 z.sf = result&0x8000 != 0
z.zf = result == 0 z.zf = result == 0
z.setHL(result) z.setHL(result)
@ -157,7 +157,7 @@ func (z *Z80) sbchl(val uint16) {
// increments a byte value // increments a byte value
func (z *Z80) inc(a byte) byte { func (z *Z80) inc(a byte) byte {
cf := z.cf cf := z.cf
result := z.addb(a, 1, false) result := z.addB(a, 1, false)
z.cf = cf z.cf = cf
return result return result
} }
@ -165,14 +165,14 @@ func (z *Z80) inc(a byte) byte {
// decrements a byte value // decrements a byte value
func (z *Z80) dec(a byte) byte { func (z *Z80) dec(a byte) byte {
cf := z.cf cf := z.cf
result := z.subb(a, 1, false) result := z.subB(a, 1, false)
z.cf = cf z.cf = cf
return result return result
} }
// executes a logic "and" between register A and a byte, then stores the // executes a logic "and" between register A and a byte, then stores the
// result in register A // result in register A
func (z *Z80) land(val byte) { func (z *Z80) lAnd(val byte) {
result := z.a & val result := z.a & val
z.sf = result&0x80 != 0 z.sf = result&0x80 != 0
z.zf = result == 0 z.zf = result == 0
@ -186,7 +186,7 @@ func (z *Z80) land(val byte) {
// executes a logic "xor" between register A and a byte, then stores the // executes a logic "xor" between register A and a byte, then stores the
// result in register A // result in register A
func (z *Z80) lxor(val byte) { func (z *Z80) lXor(val byte) {
result := z.a ^ val result := z.a ^ val
z.sf = result&0x80 != 0 z.sf = result&0x80 != 0
z.zf = result == 0 z.zf = result == 0
@ -200,7 +200,7 @@ func (z *Z80) lxor(val byte) {
// executes a logic "or" between register A and a byte, then stores the // executes a logic "or" between register A and a byte, then stores the
// result in register A // result in register A
func (z *Z80) lor(val byte) { func (z *Z80) lOr(val byte) {
result := z.a | val result := z.a | val
z.sf = result&0x80 != 0 z.sf = result&0x80 != 0
@ -215,7 +215,7 @@ func (z *Z80) lor(val byte) {
// compares a value with register A // compares a value with register A
func (z *Z80) cp(val byte) { func (z *Z80) cp(val byte) {
z.subb(z.a, val, false) z.subB(z.a, val, false)
// the only difference between cp and sub is that // the only difference between cp and sub is that
// the xf/yf are taken from the value to be subtracted, // the xf/yf are taken from the value to be subtracted,
@ -226,7 +226,7 @@ func (z *Z80) cp(val byte) {
// 0xCB opcodes // 0xCB opcodes
// rotate left with carry // rotate left with carry
func (z *Z80) cb_rlc(val byte) byte { func (z *Z80) cbRlc(val byte) byte {
old := val >> 7 old := val >> 7
val = (val << 1) | old val = (val << 1) | old
z.sf = val&0x80 != 0 z.sf = val&0x80 != 0
@ -240,7 +240,7 @@ func (z *Z80) cb_rlc(val byte) byte {
} }
// rotate right with carry // rotate right with carry
func (z *Z80) cb_rrc(val byte) byte { func (z *Z80) cbRrc(val byte) byte {
old := val & 1 old := val & 1
val = (val >> 1) | (old << 7) val = (val >> 1) | (old << 7)
z.sf = val&0x80 != 0 z.sf = val&0x80 != 0
@ -254,7 +254,7 @@ func (z *Z80) cb_rrc(val byte) byte {
} }
// rotate left (simple) // rotate left (simple)
func (z *Z80) cb_rl(val byte) byte { func (z *Z80) cbRl(val byte) byte {
cf := z.cf cf := z.cf
z.cf = val>>7 != 0 z.cf = val>>7 != 0
val = (val << 1) | bToByte(cf) val = (val << 1) | bToByte(cf)
@ -268,7 +268,7 @@ func (z *Z80) cb_rl(val byte) byte {
} }
// rotate right (simple) // rotate right (simple)
func (z *Z80) cb_rr(val byte) byte { func (z *Z80) cbRr(val byte) byte {
c := z.cf c := z.cf
z.cf = (val & 1) != 0 z.cf = (val & 1) != 0
val = (val >> 1) | (bToByte(c) << 7) val = (val >> 1) | (bToByte(c) << 7)
@ -282,7 +282,7 @@ func (z *Z80) cb_rr(val byte) byte {
} }
// shift left preserving sign // shift left preserving sign
func (z *Z80) cb_sla(val byte) byte { func (z *Z80) cbSla(val byte) byte {
z.cf = (val >> 7) != 0 z.cf = (val >> 7) != 0
val <<= 1 val <<= 1
z.sf = val&0x80 != 0 z.sf = val&0x80 != 0
@ -295,7 +295,7 @@ func (z *Z80) cb_sla(val byte) byte {
} }
// SLL (exactly like SLA, but sets the first bit to 1) // SLL (exactly like SLA, but sets the first bit to 1)
func (z *Z80) cb_sll(val byte) byte { func (z *Z80) cbSll(val byte) byte {
z.cf = val&0x80 != 0 z.cf = val&0x80 != 0
val <<= 1 val <<= 1
val |= 1 val |= 1
@ -309,7 +309,7 @@ func (z *Z80) cb_sll(val byte) byte {
} }
// shift right preserving sign // shift right preserving sign
func (z *Z80) cb_sra(val byte) byte { func (z *Z80) cbSra(val byte) byte {
z.cf = (val & 1) != 0 z.cf = (val & 1) != 0
val = (val >> 1) | (val & 0x80) // 0b10000000 val = (val >> 1) | (val & 0x80) // 0b10000000
z.sf = val&0x80 != 0 z.sf = val&0x80 != 0
@ -322,7 +322,7 @@ func (z *Z80) cb_sra(val byte) byte {
} }
// shift register right // shift register right
func (z *Z80) cb_srl(val byte) byte { func (z *Z80) cbSrl(val byte) byte {
z.cf = (val & 1) != 0 z.cf = (val & 1) != 0
val >>= 1 val >>= 1
z.sf = val&0x80 != 0 z.sf = val&0x80 != 0
@ -335,7 +335,7 @@ func (z *Z80) cb_srl(val byte) byte {
} }
// tests bit "n" from a byte // tests bit "n" from a byte
func (z *Z80) cb_bit(val byte, n byte) byte { func (z *Z80) cbBit(val byte, n byte) byte {
result := val & (1 << n) result := val & (1 << n)
z.sf = result&0x80 != 0 z.sf = result&0x80 != 0
z.zf = result == 0 z.zf = result == 0
@ -378,7 +378,7 @@ func (z *Z80) ldd() {
func (z *Z80) cpi() { func (z *Z80) cpi() {
cf := z.cf cf := z.cf
result := z.subb(z.a, z.rb(z.hl()), false) result := z.subB(z.a, z.rb(z.hl()), false)
z.setHL(z.hl() + 1) z.setHL(z.hl() + 1)
z.setBC(z.bc() - 1) z.setBC(z.bc() - 1)
@ -397,8 +397,6 @@ func (z *Z80) cpd() {
z.memPtr -= 2 z.memPtr -= 2
} }
//var halfCarrySubTable = []bool{false, false, true, false, true, false, true, true}
func (z *Z80) inRC(r *byte) { func (z *Z80) inRC(r *byte) {
*r = z.core.IORead(z.bc()) *r = z.core.IORead(z.bc())
z.zf = *r == 0 z.zf = *r == 0
@ -496,14 +494,6 @@ func (z *Z80) outd() {
} }
func (z *Z80) daa() { func (z *Z80) daa() {
// "When this instruction is executed, the A register is BCD corrected
// using the contents of the flags. The exact process is the following:
// if the least significant four bits of A contain a non-BCD digit
// (i. e. it is greater than 9) or the H flag is set, then $06 is
// added to the register. Then the four most significant bits are
// checked. If this more significant digit also happens to be greater
// than 9 or the C flag is set, then $60 is added."
// > http://z80-heaven.wikidot.com/instructions-set:daa
correction := byte(0) correction := byte(0)
if (z.a&0x0F) > 0x09 || z.hf { if (z.a&0x0F) > 0x09 || z.hf {
@ -607,7 +597,7 @@ func (z *Z80) execOpcode(opcode byte) {
switch opcode { switch opcode {
case 0x7F: case 0x7F:
z.a = z.a // ld a,a //z.a = z.a // ld a,a
case 0x78: case 0x78:
z.a = z.b // ld a,b z.a = z.b // ld a,b
case 0x79: case 0x79:
@ -624,7 +614,7 @@ func (z *Z80) execOpcode(opcode byte) {
case 0x47: case 0x47:
z.b = z.a // ld b,a z.b = z.a // ld b,a
case 0x40: case 0x40:
z.b = z.b // ld b,b //z.b = z.b // ld b,b
case 0x41: case 0x41:
z.b = z.c // ld b,c z.b = z.c // ld b,c
case 0x42: case 0x42:
@ -641,7 +631,7 @@ func (z *Z80) execOpcode(opcode byte) {
case 0x48: case 0x48:
z.c = z.b // ld c,b z.c = z.b // ld c,b
case 0x49: case 0x49:
z.c = z.c // ld c,c //z.c = z.c // ld c,c
case 0x4A: case 0x4A:
z.c = z.d // ld c,d z.c = z.d // ld c,d
case 0x4B: case 0x4B:
@ -658,7 +648,7 @@ func (z *Z80) execOpcode(opcode byte) {
case 0x51: case 0x51:
z.d = z.c // ld d,c z.d = z.c // ld d,c
case 0x52: case 0x52:
z.d = z.d // ld d,d //z.d = z.d // ld d,d
case 0x53: case 0x53:
z.d = z.e // ld d,e z.d = z.e // ld d,e
case 0x54: case 0x54:
@ -675,7 +665,7 @@ func (z *Z80) execOpcode(opcode byte) {
case 0x5A: case 0x5A:
z.e = z.d // ld e,d z.e = z.d // ld e,d
case 0x5B: case 0x5B:
z.e = z.e // ld e,e //z.e = z.e // ld e,e
case 0x5C: case 0x5C:
z.e = z.h // ld e,h z.e = z.h // ld e,h
case 0x5D: case 0x5D:
@ -692,7 +682,7 @@ func (z *Z80) execOpcode(opcode byte) {
case 0x63: case 0x63:
z.h = z.e // ld h,e z.h = z.e // ld h,e
case 0x64: case 0x64:
z.h = z.h // ld h,h //z.h = z.h // ld h,h
case 0x65: case 0x65:
z.h = z.l // ld h,l z.h = z.l // ld h,l
@ -709,7 +699,7 @@ func (z *Z80) execOpcode(opcode byte) {
case 0x6C: case 0x6C:
z.l = z.h // ld l,h z.l = z.h // ld l,h
case 0x6D: case 0x6D:
z.l = z.l // ld l,l //z.l = z.l // ld l,l
case 0x7E: case 0x7E:
z.a = z.rb(z.hl()) // ld a,(hl) z.a = z.rb(z.hl()) // ld a,(hl)
@ -817,89 +807,89 @@ func (z *Z80) execOpcode(opcode byte) {
z.setHL(val) z.setHL(val)
z.memPtr = val z.memPtr = val
case 0x87: case 0x87:
z.a = z.addb(z.a, z.a, false) // add a,a z.a = z.addB(z.a, z.a, false) // add a,a
case 0x80: case 0x80:
z.a = z.addb(z.a, z.b, false) // add a,b z.a = z.addB(z.a, z.b, false) // add a,b
case 0x81: case 0x81:
z.a = z.addb(z.a, z.c, false) // add a,c z.a = z.addB(z.a, z.c, false) // add a,c
case 0x82: case 0x82:
z.a = z.addb(z.a, z.d, false) // add a,d z.a = z.addB(z.a, z.d, false) // add a,d
case 0x83: case 0x83:
z.a = z.addb(z.a, z.e, false) // add a,e z.a = z.addB(z.a, z.e, false) // add a,e
case 0x84: case 0x84:
z.a = z.addb(z.a, z.h, false) // add a,h z.a = z.addB(z.a, z.h, false) // add a,h
case 0x85: case 0x85:
z.a = z.addb(z.a, z.l, false) // add a,l z.a = z.addB(z.a, z.l, false) // add a,l
case 0x86: case 0x86:
z.a = z.addb(z.a, z.rb(z.hl()), false) // add a,(hl) z.a = z.addB(z.a, z.rb(z.hl()), false) // add a,(hl)
case 0xC6: case 0xC6:
z.a = z.addb(z.a, z.nextB(), false) // add a,* z.a = z.addB(z.a, z.nextB(), false) // add a,*
case 0x8F: case 0x8F:
z.a = z.addb(z.a, z.a, z.cf) // adc a,a z.a = z.addB(z.a, z.a, z.cf) // adc a,a
case 0x88: case 0x88:
z.a = z.addb(z.a, z.b, z.cf) // adc a,b z.a = z.addB(z.a, z.b, z.cf) // adc a,b
case 0x89: case 0x89:
z.a = z.addb(z.a, z.c, z.cf) // adc a,c z.a = z.addB(z.a, z.c, z.cf) // adc a,c
case 0x8A: case 0x8A:
z.a = z.addb(z.a, z.d, z.cf) // adc a,d z.a = z.addB(z.a, z.d, z.cf) // adc a,d
case 0x8B: case 0x8B:
z.a = z.addb(z.a, z.e, z.cf) // adc a,e z.a = z.addB(z.a, z.e, z.cf) // adc a,e
case 0x8C: case 0x8C:
z.a = z.addb(z.a, z.h, z.cf) // adc a,h z.a = z.addB(z.a, z.h, z.cf) // adc a,h
case 0x8D: case 0x8D:
z.a = z.addb(z.a, z.l, z.cf) // adc a,l z.a = z.addB(z.a, z.l, z.cf) // adc a,l
case 0x8E: case 0x8E:
z.a = z.addb(z.a, z.rb(z.hl()), z.cf) // adc a,(hl) z.a = z.addB(z.a, z.rb(z.hl()), z.cf) // adc a,(hl)
case 0xCE: case 0xCE:
z.a = z.addb(z.a, z.nextB(), z.cf) // adc a,* z.a = z.addB(z.a, z.nextB(), z.cf) // adc a,*
case 0x97: case 0x97:
z.a = z.subb(z.a, z.a, false) // sub a,a z.a = z.subB(z.a, z.a, false) // sub a,a
case 0x90: case 0x90:
z.a = z.subb(z.a, z.b, false) // sub a,b z.a = z.subB(z.a, z.b, false) // sub a,b
case 0x91: case 0x91:
z.a = z.subb(z.a, z.c, false) // sub a,c z.a = z.subB(z.a, z.c, false) // sub a,c
case 0x92: case 0x92:
z.a = z.subb(z.a, z.d, false) // sub a,d z.a = z.subB(z.a, z.d, false) // sub a,d
case 0x93: case 0x93:
z.a = z.subb(z.a, z.e, false) // sub a,e z.a = z.subB(z.a, z.e, false) // sub a,e
case 0x94: case 0x94:
z.a = z.subb(z.a, z.h, false) // sub a,h z.a = z.subB(z.a, z.h, false) // sub a,h
case 0x95: case 0x95:
z.a = z.subb(z.a, z.l, false) // sub a,l z.a = z.subB(z.a, z.l, false) // sub a,l
case 0x96: case 0x96:
z.a = z.subb(z.a, z.rb(z.hl()), false) // sub a,(hl) z.a = z.subB(z.a, z.rb(z.hl()), false) // sub a,(hl)
case 0xD6: case 0xD6:
z.a = z.subb(z.a, z.nextB(), false) // sub a,* z.a = z.subB(z.a, z.nextB(), false) // sub a,*
case 0x9F: case 0x9F:
z.a = z.subb(z.a, z.a, z.cf) // sbc a,a z.a = z.subB(z.a, z.a, z.cf) // sbc a,a
case 0x98: case 0x98:
z.a = z.subb(z.a, z.b, z.cf) // sbc a,b z.a = z.subB(z.a, z.b, z.cf) // sbc a,b
case 0x99: case 0x99:
z.a = z.subb(z.a, z.c, z.cf) // sbc a,c z.a = z.subB(z.a, z.c, z.cf) // sbc a,c
case 0x9A: case 0x9A:
z.a = z.subb(z.a, z.d, z.cf) // sbc a,d z.a = z.subB(z.a, z.d, z.cf) // sbc a,d
case 0x9B: case 0x9B:
z.a = z.subb(z.a, z.e, z.cf) // sbc a,e z.a = z.subB(z.a, z.e, z.cf) // sbc a,e
case 0x9C: case 0x9C:
z.a = z.subb(z.a, z.h, z.cf) // sbc a,h z.a = z.subB(z.a, z.h, z.cf) // sbc a,h
case 0x9D: case 0x9D:
z.a = z.subb(z.a, z.l, z.cf) // sbc a,l z.a = z.subB(z.a, z.l, z.cf) // sbc a,l
case 0x9E: case 0x9E:
z.a = z.subb(z.a, z.rb(z.hl()), z.cf) // sbc a,(hl) z.a = z.subB(z.a, z.rb(z.hl()), z.cf) // sbc a,(hl)
case 0xDE: case 0xDE:
z.a = z.subb(z.a, z.nextB(), z.cf) // sbc a,* z.a = z.subB(z.a, z.nextB(), z.cf) // sbc a,*
case 0x09: case 0x09:
z.addhl(z.bc()) // add hl,bc z.addHL(z.bc()) // add hl,bc
case 0x19: case 0x19:
z.addhl(z.de()) // add hl,de z.addHL(z.de()) // add hl,de
case 0x29: case 0x29:
z.addhl(z.hl()) // add hl,hl z.addHL(z.hl()) // add hl,hl
case 0x39: case 0x39:
z.addhl(z.sp) // add hl,sp z.addHL(z.sp) // add hl,sp
case 0xF3: case 0xF3:
z.iff1 = false z.iff1 = false
@ -1013,61 +1003,61 @@ func (z *Z80) execOpcode(opcode byte) {
z.hf = false z.hf = false
z.updateXY(z.a) z.updateXY(z.a)
case 0xA7: case 0xA7:
z.land(z.a) // and a z.lAnd(z.a) // and a
case 0xA0: case 0xA0:
z.land(z.b) // and b z.lAnd(z.b) // and b
case 0xA1: case 0xA1:
z.land(z.c) // and c z.lAnd(z.c) // and c
case 0xA2: case 0xA2:
z.land(z.d) // and d z.lAnd(z.d) // and d
case 0xA3: case 0xA3:
z.land(z.e) // and e z.lAnd(z.e) // and e
case 0xA4: case 0xA4:
z.land(z.h) // and h z.lAnd(z.h) // and h
case 0xA5: case 0xA5:
z.land(z.l) // and l z.lAnd(z.l) // and l
case 0xA6: case 0xA6:
z.land(z.rb(z.hl())) // and (hl) z.lAnd(z.rb(z.hl())) // and (hl)
case 0xE6: case 0xE6:
z.land(z.nextB()) // and * z.lAnd(z.nextB()) // and *
case 0xAF: case 0xAF:
z.lxor(z.a) // xor a z.lXor(z.a) // xor a
case 0xA8: case 0xA8:
z.lxor(z.b) // xor b z.lXor(z.b) // xor b
case 0xA9: case 0xA9:
z.lxor(z.c) // xor c z.lXor(z.c) // xor c
case 0xAA: case 0xAA:
z.lxor(z.d) // xor d z.lXor(z.d) // xor d
case 0xAB: case 0xAB:
z.lxor(z.e) // xor e z.lXor(z.e) // xor e
case 0xAC: case 0xAC:
z.lxor(z.h) // xor h z.lXor(z.h) // xor h
case 0xAD: case 0xAD:
z.lxor(z.l) // xor l z.lXor(z.l) // xor l
case 0xAE: case 0xAE:
z.lxor(z.rb(z.hl())) // xor (hl) z.lXor(z.rb(z.hl())) // xor (hl)
case 0xEE: case 0xEE:
z.lxor(z.nextB()) // xor * z.lXor(z.nextB()) // xor *
case 0xB7: case 0xB7:
z.lor(z.a) // or a z.lOr(z.a) // or a
case 0xB0: case 0xB0:
z.lor(z.b) // or b z.lOr(z.b) // or b
case 0xB1: case 0xB1:
z.lor(z.c) // or c z.lOr(z.c) // or c
case 0xB2: case 0xB2:
z.lor(z.d) // or d z.lOr(z.d) // or d
case 0xB3: case 0xB3:
z.lor(z.e) // or e z.lOr(z.e) // or e
case 0xB4: case 0xB4:
z.lor(z.h) // or h z.lOr(z.h) // or h
case 0xB5: case 0xB5:
z.lor(z.l) // or l z.lOr(z.l) // or l
case 0xB6: case 0xB6:
z.lor(z.rb(z.hl())) // or (hl) z.lOr(z.rb(z.hl())) // or (hl)
case 0xF6: case 0xF6:
z.lor(z.nextB()) // or * z.lOr(z.nextB()) // or *
case 0xBF: case 0xBF:
z.cp(z.a) // cp a z.cp(z.a) // cp a
@ -1091,36 +1081,36 @@ func (z *Z80) execOpcode(opcode byte) {
case 0xC3: case 0xC3:
z.jump(z.nextW()) // jm ** z.jump(z.nextW()) // jm **
case 0xC2: case 0xC2:
z.cond_jump(!z.zf) // jp nz, ** z.condJump(!z.zf) // jp nz, **
case 0xCA: case 0xCA:
z.cond_jump(z.zf) // jp z, ** z.condJump(z.zf) // jp z, **
case 0xD2: case 0xD2:
z.cond_jump(!z.cf) // jp nc, ** z.condJump(!z.cf) // jp nc, **
case 0xDA: case 0xDA:
z.cond_jump(z.cf) // jp c, ** z.condJump(z.cf) // jp c, **
case 0xE2: case 0xE2:
z.cond_jump(!z.pf) // jp po, ** z.condJump(!z.pf) // jp po, **
case 0xEA: case 0xEA:
z.cond_jump(z.pf) // jp pe, ** z.condJump(z.pf) // jp pe, **
case 0xF2: case 0xF2:
z.cond_jump(!z.sf) // jp p, ** z.condJump(!z.sf) // jp p, **
case 0xFA: case 0xFA:
z.cond_jump(z.sf) // jp m, ** z.condJump(z.sf) // jp m, **
case 0x10: case 0x10:
z.b-- z.b--
z.cond_jr(z.b != 0) // djnz * z.condJr(z.b != 0) // djnz *
case 0x18: case 0x18:
z.pc += uint16(z.nextB()) // jr * z.pc += uint16(z.nextB()) // jr *
z.memPtr = z.pc z.memPtr = z.pc
case 0x20: case 0x20:
z.cond_jr(!z.zf) // jr nz, * z.condJr(!z.zf) // jr nz, *
case 0x28: case 0x28:
z.cond_jr(z.zf) // jr z, * z.condJr(z.zf) // jr z, *
case 0x30: case 0x30:
z.cond_jr(!z.cf) // jr nc, * z.condJr(!z.cf) // jr nc, *
case 0x38: case 0x38:
z.cond_jr(z.cf) // jr c, * z.condJr(z.cf) // jr c, *
case 0xE9: case 0xE9:
z.pc = z.hl() // jp (hl) z.pc = z.hl() // jp (hl)
@ -1128,40 +1118,40 @@ func (z *Z80) execOpcode(opcode byte) {
z.call(z.nextW()) // call z.call(z.nextW()) // call
case 0xC4: case 0xC4:
z.cond_call(!z.zf) // cnz z.condCall(!z.zf) // cnz
case 0xCC: case 0xCC:
z.cond_call(z.zf) // cz z.condCall(z.zf) // cz
case 0xD4: case 0xD4:
z.cond_call(!z.cf) // cnc z.condCall(!z.cf) // cnc
case 0xDC: case 0xDC:
z.cond_call(z.cf) // cc z.condCall(z.cf) // cc
case 0xE4: case 0xE4:
z.cond_call(!z.pf) // cpo z.condCall(!z.pf) // cpo
case 0xEC: case 0xEC:
z.cond_call(z.pf) // cpe z.condCall(z.pf) // cpe
case 0xF4: case 0xF4:
z.cond_call(!z.sf) // cp z.condCall(!z.sf) // cp
case 0xFC: case 0xFC:
z.cond_call(z.sf) // cm z.condCall(z.sf) // cm
case 0xC9: case 0xC9:
z.ret() // ret z.ret() // ret
case 0xC0: case 0xC0:
z.cond_ret(!z.zf) // ret nz z.condRet(!z.zf) // ret nz
case 0xC8: case 0xC8:
z.cond_ret(z.zf) // ret z z.condRet(z.zf) // ret z
case 0xD0: case 0xD0:
z.cond_ret(!z.cf) // ret nc z.condRet(!z.cf) // ret nc
case 0xD8: case 0xD8:
z.cond_ret(z.cf) // ret c z.condRet(z.cf) // ret c
case 0xE0: case 0xE0:
z.cond_ret(!z.pf) // ret po z.condRet(!z.pf) // ret po
case 0xE8: case 0xE8:
z.cond_ret(z.pf) // ret pe z.condRet(z.pf) // ret pe
case 0xF0: case 0xF0:
z.cond_ret(!z.sf) // ret p z.condRet(!z.sf) // ret p
case 0xF8: case 0xF8:
z.cond_ret(z.sf) // ret m z.condRet(z.sf) // ret m
case 0xC7: case 0xC7:
z.call(0x00) // rst 0 z.call(0x00) // rst 0

View File

@ -40,26 +40,26 @@ func (z *Z80) execOpcodeCB(opcode byte) {
// rot[y] r[z] // rot[y] r[z]
switch y_ { switch y_ {
case 0: case 0:
*reg = z.cb_rlc(*reg) *reg = z.cbRlc(*reg)
case 1: case 1:
*reg = z.cb_rrc(*reg) *reg = z.cbRrc(*reg)
case 2: case 2:
*reg = z.cb_rl(*reg) *reg = z.cbRl(*reg)
case 3: case 3:
*reg = z.cb_rr(*reg) *reg = z.cbRr(*reg)
case 4: case 4:
*reg = z.cb_sla(*reg) *reg = z.cbSla(*reg)
case 5: case 5:
*reg = z.cb_sra(*reg) *reg = z.cbSra(*reg)
case 6: case 6:
*reg = z.cb_sll(*reg) *reg = z.cbSll(*reg)
case 7: case 7:
*reg = z.cb_srl(*reg) *reg = z.cbSrl(*reg)
} }
case 1: case 1:
// BIT y, r[z] // BIT y, r[z]
z.cb_bit(*reg, y_) z.cbBit(*reg, y_)
// in bit (hl), x/y flags are handled differently: // in bit (hl), x/y flags are handled differently:
if z_ == 6 { if z_ == 6 {
@ -82,8 +82,8 @@ func (z *Z80) execOpcodeCB(opcode byte) {
} }
} }
// exec_opcode_dcb executes a displaced CB opcode (DDCB or FDCB) // execOpcodeDcb executes a displaced CB opcode (DDCB or FDCB)
func (z *Z80) exec_opcode_dcb(opcode byte, addr uint16) { func (z *Z80) execOpcodeDcb(opcode byte, addr uint16) {
val := z.rb(addr) val := z.rb(addr)
result := byte(0) result := byte(0)
@ -97,26 +97,26 @@ func (z *Z80) exec_opcode_dcb(opcode byte, addr uint16) {
// rot[y] (iz+d) // rot[y] (iz+d)
switch y_ { switch y_ {
case 0: case 0:
result = z.cb_rlc(val) result = z.cbRlc(val)
case 1: case 1:
result = z.cb_rrc(val) result = z.cbRrc(val)
case 2: case 2:
result = z.cb_rl(val) result = z.cbRl(val)
case 3: case 3:
result = z.cb_rr(val) result = z.cbRr(val)
case 4: case 4:
result = z.cb_sla(val) result = z.cbSla(val)
case 5: case 5:
result = z.cb_sra(val) result = z.cbSra(val)
case 6: case 6:
result = z.cb_sll(val) result = z.cbSll(val)
case 7: case 7:
result = z.cb_srl(val) result = z.cbSrl(val)
} }
case 1: case 1:
// bit y,(iz+d) // bit y,(iz+d)
result = z.cb_bit(val, y_) result = z.cbBit(val, y_)
z.updateXY(byte(addr >> 8)) z.updateXY(byte(addr >> 8))
case 2: case 2:
result = val & ^(1 << y_) // res y, (iz+d) result = val & ^(1 << y_) // res y, (iz+d)

View File

@ -15,58 +15,58 @@ func (z *Z80) execOpcodeDDFD(opcode byte, iz *uint16) {
z.pc = *iz z.pc = *iz
//z.jump(*iz) //z.jump(*iz)
case 0x09: case 0x09:
z.addiz(iz, z.bc()) // add iz,bc z.addIZ(iz, z.bc()) // add iz,bc
case 0x19: case 0x19:
z.addiz(iz, z.de()) // add iz,de z.addIZ(iz, z.de()) // add iz,de
case 0x29: case 0x29:
z.addiz(iz, *iz) // add iz,iz z.addIZ(iz, *iz) // add iz,iz
case 0x39: case 0x39:
z.addiz(iz, z.sp) // add iz,sp z.addIZ(iz, z.sp) // add iz,sp
case 0x84: case 0x84:
z.a = z.addb(z.a, byte(*iz>>8), false) // add a,izh z.a = z.addB(z.a, byte(*iz>>8), false) // add a,izh
case 0x85: case 0x85:
z.a = z.addb(z.a, byte(*iz), false) // add a,izl z.a = z.addB(z.a, byte(*iz), false) // add a,izl
case 0x8C: case 0x8C:
z.a = z.addb(z.a, byte(*iz>>8), z.cf) // adc a,izh z.a = z.addB(z.a, byte(*iz>>8), z.cf) // adc a,izh
case 0x8D: case 0x8D:
z.a = z.addb(z.a, byte(*iz), z.cf) // adc a,izl z.a = z.addB(z.a, byte(*iz), z.cf) // adc a,izl
case 0x86: case 0x86:
z.a = z.addb(z.a, z.rb(z.displace(*iz, z.nextB())), false) // add a,(iz+*) z.a = z.addB(z.a, z.rb(z.displace(*iz, z.nextB())), false) // add a,(iz+*)
case 0x8E: case 0x8E:
z.a = z.addb(z.a, z.rb(z.displace(*iz, z.nextB())), z.cf) // adc a,(iz+*) z.a = z.addB(z.a, z.rb(z.displace(*iz, z.nextB())), z.cf) // adc a,(iz+*)
case 0x96: case 0x96:
z.a = z.subb(z.a, z.rb(z.displace(*iz, z.nextB())), false) // sub (iz+*) z.a = z.subB(z.a, z.rb(z.displace(*iz, z.nextB())), false) // sub (iz+*)
case 0x9E: case 0x9E:
z.a = z.subb(z.a, z.rb(z.displace(*iz, z.nextB())), z.cf) // sbc (iz+*) z.a = z.subB(z.a, z.rb(z.displace(*iz, z.nextB())), z.cf) // sbc (iz+*)
case 0x94: case 0x94:
z.a = z.subb(z.a, byte(*iz>>8), false) // sub izh z.a = z.subB(z.a, byte(*iz>>8), false) // sub izh
case 0x95: case 0x95:
z.a = z.subb(z.a, byte(*iz), false) // sub izl z.a = z.subB(z.a, byte(*iz), false) // sub izl
case 0x9C: case 0x9C:
z.a = z.subb(z.a, byte(*iz>>8), z.cf) // sbc izh z.a = z.subB(z.a, byte(*iz>>8), z.cf) // sbc izh
case 0x9D: case 0x9D:
z.a = z.subb(z.a, byte(*iz), z.cf) // sbc izl z.a = z.subB(z.a, byte(*iz), z.cf) // sbc izl
case 0xA6: case 0xA6:
z.land(z.rb(z.displace(*iz, z.nextB()))) // and (iz+*) z.lAnd(z.rb(z.displace(*iz, z.nextB()))) // and (iz+*)
case 0xA4: case 0xA4:
z.land(byte(*iz >> 8)) // and izh z.lAnd(byte(*iz >> 8)) // and izh
case 0xA5: case 0xA5:
z.land(byte(*iz)) // and izl z.lAnd(byte(*iz)) // and izl
case 0xAE: case 0xAE:
z.lxor(z.rb(z.displace(*iz, z.nextB()))) // xor (iz+*) z.lXor(z.rb(z.displace(*iz, z.nextB()))) // xor (iz+*)
case 0xAC: case 0xAC:
z.lxor(byte(*iz >> 8)) // xor izh z.lXor(byte(*iz >> 8)) // xor izh
case 0xAD: case 0xAD:
z.lxor(byte(*iz)) // xor izl z.lXor(byte(*iz)) // xor izl
case 0xB6: case 0xB6:
z.lor(z.rb(z.displace(*iz, z.nextB()))) // or (iz+*) z.lOr(z.rb(z.displace(*iz, z.nextB()))) // or (iz+*)
case 0xB4: case 0xB4:
z.lor(byte(*iz >> 8)) // or izh z.lOr(byte(*iz >> 8)) // or izh
case 0xB5: case 0xB5:
z.lor(byte(*iz)) // or izl z.lOr(byte(*iz)) // or izl
case 0xBE: case 0xBE:
z.cp(z.rb(z.displace(*iz, z.nextB()))) // cp (iz+*) z.cp(z.rb(z.displace(*iz, z.nextB()))) // cp (iz+*)
case 0xBC: case 0xBC:
@ -196,7 +196,7 @@ func (z *Z80) execOpcodeDDFD(opcode byte, iz *uint16) {
case 0xCB: case 0xCB:
addr := z.displace(*iz, z.nextB()) addr := z.displace(*iz, z.nextB())
op := z.nextB() op := z.nextB()
z.exec_opcode_dcb(op, addr) z.execOpcodeDcb(op, addr)
default: default:
// any other FD/DD opcode behaves as a non-prefixed opcode: // any other FD/DD opcode behaves as a non-prefixed opcode:
z.execOpcode(opcode) z.execOpcode(opcode)

View File

@ -185,21 +185,21 @@ func (z *Z80) execOpcodeED(opcode byte) {
} }
case 0x42: case 0x42:
z.sbchl(z.bc()) // sbc hl,bc z.sbcHL(z.bc()) // sbc hl,bc
case 0x52: case 0x52:
z.sbchl(z.de()) // sbc hl,de z.sbcHL(z.de()) // sbc hl,de
case 0x62: case 0x62:
z.sbchl(z.hl()) // sbc hl,hl z.sbcHL(z.hl()) // sbc hl,hl
case 0x72: case 0x72:
z.sbchl(z.sp) // sbc hl,sp z.sbcHL(z.sp) // sbc hl,sp
case 0x4A: case 0x4A:
z.adchl(z.bc()) // adc hl,bc z.adcHL(z.bc()) // adc hl,bc
case 0x5A: case 0x5A:
z.adchl(z.de()) // adc hl,de z.adcHL(z.de()) // adc hl,de
case 0x6A: case 0x6A:
z.adchl(z.hl()) // adc hl,hl z.adcHL(z.hl()) // adc hl,hl
case 0x7A: case 0x7A:
z.adchl(z.sp) // adc hl,sp z.adcHL(z.sp) // adc hl,sp
case 0x43: case 0x43:
// ld (**), bc // ld (**), bc
addr := z.nextW() addr := z.nextW()
@ -241,7 +241,7 @@ func (z *Z80) execOpcodeED(opcode byte) {
z.sp = z.rw(addr) z.sp = z.rw(addr)
z.memPtr = addr + 1 z.memPtr = addr + 1
case 0x44, 0x54, 0x64, 0x74, 0x4C, 0x5C, 0x6C, 0x7C: case 0x44, 0x54, 0x64, 0x74, 0x4C, 0x5C, 0x6C, 0x7C:
z.a = z.subb(0, z.a, false) // neg z.a = z.subB(0, z.a, false) // neg
case 0x46, 0x4e, 0x66, 0x6e: case 0x46, 0x4e, 0x66, 0x6e:
z.interruptMode = 0 // im 0 z.interruptMode = 0 // im 0
case 0x56, 0x76: case 0x56, 0x76:

View File

@ -103,6 +103,46 @@ func (f *FlagsType) GetFlags() byte {
return flags 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 *Z80CPU) IIFStr() string {
flags := []byte{'-', '-'}
if z.Iff1 {
flags[0] = '1'
}
if z.Iff2 {
flags[1] = '2'
}
return string(flags)
}
func GetFlags(f byte) FlagsType { func GetFlags(f byte) FlagsType {
return FlagsType{ return FlagsType{
S: f&0x80 != 0, S: f&0x80 != 0,

View File

@ -68,10 +68,6 @@ type Z80TestIn struct {
memorySetup []MemorySetup memorySetup []MemorySetup
} }
type Z80TestExpected struct {
expect Expect
}
type Expect struct { type Expect struct {
events []Event events []Event
registers Registers registers Registers