Wild - Tetris in 256 bytes and snake in size 55 bytes

Code Density 
Alone Coder 

 As you remember, in Inferno #3 Shiru opu─
glare Snake length 255 bytes, and in Info
Guide #10 I published a simplified one Snake 
length  121  bytes. It was written in codes
calculator Basic 48 and seemed quite
dense.
   However, thenFirestarteroffered co─
completely new algorithmSnake.He didn’t write
to the editor, he wrote onHabr,and then here
I deleted the article. But save the source itself─
l.s.
   The game took93bytes, but it worked
yes. All the "rabbits" are scattered across the field
already at the beginning of the level and are stored directly─
especially in the screen attributes. Array with core─
There are no snake dinatas either, there is only go─
fishing that movesand a certain definition is drawn─
line attribute code meaning “adult─
"loyalty" of the snake. I reduce every game frame─
there are attribute codes throughout the playing field,
except for empty spaces and rabbits. It turns out
that behind the head there is a trace - a “tail”,
a collision with which ends in pla─
obviously. The more mature the snake, the longer it is
this tail. And if she grew up to a certain level─
level, corresponding to full
absence of rabbits on the field, then the game begins─
appears first (but there is no point in this).
   Thus, you only need to write ef─
efficient rabbit generation code, checks
cells in the position of the future head, decrement
colors while counting rabbits and
key polling.

   After  Firestarter  abandoned
the project disappeared, the source code was optimized
krt17. He got 85 bytes 
same behavior. But it was clear that85bytes -
not the limit. Moreover, it is known that
IBM PC Game
mat 64 byte, and Nibbles (snake that ra─
always walks, without rabbits or tail movement─
that) from the competitionHugi compo - total 48 bytes.
   After preliminary acquaintance with
version fromkrt17I won a few bytes
by changing the order of pieces of code. Then
took registers into account when calling from BASIC. But this
It was not the most important reduction. Op code─
the dew of the keys remained quite greasy (after─
lui, fatter than in mine Snake121 ). And I
stepped in the most radical way - keyboard─
Our controls are chosen in such a way that
translated into delta head movement
most briefly: sub (hl):ld c,a:sbc a,a: 
ld b,a. To do this, I had to choose enough─ 
but the original control keys are ","
left, "." right,  ENTER up, M down.
The result was worth it - 55 bytes,tremble,
pussies!
   At the last moment I discovered that I could─
but win a couple more bytes if you serve
collision with a tail that still exists─
it does, but is already visible as black. So in
in the source code you can switch two options - 
55 bytes and 53 bytes! 

;Alone Coder 53/55 bytes 11/10/2020 
;from basic: 
;de=0x5d83/5dcc, hl=0x2d2b, bc=start 
;from TR-DOS RUN "..." CODE: 
;de=0x5d83, hl=0x2d2b, bc=start 
l1 
ld d,0x5b
 if 1==0 ;55 bytes
ld a,(hl)
inc a
cp 9
 jr nc,restart ;collided with visible
 ;tail
 else ;53 bytes 
;a=0xff in most cases 
 add a,(hl)
 cp d ;0x5b
     jr c,restart ;collided with visible
                  ;or invisible tail
    endif
     ld a,h
     inc a
     and 00000011b ;screen margin
     jr nz,norestart 
restart 
;at restart: e=0xff, d=0x5b 
     ld h,d ;0x5b 
rabbit1 
     ld a,r
     ld (hl),e ;0xff
     cpl
     jr z,rabbit2
     inc (hl) ;0x00 
rabbit2 
     dec hl
     bit 3,h
     jr nz,rabbit1 
;l=0xff 
;bc=direction (delta) 
     inc h ;0x58 
norestart 
     ld (hl),0x2d ;0x21 ;head 
;, LEFT 
;. RIGHT 
;ENTER UP 
;M DOWN 
     ld a,(23560)
     sub (hl) ;0x2d
     ld c,a
     sbc a a
     ld b,a 
;bc=1/-1/32/-32 
     ;ld d,0x5a 
move1 
     ld a,(de)
     dec a
     cp 0xfe
     jr nc,move2 ;was 0xff = rabbit
                  ;or 0x00 = empty
     ld (de),a ;move snake
                ;(decrease colour) 
move2 
     jr nz,move3
     dec (hl) ;was 0xff = uneaten rabbit,
               ;so decrease snake size
               ;(head color) 
