Преглед на файлове

code reorg, cleanup, turd polishing

Keelan Lightfoot преди 1 година
родител
ревизия
278f798e65
променени са 13 файла, в които са добавени 1456 реда и са изтрити 757 реда
  1. 198
    0
      cmd/convertfw/convertfw.go
  2. 208
    0
      cmd/convertx/convertx.go
  3. BIN
      cmd/convertx/foo.bin
  4. 0
    168
      cmd/convertx/main.go
  5. BIN
      cmd/loader/loader
  6. 7
    3
      cmd/loader/loader.go
  7. 77
    0
      cmd/romknife/romknife.go
  8. 155
    55
      dotx/dotx.go
  9. 261
    193
      monitor/monitor.go
  10. 0
    153
      monitor/protocol.go
  11. 402
    0
      monitor/receiver.go
  12. 0
    185
      monitor/serial.go
  13. 148
    0
      stfw/stfw.go

+ 198
- 0
cmd/convertfw/convertfw.go Целия файл

@@ -0,0 +1,198 @@
1
+package main
2
+
3
+import (
4
+	"code.beefchicken.com/trimtools/stfw"
5
+	"flag"
6
+	"fmt"
7
+	"io"
8
+	"os"
9
+	"path/filepath"
10
+)
11
+
12
+func main() {
13
+
14
+	flag.Parse()
15
+
16
+	if len(flag.Args()) != 2 {
17
+		fmt.Printf("Usage:\n")
18
+		fmt.Printf("    convertfw <input>.FW{1,2,3} <output prefix>\n")
19
+		fmt.Printf("    convertfw <input>.bin{1,2,3} <output prefix>\n")
20
+		os.Exit(1)
21
+	}
22
+
23
+	in := flag.Arg(0)
24
+	out := flag.Arg(1)
25
+
26
+	switch filepath.Ext(in) {
27
+	case ".FW1", ".FW2", ".FW3":
28
+		fw2bin(in, out)
29
+	case ".bin1", ".bin2", ".bin3":
30
+		bin2fw(in, out)
31
+	default:
32
+		die("Input file must have .bin{1,2,3} or .FW{1,2,3} extension. Aborting.")
33
+	}
34
+}
35
+
36
+func bin2fw(binPath, fwPath string) {
37
+
38
+	var area int
39
+	var baseAddr uint32
40
+	var startOffset uint32
41
+
42
+	switch filepath.Ext(binPath) {
43
+	case ".bin1":
44
+		area = 1
45
+		baseAddr = 0x24300000
46
+		startOffset = 0x200
47
+	case ".bin2":
48
+		area = 2
49
+		baseAddr = 0x24340000
50
+	case ".bin3":
51
+		area = 3
52
+		baseAddr = 0x24380000
53
+	default:
54
+		die("Input file must have .bin1, .bin2 or .bin3 extension. Aborting.")
55
+	}
56
+
57
+	fwPathExt := fmt.Sprintf("%s.FW%d", fwPath, area)
58
+
59
+	fmt.Printf("Loading binary data from '%s'...\n", filepath.Base(binPath))
60
+
61
+	mem, err := os.ReadFile(binPath)
62
+	if err != nil {
63
+		die("Unable to read binary file '%s': %v. Aborting.\n", os.Args[1], err)
64
+	}
65
+
66
+	if len(mem)%32 != 0 {
67
+		die("Binary must be a multiple of 32 bytes long. Aborting.")
68
+	}
69
+
70
+	fmt.Printf("Loaded successfully.\n")
71
+
72
+	fmt.Printf("Verifying checksums...\n")
73
+
74
+	scsl, scsh := stfw.GetChecksums(mem)
75
+	ccsl, ccsh := stfw.CalcChecksums(mem, int(startOffset>>1))
76
+
77
+	if scsl != ccsl {
78
+		fmt.Printf("Low ROM Checksum mismatch: Expected 0x%02X, Found 0x%02X. Updating.", scsl, ccsl)
79
+		stfw.UpdateChecksum(mem, stfw.MemoryModeCombinedLow, ccsl)
80
+	}
81
+
82
+	if scsh != ccsh {
83
+		fmt.Printf("High ROM Checksum mismatch: Expected 0x%02X, Found 0x%02X. Updating.", scsh, ccsh)
84
+		stfw.UpdateChecksum(mem, stfw.MemoryModeCombinedHigh, ccsh)
85
+	}
86
+
87
+	fmt.Printf("Writing image to '%s'...\n", filepath.Base(fwPathExt))
88
+
89
+	f, err := os.Create(fwPathExt)
90
+	if err != nil {
91
+		die("Unable to create fw file '%s': %v. Aborting.\n", fwPathExt, err)
92
+	}
93
+
94
+	if area == 1 {
95
+		fmt.Printf("Writing header for area 1 image...\n")
96
+
97
+		// write an empty serial number table. It appears TFUPD.EXE doesn't check
98
+		// this.
99
+		if _, err := f.Write(make([]byte, 0x100)); err != nil {
100
+			die("Error writing header: %v. Aborting.\n", err)
101
+		}
102
+	}
103
+
104
+	fmt.Printf("Writing records...\n")
105
+
106
+	for i := startOffset; i < uint32(len(mem)); i += 32 {
107
+		err := stfw.WriteRecord(f, &stfw.Record{
108
+			Addr: baseAddr + i,
109
+			Data: mem[i : i+32],
110
+		})
111
+		if err != nil {
112
+			die("Error writing record to file: %v. Aborting.\n", err)
113
+		}
114
+	}
115
+
116
+	fmt.Printf("Write complete.\n")
117
+	fmt.Printf("Records: %d\n", (len(mem)-int(startOffset))/32)
118
+	fmt.Printf("Bytes: %d\n", len(mem)-int(startOffset))
119
+
120
+	fmt.Printf("Address Range: 0x%08x - 0x%08x\n", baseAddr+startOffset, baseAddr+uint32(len(mem))-1)
121
+}
122
+
123
+func fw2bin(fwPath, binPath string) {
124
+
125
+	var area int
126
+
127
+	switch filepath.Ext(fwPath) {
128
+	case ".FW1":
129
+		area = 1
130
+	case ".FW2":
131
+		area = 2
132
+	case ".FW3":
133
+		area = 3
134
+	default:
135
+		die("Input file must have .FW1, .FW2 or .FW3 extension. Aborting.")
136
+	}
137
+
138
+	fmt.Printf("Loading image from '%s'...\n", filepath.Base(fwPath))
139
+
140
+	f, err := os.Open(fwPath)
141
+	if err != nil {
142
+		die("Unable to open input file '%s': %v. Aborting.\n", fwPath, err)
143
+	}
144
+
145
+	if area == 1 {
146
+		_, err = f.Seek(0x100, io.SeekStart)
147
+		if err != nil {
148
+			die("Unable seek past header: %v. Aborting.\n", err)
149
+		}
150
+	}
151
+
152
+	mem := make([]byte, 0x20000)
153
+
154
+	records := 0
155
+
156
+	lowAddr := uint32(0xffffffff)
157
+	highAddr := uint32(0)
158
+
159
+	for {
160
+		rec, err := stfw.ReadRecord(f)
161
+		if err != nil {
162
+			if err == io.EOF {
163
+				break
164
+			}
165
+			die("Unexpected error reading input file '%s': %v. Aborting.\n", fwPath, err)
166
+		}
167
+		if !rec.Good {
168
+			die("Bad checksum in record %d (Address 0x%08x). Aborting.\n", records, rec.Addr)
169
+		}
170
+		copy(mem[rec.Addr&0x1ffff:], rec.Data)
171
+		records++
172
+		if rec.Addr < lowAddr {
173
+			lowAddr = rec.Addr
174
+		}
175
+		if rec.Addr > highAddr {
176
+			highAddr = rec.Addr + 31
177
+		}
178
+
179
+	}
180
+	fmt.Printf("Loaded successfully.\n")
181
+	fmt.Printf("Records: %d\n", records)
182
+	fmt.Printf("Bytes: %d\n", records*32)
183
+
184
+	fmt.Printf("Address Range: 0x%08x-0x%08x\n", lowAddr, highAddr)
185
+
186
+	binPathExt := fmt.Sprintf("%s.bin%d", binPath, area)
187
+	fmt.Printf("Writing binary to '%s'...\n", filepath.Base(binPathExt))
188
+	err = os.WriteFile(binPathExt, mem, 0644)
189
+	if err != nil {
190
+		die("Unable write binary file: %v. Aborting.\n", err)
191
+	}
192
+
193
+}
194
+
195
+func die(format string, a ...interface{}) {
196
+	fmt.Fprintf(os.Stderr, format, a...)
197
+	os.Exit(1)
198
+}

+ 208
- 0
cmd/convertx/convertx.go Целия файл

