|
NASM Helper
Programming in Assembler can became difficult, when you want to pass parameters to a function and/or using local variables.
I wrote some macros, to have a simple solution for using arguments and local variables easily in the same way. The macros
are written for NASM.
Although NASM has the "%local" directive to declare local variables (see %local Directive)
and some Helper Macros for C Interface, I prefer my solution.
See sample.asm with a lot of examples and at
the bottom of this page you find a complex function as example.
I release my macros in hope that it helps other people. And make it easier to write programs in Assembler.
Download
nasm-helper-1.2.zip (08/01/2022), includes the macros, sample.asm and assembler.syntax.
nasm-helper-1.1.zip (07/01/2018), includes the macros, sample.asm and assembler.syntax.
nasm-helper-1.0.zip (29/12/2017), includes the macros, sample.asm and assembler.syntax.
How it works
Initialize a function:
When you want to use a function that should be called with parameters or use local variables, then initialize the function with "M_Vars".
MyFunction:
M_Vars
Declare arguments (function parameters):
To declare arguments use "M_Arg <name>, <size>". "Name" is the name of the variable and "size" is the variable size.
Valid size values are "S_WORD", "S_DWORD", "S_QWORD".
M_Arg var1, S_WORD
M_Arg var2, S_DWORD
Declare local variables:
To declare local variables use "M_Local <name>, <size>". "Name" is the name of the variable and "size" is the variable size.
Valid size values are "S_WORD", "S_DWORD", "S_QWORD" or for arrays multiple of 2 values.
M_Local foo , S_WORD
M_Local temp1 , S_DWORD
M_Local temp2 , S_WORD
M_Local array , S_WORD * 100
M_Local array2, S_DWORD * 20
End of declarations:
When you declared your variables use
"M_Code"
at the beginning of your function code.
M_Code
Note: M_Code is the same as
enter LOCAL_LEN, 0
Using the variables:
You can use arguments and local variables in the same way. The syntax is a dot and then the variable name. For example ".foo".
mov ax, [.foo]
mov [.temp1], edx
cmp word [.var1], 2001h
Function end:
At the end of your function use
M_Ret
Which is the same as
leave
ret ARG_LEN
M_VarsEnd
Function sample:
MyFunction:
; We are using arguments and/or local variables
M_Vars
; Declare arguments
M_Arg var1, S_WORD
M_Arg var2, S_DWORD
; Declare local variables
M_Local foo, S_WORD
; Function code
M_Code
; Do something
nop
nop
; Return
M_Ret
How to call the function with parameters:
You pass the parameters to the function with "push". You have to pass them in the opposite direction of your declaration. The first parameter is pushed at last and the
last parameter is pushed first.
push dword 6 ; This became .var2
push ax ; This became .var1
call MyFunction ; (word var1, dword var2)
sample.asm:
The sample.asm program is written for DOS. It is just for demonstration purpose and makes nothing useful, but it shows you
- how to use arguments and local variables.
- using a function return value.
- using the M_Printf macro.
- using the bit macro.
- declare and using an array.
- set all local variables of a function to 0.
- a recursive function sample.
Output of sample.asm:
Hello world.
Hello world with macro!
var2 is not 100.
Value is not 100.
Bit 4 is set.
RecursiveSample:
depth: 1 value1: 1 value2: 0
depth: 2 value1: 2 value2: 1
depth: 3 value1: 4 value2: 2
depth: 4 value1: 8 value2: 3
Back in depth 3
Back in depth 2
Back in depth 1
mc/assembler.syntax
I extended the assembler syntax highlighting file of the Midnight Commander for my needs. The syntax highlighting file is
usually stored in "/usr/share/mc/syntax/".
Sample files
sample.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
|
;========================================================= ; Demonstration of NASM Helper by Elmar Hanlhofer ;=========================================================
[ORG 0x100]
%define STRING_END "$"
%include "sys/const.inc" %include "sys/sys16.inc"
%include "macro/args.inc" %include "macro/bit.inc" %include "macro/printf.inc"
TESTBIT4 equ 4 OTHERBIT equ 7
start: push cs pop ds
push cs pop ss
mov sp, 0FFFEh
;============================== push word hello call PrintString ; (word string) M_PrintfNL "Hello world with macro!" ; Do an extra new line M_PrintfNL ""
;============================== push word 5 push word 200 call SimpleAdd ; (word value1, word value2) push ax push word 150 call SimpleAdd ; (word value1, word value2) ; ax has now the value 355 (136h) ;============================== push ax call LocalStuff ; (word value) M_PrintfNL ""
;============================== push word 20 call IsValue100 ; (word value) jc .is_not_100 M_PrintfNL "Value is 100." jmp .next .is_not_100: M_PrintfNL "Value is not 100." .next: M_PrintfNL ""
;============================== mov ax, b(TESTBIT4) | b(OTHERBIT) test ax, b(TESTBIT4) jne .is_set M_PrintfNL "Bit 4 is not set." jmp .next2 .is_set: M_PrintfNL "Bit 4 is set."
.next2: M_PrintfNL ""
mov bx, ~b(OTHERBIT) ; Bitwise not sample ;============================== push word TRUE push dword 130 push word 20 call SampleClearLocalVars ; (word value1, dword value2, word bool)
;============================== call LocalArraySample M_PrintfNL "" ;============================== push word 0 push word 1 call RecursiveSample ; (word value1, word value2)
;============================== .exit: mov ax, 0x4c00 int 21h
hello db "Hello world.", 0Dh, 0Ah, "$"
;====================================== ;void PrintString (word string) ;====================================== PrintString: M_Vars M_Arg string, S_WORD
M_Code push ax push dx
mov ah, 9 mov dx, [.string] int 21h
pop dx pop ax M_Ret
;====================================== ;void PrintCharAL (ax) ;-------------------------------------- ;Print a single char ;====================================== PrintCharAL: push ax push dx mov dl, al mov ah, 2 int 21h pop dx pop ax
ret
;====================================== ;word SimpleAdd (word value1, word value2) ;====================================== SimpleAdd: M_Vars M_Arg value1, S_WORD M_Arg value2, S_WORD
M_Code
mov ax, [.value1] add ax, [.value2]
; ax is the return value M_Ret
;====================================== ;word LocalStuff (word value) ;-------------------------------------- ;Just demonstrating how to work with arguments and local variables ;Return word ;====================================== LocalStuff: M_Vars
M_Arg value, S_WORD
M_Local var1, S_WORD M_Local var2, S_WORD M_Code push bx
mov ax, [.value] mov [.var1], ax mov word [.var2], 20 add word [.var2], ax
mov bx, [.var1]
cmp word [.var2], 100 je .result1 M_PrintfNL "var2 is not 100." jmp .done
.result1: M_PrintfNL "var2 is 100."
.done: ; ax is the return variable mov ax, [.var2]
pop bx M_Ret
;====================================== ;bool IsValue100 (word value) ;====================================== IsValue100: M_Vars M_Arg value, S_WORD
M_Code
cmp word [.value], 100 je .is100
; Set carry flag for bool return (false, error) stc jmp .done
.is100: ; Clear carry flag for bool return (true, success) clc .done: ; Carry flag is the return value M_Ret
;====================================== ;void SampleClearLocalVars (word value1, dword value2, word bool) ;-------------------------------------- ;Demonstrate how to init local variables with value 0 ;====================================== SampleClearLocalVars: M_Vars
M_Arg value1, S_WORD M_Arg value2, S_DWORD M_Arg bool , S_WORD M_Local val1 , S_WORD M_Local len , S_DWORD M_Local buffer, S_WORD M_Local absval, S_WORD M_Code push ax push bx
M_LocalClear mov ax, [.value1] mov bx, [.value2]
cmp byte [.bool], TRUE je .is_true nop nop .is_true: cmp ax, 200 je .result1 mov [.buffer], bx .result1: cmp word [.buffer], 0 je .not_set nop nop jmp .done .not_set: nop nop .done: pop bx pop ax M_Ret
;====================================== ;void LocalArraySample () ;====================================== LocalArraySample: M_Vars
M_Local array, S_WORD * 100 M_Local dummy, S_DWORD M_Code ; Clear array and any other local variables M_LocalClear
push di
; Get array address M_GetLocalAddress array, di ; Set 3rd field in array to 30 mov word [ss:di + 2 * S_WORD], 30 pop di M_Ret
;====================================== ;void RecursiveSample (word value1, word value2) ;====================================== RecursiveSample: M_Vars
M_Arg value1, S_WORD M_Arg value2, S_WORD M_Local depth, S_WORD M_Local temp , S_WORD M_Code push ax
mov ax, [.value2] inc ax mov [.depth], ax
cmp byte [.value2], 0 jne .skip_header M_PrintfNL "RecursiveSample:"
.skip_header: M_Printf " depth: " mov al, [.depth] add al, "0" call PrintCharAL
M_Printf " value1: " mov ax, [.value1] add al, "0" call PrintCharAL M_Printf " value2: " mov ax, [.value2] add al, "0" call PrintCharAL M_PrintfNL ""
cmp byte [.depth], 4 je .break shl word [.value1], 1 push word [.depth] push word [.value1] call RecursiveSample
M_Printf " Back in depth " mov al, [.depth] add al, "0" call PrintCharAL M_PrintfNL ""
.break: pop ax M_Ret
|
Arguments macro
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
;=========================================================== ; Macros to simplify usage of arguments and local variables. ; ; Written by Elmar Hanlhofer, (C)2017-2022, https://www.plop.at ; Free to use, without warranty. ;===========================================================
%macro M_Vars 0
%push my_args
%ifdef SYS64 %assign %$arg_offset_macro S_QWORD + S_QWORD ; Keep rbp and call return save on stack %elifdef SYS32 %assign %$arg_offset_macro S_DWORD + S_DWORD ; Keep ebp and call return save on stack %else %assign %$arg_offset_macro S_WORD + S_WORD ; Keep bp and call return save on stack %endif
%define ARG_LEN ; Keep ARG_LEN empty until use of arguments, to have an empty return ; parameter if no arguments are used.
%assign %$local_offset_macro 0 %assign %$local_len_macro 0 %define LOCAL_LEN %$local_len_macro
%endmacro
%macro M_Local 2
.%{1}_base_macro equ %$local_offset_macro .%{1}_offset_macro equ %$local_offset_macro + %2 %ifdef SYS64 %define .%1 (rbp - .%{1}_offset_macro) %elifdef SYS32 %define .%1 (ebp - .%{1}_offset_macro) %else %define .%1 (bp - .%{1}_offset_macro) %endif %assign %$local_offset_macro %$local_offset_macro + %2 %assign %$local_len_macro %$local_len_macro + %2
%endmacro
%macro M_Arg 2
%ifndef ARGS ; Inititalize ARG_LEN %define ARGS %assign %$arg_len_macro 0 %define ARG_LEN %$arg_len_macro %endif
.%{1}_offset_macro equ %$arg_offset_macro %ifdef SYS64 %define .%1 (rbp + .%{1}_offset_macro) %elifdef SYS32 %define .%1 (ebp + .%{1}_offset_macro) %else %define .%1 (bp + .%{1}_offset_macro) %endif %assign %$arg_offset_macro %$arg_offset_macro + %2 %assign %$arg_len_macro %$arg_len_macro + %2
%endmacro
%macro M_VarsEnd 0
%undef .arg_len_macro %undef .local_len_macro
%undef LOCAL_LEN %undef ARG_LEN %undef ARGS %pop ; Restore original context
%endmacro
%macro M_LocalClear 0 ; Set local variables to 0
%ifdef SYS64 push rcx push rdi
mov rcx, LOCAL_LEN / 2 mov rdi, rbp sub rdi, S_QWORD ; Dont touch rbp on stack
.local_clear_stack: mov word [ss:rdi], 0 sub rdi, 2 loop .local_clear_stack
pop rdi pop rcx
%elifdef SYS32 push ecx push edi
mov ecx, LOCAL_LEN / 2 mov edi, ebp sub edi, S_DWORD ; Dont touch ebp on stack
.local_clear_stack: mov word [ss:edi], 0 sub edi, 2 loop .local_clear_stack
pop edi pop ecx
%else push cx push di
mov cx, LOCAL_LEN / 2 mov di, bp sub di, S_WORD ; Dont touch bp on stack
.local_clear_stack: mov word [ss:di], 0 sub di, 2 loop .local_clear_stack
pop di pop cx
%endif
%endmacro
%macro M_GetLocalAddress 2 ; Get stack address to register (local_variable, register)
%ifdef SYS64 mov %2, rbp %elifdef SYS32 mov %2, ebp %else mov %2, bp %endif sub %2, .%{1}_offset_macro
%endmacro
%define M_Code enter LOCAL_LEN, 0
%macro M_Ret 0
leave ret ARG_LEN M_VarsEnd %endmacro
%macro M_Retf 0
leave retf ARG_LEN M_VarsEnd %endmacro
|
Sample 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
mov dword [.length], 8 ; At first, receive only 8 bytes of the device descriptor
; Setup Packet push word [.length] push word 0x0000 push word USB_DESCRIPTOR_TYPE_DEVICE << 8 ; 0x0100 push word USB_GET_DESCRIPTOR ; 0x06 push word 0x80 call CreateSetupPacket ; (word bmRequestType, word bRequest, word wValue, word wIndex, word wLength) jc .error
mov [.setup], eax
; Allocate buffer for the descriptor push dword 8 call MEM_Allocate jc .error
mov [.buffer], eax
push dword 0 push dword Enumerate push dword [.length] push dword [.buffer] push dword [.setup] push dword [.device] call QueueControlTransfer ; (dword device, dword setup, dword buffer, ; dword buffer_len, dword callback, dword callback_data) jc .error
|
Complex function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
|
;====================================== ;bool QueueControlTransfer (dword device, dword setup, ; dword buffer, dword buffer_len, ; dword callback, dword callback_data) ;====================================== QueueControlTransfer: M_Vars M_Arg device , S_DWORD M_Arg setup , S_DWORD M_Arg buffer , S_DWORD M_Arg buffer_len, S_DWORD M_Arg callback , S_DWORD M_Arg callback_data, S_DWORD
M_Local ed0 , S_DWORD M_Local first_td , S_DWORD M_Local current_td, S_DWORD M_Local last_td , S_DWORD M_Local new_td , S_DWORD
M_Local endpoint_max_packet_size, S_DWORD
M_Local request_direction, S_WORD M_Local data_toggle , S_WORD M_Local buffer_rounding , S_WORD
M_Local transfer, S_DWORD M_Local transfer_td_list, S_DWORD
M_Local host, S_DWORD M_Code M_LocalClear push eax push ebx push ecx push edx push esi push edi
mov ebx, [.device] ;================================ ; Setup direction mov esi, [.setup] mov al, [esi] test al, 80h ; bit 7 0...Host to device ; bit 7 1...Device to host je .host_to_device mov word [.request_direction], OHCI_IN ; Device to host jmp .direction_done
.host_to_device: mov word [.request_direction], OHCI_OUT ; Host to device
.direction_done:
;================================ ; Get device host push dword [ebx + USB_DEVICE.dHost] pop dword [.host] ;================================ ; Get address of endpoint descriptor for EP0 mov esi, [ebx + USB_DEVICE.dEP0] mov [.ed0], esi
;================================ ; Create the transfer ;================================ push dword TRANSFER.size call MEM_Allocate jc .error_transfer mov [.transfer], eax
;================================ ; Get address for the td_list mov eax, [.transfer] add eax, TRANSFER.dTDList mov [.transfer_td_list], eax
;================================ ; Setup Transfer push dword [.device] push dword TRANSFER.dDevice push dword [.transfer] call SetValueDword ; (dword address, dword offset, dword value)
push dword [.ed0] push dword TRANSFER.dED push dword [.transfer] call SetValueDword ; (dword address, dword offset, dword value)
push dword [.setup] push dword TRANSFER.dSetup push dword [.transfer] call SetValueDword ; (dword address, dword offset, dword value)
push dword [.buffer] push dword TRANSFER.dBuffer push dword [.transfer] call SetValueDword ; (dword address, dword offset, dword value)
push dword [.buffer_len] push dword TRANSFER.dBufferLen push dword [.transfer] call SetValueDword ; (dword address, dword offset, dword value)
push dword [.callback] push dword TRANSFER.dCallback push dword [.transfer] call SetValueDword ; (dword address, dword offset, dword value)
push dword [.callback_data] push dword TRANSFER.dCallbackData push dword [.transfer] call SetValueDword ; (dword address, dword offset, dword value)
;================================ ; Add Transfer to the host push dword [.transfer] mov edi, [.host] add edi, OHCI_HOST.dTransferList push edi call LIST_AddEntry ; (dword list, dword entry) jc .error_transfer
;================================ ; Get Max Packet Size of EP0 movzx edx, word [ebx + USB_DEVICE.wEP0_MaxPacketSize] mov [.endpoint_max_packet_size], edx ;================================ ; Get Last TD of ED, which became the first td of the new transfer mov edx, [esi + OHCI_ED.dTailP] mov [.current_td], edx mov [.first_td] , edx mov [.last_td] , edx ;================================ ; Allocate following TD push dword OHCI_TD.size call MEM_AllocateAligned16 jc .error mov [.new_td], eax ;================================ ; Update previous transfer descriptor with the SETUP ;================================ mov byte [.data_toggle], 2 ; Initial data toggle for SETUP PID
push dword [.new_td] push word SETUP_PACKET.size push dword [.setup] push word OHCI_CC_NOT_ACCESSED push word [.data_toggle] push word OHCI_NO_DELAY_INTERRUPT push word OHCI_SETUP push word FALSE push dword [.current_td] call UpdateTransferDescriptor ; (dword td_addr, word buffer_rounding, word pid, ; word delay_interrupt, word data_toggle, word condition_code, ; dword buffer_addr, word buffer_len, dword next_td_addr)
;================================ ; Add TD to the Transfer TD list push dword [.current_td] push dword [.transfer_td_list] call LIST_NewEntry ; (dword list, dword value) jc .error ;=================================== ; Create TDs for data packets ;===================================
mov edi, [.buffer] mov ecx, [.buffer_len]
.data_packets_loop: ; Use the latest created TD buffer mov eax, [.new_td] mov [.current_td], eax mov [.last_td] , eax or ecx, ecx je .data_packets_done ; Allocate a new following TD push dword OHCI_TD.size call MEM_AllocateAligned16 jc .error mov [.new_td], eax ; Check data packet size mov edx, ecx cmp edx, [.endpoint_max_packet_size] jb .data_packet_small
; Data packet is too large, force to max packet size mov edx, [.endpoint_max_packet_size]
; Disallow buffer rounding mov word [.buffer_rounding], FALSE jmp .update_td
.data_packet_small: ; Allow buffer rounding mov word [.buffer_rounding], TRUE
.update_td: xor byte [.data_toggle], 1 push dword [.new_td] push dx ; This is in word size! Loop is using edx, but here it is at maximum word size. ; Max TD buffer len is word. push edi push word OHCI_CC_NOT_ACCESSED push word [.data_toggle] push word OHCI_NO_DELAY_INTERRUPT push word [.request_direction] push word [.buffer_rounding] push dword [.current_td] call UpdateTransferDescriptor ; (dword td_addr, word buffer_rounding, word pid, ; word delay_interrupt, word data_toggle, word condition_code, ; dword buffer_addr, word buffer_len, dword next_td_addr)
; Add TD to the Transfer TD list push dword [.current_td] push dword [.transfer_td_list] call LIST_NewEntry ; (dword list, dword value) jc .error
add edi, edx ; Set new destination buffer address for next TD sub ecx, edx jmp .data_packets_loop
.data_packets_done: ;================================= ; Create TD for SETUP status ;================================= ; Allocate a new following TD push dword OHCI_TD.size call MEM_AllocateAligned16 jc .error mov [.new_td], eax ; Switch direction cmp word [.request_direction], OHCI_IN je .dir_out mov word [.request_direction], OHCI_IN jmp .dir_done
.dir_out: mov word [.request_direction], OHCI_OUT
.dir_done: mov word [.buffer_rounding], FALSE xor byte [.data_toggle], 1 push dword [.new_td] push word 0 ; buffer len push dword 0 ; buffer addr push word OHCI_CC_NOT_ACCESSED ; condition code push word [.data_toggle] push word 6 ; delay interrupt push word [.request_direction] push word [.buffer_rounding] push dword [.current_td] call UpdateTransferDescriptor ; (dword td_addr, word buffer_rounding, word pid, ; word delay_interrupt, word data_toggle, word condition_code, ; dword buffer_addr, word buffer_len, dword next_td_addr)
; Add TD to the Transfer TD list push dword [.current_td] push dword [.transfer_td_list] call LIST_NewEntry ; (dword list, dword value) jc .error
; Update Last TD value in transfer push dword [.current_td] push dword TRANSFER.dLastTD push dword [.transfer] call SetValueDword
;================================= ; Set Control List filled push dword [.host] call SetControlListFilled
clc jmp .done .error: push dword [.transfer] push dword [.host] call FreeTransfer ; (dword host, dword transfer) .error_transfer: push dword [.transfer] call MEM_Free stc
.done: pop edi pop esi pop edx pop ecx pop ebx pop eax M_Ret
|
© 2024 by
Elmar Hanlhofer This page was last modified on 18/03/2022.
|