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 call
ed. 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
Multiple -A with Greater Than/Less Than Break Bash Script
Overhead of Supporting Floating Point Arithmetic Inside the Linux Kernel
How to Make a Bash String of Command with Redirect and Pipe
How to Scale Ejabberd Server MAChine on Centos to Handle 200 K Connections
Error Cl_Device_Not_Available When Calling Clcreatecontext (Intel Core2Duo, Intel Ocl Sdk 3.0 Beta)
Why Can't Cuda's Examples Makefile Find the Cuda Libraries
How to Solve "Bash: Ls: Command Not Found"
"Echo" Output Different Answer by Sh and Bash
Hash ("#") Symbol in /Etc/Environment Causes String to Be Split
Can Someone Explain the Shell Shock Bash Code
Unix How to Block Unix/Linux 'Wall' Messaging
Compling C++ Code Using Command Line
After Segfault: Is There a Way, to Check If Pointer Is Still Valid
How to Redirect Cout to Console in Linux
Requirement for Transport Stream Streaming Server
Overriding the Variable in Distro.Conf Config File in Custom Image Recipe Yocto