
===============================
Writing Nifty List 3.4p Modules

David A. Lyons        17-Oct-91
===============================

This is sketchy documatation for writing your own modules.  Along with
the sample module, hopefully it's enough to get you started.


-------------------------------------------------------
Using Nifty List Services from Outside a Command Module
-------------------------------------------------------
Many Nifty List service calls can be made from outside of Nifty List
command modules.  But if you're not a module, you need a special way to
determine the Nifty List service address.  Here it is:

1.  Call MessageByName with
      createFlag=0
      input record = $1F "DAL Systems: Nifty List service"

2.  Pass the resulting Message type to MessageCenter, along
    with action=2 (get message) and a new handle you've
    created with NewHandle.  MessageCenter sizes and fills in
    your handle.  The address of the Nifty List service routine
    is at offset $28 in the handle (it's the only thing in
    there besides the system overhead and the name string).

Starting with Nifty List 3.3, there is another way to ask Nifty List
to do things if you are running under System 6:  Call SendRequest
by name to "DAL Systems~Nifty List~" with reqCode = $8000+nlXXXX,
dataIn = data value for the service, and dataOut = NIL if you don't
want a result, or a pointer to a 6-byte buffer if you do (first word
is the recvCount from SendRequest, and the other 4 bytes are the
service result).

------------------------
Command Module Structure
------------------------
A Nifty List command module has the same basic structure as an NDA, so
it should be possible to write Nifty List modules in common high-level
languages, as well as in assembly.  (Be careful if your compiler is trying
to be helpful by putting special glue code around some of the entry
points.)

The module's filetype must be $BC, and the auxiliary type must be $4001.
Modules have to be in the same directory at Nifty List.


------------------
DAOpen: @infoTable
------------------
The module's DAOpen routine returns pointer to the module's info table,
which has the following format.

*
* Info Record
*
InfoRec  dc.w InfoEnd-InfoRec       ;size of this Info record
         dc.w 0                     ;format (use 0)
         dc.w 0                     ;patch type (use 0)
         dc.l NLService             ;address to patch
         dc.w 12                    ;bytes per cmd in cmdTbl (use 12)
         dc.l cmdTbl                ;pointer to command table
InfoEnd

The command table looks like this:

*
* Command Table--for each command in this module:
*    +000:  pointer to command name (Pascal string)
*    +004:  address of command entry point
*    +008:  address of help routine
*
* (The first entry is for the module itself.)
*
cmdTbl   dc.l moduleName,0,HelpModule
         dc.l nameOne,cmdOne,helpOne
         dc.l nameShPurge,cmdShPurge,helpShPurge
         dc.l 0

For each command there's a pointer to the Pascal-string name, a
pointer to the command's entry point (which should RTL), and a
pointer to the command's Help routine (which should display help
and then RTL).


--------
DAAction
--------
Nifty List calls the DAAction routine with A equal to one of the
following.  X and Y are undefined.  You should return A=0 (the
return value may eventually be used to refuse to shut down, for
example, but right now you must always return 0 in A).

A = actBirth
    Called when Nifty List first loads a module

A = actDeath
    Called when Nifty List is about to remove a module

A = actEnterNL
    Called when the user enters the Nifty List command environment

A = actExitNL
    Called when the user leaves the Nifty List command environment


-------------------------
NLService(long,code):long
-------------------------
Nifty List patches over 4 bytes at the address indicated in the
module's info record.  The module should JSL *to* the patched location
to call a Nifty List service routine, with parameters on the stack.

Every service takes a four-byte input parameter (although some of the
services ignore part or all of this parameter).  Some services also
return a four-byte result.

To call a service with no result space:

    pha
    phx
    pea nlXXXXXX
    jsl NLService

The service removes the long input and the service code from the
stack before returning (just like a toolbox call would).

To call a service that needs result space:

    pha
    pha                    ;make room for result
    phx
    phy                    ;push 4-byte input
    pea nlXXXXXX
    jsl NLService
    ply
    plx                    ;pull 4-byte result


Here is a list of all the services.


*
* Environment management
*
0000 nlRecover()
         Does not return.  Enters Nifty List command level but BRKs when
         you leave.  For debugging when you've crashed and need a special
         way to go into Nifty List because you were already in a CDA.

0001 nlEnter():result
         Attempts to call Nifty List command level.  Returns 0 if
         successful.

