Jump to content
IGNORED

CLI parser for MDOS


mizapf

Recommended Posts

While I'm working on some test programs, I thought this tool could be helpful for others, so I'll publish it here. It is an extension of a 10-year-old work contained in my XMODEM tool, and it serves to parse the command line arguments when you run a program in MDOS.

 

So this allows you to process the arguments in a line like

 

testprog /n1000 /sMichael /i-10 hello

 

It is also contained in the attached image.

 

The license is the 2-clause BSD license, which basically says you should mention me as the author when you use it in your own distributed software.

 

*****************************************************************
*
*   Command line parser for MDOS
*
*   Boolean: /x is present, or not
*   Integer: /x<ws>*[<sign>]<value> delivers the signed value
*   String:  /x<ws>*<string> delivers the null-terminated string
*   Hex:     /x<ws>*<hexstring> delivers the value of the hexadecimally 
*                            interpreted last four characters of the hexstring
*   Direct:  <ws>*<string> delivers the null-terminated string
*          (Argument without /x)
*
*   No escaping or quoting; the arguments must not contain whitespace, 
*   slashes, or NUL
*
*   <ws>* = 0 or more whitespaces (space or tab)
*
*   Example:
*
*        LI   R1,LINE       Buffer where the argument string is copied to
*        BLWP @CLILIN
*        JEQ  ARGSF
*        LI   R1,NOARG      Error message, "missing arguments"
*        JMP  ERR
*
*  ARGSF MOV  R1,R0         LINE address now in R0 
*        LI   R1,'s '       Line contains a /s1000 (or /s 1000)
*        LI   R2,INT        Integer
*        BLWP @CLIPRS
*        JEQ  $+6           Yes, R3=1000, skip next line
*        LI   R3,42         If not set, use 42 as a default
*
*   Version 1.0
*
*   Copyright 2022, Michael Zapf
*
*   Redistribution and use in source and binary forms, with or without 
*   modification, are permitted provided that the following conditions are met:
*
*   - Redistributions of source code must retain the above copyright notice, 
*     this list of conditions and the following disclaimer.
*   - Redistributions in binary form must reproduce the above copyright notice, 
*     this list of conditions and the following disclaimer in the documentation 
*     and/or other materials provided with the distribution.
*
*   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
*   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
*   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
*   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
*   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
*   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
*   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
*   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
*   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
*   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
*   EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************

       DEF  CLIPRS,CLILIN

BOOL   EQU  1
INT    EQU  2
STRING EQU  3
HEX    EQU  4

DIRECT EQU  0

WS     BSS  32
SLASH  BYTE '/'
SPACE  BYTE ' '
H2000  DATA >2000
TAB    BYTE >07
H2B    BYTE >2B
H2D    BYTE >2D
H30    BYTE >30
H37    BYTE >37
H39    BYTE >39
H41    BYTE >41
H46    BYTE >46
TEN    DATA 10

* Get arguments from CLI

* Caller:
* LI   R1,BUFFER
* BLWP @CLILIN
* JEQ  GOTIT
* Buffer will be null-terminated

CLWS   BSS  32
CLILIN DATA CLWS,$+2
       MOV  @>0128,R0
       JNE  ST1
       SZC  @H2000,R15
       RTWP

ST1    CLR  R2
       MOV  @2(R13),R3
       MOV  *R0+,R1
       LI   R5,6
       MOVB *R0+,R2
       DEC  R5
       SRL  R2,8
GL1    MOVB *R0+,*R3+
       DEC  R2
       JEQ  GOTALL
       DEC  R5
       JNE  GL1
       MOV  R1,R0
       JEQ  GOTALL
       MOV  *R0+,R1
       LI   R5,6
       JMP  GL1
GOTALL CLR  R1
       MOVB R1,*R3
       SOC  @H2000,R15
       RTWP

* Originally, the parser was planned to accept an argument group (arggroup)
* in this way: /n /m /i = /nmi
* Also, the value of a valued parameter should be appended directly
* like this: /c 1000 = /c1000
* This may be ambiguous, however, when n requires a value and m and i 
* are boolean switches: /nmichael  
* The parser cannot tell whether "i" is the flag or part of the value of n
* unless we inform it that n is a valued parameter

* Available data types
FUNC   DATA GBOOL,GINT,GSTR,GHEX

CLIPRS DATA WS,$+2
       MOV  *R13,R0
       MOV  @2(R13),R1
       MOV  @4(R13),R2
       CLR  R5           Mode: /... !=0 , else 0
       CI   R2,DIRECT
       JNE  GLOOP        different parsing for direct
       B    @GFD
