News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Why does the second string output produce garbage?

Started by bugthis, July 08, 2024, 11:29:54 AM

Previous topic - Next topic

bugthis

Here's the code:

Code (asm) Select
.MODEL SMALL, C

.DATA
  preTest DB 1 DUP(1)  ; if i comment this line, then the output seems to be partly correct. But
                       ; with this line, it isn't. But why? 
    ; (EDIT: DUP is fixed now in the forum code here. It was DUB before. But in the actual program code it was always DUP. )
  VbeInfoBlock DB "VBE2"  ; Vesa VBE 2.0 needs the string VBE2 to detect the VbeInfoBlock
  vesastring DB 508 DUP(48)  ; the VbeInfoBlock needs 4 + 508 = 512 bytes in total.
  ENDL DB "EOL", 10, 13, '$'  ; The ENDL and its '$' is a safety feature.
                              ; I use it also to end the output of the first comment.

.STACK 256

.CODE
start:
  MOV AX, @DATA   ; setup DS and ES
  MOV DS, AX
  MOV ES, AX

  MOV DX, OFFSET VbeInfoBlock
  MOV AH, 09h
  INT 21h         ; first output of VbeInfoBlock

  MOV DI, DX  ; There is probably a better way to setup ES:DI, but this seems to work.
              ; ES:DI needs to point to the beginning of the VbeIinfoBlock starting with VBE2
              ; before calling int 10h. This does the trick.
  MOV AX, 4F00h  ; VESA VBE function 00h
  INT 10h

  MOV DI, 00h    ; setting DI back to 0 manually.
  MOV AX, 0900h

  ; Information:
  ; At this point everything looks fine in debug, when i dump the memory contents with "d es:dx"
  ; which points to VbeInfoBlock the dump output shows the string "VESA" like it should.
  ; this also means the Vesa function call did work. VBE2 is changed to VESA like it should.
  ; AH and AL are also swapped after calling int 10h, as they should be.

  ; The following interrupt should display this string, like it did before, but it doesn't.
  ; And that regardless of the fact, that DX, AX, DI all have the same values as in the first output.
  INT 21h     ; second output of VbeInfoBlock displaying garbage

  MOV AX, 4C00h
  INT 21h     ; return to DOS
  end start

The second output produces garbage. See the screenshots.
When the preTest variable is commented, the output seems to be much better.

Output of vesa.exe. The second output (after EOL) is wrong.


In debug a dump of ds:dx shows the correct values:


But the output is still broken:


NoCforMe

At first glance:
You set DX = OFFSET VbeInfoBlock before two interrupt calls (INT 21h and INT 10h), by which time DX is, I'm pretty sure, trashed. (I forget just what the ABI specification is for DOS INT 21 calls, but I think only SI, DI and (maybe) BX are preserved, but certainly not AX, CX or DX.)

Try setting DX to the offset of what you want to print just before that INT 21h/AH = 9 and see if that works better.

Also, shouldn't your preTest DB 1 DUB(1) be preTest DB 1 DUP(1)? And if DUP is 1, there's no need for it anyhow. Just write preTest DB 1 if that's the value you want.
Assembly language programming should be fun. That's why I do it.

bugthis

Quote from: NoCforMe on July 08, 2024, 12:01:59 PMAt first glance:
You set DX = OFFSET VbeInfoBlock before two interrupt calls (INT 21h and INT 10h), by which time DX is, I'm pretty sure, trashed. (I forget just what the ABI specification is for DOS INT 21 calls, but I think only SI, DI and (maybe) BX are preserved, but certainly not AX, CX or DX.)

Try setting DX to the offset of what you want to print just before that INT 21h/AH = 9 and see if that works better.
I had a
MOV DX, OFFSET VbeInfoBlock
at the beginning in my code before all int 21h calls, but DX never changed. And the error still occurred. Since DX remained the same all the time and the command was therefore not necessary, I removed it to save memory.
So this isn't the reason for the error.


QuoteAlso, shouldn't your preTest DB 1 DUB(1) be preTest DB 1 DUP(1)?
The DUB is just a transmission error when writing the code for the forum. Unfortunately, I can't use copy & paste from QEMU. The program compiles, in the code it is DUP like it should be. See the running program in the screenshots. With DUB it wouldn't compile.
But i will correct it above. So thanks for the hint.

QuoteAnd if DUP is 1, there's no need for it anyhow.
That's not the point. I had DUP = 80 before for testing purposes. I just decreased it to the absolute minimum to still be able to trigger the bug.

sinsi

What are you trying to print? If you're trying to dump the bytes you need to convert them to characters first.
DOS function 9 prints a string of characters, a byte of 00 would be converted to two characters, 3030h.
Also, the function needs a $ to terminate the string.

bugthis

#4
Quote from: sinsi on July 08, 2024, 12:35:48 PMWhat are you trying to print? If you're trying to dump the bytes you need to convert them to characters first.
DOS function 9 prints a string of characters, a byte of 00 would be converted to two characters, 3030h.
The program is still in its early stages. I actually just wanted to output the VBE info quick & dirty. The program is not finished yet, values are not  yet extracted correctly from the info block, but it should at least display the string "VESA" correctly and start the output at VESA and end at the $ of ENDL at the latest. It doesn't do that at the moment as long as the preTest is not commented.

