PROGRAMMATIC INTERFACE DOCUMENT


This file is intended for computer programmers who wish to access
ZIPKEY directly from within their programs.  If you are not a
computer programmer, you may ignore this file.


The ZIPKEY Interrupt

When ZIPKEY is installed as a memory-resident program (using the
ZIPKEY 4 command), it takes over one of the 8086's interrupts,
INT 179 (decimal; it's B3 in hexadecimal).  This interrupt serves
two functions: first, it is the method by which ZIPKEY detects if
it has already been installed.  Second, it provides the interface
by which computer programs can call ZIPKEY directly, to obtain
zipcode-related services.

I conducted a survey of numerous computers before selecting INT
179 to be used by ZIPKEY.  I found no computers or peripherals
that use this interrupt, and I am hopeful that there are none.
If your computer does use INT 179, then ZIPKEY and the device or
program using INT 179 will interfere with each other.  You can
change the interrupt number ZIPKEY uses by modifying the ZIPKEY
program itself: the number 179 is stored in the fourth byte of
the ZIPKEY program code, immediately beyond the three-byte JMP
instruction that begins the program.  This JMP occurs at the very
start of the ZIPKEY.COM file, or immediately beyond the 512-byte
header of the ZIPKEY.EXE file.  You can use a debugger or a hex
editor to modify the byte to an unused interrupt number.


Testing for ZIPKEY's Presence

Before you program attempts to call ZIPKEY, it may wish to verify
that ZIPKEY has been installed, and report an error if it isn't.
Otherwise, you program may crash when it tries to call code that
isn't there.

You can test for ZIPKEY's presence by examining the INT 179
handler, whose doubleword pointer is stored at offset 179*4 (=716
=02CCH) within segment 0 of the 8086 memory space.  If ZIPKEY is
installed, the doubleword points to its code segment.  Location
075H within that code segment contains the signature string
ZIPKEY.  Thus, the following code sequence will check for
ZIPKEY's presence:
                                                              P-2

OUR_ZIPKEY:                ; our copy of the template
  DB 'ZIPKEY'

CHECK_FOR_ZIPKEY:
  PUSH DS                  ; save register value
  SUB AX,AX                ; load zero
  MOV DS,AX                ; point DS to 0 -- 8086 interrupts
  MOV DS,WORD PTR 02CEH    ; fetch the segment part of INT 179
  MOV SI,075H              ; point to signature in segment
  PUSH CS                  ; push our code segment
  POP ES                   ; set ES to our code segment
  MOV DI,OFFSET OUR_ZIPKEY ; now ES:DI points to 'ZIPKEY'
  MOV CX,3                 ; there are 3 words in 'ZIPKEY'
  REPE CMPSW               ; does DS:75H point to 'ZIPKEY'?
  POP DS                   ; restore register before jumping
  JZ ZIPKEY_INSTALLED      ; jump if it does match
ZIPKEY_NOT_INSTALLED:      ; it does not match

Following the ZIPKEY signature, at offset 07BH within the ZIPKEY
code segment, is a single byte that is zero in the initial
version of ZIPKEY, but will increment each time I release a
ZIPKEY with new features or any other changes to the programmatic
interface.  That way, if you program needs ZIPKEY to be of a
sufficiently recent version, it can ensure that it is.  (You can
also use the version number returned by the ZK_VERSION function
described shortly.)


ZIPKEY Calling Conventions

ZIPKEY's interface works similarly to that of the MS-DOS
operating system, or the LIM-EMS specification.  The interface is
specified in 8086 assembly language.  You load a function number
into the AH register, and possibly other input parameters into
other registers, then you execute an INT instruction with an
appropriate interrupt number -- in ZIPKEY's case, the INT 179
instruction.

When ZIPKEY has completed the function, it returns control to
your program at the instruction following the INT 179.  The
success of the call is indicated by the Carry flag: NoCarry (flag
= 0) indicates success; Carry (flag = 1) indicates failure.  If
the Carry flag is set, AL is usually set to an error code (the
exceptions are those functions which, as mentioned in their
following descriptions, return the state code for a suggested
city).  Here are the possible error codes:

0FDH is returned if ZIPKEY is busy. You will get this error only
     if the program calling ZIPKEY had interrupted another
     program which also happened to be in the middle of a ZIPKEY
     call.  ZIPKEY is not reentrant, so it must refuse calls in
     this situation.  If you think this interrupted-programming
     scenario is possible, you should check for this possibility.
     If ZIPKEY is busy, you should give the interrupted code a
     chance to complete the ZIPKEY call, and try your call again
     later.
                                                              P-3

0FEH is returned if you provided an illegal function number in
     the AH register.

0FFH is returned if the searching function requested failed to
     find a valid entry, or even a suggested value.

Here are ZIPKEY's register-preserving conventions:  The AX
register is clobbered by an INT 179 call; but all other 8086
machine registers are preserved unless they are specifically
named as having return values.  If a function returns an error,
such return registers may be clobbered even if they contain no
values in the return case.  For example, the ZK_STCITY_ZIP
function returns values in BH, CX, and DX.  Except for AX, all
other registers not mentioned are preserved (SI,DI,BP,DS,ES).
The BH, CX, and DX registers may be clobbered even if the search
fails (and Carry is returned).


The ZIPKEY Functions

Following are the functions available to you when ZIPKEY is
installed. For each function, you load the input registers with
the values indicated, then invoke INT 179.

The names of the functions (ZK_VERSION, ZK_ABBR_ST, etc.) have no
significance to ZIPKEY-- I present them merely as suggested names
if you wish to implement a library of calls; and also so that I
can refer to the functions by name.

ZK_VERSION:   returns ZIPKEY version information.

  In: AH = 070H

 Out: AX = The ZIPKEY program version number, as shown when
         ZIPKEY is invoked or the ZIPKEY window is popped up.  AH
         is the major version number (to the left of the decimal
         point) and AL is the minor version number (to the right
         of the decimal point).  For example, for V1.0 the return
         AX value would be hex 100.

      CL = The number of states, (and territories, etc.) in the
         current database.  Each state is assigned an internal
         numeric code by ZIPKEY, that is used by many of the
         functions of ZIPKEY's programmatic interface.  These
         codes range from 0 to CL-1.

      DX = The date of the current ZIPKEY.OVL database.  DL is
         the month, ranging from 1 for January to 12 for
         December.  DH is the year minus 1900; e.g., decimal 89
         for 1989.
                                                              P-4

ZK_ABBR_ST:   converts a two-letter abbreviation into a state
              code.

 In:  AH = 071H

      BX = The 2-letter ASCII abbreviation for the state, in
         either upper- or lower-case.  BL is the first letter and
         BH is the second letter.

Out:  AL = The ZIPKEY state code, suitable for input to other
         ZIPKEY functions in this chapter.

         AL = 0FFH with the Carry flag set if not found.


ZK_ST_ABBR:   converts a state code into a two-letter
              abbreviation.

 In:  AH = 072H

      BL = The ZIPKEY state code, ranging from 0 through CL-1,
         where CL is returned by ZK_VERSION.

Out:  AX = The 2-letter ASCII abbreviation, upper case.  AL is
      the first letter and AH is the second letter.

      The Carry flag is set if the input state code BL was
      invalid.


ZK_ST_NAME:   converts a state code into a full state name.

 In:  AH = 073H

      BL = The ZIPKEY state code, as returned by ZK_ABBR_ST.

      ES:DI points to the buffer to contain the returned state
      name.

Out:  The full state name is output to ES:DI-pointed memory. DI
      is advanced beyond the output.

    The Carry flag is set if the input state code BL was invalid.
                                                              P-5

ZK_ZIP_DIGITS:  converts zipcode DXCH into a ASCII digits string.

 In:  AH = 074H

      DX = The zip region, ranging from 0 to 999.

      CH = The last 2 digits of the zipcode, from 0 to 99.  Or,
           if you want only 3 digits output, set CH to 255.

Out:  The 5 ASCII decimal digits of the zipcode are output to
      ES:DI-pointed memory.  DI advanced beyond the output.

      The Carry flag is set if DX was greater than 999.

      NOTE this is not really a database function-- it is just
      an unpacking function provided for convenience.


ZK_ZIP_ST:   looks up the state code for zipcode DXCH.

 In:  AH = 075H

      DX = The zip region, ranging from 0 to 999.

      CH = The last 2 digits of the zipcode, from 0 to 99.

Out:  AL = the ZIPKEY state code.

      BX = the area code.

      The Carry flag is set, and AL=0FFH, if the zipcode
      region (the first three digits) is invalid.

      Note: for speed, this routine does not check for validity
      of the individual zipcode: it looks at only the zip
      region and the state-exceptions list.  If you wish to
      check the validity of a zipcode, use ZK_ZIP_CITY instead.
                                                              P-6

ZK_ZIP_CITY: looks up the state and city for zipcode DXCH.

 In:  AH = 076H

      DX = The zip region, ranging from 0 to 999.

      CH = The last 2 digits of the zipcode, from 0 to 99.

      ES:DI points to the buffer to contain the returned city
      name.

Out:  The ZIPKEY state code is returned in the AL register,
      and the area code in the BX register.  The
      city name is output to ES:DI-pointed memory.  DI is
      advanced beyond the output.  To ensure that the name does
      not exceed a maximum width, you can set the width using the
      ZK_RESTORE function.  The Carry flag is set if the zipcode
      was not found, with AL/DI set to the suggested state/city
      if there is one.  AL is set to 0FFH if there is no
      suggested city.

      NOTE that a previous call to ZK_ZIP_ST can and should be
      made if you wish to suppress output for the not-found-but-
      suggested case.


ZK_ZIP_KEYS: plays the back exit key BX for the entry with
             zipcode DXCH.

 In:  AH = 077H

      BX = The 16-bit BIOS keycode for a defined ZIPKEY alternate
         exit key.  This is the code returned in the AX register
         after you call the keyboard service routine (MOV AH,0
         followed by INT 016H).  For example, the code for the F1
         function key on a 100% IBM-compatible is 03B00H; add
         0100H for subsequent function keys.

      DX = The zip region, ranging from 0 to 999.

      CH = The last 2 digits of the zipcode, from 0 to 99.

Out:  If there is a failure with no suggested city, ZIPKEY
      returns Carry to the caller.  Otherwise, it returns
      NoCarry, and the zipcode specification defined by the BX
      exit-key is inserted into the BIOS keystrokes buffer, as if
      a user had popped up the ZIPKEY window, typed in the
      zipcode, and exited with the BX exit key.
                                                              P-7

ZK_STCITY_ZIP:  looks up the zipcodes for a given state and city.

 In:  AH = 078H

      BL = The ZIPKEY state code for the city to be looked up.

      DS:SI points to the city-name specification.  The
          specification is a string of ASCII characters,
          terminated by a carriage-return (0DH) if the complete
          name has been given, terminated by a null (00H) if
          ZIPKEY should match any city whose name begins with the
          specification, whether or not that is the complete city
          name.  Letters in the string can be in either upper- or
          lower-case.  ZIPKEY will also unabbreviate the string,
          as described in Chapter 2.

Out:  ZIPKEY accumulates a list of all cities that match the
      specification given.  The list is sorted first
      alphabetically by city name, then numerically by zipcode.
      Each entry on the list is a range of zipcodes for a city
      that matches the specification.  If the first and last
      zipcodes in the range are the same, the range consists of
      just the one zipcode.  If they are different, then they
      both represent valid codes for a matching city.  Any
      zipcodes between the first and last may be either valid
      zipcodes for the same city, or undefined zipcodes.

      The capacity of ZIPKEY's matching-entries buffer is 50. If
      there are more than 50 entries for the specification,
      ZIPKEY will return a count of 51 entries.  You may make
      calls to ZK_CASE_ZIP to determine the entries returned, but
      be aware that unless the complete count happened to be 51,
      the list is incomplete.  The only way to obtain the
      complete list of entries is to extend the city-name
      specification by adding one or more letters in all possible
      combinations.

      The zipcode range for the first entry is returned with this
      call.  To obtain the remaining entries, you make repeated
      calls to ZK_CASE_ZIP.  To obtain a readable display for
      each entry, you call ZK_ZIP_CITY, ZK_ZIP_ST, and
      ZK_ZIP_DIGITS.

The return register values are:

      BH = The number of matching entries (Carry set if BH=0). If
         ZIPKEY's entries-buffer overflowed, BH is set to 51 (see
         the above discussion about capacity).

      DX = The zip region (0 to 999) of the first entry.

      CL = The value of the bottom two digits for the first
         zipcode in the range.

      CH = The value of the bottom two digits for the last
         zipcode in the range.
                                                              P-8

