Array of pointers, VirtualAlloc and RtlMoveMemory. MASM, some kind of problem.

  • Release Candidate 6
    Guest:
    We are at a “proposed final” true release candidate with nothing known remaining to be changed or fixed. For the full story, please see this page in the "Pre-Release Announcements & Feedback" forum.
    /Steve.
  • Be sure to checkout “Tips & Tricks”
    Dear Guest Visitor → Once you register and log-in:

    This forum does not automatically send notices of new content. So if, for example, you would like to be notified by mail when Steve posts an update to his blog (or of any other specific activity anywhere else), you need to tell the system what to “Watch” for you. Please checkout the “Tips & Tricks” page for details about that... and other tips!

    /Steve.

coffeeprogrammer

Well-known member
Jul 19, 2021
116
11
Does anybody know how to fix the addElement function so it ends up as another element in the array. The idea is a dynamic array, where arrayPtr is a pointer to the first element, then new elements can be added dynamically and kept track of by increasing the arrayPtr value. So in-fact I think what it would end up being is an array of pointers to DbRecord structs in memory. Allocated by VirtualAlloc and copied by RtlMoveMemory. I am kinda of hung up on RtlMoveMemeory line. I feel like my line of thinking is correct.
Code:
.386
.model flat, stdcall
option casemap :none

include windows.inc
include user32.inc
include kernel32.inc

addElement PROTO: ptr DbRecord

.data?
    DbRecord struct
        Id        dd    ?
        WordOne        db 32    dup(?) ; db is define byte, set value of byte
        WordTwo        db 32    dup(?)
        WordThree    db 32    dup(?)
        Year        dd    ?
    DbRecord ends

    arrayPtr dd ?        ; pointer in memory to start of array
    newElementPointer DbRecord <>
    hStdOut dd ?
    bytesWritten dd ?

.data
    arrayCount dd 0
    hello db 'Hello World!', 0

.code
main proc
    LOCAL DbRecord01:DbRecord
    mov [DbRecord01.Id], 1;

    ; any other way than one character at a time?
    mov byte ptr [DbRecord01.WordOne], 'D'
    mov byte ptr [DbRecord01.WordOne + 1], 'o'
    mov byte ptr [DbRecord01.WordOne + 2], 'g'
    mov byte ptr [DbRecord01.WordOne + 3], 0

    mov byte ptr [DbRecord01.WordTwo], 'C'
    mov byte ptr [DbRecord01.WordTwo + 1], 'a'
    mov byte ptr [DbRecord01.WordTwo + 2], 't'
    mov byte ptr [DbRecord01.WordTwo + 3], 0

    mov byte ptr [DbRecord01.WordThree], 'E'
    mov byte ptr [DbRecord01.WordThree + 1], 'y'
    mov byte ptr [DbRecord01.WordThree + 2], 'e'
    mov byte ptr [DbRecord01.WordThree + 3], 0

    mov [DbRecord01.Year], 2022;

    invoke GetStdHandle, STD_OUTPUT_HANDLE
    mov [hStdOut], eax

    invoke WriteConsole, hStdOut, offset hello, sizeof hello, offset bytesWritten, NULL
    invoke addElement, addr DbRecord01

    ret
main endp

addElement proc DbRecordPointer: ptr DbRecord
    invoke VirtualAlloc, NULL, sizeof DbRecord, MEM_COMMIT, PAGE_READWRITE  ; I beleive store a memory address in eax
    invoke RtlMoveMemory, DbRecord ptr [eax], DbRecordPointer, sizeof DbRecord ; but how to use that memory address here?
    ret
addElement endp

end main
 
PS I asked on StackOver flow as well and someone asked if it was an assembler error or a runtime error, it is a assembler error

INVOKE argument type mismatch : argument : 1

on the invoke RtlMoveMemory line, that line was just one of my not fully educated guesses.
 
ok I am closer, but I still have a problem
How do I get the value of eax ("memory location from VirtualAlloc",) where data was copied into arrayPtr (arrayPtr + count * sizeof DbRecord)

Code:
.386

.model flat, stdcall

option casemap :none


include windows.inc

include user32.inc

include kernel32.inc


addElement PROTO: ptr DbRecord


.data?

    DbRecord struct

        Id        dd    ?

        WordOne        db 32    dup(?) ; db is define byte, set value of byte

        WordTwo        db 32    dup(?)

        WordThree    db 32    dup(?)

        Year        dd    ?

    DbRecord ends


    arrayPtr dword ?        ; pointer in memory to start of array

;    newElementPointer DbRecord <>

    hStdOut dd ?

    bytesWritten dd ?


.data

    arrayCount dd 0

    hello db 'Hello World!', 0


.code

