mirror of
https://wiilab.wiimart.org/wiimart/WiiMart-Patcher
synced 2025-09-03 20:11:19 +02:00
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:
parent
4a232c552b
commit
e17dbf9ce1
1
go.mod
1
go.mod
@ -6,5 +6,6 @@ require (
|
||||
github.com/logrusorgru/aurora/v3 v3.0.0
|
||||
github.com/wii-tools/GoNUSD v0.2.2
|
||||
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
|
||||
)
|
||||
|
2
go.sum
2
go.sum
@ -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/arclib v1.0.0 h1:OAmbL3NDUmlR0wa1VpJhxnKiIRFZz1CC40lTVPUm8Ms=
|
||||
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/go.mod h1:GK+f2POk+rVu1p4xqLSb4ll1SKKbfOO6ZAB+oPLV3uQ=
|
||||
|
17
main.go
17
main.go
@ -6,6 +6,7 @@ import (
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"github.com/wii-tools/GoNUSD"
|
||||
"github.com/wii-tools/arclib"
|
||||
"github.com/wii-tools/powerpc"
|
||||
"github.com/wii-tools/wadlib"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
@ -137,6 +138,22 @@ func main() {
|
||||
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.
|
||||
func check(err error) {
|
||||
if err != nil {
|
||||
|
@ -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)
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
package main
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"strings"
|
||||
|
||||
. "github.com/wii-tools/powerpc"
|
||||
)
|
||||
|
||||
const (
|
||||
NintendoBaseDomain = "shop.wii.com"
|
||||
@ -15,39 +19,42 @@ const (
|
||||
// See docs/patch_base_domain.md for more information.
|
||||
func PatchBaseDomain() PatchSet {
|
||||
return PatchSet{
|
||||
Patch{
|
||||
Name: "Change Base Domain",
|
||||
Patches: []Patch{
|
||||
{
|
||||
Name: "Modify /startup domain",
|
||||
|
||||
Before: []byte(ShowManualURL),
|
||||
After: padReplace(ShowManualURL),
|
||||
},
|
||||
Patch{
|
||||
{
|
||||
Name: "Modify oss-auth URL",
|
||||
AtOffset: 3180692,
|
||||
|
||||
Before: []byte(GetLogURL),
|
||||
After: padReplace(GetLogURL),
|
||||
},
|
||||
Patch{
|
||||
{
|
||||
Name: "Modify trusted base domain prefix",
|
||||
AtOffset: 3323432,
|
||||
|
||||
Before: []byte(TrustedDomain),
|
||||
After: padReplace(TrustedDomain),
|
||||
},
|
||||
Patch{
|
||||
{
|
||||
Name: "Modify ECS SOAP endpoint URL",
|
||||
AtOffset: 3268896,
|
||||
|
||||
Before: []byte(ECommerceBaseURL),
|
||||
After: padReplace(ECommerceBaseURL),
|
||||
},
|
||||
Patch{
|
||||
{
|
||||
Name: "Wildcard replace other instances",
|
||||
|
||||
Before: []byte(NintendoBaseDomain),
|
||||
After: padReplace(baseDomain),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,5 +67,5 @@ func padReplace(url string) []byte {
|
||||
}
|
||||
|
||||
padding := len(url) - len(replaced)
|
||||
return append([]byte(replaced), emptyBytes(padding)...)
|
||||
return append([]byte(replaced), EmptyBytes(padding)...)
|
||||
}
|
||||
|
@ -1,19 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
. "github.com/wii-tools/powerpc"
|
||||
)
|
||||
|
||||
// LoadCustomCA loads our custom certificate, either generated or loaded,
|
||||
// into the IOS trust store for EC usage.
|
||||
// It is assumed that rootCertificate has been loaded upon invoking this patchset.
|
||||
// See docs/patch_custom_ca_ios.md for more information.
|
||||
func LoadCustomCA() PatchSet {
|
||||
return PatchSet{
|
||||
Patch{
|
||||
Name: "Load Custom CA within IOS",
|
||||
Patches: []Patch{
|
||||
{
|
||||
Name: "Insert custom CA into free space",
|
||||
AtOffset: 3037368,
|
||||
|
||||
Before: emptyBytes(len(rootCertificate)),
|
||||
Before: EmptyBytes(len(rootCertificate)),
|
||||
After: rootCertificate,
|
||||
},
|
||||
Patch{
|
||||
{
|
||||
Name: "Modify NHTTPi_SocSSLConnect to load cert",
|
||||
AtOffset: 644624,
|
||||
|
||||
@ -67,7 +73,7 @@ func LoadCustomCA() PatchSet {
|
||||
LI(R3, 0xfc14),
|
||||
// b FUNCTION_PROLOG
|
||||
B(0x800acb10, 0x800acbb0),
|
||||
}.toBytes(),
|
||||
}.Bytes(),
|
||||
After: Instructions{
|
||||
// Our certificate is present at 0x802e97b8.
|
||||
// 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(), NOP(), NOP(), NOP(), NOP(), NOP(), NOP(),
|
||||
}.toBytes(),
|
||||
}.Bytes(),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
. "github.com/wii-tools/powerpc"
|
||||
)
|
||||
|
||||
var PatchECCfgPath = PatchSet{
|
||||
Patch{
|
||||
Name: "Change EC Configuration Path",
|
||||
Patches: []Patch{
|
||||
{
|
||||
AtOffset: 3319968,
|
||||
|
||||
Before: []byte("ec.cfg\x00\x00"),
|
||||
After: []byte("osc.cfg\x00"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1,6 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
. "github.com/wii-tools/powerpc"
|
||||
)
|
||||
|
||||
var NegateECTitle = PatchSet{
|
||||
Name: "Negate EC Title Check",
|
||||
|
||||
Patches: []Patch{
|
||||
Patch{
|
||||
Name: "Permit downloading all titles",
|
||||
AtOffset: 619648,
|
||||
@ -9,13 +16,13 @@ var NegateECTitle = PatchSet{
|
||||
Before: Instructions{
|
||||
STWU(R1, R1, 0xffe0),
|
||||
MFSPR(),
|
||||
}.toBytes(),
|
||||
}.Bytes(),
|
||||
|
||||
// Immediately return true
|
||||
After: Instructions{
|
||||
LI(R3, 1),
|
||||
BLR(),
|
||||
}.toBytes(),
|
||||
}.Bytes(),
|
||||
},
|
||||
Patch{
|
||||
Name: "Mark all titles as managed",
|
||||
@ -24,11 +31,11 @@ var NegateECTitle = PatchSet{
|
||||
Before: Instructions{
|
||||
STWU(R1, R1, 0xfff0),
|
||||
MFSPR(),
|
||||
}.toBytes(),
|
||||
}.Bytes(),
|
||||
After: Instructions{
|
||||
LI(R3, 1),
|
||||
BLR(),
|
||||
}.toBytes(),
|
||||
}.Bytes(),
|
||||
},
|
||||
Patch{
|
||||
Name: "Mark all tickets as managed",
|
||||
@ -36,11 +43,11 @@ var NegateECTitle = PatchSet{
|
||||
Before: Instructions{
|
||||
STWU(R1, R1, 0xfff0),
|
||||
MFSPR(),
|
||||
}.toBytes(),
|
||||
}.Bytes(),
|
||||
After: Instructions{
|
||||
LI(R3, 1),
|
||||
BLR(),
|
||||
}.toBytes(),
|
||||
}.Bytes(),
|
||||
},
|
||||
Patch{
|
||||
Name: "Nullify ec::removeAllTitles",
|
||||
@ -48,10 +55,11 @@ var NegateECTitle = PatchSet{
|
||||
Before: Instructions{
|
||||
STWU(R1, R1, 0xffc0),
|
||||
MFSPR(),
|
||||
}.toBytes(),
|
||||
}.Bytes(),
|
||||
After: Instructions{
|
||||
LI(R3, 0),
|
||||
BLR(),
|
||||
}.toBytes(),
|
||||
}.Bytes(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1,9 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
. "github.com/wii-tools/powerpc"
|
||||
)
|
||||
|
||||
// OverwriteIOSPatch effectively nullifies IOSC_VerifyPublicKeySign.
|
||||
// See docs/patch_overwrite_ios.md for more information.
|
||||
var OverwriteIOSPatch = PatchSet{
|
||||
Patch{
|
||||
Name: "Overwrite IOS Syscall for ES",
|
||||
Patches: []Patch{
|
||||
{
|
||||
Name: "Clear extraneous functions",
|
||||
AtOffset: 20272,
|
||||
|
||||
@ -29,30 +35,30 @@ var OverwriteIOSPatch = PatchSet{
|
||||
// b printf
|
||||
Instruction{0x48, 0x2a, 0x8b, 0x5c},
|
||||
|
||||
padding,
|
||||
Padding,
|
||||
|
||||
// Function: textinput::EventObserver::onSE
|
||||
BLR(),
|
||||
padding, padding, padding,
|
||||
Padding, Padding, Padding,
|
||||
// Function: textinput::EventObserver::onEvent
|
||||
BLR(),
|
||||
padding, padding, padding,
|
||||
Padding, Padding, Padding,
|
||||
// Function: textinput::EventObserver::onCommand
|
||||
BLR(),
|
||||
padding, padding, padding,
|
||||
Padding, Padding, Padding,
|
||||
// Function: textinput::EventObserver::onInput
|
||||
BLR(),
|
||||
padding, padding, padding,
|
||||
}.toBytes(),
|
||||
Padding, Padding, Padding,
|
||||
}.Bytes(),
|
||||
|
||||
// We wish to clear extraneous blrs so that our custom overwriteIOSMemory
|
||||
// function does not somehow conflict.
|
||||
// We only preserve onSE, which is this immediate BLR.
|
||||
After: append(Instructions{
|
||||
BLR(),
|
||||
}.toBytes(), emptyBytes(108)...),
|
||||
}.Bytes(), EmptyBytes(108)...),
|
||||
},
|
||||
Patch{
|
||||
{
|
||||
Name: "Repair textinput::EventObserver vtable",
|
||||
AtOffset: 3095452,
|
||||
|
||||
@ -76,7 +82,7 @@ var OverwriteIOSPatch = PatchSet{
|
||||
0x80, 0x01, 0x43, 0xf0,
|
||||
},
|
||||
},
|
||||
Patch{
|
||||
{
|
||||
Name: "Repair ipl::keyboard::EventObserver vtable",
|
||||
AtOffset: 3097888,
|
||||
|
||||
@ -99,11 +105,11 @@ var OverwriteIOSPatch = PatchSet{
|
||||
0x80, 0x01, 0x43, 0xf0, // textinput::EventObserver::doNothing
|
||||
},
|
||||
},
|
||||
Patch{
|
||||
{
|
||||
Name: "Insert patch table",
|
||||
AtOffset: 3205088,
|
||||
|
||||
Before: emptyBytes(52),
|
||||
Before: EmptyBytes(52),
|
||||
After: []byte{
|
||||
//////////////
|
||||
// PATCH #1 //
|
||||
@ -176,13 +182,13 @@ var OverwriteIOSPatch = PatchSet{
|
||||
0x93, 0x00, 0x01, 0xff,
|
||||
},
|
||||
},
|
||||
Patch{
|
||||
{
|
||||
Name: "Insert overwriteIOSMemory",
|
||||
AtOffset: 20276,
|
||||
|
||||
// This area should be cleared in the patch
|
||||
// "Clear extraneous functions".
|
||||
Before: emptyBytes(108),
|
||||
Before: EmptyBytes(108),
|
||||
After: Instructions{
|
||||
// Our patch table is available at 0x803126e0.
|
||||
LIS(R8, 0x8031),
|
||||
@ -248,28 +254,29 @@ var OverwriteIOSPatch = PatchSet{
|
||||
|
||||
// We're finished patching!
|
||||
BLR(),
|
||||
}.toBytes(),
|
||||
}.Bytes(),
|
||||
},
|
||||
Patch{
|
||||
{
|
||||
Name: "Do not require input for exception handler",
|
||||
AtOffset: 32032,
|
||||
Before: Instructions{
|
||||
STWU(R1, R1, 0xFC10),
|
||||
}.toBytes(),
|
||||
}.Bytes(),
|
||||
After: Instructions{
|
||||
BLR(),
|
||||
}.toBytes(),
|
||||
}.Bytes(),
|
||||
},
|
||||
Patch{
|
||||
{
|
||||
Name: "Modify ipl::Exception::__ct",
|
||||
AtOffset: 31904,
|
||||
|
||||
Before: Instructions{
|
||||
BLR(),
|
||||
}.toBytes(),
|
||||
}.Bytes(),
|
||||
After: Instructions{
|
||||
// b overwriteIOSMemory
|
||||
Instruction{0x42, 0x80, 0xd2, 0x94},
|
||||
}.toBytes(),
|
||||
}.Bytes(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
152
powerpc.go
152
powerpc.go
@ -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)
|
||||
}
|
@ -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]}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user