X Server (Desktop) Programming in Assembler
 jeff owens
 @2008 GNU General Public License


CONTENTS
1.   Introduction - what this document covers
2.   Linux Programming Environments
3.   Overview of X programming
4.   Connecting to the X server
5.   Sending X commands
6.   Receiving X data
7.   Speed Considerations
8.   X protocol extensions
9.   Compiling
10   Example program using asmlibx
11.  Example program without asmlibx
12.  Debugging
13.  X tools
14.  Useful Links
15.  Example 1 source
16.  Example 2 source
  
  
1.0 Introduction
----------------

  This document explores assembler programming
  for the Linux x server. It describes building
  a sample program and debugging x programs.


2.0 Linux Programming Environments
----------------------------------

  Linux programs can run in the console, on a
  terminal, or utilize the X server.  Each
  environment is unique and has different features
  available.  A short summary follows:
  
  Console programs talk directly to the Linux
  kernel and most are command line (text) programs.
  The kernel does not supply graphics functions,
  mouse drivers, or audio handlers.  This environment
  is home to a large number of classical unix
  utilities.  It can host very fast and efficient
  programming, has lots of documentation, and can
  be confusing to new programmers.
  
  Terminal programs run in a "terminal" talking
  to the X server. The terminal environment is
  very close the the console environment and most
  console programs can also run on a terminal.
  There are a large number of different terminals
  and each provides different features.  The more
  popular terminals add mouse handling and menus
  to control fonts and other features.
  
  X servers are starting to dominate Linux computing
  and provide a more robust and simpler interface
  for most programming jobs.  The X server provides
  very good handling for keyboards, mice, graphics,
  and adds window control for working with multiple
  programs at the same time.
  
  Which environment is best for portable programs?
  
  Assembler isn't considered portable and very few
  tools assist with portability.  Currently there
  is an library (HLA) that provides some basic
  functions. It supports terminal programming and
  may be useful for simple text programs. There
  are many non-assembler libraries that supply
  portability for X server/Microsoft  and they
  can be called from most assemblers.  Portable
  assemblers include nasm and fasm.     
  
  Unix to Unix portability has been attempted
  on the LinuxAssembly web site, but has not
  got a lot of support.
  
  If portability is a goal the best path is
  probably to work in a HLL such as "C" and use
  one of the portable libraries.

  If the choice is between console/terminal/
  x server programming, the x server interface
  is well standardized and available on other
  operating systems.
  
  Why choose console/terminal programming?
  
  Unix administration and setup is still done
  in the console and on terminals.  Servers and
  other demanding tasks, do not need a X server
  and run from the console. Also, many embedded
  systems or standalone work stations have
  chosen efficient console programming. All these
  programs are textual and work on minimal hardware.
  
  For textual programs needing efficiency, choose
  the console.
  
  Why choose X server programming?
  
  Today most games, applications, browsers, and general
  use software runs under the X server.  This environment
  provides a fast growing tool set and very robust
  programming arena.  It also, is home to many
  bloated and inefficient programs.  Assembler can
  fit into this environment easily, but few tools
  exist today.  It is probably a good choice for
  casual programming.
  
  The largest audience is also on the x desktop
  and the desktop interacts with web and remote
  computers.  Overall it provides the best future
  growth path.
  
  Other considerations for console/terminals
  
  Most Linux assembler information is about terminals
  or the console. There are lots of example programs
  and a few libraries. This area is popular with novice
  programmers, but they tend to move on to other areas.
  The HLL libraries for the console are still maintained,
  but activity is minimal.
  
  Other considerations for X server programming
  
  The X server is mostly the domain of HLL languages
  and libraries. A few programs call the low level
  xlib and 2 or 3 other low level libraries.
  Low level X programming fits assembler but
  design and coding requires a server mind set.
  
  All communication with the X server is via a
  socket and responses to requests or unsolicited
  input can appear at any time.  This makes
  simple one-step-at-a-time programming unworkable.
  Instead, programs are usually event driven.
  
  For speed all X server requests are collected in
  a buffer and send in one large transfer. Responses
  have a sequence number to indicate which request
  they are for, and not all requests have a
  response.  Most programmers will let the various
  libraries handle X server communication, but
  it helps to understand what is happening.
  
  
  
