source: xtideuniversalbios/trunk/Configurator/Src/Libraries/print.asm@ 200

Last change on this file since 200 was 181, checked in by krille_n_@…, 13 years ago

Changes to all parts of the project:

  • Size optimizations.
  • Added a define (EXCLUDE_FROM_XTIDECFG) to exclude unused library code from XTIDECFG.
  • Tried to minimize time spent with interrupts disabled.
  • Some minor attempts to improve speed (reordering instructions etc).
  • Tried to improve readability, did some cleanup and fixed some errors in comments.
File size: 18.8 KB
RevLine 
[2]1; Project name : Print library
[78]2; Description : ASM library for character and string
[2]3; printing related functions.
4
5;--------------- Equates -----------------------------
6
7; String library function to include
8%define USE_PRINT_FORMAT ; Print_Format
9%define USE_PRINT_NEWLINE ; Print_Newline
10%define USE_PRINT_CHARBUFFER ; Print_CharBuffer
11%define USE_PRINT_REPEAT ; Print_Repeat
12;%define USE_PRINT_BOOL ; Print_Bool
13;%define USE_PRINT_INTSW ; Print_IntSW
14%define USE_PRINT_INTUW ; Print_IntUW
15;%define USE_PRINT_INTUDW ; Print_IntUDW
16%define USE_PRINT_INTHEXB ; Print_IntHexB
17%define USE_PRINT_INTHEXW ; Print_IntHexW
18;%define USE_PRINT_INTHEXDW ; Print_IntHexDW
19
20
21; Text mode character attribute byte bits for CGA+
22FLG_CGA_FG_B EQU (1<<0)
23FLG_CGA_FG_G EQU (1<<1)
24FLG_CGA_FG_R EQU (1<<2)
25FLG_CGA_FG_I EQU (1<<3)
26FLG_CGA_BG_B EQU (1<<4)
27FLG_CGA_BG_G EQU (1<<5)
28FLG_CGA_BG_R EQU (1<<6)
29FLG_CGA_BG_I EQU (1<<7)
30FLG_CGA_BG_BLNK EQU (1<<7) ; Blinking is default
31
32; Text mode character attribute bytes for MDA/Hercules (mono)
33; *Not displayed on some monitors
34ATTR_MDA_HIDDEN EQU 00h ; Not displayed
35ATTR_MDA_ULINE EQU 01h ; Underlined
36ATTR_MDA_NORMAL EQU 07h ; Normal (white on black)
37ATTR_MDA_INT_U EQU 09h ; High intensity, underlined
38ATTR_MDA_INT EQU 0Fh ; High intensity
39ATTR_MDA_REVERSE EQU 70h ; Reverse video (black on white)
40ATTR_MDA_BLNK EQU 87h ; Blinking white on black*
41ATTR_MDA_BLNK_INT EQU 8Fh ; Blinking high intensity*
42ATTR_MDA_BLNK_REV EQU 0F0h; Blinking reverse video
43
44
45;-------------- Private global variables -------------
46; Section containing initialized data
47;SECTION .data
48
49g_rgbHex: db "0123456789ABCDEF" ; Hexadecimal printing
50g_rgbFormat: ; Placeholders for Print_Format
51%ifdef USE_PRINT_INTSW
52 db 'd' ; Prints signed 16-bit integer (WORD)
53%endif
54%ifdef USE_PRINT_INTUW
55 db 'u' ; Prints unsigned 16-bit integer (WORD)
56%endif
57%ifdef USE_PRINT_INTUDW
58 db 'U' ; Prints unsigned 32-bit integer (DWORD)
59%endif
60%ifdef USE_PRINT_INTHEXW
61 db 'x' ; Prints 8- or 16-bit hexadecimal (BYTE, WORD)
62%endif
63%ifdef USE_PRINT_INTHEXDW
64 db 'X' ; Prints 32-bit hexadecimal (DWORD)
65%endif
66 db 's' ; Prints string (DS segment)
67 db 'S' ; Prints string (far pointer)
68%ifdef USE_PRINT_BOOL
69 db 'b' ; Prints boolean value 0 or 1
70%endif
71 db 'c' ; Prints character
72 db 'C' ; Prints character number of times
73_g_rgbFormatEnd:
74
75; Number of different placeholders for Print_Format
76CNT_PLCEHLDRS EQU (_g_rgbFormatEnd-g_rgbFormat)
77
78
79
80;-------------- Public functions ---------------------
81; Section containing code
82SECTION .text
83
84; Include one file to use DOS, BIOS or VRAM macros and functions
85;%include "prntdos.asm" ; Include to use DOS printing functions
86%include "prntbios.asm" ; Include to use BIOS printing functions
87;%include "prntvram.asm" ; Include to access VRAM directly
88
89
90;--------------------------------------------------------------------
91; Debugging macro that prints wanted character and newline.
[181]92;
[2]93; PRINT_DBG_CH
94; Parameters:
95; %1: Character to print
96; Returns:
97; Nothing
98; Corrupts registers:
99; Nothing
100;--------------------------------------------------------------------
101%macro PRINT_DBG_CH 1
102 pushf
103 push dx
104 push ax
105 mov dl, %1
106 PRINT_CHAR
[78]107 ;mov dl, CR
108 ;PRINT_CHAR
109 ;mov dl, LF
110 ;PRINT_CHAR
[2]111 pop ax
112 pop dx
113 popf
114%endmacro
115
116
117;--------------------------------------------------------------------
118; Simplified printf-style string formatting function.
119; Strings must end to STOP character.
120; Supports following formatting types:
121; %d Prints signed 16-bit integer (WORD)
122; %u Prints unsigned 16-bit integer (WORD)
123; %U Prints unsigned 32-bit integer (DWORD)
124; %x Prints 8- or 16-bit hexadecimal (BYTE, WORD)
125; %X Prints 32-bit hexadecimal (DWORD)
126; %s Prints string (DS segment)
127; %S Prints string (far pointer)
128; %b Prints boolean value 0 or 1
129; %c Prints character
130; %[num]C Prints character number of times (up to 256, 0=256)
131; %% Prints '%' character (no parameter pushed)
132;
133; Any placeholder can be set to minimum length by specifying
134; minimum number of characters. For example %8d would append spaces
135; after integer so that at least 8 characters would be printed.
136;
137; NOTE! Caller must clean the stack variables!
[181]138;
[2]139; Print_Format
140; Parameters:
141; DL: Min length character (usually space)
142; DS:SI: Pointer to string to format
143; Stack: Parameters for formatting placeholders.
144; Parameter for first placeholder must be pushed last
145; (to top of stack).
146; High word must be pushed first for 32-bit parameters.
147; Returns:
148; Nothing
149; Corrupts registers:
150; AX, SI
151;--------------------------------------------------------------------
152%ifdef USE_PRINT_FORMAT
153ALIGN JUMP_ALIGN
154Print_Format:
155 push es
156 push bp
157 push di
158 push cx
159 push dx ; Push min length character
160 push cs ; Copy CS...
161 pop es ; ...to ES
162 mov bp, sp ; Copy SP to BP
163 add bp, 2*6 ; Set BP to point first stack parameter
164 cld ; Set LODSB to increment SI
165ALIGN JUMP_ALIGN
166.PrintLoop: ; Load characters from string
167 lodsb ; Load char from DS:SI to AL, increment SI
168 xor cx, cx ; Zero minimum length
169 cmp al, STOP ; End of string?
170 je .Return ; If so, return
171 cmp al, '%' ; Format placeholder?
172 je .Format ; If so, jump to format
173ALIGN JUMP_ALIGN
174.PrintChar: ; Prints single character from AL
175 mov dl, al ; Copy character to DL
176 PRINT_CHAR ; Print character
177 mov dx, 1 ; One char printed
178ALIGN JUMP_ALIGN
179.PrintXtraSoMin: ; Checks how many spaces to append
180 sub cx, dx ; Number of spaces needed
181 jle .PrintLoop ; Read next character if no spaces needed
182 pop dx ; Pop min length character to DL
183ALIGN JUMP_ALIGN
184.PrintSpaceLoop: ; Loop to print the required spaces
185 PRINT_CHAR
186 loop .PrintSpaceLoop
187 push dx ; Store min length character
188 jmp .PrintLoop ; Jump to read next character
189ALIGN JUMP_ALIGN
190.Return: ; The only return point for this function
191 pop dx
192 pop cx
193 pop di
194 pop bp
195 pop es
196 ret
197ALIGN JUMP_ALIGN
198.UpdateMinLen: ; Updates placeholder minimum length
199 mov al, ah ; Copy digit to AL
200 mov ah, cl ; Copy previous length to AH
201 aad ; AL=10*AH+AL, AH=0
202 mov cx, ax ; Copy new length to CX
203ALIGN JUMP_ALIGN
204.Format:
205 lodsb ; Load char from DS:SI to AL, increment SI
206 cmp al, STOP
207 je .Return ; Invalid string
208 mov ah, al ; Copy char to AH
209 sub ah, '0' ; Possible digit char to digit
210 cmp ah, 9 ; Was valid digit?
211 jbe .UpdateMinLen ; If so, jump to update minimum length
212 push cx ; Store number of spaces needed
213 mov cx, CNT_PLCEHLDRS ; Load number of placeholders to CX
214 mov di, g_rgbFormat ; Load offset to format placeholder table
215 repne scasb ; Compare AL to [ES:DI] until match
216 pop cx ; Restore spaces needed
217 jne .PrintChar ; If no match, jump to print character
218 sub di, g_rgbFormat+1 ; To placeholder index
219 shl di, 1 ; Shift for word lookup
220 jmp [cs:di+.g_rgwFormJump] ; Jump to format
221
222%ifdef USE_PRINT_INTSW
223ALIGN JUMP_ALIGN
224.Print_d: ; Print signed 16-bit integer
225 mov ax, [bp] ; Load word to print
226 times 2 inc bp
227 call Print_IntSW
228 jmp .PrintXtraSoMin
229%endif
230%ifdef USE_PRINT_INTUW
231ALIGN JUMP_ALIGN
232.Print_u: ; Print unsigned 16-bit integer
233 mov ax, [bp] ; Load word to print
234 times 2 inc bp
235 call Print_IntUW
236 jmp .PrintXtraSoMin
237%endif
238%ifdef USE_PRINT_INTUDW
239ALIGN JUMP_ALIGN
240.Print_U: ; Print unsigned 32-bit integer
241 mov ax, [bp] ; Load loword
242 mov dx, [bp+2] ; Load hiword
243 add bp, 4
244 call Print_IntUDW
245 jmp .PrintXtraSoMin
246%endif
247%ifdef USE_PRINT_INTHEXW
248ALIGN JUMP_ALIGN
249.Print_x: ; Prints 8- or 16-bit hexadecimal
250 mov ax, [bp] ; Load word to print
251 times 2 inc bp
252%ifdef USE_PRINT_INTHEXB
253 test ah, ah ; 16-bit hexadecimal?
254 jnz .Print_x16 ; If so, jump to print it
255 call Print_IntHexB
256 jmp .PrintXtraSoMin
257.Print_x16:
258%endif
259 call Print_IntHexW
260 jmp .PrintXtraSoMin
261%endif
262%ifdef USE_PRINT_INTHEXDW
263ALIGN JUMP_ALIGN
264.Print_X: ; Prints 32-bit hexadecimal
265 mov ax, [bp] ; Load loword
266 mov dx, [bp+2] ; Load hiword
267 add bp, 4
268 call Print_IntHexDW
269 jmp .PrintXtraSoMin
270%endif
271ALIGN JUMP_ALIGN
272.Print_s: ; Prints string from DS segment
273 mov dx, [bp] ; Load offset to string
274 times 2 inc bp
275 PRINT_STR_LEN ; Print string
276 jmp .PrintXtraSoMin
277ALIGN JUMP_ALIGN
278.Print_S: ; Prints string using far pointer
279 push ds ; Store DS
280 mov dx, [bp] ; Load offset to string
281 mov ds, [bp+2] ; Load segment to string
282 add bp, 4
283 PRINT_STR_LEN ; Print string
284 pop ds ; Restore DS
285 jmp .PrintXtraSoMin
286%ifdef USE_PRINT_BOOL
287ALIGN JUMP_ALIGN
288.Print_b: ; Prints boolean value
289 mov ax, [bp] ; Load boolean value to print
290 times 2 inc bp
291 call Print_Bool
292 jmp .PrintXtraSoMin
293%endif
294ALIGN JUMP_ALIGN
295.Print_c: ; Prints character
296 mov ax, [bp] ; Load character to print
297 times 2 inc bp
298 jmp .PrintChar ; Jump to print character
299ALIGN JUMP_ALIGN
300.Print_C:
301 mov dx, [bp] ; Load character to print
302 times 2 inc bp
303ALIGN JUMP_ALIGN
304.CharLoop:
305 PRINT_CHAR
306 loop .CharLoop
307 jmp .PrintLoop
308
309ALIGN JUMP_ALIGN
310.g_rgwFormJump: ; Jump table for Print_Format
311%ifdef USE_PRINT_INTSW
312 dw .Print_d
313%endif
314%ifdef USE_PRINT_INTUW
315 dw .Print_u
316%endif
317%ifdef USE_PRINT_INTUDW
318 dw .Print_U
319%endif
320%ifdef USE_PRINT_INTHEXW
321 dw .Print_x
322%endif
323%ifdef USE_PRINT_INTHEXDW
324 dw .Print_X
325%endif
326 dw .Print_s
327 dw .Print_S
328%ifdef USE_PRINT_BOOL
329 dw .Print_b
330%endif
331 dw .Print_c
332 dw .Print_C
333%endif
334
335
336;--------------------------------------------------------------------
337; Prints newline character to change line.
[181]338;
[2]339; Print_Newline
340; Parameters:
341; Nothing
342; Returns:
343; Nothing
344; Corrupts registers:
345; Nothing
346;--------------------------------------------------------------------
347%ifdef USE_PRINT_NEWLINE
348ALIGN JUMP_ALIGN
349Print_Newline:
350 push ax
351 push dx
352 mov dl, LF
353 PRINT_CHAR
354 mov dl, CR
355 PRINT_CHAR
356 pop dx
357 pop ax
358 ret
359%endif
360
[181]361
[2]362;--------------------------------------------------------------------
363; Prints wanted number of characters.
[181]364;
[2]365; Print_CharBuffer
366; Parameters:
367; CX: Number of characters to print
368; ES:DI: Ptr to character buffer
369; Returns:
370; Nothing
371; Corrupts registers:
372; AX, CX
373;--------------------------------------------------------------------
374%ifdef USE_PRINT_CHARBUFFER
375ALIGN JUMP_ALIGN
376Print_CharBuffer:
377 jcxz .Return
378 push ds
379 push si
380 push dx
381 push es
382 pop ds
383 mov si, di
384 cld ; LODSB to increment SI
385ALIGN JUMP_ALIGN
386.Rep:
387 lodsb ; Load from [DS:SI] to AL
388 mov dl, al ; Copy to DL for printing
389 PRINT_CHAR
390 loop .Rep
391 pop dx
392 pop si
393 pop ds
394.Return:
395 ret
396%endif
397
398
399;--------------------------------------------------------------------
400; Repeats wanted character.
[181]401;
[2]402; Print_Repeat
403; Parameters:
404; CX: Number of times to repeat character
405; DL: Character to repeat
406; Returns:
407; Nothing
408; Corrupts registers:
409; AX, CX
410;--------------------------------------------------------------------
411%ifdef USE_PRINT_REPEAT
412ALIGN JUMP_ALIGN
413Print_Repeat:
414 jcxz .Return
415ALIGN JUMP_ALIGN
416.Rep:
417 PRINT_CHAR
418 loop .Rep
419.Return:
420 ret
421%endif
422
423
424;--------------------------------------------------------------------
425; Prints boolean value.
[181]426;
[2]427; Print_Bool
428; Parameters:
429; AX: Boolean value (0=FALSE, non-zero=TRUE)
430; Returns:
431; DX: Number of characters printed
432; Corrupts registers:
433; AX
434;--------------------------------------------------------------------
435%ifdef USE_PRINT_BOOL
436ALIGN JUMP_ALIGN
437Print_Bool:
438 mov dl, '0' ; Assume FALSE
439 test ax, ax ; Is FALSE?
440 jz .Print ; If so, jump to print
441 inc dx ; Increment to '1'
442ALIGN JUMP_ALIGN
443.Print:
444 PRINT_CHAR
445 mov dx, 1 ; One character printed
446 ret
447%endif
448
449
450;--------------------------------------------------------------------
451; Prints signed or unsigned 16-bit integer.
[181]452;
[2]453; Print_IntSW Prints signed 16-bit word
454; Print_IntUW Prints unsigned 16-bit word
455; Parameters:
456; AX: Word to print
457; Returns:
458; DX: Number of characters printed
459; Corrupts registers:
460; AX
461;--------------------------------------------------------------------
462%ifdef USE_PRINT_INTSW
463ALIGN JUMP_ALIGN
464Print_IntSW:
[78]465 test ax, ax ; Positive integer?
466 jns Print_IntUW ; If so, jump to print it
[2]467 push di ; Store DI
468 push ax ; Store word
469 mov dl, '-' ; Print '-'
470 PRINT_CHAR
471 mov di, 1 ; One character printed
472 pop ax ; Restore word
473 neg ax ; Negative to positive
474 jmp Print_ContinueFromIntSW
475%endif
476
477%ifdef USE_PRINT_INTUW or USE_PRINT_INTSW
478ALIGN JUMP_ALIGN
479Print_IntUW:
480 push di ; Store DI
481 xor di, di ; Zero character counter
482Print_ContinueFromIntSW:
483 push bx ; Store BX
484 push cx ; Store CX
485 mov bx, 10 ; Load divisor to BX
486 mov cx, 5 ; Load number of divisions to CX
487ALIGN JUMP_ALIGN
488.DivLoop:
489 xor dx, dx ; Zero DX for division
490 div bx ; DX:AX / 10 => AX=quot, DX=rem
491 push dx ; Push digit
492 loop .DivLoop ; Loop to separate all characters
493 xor bx, bx ; First char printed flag
494 mov cl, 5 ; Load number of characters to CL
495ALIGN JUMP_ALIGN
496.PrintLoop:
497 pop dx ; Pop character to DX
498 or bx, dx ; Still skipping zeroes?
499 loopz .PrintLoop ; If so, loop
500 add dx, '0' ; Digit to character
501 PRINT_CHAR ; Print character
502 inc di ; Increment chars printed
[181]503 inc cx ; Characters left
504 loop .PrintLoop ; If so, loop
[2]505 mov dx, di ; Copy chars printed to DX
506 pop cx
507 pop bx
508 pop di
509 ret
510%endif
511
512
513;--------------------------------------------------------------------
514; Prints unsigned 32-bit integer.
[181]515;
[2]516; Print_IntUDW
517; Parameters:
518; DX:AX: 32-bit unsigned integer to print
519; Returns:
520; DX: Number of characters printed
521; Corrupts registers:
522; AX
523;--------------------------------------------------------------------
524%ifdef USE_PRINT_INTUDW
525ALIGN JUMP_ALIGN
526Print_IntUDW:
527 push di ; Store DI
528 push si ; Store SI
529 push bx ; Store BX
530 push cx ; Store CX
531 mov cx, 10 ; Load divider to CX
532 mov si, cx ; Load number of divisions (10) to SI
533 xor di, di ; Zero DI (char counter)
534ALIGN JUMP_ALIGN
535.DivLoop:
536 call Math_DivDWbyW ; DX:AX / 10 => DX:AX=quot, BX=rem
537 push bx ; Push digit
538 dec si ; Decrement number of divisions
539 jnz .DivLoop ; Loop while divisions left
540 xor bx, bx ; First char printed flag
541ALIGN JUMP_ALIGN
542.PrintLoop:
543 pop dx ; Pop character to DX
544 or bx, dx ; Still skipping zeroes?
545 loopz .PrintLoop ; If so, loop
546 add dx, '0' ; Digit to character
547 PRINT_CHAR ; Print character
548 inc di ; Increment chars printed
[181]549 inc cx ; Characters left
550 loop .PrintLoop ; If so, loop
[2]551 mov dx, di ; Copy characters printed to DX
552 pop cx
553 pop bx
554 pop si
555 pop di
556 ret
557%endif
558
559
560;--------------------------------------------------------------------
561; Prints 8-bit byte as hexadecimal string.
[181]562;
[2]563; Print_IntHexB
564; Parameters:
565; AL: 8-bit BYTE to print
566; Returns:
567; DX: Number of characters printed
568; Corrupts registers:
569; AX
570;--------------------------------------------------------------------
571%ifdef USE_PRINT_INTHEXB
572ALIGN JUMP_ALIGN
573Print_IntHexB:
574 push bx
575 xor ah, ah ; Zero AH, AX=word to print
576 mov bx, 0FFh ; Set to print two digits
577 call Print_HexString ; Print hex string
578 push dx ; Store number of chars printed
579 mov dl, 'h' ; Print 'h' at the end
580 PRINT_CHAR
581 pop dx ; Restore number of chars printed
582 inc dx ; Increment for 'h'
583 pop bx
584 ret
585%endif
586
587
588;--------------------------------------------------------------------
589; Prints 16-bit word as hexadecimal string.
[181]590;
[2]591; Print_IntHexW
592; Parameters:
593; AX: 16-bit WORD to print
594; Returns:
595; DX: Number of characters printed
596; Corrupts registers:
597; AX
598;--------------------------------------------------------------------
599%ifdef USE_PRINT_INTHEXW
600ALIGN JUMP_ALIGN
601Print_IntHexW:
602 push bx
603 xor bx, bx ; Set to print all zeroes...
604 dec bx ; ...BX = FFFFh
605 call Print_HexString ; Print hex string
606 push dx ; Store number of chars printed
607 mov dl, 'h' ; Print 'h' at the end
608 PRINT_CHAR
609 pop dx ; Restore number of chars printed
610 inc dx ; Increment for 'h'
611 pop bx
612 ret
613%endif
614
615
616;--------------------------------------------------------------------
617; Prints 32-bit dword as hexadecimal string.
[181]618;
[2]619; Print_IntHexDW
620; Parameters:
621; DX:AX: 32-bit DWORD to print
622; Returns:
623; DX: Number of characters printed
624; Corrupts registers:
625; AX
626;--------------------------------------------------------------------
627%ifdef USE_PRINT_INTHEXDW
628ALIGN JUMP_ALIGN
629Print_IntHexDW:
630 push cx
631 push bx
632 push ax ; Store loword
633 mov ax, dx ; Copy hiword to AX
634 xor bx, bx ; Set to print all zeroes...
635 dec bx ; ...BX = FFFFh
636 call Print_HexString ; Print hex string
637 mov cx, dx ; Copy number of chars printed
638 pop ax ; Pop loword
639 call Print_HexString ; Print hex string
640 add cx, dx ; Add number of chars printed
641 mov dl, 'h' ; Print 'h' at the end
642 PRINT_CHAR
643 inc cx ; Increment number of chars printed
644 mov dx, cx ; Copy number of chars printed to DX
645 pop bx
646 pop cx
647 ret
648%endif
649
650
651;-------------- Private functions --------------------
652
653;--------------------------------------------------------------------
654; Prints hexadecimal character for every nybble for WORD.
[181]655;
[2]656; Print_HexString
657; Parameters:
658; AX: 16-bit WORD to print
659; BX: Mask for zero nybbles to print:
660; FFFFh = prints all digits
661; 0FFFh = does not print digit 3 if it is zero
662; 00FFh = does not print digits 3 and 2 if both are zeroes
663; 000Fh = prints only digit 0 if other are zeroes
664; 0000h = prints nothing if value is zero
665; Returns:
666; DX: Number of characters printed
667; Corrupts registers:
668; AX
669;--------------------------------------------------------------------
670%ifdef USE_PRINT_INTHEXB or USE_PRINT_INTHEXW or USE_PRINT_INTHEXDW
671ALIGN JUMP_ALIGN
672Print_HexString:
673 push bp
674 push di
675 push si
676 push cx
677
678 mov di, ax ; Backup WORD to DI
679 mov cl, 12 ; Prepare to shift nybbles
680 xor dx, dx ; Zero DX
681ALIGN JUMP_ALIGN
682.NybbleLoop:
683 mov bp, bx ; Copy mask to BP
684 mov si, di ; Copy WORD to SI
685 shr bp, cl ; Shift wanted mask nybble to BP
686 shr si, cl ; Shift wanted nybble to SI
687 and bp, 0Fh ; Clear unwanted mask bits
688 and si, 0Fh ; Clear unwanted bits
689 or bp, si ; Skip zeroes in front?
690 jz .SkipPrint ; If so, skip printing
691 mov dl, [cs:si+g_rgbHex]; Load char to DL
692 PRINT_CHAR ; Print character
693 inc dh ; Increment characters printed
694.SkipPrint:
695 sub cl, 4 ; Prepare to shift next nybble
696 jnc .NybbleLoop ; Loop while nybbles left
697 mov dl, dh ; Copy chars printed to DL
698 xor dh, dh ; Zero DH
699
700 pop cx
701 pop si
702 pop di
703 pop bp
704 ret
705%endif
Note: See TracBrowser for help on using the repository browser.