QuoteAlso, the function needs a $ to terminate the string.
There is an $, see the ENDL. It's an old programming trick.

EDIT
The variable names are for the assembler only. In principle, all bytes are a single string up to the first $.
So this:
str1 db "This "
str2 db "is "
str3 db "a "
str4 db "long "
str5 db "string. "
str5 db "With "
str6 db "an "
str7 db "end."
str8 db 13, 10, "$'

becomes:
MOV DX, OFFSET str1
MOV AH, 09h
INT 21h
with an output of:
This is a long string. With an end.

But you can also do this:
MOV DX, OFFSET str5
MOV AH, 09h
INT 21h
This will result into:
With an end.

So basically these are not string variables like in high level programming languages like C that require some sort of terminator (in C '\0'). This is assembly. You only need a terminator somewhere at the end. However, not every character string assigned to a variable name needs such a terminator. It is sufficient if one comes later in the data segment.

sinsi

Well according to your rules, the output is correct. DOS is printing the bytes, all 512 of them.
Bytes from 00-1F are not characters as such, e.g. 13 (0Dh) is a carriage return, 07 is a beep, ...

bugthis

Quote from: sinsi on July 08, 2024, 12:46:05 PMWell according to your rules, the output is correct. DOS is printing the bytes, all 512 of them.
Bytes from 00-1F are not characters as such, e.g. 13 (0Dh) is a carriage return, 07 is a beep, ...

Have you even looked at the DEBUG output? Screenshot 2
The rule should be that everything from DS:DX up to the first $ character is output. But that is not the case.
What should actually be output can be seen in the screenshot with the DEBUG, the DEBUG dump starts with VESA.
But the program output does not start with VESA instead it starts with garbage.
And now explain to me why it doesn't output the data range from DS:DX until $ correctly, even though DS and DX point to the right place.

And one more thing. Of course, the VESA query could insert a $ somewhere before and then the output would end there. But it should at least start with VESA.

sinsi

You are looking at a hex dump of bytes. Debug has converted the buffer of bytes into characters for you.
The VESA string is printed, but if you look at the byte at 0982:0035 it is 0dh which, when printed by DOS, is the CR (carriage return) character, so the cursor moves to the start of the line, then DOS prints the next characters overwriting what's there.

bugthis

Thanks, I just found that out too.

So this answers my question.

BTW:
QuoteYou are looking at a hex dump of bytes. Debug has converted the buffer of bytes into characters for you.
I know that. This is obvious. When I say it should output the stuff from the dump, I mean just the string.

BugCatcher

3. INT 21h Function 09h: Write a $-terminated string to standard output
␁ The string must be terminated by a '$' character.
␁ DS must point to the string's segment, and DX must contain the string's offset:
.data
string BYTE "This is a string$"
.code
mov ah,9
mov dx,OFFSET string
int 21h

bugthis

Quote from: BugCatcher on July 08, 2024, 11:48:38 PM3. INT 21h Function 09h: Write a $-terminated string to standard output
␁ The string must be terminated by a '$' character.
␁ DS must point to the string's segment, and DX must contain the string's offset:
.data
string BYTE "This is a string$"
.code
mov ah,9
mov dx,OFFSET string
int 21h

This is the same, as this:

.data
string DB "T"
hello DB "his "
world DB "is "
maria DB "a "
loves DB "string"
me DB "$"
.code
mov ah,9
mov dx,OFFSET string
int 21h

So, no, not every string requires a terminator. It's enough when there is a following string in the row that has a terminator and when all the following data belongs to the one, that you want to print.

NoCforMe

Quote from: bugthis on July 09, 2024, 12:49:22 AMSo, no, not every string requires a terminator.
Wrong.
Every string does require a terminator. How that terminator gets put there is up to you, the programmer, but it's got to be there, otherwise you'll get a "run-on" string, most likely w/garbage at the end.

But you knew this.
Assembly language programming should be fun. That's why I do it.

zedd151

Quote from: bugthis on July 09, 2024, 12:49:22 AMSo, no, not every string requires a terminator.
Since there is one terminator, everything before it is considered to be part of the same string - no matter whether or not you declare each part of the string with its own variable as you have done.

True you may print any portion of the string (from a given "variable" address to terminator) that has a variable name as in your example but it will always rely on the fact that a terminator is present.

 :rolleyes:
:azn:

NoCforMe

Assembly language programming should be fun. That's why I do it.

bugthis

Quote from: NoCforMe on July 09, 2024, 02:56:58 AM
Quote from: bugthis on July 09, 2024, 12:49:22 AMSo, no, not every string requires a terminator.
Wrong.
Every string does require a terminator. How that terminator gets put there is up to you, the programmer, but it's got to be there, otherwise you'll get a "run-on" string, most likely w/garbage at the end.

But you knew this.
You obviously didn't understand what i wrote.