Move to end of string within buffer - Assembly Languge - string

I am trying to take in a string and then see if the last value in the string is an EOL character. I figured I would use the length of the string read in and then add it to the address of the buffer to find the last element. This does not seem to work.
Edit: I apologize that I did not include more information. Variables are defined as such:
%define BUFLEN 256
SECTION .bss ; uninitialized data section
buf: resb BUFLEN ; buffer for read
newstr: resb BUFLEN ; converted string
rlen: resb 4
Then a dos interrupt is called to accept a string from the user like so:
; read user input
;
mov eax, SYSCALL_READ ; read function
mov ebx, STDIN ; Arg 1: file descriptor
mov ecx, buf ; Arg 2: address of buffer
mov edx, BUFLEN ; Arg 3: buffer length
int 080h
Then we go into our loop:
test_endl:
mov ecx, [rlen]
mov esi, buf
add esi, ecx ; i want to move 'rlen' bytes into buf
mov al, [esi] ; to see what the last element is
cmp al, 10 ; compare it to EOL
jnz L1_init
dec ecx ; and then decrease 'rlen' if it is an EOL
mov [rlen], ecx\
I am user NASM to compile and writing for an i386 machine.

Adding the length of the string to the address of the buffer gives access to the byte behind the string.
Based on you saying that
you want to see if the last value in the string is an EOL character
you aim to decrease 'rlen' if it is an EOL (*)
I conclude that you consider the possible EOL character part of the string as defined by its length rlen. If you don't then (*) doesn't make sense.
Use mov al,[esi-1] to see what the last element is!
test_endl:
mov ecx, [rlen]
mov esi, buf
add esi, ecx ; i want to move 'rlen' bytes into buf
mov al, [esi-1] ; to see what the last element is
cmp al, 10 ; compare it to EOL
jnz L1_init
dec ecx ; and then decrease 'rlen' if it is an EOL
mov [rlen], ecx

This is a much more roundabout way (literally) of getting to the end of the string. I loop through all the characters in the string based on what the size of the counter, rlen, is. Then, once the loop is complete, I make the comparison and decrement rlen as necessary.
test_loop:
mov al, [esi] ; get a character
inc esi ; update source pointer
dec ecx ; update char count
jnz test_loop ; loop to top if more chars
cmp al, 10 ; comparison
jne L1_init ; if not EOL jump to L1_init
mov ecx, [rlen] ; decrease the size of rlen if necessary
dec ecx
mov [rlen], ecx

Related

How to split / truncate a string variable value in Assembly?

I am currently working on a project and for storage's sake I would like to cut off a variable in assembly, and (optionally) make that the value of a register, such as eax.
I will need code that works with NASM using Intel syntax.
For example, if the variable "msg" is set to "29ak49", I want to take a part of that, like "9ak4", and put it in a register, or something similar.
As Peter Cordes mentioned in the comments, you can always add a null terminator (0) into the existing string's buffer to right truncate; if you don't mind modifying the original string data.
The example below will retrieve a substring without modifying the original string.
If you have the address of a variable, and you know where you want to truncate it, you can take the address of the starting position of the data, and add an offset to left truncate. To right truncate you can just read as many characters as you need from the new offset.
For example in x86:
msg db '29ak49' ; create a string (1 byte per char)
;; left truncate
mov esi, msg ; get the address of the start of the string
add esi, OFFSET_INTO_DATA ; offset into the string (1 byte per char)
;; right truncate
mov edi, NUM_CHARS ; number of characters to take
.loop:
movzx eax, byte [esi] ; get the value of the next character
;; do something with the character in eax
inc esi
dec edi
jnz .loop
;; end loop
EDIT:
The following is a runable test implementation as a 32-bit Linux application that prints out the substring selected based on OFFSET_INTO_DATA and NUM_CHARS (note: the algorithm is the same, but the registers have changed):
section .text
global _start
_start:
;; left truncate
mov esi, msg ; get the address of the start of the string
add esi, OFFSET_INTO_DATA ; offset into the string (1 byte per char)
;; right truncate
mov edi, NUM_CHARS ; number of characters to take
.loop:
mov ecx, esi ; get the address of the next character
call print_char_32
inc esi
dec edi
jnz .loop
jmp halt
;;; input: ecx -> character to display
print_char_32:
mov edx, 1 ; PRINT
mov ebx, 1 ;
mov eax, 4 ;
int 0x80 ;
ret
halt:
mov eax, 1 ; EXIT
int 0x80 ;
jmp halt
section .data
msg db '29ak49' ; create a string (1 byte per char)
OFFSET_INTO_DATA EQU 1
NUM_CHARS EQU 3
Compiled with:
nasm -f elf substring.asm ; ld -m elf_i386 -s -o substring substring.o

