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.
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