MS-DOS 512b Spiel: BootChess (2015)

Olivier Poudade (auch bekannt als Baud/Red Sector Inc.) hat uns bereits letzte Woche über „großes“ informiert und seit gestern ist die weltrekordverdächtige Katze auch effektiv aus dem Sack.

Viele von uns lieben das klassische Strategiespiel Schach. Neben der physischen Variante gibt es natürlich auch digitale Umsetzungen. Die Schach-Umsetzung mit der geringsten Speichergröße war bisher 1K ZX Chess von David Horne. Dieser Rekord wird nun von Olivier mit BootChess gebrochen. In nur unter 512 byte Größe setzt sein Spiel die meisten üblichen Schachregeln um und verfügt sogar über eine künstliche Intelligenz. Eine Augenweide ist BootChess nicht aber die Faszination „size coding“ dürfte mit dieser Veröffentlichung neue Dimensionen erreichen.

Und hier der komplette Quellcode:

;———-RED-SECTOR-INC.-proudly-presents-a-33-year-old-record-:———-
; ___ _
; / / _____ _ _ _____ _ _ ___ _
; .::. / / / / / / / / / /
; :::: / / ____ .-/ _ ___/-. .-/ _ ___/-. / /__
; :: / \ | | . | | | . | / /
; :: __ _ \ l | | | l | | / ___/
; .::. / / / / | l |_| l | |__/ / ____
; .::::. / __/ `–‚ `–‚ / |
; :::::::: / / |
; ___ __ Cl! ___ ___ / ___ _ _ __|
; ___ _ _ / __/_ __ _ _/ _/_ _ /_ / / ___ /__
; /_/ / / / / / / _____/ / / / __/ _ / / /
; .-/ ___/ / /______ / ___\ \___ / / __/
; | / / / | __/ ___ | \ | ___\ \___
; | / ____ | / | | _/ | | \ |
; | /–/ | ___/ | | | | | _/ |
; | | / / :: | ____/ :: | | :: \_____| | |
; |_____/ :: | __/ /_______| /_______| |_______\ | :: \_____|
; /_______| /___ _ /___ _ _ ___\ |_______\
; /___ _ _ ___\
; BootChess is the smallest computer implementation of chess on any platform
; Chess in a 512-byte x86 boot sector for Windows / Linux / OS X / DOS / BSD
; Coded by Olivier „Baudsurfer/RSi“ Poudade with extra help of Peter „QKumba“
; Ferrie. Logo by Frederic „Cleaner/Break“ Cambus. (c)2015 WTFPL v2 license.
; „Fasm BootChess.asm“ + „partcopy BootChess.bin 0 200 -f0“ = PC floppy boot
;-BootChess.asm——————-;—————————————–
x86 equ 1 ; x86=1 PC/emu vs. win32b/(DOS)Box
saf equ 0 ; saf=0 +queening -exotic failsafe
_b equ byte ; DEFAULT SETTINGS ABOVE ARE FOR A
_w equ word ; STANDARD PC BOOTSECTOR +QUEENING
_d equ dword ; x86=1 saf=0 512b inc. queening
_s equ short ; x86=1 saf=1 500b+ excl. queening
_n equ near ; x86=0 saf=1 506b inc. queening
_f equ far ; x86=0 saf=0 487b excl. queening
if x86 ; beg of boot vs .com preprocessing
org 7c00h ; std start of bootsector after post
if saf ; beg clear any start ambiguous segment
jmp _f 0:fix ; 7c0:0000 vs. 0:7c000 cs para fix-up
end if ; end clear any start ambiguous segment
fix:push cs ; if post int 19h isr bootsrap loader
pop ds ; left any bda or shadow segment values
push cs ; then enforce ds=cs=0
pop es ; then enforce es=ds=cs=0
mov aX,13h ; function set vga mode 320x200x256
else ; else if 16-bit binary assume ah=0
org 100h ; start of com binary program ip
mov aL,13h ; function set vga mode 320x200x256
end if ; end of boot vs .com preprocessing
int 10h ; standard bios video api
brd equ bf1+16 ; chess board at end of sector
mov di,brd ; set physical board index
mov bp,12 ; set 6×8+8 empty sqr mid board lines
call in2 ; pass#1 black „rnbqkbnr“ low-caps
push word opn ; pass#2 hi-caps whites & fall-through
rle:lodsb ; al=‘.’/al=null (fixed length rle)
mov cl,8 ; empty sqr mid board line length
rep stosb ; set one empty sqr mid board line
dec bp ; all empty sqr mid brd lines inited ?
jnz rle ; if not repeat init else bp=0 assumed
mov ah,’A‘-‚a‘ ; fall-through pass#2 white hi-caps
in2:mov si,br0 ; si points to endrank „rnbqkbnr“ str
if x86=0 ; if .com binary environment ch=0
mov cL,8 ; „rnbqkbnr“ endrank str length
else ; assume nothing although tempting
mov cX,8 ; „rnbqkbnr“ endrank str length
end if ; end of register ch startup value
in3:lodsb ; read physical board str car
add al,ah ; hi-caps rank 1 / low-caps rank 8
stosb ; write physical board str car
loop in3 ; all „rnbqkbnr“ str car written ?
mov cl,8 ; si-;equiv piece vals di-;0x88 brd
rep movsb ; write logical 0x88 board str vals
retn ; return to callers
ge0:mov bx,di ; physical board idx (bx=brd)
mov dh,’1′ ; beg white move src rank
ge1:mov dl,’h‘ ; beg white move src file
ge2:mov [si],dx ; beg white move src str
mov ch,’1′ ; beg white move dst rank
ge3:mov cl,’h‘ ; beg white move dst file
ge4:mov [si+2],cx ; beg white move dst str
pusha ; save all values
call idx ; passive chess coords to linear indexes
jbe mis ; white move src color not conforming
push bx ; save white move dst idx
call ver ; white move legal chess ?
pop bx ; restore white move dst idx
jc mis ; white move not legal chess
mov di,num+3 ; compare move destination rank in 7dfeh
inc si ; with move source rank in 7dfch
cmpsb ; is taxi distance to topmost bettered ?
jnc wor ; else not getting closer to black king
cmp _b [di],‘?‘ ; does any fallback move exist yet ?
jz lkj ; no, then last valid move good enough
wor:mov aL,_b[si+bx+brd-num-‚a’+6]; yes, previous valid legal exist so
dec aL ; only override if it’s a capture
js mis ; no, don’t want worse taxi distance
mov bx,fs ; it’s a capture with piece value=al
cmp bL,aL ; but hightest capture value yet ?
jnc mis ; no, less important opponent piece
max:mov fs,bx ; fs=best move yet in taxi half-ply
lkj:dec si ; realign source index
dec si ; to copy dword bst=dword idx
movsd ; after 4096 tries : move=dword bst
mis:popa ; restore all values
cmp cl,’a‘ ; end white move dst file ?
loopnz ge4 ; dec white move else next dst file
inc ch ; inc white move dst rank
cmp ch,’9′ ; end white move dst rank ?
jnz ge3 ; else next move dst rank
cpx:inc bx ; inc physical board index
dec dx ; dec white move src file
cmp dl,’`‘ ; end white move src file ?
jnz ge2 ; else next move src file
inc dh ; inc white move src rank
cmp dh,ch ; end white move src rank ? ch=9
jnz ge1 ; else next move src rank
push _d [si+4] ; get best white move found
pop _d [si] ; set it as final white move
val:mov cl,‘.‘ ; valid : empty sqr replaces src piece
call act ; active chess coords to linear indexes
xor bp,3 ; player turn and pawn unidir. delta
jz ge0 ; white turn to play (case best=0)
bla:mov al,‘?‘ ; input str clear pattern
mov di,si ; input str clear pattern (di=num)
mov cx,8 ; input str clear pattern
rep stosb ; input str clear pattern (di=brd)
call key ; get user keyboard input
jbe bla ; black move src color not conforming
opn:call ver ; di=brd, black move legal chess ?
jc bla ; white move not legal chess
jmp _s val ; validate black move
ver:call idx ; get lin indexes /w implicit passive
xchg bx,dx ; switch bx=dst idx dx=src idx
mov ah,[si+bx+brd-num-‚a’+8] ; get piece logical 0x88 brd val…
mov dh,bl ; dh=src idx dl=dst idx
sub dx,“aa“ ; get move file zero-based indexes
bsr bx,ax ; scan for 1st bit set (si=idx+10)
movsx bx,[si+bx-10-num+tab] ; bl=moved piece type idx (bh=0)
mov cx,_w [si+bx-num+tab] ; piece type deltas cl=repeats ch=num
sahf ; set piece logical 0x88 brd val
jnp sp1 ; branch if piece not pawn (bit#4!=1)
jc sp2 ; branch if pawn prev moved (bit#0=1)
sp1:jns sp3 ; branch if piece not king (bit#7!=1)
sp2:mov cl,1 ; override repeat if piece pawn or king
sp3:jnp sp4 ; branch if piece not pawn (bit#4!=1)
add bx,bp ; pawn player turn unidirection mutex
sp4:inc bx ; advance piece type struct field ptr
and ah,11111100b ; isolate piece bitmask only
vl1:push cx ; save piece type deltas
mov al,dh ; load start dst idx val
inc bx ; advance piece type struct field ptr
vl2:add al,[si+bx-num+tab] ; add this piece delta to dst idx val
xchg aL,bL ; base reg=dst idx val and preserved
mov ch,[si+bx+brd-num+8] ; read projected dst square val
xchg aL,bL ; base reg=piece type struct field ptr
cmp al,dl ; wanted move found (src+delta(s)=dst) ?
jnz dif ; different than requested move
sam:sahf ; get piece logical 0x88 brd val in flgs
jnp yes ; branch if piece is not pawn (bit#2=0)
test [si+bx-num+tab],1 ; pawn piece delta parity=diag vs. vert
jz ord ; branch if pawn piece moving vert
test ch,ch ; pawn piece vert move=;eating ?
jnz yes ; capturing verify dst sqr not empty
jmp _s bad ; else illegal chess move is a miss
ord:test ch,ch ; pawn piece vert move=;no eating ?
jz yes ; no eating=;empty dst sqr else illegal
dif:sahf ; store piece nature in flags register
jnp skp ; not pawn piece so skip direction test
test [si+bx-num+tab],1 ; pawn piece delta parity=diag vs. vert
jnz bad ; diagonal pawn move is illegal
skp:test ch,ch ; else skipping over dst square val ?
jnz bad ; projected dst sqr val is not empty
sahf ; get piece logical 0x88 brd val in flgs
jz x88 ; branch if piece is queen (bit#6=1)
jna bad ; branch if piece is not knight(bit#4=0)
x88:test al,88h ; ch=0 dst out of physical board limits?
loopz vl2 ; else cont if delta repeats remain
bad:pop cx ; restore piece type deltas
dec ch ; all possible delta nums verified ?
jnz vl1 ; if not then cont next delta type
nok:stc ; else return /w no match flg set
retn ; return to caller
yes:pop cx ; correct entry sp and disregard count
retn ; return to caller(s)
key:call prt ; refresh screen to account input echo
xor bx,bx ; bx=str idx=odd/even/alpha/num mutex
kbd:cbw ; fun blocking wait for keystroke (ah=0)
int 16h ; std bios keybd api (ah=scan al=ascii)
esc:dec ah ; was esc key pressed to quit ?
jnz car ; else default process key input
xit:if x86 ; if x86 boot context environment
int 19h ; exit through bootstrap to reboot cpu
else ; else if .com 16-bit binary
int 20h ; dos 1+ – terminate program
end if ; end of exit methods (os load or shell)
car:mov [bx+si],al ; sav ascii val to move string (si=num)
prt:pusha ; save game state snapshot
cwd ; curs location dx=(0,0)=(row,column)
mov ax,1301h ; function ega write str write mode 1
mov bl,7 ; page 0 grey car attrib matching tty
mov cl,8 ; src str lngth (curs updated horiz)
mov bp,bf1 ; es:bp is „abcdefgh“ ptr
lns:int 10h ; standard bios video api
add bp,16 ; bp=para step siz separating strings
push ax ; save old bios video api func params
mov ax,0e39h ; function teletype outp car=rank ‚9‘
sub al,dh ; decrement right handside rank value
int 10h ; standard bios video api
pop ax ; restore old bios video api fx params
cmp dh,cl ; src str total (curs updated vert)
inc dh ; preemptive off-by-one allows 9 verts
jc lns ; all 9 brd gui row strings printed ?
mov bp,si ; 10th row tail bp=move coords, cl=8
int 10h ; standard bios video api
popa ; restore game state snapshot
inc bx ; test if any more keys ?
cmp bl,4 ; frFR format input string
jc kbd ; else continue input
idx:loop idx ; ch=0 passive call load src/dst lin idx
act:mov si,num ; reinit si to point to coord input str.
mov bx,[si] ; bx=src coord (pass#1)
cbw ; empty sqr val in logical 0x88 board
call put ; place param passed as fun pass#1
mov dx,[si+2] ; bx=dst idx dx=src idx
xchg bx,dx ; fall-through for second pass
push word mat ; test for checkmate and conforming
put:xchg ax,bx ; bx|dx=[num+di]+16*((8-‚0′)-[num+di+1])
aad -10h ; shl ah,4/sub al,ah/xor ah,ah
add al,80h ; bx|dx=al-640%256-16*ah
xchg ax,bx ; bx|dx=al+128-16*ah
jcxz sim ; active call request or simulation ?
if saf=0 ; standard non-failsafe queening
cmp _b [si+3],’8′ ; validated dst rank is top-most ?
jz qq ; if so then promote pawn to queen
cmp _b [si+3],’1‘ ; validated dst rank is bottom-most ?
jnz prm ; if not no pawn queening promotion
qq: sahf ; store piece nature in flag register
jnp prm ; no pawn queening promotion
xor ah,01000110b ; transform p to promoted queen
inc cx ; queen promotion p2q or P2Q
end if ; end of conditional queening
prm:xchg ah,[si+bx+brd-num-‚a’+8] ; update piece logical 0x88 board val
xchg cl,[si+bx+brd-num-‚a‘] ; update piece physical board ascii val
or ah,1 ; update piece moved once (bit#0)
sim:retn ; return to caller(s)
mat:sahf ; catured piece king and mate ?
js xit ; if piece is king then game is over
call chk ; move src color conforming ?
jnz nok ; move src color not conforming
chk:xchg bx,dx ; src idx <- dst idx
mov al,[si+bx+brd-num-'a'] ; pass#1:src idx pass#2:dst idx di=brd
xor _b [si+len-num],8 ; self-modif 8/26 val=[1;8]/[a(A);z(Z)]
mov cl,-'a' ; assert black piece car interval
test bp,bp ; test whose turn it is to play
jnz lim ; assert white piece car interval
mov cl,-'A' ; al=ascii value cl=-(lower boundery)
lim:xadd al,cl ; tmp=al+cl cl=al al=tmp +fall-trough
db 0d4h ; aam
len:db 12h ; ah=al/8 al%=8
mov al,cl ; al=restored ascii value
test ah,ah ; set/clear zf=0 success zf=1 failure
retn ; return to caller(s) nb: destroys ah
tab db p-tab,r-tab,n-tab,b-tab ; piece type mov offset array
db q-tab,q-tab ; note original 1K ZX Chess q=k trick
br0 db „rnbqkbnr“,8,16,32,64,128 ; end rank pattern + beg piece values
db 32,16,8,’p‘,4,‘.‘,0,‘.‘,0 ; end piece values + beg mid board reps
db ‚.‘,0,‘.‘,0,’P‘,4 ; … end mid board reps
p db 2,3,-10h,-15,-17,10h,15 ; bit#2 pf=04 p[6]=r[0] overlay
r db 17,4,10h,-1h,-10h ; bit#3 ??=08 r[5]=n[0] overlay
n db 1,8,1fh,21h,12h,-0eh,-1fh ; bit#4 af=16 n[9]=b[0] overlay
db -21h,-12h ; … end of knight moves list
b db 0eh,4,-0fh,11h,-11h ; bit#5 ??=32 b[5]=q[0] overlay
q db 0fh,8,10h,11h,0fh,1h,-10h ; bit#6 zf=64 k=q except k[0]=1
db -11h,-0fh,-1h ; … end of queen/king moves list
bf1 db „abcdefgh“ ; gui file index string
num db „e2e4“ ; hardcoded Ruy Lopez opening
if saf and x86 ; x86 failsafe exotic boot environment
times 510-($-$$) db 0 ; nul padding if necessary
org 7df0h ; boot signature vbr/mbr standard offset
sig db 55h,0aah ; magic number no-endian boot signature
end if ; end of conditional failsafe signature

Shahzad Sahaib

Über Shahzad Sahaib

Sein Erstcomputer war ein Amiga 500 von Commodore. Besitzt aber noch zahlreiche weitere Retrorechner, Konsolen und Exotenhardware. Shahzad ist unter anderem Gründer der auf Handheld- und Konsolenhomebrew spezialisierten Seite PDRoms. Talentscout und Organizer in der Demoszene sowie Mitglied der Demogruppe Speckdrumm. Als Gründungsmitglied und Projektleiter der Spieleschmiede Retroguru unterstützt er aktiv die Produktion von Spielen auf alter Hardware mit weiteren kreativen Experten aus einschlägigem Kreise.