zemu 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/zemu/config.rb +1 -1
- data/lib/zemu/instance.rb +11 -6
- data/lib/zemu/interactive.rb +188 -0
- data/lib/zemu.rb +13 -0
- data/src/debug.c +6 -2
- data/src/debug.h +1 -1
- data/src/io.c.erb +12 -0
- metadata +16 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b90e15af47b7ba86466e516ad1d1695c391095e43349536679433d4ac056aa7c
|
4
|
+
data.tar.gz: 1d2b05874a9b666c28c467426017ff12fdd1b25fafaadb71d8fcc44e33c89879
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5cb969fc0731adb33864a27eb80314afd926bee8c8a59e3b01164cd0794c764522c3e3171a86d5fccd2babd9273c1067913d8641d8b23e7f41f796670fc0b794
|
7
|
+
data.tar.gz: b2a7c1edf04f6a47b0a20ec05e00cd3861d028e81e34d460f98c8006d0f50d792f896dfc7d6d59c1c87b5612f6e72c069025105a4a1a71ab22fa8a1917b64f77
|
data/lib/zemu/config.rb
CHANGED
data/lib/zemu/instance.rb
CHANGED
@@ -93,12 +93,14 @@ module Zemu
|
|
93
93
|
# Gets the given number of characters from the emulated machine's send buffer.
|
94
94
|
#
|
95
95
|
# Note: If count is greater than the number of characters currently in the buffer,
|
96
|
-
# the returned string
|
96
|
+
# the returned string will be shorter than the given count.
|
97
97
|
def serial_gets(count=nil)
|
98
98
|
return_string = ""
|
99
99
|
|
100
|
-
|
101
|
-
|
100
|
+
actual_count = @wrapper.zemu_io_serial_buffer_size()
|
101
|
+
|
102
|
+
if count.nil? || actual_count < count
|
103
|
+
count = actual_count
|
102
104
|
end
|
103
105
|
|
104
106
|
count.to_i.times do
|
@@ -111,8 +113,11 @@ module Zemu
|
|
111
113
|
# Continue running this instance until either:
|
112
114
|
# * A HALT instruction is executed
|
113
115
|
# * A breakpoint is hit
|
114
|
-
|
115
|
-
|
116
|
+
# * The number of cycles given has been executed
|
117
|
+
#
|
118
|
+
# Returns the number of cycles executed.
|
119
|
+
def continue(run_cycles=-1)
|
120
|
+
return @wrapper.zemu_debug_continue(@instance, run_cycles)
|
116
121
|
end
|
117
122
|
|
118
123
|
# Set a breakpoint of the given type at the given address.
|
@@ -156,7 +161,7 @@ module Zemu
|
|
156
161
|
|
157
162
|
wrapper.attach_function :zemu_reset, [:pointer], :void
|
158
163
|
|
159
|
-
wrapper.attach_function :zemu_debug_continue, [:pointer], :
|
164
|
+
wrapper.attach_function :zemu_debug_continue, [:pointer, :int64], :uint64
|
160
165
|
|
161
166
|
wrapper.attach_function :zemu_debug_halted, [], :bool
|
162
167
|
wrapper.attach_function :zemu_debug_break, [], :bool
|
@@ -0,0 +1,188 @@
|
|
1
|
+
module Zemu
|
2
|
+
# An interactive instance of a Zemu emulator.
|
3
|
+
# Wraps a Zemu::Instance to allow for user input and debugging.
|
4
|
+
class InteractiveInstance
|
5
|
+
# Constructor.
|
6
|
+
#
|
7
|
+
# Create a new interactive wrapper for the given instance.
|
8
|
+
def initialize(instance)
|
9
|
+
@instance = instance
|
10
|
+
|
11
|
+
@master, @slave = PTY.open
|
12
|
+
log "Opened PTY at #{@slave.path}"
|
13
|
+
end
|
14
|
+
|
15
|
+
# Logs a message to the user output.
|
16
|
+
def log(message)
|
17
|
+
STDOUT.puts " " + message
|
18
|
+
end
|
19
|
+
|
20
|
+
# Close the interactive wrapper
|
21
|
+
def close
|
22
|
+
@master.close
|
23
|
+
@slave.close
|
24
|
+
@instance.quit
|
25
|
+
end
|
26
|
+
|
27
|
+
# Run the interactive emulator until the user exits.
|
28
|
+
def run
|
29
|
+
quit = false
|
30
|
+
|
31
|
+
until quit
|
32
|
+
print "ZEMU> "
|
33
|
+
# Get a command from the user.
|
34
|
+
cmd = STDIN.gets.split
|
35
|
+
|
36
|
+
if cmd[0] == "quit"
|
37
|
+
quit = true
|
38
|
+
|
39
|
+
elsif cmd[0] == "continue"
|
40
|
+
if cmd[1].nil?
|
41
|
+
continue
|
42
|
+
else
|
43
|
+
continue(cmd[1].to_i)
|
44
|
+
end
|
45
|
+
|
46
|
+
elsif cmd[0] == "step"
|
47
|
+
continue(1)
|
48
|
+
|
49
|
+
elsif cmd[0] == "registers"
|
50
|
+
registers
|
51
|
+
|
52
|
+
elsif cmd[0] == "break"
|
53
|
+
add_breakpoint(cmd[1])
|
54
|
+
|
55
|
+
elsif cmd[0] == "memory"
|
56
|
+
if cmd[2].nil?
|
57
|
+
memory(cmd[1])
|
58
|
+
else
|
59
|
+
memory(cmd[1], cmd[2])
|
60
|
+
end
|
61
|
+
|
62
|
+
elsif cmd[0] == "help"
|
63
|
+
log "Available commands:"
|
64
|
+
log " continue [<n>] - Continue execution for <n> cycles"
|
65
|
+
log " step - Step over a single instruction"
|
66
|
+
log " registers - View register contents"
|
67
|
+
log " memory <a> [<n>] - View <n> bytes of memory, starting at address <a>."
|
68
|
+
log " <n> defaults to 1 if omitted."
|
69
|
+
log " break <a> - Set a breakpoint at the given address <a>."
|
70
|
+
log " quit - End this emulator instance."
|
71
|
+
|
72
|
+
else
|
73
|
+
log "Invalid command. Type 'help' for available commands."
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
close
|
79
|
+
end
|
80
|
+
|
81
|
+
# Outputs a table giving the current values of the instance's registers.
|
82
|
+
def registers
|
83
|
+
log "A: #{r("A")} F: #{r("F")}"
|
84
|
+
log "B: #{r("B")} C: #{r("C")}"
|
85
|
+
log "D: #{r("D")} E: #{r("E")}"
|
86
|
+
log "H: #{r("H")} L: #{r("L")}"
|
87
|
+
log ""
|
88
|
+
log "IX: #{r16("IX")}"
|
89
|
+
log "IY: #{r16("IY")}"
|
90
|
+
log "SP: #{r16("SP")}"
|
91
|
+
log "PC: #{r16("PC")}"
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns a particular 8-bit register value.
|
95
|
+
def r(reg)
|
96
|
+
return "0x%02x" % @instance.registers[reg]
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns a particular 16-bit register value.
|
100
|
+
def r16(reg)
|
101
|
+
return "0x%04x" % @instance.registers[reg]
|
102
|
+
end
|
103
|
+
|
104
|
+
# Continue for *up to* the given number of cycles.
|
105
|
+
# Fewer cycles may be executed, depending on the behaviour of the processor.
|
106
|
+
def continue(cycles=-1)
|
107
|
+
if cycles == 0
|
108
|
+
log "Invalid value: #{cycles}"
|
109
|
+
return
|
110
|
+
end
|
111
|
+
|
112
|
+
# Continue executing instruction-by-instruction.
|
113
|
+
# Process IO in-between.
|
114
|
+
cycles_left = cycles
|
115
|
+
actual_cycles = 0
|
116
|
+
|
117
|
+
while ((cycles == -1) || (cycles_left > 0))
|
118
|
+
old_pc = r16("PC")
|
119
|
+
|
120
|
+
process_serial
|
121
|
+
cycles_done = @instance.continue(1)
|
122
|
+
cycles_left -= cycles_done
|
123
|
+
actual_cycles += cycles_done
|
124
|
+
|
125
|
+
# Have we hit a breakpoint or HALT instruction?
|
126
|
+
if @instance.break?
|
127
|
+
log "Hit breakpoint at #{r16("PC")}."
|
128
|
+
break
|
129
|
+
elsif @instance.halted?
|
130
|
+
log "Executed HALT instruction at #{old_pc}."
|
131
|
+
break
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
log "Executed for #{actual_cycles} cycles."
|
136
|
+
end
|
137
|
+
|
138
|
+
# Add a breakpoint at the address given by the string.
|
139
|
+
def add_breakpoint(addr_str)
|
140
|
+
@instance.break(addr_str.to_i(16), :program)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Dump an amount of memory.
|
144
|
+
def memory(address, size="1")
|
145
|
+
if address.nil?
|
146
|
+
log "Expected an address, got #{address}."
|
147
|
+
return
|
148
|
+
end
|
149
|
+
|
150
|
+
if (address.to_i(16) < 1 || address.to_i(16) > 0xffff)
|
151
|
+
log "Invalid address: 0x%04x" % address.to_i(16)
|
152
|
+
return
|
153
|
+
end
|
154
|
+
|
155
|
+
(address.to_i(16)...address.to_i(16) + size.to_i(16)).each do |a|
|
156
|
+
m = @instance.memory(a)
|
157
|
+
if (m < 32)
|
158
|
+
log "%04x: %02x ." % [a, m]
|
159
|
+
else
|
160
|
+
log ("%04x: %02x " % [a, m]) + m.chr("UTF-8")
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Process serial input/output via the TTY.
|
166
|
+
def process_serial
|
167
|
+
# Read/write serial.
|
168
|
+
# Get the strings to be input/output.
|
169
|
+
input = ""
|
170
|
+
ready = IO.select([@master], [], [], 0)
|
171
|
+
unless ready.nil? || ready.empty?
|
172
|
+
input = @master.read(1)
|
173
|
+
end
|
174
|
+
|
175
|
+
output = @instance.serial_gets(1)
|
176
|
+
|
177
|
+
unless input.empty?
|
178
|
+
@instance.serial_puts input
|
179
|
+
log "Serial in: #{input}"
|
180
|
+
end
|
181
|
+
|
182
|
+
unless output.empty?
|
183
|
+
@master.write output
|
184
|
+
log "Serial out: #{output}"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
data/lib/zemu.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
require 'erb'
|
2
2
|
|
3
|
+
require 'pty'
|
4
|
+
|
3
5
|
require_relative 'zemu/config'
|
4
6
|
require_relative 'zemu/instance'
|
7
|
+
require_relative 'zemu/interactive'
|
5
8
|
|
6
9
|
# Zemu is a module providing an interface to build and interact with
|
7
10
|
# configurable Z80 emulators.
|
@@ -66,6 +69,16 @@ module Zemu
|
|
66
69
|
return Instance.new(configuration)
|
67
70
|
end
|
68
71
|
|
72
|
+
# Starts an interactive instance of an emulator, according to the given configuration.
|
73
|
+
#
|
74
|
+
# @param [Zemu::Config] configuration The configuration for which an emulator will be generated.
|
75
|
+
def Zemu::start_interactive(configuration)
|
76
|
+
instance = start(configuration)
|
77
|
+
|
78
|
+
interactive = InteractiveInstance.new(instance)
|
79
|
+
interactive.run
|
80
|
+
end
|
81
|
+
|
69
82
|
# Builds a library according to the given configuration.
|
70
83
|
#
|
71
84
|
# @param [Zemu::Config] configuration The configuration for which an emulator will be generated.
|
data/src/debug.c
CHANGED
@@ -8,7 +8,7 @@ RunState zemu_debug_state = UNDEFINED;
|
|
8
8
|
zuint16 breakpoints[ZEMU_DEBUG_MAX_BREAKPOINTS];
|
9
9
|
unsigned int breakpoint_count = 0;
|
10
10
|
|
11
|
-
zusize zemu_debug_continue(Z80 * instance)
|
11
|
+
zusize zemu_debug_continue(Z80 * instance, zinteger run_cycles)
|
12
12
|
{
|
13
13
|
/* Return if we've halted. */
|
14
14
|
if (zemu_debug_state == HALTED) return 0;
|
@@ -17,7 +17,11 @@ zusize zemu_debug_continue(Z80 * instance)
|
|
17
17
|
|
18
18
|
zemu_debug_state = RUNNING;
|
19
19
|
|
20
|
-
|
20
|
+
/* Run as long as:
|
21
|
+
* We don't hit a breakpoint
|
22
|
+
* We haven't run for more than the number of cycles given
|
23
|
+
*/
|
24
|
+
while (zemu_debug_state == RUNNING && (run_cycles < 0 || cycles < run_cycles))
|
21
25
|
{
|
22
26
|
cycles += zemu_debug_step(instance);
|
23
27
|
|
data/src/debug.h
CHANGED
data/src/io.c.erb
CHANGED
@@ -92,6 +92,18 @@ zuint8 zemu_io_in(void * context, zuint16 port)
|
|
92
92
|
{
|
93
93
|
return zemu_io_serial_slave_gets();
|
94
94
|
}
|
95
|
+
|
96
|
+
else if (port == <%= device.ready_port %>)
|
97
|
+
{
|
98
|
+
if (io_serial_buffer_master.head == io_serial_buffer_master.tail)
|
99
|
+
{
|
100
|
+
return 0;
|
101
|
+
}
|
102
|
+
else
|
103
|
+
{
|
104
|
+
return 1;
|
105
|
+
}
|
106
|
+
}
|
95
107
|
<% end %>
|
96
108
|
<% end %>
|
97
109
|
return 0;
|
metadata
CHANGED
@@ -1,16 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zemu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jay Valentine
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-02-18 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description:
|
13
|
+
description: |2
|
14
|
+
Zemu is a gem which allows the user to configure a Z80-based system
|
15
|
+
and then launch emulation instances of that system.
|
16
|
+
These instances can be interacted with programmatically, allowing the
|
17
|
+
user to inspect the contents of registers and memory, step, add breakpoints,
|
18
|
+
and more.
|
19
|
+
|
20
|
+
The gem requires the user to install a compatible C compiler.
|
21
|
+
Currently the only compatible compiler is clang.
|
22
|
+
|
23
|
+
Please report any issues on the GitHub page for the gem.
|
24
|
+
This is accessible under "Homepage".
|
14
25
|
email: jayv136@gmail.com
|
15
26
|
executables: []
|
16
27
|
extensions: []
|
@@ -19,6 +30,7 @@ files:
|
|
19
30
|
- lib/zemu.rb
|
20
31
|
- lib/zemu/config.rb
|
21
32
|
- lib/zemu/instance.rb
|
33
|
+
- lib/zemu/interactive.rb
|
22
34
|
- src/debug.c
|
23
35
|
- src/debug.h
|
24
36
|
- src/external/Z/API/Z/ABIs/generic/allocator.h
|
@@ -325,5 +337,5 @@ rubyforge_project:
|
|
325
337
|
rubygems_version: 2.7.6
|
326
338
|
signing_key:
|
327
339
|
specification_version: 4
|
328
|
-
summary:
|
340
|
+
summary: A configurable Z80 emulator.
|
329
341
|
test_files: []
|