4

In my assembly language class, our first assignment was to write a program to print out a simple dollar-terminated string in DOS. It looked something like this:

BITS 32
    global _main

section .data
    msg db "Hello, world!", 13, 10, ’$’

section .text
_main:
mov ah, 9
mov edx, msg
int 21h
ret

As I understand it, the $ sign serves to terminate the sting like null does in C. But what do I do if I want to put a dollar sign in the string (like I want to print out "it costs $30")? This seems like a simple question, but my professor didn't know the answer and I don't seem to find it using a google search.

Jason Baker
  • 171,942
  • 122
  • 354
  • 501
  • `BITS 32`?? How are you getting your DOS program to execute in 32-bit mode, but still be able to use `int 21h` DOS calls? If the machine code actually executes in 16-bit mode, the 16-bit `mov r16, imm16` instruction that decodes is a different length than the 32-bit `mov r32, imm32` NASM emitted, so the high 2 bytes of the `msg` address would decode as an instruction. (Possibly `00 00 add [bx+si], al`, using up those 2 bytes exactly, letting execution reach `int 21h`, so it's possible your code happened to work even in 16-bit mode) – Peter Cordes Oct 01 '20 at 23:01

7 Answers7

9

You can't use DOS's 0x09 service to display $ signs, you'll need to use 0x02. See here.

Drew McGowen
  • 10,984
  • 1
  • 27
  • 55
codelogic
  • 65,465
  • 9
  • 56
  • 54
3

Or make your own print_string to print a NULL-terminated string using the undocumented INT 29h (print character in AL).

; ds:si = address of string to print
print_string:
    lodsb                   ; load next character from ds:si
    or al, al               ; test for NULL-character
    jz .end_of_string       ; end of string encountered, return.
    int 29h                 ; print character in AL on screen
    jmp print_string        ; print next character
.end_of_string:
    ret                     ; return to callers cs:ip

(Assuming you are using NASM)

Jonas Engström
  • 4,825
  • 2
  • 35
  • 36
  • 2
    Note that if using INT 29h redirection of stdout won't work (e.g test.com > output.txt) – Jonas Engström Jan 26 '09 at 21:40
  • The preferred idiom is `test al,al` for setting FLAGS according to a register. `or al,al` is slower (on modern x86), and was never better than `test` on historical x86. [Test whether a register is zero with CMP reg,0 vs OR reg,reg?](https://stackoverflow.com/a/33724806). Of course you can also tighten up the loop by putting the load and conditional branch at the bottom, and entering the loop with a `jmp` to the `lodsb`. [Why are loops always compiled into "do...while" style (tail jump)?](https://stackoverflow.com/q/47783926) – Peter Cordes Oct 01 '20 at 22:58
3

I prefer using the write service (AH=0x40):

  • AH=0x40
  • BX is the file handle; use the value 1 to write to the same device (such as the screen) as service AH=9
  • CX is the number of bytes to be written; the data is not "terminated" (neither by NUL nor by $), so all values (from 0 to 255) can be written
  • DS:DX points to the data (in your case: the string) to be written
    (Just like for service AH=9; If you use a 32-bit DOS extension: EDX, of course)

The service is actually intended for writing data to a file; however, it can also be used to write a "string" to the "output" by setting BX to 1.

Martin Rosenau
  • 14,832
  • 2
  • 13
  • 30
0

One way is to find the call that prints a single character. You can print any character with that. Break the string up and print "it costs ", then the '$', and finally, "30". More work, but it gets the job done.

gbarry
  • 9,651
  • 5
  • 29
  • 39
-1

You can use 02 service of INT 21H instead of 09 service.

Here is the sample.

mov dl, '$'

mov ah,02

int 21h
Nomesh DeSilva
  • 1,613
  • 3
  • 22
  • 40
-2

Um. You could write assembly that would taken into account for escaped $, e.g. \$? But then your \ becomes a special symbol too, and you need to use \\ to print a \

Calyth
  • 1,681
  • 2
  • 16
  • 25
  • `int 21h` / AH=9 is a DOS "system call". You don't write the implementation of it, just calls to it. It's the DOS equivalent of ISO C `puts` or `fputs(..., stdout)`, except it annoyingly uses `'$'` instead of `0` as a terminator. See [Why did DOS use dollar-terminated strings?](https://retrocomputing.stackexchange.com/q/7638) on retrocomputing. If you mean writing your own string-output function, then simply don't use `$` terminators in the first place. Making it use `$` but in an incompatible way to DOS has near zero value. – Peter Cordes Oct 01 '20 at 22:56
-5

Try '$$', '\044' (octal) or '\x24' (hex)

Jacek Ławrynowicz
  • 2,600
  • 2
  • 20
  • 23
  • The problem is not that the `$` cannot be specified in a string: Typing `db "This costs $100."` works absolutely well: It will "create" a string with a `$` in the middle. The problem is that in assembly language there is absolutely no possibility to find out if this is only one string or two strings (for example: `"This "` and `"costs $100."`). The function used by the OP uses the `$` to find the end of the string. `"$$"` won't work because the "next string" might begin with a `$`: The two strings `"Hello world."` and `"$100"` would be equal to: `"Hello world.$$100."` – Martin Rosenau Oct 01 '20 at 16:39