move3 
     dec de
     bit 6,d
     jr nz,move1 
;e=0xff 
;d=0x3f 
     add hl,bc
     jr l1

                 * * *

   Пожалуй, самым  коротким  Тетрисом  для
Спектрума долгое время был Impetris - пла─ 
гин для ACEdit. Занимал он 745 байт, кидал
цветные  фигуры, показывал следующую, счи─
тал  очки  (они  даже прыгали на экране) и
даже  ускорялся  по  мере прохождения. Что
касается  прыгающих очков и ускорения, это
было  довольно излишне в этом классе, да и
остальное  выглядит необязательным для ре─
корда. А  рекорд  на  IBM PC  кpresent
time dropped below256 bytes (without under─
points count).
   What can we do?
   I took as a basis notImpetris,but Tetris
from NedoOS.There was quite loose, but
very clear code, previously written in
two hours as a tutorial. I did the work
on the stream, or rather, on two streams - posko─
lku Tetris is really not bad for the game─
assembly language lessons.
   For example, what I did:
  - replaced the coordinates of the figure with the address.
 - combined drawing and erasing procedures 
and checks. 
  - combined the procedures  storeposition  and 
restoreposition. 
  -reduced key polling to a minimum.
 - optimized search and removal of complete ones 
lines (contrary to intuition,CPIRdid not help!) 
   I stopped streaming when I reached256
byte. But I couldn't decideShould I leave the walls?
(their absence led to looping
fields and the inability to show the score). But
then they suggested a simple procedure for me─
chat, and a few more came to mind
ideas, so the final version fits 
256  bytes along with display of the score, that is 
turns out better than the version forIBM PC!
 Please note: the game expects that
at the beginning of the printer buffer (0x5b00) lie
zeros, as required on a48K machine. Cro─
besides, again, as it should be, ek─
the wound is filled with the attribute0x38.Memory after
The program is also considered empty.
   The shapes take1bytes for each, except
the last one, and the first two are even used
as a parameterjp.
The walls are at a distance 16 know─
places apart from each other to draw them
one cycle, not two (the bottom is guaranteed
printer buffer).
   And you can even play it!

;Alone Coder 02.28.12.2020 
;48K or usr0 only! 
;(0x5b00..5b1f must be clean) 
SCORE=1 
WALLS=1 
    if WALLS 
fieldwid=16 
    else 
fieldwid=32 
    endif 
fieldhgt=24 
fieldx=16-fieldwid/2 
fieldy=0 
addrDropFig=0x580c 
emptyattr=0x38 

newfig=%0110011011000110 
    if WALLS 
;begin=0x6100-(fieldx-1)-16 
begin=newfig-13 
    else 
begin=newfig 
    endif 
display "begin=",begin,"=",/D,begin 

;hl=0x2d2b 
;de=0x5dxx 
;bc=begin 
;hx=0 
;iy=0x5c3a 
;bc'=0x1114 
;hl'=10072 
    if WALLS
     xor a
     ld hl,0x5800+(fieldx-1)
     ld (hl),a
     ld de,0x5800+(fieldx-1)+fieldwid
     ;ld b,2 ;bc,0x300-(fieldx-1)-32
     ld bc,0x300-(fieldx-1)-16
     ldir 
;newfig 
    endif
    if (WALLS == 0) && (SCORE == 1)
     ld (iy+86),0xff
    endif
     ld a,r
     and 7
     ld hl,figs
     add a,l
     ld l,a
     xor a
     ld de,curfig
     ld (de),a
     inc de
     ld b,3 ;-
      rld
      ld (de),a
      inc de ;e
     djnz $-4
     ld hl,addrDropFig
     ld (curxy),hl 
gameloop 
     ld a,prfig_pixel&0xff ;proc addr
     call prfig_curxy
     halt
     halt
     halt
     ld a,prfig_clearpixel&0xff ;procaddr
     call prfig_curxy 
;nz!!! 
     call storeposition_ldir ;bc=0
     ld hl,(curxy)
     ld (oldcurxy),hl

     ld hl,(curxy)
      xor a ;z!
     in a,(0xfe)
     rra ;'q'
     jr c,$+3
      dec l
     rra ;'w'
     jr c,$+3
      inc l
     ;rra ;'e'
     ;jr nc,godown ;bug with right+down!! 