3.0 Overview of X programming
-----------------------------

  One of the largest problems facing programmers, is
  supporting all the different displays, keyboards,
  and other input devices.  The manufactures may
  provide device drivers and often the operating system
  will try to standardize the interface.  Libraries,
  also try to solve this problem by providing standard
  routines.

  A group at MIT decided the best solution was to
  leap frog the needs of programmers and provide
  a standard tool with additional features for
  abstraction and multiprocessing.  This would
  allow displays to be on a remote computer or
  even multiple displays on one computer.

  They also addressed problems with multiple users
  and programs sharing the same display. Next, they
  wrote a low level library to further isolate
  programmers from the details.  Today, most programs
  have gone further and add another library level.

  The picture looks something like this:

  computer hardware
    operating system
      x server
        x library
          HLL library
            applications

  This stack of handlers has produced fairly fast
  programs, but often they end up bloated in size and
  slow to load.

  Assembler programmers can work with the x library
  or with the HLL library, and may prefer doing
  graphic applications using these libraries. For
  non-graphical applications, the assembly programmer
  can easily bypass the standard libraries and
  gain some speed and size advantages.

  This document introduces the tools and information
  needed for talking directly to a x server.

  Another concept to be aware of is abstraction.
  With x servers we have levels of usage:
    display
    desktop
    window
    sub-window

  The display can be a device or file and a computer
  can have multiple displays defined.  Within a display
  we can have multiple desktops.  Each desktop can have
  a set of windows, and windows can have sub-windows.

  Any program can talk to these abstract items and
  request control of resources.  Creating them and finding
  out about them is where things get difficult.


4.0 Connecting to the X server
------------------------------

  In a multiprocessing environment you want
  the display to be available for everyone
  and still resolve conflicts. Also, you want
  to avoid having any process hog the
  communication channels.

  This is accomplished by using a socket that
  any process can connect to.  To keep this
  socket secure a simple handshake process was
  defined.

  Once we have a socket connected, the data
  flows to and from the x server as demand
  requires. We don't have to worry about
  other programs.

  The connection process goes like this:

   1. Select the display.  Usually, we look
      in a programs environment for the variable:
        DISPLAY=0:0
      The 0:0 specifies our target display.
   2. Look for the authorization string.  This key
      will open x server communication and is
      often placed in a local file.
   3. Create a socket and send our key
   4. Read reply with connection information.


5.0 Sending X commands
----------------------

  Commands are sent as packets of information. These
  packets share a standard format and set of rules.
  For portability all packets have lengths in multiples
  of dwords.  The initial packet always contains:

   1 byte - op code
   1 byte - value depends upon op code
   2 bytes- length of this request in dwords

  The rest of the packet depends upon the op code.
  The format of the various op codes is called the
  protocol.  

  Each request has a sequence number associated with it.
  The first request will be "1" and the next "2", etc.
  This number must be kept by the sending function and
  another counter is kept by the x server. This sequence
  number is needed to identify replies.  The next section
  will discuss this further.

  The protocol op codes (request codes) are:

  
