head     56.3;
access   ;
symbols  ;
locks    ; strict;
comment  @# @;


56.3
date     93.01.27.13.26.33;  author jwh;  state Exp;
branches ;
next     56.2;

56.2
date     93.01.27.12.05.02;  author jwh;  state Exp;
branches ;
next     56.1;

56.1
date     91.11.05.09.46.45;  author jwh;  state Exp;
branches ;
next     55.1;

55.1
date     91.08.25.10.23.52;  author jwh;  state Exp;
branches ;
next     54.1;

54.1
date     91.03.18.15.27.20;  author jwh;  state Exp;
branches ;
next     53.1;

53.1
date     91.03.11.19.28.13;  author jwh;  state Exp;
branches ;
next     52.1;

52.1
date     91.02.19.09.12.11;  author jwh;  state Exp;
branches ;
next     51.1;

51.1
date     91.01.30.16.11.57;  author jwh;  state Exp;
branches ;
next     50.1;

50.1
date     90.10.29.16.26.37;  author jwh;  state Exp;
branches ;
next     49.1;

49.1
date     90.08.14.14.10.52;  author jwh;  state Exp;
branches ;
next     48.1;

48.1
date     90.07.26.11.17.04;  author jwh;  state Exp;
branches ;
next     47.1;

47.1
date     90.05.14.10.59.58;  author dew;  state Exp;
branches ;
next     46.2;

46.2
date     90.05.09.14.13.48;  author dew;  state Exp;
branches ;
next     46.1;

46.1
date     90.05.07.08.47.08;  author jwh;  state Exp;
branches ;
next     45.1;

45.1
date     90.04.19.15.54.57;  author jwh;  state Exp;
branches ;
next     44.10;

44.10
date     90.04.13.12.00.21;  author dew;  state Exp;
branches ;
next     44.9;

44.9
date     90.04.11.09.46.27;  author dew;  state Exp;
branches ;
next     44.8;

44.8
date     90.04.09.19.35.22;  author dew;  state Exp;
branches ;
next     44.7;

44.7
date     90.04.05.16.16.50;  author dew;  state Exp;
branches ;
next     44.6;

44.6
date     90.04.05.15.03.27;  author dew;  state Exp;
branches ;
next     44.5;

44.5
date     90.04.05.14.30.00;  author dew;  state Exp;
branches ;
next     44.4;

44.4
date     90.04.04.17.59.38;  author dew;  state Exp;
branches ;
next     44.3;

44.3
date     90.04.03.17.50.37;  author dew;  state Exp;
branches ;
next     44.2;

44.2
date     90.04.03.13.59.56;  author dew;  state Exp;
branches ;
next     44.1;

44.1
date     90.04.01.22.12.05;  author jwh;  state Exp;
branches ;
next     43.4;

43.4
date     90.03.30.17.04.28;  author dew;  state Exp;
branches ;
next     43.3;

43.3
date     90.03.30.13.45.35;  author dew;  state Exp;
branches ;
next     43.2;

43.2
date     90.03.26.13.33.15;  author dew;  state Exp;
branches ;
next     43.1;

43.1
date     90.03.20.14.04.03;  author jwh;  state Exp;
branches ;
next     42.2;

42.2
date     90.03.14.17.07.15;  author dew;  state Exp;
branches ;
next     42.1;

42.1
date     90.01.23.17.48.50;  author jwh;  state Exp;
branches ;
next     41.3;

41.3
date     90.01.18.22.54.31;  author dew;  state Exp;
branches ;
next     41.2;

41.2
date     90.01.15.11.38.01;  author dew;  state Exp;
branches ;
next     41.1;

41.1
date     89.12.22.11.30.51;  author jwh;  state Exp;
branches ;
next     1.4;

1.4
date     89.12.21.15.15.25;  author jwh;  state Exp;
branches ;
next     1.3;

1.3
date     89.12.18.14.54.06;  author dew;  state Exp;
branches ;
next     1.2;

1.2
date     89.12.15.14.10.55;  author dew;  state Exp;
branches ;
next     1.1;

1.1
date     89.12.14.15.51.31;  author dew;  state Exp;
branches ;
next     ;


desc
@This file represents the high level implementation of the HP PARALLEL 
(centronics) PWS implementation.
@


56.3
log
@
pws2rcs automatic delta on Wed Jan 27 13:14:25 MST 1993
@
text
@{system options}
$modcal on$
$allow_packed on$
$partial_eval on$

{code generation options}
$debug off$
$linenum 10000$
$iocheck off$
$ovflcheck off$
$range off$
$stackcheck off$

{listing options}
$lines 57$
$pagewidth 130$
$tables off$
$copyright 'Hewlett Packard Company, 1989'$

