COMMENT *

			     CLUBware  (tm)

	  PRSLASHO - Enhancement to the Basic Compiler Print statement
				(for routines compiled with /O option)

		   Copyright 1984 Rayhawk Automation N.W. Inc
				  P.O. Box 1427
				  Beaverton, Oregon   97075
									      *

SCRDSEG   SEGMENT

	  ASSUME  CS:SCRDSEG,DS:NOTHING,ES:NOTHING,SS:STACK

ORIG_INT10	  LABEL  WORD	       ; original int 10
	  DW	  0
	  DW	  0

;______________________________________________________________________________

;   Swap the code used by BASIC to print strings.  Swap must be made
;    after BASIC has been loaded and initialized its environment.  When the
;    screen is cleared INTSWAP intercepts the BASIC print statement.

ORIG_SUB	  LABEL  WORD	       ; original subroutine for printing
	  DW	  0		       ;  strings
BASIC_SEGMENT	  LABEL  WORD
	  DW	  0

FLAG_ADDRESS	  LABEL  WORD	       ; address of flag used to determine
	  DW	  0		       ;  target of i/o for basic


SWAPFLAG	  LABEL  BYTE
		  DB  00000000b        ; flag controlling swapping of subs
TRAP_CHAR	  EQU 00000001b        ; trap next character to be printed
SECOND_TIME	  EQU 00000010b        ; second character printed
UNDER_DEBUG	  EQU 00000100b        ; executing under debug

SCREEN_PAGE	  LABEL  WORD
	  DW	  0		       ; address of current page

SCREEN_POSITION   LABEL  WORD
	  DW	  0		       ; address of screen position

BYTES_LINE	  LABEL  WORD
	  DW	  0		       ; address of number of bytes per line

CHAR_ATTRIBUTE	  LABEL  WORD
	  DW	  0		       ; address of attribute

CALLERS_SEG	  EQU  WORD PTR [BP+4]	; location of callers segment on stack

STRING_CALLER	  EQU  WORD PTR [BP+16] ; location of callers offset on stack

NUMBER_CALLER	  EQU  WORD PTR [BP+26] ; location of callers offset on stack


INTSWAP   PROC	  FAR

	  CMP	  AX,0600h	       ; clear screen request?
	  JNE	  NOT_SCREEN_CLEAR
	  MOV	  SWAPFLAG,TRAP_CHAR   ; try trapping the next char printed
	  JMP	  DWORD PTR ORIG_INT10 ; pass call to BIOS


NOT_SCREEN_CLEAR:

	  TEST	  SWAPFLAG,TRAP_CHAR   ; supposed to trap next char?
	  JZ	  NOT_TRAPPED
	  CMP	  AH,09 	       ; request to print a char?
	  JNE	  NOT_TRAPPED
	  TEST	  SWAPFLAG,SECOND_TIME ; is this the second char printed?
	  JNZ	  DO_SWAP
	  OR	  SWAPFLAG,SECOND_TIME ; first character has been seen

NOT_TRAPPED:
	  JMP	  DWORD PTR ORIG_INT10 ; pass call to BIOS

DO_SWAP:
	  TEST	  SWAPFLAG,UNDER_DEBUG
	  JNZ	  NOT_TRAPPED
	  OR	  SWAPFLAG,UNDER_DEBUG
	  PUSH	  BP		       ; address the stack
	  MOV	  BP,SP
	  PUSH	  DS
	  PUSH	  SI
	  PUSH	  AX


	  MOV	  AX,CALLERS_SEG       ; address basic's code space
	  MOV	  DS,AX

	  MOV	  SI,NUMBER_CALLER     ; load offset of intermediate caller
	  SUB	  SI,8		       ; back up to start of LOOP

;    do we recognize this as a number being printed?

	  CMP	  WORD PTR DS:[SI],0AACh	     ; code to print number?
	  JNE	  MAYBE_STRING			     ; jump if so

	  CMP	  BYTE PTR DS:[SI+8],0EBh	     ; just to make sure
	  JE	  GET_SUB_ADDRESS
	  JMP	  ALREADY_SAVED


;     see if perhaps this is a string being printed