main proc

    LOCAL DbRecord01:DbRecord

    mov [DbRecord01.Id], 1;


    ; any other way than one character at a time?

    mov byte ptr [DbRecord01.WordOne], 'D'

    mov byte ptr [DbRecord01.WordOne + 1], 'o'

    mov byte ptr [DbRecord01.WordOne + 2], 'g'

    mov byte ptr [DbRecord01.WordOne + 3], 0


    mov byte ptr [DbRecord01.WordTwo], 'C'

    mov byte ptr [DbRecord01.WordTwo + 1], 'a'

    mov byte ptr [DbRecord01.WordTwo + 2], 't'

    mov byte ptr [DbRecord01.WordTwo + 3], 0


    mov byte ptr [DbRecord01.WordThree], 'E'

    mov byte ptr [DbRecord01.WordThree + 1], 'y'

    mov byte ptr [DbRecord01.WordThree + 2], 'e'

    mov byte ptr [DbRecord01.WordThree + 3], 0


    mov [DbRecord01.Year], 2022;


    invoke GetStdHandle, STD_OUTPUT_HANDLE

    mov [hStdOut], eax


    invoke WriteConsole, hStdOut, offset hello, sizeof hello, offset bytesWritten, NULL

    invoke addElement, addr DbRecord01


    ret

main endp


addElement proc uses edx DbRecordPointer: ptr DbRecord

    Local newElementPointer: Dword

    invoke VirtualAlloc, NULL, sizeof DbRecord, MEM_COMMIT, PAGE_READWRITE  ; I beleive store a memory address in eax

    mov newElementPointer, eax


    ;invoke RtlMoveMemory, newElementPointer , DbRecordPointer, sizeof DbRecord ; but how to use that memory address here?

    invoke RtlMoveMemory, eax , DbRecordPointer, sizeof DbRecord

    mov edx, arrayCount

    inc edx

    mov arrayCount, edx

    ;mov dword ptr [arrayPtr+arrayCount], eax ; How do I get the value of eax ("memory location of VirtualAlloc",) where copied into arrayPtr


    ret

addElement endp


end main
 
Well I am not sure if anyone cares, but in the past I have a little frustrated with assembler, because when I hit a wall it would take a lot longer to overcome than with higher level languages. But with ChatGPT and stackover flow I made good progress the last few days. This is the program I was trying to get working. Next I need to save it to a data file and restore. Learned LEA and repne scasb and similar string operations.




Code:
.386
.model flat, stdcall
option casemap :none

include windows.inc
include user32.inc
include kernel32.inc

addElement PROTO: ptr DbRecord
readArray PROTO
stringLength PROTO : ptr dword

.data?
    DbRecord struct
        Id        dd    ?
        WordOne        db 32    dup(?) ; db is define byte, set value of byte
        WordTwo        db 32    dup(?)
        WordThree    db 32    dup(?)
        Year        dd    ?
    DbRecord ends

    array dword 512 dup(?)        ; pointer in memory to start of array
;    newElementPointer DbRecord <>
    hStdOut dd ?
    bytesWritten dd ?

.data
    arrayCount dd 0
    hello db 'Hello World!', 0

.code
main proc
    LOCAL DbRecord01:DbRecord
    LOCAL DbRecord02:DbRecord
    mov [DbRecord01.Id], 1;

    ; any other way than one character at a time?
    mov byte ptr [DbRecord01.WordOne], 'D'
    mov byte ptr [DbRecord01.WordOne + 1], 'o'
    mov byte ptr [DbRecord01.WordOne + 2], 'g'
    mov byte ptr [DbRecord01.WordOne + 3], 0

    mov byte ptr [DbRecord01.WordTwo], 'C'
    mov byte ptr [DbRecord01.WordTwo + 1], 'a'
    mov byte ptr [DbRecord01.WordTwo + 2], 't'
    mov byte ptr [DbRecord01.WordTwo + 3], 0

    mov byte ptr [DbRecord01.WordThree], 'E'
    mov byte ptr [DbRecord01.WordThree + 1], 'y'
    mov byte ptr [DbRecord01.WordThree + 2], 'e'
    mov byte ptr [DbRecord01.WordThree + 3], 0
    mov [DbRecord01.Year], 2022;

    mov [DbRecord02.Id], 2;

    ; any other way than one character at a time?
    mov byte ptr [DbRecord02.WordOne], 'C'
    mov byte ptr [DbRecord02.WordOne + 1], 'a'
    mov byte ptr [DbRecord02.WordOne + 2], 'r'
    mov byte ptr [DbRecord02.WordOne + 3], 0

    mov byte ptr [DbRecord02.WordTwo], 'H'
    mov byte ptr [DbRecord02.WordTwo + 1], 'o'
    mov byte ptr [DbRecord02.WordTwo + 2], 'u'
    mov byte ptr [DbRecord02.WordTwo + 3], 's'
    mov byte ptr [DbRecord02.WordTwo + 4], 'e'
    mov byte ptr [DbRecord02.WordTwo + 5], 0

    mov byte ptr [DbRecord02.WordThree], 'W'
    mov byte ptr [DbRecord02.WordThree + 1], 'i'
    mov byte ptr [DbRecord02.WordThree + 2], 'n'
    mov byte ptr [DbRecord02.WordThree + 3], 'd'
    mov byte ptr [DbRecord02.WordThree + 4], 'o'
    mov byte ptr [DbRecord02.WordThree + 5], 'w'
    mov byte ptr [DbRecord02.WordThree + 6], 's'
    mov byte ptr [DbRecord02.WordThree + 7], 0

    mov [DbRecord02.Year], 2002;
        
    invoke GetStdHandle, STD_OUTPUT_HANDLE
    mov [hStdOut], eax

    invoke WriteConsole, hStdOut, offset hello, sizeof hello, offset bytesWritten, NULL
    invoke addElement, addr DbRecord01
    invoke addElement, addr DbRecord02
    invoke readArray

    invoke ExitProcess, 0
    ret
