zemu 1.0.0 → 1.1.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 +48 -18
- data/lib/zemu/instance.rb +50 -8
- data/lib/zemu/interactive.rb +0 -19
- data/src/bus.c.erb +26 -6
- data/src/bus.h.erb +2 -2
- data/src/debug.c +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e57309f08bc232fd44db53f5961029283fdffaaf0e01c9e8e2ea36807987b33
|
4
|
+
data.tar.gz: 493c60567914e637c50244dc16f9d4ba47d8eea7ed1b3c8abce442fa142264bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4746e81b7cc05441d12fe45c284955a4aae66a8877be77168a110bb08aada6f0bd6e1cf7ca5d1085e0a059d1d4e1d0ed8288f1aae72d159b0cb699342f021005
|
7
|
+
data.tar.gz: 667278d74f889f7d555807d548715fa110d6a8b9d0dd0aacd77c37806517218e7058b42c61bc6d744c063fd7869c490a12674308fbc401face274209320d6b91
|
data/lib/zemu/config.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
1
3
|
module Zemu
|
2
4
|
# Abstract configuration object.
|
3
5
|
# All configuration objects should inherit from this.
|
@@ -89,10 +91,17 @@ module Zemu
|
|
89
91
|
end
|
90
92
|
|
91
93
|
@nmi = false
|
94
|
+
@interrupt = false
|
92
95
|
|
93
96
|
super
|
94
97
|
end
|
95
98
|
|
99
|
+
# Parameters used for generating C code to
|
100
|
+
# implement this bus device.
|
101
|
+
def memory
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
96
105
|
# Setup to be performed on initialising the emulator
|
97
106
|
# instance.
|
98
107
|
def when_setup
|
@@ -147,7 +156,7 @@ module Zemu
|
|
147
156
|
# Deriving objects can use the nmi function
|
148
157
|
# to set the state of the non-maskable interrupt
|
149
158
|
# at each clock cycle.
|
150
|
-
def clock
|
159
|
+
def clock(cycles)
|
151
160
|
end
|
152
161
|
|
153
162
|
# FFI functions provided by this device.
|
@@ -165,6 +174,16 @@ module Zemu
|
|
165
174
|
@nmi
|
166
175
|
end
|
167
176
|
|
177
|
+
# Sets state of INT for this device.
|
178
|
+
def interrupt(state)
|
179
|
+
@interrupt = state
|
180
|
+
end
|
181
|
+
|
182
|
+
# Gets state of INT for this device.
|
183
|
+
def interrupt?
|
184
|
+
@interrupt
|
185
|
+
end
|
186
|
+
|
168
187
|
# Parameters for a bus device.
|
169
188
|
def params
|
170
189
|
%w(name)
|
@@ -208,6 +227,32 @@ module Zemu
|
|
208
227
|
false
|
209
228
|
end
|
210
229
|
|
230
|
+
# Parameters used for generating C code to
|
231
|
+
# implement this memory block.
|
232
|
+
def memory
|
233
|
+
contents_initializer = ""
|
234
|
+
@contents.each_slice(32) do |c|
|
235
|
+
contents_initializer += c.map { |b| b.to_s }.join(", ")
|
236
|
+
contents_initializer += ",\n"
|
237
|
+
end
|
238
|
+
|
239
|
+
m = OpenStruct.new
|
240
|
+
|
241
|
+
m.setup = <<eos
|
242
|
+
zuint8 memory_#{name}[#{size}] =
|
243
|
+
{
|
244
|
+
#{contents_initializer}
|
245
|
+
};
|
246
|
+
eos
|
247
|
+
|
248
|
+
m.address = address
|
249
|
+
m.size = size
|
250
|
+
m.access_read = "memory_#{name}"
|
251
|
+
m.access_write = "memory_#{name}"
|
252
|
+
|
253
|
+
m
|
254
|
+
end
|
255
|
+
|
211
256
|
# Memory bus read handler.
|
212
257
|
#
|
213
258
|
# Handles read access via the memory bus to this device.
|
@@ -217,15 +262,6 @@ module Zemu
|
|
217
262
|
# Returns the value read, or nil if no value
|
218
263
|
# (e.g. if address falls outside range for this device).
|
219
264
|
def mem_read(addr)
|
220
|
-
# Return value in memory's contents if the address
|
221
|
-
# falls within range.
|
222
|
-
if (addr >= address) && (addr < (address + size))
|
223
|
-
offset = addr - address
|
224
|
-
return @contents[offset]
|
225
|
-
end
|
226
|
-
|
227
|
-
# Otherwise return nil - address does not correspond
|
228
|
-
# to this memory block.
|
229
265
|
nil
|
230
266
|
end
|
231
267
|
|
@@ -236,12 +272,6 @@ module Zemu
|
|
236
272
|
# @param addr The address being accessed.
|
237
273
|
# @param value The value being written.
|
238
274
|
def mem_write(addr, value)
|
239
|
-
# If address falls within range, set value in
|
240
|
-
# memory contents.
|
241
|
-
if (addr >= address) && (addr < (address + size))
|
242
|
-
offset = addr - address
|
243
|
-
@contents[offset] = value
|
244
|
-
end
|
245
275
|
end
|
246
276
|
|
247
277
|
# Valid parameters for this object.
|
@@ -673,10 +703,10 @@ module Zemu
|
|
673
703
|
#
|
674
704
|
# Handles a clock cycle for this device.
|
675
705
|
# Sets NMI active if the count reaches 0.
|
676
|
-
def clock
|
706
|
+
def clock(cycles)
|
677
707
|
if @running == RUNNING
|
678
708
|
if @count > 0
|
679
|
-
@count -=
|
709
|
+
@count -= cycles
|
680
710
|
else
|
681
711
|
nmi(true)
|
682
712
|
end
|
data/lib/zemu/instance.rb
CHANGED
@@ -2,6 +2,17 @@ require 'ffi'
|
|
2
2
|
require 'ostruct'
|
3
3
|
|
4
4
|
module Zemu
|
5
|
+
# Exception raised when an error occurs in a Zemu::Instance.
|
6
|
+
class InstanceError < StandardError
|
7
|
+
# Constructor.
|
8
|
+
#
|
9
|
+
# @param msg The exception message
|
10
|
+
#
|
11
|
+
def initialize(msg="An error occurred with the Zemu::Instance")
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
5
16
|
# Represents an instance of a Zemu emulator.
|
6
17
|
#
|
7
18
|
# Provides methods by which the state of the emulator can be observed
|
@@ -60,8 +71,6 @@ module Zemu
|
|
60
71
|
UNDEFINED = -1
|
61
72
|
end
|
62
73
|
|
63
|
-
|
64
|
-
|
65
74
|
def initialize(configuration)
|
66
75
|
@devices = configuration.devices
|
67
76
|
|
@@ -93,6 +102,7 @@ module Zemu
|
|
93
102
|
v = d.mem_read(addr)
|
94
103
|
unless v.nil?
|
95
104
|
r = v
|
105
|
+
break
|
96
106
|
end
|
97
107
|
end
|
98
108
|
|
@@ -113,6 +123,7 @@ module Zemu
|
|
113
123
|
v = d.io_read(port)
|
114
124
|
unless v.nil?
|
115
125
|
r = v
|
126
|
+
break
|
116
127
|
end
|
117
128
|
end
|
118
129
|
|
@@ -120,9 +131,9 @@ module Zemu
|
|
120
131
|
end
|
121
132
|
|
122
133
|
# IO read handler.
|
123
|
-
@io_clock = Proc.new do
|
134
|
+
@io_clock = Proc.new do |cycles|
|
124
135
|
@devices.each do |d|
|
125
|
-
d.clock()
|
136
|
+
d.clock(cycles)
|
126
137
|
end
|
127
138
|
|
128
139
|
bus_state = 0
|
@@ -131,6 +142,10 @@ module Zemu
|
|
131
142
|
bus_state |= 1
|
132
143
|
end
|
133
144
|
|
145
|
+
if @devices.any? { |d| d.interrupt? }
|
146
|
+
bus_state |= 2
|
147
|
+
end
|
148
|
+
|
134
149
|
bus_state
|
135
150
|
end
|
136
151
|
|
@@ -147,6 +162,7 @@ module Zemu
|
|
147
162
|
@state = RunState::UNDEFINED
|
148
163
|
|
149
164
|
@breakpoints = {}
|
165
|
+
@tracepoints = {}
|
150
166
|
end
|
151
167
|
|
152
168
|
# Returns the device with the given name, or nil
|
@@ -221,7 +237,7 @@ module Zemu
|
|
221
237
|
# emulated machine.
|
222
238
|
def serial_puts(string)
|
223
239
|
string.each_char do |c|
|
224
|
-
|
240
|
+
device('serial').put_byte(c.ord)
|
225
241
|
end
|
226
242
|
end
|
227
243
|
|
@@ -237,14 +253,14 @@ module Zemu
|
|
237
253
|
def serial_gets(count=nil)
|
238
254
|
return_string = ""
|
239
255
|
|
240
|
-
actual_count =
|
256
|
+
actual_count = device('serial').transmitted_count()
|
241
257
|
|
242
258
|
if count.nil? || actual_count < count
|
243
259
|
count = actual_count
|
244
260
|
end
|
245
261
|
|
246
262
|
count.to_i.times do
|
247
|
-
return_string +=
|
263
|
+
return_string += device('serial').get_byte().chr
|
248
264
|
end
|
249
265
|
|
250
266
|
return return_string
|
@@ -283,6 +299,12 @@ module Zemu
|
|
283
299
|
|
284
300
|
pc = @wrapper.zemu_debug_pc(@instance)
|
285
301
|
|
302
|
+
# If there's a tracepoint at this address,
|
303
|
+
# execute the associated proc.
|
304
|
+
unless @tracepoints[pc].nil?
|
305
|
+
@tracepoints[pc].call(self)
|
306
|
+
end
|
307
|
+
|
286
308
|
# If the PC is now pointing to one of our breakpoints,
|
287
309
|
# we're in the BREAK state.
|
288
310
|
if @breakpoints[pc]
|
@@ -304,6 +326,26 @@ module Zemu
|
|
304
326
|
@breakpoints[address] = true
|
305
327
|
end
|
306
328
|
|
329
|
+
# Set a tracepoint to execute the given block at an address.
|
330
|
+
#
|
331
|
+
# @param address The address of the tracepoint
|
332
|
+
# @param block The block to execute at the tracepoint.
|
333
|
+
# The block takes the instance as a parameter.
|
334
|
+
#
|
335
|
+
# @example
|
336
|
+
# instance.trace(0x1234) do |i|
|
337
|
+
# puts "HL value: 04x" % i.registers["HL"]
|
338
|
+
# end
|
339
|
+
#
|
340
|
+
def trace(address, &block)
|
341
|
+
if block.arity != 1
|
342
|
+
raise InstanceError,
|
343
|
+
"Wrong arity for tracepoint - expected 1, got #{block.arity}"
|
344
|
+
end
|
345
|
+
|
346
|
+
@tracepoints[address] = block
|
347
|
+
end
|
348
|
+
|
307
349
|
# Remove a breakpoint of the given type at the given address.
|
308
350
|
# Does nothing if no breakpoint previously existed at that address.
|
309
351
|
#
|
@@ -343,7 +385,7 @@ module Zemu
|
|
343
385
|
|
344
386
|
wrapper.callback :io_write_handler, [:uint8, :uint8], :void
|
345
387
|
wrapper.callback :io_read_handler, [:uint8], :uint8
|
346
|
-
wrapper.callback :io_clock_handler, [:
|
388
|
+
wrapper.callback :io_clock_handler, [:uint64], :uint8
|
347
389
|
|
348
390
|
wrapper.attach_function :zemu_set_mem_write_handler, [:mem_write_handler], :void
|
349
391
|
wrapper.attach_function :zemu_set_mem_read_handler, [:mem_read_handler], :void
|
data/lib/zemu/interactive.rb
CHANGED
@@ -183,9 +183,6 @@ module Zemu
|
|
183
183
|
serial_count = @instance.serial_delay.to_f
|
184
184
|
|
185
185
|
while ((cycles == -1) || (cycles_left > 0))
|
186
|
-
# Get time before execution.
|
187
|
-
start = Time.now
|
188
|
-
|
189
186
|
old_pc = r16("PC")
|
190
187
|
|
191
188
|
if (serial_count >= @instance.serial_delay)
|
@@ -197,26 +194,10 @@ module Zemu
|
|
197
194
|
cycles_left -= cycles_done
|
198
195
|
actual_cycles += cycles_done
|
199
196
|
|
200
|
-
@trace << r16("PC")
|
201
|
-
@trace = @trace[1..] if @trace.size > 200
|
202
|
-
|
203
|
-
# Get time after execution.
|
204
|
-
ending = Time.now
|
205
|
-
|
206
197
|
# Get elapsed time and calculate padding time to match clock speed.
|
207
198
|
if @instance.clock_speed > 0
|
208
|
-
elapsed = ending - start
|
209
|
-
|
210
199
|
execution_time = cycles_done * (1.0/@instance.clock_speed)
|
211
200
|
serial_count += execution_time
|
212
|
-
|
213
|
-
padding = execution_time - elapsed
|
214
|
-
sleep(padding) unless padding < 0
|
215
|
-
end
|
216
|
-
|
217
|
-
if (@instance.memory(0x200) != 0xf3)
|
218
|
-
log "Buffer overflow at #{r16("PC")}"
|
219
|
-
break
|
220
201
|
end
|
221
202
|
|
222
203
|
# Have we hit a breakpoint or HALT instruction?
|
data/src/bus.c.erb
CHANGED
@@ -7,6 +7,11 @@ io_write_handler_t * io_write_handler;
|
|
7
7
|
io_read_handler_t * io_read_handler;
|
8
8
|
io_clock_handler_t * io_clock_handler;
|
9
9
|
|
10
|
+
<% devices.each do |d| %>
|
11
|
+
<% next if d.memory.nil? %>
|
12
|
+
<%= d.memory.setup %>
|
13
|
+
<% end %>
|
14
|
+
|
10
15
|
void zemu_set_mem_write_handler(mem_write_handler_t * h)
|
11
16
|
{
|
12
17
|
mem_write_handler = h;
|
@@ -34,25 +39,37 @@ void zemu_set_io_clock_handler(io_clock_handler_t * h)
|
|
34
39
|
|
35
40
|
zuint8 zemu_memory_read(void * context, zuint16 address)
|
36
41
|
{
|
37
|
-
|
38
|
-
return mem_read_handler(address_32);
|
42
|
+
return zemu_memory_peek(address);
|
39
43
|
}
|
40
44
|
|
41
45
|
void zemu_memory_write(void * context, zuint16 address, zuint8 value)
|
42
46
|
{
|
43
|
-
|
44
|
-
mem_write_handler(address_32, value);
|
47
|
+
zemu_memory_poke(address, value);
|
45
48
|
}
|
46
49
|
|
47
50
|
zuint8 zemu_memory_peek(zuint16 address)
|
48
51
|
{
|
49
52
|
zuint32 address_32 = address;
|
53
|
+
|
54
|
+
<% devices.each do |d| %>
|
55
|
+
<% next if d.memory.nil? %>
|
56
|
+
if ((address_32 >= <%= d.memory.address %>) && (address_32 < <%= d.memory.address + d.memory.size%>))
|
57
|
+
return <%= d.memory.access_read %>[address_32 - <%= d.memory.address %>];
|
58
|
+
<% end %>
|
59
|
+
|
50
60
|
return mem_read_handler(address_32);
|
51
61
|
}
|
52
62
|
|
53
63
|
void zemu_memory_poke(zuint16 address, zuint8 value)
|
54
64
|
{
|
55
65
|
zuint32 address_32 = address;
|
66
|
+
|
67
|
+
<% devices.each do |d| %>
|
68
|
+
<% next if d.memory.nil? %>
|
69
|
+
if ((address_32 >= <%= d.memory.address %>) && (address_32 < <%= d.memory.address + d.memory.size%>))
|
70
|
+
<%= d.memory.access_write %>[address_32 - <%= d.memory.address %>] = value;
|
71
|
+
<% end %>
|
72
|
+
|
56
73
|
mem_write_handler(address_32, value);
|
57
74
|
}
|
58
75
|
|
@@ -91,9 +108,12 @@ void zemu_io_out(void * context, zuint16 port, zuint8 value)
|
|
91
108
|
io_write_handler((zuint8)port, value);
|
92
109
|
}
|
93
110
|
|
94
|
-
void zemu_io_clock(Z80 * instance)
|
111
|
+
void zemu_io_clock(Z80 * instance, zusize cycles)
|
95
112
|
{
|
96
|
-
zuint8 bus_state = io_clock_handler();
|
113
|
+
zuint8 bus_state = io_clock_handler(cycles);
|
97
114
|
|
98
115
|
if (bus_state & 0x01) zemu_io_nmi(instance);
|
116
|
+
|
117
|
+
if (bus_state & 0x02) zemu_io_int_on(instance);
|
118
|
+
else zemu_io_int_off(instance);
|
99
119
|
}
|
data/src/bus.h.erb
CHANGED
@@ -26,7 +26,7 @@ typedef void io_write_handler_t(zuint8, zuint8);
|
|
26
26
|
typedef zuint8 io_read_handler_t(zuint8);
|
27
27
|
|
28
28
|
/* Type of a function to handle a clock cycle for a peripheral. */
|
29
|
-
typedef zuint8 io_clock_handler_t(
|
29
|
+
typedef zuint8 io_clock_handler_t(zusize);
|
30
30
|
|
31
31
|
void zemu_set_mem_write_handler(mem_write_handler_t * h);
|
32
32
|
void zemu_set_mem_read_handler(mem_read_handler_t * h);
|
@@ -48,6 +48,6 @@ zusize zemu_io_serial_buffer_size(void);
|
|
48
48
|
zuint8 zemu_io_in(void * context, zuint16 port);
|
49
49
|
void zemu_io_out(void * context, zuint16 port, zuint8 value);
|
50
50
|
void zemu_io_nmi(Z80 * instance);
|
51
|
-
void zemu_io_clock(Z80 * instance);
|
51
|
+
void zemu_io_clock(Z80 * instance, zusize cycles);
|
52
52
|
|
53
53
|
#endif
|
data/src/debug.c
CHANGED
@@ -8,7 +8,7 @@ zusize zemu_debug_step(Z80 * instance)
|
|
8
8
|
zusize cycles = z80_run(instance, 1);
|
9
9
|
|
10
10
|
/* Execute the per-cycle behaviour of the peripheral devices. */
|
11
|
-
|
11
|
+
zemu_io_clock(instance, cycles);
|
12
12
|
|
13
13
|
return cycles;
|
14
14
|
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zemu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.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:
|
11
|
+
date: 2022-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
Zemu is a gem which allows the user to configure a Z80-based system
|