		    ZSM 3.2, a CP/M Z80 assembler
		-------------------------------------

| Changes from version 2.8 documentation have a 'change bar'
| <<< here.

;! ZSM 3.0 is similar to the earlier versions of ZSM (including
2.9 T) but the error checking has been made more rigorous. I
have used the mark ';!' to indicate the changes from the
previous version 2.9 T.   C.G.Maxfield.

% Comments for version 3.1 have a '%'
% <<< here (what a mess!).

ZSM is an assembler for the Z80 microprocessor.  It uses standard
Zilog/Mostek mnemonics, supports conditional assembly and
produces an Intel HEX file suitable for loading with the CP/M LOAD
utility.  ZSM is a disk based assembler, i.e. source code is read
from disk, the resulting object code is written to disk and an
optional print file can be written to disk.  Alternatively the
print file can be redirected to the console.



1. Using the assembler.
-----------------------

The assembler is initiated by typing:

          ZSM filename.pqr

This takes the file 'filename.ZSM' and assembles it according to
the parameters 'p','q' and 'r' and the options chosen.  The
parameters must be selected as follows:

     p   - the disk drive containing the source code,
           'filename.ZSM'. A to P or @ for default drive.

     q  - the disk drive where the object code, 'filename.HEX' is
          to be written.  A to P or @ for default drive, Z if not
          required.

     r   - the disk drive where the listing is to be written,
          'filename.PRN'.  A to O or @ for default drive, P to
          the list device, X to the console, Y to the console
          with errors to the printer, Z to suppress listing.

For example assembling the file TEST.ZSM:-

     ZSM TEST.AAB

This expects the file TEST.Z80 on drive A, will write the object
code file, TEST.HEX to drive A and the listing file, TEST.PRN to
drive B.

| 2.9  If no filename is found or there is an error, the user
|     will be reminded of all these conventions.

| 2.9  Now prints out a message at the start of each pass & sort
|     of symbols.

| 2.9  The default origin is now 0100h, not 0000h.

| 2.9  Drive O: now works for .prn file.



2. Assembler syntax
-------------------

2.1 ASSEMBLER MNEMONICS

The assembler recognises all standard Z80 mnemonics as defined in
the Z80 CPU Technical Manual.  The following mnemonics are
recognised in both forms shown.

     ADC A,s        ADC s
     ADD A,s        ADD s
     SBC A,s        SBC s

Where 's' can be a single register, (HL), (IX+d), (IY+d) or n.
Where 'n' is a constant, and 'd' is a displacement.

;! Earlier versions of ZSM (i. e. before ver 3.0) accepted AND A,s
CP A,s  OR A,s  SUB A,s and XOR A,s as valid instructions. As these
are non-standard, they now generate a syntax error.


2.2 LABELS

Labels may be up to 11 characters long.  The first character of
the label must be alphabetic (i.e.  A-Z) or '_' or '$'.  A colon,
':' following the label is optional, both for labels and equates.
| 2.9	allows '%' in labels
% 3.1	disabled '%' in labels and
%   	enabled '_' and '$' as the first character in labels (as
%    	stated in the doc. some versions ago!)

2.3 CONSTANTS

ZSM allows binary, octal, decimal, hexadecimal and ASCII
constants according to the following conventions:

Binary - number formed from binary digits (0,1) and terminated by
the character 'B'. Range 0000000000000000B to 1111111111111111B.

Example:       LD HL,10001111101101B

Octal - number formed from octal digits (0-7) and terminated by
the character 'Q'. Range 000000Q to 177777Q.

Example:       LD HL,23670Q

Decimal - number formed from decimal digits (0-9) and EITHER left
unterminated or terminated by the character 'D'.
Range: 0 to 65535.

Example:       LD HL,32145

Hexadecimal - number formed from hexadecimal digits (0-9 and A-F)
and terminated by the character 'H'.  A hex number beginning with
a letter must be preceded by a 0 to distinguish it from a label
or register name.  Range 0000H to FFFFH.

Example:       LD HL,0A3FH

ASCII  - number formed from ASCII value of characters enclosed in
single quotes.  Range  ' ' to '~' or 20H to 7EH including all
alphanumerics and punctuation.
| Use "''" to include a "'" in a string

Examples:      LD HL,'Wk'
               LD A,'*'

The $ sign used as a constant represents the current value of the
program counter.
| 2.9  Using '$' in long DB / DWs now works properly, even when
|     subtracting (or multiplying etc) it...


2.4 EXPRESSION EVALUATION

Operands in instructions and pseudo-ops may consist of arithmetic
expressions which are evaluated at assembly time and the
calculated value used as part of the object code.  The following
operators may be used to form expressions:

     +              Addition
     -              Subtraction (or unary Negation)
     *              Multiplication
     /              Division
     .MOD.          Modulus
|    \              Alternate modulus for 2.9  --  2.8 uses %
%    %              Version 3.1 can use % for modulus too.
|    > or .GT.      Greater Than. X.GT.Y true if X greater than Y.
     .GE.           Greater than or Equal.  X.GE.Y true if X
                    greater than or equal to Y.
|    < or .LT.      Less Than. X.LT.Y true if X less than Y.
|    .LESS.	    Signed Less than -- others are unsigned  (2.9)
     .LE.           Less than or Equal.  X.LE.Y true if X less
                    than or equal to Y.
     = or .EQ.      Equal. X.EQ.Y true if X equals Y.
     .NE.           Not Equal. X.NE.Y true if X not equal to Y.
|    .SHL. n        Shift Left n (unsigned) bits.
|    .SHR. n        Shift Right n (unsigned) bits.
     .NOT.          Logical NOT (unary)
     .AND.          Logical AND
     .OR.           Logical OR
     .XOR.          Logical Exclusive OR
     .LOW.          Low byte of expression (.MOD. 256)
     .HIGH.         High byte of expression (.SHR. 8)

Expressions are evaluated left to right with no precedence.  No
parentheses can be used in expressions so some care is required
to ensure that  expressions are correctly formed.

;! These conventions now also apply to the Unary expressions .NOT.
.LOW. and .HIGH. Versions of ZSM earlier than ver 3.0 made
exceptions of these operators. If they are used in your favourite
program, these operators will have to be changed.

;! However it is now possible to use instructions like:-

	LD	A,LABEL .HIGH.
;! The old version:-
	LD 	A,.HIGH. LABEL

;! used to result in an error, rather than loading A with the high
byte of the 16 bit value given to LABEL.


;! I regret that, in general, negative numbers must still be at the
start of an evaluation string. This is because two consecutive 
arithmetic operators is meaningless and therefore an error. For
instance:-

A + * B is ambiguous. Should A be added to B or Multiplied by it?

Hence A * -B is also ambiguous. Use -B * A



2.5 PSEUDO-OPCODES

DB or DEFB (define byte)
The DB pseudo-op tells the assembler to reserve a byte or string
of bytes with specific values.  The bytes may be defined as
constants, expressions as described earlier or as already defined
symbols.

<label>:  DB   <item or list of items>

For example:

          DB   3,-5,34+LABEL,'A'


DS or DEFS (define storage)
This tells the assembler to reserve a specified number of bytes
for storage.  Note that the data stored in the bytes is not
defined.


DW or DEFW (define word)
This is exactly the same as the DB Pseudo-op except that the
data is stored in words (2 bytes each) rather than bytes.


END (end of assembly)
This pseudo-op is used to terminate assembly of a block of source
code.


ENDIF (end of IF definition)
Used to terminate a block of conditionally assembled code.  See IF
Pseudo-op for further details.


EQU (equate)
The EQU pseudo-op assigns a value to a label or symbol.  The
format is:

     label:    EQU  <item>

Label is the name of the symbol and item is any of:  a  constant,
an  address,  a label or an expression.  Once the value of the
symbol is assigned it is in effect for the entire source program.


FORM (form feed)
Forces a new page in the listing.
| also PAGE and EJECT with the same effect.
| 2.9  If the Z80 CPU version running on a CP/M+ machine, the
|     time and date is added to the page heading.  If the 'CP/M
|     date' hasn't been set, only the time is printed.
| 2.9  If the .prn output is going to the console, no headers are
|     generated.


| TITLE
| Allows the user to add a descriptive line to each page heading.
| Forces a new page. A blank title is allowed, only the source file
  name and page number will be included.


|	TITLE <A line of description!>


IF (begin conditional assembly)
The IF, ELSE and ENDIF pseudo-ops define a sequence of assembly
language statements which are to be included or excluded during
the assembly.  The form is:

          IF   <expression>
          statement #1
          statement #2
             .....
          statement #n
         [ELSE
             .....
             .....    ]
          ENDIF