main endp

addElement proc uses edx DbRecordPointer: ptr DbRecord
    Local newElementPointer: Dword
    invoke VirtualAlloc, NULL, sizeof DbRecord, MEM_COMMIT, PAGE_READWRITE
    mov newElementPointer, eax
    invoke RtlMoveMemory, eax , DbRecordPointer, sizeof DbRecord
    mov edx, arrayCount
    mov dword ptr  [array +4*edx], eax
    inc edx
    mov arrayCount, edx

    ret
addElement endp

readArray proc uses ebx
    xor ebx, ebx
    .while ebx < arrayCount
        mov eax, [array+4*ebx]
        lea eax, [eax+offset DbRecord.WordOne]
        invoke stringLength, eax
        mov edx, eax
        mov eax, [array+4*ebx]
        lea eax, [eax+offset DbRecord.WordOne]

        invoke WriteConsole, hStdOut, eax, edx, offset bytesWritten, NULL

        mov eax, [array+4*ebx]
        lea eax, [eax+offset DbRecord.WordTwo]
        invoke stringLength, eax
        mov edx, eax
        mov eax, [array+4*ebx]
        lea eax, [eax+offset DbRecord.WordTwo]

        invoke WriteConsole, hStdOut, eax, edx, offset bytesWritten, NULL

        mov eax, [array+4*ebx]
        lea eax, [eax+offset DbRecord.WordThree]
        invoke stringLength, eax
        mov edx, eax
        mov eax, [array+4*ebx]
        lea eax, [eax+offset DbRecord.WordThree]

        invoke WriteConsole, hStdOut, eax, edx, offset bytesWritten, NULL

        inc ebx
    .endw
    ret
readArray endp

stringLength proc uses ecx stringPtr: ptr dword

    ; find length of string
    mov edi, stringPtr
    mov ecx, -1
    mov al, 0
    repne scasb
    not ecx

    mov eax, ecx
    ret

stringLength endp

end main
 
I'm looking at your posts. My assembly programs were for DOS using MASM 5.1. I'm not used to things like 32-bit registers, proc, invoke, PROTO, .while and so on. I noticed your question, "any other way to do this than one character at a time" REP MOVSB will copy a string from DS: (E)SI to ES: (E)DI.
 
Last edited:
Wow, cool, I will have to see if I can figure that out exactly. Assembler feels a whole new world for me, I know my programs are short but once I get an idea I feel compelled to stick with it until I have something working. I feel like if I make a lot of small simpler programs to figure things out, at some point I might be able to write bigger programs. I recently figured out putting the & character in front of variable in the watch windows to see the full array, I had a hard time debugging asm when I started which did not help. Thanks a lot for the tip, always welcome.
 
I'm looking at your posts. My assembly programs were for DOS using MASM 5.1. I'm not used to things like 32-bit registers, proc, invoke, PROTO, .while and so on. I noticed your question, "any other way to do this than one character at a time" REP MOVSB will copy a string from DS: (E)SI to ES: (E)DI.
32 bit registers, at some point I want to tackle 64-bit, assembler will never die, I know the calling convention is different. I've thought about getting a Win98 VM and doing some dos programming, but I have not because there are so many modern things I need to learn. I am also trying to tackle ReactOS and I've read a few Windows Kernel/System programming books. So no time for DOS really and then there is Linux. :)
 
You've probably already found info on the MOVS instructions. Here's one on Stack Overflow that has a fairly detailed answer:

To get you started:
Set DS and ES if necessary
MOV ESI, source address *
MOV EDI, destination address *
MOV ECX, number of bytes to move
CLD so that ESI and EDI increment (STD would make them decrement)
REP MOVSB copies bytes from DS:ESI to ES:EDI and increments ESI and EDI after each byte (note: copies, not moves)

* You can also use LEA

You can get the Intel software development manuals here:

For comparison here's my 1983 Programmer's Reference Manual:
20230118_105212s.jpg
 
Last edited: