Part Eight:
The Ports

    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.
 
 

Bitmask
Bit 7
Bit 6
Bit 5
Bit 4
Bit 3
Bit 2
Bit 1
Bit 0
%111111
MORE
 EXIT
2ND 
F1 
F2 
F3 
F4 
F5 
%1011111
ALPHA 
GRAPH 
LOG 
LN 
x^2 
,
STO 
 
%1101111
x-var 
TABLE 
SIN 
EE 
%1110111
DEL 
PRGM 
COS 
%1111011
 
CUSTOM 
TAN 
(-) 
%1111101
 
CLEAR 
ENTER 
 %1111110
 
 
 
 
UP 
RIGHT 
LEFT 
DOWN 

 

    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.