Значок ресурса

FORTH Compiler/Editor

Нет прав на скачивание
Автор: David Millington
Год: 1984
Издатели: Your Computer
Языки: 🇬🇧 Английский
Формат: 📼 TZX лента
Требования: 🖥️ ZX Spectrum 48K

Ссылки:
Страница на ZXArt
Страница на World Of Spectrum
Страница на Spectrum Computing

Скриншоты:
FORTHCompilerEditor-RUN-1.png


FORTH





David Millington presents a program


to develop Forth on your Spectrum.





This version of the computer language Forth will run on the


48K Spectrum, but because of many of the POKE addresses


used, it is not compatible with Microdrives and the Inter-


face 1 operating system.


It consists of two distinct parts: a compiler and editor


written in Basic and a set of Forth words in machine code.


The main advantage of Forth as a language is its very fast


execution speed and this implementation will run typically


50 times faster than Basic.


The speed increase is due to the fact that a Forth


program is converted directly into machine code, and the


modular nature of Forth makes the process of compilation


very easy. Since the compiler in my version is in Basic, it


compiles quite slowly, but the code produced will run as


fast as commercial Forths.


The theory of simple programming in Forth has been


covered in previous issues of Your Computer, but I will


recap the simple concepts for those who are new to the


language. My implementation is mostly standard Forth and


includes all the usual structures, but it differs in its


use of variables and strings and is less flexible in the


methods of defining words. However, it should serve as an


introduction to the advantages of the language and can be


used for applications such as graphic games, as I hope to


show in a future article.


[When you load the program, you] should be presented with


the title, and after a short pause a question mark prompt


and a flashing blank cursor will appear at the bottom,


awaiting your commands.





The main feature of Forth is the stack, which is simply a


pile of numbers. A number can be added to the top of the


stack and later the top number can be removed. These two


simple operations are the basis of Forth. You should now


type in 23 and press ENTER, and this number will be placed


on top of the stack. If you now type a full stop and ENTER,


the top number on the stack will be removed and printed.


The full stop is an example of a Forth word, many of which


do something to the stack, as shown. The word + will fetch


two numbers from the stack, add them together, and place


the sum back on top. You should now be able to use Forth to


add together two numbers and print the result. One way is


to enter


23 45 + .


and the answer 68 will appear. This also illustrates how


several items can be entered together, separated by spaces.


Similarly the words -, /, * are available for arithmetic,


and complex expressions can be evaluated. Consider the


Basic statement


PRINT (5+11)/(5-3)


The equivalent in Forth is


5 11 + 5 3 - / .


Both will yield the answer 8. If the Forth version seems


strange, study figure 3, which details the effect upon the


stack as each command is executed. Forth simply requires


each operation to be placed after the operands instead of


in between, whether they are numbers or other expressions.


This is known as postfix notation, and it automatically


removes the need for brackets.


___________________________________________________________





Figure 3.





Expression 5 11 + 5 3 - / .


- - - - - - - - - - - - - - - - - - - - - - - - - - - - -


Stack 3


11 5 5 2


5 5 16 16 16 16 8


___________________________________________________________





You should experiment with various expressions until you


are sure what is happening and how the stack is being used.


Sooner or later you will discover that Forth operates only


on integers. Try


20 6 / .


and the answer 3 is produced. The range of numbers which


can be handled is -32768 to 32767, although any between


32768 and 65535 can be entered and they will be converted


to negatives. If you try to enter anything outside this


range, then the error message 'Number out of range' will


appear. A full list of the system's error messages is given


in figure 4 for reference. [Figure 4, and the other refe-


rence tables, can be found at the end of this text file.]





So far we have done only simple calculations in direct mode


and you are probably waiting to try a full program. The


main point to realise is that a Forth program bears little


resemblance to either Basic or machine code. There are no


line numbers and there is no sequential order of instruc-


tions that constitute an actual program. Instead we store


instructions by defining words. You have met five words so


far and those which you define yourself can be used in


exactly the same way.


Suppose we wish to define a word called average which


will calculate the average of three numbers. Enter


:average + + 3 / .;


There will be a delay while the word is compiled, and


then the prompt and cursor will reappear. The syntax for


defining a word is a colon followed by the name we wish to


use followed by a sequence of operations and terminated


with a semicolon. There is no space after the colon or


before the semicolon.


If you now enter 10 14 3 average . then 9, the average


