News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

My first post: Array of Strings in Assembly.

Started by AssemblyChallenge, August 20, 2014, 07:24:56 AM

Previous topic - Next topic

AssemblyChallenge

Hi all.

As I found some answers while googling around in MASM's forums, I thought it would be fair enough to start my presence here by giving instead of asking in my first post. I have been teaching myself Assembly for the last months by reading a great book and bumping my head against the computer xD xD Also, english is not my native language so I apologize for any mistake.

In recent days I took a very interesting reading about Structures in Assembly, from there, I got the tools I needed to start and create a static Array. I know, I know, most part of the time we'll be dealing with dinamic structures but there are times when you just need an array, plain and simple, where you can store and/or access several lines of text quick and easy. As with others high level languages, a static array of strings has several details:

- Every element is exactly the same size (in memory).
- If the text lines are very short you would be wasting memory.
- You must be careful with long lines of text that could overwrite other elements in the array.

* The first is also a big advantage, as you can use an index to access every element quickly.
* The second is to be spected as every text has its own length. Just declare the primary string (structure) long enough for what you need.
* If you use the array for storage (ie. a text file) make sure every line is shorter than the primary structure at runtime so it can be truncated, etc before storing inside the array.

Now some code. =) =) Here is a very simple example, declaring an small array with 3 strings preloaded and a loop printing the text. In my case, I made a console project in Easycode so you should do the same with your IDE and fit the code.

There are too many comments but I'm aiming at the poor noobs like me xD xD


; please include masm32.lib and masm32.inc for the printing part.

.Const

MaxString Equ 30 ; lenght of every line of text; this way you can give maintenance better ;-)
MaxArray Equ 2 ; three elements in array (0, 1, 2)

.Data?

.Data

LoopCounter DD 3

; main structure containing the string itself

StringStruct Struct
MyString DB MaxString Dup(0) ; VERY important as you can't add the "0,13, etc" at the end.
StringStruct EndS

; declare array and preload text on it
; you can, of course, leave it empty and fill it at runtime

MyArray StringStruct MaxArray Dup (<"First text">, <"Second text">, <"Final text">)
; name     type          elements                     text stored

LineFeed DB 13, 10, 0 ; change line after you print it

Main Proc Private

Mov Ecx, LoopCounter ; initialize print loop
Mov Edi, 0 ; EDI is the index in order to access every element

@MyLoop:
     Push Ecx ; avoid disaster with StdOut
     Invoke StdOut, Addr (StringStruct Ptr MyArray[Edi]).MyString  ; print element at Array[Edi]
     Invoke StdOut, Addr LineFeed ; same as Enter at the end
     Pop Ecx ; restore loop counter
     Add Edi, Type StringStruct ; move EDI to the next element in array
Loop @MyLoop

Ret
Main EndP


Greetings to Masm32's comunity. =) =)

Gunther

Hi AssemblyChallenge,

you should check the examples and tutorials in the MASM32 package. And welcome to the forum.

Gunther
You have to know the facts before you can distort them.

jj2007

Hi AssemblyChallenge,

Nice first post :t

Welcome to the Forum :icon14:

Zen

Zen

Farabi

Hi, for a string you can allocate a dynamic memory for the string where the pointer is placed on another array.



.data
string_holder dd 0,0,0,0,0,0,0,0
string dd 0

.code
invoke GlobalAlloc,GMEM_FIXED or GMEM_ZEROINIT,1024
mov string,eax
invoke lstrcat,string,CADD("My First string")

lea edx,string_holder
add edx,0*4
mov [eax],string


invoke GlobalAlloc,GMEM_FIXED or GMEM_ZEROINIT,1024
mov string,eax
invoke lstrcat,string,CADD("My Second string")

lea edx,string_holder
add edx,1*4
mov [eax],string

invoke GlobalAlloc,GMEM_FIXED or GMEM_ZEROINIT,1024
mov string,eax
invoke lstrcat,string,CADD("My Third string")
http://farabidatacenter.url.ph/MySoftware/
My 3D Game Engine Demo.

Contact me at Whatsapp: 6283818314165

AssemblyChallenge

Quote from: Zen on August 20, 2014, 09:27:02 AM
ASSEMBLYCHALLENGE,

You will probably find this helpful: MASM Programmer's Guide (pdf)

Thank you! Already got a copy  8)

The book I'm reading is ''Assembly Language for x86 Processors (6th Edition)'' by Kip Irvine and so far no complains; it's very complete and not so hard to understand. The only detail: He uses Visual Studio -wich is fine- but sometimes I have to make a few "translations" for Easycode.

Greetings.

AssemblyChallenge

@Farabi:

That's great and the best approach -I guess- for bigger text arrays. My example tries to give a hand with simpler problem: small arrays, no need for GlobalAlloc and so on. In my own case, I made a small project a couple of weeks ago where dinamic memory was overkill  :t

Regards.

AssemblyChallenge

Helping a little bit more:

As every element is in the same memory block, you have instant access to any of them. Let's say you have a 10 elements array (from 0..9) and you need to read/write element 4, assuming EDI as index again is very easy, all you have to do is multiply the element (4) by the size of the string structure:


