-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathuci.asm
More file actions
784 lines (649 loc) · 21.1 KB
/
uci.asm
File metadata and controls
784 lines (649 loc) · 21.1 KB
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
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
; ==============================================================================
; RCA 1802/1806 Chess Engine - UCI Protocol Implementation
; ==============================================================================
; Universal Chess Interface for communication with GUI
; Implements minimal UCI subset for playability
; ==============================================================================
; ------------------------------------------------------------------------------
; UCI Command Buffer - defined in board-0x88.asm
; ------------------------------------------------------------------------------
; UCI_BUFFER ($7000, 2048 bytes) and UCI_STATE ($64B8) defined in board-0x88.asm
UCI_BUFFER_LEN EQU 2047 ; Max chars (16-bit counter, 2048-byte buffer)
UCI_READY EQU 1 ; Ready state
; ------------------------------------------------------------------------------
; UCI String Constants
; ------------------------------------------------------------------------------
; These would be defined as byte arrays in actual implementation
STR_UCI DB "uci", 0
STR_ISREADY DB "isready", 0
STR_POSITION DB "position", 0
STR_GO DB "go", 0
STR_QUIT DB "quit", 0
STR_UCINEWGAME DB "ucinewgame", 0
STR_STOP DB "stop", 0
; Response strings (CR+LF for terminal display)
STR_ID_NAME DB "id name RCA-Chess-1806", 13, 10, 0
STR_ID_AUTHOR DB "id author Mark Abene & Claude Code", 13, 10, 0
STR_OPTION_DEPTH DB "option name Depth type spin default 4 min 1 max 6", 13, 10, 0
STR_UCIOK DB "uciok", 13, 10, 0
STR_READYOK DB "readyok", 13, 10, 0
STR_BESTMOVE DB "bestmove ", 0
STR_INFO_DEPTH DB "info depth ", 0
STR_SCORE_CP DB " score cp ", 0
STR_NODES DB " nodes ", 0
; ------------------------------------------------------------------------------
; UCI_INIT - Initialize UCI interface
; ------------------------------------------------------------------------------
UCI_INIT:
; Set up serial I/O (if needed)
; TODO: Hardware-specific initialization
; Set initial state
LDI UCI_READY
PLO 13
RLDI 10, UCI_STATE
GLO 13
STR 10
RETN
; ------------------------------------------------------------------------------
; UCI_READ_LINE - Read a line of input
; ------------------------------------------------------------------------------
; Input: None
; Output: Line in UCI_BUFFER, null-terminated
; D = length
; Uses: A, D
;
; Reads characters until newline or max length
; ------------------------------------------------------------------------------
UCI_READ_LINE:
RLDI 10, UCI_BUFFER
LDI 0
PHI 13 ; R13.1 = count high byte = 0
PLO 13 ; R13.0 = count low byte = 0
UCI_READ_LOOP:
; Read character from serial input
CALL SERIAL_READ_CHAR
; Returns character in D
; Check for carriage return (CR = 13)
SMI 13
LBZ UCI_READ_DONE
; Check for newline (LF = 10) - restore D first
ADI 13 ; Restore: (D-13)+13 = D
SMI 10 ; Check for LF
LBZ UCI_READ_DONE
ADI 10 ; Restore D
; Store character
STR 10
INC 10
; Increment 16-bit count
INC 13
; Check buffer limit (16-bit: UCI_BUFFER_LEN = 2047 = $07FF)
GLO 13
XRI LOW(UCI_BUFFER_LEN)
LBNZ UCI_READ_LOOP
GHI 13
XRI HIGH(UCI_BUFFER_LEN)
LBNZ UCI_READ_LOOP
; Count == UCI_BUFFER_LEN, buffer full
UCI_READ_DONE:
; Null-terminate string
LDI 0
STR 10
; Echo CR+LF
LDI 13
CALL SERIAL_WRITE_CHAR
LDI 10
CALL SERIAL_WRITE_CHAR
; Return length
GLO 13
RETN
; NOTE: UCI_WRITE_STRING removed - use F_MSG with R15 directly
; The old UCI_WRITE_STRING used R10, which clobbered the negamax loop counter!
; ------------------------------------------------------------------------------
; UCI_PROCESS_COMMAND - Process UCI command from buffer
; ------------------------------------------------------------------------------
; Input: Command in UCI_BUFFER
; Output: Appropriate action taken
; ------------------------------------------------------------------------------
UCI_PROCESS_COMMAND:
; Compare command with known commands
; Check "uci"
RLDI 10, UCI_BUFFER
RLDI 11, STR_UCI
CALL STRCMP
LBZ UCI_CMD_UCI
; Check "isready"
RLDI 10, UCI_BUFFER
RLDI 11, STR_ISREADY
CALL STRCMP
LBZ UCI_CMD_ISREADY
; Check "position"
RLDI 10, UCI_BUFFER
RLDI 11, STR_POSITION
CALL STRNCMP ; Compare first 8 chars
LBZ UCI_CMD_POSITION
; Check "go"
RLDI 10, UCI_BUFFER
RLDI 11, STR_GO
CALL STRNCMP
LBZ UCI_CMD_GO
; Check "quit"
RLDI 10, UCI_BUFFER
RLDI 11, STR_QUIT
CALL STRCMP
LBZ UCI_CMD_QUIT
; Check "ucinewgame"
RLDI 10, UCI_BUFFER
RLDI 11, STR_UCINEWGAME
CALL STRCMP
LBZ UCI_CMD_UCINEWGAME
; Check "stop"
RLDI 10, UCI_BUFFER
RLDI 11, STR_STOP
CALL STRCMP
LBZ UCI_CMD_STOP
; Unknown command - ignore
RETN
; ------------------------------------------------------------------------------
; UCI Command Handlers
; ------------------------------------------------------------------------------
UCI_CMD_UCI:
; Send identification
RLDI 15, STR_ID_NAME
SEP 4
DW F_MSG
RLDI 15, STR_ID_AUTHOR
SEP 4
DW F_MSG
; Send options
RLDI 15, STR_OPTION_DEPTH
SEP 4
DW F_MSG
; Send uciok
RLDI 15, STR_UCIOK
SEP 4
DW F_MSG
RETN
UCI_CMD_ISREADY:
; Send readyok
RLDI 15, STR_READYOK
SEP 4
DW F_MSG
RETN
UCI_CMD_POSITION:
; Parse position command
; Format: "position startpos" or "position startpos moves e2e4 ..."
; Check if "startpos"
RLDI 10, UCI_BUFFER + 9
; Check for "startpos"
LDN 10
XRI 's'
LBNZ UCI_POS_DONE ; Not startpos (FEN not supported yet)
; Initialize to starting position
CALL INIT_BOARD
CALL INIT_MOVE_HISTORY
; --- Compute and store starting position hash for repetition detection ---
CALL HASH_INIT ; Compute hash for starting position
RLDI 10, HASH_HIST ; R10 → first hash history entry
RLDI 13, HASH_HI
LDA 13 ; D = HASH_HI, R13 → HASH_LO
STR 10
INC 10
LDN 13 ; D = HASH_LO
STR 10
RLDI 10, HASH_HIST_COUNT
LDI 1
STR 10 ; count = 1 (starting position stored)
; Skip past "startpos" (8 chars) to check for " moves"
; Buffer now at "startpos..." - need to find " moves "
RLDI 10, UCI_BUFFER + 17
; Check if there's more (should be space or null)
LDN 10
LBZ UCI_POS_DONE ; End of string, no moves
XRI ' '
LBNZ UCI_POS_DONE ; Not a space, unexpected
; Skip the space
INC 10
; Check for "moves" (5 chars)
LDN 10
XRI 'm'
LBNZ UCI_POS_DONE
INC 10
LDN 10
XRI 'o'
LBNZ UCI_POS_DONE
INC 10
LDN 10
XRI 'v'
LBNZ UCI_POS_DONE
INC 10
LDN 10
XRI 'e'
LBNZ UCI_POS_DONE
INC 10
LDN 10
XRI 's'
LBNZ UCI_POS_DONE
INC 10 ; R10 now past "moves"
; Now parse move list: " e2e4 e7e5 ..."
UCI_POS_MOVE_LOOP:
; Skip spaces
LDN 10
LBZ UCI_POS_DONE ; End of string
XRI ' '
LBNZ UCI_POS_PARSE_MOVE ; Not a space, should be move
INC 10
LBR UCI_POS_MOVE_LOOP
UCI_POS_PARSE_MOVE:
; R10 points to start of move (e.g., "e2e4")
; Parse from square (2 chars)
CALL ALGEBRAIC_TO_SQUARE
XRI $FF
LBZ UCI_POS_DONE ; Invalid square, abort
; Save from square
XRI $FF ; Restore value (XRI is self-inverse)
PHI 7 ; R7.1 = from square
; Parse to square (2 chars)
CALL ALGEBRAIC_TO_SQUARE
XRI $FF
LBZ UCI_POS_DONE ; Invalid square, abort
XRI $FF ; Restore value
; Store from and to in MOVE_FROM/MOVE_TO
PLO 7 ; R7.0 = to square
RLDI 8, MOVE_FROM
GHI 7
STR 8 ; MOVE_FROM = from
INC 8
GLO 7
STR 8 ; MOVE_TO = to
; ---- Check for promotion suffix (5th char: q/r/b/n) ----
; Clear UNDO_PROMOTION first (default: no promotion)
RLDI 8, UNDO_PROMOTION
LDI 0
STR 8 ; UNDO_PROMOTION = 0
; Check next char for promotion piece
LDN 10 ; Peek next char
XRI 'q'
LBZ UCI_PROMO_QUEEN
XRI $03 ; 'q'^'r' = $71^$72 = $03
LBZ UCI_PROMO_ROOK
XRI $10 ; 'r'^'b' = $72^$62 = $10
LBZ UCI_PROMO_BISHOP
XRI $0C ; 'b'^'n' = $62^$6E = $0C
LBZ UCI_PROMO_KNIGHT
LBR UCI_PROMO_DONE ; Not a promotion char, don't advance
UCI_PROMO_QUEEN:
LDI QUEEN_TYPE ; $05
LBR UCI_PROMO_STORE
UCI_PROMO_ROOK:
LDI ROOK_TYPE ; $04
LBR UCI_PROMO_STORE
UCI_PROMO_BISHOP:
LDI BISHOP_TYPE ; $03
LBR UCI_PROMO_STORE
UCI_PROMO_KNIGHT:
LDI KNIGHT_TYPE ; $02
UCI_PROMO_STORE:
STR 8 ; UNDO_PROMOTION = piece type
INC 10 ; Advance past promotion char
UCI_PROMO_DONE:
; ---- Record move in MOVE_HIST for opening book ----
; Skip recording past book depth (prevents 8-bit GAME_PLY overflow)
RLDI 8, GAME_PLY
LDN 8 ; D = GAME_PLY
SMI BOOK_PLY_LIMIT ; past book depth?
LBDF UCI_HIST_DONE ; skip recording + increment
; Calculate offset: GAME_PLY * 2
LDN 8 ; reload GAME_PLY (SMI clobbered D)
SHL ; D = GAME_PLY * 2
PLO 9 ; R9.0 = offset
; R9 = MOVE_HIST + offset
LDI HIGH(MOVE_HIST)
PHI 9
LDI LOW(MOVE_HIST)
SEX 2
STR 2
GLO 9 ; D = offset
ADD ; D = LOW(MOVE_HIST) + offset
PLO 9
LDI HIGH(MOVE_HIST)
ADCI 0 ; Add carry to high byte
PHI 9
; Store from square
GHI 7 ; from square
STR 9
INC 9
; Store to square
GLO 7 ; to square
STR 9
; Increment GAME_PLY
LDN 8 ; R8 still points to GAME_PLY
ADI 1
STR 8
UCI_HIST_DONE:
; ---- End of MOVE_HIST recording ----
; Apply the move (save R10 - MAKE_MOVE clobbers it!)
GHI 10
STXD
GLO 10
STXD
CALL MAKE_MOVE
IRX
LDXA
PLO 10
LDX
PHI 10
; ---- Record hash for repetition detection ----
RLDI 13, HASH_HIST_COUNT
LDN 13 ; D = count
XRI 255 ; Zero if count == 255 (table full)
LBZ UCI_POS_MOVE_LOOP ; Full, skip recording
LDN 13 ; D = count (reload)
SHL ; D = (count*2) & $FF, DF = carry for counts >= 128
PLO 8 ; R8.0 = offset low byte
LDI HIGH(HASH_HIST) ; DF preserved (LDI doesn't affect DF)
ADCI 0 ; D = HIGH(HASH_HIST) + carry from SHL
PHI 8 ; R8 = &HASH_HIST[count] (correct for all counts)
RLDI 9, HASH_HI
LDA 9 ; D = HASH_HI
STR 8
INC 8
LDN 9 ; D = HASH_LO
STR 8
LDN 13 ; count (R13 still at HASH_HIST_COUNT)
ADI 1
STR 13 ; count++
LBR UCI_POS_MOVE_LOOP
UCI_POS_DONE:
RETN
; ------------------------------------------------------------------------------
; ALGEBRAIC_TO_SQUARE - Convert algebraic notation to 0x88 square
; ------------------------------------------------------------------------------
; Input: R10 = pointer to 2-char string (e.g., "e2"), advanced by 2
; Output: D = 0x88 square index, or $FF if invalid
; Uses: R7.0 for file temp
; ------------------------------------------------------------------------------
ALGEBRAIC_TO_SQUARE:
; Get file character (a-h)
LDA 10 ; Load file char, advance R10
SMI 'a' ; Convert to 0-7
LBNF ATS_INVALID ; If DF=0 (borrow), char < 'a', invalid
PLO 7 ; Save potential file in R7.0
SMI 8 ; Check if >= 8
LBDF ATS_INVALID ; If >= 8, invalid
; Get rank character (1-8)
LDA 10 ; Load rank char, advance R10
SMI '1' ; Convert to 0-7
LBNF ATS_INVALID ; If DF=0 (borrow), char < '1', invalid
SMI 8 ; Check if >= 8
LBDF ATS_INVALID ; If >= 8, invalid
ADI 8 ; Restore 0-7 value
; Calculate 0x88 square = rank * 16 + file
; rank is in D (0-7), file in R7.0
SHL ; D = rank * 2
SHL ; D = rank * 4
SHL ; D = rank * 8
SHL ; D = rank * 16
STR 2 ; Save rank*16 on stack
GLO 7 ; Get file (0-7)
ADD ; D = rank*16 + file
RETN
ATS_INVALID:
INC 10 ; Skip second char even on error (keep R10 consistent)
LDI $FF
RETN
UCI_CMD_GO:
; Parse go command
; Format: "go depth 6" or "go infinite", etc.
; Default to depth 1, parse "depth N" if present
; Store in memory (R5 is SRET in BIOS mode!)
; Default depth = 1
LDI 1
PLO 7 ; R7.0 = depth (default 1)
; Check for "depth " after "go "
; Buffer: "go depth 3" or "go" or "go infinite"
RLDI 10, UCI_BUFFER + 3
; Check if we have "depth"
LDN 10
XRI 'd'
LBNZ UCI_GO_SET_DEPTH ; Not "depth", use default
INC 10
LDN 10
XRI 'e'
LBNZ UCI_GO_SET_DEPTH
INC 10
LDN 10
XRI 'p'
LBNZ UCI_GO_SET_DEPTH
INC 10
LDN 10
XRI 't'
LBNZ UCI_GO_SET_DEPTH
INC 10
LDN 10
XRI 'h'
LBNZ UCI_GO_SET_DEPTH
INC 10
LDN 10
XRI ' '
LBNZ UCI_GO_SET_DEPTH
INC 10 ; R10 now at the number
; Parse the depth number (1-9, single digit only)
; Depths > 9 would take impractically long on the 1802 anyway
LDN 10
SMI '0' ; Convert ASCII to value
LBNF UCI_GO_SET_DEPTH ; Invalid (< '0'), use default
SMI 10 ; Check if >= 10
LBDF UCI_GO_SET_DEPTH ; >= 10, use default
ADI 10 ; Restore 0-9 value
LBZ UCI_GO_SET_DEPTH ; Depth 0 invalid, use default
PLO 7 ; R7.0 = parsed depth (1-9)
UCI_GO_SET_DEPTH:
; Set SEARCH_DEPTH from R7.0
RLDI 13, SEARCH_DEPTH
LDI 0
STR 13 ; SEARCH_DEPTH high = 0
INC 13
GLO 7
STR 13 ; SEARCH_DEPTH low = depth
; ---- Check opening book first ----
CALL BOOK_LOOKUP
LBZ UCI_GO_SEARCH ; D=0: no book hit, do normal search
; Book hit! Copy BOOK_MOVE to BEST_MOVE
RLDI 8, BOOK_MOVE_FROM
RLDI 9, BEST_MOVE
; Store book move in BEST_MOVE (from in high, to in low)
LDA 8 ; BOOK_MOVE_FROM
STR 9 ; BEST_MOVE high = from
INC 9
LDN 8 ; BOOK_MOVE_TO
STR 9 ; BEST_MOVE low = to
LBR UCI_GO_SEND_MOVE
UCI_GO_SEARCH:
; Clear stale promotion flag before search
; (opponent's promotion move leaves UNDO_PROMOTION set, which would
; corrupt every MAKE_MOVE during search)
RLDI 10, UNDO_PROMOTION
LDI 0
STR 10 ; UNDO_PROMOTION = 0
; Run search
CALL SEARCH_POSITION
UCI_GO_SEND_MOVE:
; Best move now at BEST_MOVE
; Send best move
RLDI 15, STR_BESTMOVE
SEP 4
DW F_MSG
; Convert best move to algebraic and send
; TODO: Load best move and convert
CALL UCI_SEND_BEST_MOVE
; Send CR+LF
LDI 13
CALL SERIAL_WRITE_CHAR
LDI 10
CALL SERIAL_WRITE_CHAR
RETN
UCI_CMD_QUIT:
; Return to monitor at $8000
LBR $8003
; ------------------------------------------------------------------------------
; UCI_CMD_UCINEWGAME - Prepare for a new game
; ------------------------------------------------------------------------------
; Clears transposition table and resets game state
; ------------------------------------------------------------------------------
UCI_CMD_UCINEWGAME:
; Clear workspace RAM to prevent stale variable bugs
CALL WORKSPACE_CLEAR
; Clear transposition table
CALL TT_CLEAR
; Reset game ply counter
RLDI 10, GAME_PLY
LDI 0
STR 10
; Clear move history
CALL INIT_MOVE_HISTORY
; Initialize board to starting position
CALL INIT_BOARD
RETN
; ------------------------------------------------------------------------------
; UCI_CMD_STOP - Stop calculating (no-op for now)
; ------------------------------------------------------------------------------
; On a single-threaded system we cannot interrupt search.
; GUI should wait for bestmove response.
; This is here for protocol compatibility.
; ------------------------------------------------------------------------------
UCI_CMD_STOP:
; No-op - search completes naturally
; GUI will receive bestmove when search finishes
RETN
; ------------------------------------------------------------------------------
; UCI_SEND_BEST_MOVE - Send best move in algebraic notation
; ------------------------------------------------------------------------------
UCI_SEND_BEST_MOVE:
; BEST_MOVE contains raw from/to squares (not encoded)
; BEST_MOVE[0] = from square (0x88), BEST_MOVE[1] = to square (0x88)
RLDI 10, BEST_MOVE
LDA 10 ; from square (raw 0x88)
PHI 13 ; R13.1 = from
LDN 10 ; to square (raw 0x88)
PLO 13 ; R13.0 = to
; Convert to algebraic notation (e.g., "e2e4")
; From square
GHI 13 ; From square
CALL SQUARE_TO_ALGEBRAIC
; Sends two characters (file, rank)
; To square
GLO 13 ; To square
CALL SQUARE_TO_ALGEBRAIC
; Check if this is a promotion move (pawn reaching last rank)
; Load piece at from square
LDI HIGH(BOARD)
PHI 10
GHI 13 ; from square
PLO 10
LDI LOW(BOARD)
STR 2
GLO 10
ADD
PLO 10
GHI 10
ADCI 0
PHI 10 ; R10 = &BOARD[from]
LDN 10 ; D = piece at from
ANI PIECE_MASK ; Get piece type
XRI PAWN_TYPE ; Is it a pawn?
LBNZ UCI_BM_NO_PROMO
; It's a pawn - check if to square is on last rank
GLO 13 ; to square
ANI $70 ; Isolate rank
LBZ UCI_BM_IS_PROMO ; Rank 0 = promotion for black
XRI $70
LBZ UCI_BM_IS_PROMO ; Rank 7 = promotion for white
LBR UCI_BM_NO_PROMO
UCI_BM_IS_PROMO:
; Append 'q' for queen promotion
LDI 'q'
CALL SERIAL_WRITE_CHAR
UCI_BM_NO_PROMO:
RETN
; ------------------------------------------------------------------------------
; SQUARE_TO_ALGEBRAIC - Convert 0x88 square to algebraic notation
; ------------------------------------------------------------------------------
; Input: D = square (0x88 format)
; Output: Two characters sent (file letter, rank number)
; ------------------------------------------------------------------------------
SQUARE_TO_ALGEBRAIC:
PLO 7 ; Save square in R7.0 (R13 used by caller!)
; Get file (0-7 → 'a'-'h')
ANI $07 ; File bits
ADI 'a' ; Convert to ASCII
CALL SERIAL_WRITE_CHAR
; Get rank (0-7 → '1'-'8')
GLO 7 ; Get square from R7.0
SHR
SHR
SHR
SHR ; Rank bits
ADI '1' ; Convert to ASCII
CALL SERIAL_WRITE_CHAR
RETN
; ------------------------------------------------------------------------------
; String Comparison Functions
; ------------------------------------------------------------------------------
; STRCMP - Compare two null-terminated strings
; Input: A = string 1, B = string 2
; Output: D = 0 if equal, non-zero if different
STRCMP:
LDN 10 ; D = char from string 1
STR 2 ; Save to stack
LDN 11 ; D = char from string 2
XOR ; D = char1 XOR char2 (0 if equal)
LBNZ STRCMP_DIFF ; If XOR != 0, characters differ
; Characters are equal, check if end of both strings
LDN 10
LBZ STRCMP_EQUAL ; If null terminator, both ended - equal
; Continue comparing
INC 10
INC 11
LBR STRCMP
STRCMP_EQUAL:
LDI 0
RETN
STRCMP_DIFF:
LDI 1
RETN
; STRNCMP - Compare until space or null in string 1
; Input: R10 = string 1 (command buffer), R11 = string 2 (command name)
; Output: D = 0 if equal, non-zero if different
STRNCMP:
; Check if string 1 hit space (end of command word)
LDN 10
XRI ' '
LBZ STRNCMP_CHECK_END
; Check if string 1 hit null
LDN 10
LBZ STRNCMP_CHECK_END
; Compare characters
LDN 10 ; D = char from string 1
STR 2 ; Save to stack
LDN 11 ; D = char from string 2
XOR ; D = char1 XOR char2 (0 if equal)
LBNZ STRNCMP_DIFF ; If different, not equal
INC 10
INC 11
LBR STRNCMP
STRNCMP_CHECK_END:
; String 1 ended (space or null) - check if string 2 also ended
LDN 11
LBZ STRNCMP_EQUAL ; String 2 is null - match!
LBR STRNCMP_DIFF ; String 2 has more chars - no match
STRNCMP_EQUAL:
LDI 0
RETN
STRNCMP_DIFF:
LDI 1
RETN
; ==============================================================================
; End of UCI Implementation
; ==============================================================================
; NOTE: SERIAL_READ_CHAR and SERIAL_WRITE_CHAR are provided by serial-io-9600.asm
; ==============================================================================