of these three numbers, will be printed. The word average


is now as much a part of the computer's Forth vocabulary as


the built in or 'core' words, and all are stored in an area


of memory called the dictionary. You can define further


words using both core words and your own, and the idea is


to evolve a program consisting of nested word definitions


until typically only a single word is needed to execute


your program.


This makes program development easier than in Basic since


tasks can be subdivided and appropriate words written and


tested separately. The importance of the stack becomes


apparent since it is used to pass parameters to and from


words, as was demonstrated with the word average.





Figure 5 lists and briefly explains most of the Forth words


supported. Those already familiar with the language should


be able to try some larger programs, but before embarking


on anything too adventurous you will need to know how to


edit your work in case of errors. Forth is just as prone to


program bugs and typing errors as Basic but, like any com-


piled language, errors take a long time to correct.


There are several commands for editing and using periphe-


rals and these are listed in figure 6. It should be noted


that these are not part of the Forth language and are by no


means standard in any other Forth systems, which instead


use screens for editing. To indicate a system command you


should begin the line with an asterisk. Enter *edit average


and you should find the definition brought to the bottom of


the screen just as you first entered it. It can be altered


using standard cursor controls and entered when finished.


Alter it to


:average + + + 4 / .;


to allow the average of four numbers to be found. On


pressing ENTER you will hear a strange squeak, which is an


effect of the Sinclair editor being used at high speed.


After several seconds the cursor will reappear with the


amendment made to average.


In general, the more words you have defined, the longer


the delay will be, since a lot of recompiling must be done.


Every definition you enter is stored in a source code


buffer in case editing is required later. The command *list


will list every definition in the buffer. However, if you


type *del then the source buffer is cleared and you will be


unable to edit average, although it can still be used in


Forth. This explains why you cannot edit the core words.


There are Save and Load commands for both the source


buffer and the Forth dictionary, again described in figure


6. After a *sload command there will be a long delay while


the source code is compiled. The command *reset will delete


everything and start Forth from the beginning again. If you


have a ZX Printer then it can be turned on with *pr on,


allowing all output to be printed. *pr off will cancel this


facility.


As stated before, figure 5 contains brief explanations


of the more common Forth words. Emit is used for printing


single characters, and on the Spectrum it is especially


useful since it can handle the usual colour and position


control characters. For instance


16 emit 4 emit 42 emit


will print a green star.'


There are two words which deal with keyboard input, but


only at the single character level. Get will wait for a key


or shifted key to be pressed, and returns its code on the


stack. It will produce the standard key click, and when it


is called rapidly, it will allow keys to repeat. The other


word is key, and this works like INKEY$ in Basic. It will


return the code of the key which is being pressed, or 0 if


none is pressed. There is no implied wait as there is with


get.


There are four words for manipulating the stack and these


are illustrated diagrammatically in figure 5b. The most


useful is dup, as it will duplicate the top number, allow-


ing one copy to be used and the other preserved for later


use.





In any computer language, the ability to perform repeated


operations using loops, and to make decisions are very


important. You may think that the absence of line numbers


and GO TO will make this difficult but, in fact, Forth


provides several useful control words and these allow


clearer program structures to be set up than in Basic.


There are direct equivalents of Basic's FOR-NEXT state-


ments and these are do and loop. The actual layout of this


and the other structures are shown in figure 5. The word


ind will place the index counter of the loop on the stack


where it can be used. In standard Forth this word is simply


I, but I have altered it to avoid confusion with the vari-


able I.


The word +loop will allow steps of other than 1. The


following direct line illustrate how different step sizes


and directions are catered for, and should be entered


individually:


20 1 do ind . loop


1 20 do ind . loop


100 1 do ind . 17 +loop


-30 30 do ind . 2 +loop


Figure 7 contains the definition of a word called count,


which uses a loop to show the speed of Forth. Two points


are shown from this listing. A Forth word definition can


span several lines provided the : and ; mark the beginning


and end, and also comments can be included provided they


are on a separate line and surrounded by brackets.


Enter the definition and execute it by typing count, and


you should see the numbers from 1 to 10000 being rapidly


printed in the top left of the screen. You should also try


editing count to see how each line is presented separately


at the bottom.


The begin-until loop will allow a block of instructions


to be repeated until the condition at the end of the loop


is true, and the begin-while-repeat structure will cause


looping as long as the condition at the beginning is true.


