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 +4 -4
- data/lib/zemu/config.rb +320 -286
- data/lib/zemu/instance.rb +95 -0
- data/src/bus.c.erb +40 -29
- data/src/bus.h.erb +21 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31940c64874b7aaeb203199df84fc7cdb7cedc8736728e147e2fda5557675d83
|
4
|
+
data.tar.gz: 7a236b15b5075d866aaedaa2793b535e5c1bf7e3fb279c4a7b3702f93ec6e719
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
#
|
103
|
-
#
|
104
|
-
|
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
|
-
#
|
111
|
-
#
|
112
|
-
|
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
|
-
#
|
119
|
-
#
|
120
|
-
|
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
|
-
#
|
127
|
-
#
|
128
|
-
|
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
|
-
#
|
135
|
-
#
|
136
|
-
|
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
|
-
#
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
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
|
-
|
196
|
-
|
197
|
-
|
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
|
-
#
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
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
|
-
#
|
272
|
-
|
273
|
-
|
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
|
-
#
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
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
|
-
#
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
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
|
-
#
|
449
|
-
|
450
|
-
|
451
|
-
#
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
# registers.
|
494
|
-
def
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
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
|
-
#
|
631
|
-
|
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
|
-
#
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
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
|
-
#
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
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
|
-
|
4
|
-
|
5
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
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);
|