Conditionals may be nested up to n levels deep.  Values used in
the expression must be defined before the IF statement.  When the
assembler finds an IF pseudo-op the expression is evaluated.  If
the expression evaluates to a logical TRUE (non-zero) then the
statements following, up to the ENDIF are included in the
assembly.  The optional ELSE pseudo-op allows alternate code to be
generated when the opposite condition exists.  An ELSE is always
related to the most recent active IF.

| n levels = at least eight (now an equate in zsm.zsm -- 'maxif')

     IF   <expr1>  -----------+
                              |
     IF   <expr2>  ------+    |
                         |    |
     ELSE ---------------+    |
                         |    |
     ENDIF --------------+    |
                              |
     IF   <expr3> -------+    |
                         |    |
     ENDIF --------------+    |
                              |
     ENDIF -------------------+


LIST (use following options in listing)
This is used to control what parts of the assembly listing get
sent to the listing file.  The word LIST is followed by a string
of option words separated by commas.
| 2.9  Multiple list options now work!
| 2.9  The listing starts using the default values -- 2.8 used
|     those in use at the end of pass 1.

     LIST OFF
     Suppresses the listing until a LIST ON command.

     LIST ON (default)
     Turns on the listing file after a LIST OFF statement.

     LIST COND (default)
     Forces generation and printing of all blocks of code which
     are part of conditional assemblies whether they are
     assembled or not.

     LIST NOCOND
     Suppresses listing of IF, ELSE and ENDIF statements and any
     conditionally assembled code which is NOT included in the
     assembly,  i.e. only the code actually assembled is shown in
     the listing.

     LIST SYMBOL (default)
     Generate a symbol table at the end of the listing file.

     LIST NOSYMBOL
     Suppress printing of a symbol table at the end of the
     listing file.

     LIST TABS (default)
     Tab characters (ASCII 9) are to be used in the listing file.

     LIST NOTABS
     Do not use tabs in the listing file.

| 2.9   LIST SORT (default)
|       Sort any symbol table produced.

| 2.9   LIST NOSORT
|       Order of symbols in table is their order in the source file.
|	This is much faster for large tables...


| 2.9 also has:
| ASSERT (test all is well at assembly time)
| This is used to check for illogical conditions at assembly time
| such as program sections too long, silly equates etc.

|	ASSERT <expression>

| If the expression evaluates to false (zero), an error results.
| The result is only used during pass 2, so forward references are
| safe!

;! Please see Appendix A for further details of Ian Watter's
ASSERT pseudo-op. C.G.M.


2.6 COMMENTS
Remarks or comments are free format including any printable ASCII
character as long as the comment is preceded by a semicolon, ';'.
The comment may follow an opcode, operand or label or may exist
on a line by itself.



3. Error Messages

ZSM generates a number of error messages while assembling to
inform you of its progress. They fall into two categories: errors
found while setting up files for assembly operation and those
encountered while assembling the file.  The first type of error
message will be one of the following:

     Source (.ZSM) file not found.
     Unable to create/open object (.HEX) file.
     Unable to create/open output (.PRN) file.

The first message is generated when the filename specified in the
command line cannot be found on the selected  disk.  The other
messages occur while the output (HEX and PRN) files are being set
up, usually the disk or directory is full.  These errors are sent
only to the console and the assembly is aborted.
| 2.9  Some help is also given to get it right next time.

The following errors can occur during the assembly process.  The
offending source line is displayed on the console, followed by
the error message.  If a listing file has been selected the error
message appears in the listing on the line following the error.
Errors are displayed even if the listing has been disabled using
the 'LIST OFF' pseudo-op.

     "Too many IF statements"
     Only 8 levels of nested IF statements are allowed now

     "ENDIF without matching IF statement"
     IF and ENDIF should always be paired to indicate the start
     and end of a section of conditional code.

     "ELSE without matching IF statement"
     The ELSE pseudo-op should only appear within a section of
     conditional code between IF and ENDIF.

     "Relative jump range error"
     The address specified in a relative jump instruction is out
     of the range -128 to +127.

     "Expression error"
     This error is caused by a badly formed expression.  Note that
     brackets are not allowed in expressions.

     "Illegal opcode"
     The assembler cannot recognise the instruction.

     "Multiple definition"
     The symbol has already been defined.

     "Syntax error"
     This error message covers a multitude of sins.  It is usually
     caused by misspelling or omission of a delimiter such as a
     comma or semicolon.

     "Undefined symbol"
     The  symbol  used in the instruction or expression  has  not
     been defined.

     "Value error"
     This error appears when the value of a symbol or  expression
     exceeds  the range allowable.  For example an index register
     displacement value must be in the range +127 to -128.
     "Symbol Table Overflow"

     "Divide by Zero"
     The expression being evaluated contains a division by zero.

