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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 79900374a7a271fbfc1333290b4078e8e587ebbec09034c9cde520623459fcd4
4
- data.tar.gz: 6282f2f1d04cf8188d84a058100d7db55d9305c3c577104455ead68195d17adb
3
+ metadata.gz: dbdac0f92af987d3202f5975f4bc119c448a0c3356c5bfa069ff659977e6c150
4
+ data.tar.gz: 55becc89a08a3aee55baddf0dd172bd702dbfe2ab328340578f5ed313a983aa3
5
5
  SHA512:
6
- metadata.gz: 30dcdd1f3b79b767816a23a28c34107fb91ea48a8bbff445fa90228d4c2fe83276ba4f5ffb727f881e1864edeb40cbbefe1f637cb2e07a2793c0bbbab3ecf7fb
7
- data.tar.gz: cf6f4519a152b2f1372c581222a35bcd33a32934607bdeae878330216551a26f712a7837ddf5a9935389c4c6a1b2aeba600c2e0c178fc083bb5dce616542cf19
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
- return @wrapper.zemu_debug_continue(@instance, run_cycles)
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
- @wrapper.zemu_debug_set_breakpoint(address)
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 @wrapper.zemu_debug_halted
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 @wrapper.zemu_debug_break
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 :zemu_debug_continue, [:pointer, :int64], :uint64
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
- RunState zemu_debug_state = UNDEFINED;
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
- return cycles;
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
- void zemu_debug_set_breakpoint(zuint16 address)
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
- zboolean zemu_debug_halted(void)
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
- return (zemu_debug_state == HALTED);
63
+ halted = state;
112
64
  }
113
65
 
114
- zboolean zemu_debug_break(void)
66
+ zboolean zemu_debug_halted(void)
115
67
  {
116
- return (zemu_debug_state == BREAK);
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
- void zemu_debug_set_breakpoint(zuint16 address);
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.2.2
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-02-29 00:00:00.000000000 Z
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