ZK_CITY_ZIP:   looks up zipcodes for a given city.

 In:  AH = 079H

      BL = The ZIPKEY state code of the first state to be
         searched.

      DS:SI points to the city-name specification, just as in
         ZK_STCITY_ZIP.

Out:  ZIPKEY accumulates a list of zipcode ranges, just as in
      ZK_STCITY_ZIP.  With this function, however, if no matching
      entries are found for state BL, then BL is incremented to
      search in the next state.  Searching continues until either
      a match is found or the last state code is reached.  If
      there is no matching entry for the last state, ZIPKEY
      returns AL=0FFH with the Carry flag set.  This differs from
      the city-only search that is initiated interactively from
      ZIPKEY's popup window-- the interactive search cycles
      around to the first state and continues looking from there.

      To get a complete list of matching cities for all states,
      you call this function first with BL=0, then repeat the
      call with BL set to the previous returned AL+1, until you
      get a failed find.


The return register values are:

      AL = The ZIPKEY state code of the first matching state. AL
         is set to 0FFH with Carry set if no state was found.

      BH = The number of matching entries (Carry set if BH=0). If
         ZIPKEY's entries-buffer overflowed, BH is set to 51.

      DX = The zip region (0 to 999) of the first entry.

      CL = The value of the bottom two digits for the first
         zipcode in the range.

      CH = The value of the bottom two digits for the last zipcode
         in the range.
                                                              P-9