| 2.9  "Label Error"
|      The label is malformed or in the wrong place etc.

| 2.9  "Assertion false"
|       The expression in an ASSERT test evaluated to false (zero).



Other version 2.9 notes
-----------------------

Many thanks to those who have gone before me, I hope y'all think what
I have done is an improvement!  There's still LOTS that could be done,
but I leave that to you, dear reader.

For a start, the symbol table really ought to be in a tree format or
hashed for faster access.  The bubble sort routine that sorts it just
has to be put out of its (and your) misery soon.  A proper expression
analyser (none of this just left to right stuff) would be nice, as
would macros.  The code generation section can be made to go much
faster too.

In fact, the whole thing could and should be re-written.  More likely,
you will stomp on the couple of creepy crawlies mentioned in the test
file.  Then you'll think, "Hmmm, THAT bit of code needs changing."
Then another bit, then another bit, then another...

I've enjoyed touching ZSM up.  Hope you do too.

Good luck!

            Ian


Changes made in version 3.1
---------------------------

   - Removed feature level for ZSM version.
   - Removed date/time code.
   - Enabled '_' and '$' as the first character in labels (as stated
     in the doc. some versions ago!).
   - Modulus can be '%' too, as in previous versions.
   - Disabled '%' as valid character for labels.
   - Modified usage explanation.

By Miguel Garcia / FloppySoftware - 30 Nov 2016.

http://www.floppysoftware.es
floppysoftware@gmail.com


Changes made in version 3.2
---------------------------

   - Solved bug in rotate op-codes (rl r, rlc r, rr r, rrc r, sla r,
     sra r, srl r): op-codes with 'c' register were translated to
     machine code as if the 'e' register was used instead. See S24
     section.
   - Added patch from Ed, to restore support for date/time in titles
     in CP/M 3: "This patched version restores date/time code as
     assembly-time option (see DATE EQU). Now works with 8080 or Z80.".

By Miguel Garcia / FloppySoftware - 01 Jan 2017.

http://www.floppysoftware.es
floppysoftware@gmail.com


APPENDIX A.

A few further words about Ian Watter's ASSERT pseudo-op.

It is best thought of as an 'alarm contact' that can be used by the
programmer for any purpose that comes to mind. It's use is best
illustrated by the example below that has been extracted from the
source code for ZSM 2.9 T :-


;
endboot:	ds 1
;
INBUF:	equ 100h		;.ZSM INPUT BUFFER
OUTBUF:	equ inbuf+buflen	;.PRN OUTPUT BUFFER
OUTBF2:	equ outbuf+seclen	;.HEX OUTPUT BUFFER
;
	ORG outbf2+seclen	;end of I/O BUFFER SPACE
;
	ASSERT $ > endboot	; check boot-up code not too big
;



It will be noted that the boot-up code preceds the example shown
above, and that the base address for the main code that follows the
example, is set by the ORG statement.

If additions are made to the boot code during program development,
so that the address of 'endboot:' is now greater than that set by
the ORG statement, ASSERT will generate an error during assembly;
thus warning the programmer that the the start of the main code has
been overwritten, and adjustments will have to be made.

By giving it a suitable expression to test, ASSERT can be used to
check for other conditions, during assembly. The essential point to
remember is, the expression must evaluate to zero to cause an alarm.
In the example above, the 'greater than' symbol serves this
function. 

It is often necessary to determine the current starting address of a
particular routine, so that it can be de-bugged. Placing an ASSERT
pseudo-op, with no expression following it, at the start of the
routine in question, will cause a 'benign error' at this point, and
so reveal its address during assembly.


C.G.M. March 1991.
e in question, will cause a 'benign error' at this point, and
so reveal its address during assemb