;+
;
;  Macro:       MOVS
;
;  Author:      Hunter Goatley, goathunter@WKUVX1.BITNET
;               May 25, 1992
;
;  Functional description:
;
;       The MOVS macro copies a given string to a given destination
;       address.  The length of the string can be optionally copied
;       to the DESTLEN parameter.
;
;       This macro is useful when you want to copy small ASCII strings
;       to a buffer, without creating the string in a data area.  For
;       example:
;
;               MOVS    <SYSTEM>,JIB$T_USERNAME+1(R3),R0
;               MOVB    R0,JIB$T_USERNAME(R3)
;
;       The sequence above would generate the following instructions:
;
;               PUSHL   R0
;               MOVAB   JIB$T_USERNAME+1(R3),R0
;               MOVL    #^A/SYST/,(R0)+
;               MOVW    #^A/EM/,(R0)+
;               POPL    R0
;               MOVL    #6,R0
;               MOVB    R0,JIB$T_USERNAME(R3)
;
;-
        .MACRO  MOVS    STRING,DEST,DESTLEN

        ;
        ;  Initialize the work symbols.
        ;
        ..MOVS_LENGTH = %LENGTH(STRING)         ;Get the string length
        ..MOVS_POS = 0                          ;Offset into string starts at 0

        ;
        ;  Count the number of longwords, words, and bytes in the string.
        ;
        ..MOVS_L_CNT    = <..MOVS_LENGTH / 4>
        ..MOVS_W_CNT    = <..MOVS_LENGTH / 2> & 1
        ..MOVS_B_CNT    = <..MOVS_LENGTH> & 1

        PUSHL   R0                              ;Save R0 for work
        MOVAB   DEST,R0                         ;Destination address -> R0
        ;
        ;  For each longword that can be moved, generate an instruction
        ;  like the following:
        ;               MOVL    #^A/TEXT/,(R0)+
        ;
        .REPEAT ..MOVS_L_CNT
        MOVL    #^A/%EXTRACT(..MOVS_POS,4,<STRING>)/,(R0)+
        ..MOVS_POS = ..MOVS_POS + 4             ;Point to next bytes
        .ENDR

        ;
        ;  Now do any words that can be done.
        ;
        .REPEAT ..MOVS_W_CNT
        MOVW    #^A/%EXTRACT(..MOVS_POS,2,<STRING>)/,(R0)+
        ..MOVS_POS = ..MOVS_POS + 2             ;Point to next bytes
        .ENDR

        ;
        ;  Now do the byte, if there is one.
        ;
        .REPEAT ..MOVS_B_CNT
        MOVB    #^A/%EXTRACT(..MOVS_POS,1,<STRING>)/,(R0)+
        .ENDR

        POPL    R0                              ;Restore contents of R0

        ;
        ;  Leave length in DESTLEN, if specified.
        ;
        .IIF    NOT_BLANK, DESTLEN,     MOVL    #..MOVS_LENGTH,DESTLEN

        .ENDM   MOVS                            ;*End of MOVS macro



;+
;
;  Macro:       PUSHREG & POPREG
;
;  Author:      Hunter Goatley, goathunter@WKUVX1.BITNET
;               May 25, 1992
;
;  Functional description:
;
;       PUSHREG and POPREG accept as inputs a list of registers whose
;       contents are pushed onto and popped off of the stack using
;       PUSHL and POPL instructions.  This is faster than using
;       the PUSHR and POPR instructions.
;
;  WARNING: the list of registers passed to POPREG must be in the exact
;           opposite order from the list passed to PUSHREG.  For example:
;
;               PUSHREG <R0,R1,R2>
;               POPREG  <R2,R1,R0>
;
;-
        .MACRO  PUSHREG REGS                    ;*Push registers on stack;
        .IRP    REG,<REGS>                      ;*For each register given;
        PUSHL   REG                             ; Save contents of REG;
        .ENDR                                   ;*Loop until no more;
        .ENDM   PUSHREG                         ;*End of PUSHREG macro;

        .MACRO  POPREG  REGS                    ;*Pop registers off stack;
        .IRP    REG,<REGS>                      ;*For each register given;
        POPL    REG                             ; Restore contents of REG;
        .ENDR                                   ;*Loop until no more;
        .ENDM   POPREG                          ;*End of POPREG macro;

