/*
 * The routines in this module interface with
 * abios
 */
#include <dos.h>
#include <malloc.h>
#include "pcparam.h"
#include "abios_st.h"
#define E_FATAL	1
#define E_OK	0
#define NULL	0

#ifdef ABIOS

extern u_short *dbugptr;


/*
 * This routine initializes the abios functions
 * It is started in real mode because it must make
 * standard bios calls.
 */
struct	System_parameter_table	system_parameter_table;
u_short	real_anchor = 0;	/* address of the real anchor pointer
				 * to free the memory when we are done
				 */
struct Device_info	device_info[] = {
	{"ABIOS internal call",INIT,0,0,0,0},	/* 0x00 */
	{"diskette",INIT,0,0,0,0},		/* 0x01 */
	{"disk",INIT,0,0,0,0},			/* 0x02 */
	{"video",INIT,0,0,0,0},			/* 0x03 */
	{"keyboard",INIT,0,0,0,0},		/* 0x04 */
	{"parallel port",INIT,0,0,0,0},		/* 0x05 */
	{"async port",INIT,0,0,0,0},		/* 0x06 */
	{"system timer",INIT,0,0,0,0},		/* 0x07 */
	{"real-time clock",INIT,0,0,0,0},	/* 0x08 */
	{"system services",INIT,0,0,0,0},	/* 0x09 */
	{"NMI",INIT,0,0,0,0},			/* 0x0a */
	{"mouse",INIT,0,0,0,0},			/* 0x0b */
	{"reserved (0x0c)",INIT,0,0,0,0},	/* 0x0c */
	{"reserved (0x0d)",INIT,0,0,0,0},	/* 0x0d */
	{"NVRAM",INIT,0,0,0,0},			/* 0x0e */
	{"DMA",INIT,0,0,0,0},			/* 0x0f */
	{"POS",INIT,0,0,0,0},			/* 0x10 */
	{"reserved (0x11)",INIT,0,0,0,0},	/* 0x11 */
	{"reserved (0x12)",INIT,0,0,0,0},	/* 0x12 */
	{"reserved (0x13)",INIT,0,0,0,0},	/* 0x13 */
	{"reserved (0x14)",INIT,0,0,0,0},	/* 0x14 */
	{"reserved (0x15)",INIT,0,0,0,0},	/* 0x15 */
	{"keyboard security",INIT,0,0,0,0},	/* 0x16 */
	{"reserved (0x17)",INIT,0,0,0,0},	/* 0x17 */
};

int	known_device_ids=(sizeof(device_info)/sizeof(device_info[0]));

#ifdef NOMALLOC
/*
 * get around weird bugs in the dos malloc
 */
char	data_block[16*1024] = {0};
char	*dp=data_block;
#define	free(x)
#endif /* NOMALLOC */

