1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
|
TITLE PRINTF ROUITNE FOR MS-DOS
;
; PRINTF(Control String, arg1, arg2,...,argn-1,argn)
;
; Characters are output to PFHandle according to the
; specifications contained in the Control String.
;
; The conversion characters are as follow:
;
; %c - output the next argument as a character
; %s - output the next argument as a string
; %x - output the next argument as a hexidecimal number
; using abcedf
; %X - output the next argument as a hexidecimal number
; using ABCDEF
; %d - output the next argument as a decimal number
;
;
; Other format specifiers that may precede the conversion character are:
;
; - (minus sign) - causes the field to be left-adjusted
; + (plus sign) - causes the field to be right-adjusted (default)
; n - digit specifing the minimum field width (default to 1)
; L - specifing a long integer
;
; On entry to PRINTF the stack contains the return address and a pointer
; to an argument list.
;
; ____________________
; | Ret Addr | <= SP
; --------------------
; | Ptr to Arg List |
; --------------------
;
; And the argument list contains the following:
;
; String_ptr (a pointer to the control string)
; Arg 1
; Arg 2
; .
; .
; .
; Arg n-1
; Arg n
;
; If the argument is a %s or %c the arg contains a pointer to the string
; or character.
;
; The arguments are used in one-to-one correspondence to % specifiers.
.xlist
.xcref
INCLUDE dossym.asm
.cref
.list
printf_CODE SEGMENT public byte
ASSUME CS:PRINTF_CODE,DS:NOTHING,ES:NOTHING,SS:NOTHING
PUBLIC PRINTF, PFHandle
PUBLIC PRINTF_LAST
PFHandle DW 1
PRINTF_LEFT DB 0
PRINTF_LONG DB 0
PRINTF_HEX DB 0
TABLE_INDEX DB 0
S_FLAG DB 0
PRINTF_WIDTH DW 0
PRINTF_BASE DW 0
PAD_CHAR DB " "
PRINTF_TABLE DB "0123456789ABCDEFabcdef"
PRINTF_STACK STRUC
OLDES DW ?
OLDDS DW ?
OLDSI DW ?
OLDDI DW ?
OLDAX DW ?
OLDBX DW ?
OLDCX DW ?
OLDDX DW ?
OLDBP DW ?
OLDCS DW ?
OLDIP DW ?
STRING DW ?
PRINTF_STACK ENDS
PRINTF_ARGS STRUC
CONSTR DW ?
ARG DW ?
PRINTF_ARGS ENDS
RET_ADDR1 DW ?
RET_ADDR2 DW ?
BUFSIZ = 20
PRINTF_BUF DB BUFSIZ DUP (?)
db 0 ;This buffer is always nul terminated
BUFEND DW $-PRINTF_BUF
PRINTF proc far
PUSH BP ;Save the callers' registers
PUSH DX
PUSH CX
PUSH BX
PUSH AX
PUSH DI
PUSH SI
PUSH ES
PUSH DS
MOV BP,SP
PUSH CS
POP ES ;ES points to Printf segment
MOV DI,OFFSET PRINTF_BUF ;DI points to the output buffer
MOV BP,[BP.STRING] ;BP points to the argument list
MOV SI,DS:[BP] ;SI points to the control string
XOR BX,BX ;BX is the index into the arg list
CALL Clear_flags ; initialize the world
GET_CHAR:
LODSB ;Get a character
CMP AL,"%" ;Is it a conversion specifier?
JZ CONV_CHAR ;Yes - find out which one
OR AL,AL ;Is it the end of the control string?
JZ PRINTF_DONE ;Yes - then we're done
CALL OUTCHR ;Otherwise store the character
JMP SHORT GET_CHAR ;And go get another
PRINTF_DONE:
CALL FLUSH
POP DS
POP ES
POP SI
POP DI
POP AX
POP BX
POP CX
POP DX
POP BP
POP CS:[RET_ADDR1] ;Fix up the stack
POP CS:[RET_ADDR2]
POP AX
PUSH CS:[RET_ADDR2]
PUSH CS:[RET_ADDR1]
RET
printf endp
PRINTF_PERCENT:
CALL OUTCHR
JMP GET_CHAR
CONV_CHAR:
;Look for any format specifiers preceeding the conversion character
LODSB
CMP AL,"%" ;Just print the %
JZ PRINTF_PERCENT
CMP AL,"-" ;Right justify the field
JZ LEFT_ADJ
CMP AL,"+" ;Left justify the field
JZ NXT_CONV_CHAR
CMP AL,"L" ;Is it a long integer
JZ LONG_INT
CMP AL,"l"
JZ LONG_INT
CMP AL,"0" ;Is it a precision specification
JB LOOK_CONV_CHAR
CMP AL,"9"
JA LOOK_CONV_CHAR
CMP AL,"0"
JNZ NOT_PAD
CMP CS:[PRINTF_WIDTH],0
JNZ NOT_PAD
MOV CS:BYTE PTR [PAD_CHAR],"0"
NOT_PAD:
PUSH AX ;Adjust decimal place on precision
MOV AX,10
MUL CS:[PRINTF_WIDTH]
MOV CS:[PRINTF_WIDTH],AX
POP AX
XOR AH,AH
SUB AL,"0"
ADD CS:[PRINTF_WIDTH],AX ;And save the total
JMP SHORT NXT_CONV_CHAR
;Set the correct flags for the options in a conversion
LEFT_ADJ:
INC CS:BYTE PTR[PRINTF_LEFT]
JMP SHORT NXT_CONV_CHAR
LONG_INT:
INC CS:BYTE PTR[PRINTF_LONG]
NXT_CONV_CHAR:
JMP CONV_CHAR
;Look for a conversion character
LOOK_CONV_CHAR:
CMP AL,"X"
JZ HEX_UP
;Make all other conversion characters upper case
CMP AL,"a"
JB CAPS
CMP AL,"z"
JG CAPS
AND AL,0DFH
CAPS:
CMP AL,"X"
JZ HEX_LO
CMP AL,"D"
JZ DECIMAL
CMP AL,"C"
JZ C_PUT_CHAR
CMP AL,"S"
JZ S_PUT_STRG
;Didn't find any legal conversion character - IGNORE it
call clear_flags
jmp get_char
HEX_LO:
MOV CS:[TABLE_INDEX],6 ;Will print lower case hex digits
HEX_UP:
MOV CS:[PRINTF_BASE],16 ;Hex conversion
JMP CONV_TO_NUM
DECIMAL:
MOV CS:[PRINTF_BASE],10 ;Decimal conversion
JMP CONV_TO_NUM
S_PUT_STRG:
INC CS:[S_FLAG] ;It's a string specifier
C_PUT_CHAR:
PUSH SI ;Save pointer to control string
MOV SI,BX
ADD BX,2
MOV SI,ds:[BP+SI.ARG] ;Point to the % string or character
CMP BYTE PTR CS:[S_FLAG],0
JNZ S_PUT_1
LODSB
cmp al,0
jz short c_s_end
CALL OUTCHR ;Put it into our buffer
JMP SHORT C_S_END
S_PUT_1:
mov cx,cs:[printf_width]
or cx,cx
jz s_put_2
cmp cs:byte ptr[printf_left],0
jnz s_put_2
push si
call Pad_string
pop si
s_put_2:
push si
s_put_3:
LODSB ;Put them all in our buffer
CMP AL,0
jz s_put_4
CALL OUTCHR
jmp short S_PUT_3
s_put_4:
pop si
cmp byte ptr[printf_left],0
jz c_s_end
mov cx,cs:[printf_width]
or cx,cx
jz c_s_end
call Pad_string
C_S_END:
call clear_flags
POP SI ;Restore control string pointer
JMP GET_CHAR ;Go get another character
pad_string:
xor dx,dx
count_loop:
lodsb
or al,al
jz count_done
inc dx
jmp short count_loop
count_done:
sub cx,dx
jbe count_ret
call pad
count_ret:
ret
CONV_TO_NUM:
PUSH SI ;Save pointer to control string
MOV SI,BX ;Get index into argument list
ADD BX,2 ;Increment the index
MOV AX,ds:[BP+SI.ARG] ;Lo word of number in SI
CMP BYTE PTR CS:[PRINTF_LONG],0 ;Is this is a short or long integer?
JZ NOT_LONG_INT
MOV SI,BX ;Copy index
ADD BX,2 ;Increment the index
MOV DX,ds:[BP+SI.ARG] ;Hi word of number in BP
JMP SHORT DO_CONV
NOT_LONG_INT:
XOR DX,DX ;Hi word is zero
DO_CONV:
PUSH BX ;Save index into arguemnt list
MOV si,CS:[PRINTF_BASE]
MOV cx,CS:[PRINTF_WIDTH]
CALL PNUM
CALL PAD
CONV_DONE:
call clear_flags
POP BX
POP SI
jmp get_char
PNUM:
DEC CX
PUSH AX
MOV AX,DX
XOR DX,DX
DIV SI
MOV BX,AX
POP AX
DIV SI
XCHG BX,DX
PUSH AX
OR AX,DX
POP AX
JZ DO_PAD
PUSH BX
CALL PNUM
POP BX
JMP SHORT REM
DO_PAD:
CMP CS:BYTE PTR[PRINTF_LEFT],0
JNZ REM
CALL PAD
REM:
MOV AX,BX
CMP AL,10
JB NOT_HEX
CMP CS:BYTE PTR [PRINTF_HEX],0
JNZ NOT_HEX
ADD AL,CS:BYTE PTR [TABLE_INDEX]
NOT_HEX:
MOV BX,OFFSET PRINTF_TABLE
PUSH DS
PUSH CS
POP DS
XLAT 0
POP DS
push cx
CALL OUTCHR
pop cx
RET
PAD:
OR CX,CX
JLE PAD_DONE
MOV AL,CS:BYTE PTR [PAD_CHAR]
PAD_LOOP:
push cx
CALL OUTCHR
pop cx
LOOP PAD_LOOP
PAD_DONE:
RET
OUTCHR:
STOSB
CMP DI,offset bufend-1 ;Don't count the nul
RETNZ
MOV CX,BUFSIZ
WRITE_CHARS:
push bx
MOV BX,PFHandle
push ds
PUSH CS
POP DS
MOV DX,OFFSET PRINTF_BUF
MOV AH,WRITE
INT 21H
pop ds
pop bx
MOV DI,OFFSET PRINTF_BUF
RET
FLUSH:
CMP DI,OFFSET PRINTF_BUF
RETZ
SUB DI,OFFSET PRINTF_BUF
MOV CX,DI
call write_chars
ret
CLEAR_FLAGS:
XOR ax,ax
MOV BYTE PTR CS:[PRINTF_LEFT],al ;Reset justifing flag
MOV BYTE PTR CS:[PRINTF_LONG],al ;Reset long flag
MOV BYTE PTR CS:[TABLE_INDEX],al ;Reset hex table index
MOV CS:[PRINTF_WIDTH],ax ;Reinitialize width to 0
MOV BYTE PTR CS:[PAD_CHAR]," " ;Reset padding character
MOV BYTE PTR CS:[S_FLAG],al ;Clear the string flag
ret
PRINTF_LAST LABEL WORD
printf_CODE ENDS
END
|