....|....|....|....|....|....|...|....|....|....|....|....|....|..... Alphanso's Assembler Assylum Assembly Language Programming for the Beginner by Super Dave Heyliger Let's Write an XCALL! Oh boy! What we all have been waiting for... XCALLS! Yes beginners, this month we will take a look at how to write a BASIC XCALL subroutine. Since you have been following the series with extreme dedication, the following presentation should be fairly straight forward. Only a few new "tricks of the trade" will be disclosed, and the subroutine examined is an easy XCALL to understand. Enough of the introductions and on with the session. Before We Begin Before you can dash madly off and create all those XCALLS you have been dreaming about, you need to know how AMOS exchanges variable information back and forth between a subroutine and a BASIC program. Quickly you grab your Assembly Language Library documentation and begin scanning for the info, right? WRONG! Why (I wish I knew the answer) does Alpha Micro contain the variable passing documentation in the back of the AlphaBASIC manual??? It's a mystery, folks. Therefore, it might be of some use to whip out your AlphaBASIC Manual if you need more details than the ones I give you in this month's submission - see chapter 18. Usually, an XCALL is created to have AMOS perform a duty from within BASIC that is difficult (if not impossible) to do with only the AlphaBASIC instructions. And, usually, the XCALL requires you to "pass" some variables into the subroutine. The XCALL then uses these variables while executing the assembled instructions of the subroutine, and finally AMOS returns these variables (possible changed) back to the BASIC program. That's basically the "behind the scenes" actions of an XCALL. In order to "pass" the desired variables back and forth, there is a pre-defined "template" that AMOS expects to decipher. An example BASIC XCALL line and the corresponding template are presented below: The format of an XCALL: {{ BASIC program}} | | XCALL {{subroutine name}},{{var 1}},{{var 2}}, {{...}} | | {{ End of BASIC program}} The expected template for AMOS: Register A0 : Indexes the "impure" variable area. Think of A0 as the .OFINI pointer you have learned to use. Register A3 : Points to the base of the Argument list. Contained in this list is all the pertainent information about the variables being passed (discussed below). Register A4 : Points to the base of the free memory area that may be used by the external subroutine as workspace. This is actually the pointer to the first word PAST the argument list. Register A5 : This is the pointer to an arithmetic stack, similar to your user stack (register A7 or the SP). Therefore, A5 MUST NOT BE USED BY THE SUBROUTINE! General Specifications of the Template The number of arguments (or variables) that you may pass in is theoretically unlimited, but for most XCALLS, this number will not be too large. When a variable is "passed", it is passed by REFERENCE. This is the fancy-pants way of saying that the VALUES of the variables are not passed into the subroutine, but the ADDRESSES of each variable is what is actually "passed". Again, register A3 points to the base of the argument list. Contents of this list are as follows: * Word one (first two bytes) contains the binary count of the number of variables actually passed. * After this first word, there is a detailed "breakdown" for EACH variable. This "breakdown" is a 10-byte description: + 1 Word containing the variable type code (only bits 0-2 are used). binary value 000 : unformatted binary value 010 : string binary value 100 : floating point binary value 110 : binary + 1 Longword containing the absolute address of the variable (remember, the variable's address has actually been "passed") + 1 Longword containing the size of the variable in bytes. In graphical form, the argument list looks something like this: <-word-><---- 10 bytes ----><---- 10 bytes ---->< --\ \---> +-------+---------------------+---------------------+---\ \----+ | count | type, address, size | type, address, size | - - - -| +-------+---------------------+---------------------+---\ \----+ ^ "var 1" "var 2" ^ | | \ / A3 A4 XCALL USERNO The following XCALL is a great example for the beginner learning how to create XCALLS. It is called USERNO - "User Number". What this small XCALL will do is to return your particular "JOB" number, i.e., if you are listed third whenever you do a SYSTAT, then USERNO will return to your BASIC program the value "3". The following is the listing for USERNO: ; this program searchs the job table for the user's job and returns the line ; number in the table of the user's job. syntax for calling from basic is ; XCALL USERNO,FFFF ; where FFFF is a floating point number. ; written by Robert Kurz, 1547 Cherrywood Drive, Martinez, CA 94553 ; donated by Robert Kurz & Doug Shaker ; ; Slightly modified for AAA 04-23-86 ; SEARCH SYS ;Collecting monitor calls SEARCH SYSSYM OBJNAM USERNO.SBR ;notice .SBR extension USERNO: PHDR -1,0,PH$REE!PH$REU ; Re-entrant and re-usable CMPW (A3)+,#1 ; number of arguments must be 1 BNE SYNTAX ; else print out syntax error msg. MOVW (A3)+,D5 ; type of argument ANDW #7,D5 ; mask off "type" (bits 0..2) CMPW D5,#4 ; must be floating point BNE SYNTAX ; else syntax error message MOV (A3)+,A2 ; address of variable to A2 JOBIDX A1 ; get the address of our job MOV JOBTBL,A0 ; get the address of the jobtable CLR D1 ; and clear the "count" LOOP: INC D1 ; increment job number CMP A1,(A0)+ ; compare this entry to our job BNE LOOP ; if not the same try the next one FLTOF D1,@A2 ; convert to floating point RTN ; all done SYNTAX: TYPECR RTN END Line-by-Line, Byte-by-Byte SEARCH SYS SEARCH SYSSYM These two lines should look VERY familiar. What the SEARCH does is grab all of the monitor calls located in the above two .UNV files on DSK0:[7,7]. We have discussed this portion of the code thoroughly. OBJNAM USERNO.SBR Although OBJNAM is another "thoroughly" discussed topic, notice that USERNO is informing AMOS that what it is to create, once assembled, is USERNO.SBR - not the default value of assembly, USERNO.LIT! Now you can see why I stressed placing the expected assembled file name even on a .LIT creation: consistent formating. USERNO: PHDR -1,0,PH$REE!PH$REU USERNO: is a label (not really necessary, but it acts as a "header" for the source code making it easier to read). Then the usual Program HeaDeR. Again, if a file is to be LOADed into system memory, AMOS will check to make sure that the program is re-entrant and re-useable by consulting the header. This is a good programming practice I have always stressed. CMPW (A3)+,#1 CoMPare the Word pointed to by A3 and see if whatever A3 points to is a "1", and at the same time, point A3 to the first byte PAST the word we are looking at. Remember, the () means "look at what the register is pointing to", and the "+" means "increment the register by the appropriate amount" - due to the Compare WORD, this amount is 2 bytes. Since the first word is the count of the variables used by USERNO, what this line is effectively doing is checking to make sure that one and only one variable was passed. BNE SYNTAX Branch if Not Equal to the label SYNTAX. Upon every Compare, the next instruction is usually a "poll" to the status register checking to see the results of the Compare. If the above Compare did NOT produce an "equal" result, then the next instruction would be at the line SYNTAX. MOVW (A3)+,D5 MOVe the Word pointed to by register A3 into the data register D5 and at the same time increment the pointer A3 by the appropriate amount. Since A3 was previously incremented a few instructions ago, A3 is now pointing to the first byte of the "variable type". This instruction has the effect of moving the variable type word into the register D5 where some future work will take place. A3 is then incremented to point to the first byte of the longword that contains the address of the variable that was passed in to USERNO (see the diagram above). ANDW #7,D5 "AND" the Word inside register D5 with the value of 7. What AND does is "copy" the BITS inside a register, the bits copied depends on the "#" value ANDed. AND will copy only the bits that are "on" from the "#" (7 in this program). Bits that are "off" from the number causes AND to copy zeros. 15..............0 <-- bit location indicators | | V V 0000000000000100 <-- register D5's contents (hopefully) AND 0000000000000111 <-- bit representation of #7 ------------------------- 0000000000000100 <-- AND copies the bits at location 0..2 if "#" is a 7. D5 now contains this value. The AND was performed to isolate the bits that contain the variable type definition. D5 now contains the type definition bits. CMPW D5,#4 CoMPare the Word in register A2 with the value of four. This is to check to make sure that the variable type passed into the subroutine was indeed a floating point variable. Any value other than a four would cause problems. BNE SYNTAX Again, the status register was set according to the CoMPare Word above. The status register should have the zero flag SET if the comparison was equal. If the comparison was NOT equal, then Branch to the label SYNTAX. MOV (A3)+,A2 Move the value pointed to by A3 into the register A2, and then increment A3 by the appropriate amount. The value moved into A2 is the address of the floating point variable that was passed into the subroutine. The incrementation on register A3 is +4 (due to the MOVe for MOVe longword). A3 now points to the size of the variable (not used in this subroutine). JOBIDX A1 The monitor call JOBIDX will return the base address value of YOUR JOBs JCB. Remember, each JOB on the system has a Job Control Block that AMOSL.MON maintains to monitor your JOBs every move. Register A1 now contains the pointer to the beginning of this block (an actual location inside system memory). MOV JOBTBL,A0 We have seen this MOVe often. What this MOVe does is move into the register A0 a pointer that points to the base of the JCB address table (see the January .LOG submission). We will be scanning the JCB pointer table looking for a match between the table pointer and your pointer stored in register A1, counting as we go. CLR D1 D1 will be used as a "counter". Each time we scan the JBC table and it doesn't match the base address of YOUR JBC pointer, the counter will be incremented. CLR makes sure that no bits are set inside the register. LOOP: INC D1 LOOP is a label that the subroutine will jump back to each time a match is not found between JCB addresses. It is here where we INCrement the counting register before each JCB to JCB comparison. CMP A1,(A0)+ CpMPare the longword that is contained in A1 (YOUR JCB) to the longword pointed to by A0 (the sequential JCB addresses contained in the JOBTaBLe), and then increment A0 by the appropriate amount (+4). Again, we are looking for a match between JCB addresses. A0 will sequentially scan down the list of JCBs looking at one JCB for each time through the LOOP. BNE LOOP If there is no match, Branch if Not Equal to the label LOOP. FLTOF D1,@A2 We have found a match, so convert the longword contained in D1 (the count) to a two's complement floating point number (to match the variable type passed in), and place this number into the memory location pointed to by register A2. This just so happens to be the memory location of the floating point variable we passed in to USERNO (from the instruction above MOV (A3)+,A2). RTN ReTurN to BASIC upon finding the JOBs user number. SYNTAX: TYPECR SYNTAX: contains the "error message" that the subroutine will use whenever incorrect usage of USERNO is called. All the instructions after the BNE SYNTAX up to this line are bypassed if the branch takes place. RTN ReTurN to BASIC after typing out the error message. END End of the source code. Wrapping Up This subroutine only used one variable. If more variables are used, the same pattern as the one above would be employed, but you have to keep track of the bytes pointed to by A3 and make sure you are changing values in the addresses of the variables and not making changes to the variable type bytes etc. Examine other subroutines found on the Network [100,52], and then try to create your own simple subroutine and see if you can make it work. Hope this series was of some help, and call me if you have questions. Bye! .