zemu 0.4.1 → 1.0.0

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