zemu 0.6.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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