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 +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);
|