zemu 0.5.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 +252 -276
- data/lib/zemu/debug.rb +5 -0
- data/lib/zemu/instance.rb +5 -5
- data/lib/zemu/interactive.rb +21 -1
- data/lib/zemu.rb +8 -27
- data/src/bus.c.erb +88 -0
- data/src/{io.h.erb → bus.h.erb} +7 -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 -59
- data/src/memory.h.erb +0 -11
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
|
@@ -478,8 +443,10 @@ module Zemu
|
|
478
443
|
raise RangeError, "Initialization file for Zemu::Config::BlockDrive '#{name}' is of wrong size."
|
479
444
|
end
|
480
445
|
end
|
446
|
+
end
|
481
447
|
|
482
|
-
|
448
|
+
# Defines generated C to declare the block device.
|
449
|
+
def when_setup
|
483
450
|
<<-eos
|
484
451
|
#include <stdio.h>
|
485
452
|
|
@@ -520,9 +487,11 @@ zuint8 zemu_io_#{name}_readbyte(zuint32 sector, zuint32 offset)
|
|
520
487
|
return sector_data_#{name}[offset];
|
521
488
|
}
|
522
489
|
eos
|
523
|
-
|
490
|
+
end
|
524
491
|
|
525
|
-
|
492
|
+
# Defines generated C to handle reading the block drive's
|
493
|
+
# registers.
|
494
|
+
def when_io_read
|
526
495
|
<<-eos
|
527
496
|
if (port == #{base_port})
|
528
497
|
{
|
@@ -539,9 +508,11 @@ else if (port == #{base_port+7})
|
|
539
508
|
return drive_status_#{name};
|
540
509
|
}
|
541
510
|
eos
|
542
|
-
|
511
|
+
end
|
543
512
|
|
544
|
-
|
513
|
+
# Defines generated C to handle writing to the block drive's
|
514
|
+
# registers.
|
515
|
+
def when_io_write
|
545
516
|
<<-eos
|
546
517
|
if (port == #{base_port})
|
547
518
|
{
|
@@ -604,7 +575,6 @@ else if (port == #{base_port+7})
|
|
604
575
|
}
|
605
576
|
}
|
606
577
|
eos
|
607
|
-
end
|
608
578
|
end
|
609
579
|
|
610
580
|
# Array of sectors of this drive.
|
@@ -645,7 +615,7 @@ eos
|
|
645
615
|
end
|
646
616
|
|
647
617
|
# Valid parameters for a BlockDrive, along with those
|
648
|
-
# defined in [Zemu::Config::
|
618
|
+
# defined in [Zemu::Config::BusDevice].
|
649
619
|
def params
|
650
620
|
super + %w(base_port sector_size num_sectors)
|
651
621
|
end
|
@@ -656,30 +626,27 @@ eos
|
|
656
626
|
# Represents a timer device, the period of which can be controlled
|
657
627
|
# by the CPU through an IO port. The timer generates an NMI once this
|
658
628
|
# period has expired. The timer can be reset via a control port.
|
659
|
-
class Timer <
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
"zuint8 io_#{name}_running = 0;\n"
|
666
|
-
end
|
667
|
-
|
668
|
-
when_read do
|
669
|
-
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
|
670
635
|
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
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
|
675
642
|
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
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"
|
683
650
|
end
|
684
651
|
|
685
652
|
# Valid parameters for a Timer, along with those defined in
|
@@ -694,11 +661,8 @@ eos
|
|
694
661
|
return binding
|
695
662
|
end
|
696
663
|
|
697
|
-
# The
|
698
|
-
attr_reader :
|
699
|
-
|
700
|
-
# The IO devices of this configuration object.
|
701
|
-
attr_reader :io
|
664
|
+
# The bus devices of this configuration object.
|
665
|
+
attr_reader :devices
|
702
666
|
|
703
667
|
# Parameters accessible by this configuration object.
|
704
668
|
def params
|
@@ -737,8 +701,7 @@ eos
|
|
737
701
|
#
|
738
702
|
# @raise [Zemu::ConfigError] Raised if the +name+ parameter is not set, or contains whitespace.
|
739
703
|
def initialize
|
740
|
-
@
|
741
|
-
@io = []
|
704
|
+
@devices = []
|
742
705
|
|
743
706
|
super
|
744
707
|
|
@@ -753,16 +716,29 @@ eos
|
|
753
716
|
|
754
717
|
# Adds a new memory section to this configuration.
|
755
718
|
#
|
719
|
+
# Deprecated - retained only for backwards compatibility.
|
720
|
+
# Use add_device instead.
|
721
|
+
#
|
756
722
|
# @param [Zemu::Config::Memory] mem The memory object to add.
|
757
723
|
def add_memory(mem)
|
758
|
-
@
|
724
|
+
@devices << mem
|
759
725
|
end
|
760
726
|
|
761
727
|
# Adds a new IO device to this configuration.
|
762
728
|
#
|
763
|
-
#
|
729
|
+
# Deprecated - retained only for backwards compatibility.
|
730
|
+
# Use add_device instead.
|
731
|
+
#
|
732
|
+
# @param [Zemu::Config::BusDevice] io The IO device to add.
|
764
733
|
def add_io(io)
|
765
|
-
@
|
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
|
766
742
|
end
|
767
743
|
end
|
768
744
|
|
data/lib/zemu/debug.rb
CHANGED
data/lib/zemu/instance.rb
CHANGED
@@ -61,9 +61,9 @@ module Zemu
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def initialize(configuration)
|
64
|
-
# Methods defined by
|
64
|
+
# Methods defined by bus devices that we make
|
65
65
|
# accessible to the user.
|
66
|
-
@
|
66
|
+
@device_methods = []
|
67
67
|
|
68
68
|
@clock = configuration.clock_speed
|
69
69
|
@serial_delay = configuration.serial_delay
|
@@ -275,10 +275,10 @@ module Zemu
|
|
275
275
|
wrapper.attach_function :zemu_debug_get_memory, [:uint16], :uint8
|
276
276
|
wrapper.attach_function :zemu_debug_set_memory, [:uint16, :uint8], :void
|
277
277
|
|
278
|
-
configuration.
|
278
|
+
configuration.devices.each do |device|
|
279
279
|
device.functions.each do |f|
|
280
280
|
wrapper.attach_function(f["name"].to_sym, f["args"], f["return"])
|
281
|
-
@
|
281
|
+
@device_methods << f["name"].to_sym
|
282
282
|
end
|
283
283
|
end
|
284
284
|
|
@@ -287,7 +287,7 @@ module Zemu
|
|
287
287
|
|
288
288
|
# Redirects calls to I/O FFI functions.
|
289
289
|
def method_missing(method, *args)
|
290
|
-
if @
|
290
|
+
if @device_methods.include? method
|
291
291
|
return @wrapper.send(method)
|
292
292
|
end
|
293
293
|
|
data/lib/zemu/interactive.rb
CHANGED
@@ -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
@@ -111,7 +111,7 @@ module Zemu
|
|
111
111
|
|
112
112
|
inputs_str = inputs.map { |i| File.join(SRC, i) }.join(" ")
|
113
113
|
|
114
|
-
inputs_str += " " + File.join(autogen, "
|
114
|
+
inputs_str += " " + File.join(autogen, "bus.c")
|
115
115
|
|
116
116
|
defines = {
|
117
117
|
"CPU_Z80_STATIC" => 1,
|
@@ -143,14 +143,13 @@ module Zemu
|
|
143
143
|
#
|
144
144
|
# @param [Zemu::Config] configuration The configuration for which an emulator will be generated.
|
145
145
|
def Zemu::generate(configuration)
|
146
|
-
|
147
|
-
generate_io(configuration)
|
146
|
+
generate_bus(configuration)
|
148
147
|
end
|
149
148
|
|
150
|
-
# Generates the
|
151
|
-
def Zemu::
|
152
|
-
header_template = ERB.new File.read(File.join(SRC, "
|
153
|
-
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"))
|
154
153
|
|
155
154
|
autogen = File.join(configuration.output_directory, "autogen_#{configuration.name}")
|
156
155
|
|
@@ -158,28 +157,10 @@ module Zemu
|
|
158
157
|
Dir.mkdir autogen
|
159
158
|
end
|
160
159
|
|
161
|
-
File.write(File.join(autogen, "
|
160
|
+
File.write(File.join(autogen, "bus.h"),
|
162
161
|
header_template.result(configuration.get_binding))
|
163
162
|
|
164
|
-
File.write(File.join(autogen, "
|
165
|
-
source_template.result(configuration.get_binding))
|
166
|
-
end
|
167
|
-
|
168
|
-
# Generates the io.c and io.h files for a given configuration.
|
169
|
-
def Zemu::generate_io(configuration)
|
170
|
-
header_template = ERB.new File.read(File.join(SRC, "io.h.erb"))
|
171
|
-
source_template = ERB.new File.read(File.join(SRC, "io.c.erb"))
|
172
|
-
|
173
|
-
autogen = File.join(configuration.output_directory, "autogen_#{configuration.name}")
|
174
|
-
|
175
|
-
unless Dir.exist? autogen
|
176
|
-
Dir.mkdir autogen
|
177
|
-
end
|
178
|
-
|
179
|
-
File.write(File.join(autogen, "io.h"),
|
180
|
-
header_template.result(configuration.get_binding))
|
181
|
-
|
182
|
-
File.write(File.join(autogen, "io.c"),
|
163
|
+
File.write(File.join(autogen, "bus.c"),
|
183
164
|
source_template.result(configuration.get_binding))
|
184
165
|
end
|
185
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.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,59 +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
|
-
zuint32 address_32 = address;
|
13
|
-
<% memory.each do |mem| %>
|
14
|
-
if (address_32 >= 0x<%= mem.address.to_s(16) %> && address_32 < 0x<%= (mem.address + mem.size).to_s(16) %>)
|
15
|
-
{
|
16
|
-
return zemu_memory_block_<%= mem.name %>[address_32 - 0x<%= mem.address.to_s(16) %>];
|
17
|
-
}
|
18
|
-
<% end %>
|
19
|
-
/* Unmapped memory has a value of 0. */
|
20
|
-
return 0;
|
21
|
-
}
|
22
|
-
|
23
|
-
void zemu_memory_write(void * context, zuint16 address, zuint8 value)
|
24
|
-
{
|
25
|
-
zuint32 address_32 = address;
|
26
|
-
<% memory.each do |mem| %>
|
27
|
-
<% next if mem.readonly? %>
|
28
|
-
if (address_32 >= 0x<%= mem.address.to_s(16) %> && address_32 < 0x<%= (mem.address + mem.size).to_s(16) %>)
|
29
|
-
{
|
30
|
-
zemu_memory_block_<%= mem.name %>[address_32 - 0x<%= mem.address.to_s(16) %>] = value;
|
31
|
-
}
|
32
|
-
<% end %>
|
33
|
-
}
|
34
|
-
|
35
|
-
zuint8 zemu_memory_peek(zuint16 address)
|
36
|
-
{
|
37
|
-
zuint32 address_32 = address;
|
38
|
-
<% memory.each do |mem| %>
|
39
|
-
if (address_32 >= 0x<%= mem.address.to_s(16) %> && address_32 < 0x<%= (mem.address + mem.size).to_s(16) %>)
|
40
|
-
{
|
41
|
-
return zemu_memory_block_<%= mem.name %>[address_32 - 0x<%= mem.address.to_s(16) %>];
|
42
|
-
}
|
43
|
-
<% end %>
|
44
|
-
/* Unmapped memory has a value of 0. */
|
45
|
-
return 0;
|
46
|
-
}
|
47
|
-
|
48
|
-
void zemu_memory_poke(zuint16 address, zuint8 value)
|
49
|
-
{
|
50
|
-
zuint32 address_32 = address;
|
51
|
-
<% memory.each do |mem| %>
|
52
|
-
<% next if mem.readonly? %>
|
53
|
-
if (address_32 >= 0x<%= mem.address.to_s(16) %> && address_32 < 0x<%= (mem.address + mem.size).to_s(16) %>)
|
54
|
-
{
|
55
|
-
zemu_memory_block_<%= mem.name %>[address_32 - 0x<%= mem.address.to_s(16) %>] = value;
|
56
|
-
return;
|
57
|
-
}
|
58
|
-
<% end %>
|
59
|
-
}
|
data/src/memory.h.erb
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
#include "emulation/CPU/Z80.h"
|
2
|
-
|
3
|
-
#include <stdio.h>
|
4
|
-
|
5
|
-
zuint8 zemu_memory_read(void * context, zuint16 address);
|
6
|
-
|
7
|
-
void zemu_memory_write(void * context, zuint16 address, zuint8 value);
|
8
|
-
|
9
|
-
zuint8 zemu_memory_peek(zuint16 address);
|
10
|
-
|
11
|
-
void zemu_memory_poke(zuint16 address, zuint8 value);
|