From 3d6450f216f98a5348f86542444737c0a6178fc4 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Tue, 16 Oct 2018 23:44:31 -0700 Subject: [PATCH] 4707 - subx: dependency-injected write() primitive --- subx/056write.subx | 251 ++++++++++++++++++++++++++++++++++++++++++ subx/apps/crenshaw2-1 | Bin 2554 -> 2979 bytes subx/apps/factorial | Bin 2581 -> 3006 bytes 3 files changed, 251 insertions(+) create mode 100644 subx/056write.subx diff --git a/subx/056write.subx b/subx/056write.subx new file mode 100644 index 00000000..46cc985b --- /dev/null +++ b/subx/056write.subx @@ -0,0 +1,251 @@ +# write: like _write, but also support in-memory output streams (`ostream`s) +# in addition to file descriptors. +# +# Our first dependency-injected and testable primitive. We can pass it either +# a file descriptor or an address to an output stream or ostream. If a file +# descriptor is passed in, we _write to it using the right syscall. If a 'fake +# file descriptor' or ostream is passed in, we append to the output stream. +# This lets us redirect output in tests and check it later. +# +# We assume our data segment will never begin at an address shorter than +# 0x08000000, so any smaller arguments are assumed to be real file descriptors. +# +# An ostream looks like this: +# write: int # index at which writes go +# data: (array byte) # prefixed by length as usual + +== data + +# In-memory ostream for tests to write() to. +# Also illustrates the layout of ostreams. +Test-ostream: + # current write index + 00 00 00 00 + # length (= 8) + 08 00 00 00 + # data + 00 00 00 00 00 00 00 00 # 8 bytes + +== code + +# instruction effective address operand displacement immediate +# op subop mod rm32 base index scale r32 +# 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes + +# main: (manual test if this is the last file loaded) +#? e8/call test-write-appends/disp32 + e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. + # syscall(exit, Num-test-failures) + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 1/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + b8/copy-to-EAX 1/imm32 + cd/syscall 0x80/imm8 + +write: # f : fd or (address stream), s : (address array byte) -> + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # save registers + 50/push-EAX + 51/push-ECX + 52/push-EDX + 53/push-EBX + 56/push-ESI + # if (f < 0x08000000) _write(f, s), return # f can't be a user-mode address, so treat it as a kernel file descriptor + 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 0x08000000/imm32 # compare *(EBP+8) + 7f/jump-if-greater $write:else/disp8 + # push args + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + # call + e8/call _write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + eb/jump $write:end/disp8 +$write:else: + # otherwise, treat 'f' as a stream to append to + # + # pseudocode: + # destend = &f.data[t.length] + # oldw = f.write + # if s.length == 0 return + # f.write += s.length + # dest = &f.data[oldw] + # srcend = &s.data[s.length] + # src = &s.data[0] + # while true: + # if src >= srcend break + # if dest >= destend break # for now silently ignore filled up ostream buffer + # *dest = *src + # ++src + # ++dest + # + # key registers to set up for the loop: + # EAX/dest, ECX/destend, EBX/src, ESI/srcend + # we save EDX for byte operations (has to be one of the first 4 registers) + # + # register setup before the loop: + # EAX = *(EBP+8) # f + # EBX = *(EBP+12) # s + # ECX = *(EAX+4) # f.length + # ECX = EAX+8+ECX # destend = &f.data[f.length] + # ESI = *EAX # oldw = f.write + # EDX = *EBX # s.length + # *EAX = *EAX + EDX # update f.write (allowed to go past f.length) + # # do this here just because it's convenient + # EAX = EAX+8+ESI # dest = &f.data[f.write] + # ESI = EBX+4+EDX # srcend = &s.data[s.length] + # EBX = EBX+4 # src = &s.data[0] + # + # EAX = f + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX + # EBX = s + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX + # ECX = f.length + 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy *(EAX+4) to ECX + # ECX/destend = &f.data[f.length] + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 8/disp8 . # copy EAX+ECX+8 to ECX + # ESI/oldw = f.write + 8b/copy 0/mod/indirect 0/rm32/EAX . . . 6/r32/ESI . . # copy *EAX to ESI + # EDX = s.length + 8b/copy 0/mod/indirect 3/rm32/EBX . . . 2/r32/EDX . . # copy *EBX to EDX + # if EDX == 0 return + 81 7/subop/compare 3/mod/direct 2/rm32/EDX . . . . . 0/imm32 # compare EDX + 74/jump-if-equal $write:end/disp8 + # f.write += s.length + 01/add 0/mod/indirect 0/rm32/EAX . . . 2/r32/EDX . . # add EDX to *EAX + # EAX/dest = &f.data[oldw] + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 6/index/ESI . 0/r32/EAX 8/disp8 . # copy EAX+ESI+8 to EAX + # ESI/srcend = &s.data[s.length] + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 3/base/EBX 2/index/EDX . 6/r32/ESI 4/disp8 . # copy EBX+EDX+4 to ESI + # EBX/src = &s.data + 81 0/subop/add 3/mod/direct 3/rm32/EBX . . . . . 4/imm32 # add to EBX + # while (true) +$write:loop: + # if EBX/src >= ESI/srcend break + 39/compare 3/mod/direct 3/rm32/EBX . . . 6/r32/ESI . . # compare EBX with ESI + 7d/jump-if-greater-or-equal $write:end/disp8 + # if EAX/dest >= ECX/destend break (for now silently ignore filled up ostream buffer) + 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX + 7d/jump-if-greater-or-equal $write:end/disp8 + # copy one byte + 8a/copy-byte 0/mod/indirect 3/rm32/EBX . . . 2/r32/DL . . # copy byte at *EBX to DL + 88/copy-byte 0/mod/indirect 0/rm32/EAX . . . 2/r32/DL . . # copy byte at DL to *EAX + # updates + 40/increment-EAX + 43/increment-EBX + eb/jump $write:loop/disp8 +$write:end: + # restore registers + 5e/pop-to-ESI + 5b/pop-to-EBX + 5a/pop-to-EDX + 59/pop-to-ECX + 58/pop-to-EAX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +clear-ostream: # f : (address ostream) + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # save registers + 50/push-EAX + 51/push-ECX + # EAX = f + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX + # ECX = f.length + 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy *(EAX+4) to ECX + # ECX = &f.data[f.length] + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 8/disp8 . # copy EAX+ECX+8 to ECX + # f.write = 0 + c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX + # EAX = f.data + 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 8/imm32 # add to EAX + # while (true) +$clear-ostream:loop: + # if EAX >= ECX break + 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX + 7d/jump-if-greater-or-equal $clear-ostream:end/disp8 + # *EAX = 0 + c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX + # EAX += 4 + 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 4/imm32 # add to EAX + eb/jump $clear-ostream:loop/disp8 +$clear-ostream:end: + # restore registers + 59/pop-to-ECX + 58/pop-to-EAX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +test-write-single: + # clear-ostream(Test-ostream) + # push args + 68/push Test-ostream/imm32 + # call + e8/call clear-ostream/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # write(Test-ostream, "Ab") + # push args + 68/push "Ab"/imm32 + 68/push Test-ostream/imm32 + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(*Test-ostream.data, 41/A 62/b 00 00, msg) + # push args + 68/push "F - test-write-single"/imm32 + 68/push 0x006241/imm32/Ab + # push *Test-ostream.data + b8/copy-to-EAX Test-ostream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 8/disp8 . # push *(EAX+8) + # call + e8/call check-ints-equal/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # end + c3/return + +test-write-appends: + # clear-ostream(Test-ostream) + # push args + 68/push Test-ostream/imm32 + # call + e8/call clear-ostream/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # write(Test-ostream, "C") + # push args + 68/push "C"/imm32 + 68/push Test-ostream/imm32 + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(Test-ostream, "D") + # push args + 68/push "D"/imm32 + 68/push Test-ostream/imm32 + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(*Test-ostream.data, 43/C 44/D 00 00, msg) + # push args + 68/push "F - test-write-appends"/imm32 + 68/push 0x00004443/imm32/C-D + # push *Test-ostream.data + b8/copy-to-EAX Test-ostream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 8/disp8 . # push *(EAX+8) + # call + e8/call check-ints-equal/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # end + c3/return diff --git a/subx/apps/crenshaw2-1 b/subx/apps/crenshaw2-1 index a924209d3d02d1e4335c5a631e13a82bf0397339..52814a1d424c105df632d9c8387b440bff7191e3 100755 GIT binary patch literal 2979 zcmbVNUu+ab9G-)g+a9N!G^h}1E=m(MZY?Q|p8wM0+6ty5t%yo1*6VfcUO2YL-kzli zrCf-HcI}%lK42PoFyR5?zob$hz-y>6;eq&|(P;XB$?6#+(HGIW{=S*LorPWl;gbE` z>^I-1(7*rXsoxHi`A}R)h%6$XmCj>aO1QK74w1_5B1eaz&X=z13h(rOEQ^CXIf!Ka zHGs(>vJO3NUUBZF%_%KJsk1EDlTu>bmvH}sE|)mwgTG4f1i(~hK=(XN&eL^v(HPfx zsiPX4Gqh!5vQzbc({vU)l)pC6JgQDhD;2Ub8nAMEC|2UtM21FJHi0*IrO;ssPh?>c-3OgJ|ZQ)@Fmts zi3>h>Ra$%zU@FoX@pTW}s2i8MOTNTeDRBiL*6B-ZkrK}P)V8-?U-2k-e;ZgTi)u>`U)Ih`4Q*b_(Yij_A!`the;`=X3uO0AH z;Jzif9RMNO>l_Yn^MJ>|?U&qR0EKQ>fIAEL18`d;_bfo6yE(w+MY)M^QIv~xw=p(V zmgj#jteC%xT%1bP@&CDgqFg#Yfb}|Q(yC?POEz5@_6u|;Y_#dRC|yLYaUTBtAmE3g+a|fM0~ESHQbtwxU7s74+*zsnQGol6&;3Ex=C_jjW`G-6 zU!B8;k{bg^B%1I30JqEMo{-!LgOeT7A;*siHMS!Lz7C( z`!+a@*Id`>L&*I@;S9nu!rT8+_ypm5gwQgDC4?}!;j|#^LpX|X8sTdSWm0EYc&%S8 zoNftrKRvX~F3(90W%yl;i@v+!^NDx^HRIZmyq2-FVc3@(!a3`X zt8q&!Sn8OWwKTPmHAZq8YAc1%dG|_6I-l2!;X(}w%S>l(A|WWA1ZlI)!2v|Cb|`O+ ztGTS9fq>tG^!O9aj2_CTO-&WFv8<(g&4Ojp?BNaUpx+}mNi#-`TB)7_DrV)Nhl}Z) zDn=@B;&8^}x76a(B7zyJc*|+Vh@}@Of!}W{Mlp)HoO)0*4Xrws8dzuAG}GhWppVxY zbhUK3-k`*zwIpf{&KDvxhcma4i6!4gnkcsIw>Z;+K5AN;sq(qv=u$llx87oMZq%TI cBXUuk8jepqYs53UmN}?q4XYq_5$B`jf4h-!B20m4{#N*n-W@xP)n=Wh0uiyjh0$Bon^(+ikoQ=3n5*! z4A_lb=}PhaAW*uRK;1+T6a;Ni5WI*B#Y!=1$8%=L{eyvf?#%hWd+vYlkXw73oeM)f z2W-ROunhcm8^#Vy0p2mP91|(S;D8m^y;t_)Ou>JTl4rtF9#)p?E*_()!|B93tv&Cv z2K&W)q0PKDLv<3*)m;tLYV4vh>#-qq2iuhEX*d;&9jzU6H-i~-*`>2ensU7Z#W10-NJOX8im)Xjr_Jx%+$_%Ra!vsq%noIB z@aX@+2PL>yXWW;JB2Dzc-hvOQoY}C0W@^|!L;pNPJ(eym(B!?0sO9)Zk`v6+4#>_4 zyF`=0BkEEcyGHvZ*^;ntXePPG6LudKhTazaZNl9GTW+wCK6>abuCg+iUg2CT0dLHn zt9%^|)i@HWT{uS3n)G{JB!oazRT7FclkEzaS|8%FX2*oRN;5e|rj310yUyGmVOJ%0 z9ZQhw$H(}lVMpLC34WBoF`Lh{g5SwU>ZXY@?(%vYJFeLW!cGW#*65Sd-PG_kfo}_Z zq%HVuCirL<%%7Pc=j3zpPuU+cE;O=m!6m2v#PshdQco$)AIJ^LD&-Sp=U>Pd${uX0 Y`Y2iC^IKX;mJXz9Xy zJA3Z=&Uf#*-@QAvr?0oBriOW+b!h8N@KfFptQ+qBu%q!fC!a{ z3UTWm3dRBv+4OuFtV<`Jo4z{0UrAE}biCPoF(w%myboAcY}W&7PP< zQwW=TYiIi;$!s?pjT$;h_UB zV2-q^WIq>jL>7_Hj0>nh37fs&5UE^qa#VP2T@>poVO<=2l7mQAUjvvJWvq-5yC7Nj zleO-e9rl!vIOR*&|G`KuaoPue5#Sks-8})_3p6>;%e_?N7B5vaiuED2l)ImGO(d|` zCH+Zz>@1(07FJ4RWjJ7EM@_`wEkuTf*EW>As{cGh>pmH&<}Fusyj<*pmt-+Ee@%;9 zXp+{P@TZJbb8w%q_zQrE(GV+R#4dy_biZdYXCD<3-}@39gv5_NcwJchGr;apPsrCj zxKcMNbeDaJb3(!fh;{lB%|gODOYdgOtsRes_qQPuZ!EQ(M}Bu#d}(r{*&gpKg%i}e z*ZCQ#)Ri#^tNBTLpn=LoDOjI~wLJt7r!#52&-b4nH#G@(5Zt!}cNicflh%;{*980y zxI=<_8la=QC%~Npd=cC>!TlDXquUhV@}}HMxYLx2bnUIUYGB1&2e~-iUCY1ALqxfB zg5NAwinOX3IB3$9VLnTD!WNUR3*&3_8uu~2q_=X7M0@TIrLe<(F4dY`KN zvP`i?8=&LI4*K%?@9~hw9TD8!08uz;H3Yb?0^SeZPQf(* zI=WXV!_)oL=hh4EoY4I^z`f>kFN@y%U2xwFaAO<2Iea9zj{qbR&G$fn`?Ak{LvU5W zl>^-O0Pi3`#U@)@_>e@A?}NDV`v<)Cr!BcpJ&!cnW@hd8Gb-(7Uo6t^z@0v_jm0(zXwNEvit_t=bJ+#+0&q<7?`Gt(z zmSM)aQqm4zjVQJgb>Y(mTEid@X(#5KH$De3Af4Gp+6xpFoWOUViE$SN0 z9=5;<`aF7@v|K({E!9;($E+@Tq?F3aPNgCzj-_3GTQxo{BA6jNzp_ehTvv;f!0)$J zRdS_lRz9R?ImN3b2iBR=wA3-T=o8h7u9Ys<8pmf%;*r{SK3}`P{gdi{?}`Ew4p0xoBEd(ud?sPA@vUi1X3*KT5QG A8vp<4KCOXu6 z3iM+wYxc$Pu}#9fN%HgRoe3~O&vkVUNJq<(qsAelYRQMj^U@NbYB>mk@(Pi{+iO#+ zHV3tdCA=ievm>skp%RQG)N8%Ijt%Kaj_8XbERIM==K5bOrgKl(B?`{EIX4Qvx-+4Ud7wkPq4ou zH_hxS=YFOR$~{a|-P;rY58DiP#pQJ?SsI9HYkWO}FvCS>!XTY99kC0{-evZ38@m*- z_n3XlY;VZgI(Q>4x2%4n)0Jua!~sP=%cD~l>ZpeB)&=w@krQ_2{DWUXpl