passing dennis' eeprom tests

This commit is contained in:
Matthew Berry 2021-07-02 18:02:16 -07:00
parent e5ec0eab9f
commit 4ce01591ee
4 changed files with 135 additions and 4 deletions

View file

@ -14,6 +14,10 @@ macro set_bit(value, bit)
({{value}} | 1 << {{bit}})
end
macro set_bit(value, bit, set)
(clear_bit({{value}}, {{bit}}) | {{set}} << {{bit}})
end
macro clear_bit(value, bit)
({{value}} & ~(1 << {{bit}}))
end

View file

@ -23,7 +23,13 @@ module GBA
when 0x7 then @gba.ppu.oam[index & 0x3FF]
when 0x8, 0x9,
0xA, 0xB,
0xC, 0xD then @gba.cartridge.rom[index & 0x01FFFFFF]
0xC then @gba.cartridge.rom[index & 0x01FFFFFF]
when 0xD
if @gba.storage.eeprom? index
@gba.storage[index]
else
@gba.cartridge.rom[index & 0x01FFFFFF]
end
when 0xE, 0xF then @gba.storage[index]
else abort "Unmapped read: #{hex_str index.to_u32}"
end
@ -45,7 +51,13 @@ module GBA
when 0x7 then (@gba.ppu.oam.to_unsafe + (index & 0x3FF)).as(HalfWord*).value
when 0x8, 0x9,
0xA, 0xB,
0xC, 0xD then (@gba.cartridge.rom.to_unsafe + (index & 0x01FFFFFF)).as(HalfWord*).value
0xC then (@gba.cartridge.rom.to_unsafe + (index & 0x01FFFFFF)).as(HalfWord*).value
when 0xD
if @gba.storage.eeprom? index
@gba.storage[index].to_u16!
else
(@gba.cartridge.rom.to_unsafe + (index & 0x01FFFFFF)).as(HalfWord*).value
end
when 0xE, 0xF then @gba.storage.read_half(index)
else abort "Unmapped read: #{hex_str index.to_u32}"
end
@ -84,7 +96,13 @@ module GBA
when 0x7 then (@gba.ppu.oam.to_unsafe + (index & 0x3FF)).as(Word*).value
when 0x8, 0x9,
0xA, 0xB,
0xC, 0xD then (@gba.cartridge.rom.to_unsafe + (index & 0x01FFFFFF)).as(Word*).value
0xC then (@gba.cartridge.rom.to_unsafe + (index & 0x01FFFFFF)).as(Word*).value
when 0xD
if @gba.storage.eeprom? index
@gba.storage[index].to_u32!
else
(@gba.cartridge.rom.to_unsafe + (index & 0x01FFFFFF)).as(Word*).value
end
when 0xE, 0xF then @gba.storage.read_word(index)
else abort "Unmapped read: #{hex_str index.to_u32}"
end
@ -107,6 +125,7 @@ module GBA
when 0x6
address = 0x1FFFE_u32 & index # todo ignored range is different when in bitmap mode
(@gba.ppu.vram.to_unsafe + address).as(HalfWord*).value = 0x0101_u16 * value if address <= 0x0FFFF
when 0xD then @gba.storage[index] = value if @gba.storage.eeprom? index
when 0xE, 0xF then @gba.storage[index] = value
else log "Unmapped write: #{hex_str index.to_u32}"
end
@ -126,6 +145,7 @@ module GBA
address -= 0x8000 if address > 0x17FFF
(@gba.ppu.vram.to_unsafe + address).as(HalfWord*).value = value
when 0x7 then (@gba.ppu.oam.to_unsafe + (index & 0x3FF)).as(HalfWord*).value = value
when 0xD then @gba.storage[index] = value.to_u8! if @gba.storage.eeprom? index
when 0xE, 0xF then write_half_slow(index, value)
else log "Unmapped write: #{hex_str index.to_u32}"
end
@ -145,6 +165,7 @@ module GBA
address -= 0x8000 if address > 0x17FFF
(@gba.ppu.vram.to_unsafe + address).as(Word*).value = value
when 0x7 then (@gba.ppu.oam.to_unsafe + (index & 0x3FF)).as(Word*).value = value
when 0xD then @gba.storage[index] = value.to_u8! if @gba.storage.eeprom? index
when 0xE, 0xF then write_word_slow(index, value)
else log "Unmapped write: #{hex_str index.to_u32}"
end

