zemu 0.4.2 → 1.1.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 +424 -384
- data/lib/zemu/debug.rb +39 -6
- data/lib/zemu/instance.rb +145 -8
- data/lib/zemu/interactive.rb +13 -12
- data/lib/zemu.rb +8 -27
- data/src/bus.c.erb +119 -0
- data/src/bus.h.erb +53 -0
- data/src/debug.c +1 -1
- 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
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
1
3
|
module Zemu
|
2
4
|
# Abstract configuration object.
|
3
5
|
# All configuration objects should inherit from this.
|
@@ -75,10 +77,123 @@ module Zemu
|
|
75
77
|
# @param [String] compiler The path to the compiler to be used for compiling the emulator executable.
|
76
78
|
#
|
77
79
|
class Config < ConfigObject
|
80
|
+
# Bus Device.
|
81
|
+
#
|
82
|
+
# Represents a device connected to the I/O
|
83
|
+
# or memory buses, or both.
|
84
|
+
class BusDevice < ConfigObject
|
85
|
+
# Constructor.
|
86
|
+
#
|
87
|
+
# This object should not be constructed directly.
|
88
|
+
def initialize
|
89
|
+
if self.class == Zemu::Config::BusDevice
|
90
|
+
raise NotImplementedError, "Cannot construct an instance of the abstract class Zemu::Config::BusDevice."
|
91
|
+
end
|
92
|
+
|
93
|
+
@nmi = false
|
94
|
+
@interrupt = false
|
95
|
+
|
96
|
+
super
|
97
|
+
end
|
98
|
+
|
99
|
+
# Parameters used for generating C code to
|
100
|
+
# implement this bus device.
|
101
|
+
def memory
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
# Setup to be performed on initialising the emulator
|
106
|
+
# instance.
|
107
|
+
def when_setup
|
108
|
+
""
|
109
|
+
end
|
110
|
+
|
111
|
+
# Memory bus write handler.
|
112
|
+
#
|
113
|
+
# Handles write access via the memory bus to this device.
|
114
|
+
#
|
115
|
+
# @param addr The address being accessed.
|
116
|
+
# @param value The value being written.
|
117
|
+
def mem_write(addr, value)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Memory bus read handler.
|
121
|
+
#
|
122
|
+
# Handles read access via the memory bus to this device.
|
123
|
+
#
|
124
|
+
# @param addr The address being accessed.
|
125
|
+
#
|
126
|
+
# Returns the value read, or nil if no value
|
127
|
+
# (e.g. if address falls outside range for this device).
|
128
|
+
def mem_read(addr)
|
129
|
+
nil
|
130
|
+
end
|
131
|
+
|
132
|
+
# IO bus write handler.
|
133
|
+
#
|
134
|
+
# Handles write access via the IO bus to this device.
|
135
|
+
#
|
136
|
+
# @param port The IO port being accessed.
|
137
|
+
# @param value The value being written.
|
138
|
+
def io_write(port, value)
|
139
|
+
end
|
140
|
+
|
141
|
+
# IO bus read handler.
|
142
|
+
#
|
143
|
+
# Handles read access via the IO bus to this device.
|
144
|
+
#
|
145
|
+
# @param port The IO port being accessed.
|
146
|
+
#
|
147
|
+
# Returns the value read from the port, or nil if no
|
148
|
+
# value (e.g. port does not correspond to this device).
|
149
|
+
def io_read(port)
|
150
|
+
nil
|
151
|
+
end
|
152
|
+
|
153
|
+
# Clock handler.
|
154
|
+
#
|
155
|
+
# Handles a clock cycle for this device.
|
156
|
+
# Deriving objects can use the nmi function
|
157
|
+
# to set the state of the non-maskable interrupt
|
158
|
+
# at each clock cycle.
|
159
|
+
def clock(cycles)
|
160
|
+
end
|
161
|
+
|
162
|
+
# FFI functions provided by this device.
|
163
|
+
def functions
|
164
|
+
[]
|
165
|
+
end
|
166
|
+
|
167
|
+
# Sets state of the NMI for this device.
|
168
|
+
def nmi(state)
|
169
|
+
@nmi = state
|
170
|
+
end
|
171
|
+
|
172
|
+
# Gets state of NMI for this device.
|
173
|
+
def nmi?
|
174
|
+
@nmi
|
175
|
+
end
|
176
|
+
|
177
|
+
# Sets state of INT for this device.
|
178
|
+
def interrupt(state)
|
179
|
+
@interrupt = state
|
180
|
+
end
|
181
|
+
|
182
|
+
# Gets state of INT for this device.
|
183
|
+
def interrupt?
|
184
|
+
@interrupt
|
185
|
+
end
|
186
|
+
|
187
|
+
# Parameters for a bus device.
|
188
|
+
def params
|
189
|
+
%w(name)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
78
193
|
# Memory object.
|
79
194
|
#
|
80
195
|
# This is an abstract class from which all other memory objects inherit.
|
81
|
-
class Memory <
|
196
|
+
class Memory < BusDevice
|
82
197
|
# Constructor.
|
83
198
|
#
|
84
199
|
# Do not use, as this is an abstract class. Use one of the subclasses instead.
|
@@ -107,15 +222,62 @@ module Zemu
|
|
107
222
|
end
|
108
223
|
end
|
109
224
|
|
110
|
-
#
|
225
|
+
# Is this memory read-only?
|
111
226
|
def readonly?
|
112
|
-
|
227
|
+
false
|
228
|
+
end
|
229
|
+
|
230
|
+
# Parameters used for generating C code to
|
231
|
+
# implement this memory block.
|
232
|
+
def memory
|
233
|
+
contents_initializer = ""
|
234
|
+
@contents.each_slice(32) do |c|
|
235
|
+
contents_initializer += c.map { |b| b.to_s }.join(", ")
|
236
|
+
contents_initializer += ",\n"
|
237
|
+
end
|
238
|
+
|
239
|
+
m = OpenStruct.new
|
240
|
+
|
241
|
+
m.setup = <<eos
|
242
|
+
zuint8 memory_#{name}[#{size}] =
|
243
|
+
{
|
244
|
+
#{contents_initializer}
|
245
|
+
};
|
246
|
+
eos
|
247
|
+
|
248
|
+
m.address = address
|
249
|
+
m.size = size
|
250
|
+
m.access_read = "memory_#{name}"
|
251
|
+
m.access_write = "memory_#{name}"
|
252
|
+
|
253
|
+
m
|
254
|
+
end
|
255
|
+
|
256
|
+
# Memory bus read handler.
|
257
|
+
#
|
258
|
+
# Handles read access via the memory bus to this device.
|
259
|
+
#
|
260
|
+
# @param addr The address being accessed.
|
261
|
+
#
|
262
|
+
# Returns the value read, or nil if no value
|
263
|
+
# (e.g. if address falls outside range for this device).
|
264
|
+
def mem_read(addr)
|
265
|
+
nil
|
266
|
+
end
|
267
|
+
|
268
|
+
# Memory bus write handler.
|
269
|
+
#
|
270
|
+
# Handles write access via the memory bus to this device.
|
271
|
+
#
|
272
|
+
# @param addr The address being accessed.
|
273
|
+
# @param value The value being written.
|
274
|
+
def mem_write(addr, value)
|
113
275
|
end
|
114
276
|
|
115
277
|
# Valid parameters for this object.
|
116
278
|
# Should be extended by subclasses but NOT REPLACED.
|
117
279
|
def params
|
118
|
-
|
280
|
+
super + %w(address size)
|
119
281
|
end
|
120
282
|
|
121
283
|
# Reads the contents of a file in binary format and
|
@@ -155,8 +317,14 @@ module Zemu
|
|
155
317
|
super
|
156
318
|
end
|
157
319
|
|
320
|
+
# Is this memory block readonly?
|
158
321
|
def readonly?
|
159
|
-
|
322
|
+
true
|
323
|
+
end
|
324
|
+
|
325
|
+
# Memory write handler.
|
326
|
+
def mem_write(addr, port)
|
327
|
+
# Does nothing - cannot write to read-only memory.
|
160
328
|
end
|
161
329
|
end
|
162
330
|
|
@@ -165,169 +333,13 @@ module Zemu
|
|
165
333
|
# Represents a block of memory which can be read and written.
|
166
334
|
class RAM < Memory
|
167
335
|
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
336
|
|
325
337
|
# Serial Input/Output object
|
326
338
|
#
|
327
339
|
# Represents a serial connection between the emulated CPU
|
328
340
|
# and the host machine, with input and output mapped to Z80 I/O
|
329
341
|
# ports.
|
330
|
-
class SerialPort <
|
342
|
+
class SerialPort < BusDevice
|
331
343
|
# Constructor.
|
332
344
|
#
|
333
345
|
# Takes a block in which the parameters of the serial port
|
@@ -348,92 +360,93 @@ module Zemu
|
|
348
360
|
def initialize
|
349
361
|
super
|
350
362
|
|
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
|
363
|
+
@buffer_tx = []
|
364
|
+
@buffer_rx = []
|
365
|
+
end
|
399
366
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
367
|
+
# IO bus read handler.
|
368
|
+
#
|
369
|
+
# Handles read access via the IO bus to this device.
|
370
|
+
#
|
371
|
+
# @param port The IO port being accessed.
|
372
|
+
#
|
373
|
+
# Returns the value read, or nil if the port does not
|
374
|
+
# correspond to this device.
|
375
|
+
def io_read(port)
|
376
|
+
if port == in_port
|
377
|
+
return @buffer_rx.shift()
|
378
|
+
elsif port == ready_port
|
379
|
+
if @buffer_rx.empty?
|
380
|
+
return 0
|
381
|
+
else
|
382
|
+
return 1
|
383
|
+
end
|
416
384
|
end
|
417
385
|
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
386
|
+
nil
|
387
|
+
end
|
388
|
+
|
389
|
+
# IO bus write handler.
|
390
|
+
#
|
391
|
+
# Handles write access via the IO bus to this device.
|
392
|
+
#
|
393
|
+
# @param port The IO port being accessed.
|
394
|
+
# @param value The value being written.
|
395
|
+
def io_write(port, value)
|
396
|
+
if port == out_port
|
397
|
+
@buffer_tx << value
|
423
398
|
end
|
424
399
|
end
|
425
400
|
|
426
|
-
#
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
401
|
+
# Gets number of bytes transmitted by the CPU,
|
402
|
+
# but not yet read from this device.
|
403
|
+
def transmitted_count
|
404
|
+
@buffer_tx.size
|
405
|
+
end
|
406
|
+
|
407
|
+
# Gets a byte transmitted by the CPU, or nil
|
408
|
+
# if transmit buffer is empty.
|
409
|
+
def get_byte
|
410
|
+
@buffer_tx.shift()
|
411
|
+
end
|
412
|
+
|
413
|
+
# Puts a byte in the receive buffer of this device.
|
414
|
+
def put_byte(b)
|
415
|
+
@buffer_rx << b
|
416
|
+
end
|
417
|
+
|
418
|
+
# Puts a string in the receive buffer of this device.
|
419
|
+
def puts(s)
|
420
|
+
s.each_byte { |b| put_byte(b) }
|
421
|
+
end
|
422
|
+
|
423
|
+
# Gets a string from the transmit buffer of this device.
|
424
|
+
# String length will be no more than n, but may be less
|
425
|
+
# if fewer characters exist in buffer.
|
426
|
+
#
|
427
|
+
# @param n Length of string to retrieve. If omitted the
|
428
|
+
# entire buffer will be returned.
|
429
|
+
def gets(n=nil)
|
430
|
+
s = ""
|
431
|
+
|
432
|
+
if n.nil?
|
433
|
+
until (c = get_byte()).nil?
|
434
|
+
s += c.chr
|
435
|
+
end
|
436
|
+
else
|
437
|
+
n.times do
|
438
|
+
c = get_byte()
|
439
|
+
break if c.nil?
|
440
|
+
|
441
|
+
s += c.chr
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
s
|
433
446
|
end
|
434
447
|
|
435
448
|
# Valid parameters for a SerialPort, along with those
|
436
|
-
# defined in [Zemu::Config::
|
449
|
+
# defined in [Zemu::Config::BusDevice].
|
437
450
|
def params
|
438
451
|
super + %w(in_port out_port ready_port)
|
439
452
|
end
|
@@ -443,7 +456,16 @@ module Zemu
|
|
443
456
|
#
|
444
457
|
# Represents a device with a sequence of sectors of a fixed size,
|
445
458
|
# which can be accessed via IO instructions as an IDE drive.
|
446
|
-
class BlockDrive <
|
459
|
+
class BlockDrive < BusDevice
|
460
|
+
# Mode for reading drive.
|
461
|
+
DRIVE_MODE_READ = 0x01
|
462
|
+
|
463
|
+
# Mode for writing drive.
|
464
|
+
DRIVE_MODE_WRITE = 0x02
|
465
|
+
|
466
|
+
# Uninitialised drive mode.
|
467
|
+
DRIVE_MODE_UNINIT = 0x00
|
468
|
+
|
447
469
|
# Constructor.
|
448
470
|
#
|
449
471
|
# Takes a block in which the parameters of the block drive
|
@@ -479,131 +501,110 @@ module Zemu
|
|
479
501
|
end
|
480
502
|
end
|
481
503
|
|
482
|
-
|
483
|
-
|
484
|
-
|
504
|
+
@lba_0 = 0
|
505
|
+
@lba_1 = 0
|
506
|
+
@lba_2 = 0
|
507
|
+
@lba_3 = 0
|
485
508
|
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
509
|
+
@drive_mode = DRIVE_MODE_UNINIT
|
510
|
+
@drive_status = 0b01000000
|
511
|
+
@sector_offset = 0
|
512
|
+
@sector_data = []
|
513
|
+
end
|
491
514
|
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
515
|
+
# IO bus read handler.
|
516
|
+
#
|
517
|
+
# Handles read access via the IO bus to this device.
|
518
|
+
#
|
519
|
+
# @param port The IO port being accessed.
|
520
|
+
#
|
521
|
+
# Returns the value read, or nil if the port does not
|
522
|
+
# correspond to this device.
|
523
|
+
def io_read(port)
|
524
|
+
if port == base_port
|
525
|
+
b = @sector_data.shift()
|
526
|
+
|
527
|
+
if @sector_data.empty?
|
528
|
+
@drive_status = 0b01000000
|
529
|
+
end
|
496
530
|
|
497
|
-
|
498
|
-
|
499
|
-
|
531
|
+
return b
|
532
|
+
elsif port == (base_port + 7)
|
533
|
+
return @drive_status
|
534
|
+
end
|
500
535
|
|
501
|
-
|
502
|
-
|
503
|
-
fread(sector_data_#{name}, #{sector_size}, 1, fptr);
|
504
|
-
fclose(fptr);
|
536
|
+
nil
|
537
|
+
end
|
505
538
|
|
506
|
-
|
507
|
-
|
539
|
+
# Get a sector number from the four LBA registers.
|
540
|
+
def get_sector()
|
541
|
+
sector = 0
|
542
|
+
sector |= @lba_0
|
543
|
+
sector |= @lba_1 << 8
|
544
|
+
sector |= @lba_2 << 16
|
545
|
+
sector |= @lba_3 << 24
|
508
546
|
|
509
|
-
|
510
|
-
|
511
|
-
FILE * fptr = fopen("#{@initialize_from}", "r+b");
|
512
|
-
fseek(fptr, loaded_sector_#{name} * #{sector_size}, SEEK_SET);
|
513
|
-
fwrite(sector_data_#{name}, 1, #{sector_size}, fptr);
|
514
|
-
fclose(fptr);
|
515
|
-
}
|
547
|
+
sector
|
548
|
+
end
|
516
549
|
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
550
|
+
# Write sector data to the sector currently pointed
|
551
|
+
# to by the LBA registers.
|
552
|
+
def write_current_sector()
|
553
|
+
file_offset = get_sector() * sector_size
|
554
|
+
File.open(@initialize_from, "r+b") do |f|
|
555
|
+
f.seek(file_offset)
|
556
|
+
f.write(@sector_data.pack("C" * sector_size))
|
523
557
|
end
|
558
|
+
end
|
524
559
|
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
{
|
533
|
-
drive_status_#{name} = 0b01000000;
|
534
|
-
}
|
535
|
-
return b;
|
536
|
-
}
|
537
|
-
else if (port == #{base_port+7})
|
538
|
-
{
|
539
|
-
return drive_status_#{name};
|
540
|
-
}
|
541
|
-
eos
|
560
|
+
# Load sector data pointed to by LBA registers.
|
561
|
+
def load_sector()
|
562
|
+
file_offset = get_sector() * sector_size
|
563
|
+
File.open(@initialize_from, "rb") do |f|
|
564
|
+
f.seek(file_offset)
|
565
|
+
s = f.read(sector_size)
|
566
|
+
@sector_data = s.unpack("C" * sector_size)
|
542
567
|
end
|
568
|
+
end
|
543
569
|
|
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
|
570
|
+
# IO bus write handler.
|
571
|
+
#
|
572
|
+
# Handles write access via the IO bus to this device.
|
573
|
+
#
|
574
|
+
# @param port The IO port being accessed.
|
575
|
+
# @param value The value being written.
|
576
|
+
def io_write(port, value)
|
577
|
+
if port == base_port
|
578
|
+
if @drive_mode == DRIVE_MODE_WRITE
|
579
|
+
@sector_data << value
|
580
|
+
if @sector_data.size >= sector_size
|
581
|
+
write_current_sector()
|
582
|
+
@drive_status = 0b01000000
|
583
|
+
end
|
584
|
+
end
|
585
|
+
elsif port == (base_port + 3)
|
586
|
+
@lba_0 = (value & 0xff)
|
587
|
+
elsif port == (base_port + 4)
|
588
|
+
@lba_1 = (value & 0xff)
|
589
|
+
elsif port == (base_port + 5)
|
590
|
+
@lba_2 = (value & 0xff)
|
591
|
+
elsif port == (base_port + 6)
|
592
|
+
@lba_3 = (value & 0x1f)
|
593
|
+
elsif port == (base_port + 7)
|
594
|
+
# Read command.
|
595
|
+
if value == 0x20
|
596
|
+
load_sector()
|
597
|
+
|
598
|
+
@drive_mode = DRIVE_MODE_READ
|
599
|
+
@drive_status = 0b00001000
|
600
|
+
|
601
|
+
# Write command.
|
602
|
+
elsif value == 0x30
|
603
|
+
@sector_data = []
|
604
|
+
|
605
|
+
@drive_mode = DRIVE_MODE_WRITE
|
606
|
+
@drive_status = 0b00001000
|
607
|
+
end
|
607
608
|
end
|
608
609
|
end
|
609
610
|
|
@@ -637,15 +638,29 @@ eos
|
|
637
638
|
@initialize_from = file
|
638
639
|
end
|
639
640
|
|
641
|
+
# Read a byte at the given offset in a sector.
|
642
|
+
#
|
643
|
+
# @param sector The sector to read from.
|
644
|
+
# @param offset Offset in that sector to read.
|
645
|
+
#
|
646
|
+
# Returns the byte read from the file.
|
647
|
+
def read_byte(sector, offset)
|
648
|
+
file_offset = (sector * sector_size) + offset
|
649
|
+
File.open(@initialize_from, "rb") do |f|
|
650
|
+
f.seek(file_offset)
|
651
|
+
s = f.read(1)
|
652
|
+
return s.unpack("C")[0]
|
653
|
+
end
|
654
|
+
end
|
655
|
+
|
640
656
|
# Defines FFI API which will be available to the instance wrapper if this IO device is used.
|
641
657
|
def functions
|
642
658
|
[
|
643
|
-
{"name" => "zemu_io_#{name}_readbyte", "args" => [:uint32, :uint32], "return" => :uint8},
|
644
659
|
]
|
645
660
|
end
|
646
661
|
|
647
662
|
# Valid parameters for a BlockDrive, along with those
|
648
|
-
# defined in [Zemu::Config::
|
663
|
+
# defined in [Zemu::Config::BusDevice].
|
649
664
|
def params
|
650
665
|
super + %w(base_port sector_size num_sectors)
|
651
666
|
end
|
@@ -656,29 +671,45 @@ eos
|
|
656
671
|
# Represents a timer device, the period of which can be controlled
|
657
672
|
# by the CPU through an IO port. The timer generates an NMI once this
|
658
673
|
# period has expired. The timer can be reset via a control port.
|
659
|
-
class Timer <
|
660
|
-
|
661
|
-
|
674
|
+
class Timer < BusDevice
|
675
|
+
# Timer is running - count decrements once per clock cycle.
|
676
|
+
RUNNING = 0x01
|
662
677
|
|
663
|
-
|
664
|
-
|
665
|
-
"zuint8 io_#{name}_running = 0;\n"
|
666
|
-
end
|
678
|
+
# Timer is stopped - count does not change on clock cycle.
|
679
|
+
STOPPED = 0x00
|
667
680
|
|
668
|
-
|
669
|
-
|
681
|
+
def initialize
|
682
|
+
super
|
670
683
|
|
671
|
-
|
672
|
-
|
673
|
-
|
684
|
+
@count = 0
|
685
|
+
@running = false
|
686
|
+
end
|
687
|
+
|
688
|
+
# IO bus write handler.
|
689
|
+
#
|
690
|
+
# Handles write access via the IO bus to this device.
|
691
|
+
#
|
692
|
+
# @param port The IO port being accessed.
|
693
|
+
# @param value The value being written.
|
694
|
+
def io_write(port, value)
|
695
|
+
if port == count_port
|
696
|
+
@count = value
|
697
|
+
elsif port == control_port
|
698
|
+
@running = if value == 0 then STOPPED else RUNNING end
|
674
699
|
end
|
700
|
+
end
|
675
701
|
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
702
|
+
# Clock handler.
|
703
|
+
#
|
704
|
+
# Handles a clock cycle for this device.
|
705
|
+
# Sets NMI active if the count reaches 0.
|
706
|
+
def clock(cycles)
|
707
|
+
if @running == RUNNING
|
708
|
+
if @count > 0
|
709
|
+
@count -= cycles
|
710
|
+
else
|
711
|
+
nmi(true)
|
712
|
+
end
|
682
713
|
end
|
683
714
|
end
|
684
715
|
|
@@ -694,11 +725,8 @@ eos
|
|
694
725
|
return binding
|
695
726
|
end
|
696
727
|
|
697
|
-
# The
|
698
|
-
attr_reader :
|
699
|
-
|
700
|
-
# The IO devices of this configuration object.
|
701
|
-
attr_reader :io
|
728
|
+
# The bus devices of this configuration object.
|
729
|
+
attr_reader :devices
|
702
730
|
|
703
731
|
# Parameters accessible by this configuration object.
|
704
732
|
def params
|
@@ -737,8 +765,7 @@ eos
|
|
737
765
|
#
|
738
766
|
# @raise [Zemu::ConfigError] Raised if the +name+ parameter is not set, or contains whitespace.
|
739
767
|
def initialize
|
740
|
-
@
|
741
|
-
@io = []
|
768
|
+
@devices = []
|
742
769
|
|
743
770
|
super
|
744
771
|
|
@@ -753,16 +780,29 @@ eos
|
|
753
780
|
|
754
781
|
# Adds a new memory section to this configuration.
|
755
782
|
#
|
783
|
+
# Deprecated - retained only for backwards compatibility.
|
784
|
+
# Use add_device instead.
|
785
|
+
#
|
756
786
|
# @param [Zemu::Config::Memory] mem The memory object to add.
|
757
787
|
def add_memory(mem)
|
758
|
-
@
|
788
|
+
@devices << mem
|
759
789
|
end
|
760
790
|
|
761
791
|
# Adds a new IO device to this configuration.
|
762
792
|
#
|
763
|
-
#
|
793
|
+
# Deprecated - retained only for backwards compatibility.
|
794
|
+
# Use add_device instead.
|
795
|
+
#
|
796
|
+
# @param [Zemu::Config::BusDevice] io The IO device to add.
|
764
797
|
def add_io(io)
|
765
|
-
@
|
798
|
+
@devices << io
|
799
|
+
end
|
800
|
+
|
801
|
+
# Adds a new device to the bus for this configuration.
|
802
|
+
#
|
803
|
+
# @param [Zemu::Config::BusDevice] device The device to add.
|
804
|
+
def add_device(device)
|
805
|
+
@devices << device
|
766
806
|
end
|
767
807
|
end
|
768
808
|
|