Analysis of Answers for Assembly Language Experiment 10-3

“Assembly Language”, 3rd Edition by Wang Shuang

Chapter 10 CALL and RET Instructions

Experiment 10 Writing Subroutines (Page 206)

————————————

Note: Since Experiment 10 has 3 questions, and each question is more complex than previous experiments with longer code, it is divided into three articles. This is the reference answer for Question 3.

———————————–

3. Value Display

Problem

Write a program to display the data in the data segment in decimal format.

data segment

dw 123, 12666, 1, 8, 3, 38

data ends

These data are stored in memory as binary information, indicating the size of the values. To display them on the screen in a human-readable format, we need to convert the information. For example, the value 12666 is stored in the machine as binary information: 0011000101111010B (317AH), which the computer can understand. To read the understandable value 12666 on the display, we should see a string of characters: “12666”. Since the graphics card follows ASCII encoding, to display this string of characters on the monitor, it should be stored in the machine as ASCII codes: 31H, 32H, 36H, 36H, 36H (the ASCII codes for characters “0” to “9” are 30H to 39H).

From the above analysis, we can see that in the conceptual world, there is an abstract data 12666, which represents a numerical size. In the real world, it can have multiple representations, stored in electronic machines as high and low levels (binary), or written in human language “12666” on paper, blackboard, or screen. Now, the problem we face is to convert the same abstract data from one representation to another.

To display the data in decimal format on the screen, we need to perform two steps:

(1) Convert the data stored in binary format into a string representing decimal format;

(2) Display the string in decimal format;

We have already implemented the second step in the first subroutine of this experiment, so we just need to call show_str. Let’s discuss the first step, as converting binary information into a decimal string is a commonly needed function, and we should write a general subroutine for it.

Subroutine Description

Name: dtoc

Function: Converts word-type data into a string representing a decimal number, with the string ending with 0

Parameters: (ax) = word-type data

ds:si points to the starting address of the string

Returns: None

Example of Application: Write a program to display the data 12666 in decimal format at row 8, column 3 on the screen, in green. When displaying, we call the first subroutine show_str from this experiment.

assume cs:codedata segment  db 10 dup(0)data endscode segment  start: mov ax, 12666         mov bx, data         mov ds, bx         mov si, 0         call dtoc         mov dh, 8         mov dl, 3         mov cl, 2         call show_str         :         :         :code endsend start  

Tip

Next, we will conduct a simple analysis of this problem.

(1) To obtain the string “12666”, we need to get a series of ASCII codes representing that string: 31H, 32H, 36H, 36H, 36H.

The ASCII code corresponding to decimal digit characters = decimal digit value + 30H.

To obtain the string representing the decimal number, we first need to find the value of each digit of the decimal number.

Example: For 12666, we first find the values of each digit: 1, 2, 6, 6, 6. Then we add 30H to each of these numbers to get the ASCII code string representing 12666: 31H, 32H, 36H, 36H, 36H.

(2) So, how do we get the value of each digit? We can use the following method:

Analysis of Answers for Assembly Language Experiment 10-3

As seen, dividing 12666 by 10 gives a remainder 5 times, recording each remainder gives us the value of each digit.

(3) Based on the above analysis, we can derive the processing steps as follows:

Divide 12666 by 10, loop 5 times, record each remainder, and add 30H to each remainder to obtain the ASCII code string representing the decimal number. As follows:

Analysis of Answers for Assembly Language Experiment 10-3

(4) Questioning (3).

Given that the data is 12666, we know that 5 iterations were performed. However, in practical problems, the programmer does not know the value of the data, meaning the program cannot determine the number of iterations in advance.

So, how do we determine that all digit values have been obtained? We can see that as long as the quotient is 0, all digit values have been obtained. We can use the jcxz instruction to implement the related functionality.

===================

Analysis Explanation:

The difficulty and core of Question 3 should be how to extract each digit of a decimal number and then convert these digits into their corresponding ASCII character codes. The implementation idea has been detailed by Professor Wang Shuang in the tips section, so it will not be repeated here. However, there are some special considerations during code implementation, such as the order of operations, register preservation, etc., which need careful thought. For example, in the following sample code, the data is in the data segment, and the string storage is in the code segment. Thus, when the code runs, the ds must point to the data segment to fetch data, and during conversion and display, ds must point to the code segment. It is easy to introduce bugs while writing code, so special attention is needed. It is recommended to thoroughly debug the code after completion to gain a better understanding of the code and logic.

