From e412ac311b09de281709374ff01fc0ed8aace70c Mon Sep 17 00:00:00 2001 From: sacckey Date: Sun, 11 Oct 2026 06:01:56 +0900 Subject: [PATCH 1/2] Keep RLC and RRC results on memory within a byte RLC (HL) and RRC (HL) wrote the unmasked result back to memory, so a byte grew by up to seven bits on every rotation. CRuby kept running with the larger Integer, but the Spinel browser build raised RangeError once the value no longer fit. Co-Authored-By: Claude Opus 5.5 --- lib/rubyboy/cpu.rb | 4 ++-- spec/rubyboy/cpu_spec.rb | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 spec/rubyboy/cpu_spec.rb diff --git a/lib/rubyboy/cpu.rb b/lib/rubyboy/cpu.rb index b0a78c5..af04b1e 100644 --- a/lib/rubyboy/cpu.rb +++ b/lib/rubyboy/cpu.rb @@ -1071,7 +1071,7 @@ def reti(cycles:) def rlc8(x, cycles:) value = get_value(x) - value = (value << 1) | (value >> 7) + value = ((value << 1) | (value >> 7)) & 0xff set_value(x, value) update_flags( z: value == 0, @@ -1085,7 +1085,7 @@ def rlc8(x, cycles:) def rrc8(x, cycles:) value = get_value(x) - value = (value >> 1) | (value << 7) + value = ((value >> 1) | (value << 7)) & 0xff set_value(x, value) update_flags( z: value == 0, diff --git a/spec/rubyboy/cpu_spec.rb b/spec/rubyboy/cpu_spec.rb new file mode 100644 index 0000000..04cf7e2 --- /dev/null +++ b/spec/rubyboy/cpu_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +RSpec.describe Rubyboy::Cpu do + let(:memory) { Array.new(0x10000, 0) } + let(:cpu) do + bus = instance_double(Rubyboy::Bus) + allow(bus).to receive(:read_byte) { |addr| memory[addr] } + allow(bus).to receive(:read_word) { |addr| memory[addr] | (memory[addr + 1] << 8) } + allow(bus).to receive(:write_byte) { |addr, value| memory[addr] = value } + described_class.new(bus, Rubyboy::Interrupt.new) + end + + # LD HL, 0xc000, then the CB-prefixed rotate eight times: the byte comes back. + def rotate_hl_eight_times(cb_opcode, value) + program = [0x21, 0x00, 0xc0] + ([0xcb, cb_opcode] * 8) + memory[0x0100, program.size] = program + memory[0xc000] = value + 9.times { cpu.exec } + memory[0xc000] + end + + it 'keeps RLC (HL) within a byte' do + expect(rotate_hl_eight_times(0x06, 0x81)).to eq(0x81) + end + + it 'keeps RRC (HL) within a byte' do + expect(rotate_hl_eight_times(0x0e, 0x81)).to eq(0x81) + end +end From f08ba64ecd4c5da0e7ba4ced7327294db0745bc7 Mon Sep 17 00:00:00 2001 From: sacckey Date: Sun, 11 Oct 2026 06:01:56 +0900 Subject: [PATCH 2/2] Bump version to 1.7.1 Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 4 ++++ Gemfile.lock | 2 +- lib/rubyboy/version.rb | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbcc456..ad741fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [1.7.1] - 2026-10-11 + +- Fix RLC and RRC on memory writing values over a byte, which could stop the Spinel browser build + ## [1.7.0] - 2026-10-10 - Require Ruby 4.0 or higher diff --git a/Gemfile.lock b/Gemfile.lock index 9daf4e4..d4b0d31 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - rubyboy (1.7.0) + rubyboy (1.7.1) ffi (~> 1.16, >= 1.16.3) GEM diff --git a/lib/rubyboy/version.rb b/lib/rubyboy/version.rb index 4491007..7ec6611 100644 --- a/lib/rubyboy/version.rb +++ b/lib/rubyboy/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Rubyboy - VERSION = '1.7.0' + VERSION = '1.7.1' end