Technical Notes 1
=================

  The following is technical information and data that the programer may find
useful in programming for the "grahics environment", or for understanding the
working of this system.  This information assumes the knowledge of assembly
language programming under TRSDOS/LS-DOS version 6.  With that understood, here
goes:

The XHRG Module
===============
  The XINIT module is 28 bytes long, and has the module name "XHRG".  This
module is installed the first time XINIT is executed.  This module may also be
SYSGENed.  If you are programming graphics programs in Assembly Language (or
even Basicg), you may wish to access some of the information within the XHRG
module.  To do this, use the "@GTMOD" SVC, and then transfer the address
returned in DE to the IX register like this:

       LD   DE,XHRG$  ;GET MODULE NAME
       SVC  @GTMOD    ;FIND MODULE
       JP   NZ,NOMOD  ;ERROR -- ESCAPE IF NOT FOUND
       PUSH DE        ;TRANSFER DATA POINTER
       POP  IX        ;TO IX REGISTER
       ...continued   ;MORE CODE HERE
 XHRG$ DEFM 'XHRG'    ;MODULE NAME
       DEFB 03H       ;TERMINATOR

  Also, add these EQUates to your program:

X       EQU 128 ; THE X GRAPHICS ADDRESS PORT
Y       EQU 129 ; THE Y GRPAHICS ADDRESS PORT
DATA    EQU 130 ; THE BYTE OUTPUT TO THE GRAPHICS BOARD
OPTS    EQU 131 ; THE GRAPHICS BOARD OPTIONS REGISTER
_ERRRTN EQU 0   ; ERROR CODE (SEE INDIVIDUAL PROGRAMS FOR CODES RETURNED)
_OPORT  EQU 4   ; IMAGE OF OPTIONS REGISTER (TEXT/GRAPH AND VIDEO WAIT STATUS)
_XVIEW  EQU 6   ; CURRENT X SCROLL POSITION (IMAGE OF PORT 140)
_YVIEW  EQU 7   ; CURRENT Y SCROLL POSITION (IMAGE OF PORT 141)
_TXTGRA EQU 8   ; MIXED TEXT/GRAPHICS STATUS (IMAGE OF PORT 142)
_BUFFER EQU 9   ; BANK NUMBER RESERVED FOR "BUFFER" BANK (0=NONE RESERVED)
_BUFPTR EQU 10  ; POINTER (TWO BYTES) TO FIRST AVAILABLE BYTE IN BUFFER BANK
_IMAGE  EQU 12  ; BANK NUMBER RESERVED FOR "IMAGE" (NOT CURRENTLY SUPPORTED)
_DISK   EQU 15  ; BANK NUMBER RESERVED FOR "DISK" I/O BUFFER (0=NOT RESERVED)
_DSKPTR EQU 16  ; POINTER (TWO BYTES) TO LAST AVIALABLE BYTE IN DISK BUFFER

  Now, by using instructions like "LD A,(IX+_OPORT)", you can access the
current value of the screen and video waits, etc.  This is the standard way of
interfacing to the XHRG module.  For BASICG programmers, you can use the
"FDNMOD.BAS" program in DL3 of TRS80PRO to access module information.  If you
change the status of the screen in any way, please make sure to reflect the
change by changeing the proper bits within the XHRG module.  The following
example turns on the graphics screen:

        LD   A,(IX+_OPORT) ; GET OPTIONS REGISTER IMAGE
        SET  0,A           ; TURN ON "TEXT/GRAPHICS" BIT
        OUT  (OPTS),A      ; PROGRAM GRAPHICS BOARD
        LD   (IX+_OPORT),A ; STORE CHANGE IN XHRG MODULE

  Note that using this code does not affect the previous status of the video
waits, and that only bits 0 and 1 should be altered or tested.  Bits 2-7
control address clocking, and should be determined by each program that uses
it.  These bits should remain zero within the _OPORT location of the XHRG
module.  For details concerning the programming of the options register and
other graphics board registers, please refer to the PORTS.DOC file in the
Graphics Section (ss4) of TRS80PRO.  For accessing the graphics data via BASIC
and the FDNMOD subroutine, you must add an offset of 6 to each of the equates
listed above -- that's because FDNMOD returns the ENTRY point of the module,
and NOT the data area used by the subroutine above.


The "Frame" format
==================
  Each frame is stored into a back bank as a "header" followed by the graphics
data representing the frame.  The bank that holds this data is stored in the
XHRG module in the BUFFER field.  The format of this "header" is as follows:

HEADER:
OFFSET   DEFW  0000H      ; LENGTH OF THIS FRAME (INCLUDING HEADER)
NAMELEN  DEFB  00H        ; LENGTH OF THE NAME ASSIGNED TO THIS FRAME
NAME     DEFM  '        ' ; EIGHT CHARACTER NAME OF FRAME
XSTRT    DEFB  00H        ; X PORT POSITION FOR START OF FRAME (0-127)
XCOUNT   DEFB  00H        ; WITH OF FRAME IN BYTES
YSTRT    DEFB  00H        ; Y PORT POSITION FOR START OF FRAME (0-255)
YCOUNT   DEFB  00H        ; HEIGHT OF FRAME IN PIXELS
LMSK     DEFB  00H        ; MASK FOR CLIPPING LEFT BORDER OF FRAME
RMSK     DEFB  00H        ; MASK FOR CLIPPING RIGHT BORDER OF FRAME
LMSKC    DEFB  00H        ; MASK FOR CLIPPING LEFT BORDER SCREEN BYTE
RMSKC    DEFB  00H        ; MASK FOR CLIPPING RIGHT BORDER SCREEN BYTE
XEND     DEFB  00H        ; X PORT POSITION FOR END OF FRAME (0-127)
HEADLEN  EQU   $-HEADER   ; LENGTH OF HEADER MODULE (20 BYTES)

  The graphic data follows immediately, and consists of XCOUNT columns (from
top down) consisting of YCOUNT bytes each.  If the HL register points to the
beginning of the header module of some frame stored in a back bank, then the
next frame can be found by adding the word found at (HL) to HL.  This can be
done by:

 PUSH HL
 LD C,(HL)
 INC HL
 LD B,(HL)
 POP HL
 ADD HL,DE

  The _BUFPTR field of the XHRG module points to the first free byte in the
