ruby2600 - an atari 2600 emulator written in ruby
DESCRIPTION
The emulator was presented to the public at RubyConfBr 2013. Its source code can be downloaded at http://github.com/chesterbr/ruby2600 The video is on YouTube: http://www.youtube.com/watch?v=S3qAOu41CxETRANSCRIPT
![Page 1: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/1.jpg)
An Atari 2600 emulator100% written in Ruby
(and RSpec!)
Carlos Duarte do Nascimento (Chester)@chesterbr / http://chester.me
![Page 2: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/2.jpg)
![Page 3: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/3.jpg)
If so, you probably had this...
![Page 4: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/4.jpg)
...or one of these...
![Page 6: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/6.jpg)
Emulator
A program that runs software written for one type of computer system in
another type by simulating the hardware of the original system
![Page 7: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/7.jpg)
ruby2600
● Atari 2600 emulator● Written in Ruby● Runs quite a few classic games● Open-source
http://github.com/chesterbr/ruby2600
![Page 8: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/8.jpg)
Why?
There are great emulators out there, but they strive for speed above readability
A test-driven emulator in a high-level language is a great learning tool
Always wondered how much TDD would help on wildly unfamiliar territory
(also: why not? ☺)
![Page 9: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/9.jpg)
Work in progress!
● A few subtle bugs● Does not run every game● Not full-speed● No sound
http://github.com/chesterbr/ruby2600
![Page 10: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/10.jpg)
We'll see
● How the Atari works● CPU emulation● Architecture (just a bit) ● Ruby2600 in action● The future
![Page 11: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/11.jpg)
@chesterbrhttp://chester.me
About me (Chester)
© Ila Fox - http://www.ilafox.com
![Page 12: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/12.jpg)
Building an Emulator:
How the Atari 2600 works
![Page 13: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/13.jpg)
Let's peek inside...(Atari 2600 Jr. printed circuit board)
![Page 14: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/14.jpg)
Cartridge connector
![Page 15: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/15.jpg)
CPU: 6507
![Page 16: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/16.jpg)
Video: TIA
![Page 17: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/17.jpg)
Everything else: RIOT (6532)
![Page 18: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/18.jpg)
Challenging specs● CPU speed: 1.19 MHz (not GHz)● Max cart (program) size: 4 KB● RAM: 128 bytes● Video RAM: 0KB (game code has to drive TIA into generating each scanline in realtime)
![Page 19: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/19.jpg)
Atari 2600 in a nutshell
The CPU reads a game program from the ROM chip on the cartrigde
It confgures the pixels generated by TIA, using RAM, I/O and timers
provided by the RIOT chip
Our goal: simulate this in software
![Page 20: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/20.jpg)
Building an Emulator:
CPU
image CC-BY Konstantin Lanzet
![Page 21: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/21.jpg)
65xx family: in the past...
http://en.wikipedia.org/wiki/MOS_Technology_6502#Computers_and_games
![Page 22: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/22.jpg)
...and in the future!
http://en.wikipedia.org/wiki/MOS_Technology_6502#In_popular_culture
![Page 23: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/23.jpg)
The 6507 CPU
Reads instructions from the cartridge that manipulate and transfer bytes between chips, keeping state on
internal registers and flags
http://en.wikipedia.org/wiki/Von_Neumann_architecture
![Page 24: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/24.jpg)
Emulated 6507
As it executes each instruction, it keeps instance variables for registers
(@a, @x, @y), flags (@n, @z, ...) and @memory as an array
![Page 25: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/25.jpg)
CPU.rb
module Ruby2600 class CPU attr_accessor :memory attr_accessor :pc, :a, :x, :y, :s
# Flags (P register): nv--dizc attr_accessor :n, :v, :d, :i, :z, :c
def step ...runs a instruction... end
...
![Page 26: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/26.jpg)
module Ruby2600 class CPU attr_accessor :memory attr_accessor :pc, :a, :x, :y, :s
# Flags (P register): nv--dizc attr_accessor :n, :v, :d, :i, :z, :c
def step ...runs a instruction... end
...
CPU.rb
TESTS FIRST!TESTS FIRST!
![Page 27: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/27.jpg)
![Page 28: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/28.jpg)
Assembly debugging == PAIN!
To avoid it, we need technical specifcations
that are easy to read, yet detail-oriented enough to
test emulator code
RSpec does the job!
http://rspec.info
![Page 29: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/29.jpg)
6507 Instruction Set, 1/2
![Page 30: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/30.jpg)
6507 Instruction Set, 2/2
![Page 31: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/31.jpg)
CPU_spec.rb context 'INX' do before do cpu.memory[0] = 0xE8 # INX cpu.pc = 0x0000 cpu.x = 0x07 end
it 'should advance PC by one' do cpu.step cpu.pc.should == 0x0001 end
it 'should set X value' do cpu.step cpu.x.should == 0x08 end ...
![Page 32: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/32.jpg)
Using shared examplesshared_examples_for 'advance PC by one' do it { expect { cpu.step }.to change { cpu.pc }.by 1 }end
shared_examples_for 'set X value' do |expected| it do cpu.step
value = cpu.x value.should be(expected), "Expected: #{hex_bye(expected)}, " + "found: #{hex_byte(value)}" endend
![Page 33: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/33.jpg)
More syntactic sugar1.upto 3 do |number| shared_examples_for "advance PC by #{number.humanize}" do it { expect { cpu.step }.to change { cpu.pc }.by number } endend
RSpec.configure do |c| c.alias_it_should_behave_like_to :it_should, 'should'end
![Page 34: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/34.jpg)
“Literate Testing”
context 'INX' do before do cpu.memory[0] = 0xE8 # INX cpu.x = 0x07 end
it_should 'advance PC by one' it_should 'take two cycles' it_should 'set X value', 0x08 it_should 'reset Z flag' it_should 'reset N flag' ...
http://en.wikipedia.org/wiki/Literate_programming
![Page 35: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/35.jpg)
Less effort → better coverage
● Each instruction tested in everypossible addressing mode
● Easy to understand, high-level tests● Tests become a specifcation - specs!
![Page 36: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/36.jpg)
CPU.rb
module Ruby2600 class CPU attr_accessor :memory attr_accessor :pc, :a, :x, :y, :s
# Flags (P register): nv--dizc attr_accessor :n, :v, :d, :i, :z, :c
def step ...runs a instruction... end
...
![Page 37: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/37.jpg)
![Page 38: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/38.jpg)
def step fetch decode execute
return @time_in_cycles end
CPU.rb
![Page 39: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/39.jpg)
No byte/worddata types :-( CPU.rb
def fetch @opcode = memory[@pc] @param_lo = memory[word(@pc + 1)] @param_hi = memory[word(@pc + 2)] @param = @param_hi * 0x100 + @param_lo @pc = word(@pc + OPCODE_SIZES[@opcode]) end
Lookup table
![Page 40: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/40.jpg)
morelookup!
CPU.rb def decode if (@opcode & 0b11111) == BXX @instruction = BXX elsif (@opcode & 0b11111) == SCX @instruction = SCX else @instruction_group = @opcode & 0b11 mode_in_group = (@opcode & 0b11100) >> 2 @addressing_mode = ADDRESSING[ @instruction_group][mode_in_group] @instruction = @opcode & 0b11100011 end
@time_in_cycles = time_in_cycles end
![Page 41: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/41.jpg)
CPU.rb
def execute case @instruction when LDA flag_nz @a = load when STA store @a when JMPabs @pc = @param when BXX @pc = branch ...
![Page 42: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/42.jpg)
Final result
Full instruction set coveredby more than 1,700 tests
Only one CPU bug so farhad to be debugged in-game
![Page 43: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/43.jpg)
Building an Emulator:Building an Emulator:
ArchitectureArchitecture
image CC-BY image CC-BY Benjamin EshanBenjamin Eshan
![Page 44: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/44.jpg)
ruby2600 class diagram
![Page 45: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/45.jpg)
Memory-based I/O
CPU “talks” to other chips by reading and writing specifc memory locations:
0000-002C – TIA (write)0030-003D – TIA (read)0080-00FF – RIOT (RAM)0280-0297 – RIOT (I/O,Timer)F000-FFFF – Cartridge (ROM)
(very simplified, see: http://nocash.emubase.de/2k6specs.htm)
![Page 46: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/46.jpg)
Bus
Acts as a memory façade to theCPU, routing reads and writesto the appropriate chip class
It is also the interface where we “plug” an UI (anything that displays
images and reads keypresses)
![Page 47: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/47.jpg)
ruby2600 class diagram
![Page 48: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/48.jpg)
Ruby spice: duck typing
Instead of defning read/write methods, make Bus, TIA, RIOT and
Cart classes “quack” like arrays
![Page 49: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/49.jpg)
TIA.rb / RIOT.rb / Cart.rb(and also bus.rb!)
def [](position) ...return value for position... end
def []=(position, value) ...react to writing value to position... end
![Page 50: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/50.jpg)
Benefts
● High decoupling: CPU, TIA, RIOT and Cart are (mostly) independent
● We can use regular arrays as mocks for TIA, RIOT, Cart and Bus itself!
● We can “plug” different UIs (e.g.: text-mode rendering, network multiplayer, joystick interface, etc.)
![Page 51: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/51.jpg)
Cart.rb (full source)class Cart def initialize(rom_file) @bytes = File.open(rom_file, "rb") { |f| f.read }.unpack('C*') @bytes += @bytes if @bytes.count == 2048 end
def [](address) @bytes[address] end
def []=(address, value) # Don't write to Read-Only Memory, duh! endend
![Page 52: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/52.jpg)
TimingAs TIA generates each pixel for each scanline, it will "tick" the CPU and RIOT to keep everything in sync
image cc-by Steve Evans
![Page 53: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/53.jpg)
TIA.rbdef draw_scanline scanline = [] 0.upto(SCANLINE_WIDTH) do |pixel| scanline << topmost_pixel tick_other_chips pixel end return scanlineend
def topmost_pixel ...
def tick_other_chips @cpu.tick if pixel % 3 == 2 @riot.tick if pixel % 3 == 0end
TIA runs 3x fasterthan CPU and RIOT
![Page 54: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/54.jpg)
BALL
PLAYFIELD
PLAYERS (2)
MISSILES (2)
Graphic Objects
![Page 55: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/55.jpg)
Graphic Objects
To keep TIA's responsibility focused on building the frames, we will offload
object drawing to separate classes (Playfeld, Player, Ball, Missile)
For the common behavior, should we use composition or inheritance?
![Page 56: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/56.jpg)
Composition and InheritanceA common ancestor (Graphic)
contains the common behavior, and each class adds its own flavor
Behavior that does not defne a Graphic (position counters) is better
suited for a separate class, using composition instead of inheritance
![Page 57: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/57.jpg)
ruby2600 class diagram
![Page 58: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/58.jpg)
Building an Emulator:
Let's run it!
reproduction: reproduction: Young FrankensteinYoung Frankenstein
![Page 59: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/59.jpg)
![Page 60: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/60.jpg)
Building an Emulator:
Speeding up
TBBT 1-06 "The Middle-Earth Paradigm", © 2007 Chuck Lorre Productions / WB Television
![Page 61: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/61.jpg)
Knuth, Donald (December 1974). "Structured Programmingwith go
to Statements", ACM Journal
Me and Prof. Knuth hanging out. We're, like, bros. Really.
"We should forget about small
efficiencies, say about 97% of the time: premature
optimization is the root of all evil"
![Page 62: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/62.jpg)
Refactoring
Thanks to test coverage, we can safely play around and refactor for speed
(while keeping it readable)
![Page 63: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/63.jpg)
JRuby
We have a small working set (no more than 5KB) handled in very tight loops
Have to measure, but it smells like the kind of modern-CPU-friendly code
that could be generated via JIT
http://en.wikipedia.org/wiki/Just-in-time_compilation
![Page 64: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/64.jpg)
If nothing else works
The modular design gives us freedom to rewrite any part using other languages and/or replace them with existing code
http://www.6502.org/tools/emu/#emulation
![Page 66: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/66.jpg)
Questions?Thank you!
@chesterbrhttp://slideshare.net/chesterbr
http://github.com/chesterbr/ruby2600
![Page 67: ruby2600 - an Atari 2600 emulator written in Ruby](https://reader037.vdocuments.mx/reader037/viewer/2022100305/55838e41d8b42a8e0c8b504b/html5/thumbnails/67.jpg)
Credits and LicenseThis presentation is available under the
Creative Commons “by-nc” 3.0 licensenoticing the exceptions below
Images from third parties were included (with due credits) underfair use assumption and/or under their respective licenses.
These are excluded from the license above.
Atari™ and likewise characters/games/systems are mentioned uniquely for illustrative purposes under fair use assumption. They are property of
their rights holders, and are also excluded from the license above.