PGZ Loader
Added PGZ loader and a utility to create PGZ files from Motorola SREC. Removed SREC loader.
This commit is contained in:
parent
f06a24d8d3
commit
37f96ce1d5
7
samples/HelloPGZ/README.md
Normal file
7
samples/HelloPGZ/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Sample: HelloPGZ #
|
||||
|
||||
One of the simple executable formats that the FoenixMCP supports is the PGZ format inherited from the original Foenix stock kernel. This assembly demo uses the VASM assembler to create a simple Motorola SREC file that prints "Hello, world." This SREC file can be converted to PGZ using the SRECPGZ C program included in the utilities folder of this repository.
|
||||
|
||||
## Building ##
|
||||
|
||||
Make sure that the VASM binaries are in your executable path and then use the `build.bat` file to create the SREC file.
|
2
samples/HelloPGZ/build.bat
Normal file
2
samples/HelloPGZ/build.bat
Normal file
|
@ -0,0 +1,2 @@
|
|||
@echo off
|
||||
vasmm68k_mot -Fsrec -s37 -exec=start -L hello.lst -o hello.s37 hello.s
|
55
samples/HelloPGZ/hello.lst
Normal file
55
samples/HelloPGZ/hello.lst
Normal file
|
@ -0,0 +1,55 @@
|
|||
F00:0001 ;;;
|
||||
F00:0002 ;;; Sample PGX Program
|
||||
F00:0003 ;;;
|
||||
F00:0004
|
||||
F00:0005 org $010000
|
||||
F00:0006
|
||||
F00:0007 start: move.l #$13,d0 ; sys_chan_write
|
||||
S01:00010000: 70 13
|
||||
F00:0008 clr.l d1 ; Channel #0
|
||||
S01:00010002: 72 00
|
||||
F00:0009 move.l #greet,d2 ; Pointer to message
|
||||
S01:00010004: 24 3C 00 01 00 14
|
||||
F00:0010 move.l #greet_end-greet+1,d3 ; Length of message
|
||||
S01:0001000A: 76 0E
|
||||
F00:0011 trap #15
|
||||
S01:0001000C: 4E 4F
|
||||
F00:0012
|
||||
F00:0013 done: clr.l d0 ; sys_exit
|
||||
S01:0001000E: 70 00
|
||||
F00:0014 clr.l d1 ; Return value = 0
|
||||
S01:00010010: 72 00
|
||||
F00:0015 trap #15
|
||||
S01:00010012: 4E 4F
|
||||
F00:0016
|
||||
F00:0017 greet: dc.b "Hello, world!"
|
||||
S01:00010014: 48 65 6C 6C 6F 2C 20 77 6F 72 6C 64 21
|
||||
F00:0018 greet_end: dc.b 0
|
||||
S01:00010021: 00
|
||||
F00:0019
|
||||
|
||||
|
||||
Sections:
|
||||
S01 seg10000
|
||||
|
||||
|
||||
Sources:
|
||||
F00 hello.s
|
||||
|
||||
|
||||
Symbols:
|
||||
done EXPR(65550=0x1000e) UNUSED ABS
|
||||
greet_end EXPR(65569=0x10021) ABS
|
||||
greet EXPR(65556=0x10014) ABS
|
||||
start EXPR(65536=0x10000) UNUSED ABS
|
||||
_MOVEMBYTES EXPR(0=0x0) INTERNAL
|
||||
MOVEMSIZE EXPR(0=0x0) INTERNAL
|
||||
_MOVEMREGS EXPR(0=0x0) INTERNAL
|
||||
__LINE__ EXPR(19=0x13) INTERNAL
|
||||
__FO EXPR(0=0x0) INTERNAL
|
||||
__RS EXPR(0=0x0) INTERNAL
|
||||
REPTN EXPR(-1=0xffffffff) INTERNAL
|
||||
__VASM EXPR(1=0x1) INTERNAL
|
||||
__UNIXFS EXPR(0=0x0) INTERNAL
|
||||
|
||||
There have been no errors.
|
0
samples/HelloPGZ/hello.pgz
Normal file
0
samples/HelloPGZ/hello.pgz
Normal file
18
samples/HelloPGZ/hello.s
Normal file
18
samples/HelloPGZ/hello.s
Normal file
|
@ -0,0 +1,18 @@
|
|||
;;;
|
||||
;;; Sample PGX Program
|
||||
;;;
|
||||
|
||||
org $010000
|
||||
|
||||
start: move.l #$13,d0 ; sys_chan_write
|
||||
clr.l d1 ; Channel #0
|
||||
move.l #greet,d2 ; Pointer to message
|
||||
move.l #greet_end-greet+1,d3 ; Length of message
|
||||
trap #15
|
||||
|
||||
done: clr.l d0 ; sys_exit
|
||||
clr.l d1 ; Return value = 0
|
||||
trap #15
|
||||
|
||||
greet: dc.b "Hello, world!"
|
||||
greet_end: dc.b 0
|
4
samples/HelloPGZ/hello.s37
Normal file
4
samples/HelloPGZ/hello.s37
Normal file
|
@ -0,0 +1,4 @@
|
|||
S00B00007365673130303030C4
|
||||
S3250001000070137200243C00010014760E4E4F700072004E4F48656C6C6F2C20776F726C6467
|
||||
S307000100202100B6
|
||||
S70500010000F9
|
300
src/dev/fsys.c
300
src/dev/fsys.c
|
@ -614,6 +614,159 @@ unsigned short atoi_hex(char * hex) {
|
|||
return (atoi_hex_1(hex[0]) << 4 | atoi_hex_1(hex[1]));
|
||||
}
|
||||
|
||||
/* Loader for the PGZ binary file format
|
||||
* Supports both the original 24-bit PGZ format and the new 32-bit PGZ format
|
||||
*
|
||||
* The PGZ format:
|
||||
* First byte: ASCII "Z" for 24-bit PGZ, ASCII "z" for 32-bit PGZ
|
||||
* Remaining bytes are segments of two types:
|
||||
* 1) Data segment.
|
||||
* Each data segment starts with an address, which is either 24-bit or 32-bit long, depending on
|
||||
* which flavor of PGZ is used. The address expressed in little-endian form and is the starting
|
||||
* address of the destination for this data block.
|
||||
* Immediately following the address is the size of the block, also either 24-bits or 32-bits,
|
||||
* and also in little-endian form. This size is the number of bytes in the data segment.
|
||||
* Immediately following the size are the data bytes (as many as were specified in the size field)
|
||||
*
|
||||
* 2) Start segment.
|
||||
* A data segment consists of the same address and size fields as the data segment, but the size
|
||||
* is 0. The start segment specifies the starting address of the executable. The start segment
|
||||
* is not required unless the PGZ is intended as an executable file. If none is present, the
|
||||
* PGZ can be loaded but not executed. There should be at most one start segment in a PGZ file,
|
||||
* if more than one is present, only the last will be honored.
|
||||
*
|
||||
* Inputs:
|
||||
* path = the path to the file to load
|
||||
* destination = the destination address (ignored for PGX)
|
||||
* start = pointer to the long variable to fill with the starting address
|
||||
* (0 if not an executable, any other number if file is executable
|
||||
* with a known starting address)
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, negative number on error
|
||||
*/
|
||||
short fsys_pgz_loader(short chan, long destination, long * start) {
|
||||
unsigned char * chunk = 0;
|
||||
unsigned char * dest = 0;
|
||||
long file_idx = 0; /* Offset within the file */
|
||||
long segment_idx = 0; /* Offset within a segment */
|
||||
long address = -1; /* Current segment address */
|
||||
long count = -1; /* Current segment size */
|
||||
short use_32bits = 0; /* File format is either 24-bit or 32-bit */
|
||||
short size_idx = 0; /* Expected offset for first byte of the size */
|
||||
short data_idx = 0; /* Expected offset for the first byte of the data */
|
||||
short result = 0;
|
||||
|
||||
TRACE("fsys_pgx_loader");
|
||||
|
||||
/* Allocate the buffer we'll use for reading the file */
|
||||
chunk = malloc(DEFAULT_CHUNK_SIZE);
|
||||
if (chunk == 0) {
|
||||
result = ERR_OUT_OF_MEMORY;
|
||||
|
||||
} else {
|
||||
/* We have our buffer... start reading chunks into it */
|
||||
while (result == 0) {
|
||||
/* Try to read a chunk of data */
|
||||
short n = chan_read(chan, chunk, DEFAULT_CHUNK_SIZE);
|
||||
if (n > 0) {
|
||||
int i;
|
||||
for (i = 0; i < n; i++, file_idx++) {
|
||||
if (file_idx == 0) {
|
||||
/* Signature byte... must be either "Z", or "z" */
|
||||
if (chunk[i] == 'Z') {
|
||||
/* PGZ 24-bit signature byte */
|
||||
use_32bits = 0;
|
||||
size_idx = 3;
|
||||
data_idx = 6;
|
||||
|
||||
} else if (chunk[i] == 'z') {
|
||||
/* PGZ 32-bit signature byte */
|
||||
use_32bits = 1;
|
||||
size_idx = 4;
|
||||
data_idx = 8;
|
||||
|
||||
} else {
|
||||
/* Signature byte does not match expectation */
|
||||
return ERR_BAD_BINARY;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* We're in the segments... */
|
||||
if ((segment_idx >= 0) && (segment_idx < size_idx)) {
|
||||
/* We're in the address bytes */
|
||||
switch (segment_idx) {
|
||||
case 0:
|
||||
address = chunk[i];
|
||||
count = -1;
|
||||
break;
|
||||
case 1:
|
||||
address = address | chunk[i] << 8;
|
||||
break;
|
||||
case 2:
|
||||
address = address | chunk[i] << 16;
|
||||
log_num(LOG_INFO, "PGZ 24-bit address: ", address);
|
||||
break;
|
||||
case 3:
|
||||
address = address | chunk[i] << 24;
|
||||
log_num(LOG_INFO, "PGZ 32-bit address: ", address);
|
||||
break;
|
||||
}
|
||||
|
||||
} else if ((segment_idx >= size_idx) && (segment_idx < data_idx)) {
|
||||
/* We're in the size bytes */
|
||||
switch (segment_idx - size_idx) {
|
||||
case 0:
|
||||
dest = (unsigned char *)address;
|
||||
count = chunk[i];
|
||||
break;
|
||||
case 1:
|
||||
count = count | chunk[i] << 8;
|
||||
break;
|
||||
case 2:
|
||||
count = count | chunk[i] << 16;
|
||||
if (!use_32bits && count == 0) {
|
||||
*start = address;
|
||||
}
|
||||
log_num(LOG_INFO, "PGZ 24-bit count: ", count);
|
||||
break;
|
||||
case 3:
|
||||
count = count | chunk[i] << 24;
|
||||
if (use_32bits && count == 0) {
|
||||
*start = address;
|
||||
}
|
||||
log_num(LOG_INFO, "PGZ 32-bit count: ", count);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* We're in the data bytes */
|
||||
if (segment_idx - data_idx < count) {
|
||||
*dest++ = chunk[i];
|
||||
}
|
||||
}
|
||||
|
||||
segment_idx++;
|
||||
if (count >= 0) {
|
||||
if (segment_idx >= data_idx + count) {
|
||||
/* If we've reached the end of the segment, start the next */
|
||||
segment_idx = 0;
|
||||
address = -1;
|
||||
count = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
/* We've reached the end of the file */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loader for the PGX binary file format
|
||||
*
|
||||
|
@ -695,151 +848,6 @@ short fsys_pgx_loader(short chan, long destination, long * start) {
|
|||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loader for the Motorola SREC format
|
||||
*
|
||||
* See: https://en.wikipedia.org/wiki/SREC_(file_format)
|
||||
*
|
||||
* Inputs:
|
||||
* path = the path to the file to load
|
||||
* destination = the destination address (0 for use file's address)
|
||||
* start = pointer to the long variable to fill with the starting address
|
||||
* (0 if not an executable, any other number if file is executable
|
||||
* with a known starting address)
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, negative number on error
|
||||
*/
|
||||
short fsys_srec_loader(short chan, long destination, long * start) {
|
||||
short n, i, chksum, data_index;
|
||||
long address;
|
||||
unsigned char count,data;
|
||||
unsigned char addr[4];
|
||||
char line[FYS_SREC_MAX_LINE_LENGTH];
|
||||
unsigned char * dest;
|
||||
|
||||
TRACE("fsys_srec_loader");
|
||||
|
||||
/* TODO: verify the check sum */
|
||||
|
||||
while (1) {
|
||||
chksum = 0;
|
||||
count = 0;
|
||||
n = sys_chan_readline(chan, line, FYS_SREC_MAX_LINE_LENGTH);
|
||||
if (n < 0) {
|
||||
/* If there was an error, return it */
|
||||
return n;
|
||||
|
||||
} else if (n == 0) {
|
||||
/* If we got nothing back, we're finished */
|
||||
break;
|
||||
|
||||
} else if (n > 6) {
|
||||
/* Only process the line if it starts with S and has more than six characters */
|
||||
if (line[0] = 'S') {
|
||||
/* Get the count and start the check sum */
|
||||
count = atoi_hex(&line[1]);
|
||||
chksum = count;
|
||||
|
||||
/* Determine how to process the line based on its type */
|
||||
switch (line[1]) {
|
||||
case '1':
|
||||
/* 16-bit data line: S1nnaaaadd...ddcc */
|
||||
addr[1] = atoi_hex(&line[4]);
|
||||
addr[0] = atoi_hex(&line[6]);
|
||||
dest = (unsigned char *)(addr[1] << 8 | addr[0]);
|
||||
data_index = 8;
|
||||
count -= 2;
|
||||
chksum += addr[1] + addr[0];
|
||||
|
||||
/* Copy the data */
|
||||
for (i = 0; i < count * 2; i += 2) {
|
||||
data = atoi_hex(&line[data_index + i]);
|
||||
chksum += data;
|
||||
*dest++ = data;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '2':
|
||||
/* 24-bit address data line: S2nnaaaaaadd..ddcc */
|
||||
addr[2] = atoi_hex(&line[4]);
|
||||
addr[1] = atoi_hex(&line[6]);
|
||||
addr[0] = atoi_hex(&line[8]);
|
||||
dest = (unsigned char *)(addr[2] << 16 | addr[1] << 8 | addr[0]);
|
||||
data_index = 8;
|
||||
count -= 2;
|
||||
chksum += addr[2] + addr[1] + addr[0];
|
||||
|
||||
/* Copy the data */
|
||||
for (i = 0; i < count * 2; i += 2) {
|
||||
data = atoi_hex(&line[data_index + i]);
|
||||
chksum += data;
|
||||
*dest++ = data;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '3':
|
||||
/* 32-bit address data line: S2nnaaaaaaaadd..ddcc */
|
||||
addr[3] = atoi_hex(&line[4]);
|
||||
addr[2] = atoi_hex(&line[6]);
|
||||
addr[1] = atoi_hex(&line[8]);
|
||||
addr[0] = atoi_hex(&line[10]);
|
||||
dest = (unsigned char *)(addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]);
|
||||
data_index = 8;
|
||||
count -= 2;
|
||||
chksum += addr[3] + addr[2] + addr[1] + addr[0];
|
||||
|
||||
/* Copy the data */
|
||||
for (i = 0; i < count * 2; i += 2) {
|
||||
data = atoi_hex(&line[data_index + i]);
|
||||
chksum += data;
|
||||
*dest++ = data;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '5':
|
||||
/* 16-bit starting address */
|
||||
addr[1] = atoi_hex(&line[4]);
|
||||
addr[0] = atoi_hex(&line[6]);
|
||||
*start = (long)(addr[1] << 8 | addr[0]);
|
||||
chksum += addr[2] + addr[1] + addr[0];
|
||||
break;
|
||||
|
||||
case '6':
|
||||
/* 24-bit starting address */
|
||||
addr[2] = atoi_hex(&line[4]);
|
||||
addr[1] = atoi_hex(&line[6]);
|
||||
addr[0] = atoi_hex(&line[8]);
|
||||
*start = (long)(addr[2] << 16 | addr[1] << 8 | addr[0]);
|
||||
chksum += addr[2] + addr[1] + addr[0];
|
||||
break;
|
||||
|
||||
case '7':
|
||||
/* 32-bit starting address */
|
||||
addr[3] = atoi_hex(&line[4]);
|
||||
addr[2] = atoi_hex(&line[6]);
|
||||
addr[1] = atoi_hex(&line[8]);
|
||||
addr[0] = atoi_hex(&line[10]);
|
||||
*start = (long)(addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]);
|
||||
chksum += addr[3] + addr[2] + addr[1] + addr[0];
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Ignore any other kind of line */
|
||||
count = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we get here, we should have loaded the file successfully */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load a file into memory at the designated destination address.
|
||||
*
|
||||
|
@ -1026,7 +1034,7 @@ short fsys_init() {
|
|||
}
|
||||
|
||||
/* Register the built-in binary file loaders */
|
||||
fsys_register_loader("PRS", fsys_srec_loader);
|
||||
fsys_register_loader("PGZ", fsys_pgz_loader);
|
||||
fsys_register_loader("PGX", fsys_pgx_loader);
|
||||
|
||||
/* Register the channel driver for files. */
|
||||
|
|
2986
src/foenixmcp.s68
2986
src/foenixmcp.s68
File diff suppressed because it is too large
Load diff
7137
src/mapfile
7137
src/mapfile
File diff suppressed because it is too large
Load diff
27
utilities/srecpgz/README.md
Normal file
27
utilities/srecpgz/README.md
Normal file
|
@ -0,0 +1,27 @@
|
|||
# SRECPGZ
|
||||
|
||||
This project is a simple C program to convert Motorola SREC hex files to Foenix PGZ executable binaries.
|
||||
|
||||
## Usage:
|
||||
|
||||
The program takes one option and two required path parameters:
|
||||
1. The `--large` switch if present generates a 32-bit PGZ file. If not, the old 24-bit PGZ format will be used.
|
||||
1. The first path is the input SREC file
|
||||
2. The second path is the output binary file.
|
||||
|
||||
```
|
||||
srecpgz [--large] <input srec file> <output bin file>
|
||||
```
|
||||
|
||||
## PGZ Format
|
||||
|
||||
The output format will depend on whether or not the `--large` option was used. By default, 24-bit address and count fields are used.
|
||||
If the `--large` option is enabled, 32-bit address and count fields are used.
|
||||
|
||||
* The first byte is the header or signature byte. For 24-bit PGZ, an uppercase 'Z' is used. For 32-bit PGZ, a lowercase 'z' is used.
|
||||
* After the signature can come any number of blocks. Each block has two mandatory fields, and an optional data field:
|
||||
1. The first field is the address of the block in memory. The address is specified in 24-bit or 32-bit, little endian format.
|
||||
1. The second field is the size of the data block. The size is specified in 24-bit 32-bit, little endian format.
|
||||
1. If the size (_n_) is non-zero, immediately after the size field are _n_ bytes of data. These are the data bytes to be loaded into that address block of memory.
|
||||
* Each block immediately follows the one before it. So if a block has a size of zero, the next block starts immediately after the last size byte. If the block has a non-zero size, the next block starts immediately after the data field.
|
||||
* A block with a size of 0 and no data field specifies the starting address for the executable (the address field specifies the starting address). At least one starting address block must be contained in the PGZ file for it to be executable. If more than one starting address block is present, the last starting address block is taken to be the correct starting address.
|
BIN
utilities/srecpgz/hello.pgz
Normal file
BIN
utilities/srecpgz/hello.pgz
Normal file
Binary file not shown.
4
utilities/srecpgz/hello.s37
Normal file
4
utilities/srecpgz/hello.s37
Normal file
|
@ -0,0 +1,4 @@
|
|||
S00B00007365673130303030C4
|
||||
S3250001000070137200243C00010014760E4E4F700072004E4F48656C6C6F2C20776F726C6467
|
||||
S307000100202100B6
|
||||
S70500010000F9
|
BIN
utilities/srecpgz/srecpgx
Normal file
BIN
utilities/srecpgz/srecpgx
Normal file
Binary file not shown.
363
utilities/srecpgz/srecpgx.c
Normal file
363
utilities/srecpgz/srecpgx.c
Normal file
|
@ -0,0 +1,363 @@
|
|||
/*
|
||||
* A simple utility to convert Motorola SREC to the Foenix PGZ file format
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_BUFFER 128
|
||||
|
||||
enum {
|
||||
STAT_ADDRESS_OVERFLOW = -3, /* The address field was too big for 24-bit format */
|
||||
STAT_COUNT_OVERFLOW = -2, /* The count field was too big for 24-bit format */
|
||||
STAT_GOOD = 0, /* The record is good and may be used */
|
||||
STAT_NO_RECORD, /* The line parsed contained no record or an ignored record type */
|
||||
STAT_EOF, /* There is no more data in the file */
|
||||
STAT_BAD_ADDRESS, /* There was a parse error in the address field */
|
||||
STAT_BAD_DATA, /* There was a parse error in the data field */
|
||||
STAT_BAD_CHKSUM, /* There was a parse error in the checksum field */
|
||||
STAT_MISMATCH_CHKSUM, /* The checksum does not match the line */
|
||||
STAT_ERROR /* General file read error */
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure to contain a record from the SREC file
|
||||
*/
|
||||
typedef struct s_srecord {
|
||||
short status;
|
||||
short type;
|
||||
short count;
|
||||
short binary_count;
|
||||
long address;
|
||||
char data[MAX_BUFFER];
|
||||
short checksum;
|
||||
} t_srecord, *p_srecord;
|
||||
|
||||
/* Size of the address and count fields: 0 = 24-bit, 1 = 32-bit */
|
||||
short use_32bits = 0;
|
||||
|
||||
/*
|
||||
* Convert a hex digit to a binary number
|
||||
*/
|
||||
short hex_convert(char c) {
|
||||
if ((c >= '0') && (c <= '9')) {
|
||||
return (c - '0');
|
||||
} else if ((c >= 'a') && (c <= 'f')) {
|
||||
return (c - 'a' + 10);
|
||||
} else if ((c >= 'A') && (c <= 'F')) {
|
||||
return (c - 'A' + 10);
|
||||
} else {
|
||||
/* There was a problem... return -1 as the result */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a string of hex digits to a binary number
|
||||
*
|
||||
* Inputs:
|
||||
* raw = the source string
|
||||
* count = the number of characters to parse
|
||||
*
|
||||
* Returns:
|
||||
* the binary number read, -1 if there was an error
|
||||
*/
|
||||
long parse_hex(char * raw, short count) {
|
||||
int i;
|
||||
long result = 0;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
short n = hex_convert(raw[i]);
|
||||
if (n == -1) {
|
||||
return -1;
|
||||
} else {
|
||||
result = result * 16 + n;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the data field
|
||||
*
|
||||
* Inputs:
|
||||
* raw = the source string of hex digits (must be the start of the data field)
|
||||
* data = the buffer to fill with the binary data
|
||||
* count = the number of chars to put into data
|
||||
*/
|
||||
short parse_data(char * raw, char * data, short count) {
|
||||
int i;
|
||||
for (i = 0; i < count; i++) {
|
||||
char * digits = &raw[2*i];
|
||||
long n = parse_hex(digits, 2);
|
||||
if (n == -1) {
|
||||
return -1;
|
||||
} else {
|
||||
data[i] = (char)(n & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a record from the input stream and fill it out
|
||||
*
|
||||
* Inputs:
|
||||
* fd = FILE pointer for the SREC file to read
|
||||
* r = pointer to the record structure to fill out
|
||||
*/
|
||||
void read_record(FILE * fd, p_srecord r) {
|
||||
char buffer[MAX_BUFFER];
|
||||
short n;
|
||||
int i;
|
||||
|
||||
r->status = STAT_NO_RECORD;
|
||||
char * result = fgets(buffer, MAX_BUFFER, fd);
|
||||
if (result == 0) {;
|
||||
r->status = STAT_EOF;
|
||||
|
||||
} else if (strlen(buffer) >= 10) {
|
||||
if ((buffer[0] == 'S') && isdigit(buffer[1]) && (buffer[1] >= '0') && (buffer[1] <= '9')) {
|
||||
r->status = STAT_GOOD;
|
||||
r->type = buffer[1] - '0';
|
||||
r->count = (short)parse_hex(&buffer[2], 2);
|
||||
r->checksum = (short)parse_hex(&buffer[strlen(buffer)-2], 2);
|
||||
|
||||
for (i = 0; i < MAX_BUFFER; i++) {
|
||||
r->data[i] = 0;
|
||||
}
|
||||
|
||||
switch (r->type) {
|
||||
case 1:
|
||||
/* Data record, 16-bit address */
|
||||
r->binary_count = r->count - 3;
|
||||
r->address = parse_hex(&buffer[4], 4);
|
||||
n = parse_data(&buffer[8], r->data, r->binary_count);
|
||||
if (n == -1) {
|
||||
r->status = STAT_BAD_DATA;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/* Data record, 24-bit address */
|
||||
r->binary_count = r->count - 4;
|
||||
r->address = parse_hex(&buffer[4], 6);
|
||||
n = parse_data(&buffer[10], r->data, r->binary_count);
|
||||
if (n == -1) {
|
||||
r->status = STAT_BAD_DATA;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
/* Data record, 32-bit address */
|
||||
r->binary_count = r->count - 5;
|
||||
r->address = parse_hex(&buffer[4], 8);
|
||||
n = parse_data(&buffer[12], r->data, r->binary_count);
|
||||
if (n == -1) {
|
||||
r->status = STAT_BAD_DATA;
|
||||
}
|
||||
break;
|
||||
|
||||
case 7:
|
||||
/* Start address, 32-bit */
|
||||
r->address = parse_hex(&buffer[4], 8);
|
||||
r->binary_count = 0;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
/* Start address, 24-bit */
|
||||
r->address = parse_hex(&buffer[4], 6);
|
||||
r->binary_count = 0;
|
||||
break;
|
||||
|
||||
case 9:
|
||||
/* Start address, 16-bit */
|
||||
r->address = parse_hex(&buffer[4], 4);
|
||||
r->binary_count = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Unsupported type, return null */
|
||||
r->status = STAT_NO_RECORD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Format the record for binary output
|
||||
*
|
||||
* Inputs:
|
||||
* out = FILE pointer for the output binary file
|
||||
* r = record to be output
|
||||
*/
|
||||
short write_record(FILE * out, p_srecord r) {
|
||||
int i;
|
||||
char buffer[MAX_BUFFER];
|
||||
short count_idx; /* offset to count field in binary output */
|
||||
short data_idx; /* offset to data field in binary output */
|
||||
|
||||
fprintf(stderr, "{type=%d, addr=%08x, count=%08x}\n", r->type, (int)r->address, (int)r->binary_count);
|
||||
|
||||
if (use_32bits) {
|
||||
count_idx = 4;
|
||||
data_idx = 8;
|
||||
} else {
|
||||
count_idx = 3;
|
||||
data_idx = 6;
|
||||
}
|
||||
|
||||
/* Clear out the buffer */
|
||||
for (i = 0; i < MAX_BUFFER; i++) {
|
||||
buffer[i] = 0;
|
||||
}
|
||||
|
||||
/* Set the address of the record */
|
||||
if (use_32bits) {
|
||||
buffer[3] = (char)((r->address >> 24) & 0xff);
|
||||
} else if ((r->address >> 24) != 0) {
|
||||
return STAT_ADDRESS_OVERFLOW;
|
||||
}
|
||||
buffer[2] = (char)((r->address >> 16) & 0xff);
|
||||
buffer[1] = (char)((r->address >> 8) & 0xff);
|
||||
buffer[0] = (char)((r->address) & 0xff);
|
||||
|
||||
/* Set the count of the record */
|
||||
if (use_32bits) {
|
||||
buffer[count_idx+3] = (char)((r->binary_count >> 24) & 0xff);
|
||||
} else if ((r->binary_count >> 24) != 0) {
|
||||
return STAT_COUNT_OVERFLOW;
|
||||
}
|
||||
buffer[count_idx+2] = (char)((r->binary_count >> 16) & 0xff);
|
||||
buffer[count_idx+1] = (char)((r->binary_count >> 8) & 0xff);
|
||||
buffer[count_idx] = (char)((r->binary_count) & 0xff);
|
||||
|
||||
/* Fill out the data */
|
||||
for (i = 0; i < r->binary_count; i++) {
|
||||
buffer[data_idx+i] = r->data[i];
|
||||
}
|
||||
|
||||
int result = write(fileno(out), buffer, r->binary_count + data_idx);
|
||||
if (result == -1) {
|
||||
perror("Error writing file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
FILE * in_file;
|
||||
FILE * out_file;
|
||||
short in_file_arg;
|
||||
short out_file_arg;
|
||||
char signature[1];
|
||||
t_srecord r;
|
||||
int line_number = 0;
|
||||
int keep_going = 1;
|
||||
short n;
|
||||
|
||||
switch (argc) {
|
||||
case 3:
|
||||
in_file_arg = 1;
|
||||
out_file_arg = 2;
|
||||
use_32bits = 0;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (strcmp(argv[1], "--large") == 0) {
|
||||
in_file_arg = 2;
|
||||
out_file_arg = 3;
|
||||
use_32bits = 1;
|
||||
} else {
|
||||
fprintf(stderr, "Usage: srecpgx [--large] <inputfile> <outputfile>\n");
|
||||
exit(5);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Usage: srecpgx [--large] <inputfile> <outputfile>\n");
|
||||
exit(5);
|
||||
}
|
||||
|
||||
in_file = fopen(argv[in_file_arg], "r");
|
||||
if (!in_file) {
|
||||
fprintf(stderr, "Could not open input file (%d).\n", errno);
|
||||
exit(6);
|
||||
}
|
||||
|
||||
out_file = fopen(argv[out_file_arg], "w");
|
||||
if (!out_file) {
|
||||
perror("Could not open output file");
|
||||
exit(7);
|
||||
}
|
||||
|
||||
/* Write the signature... use a lower case 'z' to distinguish */
|
||||
if (use_32bits) {
|
||||
signature[0] = 'z';
|
||||
} else {
|
||||
signature[0] = 'Z';
|
||||
}
|
||||
if (write(fileno(out_file), signature, 1) == -1) {
|
||||
perror("Error writing the output file");
|
||||
exit(9);
|
||||
}
|
||||
|
||||
while (keep_going) {
|
||||
read_record(in_file, &r);
|
||||
line_number++;
|
||||
switch (r.status) {
|
||||
case STAT_GOOD:
|
||||
n = write_record(out_file, &r);
|
||||
if (n == -1) {
|
||||
fprintf(stderr, "Error writing the output file on line %d", line_number);
|
||||
exit(10);
|
||||
} else if (n == STAT_ADDRESS_OVERFLOW) {
|
||||
fprintf(stderr, "Address too big for 24-bit binary on line %d", line_number);
|
||||
exit(11);
|
||||
} else if (n == STAT_COUNT_OVERFLOW) {
|
||||
fprintf(stderr, "Count too big for 24-bit binary on line %d", line_number);
|
||||
exit(12);
|
||||
}
|
||||
break;
|
||||
|
||||
case STAT_ERROR:
|
||||
/* There was an error reading the file */
|
||||
fprintf(stderr, "Error reading the input file on line %d.\n", line_number);
|
||||
exit(8);
|
||||
|
||||
case STAT_EOF:
|
||||
/* End of file... stop looping, close files, and quit */
|
||||
fprintf(stderr, "d");
|
||||
keep_going = 0;
|
||||
break;
|
||||
|
||||
case STAT_BAD_ADDRESS:
|
||||
fprintf(stderr, "Bad address on line %d.\n", line_number);
|
||||
exit(1);
|
||||
|
||||
case STAT_BAD_DATA:
|
||||
fprintf(stderr, "Bad data on line %d.\n", line_number);
|
||||
exit(2);
|
||||
|
||||
case STAT_BAD_CHKSUM:
|
||||
fprintf(stderr, "Bad checksum on line %d.\n", line_number);
|
||||
exit(3);
|
||||
|
||||
case STAT_MISMATCH_CHKSUM:
|
||||
fprintf(stderr, "Checksum did not match on line %d.\n", line_number);
|
||||
exit(4);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(in_file);
|
||||
fclose(out_file);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue