Elmar Hanlhofer - Grinch Selfie
zoom

English / Deutsch | Print version
Twitter
twitter

NASM Helper


Introduction
Download
How it works
mc/assembler.syntax
Sample files
    sample.asm
    Arguments macro - args.inc
    sample2.asm
    Complex function - QueueControlTransfer

Introduction


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.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
Arguments macro - args.inc
sample2.asm
Complex function - QueueControlTransfer


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
371
372
373
374
375
;=========================================================
; 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

; Using enter instead of M_Code, result is the same
enter LOCAL_LEN, 0

mov ax, [.value1]
add ax, [.value2]

; This is the same like M_Ret
leave
ret ARG_LEN
M_VarsEnd


;======================================
;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

; Using enter instead of M_Code, result is the same
enter LOCAL_LEN, 0

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:
; This is the same like M_Ret
leave
ret ARG_LEN
M_VarsEnd


;======================================
;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
push di

; Clear array and any other local variables
M_LocalClear

; 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
;===========================================================
; Macros to simplify usage of arguments and local variables.
;
; Written by Elmar Hanlhofer, (C)2017, https://www.plop.at
; Free to use, without warranty.
;===========================================================

%macro M_Vars 0

%push my_args

%ifdef SYS64
%assign %$arg_offset S_QWORD + S_QWORD ; Keep rbp and call return save on stack
%elifdef SYS32
%assign %$arg_offset S_DWORD + S_DWORD ; Keep ebp and call return save on stack
%else
%assign %$arg_offset 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 0
%assign %$local_len 0
%define LOCAL_LEN %$local_len

%endmacro


%macro M_Local 2

.%{1}_base equ %$local_offset
.%{1}_offset equ %$local_offset + %2
%ifdef SYS64
%define .%1 (rbp - .%{1}_offset)
%elifdef SYS32
%define .%1 (ebp - .%{1}_offset)
%else
%define .%1 (bp - .%{1}_offset)
%endif
%assign %$local_offset %$local_offset + %2
%assign %$local_len %$local_len + %2

%endmacro


%macro M_Arg 2

%ifndef ARGS ; Inititalize ARG_LEN
%define ARGS
%assign %$arg_len 0
%define ARG_LEN %$arg_len
%endif

.%{1}_offset equ %$arg_offset
%ifdef SYS64
%define .%1 (rbp + .%{1}_offset)
%elifdef SYS32
%define .%1 (ebp + .%{1}_offset)
%else
%define .%1 (bp + .%{1}_offset)
%endif
%assign %$arg_offset %$arg_offset + %2
%assign %$arg_len %$arg_len + %2

%endmacro


%macro M_VarsEnd 0

%undef .arg_len
%undef .local_len

%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:
sub rdi, 2
mov word [ss:rdi], 0
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:
sub edi, 2
mov word [ss:edi], 0
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:
sub di, 2
mov word [ss:di], 0
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

%endmacro


%define M_Code enter LOCAL_LEN, 0


%macro M_Ret 0

leave
ret 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
push eax
push ebx
push ecx
push edx
push esi
push edi

M_LocalClear

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

© 2018 by Elmar Hanlhofer
This page was last modified on 29/12/2017.