Files
Tobias Diedrich c868482669 Rename bootloader to main
The presence of the main function is a hardcoded trigger in sdcc that causes
the interrupt vectors and atomic rollback helpers to be generated as part of
compilation of the file containing the main function.

This makes sure the runtime library is correctly initialized and reduces
crtstart.asm to contain only the banked call helpers.

To ensure that the __interrupt_vect in area HOME is located at the beginning
of the firmware, the linker flags are updated to set the HOME start to
0x00000 and the linker object order is modified to have rtlplayground.rel
first.

Generated code in rtlplayground.asm:
```
;--------------------------------------------------------
; interrupt vector
;--------------------------------------------------------
	.area HOME    (CODE)
__interrupt_vect:
	ljmp	__sdcc_gsinit_startup
	ljmp	_isr_ext0
	.ds	5
	ljmp	_isr_timer0
	.ds	5
	ljmp	_isr_ext1
	.ds	5
	reti
	.ds	7
	ljmp	_isr_serial
	.ds	5
	ljmp	_isr_timer2
	.ds	5
	reti
	.ds	7
	reti
	.ds	7
	ljmp	_isr_ext2
	.ds	5
	ljmp	_isr_ext3
; restartable atomic support routines
	.ds	2
sdcc_atomic_exchange_rollback_start::
	nop
	nop
sdcc_atomic_exchange_pdata_impl:
	movx	a, @r0
	mov	r3, a
	mov	a, r2
	movx	@r0, a
	sjmp	sdcc_atomic_exchange_exit
	nop
	nop
sdcc_atomic_exchange_xdata_impl:
	movx	a, @dptr
	mov	r3, a
	mov	a, r2
	movx	@dptr, a
	sjmp	sdcc_atomic_exchange_exit
sdcc_atomic_compare_exchange_idata_impl:
	mov	a, @r0
	cjne	a, ar2, .+#5
	mov	a, r3
	mov	@r0, a
	ret
	nop
sdcc_atomic_compare_exchange_pdata_impl:
	movx	a, @r0
	cjne	a, ar2, .+#5
	mov	a, r3
	movx	@r0, a
	ret
	nop
sdcc_atomic_compare_exchange_xdata_impl:
	movx	a, @dptr
	cjne	a, ar2, .+#5
	mov	a, r3
	movx	@dptr, a
	ret
sdcc_atomic_exchange_rollback_end::

sdcc_atomic_exchange_gptr_impl::
	jnb	b.6, sdcc_atomic_exchange_xdata_impl
	mov	r0, dpl
	jb	b.5, sdcc_atomic_exchange_pdata_impl
sdcc_atomic_exchange_idata_impl:
	mov	a, r2
	xch	a, @r0
	mov	dpl, a
	ret
sdcc_atomic_exchange_exit:
	mov	dpl, r3
	ret
sdcc_atomic_compare_exchange_gptr_impl::
	jnb	b.6, sdcc_atomic_compare_exchange_xdata_impl
	mov	r0, dpl
	jb	b.5, sdcc_atomic_compare_exchange_pdata_impl
	sjmp	sdcc_atomic_compare_exchange_idata_impl
```
2026-03-01 17:35:13 +01:00

171 lines
3.9 KiB
C