replace a string in assembly

I wanna get a source string ,find a key in it and replace the key with a replace string so i copy the rest of source and the replace string in the result .
it outputs the correct prompt when the key doesnt exist in the source string : "The key does not appear in the string."
but when the source contains the key it stucks and doesnt continue running
(it looks sth in found label part have been missed and have an overflow)
can anyone help to correct the found part ?
any help will be appreciate :)
; program to search for one string embedded in another
; author: R. Detmer revised: 10/97
.386
.MODEL FLAT
ExitProcess PROTO NEAR32 stdcall, dwExitCode:DWORD
INCLUDE io.h
cr EQU 0dh ; carriage return character
Lf EQU 0ah ; linefeed character
.STACK 4096 ; reserve 4096-byte stack
.DATA
prompt1 BYTE "String to search? ", 0
prompt2 BYTE cr, Lf, "Key to search for? ", 0
prompt3 BYTE cr, Lf, "Word to replace? ", 0
source BYTE 100 DUP (?)
key BYTE 20 DUP (?)
replace BYTE 20 DUP (?)
srcLength DWORD ?
keyLength DWORD ?
repLength DWORD ?
BeginLength DWORD ?
restLength DWORD ?
cpyLength DWORD ?
lastPosn DWORD ?
restPosition DWORD ?
firstParam DWORD ?
secondParam DWORD ?
keyPosition DWORD ?
failure BYTE cr,Lf,Lf,"The key does not appear in the string.",cr,Lf,0
success BYTE cr,Lf,Lf, " The result string is : " ,cr,Lf,Lf
result BYTE 200 DUP (?)
PUBLIC _start ; make entry point public
.CODE
_start: output prompt1 ; ask for
input source,100 ; and input source string
lea eax, source ; find length of string
push eax ; length parameter
call strlen
mov srcLength,eax ; save length of source
output prompt2 ; ask for
input key,20 ; and input key string
lea eax, key ; find length of string
push eax ; length parameter
call strlen
mov keyLength,eax ; save length of key
output prompt3 ; ask for
input replace,20 ; and input replace string
lea eax, replace ; find length of string
push eax ; length parameter
call strlen
dec eax
mov repLength,eax ; save length of replace
; calculate last position of source to check
mov eax,srcLength
sub eax,keyLength
inc eax ; srcLength − keyLength + 1
mov lastPosn, eax
cld ; left to right comparison
mov eax,1 ; starting position
whilePosn: cmp eax,lastPosn ; position <= last_posn?
jnle endWhilePosn ; exit if past last position
lea esi,source ; address of source string
add esi,eax ; add position
dec esi ; address of position to check is incremented automatically
lea edi,key ; address of key
mov ecx,keyLength ; number of positions to check
repe cmpsb ; check
jz found ; exit on success
inc eax ; increment position
jmp whilePosn ; repeat
endWhilePosn:
output failure ; the search failed
jmp quit ; exit
;-------------------------------------------------------------
found:
mov keyPosition, eax ; position of key
mov ebx, eax ;copy start position of key
lea eax, source
sub ebx, eax ;position - source address
mov BeginLength, ebx ;begin Source length (before key)
add ebx, keyLength
mov eax, srcLength
sub eax, ebx
mov restLength, eax ;rest of Source length (after key)
mov eax, keyPosition
add eax, keyLength ; position + key
mov restPosition, eax
;source begin to result
lea eax, result
mov firstParam, eax ; destination address
lea eax, source
mov secondParam, eax
mov eax, BeginLength ; copy length
mov cpyLength, eax
mov esi,firstParam ;initial source address
mov edi,secondParam ;destination
mov ecx ,cpyLength
rep movsb ;copy bytes
;replace to result
mov eax, firstParam
add eax , BeginLength
mov firstParam, eax ; address of rest of result
lea eax, replace
mov secondParam, eax ; string to replace
mov eax, repLength ; copy length
mov cpyLength, eax
mov esi,firstParam ;initial source address
mov edi,secondParam ;destination
mov ecx ,cpyLength
rep movsb ;copy bytes
;Rest to result
mov eax, firstParam
add eax , repLength
mov firstParam, eax ; address of rest of result
mov eax, restPosition
mov secondParam, eax
mov eax, restLength
mov cpyLength, eax
mov esi,firstParam ;initial source address
mov edi,secondParam ;destination
mov ecx ,cpyLength
rep movsb ;copy bytes
mov BYTE PTR [edi],0 ;terminate destination string
output success
quit:
INVOKE ExitProcess, 0 ; exit with return code 0
;----------------------------------------------------------
strlen PROC NEAR32
; find length of string whose address is passed on stack
; length returned in EAX
push ebp ; establish stack frame
mov ebp, esp
pushf ; save flags
push ebx ; and EBX
sub eax, eax ; length := 0
mov ebx, [ebp+8] ; address of string
whileChar: cmp BYTE PTR [ebx], 0 ; null byte?
je endWhileChar ; exit if so
inc eax ; increment length
inc ebx ; point at next character
jmp whileChar ; repeat
endWhileChar:
pop ebx ; restore registers and flags
popf
pop ebp
ret 4 ; return, discarding parameter
strlen ENDP
END
found:
mov keyPosition, eax ; position of key
mov ebx, eax ;copy start position of key
lea eax, source
sub ebx, eax ;position - source address
mov BeginLength, ebx ;begin Source length (before key)
In these lines you have subtracted things that cannot be subtracted.
When you get at the label found, EAX has a 1-based relative position index that you copy to the EBX register. This value ranges from 1 to 100. Now you subtract the absolute address of your source buffer. This could be in the millions. That's clearly a mistake. It becomes disastrous when later on you use it as a loop counter and start corrupting memory!
success BYTE cr,Lf,Lf, " The result string is : " ,cr,Lf,Lf
You forgot to zero-terminate the success message.
It will disrupt your final macro call output success and so it would seem that the program didn't correctly replace the string.