Both these loops will be useful in different circumstances.


The conditions are the results of the operators <, >, <>


and =, which all require the two numbers to be compared to


be on the stack, and they will return a true value - 1 -


or a false value - 0. Until and while both expect such a


condition code to be on the stack. They use this value to


decided whether to exit or to continue looping. The follo-


wing line uses a loop to wait for the a key to be pressed:


begin key a = until


The if-then-else structure will allow two different


sections of code to be executed depending on whether a


condition is true or false, before resuming with the normal


flow of execution. The actual layout of these structures is


again explained in figure 5.


It must be emphasised that while all of the control


structures can be mixed and nested to any depth, they must


not cross or be jumped out of other than by a normal exit.


Also, all of the loop must be in the same word definition.


The word ind will return the index of the inner-most loop,


and will only give the correct value if it occurs in the


same word definition as the start and finish of the loop.


If you do cross your structures, then a crash is likely.


The ease of crashing Forth is a penalty of its high speed


and closeness to machine code.


You can place the following instructions in a loop if you


think that it might not exit:


key 32 = if abort then


Pressing the space key will stop the program with no ill


effects, and the message


Program ABORTed


will appear. Abort is the one word which will safely stop


execution and jump out of all the nested loops and words.





The rest of figure 7 shows some example of word definitions


to illustrate different aspects of simple programming. Type


will allow you to enter a line of text onto the screen,


terminated by enter. It shows a begin-until loop in action,


and also illustrates the use of Get. Fill is a simple


utility to fill the screen with the character of your


choice. For example,


35 fill


will fill the screen with hash signs. This may be slightly


slower than you expected, but this is due to the slowness


of Sinclair's print routine.


The next word, square, will calculate and print the


square of the number on the stack. It is called by the


final word, squares, which will print a formatted table of


square numbers up to any specified value.


When you have entered all these definitions, you may like


to save them on tape, to try out the cassette commands. It


is important that you should experiment with Forth and the


facilities of the compiler. [In fact, they are already on


Forth.tzx, under the name "Figure 7", following the main


program.]





