zemu 0.3.2 → 0.4.1

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: 7d5844a8eefff0375ee504c0b00d2348fcd4b4c0f0a745c62b3a85048525d7fd
4
- data.tar.gz: d48dffdd9ce6213cd674e091e56bacb12afd8f7d212cd0c6443f870b34d3c096
3
+ metadata.gz: 99d9e825251363ade2a565f28d7545f81b3dc93221951980a12bed3d3b8ab82c
4
+ data.tar.gz: c93e03bccdf934341909811d4a2d07fb58d31121cd25983b0659495a2d9b176c
5
5
  SHA512:
6
- metadata.gz: c790425ce48e7b88bc2f9c4d03eb9113b039cadde64ec1f171fe5ec8b120efdd3951d00ad23d96426bd2e77af85638babdc7844d8c1bd9fe00ab68f3574e9808
7
- data.tar.gz: 97d8e53911049e9dde8eb816ff0c8635d061901dc7e672fbaf653464f434d9fd0840250b8504f724408f281ae6431dea1441300ab820afb289eae764c755d8d1
6
+ metadata.gz: 0c4024d10bfd6521b25f98f8172e40163cd635d354961554406532c48f58b2063a3ac945a0378907c472db3287dff3f8643f7bfbbf256684b81a9511a15dd3e1
7
+ data.tar.gz: c826fa009c55c9890faa7d9ef5f172c3ec123a1912e55ff75dca57c51e377cac7014b9d07f8ea9ab682e9ae1ea718c0fae70fda42b0eb0a471fe24c2f494ef96
data/lib/zemu/config.rb CHANGED
@@ -439,6 +439,218 @@ module Zemu
439
439
  end
440
440
  end
441
441
 
