

;	W_SCRN - A MACRO to write directly to screen ram
;
; (doesn't use BIOS) (parameters are Line#, Col#)
;
; This MACRO would usually be used to create a subroutine. When
; entering, the SI register must point to the $ terminated string
; to be printed.

W_SCRN  MACRO;   LINE, COL
	PUSH AX
	PUSH DI
	PUSH ES
	MOV ES,SCR_SEG
        MOV DI, (#1 * 160 + #2 * 2)
M0:     MOV AL,[SI]
	CMP AL,'$'
        JE >M1
	MOV ES:[DI],AL
	INC SI
	INC DI
	INC DI ;; Skip attribute byte
        JMP M0
	
M1:     POP ES
	POP DI
	POP AX
        #EM



; PRINT_LM prints text with a constant left margin starting
; at a screen location stored at STARTLM as 2*COL + 160*ROW
; No registers are saved except ES, so save AX, BL, SI, DI if
; you need 'em.

PRINT_LM MACRO; LMARG, STARTROW, STRING2DO
        PUSH ES
        MOV ES, SCR_SEG
        MOV DI, #3 ;; address of string
        MOV SI, (#2 * 160 + #1 * 2)
        MOV AX, SI ;; AX holds beginning of current line
M3:     MOV BL, [DI]
        INC DI
        CMP BL, 10 ;; 10 means we're finished
        JE > M1
        CMP BL, 13 ;; 13 means end line (do linefeed)
        JNE > M2
        ADD AX, 160 ;; drop to next line
        MOV SI, AX
        JNZ M3 ;; forced branch
M2:     MOV ES:[SI], BL
        INC SI
        INC SI
        JMP NEAR M3
M1:     POP ES
        #EM

; A macro to write a specified # of bytes to the current cursor
; position without the need for string terminator. You must have
; DS:DX pointing to the string.  This is because QUICKA can't pass text
; strings such as [BUFFER+BP]  to a MACRO

WRITE_SCREEN MACRO; N_BYTES

	MOV AH, 040h
        MOV BX, 1;; Handle for screen
        MOV CX, #1;;N_BYTES
	INT 021h

        #EM

; WRITE_TSTR writes a string terminated by a marker byte, 0 or $ for
; example, to the screen.  It uses macro COUNT2C which uses FIND_BYTE

WRITE_TSTR MACRO; ADDRESS, CHR
        COUNT2C #1, #2
	MOV AH, 040h
        MOV BX, 1;; Handle for screen
        MOV DX, #1
	INT 021h

        #EM

; COUNT2C counts the bytes in a string up to a specified byte, but not
; including it.  Returns the byte count in CX

COUNT2C MACRO; OFFSET, CHR
        FIND_BYTE #1, #2, 0FFFFh ;; allows 64K search
        SUB DI, #1 + 1 ;; make DI = absolute # of bytes
        MOV CX, DI
        #EM


; WRITE_VERT writes a column of fields to the screen. Parameters are:
; Offset to screen byte (ROW*160 + COL*2)
; # of characters per field
; # of fields
; Address label of character field array

WRITE_VERT MACRO;; SCREEN_OFFSET, # CHARS/FIELD, #FIELDS, ADDRESS

        MOV ES, SCR_SEG;; B800 OR B000
        MOV SI, #4
        MOV BX, #1
        MOV CX, #3

M0:     PUSH CX
        MOV CX, #2
        MOV DI, BX
M1:     MOV AL, [SI]
        MOV ES:[DI], AL
        INC SI
        INC DI
        INC DI;; Skip attribute byte on screen
        LOOP M1
        ADD BX, 160;; drop down one row
        POP CX
        LOOP M0
#EM
;       A MACRO TO FIND A BYTE IN A STRING

; Assumes string is in data segment, after finding the byte,
; DI will point to its offset + 1.
; If no match is found, the ZERO flag will be clear.

FIND_BYTE MACRO; STRING, XBYTE, XLENGTH
	CLD ; 3.15 added 8/21/93
	PUSH DS
	POP ES
	MOV DI, OFFSET #1;; String is name of string to search
	MOV CX, #3;; XLENGTH  is max length to be searched
	MOV AL, #2;; XBYTE is the byte we are looking for
	REPNE SCASB
        #EM

; A macro to convert the 16 bit binary # in AX to the 4 digit text Hex
; string representing it. The string goes to an offset supplied as the
; argument.

HEXTEX  MACRO;   STRADR

        PUSH AX,BX,CX,DI

	MOV CX,4;;       Counter for 4 hex digits
	MOV DI, OFFSET #1
	ADD DI,3;; LSD first
M1:     MOV BL,AL
	AND BL,0Fh
	CMP BL,0Ah
        JB >M2
	ADD BL,'A'-10
        JMP >M3
M2:     ADD BL,'0'
M3:     MOV [DI],BL
	DEC DI
	PUSH CX
	MOV CX,4
	SHR AX,CL
	POP CX
        LOOP M1
        POP DI,CX,BX,AX

;; Put a RET here if you're using the macro as a subroutine

	#EM

; A MACRO to call DOS (INT 021h) and check the CRITIC variable
; and jmp to the specified address if the user ignores a critical
; error

GO2DOS  MACRO; BADJUMP
	MOV CRITIC,0;; Means critical error not ignored
	INT 021h
	CMP CRITIC,0
        JE >M1
	JMP #1
M1:
	#EM



; A MACRO TO DELAY # OF TIMER TICKS. THERE ARE 18.2 TICKS/SEC
; THE TIMER 0 INTERUPT MUST BE REDIRECTED TO MY ROUTINE MYVEC8



TICKS   MACRO;   TIX
        PUSH AX; 4.15 introduced use of AX so value could be variable
        MOV AX, #1
        MOV VARBL, AX
        POP AX
M1:     CMP VARBL,0
        JNE M1
	#EM


; This macro moves a string of bytes, expected to be
; both in code segment.

SMOVE    MACRO;   FROM, TO, N_BYTES

	MOV CX,#3
	PUSH CS
	PUSH CS
	POP ES
	POP DS
	MOV DI,OFFSET #2
	MOV SI,OFFSET #1
	CLD
	REP MOVSB

	#EM

;               FILL - A MACRO

; This macro fills a block of memory with the specified
; byte.

FILL    MACRO;   CHAR, START_ADR, N_BYTES
	MOV CX,#3
	MOV AL,#1
M1:     MOV SI,CX
        MOV [#2-1][SI],AL
        LOOP M1
	#EM

;               READ_FILE - A MACRO

READ_FILE MACRO; HANDLE, N_BYTES, BUFFER, NOERR_JMP

	MOV AH,03Fh;; Read file command
	MOV BX, #1;; Handle
	MOV CX, #2 ;; # of bytes to read
	MOV DX, OFFSET #3
	INT 021h;; Call DOS
	JNC #4 ;; Carry clear means no error

; user's error handling code, if any, follows macro call

	#EM

;       CURSET - A MACRO

; This macro moves the cursor to the row/column supplied in
; the parameter word, high = row, low = col. ex: 0110 means
; row 1, column 16 (hex 10). VPAGE assumed to hold video page #

CURSET MACRO;    ROWCOL
        PUSH AX,BX,DX
	MOV AH, 02
	MOV BH,VPAGE
	MOV DX,#1
	INT 010h
        POP DX,BX,AX
	#EM

; MACROS TO MOVE THE CURSOR TO THE START OF INFO LINES 1,2,3,4

INFO1   MACRO
	CURSET 01200h;; INFO LINE 1 = 18
        #EM
INFO2   MACRO
	CURSET 01300h;; INFO LINE 2 = 19
        #EM
INFO3   MACRO
	CURSET 01400;; LINE 20
        #EM
INFO4   MACRO
	CURSET 01500;; LINE 21
        #EM


;       PRINTL - A MACRO

; This macro prints a line to the standard output. The
; line must be terminated in a '$'. The parameter required
; is the address (offset) of the line's first byte.


PRINTL MACRO;    STRING
	PUSH BP
	MOV BP,OFFSET #1
	CALL PRNT_LINE
	POP BP
	#EM

;       PRINTLA - Print line plus attribute:
; Note: line is terminated in "$"

PRINTLA MACRO;   STRING, ATTR

; first, count the # of letters:

	FIND_BYTE #1, '$', 81; DI will point to $+1 address
	SUB DI, OFFSET #1
	DEC DI; get # of bytes, not incl. $
	MOV CX, DI      ; Put # of bytes into CX
	MOV AH, 09h     ; BIOS function
	MOV AL, ' '
	MOV BH, VPAGE
	MOV BL, #2
	INT 010h        ; write CX spaces with attribute @ cursor

; note that above function didn't move the cursor, now write the
; string:

	PRINTL #1
	#EM

;   LOCATION - A MACRO     *** REV 2.10 ***

; This Macro takes an ascii character as a parameter and
; generates code to put it to the location field without
; affecting any registers

LOCATION    MACRO;   CHAR
	PUSH AX
	MOV AX,#1
	CALL PUT_LOC
	POP AX
	#EM

;   EROR - A MACRO *** REV. 2.10

; This Macro takes an ascii character as a parameter and
; generates code to put it to the error field without
; affecting any registers

EROR   MACRO;   CHAR
	MOV ERR_CHAR,#1
	CALL PUT_ERCH
	#EM

;       PSTATUS - MACRO

; Prints the addressed status message at row 22, column 0.
; Message label is parameter. Also puts the specified attribute
; to the line

PSTATUS MACRO; STRING, ATTR
        PUSH AX,BX,DX

;; set cursor to line 22

	MOV AH, 02
	MOV BH,VPAGE
	MOV DX,22*0100h
	INT 010h

;; clear line and put attribute, cursor doesn't move (I hope)

	MOV AL, ' '
	MOV BL, #2
	MOV CX, 80
	MOV BH, VPAGE
	MOV AH, 09h
	INT 010h

;;      PUT_CHARS #2, ' ', 80
	PRINTL #1
        POP DX,BX,AX
        #EM

; PUT_CHARS this macro uses INT 10h, function 09h to put
; N chars to the screen at the current cursor position. All positions
; receive the same character and attribute.  It can be used to clear
; screen, or fill an area with a particular attribute, or make a line
; VPAGE must be defined. WARNING: REGISTERS AREN'T SAVED

PUT_CHARS       MACRO; ATTRIB, CHAR, NUMBER
	MOV AL, #2
	MOV BL, #1
	MOV CX, #3
	MOV BH, VPAGE
	MOV AH, 09h
	INT 010h
        #EM

;************************* drawing lines and box macros *****************
;
; ****************** Borrowed from the FREQ source code ******************

; Macros for drawing boxes:

; These macros can be used to draw lines or boxes, from either
; double or single lines.
; The routines write directly to screen ram
; The macros are combined into a BOX macro which can draw a
; box with just the following parameters given:
; ROW, COLUMN of upper left corner
; CHAR_SET pointer which is 0 for double lines and
; SING_SET for single lines. It uses local constant symbol S0
; WIDTH AND HEIGHT
; COLOR attribute to be used (use color name from EQUATES list)

; When drawing without using the BOX macro, start with the POINT
; macro, which sets up the ROW, COL, CHAR_SET, and COLOR for
; subsequent macros.
; The POINT macro also sets up local constant symbols R0 AND C0 for
; later reference to ROW and COL. It also sets ES equal to the variable
; SCR_SEG which should be defined in the video init part of your program
; SCR_SEG is usually B800 for color monitors.


; POINT puts "cursor" (DI) at ROW, COL

POINT MACRO; ROW, COL, CHAR_SET, COLOR
        MOV DI, #1*160 +#2*2
        S0 EQU #3
        MOV AH, #4
R0      EQU #1
C0      EQU #2
        MOV ES, SCR_SEG
        #EM

RIGHT MACRO; NUMBER, SET
        MOV AL, [HORIZ+S0]
        ;MOV AH, BOX_COLOR
        MOV CX, #1
        CLD; increment
        ;MOV ES, SCR_SEG
        REP STOSW
H1      EQU 1; Horiz direction is right
#EM


LEFT MACRO; NUMBER, SET
        MOV AL, [HORIZ+S0]
        ;MOV AH, BOX_COLOR
        MOV CX, #1
        STD; decrement
        ;MOV ES, SCR_SEG
        REP STOSW
H1      EQU 0; Horiz direction is left
#EM

UP MACRO; NUMBER, SET
        MOV AL, [VERT+S0]
        ;MOV AH, BOX_COLOR
        MOV CX, #1
        ;MOV ES, SCR_SEG
M1:     MOV ES:[DI], AX
        SUB DI, 160; UP ONE ROW
        LOOP M1
V1      EQU 1; Vertical direction is up
#EM

DN MACRO; NUMBER, SET
        MOV AL, [VERT + S0]
        ;MOV AH, BOX_COLOR
        MOV CX, #1
        ;MOV ES, SCR_SEG
M2:     MOV ES:[DI], AX
        ADD DI, 160; DOWN ONE ROW
        LOOP M2
V1      EQU 0; Vertical direction is DOWN
#EM

; A86 USAGE NOTE: THE ## DOUBLE HASH'S ARE NECESSARY IF
; THE "IF" STATEMENT IS TO BE EVALUATED BASED ON THE VALUE
; OF ITS ARGUMENT WHEN THE STATEMENT IS EXPANDED. OTHERWISE, IT
; WILL BE EVALUATED ONLY WHEN  THE MACRO IS READ AND THE "IF" WILL
; HAVE NO EFFECT, EVEN IF ITS ARGUMENT HAS CHANGED.
;
; ALSO, THE ARGUMENT MUST BE A LOCAL SYMBOL, SINCE A86 DOESN'T
; ALLOW REDEFINITION OF ANY OTHERS


TURN_UP MACRO; NUMBER, SET
        MOV BL, H1
        ##IF H1
        MOV AL, [LRC + S0]
        ##ELSE
        MOV AL, [LLC + S0]

        ##ENDIF
        ;MOV AH, BOX_COLOR
        ;MOV ES, SCR_SEG
        MOV ES:[DI], AX
        SUB DI, 160; cursor up to above space
        UP #1-1, S0
#EM

TURN_DN MACRO; NUMBER, SET
        ##IF H1
        MOV AL, [URC + S0]
        ##ELSE
        MOV AL, [ULC + S0]
        ##ENDIF
        ;MOV AH, BOX_COLOR
        ;MOV ES, SCR_SEG
        MOV ES:[DI], AX
        ADD DI, 160; cursor down
        DN #1-1, S0
#EM

TURN_RT MACRO; NUMBER, SET
        ##IF V1
        MOV AL, [ULC + S0]
        ##ELSE
        MOV AL, [URC + S0]
        ##ENDIF
        ;MOV AH, BOX_COLOR
        ;MOV ES, SCR_SEG
        MOV ES:[DI], AX
        INC DI
        INC DI
        RIGHT #1-1, S0
#EM

TURN_LT MACRO; NUMBER, SET
        MOV BL, V1
        ##IF V1
        MOV AL, [URC + S0]
        ##ELSE
        MOV AL, [LRC + S0]
        ##ENDIF
        ;MOV AH, BOX_COLOR
        ;MOV ES, SCR_SEG
        MOV ES:[DI], AX
        DEC DI
        DEC DI
        LEFT #1-1, S0
#EM

; CONNECT_V is a macro which draws a vertical connecting line
; in the down direction. It starts with a tee down, ends with a tee
; up, and puts a cross where it detects any line crossing.  You
; give parameters as ROW, COL OFFSETS from the previously defined
; upper left corner, plus a length.

; To split a box in half, it should have a width of N, where N is odd
; and the COL_OFFSET should be (N-1)/2

CONNECT_V MACRO; ROW_OFFSET, COL_OFFSET, LENGTH
        MOV DI, (R0 + #1)*160 + (C0 + #2)*2; plot starting point
        MOV AL, [TEE_DN + S0]
        MOV ES:[DI], AL
        MOV BL, [HORIZ + S0]; to test for crossings
        MOV AL, [VERT + S0]
        MOV BH, [CROSS + S0]
        MOV CX, #3 - 2; subtract 2 for end tees
M1:     ADD DI, 160
        CMP ES:[DI], BL
        JNE >M2
        MOV ES:[DI], BH; install a cross
        JMP >M3
M2:     MOV ES:[DI], AX
M3:     LOOP M1
        ADD DI, 160
        MOV AL, [TEE_UP + S0]
        MOV ES:[DI], AL
#EM

; CONNECT_H is a macro which draws a horizontal connecting line
; in the right direction. It starts with a tee right, ends with a tee
; left, and puts a cross where it detects any line crossing.  You
; give parameters as ROW, COL OFFSETS from the previously defined
; upper left corner, plus a length.

; To split a box in half, the box should have a height of N which
; is an odd number.  Then the row offset should be (N-1)/2


CONNECT_H MACRO; ROW_OFFSET, COL_OFFSET, LENGTH
        MOV DI, (R0 + #1)*160 + (C0 + #2)*2; plot starting point
        MOV AL, [TEE_RT + S0]
        MOV ES:[DI], AL
        MOV BL, [VERT + S0]; to test for crossings
        MOV AL, [HORIZ + S0]
        MOV BH, [CROSS + S0]
        MOV CX, #3 - 2; subtract 2 for end tees
M1:     INC DI
        INC DI
        CMP ES:[DI], BL
        JNE >M2
        MOV ES:[DI], BH; install a cross
        JMP >M3
M2:     MOV ES:[DI], AX
M3:     LOOP M1
        INC DI
        INC DI
        MOV AL, [TEE_LT + S0]
        MOV ES:[DI], AL
#EM


BOX     MACRO;; ROW, COL, WIDTH, HEIGHT, SET, COLOR
V1      EQU 1;; SIMULATE UP DIRECTION
        POINT #1, #2, #5, #6
        TURN_RT #3-1, #5
        TURN_DN #4-1, #5
        TURN_LT #3-1, #5
        TURN_UP #4-1, #5
#EM