BUFFER bank.  You can use this value to quickly add frames to the buffer, and
to detect when the end of the frames has been found, when scanning through the
frames one by one.

The Masks
=========
  Since a frame may be positioned anywhere in the graphics memory, not just on
"byte boundries", "masks" are needed to make sure only the proper pixels are
modified within the graphics memory.  These masks are used like this:

LMSK is used to strip off unneeded bits from the data stored in memory.  When a
frame is XCUT into a back bank, only rarely does the left border use the ENTIRE
byte that is stored.  By ANDing the stored byte with the LMSK value, the
unwanted bits (i.e. the ones outside the frame) are set to zero, and the bits
that are actually part of the frame are left un-altered.  RMSK serves the exact
same function for the right border of the frame.

LMSKC is actually just the "compliment" of LMSK, and is used to preserve the
portion of the byte on the screen that the frame partially occupies.  The byte
is read in from the screen, ANDed with LMSKC to set all bits that are part of
the frame to zero, and then this value is ORed with the value returned by the
above ANDing the frame byte with LSMK.  The result of these operations is the
merging of the frame with the screen -- this value is then OUTed to the
graphics screen.  RMSKC functions the same for the right border.  Note that no
special operations need to be done to the "interior" columns of the frame data.
Also note the special case of a frame that falles entirely within one "byte"
column (i.e. is less than 8 pixels wide).  Such a byte must have BOTH the left
and right masks applied in turn to give the desired result.

  It is said a picture is worth a thousand words.  Such is the case with this
masking concept -- if you have trouble following the above, and are a
registered user if XINIT, then let me know, and I can mail you, via US Post, a
graphic detail of the above operations, and can answer any questions you may
have.

Special Routines
================
  The following routines are used to convert an "x,y" pixel address into the
necessary port/bit information required by the graphics board.  Also included
are routines to calculate the mask values listed above.  These are the standard
methods of computing these values and working with them:

CNVCORD:            ; ROUTINE TO CALCULATE PORT/BIT VALUE FROM X,Y
                    ; HL = X VALUE (0-1023).  Y NEEDS NO CONVERSION.
        PUSH BC     ; SAVE REGISTERS USED
        LD   A,L    ; GET LOW ORDER BITS OF X COORDINATE
        AND  7      ; GET REMAINDER OF X/8 INTO A
        RR   H      ; COMPUTE HL/8 THE FAST WAY
        RR   L      ; HL/2
        RR   H
        RR   L      ; HL/4
        RR   H
        RR   L      ; L=HL/8, H=GARBAGE
        LD   H,A    ; H=PORT ADDRESS, L=REMAINDER
        LD   A,7    ; GET MAX REMAINDER
        SUB  H      ; COMPUTE 7-REMAINDER
        LD   B,A    ; COMPUTE BIT POSITION
        LD   A,1    ; BIT POSITION 0
        JR   Z,SKIP ; SKIP IF 7-REMAINDER=0
LOOP    SLA  A      ; MOVE TO NEXT BIT POSITION
        DJNZ LOOP   ; UNTIL PROPER BIT IS SET
SKIP    LD   H,L    ; MOVE QUOTIENT TO H
        LD   L,A    ; PLACE PIXEL LOCATION INTO L
        POP  BC     ; RESTORE USED REGISTER
        RET         ; RETURN WITH H=PORT ADDRESS, L=PIXEL VALUE


GETMASK:               ; ROUTINE TO BREAK DOWN AN X ADDRESS AND COMPUTE MASKS
        LD   HL,(X1)   ; GET X VALUE FOR LEFT SIDE
        CALL CNVCORD   ; CONVERT TO PORT/PIXEL
        SLA  A         ; SHIFT PIXEL LEFT
        SUB  1         ; FORCE ALL BITS => OF PIXEL TO ON
        LD   (LMSK),A  ; SAVE LEFT MASK (CLIP MASK)
        CPL            ; FORCE ALL BITS <= OF PIXEL TO ON
        LD   (LMSKC),A ; SAVE LEFT MASK COMPLIMENT (MERGE MASK)
        LD   HL,(X2)   ; GET SECOND X VALUE (FOR RIGHT SIDE)
        CALL CNVCORD   ; CONVERT TO PORT/PIXEL
        SUB  1         ; FORCE ALL BITS => OF BIT TO ON
        LD   (RMSKC),A ; SAVE RIGHT MASK COMPLIMENT (MERGE MASK)
        CPL            ; FORCE ALL BITS <= OF BIT TO ON
        LD   (RMSK),A  ; SAVE RIGHT MASK (CLIP MASK)
        RET            ; DONE CONVERTING X1,Y1-X2,Y2 INTO USEABLE FORM

  Well, that's it.  This is all the basic information necessary to work within
the graphics environment.  For further information, or if you have any
questions or comments, contact Paul Bradshaw via CompuServe at [72177,2032], or
via U.S. Post at

Paul Bradshaw
1372 West Second Ave
Columbus OH  43212

  Thanks, and I hope you find this information interesting and helpful!
