flat assembler
Message board for the users of flat assembler.

Index > Assembly > New version of TetrOS

Author
Thread Post new topic Reply to topic
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 03 Nov 2019, 17:49
While preparing to organize a 512-byte coding contest for the 20th anniversary of fasm, I decided to revisit my own old piece. Obviously, as an organizer I'm not going to participate in the new contest myself, but perhaps an example from me could serve as an inspiration for potential contestants.

TetrOS is a boot sector game I wrote in 2004 for a 512-byte OS contest we had at the time. It did not attempt to be anything more than a game, but I thought that the fact that it is bootable (plus the pun in the name) should be enough for it to qualify. I did submit it in a bit of a hurry, though, and it was a bit unpolished - as I admitted in the comment to my submission.

It did not win any notable place in the actual contest, but is had been appreciated nonetheless. Some kind soul has even given it a page on MobyGames with nice little description and several screenshots.

It is worth noting that many of game design choices, like the width of well (12 columns instead of standard 10), were made to replicate the feel of my personal favorite, a Tetris built into DOS Navigator (which I used to play for hours to clear my mind while coding in the DOS days). The main reason why I defined all the pieces directly through data (instead of attempting to generate them with some trick) was that I wanted all of them to behave the same as I was used to. A feature that did not make the cut was the scoring system - Tetris in DOS Navigator gave you more points for clearing the lines higher in the well, and that encouraged more risky play patterns. I liked that approach, but then I decided to remove it from TetrOS before submission, because I thought that it might appear random at first and give impression of something that had to be done because of the space constraints (while it was in fact the opposite of that).

Now, as I went back to this old code and started cleaning it up, I found out that in many places it could be optimized better, some of the instructions were in fact completely redundant. I realized I may be able to reclaim enough space to fit some additional features. So I did my best and I managed to add what felt like the most missing one - a preview of the forthcoming block.

And here it comes, a shiny new version:
Code:
; TetrOS version 1.02
; by Tomasz Grysztar

; Requires VGA and 80386 CPU or higher.
; Version 1.01 was submitted in 2004 for a 512-byte OS contest.

; For your playing pleasure, it's a boot-sector Tetris game.
; Keys:
;   Left - move left
;   Right - move right
;   Up - rotate
;   Down - drop
;   Esc - new game at any time

format binary as 'img'
org 7C00h

ROWS = 23
COLUMNS = 12
DELAY = 4

virtual at 46Ch
  clock dw ?
end virtual

virtual at bp
  current dw ?
  current_column db ?
  current_row dw ?
  next dw ?
  score dw ?
  last_tick dw ?
  random dw ?
end virtual

label well at 9000h
label pics at well-2*64

        xor     ax,ax
        mov     sp,8000h
        mov     ss,ax
        mov     ds,ax
        mov     es,ax
        push    ax
        push    start
        retf

start:
        mov     bp,sp

        mov     al,13h
        int     10h

        mov     di,3*4
        mov     ax,int_3
        stosw
        xor     ax,ax
        stosw
        lea     di,[next]
        stosw   ; [next]
        stosw   ; [score]
        mov     ax,[clock]
        stosw   ; [last_tick]
        stosw   ; [random]

        mov     di,pics
        mov     cx,64
        mov     al,1    ; remove this instruction to get randomized background
        rep     stosb
        mov     ah,15
        mov     dx,7
      @@:
        mov     al,15
        stosb
        mov     al,ah
        mov     cl,6
        rep     stosb
        mov     ax,0708h
        stosb
        dec     dx
        jnz     @b
        mov     cl,8
        rep     stosb

        or      ax,-1
        stosw
        stosw
        stosw
        mov     cl,ROWS+4
        mov     ax,11b + (-1) shl (COLUMNS+2)
        rep     stosw
new_piece:
        mov     bx,[random]
        mov     ax,257
        mul     bx
        inc     ax
        mov     cx,43243
        div     cx
        mov     [random],dx
        and     bx,7
        jz      new_piece
        shl     bx,1
        mov     ax,[pieces+bx-2]
        xchg    ax,[next]
        or      ax,ax
        jz      new_piece
        lea     di,[current]
        stosw   ; [current]
        mov     al,COLUMNS/2
        stosb   ; [current_column]
        mov     ax,well + (3+ROWS-4)*2
        stosw   ; [current_row]
        mov     si,no_move
        call    first_move
        jz      update_screen
        inc     bp      ; game over

process_key:
        xor     ah,ah
        int     16h
        mov     al,ah
        dec     ah
        jz      start
        test    bp,bp
        jnp     process_key
        mov     si,rotate
        cmp     al,48h
        je      action
        mov     si,left
        cmp     al,4Bh
        je      action
        mov     si,right
        cmp     al,4Dh
        je      action
        cmp     al,50h
        jne     main_loop

drop_down:
        call    do_move_down
        jz      drop_down

action:
        call    do_move

update_screen:
        mov     bx,7
        mov     dx,12h
        mov     ah,2
        int     10h
        mov     cl,12
      print_score:
        mov     ax,[score]
        shr     ax,cl
        and     al,0Fh
        cmp     al,10
        sbb     al,69h
        das
        mov     ah,0Eh
        int     10h
        sub     cl,4
        jnc     print_score
        push    es
        push    0A000h
        pop     es
        mov     si,well+3*2
        mov     di,320*184+160-(COLUMNS*8)/2
      draw_well:
        lodsw
        push    si
        mov     dl,COLUMNS
        xchg    bx,ax
        shr     bx,2
        call    draw_row
        pop     si
        cmp     si,well+(3+ROWS)*2
        jb      draw_well
        mov     di,320*100+250
        mov     bx,[next]
      draw_preview:
        mov     dl,4
        call    draw_row
        cmp     di,320*68
        ja      draw_preview
        pop     es

