65.9K
CodeProject 正在变化。 阅读更多。
Home

MASM 汇编中的二维列表实现

starIconstarIconstarIconstarIconstarIcon

5.00/5 (7投票s)

2016年2月27日

CPOL

4分钟阅读

viewsIcon

17439

downloadIcon

436

使用 C 函数 realloc 在 MASM 汇编中实现列表数据结构

引言

在本文中,您将看到一个使用 MASM 汇编并结合 C 函数 realloc 实现的二维列表数据结构。

背景

汇编语言没有内置的List数据结构,对于 MASM,其dup语法在创建预定义大小的数组方面能力有限。通常,此数组必须定义得比实际所需大小更大,在某些情况下,最大尺寸在定义数组之前很难评估。

本文介绍了一种新的List数据结构来实现基本的列表操作,即添加新元素,按索引获取元素。还介绍了一个二维List结构。新的List结构支持DWORDREAL8数据类型来存储元素数据。

数据结构

以下是List和二维List的数据结构。在这些数据结构中,定义了一些新的类型

PDWORD TYPEDEF PTR DWORD
PREAL8 TYPEDEF PTR REAL8
PLISTI TYPEDEF PTR ListI
PLISTR TYPEDEF PTR ListR

ListI 数据结构

ListI STRUCT
    p PDWORD ? ; pointer to beginning address of the list data
    n DWORD ?  ; number of elements
ListI ENDS

ListR 数据结构

ListR STRUCT
    p PREAL8 ? ; pointer to beginning address of the list data
    n DWORD ?  ; count of elements
ListR ENDS

List2DI 数据结构

List2DI STRUCT
    dp PDWORD ?  ; pointer to beginning address of row data
    np PDWORD ?  ; pointer to beginning address of number of elements in row, or of number of columns in row
    n DWORD ?    ; number of rows
List2DI ENDS

List2DR 数据结构

List2DR STRUCT
    dp PREAL8 ?  ; pointer to beginning address of row data
    np PDWORD ?  ; pointer to beginning address of number of elements in row, or of number of columns in row
    n DWORD ?    ; number of rows
List2DR ENDS

代码如下。

代码

List实现的所有函数都在两个 ASM 文件中,List.asmList.inc

ListUtility.asm是基于List库的便捷函数集合。

这些文件中的大多数函数都带有自明注释。

这是List.inc

.686p    

.model flat, c  ; c calling convention

PDWORD TYPEDEF PTR DWORD
PREAL8 TYPEDEF PTR REAL8
PLISTI TYPEDEF PTR ListI
PLISTR TYPEDEF PTR ListR

;------------------------------
; public function prototypes
;------------------------------

; C function prototypes from C Runtime Library 
" C:\Program Files\Microsoft Visual Studio 10.0\VC\lib\msvcrt.lib"
; From MSDN defination

printf PROTO,
   formatString: PTR BYTE,
   printList: VARARG

ListI STRUCT
    p PDWORD ? ; pointer to beginning address of the list
    n DWORD 0  ; count of elements, initialize with default 0
ListI ENDS

ListR STRUCT
    p PREAL8 ? ; pointer to beginning address of the list
    n DWORD 0  ; count of elements, initialize with default 0
ListR ENDS

List2DI STRUCT
    dp PDWORD ?  ; pointer to beginning address of row data
    np PDWORD ?  ; pointer to beginning address of number of elements in row
    n DWORD 0    ; number of rows
List2DI ENDS

List2DR STRUCT
    dp PREAL8 ?  ; pointer to beginning address of row data
    np PDWORD ?  ; pointer to beginning address of number of elements in row
    n DWORD 0    ; number of rows
List2DR ENDS

PushI PROTO,
    list : PTR ListI, ; pointer of 1D ListI
    element : DWORD   ; new element

PushR PROTO,
    list : PTR ListR, ; pointer of 1D ListR
    element : REAL8   ; new element

Push2DI PROTO,
    list2d: PTR List2DI,  ; pointer of 2D List2DI
    element1d : PTR ListI ; the new 1D ListI element

GetListIElement PROTO,
    list2d: PTR List2DI, ; pointer of 2D list
    rowIndex : DWORD,
    element : PTR ListI

Push2DR PROTO,
    list2d: PTR List2DR,  ; pointer of 2D List2DI
    element1d : PTR ListR ; the new 1D ListI element

GetListRElement PROTO,
    list2d: PTR List2DR, ; pointer of 2D list
    rowIndex : DWORD,
    element : PTR ListR

CopyListI PROTO,
    srclist : PTR ListI,
    destList : PTR ListI

CopyListR PROTO,
    srcList : PTR ListR,
    destList : PTR ListR

BubbleSortListI PROTO,
    list : PTR ListI

CompareListI PROTO,
    list1 : PTR ListI,
    list2 : PTR ListI

ContainsListI PROTO,
    list2d : PTR List2DI,
    list : PTR ListI

BubbleSortListR PROTO,
    list : PTR ListR

CompareListR PROTO,
    list1 : PTR ListR,
    list2 : PTR ListR

ContainsListR PROTO,
    list2d : PTR List2DR,
    list : PTR ListR

;------------------------
; Macro
;------------------------

; return number of rows in eax
GetSizeList2DI MACRO list2d:REQ, retv:REQ
    push ebx
    push esi

    mov esi, list2d
    mov ebx, (List2DI PTR [esi]).n
    mov retv, ebx

    pop esi
    pop ebx
endm

; return number of rows in eax
GetSizeList2DR MACRO list2d:REQ, retv:REQ         
    push ebx
    push esi

    mov esi, list2d
    mov ebx, (List2DR PTR [esi]).n
    mov retv, ebx

    pop esi
    pop ebx
endm

; listiObj : ListI, idx : DWORD, retv : DWORD
; uses esi, ebx
GetListIVal MACRO listiObj:REQ, idx:REQ, retv:REQ     
    push ebx
    push esi
    
    mov esi, offset listiObj
    mov esi, (ListI PTR [esi]).p    
    mov ebx, idx
    imul ebx, TYPE DWORD
    add esi, ebx
    mov ebx, (DWORD PTR[esi])
    mov retv, ebx

    pop esi
    pop ebx
