├── .hgignore ├── .hgtags ├── Makefile ├── README.md ├── bin ├── bootstrp.bat ├── shelta.bat ├── shelta86.com ├── sheltan.com ├── sheltas.com ├── sheltas2.com └── vtab.txt ├── doc ├── bootstrp.txt ├── fdos2013.txt ├── nasm2009.txt └── readme.txt ├── eg ├── 99.she ├── demo.she ├── hello.she ├── sheltas.she └── str.she ├── lib ├── 8086 │ ├── 8086.she │ ├── bios.she │ ├── dos.she │ ├── file.she │ ├── gupi.she │ └── string.she └── gupi │ └── linklist.she └── src ├── shelta86.asm └── shelta86.s /.hgignore: -------------------------------------------------------------------------------- 1 | syntax: regexp 2 | 3 | eg/.*\.com 4 | eg/.*\.COM 5 | -------------------------------------------------------------------------------- /.hgtags: -------------------------------------------------------------------------------- 1 | f40dd60ffb991d8addda5b66a0ffb22d14346264 rel_1_0_1999_1223 2 | af698677b31103929db1923411aaddb55b058c03 rel_1_1_2009_0307 3 | 400e96c665048933d5acc769a96cafd226726b8c rel_1_2_2013_1130 4 | 92e1d8c0fd4b17ac114188a75e5a27c3640cac5c rel_1_2_2014_0422 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | YASM?=yasm 2 | 3 | bin/sheltan.com: src/shelta86.s 4 | $(YASM) src/shelta86.s -o bin/sheltan.com 5 | 6 | all: bin/sheltan.com 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Shelta 2 | ====== 3 | 4 | Shelta is a minimal Forth-like language. It has barely any semantics of its 5 | own; it relies on inline machine code to write anything resembling an actual 6 | program in it. 7 | 8 | In the spirit of compilers for languages such as FALSE and brainfuck, a 9 | Shelta-to-8086 compiler was implemented (with some help from Ben Olmstead) as 10 | an MS-DOS `.COM` executable containing less than 512 bytes of 80286 machine 11 | code. 12 | 13 | What's more, it has also been bootstrapped — that is to say, a Shelta compiler 14 | was written in Shelta, which was compiled with the original compiler, and then 15 | compiled again with the resulting compiler, producing a wholly self-hosted 16 | executable. 17 | 18 | For more information, see the files in the `doc` directory of this distribution. 19 | -------------------------------------------------------------------------------- /bin/bootstrp.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM BOOTSTRP.BAT 3 | REM v1.1.2009.0307 (c)1999-2009 Chris Pressey, Cat's-Eye Technologies. 4 | REM Builds the bootstrapped versions (S & S2) of the Shelta compiler. 5 | @echo on 6 | call bin\shelta n eg\sheltas 7 | copy eg\sheltas.com bin\sheltas.com 8 | call bin\shelta s eg\sheltas 9 | copy eg\sheltas.com bin\sheltas2.com 10 | call bin\shelta s2 eg\sheltas 11 | del eg\sheltas.com 12 | -------------------------------------------------------------------------------- /bin/shelta.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM SHELTA.BAT 3 | REM v1.2-2013.1130 (c)1999-2013 Chris Pressey, Cat's-Eye Technologies. 4 | REM A 'make'-like utility for Shelta compilers, as an MS-DOS batchfile. 5 | 6 | REM -- Change the following lines to tailor what libraries are 7 | REM -- included by default. See readme.txt 8 | type lib\8086\8086.she >s.she 9 | type lib\8086\gupi.she >>s.she 10 | type lib\8086\dos.she >>s.she 11 | type lib\8086\string.she >>s.she 12 | type lib\gupi\linklist.she >>s.she 13 | 14 | REM -- This section builds the source file, always called 's.she'. 15 | if not exist %2.she echo Can't find project file %2.she! 16 | if exist %3.she type %3.she >>s.she 17 | if exist %4.she type %4.she >>s.she 18 | if exist %5.she type %5.she >>s.she 19 | if exist %6.she type %6.she >>s.she 20 | if exist %7.she type %7.she >>s.she 21 | if exist %8.she type %8.she >>s.she 22 | if exist %9.she type %9.she >>s.she 23 | if exist %2.she type %2.she >>s.she 24 | type bin\vtab.txt >>s.she 25 | 26 | rem bin\shelta%1.com %2.com 28 | 29 | if errorlevel 32 echo Source file could not be opened. 30 | if errorlevel 16 echo Error - Unknown identifier in source file. 31 | del s.she 32 | -------------------------------------------------------------------------------- /bin/shelta86.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catseye/Shelta/96ff46dbb3749f719d5d4b1e7743eaa079120409/bin/shelta86.com -------------------------------------------------------------------------------- /bin/sheltan.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catseye/Shelta/96ff46dbb3749f719d5d4b1e7743eaa079120409/bin/sheltan.com -------------------------------------------------------------------------------- /bin/sheltas.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catseye/Shelta/96ff46dbb3749f719d5d4b1e7743eaa079120409/bin/sheltas.com -------------------------------------------------------------------------------- /bin/sheltas2.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catseye/Shelta/96ff46dbb3749f719d5d4b1e7743eaa079120409/bin/sheltas2.com -------------------------------------------------------------------------------- /bin/vtab.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/bootstrp.txt: -------------------------------------------------------------------------------- 1 | Making the Snake Eat its Tail: Bootstrapping 2 | -------------------------------------------- 3 | Oct 20 1999, Chris Pressey, Cat's Eye Technologies. 4 | 5 | What is bootstrapping? 6 | ---------------------- 7 | 8 | Bootstrapping is the act of implementing a compiler for a language in 9 | that same language, or a subset of it. It is a well-understood aspect 10 | of compilation and the translation of compilers from one machine onto 11 | a different machine. 12 | 13 | Bootstrapping is a fairly esoteric discipline, however, partly because 14 | there's little need to do it more than once for any given compiler and 15 | any given machine, but also because at a basic level, bootstrapping is 16 | somewhat difficult to understand. 17 | 18 | How, for example, do you write a compiler that compiles itself, without 19 | first having the compiler??? Sounds more than a little bit like the 20 | "Which came first, the chicken or the egg" paradox. 21 | 22 | The term 'bootstrap' itself comes from the whimsical idea that if you 23 | were to bend over and tug at the straps on your own boots, you could 24 | lift yourself off the ground. 25 | 26 | So what's the trick to making a compiler levitate? 27 | -------------------------------------------------- 28 | 29 | Well, first of all let me put your mind at rest - there's no paradox 30 | or magic or anything else spooky involved, although it can feel that 31 | way sometimes. There are in fact two realistic options for 32 | bootstrapping: 33 | 34 | - Write (on paper) the compiler in the language which it compiles, 35 | then hand-translate (i.e. manually compile) it to assembly or 36 | machine language. This approach has been the one taken for the 37 | first compilers for both Pascal and LISP. 38 | 39 | - First write a compiler for the language in another, already-available 40 | language, such as assembly language, or C. Then re-write that compiler 41 | in the language which it compiles. This is the approach many 42 | bootstraps have taken. 43 | 44 | How was Shelta bootstrapped? 45 | ---------------------------- 46 | 47 | I took the second approach. 48 | 49 | First I wrote the Shelta compiler SHELTA86.COM in assembly language 50 | (SHELTA86.ASM) using Turbo Assembler 3.1. 51 | 52 | +----------------+ 53 | SHELTA86.ASM | TASM ---> 8086 | SHELTA86.COM 54 | +----+ +----+ 55 | | 8086 | 56 | +------+ 57 | TASM.EXE 58 | 59 | This is a 'tee diagram' as is commonly used by people who have to 60 | do these sorts of things... it's pretty simple to understand. 61 | 62 | The filename on the left is the input into the 'tee', the filename on 63 | the right is the output of the 'tee'. The filename on the bottom is 64 | the tool which is translating the input into the output. The formats 65 | listed inside the tee are the languages each of the files is written in. 66 | 67 | Because the output of this tee is a compiler, however, it can exist as 68 | a tee in it's own right: 69 | 70 | +-----------------+ 71 | | Shelta --> 8086 | 72 | +----------+-----+ +----+ 73 | SHELTA86.ASM | TASM ---> 8086 | 8086 | 74 | +----+ +----+------+ 75 | | 8086 | SHELTA86.COM 76 | +------+ 77 | TASM.EXE 78 | 79 | So I re-wrote SHELTA86.ASM in Shelta/GUPI, calling it SHELTAS.SHE. 80 | 81 | +-----------------+ 82 | SHELTAS.SHE | Shelta --> 8086 | SHELTAS.COM 83 | +----------+-----+ +----+ 84 | SHELTA86.ASM | TASM ---> 8086 | 8086 | 85 | +----+ +----+------+ 86 | | 8086 | SHELTA86.COM 87 | +------+ 88 | TASM.EXE 89 | 90 | Lo and behold! A Shelta compiler written in Shelta. But that's not 91 | the whole story - at this point the bootstraps have been pulled taut, 92 | but there is one more tug that must be made to actually get levitating. 93 | The compiler SHELTAS.COM must prove it's worth, meeting it's maker 94 | so to speak: 95 | 96 | +-----------------+ 97 | SHELTAS.SHE | Shelta --> 8086 | SHELTAS2.COM 98 | +-----------+-----+ +----+ 99 | SHELTAS.SHE | Shelta --> 8086 | 8086 | 100 | +----------+-----+ +----+------+ 101 | SHELTA86.ASM | TASM ---> 8086 | 8086 | SHELTAS.COM 102 | +----+ +----+------+ 103 | | 8086 | SHELTA86.COM 104 | +------+ 105 | TASM.EXE 106 | 107 | Now, because of some subtle differences in SHELTA86.ASM and SHELTAS.SHE 108 | (the assembly language version does no optimization), the sizes and 109 | contents of all three of these Shelta compilers differ slightly. But 110 | if the process was carried on one step further, the resultant compiler 111 | would be the same as SHELTAS2.COM. The following might help clarify why 112 | this is: 113 | 114 | SHELTAS.SHE +-----------------+ 115 | Optimizing | Shelta --> 8086 | SHELTAS2.COM 116 | SHELTAS.SHE +-----------+-----+ +----+ Optimizing 117 | Optimizing | Shelta --> 8086 | 8086 | Optimized 118 | +----------+-----+ +----+------+ 119 | SHELTA86.ASM | TASM ---> 8086 | 8086 | SHELTAS.COM 120 | NonOptimizing+----+ +----+------+ Optimizing 121 | Hand-Optimized | 8086 | SHELTA86.COM Non-Optimized 122 | +------+ Non-Optimizing 123 | Hand-Optimized 124 | 125 | OK, but why did you choose to do this, anyway? 126 | ---------------------------------------------- 127 | 128 | Well, there was certainly no reason to. I was not moving Shelta from 129 | one machine to another, nor was I treating SHELTA86.ASM as a quick 130 | hack which would be discarded once an optimizing compiler could be 131 | bootstrapped. 132 | 133 | On the other hand, there was no reason *not* to, so... 134 | 135 | I did it mainly to say that I could. Not everyone can design a language, 136 | write a compiler for it in the form of a 1/2-kbyte COM file, then 137 | bootstrap it. I'm not sure I can say it was the hardest thing I've 138 | ever done, but it was difficult enough. 139 | 140 | Plus, well, it's the kind of freaky self-referential thing I've always 141 | been interested in. A compiler written in the language which it 142 | compiles, which in the end appears to have been compiled by itself. 143 | 144 | In the preceding section I may have made what I did seem like a walk 145 | in the park, but it wasn't. A large portion of the time was spent on: 146 | 147 | - fixing bugs in SHELTA86.ASM 148 | - building GUPI so that Shelta could be powerful enough to bootstrap 149 | meaningfully (I could have just included SHELTA86.COM entirely as 150 | inline assembly, but that would kind of defeat the purpose) 151 | - fixing bugs in SHELTAS.SHE 152 | - testing SHELTAS2.COM - more concentration than time, actually. 153 | When you have five Shelta compilers, two in source form 154 | (Turbo Assembler and Shelta) and three in executable form, you're 155 | bound to get a little disoriented from having to keep track of 156 | their interdependencies. 157 | 158 | I can also offer the following piece of advice to anyone who is going 159 | to be trying something similar: if you've already squashed down your 160 | first compiler's source code in order to (say) claim bragging rights on 161 | having built an 512-byte compiler, DO NOT attempt to simply translate 162 | the optimized assembly code into another language. Rewrite it instead. 163 | Especially for a program of this size. Initially trying to do a 164 | literal translation from SHELTA86.ASM to SHELTAS.SHE was easily the 165 | biggest mistake I made. 166 | 167 | Where can I find further information on bootstrapping? 168 | ------------------------------------------------------ 169 | 170 | Two books are of note: the notorious "Dragon" book by Aho, Sethi and 171 | Ullman gives it a brief once-over; "Compilers and Compiler Generators" 172 | by Terry gives it a more thorough and readable treatment. 173 | 174 | Happy levitating! 175 | 176 | Chris Pressey, Oct 20 1999 177 | Cat's Eye Technologies, Winnipeg, Manitoba, Canada 178 | -------------------------------------------------------------------------------- /doc/fdos2013.txt: -------------------------------------------------------------------------------- 1 | Another four years, and here I am again. 2 | 3 | Perhaps it's simply bad luck to name a programming language after a spoken 4 | human language. After all, it didn't seem to work too well for the Plain 5 | English folks. 6 | 7 | Well anyway. Lately I've been trying to put my languages and things online. 8 | The rationale is straightfoward enough; Javascript and HTML5 are mature 9 | enough to make it possible now, and people by their nature are lazy. If, to 10 | try something out, you need to install this, that, and the other on your 11 | system and configure them, chances are you'll be like, ehh. Don't feel like 12 | it right now. But if it's just on a web page, you don't have that obstacle. 13 | 14 | And my thoughts turned to my DOS stuff, because one of the more burdensome 15 | things to ask the user to install is a system emulator. 16 | 17 | I had come across jpc some time earlier, but I'd really prefer something in 18 | Javascript, just because of the poor security track record Java has had 19 | lately. (Guys, the whole idea was that you didn't have to trust a Java 20 | applet, because it would be running in a virtual machine that didn't allow... 21 | yeah, anyway) I looked a bit more and found jslinux, but it isn't open 22 | source. So I looked a bit more and eventually found v86. It's not quite 23 | as mature as the others, but for simple things it seems to work fine, and 24 | as a nice bonus it's BSD-licensed, too. 25 | 26 | So I started preparing a FreeDOS boot disk image with the Shelta distribution 27 | on it. To my surprise, it was flakey. Sometimes the compilers would work, 28 | and sometimes they'd hang. Sometimes the bootstrapped compiler seemed to 29 | work better than the Nasm one, and sometimes it didn't. The problems 30 | persisted after I re-assembled the source with the latest version of Nasm, 31 | which did generate slightly different code, but only slightly (only two 32 | instructions in sheltan.com were changed, and the code was the same size.) 33 | 34 | This was all somewhat surprising. The thing is, it worked perfectly in DOSBox 35 | (which is probably what I was developing it under in 2009.) And the same 36 | flakiness was happening under both QEMU and v86, leading me to believe it was 37 | a problem with FreeDOS. I tried FreeDOS version 1.1 as well as 1.0 -- no 38 | difference in behaviour. I briefly looked for a web-enabled DOSBox, and it 39 | turns out there are two: jdosbox, which is Java, and a gigantic (~4M) jarfile 40 | besides, and jsdosbox, which is a GWT hack applied to jdosbox, which is 41 | frankly really unappealing. 42 | 43 | So I turned my attention back to FreeDOS, determined to get Shelta working 44 | under it under v86. 45 | 46 | After some experimentation, I figured it must be INT 21h/ah=07h that FreeDOS 47 | was having problems with. It's the legacy DOS function for reading a 48 | character from standard input. Shelta uses it because it's significantly 49 | fewer instructions than the "normal" DOS input function. 50 | 51 | Thing is, INT 21h/ah=07h doesn't have a way to indicate EOF. That's why 52 | Shelta has traditionally insisted that Shelta source code end with a zero 53 | byte (ASCII NUL character,) so that it can tell when the source has ended. 54 | 55 | If the Shelta compiler wasn't consistently reading that character, that could 56 | explain the hanging. 57 | 58 | But after more experimentation, I couldn't tell what was wrong with 59 | FreeDOS'es implementation of this function. I even looked at the FreeDOS 60 | source code, but it wasn't too helpful; it seems to be implemented in terms 61 | of the "normal" DOS read function. 62 | 63 | It wasn't until I tried replacing the check for the NUL character with a 64 | different character that it worked. Yes -- FreeDOS sometimes has trouble 65 | reading NUL characters using this function. I'm not sure why. Since the 66 | function is for reading from standard input, and in the absence of redirection 67 | standard input means the keyboard, and on the keyboard you can type a NUL 68 | (Ctrl+@), and since it works on both MS-DOS whence I originally developed 69 | Shelta and DOSBox -- this is probably a bug in FreeDOS. It might be confused 70 | about standard input not being open in "binary mode" or something. I'd submit 71 | a bug report, but this is possibly the only piece of software on the planet 72 | that is affected... 73 | 74 | Anyway, solving the problem after realizing this was simple. Instead of 75 | insisting Shelta programs end with a NUL, we now insist that they end with 76 | everybody's favourite ASCII character, the VERTICAL TAB. And we apologize 77 | to all those Shelta users who have huge code bases written in Shelta in which 78 | vertical tabs are used to critical effect in some way I can't imagine. For 79 | the rest of us, the SHELTA.BAT batchfile handles all this for us, so the change 80 | is pretty much transparent. 81 | 82 | The new check for vertical-tab-as-EOF does add a few bytes to the compilers, 83 | but the Nasm version is still under 512 bytes. 84 | 85 | And with those small changes, the Shelta language is now at version 1.2. 86 | 87 | Happy bootstrapping -- in your browser! 88 | Chris Pressey 89 | November 30, 2013 90 | Vancouver, BC 91 | -------------------------------------------------------------------------------- /doc/nasm2009.txt: -------------------------------------------------------------------------------- 1 | Well, here we are, ten years later. 2 | 3 | What brought me back here was the fact that no one uses Turbo Assembler 4 | anymore. I'm not even sure if Borland is around anymore. And I started 5 | thinking, well, I use NASM these days; maybe I should translate the 8086 6 | assembly version of Shelta into NASM. It's free, and tinkers like free. 7 | Plus Ben recommended it, way back when it was something like version 0.98. 8 | I said I'd wait until it was past version 1.0. Well, it's 2.mumble now, 9 | so it's high time, right? 10 | 11 | So I started translating, and I discovered just how much more explicit 12 | NASM is. I was aiming to reproduce the same SHELTA86.COM file, or at 13 | least one of the same length with the labels in the same places. I had 14 | to go through some lengths to stop NASM from inserting redundant ds: 15 | segment references, and from padding the start of the data segment to a 16 | word boundary (I just left out the data segment directive entirely.) 17 | 18 | But then, when I got it all nice and translated -- Shock! Horror! 19 | I discovered the awful truth: shelta86.com cannot compile sheltas.she. 20 | 21 | Where did I get the nerve to say that I had bootstrapped a half-kilobyte 22 | compiler? Misleading at best! I had bootstrapped a probably about 555- 23 | byte compiler. I then butchered the language it compiled -- removing 24 | three instruction forms -- so that I could shove that compiler into half 25 | a kilobyte. At no time did I actually bootstrap the <512-byte version. 26 | No, that would have required rewriting shelta.she to have a lot of blocks 27 | with temporary names that were only pushed once, and other garbage like 28 | that, so that the stripped-down compiler wouldn't choke on it. One of 29 | the nice things about (the full) Shelta, I think, is that while it is 30 | small, it doesn't force you to wallow in garbage. At least not a lot. 31 | 32 | So I screwed up my courage, cracked my knuckles, and tried to live up to 33 | my own hype. I re-instated the string (`) and push-pointer-anonymously 34 | (]) functions, which bumped the size back up to around 555 bytes. I 35 | didn't bother with the push-named-pointer (]Name) form, because it's not 36 | used in sheltas.she. OK, so neither are strings, but a lot of the other 37 | example Shelta programs use strings, so I thought they would be good to 38 | have. 39 | 40 | I then proceeded to squish the living daylights out of the new, NASM- 41 | language shelta86.s. Mostly this involved long, hard looks at the logic 42 | and detailed liveness analysis (done by hand, of course.) There were a 43 | few small tweaks that were easily done; for example, removing one or two 44 | instructions that were completely unnecessary, and replacing the jmp 45 | in the handler dispatch with a call (several ret statements take up less 46 | space than several jmps back to the top of the loop. Who cares about 47 | wasting space on the stack?) The most significant savings, though, came 48 | from factoring out some code to write a push instruction and calling an 49 | existing routine for it instead, and from shuffling registers to keep dx 50 | free long enough so that it, instead of a memory location, could be used 51 | to store one of the crucial computed pointers. The result: a 509-byte 52 | executable which did all that the old shelta86.com could do *and* enough 53 | more to actually compile sheltas.she! 54 | 55 | The old shelta86.com is still in the distribution, for comparison, or 56 | nostalgia, or completeness, or whatever. The new executable is called 57 | sheltan.com (for Shelta in NASM, I suppose.) The bootstrapping and 58 | driver scripts have been changed slightly to accomodate this newfangle- 59 | ness. I haven't touched the other documentation, which is now slightly 60 | inaccurate but still quite useful. 61 | 62 | Happy bootstrapping! 63 | Chris Pressey 64 | March 7, 2009 65 | Bellevue, WA 66 | -------------------------------------------------------------------------------- /doc/readme.txt: -------------------------------------------------------------------------------- 1 | Shelta 2 | ------------------------ 3 | * * * NEAR-BETA VERSION v1999.12.23 * * * 4 | 5 | Shelta NEAR-BETA (c)1999 Chris Pressey, Cat's-Eye Technologies. 6 | All rights reserved. No warranty for suitability of any kind. 7 | Consider yourself lucky if your head doesn't blow up. 8 | This product is freely redistributable provided that all distributed copies 9 | include the entire original unmodified archive. 10 | 11 | What is Shelta ? 12 | --------------------------------- 13 | 14 | Shelta (which I'll generally just call Shelta during 15 | the scope of it's documentation) is a set of interrelated software 16 | systems: 17 | 18 | - Shelta the Language - somewhere between FORTH, FALSE, and Assembler 19 | - SHELTA86.COM the Tool - a Shelta compiler written in 8086 asm 20 | - SHELTA.BAT the Compiler - organizer ('make') for Shelta files & libraries 21 | - SHELTAS.COM and SHELTAS2.COM - Shelta compilers written in Shelta 22 | - The GUPI Protocol - a standardized library of Shelta definitions 23 | 24 | What's the history of Shelta? 25 | ----------------------------- 26 | 27 | My first programming language ever was called Maentwrog, a term taken 28 | from that wholly remarkable book, _The Meaning of Liff_, by Douglas 29 | Adams. 30 | 31 | Maentwrog sucked. But, it worked, kind of. It was interpreted, but 32 | as I recall, it wasn't even tokenized... making the interpreter more 33 | than a little slow. It was basically a subset of FORTH - not much 34 | special there. 35 | 36 | My second programming language ever was based on Maentwrog, and it 37 | spawned a big hit called Befunge. Befunge left Maentwrog in the dust, 38 | because there WAS much special there - Befunge is 2D, and that's 39 | trippy. If you haven't tried to program in Befunge yet... try it! 40 | 41 | However, I've always felt that I fell somewhat short of the mark I 42 | was trying to make with Befunge-93. After all, it was inspired by 43 | FALSE and Brainf*ck, but unlike either of them, it was not a small 44 | machine-dependent compiler. It was a big, portable interpreter. 45 | (In 1998 I rewrote the interpreter in assembly language to make a 46 | Befunge-93 interpreter that fit into 2K. But it's just not the same.) 47 | The urge to write a tiny compiler has been gnawing at me for the 48 | past few years. 49 | 50 | As such, Maentwrog has not gone totally forgotten. Over the years I've 51 | made a few attempts at reworking the Maentwrog language, with little 52 | success, until now. The main thing holding back Maentwrog for so 53 | long was it's lack of strict design principles. Only now has the 54 | subconscious philosophy of Maentwrog evolved to a point where it means 55 | anything. The result is Shelta. 56 | 57 | What is Shelta's Etymology? 58 | --------------------------- 59 | 60 | _The Oxford Dictionary of Current English_, 1996, describes Shelta as an 61 | "ancient hybrid secret language used by Irish tinkers, Gypsies, etc." 62 | Shelta is targeted at a similar present-day audience. 63 | Would you sometimes rather consider yourself a tinker (or a Gypsy) than 64 | a computer programmer? Then Shelta may just be for you. 65 | 66 | What is Shelta's Philosophy? 67 | ---------------------------- 68 | 69 | Shelta's philosophy is one of simplicity of translation. Shelta is 70 | easy to implement in assembly language. Shelta is nearly as low-level 71 | as assembly language. Very small Shelta compilers can be built. 72 | 73 | Shelta is also relatively easy to bootstrap - that is, it's not that 74 | difficult to implement a Shelta compiler in Shelta itself. In fact, 75 | that (along with writing a ridiculously small compiler) was my main 76 | motivation for designing Shelta and building SHELTA86.COM. For more 77 | information on the bootstrapped Shelta compilers and bootstrapping in 78 | general, see the file bootstrp.txt. 79 | 80 | In and of itself, Shelta has no actual functional semantics: only 81 | structural ones. It relies on a either library of functions (such as 82 | GUPI, described below) or inline machine language in order to be 83 | considered Turing-Complete. That is, not unlike the ancient Shelta 84 | language, Shelta is hybridized: the actual programming 85 | is usually done in Shelta/GUPI. 86 | 87 | What are Shelta's Influences? 88 | ----------------------------- 89 | 90 | Shelta is influenced largely by the wholly remarkable programming 91 | language FALSE, by Wouter van Oortmerssen - "FORTH with lambda 92 | functions". However, it is lower-level than FALSE. It is more like 93 | FORTH in some ways - for example, multicharacter user-defined names 94 | can be used to name unlimited variables, not just the a-z in FALSE. 95 | Lastly, it is unlike FORTH and more like Assembler in that there is 96 | no FORTH-like environment nor any fixed-size blocks of text as 97 | input files. 98 | 99 | What is Shelta's Syntax? 100 | ------------------------ 101 | 102 | Tokens are delimited by whitespace - any whitespace and as much of it 103 | as you like, but as long as two non-whitespace characters are adjacent, 104 | they are considered part of the same token. Shelta's idea of 105 | whitespace is, in ASCII, everything from #32 (space) down to #1 (^A). 106 | (#0 (NUL) is considered synonymous with an end-of-file condition.) 107 | 108 | The exception to the above rule is a comment block, which begins 109 | (anywhere) with a ";" character and ends at the next ";" character. 110 | This can occur even in the middle of a token, so "HE; foo ;LLO" is 111 | taken to be the token "HELLO". 112 | 113 | User-defined tokens - depicted with "Name" in the following table - 114 | can contain any non-whitespace characters, and can begin with any 115 | non-whitespace characters except for "[", "]", "\", '^', "_" and "`". 116 | (This includes digits - "1" by itself is a name, not a number.) 117 | 118 | What are the recognized tokens of Shelta? 119 | ----------------------------------------- 120 | 121 | [ Begin block. 122 | * ] End block, push pointer. 123 | ]=Name End block, name pointer. 124 | ]:Name End block, name pointer to compile-time-only block. 125 | * ]Name End block, push named pointer. 126 | ^Name Push pointer to previously named block. 127 | _^Name Insert pointer to previously named block. 128 | Name Insert contents of previously named block. 129 | 130 | * `ABC Insert string. 131 | _123 Insert decimal byte. 132 | __1234 Insert decimal word. 133 | \123 Push decimal word. 134 | 135 | * = not available in SHELTA86. 136 | 137 | What are some common syntactic idioms in Shelta? 138 | ------------------------------------------------ 139 | 140 | [ `ABC ] Push pointer to string. 141 | [ _5 _5 _5 _3 ] Push pointer to byte array. 142 | [ __1234 __9999 ] Push pointer to word array. 143 | [ _5 _5 ]=my-data Name a byte array. 144 | [ _^my-data ]=my-refs Name an array of references to data. 145 | _88 Insert anonymous inline machine code. 146 | [ _88 ]:xyz Define xyz as inline machine code. 147 | xyz Insert named inline machine code. 148 | [ bar baz ]:foo Declare 'foo' as an inline proc 149 | foo Insert 'foo' as an inline proc. 150 | [ ]=bar Define named label. 151 | 152 | Where do you use : instead of = after ]? 153 | ---------------------------------------- 154 | 155 | Originally, Shelta did not distinguish between blocks used as 156 | updatable stores, subroutines, or templates for inlined instructions. 157 | As such, it would include all of them into the resulting executable, 158 | even the blocks only used at compile-time to define inline instructions. 159 | 160 | By using : instead of = after ], the Shelta compiler will treat the 161 | block as containing information which is only used at compile-time. 162 | This is essentially a contract between the programmer and the compiler; 163 | the programmer promises not to expect the ^Name or _^Name syntax to work 164 | on the block, and the compiler ensures the block does not show up 165 | extraneously in the resulting executable. 166 | 167 | What are some of the quirks of Shelta? 168 | -------------------------------------- 169 | 170 | Shelta's lambda syntax is not uniform. On the top level, an empty 171 | block such as this: 172 | [ ]=label 173 | is not necessarily defined to actually work. It is only defined to 174 | produce sensible results when nested within another block like this: 175 | [ [ ]=label foo ]=block 176 | Also, this is NOT the same thing as saying: 177 | [ [ foo ]=block1 bar ]=block2 178 | This linearizes block1 out of block2, almost as if you had said 179 | [ foo ]=block1 [ bar ]=block2 180 | Except that the identifier block1 is 'supposed' to be local to block2 181 | (it ISN'T, but it might be good programming practice to treat it that 182 | way anyway! :-) 183 | 184 | To make things even worse, nesting more than two levels deep like so 185 | [ foo [ bar [ baz ] quuz ] phlef ] 186 | probably doesn't do what you expect. Feel free to experiment, though. 187 | 188 | Shelta does not have or use forward references. That seems to be no 189 | problem, with the lambda-like declarations, but it can often force you 190 | to write weird and awkwardly structured code. If you need to refer to 191 | the current block from within it, you can always name a block twice: 192 | 193 | [ foo ^this bar ]=this ; won't work! ; 194 | [ [ ]=-this foo ^-this bar ]=this ; works! ^this == ^-this ; 195 | 196 | What is SHELTA86.COM? 197 | --------------------- 198 | 199 | The Shelta compiler is implemented in 8086 assembly language and assembles 200 | to a tiny (LESS THAN HALF A KILOBYTE! :-) executable program. There are 201 | several restrictions on the program in order to trim fat: 202 | 203 | - Input file goes in standard input, .COM file comes out standard output. 204 | File should end in a ^@ (NUL) character to indicate EOF. 205 | This NUL should be preceded by whitespace (otherwise it'll form 206 | part of a token - you don't want that! :-) 207 | - If a file error occurs, error code 32 is returned. 208 | - If an undefined token is found, error code 16 is returned. 209 | - The forms ] (End block push pointer,) ]Name (End block push named 210 | pointer,) and `xyz (Insert String) are not supported. It is not 211 | difficult to work around these by explicitly naming and pushing 212 | blocks and using ASCII decimal sequences for strings. These 213 | inequities could even be addressed by a simple pre-processor. 214 | 215 | What is SHELTA.BAT? 216 | ------------------- 217 | 218 | SHELTA.BAT allows one to harness a Shelta compiler such as SHELTA86.COM, 219 | without having to directly put up with it's silly interface. 220 | 221 | Usage: 222 | SHELTA compiler project-file {library-files...} 223 | 224 | 'compiler' is one of: 86 (the assembly-language compiler,) S (the 225 | compiler written in Shelta and compiled with 86), or S2 (the 226 | compiler written in Shelta and compiled with S.) (See the file 227 | bootstrp.txt for more information on the Shelta compilers written 228 | in Shelta.) 229 | 230 | You don't need to append '.she' to project-file or any library-file 231 | you choose to include, SHELTA will do that for you and will 232 | automatically name the output 'project-file.COM'. 233 | 234 | SHELTA should support up to nine arguments, so you can specify seven 235 | library files on the command line (there's no 'include' directive in 236 | Shelta itself.) 237 | 238 | As an example of how to use SHELTA, here's how to build and test one 239 | of the example Shelta/GUPI programs, "Hello, world!": 240 | 241 | (Updated Dec 8 2002 to reflect new directory structure:) 242 | 243 | cd shelta-<> 244 | bin\shelta s2 prj\hello 245 | prj\hello 246 | 247 | How can I specify what libraries for SHELTA.BAT to use by default? 248 | ------------------------------------------------------------------ 249 | 250 | By default SHELTA.BAT includes the following libraries: 251 | 252 | 8086\8086.she 8086 subset definition 253 | 8086\gupi.she General GUPI library (defined in 8086 subset) 254 | 8086\string.she GUPI string functions (defined in 8086 subset) 255 | 8086\dos.she DOS-dependent GUPI I/O (defined in 8086 subset) 256 | gupi\linklist.she Linked list library (defined in GUPI) 257 | 258 | You can edit SHELTA.BAT to change which libraries it uses by default. 259 | (It is just a .BAT file after all.) 260 | 261 | 8086.she: One could presumably replace these inline instructions with 262 | equivalent instructions for another relative-addressing processor, 263 | change a few lines of SHELTA86.ASM, and voila! You could compile 264 | Shelta to some other CPU. It'd be a cute trick... 265 | 266 | gupi.she: While Shelta comes with GUPI, you don't need to use GUPI 267 | with Shelta! You can comment out gupi.she and completely redefine the 268 | semtantics of your Shelta. For example, you could use a very small 269 | set of instructions (a tar pit) and use Shelta to compile languages 270 | very similar to Brainf*ck, Malbolge, etc. 271 | 272 | dos.she: You can replace the dos.she library with the bios.she library; 273 | it does the same thing but goes directly through the BIOS instead, and 274 | you can write code that will work without DOS loaded (so you could even 275 | build your own OS or embedded controller code with Shelta! ;-) 276 | 277 | What is GUPI? 278 | ------------- 279 | 280 | GUPI stands for Generic Utilitarian Programming Interface. GUPI 281 | is a set of Shelta definitions that acts as a standard library. 282 | 283 | (Fact is, I'm not a big fan of how GUPI turned out; it is a 284 | contrived and contingent beast, rather than the beautifully 285 | designed and conceptually airtight scheme I had hoped for. 286 | But I figure that if it was good enough to get me this far, it's 287 | worth keeping around, and the hybrid design of Shelta makes it 288 | easy to swap it for something else at a later time, anyway.) 289 | 290 | The GUPI semantics as presented here work on a stack of word values 291 | in a FORTH-like manner. Note that GUPI is not yet well documented. 292 | Nor is it guaranteed not to change (although it looks unlikely; 293 | any major change will warrant it's own library; "GUPII" perhaps? :-) 294 | 295 | What are some of the naming conventions of GUPI? 296 | ------------------------------------------------ 297 | 298 | Generally speaking... 299 | The suffix b indicates 'byte'. 300 | The suffix c indicates 'character'. 301 | The suffix if indicates 'decision on a boolean'. 302 | The suffix s indicates 'string with length' (block). 303 | The suffix w indicates 'word' (normally 16-bit). 304 | The suffix z indicates 'null-terminated string' (ASCIIZ). 305 | 306 | Lack of any suffix usually indicates 'any type'. 307 | 308 | What are the basic GUPI commands? 309 | --------------------------------- 310 | 311 | pop pop and discard top stack element 312 | dup duplicate top stack element 313 | swap pop a, pop b, push a, push b 314 | 315 | to pop pointer, machine unary jump to pointer 316 | do pop pointer, machine sub call pointer 317 | toif pop pointer, pop boolean, unary jump if nonzero 318 | doif pop pointer, pop boolean, sub call if nonzero 319 | begin pop return pointer and push onto call stack 320 | end push return pointer from call stack 321 | 322 | begin, end, and do/doif lead to the following GUPI idiom: 323 | [ begin baz end ]=bar Declare 'bar' as a subroutine. 324 | ^bar do Call 'bar' as a subroutine call. 325 | 326 | What are the memory-access commands? 327 | ------------------------------------ 328 | 329 | getb pop pointer, push byte data at pointer 330 | putb pop pointer, pop byte value, write at pointer 331 | getw pop pointer, push word data at pointer 332 | putw pop pointer, pop word value, write at pointer 333 | 334 | What are the arithmetic and logic commands? 335 | ------------------------------------------- 336 | 337 | ++ pop a, push a + 1 338 | -- pop a, push a - 1 339 | ** pop a, push a << 1 340 | // pop a, push a >> 1 341 | << pop a, pop b, push b << a 342 | >> pop a, pop b, push b >> a 343 | + pop a, pop b, push b + a 344 | - pop a, pop b, push b - a 345 | * pop a, pop b, push b * a 346 | / pop a, pop b, push b / a 347 | % pop a, pop b, push b mod a 348 | *1 /% pop a, pop b, push b / a, push b mod a 349 | ! pop a, push binary not a 350 | zero pop a, push 1 if a = 0, push 0 otherwise 351 | & pop a, pop b, push a binary and b 352 | | pop a, pop b, push a binary or b 353 | ~ pop a, pop b, push a binary xor b 354 | 355 | *1. The algorithm commonly used for binary division actually computes 356 | the results of both division and modulo (remainder for a > 0). If 357 | both results are desired by the program, using /% is usually nearly 358 | twice as efficient as using / and % seperately. 359 | 360 | Indirect arithmetic? 361 | -------------------- 362 | 363 | @++ pop pointer, increment word at pointer 364 | @-- pop pointer, decrement word at pointer 365 | 366 | How does GUPI interface with the operating system? 367 | -------------------------------------------------- 368 | 369 | outs pop length, pop pointer, send bytes to stdout 370 | outc pop word, send low byte to stdout 371 | inc wait for input on stdin, push character read 372 | qinc quietly wait for input on stdin, push character 373 | chkin immediately return input status (is a char waiting?) 374 | flin flush all unread input 375 | halt pop a, stop program and return to operating system 376 | with error code 'a' 377 | 378 | And dynamic memory? 379 | ------------------- 380 | 381 | malloc pop size, push ptr to memory of length size 382 | mfree pop ptr, reset heap ptr to ptr 383 | (Note that mfree will free ALL pointers that were 384 | allocated with malloc since the pointer that is being 385 | freed was allocated. It's good for local 386 | linked lists and such, but be careful!) 387 | 388 | What is "Portable Shelta/GUPI"? 389 | ------------------------------- 390 | 391 | The short answer is, "Portable Shelta/GUPI" is the subset of the 392 | union of the Shelta and GUPI languages where, through patience 393 | and restraint - i.e. discipline - the Shelta/GUPI programmer 394 | does not use any machine-dependent or self-modifying code, and 395 | restricts themselves to the GUPI functions that do likewise or 396 | are specified precisely and abstractly enough to be ported, 397 | that is, re-written in some other machine or VM bytecode. 398 | 399 | Where can I get updates on Shelta's condition? 400 | ---------------------------------------------- 401 | 402 | Shelta's official web site is located at: 403 | 404 | http://www.catseye.mb.ca/esoteric/shelta/ 405 | 406 | Happy tinkering! 407 | 408 | Chris Pressey, Dec 23 1999 409 | Cat's-Eye Technologies, Winnipeg, Manitoba, Canada 410 | -------------------------------------------------------------------------------- /eg/99.she: -------------------------------------------------------------------------------- 1 | ; 2 | 99.she v1999.12.23 (c)2000 Chris Pressey, Cat's Eye Technologies. 3 | The song "Ninety-Nine Bottles of Beer" implemented in Shelta/GUPI. 4 | ; 5 | 6 | [ _32 `bottles _32 `of _32 `beer _32 `on _32 `the _32 `wall, _13 _10 ]=L1 7 | [ _32 `bottles _32 `of _32 `beer, _13 _10 ]=L2 8 | [ `Take _32 `one _32 `down, _32 `pass _32 `it _32 `around, _13 _10 ]=L3 9 | [ _32 `bottles _32 `of _32 `beer _32 `on _32 `the _32 `wall. _13 _10 _13 _10 ]=L4 10 | 11 | [ `9 ]=bh [ `9 ]=bl 12 | [ begin ^bh getb outc ^bl getb outc end ]=btls 13 | [ begin ^bh getb \1 - ^bh putb \57 ^bl putb end ]=digit 14 | 15 | [ [ ]=iloop 16 | 17 | ^btls do 18 | ^L1 \31 outs 19 | 20 | ^btls do 21 | ^L2 \19 outs 22 | 23 | ^L3 \32 outs 24 | 25 | ^bl getb \1 - ^bl putb 26 | ^bl getb \47 - zero ^digit doif 27 | 28 | ^btls do 29 | ^L4 \33 outs 30 | 31 | ^bh getb \47 - ^iloop toif 32 | 33 | \0 halt 34 | ] to 35 | -------------------------------------------------------------------------------- /eg/demo.she: -------------------------------------------------------------------------------- 1 | ; 2 | demo.she v1999.12.23 (c)2000 Chris Pressey, Cat's Eye Technologies. 3 | A demonstration of some of the basic features of Shelta and GUPI. 4 | ; 5 | 6 | [ 7 | [ ]=hw `Hello, _32 `world! ; an empty block denotes a label ; 8 | [ ]=eol _13 _10 9 | ]=hello 10 | 11 | [ _0 ]=i 12 | [ _0 ]=pad 13 | [ __0 ]=hptr 14 | 15 | [ 16 | begin 17 | \1024 malloc ^hptr putw 18 | [ ]=wloop 19 | ^i getb ^hptr getw ^i getb + putb 20 | ^i getb ++ ^i putb 21 | ^i getb ^wloop toif 22 | ^hptr getw \32 + \223 outs 23 | end 24 | ] do 25 | 26 | ^hello \15 outs 27 | 28 | ^hw \12 outs 29 | ^eol \2 outs 30 | 31 | ^hello getb outc 32 | 33 | ^hello \1 + getb outc 34 | 35 | \65 ^hello putb ^hello \15 outs 36 | 37 | \1000 \8 / outc 38 | \8 \8 * ++ outc 39 | \8 \9 * ++ outc 40 | \9 \9 * -- outc 41 | 42 | flin 43 | [ 44 | [ ]=loop 45 | inc outc ^loop to ;forever!; 46 | ] to 47 | -------------------------------------------------------------------------------- /eg/hello.she: -------------------------------------------------------------------------------- 1 | ; 2 | hello.she v1999.12.23 (c)2000 Chris Pressey, Cat's Eye Technologies. 3 | The ubiquitous greeting message, implemented in Shelta/GUPI. 4 | ; 5 | [ `Hello, _32 `world! _13 _10 ] \15 outs \0 halt 6 | -------------------------------------------------------------------------------- /eg/sheltas.she: -------------------------------------------------------------------------------- 1 | ; 2 | sheltas.she v1999.12.23 (c)2000 Chris Pressey, Cat's Eye Technologies. 3 | A bootstrappable Shelta compiler written in Shelta/GUPI. 4 | ; 5 | 6 | [ __0 ]=safestart 7 | [ __0 ]=namestart 8 | 9 | [ __0 ]=codeba 10 | [ __0 ]=stacba 11 | [ __0 ]=safeba 12 | [ __0 ]=macrba 13 | [ __0 ]=tokenba 14 | 15 | [ __0 ]=symthead 16 | [ __0 ]=codeh 17 | [ __0 ]=stach 18 | [ __0 ]=safeh 19 | [ __0 ]=macrh 20 | [ __0 ]=tokenh 21 | 22 | [ begin \16 halt end ]=badtok 23 | [ begin dupz end ]=fndupz 24 | 25 | ;--------------------------------------; 26 | 27 | [ __0 ]=newn 28 | [ ; addr dlen strz -> void ; 29 | begin 30 | ^fndupz do 31 | 32 | ; link up the new node ; 33 | ^symthead getw \6 ll-node dup ^newn putw ll-link 34 | ^newn getw ^symthead putw 35 | 36 | ; addr dlen strz ; 37 | ^newn getw ll-dptr putw 38 | ^newn getw ll-dptr \2 + putw 39 | ^newn getw ll-dptr \4 + putw 40 | 41 | end 42 | ]=InsertSymbol 43 | 44 | [ __0 ]=lui 45 | [ __0 ]=luitok 46 | [ \0 end ]=luno 47 | [ ^lui getw ll-dptr \4 + getw 48 | ^lui getw ll-dptr \2 + getw end ]=luyes 49 | [ ; strz -> dlen addr, that is, addr is pushed first; 50 | begin 51 | ^luitok putw 52 | ^symthead getw ^lui putw 53 | 54 | [ ]=luloop 55 | ^lui getw zero ^luno toif 56 | ^lui getw ll-dptr getw ^luitok getw eqzz ^luyes toif 57 | ^lui getw ll-next ^lui putw 58 | ^luloop to 59 | 60 | ]=LookupSymbol 61 | 62 | ;--------------------------------------; 63 | 64 | [ __0 ]=ddtoken ; contains pointer into token where to decipher ; 65 | [ __0 ]=ddvalue ; contains running tally of the value ; 66 | [ 67 | begin 68 | ^tokenba getw + ^ddtoken putw 69 | \0 ^ddvalue putw 70 | [ ]=ddLoop 71 | ^ddvalue getw \10 * 72 | ^ddtoken getw getb \48 - + 73 | ^ddvalue putw 74 | 75 | ^ddtoken @++ 76 | ^ddtoken getw getb \47 > ^ddLoop toif 77 | 78 | ^ddvalue getw 79 | end 80 | ]=DecipherDecimal 81 | 82 | ;--------------------------------------; 83 | 84 | [ 85 | begin 86 | ^codeh getw ++ putw 87 | \184 ^codeh getw putb 88 | \80 ^codeh getw \3 + putb 89 | ^codeh getw \4 + ^codeh putw 90 | end 91 | ]=WritePush 92 | 93 | [ 94 | begin 95 | \1 ^DecipherDecimal do ^WritePush do 96 | end 97 | ]=PushWord 98 | 99 | ;--------------------------------------; 100 | 101 | [ 102 | ^tokenba getw \2 + ^LookupSymbol do pop 103 | dup zero ^badtok toif 104 | ^safeba getw - \260 + 105 | ^codeh getw putw 106 | ^codeh getw \2 + ^codeh putw 107 | end 108 | ]=LiteralSymbol 109 | [ 110 | \2 ^DecipherDecimal do ^codeh getw putw 111 | ^codeh getw \2 + ^codeh putw 112 | end 113 | ]=LiteralWord 114 | [ 115 | begin 116 | 117 | ^tokenba getw ++ getb \95 - zero ^LiteralWord toif 118 | ^tokenba getw ++ getb \94 - zero ^LiteralSymbol toif 119 | \1 ^DecipherDecimal do 120 | ^codeh getw putb 121 | ^codeh @++ 122 | end 123 | ]=LiteralByte 124 | 125 | ;--------------------------------------; 126 | 127 | [ 128 | begin 129 | ^tokenba getw ++ ^LookupSymbol do pop 130 | dup zero ^badtok toif 131 | ^safeba getw - \260 + ^WritePush do 132 | end 133 | ]=PushPointer 134 | 135 | ;--------------------------------------; 136 | 137 | [ __0 ]=strct 138 | [ 139 | begin 140 | \1 ^strct putw 141 | [ ]=strLoop 142 | 143 | ^tokenba getw ^strct getw + getb 144 | 145 | ^codeh getw putb 146 | 147 | ^codeh @++ 148 | ^strct @++ 149 | 150 | ^tokenba getw ^strct getw + getb ^strLoop toif 151 | 152 | end 153 | ]=String 154 | 155 | ;--------------------------------------; 156 | 157 | [ 158 | begin 159 | ^codeh getw 160 | ^stach getw putw 161 | ^stach getw \2 + ^stach putw 162 | end 163 | ]=BeginBlock 164 | 165 | [ __0 ]=ebtokptr 166 | [ __0 ]=ebtoklen 167 | [ __0 ]=ebdatlen 168 | [ __0 ]=origcodeh 169 | [ 170 | begin 171 | ; adjust namestart ... possibly the weirdest Shelta-ism ; 172 | ^namestart getw ^origcodeh getw + ^stach getw \2 - getw - ^namestart putw 173 | end 174 | ]=AdjustName 175 | [ __0 ]=nei ; a shared counter the for next two subroutines ; 176 | [ 177 | ^macrh getw ^namestart putw 178 | 179 | ; copy everything from origcodeh to codeh into the macro area ; 180 | 181 | ^origcodeh getw ^nei putw 182 | 183 | [ ]=mloop 184 | 185 | ^nei getw getb ^macrh getw putb 186 | ^nei @++ 187 | ^macrh @++ 188 | ^nei getw ^codeh getw - ^mloop toif 189 | 190 | ; change codeh back to origcodeh ; 191 | 192 | ^origcodeh getw ^codeh putw 193 | 194 | end 195 | ]=MacroInstead 196 | [ 197 | begin 198 | ^tokenba getw ++ getb \58 - zero ^MacroInstead toif 199 | 200 | ; copy everything from origcodeh to codeh into a safe area ; 201 | 202 | ^origcodeh getw ^nei putw 203 | 204 | [ ]=neloop 205 | 206 | ^nei getw getb ^safeh getw putb 207 | ^nei @++ 208 | ^safeh @++ 209 | ^nei getw ^codeh getw - ^neloop toif 210 | 211 | ; change codeh back to origcodeh ; 212 | 213 | ^origcodeh getw ^codeh putw 214 | end 215 | ]=NotEmpty 216 | [ begin ^tokenba getw \2 + ^ebtokptr putw end ]=incebtokptr 217 | [ 218 | ; insert name into dictionary ; 219 | ^namestart getw ^ebdatlen getw ^ebtokptr getw ^InsertSymbol do 220 | end 221 | ]=NameIt 222 | [ 223 | begin 224 | 225 | ^tokenba getw ++ ^ebtokptr putw 226 | ^tokenba getw ++ getb \58 - zero ^incebtokptr doif 227 | ^tokenba getw ++ getb \61 - zero ^incebtokptr doif 228 | 229 | ^safeh getw dup ^safestart putw ^namestart putw ; track starts ; 230 | 231 | ^ebtokptr getw lenz ^ebtoklen putw 232 | 233 | ^stach getw \2 - ^stach putw 234 | ^stach getw getw ^origcodeh putw ; get original code head ; 235 | 236 | ^codeh getw ^origcodeh getw - ^ebdatlen putw 237 | 238 | ^stach getw ^stacba getw - ^AdjustName doif 239 | 240 | ^ebdatlen getw \0 > ^NotEmpty doif 241 | 242 | ; write push instruction if '=' or ':' not used ; 243 | 244 | ^tokenba getw ++ getb \58 - zero ^NameIt toif 245 | ^tokenba getw ++ getb \61 - zero ^NameIt toif 246 | 247 | \184 ^codeh getw putb 248 | \80 ^codeh getw \3 + putb 249 | ^safestart getw ^safeba getw \260 - - ^codeh getw ++ putw 250 | 251 | ^codeh getw \4 + ^codeh putw 252 | 253 | ^tokenba getw ++ getb ^NameIt toif 254 | end 255 | ]=EndBlock 256 | 257 | ;--------------------------------------; 258 | 259 | [ __0 ]=urctr 260 | [ __0 ]=urlen 261 | [ __0 ]=uraddr 262 | [ 263 | ^codeh getw -- -- ^codeh putw 264 | ; ^codeh \4 \2 fwrite ^crlf \2 \2 fwrite ; 265 | end 266 | ]=wipeit 267 | [ 268 | ^codeh getw \2 - getb \80 - zero ^wipeit toif end 269 | ]=peep 270 | [ 271 | ^codeh getw -- getb \88 - zero ^peep toif end 272 | ]=peepok 273 | [ 274 | begin 275 | ^codeh getw ^codeba getw - ^peepok toif end 276 | ]=clean 277 | [ 278 | [ ]=urloop 279 | ^uraddr getw getb ^codeh getw putb 280 | ^uraddr @++ 281 | ^codeh @++ 282 | ^urlen @-- 283 | ^urctr @++ 284 | 285 | ^urctr getw -- zero ^clean doif 286 | 287 | ^urlen getw ^urloop toif 288 | end 289 | ]=curloop 290 | [ 291 | begin 292 | \0 ^urctr putw ^tokenba getw ^LookupSymbol do ^urlen putw 293 | dup zero ^badtok toif 294 | ^uraddr putw 295 | 296 | ; copy urlen bytes from uraddr to codeh ; 297 | 298 | ; 1999.10.14 peephole optimization commented out. 299 | it crashes and it's not strictly necessary. someday, perhaps... ; 300 | 301 | ^urlen getw ^curloop toif 302 | 303 | end 304 | ]=Unroll 305 | 306 | ;--------------------------------------; 307 | 308 | [ end ]=goodc ; char was dupped and is on stack ; 309 | [ 310 | begin 311 | [ ]=floop 312 | qinc dup \59 - ^goodc toif pop ; return good char if not semicolon ; 313 | [ ]=cloop 314 | qinc \59 - zero ^floop toif 315 | ^cloop to 316 | end 317 | ]=scanc 318 | 319 | [ _0 ]=inbyte 320 | [ _0 ]=eoff 321 | [ \1 ^eoff putb end ]=goteof 322 | [ 323 | begin 324 | ^tokenba getw ^tokenh putw 325 | [ ]=scanloop 326 | ^scanc do ^inbyte putb 327 | ^inbyte getb \11 - zero ^goteof toif 328 | \33 ^inbyte getb > ^scanloop toif 329 | 330 | [ ]=scisloop 331 | ^inbyte getb ^tokenh getw putb ^tokenh @++ ;write char to token; 332 | 333 | ^scanc do ^inbyte putb 334 | ^inbyte getb zero ^goteof toif 335 | ^inbyte getb \32 > ^scisloop toif 336 | 337 | \0 ^tokenh getw putb 338 | end 339 | ]=scantok 340 | 341 | ; --- startup --- get dynamic memory off of heap --- ; 342 | 343 | \16384 malloc dup ^safeba putw \2 + ^safeh putw 344 | \4096 malloc dup ^macrba putw ^macrh putw 345 | \4096 malloc dup ^codeba putw ^codeh putw 346 | \256 malloc dup ^stacba putw ^stach putw 347 | \128 malloc dup ^tokenba putw ^tokenh putw 348 | 349 | [ 350 | ; write output file ; 351 | 352 | ; put in a jump over the safe area ; 353 | 354 | ^safeh getw ^safeba getw - ++ 355 | 356 | \233 outc 357 | dup \255 & outc \8 >> \255 & outc 358 | \144 outc 359 | 360 | ; make the first word of the safe area an offset ; 361 | ; to just past the last word of the code ; 362 | 363 | ^safeh getw ^safeba getw - ^codeh getw + ^codeba getw \260 - - ^safeba getw putw 364 | 365 | ^safeba getw ^safeh getw ^safeba getw - outs 366 | ^codeba getw ^codeh getw ^codeba getw - outs 367 | \0 halt 368 | ]=tail 369 | [ [ ]=main 370 | ^scantok do 371 | ^eoff getb ^tail toif 372 | ^tokenba getw getb \91 - \5 > ^Unroll doif 373 | ^tokenba getw getb \91 - zero ^BeginBlock doif 374 | ^tokenba getw getb \92 - zero ^PushWord doif 375 | ^tokenba getw getb \93 - zero ^EndBlock doif 376 | ^tokenba getw getb \94 - zero ^PushPointer doif 377 | ^tokenba getw getb \95 - zero ^LiteralByte doif 378 | ^tokenba getw getb \96 - zero ^String doif 379 | ^main to 380 | ]=Shelta ^Shelta to 381 | 382 | ; end of sheltas.she ; 383 | -------------------------------------------------------------------------------- /eg/str.she: -------------------------------------------------------------------------------- 1 | ; 2 | str.she v1999.12.23 (c)2000 Chris Pressey, Cat's Eye Technologies. 3 | Demonstrates searching a list of strings. 4 | ; 5 | 6 | [ __0 ]=head 7 | [ __0 ]=newn 8 | 9 | [ `Moe _0 ]=s1 10 | [ `Curly _0 ]=s2 11 | [ `Larry _0 ]=s3 12 | 13 | [ `Larry _0 ]=target ; change this variable to test ; 14 | 15 | [ ; strz -> strz ; 16 | begin dup lenz ++ malloc cpzz end 17 | ]=strdup 18 | 19 | [ ; stooge -> void ; 20 | begin 21 | ^strdup do 22 | ^head getw \2 ll-node dup ^newn putw ll-link 23 | ^newn getw dup ^head putw ll-dptr putw 24 | end 25 | ]=add-stooge 26 | 27 | [ [ `No ] \2 outs \1 halt ]=no 28 | [ [ `Yes ] \3 outs \0 halt ]=yes 29 | 30 | [ 31 | 32 | ^s1 ^add-stooge do 33 | ^s2 ^add-stooge do 34 | ^s3 ^add-stooge do 35 | 36 | ^head getw ^newn putw 37 | 38 | [ ]=cloop 39 | ^newn getw zero ^no toif 40 | ^newn getw ll-dptr getw ^target eqzz ^yes toif 41 | ^newn getw ll-next ^newn putw 42 | ^cloop to 43 | 44 | ] to 45 | -------------------------------------------------------------------------------- /lib/8086/8086.she: -------------------------------------------------------------------------------- 1 | ; 2 | 8086\8086.she v1999.10.10 (c)1999 Chris Pressey, Cat's-Eye Technologies. 3 | Defines the instructions of the Intel 8086 chip and it's successors. 4 | ; 5 | 6 | [ _244 ]:hlt 7 | 8 | [ _146 ]:xchg-dx-ax 9 | [ _147 ]:xchg-bx-ax 10 | [ _145 ]:xchg-cx-ax 11 | 12 | [ _80 ]:push-ax 13 | [ _83 ]:push-bx 14 | [ _81 ]:push-cx 15 | [ _82 ]:push-dx 16 | 17 | [ _255 _55 ]:push[bx] 18 | 19 | [ _57 _195 ]:cmp-bx-ax 20 | 21 | [ _161 ]:mov-ax[] 22 | [ _163 ]:mov[]ax 23 | 24 | [ _142 _6 ]:mov-es[] 25 | 26 | [ _88 ]:pop-ax 27 | [ _91 ]:pop-bx 28 | [ _89 ]:pop-cx 29 | [ _90 ]:pop-dx 30 | 31 | [ _95 ]:pop-di 32 | [ _94 ]:pop-si 33 | 34 | [ _86 ]:push-si 35 | [ _87 ]:push-di 36 | 37 | [ _138 _4 ]:mov-al[si] 38 | [ _58 _5 ]:cmp-al[di] 39 | 40 | [ _139 _5 ]:mov-ax[di] 41 | 42 | [ _211 _224 ]:shl-ax-cl 43 | [ _211 _232 ]:shr-ax-cl 44 | 45 | [ _209 _224 ]:shl-ax-1 46 | [ _209 _232 ]:shr-ax-1 47 | 48 | [ _180 ]:mov-ah 49 | [ _176 ]:mov-al 50 | [ _177 ]:mov-cl 51 | [ _185 ]:mov-cx 52 | [ _187 ]:mov-bx 53 | [ _179 ]:mov-bl 54 | 55 | [ _255 _208 ]:call-ax 56 | [ _255 _224 ]:jmp-ax 57 | 58 | [ _50 _192 ]:xor-al-al 59 | [ _50 _228 ]:xor-ah-ah 60 | [ _48 _255 ]:xor-bh-bh 61 | [ _38 ]:es 62 | 63 | [ _137 _195 ]:mov-bx-ax 64 | [ _137 _194 ]:mov-dx-ax 65 | [ _137 _193 ]:mov-cx-ax 66 | 67 | [ _136 _204 ]:mov-ah-cl 68 | [ _136 _206 ]:mov-dh-cl 69 | 70 | [ _139 _210 ]:mov-bx-dx 71 | [ _136 _7 ]:mov[bx]al 72 | [ _137 _7 ]:mov[bx]ax 73 | [ _137 _15 ]:mov[bx]cx 74 | [ _136 _15 ]:mov[bx]cl 75 | [ _139 _7 ]:mov-ax[bx] 76 | [ _138 _15 ]:mov-cl[bx] 77 | 78 | [ _136 _5 ]:mov[di]al 79 | 80 | [ _116 ]:je 81 | [ _117 ]:jne 82 | [ _114 ]:jb 83 | [ _119 ]:ja 84 | [ _235 ]:jmp 85 | [ _11 _192 ]:or-ax-ax 86 | [ _10 _192 ]:or-al-al 87 | [ _9 _210 ]:or-dx-dx 88 | [ _247 _208 ]:not-ax 89 | 90 | [ _131 _251 ]:cmp-bx 91 | 92 | [ _70 ]:inc-si 93 | [ _71 ]:inc-di 94 | 95 | [ _67 ]:inc-bx 96 | [ _74 ]:dec-dx 97 | 98 | [ _128 _228 ]:and-ah 99 | [ _35 _194 ]:and-ax-dx 100 | [ _11 _194 ]:or-ax-dx 101 | [ _51 _194 ]:xor-ax-dx 102 | 103 | [ _49 _201 ]:xor-cx-cx 104 | [ _51 _192 ]:xor-ax-ax 105 | [ _49 _210 ]:xor-dx-dx 106 | 107 | [ _1 _208 ]:add-ax-dx 108 | [ _41 _208 ]:sub-ax-dx 109 | [ _41 _194 ]:sub-dx-ax 110 | 111 | [ _247 _234 ]:imul-dx 112 | [ _247 _249 ]:idiv-cx 113 | 114 | [ _64 ]:inc-ax 115 | [ _72 ]:dec-ax 116 | 117 | [ _159 ]:lahf 118 | [ _235 ]:jmp 119 | [ _144 ]:nop 120 | 121 | [ _205 ]:int 122 | -------------------------------------------------------------------------------- /lib/8086/bios.she: -------------------------------------------------------------------------------- 1 | ; 2 | 8086\bios.she v1999.12.23 (c)1999 Chris Pressey, Cat's-Eye Technologies. 3 | BIOS interface for the OS-dependent part of GUPI. 4 | ; 5 | 6 | ;interrupt # for keybd ; [ _22 ]:keybd 7 | ;interrupt # for video ; [ _16 ]:video 8 | 9 | ; void -> halt; [ pop-ax jmp _254 ]:halt 10 | 11 | ; char -> void; [ pop-ax mov-ah _14 mov-bl _15 int video ]:outc 12 | 13 | ;string sizeb -> void; [ pop-dx pop-si mov-al[si] 14 | mov-ah _14 mov-bl _15 int video 15 | inc-si dec-dx or-dx-dx jne _242 ]:outs 16 | 17 | ; void -> char; [ xor-ah-ah int keybd xor-ah-ah push-ax ]:qinc 18 | ; void -> char; [ qinc dup outc ]:inc 19 | ; void -> bool; [ mov-ah _1 int keybd je _4 inc-ax jmp _3 nop xor-ax-ax push-ax ]:chkin 20 | ; void -> void; [ mov-ah _1 int keybd je _6 xor-ah-ah int keybd jmp _244 ]:flin 21 | -------------------------------------------------------------------------------- /lib/8086/dos.she: -------------------------------------------------------------------------------- 1 | ; 2 | 8086\dos.she v1999.12.23 (c)1999 Chris Pressey, Cat's-Eye Technologies. 3 | DOS interface for the OS-dependent part of GUPI. 4 | ; 5 | 6 | ;interrupt # for DOS ; [ _33 ]:dos 7 | 8 | ; void -> halt; [ pop-ax mov-ah _76 int dos ]:halt 9 | ;string sizeb -> void; [ mov-ah _64 _187 _1 _0 pop-cx pop-dx int dos ]:outs 10 | ; char -> void; [ mov-ah _2 pop-dx int dos ]:outc 11 | ; void -> char; [ mov-ah _1 int dos push-ax ]:inc 12 | ; void -> char; [ mov-ah _7 int dos xor-ah-ah push-ax ]:qinc 13 | ; void -> bool; [ mov-ah _11 int dos xor-ah-ah push-ax ]:chkin 14 | ; void -> void; [ xor-ax-ax mov-ah _12 int dos ]:flin 15 | 16 | -------------------------------------------------------------------------------- /lib/8086/file.she: -------------------------------------------------------------------------------- 1 | ; 2 | 8086\fileio.she v1999.12.23 3 | (c)1999 Chris Pressey, Cat's-Eye Technologies. 4 | DOS file functions. 5 | ; 6 | 7 | ; zfnm -> fhdl ; 8 | [ pop-dx mov-ah _61 xor-al-al int dos push-ax ]:fopenz 9 | 10 | ; string fhdl -> fstat ; 11 | [ mov-ah _63 pop-bx mov-cx __1 pop-dx int dos push-ax ]:freadc 12 | 13 | ; zfnm -> fhdl ; 14 | [ pop-dx mov-ah _60 xor-cx-cx int dos ]:fcreatez 15 | 16 | ;str szb fhdl -> void ; 17 | [ mov-ah _64 pop-bx pop-cx pop-dx int dos ]:fwrite 18 | 19 | ; fhdl -> void ; 20 | [ pop-bx mov-ah _62 int dos ]:fclose 21 | 22 | ; quit? [ begin ^tokenba getw dup lenz \2 fwrite halt end ]=fnhalt ; 23 | -------------------------------------------------------------------------------- /lib/8086/gupi.she: -------------------------------------------------------------------------------- 1 | ; 2 | 8086\gupi.she v1999.10.10 (c)1999 Chris Pressey, Cat's-Eye Technologies. 3 | 8086-compatible semantics for GUPI. 4 | ; 5 | 6 | ; input stack -> output stack ; 7 | ; bottom..top -> top..bottom ; 8 | 9 | ; word -> void ; [ pop-ax ]:pop 10 | ; word -> word word; [ pop-ax push-ax push-ax ]:dup 11 | ; wrd1 wrd2 -> wrd1 wrd2; [ pop-ax pop-bx push-ax push-bx ]:swap 12 | 13 | ; addr -> byte ; [ pop-ax xchg-bx-ax mov-ax[bx] xor-ah-ah push-ax ]:getb 14 | ; byte addr -> void ; [ pop-ax xchg-bx-ax pop-cx mov[bx]cl ]:putb 15 | ; addr -> word ; [ pop-ax xchg-bx-ax push[bx] ]:getw 16 | ; word addr -> void ; [ pop-ax xchg-bx-ax pop-cx mov[bx]cx ]:putw 17 | 18 | ; word -> word ; [ pop-ax inc-ax push-ax ]:++ 19 | ; word -> word ; [ pop-ax dec-ax push-ax ]:-- 20 | ; word -> word ; [ pop-ax shl-ax-1 push-ax ]:** 21 | ; word -> word ; [ pop-ax shr-ax-1 push-ax ]:// 22 | 23 | ; word word -> word ; [ pop-ax xchg-cx-ax pop-ax shl-ax-cl push-ax ]:<< 24 | ; word word -> word ; [ pop-ax xchg-cx-ax pop-ax shr-ax-cl push-ax ]:>> 25 | 26 | ; addr -> void ; [ pop-bx mov-ax[bx] inc-ax mov[bx]ax ]:@++ 27 | ; addr -> void ; [ pop-bx mov-ax[bx] dec-ax mov[bx]ax ]:@-- 28 | 29 | ; word word -> word ; [ pop-ax pop-dx add-ax-dx push-ax ]:+ 30 | ; word word -> word ; [ pop-ax pop-dx sub-dx-ax xchg-dx-ax push-ax ]:- 31 | ; word word -> word ; [ pop-ax pop-dx imul-dx push-ax ]:* 32 | ; word word -> word ; [ pop-ax xchg-cx-ax pop-ax xor-dx-dx idiv-cx push-ax ]:/ 33 | ; word word -> word ; [ pop-ax xchg-cx-ax pop-ax xor-dx-dx idiv-cx push-dx ]:% 34 | ; word word -> word word; [ pop-ax xchg-cx-ax pop-ax xor-dx-dx idiv-cx push-dx push-ax ]:/% 35 | ; word word -> word ; [ pop-ax pop-dx or-ax-dx push-ax ]:| 36 | ; word word -> word ; [ pop-ax pop-dx and-ax-dx push-ax ]:& 37 | ; word word -> word ; [ pop-ax pop-dx xor-ax-dx push-ax ]:~ 38 | ; word -> word ; [ pop-ax not-ax push-ax ]:! 39 | ; word -> word ; [ pop-ax or-ax-ax je _4 xor-ax-ax jmp _1 inc-ax push-ax ]:zero 40 | ; word word -> word ; [ pop-ax pop-bx cmp-bx-ax ja _4 xor-ax-ax jmp _1 inc-ax push-ax ]:> 41 | 42 | ; addr -> (call) ; [ pop-ax call-ax ]:do 43 | ; addr -> (branch) ; [ pop-ax jmp-ax ]:to 44 | ; bool addr -> (call) ; [ pop-ax pop-dx or-dx-dx je _2 jmp-ax ]:toif 45 | ; bool addr -> (branch) ; [ pop-ax pop-dx or-dx-dx je _2 call-ax ]:doif 46 | 47 | ; memory for call stack: ; [ __0 __0 __0 __0 __0 __0 __0 __0 48 | __0 __0 __0 __0 __0 __0 __0 __0 49 | __0 __0 __0 __0 __0 __0 __0 __0 50 | __0 __0 __0 __0 __0 __0 __0 __0 ]=clstk 51 | ; memory for stack pointer; [ __0 ]=clsp 52 | 53 | ; (call) -> void ; [ pop-ax _139 _30 _^clsp _137 _135 _^clstk _131 _6 _^clsp _2 ]:begin 54 | ; void -> (return) ; [ _131 _46 _^clsp _2 _139 _30 _^clsp _139 _135 _^clstk push-ax _195 ]:end 55 | 56 | ; sizw -> ptrw ; 57 | [ mov-bx __260 mov-ax[bx] 58 | pop-dx 59 | push-ax 60 | add-ax-dx 61 | mov-bx __260 mov[bx]ax ]:malloc 62 | 63 | ; ptrw -> void ; 64 | [ pop-ax mov-bx __260 mov[bx]ax ]:mfree 65 | 66 | -------------------------------------------------------------------------------- /lib/8086/string.she: -------------------------------------------------------------------------------- 1 | ; 2 | 8086\string.she v1999.10.10 (c)1999 Chris Pressey, Cat's-Eye Technologies. 3 | GUPI string-manipulation extensions. 4 | ; 5 | 6 | ; strz strz -> bool ; 7 | [ 8 | pop-di pop-si 9 | mov-al[si] cmp-al[di] 10 | je _5 11 | xor-ax-ax 12 | jmp _13 13 | nop 14 | or-al-al 15 | je _4 16 | inc-si inc-di 17 | jmp _237 18 | xor-ah-ah mov-al _1 19 | push-ax 20 | ]:eqzz 21 | 22 | ; strz strz -> strz(2) ; 23 | [ 24 | pop-di pop-si push-di 25 | mov-al[si] mov[di]al 26 | or-al-al 27 | je _4 28 | inc-si inc-di 29 | jmp _244 30 | ]:cpzz 31 | 32 | ; strz -> word ; 33 | [ 34 | pop-si 35 | push-si 36 | mov-al[si] 37 | or-al-al 38 | je _3 39 | inc-si 40 | jmp _247 41 | pop-dx 42 | push-si 43 | pop-ax 44 | sub-ax-dx 45 | push-ax 46 | ]:lenz 47 | 48 | [ ; strz -> strz ; 49 | dup lenz ++ malloc cpzz 50 | ]:dupz 51 | 52 | 53 | -------------------------------------------------------------------------------- /lib/gupi/linklist.she: -------------------------------------------------------------------------------- 1 | ; 2 | gupi\linklist.she v1999.10.10 (c)1999 Chris Pressey, Cat's-Eye Technologies. 3 | GUPI linked list extensions. 4 | ; 5 | 6 | ; size -> node ; [ \2 + malloc ]:ll-node 7 | ; next node -> void ; [ putw ]:ll-link 8 | ; node -> next ; [ getw ]:ll-next 9 | ; node -> data-ptr ; [ \2 + ]:ll-dptr 10 | -------------------------------------------------------------------------------- /src/shelta86.asm: -------------------------------------------------------------------------------- 1 | IDEAL 2 | 3 | ; shelta86.asm v1999.10.20 (c)1999 Chris Pressey, Cat's-Eye Technologies. 4 | ; Implements an assembler/compiler for the Shelta language, in 8086 assembly. 5 | 6 | ; * Special thanks to Ben Olmstead (BEM) for his suggestions for how to 7 | ; reduce SHELTA86.COM's size even further. 8 | 9 | MODEL tiny 10 | P8086 11 | 12 | DATASEG 13 | 14 | symth dw symt 15 | codeh dw code 16 | stach dw stac 17 | safeh dw safe + 2 18 | macrh dw macr 19 | 20 | ttable dw BeginBlock, PushWord, EndBlock, PushPointer, LiteralByte ; , String 21 | ; [ \ ] ^ _ ` 22 | 23 | UDATASEG 24 | 25 | token db 128 dup (?) 26 | 27 | safestart dw ? 28 | namestart dw ? 29 | toklength dw ? 30 | 31 | safe db 16384 dup (?) 32 | symt db 16384 dup (?) ; 16K + 16K = 32K 33 | code db 4096 dup (?) ; 34 | macr db 4096 dup (?) ; + 8K = 40K 35 | stac db 256 dup (?) 36 | 37 | CODESEG 38 | ORG 0100h 39 | 40 | ; EQUATES 41 | 42 | safeadj EQU (offset safe - 0104h) 43 | codeadj EQU (offset code - 0104h) 44 | 45 | ; Main program. 46 | PROC Main 47 | 48 | WhileFile: 49 | 50 | ; ----- begin scanning token 51 | 52 | call ScanChar ; get char -> al 53 | or al, al 54 | jz @@EndFile 55 | cmp al, 32 56 | jbe WhileFile ; repeat if char is whitespace 57 | 58 | mov di, offset token 59 | cld 60 | 61 | @@TokenLoop: stosb ; put char in token 62 | call ScanChar ; get char 63 | cmp al, 32 64 | ja @@TokenLoop ; repeat if char is not whitespace 65 | 66 | @@Terminate: mov [byte di], 0 ; return null-terminated token 67 | 68 | ; ----- end scanning token 69 | 70 | mov si, offset token + 1 71 | 72 | mov al, [byte token] 73 | sub al, '[' 74 | cmp al, 4 75 | ja @@Unroll 76 | 77 | xor ah, ah 78 | shl ax, 1 79 | xchg bx, ax 80 | mov ax, [offset ttable + bx] 81 | jmp ax ; jump to handler as listed in ttable 82 | 83 | @@Unroll: dec si ; start at first character of token 84 | call LookupSymbol ; destroys DI & SI, but that's OK 85 | 86 | ; copy cx bytes from ax to codeh 87 | 88 | xchg ax, si 89 | mov di, [codeh] ; use di to track codeh 90 | rep movsb 91 | 92 | UpCodeH: mov [codeh], di 93 | jmp short WhileFile 94 | 95 | @@EndFile: ; put in a jump over the safe area 96 | 97 | mov ax, [safeh] 98 | sub ax, offset safe - 1 99 | mov bx, offset token ; re-use token 100 | mov [byte bx], 0e9h 101 | mov [word bx + 1], ax 102 | mov [byte bx + 3], 90h 103 | 104 | mov cx, 4 105 | mov dx, offset token 106 | call WriteIt 107 | 108 | ; make the first word of the safe area an offset 109 | ; to just past the last word of the code 110 | 111 | mov cx, [safeh] 112 | mov dx, offset safe 113 | sub cx, dx 114 | mov ax, cx 115 | add ax, [codeh] 116 | sub ax, codeadj 117 | mov [word safe], ax 118 | 119 | call WriteIt 120 | 121 | mov cx, [codeh] 122 | mov dx, offset code 123 | sub cx, dx 124 | call WriteIt 125 | 126 | xor al, al 127 | 128 | GlobalExit: mov ah, 4ch ; exit to DOS 129 | int 21h 130 | ENDP Main 131 | 132 | PROC WriteIt 133 | 134 | mov ah, 40h 135 | mov bx, 1 136 | int 21h 137 | jnc @@OK 138 | mov al, 32 139 | jmp short GlobalExit 140 | @@OK: ret 141 | ENDP WriteIt 142 | 143 | ; -------------------------------- HANDLERS --------------------------- ; 144 | ; When coming into any handler, di will equal the address of the null 145 | ; (that is, the number of characters in the token + offset token) 146 | 147 | ; ==== [ ==== BEGIN BLOCK ==== ; 148 | 149 | BeginBlock: mov di, [stach] ; push [ onto stack 150 | mov ax, [codeh] 151 | stosw ; mov [bx], ax 152 | mov [stach], di 153 | jmp WhileFile 154 | 155 | ; ==== ] ==== END BLOCK ==== ; 156 | 157 | EndBlock: ;mov si, offset token + 1 ; si = token + 1 until... 158 | ;cmp [byte ds:si], '=' 159 | ;je @@Smaller 160 | ;cmp [byte ds:si], ':' 161 | ;je @@Smaller 162 | ;jmp short @@CarryOn 163 | ; remove : or = from length 164 | @@Smaller: dec di ; di left over from scanning token 165 | 166 | @@CarryOn: mov bx, di ; di now free to hold something until @@WName 167 | sub bx, si ; get length 168 | 169 | mov ax, [safeh] 170 | mov [safestart], ax 171 | mov [namestart], ax 172 | xchg ax, di ; di now holds safe area head location 173 | 174 | mov [toklength], bx ; length of token 175 | sub [stach], 2 176 | mov bx, [stach] ; pop [ from stack 177 | 178 | mov ax, [bx] ; ax = codeh when [ happened 179 | 180 | mov bp, [codeh] ; find length 181 | sub bp, ax 182 | ; mov bp, bx ; bp = length of data between [ ... ] 183 | ; until @@WName below... ugh 184 | 185 | cmp [stach], offset stac 186 | je @@StackEmpty 187 | 188 | 189 | mov bx, [stach] 190 | sub bx, 2 191 | mov cx, [bx] 192 | 193 | ; namestart = [namestart] - (cx - ax) 194 | 195 | sub cx, ax 196 | sub [namestart], cx 197 | 198 | ; if dlength > 0, 199 | 200 | @@StackEmpty: ;or bp, bp 201 | ;jz @@Empty 202 | 203 | cmp [byte si], ':' ; si still = offset token + 1 204 | jne @@PreCopyLoop 205 | 206 | mov di, [macrh] ; use macro area instead of safe if : 207 | mov [namestart], di 208 | 209 | ; copy everything from ax to codeh into the di area 210 | 211 | @@PreCopyLoop: mov dx, ax 212 | mov cx, bp ; [codeh] sub cx, ax 213 | push si 214 | xchg si, ax 215 | rep movsb 216 | pop si 217 | 218 | ; change codeh back to dx (old codeh before [) 219 | 220 | mov [codeh], dx 221 | 222 | ;mov si, offset token + 1 223 | cmp [byte si], ':' ; si still = offset token + 1 224 | je @@UpdateMacr 225 | 226 | mov [safeh], di 227 | jmp short @@Empty 228 | @@UpdateMacr: mov [macrh], di 229 | ;jmp short @@NameIt 230 | 231 | ; write push instruction if '=' or ':' not used 232 | 233 | @@Empty: ;cmp [byte si], '=' ; si still = offset token + 1 234 | ;je @@NameIt 235 | 236 | ;mov ax, [safestart] 237 | ;sub ax, safeadj 238 | ;mov bx, [word codeh] 239 | ;mov [byte bx], 0b8h 240 | ;mov [word bx + 1], ax 241 | ;mov [byte bx + 3], 50h 242 | ;add [codeh], 4 243 | 244 | ;cmp [byte si], 0 ; still offset token + 1! 245 | ;je @@Anonymous 246 | 247 | ; insert namestart into dictionary 248 | 249 | @@NameIt: mov cx, [namestart] 250 | mov ax, [toklength] 251 | 252 | ;cmp [byte si], '=' 253 | ;je @@Bigger 254 | ;cmp [byte si], ':' 255 | ;je @@Bigger 256 | ;jmp short @@WName 257 | 258 | @@Bigger: inc si 259 | 260 | @@WName: ; Destroys DI but that's OK. 261 | ; INPUT: bx = ADDRESS of token to insert, ax = length of symbol, 262 | ; cx = pointer to data, dx = length of data 263 | ; OUTPUT: ds:bx = pointer to newly allocated symbol 264 | 265 | mov di, [symth] ; di no longer contains macrh/safeh 266 | add ax, 6 ; 1 word for length, 1 for ptr, 1 for data length 267 | add [symth], ax 268 | 269 | stosw ; mov [word di], ax ; place ax length in symt 270 | 271 | sub ax, 6 272 | xchg cx, ax ; cx <- ax; ax <- cx 273 | stosw ; mov [word di], cx ; place cx (ptr to data) 274 | xchg ax, bp 275 | stosw ; mov [word di], bp ; place bp (ptr length) 276 | 277 | rep movsb 278 | 279 | mov [symth], di 280 | 281 | @@Anonymous: jmp WhileFile 282 | 283 | ; ==== ^ ==== PUSH POINTER ==== ; 284 | 285 | PushPointer: ;mov si, offset token + 1 286 | call LookupSymbol ; destroys di & si, should be OK 287 | 288 | sub ax, safeadj 289 | mov di, [word codeh] 290 | jmp short WritePush 291 | 292 | ; ==== ` ==== STRING ==== ; 293 | ; 294 | ;String: ;mov si, offset token + 1 295 | ; mov di, [codeh] 296 | ;@@Loop: mov al, [byte ds:si] 297 | ; stosb 298 | ; inc si 299 | ; cmp [byte ds:si], 0 300 | ; jne @@Loop 301 | ; jmp UpCodeH 302 | 303 | ; ==== _ ==== LITERAL BYTE ==== ; 304 | 305 | LiteralByte: ;mov si, offset token + 1 306 | cmp [byte si], '_' 307 | je LiteralWord 308 | cmp [byte si], '^' 309 | je LiteralSymbol 310 | call DecipherDecimal ; destroys DI, that's OK 311 | stosb ; mov [byte bx], al 312 | CheapTrick: mov [codeh], di 313 | jmp WhileFile 314 | 315 | ; ==== __ ==== LITERAL WORD ==== ; 316 | 317 | LiteralWord: inc si 318 | call DecipherDecimal ; destroys DI, that's OK 319 | FunkyTrick: stosw ; mov [word bx], ax 320 | jmp short CheapTrick 321 | 322 | ; ==== _^ ==== LITERAL SYMBOL ==== ; 323 | 324 | LiteralSymbol: inc si 325 | call LookupSymbol ; destroys DI & SI, that's OK 326 | 327 | sub ax, safeadj 328 | 329 | mov di, [word codeh] 330 | jmp short FunkyTrick 331 | ;mov [word bx], ax 332 | ;inc [codeh] 333 | ;jmp short CheapTrick 334 | 335 | ; ==== \ ==== PUSH WORD ==== ; 336 | 337 | PushWord: ;mov si, offset token + 1 338 | call DecipherDecimal ; destroys di, that's OK 339 | 340 | WritePush: mov [byte di], 0b8h ; B8h, low byte, high byte, 50h 341 | inc di 342 | stosw ; mov [word di + 1], ax 343 | mov al, 50h 344 | stosb 345 | mov [codeh], di 346 | jmp WhileFile 347 | 348 | ; -------------------------------- SUBROUTINES --------------------------- ; 349 | 350 | PROC DecipherDecimal ; uses and destroys DI 351 | ; INPUT: si = address of token 352 | ; OUTPUT: ax = value, di = codeh 353 | 354 | 355 | xor di, di 356 | 357 | @@Loop: lodsb ; mov al, [byte ds:si], inc si 358 | 359 | mov bx, di 360 | mov cl, 3 361 | shl bx, cl 362 | mov cx, di 363 | shl cx, 1 364 | add bx, cx 365 | 366 | sub al, '0' 367 | cbw 368 | add bx, ax 369 | mov di, bx 370 | 371 | cmp [byte ds:si], '0' 372 | jae @@Loop 373 | 374 | xchg ax, di 375 | mov di, [word codeh] 376 | ret 377 | ENDP DecipherDecimal 378 | 379 | PROC ScanChar 380 | ; Scans a single character from the input file, placing 381 | ; it in register al, which will be 0 upon error 382 | ; or eof (so don't embed nulls in the Shelta source...) 383 | 384 | mov ah, 7 ; read from stdin one byte 385 | int 21h 386 | cmp al, ';' ; check for comment 387 | je @@Comment 388 | ret 389 | @@Comment: mov ah, 7 ; read from stdin one byte 390 | int 21h 391 | cmp al, ';' ; check for comment 392 | jne @@Comment 393 | jmp short ScanChar 394 | 395 | ENDP ScanChar 396 | 397 | PROC LookupSymbol 398 | ; INPUT: si = address of symbol to find, di = address of null termination 399 | ; OUTPUT: ds:ax = pointer to contents or zero if not found 400 | ; cx = length of contents 401 | 402 | mov bx, offset symt ; bx starts at symbol table 403 | mov bp, si 404 | sub di, si 405 | 406 | @@Loop: mov ax, [word bx] ; first word = token size 407 | 408 | mov dx, bx ; keep track of start of this symt entry 409 | 410 | sub ax, 6 411 | cmp ax, di 412 | jne @@Exit ; if it doesn't fit, you must acquit 413 | 414 | add bx, 6 ; bx now points to token in symbol table 415 | 416 | ; exit if right token 417 | 418 | xor si, si ; reset si to token 419 | @@Inner: mov al, [byte ds:bx] ; get byte from bx=symt 420 | cmp [byte bp + si], al ; compare to si=token 421 | jne @@Exit 422 | inc bx 423 | inc si 424 | cmp si, di ; hit the length yet? 425 | jb @@Inner ; no, repeat 426 | 427 | ; a match! 428 | 429 | mov bx, dx 430 | mov cx, [word bx + 4] ; third word = data length 431 | mov ax, [word bx + 2] ; second word = data ptr 432 | ret 433 | 434 | @@Exit: mov bx, dx 435 | mov ax, [word bx] 436 | add bx, ax 437 | cmp bx, [symth] 438 | jb @@Loop 439 | 440 | mov al, 16 ; return 16 if unknown identifier 441 | jmp GlobalExit 442 | 443 | ENDP LookupSymbol 444 | 445 | END Main 446 | -------------------------------------------------------------------------------- /src/shelta86.s: -------------------------------------------------------------------------------- 1 | ; shelta86.s 2 | ; v1.2-2013.1130 3 | ; (c)2009-2013 Chris Pressey, Cat's Eye Technologies. All rights reserved. 4 | 5 | ; Implements an assembler/compiler for the Shelta language, in 8086 machine 6 | ; language, in the format of the NASM assembler. 7 | 8 | ; * Special thanks to Ben Olmstead (BEM) for his suggestions for how to 9 | ; reduce SHELTA86.COM's size even further. 10 | 11 | org 0100h 12 | bits 16 13 | cpu 8086 14 | 15 | ;-------------- Code 16 | 17 | ; Main program. 18 | 19 | WhileFile: 20 | 21 | ; ----- begin scanning token 22 | 23 | call word ScanChar ; get char -> al 24 | cmp al, 0bh ; is it a vertical tab? 25 | je EndFile 26 | cmp al, 32 27 | jbe WhileFile ; repeat if char is whitespace 28 | 29 | mov di, token 30 | cld 31 | 32 | .TokenLoop: stosb ; put char in token 33 | call word ScanChar ; get char 34 | cmp al, 32 35 | ja .TokenLoop ; repeat if char is not whitespace 36 | 37 | mov byte [di], 0 ; null-terminate the token 38 | 39 | ; ----- end scanning token 40 | 41 | mov si, token + 1 42 | 43 | mov al, [token] 44 | sub al, '[' 45 | cmp al, 5 46 | ja .Unroll 47 | xor ah, ah 48 | shl ax, 1 49 | xchg bx, ax 50 | mov ax, [ttable + bx] 51 | call ax ; call handler as listed in ttable 52 | jmp short WhileFile 53 | 54 | .Unroll: dec si ; start at first character of token 55 | call word LookupSymbol ; destroys DI & SI, but that's OK 56 | 57 | ; copy cx bytes from ax to codeh 58 | 59 | xchg ax, si 60 | mov di, [codeh] ; use di to track codeh 61 | rep movsb 62 | 63 | UpCodeH: mov [codeh], di 64 | jmp short WhileFile 65 | 66 | EndFile: ; put in a jump over the safe area 67 | 68 | mov di, token ; re-use token 69 | mov al, 0e9h 70 | stosb 71 | mov ax, [safeh] 72 | sub ax, safe - 1 73 | stosw 74 | mov al, 090h 75 | stosb 76 | 77 | mov cx, 4 78 | mov dx, token 79 | call word WriteIt 80 | 81 | ; make the first word of the safe area an offset 82 | ; to just past the last word of the code 83 | 84 | mov cx, [safeh] 85 | mov dx, safe 86 | sub cx, dx 87 | mov ax, cx 88 | add ax, [codeh] 89 | sub ax, codeadj 90 | mov [safe], ax 91 | 92 | call word WriteIt 93 | 94 | mov cx, [codeh] 95 | mov dx, code 96 | sub cx, dx 97 | call word WriteIt 98 | 99 | xor al, al 100 | 101 | GlobalExit: mov ah, 4ch ; exit to DOS 102 | int 21h 103 | 104 | WriteIt: 105 | mov ah, 40h ; write data to file 106 | mov bx, 1 ; filehandle #1 = stdout 107 | int 21h 108 | jnc .OK 109 | mov al, 32 110 | jmp short GlobalExit 111 | .OK: ret 112 | 113 | ; -------------------------------- HANDLERS --------------------------- ; 114 | ; When coming into any handler, di will equal the address of the null 115 | ; (that is, the number of characters in the token + offset token) 116 | 117 | ; ==== [ ==== BEGIN BLOCK ==== ; 118 | 119 | BeginBlock: mov di, [stach] ; push [ onto stack 120 | mov ax, [codeh] 121 | stosw ; mov [bx], ax 122 | mov [stach], di 123 | ret 124 | 125 | ; ==== ] ==== END BLOCK ==== ; 126 | 127 | EndBlock: dec di ; di left over from scanning token 128 | 129 | mov bx, di ; di now free to hold something until .WName 130 | sub bx, si ; get length of token 131 | mov [toklength], bx ; store it for later 132 | 133 | mov ax, [safeh] 134 | mov [safestart], ax 135 | mov dx, ax ; dx = namestart initially = safestart = safeh 136 | xchg ax, di ; di now holds safe area head location 137 | 138 | sub word [stach], byte 2 139 | mov bx, [stach] ; pop [ from stack 140 | mov ax, [bx] ; ax = codeh when [ happened 141 | 142 | mov bp, [codeh] ; find length 143 | sub bp, ax ; bp = length of code between [ ... ] (codeh - old codeh) 144 | 145 | cmp word [stach], stac 146 | je .StackEmpty 147 | 148 | mov cx, [bx - 2] ; cx = contents popped from stack 149 | 150 | ; namestart:dx = namestart:dx - (contents:cx - tokenlength:ax) 151 | 152 | sub cx, ax 153 | sub dx, cx 154 | 155 | .StackEmpty: cmp byte [si], ':' ; si still = offset token + 1 156 | jne .PreCopy 157 | 158 | mov di, [macrh] ; copy into macro area instead of safe area if : 159 | mov dx, di 160 | 161 | ; copy everything from ax to codeh into the di area 162 | 163 | .PreCopy: push ax 164 | mov cx, bp 165 | push si 166 | xchg si, ax 167 | rep movsb 168 | pop si 169 | pop ax 170 | 171 | ; restore codeh back to old codeh before [ 172 | 173 | mov [codeh], ax 174 | cmp byte [si], ':' ; si still = offset token + 1 175 | jne .UpdateSafe 176 | 177 | mov [macrh], di 178 | jmp short .NameIt 179 | 180 | .UpdateSafe: mov [safeh], di 181 | 182 | ; write push instruction if '=' or ':' not used 183 | 184 | cmp byte [si], '=' ; si still = offset token + 1 185 | je .NameIt 186 | 187 | mov ax, [safestart] 188 | sub ax, safeadj 189 | 190 | mov di, [codeh] ; di no longer contains macrh/safeh 191 | jmp short WritePush 192 | 193 | ; insert namestart into dictionary 194 | 195 | .NameIt: mov cx, dx 196 | mov ax, [toklength] 197 | 198 | inc si 199 | 200 | .WName: ; Insert token into the symbol table. 201 | ; DESTROYS: DI 202 | ; INPUT: si = pointer to token text 203 | ; ax = length of token text 204 | ; cx = pointer to data associated with token 205 | ; bp = length of data associated with token 206 | 207 | mov di, [symth] ; di no longer contains macrh/safeh 208 | add ax, 6 ; 1 word for length, 1 for ptr, 1 for data length 209 | 210 | stosw ; place ax length in symt 211 | 212 | sub ax, 6 213 | xchg cx, ax ; cx <- ax; ax <- cx 214 | stosw ; place cx (ptr to data) 215 | xchg ax, bp 216 | stosw ; place bp (ptr length) 217 | 218 | rep movsb 219 | 220 | mov [symth], di 221 | 222 | ret 223 | 224 | ; ==== ^ ==== PUSH POINTER ==== ; 225 | 226 | PushPointer: call LookupSymbol ; destroys di & si, should be OK 227 | 228 | sub ax, safeadj 229 | mov di, [codeh] 230 | jmp short WritePush 231 | 232 | ; ==== ` ==== STRING ==== ; 233 | 234 | String: mov di, [codeh] 235 | .Loop: mov al, [si] 236 | stosb 237 | inc si 238 | cmp byte [si], 0 239 | jne .Loop 240 | mov [codeh], di 241 | ret 242 | 243 | ; ==== _ ==== LITERAL BYTE ==== ; 244 | 245 | LiteralByte: cmp byte [si], '_' 246 | je LiteralWord 247 | cmp byte [si], '^' 248 | je LiteralSymbol 249 | call DecipherDecimal ; sets DI to [codeh] 250 | jmp short GnarlyTrick 251 | 252 | ; ==== __ ==== LITERAL WORD ==== ; 253 | 254 | LiteralWord: inc si 255 | call DecipherDecimal ; sets DI to [codeh] 256 | FunkyTrick: stosw 257 | jmp short CheapTrick 258 | 259 | ; ==== _^ ==== LITERAL SYMBOL ==== ; 260 | 261 | LiteralSymbol: inc si 262 | call LookupSymbol ; destroys DI & SI, that's OK 263 | 264 | sub ax, safeadj 265 | 266 | mov di, [codeh] 267 | jmp short FunkyTrick 268 | 269 | ; ==== \ ==== PUSH WORD ==== ; 270 | 271 | PushWord: call DecipherDecimal ; sets DI to [codeh] 272 | 273 | WritePush: mov byte [di], 0b8h ; B8h, low byte, high byte, 50h 274 | inc di 275 | stosw 276 | mov al, 50h 277 | GnarlyTrick: stosb 278 | CheapTrick: mov [codeh], di 279 | ret 280 | 281 | ; -------------------------------- SUBROUTINES --------------------------- ; 282 | 283 | DecipherDecimal: 284 | ; INPUT: si = address of token 285 | ; OUTPUT: ax = value, di = codeh 286 | ; uses and destroys DI 287 | 288 | xor di, di 289 | 290 | .Loop: lodsb 291 | 292 | mov bx, di 293 | mov cl, 3 294 | shl bx, cl 295 | mov cx, di 296 | shl cx, 1 297 | add bx, cx 298 | 299 | sub al, '0' 300 | cbw 301 | add bx, ax 302 | mov di, bx 303 | 304 | cmp byte [si], '0' 305 | jae .Loop 306 | 307 | xchg ax, di 308 | mov di, [codeh] 309 | ret 310 | 311 | ; Scans a single character from the input file, placing 312 | ; it in register al, which will be 0 upon error 313 | ; or eof (so don't embed nulls in the Shelta source...) 314 | 315 | ScanChar: 316 | mov ah, 7 ; read from stdin one byte 317 | int 21h 318 | cmp al, ';' ; check for comment 319 | je .Comment 320 | ret 321 | .Comment: mov ah, 7 ; read from stdin one byte 322 | int 21h 323 | cmp al, ';' ; check for comment 324 | jne .Comment 325 | jmp short ScanChar 326 | 327 | LookupSymbol: 328 | ; INPUT: si = address of symbol to find, di = address of null termination 329 | ; OUTPUT: ds:ax = pointer to contents or zero if not found 330 | ; cx = length of contents 331 | 332 | mov bx, symt ; bx starts at symbol table 333 | mov bp, si 334 | sub di, si 335 | 336 | .Loop: mov ax, [bx] ; first word = token size 337 | 338 | mov dx, bx ; keep track of start of this symt entry 339 | 340 | sub ax, 6 341 | cmp ax, di 342 | jne .Exit ; if it doesn't fit, you must acquit 343 | 344 | ; exit if right token 345 | 346 | xor si, si ; reset si to token 347 | .Inner: mov al, [bx + 6] ; get byte from bx+6=pointer to token text 348 | cmp [bp + si], al ; compare to si=token 349 | jne .Exit 350 | inc bx 351 | inc si 352 | cmp si, di ; hit the length yet? 353 | jb .Inner ; no, repeat 354 | 355 | ; a match! 356 | 357 | mov bx, dx 358 | mov cx, [bx + 4] ; third word = data length 359 | mov ax, [bx + 2] ; second word = data ptr 360 | ret 361 | 362 | .Exit: mov bx, dx 363 | mov ax, [bx] 364 | add bx, ax 365 | cmp bx, [symth] 366 | jb .Loop 367 | 368 | mov al, 16 ; return 16 if unknown identifier 369 | jmp GlobalExit 370 | 371 | ;-------------- Initialized Data 372 | 373 | symth: dw symt 374 | codeh: dw code 375 | stach: dw stac 376 | safeh: dw safe + 2 377 | macrh: dw macr 378 | 379 | ttable: dw BeginBlock, PushWord, EndBlock, PushPointer, LiteralByte, String 380 | ; [ \ ] ^ _ ` 381 | 382 | ;-------------- Uninitialized Data 383 | 384 | section .bss 385 | 386 | token: resb 128 387 | 388 | safestart: resw 1 389 | toklength: resw 1 390 | 391 | safe: resb 16384 392 | symt: resb 16384 ; 16K + 16K = 32K 393 | code: resb 4096 394 | macr: resb 4096 ; + 8K = 40K 395 | stac: resb 256 396 | 397 | ;-------------- Equates 398 | 399 | safeadj equ (safe - 0104h) 400 | codeadj equ (code - 0104h) 401 | --------------------------------------------------------------------------------