zemu 0.5.0 → 0.6.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: 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);