Migrate instruction encoding to external library

The PowerPC instruction encoder and patching logic is now available at https://github.com/wii-tools/powerpc.
This commit is contained in:
Spotlight 2022-04-10 23:20:37 -05:00
parent 4a232c552b
commit e17dbf9ce1
No known key found for this signature in database
GPG Key ID: 874AA355B3209BDC
11 changed files with 463 additions and 818 deletions

1
go.mod
View File

@ -6,5 +6,6 @@ require (
github.com/logrusorgru/aurora/v3 v3.0.0 github.com/logrusorgru/aurora/v3 v3.0.0
github.com/wii-tools/GoNUSD v0.2.2 github.com/wii-tools/GoNUSD v0.2.2
github.com/wii-tools/arclib v1.0.0 github.com/wii-tools/arclib v1.0.0
github.com/wii-tools/powerpc v0.0.0-20220411041712-104089eba964
github.com/wii-tools/wadlib v0.3.1 github.com/wii-tools/wadlib v0.3.1
) )

2
go.sum
View File

@ -4,5 +4,7 @@ github.com/wii-tools/GoNUSD v0.2.2 h1:vHiSLI9sK9Q4zbCt24jMiq/lo2FISLBuCHoaY3Bd88
github.com/wii-tools/GoNUSD v0.2.2/go.mod h1:Wf0ZMf4jdyQYSaSmNyFsqfQbngFA/cM9mpJBZ63Qlzs= github.com/wii-tools/GoNUSD v0.2.2/go.mod h1:Wf0ZMf4jdyQYSaSmNyFsqfQbngFA/cM9mpJBZ63Qlzs=
github.com/wii-tools/arclib v1.0.0 h1:OAmbL3NDUmlR0wa1VpJhxnKiIRFZz1CC40lTVPUm8Ms= github.com/wii-tools/arclib v1.0.0 h1:OAmbL3NDUmlR0wa1VpJhxnKiIRFZz1CC40lTVPUm8Ms=
github.com/wii-tools/arclib v1.0.0/go.mod h1:uXFan/NSXoQ2pOVPN4ugZ4nJX7esBnjB1QUgVrEzK/4= github.com/wii-tools/arclib v1.0.0/go.mod h1:uXFan/NSXoQ2pOVPN4ugZ4nJX7esBnjB1QUgVrEzK/4=
github.com/wii-tools/powerpc v0.0.0-20220411041712-104089eba964 h1:/ZeMBIoblxzyhKHQJncvBZaxqi6RZz4rm4QvL0QiguA=
github.com/wii-tools/powerpc v0.0.0-20220411041712-104089eba964/go.mod h1:bt/52tMfh1hmEXwFh1i1AOEMNHoe0jSXkcRunOSdFkc=
github.com/wii-tools/wadlib v0.3.1 h1:g0Szzof/YsBLghP+JpoVzT/6M6jpl+AH9PHiUuG3cd8= github.com/wii-tools/wadlib v0.3.1 h1:g0Szzof/YsBLghP+JpoVzT/6M6jpl+AH9PHiUuG3cd8=
github.com/wii-tools/wadlib v0.3.1/go.mod h1:GK+f2POk+rVu1p4xqLSb4ll1SKKbfOO6ZAB+oPLV3uQ= github.com/wii-tools/wadlib v0.3.1/go.mod h1:GK+f2POk+rVu1p4xqLSb4ll1SKKbfOO6ZAB+oPLV3uQ=

17
main.go
View File