main_loop:
        mov     ah,1
        int     16h
        jnz     process_key
        mov     ax,[clock]
        sub     ax,[last_tick]
        cmp     al,DELAY
        jb      main_loop
        add     [last_tick],ax
        call    do_move_down
        jz      update_screen
        mov     dx,1
        mov     si,well+3*2
        mov     di,si
      check_row:
        lodsw
        inc     ax
        jz      remove_row
        dec     ax
        stosw
        jmp     check_next_row
      remove_row:
        shl     dx,1
      check_next_row:
        cmp     di,well+(3+ROWS)*2
        jb      check_row
        add     [score],dx
        jmp     new_piece

draw_row:
        push    di
      blit_row:
        shr     bx,1
        mov     si,pics
        jnc     blit_block
        add     si,64
      blit_block:
        mov     cx,8
        rep     movsb
        add     di,320-8
        test    si,111111b
        jnz     blit_block
        sub     di,320*8-8
        dec     dx
        jnz     blit_row
        pop     di
        sub     di,320*8
        ret

do_move_down:
        mov     si,down
do_move:
        mov     ax,clear_piece
        int3
first_move:
        push    dword [current]
        call    si
        xor     ch,ch
        mov     ax,test_piece
        int3
        mov     al,draw_piece and 0FFh
        pop     edx
        or      ch,ch
        jz      @f
        mov     dword [current],edx
      @@:
        int3
      no_move:
        ret
down:
        sub     byte [current_row],2
        ret
left:
        dec     [current_column]
        ret
right:
        inc     [current_column]
        ret
rotate:
        mov     cx,3
     @@:
        bt      [current],cx
        rcl     dx,1
        add     cl,4
        cmp     cl,16
        jb      @b
        sub     cl,17
        jnc     @b
        mov     [current],dx
        ret

int_3:
        mov     di,[current_row]
        mov     bx,4
      on_piece_row:
        mov     dx,[current]
        mov     cl,bh
        shr     dx,cl
        and     dx,1111b
        mov     cl,[current_column]
        add     cl,4
        shl     edx,cl
        shr     edx,4
        call    ax
        add     bh,4
        scasw
        dec     bl
        jnz     on_piece_row
        iret

clear_piece:
        not     dx
        and     [di],dx
        ret
test_piece:
        test    [di],dx
        jz      @f
        inc     ch
     @@:
        ret
draw_piece:
        or      [di],dx
        ret

pieces dw 0010001000100010b
       dw 0010011000100000b
       dw 0010001001100000b
       dw 0100010001100000b
       dw 0000011001100000b
       dw 0100011000100000b
       dw 0010011001000000b

rb 7C00h+510-$
dw 0AA55h    
The code has been tested in DOSBox and on an actual hardware (my Pentium 60 machine from 1995).

You may notice a comment that encourages to remove one of the instructions - this change makes the background color be chosen randomly every time the game is restarted. It is fun, but also some of the color combinations look horrible (a perk of the randomness), so I decided to not enable this by default and keep the original look, even though this change also makes the code even smaller.

One of the tricks in TetrOS is to hook the interrupt 3 to make a function callable with just a single byte (INT3 instruction). Converting the function to an interrupt also makes it automatically preserve flags - which is actually relevant here and allows to save on PUSHF/POPF that would be otherwise needed. Nonetheless, in the new version the INT3 function is only called three times and the trick no longer gains anything. It breaks even, though, so I decided to keep it - as I consider it a kind of a trademark of TetrOS that I should not remove without a really good reason.

Because I deemed the addition of a piece preview the most important one, I did not manage to bring back the height-dependent scoring system this time. Perhaps this is something I may still work towards in the future.


Description: TetrOS 1.02 in action
Filesize: 1.97 KB
Viewed: 27256 Time(s)

TetrOS.png


Description: TetrOS 1.02 - source code and bootable image (please scroll down the thread to find later revisions)
Download
Filename: tetros102.zip
Filesize: 2.35 KB
Downloaded: 1225 Time(s)

Post 03 Nov 2019, 17:49
View user's profile Send private message Visit poster's website Reply with quote
Mike Gonta



Joined: 26 Dec 2010
Posts: 243
Mike Gonta 04 Nov 2019, 09:11
Tomasz Grysztar wrote:
While preparing to organize a 512-byte coding contest for the 20th anniversary of fasm, ...
It seems quite fitting (in an historical context) that such a contest (that is dependent on the BIOS)* coincides with Intel saying "Bye to BIOS" by 2020.


*Of course, there is always emulation, which is the sincerest form of flattery.

_________________
Mike Gonta
look and see - many look but few see

https://mikegonta.com
Post 04 Nov 2019, 09:11
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 04 Nov 2019, 11:49
Mike Gonta wrote:
It seems quite fitting (in an historical context) that such a contest (that is dependent on the BIOS)* coincides with Intel saying "Bye to BIOS" by 2020.
I did not plan to limit the contest this way (see the thread where we discuss potential rules), but perhaps this is a good reason to make it so.

Back to the new TetrOS: I made another kind of screenshot, this is my old DOS PC running it. Note that it's a variant that has instruction responsible for background color commented out to enable random coloring.


Description: TetrOS 1.02 (altered) on a CRT monitor
Filesize: 125.43 KB
Viewed: 27172 Time(s)

TetrOS_CRT.jpg


Post 04 Nov 2019, 11:49
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 04 Nov 2019, 12:59
Another previously overlooked (and substantial) optimization allowed me to implement a height-dependent scoring system that I wanted. Please welcome version 1.03:
Code:
; TetrOS version 1.03
; by Tomasz Grysztar

; Requires VGA and 80386 CPU or higher.
; Version 1.01 was submitted in 2004 for a 512-byte OS contest.

; For your playing pleasure, it's a boot-sector Tetris game.
; Keys:
;   Left - move left
;   Right - move right
;   Up - rotate
;   Down - drop
;   Esc - new game at any time

format binary as 'img'
org 7C00h

ROWS = 23
COLUMNS = 12
DELAY = 4

virtual at 46Ch
  clock dw ?
end virtual

virtual at bp
  current dw ?
  current_column db ?
  current_row dw ?
  next dw ?
  score dw ?
  last_tick dw ?
  random dw ?
end virtual

label well at 9000h
label pics at well-2*64

        xor     ax,ax
        mov     sp,8000h
        mov     ss,ax
        mov     ds,ax
        mov     es,ax
        push    ax
        push    start
        retf

start:
        mov     bp,sp

        mov     al,13h
        int     10h

        mov     di,3*4
        mov     ax,int_3
        stosw
        xor     ax,ax
        stosw
        lea     di,[next]
        stosw   ; [next]
        stosw   ; [score]
        mov     ax,[clock]
        stosw   ; [last_tick]
        stosw   ; [random]

        mov     di,pics
        mov     cx,64
        mov     al,1    ; remove this instruction to get randomized background
        rep     stosb
        mov     ah,15
        mov     dx,7
      @@:
        mov     al,15
        stosb
        mov     al,ah
        mov     cl,6
        rep     stosb
        mov     ax,0708h
        stosb
        dec     dx
        jnz     @b
        mov     cl,8
        rep     stosb

        or      ax,-1
        stosw
        stosw
        stosw
        mov     cl,ROWS+4
        mov     ax,not ( (1 shl COLUMNS - 1) shl ((16-COLUMNS)/2) )
        rep     stosw
new_piece:
        mov     bx,[random]
        mov     ax,257
        mul     bx
        inc     ax
        mov     cx,43243
        div     cx
        mov     [random],dx
        and     bx,7
        jz      new_piece
        shl     bx,1
        mov     ax,[pieces+bx-2]
        xchg    ax,[next]
        or      ax,ax
        jz      new_piece
        lea     di,[current]
        stosw   ; [current]
        mov     al,6
        stosb   ; [current_column]
        mov     ax,well + (3+ROWS-4)*2
        stosw   ; [current_row]
        mov     si,no_move
        call    first_move
        jz      update_screen
        inc     bp      ; game over

process_key:
        xor     ah,ah
        int     16h
        mov     al,ah
        dec     ah
        jz      start
        test    bp,bp
        jnp     process_key
        mov     si,rotate
        cmp     al,48h
        je      action
        mov     si,left
        cmp     al,4Bh
        je      action
        mov     si,right
        cmp     al,4Dh
        je      action
        cmp     al,50h
        jne     main_loop

drop_down:
        call    do_move_down
        jz      drop_down

action:
        call    do_move

update_screen:
        mov     bx,7
        mov     dx,12h
        mov     ah,2
        int     10h
        mov     cl,12
      print_score:
        mov     ax,[score]
        shr     ax,cl
        and     al,0Fh
        cmp     al,10
        sbb     al,69h
        das
        mov     ah,0Eh
        int     10h
        sub     cl,4
        jnc     print_score
        push    es
        push    0A000h
        pop     es
        mov     si,well+3*2
        mov     di,320*184+160-(COLUMNS*8)/2
      draw_well:
        lodsw
        push    si
        mov     dl,COLUMNS
        xchg    bx,ax
        shr     bx,(16-COLUMNS)/2
        call    draw_row
        pop     si
        cmp     si,well+(3+ROWS)*2
        jb      draw_well
        mov     di,320*100+250
        mov     bx,[next]
      draw_preview:
        mov     dl,4
        call    draw_row
        cmp     di,320*68
        ja      draw_preview
        pop     es

main_loop:
        mov     ah,1
        int     16h
        jnz     process_key
        mov     ax,[clock]
        sub     ax,[last_tick]
        cmp     al,DELAY
        jb      main_loop
        add     [last_tick],ax
        call    do_move_down
        jz      update_screen
        movzx   dx,byte [current_row]
        shr     dx,2
        mov     si,well+3*2
        mov     di,si
      check_row:
        lodsw
        inc     ax
        jz      remove_row
        dec     ax
        stosw
        jmp     check_next_row
      remove_row:
        shl     dx,1
      check_next_row:
        cmp     di,well+(3+ROWS)*2
        jb      check_row
        add     [score],dx
        jmp     new_piece

draw_row:
        push    di
      blit_row:
        shr     bx,1
        mov     si,pics
        jnc     blit_block
        add     si,64
      blit_block:
        mov     cx,8
        rep     movsb
        add     di,320-8
        test    si,111111b
        jnz     blit_block
        sub     di,320*8-8
        dec     dx
        jnz     blit_row
        pop     di
        sub     di,320*8
        ret

do_move_down:
        mov     si,down
do_move:
        mov     al,1
        int3
first_move:
        push    dword [current]
        call    si
        xor     ax,ax
        int3
        inc     ax
        pop     edx
        test    ah,ah
        jz      @f
        mov     dword [current],edx
      @@:
        int3
      no_move:
        ret
down:
        sub     byte [current_row],2
        ret
left:
        dec     [current_column]
        ret
right:
        inc     [current_column]
        ret
rotate:
        mov     cx,3
     @@:
        bt      [current],cx
        rcl     dx,1
        add     cl,4
        cmp     cl,16
        jb      @b
        sub     cl,17
        jnc     @b
        mov     [current],dx
        ret

int_3:
        mov     di,[current_row]
        mov     bx,4
      on_piece_row:
        mov     dx,[current]
        mov     cl,bh
        shr     dx,cl
        and     dx,1111b
        mov     cl,[current_column]
        add     cl,4
        shl     edx,cl
        shr     edx,4
        test    al,al
        jz      @f
        xor     [di],dx
      @@:
        test    [di],dx
        jz      @f
        inc     ah
      @@:
        add     bh,4
        scasw
        dec     bl
        jnz     on_piece_row
        iret

pieces dw 0010001000100010b
       dw 0010011000100000b
       dw 0010001001100000b
       dw 0100010001100000b
       dw 0000011001100000b
       dw 0100011000100000b
       dw 0010011001000000b

rb 7C00h+510-$
dw 0AA55h    
It still has 6 spare bytes. So to put them to good use, I tried one more thing: semi-levels marked by increasing falling speed. The increments happen every 100h points and every 400h revert back to the starting speed for a bit of respite. This time I enabled the background randomization:
Code:
; TetrOS version 1.04
; by Tomasz Grysztar

; Requires VGA and 80386 CPU or higher.
; Version 1.01 was submitted in 2004 for a 512-byte OS contest.

; For your playing pleasure, it's a boot-sector Tetris game.
; Keys:
;   Left - move left
;   Right - move right
;   Up - rotate
;   Down - drop
;   Esc - new game at any time

format binary as 'img'
org 7C00h

ROWS = 23
COLUMNS = 12
BACKGROUND = 0 ; background color, 0 for randomized

virtual at 46Ch
  clock dw ?
end virtual

virtual at bp
  current dw ?
  current_column db ?
  current_row dw ?
  next dw ?
  score dw ?
  last_tick dw ?
  random dw ?
end virtual

label well at 9000h
label pics at well-2*64

        xor     ax,ax
        mov     sp,8000h
        mov     ss,ax
        mov     ds,ax
        mov     es,ax
        push    ax
        push    start
        retf

start:
        mov     bp,sp

        mov     al,13h
        int     10h

        mov     di,3*4
        mov     ax,int_3
        stosw
        xor     ax,ax
        stosw
        lea     di,[next]
        stosw   ; [next]
        stosw   ; [score]
        mov     ax,[clock]
        stosw   ; [last_tick]
        stosw   ; [random]

        mov     di,pics
        mov     cx,64
if BACKGROUND
        mov     ax,0F00h + BACKGROUND
else
        mov     ah,15
end if
        rep     stosb
        mov     dx,7
      @@:
        mov     al,15
        stosb
        mov     al,ah
        mov     cl,6
        rep     stosb
        mov     ax,0708h
        stosb
        dec     dx
        jnz     @b
        mov     cl,8
        rep     stosb

        or      ax,-1
        stosw
        stosw
        stosw
        mov     cl,ROWS+4
        mov     ax,not ( (1 shl COLUMNS - 1) shl ((16-COLUMNS)/2) )
        rep     stosw
new_piece:
        mov     bx,[random]
        mov     ax,257
        mul     bx
        inc     ax
        mov     cx,43243
        div     cx
        mov     [random],dx
        and     bx,7
        jz      new_piece
        shl     bx,1
        mov     ax,[pieces+bx-2]
        xchg    ax,[next]
        or      ax,ax
        jz      new_piece
        lea     di,[current]
        stosw   ; [current]
        mov     al,6
        stosb   ; [current_column]
        mov     ax,well+(3+ROWS-4)*2
        stosw   ; [current_row]
        mov     si,no_move
        call    first_move
        jz      update_screen
        inc     bp      ; game over

process_key:
        xor     ah,ah
        int     16h
        mov     al,ah
        dec     ah
        jz      start
        test    bp,bp
        jnp     process_key
        mov     si,rotate
        cmp     al,48h
        je      action
        mov     si,left
        cmp     al,4Bh
        je      action
        mov     si,right
        cmp     al,4Dh
        je      action
        cmp     al,50h
        jne     main_loop

drop_down:
        call    do_move_down
        jz      drop_down

action:
        call    do_move

update_screen:
        mov     bx,7
        mov     dx,12h
        mov     ah,2
        int     10h
        mov     cl,12
      print_score:
        mov     ax,[score]
        shr     ax,cl
        and     al,0Fh
        cmp     al,10
        sbb     al,69h
        das
        mov     ah,0Eh
        int     10h
        sub     cl,4
        jnc     print_score
        push    es
        push    0A000h
        pop     es
        mov     si,well+3*2
        mov     di,320*184+160-(COLUMNS*8)/2
      draw_well:
        lodsw
        push    si
        mov     dl,COLUMNS
        xchg    bx,ax
        shr     bx,(16-COLUMNS)/2
        call    draw_row
        pop     si
        cmp     si,well+(3+ROWS)*2
        jb      draw_well
        mov     di,320*100+250
        mov     bx,[next]
      draw_preview:
        mov     dl,4
        call    draw_row
        cmp     di,320*68
        ja      draw_preview
        pop     es

main_loop:
        mov     ah,1
        int     16h
        jnz     process_key
        mov     al,-1
        xor     al,byte [score+1]
        and     al,11b
        mov     cx,[clock]
        sub     cx,[last_tick]
        cmp     cl,al
        jbe     main_loop
        add     [last_tick],cx
        call    do_move_down
        jz      update_screen
        movzx   dx,byte [current_row]
        shr     dx,2
        mov     si,well+3*2
        mov     di,si
      check_row:
        lodsw
        inc     ax
        jz      remove_row
        dec     ax
        stosw
        jmp     check_next_row
      remove_row:
        shl     dx,1
      check_next_row:
        cmp     di,well+(3+ROWS)*2
        jb      check_row
        add     [score],dx
        jmp     new_piece

draw_row:
        push    di
      blit_row:
        shr     bx,1
        mov     si,pics
        jnc     blit_block
        add     si,64
      blit_block:
        mov     cx,8
        rep     movsb
        add     di,320-8
        test    si,111111b
        jnz     blit_block
        sub     di,320*8-8
        dec     dx
        jnz     blit_row
        pop     di
        sub     di,320*8
        ret

do_move_down:
        mov     si,down
do_move:
        mov     al,1
        int3
