zemu 0.4.1 → 1.0.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.
data/lib/zemu/debug.rb CHANGED
@@ -5,22 +5,55 @@ module Zemu
5
5
  # Loads a map file at the given path, and returns a hash of address => Symbol
6
6
  # for the symbols defined within.
7
7
  def self.load_map(path)
8
- symbols = {}
8
+ symbols = []
9
9
 
10
10
  File.open(path, "r") do |f|
11
11
  f.each_line do |l|
12
12
  s = Symbol.parse(l)
13
13
 
14
- if symbols[s.address].nil?
15
- symbols[s.address] = []
14
+ symbols << s
15
+ end
16
+ end
17
+
18
+ return Symbols.new(symbols)
19
+ end
20
+
21
+ # Contains a set of symbols.
22
+ # Allows for various lookup operations.
23
+ class Symbols
24
+ # Constructor.
25
+ def initialize(syms)
26
+ @syms = []
27
+ syms.each do |s|
28
+ @syms << s
29
+ end
30
+ end
31
+
32
+ # Access all symbols with a given address.
33
+ def [](address)
34
+ at_address = []
35
+ @syms.each do |s|
36
+ if s.address == address
37
+ at_address << s
16
38
  end
39
+ end
40
+
41
+ return at_address.sort_by(&:label)
42
+ end
17
43
 
18
- symbols[s.address] << s
19
- symbols[s.address].sort_by!(&:label)
44
+ # Find a symbol with a given name.
45
+ def find_by_name(name)
46
+ @syms.each do |s|
47
+ return s if s.label == name
20
48
  end
49
+
50
+ return nil
21
51
  end
22
52
 
23
- return symbols
53
+ # Get symbols as a hash.
54
+ def hash
55
+ return @syms
56
+ end
24
57
  end
25
58
 
26
59
  # Represents a symbol definition, of the form `label = address`.
data/lib/zemu/instance.rb CHANGED
@@ -60,7 +60,15 @@ module Zemu
60
60
  UNDEFINED = -1
61
61
  end
62
62
 
63
+
64
+
63
65
  def initialize(configuration)
66
+ @devices = configuration.devices
67
+
68
+ # Methods defined by bus devices that we make
69
+ # accessible to the user.
70
+ @device_methods = []
71
+
64
72
  @clock = configuration.clock_speed
65
73
  @serial_delay = configuration.serial_delay
66
74
 
@@ -69,6 +77,70 @@ module Zemu
69
77
  @serial = []
70
78
 
71
79
  @instance = @wrapper.zemu_init
80
+
81
+ # Declare handlers.
82
+ # Memory write handler.
83
+ @mem_write = Proc.new do |addr, value|
84
+ @devices.each do |d|
85
+ d.mem_write(addr, value)
86
+ end
87
+ end
88
+
89
+ # Memory read handler.
90
+ @mem_read = Proc.new do |addr|
91
+ r = 0
92
+ @devices.each do |d|
93
+ v = d.mem_read(addr)
94
+ unless v.nil?
95
+ r = v
96
+ end
97
+ end
98
+
99
+ r
100
+ end
101
+
102
+ # IO write handler.
103
+ @io_write = Proc.new do |port, value|
104
+ @devices.each do |d|
105
+ d.io_write(port, value)
106
+ end
107
+ end
108
+
109
+ # IO read handler.
110
+ @io_read = Proc.new do |port|
111
+ r = 0
112
+ @devices.each do |d|
113
+ v = d.io_read(port)
114
+ unless v.nil?
115
+ r = v
116
+ end
117
+ end
118
+
119
+ r
120
+ end
121
+
122
+ # IO read handler.
123
+ @io_clock = Proc.new do
124
+ @devices.each do |d|
125
+ d.clock()
126
+ end
127
+
128
+ bus_state = 0
129
+
130
+ if @devices.any? { |d| d.nmi? }
131
+ bus_state |= 1
132
+ end
133
+
134
+ bus_state
135
+ end
136
+
137
+ # Attach handlers.
138
+ @wrapper.zemu_set_mem_write_handler(@mem_write)
139
+ @wrapper.zemu_set_mem_read_handler(@mem_read)
140
+ @wrapper.zemu_set_io_write_handler(@io_write)
141
+ @wrapper.zemu_set_io_read_handler(@io_read)
142
+ @wrapper.zemu_set_io_clock_handler(@io_clock)
143
+
72
144
  @wrapper.zemu_power_on(@instance)
