From 1a30aa5a09cc433783201942c2a8ed1bc368970f Mon Sep 17 00:00:00 2001 From: klein panic Date: Sun, 29 Sep 2024 02:42:50 -0400 Subject: [PATCH] Initial commit --- main | Bin 0 -> 10688 bytes main.O | Bin 0 -> 4960 bytes main.asm | 623 ++++++++++++++++++++++++++++++++++++++++++++++++++ main.asm.bak | 623 ++++++++++++++++++++++++++++++++++++++++++++++++++ main.asm.bak1 | 623 ++++++++++++++++++++++++++++++++++++++++++++++++++ main.o | Bin 0 -> 4912 bytes 6 files changed, 1869 insertions(+) create mode 100755 main create mode 100644 main.O create mode 100644 main.asm create mode 100644 main.asm.bak create mode 100644 main.asm.bak1 create mode 100644 main.o diff --git a/main b/main new file mode 100755 index 0000000000000000000000000000000000000000..91f580482f81431005c39cc6d6f9d1331b5d8f29 GIT binary patch literal 10688 zcmb<-^>JfjWMqH=CI&kO5HCc;0W1U|85kJU!CWxmz+l0^!r;Kb&AR=mLZqZuN>2vO$I{Knuoi;6A-D??|A3d?a96%Nqo$1xWbp0r~wD!iG; zTvYffj=8Atryg@r5lBG^5fPZDMfh7~7#SEmnvWR3jdeKAqGHa#4mP$kM1|wHiwX~q z0|Ucx7Zrj3|G_j!KSQUFiUf+OGB8tR_*;)LfK7FPo0yHY&$mR9HaCw6jD-pff~8Q5yCn!^~Fo0r(1r%2tpt$03?7ZO7xku#(GXq2Cg`FVZ&HVoJ`>p0j%#NKG zT{;iAFrGO0f~olci%Tbq%0U$sj%FSepU#5^9|~~tsCaaK17&c5KQ5gIKQ94=R`w+p zNIZ2z%miguepV053;eCE|Nj5?XgtEe!oU!F_{H+y|Nnb-9{1=x{(}3@|NoAiM_yb2 z(??&N0Mo~OI{&+LetYro&;S2m<~x_pM=qToU#S26|Nq63KmY%C9`@<{-+9cX^HDbs zIM+GuIKgm&!EvVoa|F|iu)kn^o%ddBfUsT&{DX?PbiR7w`tSdLNJ0RW9sHo224alj z(GVC7fzc2c4S~@R7!85Z5Eu=C(GVEvA)sH9TcEEOVQ8pZnwMFjA7Q}Yk(!f}ucJ_& zUzC%gh!H$G3=9lRU{O#Hgn)+dY4DqdPf>~XG5qA?<@!- z)4SZe+$Yk<+uO(2+sD?<(6+2pQBBdVth8L;phZz#(b>DyyBTD5G(O`B*)Cl#LVbZ>g^pF+3eli+zOF4@Bul+w-l<{zz1XzjAQ4c zs2mw-s^}bP3-X7dZCRvkq^*Isfue?@9Wy5r3!`m|w|8`;cVwx7cSdAJWQKRNtzk^0 zp?7nncVxP^ceJ6mA~V=_yE5;{DBD*3NJRsETRU4@TL*7%Z+%;P+ek%Q16%LNW^V)A zj50-)NITmoyGXl8J6qdmTU%S(s3-$lecRH^^e9{XDBCDoecR~BNG2u*a0GyZ9Gw7l zu~`^C{zn(*VgN-K69Z`I30V#__{7M-!~p6rBa2&u)iX0FK-YP|*ftdj`6a*6k z(V*omj10^Spm8A(ABOdz=70u%U}7NJ4xA4d;prbF2Erjw@qE~N7LXtV14BAg+(rVPE4@1@8K@-0Y z6_*o6wf8C196L1eA5igTG;vu_1T!!)bfAelK*jsf#3L9%$BLh_?{D`1qva;`ri{ z#G(?0_|%ldl0=61)Vvf12EF3S+>*p32EF2vA_$!UV}Xs)E6N9})`OC%Nu}xWiA9Nd zAaCiVfVr7@Y55FLc4AUeQEC~Cmy?;73QI5)($e7afq?;3(SVJD<$LIYGWhy6up|S< zvNBK&3rhNsIu*1)4Vu3|qniv2450D~sufIu2JyfQC;=L}2kD2@u?+C?P64VP>I|rA z2G9T-w7$g`NK*Pmg2@o5^0ExrG m9>j-X2~`NI4$VR|E`uPveFRYtBd4iBSfD{)2n#}@>jwaP4Iy>_ literal 0 HcmV?d00001 diff --git a/main.O b/main.O new file mode 100644 index 0000000000000000000000000000000000000000..bd3c797cf6f4e3c47d3d5cf4370cb876791b18dc GIT binary patch literal 4960 zcmb<-^>JfjWMqH=Mg}_u1P><4z~F#jLfH-sLJWLJqU13_c7ZSxn)U-M5CM4z1t+1x za6Tsk1K7RHa1jXO0aT#?gbOF3!f-x6R6iSB1j6_LRrnN2Lqr)E(9IKt>W9h0=m#K& zF)%O~zyzQ)$gfZ)m{MV2U_i41EH1zWrr`QP?gXjhfCw{4Kxqpc_B%lJLzTfPbo)IB z=ud;|gfP(chv0Dk2B`fN5N!+~_p>uFFrd3X6^H!>+z^ej5DHGB+h2@Be*jc}ADRX< zE<-&I_dno)SP2Vfko!SiLU;cJ9QHHtLiD4PF#Ry`xg_dGcmE0o1_t!B4>lWW|5-2x zL7?m3#K6FSp7%hiFF^H6Kn(|_dj%}ldq#to?n!cq6pQEph7)5pYG6P zU|`q}a>+gr0rCO^KdVRc4+j3eHB1Z)9-YTMn%@+p9cNKtWbo)UQ3YxD?EK@=Tcg7C zXQwa&1HJ}$U2^#pm6xS1XWaSCn$73FWCWdzDMVO zup9JXZqVazea6VZumfxcR1y?yU@vm;x1MBVU}&&WVdQTGg@H%o5s>R*5BqwC==wUk zdphen`}=!)y6PIb&5N`CwD|}N+om$5#2tS&fjraA0uGIC8we!=rW7x5{%D3sFhc#@ z{Knuoi;6A-D??|A3d?a96%JMg28LrUDm-b&TvT{7kGZJuRUC6s;ZHs0q9Txj;u8^= zPek}zWf&P4JerRfz>Re{&Z1(@zz#OHGem{sxQhx8j{^h4aTgVV|Np@>NIyfTkBS6} zsWLEAW%yf%ag1K{o^srAg5d;%<4y(U2&NZdf5G}X@4eUnVZ9Le z2NiMYeD%Wh-~a!RgaFF({Ggl$Vt}v?ynF|RAV>s)(c3i;Q3eKBI|x+QA)5m#a6oE6 zNdgqGAT|gm!OMRRkQ7J=1?MJa=IJFC=Q8MLpK?b>br+} z>NEH`2Ky=)=^2{o85lA!HejodV1^>Q55@+`L91(UTM*fO;IaT($AZLR{sxr>AblWz zf)X`|4Z@)y0!clT3#S+b83h@I7zNmvIixvMIfPjm7?|`J1Q{Kmj_G0q)yJS#Dm6y}oe7~byt7Ibm6#csgcyG)+Le`->l?Hvsw+BsmwGpY42^~= z^HF5xWMX7gv}5LFVqq-x_HJ%&_Cd&TgXEZbnV1=UO1-@!Bb&XOn_D5$20kD+_?ALd z8~7-yDcZp}c0P*Ak&&i~&XKkt4;b2(McPK%8h9HhYAD(oBx3#y8 zRJ1j)^^R=zHn7boQ&fqxvyHNgw2QQ}wT-s5wY80kGO*RREzL}ivel2Wjk49ZjgE|D zVqyS=JShExf*X`LLGce_qhP(F{FKC!L<=B4GMsD}tZBomX8ic-s9rsrhlrJ^W>2tbsk6(#1T!d2!c zGw2m(6qgj0BqlNF6<6j$Xeb}-I zvPuY>nE^Q`LBv2LGXu0U1mYlJW(H91LlR(M0E>XCTU1d722i65Bo1m0A^#xNK`)x*jWn4}}P{KWKcI9LHQLq3>B5LsYzm>Hx%0S;zE zIJsz9$OH90l2EHx!QwW1)u7(|z)=9S=5lwXpOS`?p(MM+9g zVtG8cL;`sj!HX|0Nh|{81aRU26Q*F2fdSNAM^0Lcq2i$E2Sp_;KkY;lhvg?{kU|EC tImk)E3r!qk7RZP|G;vt#CKLDR^KoS4| literal 0 HcmV?d00001 diff --git a/main.asm b/main.asm new file mode 100644 index 0000000..2263e48 --- /dev/null +++ b/main.asm @@ -0,0 +1,623 @@ +; Build with: nasm -f elf64 -g main.nasm && ld main.o -static -o main + +BITS 64 ; 64 bits. +CPU X64 ; Target the x86_64 family of CPUs. + +section .rodata + +sun_path: db "/tmp/.X11-unix/X0", 0 +static sun_path:data + +hello_world: db "Hello, world!" +static hello_world:data + +section .data + +id: dd 0 +static id:data + +id_base: dd 0 +static id_base:data + +id_mask: dd 0 +static id_mask:data + +root_visual_id: dd 0 +static root_visual_id:data + + +section .text + +%define AF_UNIX 1 +%define SOCK_STREAM 1 + +%define SYSCALL_READ 0 +%define SYSCALL_WRITE 1 +%define SYSCALL_POLL 7 +%define SYSCALL_SOCKET 41 +%define SYSCALL_CONNECT 42 +%define SYSCALL_EXIT 60 +%define SYSCALL_FCNTL 72 + +; Create a UNIX domain socket and connect to the X11 server. +; @returns The socket file descriptor. +x11_connect_to_server: +static x11_connect_to_server:function + push rbp + mov rbp, rsp + + ; Open a Unix socket: socket(2). + mov rax, SYSCALL_SOCKET + mov rdi, AF_UNIX ; Unix socket. + mov rsi, SOCK_STREAM ; Stream oriented. + mov rdx, 0 ; Automatic protocol. + syscall + + cmp rax, 0 + jle die + + mov rdi, rax ; Store socket fd in `rdi` for the remainder of the function. + + sub rsp, 112 ; Store struct sockaddr_un on the stack. + + mov WORD [rsp], AF_UNIX ; Set sockaddr_un.sun_family to AF_UNIX + ; Fill sockaddr_un.sun_path with: "/tmp/.X11-unix/X0". + lea rsi, sun_path + mov r12, rdi ; Save the socket file descriptor in `rdi` in `r12`. + lea rdi, [rsp + 2] + cld ; Move forward + mov ecx, 19 ; Length is 19 with the null terminator. + rep movsb ; Copy. + + ; Connect to the server: connect(2). + mov rax, SYSCALL_CONNECT + mov rdi, r12 + lea rsi, [rsp] + %define SIZEOF_SOCKADDR_UN 2+108 + mov rdx, SIZEOF_SOCKADDR_UN + syscall + + cmp rax, 0 + jne die + + mov rax, rdi ; Return the socket fd. + + add rsp, 112 + pop rbp + ret + +; Send the handshake to the X11 server and read the returned system information. +; @param rdi The socket file descriptor +; @returns The window root id (uint32_t) in rax. +x11_send_handshake: +static x11_send_handshake:function + push rbp + mov rbp, rsp + + sub rsp, 1<<15 + mov BYTE [rsp + 0], 'l' ; Set order to 'l'. + mov WORD [rsp + 2], 11 ; Set major version to 11. + + ; Send the handshake to the server: write(2). + mov rax, SYSCALL_WRITE + mov rdi, rdi + lea rsi, [rsp] + mov rdx, 12 + syscall + + cmp rax, 12 ; Check that all bytes were written. + jnz die + + ; Read the server response: read(2). + ; Use the stack for the read buffer. + ; The X11 server first replies with 8 bytes. Once these are read, it replies with a much bigger message. + mov rax, SYSCALL_READ + mov rdi, rdi + lea rsi, [rsp] + mov rdx, 8 + syscall + + cmp rax, 8 ; Check that the server replied with 8 bytes. + jnz die + + cmp BYTE [rsp], 1 ; Check that the server sent 'success' (first byte is 1). + jnz die + + ; Read the rest of the server response: read(2). + ; Use the stack for the read buffer. + mov rax, SYSCALL_READ + mov rdi, rdi + lea rsi, [rsp] + mov rdx, 1<<15 + syscall + + cmp rax, 0 ; Check that the server replied with something. + jle die + + ; Set id_base globally. + mov edx, DWORD [rsp + 4] + mov DWORD [id_base], edx + + ; Set id_mask globally. + mov edx, DWORD [rsp + 8] + mov DWORD [id_mask], edx + + ; Read the information we need, skip over the rest. + lea rdi, [rsp] ; Pointer that will skip over some data. + + mov cx, WORD [rsp + 16] ; Vendor length (v). + movzx rcx, cx + + mov al, BYTE [rsp + 21]; Number of formats (n). + movzx rax, al ; Fill the rest of the register with zeroes to avoid garbage values. + imul rax, 8 ; sizeof(format) == 8 + + add rdi, 32 ; Skip the connection setup + + ; Skip over padding. + add rdi, 3 + and rdi, -4 + + add rdi, rcx ; Skip over the vendor information (v). + add rdi, rax ; Skip over the format information (n*8). + + mov eax, DWORD [rdi] ; Store (and return) the window root id. + + ; Set the root_visual_id globally. + mov edx, DWORD [rdi + 32] + mov DWORD [root_visual_id], edx + + add rsp, 1<<15 + pop rbp + ret + +; Increment the global id. +; @return The new id. +x11_next_id: +static x11_next_id:function + push rbp + mov rbp, rsp + + mov eax, DWORD [id] ; Load global id. + + mov edi, DWORD [id_base] ; Load global id_base. + mov edx, DWORD [id_mask] ; Load global id_mask. + + ; Return: id_mask & (id) | id_base + and eax, edx + or eax, edi + + add DWORD [id], 1 ; Increment id. + + pop rbp + ret + +; Open the font on the server side. +; @param rdi The socket file descriptor. +; @param esi The font id. +x11_open_font: +static x11_open_font:function + push rbp + mov rbp, rsp + + %define OPEN_FONT_NAME_BYTE_COUNT 5 + %define OPEN_FONT_PADDING ((4 - (OPEN_FONT_NAME_BYTE_COUNT % 4)) % 4) + %define OPEN_FONT_PACKET_U32_COUNT (3 + (OPEN_FONT_NAME_BYTE_COUNT + OPEN_FONT_PADDING) / 4) + %define X11_OP_REQ_OPEN_FONT 0x2d + + sub rsp, 6*8 + mov DWORD [rsp + 0*4], X11_OP_REQ_OPEN_FONT | (OPEN_FONT_NAME_BYTE_COUNT << 16) + mov DWORD [rsp + 1*4], esi + mov DWORD [rsp + 2*4], OPEN_FONT_NAME_BYTE_COUNT + mov BYTE [rsp + 3*4 + 0], 'f' + mov BYTE [rsp + 3*4 + 1], 'i' + mov BYTE [rsp + 3*4 + 2], 'x' + mov BYTE [rsp + 3*4 + 3], 'e' + mov BYTE [rsp + 3*4 + 4], 'd' + + + mov rax, SYSCALL_WRITE + mov rdi, rdi + lea rsi, [rsp] + mov rdx, OPEN_FONT_PACKET_U32_COUNT*4 + syscall + + cmp rax, OPEN_FONT_PACKET_U32_COUNT*4 + jnz die + + add rsp, 6*8 + + pop rbp + ret + +; Create a X11 graphical context. +; @param rdi The socket file descriptor. +; @param esi The graphical context id. +; @param edx The window root id. +; @param ecx The font id. +x11_create_gc: +static x11_create_gc:function + push rbp + mov rbp, rsp + + sub rsp, 8*8 + +%define X11_OP_REQ_CREATE_GC 0x37 +%define X11_FLAG_GC_BG 0x00000004 +%define X11_FLAG_GC_FG 0x00000008 +%define X11_FLAG_GC_FONT 0x00004000 +%define X11_FLAG_GC_EXPOSE 0x00010000 + +%define CREATE_GC_FLAGS X11_FLAG_GC_BG | X11_FLAG_GC_FG | X11_FLAG_GC_FONT +%define CREATE_GC_PACKET_FLAG_COUNT 3 +%define CREATE_GC_PACKET_U32_COUNT (4 + CREATE_GC_PACKET_FLAG_COUNT) +%define MY_COLOR_RGB 0x0000ffff + + mov DWORD [rsp + 0*4], X11_OP_REQ_CREATE_GC | (CREATE_GC_PACKET_U32_COUNT<<16) + mov DWORD [rsp + 1*4], esi + mov DWORD [rsp + 2*4], edx + mov DWORD [rsp + 3*4], CREATE_GC_FLAGS + mov DWORD [rsp + 4*4], MY_COLOR_RGB + mov DWORD [rsp + 5*4], 0 + mov DWORD [rsp + 6*4], ecx + + mov rax, SYSCALL_WRITE + mov rdi, rdi + lea rsi, [rsp] + mov rdx, CREATE_GC_PACKET_U32_COUNT*4 + syscall + + cmp rax, CREATE_GC_PACKET_U32_COUNT*4 + jnz die + + add rsp, 8*8 + + pop rbp + ret + +; Create the X11 window. +; @param rdi The socket file descriptor. +; @param esi The new window id. +; @param edx The window root id. +; @param ecx The root visual id. +; @param r8d Packed x and y. +; @param r9d Packed w and h. +x11_create_window: +static x11_create_window:function + push rbp + mov rbp, rsp + + %define X11_OP_REQ_CREATE_WINDOW 0x01 + %define X11_FLAG_WIN_BG_COLOR 0x00000002 + %define X11_EVENT_FLAG_KEY_RELEASE 0x0002 + %define X11_EVENT_FLAG_EXPOSURE 0x8000 + %define X11_FLAG_WIN_EVENT 0x00000800 + + %define CREATE_WINDOW_FLAG_COUNT 2 + %define CREATE_WINDOW_PACKET_U32_COUNT (8 + CREATE_WINDOW_FLAG_COUNT) + %define CREATE_WINDOW_BORDER 1 + %define CREATE_WINDOW_GROUP 1 + + sub rsp, 12*8 + + mov DWORD [rsp + 0*4], X11_OP_REQ_CREATE_WINDOW | (CREATE_WINDOW_PACKET_U32_COUNT << 16) + mov DWORD [rsp + 1*4], esi + mov DWORD [rsp + 2*4], edx + mov DWORD [rsp + 3*4], r8d + mov DWORD [rsp + 4*4], r9d + mov DWORD [rsp + 5*4], CREATE_WINDOW_GROUP | (CREATE_WINDOW_BORDER << 16) + mov DWORD [rsp + 6*4], ecx + mov DWORD [rsp + 7*4], X11_FLAG_WIN_BG_COLOR | X11_FLAG_WIN_EVENT + mov DWORD [rsp + 8*4], 0 + mov DWORD [rsp + 9*4], X11_EVENT_FLAG_KEY_RELEASE | X11_EVENT_FLAG_EXPOSURE + + + mov rax, SYSCALL_WRITE + mov rdi, rdi + lea rsi, [rsp] + mov rdx, CREATE_WINDOW_PACKET_U32_COUNT*4 + syscall + + cmp rax, CREATE_WINDOW_PACKET_U32_COUNT*4 + jnz die + + add rsp, 12*8 + + pop rbp + ret + +; Map a X11 window. +; @param rdi The socket file descriptor. +; @param esi The window id. +x11_map_window: +static x11_map_window:function + push rbp + mov rbp, rsp + + sub rsp, 16 + + %define X11_OP_REQ_MAP_WINDOW 0x08 + mov DWORD [rsp + 0*4], X11_OP_REQ_MAP_WINDOW | (2<<16) + mov DWORD [rsp + 1*4], esi + + mov rax, SYSCALL_WRITE + mov rdi, rdi + lea rsi, [rsp] + mov rdx, 2*4 + syscall + + cmp rax, 2*4 + jnz die + + add rsp, 16 + + pop rbp + ret + +; Read the X11 server reply. +; @return The message code in al. +x11_read_reply: +static x11_read_reply:function + push rbp + mov rbp, rsp + + sub rsp, 32 + + mov rax, SYSCALL_READ + mov rdi, rdi + lea rsi, [rsp] + mov rdx, 32 + syscall + + cmp rax, 1 + jle die + + mov al, BYTE [rsp] + + add rsp, 32 + + pop rbp + ret + +die: + mov rax, SYSCALL_EXIT + mov rdi, 1 + syscall + + +; Set a file descriptor in non-blocking mode. +; @param rdi The file descriptor. +set_fd_non_blocking: +static set_fd_non_blocking:function + push rbp + mov rbp, rsp + + %define F_GETFL 3 + %define F_SETFL 4 + + %define O_NONBLOCK 2048 + + mov rax, SYSCALL_FCNTL + mov rdi, rdi + mov rsi, F_GETFL + mov rdx, 0 + syscall + + cmp rax, 0 + jl die + + ; `or` the current file status flag with O_NONBLOCK. + mov rdx, rax + or rdx, O_NONBLOCK + + mov rax, SYSCALL_FCNTL + mov rdi, rdi + mov rsi, F_SETFL + mov rdx, rdx + syscall + + cmp rax, 0 + jl die + + pop rbp + ret + +; Poll indefinitely messages from the X11 server with poll(2). +; @param rdi The socket file descriptor. +; @param esi The window id. +; @param edx The gc id. +poll_messages: +static poll_messages:function + push rbp + mov rbp, rsp + + sub rsp, 32 + + %define POLLIN 0x001 + %define POLLPRI 0x002 + %define POLLOUT 0x004 + %define POLLERR 0x008 + %define POLLHUP 0x010 + %define POLLNVAL 0x020 + + mov DWORD [rsp + 0*4], edi + mov DWORD [rsp + 1*4], POLLIN + + mov DWORD [rsp + 16], esi ; window id + mov DWORD [rsp + 20], edx ; gc id + mov BYTE [rsp + 24], 0 ; exposed? (boolean) + + .loop: + mov rax, SYSCALL_POLL + lea rdi, [rsp] + mov rsi, 1 + mov rdx, -1 + syscall + + cmp rax, 0 + jle die + + cmp DWORD [rsp + 2*4], POLLERR + je die + + cmp DWORD [rsp + 2*4], POLLHUP + je die + + mov rdi, [rsp + 0*4] + call x11_read_reply + + %define X11_EVENT_EXPOSURE 0xc + cmp eax, X11_EVENT_EXPOSURE + jnz .received_other_event + + .received_exposed_event: + mov BYTE [rsp + 24], 1 ; Mark as exposed. + + .received_other_event: + + cmp BYTE [rsp + 24], 1 ; exposed? + jnz .loop + + .draw_text: + mov rdi, [rsp + 0*4] ; socket fd + lea rsi, [hello_world] ; string + mov edx, 13 ; length + mov ecx, [rsp + 16] ; window id + mov r8d, [rsp + 20] ; gc id + mov r9d, 100 ; x + shl r9d, 16 + or r9d, 100 ; y + call x11_draw_text + + + jmp .loop + + + add rsp, 32 + pop rbp + ret + +; Draw text in a X11 window with server-side text rendering. +; @param rdi The socket file descriptor. +; @param rsi The text string. +; @param edx The text string length in bytes. +; @param ecx The window id. +; @param r8d The gc id. +; @param r9d Packed x and y. +x11_draw_text: +static x11_draw_text:function + push rbp + mov rbp, rsp + + sub rsp, 1024 + + mov DWORD [rsp + 1*4], ecx ; Store the window id directly in the packet data on the stack. + mov DWORD [rsp + 2*4], r8d ; Store the gc id directly in the packet data on the stack. + mov DWORD [rsp + 3*4], r9d ; Store x, y directly in the packet data on the stack. + + mov r8d, edx ; Store the string length in r8 since edx will be overwritten next. + mov QWORD [rsp + 1024 - 8], rdi ; Store the socket file descriptor on the stack to free the register. + + ; Compute padding and packet u32 count with division and modulo 4. + mov eax, edx ; Put dividend in eax. + mov ecx, 4 ; Put divisor in ecx. + cdq ; Sign extend. + idiv ecx ; Compute eax / ecx, and put the remainder (i.e. modulo) in edx. + ; LLVM optimizer magic: `(4-x)%4 == -x & 3`, for some reason. + neg edx + and edx, 3 + mov r9d, edx ; Store padding in r9. + + mov eax, r8d + add eax, r9d + shr eax, 2 ; Compute: eax /= 4 + add eax, 4 ; eax now contains the packet u32 count. + + + %define X11_OP_REQ_IMAGE_TEXT8 0x4c + mov DWORD [rsp + 0*4], r8d + shl DWORD [rsp + 0*4], 8 + or DWORD [rsp + 0*4], X11_OP_REQ_IMAGE_TEXT8 + mov ecx, eax + shl ecx, 16 + or [rsp + 0*4], ecx + + ; Copy the text string into the packet data on the stack. + mov rsi, rsi ; Source string in rsi. + lea rdi, [rsp + 4*4] ; Destination + cld ; Move forward + mov ecx, r8d ; String length. + rep movsb ; Copy. + + mov rdx, rax ; packet u32 count + imul rdx, 4 + mov rax, SYSCALL_WRITE + mov rdi, QWORD [rsp + 1024 - 8] ; fd + lea rsi, [rsp] + syscall + + cmp rax, rdx + jnz die + + add rsp, 1024 + + pop rbp + ret + +_start: +global _start:function + call x11_connect_to_server + mov r15, rax ; Store the socket file descriptor in r15. + + mov rdi, rax + call x11_send_handshake + + mov r12d, eax ; Store the window root id in r12. + + call x11_next_id + mov r13d, eax ; Store the gc_id in r13. + + call x11_next_id + mov r14d, eax ; Store the font_id in r14. + + mov rdi, r15 + mov esi, r14d + call x11_open_font + + + mov rdi, r15 + mov esi, r13d + mov edx, r12d + mov ecx, r14d + call x11_create_gc + + call x11_next_id + + mov ebx, eax ; Store the window id in ebx. + + mov rdi, r15 ; socket fd + mov esi, eax + mov edx, r12d + mov ecx, [root_visual_id] + mov r8d, 200 | (200 << 16) ; x and y are 200 + %define WINDOW_W 800 + %define WINDOW_H 600 + mov r9d, WINDOW_W | (WINDOW_H << 16) + call x11_create_window + + mov rdi, r15 ; socket fd + mov esi, ebx + call x11_map_window + + mov rdi, r15 ; socket fd + call set_fd_non_blocking + + mov rdi, r15 ; socket fd + mov esi, ebx ; window id + mov edx, r13d ; gc id + call poll_messages + + ; The end. + mov rax, SYSCALL_EXIT + mov rdi, 0 + syscall diff --git a/main.asm.bak b/main.asm.bak new file mode 100644 index 0000000..2263e48 --- /dev/null +++ b/main.asm.bak @@ -0,0 +1,623 @@ +; Build with: nasm -f elf64 -g main.nasm && ld main.o -static -o main + +BITS 64 ; 64 bits. +CPU X64 ; Target the x86_64 family of CPUs. + +section .rodata + +sun_path: db "/tmp/.X11-unix/X0", 0 +static sun_path:data + +hello_world: db "Hello, world!" +static hello_world:data + +section .data + +id: dd 0 +static id:data + +id_base: dd 0 +static id_base:data + +id_mask: dd 0 +static id_mask:data + +root_visual_id: dd 0 +static root_visual_id:data + + +section .text + +%define AF_UNIX 1 +%define SOCK_STREAM 1 + +%define SYSCALL_READ 0 +%define SYSCALL_WRITE 1 +%define SYSCALL_POLL 7 +%define SYSCALL_SOCKET 41 +%define SYSCALL_CONNECT 42 +%define SYSCALL_EXIT 60 +%define SYSCALL_FCNTL 72 + +; Create a UNIX domain socket and connect to the X11 server. +; @returns The socket file descriptor. +x11_connect_to_server: +static x11_connect_to_server:function + push rbp + mov rbp, rsp + + ; Open a Unix socket: socket(2). + mov rax, SYSCALL_SOCKET + mov rdi, AF_UNIX ; Unix socket. + mov rsi, SOCK_STREAM ; Stream oriented. + mov rdx, 0 ; Automatic protocol. + syscall + + cmp rax, 0 + jle die + + mov rdi, rax ; Store socket fd in `rdi` for the remainder of the function. + + sub rsp, 112 ; Store struct sockaddr_un on the stack. + + mov WORD [rsp], AF_UNIX ; Set sockaddr_un.sun_family to AF_UNIX + ; Fill sockaddr_un.sun_path with: "/tmp/.X11-unix/X0". + lea rsi, sun_path + mov r12, rdi ; Save the socket file descriptor in `rdi` in `r12`. + lea rdi, [rsp + 2] + cld ; Move forward + mov ecx, 19 ; Length is 19 with the null terminator. + rep movsb ; Copy. + + ; Connect to the server: connect(2). + mov rax, SYSCALL_CONNECT + mov rdi, r12 + lea rsi, [rsp] + %define SIZEOF_SOCKADDR_UN 2+108 + mov rdx, SIZEOF_SOCKADDR_UN + syscall + + cmp rax, 0 + jne die + + mov rax, rdi ; Return the socket fd. + + add rsp, 112 + pop rbp + ret + +; Send the handshake to the X11 server and read the returned system information. +; @param rdi The socket file descriptor +; @returns The window root id (uint32_t) in rax. +x11_send_handshake: +static x11_send_handshake:function + push rbp + mov rbp, rsp + + sub rsp, 1<<15 + mov BYTE [rsp + 0], 'l' ; Set order to 'l'. + mov WORD [rsp + 2], 11 ; Set major version to 11. + + ; Send the handshake to the server: write(2). + mov rax, SYSCALL_WRITE + mov rdi, rdi + lea rsi, [rsp] + mov rdx, 12 + syscall + + cmp rax, 12 ; Check that all bytes were written. + jnz die + + ; Read the server response: read(2). + ; Use the stack for the read buffer. + ; The X11 server first replies with 8 bytes. Once these are read, it replies with a much bigger message. + mov rax, SYSCALL_READ + mov rdi, rdi + lea rsi, [rsp] + mov rdx, 8 + syscall + + cmp rax, 8 ; Check that the server replied with 8 bytes. + jnz die + + cmp BYTE [rsp], 1 ; Check that the server sent 'success' (first byte is 1). + jnz die + + ; Read the rest of the server response: read(2). + ; Use the stack for the read buffer. + mov rax, SYSCALL_READ + mov rdi, rdi + lea rsi, [rsp] + mov rdx, 1<<15 + syscall + + cmp rax, 0 ; Check that the server replied with something. + jle die + + ; Set id_base globally. + mov edx, DWORD [rsp + 4] + mov DWORD [id_base], edx + + ; Set id_mask globally. + mov edx, DWORD [rsp + 8] + mov DWORD [id_mask], edx + + ; Read the information we need, skip over the rest. + lea rdi, [rsp] ; Pointer that will skip over some data. + + mov cx, WORD [rsp + 16] ; Vendor length (v). + movzx rcx, cx + + mov al, BYTE [rsp + 21]; Number of formats (n). + movzx rax, al ; Fill the rest of the register with zeroes to avoid garbage values. + imul rax, 8 ; sizeof(format) == 8 + + add rdi, 32 ; Skip the connection setup + + ; Skip over padding. + add rdi, 3 + and rdi, -4 + + add rdi, rcx ; Skip over the vendor information (v). + add rdi, rax ; Skip over the format information (n*8). + + mov eax, DWORD [rdi] ; Store (and return) the window root id. + + ; Set the root_visual_id globally. + mov edx, DWORD [rdi + 32] + mov DWORD [root_visual_id], edx + + add rsp, 1<<15 + pop rbp + ret + +; Increment the global id. +; @return The new id. +x11_next_id: +static x11_next_id:function + push rbp + mov rbp, rsp + + mov eax, DWORD [id] ; Load global id. + + mov edi, DWORD [id_base] ; Load global id_base. + mov edx, DWORD [id_mask] ; Load global id_mask. + + ; Return: id_mask & (id) | id_base + and eax, edx + or eax, edi + + add DWORD [id], 1 ; Increment id. + + pop rbp + ret + +; Open the font on the server side. +; @param rdi The socket file descriptor. +; @param esi The font id. +x11_open_font: +static x11_open_font:function + push rbp + mov rbp, rsp + + %define OPEN_FONT_NAME_BYTE_COUNT 5 + %define OPEN_FONT_PADDING ((4 - (OPEN_FONT_NAME_BYTE_COUNT % 4)) % 4) + %define OPEN_FONT_PACKET_U32_COUNT (3 + (OPEN_FONT_NAME_BYTE_COUNT + OPEN_FONT_PADDING) / 4) + %define X11_OP_REQ_OPEN_FONT 0x2d + + sub rsp, 6*8 + mov DWORD [rsp + 0*4], X11_OP_REQ_OPEN_FONT | (OPEN_FONT_NAME_BYTE_COUNT << 16) + mov DWORD [rsp + 1*4], esi + mov DWORD [rsp + 2*4], OPEN_FONT_NAME_BYTE_COUNT + mov BYTE [rsp + 3*4 + 0], 'f' + mov BYTE [rsp + 3*4 + 1], 'i' + mov BYTE [rsp + 3*4 + 2], 'x' + mov BYTE [rsp + 3*4 + 3], 'e' + mov BYTE [rsp + 3*4 + 4], 'd' + + + mov rax, SYSCALL_WRITE + mov rdi, rdi + lea rsi, [rsp] + mov rdx, OPEN_FONT_PACKET_U32_COUNT*4 + syscall + + cmp rax, OPEN_FONT_PACKET_U32_COUNT*4 + jnz die + + add rsp, 6*8 + + pop rbp + ret + +; Create a X11 graphical context. +; @param rdi The socket file descriptor. +; @param esi The graphical context id. +; @param edx The window root id. +; @param ecx The font id. +x11_create_gc: +static x11_create_gc:function + push rbp + mov rbp, rsp + + sub rsp, 8*8 + +%define X11_OP_REQ_CREATE_GC 0x37 +%define X11_FLAG_GC_BG 0x00000004 +%define X11_FLAG_GC_FG 0x00000008 +%define X11_FLAG_GC_FONT 0x00004000 +%define X11_FLAG_GC_EXPOSE 0x00010000 + +%define CREATE_GC_FLAGS X11_FLAG_GC_BG | X11_FLAG_GC_FG | X11_FLAG_GC_FONT +%define CREATE_GC_PACKET_FLAG_COUNT 3 +%define CREATE_GC_PACKET_U32_COUNT (4 + CREATE_GC_PACKET_FLAG_COUNT) +%define MY_COLOR_RGB 0x0000ffff + + mov DWORD [rsp + 0*4], X11_OP_REQ_CREATE_GC | (CREATE_GC_PACKET_U32_COUNT<<16) + mov DWORD [rsp + 1*4], esi + mov DWORD [rsp + 2*4], edx + mov DWORD [rsp + 3*4], CREATE_GC_FLAGS + mov DWORD [rsp + 4*4], MY_COLOR_RGB + mov DWORD [rsp + 5*4], 0 + mov DWORD [rsp + 6*4], ecx + + mov rax, SYSCALL_WRITE + mov rdi, rdi + lea rsi, [rsp] + mov rdx, CREATE_GC_PACKET_U32_COUNT*4 + syscall + + cmp rax, CREATE_GC_PACKET_U32_COUNT*4 + jnz die + + add rsp, 8*8 + + pop rbp + ret + +; Create the X11 window. +; @param rdi The socket file descriptor. +; @param esi The new window id. +; @param edx The window root id. +; @param ecx The root visual id. +; @param r8d Packed x and y. +; @param r9d Packed w and h. +x11_create_window: +static x11_create_window:function + push rbp + mov rbp, rsp + + %define X11_OP_REQ_CREATE_WINDOW 0x01 + %define X11_FLAG_WIN_BG_COLOR 0x00000002 + %define X11_EVENT_FLAG_KEY_RELEASE 0x0002 + %define X11_EVENT_FLAG_EXPOSURE 0x8000 + %define X11_FLAG_WIN_EVENT 0x00000800 + + %define CREATE_WINDOW_FLAG_COUNT 2 + %define CREATE_WINDOW_PACKET_U32_COUNT (8 + CREATE_WINDOW_FLAG_COUNT) + %define CREATE_WINDOW_BORDER 1 + %define CREATE_WINDOW_GROUP 1 + + sub rsp, 12*8 + + mov DWORD [rsp + 0*4], X11_OP_REQ_CREATE_WINDOW | (CREATE_WINDOW_PACKET_U32_COUNT << 16) + mov DWORD [rsp + 1*4], esi + mov DWORD [rsp + 2*4], edx + mov DWORD [rsp + 3*4], r8d + mov DWORD [rsp + 4*4], r9d + mov DWORD [rsp + 5*4], CREATE_WINDOW_GROUP | (CREATE_WINDOW_BORDER << 16) + mov DWORD [rsp + 6*4], ecx + mov DWORD [rsp + 7*4], X11_FLAG_WIN_BG_COLOR | X11_FLAG_WIN_EVENT + mov DWORD [rsp + 8*4], 0 + mov DWORD [rsp + 9*4], X11_EVENT_FLAG_KEY_RELEASE | X11_EVENT_FLAG_EXPOSURE + + + mov rax, SYSCALL_WRITE + mov rdi, rdi + lea rsi, [rsp] + mov rdx, CREATE_WINDOW_PACKET_U32_COUNT*4 + syscall + + cmp rax, CREATE_WINDOW_PACKET_U32_COUNT*4 + jnz die + + add rsp, 12*8 + + pop rbp + ret + +; Map a X11 window. +; @param rdi The socket file descriptor. +; @param esi The window id. +x11_map_window: +static x11_map_window:function + push rbp + mov rbp, rsp + + sub rsp, 16 + + %define X11_OP_REQ_MAP_WINDOW 0x08 + mov DWORD [rsp + 0*4], X11_OP_REQ_MAP_WINDOW | (2<<16) + mov DWORD [rsp + 1*4], esi + + mov rax, SYSCALL_WRITE + mov rdi, rdi + lea rsi, [rsp] + mov rdx, 2*4 + syscall + + cmp rax, 2*4 + jnz die + + add rsp, 16 + + pop rbp + ret + +; Read the X11 server reply. +; @return The message code in al. +x11_read_reply: +static x11_read_reply:function + push rbp + mov rbp, rsp + + sub rsp, 32 + + mov rax, SYSCALL_READ + mov rdi, rdi + lea rsi, [rsp] + mov rdx, 32 + syscall + + cmp rax, 1 + jle die + + mov al, BYTE [rsp] + + add rsp, 32 + + pop rbp + ret + +die: + mov rax, SYSCALL_EXIT + mov rdi, 1 + syscall + + +; Set a file descriptor in non-blocking mode. +; @param rdi The file descriptor. +set_fd_non_blocking: +static set_fd_non_blocking:function + push rbp + mov rbp, rsp + + %define F_GETFL 3 + %define F_SETFL 4 + + %define O_NONBLOCK 2048 + + mov rax, SYSCALL_FCNTL + mov rdi, rdi + mov rsi, F_GETFL + mov rdx, 0 + syscall + + cmp rax, 0 + jl die + + ; `or` the current file status flag with O_NONBLOCK. + mov rdx, rax + or rdx, O_NONBLOCK + + mov rax, SYSCALL_FCNTL + mov rdi, rdi + mov rsi, F_SETFL + mov rdx, rdx + syscall + + cmp rax, 0 + jl die + + pop rbp + ret + +; Poll indefinitely messages from the X11 server with poll(2). +; @param rdi The socket file descriptor. +; @param esi The window id. +; @param edx The gc id. +poll_messages: +static poll_messages:function + push rbp + mov rbp, rsp + + sub rsp, 32 + + %define POLLIN 0x001 + %define POLLPRI 0x002 + %define POLLOUT 0x004 + %define POLLERR 0x008 + %define POLLHUP 0x010 + %define POLLNVAL 0x020 + + mov DWORD [rsp + 0*4], edi + mov DWORD [rsp + 1*4], POLLIN + + mov DWORD [rsp + 16], esi ; window id + mov DWORD [rsp + 20], edx ; gc id + mov BYTE [rsp + 24], 0 ; exposed? (boolean) + + .loop: + mov rax, SYSCALL_POLL + lea rdi, [rsp] + mov rsi, 1 + mov rdx, -1 + syscall + + cmp rax, 0 + jle die + + cmp DWORD [rsp + 2*4], POLLERR + je die + + cmp DWORD [rsp + 2*4], POLLHUP + je die + + mov rdi, [rsp + 0*4] + call x11_read_reply + + %define X11_EVENT_EXPOSURE 0xc + cmp eax, X11_EVENT_EXPOSURE + jnz .received_other_event + + .received_exposed_event: + mov BYTE [rsp + 24], 1 ; Mark as exposed. + + .received_other_event: + + cmp BYTE [rsp + 24], 1 ; exposed? + jnz .loop + + .draw_text: + mov rdi, [rsp + 0*4] ; socket fd + lea rsi, [hello_world] ; string + mov edx, 13 ; length + mov ecx, [rsp + 16] ; window id + mov r8d, [rsp + 20] ; gc id + mov r9d, 100 ; x + shl r9d, 16 + or r9d, 100 ; y + call x11_draw_text + + + jmp .loop + + + add rsp, 32 + pop rbp + ret + +; Draw text in a X11 window with server-side text rendering. +; @param rdi The socket file descriptor. +; @param rsi The text string. +; @param edx The text string length in bytes. +; @param ecx The window id. +; @param r8d The gc id. +; @param r9d Packed x and y. +x11_draw_text: +static x11_draw_text:function + push rbp + mov rbp, rsp + + sub rsp, 1024 + + mov DWORD [rsp + 1*4], ecx ; Store the window id directly in the packet data on the stack. + mov DWORD [rsp + 2*4], r8d ; Store the gc id directly in the packet data on the stack. + mov DWORD [rsp + 3*4], r9d ; Store x, y directly in the packet data on the stack. + + mov r8d, edx ; Store the string length in r8 since edx will be overwritten next. + mov QWORD [rsp + 1024 - 8], rdi ; Store the socket file descriptor on the stack to free the register. + + ; Compute padding and packet u32 count with division and modulo 4. + mov eax, edx ; Put dividend in eax. + mov ecx, 4 ; Put divisor in ecx. + cdq ; Sign extend. + idiv ecx ; Compute eax / ecx, and put the remainder (i.e. modulo) in edx. + ; LLVM optimizer magic: `(4-x)%4 == -x & 3`, for some reason. + neg edx + and edx, 3 + mov r9d, edx ; Store padding in r9. + + mov eax, r8d + add eax, r9d + shr eax, 2 ; Compute: eax /= 4 + add eax, 4 ; eax now contains the packet u32 count. + + + %define X11_OP_REQ_IMAGE_TEXT8 0x4c + mov DWORD [rsp + 0*4], r8d + shl DWORD [rsp + 0*4], 8 + or DWORD [rsp + 0*4], X11_OP_REQ_IMAGE_TEXT8 + mov ecx, eax + shl ecx, 16 + or [rsp + 0*4], ecx + + ; Copy the text string into the packet data on the stack. + mov rsi, rsi ; Source string in rsi. + lea rdi, [rsp + 4*4] ; Destination + cld ; Move forward + mov ecx, r8d ; String length. + rep movsb ; Copy. + + mov rdx, rax ; packet u32 count + imul rdx, 4 + mov rax, SYSCALL_WRITE + mov rdi, QWORD [rsp + 1024 - 8] ; fd + lea rsi, [rsp] + syscall + + cmp rax, rdx + jnz die + + add rsp, 1024 + + pop rbp + ret + +_start: +global _start:function + call x11_connect_to_server + mov r15, rax ; Store the socket file descriptor in r15. + + mov rdi, rax + call x11_send_handshake + + mov r12d, eax ; Store the window root id in r12. + + call x11_next_id + mov r13d, eax ; Store the gc_id in r13. + + call x11_next_id + mov r14d, eax ; Store the font_id in r14. + + mov rdi, r15 + mov esi, r14d + call x11_open_font + + + mov rdi, r15 + mov esi, r13d + mov edx, r12d + mov ecx, r14d + call x11_create_gc + + call x11_next_id + + mov ebx, eax ; Store the window id in ebx. + + mov rdi, r15 ; socket fd + mov esi, eax + mov edx, r12d + mov ecx, [root_visual_id] + mov r8d, 200 | (200 << 16) ; x and y are 200 + %define WINDOW_W 800 + %define WINDOW_H 600 + mov r9d, WINDOW_W | (WINDOW_H << 16) + call x11_create_window + + mov rdi, r15 ; socket fd + mov esi, ebx + call x11_map_window + + mov rdi, r15 ; socket fd + call set_fd_non_blocking + + mov rdi, r15 ; socket fd + mov esi, ebx ; window id + mov edx, r13d ; gc id + call poll_messages + + ; The end. + mov rax, SYSCALL_EXIT + mov rdi, 0 + syscall diff --git a/main.asm.bak1 b/main.asm.bak1 new file mode 100644 index 0000000..2263e48 --- /dev/null +++ b/main.asm.bak1 @@ -0,0 +1,623 @@ +; Build with: nasm -f elf64 -g main.nasm && ld main.o -static -o main + +BITS 64 ; 64 bits. +CPU X64 ; Target the x86_64 family of CPUs. + +section .rodata + +sun_path: db "/tmp/.X11-unix/X0", 0 +static sun_path:data + +hello_world: db "Hello, world!" +static hello_world:data + +section .data + +id: dd 0 +static id:data + +id_base: dd 0 +static id_base:data + +id_mask: dd 0 +static id_mask:data + +root_visual_id: dd 0 +static root_visual_id:data + + +section .text + +%define AF_UNIX 1 +%define SOCK_STREAM 1 + +%define SYSCALL_READ 0 +%define SYSCALL_WRITE 1 +%define SYSCALL_POLL 7 +%define SYSCALL_SOCKET 41 +%define SYSCALL_CONNECT 42 +%define SYSCALL_EXIT 60 +%define SYSCALL_FCNTL 72 + +; Create a UNIX domain socket and connect to the X11 server. +; @returns The socket file descriptor. +x11_connect_to_server: +static x11_connect_to_server:function + push rbp + mov rbp, rsp + + ; Open a Unix socket: socket(2). + mov rax, SYSCALL_SOCKET + mov rdi, AF_UNIX ; Unix socket. + mov rsi, SOCK_STREAM ; Stream oriented. + mov rdx, 0 ; Automatic protocol. + syscall + + cmp rax, 0 + jle die + + mov rdi, rax ; Store socket fd in `rdi` for the remainder of the function. + + sub rsp, 112 ; Store struct sockaddr_un on the stack. + + mov WORD [rsp], AF_UNIX ; Set sockaddr_un.sun_family to AF_UNIX + ; Fill sockaddr_un.sun_path with: "/tmp/.X11-unix/X0". + lea rsi, sun_path + mov r12, rdi ; Save the socket file descriptor in `rdi` in `r12`. + lea rdi, [rsp + 2] + cld ; Move forward + mov ecx, 19 ; Length is 19 with the null terminator. + rep movsb ; Copy. + + ; Connect to the server: connect(2). + mov rax, SYSCALL_CONNECT + mov rdi, r12 + lea rsi, [rsp] + %define SIZEOF_SOCKADDR_UN 2+108 + mov rdx, SIZEOF_SOCKADDR_UN + syscall + + cmp rax, 0 + jne die + + mov rax, rdi ; Return the socket fd. + + add rsp, 112 + pop rbp + ret + +; Send the handshake to the X11 server and read the returned system information. +; @param rdi The socket file descriptor +; @returns The window root id (uint32_t) in rax. +x11_send_handshake: +static x11_send_handshake:function + push rbp + mov rbp, rsp + + sub rsp, 1<<15 + mov BYTE [rsp + 0], 'l' ; Set order to 'l'. + mov WORD [rsp + 2], 11 ; Set major version to 11. + + ; Send the handshake to the server: write(2). + mov rax, SYSCALL_WRITE + mov rdi, rdi + lea rsi, [rsp] + mov rdx, 12 + syscall + + cmp rax, 12 ; Check that all bytes were written. + jnz die + + ; Read the server response: read(2). + ; Use the stack for the read buffer. + ; The X11 server first replies with 8 bytes. Once these are read, it replies with a much bigger message. + mov rax, SYSCALL_READ + mov rdi, rdi + lea rsi, [rsp] + mov rdx, 8 + syscall + + cmp rax, 8 ; Check that the server replied with 8 bytes. + jnz die + + cmp BYTE [rsp], 1 ; Check that the server sent 'success' (first byte is 1). + jnz die + + ; Read the rest of the server response: read(2). + ; Use the stack for the read buffer. + mov rax, SYSCALL_READ + mov rdi, rdi + lea rsi, [rsp] + mov rdx, 1<<15 + syscall + + cmp rax, 0 ; Check that the server replied with something. + jle die + + ; Set id_base globally. + mov edx, DWORD [rsp + 4] + mov DWORD [id_base], edx + + ; Set id_mask globally. + mov edx, DWORD [rsp + 8] + mov DWORD [id_mask], edx + + ; Read the information we need, skip over the rest. + lea rdi, [rsp] ; Pointer that will skip over some data. + + mov cx, WORD [rsp + 16] ; Vendor length (v). + movzx rcx, cx + + mov al, BYTE [rsp + 21]; Number of formats (n). + movzx rax, al ; Fill the rest of the register with zeroes to avoid garbage values. + imul rax, 8 ; sizeof(format) == 8 + + add rdi, 32 ; Skip the connection setup + + ; Skip over padding. + add rdi, 3 + and rdi, -4 + + add rdi, rcx ; Skip over the vendor information (v). + add rdi, rax ; Skip over the format information (n*8). + + mov eax, DWORD [rdi] ; Store (and return) the window root id. + + ; Set the root_visual_id globally. + mov edx, DWORD [rdi + 32] + mov DWORD [root_visual_id], edx + + add rsp, 1<<15 + pop rbp + ret + +; Increment the global id. +; @return The new id. +x11_next_id: +static x11_next_id:function + push rbp + mov rbp, rsp + + mov eax, DWORD [id] ; Load global id. + + mov edi, DWORD [id_base] ; Load global id_base. + mov edx, DWORD [id_mask] ; Load global id_mask. + + ; Return: id_mask & (id) | id_base + and eax, edx + or eax, edi + + add DWORD [id], 1 ; Increment id. + + pop rbp + ret + +; Open the font on the server side. +; @param rdi The socket file descriptor. +; @param esi The font id. +x11_open_font: +static x11_open_font:function + push rbp + mov rbp, rsp + + %define OPEN_FONT_NAME_BYTE_COUNT 5 + %define OPEN_FONT_PADDING ((4 - (OPEN_FONT_NAME_BYTE_COUNT % 4)) % 4) + %define OPEN_FONT_PACKET_U32_COUNT (3 + (OPEN_FONT_NAME_BYTE_COUNT + OPEN_FONT_PADDING) / 4) + %define X11_OP_REQ_OPEN_FONT 0x2d + + sub rsp, 6*8 + mov DWORD [rsp + 0*4], X11_OP_REQ_OPEN_FONT | (OPEN_FONT_NAME_BYTE_COUNT << 16) + mov DWORD [rsp + 1*4], esi + mov DWORD [rsp + 2*4], OPEN_FONT_NAME_BYTE_COUNT + mov BYTE [rsp + 3*4 + 0], 'f' + mov BYTE [rsp + 3*4 + 1], 'i' + mov BYTE [rsp + 3*4 + 2], 'x' + mov BYTE [rsp + 3*4 + 3], 'e' + mov BYTE [rsp + 3*4 + 4], 'd' + + + mov rax, SYSCALL_WRITE + mov rdi, rdi + lea rsi, [rsp] + mov rdx, OPEN_FONT_PACKET_U32_COUNT*4 + syscall + + cmp rax, OPEN_FONT_PACKET_U32_COUNT*4 + jnz die + + add rsp, 6*8 + + pop rbp + ret + +; Create a X11 graphical context. +; @param rdi The socket file descriptor. +; @param esi The graphical context id. +; @param edx The window root id. +; @param ecx The font id. +x11_create_gc: +static x11_create_gc:function + push rbp + mov rbp, rsp + + sub rsp, 8*8 + +%define X11_OP_REQ_CREATE_GC 0x37 +%define X11_FLAG_GC_BG 0x00000004 +%define X11_FLAG_GC_FG 0x00000008 +%define X11_FLAG_GC_FONT 0x00004000 +%define X11_FLAG_GC_EXPOSE 0x00010000 + +%define CREATE_GC_FLAGS X11_FLAG_GC_BG | X11_FLAG_GC_FG | X11_FLAG_GC_FONT +%define CREATE_GC_PACKET_FLAG_COUNT 3 +%define CREATE_GC_PACKET_U32_COUNT (4 + CREATE_GC_PACKET_FLAG_COUNT) +%define MY_COLOR_RGB 0x0000ffff + + mov DWORD [rsp + 0*4], X11_OP_REQ_CREATE_GC | (CREATE_GC_PACKET_U32_COUNT<<16) + mov DWORD [rsp + 1*4], esi + mov DWORD [rsp + 2*4], edx + mov DWORD [rsp + 3*4], CREATE_GC_FLAGS + mov DWORD [rsp + 4*4], MY_COLOR_RGB + mov DWORD [rsp + 5*4], 0 + mov DWORD [rsp + 6*4], ecx + + mov rax, SYSCALL_WRITE + mov rdi, rdi + lea rsi, [rsp] + mov rdx, CREATE_GC_PACKET_U32_COUNT*4 + syscall + + cmp rax, CREATE_GC_PACKET_U32_COUNT*4 + jnz die + + add rsp, 8*8 + + pop rbp + ret + +; Create the X11 window. +; @param rdi The socket file descriptor. +; @param esi The new window id. +; @param edx The window root id. +; @param ecx The root visual id. +; @param r8d Packed x and y. +; @param r9d Packed w and h. +x11_create_window: +static x11_create_window:function + push rbp + mov rbp, rsp + + %define X11_OP_REQ_CREATE_WINDOW 0x01 + %define X11_FLAG_WIN_BG_COLOR 0x00000002 + %define X11_EVENT_FLAG_KEY_RELEASE 0x0002 + %define X11_EVENT_FLAG_EXPOSURE 0x8000 + %define X11_FLAG_WIN_EVENT 0x00000800 + + %define CREATE_WINDOW_FLAG_COUNT 2 + %define CREATE_WINDOW_PACKET_U32_COUNT (8 + CREATE_WINDOW_FLAG_COUNT) + %define CREATE_WINDOW_BORDER 1 + %define CREATE_WINDOW_GROUP 1 + + sub rsp, 12*8 + + mov DWORD [rsp + 0*4], X11_OP_REQ_CREATE_WINDOW | (CREATE_WINDOW_PACKET_U32_COUNT << 16) + mov DWORD [rsp + 1*4], esi + mov DWORD [rsp + 2*4], edx + mov DWORD [rsp + 3*4], r8d + mov DWORD [rsp + 4*4], r9d + mov DWORD [rsp + 5*4], CREATE_WINDOW_GROUP | (CREATE_WINDOW_BORDER << 16) + mov DWORD [rsp + 6*4], ecx + mov DWORD [rsp + 7*4], X11_FLAG_WIN_BG_COLOR | X11_FLAG_WIN_EVENT + mov DWORD [rsp + 8*4], 0 + mov DWORD [rsp + 9*4], X11_EVENT_FLAG_KEY_RELEASE | X11_EVENT_FLAG_EXPOSURE + + + mov rax, SYSCALL_WRITE + mov rdi, rdi + lea rsi, [rsp] + mov rdx, CREATE_WINDOW_PACKET_U32_COUNT*4 + syscall + + cmp rax, CREATE_WINDOW_PACKET_U32_COUNT*4 + jnz die + + add rsp, 12*8 + + pop rbp + ret + +; Map a X11 window. +; @param rdi The socket file descriptor. +; @param esi The window id. +x11_map_window: +static x11_map_window:function + push rbp + mov rbp, rsp + + sub rsp, 16 + + %define X11_OP_REQ_MAP_WINDOW 0x08 + mov DWORD [rsp + 0*4], X11_OP_REQ_MAP_WINDOW | (2<<16) + mov DWORD [rsp + 1*4], esi + + mov rax, SYSCALL_WRITE + mov rdi, rdi + lea rsi, [rsp] + mov rdx, 2*4 + syscall + + cmp rax, 2*4 + jnz die + + add rsp, 16 + + pop rbp + ret + +; Read the X11 server reply. +; @return The message code in al. +x11_read_reply: +static x11_read_reply:function + push rbp + mov rbp, rsp + + sub rsp, 32 + + mov rax, SYSCALL_READ + mov rdi, rdi + lea rsi, [rsp] + mov rdx, 32 + syscall + + cmp rax, 1 + jle die + + mov al, BYTE [rsp] + + add rsp, 32 + + pop rbp + ret + +die: + mov rax, SYSCALL_EXIT + mov rdi, 1 + syscall + + +; Set a file descriptor in non-blocking mode. +; @param rdi The file descriptor. +set_fd_non_blocking: +static set_fd_non_blocking:function + push rbp + mov rbp, rsp + + %define F_GETFL 3 + %define F_SETFL 4 + + %define O_NONBLOCK 2048 + + mov rax, SYSCALL_FCNTL + mov rdi, rdi + mov rsi, F_GETFL + mov rdx, 0 + syscall + + cmp rax, 0 + jl die + + ; `or` the current file status flag with O_NONBLOCK. + mov rdx, rax + or rdx, O_NONBLOCK + + mov rax, SYSCALL_FCNTL + mov rdi, rdi + mov rsi, F_SETFL + mov rdx, rdx + syscall + + cmp rax, 0 + jl die + + pop rbp + ret + +; Poll indefinitely messages from the X11 server with poll(2). +; @param rdi The socket file descriptor. +; @param esi The window id. +; @param edx The gc id. +poll_messages: +static poll_messages:function + push rbp + mov rbp, rsp + + sub rsp, 32 + + %define POLLIN 0x001 + %define POLLPRI 0x002 + %define POLLOUT 0x004 + %define POLLERR 0x008 + %define POLLHUP 0x010 + %define POLLNVAL 0x020 + + mov DWORD [rsp + 0*4], edi + mov DWORD [rsp + 1*4], POLLIN + + mov DWORD [rsp + 16], esi ; window id + mov DWORD [rsp + 20], edx ; gc id + mov BYTE [rsp + 24], 0 ; exposed? (boolean) + + .loop: + mov rax, SYSCALL_POLL + lea rdi, [rsp] + mov rsi, 1 + mov rdx, -1 + syscall + + cmp rax, 0 + jle die + + cmp DWORD [rsp + 2*4], POLLERR + je die + + cmp DWORD [rsp + 2*4], POLLHUP + je die + + mov rdi, [rsp + 0*4] + call x11_read_reply + + %define X11_EVENT_EXPOSURE 0xc + cmp eax, X11_EVENT_EXPOSURE + jnz .received_other_event + + .received_exposed_event: + mov BYTE [rsp + 24], 1 ; Mark as exposed. + + .received_other_event: + + cmp BYTE [rsp + 24], 1 ; exposed? + jnz .loop + + .draw_text: + mov rdi, [rsp + 0*4] ; socket fd + lea rsi, [hello_world] ; string + mov edx, 13 ; length + mov ecx, [rsp + 16] ; window id + mov r8d, [rsp + 20] ; gc id + mov r9d, 100 ; x + shl r9d, 16 + or r9d, 100 ; y + call x11_draw_text + + + jmp .loop + + + add rsp, 32 + pop rbp + ret + +; Draw text in a X11 window with server-side text rendering. +; @param rdi The socket file descriptor. +; @param rsi The text string. +; @param edx The text string length in bytes. +; @param ecx The window id. +; @param r8d The gc id. +; @param r9d Packed x and y. +x11_draw_text: +static x11_draw_text:function + push rbp + mov rbp, rsp + + sub rsp, 1024 + + mov DWORD [rsp + 1*4], ecx ; Store the window id directly in the packet data on the stack. + mov DWORD [rsp + 2*4], r8d ; Store the gc id directly in the packet data on the stack. + mov DWORD [rsp + 3*4], r9d ; Store x, y directly in the packet data on the stack. + + mov r8d, edx ; Store the string length in r8 since edx will be overwritten next. + mov QWORD [rsp + 1024 - 8], rdi ; Store the socket file descriptor on the stack to free the register. + + ; Compute padding and packet u32 count with division and modulo 4. + mov eax, edx ; Put dividend in eax. + mov ecx, 4 ; Put divisor in ecx. + cdq ; Sign extend. + idiv ecx ; Compute eax / ecx, and put the remainder (i.e. modulo) in edx. + ; LLVM optimizer magic: `(4-x)%4 == -x & 3`, for some reason. + neg edx + and edx, 3 + mov r9d, edx ; Store padding in r9. + + mov eax, r8d + add eax, r9d + shr eax, 2 ; Compute: eax /= 4 + add eax, 4 ; eax now contains the packet u32 count. + + + %define X11_OP_REQ_IMAGE_TEXT8 0x4c + mov DWORD [rsp + 0*4], r8d + shl DWORD [rsp + 0*4], 8 + or DWORD [rsp + 0*4], X11_OP_REQ_IMAGE_TEXT8 + mov ecx, eax + shl ecx, 16 + or [rsp + 0*4], ecx + + ; Copy the text string into the packet data on the stack. + mov rsi, rsi ; Source string in rsi. + lea rdi, [rsp + 4*4] ; Destination + cld ; Move forward + mov ecx, r8d ; String length. + rep movsb ; Copy. + + mov rdx, rax ; packet u32 count + imul rdx, 4 + mov rax, SYSCALL_WRITE + mov rdi, QWORD [rsp + 1024 - 8] ; fd + lea rsi, [rsp] + syscall + + cmp rax, rdx + jnz die + + add rsp, 1024 + + pop rbp + ret + +_start: +global _start:function + call x11_connect_to_server + mov r15, rax ; Store the socket file descriptor in r15. + + mov rdi, rax + call x11_send_handshake + + mov r12d, eax ; Store the window root id in r12. + + call x11_next_id + mov r13d, eax ; Store the gc_id in r13. + + call x11_next_id + mov r14d, eax ; Store the font_id in r14. + + mov rdi, r15 + mov esi, r14d + call x11_open_font + + + mov rdi, r15 + mov esi, r13d + mov edx, r12d + mov ecx, r14d + call x11_create_gc + + call x11_next_id + + mov ebx, eax ; Store the window id in ebx. + + mov rdi, r15 ; socket fd + mov esi, eax + mov edx, r12d + mov ecx, [root_visual_id] + mov r8d, 200 | (200 << 16) ; x and y are 200 + %define WINDOW_W 800 + %define WINDOW_H 600 + mov r9d, WINDOW_W | (WINDOW_H << 16) + call x11_create_window + + mov rdi, r15 ; socket fd + mov esi, ebx + call x11_map_window + + mov rdi, r15 ; socket fd + call set_fd_non_blocking + + mov rdi, r15 ; socket fd + mov esi, ebx ; window id + mov edx, r13d ; gc id + call poll_messages + + ; The end. + mov rax, SYSCALL_EXIT + mov rdi, 0 + syscall diff --git a/main.o b/main.o new file mode 100644 index 0000000000000000000000000000000000000000..a7e5e24e65a81c79a2c89b876b64ac68f065ac9c GIT binary patch literal 4912 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 QKFIyB@?#20HLWmDF6Tf literal 0 HcmV?d00001