/*
 *  TEX Device Driver  ver 2.02-
 *  (c) copyright 1988, 1989 by TSG, 1990-93 by SHIMA
 *
 *  buffer.c :
 *  Apr. 15, 1989 : first edition
 *  Jul.  1, 1989 : 2nd edition
 *                      programed by T.IWAI
 *	modfied for non PC-9801 machines by sempa 1992
 *	very slightly modified against warnings by Oh-Yeah? 25 May 1992
 *  support EMS by SHIMA,  Dec 1992
 */

#include <stdio.h>
#include <dos.h>
#include <io.h>
#include <fcntl.h>
#include <sys\stat.h>
#define _DEF_STDIO_H_
#include <string.h>
#define _DEF_STRING_H_
#include "dd.h"
#if defined(TURBOC) || defined(MSC)
#ifndef _DEF_DOS_H_
#include <dos.h>
#define _DEF_DOS_H_
#endif
#endif
#include "err.h"
#include "buffer.h"

#ifdef TURBOC
#include <alloc.h>
#define BUFMALLOC(n)    farmalloc(n)
#define HUGEMALLOC(n)   farmalloc(n)
#elif defined(MSC)
#include <malloc.h>
#define BUFMALLOC(n)    halloc(n,1)
#define HUGEMALLOC(n)   halloc(n,1)
#elif defined(x68k)
#define BUFMALLOC(n)    malloc(n)
#define HUGEMALLOC(n)   malloc(n)
#endif

/* loadpk.c */
int file_read(int, BUFFER *, int);
void flush_pk(void);

/* pret.c */
extern int f_break_bitmap;

extern int f_get_pk;
extern int r_b_f_count;
extern long sp_per_pxl;
extern char *goth;
extern BOOL k_16;
extern int k_scale;
extern int k_base;
extern int sum_option;

#ifdef HIRES98
extern int hireso;
#endif

int p_b_f_count;
int b_b_f_count;
char *fc_name;
static int fc_id = ('f' + 'c' + '_' + 'i' + 'd');
static uchar *heap_buffer;

static void buffer_alloc(BUF_INFO *, BUFFER *(*)());
static void set_buffer(BUF_INFO *);
void buffer_init(void);
void pk_buf_flush(void);
char *marea(unsigned int);
void clear_buf(BUF_INFO *);
char *dup_string(char *);
FONT_INFO *font_info_alloc(void);
ulong leftbuffer(void);
void file_write(int, BUFFER *, int);
void load_fc(void);
void save_fc(void);
long check_sum(void);
int free_buffer(int);
void recover_buffer(void);
static void buf_write(int, BUF_INFO *);
static int buf_read(int, BUF_INFO *);

#define omit_divide(a,b) ( ( (a)/(b) ) * (b) )
/* ｂで割り切れるようにする（端数切捨て）*/

#ifndef NEMS
#include "emslib.c"

int	total_EMS_page;
int system_EMS_end;
int max_user_page;			/* = total_EMS_page - system_EMS_end */
int f_cont_EMS;

int initialize_ems( int, int );
int set_ems( int );
int alloc_ems( int, int, int, int, int );
int copy_to_orig( uint, uint, uint, uint, long );
int copy_to_user( uint, uint, uint, uint, long );
int copy_to_page( uint, uint, uint, long );
void release_ems( void );

/*
 *   min_page >= 4
 */
int initialize_ems( int min_page, int max_page )
{
	int	pages;
	static char *handle_name = "dviout  ";

	if( !emm_installed() || !get_page_frame_address( &pf_addr ) ) goto end;
	if( search_handle_name( handle_name, &(input_regs.x.dx) ) )
		deallocate_expanded_memory_pages();
	if( min_page > (pages = count_unallocated_pages()) ) goto end;
	if( max_page > pages ) max_page = pages;
	if( allocate_expanded_memory_pages( max_page, &(input_regs.x.dx) )
     && set_handle_name( handle_name )
     && set_ems( 0 ) )
		total_EMS_page = max_page;
end:
	return( total_EMS_page );
}

