zemu 1.0.0 → 1.1.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: 31940c64874b7aaeb203199df84fc7cdb7cedc8736728e147e2fda5557675d83
4
- data.tar.gz: 7a236b15b5075d866aaedaa2793b535e5c1bf7e3fb279c4a7b3702f93ec6e719
3
+ metadata.gz: 8e57309f08bc232fd44db53f5961029283fdffaaf0e01c9e8e2ea36807987b33
4
+ data.tar.gz: 493c60567914e637c50244dc16f9d4ba47d8eea7ed1b3c8abce442fa142264bf
5
5
  SHA512:
6
- metadata.gz: bd05cbd8eb7a42df7afa9f4c62957e138dcdef1b02c4c458ffdecf8156f64972664eaf2d54d48741d198dc9ee8b0a5f97352c1fcd7c8a4fa6736da50626a60fd
7
- data.tar.gz: ac3fdb4305fc84ae7766db21351321c9c4e7c3041c3a6549e9c416be03666ac4f56d8275248ae707429941f34a2077596aadf8e9fee45290300f20f7fe4baf7f
6
+ metadata.gz: 4746e81b7cc05441d12fe45c284955a4aae66a8877be77168a110bb08aada6f0bd6e1cf7ca5d1085e0a059d1d4e1d0ed8288f1aae72d159b0cb699342f021005
7
+ data.tar.gz: 667278d74f889f7d555807d548715fa110d6a8b9d0dd0aacd77c37806517218e7058b42c61bc6d744c063fd7869c490a12674308fbc401face274209320d6b91
data/lib/zemu/config.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'ostruct'
2
+
1
3
  module Zemu
2
4
  # Abstract configuration object.
3
5
  # All configuration objects should inherit from this.
@@ -89,10 +91,17 @@ module Zemu
89
91
  end
90
92
 
91
93
  @nmi = false
94
+ @interrupt = false
92
95
 
93
96
  super
94
97
  end
95
98
 
99
+ # Parameters used for generating C code to
100
+ # implement this bus device.
101
+ def memory
102
+ nil
103
+ end
104
+
96
105
  # Setup to be performed on initialising the emulator
97
106
  # instance.
98
107
  def when_setup
@@ -147,7 +156,7 @@ module Zemu
147
156
  # Deriving objects can use the nmi function
148
157
  # to set the state of the non-maskable interrupt
149
158
  # at each clock cycle.
150
- def clock
159
+ def clock(cycles)
151
160
  end
152
161
 
153
162
  # FFI functions provided by this device.
@@ -165,6 +174,16 @@ module Zemu
165
174
  @nmi
166
175
  end
167
176
 
177
+ # Sets state of INT for this device.
178
+ def interrupt(state)
179
+ @interrupt = state
180
+ end
181
+
182
+ # Gets state of INT for this device.
183
+ def interrupt?
184
+ @interrupt
185
+ end
186
+
168
187
  # Parameters for a bus device.
169
188
  def params
170
189
  %w(name)
@@ -208,6 +227,32 @@ module Zemu
208
227
  false
209
228
  end
210
229
 
230
+ # Parameters used for generating C code to
231
+ # implement this memory block.
232
+ def memory
233
+ contents_initializer = ""
234
+ @contents.each_slice(32) do |c|
235
+ contents_initializer += c.map { |b| b.to_s }.join(", ")
236
+ contents_initializer += ",\n"
237
+ end
238
+
239
+ m = OpenStruct.new
240
+
241
+ m.setup = <<eos
242
+ zuint8 memory_#{name}[#{size}] =
243
+ {
244
+ #{contents_initializer}
245
+ };
246
+ eos
247
+
248
+ m.address = address
249
+ m.size = size
250
+ m.access_read = "memory_#{name}"
251
+ m.access_write = "memory_#{name}"
252
+
253
+ m
254
+ end
255
+
211
256
  # Memory bus read handler.
212
257
  #
213
258
  # Handles read access via the memory bus to this device.
@@ -217,15 +262,6 @@ module Zemu
217
262
  # Returns the value read, or nil if no value
218
263
  # (e.g. if address falls outside range for this device).
219
264
  def mem_read(addr)