abios_init()
{
	struct	System_init_table	*system_init_table;
	struct Function_transfer_table far *fft;
	struct Device_block far *db;
	int	number_logical_devices = 1;
	int	total_data_pointer_length = 0;
	int	i,j,size;
#ifdef DEBUG
	int	number_logical_ids;
#endif /* DEBUG */

	/* call bios to build the system parameter table */
	if (bldspt((char far *)&system_parameter_table)) {
		return(E_FATAL);
	}

	DEBUGF(*dbugptr & ABIOSDEBUG,printf("starting ABIOS:\n"););
	/* Now build the system initialization table */
#ifdef NOMALLOC
	system_init_table = (struct System_init_table *) dp;
	dp += system_parameter_table.number_of_entries*
					sizeof(struct System_init_table);
#else /* NOMALLOC */
	system_init_table = (struct System_init_table *) malloc(
	   			system_parameter_table.number_of_entries*
					sizeof(struct System_init_table));
	if (system_init_table == NULL ) {
		return(E_FATAL);
	}
#endif /* NOMALLOC */
	if (bldsit((char far *)system_init_table)) {
		free(system_init_table);
		return(E_FATAL);
	}

	DEBUGF(*dbugptr & ABIOSDEBUG,
		printf(" sys_init done, minstack = %d\n",
				system_parameter_table.min_stack);
	);

	/*
	 * Now we have to build the common data area. It must start on a 
	 * paragraph (segment) boundary. Its size is (ABIOS_HEAD+sizeof(struct 
	 * Logical_device)* number_of_logical_devices + data_pointers_size).
	 */
	/* get common data area size */
	for (i=0; i < system_parameter_table.number_of_entries; i++) {
		number_logical_devices += system_init_table[i].
							    number_logical_ids;
		total_data_pointer_length +=  system_init_table[i].
							   data_pointer_length;
	}
#ifdef DEBUG
	number_logical_ids=number_logical_devices;
#endif
	size = ABIOS_HEAD +
		(number_logical_devices * sizeof(struct Logical_device)) + 
					              total_data_pointer_length;
#ifdef NOMALLOC
	anchor_pointer = (struct Common_data_area_head far *) dp;
	bzero(dp,size+16);
	dp += size+16;
#else /* NOMALLOC */
	anchor_pointer = (struct Common_data_area_head far *) malloc(size+16);
	real_anchor = FP_OFF(anchor_pointer);
	bzero(real_anchor,size+16);
	if (real_anchor == NULL ) {
		free (system_init_table);
		return(E_FATAL);
	}
#endif /* NOMALLOC */
	ALIGN_SEGMENT(anchor_pointer);
	anchor_seg = FP_SEG(anchor_pointer);
	/* load data pointer 0 value */
	anchor_pointer->data_ptr_0 = size - sizeof(struct Data_pointer)
							    - sizeof(u_short);
	anchor_pointer->number_logical_ids = number_logical_devices;
	/* allocate Function Transfer Table and Data areas. */
	number_logical_devices = 2;
	for (i=0; i < system_parameter_table.number_of_entries; i++) {
		int	device_id = system_init_table[i].device_id;

		/* don't initalize unknown devices */
		if (device_id >= known_device_ids) {
			DEBUGF(*dbugptr & ABIOSDEBUG,
				printf("skipping unknown device_id=0x%x\n",
		   			device_id);
			);
			continue;
		}

		/* don't initalize know devices we don't need or want */
		if (device_info[device_id].init != INIT) {
			DEBUGF(*dbugptr & ABIOSDEBUG,
				printf("skipping %s device_id=0x%x\n",
		   			device_info[device_id].name,device_id);
			);
			continue;
		}

		/* allocate memory for function table and device block */
#ifdef NOMALLOC
		fft = (struct Function_transfer_table far *) dp;
		dp += system_init_table[i].function_transfer_table_length;
		db = (struct Device_block far *) dp;
		dp += system_init_table[i].device_block_length;
#else /* NOMALLOC */
		fft = (struct Function_transfer_table far *) malloc
			(system_init_table[i].function_transfer_table_length);
		db = (struct Device_block far *) malloc
				(system_init_table[i].device_block_length);
#endif /* NOMALLOC */

		DEBUGF(*dbugptr & ABIOSDEBUG,
		 printf("initing %s device_id=0x%x logical_id= 0x%x count=%d\n",
		   device_info[device_id].name,device_id,number_logical_devices,
				       system_init_table[i].number_logical_ids);
		);
		DEBUGF(*dbugptr & ABIOSDEBUG,
		 printf("	data pointers = %d\n",
		      system_init_table[i].data_pointer_length /
						sizeof(struct Data_pointer));
		);

		/* initialize the logical device pointers */
		for (j=0; j < system_init_table[i].number_logical_ids;j++) {
			logical_device(j+number_logical_devices).
							      device_block=db;
			logical_device(j+number_logical_devices).
					          function_transfer_table=fft;
		}

		/* have ABIOS fill in the proper fields */
		if (initfft(system_init_table[i].log_device_init,
		               FP_SEG(anchor_pointer),number_logical_devices,
				    system_init_table[i].number_logical_ids)) {
			/* device init failed */
#ifdef NOMALLOC
			dp -= system_init_table[i].
						function_transfer_table_length;
			dp -= system_init_table[i].device_block_length;
#else /* NOMALLOC */
			free (FP_OFF(fft));
			free (FP_OFF(db));
#endif /* NOMALLOC */
			device_info[device_id].init = NOINIT;
			DEBUGF(*dbugptr & ABIOSDEBUG,printf("init failed\n"););
		} else {
			device_info[device_id].fft = FP_OFF(fft);
			device_info[device_id].db = FP_OFF(db);
			device_info[device_id].logical_id = 
							number_logical_devices;
			device_info[device_id].device_count = 
				       system_init_table[i].number_logical_ids;
			number_logical_devices +=
					system_init_table[i].number_logical_ids;
		}
	}

	/*
	 * now convert the data pointer values to segment/offset
	 * this needs to be changes to go to protected mode.
	 */
	for (i=0; i < (total_data_pointer_length/sizeof(struct Data_pointer))
									; i++) {
		/* if address if above 1 Meg */
		if (data_pointer(i).dp_segment & 0xfff0) {
			data_pointer(i).dp_segment = 0;
			data_pointer(i).dp_offset = 0;
		} else {
			data_pointer(i).dp_segment = ((data_pointer(i).
						dp_segment & 0xf) << 12);
		}
	}
#ifdef NOMALLOC
	if (dp - data_block > sizeof(data_block)) {
		printf("OUT of MEMORY in ABIOS init!\n");
		return(E_FATAL);
	}
#endif /* NOMALLOC */
	DEBUGF(*dbugptr & ABIOSDEBUG,
		printcommon(anchor_pointer,number_logical_ids,
			total_data_pointer_length/sizeof(struct Data_pointer));
	);
	free(system_init_table);
	return(E_OK);
}