[The system also has some features which were not described


in the article. Some of these are similar to standard Forth


features, but most of them are not quite the same as FIG-


or Forth-97. They're quite useful, though, so I'll describe


them in short. Keep in mind that I've only gleaned all this


from the program itself; it is probably accurate, but I may


have made a mistake.


First up are a number of memory management words. First


of these is allot. This takes a number from the stack, and


allots that number of bytes from a dedicated area between


the compiled code and the stack. The base address of this


area is then pushed onto the stack. You can safely use this


memory to store values in, as described later. Subsequent


calls to allot will reserve further memory directly follow-


ing the previous one.


The word clear will clear both the stack and the allot


area. After clear, the next value pushed will be pushed


onto the base of the stack just as if the system had just


been reset, and the next allot will allot from the start


of the allot area. In effect, it's like a *reset, but


without losing all your code.


If you want to use your allotted memory, or indeed any


memory not part of the stack or your program, you can use


^, ?, ! and @. The ^ word pops an address from the stack,


followed by a value, and stores the lower byte of that


value at the address specified. For values below 256, that


is the value itself. This means that


255 23692 ^


is the same thing as


POKE 23692,255


that is, it sets the scroll count 255.


Conversely, ? pops an address, and pushes the contents


of that memory location onto the stack. Thus, 23693 ? . is


the Forth equivalent of PRINT PEEK 23693, which is the


current permanent colours.


The words ! and @ are very similar to ^ and ? respec-


tively, except that they work on two-byte numbers. They


store and read the low byte of the value at the specified


address and the high byte at the next address, just as


described in chapter 25 of the Spectrum manual. For


example, 23606 @ will put the address of the character set


on the stack, and 30000 23606 ! will change it (hopefully


to the address where you have loaded your new, beautifully


redesigned typeface).


Note that you are not limited to addresses within the


Forth system for any of these four words, and that messing


about with addresses which you aren't sure are safe is just


as dangerous as using careless POKEs in Basic. The Forth


system itself starts at 43000, and anything above that and


below the UDG area is unsafe to ^ or ! in, except for


memory you have requested using allot.


The words ! and @ should not be confused with the next


feature, which is that of variables. Like Basic, Forth does


use variables, but because of the strong focus on the stack


their use is more limited. In the present system, they are


even more limited than in most. You have exactly 26 of


them; they are identified by a single lower-case letter, a


to z. Each can contain a single Forth value, just like a


stack entry. To store a value into one of these variables,


push the value onto the stack and then enter the variable


name letter, followed immediately, without a space, by a !.


To retrieve a variable value, use the name letter followed


by a @. For example,


42 d!


will store the value 42 into variable d, and


t@ .


will print out the value currently stored in variable t.


Note the difference between this feature and the double-


byte peek/poke commands: in variable use, the ! and @ are


directly attached to the variable name, while the peek/poke


commands are individual words, separated from the address


they work on by a space just like any other normal word.





Rather useful as well are the two string functions. If you


include any string between double quote marks within your


command, this will put that string, with an ENTER (CHR$ 13)


appended, into the compiled code, and push its start add-


ress on the stack. To complement this, there is the $ word,


which takes an address from the stack and prints the string


starting at that address and continuing until the first


CHR$ 13 found. Do note that this CHR$ 13 is not itself


printed, so if you want a message to end with a newline,


you will have to use 13 emit. Note also that this string


address is a normal 16-bit number, and you can perform


arithmetical operations on it just as you could in Basic.


For example, try:


"Oh Hello, World!" 3 + $


(The same thing is true, by the way, for the addresses used


by ^ ? ! @ and allot - but take care, again, not to make a


mistake and point where you didn't intend to.)


Finally, there is the "insert direct code" facility. This


puts bytes, specified by hexadecimal numbers, directly into


the code stream. Needless to say, this is both a very


powerful and very dangerous feature. It is accessed by


using the # symbol followed immediately (again without


intervening space) by the hexadecimal codes for the machine


code or data to be inserted. Each byte must be exactly two


hex digits (so if you want to insert 12, you must use 0c,


never just c.)


Take care: the code you insert is executed literally, as


machine code, as part of your command. You can, of course,


do untold damage with this, but you can also use it to


great effect, if you take care. For example, you can use


Forth's Pop and Push routines by inserting #cdd4a8 and


#cdc8a8 respectively. These put the value from the top of


the stack into HL, and push HL back onto the stack. In


between, you can do with HL whatever you want; for example,


you can shift it for a quick 2 * or 2 /, or even hand it


over to the ROM calculator to extend your Forth program


with SIN and COS.


Less trickily, you can call simple ROM routines like the


ones for BEEP or CLS. Or even more simply, just the one


word #c7 inserts the command RST 0, which resets the


Spectrum from within Forth! Less drastically, #cf means


RST 8, which is the error report facility of the Spectrum


ROM; Forth captures the error handling for its own use, so


this is not immensely useful, but one very handy use of


it is to include #cf16. This triggers error H, STOP in


INPUT, which causes the Forth system to stop cleanly


without resetting the computer.


And by the way, yes, that does mean that the last undocu-


mented feature of Forth is not a new word, but the way to


stop the system from the editor: simply break into the


command prompt using the down arrow key, or shift-6. It's


a normal Basic INPUT, disguised with a crafty POKE.]


___________________________________________________________





Figure 4. Forth compiler error messages.





Undefined word A word is either undefined in the


dictionary or in the case of *edit,


the source code if the definition


is unavailable.


Bad line The line entered generally does not


make sense. This error may also be


produced if you exceed the memory


reserved for the compiler.


Bad variable An illegal variable name has been


used.


Division by zero 1 0 / has been attempted, for


example.


Number out of range A number outside the range -32768


to 65535 has been entered.


Invalid number A number contains a non-numeric


character.


BREAK BREAK was pressed when using tape,


printer or 'scroll?'


Invalid name Illegal file name in cassette


commands.


Invalid colour code Same as in Basic.


Tape loading error Same as in Basic.


Program ABORTed The word abort has been executed.





After any error message, the line containing the error


must be entered again in full. If any error occurs after


editing a word, the word will automatically be presented


again for editing, starting from the beginning, and you


must skip through it by pressing ENTER, until you reach


the offending line.


___________________________________________________________





Figure 5. Summary of main Forth words.





Arithmetic operators:


+ Add two numbers and place result on stack.


- Subtract top number from second number.


* Multiply top two numbers.


/ Divide top number into second number. Result


is rounded to lowest integer.


Conditional operators:


=,<>,<,> Compare top two numbers according to specified


condition, and return 1 if the condition


holds, otherwise 0.


