zemu 0.4.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/zemu/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 < ConfigObject
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
- # @return [Boolean] true if this memory section is readonly, false otherwise.
206
+ # Is this memory read-only?
111
207
  def readonly?
112
- return false
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
- return %w(name address size)
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
- return true
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 < IOPort
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
- when_setup do
352
- "SerialBuffer io_#{name}_buffer_master = { .head = 0, .tail = 0 };\n" +
353
- "SerialBuffer io_#{name}_buffer_slave = { .head = 0, .tail = 0 };\n" +
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
- when_read do
401
- "if (port == #{in_port})\n" +
402
- "{\n" +
403
- " return zemu_io_#{name}_slave_gets();\n" +
404
- "}\n" +
405
- "else if (port == #{ready_port})\n" +
406
- "{\n" +
407
- " if (io_#{name}_buffer_master.head == io_#{name}_buffer_master.tail)\n" +
408
- " {\n" +
409
- " return 0;\n" +
410
- " }\n" +
411
- " else\n" +
412
- " {\n" +
413
- " return 1;\n" +
414
- " }\n" +
415
- "}\n"
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
- when_write do
419
- "if (port == #{out_port})\n" +
420
- "{\n" +
421
- " zemu_io_#{name}_slave_puts(value);\n" +
422
- "}\n"
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
- # Defines FFI API which will be available to the instance wrapper if this IO device is used.
427
- def functions
428
- [
429
- {"name" => "zemu_io_#{name}_master_puts".to_sym, "args" => [:uint8], "return" => :void},
430
- {"name" => "zemu_io_#{name}_master_gets".to_sym, "args" => [], "return" => :uint8},
431
- {"name" => "zemu_io_#{name}_buffer_size".to_sym, "args" => [], "return" => :uint64}
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::IOPort].
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 < IOPort
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
- when_setup do
483
- <<-eos
484
- #include <stdio.h>
485
-
486
- zuint8 sector_data_#{name}[#{sector_size}];
487
- zuint32 sector_data_#{name}_offset;
488
- zuint32 loaded_sector_#{name} = Z_UINT32_MAXIMUM;
489
- zuint8 drive_mode_#{name};
490
- zuint8 drive_status_#{name} = 0b01000000;
491
-
492
- zuint8 lba_#{name}_0;
493
- zuint8 lba_#{name}_1;
494
- zuint8 lba_#{name}_2;
495
- zuint8 lba_#{name}_3;
496
-
497
- void internal_#{name}_load_sector(zuint32 sector)
498
- {
499
- if (loaded_sector_#{name} == sector) return;
500
-
501
- FILE * fptr = fopen("#{@initialize_from}", "rb");
502
- fseek(fptr, sector * #{sector_size}, SEEK_SET);
503
- fread(sector_data_#{name}, #{sector_size}, 1, fptr);
504
- fclose(fptr);
505
-
506
- loaded_sector_#{name} = sector;
507
- }
508
-
509
- void internal_#{name}_write_current_sector()
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
- }
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
- when_read do
526
- <<-eos
527
- if (port == #{base_port})
528
- {
529
- zuint8 b = sector_data_#{name}[sector_data_#{name}_offset];
530
- sector_data_#{name}_offset++;
531
- if (sector_data_#{name}_offset >= #{sector_size})
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
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
- when_write do
545
- <<-eos
546
- if (port == #{base_port})
547
- {
548
- if (drive_mode_#{name} == 0x01)
549
- {
550
- sector_data_#{name}[sector_data_#{name}_offset] = value;
551
- sector_data_#{name}_offset++;
552
- if (sector_data_#{name}_offset >= #{sector_size})
553
- {
554
- internal_#{name}_write_current_sector();
555
- drive_status_#{name} = 0b01000000;
556
- }
557
- }
558
- }
559
- else if (port == #{base_port+3})
560
- {
561
- lba_#{name}_0 = value;
562
- }
563
- else if (port == #{base_port+4})
564
- {
565
- lba_#{name}_1 = value;
566
- }
567
- else if (port == #{base_port+5})
568
- {
569
- lba_#{name}_2 = value;
570
- }
571
- else if (port == #{base_port+6})
572
- {
573
- lba_#{name}_3 = value & 0b00011111;
574
- }
575
- else if (port == #{base_port+7})
576
- {
577
- if (value == 0x20)
578
- {
579
- zuint32 sector = 0;
580
- sector |= (zuint32)lba_#{name}_3 << 24;
581
- sector |= (zuint32)lba_#{name}_2 << 16;
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::IOPort].
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 < IOPort
660
- def initialize
661
- super
644
+ class Timer < BusDevice
645
+ # Timer is running - count decrements once per clock cycle.
646
+ RUNNING = 0x01
662
647
 
663
- when_setup do
664
- "zuint8 io_#{name}_count;\n" +
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
- when_read do
669
- end
651
+ def initialize
652
+ super
670
653
 
671
- when_write do
672
- "if (port == #{count_port}) io_#{name}_count = value;\n" +
673
- "else if (port == #{control_port}) io_#{name}_running = value;\n"
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
- when_clock do
677
- "if (io_#{name}_running)\n" +
678
- "{\n" +
679
- " if (io_#{name}_count > 0) io_#{name}_count--;\n" +
680
- " else zemu_io_nmi(instance);\n" +
681
- "}\n"
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 memory sections of this configuration object.
698
- attr_reader :memory
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
- @memory = []
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
- @memory << mem
758
+ @devices << mem
759
759
  end
760
760
 
761
761
  # Adds a new IO device to this configuration.
762
762
  #
763
- # @param [Zemu::Config::IOPort] io The IO device to add.
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
- @io << io
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