0002 nlRemoveNL(@WordBuff):Handle
         Data = pointer to 2-byte result buffer (gets ID for UserShutDown),
         Result = handle to feed to RemoveCDA (NIL if can't remove)

0003 nlGetInfo(@buffer)
         Data = pointer to 256-byte buffer to receive table; first word =
                length in bytes, including the length count itself.
         Buffer:
           +000  TableSize   Word
           +002  nlVersion   Long
           +006  nlMemID     Word
           +008  nlBusyFlag  Word
           +010  CompactFlg  Word

0004 nlInstallHook(ref,@hook)
         [not implemented]
         This will allow modules to install routines for NL to call at
         certain times.

0005 nlRemoveHook(ref,@hook)
         [not implemented]

0006 nlGetDirectory():@dirname
         Returns pointer to class-1 string giving name of directory
         Nifty List is executing from.  (Don't change the string,
         just use it.)

0007 nlNewSession(@callBackProc):sessionRef
         Creates a Nifty List session and returns a long value
         distinguishing the new session from all other sessions.

0008 nlKillSession(sessionRef)
         Destroys a Nifty List session that was created with
         nlNewSession.

0009 nlSetSession(sessionRef):oldRef
         Makes the specified session the current one, returning
         the old session reference.  When you're done doing your
         stuff, call nlSetSession again to restore the old one.

         CallBack(LongIn,code)  (Pascal-style parameters)

         Code:
           0 = cbWrite = output (LongIn points to word-string)
           2 = cbWriteC = output (LongIn points to a C string)
           4 = cbFlush (output all the output, if you've been saving some)
           6 = cbGetKey (store a key at *LongIn (word) or don't--defaults
                 to the "continue" key)
           8 = cbChkAbort (store a $0001 at *LongIn to ask for abort)
          $A = cbAbort (abort, don't return!)
          $C = cbGetString (input into GS/OS result buffer at LongIn)

         CallBack is called with B and D undefined (must preserve).

000A nlWelcome(0)
         Outputs the Nifty List title screen.

000B nlLoadStuff(0):error
         Forces the data file to get loaded, if it isn't.
         Input parameter is 0.  Result is error code (0 if
         no problem).

000C nlGetTextState [NL 3.3]
         [For internal use for now.]

000D nlSetTextState [NL 3.3]
         [For internal use for now.]

*
* Information services
*
0010 nlGetFirstHandle(Kind):Handle
         Kind = 0, 1, or 2
         Result = first handle in one of the Memory Manager's 3 handle
                  chains (0=Used, 1=Purged, 2=Free)

0011 nlGetHandleInfo(@info)
         info:
           +000 = handle (Long)
           +004 = @buffer
         What's returned in buffer:
           +000 = count of bytes returned
           +002 = at least 20 bytes of stuff--a copy of the *current*
                  format of a Master Pointer record (ptr, attr, id,
                  size, previous, next)

0012 nlLookup(@stuff)
         Data = pointer to stuff:
           +000: Word    Section number to look in (nlSecSysTool, etc)
           +002: Long    Data to look up
           +006: Page    @outbuffer (256 bytes)
         Returns a pascal string in the output buffer (null string
         means nothing found)

0013 nlIndLookup(@stuff)
         Data = pointer to stuff:
           +000: Word    Section number to look in (nlSecSysTool, etc)
           +002: Long    Index (1=first item in section, etc)
           +006: Page    @outbuffer (256 bytes)
         Returned in outbuffer:
           +000: Long    Value associated with the Index-th piece of data
           +004: PString String associated with the Index-th piece of data
                    (null string if no data found)

0014 nlGetProcName(address/4):@procName [NL 3.1]
         Data = address.
         Result = address of Pascal string name associated with the address,
                  or NIL if none.

0015 nlClassifyAddr(address/4):result [NL 3.3]
         Data = address.
         Low word of result indicates the owner of the address:
           0 = System (for example, ROM or a RAM-based system tool set)
           1 = System-owned toolbox patch (within TSx)
           2 = non-system-owned RAM address
           3 = strange or invalid
         High word of result contains a corresponding ASCII character:
           0 = blank
           1 = "+"
           2 = "*"
           3 = "?"

*
* Utility
*
0020 nlScanHandles(@parms)
         parms:
           +000  Word   WhichList   (0=used handles, 1=purged, 2=free)
           +002  Ptr    BankValue   Any pointer to desired bank
           +006  Ptr    theProc     Procedure to call for each handle
                                    (gets parameter = Handle)

     theProc must remove the 4-byte parameter from the stack before
     returning.  When theProc gets control, the Bank register is
     already set to the bank specified by BankValue (byte +2).


0021 nlDisasm1(@code):@NewAddress [NL 3.2]
         Disassembles one line of code at the specified address, and returns
         a pointer to the byte just following the line disassembled.

0022 nlExecCmdLine(@cmdline):0
         Executes a specified command line (pointer to GS/OS
         string).  The return value is reserved & is currently
         always 0.

0023 nlGetRange(@buffer):NumParms
         Returns 2 if the user typed a range before your command;
         otherwise returns 1.

         Fills your 16-byte buffer with the following:
           +000  rangeStart
           +004  rangeEnd
           +008  rawStart
           +012  rawEnd

         This is for fetching the one or two hex numbers or addresses
         the user typed *before* your command.  They are already parsed
         by Nifty List.

         Use rangeStart and rangeEnd if you're looking for *addresses*.
         The bank byte is handled appropriately for you.

         Use rawStart and rawEnd if you're looking for *numbers*; no
         special bank handling is done for these values, so typing
         a 0 always gets you a 0, not a $xx0000.


0024 nlGetAGlobal(ref):value/4
         Data = reference number, value = long result

         Retrieves a value from a Nifty List global variable (the
         reference number values are in the equates file).

0025 nlSetAGlobal(@(ref,value))
         Data = ptr to a record:
            +000  reference word
            +002  long value

         Stores a value into a Nifty List global variable.
         ref = nlgNUM1: the number parsed before your command

0026 nlAbortToCmd(ignored)
         Aborts to the Nifty List command line, if possible
         [should return error if Nifty List not active--doesn't check yet]

*
* Input/Output
*
0030 nlWriteChar(char)
         Outputs a character--control characters are acted on.

0031 nlShowChar(char)
         Outputs a character, but nonprintable characters show up
         in a harmless way (like as periods).

0032 nlWriteStr(@pascalString)
         Outputs a Pascal string.

0033 nlShowStr(@pascalString)
         Outputs a Pascal string, but nonprintable characters show
         up in a harmless way.

0034 nlWriteCStr(@cString)
         Outputs a C string.

0035 nlShowCStr(@cString)
         Outputs a C string, but nonprintable characters show up in
         a harmless way.

0036 nlWriteText(@record)
         record+000 = length of text
         record+002 = pointer to text
         Writes the specified number of characters.

0037 nlShowText(@record)  [see above]
         Writes the specified characters, but nonprintable characters
         show up in a harmess way.

0038 nlWriteByte(byte)
         Outputs a byte in hex (2 characters).

0039 nlWriteWord(word)
         Outputs a word in hex (4 characters).

003A nlWritePtr(long)
         Outputs a pointer (xx/xxxx).

003B nlWriteLong(long)
         Outputs a long in hex (8 characters).

003C nlGetLn(...)
         [not implemented]

003D nlGetChar(dummy):char
         result = character (waits for one to be input)

003E nlCheckKey(dummy):result
         result, low word 0=no key pressed; nonzero=a key was pressed

003F nlCrout(dummy)
         Outputs a carriage return (begins a new line).  Does NOT
         return to the caller if the user hits Apple-period, etc!

0040 nlSpout(dummy)
         Outputs a blank.

0041 nlPause(dummy)
         Lets the user pause the screen, do screen dumps, etc.
         Normally returns right away.  Does not return if the
         user wants to abort.

0042 nlHandleInfo(handle)
         Displays address and owner information for a handle,
         in the same format as the "I" command.

0043 nlWriteNoVoice(@cString)
         Displays a C String if and only if the user did not set the
         "v" flag (avoids annoying decorative displays, like lines of
         dashes, which may be prounced "dash, dash, dash, dash...")

0044 nlShowWString(@wString)
         Displays a string that begins with a length word.  Nonprintable
         characters appear in a harmless way.
*
* Parsing
*
0050 nlChrGet():char
         Advances to the next character on the command line and
         returns is.

0051 nlChrGot():char
         Returns the command line character we're already on.

0052 nlEatBlanks():char
         Advances 0 or more times, until we're not sitting at a
         blank.  Returns like nlChrGot.

0054 nlEvalExpr(@buffer):actualSize
         buffer:
           +000  Word     MaxExprSize   maximum size of expr this buffer
                                        can hold (must be at least 4)
           +002  Word     ActExprSize   actual size of parsed expression
                                        (returned)
           +004  n Bytes  Expr          parsed expression (0 or more bytes,
                                        returned)

         The nlEvalExpr result is just a copy of the ActExprSize word
         returned in buffer.

         Note that calling nlEvalExpr is a simple way to let the user
         type a GS/OS pathname.  The expression, starting with the
         length word, is already a class-one GS/OS string.

*
* Memory access
*
0060 nlGetByte(@addr):byte
         Data = addr; value = byte found at that address

0061 nlGetWord(@addr):word
         Data = addr; value = word found at that address

0062 nlGetLong(@addr):long
         Data = addr; value = long found at that address


Reference numbers for globals:
         01 = nlgNUM1  (appropriate for getting an address-type value that NL parsed
                       before calling your command)
         02 = nlgADDR
         03 = nlgInfoTable (address of the table nlGetInfo uses; need to document
                            which fields are ok)

[end of Writing.Modules]
