zemu 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|