zemu 0.5.0 → 0.6.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: b7ab3a65fd7f45896cac4297f3715d138a1753def97266f78dd1af1b30e0f012
4
- data.tar.gz: 6d33e40e95a4add449616e6793b2817d2a9c5113f7fcc6fe0f7bf075d0d9dec2
3
+ metadata.gz: 62fdf5d376fa7ea32170f7f2ae62af17f3e96f8714313af513aabf86ef56fc71
4
+ data.tar.gz: 3adcc264bc22f519fff6bb0604051058b198ef3a200c3c5b118f1de1796d84ce
5
5
  SHA512:
6
- metadata.gz: 4551b92671a66d326ec426cbfd240427d93d181ef000c47bda8505185f9ac865b51411bfc35222d097f79e810af9cb9337a50473c7ef80c5b0a63c7d98fa550f
7
- data.tar.gz: aeb983650f57cbb5a7e76b6caffbf81a0ccef919133754fae2608d1a8b04708b329ae5d75e2f5606e8409808b8b456d26b776c28c0ee9a7c39f6f4c1b1ddf038
6
+ metadata.gz: 4bd2cbaf5099cd0874a5d59b519aaf7371e0c3b537fc7c57d70cb82f9003c751362dbed28aa1d5187736932f8d498746d95d1a404f1b43ff3cee571a1248c23a
7
+ data.tar.gz: 9a320b4fbaddf08a24ba5ef58452ab0750d9fec1476bc8b389d43fc2b623364ab82cd037e61560828740a3e01adfa7647093eefad14d72ee5f6ec0152beb11fe
data/lib/zemu/config.rb CHANGED
@@ -75,10 +75,83 @@ module Zemu
75
75
  # @param [String] compiler The path to the compiler to be used for compiling the emulator executable.
76
76
  #
77
77
  class Config < ConfigObject
78
+ # Bus Device.
79
+ #
80
+ # Represents a device connected to the I/O
81
+ # or memory buses, or both.
82
+ class BusDevice < ConfigObject
83
+ # Constructor.
84
+ #
85
+ # This object should not be constructed directly.
86
+ def initialize
87
+ if self.class == Zemu::Config::BusDevice
88
+ raise NotImplementedError, "Cannot construct an instance of the abstract class Zemu::Config::BusDevice."
89
+ end
90
+
91
+ super
92
+ end
93
+
94
+ # Setup to be performed on initialising the emulator
95
+ # instance.
96
+ def when_setup
97
+ ""
98
+ end
99
+
100
+ # Memory bus write handler.
101
+ #
102
+ # Defines C code generated for handling memory
103
+ # writes for this device.
104
+ def when_mem_write
105
+ ""
106
+ end
107
+
108
+ # Memory bus read handler.
109
+ #
110
+ # Defines C code generated for handling memory
111
+ # reads for this device.
112
+ def when_mem_read
113
+ ""
114
+ end
115
+
116
+ # IO bus write handler.
117
+ #
118
+ # Defines C code generated for handling IO
119
+ # writes for this device.
120
+ def when_io_write
121
+ ""
122
+ end
123
+
124
+ # IO bus read handler.
125
+ #
126
+ # Defines C code generated for handling IO
127
+ # reads for this device.
128
+ def when_io_read
129
+ ""
130
+ end
131
+
132
+ # Clock handler.
133
+ #
134
+ # Defines C code which executes for every
135
+ # clock cycle.
136
+ def when_clock
137
+ ""
138
+ end
139
+
140
+ # FFI functions provided by this device.
141
+ def functions
142
+ []
143
+ end
144
+
145
+ # Parameters for a bus device.
146
+ def params
147
+ %w(name)
148
+ end
149
+ end
150
+
78
151
  # Memory object.
79
152
  #
80
153
  # This is an abstract class from which all other memory objects inherit.
81
- class Memory < ConfigObject
154
+ class Memory < BusDevice
82
155
  # Constructor.
83
156
  #
84
157
  # Do not use, as this is an abstract class. Use one of the subclasses instead.
@@ -107,15 +180,50 @@ module Zemu
107
180
  end
108
181
  end
109
182
 
110
- # @return [Boolean] true if this memory section is readonly, false otherwise.
183
+ # Is this memory read-only?
111
184
  def readonly?
112
- return false
185
+ false
186
+ end
187
+
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)
193
+ end
194
+
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
211
+ end
212
+
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
113
221
  end
114
222
 
115
223
  # Valid parameters for this object.
116
224
  # Should be extended by subclasses but NOT REPLACED.
117
225
  def params
118
- return %w(name address size)
226
+ super + %w(address size)
119
227
  end
120
228
 
121
229
  # Reads the contents of a file in binary format and
