English / Deutsch | Print version

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
How it works
mc/assembler.syntax
Sample files
    sample.asm
    Arguments macro - args.inc
    sample2.asm
    Complex function - QueueControlTransfer

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

© 2026 by Elmar Hanlhofer
This page was last modified on 18/03/2022.