ZK_CASE_ZIP:  fetches an entry from a previous ZK_(ST)CITY_ZIP
              call.

 In:  AH = 07AH

      BL = a case number, from 0 to BH-1, where BH is the number
         returned by the last call to ZK_STCITY_ZIP or
         ZK_CITY_ZIP.

Out:  AL = The ZIPKEY state code for this entry.  Note that this
         is the same value for all entries returned by a given
         call to ZK_STCITY_ZIP or ZK_CITY_ZIP.

      DX = The zip region (0 to 999).

      CL = The bottom 2 digits (0 to 99) of the first zipcode in
         the range.

      CH = the bottom 2 digits (0 to 99) of the last zipcode in
         the range. CH = CL if there is only one zipcode.


ZK_SAVE: fetches values necessary to save ZIPKEY's context.

 In:  AH = 07BH

Out:  BL = maximum number of characters for a ZK_ZIP_CITY city name.

      BH = the ZIPKEY state code for the last city-name search.

      BH = 0FF if there is no previous ZK_STCITY_ZIP or
         ZK_CITY_ZIP call.

      CXDX = internal codes identifying the last city search

      This function is provided just in case somebody wishes to
      incorporate ZIPKEY into a multitasking system, in which
      more than one task might be using ZIPKEY.  The task-
      switching code must call ZK_SAVE, record BX,CX, and DX as
      part of the task state, and feed those values to ZK_RESTORE
      before restarting the task.

      ZK_SAVE can also be used to read the current setting for
      the maximum city width, returned in the BL register.
                                                             P-10