@@ -155,8 +263,17 @@ module Zemu
155
263
  super
156
264
  end
157
265
 
266
+ # Is this memory block readonly?
158
267
  def readonly?
159
- return true
268
+ true
269
+ end
270
+
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
+ ""
160
277
  end
161
278
  end
162
279
 
@@ -165,169 +282,13 @@ module Zemu
165
282
  # Represents a block of memory which can be read and written.
166
283
  class RAM < Memory
167
284
  end
168
-
169
- # Input/Output Port object
170
- #
171
- # Represents an input/output device assigned to one or more ports.
172
- #
173
- # This is an abstract class and cannot be instantiated directly.
174
- # The when_setup, when_read, and when_write methods can be used to define
175
- # the behaviour of a subclass.
176
- #
177
- # @example
178
- # class MyIODevice < IOPort
179
- # # Extend the parameters of the object so we can define a port.
180
- # def params
181
- # super + "port"
182
- # end
183
- #
184
- # def initialize
185
- # super
186
- #
187
- # # Define the setup for the IO device.
188
- # # This is some global C code that ends up in "io.c".
189
- # # Parameters can be used here, as the block is instance-evaluated.
190
- # when_setup do
191
- # %Q(zuint8 #{name}_value = 42;)
192
- # end
193
- #
194
- # # Define the logic when reading from an IO port.
195
- # # The C variable "port" takes the value of the 8-bit port
196
- # # address being read from, and should be used to identify
197
- # # if this IO device is the one being used.
198
- # when_read do
199
- # %Q(if (port == #{port}) return #{name}_value;)
200
- # end
201
- #
202
- # # Define the logic when writing to the IO port.
203
- # # Similar to #when_read, but we have access to an extra
204
- # # C variable, "value". This is the value being written
205
- # # to the IO port.
206
- # when_write do
207
- # %Q(if (port == #{port}) #{name}_value = value;)
208
- # end
209
- # end
210
- # end
211
- #
212
- # # The subclass can now be declared as below:
213
- # device = MyIODevice.new do
214
- # name "myDevice"
215
- # port 11
216
- # end
217
- #
218
- #
219
- class IOPort < ConfigObject
220
- attr_reader :io_type
221
-
222
- # Constructor.
223
- #
224
- # Do not use, as this is an abstract class. Use one of the subclasses instead.
225
- def initialize
226
- if self.class == Zemu::Config::IOPort
227
- raise NotImplementedError, "Cannot construct an instance of the abstract class Zemu::Config::IOPort."
228
- end
229
-
230
- @ports = []
231
- @setup_block = nil
232
- @read_block = nil
233
- @write_block = nil
234
- @clock_block = nil
235
-
236
- super
237
- end
238
-
239
- # Defines the setup behaviour of this IO device.
240
- #
241
- # Expects a block, the return value of which is a string
242
- # containing all data and function declarations required by this IO device.
243
- #
244
- # The block will be instance-evaluated at build-time, so it is possible to use
245
- # instance variables of the IO device.
246
- def when_setup(&block)
247
- @setup_block = block
248
- end
249
-
250
- # Defines the read behaviour of this IO device.
251
- #
252
- # Expects a block, the return value of which is a string
253
- # containing the behaviour of this IO device when a value is read from the IO bus.
254
- # Care must be taken to ensure that this functionality does not conflict with that of
255
- # any other IO devices.
256
- #
257
- # The block will be instance-evaluated at build-time, so it is possible to use
258
- # instance variables of the IO device.
259
- def when_read(&block)
260
- @read_block = block
261
- end
262
-
263
- # Defines the write behaviour of this IO device.
264
- #
265
- # Expects a block, the return value of which is a string
266
- # containing the behaviour of this IO device when a value is written to the IO bus.
267
- # Care must be taken to ensure that this functionality does not conflict with that of
268
- # any other IO devices.
269
- #
270
- # The block will be instance-evaluated at build-time, so it is possible to use
271
- # instance variables of the IO device.
272
- def when_write(&block)
273
- @write_block = block
274
- end
275
-
276
- # Defines the per-cycle behaviour of this IO device.
277
- #
278
- # Expects a block, the return value of which is a string
279
- # defining the behaviour of the IO device on each system clock cycle.
280
- # Care must be taken to ensure that this functionality does not conflict with that of
281
- # any other IO devices.
282
- #
283
- # The block will be instance-evaluated at build-time, so it is possible to use
284
- # instance variables of the IO device.
285
- def when_clock(&block)
286
- @clock_block = block
287
- end
288
-
289
- # Evaluates the when_setup block of this IO device and returns the resulting string.
290
- def setup
291
- return instance_eval(&@setup_block) unless @setup_block.nil?
292
- return ""
293
- end
294
-
295
- # Evaluates the when_read block of this IO device and returns the resulting string.
296
- def read
297
- return instance_eval(&@read_block) unless @read_block.nil?
298
- return ""
299
- end
300
-
301
- # Evaluates the when_write block of this IO device and returns the resulting string.
302
- def write
303
- return instance_eval(&@write_block) unless @write_block.nil?
304
- return ""
305
- end
306
-
307
- # Evaluates the when_clock block of this IO device and returns the resulting string.
308
- def clock
309
- return instance_eval(&@clock_block) unless @clock_block.nil?
310
- return ""
311
- end
312
-
313
- # Defines FFI API which will be available to the instance wrapper if this IO device is used.
314
- def functions
315
- []
316
- end
317
-
318
- # Valid parameters for this object.
319
- # Should be extended by subclasses but NOT REPLACED.
320
- def params
321
- %w(name)
322
- end
323
- end
324
285
 