73
145
  @wrapper.zemu_reset(@instance)
74
146
 
@@ -77,6 +149,18 @@ module Zemu
77
149
  @breakpoints = {}
78
150
  end
79
151
 
152
+ # Returns the device with the given name, or nil
153
+ # if no such device exists.
154
+ def device(name)
155
+ @devices.each do |d|
156
+ if d.name == name
157
+ return d
158
+ end
159
+ end
160
+
161
+ nil
162
+ end
163
+
80
164
  # Returns the clock speed of this instance in Hz.
81
165
  def clock_speed
82
166
  return @clock
@@ -253,6 +337,21 @@ module Zemu
253
337
 
254
338
  wrapper.ffi_lib [File.join(configuration.output_directory, "#{configuration.name}.so")]
255
339
 
340
+ # Handler types for handling bus accesses.
341
+ wrapper.callback :mem_write_handler, [:uint32, :uint8], :void
342
+ wrapper.callback :mem_read_handler, [:uint32], :uint8
343
+
344
+ wrapper.callback :io_write_handler, [:uint8, :uint8], :void
345
+ wrapper.callback :io_read_handler, [:uint8], :uint8
346
+ wrapper.callback :io_clock_handler, [:void], :uint8
347
+
348
+ wrapper.attach_function :zemu_set_mem_write_handler, [:mem_write_handler], :void
349
+ wrapper.attach_function :zemu_set_mem_read_handler, [:mem_read_handler], :void
350
+
351
+ wrapper.attach_function :zemu_set_io_write_handler, [:io_write_handler], :void
352
+ wrapper.attach_function :zemu_set_io_read_handler, [:io_read_handler], :void
353
+ wrapper.attach_function :zemu_set_io_clock_handler, [:io_clock_handler], :void
354
+
256
355
  wrapper.attach_function :zemu_init, [], :pointer
257
356
  wrapper.attach_function :zemu_free, [:pointer], :void
258
357
 
@@ -271,15 +370,25 @@ module Zemu
271
370
  wrapper.attach_function :zemu_debug_get_memory, [:uint16], :uint8
272
371
  wrapper.attach_function :zemu_debug_set_memory, [:uint16, :uint8], :void
273
372
 
274
- configuration.io.each do |device|
373
+ configuration.devices.each do |device|
275
374
  device.functions.each do |f|
276
375
  wrapper.attach_function(f["name"].to_sym, f["args"], f["return"])
376
+ @device_methods << f["name"].to_sym
277
377
  end
278
378
  end
279
379
 
280
380
  return wrapper
281
381
  end
282
382
 
383
+ # Redirects calls to I/O FFI functions.
384
+ def method_missing(method, *args)
385
+ if @device_methods.include? method
386
+ return @wrapper.send(method)
387
+ end
388
+
389
+ super
390
+ end
391
+
283
392
  private :make_wrapper
284
393
  end
285
394
  end
@@ -11,6 +11,7 @@ module Zemu
11
11
  # to the emulator window.
12
12
  def initialize(instance, options = {})
13
13
  @print_serial = options[:print_serial]
14
+ @trace = []
14
15
 
15
16
  @instance = instance
16
17
 
@@ -70,6 +71,9 @@ module Zemu
70
71
  elsif cmd[0] == "map"
71
72
  load_map(cmd[1])
72
73
 
74
+ elsif cmd[0] == "trace"
75
+ trace()
76
+
73
77
  elsif cmd[0] == "help"
74
78
  log "Available commands:"
75
79
  log " continue [<n>] - Continue execution for <n> cycles"