220
- # Return value in memory's contents if the address
221
- # falls within range.
222
- if (addr >= address) && (addr < (address + size))
223
- offset = addr - address
224
- return @contents[offset]
225
- end
226
-
227
- # Otherwise return nil - address does not correspond
228
- # to this memory block.
229
265
  nil
230
266
  end
231
267
 
@@ -236,12 +272,6 @@ module Zemu
236
272
  # @param addr The address being accessed.
237
273
  # @param value The value being written.
238
274
  def mem_write(addr, value)
239
- # If address falls within range, set value in
240
- # memory contents.
241
- if (addr >= address) && (addr < (address + size))
242
- offset = addr - address
243
- @contents[offset] = value
244
- end
245
275
  end
246
276
 
247
277
  # Valid parameters for this object.
@@ -673,10 +703,10 @@ module Zemu
673
703
  #
674
704
  # Handles a clock cycle for this device.
675
705
  # Sets NMI active if the count reaches 0.
676
- def clock
706
+ def clock(cycles)
677
707
  if @running == RUNNING
678
708
  if @count > 0
679
- @count -= 1
709
+ @count -= cycles
680
710
  else
681
711
  nmi(true)
682
712
  end
data/lib/zemu/instance.rb CHANGED
@@ -2,6 +2,17 @@ require 'ffi'
2
2
  require 'ostruct'
3
3
 
4
4
  module Zemu
5
+ # Exception raised when an error occurs in a Zemu::Instance.
6
+ class InstanceError < StandardError
7
+ # Constructor.
8
+ #
9
+ # @param msg The exception message
10
+ #
11
+ def initialize(msg="An error occurred with the Zemu::Instance")
12
+ super
13
+ end
14
+ end
15
+
5
16
  # Represents an instance of a Zemu emulator.
6
17
  #
7
18
  # Provides methods by which the state of the emulator can be observed
@@ -60,8 +71,6 @@ module Zemu
60
71
  UNDEFINED = -1
61
72
  end
62
73
 
63
-
64
-
65
74
  def initialize(configuration)
66
75
  @devices = configuration.devices
67
76
 
@@ -93,6 +102,7 @@ module Zemu
93
102
  v = d.mem_read(addr)
94
103
  unless v.nil?
95
104
  r = v
105
+ break
96
106
  end
97
107
  end
98
108
 
@@ -113,6 +123,7 @@ module Zemu
113
123
  v = d.io_read(port)
114
124
  unless v.nil?
115
125
  r = v
126
+ break
116
127
  end
117
128
  end
118
129
 
@@ -120,9 +131,9 @@ module Zemu
120
131
  end
121
132
 
122
133
  # IO read handler.
123
- @io_clock = Proc.new do
134
+ @io_clock = Proc.new do |cycles|
124
135
  @devices.each do |d|
125
- d.clock()
136
+ d.clock(cycles)
126
137
  end
127
138
 
128
139
  bus_state = 0
@@ -131,6 +142,10 @@ module Zemu
131
142
  bus_state |= 1
132
143
  end
133
144
 
145
+ if @devices.any? { |d| d.interrupt? }
146
+ bus_state |= 2
147
+ end
148
+
134
149
  bus_state
135
150
  end
136
151
 
@@ -147,6 +162,7 @@ module Zemu
147
162
  @state = RunState::UNDEFINED
148
163
 
149
164
  @breakpoints = {}
165
+ @tracepoints = {}
150
166
  end
151
167
 
152
168
  # Returns the device with the given name, or nil
@@ -221,7 +237,7 @@ module Zemu
221
237
  # emulated machine.
222
238
  def serial_puts(string)
223
239
  string.each_char do |c|
224
- @wrapper.zemu_io_serial_master_puts(c.ord)
240
+ device('serial').put_byte(c.ord)
225
241
  end
226
242
  end
227
243
 
@@ -237,14 +253,14 @@ module Zemu
237
253
  def serial_gets(count=nil)
238
254
  return_string = ""
239
255
 
240
- actual_count = @wrapper.zemu_io_serial_buffer_size()
256
+ actual_count = device('serial').transmitted_count()
241
257
 
242
258
  if count.nil? || actual_count < count
243
259
  count = actual_count
244
260
  end
245
261
 
246
262
  count.to_i.times do