endm

; listrObj : ListR, idx : DWORD, retv : REAL8
; uses esi, ebx
GetListRVal MACRO listrObj:REQ, idx:REQ, retv:REQ     
    push ebx
    push esi
    
    mov esi, offset listrObj
    mov esi, (ListR PTR [esi]).p    
    mov ebx, idx
    imul ebx, TYPE REAL8
    add esi, ebx
    fld (REAL8 PTR[esi])
    fstp retv

    pop esi
    pop ebx
endm

这是List.asm

include List.inc

;------------------------------
; private function prototpyes
;------------------------------

; C function prototypes from C Runtime Library
; "C:\Program Files\Microsoft Visual Studio 10.0\VC\lib\msvcrt.lib"
; From MSDN defination

calloc PROTO, ; return allocated memory pointer address in eax
    num: DWORD,
    elementSize: DWORD

realloc PROTO, ; return reallocated memory pointer address in eax
    memBlock : PTR,
    newSize : DWORD

memmove PROTO,
    dest : PTR,
    src : PTR,
    count : DWORD

free PROTO,
    ptrBlock : PTR

; local functions
SizeOfDPI PROTO,
    list2d: PTR List2DI ; pointer of 2D list

CopyFirstI PROTO,
    list2d: PTR List2DI, ; pointer of 2D list
    elAddr : DWORD,
    elBytes : DWORD,
    elNumber : DWORD

CopyNPI PROTO,
    list2d: PTR List2DI, ; pointer of 2D list
    elNumber : DWORD

CopyDPI PROTO,
    list2d: PTR List2DI, ; pointer of 2D list
    elAddr : DWORD,
    elBytes : DWORD

SizeOfDPR PROTO,
    list2d: PTR List2DR ; pointer of 2D list

CopyFirstR PROTO,
    list2d: PTR List2DR, ; pointer of 2D list
    elAddr : DWORD,
    elBytes : DWORD,
    elNumber : DWORD

CopyNPR PROTO,
    list2d: PTR List2DR, ; pointer of 2D list
    elNumber : DWORD

CopyDPR PROTO,
    list2d: PTR List2DR, ; pointer of 2D list
    elAddr : DWORD,
    elBytes : DWORD

.code

; push an integer into a list,
; return ListI member p new memory address in eax, list value (address) is unchanged
PushI proc uses ebx esi ecx,
list : PTR ListI, ; check the value in this address to locate the address of ListI struct object list
element : DWORD
    
    local endPos : DWORD    
    local count : DWORD
    
    ; set esi to list
    ; [esi] is the value in esi address, which is the beginning address of ListI object list,
    ; it is also the address of the ListI object first member p
    ; (ListI PTR [esi]) is the reference to the struct object list    
    ; (ListI PTR [esi]).p is the pointer to beginning address of list.p
    ; (ListI PTR [esi]).n is the value of list.n

    mov esi, list

    ; check if list.p is null via esi
    cmp (ListI PTR [esi]).p, 0

    JNE NOT_NULL ; skip list.p initialization if list.p is not null

    ; initialize list.p and list.n if list.p is null
    invoke calloc, 0, TYPE DWORD ; return allocated memory pointer in eax
    mov (ListI PTR [esi]).p, eax ; initialize list.p
    mov (ListI PTR [esi]).n, 0 ; initialize list.n

NOT_NULL:

    ; get new size in bytes    
    mov esi, list ; address value of the ListI object    
    mov ebx, (ListI PTR [esi]).n ;
    
    mov count, ebx ; original count of elements
    imul ebx, TYPE DWORD ; convert to bytes count
    mov endPos, ebx ; save original last element position
    add ebx, TYPE DWORD ; get new bytes length for realloc
    
    ; realloc to new bytes size for ListI member p, its new pointer address is in eax    
    invoke realloc, (ListI PTR [esi]).p, ebx        
    
    ; set to new memory location
    mov (ListI PTR [esi]).p, eax

    ; append new element
    mov esi, eax ; set ListI member p new pointer offset in esi    
    add esi, endPos ; move esi to the original list last element position
    mov ebx, element ; set new element in ebx
    mov [esi], ebx ; append the new element to the original list last element position    
    
    ; set new count
    mov esi, list ; get original list address from its pointer
    mov ebx, count ; original count of elements
    inc ebx ; get new count of elements
    mov (ListI PTR [esi]).n, ebx ; set new count of list to ListI member n
                
    ret
PushI endp

; push a real8 into a list, return ListR member p new memory address in eax, list value (address) is unchanged
PushR proc uses ebx esi ecx,
list : PTR ListR, ; check the value in this address to locate the address of ListR struct object list
element : REAL8
    
    local endPos : DWORD    
    local count : DWORD

    ; set esi to list
    ; [esi] is the value in esi address, which is the beginning address of ListR object list,
    ; it is also the address of the ListR object first member p
    ; (ListR PTR [esi]) is the reference to the struct object list
    ; (ListR PTR [esi]).p is the pointer to beginning address of list.p
    ; (ListR PTR [esi]).n is the value of list.n

    mov esi, list

    ; check if list.p is null via esi
    cmp (ListR PTR [esi]).p, 0

    JNE NOT_NULL ; skip list.p initialization if list.p is not null

    ; initialize list.p and list.n if list.p is null
    invoke calloc, 0, TYPE REAL8 ; return allocated memory pointer in eax
    mov (ListR PTR [esi]).p, eax ; initialize list.p
    mov (ListR PTR [esi]).n, 0 ; initialize list.n

