0

I'm using TASM, TLINK and TD (debugger) in DOSBox.

I've recently tried programming a simple ASM 8086 program that is suppose to print the value at address 0100h. When I print the result I get output that resembles:

enter image description here

My code is:

.MODEL SMALL
.STACK
.DATA
.CODE
.STARTUP

MOV SI,0100H
MOV WORD PTR[SI],31
MOV DX,0
MOV AH,09H
MOV DX,[SI]
INT 21H

MOV AH,4CH
INT 21H

END
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Danielix
  • 5
  • 7
  • And i'm using also DOSBOX – Danielix Apr 21 '16 at 20:39
  • 3
    What do you expect it to print? – Leandros Apr 21 '16 at 20:40
  • 31 that i've moved before in dx, but it print msdos error --symbols-- write failed, disk full? but my disk isn't full – Danielix Apr 21 '16 at 20:45
  • thanks but i MUST print a value of a memory adress example: 0100h – Danielix Apr 21 '16 at 20:48
  • Then set `DX` to that value. – Leandros Apr 21 '16 at 20:49
  • for that i moved that value in DX – Danielix Apr 21 '16 at 20:49
  • What do you expect to be at address `0100H`? – Leandros Apr 21 '16 at 20:50
  • i'm expecting to find 31 and next i'm expecting that in dx theres 31 and with the TURBODEBUGGER i've checked that in the DX reg theres 31 but when i print it, no results. – Danielix Apr 21 '16 at 20:52
  • As said in my answer, it'll print the `$` terminated **string** at the address of `DX`. 31 corresponds to some unprintable value (in ASCII) and since it's not terminated it'll print whatever it finds after that address until it encounters a `$`. – Leandros Apr 21 '16 at 20:55
  • and what i can do to print 31 directly from the memory? – Danielix Apr 21 '16 at 20:55
  • You would have to write a routine to do it for you. There is no built-in support for printing hex values. See [here](http://stackoverflow.com/questions/3853730/printing-hexadecimal-digits-with-assembly) or [here](http://stackoverflow.com/questions/18879672/printing-hex-values-in-x86-assembly). – Leandros Apr 21 '16 at 20:59
  • since i'm trying to do: MOV DX,[SI]+'$' And for now it printed no strange symbols but no 31.... i'm trying. But you have obiuvsly reason – Danielix Apr 21 '16 at 21:04
  • The literal number 31 will be printed as an ascii character, and 31 in ascii is a non-printable control character. If you, for example, put 64 in, it'll print an @ sign. [Here is a complete ascii table.](http://ascii.cl/). – Leandros Apr 21 '16 at 21:16
  • YAH, BUT I'M TRYING TO ADD THAT damn $ to the end of the dx........**crying*** – Danielix Apr 21 '16 at 21:29
  • The `$` has to be at location 101H. – Leandros Apr 21 '16 at 21:34

2 Answers2

4

Invoking the 21H interrupt with AH set to 09H, will print the $ terminated string in register DX. In your case DX contains 31H, which will point (I assume) to garbage, that's why you're getting random symbols printed.

Create the string you want to print inside your data section, and make the DX register point to it, before invoking the print syscall.

Leandros
  • 16,805
  • 9
  • 69
  • 108
  • In other words, this interrupt prints to the screen until it sees a `$`, regardless of whether it makes sense or not. There doesn't appear to be an `$` in the output, but we may not have seen all of it (or it doesn't all fit on the screen). – puppydrum64 Dec 20 '22 at 11:36
  • 2
    @puppydrum64 The dollar sign is **the terminator**. It never appears in the output, because it is taken to indicate the end of the data to display. – ecm Dec 20 '22 at 12:43
  • Oh yeah, I forgot. – puppydrum64 Dec 20 '22 at 12:45
3

I think there's a bit of a misunderstanding here that hasn't been addressed. A number as stored in computer memory can't be printed as-is. In assembly, the number must be converted to a string, and you need to print that string. Thankfully, this is fairly simple, as ASCII was designed so that the string representation of any single-digit base 10 number 0 through 9 is its value plus 0x30. That is to say, '0' = 30h, '1' = 31h, etc. The only problem is that by default a number is stored as binary, which means that it's far easier to print a number as hexadecimal than as decimal. The conversion factor for a single hex digit of the form 0x0n to its ASCII value is quite easy:

; AL = the digit you wish to print, ranges from 0x00 to 0x0F
cmp al,0Ah
jb noCorrectHex
add al,7    ; converts 0x0A to 0x41 = 'A' , 0x0B to 0x42 = 'B', etc. 
noCorrectHex:
add al,'0'  ; evaluates to "add al,30h"

If you start with the leftmost digit of your word (after loading it from memory into a register) and proceed from left to right you can print the digits in order using this method. I use the DOS interrupt for printing a single character, not a string.

PrintHex:
; AL = the byte you wish to print.
; this prints the hexadecimal representation of a byte to the screen.

push ax
   shr al,1
   shr al,1
   shr al,1
   shr al,1
   call PrintHexChar
pop ax
and al,0Fh

;fallthrough is intentional

PrintHexChar:
cmp al,0Ah
jb noCorrectHex
   add al,7    ; converts 0x0A to 0x41 = 'A' , 0x0B to 0x42 = 'B', etc. 
noCorrectHex:
add al,'0'  ; evaluates to "add al,30h"

mov ah,02h
mov DL,AL
int 21h
ret

And here's another way to perform the math to convert a single hex digit to ASCII, it's a bit hard to follow what's going on but it doesn't require any branching (I'm not sure if this is better or worse than just doing the more readable version, but you get the same result)

PrintHexChar:
and al,0Fh ;clear C and A flags. Required even if top 4 bits are already 0. 
daa
add al,0F0h
adc al,40h

mov ah,02h
mov DL,AL
int 21h
ret
Sep Roland
  • 33,889
  • 7
  • 43
  • 76
puppydrum64
  • 1,598
  • 2
  • 15
  • `and al,0F0h` is not needed because the `shr` uses will also do away with the low 4 bits. – ecm Dec 20 '22 at 12:47
  • Originally my code said `clc daa ...` but I kept getting the wrong value for the top nibble only, even though the same code printed the bottom nibble correctly. I came to the conclusion that the `shr` was setting the aux carry and having the `and al,0F0h` fixed it. – puppydrum64 Dec 20 '22 at 12:50
  • I don't mean `and al, 0Fh`. I meant the `and al, 0F0h` **before the shifts**. You have `and al, 0Fh` before your `daa` so the `and al, 0F0h` can't affect the AC for `daa`. – ecm Dec 20 '22 at 12:54
  • Derp. I'm used to Z80 where `and &F0 rrca rrca rrca rrca` is faster and takes fewer bytes than 4 shifts. – puppydrum64 Dec 20 '22 at 12:55
  • 2
    code golf [Little Endian Number to String Conversion](https://codegolf.stackexchange.com/a/193842) shows (and explains) another 4-instruction 7-byte sequence for converting a 4-bit integer to hex. But if the top 4 bits are already 0, it's only 3 instructions, 5 bytes: `cmp al, 10` ; `sbb al, 0x69` ; `das` since it sets AC and other flags before running a BCD instruction – Peter Cordes Dec 20 '22 at 20:00