GLOOP  MOVB *R0+,R4
       JNE  GLP0
       SZC  @H2000,R15   End reached, not found
       RTWP
GLP0   CB   R4,@SLASH
       JNE  GLP1
       SETO R5           Set slash mode on
       JMP  GLOOP
GLP1   CB   R4,R1
       JEQ  FOUND
       CI   R1,>6000
       JH   GLP3
GLP2   CLR  R5           Reset slash mode
       JMP  GLOOP
GLP3   AI   R4,>2000
       CB   R4,R1
       JEQ  FOUND
       JMP  GLP2
       
FOUND  CI   R2,HEX
       JLE  F1
       SZC  @H2000,R15
       RTWP   
F1     DEC  R2
       SLA  R2,1
       MOV  @FUNC(R2),R3
       B    *R3

* Check for whitespace
* Argument in R10 HB
* Returns EQ

WSPACE CB   R10,@SPACE
       JEQ  WEND
       CB   R10,@TAB
WEND   RT
       
* --------------------------
* Check by data type
* --------------------------

* ----- Boolean ------
* When it's found, it is set

GBOOL  SOC  @H2000,R15   Then we have no value; set EQ
       RTWP

* ----- Integer -------
* Added +/-

* INT: skip ws, parse until next ws
GINT   CLR  R6           Flag: !=0 <=> in number
       CLR  R7           Sign present (0=no, -1=yes)
       CLR  R8           Value (also R9)
       CLR  R1           Sign (0=+, -1=-)
GI1    MOVB *R0+,R4
       JEQ  GIEND        Reached end of string
       MOVB R4,R10
       BL   @WSPACE
       JNE  GI2
* ws is legal if we have a number, or if we neither have a number nor a sign
       MOV  R6,R6        Do we have a number?
       JNE  GI5          Yes, we are done
       MOV  R7,R7        No number, but a sign?
       JEQ  GI1          No, then continue
       JMP  GERR         Yes, then this is an error       
       
GI2    CB   R4,@H2B      Plus
       JEQ  GI3
       CB   R4,@H2D      Minus
       JNE  GI4
       SETO R1           Set minus flag
GI3    MOV  R7,R7        Do we have a sign already?
       JNE  GERR         Yes, then this is an error
       SETO R7           Now we have a sign
       JMP  GI1

GI4    CB   R4,@H30
       JL   GERR         invalid character
       CB   R4,@H39
       JH   GERR         invalid character
       SETO R6           Valid digit
       SB   @H30,R4
       SRL  R4,8
       MPY  @TEN,R8      R8 is our current number
       A    R4,R9        R8/9 = R8 * 10 + R4
       MOV  R9,R8        Value in R8
       JMP  GI1
       
GIEND  MOV  R6,R6        Reached end; do we have a number?
       JEQ  GERR         no, so that is an error
GI5    MOV  R1,R1        Check sign
       JEQ  GI6
       NEG  R8
GI6    MOV  R8,@6(R13)   Number in R8 goes to caller's R3
       SOC  @H2000,R15   was OK
       RTWP
GERR   SZC  @H2000,R15
       RTWP

* --- String -----------------
* Skip whitespace before;
* ends before first whitespace behind

GSTR   MOV  @6(R13),R5
       CLR  R6
GS1    MOVB *R0+,R4
       JEQ  GS3
       MOVB R4,R10
       BL   @WSPACE
       JNE  GS2
       MOV  R6,R6
       JEQ  GS1
       JMP  GS4
GS2    SETO R6
       MOVB R4,*R5+
       JMP  GS1

GS3    MOV  R6,R6
       JEQ  GS5
GS4    CLR  R0
       MOVB R0,*R5+      Null termination
       SOC  @H2000,R15
       RTWP
GS5    SZC  @H2000,R15
       RTWP

* ------ Hex value -------
* If there are more than four digits, the leftmost digits are ignored
* 12ABC5 = ABC5

GHEX   CLR  R6           Flag: !=0 <=> in number
       CLR  R8
GH1    MOVB *R0+,R4
       JEQ  GH2          Reached end of string
       MOVB R4,R10
       BL   @WSPACE
       JEQ  GH3
       BL   @HEXDIG
       MOVB R10,R10
       JLT  GERR
       SETO R6
       SLA  R8,4
       A    R10,R8
       JMP  GH1
GH2    MOV  R6,R6        Reached end; do we have a number?
       JEQ  GERR         no, so that is an error
GH3    MOV  R6,R6        Whitespace; do we have a number?
       JEQ  GH1          no, so continue skipping over WS
       MOV  R8,@6(R13)   Number in R8 goes to caller's R3
       SOC  @H2000,R15   was OK
       RTWP
       