247
- return_string += @wrapper.zemu_io_serial_master_gets().chr
263
+ return_string += device('serial').get_byte().chr
248
264
  end
249
265
 
250
266
  return return_string
@@ -283,6 +299,12 @@ module Zemu
283
299
 
284
300
  pc = @wrapper.zemu_debug_pc(@instance)
285
301
 
302
+ # If there's a tracepoint at this address,
303
+ # execute the associated proc.
304
+ unless @tracepoints[pc].nil?
305
+ @tracepoints[pc].call(self)
306
+ end
307
+
286
308
  # If the PC is now pointing to one of our breakpoints,
287
309
  # we're in the BREAK state.
288
310
  if @breakpoints[pc]
@@ -304,6 +326,26 @@ module Zemu
304
326
  @breakpoints[address] = true
305
327
  end
306
328
 
329
+ # Set a tracepoint to execute the given block at an address.
330
+ #
331
+ # @param address The address of the tracepoint
332
+ # @param block The block to execute at the tracepoint.
333
+ # The block takes the instance as a parameter.
334
+ #
335
+ # @example
336
+ # instance.trace(0x1234) do |i|
337
+ # puts "HL value: 04x" % i.registers["HL"]
338
+ # end
339
+ #
340
+ def trace(address, &block)
341
+ if block.arity != 1
342
+ raise InstanceError,
343
+ "Wrong arity for tracepoint - expected 1, got #{block.arity}"
344
+ end
345
+
346
+ @tracepoints[address] = block
347
+ end
348
+
307
349
  # Remove a breakpoint of the given type at the given address.
308
350
  # Does nothing if no breakpoint previously existed at that address.
309
351
  #
@@ -343,7 +385,7 @@ module Zemu
343
385
 
344
386
  wrapper.callback :io_write_handler, [:uint8, :uint8], :void
345
387
  wrapper.callback :io_read_handler, [:uint8], :uint8
346
- wrapper.callback :io_clock_handler, [:void], :uint8
388
+ wrapper.callback :io_clock_handler, [:uint64], :uint8
347
389
 
348
390
  wrapper.attach_function :zemu_set_mem_write_handler, [:mem_write_handler], :void
349
391
  wrapper.attach_function :zemu_set_mem_read_handler, [:mem_read_handler], :void
@@ -183,9 +183,6 @@ module Zemu
183
183
  serial_count = @instance.serial_delay.to_f
184
184
 
185
185
  while ((cycles == -1) || (cycles_left > 0))
186
- # Get time before execution.
187
- start = Time.now
188
-
189
186
  old_pc = r16("PC")
190
187
 
191
188
  if (serial_count >= @instance.serial_delay)
@@ -197,26 +194,10 @@ module Zemu
197
194
  cycles_left -= cycles_done
198
195
  actual_cycles += cycles_done
199
196
 
200
- @trace << r16("PC")
201
- @trace = @trace[1..] if @trace.size > 200
202
-
203
- # Get time after execution.
204
- ending = Time.now
205
-
206
197
  # Get elapsed time and calculate padding time to match clock speed.
207
198
  if @instance.clock_speed > 0
208
- elapsed = ending - start
209
-
210
199
  execution_time = cycles_done * (1.0/@instance.clock_speed)
211
200
  serial_count += execution_time
212
-
213
- padding = execution_time - elapsed
214
- sleep(padding) unless padding < 0
215
- end
216
-
217
- if (@instance.memory(0x200) != 0xf3)
218
- log "Buffer overflow at #{r16("PC")}"
219
- break
220
201
  end
221
202
 
222
203
  # Have we hit a breakpoint or HALT instruction?
data/src/bus.c.erb CHANGED
@@ -7,6 +7,11 @@ io_write_handler_t * io_write_handler;
7
7
  io_read_handler_t * io_read_handler;
8
8
  io_clock_handler_t * io_clock_handler;
9
9
 
10
+ <% devices.each do |d| %>
11
+ <% next if d.memory.nil? %>
12
+ <%= d.memory.setup %>
13
+ <% end %>
14
+
10
15
  void zemu_set_mem_write_handler(mem_write_handler_t * h)