nodown 
     jr nz,noautodown ;left or right
                     ;pressed - don't fall
     exx
     rlc b ;was 0x11, then random(?) with
            ;few bits on
     exx
     jr nc,noautodown 
godown 
     ld c,32 ;also flag for stopfig
     add hl,bc 
noautodown 
     ld (curxy),hl
     rra ;'e';'r'
     jr c,norotfig
     ld hl,curfig
     ld sp,hl
     pop bc
     pop de
     ld sp,hl 
rotfig0 
     xor a
     rr c
     rla
     rr b
     rla
     rr e
     rla
     rr d
     rla
     ld (hl),a
     inc l
     jr nz,rotfig0
     ld c,l ;0 ;or else rotation near a
                ;wall can stop fig!!! 
norotfig 
     ld lx,c ;32=stopfig for godown,
              ;0=no godown
     ld a,prfig_checkpixel&0xff ;procaddr
     call prfig_curxy ;z=collision
     call z,restoreposition ;can't move
                        ;in that direction
     jr nz,gameloop ;no collision 
;a=0 
     cp lx ;32=stopfig for godown,
            ;0=no godown
     jr z,gameloop ;collided not with gnd 
stopfig 
     ld hl,(oldcurxy)
     ld a,prfig_pixel&0xff ;proc addr
     call prfig 
;search a filled line from above 
;if found, shift down (with lddr)
     ;ld hl,0x5800+fieldx-1
     ;ld lx,fieldhgt
     ld hl,0x5a00+fieldx-1 ;only lowest 8
                       ;lines can be fired 
finddellines0 
     xor a ;for "or (hl)"
     ld b,fieldwid 
checkfilledline0 
     inc hl
     or (hl)
     djnz checkfilledline0
     jr nz,finddellines_nofire
     ;ld a,emptyattr
     ;ld bc,fieldwid
     ;cpir
     ;jr z,finddellines_nofire ;extrabyte 
;hl=last byte of current line 
     push hl
     ex de,hl
     ld hl,-32
     add hl,de
     ld a,h
     sub 0x58
     ld b,a
     ld c,l 
;bc=(y-1)*32 
;bc=hl-#5800=de-#5820 
     lddr
    if SCORE
     inc hx
     ld c,hx
     ld a,22
     rst 16 ;requires iy=23610
     xor a
     rst 16
     rst 16
     ;call 11563
     ;call 11747
     call 0x1a1b ;print bc
    endif
     pop hl 
finddellines_nofire 
     ;ld de,32-fieldwid
     ;add hl,de
     ;xor a ;for "or (hl)" in next line
     ;dec lx
     ;jr nz,finddellines0
     ld a,l
     add a,32-fieldwid
     ld l,a
     jr nc,finddellines0
    if SCORE
     ;ld b,hx ;lines fired (0..4)
     ;inc a ;ld c,1 ;if there are fired
            ;lines, c=0 (if there aren't,
            ;c<<256 = 0)
     ;add a,a
     ;djnz $-1 ;a=2,4,8,16
     ;exx
     ;add a,h
     ;ld h,a
     ;exx
     ;ld c,a
     ;ld a,22
     ;rst 16 ;requires iy=23610
     ;xor a
     ;rst 16
     ;rst 16
     ;call 11563 ;6683
     ;call 11747 ;-
    endif
    ;if WALLS
     ;jp newfig
     ;else
     db 0xc3
    ;endif 
figs 
     display "figs=",figs
     db %11000110 ;zigzag2
     db %01100110 ;square
     db %00110110 ;zigzag1
     db %11110000 ;I
     db %00101110 ;L
     db %01000111 ;J
     db %01001110 ;T
     db %01001110 ;T (8 figs) 
nfigs=($-figs) 
     display "nfigs=",nfigs

restoreposition ;z 
     ld hl,(oldcurxy)
     ld (curxy),hl 
storeposition_ldir 
restoreposition_ldir 
;nz=store 
;z=restore 
     ld bc,4
     ld de,curfig
     ld hl,oldcurfig
      jr z,$+3
      ex de,hl
     ldir 
prfig_clearpixel 
     ld (hl),emptyattr
     ret

prfig_pixel 
     ld (hl),0 
prfig_checkpixel 
     and (hl)
     ret

prfig_curxy 
     ld hl,(curxy) 
prfig 
;NC!!! 
     ld (prfig_calladdr),a
     or h 
;(a&8) != 0 
     ld de,curfig 