NOT_NULL:

    ; get new size in bytes
    mov esi, list ; address value of the ListR object
    mov ebx, (ListR PTR [esi]).n ; [esi] is the value in esi address, which is the beginning address of ListR member p
    mov count, ebx ; original count of elements
    imul ebx, TYPE REAL8 ; convert to bytes count
    mov endPos, ebx ; save original last element position
    add ebx, TYPE REAL8 ; get new bytes length for realloc
    
    ; realloc to new bytes size for ListR member p, its new pointer address is in eax    
    invoke realloc, (ListR PTR [esi]).p, ebx        
    
    ; set to new memory location
    mov (ListR PTR [esi]).p, eax

    ; append new element
    mov esi, eax ; set ListD member p new pointer offset in esi    
    add esi, endPos ; move esi to the original list last element position
    fld element ; save new element at ST(0)    
    fstp REAL8 PTR[esi] ; append the new element to the original list last element position    
    
    ; set new count
    mov esi, list ; get original list address from its pointer
    mov ebx, count ; original count of elements
    inc ebx ; get new count of elements
    mov (ListR PTR [esi]).n, ebx ; set new count of list to ListR member n
                
    ret
PushR endp

; push a 1D list to a 2D list
Push2di proc uses ebx esi ecx,
list2d: PTR List2DI, ; pointer of 2D list
element : PTR ListI ; the new 1D list element

    local list2dNumber : DWORD
    local elNumber, elBytes, elAddr, elEndAddr : DWORD
    local dpBytes, dpAddr, dpEndAddr : DWORD
    local npBytes, npAddr, npEndAddr : DWORD
    
    ; get new element info

    ; get elBytes
    mov esi, element
    mov ebx, (ListI PTR [esi]).n
    mov elNumber, ebx
    imul ebx, TYPE DWORD
    mov elBytes, ebx
    
    ; get elAddr    
    mov ebx, [esi]
    mov elAddr, ebx

    ; get elEndAddr
    mov ebx, elAddr
    add ebx, elBytes
    mov elEndAddr, ebx

    ; check if list2d.p is null
    mov esi, list2d
    cmp (List2DI PTR [esi]).dp, 0

    JNE NOT_NULL ; skip list2d.p initialization if list2d.p is not null
    
    invoke CopyFirstI, list2d, elAddr, elBytes, elNumber
        
    JMP BLANK

NOT_NULL:

    invoke CopyDPI,list2d, elAddr, elBytes

    invoke CopyNPI, list2d, elNumber

    ; set list2d.n
    mov esi, list2d
    inc (List2DI PTR [esi]).n

BLANK:

    ret
Push2di endp

CopyFirstI proc uses eax ebx esi,
list2d: PTR List2DI, ; pointer of 2D list
elAddr : DWORD,
elBytes : DWORD,
elNumber : DWORD

    ; initialize list2d.dp
    invoke calloc, elNumber, TYPE DWORD ; return allocated memory pointer in eax
    mov esi, list2d
    mov (List2DI PTR [esi]).dp, eax ; initialize list2d.p
            
    ; initialize list2.np    
    invoke calloc, 1, TYPE DWORD ; return allocated memory pointer in eax
    mov esi, list2d
    mov (List2DI PTR [esi]).np, eax ; initialize list2d.p

    ; initialize list2d.n
    mov esi, list2d
    mov (List2DI PTR [esi]).n, 1 ; initialize list2d.n

    ; set list2d.np
    mov esi, list2d
    mov esi, (List2DI PTR [esi]).np ; get np address
    mov ebx, elNumber
    mov [esi], ebx

    ; copy element to list2d.dp
    mov esi, list2d
    mov esi, (List2DI PTR [esi]).dp ; get dp address
    invoke memmove, esi, elAddr, elBytes

    ret
CopyFirstI endp

CopyDPI proc uses eax ebx esi,
list2d: PTR List2DI, ; pointer of 2D list
elAddr : DWORD,
elBytes : DWORD

    local dpBytes, dpAddr, dpEndAddr, newLen : DWORD

    ; get dpBytes
    invoke SizeOfDPI, list2d    
    mov dpBytes, eax

    ; get dpAddr to realloc
    mov esi, list2d
    mov esi, (List2DI PTR [esi]).dp ; get np address
    mov dpAddr, esi        

    ; extend dp with more elBytes bytes
    mov ebx, dpBytes
    add ebx, elBytes
    mov newLen, ebx

    invoke realloc, dpAddr, newLen

    ; get new dp address which could be different with original npAddr
    mov esi, list2d
    mov (List2DI PTR [esi]).dp, eax    
    mov dpAddr, eax
    
    ; get new dp end address
    mov ebx, dpAddr
    add ebx, dpBytes
    mov dpEndAddr, ebx

    ; copy element to list2d.dp    
    invoke memmove, dpEndAddr, elAddr, elBytes

    ret
CopyDPI endp

CopyNPI proc uses eax ebx esi,
list2d: PTR List2DI, ; pointer of 2D list
elNumber : DWORD

    local list2dNumber, npBytes, npAddr, npEndAddr, newLen : DWORD

    ; get list2dNumber
    mov esi, list2d
    mov ebx, (List2DI PTR [esi]).n
    mov list2dNumber, ebx

    ; get npBytes
    mov ebx, list2dNumber
    imul ebx, TYPE DWORD
    mov npBytes, ebx
        
    ; get npAddr to realloc
    mov esi, list2d
    mov esi, (List2DI PTR [esi]).np ; get np address
    mov npAddr, esi    
    
    ; extend np with one new DWORD item
    mov ebx, npBytes
    add ebx, TYPE DWORD
    mov newLen, ebx

    invoke realloc, npAddr, newLen

    ; get new np address which could be different with original npAddr
    mov esi, list2d
    mov (List2DI PTR [esi]).np, eax
    mov npAddr, eax

    ; get new np end address
    mov ebx, npAddr
    add ebx, npBytes
    mov npEndAddr, ebx

    ; set list2d.np
    mov esi, list2d
    mov esi, npEndAddr
    mov ebx, elNumber
    mov [esi], ebx

    ret

CopyNPI endp

; get sum of list2d.dp in eax
SizeOfDPI proc uses esi ecx ebx,
list2d: PTR List2DI ; pointer of 2D list
    
    local dpBytes : DWORD
            
    mov dpBytes, 0 ; initial countBytes

    mov esi, list2d        
    mov ecx, (List2DI PTR [esi]).n ; loop count : rows in list2d
    
    JCXZ BLANK ; if ecx == 0 then skip

    mov esi, list2d        
    mov esi, (List2DI PTR [esi]).np ; get np address