* Delivers the value of the hex digit in R10=000x, or FFFF if it is 
* not a hex digit
HEXDIG CB   R10,@H30
       JL   HEXD4
       CB   R10,@H39
       JH   HEXD1
       SB   @H30,R10     >34 - >30 = >04
       JMP  HEXD3
HEXD1  CI   R10,>6000  
       JL   HEXD2
       AI   R10,>E000    Make uppercase
HEXD2  CB   R10,@H41
       JL   HEXD4
       CB   R10,@H46
       JH   HEXD4
       SB   @H37,R10     >41 - >37 = >0A
HEXD3  SRL  R10,8
       RT
HEXD4  SETO R10
       RT
       
* ----- Direct argument -------
* has a value only; no name
* R1 points to list of non-boolean flags
* R3 is the target buffer (gets null termination)

* ...yyyy.../x...
* yyyy is the value
* .../x...yyyy..zzzz
* If x is non-boolean, zzzz is the value
* If x is boolean, yyyy is the value

GFD    CLR  R5           Slash mode
GFD0   MOVB *R0+,R4      EOL
       JEQ  GFD7
       MOV  R5,R5        Are we in slash mode
       JNE  GFD4
       MOVB R4,R10       not in value
       BL   @WSPACE
       JEQ  GFD0         skip WS
       CB   R4,@SLASH    No WS; it is a slash?
       JNE  GFD3         Not a slash, not in value, not a whitespace
       SETO R5           Enter slash mode
       JMP  GFD0

* Next character after slash in R4; check list
* If the character is in the list, we have a valued parameter
* If not, the option is boolean; continue searching

GFD4   MOV  R1,R3        List of non-boolean options
GFD41  MOVB *R3+,R6
       JEQ  GFD          End of list; continue searching
       CB   R6,R4
       JEQ  GFD6         found
       CI   R4,>6000     
       JH   GFD41        char is lowercase and does not match
       AI   R4,>2000     try to make it lowercase
       CB   R6,R4        does it match?
       JNE  GFD41

* Found the character in the list
* If the next character is WS, skip all WS, then find the next WS
* If not, find the next WS
* Continue from above

GFD6   MOVB *R0+,R10     EOL
       JEQ  GFD7
       BL   @WSPACE
       JNE  GFD63
GFD62  MOVB *R0+,R10     Skip all WS
       JEQ  GFD7
       BL   @WSPACE
       JEQ  GFD62
GFD63  MOVB *R0+,R10     Find the next WS
       JEQ  GFD7
       BL   @WSPACE
       JNE  GFD63  
       JMP  GFD

* Reached EOL before the option value was over, so there is no direct argument
GFD7   SZC  @H2000,R15   Reached EOL; not successful
       RTWP

* Copy everything up to the next WS or EOL       
GFD3   MOV  @6(R13),R1
       DEC  R0
GFD9   MOVB *R0+,R10
       JEQ  GFD10
       BL   @WSPACE
       JEQ  GFD10
       MOVB R10,*R1+
       JMP  GFD9
       
GFD10  CLR  R10
       MOVB R10,*R1+     Terminator
       SOC  @H2000,R15   Got value
       RTWP      
       
       END

 

cliparse.dsk

  • Like 9
Link to comment
Share on other sites

Thanks for the code. It's rare we get new well documented GeneveOS code. Command line parsing always seems like it's a trivial exercise, until you try it! 😃
 
I was looking at compiling (TIC) some Clint code but it was missing getargs_h - which isn't to be found. Of course, no telling if, at this point, all of his compiling was being done with TIC on another platform.
 
image.png.a1a204fa78fa82cc63cd358e52702e17.png
  • Like 3
Link to comment
Share on other sites

I believe such a command line parser is a crucial requirement for writing programs for MDOS. Otherwise, we stay in the usual program design of the TI-99/4A, that is, design a user interface to input values during runtime.

 

(Terminology: By MDOS I refer to the "shell" of the GeneveOS, i.e. the blue screen with white letters where you can load and run programs or commands.)

  • Like 2
Link to comment
Share on other sites

4 hours ago, dhe said:
Thanks for the code. It's rare we get new well documented GeneveOS code. Command line parsing always seems like it's a trivial exercise, until you try it! 😃
 
I was looking at compiling (TIC) some Clint code but it was missing getargs_h - which isn't to be found. Of course, no telling if, at this point, all of his compiling was being done with TIC on another platform.
 
 

Have you looked for getargs_h in any of the TIC files within the 9640News diskettes?  Just in the event the file was in there.

  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...