prfig_lines 
     ex de,hl
     ld b,(hl) ;%0000pppp
     set 4,b   ;%0001pppp
     ex de,hl 
prfig_pixels 
prfig_calladdr=$+1 
     call c,prfig_pixel
     inc hl ;x
     srl b ;pixel
     jr nz,prfig_pixels
     ld c,32-5
     add hl,bc
     inc e
     jr nz,prfig_lines
     or a
     ret 
end ;это конец файла игры! 

curxy 
     dw 0 
oldcurxy 
     dw 0

     align 256
     ds 256-4 
curfig 
     ds 4
     nop ;for spoiling 
oldcurfig 
     ds4

 * * *

   Many have seen the ray trace source on
BASIC, which counts two balls 8cha─
owls This source flashed on the Internet
in2018 and disappeared, but made me think─
Xia: what speed can you expect?
in assembler, and is it possible to fit the render into 
1 kilobyte (procedural graphics genre)? 
   To check, I did the following:
om:
  - first rewrote this render in C++
Builder and got the correct picture. 
 - then replaced the floats with 32-bit ones─ 
ly numbers. 
  -then rewrote the code for compatibility 
withNedoLang. 
  - moved the code to  NedoLang and added it to 
performance. 
  - procedures compiled by NedoLang 
into assembler, optimized one after another. 
  - determined the required ranges of numbers and ne─ 
I rewrote everything to 16-bit integers. 
  -only after that began to drive into the ki─ 
lobyte taking into account the compressor (Hrum turned out 
better than MegaLZ ) and add small op─ 
timization to be on the edge. Pos─ 
The latest optimization was on-the-fly calculation 
squares, beyond which you definitely will not go─ 
balls are moving. (I did not enter these times─ 
measures firmly and even made a version from anime─ 
tion.) 
   So, my result (demonstrated on
Cosmonautics Day in 2019 year): exactly 1024 
bytes, less than  71  seconds at the Pentagon.
Acceleration400times at the same resolution 
256x176,as in the original! 
   It’s interesting thatDaniel A.Nagyalso provo─
did the same experiment, but used
your library of 2-byte floats (lpfp).
Its version is packaged
about 4 kilobytes and renders the screen more than5
minutes, with the quality just a little bit better,
than in our 16-bit integers...
   I’ll save my version as a keepsake in the app─
marriage. The color version is in the repository
NedoOS. Maybe someone can speed it up 
or come up with a useful application for this
raytrace?

 * * *

   There were a few more interesting things on Hugi compo
resin size compo:
  -evaluating expressions with +-*/ , brackets 
and unary +- ( 117 bytes) 
  -gamePong ( 142 bytes)
 - game "tic-tac-toe" ( 213 bytes)
 - game Sokoban with a given map ( 189 
byte) 
  - interpreter brainfuck ( 98 bytes)
 - search for prime numbers up to a million ( 76 
byte) 
   There are also known records for the size of the shah─
matte programs (several less versions 
512  bytes, game qualitywhich are yet to come─ 
worth comparing) and in terms of the size of the hatch lo─
gotype Unix ( http://www.deater.net/weave/ 
vmwprod/asm/ll/ll.html  - however, the code for 
Z80 is written very poorly there). 
 Usually  x86  outperforms all others
processors. But does this mean that it is impossible
come up with a tighter command system?
Of course not.  There's quite a lot of bits in there
is spent on specifying register numbers,
addresses and word width. The byte size itself in 
8bit is also not necessarily ideal. Commands 
can be shorter, up to4 bits per station─
kov processor.
   I conducted such an experiment in a newspaper
ACNews #59;then I finished this engine, but 
never used it; then I tried to write
stack processor with 4-bit instructions and
forFPGA.
 My last thought on interpretability─
mu stack processorlooked like this (with─
absolutely not tested for functionality─
ness):

;for conditions, outputs and branches 3 commands 
; jrNC N, ifZ{scf} 
;for mathematics 4 commands 
; sub, add, sub1, inc(+rcf) 
;for stack 3 commands 
; push(dup), pop(skip), swap 
;for variables 4 commands: 
; getvar N, putvar N, peek8, poke8 
;3 commands to control 
; callr N, ret, z80 
(z80 allows you to further write machine code) 

