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

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
}