zemu 0.6.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 62fdf5d376fa7ea32170f7f2ae62af17f3e96f8714313af513aabf86ef56fc71
4
- data.tar.gz: 3adcc264bc22f519fff6bb0604051058b198ef3a200c3c5b118f1de1796d84ce
3
+ metadata.gz: 31940c64874b7aaeb203199df84fc7cdb7cedc8736728e147e2fda5557675d83
4
+ data.tar.gz: 7a236b15b5075d866aaedaa2793b535e5c1bf7e3fb279c4a7b3702f93ec6e719
5
5
  SHA512:
6
- metadata.gz: 4bd2cbaf5099cd0874a5d59b519aaf7371e0c3b537fc7c57d70cb82f9003c751362dbed28aa1d5187736932f8d498746d95d1a404f1b43ff3cee571a1248c23a
7
- data.tar.gz: 9a320b4fbaddf08a24ba5ef58452ab0750d9fec1476bc8b389d43fc2b623364ab82cd037e61560828740a3e01adfa7647093eefad14d72ee5f6ec0152beb11fe
6
+ metadata.gz: bd05cbd8eb7a42df7afa9f4c62957e138dcdef1b02c4c458ffdecf8156f64972664eaf2d54d48741d198dc9ee8b0a5f97352c1fcd7c8a4fa6736da50626a60fd
7
+ data.tar.gz: ac3fdb4305fc84ae7766db21351321c9c4e7c3041c3a6549e9c416be03666ac4f56d8275248ae707429941f34a2077596aadf8e9fee45290300f20f7fe4baf7f
data/lib/zemu/config.rb CHANGED
@@ -88,6 +88,8 @@ module Zemu
88
88
  raise NotImplementedError, "Cannot construct an instance of the abstract class Zemu::Config::BusDevice."
89
89
  end
90
90
 
91
+ @nmi = false
92
+
91
93
  super
92
94
  end
93
95
 
@@ -99,42 +101,53 @@ module Zemu
99
101
 
100
102
  # Memory bus write handler.
101
103
  #
102
- # Defines C code generated for handling memory
103
- # writes for this device.
104
- def when_mem_write
105
- ""
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)
106
109
  end
107
110
 
108
111
  # Memory bus read handler.
109
112
  #
110
- # Defines C code generated for handling memory
111
- # reads for this device.
112
- def when_mem_read
113
- ""
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
114
121
  end
115
122
 
116
123
  # IO bus write handler.
117
124
  #
118
- # Defines C code generated for handling IO
119
- # writes for this device.
120
- def when_io_write
121
- ""
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)
122
130
  end
123
131
 
124
132
  # IO bus read handler.
125
133
  #
126
- # Defines C code generated for handling IO
127
- # reads for this device.
128
- def when_io_read
129
- ""
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
130
142
  end
131
143
 
132
144
  # Clock handler.
133
145
  #
134
- # Defines C code which executes for every
135
- # clock cycle.
136
- def when_clock
137
- ""
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
138
151
  end
139
152
 
140
153
  # FFI functions provided by this device.
@@ -142,6 +155,16 @@ module Zemu
142
155
  []
143
156
  end
144
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
+
145
168
  # Parameters for a bus device.
146
169
  def params
147
170
  %w(name)
@@ -185,39 +208,40 @@ module Zemu
185
208
  false
186
209
  end
187
210
 
188
- # Defines generated C to declare this memory block.
189
- def when_setup
190
- init_array = []
191
- contents.each_with_index do |b, i|
192
- init_array << ((i % 16 == 0) ? "\n " : "") + ("0x%02x, " % b)
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]
193
225
  end
194
226
 