#ifdef DEBUG
printcommon(ptr,number_ids,d_count)
	u_char far *ptr;
	u_short number_ids;
	u_short d_count;
{
	int i;

	printf("********************************************************\n");
	printf("Anchor base addr = %lx\n",ptr);
	printf("data pointer 0 = %02x %02x\n",*ptr++,*ptr++);
	printf("logical id count=%02x %02x\n",*ptr++,*ptr++);
	printf("reserved        =%02x %02x %02x %02x\n",
						*ptr++,*ptr++,*ptr++,*ptr++);
	printf("*** Logical devices ***\n");
	for (i=1; i <= number_ids; i++) {
		printf(" device %02x DB=%02x %02x %02x %02x",
						i,*ptr++,*ptr++,*ptr++,*ptr++);
		printf(" FTT=%02x %02x %02x %02x\n",
						*ptr++,*ptr++,*ptr++,*ptr++);
	}
	printf("*** Data pointers ***\n");
	while (d_count--) {
		printf(" dptr %02x (%lx)",d_count,ptr);
 		printf(" length = %02x %02x ",*ptr++,*ptr++);
 		printf(" addr = %02x %02x %02x %02x\n",
						*ptr++,*ptr++,*ptr++,*ptr++);
	}
	printf("data ptr count = %02x %02x\n",*ptr++,*ptr++);
}
#endif /* DEBUG */

abios_return()
{
#ifndef NOMALLOC
	int	i;

	if (real_anchor != NULL) {
		free (real_anchor);
	}

	/*
	 * free all the function tables and device blocks.
	 */
	for (i=0; i < known_device_ids; i++) {
		if (device_info[i].fft)
			free(device_info[i].fft);
		if (device_info[i].db)
			free(device_info[i].db);
	}
#endif /* NOMALLOC */
	/* reset the disks */
	int13(0,0,0,0,0,0,0);
	int13(0,1,0,0,0,0,0);
	int13(0,0x80,0,0,0,0,0);
	int13(0,0x81,0,0,0,0,0);
}
#else	/* ABIOS */
#ifdef NOMALLOC
char	data_block[16*1024] = {0};
#endif /* NOMALLOC */
#endif  /* ABIOS */
