zemu 0.2.2 → 0.3.9

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: 3278a3fa602a14489f1bfe77bee8d51781be503fac50dba4334dd400f492b5ca
4
+ data.tar.gz: 0dc3644e09613e7449bbc2dd3167f4823fca647cd0b5df94b35a0d39768a8b26
5
5
  SHA512:
6
- metadata.gz: 30dcdd1f3b79b767816a23a28c34107fb91ea48a8bbff445fa90228d4c2fe83276ba4f5ffb727f881e1864edeb40cbbefe1f637cb2e07a2793c0bbbab3ecf7fb
7
- data.tar.gz: cf6f4519a152b2f1372c581222a35bcd33a32934607bdeae878330216551a26f712a7837ddf5a9935389c4c6a1b2aeba600c2e0c178fc083bb5dce616542cf19
6
+ metadata.gz: bb87eef723a62c64742f06ec85bf7f5c976103c6e5b8678b5efec665b49ea3242efe59238f1ea28808c7ef3e597471ec050cda5f0152f3c74fa900f88df294ea
7
+ data.tar.gz: 29de4f2e78acae5a24a443595e3b683acb3fd3a6d00ec84991c1126e3e7b07ce35078de8721347ec84926ee99fd2bd21912b1ef768fa868c0d1cc75ae966db02
@@ -128,7 +128,7 @@ module Zemu
128
128
 
129
129
  includes_str += " -I" + autogen
130
130
 
131
- command = "#{compiler} -Werror -Wno-unknown-warning-option -fPIC -shared -Wl,-undefined -Wl,dynamic_lookup #{includes_str} #{defines_str} -o #{output} #{inputs_str}"
131
+ command = "#{compiler} -O2 -Werror -Wno-unknown-warning-option -fPIC -shared -Wl,-undefined -Wl,dynamic_lookup #{includes_str} #{defines_str} -o #{output} #{inputs_str}"
132
132
 
133
133
  # Run the compiler and generate a library.
134
134
  return system(command)
@@ -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
@@ -421,7 +490,7 @@ module Zemu
421
490
 
422
491
  # Parameters accessible by this configuration object.
423
492
  def params
424
- return %w(name compiler output_directory clock_speed)
493
+ return %w(name compiler output_directory clock_speed serial_delay)
425
494
  end
426
495
 
427
496
  # Initial value for parameters of this configuration object.
@@ -429,7 +498,8 @@ module Zemu
429
498
  return {
430
499
  "compiler" => "clang",
431
500
  "output_directory" => "bin",
432
- "clock_speed" => 0
501
+ "clock_speed" => 0,
502
+ "serial_delay" => 0
433
503
  }
434
504
  end
435
505
 
@@ -37,8 +37,24 @@ 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
57
+ @serial_delay = configuration.serial_delay
42
58
 
43
59
  @wrapper = make_wrapper(configuration)
44
60
 
@@ -47,6 +63,10 @@ module Zemu
47
63
  @instance = @wrapper.zemu_init
48
64
  @wrapper.zemu_power_on(@instance)
49
65
  @wrapper.zemu_reset(@instance)
66
+
67
+ @state = RunState::UNDEFINED
68
+
69
+ @breakpoints = {}
50
70
  end
51
71
 
52
72
  # Returns the clock speed of this instance in Hz.
@@ -54,6 +74,11 @@ module Zemu
54
74
  return @clock
55
75
  end
56
76
 
77
+ # Returns the delay between characters on the serial port for this instance in seconds.
78
+ def serial_delay
79
+ return @serial_delay
80
+ end
81
+
57
82
  # Returns a hash containing current values of the emulated
58
83
  # machine's registers. All names are as those given in the Z80
59
84
  # reference manual.
@@ -124,7 +149,32 @@ module Zemu
124
149
  #
125
150
  # Returns the number of cycles executed.
126
151
  def continue(run_cycles=-1)
127
- return @wrapper.zemu_debug_continue(@instance, run_cycles)
152
+ # Return immediately if we're HALTED.
153
+ return if @state == RunState::HALTED
154
+
155
+ cycles_executed = 0
156
+
157
+ @state = RunState::RUNNING
158
+
159
+ # Run as long as:
160
+ # We haven't hit a breakpoint
161
+ # We haven't halted
162
+ # We haven't hit the number of cycles we've been told to execute for.
163
+ while (run_cycles == -1 || cycles_executed < run_cycles) && (@state == RunState::RUNNING)
164
+ cycles_executed += @wrapper.zemu_debug_step(@instance)
165
+
166
+ pc = @wrapper.zemu_debug_pc(@instance)
167
+
168
+ # If the PC is now pointing to one of our breakpoints,
169
+ # we're in the BREAK state.
170
+ if @breakpoints[pc]
171
+ @state = RunState::BREAK
172
+ elsif @wrapper.zemu_debug_halted()
173
+ @state = RunState::HALTED
174
+ end
175
+ end
176
+
177
+ return cycles_executed
128
178
  end