195
- <<-eos
196
- /* Initialization memory block "#{name}" */
197
- #{if self.readonly? then "const" else "" end} zuint8 zemu_memory_block_#{name}[0x#{size.to_s(16)}] =
198
- {#{init_array.join("")}
199
- };
200
- eos
201
- end
202
-
203
- # Defines generated C to handle reading this memory block.
204
- def when_mem_read
205
- <<-eos
206
- if (address_32 >= 0x#{address.to_s(16)} && address_32 < 0x#{(address + size).to_s(16)})
207
- {
208
- return zemu_memory_block_#{name}[address_32 - 0x#{address.to_s(16)}];
209
- }
210
- eos
227
+ # Otherwise return nil - address does not correspond
228
+ # to this memory block.
229
+ nil
211
230
  end
212
231
 
213
- # Defines generated C to handle writing to this memory block.
214
- def when_mem_write
215
- <<-eos
216
- if (address_32 >= 0x#{address.to_s(16)} && address_32 < 0x#{(address + size).to_s(16)})
217
- {
218
- zemu_memory_block_#{name}[address_32 - 0x#{address.to_s(16)}] = value;
219
- }
220
- eos
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
221
245
  end
222
246
 
223
247
  # Valid parameters for this object.
@@ -268,12 +292,9 @@ eos
268
292
  true
269
293
  end
270
294
 
271
- # Defines generated C to handle writing to this
272
- # memory block. Because this block is read-only,
273
- # no code is generated to handle writes.
274
- def when_mem_write
275
- # Cannot write to read-only memory.
276
- ""
295
+ # Memory write handler.
296
+ def mem_write(addr, port)
297
+ # Does nothing - cannot write to read-only memory.
277
298
  end
278
299
  end
279
300
 
@@ -308,93 +329,90 @@ eos
308
329
  #
309
330
  def initialize
310
331
  super
332
+
333
+ @buffer_tx = []
334
+ @buffer_rx = []
311
335
  end
312
336
 
313
- # Defines generated C to declare the serial device.
314
- def when_setup
315
- "SerialBuffer io_#{name}_buffer_master = { .head = 0, .tail = 0 };\n" +
316
- "SerialBuffer io_#{name}_buffer_slave = { .head = 0, .tail = 0 };\n" +
317
- "\n" +
318
- "zusize zemu_io_#{name}_buffer_size(void)\n" +
319
- "{\n" +
320
- " zusize start = io_#{name}_buffer_slave.head;\n" +
321
- " zusize end = io_#{name}_buffer_slave.tail\n;" +
322
- " if (end < start) end += ZEMU_IO_SERIAL_BUFFER_SIZE;\n" +
323
- " return end - start;\n" +
324
- "}\n" +
325
- "\n" +
326
- "void zemu_io_#{name}_slave_puts(zuint8 val)\n" +
327
- "{\n" +
328
- " io_#{name}_buffer_slave.buffer[io_#{name}_buffer_slave.tail] = val;\n" +
329
- " io_#{name}_buffer_slave.tail++;\n" +
330
- " if (io_#{name}_buffer_slave.tail >= ZEMU_IO_SERIAL_BUFFER_SIZE)\n" +
331
- " io_#{name}_buffer_slave.tail = 0;\n" +
332
- "}\n" +
333
- "\n" +
334
- "zuint8 zemu_io_#{name}_slave_gets(void)\n" +
335
- "{\n" +
336
- " zuint8 val = io_#{name}_buffer_master.buffer[io_#{name}_buffer_master.head];\n" +
337
- " io_#{name}_buffer_master.head++;\n" +
338
- " if (io_#{name}_buffer_master.head >= ZEMU_IO_SERIAL_BUFFER_SIZE)\n" +
339
- " io_#{name}_buffer_master.head = 0;\n" +
340
- "\n" +
341
- " return val;\n" +
342
- "}\n" +
343
- "\n" +
344
- "void zemu_io_#{name}_master_puts(zuint8 val)\n" +
345
- "{\n" +
346
- " io_#{name}_buffer_master.buffer[io_#{name}_buffer_master.tail] = val;\n" +
347
- " io_#{name}_buffer_master.tail++;\n" +
348
- " if (io_#{name}_buffer_master.tail >= ZEMU_IO_SERIAL_BUFFER_SIZE)\n" +
349
- " io_#{name}_buffer_master.tail = 0;\n" +
350
- "}\n" +
351
- "\n" +
352
- "zuint8 zemu_io_#{name}_master_gets(void)\n" +
353
- "{\n" +
354
- " zuint8 val = io_#{name}_buffer_slave.buffer[io_#{name}_buffer_slave.head];\n" +
355
- " io_#{name}_buffer_slave.head++;\n" +
356
- " if (io_#{name}_buffer_slave.head >= ZEMU_IO_SERIAL_BUFFER_SIZE)\n" +
357
- " io_#{name}_buffer_slave.head = 0;\n" +
358
- "\n" +
359
- " return val;\n" +
360
- "}\n"
361
- end
362
-
363
- # Defines generated C to handle reading from serial port's
364
- # registers.
365
- def when_io_read
366
- "if (port == #{in_port})\n" +
367
- "{\n" +
368
- " return zemu_io_#{name}_slave_gets();\n" +
369
- "}\n" +
370
- "else if (port == #{ready_port})\n" +
371
- "{\n" +
372
- " if (io_#{name}_buffer_master.head == io_#{name}_buffer_master.tail)\n" +
373
- " {\n" +
374
- " return 0;\n" +
375
- " }\n" +
376
- " else\n" +
377
- " {\n" +
378
- " return 1;\n" +
379
- " }\n" +
380
- "}\n"
381
- end
382
-
383
- # Defines generated C to handle writing to the serial port's registers.
384
- def when_io_write
385
- "if (port == #{out_port})\n" +
386
- "{\n" +
387
- " zemu_io_#{name}_slave_puts(value);\n" +
388
- "}\n"
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
354
+ end
355
+
356
+ nil
389
357
  end
390
358
 
391
- # Defines FFI API which will be available to the instance wrapper if this IO device is used.
392
- def functions
393
- [
394
- {"name" => "zemu_io_#{name}_master_puts".to_sym, "args" => [:uint8], "return" => :void},
395
- {"name" => "zemu_io_#{name}_master_gets".to_sym, "args" => [], "return" => :uint8},
396
- {"name" => "zemu_io_#{name}_buffer_size".to_sym, "args" => [], "return" => :uint64}
397
- ]
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
368
+ end
369
+ end
370
+
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
398
416
  end
399
417
 
400
418
  # Valid parameters for a SerialPort, along with those
@@ -409,6 +427,15 @@ eos
409
427
  # Represents a device with a sequence of sectors of a fixed size,
410
428
  # which can be accessed via IO instructions as an IDE drive.
411
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
+
412
439
  # Constructor.
413
440
  #
414
441
  # Takes a block in which the parameters of the block drive
@@ -443,138 +470,112 @@ eos
443
470
  raise RangeError, "Initialization file for Zemu::Config::BlockDrive '#{name}' is of wrong size."
444
471
  end
445
472
  end
473
+
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 = []
446
483
  end
447
484
 
448
- # Defines generated C to declare the block device.
449
- def when_setup
450
- <<-eos
451
- #include <stdio.h>
452
-
453
- zuint8 sector_data_#{name}[#{sector_size}];
454
- zuint32 sector_data_#{name}_offset;
455
- zuint32 loaded_sector_#{name} = Z_UINT32_MAXIMUM;
456
- zuint8 drive_mode_#{name};
457
- zuint8 drive_status_#{name} = 0b01000000;
458
-
459
- zuint8 lba_#{name}_0;
460
- zuint8 lba_#{name}_1;
461
- zuint8 lba_#{name}_2;
462
- zuint8 lba_#{name}_3;
463
-
464
- void internal_#{name}_load_sector(zuint32 sector)
465
- {
466
- if (loaded_sector_#{name} == sector) return;
467
-
468
- FILE * fptr = fopen("#{@initialize_from}", "rb");
469
- fseek(fptr, sector * #{sector_size}, SEEK_SET);
470
- fread(sector_data_#{name}, #{sector_size}, 1, fptr);
471
- fclose(fptr);
472
-
473
- loaded_sector_#{name} = sector;
474
- }
475
-
476
- void internal_#{name}_write_current_sector()
477
- {
478
- FILE * fptr = fopen("#{@initialize_from}", "r+b");
479
- fseek(fptr, loaded_sector_#{name} * #{sector_size}, SEEK_SET);
480
- fwrite(sector_data_#{name}, 1, #{sector_size}, fptr);
481
- fclose(fptr);
482
- }
483
-
484
- zuint8 zemu_io_#{name}_readbyte(zuint32 sector, zuint32 offset)
485
- {
486
- internal_#{name}_load_sector(sector);
487
- return sector_data_#{name}[offset];
488
- }
489
- eos
490
- end
491
-
492
- # Defines generated C to handle reading the block drive's
493
- # registers.
494
- def when_io_read
495
- <<-eos
496
- if (port == #{base_port})
497
- {
498
- zuint8 b = sector_data_#{name}[sector_data_#{name}_offset];
499
- sector_data_#{name}_offset++;
500
- if (sector_data_#{name}_offset >= #{sector_size})
501
- {
502
- drive_status_#{name} = 0b01000000;
503
- }
504
- return b;
505
- }
506
- else if (port == #{base_port+7})
507
- {
508
- return drive_status_#{name};
509
- }
510
- eos
511
- end
512
-
513
- # Defines generated C to handle writing to the block drive's
514
- # registers.
515
- def when_io_write
516
- <<-eos
517
- if (port == #{base_port})
518
- {
519
- if (drive_mode_#{name} == 0x01)
520
- {
521
- sector_data_#{name}[sector_data_#{name}_offset] = value;
522
- sector_data_#{name}_offset++;
523
- if (sector_data_#{name}_offset >= #{sector_size})
524
- {
525
- internal_#{name}_write_current_sector();
526
- drive_status_#{name} = 0b01000000;
527
- }
528
- }
529
- }
530
- else if (port == #{base_port+3})
531
- {
532
- lba_#{name}_0 = value;
533
- }
534
- else if (port == #{base_port+4})
535
- {
536
- lba_#{name}_1 = value;
537
- }
538
- else if (port == #{base_port+5})
539
- {
540
- lba_#{name}_2 = value;
541
- }
542
- else if (port == #{base_port+6})
543
- {
544
- lba_#{name}_3 = value & 0b00011111;
545
- }
546
- else if (port == #{base_port+7})
547
- {
548
- if (value == 0x20)
549
- {
550
- zuint32 sector = 0;
551
- sector |= (zuint32)lba_#{name}_3 << 24;
552
- sector |= (zuint32)lba_#{name}_2 << 16;
553
- sector |= (zuint32)lba_#{name}_1 << 8;
554
- sector |= (zuint32)lba_#{name}_0;
555
-
556
- internal_#{name}_load_sector(sector);
557
- sector_data_#{name}_offset = 0;
558
-
559
- drive_mode_#{name} = 0x00;
560
- drive_status_#{name} = 0b00001000;
561
- }
562
- else if (value == 0x30)
563
- {
564
- zuint32 sector = 0;
565
- sector |= (zuint32)lba_#{name}_3 << 24;
566
- sector |= (zuint32)lba_#{name}_2 << 16;
567
- sector |= (zuint32)lba_#{name}_1 << 8;
568
- sector |= (zuint32)lba_#{name}_0;
569
-
570
- internal_#{name}_load_sector(sector);
571
- sector_data_#{name}_offset = 0;
572
-
573
- drive_mode_#{name} = 0x01;
574
- drive_status_#{name} = 0b00001000;
575
- }
576
- }
577
- eos
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
504
+ end
505
+
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)
537
+ end
538
+ end
539
+
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
578
+ end
578
579
  end
579
580
 
580
581
  # Array of sectors of this drive.
@@ -607,10 +608,24 @@ eos
607
608
  @initialize_from = file
608
609
  end
609
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
+
610
626
  # Defines FFI API which will be available to the instance wrapper if this IO device is used.
611
627
  def functions
612
628
  [
613
- {"name" => "zemu_io_#{name}_readbyte", "args" => [:uint32, :uint32], "return" => :uint8},
614
629
  ]
615
630
  end
616
631
 
@@ -627,26 +642,45 @@ eos
627
642
  # by the CPU through an IO port. The timer generates an NMI once this
628
643
  # period has expired. The timer can be reset via a control port.
629
644
  class Timer < BusDevice
630
- # Defines generated C that sets up the timer.
631
- def when_setup
632
- "zuint8 io_#{name}_count;\n" +
633
- "zuint8 io_#{name}_running = 0;\n"
634
- end
645
+ # Timer is running - count decrements once per clock cycle.
646
+ RUNNING = 0x01
635
647
 
636
- # Defines generated C that handles writing to the timer's
637
- # registers.
638
- def when_io_write
639
- "if (port == #{count_port}) io_#{name}_count = value;\n" +
640
- "else if (port == #{control_port}) io_#{name}_running = value;\n"
648
+ # Timer is stopped - count does not change on clock cycle.
649
+ STOPPED = 0x00
650
+
651
+ def initialize
652
+ super
653
+
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
669
+ end
641
670
  end
642
671
 
643
- # Defines generated C that handles a clock tick for the timer.
644
- def when_clock
645
- "if (io_#{name}_running)\n" +
646
- "{\n" +
647
- " if (io_#{name}_count > 0) io_#{name}_count--;\n" +
648
- " else zemu_io_nmi(instance);\n" +
649
- "}\n"
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
683
+ end
650
684
  end
651
685
 
652
686
  # Valid parameters for a Timer, along with those defined in
data/lib/zemu/instance.rb CHANGED
@@ -60,7 +60,11 @@ module Zemu
60
60
  UNDEFINED = -1
61
61
  end
62
62
 
63
+
64
+
63
65
  def initialize(configuration)
66
+ @devices = configuration.devices
67
+
64
68
  # Methods defined by bus devices that we make
65
69
  # accessible to the user.
66
70
  @device_methods = []
@@ -73,6 +77,70 @@ module Zemu
73
77
  @serial = []
74
78
 
75
79
  @instance = @wrapper.zemu_init
80
+
81
+ # Declare handlers.
82
+ # Memory write handler.
83
+ @mem_write = Proc.new do |addr, value|
84
+ @devices.each do |d|
85
+ d.mem_write(addr, value)
86
+ end
87
+ end
88
+
89
+ # Memory read handler.
90
+ @mem_read = Proc.new do |addr|
91
+ r = 0
92
+ @devices.each do |d|
93
+ v = d.mem_read(addr)
94
+ unless v.nil?
95
+ r = v
96
+ end
97
+ end
98
+
99
+ r
100
+ end
101
+
102
+ # IO write handler.
103
+ @io_write = Proc.new do |port, value|
104
+ @devices.each do |d|
105
+ d.io_write(port, value)
106
+ end
107
+ end
108
+
109
+ # IO read handler.
110
+ @io_read = Proc.new do |port|
111
+ r = 0
112
+ @devices.each do |d|
113
+ v = d.io_read(port)
114
+ unless v.nil?
115
+ r = v
116
+ end
117
+ end
118
+
119
+ r
120
+ end
121
+
122
+ # IO read handler.
123
+ @io_clock = Proc.new do
124
+ @devices.each do |d|
125
+ d.clock()
126
+ end
127
+
128
+ bus_state = 0
129
+
130
+ if @devices.any? { |d| d.nmi? }
131
+ bus_state |= 1
132
+ end
133
+
134
+ bus_state
135
+ end
136
+
137
+ # Attach handlers.
138
+ @wrapper.zemu_set_mem_write_handler(@mem_write)
139
+ @wrapper.zemu_set_mem_read_handler(@mem_read)
140
+ @wrapper.zemu_set_io_write_handler(@io_write)
141
+ @wrapper.zemu_set_io_read_handler(@io_read)
142
+ @wrapper.zemu_set_io_clock_handler(@io_clock)
143
+
76
144
  @wrapper.zemu_power_on(@instance)
77
145
  @wrapper.zemu_reset(@instance)
78
146
 
@@ -81,6 +149,18 @@ module Zemu
81
149
  @breakpoints = {}
82
150
  end
83
151
 
152
+ # Returns the device with the given name, or nil
153
+ # if no such device exists.
154
+ def device(name)
155
+ @devices.each do |d|
156
+ if d.name == name
157
+ return d
158
+ end
159
+ end
160
+
161
+ nil
162
+ end
163
+
84
164
  # Returns the clock speed of this instance in Hz.
85
165
  def clock_speed
86
166
  return @clock
@@ -257,6 +337,21 @@ module Zemu
257
337
 
258
338
  wrapper.ffi_lib [File.join(configuration.output_directory, "#{configuration.name}.so")]
259
339
 
340
+ # Handler types for handling bus accesses.
341
+ wrapper.callback :mem_write_handler, [:uint32, :uint8], :void
342
+ wrapper.callback :mem_read_handler, [:uint32], :uint8
343
+
344
+ wrapper.callback :io_write_handler, [:uint8, :uint8], :void
345
+ wrapper.callback :io_read_handler, [:uint8], :uint8
346
+ wrapper.callback :io_clock_handler, [:void], :uint8
347
+
348
+ wrapper.attach_function :zemu_set_mem_write_handler, [:mem_write_handler], :void
349
+ wrapper.attach_function :zemu_set_mem_read_handler, [:mem_read_handler], :void
350
+
351
+ wrapper.attach_function :zemu_set_io_write_handler, [:io_write_handler], :void
352
+ wrapper.attach_function :zemu_set_io_read_handler, [:io_read_handler], :void
353
+ wrapper.attach_function :zemu_set_io_clock_handler, [:io_clock_handler], :void
354
+
260
355
  wrapper.attach_function :zemu_init, [], :pointer
261
356
  wrapper.attach_function :zemu_free, [:pointer], :void
262
357
 
data/src/bus.c.erb CHANGED
@@ -1,43 +1,59 @@
1
1
  #include "bus.h"
2
2
 
3
- <% devices.each do |device| %>
4
- <%= device.when_setup %>
5
- <% end %>
3
+ mem_write_handler_t * mem_write_handler;
4
+ mem_read_handler_t * mem_read_handler;
5
+
6
+ io_write_handler_t * io_write_handler;
7
+ io_read_handler_t * io_read_handler;
8
+ io_clock_handler_t * io_clock_handler;
9
+
10
+ void zemu_set_mem_write_handler(mem_write_handler_t * h)
11
+ {
12
+ mem_write_handler = h;
13
+ }
14
+
15
+ void zemu_set_mem_read_handler(mem_read_handler_t * h)
16
+ {
17
+ mem_read_handler = h;
18
+ }
19
+
20
+ void zemu_set_io_write_handler(io_write_handler_t * h)
21
+ {
22
+ io_write_handler = h;
23
+ }
24
+
25
+ void zemu_set_io_read_handler(io_read_handler_t * h)
26
+ {
27
+ io_read_handler = h;
28
+ }
29
+
30
+ void zemu_set_io_clock_handler(io_clock_handler_t * h)
31
+ {
32
+ io_clock_handler = h;
33
+ }
6
34
 
7
35
  zuint8 zemu_memory_read(void * context, zuint16 address)
8
36
  {
9
37
  zuint32 address_32 = address;
10
- <% devices.each do |mem| %>
11
- <%= mem.when_mem_read %>
12
- <% end %>
13
- /* Unmapped memory has a value of 0. */
14
- return 0;
38
+ return mem_read_handler(address_32);
15
39
  }
16
40
 
17
41
  void zemu_memory_write(void * context, zuint16 address, zuint8 value)
18
42
  {
19
43
  zuint32 address_32 = address;
20
- <% devices.each do |mem| %>
21
- <%= mem.when_mem_write %>
22
- <% end %>
44
+ mem_write_handler(address_32, value);
23
45
  }
24
46
 
25
47
  zuint8 zemu_memory_peek(zuint16 address)
26
48
  {
27
49
  zuint32 address_32 = address;
28
- <% devices.each do |mem| %>
29
- <%= mem.when_mem_read %>
30
- <% end %>
31
- /* Unmapped memory has a value of 0. */
32
- return 0;
50
+ return mem_read_handler(address_32);
33
51
  }
34
52
 
35
53
  void zemu_memory_poke(zuint16 address, zuint8 value)
36
54
  {
37
55
  zuint32 address_32 = address;
38
- <% devices.each do |mem| %>
39
- <%= mem.when_mem_write %>
40
- <% end %>
56
+ mem_write_handler(address_32, value);
41
57
  }
42
58
 
43
59
  void zemu_io_nmi(Z80 * instance)
@@ -62,10 +78,7 @@ zuint8 zemu_io_in(void * context, zuint16 port)
62
78
  */
63
79
  port &= 0x00FF;
64
80
 
65
- <% devices.each do |device| %>
66
- <%= device.when_io_read %>
67
- <% end %>
68
- return 0;
81
+ return io_read_handler((zuint8)port);
69
82
  }
70
83
 
71
84
  void zemu_io_out(void * context, zuint16 port, zuint8 value)
@@ -75,14 +88,12 @@ void zemu_io_out(void * context, zuint16 port, zuint8 value)
75
88
  */
76
89
  port &= 0x00FF;
77
90
 
78
- <% devices.each do |device| %>
79
- <%= device.when_io_write %>
80
- <% end %>
91
+ io_write_handler((zuint8)port, value);
81
92
  }
82
93
 
83
94
  void zemu_io_clock(Z80 * instance)
84
95
  {
85
- <% devices.each do |device| %>
86
- <%= device.when_clock %>
87
- <% end %>
96
+ zuint8 bus_state = io_clock_handler();
97
+
98
+ if (bus_state & 0x01) zemu_io_nmi(instance);
88
99
  }
data/src/bus.h.erb CHANGED
@@ -13,6 +13,27 @@ typedef struct {
13
13
  unsigned int tail;
14
14
  } SerialBuffer;
15
15
 
16
+ /* Type of a function writing to a memory address. */
17
+ typedef void mem_write_handler_t(zuint32, zuint8);
18
+
19
+ /* Type of a function reading from a memory address. */
20
+ typedef zuint8 mem_read_handler_t(zuint32);
21
+
22
+ /* Type of a function writing to an IO port. */
23
+ typedef void io_write_handler_t(zuint8, zuint8);
24
+
25
+ /* Type of a function reading from an IO port. */
26
+ typedef zuint8 io_read_handler_t(zuint8);
27
+
28
+ /* Type of a function to handle a clock cycle for a peripheral. */
29
+ typedef zuint8 io_clock_handler_t(void);
30
+
31
+ void zemu_set_mem_write_handler(mem_write_handler_t * h);
32
+ void zemu_set_mem_read_handler(mem_read_handler_t * h);
33
+
34
+ void zemu_set_io_write_handler(io_write_handler_t * h);
35
+ void zemu_set_io_read_handler(io_read_handler_t * h);
36
+ void zemu_set_io_clock_handler(io_clock_handler_t * h);
16
37
 
17
38
  zuint8 zemu_memory_read(void * context, zuint16 address);
18
39
  void zemu_memory_write(void * context, zuint16 address, zuint8 value);
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zemu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jay Valentine