ZK_RESTORE: restores ZIPKEY's context from a previous ZK_SAVE call.

 In:  AH = 07CH

      BL = the maximum number of characters for a city name.

      BH = the ZIPKEY state code returned by a previous ZK_SAVE
         call.

      BH = 0FF if you are just setting the city width.

      CXDX = internal codes returned by a previous ZK_SAVE call.

Out:  Carry if BH,CX,DX are not codes which could have been
      returned by a previous ZK_SAVE call

      This function will be used most often to set the maximum
      city width.  It is also used in multitasking systems, as
      described under the previous function ZK_SAVE.


ZK_POPUP:   pops up a ZIPKEY window from a program.

 In:  AH = 07DH

      BL = an index number if you wish to simulate the pressing of
         a hotkey.

      BL = 0FF for immediate popup with nothing played back after
         a confirming Enter key.

Out:  ZIPKEY takes action as if the hotkey indexed by BL has been
      pressed.  This typically includes popping up a window,
      waiting for the user to type a zipcode specification, and
      playing back an indicated sequence through the BIOS
      keystrokes buffer.  The values of BL are defined by the
      listing of hotkeys available from the configuration editing
      menu: BL=0 for the first hotkey, BL=1 for the second, etc.
      If BL is not a valid index ZIPKEY pops up a window, then
      plays back nothing if a confirming Enter is given (ZIPKEY
      will play back alternate exit keys, however).  ZIPKEY does
      not return from this interrupt call until the window is
      popped away-- then it returns NoCarry.
                                                             P-11