11
16
  {
12
17
  mem_write_handler = h;
@@ -34,25 +39,37 @@ void zemu_set_io_clock_handler(io_clock_handler_t * h)
34
39
 
35
40
  zuint8 zemu_memory_read(void * context, zuint16 address)
36
41
  {
37
- zuint32 address_32 = address;
38
- return mem_read_handler(address_32);
42
+ return zemu_memory_peek(address);
39
43
  }
40
44
 
41
45
  void zemu_memory_write(void * context, zuint16 address, zuint8 value)
42
46
  {
43
- zuint32 address_32 = address;
44
- mem_write_handler(address_32, value);
47
+ zemu_memory_poke(address, value);
45
48
  }
46
49
 
47
50
  zuint8 zemu_memory_peek(zuint16 address)
48
51
  {
49
52
  zuint32 address_32 = address;
53
+
54
+ <% devices.each do |d| %>
55
+ <% next if d.memory.nil? %>
56
+ if ((address_32 >= <%= d.memory.address %>) && (address_32 < <%= d.memory.address + d.memory.size%>))
57
+ return <%= d.memory.access_read %>[address_32 - <%= d.memory.address %>];
58
+ <% end %>
59
+
50
60
  return mem_read_handler(address_32);
51
61
  }
52
62
 
53
63
  void zemu_memory_poke(zuint16 address, zuint8 value)
54
64
  {
55
65
  zuint32 address_32 = address;
66
+
67
+ <% devices.each do |d| %>
68
+ <% next if d.memory.nil? %>
69
+ if ((address_32 >= <%= d.memory.address %>) && (address_32 < <%= d.memory.address + d.memory.size%>))
70
+ <%= d.memory.access_write %>[address_32 - <%= d.memory.address %>] = value;
71
+ <% end %>
72
+
56
73
  mem_write_handler(address_32, value);
57
74
  }
58
75
 
@@ -91,9 +108,12 @@ void zemu_io_out(void * context, zuint16 port, zuint8 value)
91
108
  io_write_handler((zuint8)port, value);
92
109
  }
93
110
 
94
- void zemu_io_clock(Z80 * instance)
111
+ void zemu_io_clock(Z80 * instance, zusize cycles)
95
112
  {
96
- zuint8 bus_state = io_clock_handler();
113
+ zuint8 bus_state = io_clock_handler(cycles);
97
114
 
98
115
  if (bus_state & 0x01) zemu_io_nmi(instance);
116
+
117
+ if (bus_state & 0x02) zemu_io_int_on(instance);
118
+ else zemu_io_int_off(instance);
99
119
  }
data/src/bus.h.erb CHANGED
@@ -26,7 +26,7 @@ typedef void io_write_handler_t(zuint8, zuint8);
26
26
  typedef zuint8 io_read_handler_t(zuint8);
27
27
 
28
28
  /* Type of a function to handle a clock cycle for a peripheral. */
29
- typedef zuint8 io_clock_handler_t(void);
29
+ typedef zuint8 io_clock_handler_t(zusize);
30
30
 
31
31
  void zemu_set_mem_write_handler(mem_write_handler_t * h);
32
32
  void zemu_set_mem_read_handler(mem_read_handler_t * h);
@@ -48,6 +48,6 @@ zusize zemu_io_serial_buffer_size(void);
48
48
  zuint8 zemu_io_in(void * context, zuint16 port);
49
49
  void zemu_io_out(void * context, zuint16 port, zuint8 value);
50
50
  void zemu_io_nmi(Z80 * instance);
51
- void zemu_io_clock(Z80 * instance);
51
+ void zemu_io_clock(Z80 * instance, zusize cycles);
52
52
 
53
53
  #endif
data/src/debug.c CHANGED
@@ -8,7 +8,7 @@ zusize zemu_debug_step(Z80 * instance)
8
8
  zusize cycles = z80_run(instance, 1);
9
9
 
10
10
  /* Execute the per-cycle behaviour of the peripheral devices. */
11
- for (zusize i = 0; i < cycles; i++) zemu_io_clock(instance);
11
+ zemu_io_clock(instance, cycles);
12
12
 
13
13
  return cycles;
14
14
  }
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: 1.0.0
4
+ version: 1.1.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: 2021-12-30 00:00:00.000000000 Z
11
+ date: 2022-01-30 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