MAYBE_STRING:
	  CMP	  WORD PTR DS:[SI+4],0E8ACh	     ; code to print string?
	  JNE	  ALREADY_SAVED 		     ; jump if not

	  CMP	  BYTE PTR DS:[SI+8],0E2h	     ; just to make sure
	  JNE	  ALREADY_SAVED


GET_SUB_ADDRESS:
	  ADD	  SI,WORD PTR DS:[SI+6] 	     ; load offset of sub
	  ADD	  SI,8

;   have we already taken over this subroutine once before?

	  CMP	  WORD PTR DS:[SI+1],OFFSET PRINTER
	  JE	  ALREADY_SAVED

	  CMP	  BYTE PTR DS:[SI],9Ah		     ; opcode is far call?
	  JE	  SECOND_COPY


;   track down the data we need to perform screen output

	  PUSH	  SI
	  PUSH	  AX

	  MOV	  AX,WORD PTR DS:[SI+3] 	     ; load offset of flag
	  MOV	  FLAG_ADDRESS,AX		     ; save it for later

	  ADD	  SI,WORD PTR DS:[SI+11]  ; code where screen position is used
	  ADD	  SI,60h
	  ADD	  SI,WORD PTR DS:[SI]	  ; code where screen position is used
	  ADD	  SI,8
	  MOV	  AX,WORD PTR DS:[SI]
	  MOV	  SCREEN_POSITION,AX

	  ADD	  SI,23h	       ; code where bytes/line is used
	  MOV	  AX,WORD PTR DS:[SI]
	  MOV	  BYTES_LINE,AX

	  ADD	  SI,5Eh	       ; code where attribute is used
	  MOV	  AX,WORD PTR DS:[SI]
	  MOV	  CHAR_ATTRIBUTE,AX

	  SUB	  SI,5		       ; back up to where new position is called
	  ADD	  SI,WORD PTR DS:[SI]  ; add offset of subroutine

	  ADD	  SI,11 	       ; forward to where next sub is called
	  ADD	  SI,WORD PTR DS:[SI]  ; add offset of subroutine

	  ADD	  SI,5		       ; code where page is used
	  MOV	  AX,WORD PTR DS:[SI]
	  MOV	  SCREEN_PAGE,AX


	  POP	  AX
	  POP	  SI


;   replace the original code with a call to PRINTER to intercept prints

SECOND_COPY:
	  MOV	  BYTE PTR DS:[SI],9Ah		      ; opcode is far call
	  MOV	  WORD PTR DS:[SI+1],OFFSET PRINTER   ; target for call
	  MOV	  WORD PTR DS:[SI+3],SEG    PRINTER
	  MOV	  BASIC_SEGMENT,AX		      ; save basic's code seg
	  ADD	  SI,5				      ; walk past call
	  MOV	  ORIG_SUB,SI			      ; save original sub addr

ALREADY_SAVED:
	  MOV	  SWAPFLAG,0			      ; clear the swap flag

	  POP	  AX
	  POP	  SI
	  POP	  DS
	  POP	  BP
	  JMP	  DWORD PTR ORIG_INT10 ; pass clear screen to BIOS now



INTSWAP   ENDP

;______________________________________________________________________________

COMMENT * PRINTER prints a character string on the screen starting
		  at the current cursor position.  After the string is
		  written to the screen the cursor position is updated
		  to just after the string.

	  To use this routine, a clear screen must occur
		  to swap in the PRINTER routine for basic.


	  The PRSLASHO module will take over the INT 10h and make
		  this module permanently resident.  The user from inside
		  his BASIC program will clear the screen with a CLS
		  which will perform the vector swap.

	  Algorithm:

	  On entry
		  DS:[SI] points to string to write
		  CX	  contains count of characters to write
		  BX	  may contain count of blanks to write
		  SCREEN_PAGE	  contains the page to which we write
		  CHAR_ATTRIBUTE  contains the attribute for the text
		  SCREEN_POSITION contains the current screen location and must
			       be updated after print is complete


									      *
;______________________________________________________________________________

;  Local data addressable through CS register


CHAR_COUNT	  LABEL  WORD
	  DW	  0


