Printing individual characters from a string to standard output in ms-dos/assembly x86-16 - dos

I am new to x86-16bit programming. I am very stumped by how I call function 2 properly to read characters individually from a string. If anybody has any ideas it would be greatly appreciated. The code below shows my current attempt (one of many).
.model small
.data
message db "Hello, DOS Here!", 0dh, 0ah
.code
main proc
mov ax, #data
mov ds, ax
L1:
mov ah, 2
mov bx, 1
int 21h
loop L1
.EXIT
main endp
end main
I'm also supposed to use push and pop to be able to print the string in the same method but backwards. I'm sure I'm missing something obvious. All I get when it prints is the British pound symbol for a few lines.
(Dec: 156; Hex: 9C)

With this definition message db "Hello, DOS Here!", 0dh, 0ah, next code will print the complete message:
mov ah, 02h
mov dl, message ;Get the H character
int 21h
mov dl, message+1 ;Get the e character
int 21h
mov dl, message+2
int 21h
mov dl, message+3
int 21h
mov dl, message+4
int 21h
mov dl, message+5
int 21h
mov dl, message+6
int 21h
mov dl, message+7
int 21h
mov dl, message+8
int 21h
mov dl, message+9
int 21h
mov dl, message+10
int 21h
mov dl, message+11
int 21h
mov dl, message+12
int 21h
mov dl, message+13
int 21h
mov dl, message+14
int 21h
mov dl, message+15
int 21h
mov dl, message+16 ;Get the 0Dh carriage return
int 21h
mov dl, message+17 ;Get the 0Ah linefeed
int 21h
Rather stupid, wouldn't you say?
In order to use a loop and obtain much compacter code we need to:
Put the address of your message in an address register. You can choose from SI, DI, BX and BP. I've picked BX in below code.
Read one byte at this address.
Output the byte with DOS.
Increment the pointer in the address register.
Repeat steps 2, 3, 4, 5 for all the text by checking if it was the linefeed character that you just outputted. Since in your text that's the very last character.
A version of this loop:
mov bx, OFFSET message ;1.
Again:
mov dl, [bx] ;2.
mov ah, 02h ;3.
int 21h
inc bx ;4.
cmp dl, 0Ah ;5.
jne Again
I'm also supposed to use push and pop to be able to print the string in the same method but backwards.
To accomplish this next task of yours, you would put a push dx in between steps 2. and 3. Then write an additional loop to display the reversed string:
Again2:
pop dx
mov ah, 02h
int 21h
cmp dl, "H"
jne Again2
The pitfalls here will be that
you shouldn't output the linefeed and carriage return before the other (real) characters ==> Hint: do 2 pops before starting the 2nd loop
you can't readily assume that "H" will only appear as the text's first character ==> Hint: count the number of pushs

Caveat: I haven't done this in decades and I don't have a compiler in front of me that can do this. This looks like an assignment so I'm not going to write the code, but I'll point you in the right direction.
Int 21h function 2 requires you to set dl equal to the character you want to output. No pushes and pops required here since DOS doesn't use the stack for parameter passing. First, outside of your loop, you want to point a register to the address of the message, something like: MOV si, message. Then, you need to dereference a single byte from the message and put it into dl. This will require using indirect address notation. Then you can call interrupt 21h and write the character.
As for the loop, there are a few ways to approach it. You could use a counter, with cx being the typical register for that, and use a LOOP statement or DEC and JNZ statements. Or you could put a marker at the end of the string, and terminate the loop when you hit that marker. A null character is commonly used for this. You will also need to move to the next character in the string. You can do that by adding an index, or by incrementing the value of the si register each iteration of the loop. Something like INC si
With all that said, there is a much easier way that avoids the loop but maybe it is cheating. Take a look at interrupt 21h function 9

