mirror of
https://github.com/romychs/Ocean-240.2-Emulator.git
synced 2026-04-21 11:03:21 +03:00
Support extended-stack, code coverage, PEEKW fn in expr
This commit is contained in:
parent
dd39115296
commit
603ebac8a6
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"okemu/gval"
|
"okemu/gval"
|
||||||
|
//"okemu/okean240"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -206,24 +207,25 @@ func getUint16(name string, ctx map[string]interface{}) uint16 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Breakpoint) Hit(ctx map[string]interface{}) bool {
|
func (b *Breakpoint) Hit(parameters map[string]interface{}) bool {
|
||||||
if !b.enabled {
|
if !b.enabled {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if b.bpType == BPTypeSimplePC {
|
if b.bpType == BPTypeSimplePC {
|
||||||
pc := getUint16("PC", ctx)
|
pc := getUint16("PC", parameters)
|
||||||
if pc == b.addr {
|
if pc == b.addr {
|
||||||
log.Debugf("Breakpoint Hit PC=0x%04X", b.addr)
|
log.Debugf("Breakpoint Hit PC=0x%04X", b.addr)
|
||||||
}
|
}
|
||||||
return pc == b.addr
|
return pc == b.addr
|
||||||
} else if b.bpType == BPTypeSimpleSP {
|
} else if b.bpType == BPTypeSimpleSP {
|
||||||
sp := getUint16("SP", ctx)
|
sp := getUint16("SP", parameters)
|
||||||
if sp >= b.addr {
|
if sp >= b.addr {
|
||||||
log.Debugf("Breakpoint Hit SP>=0x%04X", b.addr)
|
log.Debugf("Breakpoint Hit SP>=0x%04X", b.addr)
|
||||||
}
|
}
|
||||||
return sp >= b.addr
|
return sp >= b.addr
|
||||||
}
|
}
|
||||||
value, err := b.eval.EvalBool(context.Background(), ctx)
|
bc := context.WithValue(context.Background(), "MEM", parameters["MEM"])
|
||||||
|
value, err := b.eval.EvalBool(bc, parameters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
@ -233,7 +235,9 @@ func (b *Breakpoint) Hit(ctx map[string]interface{}) bool {
|
|||||||
var language gval.Language
|
var language gval.Language
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
language = gval.NewLanguage(gval.Base(), gval.Arithmetic(), gval.Bitmask(), gval.PropositionalLogic())
|
language = gval.NewLanguage(gval.Base(), gval.Arithmetic(), gval.Bitmask(), gval.PropositionalLogic(),
|
||||||
|
gval.Function("PEEKW", cfPeekW),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Breakpoint) SetExpression(expression string) error {
|
func (b *Breakpoint) SetExpression(expression string) error {
|
||||||
@ -254,3 +258,21 @@ func (b *Breakpoint) Expression() string {
|
|||||||
func (b *Breakpoint) MBank() uint8 {
|
func (b *Breakpoint) MBank() uint8 {
|
||||||
return b.mBank
|
return b.mBank
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cfPeekW(ctx context.Context, arguments ...interface{}) (interface{}, error) {
|
||||||
|
if len(arguments) != 1 {
|
||||||
|
return nil, fmt.Errorf("expected 1 argument")
|
||||||
|
}
|
||||||
|
addr, ok := arguments[0].(uint)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("argument must be a uint")
|
||||||
|
}
|
||||||
|
memInt := ctx.Value("MEM")
|
||||||
|
memRead, ok := memInt.(func(uint16) uint8)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("MEM must be func(uint16) uint8")
|
||||||
|
}
|
||||||
|
lo := memRead(uint16(addr))
|
||||||
|
hi := memRead(uint16(addr + 1))
|
||||||
|
return (uint16(hi) << 8) | uint16(lo), nil
|
||||||
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ var ctx = map[string]interface{}{
|
|||||||
|
|
||||||
const exprRep = "PC=00115h and (B=5 or BC = 5)"
|
const exprRep = "PC=00115h and (B=5 or BC = 5)"
|
||||||
const exprDst = "PC==0x00115 && (B==5 || BC == 5)"
|
const exprDst = "PC==0x00115 && (B==5 || BC == 5)"
|
||||||
|
const exprFn = "PC=PEEKW(SP-2) AND SP>=100"
|
||||||
|
|
||||||
func Test_PatchExpression(t *testing.T) {
|
func Test_PatchExpression(t *testing.T) {
|
||||||
ex := patchExpression(exprRep)
|
ex := patchExpression(exprRep)
|
||||||
@ -29,7 +30,7 @@ func Test_PatchExpression(t *testing.T) {
|
|||||||
|
|
||||||
func Test_ComplexExpr(t *testing.T) {
|
func Test_ComplexExpr(t *testing.T) {
|
||||||
|
|
||||||
b, e := NewBreakpoint(expr1)
|
b, e := NewBreakpoint(expr1, 1)
|
||||||
//e := b.SetExpression(exp1)
|
//e := b.SetExpression(exp1)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
t.Error(e)
|
t.Error(e)
|
||||||
@ -45,7 +46,7 @@ func Test_ComplexExpr(t *testing.T) {
|
|||||||
const expSimplePC = "PC=00119h"
|
const expSimplePC = "PC=00119h"
|
||||||
|
|
||||||
func Test_BPSetPC(t *testing.T) {
|
func Test_BPSetPC(t *testing.T) {
|
||||||
b, e := NewBreakpoint(expSimplePC)
|
b, e := NewBreakpoint(expSimplePC, 1)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
t.Error(e)
|
t.Error(e)
|
||||||
} else if b != nil {
|
} else if b != nil {
|
||||||
@ -75,3 +76,12 @@ func Test_GetCtx(t *testing.T) {
|
|||||||
t.Errorf("PC value not found in context")
|
t.Errorf("PC value not found in context")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_PeekWFn(t *testing.T) {
|
||||||
|
b, e := NewBreakpoint(exprFn, 1)
|
||||||
|
if e != nil {
|
||||||
|
t.Error(e)
|
||||||
|
}
|
||||||
|
b.enabled = true
|
||||||
|
b.Hit(ctx)
|
||||||
|
}
|
||||||
|
|||||||
@ -12,3 +12,12 @@ const getMachineResponse = "64K RAM, no ZX\n"
|
|||||||
const respErrorLoading = "ERROR loading file"
|
const respErrorLoading = "ERROR loading file"
|
||||||
const quitResponse = "Sayonara baby\n"
|
const quitResponse = "Sayonara baby\n"
|
||||||
const runUntilBPMessage = "Running until a breakpoint, key press or data sent, menu opening or other event\n"
|
const runUntilBPMessage = "Running until a breakpoint, key press or data sent, menu opening or other event\n"
|
||||||
|
|
||||||
|
var PushValueTypeName = []string{
|
||||||
|
"default",
|
||||||
|
"call",
|
||||||
|
"rst",
|
||||||
|
"push",
|
||||||
|
"maskable_interrupt",
|
||||||
|
"non_maskable_interrupt",
|
||||||
|
}
|
||||||
|
|||||||
@ -189,7 +189,7 @@ var commandHandlers = map[string]CommandHandler{
|
|||||||
"about": (*ZRCP).handleAbout,
|
"about": (*ZRCP).handleAbout,
|
||||||
"clear-membreakpoints": (*ZRCP).handleClearMemBreakpoints,
|
"clear-membreakpoints": (*ZRCP).handleClearMemBreakpoints,
|
||||||
"close-all-menus": (*ZRCP).handleEmptyHandler,
|
"close-all-menus": (*ZRCP).handleEmptyHandler,
|
||||||
"cpu-code-coverage": (*ZRCP).handleEmptyHandler,
|
"cpu-code-coverage": (*ZRCP).handleCPUCodeCoverage,
|
||||||
"cpu-history": (*ZRCP).handleCPUHistory,
|
"cpu-history": (*ZRCP).handleCPUHistory,
|
||||||
"cpu-step": (*ZRCP).handleCpuStep,
|
"cpu-step": (*ZRCP).handleCpuStep,
|
||||||
"disable-breakpoint": (*ZRCP).handleDisableBreakpoint,
|
"disable-breakpoint": (*ZRCP).handleDisableBreakpoint,
|
||||||
@ -239,9 +239,8 @@ func (p *ZRCP) handleDisassemble() (string, error) {
|
|||||||
func convertToUint16(s string) (uint16, error) {
|
func convertToUint16(s string) (uint16, error) {
|
||||||
v := strings.TrimSpace(strings.ToUpper(s))
|
v := strings.TrimSpace(strings.ToUpper(s))
|
||||||
base := 0
|
base := 0
|
||||||
if strings.HasSuffix(v, "h") || strings.HasSuffix(v, "H") {
|
if strings.HasSuffix(v, "H") {
|
||||||
v = strings.TrimSuffix(v, "H")
|
v = strings.TrimSuffix(v, "H")
|
||||||
v = strings.TrimSuffix(v, "h")
|
|
||||||
base = 16
|
base = 16
|
||||||
}
|
}
|
||||||
a, e := strconv.ParseUint(v, base, 16)
|
a, e := strconv.ParseUint(v, base, 16)
|
||||||
@ -282,21 +281,26 @@ func (p *ZRCP) SetMemBreakpoint(param string) string {
|
|||||||
|
|
||||||
func (p *ZRCP) handleCPUHistory() (string, error) {
|
func (p *ZRCP) handleCPUHistory() (string, error) {
|
||||||
params := strings.Split(p.params, " ")
|
params := strings.Split(p.params, " ")
|
||||||
if len(params) == 0 {
|
if len(params) < 1 {
|
||||||
return "", errors.New("error, no parameter")
|
return "", errors.New("error, no parameters")
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := params[0]
|
cmd := params[0]
|
||||||
nspe := errors.New("error, no second parameter")
|
nspe := errors.New("error, no second parameter")
|
||||||
|
|
||||||
switch cmd {
|
switch cmd {
|
||||||
|
|
||||||
case "enabled":
|
case "enabled":
|
||||||
if len(params) != 2 {
|
if len(params) < 2 {
|
||||||
return "", nspe
|
return "", nspe
|
||||||
}
|
}
|
||||||
p.debugger.SetCpuHistoryEnabled(params[1] == "yes")
|
p.debugger.SetCpuHistoryEnabled(params[1] == "yes")
|
||||||
|
|
||||||
case "clear":
|
case "clear":
|
||||||
p.debugger.CpuHistoryClear()
|
p.debugger.CpuHistoryClear()
|
||||||
|
|
||||||
case "started":
|
case "started":
|
||||||
if len(params) != 2 {
|
if len(params) < 2 {
|
||||||
return "", nspe
|
return "", nspe
|
||||||
}
|
}
|
||||||
p.debugger.SetCpuHistoryStarted(params[1] == "yes")
|
p.debugger.SetCpuHistoryStarted(params[1] == "yes")
|
||||||
@ -322,10 +326,15 @@ 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 "ignrephalt":
|
||||||
|
// ignore
|
||||||
|
default:
|
||||||
return "", errors.New("error: unknown history command: " + cmd)
|
return "", errors.New("error: unknown history command: " + cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *ZRCP) handleLoadBinary() (string, error) {
|
func (p *ZRCP) handleLoadBinary() (string, error) {
|
||||||
params := strings.Split(p.params, " ")
|
params := strings.Split(p.params, " ")
|
||||||
loadError := errors.New(respErrorLoading)
|
loadError := errors.New(respErrorLoading)
|
||||||
@ -461,6 +470,19 @@ func (p *ZRCP) handleSetRegister() (string, error) {
|
|||||||
return "error", errors.New("invalid set register value")
|
return "error", errors.New("invalid set register value")
|
||||||
}
|
}
|
||||||
switch params[0] {
|
switch params[0] {
|
||||||
|
case "AF":
|
||||||
|
state.A = uint8(val >> 8)
|
||||||
|
state.Flags.SetFlags(uint8(val))
|
||||||
|
case "BC":
|
||||||
|
state.B = uint8(val >> 8)
|
||||||
|
state.C = uint8(val)
|
||||||
|
case "DE":
|
||||||
|
state.D = uint8(val >> 8)
|
||||||
|
state.E = uint8(val)
|
||||||
|
case "HL":
|
||||||
|
state.H = uint8(val >> 8)
|
||||||
|
state.L = uint8(val)
|
||||||
|
// ------------------------------
|
||||||
case "SP":
|
case "SP":
|
||||||
state.SP = uint16(val)
|
state.SP = uint16(val)
|
||||||
case "PC":
|
case "PC":
|
||||||
@ -469,8 +491,25 @@ func (p *ZRCP) handleSetRegister() (string, error) {
|
|||||||
state.IX = uint16(val)
|
state.IX = uint16(val)
|
||||||
case "IY":
|
case "IY":
|
||||||
state.IY = uint16(val)
|
state.IY = uint16(val)
|
||||||
|
// ------------------------------
|
||||||
|
case "AF'":
|
||||||
|
state.AAlt = uint8(val >> 8)
|
||||||
|
state.FlagsAlt.SetFlags(uint8(val))
|
||||||
|
case "BC'":
|
||||||
|
state.BAlt = uint8(val >> 8)
|
||||||
|
state.CAlt = uint8(val)
|
||||||
|
case "DE'":
|
||||||
|
state.DAlt = uint8(val >> 8)
|
||||||
|
state.EAlt = uint8(val)
|
||||||
|
case "HL'":
|
||||||
|
state.HAlt = uint8(val >> 8)
|
||||||
|
state.LAlt = uint8(val)
|
||||||
|
|
||||||
|
// ------------------------------
|
||||||
case "A":
|
case "A":
|
||||||
state.A = uint8(val)
|
state.A = uint8(val)
|
||||||
|
case "F":
|
||||||
|
state.Flags.SetFlags(uint8(val))
|
||||||
case "B":
|
case "B":
|
||||||
state.B = uint8(val)
|
state.B = uint8(val)
|
||||||
case "C":
|
case "C":
|
||||||
@ -483,6 +522,24 @@ func (p *ZRCP) handleSetRegister() (string, error) {
|
|||||||
state.H = uint8(val)
|
state.H = uint8(val)
|
||||||
case "L":
|
case "L":
|
||||||
state.L = uint8(val)
|
state.L = uint8(val)
|
||||||
|
// ------------------------------
|
||||||
|
case "A'":
|
||||||
|
state.AAlt = uint8(val)
|
||||||
|
case "F'":
|
||||||
|
state.FlagsAlt.SetFlags(uint8(val))
|
||||||
|
case "B'":
|
||||||
|
state.BAlt = uint8(val)
|
||||||
|
case "C'":
|
||||||
|
state.CAlt = uint8(val)
|
||||||
|
case "D'":
|
||||||
|
state.DAlt = uint8(val)
|
||||||
|
case "E'":
|
||||||
|
state.EAlt = uint8(val)
|
||||||
|
case "H'":
|
||||||
|
state.HAlt = uint8(val)
|
||||||
|
case "L'":
|
||||||
|
state.LAlt = uint8(val)
|
||||||
|
// ------------------------------
|
||||||
case "I":
|
case "I":
|
||||||
state.I = uint8(val)
|
state.I = uint8(val)
|
||||||
case "R":
|
case "R":
|
||||||
@ -537,10 +594,14 @@ func (p *ZRCP) getExtendedStack() (string, error) {
|
|||||||
|
|
||||||
resp := ""
|
resp := ""
|
||||||
spEnd := sp - uint16(size*2)
|
spEnd := sp - uint16(size*2)
|
||||||
|
es, err := p.computer.ExtendedStack()
|
||||||
|
if err == nil {
|
||||||
for i := sp; i > spEnd; i -= 2 {
|
for i := sp; i > spEnd; i -= 2 {
|
||||||
resp += fmt.Sprintf("%04XH default\n", p.computer.MemRead(i))
|
resp += fmt.Sprintf("%04XH %s\n", p.computer.MemRead(i), PushValueTypeName[es[i]])
|
||||||
}
|
}
|
||||||
return resp, nil
|
}
|
||||||
|
log.Tracef("extended-stack get: %s", resp)
|
||||||
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ZRCP) handleSetBreakpoint() (string, error) {
|
func (p *ZRCP) handleSetBreakpoint() (string, error) {
|
||||||
@ -742,8 +803,47 @@ func (p *ZRCP) handleSetBreakpointPassCount() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *ZRCP) handleExtendedStack() (string, error) {
|
func (p *ZRCP) handleExtendedStack() (string, error) {
|
||||||
if strings.HasPrefix(p.params, "get") {
|
params := strings.Split(p.params, " ")
|
||||||
|
if len(params) < 1 {
|
||||||
|
return "", errors.New("error, not enough params")
|
||||||
|
}
|
||||||
|
cmd := params[0]
|
||||||
|
if cmd == "get" {
|
||||||
return p.getExtendedStack()
|
return p.getExtendedStack()
|
||||||
|
} else if cmd == "enabled" {
|
||||||
|
if len(params) < 2 {
|
||||||
|
return "", errors.New("error, expected yes|no")
|
||||||
|
}
|
||||||
|
p.computer.SetExtendedStack(params[1] == "yes")
|
||||||
|
} else {
|
||||||
|
return "", errors.New("error, unknown sub-command: " + cmd)
|
||||||
}
|
}
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleCPUCodeCoverage Handle commands:
|
||||||
|
// cpu-code-coverage enabled yes
|
||||||
|
// cpu-code-coverage enabled no
|
||||||
|
// cpu-code-coverage clear
|
||||||
|
func (p *ZRCP) handleCPUCodeCoverage() (string, error) {
|
||||||
|
command := strings.Split(p.params, " ")
|
||||||
|
if len(command) < 1 {
|
||||||
|
return "", errors.New("error, not enough arguments")
|
||||||
|
}
|
||||||
|
cmd := command[0]
|
||||||
|
resp := ""
|
||||||
|
switch cmd {
|
||||||
|
case "enabled":
|
||||||
|
if len(command) < 2 {
|
||||||
|
return "", errors.New("error, not arguments for enabled [yas|no]")
|
||||||
|
}
|
||||||
|
p.computer.SetCodeCoverage(command[1] == "yes")
|
||||||
|
case "clear":
|
||||||
|
p.computer.ClearCodeCoverage()
|
||||||
|
case "get":
|
||||||
|
for addr, _ := range p.computer.CodeCoverage() {
|
||||||
|
resp += fmt.Sprintf("%04X ", addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -1,74 +0,0 @@
|
|||||||
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
|
|
||||||
)
|
|
||||||
@ -173,6 +173,7 @@ func (c *ComputerType) getContext() map[string]interface{} {
|
|||||||
context["DE"] = uint16(s.D)<<8 | uint16(s.E)
|
context["DE"] = uint16(s.D)<<8 | uint16(s.E)
|
||||||
context["HL"] = uint16(s.H)<<8 | uint16(s.L)
|
context["HL"] = uint16(s.H)<<8 | uint16(s.L)
|
||||||
context["AF"] = uint16(s.A)<<8 | uint16(s.Flags.GetFlags())
|
context["AF"] = uint16(s.A)<<8 | uint16(s.Flags.GetFlags())
|
||||||
|
context["MEM"] = c.memory.MemRead
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -458,3 +459,23 @@ func (c *ComputerType) AutoLoadFloppy() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ComputerType) ClearCodeCoverage() {
|
||||||
|
c.cpu.ClearCodeCoverage()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ComputerType) SetCodeCoverage(enabled bool) {
|
||||||
|
c.cpu.SetCodeCoverage(enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ComputerType) CodeCoverage() map[uint16]bool {
|
||||||
|
return c.cpu.CodeCoverage()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ComputerType) SetExtendedStack(enabled bool) {
|
||||||
|
c.cpu.SetExtendedStack(enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ComputerType) ExtendedStack() ([]byte, error) {
|
||||||
|
return c.cpu.ExtendedStack()
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,18 @@
|
|||||||
package c99
|
package c99
|
||||||
|
|
||||||
import "okemu/z80"
|
import (
|
||||||
|
"errors"
|
||||||
|
"okemu/z80"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PushValueTypeDefault = iota
|
||||||
|
PushValueTypeCall
|
||||||
|
PushValueTypeRst
|
||||||
|
PushValueTypePush
|
||||||
|
PushValueTypeMaskableInt
|
||||||
|
PushValueTypeNonMaskableInt
|
||||||
|
)
|
||||||
|
|
||||||
type Z80 struct {
|
type Z80 struct {
|
||||||
|
|
||||||
@ -35,6 +47,10 @@ type Z80 struct {
|
|||||||
|
|
||||||
core z80.MemIoRW
|
core z80.MemIoRW
|
||||||
memAccess map[uint16]byte
|
memAccess map[uint16]byte
|
||||||
|
codeCoverageEnabled bool
|
||||||
|
codeCoverage map[uint16]bool
|
||||||
|
extendedStackEnabled bool
|
||||||
|
extendedStack [65536]uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -92,13 +108,17 @@ func New(core z80.MemIoRW) *Z80 {
|
|||||||
z.intPending = false
|
z.intPending = false
|
||||||
z.nmiPending = false
|
z.nmiPending = false
|
||||||
z.intData = 0
|
z.intData = 0
|
||||||
|
z.codeCoverageEnabled = false
|
||||||
|
z.codeCoverage = make(map[uint16]bool)
|
||||||
return &z
|
return &z
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunInstruction executes the next instruction in memory + handles interrupts
|
// RunInstruction executes the next instruction in memory + handles interrupts
|
||||||
func (z *Z80) RunInstruction() (uint32, *map[uint16]byte) {
|
func (z *Z80) RunInstruction() (uint32, *map[uint16]byte) {
|
||||||
z.memAccess = map[uint16]byte{}
|
z.memAccess = map[uint16]byte{}
|
||||||
|
if z.codeCoverageEnabled {
|
||||||
|
z.codeCoverage[z.pc] = true
|
||||||
|
}
|
||||||
pre := z.cycleCount
|
pre := z.cycleCount
|
||||||
if z.isHalted {
|
if z.isHalted {
|
||||||
z.execOpcode(0x00)
|
z.execOpcode(0x00)
|
||||||
@ -223,3 +243,35 @@ func (z *Z80) getAltFlags() z80.FlagsType {
|
|||||||
func (z *Z80) PC() uint16 {
|
func (z *Z80) PC() uint16 {
|
||||||
return z.pc
|
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
|
||||||
|
}
|
||||||
|
|||||||
@ -28,6 +28,7 @@ func (z *Z80) ww(addr uint16, val uint16) {
|
|||||||
func (z *Z80) pushW(val uint16) {
|
func (z *Z80) pushW(val uint16) {
|
||||||
z.sp -= 2
|
z.sp -= 2
|
||||||
z.ww(z.sp, val)
|
z.ww(z.sp, val)
|
||||||
|
z.extendedStack[z.sp] = PushValueTypePush
|
||||||
}
|
}
|
||||||
|
|
||||||
func (z *Z80) popW() uint16 {
|
func (z *Z80) popW() uint16 {
|
||||||
|
|||||||
@ -20,6 +20,7 @@ func (z *Z80) condJump(condition bool) {
|
|||||||
// calls to next word in memory
|
// calls to next word in memory
|
||||||
func (z *Z80) call(addr uint16) {
|
func (z *Z80) call(addr uint16) {
|
||||||
z.pushW(z.pc)
|
z.pushW(z.pc)
|
||||||
|
z.extendedStack[z.sp] = PushValueTypeCall
|
||||||
z.pc = addr
|
z.pc = addr
|
||||||
z.memPtr = addr
|
z.memPtr = addr
|
||||||
}
|
}
|
||||||
@ -1155,21 +1156,28 @@ func (z *Z80) execOpcode(opcode byte) {
|
|||||||
|
|
||||||
case 0xC7:
|
case 0xC7:
|
||||||
z.call(0x00) // rst 0
|
z.call(0x00) // rst 0
|
||||||
|
z.extendedStack[z.sp] = PushValueTypeRst
|
||||||
case 0xCF:
|
case 0xCF:
|
||||||
z.call(0x08) // rst 1
|
z.call(0x08) // rst 1
|
||||||
|
z.extendedStack[z.sp] = PushValueTypeRst
|
||||||
case 0xD7:
|
case 0xD7:
|
||||||
z.call(0x10) // rst 2
|
z.call(0x10) // rst 2
|
||||||
|
z.extendedStack[z.sp] = PushValueTypeRst
|
||||||
case 0xDF:
|
case 0xDF:
|
||||||
z.call(0x18) // rst 3
|
z.call(0x18) // rst 3
|
||||||
|
z.extendedStack[z.sp] = PushValueTypeRst
|
||||||
case 0xE7:
|
case 0xE7:
|
||||||
z.call(0x20) // rst 4
|
z.call(0x20) // rst 4
|
||||||
|
z.extendedStack[z.sp] = PushValueTypeRst
|
||||||
case 0xEF:
|
case 0xEF:
|
||||||
z.call(0x28) // rst 5
|
z.call(0x28) // rst 5
|
||||||
|
z.extendedStack[z.sp] = PushValueTypeRst
|
||||||
case 0xF7:
|
case 0xF7:
|
||||||
z.call(0x30) // rst 6
|
z.call(0x30) // rst 6
|
||||||
|
z.extendedStack[z.sp] = PushValueTypeRst
|
||||||
case 0xFF:
|
case 0xFF:
|
||||||
z.call(0x38) // rst 7
|
z.call(0x38) // rst 7
|
||||||
|
z.extendedStack[z.sp] = PushValueTypeRst
|
||||||
case 0xC5:
|
case 0xC5:
|
||||||
z.pushW(z.bc()) // push bc
|
z.pushW(z.bc()) // push bc
|
||||||
case 0xD5:
|
case 0xD5:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user