/* Copyright (C) 1989, 1992 Aladdin Enterprises.  All rights reserved.
   Distributed by Free Software Foundation, Inc.

This file is part of Ghostscript.

Ghostscript is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
to anyone for the consequences of using it or for whether it serves any
particular purpose or works at all, unless he says so in writing.  Refer
to the Ghostscript General Public License for full details.

Everyone is granted permission to copy, modify and redistribute
Ghostscript, but only under the conditions described in the Ghostscript
General Public License.  A copy of this license is supposed to have been
given to you along with Ghostscript so you can know your rights and
responsibilities.  It should be in a file named COPYING.  Among other
things, the copyright notice and this notice must be preserved on all
copies.  */

/* gp_pc98.c */
/* Part of DJ's GCC-specific routines for Ghostscript */
/* Machine dependent routines for NEC PC-9801 / H98 */

#include <pc.h>
#include "gx.h"
#include "dos_.h"
#include "memory_.h"
#include "gp_djgpp.h"
#include "djgpp.h"

private gp_djgpp_identify_proc(gp_pc9801_identify);
private gp_djgpp_open_printer_proc(gp_pc9801_open_printer);
private gp_djgpp_close_printer_proc(gp_pc9801_close_printer);
private gp_djgpp_alloc_buffer_proc(gp_pc9801_alloc_buffer);
private gp_djgpp_free_buffer_proc(gp_pc9801_free_buffer);
private gp_djgpp_flush_printer_proc(gp_pc9801_port_flush);
private gp_djgpp_flush_printer_proc(gp_pc9801_bios_flush);

gp_djgpp_system_procs gp_pc98_system_procs = {
  gp_printer_to_bios,
  gp_pc9801_identify,
  0, /* open console */
  0, /* close console */
  gp_pc9801_open_printer, 
  gp_pc9801_close_printer,
  gp_pc9801_alloc_buffer,
  gp_pc9801_free_buffer,
  gp_pc9801_port_flush,
  gp_pc9801_bios_flush,
};

private uint gp_pc9801_real_malloc(uint ,uint *);
private short gp_pc9801_real_free(uint );
private uint gp_pc9801_printer_buf_selecta;
private int gp_pc9801_pch98_f; /* High-Resolution mode ? */

private int
  gp_pc9801_identify(int oem_no)
{
#define KT 0x0502
#define KB ((KT)+16*2)
  ushort ks = *(ushort *)(PC_CONV_BASE + 0x0524);
  ushort ke = *(ushort *)(PC_CONV_BASE + 0x0526);
  uchar kc = *(uchar *)(PC_CONV_BASE + 0x0528);
  uchar func = *(uchar *)(PC_CONV_BASE + 0x600 + 0x0111);
  uchar scr = *(uchar *)(PC_CONV_BASE + 0x600 + 0x0112);
  uchar lm = *(uchar *)(PC_CONV_BASE + 0x600 + 0x0113);
  
  if (oem_no != 0xff) return 0; /* OEM serial must be '0xFF'. */
  
  if ((ks >= KT && ks < KB) && (ke >= KT && ke < KB)) {
    ushort kk = ks + kc * 2;
    if (kk >= KB) kk -= (KB-KT);
    if (kk == ke) return 1;
  }
  
  /*   Probably, following routine is does not work on PC-9801 with
   * 30-lines-BIOS and on PC-H98. ( This function will return 0. )
   */
  if (func < 3) {
    if (lm == 0) {
      return (func == 0) ? (scr == 19) : (scr == 18);
    }
    else if (lm == 1) {
      return (func == 0) ? (scr == 24) : (scr == 23);
    }
  }
  
  return 0;
#undef KT
#undef KB
}

private int
  gp_pc9801_open_printer(int t,int n)
{
  gp_pc9801_pch98_f = *(uchar *)(PC_CONV_BASE + 0x501) & 8;
  return 0;
}

private int
  gp_pc9801_close_printer(int t,int n)
{  return 0;
}

