1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
3
4//go:build linux
5
6#include "textflag.h"
7#include "go_asm.h"
8#include "funcdata.h"
9
10// callbackasm1 is the second part of the callback trampoline.
11// On entry:
12// - CX contains the callback index (set by callbackasm)
13// - 0(SP) contains the return address to C caller
14// - 4(SP), 8(SP), ... contain C arguments (cdecl convention)
15//
16// i386 cdecl calling convention:
17// - All arguments passed on stack
18// - Return value in EAX (and EDX for 64-bit)
19// - Caller cleans the stack
20// - Callee must preserve: EBX, ESI, EDI, EBP
21TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
22 NO_LOCAL_POINTERS
23
24 // Save the return address
25 MOVL 0(SP), AX
26
27 // Allocate stack frame (must be done carefully to preserve args access)
28 // Layout:
29 // 0-15: saved callee-saved registers (BX, SI, DI, BP)
30 // 16-19: saved callback index
31 // 20-23: saved return address
32 // 24-47: callbackArgs struct (24 bytes: index, args, result[4],
33 // matching Go's callbackArgs with room for 64-bit returns)
34 // 48-303: copy of C arguments (256 bytes for 64 args, matching callbackMaxFrame)
35 // Total: 304 bytes, already 16-byte aligned
36 SUBL $304, SP
37
38 // Save callee-saved registers
39 MOVL BX, 0(SP)
40 MOVL SI, 4(SP)
41 MOVL DI, 8(SP)
42 MOVL BP, 12(SP)
43
44 // Save callback index and return address
45 MOVL CX, 16(SP)
46 MOVL AX, 20(SP)
47
48 // Copy C arguments from original stack location to our frame
49 // Original args start at 304+4(SP) = 308(SP) (past our frame + original return addr)
50 // Copy to our frame at 48(SP)
51 // Copy 64 arguments (256 bytes, matching callbackMaxFrame = 64 * ptrSize)
52 MOVL 308(SP), AX
53 MOVL AX, 48(SP)
54 MOVL 312(SP), AX
55 MOVL AX, 52(SP)
56 MOVL 316(SP), AX
57 MOVL AX, 56(SP)
58 MOVL 320(SP), AX
59 MOVL AX, 60(SP)
60 MOVL 324(SP), AX
61 MOVL AX, 64(SP)
62 MOVL 328(SP), AX
63 MOVL AX, 68(SP)
64 MOVL 332(SP), AX
65 MOVL AX, 72(SP)
66 MOVL 336(SP), AX
67 MOVL AX, 76(SP)
68 MOVL 340(SP), AX
69 MOVL AX, 80(SP)
70 MOVL 344(SP), AX
71 MOVL AX, 84(SP)
72 MOVL 348(SP), AX
73 MOVL AX, 88(SP)
74 MOVL 352(SP), AX
75 MOVL AX, 92(SP)
76 MOVL 356(SP), AX
77 MOVL AX, 96(SP)
78 MOVL 360(SP), AX
79 MOVL AX, 100(SP)
80 MOVL 364(SP), AX
81 MOVL AX, 104(SP)
82 MOVL 368(SP), AX
83 MOVL AX, 108(SP)
84 MOVL 372(SP), AX
85 MOVL AX, 112(SP)
86 MOVL 376(SP), AX
87 MOVL AX, 116(SP)
88 MOVL 380(SP), AX
89 MOVL AX, 120(SP)
90 MOVL 384(SP), AX
91 MOVL AX, 124(SP)
92 MOVL 388(SP), AX
93 MOVL AX, 128(SP)
94 MOVL 392(SP), AX
95 MOVL AX, 132(SP)
96 MOVL 396(SP), AX
97 MOVL AX, 136(SP)
98 MOVL 400(SP), AX
99 MOVL AX, 140(SP)
100 MOVL 404(SP), AX
101 MOVL AX, 144(SP)
102 MOVL 408(SP), AX
103 MOVL AX, 148(SP)
104 MOVL 412(SP), AX
105 MOVL AX, 152(SP)
106 MOVL 416(SP), AX
107 MOVL AX, 156(SP)
108 MOVL 420(SP), AX
109 MOVL AX, 160(SP)
110 MOVL 424(SP), AX
111 MOVL AX, 164(SP)
112 MOVL 428(SP), AX
113 MOVL AX, 168(SP)
114 MOVL 432(SP), AX
115 MOVL AX, 172(SP)
116 MOVL 436(SP), AX
117 MOVL AX, 176(SP)
118 MOVL 440(SP), AX
119 MOVL AX, 180(SP)
120 MOVL 444(SP), AX
121 MOVL AX, 184(SP)
122 MOVL 448(SP), AX
123 MOVL AX, 188(SP)
124 MOVL 452(SP), AX
125 MOVL AX, 192(SP)
126 MOVL 456(SP), AX
127 MOVL AX, 196(SP)
128 MOVL 460(SP), AX
129 MOVL AX, 200(SP)
130 MOVL 464(SP), AX
131 MOVL AX, 204(SP)
132 MOVL 468(SP), AX
133 MOVL AX, 208(SP)
134 MOVL 472(SP), AX
135 MOVL AX, 212(SP)
136 MOVL 476(SP), AX
137 MOVL AX, 216(SP)
138 MOVL 480(SP), AX
139 MOVL AX, 220(SP)
140 MOVL 484(SP), AX
141 MOVL AX, 224(SP)
142 MOVL 488(SP), AX
143 MOVL AX, 228(SP)
144 MOVL 492(SP), AX
145 MOVL AX, 232(SP)
146 MOVL 496(SP), AX
147 MOVL AX, 236(SP)
148 MOVL 500(SP), AX
149 MOVL AX, 240(SP)
150 MOVL 504(SP), AX
151 MOVL AX, 244(SP)
152 MOVL 508(SP), AX
153 MOVL AX, 248(SP)
154 MOVL 512(SP), AX
155 MOVL AX, 252(SP)
156 MOVL 516(SP), AX
157 MOVL AX, 256(SP)
158 MOVL 520(SP), AX
159 MOVL AX, 260(SP)
160 MOVL 524(SP), AX
161 MOVL AX, 264(SP)
162 MOVL 528(SP), AX
163 MOVL AX, 268(SP)
164 MOVL 532(SP), AX
165 MOVL AX, 272(SP)
166 MOVL 536(SP), AX
167 MOVL AX, 276(SP)
168 MOVL 540(SP), AX
169 MOVL AX, 280(SP)
170 MOVL 544(SP), AX
171 MOVL AX, 284(SP)
172 MOVL 548(SP), AX
173 MOVL AX, 288(SP)
174 MOVL 552(SP), AX
175 MOVL AX, 292(SP)
176 MOVL 556(SP), AX
177 MOVL AX, 296(SP)
178 MOVL 560(SP), AX
179 MOVL AX, 300(SP)
180
181 // Set up callbackArgs struct at 24(SP)
182 // struct callbackArgs {
183 // index uintptr // offset 0
184 // args unsafe.Pointer // offset 4
185 // result [4]uintptr // offset 8: result[0] and result[1] hold
186 // a 64-bit return, result[2:] are unused
187 // }
188 MOVL 16(SP), AX // callback index
189 MOVL AX, 24(SP) // callbackArgs.index
190 LEAL 48(SP), AX // pointer to copied arguments
191 MOVL AX, 28(SP) // callbackArgs.args
192 MOVL $0, 32(SP) // callbackArgs.result[0] = 0
193 MOVL $0, 36(SP) // callbackArgs.result[1] = 0
194 MOVL $0, 40(SP) // callbackArgs.result[2] = 0
195 MOVL $0, 44(SP) // callbackArgs.result[3] = 0
196
197 // Call crosscall2(fn, frame, 0, ctxt)
198 // crosscall2 expects arguments on stack:
199 // 0(SP) = fn
200 // 4(SP) = frame (pointer to callbackArgs)
201 // 8(SP) = ignored (was n)
202 // 12(SP) = ctxt
203 SUBL $16, SP
204
205 MOVL ·callbackWrap_call(SB), AX
206 MOVL (AX), AX // fn = *callbackWrap_call
207 MOVL AX, 0(SP) // fn
208 LEAL (24+16)(SP), AX // &callbackArgs (adjusted for SUB $16)
209 MOVL AX, 4(SP) // frame
210 MOVL $0, 8(SP) // 0
211 MOVL $0, 12(SP) // ctxt
212
213 CALL crosscall2(SB)
214
215 ADDL $16, SP
216
217 // Get result from callbackArgs.result. i386 cdecl returns 64-bit
218 // values in EDX:EAX, so forward both halves written by
219 // setInt64Result/setUint64Result.
220 MOVL 32(SP), AX
221 MOVL 36(SP), DX
222
223 // Restore callee-saved registers
224 MOVL 0(SP), BX
225 MOVL 4(SP), SI
226 MOVL 8(SP), DI
227 MOVL 12(SP), BP
228
229 // Restore return address and clean up
230 MOVL 20(SP), CX // get return address
231 ADDL $304, SP // remove our frame
232 MOVL CX, 0(SP) // put return address back
233
234 RET