325
286
  # Serial Input/Output object
326
287
  #
327
288
  # Represents a serial connection between the emulated CPU
328
289
  # and the host machine, with input and output mapped to Z80 I/O
329
290
  # ports.
330
- class SerialPort < IOPort
291
+ class SerialPort < BusDevice
331
292
  # Constructor.
332
293
  #
333
294
  # Takes a block in which the parameters of the serial port
@@ -347,80 +308,84 @@ module Zemu
347
308
  #
348
309
  def initialize
349
310
  super
311
+ end
350
312
 
351
- when_setup do
352
- "SerialBuffer io_#{name}_buffer_master = { .head = 0, .tail = 0 };\n" +
353
- "SerialBuffer io_#{name}_buffer_slave = { .head = 0, .tail = 0 };\n" +
354
- "\n" +
355
- "zusize zemu_io_#{name}_buffer_size(void)\n" +
356
- "{\n" +
357
- " zusize start = io_#{name}_buffer_slave.head;\n" +
358
- " zusize end = io_#{name}_buffer_slave.tail\n;" +
359
- " if (end < start) end += ZEMU_IO_SERIAL_BUFFER_SIZE;\n" +
360
- " return end - start;\n" +
361
- "}\n" +
362
- "\n" +
363
- "void zemu_io_#{name}_slave_puts(zuint8 val)\n" +
364
- "{\n" +
365
- " io_#{name}_buffer_slave.buffer[io_#{name}_buffer_slave.tail] = val;\n" +
366
- " io_#{name}_buffer_slave.tail++;\n" +
367
- " if (io_#{name}_buffer_slave.tail >= ZEMU_IO_SERIAL_BUFFER_SIZE)\n" +
368
- " io_#{name}_buffer_slave.tail = 0;\n" +
369
- "}\n" +
370
- "\n" +
371
- "zuint8 zemu_io_#{name}_slave_gets(void)\n" +
372
- "{\n" +
373
- " zuint8 val = io_#{name}_buffer_master.buffer[io_#{name}_buffer_master.head];\n" +
374
- " io_#{name}_buffer_master.head++;\n" +
375
- " if (io_#{name}_buffer_master.head >= ZEMU_IO_SERIAL_BUFFER_SIZE)\n" +
376
- " io_#{name}_buffer_master.head = 0;\n" +
377
- "\n" +
378
- " return val;\n" +
379
- "}\n" +
380
- "\n" +
381
- "void zemu_io_#{name}_master_puts(zuint8 val)\n" +
382
- "{\n" +
383
- " io_#{name}_buffer_master.buffer[io_#{name}_buffer_master.tail] = val;\n" +
384
- " io_#{name}_buffer_master.tail++;\n" +
385
- " if (io_#{name}_buffer_master.tail >= ZEMU_IO_SERIAL_BUFFER_SIZE)\n" +
386
- " io_#{name}_buffer_master.tail = 0;\n" +
387
- "}\n" +
388
- "\n" +
389
- "zuint8 zemu_io_#{name}_master_gets(void)\n" +
390
- "{\n" +
391
- " zuint8 val = io_#{name}_buffer_slave.buffer[io_#{name}_buffer_slave.head];\n" +
392
- " io_#{name}_buffer_slave.head++;\n" +
393
- " if (io_#{name}_buffer_slave.head >= ZEMU_IO_SERIAL_BUFFER_SIZE)\n" +
394
- " io_#{name}_buffer_slave.head = 0;\n" +
395
- "\n" +
396
- " return val;\n" +
397
- "}\n"
398
- end
399
-
400
- when_read do
401
- "if (port == #{in_port})\n" +
402
- "{\n" +
403
- " return zemu_io_#{name}_slave_gets();\n" +
404
- "}\n" +
405
- "else if (port == #{ready_port})\n" +
406
- "{\n" +
407
- " if (io_#{name}_buffer_master.head == io_#{name}_buffer_master.tail)\n" +
408
- " {\n" +
409
- " return 0;\n" +
410
- " }\n" +
411
- " else\n" +
412
- " {\n" +
413
- " return 1;\n" +
414
- " }\n" +
415
- "}\n"
416
- end
417
-
418
- when_write do
419
- "if (port == #{out_port})\n" +
420
- "{\n" +
421
- " zemu_io_#{name}_slave_puts(value);\n" +
422
- "}\n"
423
- end
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"
424
389
  end