;enter the script using rst #10, for this you need 
;patch the address in the channel descriptor #5cb6 
;(#5d26 in TR-DOS) 

;104b: 
_startscript 
 pophl ;skip #15fe (return from call
                                   ;#162c)
     pop hl ;restore hl'
     exx 
;de=вершина стека 
     pop hl ;address after RST #10 
_callq: 
     ld c,0x55 
_getcmd: 
     ld a,(hl)
     rlc c
     jr nc,$+2+5 ;первый раз не сдвигаем
      rrca
      rrca
      rrca
      rrca
      inc hl
     and 15
     ret z ;0=ret ;флаги сейчас в f',
         ;на выходе из call они достанутся
     ld b,a
    push hl ;return addr
     ld l,(hl) ;read 8bit
djnz _ncall 
;1 ;call N 
;lies at the end of the byte, so now 
;c=0x55, hl=next byte 
 call jphl ;jp (hl);call machine code
 ;procedure ;output flags in f' 
_popincq 
;flags are now in f' 
 pop hl ;return addr after call-ret
 inc hl ;skip 8bit
 jr _callq ;can be converted to
 ;push bc... pop bc (slower, but
 ;does ex af,af') 
_ncall: 
 ld h,vars/256
 djnz _nputvar 
;2 ; putvar N: (var)<=reg (do not delete it, 
;otherwise there's an extra pop) 
;lies at the end of the byte, so now 
;c=0x55, hl=next byte 
ld (hl),e
 inc hl
ld (hl),d
 jr _popincq ;can't throw away 
_nputvar: 
djnz _ngetvar 
;3 ; getvar N: reg<=(var) (overwriting the old one 
;TOS, otherwise there is an extra push - but without it you can 
;merge branches) 
;lies at the end of the byte, so now 
;c=0x55, hl=next byte 
ld e,(hl)
 inc hl
ld d,(hl)
jr _popincq 
_ngetvar: 
 pop hl
 ex af,af' ;flags
 djnz _njrnc 
;4 
 jr c,_getcmd
ld l,(hl) 
_njrnc: 
 djnz_npush 
;5 
     push de 
_npush: 
     djnz _npop 
;6 
     pop de 
_npop: 
     djnz _nswap 
;7 
     ex de,hl
     ex (sp),hl
     ex de,hl 
_nswap: 
     ex (sp),hl
     djnz _nsub 
;8 ;reg<=pop-reg-CY ;_oldminusnew 
     sbc hl,de 
_nsub: 
     djnz _nadd 
;9 ;reg<=reg+pop 
     add hl,de 
_nadd: 
     djnz _nsub1 
;A ;sub1 
     scf
     ld d,b ;0
     ld e,b ;0
     sbc hl,de 
_nsub1: 
     ex de,hl
    pop hl
     djnz _ninc 
;B ;inc(+rcf) 
     inc de
     or a 
_ninc: 
     djnz _npeek8 
;C ;peek8 ;можно сделать h<-l<-(hl)? 
     ld l,(hl)
     ld h,b ;0 
_npeek8: 
     djnz _npoke8 
;D ;poke8 
     ld a,l
     pop hl
     ld (hl),a 
_npoke8: 
     djnz _nifzscf 
;E ;ifZ{scf} 
     jr nz,_nifzscf
     scf 
_nifzscf: 
    ex af,af' ;flags
 djnz _getcmd 
;F ;z80 - no output to _getcmd 
;lies at the end of the byte, so now 
;c=0x55, hl=next byte 
jp (hl)

   We, of course, do not believe in fantastic
numbers like "The code for the M-machine was in three─ 
three times less code for PDP-11 and i8086 
two to three times less code for Motorola 
68000 and one and a half to two times less code for 
National Semiconductor 32000",but what the hell 
not kidding? After all, teams can even be
floating length. And if you sharpen it at all
processor for a specific task (for example─
measures, matrix calculations), then the program
for it may be half as much as for
regular CPU: https:// 
nabbla1.livejournal.com/234532.html 
   But the question is: if you didn’t depend on those─
current command systems and ready-made compilations─
ditch, and they designed it themselvesprocessor, then ka─
What command system would you suggest? Auto─
ryRISC-Vclaim that despite the request─
this command system, they have reached density
code is no worsex86.Is this really true?
le? Is it possible to somehow prove “almost-opti─
smallness" of the command system in size for
typical tasks? For example, what is more than 
10%  can no longer be reduced at the given limits─ 
tions (say, no more than256 commands)?
   Write your thoughts!

Share your thoughts about the article