LINE_START	  LABEL  WORD
	  DW	  0		       ;  0
	  DW	  160		       ;  1
	  DW	  320		       ;  2
	  DW	  480		       ;  3
	  DW	  640		       ;  4
	  DW	  800		       ;  5
	  DW	  960		       ;  6
	  DW	  1120		       ;  7
	  DW	  1280		       ;  8
	  DW	  1440		       ;  9

	  DW	  1600		       ;  10
	  DW	  1760		       ;  11
	  DW	  1920		       ;  12
	  DW	  2080		       ;  13
	  DW	  2240		       ;  14
	  DW	  2400		       ;  15
	  DW	  2560		       ;  16
	  DW	  2720		       ;  17
	  DW	  2880		       ;  18
	  DW	  3040		       ;  19
	  DW	  3200		       ;  20
	  DW	  3360		       ;  21
	  DW	  3520		       ;  22
	  DW	  3680		       ;  23
	  DW	  3840		       ;  24

PAGE_START	  LABEL  WORD
	  DW	  0
	  DW	  4000
	  DW	  8000
	  DW	  12000

STARTING_PAGE	  LABEL  WORD
	  DW	  0

END_OF_PAGE	  LABEL  WORD
	  DW	  0

STARTING_LINE	  LABEL  WORD
	  DW	  0

RESIDUAL	  LABEL  WORD
	  DW	  0


LOCAL_FLAG	  LABEL  BYTE
		  DB   00000000b
JUST_BLANKS	  EQU  00000001b       ; basic wants just blanks displayed
ZERO_TAGGED	  EQU  00000010b       ; string length unknown, tagged by 0


;______________________________________________________________________________

COMMENT *

   New PRINT service routine for BASIC

     Calls to routine XXXX:0C5B are rerouted to this piece of code
     These calls come from several places within the BASIC rutime library
     We service calls from four locations:  - calls to display a string
					    - calls to expand tabs
					    - calls to blank pad fields
					    - calls to display a number
     Calls from anywhere else are serviced by the original basic code.
     The four labels corresponding to the four types of calls we service
     are  STRING_IO
	  REPEAT_BLANKS
	  NUMBER_STRING
	  PAD_FIELD
     The code labeled BASIC_IO passes the interrupt back to the basic code
     for service.
									      *


PRINTER   PROC	  FAR

	  PUSH	  BP
	  PUSH	  ES
	  MOV	  BP,BASIC_SEGMENT
	  MOV	  ES,BP

	  MOV	  LOCAL_FLAG,0	       ; clear the flag for string i/o


;    check context for simple text to screen or possibly something
;      more involved that basic should handle

	  MOV	  BP,FLAG_ADDRESS
	  CMP	  WORD PTR DS:[BP],0
	  JNE	  BASIC_IO

	  MOV	  BP,BYTES_LINE
	  CMP	  BYTE PTR DS:[BP],50h ; 80 bytes per line?
	  JNE	  BASIC_IO

	  MOV	  BP,SP 	       ; address the stack

	  MOV	  BP,WORD PTR [BP+8]	   ; load offset of caller

	  CMP	  BYTE PTR ES:[BP],0EBh    ; number to print?
	  JE	  NUMBER_STRING

	  CMP	  BYTE PTR ES:[BP],04Bh    ; tab to expand?
	  JE	  REPEAT_BLANKS

	  CMP	  BYTE PTR ES:[BP],0E2h    ; is this a loop instruction?
	  JNE	  BASIC_IO
	  CMP	  BYTE PTR ES:[BP-4],0ACh  ; is this LODSB in loop?
	  JE	  STRING_IO
	  CMP	  WORD PTR ES:[BP-5],20B0h ; is this MOV AL in loop?
	  JE	  PAD_FIELD

;    allow original service routine to perform I/O

BASIC_IO:
	  MOV	  BP,SP
	  PUSH	  AX
	  MOV	  AX,WORD PTR [BP+8]
	  POP	  AX

	  POP	  ES
	  POP	  BP
	  ADD	  SP,4		       ; clear our far call from stack
	  PUSH	  SI		       ; original first two statements
	  MOV	  SI,FLAG_ADDRESS
	  MOV	  SI,WORD PTR DS:[SI]  ;   of the basic subroutine
	  JMP	  DWORD PTR ORIG_SUB   ; pass control to original routine



; ----------


;    send a number string to screen
;		  string length is not known but end is tagged by a zero

NUMBER_STRING:

	  PUSH	  BX		       ; save registers used
	  PUSH	  DX
	  PUSH	  DI