AllocColor 84 CreateWindow 1
AllocColorCells 86 ChangeWindowAttributes 2
AllocColorPlanes 87 GetWindowAttributes 3
AllocNamedColor 85 DestroyWindow 4
AllowEvents 35 DestroySubwindows 5
Bell 104 ChangeSaveSet 6
ChangeActivePointerGrab 30 ReparentWindow 7
ChangeGC 56 MapWindow 8
ChangeHosts 109 MapSubwindows 9
ChangeKeyboardControl 102 UnmapWindow 10
ChangeKeyboardMapping 100 UnmapSubwindows 11
ChangePointerControl 105 ConfigureWindow 12
ChangeProperty 18 CirculateWindow 13
ChangeSaveSet 6 GetGeometry 14
ChangeWindowAttributes 2 QueryTree 15
CirculateWindow 13 InternAtom 16
ClearArea 61 GetAtomName 17
CloseFont 46 ChangeProperty 18
ConfigureWindow 12 DeleteProperty 19
ConvertSelection 24 GetProperty 20
CopyArea 62 ListProperties 21
CopyColormapAndFree 80 SetSelectionOwner 22
CopyGC 57 GetSelectionOwner 23
CopyPlane 63 ConvertSelection 24
CreateColormap 78 SendEvent 25
CreateCursor 93 GrabPointer 26
CreateGC 55 UngrabPointer 27
CreateGlyphCursor 94 GrabButton 28
CreatePixmap 53 UngrabButton 29
CreateWindow 1 ChangeActivePointerGrab 30
DeleteProperty 19 GrabKeyboard 31
DestroySubwindows 5 UngrabKeyboard 32
DestroyWindow 4 GrabKey 33
FillPoly 69 UngrabKey 34
ForceScreenSaver 115 AllowEvents 35
FreeColormap 79 GrabServer 36
FreeColors 88 UngrabServer 37
FreeCursor 95 QueryPointer 38
FreeGC 60 GetMotionEvents 39
FreePixmap 54 TranslateCoords 40
GetAtomName 17 WarpPointer 41
GetFontPath 52 SetInputFocus 42
GetGeometry 14 GetInputFocus 43
GetImage 73 QueryKeymap 44
GetInputFocus 43 OpenFont 45
GetKeyboardControl 103 CloseFont 46
GetKeyboardMapping 101 QueryFont 47
GetModifierMapping 119 QueryTextExtents 48
GetMotionEvents 39 ListFonts 49
GetPointerControl 106 ListFontsWithInfo 50
GetPointerMapping 117 SetFontPath 51
GetProperty 20 GetFontPath 52
GetScreenSaver 108 CreatePixmap 53
GetSelectionOwner 23 FreePixmap 54
GetWindowAttributes 3 CreateGC 55
GrabButton 28 ChangeGC 56
GrabKey 33 CopyGC 57
GrabKeyboard 31 SetDashes 58
GrabPointer 26 SetClipRectangles 59
GrabServer 36 FreeGC 60
ImageText16 77 ClearArea 61
ImageText8 76 CopyArea 62
InstallColormap 81 CopyPlane 63
InternAtom 16 PolyPoint 64
KillClient 113 PolyLine 65
ListExtensions 99 PolySegment 66
ListFonts 49 PolyRectangle 67
ListFontsWithInfo 50 PolyArc 68
ListHosts 110 FillPoly 69
ListInstalledColormaps 83 PolyFillRectangle 70
ListProperties 21 PolyFillArc 71
LookupColor 92 PutImage 72
MapSubwindows 9 GetImage 73
MapWindow 8 PolyText8 74
NoOperation 127 PolyText16 75
OpenFont 45 ImageText8 76
PolyArc 68 ImageText16 77
PolyFillArc 71 CreateColormap 78
PolyFillRectangle 70 FreeColormap 79
PolyLine 65 CopyColormapAndFree 80
PolyPoint 64 InstallColormap 81
PolyRectangle 67 UninstallColormap 82
PolySegment 66 ListInstalledColormaps 83
PolyText16 75 AllocColor 84
PolyText8 74 AllocNamedColor 85
PutImage 72 AllocColorCells 86
QueryBestSize 97 AllocColorPlanes 87
QueryColors 91 FreeColors 88
QueryExtension 98 StoreColors 89
QueryFont 47 StoreNamedColor 90
QueryKeymap 44 QueryColors 91
QueryPointer 38 LookupColor 92
QueryTextExtents 48 CreateCursor 93
QueryTree 15 CreateGlyphCursor 94
RecolorCursor 96 FreeCursor 95
ReparentWindow 7 RecolorCursor 96
RotateProperties 114 QueryBestSize 97
SendEvent 25 QueryExtension 98
SetAccessControl 111 ListExtensions 99
SetClipRectangles 59 ChangeKeyboardMapping 100
SetCloseDownMode 112 GetKeyboardMapping 101
SetDashes 58 ChangeKeyboardControl 102
SetFontPath 51 GetKeyboardControl 103
SetInputFocus 42 Bell 104
SetModifierMapping 118 ChangePointerControl 105
SetPointerMapping 116 GetPointerControl 106
SetScreenSaver 107 SetScreenSaver 107
SetSelectionOwner 22 GetScreenSaver 108
StoreColors 89 ChangeHosts 109
StoreNamedColor 90 ListHosts 110
TranslateCoords 40 SetAccessControl 111
UngrabButton 29 SetCloseDownMode 112
UngrabKey 34 KillClient 113
UngrabKeyboard 32 RotateProperties 114
UngrabPointer 27 ForceScreenSaver 115
UngrabServer 37 SetPointerMapping 116
UninstallColormap 82 GetPointerMapping 117
UnmapSubwindows 11 SetModifierMapping 118
UnmapWindow 10 GetModifierMapping 119
WarpPointer 41 NoOperation 127

   
  The fields for each request code are documented at MIT
  (see links). A good description of these requests is
  contained in the book: X protocol Reference Manual, by
  O'Reilly & Associates