private byte *
  gp_pc9801_alloc_buffer(int size, int t)
{
  uint sel,off;
  if (t == gp_printer_to_bios) {
      sel = gp_pc9801_real_malloc(size,&off);
    if (sel == 0)
      return 0;
    gp_pc9801_printer_buf_selecta = sel;
    return (byte *)PC_CONV_BASE + off;
  }
  else {
    return malloc(size);
  }
}

private void
  gp_pc9801_free_buffer(byte *buf,int t)
{
  if (t == gp_printer_to_bios) {
    gp_pc9801_real_free(gp_pc9801_printer_buf_selecta);
  }
  else {
    free(buf);
  }
}

private int
  gp_pc9801_port_flush(byte *buf,int size)
{
  if (gp_pc9801_pch98_f) {
    for ( ;size>0;size--,buf++) {
      while((inportb(0x42) & 0xd4)!=0x44);
      outportb(0x40, *buf);
      outportb(0x46, 0x04);
      outportb(0x46, 0x05);
    }
  }
  else {
    for ( ;size>0;size--,buf++) {
      while(!(inportb(0x42) & 0x04));
      outportb(0x40, *buf);
      outportb(0x46, 0x0e);
      outportb(0x46, 0x0f);
    }
  }
  return 0;
}

private int gp_pc9801_call_printer_bios(int ,uint , uint );
private int
  gp_pc9801_bios_flush(byte *buf,int total_size)
{
  int size = total_size;
  uint off = buf-(byte *)PC_CONV_BASE;
  
  while (size) {
    size = gp_pc9801_call_printer_bios(size,
      gp_pc9801_printer_buf_selecta,(off&0x0f)+total_size-size);
  }
  return 0;
}

/* Internal routines */

/*
 * Allocate/Free conventional memory for BIOS call.
 * Call Printer BIOS.
 *
 * These are based on the codes in kmtar(K.Mori's tar) PowerUp Kit v 3.3.
 *
 *    Written by K.Masaki (tantan) Nifty SGL00213 
 *                                 PCS   pcs50708
 *     For DJgpp get real memory  function.
 *     1992-01-02
 */
/*
 * int gp_pc9801_real_malloc(uint size,uint *address);
 *
 * Function : allocate conventional memory. (NEC PC-9801)
 * size     : size of memory required.
 * address  : real memory address by 32bit offset
 *          : (set by this function)
 * Return   : selecta
 *          : if 0, fatal.
 */
private uint
  gp_pc9801_real_malloc(uint size,uint *address)
{
	asm("movl %0,%%ebx"::"g" (size):"bx");
	asm("movw	$0xFF48,%ax;int $0x21");
	asm("movl	%%ebx,%0":"=g" (*address)::"ax","bx");
}

/*
 * short gp_pc9801_real_free(uint selecta);  
 *
 * Function : free conventional memory allocated by
 *          :   function gp_pc9801_real_malloc()
 * Return   : 0  means OK!!
 *   	      : -1 means ERROR!!
 */
private short
  gp_pc9801_real_free(uint selecta)
{
	asm("movl  %0,%%eax;push  %%es;movw  %%ax,%%es"::"g" (selecta):"ax","bx");
	asm("movw  $0xFF49,%ax;int $0x21;pop %es;andl $0xFFFF,%eax");
}

/*
 * short gp_pc9801_call_printer_bios(uint size,uint selecta, uint offset)
 *
 * Function       : call printer BIOS to flush print buffer.
 * Arguments      :
 *   int size     : size of buffer
 *   uint selecta : segment address of print buffer
 *                : (selecta returned by gp_pc9801_real_malloc())
 *   uint offset  : offset address of buffer
 * Return         : result code
 */
private int
  gp_pc9801_call_printer_bios(int size,uint _seg, uint _off)
{
	asm("push %es");
	asm("movb $0x30,%ah");
	asm("movw %0,%%bx"::"g" (_off):"ax","bx","cx","dx","bp");
 	asm("movw %0,%%cx"::"g" (size):"ax","bx","cx","dx","bp");
 	
 	asm("movw %0,%%si"::"g" (_seg):"si");
 	asm("movw %si,%es");
 	asm("int $0x1a");
 	
 	asm("movw %cx,%ax");
 	asm("pop %es");
}
