Skip to content

Commit

Permalink
code comments
Browse files Browse the repository at this point in the history
drhelius committed Oct 17, 2016
1 parent 16e490a commit 59f7d94
Showing 15 changed files with 130 additions and 29 deletions.
26 changes: 19 additions & 7 deletions gb/core/core.go
Original file line number Diff line number Diff line change
@@ -13,32 +13,40 @@ import (
)

var pallete = [12]uint8{
0x87, 0x96, 0x03,
0x4d, 0x6b, 0x03,
0x2b, 0x55, 0x03,
0x14, 0x44, 0x03}
// R G B
0x87, 0x96, 0x03, // color 0
0x4d, 0x6b, 0x03, // color 1
0x2b, 0x55, 0x03, // color 2
0x14, 0x44, 0x03} // color 3

// RunToVBlank runs a single frame of the emulator
// The emulator must run at 60fps
func RunToVBlank(colorFrameBuffer []uint8) {

// keep updating each system
// until the vblank is reached
for vblank := false; !vblank; {
var clockCycles = cpu.Tick()
vblank = video.Tick(clockCycles)
input.Tick(clockCycles)
}

// the frame buffer of the Game Boy encodes color
// as a four shades of gray (or green)
// we have to transform these 4 colors to RGB by using
// a predefined pallete
for i, pixelCount := 0, util.GbWidth*util.GbHeight; i < pixelCount; i++ {
colorFrameBuffer[i*4] = pallete[video.GbFrameBuffer[i]*3] // red
colorFrameBuffer[(i*4)+1] = pallete[(video.GbFrameBuffer[i]*3)+1] // green
colorFrameBuffer[(i*4)+2] = pallete[(video.GbFrameBuffer[i]*3)+2] // blue
}
}