;	  ...	  1) store character count and mark the flag for type of i/o

	  DEC	  SI		       ; backup to start of string
	  MOV	  CHAR_COUNT,80        ; always less than 80 characters
	  OR	  LOCAL_FLAG,ZERO_TAGGED
	  MOV	  RESIDUAL,CX	       ; restore original cx contents when done
	  JMP	  SETUP_START


; ----------


;    repeated blanks sent to screen to expand a tab

REPEAT_BLANKS:

	  PUSH	  BX		       ; save registers used
	  PUSH	  DX
	  PUSH	  DI


;	  ...	  1) store character count and mark the flag for type of i/o

	  MOV	  CHAR_COUNT,BX        ; save character count
	  OR	  LOCAL_FLAG,JUST_BLANKS
	  MOV	  RESIDUAL,1	       ; leave one in cx register when done
	  JMP	  SETUP_START


; ----------


;    repeated blanks sent to screen to fill a field area

PAD_FIELD:

	  PUSH	  BX		       ; save registers used
	  PUSH	  DX
	  PUSH	  DI


;	  ...	  1) store character count and mark the flag for type of i/o

	  MOV	  CHAR_COUNT,CX        ; save character count
	  OR	  LOCAL_FLAG,JUST_BLANKS
	  MOV	  RESIDUAL,1	       ; leave one in cx register when done
	  JMP	  SETUP_START


; ----------


;    write the string for basic

STRING_IO:

;	  CMP	  BYTE PTR DS:[06CAh],50h  ; 80 bytes per line?
;		  WAS ORIGINALLY 6D0h
;	  JNE	  BASIC_IO

	  PUSH	  BX		       ; save registers used,
	  PUSH	  DX
	  PUSH	  DI



;	  ...	  1) backup to start of string and store character count

	  DEC	  SI		       ; backup to start of string
	  MOV	  CHAR_COUNT,CX        ; save character count
	  MOV	  RESIDUAL,1	       ; leave one in cx register when done


;	  ...	  2) load current page

SETUP_START:
	  MOV	  BP,SCREEN_PAGE
	  MOV	  BL,BYTE PTR DS:[BP]  ; load current page
	  SUB	  BH,BH
	  SHL	  BX,1		       ; convert to a table offset
	  MOV	  DI,PAGE_START[BX]    ; load start of page
	  MOV	  STARTING_PAGE,DI     ; save the page
	  MOV	  END_OF_PAGE,DI       ; save ending page pointer
	  ADD	  END_OF_PAGE,4000



;	  ...	  3) load current position on page

	  MOV	  BP,SCREEN_POSITION
	  MOV	  DX,WORD PTR DS:[BP]  ; load current position
	  DEC	  DH		       ; basic counts from 1 instead of 0
	  DEC	  DL		       ; basic counts from 1 instead of 0
	  MOV	  BL,DL 	       ; load row number
	  SHL	  BX,1		       ; two bytes per table entry
	  ADD	  DI,LINE_START[BX]    ; add in start of line
	  MOV	  STARTING_LINE,DI     ; store this for later


	  MOV	  DL,DH 	       ; bring down column number
	  SUB	  DH,DH
	  ADD	  DI,DX 	       ; add in column position
	  ADD	  DI,DX 	       ; account for attribute bytes



;	  ...	  4) load attribute for string

	  MOV	  BP,CHAR_ATTRIBUTE
	  MOV	  BH,BYTE PTR DS:[BP]  ; load attribute for string



;	  ...	  5) move character count to CX for use in loop

	  MOV	  CX,CHAR_COUNT        ; load the character count



;	  ...	  6) load type of crt display from 0000:463

	  SUB	  AX,AX 	       ; address system memory
	  MOV	  ES,AX

	  MOV	  DX,WORD PTR ES:[463h]      ; load address of display adapter
	  ADD	  DX,6			     ; address crt status port


;	  ...	  7) address the screen segment

	  MOV	  AX,0B000h	       ; screen seg for monochrome card
	  CMP	  DX,03DAh	       ; is this a graphic card?
	  JNE	  MONOCHROME

	  MOV	  AX,0B800h	       ; load screen seg for graphic card

MONOCHROME:

	  MOV	  ES,AX 	       ; address the screen buffer



