badline 0.1.0 → 0.2.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 +4 -4
- data/CHANGELOG.md +167 -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 +88 -23
- 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 +131 -15
- data/lib/badline/cia/interrupt_register.rb +88 -0
- data/lib/badline/cia/serial.rb +219 -0
- data/lib/badline/cia/timer.rb +43 -31
- data/lib/badline/cia.rb +109 -74
- data/lib/badline/color_memory.rb +14 -5
- data/lib/badline/computer.rb +68 -8
- data/lib/badline/control_ports.rb +54 -9
- data/lib/badline/cpu/addressing.rb +131 -0
- data/lib/badline/cpu/microcode.rb +171 -0
- data/lib/badline/cpu/operations.rb +88 -0
- data/lib/badline/cpu/stack_operations.rb +82 -0
- data/lib/badline/cpu.rb +108 -167
- 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 +33 -0
- data/lib/badline/kernal_trap/drive.rb +239 -0
- data/lib/badline/kernal_trap/file.rb +4 -24
- data/lib/badline/kernal_trap/load.rb +2 -2
- data/lib/badline/kernal_trap/routine.rb +34 -0
- data/lib/badline/kernal_trap/save.rb +1 -1
- data/lib/badline/kernal_trap/serial.rb +139 -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/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 +32 -27
- data/lib/badline/storage/crt_file.rb +18 -2
- data/lib/badline/storage/disk_image.rb +52 -5
- data/lib/badline/storage/host_directory.rb +18 -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 +373 -49
- data/lib/badline.rb +5 -0
- data/vendor/.gitkeep +0 -0
- metadata +103 -4
|
@@ -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
|
|
@@ -11,9 +11,8 @@ module Badline
|
|
|
11
11
|
# $D6 - zeropage_x - 6 cycles
|
|
12
12
|
# $DE - absolute_x - 7 cycles
|
|
13
13
|
def dec(addr, value)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
write_modified(addr, v, result)
|
|
14
|
+
result = (value - 1) & 0xff
|
|
15
|
+
write_modified(addr, result)
|
|
17
16
|
update_number_flags(result)
|
|
18
17
|
end
|
|
19
18
|
|
|
@@ -22,7 +21,7 @@ module Badline
|
|
|
22
21
|
# Opcodes:
|
|
23
22
|
# $CA - implied - 2 cycles
|
|
24
23
|
def dex(_addr, _value)
|
|
25
|
-
|
|
24
|
+
@x = (@x - 1) & 0xff
|
|
26
25
|
update_number_flags(@x)
|
|
27
26
|
end
|
|
28
27
|
|
|
@@ -31,7 +30,7 @@ module Badline
|
|
|
31
30
|
# Opcodes:
|
|
32
31
|
# $88 - implied - 2 cycles
|
|
33
32
|
def dey(_addr, _value)
|
|
34
|
-
|
|
33
|
+
@y = (@y - 1) & 0xff
|
|
35
34
|
update_number_flags(@y)
|
|
36
35
|
end
|
|
37
36
|
|
|
@@ -43,9 +42,8 @@ module Badline
|
|
|
43
42
|
# $F6 - zeropage_x - 6 cycles
|
|
44
43
|
# $FE - absolute_x - 7 cycles
|
|
45
44
|
def inc(addr, value)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
write_modified(addr, v, result)
|
|
45
|
+
result = (value + 1) & 0xff
|
|
46
|
+
write_modified(addr, result)
|
|
49
47
|
update_number_flags(result)
|
|
50
48
|
end
|
|
51
49
|
|
|
@@ -54,7 +52,7 @@ module Badline
|
|
|
54
52
|
# Opcodes:
|
|
55
53
|
# $E8 - implied - 2 cycles
|
|
56
54
|
def inx(_addr, _value)
|
|
57
|
-
|
|
55
|
+
@x = (@x + 1) & 0xff
|
|
58
56
|
update_number_flags(@x)
|
|
59
57
|
end
|
|
60
58
|
|
|
@@ -63,7 +61,7 @@ module Badline
|
|
|
63
61
|
# Opcodes:
|
|
64
62
|
# $C8 - implied - 2 cycles
|
|
65
63
|
def iny(_addr, _value)
|
|
66
|
-
|
|
64
|
+
@y = (@y + 1) & 0xff
|
|
67
65
|
update_number_flags(@y)
|
|
68
66
|
end
|
|
69
67
|
end
|
|
@@ -24,7 +24,7 @@ module Badline
|
|
|
24
24
|
# Opcodes:
|
|
25
25
|
# $68 - implied - 4 cycles
|
|
26
26
|
def pla(_addr, _value)
|
|
27
|
-
|
|
27
|
+
@a = stack_pull
|
|
28
28
|
update_number_flags(@a)
|
|
29
29
|
end
|
|
30
30
|
|
|
@@ -33,71 +33,19 @@ module Badline
|
|
|
33
33
|
# Opcodes:
|
|
34
34
|
# $28 - implied - 4 cycles
|
|
35
35
|
def plp(_addr, _value)
|
|
36
|
-
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# Jump to absolute address.
|
|
40
|
-
#
|
|
41
|
-
# Opcodes:
|
|
42
|
-
# $4C - absolute - 3 cycles
|
|
43
|
-
def jmp(addr, _value)
|
|
44
|
-
@program_counter = addr
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Jump to subroutine. The operand high byte is fetched only after the
|
|
48
|
-
# return address has been pushed, so a JSR executing inside the stack
|
|
49
|
-
# jumps via the value its own push just wrote.
|
|
50
|
-
#
|
|
51
|
-
# Opcodes:
|
|
52
|
-
# $20 - absolute - 6 cycles
|
|
53
|
-
def jsr(addr, _value)
|
|
54
|
-
return_addr = (program_counter - 1) & 0xffff
|
|
55
|
-
cycle { @memory.peek(stack_address) } # internal cycle: dummy stack read
|
|
56
|
-
write_byte(stack_address, high_byte(return_addr))
|
|
57
|
-
write_byte(stack_address(-1), low_byte(return_addr))
|
|
58
|
-
@stack_pointer = (@stack_pointer - 2) & 0xff
|
|
59
|
-
@program_counter = uint16(addr, read_byte(return_addr))
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Return from interrupt.
|
|
63
|
-
#
|
|
64
|
-
# Opcodes:
|
|
65
|
-
# $40 - implied - 6 cycles
|
|
66
|
-
def rti(_addr, _value)
|
|
67
|
-
cycle do
|
|
68
|
-
@stack_pointer = (@stack_pointer + 1) & 0xff
|
|
69
|
-
@status.value = memory[stack_address]
|
|
70
|
-
@status.break = false
|
|
71
|
-
end
|
|
72
|
-
@program_counter = uint16(stack_pull, stack_pull)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# Return from subroutine.
|
|
76
|
-
#
|
|
77
|
-
# Opcodes:
|
|
78
|
-
# $60 - implied - 6 cycles
|
|
79
|
-
def rts(_addr, _value)
|
|
80
|
-
cycle do
|
|
81
|
-
@program_counter = (uint16(stack_pull, stack_pull) + 1) & 0xffff
|
|
82
|
-
end
|
|
36
|
+
status.value = stack_pull & 0b11101111
|
|
83
37
|
end
|
|
84
38
|
|
|
85
39
|
private
|
|
86
40
|
|
|
87
41
|
def stack_pull
|
|
88
|
-
|
|
89
|
-
|
|
42
|
+
@stack_pointer = (@stack_pointer + 1) & 0xff
|
|
43
|
+
@memory.peek(stack_address)
|
|
90
44
|
end
|
|
91
45
|
|
|
92
46
|
def stack_push(value)
|
|
93
47
|
write_byte(stack_address, value)
|
|
94
|
-
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def stack_push16(value)
|
|
98
|
-
write_byte(stack_address, high_byte(value))
|
|
99
|
-
write_byte(stack_address(-1), low_byte(value))
|
|
100
|
-
cycle { @stack_pointer = (@stack_pointer - 2) & 0xff }
|
|
48
|
+
@stack_pointer = (@stack_pointer - 1) & 0xff
|
|
101
49
|
end
|
|
102
50
|
end
|
|
103
51
|
end
|
|
@@ -16,7 +16,7 @@ module Badline
|
|
|
16
16
|
# $B9 - absolute_y - 4+ cycles
|
|
17
17
|
# $BD - absolute_x - 4+ cycles
|
|
18
18
|
def lda(_addr, value)
|
|
19
|
-
@a =
|
|
19
|
+
@a = value
|
|
20
20
|
update_number_flags(@a)
|
|
21
21
|
end
|
|
22
22
|
|
|
@@ -29,7 +29,7 @@ module Badline
|
|
|
29
29
|
# $B6 - zeropage_y - 4 cycles
|
|
30
30
|
# $BE - absolute_y - 4+ cycles
|
|
31
31
|
def ldx(_addr, value)
|
|
32
|
-
@x =
|
|
32
|
+
@x = value
|
|
33
33
|
update_number_flags(@x)
|
|
34
34
|
end
|
|
35
35
|
|
|
@@ -42,7 +42,7 @@ module Badline
|
|
|
42
42
|
# $B4 - zeropage_x - 4 cycles
|
|
43
43
|
# $BC - absolute_x - 4+ cycles
|
|
44
44
|
def ldy(_addr, value)
|
|
45
|
-
@y =
|
|
45
|
+
@y = value
|
|
46
46
|
update_number_flags(@y)
|
|
47
47
|
end
|
|
48
48
|
|
|
@@ -85,7 +85,7 @@ module Badline
|
|
|
85
85
|
# Opcodes:
|
|
86
86
|
# $AA - implied - 2 cycles
|
|
87
87
|
def tax(_addr, _value)
|
|
88
|
-
|
|
88
|
+
@x = a
|
|
89
89
|
update_number_flags(@x)
|
|
90
90
|
end
|
|
91
91
|
|
|
@@ -94,7 +94,7 @@ module Badline
|
|
|
94
94
|
# Opcodes:
|
|
95
95
|
# $A8 - implied - 2 cycles
|
|
96
96
|
def tay(_addr, _value)
|
|
97
|
-
|
|
97
|
+
@y = a
|
|
98
98
|
update_number_flags(@y)
|
|
99
99
|
end
|
|
100
100
|
|
|
@@ -103,7 +103,7 @@ module Badline
|
|
|
103
103
|
# Opcodes:
|
|
104
104
|
# $BA - implied - 2 cycles
|
|
105
105
|
def tsx(_addr, _value)
|
|
106
|
-
|
|
106
|
+
@x = stack_pointer
|
|
107
107
|
update_number_flags(@x)
|
|
108
108
|
end
|
|
109
109
|
|
|
@@ -112,7 +112,7 @@ module Badline
|
|
|
112
112
|
# Opcodes:
|
|
113
113
|
# $8A - implied - 2 cycles
|
|
114
114
|
def txa(_addr, _operand)
|
|
115
|
-
|
|
115
|
+
@a = x
|
|
116
116
|
update_number_flags(@a)
|
|
117
117
|
end
|
|
118
118
|
|
|
@@ -121,7 +121,7 @@ module Badline
|
|
|
121
121
|
# Opcodes:
|
|
122
122
|
# $9A - implied - 2 cycles
|
|
123
123
|
def txs(_addr, _operand)
|
|
124
|
-
|
|
124
|
+
@stack_pointer = x
|
|
125
125
|
end
|
|
126
126
|
|
|
127
127
|
# Transfer Y register to accumulator.
|
|
@@ -129,7 +129,7 @@ module Badline
|
|
|
129
129
|
# Opcodes:
|
|
130
130
|
# $98 - implied - 2 cycles
|
|
131
131
|
def tya(_addr, _operand)
|
|
132
|
-
|
|
132
|
+
@a = y
|
|
133
133
|
update_number_flags(@a)
|
|
134
134
|
end
|
|
135
135
|
end
|
|
@@ -22,18 +22,8 @@ module Badline
|
|
|
22
22
|
include InstructionSet::Stack
|
|
23
23
|
include InstructionSet::Transfer
|
|
24
24
|
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
# Opcodes:
|
|
29
|
-
# $00 - implied - 7 cycles
|
|
30
|
-
def brk(_addr, _value)
|
|
31
|
-
status.break = true
|
|
32
|
-
handle_interrupt(0xfffe, brk: true, pre_cycles: 1)
|
|
33
|
-
status.break = false
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# No operation. Does nothing but consume a clock cycle.
|
|
25
|
+
# No operation. The illegal forms with an operand still read it, in
|
|
26
|
+
# the steps before this is called.
|
|
37
27
|
#
|
|
38
28
|
# Opcodes:
|
|
39
29
|
# $EA - implied - 2 cycles
|
|
@@ -42,35 +32,23 @@ module Badline
|
|
|
42
32
|
# $0C - absolute - 4 cycles (illegal)
|
|
43
33
|
# $1C, $3C, $5C, $7C, $DC, $FC - absolute_x - 4+ cycles (illegal)
|
|
44
34
|
# $1A, $3A, $5A, $7A, $DA, $FA - implied - 2 cycles (illegal)
|
|
45
|
-
def nop(_addr, _value)
|
|
46
|
-
cycle
|
|
47
|
-
end
|
|
35
|
+
def nop(_addr, _value); end
|
|
48
36
|
|
|
49
37
|
private
|
|
50
38
|
|
|
51
|
-
def resolve(value)
|
|
52
|
-
if value == :lazy
|
|
53
|
-
realize_value(@instruction, @operand, @address)
|
|
54
|
-
elsif value.is_a?(Proc)
|
|
55
|
-
value.call
|
|
56
|
-
else
|
|
57
|
-
value
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
39
|
def update_number_flags(value)
|
|
62
40
|
status.zero = value.zero?
|
|
63
|
-
status.negative = value
|
|
41
|
+
status.negative = value & 0x80 != 0
|
|
64
42
|
value
|
|
65
43
|
end
|
|
66
44
|
|
|
67
|
-
#
|
|
68
|
-
|
|
45
|
+
# Stores a read-modify-write result. For memory, the write happens in
|
|
46
|
+
# the steps that follow (see Operations#op_rmw_read).
|
|
47
|
+
def write_modified(addr, result)
|
|
69
48
|
if addr == :accumulator
|
|
70
|
-
|
|
49
|
+
@a = result
|
|
71
50
|
else
|
|
72
|
-
|
|
73
|
-
write_byte(addr, result)
|
|
51
|
+
@rmw_result = result
|
|
74
52
|
end
|
|
75
53
|
end
|
|
76
54
|
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Interrupts
|
|
5
|
+
BREAK_MASK = 0x10
|
|
6
|
+
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
# Samples the IRQ line and the NMI latch once per cycle. An instruction
|
|
10
|
+
# boundary commits the sample taken on its second-to-last cycle.
|
|
11
|
+
def poll
|
|
12
|
+
if @skip_poll
|
|
13
|
+
@skip_poll = false
|
|
14
|
+
return
|
|
15
|
+
end
|
|
16
|
+
@irq_pending = @irq_sample
|
|
17
|
+
@nmi_pending = @nmi_sample
|
|
18
|
+
@irq_sample = @irq && !@status.interrupt?
|
|
19
|
+
@nmi_sample = @nmi
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# A cycle the VIC stalls through BA keeps sampling the lines, OR-ed with
|
|
23
|
+
# the sample it already holds, but the boundary pipeline doesn't advance
|
|
24
|
+
# and a skipped poll stays owed to the next real cycle.
|
|
25
|
+
def sample_while_stalled
|
|
26
|
+
@irq_sample ||= @irq && !interrupt_disabled_after_stall?
|
|
27
|
+
@nmi_sample = true if @nmi
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# A stalled CLI or SEI execute cycle masks with the I it is about to
|
|
31
|
+
# set. Every other step masks with the current I, PLP included: its
|
|
32
|
+
# pulled I only arrives with the stalled stack read.
|
|
33
|
+
def interrupt_disabled_after_stall?
|
|
34
|
+
return @status.interrupt? unless @plan.equal?(CPU::IMPLIED_PLAN)
|
|
35
|
+
|
|
36
|
+
case @operation
|
|
37
|
+
when :cli then false
|
|
38
|
+
when :sei then true
|
|
39
|
+
else @status.interrupt?
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Runs in the opcode fetch cycle in place of the fetch, with no bus
|
|
44
|
+
# access, and switches to the interrupt plan.
|
|
45
|
+
def start_interrupt
|
|
46
|
+
@interrupt = @boundary_nmi ? 0xfffa : 0xfffe
|
|
47
|
+
@brk = false
|
|
48
|
+
@plan = CPU::INTERRUPT_PLAN
|
|
49
|
+
@writes = CPU::INTERRUPT_WRITES
|
|
50
|
+
@index = 1
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# BRK reads the byte after its opcode and ignores it, and selects the
|
|
54
|
+
# IRQ vector.
|
|
55
|
+
def op_brk_dummy
|
|
56
|
+
@memory.peek(@program_counter)
|
|
57
|
+
@interrupt = 0xfffe
|
|
58
|
+
@brk = true
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def op_int_push_pch
|
|
62
|
+
@address = @brk ? (@program_counter + 1) & 0xffff : @program_counter
|
|
63
|
+
@memory.poke(stack_address, high_byte(@address))
|
|
64
|
+
@stack_pointer = (@stack_pointer - 1) & 0xff
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def op_int_push_pcl
|
|
68
|
+
@memory.poke(stack_address, low_byte(@address))
|
|
69
|
+
@stack_pointer = (@stack_pointer - 1) & 0xff
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def op_int_push_status
|
|
73
|
+
value = @brk ? @status.value | BREAK_MASK : @status.value & ~BREAK_MASK
|
|
74
|
+
@memory.poke(stack_address, value)
|
|
75
|
+
@stack_pointer = (@stack_pointer - 1) & 0xff
|
|
76
|
+
@status.interrupt = true
|
|
77
|
+
# An NMI that arrives before cycle 4 of a BRK or IRQ takes it over:
|
|
78
|
+
# the sequence finishes with the NMI vector.
|
|
79
|
+
@interrupt = 0xfffa if @interrupt == 0xfffe && @nmi_pending
|
|
80
|
+
@nmi = false if @interrupt == 0xfffa
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def op_int_vector_low
|
|
84
|
+
@address = @memory.peek(@interrupt)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Clears any interrupt that is pending, so the handler's first
|
|
88
|
+
# instruction always runs before the next interrupt. This applies to
|
|
89
|
+
# BRK too.
|
|
90
|
+
def op_int_vector_high
|
|
91
|
+
high = @memory.peek((@interrupt + 1) & 0xffff)
|
|
92
|
+
@program_counter = @address | (high << 8)
|
|
93
|
+
@irq_sample = @irq_pending = false
|
|
94
|
+
@nmi_sample = @nmi_pending = false
|
|
95
|
+
@interrupt = nil
|
|
96
|
+
if @brk
|
|
97
|
+
@status.break = false
|
|
98
|
+
end_instruction
|
|
99
|
+
else
|
|
100
|
+
end_sequence
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
data/lib/badline/joystick.rb
CHANGED
|
@@ -4,22 +4,22 @@ module Badline
|
|
|
4
4
|
class Joystick
|
|
5
5
|
DIRECTIONS = { up: 0, down: 1, left: 2, right: 3, fire: 4 }.freeze
|
|
6
6
|
|
|
7
|
+
# Kept up to date as switches move, since the CIA samples the fire line
|
|
8
|
+
# every cycle to drive the light pen input.
|
|
9
|
+
attr_reader :port_bits
|
|
10
|
+
|
|
7
11
|
def initialize
|
|
8
|
-
@
|
|
12
|
+
@port_bits = 0xff
|
|
9
13
|
end
|
|
10
14
|
|
|
11
15
|
def press(direction)
|
|
12
|
-
|
|
16
|
+
bit = DIRECTIONS[direction]
|
|
17
|
+
@port_bits &= ~(1 << bit) if bit
|
|
13
18
|
end
|
|
14
19
|
|
|
15
20
|
def release(direction)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def port_bits
|
|
20
|
-
bits = 0xff
|
|
21
|
-
DIRECTIONS.each { |dir, bit| bits &= ~(1 << bit) if @pressed[dir] }
|
|
22
|
-
bits
|
|
21
|
+
bit = DIRECTIONS[direction]
|
|
22
|
+
@port_bits |= 1 << bit if bit
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
end
|