/* This program modifies an Atari XL OS ROM image by adding the necessary
   checksums to it.
   The checksums stored in an XL OS ROM are necessary for the computer to run,
   as the system's coldstart routine uses them to verify the ROM's correctness
   (see the VFR and VSR routines). If they son't match, the OS jumps to Self
   Test.
   The checksums are also verified by the SALT cartridges' OS ROM tests. */

#include <stdlib.h>
#include <stdio.h>

enum {
	ROM_SIZE = 0x4000, /* Size of the ROM image in bytes. */
	ROM_START = 0xc000 /* Address of the ROM's first byte in the Atari
	                      computer's address space. */
};

/* A checksum is computed by sequentially summing up all bytes of the ROM area
   (skipping the location of the checksum itself), ignoring any carry bits. */
void write_checksum(unsigned char rom[ROM_SIZE],
		    unsigned int start, unsigned int end,
		    unsigned int lsb, unsigned int msb)
{
	unsigned int i;
	unsigned int sum = 0;

	start -= ROM_START;
	end -= ROM_START;
	lsb -= ROM_START;
	msb -= ROM_START;

	for (i = start; i <= end; ++i) {
		if (i != lsb && i != msb)
			sum += rom[i];
	}

	rom[lsb] = sum & 0xff;
	rom[msb] = (sum >> 8) & 0xff;
}

int main(int argc, char *argv[])
{
	FILE *f;
	unsigned char rom[ROM_SIZE];
	if (argc < 3) {
		fprintf(stderr, " Usage: %s <input filename> <output filename>\n", argv[0]);
		return EXIT_FAILURE;
	}

	/* Read ROM image. */
	f = fopen(argv[1], "rb");
	if (f == NULL) {
		fprintf(stderr, "Can't open %s\n", argv[1]);
		return EXIT_FAILURE;
	}
	if (fread(rom, sizeof(char), ROM_SIZE, f) != ROM_SIZE) {
		fprintf(stderr, "Can't read %s or file length not equal to %04x bytes\n", argv[1], ROM_SIZE);
		return EXIT_FAILURE;
	}
	if (fclose(f) != 0) {
		fprintf(stderr, "Can't close %s\n", argv[1]);
		return EXIT_FAILURE;
	}

	/* The XL OS ROM contains two 16-bit checksums, one for each half of
	   the ROM.

	   Checksum of the first 8K ROM half is located at 0xc000-0xc001. */
	write_checksum(rom, 0xc000, 0xdfff, 0xc000, 0xc001);

	/* Checksum of the first 8K ROM half is located at 0xfff8-0xfff9. */
	write_checksum(rom, 0xe000, 0xffff, 0xfff8, 0xfff9);

	/* Save modified ROM image. */
	f = fopen(argv[2], "wb");
	if (f == NULL) {
		fprintf(stderr, "Can't open %s\n", argv[2]);
		return EXIT_FAILURE;
	}
	if (fwrite(rom, sizeof(char), ROM_SIZE, f) != ROM_SIZE) {
		fprintf(stderr, "Can't write %s\n", argv[2]);
		return EXIT_FAILURE;
	}
	if (fclose(f) != 0) {
		fprintf(stderr, "Can't close %s\n", argv[2]);
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}
