zemu 0.4.0 → 0.6.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 +4 -4
- data/lib/zemu/config.rb +273 -294
- data/lib/zemu/debug.rb +39 -6
- data/lib/zemu/instance.rb +40 -1
- data/lib/zemu/interactive.rb +30 -4
- data/lib/zemu.rb +19 -34
- data/src/bus.c.erb +88 -0
- data/src/{io.h.erb → bus.h.erb} +7 -0
- data/src/debug.c +5 -0
- data/src/debug.h +1 -2
- data/src/main.c +1 -2
- metadata +4 -6
- data/src/io.c.erb +0 -52
- data/src/memory.c.erb +0 -43
- data/src/memory.h.erb +0 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 62fdf5d376fa7ea32170f7f2ae62af17f3e96f8714313af513aabf86ef56fc71
|
|
4
|
+
data.tar.gz: 3adcc264bc22f519fff6bb0604051058b198ef3a200c3c5b118f1de1796d84ce
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4bd2cbaf5099cd0874a5d59b519aaf7371e0c3b537fc7c57d70cb82f9003c751362dbed28aa1d5187736932f8d498746d95d1a404f1b43ff3cee571a1248c23a
|
|
7
|
+
data.tar.gz: 9a320b4fbaddf08a24ba5ef58452ab0750d9fec1476bc8b389d43fc2b623364ab82cd037e61560828740a3e01adfa7647093eefad14d72ee5f6ec0152beb11fe
|
data/lib/zemu/config.rb
CHANGED
|
@@ -75,10 +75,83 @@ module Zemu
|
|
|
75
75
|
# @param [String] compiler The path to the compiler to be used for compiling the emulator executable.
|
|
76
76
|
#
|
|
77
77
|
class Config < ConfigObject
|
|
78
|
+
# Bus Device.
|
|
79
|
+
#
|
|
80
|
+
# Represents a device connected to the I/O
|
|
81
|
+
# or memory buses, or both.
|
|
82
|
+
class BusDevice < ConfigObject
|
|
83
|
+
# Constructor.
|
|
84
|
+
#
|
|
85
|
+
# This object should not be constructed directly.
|
|
86
|
+
def initialize
|
|
87
|
+
if self.class == Zemu::Config::BusDevice
|
|
88
|
+
raise NotImplementedError, "Cannot construct an instance of the abstract class Zemu::Config::BusDevice."
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
super
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Setup to be performed on initialising the emulator
|
|
95
|
+
# instance.
|
|
96
|
+
def when_setup
|
|
97
|
+
""
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Memory bus write handler.
|
|
101
|
+
#
|
|
102
|
+
# Defines C code generated for handling memory
|
|
103
|
+
# writes for this device.
|
|
104
|
+
def when_mem_write
|
|
105
|
+
""
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Memory bus read handler.
|
|
109
|
+
#
|
|
110
|
+
# Defines C code generated for handling memory
|
|
111
|
+
# reads for this device.
|
|
112
|
+
def when_mem_read
|
|
113
|
+
""
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# IO bus write handler.
|
|
117
|
+
#
|
|
118
|
+
# Defines C code generated for handling IO
|
|
119
|
+
# writes for this device.
|
|
120
|
+
def when_io_write
|
|
121
|
+
""
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# IO bus read handler.
|
|
125
|
+
#
|
|
126
|
+
# Defines C code generated for handling IO
|
|
127
|
+
# reads for this device.
|
|
128
|
+
def when_io_read
|
|
129
|
+
""
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Clock handler.
|
|
133
|
+
#
|
|
134
|
+
# Defines C code which executes for every
|
|
135
|
+
# clock cycle.
|
|
136
|
+
def when_clock
|
|
137
|
+
""
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# FFI functions provided by this device.
|
|
141
|
+
def functions
|
|
142
|
+
[]
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Parameters for a bus device.
|
|
146
|
+
def params
|
|
147
|
+
%w(name)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
78
151
|
# Memory object.
|
|
79
152
|
#
|
|
80
153
|
# This is an abstract class from which all other memory objects inherit.
|
|
81
|
-
class Memory <
|
|
154
|
+
class Memory < BusDevice
|
|
82
155
|
# Constructor.
|
|
83
156
|
#
|
|
84
157
|
# Do not use, as this is an abstract class. Use one of the subclasses instead.
|
|
@@ -107,15 +180,50 @@ module Zemu
|
|
|
107
180
|
end
|
|
108
181
|
end
|
|
109
182
|
|
|
110
|
-
#
|
|
183
|
+
# Is this memory read-only?
|
|
111
184
|
def readonly?
|
|
112
|
-
|
|
185
|
+
false
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Defines generated C to declare this memory block.
|
|
189
|
+
def when_setup
|
|
190
|
+
init_array = []
|
|
191
|
+
contents.each_with_index do |b, i|
|
|
192
|
+
init_array << ((i % 16 == 0) ? "\n " : "") + ("0x%02x, " % b)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
<<-eos
|
|
196
|
+
/* Initialization memory block "#{name}" */
|
|
197
|
+
#{if self.readonly? then "const" else "" end} zuint8 zemu_memory_block_#{name}[0x#{size.to_s(16)}] =
|
|
198
|
+
{#{init_array.join("")}
|
|
199
|
+
};
|
|
200
|
+
eos
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Defines generated C to handle reading this memory block.
|
|
204
|
+
def when_mem_read
|
|
205
|
+
<<-eos
|
|
206
|
+
if (address_32 >= 0x#{address.to_s(16)} && address_32 < 0x#{(address + size).to_s(16)})
|
|
207
|
+
{
|
|
208
|
+
return zemu_memory_block_#{name}[address_32 - 0x#{address.to_s(16)}];
|
|
209
|
+
}
|
|
210
|
+
eos
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Defines generated C to handle writing to this memory block.
|
|
214
|
+
def when_mem_write
|
|
215
|
+
<<-eos
|
|
216
|
+
if (address_32 >= 0x#{address.to_s(16)} && address_32 < 0x#{(address + size).to_s(16)})
|
|
217
|
+
{
|
|
218
|
+
zemu_memory_block_#{name}[address_32 - 0x#{address.to_s(16)}] = value;
|
|
219
|
+
}
|
|
220
|
+
eos
|
|
113
221
|
end
|
|
114
222
|
|
|
115
223
|
# Valid parameters for this object.
|
|
116
224
|
# Should be extended by subclasses but NOT REPLACED.
|
|
117
225
|
def params
|
|
118
|
-
|
|
226
|
+
super + %w(address size)
|
|
119
227
|
end
|
|
120
228
|
|
|
121
229
|
# Reads the contents of a file in binary format and
|
|
@@ -155,8 +263,17 @@ module Zemu
|
|
|
155
263
|
super
|
|
156
264
|
end
|
|
157
265
|
|
|
266
|
+
# Is this memory block readonly?
|
|
158
267
|
def readonly?
|
|
159
|
-
|
|
268
|
+
true
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Defines generated C to handle writing to this
|
|
272
|
+
# memory block. Because this block is read-only,
|
|
273
|
+
# no code is generated to handle writes.
|
|
274
|
+
def when_mem_write
|
|
275
|
+
# Cannot write to read-only memory.
|
|
276
|
+
""
|
|
160
277
|
end
|
|
161
278
|
end
|
|
162
279
|
|
|
@@ -165,169 +282,13 @@ module Zemu
|
|
|
165
282
|
# Represents a block of memory which can be read and written.
|
|
166
283
|
class RAM < Memory
|
|
167
284
|
end
|
|
168
|
-
|
|
169
|
-
# Input/Output Port object
|
|
170
|
-
#
|
|
171
|
-
# Represents an input/output device assigned to one or more ports.
|
|
172
|
-
#
|
|
173
|
-
# This is an abstract class and cannot be instantiated directly.
|
|
174
|
-
# The when_setup, when_read, and when_write methods can be used to define
|
|
175
|
-
# the behaviour of a subclass.
|
|
176
|
-
#
|
|
177
|
-
# @example
|
|
178
|
-
# class MyIODevice < IOPort
|
|
179
|
-
# # Extend the parameters of the object so we can define a port.
|
|
180
|
-
# def params
|
|
181
|
-
# super + "port"
|
|
182
|
-
# end
|
|
183
|
-
#
|
|
184
|
-
# def initialize
|
|
185
|
-
# super
|
|
186
|
-
#
|
|
187
|
-
# # Define the setup for the IO device.
|
|
188
|
-
# # This is some global C code that ends up in "io.c".
|
|
189
|
-
# # Parameters can be used here, as the block is instance-evaluated.
|
|
190
|
-
# when_setup do
|
|
191
|
-
# %Q(zuint8 #{name}_value = 42;)
|
|
192
|
-
# end
|
|
193
|
-
#
|
|
194
|
-
# # Define the logic when reading from an IO port.
|
|
195
|
-
# # The C variable "port" takes the value of the 8-bit port
|
|
196
|
-
# # address being read from, and should be used to identify
|
|
197
|
-
# # if this IO device is the one being used.
|
|
198
|
-
# when_read do
|
|
199
|
-
# %Q(if (port == #{port}) return #{name}_value;)
|
|
200
|
-
# end
|
|
201
|
-
#
|
|
202
|
-
# # Define the logic when writing to the IO port.
|
|
203
|
-
# # Similar to #when_read, but we have access to an extra
|
|
204
|
-
# # C variable, "value". This is the value being written
|
|
205
|
-
# # to the IO port.
|
|
206
|
-
# when_write do
|
|
207
|
-
# %Q(if (port == #{port}) #{name}_value = value;)
|
|
208
|
-
# end
|
|
209
|
-
# end
|
|
210
|
-
# end
|
|
211
|
-
#
|
|
212
|
-
# # The subclass can now be declared as below:
|
|
213
|
-
# device = MyIODevice.new do
|
|
214
|
-
# name "myDevice"
|
|
215
|
-
# port 11
|
|
216
|
-
# end
|
|
217
|
-
#
|
|
218
|
-
#
|
|
219
|
-
class IOPort < ConfigObject
|
|
220
|
-
attr_reader :io_type
|
|
221
|
-
|
|
222
|
-
# Constructor.
|
|
223
|
-
#
|
|
224
|
-
# Do not use, as this is an abstract class. Use one of the subclasses instead.
|
|
225
|
-
def initialize
|
|
226
|
-
if self.class == Zemu::Config::IOPort
|
|
227
|
-
raise NotImplementedError, "Cannot construct an instance of the abstract class Zemu::Config::IOPort."
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
@ports = []
|
|
231
|
-
@setup_block = nil
|
|
232
|
-
@read_block = nil
|
|
233
|
-
@write_block = nil
|
|
234
|
-
@clock_block = nil
|
|
235
|
-
|
|
236
|
-
super
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
# Defines the setup behaviour of this IO device.
|
|
240
|
-
#
|
|
241
|
-
# Expects a block, the return value of which is a string
|
|
242
|
-
# containing all data and function declarations required by this IO device.
|
|
243
|
-
#
|
|
244
|
-
# The block will be instance-evaluated at build-time, so it is possible to use
|
|
245
|
-
# instance variables of the IO device.
|
|
246
|
-
def when_setup(&block)
|
|
247
|
-
@setup_block = block
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
# Defines the read behaviour of this IO device.
|
|
251
|
-
#
|
|
252
|
-
# Expects a block, the return value of which is a string
|
|
253
|
-
# containing the behaviour of this IO device when a value is read from the IO bus.
|
|
254
|
-
# Care must be taken to ensure that this functionality does not conflict with that of
|
|
255
|
-
# any other IO devices.
|
|
256
|
-
#
|
|
257
|
-
# The block will be instance-evaluated at build-time, so it is possible to use
|
|
258
|
-
# instance variables of the IO device.
|
|
259
|
-
def when_read(&block)
|
|
260
|
-
@read_block = block
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
# Defines the write behaviour of this IO device.
|
|
264
|
-
#
|
|
265
|
-
# Expects a block, the return value of which is a string
|
|
266
|
-
# containing the behaviour of this IO device when a value is written to the IO bus.
|
|
267
|
-
# Care must be taken to ensure that this functionality does not conflict with that of
|
|
268
|
-
# any other IO devices.
|
|
269
|
-
#
|
|
270
|
-
# The block will be instance-evaluated at build-time, so it is possible to use
|
|
271
|
-
# instance variables of the IO device.
|
|
272
|
-
def when_write(&block)
|
|
273
|
-
@write_block = block
|
|
274
|
-
end
|
|
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
|
-
|
|
289
|
-
# Evaluates the when_setup block of this IO device and returns the resulting string.
|
|
290
|
-
def setup
|
|
291
|
-
return instance_eval(&@setup_block) unless @setup_block.nil?
|
|
292
|
-
return ""
|
|
293
|
-
end
|
|
294
|
-
|
|
295
|
-
# Evaluates the when_read block of this IO device and returns the resulting string.
|
|
296
|
-
def read
|
|
297
|
-
return instance_eval(&@read_block) unless @read_block.nil?
|
|
298
|
-
return ""
|
|
299
|
-
end
|
|
300
|
-
|
|
301
|
-
# Evaluates the when_write block of this IO device and returns the resulting string.
|
|
302
|
-
def write
|
|
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 ""
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
# Defines FFI API which will be available to the instance wrapper if this IO device is used.
|
|
314
|
-
def functions
|
|
315
|
-
[]
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
# Valid parameters for this object.
|
|
319
|
-
# Should be extended by subclasses but NOT REPLACED.
|
|
320
|
-
def params
|
|
321
|
-
%w(name)
|
|
322
|
-
end
|
|
323
|
-
end
|
|
324
285
|
|
|
325
286
|
# Serial Input/Output object
|
|
326
287
|
#
|
|
327
288
|
# Represents a serial connection between the emulated CPU
|
|
328
289
|
# and the host machine, with input and output mapped to Z80 I/O
|
|
329
290
|
# ports.
|
|
330
|
-
class SerialPort <
|
|
291
|
+
class SerialPort < BusDevice
|
|
331
292
|
# Constructor.
|
|
332
293
|
#
|
|
333
294
|
# Takes a block in which the parameters of the serial port
|
|
@@ -347,80 +308,84 @@ module Zemu
|
|
|
347
308
|
#
|
|
348
309
|
def initialize
|
|
349
310
|
super
|
|
311
|
+
end
|
|
350
312
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
313
|
+
# Defines generated C to declare the serial device.
|
|
314
|
+
def when_setup
|
|
315
|
+
"SerialBuffer io_#{name}_buffer_master = { .head = 0, .tail = 0 };\n" +
|
|
316
|
+
"SerialBuffer io_#{name}_buffer_slave = { .head = 0, .tail = 0 };\n" +
|
|
317
|
+
"\n" +
|
|
318
|
+
"zusize zemu_io_#{name}_buffer_size(void)\n" +
|
|
319
|
+
"{\n" +
|
|
320
|
+
" zusize start = io_#{name}_buffer_slave.head;\n" +
|
|
321
|
+
" zusize end = io_#{name}_buffer_slave.tail\n;" +
|
|
322
|
+
" if (end < start) end += ZEMU_IO_SERIAL_BUFFER_SIZE;\n" +
|
|
323
|
+
" return end - start;\n" +
|
|
324
|
+
"}\n" +
|
|
325
|
+
"\n" +
|
|
326
|
+
"void zemu_io_#{name}_slave_puts(zuint8 val)\n" +
|
|
327
|
+
"{\n" +
|
|
328
|
+
" io_#{name}_buffer_slave.buffer[io_#{name}_buffer_slave.tail] = val;\n" +
|
|
329
|
+
" io_#{name}_buffer_slave.tail++;\n" +
|
|
330
|
+
" if (io_#{name}_buffer_slave.tail >= ZEMU_IO_SERIAL_BUFFER_SIZE)\n" +
|
|
331
|
+
" io_#{name}_buffer_slave.tail = 0;\n" +
|
|
332
|
+
"}\n" +
|
|
333
|
+
"\n" +
|
|
334
|
+
"zuint8 zemu_io_#{name}_slave_gets(void)\n" +
|
|
335
|
+
"{\n" +
|
|
336
|
+
" zuint8 val = io_#{name}_buffer_master.buffer[io_#{name}_buffer_master.head];\n" +
|
|
337
|
+
" io_#{name}_buffer_master.head++;\n" +
|
|
338
|
+
" if (io_#{name}_buffer_master.head >= ZEMU_IO_SERIAL_BUFFER_SIZE)\n" +
|
|
339
|
+
" io_#{name}_buffer_master.head = 0;\n" +
|
|
340
|
+
"\n" +
|
|
341
|
+
" return val;\n" +
|
|
342
|
+
"}\n" +
|
|
343
|
+
"\n" +
|
|
344
|
+
"void zemu_io_#{name}_master_puts(zuint8 val)\n" +
|
|
345
|
+
"{\n" +
|
|
346
|
+
" io_#{name}_buffer_master.buffer[io_#{name}_buffer_master.tail] = val;\n" +
|
|
347
|
+
" io_#{name}_buffer_master.tail++;\n" +
|
|
348
|
+
" if (io_#{name}_buffer_master.tail >= ZEMU_IO_SERIAL_BUFFER_SIZE)\n" +
|
|
349
|
+
" io_#{name}_buffer_master.tail = 0;\n" +
|
|
350
|
+
"}\n" +
|
|
351
|
+
"\n" +
|
|
352
|
+
"zuint8 zemu_io_#{name}_master_gets(void)\n" +
|
|
353
|
+
"{\n" +
|
|
354
|
+
" zuint8 val = io_#{name}_buffer_slave.buffer[io_#{name}_buffer_slave.head];\n" +
|
|
355
|
+
" io_#{name}_buffer_slave.head++;\n" +
|
|
356
|
+
" if (io_#{name}_buffer_slave.head >= ZEMU_IO_SERIAL_BUFFER_SIZE)\n" +
|
|
357
|
+
" io_#{name}_buffer_slave.head = 0;\n" +
|
|
358
|
+
"\n" +
|
|
359
|
+
" return val;\n" +
|
|
360
|
+
"}\n"
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# Defines generated C to handle reading from serial port's
|
|
364
|
+
# registers.
|
|
365
|
+
def when_io_read
|
|
366
|
+
"if (port == #{in_port})\n" +
|
|
367
|
+
"{\n" +
|
|
368
|
+
" return zemu_io_#{name}_slave_gets();\n" +
|
|
369
|
+
"}\n" +
|
|
370
|
+
"else if (port == #{ready_port})\n" +
|
|
371
|
+
"{\n" +
|
|
372
|
+
" if (io_#{name}_buffer_master.head == io_#{name}_buffer_master.tail)\n" +
|
|
373
|
+
" {\n" +
|
|
374
|
+
" return 0;\n" +
|
|
375
|
+
" }\n" +
|
|
376
|
+
" else\n" +
|
|
377
|
+
" {\n" +
|
|
378
|
+
" return 1;\n" +
|
|
379
|
+
" }\n" +
|
|
380
|
+
"}\n"
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
# Defines generated C to handle writing to the serial port's registers.
|
|
384
|
+
def when_io_write
|
|
385
|
+
"if (port == #{out_port})\n" +
|
|
386
|
+
"{\n" +
|
|
387
|
+
" zemu_io_#{name}_slave_puts(value);\n" +
|
|
388
|
+
"}\n"
|
|
424
389
|
end
|
|
425
390
|
|
|
426
391
|
# Defines FFI API which will be available to the instance wrapper if this IO device is used.
|
|
@@ -433,7 +398,7 @@ module Zemu
|
|
|
433
398
|
end
|
|
434
399
|
|
|
435
400
|
# Valid parameters for a SerialPort, along with those
|
|
436
|
-
# defined in [Zemu::Config::
|
|
401
|
+
# defined in [Zemu::Config::BusDevice].
|
|
437
402
|
def params
|
|
438
403
|
super + %w(in_port out_port ready_port)
|
|
439
404
|
end
|
|
@@ -443,7 +408,7 @@ module Zemu
|
|
|
443
408
|
#
|
|
444
409
|
# Represents a device with a sequence of sectors of a fixed size,
|
|
445
410
|
# which can be accessed via IO instructions as an IDE drive.
|
|
446
|
-
class BlockDrive <
|
|
411
|
+
class BlockDrive < BusDevice
|
|
447
412
|
# Constructor.
|
|
448
413
|
#
|
|
449
414
|
# Takes a block in which the parameters of the block drive
|
|
@@ -466,18 +431,9 @@ module Zemu
|
|
|
466
431
|
#
|
|
467
432
|
#
|
|
468
433
|
def initialize
|
|
469
|
-
@blocks = []
|
|
470
434
|
@initialize_from = nil
|
|
471
435
|
|
|
472
436
|
super
|
|
473
|
-
|
|
474
|
-
num_sectors.times do
|
|
475
|
-
sector = []
|
|
476
|
-
sector_size.times do
|
|
477
|
-
sector << 0
|
|
478
|
-
end
|
|
479
|
-
@blocks << sector
|
|
480
|
-
end
|
|
481
437
|
|
|
482
438
|
# Initialize from provided file if applicable.
|
|
483
439
|
unless @initialize_from.nil?
|
|
@@ -486,17 +442,11 @@ module Zemu
|
|
|
486
442
|
if (file_size != num_sectors * sector_size)
|
|
487
443
|
raise RangeError, "Initialization file for Zemu::Config::BlockDrive '#{name}' is of wrong size."
|
|
488
444
|
end
|
|
489
|
-
|
|
490
|
-
File.open(@initialize_from, "rb") do |f|
|
|
491
|
-
num_sectors.times do |s|
|
|
492
|
-
sector_size.times do |b|
|
|
493
|
-
@blocks[s][b] = f.getbyte()
|
|
494
|
-
end
|
|
495
|
-
end
|
|
496
|
-
end
|
|
497
445
|
end
|
|
446
|
+
end
|
|
498
447
|
|
|
499
|
-
|
|
448
|
+
# Defines generated C to declare the block device.
|
|
449
|
+
def when_setup
|
|
500
450
|
<<-eos
|
|
501
451
|
#include <stdio.h>
|
|
502
452
|
|
|
@@ -537,9 +487,11 @@ zuint8 zemu_io_#{name}_readbyte(zuint32 sector, zuint32 offset)
|
|
|
537
487
|
return sector_data_#{name}[offset];
|
|
538
488
|
}
|
|
539
489
|
eos
|
|
540
|
-
|
|
490
|
+
end
|
|
541
491
|
|
|
542
|
-
|
|
492
|
+
# Defines generated C to handle reading the block drive's
|
|
493
|
+
# registers.
|
|
494
|
+
def when_io_read
|
|
543
495
|
<<-eos
|
|
544
496
|
if (port == #{base_port})
|
|
545
497
|
{
|
|
@@ -556,9 +508,11 @@ else if (port == #{base_port+7})
|
|
|
556
508
|
return drive_status_#{name};
|
|
557
509
|
}
|
|
558
510
|
eos
|
|
559
|
-
|
|
511
|
+
end
|
|
560
512
|
|
|
561
|
-
|
|
513
|
+
# Defines generated C to handle writing to the block drive's
|
|
514
|
+
# registers.
|
|
515
|
+
def when_io_write
|
|
562
516
|
<<-eos
|
|
563
517
|
if (port == #{base_port})
|
|
564
518
|
{
|
|
@@ -621,12 +575,31 @@ else if (port == #{base_port+7})
|
|
|
621
575
|
}
|
|
622
576
|
}
|
|
623
577
|
eos
|
|
624
|
-
end
|
|
625
578
|
end
|
|
626
579
|
|
|
627
580
|
# Array of sectors of this drive.
|
|
628
581
|
def blocks
|
|
629
|
-
|
|
582
|
+
b = []
|
|
583
|
+
|
|
584
|
+
if @initialize_from.nil?
|
|
585
|
+
num_sectors.times do
|
|
586
|
+
this_block = []
|
|
587
|
+
sector_size.times do
|
|
588
|
+
this_block << 0
|
|
589
|
+
end
|
|
590
|
+
b << this_block
|
|
591
|
+
end
|
|
592
|
+
return b
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
File.open(@initialize_from, "rb") do |f|
|
|
596
|
+
num_sectors.times do
|
|
597
|
+
this_block = f.read(sector_size)
|
|
598
|
+
b << this_block.unpack("C" * sector_size)
|
|
599
|
+
end
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
b
|
|
630
603
|
end
|
|
631
604
|
|
|
632
605
|
# Set file to initialize from.
|
|
@@ -642,7 +615,7 @@ eos
|
|
|
642
615
|
end
|
|
643
616
|
|
|
644
617
|
# Valid parameters for a BlockDrive, along with those
|
|
645
|
-
# defined in [Zemu::Config::
|
|
618
|
+
# defined in [Zemu::Config::BusDevice].
|
|
646
619
|
def params
|
|
647
620
|
super + %w(base_port sector_size num_sectors)
|
|
648
621
|
end
|
|
@@ -653,30 +626,27 @@ eos
|
|
|
653
626
|
# Represents a timer device, the period of which can be controlled
|
|
654
627
|
# by the CPU through an IO port. The timer generates an NMI once this
|
|
655
628
|
# period has expired. The timer can be reset via a control port.
|
|
656
|
-
class Timer <
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
"zuint8 io_#{name}_running = 0;\n"
|
|
663
|
-
end
|
|
664
|
-
|
|
665
|
-
when_read do
|
|
666
|
-
end
|
|
629
|
+
class Timer < BusDevice
|
|
630
|
+
# Defines generated C that sets up the timer.
|
|
631
|
+
def when_setup
|
|
632
|
+
"zuint8 io_#{name}_count;\n" +
|
|
633
|
+
"zuint8 io_#{name}_running = 0;\n"
|
|
634
|
+
end
|
|
667
635
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
636
|
+
# Defines generated C that handles writing to the timer's
|
|
637
|
+
# registers.
|
|
638
|
+
def when_io_write
|
|
639
|
+
"if (port == #{count_port}) io_#{name}_count = value;\n" +
|
|
640
|
+
"else if (port == #{control_port}) io_#{name}_running = value;\n"
|
|
641
|
+
end
|
|
672
642
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
643
|
+
# Defines generated C that handles a clock tick for the timer.
|
|
644
|
+
def when_clock
|
|
645
|
+
"if (io_#{name}_running)\n" +
|
|
646
|
+
"{\n" +
|
|
647
|
+
" if (io_#{name}_count > 0) io_#{name}_count--;\n" +
|
|
648
|
+
" else zemu_io_nmi(instance);\n" +
|
|
649
|
+
"}\n"
|
|
680
650
|
end
|
|
681
651
|
|
|
682
652
|
# Valid parameters for a Timer, along with those defined in
|
|
@@ -691,11 +661,8 @@ eos
|
|
|
691
661
|
return binding
|
|
692
662
|
end
|
|
693
663
|
|
|
694
|
-
# The
|
|
695
|
-
attr_reader :
|
|
696
|
-
|
|
697
|
-
# The IO devices of this configuration object.
|
|
698
|
-
attr_reader :io
|
|
664
|
+
# The bus devices of this configuration object.
|
|
665
|
+
attr_reader :devices
|
|
699
666
|
|
|
700
667
|
# Parameters accessible by this configuration object.
|
|
701
668
|
def params
|
|
@@ -734,8 +701,7 @@ eos
|
|
|
734
701
|
#
|
|
735
702
|
# @raise [Zemu::ConfigError] Raised if the +name+ parameter is not set, or contains whitespace.
|
|
736
703
|
def initialize
|
|
737
|
-
@
|
|
738
|
-
@io = []
|
|
704
|
+
@devices = []
|
|
739
705
|
|
|
740
706
|
super
|
|
741
707
|
|
|
@@ -750,16 +716,29 @@ eos
|
|
|
750
716
|
|
|
751
717
|
# Adds a new memory section to this configuration.
|
|
752
718
|
#
|
|
719
|
+
# Deprecated - retained only for backwards compatibility.
|
|
720
|
+
# Use add_device instead.
|
|
721
|
+
#
|
|
753
722
|
# @param [Zemu::Config::Memory] mem The memory object to add.
|
|
754
723
|
def add_memory(mem)
|
|
755
|
-
@
|
|
724
|
+
@devices << mem
|
|
756
725
|
end
|
|
757
726
|
|
|
758
727
|
# Adds a new IO device to this configuration.
|
|
759
728
|
#
|
|
760
|
-
#
|
|
729
|
+
# Deprecated - retained only for backwards compatibility.
|
|
730
|
+
# Use add_device instead.
|
|
731
|
+
#
|
|
732
|
+
# @param [Zemu::Config::BusDevice] io The IO device to add.
|
|
761
733
|
def add_io(io)
|
|
762
|
-
@
|
|
734
|
+
@devices << io
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
# Adds a new device to the bus for this configuration.
|
|
738
|
+
#
|
|
739
|
+
# @param [Zemu::Config::BusDevice] device The device to add.
|
|
740
|
+
def add_device(device)
|
|
741
|
+
@devices << device
|
|
763
742
|
end
|
|
764
743
|
end
|
|
765
744
|
|
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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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
|
@@ -37,6 +37,14 @@ module Zemu
|
|
|
37
37
|
"L'" => 19
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
# Mapping of extended registers
|
|
41
|
+
# to the registers that comprise them.
|
|
42
|
+
REGISTERS_EXTENDED = {
|
|
43
|
+
"HL" => ["H", "L"],
|
|
44
|
+
"BC" => ["B", "C"],
|
|
45
|
+
"DE" => ["D", "E"]
|
|
46
|
+
}
|
|
47
|
+
|
|
40
48
|
# States that the emulated machine can be in.
|
|
41
49
|
class RunState
|
|
42
50
|
# Currently executing an instruction.
|
|
@@ -53,6 +61,10 @@ module Zemu
|
|
|
53
61
|
end
|
|
54
62
|
|
|
55
63
|
def initialize(configuration)
|
|
64
|
+
# Methods defined by bus devices that we make
|
|
65
|
+
# accessible to the user.
|
|
66
|
+
@device_methods = []
|
|
67
|
+
|
|
56
68
|
@clock = configuration.clock_speed
|
|
57
69
|
@serial_delay = configuration.serial_delay
|
|
58
70
|
|
|
@@ -91,6 +103,12 @@ module Zemu
|
|
|
91
103
|
REGISTERS.each do |reg, num|
|
|
92
104
|
r[reg] = @wrapper.zemu_debug_register(@instance, num)
|
|
93
105
|
end
|
|
106
|
+
|
|
107
|
+
REGISTERS_EXTENDED.each do |reg, components|
|
|
108
|
+
hi = components[0]
|
|
109
|
+
lo = components[1]
|
|
110
|
+
r[reg] = (r[hi] << 8) | r[lo]
|
|
111
|
+
end
|
|
94
112
|
|
|
95
113
|
return r
|
|
96
114
|
end
|
|
@@ -105,6 +123,16 @@ module Zemu
|
|
|
105
123
|
return @wrapper.zemu_debug_get_memory(address)
|
|
106
124
|
end
|
|
107
125
|
|
|
126
|
+
# Set the value in memory at a given address.
|
|
127
|
+
#
|
|
128
|
+
# @param address The address in memory to be set.
|
|
129
|
+
# @param value The value to set to.
|
|
130
|
+
#
|
|
131
|
+
# Returns nothing.
|
|
132
|
+
def set_memory(address, value)
|
|
133
|
+
@wrapper.zemu_debug_set_memory(address, value)
|
|
134
|
+
end
|
|
135
|
+
|
|
108
136
|
# Write a string to the serial line of the emulated CPU.
|
|
109
137
|
#
|
|
110
138
|
# @param string The string to be sent.
|
|
@@ -245,16 +273,27 @@ module Zemu
|
|
|
245
273
|
wrapper.attach_function :zemu_debug_pc, [:pointer], :uint16
|
|
246
274
|
|
|
247
275
|
wrapper.attach_function :zemu_debug_get_memory, [:uint16], :uint8
|
|
276
|
+
wrapper.attach_function :zemu_debug_set_memory, [:uint16, :uint8], :void
|
|
248
277
|
|
|
249
|
-
configuration.
|
|
278
|
+
configuration.devices.each do |device|
|
|
250
279
|
device.functions.each do |f|
|
|
251
280
|
wrapper.attach_function(f["name"].to_sym, f["args"], f["return"])
|
|
281
|
+
@device_methods << f["name"].to_sym
|
|
252
282
|
end
|
|
253
283
|
end
|
|
254
284
|
|
|
255
285
|
return wrapper
|
|
256
286
|
end
|
|
257
287
|
|
|
288
|
+
# Redirects calls to I/O FFI functions.
|
|
289
|
+
def method_missing(method, *args)
|
|
290
|
+
if @device_methods.include? method
|
|
291
|
+
return @wrapper.send(method)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
super
|
|
295
|
+
end
|
|
296
|
+
|
|
258
297
|
private :make_wrapper
|
|
259
298
|
end
|
|
260
299
|
end
|
data/lib/zemu/interactive.rb
CHANGED
|
@@ -5,7 +5,14 @@ module Zemu
|
|
|
5
5
|
# Constructor.
|
|
6
6
|
#
|
|
7
7
|
# Create a new interactive wrapper for the given instance.
|
|
8
|
-
|
|
8
|
+
# The options hash allows the user to configure the behaviour
|
|
9
|
+
# of the interactive instance:
|
|
10
|
+
# :print_serial => true if serial input/output should be logged
|
|
11
|
+
# to the emulator window.
|
|
12
|
+
def initialize(instance, options = {})
|
|
13
|
+
@print_serial = options[:print_serial]
|
|
14
|
+
@trace = []
|
|
15
|
+
|
|
9
16
|
@instance = instance
|
|
10
17
|
|
|
11
18
|
@symbol_table = {}
|
|
@@ -64,6 +71,9 @@ module Zemu
|
|
|
64
71
|
elsif cmd[0] == "map"
|
|
65
72
|
load_map(cmd[1])
|
|
66
73
|
|
|
74
|
+
elsif cmd[0] == "trace"
|
|
75
|
+
trace()
|
|
76
|
+
|
|
67
77
|
elsif cmd[0] == "help"
|
|
68
78
|
log "Available commands:"
|
|
69
79
|
log " continue [<n>] - Continue execution for <n> cycles"
|
|
@@ -84,6 +94,14 @@ module Zemu
|
|
|
84
94
|
close
|
|
85
95
|
end
|
|
86
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
|
+
|
|
87
105
|
# Outputs a table giving the current values of the instance's registers.
|
|
88
106
|
# For the 16-bit registers (BC, DE, HL, IX, IY, SP, PC), attempts to identify the symbol
|
|
89
107
|
# to which they point.
|
|
@@ -179,6 +197,9 @@ module Zemu
|
|
|
179
197
|
cycles_left -= cycles_done
|
|
180
198
|
actual_cycles += cycles_done
|
|
181
199
|
|
|
200
|
+
@trace << r16("PC")
|
|
201
|
+
@trace = @trace[1..] if @trace.size > 200
|
|
202
|
+
|
|
182
203
|
# Get time after execution.
|
|
183
204
|
ending = Time.now
|
|
184
205
|
|
|
@@ -193,6 +214,11 @@ module Zemu
|
|
|
193
214
|
sleep(padding) unless padding < 0
|
|
194
215
|
end
|
|
195
216
|
|
|
217
|
+
if (@instance.memory(0x200) != 0xf3)
|
|
218
|
+
log "Buffer overflow at #{r16("PC")}"
|
|
219
|
+
break
|
|
220
|
+
end
|
|
221
|
+
|
|
196
222
|
# Have we hit a breakpoint or HALT instruction?
|
|
197
223
|
if @instance.break?
|
|
198
224
|
log "Hit breakpoint at #{r16("PC")}."
|
|
@@ -252,7 +278,7 @@ module Zemu
|
|
|
252
278
|
|
|
253
279
|
syms = {}
|
|
254
280
|
begin
|
|
255
|
-
syms.merge! Debug.load_map(path.to_s)
|
|
281
|
+
syms.merge! Debug.load_map(path.to_s).hash
|
|
256
282
|
rescue ArgumentError => e
|
|
257
283
|
log "Error loading map file: #{e.message}"
|
|
258
284
|
syms.clear
|
|
@@ -275,12 +301,12 @@ module Zemu
|
|
|
275
301
|
|
|
276
302
|
unless input.empty?
|
|
277
303
|
@instance.serial_puts input
|
|
278
|
-
log "Serial in: #{input}"
|
|
304
|
+
log "Serial in: #{input} ($#{input.ord.to_s(16)})" if @print_serial
|
|
279
305
|
end
|
|
280
306
|
|
|
281
307
|
unless output.empty?
|
|
282
308
|
@master.write output
|
|
283
|
-
log "Serial out: #{output}"
|
|
309
|
+
log "Serial out: #{output} ($#{output.ord.to_s(16)})" if @print_serial
|
|
284
310
|
end
|
|
285
311
|
end
|
|
286
312
|
end
|
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
|
-
|
|
68
|
-
|
|
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
|
|
@@ -73,19 +75,19 @@ module Zemu
|
|
|
73
75
|
# Starts an interactive instance of an emulator, according to the given configuration.
|
|
74
76
|
#
|
|
75
77
|
# @param [Zemu::Config] configuration The configuration for which an emulator will be generated.
|
|
76
|
-
def Zemu::start_interactive(configuration)
|
|
78
|
+
def Zemu::start_interactive(configuration, options = {})
|
|
77
79
|
instance = start(configuration)
|
|
78
80
|
|
|
79
|
-
interactive = InteractiveInstance.new(instance)
|
|
81
|
+
interactive = InteractiveInstance.new(instance, options)
|
|
80
82
|
interactive.run
|
|
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
|
-
|
|
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, "
|
|
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
|
-
|
|
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
|
|
165
|
-
def Zemu::
|
|
166
|
-
header_template = ERB.new File.read(File.join(SRC, "
|
|
167
|
-
source_template = ERB.new File.read(File.join(SRC, "
|
|
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, "
|
|
160
|
+
File.write(File.join(autogen, "bus.h"),
|
|
176
161
|
header_template.result(configuration.get_binding))
|
|
177
162
|
|
|
178
|
-
File.write(File.join(autogen, "
|
|
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,88 @@
|
|
|
1
|
+
#include "bus.h"
|
|
2
|
+
|
|
3
|
+
<% devices.each do |device| %>
|
|
4
|
+
<%= device.when_setup %>
|
|
5
|
+
<% end %>
|
|
6
|
+
|
|
7
|
+
zuint8 zemu_memory_read(void * context, zuint16 address)
|
|
8
|
+
{
|
|
9
|
+
zuint32 address_32 = address;
|
|
10
|
+
<% devices.each do |mem| %>
|
|
11
|
+
<%= mem.when_mem_read %>
|
|
12
|
+
<% end %>
|
|
13
|
+
/* Unmapped memory has a value of 0. */
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
void zemu_memory_write(void * context, zuint16 address, zuint8 value)
|
|
18
|
+
{
|
|
19
|
+
zuint32 address_32 = address;
|
|
20
|
+
<% devices.each do |mem| %>
|
|
21
|
+
<%= mem.when_mem_write %>
|
|
22
|
+
<% end %>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
zuint8 zemu_memory_peek(zuint16 address)
|
|
26
|
+
{
|
|
27
|
+
zuint32 address_32 = address;
|
|
28
|
+
<% devices.each do |mem| %>
|
|
29
|
+
<%= mem.when_mem_read %>
|
|
30
|
+
<% end %>
|
|
31
|
+
/* Unmapped memory has a value of 0. */
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
void zemu_memory_poke(zuint16 address, zuint8 value)
|
|
36
|
+
{
|
|
37
|
+
zuint32 address_32 = address;
|
|
38
|
+
<% devices.each do |mem| %>
|
|
39
|
+
<%= mem.when_mem_write %>
|
|
40
|
+
<% end %>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
void zemu_io_nmi(Z80 * instance)
|
|
44
|
+
{
|
|
45
|
+
z80_nmi(instance);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
void zemu_io_int_on(Z80 * instance)
|
|
49
|
+
{
|
|
50
|
+
z80_int(instance, TRUE);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
void zemu_io_int_off(Z80 * instance)
|
|
54
|
+
{
|
|
55
|
+
z80_int(instance, FALSE);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
zuint8 zemu_io_in(void * context, zuint16 port)
|
|
59
|
+
{
|
|
60
|
+
/* Z80 IO ports occupy the lower half of the address bus.
|
|
61
|
+
* We cannot assume that the top half is valid.
|
|
62
|
+
*/
|
|
63
|
+
port &= 0x00FF;
|
|
64
|
+
|
|
65
|
+
<% devices.each do |device| %>
|
|
66
|
+
<%= device.when_io_read %>
|
|
67
|
+
<% end %>
|
|
68
|
+
return 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
void zemu_io_out(void * context, zuint16 port, zuint8 value)
|
|
72
|
+
{
|
|
73
|
+
/* Z80 IO ports occupy the lower half of the address bus.
|
|
74
|
+
* We cannot assume that the top half is valid.
|
|
75
|
+
*/
|
|
76
|
+
port &= 0x00FF;
|
|
77
|
+
|
|
78
|
+
<% devices.each do |device| %>
|
|
79
|
+
<%= device.when_io_write %>
|
|
80
|
+
<% end %>
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
void zemu_io_clock(Z80 * instance)
|
|
84
|
+
{
|
|
85
|
+
<% devices.each do |device| %>
|
|
86
|
+
<%= device.when_clock %>
|
|
87
|
+
<% end %>
|
|
88
|
+
}
|
data/src/{io.h.erb → bus.h.erb}
RENAMED
|
@@ -13,6 +13,13 @@ typedef struct {
|
|
|
13
13
|
unsigned int tail;
|
|
14
14
|
} SerialBuffer;
|
|
15
15
|
|
|
16
|
+
|
|
17
|
+
zuint8 zemu_memory_read(void * context, zuint16 address);
|
|
18
|
+
void zemu_memory_write(void * context, zuint16 address, zuint8 value);
|
|
19
|
+
|
|
20
|
+
zuint8 zemu_memory_peek(zuint16 address);
|
|
21
|
+
void zemu_memory_poke(zuint16 address, zuint8 value);
|
|
22
|
+
|
|
16
23
|
void zemu_io_serial_master_puts(zuint8 val);
|
|
17
24
|
zuint8 zemu_io_serial_master_gets(void);
|
|
18
25
|
zusize zemu_io_serial_buffer_size(void);
|
data/src/debug.c
CHANGED
data/src/debug.h
CHANGED
data/src/main.c
CHANGED
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
|
+
version: 0.6.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
|
+
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/memory.c.erb
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
#include "memory.h"
|
|
2
|
-
|
|
3
|
-
<% memory.each do |mem| %>
|
|
4
|
-
/* Initialization memory block "<%= mem.name %>" */
|
|
5
|
-
<%= mem.readonly? ? "const " : "" %>zuint8 zemu_memory_block_<%= mem.name %>[0x<%= mem.size.to_s(16) %>] =
|
|
6
|
-
{<% mem.contents.each_with_index do |b, i| %><%= (i % 16 == 0) ? "\n " : "" %><%= ("0x%02x, " % b) %><% end %>
|
|
7
|
-
};
|
|
8
|
-
<% end %>
|
|
9
|
-
|
|
10
|
-
zuint8 zemu_memory_read(void * context, zuint16 address)
|
|
11
|
-
{
|
|
12
|
-
<% memory.each do |mem| %>
|
|
13
|
-
if (address >= 0x<%= mem.address.to_s(16) %> && address < 0x<%= (mem.address + mem.size).to_s(16) %>)
|
|
14
|
-
{
|
|
15
|
-
return zemu_memory_block_<%= mem.name %>[address - 0x<%= mem.address.to_s(16) %>];
|
|
16
|
-
}
|
|
17
|
-
<% end %>
|
|
18
|
-
/* Unmapped memory has a value of 0. */
|
|
19
|
-
return 0;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
void zemu_memory_write(void * context, zuint16 address, zuint8 value)
|
|
23
|
-
{
|
|
24
|
-
<% memory.each do |mem| %>
|
|
25
|
-
<% next if mem.readonly? %>
|
|
26
|
-
if (address >= 0x<%= mem.address.to_s(16) %> && address < 0x<%= (mem.address + mem.size).to_s(16) %>)
|
|
27
|
-
{
|
|
28
|
-
zemu_memory_block_<%= mem.name %>[address - 0x<%= mem.address.to_s(16) %>] = value;
|
|
29
|
-
}
|
|
30
|
-
<% end %>
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
zuint8 zemu_memory_peek(zuint16 address)
|
|
34
|
-
{
|
|
35
|
-
<% memory.each do |mem| %>
|
|
36
|
-
if (address >= 0x<%= mem.address.to_s(16) %> && address < 0x<%= (mem.address + mem.size).to_s(16) %>)
|
|
37
|
-
{
|
|
38
|
-
return zemu_memory_block_<%= mem.name %>[address - 0x<%= mem.address.to_s(16) %>];
|
|
39
|
-
}
|
|
40
|
-
<% end %>
|
|
41
|
-
/* Unmapped memory has a value of 0. */
|
|
42
|
-
return 0;
|
|
43
|
-
}
|
data/src/memory.h.erb
DELETED