Coding and testing the procedure nibble_to_ASCII

In the post "nibble_to_ASCII procedure", I ended up with two different possible implementations for this procedure; here I summarize how I made and tested both of them. In the end, I write my considerations about the two different options.

nibble_to_ASCII with "add-like" algorithm

I began to code "nibble_to_ASCII" with the "add-like" algorithm from the memory address at SEG:0100 (Fig. A).

Best practice with DEBUG.EXE
Fig. A

Since this is a service procedure, the first thing I set was a call to the memory address SEG:0110 where I wrote indeed the real procedure. I did so for the following reason. nibble_to_ASCII is a service procedure called by stack_to_string and I wanted to test that both the call into the procedure and the return to the caller were working. The command p in DEBUG.EXE executes all instructions starting from a CALL instruction (being it the beginning of a procedure) until the next RET instruction, so I need an initial CALL instruction to be used in combination with the command p in order to trigger the test.

As I wrote the JS instruction at address SEG:0116 I couldn't know the real target memory address of the jump (since this is a jump ahead in a portion of the code that has not been written yet) so I decided to jump to the same memory location of the instruction itself (marked with red colour) knowing that I had to fix this line later on. Few lines of code later, I found my destination of the jump so I could fix it (marked in green colour).

Best practice with DEBUG.EXE
Fig. B

In Fig. B I did a visual check that everything was all right before start testing. IP was pointing at 0x0100 where it finds the instruction call (in purple); the CALL instruction was pointing at 0x0110 where the actual procedure begins (yellow); the JS was pointing to the correct memory location (green). After the visual check, I moved on to testing.

Best practice with DEBUG.EXE
Fig. C

In Fig. C , I intended to start with an initial value of 0xAAAA in AX. If everything was fine it should convert it into 0xAA41 since the procedure should only look at the lowest nibble (0xA in this case) and convert it into its corresponding ASCII code (0x41) and place the result in register AL discarding all the rest. I set AX=AAAA (red) and I ran the code using t (trace CPU instruction one at a time). The call worked fine (orange). Bit masking worked fine (yellow). But from now on the code wasn't performing as I expected.

Best practice with DEBUG.EXE
Fig. D

in Fig. D you see the problem. I was adding a value of 0x48 but I meant a decimal value of 48 (which is 0x30) and I was comparing against 0x58 but I meant a decimal value of 58 (which is 0x3A). This is a common problem that rises when manufacturing the code by hand. In DEBUG.EXE there is no ambiguity: all numbers you see are only hexadecimal and I forgot to convert. Nevertheless, I saw that the code was fine in its logic: the CMP instruction was affecting the flags (blu) and the JS was jumping since the sign flag was set1 (purple), but, of course, the result of the conversion was wrong.

Best practice with DEBUG.EXE
Fig. E

In Fig. E you see my interventions to correct the code changing the 2 bytes (red square around them) and then I restarted testing this time successfully (Fig. E and Fig. F).

Best practice with DEBUG.EXE
Fig. F

I repeated the test a second time (Fig. G) but this time I used the command p (DEBUG.EXE proceeds to execute all instructions inside any CALL, LOOP, REP-string and INT). AX was already loaded with 0x0041 so I expect that the code looks at only the 0x1 and converts it into the ASCII code 0x31. Before performing the test I had to adjust IP (purple), then I pressed [p] and [ENTER] with a successful result (orange).

Best practice with DEBUG.EXE
Fig. G

nibble_to_ASCII with "look-up table" algorithm

After the first option with the "add-like" algorithm was coded and tested, I set to code and test the second option with the "look-up table" algorithm.

Best practice with DEBUG.EXE
Fig. H

I began coding it starting at 0x0120 since the previous line was already occupied (Fig. H). When I had to point BX to the beginning of the translation table I didn't know which value to write here (since the table was still going to be written) so I used an arbitrary value (0x1122 in red) keeping in mind that I would have to fix it later on. Since the code ends at byte 0x0128 I could start to write my translation table aligned with the beginning of the following paragraph 0x0130 (yellow).