int set_ems( int page )
{
	int	i, f;

	if( page == log_to_phys_map[0].log ){
		if( f_cont_EMS ) return( TRUE );
	}
	else{
		i = 0;
		goto set;
	}
	f = 0;
	for( i=1; i<4; i++ ){
		if( ++page != log_to_phys_map[i].log ){
set:		f = 1;
			log_to_phys_map[i].log = page;
		}
	}
	f_cont_EMS = 1;
	if( !f ) return( TRUE );
	else	return( map_expand_multi_pages() );
}

static int s_page[4];
/*
 *  flag  0: save the current pages and allocate new pages
 *        1: allocate new pages
 *       -1: recover the original pages
 *
 *       0                    : keep the former page
 *      -1, -2, -3, -4        : original pages
 *       1,  2,  3,  4(, ...) : new pages
 */
int alloc_ems( int flag, int p0, int p1, int p2, int p3 )
{
	int	i;
	int	tmp[4];
	static int f_cont;

	if( flag >= 0 ){
		tmp[0] = p0;
		tmp[1] = p1;
		tmp[2] = p2;
		tmp[3] = p3;
		if( !flag ){
			for( i=0; i<4; i++ ) s_page[i] = log_to_phys_map[i].log;
			f_cont = f_cont_EMS;	/* save the flag for continuous pages */
		}
		for( i=0; i<4; i++ ){
			if( tmp[i] < 0 ) log_to_phys_map[i].log = s_page[-1-tmp[i]];
			else if( tmp[i] > 0 ){
				if( (log_to_phys_map[i].log = (tmp[i]+system_EMS_end-1) )
					>= total_EMS_page ) return( FALSE );
			}
		}
		f_cont_EMS = 0;			/* clear the flag */
	}
	else{
		for( i=0; i<4; i++ ){
			log_to_phys_map[i].log = s_page[i];
		}
		f_cont_EMS = f_cont;	/* recover the flag */
	}
	return( map_expand_multi_pages() );
}

static void chk_adr( uint *adr_seg, uint *adr_off )
{
	uint	i;
	long	off;

	off = (uchar huge *) MK_FP( *adr_seg, *adr_off ) - (uchar huge *)pf_addr;
	if( off >= 0 && off < 0x10000L ){
		for( i = 0; off >= 0x4000; i++, off -= 0x4000 );
		*adr_seg = i;
		*adr_off = off;
	}
}

int copy_to_orig( uint s_seg, uint s_off, uint d_seg, uint d_off,
	long length )
{
	chk_adr( &s_seg, &s_off );
	if( s_seg < 4 ) s_seg = log_to_phys_map[s_seg].log;
	chk_adr( &d_seg, &d_off );
	if( d_seg < 4 ) d_seg = s_page[d_seg];
 	return( move_expand_memory( s_seg, s_off, d_seg, d_off, length ) );
}

int copy_to_user( uint s_seg, uint s_off, uint d_seg, uint d_off,
	long length )
{
	chk_adr( &s_seg, &s_off );
	if( s_seg < 4 ) s_seg = log_to_phys_map[s_seg].log;
	chk_adr( &d_seg, &d_off );
	if( d_seg < 4 ) d_seg += system_EMS_end;
 	return( move_expand_memory( s_seg, s_off, d_seg, d_off, length ) );
}

int copy_to_page( uint s_off, uint d_page, uint d_off, long length )
{
	int		i;

	for( i = 0; s_off >= 0x4000; i++, s_off -= 0x4000 );
	for( ; d_off >= 0x4000; d_page++, d_off -= 0x4000 );
 	return( move_expand_memory( log_to_phys_map[i].log, s_off,
		d_page, d_off, length ) );
}

void release_ems( void )
{
	if( total_EMS_page ) deallocate_expanded_memory_pages();
}
#endif