L1:    
        
        mov ebx, [esi] ; get current DWORD elements number in this row
                    
        imul ebx, TYPE DWORD ; convert to bytes

        add dpBytes, ebx ; save total bytes
        
        add esi, TYPE DWORD ; move to next row

    LOOP L1

BLANK:
    
    mov eax, dpBytes

    ret

SizeOfDPI endp

; get element at row index
GetListIElement proc uses esi ecx ebx,
list2d: PTR List2DI, ; pointer of 2D list
rowIndex : DWORD,
element : PTR ListI
    
    local dpBytes, count, lastdpBytes : DWORD

    mov dpBytes, 0
    mov count, 0

    mov esi, element

    ; check if element.p is null via esi
    cmp (ListI PTR [esi]).p, 0

    JNE NOT_NULL ; skip list.p initialization if list.p is not null

    ; initialize list.p and list.n if list.p is null
    invoke calloc, 0, TYPE DWORD ; return allocated memory pointer in eax
    mov (ListI PTR [esi]).p, eax ; initialize list.p
    mov (ListI PTR [esi]).n, 0 ; initialize list.n
            
    NOT_NULL:
    
    mov esi, list2d        
    mov ebx, (List2DI PTR [esi]).n ; loop count : rows in list2d
    cmp ebx, rowIndex
    JLE INDEX_EXCEED
    
    mov ecx, rowIndex     ;rowIndex start from 0, this ensure loop count is 1 if rowIndex == 0
    inc ecx

    mov esi, list2d        
    mov esi, (List2DI PTR [esi]).np ; get np address

L1:    
        
        mov ebx, [esi] ; get current DWORD elements number in this row
        
        mov count, ebx

        imul ebx, TYPE DWORD ; convert to bytes

        mov lastdpBytes, ebx

        add dpBytes, ebx ; save total bytes
        
        add esi, TYPE DWORD ; move to next row

    LOOP L1

FIRST_ITEM:

    ; get dp address for rowIndex
    mov esi, list2d        
    mov ebx, (List2DI PTR [esi]).dp ; get np address
    add ebx, dpBytes
    sub ebx, lastdpBytes

    ; return element.p
    mov esi, element    
    mov (ListI PTR [esi]).p, ebx

    ; return element.n
    mov ebx, count
    mov (ListI PTR [esi]).n, ebx

INDEX_EXCEED:        

    ret

GetListIElement endp

; push a 1D list to a 2D list
Push2dR proc uses ebx esi ecx,
list2d: PTR List2DR, ; pointer of 2D list
element : PTR ListR ; the new 1D list element

    local list2dNumber : DWORD
    local elNumber, elBytes, elAddr, elEndAddr : DWORD
    local dpBytes, dpAddr, dpEndAddr : DWORD
    local npBytes, npAddr, npEndAddr : DWORD
    
    ; get new element info

    ; get elBytes
    mov esi, element
    mov ebx, (ListR PTR [esi]).n
    mov elNumber, ebx
    imul ebx, TYPE REAL8
    mov elBytes, ebx
    
    ; get elAddr    
    mov ebx, [esi]
    mov elAddr, ebx

    ; get elEndAddr
    mov ebx, elAddr
    add ebx, elBytes
    mov elEndAddr, ebx

    ; check if list2d.dp is null
    mov esi, list2d
    cmp (List2DR PTR [esi]).dp, 0

    JNE NOT_NULL ; skip list2d.p initialization if list2d.p is not null
    
    invoke CopyFirstR, list2d, elAddr, elBytes, elNumber
        
    JMP BLANK

NOT_NULL:

    invoke CopyDPR,list2d, elAddr, elBytes

    invoke CopyNPR, list2d, elNumber

    ; set list2d.n
    mov esi, list2d
    inc (List2DR PTR [esi]).n

BLANK:

    ret
Push2dR endp

CopyFirstR proc uses eax ebx esi,
list2d: PTR List2DR, ; pointer of 2D list
elAddr : DWORD,
elBytes : DWORD,
elNumber : DWORD

    ; initialize list2d.dp
    invoke calloc, elNumber, TYPE REAL8 ; return allocated memory pointer in eax
    mov esi, list2d
    mov (List2DR PTR [esi]).dp, eax ; initialize list2d.p
            
    ; initialize list2.np    
    invoke calloc, 1, TYPE DWORD ; return allocated memory pointer in eax
    mov esi, list2d
    mov (List2DR PTR [esi]).np, eax ; initialize list2d.p

    ; initialize list2d.n
    mov esi, list2d
    mov (List2DR PTR [esi]).n, 1 ; initialize list2d.n

    ; set list2d.np
    mov esi, list2d
    mov esi, (List2DR PTR [esi]).np ; get np address
    mov ebx, elNumber
    mov [esi], ebx

    ; copy element to list2d.dp
    mov esi, list2d
    mov esi, (List2DR PTR [esi]).dp ; get dp address
    invoke memmove, esi, elAddr, elBytes

    ret
CopyFirstR endp

CopyDPR proc uses eax ebx esi,
list2d: PTR List2DR, ; pointer of 2D list
elAddr : DWORD,
elBytes : DWORD

    local dpBytes, dpAddr, dpEndAddr, newLen : DWORD

    ; get dpBytes
    invoke SizeOfDPR, list2d    
    mov dpBytes, eax

    ; get dpAddr to realloc
    mov esi, list2d
    mov esi, (List2DR PTR [esi]).dp ; get np address
    mov dpAddr, esi        

    ; extend dp with more elBytes bytes
    mov ebx, dpBytes
    add ebx, elBytes
    mov newLen, ebx

    invoke realloc, dpAddr, newLen

    ; get new dp address which could be different with original npAddr
    mov esi, list2d
    mov (List2DR PTR [esi]).dp, eax    
    mov dpAddr, eax
    
    ; get new dp end address
    mov ebx, dpAddr
    add ebx, dpBytes
    mov dpEndAddr, ebx

    ; copy element to list2d.dp    
    invoke memmove, dpEndAddr, elAddr, elBytes

    ret
