1 # write: like _write, but also support in-memory streams in addition to file 2 # descriptors. 3 # 4 # Our first dependency-injected and testable primitive. We can pass it either 5 # a file descriptor or an address to a stream. If a file descriptor is passed 6 # in, we _write to it using the right syscall. If a 'fake file descriptor' or 7 # stream is passed in, we append to the stream. This lets us redirect output 8 # in tests and check it later. 9 # 10 # We assume our data segment will never begin at an address shorter than 11 # 0x08000000, so any smaller arguments are assumed to be real file descriptors. 12 # 13 # A stream looks like this: 14 # read: int # index at which to read next 15 # write: int # index at which writes go 16 # data: (array byte) # prefixed by size as usual 17 18 == code 19 # instruction effective address register displacement immediate 20 # . op subop mod rm32 base index scale r32 21 # . 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 22 23 # TODO: come up with a way to signal when a write to disk fails 24 write: # f: fd or (addr stream byte), s: (addr array byte) 25 # . prologue 26 55/push-ebp 27 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 28 # if (f < 0x08000000) _write(f, s) and return # f can't be a user-mode address, so treat it as a kernel file descriptor 29 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 0x08000000/imm32 # compare *(ebp+8) 30 73/jump-if-addr>= $write:fake/disp8 31 # . . push args 32 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) 33 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 34 # . . call 35 e8/call _write/disp32 36 # . . discard args 37 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 38 eb/jump $write:end/disp8 39 $write:fake: 40 # otherwise, treat 'f' as a stream to append to 41 # . save registers 42 50/push-eax 43 51/push-ecx 44 52/push-edx 45 53/push-ebx 46 # ecx = f 47 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx 48 # edx = f->write 49 8b/copy 0/mod/indirect 1/rm32/ecx . . . 2/r32/edx . . # copy *ecx to edx 50 # ebx = f->size 51 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 3/r32/ebx 8/disp8 . # copy *(ecx+8) to ebx 52 # eax = _append-3(&f->data[f->write], &f->data[f->size], s) 53 # . . push s 54 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) 55 # . . push &f->data[f->size] 56 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ecx 3/index/ebx . 3/r32/ebx 0xc/disp8 . # copy ecx+ebx+12 to ebx 57 53/push-ebx 58 # . . push &f->data[f->write] 59 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ecx 2/index/edx . 3/r32/ebx 0xc/disp8 . # copy ecx+edx+12 to ebx 60 53/push-ebx 61 # . . call 62 e8/call _append-3/disp32 63 # . . discard args 64 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 65 # f->write += eax 66 01/add 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # add eax to *ecx 67 # . restore registers 68 5b/pop-to-ebx 69 5a/pop-to-edx 70 59/pop-to-ecx 71 58/pop-to-eax 72 $write:end: 73 # . epilogue 74 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 75 5d/pop-to-ebp 76 c3/return 77 78 test-write-single: 79 # clear-stream(_test-stream) 80 # . . push args 81 68/push _test-stream/imm32 82 # . . call 83 e8/call clear-stream/disp32 84 # . . discard args 85 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 86 # write(_test-stream, "Ab") 87 # . . push args 88 68/push "Ab"/imm32 89 68/push _test-stream/imm32 90 # . . call 91 e8/call write/disp32 92 # . . discard args 93 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 94 # check-ints-equal(*_test-stream->data, 41/A 62/b 00 00, msg) 95 # . . push args 96 68/push "F - test-write-single"/imm32 97 68/push 0x006241/imm32/Ab 98 # . . push *_test-stream->data 99 b8/copy-to-eax _test-stream/imm32 100 ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 0xc/disp8 . # push *(eax+12) 101 # . . call 102 e8/call check-ints-equal/disp32 103 # . . discard args 104 81 0/subop/add 3/mod/direct 4/rm32/esp . pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */;;; Finite State Machine Interpreter (FSM) to game :which fsm thing word "mach :which end to fsm :machine cleartext setcursor [0 3] localmake "start startpart :machine localmake "moves movepart :machine localmake "accept acceptpart :machine fsm1 :start end to fsm1 :here ifelse memberp :here :accept [accept] [reject] fsm1 (fsmnext :here readchar) end to fsmnext :here :input blank if memberp :input (list char 13 char 10) ~ [print ifelse memberp :here :accept ["| ACCEPT|] ["| REJECT|] output :start] type :input catch "error [output last find [fsmtest :here :input ?] :moves] output -1 end to fsmtest :here :input :move output and (equalp :here arrowtail :move) ~ (memberp :input arrowtext :move) end ;; Display machine state to accept display "accept end to reject display "reject end to blank display "| | end to display :text localmake "oldpos cursor setcursor [15 1] type :text setcursor :oldpos end ;; Data abstraction for machines to startpart :machine output first :machine end to movepart :machine output first bf :machine end to acceptpart :machine output last :machine end to make.machine :start :moves :accept output (list :start :moves :accept) end ;; Data abstraction for arrows to arrowtail :arrow output first :arrow end to arrowtext :arrow output first butfirst :arrow end to arrowhead :arrow output last :arrow end to make.arrow :tai