;	  ...	  8) move string to screen while synchronizing
;		      with horizontal retrace

	  TEST	  LOCAL_FLAG,JUST_BLANKS
	  JZ	  DISPLAY_LOOP

	  MOV	  BL,20h	       ; load a blank
BLANK_LOOP:
	  CALL	  DISPLAY_CHAR	       ; display a line of blanks for basic
	  LOOP	  BLANK_LOOP
	  JMP	  UPDATE_POSITION


DISPLAY_LOOP:
	  LODSB 		       ; load next character

	  CMP	  AL,20h	       ; special character?
	  JGE	  NOT_SPECIAL
	  CALL	  SPECIAL
	  JZ	  ANOTHER_CHAR	       ; if flag set, go for another character

NOT_SPECIAL:

	  MOV	  BL,AL

	  CLI
HSYNC_WAIT1:
	  IN	  AL,DX 	       ; check for horizontal retrace
	  TEST	  AL,1
	  JNZ	  HSYNC_WAIT1	       ; wait for retrace
HSYNC_WAIT2:
	  IN	  AL,DX 	       ; check for horizontal retrace
	  TEST	  AL,1
	  JZ	  HSYNC_WAIT2	       ; wait for retrace

	  MOV	  AX,BX
	  STOSW 		       ; store character and attribute

ANOTHER_CHAR:
	  STI

	  CMP	  DI,END_OF_PAGE
	  JL	  NOT_SCROLLED

	  MOV	  AL,0Dh	       ; force a carriage return and scroll
	  CALL	  SPECIAL

NOT_SCROLLED:

	  LOOP	  DISPLAY_LOOP	       ; repeat cx times



;	  ...	 9) update cursor position on screen


UPDATE_POSITION:
	  MOV	  AX,DI
	  SUB	  AX,STARTING_PAGE
	  SHR	  AX,1		       ; eliminate attribute bytes
	  SUB	  DX,DX
	  MOV	  BX,80 	       ; divide by bytes per line
	  DIV	  BX		       ;   quotient in AL (ROW)
				       ;   remainder in DL (COLUMN)
	  MOV	  DH,AL
	  MOV	  BP,SCREEN_PAGE
	  MOV	  BH,BYTE PTR DS:[BP]  ; load page number
	  MOV	  AH,2		       ; request new position
	  INT	  10h


;	  ...	  10) update cursor position

	  XCHG	  DH,DL 	       ; basic likes this reversed
	  INC	  DH		       ; basic counts from 1 instead of 0
	  INC	  DL		       ; basic counts from 1 instead of 0
	  MOV	  BP,SCREEN_POSITION
	  MOV	  WORD PTR DS:[BP],DX


;	  ...	  11) leave registers in manner BASIC expects

	  MOV	  CX,RESIDUAL	       ; let decrement instr take this to zero
				       ;  inside basic
	  POP	  DI
	  POP	  DX

	  POP	  BX
	  POP	  ES
	  POP	  BP


	  ADD	  SP,4		       ; throw away offset and code segment
				       ;  from our call

	  POP	  AX		       ; throw away near call on stack
	  PUSH	  BASIC_SEGMENT        ; and convert to a far call
	  PUSH	  AX
	  RET			       ; return to basic


PRINTER   ENDP

;______________________________________________________________________________

;  subroutine to handle special control characters

SPECIAL   PROC	  NEAR

; ----------

	  CMP	  AL,0Ah	       ; line feed?
	  JE	  NEW_LINE


; ----------

	  CMP	  AL,0Bh	       ; home?
	  JNE	  NOT_HOME
	  MOV	  DI,STARTING_PAGE     ; start over at top of screen
	  MOV	  STARTING_LINE,DI
	  SUB	  AL,AL
	  RET

NOT_HOME:

; ----------

	  CMP	  AL,0Ch	       ; clear screen?
	  JNE	  NOT_CLEAR
	  MOV	  DI,STARTING_PAGE
	  MOV	  STARTING_LINE,DI
	  MOV	  AL,0		       ; clear whole window
	  JMP	  SHORT SCROLL_SCREEN


NOT_CLEAR:

; ----------

	  CMP	  AL,0Dh	       ; carriage return?
	  JNE	  NOT_CR

