
LeanChess: The smallest chess-playing program at 328 bytes - krilovsky
https://leanchess.github.io/
======
ksaj
This is really cool.

One trick I used to use to keep code small, was to take advantage of the fact
that CX always equals 00h upon exiting a loop (when there are no short-cutting
JMP related statements before it, of course).

Same goes for when you know what the registers will be set to after a
particular interrupt has been triggered, such as if it has counted characters
or loops or whatever. Not all of the return values are standardized, but a
good lot of them are.

I've noticed sometimes people preserve registers a lot more often than they
have to, like this:

    
    
        enter:
          push ax
          push bx
    
          mov  ax, ?  ; whatever value
          mov  bx, ?  ; whatever value
          int  21h    ; for example....
    
          pop  bx
          pop  ax
    
        exit:
          mov  ax, 0h
          mov  ax, bx
          ...
    

... to me this is really stupid code, but I see it a LOT in assembler.
Instead, they could do something tinier like this:

    
    
        enter:
          mov ax, ?  ; whatever value
          mov bx, ?  ; whatever value
          int 21h    ; for example....
    
        exit:
          ; now stuff the regs to what you
          ; want them to be, keeping in mind
          ; the known exit values from the
          ; interrupt
    

I used to have a lot of tricks, since programming on the 8086 pretty much
ensured you needed to know at least a few of them.

It's a rabbit hole, but a fun one to follow if you are trying to shrink your
code. And if AX is already zero, use INC AX instead of MOV. But those kinds of
things I'm sure you already do in your code. I haven't reviewed it all that
thoroughly.

~~~
leanchess
Thanks.

Indeed, the very first instruction is

    
    
      int 10h  ;BIOS display mode 0
    

that relies on AX being 0.

As for CX, there aren't that many loops in LeanChess (precisely 5, but who's
counting :)). However, CH being always 00h allows to save a couple more bytes
by doing

    
    
      mov cl, 8
    

instead of

    
    
      mov cx, 8

~~~
ksaj
Yup, I actually got to the end of reading the code and noticed you do things
like that. You have to read slowly to get all the nuances, so it was totally
worth the effort.

The weirdest DOS "shortcut" I ever did was to remap INT 21h onto INT 3, giving
me a single-byte INT 21h with a debug-killing side effect. Of course you have
to have enough INT 21h's to convert to make that worthwhile, but it is a neat
trick to have about if you have a lot of the same INT repeated throughout.

    
    
        sub     ax,ax
        mov     ds,ax
        mov     es,ax
        mov     si,21h*4
        mov     di,3*4
        movsw
        movsw
    

Of course if you aim it just right, you don't need the sub ax,ax. For that
matter, it's an ideal place to use cx if you just exited a loop. And even
then, you could probably find other short methods to make similar change. You
don't even need to cli/sti because the chances of an INT 3 happening in the
middle of the change is of no consequence, and all _your_ INT 3 calls will
only happen after this part runs.