CopyDPR endp

CopyNPR proc uses eax ebx esi,
list2d: PTR List2DR, ; pointer of 2D list
elNumber : DWORD

    local list2dNumber, npBytes, npAddr, npEndAddr, newLen : DWORD

    ; get list2dNumber
    mov esi, list2d
    mov ebx, (List2DR PTR [esi]).n
    mov list2dNumber, ebx

    ; get npBytes
    mov ebx, list2dNumber
    imul ebx, TYPE DWORD
    mov npBytes, ebx
        
    ; get npAddr to realloc
    mov esi, list2d
    mov esi, (List2DR PTR [esi]).np ; get np address
    mov npAddr, esi    
    
    ; extend np with one new DWORD item
    mov ebx, npBytes
    add ebx, TYPE DWORD
    mov newLen, ebx

    invoke realloc, npAddr, newLen

    ; get new np address which could be different with original npAddr
    mov esi, list2d
    mov (List2DR PTR [esi]).np, eax
    mov npAddr, eax

    ; get new np end address
    mov ebx, npAddr
    add ebx, npBytes
    mov npEndAddr, ebx

    ; set list2d.np
    mov esi, list2d
    mov esi, npEndAddr
    mov ebx, elNumber
    mov [esi], ebx

    ret

CopyNPR endp

; get sum of list2d.dp in eax
SizeOfDPR proc uses esi ecx ebx,
list2d: PTR List2DR ; pointer of 2D list
    
    local dpBytes : DWORD
            
    mov dpBytes, 0 ; initial countBytes

    mov esi, list2d        
    mov ecx, (List2DI PTR [esi]).n ; loop count : rows in list2d
    
    JCXZ BLANK ; if ecx == 0 then skip

    mov esi, list2d        
    mov esi, (List2DR PTR [esi]).np ; get np address

L1:    
        
        mov ebx, [esi] ; get current DWORD elements number in this row
                    
        imul ebx, TYPE REAL8 ; convert to bytes offset in dp

        add dpBytes, ebx ; save total bytes
        
        add esi, TYPE DWORD ; move to next row

    LOOP L1

BLANK:
    
    mov eax, dpBytes

    ret

SizeOfDPR endp

; get element at row index
GetListRElement proc uses esi ecx ebx,
list2d: PTR List2DR, ; pointer of 2D list
rowIndex : DWORD,
element : PTR ListR
    
    local dpBytes, count, lastdpBytes : DWORD

    mov dpBytes, 0
    mov count, 0

    mov esi, element

    ; check if element.p is null via esi
    cmp (ListR PTR [esi]).p, 0

    JNE NOT_NULL ; skip list.p initialization if list.p is not null

    ; initialize list.p and list.n if list.p is null
    invoke calloc, 0, TYPE REAL8 ; return allocated memory pointer in eax
    mov (ListR PTR [esi]).p, eax ; initialize list.p
    mov (ListR PTR [esi]).n, 0 ; initialize list.n
            
    NOT_NULL:
    
    mov esi, list2d        
    mov ebx, (List2DR PTR [esi]).n ; loop count : rows in list2d
    cmp ebx, rowIndex
    JLE INDEX_EXCEED
    
    mov ecx, rowIndex     ;rowIndex start from 0, this ensure loop count is 1 if rowIndex == 0
    inc ecx

    mov esi, list2d        
    mov esi, (List2DR PTR [esi]).np ; get np address

L1:    
        
        mov ebx, [esi] ; get current DWORD elements number in this row
        
        mov count, ebx

        imul ebx, TYPE REAL8 ; convert to bytes offset in dp

        mov lastdpBytes, ebx

        add dpBytes, ebx ; save total bytes
        
        add esi, TYPE DWORD ; move to next row

    LOOP L1

FIRST_ITEM:

    ; get dp address for rowIndex
    mov esi, list2d        
    mov ebx, (List2DR PTR [esi]).dp ; get np address
    add ebx, dpBytes
    sub ebx, lastdpBytes

    ; return element.p
    mov esi, element    
    mov (ListR PTR [esi]).p, ebx

    ; return element.n
    mov ebx, count
    mov (ListR PTR [esi]).n, ebx

INDEX_EXCEED:        

    ret

GetListRElement endp

; copy list to newList
CopyListI proc uses esi eax ebx,
list : PTR ListI,
newList : PTR ListI ; newList need to initialize with default n value, i.e. declare as global varaible "newList ListI <?>"

    local count, srcAddr, oriAddr, oriCount, copyBytes : DWORD

    ; get copy source address
    mov esi, list
    mov ebx, (ListI PTR [esi]).n
    mov count, ebx
    mov ebx, (ListI PTR [esi]).p
    mov srcAddr, ebx

    ; get free original address
    mov esi, newList
    mov ebx, (ListI PTR [esi]).p
    mov oriAddr, ebx

    ; this n value could be a wild value if newList is declared a local variable or
    ; or declared a global variable as "newList ListI <>" rather than "newList ListI <?>"
    ; declare newList as a global with "newList ListI <?>" is correct way,
    ; which always uses n default value 0 in ListI struct defination to initialize ListI.n
    ; while "newList ListI <>" declaration does not
    mov ebx, (ListI PTR [esi]).n
    mov oriCount, ebx

    ; initialize or reset list.p and list.n
    invoke calloc, count, TYPE DWORD ; return allocated memory pointer in eax
    mov esi, newList
    mov (ListI PTR [esi]).p, eax ; initialize list.p
    mov ebx, count
    mov (ListI PTR [esi]).n, ebx ; initialize list.n
    
    ; free original newList.p here to avoid memory leak
    mov ebx, oriCount
    cmp ebx, 0
    JE SKIP_FREE ; if oriCount == 0
    invoke free, oriAddr ; if oriCount != 0, free could fail if the oriCount is a wild n value above

SKIP_FREE:

    ; get bytes count to copy
    mov ebx, count
    imul ebx, TYPE DWORD
    mov copyBytes, ebx

    ; copy
    mov esi, newList
    invoke memmove, (ListI PTR [esi]).p, srcAddr, copyBytes

    ret