ZK_REGION_CITY:   outputs the name of the primary city for a
                  zipcode region.

 In:  AH = 07EH

      DX = The zip region, ranging from 0 to 999.

      ES:DI points to the buffer to contain the returned city
         name.

Out:  AL = The ZIPKEY state code for the region if the region
      exists.  BX is set to the area code.
      The name of primary city for this region is output
      to ES:DI-pointed memory.  DI is advanced beyond the output.
      AL = 0FF with the Carry flag set if the region does not
      exist.


ZK_SWITCH:   enables/disables hotkeys and/or DOS recursion checking.

 In:  AH = 07FH

      BL = a control code:
            0 for turn off hotkeys
            1 for turn on hotkeys
            2 for no change, just return hotkey status
            3 for toggle hotkey status
            6 for disabling DOS recursion checking

Out:  The activation of hotkeys is disabled/enabled/reported.
      When a hotkey is disabled ZIPKEY ignores it, and it reverts
      to its non-ZIPKEY functionality.  AL is returned set to the
      hotkeys status: 0 for off (disabled), 1 for on (enabled).

      If BL=6, ZIPKEY's program interface
      (INT 179) no longer checks
      to see if DOS is busy and cannot be called.  It is the calling
      program's responsibility to insure that DOS can be called.

ZK_AREA_ST:   Delivers the state associated with area code BX.

 In:  AH = 080H
      BX = area code (decimal integer)

Out:  AL = the ZIPKEY state code for the area code, if the area code
      exists.  DX is set to the first zip region associated with that
      state, and CX to the number of zip regions until the last
      region associated with the state.  You must make CX subsequent
      calls to ZK_REGION_CITY to find which regions are actually within
      the state, and/or contain the area code wanted.

      If the area code is Canadian, we return a special state code
      that will be honored by ZK_ST_ABBR and ZK_ST_NAME, but the
      region DX is set to 1000.  If the area code does not exist,
      we return Carry, AL=0FFH and DX=1001.
                                                             P-12

Sample Program Code

Following are fragments of assembly-language code that call
ZIPKEY's programmatic interface.  I've tried to make them
compatible with all the major assemblers, although they are
tested only against my A86 assembler. If you do use my A86
assembler, and you reuse the local labels L3, L4, L6, L8, and L10
elsewhere in your program, you should insert ">" into their
forward references in the following code: JC >L3; JC >L4; JMP
>L6, JNE >L8, JE >L10.

RO_NAME:
  DB 'RO',0
NOME_NAME:
  DB 'nome',0DH


; output a list of states to memory at 08000H

L0:
  MOV AH,070H       ; ZIPKEY function number ZK_VERSION
  INT 179           ; fetch the version information
  MOV CH,0          ; extend states count CL to CX
  MOV BL,0          ; load the first state code
  MOV DI,08000H     ; load the output pointer
L1:                 ; loop here to output each state name
  MOV AH,072H       ; ZIPKEY function number ZK_ST_ABBR
  INT 179           ; fetch the state abbreviation
  STOSW             ; output the abbreviation
  MOV AL,' '        ; load blank
  STOSB             ; output an intervening blank
  MOV AH,073H       ; ZIPKEY function number ZK_ST_NAME
  INT 179           ; output the full state name
  MOV AX,0A0DH      ; load CRLF
  STOSW             ; output CRLF to end this line
  INC BL            ; increment BL to the next state code
  LOOP L1           ; loop to output the next state
                                                             P-13

; output to memory the list of valid zipcodes for region 474

  MOV DX,474        ; load the region number
  MOV DI,08000H     ; load the output pointer
  MOV CH,0          ; the first zipcode has bottom digits 00
L2:                 ; loop here to output each entry
  MOV AH,075H       ; ZIPKEY function number ZK_ZIP_ST
  INT 179           ; check the validity of this zipcode
  JC L3             ; skip if this is not a valid zipcode
  MOV BL,AL         ; zipcode valid: copy state code to BL
  MOV AH,074H       ; ZIPKEY function number ZK_ZIP_DIGITS
  INT 179           ; output the zipcode digits
  MOV AL,' '        ; load blank
  STOSB             ; output an intervening blank
  CALL CITY_ST_CRLF ; output the city, state, and CRLF