.Data

MyIndex DD 0 ; put here the element to access

.Code
.

    Mov MyIndex, 4  ; fourth element please (or whatever between 0..9)
    Mov Eax, Type StringStruct  ; size of structure
    Mul MyIndex   ; multiply and store displacement in EAX
    Jc @Overflow  ; just in case as EDX is used too if result too big
    Mov Edi, Eax   ; save final result in EDI
.
.
   ... whatever...  Addr (StringStruct Ptr MyArray[Edi]).MyString



hutch--

Making a static array in either the initialised or uninitialised data section is easy enough and occasionally useful but dynamic array are far more powerful and can be allocated in their millions of members. One thing you will need to be careful with using the Irvine book is not properly preserving registers. The Windows specification is known as the Intel ABI which involves preserving EBX ESI EDI EBP and ESP between procedure calls. A normal MASM proc preserves EBP and ESP but if you use any of EBX ESI or EDI you need to preserve them before the proc uses them and restore them before the proc exits.

The spec allows you to trash EAX ECX and EDX and you should write your code accordingly.

Here is a small example for you. Plonk this into the MASM32 editor and build it with the normal option.


IF 0  ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
                      Build this template with "CONSOLE ASSEMBLE AND LINK"
ENDIF ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    include \masm32\include\masm32rt.inc

    .data
      aarr dd 0,1,2,3,4,5,6,7,8,9

    .code

start:
   
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    call main
    inkey
    exit

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

main proc

    push ebx                    ; preserve these 2 as they are used
    push esi

    mov esi, 10                 ; use ESI as the counter

    mov ebx, OFFSET aarr        ; put the address in EBX

  @@:
    print str$([ebx]),13,10     ; display dereferenced array item (each number)
    add ebx, 4                  ; set address to next number
    sub esi, 1                  ; decrement the counter
    jnz @B

    pop esi                     ; restore them again before exit
    pop ebx

    ret                         ; bye bye

main endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

end start


Result will look like this.


0
1
2
3
4
5
6
7
8
9
Press any key to continue ...


jj2007

Quote from: hutch-- on August 20, 2014, 03:46:39 PM
The spec allows you to trash EAX ECX and EDX

But the ABI does not force you to trash these regs. For example, if a coder for whatever strange reasons thinks that ecx is a precious register that should be preserved between calls to a library, as demonstrated here, then he is absolutely free to do so. No (legit) sheriffs will show up and throw you in the coder's jail 8)

hutch--

Yes there is, its called the Windows API, it will say nasty things to you for making mistakes before it closes your program down.

I have had this problem for years, most of the old fellas know how to use registers in unconventional ways but they try and teach the learners the right way first rather than mislead them into writing broken unreliable code that may work on one OS version and crash on the next. Microsoft wrote the API in conformity with the Intel ABI, unless you are writing your own OS you have little choice apart from the occasional unconventional code that runs behind correct register preservations lower down the call tree.

jj2007

Quote from: hutch-- on August 20, 2014, 04:54:48 PM
Yes there is, its called the Windows API, it will say nasty things to you for making mistakes before it closes your program down.

The Windows API is one thing, a "private" library is another thing. Sure, invoke Sleep, 500 will trash ecx, but Delay 500 won't, and it's documented, and it's legal code that will never crash, even in Windows 19.3 :biggrin:

qWord

AssemblyChallenge,
just some some notes to the code of your first post:
- Equates (EQU) exist only while assembling thus there is not need to put their declaration into the .const-section
- Because  MyArray is already correctly typed, you could directly write: Addr MyArray[Edi].MyString
- The type-operator is not need in your example, because MASM does resolve type-IDs to their size in bytes if not used for declaration or definition: Add Edi, StringStruct
MREAL macros - when you need floating point arithmetic while assembling!

hutch--

 I still fail to see the point of misleading beginners when they need to learn the ABI to write reliable code. Without it they are being lead down the garden path writing unreliable garbage. The notion of "private library" is fine IF the end user knows its deviant and uses a different register convention to the standard ABI that Microsoft support.

"Sure, invoke Sleep, 500 will trash ecx, " This may be fine in one version of Windows but you have no way of knowing what it will be in another and this is the problem of testing on one version. Having written code for every version of NT4 sp1/2/3/4/5/6, win95 a/b/c win98/se Win2000 sp 1/2/3/4, XP sp 1/2/3 and now Win7, you can be sure that its not the same as Microsoft compilers have changed over time.

AssemblyChallenge

Quote from: qWord on August 20, 2014, 07:15:11 PM
AssemblyChallenge,
just some some notes to the code of your first post:
- Equates (EQU) exist only while assembling thus there is not need to put their declaration into the .const-section
- Because  MyArray is already correctly typed, you could directly write: Addr MyArray[Edi].MyString
- The type-operator is not need in your example, because MASM does resolve type-IDs to their size in bytes if not used for declaration or definition: Add Edi, StringStruct

Thank you, qWord, I'll try and see how it goes xD xD My example is based on Invine's book. The whole structure addressing thing is a little bit intimidating and that was one of the reasons I choose it; kind of pulling the ghost's sheet  :bgrin: :bgrin: