
   Accessing the screen under Linux

   When  it comes to writing on the screen, we aren't limited to writing
   where  the cursor currently is. You surely have seen a program, which
   despite  of  its  textual  interface, was able to write on the screen
   wherever it wanted. This is just the thing we're going to do now.
     ________________________________________________________________

   Using the terminal's control sequences

   (skip control sequences)
   Each terminal program has different control sequences and if you want
   to write programs which will work on every terminal, you should learn
   the  ncurses  library.  I'm  going  to  describe  only  a  few of the
   sequences of the standard terminal program, xterm.
   First  thing:  what exactly is a control character (control sequence)
   anyway?
   It  is  a  special sequence of characters describing the behaviour of
   the  terminal.  You  surely  know  at leas a few already: BEL (beep),
   CR/LF (noe line), TAB (the tabulation character). Now you're going to
   learn  two  more:  changing the text and background colour and moving
   the cursor to another location on the screen.
   I've used the file xterm_controls.txt.

   Colouring the text

   (skip text colouring)
   The control sequence responsible for changing the text and backgroung
   colours looks like this:
   ESC[(attr);(txt);(bg)m
   where:
     * ESC is the Escape code, 1Bh. If you wonder how to type this code,
       you need to press Ctrl+V followed by ESC.
     * (attr)  =  character attribute. One of the values below (as text,
       not binary):
          + 0 - bring back all attributes (go back to normal mode)
          + 1 - bright (usually turns on the bold font)
          + 2 - dark
          + 3 - underline
          + 5 - blink
          + 7 - colour reverse
          + 8 - hidden
     * (txt) = text colour:
          + 30 - black
          + 31 - red
          + 32 - green
          + 33 - yellow
          + 34 - blue
          + 35 - magenta
          + 36 - cyan
          + 37 - white
     * (bg) = background colour:
          + 40 - black
          + 41 - red
          + 42 - green
          + 43 - yellow
          + 44 - blue
          + 45 - magenta
          + 46 - cyan
          + 47 - white
     * m - literally: lower case m

   If,  for  example,  you want to write something in red and then bring
   back  the  original console settings, you need to normally (using int
   80h,  EAX=4,  EBX=1,  ECX=address,  EDX=length) display the following
   sequence:
   1bh, "[0;31;40m Your message", 1bh, "[0;37;40m".
   The  last sequence brings back the default terminal settings (grey on
   black).

   Changing the current cursor position

   (skip to he program)
   The  control  sequence  responsible for placing the cursor looks like
   this:
   ESC [ r ; c H
   where:
     * ESC is the Escape key code, 1Bh.
     * r = row number (if not given, 1 is assumed)
     * c = column number (if not given, 1 is assumed)
     * H - literally: upper case H

   If,  for example, you want to write something in the ninth row of the
   tenth  column,  you  should  normally  (using  int 80h, EAX=4, EBX=1,
   ECX=address, EDX=length) display the following sequence:
   1bh, "[10;10HMessage"
     ________________________________________________________________

   Here's the promised program for drawing frames:
   (skip the program)
; Drawing windows with frames around them
;
; Author: Bogdan D.
;
; nasm -O999 -o ramki.o -f elf ramki.asm
; ld -s -o ramki ramki.o

section .text
global _start


_start:
        mov     eax, 4
        mov     ebx, 1
        mov     ecx, czysc
        mov     edx, czysc_dl
        int     80h                     ; display screen clearing sequence

        mov     ax, (36<<8)+44          ; text and background colour:
                                        ; yellow on blue
        mov     bx, 1                   ; Upper-Left (U-L) column
        mov     cx, 1                   ; U-L row
        mov     si, 9                   ; Bottom-Right (B-R) column
        mov     bp, 9                   ; B-R row
        call    rysuj_okienko

        mov     ax, (37<<8)+40          ; white on black
        mov     bx, 10
        mov     cx, 10
        mov     si, 20
        mov     bp, 16
        call    rysuj_okienko

        mov     eax, 4
        mov     ebx, 1
        mov     ecx, nwln
        mov     edx, 1
        int     80h                     ; print a newline character

        mov     eax, 1
        xor     ebx, ebx
        int     80h                     ; exit the program


rysuj_okienko:

; input:
;
;  AH = character attribute (colour)
;  AL = background colour
;  BX = column of the U-L window corner
;  CX = row of the U-L window corner
;  SI = colmun of the B-R window corner
;  BP = row of the B-R window corner
;
; output:
;  nothing

; double ASCII frames
;r_p    equ     0bah                    ; right side
;r_pg   equ     0bbh                    ; top right (corner)
;r_pd   equ     0bch                    ; bottom right

;r_g    equ     0cdh                    ; top
;r_d    equ     r_g                     ; bottom

;r_l    equ     r_p                     ; left side
;r_lg   equ     0c9h                    ; left top
;r_ld   equ     0c8h                    ; left bottom


r_p     equ     "|"                     ; right side
r_pg    equ     "\"                     ; top right (corner)
r_pd    equ     "/"                     ; bottom right

r_g     equ     "="                     ; top
r_d     equ     r_g                     ; bottom

r_l     equ     r_p                     ; left side
r_lg    equ     "/"                     ; left top
r_ld    equ     "\"                     ; left bottom

spacja  equ     20h


        push    bx
        push    cx

        mov     dl, r_lg
        call    znak                    ; draw U-L corner

        push    bx
        mov     dl, r_g                 ; will draw the top edge

                                        ;while BX<SI, draw the top edge
.rysuj_gora:
        inc     bx
        cmp     bx, si
        je      .dalej
        call    znak
        jmp     short .rysuj_gora

.dalej:
        mov     dl, r_pg
        call    znak                    ; draw U-R corner
        pop     bx
        push    bx

                                        ; draw the middle
                                ;while CX<BP, draw the interior of the frame
.rysuj_srodek:
        inc     cx
        cmp     cx, bp
        je      .ostatni

        mov     dl, r_l
        call    znak                    ; start with left side

        push    bx
        mov     dl, spacja              ; spaces inside
.rysuj_srodek2:
        inc     bx
        cmp     bx, si          ; while BX<SI, draw the interior (spaces)
        je      .dalej2
        call    znak
        jmp     short .rysuj_srodek2

.dalej2:
        mov     dl, r_p
        call    znak                    ; draw right side
        pop     bx

        jmp     short .rysuj_srodek

.ostatni:
        mov     dl, r_ld
        call    znak                    ; draw B-L corner
        pop     bx

        mov     dl, r_d         ; will draw the bottom edge of the frame
.rysuj_dol:
        inc     bx
        cmp     bx, si                  ;while BX<SI, draw the bottom edge
        je      .dalej3
        call    znak
        jmp     short .rysuj_dol

.dalej3:
        mov     dl, r_pd
        call    znak                    ; draw B-R corner

        pop     cx
        pop     bx

        ret


znak:

;  AH = character attribute (colour)
;  AL = background colour
;  BX = character column
;  CX = character row
;  DL = the character to write

        push    eax
        push    ebx
        push    ecx
        push    edx

        push    ax
        mov     dh, 10
        shr     ax, 8                   ; AX = character colour
        div     dh                      ; AL = AL/10, AH = AL mod 10
        add     ax, "00"                ; add to the quotient and remainder
                                        ; the ASCII code of the '0' digit
        mov     [fg], ax                ; put the character colour
                                        ; number into [fg]

        pop     ax
        and     ax, 0FFh                ; AX = background colour
        div     dh                      ; divide by 10
        add     ax, "00"
        mov     [bg], ax

        mov     ax, bx                  ; AX = character column
        and     ax, 0FFh
        div     dh                      ; divide by 10
        add     ax, "00"
        mov     [kolumna], ax

        mov     ax, cx                  ; AX = character row
        and     ax, 0FFh
        div     dh                      ; divide by 10
        add     ax, "00"
        mov     [wiersz], ax

        mov     [znaczek], dl           ; save what character to display


        mov     eax, 4
        mov     ebx, 1
        mov     ecx, pozycja
        mov     edx, napis_dl
        int     80h                     ; print the string together with the
                                        ; positioning code

        pop     edx
        pop     ecx
        pop     ebx
        pop     eax

        ret


section .data

ESC             equ     1Bh

pozycja         db      ESC, "["        ; cursor positioning sequence
wiersz          db      "00;"
kolumna         db      "00H"
napis           db      ESC, "["        ; colour change sequence
atr             db      "0;"
fg              db      "00;"
bg              db      "00m"
znaczek         db      "x"             ; character to print
napis_dl        equ     $ - pozycja

czysc           db      ESC, "[2J"      ; screen clearing sequence
czysc_dl        equ     $ - czysc

nwln            db      10
     ________________________________________________________________

   Using the /dev/vcsaN character devices

   Another  way  to  move on the screen is to write to special character
   devices - the /dev/vcsaN files (root privileges may be required).
   The man vcsa page (the example program, strictly speaking) shows that
   the  contents  of  these  files  is  simple: there are 4 bytes at the
   beginning,  meaning  the  number  of  rows,  the  number  of  columns
   (different  resolutions  are  available)  and  the  X  and  Y  cursor
   coordinates.  After  these, the screen contents starts (starting with
   the  upper-left  corner)  -  characters  and  their  attributes.  The
   attribute bytes are the same as under DOS:
   3-0 - character colour (16 possibilities)
   6-4 - background colour (8 possibilities)
   7 - blinking (if not working, there are 16 background colours instead
   of 8)
   Here are the colours:
   Black  -  0,  blue - 1, green - 2, skyblue - 3, red - 4, magenta - 5,
   brown - 6, light grey (the standard) - 7, dark grey - 8, light blue -
   9,  light  green  -  10,  light  skyblue  - 11, light red - 12, light
   magenta - 13, yellow - 14, white - 15.
   Now  you  see there's nothing hard in it - all you have to do is open
   the  file,  read  the  screen size and write the correct bytes in the
   correct places (using the seeking function).
   Here's an example:
   (skip the vcsa example)
; Program for writing directly to the console file
;
; Author: Bogdan D., bogdandr // op .. pl
;
; assemble:
;
; nasm -O999 -f elf -o konsola.o konsola.asm
; ld -s -o konsola konsola.o


%idefine        sys_exit                1
%idefine        sys_read                3
%idefine        sys_write               4
%idefine        sys_open                5
%idefine        sys_close               6
%idefine        sys_lseek               19
%define         SEEK_SET                0
%define         O_RDWR                  02o

; position to display
%define         nasz_wiersz             10
%define         nasza_kolumna           10

section .text

global _start

_start:
        mov     eax, sys_open           ; open the file
        mov     ebx, plik               ; file name
        mov     ecx, O_RDWR             ; read and write
        mov     edx, 600q               ; read and write for the user
        int     80h                     ; open

        cmp     eax, 0
        jl      .koniec

        mov     ebx, eax                ; file handle

        mov     eax, sys_read           ; read the file (console
                                        ; attributes first)
        mov     ecx, konsola            ; where to read
        mov     edx, 4                  ; how many bytes to read
        int     80h


        mov     eax, sys_lseek          ; seek to the correct psition

        movzx   ecx, byte [l_kolumn]
        imul    ecx, nasz_wiersz
        add     ecx, nasza_kolumna      ;ECX=row*row length + column

        shl     ecx, 1                  ; ECX *= 2, because there are 2 bytes
                                        ; on the scrren for each character:
                                        ; the character and its attribute
        add     ecx, 4                  ; +4, because we're moving from
                                        ; the beginning of the file

        mov     edx, SEEK_SET           ; start at the beginning of the file
        int     80h

        mov     eax, sys_write          ; writing to the file
        mov     ecx, znak               ; what to write
        mov     edx, 2                  ; how many bytes to write
        int     80h

        mov     eax, sys_close          ; close the file
        int     80h

        xor     eax, eax                ; EAX = 0 = no errors

.koniec:
        mov     ebx, eax
        mov     eax, sys_exit
        int     80h                     ; exit with zero code or the error,
                                        ; from opening the file


section .data

plik    db      "/dev/vcsa1", 0 ; first text console file

                                        ; attributes of the console:
konsola:
l_wierszy       db      0
l_kolumn        db      0
kursor_x        db      0
kursor_y        db      0

                        ; character with the attribute we're going to display:
znak            db      "*"
atrybut         db      43h             ; skyblue on red

   On-line contents (Alt+2)
   Helpers for people with disabilities (Alt+0)
