Chapter 7: Translating Assembly to Other Programming Languages
This chapter is going to be a weird one, because most people don’t start with assembly language before moving to higher level languages. In fact most people would probably recommend against Assembly as a first programming language.
But for the purpose of this chapter alone, I will be assuming that you have been following the first 6 chapters of this Assembly book and want to know how this knowledge can be used to translate the Assembly into other languages like C and C++. This is actually very easy to do because the other languages are easier and have built in functions for you to use.
So what I did is write a test suite program. It makes use of the core 4 of my chastelib functions (putstring,putint,intstr,strint) as well as some other utility functions just for displaying single characters, lines, and spaces.
In this chapter, I will be including the entire source code of the main program “main.asm” as well as “chastelib16.asm” which is the included file containing all the useful output functions. Snippets of these have been included throughout the book but by including them all in this chapter, you can be sure that you have the most updated and commented version of the source code. This will become more important later when I show you the C equivalent program.
main.asm
1 org 100h
2
3 main:
4
5 mov eax,main_string
6 call putstring
7
8 mov word[radix],16 ; can choose radix for integer output!
9 mov word[int_width],1
10 mov byte[int_newline],0
11
12 mov ax,input_string_int ;address of input string to convert to integer using current radix
13 call strint ;call strint to return the string in eax register
14 mov bx,ax ;bx=ax (copy the converted value returned in ax to bx)
15
16 mov ax,0
17 loop1:
18
19 mov word[radix],2 ;set radix to binary
20 mov word[int_width],8 ;width of 8 for maximum 8 bits
21 call putint
22 call putspace
23 mov word[radix],16 ;set radix to hexadecimal
24 mov word[int_width],2 ;width of 8 for maximum 8 bits
25 call putint
26 call putspace
27 mov word[radix],10 ;set radix to decimal (what humans read)
28 mov word[int_width],3 ;width of 8 for maximum 8 bits
29 call putint
30
31 cmp al,0x20
32 jb not_char
33 cmp al,0x7E
34 ja not_char
35
36 call putspace
37 call putchar
38
39 not_char: ;jump here if character is outside range to print
40
41 call putline
42
43 inc ax
44 cmp ax,bx;
45 jnz loop1
46
47 mov ax,4C00h ;DOS system call number ah=0x4C to exit program with ah=0x00 as return value
48 int 21h ;DOS interrupt to exit the program with numbers on previous line
49
50 ;A string to test if output works
51 main_string db 'This program is the official test suite for the DOS Assembly version of chastelib.',0Ah,0
52
53 ;test string of integer for input
54 input_string_int db '100',0
55
56 include 'chastelib16.asm' ; use %include if assembling with NASM instead of FASM.
57
58 ; This 16 bit DOS Assembly source has been formatted for the FASM assembler.
59 ; In order to run it, you will need the DOSBOX emulator or something similar.
60 ; First, assemble it into a binary file. FASM will automatically add
61 ; the .com extension because of the "org 100h" command.
62 ;
63 ; fasm main.asm
64 ;
65 ; Then you will need to open DOSBOX and mount the folder that it is in.
66 ; For example:
67 ;
68 ; mount c ~/.dos
69 ; c:
70 ;
71 ; Then, you will be able to just run the main.com.
72 ;
73 ; main
chastelib16.asm
1 ; This file is where I keep my function definitions.
2 ; These are usually my string and integer output routines.
3
4 ;this is my best putstring function for DOS because it uses call 40h of interrupt 21h
5 ;this means that it works in a similar way to my Linux Assembly code
6 ;the plan is to make both my DOS and Linux functions identical except for the size of registers involved
7
8 stdout dw 1 ; variable for standard output so that it can theoretically be redirected
9
10 putstring:
11
12 push ax
13 push bx
14 push cx
15 push dx
16
17 mov bx,ax ;copy ax to bx for use as index register
18
19 putstring_strlen_start: ;this loop finds the length of the string as part of the putstring function
20
21 cmp byte[bx],0 ;compare this byte with 0
22 jz putstring_strlen_end ;if comparison was zero, jump to loop end because we have found the length
23 inc bx ;increment bx (add 1)
24 jmp putstring_strlen_start ;jump to the start of the loop and keep trying until we find a zero
25
26 putstring_strlen_end:
27
28 sub bx,ax ; sub ax from bx to get the difference for number of bytes
29 mov cx,bx ; mov bx to cx
30 mov dx,ax ; dx will have address of string to write
31
32 mov ah,40h ; select DOS function 40h write
33 mov bx,[stdout] ; file handle 1=stdout
34 int 21h ; call the DOS kernel
35
36 pop dx
37 pop cx
38 pop bx
39 pop ax
40
41 ret
42
43
44
45 ;this is the location in memory where digits are written to by the intstr function
46
47 int_string db 16 dup '?' ;enough bytes to hold maximum size 16-bit binary integer
48
49 ;this is the end of the integer string optional line feed and terminating zero
50 ;clever use of this label can change the ending to be a different character when needed
51
52 int_newline db 0Dh,0Ah,0 ;the proper way to end a line in DOS/Windows
53
54 radix dw 2 ;radix or base for integer output. 2=binary, 8=octal, 10=decimal, 16=hexadecimal
55 int_width dw 8
56
57 intstr:
58
59 mov bx,int_newline-1 ;find address of lowest digit(just before the newline 0Ah)
60 mov cx,1
61
62 digits_start:
63
64 mov dx,0;
65 div word [radix]
66 cmp dx,10
67 jb decimal_digit
68 jge hexadecimal_digit
69
70 decimal_digit: ;we go here if it is only a digit 0 to 9
71 add dx,'0'
72 jmp save_digit
73
74 hexadecimal_digit:
75 sub dx,10
76 add dx,'A'
77
78 save_digit:
79
80 mov [bx],dl
81 cmp ax,0
82 jz intstr_end
83 dec bx
84 inc cx
85 jmp digits_start
86
87 intstr_end:
88
89 prefix_zeros:
90 cmp cx,[int_width]
91 jnb end_zeros
92 dec bx
93 mov byte[bx], '0'
94 inc cx
95 jmp prefix_zeros
96 end_zeros:
97
98 mov ax,bx ; store string in ax for display later
99
100 ret
101
102
103
104 ;function to print string form of whatever integer is in ax
105 ;The radix determines which number base the string form takes.
106 ;Anything from 2 to 36 is a valid radix
107 ;in practice though, only bases 2,8,10,and 16 will make sense to other programmers
108 ;this function does not process anything by itself but calls the combination of my other
109 ;functions in the order I intended them to be used.
110
111 putint:
112
113 push ax
114 push bx
115 push cx
116 push dx
117
118 call intstr
119 call putstring
120
121 pop dx
122 pop cx
123 pop bx
124 pop ax
125
126 ret
127
128
129
130
131
132
133
134
135 ;this function converts a string pointed to by ax into an integer returned in ax instead
136 ;it is a little complicated because it has to account for whether the character in
137 ;a string is a decimal digit 0 to 9, or an alphabet character for bases higher than ten
138 ;it also checks for both uppercase and lowercase letters for bases 11 to 36
139 ;finally, it checks if that letter makes sense for the base.
140 ;For example, G to Z cannot be used in hexadecimal, only A to F can
141 ;The purpose of writing this function was to be able to accept user input as integers
142
143 strint:
144
145 mov bx,ax ;copy string address from ax to bx because ax will be replaced soon!
146 mov ax,0
147
148 read_strint:
149 mov cx,0 ; zero cx so only lower 8 bits are used
150 mov cl,[bx] ;copy byte/character at address bx to cl register (lowest part of cx)
151 inc bx ;increment bx to be ready for next character
152 cmp cl,0 ; compare this byte with 0
153 jz strint_end ; if comparison was zero, this is the end of string
154
155 ;if char is below '0' or above '9', it is outside the range of these and is not a digit
156 cmp cl,'0'
157 jb not_digit
158 cmp cl,'9'
159 ja not_digit
160
161 ;but if it is a digit, then correct and process the character
162 is_digit:
163 sub cl,'0'
164 jmp process_char
165
166 not_digit:
167 ;it isn't a decimal digit, but it could be perhaps an alphabet character
168 ;which could be a digit in a higher base like hexadecimal
169 ;we will check for that possibility next
170
171 ;if char is below 'A' or above 'Z', it is outside the range of these and is not capital letter
172 cmp cl,'A'
173 jb not_upper
174 cmp cl,'Z'
175 ja not_upper
176
177 is_upper:
178 sub cl,'A'
179 add cl,10
180 jmp process_char
181
182 not_upper:
183
184 ;if char is below 'a' or above 'z', it is outside the range of these and is not lowercase letter
185 cmp cl,'a'
186 jb not_lower
187 cmp cl,'z'
188 ja not_lower
189
190 is_lower:
191 sub cl,'a'
192 add cl,10
193 jmp process_char
194
195 not_lower:
196
197 ;if we have reached this point, result invalid and end function
198 jmp strint_end
199
200 process_char:
201
202 cmp cx,[radix] ;compare char with radix
203 jae strint_end ;if this value is above or equal to radix, it is too high despite being a valid digit/alpha
204
205 mov dx,0 ;zero dx because it is used in mul sometimes
206 mul word [radix] ;mul ax with radix
207 add ax,cx
208
209 jmp read_strint ;jump back and continue the loop if nothing has exited it
210
211 strint_end:
212
213 ret
214
215
216
217 ;returns in al register a character from the keyboard
218 getchr:
219
220 mov ah,1
221 int 21h
222
223 ret
224
225 ;the next utility functions simply print a space or a newline
226 ;these help me save code when printing lots of things for debugging
227
228 space db ' ',0
229 line db 0Dh,0Ah,0
230
231 putspace:
232 push ax
233 mov ax,space
234 call putstring
235 pop ax
236 ret
237
238 putline:
239 push ax
240 mov ax,line
241 call putstring
242 pop ax
243 ret
244
245 ;a function for printing a single character that is the value of al
246
247 char: db 0,0
248
249 putchar:
250 push ax
251 mov [char],al
252 mov ax,char
253 call putstring
254 pop ax
255 ret
Now that you have the full source code. You can either copy and paste it from the PDF or epub edition (if you purchased the Leanpub edition) or you can download it directly from the github repository I have linked to at least twice in this book already.
But you don’t even have to assembly and run it to see what it does because I am going to show you the entire output that it generates!
Assembly Test Suite Output
1 This program is the official test suite for the DOS Assembly version of chastelib.
2 00000000 00 000
3 00000001 01 001
4 00000010 02 002
5 00000011 03 003
6 00000100 04 004
7 00000101 05 005
8 00000110 06 006
9 00000111 07 007
10 00001000 08 008
11 00001001 09 009
12 00001010 0A 010
13 00001011 0B 011
14 00001100 0C 012
15 00001101 0D 013
16 00001110 0E 014
17 00001111 0F 015
18 00010000 10 016
19 00010001 11 017
20 00010010 12 018
21 00010011 13 019
22 00010100 14 020
23 00010101 15 021
24 00010110 16 022
25 00010111 17 023
26 00011000 18 024
27 00011001 19 025
28 00011010 1A 026
29 00011011 1B 027
30 00011100 1C 028
31 00011101 1D 029
32 00011110 1E 030
33 00011111 1F 031
34 00100000 20 032
35 00100001 21 033 !
36 00100010 22 034 "
37 00100011 23 035 #
38 00100100 24 036 $
39 00100101 25 037 %
40 00100110 26 038 &
41 00100111 27 039 '
42 00101000 28 040 (
43 00101001 29 041 )
44 00101010 2A 042 *
45 00101011 2B 043 +
46 00101100 2C 044 ,
47 00101101 2D 045 -
48 00101110 2E 046 .
49 00101111 2F 047 /
50 00110000 30 048 0
51 00110001 31 049 1
52 00110010 32 050 2
53 00110011 33 051 3
54 00110100 34 052 4
55 00110101 35 053 5
56 00110110 36 054 6
57 00110111 37 055 7
58 00111000 38 056 8
59 00111001 39 057 9
60 00111010 3A 058 :
61 00111011 3B 059 ;
62 00111100 3C 060 <
63 00111101 3D 061 =
64 00111110 3E 062 >
65 00111111 3F 063 ?
66 01000000 40 064 @
67 01000001 41 065 A
68 01000010 42 066 B
69 01000011 43 067 C
70 01000100 44 068 D
71 01000101 45 069 E
72 01000110 46 070 F
73 01000111 47 071 G
74 01001000 48 072 H
75 01001001 49 073 I
76 01001010 4A 074 J
77 01001011 4B 075 K
78 01001100 4C 076 L
79 01001101 4D 077 M
80 01001110 4E 078 N
81 01001111 4F 079 O
82 01010000 50 080 P
83 01010001 51 081 Q
84 01010010 52 082 R
85 01010011 53 083 S
86 01010100 54 084 T
87 01010101 55 085 U
88 01010110 56 086 V
89 01010111 57 087 W
90 01011000 58 088 X
91 01011001 59 089 Y
92 01011010 5A 090 Z
93 01011011 5B 091 [
94 01011100 5C 092 \
95 01011101 5D 093 ]
96 01011110 5E 094 ^
97 01011111 5F 095 _
98 01100000 60 096 `
99 01100001 61 097 a
100 01100010 62 098 b
101 01100011 63 099 c
102 01100100 64 100 d
103 01100101 65 101 e
104 01100110 66 102 f
105 01100111 67 103 g
106 01101000 68 104 h
107 01101001 69 105 i
108 01101010 6A 106 j
109 01101011 6B 107 k
110 01101100 6C 108 l
111 01101101 6D 109 m
112 01101110 6E 110 n
113 01101111 6F 111 o
114 01110000 70 112 p
115 01110001 71 113 q
116 01110010 72 114 r
117 01110011 73 115 s
118 01110100 74 116 t
119 01110101 75 117 u
120 01110110 76 118 v
121 01110111 77 119 w
122 01111000 78 120 x
123 01111001 79 121 y
124 01111010 7A 122 z
125 01111011 7B 123 {
126 01111100 7C 124 |
127 01111101 7D 125 }
128 01111110 7E 126 ~
129 01111111 7F 127
130 10000000 80 128
131 10000001 81 129
132 10000010 82 130
133 10000011 83 131
134 10000100 84 132
135 10000101 85 133
136 10000110 86 134
137 10000111 87 135
138 10001000 88 136
139 10001001 89 137
140 10001010 8A 138
141 10001011 8B 139
142 10001100 8C 140
143 10001101 8D 141
144 10001110 8E 142
145 10001111 8F 143
146 10010000 90 144
147 10010001 91 145
148 10010010 92 146
149 10010011 93 147
150 10010100 94 148
151 10010101 95 149
152 10010110 96 150
153 10010111 97 151
154 10011000 98 152
155 10011001 99 153
156 10011010 9A 154
157 10011011 9B 155
158 10011100 9C 156
159 10011101 9D 157
160 10011110 9E 158
161 10011111 9F 159
162 10100000 A0 160
163 10100001 A1 161
164 10100010 A2 162
165 10100011 A3 163
166 10100100 A4 164
167 10100101 A5 165
168 10100110 A6 166
169 10100111 A7 167
170 10101000 A8 168
171 10101001 A9 169
172 10101010 AA 170
173 10101011 AB 171
174 10101100 AC 172
175 10101101 AD 173
176 10101110 AE 174
177 10101111 AF 175
178 10110000 B0 176
179 10110001 B1 177
180 10110010 B2 178
181 10110011 B3 179
182 10110100 B4 180
183 10110101 B5 181
184 10110110 B6 182
185 10110111 B7 183
186 10111000 B8 184
187 10111001 B9 185
188 10111010 BA 186
189 10111011 BB 187
190 10111100 BC 188
191 10111101 BD 189
192 10111110 BE 190
193 10111111 BF 191
194 11000000 C0 192
195 11000001 C1 193
196 11000010 C2 194
197 11000011 C3 195
198 11000100 C4 196
199 11000101 C5 197
200 11000110 C6 198
201 11000111 C7 199
202 11001000 C8 200
203 11001001 C9 201
204 11001010 CA 202
205 11001011 CB 203
206 11001100 CC 204
207 11001101 CD 205
208 11001110 CE 206
209 11001111 CF 207
210 11010000 D0 208
211 11010001 D1 209
212 11010010 D2 210
213 11010011 D3 211
214 11010100 D4 212
215 11010101 D5 213
216 11010110 D6 214
217 11010111 D7 215
218 11011000 D8 216
219 11011001 D9 217
220 11011010 DA 218
221 11011011 DB 219
222 11011100 DC 220
223 11011101 DD 221
224 11011110 DE 222
225 11011111 DF 223
226 11100000 E0 224
227 11100001 E1 225
228 11100010 E2 226
229 11100011 E3 227
230 11100100 E4 228
231 11100101 E5 229
232 11100110 E6 230
233 11100111 E7 231
234 11101000 E8 232
235 11101001 E9 233
236 11101010 EA 234
237 11101011 EB 235
238 11101100 EC 236
239 11101101 ED 237
240 11101110 EE 238
241 11101111 EF 239
242 11110000 F0 240
243 11110001 F1 241
244 11110010 F2 242
245 11110011 F3 243
246 11110100 F4 244
247 11110101 F5 245
248 11110110 F6 246
249 11110111 F7 247
250 11111000 F8 248
251 11111001 F9 249
252 11111010 FA 250
253 11111011 FB 251
254 11111100 FC 252
255 11111101 FD 253
256 11111110 FE 254
257 11111111 FF 255
main.c (The C Test Suite)
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include "chastelib.h"
4
5 int main(int argc, char *argv[])
6 {
7 int a=0,b;
8
9 radix=16;
10 int_width=1;
11
12 putstring("This program is the official test suite for the C version of chastelib.\n");
13
14 b=strint("100");
15 while(a<b)
16 {
17 radix=2;
18 int_width=8;
19 putint(a);
20 putstring(" ");
21 radix=16;
22 int_width=2;
23 putint(a);
24 putstring(" ");
25 radix=10;
26 int_width=3;
27 putint(a);
28
29 if(a>=0x20 && a<=0x7E)
30 {
31 putstring(" ");
32 putchar(a);
33 }
34
35 putstring("\n");
36 a+=1;
37 }
38
39 return 0;
40 }
The C version above does the exact same steps as the assembly version, but is calling functions that do very much the same steps as the assembly version. I will show you the contents of the included file “chastelib.h” next. Be prepared for a slightly less painful mess of code! However, much like the Assembly version it is heavily commented to help with understanding it.
chastelib.h (The C chastelib library)
1 /*
2 This file is a library of functions written by Chastity White Rose. The functions are for converting strings into integers and integers into strings.
3 I did it partly for future programming plans and also because it helped me learn a lot in the process about how pointers work
4 as well as which features the standard library provides, and which things I need to write my own functions for.
5
6 As it turns out, the integer output routines for C are too limited for my tastes. This library corrects this problem.
7 Using the global variables and functions in this file, integers can be output in bases/radixes 2 to 36
8 */
9
10 /*
11 These two lines define a static array with a size big enough to store the digits of an integer, including padding it with extra zeroes.
12 The integer conversion function always references a pointer to this global string, and this allows other standard library functions
13 such as printf to display the integers to standard output or even possibly to files.
14 */
15
16 #define usl 32 /*usl stands for Unsigned String Length*/
17 char int_string[usl+1]; /*global string which will be used to store string of integers. Size is usl+1 for terminating zero*/
18
19 /*radix or base for integer output. 2=binary, 8=octal, 10=decimal, 16=hexadecimal*/
20 int radix=2;
21 /*default minimum digits for printing integers*/
22 int int_width=1;
23
24 /*
25 This function is one that I wrote because the standard library can display integers as decimal, octal, or hexadecimal, but not any other bases(including binary, which is my favorite).
26 My function corrects this, and in my opinion, such a function should have been part of the standard library, but I'm not complaining because now I have my own, which I can use forever!
27 More importantly, it can be adapted for any programming language in the world if I learn the basics of that language.
28 */
29
30 char *intstr(unsigned int i)
31 {
32 int width=0;
33 char *s=int_string+usl;
34 *s=0;
35 while(i!=0 || width<int_width)
36 {
37 s--;
38 *s=i%radix;
39 i/=radix;
40 if(*s<10){*s+='0';}
41 else{*s=*s+'A'-10;}
42 width++;
43 }
44 return s;
45 }
46
47 /*
48 This function prints a string using fwrite.
49 This algorithm is the best C representation of how my Assembly programs also work.
50 Its true purpose is to be used in the putint function for conveniently printing integers,
51 but it can print any valid string.
52 */
53
54 void putstring(const char *s)
55 {
56 int c=0;
57 const char *p=s;
58 while(*p++){c++;}
59 fwrite(s,1,c,stdout);
60 }
61
62 /*
63 This function uses both intstr and putstring to print an integer in the currently selected radix and width.
64 */
65
66 void putint(unsigned int i)
67 {
68 putstring(intstr(i));
69 }
70
71 /*
72 This function is my own replacement for the strtol function from the C standard library.
73 I didn't technically need to make this function because the functions from stdlib.h can already convert strings from bases 2 to 36 into integers.
74 However, my function is simpler because it only requires 2 arguments instead of three, and it also does not handle negative numbers.
75 I have never needed negative integers, but if I ever do, I can use the standard functions or write my own in the future.
76 */
77
78 int strint(const char *s)
79 {
80 int i=0;
81 char c;
82 if( radix<2 || radix>36 ){printf("Error: radix %i is out of range!\n",radix);}
83 while( *s == ' ' || *s == '\n' || *s == '\t' ){s++;} /*skip whitespace at beginning*/
84 while(*s!=0)
85 {
86 c=*s;
87 if( c >= '0' && c <= '9' ){c-='0';}
88 else if( c >= 'A' && c <= 'Z' ){c-='A';c+=10;}
89 else if( c >= 'a' && c <= 'z' ){c-='a';c+=10;}
90 else if( c == ' ' || c == '\n' || c == '\t' ){break;}
91 else{printf("Error: %c is not an alphanumeric character!\n",c);break;}
92 if(c>=radix){printf("Error: %c is not a valid character for radix %i\n",*s,radix);break;}
93 i*=radix;
94 i+=c;
95 s++;
96 }
97 return i;
98 }
99
100 /*
101 Those four functions above are the core of chastelib.
102 While there may be extensions written for specific programs, these functions are essential for absolutely every program I write.
103
104 The only reason you would not need them is if you only output numbers in decimal or hexadecimal, because printf in C can do all that just fine.
105 However, the reason my core functions are superior to printf is that printf and its family of functions require the user to memorize all the arcane symbols for format specifiers.
106
107 The core functions are primarily concerned with standard output and the conversion of strings and integers. They do not deal with input from the keyboard or files. A separate extension will be written for my programs that need these features.
108 */
Now that you have witnessed the largest dump of code to ever be included in a chapter of a book, I want you to look it over carefully and you will notice that there is almost direct equivalence between the Assembly version and the C version.
Here is a detailed breakdown of how both versions operate despite the language syntax looking completel different.
putstring finds the terminating zero to calculate string length and prints that length of bytes. It achieves this by using the fwrite function which is part of the standard library. Just like the “ah=40h” DOS call, it must be given the arguments to say “Start at this address and write exactly this number of bytes to standard output!”. It also uses tons of pointer arithmetic in both the C and assembly versions. In this case, I would argue that the Assembly version might be easier to read than the C version. C lets a person create some weird looking code with the syntax used for pointers
intstr converts an integer into a string at a specific predetermined address and then returns a pointer to this address from the function. In both the C and Assembly version, the process of repeated division by the radix (also known as number base) while the integer is above zero or the string has not reached the minimum width or length I want the string to have. It will prefix the string with extra zeros just so it lines up perfectly as you say in the output earlier in this chapter.
putint is merely a convenience function the calls intstr and then putstr to convert and print an integer in one step. Functions are designed to repeat frequent operations to reduce code size and save programmer time. Though to be honest, if your time was valuable to you, you probably wouldn’t be reading a DOS assembly language book. Thanks for reading my book anyway!
strint does the opposite of intstr as you might guess from its name. It converts a string into an integer. Its usefulness is not fully obvious here because the example program reads a predefined string. Ordinarily, you would get user input from either the keyboard during the program or from command line arguments passed to the program before it starts. One small piece of advice though, if you want to accept user input, C is a better language than Assembly because I haven’t been successful in getting anyone to assembly and run my DOS assembly programs anyway!
You probably noticed other functions like putchar, putline, and putspace. I made these convenience functions in assembly because there are times when you need to print a character to separate numbers. Usually spaces and newlines are the most important. The putchar function exists in the C standard library since at least 1989 and probably much earlier.
Portable Assembly Language
C has been called a portable assembly language because C compilers translate C code into assembly language and then link it with the precompiled functions in other libraries. In short, they do the opposite process of what I have done in this chapter. I wrote these string and integer output routines because they didn’t exist in assembly language by default.
C already has printf,putchar, and fwrite. These are more than enough to handle outputting text including numbers without having to use the functions I have written. But in any case, I provided them in this chapter for helping people understand how these operations are done.
But when you are trying to write programs for DOS, assembly is still better because there are not enough easy to find C compilers that still work in 16 bit DOS mode. There was one made by the company Borland known as Turbo C. If you are lucky enough to find it on the internet and get it running, you might enjoy it.
C++ is a programming language that came after C and includes everything C has plus more. However, this book is about assembly and this chapter was but a brief introduction to the idea of translating assembly language into C for the purpose of having something portable to all platforms.
My other book, Chastity’s Code Cookbook uses mostly C code as an introduction to programming. If you liked this chapter, consider reading it for more nerdy programming content.