@@ -0,0 +1,208 @@
1
+/*
2
+
3
+convertx - tools for mangling Trimble 4000 ROMs
4
+
5
+Release 1.0
6
+
7
+This is a collection of tools for working with fourth generation Trimble 4000
8
+GPS receiver firmware images in the .X format as included with Trimble's
9
+firmware update tool.
10
+
11
+Because this code was written to solve a single instance of a single problem, it
12
+is my tradition to write it as disposable code. This is code that is good enough
13
+to get the job done, and not so terrible that I can't stand to look at it. But
14
+no effort has been made to optimize it for memory or speed.
15
+
16
+Another consequence of this style of development is that when I write tools such
17
+as this, they are not stand-alone programs. I have made no effort to create a
18
+command line interface to this code. I have structured it in a modular manner,
19
+and I've used the Go paradigm of exported-functions-have-capital-letters to
20
+identify the useful building blocks. I then use these building blocks in the
21
+main() function to write a short program to accomplish the task at hand. If I
22
+like it, it gets spun off into its own function to save it, which then frees up
23
+my main() for the next project.
24
+
25
+This program is in the public domain.
26
+
27
+This program was written by Keelan Lightfoot
28
+http://www.beefchicken.com/
29
+*/
30
+
31
+package main
32
+
33
+import (
34
+	"code.beefchicken.com/trimtools/dotx"
35
+	"flag"
36
+	"fmt"
37
+	"io/ioutil"
38
+	"os"
39
+	"path/filepath"
40
+	"strings"
41
+)
42
+
43
+const (
44
+	memOpBlockSize = 244
45
+)
46
+
47
+func guessFileType(fn string) string {
48
+	switch filepath.Ext(fn) {
49
+	case ".X":
50
+		return "x"
51
+	case ".bin":
52
+		return "bin"
53
+	}
54
+	return ""
55
+}
56
+
57
+func main() {
58
+
59
+	var metaPath string
60
+
61
+	var printInfo bool
62
+	flag.BoolVar(&printInfo, "i", false, "Print image metadata")
63
+
64
+	flag.Parse()
65
+
66
+	if len(flag.Args()) != 2 {
67
+		fmt.Printf("Usage:\n")
68
+		fmt.Printf("    convertx <input>.X <output prefix>\n")
69
+		fmt.Printf("    convertx <input>.bin <output prefix>\n")
70
+		os.Exit(1)
71
+	}
72
+
73
+	inFile := flag.Arg(0)
74
+	outFile := flag.Arg(1)
75
+
76
+	fromBin := false
77
+	switch filepath.Ext(inFile) {
78
+	case ".X":
79
+		metaPath = outFile + ".blocks"
80
+		outFile = outFile + ".bin"
81
+	case ".bin":
82
+		fromBin = true
83
+		outFile = outFile + ".X"
84
+		metaPath = strings.TrimSuffix(inFile, filepath.Ext(inFile)) + ".blocks"
85
+	default:
86
+		die("Input file must have .bin or .X extension. Aborting.")
87
+	}
88
+
89
+	var dotX *dotx.DotXFile
90
+
91
+	if fromBin {
92
+		fmt.Printf("Loading block info from '%s'...\n", metaPath)
93
+
94
+		meta, err := dotx.LoadMeta(metaPath)
95
+		if err != nil {
96
+			die("Unable to read block info from '%s': %v. Aborting.\n", metaPath, err)
97
+		}
98
+
99
+		fmt.Printf("Loading binary image from '%s'...\n", inFile)
100
+
101
+		b, err := ioutil.ReadFile(inFile)
102
+		if err != nil {
103
+			die("Unable to read binary image from '%s': %v. Aborting.\n", inFile, err)
104
+		}
105
+		fmt.Printf("Combining block info and binary image...\n")
106
+
107
+		dotX, err = dotx.NewDotXFromMetaAndMem(meta, b)
108
+		if err != nil {
109
+			die("Unable to combine block info and binary image: %v. Aborting.\n", err)
110
+		}
111
+	} else {
112
+		fmt.Printf("Loading .X image from '%s'...\n", inFile)
113
+
114
+		f, err := os.Open(inFile)
115
+		if err != nil {
116
+			die("Unable to open '%s': %v. Aborting.\n", inFile, err)
117
+		}
118
+		defer f.Close()
119
+		dotX, err = dotx.ReadDotX(f)
120
+		if err != nil {
121
+			die("Unable to read .X image from '%s': %v. Aborting.\n", inFile, err)
122
+
123
+		}
124
+	}
125
+
126
+	fmt.Printf("Loaded successfully.\n")
127
+
128
+	mem := dotX.ToMemory()
129
+
130
+	if printInfo {
131
+		dotX.PrintInfo()
132
+		mem.PrintInfo()
133
+	}
134
+
135
+	if false {
136
+		// patch WNRO
137
+		verMaj, verMin := mem.FirmwareVersion()
138
+		ver := float64(verMaj) + float64(verMin)/100
139
+
140
+		if ver < 7.19 {
141
+			fmt.Printf("Warning: WRNO auto-patching has only been tested for firmware versions >= 7.19, use on other versions may produce unpredictable results.")
142
+		}
143
+		// Because the memory slice backs both blocks and records, the data can be modified
144
+		// in any of the three records types simultaneously.
145
+		// This will apply the WRNO patch to 732.X and 719.X
146
+		// 1. find the matching instructions (addi.w  #$400,d2)
147
+		matches := dotX.FindSequence([]uint8{0x06, 0x42, 0x04, 0x00})
148
+		if len(matches) == 0 {
149
+			fmt.Printf("Could not find ADDI.W  #$400,D2 instruction. Aborting WRNO patch attempt")
150
+			os.Exit(1)
151
+		}
152
+
153
+		mem := matches[0].Block.Bytes
154
+		match := matches[0].Matches[0]
155
+
156
+		fmt.Printf("Found WNRO ADDI.W at %08X\n", matches[0].Block.StartAddr+match)
157
+
158
+		// 2. change partial-rollover check cmpi to 0
159
+		mem[match-4] = 0
160
+		mem[match-3] = 0
161
+
162
+		// 2. change $400 to $800
163
+		mem[match+2] = 8
164
+
165
+		// 3. recalculate the block checksums
166
+		dotX.UpdateChecksums()
167
+	}
168
+	//
169
+	// 	// Example 2: extracting and saving DSP firmware
170
+	// 	if !strings.HasSuffix(os.Args[1], "732.X") {
171
+	// 		log.Fatal("Example 2 only works with 732.X")
172
+	// 	}
173
+	//dotX.ExtractSigCode(0x7201C8),
174
+
175
+	if fromBin {
176
+		fmt.Printf("Updating checksums...")
177
+
178
+		dotX.UpdateChecksums()
179
+
180
+		fmt.Printf("Saving .X to '%s'...\n", outFile)
181
+		out, err := os.Create(outFile)
182
+		if err != nil {
183
+			die("Unable write create .X file: %v. Aborting.\n", err)
184
+		}
185
+		defer out.Close()
186
+		err = dotX.WriteDotX(out)
187
+		if err != nil {
188
+			die("Unable write .X file: %v. Aborting.\n", err)
189
+		}
190
+	} else {
191
+		fmt.Printf("Saving block info to '%s'...\n", metaPath)
192
+		err := dotX.SaveMeta(metaPath)
193
+		if err != nil {
194
+			die("Unable write block info file: %v. Aborting.\n", err)
195
+		}
196
+		fmt.Printf("Saving binary to '%s'...\n", outFile)
197
+		err = dotX.SaveBinary(outFile)
198
+		if err != nil {
199
+			die("Unable write binary file: %v. Aborting.\n", err)
200
+		}
201
+	}
202
+
203
+}
204
+
205
+func die(format string, a ...interface{}) {
206
+	fmt.Printf(format, a...)
207
+	os.Exit(1)
208
+}

BIN
cmd/convertx/foo.bin Целия файл


+ 0
- 168
cmd/convertx/main.go Целия файл

@@ -1,168 +0,0 @@
1
-/*
2
-
3
-convertx - tools for mangling Trimble 4000 ROMs
4
-
5
-Release 1.0
6
-
7
-This is a collection of tools for working with fourth generation Trimble 4000
8
-GPS receiver firmware images in the .X format as included with Trimble's
9
-firmware update tool.
10
-
11
-Because this code was written to solve a single instance of a single problem, it
12
-is my tradition to write it as disposable code. This is code that is good enough
13
-to get the job done, and not so terrible that I can't stand to look at it. But
14
-no effort has been made to optimize it for memory or speed.
15
-
16
-Another consequence of this style of development is that when I write tools such
17
-as this, they are not stand-alone programs. I have made no effort to create a
18
-command line interface to this code. I have structured it in a modular manner,
19
-and I've used the Go paradigm of exported-functions-have-capital-letters to
20
-identify the useful building blocks. I then use these building blocks in the
21
-main() function to write a short program to accomplish the task at hand. If I
22
-like it, it gets spun off into its own function to save it, which then frees up
23
-my main() for the next project.
24
-
25
-This program is in the public domain.
26
-
27
-This program was written by Keelan Lightfoot
28
-http://www.beefchicken.com/
29
-*/
30
-
31
-package main
32
-
33
-import (
34
-	"code.beefchicken.com/trimtools/dotx"
35
-	"flag"
36
-	"fmt"
37
-	"log"
38
-	"os"
39
-	"strings"
40
-)
41
-
42
-const (
43
-	memOpBlockSize = 244
44
-)
45
-
46
-
47
-func guessFileType(fn string) string {
48
-	switch filepath.Ext(fn) {
49
-	case ".X":
50
-		return "x"
51
-	case ".bin":
52
-		return "bin"
53
-	} 
54
-	return ""	
55
-}
56
-
57
-func main() {
58
-
59
-	var inFile,outFile,  inFileFormat, outFileFormat string
60
-	var printInfo bool
61
-
62
-	flag.StringVar(&inFile, "in", "", "input file")
63
-	flag.StringVar(&outFile, "out", "", "output file")
64
-	flag.StringVar(&inFileFormat, "infmt", "", "input file format (x or bin)")
65
-	flag.StringVar(&outFileFormat, "outfmt", "", "input file format (x or bin)")
66
-	flag.BoolVar(&printInfo, "i", false, "Print image metadata")
67
-
68
-	if inFileFormat == "" {
69
-		if inFileFormat = guessFileType(inFile, "x"); inFileFormat == "" {
70
-			fmt.Println("Input file format not specified and extension unrecognized. Assuming HP64000.")
71
-			inFileFormat = "x"
72
-		}
73
-	}
74
-	
75
-	if outFileFormat == "" {
76
-		if outFileFormat = guessFileType(inFile, "x"); outFileFormat == "" {
77
-			fmt.Println("Output file format not specified and extension unrecognized.  Assuming binary.")
78
-			outFileFormat = "bin"
79
-		}
80
-	}
81
-
82
-	flag.Parse()
83
-
84
-	if inFile == "" {
85
-		fmt.Printf("Input file is required.")
86
-		os.Exit(1)
87
-	}
88
-	
89
-	if outFile == "" {
90
-		fmt.Printf("Input file is required.")
91
-		os.Exit(1)
92
-	}
93
-
94
-	// load file
95
-	f, err := os.Open(inFile)
96
-	if err != nil {
97
-		log.Fatal(err)
98
-	}
99
-	defer f.Close()
100
-
101
-	dotX, err := dotx.ReadDotX(f)
102
-	if err != nil {
103
-		log.Fatal(err)
104
-	}
105
-
106
-	dotX.Blocks[2].ToRecords(memOpBlockSize)
107
-
108
-	mem := dotX.ToMemory()
109
-
110
-	if printInfo {
111
-		dotX.PrintInfo()
112
-		mem.PrintInfo()
113
-	}
114
-
115
-	// 	if patchWNRO {
116
-	//
117
-	// 		verMaj, verMin := mem.FirmwareVersion()
118
-	// 		ver := float64(verMaj) + float64(verMin)/100
119
-	//
120
-	// 		if ver < 7.19 {
121
-	// 			fmt.Printf("Warning: WRNO auto-patching has only been tested for firmware versions >= 7.19, use on other versions may produce unpredictable results.")
122
-	// 		}
123
-	// 		// Because the memory slice backs both blocks and records, the data can be modified
124
-	// 		// in any of the three records types simultaneously.
125
-	// 		// This will apply the WRNO patch to 732.X and 719.X
126
-	// 		// 1. find the matching instructions (addi.w  #$400,d2)
127
-	// 		matches := mem.FindSequence([]uint8{0x06, 0x42, 0x04, 0x00})
128
-	// 		if len(matches) == 0 {
129
-	// 			fmt.Printf("Could not find ADDI.W  #$400,D2 instruction. Aborting WRNO patch attempt")
130
-	// 			os.Exit(1)
131
-	// 		}
132
-	// 		// 2. change partial-rollover check cmpi to 0
133
-	// 		mem.Memory[matches[0]-4] = 0
134
-	// 		mem.Memory[matches[0]-3] = 0
135
-	//
136
-	// 		// 2. change $400 to $800
137
-	// 		mem.Memory[matches[0]+2] = 8
138
-	//
139
-	// 		// 3. recalculate the block checksums
140
-	// 		mem.UpdateChecksums()
141
-	// 	}
142
-	//
143
-	// 	// Example 2: extracting and saving DSP firmware
144
-	// 	if !strings.HasSuffix(os.Args[1], "732.X") {
145
-	// 		log.Fatal("Example 2 only works with 732.X")
146
-	// 	}
147
-	//dotX.ExtractSigCode(0x7201C8),
148
-
149
-	if binOut != "" {
150
-		err := dotX.SaveBinary(binOut)
151
-		if err != nil {
152
-			log.Fatal(err)
153
-		}
154
-	}
155
-
156
-	if xOut != "" {
157
-		out, err := os.Create(xOut)
158
-		if err != nil {
159
-			log.Fatal(err)
160
-		}
161
-		defer out.Close()
162
-		err = dotX.WriteDotX(out)
163
-		if err != nil {
164
-			log.Fatal(err)
165
-		}
166
-	}
167
-
168
-}

BIN
cmd/loader/loader Целия файл


cmd/loader/main.go → cmd/loader/loader.go Целия файл

@@ -43,20 +43,24 @@ func main() {
43 43
 	var fw *dotx.DotXFile
44 44
 
45 45
 	var mon *monitor.Monitor
46
+	var rec *monitor.Receiver
47
+
46 48
 	var err error
47 49
 
48 50
 	if dummy {
49 51
 		mon = &monitor.Monitor{
50
-			Debug: debug,
52
+			//			Debug: debug,
51 53
 			Dummy: dummy,
52 54
 		}
53 55
 		fmt.Println("Monitor dummy mode active.")
54 56
 
55 57
 	} else {
56
-		mon, err = monitor.Connect(device, baud, 8, 1, parity, debug)
58
+
59
+		rec, err = monitor.Connect(device, baud, 8, 1, parity, debug)
57 60
 		if err != nil {
58 61
 			log.Fatal(err)
59 62
 		}
63
+		mon = monitor.NewMonitor(rec)
60 64
 		defer mon.Close()
61 65
 	}
62 66
 
@@ -445,7 +449,7 @@ func handleJCommand(mon *monitor.Monitor, cmd []string) bool {
445 449
 		fmt.Printf("invalid address: '%s'\n", cmd[1])
446 450
 		return false
447 451
 	}
448
-	mon.Jump(addr)
452
+	mon.CmdJump(addr)
449 453
 	return false
450 454
 }
