diff --git a/ms213x_patch/asm/asm.sh b/ms213x_patch/asm/asm.sh new file mode 100755 index 0000000..f024547 --- /dev/null +++ b/ms213x_patch/asm/asm.sh @@ -0,0 +1,5 @@ +as31 -Fbin init.asm +as31 -Fbin hook.asm +as31 -Fbin finishf660.asm +as31 -Fbin finishsig.asm +as31 -Fbin readinfo.asm diff --git a/ms213x_patch/asm/finishf660.asm b/ms213x_patch/asm/finishf660.asm new file mode 100644 index 0000000..c0a750c --- /dev/null +++ b/ms213x_patch/asm/finishf660.asm @@ -0,0 +1,36 @@ +INC DPTR +MOVX @DPTR, A + +MOV DPTR, #0x7b10 +MOVX A, @DPTR +MOV R7, A + +INC DPTR +MOVX A, @DPTR +MOV R6, A + +INC DPTR +MOVX A, @DPTR +MOV R5, A + +INC DPTR +MOVX A, @DPTR +MOV R4, A + +MOV DPTR, #0xf660 +MOV A, R7 +MOVX @DPTR, A + +INC DPTR +MOV A, R6 +MOVX @DPTR, A + +INC DPTR +MOV A, R5 +MOVX @DPTR, A + +INC DPTR +MOV A, R4 +MOVX @DPTR, A + +RET diff --git a/ms213x_patch/asm/finishf660.bin b/ms213x_patch/asm/finishf660.bin new file mode 100644 index 0000000..fa49133 --- /dev/null +++ b/ms213x_patch/asm/finishf660.bin @@ -0,0 +1 @@ +{`" \ No newline at end of file diff --git a/ms213x_patch/asm/finishsig.asm b/ms213x_patch/asm/finishsig.asm new file mode 100644 index 0000000..9e915d6 --- /dev/null +++ b/ms213x_patch/asm/finishsig.asm @@ -0,0 +1,7 @@ +ORL A, R7 + +MOV DPTR, #0x7b14 +MOVX @DPTR, A + +MOV R7, A +RET diff --git a/ms213x_patch/asm/finishsig.bin b/ms213x_patch/asm/finishsig.bin new file mode 100644 index 0000000..af15e55 --- /dev/null +++ b/ms213x_patch/asm/finishsig.bin @@ -0,0 +1 @@ +O{" \ No newline at end of file diff --git a/ms213x_patch/asm/hook.asm b/ms213x_patch/asm/hook.asm new file mode 100644 index 0000000..a93a3ae --- /dev/null +++ b/ms213x_patch/asm/hook.asm @@ -0,0 +1,82 @@ +hookRun: + MOV DPTR, #0x12b3 + + MOVX A, @DPTR + XRL A, R0 + JNZ hookRet + + LCALL hookWork + + MOV DPTR, #0x12b3+1 + MOVX @DPTR, A + INC DPTR + MOV A, R2 + MOVX @DPTR, A + INC DPTR + MOV A, R3 + MOVX @DPTR, A + INC DPTR + MOV A, R4 + MOVX @DPTR, A + INC DPTR + MOV A, R5 + MOVX @DPTR, A + INC DPTR + MOV A, R6 + MOVX @DPTR, A + INC DPTR + MOV A, R7 + MOVX @DPTR, A + MOV DPTR, #0x12b3 + MOV A, #0xFF + RLC A + MOVX @DPTR, A + + SETB EA + +hookRet: + RET + +hookWork: + CLR EA + + MOV DPTR, #0x12b3+1 + MOVX A, @DPTR + MOV 1, A + + INC DPTR + MOVX A, @DPTR + MOV 0, A + + PUSH 0 + PUSH 1 + + INC DPTR + MOVX A, @DPTR + MOV R3, A + + INC DPTR + MOVX A, @DPTR + MOV R4, A + + INC DPTR + MOVX A, @DPTR + MOV R5, A + + INC DPTR + MOVX A, @DPTR + MOV R6, A + + INC DPTR + MOVX A, @DPTR + MOV R7, A + + RRC A + MOV A, R7 + + MOV DPH, R3 + MOV DPL, R4 + + RET ;Call address in HID+1 + + diff --git a/ms213x_patch/asm/hook.bin b/ms213x_patch/asm/hook.bin new file mode 100644 index 0000000..1eac88f Binary files /dev/null and b/ms213x_patch/asm/hook.bin differ diff --git a/ms213x_patch/asm/init.asm b/ms213x_patch/asm/init.asm new file mode 100644 index 0000000..5ec4645 --- /dev/null +++ b/ms213x_patch/asm/init.asm @@ -0,0 +1,18 @@ +; Tell host about the existence of this patch + +MOV DPTR, #0x7B00 +MOV A, #'B' +MOVX @DPTR, A +INC DPTR +MOV A, #'V' +MOVX @DPTR, A +INC DPTR +MOV A, #'D' +MOVX @DPTR, A +INC DPTR +MOV A, #'B' +MOVX @DPTR, A +INC DPTR +CLR A +MOVX @DPTR, A +RET diff --git a/ms213x_patch/asm/init.bin b/ms213x_patch/asm/init.bin new file mode 100644 index 0000000..f5b43d2 Binary files /dev/null and b/ms213x_patch/asm/init.bin differ diff --git a/ms213x_patch/asm/readinfo.asm b/ms213x_patch/asm/readinfo.asm new file mode 100644 index 0000000..c7f2956 --- /dev/null +++ b/ms213x_patch/asm/readinfo.asm @@ -0,0 +1,26 @@ +; Resolution info +MOV DPTR, #0x7b10 +MOVX A, @DPTR +MOV R2, A + +INC DPTR +MOVX A, @DPTR +MOV R3, A + +INC DPTR +MOVX A, @DPTR +MOV R4, A + +INC DPTR +MOVX A, @DPTR +MOV R5, A + +; Signal info +INC DPTR +MOVX A, @DPTR + +; Frame counter +MOV R6, 0x29 +MOV R7, 0x28 + +RET diff --git a/ms213x_patch/asm/readinfo.bin b/ms213x_patch/asm/readinfo.bin new file mode 100644 index 0000000..13682ec --- /dev/null +++ b/ms213x_patch/asm/readinfo.bin @@ -0,0 +1 @@ +{)(" \ No newline at end of file diff --git a/ms213x_patch/main.go b/ms213x_patch/main.go new file mode 100644 index 0000000..f1fac51 --- /dev/null +++ b/ms213x_patch/main.go @@ -0,0 +1,48 @@ +package main + +import ( + "encoding/binary" + "flag" + "log" + "os" + + "github.com/BertoldVdb/ms-tools/mshal/ms213x" +) + +func main() { + input := flag.String("input", "", "Input filename") + output := flag.String("output", "/tmp/modified.bin", "Output filename") + flag.Parse() + + in, err := os.ReadFile(*input) + if err != nil { + log.Fatalln("Failed to open file:", err) + } + + if err := ms213x.CheckImage(in); err != nil { + log.Fatalln("Failed to parse image:", in) + } + + clen := binary.BigEndian.Uint16(in[2:]) + code := in[0x30:] + code = code[:clen] + + code, err = patch(code) + if err != nil { + log.Fatalln("Failed to patch code:", err) + } + + out := make([]byte, 0x30, 0x30+len(code)+4) + copy(out, in) + binary.BigEndian.PutUint16(out[2:], uint16(len(code))) + + out = append(out, code...) + out = binary.BigEndian.AppendUint32(out, 0) + ms213x.FixImage(out) + + os.WriteFile("/tmp/modcode.bin", code, 0644) + + if err := os.WriteFile(*output, out, 0644); err != nil { + log.Fatalln("Failed to write output:", err) + } +} diff --git a/ms213x_patch/ms213x_patch b/ms213x_patch/ms213x_patch new file mode 100755 index 0000000..55f0ce8 Binary files /dev/null and b/ms213x_patch/ms213x_patch differ diff --git a/ms213x_patch/patch.go b/ms213x_patch/patch.go new file mode 100644 index 0000000..4f5eade --- /dev/null +++ b/ms213x_patch/patch.go @@ -0,0 +1,122 @@ +package main + +import ( + "crypto/sha256" + "encoding/binary" + "encoding/hex" + "fmt" + "log" + + _ "embed" +) + +type patcher struct { + image []byte + + hookOffset uint16 +} + +//go:embed asm/init.bin +var patchInit []byte + +//go:embed asm/hook.bin +var patchHook []byte + +//go:embed asm/finishf660.bin +var patchFinishf660 []byte + +//go:embed asm/finishsig.bin +var patchFinishSig []byte + +//go:embed asm/readinfo.bin +var patchReadInfo []byte + +func (p *patcher) addCode(code []byte) uint16 { + offs := len(p.image) + p.image = append(p.image, code...) + return uint16(offs) +} + +func (p *patcher) detourCall(offset uint16, dest uint16) { + a := p.image[offset] + if a != 0x02 && a != 0x12 { + panic("Not LJMP or LCALL") + } + + var code [6]byte + code[0] = 0x12 /* LCALL */ + binary.BigEndian.PutUint16(code[1:], dest) + + code[3] = 0x02 /* LJMP */ + copy(code[4:], p.image[offset+1:]) /* Copy original address */ + + binary.BigEndian.PutUint16(p.image[offset+1:], p.addCode(code[:])) +} + +func (p *patcher) createHook(cmd uint8) uint16 { + return p.addCode([]byte{ + 0x78, cmd, + 0x02, byte(p.hookOffset >> 8), byte(p.hookOffset), + }) +} + +func (p *patcher) replaceJump(offset, dest uint16) { + p.image[offset] = 0x02 + binary.BigEndian.PutUint16(p.image[offset+1:], dest) +} + +func patch(in []byte) ([]byte, error) { + /* Check if it is a file we know how to handle */ + hash := sha256.Sum256(in) + hashStr := hex.EncodeToString(hash[:]) + if hashStr != "cc67f79a043da85dc8e6688a22111ade626e519e1ab549f110b3a06308190047" { + return nil, fmt.Errorf("code hash %s is not supported", hashStr) + } + + p := patcher{ + image: in, + } + + /* Add init code */ + p.detourCall(0x4d48, p.addCode(patchInit)) + + /* Add hook code and update its internal jump */ + p.hookOffset = p.addCode(patchHook) + binary.BigEndian.PutUint16(p.image[p.hookOffset+8:], binary.BigEndian.Uint16(p.image[p.hookOffset+8:])+p.hookOffset) + + /* Add hook entry to main loop */ + p.detourCall(0x4d70, p.createHook(0xEF)) /* Not in IRQ, but handled the same as the other USB commands */ + + /* Add 0xEE, remove result codes 0xFF and 0xFE which are legitimate commands (function unclear, can still call them + * via hook if needed) */ + table, dflt := p.jumptableParse(0x1d9c) + var newTable []jumptableEntry + needInsert := true + for _, b := range table { + if b.Key > 0xEE && needInsert { + needInsert = false + newTable = append(newTable, jumptableEntry{ + Key: 0xEE, + Address: p.createHook(0xEE), + }) + } + if b.Key >= 0xFE { + break + } + newTable = append(newTable, b) + } + p.jumptableWrite(0x1d9c, newTable, dflt) + + /* Write 0xF660 also to safe place (0x7b10) */ + binary.BigEndian.PutUint16(p.image[0xbbb3:], 0x7b10) + binary.BigEndian.PutUint16(p.image[0xbbbf:], 0x7b12) + p.replaceJump(0xbbc3, p.addCode(patchFinishf660)) + + /* Write signal info to safe place (0x7b14) */ + p.replaceJump(0xe9c6, p.addCode(patchFinishSig)) + + /* Finally, add read results function */ + log.Printf("ReadInfo Offset: %02x", p.addCode(patchReadInfo)) + + return p.image, nil +} diff --git a/ms213x_patch/patch_jumptable.go b/ms213x_patch/patch_jumptable.go new file mode 100644 index 0000000..1cdaccc --- /dev/null +++ b/ms213x_patch/patch_jumptable.go @@ -0,0 +1,44 @@ +package main + +import "encoding/binary" + +type jumptableEntry struct { + Key uint8 + Address uint16 +} + +func (p *patcher) jumptableParse(offset uint16) ([]jumptableEntry, uint16) { + var results []jumptableEntry + + lastKey := -1 + for { + addr := binary.BigEndian.Uint16(p.image[offset:]) + offset += 2 + if addr > 0 { + key := int(p.image[offset]) + offset++ + + if key < lastKey { + panic("Parse error") + } + lastKey = key + + results = append(results, jumptableEntry{ + Key: uint8(key), + Address: addr, + }) + } else { + return results, binary.BigEndian.Uint16(p.image[offset:]) + } + } +} + +func (p *patcher) jumptableWrite(offset uint16, entries []jumptableEntry, dflt uint16) { + for _, m := range entries { + binary.BigEndian.PutUint16(p.image[offset:], m.Address) + p.image[offset+2] = m.Key + offset += 3 + } + binary.BigEndian.PutUint16(p.image[offset:], 0) + binary.BigEndian.PutUint16(p.image[offset+2:], dflt) +}