24 August 2004 A 32-bit compiler at last I arranged all my source code in one file. I thought that this would simplify things, but I found that the file was a pain to edit when I had to edit several parts of the file at once. I may split the file up again before I'm finished with Karig. I also got rid of the T register. Now I have variables _row and _col and a routine emit, which prints a character to the screen at the current row and column (unless either or both are out of bounds) and increments _col. Latest code I finally have a working (if incomplete) compiler. I lifted it from Chuck Moore's (public domain) ColorForth source code. My new code sets up the system, compiles some precode, and runs the resulting machine code. The machine code simply prints "Hello" on the screen and then stops. The top-level lines to compile and run the precode are as follows: _lit 18 call load call dict jmp $ This stores the number 18 onto the data stack, calls load to start the compiler, calls the resulting machine code (at address dict, i.e., the one-megabyte mark in memory where the dictionary begins), and halts the computer. Block 18 Note that load works here as it does in ColorForth — that is, the code isn't really loaded per se; it's already in memory, at block 18, i.e., at address 18*1024 = 0x12*0x400 = 0x4800. NASM of course does not allow more than one ORG directive per file, so I had to use the TIMES directive to ensure that the precode wound up at the correct address: times ((18*1024) - ($-$$+boot)) db 0 Precode To create the precode, I discovered that my DP macro does not work — I can't use dp "cls" + COMPILE, for example, to store the code needed to compile a call to cls. DP will produce only "colorless" words, with the color bits all set to zero. So I ended up using DP to calculate the cells for the words "cls", "emit", "refresh", and ";" and copied the values into the source code. To each word value, I added 4 to set the color bits to "compile word." I also had to hand-create the precode cells needed to compile code to push a number onto the stack. Each character is a number that fits into less than 27 bits, so each character would be stored as a ColorForth 27-bit number. To do this, I multiplied the original number by 32 (or shift its bits left by five, take your pick) and added 6 to set the color bits to "compile short (27-bit) number." Finally, note that the precode needs to end with a zero, so that the compiler knows when to stop compiling. autoexec: dd 0x95200004 ; Compile "cls" dd 0x00000906 ; Compile short number 'H' (0x48) dd 0x48B90004 ; Compile "emit" dd 0x00000CA6 ; Compile short number 'e' (0x65) dd 0x48B90004 ; Compile "emit" dd 0x00000D86 ; Compile short number 'l' (0x6C) dd 0x48B90004 ; Compile "emit" dd 0x00000D86 ; Compile short number 'l' (0x6C) dd 0x48B90004 ; Compile "emit" dd 0x00000DE6 ; Compile short number 'o' (0x6F) dd 0x48B90004 ; Compile "emit" dd 0x14B0A404 ; Compile "refresh" dd 0xF0000004 ; Compile ";" dd 0 ; end of precode Wordlists I copied the definitions of a number of words in ColorForth, but I haven't tested any of these (except for ";"). I expect they will work, but I haven't used them yet. The words I incorporated into karig.asm are:
I added words specific to Karig: "cls" to clear the screen buffer, "emit" to put a character on the screen, and "refresh" to copy the screen buffer to the screen. Wordlist sizes For a while, I had a bug that I had trouble tracking down: The compiler wasn't compiling any CALL statements to any of my routines. I finally figured out what the problem was: In ColorForth, there are two variables, macros and forths, that tell the compiler how many words are in the two wordlists. In Chuck Moore's source, these variables are set to zero and altered at runtime while the system is booting. If they are zero, the compiler won't work, so I had to change these in the source. macros: dd (macro1-macro0)/4 forths: dd (forth1-forth0)/4 Emitline and dump Finally, because I was having problems getting this to work, I ended up writing two routines, emitline and dump, to show me the contents of memory. Emitline prints a null-terminated string in memory to the current line (you pass it the address of the first character in the string), and dump generates a string from the values of 26 bytes in memory, starting at the address you pass, and passes the string's address to emitline. Neither of these are in the wordlists, but they are useful enough that I left them in the source code. What's next? I suppose now I ought to test out the rest of the routines in the wordlist. Of course I really also need to work on the keyboard loop. It would be nice to have the startup process end in something other than "jmp $". The startup process should set up the screen (the status bar along the top, the block viewer in the middle, the shell along the bottom) and end up in a keyloop — one that echoes characters into the shell, one that calls a routine when a nonprinting key is pressed. Check the index for other entries. |