You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
129 lines
3.0 KiB
Go
129 lines
3.0 KiB
Go
package s19
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
)
|
|
|
|
type S19Writer struct {
|
|
FilePath string
|
|
File *os.File
|
|
}
|
|
|
|
func NewS19Writer(filePath string) (*S19Writer, error) {
|
|
file, err := os.Create(filePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &S19Writer{
|
|
FilePath: filePath,
|
|
File: file,
|
|
}, nil
|
|
}
|
|
|
|
func (fw *S19Writer) WriteCPSHeader() error {
|
|
// S0 14 5E73 3D466C617368205265706F72745E6F3D30 <Checksum>
|
|
//
|
|
return nil
|
|
}
|
|
|
|
// 32byte per record
|
|
func (fw *S19Writer) CreateRecordsFromBinary(originalFile *os.File, baseAddress uint32, maxDataSize int) ([]SRecord, error) {
|
|
var records []SRecord
|
|
buffer := make([]byte, maxDataSize)
|
|
currentAddress := baseAddress
|
|
|
|
for {
|
|
// Read up to maxDataSize bytes from the file
|
|
n, err := originalFile.Read(buffer)
|
|
if err != nil && err != io.EOF {
|
|
return nil, err
|
|
}
|
|
|
|
// If we read nothing, we're done
|
|
if n == 0 {
|
|
break
|
|
}
|
|
|
|
// Create an S3 record
|
|
record := SRecord{
|
|
Type: "S3",
|
|
Address: currentAddress,
|
|
Data: append([]byte{}, buffer[:n]...), // Copy the data
|
|
}
|
|
records = append(records, record)
|
|
|
|
// Update the current address
|
|
currentAddress += uint32(n)
|
|
}
|
|
|
|
return records, nil
|
|
}
|
|
func (fw *S19Writer) WriteRecords(records []SRecord) error {
|
|
for _, record := range records {
|
|
line, err := writeSRecord(record)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = fw.File.WriteString(line + "\n")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
func (fw *S19Writer) Close() error {
|
|
return fw.File.Close()
|
|
}
|
|
|
|
// writeSRecord creates a formatted S-record line
|
|
func writeSRecord(record SRecord) (string, error) {
|
|
var addressLength int
|
|
switch record.Type {
|
|
case "S0", "S1": // 2-byte address
|
|
addressLength = 2
|
|
case "S2": // 3-byte address
|
|
addressLength = 3
|
|
case "S3": // 4-byte address
|
|
addressLength = 4
|
|
case "S9": // Termination record with a 2-byte start address
|
|
addressLength = 2
|
|
case "S7": // The address field contains the starting execution address and is interpreted as a 4-byte big-endian address. There is no data field.
|
|
addressLength := 1
|
|
if record.Address > 0xFF {
|
|
addressLength = 2
|
|
}
|
|
if record.Address > 0xFFFF {
|
|
addressLength = 4
|
|
}
|
|
addressFormat := fmt.Sprintf("%%0%dX", addressLength*2)
|
|
address := fmt.Sprintf(addressFormat, record.Address)
|
|
|
|
return fmt.Sprintf("S%s%s", record.Type[1:], address), nil
|
|
default:
|
|
return "", fmt.Errorf("unsupported record type: %s", record.Type)
|
|
}
|
|
|
|
// Calculate the byte count: address bytes + data bytes + checksum byte
|
|
byteCount := 1 + addressLength + len(record.Data)
|
|
|
|
// Create the record string
|
|
addressFormat := fmt.Sprintf("%%0%dX", addressLength*2)
|
|
address := fmt.Sprintf(addressFormat, record.Address)
|
|
|
|
// Convert data to hexadecimal string
|
|
data := hex.EncodeToString(record.Data)
|
|
|
|
// Combine all components
|
|
body := fmt.Sprintf("%02X%s%s", byteCount, address, data)
|
|
|
|
// Compute checksum
|
|
checksum := computeChecksum(body)
|
|
|
|
// Construct the final S-record
|
|
return fmt.Sprintf("S%s%s%02X", record.Type[1:], body, checksum), nil
|
|
}
|