I want to display a score in a game that I built in MASM32, and I have a problem, how do I convert a DWORD to a DB (string).
There is the function crt__itoa to convert a dword to an integer , but for some reason it doesn't work (do i need to include an other lib ? ).
There is the function TextOutA to display a score, but again I cant print it out because I don't have a string so it can print it from.
do i need to include an other lib? - Probably. You need msvcrt.inc and msvcrt.lib for crt__itoa.
masm32rt.inc is the Swiss Army Knife for such cases. Here is a working example:
include c:\masm32\include\masm32rt.inc
.DATA
fmt db "%s",10,0
num dd 1234567
num_str db 16 dup (?)
.CODE
main PROC
; with CALL:
push 10
push OFFSET num_str
push num
call crt__itoa
add esp, 12
push OFFSET num_str
push OFFSET fmt
call crt_printf
add esp, 8
; or with INVOKE:
invoke crt__itoa, num, OFFSET num_str, 10
invoke crt_printf, OFFSET fmt, OFFSET num_str
invoke ExitProcess, 0
main ENDP
END main
The program does not stop. If you don't call it in an extra command prompt window it will open a window and close it immediately. In Qeditor I suggest to insert a line "Run & Pause" into menus.ini:
...
&Run Program,"{b}.exe"
Run && Pause,cmd.exe /C"{b}.exe" & pause
...
Now you have a new item under "Project".
Next is a method made with Visual Studio 2010 C++ that manually converts EAX into a string (it doesn't need any library, just copy-paste and use). It takes a number as parameter, assign it to EAX, convert it to string and display the string :
void number2string ( int value ) {
char buf[11];
__asm { ;EXTRACT DIGITS ONE BY ONE AND PUSH THEM INTO STACK.
mov eax, value
mov ebx, 10 ;DIGITS ARE EXTRACTED DIVIDING BY 10.
mov cx, 0 ;COUNTER FOR EXTRACTED DIGITS.
cycle1:
mov edx, 0 ;NECESSARY TO DIVIDE BY EBX.
div ebx ;EDX:EAX / 10 = EAX:QUOTIENT EDX:REMAINDER.
push dx ;PRESERVE DIGIT EXTRACTED (DL) FOR LATER.
inc cx ;INCREASE COUNTER FOR EVERY DIGIT EXTRACTED.
cmp eax, 0 ;IF NUMBER IS
jne cycle1 ;NOT ZERO, LOOP.
;NOW RETRIEVE PUSHED DIGITS IN REVERSE ORDER.
mov esi, 0 ;POINTER TO STRING'S CHARACTERS.
cycle2:
pop dx ;GET A DIGIT.
add dl, 48 ;CONVERT DIGIT TO CHARACTER.
mov buf[ esi ], dl
inc esi ;NEXT POSITION IN STRING.
loop cycle2
mov buf[ esi ], 0 ;MAKE IT ASCIIZ STRING.
}
printf( buf );
scanf( "%s",buf ); // TO STOP PROGRAM AND LET US SEE RESULT.
}
Pay attention : previous method is a "void", so you call it as usual :
number2string( 1234567890 ); // CONVERT THIS BIG NUMBER IN STRING AND DISPLAY.
You can modify the method to return the string or to do anything you want.
Now (for those who are tough enough) the same previous procedure for pure assembler, made with GUI Turbo Assembler x64 (http://sourceforge.net/projects/guitasm8086/), this full program shows how it works :
.model small
.586
.stack 100h
.data
buf db 11 dup (?) ;STRING.
.code
start:
;INITIALIZE DATA SEGMENT.
mov ax, #data
mov ds, ax
;CONVERT EAX TO STRING.
call dollars ;FILL BUF WITH '$', NECESSARY TO DISPLAY.
mov eax, 1234567890
call number2string ;PARAMETER:EAX. RETURN:VARIABLE BUF.
;DISPLAY BUF (EAX CONVERTED TO STRING).
mov ah, 9
mov dx, offset buf
int 21h
;WAIT UNTIL USER PRESS ANY KEY.
mov ah, 7
int 21h
;FINISH PROGRAM.
mov ax, 4c00h
int 21h
;------------------------------------------
;NUMBER TO CONVERT MUST ENTER IN EAX.
;ALGORITHM : EXTRACT DIGITS ONE BY ONE, STORE
;THEM IN STACK, THEN EXTRACT THEM IN REVERSE
;ORDER TO CONSTRUCT STRING (BUF).
number2string proc
mov ebx, 10 ;DIGITS ARE EXTRACTED DIVIDING BY 10.
mov cx, 0 ;COUNTER FOR EXTRACTED DIGITS.
cycle1:
mov edx, 0 ;NECESSARY TO DIVIDE BY EBX.
div ebx ;EDX:EAX / 10 = EAX:QUOTIENT EDX:REMAINDER.
push dx ;PRESERVE DIGIT EXTRACTED (DL) FOR LATER.
inc cx ;INCREASE COUNTER FOR EVERY DIGIT EXTRACTED.
cmp eax, 0 ;IF NUMBER IS
jne cycle1 ;NOT ZERO, LOOP.
;NOW RETRIEVE PUSHED DIGITS.
mov si, offset buf
cycle2:
pop dx
add dl, 48 ;CONVERT DIGIT TO CHARACTER.
mov [ si ], dl
inc si
loop cycle2
ret
number2string endp
;------------------------------------------
;FILLS VARIABLE BUF WITH '$'.
;USED BEFORE CONVERT NUMBERS TO STRING, BECAUSE
;THE STRING WILL BE DISPLAYED.
dollars proc
mov si, offset buf
mov cx, 11
six_dollars:
mov bl, '$'
mov [ si ], bl
inc si
loop six_dollars
ret
dollars endp
end start
Related
I have an assignment asking me to find the hamming distance of two user-input strings that are not necessarily equal in length.
So, I made the following algorithm:
Read both strings
check the length of each string
compare the length of the strings
if(str1 is shorter)
set counter to be the length of str1
END IF
if(str1 is longer)
set counter to be the length of str2
END IF
if(str1 == str2)
set counter to be length of str1
END IF
loop through each digit of the strings
if(str1[digitNo] XOR str2[digitNo] == 1)
inc al
END IF
the final al value is the hamming distance of the strings, print it.
But I'm stuck at step 3 and I don't seem to get it working. any help?
I tried playing around with the registers to save the values in, but none of that worked, I still didn't get it working.
; THIS IS THE CODE I GOT
.model small
.data
str1 db 255
db ?
db 255 dup(?)
msg1 db 13,10,"Enter first string:$"
str2 db 255
db ?
db 255 dup(?)
msg2 db 13,10,"Enter second string:$"
one db "1"
count db ?
.code
.startup
mov ax,#data
mov ds,ax
; printing first message
mov ah, 9
mov dx, offset msg1
int 21h
; reading first string
mov ah, 10
mov dx, offset str1
int 21h
; printing second message
mov ah, 9
mov dx, offset msg2
int 21h
; reading second string
mov ah, 10
mov dx, offset str2
int 21h
; setting the values of the registers to zero
mov si, 0
mov di, 0
mov cx, 0
mov bx, 0
; checking the length of the first string
mov bl, str1+1
add bl, 30h
mov ah, 02h
mov dl, bl
int 21h
; checking the length of the second string
mov bl, str2+1
add bl, 30h
mov ah, 02h
mov dh, bl
int 21h
; comparing the length of the strings
cmp dl,dh
je equal
jg str1Greater
jl str1NotGreater
; if the strings are equal we jump here
equal:
mov cl, dl
call theLoop
; if the first string is greater than the second, we jump here and set counter of str1
str1Greater:
; if the second string is greater than the first, we jump here and set counter to length of str2
Str1NotGreater:
; this is the loop that finds and prints the hamming distance
;we find it by looping over the strings and taking the xor for each 2, then incrementing counter of ones for each xor == 1
theLoop:
end
So, in the code I provided, it's supposed to print the length of each string (it prints the lengths next to each other), but it seems to always keep printing the length of the first string, twice. The register used to store the length of the first string is dl, and the register used to store the length of the second is dh, if I change it back to dl, it would then print the correct length, but I want to compare the lengths, and I think it won't be possible to do so if I save it in dl both times.
but it seems to always keep printing the length of the first string, twice.
When outputting a character with the DOS function 02h you don't get to choose which register to use to supply the character! It's always DL.
Since after printing both lengths you still want to work with these lengths it will be better to not destroy them in the first place. Put the 1st length in BL and the second length in BH. For outputting you copy these in turn to DL where you do the conversion to a character. This of course can only work for strings of at most 9 characters.
; checking the length of the first string
mov BL, str1+1
mov dl, BL
add dl, 30h
mov ah, 02h
int 21h
; checking the length of the second string
mov BH, str2+1
mov dl, BH
add dl, 30h
mov ah, 02h
int 21h
; comparing the length of the strings
cmp BL, BH
ja str1LONGER
jb str1SHORTER
; if the strings are equal we ** FALL THROUGH ** here
equal:
mov cl, BL
mov ch, 0
call theLoop
!!!! You need some way out at this point. Don't fall through here !!!!
; if the first string is greater than the second, we set counter of str1
str1LONGER:
; if the second string is greater than the first, we set counter to length of str2
Str1SHORTER:
; this is the loop that finds and prints the hamming distance
;we find it by looping over the strings and taking the xor for each 2, then incrementing counter of ones for each xor == 1
theLoop:
Additional notes
Lengths are unsigned numbers. Use the unsigned conditions above and below.
Talking about longer and shorter makes more sense for strings.
Don't use 3 jumps if a mere fall through in the code can do the job.
Your code in theLoop will probably use CX as a counter. Don't forget to zero CH. Either using 2 instructions like I did above or else use movzx cx, BL if you're allowed to use instructions that surpass the original 8086.
Bonus
mov si, offset str1+2
mov di, offset str2+2
mov al, 0
MORE:
mov dl, [si]
cmp dl, [di]
je EQ
inc al
EQ:
inc si
inc di
loop MORE
8086 Write a program that takes one string from the input and inserts white space between every two letters
using nasm and MSDOS
i have do following code but it is not working
start:
mov ax,data
mov ds, ax
mov es, ax
mov cx, size;cx will contain size,we need it to
; check if we have got 10 inputs from key board
lea dx, Enter_string;it will display a text on screen to enter text
mov ah, 9
int 21h
call get_string;input string from keyboard
mov ax, 4ch;terminating
int 21h
get_string:
mov si, 0; si will be used as index
mov bx, offset string
get_char:
mov ah, 1; get a char from keyboard
int 21h
mov [bx][si], al; saving input in string
inc si
cmp si,cx;if si=7 than, no need to take more input
jne get_char
ret
. You should prefer the simplicity of .COM programs while learning. They already start with all the segment registers pointing to the program.
. The DOS exit function expects the function number in AH.
. On NASM you don't use mov bx, offset string. Just write mov bx, string.
. It's easy to combine the tasks of inputting a string and interjecting space characters. See below code:
org 256 ;.COM programs have CS=DS=ES=SS
start:
mov cx, size ;cx will contain size,we need it to
; check if we have got 10 inputs from key board
mov dx, Enter_string ;it will display a text on screen to enter text
mov ah, 9
int 21h
call get_string ;input string from keyboard
mov ax, 4C00h ;terminating
int 21h
get_string:
push cx
mov si, 0 ; si will be used as index
get_char:
mov ah, 1 ; get a char from keyboard
int 21h
mov ah, " "
mov [string+si], ax ; saving input in string PLUS THE SPACE CHARACTER
add si, 2
dec cx ;if si=7 than, no need to take more input
jnz get_char
pop cx
ret
Remember that you don't actually need the last space character. Just overwrite it with the string terminator that you normally would add to this string!
I'm actually looking to be pointed in the right direction on an issue.
I'm looking to convert a date in x86 Assembly from the format "DD-MMM-YYYY" to a unique number so that it can be bubble sorted later and eventually converted back.
So, when I have a string input ie:
.data
inDate dw "08-SEP-1993"
And I want to split it up to
day = "08"
month = "SEP"
year = "1993"
So that I can process it further (I'll be converting SEP to "7", ect.)
So my question is what is a simple, efficient way to break the date down (code-wise)? I know I'll need to convert the date format to allow for sorting, but I'm new to Assembly so I'm not positive how to break the string up so I can convert it.
Also, as a second question, how would you convert a number from the string to an actual numerical value?
Thanks!
NOTE: I suppose it should be noted I'm using masm32
Next little program was made with EMU8086 (16 bits), it captures numbers from keyboard as strings, convert them to numeric to compare, and finally it converts a number to string to display. Notice the numbers are captured with 0AH, which requieres a 3-level variable "str". The conversion procedures that you need are at the bottom of the code (string2number and number2string).
.model small
.stack 100h
.data
counter dw ?
msj1 db 'Enter a number: $'
msj2 db 'The highest number is: $'
break db 13,10,'$'
str db 6 ;MAX NUMBER OF CHARACTERS ALLOWED (4).
db ? ;NUMBER OF CHARACTERS ENTERED BY USER.
db 6 dup (?) ;CHARACTERS ENTERED BY USER.
highest dw 0
buffer db 6 dup(?)
.code
;INITIALIZE DATA SEGMENT.
mov ax, #data
mov ds, ax
;-----------------------------------------
;CAPTURE 5 NUMBERS AND DETERMINE THE HIGHEST.
mov counter, 5 ;HOW MANY NUMBERS TO CAPTURE.
enter_numbers:
;DISPLAY MESSAGE.
mov dx, offset msj1
call printf
;CAPTURE NUMBER AS STRING.
mov dx, offset str
call scanf
;DISPLAY LINE BREAK.
mov dx, offset break
call printf
;CONVERT CAPTURED NUMBER FROM STRING TO NUMERIC.
mov si, offset str ;PARAMETER (STRING TO CONVERT).
call string2number ;NUMBER RETURNS IN BX.
;CHECK IF CAPTURED NUMBER IS THE HIGHEST.
cmp highest, bx
jae ignore ;IF (HIGHEST >= BX) IGNORE NUMBER.
;IF NO JUMP TO "IGNORE", CURRENT NUMBER IS HIGHER THAN "HIGHEST".
mov highest, bx ;CURRENT NUMBER IS THE HIGHEST.
ignore:
;CHECK IF WE HAVE CAPTURED 5 NUMBERS ALREADY.
dec counter
jnz enter_numbers
;-----------------------------------------
;DISPLAY HIGHEST NUMBER.
;FIRST, FILL BUFFER WITH '$' (NECESSARY TO DISPLAY).
mov si, offset buffer
call dollars
;SECOND, CONVERT HIGHEST NUMBER TO STRING.
mov ax, highest
mov si, offset buffer
call number2string
;THIRD, DISPLAY STRING.
mov dx, offset msj2
call printf
mov dx, offset buffer
call printf
;FINISH PROGRAM.
mov ax, 4c00h
int 21h
;-----------------------------------------
;PARAMETER : DX POINTING TO '$' FINISHED STRING.
proc printf
mov ah, 9
int 21h
ret
endp
;-----------------------------------------
;PARAMETER : DX POINTING TO BUFFER TO STORE STRING.
proc scanf
mov ah, 0Ah
int 21h
ret
endp
;------------------------------------------
;CONVERT STRING TO NUMBER.
;PARAMETER : SI POINTING TO CAPTURED STRING.
;RETURN : NUMBER IN BX.
proc string2number
;MAKE SI TO POINT TO THE LEAST SIGNIFICANT DIGIT.
inc si ;POINTS TO THE NUMBER OF CHARACTERS ENTERED.
mov cl, [ si ] ;NUMBER OF CHARACTERS ENTERED.
mov ch, 0 ;CLEAR CH, NOW CX==CL.
add si, cx ;NOW SI POINTS TO LEAST SIGNIFICANT DIGIT.
;CONVERT STRING.
mov bx, 0
mov bp, 1 ;MULTIPLE OF 10 TO MULTIPLY EVERY DIGIT.
repeat:
;CONVERT CHARACTER.
mov al, [ si ] ;CHARACTER TO PROCESS.
sub al, 48 ;CONVERT ASCII CHARACTER TO DIGIT.
mov ah, 0 ;CLEAR AH, NOW AX==AL.
mul bp ;AX*BP = DX:AX.
add bx, ax ;ADD RESULT TO BX.
;INCREASE MULTIPLE OF 10 (1, 10, 100...).
mov ax, bp
mov bp, 10
mul bp ;AX*10 = DX:AX.
mov bp, ax ;NEW MULTIPLE OF 10.
;CHECK IF WE HAVE FINISHED.
dec si ;NEXT DIGIT TO PROCESS.
loop repeat ;COUNTER CX-1, IF NOT ZERO, REPEAT.
ret
endp
;------------------------------------------
;FILLS VARIABLE WITH '$'.
;USED BEFORE CONVERT NUMBERS TO STRING, BECAUSE
;THE STRING WILL BE DISPLAYED.
;PARAMETER : SI = POINTING TO STRING TO FILL.
proc dollars
mov cx, 6
six_dollars:
mov bl, '$'
mov [ si ], bl
inc si
loop six_dollars
ret
endp
;------------------------------------------
;CONVERT A NUMBER IN STRING.
;ALGORITHM : EXTRACT DIGITS ONE BY ONE, STORE
;THEM IN STACK, THEN EXTRACT THEM IN REVERSE
;ORDER TO CONSTRUCT STRING (STR).
;PARAMETERS : AX = NUMBER TO CONVERT.
; SI = POINTING WHERE TO STORE STRING.
proc number2string
mov bx, 10 ;DIGITS ARE EXTRACTED DIVIDING BY 10.
mov cx, 0 ;COUNTER FOR EXTRACTED DIGITS.
cycle1:
mov dx, 0 ;NECESSARY TO DIVIDE BY BX.
div bx ;DX:AX / 10 = AX:QUOTIENT DX:REMAINDER.
push dx ;PRESERVE DIGIT EXTRACTED FOR LATER.
inc cx ;INCREASE COUNTER FOR EVERY DIGIT EXTRACTED.
cmp ax, 0 ;IF NUMBER IS
jne cycle1 ;NOT ZERO, LOOP.
;NOW RETRIEVE PUSHED DIGITS.
cycle2:
pop dx
add dl, 48 ;CONVERT DIGIT TO CHARACTER.
mov [ si ], dl
inc si
loop cycle2
ret
endp
Now the 32 bits version. Next is a little program that assigns to EAX a big number, convert it to string and convert it back to numeric, here it is:
.model small
.586
.stack 100h
.data
msj1 db 13,10,'Original EAX = $'
msj2 db 13,10,'Flipped EAX = $'
msj3 db 13,10,'New EAX = $'
buf db 11
db ?
db 11 dup (?)
.code
start:
;INITIALIZE DATA SEGMENT.
mov ax, #data
mov ds, ax
;CONVERT EAX TO STRING TO DISPLAY IT.
call dollars ;NECESSARY TO DISPLAY.
mov eax, 1234567890
call number2string ;PARAMETER:AX. RETURN:VARIABLE BUF.
;DISPLAY 'ORIGINAL EAX'.
mov ah, 9
mov dx, offset msj1
int 21h
;DISPLAY BUF (EAX CONVERTED TO STRING).
mov ah, 9
mov dx, offset buf
int 21h
;FLIP EAX.
call dollars ;NECESSARY TO DISPLAY.
mov eax, 1234567890
call flip_eax ;PARAMETER:AX. RETURN:VARIABLE BUF.
;DISPLAY 'FLIPPED EAX'.
mov ah, 9
mov dx, offset msj2
int 21h
;DISPLAY BUF (EAX FLIPPED CONVERTED TO STRING).
mov ah, 9
mov dx, offset buf
int 21h
;CONVERT STRING TO NUMBER (FLIPPED EAX TO EAX).
mov si, offset buf ;STRING TO REVERSE.
call string2number ;RETURN IN EBX.
mov eax, ebx ;THIS IS THE NEW EAX FLIPPED.
;CONVERT EAX TO STRING TO DISPLAY IT.
call dollars ;NECESSARY TO DISPLAY.
call number2string ;PARAMETER:EAX. RETURN:VARIABLE BUF.
;DISPLAY 'NEW EAX'.
mov ah, 9
mov dx, offset msj3
int 21h
;DISPLAY BUF (EAX CONVERTED TO STRING).
mov ah, 9
mov dx, offset buf
int 21h
;WAIT UNTIL USER PRESS ANY KEY.
mov ah, 7
int 21h
;FINISH PROGRAM.
mov ax, 4c00h
int 21h
;------------------------------------------
flip_eax proc
mov si, offset buf ;DIGITS WILL BE STORED IN BUF.
mov bx, 10 ;DIGITS ARE EXTRACTED DIVIDING BY 10.
mov cx, 0 ;COUNTER FOR EXTRACTED DIGITS.
extracting:
;EXTRACT ONE DIGIT.
mov edx, 0 ;NECESSARY TO DIVIDE BY EBX.
div ebx ;EDX:EAX / 10 = EAX:QUOTIENT EDX:REMAINDER.
;INSERT DIGIT IN STRING.
add dl, 48 ;CONVERT DIGIT TO CHARACTER.
mov [ si ], dl
inc si
;NEXT DIGIT.
cmp eax, 0 ;IF NUMBER IS
jne extracting ;NOT ZERO, REPEAT.
ret
flip_eax endp
;------------------------------------------
;CONVERT STRING TO NUMBER IN EBX.
;SI MUST ENTER POINTING TO THE STRING.
string2number proc
;COUNT DIGITS IN STRING.
mov cx, 0
find_dollar:
inc cx ;DIGIT COUNTER.
inc si ;NEXT CHARACTER.
mov bl, [ si ]
cmp bl, '$'
jne find_dollar ;IF BL != '$' JUMP.
dec si ;BECAUSE IT WAS OVER '$', NOT OVER THE LAST DIGIT.
;CONVERT STRING.
mov ebx, 0
mov ebp, 1 ;MULTIPLE OF 10 TO MULTIPLY EVERY DIGIT.
repeat:
;CONVERT CHARACTER.
mov eax, 0 ;NOW EAX==AL.
mov al, [ si ] ;CHARACTER TO PROCESS.
sub al, 48 ;CONVERT ASCII CHARACTER TO DIGIT.
mul ebp ;EAX*EBP = EDX:EAX.
add ebx, eax ;ADD RESULT TO BX.
;INCREASE MULTIPLE OF 10 (1, 10, 100...).
mov eax, ebp
mov ebp, 10
mul ebp ;AX*10 = EDX:EAX.
mov ebp, eax ;NEW MULTIPLE OF 10.
;CHECK IF WE HAVE FINISHED.
dec si ;NEXT DIGIT TO PROCESS.
loop repeat ;CX-1, IF NOT ZERO, REPEAT.
ret
string2number endp
;------------------------------------------
;FILLS VARIABLE STR WITH '$'.
;USED BEFORE CONVERT NUMBERS TO STRING, BECAUSE
;THE STRING WILL BE DISPLAYED.
dollars proc
mov si, offset buf
mov cx, 11
six_dollars:
mov bl, '$'
mov [ si ], bl
inc si
loop six_dollars
ret
dollars endp
;------------------------------------------
;NUMBER TO CONVERT MUST ENTER IN EAX.
;ALGORITHM : EXTRACT DIGITS ONE BY ONE, STORE
;THEM IN STACK, THEN EXTRACT THEM IN REVERSE
;ORDER TO CONSTRUCT STRING (BUF).
number2string proc
mov ebx, 10 ;DIGITS ARE EXTRACTED DIVIDING BY 10.
mov cx, 0 ;COUNTER FOR EXTRACTED DIGITS.
cycle1:
mov edx, 0 ;NECESSARY TO DIVIDE BY EBX.
div ebx ;EDX:EAX / 10 = EAX:QUOTIENT EDX:REMAINDER.
push dx ;PRESERVE DIGIT EXTRACTED (DL) FOR LATER.
inc cx ;INCREASE COUNTER FOR EVERY DIGIT EXTRACTED.
cmp eax, 0 ;IF NUMBER IS
jne cycle1 ;NOT ZERO, LOOP.
;NOW RETRIEVE PUSHED DIGITS.
mov si, offset buf
cycle2:
pop dx
add dl, 48 ;CONVERT DIGIT TO CHARACTER.
mov [ si ], dl
inc si
loop cycle2
ret
number2string endp
end start
I have to write a program that
takes a string from the key board buffer and puts it into a character array
asks for a character to remove
removes the character from the character array while shifting everything else over
I want to use a stack to accomplish this. So here is my logic.
Starting from the end of the string compare that character with the character that is to be removed. If it isn't the character push it on to the stack. If it is ignore it and move on through the string. Then starting from the beginning pop everything into place.
I'm supposed to use a procedure to accomplish this. When I'm stepping through everything seems to be working ok until I attempt to leave the procedure and return to main. I'm fairly sure my logic in my procedure is the problem. Right now when I attempt to work with the string "The" and remove the e I get "he".
TITLE String Manipulation
INCLUDE Irvine32.inc
.data
prompt byte "Please enter a string to manipulate : ",0
prompt2 byte "Please enter a character to remove: ",0
answerMSG byte "The new string is: ",0
string BYTE 51 DUP (0)
char BYTE ?
byteCount dword ?
.code
main PROC
call clrscr
push eax ;perserve the registers
push ecx
push edx
mov edx, OFFSET prompt ;prints the prompt
call writeString
mov edx, OFFSET string ;moves the register to the first location for the string
mov ecx, SIZEOF string ;Sets the max characters
call readString
mov byteCount,eax ;places actual count into a counting register
call crlf
mov edx, OFFSET prompt2 ;prints the prompt
call writeString
mov edx, OFFSET char
mov ecx, 1
call readString
call clrscr
mov ecx, byteCount
mov edx, OFFSET string
call stringMan
mov edx, OFFSET string
call writeString
pop edx
pop ecx
pop eax
main ENDP
;
stringMan PROC USES eax ecx edx
mov eax,0
L1:
movzx edx , string[ecx]
cmp dl, char
jz L2
push edx
inc eax
L2:
mov string[ecx],0
LOOP L1
mov ecx,eax
mov eax,0
L3:
pop edx
mov byte ptr string[eax],dl
inc eax
loop L3
ret
stringMan ENDP
END main
Figured it out.
Answer:
I was not dealing with getting a character from the console correctly. I also was not dealing with the case when ecx = 0. This is the first position of the character array. So I was not comparing the correct character, and not pushing the first character onto the array when necessary. I have fixed it by removing
mov edx, OFFSET char
mov ecx, 1
call readString
and replacing it with
call readChar
mov char,al
then adding this after the L1 loop.
movzx edx , string[ecx]
cmp dl,char
jz L4
push edx
inc eax
L4:
It now works as designed. I just have some formatting issues to clear up.
Answer:
I was not dealing with getting a character from the console correctly. I also was not dealing with the case when ecx = 0. This is the first position of the character array. So I was not comparing the correct character, and not pushing the first character onto the array when necessary. I have fixed it by removing
mov edx, OFFSET char
mov ecx, 1
call readString
and replacing it with
call readChar
mov char,al
then adding this after the L1 loop.
movzx edx , string[ecx]
cmp dl,char
jz L4
push edx
inc eax
L4:
It now works as designed. I just have some formatting issues to clear up.
I have writen this little experiement bootstrap that has a getline and print_string "functions". The boot stuff is taken from MikeOS tutorial but the rest I have writen myself. I compile this with NASM and run it in QEMU.
So the actual question: I've declared this variable curInpLn on line 6. What ever the user types is saved on that variable and then after enter is hit it is displayed to the user with some additional messages. What I'd like to do is to clear the contents of curInpLn each time the getline function is called but for some reason I can't manage to do that. I'm quite the beginner with Assmebly at the moment.
You can compile the code to bin format and then create a floppy image of it with: "dd status=noxfer conv=notrunc if=FILENAME.bin of=FILENAME.flp" and run it in qemu with: "qemu -fda FILENAME.flp"
BITS 16
jmp start
welcomeSTR: db 'Welcome!',0
promptSTR: db 'Please prompt something: ',0
responseSTR: db 'You prompted: ',0
curInpLn: times 80 db 0 ;this is a variable to hold the input 'command'
curCharCnt: dw 0
curLnNum: dw 1
start:
mov ax, 07C0h ; Set up 4K stack space after this bootloader
add ax, 288 ; (4096 + 512) / 16 bytes per paragraph
mov ss, ax
mov sp, 4096
mov ax, 07C0h ; Set data segment to where we're loaded
mov ds, ax
call clear_screen
lea bx, [welcomeSTR] ; Put string position into SI
call print_string
call new_line
.waitCMD:
lea bx, [promptSTR]
call print_string
call getLine ; Call our string-printing routine
jmp .waitCMD
getLine:
cld
mov cx, 80 ;number of loops for loopne
mov di, 0 ;offset to bx
lea bx, [curInpLn] ;the address of our string
.gtlLoop:
mov ah, 00h ;This is an bios interrupt to
int 16h ;wait for a keypress and save it to al
cmp al, 08h ;see if backspace was pressed
je .gtlRemChar ;if so, jump
mov [bx+di], al ;effective address of our curInpLn string
inc di ;is saved in bx, di is an offset where we will
;insert our char in al
cmp al, 0Dh ;see if character typed is car-return (enter)
je .gtlDone ;if so, jump
mov ah, 0Eh ;bios interrupt to show the char in al
int 10h
.gtlCont:
loopne .gtlLoop ;loopne loops until cx is zero
jmp .gtlDone
.gtlRemChar:
;mov [bx][di-1], 0 ;this needs to be solved. NASM gives error on this.
dec di
jmp .gtlCont
.gtlDone:
call new_line
lea bx, [responseSTR]
call print_string
mov [curCharCnt], di ;save the amount of chars entered to a var
lea bx, [curInpLn]
call print_string
call new_line
ret
print_string: ; Routine: output string in SI to screen
mov si, bx
mov ah, 0Eh ; int 10h 'print char' function
.repeat:
lodsb ; Get character from string
cmp al, 0
je .done ; If char is zero, end of string
int 10h ; Otherwise, print it
jmp .repeat
.done:
ret
new_line:
mov ax, [curLnNum]
inc ax
mov [curLnNum], ax
mov ah, 02h
mov dl, 0
mov dh, [curLnNum]
int 10h
ret
clear_screen:
push ax
mov ax, 3
int 10h
pop ax
ret
times 510-($-$$) db 0 ; Pad remainder of boot sector with 0s
dw 0xAA55 ; The standard PC boot signature
I haven't written code in Assembly for 20 years (!), but it looks like you need to use the 'stosw' instruction (or 'stosb'). STOSB loads the value held in AL to the byte pointed to by ES:DI, whereas STOSSW loads the value held in AX to the word pointed to by ES:DI. The instruction automatically advances the pointer. As your variable curInpLn is 80 bytes long, you can clear it with 40 iterations of STOSW. Something like
xor ax, ax ; ax = 0
mov es, ds ; point es to our data segment
mov di, offset curInpLn ; point di at the variable
mov cx, 40 ; how many repetitions
rep stosw ; zap the variable
This method is probably the quickest method of clearing the variable as it doesn't require the CPU to retrieve any instructions from the pre-fetch queue. In fact, it allows the pre-fetch queue to fill up, thus allowing any following instructions to execute as quickly as possible.