View file

@ -32,7 +32,7 @@ module GBA
puts "Backup type could not be identified.".colorize.fore(:red) unless type
puts "Backup type: #{type}, save path: #{save_path}"
storage = case type
in Type::EEPROM then abort "todo: Support EEPROM"
in Type::EEPROM then EEPROM.new
in Type::SRAM, nil then SRAM.new
in Type::FLASH, Type::FLASH512, Type::FLASH1M then Flash.new type
end
@ -60,6 +60,10 @@ module GBA
abstract def []=(index : Int, value : Byte) : Nil
def eeprom?(index : Int) : Bool
self.class == EEPROM && (0x0D000000..0x0DFFFFFF).includes? index
end
private def self.find_type(file : File) : Type?
str = file.gets_to_end
Type.each { |type| return type if type.regex.matches?(str) }

View file

@ -0,0 +1,102 @@
module GBA
class EEPROM < Storage
@[Flags]
enum State
READY
READ
READ_IGNORE
WRITE
ADDRESS
WRITE_FINAL_BIT
LOCK_ADDRESS
CMD_1
CMD_2
IDENTIFICATION
PREPARE_WRITE
PREPARE_ERASE
SET_BANK
end
@memory = Bytes.new(0x2000, 0xFF)
@state = State::READY
@buffer = Buffer.new
@address : UInt32 = 0
@ignored_reads = 0
def [](index : Int) : Byte
case @state
when .includes? State::READ_IGNORE
if (@ignored_reads += 1) == 4
@state ^= State::READ_IGNORE
@buffer.value = @memory.to_unsafe.as(UInt64*)[@address]
@buffer.size = 64
end
when State::READ
value = @buffer.pop.to_u8
if @buffer.size == 0
@state = State::READY
@buffer.clear
end
return value
end
1_u8
end
def []=(index : Int, value : Byte) : Nil
return if @state == State::READ || @state == State::READ_IGNORE
value &= 1
@buffer.push value
case @state
when State::READY
if @buffer.size == 2
case @buffer.value
when 0b10 then @state = State::ADDRESS | State::WRITE | State::WRITE_FINAL_BIT
when 0b11 then @state = State::ADDRESS | State::READ | State::READ_IGNORE | State::WRITE_FINAL_BIT; @ignored_reads = 0
end
@address = 0
@buffer.clear
end
when .includes? State::ADDRESS
if @buffer.size == 14 # todo: support 4Kbit eeprom
@address = @buffer.value.to_u32
@memory.to_unsafe.as(UInt64*)[@address] = 0 if @state.includes? State::WRITE
@state ^= State::ADDRESS
@buffer.clear
end
when .includes? State::WRITE
index = (@buffer.size - 1) // 8
if @buffer.size == 64
@memory.to_unsafe.as(UInt64*)[@address] = @buffer.value
@buffer.clear
@state = State::READY | State::WRITE_FINAL_BIT
end
when .includes? State::WRITE_FINAL_BIT
@buffer.clear
@state ^= State::WRITE_FINAL_BIT
end
end
end
private class Buffer
property size = 0
property value : UInt64 = 0
def push(value : Int) : UInt64
@size += 1
@value = (@value << 1) | (value & 1)
end
def pop : UInt64
abort "Invalid buffer size #{@size}" if @size <= 0
@size -= 1
@value >> @size & 1
end
def clear : Nil
@size = 0
@value = 0
end
end
end