zemu 0.2.2 → 0.3.9

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