Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!ucbvax!ICASE.EDU!csrobe From: csrobe@ICASE.EDU (Charles S. [Chip] Roberson) Newsgroups: comp.sys.atari.st Subject: A Lesson Learned Message-ID: <8903202254.AA00303@icase.edu> Date: 20 Mar 89 22:54:30 GMT Sender: daemon@ucbvax.BERKELEY.EDU Organization: The Internet Lines: 75 Greetings fellow psycho-programmers! Well, I just learned a lesson today and I thought I pass it on to any others who haven't learned this, yet. It only blew a day and a half of my time. Below is a pretty slick fprintf() statement from some of the UUPC/ST code munged enough to simplify this example. Looking at it, I was sure that it should work. I mean, why not? It's pretty straight forward -- pass fprintf() 3 arguments: stderr, the appropriate format string, and finally the desired value to print. Then once the value is passed, increment. Right? Wrong! -------------------------------------------------------------------------- #include #include unsigned char charlie = 'I'; void oops() { unsigned char *cp; cp = charlie; fprintf( stderr, isprint(*cp)? "[%c]":"[%02x]", *cp++ ); } -------------------------------------------------------------------------- MWC passes parameters in reverse order. That means that the "*cp++" gets evaluated BEFORE the "isprint(*cp)..." stuff. So, everytime fprintf will get passed the approprate character, but isprint is going to look at whatever is in the next data location. [In my case that was usually garbage in an unused portion of a character buffer!] For those of you who need to see the nasty stuff, here is a segment of the MWC assembly code. It should explain everything. Especially, note the lines that I marked with an '*'. [Thanks to MWC's db pgm!] -------------------------------------------------------------------------- movea.l -8(a6), a0 ;; move *cp into a0 for passing to fprintf() * addq.l $1, -8(a6) ;; increment cp as specified in fprintf arg clr.w d0 ;; clean out cruft in d0 move.b (a0), d0 ;; load value destined for passing to fprintf() move d0, -(a7) ;; push it on the stack * movea.l -8(a6), a0 ;; move *cp into a0 for isprint() check clr.w d0 ;; clean out some more cruft move.b (a0), d0 ;; stash *cp in d0 for testing swap.w d0 ;; toss it into the upper word clr.w d0 ;; clean out the lower word swap.w d0 ;; move *cp value back into lower word movea.l d0, a0 ;; prep for isprint() adda.l $_ctype_+1, a0 ;; mung it a little move.b (a0), d0 ;; mung it some more ext.w d0 ;; ok, it's munged andi $171, d0 ;; does it map to printable set? beq.s L10006 ;; nope, go print it in hex pea L37 ;; yes, use character format in fprintf bra.s L10007 ;; go finish pushing args L10006: pea L38 ;; use, hexidecimal format in fprintf L10007: pea _stderr_ ;; finally, pass 1st arg to fprintf jsr fprintf_ ;; go do it! -------------------------------------------------------------------------- The moral of this story? Well, as my Theory of Prog. Lang prof would say, "That's just another insecurity of C!"; to which he would add "Ada is a much better language." Well, I guess I would say "Hey! Be careful out there." Seriously, this is one side-effect that I hadn't thought about. How do other ST compiler's pass their parameters? So much for portability, eh? Does anybody know how the dpANS standard would affect this, if at all? I can't seem to remember anything in it that would. oh well. cheers, -c