Welcome to the blackest of the black computer arts - Assembler!
Let's look at this problem from an Assembler programmer's point of view.
You have a number, let's say BL ('cause that's a good Assemblerish name) and what you want to do is produce BL lines of output. Let's say BL=5
Now, CX is really good as a counter register, so if we copy BL to CL and clear CH by XORing it with itself, we have CX=#lines too.
The number of characters we have to print before reversing is conveniently one more than this, so we could increment CX - and that's an important number, so save it in say, BP
Each line consists of the letters 'Z' down to ('Z' - BH + 1) and one asterisk, then the same in reverse, and a new line. And every new line, you reduce the number of letters printed by one and add one to the number of asterisks.
To produce one line, we'd
- Load a register - say, DL, with 'Z' (DL because it's convenient as the output for a 'printme' routine)
- Copy that to another register, say BH (a register-to-register move is faster and smaller than a load-immediate)
- Subtract BL from BH, yielding a magic value, 'Z' - 5 = 'U' - is there a bell ringing yet?
We want to print a count of letters, so if we copy BP into CX, that should be useful.
Here's something we want to do CX times:
- Write out the character in DL - with a twist.
- Change the character in DL by DECREMENTING it
Then we need to output another BP bytes, so sopy BP to CX again
Here's something we want to do CX times:
- Write out the character in DL - with a twist.
- Change the character in DL by INCREMENTING it
Then send CR and LF
So - what's the twist?
Well, if the character in DL is LESS THAN or EQUAL TO the character in BH, we want to substitute an asterisk, so
- save DX on the stack
- load DL with ASTERISK if DL<=BH
- output DL
- pop DX back from the stack, restoring its value before we substituted the asterisk.
Done that line - decrement the count of lines in BL; if the result is non-zero, produce another line
- If it is zero, we're all done.
Note here that by decrementing the count of lines in BL, the number subtracted from DL at the start of the produce-a-line routine decrements, so the 'change to aster' value becomes 'V', 'W'...
Now let's get downright kinky.
You'll note that the only difference between the two write-CX-characters routines is that one DECREMENTS and the other INCREMENTS DL...
Let's set SI to 0FFFFH and DI to 1 before we start, just for laughs.
Now - DH is sitting on the bench for this entire game.
Suppose we write the routine to print CX characters such that it ADDs SI to DX. That will give us the decrement we need in the loop the first time.
- When the loop ends, suppose we EXCHANGE DI and SI.
- The next time the loop runs, it will INCREMENT DX, then exchange SI and DI again.
Useful, huh?
Oh, and while we're twisting, we can get DH involved by using it to store DL rather than pushing DX and popping it back. Move DL to DH, test for substituting the aster, write out and restore DL from DH.
So - the point with this routine is that it doesn't rely on a string in storage...