zemu 0.3.9 → 0.5.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: 3278a3fa602a14489f1bfe77bee8d51781be503fac50dba4334dd400f492b5ca
4
- data.tar.gz: 0dc3644e09613e7449bbc2dd3167f4823fca647cd0b5df94b35a0d39768a8b26
3
+ metadata.gz: b7ab3a65fd7f45896cac4297f3715d138a1753def97266f78dd1af1b30e0f012
4
+ data.tar.gz: 6d33e40e95a4add449616e6793b2817d2a9c5113f7fcc6fe0f7bf075d0d9dec2
5
5
  SHA512:
6
- metadata.gz: bb87eef723a62c64742f06ec85bf7f5c976103c6e5b8678b5efec665b49ea3242efe59238f1ea28808c7ef3e597471ec050cda5f0152f3c74fa900f88df294ea
7
- data.tar.gz: 29de4f2e78acae5a24a443595e3b683acb3fd3a6d00ec84991c1126e3e7b07ce35078de8721347ec84926ee99fd2bd21912b1ef768fa868c0d1cc75ae966db02
6
+ metadata.gz: 4551b92671a66d326ec426cbfd240427d93d181ef000c47bda8505185f9ac865b51411bfc35222d097f79e810af9cb9337a50473c7ef80c5b0a63c7d98fa550f
7
+ data.tar.gz: aeb983650f57cbb5a7e76b6caffbf81a0ccef919133754fae2608d1a8b04708b329ae5d75e2f5606e8409808b8b456d26b776c28c0ee9a7c39f6f4c1b1ddf038
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
data/lib/zemu/debug.rb ADDED
@@ -0,0 +1,96 @@
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
+ symbols << s
15
+ end
16
+ end
17
+
18
+ return Symbols.new(symbols)
19
+ end
20
+
21
+ # Contains a set of symbols.
22
+ # Allows for various lookup operations.
23
+ class Symbols
24
+ # Constructor.
25
+ def initialize(syms)
26
+ @syms = []
27
+ syms.each do |s|
28
+ @syms << s
29
+ end
30
+ end
31
+
32
+ # Access all symbols with a given address.
33
+ def [](address)
34
+ at_address = []
35
+ @syms.each do |s|
36
+ if s.address == address
37
+ at_address << s
38
+ end
39
+ end
40
+
41
+ return at_address.sort_by(&:label)
42
+ end
43
+
44
+ # Find a symbol with a given name.
45
+ def find_by_name(name)
46
+ @syms.each do |s|
47
+ return s if s.label == name
48
+ end
49
+
50
+ return nil
51
+ end
52
+ end
53
+
54
+ # Represents a symbol definition, of the form `label = address`.
55
+ class Symbol
56
+ # Parse a symbol definition, returning a Symbol instance.
57
+ def self.parse(s)
58
+ # Split on whitespace.
59
+ tokens = s.to_s.split(' ')
60
+
61
+ if tokens.size < 3
62
+ raise ArgumentError, "Invalid symbol definition: '#{s}'"
63
+ end
64
+
65
+ label = tokens[0]
66
+
67
+ address = nil
68
+
69
+ if /0x[0-9a-fA-F]+/ =~ tokens[2]
70
+ address = tokens[2][2..-1].to_i(16)
71
+ elsif /\$[0-9a-fA-F]+/ =~ tokens[2]
72
+ address = tokens[2][1..-1].to_i(16)
73
+ elsif /\d+/ =~ tokens[2]
74
+ address = tokens[2].to_i
75
+ end
76
+
77
+ if address.nil?
78
+ raise ArgumentError, "Invalid symbol address: '#{tokens[2]}'"
79
+ end
80
+
81
+ return self.new(label, address)
82
+ end
83
+
84
+ # Textual label for this symbol.
85
+ attr_reader :label
86
+
87
+ # Address of this symbol in the binary.
88
+ attr_reader :address
89
+
90
+ def initialize(label, address)
91
+ @label = label
92
+ @address = address
93
+ end
94
+ end
95
+ end
96
+ 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.
@@ -53,6 +61,10 @@ module Zemu
53
61
  end
54
62
 
55
63
  def initialize(configuration)
64
+ # Methods defined by IO devices that we make
65
+ # accessible to the user.
66
+ @io_methods = []
67
+
56
68
  @clock = configuration.clock_speed
57
69
  @serial_delay = configuration.serial_delay
58
70
 
@@ -91,6 +103,12 @@ module Zemu
91
103
  REGISTERS.each do |reg, num|
92
104
  r[reg] = @wrapper.zemu_debug_register(@instance, num)
93
105
  end
106
+
107
+ REGISTERS_EXTENDED.each do |reg, components|
108
+ hi = components[0]
109
+ lo = components[1]
110
+ r[reg] = (r[hi] << 8) | r[lo]
111
+ end
94
112
 
95
113
  return r
96
114
  end
@@ -105,6 +123,16 @@ module Zemu
105
123
  return @wrapper.zemu_debug_get_memory(address)
106
124
  end
107
125
 
126
+ # Set the value in memory at a given address.
127
+ #
128
+ # @param address The address in memory to be set.
129
+ # @param value The value to set to.
130
+ #
131
+ # Returns nothing.
132
+ def set_memory(address, value)
133
+ @wrapper.zemu_debug_set_memory(address, value)
134
+ end
135
+
108
136
  # Write a string to the serial line of the emulated CPU.
109
137
  #
110
138
  # @param string The string to be sent.
@@ -142,6 +170,16 @@ module Zemu
142
170
  return return_string
