;+ ; ; 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 ,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,)/,(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,)/,(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,)/,(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 ; POPREG ; ;- .MACRO PUSHREG REGS ;*Push registers on stack; .IRP REG, ;*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, ;*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 ,<%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 ,<%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,, - PUSHREG MOVC3 LENGTH,SRC,DEST .IIF IDENTICAL PRESERVE,, - POPREG .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,, PUSHREG 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,, POPREG .ENDC ; ; Here, the length was not a literal, so just generate a MOVC3 ; instruction, preserving registers if necessary. ; .IF_FALSE .IIF IDENTICAL PRESERVE,, PUSHREG MOVC3 LENGTH,SRC,DEST ;Just use a MOVC3 .IIF IDENTICAL PRESERVE,, POPREG .ENDC .ENDM MOVCX ;*End of MOVCX macro