442
+ # Block drive object
443
+ #
444
+ # Represents a device with a sequence of sectors of a fixed size,
445
+ # which can be accessed via IO instructions as an IDE drive.
446
+ class BlockDrive < IOPort
447
+ # Constructor.
448
+ #
449
+ # Takes a block in which the parameters of the block drive
450
+ # can be initialized.
451
+ #
452
+ # All parameters can be set within this block.
453
+ # They become readonly as soon as the block completes.
454
+ #
455
+ # Constructor raises RangeError if a file is provided for initialization
456
+ # and it is of the wrong size.
457
+ #
458
+ # @example
459
+ #
460
+ # Zemu::Config::BlockDrive.new do
461
+ # name "drive"
462
+ # base_port 0x0c
463
+ # sector_size 512
464
+ # num_sectors 64
465
+ # end
466
+ #
467
+ #
468
+ def initialize
469
+ @initialize_from = nil
470
+
471
+ super
472
+
473
+ # Initialize from provided file if applicable.
474
+ unless @initialize_from.nil?
475
+ # Check file size.
476
+ file_size = File.size(@initialize_from)
477
+ if (file_size != num_sectors * sector_size)
478
+ raise RangeError, "Initialization file for Zemu::Config::BlockDrive '#{name}' is of wrong size."
479
+ end
480
+ end
481
+
482
+ when_setup do
483
+ <<-eos
484
+ #include <stdio.h>
485
+
486
+ zuint8 sector_data_#{name}[#{sector_size}];
487
+ zuint32 sector_data_#{name}_offset;
488
+ zuint32 loaded_sector_#{name} = Z_UINT32_MAXIMUM;
489
+ zuint8 drive_mode_#{name};
490
+ zuint8 drive_status_#{name} = 0b01000000;
491
+
492
+ zuint8 lba_#{name}_0;
493
+ zuint8 lba_#{name}_1;
494
+ zuint8 lba_#{name}_2;
495
+ zuint8 lba_#{name}_3;
496
+
497
+ void internal_#{name}_load_sector(zuint32 sector)
498
+ {
499
+ if (loaded_sector_#{name} == sector) return;
500
+
501
+ FILE * fptr = fopen("#{@initialize_from}", "rb");
502
+ fseek(fptr, sector * #{sector_size}, SEEK_SET);
503
+ fread(sector_data_#{name}, #{sector_size}, 1, fptr);
504
+ fclose(fptr);
505
+
506
+ loaded_sector_#{name} = sector;
507
+ }
508
+
509
+ void internal_#{name}_write_current_sector()
510
+ {
511
+ FILE * fptr = fopen("#{@initialize_from}", "r+b");
512
+ fseek(fptr, loaded_sector_#{name} * #{sector_size}, SEEK_SET);
513
+ fwrite(sector_data_#{name}, 1, #{sector_size}, fptr);
514
+ fclose(fptr);
515
+ }
516
+
517
+ zuint8 zemu_io_#{name}_readbyte(zuint32 sector, zuint32 offset)
518
+ {
519
+ internal_#{name}_load_sector(sector);
520
+ return sector_data_#{name}[offset];
521
+ }
522
+ eos
523
+ end
524
+
525
+ when_read do
526
+ <<-eos
527
+ if (port == #{base_port})
528
+ {
529
+ zuint8 b = sector_data_#{name}[sector_data_#{name}_offset];
530
+ sector_data_#{name}_offset++;
531
+ if (sector_data_#{name}_offset >= #{sector_size})
532
+ {
533
+ drive_status_#{name} = 0b01000000;
534
+ }
535
+ return b;
536
+ }
537
+ else if (port == #{base_port+7})
538
+ {
539
+ return drive_status_#{name};
540
+ }
541
+ eos
542
+ end
543
+
544
+ when_write do
545
+ <<-eos
546
+ if (port == #{base_port})
547
+ {
548
+ if (drive_mode_#{name} == 0x01)
549
+ {
550
+ sector_data_#{name}[sector_data_#{name}_offset] = value;
551
+ sector_data_#{name}_offset++;
552
+ if (sector_data_#{name}_offset >= #{sector_size})
553
+ {
554
+ internal_#{name}_write_current_sector();
555
+ drive_status_#{name} = 0b01000000;
556
+ }
557
+ }
558
+ }
559
+ else if (port == #{base_port+3})
560
+ {
561
+ lba_#{name}_0 = value;
562
+ }
563
+ else if (port == #{base_port+4})
564
+ {
565
+ lba_#{name}_1 = value;
566
+ }
567
+ else if (port == #{base_port+5})
568
+ {
569
+ lba_#{name}_2 = value;
570
+ }
571
+ else if (port == #{base_port+6})
572
+ {
573
+ lba_#{name}_3 = value & 0b00011111;
574
+ }
575
+ else if (port == #{base_port+7})
576
+ {
577
+ if (value == 0x20)
578
+ {
579
+ zuint32 sector = 0;
580
+ sector |= (zuint32)lba_#{name}_3 << 24;
581
+ sector |= (zuint32)lba_#{name}_2 << 16;
582
+ sector |= (zuint32)lba_#{name}_1 << 8;
583
+ sector |= (zuint32)lba_#{name}_0;
584
+
585
+ internal_#{name}_load_sector(sector);
586
+ sector_data_#{name}_offset = 0;
587
+
588
+ drive_mode_#{name} = 0x00;
589
+ drive_status_#{name} = 0b00001000;
590
+ }
591
+ else if (value == 0x30)
592
+ {
593
+ zuint32 sector = 0;
594
+ sector |= (zuint32)lba_#{name}_3 << 24;
595
+ sector |= (zuint32)lba_#{name}_2 << 16;
596
+ sector |= (zuint32)lba_#{name}_1 << 8;
597
+ sector |= (zuint32)lba_#{name}_0;
598
+
599
+ internal_#{name}_load_sector(sector);
600
+ sector_data_#{name}_offset = 0;
601
+
602
+ drive_mode_#{name} = 0x01;
603
+ drive_status_#{name} = 0b00001000;
604
+ }
605
+ }
606
+ eos
607
+ end
608
+ end
609
+
610
+ # Array of sectors of this drive.
611
+ def blocks
612
+ b = []
613
+
614
+ if @initialize_from.nil?
615
+ num_sectors.times do
616
+ this_block = []
617
+ sector_size.times do
618
+ this_block << 0
619
+ end
620
+ b << this_block
621
+ end
622
+ return b
623
+ end
624
+
625
+ File.open(@initialize_from, "rb") do |f|
626
+ num_sectors.times do
627
+ this_block = f.read(sector_size)
628
+ b << this_block.unpack("C" * sector_size)
629
+ end
630
+ end
631
+
632
+ b
633
+ end
634
+
635
+ # Set file to initialize from.
636
+ def initialize_from(file)
637
+ @initialize_from = file
638
+ end
639
+
640
+ # Defines FFI API which will be available to the instance wrapper if this IO device is used.
641
+ def functions
642
+ [
643
+ {"name" => "zemu_io_#{name}_readbyte", "args" => [:uint32, :uint32], "return" => :uint8},
644
+ ]
645
+ end
646
+
647
+ # Valid parameters for a BlockDrive, along with those
648
+ # defined in [Zemu::Config::IOPort].
649
+ def params
650
+ super + %w(base_port sector_size num_sectors)
651
+ end
652
+ end
653
+
442
654
  # Non-Maskable Interrupt Timer
443
655
  #
444
656
  # Represents a timer device, the period of which can be controlled
@@ -490,7 +702,7 @@ module Zemu
490
702
 
491
703
  # Parameters accessible by this configuration object.
492
704
  def params
493
- return %w(name compiler output_directory clock_speed)
705
+ return %w(name compiler output_directory clock_speed serial_delay)
494
706
  end
495
707
 
496
708
  # Initial value for parameters of this configuration object.
@@ -498,7 +710,8 @@ module Zemu
498
710
  return {
499
711
  "compiler" => "clang",
500
712
  "output_directory" => "bin",
501
- "clock_speed" => 0
713
+ "clock_speed" => 0,
714
+ "serial_delay" => 0
502
715
  }
503
716
  end
504
717
 
data/lib/zemu/debug.rb ADDED
@@ -0,0 +1,68 @@
1
+ module Zemu
2
+ # Handles debugging functionality, like mapping of symbols to addresses,
3
+ # disassembling of instructions, etc.
4
+ module Debug
5
+ # Loads a map file at the given path, and returns a hash of address => Symbol
6
+ # for the symbols defined within.
7
+ def self.load_map(path)
8
+ symbols = {}
9
+
10
+ File.open(path, "r") do |f|
11
+ f.each_line do |l|
12
+ s = Symbol.parse(l)
13
+
14
+ if symbols[s.address].nil?
15
+ symbols[s.address] = []
16
+ end
17
+
18
+ symbols[s.address] << s
19
+ symbols[s.address].sort_by!(&:label)
20
+ end
21
+ end
22
+
23
+ return symbols
24
+ end
25
+
26
+ # Represents a symbol definition, of the form `label = address`.
27
+ class Symbol
28
+ # Parse a symbol definition, returning a Symbol instance.
29
+ def self.parse(s)
30
+ # Split on whitespace.
31
+ tokens = s.to_s.split(' ')
32
+
33
+ if tokens.size < 3
34
+ raise ArgumentError, "Invalid symbol definition: '#{s}'"
35
+ end
36
+
37
+ label = tokens[0]
38
+
39
+ address = nil
40
+
41
+ if /0x[0-9a-fA-F]+/ =~ tokens[2]
42
+ address = tokens[2][2..-1].to_i(16)
43
+ elsif /\$[0-9a-fA-F]+/ =~ tokens[2]
44
+ address = tokens[2][1..-1].to_i(16)
45
+ elsif /\d+/ =~ tokens[2]
46
+ address = tokens[2].to_i
47
+ end
48
+
49
+ if address.nil?
50
+ raise ArgumentError, "Invalid symbol address: '#{tokens[2]}'"
51
+ end
52
+
53
+ return self.new(label, address)
54
+ end
55
+
56
+ # Textual label for this symbol.
57
+ attr_reader :label
58
+
59
+ # Address of this symbol in the binary.
60
+ attr_reader :address
61
+
62
+ def initialize(label, address)
63
+ @label = label
64
+ @address = address
65
+ end
66
+ end
67
+ end
68
+ end
data/lib/zemu/instance.rb CHANGED
@@ -37,6 +37,14 @@ module Zemu
37
37
  "L'" => 19
38
38
  }
39
39
 
40
+ # Mapping of extended registers
41
+ # to the registers that comprise them.
42
+ REGISTERS_EXTENDED = {
43
+ "HL" => ["H", "L"],
44
+ "BC" => ["B", "C"],
45
+ "DE" => ["D", "E"]
46
+ }
47
+
40
48
  # States that the emulated machine can be in.
41
49
  class RunState
42
50
  # Currently executing an instruction.
@@ -54,6 +62,7 @@ module Zemu
54
62
 
55
63
  def initialize(configuration)
56
64
  @clock = configuration.clock_speed
65
+ @serial_delay = configuration.serial_delay
57
66
 
58
67
  @wrapper = make_wrapper(configuration)
59
68
 
@@ -73,6 +82,11 @@ module Zemu
73
82
  return @clock
74
83
  end
75
84
 
85
+ # Returns the delay between characters on the serial port for this instance in seconds.
86
+ def serial_delay
87
+ return @serial_delay
88
+ end
89
+
76
90
  # Returns a hash containing current values of the emulated
77
91
  # machine's registers. All names are as those given in the Z80
78
92
  # reference manual.
@@ -85,6 +99,12 @@ module Zemu
85
99
  REGISTERS.each do |reg, num|
86
100
  r[reg] = @wrapper.zemu_debug_register(@instance, num)
87
101
  end
102
+
103
+ REGISTERS_EXTENDED.each do |reg, components|
104
+ hi = components[0]
105
+ lo = components[1]
106
+ r[reg] = (r[hi] << 8) | r[lo]
107
+ end
88
108
 
89
109
  return r
90
110
  end
@@ -99,6 +119,16 @@ module Zemu
99
119
  return @wrapper.zemu_debug_get_memory(address)
100
120
  end
101
121
 
122
+ # Set the value in memory at a given address.
123
+ #
124
+ # @param address The address in memory to be set.
125
+ # @param value The value to set to.
126
+ #
127
+ # Returns nothing.
128
+ def set_memory(address, value)
129
+ @wrapper.zemu_debug_set_memory(address, value)
130
+ end
131
+
102
132
  # Write a string to the serial line of the emulated CPU.
103
133
  #
104
134
  # @param string The string to be sent.
@@ -136,6 +166,16 @@ module Zemu
136
166
  return return_string
137
167
  end
138
168
 
169
+ # Get a byte from the attached disk drive.
170
+ #
171
+ # @param sector The sector to read
172
+ # @param offset The offset in sector to read
173
+ #
174
+ # Gets a byte at the given offset in the given sector.
175
+ def drive_readbyte(sector, offset)
176
+ return @wrapper.zemu_io_block_drive_readbyte(sector, offset)
177
+ end
178
+
139
179
  # Continue running this instance until either:
140
180
  # * A HALT instruction is executed
141
181
  # * A breakpoint is hit
@@ -229,10 +269,11 @@ module Zemu
229
269
  wrapper.attach_function :zemu_debug_pc, [:pointer], :uint16
230
270
 
231
271
  wrapper.attach_function :zemu_debug_get_memory, [:uint16], :uint8
272
+ wrapper.attach_function :zemu_debug_set_memory, [:uint16, :uint8], :void
232
273
 
233
274
  configuration.io.each do |device|
234
275
  device.functions.each do |f|
235
- wrapper.attach_function(f["name"], f["args"], f["return"])
276
+ wrapper.attach_function(f["name"].to_sym, f["args"], f["return"])
236
277
  end
237
278
  end
238
279
 
@@ -5,9 +5,17 @@ module Zemu
5
5
  # Constructor.
6
6
  #
7
7
  # Create a new interactive wrapper for the given instance.
8
- def initialize(instance)
8
+ # The options hash allows the user to configure the behaviour
9
+ # of the interactive instance:
10
+ # :print_serial => true if serial input/output should be logged
11
+ # to the emulator window.
12
+ def initialize(instance, options = {})
13
+ @print_serial = options[:print_serial]
14
+
9
15
  @instance = instance
10
16
 
17
+ @symbol_table = {}
18
+
11
19
  @master, @slave = PTY.open
12
20
  log "Opened PTY at #{@slave.path}"
13
21
  end
@@ -59,6 +67,9 @@ module Zemu
59
67
  memory(cmd[1], cmd[2])
60
68
  end
61
69
 
70
+ elsif cmd[0] == "map"
71
+ load_map(cmd[1])
72
+
62
73
  elsif cmd[0] == "help"
63
74
  log "Available commands:"
64
75
  log " continue [<n>] - Continue execution for <n> cycles"
@@ -66,6 +77,7 @@ module Zemu
66
77
  log " registers - View register contents"
67
78
  log " memory <a> [<n>] - View <n> bytes of memory, starting at address <a>."
68
79
  log " <n> defaults to 1 if omitted."
80
+ log " map <path> - Load symbols from map file at <path>"
69
81
  log " break <a> - Set a breakpoint at the given address <a>."
70
82
  log " quit - End this emulator instance."
71
83
 
@@ -79,16 +91,53 @@ module Zemu
79
91
  end
80
92
 
81
93
  # Outputs a table giving the current values of the instance's registers.
94
+ # For the 16-bit registers (BC, DE, HL, IX, IY, SP, PC), attempts to identify the symbol
95
+ # to which they point.
82
96
  def registers
83
97
  log "A: #{r("A")} F: #{r("F")}"
84
- log "B: #{r("B")} C: #{r("C")}"
85
- log "D: #{r("D")} E: #{r("E")}"
86
- log "H: #{r("H")} L: #{r("L")}"
98
+
99
+ registers_gp('B', 'C')
100
+ registers_gp('D', 'E')
101
+ registers_gp('H', 'L')
102
+
87
103
  log ""
88
- log "IX: #{r16("IX")}"
89
- log "IY: #{r16("IY")}"
90
- log "SP: #{r16("SP")}"
91
- log "PC: #{r16("PC")}"
104
+
105
+ register_16("IX")
106
+ register_16("IY")
107
+ register_16("SP")
108
+ register_16("PC")
109
+ end
110
+
111
+ # Displays the value of a 16-bit register.
112
+ def register_16(r)
113
+ value = @instance.registers[r]
114
+
115
+ log "#{r}: #{r16(r)} (#{get_symbol(value)})"
116
+ end
117
+
118
+ # Displays the value of a general-purpose 16-bit register pair.
119
+ def registers_gp(hi, lo)
120
+ value = hilo(@instance.registers[hi], @instance.registers[lo])
121
+
122
+ log "#{hi}: #{r(hi)} #{lo}: #{r(lo)} (#{get_symbol(value)})"
123
+ end
124
+
125
+ # Gets the symbol associated with the given value.
126
+ # If no matching symbol, gives offset from previous symbol.
127
+ def get_symbol(value)
128
+ syms = nil
129
+ addr = value
130
+ while addr > 0 do
131
+ syms = @symbol_table[addr]
132
+ break unless syms.nil?
133
+ addr -= 1
134
+ end
135
+
136
+ sym = if syms.nil? then nil else syms[0] end
137
+
138
+ sym_str = "<#{if sym.nil? then 'undefined' else sym.label end}#{if addr == value then '' else "+#{value-addr}" end}>"
139
+
140
+ return sym_str
92
141
  end
93
142
 
94
143
  # Returns a particular 8-bit register value.
@@ -101,6 +150,11 @@ module Zemu
101
150
  return "0x%04x" % @instance.registers[reg]
102
151
  end
103
152
 
153
+ # Concatenates two 8-bit values, in big-endian format.
154
+ def hilo(hi, lo)
155
+ return (hi << 8) | lo
156
+ end
157
+
104
158
  # Continue for *up to* the given number of cycles.
105
159
  # Fewer cycles may be executed, depending on the behaviour of the processor.
106
160
  def continue(cycles=-1)
@@ -114,13 +168,19 @@ module Zemu
114
168
  cycles_left = cycles
115
169
  actual_cycles = 0
116
170
 
171
+ serial_count = @instance.serial_delay.to_f
172
+
117
173
  while ((cycles == -1) || (cycles_left > 0))
118
174
  # Get time before execution.
119
175
  start = Time.now
120
176
 
121
177
  old_pc = r16("PC")
122
178
 
123
- process_serial
179
+ if (serial_count >= @instance.serial_delay)
180
+ process_serial
181
+ serial_count = 0.0
182
+ end
183
+
124
184
  cycles_done = @instance.continue(1)
125
185
  cycles_left -= cycles_done
126
186
  actual_cycles += cycles_done
@@ -132,7 +192,9 @@ module Zemu
132
192
  if @instance.clock_speed > 0
133
193
  elapsed = ending - start
134
194
 
135
- execution_time = cycles_done * (1/@instance.clock_speed)
195
+ execution_time = cycles_done * (1.0/@instance.clock_speed)
196
+ serial_count += execution_time
197
+
136
198
  padding = execution_time - elapsed
137
199
  sleep(padding) unless padding < 0
138
200
  end
@@ -169,7 +231,7 @@ module Zemu
169
231
 
170
232
  (address.to_i(16)...address.to_i(16) + size.to_i(16)).each do |a|
171
233
  m = @instance.memory(a)
172
- if (m < 32)
234
+ if (m < 32 || m > 126)
173
235
  log "%04x: %02x ." % [a, m]
174
236
  else
175
237
  log ("%04x: %02x " % [a, m]) + m.chr("UTF-8")
@@ -177,6 +239,34 @@ module Zemu
177
239
  end
178
240
  end
179
241
 
242
+ # Loads a MAP file from the given path.
243
+ def load_map(path)
244
+ if path.nil?
245
+ log "No path specified."
246
+ return
247
+ end
248
+
249
+ unless File.exist?(path.to_s)
250
+ log "Map file '#{path}' does not exist."
251
+ return
252
+ end
253
+
254
+ if File.directory?(path.to_s)
255
+ log "Cannot open '#{path}': it is a directory."
256
+ return
257
+ end
258
+
259
+ syms = {}
260
+ begin
261
+ syms.merge! Debug.load_map(path.to_s)
262
+ rescue ArgumentError => e
263
+ log "Error loading map file: #{e.message}"
264
+ syms.clear
265
+ end
266
+
267
+ @symbol_table.merge! syms
268
+ end
269
+
180
270
  # Process serial input/output via the TTY.
181
271
  def process_serial
182
272
  # Read/write serial.
@@ -191,12 +281,12 @@ module Zemu
191
281
 
192
282
  unless input.empty?
193
283
  @instance.serial_puts input
194
- log "Serial in: #{input}"
284
+ log "Serial in: #{input} ($#{input.ord.to_s(16)})" if @print_serial
195
285
  end
196
286
 
197
287
  unless output.empty?
198
288
  @master.write output
199
- log "Serial out: #{output}"
289
+ log "Serial out: #{output} ($#{output.ord.to_s(16)})" if @print_serial
200
290
  end
201
291
  end
202
292
  end
data/lib/zemu.rb CHANGED
@@ -5,6 +5,7 @@ require 'pty'
5
5
  require_relative 'zemu/config'
6
6
  require_relative 'zemu/instance'
7
7
  require_relative 'zemu/interactive'
8
+ require_relative 'zemu/debug'
8
9
 
9
10
  # Zemu is a module providing an interface to build and interact with
10
11
  # configurable Z80 emulators.
@@ -72,10 +73,10 @@ module Zemu
72
73
  # Starts an interactive instance of an emulator, according to the given configuration.
73
74
  #
74
75
  # @param [Zemu::Config] configuration The configuration for which an emulator will be generated.
75
- def Zemu::start_interactive(configuration)
76
+ def Zemu::start_interactive(configuration, options = {})
76
77
  instance = start(configuration)
77
78
 
78
- interactive = InteractiveInstance.new(instance)
79
+ interactive = InteractiveInstance.new(instance, options)
79
80
  interactive.run
80
81
  end
81
82
 
data/src/debug.c CHANGED
@@ -72,3 +72,8 @@ zuint8 zemu_debug_get_memory(zuint16 address)
72
72
  {
73
73
  return zemu_memory_peek(address);
74
74
  }
75
+
76
+ void zemu_debug_set_memory(zuint16 address, zuint8 value)
77
+ {
78
+ zemu_memory_poke(address, value);
79
+ }
data/src/io.c.erb CHANGED
@@ -4,6 +4,21 @@
4
4
  <%= device.setup %>
5
5
  <% end %>
6
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
+
7
22
  zuint8 zemu_io_in(void * context, zuint16 port)
8
23
  {
9
24
  /* Z80 IO ports occupy the lower half of the address bus.
@@ -29,11 +44,6 @@ void zemu_io_out(void * context, zuint16 port, zuint8 value)
29
44
  <% end %>
30
45
  }
31
46
 
32
- void zemu_io_nmi(Z80 * instance)
33
- {
34
- z80_nmi(instance);
35
- }
36
-
37
47
  void zemu_io_clock(Z80 * instance)
38
48
  {
39
49
  <% io.each do |device| %>
data/src/memory.c.erb CHANGED
@@ -9,10 +9,11 @@
9
9
 
10
10
  zuint8 zemu_memory_read(void * context, zuint16 address)
11
11
  {
12
+ zuint32 address_32 = address;
12
13
  <% memory.each do |mem| %>
13
- if (address >= 0x<%= mem.address.to_s(16) %> && address < 0x<%= (mem.address + mem.size).to_s(16) %>)
14
+ if (address_32 >= 0x<%= mem.address.to_s(16) %> && address_32 < 0x<%= (mem.address + mem.size).to_s(16) %>)
14
15
  {
15
- return zemu_memory_block_<%= mem.name %>[address - 0x<%= mem.address.to_s(16) %>];
16
+ return zemu_memory_block_<%= mem.name %>[address_32 - 0x<%= mem.address.to_s(16) %>];
16
17
  }
17
18
  <% end %>
18
19
  /* Unmapped memory has a value of 0. */
@@ -21,23 +22,38 @@ zuint8 zemu_memory_read(void * context, zuint16 address)
21
22
 
22
23
  void zemu_memory_write(void * context, zuint16 address, zuint8 value)
23
24
  {
25
+ zuint32 address_32 = address;
24
26
  <% memory.each do |mem| %>
25
27
  <% next if mem.readonly? %>
26
- if (address >= 0x<%= mem.address.to_s(16) %> && address < 0x<%= (mem.address + mem.size).to_s(16) %>)
28
+ if (address_32 >= 0x<%= mem.address.to_s(16) %> && address_32 < 0x<%= (mem.address + mem.size).to_s(16) %>)
27
29
  {
28
- zemu_memory_block_<%= mem.name %>[address - 0x<%= mem.address.to_s(16) %>] = value;
30
+ zemu_memory_block_<%= mem.name %>[address_32 - 0x<%= mem.address.to_s(16) %>] = value;
29
31
  }
30
32
  <% end %>
31
33
  }
32
34
 
33
35
  zuint8 zemu_memory_peek(zuint16 address)
34
36
  {
37
+ zuint32 address_32 = address;
35
38
  <% memory.each do |mem| %>
36
- if (address >= 0x<%= mem.address.to_s(16) %> && address < 0x<%= (mem.address + mem.size).to_s(16) %>)
39
+ if (address_32 >= 0x<%= mem.address.to_s(16) %> && address_32 < 0x<%= (mem.address + mem.size).to_s(16) %>)
37
40
  {
38
- return zemu_memory_block_<%= mem.name %>[address - 0x<%= mem.address.to_s(16) %>];
41
+ return zemu_memory_block_<%= mem.name %>[address_32 - 0x<%= mem.address.to_s(16) %>];
39
42
  }
40
43
  <% end %>
41
44
  /* Unmapped memory has a value of 0. */
42
45
  return 0;
43
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 CHANGED
@@ -7,3 +7,5 @@ zuint8 zemu_memory_read(void * context, zuint16 address);
7
7
  void zemu_memory_write(void * context, zuint16 address, zuint8 value);
8
8
 
9
9
  zuint8 zemu_memory_peek(zuint16 address);
10
+
11
+ void zemu_memory_poke(zuint16 address, zuint8 value);
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.3.2
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jay Valentine
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-27 00:00:00.000000000 Z
11
+ date: 2021-11-18 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
@@ -29,6 +29,7 @@ extra_rdoc_files: []
29
29
  files:
30
30
  - lib/zemu.rb
31
31
  - lib/zemu/config.rb
32
+ - lib/zemu/debug.rb
32
33
  - lib/zemu/instance.rb
33
34
  - lib/zemu/interactive.rb
34
35
  - src/debug.c
@@ -333,8 +334,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
333
334
  - !ruby/object:Gem::Version
334
335
  version: '0'
335
336
  requirements: []
336
- rubyforge_project:
337
- rubygems_version: 2.7.6
337
+ rubygems_version: 3.1.2
338
338
  signing_key:
339
339
  specification_version: 4
340
340
  summary: A configurable Z80 emulator.