uchar *get_near_work(uint size)
{
	if (heap_buffer) free(heap_buffer);
	if (size != 0 && size <= coreleft()){
		heap_buffer = malloc(size);		/* get from near heap */
		return(heap_buffer);
	}
	return(heap_buffer = NULL);
}

BUFFER *get_work(uint size)
{
	if (size <= COMMON_SIZE)
		return(common_work);				/* common_work[] in near area */
	if (size <= work_buf_size)
		return(work_buf_addr);				/* Working buffer in far area */
#ifndef	NEMS
	if (max_user_page >= 4){
		alloc_ems( 0, 1, 2, 3, 4 );
		return(pf_addr);					/* EMS */
	}
#endif
	return(get_near_work(size));			/* get from near heap */
}

static BUFFER *
    bufmalloc(long size)
	/* BUFFER用のメモリー確保
     */
{
	return ((BUFFER *)BUFMALLOC(size));
}

static BUFFER *
    hugemalloc(long size)
	/* HUGE_BUF用のメモリー確保
     */
{
	return ((BUFFER *)HUGEMALLOC(size));
}

static void set_buffer(BUF_INFO *buf)
{
#ifndef	NEMS
	if( buf->type & f_EMS ){
		buf->ems_top = system_EMS_end;
		system_EMS_end = (buf->ems_last += buf->ems_top);
		max_user_page = total_EMS_page - system_EMS_end;
		buf->ems_start = pf_addr;
		if( buf->ems_last - buf->ems_top >= 4 ){
			(HUGE_BUF*)buf->ems_end = (HUGE_BUF*)buf->ems_start + 0x10000L;
		}
		else{
			(HUGE_BUF*)buf->ems_end =
			(HUGE_BUF*)buf->ems_start + 0x4000L*(buf->ems_last - buf->ems_top);
		}
	}
	if( buf->size == 0 ){
		buf->ems_bank = buf->ems_top;
		buf->start = buf->ems_start;
		buf->end   = buf->ems_end;
	}
	else{
#endif
	if (buf->type & f_HUGE)
		(HUGE_BUF *)buf->end =
		(HUGE_BUF *)buf->start + buf->size;
	else
		buf->end = buf->start + (uint)buf->size;
#ifndef	NEMS
	}
#endif
	buf->current = buf->start;
}

static void buffer_alloc(BUF_INFO *buf, BUFFER *(*alloc) (long size))
	/* size分のbufferを確保し，セットする．
     */
{
	ENTER("buffer_alloc");

	if (buf->size != 0 && (buf->start = (*alloc) (buf->size)) == NULL)
		error(NO_MEMORY,
#ifdef	JAPANESE
			  "バッファ領域の不足"
#else
			  "Cannot get Buffer"
#endif
			);

	set_buffer(buf);
	END();
}