first_move:
        push    dword [current]
        call    si
        xor     ax,ax
        int3
        inc     ax
        pop     edx
        test    ah,ah
        jz      @f
        mov     dword [current],edx
      @@:
        int3
      no_move:
        ret
down:
        sub     byte [current_row],2
        ret
left:
        dec     [current_column]
        ret
right:
        inc     [current_column]
        ret
rotate:
        mov     cx,3
     @@:
        bt      [current],cx
        rcl     dx,1
        add     cl,4
        cmp     cl,16
        jb      @b
        sub     cl,17
        jnc     @b
        mov     [current],dx
        ret

int_3:
        mov     di,[current_row]
        mov     bx,4
      on_piece_row:
        mov     dx,[current]
        mov     cl,bh
        shr     dx,cl
        and     dx,1111b
        mov     cl,[current_column]
        add     cl,4
        shl     edx,cl
        shr     edx,4
        test    al,al
        jz      @f
        xor     [di],dx
      @@:
        test    [di],dx
        jz      @f
        inc     ah
      @@:
        add     bh,4
        scasw
        dec     bl
        jnz     on_piece_row
        iret

pieces dw 0010001000100010b
       dw 0010011000100000b
       dw 0010001001100000b
       dw 0100010001100000b
       dw 0000011001100000b
       dw 0100011000100000b
       dw 0010011001000000b

rb 7C00h+510-$
dw 0AA55h    
This is the last revision for now, I promise. After playing it I think that it finally feels like a real game.


Description: TetrOS 1.04 - source code and bootable image (please scroll down the thread to find the final version)
Download
Filename: tetros104.zip
Filesize: 2.37 KB
Downloaded: 1086 Time(s)

Description: TetrOS 1.03 - source code and bootable image (please scroll down the thread to find the final version)
Download
Filename: tetros103.zip
Filesize: 2.34 KB
Downloaded: 1078 Time(s)



Last edited by Tomasz Grysztar on 05 Dec 2019, 15:51; edited 1 time in total
Post 04 Nov 2019, 12:59
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 17 Nov 2019, 13:31
I must say that I find 1.04, with its cycling speed levels and scoring system tied to height, surprisingly playable. I have been playing it much more than justifiable by testing purposes. Smile


Description: TetrOS 1.04 waiting for me to press Esc once more
Filesize: 3.11 KB
Viewed: 26495 Time(s)

TetrOS104.png


Post 17 Nov 2019, 13:31
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 23 Nov 2019, 14:05
By finally letting go of the INT3 trick I could strip off a couple more bytes. I have no good use for them, so this is still the same as 1.04 functionally. But this is yet another testament to how ridiculously under-optimized the initial version was.
Code:
; TetrOS version 1.04a
; by Tomasz Grysztar

; Requires VGA and 80386 CPU or higher.
; Version 1.01 was submitted in 2004 for a 512-byte OS contest.

; For your playing pleasure, it's a boot-sector Tetris game.
; Keys:
;   Left - move left
;   Right - move right
;   Up - rotate
;   Down - drop
;   Esc - new game at any time

format binary as 'img'
org 7C00h

ROWS = 23
COLUMNS = 12
BACKGROUND = 0 ; background color, 0 for randomized

virtual at 46Ch
  clock dw ?
end virtual

virtual at bp
  current dw ?
  current_column db ?
  current_row dw ?
  next dw ?
  score dw ?
  last_tick dw ?
  random dw ?
end virtual

label well at 9000h
label pics at well-2*64

        xor     ax,ax
        mov     sp,8000h
        mov     ss,ax
        mov     ds,ax
        mov     es,ax
        push    ax
        push    start
        retf

start:
        mov     bp,sp

        mov     al,13h
        int     10h

        xor     ax,ax
        lea     di,[next]
        stosw   ; [next]
        stosw   ; [score]
        mov     ax,[clock]
        stosw   ; [last_tick]
        stosw   ; [random]

        mov     di,pics
        mov     cx,64
if BACKGROUND
        mov     ax,0F00h + BACKGROUND
else
        mov     ah,15
end if
        rep     stosb
        mov     dx,7
      @@:
        mov     al,15
        stosb
        mov     al,ah
        mov     cl,6
        rep     stosb
        mov     ax,0708h
        stosb
        dec     dx
        jnz     @b
        mov     cl,8
        rep     stosb

        or      ax,-1
        stosw
        stosw
        stosw
        mov     cl,ROWS+4
        mov     ax,not ( (1 shl COLUMNS - 1) shl ((16-COLUMNS)/2) )
        rep     stosw
new_piece:
        mov     bx,[random]
        mov     ax,257
        mul     bx
        inc     ax
        mov     cx,43243
        div     cx
        mov     [random],dx
        and     bx,7
        jz      new_piece
        shl     bx,1
        mov     ax,[pieces+bx-2]
        xchg    ax,[next]
        or      ax,ax
        jz      new_piece
        lea     di,[current]
        stosw   ; [current]
        mov     al,6
        stosb   ; [current_column]
        mov     ax,well+(3+ROWS-4)*2
        stosw   ; [current_row]
        mov     si,no_move
        call    first_move
        jz      update_screen
        inc     bp      ; game over

process_key:
        xor     ah,ah
        int     16h
        mov     al,ah
        dec     ah
        jz      start
        test    bp,bp
        jnp     process_key
        mov     si,rotate
        cmp     al,48h
        je      action
        mov     si,left
        cmp     al,4Bh
        je      action
        mov     si,right
        cmp     al,4Dh
        je      action
        cmp     al,50h
        jne     main_loop

drop_down:
        call    do_move_down
        jz      drop_down

action:
        call    do_move