Yeah, using function 9 would be 'cheating' as it is one of the other exercises (which was the easy one). I have revised my code and got it to start with the beginning character. I just need to figure out how to get it to keep looping so that it continues to read the string and not stop arbitrarily. Here's what I've got so far (It prints H and the 15 e's):
.model small
.data
message db "Hello, DOS Here!"
.code
main proc
mov ax, #data
mov ds, ax
mov ah, 2
mov cx, 16
mov dl, message
L1:
int 21h
mov dl, message + 1
loop L1
.EXIT
main endp
end main

Related

Getting error after copying 8086 code from book

I am having troubles running this 8086 programs that take one letter input from keyboard and outputs "the letter you typed is _"
I just started reading my college book on this and trying to run some code from the book on my computer but got stuck here.
The code below is from my college book.
I am running it in emu8086 emulator.
CODE SEGMENT
; set the DS register
MOV AX, DATA
MOV DS, AX
; Read Keyboard
MOV AH, 08H
INT 21H
; Save input
MOV BL, AL
; Display first part of Message
MOV AH, 09H
MOV DX, OFFSET MESSAGE
INT 21H
; Display character of BL register
MOV AH, 02H
MOV DL, BL
INT 21H
; Exit to DOS
MOV AX, 4C00H
INT 21H
CODE ENDS
DATA SEGMENT
MESSAGE DB “The letter you typed is $”
DATA ENDS
END
The error I am getting is
INT 21h, AH=09h -
address: 00020
byte 24h not found after 2000 bytes.
; correct example of INT 21h/9h:
mov dx, offset msg
mov ah, 9
int 21h
ret
msg db "Hello$"
After further debugging by myself I have concluded there is some problem with this
MOV AX, DATA
MOV DS, AX
If I remove it from my code it executes but with some garbage in the start.
Any suggestions will be appreciated.
Update:
Image of emulator around the string "the letter you typed $"
umm so after giving up for 2 days I referenced another book, and in there they weren't using segments. So I removed segments and the program ran flawlessly here is the new code:
ORG 100H
MOV AH, 08H ; Read Keyboard
INT 21H
MOV BL, AL ; Save input
MOV AH, 09H ; Display first part of Message
MOV DX, OFFSET MESSAGE
INT 21H
MOV AH, 02H ; Display character of BL register
MOV DL, BL
INT 21H
MOV AX, 4C00H ; Exit to DOS
INT 21H
RET
MESSAGE DB "The letter you typed is $"
Thank you to everyone who tried to help me, really appreciated.

How to print an integer stored in a variable

So I have a program in 8086 assembly that allows the user to enter 2 digits, store them in a variable and then print out the number:
data segment
broj db ?
ends
stack segment
dw 128 dup(0)
ends
code segment
mov ax, data
mov ds, ax
mov es, ax
mov ah, 1h
int 21h
sub al, 48d
mov bl, 10d
mul bl
mov broj, al
mov ah, 1h
int 21h
sub al, 48d
add broj, al
mov dl, broj
sub dl, 48d
mov ah, 2h
int 21h
mov ax, 4c00h
int 21h
ends
However whenever I enter a number for example 21 it doesn't give me the number instead it gives me ASCII Code for that value.
Can anyone help?!
However whenever I enter a number for example 21 it doesn't give me the number instead it gives me ASCII Code for that value.
If you feed (input) your program a number that consists of 2 digits, then you'll have to print also 2 digits! Currently your code contains just the one character output function.
First divide the number in broj by 10 giving a quotient (in AL) and a remainder (in AH).
Convert the quotient to character (Add 48) and print it.
Convert the remainder to character (Add 48) and print it.
Example:
mov al, broj
mov ah, 0
mov bl, 10
div bl
add ax, "00"
mov dx, ax
mov ah, 02h
int 21h
mov dl, dh
mov ah, 02h
int 21h

Adobe Type 1 Encryption Algorithm in Assembly

I am trying to encrypt a text file using the adobe type 1 font encryption algorithm. However, I don't know how to properly implement the algorithm in assembly language. Please, help me if you can.
Here is the adobe type 1 font encryption algorithm:
unsigned short int r;
unsigned short int c1 =52845;
unsigned short int c2 = 22719;
unsigned char eencrypt(char plain) unsigned char plain;
{ unsigned char cipher;
cipher = (plain ^ (r >> 8));
r = (cipher + r) * c1 + c2;
return cipher;
}
Here is my code:
.model tiny
.data
filename db "file.txt", 0
bufferSize = 512
filehandle dw ?
buffer db bufferSize dup (0)
r dw 0
c1 dw 52845
c2 dw 22719
cipher dw ?
message1 db 'Cannot open file. $'
message2 db 'Cannot read file. $'
message3 db 'Cannot close file. $'
.code
org 100h
start:
call open
call read
call close
call Exit
;procedures
open:
mov ah,3DH
mov al,0
mov dx, offset filename
int 21h
jc openErr
mov filehandle, ax
ret
read: ;reads file
mov ah, 3Fh
mov bx, filehandle
mov cx, bufferSize
mov dx, offset buffer
int 21h
cmp ax,0
jc readErr
;displays content of file
call clear
mov ah, 9
mov dx, offset buffer
int 21h
ret
close:
mov ah, 3Eh
mov bx, filehandle
int 21h
jc closeErr
ret
encrypt:
; need loop to loop through each char, don't know how to do that
mov ax, [r]
shr ax, 8
mov bl, [buffer]
xor bh,bh
xor bx, ax
mov cipher, bx
mov dx, cipher
add dx, [r] ;get error: extra characters on line
imul dx, c1
add dx, c2
mov [r], dx
;decrypt:
clear: ;clears the screen
mov ax,003h
int 10h
ret
Exit:
mov ax, 4C00h
int 21h
newline: ;prints a newline
mov ah, 2
mov dl, 0DH
int 21h
mov dl, 0AH
int 21h
ret
;error messages
openErr :
call newline
lea DX,message1 ;set up pointer to error message
mov AH,9 ;display string function
int 21H ;DOS call
stc ;set error flag
ret
readErr :
call newline
lea DX,message2 ;set up pointer to error message
mov AH,9 ;display string function
int 21H ;DOS call
stc ;set error flag
ret
closeErr :
call newline
lea DX,message3 ;set up pointer to error message
mov AH,9 ;display string function
int 21H ;DOS call
stc ;set error flag
ret
end start
;encrypt:
; need loop to loop through each char, don't know how to do that
;mov ax, r
;shr ax, 8 r>>8
;mov bl, buffer bl = buffer
buffer is symbolic address, not value in the buffer. Use mov bl,[buffer] to read the value from memory. And if you want to loop over full buffer content, put that address into some register, si is often favoured for keeping pointer to the source of data (because it is hardcoded in lods instructions, and "s" may be read as "source"). If you are C++ compiler, you don't care, and you pick any spare register you have, without considering it's name.
;xor bx, ax bx = buffer ^(r>>8)
bh was not set and may be anything, ruining your calculation. Either do xor bx,bx before loading the bl from buffer, or xor bh,bh to clear bh only (tiny fraction slower on modern x86 because it must then combine bh+bl into bx), or use movzx bx,byte ptr [buffer] in previous step to zero-extend the char value into word value.
;mov cipher, bx cipher = buffer^(r>>8)
Label cipher was defined ahead of db ?, but here you are writing two bytes into memory, so it will also overwrite first byte of message1 string. And I strongly suggest to use [] every time you write into memory, even when TASM/MASM allows this kind of syntax, but I find it difficult to read (seeing [cipher] will make my brain automatically think "memory access" even with quick glance on source).
;mov cx, c1
c1 and c2 can be EQU, so they will compile as intermediate values directly into code.
;add cx, c2
That's not, what the C encrypt does (multiplication has priority over addition).
;mov dx, cipher
Again you treat cipher as word, which makes sense, but it's against the db ? (so the db needs fixing, not code here).
;add dx, r
;imul dx, cx
I would use [] brackets around r again, and the multiplication/addition order is of course wrong.
Not a bad try, but don't hesitate to uncomment that piece of code, pre-set registers with some debug values, and jump straight to it, open the binary in debugger, and single-step over it to verify it works as you did want. You would probably quickly find all those things I wrote above.
Then just turn it into loop (use any ASM book/tutorial with some example about working with arrays/strings, learn to use pointers, then it should be not that hard to finish).

Assembler (TASM x64) arrays and elements

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

reading from File in assembly

i am trying to read a username and a password from a file in x86 assembly for the perpose of authentication
obviously the file consists of two lines , the user name and the password how can i read the two lines seperately and compare them?
My attempt:
proc read_file
mov ah,3dh
lea dx,file_name
int 21h
mov bx, ax
xor si,si
repeat:
mov ah, 3fh
lea dx, buffer
mov cx, 100
int 21h
mov si, ax
mov buffer[si], '$'
mov ah, 09h
int 21h ;print on screen
cmp si, 100
je repeat
jmp stop;jump to end
stop:
RET
read_file ENDP
Go here and read up on functions like CreateFile and ReadFile.
You should use system class to do that and it depends on whether you use Windows or Linux.
Check this : http://www.freebsd.org/doc/en/books/developers-handbook/x86-system-calls.html

Resources