zemu 0.4.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/zemu/config.rb +401 -391
- data/lib/zemu/debug.rb +39 -6
- data/lib/zemu/instance.rb +110 -1
- data/lib/zemu/interactive.rb +21 -1
- data/lib/zemu.rb +17 -32
- data/src/bus.c.erb +99 -0
- data/src/bus.h.erb +53 -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/io.h.erb +0 -25
- data/src/memory.c.erb +0 -59
- data/src/memory.h.erb +0 -11
data/lib/zemu/config.rb
CHANGED
@@ -75,10 +75,106 @@ 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
|
+
@nmi = false
|
92
|
+
|
93
|
+
super
|
94
|
+
end
|
95
|
+
|
96
|
+
# Setup to be performed on initialising the emulator
|
97
|
+
# instance.
|
98
|
+
def when_setup
|
99
|
+
""
|
100
|
+
end
|
101
|
+
|
102
|
+
# Memory bus write handler.
|
103
|
+
#
|
104
|
+
# Handles write access via the memory bus to this device.
|
105
|
+
#
|
106
|
+
# @param addr The address being accessed.
|
107
|
+
# @param value The value being written.
|
108
|
+
def mem_write(addr, value)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Memory bus read handler.
|
112
|
+
#
|
113
|
+
# Handles read access via the memory bus to this device.
|
114
|
+
#
|
115
|
+
# @param addr The address being accessed.
|
116
|
+
#
|
117
|
+
# Returns the value read, or nil if no value
|
118
|
+
# (e.g. if address falls outside range for this device).
|
119
|
+
def mem_read(addr)
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
|
123
|
+
# IO bus write handler.
|
124
|
+
#
|
125
|
+
# Handles write access via the IO bus to this device.
|
126
|
+
#
|
127
|
+
# @param port The IO port being accessed.
|
128
|
+
# @param value The value being written.
|
129
|
+
def io_write(port, value)
|
130
|
+
end
|
131
|
+
|
132
|
+
# IO bus read handler.
|
133
|
+
#
|
134
|
+
# Handles read access via the IO bus to this device.
|
135
|
+
#
|
136
|
+
# @param port The IO port being accessed.
|
137
|
+
#
|
138
|
+
# Returns the value read from the port, or nil if no
|
139
|
+
# value (e.g. port does not correspond to this device).
|
140
|
+
def io_read(port)
|
141
|
+
nil
|
142
|
+
end
|
143
|
+
|
144
|
+
# Clock handler.
|
145
|
+
#
|
146
|
+
# Handles a clock cycle for this device.
|
147
|
+
# Deriving objects can use the nmi function
|
148
|
+
# to set the state of the non-maskable interrupt
|
149
|
+
# at each clock cycle.
|
150
|
+
def clock
|
151
|
+
end
|
152
|
+
|
153
|
+
# FFI functions provided by this device.
|
154
|
+
def functions
|
155
|
+
[]
|
156
|
+
end
|
157
|
+
|
158
|
+
# Sets state of the NMI for this device.
|
159
|
+
def nmi(state)
|
160
|
+
@nmi = state
|
161
|
+
end
|
162
|
+
|
163
|
+
# Gets state of NMI for this device.
|
164
|
+
def nmi?
|
165
|
+
@nmi
|
166
|
+
end
|
167
|
+
|
168
|
+
# Parameters for a bus device.
|
169
|
+
def params
|
170
|
+
%w(name)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
78
174
|
# Memory object.
|
79
175
|
#
|
80
176
|
# This is an abstract class from which all other memory objects inherit.
|
81
|
-
class Memory <
|
177
|
+
class Memory < BusDevice
|
82
178
|
# Constructor.
|
83
179
|
#
|
84
180
|
# Do not use, as this is an abstract class. Use one of the subclasses instead.
|
@@ -107,15 +203,51 @@ module Zemu
|
|
107
203
|
end
|
108
204
|
end
|
109
205
|
|
110
|
-
#
|
206
|
+
# Is this memory read-only?
|
111
207
|
def readonly?
|
112
|
-
|
208
|
+
false
|
209
|
+
end
|
210
|
+
|
211
|
+
# Memory bus read handler.
|
212
|
+
#
|
213
|
+
# Handles read access via the memory bus to this device.
|
214
|
+
#
|
215
|
+
# @param addr The address being accessed.
|
216
|
+
#
|
217
|
+
# Returns the value read, or nil if no value
|
218
|
+
# (e.g. if address falls outside range for this device).
|
219
|
+
def mem_read(addr)
|
220
|
+
# Return value in memory's contents if the address
|
221
|
+
# falls within range.
|
222
|
+
if (addr >= address) && (addr < (address + size))
|
223
|
+
offset = addr - address
|
224
|
+
return @contents[offset]
|
225
|
+
end
|
226
|
+
|
227
|
+
# Otherwise return nil - address does not correspond
|
228
|
+
# to this memory block.
|
229
|
+
nil
|
230
|
+
end
|
231
|
+
|
232
|
+
# Memory bus write handler.
|
233
|
+
#
|
234
|
+
# Handles write access via the memory bus to this device.
|
235
|
+
#
|
236
|
+
# @param addr The address being accessed.
|
237
|
+
# @param value The value being written.
|
238
|
+
def mem_write(addr, value)
|
239
|
+
# If address falls within range, set value in
|
240
|
+
# memory contents.
|
241
|
+
if (addr >= address) && (addr < (address + size))
|
242
|
+
offset = addr - address
|
243
|
+
@contents[offset] = value
|
244
|
+
end
|
113
245
|
end
|
114
246
|
|
115
247
|
# Valid parameters for this object.
|
116
248
|
# Should be extended by subclasses but NOT REPLACED.
|
117
249
|
def params
|
118
|
-
|
250
|
+
super + %w(address size)
|
119
251
|
end
|
120
252
|
|
121
253
|
# Reads the contents of a file in binary format and
|
@@ -155,8 +287,14 @@ module Zemu
|
|
155
287
|
super
|
156
288
|
end
|
157
289
|
|
290
|
+
# Is this memory block readonly?
|
158
291
|
def readonly?
|
159
|
-
|
292
|
+
true
|
293
|
+
end
|
294
|
+
|
295
|
+
# Memory write handler.
|
296
|
+
def mem_write(addr, port)
|
297
|
+
# Does nothing - cannot write to read-only memory.
|
160
298
|
end
|
161
299
|
end
|
162
300
|
|
@@ -165,169 +303,13 @@ module Zemu
|
|
165
303
|
# Represents a block of memory which can be read and written.
|
166
304
|
class RAM < Memory
|
167
305
|
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
306
|
|
325
307
|
# Serial Input/Output object
|
326
308
|
#
|
327
309
|
# Represents a serial connection between the emulated CPU
|
328
310
|
# and the host machine, with input and output mapped to Z80 I/O
|
329
311
|
# ports.
|
330
|
-
class SerialPort <
|
312
|
+
class SerialPort < BusDevice
|
331
313
|
# Constructor.
|
332
314
|
#
|
333
315
|
# Takes a block in which the parameters of the serial port
|
@@ -348,92 +330,93 @@ module Zemu
|
|
348
330
|
def initialize
|
349
331
|
super
|
350
332
|
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
"\n" +
|
355
|
-
"zusize zemu_io_#{name}_buffer_size(void)\n" +
|
356
|
-
"{\n" +
|
357
|
-
" zusize start = io_#{name}_buffer_slave.head;\n" +
|
358
|
-
" zusize end = io_#{name}_buffer_slave.tail\n;" +
|
359
|
-
" if (end < start) end += ZEMU_IO_SERIAL_BUFFER_SIZE;\n" +
|
360
|
-
" return end - start;\n" +
|
361
|
-
"}\n" +
|
362
|
-
"\n" +
|
363
|
-
"void zemu_io_#{name}_slave_puts(zuint8 val)\n" +
|
364
|
-
"{\n" +
|
365
|
-
" io_#{name}_buffer_slave.buffer[io_#{name}_buffer_slave.tail] = val;\n" +
|
366
|
-
" io_#{name}_buffer_slave.tail++;\n" +
|
367
|
-
" if (io_#{name}_buffer_slave.tail >= ZEMU_IO_SERIAL_BUFFER_SIZE)\n" +
|
368
|
-
" io_#{name}_buffer_slave.tail = 0;\n" +
|
369
|
-
"}\n" +
|
370
|
-
"\n" +
|
371
|
-
"zuint8 zemu_io_#{name}_slave_gets(void)\n" +
|
372
|
-
"{\n" +
|
373
|
-
" zuint8 val = io_#{name}_buffer_master.buffer[io_#{name}_buffer_master.head];\n" +
|
374
|
-
" io_#{name}_buffer_master.head++;\n" +
|
375
|
-
" if (io_#{name}_buffer_master.head >= ZEMU_IO_SERIAL_BUFFER_SIZE)\n" +
|
376
|
-
" io_#{name}_buffer_master.head = 0;\n" +
|
377
|
-
"\n" +
|
378
|
-
" return val;\n" +
|
379
|
-
"}\n" +
|
380
|
-
"\n" +
|
381
|
-
"void zemu_io_#{name}_master_puts(zuint8 val)\n" +
|
382
|
-
"{\n" +
|
383
|
-
" io_#{name}_buffer_master.buffer[io_#{name}_buffer_master.tail] = val;\n" +
|
384
|
-
" io_#{name}_buffer_master.tail++;\n" +
|
385
|
-
" if (io_#{name}_buffer_master.tail >= ZEMU_IO_SERIAL_BUFFER_SIZE)\n" +
|
386
|
-
" io_#{name}_buffer_master.tail = 0;\n" +
|
387
|
-
"}\n" +
|
388
|
-
"\n" +
|
389
|
-
"zuint8 zemu_io_#{name}_master_gets(void)\n" +
|
390
|
-
"{\n" +
|
391
|
-
" zuint8 val = io_#{name}_buffer_slave.buffer[io_#{name}_buffer_slave.head];\n" +
|
392
|
-
" io_#{name}_buffer_slave.head++;\n" +
|
393
|
-
" if (io_#{name}_buffer_slave.head >= ZEMU_IO_SERIAL_BUFFER_SIZE)\n" +
|
394
|
-
" io_#{name}_buffer_slave.head = 0;\n" +
|
395
|
-
"\n" +
|
396
|
-
" return val;\n" +
|
397
|
-
"}\n"
|
398
|
-
end
|
333
|
+
@buffer_tx = []
|
334
|
+
@buffer_rx = []
|
335
|
+
end
|
399
336
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
337
|
+
# IO bus read handler.
|
338
|
+
#
|
339
|
+
# Handles read access via the IO bus to this device.
|
340
|
+
#
|
341
|
+
# @param port The IO port being accessed.
|
342
|
+
#
|
343
|
+
# Returns the value read, or nil if the port does not
|
344
|
+
# correspond to this device.
|
345
|
+
def io_read(port)
|
346
|
+
if port == in_port
|
347
|
+
return @buffer_rx.shift()
|
348
|
+
elsif port == ready_port
|
349
|
+
if @buffer_rx.empty?
|
350
|
+
return 0
|
351
|
+
else
|
352
|
+
return 1
|
353
|
+
end
|
416
354
|
end
|
417
355
|
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
356
|
+
nil
|
357
|
+
end
|
358
|
+
|
359
|
+
# IO bus write handler.
|
360
|
+
#
|
361
|
+
# Handles write access via the IO bus to this device.
|
362
|
+
#
|
363
|
+
# @param port The IO port being accessed.
|
364
|
+
# @param value The value being written.
|
365
|
+
def io_write(port, value)
|
366
|
+
if port == out_port
|
367
|
+
@buffer_tx << value
|
423
368
|
end
|
424
369
|
end
|
425
370
|
|
426
|
-
#
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
371
|
+
# Gets number of bytes transmitted by the CPU,
|
372
|
+
# but not yet read from this device.
|
373
|
+
def transmitted_count
|
374
|
+
@buffer_tx.size
|
375
|
+
end
|
376
|
+
|
377
|
+
# Gets a byte transmitted by the CPU, or nil
|
378
|
+
# if transmit buffer is empty.
|
379
|
+
def get_byte
|
380
|
+
@buffer_tx.shift()
|
381
|
+
end
|
382
|
+
|
383
|
+
# Puts a byte in the receive buffer of this device.
|
384
|
+
def put_byte(b)
|
385
|
+
@buffer_rx << b
|
386
|
+
end
|
387
|
+
|
388
|
+
# Puts a string in the receive buffer of this device.
|
389
|
+
def puts(s)
|
390
|
+
s.each_byte { |b| put_byte(b) }
|
391
|
+
end
|
392
|
+
|
393
|
+
# Gets a string from the transmit buffer of this device.
|
394
|
+
# String length will be no more than n, but may be less
|
395
|
+
# if fewer characters exist in buffer.
|
396
|
+
#
|
397
|
+
# @param n Length of string to retrieve. If omitted the
|
398
|
+
# entire buffer will be returned.
|
399
|
+
def gets(n=nil)
|
400
|
+
s = ""
|
401
|
+
|
402
|
+
if n.nil?
|
403
|
+
until (c = get_byte()).nil?
|
404
|
+
s += c.chr
|
405
|
+
end
|
406
|
+
else
|
407
|
+
n.times do
|
408
|
+
c = get_byte()
|
409
|
+
break if c.nil?
|
410
|
+
|
411
|
+
s += c.chr
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
s
|
433
416
|
end
|
434
417
|
|
435
418
|
# Valid parameters for a SerialPort, along with those
|
436
|
-
# defined in [Zemu::Config::
|
419
|
+
# defined in [Zemu::Config::BusDevice].
|
437
420
|
def params
|
438
421
|
super + %w(in_port out_port ready_port)
|
439
422
|
end
|
@@ -443,7 +426,16 @@ module Zemu
|
|
443
426
|
#
|
444
427
|
# Represents a device with a sequence of sectors of a fixed size,
|
445
428
|
# which can be accessed via IO instructions as an IDE drive.
|
446
|
-
class BlockDrive <
|
429
|
+
class BlockDrive < BusDevice
|
430
|
+
# Mode for reading drive.
|
431
|
+
DRIVE_MODE_READ = 0x01
|
432
|
+
|
433
|
+
# Mode for writing drive.
|
434
|
+
DRIVE_MODE_WRITE = 0x02
|
435
|
+
|
436
|
+
# Uninitialised drive mode.
|
437
|
+
DRIVE_MODE_UNINIT = 0x00
|
438
|
+
|
447
439
|
# Constructor.
|
448
440
|
#
|
449
441
|
# Takes a block in which the parameters of the block drive
|
@@ -479,131 +471,110 @@ module Zemu
|
|
479
471
|
end
|
480
472
|
end
|
481
473
|
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
fseek(fptr, loaded_sector_#{name} * #{sector_size}, SEEK_SET);
|
513
|
-
fwrite(sector_data_#{name}, 1, #{sector_size}, fptr);
|
514
|
-
fclose(fptr);
|
515
|
-
}
|
516
|
-
|
517
|
-
zuint8 zemu_io_#{name}_readbyte(zuint32 sector, zuint32 offset)
|
518
|
-
{
|
519
|
-
internal_#{name}_load_sector(sector);
|
520
|
-
return sector_data_#{name}[offset];
|
521
|
-
}
|
522
|
-
eos
|
474
|
+
@lba_0 = 0
|
475
|
+
@lba_1 = 0
|
476
|
+
@lba_2 = 0
|
477
|
+
@lba_3 = 0
|
478
|
+
|
479
|
+
@drive_mode = DRIVE_MODE_UNINIT
|
480
|
+
@drive_status = 0b01000000
|
481
|
+
@sector_offset = 0
|
482
|
+
@sector_data = []
|
483
|
+
end
|
484
|
+
|
485
|
+
# IO bus read handler.
|
486
|
+
#
|
487
|
+
# Handles read access via the IO bus to this device.
|
488
|
+
#
|
489
|
+
# @param port The IO port being accessed.
|
490
|
+
#
|
491
|
+
# Returns the value read, or nil if the port does not
|
492
|
+
# correspond to this device.
|
493
|
+
def io_read(port)
|
494
|
+
if port == base_port
|
495
|
+
b = @sector_data.shift()
|
496
|
+
|
497
|
+
if @sector_data.empty?
|
498
|
+
@drive_status = 0b01000000
|
499
|
+
end
|
500
|
+
|
501
|
+
return b
|
502
|
+
elsif port == (base_port + 7)
|
503
|
+
return @drive_status
|
523
504
|
end
|
524
505
|
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
506
|
+
nil
|
507
|
+
end
|
508
|
+
|
509
|
+
# Get a sector number from the four LBA registers.
|
510
|
+
def get_sector()
|
511
|
+
sector = 0
|
512
|
+
sector |= @lba_0
|
513
|
+
sector |= @lba_1 << 8
|
514
|
+
sector |= @lba_2 << 16
|
515
|
+
sector |= @lba_3 << 24
|
516
|
+
|
517
|
+
sector
|
518
|
+
end
|
519
|
+
|
520
|
+
# Write sector data to the sector currently pointed
|
521
|
+
# to by the LBA registers.
|
522
|
+
def write_current_sector()
|
523
|
+
file_offset = get_sector() * sector_size
|
524
|
+
File.open(@initialize_from, "r+b") do |f|
|
525
|
+
f.seek(file_offset)
|
526
|
+
f.write(@sector_data.pack("C" * sector_size))
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
# Load sector data pointed to by LBA registers.
|
531
|
+
def load_sector()
|
532
|
+
file_offset = get_sector() * sector_size
|
533
|
+
File.open(@initialize_from, "rb") do |f|
|
534
|
+
f.seek(file_offset)
|
535
|
+
s = f.read(sector_size)
|
536
|
+
@sector_data = s.unpack("C" * sector_size)
|
542
537
|
end
|
538
|
+
end
|
543
539
|
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
sector |= (zuint32)lba_#{name}_1 << 8;
|
583
|
-
sector |= (zuint32)lba_#{name}_0;
|
584
|
-
|
585
|
-
internal_#{name}_load_sector(sector);
|
586
|
-
sector_data_#{name}_offset = 0;
|
587
|
-
|
588
|
-
drive_mode_#{name} = 0x00;
|
589
|
-
drive_status_#{name} = 0b00001000;
|
590
|
-
}
|
591
|
-
else if (value == 0x30)
|
592
|
-
{
|
593
|
-
zuint32 sector = 0;
|
594
|
-
sector |= (zuint32)lba_#{name}_3 << 24;
|
595
|
-
sector |= (zuint32)lba_#{name}_2 << 16;
|
596
|
-
sector |= (zuint32)lba_#{name}_1 << 8;
|
597
|
-
sector |= (zuint32)lba_#{name}_0;
|
598
|
-
|
599
|
-
internal_#{name}_load_sector(sector);
|
600
|
-
sector_data_#{name}_offset = 0;
|
601
|
-
|
602
|
-
drive_mode_#{name} = 0x01;
|
603
|
-
drive_status_#{name} = 0b00001000;
|
604
|
-
}
|
605
|
-
}
|
606
|
-
eos
|
540
|
+
# IO bus write handler.
|
541
|
+
#
|
542
|
+
# Handles write access via the IO bus to this device.
|
543
|
+
#
|
544
|
+
# @param port The IO port being accessed.
|
545
|
+
# @param value The value being written.
|
546
|
+
def io_write(port, value)
|
547
|
+
if port == base_port
|
548
|
+
if @drive_mode == DRIVE_MODE_WRITE
|
549
|
+
@sector_data << value
|
550
|
+
if @sector_data.size >= sector_size
|
551
|
+
write_current_sector()
|
552
|
+
@drive_status = 0b01000000
|
553
|
+
end
|
554
|
+
end
|
555
|
+
elsif port == (base_port + 3)
|
556
|
+
@lba_0 = (value & 0xff)
|
557
|
+
elsif port == (base_port + 4)
|
558
|
+
@lba_1 = (value & 0xff)
|
559
|
+
elsif port == (base_port + 5)
|
560
|
+
@lba_2 = (value & 0xff)
|
561
|
+
elsif port == (base_port + 6)
|
562
|
+
@lba_3 = (value & 0x1f)
|
563
|
+
elsif port == (base_port + 7)
|
564
|
+
# Read command.
|
565
|
+
if value == 0x20
|
566
|
+
load_sector()
|
567
|
+
|
568
|
+
@drive_mode = DRIVE_MODE_READ
|
569
|
+
@drive_status = 0b00001000
|
570
|
+
|
571
|
+
# Write command.
|
572
|
+
elsif value == 0x30
|
573
|
+
@sector_data = []
|
574
|
+
|
575
|
+
@drive_mode = DRIVE_MODE_WRITE
|
576
|
+
@drive_status = 0b00001000
|
577
|
+
end
|
607
578
|
end
|
608
579
|
end
|
609
580
|
|
@@ -637,15 +608,29 @@ eos
|
|
637
608
|
@initialize_from = file
|
638
609
|
end
|
639
610
|
|
611
|
+
# Read a byte at the given offset in a sector.
|
612
|
+
#
|
613
|
+
# @param sector The sector to read from.
|
614
|
+
# @param offset Offset in that sector to read.
|
615
|
+
#
|
616
|
+
# Returns the byte read from the file.
|
617
|
+
def read_byte(sector, offset)
|
618
|
+
file_offset = (sector * sector_size) + offset
|
619
|
+
File.open(@initialize_from, "rb") do |f|
|
620
|
+
f.seek(file_offset)
|
621
|
+
s = f.read(1)
|
622
|
+
return s.unpack("C")[0]
|
623
|
+
end
|
624
|
+
end
|
625
|
+
|
640
626
|
# Defines FFI API which will be available to the instance wrapper if this IO device is used.
|
641
627
|
def functions
|
642
628
|
[
|
643
|
-
{"name" => "zemu_io_#{name}_readbyte", "args" => [:uint32, :uint32], "return" => :uint8},
|
644
629
|
]
|
645
630
|
end
|
646
631
|
|
647
632
|
# Valid parameters for a BlockDrive, along with those
|
648
|
-
# defined in [Zemu::Config::
|
633
|
+
# defined in [Zemu::Config::BusDevice].
|
649
634
|
def params
|
650
635
|
super + %w(base_port sector_size num_sectors)
|
651
636
|
end
|
@@ -656,29 +641,45 @@ eos
|
|
656
641
|
# Represents a timer device, the period of which can be controlled
|
657
642
|
# by the CPU through an IO port. The timer generates an NMI once this
|
658
643
|
# period has expired. The timer can be reset via a control port.
|
659
|
-
class Timer <
|
660
|
-
|
661
|
-
|
644
|
+
class Timer < BusDevice
|
645
|
+
# Timer is running - count decrements once per clock cycle.
|
646
|
+
RUNNING = 0x01
|
662
647
|
|
663
|
-
|
664
|
-
|
665
|
-
"zuint8 io_#{name}_running = 0;\n"
|
666
|
-
end
|
648
|
+
# Timer is stopped - count does not change on clock cycle.
|
649
|
+
STOPPED = 0x00
|
667
650
|
|
668
|
-
|
669
|
-
|
651
|
+
def initialize
|
652
|
+
super
|
670
653
|
|
671
|
-
|
672
|
-
|
673
|
-
|
654
|
+
@count = 0
|
655
|
+
@running = false
|
656
|
+
end
|
657
|
+
|
658
|
+
# IO bus write handler.
|
659
|
+
#
|
660
|
+
# Handles write access via the IO bus to this device.
|
661
|
+
#
|
662
|
+
# @param port The IO port being accessed.
|
663
|
+
# @param value The value being written.
|
664
|
+
def io_write(port, value)
|
665
|
+
if port == count_port
|
666
|
+
@count = value
|
667
|
+
elsif port == control_port
|
668
|
+
@running = if value == 0 then STOPPED else RUNNING end
|
674
669
|
end
|
670
|
+
end
|
675
671
|
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
672
|
+
# Clock handler.
|
673
|
+
#
|
674
|
+
# Handles a clock cycle for this device.
|
675
|
+
# Sets NMI active if the count reaches 0.
|
676
|
+
def clock
|
677
|
+
if @running == RUNNING
|
678
|
+
if @count > 0
|
679
|
+
@count -= 1
|
680
|
+
else
|
681
|
+
nmi(true)
|
682
|
+
end
|
682
683
|
end
|
683
684
|
end
|
684
685
|
|
@@ -694,11 +695,8 @@ eos
|
|
694
695
|
return binding
|
695
696
|
end
|
696
697
|
|
697
|
-
# The
|
698
|
-
attr_reader :
|
699
|
-
|
700
|
-
# The IO devices of this configuration object.
|
701
|
-
attr_reader :io
|
698
|
+
# The bus devices of this configuration object.
|
699
|
+
attr_reader :devices
|
702
700
|
|
703
701
|
# Parameters accessible by this configuration object.
|
704
702
|
def params
|
@@ -737,8 +735,7 @@ eos
|
|
737
735
|
#
|
738
736
|
# @raise [Zemu::ConfigError] Raised if the +name+ parameter is not set, or contains whitespace.
|
739
737
|
def initialize
|
740
|
-
@
|
741
|
-
@io = []
|
738
|
+
@devices = []
|
742
739
|
|
743
740
|
super
|
744
741
|
|
@@ -753,16 +750,29 @@ eos
|
|
753
750
|
|
754
751
|
# Adds a new memory section to this configuration.
|
755
752
|
#
|
753
|
+
# Deprecated - retained only for backwards compatibility.
|
754
|
+
# Use add_device instead.
|
755
|
+
#
|
756
756
|
# @param [Zemu::Config::Memory] mem The memory object to add.
|
757
757
|
def add_memory(mem)
|
758
|
-
@
|
758
|
+
@devices << mem
|
759
759
|
end
|
760
760
|
|
761
761
|
# Adds a new IO device to this configuration.
|
762
762
|
#
|
763
|
-
#
|
763
|
+
# Deprecated - retained only for backwards compatibility.
|
764
|
+
# Use add_device instead.
|
765
|
+
#
|
766
|
+
# @param [Zemu::Config::BusDevice] io The IO device to add.
|
764
767
|
def add_io(io)
|
765
|
-
@
|
768
|
+
@devices << io
|
769
|
+
end
|
770
|
+
|
771
|
+
# Adds a new device to the bus for this configuration.
|
772
|
+
#
|
773
|
+
# @param [Zemu::Config::BusDevice] device The device to add.
|
774
|
+
def add_device(device)
|
775
|
+
@devices << device
|
766
776
|
end
|
767
777
|
end
|
768
778
|
|