zemu 0.2.2 → 0.3.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 +77 -8
- data/lib/zemu/instance.rb +59 -8
- data/src/debug.c +13 -61
- data/src/debug.h +5 -17
- data/src/io.c.erb +12 -0
- data/src/io.h.erb +7 -0
- 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: dbdac0f92af987d3202f5975f4bc119c448a0c3356c5bfa069ff659977e6c150
|
4
|
+
data.tar.gz: 55becc89a08a3aee55baddf0dd172bd702dbfe2ab328340578f5ed313a983aa3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a41f637706dc296b456da80be43af7cb1aeb7e7e34700c3d2e731f9da3698a16cb18f7b622b8a82054eb3c789d451bea6cbd73f145f9a76e6fd8c5850c9fbede
|
7
|
+
data.tar.gz: 8174742e9ab6adc69f284bd8a33763d03074daafaa638f385fc4ff3cc27db2ebbeed85d23999b77439a2340722882bc1048626a8b4bc5eda1768263cdecdbff4
|
data/lib/zemu/config.rb
CHANGED
@@ -228,6 +228,10 @@ module Zemu
|
|
228
228
|
end
|
229
229
|
|
230
230
|
@ports = []
|
231
|
+
@setup_block = nil
|
232
|
+
@read_block = nil
|
233
|
+
@write_block = nil
|
234
|
+
@clock_block = nil
|
231
235
|
|
232
236
|
super
|
233
237
|
end
|
@@ -269,28 +273,46 @@ module Zemu
|
|
269
273
|
@write_block = block
|
270
274
|
end
|
271
275
|
|
276
|
+
# Defines the per-cycle behaviour of this IO device.
|
277
|
+
#
|
278
|
+
# Expects a block, the return value of which is a string
|
279
|
+
# defining the behaviour of the IO device on each system clock cycle.
|
280
|
+
# Care must be taken to ensure that this functionality does not conflict with that of
|
281
|
+
# any other IO devices.
|
282
|
+
#
|
283
|
+
# The block will be instance-evaluated at build-time, so it is possible to use
|
284
|
+
# instance variables of the IO device.
|
285
|
+
def when_clock(&block)
|
286
|
+
@clock_block = block
|
287
|
+
end
|
288
|
+
|
272
289
|
# Evaluates the when_setup block of this IO device and returns the resulting string.
|
273
290
|
def setup
|
274
|
-
instance_eval(&@setup_block)
|
291
|
+
return instance_eval(&@setup_block) unless @setup_block.nil?
|
292
|
+
return ""
|
275
293
|
end
|
276
294
|
|
277
295
|
# Evaluates the when_read block of this IO device and returns the resulting string.
|
278
296
|
def read
|
279
|
-
instance_eval(&@read_block)
|
297
|
+
return instance_eval(&@read_block) unless @read_block.nil?
|
298
|
+
return ""
|
280
299
|
end
|
281
300
|
|
282
301
|
# Evaluates the when_write block of this IO device and returns the resulting string.
|
283
302
|
def write
|
284
|
-
instance_eval(&@write_block)
|
303
|
+
return instance_eval(&@write_block) unless @write_block.nil?
|
304
|
+
return ""
|
305
|
+
end
|
306
|
+
|
307
|
+
# Evaluates the when_clock block of this IO device and returns the resulting string.
|
308
|
+
def clock
|
309
|
+
return instance_eval(&@clock_block) unless @clock_block.nil?
|
310
|
+
return ""
|
285
311
|
end
|
286
312
|
|
287
313
|
# Defines FFI API which will be available to the instance wrapper if this IO device is used.
|
288
314
|
def functions
|
289
|
-
[
|
290
|
-
{"name" => "zemu_io_#{name}_master_puts".to_sym, "args" => [:uint8], "return" => :void},
|
291
|
-
{"name" => "zemu_io_#{name}_master_gets".to_sym, "args" => [], "return" => :uint8},
|
292
|
-
{"name" => "zemu_io_#{name}_buffer_size".to_sym, "args" => [], "return" => :uint64}
|
293
|
-
]
|
315
|
+
[]
|
294
316
|
end
|
295
317
|
|
296
318
|
# Valid parameters for this object.
|
@@ -401,6 +423,15 @@ module Zemu
|
|
401
423
|
end
|
402
424
|
end
|
403
425
|
|
426
|
+
# Defines FFI API which will be available to the instance wrapper if this IO device is used.
|
427
|
+
def functions
|
428
|
+
[
|
429
|
+
{"name" => "zemu_io_#{name}_master_puts".to_sym, "args" => [:uint8], "return" => :void},
|
430
|
+
{"name" => "zemu_io_#{name}_master_gets".to_sym, "args" => [], "return" => :uint8},
|
431
|
+
{"name" => "zemu_io_#{name}_buffer_size".to_sym, "args" => [], "return" => :uint64}
|
432
|
+
]
|
433
|
+
end
|
434
|
+
|
404
435
|
# Valid parameters for a SerialPort, along with those
|
405
436
|
# defined in [Zemu::Config::IOPort].
|
406
437
|
def params
|
@@ -408,6 +439,44 @@ module Zemu
|
|
408
439
|
end
|
409
440
|
end
|
410
441
|
|
442
|
+
# Non-Maskable Interrupt Timer
|
443
|
+
#
|
444
|
+
# Represents a timer device, the period of which can be controlled
|
445
|
+
# by the CPU through an IO port. The timer generates an NMI once this
|
446
|
+
# period has expired. The timer can be reset via a control port.
|
447
|
+
class Timer < IOPort
|
448
|
+
def initialize
|
449
|
+
super
|
450
|
+
|
451
|
+
when_setup do
|
452
|
+
"zuint8 io_#{name}_count;\n" +
|
453
|
+
"zuint8 io_#{name}_running = 0;\n"
|
454
|
+
end
|
455
|
+
|
456
|
+
when_read do
|
457
|
+
end
|
458
|
+
|
459
|
+
when_write do
|
460
|
+
"if (port == #{count_port}) io_#{name}_count = value;\n" +
|
461
|
+
"else if (port == #{control_port}) io_#{name}_running = value;\n"
|
462
|
+
end
|
463
|
+
|
464
|
+
when_clock do
|
465
|
+
"if (io_#{name}_running)\n" +
|
466
|
+
"{\n" +
|
467
|
+
" if (io_#{name}_count > 0) io_#{name}_count--;\n" +
|
468
|
+
" else zemu_io_nmi(instance);\n" +
|
469
|
+
"}\n"
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
# Valid parameters for a Timer, along with those defined in
|
474
|
+
# [Zemu::Config::IOPort].
|
475
|
+
def params
|
476
|
+
super + %w(count_port control_port)
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
411
480
|
# Gets a binding for this object.
|
412
481
|
def get_binding
|
413
482
|
return binding
|
data/lib/zemu/instance.rb
CHANGED
@@ -37,6 +37,21 @@ module Zemu
|
|
37
37
|
"L'" => 19
|
38
38
|
}
|
39
39
|
|
40
|
+
# States that the emulated machine can be in.
|
41
|
+
class RunState
|
42
|
+
# Currently executing an instruction.
|
43
|
+
RUNNING = 0
|
44
|
+
|
45
|
+
# Executed a HALT instruction in the previous cycle.
|
46
|
+
HALTED = 1
|
47
|
+
|
48
|
+
# Hit a breakpoint in the previous cycle.
|
49
|
+
BREAK = 2
|
50
|
+
|
51
|
+
# Undefined. Emulated machine has not yet reached a well-defined state.
|
52
|
+
UNDEFINED = -1
|
53
|
+
end
|
54
|
+
|
40
55
|
def initialize(configuration)
|
41
56
|
@clock = configuration.clock_speed
|
42
57
|
|
@@ -47,6 +62,10 @@ module Zemu
|
|
47
62
|
@instance = @wrapper.zemu_init
|
48
63
|
@wrapper.zemu_power_on(@instance)
|
49
64
|
@wrapper.zemu_reset(@instance)
|
65
|
+
|
66
|
+
@state = RunState::UNDEFINED
|
67
|
+
|
68
|
+
@breakpoints = []
|
50
69
|
end
|
51
70
|
|
52
71
|
# Returns the clock speed of this instance in Hz.
|
@@ -124,7 +143,32 @@ module Zemu
|
|
124
143
|
#
|
125
144
|
# Returns the number of cycles executed.
|
126
145
|
def continue(run_cycles=-1)
|
127
|
-
|
146
|
+
# Return immediately if we're HALTED.
|
147
|
+
return if @state == RunState::HALTED
|
148
|
+
|
149
|
+
cycles_executed = 0
|
150
|
+
|
151
|
+
@state = RunState::RUNNING
|
152
|
+
|
153
|
+
# Run as long as:
|
154
|
+
# We haven't hit a breakpoint
|
155
|
+
# We haven't halted
|
156
|
+
# We haven't hit the number of cycles we've been told to execute for.
|
157
|
+
while (run_cycles == -1 || cycles_executed < run_cycles) && (@state == RunState::RUNNING)
|
158
|
+
cycles_executed += @wrapper.zemu_debug_step(@instance)
|
159
|
+
|
160
|
+
pc = @wrapper.zemu_debug_pc(@instance)
|
161
|
+
|
162
|
+
# If the PC is now pointing to one of our breakpoints,
|
163
|
+
# we're in the BREAK state.
|
164
|
+
if (@breakpoints.select { |b| b == pc }.size) > 0
|
165
|
+
@state = RunState::BREAK
|
166
|
+
elsif @wrapper.zemu_debug_halted()
|
167
|
+
@state = RunState::HALTED
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
return cycles_executed
|
128
172
|
end
|
129
173
|
|
130
174
|
# Set a breakpoint of the given type at the given address.
|
@@ -133,17 +177,26 @@ module Zemu
|
|
133
177
|
# @param type The type of breakpoint:
|
134
178
|
# * :program => Break when the program counter hits the address given.
|
135
179
|
def break(address, type)
|
136
|
-
@
|
180
|
+
@breakpoints << address
|
181
|
+
end
|
182
|
+
|
183
|
+
# Remove a breakpoint of the given type at the given address.
|
184
|
+
# Does nothing if no breakpoint previously existed at that address.
|
185
|
+
#
|
186
|
+
# @param address The address of the breakpoint to be removed.
|
187
|
+
# @param type The type of breakpoint. See Instance#break.
|
188
|
+
def remove_break(address, type)
|
189
|
+
@breakpoints.reject! { |b| b == address }
|
137
190
|
end
|
138
191
|
|
139
192
|
# Returns true if the CPU has halted, false otherwise.
|
140
193
|
def halted?
|
141
|
-
return @
|
194
|
+
return @state == RunState::HALTED
|
142
195
|
end
|
143
196
|
|
144
197
|
# Returns true if a breakpoint has been hit, false otherwise.
|
145
198
|
def break?
|
146
|
-
return @
|
199
|
+
return @state == RunState::BREAK
|
147
200
|
end
|
148
201
|
|
149
202
|
# Powers off the emulated CPU and destroys this instance.
|
@@ -168,14 +221,12 @@ module Zemu
|
|
168
221
|
|
169
222
|
wrapper.attach_function :zemu_reset, [:pointer], :void
|
170
223
|
|
171
|
-
wrapper.attach_function :
|
224
|
+
wrapper.attach_function :zemu_debug_step, [:pointer], :uint64
|
172
225
|
|
173
226
|
wrapper.attach_function :zemu_debug_halted, [], :bool
|
174
|
-
wrapper.attach_function :zemu_debug_break, [], :bool
|
175
|
-
|
176
|
-
wrapper.attach_function :zemu_debug_set_breakpoint, [:uint16], :void
|
177
227
|
|
178
228
|
wrapper.attach_function :zemu_debug_register, [:pointer, :uint16], :uint16
|
229
|
+
wrapper.attach_function :zemu_debug_pc, [:pointer], :uint16
|
179
230
|
|
180
231
|
wrapper.attach_function :zemu_debug_get_memory, [:uint16], :uint8
|
181
232
|
|
data/src/debug.c
CHANGED
@@ -1,69 +1,16 @@
|
|
1
1
|
#include "debug.h"
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
/* Currently, the number of breakpoints is defined statically.
|
6
|
-
* Perhaps in future there will be an unlimited number.
|
7
|
-
*/
|
8
|
-
zuint16 breakpoints[ZEMU_DEBUG_MAX_BREAKPOINTS];
|
9
|
-
unsigned int breakpoint_count = 0;
|
10
|
-
|
11
|
-
zusize zemu_debug_continue(Z80 * instance, zinteger run_cycles)
|
12
|
-
{
|
13
|
-
/* Return if we've halted. */
|
14
|
-
if (zemu_debug_state == HALTED) return 0;
|
15
|
-
|
16
|
-
zusize cycles = 0;
|
17
|
-
|
18
|
-
zemu_debug_state = RUNNING;
|
19
|
-
|
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))
|
25
|
-
{
|
26
|
-
cycles += zemu_debug_step(instance);
|
27
|
-
|
28
|
-
/* See if the Program Counter now matches any address
|
29
|
-
* in the breakpoint array.
|
30
|
-
*/
|
31
|
-
for (unsigned int b = 0; b < breakpoint_count; b++)
|
32
|
-
{
|
33
|
-
if (instance->state.pc == breakpoints[b])
|
34
|
-
{
|
35
|
-
zemu_debug_state = BREAK;
|
36
|
-
}
|
37
|
-
}
|
38
|
-
}
|
39
|
-
|
40
|
-
return cycles;
|
41
|
-
}
|
3
|
+
zboolean halted = FALSE;
|
42
4
|
|
43
5
|
zusize zemu_debug_step(Z80 * instance)
|
44
6
|
{
|
45
7
|
/* Will run for at least one cycle. */
|
46
8
|
zusize cycles = z80_run(instance, 1);
|
47
9
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
void zemu_debug_halt(void * context, zboolean state)
|
52
|
-
{
|
53
|
-
if (state)
|
54
|
-
{
|
55
|
-
zemu_debug_state = HALTED;
|
56
|
-
}
|
57
|
-
else
|
58
|
-
{
|
59
|
-
zemu_debug_state = RUNNING;
|
60
|
-
}
|
61
|
-
}
|
10
|
+
/* Execute the per-cycle behaviour of the peripheral devices. */
|
11
|
+
for (zusize i = 0; i < cycles; i++) zemu_io_clock(instance);
|
62
12
|
|
63
|
-
|
64
|
-
{
|
65
|
-
breakpoints[breakpoint_count] = address;
|
66
|
-
breakpoint_count++;
|
13
|
+
return cycles;
|
67
14
|
}
|
68
15
|
|
69
16
|
zuint16 zemu_debug_register(Z80 * instance, zuint16 r)
|
@@ -106,14 +53,19 @@ zuint16 zemu_debug_register(Z80 * instance, zuint16 r)
|
|
106
53
|
}
|
107
54
|
}
|
108
55
|
|
109
|
-
|
56
|
+
zuint16 zemu_debug_pc(Z80 * instance)
|
57
|
+
{
|
58
|
+
return instance->state.pc;
|
59
|
+
}
|
60
|
+
|
61
|
+
void zemu_debug_halt(void * context, zboolean state)
|
110
62
|
{
|
111
|
-
|
63
|
+
halted = state;
|
112
64
|
}
|
113
65
|
|
114
|
-
zboolean
|
66
|
+
zboolean zemu_debug_halted(void)
|
115
67
|
{
|
116
|
-
return (
|
68
|
+
return (halted);
|
117
69
|
}
|
118
70
|
|
119
71
|
zuint8 zemu_debug_get_memory(zuint16 address)
|
data/src/debug.h
CHANGED
@@ -3,28 +3,16 @@
|
|
3
3
|
#include <stdio.h>
|
4
4
|
|
5
5
|
#include "memory.h"
|
6
|
-
|
7
|
-
/* Define number of breakpoints, if not done so already. */
|
8
|
-
#ifndef ZEMU_DEBUG_MAX_BREAKPOINTS
|
9
|
-
#define ZEMU_DEBUG_MAX_BREAKPOINTS 256
|
10
|
-
#endif
|
11
|
-
|
12
|
-
typedef enum RunState
|
13
|
-
{
|
14
|
-
RUNNING,
|
15
|
-
HALTED,
|
16
|
-
BREAK,
|
17
|
-
UNDEFINED
|
18
|
-
} RunState;
|
19
|
-
|
20
|
-
zusize zemu_debug_continue(Z80 * instance, zinteger run_cycles);
|
6
|
+
#include "io.h"
|
21
7
|
|
22
8
|
zusize zemu_debug_step(Z80 * instance);
|
23
9
|
|
24
10
|
void zemu_debug_halt(void * context, zboolean state);
|
25
11
|
|
26
12
|
zboolean zemu_debug_halted(void);
|
27
|
-
|
28
|
-
|
13
|
+
zboolean zemu_debug_break(void);
|
14
|
+
zboolean zemu_debug_running(void);
|
29
15
|
|
30
16
|
zuint16 zemu_debug_register(Z80 * instance, zuint16 r);
|
17
|
+
|
18
|
+
zuint16 zemu_debug_pc(Z80 * instance);
|
data/src/io.c.erb
CHANGED
@@ -28,3 +28,15 @@ void zemu_io_out(void * context, zuint16 port, zuint8 value)
|
|
28
28
|
<%= device.write %>
|
29
29
|
<% end %>
|
30
30
|
}
|
31
|
+
|
32
|
+
void zemu_io_nmi(Z80 * instance)
|
33
|
+
{
|
34
|
+
z80_nmi(instance);
|
35
|
+
}
|
36
|
+
|
37
|
+
void zemu_io_clock(Z80 * instance)
|
38
|
+
{
|
39
|
+
<% io.each do |device| %>
|
40
|
+
<%= device.clock %>
|
41
|
+
<% end %>
|
42
|
+
}
|
data/src/io.h.erb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
#ifndef _ZEMU_IO_H
|
2
|
+
#define _ZEMU_IO_H
|
3
|
+
|
1
4
|
#include "emulation/CPU/Z80.h"
|
2
5
|
|
3
6
|
#ifndef ZEMU_IO_SERIAL_BUFFER_SIZE
|
@@ -16,3 +19,7 @@ zusize zemu_io_serial_buffer_size(void);
|
|
16
19
|
|
17
20
|
zuint8 zemu_io_in(void * context, zuint16 port);
|
18
21
|
void zemu_io_out(void * context, zuint16 port, zuint8 value);
|
22
|
+
void zemu_io_nmi(Z80 * instance);
|
23
|
+
void zemu_io_clock(Z80 * instance);
|
24
|
+
|
25
|
+
#endif
|
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: 0.
|
4
|
+
version: 0.3.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-03-05 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
|