NEW_LINE:
	  MOV	  DI,STARTING_LINE
	  ADD	  DI,160
	  JMP	  SHORT TEST_RIGHT

NOT_CR:

; ----------

	  CMP	  AL,1Ch	       ; move right?
	  JNE	  NOT_RIGHT
	  ADD	  DI,2
	  JMP	  SHORT TEST_RIGHT

NOT_RIGHT:

; ----------

	  CMP	  AL,1Dh	       ; move left?
	  JNE	  NOT_LEFT
	  SUB	  DI,2
	  JMP	  SHORT TEST_LEFT

NOT_LEFT:

; ----------

	  CMP	  AL,1Eh	       ; move up?
	  JNE	  NOT_UP

	  SUB	  DI,160
	  JMP	  SHORT TEST_LEFT

NOT_UP:

; ----------

	  CMP	  AL,1Fh	       ; move down?
	  JNE	  NOT_DOWN

	  ADD	  DI,160
	  JMP	  SHORT TEST_RIGHT

NOT_DOWN:
	  JMP	  SHORT TEST_FOR_TAB

; ----------

TEST_RIGHT:
	  CMP	  DI,END_OF_PAGE       ; are we past line 25?
	  JL	  VALID_RIGHT
	  MOV	  DI,STARTING_PAGE     ; back at start of last line
	  ADD	  DI,3840
	  MOV	  STARTING_LINE,DI

	  MOV	  AL,1		       ; scroll one line
	  JMP	  SHORT SCROLL_SCREEN

VALID_RIGHT:
	  SUB	  AL,AL
	  RET

; ----------

SCROLL_SCREEN:
	  PUSH	  CX
	  MOV	  CX,0		       ; start in upper left corner
	  PUSH	  DX
	  MOV	  DX,184Fh	       ; end in lower right
	  MOV	  AH,6
	  PUSHF 		       ; simulate an INT 10
	  CALL	  DWORD PTR ORIG_INT10
	  POP	  DX
	  POP	  CX
	  SUB	  AL,AL
	  RET

; ----------

TEST_LEFT:
	  CMP	  DI,STARTING_PAGE
	  JGE	  VALID_LEFT

	  MOV	  DI,STARTING_PAGE

VALID_LEFT:
	  SUB	  AL,AL
	  RET

; ----------

TEST_FOR_TAB:

	  CMP	  AL,09h	       ; tab?
	  JNE	  NOT_TAB

	  PUSH	  CX
	  PUSH	  DX
	  MOV	  AX,DI
	  SUB	  AX,STARTING_LINE
	  SHR	  AX,1		       ; discount attribute bytes
	  SUB	  DX,DX
	  MOV	  CX,8
	  DIV	  CX
	  MOV	  CX,8		       ; tab positions are every 8 charactes
	  SUB	  CX,DX 	       ; subtract off remainder
	  MOV	  BL,' '               ; write some blanks
	  POP	  DX
TAB_LOOP:
	  CALL	  DISPLAY_CHAR
	  LOOP	  TAB_LOOP

	  POP	  CX
	  SUB	  AL,AL
	  RET

NOT_TAB:

; ----------

	  CMP	  AL,08h	       ; backspace?
	  JNE	  NOT_BACKSPACE

	  CMP	  DI,STARTING_LINE
	  JE	  AT_START
	  SUB	  DI,2		       ; back up a space

AT_START:
	  MOV	  BL,' '               ; write a blank
	  CALL	  DISPLAY_CHAR

	  SUB	  DI,2		       ; back up a space
	  SUB	  AL,AL
	  RET

NOT_BACKSPACE:

; ----------

	  CMP	  AL,00h	       ; null?
	  JNE	  NOT_NULL

	  TEST	  LOCAL_FLAG,ZERO_TAGGED  ; does this mean end of string?
	  JZ	  NOT_NULL		  ;  branch if null meaningless
	  MOV	  CX,1		       ; terminate the display loop
	  DEC	  SI		       ; backup so basic can see null too
	  SUB	  AL,AL
	  RET

NOT_NULL:

; ----------

	  MOV	  AH,2
	  SUB	  AH,1		       ; set flag to display the char
	  RET

SPECIAL   ENDP

;______________________________________________________________________________

