Search (using Google):  Web Karig

 

23 January 2004

A tiny compiler, part 4

I had to make a few changes to my keyboard-loop code to get it ready to pass words on to the actual compiler code.

Code changes

The updated code works as follows (changes are in boldface):

The code sets up the segment registers and stack, clears the text screen, and enters the loop.

Changes to the key loop

The loop starts by setting the attribute (color) of the next two character slots on the screen, so that the next characters typed are consistent with the value in attrib.

Next, I get a key. If the key is not F1, F2, F3, or F4, I branch ahead. I check this by subtracting the scan code for F1 from the scan code I received. If the unsigned result is greater than four, I branch ahead. Otherwise, I save the result in mode so that I can use it later as an offset into the modes table when it comes time to process the word. Then I use the result as an offset into the attribs table so I can store the current attribute byte in attrib. Finally, I return to the top of the loop.

		sub	ah, 0x3B
		cmp	ah, 4
		jnb	.2
		mov	[mode], ah ; <-- CHANGE
		xor	bh, bh
		mov	bl, ah
		mov	ah, [attribs+ax]
		mov	[attrib], ah
		jmp	short .4

If the key was not a function key, I test the ASCII code. If it is 33 or greater, then the character is printable and can be part of a word; otherwise, I go back for another key.

If the keypress produced a visible character, I save the character into the newword buffer. I preserve the character already there by moving it into the other slot in the buffer. Note that the consequence of storing new characters in this way is that the word is stored backwards — if you type "ab", newword will contain "ba". This is a consequence of the fact that the second character's ASCII code arrives in AL and of my realization that writing code to correct this was unnecessary.

		cmp	al, 33
		jb	.1
		mov	ah, [newword] ; <-- CHANGE
		mov	[newword], ax ; <-- CHANGE

The new character is printed to the screen, and the cursor is moved forward by one character. If the character just typed was the first of the two characters in the current word, then I go back to get the next key.

If the character was the second of the two, I call the routine to process the word — I grab mode, use it as an offset into the modes table, and call the address I find there. The routine sets the carry flag on an error, in which case I print a question mark after the word on the screen; otherwise I print a space.

		; SIX LINES ADDED:
		mov	bx, [mode]
		shl	bx, 1
		mov	bx, [modes+bx]
		call	bx
		mov	ax, (0xE * 0x100) + '?'
		jc	.3

		mov	ax, (0xE * 0x100) + ' '
	.3:	xor	bh, bh ; <-- LOCAL LABEL ADDED
		int	0x10

I then print another space (to bring the number of characters devoted to this word to four) and return to the top of the loop.

Other changes

The code requires the addition of two variables, a table of four two-byte addresses, and four compiler-mode routines. The compiler-mode routines are currently dummy routines that just set or clear the carry flag to test how well the calling code works: The code must print a question mark after the current word if the carry is set; otherwise it must print a space.


; ------ Compiler-mode routines.
define:
		stc
		ret
compile:
		clc
		ret
execute:
		stc
		ret
hex:
		clc
		ret

; ------ Variables.
c:
		db	0
attrib:
		dw	0x04   ; facilitates "mov bx, [attrib]"

attribs:
		db	0x04   ; red on black (define)
		db	0x02   ; green on black (compile)
		db	0x0E   ; yellow on black (execute)
		db	0x05   ; magenta on black (hex)

mode:
		dw	0      ; facilitates "mov bx, [mode]"

modes:
		dw	define, compile, execute, hex

newword:
		db	"c,"

Results

The code and data take up only 160 out of 510 bytes, so apparently I have plenty of room to add every feature I want. :-)

The code should work in the same way as the original keyboard-loop code, except that you'll see a question mark after every red word and every yellow word, but not after any green or magenta words. It works as advertised on my laptop.

Some advice

If you're writing in assembly language and are trying to read an address from a table into a register so you can CALL the address via the register, make sure you don't type call [bx] when you really need call bx. I just spent two hours trying to figure out why my computer was crashing when I ran my code before I figured out that using call [bx] was the wrong thing to do here. Sigh. :-(

Check the index for other entries.