zemu 1.0.0 → 1.1.0

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: 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