L3:
  INC CH            ; increment the bottom 2 digits counter
  CMP CH,100        ; have we completed the region?
  JB L2             ; loop if not, to output the next line


; output a list of Illinois cities starting with RO

  MOV DI,08000H     ; load the output pointer
  MOV BX,'LI'       ; characters IL are reversed on the 8086
  MOV AH,071H       ; ZIPKEY function number ZK_ABBR_ST
  INT 179           ; fetch the state code for this abbreviation
  MOV BL,AL         ; copy the state code to BL
  PUSH CS           ; push our code segment
  POP DS                 ; set DS to our code segment
  MOV SI,OFFSET RO_NAME  ; point to "RO" city specification
  MOV AH,078H            ; ZIPKEY function number ZK_STCITY_ZIP
  INT 179           ; get the list of entries for this city
  JC L4             ; skip if the list was empty
  CALL OUT_ENTRIES
L4:


; output a list of Nomes in the United States

  MOV DI,08000H            ; load the output pointer
  PUSH CS                  ; push our code segment
  POP DS                   ; set DS to our code segment
  MOV SI,OFFSET NOME_NAME  ; point to "Nome" specification
  MOV BL,0                 ; first state code is zero
  JMP L6                   ; jump into loop

L5:                        ; loop here for each matching state
  PUSH AX                  ; save the state code
  CALL OUT_ENTRIES         ; output the entries for this state
  POP BX                   ; restore the state code to BL
  INC BX                   ; increment to the next state
L6:
  MOV AH,079H              ; ZIPKEY function number ZK_CITY_ZIP
  INT 179                  ; find a state with the desired city
  JNC L5                   ; loop if found, to make display
                                                             P-14

; set the maximum city width to 17

  MOV BL,17         ; load the desired city width
  MOV BH,0FFH       ; code signals that we load width only
  MOV AH,07CH       ; ZIPKEY function number ZK_RESTORE
  INT 179           ; set the city width


; save and restore ZIPKEY context

  MOV AH,07BH       ; ZIPKEY function number ZK_SAVE
  INT 179           ; fetch the ZIPKEY context
  PUSH BX           ; push the context onto our stack
  PUSH CX
  PUSH DX
; ....
  POP DX            ; pop the saved context
  POP CX
  POP BX
  MOV AH,07CH       ; ZIPKEY function number ZK_RESTORE
  INT 179           ; restore the ZIPKEY context


; popup a window as if the first hotkey were pressed

  MOV AH,07DH       ; ZIPKEY function number ZK_POPUP
  MOV BL,0          ; index for the first hotkey is zero
  INT 179           ; popup the ZIPKEY window


; insert keystrokes for zipcode 47401, as if alternate
;   exit key F1 had been pressed

  MOV DX,474        ; load the region number 474
  MOV CH,01         ; load the last two digits 01
  MOV BX,03B00      ; load the BIOS code for the F1 key
  MOV AH,077H       ; ZIPKEY function number ZK_ZIP_KEYS
  INT 179           ; insert keystrokes for 47401 entry


; toggle the activation of ZIPKEY's hotkeys

  MOV BL,3          ; control code for "toggle"
  MOV AH,07FH       ; ZIPKEY function number ZK_SWITCH
  INT 179           ; toggle the activation of hotkeys
                                                             P-15

; output to DI a list of sample cities for area code 512

  MOV BP,512      ; load the area code number
  MOV DI,08000H   ; load the output pointer
  MOV DX,BP       ; set DX to the area code
  CALL OUT_DX3    ; output the area code
  MOV BX,BP       ; copy the area code to BX, for ZK_AREA_ST
  MOV AH,080H     ; function code for ZK_AREA_ST
  INT 179         ; set AL to the state code for area code BX
  JC OUT_CRLF     ; exit if the area code did not exist
  MOV BL,AL       ; we have a state code: copy it to BL
  MOV AH,073H     ; function code for ZK_ST_NAME
  INT 179         ; output the name of this area code's state
  CALL OUT_CRLF   ; output a CRLF to complete the header line
  CMP DX,1000     ; was it a Canadian area code?
  JAE OUT_CRLF    ; skip if it was