;+
;
;  Macro:       MOVCX
;
;  Author:      Hunter Goatley, goathunter@WKUVX1.BITNET
;               May 25, 1992
;
;  Functional description:
;
;       MOVCX is designed to serve as a functional replacement for MOVC3.
;       Because MOVC3 is a relatively expensive instruction on most VAX
;       processors, it is desirable to use a combination of MOVQ, MOVL,
;       MOVW, and MOVB instructions when copying small strings.  MOVCX
;       will automatically determine if the given length parameter is
;       a literal value (known at assembly-time) and, if so, will generate
;       the appropriate MOVx instructions to copy the string.  If the
;       string length is not a literal, or is greater than 32 bytes, a
;       MOVC3 is generated.  In both cases, the macro will, by default,
;       preserve the registers used as scratch registers.
;
;       NOTE: this macro also calls the PUSHREG, POPREG, and MOVCX_GEN_MOVX
;       macros.  It also defines a number of symbols, all prefixed by the
;       string "..MOVCX_".
;
;  Formal parameters:
;
;       LENGTH          The length of the string
;       SRC             The address of the first byte of the source
;       DEST            The address of the first byte of the destination
;       PRESERVE        If YES (the default), all registers are preserved
;       LIMIT           MOVC3 size limit (default 32)
;
;       If PRESERVE=NO and the length is a literal, R0 and R1 are used as
;       work registers.
;
;  Calling sequence:
;
;       MOVCX   #STRING_LEN,SOURCE,DESTINATION
;       MOVCX   (R3),(R4),(R5)
;       MOVCX   #STRING_LEN,SOURCE,DESTINATION,PRESERVE=NO      ;Don't save regs
;
;-
        .MACRO  MOVCX   LENGTH,SRC,DEST,PRESERVE=YES,LIMIT=32
        ..MOVCX_X = 0                           ;Initialize the work variable

        ; Get the type of the length parameter.  The type returned is
        ; either an 8-bit or 16-bit value.  Bits 0--3 represent the
        ; register used; bits 4--7 represent the addressing mode.  The
        ; addressing mode returned by .NTYPE isn't exactly the same as
        ; the normal addressing mode encoding. Specifically, a literal
        ; is specified by bits 4--7 being all zero (instead of holding
        ; one of the values 0--3).
        ;
        .NTYPE  ..MOVCX_LEN_TYPE,LENGTH         ;Get the addressing mode

        ; Since we're only interested in bits 4--7, shift the type
        ; returned right 4 bits, then AND it with hexadecimal F to
        ; clear out the high byte in case a 16-bit value was returned.
        ;
        ..MOVCX_LEN_TYPE = <..MOVCX_LEN_TYPE@-4>&^XF


        ; Now check to see if the addressing mode is short literal
        ; (value is 0) or immediate (value is 1).

        .IF LE <..MOVCX_LEN_TYPE-1>
                ;
                ;  Because it's a literal, the "#" should be present.
                ;  Let's be safe, though, and check for it before using
                ;  the length as a number (in symbol ..MOVCX_LENGTH).
                ;
                .IF IDN <#>,<%EXTRACT(0,1,LENGTH)>
                        ..MOVCX_X = %LENGTH(LENGTH)
                        ..MOVCX_LENGTH = <%EXTRACT(1,..MOVCX_X,LENGTH)>
                .ENDC
                ;
                ;  If "S^" is present (short literal), remove it.
                ;
                .IF IDN <S^#>,<%EXTRACT(0,3,LENGTH)>
                        ..MOVCX_X = %LENGTH(LENGTH)
                        ..MOVCX_LENGTH = <%EXTRACT(3,..MOVCX_X,LENGTH)>
                .ENDC
                ;
                ;  If "I^" is present (immediate mode), remove it.
                ;
                .IF IDN <I^#>,<%EXTRACT(0,3,LENGTH)>
                        ..MOVCX_X = %LENGTH(LENGTH)
                        ..MOVCX_LENGTH = <%EXTRACT(3,..MOVCX_X,LENGTH)>
                .ENDC

                ;
                ;  At this point, ..MOVCX_LENGTH holds the length of the
                ;  string to copy.
                ;
                ;  If we exceed the length limit (32, by default), just
                ;  generate a MOVC3 instruction (and save regs if desired).
                ;
                .IF GT <..MOVCX_LENGTH - LIMIT>

                        .IIF IDENTICAL PRESERVE,<YES>, -
                                PUSHREG <R5,R4,R3,R2,R1,R0>
                        MOVC3   LENGTH,SRC,DEST
                        .IIF IDENTICAL PRESERVE,<YES>, -
                                POPREG  <R0,R1,R2,R3,R4,R5>

                .IF_FALSE                       ;Didn't exceed LIMIT
                        ;
                        ;  Now count the number of quadwords, longwords, words,
                        ;  and bytes in the string.
                        ;
                        ..MOVCX_Q_LEN = <..MOVCX_LENGTH / 8>
                        ..MOVCX_L_CNT = <..MOVCX_LENGTH / 4> & 1
                        ..MOVCX_W_CNT = <..MOVCX_LENGTH / 2> & 1
                        ..MOVCX_B_CNT = <..MOVCX_LENGTH> & 1

                        ;
                        ;  Save registers, if necessary.
                        ;
                        .IIF IDENTICAL PRESERVE,<YES>,  PUSHREG <R0,R1>

                        MOVAB   SRC,R0          ;Move SRC address to R0
                        MOVAB   DEST,R1         ;Move DEST address to R1

                        .IF NE ..MOVCX_Q_LEN    ;*If there are any of this type
                        .REPEAT ..MOVCX_Q_LEN   ;... generate that number of
                        MOVQ    (R0)+,(R1)+     ;... MOVQ instructions
                        .ENDR                   ;*Loop until no more MOVQs
                        .ENDC                   ;*End of .IF

                        .IIF NE, ..MOVCX_L_CNT, MOVL    (R0)+,(R1)+
                        .IIF NE, ..MOVCX_W_CNT, MOVW    (R0)+,(R1)+
                        .IIF NE, ..MOVCX_B_CNT, MOVB    (R0)+,(R1)+

                        ;
                        ;  Restore the registers, if they were saved.
                        ;
                        .IIF IDENTICAL PRESERVE,<YES>,  POPREG  <R1,R0>
                .ENDC
        ;
        ;  Here, the length was not a literal, so just generate a MOVC3
        ;  instruction, preserving registers if necessary.
        ;
        .IF_FALSE

                .IIF IDENTICAL PRESERVE,<YES>,  PUSHREG <R5,R4,R3,R2,R1,R0>

                MOVC3   LENGTH,SRC,DEST         ;Just use a MOVC3

                .IIF IDENTICAL PRESERVE,<YES>,  POPREG  <R0,R1,R2,R3,R4,R5>

        .ENDC

        .ENDM   MOVCX                           ;*End of MOVCX macro