425
390
 
426
391
  # Defines FFI API which will be available to the instance wrapper if this IO device is used.
@@ -433,7 +398,7 @@ module Zemu
433
398
  end
434
399
 
435
400
  # Valid parameters for a SerialPort, along with those
436
- # defined in [Zemu::Config::IOPort].
401
+ # defined in [Zemu::Config::BusDevice].
437
402
  def params
438
403
  super + %w(in_port out_port ready_port)
439
404
  end
@@ -443,7 +408,7 @@ module Zemu
443
408
  #
444
409
  # Represents a device with a sequence of sectors of a fixed size,
445
410
  # which can be accessed via IO instructions as an IDE drive.
446
- class BlockDrive < IOPort
411
+ class BlockDrive < BusDevice
447
412
  # Constructor.
448
413
  #
449
414
  # Takes a block in which the parameters of the block drive
@@ -478,8 +443,10 @@ module Zemu
478
443
  raise RangeError, "Initialization file for Zemu::Config::BlockDrive '#{name}' is of wrong size."
479
444
  end
480
445
  end
446
+ end
481
447
 
482
- when_setup do
448
+ # Defines generated C to declare the block device.
449
+ def when_setup
483
450
  <<-eos
484
451
  #include <stdio.h>
485
452
 
@@ -520,9 +487,11 @@ zuint8 zemu_io_#{name}_readbyte(zuint32 sector, zuint32 offset)
520
487
  return sector_data_#{name}[offset];
521
488
  }
522
489
  eos
523
- end
490
+ end
524
491
 
525
- when_read do
492
+ # Defines generated C to handle reading the block drive's
493
+ # registers.
494
+ def when_io_read
526
495
  <<-eos