program pllel_start{{(input, output){for debug};

{local search{{
$search 'KERNEL.CODE',
	'IOLIB.CODE',
	'PLLEL_ASM.CODE'$
{system search{}
$search 'IOLIB:KERNEL',
	'IOLIB:IOLIB',
	'IOLIB:PLLEL_ASM',
	'IOLIB:COMASM'$
{}

$page$
module pllel_util;
{------------------------------------------------------------------------------}
{------------------  parallel driver utilities               ------------------}
{------------------------------------------------------------------------------}
{
	This module contains types, constants, procedures and functions
	necessary to perform low level I/O on the parallel hardware.
	These routines are intended to be invoked as necessary by routines
	in a higher level module which have need of this functionality.

	The ordering of the procedures and functions within this module imply
	a hierarchy.  The first routines encountered are the lowest level
	routines.  The following routines can make use of them.  Generally,
	routines cannot invoke routines which are higher than them in the
	hierarchy.  Higher level modules will generally invoke the routines
	that are highest in the hierarchy of this module.

	The highest level routines generally conduct a O/S described transfer,
	thus they make use of the current buffer pointer and other system
	variables.

	There are a set of pointers, refered to herein has my ptrs, which are
	assumed to be valid by the majority of routines within this module.
	These pointers are made valid by calling set_my_ptrs.  Thus, users
	of this module should call set_my_ptrs before calling any other routine
	within this module.  These pointers uniquely define the hardware and
	user options required of the caller.

	A set of assembler routines found in PLLEL_ASM are invoked by this module.
	These routines form a high level interface to the PWS I/O common assembly
	routines (IOCOMASM).
}


import sysglobals, iodeclarations, iocomasm, asm, parallel_3, pllel_asm;

export

type
	ptr_misc_block = ^misc_block;
	misc_block = record
			peripheral_type:        io_byte;
			peripheral_reset:       io_byte;
			options:                driver_options_type;
			options_reset:          driver_options_type;
			state:                  driver_state_type;
			d_isr_ie:               driver_int_state_type;
			d_int_e:                boolean;
			u_isr_ie:               user_isr_status_type;
			u_isr_status:           user_isr_status_type;
			last_read_nack:         boolean;
			time_rec:               timer_rec;
			my_int_level:           io_byte;
		end;

type
	reg_type = (rw_whole, readbits, writebits);
	sysreg_type = packed record
			byte0:                          char;
			case reg_type of
				rw_whole: (bl:          char);
				readbits: (cardid:      char);
				writebits:(softreset:   char);
		end;

	intdma_type = packed record
			byte2:                          char;
			case reg_type of
				rw_whole: (bl:          char);
				readbits:(int_e_set:    boolean;
					  int_r_set:    boolean;
					  int_lvl:      0..3;
					  iomod_set:    boolean;
					  io_set:       boolean;
					  dma_e1_set:   boolean;
					  dma_e0_set:   boolean);
				writebits:(int_e:       boolean;
					  pad:          0..7;
					  iomod:        boolean;
					  io:           boolean;
					  dma_e1:       boolean;
					  dma_e0:       boolean);
		end;

	comm_per_type = packed record
			byte4:                          char;
			case reg_type of
				rw_whole: (bl:          char);
				readbits: (c_fifofull:  boolean;
					  c_fifoempty:  boolean;
					  c_nstrobe_low:boolean;
					  c_busy_high:  boolean;
					  c_nack_low:   boolean;
					  p_nerror_low: boolean;
					  p_select_high:boolean;
					  p_perror_high:boolean);
				writebits:(do_not_write:char);
		end;

	phy_hostline_type = packed record
			byte6:                          char;
			case reg_type of
				rw_whole: (bl:          char);
				readbits: (pad:         0..hex('1f');
					  ninit_low:    boolean;
					  nselectin_low:boolean;
					  wr_nrd_high:  boolean);
				writebits:(do_not_write:char);
		end;


	int_state_type = packed record
			byte8:                          char;
			case reg_type of
				rw_whole: (bl:          char);
				readbits: (fifo_full:   boolean;
					  fifo_empty:   boolean;
					  pad:          boolean;
					  busy_low:     boolean;
					  nack_low_trans:boolean;
					  nerror_trans: boolean;
					  select_trans: boolean;
					  pe_trans:     boolean);
				{writebits are same as readbits}
		end;

	fifo_type = packed record
			byte10:                         char;
			case reg_type of
				rw_whole: (bl:          char);
				readbits: (fifoin:      char);
				writebits:(fifoout:     char);
		end;



	ptr_parallel_hw_type = ^parallel_hw_type;
	parallel_hw_type = packed record
			sysreg:         sysreg_type;
			intdma:         intdma_type;
			comm_per:       comm_per_type;
			hostline:       phy_hostline_type;
			int_state:      int_state_type;
			fifo:           fifo_type;
		end;

type
	ptr_char_type = ^char;
	ptr_buf_info_type = ^buf_info_type;

const
	delay_ms =      1;
	delay_second =  1000;
	USE_SMALL_FIFO = TRUE;
	USE_LARGE_FIFO = FALSE;
	XMITTED_MASK = hex('60');       {for write_verify}
	LAST_BYTE_XMITTED = hex('40');       {for write_verify}



var
	{
	  Usage info for these pointers.
	  The PLLEL_DRIVE module entry point routines should call set_my_ptrs prior to calling
	  any routines within the PLLEL_UTILS module.  The PLLEL_UTILS routines assume that
	  these pointers are valid.

	  NOTE: Assembly routines access my_tmp_ptr, so it CAN NOT be moved!!!!!
	}
	my_tmp_ptr:     pio_tmp_ptr;
	my_misc_ptr:    ptr_misc_block;
	my_hw_ptr:      ptr_parallel_hw_type;

	procedure       delay(time:integer);
	procedure       set_my_ptrs(tmp_ptr:pio_tmp_ptr);
	procedure       dvr_error(io_error_code:integer);
	procedure       set_int;
	procedure       reset_peripheral;
	procedure       peripheral_check;
	procedure       set_peripheral_type;
	procedure       set_wr_nrd_low;
	procedure       set_bus_out;
	procedure       set_bus_in(set_iomod:boolean);
	procedure       fhs_out(buf:ptr_char_type; var count:integer);
	procedure       fhs_in(buf:ptr_char_type; var count:integer; exit_on_nack:boolean);
	procedure       do_fhs_tfr(ptr_buf:ptr_buf_info_type);
	function        dma_available:boolean;
	function        get_buf_ptr:ptr_buf_info_type;
	procedure       dma_int_handler(temp:ANYPTR);
	procedure       dma_start(bcb:ANYPTR);
	procedure       ovl_int_out_start;
	procedure       ovl_int_in_start;
	procedure       do_ovl_int_fifo_empty;
	procedure       do_ovl_int_fifo_full;

implement



{*********************************************************
 * procedure name:
 *              delay(time:integer)
 *                      time is in units of milliseconds
 *
 * input dependencies:
 *              none.
 *
 * functional description:
 *              delay time.
 *
 * output environement:
 *              the requested amount of time has passed.
 *
 *********************************************************}
procedure delay(time:integer);
var
	tr:timer_rec;
begin
	tr.time := time;
	start_timer(tr);
	repeat until time_expired(tr);
end;


{*********************************************************
 * procedure name:
 *              set_my_ptrs(tmp_ptr:pio_tmp_ptr);
 *                      tmp_ptr points to the i/o temp
 *                      block for this select code.
 *
 * input dependencies:
 *              none.
 *
 * functional description:
 *              set up the parallel drivers global pointers.
 *              affectionatly called my ptrs.
 *
 * output environement:
 *              parallel driver pointers (my ptrs) are valid.
 *
 *********************************************************}
procedure set_my_ptrs(tmp_ptr:pio_tmp_ptr);
begin
	if tmp_ptr <> NIL then
	with tmp_ptr^ do
	begin
		if (card_addr = NIL) or (isc_table[my_isc].card_type <> pllel_card) then
			io_escape(ioe_no_card, my_isc);

		my_tmp_ptr  := tmp_ptr;
		my_misc_ptr := addr(drv_misc[1]);
		my_hw_ptr   := card_addr;
	end
	else
		io_escape(ioe_no_card, 0);
end;


{*********************************************************
 * procedure name:
 *              dvr_error(io_error_code:integer);
 *                      io_error_code is defined in IODECLARATIONS.
 *
 * input dependencies:
 *              my ptrs are valid.
 *              driver is in an error state.
 *
 * functional description:
 *              set driver state, turn off all interrupts,
 *              terminate any existing transfers, and
 *              execute a system escape.
 *
 * output environement:
 *
 *********************************************************}
procedure dvr_error(io_error_code:integer);
begin
	with my_misc_ptr^do
	begin
		if state.bl <> DISABLED_BY_USER then
			state.bl := INACTIVE_ERROR;
		d_isr_ie.bl := 0;
		d_int_e := false;
		set_int;
	end;

	abort_io;
	io_escape(io_error_code, my_tmp_ptr^.my_isc);
end;


{*********************************************************
 * procedure name:
 *              set_int;
 *
 * input dependencies:
 *              my ptrs are valid.
 *              desired driver and user interrupts have been
 *              set up in the misc block.
 *              if true interrupt is desired, this has also
 *              been so indicated.
 *
 * functional description:
 *              reset all existing interrupt latches.
 *              merge both the driver and user interrupt
 *              requests into one, and store in h/w register.
 *              if true interrupt is desired, enable interrupts.
 *
 * output environement:
 *              hardware is set up for desired interrupts.
 *
 *********************************************************}
procedure set_int;
begin
	with my_hw_ptr^, my_misc_ptr^ do
	begin
		int_state.bl := #0; {reset latches}
		int_state.bl := chr(binior(d_isr_ie.bl, u_isr_ie.bl));
		if u_isr_ie.bl <> 0 then
			intdma.int_e := true
		else
			intdma.int_e := d_int_e;
	end;
end;


{*********************************************************
 * procedure name:
 *      reset_peripheral;
 *
 * input dependencies:
 *      my_ptrs are set up.
 *      NOTE: it is not assumed that a peripheral is present,
 *      nor is any attempt made to determine such.
 *
 * functional description:
 *      set state to INACTIVE_ERROR and peripheral_type to
 *          NOT_PRESENT.  This will force the driver to exactly
 *          determine peripheral_type and bus state on next
 *          transfer request.
 *      follow reset specifications:
 *              set wr/nrd high.
 *              float nStrobe and Data lines.
 *              release Busy.
 *              set nInit low - reset the peripheral.
 *              wait for busy high or 25ms.
 *              reset nInit line.
 *              wait for busy low.
 *
 * output environement:
 *              peripheral has been reset, and has correctly
 *              responded.  if user timing is in place and
 *              peripheral does not respond to reset, then
 *              escaped (dvr_error).
 *
 *********************************************************}
procedure reset_peripheral;
var
	use_timer:boolean;
begin
	with my_hw_ptr^, my_misc_ptr^, my_tmp_ptr^ do
	begin
		if peripheral_type < USER_SPEC_NO_DEVICE then
			peripheral_type := NOT_PRESENT;
		if state.bl <> DISABLED_BY_USER then
			state.bl := INACTIVE_ERROR;

		{
		  set host lines in prep for peripheral reset.
		}
		hostline.wr_nrd_high := true;
		intdma.iomod := true;
		intdma.io := false;
		hostline.nselectin_low := true;

		{
		  reset peripheral, and wait for busy to go high.
		}
		hostline.ninit_low := true;
		delay(delay_ms); {wait for everything to settle}
		time_rec.time := 50*delay_ms; {spec says 25}
		start_timer(time_rec);
		repeat until comm_per.c_busy_high or time_expired(time_rec);
		hostline.ninit_low := false;

		{
		  wait for busy to go low.
		}
		use_timer := timeout <> 0;
		if use_timer then
		begin
			time_rec.time := timeout;
			start_timer(time_rec);
		end;
		repeat
			{
			  if busy never goes low, then device is in a bad state.
			}
			if (use_timer) and (time_expired(time_rec)) then
				dvr_error(ioe_timeout);
		until not comm_per.c_busy_high;
	end;
end;


{*********************************************************
 * procedure name:
 *              peripheral_check;
 *
 * input dependencies:
 *              my_ptrs are valid.
 *              device type may/may not be known.
 *
 * functional description:
 *              check that the peripheral is not in an
 *                 error state by checking the select, nerror,
 *                 and perror lines.
 *              if driver has been told to ignore the perror
 *                 line, then do so.
 *
 * output environement:
 *              device is present and ready to talk.
 *              if device not present and user timing in place
 *              and user timeout expired, then set peripheral_type
 *              is set to NOT_PRESENT and escaped (dvr_error)
 *
 *********************************************************}
procedure peripheral_check;
var
	mask:integer;
	use_timer: boolean;
begin
	with my_hw_ptr^, my_misc_ptr^ do
	begin
		if options.ignore_pe then
			mask := hex('6')  {select, and nerror}
		else
			mask := hex('7'); {select, nerror, and perror}
		if (binand(ord(comm_per.bl), mask) <> PLLEL_PERIPHERAL_ONLINE) then
		with my_tmp_ptr^ do
		begin
			use_timer := timeout <> 0;
			if use_timer then
			begin
				time_rec.time := timeout;
				start_timer(time_rec);
			end;
			repeat until (binand(ord(comm_per.bl), mask) =
							PLLEL_PERIPHERAL_ONLINE) or
				     (use_timer and time_expired(time_rec));
			if (use_timer) and (time_expired(time_rec)) then
			begin
				if peripheral_type < USER_SPEC_NO_DEVICE then
				begin
					if (peripheral_type <> HP_BIDIRECTIONAL) or
					   (binand(ord(comm_per.bl), 7) <> 3) then
						peripheral_type := NOT_PRESENT;
				end;
				dvr_error(ioe_timeout);
			end;
		end;
	end;
end;


{*********************************************************
 * procedure name:
 *              set_peripheral_type;
 *
 * input dependencies:
 *              my ptrs active.
 *              peripheral_type is assumed to be NOT_PRESENT.
 *
 *                do not call this routine if the peripheral
 *                type is known - as for output only peripherals
 *                this routine will take 2 seconds.
 *
 *                do not call this routine if peripheral_type
 *                is set by user (>= USER_SPEC_NO_DEVICE) as this
 *                routine will override user specification.
 *
 * functional description:
 *              check if peripheral is online.
 *              if it is, then follow spec to determine if
 *              peripheral is bidirectional or not:
 *                      reset peripheral
 *                      set Wr/nRd to read, but do not take
 *                      over busy line.
 *                      if device bidirectional, it will
 *                      give up Busy line, and since no one
 *                      owns it, it will float high.
 *              since bus is no longer in a known state set current
 *              state to INACTIVE_ERROR, which always forces
 *              the driver to set bus state to desired state.
 *
 * output environement:
 *              peripheral_type is set.
 *              state.bl is INACTIVE_ERROR;
 *
 *********************************************************}
procedure set_peripheral_type;
var
	type_found:boolean;
	i:integer;
begin
	with my_hw_ptr^, my_misc_ptr^, my_tmp_ptr^ do
	begin
		type_found := false;
		i := ord(comm_per.bl);
		if binand(i, hex('1f')) = hex('13') then {device not physically there}
		begin
			type_found := true;
			peripheral_type := NOT_PRESENT;
		end
		else
		begin
			{
			  device is there, see if it is online.
			}
			try
				peripheral_check;
			recover
			begin
				if escapecode = ioescapecode then
				begin
					{
					  peripheral is in bad state, type has been
					  set to NOT_PRESENT.
					}
					type_found := true;
				end
				else
					escape(escapecode);
			end;
		end;


		{
		  now know that a device is there, what type?
		}
		if not type_found then
		begin
			if options.wr_nrd_low then
				peripheral_type := OUTPUT_ONLY
			else
			begin
				{
				  check for bidirectional device
				}
				reset_peripheral;
				intdma.io := false;     {output, give up busy line}
				hostline.wr_nrd_high := false; {tell device input}

				{spec max for peripheral to give up busy is 2 seconds}
				time_rec.time := 2 * delay_second;
				start_timer(time_rec);
				repeat until (comm_per.c_busy_high) or time_expired(time_rec);

				if (comm_per.c_busy_high) then
					peripheral_type := HP_BIDIRECTIONAL
				else
					peripheral_type := OUTPUT_ONLY;
				if peripheral_type = HP_BIDIRECTIONAL then
				begin
					d_isr_ie.nack_low_trans := true;
					set_int;
					hostline.wr_nrd_high := true;
					repeat until int_state.nack_low_trans;
					d_isr_ie.nack_low_trans := false;
					set_int;
					delay(delay_ms); {let everything settle}
				end
				else
					hostline.wr_nrd_high := true;


			end;
		end;
		state.bl := INACTIVE_ERROR;
	end;
end;


{*********************************************************
 * procedure name:
 *              set_wr_nrd_low;
 *
 * input dependencies:
 *              my ptrs are valid.
 *              user has set driver option to maintain
 *                wr_nrd_low at all times.  By definition,
 *                this forces peripheral_type to OUTPUT_ONLY
 *                or NOT_PRESENT.
 *
 * functional description:
 *              reset Wr/nRd.  If peripheral_type is set to
 *                bidirectional device, set it to OUTPUT_ONLY.
 *
 * output environement:
 *              Wr/nRd is low.  peripheral_type is legal.
 *
 *********************************************************}
procedure set_wr_nrd_low;
begin
	with my_hw_ptr^, my_misc_ptr^ do
	begin
		hostline.wr_nrd_high := false;
		if peripheral_type in INPUT_SET then
				peripheral_type := OUTPUT_ONLY;
	end;
end;


{*********************************************************
 * procedure name:
 *              set_bus_out;
 *
 * input dependencies:
 *              my ptrs are active.  peripheral_type may or
 *              may not be known.
 *
 * functional description:
 *              if peripheral type is not known, then determine
 *              its type.
 *              if peripheral can talk output, then set bus to
 *              output state and set state to INACTIVE_WRITE.
 *
 * output environement:
 *              bus in output state, peripheral is in a known
 *              state and driver state is INACTIVE_WRITE.
 *              OR ELSE, driver error.
 *
 *********************************************************}
procedure set_bus_out;
begin
	with my_misc_ptr^ do
	begin
		if (peripheral_type = NOT_PRESENT) then
			set_peripheral_type;
		if (peripheral_type in OUTPUT_SET) then
		with my_hw_ptr^ do
		begin
			if not state.write then
			begin
				d_isr_ie.nack_low_trans := false;
				set_int;
				intdma.iomod := (not options.use_nack);
				intdma.io := false;
				hostline.wr_nrd_high := (not options.wr_nrd_low);
				state.bl := INACTIVE_WRITE;
			end;
		end
		else if peripheral_type < USER_SPEC_NO_DEVICE then
			dvr_error(ioe_timeout)
		else
			dvr_error(ioe_misc);
	end;
end;


{*********************************************************
 * procedure name:
 *              set_bus_in(set_iomod:boolean);
 *
 * input dependencies:
 *              my ptrs are active.  peripheral_type may or
 *              may not be known.  if set_iomod is true,
 *              caller wants small fifo, large fifo otherwise.
 *
 * functional description:
 *              if peripheral type is not known, then determine
 *              its type.
 *              if peripheral can talk input, then wait for
 *              fifo to emtpy out (from last write) and set bus to
 *              output state and set state to INACTIVE_READ.
 *
 * output environement:
 *              bus in input state, peripheral is in a known
 *              state and driver state is INACTIVE_READ.
 *              OR ELSE, driver error.
 *
 *********************************************************}
procedure set_bus_in(set_iomod:boolean);
var
	use_timer:boolean;
begin
	with my_misc_ptr^ do
	begin
		if (peripheral_type = NOT_PRESENT) then
			set_peripheral_type;
		if (peripheral_type in INPUT_SET) then
		with my_hw_ptr^, my_tmp_ptr^ do
		begin
			if not state.read then
			begin
				{
				  if h/w is in an output state:
				     wait for last byte transmited
				     wait for not busy (thus nack on last byte is seen)
				}
				if (not intdma.io) then
				begin
					use_timer := timeout <> 0;
					if use_timer then
					begin
						time_rec.time := timeout;
						start_timer(time_rec);
					end;
					repeat
						if (use_timer) and (time_expired(time_rec)) then
							dvr_error(ioe_timeout);
					until (binand(ord(comm_per.bl),XMITTED_MASK) = LAST_BYTE_XMITTED);
					repeat
						if (use_timer) and (time_expired(time_rec)) then
							dvr_error(ioe_timeout);
					until not comm_per.c_busy_high;
				end;

				{
				  used to always set iomod to 1, which forced FIFO to a size
				  of 1.  No longer true due to rewrite of fhs_in and
				  dma_int_handler.
				}
				intdma.iomod := set_iomod;
				intdma.io := true;
				d_isr_ie.nack_low_trans := true;
				set_int;
				hostline.wr_nrd_high := false;
				state.bl := INACTIVE_READ;
			end;
		end
		else if peripheral_type < USER_SPEC_NO_DEVICE then
			dvr_error(ioe_timeout)
		else
			dvr_error(ioe_misc);
	end;
end;


{*********************************************************
 * procedure name:
 *              fhs_out(buf:ptr_char_type; var count:integer);
 *
 * input dependencies:
 *              my ptrs are set up.
 *              peripheral_type may or may not be known.
 *              bus may or may not be in output phase.
 *              peripheral may or may not be ready to talk.
 *
 * functional description:
 *              make sure peripheral can talk output and set
 *              bus to output phase.
 *              programmatically transfer data to peripheral.
 *              if driver option write_verify is on, then verify
 *              data byte handshaked with peripheral.
 *
 * output environement:
 *              peripheral_type is known and bus is in output state.
 *              OR ELSE dvr_error.
 *
 *********************************************************}
procedure fhs_out(buf:ptr_char_type; var count:integer);
var
	use_timer:boolean;
begin
	with my_hw_ptr^, my_misc_ptr^, my_tmp_ptr^ do
	begin
		{
		  make sure device is not in an error state.
		  make sure device can talk output, and
		  set bus to output state.
		}
		peripheral_check;
		set_bus_out;
		state.bl := ACTIVE_WRITE;

		{
		  set up some local variables to save a little bit of time.
		}
		use_timer := timeout <> 0;

		{
		  do programmatic transfer.
		}
		while count > 0 do
		begin
			if (comm_per.c_fifofull) then
			begin
				{
				  make sure peripheral hasn't seen an error.
				}
				peripheral_check;


				{
				  peripheral is o.k., wait for fifo to empty out.
				}
				if use_timer then
				begin
					time_rec.time := timeout;
					start_timer(time_rec);
				end;
				repeat
					if (use_timer) and (time_expired(time_rec)) then
						dvr_error(ioe_timeout);
				until not comm_per.c_fifofull;
			end;
			fifo.fifoout := buf^;
			buf := addr(buf^, 1);
			count := count - 1;
		end;
		if options.write_verify then
		begin
			{
			  wait for fifo empty and nStrobe not asserted.
			}
			if use_timer then
			begin
				time_rec.time := timeout;
				start_timer(time_rec);
			end;
			repeat
				if (use_timer) and (time_expired(time_rec)) then
					dvr_error(ioe_timeout);
			until (binand(ord(comm_per.bl), XMITTED_MASK) = LAST_BYTE_XMITTED);
		end
		else
			{
			  make sure device is still communicating.
			}
			peripheral_check;

		state.bl := INACTIVE_WRITE;
	end;
end;


{*********************************************************
 * procedure name:
 *              wait_nack;
 *
 * input dependencies:
 *              my ptrs are set up.
 *              a nack transition interrupt has been enabled.
 *              a inbound transfer has just occured, and
 *                want to see if an nack will occur with this
 *                byte.
 *
 * functional description:
 *              wait for either something in FIFO (obviously
 *              no nack on this byte) or an nack interrupt, or
 *              an error.
 *
 * output environement:
 *              if nack occured then last_read_nack (misc_ptr)
 *              set to true.
 *
 *********************************************************}
procedure wait_nack;
var
	i:integer;
begin
	with my_hw_ptr^ do
	begin
		if comm_per.c_fifoempty then
		begin
			repeat until (not comm_per.c_fifoempty) or (int_state.bl <> #0);
			if (int_state.nack_low_trans) then
			begin
				my_misc_ptr^.last_read_nack := true;
				set_int; {clear latches, reset interrupt}
			end;
		end;
	end;
end;


{*********************************************************
 * procedure name:
 *              fhs_in(buf:ptr_char_type; var count:integer,
 *                     exit_on_nack:boolean);
 *
 * input dependencies:
 *              my ptrs are set up.
 *              peripheral_type may or may not be known.
 *              bus may or may not be in input phase.
 *              peripheral may or may not be ready to talk.
 *
 * functional description:
 *              make sure peripheral can talk input and set
 *              bus to input phase.
 *              programmatically transfer data from peripheral.
 *              if exit_on_nack, then look for an nack occurence.
 *
 * output environement:
 *              peripheral_type is known and bus is in input state.
 *              OR ELSE dvr_error.
 *
 *********************************************************}
procedure fhs_in(buf:ptr_char_type; var count:integer; exit_on_nack:boolean);
label 1;
var
	use_timer:boolean;
begin
	with my_hw_ptr^, my_misc_ptr^, my_tmp_ptr^ do
	begin
		{
		  make sure device is not in an error state.
		  make sure device can talk input, and
		  set bus to input state.
		}
		peripheral_check;
		set_bus_in(USE_LARGE_FIFO);
		state.bl := ACTIVE_READ;

		{
		  set up some local variables to save a little bit of time.
		}
		use_timer := timeout <> 0;
		last_read_nack := false;

		{
		  do programmatic transfer
		}
		while count > 0 do
		begin
			if (comm_per.c_fifoempty) then
			begin
				peripheral_check;
				if use_timer then
				begin
					time_rec.time := timeout;
					start_timer(time_rec);
				end;
				repeat
				until (not comm_per.c_fifoempty) or
				      (int_state.bl <> #0) or
				      (use_timer and time_expired(time_rec));
				if comm_per.c_fifoempty then
				begin
					if (exit_on_nack) and (int_state.nack_low_trans) then
					begin
						last_read_nack := true;
						goto 1;
					end
					else
						dvr_error(ioe_timeout);
				end;
			end;
			buf^ := fifo.fifoin;
			buf := addr(buf^, 1);
			count := count - 1;
		end;

		{
		  if we are supposed to exit_on_nack, then there is a very good possibility
		  that the nack will occur with the last byte read.  Therefore, wait for
		  one to possibly show up.
		}
		if exit_on_nack then
			wait_nack;


		1:
		state.bl := INACTIVE_READ;
	end;
end;


{*********************************************************
 * procedure name:
 *              do_fhs_tfr(ptr_buf:ptr_buf_info_type);
 *
 * input dependencies:
 *              User has requested a programmatic transfer.
 *              my ptrs are set up.
 *              device may or may not be there (or ready).
 *              device type may or may not even be known!
 *
 * functional description:
 *              execute the desired transfer request.  This
 *              routine is responsible for updating the buffer
 *              parameters (Standard I/O buffer).
 *              If this is an output transfer, use fhs_out.
 *              If this is an input trnasfer, use fhs_in.
 *
 * output environement:
 *              If successful, transfer has completed as requested
 *              and the buffer parameters have been updated.  The
 *              device type is known and the bus is in an output
 *              state.
 *              If not successful, then an escape has occured, bus
 *              state is not known and the device type is not known.
 *
 *********************************************************}
procedure do_fhs_tfr(ptr_buf:ptr_buf_info_type);
var
	my_count:       integer;
	lastchar:       char;
begin
	with ptr_buf^, my_misc_ptr^ do
	begin
		act_tfr := FHS_tfr;
		my_count := term_count;
		if direction = FROM_MEMORY then
		begin
			fhs_out(buf_empty, term_count);
			buf_empty := addr(ptr_char_type(buf_empty)^, my_count - term_count);
		end
		else {direction is TO_MEMORY}
		begin
			if term_char = -1 then {not a transfer_until}
			begin
				fhs_in(buf_fill, term_count, end_mode);
				buf_fill := addr(ptr_char_type(buf_fill)^,
						 my_count - term_count);
			end
			else {transfer_until}
			begin
				repeat
					my_count := 1;
					fhs_in(buf_fill, my_count, end_mode);
					lastchar := ptr_char_type(buf_fill)^;
					buf_fill := addr(ptr_char_type(buf_fill)^, 1);
					term_count := term_count - 1;
				until (term_count = 0) or
				      (lastchar = chr(term_char)) or
				      ((last_read_nack) and (end_mode));
			end;
		end;

		{serial_FHS transfer has completed normally, clean up.}
		stclr;
	end;
end;


{*********************************************************
 * procedure name:
 *              FUNCTION dma_available:boolean;
 *
 * input dependencies:
 *              none
 *
 * functional description:
 *              determine if DMA hardware is available, if the
 *              DMA driver is in memory, and if a DMA channel is
 *              available for use (note that a DMA channel is not
 *              acquired).
 *
 * output environement:
 *              TRUE if dma h/w, s/w and channel is available,
 *              FALSE otherwise.
 *
 *********************************************************}
function dma_available:boolean;
begin
	dma_available := (
			   (dma_here) and
			   ((dma_isc_0 = no_isc) or (dma_isc_1 = no_isc))
			 );
end;


{*********************************************************
 * procedure name:
 *              FUNCTION get_buf_ptr:ptr_buf_info_type
 *
 * input dependencies:
 *              my ptrs are set up.
 *
 * functional description:
 *              retrieve either the input or output buffer
 *              pointer.  Since only one can be active at a
 *              time, only need to make one check.  If niether
 *              is active, then NIL will be returned.
 *
 * output environement:
 *              returns either the current active buffer pointer
 *              or NIL.
 *
 *********************************************************}
function get_buf_ptr:ptr_buf_info_type;
begin
	with my_tmp_ptr^ do
		if in_bufptr <> NIL then
			get_buf_ptr := in_bufptr
		else
			get_buf_ptr := out_bufptr;
end;


{*********************************************************
 * procedure name:
 *              dma_int_handler(temp:ANYPTR) - temp is the
 *                      driver temp space.
 *
 * input dependencies:
 *              received either a DMA complete interrupt or
 *              an nack occured interrupt.
 *
 * functional description:
 *              turn off DMA, update buffer pointers with
 *              transfer amount.  Terminate transfer buffer, and
 *              clean up hardware.
 *
 * output environement:
 *              DMA and transfer hardware cleaned up and ready
 *              for next transfer.  Transfer buffer updated and
 *              terminated.
 *
 *********************************************************}
procedure dma_int_handler(temp:ANYPTR);
var
	ptr_buf:ptr_buf_info_type;
	diroutput:boolean;
	residual_count, amt_xfered:integer;
begin
	set_my_ptrs(temp);
	with get_buf_ptr^, my_misc_ptr^, my_hw_ptr^ do
	begin
		intdma.dma_e0 := false;
		intdma.dma_e1 := false;
		residual_count := dropdma;
		amt_xfered := term_count - residual_count;
		if direction = FROM_MEMORY then
		begin
			buf_empty := addr(ptr_char_type(buf_empty)^, amt_xfered);

			{
			  wait for fifoempty
			}
			repeat until (comm_per.c_fifoempty) or (not comm_per.p_select_high);
		end
		else
		begin
			buf_fill := addr(ptr_char_type(buf_fill)^, amt_xfered);

			{
			  if this is a transfer_end then
			  wait for fifo not empty or an interrupt.
			}
			if end_mode then
				wait_nack;
		end;
		term_count := residual_count;
		d_isr_ie.bl := 0;
		d_int_e := false;
		set_int;
		stclr;
	end;
end;


{*********************************************************
 * procedure name:
 *              dma_start(bcb:ANYPTR) - bcb: Buffer Control block Pointer
 *
 * input dependencies:
 *              my ptrs are set up.
 *              User has made a valid request for either an
 *              inbound or outbound DMA transfer
 *              bus state may or may not be known.
 *              device type may or may not be known.
 *
 * functional description:
 *              set up hardware and software for a DMA transfer
 *              kick off the DMA transfer
 *
 * output environement:
 *              If successful, then DMA transfer started, bus state
 *              and device type known.
 *              If not successful, then an escape has occured, bus
 *              state is not known and device type is not known.
 *
 *********************************************************}
procedure dma_start(bcb:ANYPTR);
var
	dma_channel:integer;
	loc_intdma:intdma_type;
begin
	with my_misc_ptr^, ptr_buf_info_type(bcb)^, my_hw_ptr^ do
	begin
		{
		  insure dma enable bits are off (a little bit of
		  paranoia is good for the soul).
		}
		intdma.dma_e0 := false;
		intdma.dma_e1 := false;

		{
		  make sure device can talk
		}
		peripheral_check;

		{
		  set up the bus for the direction of the transfer,
		  and set up request for driver interupts.
		}
		if direction = FROM_MEMORY then
		begin
			set_bus_out;
			state.bl := ACTIVE_WRITE;
			d_isr_ie.bl := hex('07');
		end
		else
		begin
			set_bus_in(USE_LARGE_FIFO);
			state.bl := ACTIVE_READ;
			last_read_nack := false;
			if end_mode then
				d_isr_ie.bl := hex('0f')
			else
				d_isr_ie.bl := hex('07');
		end;
		if options.ignore_pe then
			d_isr_ie.pe_trans := false;

		{
		  get dma channel, set it up and arm it.
		}
		dma_channel := getdma;  {this gets a dma channel, sets it up, and arms it}

		{
		  haven't escaped yet so everthing is ready to communicate.
		  turn on requested interrupts.
		  Note that set_int should follow getdma.  Don't want to escape
		  with interrupts set!
		}
		d_int_e := true;
		set_int;


		{
		  set up for DMA interrupt (based on channel received) and kick off the
		  DMA transfer.

		  h/w required fix.  when doing inbound DMA, turn off i/o bit, then turn on
		  I/O and DMA enable at the same time.  for ease of implementation, use same
		  algorithm for both inbound and outbound DMA.
		}
		loc_intdma.bl := intdma.bl;
		intdma.io := false;
		if dma_channel = 0 then
		begin
			dma_ch_0.real_proc := dma_int_handler;
			loc_intdma.dma_e0 := true;
		end
		else
		begin
			dma_ch_1.real_proc := dma_int_handler;
			loc_intdma.dma_e1 := true;
		end;
		intdma.bl := loc_intdma.bl; {this kicks off the DMA}
	end;
end;



{*********************************************************
 * procedure name:
 *              ovl_int_out_start
 *
 * input dependencies:
 *              my ptrs set up.
 *              bus state may or may not be known,
 *              device type may or may not be known.
 *
 * functional description:
 *              start an ouput overlap interrupt transfer
 *              sequence.
 *
 * output environement:
 *              if successful, bus is in output state,
 *              device type is known and overlap interrupt
 *              transfer in output direction is started.
 *              if not successful, this routine has escaped.
 *
 *********************************************************}
procedure ovl_int_out_start;
begin
	with my_misc_ptr^ do
	begin
		peripheral_check;
		set_bus_out;
		state.bl := ACTIVE_WRITE;
		d_isr_ie.fifo_empty := true;
		d_int_e := true;
		set_int;
	end;
end;


{*********************************************************
 * procedure name:
 *              ovl_int_in_start
 *
 * input dependencies:
 *              my ptrs set up.
 *              bus state may or may not be known,
 *              device type may or may not be known.
 *
 * functional description:
 *              start an input overlap interrupt transfer
 *              sequence.
 *              note a fifo of size 1 is used for this type
 *                 of transfer.  This allows an interrupt
 *                 with each byte transfered.
 *
 * output environement:
 *              if successful, bus is in input state,
 *              device type is known and overlap interrupt
 *              transfer in input direction is started.
 *              if not successful, this routine has escaped.
 *
 *********************************************************}
procedure ovl_int_in_start;
begin
	with my_misc_ptr^ do
	begin
		peripheral_check;
		state.bl := INACTIVE_ERROR;
		set_bus_in(USE_SMALL_FIFO);
		state.bl := ACTIVE_READ;
		d_isr_ie.fifo_full := true;
		d_int_e := true;
		set_int;
	end;
end;


{*********************************************************
 * procedure name:
 *              do_ovl_int_fifo_empty
 *
 * input dependencies:
 *              my ptrs set up
 *              a fifo empty interrupt has occured
 *              an output overlap interrupt transfer is
 *                 currently in progress.
 *
 * functional description:
 *              update buffer pointers, get another byte into
 *              fifo, and set up for another interrupt.
 *              if transfer has completed, terminate buffer.
 *              since can't escape from an interrupt, handle
 *                all errors here - if error occurs, terminate
 *                transfer.
 *
 * output environement:
 *              either transfer is continuing or has terminated.
 *              if transfer terminated, then h/w and s/w are cleaned
 *              up.
 *
 *********************************************************}
procedure do_ovl_int_fifo_empty;
var
	my_count:integer;
begin
	with get_buf_ptr^, my_misc_ptr^ do
	begin
		try
			my_count := 1;
			fhs_out(buf_empty, my_count);
			buf_empty := addr(ptr_char_type(buf_empty)^, 1);
			term_count := term_count - 1;
			if (term_count = 0) then {transfer completed}
			begin
				stclr;
				d_isr_ie.fifo_empty := false;
				d_int_e := false;
				set_int;
			end
			else
			begin
				d_isr_ie.fifo_empty := true;
				d_int_e := true;
				set_int;
			end;
		recover
		if (escapecode = ioescapecode) then
		begin
			stclr;
			d_isr_ie.fifo_empty := false;
			d_int_e := false;
			set_int;
		end;
	end;
end;


{*********************************************************
 * procedure name:
 *              do_ovl_int_fifo_full
 *
 * input dependencies:
 *              my ptrs set up
 *              a fifo full interrupt has occured
 *              an input overlap interrupt transfer is
 *                 currently in progress.
 *
 * functional description:
 *              update buffer pointers, get another byte from
 *              fifo, and set up for another interrupt.
 *              if transfer has completed, terminate buffer.
 *              since can't escape from an interrupt, handle
 *                all errors here - if error occurs, terminate
 *                transfer.
 *
 * output environement:
 *              either transfer is continuing or has terminated.
 *              if transfer terminated, then h/w and s/w are cleaned
 *              up.
 *
 *********************************************************}
procedure do_ovl_int_fifo_full;
var
	my_count:integer;
	last_char:char;
begin
	with get_buf_ptr^, my_misc_ptr^ do
	begin
		try
			peripheral_check;
			my_count := 1;
			fhs_in(buf_fill, my_count, end_mode);
			last_char := ptr_char_type(buf_fill)^;
			buf_fill := addr(ptr_char_type(buf_fill)^, 1);
			term_count := term_count - 1;
			if (term_count = 0) or
			   (end_mode and last_read_nack) or
			   ((term_char <> -1) and (last_char = chr(term_char))) then
			begin
				stclr;
				state.bl := INACTIVE_ERROR;
				d_isr_ie.fifo_full := false;
				d_int_e := false;
				set_int;
			end
			else
			begin
				d_isr_ie.fifo_full := true;
				d_int_e := true;
				set_int;
			end;
		recover
		if (escapecode = ioescapecode) then
		begin
			stclr;
			d_isr_ie.fifo_full := false;
			d_int_e := false;
			set_int;
		end;
	end;
end;

end {pllel_util};

$page$
module pllel_drive;
{------------------------------------------------------------------------------}
{------------------  parallel driver hook flow control       ------------------}
{------------------------------------------------------------------------------}
{

	This module acts as a demuxer for the PWS I/O system.  I/O system calls
	enter into this module and are translatted into PARALLEL I/O requests.
	The later requests are invoked by calls to routines within the PLLEL_UTIL
	module.

	The functionality of each of the I/O requests are defined in either the
	Procedure Library GENERAL I/O discussions, the Procedure Library PARALLEL
	Inteface discussions, or in the Systems Designers Guide.  Unless necessary,
	the functionality is NOT repeated here in the guise of routine headers.
}


import sysglobals, asm, iodeclarations, iocomasm, parallel_3, pllel_util, pllel_asm;

export

	procedure       pinit_hook(temp:ANYPTR);
	procedure       pisr_hook(temp:PISRIB);
	procedure       prdb_hook(temp:ANYPTR; VAR x:CHAR);
	procedure       pwtb_hook(temp:ANYPTR;     x:CHAR);
	procedure       prdw_hook(temp:ANYPTR; VAR x:io_word);
	procedure       pwtw_hook(temp:ANYPTR;     x:io_word);
	procedure       prds_hook(temp:ANYPTR; reg:io_word; VAR x : io_word);
	procedure       pwtc_hook(temp:ANYPTR; reg:io_word;     x : io_word );
	procedure       pend_hook(temp:ANYPTR; VAR b:boolean);
	procedure       ptfr_hook(temp:ANYPTR; bcb:ANYPTR );


implement

procedure pinit_hook(temp:ANYPTR);
var
	timevalue:integer;
begin
	try {this routine should NEVER escape!}

		{
		  this routine should also not go into a forever loop for whatever
		  reason.  save timeout value, and set timeout to 1ms.
		}
		with pio_tmp_ptr(temp)^ do
		begin
			timevalue := timeout;
			timeout := 1;
		end;

		set_my_ptrs(temp);

		with my_hw_ptr^, my_tmp_ptr^, my_misc_ptr^ do
		begin

			{
			  clear up any existing transfers
			}
			if state.active_xfer then
				abort_io;

			{
			  restore reset defaults
			}
			peripheral_type := peripheral_reset;
			if peripheral_type = USER_SPEC_NO_DEVICE then
				state.bl := DISABLED_BY_USER
			else
				state.bl := INACTIVE_ERROR;
			options.w := options_reset.w;
			if options.wr_nrd_low then
				set_wr_nrd_low;


			{
			  reset h/w
			}
			intdma.int_e := false;
			hostline.bl := #0;               {turn off all hostlines}
			sysreg.softreset := #0;
			delay(delay_ms);
			if not options.wr_nrd_low then
				hostline.wr_nrd_high := true;
			d_int_e := false;
			intdma.io := false;  {output forces chip to own data & nstrobe lines}
			d_isr_ie.w := 0;
			u_isr_ie.w := 0;
			set_int;
			hostline.nselectin_low := true;


			{
			  reset driver variables
			}
			u_isr_status.w := 0;
			last_read_nack := false;
			user_isr.dummy_pr := nil;
			user_isr.dummy_sl := nil;
			in_bufptr := nil;
			out_bufptr := nil;
			my_int_level := intdma.int_lvl + 3;
		end;
	recover
		;

	{
	  restore timeout value
	}
	try
		pio_tmp_ptr(temp)^.timeout := timevalue;
	recover
		;
end;

procedure pisr_hook(temp:PISRIB);
type
	pxlate_type = record
			case integer of
				1:(pproc:parallel_user_isr_type);
				2:(ioproc:io_proc);
		end;

var
	save_ioe_result:integer;
	save_ioe_isc:integer;
	save_int_state:int_state_type;
	d_isr_ir:driver_int_state_type;
	ptr_buf:ptr_buf_info_type;
	p:pxlate_type;
	do_u_isr_ovl:boolean;

	procedure kill_act_tfr;
	begin
		with my_misc_ptr^ do
		begin
			d_isr_ie.bl := 0;
			d_int_e := false;
			abort_io;
		end;
	end;
begin
	do_u_isr_ovl := false;
	set_my_ptrs(anyptr(temp));
	with my_tmp_ptr^, my_misc_ptr^, my_hw_ptr^ do
	begin
		{
		  save current h/w interrupts - support routines will reset the
		  driver interrupt conditions and call set_int.  set_int resets
		  the h/w interrupt conditions, thus a copy is necessary
		}
		save_int_state.bl := int_state.bl;

		{handle driver interrupts}
		d_isr_ir.bl := binand(d_isr_ie.bl, ord(save_int_state.bl));
		if (d_isr_ir.bl <> 0) then
		begin
			{only supposed to be getting driver interrupts with
			 active transfers}
			ptr_buf := get_buf_ptr;
			if (ptr_buf = nil) then
			begin
				{reset all driver interrupts - this should never happen}
				d_isr_ie.bl := 0;
				d_int_e := false;
			end
			else
			with ptr_buf^ do
			begin
				{
				  utility routines will set ioe_result and ioe_isc.
				  Can't allow ioe_result and ioe_isc to be modified!
				}
				save_ioe_result := ioe_result;
				save_ioe_isc := ioe_isc;
				if (act_tfr = DMA_tfr) then
				begin
					if binand(ord(save_int_state.bl), hex('07')) <> 0 then
					begin
						{peripheral gone off line during dma transfer,
						 wait for peripheral to respond}
						try
							peripheral_check;
							{if didn't escape, peripheral back online and
							 DMA continuing}
						recover
							kill_act_tfr;
					end
					else if save_int_state.nack_low_trans then
					begin
						last_read_nack := true;
						dma_int_handler(temp);
					end
					else
						kill_act_tfr;
				end
				else if (act_tfr = INTR_tfr) then
				begin
					do_u_isr_ovl := true;
					if save_int_state.fifo_full then
						do_ovl_int_fifo_full
					else if save_int_state.fifo_empty then
						do_ovl_int_fifo_empty
					else
					begin
						do_u_isr_ovl := false;
						kill_act_tfr;
					end;
				end
				else if (act_tfr = FHS_tfr) then
				begin
					try
						{fake out the tfr hook}
						d_isr_ie.fifo_empty := false;
						d_isr_ie.fifo_full := false;
						d_int_e := false;
						set_int;
						do_fhs_tfr(ptr_buf);
					recover
						kill_act_tfr;
				end
				else
					kill_act_tfr;
				ioe_result := save_ioe_result;
				ioe_isc := save_ioe_isc;
			end;

		end;

		{handle user isr interrupts}
		u_isr_status.w := 0;
		u_isr_status.bl := binand(u_isr_ie.bl, ord(save_int_state.bl));
		if (u_isr_ie.xfer_extend) and (do_u_isr_ovl) then
			u_isr_status.xfer_extend := true;
		if (u_isr_status.bl <> 0) then
		begin
			{disable user interrupts on interrupting conditions only.}
			u_isr_ie.bl := u_isr_ie.bl - u_isr_status.bl;

			{call the user isr}
			p.ioproc := user_isr.real_proc;
			call(p.pproc, my_isc);

			{clear user isr status}
			u_isr_status.w := 0;
		end;

		{reset up any interrupts}
		set_int;
	end;
end;

procedure prdb_hook(temp:ANYPTR; VAR x:CHAR);
var
	count:integer;
begin
	set_my_ptrs(temp);
	count := 1;
	fhs_in(addr(x), count, true);
end;

procedure pwtb_hook(temp:ANYPTR; x:CHAR);
var
	count:integer;
begin
	set_my_ptrs(temp);
	count := 1;
	fhs_out(addr(x), count);
end;

procedure prdw_hook(temp:ANYPTR; VAR x:io_word);
var
	count:integer;
begin
	set_my_ptrs(temp);
	count := 2;
	fhs_in(addr(x), count, true);
end;

procedure pwtw_hook(temp:ANYPTR; x:io_word);
var
	count:integer;
begin
	set_my_ptrs(temp);
	count := 2;
	fhs_out(addr(x), count);
end;

procedure prds_hook(temp:ANYPTR; reg:io_word; VAR x:io_word);
var
	status:p3regs_type;
begin
	set_my_ptrs(temp);
	with my_hw_ptr^, my_tmp_ptr^, my_misc_ptr^ do
	begin
		status.w := 0;
		case reg of
			PLLEL_REG_CARD_ID:
				begin
					status.w := PARALLEL_CARDID;
				end;
			PLLEL_REG_INTDMA_STATUS:
				begin
					status.bl := ord(intdma.bl);
					status.intdma_status.pad := 0;
				end;
			PLLEL_REG_PERIPHERAL_STATUS:
				begin
					status.bl   := ord(comm_per.bl);
					status.peripheral_status.pad := 0;
				end;
			PLLEL_REG_COMM_STATUS:
				begin
					status.bl := binlsr(ord(comm_per.bl),3);
					status.comm_status.pad := 0;
				end;
			PLLEL_REG_HOST_LINE_CONTROL:
				begin
					status.bl := ord(hostline.bl);
					status.host_line.pad := 0;
				end;
			PLLEL_REG_IO_CONTROL:
				begin
					status.bl := binlsr(ord(intdma.bl),2);
					status.io_control.pad := 0;
				end;
			PLLEL_REG_FIFO:
				begin
					status.bl := ord(fifo.fifoin);
				end;
			PLLEL_REG_PERIPHERAL_TYPE:
				begin
					if peripheral_type = NOT_PRESENT then
					try
						set_peripheral_type;
					recover
						if escapecode <> ioescapecode then
							escape(escapecode);

					status.bl := peripheral_type;
				end;
			PLLEL_REG_TYPE_RESET:
				begin
					status.bl := peripheral_reset;
				end;
			PLLEL_REG_INTERRUPT_STATE:
				begin
					status.bl := d_isr_ie.bl;
				end;
			PLLEL_REG_DRIVER_OPTIONS:
				begin
					status.bl := options.bl;
				end;
			PLLEL_REG_OPTIONS_RESET:
				begin
					status.bl := options_reset.bl;
				end;
			PLLEL_REG_DRIVER_STATE:
				begin
					status.driver_state := state;
				end;
			PLLEL_REG_HOOK_STATUS:
				begin
					if (user_isr.dummy_pr = NIL) then
						status.bl := USER_ISR_HOOK_INACTIVE
					else
						status.bl := USER_ISR_HOOK_ACTIVE;
				end;
			PLLEL_REG_USER_ISR_ENABLE:
				begin
					status.user_isr_status := u_isr_ie;
				end;
			PLLEL_REG_USER_ISR_STATUS:
				begin
					status.user_isr_status := u_isr_status;
				end;
			OTHERWISE
				dvr_error(ioe_rds_wtc);
		end;
		x := status.w;
	end;
end;

procedure pwtc_hook(temp:ANYPTR; reg:io_word; x:io_word );
var
	control:p3regs_type;
	peripheral_online:boolean;
	timevalue:integer;
begin
	set_my_ptrs(temp);
	with my_hw_ptr^, my_tmp_ptr^, my_misc_ptr^ do
	begin
		control.w := x;
		case reg of
			PLLEL_REG_RESET:
				begin
					pinit_hook(temp);
				end;
			PLLEL_REG_HOST_LINE_CONTROL:
				begin
					control.host_line.pad := 0;
					hostline.bl := chr(control.bl);
				end;
			PLLEL_REG_IO_CONTROL:
				begin
					intdma.io       := control.io_control.input_high;
					intdma.iomod    := control.io_control.modify_io;

					{force driver to set up bus on next transfer}
					if not state.disabled then
						state.bl := INACTIVE_ERROR;
				end;
			PLLEL_REG_FIFO:
				begin
					fifo.fifoout := chr(control.bl);
				end;
			PLLEL_REG_PERIPHERAL_TYPE,
			PLLEL_REG_TYPE_RESET:
				begin
					if control.bl in USER_SET then
					begin
						if reg = PLLEL_REG_PERIPHERAL_TYPE then
						begin
							if (options.wr_nrd_low) and
							   (control.bl in INPUT_SET) then
								dvr_error(ioe_rds_wtc);
							peripheral_type := control.bl;
						end
						else
							peripheral_reset := control.bl;
					end
					else
						dvr_error(ioe_rds_wtc);
				end;
			PLLEL_REG_PERIPHERAL_RESET:
				begin
					{
					  do a quick test to see if the attached peripheral
					  is online.

					  Want to do a hard reset if the device is not online,
					  the device type is specified by user, or the wr_nrd_low
					  option is used.

					  Otherwise, call set_peripheral_type which will do a reset
					  and additionally determine the type of the attached device.
					  This prevents a 'double reset'.
					}
					timevalue := timeout;
					timeout := 1;
					try
						peripheral_check;
						peripheral_online := true;
						timeout := timevalue;
					recover
					begin
						timeout := timevalue;
						if escapecode = ioescapecode then
							peripheral_online := false
						else
							escape(escapecode);
					end;
					if (not peripheral_online) or
					   (peripheral_type in [USER_SPEC_NO_DEVICE,
							       USER_SPEC_OUTPUT_ONLY,
							       USER_SPEC_HP_BIDIRECTIONAL]) or
					   (options.wr_nrd_low) then
						reset_peripheral
					else
					begin
						peripheral_type := NOT_PRESENT;
						set_peripheral_type;
					end;
				end;
			PLLEL_REG_DRIVER_OPTIONS:
				begin
					control.bh := 0;
					control.driver_options.pad := 0;
					if options.wr_nrd_low and
					   not control.driver_options.wr_nrd_low then
					begin {wr_nrd_low is being turned off}
						peripheral_type := NOT_PRESENT;
					end;
					options.w := control.w;
					if options.wr_nrd_low then
						set_wr_nrd_low;
					{
					  force bus to be set on next write
					  this forces use_nack and other options in place.
					}
					state.bl := INACTIVE_ERROR;
				end;
			PLLEL_REG_OPTIONS_RESET:
				begin
					control.bh := 0;
					control.driver_options.pad := 0;
					options_reset.w := control.w;
				end;
			PLLEL_REG_HOOK_CLEAR:
				begin
					u_isr_ie.w := 0;
					u_isr_status.w := 0;
					user_isr.dummy_pr := NIL;
					user_isr.dummy_sl := NIL;
					set_int;
				end;
			PLLEL_REG_USER_ISR_ENABLE:
				begin
					if user_isr.dummy_pr = NIL then
						dvr_error(ioe_rds_wtc);

					u_isr_ie.bl := control.bl;
					set_int;
				end;
			OTHERWISE
				dvr_error(ioe_rds_wtc);
		end;
	end;
end;

procedure pend_hook(temp:ANYPTR; VAR b:boolean);
begin
	set_my_ptrs(temp);
	b := my_misc_ptr^.last_read_nack;
end;

procedure ptfr_hook(temp:ANYPTR; bcb:ANYPTR );
VAR
	tmp:            integer;
BEGIN
	set_my_ptrs(temp);
	with my_tmp_ptr^, ptr_buf_info_type(bcb)^, my_misc_ptr^ do
	begin
		{check for illegal full duplex transfer request}
		if (direction = TO_MEMORY) and (out_bufptr <> NIL) then
		begin
			dvr_error(ioe_bad_tfr);
		end
		else if (direction = FROM_MEMORY) and (in_bufptr <> NIL) then
		begin
			dvr_error(ioe_bad_tfr);
		end;

		{check for illegal word transfer request}
		if (b_w_mode = TRUE) then
		begin
			dvr_error(ioe_bad_tfr);
		end;

		{check for illegal transfer end request for outbound transfers}
		if (end_mode) and (direction = FROM_MEMORY) then
		begin
			dvr_error(ioe_bad_tfr);
		end;

		{check for illegal transfer request}
		if (usr_tfr in [dummy_tfr_1, dummy_tfr_2]) then
		begin
			dvr_error(ioe_bad_tfr);
		end;


		{ adjust transfer request type of FASTEST }
		if usr_tfr in [serial_FASTEST, overlap_FASTEST] then
		begin
			usr_tfr := pred(usr_tfr);  {--> FHS}
			if (dma_available) then
			begin
				usr_tfr := pred(usr_tfr);  {--> DMA}
			end;
		end;


		{ adjust transfer request type of OVERLAP }
		if usr_tfr = OVERLAP then
		begin
			if (dma_available) then
				usr_tfr := OVERLAP_DMA
			else
				usr_tfr := OVERLAP_INTR;
		end;


		{transfer accepted, mark buffer busy}
		stbsy;

		{do the various transfers}
		if usr_tfr in [serial_DMA, overlap_DMA] then
		begin
			act_tfr := DMA_tfr;
			dma_start(bcb);

			if usr_tfr = serial_DMA then
			begin
				repeat until active_isc = no_isc;
			end;
		end
		else if usr_tfr = serial_FHS then
		begin
			do_fhs_tfr(bcb);
		end
		else if usr_tfr in [overlap_FHS, overlap_INTR] then
		begin
			if usr_tfr = overlap_FHS then
				act_tfr := FHS_tfr
			else
				act_tfr := INTR_tfr;

			if direction = FROM_MEMORY then
				ovl_int_out_start
			else
				ovl_int_in_start;
		end
		else
			dvr_error(ioe_bad_tfr);
	end;
end;


end {pllel_drive};

$page$
{------------------------------------------------------------------------------}
{------------------  parallel driver main program.           ------------------}
{------------------------------------------------------------------------------}

import sysglobals, isr, loader, asm, iodeclarations, general_0, parallel_3,
       pllel_drive, pllel_util;

function pllel_init:boolean;
type
	p_drv_type = ^drv_table_type;
var
	card_found:boolean;
	io_isc:type_isc;
	io_lvl:io_byte;
	p_drivers:p_drv_type;
	p_hw:ptr_parallel_hw_type;
	p_misc:ptr_misc_block;
	i:integer;
	pch:ptr_char_type;
begin
	card_found := false;

	io_revid := io_revid + ' P3.2';

	{
	  set up the driver tables
	}
	newbytes(p_drivers, sizeof(drv_table_type));
	p_drivers^ := dummy_drivers;
	with p_drivers^ do begin
		iod_init  := pinit_hook;
		iod_isr   := pisr_hook;
		iod_rdb   := prdb_hook;
		iod_wtb   := pwtb_hook;
		iod_rdw   := prdw_hook;
		iod_wtw   := pwtw_hook;
		iod_rds   := prds_hook;
		iod_wtc   := pwtc_hook;
		iod_end   := pend_hook;
		iod_tfr   := ptfr_hook;
	end;


	{
	  look for parallel interfaces, and initialize.
	}
	for io_isc:=iominisc to iomaxisc do
	with isc_table[io_isc] do
	begin
		if (card_ptr <> nil) and
		   (card_type = pllel_card) then
		begin
			card_found := true;
			p_hw := io_tmp_ptr^.card_addr;
			p_misc := addr(io_tmp_ptr^.drv_misc[1]);


			{
			  set up O/S I/O hooks
			}
			io_drv_ptr := anyptr(p_drivers);

			{
			  set up ISR handler
			}
			io_lvl := p_hw^.intdma.int_lvl + 3;
			if io_tmp_ptr^.myisrib.intregaddr <> nil then  {isr already exits}
			begin
				{
				  unlink existing isr hook.
				}
				isrunlink(io_lvl, addr(io_tmp_ptr^.myisrib));
			end;

			permisrlink(
				io_drv_ptr^.iod_isr,                { isr handler   }
				ADDR(p_hw^.intdma, 1),              { card address  }
				hex('c0'),                          { intr. mask    }
				hex('c0'),                          { intr. value   }
				io_lvl,                             { level         }
				ADDR(io_tmp_ptr^.myisrib));         { isrib info    }


			{
			  initialize driver variables.
			}
			pch := addr(p_misc^);
			for i := 1 to sizeof(misc_block) do
			begin
				pch^ := #0;
				pch := addr(pch^,1);
			end;
			with p_misc^ do
			begin
				peripheral_type := NOT_PRESENT;
				peripheral_reset := NOT_PRESENT;
				options.w := 0;
				options_reset.w := 0;
				state.bl := INACTIVE_ERROR;
				d_isr_ie.w := 0;
				d_int_e := false;
				u_isr_ie.w := 0;
				u_isr_status.w := 0;
				last_read_nack := false;
			end;


			{
			  initialize driver & h/w.
			}
			with io_tmp_ptr^ do
			try
				i := timeout;
				timeout := 1;
				pinit_hook(io_tmp_ptr);
				timeout := i;
			recover
			begin
				timeout := i;
				if escapecode <> ioescapecode then
					escape(escapecode);
			end;
		end;
	end;
	pllel_init := card_found;
end;

begin {program pllel_start}
	if pllel_init then
		markuser;
end.
@


56.2
log
@
pws2rcs automatic delta on Wed Jan 27 11:57:27 MST 1993
@
text
@d1 2246
@


56.1
log
@Automatic bump of revision number for PWS version 3.25
@
text
@a0 2246
{system options}
$modcal on$
$allow_packed on$
$partial_eval on$

{code generation options}
$debug off$
$linenum 10000$
$iocheck off$
$ovflcheck off$
$range off$
$stackcheck off$

{listing options}
$lines 57$
$pagewidth 130$
$tables off$
$copyright 'Hewlett Packard Company, 1989'$

program pllel_start{{(input, output){for debug};

{local search{{
$search 'KERNEL.CODE',
	'IOLIB.CODE',
	'PLLEL_ASM.CODE'$
{system search{}
$search 'IOLIB:KERNEL',
	'IOLIB:IOLIB',
	'IOLIB:PLLEL_ASM',
	'IOLIB:COMASM'$
{}

$page$
module pllel_util;
{------------------------------------------------------------------------------}
{------------------  parallel driver utilities               ------------------}
{------------------------------------------------------------------------------}
{
	This module contains types, constants, procedures and functions
	necessary to perform low level I/O on the parallel hardware.
	These routines are intended to be invoked as necessary by routines
	in a higher level module which have need of this functionality.

	The ordering of the procedures and functions within this module imply
	a hierarchy.  The first routines encountered are the lowest level
	routines.  The following routines can make use of them.  Generally,
	routines cannot invoke routines which are higher than them in the
	hierarchy.  Higher level modules will generally invoke the routines
	that are highest in the hierarchy of this module.

	The highest level routines generally conduct a O/S described transfer,
	thus they make use of the current buffer pointer and other system
	variables.

	There are a set of pointers, refered to herein has my ptrs, which are
	assumed to be valid by the majority of routines within this module.
	These pointers are made valid by calling set_my_ptrs.  Thus, users
	of this module should call set_my_ptrs before calling any other routine
	within this module.  These pointers uniquely define the hardware and
	user options required of the caller.

	A set of assembler routines found in PLLEL_ASM are invoked by this module.
	These routines form a high level interface to the PWS I/O common assembly
	routines (IOCOMASM).
}


import sysglobals, iodeclarations, iocomasm, asm, parallel_3, pllel_asm;

export

type
	ptr_misc_block = ^misc_block;
	misc_block = record
			peripheral_type:        io_byte;
			peripheral_reset:       io_byte;
			options:                driver_options_type;
			options_reset:          driver_options_type;
			state:                  driver_state_type;
			d_isr_ie:               driver_int_state_type;
			d_int_e:                boolean;
			u_isr_ie:               user_isr_status_type;
			u_isr_status:           user_isr_status_type;
			last_read_nack:         boolean;
			time_rec:               timer_rec;
			my_int_level:           io_byte;
		end;

type
	reg_type = (rw_whole, readbits, writebits);
	sysreg_type = packed record
			byte0:                          char;
			case reg_type of
				rw_whole: (bl:          char);
				readbits: (cardid:      char);
				writebits:(softreset:   char);
		end;

	intdma_type = packed record
			byte2:                          char;
			case reg_type of
				rw_whole: (bl:          char);
				readbits:(int_e_set:    boolean;
					  int_r_set:    boolean;
					  int_lvl:      0..3;
					  iomod_set:    boolean;
					  io_set:       boolean;
					  dma_e1_set:   boolean;
					  dma_e0_set:   boolean);
				writebits:(int_e:       boolean;
					  pad:          0..7;
					  iomod:        boolean;
					  io:           boolean;
					  dma_e1:       boolean;
					  dma_e0:       boolean);
		end;

	comm_per_type = packed record
			byte4:                          char;
			case reg_type of
				rw_whole: (bl:          char);
				readbits: (c_fifofull:  boolean;
					  c_fifoempty:  boolean;
					  c_nstrobe_low:boolean;
					  c_busy_high:  boolean;
					  c_nack_low:   boolean;
					  p_nerror_low: boolean;
					  p_select_high:boolean;
					  p_perror_high:boolean);
				writebits:(do_not_write:char);
		end;

	phy_hostline_type = packed record
			byte6:                          char;
			case reg_type of
				rw_whole: (bl:          char);
				readbits: (pad:         0..hex('1f');
					  ninit_low:    boolean;
					  nselectin_low:boolean;
					  wr_nrd_high:  boolean);
				writebits:(do_not_write:char);
		end;


	int_state_type = packed record
			byte8:                          char;
			case reg_type of
				rw_whole: (bl:          char);
				readbits: (fifo_full:   boolean;
					  fifo_empty:   boolean;
					  pad:          boolean;
					  busy_low:     boolean;
					  nack_low_trans:boolean;
					  nerror_trans: boolean;
					  select_trans: boolean;
					  pe_trans:     boolean);
				{writebits are same as readbits}
		end;

	fifo_type = packed record
			byte10:                         char;
			case reg_type of
				rw_whole: (bl:          char);
				readbits: (fifoin:      char);
				writebits:(fifoout:     char);
		end;



	ptr_parallel_hw_type = ^parallel_hw_type;
	parallel_hw_type = packed record
			sysreg:         sysreg_type;
			intdma:         intdma_type;
			comm_per:       comm_per_type;
			hostline:       phy_hostline_type;
			int_state:      int_state_type;
			fifo:           fifo_type;
		end;

type
	ptr_char_type = ^char;
	ptr_buf_info_type = ^buf_info_type;

const
	delay_ms =      1;
	delay_second =  1000;
	USE_SMALL_FIFO = TRUE;
	USE_LARGE_FIFO = FALSE;
	XMITTED_MASK = hex('60');       {for write_verify}
	LAST_BYTE_XMITTED = hex('40');       {for write_verify}



var
	{
	  Usage info for these pointers.
	  The PLLEL_DRIVE module entry point routines should call set_my_ptrs prior to calling
	  any routines within the PLLEL_UTILS module.  The PLLEL_UTILS routines assume that
	  these pointers are valid.

	  NOTE: Assembly routines access my_tmp_ptr, so it CAN NOT be moved!!!!!
	}
	my_tmp_ptr:     pio_tmp_ptr;
	my_misc_ptr:    ptr_misc_block;
	my_hw_ptr:      ptr_parallel_hw_type;

	procedure       delay(time:integer);
	procedure       set_my_ptrs(tmp_ptr:pio_tmp_ptr);
	procedure       dvr_error(io_error_code:integer);
	procedure       set_int;
	procedure       reset_peripheral;
	procedure       peripheral_check;
	procedure       set_peripheral_type;
	procedure       set_wr_nrd_low;
	procedure       set_bus_out;
	procedure       set_bus_in(set_iomod:boolean);
	procedure       fhs_out(buf:ptr_char_type; var count:integer);
	procedure       fhs_in(buf:ptr_char_type; var count:integer; exit_on_nack:boolean);
	procedure       do_fhs_tfr(ptr_buf:ptr_buf_info_type);
	function        dma_available:boolean;
	function        get_buf_ptr:ptr_buf_info_type;
	procedure       dma_int_handler(temp:ANYPTR);
	procedure       dma_start(bcb:ANYPTR);
	procedure       ovl_int_out_start;
	procedure       ovl_int_in_start;
	procedure       do_ovl_int_fifo_empty;
	procedure       do_ovl_int_fifo_full;

implement



{*********************************************************
 * procedure name:
 *              delay(time:integer)
 *                      time is in units of milliseconds
 *
 * input dependencies:
 *              none.
 *
 * functional description:
 *              delay time.
 *
 * output environement:
 *              the requested amount of time has passed.
 *
 *********************************************************}
procedure delay(time:integer);
var
	tr:timer_rec;
begin
	tr.time := time;
	start_timer(tr);
	repeat until time_expired(tr);
end;


{*********************************************************
 * procedure name:
 *              set_my_ptrs(tmp_ptr:pio_tmp_ptr);
 *                      tmp_ptr points to the i/o temp
 *                      block for this select code.
 *
 * input dependencies:
 *              none.
 *
 * functional description:
 *              set up the parallel drivers global pointers.
 *              affectionatly called my ptrs.
 *
 * output environement:
 *              parallel driver pointers (my ptrs) are valid.
 *
 *********************************************************}
procedure set_my_ptrs(tmp_ptr:pio_tmp_ptr);
begin
	if tmp_ptr <> NIL then
	with tmp_ptr^ do
	begin
		if (card_addr = NIL) or (isc_table[my_isc].card_type <> pllel_card) then
			io_escape(ioe_no_card, my_isc);

		my_tmp_ptr  := tmp_ptr;
		my_misc_ptr := addr(drv_misc[1]);
		my_hw_ptr   := card_addr;
	end
	else
		io_escape(ioe_no_card, 0);
end;


{*********************************************************
 * procedure name:
 *              dvr_error(io_error_code:integer);
 *                      io_error_code is defined in IODECLARATIONS.
 *
 * input dependencies:
 *              my ptrs are valid.
 *              driver is in an error state.
 *
 * functional description:
 *              set driver state, turn off all interrupts,
 *              terminate any existing transfers, and
 *              execute a system escape.
 *
 * output environement:
 *
 *********************************************************}
procedure dvr_error(io_error_code:integer);
begin
	with my_misc_ptr^do
	begin
		if state.bl <> DISABLED_BY_USER then
			state.bl := INACTIVE_ERROR;
		d_isr_ie.bl := 0;
		d_int_e := false;
		set_int;
	end;

	abort_io;
	io_escape(io_error_code, my_tmp_ptr^.my_isc);
end;


{*********************************************************
 * procedure name:
 *              set_int;
 *
 * input dependencies:
 *              my ptrs are valid.
 *              desired driver and user interrupts have been
 *              set up in the misc block.
 *              if true interrupt is desired, this has also
 *              been so indicated.
 *
 * functional description:
 *              reset all existing interrupt latches.
 *              merge both the driver and user interrupt
 *              requests into one, and store in h/w register.
 *              if true interrupt is desired, enable interrupts.
 *
 * output environement:
 *              hardware is set up for desired interrupts.
 *
 *********************************************************}
procedure set_int;
begin
	with my_hw_ptr^, my_misc_ptr^ do
	begin
		int_state.bl := #0; {reset latches}
		int_state.bl := chr(binior(d_isr_ie.bl, u_isr_ie.bl));
		if u_isr_ie.bl <> 0 then
			intdma.int_e := true
		else
			intdma.int_e := d_int_e;
	end;
end;


{*********************************************************
 * procedure name:
 *      reset_peripheral;
 *
 * input dependencies:
 *      my_ptrs are set up.
 *      NOTE: it is not assumed that a peripheral is present,
 *      nor is any attempt made to determine such.
 *
 * functional description:
 *      set state to INACTIVE_ERROR and peripheral_type to
 *          NOT_PRESENT.  This will force the driver to exactly
 *          determine peripheral_type and bus state on next
 *          transfer request.
 *      follow reset specifications:
 *              set wr/nrd high.
 *              float nStrobe and Data lines.
 *              release Busy.
 *              set nInit low - reset the peripheral.
 *              wait for busy high or 25ms.
 *              reset nInit line.
 *              wait for busy low.
 *
 * output environement:
 *              peripheral has been reset, and has correctly
 *              responded.  if user timing is in place and
 *              peripheral does not respond to reset, then
 *              escaped (dvr_error).
 *
 *********************************************************}
procedure reset_peripheral;
var
	use_timer:boolean;
begin
	with my_hw_ptr^, my_misc_ptr^, my_tmp_ptr^ do
	begin
		if peripheral_type < USER_SPEC_NO_DEVICE then
			peripheral_type := NOT_PRESENT;
		if state.bl <> DISABLED_BY_USER then
			state.bl := INACTIVE_ERROR;

		{
		  set host lines in prep for peripheral reset.
		}
		hostline.wr_nrd_high := true;
		intdma.iomod := true;
		intdma.io := false;
		hostline.nselectin_low := true;

		{
		  reset peripheral, and wait for busy to go high.
		}
		hostline.ninit_low := true;
		delay(delay_ms); {wait for everything to settle}
		time_rec.time := 50*delay_ms; {spec says 25}
		start_timer(time_rec);
		repeat until comm_per.c_busy_high or time_expired(time_rec);
		hostline.ninit_low := false;

		{
		  wait for busy to go low.
		}
		use_timer := timeout <> 0;
		if use_timer then
		begin
			time_rec.time := timeout;
			start_timer(time_rec);
		end;
		repeat
			{
			  if busy never goes low, then device is in a bad state.
			}
			if (use_timer) and (time_expired(time_rec)) then
				dvr_error(ioe_timeout);
		until not comm_per.c_busy_high;
	end;
end;


{*********************************************************
 * procedure name:
 *              peripheral_check;
 *
 * input dependencies:
 *              my_ptrs are valid.
 *              device type may/may not be known.
 *
 * functional description:
 *              check that the peripheral is not in an
 *                 error state by checking the select, nerror,
 *                 and perror lines.
 *              if driver has been told to ignore the perror
 *                 line, then do so.
 *
 * output environement:
 *              device is present and ready to talk.
 *              if device not present and user timing in place
 *              and user timeout expired, then set peripheral_type
 *              is set to NOT_PRESENT and escaped (dvr_error)
 *
 *********************************************************}
procedure peripheral_check;
var
	mask:integer;
	use_timer: boolean;
begin
	with my_hw_ptr^, my_misc_ptr^ do
	begin
		if options.ignore_pe then
			mask := hex('6')  {select, and nerror}
		else
			mask := hex('7'); {select, nerror, and perror}
		if (binand(ord(comm_per.bl), mask) <> PLLEL_PERIPHERAL_ONLINE) then
		with my_tmp_ptr^ do
		begin
			use_timer := timeout <> 0;
			if use_timer then
			begin
				time_rec.time := timeout;
				start_timer(time_rec);
			end;
			repeat until (binand(ord(comm_per.bl), mask) =
							PLLEL_PERIPHERAL_ONLINE) or
				     (use_timer and time_expired(time_rec));
			if (use_timer) and (time_expired(time_rec)) then
			begin
				if peripheral_type < USER_SPEC_NO_DEVICE then
				begin
					if (peripheral_type <> HP_BIDIRECTIONAL) or
					   (binand(ord(comm_per.bl), 7) <> 3) then
						peripheral_type := NOT_PRESENT;
				end;
				dvr_error(ioe_timeout);
			end;
		end;
	end;
end;


{*********************************************************
 * procedure name:
 *              set_peripheral_type;
 *
 * input dependencies:
 *              my ptrs active.
 *              peripheral_type is assumed to be NOT_PRESENT.
 *
 *                do not call this routine if the peripheral
 *                type is known - as for output only peripherals
 *                this routine will take 2 seconds.
 *
 *                do not call this routine if peripheral_type
 *                is set by user (>= USER_SPEC_NO_DEVICE) as this
 *                routine will override user specification.
 *
 * functional description:
 *              check if peripheral is online.
 *              if it is, then follow spec to determine if
 *              peripheral is bidirectional or not:
 *                      reset peripheral
 *                      set Wr/nRd to read, but do not take
 *                      over busy line.
 *                      if device bidirectional, it will
 *                      give up Busy line, and since no one
 *                      owns it, it will float high.
 *              since bus is no longer in a known state set current
 *              state to INACTIVE_ERROR, which always forces
 *              the driver to set bus state to desired state.
 *
 * output environement:
 *              peripheral_type is set.
 *              state.bl is INACTIVE_ERROR;
 *
 *********************************************************}
procedure set_peripheral_type;
var
	type_found:boolean;
	i:integer;
begin
	with my_hw_ptr^, my_misc_ptr^, my_tmp_ptr^ do
	begin
		type_found := false;
		i := ord(comm_per.bl);
		if binand(i, hex('1f')) = hex('13') then {device not physically there}
		begin
			type_found := true;
			peripheral_type := NOT_PRESENT;
		end
		else
		begin
			{
			  device is there, see if it is online.
			}
			try
				peripheral_check;
			recover
			begin
				if escapecode = ioescapecode then
				begin
					{
					  peripheral is in bad state, type has been
					  set to NOT_PRESENT.
					}
					type_found := true;
				end
				else
					escape(escapecode);
			end;
		end;


		{
		  now know that a device is there, what type?
		}
		if not type_found then
		begin
			if options.wr_nrd_low then
				peripheral_type := OUTPUT_ONLY
			else
			begin
				{
				  check for bidirectional device
				}
				reset_peripheral;
				intdma.io := false;     {output, give up busy line}
				hostline.wr_nrd_high := false; {tell device input}

				{spec max for peripheral to give up busy is 2 seconds}
				time_rec.time := 2 * delay_second;
				start_timer(time_rec);
				repeat until (comm_per.c_busy_high) or time_expired(time_rec);

				if (comm_per.c_busy_high) then
					peripheral_type := HP_BIDIRECTIONAL
				else
					peripheral_type := OUTPUT_ONLY;
				if peripheral_type = HP_BIDIRECTIONAL then
				begin
					d_isr_ie.nack_low_trans := true;
					set_int;
					hostline.wr_nrd_high := true;
					repeat until int_state.nack_low_trans;
					d_isr_ie.nack_low_trans := false;
					set_int;
					delay(delay_ms); {let everything settle}
				end
				else
					hostline.wr_nrd_high := true;


			end;
		end;
		state.bl := INACTIVE_ERROR;
	end;
end;


{*********************************************************
 * procedure name:
 *              set_wr_nrd_low;
 *
 * input dependencies:
 *              my ptrs are valid.
 *              user has set driver option to maintain
 *                wr_nrd_low at all times.  By definition,
 *                this forces peripheral_type to OUTPUT_ONLY
 *                or NOT_PRESENT.
 *
 * functional description:
 *              reset Wr/nRd.  If peripheral_type is set to
 *                bidirectional device, set it to OUTPUT_ONLY.
 *
 * output environement:
 *              Wr/nRd is low.  peripheral_type is legal.
 *
 *********************************************************}
procedure set_wr_nrd_low;
begin
	with my_hw_ptr^, my_misc_ptr^ do
	begin
		hostline.wr_nrd_high := false;
		if peripheral_type in INPUT_SET then
				peripheral_type := OUTPUT_ONLY;
	end;
end;


{*********************************************************
 * procedure name:
 *              set_bus_out;
 *
 * input dependencies:
 *              my ptrs are active.  peripheral_type may or
 *              may not be known.
 *
 * functional description:
 *              if peripheral type is not known, then determine
 *              its type.
 *              if peripheral can talk output, then set bus to
 *              output state and set state to INACTIVE_WRITE.
 *
 * output environement:
 *              bus in output state, peripheral is in a known
 *              state and driver state is INACTIVE_WRITE.
 *              OR ELSE, driver error.
 *
 *********************************************************}
procedure set_bus_out;
begin
	with my_misc_ptr^ do
	begin
		if (peripheral_type = NOT_PRESENT) then
			set_peripheral_type;
		if (peripheral_type in OUTPUT_SET) then
		with my_hw_ptr^ do
		begin
			if not state.write then
			begin
				d_isr_ie.nack_low_trans := false;
				set_int;
				intdma.iomod := (not options.use_nack);
				intdma.io := false;
				hostline.wr_nrd_high := (not options.wr_nrd_low);
				state.bl := INACTIVE_WRITE;
			end;
		end
		else if peripheral_type < USER_SPEC_NO_DEVICE then
			dvr_error(ioe_timeout)
		else
			dvr_error(ioe_misc);
	end;
end;


{*********************************************************
 * procedure name:
 *              set_bus_in(set_iomod:boolean);
 *
 * input dependencies:
 *              my ptrs are active.  peripheral_type may or
 *              may not be known.  if set_iomod is true,
 *              caller wants small fifo, large fifo otherwise.
 *
 * functional description:
 *              if peripheral type is not known, then determine
 *              its type.
 *              if peripheral can talk input, then wait for
 *              fifo to emtpy out (from last write) and set bus to
 *              output state and set state to INACTIVE_READ.
 *
 * output environement:
 *              bus in input state, peripheral is in a known
 *              state and driver state is INACTIVE_READ.
 *              OR ELSE, driver error.
 *
 *********************************************************}
procedure set_bus_in(set_iomod:boolean);
var
	use_timer:boolean;
begin
	with my_misc_ptr^ do
	begin
		if (peripheral_type = NOT_PRESENT) then
			set_peripheral_type;
		if (peripheral_type in INPUT_SET) then
		with my_hw_ptr^, my_tmp_ptr^ do
		begin
			if not state.read then
			begin
				{
				  if h/w is in an output state:
				     wait for last byte transmited
				     wait for not busy (thus nack on last byte is seen)
				}
				if (not intdma.io) then
				begin
					use_timer := timeout <> 0;
					if use_timer then
					begin
						time_rec.time := timeout;
						start_timer(time_rec);
					end;
					repeat
						if (use_timer) and (time_expired(time_rec)) then
							dvr_error(ioe_timeout);
					until (binand(ord(comm_per.bl),XMITTED_MASK) = LAST_BYTE_XMITTED);
					repeat
						if (use_timer) and (time_expired(time_rec)) then
							dvr_error(ioe_timeout);
					until not comm_per.c_busy_high;
				end;

				{
				  used to always set iomod to 1, which forced FIFO to a size
				  of 1.  No longer true due to rewrite of fhs_in and
				  dma_int_handler.
				}
				intdma.iomod := set_iomod;
				intdma.io := true;
				d_isr_ie.nack_low_trans := true;
				set_int;
				hostline.wr_nrd_high := false;
				state.bl := INACTIVE_READ;
			end;
		end
		else if peripheral_type < USER_SPEC_NO_DEVICE then
			dvr_error(ioe_timeout)
		else
			dvr_error(ioe_misc);
	end;
end;


{*********************************************************
 * procedure name:
 *              fhs_out(buf:ptr_char_type; var count:integer);
 *
 * input dependencies:
 *              my ptrs are set up.
 *              peripheral_type may or may not be known.
 *              bus may or may not be in output phase.
 *              peripheral may or may not be ready to talk.
 *
 * functional description:
 *              make sure peripheral can talk output and set
 *              bus to output phase.
 *              programmatically transfer data to peripheral.
 *              if driver option write_verify is on, then verify
 *              data byte handshaked with peripheral.
 *
 * output environement:
 *              peripheral_type is known and bus is in output state.
 *              OR ELSE dvr_error.
 *
 *********************************************************}
procedure fhs_out(buf:ptr_char_type; var count:integer);
var
	use_timer:boolean;
begin
	with my_hw_ptr^, my_misc_ptr^, my_tmp_ptr^ do
	begin
		{
		  make sure device is not in an error state.
		  make sure device can talk output, and
		  set bus to output state.
		}
		peripheral_check;
		set_bus_out;
		state.bl := ACTIVE_WRITE;

		{
		  set up some local variables to save a little bit of time.
		}
		use_timer := timeout <> 0;

		{
		  do programmatic transfer.
		}
		while count > 0 do
		begin
			if (comm_per.c_fifofull) then
			begin
				{
				  make sure peripheral hasn't seen an error.
				}
				peripheral_check;


				{
				  peripheral is o.k., wait for fifo to empty out.
				}
				if use_timer then
				begin
					time_rec.time := timeout;
					start_timer(time_rec);
				end;
				repeat
					if (use_timer) and (time_expired(time_rec)) then
						dvr_error(ioe_timeout);
				until not comm_per.c_fifofull;
			end;
			fifo.fifoout := buf^;
			buf := addr(buf^, 1);
			count := count - 1;
		end;
		if options.write_verify then
		begin
			{
			  wait for fifo empty and nStrobe not asserted.
			}
			if use_timer then
			begin
				time_rec.time := timeout;
				start_timer(time_rec);
			end;
			repeat
				if (use_timer) and (time_expired(time_rec)) then
					dvr_error(ioe_timeout);
			until (binand(ord(comm_per.bl), XMITTED_MASK) = LAST_BYTE_XMITTED);
		end
		else
			{
			  make sure device is still communicating.
			}
			peripheral_check;

		state.bl := INACTIVE_WRITE;
	end;
end;


{*********************************************************
 * procedure name:
 *              wait_nack;
 *
 * input dependencies:
 *              my ptrs are set up.
 *              a nack transition interrupt has been enabled.
 *              a inbound transfer has just occured, and
 *                want to see if an nack will occur with this
 *                byte.
 *
 * functional description:
 *              wait for either something in FIFO (obviously
 *              no nack on this byte) or an nack interrupt, or
 *              an error.
 *
 * output environement:
 *              if nack occured then last_read_nack (misc_ptr)
 *              set to true.
 *
 *********************************************************}
procedure wait_nack;
var
	i:integer;
begin
	with my_hw_ptr^ do
	begin
		if comm_per.c_fifoempty then
		begin
			repeat until (not comm_per.c_fifoempty) or (int_state.bl <> #0);
			if (int_state.nack_low_trans) then
			begin
				my_misc_ptr^.last_read_nack := true;
				set_int; {clear latches, reset interrupt}
			end;
		end;
	end;
end;


{*********************************************************
 * procedure name:
 *              fhs_in(buf:ptr_char_type; var count:integer,
 *                     exit_on_nack:boolean);
 *
 * input dependencies:
 *              my ptrs are set up.
 *              peripheral_type may or may not be known.
 *              bus may or may not be in input phase.
 *              peripheral may or may not be ready to talk.
 *
 * functional description:
 *              make sure peripheral can talk input and set
 *              bus to input phase.
 *              programmatically transfer data from peripheral.
 *              if exit_on_nack, then look for an nack occurence.
 *
 * output environement:
 *              peripheral_type is known and bus is in input state.
 *              OR ELSE dvr_error.
 *
 *********************************************************}
procedure fhs_in(buf:ptr_char_type; var count:integer; exit_on_nack:boolean);
label 1;
var
	use_timer:boolean;
begin
	with my_hw_ptr^, my_misc_ptr^, my_tmp_ptr^ do
	begin
		{
		  make sure device is not in an error state.
		  make sure device can talk input, and
		  set bus to input state.
		}
		peripheral_check;
		set_bus_in(USE_LARGE_FIFO);
		state.bl := ACTIVE_READ;

		{
		  set up some local variables to save a little bit of time.
		}
		use_timer := timeout <> 0;
		last_read_nack := false;

		{
		  do programmatic transfer
		}
		while count > 0 do
		begin
			if (comm_per.c_fifoempty) then
			begin
				peripheral_check;
				if use_timer then
				begin
					time_rec.time := timeout;
					start_timer(time_rec);
				end;
				repeat
				until (not comm_per.c_fifoempty) or
				      (int_state.bl <> #0) or
				      (use_timer and time_expired(time_rec));
				if comm_per.c_fifoempty then
				begin
					if (exit_on_nack) and (int_state.nack_low_trans) then
					begin
						last_read_nack := true;
						goto 1;
					end
					else
						dvr_error(ioe_timeout);
				end;
			end;
			buf^ := fifo.fifoin;
			buf := addr(buf^, 1);
			count := count - 1;
		end;

		{
		  if we are supposed to exit_on_nack, then there is a very good possibility
		  that the nack will occur with the last byte read.  Therefore, wait for
		  one to possibly show up.
		}
		if exit_on_nack then
			wait_nack;


		1:
		state.bl := INACTIVE_READ;
	end;
end;


{*********************************************************
 * procedure name:
 *              do_fhs_tfr(ptr_buf:ptr_buf_info_type);
 *
 * input dependencies:
 *              User has requested a programmatic transfer.
 *              my ptrs are set up.
 *              device may or may not be there (or ready).
 *              device type may or may not even be known!
 *
 * functional description:
 *              execute the desired transfer request.  This
 *              routine is responsible for updating the buffer
 *              parameters (Standard I/O buffer).
 *              If this is an output transfer, use fhs_out.
 *              If this is an input trnasfer, use fhs_in.
 *
 * output environement:
 *              If successful, transfer has completed as requested
 *              and the buffer parameters have been updated.  The
 *              device type is known and the bus is in an output
 *              state.
 *              If not successful, then an escape has occured, bus
 *              state is not known and the device type is not known.
 *
 *********************************************************}
procedure do_fhs_tfr(ptr_buf:ptr_buf_info_type);
var
	my_count:       integer;
	lastchar:       char;
begin
	with ptr_buf^, my_misc_ptr^ do
	begin
		act_tfr := FHS_tfr;
		my_count := term_count;
		if direction = FROM_MEMORY then
		begin
			fhs_out(buf_empty, term_count);
			buf_empty := addr(ptr_char_type(buf_empty)^, my_count - term_count);
		end
		else {direction is TO_MEMORY}
		begin
			if term_char = -1 then {not a transfer_until}
			begin
				fhs_in(buf_fill, term_count, end_mode);
				buf_fill := addr(ptr_char_type(buf_fill)^,
						 my_count - term_count);
			end
			else {transfer_until}
			begin
				repeat
					my_count := 1;
					fhs_in(buf_fill, my_count, end_mode);
					lastchar := ptr_char_type(buf_fill)^;
					buf_fill := addr(ptr_char_type(buf_fill)^, 1);
					term_count := term_count - 1;
				until (term_count = 0) or
				      (lastchar = chr(term_char)) or
				      ((last_read_nack) and (end_mode));
			end;
		end;

		{serial_FHS transfer has completed normally, clean up.}
		stclr;
	end;
end;


{*********************************************************
 * procedure name:
 *              FUNCTION dma_available:boolean;
 *
 * input dependencies:
 *              none
 *
 * functional description:
 *              determine if DMA hardware is available, if the
 *              DMA driver is in memory, and if a DMA channel is
 *              available for use (note that a DMA channel is not
 *              acquired).
 *
 * output environement:
 *              TRUE if dma h/w, s/w and channel is available,
 *              FALSE otherwise.
 *
 *********************************************************}
function dma_available:boolean;
begin
	dma_available := (
			   (dma_here) and
			   ((dma_isc_0 = no_isc) or (dma_isc_1 = no_isc))
			 );
end;


{*********************************************************
 * procedure name:
 *              FUNCTION get_buf_ptr:ptr_buf_info_type
 *
 * input dependencies:
 *              my ptrs are set up.
 *
 * functional description:
 *              retrieve either the input or output buffer
 *              pointer.  Since only one can be active at a
 *              time, only need to make one check.  If niether
 *              is active, then NIL will be returned.
 *
 * output environement:
 *              returns either the current active buffer pointer
 *              or NIL.
 *
 *********************************************************}
function get_buf_ptr:ptr_buf_info_type;
begin
	with my_tmp_ptr^ do
		if in_bufptr <> NIL then
			get_buf_ptr := in_bufptr
		else
			get_buf_ptr := out_bufptr;
end;


{*********************************************************
 * procedure name:
 *              dma_int_handler(temp:ANYPTR) - temp is the
 *                      driver temp space.
 *
 * input dependencies:
 *              received either a DMA complete interrupt or
 *              an nack occured interrupt.
 *
 * functional description:
 *              turn off DMA, update buffer pointers with
 *              transfer amount.  Terminate transfer buffer, and
 *              clean up hardware.
 *
 * output environement:
 *              DMA and transfer hardware cleaned up and ready
 *              for next transfer.  Transfer buffer updated and
 *              terminated.
 *
 *********************************************************}
procedure dma_int_handler(temp:ANYPTR);
var
	ptr_buf:ptr_buf_info_type;
	diroutput:boolean;
	residual_count, amt_xfered:integer;
begin
	set_my_ptrs(temp);
	with get_buf_ptr^, my_misc_ptr^, my_hw_ptr^ do
	begin
		intdma.dma_e0 := false;
		intdma.dma_e1 := false;
		residual_count := dropdma;
		amt_xfered := term_count - residual_count;
		if direction = FROM_MEMORY then
		begin
			buf_empty := addr(ptr_char_type(buf_empty)^, amt_xfered);

			{
			  wait for fifoempty
			}
			repeat until (comm_per.c_fifoempty) or (not comm_per.p_select_high);
		end
		else
		begin
			buf_fill := addr(ptr_char_type(buf_fill)^, amt_xfered);

			{
			  if this is a transfer_end then
			  wait for fifo not empty or an interrupt.
			}
			if end_mode then
				wait_nack;
		end;
		term_count := residual_count;
		d_isr_ie.bl := 0;
		d_int_e := false;
		set_int;
		stclr;
	end;
end;


{*********************************************************
 * procedure name:
 *              dma_start(bcb:ANYPTR) - bcb: Buffer Control block Pointer
 *
 * input dependencies:
 *              my ptrs are set up.
 *              User has made a valid request for either an
 *              inbound or outbound DMA transfer
 *              bus state may or may not be known.
 *              device type may or may not be known.
 *
 * functional description:
 *              set up hardware and software for a DMA transfer
 *              kick off the DMA transfer
 *
 * output environement:
 *              If successful, then DMA transfer started, bus state
 *              and device type known.
 *              If not successful, then an escape has occured, bus
 *              state is not known and device type is not known.
 *
 *********************************************************}
procedure dma_start(bcb:ANYPTR);
var
	dma_channel:integer;
	loc_intdma:intdma_type;
begin
	with my_misc_ptr^, ptr_buf_info_type(bcb)^, my_hw_ptr^ do
	begin
		{
		  insure dma enable bits are off (a little bit of
		  paranoia is good for the soul).
		}
		intdma.dma_e0 := false;
		intdma.dma_e1 := false;

		{
		  make sure device can talk
		}
		peripheral_check;

		{
		  set up the bus for the direction of the transfer,
		  and set up request for driver interupts.
		}
		if direction = FROM_MEMORY then
		begin
			set_bus_out;
			state.bl := ACTIVE_WRITE;
			d_isr_ie.bl := hex('07');
		end
		else
		begin
			set_bus_in(USE_LARGE_FIFO);
			state.bl := ACTIVE_READ;
			last_read_nack := false;
			if end_mode then
				d_isr_ie.bl := hex('0f')
			else
				d_isr_ie.bl := hex('07');
		end;
		if options.ignore_pe then
			d_isr_ie.pe_trans := false;

		{
		  get dma channel, set it up and arm it.
		}
		dma_channel := getdma;  {this gets a dma channel, sets it up, and arms it}

		{
		  haven't escaped yet so everthing is ready to communicate.
		  turn on requested interrupts.
		  Note that set_int should follow getdma.  Don't want to escape
		  with interrupts set!
		}
		d_int_e := true;
		set_int;


		{
		  set up for DMA interrupt (based on channel received) and kick off the
		  DMA transfer.

		  h/w required fix.  when doing inbound DMA, turn off i/o bit, then turn on
		  I/O and DMA enable at the same time.  for ease of implementation, use same
		  algorithm for both inbound and outbound DMA.
		}
		loc_intdma.bl := intdma.bl;
		intdma.io := false;
		if dma_channel = 0 then
		begin
			dma_ch_0.real_proc := dma_int_handler;
			loc_intdma.dma_e0 := true;
		end
		else
		begin
			dma_ch_1.real_proc := dma_int_handler;
			loc_intdma.dma_e1 := true;
		end;
		intdma.bl := loc_intdma.bl; {this kicks off the DMA}
	end;
end;



{*********************************************************
 * procedure name:
 *              ovl_int_out_start
 *
 * input dependencies:
 *              my ptrs set up.
 *              bus state may or may not be known,
 *              device type may or may not be known.
 *
 * functional description:
 *              start an ouput overlap interrupt transfer
 *              sequence.
 *
 * output environement:
 *              if successful, bus is in output state,
 *              device type is known and overlap interrupt
 *              transfer in output direction is started.
 *              if not successful, this routine has escaped.
 *
 *********************************************************}
procedure ovl_int_out_start;
begin
	with my_misc_ptr^ do
	begin
		peripheral_check;
		set_bus_out;
		state.bl := ACTIVE_WRITE;
		d_isr_ie.fifo_empty := true;
		d_int_e := true;
		set_int;
	end;
end;


{*********************************************************
 * procedure name:
 *              ovl_int_in_start
 *
 * input dependencies:
 *              my ptrs set up.
 *              bus state may or may not be known,
 *              device type may or may not be known.
 *
 * functional description:
 *              start an input overlap interrupt transfer
 *              sequence.
 *              note a fifo of size 1 is used for this type
 *                 of transfer.  This allows an interrupt
 *                 with each byte transfered.
 *
 * output environement:
 *              if successful, bus is in input state,
 *              device type is known and overlap interrupt
 *              transfer in input direction is started.
 *              if not successful, this routine has escaped.
 *
 *********************************************************}
procedure ovl_int_in_start;
begin
	with my_misc_ptr^ do
	begin
		peripheral_check;
		state.bl := INACTIVE_ERROR;
		set_bus_in(USE_SMALL_FIFO);
		state.bl := ACTIVE_READ;
		d_isr_ie.fifo_full := true;
		d_int_e := true;
		set_int;
	end;
end;


{*********************************************************
 * procedure name:
 *              do_ovl_int_fifo_empty
 *
 * input dependencies:
 *              my ptrs set up
 *              a fifo empty interrupt has occured
 *              an output overlap interrupt transfer is
 *                 currently in progress.
 *
 * functional description:
 *              update buffer pointers, get another byte into
 *              fifo, and set up for another interrupt.
 *              if transfer has completed, terminate buffer.
 *              since can't escape from an interrupt, handle
 *                all errors here - if error occurs, terminate
 *                transfer.
 *
 * output environement:
 *              either transfer is continuing or has terminated.
 *              if transfer terminated, then h/w and s/w are cleaned
 *              up.
 *
 *********************************************************}
procedure do_ovl_int_fifo_empty;
var
	my_count:integer;
begin
	with get_buf_ptr^, my_misc_ptr^ do
	begin
		try
			my_count := 1;
			fhs_out(buf_empty, my_count);
			buf_empty := addr(ptr_char_type(buf_empty)^, 1);
			term_count := term_count - 1;
			if (term_count = 0) then {transfer completed}
			begin
				stclr;
				d_isr_ie.fifo_empty := false;
				d_int_e := false;
				set_int;
			end
			else
			begin
				d_isr_ie.fifo_empty := true;
				d_int_e := true;
				set_int;
			end;
		recover
		if (escapecode = ioescapecode) then
		begin
			stclr;
			d_isr_ie.fifo_empty := false;
			d_int_e := false;
			set_int;
		end;
	end;
end;


{*********************************************************
 * procedure name:
 *              do_ovl_int_fifo_full
 *
 * input dependencies:
 *              my ptrs set up
 *              a fifo full interrupt has occured
 *              an input overlap interrupt transfer is
 *                 currently in progress.
 *
 * functional description:
 *              update buffer pointers, get another byte from
 *              fifo, and set up for another interrupt.
 *              if transfer has completed, terminate buffer.
 *              since can't escape from an interrupt, handle
 *                all errors here - if error occurs, terminate
 *                transfer.
 *
 * output environement:
 *              either transfer is continuing or has terminated.
 *              if transfer terminated, then h/w and s/w are cleaned
 *              up.
 *
 *********************************************************}
procedure do_ovl_int_fifo_full;
var
	my_count:integer;
	last_char:char;
begin
	with get_buf_ptr^, my_misc_ptr^ do
	begin
		try
			peripheral_check;
			my_count := 1;
			fhs_in(buf_fill, my_count, end_mode);
			last_char := ptr_char_type(buf_fill)^;
			buf_fill := addr(ptr_char_type(buf_fill)^, 1);
			term_count := term_count - 1;
			if (term_count = 0) or
			   (end_mode and last_read_nack) or
			   ((term_char <> -1) and (last_char = chr(term_char))) then
			begin
				stclr;
				state.bl := INACTIVE_ERROR;
				d_isr_ie.fifo_full := false;
				d_int_e := false;
				set_int;
			end
			else
			begin
				d_isr_ie.fifo_full := true;
				d_int_e := true;
				set_int;
			end;
		recover
		if (escapecode = ioescapecode) then
		begin
			stclr;
			d_isr_ie.fifo_full := false;
			d_int_e := false;
			set_int;
		end;
	end;
end;

end {pllel_util};

$page$
module pllel_drive;
{------------------------------------------------------------------------------}
{------------------  parallel driver hook flow control       ------------------}
{------------------------------------------------------------------------------}
{

	This module acts as a demuxer for the PWS I/O system.  I/O system calls
	enter into this module and are translatted into PARALLEL I/O requests.
	The later requests are invoked by calls to routines within the PLLEL_UTIL
	module.

	The functionality of each of the I/O requests are defined in either the
	Procedure Library GENERAL I/O discussions, the Procedure Library PARALLEL
	Inteface discussions, or in the Systems Designers Guide.  Unless necessary,
	the functionality is NOT repeated here in the guise of routine headers.
}


import sysglobals, asm, iodeclarations, iocomasm, parallel_3, pllel_util, pllel_asm;

export

	procedure       pinit_hook(temp:ANYPTR);
	procedure       pisr_hook(temp:PISRIB);
	procedure       prdb_hook(temp:ANYPTR; VAR x:CHAR);
	procedure       pwtb_hook(temp:ANYPTR;     x:CHAR);
	procedure       prdw_hook(temp:ANYPTR; VAR x:io_word);
	procedure       pwtw_hook(temp:ANYPTR;     x:io_word);
	procedure       prds_hook(temp:ANYPTR; reg:io_word; VAR x : io_word);
	procedure       pwtc_hook(temp:ANYPTR; reg:io_word;     x : io_word );
	procedure       pend_hook(temp:ANYPTR; VAR b:boolean);
	procedure       ptfr_hook(temp:ANYPTR; bcb:ANYPTR );


implement

procedure pinit_hook(temp:ANYPTR);
var
	timevalue:integer;
begin
	try {this routine should NEVER escape!}

		{
		  this routine should also not go into a forever loop for whatever
		  reason.  save timeout value, and set timeout to 1ms.
		}
		with pio_tmp_ptr(temp)^ do
		begin
			timevalue := timeout;
			timeout := 1;
		end;

		set_my_ptrs(temp);

		with my_hw_ptr^, my_tmp_ptr^, my_misc_ptr^ do
		begin

			{
			  clear up any existing transfers
			}
			if state.active_xfer then
				abort_io;

			{
			  restore reset defaults
			}
			peripheral_type := peripheral_reset;
			if peripheral_type = USER_SPEC_NO_DEVICE then
				state.bl := DISABLED_BY_USER
			else
				state.bl := INACTIVE_ERROR;
			options.w := options_reset.w;
			if options.wr_nrd_low then
				set_wr_nrd_low;


			{
			  reset h/w
			}
			intdma.int_e := false;
			hostline.bl := #0;               {turn off all hostlines}
			sysreg.softreset := #0;
			delay(delay_ms);
			if not options.wr_nrd_low then
				hostline.wr_nrd_high := true;
			d_int_e := false;
			intdma.io := false;  {output forces chip to own data & nstrobe lines}
			d_isr_ie.w := 0;
			u_isr_ie.w := 0;
			set_int;
			hostline.nselectin_low := true;


			{
			  reset driver variables
			}
			u_isr_status.w := 0;
			last_read_nack := false;
			user_isr.dummy_pr := nil;
			user_isr.dummy_sl := nil;
			in_bufptr := nil;
			out_bufptr := nil;
			my_int_level := intdma.int_lvl + 3;
		end;
	recover
		;

	{
	  restore timeout value
	}
	try
		pio_tmp_ptr(temp)^.timeout := timevalue;
	recover
		;
end;

procedure pisr_hook(temp:PISRIB);
type
	pxlate_type = record
			case integer of
				1:(pproc:parallel_user_isr_type);
				2:(ioproc:io_proc);
		end;

var
	save_ioe_result:integer;
	save_ioe_isc:integer;
	save_int_state:int_state_type;
	d_isr_ir:driver_int_state_type;
	ptr_buf:ptr_buf_info_type;
	p:pxlate_type;
	do_u_isr_ovl:boolean;

	procedure kill_act_tfr;
	begin
		with my_misc_ptr^ do
		begin
			d_isr_ie.bl := 0;
			d_int_e := false;
			abort_io;
		end;
	end;
begin
	do_u_isr_ovl := false;
	set_my_ptrs(anyptr(temp));
	with my_tmp_ptr^, my_misc_ptr^, my_hw_ptr^ do
	begin
		{
		  save current h/w interrupts - support routines will reset the
		  driver interrupt conditions and call set_int.  set_int resets
		  the h/w interrupt conditions, thus a copy is necessary
		}
		save_int_state.bl := int_state.bl;

		{handle driver interrupts}
		d_isr_ir.bl := binand(d_isr_ie.bl, ord(save_int_state.bl));
		if (d_isr_ir.bl <> 0) then
		begin
			{only supposed to be getting driver interrupts with
			 active transfers}
			ptr_buf := get_buf_ptr;
			if (ptr_buf = nil) then
			begin
				{reset all driver interrupts - this should never happen}
				d_isr_ie.bl := 0;
				d_int_e := false;
			end
			else
			with ptr_buf^ do
			begin
				{
				  utility routines will set ioe_result and ioe_isc.
				  Can't allow ioe_result and ioe_isc to be modified!
				}
				save_ioe_result := ioe_result;
				save_ioe_isc := ioe_isc;
				if (act_tfr = DMA_tfr) then
				begin
					if binand(ord(save_int_state.bl), hex('07')) <> 0 then
					begin
						{peripheral gone off line during dma transfer,
						 wait for peripheral to respond}
						try
							peripheral_check;
							{if didn't escape, peripheral back online and
							 DMA continuing}
						recover
							kill_act_tfr;
					end
					else if save_int_state.nack_low_trans then
					begin
						last_read_nack := true;
						dma_int_handler(temp);
					end
					else
						kill_act_tfr;
				end
				else if (act_tfr = INTR_tfr) then
				begin
					do_u_isr_ovl := true;
					if save_int_state.fifo_full then
						do_ovl_int_fifo_full
					else if save_int_state.fifo_empty then
						do_ovl_int_fifo_empty
					else
					begin
						do_u_isr_ovl := false;
						kill_act_tfr;
					end;
				end
				else if (act_tfr = FHS_tfr) then
				begin
					try
						{fake out the tfr hook}
						d_isr_ie.fifo_empty := false;
						d_isr_ie.fifo_full := false;
						d_int_e := false;
						set_int;
						do_fhs_tfr(ptr_buf);
					recover
						kill_act_tfr;
				end
				else
					kill_act_tfr;
				ioe_result := save_ioe_result;
				ioe_isc := save_ioe_isc;
			end;

		end;

		{handle user isr interrupts}
		u_isr_status.w := 0;
		u_isr_status.bl := binand(u_isr_ie.bl, ord(save_int_state.bl));
		if (u_isr_ie.xfer_extend) and (do_u_isr_ovl) then
			u_isr_status.xfer_extend := true;
		if (u_isr_status.bl <> 0) then
		begin
			{disable user interrupts on interrupting conditions only.}
			u_isr_ie.bl := u_isr_ie.bl - u_isr_status.bl;

			{call the user isr}
			p.ioproc := user_isr.real_proc;
			call(p.pproc, my_isc);

			{clear user isr status}
			u_isr_status.w := 0;
		end;

		{reset up any interrupts}
		set_int;
	end;
end;

procedure prdb_hook(temp:ANYPTR; VAR x:CHAR);
var
	count:integer;
begin
	set_my_ptrs(temp);
	count := 1;
	fhs_in(addr(x), count, true);
end;

procedure pwtb_hook(temp:ANYPTR; x:CHAR);
var
	count:integer;
begin
	set_my_ptrs(temp);
	count := 1;
	fhs_out(addr(x), count);
end;

procedure prdw_hook(temp:ANYPTR; VAR x:io_word);
var
	count:integer;
begin
	set_my_ptrs(temp);
	count := 2;
	fhs_in(addr(x), count, true);
end;

procedure pwtw_hook(temp:ANYPTR; x:io_word);
var
	count:integer;
begin
	set_my_ptrs(temp);
	count := 2;
	fhs_out(addr(x), count);
end;

procedure prds_hook(temp:ANYPTR; reg:io_word; VAR x:io_word);
var
	status:p3regs_type;
begin
	set_my_ptrs(temp);
	with my_hw_ptr^, my_tmp_ptr^, my_misc_ptr^ do
	begin
		status.w := 0;
		case reg of
			PLLEL_REG_CARD_ID:
				begin
					status.w := PARALLEL_CARDID;
				end;
			PLLEL_REG_INTDMA_STATUS:
				begin
					status.bl := ord(intdma.bl);
					status.intdma_status.pad := 0;
				end;
			PLLEL_REG_PERIPHERAL_STATUS:
				begin
					status.bl   := ord(comm_per.bl);
					status.peripheral_status.pad := 0;
				end;
			PLLEL_REG_COMM_STATUS:
				begin
					status.bl := binlsr(ord(comm_per.bl),3);
					status.comm_status.pad := 0;
				end;
			PLLEL_REG_HOST_LINE_CONTROL:
				begin
					status.bl := ord(hostline.bl);
					status.host_line.pad := 0;
				end;
			PLLEL_REG_IO_CONTROL:
				begin
					status.bl := binlsr(ord(intdma.bl),2);
					status.io_control.pad := 0;
				end;
			PLLEL_REG_FIFO:
				begin
					status.bl := ord(fifo.fifoin);
				end;
			PLLEL_REG_PERIPHERAL_TYPE:
				begin
					if peripheral_type = NOT_PRESENT then
					try
						set_peripheral_type;
					recover
						if escapecode <> ioescapecode then
							escape(escapecode);

					status.bl := peripheral_type;
				end;
			PLLEL_REG_TYPE_RESET:
				begin
					status.bl := peripheral_reset;
				end;
			PLLEL_REG_INTERRUPT_STATE:
				begin
					status.bl := d_isr_ie.bl;
				end;
			PLLEL_REG_DRIVER_OPTIONS:
				begin
					status.bl := options.bl;
				end;
			PLLEL_REG_OPTIONS_RESET:
				begin
					status.bl := options_reset.bl;
				end;
			PLLEL_REG_DRIVER_STATE:
				begin
					status.driver_state := state;
				end;
			PLLEL_REG_HOOK_STATUS:
				begin
					if (user_isr.dummy_pr = NIL) then
						status.bl := USER_ISR_HOOK_INACTIVE
					else
						status.bl := USER_ISR_HOOK_ACTIVE;
				end;
			PLLEL_REG_USER_ISR_ENABLE:
				begin
					status.user_isr_status := u_isr_ie;
				end;
			PLLEL_REG_USER_ISR_STATUS:
				begin
					status.user_isr_status := u_isr_status;
				end;
			OTHERWISE
				dvr_error(ioe_rds_wtc);
		end;
		x := status.w;
	end;
end;

procedure pwtc_hook(temp:ANYPTR; reg:io_word; x:io_word );
var
	control:p3regs_type;
	peripheral_online:boolean;
	timevalue:integer;
begin
	set_my_ptrs(temp);
	with my_hw_ptr^, my_tmp_ptr^, my_misc_ptr^ do
	begin
		control.w := x;
		case reg of
			PLLEL_REG_RESET:
				begin
					pinit_hook(temp);
				end;
			PLLEL_REG_HOST_LINE_CONTROL:
				begin
					control.host_line.pad := 0;
					hostline.bl := chr(control.bl);
				end;
			PLLEL_REG_IO_CONTROL:
				begin
					intdma.io       := control.io_control.input_high;
					intdma.iomod    := control.io_control.modify_io;

					{force driver to set up bus on next transfer}
					if not state.disabled then
						state.bl := INACTIVE_ERROR;
				end;
			PLLEL_REG_FIFO:
				begin
					fifo.fifoout := chr(control.bl);
				end;
			PLLEL_REG_PERIPHERAL_TYPE,
			PLLEL_REG_TYPE_RESET:
				begin
					if control.bl in USER_SET then
					begin
						if reg = PLLEL_REG_PERIPHERAL_TYPE then
						begin
							if (options.wr_nrd_low) and
							   (control.bl in INPUT_SET) then
								dvr_error(ioe_rds_wtc);
							peripheral_type := control.bl;
						end
						else
							peripheral_reset := control.bl;
					end
					else
						dvr_error(ioe_rds_wtc);
				end;
			PLLEL_REG_PERIPHERAL_RESET:
				begin
					{
					  do a quick test to see if the attached peripheral
					  is online.

					  Want to do a hard reset if the device is not online,
					  the device type is specified by user, or the wr_nrd_low
					  option is used.

					  Otherwise, call set_peripheral_type which will do a reset
					  and additionally determine the type of the attached device.
					  This prevents a 'double reset'.
					}
					timevalue := timeout;
					timeout := 1;
					try
						peripheral_check;
						peripheral_online := true;
						timeout := timevalue;
					recover
					begin
						timeout := timevalue;
						if escapecode = ioescapecode then
							peripheral_online := false
						else
							escape(escapecode);
					end;
					if (not peripheral_online) or
					   (peripheral_type in [USER_SPEC_NO_DEVICE,
							       USER_SPEC_OUTPUT_ONLY,
							       USER_SPEC_HP_BIDIRECTIONAL]) or
					   (options.wr_nrd_low) then
						reset_peripheral
					else
					begin
						peripheral_type := NOT_PRESENT;
						set_peripheral_type;
					end;
				end;
			PLLEL_REG_DRIVER_OPTIONS:
				begin
					control.bh := 0;
					control.driver_options.pad := 0;
					if options.wr_nrd_low and
					   not control.driver_options.wr_nrd_low then
					begin {wr_nrd_low is being turned off}
						peripheral_type := NOT_PRESENT;
					end;
					options.w := control.w;
					if options.wr_nrd_low then
						set_wr_nrd_low;
					{
					  force bus to be set on next write
					  this forces use_nack and other options in place.
					}
					state.bl := INACTIVE_ERROR;
				end;
			PLLEL_REG_OPTIONS_RESET:
				begin
					control.bh := 0;
					control.driver_options.pad := 0;
					options_reset.w := control.w;
				end;
			PLLEL_REG_HOOK_CLEAR:
				begin
					u_isr_ie.w := 0;
					u_isr_status.w := 0;
					user_isr.dummy_pr := NIL;
					user_isr.dummy_sl := NIL;
					set_int;
				end;
			PLLEL_REG_USER_ISR_ENABLE:
				begin
					if user_isr.dummy_pr = NIL then
						dvr_error(ioe_rds_wtc);

					u_isr_ie.bl := control.bl;
					set_int;
				end;
			OTHERWISE
				dvr_error(ioe_rds_wtc);
		end;
	end;
end;

procedure pend_hook(temp:ANYPTR; VAR b:boolean);
begin
	set_my_ptrs(temp);
	b := my_misc_ptr^.last_read_nack;
end;

procedure ptfr_hook(temp:ANYPTR; bcb:ANYPTR );
VAR
	tmp:            integer;
BEGIN
	set_my_ptrs(temp);
	with my_tmp_ptr^, ptr_buf_info_type(bcb)^, my_misc_ptr^ do
	begin
		{check for illegal full duplex transfer request}
		if (direction = TO_MEMORY) and (out_bufptr <> NIL) then
		begin
			dvr_error(ioe_bad_tfr);
		end
		else if (direction = FROM_MEMORY) and (in_bufptr <> NIL) then
		begin
			dvr_error(ioe_bad_tfr);
		end;

		{check for illegal word transfer request}
		if (b_w_mode = TRUE) then
		begin
			dvr_error(ioe_bad_tfr);
		end;

		{check for illegal transfer end request for outbound transfers}
		if (end_mode) and (direction = FROM_MEMORY) then
		begin
			dvr_error(ioe_bad_tfr);
		end;

		{check for illegal transfer request}
		if (usr_tfr in [dummy_tfr_1, dummy_tfr_2]) then
		begin
			dvr_error(ioe_bad_tfr);
		end;


		{ adjust transfer request type of FASTEST }
		if usr_tfr in [serial_FASTEST, overlap_FASTEST] then
		begin
			usr_tfr := pred(usr_tfr);  {--> FHS}
			if (dma_available) then
			begin
				usr_tfr := pred(usr_tfr);  {--> DMA}
			end;
		end;


		{ adjust transfer request type of OVERLAP }
		if usr_tfr = OVERLAP then
		begin
			if (dma_available) then
				usr_tfr := OVERLAP_DMA
			else
				usr_tfr := OVERLAP_INTR;
		end;


		{transfer accepted, mark buffer busy}
		stbsy;

		{do the various transfers}
		if usr_tfr in [serial_DMA, overlap_DMA] then
		begin
			act_tfr := DMA_tfr;
			dma_start(bcb);

			if usr_tfr = serial_DMA then
			begin
				repeat until active_isc = no_isc;
			end;
		end
		else if usr_tfr = serial_FHS then
		begin
			do_fhs_tfr(bcb);
		end
		else if usr_tfr in [overlap_FHS, overlap_INTR] then
		begin
			if usr_tfr = overlap_FHS then
				act_tfr := FHS_tfr
			else
				act_tfr := INTR_tfr;

			if direction = FROM_MEMORY then
				ovl_int_out_start
			else
				ovl_int_in_start;
		end
		else
			dvr_error(ioe_bad_tfr);
	end;
end;


end {pllel_drive};

$page$
{------------------------------------------------------------------------------}
{------------------  parallel driver main program.           ------------------}
{------------------------------------------------------------------------------}

import sysglobals, isr, loader, asm, iodeclarations, general_0, parallel_3,
       pllel_drive, pllel_util;

function pllel_init:boolean;
type
	p_drv_type = ^drv_table_type;
var
	card_found:boolean;
	io_isc:type_isc;
	io_lvl:io_byte;
	p_drivers:p_drv_type;
	p_hw:ptr_parallel_hw_type;
	p_misc:ptr_misc_block;
	i:integer;
	pch:ptr_char_type;
begin
	card_found := false;

	io_revid := io_revid + ' P3.2';

	{
	  set up the driver tables
	}
	newbytes(p_drivers, sizeof(drv_table_type));
	p_drivers^ := dummy_drivers;
	with p_drivers^ do begin
		iod_init  := pinit_hook;
		iod_isr   := pisr_hook;
		iod_rdb   := prdb_hook;
		iod_wtb   := pwtb_hook;
		iod_rdw   := prdw_hook;
		iod_wtw   := pwtw_hook;
		iod_rds   := prds_hook;
		iod_wtc   := pwtc_hook;
		iod_end   := pend_hook;
		iod_tfr   := ptfr_hook;
	end;


	{
	  look for parallel interfaces, and initialize.
	}
	for io_isc:=iominisc to iomaxisc do
	with isc_table[io_isc] do
	begin
		if (card_ptr <> nil) and
		   (card_type = pllel_card) then
		begin
			card_found := true;
			p_hw := io_tmp_ptr^.card_addr;
			p_misc := addr(io_tmp_ptr^.drv_misc[1]);


			{
			  set up O/S I/O hooks
			}
			io_drv_ptr := anyptr(p_drivers);

			{
			  set up ISR handler
			}
			io_lvl := p_hw^.intdma.int_lvl + 3;
			if io_tmp_ptr^.myisrib.intregaddr <> nil then  {isr already exits}
			begin
				{
				  unlink existing isr hook.
				}
				isrunlink(io_lvl, addr(io_tmp_ptr^.myisrib));
			end;

			permisrlink(
				io_drv_ptr^.iod_isr,                { isr handler   }
				ADDR(p_hw^.intdma, 1),              { card address  }
				hex('c0'),                          { intr. mask    }
				hex('c0'),                          { intr. value   }
				io_lvl,                             { level         }
				ADDR(io_tmp_ptr^.myisrib));         { isrib info    }


			{
			  initialize driver variables.
			}
			pch := addr(p_misc^);
			for i := 1 to sizeof(misc_block) do
			begin
				pch^ := #0;
				pch := addr(pch^,1);
			end;
			with p_misc^ do
			begin
				peripheral_type := NOT_PRESENT;
				peripheral_reset := NOT_PRESENT;
				options.w := 0;
				options_reset.w := 0;
				state.bl := INACTIVE_ERROR;
				d_isr_ie.w := 0;
				d_int_e := false;
				u_isr_ie.w := 0;
				u_isr_status.w := 0;
				last_read_nack := false;
			end;


			{
			  initialize driver & h/w.
			}
			with io_tmp_ptr^ do
			try
				i := timeout;
				timeout := 1;
				pinit_hook(io_tmp_ptr);
				timeout := i;
			recover
			begin
				timeout := i;
				if escapecode <> ioescapecode then
					escape(escapecode);
			end;
		end;
	end;
	pllel_init := card_found;
end;

begin {program pllel_start}
	if pllel_init then
		markuser;
end.
@


55.1
log
@Automatic bump of revision number for PWS version 3.25A
@
text
@@


54.1
log
@Automatic bump of revision number for PWS version 3.24
@
text
@@


53.1
log
@Automatic bump of revision number for PWS version 3.24B
@
text
@@


52.1
log
@Automatic bump of revision number for PWS version 3.24A
@
text
@@


51.1
log
@Automatic bump of revision number for PWS version 3.24d
@
text
@@


50.1
log
@Automatic bump of revision number for PWS version 3.23c
@
text
@@


49.1
log
@Automatic bump of revision number for PWS version 3.24b
@
text
@@


48.1
log
@Automatic bump of revision number for PWS version 3.24a
@
text
@@


47.1
log
@Automatic bump of revision number for PWS version 3.23
@
text
@@


46.2
log
@
Modifications for 1)  Showstopper defect in which IBM compatible printers
		      were not working with PRINTER TM.  This problem
		      due to timing in set_peripheral_type.  FIX:  a) do NOT
		      reset peripheral in init_hook and main program. b) do NOT
		      time peripheral_check in set_peripheral_type.  c)  modify
		      IOCONTROL PLLEL_REG_PERIPHERAL_RESET to call set_peripheral_type
		      when device is online and device type is not user specified.

		  2)  Timing glitch in reset_peripheral.  Redo all repeat until loops
		      that involve timers.
@
text
@@


46.1
log
@Automatic bump of revision number for PWS version 3.23
@
text
@d20 1
a20 1
program pllel_start{}(input, output){for debug};
d428 7
a434 8
		repeat until (not comm_per.c_busy_high) or
			     (use_timer and time_expired(time_rec));

		{
		  if busy never went low, then device is in a bad state.
		}
		if comm_per.c_busy_high then
			dvr_error(ioe_timeout);
d484 1
a484 1
			if (binand(ord(comm_per.bl), mask) <> PLLEL_PERIPHERAL_ONLINE) then
a516 1
 *                during check do not allow a timeout or hang.
d519 1
a535 1
	timevalue:integer;
d537 1
d542 2
a543 5
		try
			timevalue := timeout;
			timeout := 1;
			peripheral_check;
		recover
d545 11
a555 2
			timeout := timevalue;
			if escapecode = ioescapecode then
d557 11
a567 5
				{type has been set to not present.}
				type_found := true;
			end
			else
				escape(escapecode);
a568 1
		timeout := timevalue;
d743 7
a749 9
					until( (binand(ord(comm_per.bl),XMITTED_MASK) = LAST_BYTE_XMITTED) or
					       (use_timer and time_expired(time_rec))
					     );
					if binand(ord(comm_per.bl), XMITTED_MASK) <> LAST_BYTE_XMITTED then
						dvr_error(ioe_timeout);
					repeat until (not comm_per.c_busy_high) or
						     (use_timer and time_expired(time_rec));
					if comm_per.c_busy_high then
						dvr_error(ioe_timeout);
d836 4
a839 4
				repeat until (not comm_per.c_fifofull) or
					     (use_timer and time_expired(time_rec));
				if comm_per.c_fifofull then
					dvr_error(ioe_timeout);
d855 4
a858 5
			repeat until( (binand(ord(comm_per.bl), XMITTED_MASK) = LAST_BYTE_XMITTED) or
				      (use_timer and time_expired(time_rec))
				    );
			if binand(ord(comm_per.bl), XMITTED_MASK) <> LAST_BYTE_XMITTED then
				dvr_error(ioe_timeout);
a1595 5

			{
			  attempt to reset any attached peripheral.
			}
			reset_peripheral;
d1880 2
d1930 37
a1966 1
					reset_peripheral;
a2224 1
			  reset the attached peripheral.
a2230 1
				pwtc_hook(io_tmp_ptr, PLLEL_REG_PERIPHERAL_RESET, 0);
@


45.1
log
@Automatic bump of revision number for PWS version 3.23C
@
text
@@


44.10
log
@
For SCAN JET PLUS, after a reset and check for peripheral type
the device would not allow a transfer immediately following.
Solution is to delay for a millisecond following the nack_low_trans
to allow everything to settle down.
@
text
@@


44.9
log
@
The OVERLAP method of transfer was not supported.  Added support.

The OVERLAP_FHS transfer was doing OVERLAP_INTR for entire transfer.
Problem was interrupt was not being turned off after first transfer.
@
text
@d8 1
d591 1
a591 1
					repeat until int_state.bl <> #0;
d594 1
@


44.8
log
@Correct documentation and code inconsistency with
PLLEL_REG_CARDID -> PLLEL_REG_CARD_ID
@
text
@d1703 4
d2023 11
@


44.7
log
@
Driver was throwing away error condition by unconditionally
resetting the peripheral type to NOT_PRESENT when
an error occurs in peripheral_check.  Only do this if
the device type is not HP_BIDIRECTIONAL, or if is
HP_BIDIRECTIONAL then make sure error state is not
select and paper error.  In this case, DO NOT RESET
peripheral type.
@
text
@d1783 1
a1783 1
			PLLEL_REG_CARDID:
@


44.6
log
@
When resetting the wr_nrd_low option, the peripheral type was
not going back to bidirectional - at option reset time, set
peripheraltype to not present, this will force driver to
reevaluate peripheral type on next xfer command.
@
text
@d487 5
a491 1
					peripheral_type := NOT_PRESENT;
@


44.5
log
@
Fix for problem of doing a transfer_end after peripheral type not reset.
Problem is set_peripheral_type does not: 1) reset_peripheral before
attempting to determine peripheral_type (this is done AFTER it has
been determined a device is attached and online).  2) after determing
peripheral_type, this routine must set wr/nrd back to high and swallow
the nack.
@
text
@d1922 5
@


44.4
log
@
1) When attempting to talk input to a device that has just
   escaped (driver state is now error), and if last attempt
   to xfer was write, then driver doesn't know to wait
   for fifo to empty before setting bus to in.
   fix was to look at hardware state in set_bus_in.

2) when resetting driver and nselect_in is toggled this
   confuses an attached scan jet.  driver initialization
   must reset attached peripheral.
@
text
@d568 1
d581 12
@


44.3
log
@
fix defect where when using nack option and then turn it off, 
hardware is not updated correctly.  fixed by setting state
to INACTIVE_ERROR which will force h/w update with the next
xfer request.

moved write_verify code from inside fhs_out main loop to
after loop has completed.  Only need to write_verify after
the write has completed, not with each byte.
@
text
@d188 2
d701 3
a703 1
				  wait for the fifo to empty out.
d705 1
a705 1
				if (state.write) and (not comm_per.c_fifoempty) then
a706 1
					peripheral_check;
d713 7
a719 1
					repeat until (comm_per.c_fifoempty) or
d721 1
a721 1
					if not comm_per.c_fifoempty then
a768 3
const
	XMITTED_MASK = hex('60');       {for write_verify}
	BYTE_XMITTED = hex('40');       {for write_verify}
a770 1
	write_verify:boolean;
a786 1
		write_verify := options.write_verify;
d818 1
a818 1
		if write_verify then
d828 1
a828 1
			repeat until( (binand(ord(comm_per.bl), XMITTED_MASK) = BYTE_XMITTED) or
d831 1
a831 1
			if binand(ord(comm_per.bl), XMITTED_MASK) <> BYTE_XMITTED then
d1571 4
@


44.2
log
@Fix for failure on part 5 of LJSTART test.  While attempting
to read from an output only device, the system hung even
though a timeout was present.  Problem was call to wait_nack
which has a forever loop in it.  rewrote fhs_in handling
to not call wait_nack.
@
text
@a810 18
			if write_verify then
			begin

				{
				  wait for fifo empty, nStrobe not asserted and Busy
				  not asserted.
				}
				if use_timer then
				begin
					time_rec.time := timeout;
					start_timer(time_rec);
				end;
				repeat until( (binand(ord(comm_per.bl), XMITTED_MASK) = BYTE_XMITTED) or
					      (use_timer and time_expired(time_rec))
					    );
				if binand(ord(comm_per.bl), XMITTED_MASK) <> BYTE_XMITTED then
					dvr_error(ioe_timeout);
			end;
d814 20
a833 5

		{
		  make sure device is still communicating.
		}
		if not write_verify then
d1904 5
a1908 3
					if options.use_nack then
						{force bus to be set on next write}
						state.bl := INACTIVE_ERROR;
@


44.1
log
@Automatic bump of revision number for PWS version 3.23B
@
text
@a941 6
					if exit_on_nack then
					begin
						wait_nack;
						if last_read_nack then
							goto 1;
					end;
d943 1
d946 9
a954 1
					dvr_error(ioe_timeout);
@


43.4
log
@Made the nomenclature between PARALLEL_3 register types and PLLEL_UTIL
register types consistent.  This caused alot of problems with the
write control and read status routines.  This fixes the test failure
with OPTION1.

Updated set_peripheral_type to set a very small timeout before calling
peripheral check.  This fixes test failure with OPTION2.
@
text
@@


43.3
log
@Added changes required as per Code Review with Jeff Hendershot 
on 3/27 and 3/28.
@
text
@d93 1
a93 1
				rw_whole: (b:           char);
d101 1
a101 1
				rw_whole: (b:           char);
d120 1
a120 1
				rw_whole: (b:           char);
d135 1
a135 1
				rw_whole: (b:           char);
d147 1
a147 1
				rw_whole: (b:           char);
d162 1
a162 1
				rw_whole: (b:           char);
d347 2
a348 2
		int_state.b := #0; {reset latches}
		int_state.b := chr(binior(d_isr_ie.bl, u_isr_ie.bl));
d470 1
a470 1
		if (binand(ord(comm_per.b), mask) <> PLLEL_PERIPHERAL_ONLINE) then
d479 1
a479 1
			repeat until (binand(ord(comm_per.b), mask) =
d482 1
a482 1
			if (binand(ord(comm_per.b), mask) <> PLLEL_PERIPHERAL_ONLINE) then
d511 1
d530 1
d533 1
a533 1
	with my_hw_ptr^, my_misc_ptr^ do
d537 2
d541 2
d550 2
d823 1
a823 1
				repeat until( (binand(ord(comm_per.b), XMITTED_MASK) = BYTE_XMITTED) or
d826 1
a826 1
				if binand(ord(comm_per.b), XMITTED_MASK) <> BYTE_XMITTED then
d873 1
a873 1
			repeat until (not comm_per.c_fifoempty) or (int_state.b <> #0);
d1245 1
a1245 1
		loc_intdma.b := intdma.b;
d1257 1
a1257 1
		intdma.b := loc_intdma.b; {this kicks off the DMA}
d1543 1
a1543 1
			hostline.b := #0;               {turn off all hostlines}
d1616 1
a1616 1
		save_int_state.b := int_state.b;
d1619 1
a1619 1
		d_isr_ir.bl := binand(d_isr_ie.bl, ord(save_int_state.b));
d1642 1
a1642 1
					if binand(ord(save_int_state.b), hex('07')) <> 0 then
d1692 1
a1692 1
		u_isr_status.bl := binand(u_isr_ie.bl, ord(save_int_state.b));
d1764 1
a1764 1
					status.bl := ord(intdma.b);
d1769 1
a1769 1
					status.bl   := ord(comm_per.b);
d1774 1
a1774 1
					status.bl := binlsr(ord(comm_per.b),3);
d1779 1
a1779 1
					status.bl := ord(hostline.b);
d1784 1
a1784 1
					status.bl := binlsr(ord(intdma.b),2);
d1812 1
a1812 1
					status.bl := options.b;
d1816 1
a1816 1
					status.bl := options_reset.b;
d1860 1
a1860 1
					hostline.b := chr(control.bl);
@


43.2
log
@This revision contains major changes to the PARALLEL driver in preparation
for code review and QA.

  - documentation of routines and architecture added through out.
  - some routines were reordered to make more sense.
  - reset_peripheral: rewritten to match hardware spec.
  - peripheral_check: dependency on bus state removed.
  - set_peripheral_type: rewritten to match hardware spec.
  - set_bus_in: added parameter to determine FIFO size.  32 bytes fifo
    now allowed for DMA and FHS transfers.
  - fhs_out: rewritten to allow 32 byte FIFO and for performance.
  - wait_nack: new routine added for commonality between FHS and DMA.
  - fhs_in: rewritten to allow 32 byte FIFO and for performance.  Nack
    handling also rearchitected.
  - init_hook:  no longer attempt to determine peripheral_type on POR.
@
text
@d50 4
d274 1
d283 3
a285 1
	end;
d482 1
a482 1
			if (binand(ord(comm_per.b), mask) <> hex('02')) then
d753 1
a753 1
	XMITTED_MASK = hex('70');       {for write_verify}
d828 2
a829 1
		peripheral_check;
d841 2
d844 2
a845 2
 *              want to see if an nack will occur with this
 *              byte.
d927 1
d1135 1
d1138 2
a1139 1
			wait_nack;
d1211 2
d1222 2
d1317 1
d1430 1
d1615 1
a1615 1
			 active overlap transfers}
@


43.1
log
@Automatic bump of revision number for PWS version 3.23aA
@
text
@d33 1
d37 5
a41 1
module pllel_util;
d43 20
d182 2
d209 1
a209 2
	procedure       set_bus_in;
	procedure       do_fhs_tfr(ptr_buf:ptr_buf_info_type);
d212 1
d216 1
a216 1
	procedure       dma_start(direction:dir_of_tfr);
d224 17
d250 18
d281 18
a299 2
var
	ptr_buf_info:ptr_buf_info_type;
d314 22
d349 31
d386 5
d392 1
a392 2
		  reset peripheral, wait for busy to be released, indicating peripheral
		  has completed soft por.
d394 8
a401 1
		set_bus_out;
d403 4
a406 2
		hostline.nselectin_low := true;
		delay(delay_ms);
d408 6
a413 1
		if comm_per.c_busy_high then
d415 2
a416 10
			use_timer := timeout <> 0;
			if use_timer then
			begin
				time_rec.time := timeout;
				start_timer(time_rec);
			end;
			repeat until (not comm_per.c_busy_high) or
				     (use_timer and time_expired(time_rec));
			if comm_per.c_busy_high then
				dvr_error(ioe_timeout);
d418 8
d429 23
a456 5
	{
	  check that device is online and that here are no errors.
	  if after waiting, the device is in an error state, set the device type
	  to NOT_PRESENT and escape with a timeout error.
	}
a458 4
		if intdma.io then {input mode}
				mask := hex('7') {select, nerror, and perror}
		else {output mode}
			mask := hex('17'); {select, nerror, perror, and busy}
d460 4
a463 2
			mask := mask - 1;
		if (binand(ord(comm_per.b), mask) <> hex('02')) then
d472 2
a473 1
			repeat until (binand(ord(comm_per.b), mask) = hex('02')) or
d485 35
d532 1
a532 1
				{device type has been set to not present.}
d539 3
a541 1
		{know that a device is there, what type?}
d548 3
a550 2
				{check for bidirectional device}
				{set Wr/nRd low and wait for busy to go high}
d552 4
a555 2
				hostline.wr_nrd_high := false;
				time_rec.time := 100 * delay_ms;
a558 1
				{if busy has gone high, then bidirectional}
a563 2
				{bus is no longer in a known state, so set bus to output}
				set_bus_out;
d566 1
d570 20
d600 21
d647 24
a670 1
procedure set_bus_in;
d688 1
d702 3
a704 2
				  an inbound fifo size of 1 is required to accurately determine
				  when to set the last_read_nack boolean flag.
d706 1
a706 1
				intdma.iomod := true;
a720 35
procedure do_fhs_tfr(ptr_buf:ptr_buf_info_type);
var
	my_count:       integer;
	lastchar:       char;
begin
	with ptr_buf^, my_misc_ptr^ do
	begin
		act_tfr := FHS_tfr;
		my_count := term_count;
		if direction = FROM_MEMORY then
		begin
			fhs_out(buf_empty, term_count);
			buf_empty := addr(ptr_char_type(buf_empty)^, my_count - term_count);
		end
		else {direction is TO_MEMORY}
		begin
			if term_char = -1 then {not a transfer_until}
			begin
				fhs_in(buf_fill, term_count, end_mode);
				buf_fill := addr(ptr_char_type(buf_fill)^,
						 my_count - term_count);
			end
			else {transfer_until}
			begin
				repeat
					my_count := 1;
					fhs_in(buf_fill, my_count, false);
					lastchar := ptr_char_type(buf_fill)^;
					buf_fill := addr(ptr_char_type(buf_fill)^, 1);
					term_count := term_count - 1;
				until (term_count = 0) or
				      (lastchar = chr(term_char)) or
				      ((last_read_nack) and (end_mode));
			end;
		end;
d722 22
a743 5
		{serial_FHS transfer has completed normally, clean up.}
		stclr;
	end;
end;

d745 3
d750 1
d754 6
a759 1
		use_timer := timeout <> 0;
d762 10
a773 1
			peripheral_check;
d776 9
d796 1
a796 1
			if options.write_verify then
d798 5
d808 4
a811 4
				repeat until ( (not comm_per.c_nstrobe_low) and
					       (not comm_per.c_busy_high) ) or
					     (use_timer and time_expired(time_rec));
				if (comm_per.c_nstrobe_low) or (comm_per.c_busy_high) then
d817 6
d827 61
d891 1
a891 1
	i:integer;
d895 7
a901 1
		set_bus_in;
d903 5
d909 5
a913 1
		while (count > 0) and not (exit_on_nack and last_read_nack) do
a914 1
			peripheral_check;
d917 1
a917 1
				if timeout <> 0 then
d921 3
a923 4
					repeat until (not comm_per.c_fifoempty) or
						     (int_state.nack_low_trans) or
						     (time_expired(time_rec));
					if int_state.nack_low_trans then
d925 3
a927 3
						last_read_nack := true;
						set_int; {clear latches, reset interrupt}
						goto 1;
d929 4
a932 14
					if comm_per.c_fifoempty then
						dvr_error(ioe_timeout);
				end
				else
				begin
					repeat until (not comm_per.c_fifoempty) or
						     (int_state.nack_low_trans);
					if int_state.nack_low_trans then
					begin
						last_read_nack := true;
						set_int; {clear latches, reset interrupt}
						goto 1;
					end;
				end;
a934 19
			if exit_on_nack then  {wait for an nAck transition}
			begin
				if not int_state.nack_low_trans then
				begin
					{
					  to be safe, need to wait 10us.
					  375 nikki read is about 1us.
					}
					i := 20; {overkill for future performance improvements}
					repeat
						i := i - 1;
					until (int_state.nack_low_trans) or (i = 0);
				end;
				if int_state.nack_low_trans then
				begin
					last_read_nack := true;
					set_int; {clear latches, reset interrupt}
				end;
			end;
a936 1
			1:
d938 11
d950 2
d953 65
d1021 19
d1048 19
d1076 21
d1126 1
a1126 3
			repeat until (not comm_per.c_fifoempty) or (int_state.b <> #0);
			if int_state.nack_low_trans then
				last_read_nack := true;
d1136 24
a1159 1
procedure dma_start(direction:dir_of_tfr);
d1164 1
a1164 1
	with my_misc_ptr^, my_hw_ptr^ do
d1166 4
d1172 10
d1190 1
a1190 1
			set_bus_in;
d1193 4
a1196 1
			d_isr_ie.bl := hex('0f');
d1198 4
a1201 1
		peripheral_check;
d1203 5
d1210 2
d1213 3
d1237 21
d1262 1
a1263 1
		peripheral_check;
d1271 24
a1298 1
		set_bus_in;
d1300 1
d1308 25
d1344 1
a1344 1
			if (term_count = 0) then
d1368 25
d1403 1
a1403 1
			fhs_in(buf_fill, my_count, false);
d1436 1
d1440 1
a1440 1
module pllel_drive;
d1442 12
d1477 1
a1539 5
			{
			  optionally set peripheral type
			}
			if peripheral_type < USER_SPEC_NO_DEVICE then
				set_peripheral_type;
d1968 1
a1968 1
			dma_start(direction);
@


42.2
log
@
took out all escapes for ioe_eod_seen for an unexpected nack.
cleaned up fhs_in a little bit to accomodate this.

init_hook was cleaned up.  init_hook should never escape, nor should
it allow a forever loop (timeout = 0).  the order in which the hardware
is reset is a little suspect to me, so I rearranged it a little.
@
text
@@


42.1
log
@Automatic bump of revision number for PWS version 3.23e
@
text
@d19 1
a19 1
program pllel_start;
d185 1
a185 1
	procedure       fhs_in(buf:ptr_char_type; var count:integer; wait:boolean);
d537 2
a538 1
procedure fhs_in(buf:ptr_char_type; var count:integer; wait:boolean);
d547 1
a547 1
		while (count > 0) and not (wait and last_read_nack) do
d562 2
a563 1
						dvr_error(ioe_eod_seen);
d575 2
a576 1
						dvr_error(ioe_eod_seen);
d581 1
a581 1
			if wait then  {wait for an nAck transition}
d602 1
a843 1
	escape_occurred:boolean;
d846 10
a855 1
	set_my_ptrs(temp);
d857 1
a857 5
	with my_hw_ptr^, my_tmp_ptr^, my_misc_ptr^ do
	begin
		escape_occurred := false;
		timevalue := timeout; {don't want to wait during powerup sequence}
		timeout := 1; {don't want to wait during powerup sequence}
d859 3
a861 1
		try
d884 1
d886 2
a889 1
			intdma.int_e := false;
a894 2
			sysreg.softreset := #0;
			delay(delay_ms);
d914 3
a916 2
		recover
			escape_occurred := true;
d918 7
a924 7
		{
		  restore interrupts
		}
		timeout := timevalue;
		if escape_occurred then
			escape(escapecode);
	end;
@


41.3
log
@Fixed a h/w problem with back to back DMA by working around
software.  Also, fixed problem with losing Nack signal when
DMA ends on last byte of inbound transfer.
@
text
@@


41.2
log
@During the init_hook, reset all of the host lines instead of just the host
select line.  This insures that the por condition of the ninit line being
asserted is reset.
@
text
@d645 1
d647 8
a655 2
		if (d_isr_ie.nack_low_trans) and (int_state.nack_low_trans) then
			last_read_nack := true;
d666 1
d689 7
d699 1
a699 1
			intdma.dma_e0 := true; {this kicks off the DMA}
d704 1
a704 1
			intdma.dma_e1 := true; {this kicks off the DMA}
d706 1
d947 5
a951 1
		{save current h/w interrupts, reset all interrupt conditions}
a952 2
		int_state.b := #0;
		intdma.int_e := false;
@


41.1
log
@Automatic bump of revision number for PWS version 3.23d
@
text
@d858 1
a858 1
			hostline.nselectin_low := false;
@


1.4
log
@
pws2rcs automatic delta on Thu Dec 21 14:54:59 MST 1989
@
text
@@


1.3
log
@Made updates during review of ERS and code.  Updated code to match functionality
defined in ERS.
@
text
@d28 2
a29 1
	'IOLIB:PLLEL_ASM'$
@


1.2
log
@Updated nomenclature to reflect change from program isr terminology to the
more appropriate user isr terminology.
@
text
@d21 1
a21 1
{{
d25 1
a25 1
{}
d258 1
a259 1
		hostline.wr_nrd_high := true;
d346 1
d358 2
a359 2
				{bus is no longer in a known state, so set driver state to inactive_error}
				state.bl := INACTIVE_ERROR;
d1008 1
d1014 4
a1017 1
			u_isr_ie.w := 0;
d1020 3
d1198 6
a1203 1
							peripheral_type := control.bl
@


1.1
log
@Initial revision
@
text
@d23 1
a23 1
	'PARALLEL_3.CODE',
d51 2
a52 2
			p_isr_ie:               prog_isr_status_type;
			p_isr_status:           prog_isr_status_type;
d240 2
a241 2
		int_state.b := chr(binior(d_isr_ie.bl, p_isr_ie.bl));
		if p_isr_ie.bl <> 0 then
d863 1
a863 1
			p_isr_ie.w := 0;
d873 1
a873 1
			p_isr_status.w := 0;
d902 1
a902 1
				1:(pproc:parallel_prog_isr_type);
d913 1
d925 1
d979 1
d985 2
d988 1
d1006 5
a1010 3
		{handle program isr interrupts}
		p_isr_status.bl := binand(p_isr_ie.bl, ord(save_int_state.b));
		if (p_isr_status.bl <> 0) then
d1012 1
a1012 1
			p_isr_ie.w := 0;
d1134 1
a1134 1
						status.bl := PROG_ISR_HOOK_INACTIVE
d1136 1
a1136 1
						status.bl := PROG_ISR_HOOK_ACTIVE;
d1138 1
a1138 1
			PLLEL_REG_PROG_ISR_ENABLE:
d1140 1
a1140 1
					status.prog_isr_status := p_isr_ie;
d1142 1
a1142 1
			PLLEL_REG_PROG_ISR_STATUS:
d1144 1
a1144 1
					status.prog_isr_status := p_isr_status;
d1220 2
a1221 2
					p_isr_ie.w := 0;
					p_isr_status.w := 0;
d1226 1
a1226 1
			PLLEL_REG_PROG_ISR_ENABLE:
d1231 1
a1231 1
					p_isr_ie.bl := control.bl;
d1432 2
a1433 2
				p_isr_ie.w := 0;
				p_isr_status.w := 0;
@