Stack manipulation: (see Figure 5b)


drop Remove top number from stack.


dup Duplicate top number on stack.


swap Swap top two numbers on stack.


over Copy second number on stack to the top, over


the original top number.


Control structures:


In the following descriptions, the items in brackets


refer to any appropriate block of Forth words. All


structures can be nested within themselves and others to


any level, but all the words at any one level of nesting


must be in the same word definition.


(finish) (start) do (code) loop


Perform an indexed loop, with index starting


at (start) and counting up or down by one


until (finish) is reached. (code) is repeated


appropriate number of times.


(finish) (start) do (code) (increment) +loop


Same as previous, except size of increment is


specified.


begin (code) (condition) until


(code) is repeated until (condition) gives a


true (non-zero) value.


begin (condition) while (code) repeat


(code) is repeated as long as (condition)


gives a true value. If (condition) is false


the first time, then (code) is skipped


altogether.


(condition) if (true code) else (false code) then


If (condition) is true then (true code) is


executed, otherwise (false code) is executed.


Both parts continue executing after then .


(condition) if (true code) then


Same as previous, except the false condition


is not treated separately, and else and the


(false code) are omitted.


ind Place index counter of innermost do loop on


stack.


abort Return to command mode, clearing all nested


loops and words.


___________________________________________________________





[ Figure 5a. Non-documented Forth words.





Memory management:


allot Pops a number from the stack, and allots that


many bytes of memory from the "free area"


between the stack and the code. Pushes the


base address of the allotted block onto the


stack.


clear Resets the stack pointer and the allotted


block pointer back to their starting values.


^ Pops two numbers, and loads the memory address


of the top number with the lowest 8 bits of


the second value. In other words, the same


thing as a Basic POKE.


? Pops a number, then pushes the one byte found


at that address onto the stack. That is, PEEK.


! Does the same thing as !, except that it pokes


a full 16-bit, number into two subsequent


memory positions, in the format described in


the Spectrum manual, chapter 25.


Note that this is a separate word, and not to


be confused with the x! variable storage,


described below.


@ Does the same thing as ?, except that it peeks


and pushes a 16-bit, 2-byte number.


Like !, not to be confused with variable usage.


Variable storage:


In both these commands, x can be any single, lower-case


letter from a to z. They are not to be confused with the


two-byte peek and poke words, described above. Those are


separate words; these are always part of a command con-


sisting of one variable letter and either ! or @.


x! Pops a value and stores it in variable x.


x@ Pushes the value of variable x on the stack.


String handling:


"string" Insert "string", followed by a newline, into


memory, and push its address on the stack.


$ Pop an address from the stack, and print the


string starting at that address, up to (not


including) the first newline (CHR$ 13) found.


Direct code insertion:


# Must be followed directly (no space) by any


number of two-digit hexadecimal numbers. These


numbers will be inserted directly into the


command code. ]


___________________________________________________________





Figure 5b. Effects of stack operations.





Initial state of stack dup drop swap over


30 20


30 30 20 30


20 20 20 30 20


10 10 10 10 10


___________________________________________________________





Figure 6. Compiler operating commands.





All commands must be prefixed with an asterisk.


*edit 'word' Allows word to be edited one line at


a time, if the source code is


available, then recompiles 'word' and


everything after it.


*list Lists all word definitions in source


code buffer.


*del Clears source code buffer, but leaves


Forth dictionary intact.


*reset Clears everything and restarts Forth.


*dsave 'filename' Save entire Forth dictionary in three


parts.


*dload 'filename' Loads dictionary, and clears source


code buffer.


*ssave 'filename' Saves source code buffer on tape.


*sload 'filename' Loads source code buffer and compiles


it into dictionary. There will be a


long delay while this is done.


Note that the filename is NOT placed in quotation marks,


and if it is omitted in a load command, then the first


file found will be loaded.


*pr on Sends all further output to ZX


printer.


*pr off Uses screen again for output.


*dlist Lists contents of dictionary, and the


address of the machine code routine


for each word.


*msave Saves Forth program as an independent


machine code routine.


___________________________________________________________





Figure 7





*list





:count 10000 1 do


(set up the loop)


22 emit 0 emit 0 emit


(move print pos. to top left)


ind .


(print the loop index)


loop;





:type begin


(set up loop)


get dup


(get a character from the keyboard and make another copy


on the stack)


emit


(print the character)


