7842 - new directory organization
Baremetal is now the default build target and therefore has its sources at the top-level. Baremetal programs build using the phase-2 Mu toolchain that requires a Linux kernel. This phase-2 codebase which used to be at the top-level is now under the linux/ directory. Finally, the phase-2 toolchain, while self-hosting, has a way to bootstrap from a C implementation, which is now stored in linux/bootstrap. The bootstrap C implementation uses some literate programming tools that are now in linux/bootstrap/tools. So the whole thing has gotten inverted. Each directory should build one artifact and include the main sources (along with standard library). Tools used for building it are relegated to sub-directories, even though those tools are often useful in their own right, and have had lots of interesting programs written using them. A couple of things have gotten dropped in this process: - I had old ways to run on just a Linux kernel, or with a Soso kernel. No more. - I had some old tooling for running a single test at the cursor. I haven't used that lately. Maybe I'll bring it back one day. The reorg isn't done yet. Still to do: - redo documentation everywhere. All the README files, all other markdown, particularly vocabulary.md. - clean up how-to-run comments at the start of programs everywhere - rethink what to do with the html/ directory. Do we even want to keep supporting it? In spite of these shortcomings, all the scripts at the top-level, linux/ and linux/bootstrap are working. The names of the scripts also feel reasonable. This is a good milestone to take stock at.
This commit is contained in:
parent
c6b928be29
commit
71e4f38129
|
@ -5,13 +5,6 @@
|
|||
# . 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
|
||||
|
||||
Entry: # run all tests
|
||||
#? e8/call test-compare-equal-strings/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 . . 3/r32/ebx Num-test-failures/disp32 # copy *Num-test-failures to ebx
|
||||
e8/call syscall_exit/disp32
|
||||
|
||||
string-equal?: # s: (addr array byte), benchmark: (addr array byte) -> result/eax: boolean
|
||||
# pseudocode:
|
||||
# if (s->size != benchmark->size) return false
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
# some primitives for operating on streams:
|
||||
# - clear-stream (clears everything but the data size)
|
||||
# - rewind-stream (resets read pointer)
|
||||
#
|
||||
# We need to do this in machine code because streams need to be opaque types,
|
||||
# and we don't yet support opaque types in Mu.
|
||||
|
||||
== code
|
||||
# instruction effective address register displacement immediate
|
||||
|
|
118
108write.subx
118
108write.subx
|
@ -1,46 +1,20 @@
|
|||
# write: like _write, but also support in-memory streams in addition to file
|
||||
# descriptors.
|
||||
# write: write to in-memory streams
|
||||
#
|
||||
# Our first dependency-injected and testable primitive. We can pass it either
|
||||
# a file descriptor or an address to a stream. If a file descriptor is passed
|
||||
# in, we _write to it using the right syscall. If a 'fake file descriptor' or
|
||||
# stream is passed in, we append to the 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.
|
||||
#
|
||||
# A stream looks like this:
|
||||
# read: int # index at which to read next
|
||||
# write: int # index at which writes go
|
||||
# data: (array byte) # prefixed by size as usual
|
||||
# We need to do this in machine code because streams need to be opaque types,
|
||||
# and we don't yet support opaque types in Mu.
|
||||
|
||||
== code
|
||||
# instruction effective address register 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
|
||||
|
||||
# TODO: come up with a way to signal when a write to disk fails
|
||||
write: # f: fd or (addr stream byte), s: (addr array byte)
|
||||
write: # f: (addr stream byte), s: (addr array byte)
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
||||
# if (s == 0) return
|
||||
81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 0/imm32 # compare *(ebp+12)
|
||||
74/jump-if-= $write:end/disp8
|
||||
# if (f < 0x08000000) _write(f, s) and return # f can't be a user-mode address, so treat it as a kernel file descriptor
|
||||
81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 0x08000000/imm32 # compare *(ebp+8)
|
||||
73/jump-if-addr>= $write:fake/disp8
|
||||
# . . push args
|
||||
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
|
||||
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
|
||||
# . . 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:fake:
|
||||
# otherwise, treat 'f' as a stream to append to
|
||||
# . save registers
|
||||
50/push-eax
|
||||
51/push-ecx
|
||||
|
@ -159,4 +133,88 @@ _test-stream: # (stream byte)
|
|||
00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00
|
||||
|
||||
== code
|
||||
|
||||
# 3-argument variant of _append
|
||||
_append-3: # out: (addr byte), outend: (addr byte), s: (addr array byte) -> num_bytes_appended/eax
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
||||
# . save registers
|
||||
51/push-ecx
|
||||
# eax = _append-4(out, outend, &s->data[0], &s->data[s->size])
|
||||
# . . push &s->data[s->size]
|
||||
8b/copy 1/mod/*+disp8 5/rm32/ebp . . 0/r32/eax 0x10/disp8 . # copy *(ebp+16) to eax
|
||||
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
|
||||
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
|
||||
51/push-ecx
|
||||
# . . push &s->data[0]
|
||||
8d/copy-address 1/mod/*+disp8 0/rm32/eax . . . 1/r32/ecx 4/disp8 . # copy eax+4 to ecx
|
||||
51/push-ecx
|
||||
# . . push outend
|
||||
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
|
||||
# . . push out
|
||||
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
|
||||
# . . call
|
||||
e8/call _append-4/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
|
||||
$_append-3:end:
|
||||
# . restore registers
|
||||
59/pop-to-ecx
|
||||
# . epilogue
|
||||
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
||||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
# 4-argument variant of _append
|
||||
_append-4: # out: (addr byte), outend: (addr byte), in: (addr byte), inend: (addr byte) -> num_bytes_appended/eax: int
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
||||
# . save registers
|
||||
51/push-ecx
|
||||
52/push-edx
|
||||
53/push-ebx
|
||||
56/push-esi
|
||||
57/push-edi
|
||||
# num_bytes_appended = 0
|
||||
b8/copy-to-eax 0/imm32
|
||||
# edi = out
|
||||
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi
|
||||
# edx = outend
|
||||
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx
|
||||
# esi = in
|
||||
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0x10/disp8 . # copy *(ebp+16) to esi
|
||||
# ecx = inend
|
||||
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x14/disp8 . # copy *(ebp+20) to ecx
|
||||
$_append-4:loop:
|
||||
# if (in >= inend) break
|
||||
39/compare 3/mod/direct 6/rm32/esi . . . 1/r32/ecx . . # compare esi with ecx
|
||||
73/jump-if-addr>= $_append-4:end/disp8
|
||||
# if (out >= outend) abort # just to catch test failures fast
|
||||
39/compare 3/mod/direct 7/rm32/edi . . . 2/r32/edx . . # compare edi with edx
|
||||
73/jump-if-addr>= $_append-4:end/disp8 # TODO: abort
|
||||
# *out = *in
|
||||
8a/copy-byte 0/mod/indirect 6/rm32/esi . . . 3/r32/BL . . # copy byte at *esi to BL
|
||||
88/copy-byte 0/mod/indirect 7/rm32/edi . . . 3/r32/BL . . # copy byte at BL to *edi
|
||||
# ++num_bytes_appended
|
||||
40/increment-eax
|
||||
# ++in
|
||||
46/increment-esi
|
||||
# ++out
|
||||
47/increment-edi
|
||||
eb/jump $_append-4:loop/disp8
|
||||
$_append-4:end:
|
||||
# . restore registers
|
||||
5f/pop-to-edi
|
||||
5e/pop-to-esi
|
||||
5b/pop-to-ebx
|
||||
5a/pop-to-edx
|
||||
59/pop-to-ecx
|
||||
# . epilogue
|
||||
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
||||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
# . . vim:nowrap:textwidth=0
|
||||
|
|
|
@ -1,273 +1,13 @@
|
|||
# read-byte-buffered: one higher-level abstraction atop 'read'.
|
||||
# Read a single byte from a stream.
|
||||
#
|
||||
# There are many situations where 'read' is a lot to manage, and we need
|
||||
# to abstract some details away. One of them is when we want to read a file
|
||||
# character by character. In this situation we follow C's FILE data structure,
|
||||
# which manages the underlying file descriptor together with the buffer it
|
||||
# reads into. We call our version 'buffered-file'. Should be useful with other
|
||||
# primitives as well, in later layers.
|
||||
|
||||
== data
|
||||
|
||||
# The buffered file for standard input. Also illustrates the layout for
|
||||
# buffered-file: a pointer to the backing store, followed by a 'buffer' stream
|
||||
Stdin: # buffered-file
|
||||
# file descriptor or (addr stream byte)
|
||||
0/imm32 # standard input
|
||||
$Stdin->buffer:
|
||||
# inlined fields for a stream
|
||||
# current write index
|
||||
0/imm32
|
||||
# current read index
|
||||
0/imm32
|
||||
# size
|
||||
8/imm32
|
||||
# data
|
||||
00 00 00 00 00 00 00 00 # 8 bytes
|
||||
|
||||
# TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But
|
||||
# I don't want to type in 1024 bytes here.
|
||||
# We need to do this in machine code because streams need to be opaque types,
|
||||
# and we don't yet support opaque types in Mu.
|
||||
|
||||
== code
|
||||
# instruction effective address register 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
|
||||
|
||||
# Return next byte value in eax, with top 3 bytes cleared.
|
||||
# On reaching end of file, return 0xffffffff (Eof).
|
||||
read-byte-buffered: # f: (addr buffered-file) -> byte-or-Eof/eax: byte
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
||||
# . save registers
|
||||
51/push-ecx
|
||||
56/push-esi
|
||||
# esi = f
|
||||
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi
|
||||
# ecx = f->read
|
||||
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 8/disp8 . # copy *(esi+8) to ecx
|
||||
# if (f->read >= f->write) populate stream from file
|
||||
3b/compare 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # compare ecx with *(esi+4)
|
||||
7c/jump-if-< $read-byte-buffered:from-stream/disp8
|
||||
# . clear-stream(stream = f+4)
|
||||
# . . push args
|
||||
8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy esi+4 to eax
|
||||
50/push-eax
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# . f->read must now be 0; update its cache at ecx
|
||||
31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx
|
||||
# . eax = read(f->fd, stream = f+4)
|
||||
# . . push args
|
||||
50/push-eax
|
||||
ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi
|
||||
# . . call
|
||||
e8/call read/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# if (eax == 0) return 0xffffffff
|
||||
3d/compare-eax-and 0/imm32
|
||||
75/jump-if-!= $read-byte-buffered:from-stream/disp8
|
||||
b8/copy-to-eax 0xffffffff/imm32/Eof
|
||||
eb/jump $read-byte-buffered:end/disp8
|
||||
$read-byte-buffered:from-stream:
|
||||
# byte-or-Eof = f->data[f->read]
|
||||
31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax
|
||||
8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0x10/disp8 . # copy byte at *(esi+ecx+16) to AL
|
||||
# ++f->read
|
||||
ff 0/subop/increment 1/mod/*+disp8 6/rm32/esi . . . . 8/disp8 . # increment *(esi+8)
|
||||
$read-byte-buffered:end:
|
||||
# . restore registers
|
||||
5e/pop-to-esi
|
||||
59/pop-to-ecx
|
||||
# . epilogue
|
||||
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
||||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
# - tests
|
||||
|
||||
test-read-byte-buffered-single:
|
||||
# - check that read-byte-buffered returns first byte of 'file'
|
||||
# setup
|
||||
# . clear-stream(_test-stream)
|
||||
# . . push args
|
||||
68/push _test-stream/imm32
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# . clear-stream(_test-buffered-file->buffer)
|
||||
# . . push args
|
||||
68/push $_test-buffered-file->buffer/imm32
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# . write(_test-stream, "Ab")
|
||||
# . . push args
|
||||
68/push "Ab"/imm32
|
||||
68/push _test-stream/imm32
|
||||
# . . call
|
||||
e8/call write/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# read-byte-buffered(_test-buffered-file)
|
||||
# . . push args
|
||||
68/push _test-buffered-file/imm32
|
||||
# . . call
|
||||
e8/call read-byte-buffered/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# check-ints-equal(eax, 'A', msg)
|
||||
# . . push args
|
||||
68/push "F - test-read-byte-buffered-single"/imm32
|
||||
68/push 0x41/imm32
|
||||
50/push-eax
|
||||
# . . 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-read-byte-buffered-multiple:
|
||||
# - call read-byte-buffered twice, check that second call returns second byte
|
||||
# setup
|
||||
# . clear-stream(_test-stream)
|
||||
# . . push args
|
||||
68/push _test-stream/imm32
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# . clear-stream($_test-buffered-file->buffer)
|
||||
# . . push args
|
||||
68/push $_test-buffered-file->buffer/imm32
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# . write(_test-stream, "Ab")
|
||||
# . . push args
|
||||
68/push "Ab"/imm32
|
||||
68/push _test-stream/imm32
|
||||
# . . call
|
||||
e8/call write/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# read-byte-buffered(_test-buffered-file)
|
||||
# . . push args
|
||||
68/push _test-buffered-file/imm32
|
||||
# . . call
|
||||
e8/call read-byte-buffered/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# read-byte-buffered(_test-buffered-file)
|
||||
# . . push args
|
||||
68/push _test-buffered-file/imm32
|
||||
# . . call
|
||||
e8/call read-byte-buffered/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# check-ints-equal(eax, 'b', msg)
|
||||
# . . push args
|
||||
68/push "F - test-read-byte-buffered-multiple"/imm32
|
||||
68/push 0x62/imm32
|
||||
50/push-eax
|
||||
# . . 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-read-byte-buffered-end-of-file:
|
||||
# - call read-byte-buffered on an empty 'file', check that it returns Eof
|
||||
# setup
|
||||
# . clear-stream(_test-stream)
|
||||
# . . push args
|
||||
68/push _test-stream/imm32
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# . clear-stream($_test-buffered-file->buffer)
|
||||
# . . push args
|
||||
68/push $_test-buffered-file->buffer/imm32
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# read-byte-buffered(_test-buffered-file)
|
||||
# . . push args
|
||||
68/push _test-buffered-file/imm32
|
||||
# . . call
|
||||
e8/call read-byte-buffered/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# check-ints-equal(eax, 0xffffffff, msg)
|
||||
# . . push args
|
||||
68/push "F - test-read-byte-buffered-end-of-file"/imm32
|
||||
68/push 0xffffffff/imm32/Eof
|
||||
50/push-eax
|
||||
# . . 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-read-byte-buffered-refills-buffer:
|
||||
# - consume buffered-file's buffer, check that next read-byte-buffered still works
|
||||
# setup
|
||||
# . clear-stream(_test-stream)
|
||||
# . . push args
|
||||
68/push _test-stream/imm32
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# . clear-stream($_test-buffered-file->buffer)
|
||||
# . . push args
|
||||
68/push $_test-buffered-file->buffer/imm32
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# . write(_test-stream, "Abcdefgh")
|
||||
# . . push args
|
||||
68/push "Abcdefgh"/imm32
|
||||
68/push _test-stream/imm32
|
||||
# . . call
|
||||
e8/call write/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# pretend buffer is full
|
||||
# . _test-buffered-file->read = 6 # >= _test-buffered-file->size
|
||||
b8/copy-to-eax _test-buffered-file/imm32
|
||||
c7 0/subop/copy 1/mod/*+disp8 0/rm32/eax . . . . 8/disp8 6/imm32 # copy to *(eax+8)
|
||||
# read-byte-buffered(_test-buffered-file)
|
||||
# . . push args
|
||||
68/push _test-buffered-file/imm32
|
||||
# . . call
|
||||
e8/call read-byte-buffered/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# check-ints-equal(eax, 'A', msg)
|
||||
# . . push args
|
||||
68/push "F - test-read-byte-buffered-refills-buffer"/imm32
|
||||
68/push 0x41/imm32
|
||||
50/push-eax
|
||||
# . . 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
|
||||
|
||||
# Return next byte value in eax, with top 3 bytes cleared.
|
||||
# Abort on reaching end of stream.
|
||||
read-byte: # s: (addr stream byte) -> result/eax: byte
|
||||
|
@ -299,35 +39,14 @@ $read-byte:end:
|
|||
c3/return
|
||||
|
||||
$read-byte:abort:
|
||||
# . _write(2/stderr, error)
|
||||
# . . push args
|
||||
68/push "read-byte: empty stream\n"/imm32
|
||||
68/push 2/imm32/stderr
|
||||
# . . call
|
||||
e8/call _write/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# . syscall(exit, 1)
|
||||
bb/copy-to-ebx 1/imm32
|
||||
e8/call syscall_exit/disp32
|
||||
(draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "read-byte: empty stream" 3 0) # 3=cyan
|
||||
{
|
||||
eb/jump loop/disp8
|
||||
}
|
||||
# never gets here
|
||||
|
||||
== data
|
||||
|
||||
# a test buffered file for _test-stream
|
||||
_test-buffered-file: # buffered-file
|
||||
# file descriptor or (addr stream byte)
|
||||
_test-stream/imm32
|
||||
$_test-buffered-file->buffer:
|
||||
# current write index
|
||||
0/imm32
|
||||
# current read index
|
||||
0/imm32
|
||||
# size
|
||||
6/imm32
|
||||
# data
|
||||
00 00 00 00 00 00 # 6 bytes
|
||||
|
||||
_test-input-stream: # (stream byte)
|
||||
# current write index
|
||||
0/imm32
|
||||
|
@ -401,18 +120,4 @@ _test-input-stream: # (stream byte)
|
|||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
|
||||
# a test buffered file for _test-input-stream
|
||||
_test-input-buffered-file: # buffered-file
|
||||
# file descriptor or (addr stream byte)
|
||||
_test-input-stream/imm32
|
||||
$_test-input-buffered-file->buffer:
|
||||
# current write index
|
||||
0/imm32
|
||||
# current read index
|
||||
0/imm32
|
||||
# size
|
||||
6/imm32
|
||||
# data
|
||||
00 00 00 00 00 00 # 6 bytes
|
||||
|
||||
# . . vim:nowrap:textwidth=0
|
||||
|
|
|
@ -5,32 +5,10 @@
|
|||
# . 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
|
||||
|
||||
#? Entry: # manual test
|
||||
#? # write-stream(stdout, _test-stream2)
|
||||
#? 68/push _test-stream2/imm32
|
||||
#? 68/push 1/imm32/stdout
|
||||
#? e8/call write-stream/disp32
|
||||
#? # syscall(exit, Num-test-failures)
|
||||
#? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/ebx Num-test-failures/disp32 # copy *Num-test-failures to ebx
|
||||
#? e8/call syscall_exit/disp32
|
||||
|
||||
write-stream: # f: fd or (addr stream byte), s: (addr stream byte)
|
||||
write-stream: # f: (addr stream byte), s: (addr stream byte)
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
||||
# if (f < 0x08000000) _write-stream(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 5/rm32/ebp . . . . 8/disp8 0x08000000/imm32 # compare *(ebp+8)
|
||||
73/jump-if-addr>= $write-stream:fake/disp8
|
||||
# . . push args
|
||||
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
|
||||
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
|
||||
# . . call
|
||||
e8/call _write-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
eb/jump $write-stream:end/disp8
|
||||
$write-stream:fake:
|
||||
# otherwise, treat 'f' as a stream to append to
|
||||
# . save registers
|
||||
50/push-eax
|
||||
56/push-esi
|
||||
|
@ -64,73 +42,16 @@ $write-stream:fake:
|
|||
01/add 0/mod/indirect 7/rm32/edi . . . 0/r32/eax . . # add eax to *edi
|
||||
# s->read += eax
|
||||
01/add 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # add eax to *(esi+4)
|
||||
# . restore registers
|
||||
5f/pop-to-edi
|
||||
5e/pop-to-esi
|
||||
58/pop-to-eax
|
||||
$write-stream:end:
|
||||
# . epilogue
|
||||
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
||||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
_write-stream: # fd: int, s: (addr stream byte)
|
||||
# . prologue
|
||||
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
|
||||
57/push-edi
|
||||
# esi = s
|
||||
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi
|
||||
# edi = s->read
|
||||
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 7/r32/edi 4/disp8 . # copy *(esi+4) to edi
|
||||
# edx = s->write
|
||||
8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx
|
||||
# syscall(write, fd, &s->data[s->read], s->write - s->read)
|
||||
# . . fd: ebx
|
||||
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 3/r32/ebx 8/disp8 . # copy *(ebp+8) to ebx
|
||||
# . . data: ecx = &s->data[s->read]
|
||||
8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 7/index/edi . 1/r32/ecx 0xc/disp8 . # copy esi+edi+12 to ecx
|
||||
# . . size: edx = s->write - s->read
|
||||
29/subtract 3/mod/direct 2/rm32/edx . . . 7/r32/edi . . # subtract edi from edx
|
||||
# . . syscall
|
||||
e8/call syscall_write/disp32
|
||||
# if (eax < 0) abort
|
||||
3d/compare-eax-with 0/imm32
|
||||
0f 8c/jump-if-< $_write-stream:abort/disp32
|
||||
# s->read += eax
|
||||
01/add 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # add eax to *(esi+4)
|
||||
# . restore registers
|
||||
5f/pop-to-edi
|
||||
5e/pop-to-esi
|
||||
5b/pop-to-ebx
|
||||
5a/pop-to-edx
|
||||
59/pop-to-ecx
|
||||
58/pop-to-eax
|
||||
# . epilogue
|
||||
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
||||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
$_write-stream:abort:
|
||||
# . _write(2/stderr, error)
|
||||
# . . push args
|
||||
68/push "_write-stream: failed to write to file\n"/imm32
|
||||
68/push 2/imm32/stderr
|
||||
# . . call
|
||||
e8/call _write/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# . syscall(exit, 1)
|
||||
bb/copy-to-ebx 1/imm32
|
||||
e8/call syscall_exit/disp32
|
||||
# never gets here
|
||||
|
||||
test-write-stream-single:
|
||||
# setup
|
||||
# . clear-stream(_test-stream)
|
||||
|
|
|
@ -1,209 +1,13 @@
|
|||
# write-byte-buffered: add a single byte to a buffered-file.
|
||||
# flush: write out any buffered writes to disk.
|
||||
# Write a single byte to a stream.
|
||||
#
|
||||
# TODO: Come up with a way to signal failure to write to disk. This is hard
|
||||
# since the failure may impact previous calls that were buffered.
|
||||
|
||||
== data
|
||||
|
||||
# The buffered file for standard output.
|
||||
Stdout: # buffered-file
|
||||
# file descriptor or (addr stream byte)
|
||||
1/imm32 # standard output
|
||||
$Stdout->buffer:
|
||||
# inlined fields for a stream
|
||||
# current write index
|
||||
0/imm32
|
||||
# current read index
|
||||
0/imm32
|
||||
# size
|
||||
8/imm32
|
||||
# data
|
||||
00 00 00 00 00 00 00 00 # 8 bytes
|
||||
|
||||
# TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But
|
||||
# I don't want to type in 1024 bytes here.
|
||||
# We need to do this in machine code because streams need to be opaque types,
|
||||
# and we don't yet support opaque types in Mu.
|
||||
|
||||
== code
|
||||
# instruction effective address register 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
|
||||
|
||||
# Write lower byte of 'n' to 'f'.
|
||||
write-byte-buffered: # f: (addr buffered-file), n: int
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
||||
# . save registers
|
||||
51/push-ecx
|
||||
57/push-edi
|
||||
# edi = f
|
||||
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi
|
||||
# ecx = f->write
|
||||
8b/copy 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 4/disp8 . # copy *(edi+4) to ecx
|
||||
# if (f->write >= f->size) flush and clear f's stream
|
||||
3b/compare 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 0xc/disp8 . # compare ecx with *(edi+12)
|
||||
7c/jump-if-< $write-byte-buffered:to-stream/disp8
|
||||
# . flush(f)
|
||||
# . . push args
|
||||
57/push-edi
|
||||
# . . call
|
||||
e8/call flush/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# . clear-stream(stream = f+4)
|
||||
# . . push args
|
||||
8d/copy-address 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy edi+4 to eax
|
||||
50/push-eax
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# . f->write must now be 0; update its cache at ecx
|
||||
31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx
|
||||
$write-byte-buffered:to-stream:
|
||||
# write to stream
|
||||
# f->data[f->write] = LSB(n)
|
||||
31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax
|
||||
8a/copy-byte 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/AL 0xc/disp8 . # copy byte at *(ebp+12) to AL
|
||||
88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/edi 1/index/ecx . 0/r32/AL 0x10/disp8 . # copy AL to *(edi+ecx+16)
|
||||
# ++f->write
|
||||
ff 0/subop/increment 1/mod/*+disp8 7/rm32/edi . . . . 4/disp8 . # increment *(edi+4)
|
||||
$write-byte-buffered:end:
|
||||
# . restore registers
|
||||
5f/pop-to-edi
|
||||
59/pop-to-ecx
|
||||
# . epilogue
|
||||
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
||||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
flush: # f: (addr buffered-file)
|
||||
# . prologue
|
||||
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 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to eax
|
||||
# write-stream(f->fd, data = f+4)
|
||||
# . . push args
|
||||
8d/copy-address 1/mod/*+disp8 0/rm32/eax . . . 1/r32/ecx 4/disp8 . # copy eax+4 to ecx
|
||||
51/push-ecx
|
||||
ff 6/subop/push 0/mod/indirect 0/rm32/eax . . . . . . # push *eax
|
||||
# . . call
|
||||
e8/call write-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
$flush:end:
|
||||
# . restore registers
|
||||
59/pop-to-ecx
|
||||
58/pop-to-eax
|
||||
# . epilogue
|
||||
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
||||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
test-write-byte-buffered-single:
|
||||
# - check that write-byte-buffered writes to first byte of 'file'
|
||||
# setup
|
||||
# . clear-stream(_test-stream)
|
||||
# . . push args
|
||||
68/push _test-stream/imm32
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# . clear-stream($_test-buffered-file->buffer)
|
||||
# . . push args
|
||||
68/push $_test-buffered-file->buffer/imm32
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# write-byte-buffered(_test-buffered-file, 'A')
|
||||
# . . push args
|
||||
68/push 0x41/imm32
|
||||
68/push _test-buffered-file/imm32
|
||||
# . . call
|
||||
e8/call write-byte-buffered/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# flush(_test-buffered-file)
|
||||
# . . push args
|
||||
68/push _test-buffered-file/imm32
|
||||
# . . call
|
||||
e8/call flush/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# check-stream-equal(_test-stream, "A", msg)
|
||||
# . . push args
|
||||
68/push "F - test-write-byte-buffered-single"/imm32
|
||||
68/push "A"/imm32
|
||||
68/push _test-stream/imm32
|
||||
# . . call
|
||||
e8/call check-stream-equal/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
|
||||
# . end
|
||||
c3/return
|
||||
|
||||
test-write-byte-buffered-multiple-flushes:
|
||||
# - check that write-byte-buffered correctly flushes buffered data
|
||||
# setup
|
||||
# . clear-stream(_test-stream)
|
||||
# . . push args
|
||||
68/push _test-stream/imm32
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# . clear-stream($_test-buffered-file->buffer)
|
||||
# . . push args
|
||||
68/push $_test-buffered-file->buffer/imm32
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# fill up the buffer for _test-buffered-file
|
||||
# . write($_test-buffered-file->buffer, "abcdef")
|
||||
# . . push args
|
||||
68/push "abcdef"/imm32
|
||||
68/push $_test-buffered-file->buffer/imm32
|
||||
# . . call
|
||||
e8/call write/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# write-byte-buffered(_test-buffered-file, 'g')
|
||||
# . . push args
|
||||
68/push 0x67/imm32
|
||||
68/push _test-buffered-file/imm32
|
||||
# . . call
|
||||
e8/call write-byte-buffered/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# flush(_test-buffered-file)
|
||||
# . . push args
|
||||
68/push _test-buffered-file/imm32
|
||||
# . . call
|
||||
e8/call flush/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# check-stream-equal(_test-stream, "abcdefg", msg)
|
||||
# . . push args
|
||||
68/push "F - test-write-byte-buffered-multiple-flushes"/imm32
|
||||
68/push "abcdefg"/imm32
|
||||
68/push _test-stream/imm32
|
||||
# . . call
|
||||
e8/call check-stream-equal/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
|
||||
# . end
|
||||
c3/return
|
||||
|
||||
# - variant without buffering
|
||||
|
||||
# Write lower byte of 'n' to 'f'.
|
||||
append-byte: # f: (addr stream byte), n: int
|
||||
# . prologue
|
||||
|
@ -239,17 +43,10 @@ $append-byte:end:
|
|||
c3/return
|
||||
|
||||
$append-byte:abort:
|
||||
# . _write(2/stderr, error)
|
||||
# . . push args
|
||||
68/push "append-byte: out of space\n"/imm32
|
||||
68/push 2/imm32/stderr
|
||||
# . . call
|
||||
e8/call _write/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# . syscall(exit, 1)
|
||||
bb/copy-to-ebx 1/imm32
|
||||
e8/call syscall_exit/disp32
|
||||
(draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "append-byte: out of space\n" 3 0) # 3=cyan
|
||||
{
|
||||
eb/jump loop/disp8
|
||||
}
|
||||
# never gets here
|
||||
|
||||
test-append-byte-single:
|
||||
|
@ -282,196 +79,4 @@ test-append-byte-single:
|
|||
# . end
|
||||
c3/return
|
||||
|
||||
== data
|
||||
|
||||
_test-output-stream: # (stream byte)
|
||||
# current write index
|
||||
0/imm32
|
||||
# current read index
|
||||
0/imm32
|
||||
# size
|
||||
0x800/imm32 # 2048 bytes
|
||||
# data (128 lines x 16 bytes/line)
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
|
||||
# a test buffered file for _test-output-stream
|
||||
_test-output-buffered-file: # buffered-file
|
||||
# file descriptor or (addr stream byte)
|
||||
_test-output-stream/imm32
|
||||
$_test-output-buffered-file->buffer:
|
||||
# current write index
|
||||
0/imm32
|
||||
# current read index
|
||||
0/imm32
|
||||
# size
|
||||
6/imm32
|
||||
# data
|
||||
00 00 00 00 00 00 # 6 bytes
|
||||
|
||||
_test-error-stream: # (stream byte)
|
||||
# current write index
|
||||
0/imm32
|
||||
# current read index
|
||||
0/imm32
|
||||
# line
|
||||
0x100/imm32 # 256 bytes
|
||||
# data (16 lines x 16 bytes/line)
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
|
||||
# a test buffered file for _test-error-stream
|
||||
_test-error-buffered-file: # buffered-file
|
||||
# file descriptor or (addr stream byte)
|
||||
_test-error-stream/imm32
|
||||
$_test-error-buffered-file->buffer:
|
||||
# current write index
|
||||
0/imm32
|
||||
# current read index
|
||||
0/imm32
|
||||
# size
|
||||
6/imm32
|
||||
# data
|
||||
00 00 00 00 00 00 # 6 bytes
|
||||
|
||||
# . . vim:nowrap:textwidth=0
|
||||
|
|
|
@ -89,92 +89,6 @@ test-append-byte-hex:
|
|||
# . end
|
||||
c3/return
|
||||
|
||||
# print the hex representation for the lowest byte of a number
|
||||
write-byte-hex-buffered: # f: (addr buffered-file), n: int
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
||||
# . save registers
|
||||
50/push-eax
|
||||
# AL = convert upper nibble to hex
|
||||
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax
|
||||
c1/shift 5/subop/logic-right 3/mod/direct 0/rm32/eax . . . . . 4/imm8 # shift eax right by 4 bits, while padding zeroes
|
||||
25/and-eax 0xf/imm32
|
||||
# . AL = to-hex-char(AL)
|
||||
e8/call to-hex-char/disp32
|
||||
# write-byte-buffered(f, AL)
|
||||
# . . push args
|
||||
50/push-eax
|
||||
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
|
||||
# . . call
|
||||
e8/call write-byte-buffered/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# AL = convert lower nibble to hex
|
||||
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax
|
||||
25/and-eax 0xf/imm32
|
||||
# . AL = to-hex-char(AL)
|
||||
e8/call to-hex-char/disp32
|
||||
# write-byte-buffered(f, AL)
|
||||
# . . push args
|
||||
50/push-eax
|
||||
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
|
||||
# . . call
|
||||
e8/call write-byte-buffered/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
$write-byte-hex-buffered:end:
|
||||
# . restore registers
|
||||
58/pop-to-eax
|
||||
# . epilogue
|
||||
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
||||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
test-write-byte-hex-buffered:
|
||||
# - check that write-byte-hex-buffered prints the hex textual representation
|
||||
# setup
|
||||
# . clear-stream(_test-stream)
|
||||
# . . push args
|
||||
68/push _test-stream/imm32
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# . clear-stream($_test-buffered-file->buffer)
|
||||
# . . push args
|
||||
68/push $_test-buffered-file->buffer/imm32
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# write-byte-hex-buffered(_test-buffered-file, 0xa) # exercises digit, non-digit as well as leading zero
|
||||
# . . push args
|
||||
68/push 0xa/imm32
|
||||
68/push _test-buffered-file/imm32
|
||||
# . . call
|
||||
e8/call write-byte-hex-buffered/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# flush(_test-buffered-file)
|
||||
# . . push args
|
||||
68/push _test-buffered-file/imm32
|
||||
# . . call
|
||||
e8/call flush/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# check-stream-equal(_test-stream, "0a", msg)
|
||||
# . . push args
|
||||
68/push "F - test-write-byte-hex-buffered"/imm32
|
||||
68/push "0a"/imm32
|
||||
68/push _test-stream/imm32
|
||||
# . . call
|
||||
e8/call check-stream-equal/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
|
||||
# . end
|
||||
c3/return
|
||||
|
||||
write-int32-hex: # f: (addr stream byte), n: int
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
|
@ -285,146 +199,4 @@ test-write-int32-hex:
|
|||
# . end
|
||||
c3/return
|
||||
|
||||
write-int32-hex-buffered: # f: (addr buffered-file), n: int
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
||||
$write-int32-hex-buffered:hex-prefix:
|
||||
# write-buffered(f, "0x")
|
||||
# . . push args
|
||||
68/push "0x"/imm32
|
||||
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
|
||||
# . . call
|
||||
e8/call write-buffered/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
$write-int32-hex-buffered:rest:
|
||||
# write-int32-hex-bits-buffered(f, n, 32)
|
||||
# . . push args
|
||||
68/push 0x20/imm32
|
||||
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
|
||||
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
|
||||
# . . call
|
||||
e8/call write-int32-hex-bits-buffered/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
|
||||
$write-int32-hex-buffered:end:
|
||||
# . epilogue
|
||||
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
||||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
# print rightmost 'bits' of 'n'
|
||||
# bits must be multiple of 4
|
||||
write-int32-hex-bits-buffered: # f: (addr buffered-file), n: int, bits: int
|
||||
# pseudocode:
|
||||
# bits -= 4
|
||||
# while true
|
||||
# if (bits < 0) break
|
||||
# eax = n >> bits
|
||||
# eax = eax & 0xf
|
||||
# write-byte-buffered(f, AL)
|
||||
# bits -= 4
|
||||
#
|
||||
# . prologue
|
||||
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
|
||||
# ecx = bits-4
|
||||
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # copy *(ebp+16) to ecx
|
||||
81 5/subop/subtract 3/mod/direct 1/rm32/ecx . . . . . 4/imm32 # subtract from ecx
|
||||
$write-int32-hex-bits-buffered:loop:
|
||||
# if (bits < 0) break
|
||||
81 7/subop/compare 3/mod/direct 1/rm32/ecx . . . . . 0/imm32 # compare ecx
|
||||
7c/jump-if-< $write-int32-hex-bits-buffered:end/disp8
|
||||
# eax = n >> bits
|
||||
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax
|
||||
d3/>>ecx 5/subop/pad-zeroes 3/mod/direct 0/rm32/eax . . . . . . # shift eax right by ecx bits, padding zeroes
|
||||
# eax = to-hex-char(AL)
|
||||
25/and-eax 0xf/imm32
|
||||
e8/call to-hex-char/disp32
|
||||
# write-byte-buffered(f, AL)
|
||||
# . . push args
|
||||
50/push-eax
|
||||
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
|
||||
# . . call
|
||||
e8/call write-byte-buffered/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# bits -= 4
|
||||
81 5/subop/subtract 3/mod/direct 1/rm32/ecx . . . . . 4/imm32 # subtract from ecx
|
||||
eb/jump $write-int32-hex-bits-buffered:loop/disp8
|
||||
$write-int32-hex-bits-buffered:end:
|
||||
# . restore registers
|
||||
59/pop-to-ecx
|
||||
58/pop-to-eax
|
||||
# . epilogue
|
||||
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
||||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
test-write-int32-hex-buffered:
|
||||
# - check that write-int32-hex-buffered prints the hex textual representation
|
||||
# setup
|
||||
# . clear-stream(_test-stream)
|
||||
# . . push args
|
||||
68/push _test-stream/imm32
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# . clear-stream($_test-buffered-file->buffer)
|
||||
# . . push args
|
||||
68/push $_test-buffered-file->buffer/imm32
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# write-int32-hex-buffered(_test-buffered-file, 0x8899aa)
|
||||
# . . push args
|
||||
68/push 0x8899aa/imm32
|
||||
68/push _test-buffered-file/imm32
|
||||
# . . call
|
||||
e8/call write-int32-hex-buffered/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# flush(_test-buffered-file)
|
||||
# . . push args
|
||||
68/push _test-buffered-file/imm32
|
||||
# . . call
|
||||
e8/call flush/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
#? # dump line {{{
|
||||
#? # . write-stream(2/stderr, line)
|
||||
#? # . . push args
|
||||
#? 68/push _test-stream/imm32
|
||||
#? 68/push 2/imm32/stderr
|
||||
#? # . . call
|
||||
#? e8/call write-stream/disp32
|
||||
#? # . . discard args
|
||||
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
#? # . write(2/stderr, "$\n")
|
||||
#? # . . push args
|
||||
#? 68/push "$\n"/imm32
|
||||
#? 68/push 2/imm32/stderr
|
||||
#? # . . call
|
||||
#? e8/call write/disp32
|
||||
#? # . . discard args
|
||||
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
#? # }}}
|
||||
# check-stream-equal(_test-stream, "0x008899aa", msg)
|
||||
# . . push args
|
||||
68/push "F - test-write-int32-hex-buffered"/imm32
|
||||
68/push "0x008899aa"/imm32
|
||||
68/push _test-stream/imm32
|
||||
# . . call
|
||||
e8/call check-stream-equal/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
|
||||
# . end
|
||||
c3/return
|
||||
|
||||
# . . vim:nowrap:textwidth=0
|
||||
|
|
|
@ -888,51 +888,10 @@ $from-hex-char:letter:
|
|||
c3/return
|
||||
|
||||
$from-hex-char:abort:
|
||||
# . _write(2/stderr, error)
|
||||
# . . push args
|
||||
68/push "invalid hex char: "/imm32
|
||||
68/push 2/imm32/stderr
|
||||
# . . call
|
||||
e8/call _write/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# . clear-stream($Stderr->buffer)
|
||||
# . . save eax
|
||||
50/push-eax
|
||||
# . . push args
|
||||
68/push $Stderr->buffer/imm32
|
||||
# . . call
|
||||
e8/call clear-stream/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# . . restore eax
|
||||
58/pop-to-eax
|
||||
# . write-int32-hex-buffered(Stderr, eax)
|
||||
# . . push args
|
||||
50/push-eax
|
||||
68/push Stderr/imm32
|
||||
# . . call
|
||||
e8/call write-int32-hex-buffered/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# . flush(Stderr)
|
||||
# . . push args
|
||||
68/push Stderr/imm32
|
||||
# . . call
|
||||
e8/call flush/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
||||
# . _write(2/stderr, "\n")
|
||||
# . . push args
|
||||
68/push Newline/imm32
|
||||
68/push 2/imm32/stderr
|
||||
# . . call
|
||||
e8/call _write/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# . syscall(exit, 1)
|
||||
bb/copy-to-ebx 1/imm32
|
||||
e8/call syscall_exit/disp32
|
||||
(draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "invalid hex char" 3 0) # 3=cyan
|
||||
{
|
||||
eb/jump loop/disp8
|
||||
}
|
||||
# never gets here
|
||||
|
||||
# . . vim:nowrap:textwidth=0
|
||||
|
|
193
120allocate.subx
193
120allocate.subx
|
@ -26,13 +26,9 @@ Handle-size: # (addr int)
|
|||
# A default allocation descriptor for programs to use.
|
||||
Heap: # allocation-descriptor
|
||||
# curr
|
||||
0/imm32
|
||||
0x01000000/imm32 # 16 MB
|
||||
# limit
|
||||
0/imm32
|
||||
|
||||
# a reasonable default
|
||||
Heap-size: # int
|
||||
0x800000/imm32/8MB
|
||||
0x02000000/imm32 # 32 MB
|
||||
|
||||
Next-alloc-id: # int
|
||||
0x100/imm32 # save a few alloc ids for fake handles
|
||||
|
@ -42,25 +38,6 @@ Next-alloc-id: # int
|
|||
# . 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
|
||||
|
||||
# Let's start initializing the default allocation descriptor.
|
||||
|
||||
Entry:
|
||||
# initialize heap
|
||||
# . Heap = new-segment(Heap-size)
|
||||
# . . push args
|
||||
68/push Heap/imm32
|
||||
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Heap-size/disp32 # push *Heap-size
|
||||
# . . call
|
||||
e8/call new-segment/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
|
||||
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
||||
$array-equal-main:end:
|
||||
# syscall(exit, Num-test-failures)
|
||||
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/ebx Num-test-failures/disp32 # copy *Num-test-failures to ebx
|
||||
e8/call syscall_exit/disp32
|
||||
|
||||
# Allocate and clear 'n' bytes of memory from an allocation-descriptor 'ad'.
|
||||
# Abort if there isn't enough memory in 'ad'.
|
||||
allocate: # ad: (addr allocation-descriptor), n: int, out: (addr handle _)
|
||||
|
@ -153,35 +130,26 @@ $allocate-raw:end:
|
|||
c3/return
|
||||
|
||||
$allocate-raw:abort:
|
||||
# . _write(2/stderr, error)
|
||||
# . . push args
|
||||
68/push "allocate: failed\n"/imm32
|
||||
68/push 2/imm32/stderr
|
||||
# . . call
|
||||
e8/call _write/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# . syscall(exit, 1)
|
||||
bb/copy-to-ebx 1/imm32
|
||||
e8/call syscall_exit/disp32
|
||||
(draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "allocate: failed" 3 0) # 3=cyan
|
||||
{
|
||||
eb/jump loop/disp8
|
||||
}
|
||||
# never gets here
|
||||
|
||||
test-allocate-raw-success:
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
||||
# var ad/ecx: allocation-descriptor
|
||||
68/push 0/imm32/limit
|
||||
68/push 0/imm32/curr
|
||||
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
|
||||
# ad = new-segment(512)
|
||||
# . . push args
|
||||
# var ad/ecx: allocation-descriptor containing 16 bytes
|
||||
# . var end/ecx: (addr byte)
|
||||
89/<- %ecx 4/r32/esp
|
||||
# . var start/edx: (addr byte) = end - 16
|
||||
81 5/subop/subtract %esp 0x10/imm32
|
||||
89/<- %edx 4/r32/esp
|
||||
# . ad = {start, end}
|
||||
51/push-ecx
|
||||
68/push 0x200/imm32
|
||||
# . . call
|
||||
e8/call new-segment/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
52/push-edx
|
||||
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
|
||||
# var expected-payload/ebx = ad->curr
|
||||
8b/copy 0/mod/indirect 1/rm32/ecx . . . 3/r32/ebx . . # copy *ecx to ebx
|
||||
# var h/edx: handle = {0, 0}
|
||||
|
@ -249,7 +217,7 @@ test-allocate-raw-success:
|
|||
# clean up
|
||||
c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 0x100/imm32 # copy to *Next-alloc-id
|
||||
# . reclaim locals
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x20/imm32 # add to esp
|
||||
# . epilogue
|
||||
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
||||
5d/pop-to-ebp
|
||||
|
@ -284,34 +252,26 @@ $lookup:end:
|
|||
c3/return
|
||||
|
||||
$lookup:abort:
|
||||
# . _write(2/stderr, msg)
|
||||
# . . push args
|
||||
68/push "lookup failed\n"/imm32
|
||||
68/push 2/imm32/stderr
|
||||
# . . call
|
||||
e8/call _write/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# . syscall(exit, 1)
|
||||
bb/copy-to-ebx 1/imm32/exit-status
|
||||
e8/call syscall_exit/disp32
|
||||
(draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "lookup: failed" 3 0) # 3=cyan
|
||||
{
|
||||
eb/jump loop/disp8
|
||||
}
|
||||
# never gets here
|
||||
|
||||
test-lookup-success:
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
||||
# var ad/ebx: allocation-descriptor
|
||||
68/push 0/imm32/limit
|
||||
68/push 0/imm32/curr
|
||||
# var ad/ebx: allocation-descriptor containing 16 bytes
|
||||
# . var end/ecx: (addr byte)
|
||||
89/<- %ecx 4/r32/esp
|
||||
# . var start/edx: (addr byte) = end - 16
|
||||
81 5/subop/subtract %esp 0x10/imm32
|
||||
89/<- %edx 4/r32/esp
|
||||
# . ad = {start, end}
|
||||
51/push-ecx
|
||||
52/push-edx
|
||||
89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx
|
||||
# ad = new-segment(512)
|
||||
# . . push args
|
||||
53/push-ebx
|
||||
68/push 0x200/imm32
|
||||
# . . call
|
||||
e8/call new-segment/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# var handle/ecx: handle
|
||||
68/push 0/imm32/address
|
||||
68/push 0/imm32/alloc-id
|
||||
|
@ -349,7 +309,7 @@ test-lookup-success:
|
|||
# clean up
|
||||
c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 0x100/imm32 # copy to *Next-alloc-id
|
||||
# . reclaim locals
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x20/imm32 # add to esp
|
||||
# . epilogue
|
||||
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
||||
5d/pop-to-ebp
|
||||
|
@ -391,26 +351,24 @@ _pending-test-lookup-failure:
|
|||
# . prologue
|
||||
55/push-ebp
|
||||
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
||||
# var heap/esi: allocation-descriptor
|
||||
68/push 0/imm32/limit
|
||||
68/push 0/imm32/curr
|
||||
89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi
|
||||
# heap = new-segment(512)
|
||||
# . . push args
|
||||
56/push-esi
|
||||
68/push 0x200/imm32
|
||||
# . . call
|
||||
e8/call new-segment/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# var ad/ecx: allocation-descriptor containing 16 bytes
|
||||
# . var end/ecx: (addr byte)
|
||||
89/<- %ecx 4/r32/esp
|
||||
# . var start/edx: (addr byte) = end - 16
|
||||
81 5/subop/subtract %esp 0x10/imm32
|
||||
89/<- %edx 4/r32/esp
|
||||
# . ad = {start, end}
|
||||
51/push-ecx
|
||||
52/push-edx
|
||||
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
|
||||
# var h1/ecx: handle
|
||||
68/push 0/imm32/address
|
||||
68/push 0/imm32/alloc-id
|
||||
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
|
||||
# var old_top/ebx = heap->curr
|
||||
# var old_top/ebx = ad->curr
|
||||
8b/copy 0/mod/indirect 6/rm32/esi . . . 3/r32/ebx . . # copy *esi to ebx
|
||||
# first allocation, to h1
|
||||
# . allocate(heap, 2, h1)
|
||||
# . allocate(ad, 2, h1)
|
||||
# . . push args
|
||||
51/push-ecx
|
||||
68/push 2/imm32/size
|
||||
|
@ -419,14 +377,14 @@ _pending-test-lookup-failure:
|
|||
e8/call allocate/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
|
||||
# reset heap->curr to mimic reclamation
|
||||
# reset ad->curr to mimic reclamation
|
||||
89/copy 0/mod/indirect 6/rm32/esi . . . 3/r32/ebx . . # copy ebx to *esi
|
||||
# second allocation that returns the same address as the first
|
||||
# var h2/edx: handle
|
||||
68/push 0/imm32/address
|
||||
68/push 0/imm32/alloc-id
|
||||
89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
|
||||
# . allocate(heap, 2, h2)
|
||||
# . allocate(ad, 2, h2)
|
||||
# . . push args
|
||||
52/push-edx
|
||||
68/push 2/imm32/size
|
||||
|
@ -456,7 +414,7 @@ _pending-test-lookup-failure:
|
|||
# clean up
|
||||
c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 0x100/imm32 # copy to *Next-alloc-id
|
||||
# . reclaim locals
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x20/imm32 # add to esp
|
||||
# . epilogue
|
||||
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
||||
5d/pop-to-ebp
|
||||
|
@ -566,17 +524,10 @@ allocate-region: # ad: (addr allocation-descriptor), n: int, out: (addr handle
|
|||
# we bloat a potentially cold segment in RAM so we can abort with a single
|
||||
# instruction.
|
||||
$allocate-region:abort:
|
||||
# . _write(2/stderr, error)
|
||||
# . . push args
|
||||
68/push "allocate-region: failed to allocate\n"/imm32
|
||||
68/push 2/imm32/stderr
|
||||
# . . call
|
||||
e8/call _write/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
# . syscall(exit, 1)
|
||||
bb/copy-to-ebx 1/imm32
|
||||
e8/call syscall_exit/disp32
|
||||
(draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "allocate-region: failed to allocate" 3 0) # 3=cyan
|
||||
{
|
||||
eb/jump loop/disp8
|
||||
}
|
||||
# never gets here
|
||||
|
||||
# Claim the next 'n+4' bytes of memory and initialize the first 4 to n.
|
||||
|
@ -623,18 +574,16 @@ test-allocate-array:
|
|||
# . prologue
|
||||
55/push-ebp
|
||||
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
||||
# var ad/ecx: allocation-descriptor
|
||||
68/push 0/imm32/limit
|
||||
68/push 0/imm32/curr
|
||||
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
|
||||
# ad = new-segment(512)
|
||||
# . . push args
|
||||
# var ad/ecx: allocation-descriptor containing 16 bytes
|
||||
# . var end/ecx: (addr byte)
|
||||
89/<- %ecx 4/r32/esp
|
||||
# . var start/edx: (addr byte) = end - 16
|
||||
81 5/subop/subtract %esp 0x10/imm32
|
||||
89/<- %edx 4/r32/esp
|
||||
# . ad = {start, end}
|
||||
51/push-ecx
|
||||
68/push 0x200/imm32
|
||||
# . . call
|
||||
e8/call new-segment/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
||||
52/push-edx
|
||||
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
|
||||
# var expected-payload/ebx = ad->curr
|
||||
8b/copy 0/mod/indirect 1/rm32/ecx . . . 3/r32/ebx . . # copy *ecx to ebx
|
||||
# var h/edx: handle = {0, 0}
|
||||
|
@ -711,7 +660,7 @@ test-allocate-array:
|
|||
# clean up
|
||||
c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 1/imm32 # copy to *Next-alloc-id
|
||||
# . reclaim locals
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
|
||||
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x20/imm32 # add to esp
|
||||
# . epilogue
|
||||
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
||||
5d/pop-to-ebp
|
||||
|
@ -783,18 +732,16 @@ test-copy-array:
|
|||
68/push 3/imm32
|
||||
68/push 0xc/imm32/size
|
||||
89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi
|
||||
# var ad/ecx: allocation-descriptor
|
||||
68/push 0/imm32/limit
|
||||
68/push 0/imm32/curr
|
||||
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
|
||||
# ad = new-segment(512)
|
||||
# . . push args
|
||||
# var ad/ecx: allocation-descriptor containing 32 bytes
|
||||