INT 21h AH=09h writes the bytes at DS:DX to standard output, one after another, until it meets a "$" (24h); the "$" itself is not printed. It is the first DOS service most people meet, because a message needs nothing but a DB line and three instructions.

prints two lines: "Hello from DOS" and "Costs 5"; the second string ends at its first "$"
MOV DX, OFFSET MSG1
MOV AH, 09h ; DOS: print the string at DS:DX
INT 21h
MOV DX, OFFSET MSG2
MOV AH, 09h ; DOS: print the string at DS:DX
INT 21h
MOV DX, OFFSET CRLF1
MOV AH, 09h ; DOS: print the string at DS:DX
INT 21h
MSG1 DB "Hello from DOS", 13, 10, "$"
MSG2 DB "Costs 5$ and the rest never shows", "$"
CRLF1 DB 13, 10, "$"

No line break is added: put 13, 10 (CR LF) before the "$" when the next output should start on a new line. In a .COM program DS already points at the program, so MOV DX, OFFSET MSG1 is all the setup there is. In an .EXE, DS starts out pointing at the PSP and must be loaded with the data segment first (MOV AX, @DATA / MOV DS, AX).

Printing a "$"

The terminator cannot appear in the text, so a dollar sign is printed separately with INT 21h AH=02h, or the whole text is written with INT 21h AH=40h, which takes a length instead of a terminator:

prints: Price: $5
MOV DX, OFFSET PRICE1
MOV AH, 09h ; DOS: print the string at DS:DX
INT 21h
MOV DL, "$" ; the dollar sign itself
MOV AH, 02h ; DOS: print the character in DL
INT 21h
MOV DX, OFFSET AMOUNT1
MOV AH, 09h ; DOS: print the string at DS:DX
INT 21h
PRICE1 DB "Price: $"
AMOUNT1 DB "5", 13, 10, "$"

Pitfalls

A forgotten "$" does not crash at once: DOS keeps printing whatever follows the string in memory, code included, until some byte happens to be 24h. A screenful of garbage after a message usually means exactly that. The text goes out byte by byte through the CON driver, so control characters act as with INT 21h AH=02h, a Ctrl+C typed meanwhile stops the program in MS-DOS, and with redirection the string goes to the file. DOS returns with AL = 24h; load AH again before the next call.