// LoadROM loads a new rom into the Emulator
// This fucntion must be called before running RunToVBlank
// LoadROM loads a new rom into the emulator
// this fucntion must be called before running RunToVBlank
func LoadROM(filePath string) {

fmt.Printf("loading rom \"%s\"...\n", filePath)
fmt.Printf("loading ROM \"%s\"...\n", filePath)

data, err := ioutil.ReadFile(filePath)

@@ -49,6 +57,7 @@ func LoadROM(filePath string) {
cartType := data[0x147]
var m mapper.Mapper

// check the memory mapper of the ROM
switch cartType {
case 0x00:
fmt.Println("found ROM")
@@ -69,9 +78,12 @@ func LoadROM(filePath string) {
fmt.Println("found MBC1 + SRAM + BATT")
m = new(mbcs.MBC1)
default:
// MBC1 and ROM are the only cartridges supported
panic(fmt.Sprintf("cartridge type not supported: %d", cartType))
}

// once we know which is the correct memory mapper
// we inject it into the systems that use the memory
m.Setup(data)
cpu.SetMapper(m)
video.SetMapper(m)
13 changes: 13 additions & 0 deletions gb/cpu/cpu.go
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ const (
InterruptJoypad uint8 = 0x10
)

// All the flags in the F register
const (
flagZero uint8 = 0x80
flagSub uint8 = 0x40
@@ -56,6 +57,7 @@ func Tick() uint {
clockCycles = 0

if halted {
// if an interrupt is pending leave halt
if interruptPending() != InterruptNone {
halted = false
} else {
@@ -64,13 +66,18 @@ func Tick() uint {
}

if !halted {
// acknowledge any pending interrupt s
serveInterrupt(interruptPending())

// fetch the next opcode and execute it
runOpcode(fetchOpcode())
}

updateTimers()
updateSerial()

// this is in order to delay the activation
// of ima one instruction
if imeCycles > 0 {
imeCycles -= int(clockCycles)
if imeCycles <= 0 {
@@ -89,11 +96,17 @@ func RequestInterrupt(interrupt uint8) {

func fetchOpcode() uint8 {
opcode := mem.Read(pc.GetValue())

// if there is an interrupt pending and
// the cpu is halted it fails to advance the PC register
// once the cpu resumes operation
// this bug is present in all the original DMGs
if skipPCBug {
skipPCBug = false
} else {
pc.Increment()
}

return opcode
}

3 changes: 3 additions & 0 deletions gb/cpu/functors.go
Original file line number Diff line number Diff line change
@@ -8,6 +8,9 @@ var opcodeCBArray opcodeFuncArray

func init() {

// this two arrays store the poiters to
// all the opcodes functions

opcodeArray[0x00] = opcode0x00
opcodeArray[0x01] = opcode0x01
opcodeArray[0x02] = opcode0x02
4 changes: 4 additions & 0 deletions gb/cpu/normal_opcodes.go
Original file line number Diff line number Diff line change
@@ -698,6 +698,10 @@ func opcode0x76() {

halted = true

// if there is an interrupt pending and
// the cpu is halted it fails to advance the PC register
// once the cpu resumes operation
// this bug is present in all the original DMGs
if !ime && ((ifreg & iereg & 0x1F) != 0) {
skipPCBug = true
}
2 changes: 1 addition & 1 deletion gb/cpu/registers.go
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ type EightBitReg struct {
value uint8
}

// SixteenBitReg models an 16 bit processor register
// SixteenBitReg models a 16 bit processor register
type SixteenBitReg struct {
high EightBitReg
low EightBitReg
3 changes: 3 additions & 0 deletions gb/cpu/timing.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package cpu

// machine cycles for normal opcodes
var machineCycles = [256]uint{
1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1,
1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1,
@@ -18,6 +19,7 @@ var machineCycles = [256]uint{
3, 3, 2, 0, 0, 4, 2, 4, 4, 1, 4, 0, 0, 0, 2, 4,
3, 3, 2, 1, 0, 4, 2, 4, 3, 2, 4, 1, 0, 0, 2, 4}

// machine cycles for normal opcodes plus cycles used in branches
var machineCyclesBranched = [256]uint{
1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1,
1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1,
@@ -36,6 +38,7 @@ var machineCyclesBranched = [256]uint{
3, 3, 2, 0, 0, 4, 2, 4, 4, 1, 4, 0, 0, 0, 2, 4,
3, 3, 2, 1, 0, 4, 2, 4, 3, 2, 4, 1, 0, 0, 2, 4}

// machine cycles for CB opcodes
var machineCyclesCB = [256]uint{
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
4 changes: 2 additions & 2 deletions gb/input/input.go
Original file line number Diff line number Diff line change
@@ -27,12 +27,12 @@ func Tick(cycles uint) {
}
}

// Read returns the 8 bit value at the 16 bit address of the memory
// Read returns the P1 register
func Read() uint8 {
return p1
}

// Write stores the 8 bit value at the 16 bit address of the memory
// Write stores the P1 register
func Write(value uint8) {
p1 = (p1 & 0xCF) | (value & 0x30)
update()
3 changes: 2 additions & 1 deletion gb/mbcs/common.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mbcs

// WriteCommon stores the 8 bit value at the 16 bit address of the memory
// WriteCommon emulates the internal RAM 0xC000-0xD000
// and the mirror of the internal RAM 0xE000-0xFE00
func WriteCommon(addr uint16, value uint8, mem []uint8) {
switch {
case (addr >= 0xC000) && (addr < 0xDE00):
5 changes: 3 additions & 2 deletions gb/mbcs/io.go
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ import (
"github.com/drhelius/demo-emulator/gb/video"
)

// ReadIO returns the 8 bit value at the 16 bit address of the memory
// ReadIO returns the values of the special IO registers
func ReadIO(addr uint16, mem []uint8) uint8 {
switch addr {
case 0xFF00:
@@ -24,6 +24,7 @@ func ReadIO(addr uint16, mem []uint8) uint8 {
// STAT
return mem[addr] | 0x80
case 0xFF44:
// LY
if video.ScreenEnabled {
return mem[0xFF44]
}
@@ -36,7 +37,7 @@ func ReadIO(addr uint16, mem []uint8) uint8 {
return mem[addr]
}

// WriteIO stores the 8 bit value at the 16 bit address of the memory
// WriteIO stores the values of the special IO registers
func WriteIO(addr uint16, value uint8, mem []uint8, m mapper.Mapper) {
switch addr {
case 0xFF00:
2 changes: 2 additions & 0 deletions gb/mbcs/iovalues.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package mbcs

// the initial values observed on a real Game Boy
// for the IO registers 0xFF00-0xFF
var initialValuesForFFXX = [256]uint8{
0xCF, 0x00, 0x7E, 0xFF, 0xD3, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1,
0x80, 0xBF, 0xF3, 0xFF, 0xBF, 0xFF, 0x3F, 0x00, 0xFF, 0xBF, 0x7F, 0xFF, 0x9F, 0xFF, 0xBF, 0xFF,
26 changes: 20 additions & 6 deletions gb/mbcs/mbc1.go
Original file line number Diff line number Diff line change
@@ -2,7 +2,8 @@ package mbcs

import "fmt"

// MBC1 is the public type for Memory
// MBC1 is a mapper implementation to emulate
// cartridges with a MBC1 memory bank controller
type MBC1 struct {
memoryMap []uint8
rom []uint8
@@ -30,31 +31,37 @@ func (m *MBC1) GetROM() []uint8 {
// Setup Receives the rom data and intializes memory
func (m *MBC1) Setup(r []uint8) {
m.rom = r
m.romBank = 1

m.memoryMap = make([]uint8, 0x10000)
m.ram = make([]uint8, 0x8000)

for i := 0; i < 0x100; i++ {
m.memoryMap[0xFF00+i] = initialValuesForFFXX[i]
}

m.romBank = 1
m.ramSize = m.rom[0x149]
ramBanks := 0

switch m.ramSize {
case 0x00:
fallthrough
ramBanks = 0
m.higherRAMBank = 0x00
case 0x01:
fallthrough
case 0x02:
ramBanks = 1
m.higherRAMBank = 0x00
default:
ramBanks = 4
m.higherRAMBank = 0x03
break
}

m.higherROMBank = uint(max(pow2Ceil(len(m.rom)/0x4000), 2) - 1)

fmt.Printf("%d ROM banks\n", m.higherROMBank+1)
fmt.Printf("%d RAM banks\n", m.higherRAMBank+1)
fmt.Printf("%d RAM banks\n", ramBanks)
}

// Read returns the 8 bit value at the 16 bit address of the memory
@@ -77,7 +84,7 @@ func (m *MBC1) Read(addr uint16) uint8 {
fmt.Printf("*** attempting to read from disabled RAM %X\n", addr)
return 0xFF
case addr >= 0xFF00:
// IO Registers
// IO registers
return ReadIO(addr, m.memoryMap)
}
return m.memoryMap[addr]
@@ -87,10 +94,12 @@ func (m *MBC1) Read(addr uint16) uint8 {
func (m *MBC1) Write(addr uint16, value uint8) {
switch {
case (addr >= 0x0000) && (addr < 0x2000):
// enable / disable RAM
if m.ramSize > 0 {
m.ramEnabled = ((value & 0x0F) == 0x0A)
}
case (addr >= 0x2000) && (addr < 0x4000):
// select ROM bank
if m.mode == 0 {
m.romBank = uint(value&0x1F) | (m.romBankHighBits << 5)
} else {
@@ -102,9 +111,11 @@ func (m *MBC1) Write(addr uint16, value uint8) {
m.romBank &= m.higherROMBank
case (addr >= 0x4000) && (addr < 0x6000):
if m.mode == 1 {
// select RAM bank
m.ramBank = uint16(value & 0x03)
m.ramBank &= m.higherRAMBank
} else {
// select high bits of ROM bank
m.romBankHighBits = uint(value & 0x03)
m.romBank = (m.romBank & 0x1F) | (m.romBankHighBits << 5)
if m.romBank == 0x00 || m.romBank == 0x20 || m.romBank == 0x40 || m.romBank == 0x60 {
@@ -113,12 +124,14 @@ func (m *MBC1) Write(addr uint16, value uint8) {
m.romBank &= m.higherROMBank
}
case (addr >= 0x6000) && (addr < 0x8000):
// operation mode
if (m.ramSize != 3) && ((value & 0x01) != 0) {
fmt.Printf("*** attempting to change MBC1 to mode 1 with incorrect RAM banks %X %X\n", addr, value)
} else {
m.mode = value & 0x01
}
case (addr >= 0xA000) && (addr < 0xC000):
// cartridge RAM
if m.ramEnabled {
if m.mode == 0 {
m.ram[addr-0xA000] = value
@@ -129,9 +142,10 @@ func (m *MBC1) Write(addr uint16, value uint8) {
fmt.Printf("*** attempting to write to disabled RAM %X %X\n", addr, value)
}
case (addr >= 0xC000) && (addr < 0xFE00):
// internal RAM
WriteCommon(addr, value, m.memoryMap)
case addr >= 0xFF00:
// IO Registers
// IO registers
WriteIO(addr, value, m.memoryMap, m)
default:
m.memoryMap[addr] = value
4 changes: 3 additions & 1 deletion gb/mbcs/romonly.go
Original file line number Diff line number Diff line change
@@ -2,7 +2,8 @@ package mbcs

import "fmt"

// RomOnly is the public type for Memory
// RomOnly is a mapper implementation to emulate
// cartridges with only a ROM chip
type RomOnly struct {
memoryMap []uint8
rom []uint8
@@ -48,6 +49,7 @@ func (m *RomOnly) Write(addr uint16, value uint8) {
// ROM
fmt.Printf("*** attempting to write on ROM address %X %X\n", addr, value)
case (addr >= 0xC000) && (addr < 0xFE00):
// Internal RAM
WriteCommon(addr, value, m.memoryMap)
case addr >= 0xFF00:
// IO Registers
Loading

0 comments on commit 59f7d94

Please sign in to comment.