L7:               ; loop here to check each zip region
  PUSH DI         ; save the output pointer
  ADD DI,6        ; advance pointer to the city-name column
  MOV AH,07EH     ; function code for ZK_REGION_CITY
  INT 179         ; output the city name for this region
  CMP BX,BP       ; does the city have the correct area code?
  MOV AX,DI       ; save the end-of-line output pointer
  POP DI          ; restore the start-of-line output pointer
  JNE L8          ; cancel the line if it wasn't our area code
  PUSH AX         ; we do have our area code: save end pointer
  MOV AX,'  '     ; load two blanks
  STOSW           ; output a two-blank indentation
  CALL OUT_DX3    ; output the area code
  POP DI          ; restore the end-of-line pointer
  CALL OUT_CRLF   ; output the terminating CRLF
L8:
  INC DX          ; advance DX to the next zip region
  LOOP L7         ; loop to check the next region
                                                             P-16

CITY_ST_CRLF:     ; subroutine for outputting a city line
  PUSH DX         ; preserve register across call
  MOV AH,076H     ; ZIPKEY function number ZK_ZIP_CITY
  INT 179         ; output the city name
  MOV DX,BX       ; copy the area code to DX, for output
  MOV BL,AL       ; copy the state code to BL
  MOV AL,' '      ; load blank
  STOSB           ; output an intervening blank
  MOV AH,072H     ; ZIPKEY function number ZK_ST_ABBR
  INT 179         ; fetch the state abbreviation
  STOSW           ; output the state abbreviation
  MOV AX,'( '     ; load a space and a left paren
  STOSW           ; output a space and a left paren
  CALL OUT_DX3    ; output the area code
  DEC DI          ; retract the trailing blank
  MOV AL,')'      ; load a right paren
  STOSB           ; output a right paren
  POP DX          ; restore clobbered register
OUT_CRLF:         ; subroutine for outputting line terminator
  MOV AX,0A0DH    ; load CRLF
  STOSW           ; output CRLF
  RET


OUT_DX3:            ; subroutine for outputting 3-digit DX
  PUSH CX           ; preserve register across call
  MOV CH,0FFH       ; CH code suppresses the final two digits
  MOV AH,074H       ; function code for ZK_ZIP_DIGITS
  INT 179           ; output the 3-digit code
  MOV AL,' '        ; load blank
  STOSB             ; output a trailing blank
  POP CX            ; restore clobbered register
  RET
                                                             P-17

OUT_ENTRIES:        ; subroutine for outputting a cities-range
  MOV BL,0          ; load the first entry number
L9:                 ; loop here to output each entry
  PUSH BX           ; save the entry number during output
  PUSH DI           ; save the starting output pointer
  ADD DI,5          ; advance beyond the zipcode we will output
  MOV AX,'  '       ; load blanks
  STOSW             ; blank out the trailing range slot
  STOSW             ; slot plus trailing blank are 4 characters
  POP DI            ; restore the pointer to the line start
  MOV AH,07AH       ; ZIPKEY function number ZK_CASE_ZIP
  INT 179           ; look up this entry
  MOV BL,AL         ; copy the state code to BL
  CMP CL,CH         ; is there only one zipcode?
  JE L10            ; skip if single zipcode: slot stays blank
  PUSH DI           ; it is a range: save output pointer
  ADD DI,3          ; advance the output pointer
  MOV AH,074H       ; ZIPKEY function number ZK_ZIP_DIGITS
  INT 179           ; output the end-value zipcode digits
  POP DI            ; restore starting output pointer
  ES MOV BYTE PTR [DI+5],'-' ; place the trailing hyphen
  MOV CH,CL         ; copy the start-value to CH
L10:
  MOV AH,074H       ; ZIPKEY function number ZK_ZIP_DIGITS
  INT 179           ; output the start-value zipcode digits
  ADD DI,4          ; advance beyond the end-value slot
  CALL CITY_ST_CRLF ; output the city, state, CRLF
  POP BX            ; restore the entry number BL
  INC BL            ; increment the entry number
  CMP BL,BH         ; have we exhausted the entries?
  JB L9             ; loop if not, to output the next entry
  RET

