CAMELFORTH/8051 APPLICATION NOTE #2 8051 PORT I/O B. Rodriguez 9 November 1996 Except for the words KEY and EMIT which use the 8051 serial port, CamelForth/8051 provides no words to use the 8051's on-chip I/O. This note describes how to add new Forth words to the CamelForth kernel, to perform 8051 I/O. Many Forths provide words -- commonly named PC@ (Port C@) and PC! (Port C!) -- to do IN and OUT instructions to I/O ports. This requires that the CPU has IN and OUT instructions which accept an indirect address (that is, a port address in a register). The Z80 and 8086 permit this. Unfortunately, the 8051 does not; to read or write an 8051 Special Function Register (SFR), the address of the register has to be coded into the instruction (direct addressing). Thus it is very difficult to write a general PC@ and PC! for the 8051. However, it is very easy to write a fetch and store to a _specific_ port on the 8051. KEY and EMIT are examples of this. You must remember that the top of the Forth stack is kept in DPH:DPL. When you want to put something new on the stack, first push DPH:DPL to memory, then put the new value (byte or word) in DPH:DPL. When you want to output (and drop) something from the stack, first output it from DPH:DPL, then pop the "new" top-of-stack into DPH:DPL. The kernel provides the subroutines "pushtos" and "poptos", respectively, to do the push or pop. Below are examples for Port 1 and the Timer Control register. We could write such words for every Special Function Register in the 8051, but this would increase the size of the kernel unnecessarily (especially with the newer 8051 derivatives, which have dozens of SFRs). Also, it may be desirable to "customize" the I/O words -- for example, to read and write a single bit of a port, which is much more easily done in 8051 assembler code. So it is best to determine which I/O ports you need to access, and then add just those words to your CAMEL51.ASM file. ; EXAMPLE I/O =================================== ; !P1 c -- output byte to port 1 .drw link .set link,*+1 .db 0,3,"!P1" STOREP1: mov p1,dpl ; output TOS char to P1 ljmp poptos ; pop new TOS ; @P1 -- c get byte from port 1 .drw link .set link,*+1 .db 0,3,"@P1" FETCHP1: lcall pushtos ; push old TOS mov dpl,p1 ; get P1 byte in TOS mov dph,#0 ret ; !TCON c -- output byte to TCON reg .drw link .set link,*+1 .db 0,5,"!TCON" STORETCON: mov tcon,dpl ; output TOS char to TCON ljmp poptos ; pop new TOS ; @TCON -- c get byte from TCON reg .drw link .set link,*+1 .db 0,5,"@TCON" FETCHTCON: lcall pushtos ; push old TOS mov dpl,tcon ; get TCON byte in TOS mov dph,#0 ret