Binary representation in processor's registers in Nasm

I would like to ask about process of put instructions into registers. For example: we want to overwrite count '50' into EBX (in ASCII '50' is count '2').
EBX consists of 32 bits. When we put '50' into it, it will be arranged as binary represent, yes? (0000000 | 00000000 | 00000000 | 00110010). Have a right? What happens with bits, when we place a string into register?
EAX holds 32 bits which Intel calls "integer". The programmer - and sometimes the assembler - decides how to interpret these bits. If you load EAX with the number 50 (not the string '50')
mov eax, 50
the assembler decides to generate a machine instruction that loads the 50 in a manner, that you can read it as number 50 in a binary system:
00000000000000000000000000110010
Try out, what the assembler does if you feed it with a string:
GLOBAL _start
SECTION .bss
outstr resb 40
SECTION .data
_start:
mov eax, 'Four' ; Load EAX with a string
call int2bin ; Convert it to a binary string in outstr
mov byte [edi], 10 ; Add a line feed
inc edi ; Increment the pointer
mov eax, 4 ; SYS_WRITE
mov ebx, 1 ; STDOUT
mov ecx, outstr ; Pointer to output buffer
mov edx, edi ; Count of bytes to send:
sub edx, outstr ; EDX = EDI (offset returned from int2bin) - offset of output buffer
int 0x80 ; Call kernel
mov eax, 1 ; SYS_EXIT
xor ebx, ebx ; Returncode: 0 (ok)
int 0x80 ; Call kernel
int2bin: ; Converts an integer in EAX to a binary string in outstr
mov edi, outstr ; Pointer to a string
mov ecx, 32 ; Loop counter
.LL1:
test cl, 0b111 ; CL%8 = 0 ?
jnz .F ; No: skip the next instructions
mov Byte [edi], ' ' ; Store a space
inc edi ; and increment the pointer
.F:
shl eax, 1 ; The leftmost bit into carry flag
setc dl ; Carry flag into DL
or dl, '0' ; Convert it to ASCII
mov [edi], dl ; Store it to outstr
inc edi ; Increment the pointer
loop .LL1 ; Loop ECX times
mov byte [edi], 0 ; Null termination if needed as C string (not needed here)
ret
Output:
01110010 01110101 01101111 01000110
NASM stored it backwards in EAX. The ASCII of leftmost character is stored in the rightmost byte of EAX, the second-to-last character is to be found in the second byte, and so on. Better to see when those bytes are printed as ASCII characters:
GLOBAL _start
SECTION .bss
outstr resb 40
SECTION .data
_start:
mov eax, 'Four' ; Load EAX with a string
call int2str ; Convert it to a binary string in outstr
mov byte [edi], 10 ; Add a line feed
inc edi ; Increment the pointer
mov eax, 4 ; SYS_WRITE
mov ebx, 1 ; STDOUT
mov ecx, outstr ; Pointer to output buffer
mov edx, edi ; Count of bytes to send:
sub edx, outstr ; EDX = EDI (offset returned from int2bin) - offset of output buffer
int 0x80 ; Call kernel
mov eax, 1 ; SYS_EXIT
xor ebx, ebx ; Returncode: 0 (ok)
int 0x80 ; Call kernel
int2str: ; Converts an integer in EAX to an ASCII string in outstr
mov edi, outstr ; Pointer to a string
mov ecx, 4 ; Loop counter
.LL1:
rol eax, 8
mov [edi], al ; Store it to outstr
inc edi ; Increment the pointer
loop .LL1 ; Loop ECX times
mov byte [edi], 0 ; Null termination if needed as C string (not needed here)
ret
Output:
ruoF
Both programs above show EAX in big endian order. This is the order you are familiar with looking at decimal numbers. The most significant digit is left and the least significant digit is right. However, EAX would be saved in memory or disk in little endian order, starting the sequence from the right with the least significant byte. Looking at the memory with a disassembler or debugger you would see 'F','o','u','r' as well as you had defined it in a .data section with db 'Four'. Therefore you'll get no difference when you load a register with a string, save it to memory and call the write routine of the kernel:
GLOBAL _start
SECTION .bss
outstr resb 40
SECTION .data
_start:
mov eax, 'Hell' ; Load EAX with the first part of the string
mov ebx, 'o wo' ; Load EBX with the second part
mov ecx, 'rld!' ; Load ECX with the third part
mov dword [outstr], eax ; Store the first part in outstr (little endian)
mov dword [outstr+4], ebx ; Append the second part
mov dword [outstr+8], ecx ; Append the third part
mov eax, 4 ; SYS_WRITE
mov ebx, 1 ; STDOUT
mov ecx, outstr ; Pointer to output buffer
mov edx, (3*4) ; Count of bytes to send (3 DWORD à 4 bytes)
int 0x80 ; Call kernel
mov eax, 1 ; SYS_EXIT
xor ebx, ebx ; Returncode: 0 (ok)
int 0x80 ; Call kernel
Output:
Hello world!
Please note: This behavior is made by the NASM programmers. Other assemblers might have a different behavior.

