mirror of
https://github.com/mattrberry/crab.git
synced 2024-12-26 09:58:25 +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