;-----------------------------------------------------------------------
   ;   Copyright (C) 2007 Jeff Owens
   ;
   ;   This program is free software: you can redistribute it and/or modify
   ;   it under the terms of the GNU General Public License as published by
   ;   the Free Software Foundation, either version 3 of the License, or
   ;   (at your option) any later version.
   ;
   ;   This program is distributed in the hope that it will be useful,
   ;   but WITHOUT ANY WARRANTY; without even the implied warranty of
   ;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   ;   GNU General Public License for more details.
   ;
   ;   You should have received a copy of the GNU General Public License
   ;   along with this program.  If not, see <http://www.gnu.org/licenses/>.
   
   
   global _start
   _start:
     call       env_stack
     call       x_list_extension
     call       show_extensions
       
     mov        eax,01
     int        byte 80h
   
   ;-----------
     [section .data]
   crlf:        db 0ah
     [section .text]
   
   
   show_extensions:
     lea        esi,[ecx+32]
     xor        eax,eax
     mov        al,[ecx+1]      ;get number of items returned
     mov        ebp,eax         ;save count
   show_loop:
     call       line_feed
     lea        ecx,[esi+1]
     xor        edx,edx
     mov        dl,[esi]        ;get length of name
     add        esi,edx         ;move to next name
     inc        esi
     call       crt_write
   
     dec        ebp
     jnz        show_loop
     call       line_feed
     ret
   
   line_feed:
     mov        ecx,crlf
     mov        edx,1
     call       crt_write
     ret
   ;---------- x_list_extension ------------------
   ;  x_list_extension - get list of extensions
   ; INPUTS
   ;    none
   ; OUTPUT:
   ;    failure - eax = negative error code
   ;              flags set for "js"
   ;    success - eax positive read length and flag set "jns"
   ;              ecx = buffer ptr with
   ;  resb 1  ;1 Reply
   ;  resb 1  ;number of names returned
   ;  resb 2  ;sequence number
   ;  resb 4  ;reply length
   ;  resb 24 ;unused
   ;  resb 1  ;length of extension n
   ;  resb x  ;extension n string
   
   ;  resb 1  ;length of extension n+1
   ;  resb x  ;extension n+1 string
   ; * ----------------------------------------------
   
   x_list_extension:
     mov        ecx,list_extension_request
     mov        edx,(qer_end - list_extension_request)
     neg        edx             ;indicate reply expected
     call       x_send_request
     js ger_exit
     call       x_wait_reply
   ger_exit:
     ret
   
   
     [section .data]
   list_extension_request:
    db 99       ;opcode
    db 0        ;unused
    dw 1        ;request length in dwords
   qer_end:
   
     [section .text]
   
   
   ;--------------------------------------------------
   ;  env_stack - find stack ptrs to environment
   ; INPUTS
   ;    esp = stack ptr before any pops or pushes
   ; OUTPUT
   ;    ebp = ptr to environment pointers
   ;    [enviro_ptrs] set also
   ;  * ----------------------------------------------
   env_stack:
     cld
     mov        esi,esp
   es_lp:
     lodsd
     or eax,eax
     jnz        es_lp           ;loop till start of env ptrs
     mov        ebp,esi
     mov        [enviro_ptrs],esi
     ret
   ;--------------------------------------------------------
   
   struc connect_reply
   .reply_code  resb 1
                resb 1  ;unused
   .proto_major resw 1
   .proto_minor resw 1
   .append_len  resw 1  ;dword len
   .release_num resd 1
   .id_base     resd 1
   .id_mask     resd 1
   .motion_buf_len resd 1
   .vendor_len  resw 1
   .max_req_size        resw 1
   .screen_cnt  resb 1  ;number of screen struc's at end
   .format_cnt  resb 1  ;number of format struc's at end
   .img_byte_ordr       resb 1  ;image byte order 0=lsb 1=msb
   .map_byte_ordr       resb 1  ;bitmap byte order 0=least sig first
   .scan_unit   resb 1
   .scan_pad    resb 1
   .min_keycode resb 1
   .max_keycode resb 1
                resd 1  ;unused
   .vendor              resb 8  ;string here
   .pad         resb 12 ;?
   .formats:            ;format strucs start here, followed by screen strucs
   connect_reply_len:
   endstruc
   
   struc format
   .depth               resb 1
   .bytes_per_pix       resb 1
   .scanline_pad        resb 1
                resb 5  ;unused
   format_len:
   endstruc
   
   struc screen
   .root_win    resd 1
   .color_map   resd 1
   .white_pixel resd 1
   .black_pixel resd 1
   .event_mask  resd 1
   .pix_width   resw 1
   .pix_height  resw 1
   .width_mil   resw 1
   .height_mil  resw 1
   .min_maps    resw 1
   .max_maps    resw 1
   .root_visual resd 1
   .backing     resb 1 ;0=never 1=when mapped 2=always
   .save_under  resb 1 ;bool
   .root_depth  resb 1
   .depth_cnt   resb 1 ;number of depths that follow
   ;more data here
   endstruc
   
   ;---------------------
   ;  x_connect - connect to x server
   ; INPUTS
   ;    env_stack library function must be called before
   ;              using x_connect
   ; OUTPUT:
   ;    flag set (jns) if success
   ;      and [socket_fd] global set to socket fd (dword)
   ;          [x_id_base] base for id assign (dword)
   ;          [root_win_id] set (dword)
   ;          [root_win_pix_width] set (word)
   ;          [root_win_pix_height] set (word)
   ;          [root_win_color_map] set (dword)
   ;          lib_buf has connection reply
   ;          connection_reply_length = size of reply
   ;    flag set (js) if err, eax=error code
   ;    ecx points to connection table as follows:
   ;         c_reply_code       db 0
   ;                    db 0    ;unused
   ;         c_proto_major      dw 0
   ;         c_proto_minor      dw 0
   ;         c_append_len       dw 0    ;dword len
   ;         c_release_num      dd 0
   ;         x_id_base  dd 0
   ;         c_id_mask  dd 0
   ;         c_motion_buf_len dd 0
   ;         c_vendor_len       dw 0
   ;         c_max_req_size     dw 0
   ;         c_screen_cnt       db 0    ;number of screen struc's at end
   ;         c_format_cnt       db 0  ;number of format struc's at end
   ;         c_img_byte_ordr    db 0    ;image byte order 0=lsb 1=msb
   ;         c_map_byte_ordr    db 0    ;bitmap byte order 0=least sig first
   ;         c_scan_unit        db 0
   ;         c_scan_pad db 0
   ;         c_min_keycode      db 0
   ;         c_max_keycode      db 0
   ;
   ;         c_depth            db 0
   ;         c_bytes_per_pix    db 0
   ;         c_scanline_pad     db 0
   ;                               db 0 ;pad
   ;         root_win_id        dd 0
   ;         root_win_color_map dd 0
   ;         c_white_pixel      dd 0
   ;         c_black_pixel      dd 0
   ;         c_event_mask       dd 0
   ;         root_win_pix_width dw 0
   ;         root_win_pix_height        dw 0
   ;         c_width_mil        dw 0
   ;         c_height_mil       dw 0
   ;         c_min_maps dw 0
   ;         c_max_maps dw 0
   ;         c_root_visual      dd 0
   ;         c_backing  db 0 ;0=never 1=when mapped 2=always
   ;         c_save_under       db 0 ;bool
   ;         c_root_depth       db 0
   ;         c_depth_cnt        db 0 ;number of depths that follow
   ; * ----------------------------------------------
   
   x_connect:
     xor        eax,eax
     cmp        dword [socket_fd],eax
     jne        err             ;exit if already connected
   ;check if environment variable DISPLAY=:x set
     mov        ecx,display_var
     mov        edx,display_var_contents
     call       find_env_variable
     mov        al,[display_var_contents+1]
     or al,al
     jz x_conn_strt     ;jmp if no display variable
     mov        [display_number],al   
   x_conn_strt:
     call       get_authorization ;get server info
     js err             ;exit if error
     call       connect         ;connect to socket
     js err             ;exit if error
     mov        [connection_reply_length],eax
     mov   esi, lib_buf
     cmp        byte [esi],1
     je x_conn_ok
     mov        eax,-1
     jmp        short err
   x_conn_ok:
   ;save data from connnecton reply
     mov        edi,c_reply_code
     mov        esi,lib_buf
     mov        ecx,(c_max_keycode+1) - c_reply_code
     rep        movsb
   
     mov        esi,lib_buf+connect_reply_len
     mov        ecx,4
     rep        movsb
   
   ;compute index to first screen struc
     xor        eax,eax
     mov        ax,[lib_buf+connect_reply.format_cnt]
     shl        eax,3           ;multiply by 8
     add        eax,connect_reply_len ;move to start of screen struc
     add        eax,lib_buf     ;add in buffer start
     mov        esi,eax
     mov        ecx,36
     rep        movsb
     mov        ecx,c_reply_code
   err:
     ret
   ;--------------
     [section .data]
   connection_reply_length: dd 0
   socket_fd:
   xfd_array: dd 0,-1
   
   
   c_reply_code db 0
                db 0    ;unused
   c_proto_major        dw 0
   c_proto_minor        dw 0
   c_append_len dw 0    ;dword len
   c_release_num        dd 0
   x_id_base    dd 0
   c_id_mask    dd 0
   c_motion_buf_len dd 0
   c_vendor_len dw 0
   c_max_req_size       dw 0
   c_screen_cnt db 0    ;number of screen struc's at end
   c_format_cnt db 0  ;number of format struc's at end
   c_img_byte_ordr      db 0    ;image byte order 0=lsb 1=msb
   c_map_byte_ordr      db 0    ;bitmap byte order 0=least sig first
   c_scan_unit  db 0
   c_scan_pad   db 0
   c_min_keycode        db 0
   c_max_keycode        db 0
   
   ;struc format
   c_depth              db 0
   c_bytes_per_pix      db 0
   c_scanline_pad       db 0
                   db 0 ;pad
   ;format_len
   ;endstruc
   
   ;struc screen
   root_win_id  dd 0
   root_win_color_map   dd 0
   c_white_pixel        dd 0
   c_black_pixel        dd 0
   c_event_mask dd 0
   root_win_pix_width   dw 0
   root_win_pix_height  dw 0
   c_width_mil  dw 0
   c_height_mil dw 0
   c_min_maps   dw 0
   c_max_maps   dw 0
   c_root_visual        dd 0
   c_backing    db 0 ;0=never 1=when mapped 2=always
   c_save_under db 0 ;bool
   c_root_depth db 0
   c_depth_cnt  db 0 ;number of depths that follow
   ;more data here
   ;endstruc
     [section .text]
   ;---------------------------------
   ;output: eax=negative if error,sign bit set
   ;        
   connect:
   ; create a socket
     mov        eax,102         ;socket
     mov        ebx,1           ;create socket
     mov        ecx,socket_create_blk
     int        byte 80h
     or eax,eax
     js c_exit
     mov [socket_fd2],eax
     mov [socket_fd],eax
   ; connect to it
     mov        eax,102         ;socket kernel function
     mov        ebx,3           ;connect
     mov        ecx,socket_connect_blk
     int        byte 80h
     or eax,eax
     js c_exit
   ; make the socket non-blocking
     mov ebx, [socket_fd]
     mov ecx, 3         ;F_GETFL (get flags)
     mov eax,55         ;fcntl
     int byte 0x80
     or eax,eax
     js c_exit          ;exit if error
   
     mov ebx, [socket_fd]
     mov ecx, 4          ;F_SETFL (set flags)
     mov edx, eax
     or edx, 0x800              ; NON_BLOCKING
     mov eax,55         ;fcntl
     int byte 0x80
     or eax,eax
     js c_exit
   ; write a connection request to it
     mov eax,4          ;write kernel function
     mov ebx, [socket_fd]
     mov ecx, conn_request
     mov edx, [conn_request_length]
     int byte 80h
     or eax,eax
     js c_exit  ;exit if error
   ; wait for reply
     mov        eax,[socket_fd]
     mov        esi,xfd_array
     mov        [esi],eax       ;store fd into array
     xor        eax,eax         ;wait forever
     call       wait_event
     or eax,eax         ;error check
     js conn_err
   ;test set bit, did our fd have an event?
     mov        eax,[socket_fd]
     mov        edi,ecx
     call       bit_test
     jc read_conn_reply ;jmp if correct bit
   conn_err:
     mov        eax,-1
     jmp        short c_exit
   ;read the connection reply
   read_conn_reply:
     mov ebx, [socket_fd]
     call       read_fd
   c_exit:
     or eax,eax         ;set return flag
     ret
   ;--------------------------------------
   ; check for x socket info
   ;input: [enviro_ptrs] - environment
   ;output: eax= negative if error
   ;        eax= connection packet setup if eax=positive
   ;
   get_authorization:
     mov        ebx,[enviro_ptrs]
     mov        edi,auth_path
     call       env_home                ;extract home path
     mov esi, auth_file_name
     mov ecx, auth_file_name_len
     rep movsb                  ;append .Xauthority to home path
   open_xauth:
     mov eax,5                  ;open kernel function
     mov ebx, auth_path
     xor ecx, ecx                       ;readonly
     int byte 80h                       ;read file .Xauthority
     mov edi, conn_request_len  ;in case no .Xauth found
     or eax,eax
     js no_auth                 ;jmp if file not found
   ;read and process Xauthority  file
     mov ebx, eax                       ;get handle in ebx
     call       read_fd
     js gx_exit                 ;exit if error
     mov eax,6                  ;close kernel function
     int byte 80h
   ; copy authorization proto name and data
   ; to connect request data packet
     mov esi, lib_buf + 3      ; offset of host name length
     movzx eax, byte [esi]      ; host name length
     lea esi, [esi + eax + 2]   ; skip host name
     movzx eax, byte [esi]      ; length
     lea esi, [esi + eax + 2]   ; skip it
     movzx ecx, byte [esi]      ; this ought to be auth name length
     mov [conn_request.proto_str_len], cx
     inc esi
     mov edi, conn_request.proto_str
     rep movsb
     inc esi
     add edi,byte 3       ; round up for "pad"
     and edi,byte  -4
     movzx ecx, byte [esi]      ; length of auth data
     mov [conn_request.proto_data_len], cx
     inc esi
     rep movsb
     sub edi, conn_request
     add edi,byte 3
     and edi,byte -4
   no_auth:
     mov [conn_request_length], edi
     xor        eax,eax         ;set good return
   gx_exit:
     or eax,eax         ;set result flag
     ret
   ;----------------------
     [section .data]
   
   socket_create_blk:   ;create a socket data
     dd 1       ;PF_UNIX
     dd 1       ;SOCK_STREAM
     dd 0       ;
   
   socket_connect_blk:
   socket_fd2:
     dd 0
     dd socket_path
     dd socket_path_len
   socket_path:
       dw    1     ; 1: AF_UNIX, AF_LOCAL  (/usr/include/linux/socket.h)
       db    "/tmp/.X11-unix/X"
   display_number:
       db    "0"                ;from display  variable
   socket_path_len equ $ - socket_path
   
   auth_file_name db '/.Xauthority', 0
   auth_file_name_len equ $ - auth_file_name
   auth_path    times 200 + 1 db 0
   
   
   display_var: db 'DISPLAY',0
   display_var_contents: times 8 db 0
   
   ; Connection Setup info
        align 4, db 0
   conn_request:
   .endian      db      6Ch     ; LSB first
   .unused      db      0
   .major       dw      11
   .minor       dw      0       ; major/minor version
   .proto_str_len dw    0       ; protocol_string_len
   .proto_data_len dw   0       ; fill in at runtime
   .unused2     dw      0
    conn_request_len equ $ - conn_request
   .proto_str times 256 db 0    ; enough for anybody
   
   conn_request_length dd 0
   
   local_fd     dd 0
   
     [section .text]
   ;----------------------
   ;input: ebx= fd
   ;output: eax = result & sign bit set
   read_fd:
     mov        [local_fd],ebx  ;save fd
     mov eax,3          ;kernel read function
     mov ecx, lib_buf
     mov edx, 700               ;lib_buf_len
     int byte 80h               ;read file
     jns        rf_exit         ;jmp if good read
     cmp        eax,-11
     jne        rf_exit
     mov        eax,[local_fd]
     mov        ebx,-1          ;wait forever
     call       poll_socket
     js rf_exit         ;exit if error
     mov        ebx,[local_fd]
     jmp        short read_fd
   rf_exit:
     or eax,eax
     ret
   
     [section .text]
   ;----------------------
   ;   wait_event - poll fd input/output status
   ; INPUTS
   ;    esi = array of dword fd's terminated by -1
   ;    eax = max wait time(usec), or zero to wait forever, and
   ;          minus 1 for immediate return of status
   ; OUTPUT
   ;    eax = 0 child has died? signal?
   ;        = negative, then error/signal active(-4)
   ;        = positive number of events pending
   ;    ecx = ptr to array of bits set for each fd with
   ;          pending actions, bit 1 represents stdin (fd 0).
   ;          fd's must be in numerical order (small to large).
   ;  * ---------------------------------------------------
   wait_event:
     push       eax             ;save wait forever flag
     mov        ecx,20
     mov        edi,event_buf   ;temp buffer for array
     call       blk_clear
   
     call       bit_set_list    ;set bits
     mov        ebx,[esi-8]     ;get value of highest fd
     inc        ebx             ;ebx = highest fd +1
     mov        ecx,edi         ;ecx = bit array ptr (input)
     xor        edx,edx         ;edx = 0 (no write bit array)
     xor        esi,esi         ;esi = 0 (no exceptfds bit array)
   
     pop        edi             ;get wait flag
     or edi,edi
     js we_fast_rtn     ;jmp if immediate return
     jz we_forever
   ;edi = number of microseconds to wait
     mov        [_time+4],edi   ;set microseconds
   we_fast_rtn:
     mov        edi,_time       ;assume stored time is zero
   we_forever:  
     mov        eax,142
     int        80h
     ret
   
     [section .data]
   _time:       dd      0       ;zero seconds, returns status immediately
        dd      0       ;microseconds to wait
   event_buf: dd        0,0,0,0,0,0,0
   ;bits representing fd numbers to poll, stdin=bit#1
     [section .text]
   
     
   ;----------------------
   ;   blk_clear - clear array of bytes
   ; INPUTS
   ;    ecx = size of array (byte count)
   ;    edi = array pointer
   ;    the CLD flag is set
   ; OUTPUT
   ;    ecx = 0
   ;    edi = unchanged
   ;  * ---------------------------------------------------
   blk_clear:
     push       eax
     push       edi
     xor        eax,eax
     rep        stosb
     pop        edi
     pop        eax
     ret
   
   ;----------------------
   ;bit_set_list - set bits in array
   ; INPUTS
   ;    esi = pointer to list of dword bit values
   ;          0 = bit 1 or 00000001h
   ;          -1 = end of list
   ;          values in increasing order
   ;    edi = array pointer
   ; OUTPUT
   ;    bits set in array
   ;    esi moved to end of list, beyond -1 entry
   ;  * ---------------------------------------------------
   bit_set_list:
     push       edx
     push       eax
   sa_loop:
     lodsd                      ;get bit value
     or eax,eax
     js sa_exit         ;exit if done (end of list)
     mov        edx,eax
     shr        edx,5
     and        eax,1fh
     lea        edx,[edx*4 + edi]
     bts        [edx],eax
     jmp        short sa_loop   ;loop
   sa_exit:
     pop        eax
     pop        edx
     ret
   ;------------------------------
   ;   bit_test - test array of bits
   ; INPUTS
   ;    eax = bit number
   ;          (0=bit 1) or 00000001h
   ;    edi = bit array pointer
   ; OUTPUT
   ;    carry = bit set
   ;    no-carry = bit cleared
   ;    registers unchanged
   ;  * ---------------------------------------------------
   bit_test:
     push       edx
     mov        edx,eax
     shr        edx,5
     lea        edx,[edx*4 + edi]
     and        eax,1fh
     bt dword [edx],eax ;check bit
     pop        edx
     ret
   ;------------------------------
   ;  env_home - search the environment for $HOME
   ; INPUTS
   ;     ebx = ptr to list of env pointers
   ;     edi = buffer to store $HOME contents
   ; OUTPUT
   ;    edi = ptr to zero at end of $HOME string
   ;  * ----------------------------------------------
   env_home:
     or ebx,ebx
     jz fh_50           ;jmp if home path not found
     mov        esi,[ebx]
     or esi,esi
     jz fh_50           ;jmp if home path not found
     cmp        dword [esi],'HOME'
     jne        fh_12           ;jmp if not found yet
     cmp        byte [esi + 4],'='
     je fh_20           ;jmp if HOME found
   fh_12:
     add        ebx,byte 4
     jmp        short env_home          ;loop  back and keep looking
   fh_20:
     add        esi, 5          ;move to start of home path
   ;
   ; assume edi points at execve_buf
   ;
     call       str_move
   fh_50:
     ret  
   
     [section .data]
   lib_buf      times 700 db 0
   enviro_ptrs  dd      0               ;from entry stack
     [section .text]
   ;----------------------------------
   ;  str_move - move asciiz string
   ; INPUTS
   ;    esi = input string ptr (asciiz)
   ;    edi = destination ptr
   ; OUTPUT
   ;    edi points at zero (end of moved asciiz string)
   ;  * ----------------------------------------------
   str_move:
     cld
   ms_loop:
     lodsb
     stosb
     or al,al
     jnz        ms_loop ;loop till done
     dec        edi
     ret
   ;------------------- poll_socket ----------------------------------
   ;  poll_socket - check if key avail.
   ; INPUTS
   ;    eax = fd (file descriptor)
   ;    edx = milliscond wait count,
   ;          -1=forever, 0=immediate return
   ; OUTPUT
   ;    flags set "js" - error (check before jnz)
   ;              "jz" - no event waiting, or timeout
   ;              "jnz" - event ready
   ; * ----------------------------------------------
   poll_socket:
     mov        [poll_tbl],eax          ;save fd
     mov        eax,168                 ;poll
     mov        ebx,poll_tbl
     mov        ecx,1                   ;one structure at poll_tbl
     int        80h
     or eax,eax
     js poll_exit
     jz poll_exit
     test       byte [poll_data],1
   poll_exit:
     ret
   
   
     [section .data]
   poll_tbl     dd      0       ;stdin
                dw      1       ;events of interest,data to read
   poll_data    dw      -1      ;return from poll
     [section .text]
   
   ;---------------------------------------
   ;  find_env_variable - search environment for variable name
   ; INPUTS
   ;    [enviro_ptrs] - setup by env_stack
   ;    ecx = ptr to variable name (asciiz)
   ;    edx = storage point for variable contents
   ; OUTPUT
   ;    data stored at edx, if edi is preloaded with
   ;    a zero it can be checked to see if variable found
   ;    edi - if success, edi points to end of variable stored
   ; * ----------------------------------------------
   find_env_variable:
     mov        ebx,[enviro_ptrs]
   fev_10:
     or ebx,ebx
     jz fev_50
     mov edi,[ebx]
     or edi,edi
     jz near fev_50
     mov        esi,ecx         ;get input variable name ptr
     call       str_match
     jne fev_12
     cmp [edi],byte '='
     je fev_20          ;jmp if var= found
   fev_12:
     add ebx,byte 4
     jmp short fev_10
   ;
   ; match found, store it
   ;
   fev_20:
     inc        edi             ;move past "="
     mov        esi,edi
     mov        edi,edx
     call       str_move
   fev_50:
     ret
   ;--------------------------------------------------
   ;  str_match - compare asciiz string to buffer data, use case
   ; INPUTS
   ;    esi = string1 (asciiz string)
   ;    edi = string2 buffer
   ;    assumes direction flag set to -cld- forward state
   ; OUTPUT
   ;    flags set for je or jne
   ;    esi & edi point at end of strings if match
   ; * ----------------------------------------------
   str_match:
        push    ecx
        call    strlen1                 ;find length of string1
        repe    cmpsb
        pop     ecx
        ret
   ;----------------------------------------------------
   ;  strlen1 - get length of esi string
   ; INPUTS
   ;    esi = pointer to asciiz string
   ; OUTPUT
   ;    ecx = length of string
   ;    all registers restored except for ecx
   ; * ----------------------------------------------
   strlen1:
        push    eax
        push    edi
        cld
        mov     edi,esi
        sub     al,al                   ;set al=0
        mov     ecx,-1
        repnz   scasb
        not     ecx
        dec     ecx
        pop     edi
        pop     eax
        ret
   
   ;---------------------
   ;  x_send_request - send request to x server
   ; INPUTS
   ;    ecx = packet ptr
   ;    edx = packet length, negative packet length
   ;          indicates a reply is expected.  Length
   ;          is can be set negative with "neg edx"
   ; OUTPUT:
   ;    flag set (jns) if success
   ;    flag set (js) if err, eax=error code
   ;    [sequence] - sequence number of packet sent
   ;        
   ; NOTES
   ;   If socket_fd is zero this functions connects to
   ;   x socket.  If the packet length is negative a
   ;   reply is expected and the sequence# is stored
   ;   for retrevial by x_read_socket
   ; * ----------------------------------------------
   x_send_request:
   x_send:
     mov        ebx,[socket_fd] ;get socket fd
     or ebx,ebx
     jnz        x_send2         ;jmp if connected
     push       ecx
     push       edx
     call       x_connect       ;connect to the server
     pop        edx
     pop        ecx
     js x_send_exit     ;exit if error
   x_send2:
     inc        dword [sequence]
     or edx,edx         ;check if reply expected
     jns        x_send3         ;jmp if no reply expected
     neg        edx             ;make packet length positive
     push       edx
     push       ecx
     mov        edx,list_block
     mov        esi,sequence
     call       list_put_at_end
     pop        ecx
     pop        edx
   x_send3:
     push       ecx
     push       edx
     mov        ebx,[socket_fd]
     call       poll_out
     pop        edx
     pop        ecx
   
   ;append to buffer
     cmp        edx,[x_buf_avail]
     jb queue_packet
     call       x_flush         ;flush before
   queue_packet:
     sub        [x_buf_avail],edx
     mov        esi,ecx
     mov        edi,[x_buf_ptr]
     mov        ecx,edx
     rep        movsb
     mov        [x_buf_ptr],edi
     xor        eax,eax         ;set exit flag
     ret
   ;---------------------
   ;>1 server
   ;  x_flush - send queued events to x server
   ;   the x_send_request function buffers all output
   ;   and sends if buffer becomes full or the program
   ;   waits for input.  This function flushes (sends)
   ;   the buffer to the x server.
   ; INPUTS
   ;    none
   ; OUTPUT:
   ;    sign flag set if error and eax modified
   ;    all other registers preserved.
   ; * ----------------------------------------------
   x_flush:
     pusha
     mov        ecx,x_buf
     mov        edx,[x_buf_ptr]
     sub        edx,ecx
     or edx,edx
     jz x_send_exit     ;exit if buffer empty  
     mov eax,4          ; __NR_write
     mov ebx, [socket_fd]
     int byte 80h
     cmp        eax,-11         ;is server busy
     jne        x_send4         ;jmp if success or error
     jmp        short x_flush
   x_send4:
     mov        [x_buf_ptr],dword x_buf
     mov        [x_buf_avail],dword x_buf_size
   x_send_exit:
     mov        [save_eax],eax
     popa
     mov        eax,[save_eax]
     or eax,eax
     ret
   ;---------------------
   
   poll_out:
     mov        [polled_fd],ebx
     mov        eax,168
     mov        ebx,poll_block
     mov        ecx,1   ;one fd
     mov        edx,-1  ;timeout
     int        byte 80h
     test       [poll_response], byte 4
     ret
   ;---------------------
     [section .data]
   
   poll_block:
   polled_fd:   dd 0
           dw 4 ;write now will not block
   poll_response:  dw -1
   
   sequence: dd 0               ;socket sequence#
   ;sequence# database control block
   list_block:
          dd buffer     ;top of buffer
          dd buffer_end ;end of buffer
          dd 2          ;each entry x bytes long
          dd buffer     ;first entry ptr
          dd buffer     ;last entry ptr
   ;storage for sequence# expecting a reply
   buffer: times 60 dw 0
   buffer_end:
   
   x_buf_size   equ  1024
   x_buf_ptr    dd x_buf
   x_buf_avail  dd x_buf_size
   x_buf        times x_buf_size db 0
   save_eax     dd 0
   ;-------------------------------------------------------------
     [section .text]
   ;---------------- list_put_at_end.asm -------------------
   
   struc list
   .list_buf_top_ptr resd 1
   .list_buf_end_ptr resd 1
   .list_entry_size resd 1
   .list_start_ptr resd 1
   .list_tail_ptr resd 1
   endstruc
   ;---------------------
   ;>1 list
   ;  list_put_at_end - add entry to end of list
   ; INPUTS
   ;    edx = list control block
   ;      struc list
   ;      .list_buf_top_ptr resd 1
   ;      .list_buf_end_ptr resd 1
   ;      .list_entry_size resd 1
   ;      .list_start_ptr resd 1
   ;      .list_tail_ptr resd 1
   ;      endstruc
   ;
   ;    Initially the control block for a empty
   ;    list could be set as follows by caller:
   ;       dd buffer     ;top of buffer
   ;       dd buffer_end ;end of buffer
   ;       dd x          ;each entry x bytes long
   ;       dd buffer     ;first entry ptr
   ;       dd buffer     ;last entry ptr
   ;
   ;    esi = ptr to data of length
   ;          liss_entry_size
   ;
   ; OUTPUT:
   ;    flag set (jns) if success
   ;      esi = will be advanced by size of entry
   ;      edx,ebp unchanged
   ;    flag set (js) if no room
   ;      esi,edx,ebp  unchanged
   ;
   ;    if data wraps in buffer, the global
   ;    [last_buf_put_at_end_adr] will be set        
   ; NOTES
   ;   A full list will have a one entry gap
   ;   between the list_start_ptr and list_tail_ptr.
   ;   The list pointers cycle around the buffer
   ;   and entries can be removed from start or
   ;   end of list.
   ; * ----------------------------------------------
   list_put_at_end:
     call       next_put_at_end ;eax=next stuff  edi=current stuff
     cmp        eax,[edx+list.list_start_ptr]   ;room for another entry
     jne        have_room
     mov   eax, -1
     jmp        short list_put_at_end_exit
   have_room:
     mov        [edx+list.list_tail_ptr],eax
     mov        ecx,[edx+list.list_entry_size]
     rep        movsb
   list_put_at_end_exit:
     or eax,eax
     ret
   
   ;---------------------
   ; compute next put ptr
   ;input: edx = control block
   ;       esi,ebp not available
   ;output: eax=next ptr ptr
   ;        edi=current stuff ptr
   ;
   next_put_at_end:
     mov        eax,[edx+list.list_tail_ptr]    ;get ptr to last entry
     mov        edi,eax                         ;save stuff ptr
     add        eax,[edx+list.list_entry_size]  ;move ptr forward
     cmp        eax,[edx+list.list_buf_end_ptr] ;beyond end of buffer
     jb np_exit                         ;jmp if ok
     mov        eax,[edx+list.list_buf_top_ptr] ;restart put at top of buffer
   np_exit:
     ret
   ;---------------------
   ;---------------------
     [section .text]
   ;--------- x_wait_reply -------------
   
   
   ;struc XAnyEvent
   ;.type               resd    1 ;
   ;.serial             resd    1 ; # of last request processed by server
   ;.send_event resd    1 ; true if this came from a SendEvent request
   ;.display    resd    1 ; Display the event was read from
   ;.window             resd    1 ; window on which event was requested in event mask
   ;endstruc
   
   struc XKeyEvent
   .type                resd    1; of event
   .serial              resd    1; # of last request processed by server
   .send_event  resd    1; true if this came from a SendEvent request
   .display     resd    1; Display the event was read from
   .window              resd    1;         "event" window it is reported relative to
   .root                resd    1;         root window that the event occurred on
   .subwindow   resd    1; child window
   .time                resd    1; milliseconds
   .x           resd    1
   .y           resd    1; pointer x, y coordinates in event window
   .x_root      resd    1
   .y_root              resd    1; coordinates relative to root
   .state               resd    1; key or button mask
   .keycode     resd    1; detail
   .same_screen resd    1; same screen flag
   endstruc
   struc XButtonEvent
   .type                resd    1; of event
   .serial              resd    1; # of last request processed by server
   .send_event  resd    1; true if this came from a SendEvent request
   .display     resd    1; Display the event was read from
   .window              resd    1;         "event" window it is reported relative to
   .root                resd    1;         root window that the event occurred on
   .subwindow   resd    1; child window
   .time                resd    1; milliseconds
   .x           resd    1
   .y           resd    1; pointer x, y coordinates in event window
   .x_root              resd    1
   .y_root              resd    1; coordinates relative to root
   .state               resd    1; key or button mask
   .button              resd    1; detail
   .same_screen resd    1; same screen flag
   endstruc
   
   ;---------------------
   ;>1 server
   ;  x_wait_reply - wait for xx milliseconds for reply
   ; INPUTS
   ;    none
   ; OUTPUT:
   ;    failure - eax=negative error code
   ;              flags set for js
   ;           -1=reply read error (buffer error)
   ;           -2=error packet in buffer
   ;           -3=reply out of sequence
   ;           -4=timeout expired or servers in tryagain loop
   ;           -5=unexpected event while waiting for reply.
   ;           -6=socket dead
   ;           -x=all other errors are from kernel
   ;    success - eax = number of bytes read from server
   ;              ecx = pointer to reply buffer info.            
   ;              (see file event_info.inc for buffer data)    
   ; NOTES
   ;   source file: x_wait_reply.asm
   ;   If replies are not pending this function will
   ;   return an error of -1
   ;   If reply does not occur within 2 seconds a timeout
   ;   error will be returned
   ; * ----------------------------------------------
   x_wait_reply:
     mov        edx,list_block
     call       list_check_front
     js wr_exit         ;exit if no reply pending
     mov        eax,2000        ;wait for 2 seconds max
     mov        ecx,lib_buf     ;buffer
     mov        edx,700         ;buffer length
     call       x_read_socket
   wr_exit:
     ret
   
   
   ;---------------------
   ;  list_check_front - check list top, do not remove entry
   ; INPUTS
   ;    edx = list control block
   ;      struc list
   ;      .list_buf_top_ptr resd 1
   ;      .list_buf_end_ptr resd 1
   ;      .list_entry_size resd 1
   ;      .list_start_ptr resd 1
   ;      .list_tail_ptr resd 1
   ;      endstruc
   ;
   ; OUTPUT:
   ;    flag set (jns) if success
   ;      esi = ptr to data
   ;      eax = 0
   ;      edx,ebp unchanged
   ;    flag set (js) if no data on list
   ;      eax=-1
   ;      edx,ebp  unchanged
   ;        
   ; NOTES
   ;   A full list will have a one entry gap
   ;   between the list_start_ptr and list_tail_ptr.
   ;   The list pointers cycle around the buffer
   ;   and entries can be removed from start or
   ;   end of list.
   ; * ----------------------------------------------
   list_check_front:
     mov        esi,[edx+list.list_start_ptr]
     cmp        esi,[edx+list.list_tail_ptr]
     jne        have_data
     mov        eax,-1
     jmp        short list_check_front_exit
   have_data:
     xor        eax,eax                 ;set success flag
   list_check_front_exit:
     or eax,eax
     ret
   ;---------------------
   ;  x_read_socket - read x server socket
   ; INPUTS
   ;    eax = wait length in milliseconds
   ;          0=no wait,immediate check for data
   ;         -1=forever
   ;    ecx = buffer for data
   ;    edx = buffer length
   ;
   ;    note: the sequence number queue set
   ;          by x_send_request may be used.
   ;
   ; OUTPUT:
   ;    success state          
   ;     flag set (jns) if success - expected reply or event
   ;     eax = number of bytes in buffer
   ;     ecx = reply buffer ptr
   ;    fail state
   ;     flags - set for js
   ;     eax = negative error
   ;           -1=reply read error (buffer error)
   ;           -2=error packet in buffer
   ;           -3=reply out of sequence
   ;           -4=timeout expired or servers in tryagain loop
   ;           -5=unexpected event while waiting for reply.
   ;           -6=socket died
   ;           -x=all other errors are from kernel
   ;   
   ; NOTES
   ;   see file event_info.inc for reply codes
   ;   This is the low level function used by all other
   ;   x server packet read functions.  See also,
   ;   x_wait_event
   ;   x_wait_reply
   ;   x_wait_big_reply
   ;   window_event_decode
   ; * ----------------------------------------------
   x_read_socket:
     mov        [poll_timeout],eax
     mov        [pkt_buf],ecx
     mov        [pkt_buf_length],edx
     mov        [timeout],byte 80
   ;
     call       x_flush
     jmp        short data_waiting      ;;
   x_read_socket3:
     mov        eax,[socket_fd]
     mov        edx,[poll_timeout]      ;
     call       poll_socket
     jnz        data_waiting
     mov        eax,-4                  ;
     jmp        x_read_socket_exit
   data_waiting:
     mov ebx, [socket_fd]
     mov eax,3          ; __NR_read
     mov        ecx,[pkt_buf]
     mov        edx,32          ;standard read size
     int byte 80h
     cmp        eax,-11         ;try again?
     jne        x_read_socket4  ;jmp if possible good read
     dec        dword [timeout]
     mov        eax,[timeout]
     or eax,eax
     jnz        x_read_socket3  ;loop back = retry
     mov        eax,-1
     jmp        short x_read_socket_exit
   ;check if good read
   x_read_socket4:
     or eax,eax
     js x_read_socket_exit      ;exit if error
     jnz        x_read_socket4a         ;jmp if socket data read
     mov        eax,-6                  ;eax=0, socket dead, exit
     jmp        short x_read_socket_exit
   x_read_socket4a:
     cmp        byte [ecx],0            ;error packet?
     jne        x_read_socket5          ;jmp if not error packet
   
   ;; note; do we need to pop possible reply packet here?
   
     mov        eax,-2                  ;get code = error packet
     jmp        short x_read_socket_exit
   ;check if waiting for reply, eax=read cnt, ecx=buf ptr
   x_read_socket5:
     mov        edx,list_block
     push       eax
     call       list_check_front        ;point at seq# on top of list
     pop        eax                     ;restore read count
     js x_read_socket_exit      ;exit if not reply (expected event?)
   ;verify this is a reply
     cmp        byte [ecx],1            ;reply packet
     jne        x_read_socket5a         ;jmp if not replay  
   ;this should be reply event,check seq#, esi=event ptr
     mov        bx,[ecx+2]              ;get seq# from reply
     cmp        bx,[esi]                ;check against list
     je x_read_socket7          ;jmp if sequence# match, expected pkt
   ;this is unexpected packet,check if event or reply
   x_read_socket5a:
     mov        eax,-5
     jmp        short x_read_socket_exit
   x_read_socket6:
     mov        eax,-3                  ;reply out of sequence
     jmp        short x_read_socket_exit
   ;we have expected reply,pop list, read tail if more data
   x_read_socket7:
     push       eax
     call       list_get_from_front
     pop        eax                     ;restore read length
     mov        edx,[ecx+4]             ;get remaining pkt data count
     or edx,edx
     jz x_read_socket_exit      ;jmp if all pkt data read
     add        ecx,32                  ;advance buffer ptr
     shl        edx,2                   ;convert to byte count
   ;read rest of packet
     mov ebx, [socket_fd]
     mov eax,3          ; __NR_read
     int byte 80h
     or eax,eax
     js x_read_socket_exit
     add        eax,32          ;restore correct packet length
     sub        ecx,32          ;restore buffer start
   x_read_socket_exit:
     or eax,eax
     ret
   ;--------------------------
     [section .data]
   pkt_buf      dd 0
   pkt_buf_length dd 0
   poll_timeout dd 0
   
   timeout: dd 0                ;used if server says try again later
   
     [section .text]
   
   ;---------------------
   ;  list_get_from_front - return entry from top of list
   ; INPUTS
   ;    edx = list control block
   ;      struc list
   ;      .list_buf_top_ptr resd 1
   ;      .list_buf_end_ptr resd 1
   ;      .list_entry_size resd 1
   ;      .list_start_ptr resd 1
   ;      .list_tail_ptr resd 1
   ;      endstruc
   ;
   ; OUTPUT:
   ;    flag set (jns) if success
   ;      esi = ptr to data
   ;      edx,ebp unchanged
   ;    flag set (js) if no data on list
   ;      edx,ebp  unchanged
   ;        
   ; NOTES
   ;   source file: list_get_from_front.asm
   ;   A full list will have a one entry gap
   ;   between the list_start_ptr and list_tail_ptr.
   ;   The list pointers cycle around the buffer
   ;   and entries can be removed from start or
   ;   end of list.
   ; * ----------------------------------------------
   list_get_from_front:
     mov        esi,[edx+list.list_start_ptr]
     cmp        esi,[edx+list.list_tail_ptr]
     jne        have_data2
     mov        eax,-1
     jmp        short list_get_from_front_exit
   
   have_data2:
   ;move pointer forward to next entry
     mov        eax,esi
     add        eax,[edx+list.list_entry_size]
     cmp        eax,[edx+list.list_buf_end_ptr]
     jb update_start_ptr                ;jmp if not at end
     mov        eax,[edx+list.list_buf_top_ptr] ;start at top
   update_start_ptr:
     mov        [edx+list.list_start_ptr],eax
     xor        eax,eax                 ;set success flag
   list_get_from_front_exit:
     or eax,eax
     ret
   
   
   ;----------------------------------------------------  
   ;   crt_write - display block of data
   ; INPUTS
   ;    ecx = ptr to data
   ;    edx = length of block
   ; OUTPUT
   ;   uses current color, see crt_set_color, crt_clear
   ;  * ---------------------------------------------------
   crt_write:
     mov eax, 0x4                       ; system call 0x4 (write)
     mov ebx,1          ; stdout        ; file desc. is stdout
     int byte 0x80
     ret