143
171
  end
144
172
 
173
+ # Get a byte from the attached disk drive.
174
+ #
175
+ # @param sector The sector to read
176
+ # @param offset The offset in sector to read
177
+ #
178
+ # Gets a byte at the given offset in the given sector.
179
+ def drive_readbyte(sector, offset)
180
+ return @wrapper.zemu_io_block_drive_readbyte(sector, offset)
181
+ end
182
+
145
183
  # Continue running this instance until either:
146
184
  # * A HALT instruction is executed
147
185
  # * A breakpoint is hit
@@ -235,16 +273,27 @@ module Zemu
235
273
  wrapper.attach_function :zemu_debug_pc, [:pointer], :uint16
236
274
 
237
275
  wrapper.attach_function :zemu_debug_get_memory, [:uint16], :uint8
276
+ wrapper.attach_function :zemu_debug_set_memory, [:uint16, :uint8], :void
238
277
 
239
278
  configuration.io.each do |device|
240
279
  device.functions.each do |f|
241
- wrapper.attach_function(f["name"], f["args"], f["return"])
280
+ wrapper.attach_function(f["name"].to_sym, f["args"], f["return"])
281
+ @io_methods << f["name"].to_sym
242
282
  end
243
283
  end
244
284
 
245
285
  return wrapper
246
286
  end
247
287
 
288
+ # Redirects calls to I/O FFI functions.
289
+ def method_missing(method, *args)
290
+ if @io_methods.include? method
291
+ return @wrapper.send(method)
292
+ end
293
+
294
+ super
295
+ end
296
+
248
297
  private :make_wrapper
249
298
  end
250
299
  end
@@ -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,7 +168,7 @@ module Zemu
114
168
  cycles_left = cycles
115
169
  actual_cycles = 0
116
170
 
117
- serial_count = @instance.serial_delay
171
+ serial_count = @instance.serial_delay.to_f
118
172
 
119
173
  while ((cycles == -1) || (cycles_left > 0))
120
174
  # Get time before execution.
@@ -124,7 +178,7 @@ module Zemu
124
178
 
125
179
  if (serial_count >= @instance.serial_delay)
126
180
  process_serial
127
- serial_count = 0
181
+ serial_count = 0.0
128
182
  end
129
183
 
130
184
  cycles_done = @instance.continue(1)
@@ -185,6 +239,34 @@ module Zemu
185
239
  end
186
240
  end
187
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
+
188
270
  # Process serial input/output via the TTY.
189
271
  def process_serial
190
272
  # Read/write serial.
@@ -199,12 +281,12 @@ module Zemu
199
281
 
200
282
  unless input.empty?
201
283
  @instance.serial_puts input
202
- log "Serial in: #{input}"
284
+ log "Serial in: #{input} ($#{input.ord.to_s(16)})" if @print_serial
203
285
  end
204
286
 
205
287
  unless output.empty?
206
288
  @master.write output
207
- log "Serial out: #{output}"
289
+ log "Serial out: #{output} ($#{output.ord.to_s(16)})" if @print_serial
208
290
  end
209
291
  end
210
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.
@@ -61,10 +62,12 @@ module Zemu
61
62
  SRC = File.join(__dir__, "..", "src")
62
63
 
63
64
  # Build and start an emulator according to the given configuration.
65
+ # Returns the emulator instance.
64
66
  #
65
67
  # @param [Zemu::Config] configuration The configuration for which an emulator will be generated.
66
- def Zemu::start(configuration)
67
- build(configuration)
68
+ # @param user_defines Any user-defined preprocessor macros.
69
+ def Zemu::start(configuration, user_defines={})
70
+ build(configuration, user_defines)
68
71
 
69
72
  return Instance.new(configuration)
70
73
  end
@@ -72,19 +75,19 @@ module Zemu
72
75
  # Starts an interactive instance of an emulator, according to the given configuration.
73
76
  #
74
77
  # @param [Zemu::Config] configuration The configuration for which an emulator will be generated.
75
- def Zemu::start_interactive(configuration)
78
+ def Zemu::start_interactive(configuration, options = {})
76
79
  instance = start(configuration)
77
80
 
78
- interactive = InteractiveInstance.new(instance)
81
+ interactive = InteractiveInstance.new(instance, options)
79
82
  interactive.run
80
83
  end
81
84
 
82
85
  # Builds a library according to the given configuration.
86
+ # Returns true if the build is a success, false (build failed) or nil (compiler not found) otherwise.
83
87
  #
84
88
  # @param [Zemu::Config] configuration The configuration for which an emulator will be generated.
85
- #
86
- # @returns true if the build is a success, false (build failed) or nil (compiler not found) otherwise.
87
- def Zemu::build(configuration)
89
+ # @param user_defines Any user-defined preprocessor macros.
90
+ def Zemu::build(configuration, user_defines={})
88
91
  # Create the output directory unless it already exists.
89
92
  unless Dir.exist? configuration.output_directory
90
93
  Dir.mkdir configuration.output_directory
@@ -115,6 +118,8 @@ module Zemu
115
118
  "CPU_Z80_USE_LOCAL_HEADER" => 1
116
119
  }
117
120
 
121
+ defines.merge! user_defines
122
+
118
123
  defines_str = defines.map { |d, v| "-D#{d}=#{v}" }.join(" ")
119
124
 
120
125
  includes = [
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/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.9
4
+ version: 0.5.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: 2020-08-26 00:00:00.000000000 Z
11
+ date: 2021-11-26 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.