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
|
%ifdef _X64_
[BITS 64]
[SEGMENT .data]
[EXTERN g_VectorDelta]
[EXTERN g_PieceData]
%ifdef OSX
;;; Note: The reason for this OSX stuff is that there is a bug in the mac
;;; version of nasm. See comments in data.c for more details.
[GLOBAL g_NasmVectorDelta]
[GLOBAL _g_NasmVectorDelta]
g_NasmVectorDelta:
_g_NasmVectorDelta:
alignb 32, db 0
times 256 * 4 db 0
[GLOBAL g_NasmPieceData]
[GLOBAL _g_NasmPieceData]
g_NasmPieceData:
_g_NasmPieceData:
alignb 32, db 0
times 4 * 4 * 8 db 0
%else
[EXTERN g_VectorDelta]
[EXTERN g_PieceData]
%endif
[SEGMENT .text]
%ifndef CROUTINES
[GLOBAL LastBit]
[GLOBAL _LastBit]
;;
;; ULONGLONG CDECL
;; LastBit(BITBOARD bb)
;;
LastBit:
_LastBit:
bsr rax, rdi
jnz .found
xor rax, rax
ret
.found: add rax, 1
ret
int 3
[GLOBAL FirstBit]
[GLOBAL _FirstBit]
;;
;; ULONGLONG CDECL
;; FirstBit(BITBOARD bb)
;;
FirstBit:
_FirstBit:
bsf rax, rdi
jnz .found
xor rax, rax
ret
.found: add rax, 1
ret
int 3
[GLOBAL CountBits]
[GLOBAL _CountBits]
;;
;; ULONGLONG CDECL
;; CountBits(BITBOARD bb)
;;
CountBits:
_CountBits:
xor rax, rax
mov rcx, rdi
test rcx, rcx
jz .done
.again: add rax, 1
mov rdx, rcx
sub rdx, 1
and rcx, rdx
jnz .again
.done: ret
int 3
%if 0
;; Retired 2026-09-06 (board_representation/MIGRATION.md section 7):
;; _GetAttacksBB (see.c) is now the only GetAttacks implementation --
;; all retirement criteria (correctness sweep, isolated + whole-engine
;; benchmarks, match_play.py gate) cleared. Left assembled-out rather
;; than deleted.
[GLOBAL GetAttacks]
[GLOBAL _GetAttacks]
db 43h,30h,50h,56h,72h,31h,47h,34h,54h,20h,32h,30h,30h
db 36h,20h,53h,63h,30h,74h,74h,20h,47h,61h,73h,63h,34h
iDelta dd -17
dd +15
;; [rbp-0x8] is the saved rbx from the "push rbx" in the prologue below;
;; locals must start below that, not alias it.
%define pOldList qword [rbp-0x10]
%define c dword [rbp-0x14]
%define x dword [rbp-0x18]
%define cSquareSave dword [rbp-0x1C]
%define uSideSave dword [rbp-0x20]
%define _cNonPawns 0x478
%define _uNonPawnCount 0x500
%define _rgSquare 0x0
;;
;; void CDECL (SysV AMD64 ABI)
;; GetAttacks(SEE_LIST *pList, ; rdi
;; POSITION *pos, ; rsi
;; COOR cSquare, ; edx
;; ULONG uSide) ; ecx
;;
;; NOTE: a square (COOR) is always >= 0, but a square-minus-
;; square delta is not. Any such delta must be sign-extended
;; into a 64-bit register (movsxd) before being used as a
;; scaled memory index: using the raw 32-bit register zero-
;; extends it into a huge positive 64-bit value instead of the
;; intended negative offset. This is the same bug that turned
;; up in the C DISTANCE macro -- see chess.h.
;;
GetAttacks:
_GetAttacks:
push rbp
mov rbp, rsp
push rbx
sub rsp, 0x20
mov cSquareSave, edx
mov uSideSave, ecx
mov pOldList, rdi ; pOldList = pList
mov dword [rdi], 0 ; pList->uCount = 0
add rdi, 4 ; rdi = &pList->data[0]
;; ebx = c = cSquare + iDelta[uSide]
movsxd rax, uSideSave
mov ebx, [iDelta+rax*4]
add ebx, cSquareSave
;; ecx = pPawn = BLACK_PAWN | uColor
mov ecx, uSideSave
or ecx, 2
;;
;; Check the pawns
;;
test ebx, 0x88
jnz .try_other_pawn
movsxd rax, ebx
mov eax, dword [rsi+rax*8+_rgSquare]
cmp eax, ecx
jne .try_other_pawn
mov rax, pOldList
mov dword [rax], 1
mov dword [rdi], ecx
mov dword [rdi+4], ebx
mov dword [rdi+8], 100
add rdi, 12
.try_other_pawn:
add ebx, 2
test ebx, 0x88
jnz .done_pawns
movsxd rax, ebx
mov eax, dword [rsi+rax*8+_rgSquare]
cmp eax, ecx
jne .done_pawns
mov dword [rdi], ecx
mov dword [rdi+4], ebx
mov dword [rdi+8], 100
mov rax, pOldList
add rdi, 12
add dword [rax], 1
.done_pawns:
;;
;; Do pieces
;;
;; x = pos->uNonPawnCount[uSide][0]
mov eax, uSideSave
shl eax, 5 ; uSide * 32
movsxd rax, eax
mov ecx, dword [rsi+rax+_uNonPawnCount]
mov x, ecx
.loop_continue:
mov ecx, x
sub ecx, 1
cmp ecx, 0
jl near .done
mov x, ecx
;; eax = c = pos->cNonPawns[uSide][x]
mov eax, uSideSave
shl eax, 4
add eax, ecx ; eax = uSide*16 + x
movsxd rax, eax
mov eax, dword [rsi+rax*4+_cNonPawns]
mov c, eax
;; ecx = p = pos->pSquare[c]
movsxd rax, eax
mov ecx, dword [rsi+rax*8+_rgSquare]
;; r8 = iIndex = c - cSquare (must stay signed)
sub eax, cSquareSave
movsxd r8, eax
;;
;; If there is no way for that kind of piece to get to this
;; square then keep looking.
;;
mov ebx, 1
mov r9d, ecx ; r9 = p, survives to .nothing_blocks
shr ecx, 1
shl ebx, cl
test byte [g_VectorDelta+r8*4+512], bl
jz .loop_continue
;; if (IS_KNIGHT_OR_KING(p)) goto nothing_blocks
and ecx, 3
cmp ecx, 2
mov ecx, c
je .nothing_blocks
;;
;; Not a knight or king. Check to see if there is a piece in
;; the path from cSquare to c that blocks the attack.
;;
movsx ebx, byte [g_VectorDelta+r8*4+515]
mov eax, cSquareSave
.block_loop:
add eax, ebx
cmp eax, ecx
je .nothing_blocks
movsxd rdx, eax
cmp dword [rsi+rdx*8], 0
jne .loop_continue
jmp .block_loop
.nothing_blocks:
mov dword [rdi], r9d
mov dword [rdi+4], ecx
;; index into g_PieceData: sizeof(PIECE_DATA) is 24 on x64 (not
;; 16, as on x86) because its trailing "CHAR *szName" widens
;; from 4 to 8 bytes and pads the struct to 8-byte alignment.
mov ecx, r9d
shr ecx, 1
imul ecx, ecx, 24
movsxd rcx, ecx
mov ebx, dword [g_PieceData+rcx]
mov dword [rdi+8], ebx
mov rax, pOldList
add rdi, 12
add dword [rax], 1
jmp .loop_continue
.done: add rsp, 0x20
pop rbx
pop rbp
ret
%endif ; 0 (retired GetAttacks)
%endif ; !CROUTINES
[GLOBAL LockCompareExchange]
[GLOBAL _LockCompareExchange]
;;
;; ULONG CDECL
;; LockCompareExchange(void *dest, ; rdi (SysV AMD64 ABI)
;; ULONG exch, ; esi
;; ULONG comp) ; edx
;;
LockCompareExchange:
_LockCompareExchange:
mov eax, edx ; comp -> eax (cmpxchg's implicit compare reg)
mov rcx, rdi ; dest pointer -> rcx
lock cmpxchg dword [rcx], esi
ret
int 3
[GLOBAL LockIncrement]
[GLOBAL _LockIncrement]
;;
;; ULONG CDECL
;; LockIncrement(ULONG *pDest) ; rdi (SysV AMD64 ABI)
;;
LockIncrement:
_LockIncrement:
mov rcx, rdi
mov eax, 1
lock xadd [rcx], eax
add eax, 1
ret
int 3
[GLOBAL LockDecrement]
[GLOBAL _LockDecrement]
;;
;; ULONG CDECL
;; LockDecrement(ULONG *pDest) ; rdi (SysV AMD64 ABI)
;;
LockDecrement:
_LockDecrement:
mov rcx, rdi
mov eax, -1
lock xadd [rcx], eax
add eax, -1
ret
int 3
%endif ; X64
|