Question 3 is a comprehensive problem, and many functionalities can be implemented in various ways. The following sample code is just one approach; everyone can try different methods to achieve it. For example, when converting numbers to string format, the order is reversed, requiring reordering. I used the head-tail swap method for reordering, but you can also use a stack to implement it. I hope everyone can think and try different methods.

Reference implementation code example is as follows:

;"Assembly Language", 3rd Edition by Wang Shuang; Chapter 10 CALL and RET Instructions; Experiment 10 Writing Subroutines (Page 206); 3. Value Display dtoc show_str; Application Example: dw 123,12666,1,8,3,38
assume cs:code,ds:data,ss:stackdata segment    dw 123,12666,1,8,3,38data endsstack segment    db 16 dup(0)stack endscode segment                              db 16 dup(0)    ; Buffer to store the converted string        start: mov ax, stack               mov ss, ax               mov sp, 10H     ; Initial stack segment               mov bp, 0       ; bp points to data               mov dh, 8       ; dh stores the starting row for display               mov cx, 6       ; Total of 6 data            s: push cx         ; cl is used for parameters, so save cx first               mov ax, data               mov ds, ax      ; ds points to data segment               mov ax, ds:[bp] ; ax as parameter, stores the word data to be converted               mov bx, code    ; ax cannot be reused for parameters, use bx               mov ds, bx      ; ds changes to point to string segment address               mov si, 0       ; ds:si points to the first byte of the converted string               call dtoc       ; Call dtoc subroutine to complete conversion               mov dl, 3       ; dl as parameter, starting column for display               mov cl, 2       ; cl as parameter, display attributes               call show_str   ; Call show_str subroutine to display on the screen               add bp, 2       ; Point to the next data               add dh, 1       ; Switch to the next row               pop cx               loop s         
               mov ax, 4c00H               int 21H
              ; dtoc subroutine, converts word-type data to string format, ending with 0              ; Parameters: (ax) = a word-type data              ; Returns: ds:si points to the starting address of the converted string         dtoc: push bx               push cx               push dx               push bp               push di    ; Save the registers to be used on the stack               mov bx, 10 ; As the divisor               mov di, 0  ; Record the number of divisions                 get_rem: mov dx, 0  ; High 16 bits of the dividend, use 16-bit division to prevent overflow               div bx     ; (ax) = quotient, (dx) = remainder               add dl, 30H      ; Since the remainder is less than 10, we can use dl to take it, add 30H to convert to ASCII code               mov ds:[si], dl  ; Temporarily store in ds:si               inc si     ; Point to the next position               inc di     ; Increment the number of divisions by 1               mov cx, ax ; Store the quotient in cx               jcxz do_sort        ; Must save first and then judge, otherwise the last remainder will be missed               jmp short get_rem   ; Calculate the next remainder       do_sort: mov ax, di   ; After obtaining the remainders, need to reorder the string               mov bl, 2    ; Adjust the sorting loop count to half of the number of divisions               div bl       ; (al) = quotient, (ah) = remainder               mov ah, 0    ; Now ax contains the number of loops to perform               mov cx, ax               jcxz pre_ret ; If the string length is 1, do not swap, otherwise loop indefinitely               mov bx, si               mov bp, si               sub bx, di   ; Let bx point to the start of the string               sub bp, 1    ; Let bp point to the end of the string        do_swap: mov dl, [bx]     ; Swap front and back two by two               mov dh, ds:[bp]               mov [bx], dh               mov ds:[bp], dl               inc bx               dec bp               loop do_swap      pre_ret: mov byte ptr [si], 0  ; si now points to the end of the string, just write the end marker 0               sub si, di            ; Let si point back to the start of the string               pop di               pop bp               pop dx               pop cx               pop bx               ret
               ; show_str subroutine, displays a null-terminated string at a specified position on the screen               ; Parameters: (dh) = row, (dl) = column, (cl) = attribute, ds:si points to the string               ; Returns: None     show_str: push es               push di               push si               push ax               push bx               mov ax, 0B800H               mov es, ax               mov ax, 160               mul dh               mov di, ax               mov ax, 2               mul dl               add di, ax      ; (di) = (dh)*160 + (dl)*2, points to the starting display position               mov bl, cl      ; Store the attribute in bl      do_show: mov cl, ds:[si]               mov ch, 0               jcxz show_ok               mov es:[di], cl               inc di               mov es:[di], bl               inc di               inc si               jmp short do_show      show_ok: pop bx               pop ax               pop si               pop di               pop es               retcode endsend start

The code compiled and tested successfully, as shown in the following image.

Analysis of Answers for Assembly Language Experiment 10-3

Leave a Comment