update_screen:
        mov     bx,7
        mov     dx,12h
        mov     ah,2
        int     10h
        mov     cl,12
      print_score:
        mov     ax,[score]
        shr     ax,cl
        and     al,0Fh
        cmp     al,10
        sbb     al,69h
        das
        mov     ah,0Eh
        int     10h
        sub     cl,4
        jnc     print_score
        push    es
        push    0A000h
        pop     es
        mov     si,well+3*2
        mov     di,320*184+160-(COLUMNS*8)/2
      draw_well:
        lodsw
        push    si
        mov     dl,COLUMNS
        xchg    bx,ax
        shr     bx,(16-COLUMNS)/2
        call    draw_row
        pop     si
        cmp     si,well+(3+ROWS)*2
        jb      draw_well
        mov     di,320*100+250
        mov     bx,[next]
      draw_preview:
        mov     dl,4
        call    draw_row
        cmp     di,320*68
        ja      draw_preview
        pop     es

main_loop:
        mov     ah,1
        int     16h
        jnz     process_key
        mov     al,-1
        xor     al,byte [score+1]
        and     al,11b
        mov     cx,[clock]
        sub     cx,[last_tick]
        cmp     cl,al
        jbe     main_loop
        add     [last_tick],cx
        call    do_move_down
        jz      update_screen
        movzx   dx,byte [current_row]
        shr     dx,2
        mov     si,well+3*2
        mov     di,si
      check_row:
        lodsw
        inc     ax
        jz      remove_row
        dec     ax
        stosw
        jmp     check_next_row
      remove_row:
        shl     dx,1
      check_next_row:
        cmp     di,well+(3+ROWS)*2
        jb      check_row
        add     [score],dx
        jmp     new_piece

draw_row:
        push    di
      blit_row:
        shr     bx,1
        mov     si,pics
        jnc     blit_block
        add     si,64
      blit_block:
        mov     cx,8
        rep     movsb
        add     di,320-8
        test    si,111111b
        jnz     blit_block
        sub     di,320*8-8
        dec     dx
        jnz     blit_row
        pop     di
        sub     di,320*8
        ret

do_move_down:
        mov     si,down
do_move:
        mov     al,1
        call    on_piece
first_move:
        push    dword [current]
        call    si
        xor     ax,ax
        call    on_piece
        inc     ax
        pop     edx
        test    ah,ah
        jz      @f
        mov     dword [current],edx
      @@:
        jmp     on_piece
down:
        sub     byte [current_row],2
        ret
left:
        dec     [current_column]
        ret
right:
        inc     [current_column]
no_move:
        ret
rotate:
        mov     cx,3
     @@:
        bt      [current],cx
        rcl     dx,1
        add     cl,4
        cmp     cl,16
        jb      @b
        sub     cl,17
        jnc     @b
        mov     [current],dx
        ret

on_piece:
        pushf
        mov     di,[current_row]
        mov     bx,4
      on_piece_row:
        mov     dx,[current]
        mov     cl,bh
        shr     dx,cl
        and     dx,1111b
        mov     cl,[current_column]
        add     cl,4
        shl     edx,cl
        shr     edx,4
        test    al,al
        jz      @f
        xor     [di],dx
      @@:
        test    [di],dx
        jz      @f
        inc     ah
      @@:
        add     bh,4
        scasw
        dec     bl
        jnz     on_piece_row
        popf
        ret

pieces dw 0010001000100010b
       dw 0010011000100000b
       dw 0010001001100000b
       dw 0100010001100000b
       dw 0000011001100000b
       dw 0100011000100000b
       dw 0010011001000000b

rb 7C00h+510-$
dw 0AA55h    


Last edited by Tomasz Grysztar on 05 Dec 2019, 15:51; edited 1 time in total
Post 23 Nov 2019, 14:05
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 23 Nov 2019, 18:09
I saved a couple more bytes by putting all the data next to each other and I came up with something to do with the remaining bytes. I've changed the color of the upper digits of the score - the ones that affect the speed of the game (note that speed reaches its maximum at 3 and then rolls over back to being slower). I'm not sure if I like the look of it, but still: this is yet another "feature" I managed to fit in there.
Code:
; TetrOS version 1.05
; by Tomasz Grysztar

; Requires VGA and 80386 CPU or higher.
; Version 1.01 was submitted in 2004 for a 512-byte OS contest.

; For your playing pleasure, it's a boot-sector Tetris game.
; Keys:
;   Left - move left
;   Right - move right
;   Up - rotate
;   Down - drop
;   Esc - new game at any time

format binary as 'img'
org 7C00h

ROWS = 23
COLUMNS = 12
BACKGROUND = 0 ; background color, 0 for randomized

virtual at 46Ch
  clock dw ?
end virtual

virtual at bp
  current dw ?
  current_column db ?
  current_row dw ?
  filler db ?
  next dw ?
  score dw ?
  last_tick dw ?
  random dw ?
  pics_wrt_bp:
end virtual

label well at 8000h
label pics at well-2*64
label origin at pics-(pics_wrt_bp-bp)

ORIGIN_PARITY = origin and 0FFh
while ORIGIN_PARITY and not 1
        ORIGIN_PARITY = (ORIGIN_PARITY shr 1) xor (ORIGIN_PARITY and 1)
end while

        xor     ax,ax
        mov     sp,origin
        mov     ss,ax
        mov     ds,ax
        mov     es,ax
        push    ax
        push    start
        retf

start:
        mov     al,13h
        int     10h

restart:
        mov     bp,sp

        xor     ax,ax
        lea     di,[next]
        stosw   ; [next]
        stosw   ; [score]
        mov     ax,[clock]
        stosw   ; [last_tick]
        stosw   ; [random]

        mov     cx,64
if BACKGROUND
        mov     ax,0F00h + BACKGROUND
else
        mov     ah,15