How can I read one integer each time?

I'm using an assembly library to make a program that reads three integers from standard input. When the reading is done in the console it works perfectly, but when I use a file as input, it reads the three integers at once.
This is the strace for console:
read(0, "3000\n", 512) = 5
read(0, "2000\n", 512) = 5
read(0, "1000\n", 512) = 5
And this from input file:
read(0, "3000\n2000\n1000\n", 512) = 15
read(0, "", 512) = 0
read(0, "", 512) = 0
Here are the procedures:
;--------------------------------------------------------
ReadInt:
;
; Reads a 32-bit signed decimal integer from standard
; input, stopping when the Enter key is pressed.
; All valid digits occurring before a non-numeric character
; are converted to the integer value. Leading spaces are
; ignored, and an optional leading + or - sign is permitted.
; All spaces return a valid integer, value zero.
; Receives: nothing
; Returns: If CF=0, the integer is valid, and EAX = binary value.
; If CF=1, the integer is invalid and EAX = 0.
;--------------------------------------------------------
push edx
push ecx
; Input a signed decimal string.
mov edx,digitBuffer
mov ecx,MAX_DIGITS
call ReadString
mov ecx,eax ; save length in ECX
; Convert to binary (EDX -> string, ECX = length)
call ParseInteger32 ; returns EAX, CF
pop ecx
pop edx
ret
;--------------- End of ReadInt ------------------------
;--------------------------------------------------------
ReadString:
;
; Reads a string from the keyboard and places the characters
; in a buffer.
; Receives: EDX offset of the input buffer
; ECX = maximum characters to input (including terminal null)
; Returns: EAX = size of the input string.
; Comments: Stops when Enter key (0Dh,0Ah) is pressed. If the user
; types more characters than (ECX-1), the excess characters
; are ignored.
; Written by Kip Irvine and Gerald Cahill
; Modified by Curtis Wong
;--------------------------------------------------------
enter 8, 0 ; bufSize: ebp - 4
; bytesRead: ebp - 8
pushad
mov edi,edx ; set EDI to buffer offset
mov dword [ebp - 4],ecx ; save buffer size
call ReadKeys
mov dword [ebp - 8], eax
cmp eax,0
jz .L5 ; skip move if zero chars input
cld ; search forward
mov ecx, dword [ebp - 4] ; repetition count for SCASB
dec ecx
mov al,NL ; scan for 0Ah (Line Feed) terminal character
repne scasb
jne .L1 ; if not found, jump to L1
;if we reach this line, length of input string <= (bufsize - 2)
dec dword [ebp - 8] ; second adjustment to bytesRead
dec edi ; 0Ah found: back up two positions
cmp edi,edx ; don't back up to before the user's buffer
jae .L2
mov edi,edx ; 0Ah must be the only byte in the buffer
jmp .L2 ; and jump to L2
.L1: mov edi,edx ; point to last byte in buffer
add edi,dword [ebp - 4]
dec edi
mov byte [edi],0 ; insert null byte
; Clear excess characters from the buffer, 1 byte at a time
.L6: call BufferFlush
jmp .L5
.L2: mov byte [edi],0 ; insert null byte
.L5: popad
mov eax, dword [ebp - 8]
leave
ret
;--------------- End of ReadString --------------------
You will need to buffer the input and split it because the console and files behave slightly different. A console will send you data as soon as someone presses Return, that is line by line.
Files will send you as much data as possible per call to read().
To make your code work, you will have to write a readline() function that reads the input byte by byte and returns when it sees a line feed.
Or you can use an internal buffer, fill it with as much data as possible, find the first line, return that, repeat until the buffer is empty, try to read more data, return EOF when there is no more data from the input.
As Aaron points out, the problem is that sys_read behaves differently when stdin is redirected. You could fix it as he suggests. or you could use Along32's ReadString and use a "homemade" atoi.
;--------------------
atoi:
push ebx
mov edx, [esp + 8] ; pointer to string
xor ebx, ebx ; assume not negative
cmp byte [edx], '-'
jnz notneg
inc ebx ; indicate negative
inc edx ; move past the '-'
notneg:
xor eax, eax ; clear "result"
.top:
movzx ecx, byte [edx]
inc edx
cmp ecx, byte '0'
jb .done
cmp ecx, byte '9'
ja .done
; we have a valid character - multiply
; result-so-far by 10, subtract '0'
; from the character to convert it to
; a number, and add it to result.
lea eax, [eax + eax * 4]
lea eax, [eax * 2 + ecx - '0']
jmp short .top
.done:
test ebx, ebx
jz notminus
neg eax
notminus:
pop ebx
ret
;------------------------
That expects the address of the string to be pushed on the stack and "removed" after, but I think you could just comment out that second line, and pass the address in edx (not tested!). More like the rest of the Along32 code that way. Unlike Along32's code, it returns with edx pointed to the next byte, and ecx (just cl, really) containing the "invalid" byte that stopped processing. I think you could call it repeatedly on the string returned by ReadString, saving the integer (in eax) and calling it again (without touching edx) if ecx is LF. When ecx is zero, you're done. Hope you find it helpful.