CopyListI endp

; clone array
CopyListR proc,
    srcList : PTR ListR,
    destList : PTR ListR
    
    local i, count, srcAddr, destAddr, oriAddr, oriCount, newAddr : DWORD     
    local tempR : REAL8

    pushad ; save all registers

    mov esi, srcList
    mov ebx, (ListR PTR[esi]).n
    mov count, ebx
    mov ebx, (ListR PTR[esi]).p
    mov srcAddr, ebx

    ; initialize or reset destList.p and destList.n
    invoke calloc, count, TYPE REAL8 ; return allocated memory pointer in eax    
    cmp eax, 0

    ; calloc return null, calloc fails.
    ; this is usually not caused by running out of continous heap memory block
    ; this is usually caused by a bug somewhere in the functions calling chains
    ; trace back the functions to see if somewhere having a unexpected global variable overwritting
    JE CALLOC_ERROR_ZERO

    mov destAddr, eax
        
    ; get original address to be freed
    mov esi, destList
    mov ebx, (ListR PTR [esi]).n
    mov oriCount, ebx
    mov ebx, (ListR PTR [esi]).p
    mov oriAddr, ebx ; this oriAddr maybe 0 if destList is first time referenced after its declaration
         
    mov esi, destList
    mov ebx, destAddr
    mov (ListR PTR [esi]).p, ebx ; initialize list.p with new allocated memory address    
    mov ebx, count
    mov (ListR PTR [esi]).n, ebx ; initialize list.n same as srcList
    
    ; free original destList.p here to avoid memory leak
    mov ebx, oriCount
    cmp ebx, 0
    
    ; if oriCount == 0 then skip free.
    ; this is usually when destList is first time referenced and its p member is still blank without memory allocated
    ; in this situation, oriAddr == 0x00000000, which cause free crash
    JE SKIP_FREE

    ; free invokation may crash if oriAddr is 0 (null) or an in-use address, i.e. stack memory address
    invoke free, oriAddr ; if oriCount != 0

SKIP_FREE:

    mov esi, destList
    mov esi, (ListR PTR[esi]).p    
    mov ebx, esi ; save destination array offset address in ebx

    mov i, 0
    mov ecx, count
    
L1:
        mov edi, i ; edi as index

        ; copy
        mov esi, srcAddr                        
        fld REAL8 PTR[esi + edi * 8]        
        mov esi, destAddr
        fstp REAL8 PTR[esi + edi * 8]

        inc i ; i++

    LOOP L1

    popad ; restore all registers
    JMP DONE

CALLOC_ERROR_ZERO:

        ; if return eax == -2, then calloc fails
        mov eax, -2

DONE:

    ret
CopyListR endp

end

这是ListUtility.asm

include List.inc

TRUE EQU 1
FALSE EQU 0

.data

tempListI ListI <?>
listCopyI ListI <?>
tempListR ListR <?>
listCopyR ListR <?>

MinRealCompareError REAL8 1.0E-12

.code

BubbleSortListI proc uses eax ecx esi ebx,
list : PTR ListI

    local pData : DWORD ; offset address of 1d pDataay        
    local count : DWORD ; number of elements     
    
    mov esi, list
    mov ebx, (ListI PTR [esi]).p
    mov pData, ebx
    mov ebx, (ListI PTR [esi]).n
    mov count, ebx

    ; the following code in this procedure is from 
    ; the book "assembly language for x86 processors" by Kip Irvine
    mov ecx,count
    dec ecx
L1:
    push ecx
    mov esi,pData
L2:
    mov eax,[esi]
    cmp [esi+4],eax
    jg L3
    xchg eax,[esi+4]
    mov [esi],eax
L3:
    add esi,4
    loop L2
    pop ecx
    loop L1
L4:
    
    ret

BubbleSortListI endp

CompareListI proc uses ecx ebx esi,
list1 : PTR ListI,
list2 : PTR ListI

    local array1 : DWORD ; offset array1
    local array2 : DWORD ; offset array2
    local array1Len : DWORD ; length of array    
    local array2Len : DWORD ; length of array      

    local i : DWORD
    local retEqual : DWORD
    
    mov esi, list1
    mov ebx, (ListI PTR [esi]).p
    mov array1, ebx
    mov ebx, (ListI PTR [esi]).n
    mov array1Len, ebx

    mov esi, list2
    mov ebx, (ListI PTR [esi]).p
    mov array2, ebx
    mov ebx, (ListI PTR [esi]).n
    mov array2Len, ebx

    cmp ebx, array1Len

    JNE DONE_NOT_EQUAL ; if 2 array length is different

    ; set initial return value TRUE
    mov retEqual, TRUE
    
    mov i, 0
    mov ecx, array1Len

L1:        
        mov esi, array1
        mov ebx, [esi]
        mov esi, array2
        cmp [esi], ebx

        JNE DONE_NOT_EQUAL
        
        add array1, 4
        add array2, 4
        
    LOOP L1

    JMP DONE_EQUAL

DONE_NOT_EQUAL:

        mov retEqual, FALSE

DONE_EQUAL:
        
        ; return retEqual
        mov eax, retEqual        

        ret

CompareListI endp

; check if list2d contains list with ignoring list elements order
ContainsListI proc uses ebx esi,
list2d : PTR List2DI,
list : PTR ListI
        
    local isContains, rows, i : DWORD    
        
    ; initialize return value
    mov isContains, FALSE

    ; copy list to avoid modify original list with sorting
    invoke CopyListI, list, ADDR listCopyI

    ; sort list
    invoke BubbleSortListI, ADDR listCopyI

    ; get rows
    mov esi, list2d
    mov ebx, (List2DI PTR [esi]).n
    mov rows, ebx
        
    mov i, 0
    mov ecx,rows     
L1:
        invoke GetListIElement, list2d, i, ADDR tempListI

        invoke BubbleSortListI, ADDR tempListI

        invoke CompareListI, ADDR tempListI, ADDR listCopyI

        cmp eax, TRUE

        JE DONE_TRUE        

        inc i
                
    loop L1

    JMP DONE_FALSE

