Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!utgpu!water!watmath!clyde!rutgers!ames!ucbcad!ucbvax!decvax!tektronix!tekcrl!terryl From: terryl@tekcrl.UUCP Newsgroups: comp.unix.questions Subject: Re: Teaching Assembler on VAX (BSD 4.3) (LONG) Message-ID: <1658@tekcrl.TEK.COM> Date: Tue, 19-May-87 21:11:51 EDT Article-I.D.: tekcrl.1658 Posted: Tue May 19 21:11:51 1987 Date-Received: Thu, 21-May-87 04:23:04 EDT References: <7447@brl-adm.ARPA> Reply-To: terryl@tekcrl.tek.com Organization: Tektronix, Inc., Beaverton, OR. Lines: 186 Just thought I'd add my $0.02 worth (it seems everyone else has (-:). I've been doing heavy systems work in the software realm on various flavors of 680X0 based workstations (replace X with one of 0, 1, or 2) for the last 6 years. In doing heavy systems work, I've had to write a lot of assembler for things that we just couldn't do with a C compiler (any C compiler). I am fluent in both the so-called "Motorola" assembler format, and the so-called "MIT" assembler format. I can read (and write)both with absolutley no problems at all. I know both VAX and PDP 11 assembler, and used to know IBM 1130 and 360 assembler (thank god I haven't had to work on an IBM system in over 10 years!!!(-:). I've dabbled in 8080 assembler when I was an undergrad; I can recognize National 16XXX(or 32XXX, or whatever they've named it recently), but am not as fluent in it as I am with the 680X0; so in the last 15 years I've had quite a bit of experience with various assemblers for different architectures. Personally, I don't like to use macros in assembler, especially macros that generate multiple lines of real assembler code. Sure, it makes writing assembler code much easier, and if the writer choses good mnemonic names, then conceptually it is easier to understand what the code is trying to do. However, it is a real pain in the assembly (-: to debug and maintain that code. Part of the problem is that many Unix[1]-based assemblers have no macro capability at all, so in order to get at least a semblance of a macro capabil- ity, cpp is used. The problem with cpp, bless its little heart, is that it strips out ALL the newlines of multi-line macros. So a listing of the source (if you're even lucky to be able to get an assembler listing, again a capabil- ity many Unix-based assemblers don't have), is practically useless for these multi-line macros. I've seen some assembler macros that have generated 15-20 lines of real code, and even though the names were mnemonic enough that I knew exactly what the code was supposed to do, I still didn't like it. The code was just too difficult to debug. A very good example of the use (and misuse) of macros in assembler is writing loops. Loops like clearing a block of memory, or loading some hardware with a regular pattern (usually the index of the loop, maybe with some shif- ting and or'ing in of some constant bits). These kinds of loops are extremely common in the system world, with the loop count (and address, if appropriate) as arguments to the macro. The problem is making sure the arguments to the macro are correct. You want to make the macros general enough to accept just about any argument (for example, in clearing memory, in a virtual-memory based system anyways, you always clear in terms of the hardware page size, but it is desirable to make the macro general enough so that you can clear any block of memory that is any size). Since actual code speaks louder than words, let me use my clear-memory example. For simplicity sakes, let us assume the use of a 68020 processor(which is what I'm most familiar with, anyways). First, the general macro to clear a block of memory. Also, let's assume that we'll always clear 32 bit quantities (known as a long-word in 680X0 jargon), and we'll also assume that the count is a 32 bit count, but with at most 16 bits of precision in an UNSIGNED twos-complement format (I know, I'm cheating, but in the systems world, these are valid assumptions). Let us also assume our assembler has no macro capability, so we are forced to use cpp (definitely NOT cheating, and probably true of most Unix-based assemblers). I will use the Motorola assembler format. First, the macro: #define CLEAR(count, address, reg, areg) \ move.l count,reg /* Load count */ \ beq.s 2$ /* Bail out if zero count */ \ movea.l address,areg /* Now get the address */ \ subq.l #1,reg /* Subtract one (for dbra) */ \ 1$: \ clr.l (a0)+ /* Clear a long-word */ \ dbra reg,1$ /* Go back and do it again */ \ 2$: For all of you people out there who don't know 680X0 assembler, I'll (briefly) explain what is happening. The macro loads the count of the number of long-words to clear into a data register, and if this count is zero, then do nothing. If the count is non-zero, then get the address of the start of the block of long-words to clear. Decrement the count of the number of long- words to clear by one(for an explanation of why this is necessary, wait a few lines longer....). Clear a long-word of memory, using what is known as the auto-increment addressing mode (see just about any assembler documentation on how this particular addressing mode works). I will give just a brief explanation on the dbra instruction. The dbra instruction is a specific form of the dbcc instruction on the 680X0, where the cc part of dbcc can specify one of sixteen conditions for terminating the loop. Specifically, what the dbra instruction does is take the 16 bit SIGNED twos-complement value in the first operand (which has to be a data register), and subtracts 1 from it. Then it takes the new value of the first operand and compares it -1 (that's a 16 bit SIGNED comparison), and if the new value is not -1, then branch to the label specified as the second operand. If the new value is -1, then continue with the next instruction following the dbra instruction. To understand my cheating comment, you have to understand how twos- complement arithmetic works, both in signed and unsigned arithmetic. I'll assume most of you do, and if you do, then my comment should be clear. Now, let's see a couple of examples of the use of the CLEAR macro: First, a routine to clear a page of memory NBPG bytes long: /* * Clear a VIRTUAL page of memory NBPG bytes long. * Called from C by: * * pageclear(virt_addr); */ pageclear: CLEAR(#(NBPG/4), 4(sp), d0, a0) rts Now, a routine to clear a block of memory: /* * Clear a block of memory. * Called from C by: * * bzero(virt_addr, count); * unsigned count; * * NOTE: Count must be greater than 4, and count must be less than * 262144 (65536*4), and must be a multiple of 4. */ bzero: move.l 8(sp),d0 lsr.l #2,d0 CLEAR(d0, 4(sp), d0, a0) rts A little explanation is in order here. First, I am assuming an unsigned count in a specific range. Second, I have to convert a byte count into a long- word count. That's what the first two instruction before the CLEAR macro call are for(done in UNSIGNED arithmetic). But having a count be in a specific range is not too nice, so let's provide a routine that can take an arbitrary count. /* * Clear a block of memory (arbitrary size). * Called from C by: * * blkclr(virt_addr, count); * unsigned count; * * NOTE: count must be greater than 4 (but no upper limit except for * the 32-bit architecture limit), and must be a multiple of 4. */ blkclr: move.l 8(sp),d0 lsr.l #2,d0 blkclr1: CLEAR(d0, 4(sp), d0, a0) subi.l #0x10000,d0 bge.s blkclr1 rts So now you've seen an example of a macro in assembler, and a couple of examples of its use. So why don't I like macros in assembler??? TYPOGRAPHICAL ERRORS. I am not the world's best typist (probably one of the worst (-:). If, for example, in the macro call to CLEAR used in pageclear I forgot the # (which is how you specify an immediate operand in the assembler we use for the 680X0), then the routine would have been assembled without any errors (it is syntactically correct according to the assembler), but it would have not been conceptually correct, and a quick look at the use of the macro could have very well not revealed any obvious errors (I'm famous for that (-:). Another reason I don't like using macros is that I just can't remember the order of the arguments, and then I have to go look at the actual definition of the macro, which sometimes can take a lot of time (damn, where did I define that stupid thing??? Which include file did I put it in???). Sometimes, it'll take me longer to find the actual definition of the macro than it would have if I had just coded the stupid thing up by hand in the first place. Another reason I don't like using macros is when it actually comes down to debug time (If I had a dollar for every time I've had to debug system code with our own home-grown debugger, using breakpoints, single stepping, etc. I could probably retire a rich man by now; no (-: here). Again, I have to go find the definition of the macro, and then find the use of it. So those are my reasons for not liking macros in assembler. I'm sure I could come up with more(actually, I can. In my undergrad days, learning PDP assembler, we had a REAL macro assembler. This macro assembler could do EVERYTHING, including recursive macros. One of our assignments was to write something using recursive macros. You want to talk about a nightmare!!!) Eagerly awaiting your comments (and flames (-:) Terry Laskodi of Tektronix