Skip to content

Commit

Permalink
alttp: unify modulesOKForSync between patched ROM asm check safe-for-…
Browse files Browse the repository at this point in the history
…update logic and sync logic; significantly reduces latency of critical events
  • Loading branch information
JamesDunne committed Feb 10, 2024
1 parent 4152c39 commit ed8622a
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 90 deletions.
3 changes: 1 addition & 2 deletions games/alttp/patcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,8 @@ func (p *Patcher) Patch() (err error) {
taMain.Label("moduleCheck")

// build a reversed bitmask because our bitmask lookup table is reversed:
modulesOK := [...]uint8{0x07, 0x09, 0x0B, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15, 0x16}
mask := uint16(0)
for _, m := range modulesOK {
for m := range modulesOKForSync {
mask |= uint16(1 << (15 - (m - 0x07)))
}
log.Printf("mask: %04X\n", mask)
Expand Down
14 changes: 1 addition & 13 deletions games/alttp/patcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,18 +152,6 @@ func TestPatcher_ModuleTests(t *testing.T) {
return
}

modulesOK := map[uint8]struct{}{
0x07: {},
0x09: {},
0x0B: {},
0x0E: {},
0x0F: {},
0x10: {},
0x11: {},
0x13: {},
0x15: {},
0x16: {},
}
for m := 0; m <= 0xFF; m++ {
m := uint8(m)
t.Run(fmt.Sprintf("module $%02X", m), func(t *testing.T) {
Expand All @@ -182,7 +170,7 @@ func TestPatcher_ModuleTests(t *testing.T) {
t.Logf("$0F = $%02X\n", e.WRAM[0x0F])

// check if the module should have passed:
if _, ok := modulesOK[m]; ok {
if _, ok := modulesOKForSync[m]; ok {
if e.WRAM[0x0F] == 0 {
t.Fatal("unexpected fail!")
}
Expand Down
143 changes: 68 additions & 75 deletions games/alttp/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,11 @@ func (g *Game) readMainComplete(rsps []snes.Response) {
}
}

// update frame as early as possible:
lastFrame := g.lastGameFrame
newFrame := g.wram[0x1A]
g.lastGameFrame = lastFrame

// assign local variables from WRAM:
local := g.LocalPlayer()

Expand All @@ -450,14 +455,7 @@ func (g *Game) readMainComplete(rsps []snes.Response) {
g.lastSubModule = submoduleStaging

doSync := true
if moduleStaging == 0x07 || moduleStaging == 0x09 || moduleStaging == 0x0b {
// good module, check submodule:
if g.wramU8(0x11) != 0 {
doSync = false
}
} else if moduleStaging == 0x0e {
// menu/interface module is ok:
} else {
if _, ok := modulesOKForSync[uint8(moduleStaging)]; !ok {
// bad module:
doSync = false
}
Expand Down Expand Up @@ -490,70 +488,73 @@ func (g *Game) readMainComplete(rsps []snes.Response) {
// this is documented as a uint16, but we use it as a uint8
local.PriorModule = Module(g.wram[0x010C])

inDungeon := g.wram[0x1B]
overworldArea := g.wramU16(0x8A)
dungeonRoom := g.wramU16(0xA0)
if local.OverworldArea != overworldArea {
log.Printf(
"alttp: local: overworld $%04x -> $%04x ; %s\n",
local.OverworldArea,
overworldArea,
overworldNames[overworldArea],
)
}
if local.DungeonRoom != dungeonRoom {
log.Printf(
"alttp: local: underworld $%04x -> $%04x ; %s\n",
local.DungeonRoom,
dungeonRoom,
underworldNames[dungeonRoom],
)
}
local.OverworldArea, local.DungeonRoom = overworldArea, dungeonRoom
// only sample location during sub-module 0 for any module; keeps location more stable:
if local.SubModule == 0 {
inDungeon := g.wram[0x1B]
overworldArea := g.wramU16(0x8A)
dungeonRoom := g.wramU16(0xA0)
if local.OverworldArea != overworldArea {
log.Printf(
"alttp: local: overworld $%04x -> $%04x ; %s\n",
local.OverworldArea,
overworldArea,
overworldNames[overworldArea],
)
}
if local.DungeonRoom != dungeonRoom {
log.Printf(
"alttp: local: underworld $%04x -> $%04x ; %s\n",
local.DungeonRoom,
dungeonRoom,
underworldNames[dungeonRoom],
)
}
local.OverworldArea, local.DungeonRoom = overworldArea, dungeonRoom

// TODO: fix this calculation to be compatible with alttpo
inDarkWorld := uint32(0)
if overworldArea&0x40 != 0 {
inDarkWorld = 1 << 17
}
// TODO: fix this calculation to be compatible with alttpo
inDarkWorld := uint32(0)
if overworldArea&0x40 != 0 {
inDarkWorld = 1 << 17
}

dungeon := g.wramU16(0x040C)
if local.Dungeon != dungeon {
dungName := "cave"
if dungeon < 0x20 {
dungName = dungeonNames[dungeon>>1]
dungeon := g.wramU16(0x040C)
if local.Dungeon != dungeon {
dungName := "cave"
if dungeon < 0x20 {
dungName = dungeonNames[dungeon>>1]
}
log.Printf(
"alttp: local: dungeon $%04x -> $%04x ; %s\n",
local.Dungeon,
dungeon,
dungName,
)
g.shouldUpdatePlayersList = true
}
local.Dungeon = dungeon

lastLocation := local.Location
local.Location = inDarkWorld | (uint32(inDungeon&1) << 16)
if inDungeon != 0 {
local.Location |= uint32(dungeonRoom)
} else {
local.Location |= uint32(overworldArea)
}
if local.Location != lastLocation {
g.shouldUpdatePlayersList = true
}
log.Printf(
"alttp: local: dungeon $%04x -> $%04x ; %s\n",
local.Dungeon,
dungeon,
dungName,
)
g.shouldUpdatePlayersList = true
}
local.Dungeon = dungeon

lastLocation := local.Location
local.Location = inDarkWorld | (uint32(inDungeon&1) << 16)
if inDungeon != 0 {
local.Location |= uint32(dungeonRoom)
} else {
local.Location |= uint32(overworldArea)
}
if local.Location != lastLocation {
g.shouldUpdatePlayersList = true
}

if local.Module.IsOverworld() {
local.LastOverworldX = local.X
local.LastOverworldY = local.Y
}
if local.Module.IsOverworld() {
local.LastOverworldX = local.X
local.LastOverworldY = local.Y
}

local.X = g.wramU16(0x22)
local.Y = g.wramU16(0x20)
local.X = g.wramU16(0x22)
local.Y = g.wramU16(0x20)

local.XOffs = int16(g.wramU16(0xE2)) - int16(g.wramU16(0x11A))
local.YOffs = int16(g.wramU16(0xE8)) - int16(g.wramU16(0x11C))
local.XOffs = int16(g.wramU16(0xE2)) - int16(g.wramU16(0x11A))
local.YOffs = int16(g.wramU16(0xE8)) - int16(g.wramU16(0x11C))
}

// copy $7EF000-4FF into `local.SRAM`:
//copy(local.SRAM[:], g.wram[0xF000:0xF500])
Expand Down Expand Up @@ -582,18 +583,10 @@ func (g *Game) readMainComplete(rsps []snes.Response) {
}

// did game frame change?
if g.wram[0x1A] == g.lastGameFrame {
if newFrame == lastFrame {
return
}

// increment frame timer:
lastFrame := uint64(g.lastGameFrame)
nextFrame := uint64(g.wram[0x1A])
if nextFrame < lastFrame {
nextFrame += 256
}
g.lastGameFrame = g.wram[0x1A]

// should wrap around 255 to 0:
g.monotonicFrameTime++

Expand Down
13 changes: 13 additions & 0 deletions games/alttp/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ import (
"strings"
)

var modulesOKForSync = map[uint8]struct{}{
0x07: {},
0x09: {},
0x0B: {},
0x0E: {},
0x0F: {},
0x10: {},
0x11: {},
0x13: {},
0x15: {},
0x16: {},
}

var dungeonNames = []string{
"Sewer Passage", // $37C
"Hyrule Castle", // $37D
Expand Down

0 comments on commit ed8622a

Please sign in to comment.