mirror of
https://github.com/mattrberry/crab.git
synced 2024-12-27 09:58:55 +01:00
initial commit: load cartridge, parse instr types
This commit is contained in:
commit
069a536085
15 changed files with 293 additions and 0 deletions
9
.editorconfig
Normal file
9
.editorconfig
Normal 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
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
/docs/
|
||||||
|
/lib/
|
||||||
|
/bin/
|
||||||
|
/.shards/
|
||||||
|
*.dwarf
|
6
.travis.yml
Normal file
6
.travis.yml
Normal 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
21
LICENSE
Normal 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
27
README.md
Normal 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
13
shard.yml
Normal 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
9
spec/crab_spec.cr
Normal 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
2
spec/spec_helper.cr
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
require "spec"
|
||||||
|
require "../src/crab"
|
16
src/crab.cr
Normal file
16
src/crab.cr
Normal 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
40
src/crab/bus.cr
Normal 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
24
src/crab/cartridge.cr
Normal 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
27
src/crab/cpu.cr
Normal 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
22
src/crab/gba.cr
Normal 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
57
src/crab/types.cr
Normal 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
15
src/crab/util.cr
Normal 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
|
Loading…
Reference in a new issue