/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:ps2utils.c 12.0$ */
/* $ACIS:ps2utils.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/standatr/RCS/ps2utils.c,v $ */

/*  PS/2 utilities for ATR  */

#ifndef lint
static char *rcsid = "$Header:ps2utils.c 12.0$";
#endif

#include <ctype.h>
#include <assert.h>
#include "init.h"
#include "cmds.h"

#include "sa.h"
#include "../machine/cpu.h"
#include "../caio/hdconfig.h"    /* for diskinfo structure definition */
#include "../ca_atr/pcif.h"		/* need cbcb to get data from keyboard */


ushort *mem_size = (ushort *)0x00000800;
ushort *pcif_base = (ushort *)0x00000802;
ushort *equip = (ushort *)0x00000808;
ushort *ega_info = (ushort *)0x0000080a;
long *memconfig = (long *)0x0000080c;
struct hdinfo *hdinfo[2] = {(struct hdinfo *)0x00000810,(struct hdinfo *)0x0000081d};

struct cbcb *cbcb = (struct cbcb *) 0;		/* force into data segment */
extern long cbcb_addr;				/* location of cbcb in pc memory */
struct pcif_reg *pcif_reg;
char *pcvec_map;

char delay_addr; 
int cbcb_wait = 100000;

static char pcvec_save[16];
static int old_128_window, old_512_window;

struct kbdata *kbdata;	/* ptr to keyboard data area in PS/2 memory */
static char oldkbstate;
int kb_pc_cb;
int sv_pc_cb;
int mask_pc_cb;
int screen_inited;	/* if we've initialized the screens */


/*
 * Clear the pcvec_map when we enter the debugger.
 * Also turn off scan code queueing by the PS/2
 * by turning off the fwd_int flag.
 *
 * we also test to see if the cbcb has moved (if we suspended unix and
 * restarted at a difference address) in which case we force the screens
 * to reinitialize to pick up the control block pointers.
 * we always do the full window protocols to set kbdata and pcvec_map
 * for the same reason.
 */

ps2_enter_debugger()
{

 	int i;
	int debug_flag;

 	old_512_window = get_512_window(); 
	old_128_window = get_128_window(); 

	cbcb = (struct cbcb *)(set_512_window(cbcb_addr)+pcif_512_fw);

	debug_flag = cbcb->romp_int0;
	cbcb->romp_int0 = 0;
	if (kb_pc_cb && kb_pc_cb != cbcb->cbcb_ent[KBENT].pc_cb)
		screen_inited = 0;		/* moved - force a screen init */
	kb_pc_cb = cbcb->cbcb_ent[KBENT].pc_cb;
	sv_pc_cb = cbcb->cbcb_ent[SVENT].pc_cb;
	mask_pc_cb = cbcb->cbcb_ent[MASKENT].pc_cb;


	kbdata = (struct kbdata *) (set_512_window(kb_pc_cb) + pcif_512_fw);
	oldkbstate = kbdata->fwd_int;
	kbdata->fwd_int = 0;

	pcvec_map = (char *)(set_512_window(sv_pc_cb) + pcif_512_fw);
					/* set window for pcvec_map */

	for(i=0;i<16;i++)
	   pcvec_save[i] = pcvec_map[i];

	pcvec_map[KBIRQ] = 0;			/* we don't want any kb ints forwarded */ 

	maskints(PC_ALL_INTS);
	/*
	 * ps2_exit_debugger will restore the windows from the values
	 * we saved above.
	 */
	return(debug_flag);

}	


void
ps2_exit_debugger()
{

 	int i;

	maskints(0);

	set_512_window(kb_pc_cb);	/* set window for kbdata */

	kbdata->fwd_int = oldkbstate ; 

	set_512_window(sv_pc_cb);	/* set window for pcvec_map */

	for(i=0;i<16;i++) 
	   pcvec_map[i] = pcvec_save[i];

	set_512_window(old_512_window); 

	set_128_window(old_128_window); 

}	


/*
 * if mask is non-zero then we mask off those interrupts on the 
 * PS2 side that are being forwarded to us. This will hold those
 * interrupts pending in the 8259 on the PS2 side until we re-enable
 * the interrupts. If mask is zero then we restore the 8259 mask to
 * its normal (previous) state. We use separate static structures
 * the pc side accesses them asynchronously.
 */
maskints(mask)
u_short mask;
{

	static struct mask_int new_mask_int, old_mask_int;

	if(mask) {	/* if we are masking anything */
		new_mask_int.master = (char)((mask & 0xff00) >> 8);
		new_mask_int.slave =(char)(mask & 0x00ff);
		pc_req(CB_MASKREQ,&new_mask_int,MASKENT);
	} else {	/* we are restoring the original */
		old_mask_int.master = 0;	/* restore previous */
		old_mask_int.slave = 0;		/* restore previous */
		pc_req(CB_MASKREQ,&old_mask_int,MASKENT);
	}
}  