end if
        rep     stosb
        mov     dx,7
      @@:
        mov     al,15
        stosb
        mov     al,ah
        mov     cl,6
        rep     stosb
        mov     ax,0708h
        stosb
        dec     dx
        jnz     @b
        mov     cl,8
        rep     stosb

        or      ax,-1
        stosw
        stosw
        stosw
        mov     cl,ROWS+4
        mov     ax,not ( (1 shl COLUMNS - 1) shl ((16-COLUMNS)/2) )
        rep     stosw

new_piece:
        mov     bx,[random]
        mov     ax,257
        mul     bx
        inc     ax
        mov     cx,43243
        div     cx
        mov     [random],dx
        and     bx,7
        jz      new_piece
        shl     bx,1
        mov     ax,[pieces+bx-2]
        xchg    ax,[next]
        or      ax,ax
        jz      new_piece
        lea     di,[current]
        stosw   ; [current]
        mov     al,6
        stosb   ; [current_column]
        mov     ax,well+(3+ROWS-4)*2
        stosw   ; [current_row]
        mov     si,no_move
        call    first_move
        jz      update_screen
        inc     bp      ; game over

process_key:
        xor     ah,ah
        int     16h
        mov     al,ah
        dec     ah
        jz      restart
        test    bp,bp
if ORIGIN_PARITY
        jp      process_key
else
        jnp     process_key
end if
        mov     si,rotate
        cmp     al,48h
        je      action
        mov     si,left
        cmp     al,4Bh
        je      action
        mov     si,right
        cmp     al,4Dh
        je      action
        cmp     al,50h
        jne     main_loop

drop_down:
        call    do_move_down
        jz      drop_down

action:
        call    do_move

update_screen:
        mov     bx,15
        mov     dx,12h
        mov     ah,2
        int     10h
        mov     cl,84h
      print_score:
        mov     ax,[score]
        rol     ax,cl
        and     al,0Fh
        cmp     al,10
        sbb     al,69h
        das
        mov     ah,0Eh
        int     10h
        add     cl,84h
        jns     print_score
        xor     bl,15 xor 7
        jnp     print_score
        push    es
        push    0A000h
        pop     es
        mov     si,well+3*2
        mov     di,320*184+160-(COLUMNS*8)/2
      draw_well:
        lodsw
        push    si
        mov     dl,COLUMNS
        xchg    bx,ax
        shr     bx,(16-COLUMNS)/2
        call    draw_row
        pop     si
        cmp     si,well+(3+ROWS)*2
        jb      draw_well
        mov     di,320*100+250
        mov     bx,[next]
      draw_preview:
        mov     dl,4
        call    draw_row
        cmp     di,320*68
        ja      draw_preview
        pop     es

main_loop:
        mov     ah,1
        int     16h
        jnz     process_key
        mov     al,-1
        xor     al,byte [score+1]
        and     al,11b
        mov     cx,[clock]
        sub     cx,[last_tick]
        cmp     cl,al
        jbe     main_loop
        add     [last_tick],cx
        call    do_move_down
        jz      update_screen
        movzx   dx,byte [current_row]
        shr     dx,2
        mov     si,well+3*2
        mov     di,si
      check_row:
        lodsw
        inc     ax
        jz      remove_row
        dec     ax
        stosw
        jmp     check_next_row
      remove_row:
        shl     dx,1
      check_next_row:
        cmp     di,well+(3+ROWS)*2
        jb      check_row
        add     [score],dx
        jmp     new_piece

draw_row:
        push    di
      blit_row:
        shr     bx,1
        mov     si,pics
        jnc     blit_block
        add     si,64
      blit_block:
        mov     cx,8
        rep     movsb
        add     di,320-8
        test    si,111111b
        jnz     blit_block
        sub     di,320*8-8
        dec     dx
        jnz     blit_row
        pop     di
        sub     di,320*8
        ret

do_move_down:
        mov     si,down
do_move:
        mov     al,1
        call    on_piece
first_move:
        push    dword [current]
        call    si
        xor     ax,ax
        call    on_piece
        inc     ax
        pop     edx
        test    ah,ah
        jz      @f
        mov     dword [current],edx
      @@:
        jmp     on_piece
down:
        sub     byte [current_row],2
        ret
left:
        dec     [current_column]
        ret
right:
        inc     [current_column]
no_move:
        ret
rotate:
        mov     cx,3
     @@:
        bt      [current],cx
        rcl     dx,1
        add     cl,4
        cmp     cl,16
        jb      @b
        sub     cl,17
        jnc     @b
        mov     [current],dx
        ret

on_piece:
        pushf
        mov     di,[current_row]
        mov     bx,4
      on_piece_row:
        mov     dx,[current]
        mov     cl,bh
        shr     dx,cl
        and     dx,1111b
        mov     cl,[current_column]
        add     cl,4
        shl     edx,cl
        shr     edx,4
        test    al,al
        jz      @f
        xor     [di],dx
      @@:
        test    [di],dx
        jz      @f
        inc     ah
      @@:
        add     bh,4
        scasw
        dec     bl
        jnz     on_piece_row
        popf
        ret

pieces dw 0010001000100010b
       dw 0010011000100000b
       dw 0010001001100000b
       dw 0100010001100000b
       dw 0000011001100000b
       dw 0100011000100000b
       dw 0010011001000000b

rb 7C00h+510-$
dw 0AA55h    
I added computation of ORIGIN_PARITY constant to make it more obvious what that JNP instruction is about.


Description: TetrOS 1.05 in action
Filesize: 2.42 KB
Viewed: 26384 Time(s)

TetrOS105.png


Description: TetrOS 1.05 - source code and bootable image
Download
Filename: tetros105.zip
Filesize: 2.48 KB
Downloaded: 1097 Time(s)



Last edited by Tomasz Grysztar on 17 Dec 2019, 07:03; edited 2 times in total
Post 23 Nov 2019, 18:09
View user's profile Send private message Visit poster's website Reply with quote
edfed



