How to Print Multiple Strings on New Line in the Assembly Language

Assembly code to print a new line string

You are missing the INT 21H call for the first part, that's why only the second is printed. As for the two lines, just append a CR LF to your string. You can also print the whole thing at once, such as:

.MODEL SMALL 
.STACK 100H

.DATA
MSG DB 'Fun', 10, 13, 'Day!$'
.CODE
MAIN PROC
MOV AX, @data
MOV DS, AX

LEA DX,MSG
MOV AH,9
INT 21H

MOV AH,4Ch
INT 21H

MAIN ENDP
END MAIN

How do I print multiple variables in assembly?

print_hex is supposed to called. Otherwise, the ret will fail to return back to where you came from.
That is, the code should look something like:

mov ax, varc1
call print_hex

mov ah, 09
mov dx, offset linefeed
int 21h

mov ax, varc2
call print_hex

mov ah,4ch
int 21h

print_hex:
mov cx,4 ; print 4 hex digits (= 16 bits)
.print_digit:
rol ax,4 ; move the currently left-most digit into the least significant 4 bits
mov dl,al
and dl,0xF ; isolate the hex digit we want to print
add dl,'0' ; and convert it into a character..
cmp dl,'9' ; ...
jbe .ok ; ...
add dl,7 ; ... (for 'A'..'F')
.ok: ; ...
push ax ; save AX on the stack temporarily
mov ah,2 ; INT 21H / AH=2: write character to stdout
int 21h
pop ax ; restore AX
loop .print_digit
ret

printing new lines with printf assembly

NASM accepts strings in single quotes ('...') or double quotes ("..."), which are equivalent, and do not provide any escapes; or in backquotes (`...`), which provide support for C-style escapes, which is what you want.

(See section 3.4.2, "Character Strings", in the documentation.)

To get actual ASCII newlines in your data in memory, rather than literal backslash n:

sentence0: db `Hello\nWorld\n`, 0

Or do it manually:

sentence0: db 'Hello', 10, 'World`, 10, 0

YASM (another NASM-syntax assembler) doesn't accept backticks, so the manual option is your only choice there.

And BTW, you can call puts instead of printf if you don't have any actual formatting in your format string (leave out the trailing newline).

How to make a new line and print the same characters in new line

When you write directly to video memory at B800:<adr>, the address of write decides what the value will be used for (depends on selected graphics(text!) mode of course).

In classic text mode 80x25 the video memory starts at B800:0000, and has size 80*25*2 bytes. 80*25 is probably self explanatory, 1 byte per character (in extended 8-bit ASCII encoding), so why *2? Each character has 1 more byte dedicated for colours. Actually they sit next to each other, so at B800:0000 is the ASCII value of character, and at B800:0001 is stored it's colour attribute.

Character+attribute pairs are stored from left to right row by row, so to write character '*' at position (40,12) (almost centre of screen), you have to write at address (y*160 + x*2) = (12*160 + 40*2) = 2000. The *160 is *80*2 = number of character+attribute pairs per row, ie. size of row in bytes:

mov   BYTE PTR [2000],'*'
mov BYTE PTR [2001],2Eh ; yellow ink, green paper

Or you can shorten that to single WORD write as:

mov   WORD PTR [2000],2E00h + '*'

To print on next line you simply have to adjust the address either by +160 (to move under current character), or by adding the remaining size of row to the current address to get to the first char of next line, like +80 for the 2000 from example to move at the beginning of line 13 (14th line on screen).

See this page for further details about video memory layout:

http://www.shikadi.net/moddingwiki/B800_Text


BTW the more bytes you write at one time, the faster the code runs on real (original) HW, so in your case I would strongly prefer either the WORD write storing the ASCII+attribute pair together, or even DWORD writes (in 32b mode) storing two letters+two attributes with single instruction.

Your code does first write every second byte on even addresses setting the ASCII letters, then it does write every odd byte with colour value. I would do it in this way personally:

