A port is an opening in the processor that
allows for communication with other pieces of hardware. Devices such
as the LCD, paged memory and the key board are some of the hardware that
need to use ports on the TI-86; eight ports on the 86 are active.
I'm going to go through six of these eight ports and explain exactly how
you need to communicate with each piece of outside hardware.
The two instructions used to control ports are out
and in. Out sends a byte out of the processor to a piece of
hardware identified by its port number; in recieves a byte into the processor
from hardware. The syntax for out is: out (port #),operand.
The operand can be a register, an indirect address or a value, <out
(7),a> will send the accumulator value out port 7 (the link port) and <out
(c),3> will send 3 out the port in C. The syntax for in is: in
register,(port #). Both instructions -- a little formality --
must have either the accumulator as the operand or register C as the port
number -- instructions such as <in d,(5) / out (1),%1101111> are not
possible.
Thanks to Alan Bailey, from whom alot of this information
originated.
Port 0, the screen offset. This port tells the LCD what area of RAM to use as video memory. A value between 0 and $3c can be sent, but nothing can be read from it. More will be said about this port when covering GrayScale.
Port 1, the keyboard. This is one of the most
important ports because the programmer must interact with the user through
keypresses. A call, such as _getkey, can perform the task of reading
from port 1, but often times you want to use the ports. If you want
to check for multiple keypresses, for instance, you must use the ports
because _getkey will only return a single key code.
To use port 1, you must first write a bitmask
to the port (by write, I mean send). From that bitmask, keyboard
will set up a group of seven keys that you may want to check and that you
can check by reading from the port.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
So, if you want to check for enter, put a bit mask of %1111101 out the port, then wait, then read from the port and test the bits.
ld c,1
;load C w/ the port number
out (c),%1111101
;put the bitmask out the port
nop
;no operation, wait while the keyboard forms a result
in a,(c)
;read the byte into the accumulator
;at this point, bit 0 tells whether enter is pressed, bit 1 whether +, bit 2 whether -, bit 3 whether *, ;bit 4 whether /, etc.
bit 0,a
;is enter pressed
jr z,enter_pressed
;act on the keypress
;another way of testing bit 0 would have been (just to add perspective):
rra
;bit 0 is copied into the carry flag
jr nc,enter_pressed
;test the carry flag
One thing I should note: if you want to check more than one key with the same bitmask, you don't need to write and read to the ports more than once; you can determine all the keypresses for a bitmask from the solitarily inputted byte.
Port 2, Contrast. This port is write-only ... a problem given that you need to read the contrast in order to do anything with it. TI has solved this by creating a RAM location ($c008) where whoever changes the contrast, implicitly copies the value they changed it to back to that location. The contrast port can take any value between 0 and $1f, where the larger the number, the darker the contrast. For example, to increment the contrast by one notch:
inc_contrast:
ld a,($c008)
;load the current contrast into A
cp $1f
;we don't want to increase the contrast if it's already maximum
ret p
;sign flag is reset if A is greater than or equal to $1f
inc a
;increase it (our objective)
out (2),a
;tell the port its new contrast
ld ($c008),a
;document our changes by loading A back into $c008
ret
Port 3, On interrupt and LCD power. Writing
to bit 3 of this port will turn on / off the LCD. Reading from bit
3 will tell you whether or not the on key has been pressed.
To turn off the LCD, set bit 3 of the byte your
sending out the port: <ld a,%1000 / out (3),a>. This will turn
off the screen, but the processor will still be executing commands at full
power. You can turn the LCD back on by writing a byte to port 3 that
has bit 3 reset: <sub a / out (3),a>. (You should note that <sub
a> is a faster and shorter way of loading the accumulator with 0 than <load
a,0>, which works because subtracting A from A will always leave 0.)
There is also a ROM call that will turn the LCD off for you, wait for on
to be pushed and then return at $4101 (called _off in ti86un.inc).
Reading the on key can not be done from port 1 or
with _getkey, as you may have noticed. Instead, you must identify
the action by whether bit 3 of the byte read in port 3 is set or reset;
set indicating that the key is not pressed, and reset indicating that the
key is pressed:
in a,(3)
;read the byte
and %1000
;bitmask
jr z, on_pressed
;if result of AND is 0, zero flag will be set meaning bit 3 was reset
Ports 5 & 6, Paged ROM & RAM. It's
about time you knew how the calculator fits 384k of memory into 64k of
addressable z80 space. The 64k of memory is divided into four main
parts. The first part, $0000 to $3fff, is Read-Only memory called
ROM page 0, which contains alot of the calculator's most important ROM
calls and pointers to the rest of the ROM calls. However, you never
want to call anything on page 0 because it is different in all ROM versions.
This page is static (it's unmovable).
The 16k from $4000 to $7fff is paged memory,
meaning that it contains one page of the 16 swappable pages (numbered 0
to $0f) in the calculator's banked memory. The current page -- the
page that it swapped into $4000 to $7fff -- is determined by the value
put into port 5. As this goes, only one page of ROM (not including
page 0) is available to the processor at any given moment. To change
ROM pages, put the page number out port 5: <ld a,7 / out (5),a> loads
page 7 into the area where it can be read by the calc. Note that
because it is Read-Only memory, you can't write to it.
Thankfully, because of the way our calculator is
set up, we don't have to change pages every time we want to make a ROM
call. On page $0d, there is a table of ROM calls, that automaticly
change pages, execute the routine, and then return the page back to $0d.
Page $0d is interesting because it is never used by the ROM -- it exists
solely for the asm programmer! Additionally, page $0d is the only
page that's identical in all ROM versions (if you make a program that calls
a page besides $0d, you can expect it to crash on many calculators).
Also, for this reason, page $0d is the current swapped page whenever an
asm program initially begins exectuting.
A similar structure exists for RAM: the 16k of RAM
from $8000 to $bfff is a swapped RAM page; the 16k from $c000 to $ffff
is a static RAM page we call RAM page 0.
There are eight RAM pages, numbered $40 to $47 (they
are, nonetheless, often referred to as RAM pages 0 to 7). The currently
swapped page is controlled by port 6: <ld a, $42 / out (6),a>.
Also, to see what the swapped page is: <in a,(6)>.
RAM page 2 is the swapped page when an asm program is initially opened.
Page 0 is always located between $c000 and $ffff
and is used for miscellaneous storage (the current contrast, for example)
with a large space of about 9k for running asm programs at $d748.
This page also contains areas for text and video memory.