Joined: 20 Feb 2006
Posts: 4353
Location: Now
edfed 25 Nov 2019, 09:16
funny trick to know how many bytes are available instead of rb 7C00h+510+$

Code:
repeat 7C00h+510-$
       display "0"
       db 0
end repeat
    


also, can save many more byte by using bytes to encode pieces.

but it needs to recode the piece computations also...
Code:
pieces:
        db 1111'0000b ;I
        db 1110'0100b ;T
        db 1110'0010b ;J
        db 1110'1000b ;L
        db 0110'0110b ;O
        db 0110'1100b ;S
        db 1100'0110b ;Z 
    


where and how?? there is a lot of code to modify i guess.
Code:
new_piece:
        mov     bx,[random]
        mov     ax,257
        mul     bx
        inc     ax
        mov     cx,43243
        div     cx
        mov     [random],dx
        and     bx,7
        jz      new_piece
;        shl     bx,1
        movzx   ax,byte[pieces+bx-1]
        ;shl     ax,4
        xchg    ax,[next]
        or      ax,ax
        jz      new_piece
        lea     di,[current]
        stosw   ; [current]
        mov     al,6
        stosb   ; [current_column]
        mov     ax,well+(3+ROWS-4)*2
        stosw   ; [current_row]
        mov     si,no_move
        call    first_move
        jz      update_screen
        inc     bp      ; game over    
Code:
pieces:
        db 1111'0000b ;I
        db 1110'0100b ;T
        db 1110'0010b ;J
        db 1110'1000b ;L
        db 0110'0110b ;O
        db 0110'1100b ;S
        db 1100'0110b ;Z

;       dw 0010'0010'0010'0010b
;       dw 0010'0110'0010'0000b
;       dw 0010'0010'0110'0000b
;       dw 0100'0100'0110'0000b
;       dw 0000'0110'0110'0000b
;       dw 0100'0110'0010'0000b
;       dw 0010'0110'0100'0000b

       n = 0
repeat 7C00h+510-$
       display n+30h
       n=n+1
       if n=10
       n=0
       display 13,10
       end if
       db 0
end repeat

dw 0AA55h    
Post 25 Nov 2019, 09:16
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 25 Nov 2019, 20:49
edfed wrote:
also, can save many more byte by using bytes to encode pieces.
This would only be 7 bytes gained, and I don't think 7 bytes would be enough for a code to convert these definitions into rotatable blocks.

However, you made me realize that I could skip an SHL instruction by making the piece selection look like this:
Code:
        and     bx,7 shl 1
        jz      new_piece
        mov     ax,[pieces+bx-2]    
This would gain 2 more bytes, but it would slightly affect randomization and I have no use for additional bytes anyway.
Post 25 Nov 2019, 20:49
View user's profile Send private message Visit poster's website Reply with quote
edfed



Joined: 20 Feb 2006
Posts: 4353
Location: Now
edfed 26 Nov 2019, 11:57
i've got 13 free bytes now Smile
enough to add the random rotation for new pieces. Wink

i think also, it can be possible to use the video segment for data and stack as there are 5536 bytes not used at the end of the video mem (to try)... it can save the push es, push 0a000h, pop es, pop es bytes Wink
Post 26 Nov 2019, 11:57
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 26 Nov 2019, 12:14
edfed wrote:
i think also, it can be possible to use the video segment for data and stack as there are 5536 bytes not used at the end of the video mem (to try)... it can save the push es, push 0a000h, pop es, pop es bytes Wink
The [clock] access is possible because the segment used is 0. While saving on having to set up segment for video access, you would have to do something similar for the clock access instead.
Post 26 Nov 2019, 12:14
View user's profile Send private message Visit poster's website Reply with quote
edfed



Joined: 20 Feb 2006
Posts: 4353
Location: Now
edfed 28 Nov 2019, 09:10
i find this tetros size optimisation very exiting.

i think we can fit tetros "as it is" in less than 450 bytes. that will let us add cool features.
Post 28 Nov 2019, 09:10
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 28 Nov 2019, 09:52
I already have the features and behavior that I wanted. See my remark in the initial post, that I intentionally disregarded some of the possible optimizations that would not allow to get exactly the playing experience that I prefer.

Anyway, it is up to you if you want to try optimizing it more. Perhaps you could even make a DN-like Pentix? But I should add, just in case: please do not make a TetrOS-based project your submission in the upcoming contest. Wink
Post 28 Nov 2019, 09:52
View user's profile Send private message Visit poster's website Reply with quote
edfed



Joined: 20 Feb 2006
Posts: 4353
Location: Now
edfed 28 Nov 2019, 09:57
of course not, i have another project for the contest Wink.

tetros optimising is just a game here Laughing

with setalc, i can save one byte per mov al,-1

but it needs carry flag set somewhere... then, hohoho, lets bend the code Wink
Post 28 Nov 2019, 09:57
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 05 Dec 2019, 16:07
While testing on a real hardware I noticed that I made a wrong assumption about INT 16h. I have therefore updated versions 1.04-1.05 above with a fix.
Post 05 Dec 2019, 16:07
View user's profile Send private message Visit poster's website Reply with quote
Flat12



Joined: 25 Jan 2013
Posts: 9
Flat12 06 Dec 2024, 13:03
Yea, I use TetrOS as ISA Option ROM and PCI Option ROM. Works on the AWARD Bios and on Coreboot+Seabios - tested on Abit BF6 motherboard.
Using this tutorial:
Building a "Kernel" in PCI Expansion ROM
I added ISA Option ROM support - isa_exp_rom_os.tar.gz:
https://board.flatassembler.net/topic.php?p=165451#165451

If no any drive (HDD, FDD or CD-ROM) is connected to the motherboard, TetrOS starts automatically Wink Wink
https://www.youtube.com/watch?v=NJLx2fUZc-c
Post 06 Dec 2024, 13:03
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.