6.0 Receiving X data
--------------------

  Some protocol commands have replies and most
  can generate errors. If we have windows, then
  they we can enable various event notifications.

  All received data follows these rules:
  
  1. The initial packet is 32 bytes long and
     has a standard header.  Normal replies
     have a op code of "1" and errors have a
     opcode of "0". Other opcodes are defined
     for events.
  2. If additional information is coming, it's
     size is given.
  3. All replies to requests must provide a
     sequence number

  The initial packet looks like this:

     1 byte - code
     1 byte - (depends upon code)
     2 bytes- sequence number (if reply or error)
     4 bytes- length of additional data to be sent

  The remaining fields depend on the code byte value.
  
7.0 Speed Considerations
------------------------

  Commands can be sent at any time and it is possible
  events can occur at any time. If we collect requests
  and send a large block at one time, a big speed
  increase occurs. The same is true of some event
  processing.

  All x libraries have request batching and they
  provide a "flush" function to force sending the
  current buffer contents.  Normally, requests
  are sent when the buffer is full of requests.


8.0 X protocol extensions
--------------------------


  Extensions to protocol (requests) can be added
  by compiling them into x servers.  The programmer
  can use these extensions as follows:

  1. Use QueryExtension request to check if
     extension is available.
  2. Get extension opcode from reply to
     QueryExtension.

  The example program in section 10.0 will list
  available extensions.

  
9.0 Compiling
-------------

  Our example code is written for the nasm assembler
  and can be compiled with:

  nasm -f elf -O999 -g -o example.o example.asm

  where: -f elf  <-- output in elf format
         -O999   <-- optimization passes
         -g      <-- include debug information
         -o example.o <-- create object file
         example.asm  <-- assembler source file

  The example.o output file must be linked with
  two libraries to create an executable.

  ld -o example example.o asmlibx.a asmlib.a

  where: -0 example   <-- output executable
         example.o    <-- input file to link
         asmlibx.a    <-- library to link with
         asmlib.a     <-- library to link with

  The linker "ld" is on almost all Linux distributions
  and is part of the bin utilities. Nasm is available
  at sourceforge (see links).
  
10.0 Example program - using asmlibx
-------------------------------------

  The full source for example is included
  in section 15.  A discussion of the code
  follows:

  Once we are connected, extensive information
  can be requested from the server.
  For a starter, we might want to know which
  extensions this server supports.  For this
  we need to send a protocol request:

  ListExtensions - protocol #99

  If we utilize the library "asmlibx" our program
  would appear as follows:

 ---- program start -----
_start:
  call  env_stack       ;save ptr to environment
  call  x_list_extension;get x server extensions
  call  show_extensions ;display extensions
 
  mov   eax,01          ;exit function
  int   byte 80h        ;exit program
 ---- program end ----

  The call to env_stack creates a global variable
  that points to the program environment strings.
  This is used by a server connection routine to
  find our display number.

  The x_list_extension call first checks to see if
  we are connected, and if not, attempts a connection.
  Next, it sends a request to server and collects
  the replies.

  Finally, the show_extensions is a small subroutine
  we must write to display the extension names.

  The complete listing of this program can be
  found in section 14.0.
  
  That was easy, but what happened?  Most of the
  details were handled by asmlibx and we are
  left with the results.  What if we wanted to
  go down another level?

  The next example goes into the gory details.
  If you want to enter example 1 and test it,
  skip to section 12 Debugging.