/*
* Adds data files into specified locations of an image, optionally creates
* an index in the form of a header file
*/
#include <stdint.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <argp.h>
#include <stdbool.h>
#define OFFSET 2
#define BANK0_SIZE 0x4000
#define BANK_SIZE 0xc000
#define BANK_STRIDE 0x10000
// Use a 4MB buffer, the maximum flash rom size
#define BUFFER_SIZE 0x400000
uint8_t buffer[BUFFER_SIZE];
FILE *inptr;
int outptr;
const char *argp_program_version = "imagebuilder 0.1";
const char *argp_program_bug_address = "<git@logicog.de>";
static char doc[] = "Create an image with multiple banks for RTL837X-based switches";
static char args_doc[] = "INPUT_IMAGE";
static struct argp_option options[] = {
{ "input", 'i', "FILE", 0, "Input file"},
{ 0 }
};
struct arguments {
char *input_file;
};
static error_t parse_opt(int key, char *arg, struct argp_state *state)
{
struct arguments *arguments = state->input;
switch (key) {
case 'i':
arguments->input_file = arg;
break;
case ARGP_KEY_END:
if (state->arg_num < 1) // Expect 1 command line argument at end
argp_usage(state);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static struct argp argp = {
options, parse_opt, args_doc, doc, 0, 0, 0
};
int main(int argc, char **argv)
{
struct arguments arguments;
int arg_index;
arguments.input_file = NULL;
argp_parse(&argp, argc, argv, 0, &arg_index, &arguments);
memset(buffer, 0, BUFFER_SIZE);
size_t filesize = 0;
inptr = fopen(arguments.input_file, "rb");
if (inptr == NULL) {
printf("Cannot open input file %s\n", arguments.input_file);
return 5;
}
fseek(inptr, 0L, SEEK_END);
filesize = ftell(inptr);
rewind(inptr);
printf("Input file size: %ld\n", filesize);
if (filesize > BUFFER_SIZE) {
printf("File too large.\n");
return 5;
}
int nbanks = (filesize - BANK0_SIZE) / BANK_STRIDE;
printf("Input file contains %d banks\n", nbanks);
/* Verify the size of banks in input file:
* Bank 0: 0x00002 - 0x04000 <- 0x00000 - 0x03ffe
* Bank 1: 0x04000 - 0x10000 <- 0x14000 - 0x20000
* Bank 2: 0x10000 - 0x1c000 <- 0x24000 - 0x33000
* [...]
* by verifying whether other than 0-bytes are found in the input
* file after these ranges.
*/
size_t bytes_read = fread(buffer, 1, filesize, inptr);
if (bytes_read != filesize) {
printf("Error reading input file.\n");
return 5;
}
if (buffer[0] != 2) {
printf("Expected LJMP at offset 0.\n");
return 5;
}
for (int b = BANK0_SIZE; b < BANK_STRIDE + BANK0_SIZE; b ++) {
if (buffer[b]) {
printf("WARNING: Bank 0: code segment too large at 0x%x!\n", b);
break;
}
}
for (int bank = 1; bank <= nbanks; bank++) {
for (int b = (bank + 1) * BANK_STRIDE; b < (bank + 1) * BANK_STRIDE + BANK0_SIZE; b ++) {
if (buffer[b]) {
printf("WARNING: Bank %d: code segment too large at 0x%x!\n", bank, b);
break;
}
}
}
rewind(inptr);
memset(buffer, 0, BUFFER_SIZE);
// BANK0-Size
buffer[0] = 0x00;
buffer[1] = 0x40;
// Read bank 0
bytes_read = fread(buffer + OFFSET, 1, BANK0_SIZE, inptr);
printf("Bank 0: Bytes read: %ld\n", bytes_read);
if (bytes_read != BANK0_SIZE) {
printf("Error reading input file.\n");
return 5;
}
fseek(inptr, BANK_STRIDE, SEEK_CUR);
for (int bank = 1; bank <= nbanks; bank++) {
bytes_read = fread(buffer + (bank - 1) * BANK_SIZE + BANK0_SIZE, 1, BANK_SIZE, inptr);
printf("Bank %d: bytes read: %ld\n", bank, bytes_read);
if (bank != nbanks)
fseek(inptr, BANK0_SIZE, SEEK_CUR);
}
fclose(inptr);
outptr = creat(argv[arg_index], S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if (!outptr) {
printf("Cannot open %s\n", argv[arg_index]);
return 5;
}
size_t written = write(outptr, buffer, (nbanks + 1) * BANK_STRIDE);
if (written != (nbanks + 1) * BANK_STRIDE) {
printf("Error writing output file.\n");
return 5;
}
close(outptr);
return 0;
}