527
496
  if (port == #{base_port})
528
497
  {
@@ -539,9 +508,11 @@ else if (port == #{base_port+7})
539
508
  return drive_status_#{name};
540
509
  }
541
510
  eos
542
- end
511
+ end
543
512
 
544
- when_write do
513
+ # Defines generated C to handle writing to the block drive's
514
+ # registers.
515
+ def when_io_write
545
516
  <<-eos
546
517
  if (port == #{base_port})
547
518
  {
@@ -604,7 +575,6 @@ else if (port == #{base_port+7})
604
575
  }
605
576
  }
606
577
  eos
607
- end
608
578
  end
609
579
 
610
580
  # Array of sectors of this drive.
@@ -645,7 +615,7 @@ eos
645
615
  end
646
616
 
647
617
  # Valid parameters for a BlockDrive, along with those
648
- # defined in [Zemu::Config::IOPort].
618
+ # defined in [Zemu::Config::BusDevice].
649
619
  def params
650
620
  super + %w(base_port sector_size num_sectors)
651
621
  end
@@ -656,30 +626,27 @@ eos
656
626
  # Represents a timer device, the period of which can be controlled
657
627
  # by the CPU through an IO port. The timer generates an NMI once this
658
628
  # period has expired. The timer can be reset via a control port.
659
- class Timer < IOPort
660
- def initialize
661
- super
662
-
663
- when_setup do
664
- "zuint8 io_#{name}_count;\n" +
665
- "zuint8 io_#{name}_running = 0;\n"
666
- end
667
-
668
- when_read do
669
- end
629
+ 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
670
635
 
671
- when_write do
672
- "if (port == #{count_port}) io_#{name}_count = value;\n" +
673
- "else if (port == #{control_port}) io_#{name}_running = value;\n"
674
- end
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"
641
+ end
675
642
 
676
- when_clock do
677
- "if (io_#{name}_running)\n" +
678
- "{\n" +
679
- " if (io_#{name}_count > 0) io_#{name}_count--;\n" +
680
- " else zemu_io_nmi(instance);\n" +
681
- "}\n"
682
- end
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"
683
650
  end
684
651
 
685
652
  # Valid parameters for a Timer, along with those defined in
@@ -694,11 +661,8 @@ eos
694
661
  return binding
695
662
  end
696
663
 
697
- # The memory sections of this configuration object.
698
- attr_reader :memory
699
-
700
- # The IO devices of this configuration object.
701
- attr_reader :io
664
+ # The bus devices of this configuration object.
665
+ attr_reader :devices
702
666
 
703
667
  # Parameters accessible by this configuration object.
704
668
  def params
@@ -737,8 +701,7 @@ eos
737
701
  #
738
702
  # @raise [Zemu::ConfigError] Raised if the +name+ parameter is not set, or contains whitespace.
739
703
  def initialize
740
- @memory = []
741
- @io = []
704
+ @devices = []
742
705
 
743
706
  super
744
707
 
@@ -753,16 +716,29 @@ eos
753
716
 
754
717
  # Adds a new memory section to this configuration.
755
718
  #
719
+ # Deprecated - retained only for backwards compatibility.
720
+ # Use add_device instead.
721
+ #
756
722
  # @param [Zemu::Config::Memory] mem The memory object to add.
757
723
  def add_memory(mem)
758
- @memory << mem
724
+ @devices << mem
759
725
  end
760
726
 
761
727
  # Adds a new IO device to this configuration.
762
728
  #
763
- # @param [Zemu::Config::IOPort] io The IO device to add.
729
+ # Deprecated - retained only for backwards compatibility.
730
+ # Use add_device instead.
731
+ #
732
+ # @param [Zemu::Config::BusDevice] io The IO device to add.
764
733
  def add_io(io)
765
- @io << io
734
+ @devices << io
735
+ end
736
+
737
+ # Adds a new device to the bus for this configuration.
738
+ #
739
+ # @param [Zemu::Config::BusDevice] device The device to add.
740
+ def add_device(device)
741
+ @devices << device
766
742
  end
767
743
  end
768
744
 
data/lib/zemu/debug.rb CHANGED
@@ -49,6 +49,11 @@ module Zemu
49
49
 
50
50
  return nil
51
51
  end
52
+
53
+ # Get symbols as a hash.
54
+ def hash
55
+ return @syms
56
+ end
52
57
  end
53
58
 
54
59
  # Represents a symbol definition, of the form `label = address`.
data/lib/zemu/instance.rb CHANGED
@@ -61,9 +61,9 @@ module Zemu
61
61
  end
62
62
 
63
63
  def initialize(configuration)
64
- # Methods defined by IO devices that we make
64
+ # Methods defined by bus devices that we make
65
65
  # accessible to the user.
66
- @io_methods = []
66
+ @device_methods = []
67
67
 
68
68
  @clock = configuration.clock_speed
69
69
  @serial_delay = configuration.serial_delay
@@ -275,10 +275,10 @@ module Zemu
275
275
  wrapper.attach_function :zemu_debug_get_memory, [:uint16], :uint8
276
276
  wrapper.attach_function :zemu_debug_set_memory, [:uint16, :uint8], :void
277
277
 
278
- configuration.io.each do |device|
278
+ configuration.devices.each do |device|
279
279
  device.functions.each do |f|
280
280
  wrapper.attach_function(f["name"].to_sym, f["args"], f["return"])
281
- @io_methods << f["name"].to_sym
281
+ @device_methods << f["name"].to_sym
282
282
  end
283
283
  end
284
284
 
@@ -287,7 +287,7 @@ module Zemu
287
287
 
288
288
  # Redirects calls to I/O FFI functions.
289
289
  def method_missing(method, *args)
290
- if @io_methods.include? method
290
+ if @device_methods.include? method
291
291
  return @wrapper.send(method)
292
292
  end
293
293
 
@@ -11,6 +11,7 @@ module Zemu
11
11
  # to the emulator window.
12
12
  def initialize(instance, options = {})
13
13
  @print_serial = options[:print_serial]
14
+ @trace = []
14
15
 
15
16
  @instance = instance
16
17
 
@@ -70,6 +71,9 @@ module Zemu
70
71
  elsif cmd[0] == "map"
71
72
  load_map(cmd[1])
72
73
 
74
+ elsif cmd[0] == "trace"
75
+ trace()
76
+
73
77
  elsif cmd[0] == "help"
74
78
  log "Available commands:"
75
79
  log " continue [<n>] - Continue execution for <n> cycles"
@@ -90,6 +94,14 @@ module Zemu
90
94
  close
91
95
  end
92
96
 
97
+ # Print trace for the emulator instance
98
+ # (last 200 addresses visited).
99
+ def trace
100
+ @trace.each do |t|
101
+ puts "%04x" % t
102
+ end
103
+ end
104
+
93
105
  # Outputs a table giving the current values of the instance's registers.
94
106
  # For the 16-bit registers (BC, DE, HL, IX, IY, SP, PC), attempts to identify the symbol
95
107
  # to which they point.
@@ -185,6 +197,9 @@ module Zemu
185
197
  cycles_left -= cycles_done
186
198
  actual_cycles += cycles_done
187
199
 
200
+ @trace << r16("PC")
201
+ @trace = @trace[1..] if @trace.size > 200
202
+
188
203
  # Get time after execution.
189
204
  ending = Time.now
190
205
 
@@ -199,6 +214,11 @@ module Zemu
199
214
  sleep(padding) unless padding < 0
200
215
  end
201
216
 
217
+ if (@instance.memory(0x200) != 0xf3)
218
+ log "Buffer overflow at #{r16("PC")}"
219
+ break
220
+ end
221
+
202
222
  # Have we hit a breakpoint or HALT instruction?
203
223
  if @instance.break?
204
224
  log "Hit breakpoint at #{r16("PC")}."
@@ -258,7 +278,7 @@ module Zemu
258
278
 
259
279
  syms = {}
260
280
  begin
261
- syms.merge! Debug.load_map(path.to_s)
281
+ syms.merge! Debug.load_map(path.to_s).hash
262
282
  rescue ArgumentError => e
263
283
  log "Error loading map file: #{e.message}"
264
284
  syms.clear
data/lib/zemu.rb CHANGED
@@ -111,7 +111,7 @@ module Zemu
111
111
 
112
112
  inputs_str = inputs.map { |i| File.join(SRC, i) }.join(" ")
113
113
 
114
- inputs_str += " " + File.join(autogen, "memory.c") + " " + File.join(autogen, "io.c")
114
+ inputs_str += " " + File.join(autogen, "bus.c")
115
115
 
116
116
  defines = {
117
117
  "CPU_Z80_STATIC" => 1,
@@ -143,14 +143,13 @@ module Zemu
143
143
  #
144
144
  # @param [Zemu::Config] configuration The configuration for which an emulator will be generated.
145
145
  def Zemu::generate(configuration)
146
- generate_memory(configuration)
147
- generate_io(configuration)
146
+ generate_bus(configuration)
148
147
  end
149
148
 
150
- # Generates the memory.c and memory.h files for a given configuration.
151
- def Zemu::generate_memory(configuration)
152
- header_template = ERB.new File.read(File.join(SRC, "memory.h.erb"))
153
- source_template = ERB.new File.read(File.join(SRC, "memory.c.erb"))
149
+ # Generates the bus.c and bus.h files for a given configuration.
150
+ def Zemu::generate_bus(configuration)
151
+ header_template = ERB.new File.read(File.join(SRC, "bus.h.erb"))
152
+ source_template = ERB.new File.read(File.join(SRC, "bus.c.erb"))
154
153
 
155
154
  autogen = File.join(configuration.output_directory, "autogen_#{configuration.name}")
156
155
 
@@ -158,28 +157,10 @@ module Zemu
158
157
  Dir.mkdir autogen
159
158
  end
160
159
 
161
- File.write(File.join(autogen, "memory.h"),
160
+ File.write(File.join(autogen, "bus.h"),
162
161
  header_template.result(configuration.get_binding))
163
162
 
164
- File.write(File.join(autogen, "memory.c"),
165
- source_template.result(configuration.get_binding))
166
- end
167
-
168
- # Generates the io.c and io.h files for a given configuration.
169
- def Zemu::generate_io(configuration)
170
- header_template = ERB.new File.read(File.join(SRC, "io.h.erb"))
171
- source_template = ERB.new File.read(File.join(SRC, "io.c.erb"))
172
-
173
- autogen = File.join(configuration.output_directory, "autogen_#{configuration.name}")
174
-
175
- unless Dir.exist? autogen
176
- Dir.mkdir autogen
177
- end
178
-
179
- File.write(File.join(autogen, "io.h"),
180
- header_template.result(configuration.get_binding))
181
-
182
- File.write(File.join(autogen, "io.c"),
163
+ File.write(File.join(autogen, "bus.c"),
183
164
  source_template.result(configuration.get_binding))
184
165
  end
185
166
  end
data/src/bus.c.erb ADDED
@@ -0,0 +1,88 @@
1
+ #include "bus.h"
2
+
3
+ <% devices.each do |device| %>
4
+ <%= device.when_setup %>
5
+ <% end %>
6
+
7
+ zuint8 zemu_memory_read(void * context, zuint16 address)
8
+ {
9
+ 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;
15
+ }
16
+
17
+ void zemu_memory_write(void * context, zuint16 address, zuint8 value)
18
+ {
19
+ zuint32 address_32 = address;
20
+ <% devices.each do |mem| %>
21
+ <%= mem.when_mem_write %>
22
+ <% end %>
23
+ }
24
+
25
+ zuint8 zemu_memory_peek(zuint16 address)
26
+ {
27
+ 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;
33
+ }
34
+
35
+ void zemu_memory_poke(zuint16 address, zuint8 value)
36
+ {
37
+ zuint32 address_32 = address;
38
+ <% devices.each do |mem| %>
39
+ <%= mem.when_mem_write %>
40
+ <% end %>
41
+ }
42
+
43
+ void zemu_io_nmi(Z80 * instance)
44
+ {
45
+ z80_nmi(instance);
46
+ }
47
+
48
+ void zemu_io_int_on(Z80 * instance)
49
+ {
50
+ z80_int(instance, TRUE);
51
+ }
52
+
53
+ void zemu_io_int_off(Z80 * instance)
54
+ {
55
+ z80_int(instance, FALSE);
56
+ }
57
+
58
+ zuint8 zemu_io_in(void * context, zuint16 port)
59
+ {
60
+ /* Z80 IO ports occupy the lower half of the address bus.
61
+ * We cannot assume that the top half is valid.
62
+ */
63
+ port &= 0x00FF;
64
+
65
+ <% devices.each do |device| %>
66
+ <%= device.when_io_read %>
67
+ <% end %>
68
+ return 0;
69
+ }
70
+
71
+ void zemu_io_out(void * context, zuint16 port, zuint8 value)
72
+ {
73
+ /* Z80 IO ports occupy the lower half of the address bus.
74
+ * We cannot assume that the top half is valid.
75
+ */
76
+ port &= 0x00FF;
77
+
78
+ <% devices.each do |device| %>
79
+ <%= device.when_io_write %>
80
+ <% end %>
81
+ }
82
+
83
+ void zemu_io_clock(Z80 * instance)
84
+ {
85
+ <% devices.each do |device| %>
86
+ <%= device.when_clock %>
87
+ <% end %>
88
+ }
@@ -13,6 +13,13 @@ typedef struct {
13
13
  unsigned int tail;
14
14
  } SerialBuffer;
15
15
 
16
+
17
+ zuint8 zemu_memory_read(void * context, zuint16 address);
18
+ void zemu_memory_write(void * context, zuint16 address, zuint8 value);
19
+
20
+ zuint8 zemu_memory_peek(zuint16 address);
21
+ void zemu_memory_poke(zuint16 address, zuint8 value);
22
+
16
23
  void zemu_io_serial_master_puts(zuint8 val);
17
24
  zuint8 zemu_io_serial_master_gets(void);
18
25
  zusize zemu_io_serial_buffer_size(void);
data/src/debug.h CHANGED
@@ -2,8 +2,7 @@
2
2
 
3
3
  #include <stdio.h>
4
4
 
5
- #include "memory.h"
6
- #include "io.h"
5
+ #include "bus.h"
7
6
 
8
7
  zusize zemu_debug_step(Z80 * instance);
9
8
 
data/src/main.c CHANGED
@@ -4,8 +4,7 @@
4
4
 
5
5
  #include "debug.h"
6
6
 
7
- #include "memory.h"
8
- #include "io.h"
7
+ #include "bus.h"
9
8
  #include "interrupt.h"
10
9
 
11
10
  /* Allocate and initialize a Z80 instance.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zemu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jay Valentine
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-26 00:00:00.000000000 Z
11
+ date: 2021-12-30 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |2
14
14
  Zemu is a gem which allows the user to configure a Z80-based system
@@ -32,6 +32,8 @@ files:
32
32
  - lib/zemu/debug.rb
33
33
  - lib/zemu/instance.rb
34
34
  - lib/zemu/interactive.rb
35
+ - src/bus.c.erb
36
+ - src/bus.h.erb
35
37
  - src/debug.c
36
38
  - src/debug.h
37
39
  - src/external/Z/API/Z/ABIs/generic/allocator.h
@@ -310,11 +312,7 @@ files:
310
312
  - src/external/z80/sources/Z80.c
311
313
  - src/interrupt.c
312
314
  - src/interrupt.h
313
- - src/io.c.erb
314
- - src/io.h.erb
315
315
  - src/main.c
316
- - src/memory.c.erb
317
- - src/memory.h.erb
318
316
  homepage: https://github.com/jayvalentine/zemu
319
317
  licenses:
320
318
  - GPL-3.0
data/src/io.c.erb DELETED
@@ -1,52 +0,0 @@
1
- #include "io.h"
2
-
3
- <% io.each do |device| %>
4
- <%= device.setup %>
5
- <% end %>
6
-
7
- void zemu_io_nmi(Z80 * instance)
8
- {
9
- z80_nmi(instance);
10
- }
11
-
12
- void zemu_io_int_on(Z80 * instance)
13
- {
14
- z80_int(instance, TRUE);
15
- }
16
-
17
- void zemu_io_int_off(Z80 * instance)
18
- {
19
- z80_int(instance, FALSE);
20
- }
21
-
22
- zuint8 zemu_io_in(void * context, zuint16 port)
23
- {
24
- /* Z80 IO ports occupy the lower half of the address bus.
25
- * We cannot assume that the top half is valid.
26
- */
27
- port &= 0x00FF;
28
-
29
- <% io.each do |device| %>
30
- <%= device.read %>
31
- <% end %>
32
- return 0;
33
- }
34
-
35
- void zemu_io_out(void * context, zuint16 port, zuint8 value)
36
- {
37
- /* Z80 IO ports occupy the lower half of the address bus.
38
- * We cannot assume that the top half is valid.
39
- */
40
- port &= 0x00FF;
41
-
42
- <% io.each do |device| %>
43
- <%= device.write %>
44
- <% end %>
45
- }
46
-
47
- void zemu_io_clock(Z80 * instance)
48
- {
49
- <% io.each do |device| %>
50
- <%= device.clock %>
51
- <% end %>
52
- }
data/src/memory.c.erb DELETED
@@ -1,59 +0,0 @@
1
- #include "memory.h"
2
-
3
- <% memory.each do |mem| %>
4
- /* Initialization memory block "<%= mem.name %>" */
5
- <%= mem.readonly? ? "const " : "" %>zuint8 zemu_memory_block_<%= mem.name %>[0x<%= mem.size.to_s(16) %>] =
6
- {<% mem.contents.each_with_index do |b, i| %><%= (i % 16 == 0) ? "\n " : "" %><%= ("0x%02x, " % b) %><% end %>
7
- };
8
- <% end %>
9
-
10
- zuint8 zemu_memory_read(void * context, zuint16 address)
11
- {
12
- zuint32 address_32 = address;
13
- <% memory.each do |mem| %>
14
- if (address_32 >= 0x<%= mem.address.to_s(16) %> && address_32 < 0x<%= (mem.address + mem.size).to_s(16) %>)
15
- {
16
- return zemu_memory_block_<%= mem.name %>[address_32 - 0x<%= mem.address.to_s(16) %>];
17
- }
18
- <% end %>
19
- /* Unmapped memory has a value of 0. */
20
- return 0;
21
- }
22
-
23
- void zemu_memory_write(void * context, zuint16 address, zuint8 value)
24
- {
25
- zuint32 address_32 = address;
26
- <% memory.each do |mem| %>
27
- <% next if mem.readonly? %>
28
- if (address_32 >= 0x<%= mem.address.to_s(16) %> && address_32 < 0x<%= (mem.address + mem.size).to_s(16) %>)
29
- {
30
- zemu_memory_block_<%= mem.name %>[address_32 - 0x<%= mem.address.to_s(16) %>] = value;
31
- }
32
- <% end %>
33
- }
34
-
35
- zuint8 zemu_memory_peek(zuint16 address)
36
- {
37
- zuint32 address_32 = address;
38
- <% memory.each do |mem| %>
39
- if (address_32 >= 0x<%= mem.address.to_s(16) %> && address_32 < 0x<%= (mem.address + mem.size).to_s(16) %>)
40
- {
41
- return zemu_memory_block_<%= mem.name %>[address_32 - 0x<%= mem.address.to_s(16) %>];
42
- }
43
- <% end %>
44
- /* Unmapped memory has a value of 0. */
45
- return 0;
46
- }
47
-
48
- void zemu_memory_poke(zuint16 address, zuint8 value)
49
- {
50
- zuint32 address_32 = address;
51
- <% memory.each do |mem| %>
52
- <% next if mem.readonly? %>
53
- if (address_32 >= 0x<%= mem.address.to_s(16) %> && address_32 < 0x<%= (mem.address + mem.size).to_s(16) %>)
54
- {
55
- zemu_memory_block_<%= mem.name %>[address_32 - 0x<%= mem.address.to_s(16) %>] = value;
56
- return;
57
- }
58
- <% end %>
59
- }
data/src/memory.h.erb DELETED
@@ -1,11 +0,0 @@
1
- #include "emulation/CPU/Z80.h"
2
-
3
- #include <stdio.h>
4
-
5
- zuint8 zemu_memory_read(void * context, zuint16 address);
6
-
7
- void zemu_memory_write(void * context, zuint16 address, zuint8 value);
8
-
9
- zuint8 zemu_memory_peek(zuint16 address);
10
-
11
- void zemu_memory_poke(zuint16 address, zuint8 value);