@@ -90,6 +94,14 @@ module Zemu
90
94
  close
91
95
  end
92
96
 
97
+ # Print trace for the emulator instance
98
+ # (last 200 addresses visited).
99
+ def trace
100
+ @trace.each do |t|
101
+ puts "%04x" % t
102
+ end
103
+ end
104
+
93
105
  # Outputs a table giving the current values of the instance's registers.
94
106
  # For the 16-bit registers (BC, DE, HL, IX, IY, SP, PC), attempts to identify the symbol
95
107
  # to which they point.
@@ -185,6 +197,9 @@ module Zemu
185
197
  cycles_left -= cycles_done
186
198
  actual_cycles += cycles_done
187
199
 
200
+ @trace << r16("PC")
201
+ @trace = @trace[1..] if @trace.size > 200
202
+
188
203
  # Get time after execution.
189
204
  ending = Time.now
190
205
 
@@ -199,6 +214,11 @@ module Zemu
199
214
  sleep(padding) unless padding < 0
200
215
  end
201
216
 
217
+ if (@instance.memory(0x200) != 0xf3)
218
+ log "Buffer overflow at #{r16("PC")}"
219
+ break
220
+ end
221
+
202
222
  # Have we hit a breakpoint or HALT instruction?
203
223
  if @instance.break?
204
224
  log "Hit breakpoint at #{r16("PC")}."
@@ -258,7 +278,7 @@ module Zemu
258
278
 
259
279
  syms = {}
260
280
  begin
261
- syms.merge! Debug.load_map(path.to_s)
281
+ syms.merge! Debug.load_map(path.to_s).hash
262
282
  rescue ArgumentError => e
263
283
  log "Error loading map file: #{e.message}"
264
284
  syms.clear
data/lib/zemu.rb CHANGED
@@ -62,10 +62,12 @@ module Zemu
62
62
  SRC = File.join(__dir__, "..", "src")
63
63
 
64
64
  # Build and start an emulator according to the given configuration.
65
+ # Returns the emulator instance.
65
66
  #
66
67
  # @param [Zemu::Config] configuration The configuration for which an emulator will be generated.
67
- def Zemu::start(configuration)
68
- build(configuration)
68
+ # @param user_defines Any user-defined preprocessor macros.
69
+ def Zemu::start(configuration, user_defines={})
70
+ build(configuration, user_defines)
69
71
 
70
72
  return Instance.new(configuration)
71
73
  end
@@ -81,11 +83,11 @@ module Zemu
81
83
  end
82
84
 
83
85
  # Builds a library according to the given configuration.
86
+ # Returns true if the build is a success, false (build failed) or nil (compiler not found) otherwise.
84
87
  #
85
88
  # @param [Zemu::Config] configuration The configuration for which an emulator will be generated.
86
- #
87
- # @returns true if the build is a success, false (build failed) or nil (compiler not found) otherwise.
88
- def Zemu::build(configuration)
89
+ # @param user_defines Any user-defined preprocessor macros.
90
+ def Zemu::build(configuration, user_defines={})
89
91
  # Create the output directory unless it already exists.
90
92
  unless Dir.exist? configuration.output_directory
91
93
  Dir.mkdir configuration.output_directory
@@ -109,13 +111,15 @@ module Zemu
109
111
 
110
112
  inputs_str = inputs.map { |i| File.join(SRC, i) }.join(" ")
111
113
 
112
- inputs_str += " " + File.join(autogen, "memory.c") + " " + File.join(autogen, "io.c")
114
+ inputs_str += " " + File.join(autogen, "bus.c")
113
115
 
114
116
  defines = {
115
117
  "CPU_Z80_STATIC" => 1,
116
118
  "CPU_Z80_USE_LOCAL_HEADER" => 1
117
119
  }
118
120
 
121
+ defines.merge! user_defines
122
+
119
123
  defines_str = defines.map { |d, v| "-D#{d}=#{v}" }.join(" ")
120
124
 