Best practice with DEBUG.EXE
Fig. I

As usual, I performed a visual check (Fig. I) observing that I could point BX to the correct value of 0x0130 (green) but also that I forgot to close the code with a return instruction so I fixed this too.

Best practice with DEBUG.EXE
Fig. L

In Fig. L you see my preparation for testing (I wanted to repeat the same test as I did on the previous version of the code): I adjusted the CALL to point the new code (purple); I adjusted IP (blue); I loaded AX with a starting value (green).

Best practice with DEBUG.EXE
Fig. M

In Fig. M there is the last visual check before running the test. Here you see: the calling code in purple with its pointing byte corrected marked with a red square (check the difference of this byte with the one in Fig. I); the "add-like" algorithm starting at 0x0110 in blue; the new "look-up table" algorithm starting at 0x0120 with its own translation table starting at 0x0130 in light green with BX loaded correctly (green in disassembly view and light green with a red square in hexadecimal dump view).

Best practice with DEBUG.EXE
Fig. N

In Fig. N I started testing. The CALL was ok (purple). The bit masking was ok (blue). BX pointing was ok (green). The byte translation was ok (yellow).

Best practice with DEBUG.EXE
Fig. O

In Fig. O I performed the second test using the p command instead of the t one. You still see the RET of the previous test (orange). After that, you see that I had to adjust IP (red) and then use the command p with a positive result (purple).

Conclusion

With two working codes I had to pick one for the rest of the AP-PROBE and I would like to discuss with you my reasoning about it. Looking at the two codes from outside, that is to say, from the perspective of the caller-code, they are 99.9% equivalent2.

Best practice with DEBUG.EXE
Fig. P

In Fig. P we look at the two codes one above the other and we can see their difference in size. The first is 11 bytes long and the second is 25 bytes long including the translation table for a total difference of 14 bytes. Now 14 bytes is nothing but if you put it in relation with the available space for coding the prospective changes. Out of 512 bytes, I used already 2 for the boot signature, 127 bytes for the formatted string and 27 for the exit message. The consequence is that I have only 356 bytes of available space. 14 bytes are then 3.9% of extra free space that I may not have towards the end. So the difference in the size of the code is something that I was keeping in mind for my decision, but what finally made me decide that the "add-like" algorithm (the blue one) was the one for me is the fact that this code is memory location independent. Which means that it works wherever location it is in memory meanwhile the "look-up table" algorithm (the light green one) needs to correct the pointing of BX every time we move it to reflect its current memory location. This is a huge difference for me: it means that the first code is really a tool that I can reuse every time I need and put everywhere in memory and it will always work, meanwhile, the second one, always needs to be adapted. So I decided to keep the "add-like" algorithm version of the nibble_to_ASCII procedure, but please tell me in the comment what is your opinion about it. Would you go for the "add-like" or the "look-up table" version of the code?

Best practice with DEBUG.EXE
Fig. Q

In Fig. Q you can see that I piled up my preferred version of the procedure to the rest of the code, after that I saved it again in my temporary file INWORK.BIN (similarly to Fig. G of the post "Alien Planet Probe: the manufacturing and test phase").



  1. A sign flag = 0 means "unset" and is represented in DEBUG with NG that stands for NeGative, meanwhile a sign flag = 1 means "set" and is represented with PL that stands for PLus. [click back]
  2. The "add-like" algorithm operates on AL meanwhile the "look-up table" algorithm operates on AX. It means that with AX = 0xAAAA the "add-like" algorithm returns AX = 0xAA41 and the "look-up table" algorithm returns AX = 0x0041. Additionally, the "look-up table" algorithm uses the register BX meanwhile the "add-like" algorithm doesn't use anything else more than AL. These are very few differences but I learned with the experience that, at machine level, any detail counts even the smallest one.
    You can change the code of the "look-up table" algorithm just a little bit to have the same behaviour concerning the register AX (use AND AL, 0xF in both procedures instead of AND AX, 0xF in the "look-up table" one) but the difference in the BX register usage remains. [click back]

<PREV.  -  ALL  -  NEXT>

Comments