@ -6,6 +6,7 @@ import (
"github.com/logrusorgru/aurora/v3" "github.com/logrusorgru/aurora/v3"
"github.com/wii-tools/GoNUSD" "github.com/wii-tools/GoNUSD"
"github.com/wii-tools/arclib" "github.com/wii-tools/arclib"
"github.com/wii-tools/powerpc"
"github.com/wii-tools/wadlib" "github.com/wii-tools/wadlib"
"io/fs" "io/fs"
"io/ioutil" "io/ioutil"
@ -137,6 +138,22 @@ func main() {
writeOut("patched.wad", output) writeOut("patched.wad", output)
} }
// applyDefaultPatches applies the default patches to our main DOL.
func applyDefaultPatches() {
var err error
mainDol, err = powerpc.ApplyPatchSet(OverwriteIOSPatch, mainDol)
check(err)
mainDol, err = powerpc.ApplyPatchSet(LoadCustomCA(), mainDol)
check(err)
mainDol, err = powerpc.ApplyPatchSet(PatchBaseDomain(), mainDol)
check(err)
mainDol, err = powerpc.ApplyPatchSet(NegateECTitle, mainDol)
check(err)
mainDol, err = powerpc.ApplyPatchSet(PatchECCfgPath, mainDol)
check(err)
}
// check has an anxiety attack if things go awry. // check has an anxiety attack if things go awry.
func check(err error) { func check(err error) {
if err != nil { if err != nil {

View File

@ -1,98 +0,0 @@
package main
import (
"bytes"
"errors"
"fmt"
"github.com/logrusorgru/aurora/v3"
)
var (
ErrInconsistentPatch = errors.New("before and after data present within file are not the same size")
ErrPatchOutOfRange = errors.New("patch cannot be applied past binary size")
ErrInvalidPatch = errors.New("before data present within patch did not exist in file")
)
// Patch represents a patch applied to the main binary.
type Patch struct {
// Name is an optional name for this patch.
// If present, its name will be logged upon application.
Name string
// AtOffset is the offset within the file this patch should be applied at.
// If not present, the patch will be recursively applied across the entire file.
// Relying on this behavior is highly discouraged, as it may damage other parts of the binary
// if gone unchecked.
AtOffset int
// Before is an array of the bytes to find for, i.e. present within the original file.
Before []byte
// After is an array of the bytes to replace with.
After []byte
}
// PatchSet represents multiple patches available to be applied.
type PatchSet []Patch
// applyPatch applies the given patch to the main DOL.
func applyPatch(patch Patch) error {
// Print name if present
if patch.Name != "" {
fmt.Println(" + Applying patch", aurora.Cyan(patch.Name))
}
// Ensure consistency
if len(patch.Before) != len(patch.After) {
return ErrInconsistentPatch
}
if patch.AtOffset != 0 && patch.AtOffset > len(mainDol) {
return ErrPatchOutOfRange
}
// Either Before or After should return the same length.
patchLen := len(patch.Before)
// Determine our patching behavior.
if patch.AtOffset != 0 {
// Ensure original bytes are present
originalBytes := mainDol[patch.AtOffset : patch.AtOffset+patchLen]
if !bytes.Equal(originalBytes, patch.Before) {
return ErrInvalidPatch
}
// Apply patch at the specified offset
copy(mainDol[patch.AtOffset:], patch.After)
} else {
// Recursively apply this patch.
// We cannot verify if the original contents are present via this.
mainDol = bytes.ReplaceAll(mainDol, patch.Before, patch.After)
}
return nil
}
// applyPatchSet iterates through all possible patches, noting their name.
func applyPatchSet(setName string, set PatchSet) {
fmt.Printf("Handling patch set \"%s\":\n", aurora.Yellow(setName))
for _, patch := range set {
err := applyPatch(patch)
check(err)
}
}
// emptyBytes returns an empty byte array of the given length.
func emptyBytes(length int) []byte {
return bytes.Repeat([]byte{0x00}, length)
}
// applyDefaultPatches iterates through a list of default patches.
func applyDefaultPatches() {
applyPatchSet("Overwrite IOS Syscall for ES", OverwriteIOSPatch)
applyPatchSet("Load Custom CA within IOS", LoadCustomCA())
applyPatchSet("Change Base Domain", PatchBaseDomain())
applyPatchSet("Negate EC Title Check", NegateECTitle)
applyPatchSet("Change EC Configuration Path", PatchECCfgPath)
}

View File

@ -1,6 +1,10 @@
package main package main
import "strings" import (
"strings"
. "github.com/wii-tools/powerpc"
)
const ( const (
NintendoBaseDomain = "shop.wii.com" NintendoBaseDomain = "shop.wii.com"
@ -15,39 +19,42 @@ const (
// See docs/patch_base_domain.md for more information. // See docs/patch_base_domain.md for more information.
func PatchBaseDomain() PatchSet { func PatchBaseDomain() PatchSet {
return PatchSet{ return PatchSet{
Patch{ Name: "Change Base Domain",
Patches: []Patch{
{
Name: "Modify /startup domain", Name: "Modify /startup domain",
Before: []byte(ShowManualURL), Before: []byte(ShowManualURL),
After: padReplace(ShowManualURL), After: padReplace(ShowManualURL),
}, },
Patch{ {
Name: "Modify oss-auth URL", Name: "Modify oss-auth URL",
AtOffset: 3180692, AtOffset: 3180692,
Before: []byte(GetLogURL), Before: []byte(GetLogURL),
After: padReplace(GetLogURL), After: padReplace(GetLogURL),
}, },
Patch{ {
Name: "Modify trusted base domain prefix", Name: "Modify trusted base domain prefix",
AtOffset: 3323432, AtOffset: 3323432,
Before: []byte(TrustedDomain), Before: []byte(TrustedDomain),
After: padReplace(TrustedDomain), After: padReplace(TrustedDomain),
}, },
Patch{ {
Name: "Modify ECS SOAP endpoint URL", Name: "Modify ECS SOAP endpoint URL",
AtOffset: 3268896, AtOffset: 3268896,
Before: []byte(ECommerceBaseURL), Before: []byte(ECommerceBaseURL),
After: padReplace(ECommerceBaseURL), After: padReplace(ECommerceBaseURL),
}, },
Patch{ {
Name: "Wildcard replace other instances", Name: "Wildcard replace other instances",
Before: []byte(NintendoBaseDomain), Before: []byte(NintendoBaseDomain),
After: padReplace(baseDomain), After: padReplace(baseDomain),
}, },
},
} }
} }
@ -60,5 +67,5 @@ func padReplace(url string) []byte {
} }
padding := len(url) - len(replaced) padding := len(url) - len(replaced)
return append([]byte(replaced), emptyBytes(padding)...) return append([]byte(replaced), EmptyBytes(padding)...)
} }

View File

@ -1,19 +1,25 @@
package main package main
import (
. "github.com/wii-tools/powerpc"
)
// LoadCustomCA loads our custom certificate, either generated or loaded, // LoadCustomCA loads our custom certificate, either generated or loaded,
// into the IOS trust store for EC usage. // into the IOS trust store for EC usage.
// It is assumed that rootCertificate has been loaded upon invoking this patchset. // It is assumed that rootCertificate has been loaded upon invoking this patchset.
// See docs/patch_custom_ca_ios.md for more information. // See docs/patch_custom_ca_ios.md for more information.
func LoadCustomCA() PatchSet { func LoadCustomCA() PatchSet {
return PatchSet{ return PatchSet{
Patch{ Name: "Load Custom CA within IOS",
Patches: []Patch{
{
Name: "Insert custom CA into free space", Name: "Insert custom CA into free space",
AtOffset: 3037368, AtOffset: 3037368,
Before: emptyBytes(len(rootCertificate)), Before: EmptyBytes(len(rootCertificate)),
After: rootCertificate, After: rootCertificate,
}, },
Patch{ {
Name: "Modify NHTTPi_SocSSLConnect to load cert", Name: "Modify NHTTPi_SocSSLConnect to load cert",
AtOffset: 644624, AtOffset: 644624,
@ -67,7 +73,7 @@ func LoadCustomCA() PatchSet {
LI(R3, 0xfc14), LI(R3, 0xfc14),
// b FUNCTION_PROLOG // b FUNCTION_PROLOG
B(0x800acb10, 0x800acbb0), B(0x800acb10, 0x800acbb0),
}.toBytes(), }.Bytes(),
After: Instructions{ After: Instructions{
// Our certificate is present at 0x802e97b8. // Our certificate is present at 0x802e97b8.
// r4 is the second parameter of SSLSetRootCA, the ca_cert pointer. // r4 is the second parameter of SSLSetRootCA, the ca_cert pointer.
@ -98,7 +104,8 @@ func LoadCustomCA() PatchSet {
// NOP the rest in order to allow execution to continue. // NOP the rest in order to allow execution to continue.
NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(),
}.toBytes(), }.Bytes(),
},
}, },
} }
} }

View File

@ -1,11 +1,17 @@
package main package main
import (
. "github.com/wii-tools/powerpc"
)
var PatchECCfgPath = PatchSet{ var PatchECCfgPath = PatchSet{
Patch{
Name: "Change EC Configuration Path", Name: "Change EC Configuration Path",
Patches: []Patch{
{
AtOffset: 3319968, AtOffset: 3319968,
Before: []byte("ec.cfg\x00\x00"), Before: []byte("ec.cfg\x00\x00"),
After: []byte("osc.cfg\x00"), After: []byte("osc.cfg\x00"),
}, },
},
} }

View File

@ -1,6 +1,13 @@
package main package main
import (
. "github.com/wii-tools/powerpc"
)
var NegateECTitle = PatchSet{ var NegateECTitle = PatchSet{
Name: "Negate EC Title Check",
Patches: []Patch{
Patch{ Patch{
Name: "Permit downloading all titles", Name: "Permit downloading all titles",
AtOffset: 619648, AtOffset: 619648,
@ -9,13 +16,13 @@ var NegateECTitle = PatchSet{
Before: Instructions{ Before: Instructions{
STWU(R1, R1, 0xffe0), STWU(R1, R1, 0xffe0),
MFSPR(), MFSPR(),
}.toBytes(), }.Bytes(),
// Immediately return true // Immediately return true
After: Instructions{ After: Instructions{
LI(R3, 1), LI(R3, 1),
BLR(), BLR(),
}.toBytes(), }.Bytes(),
}, },
Patch{ Patch{
Name: "Mark all titles as managed", Name: "Mark all titles as managed",
@ -24,11 +31,11 @@ var NegateECTitle = PatchSet{
Before: Instructions{ Before: Instructions{
STWU(R1, R1, 0xfff0), STWU(R1, R1, 0xfff0),
MFSPR(), MFSPR(),
}.toBytes(), }.Bytes(),
After: Instructions{ After: Instructions{
LI(R3, 1), LI(R3, 1),
BLR(), BLR(),
}.toBytes(), }.Bytes(),
}, },
Patch{ Patch{
Name: "Mark all tickets as managed", Name: "Mark all tickets as managed",
@ -36,11 +43,11 @@ var NegateECTitle = PatchSet{
Before: Instructions{ Before: Instructions{
STWU(R1, R1, 0xfff0), STWU(R1, R1, 0xfff0),
MFSPR(), MFSPR(),
}.toBytes(), }.Bytes(),
After: Instructions{ After: Instructions{
LI(R3, 1), LI(R3, 1),
BLR(), BLR(),
}.toBytes(), }.Bytes(),
}, },
Patch{ Patch{
Name: "Nullify ec::removeAllTitles", Name: "Nullify ec::removeAllTitles",
@ -48,10 +55,11 @@ var NegateECTitle = PatchSet{
Before: Instructions{ Before: Instructions{
STWU(R1, R1, 0xffc0), STWU(R1, R1, 0xffc0),
MFSPR(), MFSPR(),
}.toBytes(), }.Bytes(),
After: Instructions{ After: Instructions{
LI(R3, 0), LI(R3, 0),
BLR(), BLR(),
}.toBytes(), }.Bytes(),
},
}, },
} }

View File

@ -1,9 +1,15 @@
package main package main
import (
. "github.com/wii-tools/powerpc"
)
// OverwriteIOSPatch effectively nullifies IOSC_VerifyPublicKeySign. // OverwriteIOSPatch effectively nullifies IOSC_VerifyPublicKeySign.
// See docs/patch_overwrite_ios.md for more information. // See docs/patch_overwrite_ios.md for more information.
var OverwriteIOSPatch = PatchSet{ var OverwriteIOSPatch = PatchSet{
Patch{ Name: "Overwrite IOS Syscall for ES",
Patches: []Patch{
{
Name: "Clear extraneous functions", Name: "Clear extraneous functions",
AtOffset: 20272, AtOffset: 20272,
@ -29,30 +35,30 @@ var OverwriteIOSPatch = PatchSet{
// b printf // b printf
Instruction{0x48, 0x2a, 0x8b, 0x5c}, Instruction{0x48, 0x2a, 0x8b, 0x5c},
padding, Padding,
// Function: textinput::EventObserver::onSE // Function: textinput::EventObserver::onSE
BLR(), BLR(),
padding, padding, padding, Padding, Padding, Padding,
// Function: textinput::EventObserver::onEvent // Function: textinput::EventObserver::onEvent
BLR(), BLR(),
padding, padding, padding, Padding, Padding, Padding,
// Function: textinput::EventObserver::onCommand // Function: textinput::EventObserver::onCommand
BLR(), BLR(),
padding, padding, padding, Padding, Padding, Padding,
// Function: textinput::EventObserver::onInput // Function: textinput::EventObserver::onInput
BLR(), BLR(),
padding, padding, padding, Padding, Padding, Padding,
}.toBytes(), }.Bytes(),
// We wish to clear extraneous blrs so that our custom overwriteIOSMemory // We wish to clear extraneous blrs so that our custom overwriteIOSMemory
// function does not somehow conflict. // function does not somehow conflict.
// We only preserve onSE, which is this immediate BLR. // We only preserve onSE, which is this immediate BLR.
After: append(Instructions{ After: append(Instructions{
BLR(), BLR(),
}.toBytes(), emptyBytes(108)...), }.Bytes(), EmptyBytes(108)...),
}, },
Patch{ {
Name: "Repair textinput::EventObserver vtable", Name: "Repair textinput::EventObserver vtable",
AtOffset: 3095452, AtOffset: 3095452,
@ -76,7 +82,7 @@ var OverwriteIOSPatch = PatchSet{
0x80, 0x01, 0x43, 0xf0, 0x80, 0x01, 0x43, 0xf0,
}, },
}, },
Patch{ {
Name: "Repair ipl::keyboard::EventObserver vtable", Name: "Repair ipl::keyboard::EventObserver vtable",
AtOffset: 3097888, AtOffset: 3097888,
@ -99,11 +105,11 @@ var OverwriteIOSPatch = PatchSet{
0x80, 0x01, 0x43, 0xf0, // textinput::EventObserver::doNothing 0x80, 0x01, 0x43, 0xf0, // textinput::EventObserver::doNothing
}, },
}, },
Patch{ {
Name: "Insert patch table", Name: "Insert patch table",
AtOffset: 3205088, AtOffset: 3205088,
Before: emptyBytes(52), Before: EmptyBytes(52),
After: []byte{ After: []byte{
////////////// //////////////
// PATCH #1 // // PATCH #1 //
@ -176,13 +182,13 @@ var OverwriteIOSPatch = PatchSet{
0x93, 0x00, 0x01, 0xff, 0x93, 0x00, 0x01, 0xff,
}, },
}, },
Patch{ {
Name: "Insert overwriteIOSMemory", Name: "Insert overwriteIOSMemory",
AtOffset: 20276, AtOffset: 20276,
// This area should be cleared in the patch // This area should be cleared in the patch
// "Clear extraneous functions". // "Clear extraneous functions".
Before: emptyBytes(108), Before: EmptyBytes(108),
After: Instructions{ After: Instructions{
// Our patch table is available at 0x803126e0. // Our patch table is available at 0x803126e0.
LIS(R8, 0x8031), LIS(R8, 0x8031),
@ -248,28 +254,29 @@ var OverwriteIOSPatch = PatchSet{
// We're finished patching! // We're finished patching!
BLR(), BLR(),
}.toBytes(), }.Bytes(),
}, },
Patch{ {
Name: "Do not require input for exception handler", Name: "Do not require input for exception handler",
AtOffset: 32032, AtOffset: 32032,
Before: Instructions{ Before: Instructions{
STWU(R1, R1, 0xFC10), STWU(R1, R1, 0xFC10),
}.toBytes(), }.Bytes(),
After: Instructions{ After: Instructions{
BLR(), BLR(),
}.toBytes(), }.Bytes(),
}, },
Patch{ {
Name: "Modify ipl::Exception::__ct", Name: "Modify ipl::Exception::__ct",
AtOffset: 31904, AtOffset: 31904,
Before: Instructions{ Before: Instructions{
BLR(), BLR(),
}.toBytes(), }.Bytes(),
After: Instructions{ After: Instructions{
// b overwriteIOSMemory // b overwriteIOSMemory
Instruction{0x42, 0x80, 0xd2, 0x94}, Instruction{0x42, 0x80, 0xd2, 0x94},
}.toBytes(), }.Bytes(),
},
}, },
} }

View File

@ -1,152 +0,0 @@
package main
// uint24 returns
func uint24(num uint32) [3]byte {
if num > 0x00FFFFFF {
panic("invalid uint24 passed")
}
result := fourByte(num)
return [3]byte{result[1], result[2], result[3]}
}
// Instruction represents a 4-byte PowerPC instruction.
type Instruction [4]byte
// Instructions represents a group of PowerPC instructions.
type Instructions []Instruction
// toBytes returns the bytes necessary to represent these instructions.
func (i Instructions) toBytes() []byte {
var contents []byte
for _, instruction := range i {
contents = append(contents, instruction[:]...)
}
return contents
}
// padding is not an actual instruction - it represents 4 zeros.
var padding Instruction = [4]byte{0x00, 0x00, 0x00, 0x00}
// BLR represents the blr mnemonic on PowerPC.
func BLR() Instruction {
return [4]byte{0x4E, 0x80, 0x00, 0x20}
}
// CRXOR represents a common use of CRXOR on PowerPC.
// TODO: actually implement
func CRXOR() Instruction {
return [4]byte{0x4c, 0xc6, 0x31, 0x82}
}
// ADDI represents the addi PowerPC instruction.
func ADDI(rT Register, rA Register, value uint16) Instruction {
return EncodeInstrDForm(14, rT, rA, value)
}
// LI represents the li mnemonic on PowerPC.
func LI(rT Register, value uint16) Instruction {
return ADDI(rT, 0, value)
}
// SUBI represents the subi mnemonic on PowerPC.
// TODO: handle negative values properly?
func SUBI(rT Register, rA Register, value uint16) Instruction {
return ADDI(rT, 0, -value)
}
// ADDIS represents the addis PowerPC instruction.
func ADDIS(rT Register, rA Register, value uint16) Instruction {
return EncodeInstrDForm(15, rT, rA, value)
}
// LIS represents the lis mnemonic on PowerPC.
func LIS(rT Register, value uint16) Instruction {
return ADDIS(rT, 0, value)
}
// ORI represents the ori PowerPC instruction.
func ORI(rS Register, rA Register, value uint16) Instruction {
return EncodeInstrDForm(24, rS, rA, value)
}
// STH represents the sth PowerPC instruction.
func STH(rS Register, offset uint16, rA Register) Instruction {
return EncodeInstrDForm(44, rS, rA, offset)
}
// EIEIO represents the eieio PowerPC instruction.
func EIEIO() Instruction {
return [4]byte{0x7C, 0x00, 0x06, 0xAC}
}
// STW represents the stw PowerPC instruction.
func STW(rS Register, offset uint16, rA Register) Instruction {
return EncodeInstrDForm(36, rS, rA, offset)
}
// LWZ represents the lwz PowerPC instruction.
func LWZ(rT Register, offset uint16, rA Register) Instruction {
return EncodeInstrDForm(32, rT, rA, offset)
}
// NOP represents the nop mnemonic for PowerPC.
func NOP() Instruction {
return ORI(R0, R0, 0)
}
// CMPWI represents the cmpwi mnemonic for PowerPC.
// It does not support any other CR fields asides from 0.
func CMPWI(rA Register, value uint16) Instruction {
return EncodeInstrDForm(11, 0, rA, value)
}
// SYNC is a hack, hardcoding sync 0.
// TODO(spotlightishere): actually encode this
func SYNC() Instruction {
return [4]byte{0x7c, 0x00, 0x04, 0xac}
}
// MTSPR is a hack, hardcoding LR, r0.
// TODO(spotlightishere): actually encode this
func MTSPR() Instruction {
return [4]byte{0x7c, 0x08, 0x03, 0xa6}
}
// MFSPR is a hack, hardcoding r0, LR.
// TODO(spotlightishere): actually encode this
func MFSPR() Instruction {
return [4]byte{0x7c, 0x08, 0x02, 0xa6}
}
// STWU represents the stwu PowerPC instruction.
func STWU(rS Register, rA Register, offset uint16) Instruction {
return EncodeInstrDForm(37, rS, rA, offset)
}
// calcDestination determines the proper offset from a given
// calling address and target address.
func calcDestination(from uint, target uint) [3]byte {
// TODO(spotlightishere): Handle negative offsets properly
offset := target - from
// Sign-extend by two bytes
calc := uint32(offset >> 2)
return uint24(calc)
}
// BL represents the bl PowerPC instruction.
// It calculates the offset from the given current address and the given
// target address, saving the current address in the link register. It then branches.
func BL(current uint, target uint) Instruction {
return EncodeInstrIForm(18, calcDestination(current, target), false, true)
}
// B represents the b PowerPC instruction.
// It calculates the offset from the given current address
// and the given target address, and then branches.
func B(current uint, target uint) Instruction {
return EncodeInstrIForm(18, calcDestination(current, target), false, false)
}

View File

@ -1,160 +0,0 @@
package main
import (
"encoding/binary"
)
// Register represents a value for a PowerPC register.
type Register byte
const (
R0 = iota
R1
R2
R3
R4
R5
R6
R7
R8
R9
R10
R11
R12
R13
R14
R15
R16
R17
R18
R19
R20
R21
R22
R23
R24
R25
R26
R27
R28
R29
R30
R31
)
// Bits represents bits for a byte.
// If set, considered as 1. If not, 0.
type Bits [8]bool
// getBits returns a usable array of bits for the given byte.
func getBits(in byte) Bits {
return [8]bool{
(in>>7)&1 == 1,
(in>>6)&1 == 1,
(in>>5)&1 == 1,
(in>>4)&1 == 1,
(in>>3)&1 == 1,
(in>>2)&1 == 1,
(in>>1)&1 == 1,
in&1 == 1,
}
}
// getByte returns the byte represented by these bits.
func (b Bits) getByte() byte {
var result byte
for idx, truthy := range b {
if truthy {
result |= 1 << (7 - idx)
}
}
return result
}
// EncodeInstrDForm handles encoding a given opcode, RT, RA and SI.
// D-form assumes:
// - 6 bits for the opcode
// - 5 for rT
// - 5 for rA
// - 16 for SI
func EncodeInstrDForm(opcode byte, rT Register, rA Register, value uint16) Instruction {
var instr [2]Bits
opBits := getBits(opcode)
rTBits := getBits(byte(rT))
rABits := getBits(byte(rA))
instr[0] = Bits{
// We need the upper six bits for our opcode.
opBits[2],
opBits[3],
opBits[4],
opBits[5],
opBits[6],
opBits[7],
// Next, the lower two bits for rT.
rTBits[3],
rTBits[4],
}
instr[1] = Bits{
// Third, the lower three bits for rT.
rTBits[5],
rTBits[6],
rTBits[7],
// Finally, all five lowest bits for rA.
rABits[3],
rABits[4],
rABits[5],
rABits[6],
rABits[7],
}
firstInstr := instr[0].getByte()
secondInstr := instr[1].getByte()
valByte := twoByte(value)
return Instruction{firstInstr, secondInstr, valByte[0], valByte[1]}
}
// EncodeInstrIForm handles encoding a given opcode, LI, AA and LK.
// I-form assumes:
// - 6 bits for the opcode
// - 24 bits for LI
// - 1 bit for absolute (AA)
// - 1 bit for should store in link register (LK)
func EncodeInstrIForm(opcode byte, LI [3]byte, AA bool, LK bool) Instruction {
opBits := getBits(opcode)
liOne := getBits(LI[0])
liTwo := getBits(LI[1])
liThree := getBits(LI[2])
instr := [4]Bits{
{
// We need the upper six bits for our opcode.
opBits[2], opBits[3], opBits[4], opBits[5], opBits[6], opBits[7],
// Otherwise, copy LI as-is.
liOne[0], liOne[1],
},
{
liOne[2], liOne[3], liOne[4], liOne[5], liOne[6], liOne[7], liTwo[0], liTwo[1],
},
{
liTwo[2], liTwo[3], liTwo[4], liTwo[5], liTwo[6], liTwo[7], liThree[0], liThree[1],
},
{
liThree[2], liThree[3], liThree[4], liThree[5], liThree[6], liThree[7],
// Copy AA and LK as-is.
AA,
LK,
},
}
return Instruction{instr[0].getByte(), instr[1].getByte(), instr[2].getByte(), instr[3].getByte()}
}
// twoByte converts a uint16 to two big-endian bytes.
func twoByte(passed uint16) [2]byte {
result := make([]byte, 2)
binary.BigEndian.PutUint16(result, passed)
return [2]byte{result[0], result[1]}
}