mov ax,0B800h
mov es,ax
mov di,<target_address = y*160 + x*2> ; es:di set up
mov ah,<color_attribute_for_whole_string>
; now the print by letter gets here
mov al,'H' ; al = letter, ah = attribute (ax set up)
stosw ; [es:di] = ax, di += 2
mov al,'e' ; change only letter in ax
stosw
mov al,'l'
stosw
mov al,'l'
stosw
...

; the mov al,<char> can be replaced by LODSB
; reading ascii string from ds:si.
; then it would look like:
mov si,<address_of_ascii_string>
lodsb
stosw
lodsb
stosw
... so many times as the string length

print a string on two different lines

Here are your specific problems:

  • You define msg twice (a86 will barf on that).
  • You call int21 fn9 with the same value of msg so you're not printing the two messages out, just two copies of the first.
  • You don't have a newline character in either message so they'll abut each other rather than be on separate lines.

The solutions to those points (without providing the actual code).

  • Label the second message as msg2.
  • Load msg2 into dx before calling int21 for the second time.
  • Change the messages to put a newline before the '$' symbol (or at least the first one).

Update: Since some other helpful soul has already provided source, here's my solution. I would suggest you learn from this and modify your own code to do a similar thing. If you copy it verbatim from a public site for classwork, you'll almost certainly be caught out for plagiarism:

         jmp start                   ; This will start the program

msg db "Hello Word.",0a,"$" ; A string variable .
msg2 db "Michael J. Crawley$" ; A string variable with a value.

start: mov ah,09 ; subfunction 9 output a string
mov dx,offset msg ; DX for the string
int 21h ; Output the message
mov dx,offset msg2 ; DX for the string
int 21h ; Output the message
exit:
mov ah,4ch
mov al,00 ; Exit code
int 21h ; End program

This outputs:

Hello Word.
Michael J. Crawley

How do I print a new line after a number in a loop in x86 assembly?

You should be using the same system call to print the newline that you're using to print the numbers.

Also, newline in Linux is just LF (char 10,) not CR (char 13) followed by LF like Windows/DOS uses.

How to print to stdout on Linux in x86 assembly

This answer describes what each of the arguments does for the Linux print system call, which is what you're calling by raising int 0x80.

System calls use registers to pass in their arguments. From the linked answer, eax is the system call number (4 = print), ebx is the destination stream (1 = stdout), ecx is a pointer to the data to print, and edx is the length of the data to print.

So, the code that's actually doing the print in your loop is:

mov [num], eax  ; Moves the character in eax to the buffer pointed to by num.
mov eax, 4 ; Moves 4 into eax, i.e. selects the print system call.
mov ebx, 1 ; Moves 1 into ebx, i.e. selects stdout as the destination.
mov ecx, num ; Moves the address where your text is stored into ecx.
mov edx, 1 ; Moves the number of bytes to characters (1) into edx.
int 0x80 ; Executes a Linux system call using the above parameters.

To print a newline, you'd just need to have the line feed character (character 10 decimal) in eax before this code rather the character for a number. So, for example, adding mov eax, 10 before this code would print a line feed instead of a number.

How to make this work with your existing loop

num in your "my code" section is the buffer where you're storing the data to print. However, this code is also using this memory in order to keep track of the last number it printed. So, there are two ways around losing that information between loops:

Option 1: Simply increase the size of your buffer from 1 byte to 2 and put the line feed in the second byte. You can then just move 2 into edx instead of 1 to tell Linux that you'd like to print 2 characters, thus printing both the number and the line feed on each loop iteration.

Option 2: Allocate another single-byte buffer to store the line feed. Move the line feed character there, then make a second system call after the system call that prints the number in the loop to print the line feed. If your new buffer were called "lfbuffer", for example, then you would add this code after the int 0x80 line in your existing loop:

mov byte [lfbuffer], 10  ; Moves the a line feed to the buffer pointed to by lfbuffer.
mov eax, 4 ; Moves 4 into eax, i.e. selects the print system call.
mov ebx, 1 ; Moves 1 into ebx, i.e. selects stdout as the destination.
mov ecx, lfbuffer ; Moves the address where your line feed is stored into ecx.
mov edx, 1 ; Moves the number of bytes to characters (1) into edx.
int 0x80 ; Executes a Linux system call using the above parameters.


Related Topics



Leave a reply



Submit