1
;; bf.asm: Copyright (C) 1999 Brian Raiter <breadbox@muppetlabs.com>:2
;; Licensed under the terms of the GNU General Public License, either3
;; version 2 or (at your option) any later version.6
;; nasm -f bin -o bf bf.asm && chmod +x bf8
;; bf < foo.b > foo && chmod +x foo12
;; This is the size of the data area supplied to compiled programs.14
%define arraysize 3000016
;; For the compiler, the text segment is also the data segment. The17
;; memory image of the compiler is inside the code buffer, and is18
;; modified in place to become the memory image of the compiled19
;; program. The area of memory that is the data segment for compiled20
;; programs is not used by the compiler. The text and data segments of21
;; compiled programs are really only different areas in a single22
;; segment, from the system's point of view. Both the compiler and23
;; compiled programs load the entire file contents into a single24
;; memory segment which is both writeable and executable.26
%define TEXTORG 0x45E9B00027
%define DATAOFFSET 0x200028
%define DATAORG (TEXTORG + DATAOFFSET)30
;; Here begins the file image.34
;; At the beginning of the text segment is the ELF header and the35
;; program header table, the latter consisting of a single entry. The36
;; two structures overlap for a space of eight bytes. Nearly all37
;; unused fields in the structures are used to hold bits of code.39
;; The beginning of the ELF header.41
db 0x7F, "ELF" ; ehdr.e_ident43
;; The top(s) of the main compiling loop. The loop jumps back to44
;; different positions, depending on how many bytes to copy into the45
;; code buffer. After doing that, esi is initialized to point to the46
;; epilog code chunk, a copy of edi (the pointer to the end of the47
;; code buffer) is saved in ebp, the high bytes of eax are reset to48
;; zero (via the exchange with ebx), and then the next character of52
add esi, byte (putchar - decchar) - 467
lea esi, [byte ecx + epilog - filesize]69
cmp eax, 0x00030002 ; ehdr.e_type (0x0002), ehdr.e_machine (0x0003)70
mov ebp, edi ; ehdr.e_version73
;; The entry point for the compiler (and compiled programs), and the74
;; location of the program header table.76
dd _start ; ehdr.e_entry77
dd proghdr - $$ ; ehdr.e_phoff79
;; The last routine of the compiler, called when there is no more80
;; input. The epilog code chunk is copied into the code buffer. The81
;; text origin is popped off the stack into ecx, and subtracted from82
;; edi to determine the size of the compiled program. This value is83
;; stored in the program header table, and then is moved into edx.84
;; The program then jumps to the putchar routine, which sends the85
;; compiled program to stdout before falling through to the epilog86
;; routine and exiting.92
sub edi, ecx ; ehdr.e_flags96
jmp short putchar ; ehdr.e_ehsize98
;; 0x20 == the size of one program header table entry.100
dw 0x20 ; ehdr.e_phentsize102
;; The beginning of the program header table. 1 == PT_LOAD, indicating103
;; that the segment is to be loaded into memory.106
dd 1 ; ehdr.e_phnum & phdr.p_type, ehdr.e_shentsize107
dd 0 ; ehdr.e_shnum & phdr.p_offset, ehdr.e_shstrndx109
;; (Note that the next four bytes, in addition to containing the first110
;; two instructions of the bracket routine, also comprise the memory111
;; address of the text origin.)115
;; The bracket routine emits code for the "[" instruction. This116
;; instruction translates to a simple "jmp near", but the target of117
;; the jump will not be known until the matching "]" is seen. The118
;; routine thus outputs a random target, and pushes the location of119
;; the target in the code buffer onto the stack.124
push ebp ; phdr.p_paddr128
;; This is where the size of the executable file is stored in the129
;; program header table. The compiler updates this value just before130
;; it outputs the compiled program. This is the only field in the two131
;; headers that differs between the compiler and its compiled132
;; programs. (While the compiler is reading input, the first byte of133
;; this field is also used as an input buffer.)136
dd compilersize ; phdr.p_filesz138
;; The size of the program in memory. This entry creates an area of139
;; bytes, arraysize in size, all initialized to zero, starting at142
dd DATAOFFSET + arraysize ; phdr.p_memsz144
;; The code chunk for the "." instruction. eax is set to 4 to invoke145
;; the write system call. ebx, the file handle to write to, is set to146
;; 1 for stdout. ecx points to the buffer containing the bytes to147
;; output, and edx equals the number of bytes to output. (Note that148
;; the first byte of the first instruction, which is also the least149
;; significant byte of the p_flags field, encodes to 0xB3. Having the150
;; 2-bit set marks the memory containing the compiler, and its151
;; compiled programs, as writeable.)154
mov bl, 1 ; phdr.p_flags156
int 0x80 ; phdr.p_align158
;; The epilog code chunk. After restoring the initialized registers159
;; eax and ebx are both zero. eax is incremented to 1, so as to invoke160
;; the exit system call. ebx specifies the process's return value.167
;; The code chunks for the ">", "<", "+", and "-" instructions.181
;; The main loop of the compiler continues here, by obtaining the next182
;; character of input. This is also the code chunk for the ","183
;; instruction. eax is set to 3 to invoke the read system call. ebx184
;; the file handle to read from, is set to 0 for stdin. ecx points to185
;; a buffer to receive the bytes that are read, and edx equals the186
;; number of bytes to read.193
;; If eax is zero or negative, then there is no more input, and the194
;; compiler proceeds to the eof routine.199
;; Otherwise, esi is advanced four bytes (from the epilog code chunk200
;; to the incptr code chunk), and the character read from the input is201
;; stored in al, with the high bytes of eax reset to zero.206
;; The compiler compares the input character with ">" and "<". esi is207
;; advanced to the next code chunk with each failed test.216
;; The next four tests check for the characters "+", ",", "-", and217
;; ".", respectively. These four characters are contiguous in ASCII218
;; and so are tested for by doing successive decrements of eax.231
;; The remaining instructions, "[" and "]", have special routines for232
;; emitting the proper code. (Note that the jump back to the main loop233
;; is at the edge of the short-jump range. Routines below here234
;; therefore use this jump as a relay to return to the main loop235
;; however, in order to use it correctly, the routines must be sure236
;; that the zero flag is cleared at the time.)245
;; The endbracket routine emits code for the "]" instruction, as well246
;; as completing the code for the matching "[". The compiler first247
;; emits "cmp dh, [ecx]" and the first two bytes of a "jnz near". The248
;; location of the missing target in the code for the "[" instruction249
;; is then retrieved from the stack, the correct target value is250
;; computed and stored, and then the current instruction's jmp target251
;; is computed and emitted.256
lea esi, [byte edi - 8]264
;; This is the entry point, for both the compiler and its compiled265
;; programs. The shared initialization code sets ecx to the beginning266
;; of the array that is the compiled program's data area, and edx to267
;; one. (This also clears the zero flag for the relay jump below.) The268
;; registers are then saved on the stack, to be restored at the end.275
;; At this point, the compiler and its compiled programs diverge.276
;; Although every compiled program includes all the code in this file277
;; above this point, only the three instructions directly above are278
;; actually used by both. This point is where the compiler begins279
;; storing the generated code, so only the compiler sees the280
;; instructions below. This routine first modifies ecx to contain281
;; TEXTORG, which is stored on the stack, and then offsets it to point282
;; to filesize. edi is set equal to codebuf, and then the compiler283
;; enters the main loop.286
mov ch, (TEXTORG >> 8) & 0xFF288
mov cl, filesize - $$289
lea edi, [byte ecx + codebuf - filesize]292
;; Here ends the file image.294
compilersize equ $ - $$