DONE_TRUE:    

        mov isContains, TRUE    

DONE_FALSE:

        ; return value in eax
        mov eax, isContains

        ret
ContainsListI endp

; check if a 2d real array contains a 1d real array
ContainsListR proc,
list2d : PTR List2DR,
list : PTR ListR

    local isContains, rows, i : DWORD    
        
    ; initialize return value
    mov isContains, FALSE

    ; copy list to avoid modify original list with sorting
    invoke CopyListR, list, ADDR listCopyR

    ; sort list
    invoke BubbleSortListR, ADDR listCopyR

    ; get rows
    mov esi, list2d
    mov ebx, (List2DR PTR [esi]).n
    mov rows, ebx
        
    mov i, 0
    mov ecx,rows     
L1:
        invoke GetListRElement, list2d, i, ADDR tempListR

        invoke BubbleSortListR, ADDR tempListR

        invoke CompareListR, ADDR tempListR, ADDR listCopyR

        cmp eax, TRUE

        JE DONE_TRUE        

        inc i
                
    loop L1

    JMP DONE_FALSE

DONE_TRUE:

        mov isContains, TRUE    

DONE_FALSE:
        
        ; return value in eax
        mov eax, isContains

        ret
ContainsListR endp

; check if two 1d array equal
; return true or false in eax
CompareListR proc,
list1 : PTR ListR,
list2 : PTR ListR

    local array1 : DWORD ; offset array1
    local array2 : DWORD ; offset array2
    local array1Len : DWORD ; length of array    
    local array2Len : DWORD ; length of array  
    local dummy1, dummy2 : REAL8

    local i : DWORD
    local retEqual : DWORD
    
    ; set initial return value TRUE
    mov retEqual, TRUE

    mov esi, list1
    mov ebx, (ListR PTR [esi]).p
    mov array1, ebx
    mov ebx, (ListR PTR [esi]).n
    mov array1Len, ebx

    mov esi, list2
    mov ebx, (ListR PTR [esi]).p
    mov array2, ebx
    mov ebx, (ListR PTR [esi]).n
    mov array2Len, ebx

    cmp ebx, array1Len

    JNE DONE_NOT_EQUAL ; if 2 array length is different
            
    mov i, 0
    mov ecx, array1Len

L1:        
        ; push minimum error at ST(1)
        fld MinRealCompareError

        ; push array1[i] at ST(0)
        mov esi, array1
        fld REAL8 PTR[esi]

        ; operate array1[i] - array2[i] at ST(0)
        mov esi, array2
        fsub REAL8 PTR[esi]

        ; operate fabs(array1[i] - array2[i]) at ST(0)
        fabs

        ; compare
        fcomi ST(0), ST(1)

        ; return false if difference abs value is above error
        JA DONE_NOT_EQUAL

        ; continue if difference abs value is not above error, the 2 value are equal    
        fstp dummy2
        fstp dummy1        
        
        add array1, 8
        add array2, 8
        
    LOOP L1

    JMP DONE_EQUAL

DONE_NOT_EQUAL:

        mov retEqual, FALSE

DONE_EQUAL:

        ; return retEqual
        mov eax, retEqual        

        ret
        
CompareListR endp

; Bubble Sort 1d REAL8 array
;    for(i = 0; i< count ; i++)
;        for (j= i + 1; j< count , j++)                
;            if(array[i] > array[j])
;           {
;                temp = array[j];
;                array[j] = array[i];
;                array[i] = temp;        
;           }
BubbleSortListR proc,
list : PTR ListR
        
    local i, j : DWORD ; number of elements         
    local dummy1, dummy2 : REAL8    

    pushad ; save all general registers

    ; initialize esi with arr array address    
    mov esi, list
    mov edx, (ListR PTR[esi]).n
    mov esi, (ListR PTR[esi]).p
    
    mov i, 0 ; i = 0
        
L1:
        cmp i, edx
        JAE DONE ; i < count
            
        ; j = i + 1        
        mov ebx, i
        add ebx, 1
        mov j, ebx ; j = i + 1
                        
L2:            
            cmp j, edx
            JAE CONT_L1 ; j < count

            ; push array[i] at ST(1)
            mov edi, i
            fld REAL8 PTR[esi + edi*8] ; get current real8 value, st(1)
            
            ; push array[j] at ST(0)
            mov edi, j
            fld REAL8 PTR[esi + edi* 8] ; get next real8 value, st(0)
            
            fcomi ST(0),ST(1) ; compare array[j] with array[i]
                        
            ; if array[i] <= array[j] then continue
            JNB CONT_POP_L2
            
            ; if array[i] > array[j] then swap array[i] and array[j]
            
            ; pop ST(0) to array[i]
            mov edi, i
            fstp REAL8 PTR[esi + edi * 8]

            ; pop ST(1) to array[j]
            mov edi, j
            fstp REAL8 PTR[esi + edi * 8]

            JMP CONT_L2 ; continue without 2 fstp pop

            CONT_POP_L2: ; continue with 2 fstp pop for cleaning the 2 pushed array[j] and array[i]
                fstp dummy2
                fstp dummy1

CONT_L2:

        inc j ; j++    
        JMP L2        

CONT_L1:

        inc i ; i++        
        JMP L1
        
DONE:
        
        popad ; restore all general registers

        ret

BubbleSortListR endp

end

测试

这是一个用于List库和ListUtility中主函数的测试程序。完整的测试程序包含在附加的包中。

如何将 32 位DWORD添加到ListI

tempI DWORD 7
tempListI ListI <?>

invoke pushi, ADDR tempListI , tempI

获取tempListI中元素n的数量

mov esi, offset tempListI
mov ebx, (ListI PTR [esi]).n
mov n, ebx

使用此宏获取tempListI中索引为idx的元素

GetListIVal tempListI, idx, tempI

如何将 64 位REAL8添加到ListR

tempR REAL8 7.7
tempListR ListR <?>

invoke pushr, ADDR tempListR , tempR

获取tempListR中元素n的数量

mov esi, offset tempListR
mov ebx, (ListR PTR [esi]).n
mov n, ebx