11.0 Example program - without asmlibx
--------------------------------------
 
  This example (example2) does not use any libraries and is
  identical to example 1. It is interesting to note that the
  source file for example 1 is about 600 bytes and this example
  has a source file of over 32,000 bytes. Libraries save a lot of
  coding time and effort!

  The easiest way to approach x programming is to use
  a common x connect routine for all programs.  Then
  use the common send/receive handlers to talk to
  the x server. The server protocol functions may
  be available in  asmlibx, but often they must be
  coded.

  In example 2 we will talk about how to code a
  protocol function and use asmlibx to send
  and receive data.

  The library function that connects to a x server
  is called x_connect. We can call x_connect at
  the start of a program or let the send function
  do it for us. Our only requirement is to call
  env_stack at the start of our program.

  The two library functions for sending and receiving
  are:

    x_send_request
    x_wait_reply

  We provide x_send_request with a protocol packet
  then call x_wait_reply to get the response.  The
  code to get a list of x server extensions would
  look like this:
     
    list_extension_request:
     db 99      ;opcode
     db 0       ;unused
     dw 1       ;request length in dwords
    qer_end:
    
    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

  If a reply is expected we must negate the size of
  the send packet. The tells the send function that
  a flush is needed after sending the packet.  If we
  didn't flush, the packet would sit in a buffer and
  the reply would return a timeout error.

  If all goes well, the reply pointer will be returned
  in register ecx.

   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

  The length of the reply packet is variable
  and we must check the name count and
  use it for processing.  Also, this reply
  is held in a temporary buffer and the
  data must be used before other library
  functions are called.

  We now have the tools to code complex programs
  by adding our own protocol functions.   All we  need
  is to connect and then use x_send_request and
  x_wait_reply .   If a library function exists  we could
  use it, but mixing libraries may not  work.  Each
  library would try to set up  its own x server connection.


12.0 Debugging
--------------

  To quickly check if the x server communication
  is occurring, we can use "tracex".  Tracex creates
  a log file containing all server communication
  packets.  Run tracex as follows:

  tracex example1

  The output trace is stored at example1.tra.
  Entries contain the name of protocol functions,
  sequence numbers, and a dump of packet contents.
  We should see the following:

  --- start of file example1.tra ---  
   Authorization packet written
   
   ListExtensions request#=0001
   63 00 01 00                                       c...
   
   -Event-reply-to#0001
   01 1D 01 00 50 00 00 00 04 00 00 00 01 00 00 00   ....P...........
   00 00 00 00 04 00 00 00 90 01 00 00 04 00 00 00   ................
   
   -continuation of reply#0001
   05 53 48 41 50 45 16 4D 49 54 2D 53 55 4E 44 52   .SHAPE.MIT-SUNDR
   59 2D 4E 4F 4E 53 54 41 4E 44 41 52 44 0C 42 49   Y-NONSTANDARD.BI
   47 2D 52 45 51 55 45 53 54 53 04 53 59 4E 43 10   G-REQUESTS.SYNC.
   4D 49 54 2D 53 43 52 45 45 4E 2D 53 41 56 45 52   MIT-SCREEN-SAVER
   07 58 43 2D 4D 49 53 43 18 58 46 72 65 65 38 36   .XC-MISC.XFree86
   2D 56 69 64 4D 6F 64 65 45 78 74 65 6E 73 69 6F   -VidModeExtensio
   6E 0C 58 46 72 65 65 38 36 2D 4D 69 73 63 0B 58   n.XFree86-Misc.X
   46 72 65 65 38 36 2D 44 47 41 04 44 50 4D 53 07   Free86-DGA.DPMS.
   54 4F 47 2D 43 55 50 1B 45 78 74 65 6E 64 65 64   TOG-CUP.Extended
   2D 56 69 73 75 61 6C 2D 49 6E 66 6F 72 6D 61 74   -Visual-Informat
   69 6F 6E 06 58 56 69 64 65 6F 0A 58 2D 52 65 73   ion.XVideo.X-Res
   6F 75 72 63 65 0D 44 4F 55 42 4C 45 2D 42 55 46   ource.DOUBLE-BUF
   46 45 52 03 47 4C 58 07 53 47 49 2D 47 4C 58 06   FER.GLX.SGI-GLX.
   52 45 43 4F 52 44 07 4D 49 54 2D 53 48 4D 0F 58   RECORD.MIT-SHM.X
   49 6E 70 75 74 45 78 74 65 6E 73 69 6F 6E 05 58   InputExtension.X
   54 45 53 54 09 58 4B 45 59 42 4F 41 52 44 0B 58   TEST.XKEYBOARD.X
   43 2D 41 50 50 47 52 4F 55 50 08 53 45 43 55 52   C-APPGROUP.SECUR
   49 54 59 06 58 46 49 58 45 53 0F 58 46 72 65 65   ITY.XFIXES.XFree
   38 36 2D 42 69 67 66 6F 6E 74 06 52 45 4E 44 45   86-Bigfont.RENDE
   52 05 52 41 4E 44 52 06 44 41 4D 41 47 45 00 00   R.RANDR.DAMAGE..
  --- end of file example1.tra ---

  The second entry is our request.  It has a op code
  of 63 hex or 99 decimal. The length field says it
  is one dword long.

  Following the request is a reply.  The reply code
  of 01 says we were successful and the length field
  of 50 says we need to read an additional packet of
  80 decimal bytes.
 
  If we want to dig a little deeper, the asmbug program
  will walk through the program as it executes.  Type:

  asmbug example1

  AsmBug is easy to use if you just assume it knows what
  you want.  Click on a address to set a break and then
  click run.  Or click step and watch it execute.

  If you want a complete trace of every instruction, the
  asmtrace program can be used.  It is interactive and
  also creates the log file example1.tra. Beware, asmtrace
  produces a lot of data and expect a large file.

  The final debugging tool is inserting print statements
  into our example program.  Often this works well with
  event driven programs.  Our simple example program will
  not need print statements, but it is good to be prepared.
  The asmlib provides some functions to do logging which
  save registers to avoid introducing additional bugs.

  links to tracex, asmbug, and asmtrace are in section 14
  