linux nasm assembly print all numbers from zero to 100

I am writing a program to print out all the numbers from zero to 100. The only reason I am doing this is to test out printing out multiple digit numbers.
The problem that I am having is that my program is only printing out the numbers 1 and 2. I have no idea why. My compiler compiles fine, without error, as well as no linker errors.
Here is my code:
SECTION .data
len EQU 32
NUL EQU 0
countlen EQU 8
SECTION .bss
counter resb countlen
strlen resb countlen
SECTION .text
GLOBAL _start
_start:
mov BYTE[counter], 1 ; set counter to 1
mov BYTE[strlen], 1 ; set string length counter to 1
mov ecx, counter ; move the counter to ecx
add BYTE[ecx], NUL ; add null terminator to ecx
mov esi, 9 ; move 9 to esi
Length:
cmp [counter], esi ; compare counter to esi
jle Set ; if equal, goto set
inc BYTE[strlen] ; increment the string size
mov eax, 10 ; move 10 to eax
mov ebx, esi ; move esi to ebx
mul ebx ; multiply ebx by esi
add eax, 9 ; add nine to the result
mov esi, eax ; move the result to esi
jmp Length ; jump to Length
Set:
mov esi, 9 ; reset checker
Check:
cmp BYTE[strlen], 1 ; is it one digit?
je Single ; if yes, jump to single
cmp BYTE[strlen], 3 ; is it 100?
je Exit ; if yes, jump to Exit
Print: ; this section deals with multi-digit numbers
cmp BYTE[ecx], NUL ; check if end of string
je Exit ; if equal goto exit
mov eax, 4
mov ebx, 1
mov edx, 1
int 80h ; print number
inc ecx ; point to next digit in number
jmp Print ; jump to Print
Single: ; this section deals with single digit numbers add BYTE[counter], '0' ; convert to ASCII
mov eax, 4
mov ebx, 1
mov ecx, counter
mov edx, countlen
int 80h ; print the digit
jmp Length ; go back
Exit: ; Exit section
mov eax, 1 ; sys_exit
mov ebx, 0 ; return 0
int 80h ; syscall
Why does it do this? Also, what do I need to change to get it to work as expected?
Thanks in advance,
RileyH
UPDATE:
Edited to include the 'Print' label
This is my function to print the digits on stdout. It's in AT&T sorry ;)
movl <your decimal here>, %eax
xor %ecx, %ecx # the counter
movl $10, %ebx
loop:
xor %edx, %edx
div %ebx # isolate the last digit, remainder in edx
add $48, %dx # '0' is 48 in ascii, result is the ascii equivalent
shl $8, %dx # move the ascii byte to %dh
pushw %dx # puch ascii code on the stack
inc %esp # point to the ascii byte! (discard %dl)
inc %ecx # count the digits
cmp $0, %eax
jnz loop
movl $4, %eax # write()
movl $1, %ebx # stdout
movl %ecx, %edx # now edx holds the number of digits
movl %esp, %ecx # load the address of string array
int $0x80 # the string array is on top of the stack
Cheers!
You need to convert the number to an ASCII digit(s) in order to print to terminal. Now I won't give you my dwtoa, that will take the fun out of learning, but you could do something like this:
sys_exit equ 1
sys_write equ 4
stdout equ 1
SECTION .bss
lpBuffer resb 4
SECTION .text
GLOBAL _start
_start:
xor esi, esi
.NextNum:
call PrintNum
inc esi
cmp esi, 100
jna .NextNum
.Exit:
mov eax, sys_exit
xor ebx, ebx
int 80h
;~ #####################################################################
PrintNum:
push lpBuffer
push esi
call dwtoa
mov edi, lpBuffer
call GetStrlen
inc edx
mov ecx, lpBuffer
mov eax, sys_write
mov ebx, stdout
int 80H
ret
;~ #####################################################################
GetStrlen:
push ebx
xor ecx, ecx
not ecx
xor eax, eax
cld
repne scasb
mov byte [edi - 1], 10
not ecx
pop ebx
lea edx, [ecx - 1]
ret
Notice, I use things like sys_exit, sys_write, stdout, instead of hard coded numbers. Makes code a bit more self documenting.
EDIT: it's not an "error" per se, but just misdirection for the casual reader to access a counter and strlen as one byte variables and in other places compare the contents to 32-bit variables...
add BYTE[ecx], NUL
this perhaps adds NUL terminator to ecx, but I suppose it should append the terminator.
That could happen at place [ecx+1].
Anyway the handling of the variables and pointers is very unconventional in your code...
First: the kernel functions that 'output' stuff, assume that ecx contains the address of a string. There isn't a string allocated anywhere. If the string would just fit into the eight bytes reserved to counter at counter resb 8, and the counter would contain characters: '1','3','\0' then the approach would work. And this reveals the second thing: printf deals with strings, which encode single digits 0-9 to values 48-57. Space e.g. in this ASCII system encodes to 32 (decimal) while \NUL is ascii zero.
So, what is needed:
option 1
Initialize your counter to a string
counter db '0','0','0','0','0','0','0','1'
length dq 1
Ascii zero is not needed to terminate the string, because as I understood, it's
given to the print function
Then one can give the real pointer to the string as
lea ecx, counter // get the address of counter string
add ecx, 7 // this is the last character
Also one can increase the counter as a string one digit at a time:
loop:
mov al,[ecx] // assuming ecx still points to last character
inc al
mov [ecx],al
cmp al, '9'
jle string ok
mov al, '0'
mov [ecx],al
dec ecx
jmp loop
ok: // here the counter has been increased correctly
option 2
Increase the counter as a 32-bit integer
Convert the integer to string one digit at a time with the following algorithm:
digits = 0;
string_ptr = &my_string[32]; // move barely outside the string
do {
last_digit = a % 10 + '0'; // calculate the last digit and convert to ASCII
a = a / 10;
*--string_ptr = last_digit; // write the last digit
digits++; // count the number of digits
} while (a);
// because we predecrement string_ptr, that value also contains the exact
// start of the first character in the printable string. And digits contains the length.
To produce some good looking result, one has to still add line feeds. That can be handled separately or just appended to the original string -- and ensure they are never written over, so they can be used in all cases.

Resources