使用宏GetListRVal获取tempListI中索引为idx的元素

GetListRVal tempListR, idx, tempR

如何将ListI添加到List2DI

tempListI ListI <?>
tempList2DI List2DI <?>

invoke push2di, ADDR tempList2DI , ADDR tempListI

使用宏GetSizeList2DI获取tempList2DI中行数n

local tempAddr : DWORD
mov tempAddr , offset tempList2DI 
GetSizeList2DI tempAddr , n

获取tempList2DI中索引为idx的元素

invoke GetListIElement, ADDR tempList2DI , idx, ADDR tempListI

如何将ListI添加到List2DR

tempListR ListR <?>
tempList2DR List2DR <?>

invoke push2dr, ADDR tempList2DR , ADDR tempListR

使用宏GetSizeList2DI获取tempList2DI中行数n

local tempAddr : DWORD
mov tempAddr , offset tempList2DR 
GetSizeList2DR tempAddr , n

获取tempList2DI中索引为idx的元素

invoke GetListRElement, ADDR tempList2DR , idx, ADDR tempListR

这是ListIList2DI struct的便捷函数

; sort ListI object 
invoke BubbleSortListI, ADDR tempListI

; compare 2 ListI objects
invoke CompareListI, ADDR tempListI1, ADDR tempListI2

; copy a ListI object to another
invoke CopyListI, ADDR srcListI, ADDR destListI

; check if a List2DI object contains a ListI object with ignoring ListI inner element order
invoke ContainsListI, ADDR tempList2DI, ADDR tempListI

这是ListIList2DR struct的便捷函数

; sort ListR object 
invoke BubbleSortListR, ADDR tempListR

; compare 2 ListR objects
invoke CompareListR, ADDR tempListR1, ADDR tempListR2

; copy a ListR object to another
invoke CopyListR, ADDR srcListR, ADDR destListR

; check if a List2DR object contains a ListR object with ignoring ListR inner element order
invoke ContainsListR, ADDR tempList2DR, ADDR tempListR

Visual Studio ASM 设置

有一种方法可以在 Visual Studio 中开发 MASM 程序,这使得 ASM 调试更加容易。

如何安装和设置AsmHighlighter.vsix的完整描述包含在附加的 zip 包中。

以下是在 Visual Studio VC++ Express 2010 中设置汇编编程的步骤

  1. 参考 http://www.deconflations.com/2011/masm-assembly-in-visual-studio-2010/
  2. http://asmhighlighter.codeplex.com/releases 下载 Visual Studio 汇编扩展AsmHighlighter.vsix
  3. 使用 7-Zip 打开AsmHighlighter.vsix存档,或将其重命名为AsmHighlighter.zip,然后打开它
  4. 编辑extension.vsixmanifest,在<VisualStudio Version="10.0">节点内添加或替换<Edition>Pro</Edition><Edition>Express_All</Edition>
  5. 要查找扩展程序的安装位置,请在 VC 2010 文件夹C:\Program Files\Microsoft Visual Studio 10.0\中搜索“extension”关键字。对于 VC2010 Express,通过查找现有extension.vsixmanifest文件(在上述搜索找到的文件夹中)确定关键字“Express_All”。
  6. 将所有内容复制到C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\VCExpressExtensions\Platform\
  7. 打开 VC++ Express 2010。
  8. 在“常规”类别中创建一个新的“空项目”
  9. 解决方案资源管理器中,选择“生成自定义”=>勾选第二个“MASM ... ”项。
  10. 更改配置属性=>链接器=>系统=>子系统为控制台或其他类型
  11. 添加现有的.asm文件。步骤顺序很重要,请确保在完成步骤 9(生成自定义)之后添加.asm文件,如果您在步骤 9 之前添加了现有的.asm文件,则必须先排除它们,然后在步骤 9 完成后重新添加它们。
  12. 解决方案资源管理器中右键单击项目=>属性,如果出现新的“Microsoft Macro Assembly”项,则表示扩展已成功安装,VS 中的 MASM 开发已准备就绪。

编译

有两种编译版本,一种是在 Visual Studio C++ 中编译,另一种是通过命令行编译。

ASMList项目中的编译命令行

cls

echo off

rem compile library
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\ml.exe" /I .\include ^
                                                    /c .\src\List.asm /Fo List.obj
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\ml.exe" /I .\include ^
                                                    /c .\src\ListUtility.asm /Fo ListUtility.obj

rem build library
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\lib.exe" /subsystem:console ^
                                                    List.obj ListUtility.obj /out:.\lib\List.lib

rem compile test
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\ml.exe" /I .\include ^
                                                    /c .\ListTest.asm /Fo ListTest.obj
                                                    
rem build test                                                    
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\link.exe" ^
            /libpath:.\lib List.lib ^
            /libpath:"C:\Program Files\Microsoft Visual Studio 10.0\VC\lib" msvcrt.lib ^
            ListTest.obj /out:TestList.exe
                                                    
del *.obj

这是一个最小的命令行编译环境设置

echo off

rem run only once this batch before compiling MASM or C program

set path=C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE;%path%

set include=C:\Program Files\Microsoft Visual Studio 10.0\VC\INCLUDE;
C:\Program Files\Microsoft SDKs\Windows\v7.0A\include;%include%

set lib=C:\Program Files\Microsoft Visual Studio 10.0\VC\LIB;
C:\Program Files\Microsoft SDKs\Windows\v7.0A\lib;

set libpath=C:\Program Files\Microsoft Visual Studio 10.0\VC\LIB;%libpath%

完整的 VC++ 开发环境命令行设置是

C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\vcvars32.bat

待办事项

检查所有堆内存操作,通过释放未使用的指针来修复内存泄漏。

内存操作失败时的异常处理。

历史

  • 2016年2月27日
    • 添加了ListUtility.asm,包含用于利用 List 库的便捷函数
    • 修复了一个 bug,避免在 realloc 后释放原始堆内存时发生内存泄漏。可能还有其他地方需要进行相同的修复,这也许会在下次更改中完成。
  • 2016年2月26日
    • 初次发布
© . All rights reserved.