initial commit: load cartridge, parse instr types

This commit is contained in:
Matthew Berry 2020-08-22 00:15:30 -07:00
commit 069a536085
15 changed files with 293 additions and 0 deletions

9
.editorconfig Normal file
View file

@ -0,0 +1,9 @@
root = true
[*.cr]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
/docs/
/lib/
/bin/
/.shards/
*.dwarf

6
.travis.yml Normal file
View file

@ -0,0 +1,6 @@
language: crystal
# Uncomment the following if you'd like Travis to run specs and check code formatting
# script:
# - crystal spec
# - crystal tool format --check

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2020 your-name-here
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

27
README.md Normal file
View file

@ -0,0 +1,27 @@
# crab
TODO: Write a description here
## Installation
TODO: Write installation instructions here
## Usage
TODO: Write usage instructions here
## Development
TODO: Write development instructions here
## Contributing
1. Fork it (<https://github.com/your-github-user/crab/fork>)
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request
## Contributors
- [your-name-here](https://github.com/your-github-user) - creator and maintainer

13
shard.yml Normal file
View file

@ -0,0 +1,13 @@
name: crab
version: 0.1.0
authors:
- Matthew Berry <me@mattrb.com>
targets:
crab:
main: src/crab.cr
crystal: 0.35.1
license: MIT

9
spec/crab_spec.cr Normal file
View file

@ -0,0 +1,9 @@
require "./spec_helper"
describe Crab do
# TODO: Write tests
it "works" do
false.should eq(true)
end
end

2
spec/spec_helper.cr Normal file
View file

@ -0,0 +1,2 @@
require "spec"
require "../src/crab"

16
src/crab.cr Normal file
View file

@ -0,0 +1,16 @@
require "./crab/gba"
module Crab
VERSION = "0.1.0"
extend self
def run
gba = GBA.new ARGV[0]
gba.run
end
end
unless PROGRAM_NAME.includes?("crystal-run-spec")
Crab.run
end

40
src/crab/bus.cr Normal file
View file

@ -0,0 +1,40 @@
class Bus
CARTRIDGE = 0x08000000..0x0FFFFFFF
UNUSED = 0x10000000..0xFFFFFFFF
def initialize(@gba : GBA)
end
def [](index : Int) : Byte
# puts "read #{hex_str index.to_u32}"
case index
when CARTRIDGE then @gba.cartridge[index - CARTRIDGE.begin]
when UNUSED then 0xFF
else 0xFF
end.to_u8
end
def read_word(index : Int) : Word
self[index].to_u32 |
(self[index + 1].to_u32 << 8) |
(self[index + 2].to_u32 << 16) |
(self[index + 3].to_u32 << 24)
end
def []=(index : Int, value : Byte) : Nil
# puts "write #{hex_str index.to_u32} -> #{hex_str value}"
case index
when CARTRIDGE then @gba.cartridge[index]
when UNUSED then nil
else @todo[index] = value
end
end
def []=(index : Int, value : Word) : Nil
# puts "write #{hex_str index.to_u32} -> #{hex_str value}"
self[index] = 0xFF_u8 & (value >> 24)
self[index] = 0xFF_u8 & (value >> 16)
self[index] = 0xFF_u8 & (value >> 8)
self[index] = 0xFF_u8 & value
end
end

24
src/crab/cartridge.cr Normal file
View file

@ -0,0 +1,24 @@
class Cartridge
@rom : Bytes
getter title : String {
io = IO::Memory.new
io.write @rom[0x0A0...0x0AC]
io.to_s
}
def initialize(rom_path : String)
@rom = File.open rom_path do |file|
bytes = Bytes.new file.size
file.read bytes
bytes
end
end
def [](index : Int) : Byte
@rom[index]
end
def []=(index : Int, value : Byte) : Nil
end
end

27
src/crab/cpu.cr Normal file
View file

@ -0,0 +1,27 @@
class CPU
@registers = Slice(UInt32).new 16
def initialize(@gba : GBA)
self.pc = 0x08000000
end
def pc : Word
@registers[15]
end
def pc=(pc : Word) : Nil
@registers[15] = pc
end
def tick : Nil
puts "PC: #{hex_str pc}, INSTRUCTION: #{hex_str @gba.bus.read_word pc}, TYPE: #{Instr.from_hash hash_instr @gba.bus.read_word pc}"
# puts hex_str @gba.bus.read_word pc
# puts hex_str hash_instr @gba.bus.read_word pc
# puts Instr.from_hash hash_instr @gba.bus.read_word pc
self.pc += 4
end
def hash_instr(instr : Word) : Word
((instr >> 16) & 0x0FF0) | ((instr >> 4) & 0xF)
end
end

22
src/crab/gba.cr Normal file
View file

@ -0,0 +1,22 @@
require "./types"
require "./util"
require "./cartridge"
require "./bus"
require "./cpu"
class GBA
getter cartridge : Cartridge
getter bus : Bus { Bus.new self }
getter cpu : CPU { CPU.new self }
def initialize(rom_path : String)
@cartridge = Cartridge.new rom_path
end
def run : Nil
# puts @cartridge.title
loop do
cpu.tick
end
end
end

57
src/crab/types.cr Normal file
View file

@ -0,0 +1,57 @@
alias Byte = UInt8
alias Word = UInt32
alias Words = Slice(UInt32)
enum Instr
DATA_PROCESSING_PSR_TRANSFER
MULTIPLY
MULTIPLY_LONG
SINGLE_DATA_SWAP
BRANCH_EXCHANGE
HALFWORD_DATA_TRANSFER_REGISTER_OFFSET
HALFWORD_DATA_TRANSFER_IMMEDIATE_OFFSET
SINGLE_DATA_TRANSFER
UNDEFINED
BLOCK_DATA_TRANSFER
BRANCH
COPROCESSOR_DATA_TRANSFER
COPROCESSOR_DATA_OPERATION
COPROCESSOR_REGISTER_TRANSFER
SOFTWARE_INTERRUPT
def self.from_hash(hash : Word) : Instr
if hash & 0b111100000000 == 0b111100000000
SOFTWARE_INTERRUPT
elsif hash & 0b111100000001 == 0b111000000001
COPROCESSOR_REGISTER_TRANSFER
elsif hash & 0b111100000001 == 0b111000000001
COPROCESSOR_DATA_OPERATION
elsif hash & 0b111000000000 == 0b110000000000
COPROCESSOR_DATA_TRANSFER
elsif hash & 0b111000000000 == 0b101000000000
BRANCH
elsif hash & 0b111000000000 == 0b100000000000
BLOCK_DATA_TRANSFER
elsif hash & 0b111000000001 == 0b011000000001
UNDEFINED
elsif hash & 0b110000000000 == 0b010000000000
SINGLE_DATA_TRANSFER
elsif hash & 0b111001001001 == 0b000001001001
HALFWORD_DATA_TRANSFER_IMMEDIATE_OFFSET
elsif hash & 0b111001001001 == 0b000000001001
HALFWORD_DATA_TRANSFER_REGISTER_OFFSET
elsif hash & 0b111111111111 == 0b000100100001
BRANCH_EXCHANGE
elsif hash & 0b111110111111 == 0b000100001001
SINGLE_DATA_SWAP
elsif hash & 0b111110001111 == 0b000010001001
MULTIPLY_LONG
elsif hash & 0b111111001111 == 0b000000001001
MULTIPLY
elsif hash & 0b110000000000 == 0b000000000000
DATA_PROCESSING_PSR_TRANSFER
else
raise "Unimplemented from_hash #{hex_str hash}"
end
end
end

15
src/crab/util.cr Normal file
View file

@ -0,0 +1,15 @@
def hex_str(n : UInt8 | UInt16 | UInt32 | UInt64) : String
"0x#{n.to_s(16).rjust(sizeof(typeof(n)) * 2, '0').upcase}"
end
def bit?(value : Int, bit : Int) : Bool
(value >> bit) & 1 > 0
end
def set_bit(value : Int, bit : Int) : Nil
value | 1 << bit
end
def clear_bit(value : Int, bit : Int) : Nil
value & ~(1 << bit)
end