13 = until


(continue with the loop until the character is ENTER -


code 13)


;





:fill (the code of a character is already on the stack)


22 emit 0 emit 0 emit


704 1 do


(set up loop)


dup emit


(duplicate the character on the stack and print the top


copy)


loop


drop


(the character is removed from the stack)


;





:square dup * .


(print the square of the number on the stack)


;





:squares 1 do


(the upper limit of the loop is already on the stack)


ind .


(print the number)


6 emit


(this is a COMMA control character)


ind square


(calculate the square using the previous word we defined)


13 emit


(print on a new line)


loop;





20 squares


1 1


2 4


3 9


4 16


5 25


6 36


7 49


8 64


9 81


10 100


11 121


12 144


13 169


14 196


15 225


16 256


17 289


18 324


19 361


20 400


___________________________________________________________





[ And finally a few notes about the TZX. This contains


(of course) the program itself, which is followed by


the program used to create the default dictionary.


Following that is a small additional dictionary of


tools which I had to create to port a program ("OXO",


published in ZX Computing in June 1985, and also avail-


able at WoS) to this version of Forth, and which I


thought would make a nice sample dictionary. You can


load either the source or the compiled dictionary,


whichever you prefer. Do be careful not to load it over


the top of your own program, as this Forth does not do


Merge; load the toolset first, then enter your program.


Most of the words in this dictionary are simple


enough to understand. abs and mod do what you would


expect them to: abs turns the top stack value into its


absolute (i.e., nonnegative) value, like Basic ABS, and


mod calculates the remainder of the division of the top


two stack values - the complement of Forth's integer /.


Equally unsurprising are and, or and not. They behave


just like Basic AND, OR and NOT, including and and or


returning one of their arguments if possible. 4 2 and,


for example, gives 4, just as 4 AND 2 does in Basic.


You may wonder why I included not, when 0 = is just as


short. The reason is that 0 = compiles to code that


does <insert 0, call => while not compiles to <call


not> (which then in turn inserts 0 and calls =). Thus,


not results in rather shorter, but marginally slower,


code than 0 =. The choice is yours.


There are two screen functions, which also do just


what you would expect them to. <lin> <col> at puts the


cursor AT <lin>,<col>, while cls clears the screen


(calling two ROM routines to do so), then puts the


cursor back at the top left corner of the screen.


The last two words are not immediately obvious, as


they had to be written in machine code. The first of


these is rot, which is actually a standard Forth word,


It acts something like a three-value swap. It takes the


top three values off the stack, then puts them back on


with the third value on top, followed by the other two.


1 2 3 4 rot results in the stack holding, from the top


down, 2 4 3 1: the 2 has been rotated to the top.


Finally we have leave, also standard Forth, which


sets the final limit of the inner-most do-loop to its


current index. This means that when the loop is next


executed, the loop will terminate. Note that it is the


limit which is altered, not the current index. Strange,


but for some reason that's the standard.





One last word of warning: this Forth does not do a lot


of error-checking. If you fill the source buffer with


more text than will fit, it will merrily discard the


rest but keep updating its pointers, resulting in code


which will run but cannot be altered any more (and a


*list which will terminate in an error). Using *del


does solve this in so far that any new code will behave


as normal, but of course you still can't edit the old


source.


More seriously, you can compile more code than will


fit between the compiler and the allot area, or push


more than 250-is values and start running over the end


of RAM, or even pop more values than are on the stack


and (eventually) start popping your compiled code! None


of this will be prevented. You have to work at it, but


it's all possible.


The control structures also do no error checking at


all, and these are rather easier to break. All you have


to do is accidentally leave out one word. For example,


load the system from fresh (don't do this with your


freshly typed program in memory), then type loop as


your first line. Just the one word, without its do.


Now you'll be careful in the future. Leaving off the


structure ending word can be just as disastrous; try if


on its own.


And yes, it would certainly be possible to add checks


for all these, but they would cost a lot of compilation


time and above all memory, leaving even less room for


your source code, so that would unfortunately defeat


the purpose.


Richard Bos, October 2012. ]
Автор
Verter_bot
Загрузки
0
Просмотры
9
Расширение
zip
Размер
6.9 КБ
Хэш
f5387abd98f0cb33f9def683888466b5
Первый выпуск
Последнее обновление

Оценки

0.00 звезд(ы) 0 оценок
Назад
Вверх