/*
 *  Functions for controlling the mapping of addresses from the ROMP
 *  address space to the PC address space.
 */

int current_128_w;
int current_512_w;

/*
 *  PCIF registers 5 and 4 map the 512K and 128K windows respectively
 *  
 */

#define R5 5
#define R4 4

/*
 *  The 5 least significant bits of R5 are used with a 19 bit offset
 *  to produce a 24 bit physical address.
 */


/*
 *  The 7 least significant bits of R4 are used with a 17 bit offset
 *  to produce a 24 bit physical address.
 */


#define PC_128_MASK	0x00fe0000	/* mask off hi and low order bits */
#define PC_512_MASK	0x00f80000	/* mask off hi and low order bits */

set_512_window(pc_addr)
{
	register int ioaddr = *pcif_base + R5 + pcif_io_b;
	register int boundary,offset;

	boundary = (pc_addr & PC_512_MASK) >> PCIF_R5_SHIFT;
	current_512_w = boundary;
	offset = (pc_addr & 0x0007FFFF);
	IOOUT(ioaddr, boundary); 
	return(offset);
}

set_128_window(pc_addr)
{
	register int ioaddr = *pcif_base + R4 + pcif_io_b;
	register int boundary,offset;

	boundary = (pc_addr & PC_128_MASK) >> PCIF_R4_SHIFT;
	current_128_w = boundary;
	offset = (pc_addr & 0x0001FFFF);
	IOOUT(ioaddr,boundary);
	return(offset);
}

get_512_window()
{
	register int ioaddr = *pcif_base + R5 + pcif_io_b;

	return(IOIN(ioaddr) << PCIF_R5_SHIFT);
}

get_128_window()
{
	register int ioaddr = *pcif_base + R4 + pcif_io_b;

	return(IOIN(ioaddr) << PCIF_R4_SHIFT);
}


/*
*	pc_req
*	Generalized PC request call.
*	Waits for the op_code field in the cbcb to become blank.
*	Copies specific cbcb fields to the PC based on the type of call.
*	Interrupts the PC and then exits.
*/	


pc_req(op,unix_cb,cb_ent)	
register u_short op;
register u_long unix_cb;
register u_short cb_ent;
{
    register int pcintr = (int)&pcif_reg->romp_ctrl;
	register int old_window,i = 0;      

	old_window = get_512_window(); 		/* Save the current window */
	cbcb = (struct cbcb *)(set_512_window(cbcb_addr)+pcif_512_fw);

	while((cbcb->op_code & 0x00ff) != 0) {  /* wait for the op_code */
	     delay(1);				/* field to clear */
	     if(i++ > cbcb_wait) {
		   set_512_window(old_window);
	  	   printf("PC REQUEST TIMEOUT.\n");
		   return(-1);
		   }
	}

	PUT_PC(cbcb->cbcb_ent[cb_ent].unix_cb,unix_cb); /* load the unix_cb */

	PUT_PC(cbcb->op_code,op);	/* load the op_code */
	
	SETBIT(pcintr,R_IREQ);		/* ROMP -> PC Inter */

    delay(10);				/* field to clear */

	set_512_window(old_window);	/* restore the 512K window */

	return(0);
}


/*	
*	pc_poll  
*	Poll a specific u_short variable for a requested value.
*	Return that value to the caller, or an error (-1) if we timed out.
*/ 

pc_poll(mem_addr,val)
register u_short *mem_addr;
register u_short val;
{
	register int i = 0;      

	while((*mem_addr & val) == 0 ) { 
	     delay(1);
	     if(i++ > cbcb_wait) {
	       printf("PC POLL TIMEOUT.\n");
	       return(-1);			/* return an error */
	     }
	}

	return(0);	
}


/*
 * function to set pcvec_map observing the window protocols 
 * we return the old value in case anyone wants it.
 */

set_pcvec_map(index,value)
{
	int	old_window = get_512_window();
	int	result;
	
	(void) set_512_window(sv_pc_cb);
	result = pcvec_map[index];
	pcvec_map[index] = value;
	set_512_window(old_window);
	return(result);
}

/*
 * get the control block address observing the window protocols
 */
get_pc_cb(index)
{
	int		old_window = get_512_window();
	int		result;

	cbcb = (struct cbcb *)(set_512_window(cbcb_addr)+pcif_512_fw);
	result =  cbcb->cbcb_ent[index].pc_cb;
	set_512_window(old_window);
	return(result);
}