121
125
  includes = [
@@ -139,32 +143,13 @@ module Zemu
139
143
  #
140
144
  # @param [Zemu::Config] configuration The configuration for which an emulator will be generated.
141
145
  def Zemu::generate(configuration)
142
- generate_memory(configuration)
143
- generate_io(configuration)
144
- end
145
-
146
- # Generates the memory.c and memory.h files for a given configuration.
147
- def Zemu::generate_memory(configuration)
148
- header_template = ERB.new File.read(File.join(SRC, "memory.h.erb"))
149
- source_template = ERB.new File.read(File.join(SRC, "memory.c.erb"))
150
-
151
- autogen = File.join(configuration.output_directory, "autogen_#{configuration.name}")
152
-
153
- unless Dir.exist? autogen
154
- Dir.mkdir autogen
155
- end
156
-
157
- File.write(File.join(autogen, "memory.h"),
158
- header_template.result(configuration.get_binding))
159
-
160
- File.write(File.join(autogen, "memory.c"),
161
- source_template.result(configuration.get_binding))
146
+ generate_bus(configuration)
162
147
  end
163
148
 
164
- # Generates the io.c and io.h files for a given configuration.
165
- def Zemu::generate_io(configuration)
166
- header_template = ERB.new File.read(File.join(SRC, "io.h.erb"))
167
- source_template = ERB.new File.read(File.join(SRC, "io.c.erb"))
149
+ # Generates the bus.c and bus.h files for a given configuration.
150
+ def Zemu::generate_bus(configuration)
151
+ header_template = ERB.new File.read(File.join(SRC, "bus.h.erb"))
152
+ source_template = ERB.new File.read(File.join(SRC, "bus.c.erb"))
168
153
 
169
154
  autogen = File.join(configuration.output_directory, "autogen_#{configuration.name}")
170
155
 
@@ -172,10 +157,10 @@ module Zemu
172
157
  Dir.mkdir autogen
173
158
  end
174
159
 
175
- File.write(File.join(autogen, "io.h"),
160
+ File.write(File.join(autogen, "bus.h"),
176
161
  header_template.result(configuration.get_binding))
177
162
 
178
- File.write(File.join(autogen, "io.c"),
163
+ File.write(File.join(autogen, "bus.c"),
179
164
  source_template.result(configuration.get_binding))
180
165
  end
181
166
  end
data/src/bus.c.erb ADDED
@@ -0,0 +1,99 @@
1
+ #include "bus.h"
2
+
3
+ mem_write_handler_t * mem_write_handler;
4
+ mem_read_handler_t * mem_read_handler;
5
+
6
+ io_write_handler_t * io_write_handler;
7
+ io_read_handler_t * io_read_handler;
8
+ io_clock_handler_t * io_clock_handler;
9
+
10
+ void zemu_set_mem_write_handler(mem_write_handler_t * h)
11
+ {
12
+ mem_write_handler = h;
13
+ }
14
+
15
+ void zemu_set_mem_read_handler(mem_read_handler_t * h)
16
+ {
17
+ mem_read_handler = h;
18
+ }
19
+
20
+ void zemu_set_io_write_handler(io_write_handler_t * h)
21
+ {
22
+ io_write_handler = h;
23
+ }
24
+
25
+ void zemu_set_io_read_handler(io_read_handler_t * h)
26
+ {
27
+ io_read_handler = h;
28
+ }
29
+
30
+ void zemu_set_io_clock_handler(io_clock_handler_t * h)
31
+ {
32
+ io_clock_handler = h;
33
+ }
34
+
35
+ zuint8 zemu_memory_read(void * context, zuint16 address)
36
+ {
37
+ zuint32 address_32 = address;
38
+ return mem_read_handler(address_32);
39
+ }
40
+
41
+ void zemu_memory_write(void * context, zuint16 address, zuint8 value)
42
+ {
43
+ zuint32 address_32 = address;
44
+ mem_write_handler(address_32, value);
45
+ }
46
+
47
+ zuint8 zemu_memory_peek(zuint16 address)
48
+ {
49
+ zuint32 address_32 = address;
50
+ return mem_read_handler(address_32);
51
+ }
52
+
53
+ void zemu_memory_poke(zuint16 address, zuint8 value)
54
+ {
55
+ zuint32 address_32 = address;
56
+ mem_write_handler(address_32, value);
57
+ }
58
+
59
+ void zemu_io_nmi(Z80 * instance)
60
+ {
61
+ z80_nmi(instance);
62
+ }
63
+
64
+ void zemu_io_int_on(Z80 * instance)
65
+ {
66
+ z80_int(instance, TRUE);
67
+ }
68
+
69
+ void zemu_io_int_off(Z80 * instance)
70
+ {
71
+ z80_int(instance, FALSE);
72
+ }
73
+
74
+ zuint8 zemu_io_in(void * context, zuint16 port)
75
+ {
76
+ /* Z80 IO ports occupy the lower half of the address bus.
77
+ * We cannot assume that the top half is valid.
78
+ */
79
+ port &= 0x00FF;
80
+
81
+ return io_read_handler((zuint8)port);
82
+ }
83
+
84
+ void zemu_io_out(void * context, zuint16 port, zuint8 value)
85
+ {
86
+ /* Z80 IO ports occupy the lower half of the address bus.
87
+ * We cannot assume that the top half is valid.
88
+ */
89
+ port &= 0x00FF;
90
+
91
+ io_write_handler((zuint8)port, value);
92
+ }
93
+
94
+ void zemu_io_clock(Z80 * instance)
95
+ {
96
+ zuint8 bus_state = io_clock_handler();
97
+
98
+ if (bus_state & 0x01) zemu_io_nmi(instance);
99
+ }
data/src/bus.h.erb ADDED
@@ -0,0 +1,53 @@
1
+ #ifndef _ZEMU_IO_H
2
+ #define _ZEMU_IO_H
3
+
4
+ #include "emulation/CPU/Z80.h"
5
+
6
+ #ifndef ZEMU_IO_SERIAL_BUFFER_SIZE
7
+ #define ZEMU_IO_SERIAL_BUFFER_SIZE 256
8
+ #endif
9
+
10
+ typedef struct {
11
+ zuint8 buffer[ZEMU_IO_SERIAL_BUFFER_SIZE];
12
+ unsigned int head;
13
+ unsigned int tail;
14
+ } SerialBuffer;
15
+
16
+ /* Type of a function writing to a memory address. */
17
+ typedef void mem_write_handler_t(zuint32, zuint8);
18
+
19
+ /* Type of a function reading from a memory address. */
20
+ typedef zuint8 mem_read_handler_t(zuint32);
21
+
22
+ /* Type of a function writing to an IO port. */
23
+ typedef void io_write_handler_t(zuint8, zuint8);
24
+
25
+ /* Type of a function reading from an IO port. */
26
+ typedef zuint8 io_read_handler_t(zuint8);
27
+
28
+ /* Type of a function to handle a clock cycle for a peripheral. */
29
+ typedef zuint8 io_clock_handler_t(void);
30
+
31
+ void zemu_set_mem_write_handler(mem_write_handler_t * h);
32
+ void zemu_set_mem_read_handler(mem_read_handler_t * h);
33
+
34
+ void zemu_set_io_write_handler(io_write_handler_t * h);
35
+ void zemu_set_io_read_handler(io_read_handler_t * h);
36
+ void zemu_set_io_clock_handler(io_clock_handler_t * h);
37
+
38
+ zuint8 zemu_memory_read(void * context, zuint16 address);
39
+ void zemu_memory_write(void * context, zuint16 address, zuint8 value);
40
+
41
+ zuint8 zemu_memory_peek(zuint16 address);
42
+ void zemu_memory_poke(zuint16 address, zuint8 value);
43
+
44
+ void zemu_io_serial_master_puts(zuint8 val);
45
+ zuint8 zemu_io_serial_master_gets(void);
46
+ zusize zemu_io_serial_buffer_size(void);
47
+
48
+ zuint8 zemu_io_in(void * context, zuint16 port);
49
+ void zemu_io_out(void * context, zuint16 port, zuint8 value);
50
+ void zemu_io_nmi(Z80 * instance);
51
+ void zemu_io_clock(Z80 * instance);
52
+
53
+ #endif
data/src/debug.h CHANGED
@@ -2,8 +2,7 @@
2
2
 
3
3
  #include <stdio.h>
4
4
 
5
- #include "memory.h"
6
- #include "io.h"
5
+ #include "bus.h"
7
6
 
8
7
  zusize zemu_debug_step(Z80 * instance);
9
8
 
data/src/main.c CHANGED
@@ -4,8 +4,7 @@
4
4
 
5
5
  #include "debug.h"
6
6
 
7
- #include "memory.h"
8
- #include "io.h"
7
+ #include "bus.h"
9
8
  #include "interrupt.h"
10
9
 
11
10
  /* Allocate and initialize a Z80 instance.
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.4.1
4
+ version: 1.0.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-11-18 00:00:00.000000000 Z
11
+ date: 2021-12-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
@@ -32,6 +32,8 @@ files:
32
32
  - lib/zemu/debug.rb
33
33
  - lib/zemu/instance.rb
34
34
  - lib/zemu/interactive.rb
35
+ - src/bus.c.erb
36
+ - src/bus.h.erb
35
37
  - src/debug.c
36
38
  - src/debug.h
37
39
  - src/external/Z/API/Z/ABIs/generic/allocator.h
@@ -310,11 +312,7 @@ files:
310
312
  - src/external/z80/sources/Z80.c
311
313
  - src/interrupt.c
312
314
  - src/interrupt.h
313
- - src/io.c.erb
314
- - src/io.h.erb
315
315
  - src/main.c
316
- - src/memory.c.erb
317
- - src/memory.h.erb
318
316
  homepage: https://github.com/jayvalentine/zemu
319
317
  licenses:
320
318
  - GPL-3.0
data/src/io.c.erb DELETED
@@ -1,52 +0,0 @@
1
- #include "io.h"
2
-
3
- <% io.each do |device| %>
4
- <%= device.setup %>
5
- <% end %>
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
-
22
- zuint8 zemu_io_in(void * context, zuint16 port)
23
- {
24
- /* Z80 IO ports occupy the lower half of the address bus.
25
- * We cannot assume that the top half is valid.
26
- */
27
- port &= 0x00FF;
28
-
29
- <% io.each do |device| %>
30
- <%= device.read %>
31
- <% end %>
32
- return 0;
33
- }
34
-
35
- void zemu_io_out(void * context, zuint16 port, zuint8 value)
36
- {
37
- /* Z80 IO ports occupy the lower half of the address bus.
38
- * We cannot assume that the top half is valid.
39
- */
40
- port &= 0x00FF;
41
-
42
- <% io.each do |device| %>
43
- <%= device.write %>
44
- <% end %>
45
- }
46
-
47
- void zemu_io_clock(Z80 * instance)
48
- {
49
- <% io.each do |device| %>
50
- <%= device.clock %>
51
- <% end %>
52
- }
data/src/io.h.erb DELETED
@@ -1,25 +0,0 @@
1
- #ifndef _ZEMU_IO_H
2
- #define _ZEMU_IO_H
3
-
4
- #include "emulation/CPU/Z80.h"
5
-
6
- #ifndef ZEMU_IO_SERIAL_BUFFER_SIZE
7
- #define ZEMU_IO_SERIAL_BUFFER_SIZE 256
8
- #endif
9
-
10
- typedef struct {
11
- zuint8 buffer[ZEMU_IO_SERIAL_BUFFER_SIZE];
12
- unsigned int head;
13
- unsigned int tail;
14
- } SerialBuffer;
15
-
16
- void zemu_io_serial_master_puts(zuint8 val);
17
- zuint8 zemu_io_serial_master_gets(void);
18
- zusize zemu_io_serial_buffer_size(void);
19
-
20
- zuint8 zemu_io_in(void * context, zuint16 port);
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