home bbs files messages ]

Forums before death by AOL, social media and spammers... "We can't have nice things"

   comp.lang.asm.x86      Ahh, the lost art of x86 assembly      4,675 messages   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]

   Message 4,389 of 4,675   
   luser.droog@nospicedham.gmail.com to luser...@nospicedham.gmail.com   
   Re: shorter code to print a 16bit number   
   06 Aug 21 22:04:30   
   
   From: luser...@nospicedham.gmail.com   
      
   On Friday, July 30, 2021 at 12:13:00 AM UTC-5, luser...@nospicedham.gmail.com   
   wrote:   
   > I wrote this machine code to trace the LIT word in my forth interpreter.   
   > Is it possible to tighten up this code? It's trying to print a 16bit signed   
   > integer (ignoring INT_MIN) left to right with no leading zeros.   
   >   
   > This is assembly code, but I hope you'll forgive my ideosyncratic syntax.   
   > The operands map left to right to the reg and reg/mem fields into the   
   > mod-reg-reg/mem byte and the opcodes target the "to" form (direction   
   > field = 1), eg. in "MOV(,R,BX,AX)", BX is the dest, AX is the source, the   
   > R is needed to select register-to-register mode (the mod field). The empty   
   > first argument is for tweaks to the opcode which could be F (clear the   
   > direction field by subtracting 2) or BYTE (clear the word field by   
   subtracting 1).   
   >   
   > It tests each digit for zero where it jumps over the int 10h call except   
   > the last digit.   
   >   
   > It occurs to me that the DIV instruction has a mod field. So that means   
   > the divisor could be register indirect, right? like through SI or BX, maybe?   
   > Then it could probably be rolled up in a loop, right? Sorry if I'm jumping   
   > the gun asking for help before exerting the requisite effort.   
   >   
      
   Thanks for the help, everyone! Thanks to Rudy for pointing out that my   
   algorithm was malformed. I had tested it (sort of, I ran it and saw that   
   there was output), but I didn't check it against known values and so   
   didn't see the problem of suppressing inner zeros. (the numbers it   
   printed did appear weird, but I didn't properly consider and valuate   
   that clue).   
      
   John's code is so sweet and simple, I am envious of the ability to write   
   in such a way. I translated it to my syntax and then tried a few of the   
   variations alluded to earlier, like using BX to point to a table of divisors.   
   But I haven't actually implemented the LOOP instruction in the emulator   
   and the syntax I have for using labels in machine code hasn't been   
   incorporated into the emulator at all yet. So I haven't tested any of the   
   following code except to run it through CPP to check that macro expansion   
   works.   
      
   First I just translated John's code to my syntax (now with labels) and   
   lo and behold, it's super short and sweet. Exactly what I asked for.   
      
   CODE(lit,    (LIT, LODS, PUSH(AX))   
   #ifdef TRACE   
              , (PrtNum,    TEST(R,AX,AX), JGE, NotNeg-_, \   
                            PUSH(AX), MOVAX(0xE00+'-'), INT(10), POP(AX),   
   NEG(R,AX)),   
                (NotNeg,    MOVBX(10), XOR(,R,CX,CX)),   
                (NextDigit, INC(CX), XOR(DX,DX), DIV(BX), PUSH(DX), TEST(AX,AX),   
   JNZ, NextDigit-_),   
                (PutDigit,  POP(AX), ADDAX(0xE00+'0'), INT(10), LOOP, PutDigit-_)   
   #endif   
   )   
      
   First I tried doing it with a table of divisors and unrolled code to divide by   
   each   
   word in the table.   
      
   CODE(lit,    (LIT,      LODS, PUSH(AX))   
   #ifdef TRACE   
              , (stub,     sJMP, CODE-_),   
                (divisors, DW(10000), DW(1000), DW(100), DW(10)),   
                (CODE,     MOVBX(divisors), XOR(,R,DX,DX),   
                           DIV(Z,BX_), ADDAX(0xE00+'0'), INT(10)),   
                (L1,       MOV(,R,AX,DX), DIV(B,BX_),2, ADDAX(0xE000+'0'),   
   INT(10),   
                           MOV(,R,AX,DX), DIV(B,BX_),4, ADDAX(0xE000+'0'),   
   INT(10)),   
                (L2,       MOV(,R,AX,DX), DIV(B,BX_),6, ADDAX(0xE000+'0'),   
   INT(10),   
   	                MOD(,R,AX,DX), ADDAX(0xE00+'0'), INT(10))   
   #endif   
   )   
      
   Which is longer, uglier, replete with repetition.   
      
   Then I tried doing it with a table of divisors and a loop to run through   
   left-to-right.   
      
   CODE(lit,    (LIT,      LODS, PUSH(AX))   
   #ifdef TRACE   
       , (tracelit, MOVBX(divisors), XOR(,R,DX,DX), MOVCX(5)),   
         (digit,    DIV(Z,BX_), ADDAX(0xE00+'0'), INT(10),   
                    MOV(,R,AX,DX), ADDI(BX,2), LOOP, digit-_,   
                    NEXT)   
         (divisors, DW(10000), DW(1000), DW(100), DW(10), DW(1)),   
   #endif   
   )   
      
   where NEXT is a macro for jumping to the next Forth word, pointed to by SI.   
   That's pretty short, but it treats all numbers as unsigned and makes   
   no attempt to suppress leading zeros.   
      
   Then I tried without using a table and calling a procedure for each digit.   
      
   CODE(lit,    (LIT,      LODS, PUSH(AX))   
   #ifdef TRACE   
       , (tracelit, XOR(,R,DX,DX), MOVCX(5),   
                    TEST(R,AX,AX), JGE,notneg,   
                    MOVI(AX,0xE00+'-'), INT(10), NEG(R,AX), JNO, notneg,   
                    INC(R,DX)),   
         (notneg,   MOVI(BX,10000), DIV(R,BX), CALL, print,   
                    MOVI(BX,1000), MOV(,R,AX,DX), DIV(R,BX), CALL, print,   
                    MOVI(BX,100), MOV(,R,AX,DX), DIV(R,BX), CALL, print,   
                    MOVI(BX,10), MOV(,R,AX,DX), DIV(R,BX), CALL, print,   
                    MOV(,R,AX,DX), CALL, print,   
                    NEXT),   
         (print,    ADDAX(0xE00'0'), INT(10), RET)   
   #endif   
   )   
      
   It's pretty cool that adding labels also lets me have local procedures,   
   but I feel like the call instruction vs, the ADD and INT instructions   
   doesn't really save much and is slower and longer on the screen.   
   But maybe all that just means that I should be using a DOS string   
   printing function instead of a character printing function.   
      
   And also, Rod was right and this whole effort was a wholy   
   unnecessary X/Y problem. And if I just add a high-level LIT word,   
   then I can call U. to print the number be done with this snipe hunt.   
      
   On a side note, I stumbled across the source on github for DOS 1.25   
   and 2.0 and I'm browsing ASM.ASM from 1.25 to get ideas about how to   
   make an assembler with a more common syntax.   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]


(c) 1994,  bbs@darkrealms.ca