;  routine to display a character in BL - used only for special characters
;     and repeated blanks - attribute is in BH


DISPLAY_CHAR	  PROC	NEAR

	  CLI
HSYNC_WAIT3:
	  IN	  AL,DX 	       ; check for horizontal retrace
	  TEST	  AL,1
	  JNZ	  HSYNC_WAIT3	       ; wait for retrace
HSYNC_WAIT4:
	  IN	  AL,DX 	       ; check for horizontal retrace
	  TEST	  AL,1
	  JZ	  HSYNC_WAIT4	       ; wait for retrace

	  MOV	  AX,BX
	  STOSW 		       ; store character and attribute

	  STI
	  RET

DISPLAY_CHAR	  ENDP


;______________________________________________________________________________



LASTADDR  LABEL   BYTE

COPYRIGHT LABEL   BYTE
	  DB	  10,13
	  DB  '         CLUBware  (tm)',10,13,10,13
	  DB  'PRSLASHO - Enhancement to the Basic Compiler Print statement'
	  DB	  10,13
	  DB  '                 (for routines compiled with /O option)'
	  DB	  10,13,10,13
	  DB  '         Copyright 1984 Rayhawk Automation N.W. Inc',10,13
	  DB  '                        P.O. Box 1427',10,13
	  DB  '                        Beaverton, Oregon   97075',10,13,'$'


;______________________________________________________________________________


PRSLASHO  PROC	  FAR


	  PUSH	  DS		       ; Push addr of Program Segment Prefix
	  SUB	  AX,AX 	       ; Zero AX
	  PUSH	  AX		       ; Push zero onto stack
;					 (offset of INT 20 within PSP)


;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|
;		      | 						 |
;		      | 	   take over the INT 10h		 |
;		      | 	   interrupt if not already done	 |
;		      | 						 |
;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|

	  MOV	  DS,AX 	       ; address low memory

;	  CMP	  WORD PTR DS:[40h],OFFSET INTSWAP
;	  JE	  ALREADY_DONE

	  MOV	  AX,WORD PTR DS:[40h] ; save original int10
	  MOV	  ORIG_INT10,AX
	  MOV	  AX,WORD PTR DS:[42h]
	  MOV	  ORIG_INT10+2,AX

	  MOV	  AX,SEG PRSLASHO
	  MOV	  DS,AX
	  MOV	  DX,OFFSET INTSWAP    ; Load offset of interrupt service mod
	  MOV	  AX,2510h	       ; Prepare for DOS service call type 25
;					 to establish service for INT 10
	  INT	  21h		       ; Ask DOS to establish service

;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|
;		      | 						 |
;		      | 	   issue copyright message		 |
;		      | 						 |
;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|

	  MOV	  DX,OFFSET COPYRIGHT
	  MOV	  AH,9
	  INT	  21h


;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|
;		      | 						 |
;		      | 	   modify INT 20 into INT 27 in the	 |
;		      | 	   program segment prefix		 |
;		      | 						 |
;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|

	  MOV	  BYTE PTR ES:[01],27h ; Change INT 20h to INT 27h

;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|
;		      | 						 |
;		      | 	6) load address of ending tag into DX	 |
;		      | 						 |
;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|

	  MOV	  AX,SEG LASTADDR
	  SUB	  AX,SEG PRSLASHO
	  MOV	  CL,4		       ; prepare for 4 bit shift
	  SHL	  AX,CL 	       ; shift up (convert from seg to abs)
	  ADD	  AX,OFFSET LASTADDR   ; add address of bottom location
	  ADD	  AX,0103h	       ; Pad offset because DOS measures
;					  offset relative to Program
;					  Segment Prefix
	  MOV	  DX,AX 	       ; leave where DOS will find it

;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|
;		      | 						 |
;		      | 	7) use RET FAR to return to DOS and	 |
;		      | 	   leave service routine resident	 |
;		      | 						 |
;		      | - - - - - - - - - - - - - - - - - - - - - - - - -|

ALREADY_DONE:

	  RET

PRSLASHO  ENDP

SCRDSEG   ENDS

;______________________________________________________________________________

STACK	  SEGMENT PARA STACK 'STACK'
	  DB	  24 DUP('STACK***')
TOPSTACK  DB	  0
STACK	  ENDS

	  END	  PRSLASHO