451 455
 

+ 77
- 0
cmd/romknife/romknife.go Целия файл

@@ -0,0 +1,77 @@
1
+package main
2
+
3
+import (
4
+	"code.beefchicken.com/trimtools/stfw"
5
+	"flag"
6
+	"fmt"
7
+	"os"
8
+)
9
+
10
+func main() {
11
+
12
+	var combine, split bool
13
+	flag.BoolVar(&combine, "c", false, "combine ROM images")
14
+	flag.BoolVar(&split, "s", false, "split ROM images")
15
+
16
+	flag.Parse()
17
+
18
+	if len(flag.Args()) != 3 || !(combine || split) || combine && split {
19
+		fmt.Printf("Usage:\n")
20
+		fmt.Printf("    romknife -c <low> <high> <combined>\n")
21
+		fmt.Printf("    romknife -s <combined> <low> <high>\n")
22
+		os.Exit(1)
23
+	}
24
+
25
+	if combine {
26
+		low, err := os.ReadFile(flag.Arg(0))
27
+		if err != nil {
28
+			die("Unable to read '%s': %v. Aborting.\n", flag.Arg(0), err)
29
+		}
30
+
31
+		high, err := os.ReadFile(flag.Arg(1))
32
+		if err != nil {
33
+			die("Unable to read '%s': %v. Aborting.\n", flag.Arg(1), err)
34
+		}
35
+
36
+		if len(low) != len(high) {
37
+			die("Low and High file sizes must match. Aborting.\n")
38
+		}
39
+
40
+		combined := stfw.MergeBin(low, high)
41
+
42
+		err = os.WriteFile(flag.Arg(2), combined, 0644)
43
+		if err != nil {
44
+			die("Unable to write output to '%s': %v. Aborting.\n", flag.Arg(2), err)
45
+		}
46
+	}
47
+
48
+	if split {
49
+		combined, err := os.ReadFile(flag.Arg(0))
50
+		if err != nil {
51
+			die("Unable to read '%s': %v. Aborting.\n", flag.Arg(0), err)
52
+		}
53
+
54
+		if len(combined)%2 != 0 {
55
+			die("Combined must have even size. Aborting.\n")
56
+		}
57
+
58
+		low, high := stfw.SplitBin(combined)
59
+
60
+		err = os.WriteFile(flag.Arg(1), low, 0644)
61
+		if err != nil {
62
+			die("Unable to write file '%s': %v. Aborting.\n", flag.Arg(1), err)
63
+		}
64
+
65
+		err = os.WriteFile(flag.Arg(2), high, 0644)
66
+		if err != nil {
67
+			die("Unable to write file '%s': %v. Aborting.\n", flag.Arg(2), err)
68
+		}
69
+
70
+	}
71
+
72
+}
73
+
74
+func die(format string, a ...interface{}) {
75
+	fmt.Fprintf(os.Stderr, format, a...)
76
+	os.Exit(1)
77
+}

+ 155
- 55
dotx/dotx.go Целия файл