129
179
 
130
180
  # Set a breakpoint of the given type at the given address.
@@ -133,17 +183,26 @@ module Zemu
133
183
  # @param type The type of breakpoint:
134
184
  # * :program => Break when the program counter hits the address given.
135
185
  def break(address, type)
136
- @wrapper.zemu_debug_set_breakpoint(address)
186
+ @breakpoints[address] = true
187
+ end
188
+
189
+ # Remove a breakpoint of the given type at the given address.
190
+ # Does nothing if no breakpoint previously existed at that address.
191
+ #
192
+ # @param address The address of the breakpoint to be removed.
193
+ # @param type The type of breakpoint. See Instance#break.
194
+ def remove_break(address, type)
195
+ @breakpoints[address] = false
137
196
  end
138
197
 
139
198
  # Returns true if the CPU has halted, false otherwise.
140
199
  def halted?
141
- return @wrapper.zemu_debug_halted
200
+ return @state == RunState::HALTED
142
201
  end
143
202
 
144
203
  # Returns true if a breakpoint has been hit, false otherwise.
145
204
  def break?
146
- return @wrapper.zemu_debug_break
205
+ return @state == RunState::BREAK
147
206
  end
148
207
 
149
208
  # Powers off the emulated CPU and destroys this instance.
@@ -168,14 +227,12 @@ module Zemu
168
227
 
169
228
  wrapper.attach_function :zemu_reset, [:pointer], :void
170
229
 
171
- wrapper.attach_function :zemu_debug_continue, [:pointer, :int64], :uint64
230
+ wrapper.attach_function :zemu_debug_step, [:pointer], :uint64
172
231
 
173
232
  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
233
 
178
234
  wrapper.attach_function :zemu_debug_register, [:pointer, :uint16], :uint16
235
+ wrapper.attach_function :zemu_debug_pc, [:pointer], :uint16
179
236
 
180
237
  wrapper.attach_function :zemu_debug_get_memory, [:uint16], :uint8
181
238
 
@@ -114,13 +114,19 @@ module Zemu
114
114
  cycles_left = cycles
115
115
  actual_cycles = 0
116
116
 
117
+ serial_count = @instance.serial_delay
118
+
117
119
  while ((cycles == -1) || (cycles_left > 0))
118
120
  # Get time before execution.
119
121
  start = Time.now
120
122
 
121
123
  old_pc = r16("PC")
122
124
 
123
- process_serial
125
+ if (serial_count >= @instance.serial_delay)
126
+ process_serial
127
+ serial_count = 0
128
+ end
129
+
124
130
  cycles_done = @instance.continue(1)
125
131
  cycles_left -= cycles_done
126
132
  actual_cycles += cycles_done
@@ -132,7 +138,9 @@ module Zemu
132
138
  if @instance.clock_speed > 0
133
139
  elapsed = ending - start
134
140
 
135
- execution_time = actual_cycles * (1/@instance.clock_speed)
141
+ execution_time = cycles_done * (1.0/@instance.clock_speed)
142
+ serial_count += execution_time
143
+
136
144
  padding = execution_time - elapsed
137
145
  sleep(padding) unless padding < 0
138
146
  end
@@ -169,7 +177,7 @@ module Zemu
169
177
 
170
178
  (address.to_i(16)...address.to_i(16) + size.to_i(16)).each do |a|
171
179
  m = @instance.memory(a)
172
- if (m < 32)
180
+ if (m < 32 || m > 126)
173
181
  log "%04x: %02x ." % [a, m]
174
182
  else
175
183
  log ("%04x: %02x " % [a, m]) + m.chr("UTF-8")
@@ -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)
@@ -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);
@@ -4,6 +4,21 @@
4
4
  <%= device.setup %>
5
5
  <% end %>
6
6
 
7
+ void zemu_io_nmi(Z80 * instance)
8
+ {
9
+ z80_nmi(instance);
10
+ }
11
+
12
+ void zemu_io_int_on(Z80 * instance)
13
+ {
14
+ z80_int(instance, TRUE);
15
+ }
16
+
17
+ void zemu_io_int_off(Z80 * instance)
18
+ {
19
+ z80_int(instance, FALSE);
20
+ }
21
+
7
22
  zuint8 zemu_io_in(void * context, zuint16 port)
8
23
  {
9
24
  /* Z80 IO ports occupy the lower half of the address bus.
@@ -28,3 +43,10 @@ void zemu_io_out(void * context, zuint16 port, zuint8 value)
28
43
  <%= device.write %>
29
44
  <% end %>
30
45
  }
46
+
47
+ void zemu_io_clock(Z80 * instance)
48
+ {
49
+ <% io.each do |device| %>
50
+ <%= device.clock %>
51
+ <% end %>
52
+ }
@@ -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.9
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-08-26 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