badline 0.1.0 → 0.2.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 +4 -4
- data/CHANGELOG.md +185 -0
- data/CLAUDE.md +217 -0
- data/CODE_OF_CONDUCT.md +92 -0
- data/CONTRIBUTING.md +51 -0
- data/README.md +224 -31
- data/doc/pinned-behaviour.md +1117 -0
- data/exe/badline +22 -2
- data/exe/badline-sid +34 -0
- data/lib/badline/address_bus.rb +96 -25
- data/lib/badline/audio/aiff.rb +36 -0
- data/lib/badline/audio/bare_player.rb +129 -0
- data/lib/badline/audio/cli.rb +133 -0
- data/lib/badline/audio/console.rb +75 -0
- data/lib/badline/audio/jukebox.rb +82 -0
- data/lib/badline/audio/machine_player.rb +57 -0
- data/lib/badline/audio/options.rb +137 -0
- data/lib/badline/audio/pcm_writer.rb +57 -0
- data/lib/badline/audio/playback.rb +108 -0
- data/lib/badline/audio/renderer.rb +85 -0
- data/lib/badline/audio/sdl_sink.rb +102 -0
- data/lib/badline/audio/wav.rb +23 -0
- data/lib/badline/audio.rb +14 -0
- data/lib/badline/cartridge/action_replay.rb +111 -0
- data/lib/badline/cartridge/atomic_power.rb +24 -0
- data/lib/badline/cartridge/bank.rb +81 -0
- data/lib/badline/cartridge/comal80.rb +48 -0
- data/lib/badline/cartridge/dinamic.rb +34 -0
- data/lib/badline/cartridge/easy_flash.rb +122 -0
- data/lib/badline/cartridge/epyx_fastload.rb +71 -0
- data/lib/badline/cartridge/final_cartridge3.rb +77 -0
- data/lib/badline/cartridge/flash.rb +265 -0
- data/lib/badline/cartridge/freezer.rb +31 -0
- data/lib/badline/cartridge/fun_play.rb +38 -0
- data/lib/badline/cartridge/g_mod2.rb +62 -0
- data/lib/badline/cartridge/game_system.rb +40 -0
- data/lib/badline/cartridge/kcs_power.rb +72 -0
- data/lib/badline/cartridge/mach5.rb +34 -0
- data/lib/badline/cartridge/magic_desk.rb +6 -3
- data/lib/badline/cartridge/ocean.rb +11 -12
- data/lib/badline/cartridge/pagefox.rb +56 -0
- data/lib/badline/cartridge/retro_replay.rb +276 -0
- data/lib/badline/cartridge/rex_utility.rb +33 -0
- data/lib/badline/cartridge/rgcd.rb +48 -0
- data/lib/badline/cartridge/simons_basic.rb +40 -0
- data/lib/badline/cartridge/standard.rb +3 -19
- data/lib/badline/cartridge/super_games.rb +36 -0
- data/lib/badline/cartridge/westermann.rb +34 -0
- data/lib/badline/cartridge/zaxxon.rb +50 -0
- data/lib/badline/cartridge.rb +132 -16
- data/lib/badline/cia/interrupt_register.rb +102 -0
- data/lib/badline/cia/serial.rb +219 -0
- data/lib/badline/cia/timer.rb +43 -31
- data/lib/badline/cia.rb +138 -88
- data/lib/badline/color_memory.rb +14 -5
- data/lib/badline/computer.rb +97 -12
- data/lib/badline/control_ports.rb +54 -9
- data/lib/badline/cpu/addressing.rb +131 -0
- data/lib/badline/cpu/microcode.rb +169 -0
- data/lib/badline/cpu/operations.rb +88 -0
- data/lib/badline/cpu/stack_operations.rb +82 -0
- data/lib/badline/cpu.rb +109 -168
- data/lib/badline/cycleable.rb +3 -0
- data/lib/badline/datasette.rb +81 -0
- data/lib/badline/debug_register.rb +28 -0
- data/lib/badline/gui/application.rb +79 -18
- data/lib/badline/gui/gamepads.rb +97 -0
- data/lib/badline/gui/joy_map.rb +15 -7
- data/lib/badline/gui.rb +1 -0
- data/lib/badline/input/mouse1351.rb +41 -0
- data/lib/badline/input/paddles.rb +36 -0
- data/lib/badline/input.rb +4 -0
- data/lib/badline/instruction_set/arithmetic.rb +16 -21
- data/lib/badline/instruction_set/bitwise.rb +17 -22
- data/lib/badline/instruction_set/branch.rb +20 -19
- data/lib/badline/instruction_set/flag.rb +7 -7
- data/lib/badline/instruction_set/illegal.rb +14 -22
- data/lib/badline/instruction_set/inc_dec.rb +8 -10
- data/lib/badline/instruction_set/stack.rb +5 -57
- data/lib/badline/instruction_set/transfer.rb +9 -9
- data/lib/badline/instruction_set.rb +9 -31
- data/lib/badline/interrupts.rb +104 -0
- data/lib/badline/joystick.rb +9 -9
- data/lib/badline/kernal_trap/channel.rb +51 -0
- data/lib/badline/kernal_trap/drive/status.rb +49 -0
- data/lib/badline/kernal_trap/drive.rb +277 -0
- data/lib/badline/kernal_trap/file.rb +5 -25
- data/lib/badline/kernal_trap/load.rb +86 -24
- data/lib/badline/kernal_trap/routine.rb +51 -0
- data/lib/badline/kernal_trap/save.rb +46 -14
- data/lib/badline/kernal_trap/serial.rb +171 -0
- data/lib/badline/kernal_trap.rb +4 -0
- data/lib/badline/keyboard.rb +40 -16
- data/lib/badline/media.rb +48 -10
- data/lib/badline/memory.rb +5 -0
- data/lib/badline/roms/eapi/LICENSE.md +15 -0
- data/lib/badline/roms/eapi/README +41 -0
- data/lib/badline/roms/eapi/eapi-am29f040-14 +0 -0
- data/lib/badline/sid/decimator.rb +46 -0
- data/lib/badline/sid/envelope.rb +232 -0
- data/lib/badline/sid/filter.rb +213 -0
- data/lib/badline/sid/voice.rb +61 -0
- data/lib/badline/sid/waveform/combined.rb +126 -0
- data/lib/badline/sid/waveform/fast_forward.rb +105 -0
- data/lib/badline/sid/waveform/noise_writeback.rb +82 -0
- data/lib/badline/sid/waveform.rb +289 -0
- data/lib/badline/sid.rb +274 -7
- data/lib/badline/status.rb +251 -23
- data/lib/badline/storage/crt_file.rb +18 -2
- data/lib/badline/storage/d64_image.rb +6 -0
- data/lib/badline/storage/d71_image.rb +4 -0
- data/lib/badline/storage/d81_image.rb +4 -0
- data/lib/badline/storage/disk_image.rb +99 -13
- data/lib/badline/storage/host_directory.rb +30 -4
- data/lib/badline/storage/sid_file.rb +274 -0
- data/lib/badline/storage/song_lengths.rb +65 -0
- data/lib/badline/storage/t64.rb +67 -0
- data/lib/badline/storage/tap.rb +68 -0
- data/lib/badline/storage.rb +19 -0
- data/lib/badline/time_of_day.rb +105 -52
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic/bank.rb +14 -6
- data/lib/badline/vic/border_mask.rb +76 -0
- data/lib/badline/vic/collisions.rb +104 -0
- data/lib/badline/vic/color_patches.rb +33 -0
- data/lib/badline/vic/display_state.rb +92 -29
- data/lib/badline/vic/graphics_mode.rb +78 -53
- data/lib/badline/vic/graphics_shifter.rb +140 -0
- data/lib/badline/vic/register_log.rb +63 -0
- data/lib/badline/vic/registers.rb +3 -0
- data/lib/badline/vic/sequencer.rb +163 -137
- data/lib/badline/vic/sequencer_output.rb +116 -0
- data/lib/badline/vic/sprite/internal_bus.rb +49 -0
- data/lib/badline/vic/sprite/shifter.rb +129 -0
- data/lib/badline/vic/sprite.rb +275 -65
- data/lib/badline/vic/sprites.rb +220 -58
- data/lib/badline/vic.rb +389 -55
- data/lib/badline.rb +5 -0
- data/vendor/.gitkeep +0 -0
- metadata +104 -4
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module GUI
|
|
5
|
+
# Host game controllers wired onto the control ports.
|
|
6
|
+
#
|
|
7
|
+
# The first controller found drives joystick 2, the port most games read;
|
|
8
|
+
# a second one drives joystick 1. D-pad and left stick both steer, every
|
|
9
|
+
# face and shoulder button fires.
|
|
10
|
+
#
|
|
11
|
+
# SDL announces arrivals by device index but reports button presses by
|
|
12
|
+
# instance id, and the binding gives no way to line the two up, so this
|
|
13
|
+
# class polls the controllers every frame. It only releases directions it
|
|
14
|
+
# pressed itself, so the keyboard mapping keeps working with a pad
|
|
15
|
+
# connected.
|
|
16
|
+
class Gamepads
|
|
17
|
+
DEADZONE = 8_000
|
|
18
|
+
PORTS = [2, 1].freeze
|
|
19
|
+
|
|
20
|
+
BUTTONS = {
|
|
21
|
+
up: [SDL2::GameController::Button::DPAD_UP],
|
|
22
|
+
down: [SDL2::GameController::Button::DPAD_DOWN],
|
|
23
|
+
left: [SDL2::GameController::Button::DPAD_LEFT],
|
|
24
|
+
right: [SDL2::GameController::Button::DPAD_RIGHT],
|
|
25
|
+
fire: [SDL2::GameController::Button::A, SDL2::GameController::Button::B,
|
|
26
|
+
SDL2::GameController::Button::X, SDL2::GameController::Button::Y,
|
|
27
|
+
SDL2::GameController::Button::LEFTSHOULDER,
|
|
28
|
+
SDL2::GameController::Button::RIGHTSHOULDER]
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
31
|
+
AXES = {
|
|
32
|
+
up: [SDL2::GameController::Axis::LEFTY, -1],
|
|
33
|
+
down: [SDL2::GameController::Axis::LEFTY, 1],
|
|
34
|
+
left: [SDL2::GameController::Axis::LEFTX, -1],
|
|
35
|
+
right: [SDL2::GameController::Axis::LEFTX, 1]
|
|
36
|
+
}.freeze
|
|
37
|
+
|
|
38
|
+
def initialize(computer)
|
|
39
|
+
SDL2.init(SDL2::INIT_GAMECONTROLLER)
|
|
40
|
+
@computer = computer
|
|
41
|
+
@controllers = []
|
|
42
|
+
@pressed = Array.new(PORTS.size) { [] }
|
|
43
|
+
rescan
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def names = @controllers.map(&:name)
|
|
47
|
+
|
|
48
|
+
def rescan
|
|
49
|
+
close
|
|
50
|
+
@controllers = (0...SDL2::Joystick.num_connected_joysticks)
|
|
51
|
+
.select { |index| SDL2::Joystick.game_controller?(index) }
|
|
52
|
+
.first(PORTS.size)
|
|
53
|
+
.map { |index| SDL2::GameController.open(index) }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def poll
|
|
57
|
+
@controllers.each_with_index { |controller, index| apply(controller, index) }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def close
|
|
61
|
+
@controllers.each { |controller| controller.destroy unless controller.destroy? }
|
|
62
|
+
@controllers = []
|
|
63
|
+
@pressed.size.times { |index| release(index) }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def apply(controller, index)
|
|
69
|
+
active = Joystick::DIRECTIONS.keys.select { |dir| active?(controller, dir) }
|
|
70
|
+
(@pressed[index] - active).each { |dir| joystick(index).release(dir) }
|
|
71
|
+
active.each { |dir| joystick(index).press(dir) }
|
|
72
|
+
@pressed[index] = active
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def release(index)
|
|
76
|
+
@pressed[index].each { |dir| joystick(index).release(dir) }
|
|
77
|
+
@pressed[index] = []
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def active?(controller, direction)
|
|
81
|
+
BUTTONS.fetch(direction).any? { |button| controller.button_pressed?(button) } ||
|
|
82
|
+
tilted?(controller, direction)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def tilted?(controller, direction)
|
|
86
|
+
axis, sign = AXES[direction]
|
|
87
|
+
return false unless axis
|
|
88
|
+
|
|
89
|
+
controller.axis(axis) * sign > DEADZONE
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def joystick(index)
|
|
93
|
+
PORTS[index] == 1 ? @computer.joystick1 : @computer.joystick2
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
data/lib/badline/gui/joy_map.rb
CHANGED
|
@@ -2,17 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
module Badline
|
|
4
4
|
module GUI
|
|
5
|
+
# Maps host keys onto the two control ports while joystick mode is on.
|
|
6
|
+
#
|
|
7
|
+
# The arrow cluster plus space drives joystick 2, WASD plus left shift
|
|
8
|
+
# joystick 1, so both ports can be played at once.
|
|
5
9
|
class JoyMap
|
|
6
|
-
|
|
7
|
-
"Up" => :up,
|
|
8
|
-
"
|
|
9
|
-
"Left" => :left,
|
|
10
|
-
"Right" => :right,
|
|
11
|
-
"Space" => :fire
|
|
10
|
+
PORTS = {
|
|
11
|
+
2 => { "Up" => :up, "Down" => :down, "Left" => :left, "Right" => :right, "Space" => :fire }.freeze,
|
|
12
|
+
1 => { "W" => :up, "S" => :down, "A" => :left, "D" => :right, "Left Shift" => :fire }.freeze
|
|
12
13
|
}.freeze
|
|
13
14
|
|
|
14
15
|
def self.parse(event)
|
|
15
|
-
|
|
16
|
+
name = SDL2::Key.name_of(event.sym)
|
|
17
|
+
|
|
18
|
+
PORTS.each do |port, map|
|
|
19
|
+
direction = map[name]
|
|
20
|
+
return [port, direction] if direction
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
nil
|
|
16
24
|
end
|
|
17
25
|
end
|
|
18
26
|
end
|
data/lib/badline/gui.rb
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Input
|
|
5
|
+
# Commodore 1351 mouse in proportional mode.
|
|
6
|
+
#
|
|
7
|
+
# Each axis drives a 6-bit counter that the mouse presents on POTX/POTY
|
|
8
|
+
# shifted up one bit. A driver recovers movement by masking off bit 7,
|
|
9
|
+
# subtracting the previous reading and halving the difference. The counters
|
|
10
|
+
# wrap, so a single reading says nothing on its own, and the driver has to
|
|
11
|
+
# sample before the mouse travels 32 counts.
|
|
12
|
+
#
|
|
13
|
+
# #move takes host deltas with y counting down the screen; the counter
|
|
14
|
+
# counts the other way. The left button sits on the joystick fire line, the
|
|
15
|
+
# right button on the up line.
|
|
16
|
+
class Mouse1351
|
|
17
|
+
BUTTONS = { left: :fire, right: :up }.freeze
|
|
18
|
+
|
|
19
|
+
def initialize
|
|
20
|
+
@lines = Joystick.new
|
|
21
|
+
@x = 0
|
|
22
|
+
@y = 0
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def press(button) = @lines.press(BUTTONS[button])
|
|
26
|
+
|
|
27
|
+
def release(button) = @lines.release(BUTTONS[button])
|
|
28
|
+
|
|
29
|
+
def port_bits = @lines.port_bits
|
|
30
|
+
|
|
31
|
+
def move(delta_x, delta_y)
|
|
32
|
+
@x = (@x + delta_x) & 0x3f
|
|
33
|
+
@y = (@y - delta_y) & 0x3f
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def pot_x = @x << 1
|
|
37
|
+
|
|
38
|
+
def pot_y = @y << 1
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Input
|
|
5
|
+
# A pair of paddles on one control port.
|
|
6
|
+
#
|
|
7
|
+
# Paddle A sits on POTX with its button on the joystick left line, paddle B
|
|
8
|
+
# on POTY with its button on the right line. Positions are the 0-255 counts
|
|
9
|
+
# SID reads back, and the knobs stop at either end.
|
|
10
|
+
#
|
|
11
|
+
# Buttons are named after the host mouse buttons, as on the 1351: the left
|
|
12
|
+
# one fires paddle A, the right one paddle B.
|
|
13
|
+
class Paddles
|
|
14
|
+
BUTTONS = { left: :left, right: :right }.freeze
|
|
15
|
+
|
|
16
|
+
attr_reader :pot_x, :pot_y
|
|
17
|
+
|
|
18
|
+
def initialize
|
|
19
|
+
@lines = Joystick.new
|
|
20
|
+
@pot_x = 0x80
|
|
21
|
+
@pot_y = 0x80
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def press(button) = @lines.press(BUTTONS[button])
|
|
25
|
+
|
|
26
|
+
def release(button) = @lines.release(BUTTONS[button])
|
|
27
|
+
|
|
28
|
+
def port_bits = @lines.port_bits
|
|
29
|
+
|
|
30
|
+
def move(turn_a, turn_b)
|
|
31
|
+
@pot_x = (@pot_x + turn_a).clamp(0, 0xff)
|
|
32
|
+
@pot_y = (@pot_y + turn_b).clamp(0, 0xff)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -15,19 +15,18 @@ module Badline
|
|
|
15
15
|
# $79 - absolute_y - 4+ cycles
|
|
16
16
|
# $7D - absolute_x - 4+ cycles
|
|
17
17
|
def adc(_addr, value)
|
|
18
|
-
|
|
19
|
-
result = a + v + status.carry
|
|
18
|
+
result = a + value + status.carry
|
|
20
19
|
status.zero = result.nobits?(0xff)
|
|
21
20
|
|
|
22
21
|
if status.decimal?
|
|
23
|
-
result = (a & 0x0f) + (
|
|
22
|
+
result = (a & 0x0f) + (value & 0x0f) + status.carry
|
|
24
23
|
result += 0x06 if result > 0x09
|
|
25
24
|
c = result > 0x0f ? 1 : 0
|
|
26
|
-
result = (a & 0xf0) + (
|
|
27
|
-
update_calculation_flags(
|
|
25
|
+
result = (a & 0xf0) + (value & 0xf0) + (c << 4) + (result & 0x0f)
|
|
26
|
+
update_calculation_flags(value, result)
|
|
28
27
|
result += 0x60 if result > 0x9f
|
|
29
28
|
else
|
|
30
|
-
update_calculation_flags(
|
|
29
|
+
update_calculation_flags(value, result)
|
|
31
30
|
end
|
|
32
31
|
|
|
33
32
|
status.carry = result > 0xff
|
|
@@ -46,9 +45,8 @@ module Badline
|
|
|
46
45
|
# $D9 - absolute_y - 4+ cycles
|
|
47
46
|
# $DD - absolute_x - 4+ cycles
|
|
48
47
|
def cmp(_addr, value)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
update_number_flags(@a - v)
|
|
48
|
+
status.carry = (@a >= value)
|
|
49
|
+
update_number_flags(@a - value)
|
|
52
50
|
end
|
|
53
51
|
|
|
54
52
|
# Compare memory with X register.
|
|
@@ -58,9 +56,8 @@ module Badline
|
|
|
58
56
|
# $E4 - zeropage - 3 cycles
|
|
59
57
|
# $EC - absolute - 4 cycles
|
|
60
58
|
def cpx(_addr, value)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
update_number_flags(@x - v)
|
|
59
|
+
status.carry = @x >= value
|
|
60
|
+
update_number_flags(@x - value)
|
|
64
61
|
end
|
|
65
62
|
|
|
66
63
|
# Compare memory with Y register.
|
|
@@ -70,9 +67,8 @@ module Badline
|
|
|
70
67
|
# $C4 - zeropage - 3 cycles
|
|
71
68
|
# $CC - absolute - 4 cycles
|
|
72
69
|
def cpy(_addr, value)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
update_number_flags(@y - v)
|
|
70
|
+
status.carry = @y >= value
|
|
71
|
+
update_number_flags(@y - value)
|
|
76
72
|
end
|
|
77
73
|
|
|
78
74
|
# Subtract memory from accumulator with borrow.
|
|
@@ -87,20 +83,19 @@ module Badline
|
|
|
87
83
|
# $F9 - absolute_y - 4+ cycles
|
|
88
84
|
# $FD - absolute_x - 4+ cycles
|
|
89
85
|
def sbc(_addr, value)
|
|
90
|
-
|
|
91
|
-
v_inv = ~v & 0xff
|
|
86
|
+
inverted = ~value & 0xff
|
|
92
87
|
carry = status.carry? ? 0 : -1
|
|
93
88
|
|
|
94
|
-
result = a +
|
|
89
|
+
result = a + inverted + status.carry
|
|
95
90
|
status.zero = result.nobits?(0xff)
|
|
96
91
|
status.carry = result > 0xff
|
|
97
|
-
update_calculation_flags(
|
|
92
|
+
update_calculation_flags(inverted, result)
|
|
98
93
|
|
|
99
94
|
if status.decimal?
|
|
100
|
-
al = (a & 0x0f) - (
|
|
95
|
+
al = (a & 0x0f) - (value & 0x0f) + carry
|
|
101
96
|
al = ((al - 0x06) & 0x0F) - 0x10 if al.negative?
|
|
102
97
|
|
|
103
|
-
result = (a & 0xf0) - (
|
|
98
|
+
result = (a & 0xf0) - (value & 0xf0) + al
|
|
104
99
|
result -= 0x60 if result.negative?
|
|
105
100
|
end
|
|
106
101
|
|
|
@@ -15,7 +15,7 @@ module Badline
|
|
|
15
15
|
# $39 - absolute_y - 4+ cycles
|
|
16
16
|
# $3D - absolute_x - 4+ cycles
|
|
17
17
|
def and(_addr, value)
|
|
18
|
-
@a &=
|
|
18
|
+
@a &= value
|
|
19
19
|
update_number_flags(@a)
|
|
20
20
|
end
|
|
21
21
|
|
|
@@ -28,10 +28,9 @@ module Badline
|
|
|
28
28
|
# $16 - zeropage_x - 6 cycles
|
|
29
29
|
# $1E - absolute_x - 7 cycles
|
|
30
30
|
def asl(addr, value)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
write_modified(addr, v, result)
|
|
31
|
+
result = (value << 1) & 0xff
|
|
32
|
+
status.carry = value[7]
|
|
33
|
+
write_modified(addr, result)
|
|
35
34
|
update_number_flags(result)
|
|
36
35
|
end
|
|
37
36
|
|
|
@@ -41,10 +40,9 @@ module Badline
|
|
|
41
40
|
# $24 - zeropage - 3 cycles
|
|
42
41
|
# $2C - absolute - 4 cycles
|
|
43
42
|
def bit(_addr, value)
|
|
44
|
-
v = resolve(value)
|
|
45
43
|
status.value = ((status.value & 0b00111111) +
|
|
46
|
-
(
|
|
47
|
-
status.zero = (a &
|
|
44
|
+
(value & 0b11000000)).to_i
|
|
45
|
+
status.zero = (a & value).nobits?(0xff)
|
|
48
46
|
end
|
|
49
47
|
|
|
50
48
|
# Performs bitwise XOR on accumulator with memory.
|
|
@@ -59,7 +57,7 @@ module Badline
|
|
|
59
57
|
# $59 - absolute_y - 4+ cycles
|
|
60
58
|
# $5D - absolute_x - 4+ cycles
|
|
61
59
|
def eor(_addr, value)
|
|
62
|
-
@a = (@a ^
|
|
60
|
+
@a = (@a ^ value) & 0xff
|
|
63
61
|
update_number_flags(@a)
|
|
64
62
|
end
|
|
65
63
|
|
|
@@ -72,10 +70,9 @@ module Badline
|
|
|
72
70
|
# $56 - zeropage_x - 6 cycles
|
|
73
71
|
# $5E - absolute_x - 7 cycles
|
|
74
72
|
def lsr(addr, value)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
write_modified(addr, v, result)
|
|
73
|
+
result = (value >> 1) & 0xff
|
|
74
|
+
status.carry = value[0]
|
|
75
|
+
write_modified(addr, result)
|
|
79
76
|
update_number_flags(result)
|
|
80
77
|
end
|
|
81
78
|
|
|
@@ -91,7 +88,7 @@ module Badline
|
|
|
91
88
|
# $19 - absolute_y - 4+ cycles
|
|
92
89
|
# $1D - absolute_x - 4+ cycles
|
|
93
90
|
def ora(_addr, value)
|
|
94
|
-
@a |=
|
|
91
|
+
@a |= value
|
|
95
92
|
update_number_flags(@a)
|
|
96
93
|
end
|
|
97
94
|
|
|
@@ -104,10 +101,9 @@ module Badline
|
|
|
104
101
|
# $36 - zeropage_x - 6 cycles
|
|
105
102
|
# $3E - absolute_x - 7 cycles
|
|
106
103
|
def rol(addr, value)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
write_modified(addr, v, result)
|
|
104
|
+
result = ((value << 1) + status.carry) & 0xff
|
|
105
|
+
status.carry = value[7]
|
|
106
|
+
write_modified(addr, result)
|
|
111
107
|
update_number_flags(result)
|
|
112
108
|
end
|
|
113
109
|
|
|
@@ -120,10 +116,9 @@ module Badline
|
|
|
120
116
|
# $76 - zeropage_x - 6 cycles
|
|
121
117
|
# $7E - absolute_x - 7 cycles
|
|
122
118
|
def ror(addr, value)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
write_modified(addr, v, result)
|
|
119
|
+
result = ((value >> 1) + (status.carry? ? 0x80 : 0)) & 0xff
|
|
120
|
+
status.carry = value[0]
|
|
121
|
+
write_modified(addr, result)
|
|
127
122
|
update_number_flags(result)
|
|
128
123
|
end
|
|
129
124
|
end
|
|
@@ -7,71 +7,72 @@ module Badline
|
|
|
7
7
|
#
|
|
8
8
|
# Opcodes:
|
|
9
9
|
# $90 - relative - 2+ cycles
|
|
10
|
-
def bcc(
|
|
11
|
-
|
|
10
|
+
def bcc(_addr, _value)
|
|
11
|
+
take_branch unless status.carry?
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
# Branch if carry set (C=1).
|
|
15
15
|
#
|
|
16
16
|
# Opcodes:
|
|
17
17
|
# $B0 - relative - 2+ cycles
|
|
18
|
-
def bcs(
|
|
19
|
-
|
|
18
|
+
def bcs(_addr, _value)
|
|
19
|
+
take_branch if status.carry?
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
# Branch if equal (Z=1).
|
|
23
23
|
#
|
|
24
24
|
# Opcodes:
|
|
25
25
|
# $F0 - relative - 2+ cycles
|
|
26
|
-
def beq(
|
|
27
|
-
|
|
26
|
+
def beq(_addr, _value)
|
|
27
|
+
take_branch if status.zero?
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
# Branch if minus (N=1).
|
|
31
31
|
#
|
|
32
32
|
# Opcodes:
|
|
33
33
|
# $30 - relative - 2+ cycles
|
|
34
|
-
def bmi(
|
|
35
|
-
|
|
34
|
+
def bmi(_addr, _value)
|
|
35
|
+
take_branch if status.negative?
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
# Branch if not equal (Z=0).
|
|
39
39
|
#
|
|
40
40
|
# Opcodes:
|
|
41
41
|
# $D0 - relative - 2+ cycles
|
|
42
|
-
def bne(
|
|
43
|
-
|
|
42
|
+
def bne(_addr, _value)
|
|
43
|
+
take_branch unless status.zero?
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
# Branch if plus (N=0).
|
|
47
47
|
#
|
|
48
48
|
# Opcodes:
|
|
49
49
|
# $10 - relative - 2+ cycles
|
|
50
|
-
def bpl(
|
|
51
|
-
|
|
50
|
+
def bpl(_addr, _value)
|
|
51
|
+
take_branch unless status.negative?
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
# Branch if overflow clear (V=0).
|
|
55
55
|
#
|
|
56
56
|
# Opcodes:
|
|
57
57
|
# $50 - relative - 2+ cycles
|
|
58
|
-
def bvc(
|
|
59
|
-
|
|
58
|
+
def bvc(_addr, _value)
|
|
59
|
+
take_branch unless status.overflow?
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
# Branch if overflow set (V=1).
|
|
63
63
|
#
|
|
64
64
|
# Opcodes:
|
|
65
65
|
# $70 - relative - 2+ cycles
|
|
66
|
-
def bvs(
|
|
67
|
-
|
|
66
|
+
def bvs(_addr, _value)
|
|
67
|
+
take_branch if status.overflow?
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
private
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
# Marks the branch as taken. Operations#op_relative reads the flag
|
|
73
|
+
# and runs the extra cycles.
|
|
74
|
+
def take_branch
|
|
75
|
+
@branch_taken = true
|
|
75
76
|
end
|
|
76
77
|
end
|
|
77
78
|
end
|
|
@@ -8,7 +8,7 @@ module Badline
|
|
|
8
8
|
# Opcodes:
|
|
9
9
|
# $18 - implied - 2 cycles
|
|
10
10
|
def clc(_addr, _value)
|
|
11
|
-
|
|
11
|
+
status.carry = false
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
# Clear decimal mode flag.
|
|
@@ -16,7 +16,7 @@ module Badline
|
|
|
16
16
|
# Opcodes:
|
|
17
17
|
# $D8 - implied - 2 cycles
|
|
18
18
|
def cld(_addr, _value)
|
|
19
|
-
|
|
19
|
+
status.decimal = false
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
# Clear interrupt disable flag.
|
|
@@ -24,7 +24,7 @@ module Badline
|
|
|
24
24
|
# Opcodes:
|
|
25
25
|
# $58 - implied - 2 cycles
|
|
26
26
|
def cli(_addr, _value)
|
|
27
|
-
|
|
27
|
+
status.interrupt = false
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
# Clear overflow flag.
|
|
@@ -32,7 +32,7 @@ module Badline
|
|
|
32
32
|
# Opcodes:
|
|
33
33
|
# $B8 - implied - 2 cycles
|
|
34
34
|
def clv(_addr, _value)
|
|
35
|
-
|
|
35
|
+
status.overflow = false
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
# Set carry flag.
|
|
@@ -40,7 +40,7 @@ module Badline
|
|
|
40
40
|
# Opcodes:
|
|
41
41
|
# $38 - implied - 2 cycles
|
|
42
42
|
def sec(_addr, _value)
|
|
43
|
-
|
|
43
|
+
status.carry = true
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
# Set decimal mode flag.
|
|
@@ -48,7 +48,7 @@ module Badline
|
|
|
48
48
|
# Opcodes:
|
|
49
49
|
# $F8 - implied - 2 cycles
|
|
50
50
|
def sed(_addr, _value)
|
|
51
|
-
|
|
51
|
+
status.decimal = true
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
# Set interrupt disable flag.
|
|
@@ -56,7 +56,7 @@ module Badline
|
|
|
56
56
|
# Opcodes:
|
|
57
57
|
# $78 - implied - 2 cycles
|
|
58
58
|
def sei(_addr, _value)
|
|
59
|
-
|
|
59
|
+
status.interrupt = true
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
62
|
end
|
|
@@ -9,7 +9,7 @@ module Badline
|
|
|
9
9
|
# Opcodes:
|
|
10
10
|
# $4B - immediate - 2 cycles
|
|
11
11
|
def alr(_addr, value)
|
|
12
|
-
@a &=
|
|
12
|
+
@a &= value
|
|
13
13
|
status.carry = @a[0]
|
|
14
14
|
@a = (@a >> 1) & 0xff
|
|
15
15
|
update_number_flags(@a)
|
|
@@ -25,13 +25,14 @@ module Badline
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
# Unstable instruction. AND's value with A|magic_const and X.
|
|
28
|
-
# Magic constant varies by CPU.
|
|
28
|
+
# Magic constant varies by CPU. A VIC stall between the opcode and
|
|
29
|
+
# operand fetches clears its bits 0 and 4.
|
|
29
30
|
#
|
|
30
31
|
# Opcodes:
|
|
31
32
|
# $8B - immediate - 2 cycles
|
|
32
33
|
def ane(_addr, value)
|
|
33
|
-
magic_const = 0xee
|
|
34
|
-
@a = (a | magic_const) & x &
|
|
34
|
+
magic_const = stalled_before_this_cycle? ? @ane_constant & 0xee : @ane_constant
|
|
35
|
+
@a = (a | magic_const) & x & value
|
|
35
36
|
update_number_flags(@a)
|
|
36
37
|
end
|
|
37
38
|
|
|
@@ -40,7 +41,7 @@ module Badline
|
|
|
40
41
|
# Opcodes:
|
|
41
42
|
# $6B - immediate - 2 cycles
|
|
42
43
|
def arr(_addr, value)
|
|
43
|
-
tmp = a &
|
|
44
|
+
tmp = a & value
|
|
44
45
|
result = (tmp >> 1) | (status.carry? ? 0x80 : 0)
|
|
45
46
|
status.zero = result.zero?
|
|
46
47
|
status.negative = status.carry
|
|
@@ -94,23 +95,12 @@ module Badline
|
|
|
94
95
|
sbc(addr, inc(addr, value))
|
|
95
96
|
end
|
|
96
97
|
|
|
97
|
-
# Freezes the CPU by forcing an infinite loop.
|
|
98
|
-
#
|
|
99
|
-
# Opcodes:
|
|
100
|
-
# $02, $12, $22, $32, $42, $52, $62, $72, $92, $B2, $D2, $F2
|
|
101
|
-
def jam(_addr, _value)
|
|
102
|
-
# TODO: Handle jam
|
|
103
|
-
# It is possible to implement with loop { cycle }, but makes
|
|
104
|
-
# testing problematic.
|
|
105
|
-
10.times { cycle }
|
|
106
|
-
end
|
|
107
|
-
|
|
108
98
|
# Loads A and S with value AND stack pointer.
|
|
109
99
|
#
|
|
110
100
|
# Opcodes:
|
|
111
101
|
# $BB - absolute_y - 4 cycles
|
|
112
102
|
def las(_addr, value)
|
|
113
|
-
@a = @x = @stack_pointer =
|
|
103
|
+
@a = @x = @stack_pointer = value & stack_pointer
|
|
114
104
|
update_number_flags(@a)
|
|
115
105
|
end
|
|
116
106
|
|
|
@@ -124,7 +114,7 @@ module Badline
|
|
|
124
114
|
# $B7 - zeropage_y - 4 cycles
|
|
125
115
|
# $BF - absolute_y - 4+ cycles
|
|
126
116
|
def lax(_addr, value)
|
|
127
|
-
@a = @x =
|
|
117
|
+
@a = @x = value
|
|
128
118
|
|
|
129
119
|
update_number_flags(@a)
|
|
130
120
|
end
|
|
@@ -136,7 +126,7 @@ module Badline
|
|
|
136
126
|
# $AB - immediate - 2 cycles
|
|
137
127
|
def lxa(_addr, value)
|
|
138
128
|
magic_const = 0xee
|
|
139
|
-
@a = @x = (a | magic_const) &
|
|
129
|
+
@a = @x = (a | magic_const) & value
|
|
140
130
|
update_number_flags(@a)
|
|
141
131
|
end
|
|
142
132
|
|
|
@@ -191,8 +181,7 @@ module Badline
|
|
|
191
181
|
# Opcodes:
|
|
192
182
|
# $CB - immediate - 2 cycles
|
|
193
183
|
def sbx(_addr, value)
|
|
194
|
-
|
|
195
|
-
result = (@x & @a) - v
|
|
184
|
+
result = (@x & @a) - value
|
|
196
185
|
status.carry = result >= 0
|
|
197
186
|
@x = result & 0xff
|
|
198
187
|
update_number_flags(@x)
|
|
@@ -263,6 +252,9 @@ module Badline
|
|
|
263
252
|
|
|
264
253
|
private
|
|
265
254
|
|
|
255
|
+
# A VIC stall right before the dummy read, the second-to-last cycle,
|
|
256
|
+
# drops the & (H+1) from the stored value but not from the high byte
|
|
257
|
+
# of a page-crossing target.
|
|
266
258
|
def store_high_and(register, addr)
|
|
267
259
|
base_high = if boundary_crossed
|
|
268
260
|
(high_byte(addr) - 1) & 0xff
|
|
@@ -271,7 +263,7 @@ module Badline
|
|
|
271
263
|
end
|
|
272
264
|
result = register & ((base_high + 1) & 0xff)
|
|
273
265
|
target = boundary_crossed ? uint16(low_byte(addr), result) : addr
|
|
274
|
-
write_byte(target, result)
|
|
266
|
+
write_byte(target, stalled_before_previous_cycle? ? register : result)
|
|
275
267
|
end
|
|
276
268
|
end
|
|
277
269
|
end
|