void buffer_init(void)
	/*  バッファ領域の確保．この前に，各BUF_INFOのsizeは，設定
     *  されていること．
     */
{
	union REGS regs;

	ENTER("buffer_init");

	buffer_alloc(raster_buf_pointer, hugemalloc);

#ifdef	PC9801
	/* if( pk_buf_pointer->size <= 0 ){ */
#ifdef HIRES98
	if (hireso == 0 && pk_buf_pointer->size <= 0) {
#else
	if (pk_buf_pointer->size <= 0) {
#endif
		/*
		 *	バッファをＧＶＲＡＭの２−３画面にとる
		 */
		if (pk_buf_pointer->size == -2) {
			pk_buf_pointer->start = (BUFFER *)0xa8000008L;
			pk_buf_pointer->size = 0x17ff0L;
		}
		else {
			pk_buf_pointer->start = (BUFFER *)0xb0000008L;
			pk_buf_pointer->size = 0xfff0L;
		}
		set_buffer(pk_buf_pointer);
		regs.h.ah = 0x41;
		int86(0x18, &regs, &regs);	/* stop graphic display */
	}
	else {
		buffer_alloc(pk_buf_pointer, hugemalloc);
	}
#else
	buffer_alloc(pk_buf_pointer, hugemalloc);
#endif
	recover_buffer();
	load_fc();
	END();
}

void load_fc(void)
{
	BUF_INFO *bufc;
	struct FONT_INFO_REC font_info;
	FONT_INFO *font;
	int i, fh;

	struct FC_HEAD {
		char goth_top;
		int k16;
		int kscale;
		int kbase;
		int option;
		long chksum;
		long sp_pxl;
		BUF_INFO bufo[3];
	}
	fc_head;

	if (fc_name == NULL ||
		(fh = open(fc_name, O_RDONLY | O_BINARY)) == -1)
		return;

	read(fh, &fc_head, sizeof(struct FC_HEAD));

	if (fc_head.goth_top != *goth
		|| fc_head.sp_pxl != sp_per_pxl
		|| fc_head.k16 != k_16
		|| fc_head.kscale != k_scale
		|| fc_head.kbase != k_base
		|| fc_head.option != sum_option
		|| fc_head.chksum != check_sum())
		goto quit;

	bufc = raster_buf_pointer;

	for (i = 0; i < 3; bufc++, i++)
		if (bufc->start != fc_head.bufo[i].start
		    || bufc->type  != fc_head.bufo[i].type
		    || bufc->size  != fc_head.bufo[i].size )
			goto quit;

	for (font = first_font_info; font; font = font->next_font) {
		read(fh, &font_info, sizeof(FONT_INFO));

		if (font_info.font_code != font->font_code
			|| font_info.next_font != font->next_font
			|| font_info.c != font->c
			|| font_info.name != font->name)
			goto quit;
	}

	lseek(fh, (long)(sizeof(struct FC_HEAD)), SEEK_SET);

	for (font = first_font_info; font; font = font->next_font) {
		read(fh, &font_info, sizeof(FONT_INFO));

		for (i = 0; i < CHAR_ROOT; i++)
			font->char_info[i] = font_info.char_info[i];
		font->f_goth = font_info.f_goth;
		font->k_top = font_info.k_top;
		font->k_next = font_info.k_next;
	}

	raster_buf_pointer->current = fc_head.bufo[0].current;

	if (buf_read(fh, raster_buf_pointer) != 0
		|| (read(fh, &i, sizeof(int)), i != fc_id)) {
		error(WARNING, "Bad file %s", fc_name);
		goto quit;
	}
	fprintf(stderr, "Loading Font Cache\n");
 quit:	close(fh);
}

static int buf_read(int fd, BUF_INFO *buf)
{
	long total;
	HUGE_BUF *ptr;
	uint size;
	int cond;

#ifndef	NEMS
	if( buf->size == 0 ){
		ptr = buf->ems_start;
		set_ems( buf->ems_top );
	}
	else ptr = buf->start;
	total = (HUGE_BUF *)(buf->current) - ptr;
#else
	total = (HUGE_BUF *)(buf->current) - (HUGE_BUF *)(ptr = buf->start);
#endif
	while (total > 0L) {
		size = (total > (unsigned)0xf000) ? 0xf000 : (uint)total;
		if ((cond = size - file_read(fd, (BUFFER *)ptr, size)) != 0)
			return (cond);
		total -= size;
		ptr += size;
	}
	return (0);
}

long check_sum(void)
{
	FONT_INFO *font;
	int i;
	long chksum;
	char *adr;

	chksum = 0;
	for (font = first_font_info; font; font = font->next_font) {
		i = 9;
		if (*(adr = font->name) == '*') {
			do {
				chksum += (*adr << (i-- & 15));
			} while (*adr++ || i >= 0);
		}
		while (*adr){
			chksum += (*adr++ << (i-- & 15));
		}
		chksum += font->size_para;
	}
	return (chksum);
}

void save_fc(void)
{
	int fh, i;
	FONT_INFO *font;
	struct FC_HEAD0 {
		char goth_top;
		int k16;
		int kscale;
		int kbase;
		int option;
		long chksum;
		long sp_pxl;
	}
	fc_head0;

	if (fc_name == NULL
		|| *fc_name == 0
		|| f_get_pk == 0
		|| r_b_f_count != 0
#ifndef	NEMS
	    || raster_buf_pointer->ems_top < raster_buf_pointer->ems_bank
		|| (raster_buf_pointer->size != 0 && raster_buf_pointer->ems_bank >= 0)
#endif
		)
		return;
	for (i = 0;; i++) {
		if ((fh = open(fc_name,
			O_RDWR | O_TRUNC | O_CREAT | O_BINARY, S_IREAD | S_IWRITE)) > 0)
			break;
		if (i == 1)
			return;
		flush_pk();
	}

	fc_head0.goth_top = *goth;
	fc_head0.k16 = (int)k_16;
	fc_head0.kscale = k_scale;
	fc_head0.kbase = k_base;
	fc_head0.option = sum_option;
	fc_head0.chksum = check_sum();
	fc_head0.sp_pxl = sp_per_pxl;

	write(fh, &fc_head0, sizeof(struct FC_HEAD0));
	write(fh, raster_buf_pointer, sizeof(BUF_INFO) * 3);

	for (font = first_font_info; font; font = font->next_font)
		file_write(fh, (BUFFER *)font, sizeof(FONT_INFO));

	buf_write(fh, raster_buf_pointer);
	write(fh, &fc_id, sizeof(int));

	fprintf(stderr, "\nSaved Font Cache");

	close(fh);
}

static void buf_write(int fd, BUF_INFO *buf)
{
	long total;
	HUGE_BUF *ptr;
	uint size;

#ifndef	NEMS
	if( buf->size == 0 ){
		ptr = buf->ems_start;
		set_ems( buf->ems_top );
	}
	else ptr = buf->start;
	total = (HUGE_BUF *)(buf->current) - ptr;
#else
	total = (HUGE_BUF *)(buf->current) - (HUGE_BUF *)(ptr = buf->start);
#endif
	while (total > 0L) {
		size = (total > (unsigned)0xf000) ? 0xf000 : (uint)total;
		file_write(fd, (BUFFER *)ptr, size);
		total -= size;
		ptr += size;
	}
}

static void file_write(int fd, BUFFER *ptr, int size)
	/* セグメント外に直接ライト
     */
{
	union REGS inregs, outregs;
	struct SREGS segregs;
	static char bad_fc[2] =
	{0xff, 0xff};

	ENTER("file_write");
	inregs.h.ah = 0x40;
	segregs.ds = FP_SEG(ptr);
	inregs.x.dx = FP_OFF(ptr);
	inregs.x.cx = size;
	inregs.x.bx = fd;

	intdosx(&inregs, &outregs, &segregs);

	if (outregs.x.cflag || (uint)outregs.x.ax != (uint)size) {
		lseek(fd, 0L, SEEK_SET);
		write(fd, bad_fc, 2);
		error(PROGRAM_STOP, "Cannot write font cache");
	}
	END();
}

void pk_buf_flush(void)
	/*  pk_buf　のフラッシュ．
     */
{
	FONT_INFO *font;

	ENTER("pk_buf_flush");

	for (font = first_font_info; font; font = font->next_font)
		font->pk = NULL;
	pk_buf_pointer->current = pk_buf_pointer->start;
	p_b_f_count++;
	END();
}

static int swap_bank = -1;
static int free_mode = 2;

int free_buffer(int mode)
{
	if (work_buf_pointer->size > 0 && !free_mode){
		free_mode = 1;
		farfree(work_buf_pointer->start);
	}

	if (mode == 0 || free_mode > 1) return(-1);
	b_b_f_count++;
	if (raster_buf_pointer->type & f_EMS){
		if (raster_buf_pointer->ems_bank < 0 )
			swap_bank = raster_buf_pointer->ems_top;
		else{
			swap_bank = raster_buf_pointer->ems_bank + 1 
				+ ( (HUGE_BUF*)raster_buf_pointer->current
				- (HUGE_BUF*)raster_buf_pointer->start )/0x4000;
		}
		if (bitmap_buf_pointer->size
			> 0x4000L*(raster_buf_pointer->ems_last - swap_bank)){
				swap_bank = -1;
		}
		else{
			move_expand_memory(
				FP_SEG(bitmap_buf_pointer->start),
				FP_OFF(bitmap_buf_pointer->start),
				swap_bank, 0, bitmap_buf_pointer->size);
			b_b_f_count--;
		}
	}
	farfree(bitmap_buf_pointer->start);
	free_mode = 2;
	f_break_bitmap = 1;
	return(swap_bank);
}


void recover_buffer(void)
{
	if (free_mode > 1){
		buffer_alloc(bitmap_buf_pointer, hugemalloc);
		if (swap_bank >= 0){
			move_expand_memory(
				swap_bank, 0,
				FP_SEG(bitmap_buf_pointer->start),
				FP_OFF(bitmap_buf_pointer->start),
				bitmap_buf_pointer->size);
			swap_bank = -1;
			f_break_bitmap = 0;
		}
	}
	if (free_mode) buffer_alloc(work_buf_pointer, hugemalloc);
	free_mode = 0;
}

FONT_INFO *
    font_info_alloc(void)
	/*  １つ分のフォントバッファを確保 */
{
	FONT_INFO *font;

	ENTER("font_info_alloc");

	if ((font = (FONT_INFO *)bufmalloc(sizeof(FONT_INFO))) == NULL)
		    error(NO_MEMORY,
#ifdef	JAPANESE
				  "フォントバッファ領域の不足"
#else
				  "Cannot get FONT Buffer"
#endif
		);

	RETURN(font);
}

char *
    marea(unsigned int size)
	/* エラーチェック付きmalloc */
{
	char *ptr;

	ENTER("marea");
	if (size == 0)
		size = 1;
rep:
	if ((ptr = (char *)malloc(size)) == NULL){
		if (heap_buffer != NULL){
			get_near_work(0);
			goto rep;
		}
		error(NO_MEMORY,
#ifdef	JAPANESE
			  "ヒープエリアの不足"
#else
			  "Not enough heap area"
#endif
			);
	}

	RETURN(ptr);
}

char *
    dup_string(char *src)
	/* エラーチェック付きstrdup */
{
	char *dst;

	ENTER("dup_string");

	if ((dst = strdup(src)) == NULL)
		error(NO_MEMORY,
#ifdef	JAPANESE
			  "ヒープエリアの不足"
#else
			  "Not enough heap area"
#endif
			);
	RETURN(dst);
}

#ifndef TURBOC
/*  Turbo-C用には，アセンブラーで組んだ関数を作った．("bufclr.asm")
     *  但し，Small size 用である．おそらく，MS-Cでもそのまま動くと思わ
     *  れる（確認はしていない）．スピードが10倍は速いだろう．
     */
void clear_buf(BUF_INFO *buf)
	/* バッファ クリアー
     * この関数はアセンブラーで書いた方がよい．
     */
{
	long size;
	HUGE_BUF *tmp;

	ENTER("clear_buf");

	for (tmp = buf->start, size = buf->size; size--; *tmp++ = 0);

	END();

}

#endif

#if defined(x68k)
void clear_buf(BUF_INFO *buf)
{
	memset(buf->start, 0, buf->size);
}

#endif

ulong leftbuffer(void)
{
	ENTER("leftbuffer");

#ifdef TURBOC
	RETURN(farcoreleft());
#else
	RETURN(0L);
#endif
}

/* end of file : buffer.c */
