/*
 * Program to save/restore/verify the CMOS data of an AT BIOS.
 *
 * Copyright 1991-1994 Dave Dunfield
 * All rights reserved.
 *
 * Compile with: cc cmos -fop
 */
#include <stdio.h>

#define MAX_SIZE 128-0x10	/* Maximum potential size of CMOS ram */

unsigned char buffer1[MAX_SIZE], buffer2[MAX_SIZE], verbose = -1;

int size = 32, ignore[50]; icount = 0;

char *help[] = {
	"\nUse: CMOS <command> <filename> [options]\n",
	"command: Save    - Save CMOS data to a file",
	"         Restore - Recover CMOS data from file",
	"         Verify  - Compare CMOS data to file\n",
	"options: -Q      - QUIET, inhibit messages",
	"         I=value - Ignore byte at this offset (Verify)",
	"         S=value - Bytes of CMOS to SAVE  (default 32)\n",
	"Copyright 1991 Dave Dunfield",
	"All rights reserved",
	0 };

/*
 * Main program
 */
main(argc, argv)
	int argc;
	char *argv[];
{
	int i;
	char *ptr;
	FILE *fp;

	if(argc < 3) {
		for(i = 0; help[i]; ++i) {
			fputs(help[i], stderr);
			putc('\n', stderr); }
		return; }

	for(i=3; i < argc; ++i) {
		ptr = argv[i];
		switch((*ptr++ << 8) | *ptr++) {
				case '-q' :		/* Request QUIET mode */
				case '-Q' :
					verbose = 0;
					break;
				case 's=' :		/* Specify size to save */
				case 'S=' :
					size = atoi(ptr);
					break;
				case 'i=' :		/* Specify IGNORE bytes */
				case 'I=' :
					ignore[icount++] = atoi(ptr);
					break;
				default:
					fputs("Invalid option: ", stderr);
					fputs(argv[i], stderr);
					return; } }

	switch(*argv[1]) {
		case 's' :		/* Save CMOS data to a file */
		case 'S' :
			read_cmos(buffer1, 0x1000+size);
			fp = fopen(argv[2], "wvqb");
			fwrite(buffer1, size, fp);
			fclose(fp);
			message("CMOS data saved");
			break;
		case 'r' :		/* Restore CMOS data from a file */
		case 'R' :
			fp = fopen(argv[2], "rvqb");
			size = fread(buffer1, MAX_SIZE, fp);
			fclose(fp);
			write_cmos(buffer1, 0x1000+size);
			message("CMOS data restored");
			break;
		case 'v' :		/* Verify CMOS data with file */
		case 'V' :
			fp = fopen(argv[2], "rvqb");
			size = fread(buffer2, MAX_SIZE, fp);
			fclose(fp);
			read_cmos(buffer1, 0x1000+size);
			for(i=0; i < icount; ++i)
				buffer1[ignore[i]] = buffer2[ignore[i]] = 0;
			for(i=0; i < size; ++i) {
				if(buffer1[i] != buffer2[i]) {
					message("CMOS verify FAILED!!!");
					exit(-1); } }
			message("CMOS Data verified");
			break;
		default:			/* Invalid command */
			fputs("Unknown command: ", stderr);
			fputs(argv[1], stderr);
			return; }
}

/*
 * Issue message to used only if "verbose" enabled
 */
message(string)
	char *string;
{
	if(verbose)
		fputs(string, stdout);
}

/*
 * Read data from the CMOS ram
 */
read_cmos(dest, addr_size) asm
{
		MOV		BX,6[BP]		; Get destination
		MOV		CX,4[BP]		; Get start/size
		CLI						; Disallow interruptions
rdloop:	MOV		AL,CH			; Get address
		OUT		70h,AL			; Write it
		INC		CH				; Advance address
		IN		AL,71h			; Read the data
		MOV		[BX],AL			; Write it in dest
		INC		BX				; Advance destination
		DEC		CL				; Reduce count
		JNZ		rdloop			; And continue
		STI						; Allow interruptions
}

/*
 * Write data to the CMOS ram
 */
write_cmos(source, addr_size) asm
{
		MOV		BX,6[BP]		; Get destination
		MOV		CX,4[BP]		; Get start/size
		CLI						; Disallow interruptions
wrloop:	MOV		AL,CH			; Get address
		OUT		70h,AL			; Write it
		MOV		AL,[BX]			; Get value to write
		OUT		71h,AL			; Write it
		INC		CH				; Advance address
		INC		BX				; Advance destination
		DEC		CL				; Reduce count
		JNZ		wrloop			; And continue
		STI						; Allow interruptions
}
