I am using DosBox and I have a string read in from the buffer using an interrupt. I know where the first character is stored in memory, how do I increment to the next character?
0100 mov ah, 0a
0102 mov dx, 111
0105 int 21
0107 mov dl, [113] ;first character here
010b mov ah, 02
010d int 21
010f int 20
0111 db 0f
The question is how do I increment to the next character in the string? If I input the string "Hello" and then use inc dl it simply gives me the letter "I" instead of "e".
You need to pick a register to point at the current character. Once you have this there are instructions to advance your pointer as well as instructions to read the byte the pointer is pointing to.
Since you don't know how many characters will be read by the user input function, and you want this function to work with any input value, you'll need some sort of loop so you only print as many characters as have been read, although if you wanted to make it simple, you could make it only print the first 3 characters regardless of what was typed in. I'm going to assume you want to print out exactly what was typed by the user.
A good way to tackle this is to use the LOOP instruction which repeats (jumps into) the loop body by the count that has been placed into the CX register. Since the user input function gives us the character count, all we have to do is read that into the lower portion of the CX register (CL) for loop initialization. We'll also need a pointer to point to the current character. Once in the loop body, we'll just read whatever character is at our "current character" pointer and then call the DOS character-print function to output it to the console. Then we can advance the pointer and LOOP until all characters have been done. Note that each time the LOOP instruction is encountered, CX is decremented until it reaches zero. Once it is zero, LOOP no longer jumps back into the body and simply advances to the instruction following the LOOP.
NOTE: If you don't output a CRLF after reading the user input, there will be no line feed and the new output will overwrite where the input was read on the console. Effectively it will look like nothing happened.
Here's your modified sample:
0100 MOV AH,0A
0102 MOV DX,0123
0105 INT 21 ;input string using buffer at 0123
0107 MOV DX,0120
010A MOV AH,09
010C INT 21 ;output CRLF sequence first
010E MOV SI,0124 ;point SI at byte containing chars read
0111 XOR CX,CX ;CX = 0
0113 MOV CL,[SI] ;CX = chars_read
0115 INC SI ;mov SI to next char to display
0116 MOV DL,[SI] ;DL = character SI is pointing at
0118 MOV AH,02
011A INT 21 ;display character
011C LOOP 0115 ;loop back to INC instruction until no more chars left
011E INT 20 ;exit
0120 DB 0D ;CR
0121 DB 0A ;LF
0122 DB 24 ;"$" DOS string terminator
0123 DB 20 ;buffer start; max characters = 32
0124 DB 00 ; chars read goes here
0125 DB 00 ; input chars are read here
Related
I'm currently struggling in outputting AzByCx in ms debug since I don't really know too much about it.
Included here are the Basic commands that my teacher sent to us.
I can output A-Z easily, but not AzBxCy, there's not much of a detailed tutorial online so I came here to ask.
-e 200 "Az"
-a
1BD8:0100 mov bx, word [200]
1BD8:0104 mov cx, 3
1BD8:0107 mov ah, 2
1BD8:0109 mov dl, bl
1BD8:010B int 21
1BD8:010D mov dl, bh
1BD8:010F int 21
1BD8:0111 add bx, FF01
1BD8:0115 loop 109
1BD8:0117 mov dl, 0D
1BD8:0119 int 21
1BD8:011B mov dl, 0A
1BD8:011D int 21
1BD8:011F mov ax, 4C00
1BD8:0122 int 21
1BD8:0124
-g
AzByCx
What does this do?
initialises word at ds:200h to the string "Az" (little endian low byte is "A", high byte is "z")
use A command to assemble the following program:
load from memory into register bx (to initialise the register without having to look up the numeric ASCII codepoints)
set up cx as loop counter for three iterations
set up register ah for the interrupt 21h service 02h (display character in dl)
display character read from bl (low byte of bx)
display character from bh (high byte of bx)
add 1 to bl and -1 (in two's complement resulting in 0FFh) to bh which increments the codepoint in bl and decrements the one in bh
loop back to display subsequent letters
display a linebreak (codepoints 13 and 10, or 0Dh 0Ah) to make the output easier to read
terminate program
use G command to run the program
Microsoft DEBUG.EXE can indeed be used to write a simple program in Intel-syntax assembly, see this example. Replace the string definition db "Hello world$" with db "AzByCx$" and you're done.
However, this is definitely not a good way how to learn assembly language.
Find a better teacher.
I've tried reading about this all over the internet but here it is my problem. I am given a string of doublewords.
I have to order in decreasing order the string of the low words (least significant) from these doublewords. The high words remain unchanged.
For ex: strin DD 12345678h 1256ABCDh, 12AB4344h
the result would be 1234ABCDh, 12565678h, 12AB4344h.
Now I tried my best writting some code but it's not working properly, my insertion procedure. If you could take a look and tell me what I'm doing wrong, I'd be greatful.
I tried running it in td mode but I just can't figure out.
assume cs:code, ds:data
data segment
s dd 12345678h, 1256ABCDh, 12AB4344h
ls equ ($-s)/4 ;this is supposed to be the length of my source string
d dd ls dup (?) ;this is my destination string
aux dw ?
aux2 dw ?
data ends
code segment
insert proc
push di ;here I use the stack to get more free registers
push cx
cmp di, offset d ;if di=offset d it means that I didn't store any number yet
je addPrim
std ;we plan on working form right to left on the string for the next part
mov cx, di
sub cx, offset d ;here I find out with how many words I have to compare the word from AX
dec di
dec di ;since I work with doublewords, for some reason I thought I should decrease di
dec di ;3 times but here my procedure gets fuzzy and doesn't work properly anymore
repeta1: ;this repeat is supposed to compare the word from AX with the rest of the least
scasw ;significant words from es:di
jge DIplus2 ;if my number from AX is bigger or equal than what's in es:di, I increment
;di twice and store it
mov bx, word ptr es:[di+1] ;this part is supposed to interchange words but it's not
;working how I planned so I don't know how to change it
mov word ptr es:[di+2], bx
loop repeta1
jmp DIplus1
DIplus2:
inc di
DIplus1:
inc di
addPrim: ;this label just adds the first word in the destination string
stosw
pop cx
pop di
inc di
inc di
cld
ret
insert endp
start:
mov ax, data
mov ds, ax
mov es, ax
mov si, offset s
mov di, offset d
mov cx, ls ; store in cx the length of the strings
jcxz exit
repeta:
lodsw ;because of little endian, my first word will be my least significant word in the
;in the doubleword so right after it is moved in ax, i apply the procedure insert
call insert
lodsw ;here it moves in ax my most significan word in the dd, so i auto store it
stosw ;in my destination string
loop repeta
exit:
mov ax, 4c00h
int 21h
code ends
end start
ls equ ($-s)/4 ;this is supposed to be the length of my source string
This actually calculates the number of elements.
mov cx, di
sub cx, offset d ;here I find out with how many words ...
At the second invocation of your insert proc this will set CX=4 which is too big given a list of only 3 values. I suggest you divide CX by 4.
dec di
dec di ;since I work with doublewords...
dec di ;3 times but here my procedure gets fuzzy
This is certainly wrong. SCASW indicates you either decrement by 4 or not decrement at all!
mov bx, word ptr es:[di+1] ;this part is supposed to interchange words...
mov word ptr es:[di+2], bx
This cannot work since the offsets are only 1 byte apart!
jmp DIplus1
This yields an single increment of DI and thus an error because you want to store a word at that spot.
I have an array of nine names:
.model tiny
.data
vardas1 db "Rokas",0ah,'$'
vardas2 db "Tomas",0ah,'$'
vardas3 db "Matas",0ah,'$'
vardas4 db "Domas",0ah,'$'
vardas5 db "Augis",0ah,'$'
vardas6 db "Vofka",0ah,'$'
vardas7 db "Marka",0ah,'$'
vardas8 db "Auris",0ah,'$'
vardas9 db "Edvis",0ah,'$'
vardai dw offset vardas1, offset vardas2, offset vardas3, offset vardas4, offset vardas5, offset vardas6, offset vardas7, offset vardas8, offset vardas9
.code
org 100h
I need to read a digit from keyboard, and then I need to print that name. For example I will push 5, and console should write "Augis". BTW, second code block aren't all code, just loop that doesn't work
paieska:
mov dx, offset _comment1 ; Just string name asking user to input digit
mov ah, 9
int 21h
mov j, 00h ; Trying to input the digit from keyboard
mov ah, 01h
mov dl, 0ah
int 21h
mov bx, offset vardai ; Add array "names" to bx register
add bx, cx ; Add cx for indexing
mov dx, [bx] ; Add first array element to dx register
add cx, 2 ; Increasing cx by 2, because I'm using data word not data byte
mov ah, 9 ; Try to print it
int 21h
cmp cx, j ; Try to compare cx (index of array) to mine inputed digit "j"
jne paieska
je end
mov ah, 01h
mov dl, 0ah ;NO NEED FOR THIS - INT21/01 DOES NOT USE DL
int 21h
MOV AH, '1' ; MIN INPUT CHAR
mov bx, offset vardai ; Add array "names" to bx register WELL, ASSIGN ACTUALLY
MOV CX,2 ;NUMBER OF BYTES TO ADD (WORDS, NOT BYTES)
LOOPN:
mov dx, [bx] ; name-pointer array element to dx register
CMP AH,AL ; MATCHING char?
JE PNAME ; YES, PRINT NAME
add bx, cx ; Add cx=2 for next name
inc AH ; next possible character input
CMP AH,'9'+1 ; allowed is '1'..'9'
jne loopn ; in allowed range
; input not 1..9
mov dx, offset errormessage
PNAME:
mov ah, 9 ; Try to print it
int 21h
jmp end
Well, I tried to edit your approach with CAPS, but it became too complicated.
Essentially, you are reading a character from the keyboard using function 01. This character arrives in AL. If all goes well, it should be '1'..'9'. Notice these are the ASCII characters '1'..'9', that is hex 31..39
Next step is to set BX to the start of the table, AH to the minimum character you anticipate and CX to 2 because the table contains words, not bytes.
Now we have a loop. Load X from the table, and check whether AL is equal to AH. If the user input 1, these will be equal, so go print the string.
Otherwise, add 2 to BX to point to the next entry in the table (this could have been done by ADD BX,2 or INC BX INC BX which would mean the MOV CX,2 would be unnecessary - just the way I wrote it...) and increment the '1' in AH to '2'.
The end-condition for the loop is when AH gets incremented from '9' to - well, ':' or '9'+1. If it hasn't reached that end-condition, then run around the loop until all of the values '1'..'9' have been tested. If you haven't got to PNAME yet, then there's an error because the character input wasn't allowed, so point to an error message and then print it.
Now jumping to the end - probably you'd want to terminate the program, so you'd execute
MOV AH,4CH
INT 21H
I am trying to acces a specific bit and modify it.
I have moved 0x01ABCDEF (hex value) into ecx and want to be able to check bit values at specific position.
For example I must take byte 0 of 0x01ABCDEF (0xEF)
check if bit at position 7 is 1
set the middle 4 bits to 1 and the rest to 0.
Under x86 the most simple solution is using bit manipulation instructions like BT (bit test), BTC (bit test and complement), BTR (bit test and reset) and BTS (bit test and set).
Bit test example:
mov dl, 7 //test 7th bit
bt ecx, edx //test 7th bit in register ecx
Remember: only last 5 bits in register edx is used.
or
bt ecx, 7
In both cases the result is stored in carry flag.
It's been years since I've done asm, but you want to and your value with 0x80 and if the result is zero your bit is not set so you jump out, otherwise continue along and set your eax to the value you want (I assume the four bits you mean are the 00111100 in the fourth byte.
For example (treat this as pseudo code as it's been far too long):
and eax, 0x80
jz exit
mov eax, 0x3C
exit:
Most CPUs do not support bit-wise access, so you have to use OR to set and AND to clear bits.
As I'm not really familiar with assembly I will just give you C-ish pseudocode, but you should easily be able to transform that to assembly instructions.
value = 0x01ABCDEF;
last_byte = value & 0xFF; // = 0xEF
if (last_byte & 0x40) { // is the 7th bit set? (0x01 = 1st, 0x02 = 2nd, 0x04 = 3rd, 0x08 = 4th, 0x10 = 5th, 0x20 = 6th, 0x40 = 7th, 0x80 = 8th)
value = value & 0xFFFFFF00; // clear last byte
value = value | 0x3C; // set the byte with 00111100 bits (0x3C is the hex representation of these bits)
}
Not that you can remove the last_byte assignment and directly check value & 0x40. However, if you want to check something which is not the least significant part, you have to do shifting first. For example, to extract ABCD you would use the following:
middle_bytes = (value & 0xFFFF00) >> 8;
value & 0cFFFF00 gets rif og the more significant byte(s) (0x01) and >> 8 shifts the result left by one byte and thus gets rid of the last byte (0xEF).
I have an assignment from my comp. system org. subject and unfortunately I'm kind of new when it comes to assembly language. I'm supposed to write a program that displays the numbers 0,2,4,6,8,10 respectively. How would I go about this?
Maybe this'll answer my question: (Reactions please)
.model small
.stack 100H
.data
.code
call proc
mov cx,5
mov dx,0
L1:
mov bx,2
add dx,bx
mov ah,02h
loop L1
int 21
endp
Go see your lecturer and/or tutor and ask for advice. That's what they're there for. You haven't given us anywhere near enough info to help you out.
Here's what I think your ABCD program should look like. I suggest you use it as a baseline to try to make a 0 2 4 ... version.
model proc
.stack 100H
.data
.call
main proc
mov cx,10 ; 10 loops only.
mov dx,40h ; start dx at 'A' - 1.
L1:
inc dx ; move to next character.
mov ah,02h ; int 21,02 is print character.
int 21h
loop L1 ; loop until cx is 0
mov ax,4c00h ; int 21,4c is exit with al holding exit code.
int 21
endp
When you've at least had a go at converting this, post the code and we'll critique what you've done.
If you're taught something, it never lasts but, if you learn something, it lasts forever (alcohol-addled braincells notwithstanding :-).
Int 21 is the DOS interrupt which allows assembler programs to use various DOS functions. It's conceptually a huge switch statement based on the AH register which is why you'll see things like Int 21 Fn 02, which means execute mov ah,2 followed by int 21.
Int 21 Fn 02 will take the contents of DL and output that to the screen. So the sequence:
mov ah,02h
mov dl,41h
int 21h
will output the 'A' character (0x41).
Similarly, Int 21 Fn 4c will exit the current running process.
I'm sure your class gave you some education here.
Can you code enough assembly to print one or two numbers?
Can you code enough to calculate the numbers, even if you can't print them?
Post that much code, and you may find help here.
Otherwise, you're asking others to actually do your homework for you.
Assembly language is a symbolic representation of the numeric machine codes and other constants needed to program a particular CPU (or architecture). So assembly language for Macs (most recently Intel's X86) is different from that used to on the iPhone - ARM.
Your teacher is also probably expecting you to realise the difference between the binary form of the number you will count with, and the ASCII format you will use to display to the screen.
You do know there is more than one flavor of "Assembly Language."
You can do it exactly like the program which prints A, B, C, D, etc.: except that instead of starting at 'A', start at '0; and instead of increasing by 1 each time (from 'A' to 'B'), increase by 2 (from '0' to '2').
After printing '0', '2', '4', '6', and '8', the next number that you want to print is '10'.
To print '10', you can print '1' followed by '0'. Or, instead of invoking int 21 with ah=2 (which prints one character at a time), you can set ah=9 to print a string (set ds:dx to a block of memory which contains "10$").
Later you suggested the following solution and asked for criticism:
.model small
.stack 100H
.data
.code
main proc
call defineuser1
call defineuser2
mov cx,5
userdefine1 proc
L1:
mov dx,0
mov bx,2
add dx,bx
mov ah,02h
loop L1
int 21h
endp
userdefine2 proc
mov ah, 4ch
int 21h
userdefine2
endp
My criticisms are as follows:
defineuser1 doesn't exist (I think you mean userdefine1)
setting cx needs to be inside (not before) the procedure
invoking int 21 needs to be inside (not outside) the loop
you need special handling for "10" as I mentioned above
There's a difference between '0' (the ASCII character/digit) and 0 (the number) ... you need to print the character/digit, not the number
You need to learn to test your code (write it, step through it with debugger, and debug it), preferably before you post questions about it.
You would have a counter beginning at zero and repeatedly increment it by two, printing the result.
.model small
.stack 100H
.code
.data
var2 DB "10$"
main proc
mov cx,4
mov ax,0
mov dl,al
add dl,30h
mov ah,02h
int 21h
mov ax,0
var1:
add ax,2
mov dl,al
add dl,30h
mov bx,ax
mov ah,2h
int 21h
mov ax,bx
loop var1
mov ax,#data
mov ds,ax
mov dx,offset var2
mov ah,09h
int 21h
main endp
end main
I'm new in computer science and when i saw this question i just wanted to try it. I have managed in doing it and here is the code
MOV AX, 0
MOV BX, 2
ADDLOOP:
ADD AX, BX
CMP AX, 10
JE DONE
JMP ADDLOOP
DONE:
Ok. That's my best attempt. Lots of details left out. I should also mention that I have no frigging clue how to print a char to the screen.
Like others have mentioned, you didn't specify which assembly language so I chose x86.
Finally, go talk to your instructors, they'll help you much more than we can.
Are you using a macro for the output?
should be something like...
mov eax, 0
myloop: cmp eax, 10
jg done
output macro eax
add eax, 2
jmp myloop
done:
of course that's for 8086 assembly.