13.0 X tools
------------

  For graphics, sound, and other needs, the HLL
  libraries may be of interest.  Libraries to
  explore are: GTK, QT, and SDL.

  xwininfo

  To get information about displayed windows, run
  the xwininfo program. It allows you to click on
  window of interest and then sends a lot of data
  to stdout.

  xdpyinfo

  A good display of x server information can be
  obtained from xdpyinfo.  Just type: xdpyinfo
  and stand back.  It will dump just about everything.


14.0 Useful Links
-----------------

link to home of this tutorial and asmlib, asmlibx,
asmbug, tracex, and other x related programs
  https://thlorenz.com/linuxasmtools-net/x/

link to hundreds of X related web sites
  http://www.rahul.net/kenton/xsites.html#FAQ

nasm assembler
  http://sourceforge.net/projects/nasm

bin utilities, ld and others
  http://www.gnu.org/software/

individual tools
  http;//sourceforge.net/projects/tracex
  http://sourceforge.net/projects/asmbug
  http://sourceforge.net/projects/asmtrace

libraries
  http://sourceforge.net/projects/asmlibx
  http://sorceforge.net/projects/asmlib

X documentation, protocol, extensions
  http://webcvs.freedesktop.org/xorg/xc/

15.0 example 1 source
---------------------

A source file for this example is in download page
of https://thlorenz.com/linuxasmtools-net/x/

;-code1-
   extern env_stack
   extern crt_write
   extern x_list_extension
   
   global _start
   _start:
     call       env_stack       ;save ptr to environment
     call       x_list_extension;get x server extensions
     call       show_extensions ;display extensions
    
     mov        eax,01          ;exit function
     int        byte 80h        ;exit program
   
   ;------------------------------------------------
   ;subroutine to display list of x server extensions
   ;inputs: ecx = ptr to reply from x server
   ;
   show_extensions:
     lea        esi,[ecx+32]    ;get ptr to extension
     xor        eax,eax
     mov        al,[ecx+1]      ;get number of extensions
     mov        ebp,eax         ;save count
   show_loop:
     call       line_feed
     lea        ecx,[esi+1]     ;get ptr to extension name
     xor        edx,edx
     mov        dl,[esi]        ;get length of name
     add        esi,edx         ;move to next name
     inc        esi             ;move to next name
     call       crt_write       ;display extension
   
     dec        ebp             ;dec extension count
     jnz        show_loop       ;loop if more names
     call       line_feed       ;add final line feed
     ret
   ;-----------------------------------------------
   ;subroutine to display line-feed
   ;inputs: registers esi,ebp must be
   ;        preserved
   line_feed:
     mov        ecx,linefeed
     mov        edx,1
     call       crt_write
     ret
   ;-----------
     [section .data]
   linefeed:    db 0ah
     [section .text]
;-code end-
   
16.0 example 2 source
---------------------

A source file for this example is in download page
of https://thlorenz.com/linuxasmtools-net/x/

;-code2-
   ;-----------------------------------------------------------------------
   ;   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
;-code end-   
            

Fork me on GitHub