@@ -32,6 +32,8 @@ package dotx
32 32
 import (
33 33
 	"bytes"
34 34
 	"encoding/binary"
35
+	"encoding/hex"
36
+	"encoding/json"
35 37
 	"fmt"
36 38
 	"io"
37 39
 	"io/ioutil"
@@ -42,14 +44,13 @@ import (
42 44
 )
43 45
 
44 46
 type DotXFile struct {
45
-	ProductionFlag  uint8
46
-	HeaderText      string
47
-	TransferAddress uint32
48
-	DataBusWidth    uint16
49
-	DataWidthBase   uint16
50
-
47
+	ProductionFlag  uint8  `json:"productionFlag"`
48
+	HeaderText      string `json:"headerText"`
49
+	TransferAddress uint32 `json:"transferAddress"`
50
+	DataBusWidth    uint16 `json:"dataBusWidth"`
51
+	DataWidthBase   uint16 `json:"dataWidthBase"`
51 52
 	// 	Memory          []byte
52
-	Blocks []*Block
53
+	Blocks []*Block `json:"blocks"`
53 54
 	//	Records			[]*DataRecord
54 55
 }
55 56
 
@@ -204,6 +205,32 @@ func (dotX *DotXFile) ToMemory() *Memory {
204 205
 	return m
205 206
 }
206 207
 
208
+func LoadMeta(metaFN string) (*DotXFile, error) {
209
+	b, err := ioutil.ReadFile(metaFN)
210
+	if err != nil {
211
+		return nil, err
212
+	}
213
+	dotX := &DotXFile{}
214
+	if err = json.Unmarshal(b, dotX); err != nil {
215
+		return nil, err
216
+	}
217
+
218
+	return dotX, nil
219
+}
220
+
221
+func NewDotXFromMetaAndMem(dotX *DotXFile, mem []byte) (*DotXFile, error) {
222
+
223
+	for _, block := range dotX.Blocks {
224
+		if block.Bytes == nil {
225
+			block.Bytes = make([]byte, block.Len()+1)
226
+			copy(block.Bytes, mem[block.StartAddr:block.EndAddr+1])
227
+		}
228
+	}
229
+	dotX.UpdateChecksums()
230
+	return dotX, nil
231
+
232
+}
233
+
207 234
 func NewDotX() *DotXFile {
208 235
 	return &DotXFile{
209 236
 		DataBusWidth:  16,
@@ -235,7 +262,7 @@ func (dotX *DotXFile) WriteDotX(f *os.File) error {
235 262
 	trimbleHeader := make([]byte, 18)
236 263
 	trimbleHeader[0] = 0x82 // ?
237 264
 	trimbleHeader[1] = 0x04 // magic number
238
-	trimbleHeader[2] = 0x00 // ?
265
+	trimbleHeader[2] = 0x38 // 0x00 for 4000, 0x38 for 4400?
239 266
 	copy(trimbleHeader[3:17], dotX.HeaderText)
240 267
 	trimbleHeader[17] = dotX.ProductionFlag // Production Version
241 268
 	f.Write(trimbleHeader)
@@ -252,32 +279,27 @@ func (dotX *DotXFile) WriteDotX(f *os.File) error {
252 279
 	if err != nil {
253 280
 		return err
254 281
 	}
255
-	cs1 := &DataRecord{
256
-		Addr:  0x00FFFA54,
257
-		Bytes: []byte{0x70, 0x07, 0x78, 0x71},
258
-	}
259
-	err = cs1.Write(f)
260
-	if err != nil {
261
-		return err
262
-	}
263
-	cs2 := &DataRecord{
264
-		Addr:  0x00FFFA46,
265
-		Bytes: []byte{0x03, 0xF5},
266
-	}
267
-	err = cs2.Write(f)
268
-	if err != nil {
269
-		return err
270
-	}
282
+	// cs1 := &DataRecord{
283
+	// 		Addr:  0x00FFFA54,
284
+	// 		Bytes: []byte{0x70, 0x07, 0x78, 0x71},
285
+	// 	}
286
+	// 	err = cs1.Write(f)
287
+	// 	if err != nil {
288
+	// 		return err
289
+	// 	}
290
+	// 	cs2 := &DataRecord{
291
+	// 		Addr:  0x00FFFA46,
292
+	// 		Bytes: []byte{0x03, 0xF5},
293
+	// 	}
294
+	// 	err = cs2.Write(f)
295
+	// 	if err != nil {
296
+	// 		return err
297
+	// 	}
271 298
 
272 299
 	for _, block := range dotX.Blocks {
273
-		if !block.Checksummable() {
274
-			continue
275
-		}
276
-		for _, rec := range block.ToRecords(244) {
277
-			err = rec.Write(f)
278
-			if err != nil {
279
-				return err
280
-			}
300
+		err = block.Write(f)
301
+		if err != nil {
302
+			return err
281 303
 		}
282 304
 	}
283 305
 
@@ -290,8 +312,37 @@ func WriteBinary(memory []byte, outfile string) error {
290 312
 }
291 313
 
292 314
 func (dotX *DotXFile) SaveBinary(outfile string) error {
293
-	//err := ioutil.WriteFile(outfile, []byte(dotX.Memory), 0644)
294
-	return nil //err
315
+	memory := dotX.ToMemory()
316
+	err := ioutil.WriteFile(outfile, []byte(memory.Memory), 0644)
317
+	if err != nil {
318
+		return err
319
+	}
320
+
321
+	return err
322
+
323
+}
324
+
325
+func (dotX *DotXFile) SaveMeta(outfile string) error {
326
+
327
+	clone := *dotX
328
+	clone.Blocks = make([]*Block, len(dotX.Blocks))
329
+
330
+	for i, block := range dotX.Blocks {
331
+		b := *block
332
+		if block.Checksummable() {
333
+			b.Bytes = nil
334
+		}
335
+		clone.Blocks[i] = &b
336
+	}
337
+
338
+	b, err := json.MarshalIndent(clone, "", "  ")
339
+	if err != nil {
340
+		return err
341
+	}
342
+	err = ioutil.WriteFile(outfile, b, 0644)
343
+
344
+	return err
345
+
295 346
 }
296 347
 
297 348
 func (m *Memory) ExtractSigCode(baseAddr uint32) []byte {
@@ -397,25 +448,37 @@ func (dotX *DotXFile) PrintBlocks() {
397 448
 
398 449
 }
399 450
 
400
-// func (m *Memory) FindSequence(seq []byte) []uint32 {
401
-//
402
-// 	matches := make([]uint32, 0)
403
-// 	for _, block := range dotX.Blocks {
404
-// 		for i := 0; i <= len(block.Bytes)-len(seq); i++ {
405
-// 			if bytes.Compare(block.Bytes[i:i+len(seq)], seq) == 0 {
406
-// 				matches = append(matches, uint32(i)+block.StartAddr)
407
-// 			}
408
-// 		}
409
-// 	}
410
-// 	return matches
411
-//
412
-// }
451
+type SequenceMatch struct {
452
+	Block   *Block
453
+	Matches []uint32
454
+}
413 455
 
414
-// func (dotX *DotXFile) UpdateChecksums() {
415
-// 	for _, block := range dotX.Blocks {
416
-// 		block.UpdateChecksum()
417
-// 	}
418
-// }
456
+func (dotX *DotXFile) FindSequence(seq []byte) []SequenceMatch {
457
+
458
+	matches := make([]SequenceMatch, 0)
459
+	for _, block := range dotX.Blocks {
460
+		blockMatches := make([]uint32, 0)
461
+		for i := 0; i <= len(block.Bytes)-len(seq); i++ {
462
+			if bytes.Compare(block.Bytes[i:i+len(seq)], seq) == 0 {
463
+				blockMatches = append(blockMatches, uint32(i)+block.StartAddr)
464
+			}
465
+		}
466
+		if len(blockMatches) > 0 {
467
+			matches = append(matches, SequenceMatch{
468
+				Block:   block,
469
+				Matches: blockMatches,
470
+			})
471
+		}
472
+	}
473
+	return matches
474
+
475
+}
476
+
477
+func (dotX *DotXFile) UpdateChecksums() {
478
+	for _, block := range dotX.Blocks {
479
+		block.UpdateChecksum()
480
+	}
481
+}
419 482
 
420 483
 type processorRecord struct {
421 484
 	Length            uint16
@@ -464,10 +527,41 @@ func (d *DataRecord) Write(f *os.File) error {
464 527
 	return nil
465 528
 }
466 529
 
530
+type hexByte []byte
531
+
532
+func (w *hexByte) MarshalJSON() ([]byte, error) {
533
+	return []byte(`"` + hex.EncodeToString(*w) + `"`), nil
534
+}
535
+
536
+func (w *hexByte) UnmarshalJSON(b []byte) error {
537
+	b, err := hex.DecodeString(string(b[1 : len(b)-1]))
538
+	if err != nil {
539
+		return err
540
+	}
541
+	*w = b
542
+	return nil
543
+}
544
+
467 545
 type Block struct {
468
-	StartAddr uint32
469
-	EndAddr   uint32
470
-	Bytes     []byte
546
+	StartAddr uint32  `json:"startAddr"`
547
+	EndAddr   uint32  `json:"endAddr"`
548
+	Bytes     hexByte `json:"data,omitempty"`
549
+}
550
+
551
+func (b *Block) Write(f *os.File) error {
552
+	if b.Checksummable() {
553
+		err := b.UpdateChecksum()
554
+		if err != nil {
555
+			return err
556
+		}
557
+	}
558
+	for _, rec := range b.ToRecords(244) {
559
+		err := rec.Write(f)
560
+		if err != nil {
561
+			return err
562
+		}
563
+	}
564
+	return nil
471 565
 }
472 566
 
473 567
 func (b *Block) Len() int {
@@ -529,8 +623,14 @@ func (b *Block) CalculateChecksum() (uint32, error) {
529 623
 	return checksum, nil
530 624
 }
531 625
 
626
+//func (b *Block) UpdateChecksum() error {
627
+
532 628
 func (b *Block) UpdateChecksum() error {
533 629
 
630
+	if !b.Checksummable() {
631
+		return fmt.Errorf("block too small for checksum")
632
+	}
633
+
534 634
 	checksum, err := b.CalculateChecksum()
535 635
 	if err != nil {
536 636
 		return err

+ 261
- 193
monitor/monitor.go Целия файл

@@ -5,7 +5,7 @@ import (
5 5
 	"code.beefchicken.com/trimtools/dotx"
6 6
 	"encoding/binary"
7 7
 	"fmt"
8
-	"github.com/jacobsa/go-serial/serial"
8
+	//	"github.com/jacobsa/go-serial/serial"
9 9
 	"io"
10 10
 	"strconv"
11 11
 	"strings"
@@ -31,28 +31,17 @@ const (
31 31
 )
32 32
 
33 33
 type Monitor struct {
34
-	port       io.ReadWriteCloser
35
-	device     string
36
-	baud       uint
37
-	dataBits   uint
38
-	stopBits   uint
39
-	parity     serial.ParityMode
40
-	portConfig serial.OpenOptions
41
-
42 34
 	receiverType ReceiverType
43
-
44
-	Debug bool
45
-	Dummy bool
46
-
47
-	inMonitor bool
48
-	
49
-	seekPos uint32
35
+	Dummy        bool
36
+	inMonitor    bool
37
+	receiver     *Receiver
38
+	seekPos      uint32
50 39
 }
51 40
 
52
-type RecieverState int
41
+type ReceiverState int
53 42
 
54 43
 const (
55
-	ReceiverUnexpected RecieverState = iota
44
+	ReceiverUnexpected ReceiverState = iota
56 45
 	ReceiverUnresponsive
57 46
 	ReceiverNormal
58 47
 	ReceiverInMonitor
@@ -65,24 +54,21 @@ const (
65 54
 	ReceiverType4400
66 55
 )
67 56
 
57
+func NewMonitor(receiver *Receiver) *Monitor {
58
+	return &Monitor{receiver: receiver}
59
+}
60
+
68 61
 func (m *Monitor) StartMonitor() error {
62
+
69 63
 	if m.Dummy {
70 64
 		m.inMonitor = true
71 65
 		return nil
72 66
 	}
73 67
 	fmt.Println("Starting monitor mode...")
74 68
 
75
-	//	m.FlushInput()
76
-
77
-	// figure out baud and parity
78
-	err := m.DeterminePortConfig()
79
-	if err != nil {
80
-		return err
81
-	}
82
-
83 69
 	// once we have something that looks like a receiver on the line, figure
84 70
 	// out what mode it's in.
85
-	state, err := m.checkMonitorMode()
71
+	state, err := m.receiver.ProbeReceiverState()
86 72
 	if err != nil {
87 73
 		return err
88 74
 	}
@@ -93,72 +79,72 @@ func (m *Monitor) StartMonitor() error {
93 79
 
94 80
 	if state == ReceiverNormal {
95 81
 		// get config
96
-		conf, err := m.GetRuntimeConfig()
82
+		conf, err := m.CmdGetRuntimeConfig()
97 83
 		if err != nil {
98 84
 			return err
99 85
 		}
100 86
 		fmt.Printf("PPU Version = %.2f\n", conf.BootRomVersion)
101 87
 
102
-		opts, err := m.GetOptions()
88
+		opts, err := m.CmdGetOptions()
103 89
 		if opts.ReceiverType == 3 { // gauss
104 90
 
105 91
 		}
106 92
 		// switch to monitor mode
107
-		err = m.DoAckCommand(NrmCmdModeChange, []byte{0x57}) // I don't know what 0x57 means, it's straight from the port monitor
93
+		err = m.receiver.DoAckCommand(NrmCmdModeChange, []byte{0x57}) // 0x57 is what an ack at 38400 looks like at 9600 baud.
108 94
 		if err != nil {
109 95
 			return err
110 96
 		}
111 97
 
112 98
 		// When the monitor starts, it reverts back to 9600-N-8-1, so we have to
113 99
 		// re-configure the serial port.
114
-		err = m.useSlowBaudRate()
100
+		err = m.receiver.useSlowBaudRate()
115 101
 		if err != nil {
116 102
 			return err
117 103
 		}
118 104
 
119 105
 		// It can take a while to boot into monitor mode
120
-		err = m.waitForMonitor()
106
+		err = m.receiver.waitForMonitor()
121 107
 		if err != nil {
122 108
 			return err
123 109
 		}
124
-
125
-		// reconfigure monitor to speak at 38400-N-8-1
126
-		// 0x6D = 4800
127
-		// 0x37 = 9600
128
-		// 0x1B = 19200
129
-		// 0x0E = 38400
130
-		// This appears to respond with 0xFE, but that is just is what a 38400
131
-		// baud ACK (0x06) looks like when received at 9600 baud, and depending
132
-		// on where in the bit the UART samples, different UARTs might see
133
-		// different values, so we just ignore any no-ack error.
134
-
135
-		err = m.DoAckCommand(MonCmdBaudRate, []byte{0x00, 0x0E})
136
-		if err != nil {
137
-			if _, ok := err.(*ProtocolError); !ok {
138
-				return err
139
-			}
140
-		}
141
-
142
-		// re-configure port for 38400 baud
143
-		err = m.useFastBaudRate()
144
-		if err != nil {
110
+	}
111
+	// reconfigure monitor to speak at 38400-N-8-1
112
+	// 0x6D = 4800
113
+	// 0x37 = 9600
114
+	// 0x1B = 19200
115
+	// 0x0E = 38400
116
+	// This appears to respond with 0xFE, but that is just is what a 38400
117
+	// baud ACK (0x06) looks like when received at 9600 baud, and depending
118
+	// on where in the bit the UART samples, different UARTs might see
119
+	// different values, so we just ignore any no-ack error.
120
+
121
+	err = m.receiver.DoAckCommand(MonCmdBaudRate, []byte{0x00, 0x0E})
122
+	if err != nil {
123
+		if _, ok := err.(*ProtocolError); !ok {
145 124
 			return err
146 125
 		}
126
+	}
147 127
 
148
-		// Make sure baud switch was successful
149
-		err = m.waitForMonitor()
150
-		if err != nil {
151
-			return err
152
-		}
128
+	// re-configure port for 38400 baud
129
+	err = m.receiver.useFastBaudRate()
130
+	if err != nil {
131
+		return err
132
+	}
153 133
 
134
+	// Make sure baud switch was successful
135
+	err = m.receiver.waitForMonitor()
136
+	if err != nil {
137
+		return err
154 138
 	}
139
+
140
+	//	}
155 141
 	m.inMonitor = true
156 142
 
157 143
 	// we should be in monitor mode now.
158 144
 
159 145
 	fmt.Printf("Setting chip selects...\n")
160 146
 
161
-	err = m.setupChipSelects()
147
+	err = m.setChipSelects()
162 148
 	if err != nil {
163 149
 		return err
164 150
 	}
@@ -170,7 +156,7 @@ func (m *Monitor) StartMonitor() error {
170 156
 	return nil
171 157
 }
172 158
 
173
-func (m *Monitor) setupChipSelects() error {
159
+func (m *Monitor) setChipSelects() error {
174 160
 
175 161
 	// I don't understand the mechanism the PPU uses to communicate to the
176 162
 	// system memory. The chip select registers only apply to signals originating
@@ -251,68 +237,25 @@ func (m *Monitor) setupChipSelects() error {
251 237
 	return nil
252 238
 }
253 239
 
254
-// waitForMonitor blocks until the receiver has entered monitor mode.
255
-// It can take a while for the receiver to boot into monitor mode, this
256
-// function tests the state of the receiver by repeatedly sending an ENQ
257
-// command. When the monitor is ready, it will send an ACK.
258
-func (m *Monitor) waitForMonitor() error {
259
-	fmt.Println("Waiting for monitor to become ready...")
260
-	for i := 0; i < 30; i++ {
261
-		if ok, err := m.EnqAckTest(); ok {
262
-			return nil
263
-		} else if err != nil {
264
-			return err
265
-		}
266
-	}
267
-	return fmt.Errorf("timed out waiting for monitor")
268
-}
269
-
270 240
 // Stop sends a reboot, which disconnects the monitor, and reconfigures the port back
271 241
 // to the user configuration.
272 242
 func (m *Monitor) Stop() error {
273
-
274
-	// send the reboot command
275
-	err := m.SendStxEtxRequest(MonCmdReset, []byte{})
276
-	if err != nil {
277
-		return err
278
-	}
279
-
280
-	m.inMonitor = false
281
-
282
-	// it seems that if I switch the baud rate too soon, the UART will still
283
-	// be transmitting the above data, which will corrupt the command.
284
-	time.Sleep(2 * time.Second)
285
-
286
-	// revert the serial port
287
-	err = m.useUserBaudRate()
288
-	if err != nil {
289
-		return err
290
-	}
291
-
292
-	return nil
243
+	return m.CmdResetReceiver()
293 244
 }
294 245
 
295 246
 // Close closes the serial port associated with the monitor
296 247
 func (m *Monitor) Close() {
297
-	m.port.Close()
248
+	m.receiver.port.Close()
298 249
 }
299 250
 
300 251
 // ReadMemory reads the device memory, breaking the read up into multiple
301 252
 // commands as needed.
302 253
 func (m *Monitor) ReadMemory(addr uint32, length int) ([]byte, error) {
303
-	return m.ReadMemoryWithProgress(addr, length, nil)
304
-}
305
-
306
-// ReadMemory reads the device memory, breaking the read up into multiple
307
-// commands as needed.
308
-func (m *Monitor) ReadMemoryWithProgress(addr uint32, length int, progressFunc func(uint32)) ([]byte, error) {
309 254
 	if !m.inMonitor {
310 255
 		return nil, fmt.Errorf("receiver not in monitor")
311 256
 	}
312 257
 	//fmt.Printf("ReadMemory: addr=%08X length=%d\n", addr, length)
313 258
 
314
-	maxRetries := 3
315
-
316 259
 	out := make([]byte, 0)
317 260
 	for i := 0; i < length; i += readWriteBlockSize {
318 261
 		l := readWriteBlockSize
@@ -320,44 +263,46 @@ func (m *Monitor) ReadMemoryWithProgress(addr uint32, length int, progressFunc f
320 263
 			l = length - i
321 264
 		}
322 265
 
323
-		b := make([]byte, 5)
324
-
325 266
 		blockAddr := addr + uint32(i)
326 267
 
327
-		binary.BigEndian.PutUint32(b[0:4], blockAddr)
328
-		b[4] = byte(l)
268
+		b, err := m.CmdReadMemory(blockAddr, uint8(l))
269
+		if err != nil {
270
+			return nil, err
271
+		}
272
+		out = append(out, b...)
273
+	}
274
+	return out, nil
275
+}
329 276
 
330
-		//fmt.Printf("ReadMemory LOOP: addr=%08X length=%d\n", blockAddr, l)
277
+func (m *Monitor) CmdReadMemory(addr uint32, length uint8) ([]byte, error) {
278
+	b := make([]byte, 5)
279
+	binary.BigEndian.PutUint32(b[0:4], addr)
280
+	b[4] = byte(length)
331 281
 
332
-		var err error
282
+	//fmt.Printf("CmdReadMemory: addr=%08X length=%d\n", blockAddr, l)
333 283
 
334
-		for r := 0; r < maxRetries; r++ {
335
-			if r > 0 {
336
-				fmt.Printf("ReadMemory: read failed: %v. Retrying.\n", err)
337
-			}
338
-			var reply *CommandReply
339
-			if reply, err = m.DoStxEtxCommand(MonCmdReadMem, b); err != nil {
340
-				continue
341
-			}
342
-			if reply.Command != MonCmdReadMemAck || reply.Status != 0 {
343
-				err = newProtocolError("unexpected response from read memory")
344
-				continue
345
-			}
346
-			if replyAddr := binary.BigEndian.Uint32(reply.Data[0:4]); replyAddr != blockAddr {
347
-				err = newProtocolError("address mismatch in read reply. expected 0x%06X, received 0x%06X", addr, replyAddr)
348
-				continue
349
-			}
350
-			out = append(out, reply.Data[4:]...)
351
-			break
284
+	var err error
285
+	const maxRetries = 3
286
+
287
+	for r := 0; r < maxRetries; r++ {
288
+		if r > 0 {
289
+			fmt.Printf("CmdReadMemory: read failed: %v. Retrying.\n", err)
352 290
 		}
353
-		if err != nil {
354
-			return nil, err
291
+		var reply *CommandReply
292
+		if reply, err = m.receiver.DoStxEtxCommand(MonCmdReadMem, b); err != nil {
293
+			continue
294
+		}
295
+		if reply.Command != MonCmdReadMemAck || reply.Status != 0 {
296
+			err = newProtocolError("unexpected response from read memory")
297
+			continue
355 298
 		}
356
-		if progressFunc != nil {
357
-			progressFunc(blockAddr)
299
+		if replyAddr := binary.BigEndian.Uint32(reply.Data[0:4]); replyAddr != addr {
300
+			err = newProtocolError("address mismatch in read reply. expected 0x%06X, received 0x%06X", addr, replyAddr)
301
+			continue
358 302
 		}
303
+		return reply.Data[4:], nil
359 304
 	}
360
-	return out, nil
305
+	return nil, err
361 306
 }
362 307
 
363 308
 // WriteMemory writes the device memory, breaking the write up into multiple
@@ -372,33 +317,15 @@ func (m *Monitor) WriteMemory(addr uint32, data interface{}) error {
372 317
 	b := buf.Bytes()
373 318
 	length := len(b)
374 319
 
375
-	maxRetries := 3
376
-
377 320
 	for i := 0; i < length; i += readWriteBlockSize {
378 321
 		l := readWriteBlockSize
379 322
 		if i+l > length {
380 323
 			l = length - i
381 324
 		}
382 325
 
383
-		buf := make([]byte, 4)
384 326
 		blockAddr := addr + uint32(i)
385
-		// for writes the address words are in little endian order, but the bytes
386
-		// within the words are in big endian order
387
-		binary.BigEndian.PutUint16(buf[0:2], uint16(blockAddr&0xFFFF))
388
-		binary.BigEndian.PutUint16(buf[2:4], uint16(blockAddr>>16&0xFFFF))
389
-		buf = append(buf, b[i:i+l]...)
390
-
391
-		var err error
392
-		for r := 0; r < maxRetries; r++ {
393
-			if r > 0 {
394
-				fmt.Printf("WriteMemory: write failed: %v. Retrying.\n", err)
395
-			}
396
-			err = m.DoAckCommand(MonCmdWriteMem, buf)
397
-			if err == nil {
398
-				break
399
-			}
400
-		}
401 327
 
328
+		err := m.CmdWriteMemory(blockAddr, b[i:i+l])
402 329
 		if err != nil {
403 330
 			return err
404 331
 		}
@@ -406,55 +333,66 @@ func (m *Monitor) WriteMemory(addr uint32, data interface{}) error {
406 333
 	return nil
407 334
 }
408 335
 
336
+func (m *Monitor) CmdWriteMemory(addr uint32, data []byte) error {
337
+	const maxRetries = 3
338
+	buf := make([]byte, 4)
339
+	// for writes the address words are in little endian order, but the bytes
340
+	// within the words are in big endian order
341
+	binary.BigEndian.PutUint16(buf[0:2], uint16(addr&0xFFFF))
342
+	binary.BigEndian.PutUint16(buf[2:4], uint16(addr>>16&0xFFFF))
343
+	buf = append(buf, data...)
344
+
345
+	var err error
346
+	for r := 0; r < maxRetries; r++ {
347
+		if r > 0 {
348
+			fmt.Printf("CmdWriteMemory: write failed: %v. Retrying.\n", err)
349
+		}
350
+		err = m.receiver.DoAckCommand(MonCmdWriteMem, buf)
351
+		if err == nil {
352
+			break
353
+		}
354
+
355
+	}
356
+
357
+	if err != nil {
358
+		return err
359
+	}
360
+	return nil
361
+}
362
+
409 363
 // Jump calls the monitor's jump command.
410
-func (m *Monitor) Jump(addr uint32) error {
364
+func (m *Monitor) CmdJump(addr uint32) error {
411 365
 	if !m.inMonitor {
412 366
 		return fmt.Errorf("receiver not in monitor")
413 367
 	}
414 368
 	b := make([]byte, 4)
415 369
 	binary.BigEndian.PutUint32(b[0:4], addr)
416 370
 
417
-	return m.DoAckCommand(MonCmdJmp, b)
371
+	return m.receiver.DoAckCommand(MonCmdJmp, b)
418 372
 }
419 373
 
420 374
 // ResetReceiver calls the monitor's reset command.
421
-func (m *Monitor) ResetReceiver() error {
375
+func (m *Monitor) CmdResetReceiver() error {
422 376
 	if !m.inMonitor {
423 377
 		return fmt.Errorf("receiver not in monitor")
424 378
 	}
425
-	err := m.SendStxEtxRequest(MonCmdReset, []byte{})
379
+	err := m.receiver.SendStxEtxRequest(MonCmdReset, []byte{})
426 380
 	if err != nil {
427 381
 		return err
428 382
 	}
429 383
 	m.inMonitor = false
430
-	return nil
431
-}
432 384
 
433
-// checkMonitorMode checks if the receiver is in normal mode or monitor mode.
434
-// this is done by running command 0x82, which in normal mode is the screen dump
435
-// command, which replies with a packet of type 0x82. In monitor mode, the
436
-// command replies with a packet of type 0x92. If the receiver responds with
437
-// a 0x92 packet, we know it's in monitor mode.
438
-func (m *Monitor) checkMonitorMode() (RecieverState, error) {
439
-	fmt.Println("Preforming dummy read to determine monitor state...")
440
-
441
-	b := make([]byte, 5)
442
-
443
-	binary.BigEndian.PutUint32(b[0:], 0x80000) // 'safe' dummy location to read from, I think it's the RAM base address
444
-	b[4] = 2                                   // read 2 bytes
385
+	// it seems that if I switch the baud rate too soon, the UART will still
386
+	// be transmitting the above data, which will corrupt the command.
387
+	time.Sleep(2 * time.Second)
445 388
 
446
-	r, err := m.DoStxEtxCommand(MonCmdReadMem, b)
389
+	// revert the serial port
390
+	err = m.receiver.useUserBaudRate()
447 391
 	if err != nil {
448
-		return ReceiverUnresponsive, err
449
-	}
450
-
451
-	if r.Command == MonCmdReadMemAck && r.Status == 0 {
452
-		fmt.Println("Receiver appears to be in monitor mode.")
453
-		return ReceiverInMonitor, nil
392
+		return err
454 393
 	}
455 394
 
456
-	fmt.Println("Receiver appears to be in normal mode.")
457
-	return ReceiverNormal, nil
395
+	return nil
458 396
 }
459 397
 
460 398
 // ReceiverInfo holds all the information we can glean from the receiver by
@@ -654,9 +592,7 @@ func (m *Monitor) CaptureReceiverFirmware() (*dotx.DotXFile, error) {
654 592
 
655 593
 	for _, block := range codeBlocks {
656 594
 		blockLen := block.Len()
657
-		b, err := m.ReadMemoryWithProgress(block.StartAddr, blockLen, func(i uint32) {
658
-			fmt.Printf("$%08X  %s\r", i, progressBar(50, int(i-block.StartAddr)+bytesWriten, totalBytes))
659
-		})
595
+		b, err := m.ReadMemory(block.StartAddr, blockLen)
660 596
 		if err != nil {
661 597
 			return nil, err
662 598
 		}
@@ -665,14 +601,18 @@ func (m *Monitor) CaptureReceiverFirmware() (*dotx.DotXFile, error) {
665 601
 		bytesWriten += blockLen
666 602
 	}
667 603
 
604
+	// func(i uint32) {
605
+	// 	fmt.Printf("$%08X  %s\r", i, progressBar(50, int(i-block.StartAddr)+bytesWriten, totalBytes))
606
+	// }
607
+
668 608
 	return dotX, nil
669 609
 }
670 610
 
671
-func (m *Monitor) GetRuntimeConfig() (*RuntimeConfig, error) {
611
+func (m *Monitor) CmdGetRuntimeConfig() (*RuntimeConfig, error) {
672 612
 	if m.inMonitor {
673 613
 		return nil, fmt.Errorf("receiver not in normal mode")
674 614
 	}
675
-	r, err := m.DoStxEtxCommand(NrmCmdGetSerial, []byte{})
615
+	r, err := m.receiver.DoStxEtxCommand(NrmCmdGetSerial, []byte{})
676 616
 	if err != nil {
677 617
 		return nil, err
678 618
 	}
@@ -738,11 +678,11 @@ type Options struct {
738 678
 	ReceiverType       int
739 679
 }
740 680
 
741
-func (m *Monitor) GetOptions() (*Options, error) {
681
+func (m *Monitor) CmdGetOptions() (*Options, error) {
742 682
 	if m.inMonitor {
743 683
 		return nil, fmt.Errorf("receiver not in normal mode")
744 684
 	}
745
-	r, err := m.DoStxEtxCommand(NrmCmdGetOpt, []byte{})
685
+	r, err := m.receiver.DoStxEtxCommand(NrmCmdGetOpt, []byte{})
746 686
 	if err != nil {
747 687
 		return nil, err
748 688
 	}
@@ -865,17 +805,18 @@ func (m *Monitor) ProgramReceiver(fw *dotx.DotXFile) error {
865 805
 	}
866 806
 
867 807
 	fmt.Printf("Rebooting receiver.\n")
868
-	return m.ResetReceiver()
808
+	return m.CmdResetReceiver()
869 809
 }
870 810
 
871
-// Reader implements the io.Reader interface
872
-func (m *Monitor) xRead(p []byte) (n int, err error) {
873
-	p, err = m.ReadMemoryWithProgress(m.seekPos, len(p), nil)
811
+// Read implements the io.ReadSeeker interface
812
+func (m *Monitor) Read(p []byte) (n int, err error) {
813
+	p, err = m.ReadMemory(m.seekPos, len(p))
814
+	m.seekPos += uint32(len(p))
874 815
 	return len(p), err
875 816
 }
876 817
 
877
-// Seek implements the io.Seeker interface
878
-func (m *Monitor) xSeek(offset int64, whence int) (int64, error) { 
818
+// Seek implements the io.ReadSeeker interface
819
+func (m *Monitor) Seek(offset int64, whence int) (int64, error) {
879 820
 	var abs int64
880 821
 	switch whence {
881 822
 	case io.SeekStart:
@@ -891,3 +832,130 @@ func (m *Monitor) xSeek(offset int64, whence int) (int64, error) {
891 832
 	m.seekPos = uint32(abs)
892 833
 	return abs, nil
893 834
 }
835
+
836
+func ReadMemory(f io.ReadSeeker, addr uint32, length int) ([]byte, error) {
837
+	_, err := f.Seek(int64(addr), io.SeekStart)
838
+	if err != nil {
839
+		return nil, err
840
+	}
841
+	b := make([]byte, length)
842
+	_, err = f.Read(b)
843
+	if err != nil {
844
+		return nil, err
845
+	}
846
+	return b, nil
847
+}
848
+
849
+type RawReceiverInfo struct {
850
+	Options            [60]byte
851
+	Name1              [36]byte
852
+	Name2              [36]byte
853
+	SerialNumberString [15]byte
854
+	SerialNumber       uint32
855
+}
856
+
857
+// GetReceiverInfo reads receiver information out of the receiver. Unlike GetOptions
858
+// and GetRuntimeConfig, this reads the values straight out of memory.
859
+func GetReceiverInfo(f io.ReadSeeker) (*ReceiverInfo, error) {
860
+
861
+	info := &ReceiverInfo{}
862
+
863
+	// Read the receiver-specific configuration.
864
+	// 0x80600 and 0x22d both contain the same data, but for some reason some
865
+	// code reads from one area and some from the other.
866
+	b, err := ReadMemory(f, 0x80600, 0x97)
867
+	if err != nil {
868
+		return nil, err
869
+	}
870
+	info.OptionMemory = b[0:60]
871
+	info.Name1 = cStrtoGoStr(b[60:96])
872
+	info.Name2 = cStrtoGoStr(b[96:132])
873
+	info.SerialNumberString = cStrtoGoStr(b[132:147])
874
+	info.SerialNumber = int(binary.BigEndian.Uint32(b[147:151]))
875
+
876
+	configBase := uint32(0x000001F0)
877
+
878
+	// read the configuration memory
879
+	b, err = ReadMemory(f, configBase, 0x3A)
880
+	if err != nil {
881
+		return nil, err
882
+	}
883
+	info.ConfigMemory = b
884
+
885
+	date := int(binary.BigEndian.Uint32(info.ConfigMemory[0x222-configBase:]))
886
+	month := date / 10000
887
+	day := date / 100 % 100
888
+	year := date % 100
889
+	if year >= 80 {
890
+		year += 1900
891
+	} else {
892
+		year += 2000
893
+	}
894
+	info.FirmwareDate = time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
895
+
896
+	ver := int(binary.BigEndian.Uint16(info.ConfigMemory[0x204-configBase:]))
897
+	info.FirmwareVersionMajor = ver / 100
898
+	info.FirmwareVersionMinor = ver % 100
899
+	info.FirmwareVersion = float64(ver) / 100
900
+
901
+	info.Code1Start = binary.BigEndian.Uint32(info.ConfigMemory[0x1f0-configBase:])
902
+	info.Code1End = binary.BigEndian.Uint32(info.ConfigMemory[0x1f4-configBase:])
903
+	info.Code2Start = binary.BigEndian.Uint32(info.ConfigMemory[0x1f8-configBase:])
904
+	info.Code2End = binary.BigEndian.Uint32(info.ConfigMemory[0x1fc-configBase:])
905
+
906
+	// read checksums
907
+	b, err = ReadMemory(f, info.Code1End-12, 4)
908
+	if err != nil {
909
+		return nil, err
910
+	}
911
+	info.Code1Checksum = binary.BigEndian.Uint32(b)
912
+	b, err = ReadMemory(f, info.Code2End-12, 4)
913
+	if err != nil {
914
+		return nil, err
915
+	}
916
+	info.Code2Checksum = binary.BigEndian.Uint32(b)
917
+
918
+	if info.FirmwareVersion > 5.60 {
919
+	}
920
+	return info, nil
921
+}
922
+
923
+func CaptureReceiverFirmware(f io.ReadSeeker) (*dotx.DotXFile, error) {
924
+
925
+	info, err := GetReceiverInfo(f)
926
+	if err != nil {
927
+		return nil, err
928
+	}
929
+	dotX := dotx.NewDotX()
930
+
931
+	// these chip selects are hard-coded into the first two records in the file
932
+	dotX.AddBlock(0x00FFFA54, []byte{0x70, 0x07, 0x78, 0x71})
933
+	dotX.AddBlock(0x00FFFA46, []byte{0x03, 0xF5})
934
+
935
+	// then the code blocks
936
+	codeBlocks := []*dotx.Block{
937
+		{StartAddr: info.Code1Start, EndAddr: info.Code1End},
938
+		{StartAddr: info.Code2Start, EndAddr: info.Code2End},
939
+	}
940
+
941
+	totalBytes := 0
942
+
943
+	for _, cb := range codeBlocks {
944
+		totalBytes += cb.Len()
945
+	}
946
+
947
+	bytesWriten := 0
948
+
949
+	for _, block := range codeBlocks {
950
+		blockLen := block.Len()
951
+		b, err := ReadMemory(f, block.StartAddr, blockLen)
952
+		if err != nil {
953
+			return nil, err
954
+		}
955
+		block.Bytes = b
956
+		dotX.Blocks = append(dotX.Blocks, block)
957
+		bytesWriten += blockLen
958
+	}
959
+
960
+	return dotX, nil
961
+}

+ 0
- 153
monitor/protocol.go Целия файл

@@ -1,153 +0,0 @@
1
-package monitor
2
-
3
-import (
4
-	"fmt"
5
-	"io"
6
-)
7
-
8
-const (
9
-	MAXPACKETLLEN = 255 + 6
10
-)
11
-
12
-type ProtocolError struct {
13
-	err string
14
-}
15
-
16
-func newProtocolError(format string, a ...interface{}) error {
17
-	return &ProtocolError{err: fmt.Sprintf(format, a...)}
18
-}
19
-
20
-func (e *ProtocolError) Error() string {
21
-	return e.err
22
-}
23
-
24
-func (m *Monitor) EnqAckTest() (bool, error) {
25
-	buf := make([]byte, 1)
26
-	m.Write([]byte{0x05}) // enq
27
-	n, err := m.Read(buf)
28
-	if err == nil && n > 0 && buf[0] == 0x06 { // ack
29
-		return true, nil
30
-	} else {
31
-		if err != io.EOF {
32
-			return false, err
33
-		}
34
-	}
35
-	return false, nil
36
-}
37
-
38
-// DoAckCommand sends an STX/ETX framed command that expects a single byte reply. Sometimes
39
-// it's not an ACK that's sent, so it's up to the caller to tell this function what it
40
-// response it expects.
41
-func (m *Monitor) DoAckCommand(command byte, data []byte) error {
42
-	err := m.SendStxEtxRequest(command, data)
43
-	if err != nil {
44
-		return err
45
-	}
46
-
47
-	if m.Dummy {
48
-		return nil
49
-	}
50
-
51
-	return m.ReadAckReply()
52
-}
53
-
54
-// ReadAckReply reads a single byte reply
55
-func (m *Monitor) ReadAckReply() error {
56
-	r := make([]byte, 1)
57
-	n, err := m.Read(r)
58
-	if err != nil {
59
-		return err
60
-	}
61
-
62
-	if n != 1 || r[0] != 0x06 { // ack
63
-		return newProtocolError("ack not received")
64
-	}
65
-
66
-	return nil
67
-}
68
-
69
-type CommandReply struct {
70
-	Status  byte
71
-	Command byte
72
-	Data    []byte
73
-}
74
-
75
-// DoStxEtxCommand sends an STX/ETX framed command that expects an STX/ETX framed reply
76
-func (m *Monitor) DoStxEtxCommand(command byte, data []byte) (*CommandReply, error) {
77
-
78
-	err := m.SendStxEtxRequest(command, data)
79
-	if err != nil {
80
-		return nil, err
81
-	}
82
-
83
-	r, err := m.ReadStxEtxReply()
84
-	if err != nil {
85
-		return nil, err
86
-	}
87
-
88
-	return r, nil
89
-}
90
-
91
-// SendStxEtxRequest sends an STX/ETX framed request
92
-func (m *Monitor) SendStxEtxRequest(command byte, data []byte) error {
93
-
94
-	payload := []byte{0, command, byte(len(data))}
95
-	payload = append(payload, data...)
96
-
97
-	var checksum byte
98
-	for _, b := range payload {
99
-		checksum += b
100
-	}
101
-
102
-	packet := append([]byte{0x02}, payload...)         // stx
103
-	packet = append(packet, []byte{checksum, 0x03}...) // etx
104
-
105
-	_, err := m.Write(packet)
106
-
107
-	return err
108
-}
109
-
110
-//  ReadStxEtxReply reads a STX/ETX framed reply
111
-func (m *Monitor) ReadStxEtxReply() (*CommandReply, error) {
112
-	packet := make([]byte, MAXPACKETLLEN) // maximum packet length
113
-
114
-	_, err := m.ReadFull(packet[:4])
115
-	if err != nil {
116
-		return nil, err
117
-	}
118
-
119
-	if packet[0] != 0x02 { // stx
120
-		fmt.Printf("expected STX received: %02X\n", packet[0])
121
-		return nil, newProtocolError("expected STX received: %02X", packet[0])
122
-	}
123
-
124
-	status := packet[1]
125
-	command := packet[2]
126
-	length := int(packet[3])
127
-
128
-	_, err = m.ReadFull(packet[4 : length+6]) // also read checksum and ETX
129
-
130
-	packet = packet[:length+6] // trim to length
131
-
132
-	checksum := packet[len(packet)-2]
133
-
134
-	var computedChecksum byte
135
-	for _, b := range packet[1 : len(packet)-2] {
136
-		computedChecksum += b
137
-	}
138
-
139
-	if checksum != computedChecksum {
140
-		return nil, newProtocolError("bad checksum. received: %02X expected: %02X", checksum, computedChecksum)
141
-	}
142
-
143
-	if packet[len(packet)-1] != 0x03 { // etx
144
-		return nil, newProtocolError("expected ETX received: %02X", packet[len(packet)-1])
145
-	}
146
-
147
-	return &CommandReply{
148
-		Status:  status,
149
-		Command: command,
150
-		Data:    packet[4 : len(packet)-2],
151
-	}, nil
152
-
153
-}

+ 402
- 0
monitor/receiver.go Целия файл

@@ -0,0 +1,402 @@
1
+package monitor
2
+
3
+import (
4
+	"encoding/binary"
5
+	"fmt"
6
+	"github.com/jacobsa/go-serial/serial"
7
+	"io"
8
+	"os"
9
+)
10
+
11
+const (
12
+	MAXPACKETLLEN = 255 + 6
13
+)
14
+
15
+type Receiver struct {
16
+	port       io.ReadWriteCloser
17
+	device     string
18
+	baud       uint
19
+	dataBits   uint
20
+	stopBits   uint
21
+	parity     serial.ParityMode
22
+	portConfig serial.OpenOptions
23
+
24
+	receiverType ReceiverType
25
+
26
+	Debug bool
27
+	Dummy bool
28
+
29
+	inMonitor bool
30
+
31
+	seekPos uint32
32
+}
33
+
34
+func Connect(device string, baud int, dataBits int, stopBits int, parity string, debug bool) (*Receiver, error) {
35
+	r := &Receiver{
36
+		device:   device,
37
+		baud:     uint(baud),
38
+		dataBits: uint(dataBits),
39
+		stopBits: uint(stopBits),
40
+		Debug:    debug,
41
+	}
42
+
43
+	r.portConfig = serial.OpenOptions{
44
+		PortName:              r.device,
45
+		BaudRate:              r.baud,
46
+		DataBits:              r.dataBits,
47
+		StopBits:              r.stopBits,
48
+		MinimumReadSize:       0,
49
+		InterCharacterTimeout: 1000, // a non-zero timeout is required for waitForMonitor to work
50
+	}
51
+
52
+	switch parity {
53
+	case "none":
54
+		r.parity = serial.PARITY_NONE
55
+		r.portConfig.ParityMode = serial.PARITY_NONE
56
+	case "even":
57
+		r.parity = serial.PARITY_EVEN
58
+		r.portConfig.ParityMode = serial.PARITY_EVEN
59
+	case "odd":
60
+		r.parity = serial.PARITY_ODD
61
+		r.portConfig.ParityMode = serial.PARITY_ODD
62
+	default:
63
+		return nil, fmt.Errorf("unrecognized parity setting")
64
+	}
65
+	var err error
66
+	r.port, err = serial.Open(r.portConfig)
67
+	if err != nil {
68
+		return nil, err
69
+	}
70
+
71
+	// figure out baud and parity
72
+	err = r.ProbePortConfig()
73
+	if err != nil {
74
+		return nil, err
75
+	}
76
+
77
+	return r, nil
78
+}
79
+
80
+// ProbePortConfig probes different serial port settings to find a responding receiver
81
+func (r *Receiver) ProbePortConfig() error {
82
+
83
+	var err error
84
+
85
+	// First figure out what baud rate the receiver is currently set up for
86
+	// easiest way to test this is by sending an ENQ
87
+	for attempts := 0; attempts < 3; attempts++ {
88
+
89
+		switch attempts {
90
+		case 0:
91
+			err = r.useUserBaudRate()
92
+		case 1:
93
+			err = r.useFastBaudRate()
94
+		case 2:
95
+			err = r.useSlowBaudRate()
96
+		}
97
+		if err != nil {
98
+			return err
99
+		}
100
+
101
+		err = r.ClearComm()
102
+		if err != nil {
103
+			return err
104
+		}
105
+
106
+		ok, err := r.EnqAckTest()
107
+		if err != nil {
108
+			return err
109
+		}
110
+
111
+		if !ok {
112
+			continue
113
+		}
114
+
115
+		if ok {
116
+			return nil
117
+		}
118
+	}
119
+	return fmt.Errorf("can't find receiver")
120
+
121
+}
122
+
123
+func (r *Receiver) reconfigurePort(baudRate uint, dataBits uint, stopBits uint, parity serial.ParityMode) error {
124
+	var err error
125
+	r.port.Close()
126
+	r.portConfig.BaudRate = baudRate
127
+	r.portConfig.DataBits = dataBits
128
+	r.portConfig.StopBits = stopBits
129
+	r.portConfig.ParityMode = parity
130
+	r.port, err = serial.Open(r.portConfig)
131
+	return err
132
+}
133
+
134
+func (r *Receiver) useSlowBaudRate() error {
135
+	fmt.Println("Reconfiguring port to slow monitor baud rate: 9600 n-8-1")
136
+	return r.reconfigurePort(9600, 8, 1, serial.PARITY_NONE)
137
+}
138
+
139
+func (r *Receiver) useFastBaudRate() error {
140
+	fmt.Println("Reconfiguring port to fast baud rate: 38400 n-8-1")
141
+	//	return r.reconfigurePort(38400, 8, 1, serial.PARITY_NONE)
142
+	return r.reconfigurePort(38400, 8, 1, serial.PARITY_ODD)
143
+}
144
+
145
+func (r *Receiver) useUserBaudRate() error {
146
+	fmt.Printf("Reconfiguring port to user baud rate: %d %s-%d-%d\n", r.baud, parityToString(r.parity), r.dataBits, r.stopBits)
147
+	return r.reconfigurePort(r.baud, r.dataBits, r.stopBits, r.parity)
148
+}
149
+
150
+func parityToString(p serial.ParityMode) string {
151
+	s := []string{"none", "odd", "even"}
152
+	return s[p]
153
+}
154
+
155
+// ClearComm sents 249 nulls on the serial port, which is something trimble likes to
156
+// do when starting the monitor. I think the point of this was to flush out the FIFO
157
+// buffers.
158
+func (r *Receiver) ClearComm() error {
159
+	nulls := make([]byte, 249)
160
+	_, err := r.Write(nulls)
161
+	if err != nil {
162
+		return err
163
+	}
164
+	// 	b := make([]byte, 12000)
165
+	// 	n, _ := m.Read(b)
166
+	// 	fmt.Printf("%d %02X \n",n,b)
167
+	x := r.port.(*os.File) // TODO: verify if this is actually needed
168
+	x.Sync()
169
+	return nil
170
+}
171
+
172
+func (r *Receiver) FlushInput() {
173
+	b := make([]byte, 12000)
174
+	for {
175
+		n, _ := r.Read(b)
176
+		fmt.Printf("FLUSH read %d bytes: % 02X\n", n, b[:n])
177
+		if n == 0 {
178
+			return
179
+		}
180
+	}
181
+}
182
+
183
+// Write is wrapped so that debug messages can be printed
184
+func (r *Receiver) Write(b []byte) (int, error) {
185
+	r.debugLog("Port Write: % 02X", b)
186
+	//fmt.Printf("%02X••\n",b)
187
+	if r.Dummy {
188
+		return len(b), nil
189
+	}
190
+	n, err := r.port.Write(b)
191
+	return n, err
192
+}
193
+
194
+// Read is wrapped so that debug messages can be printed
195
+func (r *Receiver) Read(b []byte) (int, error) {
196
+	n, err := r.port.Read(b)
197
+	if err == nil {
198
+		r.debugLog("Port Read: % 02X", b)
199
+	}
200
+	return n, err
201
+}
202
+
203
+// ReadFull is wrapped so that debug messages can be printed
204
+func (r *Receiver) ReadFull(b []byte) (int, error) {
205
+	n, err := io.ReadFull(r.port, b)
206
+	if err == nil {
207
+		r.debugLog("Port ReadFull: % 02X", b)
208
+	}
209
+	return n, err
210
+}
211
+
212
+func (r *Receiver) debugLog(format string, a ...interface{}) {
213
+	if r.Debug {
214
+		fmt.Printf("    "+format+"\n", a...)
215
+	}
216
+}
217
+
218
+// waitForMonitor blocks until the receiver has entered monitor mode.
219
+// It can take a while for the receiver to boot into monitor mode, this
220
+// function tests the state of the receiver by repeatedly sending an ENQ
221
+// command. When the monitor is ready, it will send an ACK.
222
+func (r *Receiver) waitForMonitor() error {
223
+	fmt.Println("Waiting for monitor to become ready...")
224
+	for i := 0; i < 30; i++ {
225
+		if ok, err := r.EnqAckTest(); ok {
226
+			return nil
227
+		} else if err != nil {
228
+			return err
229
+		}
230
+	}
231
+	return fmt.Errorf("timed out waiting for monitor")
232
+}
233
+
234
+// ProbeReceiverState checks if the receiver is in normal mode or monitor mode.
235
+// this is done by running command 0x82, which in normal mode is the screen dump
236
+// command, which replies with a packet of type 0x82. In monitor mode, the
237
+// command replies with a packet of type 0x92. If the receiver responds with
238
+// a 0x92 packet, we know it's in monitor mode.
239
+func (r *Receiver) ProbeReceiverState() (ReceiverState, error) {
240
+	fmt.Println("Preforming dummy read to determine monitor state...")
241
+
242
+	b := make([]byte, 5)
243
+
244
+	binary.BigEndian.PutUint32(b[0:], 0x80000) // 'safe' dummy location to read from, I think it's the RAM base address
245
+	b[4] = 2                                   // read 2 bytes
246
+
247
+	reply, err := r.DoStxEtxCommand(MonCmdReadMem, b)
248
+	if err != nil {
249
+		return ReceiverUnresponsive, err
250
+	}
251
+
252
+	if reply.Command == MonCmdReadMemAck && reply.Status == 0 {
253
+		fmt.Println("Receiver appears to be in monitor mode.")
254
+		return ReceiverInMonitor, nil
255
+	}
256
+
257
+	fmt.Println("Receiver appears to be in normal mode.")
258
+	return ReceiverNormal, nil
259
+}
260
+
261
+type ProtocolError struct {
262
+	err string
263
+}
264
+
265
+func newProtocolError(format string, a ...interface{}) error {
266
+	return &ProtocolError{err: fmt.Sprintf(format, a...)}
267
+}
268
+
269
+func (e *ProtocolError) Error() string {
270
+	return e.err
271
+}
272
+
273
+func (r *Receiver) EnqAckTest() (bool, error) {
274
+	buf := make([]byte, 1)
275
+	r.Write([]byte{0x05}) // enq
276
+	n, err := r.Read(buf)
277
+	if err == nil && n > 0 && buf[0] == 0x06 { // ack
278
+		return true, nil
279
+	} else {
280
+		if err != io.EOF {
281
+			return false, err
282
+		}
283
+	}
284
+	return false, nil
285
+}
286
+
287
+// DoAckCommand sends an STX/ETX framed command that expects a single byte reply. Sometimes
288
+// it's not an ACK that's sent, so it's up to the caller to tell this function what it
289
+// response it expects.
290
+func (r *Receiver) DoAckCommand(command byte, data []byte) error {
291
+	err := r.SendStxEtxRequest(command, data)
292
+	if err != nil {
293
+		return err
294
+	}
295
+
296
+	if r.Dummy {
297
+		return nil
298
+	}
299
+
300
+	return r.ReadAckReply()
301
+}
302
+
303
+// ReadAckReply reads a single byte reply
304
+func (r *Receiver) ReadAckReply() error {
305
+	reply := make([]byte, 1)
306
+	n, err := r.Read(reply)
307
+	if err != nil {
308
+		return err
309
+	}
310
+
311
+	if n != 1 || reply[0] != 0x06 { // ack
312
+		return newProtocolError("ack not received")
313
+	}
314
+
315
+	return nil
316
+}
317
+
318
+type CommandReply struct {
319
+	Status  byte
320
+	Command byte
321
+	Data    []byte
322
+}
323
+
324
+// DoStxEtxCommand sends an STX/ETX framed command that expects an STX/ETX framed reply
325
+func (r *Receiver) DoStxEtxCommand(command byte, data []byte) (*CommandReply, error) {
326
+
327
+	err := r.SendStxEtxRequest(command, data)
328
+	if err != nil {
329
+		return nil, err
330
+	}
331
+
332
+	reply, err := r.ReadStxEtxReply()
333
+	if err != nil {
334
+		return nil, err
335
+	}
336
+
337
+	return reply, nil
338
+}
339
+
340
+// SendStxEtxRequest sends an STX/ETX framed request
341
+func (r *Receiver) SendStxEtxRequest(command byte, data []byte) error {
342
+
343
+	payload := []byte{0, command, byte(len(data))}
344
+	payload = append(payload, data...)
345
+
346
+	var checksum byte
347
+	for _, b := range payload {
348
+		checksum += b
349
+	}
350
+
351
+	packet := append([]byte{0x02}, payload...)         // stx
352
+	packet = append(packet, []byte{checksum, 0x03}...) // etx
353
+
354
+	_, err := r.Write(packet)
355
+
356
+	return err
357
+}
358
+
359
+// ReadStxEtxReply reads a STX/ETX framed reply
360
+func (r *Receiver) ReadStxEtxReply() (*CommandReply, error) {
361
+	packet := make([]byte, MAXPACKETLLEN) // maximum packet length
362
+
363
+	_, err := r.ReadFull(packet[:4])
364
+	if err != nil {
365
+		return nil, err
366
+	}
367
+
368
+	if packet[0] != 0x02 { // stx
369
+		fmt.Printf("expected STX received: %02X\n", packet[0])
370
+		return nil, newProtocolError("expected STX received: %02X", packet[0])
371
+	}
372
+
373
+	status := packet[1]
374
+	command := packet[2]
375
+	length := int(packet[3])
376
+
377
+	_, err = r.ReadFull(packet[4 : length+6]) // also read checksum and ETX
378
+
379
+	packet = packet[:length+6] // trim to length
380
+
381
+	checksum := packet[len(packet)-2]
382
+
383
+	var computedChecksum byte
384
+	for _, b := range packet[1 : len(packet)-2] {
385
+		computedChecksum += b
386
+	}
387
+
388
+	if checksum != computedChecksum {
389
+		return nil, newProtocolError("bad checksum. received: %02X expected: %02X", checksum, computedChecksum)
390
+	}
391
+
392
+	if packet[len(packet)-1] != 0x03 { // etx
393
+		return nil, newProtocolError("expected ETX received: %02X", packet[len(packet)-1])
394
+	}
395
+
396
+	return &CommandReply{
397
+		Status:  status,
398
+		Command: command,
399
+		Data:    packet[4 : len(packet)-2],
400
+	}, nil
401
+
402
+}

+ 0
- 185
monitor/serial.go Целия файл

@@ -1,185 +0,0 @@
1
-package monitor
2
-
3
-import (
4
-	"fmt"
5
-	"github.com/jacobsa/go-serial/serial"
6
-	"io"
7
-	"os"
8
-)
9
-
10
-func Connect(device string, baud int, dataBits int, stopBits int, parity string, debug bool) (*Monitor, error) {
11
-	m := &Monitor{
12
-		device:   device,
13
-		baud:     uint(baud),
14
-		dataBits: uint(dataBits),
15
-		stopBits: uint(stopBits),
16
-		Debug:    debug,
17
-	}
18
-
19
-	m.portConfig = serial.OpenOptions{
20
-		PortName:              m.device,
21
-		BaudRate:              m.baud,
22
-		DataBits:              m.dataBits,
23
-		StopBits:              m.stopBits,
24
-		MinimumReadSize:       0,
25
-		InterCharacterTimeout: 1000, // a non-zero timeout is required for waitForMonitor to work
26
-	}
27
-
28
-	switch parity {
29
-	case "none":
30
-		m.parity = serial.PARITY_NONE
31
-		m.portConfig.ParityMode = serial.PARITY_NONE
32
-	case "even":
33
-		m.parity = serial.PARITY_EVEN
34
-		m.portConfig.ParityMode = serial.PARITY_EVEN
35
-	case "odd":
36
-		m.parity = serial.PARITY_ODD
37
-		m.portConfig.ParityMode = serial.PARITY_ODD
38
-	default:
39
-		return nil, fmt.Errorf("unrecognized parity setting")
40
-	}
41
-	var err error
42
-	m.port, err = serial.Open(m.portConfig)
43
-	if err != nil {
44
-		return nil, err
45
-	}
46
-
47
-	return m, nil
48
-}
49
-
50
-// DeterminePortConfig probes different serial port settings to find a responding receiver
51
-func (m *Monitor) DeterminePortConfig() error {
52
-
53
-	var err error
54
-
55
-	// First figure out what baud rate the receiver is currently set up for
56
-	// easiest way to test this is by sending an ENQ
57
-	for attempts := 0; attempts < 3; attempts++ {
58
-
59
-		switch attempts {
60
-		case 0:
61
-			err = m.useUserBaudRate()
62
-		case 1:
63
-			err = m.useFastBaudRate()
64
-		case 2:
65
-			err = m.useSlowBaudRate()
66
-		}
67
-		if err != nil {
68
-			return err
69
-		}
70
-
71
-		err = m.ClearComm()
72
-		if err != nil {
73
-			return err
74
-		}
75
-
76
-		ok, err := m.EnqAckTest()
77
-		if err != nil {
78
-			return err
79
-		}
80
-
81
-		if !ok {
82
-			continue
83
-		}
84
-
85
-		if ok {
86
-			return nil
87
-		}
88
-	}
89
-	return fmt.Errorf("can't find receiver")
90
-
91
-}
92
-
93
-func (m *Monitor) reconfigurePort(baudRate uint, dataBits uint, stopBits uint, parity serial.ParityMode) error {
94
-	var err error
95
-	m.port.Close()
96
-	m.portConfig.BaudRate = baudRate
97
-	m.portConfig.DataBits = dataBits
98
-	m.portConfig.StopBits = stopBits
99
-	m.portConfig.ParityMode = parity
100
-	m.port, err = serial.Open(m.portConfig)
101
-	return err
102
-}
103
-
104
-func (m *Monitor) useSlowBaudRate() error {
105
-	fmt.Println("Reconfiguring port to slow monitor baud rate: 9600 n-8-1")
106
-	return m.reconfigurePort(9600, 8, 1, serial.PARITY_NONE)
107
-}
108
-
109
-func (m *Monitor) useFastBaudRate() error {
110
-	fmt.Println("Reconfiguring port to fast baud rate: 38400 n-8-1")
111
-	return m.reconfigurePort(38400, 8, 1, serial.PARITY_NONE)
112
-}
113
-
114
-func (m *Monitor) useUserBaudRate() error {
115
-	fmt.Printf("Reconfiguring port to user baud rate: %d %s-%d-%d\n", m.baud, parityToString(m.parity), m.dataBits, m.stopBits)
116
-	return m.reconfigurePort(m.baud, m.dataBits, m.stopBits, m.parity)
117
-}
118
-
119
-func parityToString(p serial.ParityMode) string {
120
-	s := []string{"none", "odd", "even"}
121
-	return s[p]
122
-}
123
-
124
-// ClearComm sents 249 nulls on the serial port, which is something trimble likes to
125
-// do when starting the monitor. I think the point of this was to flush out the FIFO
126
-// buffers.
127
-func (m *Monitor) ClearComm() error {
128
-	nulls := make([]byte, 249)
129
-	_, err := m.Write(nulls)
130
-	if err != nil {
131
-		return err
132
-	}
133
-	// 	b := make([]byte, 12000)
134
-	// 	n, _ := m.Read(b)
135
-	// 	fmt.Printf("%d %02X \n",n,b)
136
-	x := m.port.(*os.File)
137
-	x.Sync()
138
-	return nil
139
-}
140
-
141
-func (m *Monitor) FlushInput() {
142
-	b := make([]byte, 12000)
143
-	for {
144
-		n, _ := m.Read(b)
145
-		fmt.Printf("FLUSH read %d bytes: % 02X\n", n, b[:n])
146
-		if n == 0 {
147
-			return
148
-		}
149
-	}
150
-}
151
-
152
-// Write is wrapped so that debug messages can be printed
153
-func (m *Monitor) Write(b []byte) (int, error) {
154
-	m.debugLog("Port Write: % 02X", b)
155
-	//fmt.Printf("%02X••\n",b)
156
-	if m.Dummy {
157
-		return len(b), nil
158
-	}
159
-	n, err := m.port.Write(b)
160
-	return n, err
161
-}
162
-
163
-// Read is wrapped so that debug messages can be printed
164
-func (m *Monitor) Read(b []byte) (int, error) {
165
-	n, err := m.port.Read(b)
166
-	if err == nil {
167
-		m.debugLog("Port Read: % 02X", b)
168
-	}
169
-	return n, err
170
-}
171
-
172
-// ReadFull is wrapped so that debug messages can be printed
173
-func (m *Monitor) ReadFull(b []byte) (int, error) {
174
-	n, err := io.ReadFull(m.port, b)
175
-	if err == nil {
176
-		m.debugLog("Port ReadFull: % 02X", b)
177
-	}
178
-	return n, err
179
-}
180
-
181
-func (m *Monitor) debugLog(format string, a ...interface{}) {
182
-	if m.Debug {
183
-		fmt.Printf("    "+format+"\n", a...)
184
-	}
185
-}

+ 148
- 0
stfw/stfw.go Целия файл

@@ -0,0 +1,148 @@
1
+package stfw
2
+
3
+import (
4
+	"encoding/binary"
5
+	//"fmt"
6
+	"os"
7
+)
8
+
9
+type Record struct {
10
+	Addr        uint32
11
+	Data        []byte
12
+	Sum         uint8
13
+	ComputedSum uint8
14
+	Good        bool
15
+}
16
+
17
+func ReadRecord(f *os.File) (*Record, error) {
18
+
19
+	r := struct {
20
+		Data [36]byte
21
+		Sum  uint8
22
+	}{}
23
+
24
+	err := binary.Read(f, binary.BigEndian, &r)
25
+	if err != nil {
26
+		return nil, err
27
+	}
28
+
29
+	var t uint8
30
+	for _, b := range r.Data {
31
+		t += uint8(b)
32
+	}
33
+	t = ^t
34
+
35
+	record := &Record{
36
+		Addr:        uint32(r.Data[0])<<24 | uint32(r.Data[1])<<16 | uint32(r.Data[2])<<8 | uint32(r.Data[3]),
37
+		Data:        r.Data[4:],
38
+		Sum:         r.Sum,
39
+		ComputedSum: t,
40
+		Good:        r.Sum == t,
41
+	}
42
+
43
+	return record, nil
44
+}
45
+
46
+func WriteRecord(f *os.File, rec *Record) error {
47
+
48
+	r := struct {
49
+		Data [36]byte
50
+		Sum  uint8
51
+	}{}
52
+
53
+	r.Data[0] = byte(rec.Addr >> 24)
54
+	r.Data[1] = byte(rec.Addr >> 16)
55
+	r.Data[2] = byte(rec.Addr >> 8)
56
+	r.Data[3] = byte(rec.Addr)
57
+
58
+	copy(r.Data[4:], rec.Data[:])
59
+
60
+	for _, b := range r.Data {
61
+		r.Sum += uint8(b)
62
+	}
63
+	r.Sum = ^r.Sum
64
+
65
+	err := binary.Write(f, binary.BigEndian, r)
66
+	if err != nil {
67
+		return err
68
+	}
69
+
70
+	return nil
71
+}
72
+
73
+func SplitBin(mem []byte) ([]byte, []byte) {
74
+	low := make([]byte, len(mem)>>1)
75
+	high := make([]byte, len(mem)>>1)
76
+	for i := 0; i < len(mem); i += 2 {
77
+		low[i/2] = mem[i]
78
+		high[i/2] = mem[i+1]
79
+	}
80
+	return low, high
81
+}
82
+
83
+func MergeBin(low []byte, high []byte) []byte {
84
+	mem := make([]byte, len(low)*2)
85
+	for i := 0; i < len(low); i++ {
86
+		mem[i*2] = low[i]
87
+		mem[i*2+1] = high[i]
88
+	}
89
+	return mem
90
+}
91
+
92
+type MemoryMode int
93
+
94
+func (m MemoryMode) physAddr(addr int) int {
95
+	switch m {
96
+	case MemoryModeCombinedLow:
97
+		return addr * 2
98
+	case MemoryModeCombinedHigh:
99
+		return addr*2 + 1
100
+	}
101
+	return addr
102
+}
103
+
104
+const (
105
+	MemoryModeLinear MemoryMode = iota
106
+	MemoryModeCombinedLow
107
+	MemoryModeCombinedHigh
108
+)
109
+
110
+func GetChecksum(mem []byte, mm MemoryMode) uint16 {
111
+	return uint16(mem[mm.physAddr(0xfffe)])*256 + uint16(mem[mm.physAddr(0xffff)])
112
+}
113
+
114
+func GetChecksums(mem []byte) (uint16, uint16) {
115
+	return GetChecksum(mem, MemoryModeCombinedLow), GetChecksum(mem, MemoryModeCombinedHigh)
116
+}
117
+
118
+func CalcChecksum(mem []byte, mm MemoryMode, start int) uint16 {
119
+	var cs uint16
120
+	for i := 0; i <= 0xFFFB; i++ {
121
+		cs = addx(uint16(mem[mm.physAddr(i)]), cs)
122
+	}
123
+	cs = ^cs
124
+	return cs
125
+}
126
+
127
+func CalcChecksums(mem []byte, start int) (uint16, uint16) {
128
+	return CalcChecksum(mem, MemoryModeCombinedLow, start), CalcChecksum(mem, MemoryModeCombinedHigh, start)
129
+}
130
+
131
+func UpdateChecksum(mem []byte, mm MemoryMode, cs uint16) {
132
+	mem[mm.physAddr(0xfffe)] = byte(cs >> 8)
133
+	mem[mm.physAddr(0xffff)] = byte(cs)
134
+}
135
+
136
+var xFlag bool
137
+
138
+func addx(x, y uint16) uint16 {
139
+	if x+y < y {
140
+		xFlag = true
141
+		return x + y
142
+	}
143
+	if xFlag {
144
+		xFlag = false
145
+		return x + y + 1
146
+	}
147
+	return x + y
148
+}