InterNestor Lite 2.0 Programmer's manual Copyright (c) 2003-2010 by Konamiman ---------------------------------------- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. See http://www.gnu.org/licenses/ 1. INTRODUCTION 1.1. WHAT IS INTERNESTOR LITE? InterNestor Lite (INL) is a TCP/IP stack for MSX2 computers with 128K RAM. There are two versions: INL for serial port, and INL for Ethernet. INL for serial (RS232) port allows Internet connection via ISP using a modem, as well as direct connection to another computer using a null-modem cable. This version uses PPP as the link layer protocol, and requires the Fossil driver (developed by Erik Mass) to be installed in order to work. INL for Ethernet allows communication with other machines in an Ethernet network, as well as Internet connection by means of an ADSL or cable router. An Ethernet UNAPI implementation is required by this version. Starting at version 2.0, INL is compatible with the TCP/IP UNAPI specification. This means that internet applications targetting this specification can be used when INL is installed. This manual is intended for programmers. If you are an end user, you should look at InterNestor Lite User's manual, available at www.konamiman.com. 1.2. DIFFERENCES WITH INL 1.x Tihs section summarizes the main differences between the TCP/IP UNAPI specification and INL 1.x from the programmer point of view. - The most important concept to understand is that you will not develop "client applications for INL" anymore. Instead, you will develop "TCP/IP UNAPI client applications". These applications will work with INL 2.0, but also with any other TCP/IP stack compatible with the TCP/IP UNAPI specification that can be developed in the future. - The INL 1.x detection method has been removed. You will not try to detect INL anymore; instead, you will perform a standard UNAPI discovery procedure in order to detect any TCP/IP implementation that can be available. - An exception for the above point is when you want to use any of the INL implementation-specific functions. In this case, you invoke the standard UNAPI routine to get information about the implementation, and check that the implementation name is "Inte rNestor Lite". - The old INL 1.x API has been removed, this means that the old client applications developed for INL 1.x will not work with INL 2.0. Applications will have to be upgraded to become TCP/IP UNAPI client applications. - Unlike INL 1.x, INL 2.0 is not capable of transferring data from/to page 1 on the user space. This is allowed by the UNAPI specification. - Unlike INL 1.x, INL 2.0 is capable of transferring data from/to any slot and segment (on pages 0, 2 and 3), not only the TPA segments. - Now you have to check if the capabilities you need (for example opening passive TCP connections) are actually implemented by the installed TCP/IP UNAPI implementation. You can do this in two ways: actively, by invoking the TCPIP_GET_CAPAB routine and checking the capabilities flags returned; and passively, by trying to invoke the desired routines and checking if a "not implemented" error code is returned. - The UDP protocol is now managed in a completely different way, more consistent with how TCP/IP stacks on other platforms do things. In INL 1.x, you had a single queue for incoming UDP datagrams, and you had to check local port, remote port, and remote IP of each received datagram. In the TCP/IP UNAPI specification, before sending and receiving UDP datagrams you need to open UDP connections; each connection is associated to a local UDP port. You send datagrams to a given connection, not to a given port; and you still need to check the remote port and remote IP of each received datagram, but not the local port as each connection has only one associated port. - In INL 1.x, you ensured that TCP connections accidentally left open by other applications were properly closed by trying to close or abort all connections (numbered from 1 to 4) one by one. Now, you can achieve the same effect by invoking TCPIP_TCP_CLOSE or TCPIP_TCP_ABORT only once, passing zero as the connection number. By the way this now applies to UDP as well. - Now TCP and UDP connections can be opened as "transient" or as "resident". The only difference is that resident connections will NOT be closed (or aborted) when invoking TCPIP_TCP_CLOSE, TCPIP_TCP_ABORT or TCPIP_UDP_CLOSE with zero as the connection number. Usually you will open connections as "transient". - Most of the INL 1.x routines that offer features not defined in the TCP/IP UNAPI specification are offered as implementation-specific routines, with the notable exception of Base64 encoding (removed due to lack of memory space in the INL code segment). If you need to implement Base64 encoding in your application, feel free to grab the Base64 related routines from the INL 1.1 source code, available at www.konamiman.com. Please take a look at the UNAPI specification, the TCP/IP UNAPI specification and the source code of the base applications for more details. These resources are available at www.konamiman.com. 2. INTERNESTOR LITE - PROGRAMMER'S GUIDE This part of the INL manual supplies all the required information to develop programs that use INL to gain Internet access or to communicate with other machines in a point-to-point way or in a local network by using TCP/IP. 2.1. IMPLEMENTATION NAME AND VERSION The UNAPI implementation name of INL (whose pointer is returned by the mandatory UNAPI information retrieving routine) is "InterNestor Lite". This manual describes implementation version 2.0. 2.2. AVAILABLE CAPABILITIES AND FEATURES The TCPIP_GET_CAPAB routine will return the following information for INL 2.0. Capabilities flags set: - Send ICMP echo messages (PINGs) and retrieve the answers - Resolve host names by querying a DNS server - Open TCP connections in active mode - Open TCP connections in passive mode, with specified remote socket - Open TCP connections in passive mode, with unsepecified remote socket - Explicitly set the PUSH bit when sending TCP data - Flush the output buffer of a TCP connection - Open UDP connections - Explicitly set the TTL and TOS for outgoing datagrams - Explicitly set the automatic reply to PINGs on or off - Automatically obtain the IP addresses, by using DHCP or an equivalent protocol Capabilities flags NOT set: - Resolve host names by querying a local hosts file or database - Send and receive TCP urgent data - Send data to a TCP connection before the ESTABLISHED state is reached - Open raw IP connections Features flags set: - Checking network state requires sending a packet in looback mode, or other expensive (time consuming) procedure - User timeout suggested when opening a TCP connection is actually applied Features flags NOT set: - Physical link is point to point - Physical link is wireless - Connection pool is shared by TCP, UDP and raw IP (see explanation about maximum simultaneus connection support below) - The TCP/IP handling code is assisted by external hardware - The loopback address (127.x.x.x) is supported - A host name resolution cache is implemented - IP datagram fragmentation is supported The maximum datagram size allowed is 576 bytes. 2.3. IMPLEMENTATION SPECIFIC ROUTINES This section describes the implementation-specific routines exposed by INL. These are mostly the routines that were present in INL 1.x but are not defined in the TCP/IP UNAPI specification. Standard routines, as well as the error codes, are defined in the TCP/IP UNAPI specification. * INL_GET_DATASEG: Get the INL data segment number Input: A = 128 Output: A = Error code (always zero) B = Segment number INL installs itself on wo segments, both in the primary mapper. One segment is for code, and its number is obtained when performing the UNAPI discovery procedure. The other is for data, and its number can be obtained by invoking this routine. Contents of the data segment are described in section 2.4. * INL_ETH2ASCII: Converts a Ethernet hardware address into an ASCII string Input: A = 129 HL = Source address in TPA of hardware address to convert DE = Address on TPA where to copy the generated string A = Flags that indicate the conversion format: Bit 0: 0 to use upper case for hexadecimal letters 1 to use lower case for hexadecimal letters Bit 1: 0 to use "-" as two-digit group separator 1 to use ":" as two-digit group separator Output: A = Error code (always zero) This routine converts the 6 byte hardware address passed on the address specified by HL into an ASCII string, which is copied to the TPA address specified in DE. For example, if the address 0x112233AABBCC is passed, the generated string is "11-22-33-AA-BB-CC" if A=0, and "11:22:33:aa:bb:cc" if A=3. The generated string has always a length of 17 characters, and no termination character is added to it. * INL_ASCII2ETH: Obtains the hardware address represented by an ASCII string Input: A = 130 HL = String address in TPA Output: L-H-E-D-C-B = Resulting hardware adrress A = Error code (ERR_INV_PAR if not a valid string) This routine interprets the ASCII string whose address in TPA is passed in HL, and if it represents a valid hardware address, then A=0 and the resulting address in HL, DE and BC are returned; otherwise, A=ERR_INV_PAR is returned. The format of the string must be the same as generated by the ETH2ASCII routine: six two-hexadecimal digits groups separated by "-" or ":" characters. It is not necessary that the string have any special termination character. * INL_MASK2BITS: Obtains the number of bits set to one in a subnet mask Input: A = 131 L.H.E.D = Subnet mask Output: A = Error code (always zero) B = Number of bits set to one This routine examinates the subnet mask passed in HL and DE, and returns in B the number of bits that it has set to one. The routine begins the examination from the most significant bit and stops when encountering the first bit set to zero; that is, if for example the mask 255.192.255.255 is passed, then it will return B=10. * INL_BITS2MASK: Generates a subnet mask from the number of bits set to one (Ethernet version only) Input: A = 132 B = Number of bits set to one Output: A = Error code (always zero) L.H.E.D = Subnet mask This routine generates a subnet mask from its number of bits set to one. For example, if B=17 is passed, it returns the mask 255.255.128.0. The number of bits passed must be a value between 0 and 32. Values higher than 32 generate the mask 255.255.255.255. * INL_GET_TBLADD: Obtains the address and the capacity of the ARP and routing tables Input: A = 133 Output: A = Error code (ERR_NOT_IMP in the serial version) IX = ARP table address IY = routing table address B = ARP table capacity C = routing table capacity This routine is available on the Ethernet version only. In the serial version, it will always return ERR_NOT_IMP. This routine returns in IX and IY the addresses in the data segment of the ARP and routing tables, and in B and C their capacities (the maximum number of entries they can afford). The capacities are 32 ARP entries and 16 routing entries in this code version of INL, but they could change in future versions. The format of both tables is described in section 2.5. * INL_CALC_MD5: Calculation of the MD5 hash of a block of data Input: A = 134 HL = Block address in TPA DE = Address where the resulting hash will be stored (16 bytes) BC = Block length (0 to 1024 bytes) Output: A = Error code (ERR_INV_PAR if BC>1024 was specified) This routine calculates the hash code of the passed data block using the MD5 algorithm described in RFC1321. The resulting hash is a 16 byte "signature" generated from the data of the block; theorically it is unique for each data block and it is irreversible (that is, it is not possible to obtain back the original data block from the hash). This routine is used by the INL serial version in the authentication phase using the CHAP protocol during the PPP connection establishment. It may be useful when implementing higher level protocols over TCP that use MD5 based authentication mechanisms (for example a SMTP client). * INL_CALC_CHKSUM: Calculation of the standard checksum for a block of data Input: A = 135 HL = Block address in TPA BC = Block length (1 to 1024 bytes) Output: A = Error code (ERR_INV_PAR if BC>1024 or BC=0 was specified) DE = Resulting checksum This routine calculates the standard checksum (that is, the one based on one's complements used in the IP, TCP and UDP headers) of the specified block. The checksum is a verification sum generated from the data on the block, and it is used normally to verify the integrity of the packets received from Internet. The following is a simple procedure to test how this routine works: 1) Generate a random data block. 2) Put the two first bytes of the block to 0. 3) Pass the block to CALC_CHKSUM. 4) Write the returned chekcsum in the two bytes that we had previously set to 0: LD (BLOCK),DE 5) Pass again the block to CALC_CHKSUM. The result returned by the last operation should be DE=0 (correct checksum, meaning that the block was not modified -apart from modifying the checksum itself- between the two calls to CALC_CHKSUM). In a real case, the block would be a datagram, and between steps 4 and 5 there would have been a transfer of the dtagaram through Internet, with a possible data corruption; that's why the checksum is useful. * INL_IP_STRING: Generate a string representing a given IP address Input: A = 136 HL, DE = IP address, with the format L.H.E.D BC = Address on TPA where the string will be generated (maximum 16 bytes) A = Termination character to be used in the generated string Output: A = Error code (always zero( This function is useful for applications that need to display information about IP addresses. For example, if HL=#0304 and DE=#0A0B are passed, it will generate the string "4.3.11.10". The generated string will be zero-terminated. The buffer in TPA for the generated string should have a length of 16 bytes, to allow the storage of the longest possible generated string (with the form "xxx.xxx.xxx.xxx$"). * INL_GET_MAC: Obtains the Ethernet hardware address Input: A = 137 Output: A = Error code (ERR_NOT_IMP in the serial version) L-H-E-D-C-B = Ethernet hardware address This routine is available on the Ethernet version only. In the serial version, it will always return ERR_NOT_IMP. * INL_SEND_MODEM: Sends a command to the modem and obtains the reply Input: A = 138 B = 0 to send the command pointed by HL B = 1 to send ATDT+the number stored in BUF_DIAL HL = Address of the command in TPA (if B=0) DE = Address where to store the reply in TPA (0 if we are not interested in the reply) C = Termination character to be used in the reply (if DE<>0) Output: A = Error code: ERR_NOT_IMP in the Ethernet version ERR_INV_OPER if B=1 was passed but BUF_DIAL contains an empty string B = Termination code: 0 if none of the other conditions apply 1 if the answer starts with OK 2 if the answer starts with ERROR 4 if the answer starts with the string stored in BUF_MODREP This routine is available on the serial version only. In the Ethernet version, it will always return ERR_NOT_IMP. This routine is the only one of INL that allows interaction with the modem. It is tipically used in two scenarios: 1) Connecting to the ISP before opening a PPP connection. In this case it is necessary to first establish the ISP phone number in the buffer BUF_DIAL of the INL data segment (and, if necessary, to establish the positive modem reply on BUF_MODREP), and then to execute this routine passing Cy=1 to it. The routine does not return until a reply is received from the modem. If this routine returns A=1 or A=4, then the connection was successful and it is possible to proceed to open the PPP connection; otherwise the connection could not be established. More precisely, if A=8 then no command will have been sent to the modem. 2) Sending an initialization command to the modem, prior to establishing the connection. In this case the routine must be executed by passing to it Cy=0 and HL pointing to the modem command to be sent. As in the other case, the routine will not return until a modem reply is received. If the routine returns A=1, a successful execution of the command should be assumed. In both cases, it is possible to pass in DE the address of a buffer in TPA where the modem reply will be stored, in case we want to examine it (if it is not enough for us to know that it was "OK", "ERROR" or "CONNECT"). WARNING: If there is no modem connected to the RS232 port, this routine will hang the computer. Note: This routine can detect whether the modem has the local echo feature enabled. In this case, the command itself will be filtered and not included in the reply. * INL_PPP_OPEN: Open a PPP connection Input: A = 139 Output: A = Error code (ERR_NOT_IMP in the Ethernet version) This routine is available on the serial version only. In the Ethernet version, it will always return ERR_NOT_IMP. This routine initiates a PPP connection open, assuming that the needed parameters of the INL data segment were previously appropriately set up (PPP_USER, PPP_PASSWORD and if necessary, the IP addresses as well). In case of ISP connections, it is necessary that the modem has established a physical connection already; the SEND_MODEM routine is used for this. The routine returns immediately, and the connection is established as a background task, controlled by the timer interrupt service routine. It is possible to check the connection state at any time by simply reading the PPP_STATE variable on the INL data segment (the connection will be completely established when this variable reaches a value of 4). It PPP_STATE returns to 0 (connection closed), it means that an error occurred and the connection could not be completed. The error code can then be obtained from the variable PPP_CLCODE. WARNING: Never execute this routine when the connection is already opened or opening (that is, when PPP_STATE has any value other than zero). When INL.COM is used to open a connection, an error message is shown if the connection is already opened or opening; but the connection state check is done by INL.COM itself, not by the routine PPP_OPEN. * PPP_CLOSE (#404E): Close the PPP connection Input: 140 Output: A = Error code (ERR_NOT_IMP in the Ethernet version) This routine is available on the serial version only. In the Ethernet version, it will always return ERR_NOT_IMP. This routine closes the PPP connection by sending a LCP termination packet to the peer, and then sets PPP_STATE to zero (for simplicity's sake, a deliberated violation of the PPP protocol is committed here: theorically a confirmation packet sent by the peer should be awaited for before actually considering the connection as closed). In ISP connections, no command will be sent to the modem, but it should disconnect in few seconds, because the ISP will close the physical link when receiving the terminat ion packet. This routine may be executed safely whatever the PPP connection state is, even if it is already closed. 2.4. THE DATA SEGMENT INL uses a data segment as a storage space for various configuration parameters, state variables and data buffers. The data segment number can be obtained by invoking the INL_GET_DATASEG routine, it is always a segment on the primary mapper. In this section the configuration and state variables part will be explained with detail; it is the part of the data segment that may have interest for the programmer. Most of the variables of this part have an associated option in INL.COM that allows modifying them easily. To access the configuration and state variables you need to do the following: 1) Make sure that the primary mapper is switched on page 2 (this is the default state for .COM programs). 2) Switch the INL data segment on page 2 (obtain the segment number by using the INL_GET_DATASEG routine, see section 3.3). 3) Read or write the desired variables with LD instructions. 4) Optionally, restore the previous slot/segment on page 2. Of course, you can also access the variables by switching the data segment in any other page; but since the INL code always uses page 2 for this purpose, in the variables description the range #8000-#BFFF is used to specify their addresses. Following there is the detailed description of the configuration variables part; these variables may be modified by the programmer unless otherwise stated. Note that some of these variables (for example the IP addresses) may be accessed and modified by using the standard TCP/IP UNAPI routines as well. The following variables exist in all versions of INL: * BUF_IPLOCAL (#8000): Local IP address. * BUF_IPDNS1 (#8004): IP address of the primary DNS server. * BUF_IPDNS2 (#8008): IP address of the secondary DNS server. * REPLYECHO (#800C): Flag to indicate whether the incoming ICMP echo requests (PINGs) will be replied or not. By default it is activated. * TTL (#800D): TTL for the outgoing datagrams. The default value is 64. * TOS (#800E): TOS for the outgoing datagrams. The default value is 0. * CHKVECT (#800F): Checksums calculation vector. Its behavior is explained in the User's Manual. The default value is 31. * PING_SIZE (#8010): Default size for the data part of the outgoing ICMP echo requests (PINGs); it must be a two-byte number in the range 0 to 512. The default value is 64. The following variables are specific of the serial version: * BUF_DIAL (#8012): 16 byte buffer to store the ISP phone number. By default it is an empty string. * BUF_MODREP (#8022): 16 byte buffer to store the expected modem reply upon successful dialing. By default it is the string "CONNECT". * BUF_PPPUSER (#8032): 32 byte buffer to store the user name for the PPP authentication. By default it is an empty string. * BUF_PPPASSW (#8052): 32 byte buffer to store the password for the PPP authentication. By default it is an empty string. * BUF_IPREMOTE (#8072): Peer's IP address. * NEG_DNS (#8076): Flag to indicate whether the IP addresses of the DNS servers will be negotiated during the PPP connection establishment or not. By default it is activated. * USEPPPECHO (#8077): Flag to indicate whether PPP echo requests will be periodically sent to check the connection or not. By default it is activated. * USEPPPVJ (#8078): Flag to indicate whether Van Jacobson compression should be used. By default it is activated. * PPP_STATE (#8079) - READ ONLY: Current state of the PPP connection, it may have one of these values: 0: Closed 1: Negotiating link (LCP) 2: Authenticating (PAP or CHAP) 3: Negotiating IP addresses (IPCP) 4: Open (datagrams can be sent and received) The only transitions possible between states are: 0->1 (when executing PPP_OPEN), 1->2, 1->3, 2->3, 3->4 and 4->0 (when executing PPP_CLOSE). Again, for the sake of simplicity the PPP protocol specifications are deliberately violated here: the automaton should have more states and permit more transitions between them. * PPP_CLCODE (#807A) - READ ONLY: Cause of the last PPP connection close, it may have one of the following values: 0: No PPP connection has been established since INL was installed 1: Error when dialing the ISP number (the modem did not return "CONNECT" or equivalent) 2: The user has closed the connection 3: The peer has closed the connection with a LCP termination packet 4: The peer has closed the connection with a IPCP termination packet 5: Authentication failed 6: Authentication required but with an unknown method (it is not PAP nor CHAP) 7: Authentication required but PPP_USER is an empty string 8: LCP negociation took more than one minute 9: Authentication took more than one minute 10: IPCP negociation took more than one minute 11: Local IP address unknown by both us and the peer 12: Remote IP address unknown by both us and the peer 13: LCP "Code Reject" packet received 14: LCP "Protocol Reject" packet received 15: IPCP "Code Reject" packet received 16: IPCP "Protocol Reject" packet received 17: Too many PPP echo requests received without receiving a reply The following variables are specific of the Ethernet version: * OBSLOT (#8012): Formerly the ObsoNET slot, currently unused. * HWAD (#8013): Ethernet hardware address, six bytes. * SUBNET_MASK (#8019): Subnet mask. * DEFGW (#801D): Default gateway. * ARP_TOUT (#8021): Dynamic ARP entries lifetimes, in 1/60 seconds (four bytes). By default it is the value equivalent to five minutes. * ARP_TOUT_SECS (#8025): Dynamic ARP entries lifetimes, in seconds (four bytes). This variable and the previous one must be kept syncrhronized. * FRAME_TYPE (#8029): Flag that indicates the Ethernet frame type for outgoing packets, Ethernet II (0) or IEEE802.3 (#FF). By default it is 0. * CHECK_NET (#802A): Flag that indicates whether to perform automatic network connection checkings every ten seconds (#FF) or not (0). By default it is #FF. * DHCP_VECT (#802B): DHCP configuration vector (see the INL IP D command in the User's Guide); 16 bits, of which only six are used in this code version of INL. By default it is 63. * DHCP_VECT_O (#802D): Vector that indicates which parameters have actually been obtained by using DHCP. It is established when the DHCP automaton reaches the BOUND or the CONFIGURED state. READ ONLY. * DHCP_STATE (#802F): DHCP automaton state (READ ONLY): 0: INIT 1: SELECTING 2: REQUESTING 3: BOUND 4: RENEWING 5: REBINDING 6: INFORMING 7: CONFIGURED The storage format of these variables is as follows: - Two byte values are stored in the usual little-endian format. - The strings are stored with a 0 termination character. - IP addresses are stored in their natural order; for example 1.2.3.4 will be stored as DB 1,2,3,4. The default value for all the IP addresses is 0.0.0.0 - Flags are 0 if deactivated and #FF if activated. - Four-byte numbers are stored in big-endian format (high byte first). Note: the default values for the variables are the values they have when INL is installed, or after executing INL D. There is not a routine in the code segment for initializing the variables to their default values; INL.COM does this task "manually", modifying each variable sepparately. 2.5. ARP AND ROUTING TABLES FORMAT (ETHERNET VERSION ONLY) In this section the format of the ARP and routing tables used by INL is explained. The addresses and capacities of these tables can be obtained by using the INL_GET_TBLADD routine, and the use that INL makes of them is explained in the User's manual (see INL.COM commands INL ARP and INL ROU). An entry of the ARP table consists of 16 bytes, whose meaning is as follows: +0: Entry state (1 byte) 0: Unused 1: Static entry 2: Dinamic entry in use 3: Dinamic entry being resolved +1: Ethernet address (6 bytes) Valid on entry states 1 and 2 +7: IP address (4 bytes) Valid on entry states 1, 2 and 3 +11: Entry expiration timer (4 bytes, 1/60 secs) Valid on entry state 2 +15: Unused, always zero (1 byte) On state 3, the meaning of bytes 11 to 14 change as follows: +11: Timer for sending the next ARP packet (1 byte, 1/60 secs) +12: Number of ARP packets already sent (1 byte) +13, +14: Unused, always zero Static entries must be created manually, by searching an empty entry and filling appropriately the Ethernet address, IP address and state entry fields; INL.COM does this when INL ARP A is executed. Entries are also manually deleted, by simply writing zero in the state field. Entries in resolution process are created when INL needs to learn the hardware address corresponding to a given IP address. Up to five requests, with a separation of two seconds between them, are sent. If a reply arrives, the entry becomes dynamic; otherwise it is deleted. Dynamic entries are created from the entries in resolution process, or directly when a non-requested ARP request or reply arrives, as specified in RFC826. When the remaining lifetime timer reaches the value zero, the entry is deleted. The timer is reset to its initial value if a request or reply arrives from the same IP address. When INL needs to create a new dynamic entry and the table is full, the dynamic entry in use whose remaining lifetime is smaller is searched, and it is overwritten with the new entry. INL recognizes incoming UNARP packets (RFC1868): when the "Hardware address length" field of a received ARP reply packet has the value zero, the associated entry is deleted from the ARP table, if it exists. Regarding the routing table, the format of its entries (16 bytes each) is as follows: +0: Entry state (1 byte) 0: Unused 1: In use +1: Destination subnet IP address (4 bytes) +5: Destination subnet mask (4 bytes) +9: Router IP address (4 bytes) +13 to +15: Unused, always zero (3 bytes) These entries must be created manually, searching for an empty entry and filling appropriately all fields; INL.COM does this when INL ROU A is executed. Entries are also manually deleted, by simply writing zero in the state field. 3. CHANGE LIST AND CONTACT Please see the INL User's manual for a detailed change list and contact information.