About Me
Welcome to my blog! This is where I collect my observations and notes on programming and technology. The main subjects range from implementation details to broader ideas about programming.
Main Topics
- Engineering Projects: Exploring implementation details and how technical systems work.
- C/C++: Notes on language features and programming techniques.
- The Programmer’s Perspective: Ideas about developing a career and a way of thinking as a programmer.
For more, visit the categories page.
Contact
If you have questions or would like to discuss something, please get in touch through the About page.
Thank you for reading and for your support. I hope these notes help you on your own technical journey!
Begin with the high-level operation
In C, displaying an integer can be hidden behind a small function:
1 | bool print_data(int a) |
A C++ template can work with different types that support stream insertion:
1 | template <class T> |
The task here is to expose some of the lower-level work behind displaying a number. We need to know both how characters reach the display and how an integer becomes a sequence of character codes.
Text video memory
The CPU communicates with display hardware through its interface and mapped storage. In the color text-mode environment used by this experiment, video memory begins at physical 0xB8000. Each screen cell uses two bytes: a character byte followed by an attribute byte.
An 80-column, 25-row screen uses 80 × 25 × 2 = 4000 visible bytes. Historical adapters can provide several pages, often at 4 KiB page intervals, with the active page selected by the display configuration. The source describes a 32 KiB region but gives B900:0000 as its end; that address is only 4 KiB beyond B800:0000, so the two claims are inconsistent. This exercise only needs the first text page.
We will display at row 8, column 3 using zero-based row and column parameters. The byte offset is row × 160 + column × 2.
A number is not its printed digits
The integer 12666 is stored as a binary value, whereas the visible text requires character codes for 1, 2, 6, 6, and 6: 31h, 32h, 36h, 36h, and 36h. The correct hexadecimal integer value is 317Ah; the binary transcription in the source is inconsistent with it.
ASCII digit codes are consecutive, so add 30h to a digit between zero and nine. First, however, the integer must be separated into decimal digits.

Divide by ten, retain the remainder, and repeat with the quotient. Each remainder becomes one digit after adding 30h. The remainders arrive from least significant digit to most significant digit, so their order must be reversed when forming the string.

For 12666, five divisions suffice, but the general loop stops when the quotient becomes zero. A conditional branch expresses that condition more directly than LOOP, which decrements a counter. CX can be changed by ordinary instructions too; its special role in LOOP does not make it otherwise immutable. A complete conversion also handles the input zero explicitly.
Division and quotient overflow
With an 8-bit divisor, unsigned DIV divides AX, returning quotient in AL and remainder in AH. With a 16-bit divisor, it divides DX:AX, returning quotient in AX and remainder in DX. The Chinese notes use BX in several places where the hardware instruction requires DX; a custom subroutine can choose a different interface, but DIV itself cannot.
For example, AX = 0100h divided by an 8-bit value one would need a quotient of 256, which cannot fit in AL. That operation raises divide error. To demonstrate the 8-bit case the operand is cl, not cx; operand width determines which division form is used.
This motivates a wider division subroutine that returns a quotient in two registers. Its implementation still needs to reject a zero divisor and keep each intermediate hardware division within range.
Divide the program into three routines
dtos converts a number into a string. The original proposed buffer is addressed by DS:SI, has ten zero-initialized bytes, and allows nine characters plus a terminator. That is enough for some inputs but not every unsigned 32-bit value, which may require ten digits plus the terminator. The eventual interface should state the input width and buffer capacity explicitly.
divdw performs a doubleword-by-word division. The proposed custom interface uses BX:AX for the input dividend, CX for the divisor, BX:AX for the quotient, and CX for the remainder. Internally it must adapt to the hardware’s DX:AX convention. Returning a wider quotient avoids the particular single-word quotient limit.
show_str copies a zero-terminated string into text video memory. Its parameters are DH for row 0–24, DL for column 0–79, CL for the color attribute, and DS:SI for the string address. Its visible screen output is the result.
The routines have distinct responsibilities. show_str can be developed independently with a sample string; divdw should be established before dtos, which repeatedly needs division. Clear parameter and return conventions keep their coupling manageable.
Testing show_str
Start with a fixed string and known location:
1 | assume cs:code |
The original skeleton’s moc is a typo for mov. It is also reasonable to write and debug the body inline first, then extract it into a callable routine once it works. A DOS executable using calls and pushes must have a valid stack supplied or initialized by its environment.
The recorded implementation
The original code is retained below, including its Chinese comments and punctuation. As the source warns, full-width semicolons are not valid MASM comment delimiters; replace them with ASCII ; or remove those annotations before assembling.
1 | assume cs:code |
The routine preserves registers it uses, temporarily saves the color while calculating row × 160 + column × 2, sets ES to B800h, and copies each byte until the zero terminator. SI advances by one source byte, while DI advances by two video-memory bytes.
At an even cell offset, the first byte is the character; the next byte is its display attribute:

This installment implements the display routine. The wider division and numeric conversion routines remain the next parts of the original exercise; it does not yet contain a complete general-purpose numeric printer.
If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. All the images used in the blog are my original works or AI works, if you want to take it,don't hesitate. Thank you !