;==============================================================
;                 HEX2IMG by Martin Eberhard
;                      a PTDOS Utility
;
; Creates a PTDOS Image file from an Intel hex file
;
; Usage: (NOTE: ASM does not allow exclamation points even
;         in comments, so we use "bang" here to mean one.)
;
;  HEX2IMG ?
;
; prints a help screen
;
;  HEX2IMG <ifile>,<ofile>[,bang<bsize>][,T=<type>][,E=<hhhh>]
;
; Block size:
;
; <bsize> sets the disk block size for <ofile>. If absent then
; HEX2IMG will figure out the best block size, though never
; larger than MAXBLK.
;
; File Type:
;
; <outfile>'s file type is set to this ASCII character. 'D'
; causes the file to be a device driver. If absent, the file
; type will be the default Image file type, '*' (which
; appears as 'I*' in the PTDOS directory).
;
; Execution address:
;  E=N      forces the Image file to be non-executable (meaning
;           that it does not have an execution address at its
;           end).
;  E=<hhhh> forces the Image file to be executable, with the
;           provided hex address installed at the end of the
;           Image file.
;  If no E= option is present, then the Image file will be
;  executable, with the execution address equal to the first
;  address of <infile> - unless an execution address record
;  (Type 01) is at the end of the file, and its address is
;  not zero. In this case, the execution address is taken from
;  the Type 01 record.
;
; Note: this program works with any device driver that does
; not use user memory (below PTDOS). For example:
;
;   HEX2IMG XMODEM,MYFILE
;
; ..will receive an Intel hex file via the serial port, using
; the Xmodem protocol. It will produce an executable image
; file on disk, called MYFILE. (The XMODEM device driver runs
; in high memory, above the Sol-20's video memory.)
;==============================================================
; Revision History
;
; 1.00  23 February 2015  M.Eberhard
;   Created
; 1.01  24 Feb 2015 M. Eberhard
;   'T=D' will create a device driver file. Improved command
;   parsing. Speed up inner hex-load loop.
; 1.02  25 Feb 2015  M.Eberhard
;   Flush some chrs from the end of the input file if we have
;   not seen the EOF, to make device drivers happy
; 1.03  2 March 2015  M. Eberhard
;   Improved PTDOS error reporting
; 1.04  2 May 2015  M. Eberhard
;   Move code to 100h, so DR's LOAD can create a binary file.
;   Put stack and some variables into page 0 to save space.
; 1.05  12 May 2015  M. Eberhard
;   Initialize DELIM variable to fix bug
; 1.06  5 June 2015  M. Eberhard
;   Fix bug calculating ideal block size
; 1.07 19 June 2015  M. Eberhard
;   Better algorithm for calculating "ideal" block size.
;   Improved error reporting for PTDOS errors.
;
; To do (maybe)
;   Error on illegal record type
;   Error on >0 byte count for type 01 record
;   Option to allow file to stay open after loading (page 5-8)
;==============================================================
; NOTES
;
; Written to assemble with Digital Research's ASM. The origin
; of this program is 100h (rather than 0), to avoid an
; "INVERTED LOAD ADDRESS" error when using Digital Research's
; LOAD to create an executable binary file. (CP/M Operating
; System Manual page I-8)
;
; Page references in the comments refer to the PTDOS ver. 1.5
; User's Manual unless they state otherwise.
;
; HEX2IMG reads the entire input file before writing the
; output file. (The Intel hex file is converted to binary as
; it is put into the memory buffer, to make it compact.) This
; is so that it will work when <ifile> is actually a device
; driver (such as SERIAL) that does not have flow control
; capability. If the entire file, once converted to binary,
; does not fit in the memory buffer, then an error message
; will be printed and the conversion will abort.
;
; The data buffer overlays all of the code that is not needed
; once it is time to read from the input file. This frees all
; but about 700 bytes of user memory (below PTDOS) for for
; buffer space.
;
; HEX2IMG assumes the first hex record has the lowest
; address and that the last address has the highest address.
; Unless an execution address is specified by the user or
; specified by a non-zero address in the terminating Type 01
; hex record, the address of the first hex record address is
; also assumed to be the execution address. Any gaps between
; hex records will be filled with garbage bytes in the output
; file.
;
; Intel Hex Format
;
; (After the colon, each character is an ASCII hex digit.)
;
; :CCAAAATT<DATA>SS
;
;    : = (ASCII colon) Start-of-record marker
;   CC = Total number of data bytes
; AAAA = Memory load address for the 1st record data byte.
;        Subsequent data bytes in the record are written to
;        sequential memory addresses.
;   TT = Record type. The following record types are supported:
;         00 = Normal data record
;            Note: a type 00 record with no data (CC=00) also
;            serves as an end-of-file record.
;         01 = End-of-file record. (CC must be 00.)
;            Note: AAAA is assumed to be the execution address
;            for the file unless it is 0000.
;<DATA>= CC data bytes, 2 hex digits each.
;   SS = Record checksum. When all hex bytes of the record
;        (including this checksum) are added together, the
;        result will be 00. Speed up inner hex loop.  
;==============================================================
FALSE	equ	0
TRUE	equ	not FALSE

VERBOS	equ	FALSE		;prints lots of stuff

;----------------
;ASCII Characters
;----------------
CR	equ	0Dh		;carriage return
LF	equ	0Ah		;Linefeed
QUOTE	equ	27H		;single quote
MODE	equ	80h		;Sol-20 Mode Select key
ESC	equ	1Bh		;Escape

;---------------
;Program Equates
;---------------
PROATR	equ	0		;Output file protect attributes

MAXBLK	equ	9C0h		;max allowed block size
FILTYP	equ	'*'		;Default file type= I*

;-----------------------------
;PTDOS definitions from PTDEFS
;-----------------------------
;1) MAIN ENTRY POINTS  (EP AREA)

SYS	EQU	0BCBCh
RB	EQU	SYS-3	;READ SINGLE BYTE
WB	EQU	SYS-6	;WRITE SINGLE BYTE
UTIL	EQU	SYS-9	;UTILITY HANDLER
RESET	EQU	SYS-12	;RESET TO SYSTEM
ERRL0	EQU	SYS-15	;ERROR LEVEL 0 RETURN FROM DRIVER
ERRL1	EQU	SYS-18	;ERROR LEVEL 1 RETURN FROM DRIVER
ERRL2	EQU	SYS-21	;ERROR LEVEL 2 RETURN FROM DRIVER
SYSGLO	EQU	SYS-23	;POINTER TO SYSTEM GLOBAL AREA
CONIN	EQU	SYS-26	;CONSOLE INPUT ONE CHARACTER
CONOUT	EQU	SYS-29	;CONSOLE OUTPUT ONE CHARACTER
CONTST	EQU	SYS-32	;CONSOLE INPUT READY TEST
PSCAN	EQU	SYS-35	;PARAMETER SCANNER ENTRY
INTDN	EQU	SYS-38	;INTERRUPT DONE ENTRY POINT
INTDK	EQU	SYS-41	;INTERRUPT PROCESSING ENTRY

; 2) SYS CALL OPERATIONS  (CODE FOR OPERATION BYTE)

CREOP	EQU	0	;CREATE FILE  (DE -> PARMS)
OPEOP	EQU	1	;OPEN FILE  (DE -> NAME, HL = BUFF OPT)
KILOP	EQU	2	;KILL FILE  (DE -> NAME)
RBLOP	EQU	3	;READ BLOCK OF DATA
WBLOP	EQU	4	;WRITE BLOCK OF DATA
SPAOP	EQU	5	;MOVE FILE CURSOR
EOFOP	EQU	6	;ENDFILE A FILE
CLOOP	EQU	7	;CLOSE A FILE
CHTOP	EQU	8	;CHANGE TYPE OF FILE
CHAOP	EQU	9	;CHANGE ATTRIBUTES OF A FILE
CHNOP	EQU	10	;CHANGE NAME OF A FILE
INFOP	EQU	11	;GET INFO ABOUT A FILE
SUNOP	EQU	12	;SET DEFAULT UNIT
RETOP	EQU	13	;RETURN TO CI
RESOP	EQU	14	;RESET
ABTOP	EQU	15	;ABORT, THEN RETURN TO CI
SEKOP	EQU	16	;SET FILE CURSOR TO ARBITRARY POINT (SEEK
RNDOP	EQU	17	;MAKE FILE RANDOMLY ACCESSIBLE
CAOP 	EQU	18	;CLOSE ALL FILES (OR ALL ON A UNIT)
CTLOP	EQU	19	;CONTROL/STATUS OF DRIVER
SREOP	EQU	20	;SHORT RESET TO CI
DRDOP	EQU	21	;DELIMITED READ
DWROP	EQU	22	;DELIMITED WRITE
DSPOP	EQU	23	;DELIMITED SPACE
RTROP	EQU	24	;RETURN AND SET TRAP

; 3) FILE PROTECTION ATTRIBUTES

PKIL	EQU	1	;DELETE PROTECT
PWRI	EQU	2	;WRITE PROTECTION
PREA	EQU	4	;READ PROTECTION
PFINF	EQU	8	;INFORMATION PROTECT (FILES WON'T LIST)
PATR	EQU	10h	;ATTRIBUTE CHANGE PROTECTION
PNAT	EQU	20h	;NAME AND TYPE CHANGE PROTECTION
PALO	EQU	40h	;DISK ALLOCATION PROHIBITION
PUSR	EQU	80h	;USER ATTRIBUTE

; 4) SYSTEM GLOBAL AREA

SGA	equ	0	;System Global Area base
GLCIF	EQU	SGA	;CI INPUT FILE #
GLCOF	EQU	SGA+1	;CI OUTPUT FILE #
GLUTF	EQU	SGA+2	;UTILITY FILE #
GLECF	EQU	SGA+3	;CI ECHO FILE #
GLUNI	EQU	SGA+4	;DEFAULT UNIT #
GLPRM	EQU	SGA+5	;# OF PERMANENTLY OPEN FILES
GLMXU	EQU	SGA+6	;MAX UNIT NUMBER (= # DRIVES+1)
GLSWI	EQU	SGA+7	;SYSTEM SWITCHES
GLERS	EQU	SGA+9	;SOFT (LEVEL 2) ERROR WORD
GLERM	EQU	SGA+11	;MODERATE (LEVEL 1) ERROR WORD
GLERH	EQU	SGA+13	;HARD (LEVEL 0) ERROR WORD
GLTRP	EQU	SGA+15	;CI RETURN TRAP WORD
GLPRO	EQU	SGA+17	;USER MEMORY PROTECT WORD
GLFLG	EQU	SGA+19	;CHARACTER WAITING FLAG
GLBYT	EQU	SGA+20	;THE CHARACTER
GLRCH	EQU	SGA+21	;FILE #0 READ CHARACTER ROUTINE POINTER
GLWCH	EQU	SGA+23	;FILE #1 WRITE CHARACTER ROUTINE POINTER
GLTCH	EQU	SGA+25	;CONSOLE TEST CHARACTER READY ROUTINE PTR
GLNCT	EQU	SGA+27	;NULL COUNT FOLLOWING CR
GLVER	EQU	SGA+28	;SYSTEM VERSION
GLDAT	EQU	SGA+29	;DATE (MM,DD,YY)
GLNAM	EQU	SGA+32	;DISK NAME (8 ASCII CHARACTERS)
GLPAS	EQU	SGA+40	;PASSWORD
GLBD1	EQU	SGA+48	;WAIT FOR INTERRUPT ROUTINE ADDRESS
GLBD2	EQU	SGA+50	;RESERVED FOR FUTURE USE
GLIF1	EQU	SGA+52	;INTERRUPT ENABLE FLAG
GLIF2	EQU	SGA+53	;DISK INTERRUPT PROCESSING COMPLETE FLAG
GLLOW	EQU	SGA+54	;LOW ADDRESS OF BUFFER AREA
GLECH	EQU	SGA+56	;ECHO DISABLE SWITCH
GLLOK	EQU	SGA+57	;DISK WRITE LOCK SWITCH
GLUPS	EQU	SGA+58	;UPSHIFT SWITCH
GLRBC	EQU	SGA+59	;READ-BACK CHECK SWITCH
GLBIO	EQU	SGA+60	;BINARY CONSOLE I/O SWITCH
GLLOG	EQU	SGA+61	;LOG FILE SWITCH
GLVRB	EQU	SGA+62	;VERBOSE SWITCH
GLFTR	EQU	SGA+63	;SWITCH RESERVED FOR FUTURE USE

; 6) CXBUF AND OLBUF

CXBUF	EQU	0BCC0H	;COMMAND EXECUTION BUFFER
OLBUF	EQU	0BDC0H 	;OVERLAY BUFFER

; 7) STATUS BITS IN USER DEVICE WORD

SBPRO	EQU	1	;SETTABLE PROMPT MESSAGE
SBINT	EQU	2	;INTERRUPT DRIVEN
SBATB	EQU	4	;DOES ABSOLUTE TABS
SBFF	EQU	8	;DOES FORM-FEEDS
SBSTB	EQU	10h	;SETTABLE TABS
SBTAB	EQU	20h	;DOES TAB'S (CONTROL-I)
SBIAT	EQU	40h	;INTERACTIVE
SBDUP	EQU	80h	;DUPLICATIVE

; 8)  ERROR NUMBERS

ERNEX	EQU	1	;NONEXISTENT FILE
ERAEX	EQU	2	;FILE ALREADY EXISTS
ERUFN	EQU	3	;UNASSIGNED FILE NUMBER
ERPRO	EQU	4	;PROTECTION VIOLATION
ERNIF	EQU	5	;FILE NOT TYPE IMAGE
ERMOP	EQU	6	;ILLEGAL OPERATION ON MULTIPLY OPEN FILE
ERRAC	EQU	7	;RANDOM ACCESS TO NON-RANDON FILE
ERRDV	EQU	8	;RANDOM ACCESS TO DEVICE FILE
ERZBC	EQU	9	;ZERO BLOCK COUNT IN DEVICE IMAGE FILE

ERIOP	EQU	10	;ILLEGAL PTDOS OPERATION
ERIBF	EQU	11	;ILLEGAL BUFFER ADDRESS
ERIBS	EQU	12	;ILLEGAL BLOCK SIZE
ERINM	EQU	13	;ILLEGAL CHARACTER IN NAME
ERICU	EQU	14	;ILLEGAL CHARACTER IN UNIT
ERIUN	EQU	15	;ILLEGAL UNIT
ERNTL	EQU	16	;NAME TOO LONG
ERMEM	EQU	17	;USER MEMORY PROTECT VIOLATION
ERSMP	EQU	18	;SYSTEM MEMORY PROTECT VIOLATION
ERUCN	EQU	19	;UNIT CONFLICT
ERIUP	EQU	20	;ILLEGAL UTILITY OPERATION
ERDRI	EQU	21	;DRIVER ERROR
ERIDA	EQU	22	;ILLEGAL DRIVER ACCESS
ERNCT	EQU	23	;DRIVER NOT PARTICIPATING IN THIS CTRL

EREOF	EQU	24	;END OF FILE ENCOUNTERED (OR BOF)
ERSEK	EQU	25	;SEEK ADDRESS OUT OF RANGE

ERTOP	EQU	26	;TOO MANY FILES OPEN
ERMOV	EQU	27	;MEMORY OVERFLOW
ERNID	EQU	28	;NO FILE ID'S LEFT
ERDFL	EQU	29	;DISK FULL
ERDIR	EQU	30	;DIRECTORY FULL

ERFSB	EQU	32	;FILE STRUCTURE BAD
ERFIC	EQU	33	;FILE ID CONFLICT
ERBSC	EQU	34	;BLOCK SIZE CONFLICT
ERSCC	EQU	35	;SECTOR CONFLICT
ERCFS	EQU	36	;CAN'T FIND SECTOR
ERCRC	EQU	37	;CRC ERROR OR READ/WRITE ABORTS
ERCFT	EQU	38	;CAN'T FIND TRACK
ERRBC	EQU	39	;READ-BACK-CHECK FAILED
ERLOK	EQU	40	;DISK IS WRITE LOCKED
ERXXX	EQU	41	;CATESTROPHIC ERROR
ERSYN	EQU	42	;COMMAND SYNTAX ERROR
ERNAX	EQU	43	;NAME EXPECTED
ERNUX	EQU	44	;FILE NUMBER EXPECTED
ERBCX	EQU	45	;BYTE COUNT EXPECTED
ERADX	EQU	46	;ADDRESS EXPECTED
ERVAX	EQU	47	;VALUE EXPECTED
EROPX	EQU	48	;OPTIONAL PARAMETER EXPECTED
ERINA	EQU	49	;ILLEGAL FILE NAME
ERIFI	EQU	50	;ILLEGAL FILE NUMBER
ERIAD	EQU	51	;ILLEGAL ADDRESS
ERIBC	EQU	52	;ILLEGAL BYTE COUNT
ERIVA	EQU	53	;ILLEGAL VALUE
ERIOS	EQU	54	;ILLEGAL OPTION SPECIFIER
ERITY	EQU	55	;ILLEGAL TYPE
ERIAT	EQU	56	;ILLEGAL ATTRIBUTES
ERRED	EQU	57	;DRIVE NOT READY

; 9) PARAMETER SCANNER OPERATIONS

PSCO	EQU	0	;CREATE IF NONE EXIST AND OPEN
PSOP	EQU	1	;OPEN FILE
PSFCO	EQU	2	;FORCE CREATE AND OPEN FILE
PSC	EQU	4	;CREATE FILE IF NONE EXIST
PSN	EQU	5	;READ NAME TO REG DE
PSFC	EQU	6	;FORCE CREATE FILE
PSOPT	EQU	8 	;READ ALL CHRS UNTIL , ; cr 0
PSV	EQU	85H	;CONVERT VALUE INTO REG DE
PSONE	EQU	0A5H	;READ SINGLE BYTE

;NOTE: ABOVE VALUES + 40H = USER INPUT BUFFER IN REG HL

PSCN	EQU	0D5H	;CONVERT MY VALUE TO DE, BASE IN REG B

; 10) UTILITIES

UXOP	EQU	0	;EXPLAIN ERROR
UXOP1	EQU	1	;DATA FOR UXOP
UXOP2	EQU	2	;DITTO, PART 2
UCAT	EQU	3	;CATALOG ROUTINE

;---------------------------
;PTDOS equates not in PTDEFS
;---------------------------
DNLEN	equ	10	;max PTDOS name/drive (Page 7-1)
PSBSIZ	equ	20	;PSCAN buffer size (Page 6-10)
DDFTYP	equ	255	;device driver type (Page 9-1)

;=========================================================
;= Uninitialized variables and data structures in Page 0 =
;=========================================================
	org	0

;-------------------------------------------------
;Input File name from the user, filled in by PSCAN 
;-------------------------------------------------
IFNAME:	ds	PSBSIZ		;Disk file name, including drive

;-------------------------------------------
;Output File Header for CREATE SYS operation
;(OFNAME gets filled in by PDCAN.)
;(Page 7-7) 
;-------------------------------------------
OFILE:
OFTYPE:	ds	1		;disk file type
OBSIZE:	ds	2		;Disk block size
OPATTR:	ds	1		;Disk protection attributes
OFNAME:	ds	PSBSIZ		;Disk file name, including drive

;-----------------------
;Uninitialized variables
;-----------------------
NBFMAX:	ds	2		;negative max bytes in data buffer
EXEADR:	ds	2		;execution address
FILNUM:	ds	1		;File number assigned by PTDOS

;=====================================================
;= Start of Execution                                =
;= Located at 100h so DR's LOAD can create a binary. =
;=====================================================
	org	100h

;----------------------------------------------------
;Set up a local stack in page 0, and then initialize,
;using code that gets wiped out by the data buffer.
;----------------------------------------------------
	lxi	sp,100h		;stack in first page of RAM
	call	INIT

;--------------------------------------------------------------
;Read the input file into the buffer, converting to binary as
;we go. Checking for legitimate Intel hex, remember the start
;address, file size, and potential execution address (unless
;the user overrode this with an 'E=' option), and check for
;buffer overflow.
;On Entry:
;  NBFMAX = the negative of the max number of bytes allowed
;           in the data buffer (with room for an execution
;           address at the end)
;  EXEFLG = 0FFh if no 'E=' option from user
;         = 1 if user-supplied execution address
;         = 0 if 'E=N' option from user
;  EXEADR = user-supplied execution address, if provided
;  IFNAME = input file name, null terminated
;           (max 10 characters, including drive number)
;  FILNUM = file number assigned to the input file by PTDOS
; The output file descripor (OFILE) is set up
;  OFTYPE = user-supplied file type, if one was entered
;  OBSIZE = user-supplied block size, if one was supplied
;         = 0 otherwise (meaning compute an optimal block size)
;  OPATTR = PROATR (default protection attributes)
;  OFNAME = ouput file name, null-terminated
;           (max 10 characters, including drive number)
;  The input file is open and ready
;--------------------------------------------------------------
 if VERBOS
	call	CILPRT
	db	'Buffer size:',' '+80h
	lhld	NBFMAX
	mov	a,h		;make it positive
	cma
	mov	h,a
	mov	a,l
	cma
	mov	l,a
	inx	h
	call	PHLHEX
 endif

	jmp	GETCOL		;Wait for colon to get going

;Check record checksum in a

CHKSUM:	ora	a
	jnz	CSERR		;Report any checksum error

;0-byte record means we are done with the file.

	ora	c		;0-byte record?
	jz	HLDNE		;y: done with the hex file

;Wait for colon to start the next record

GETCOL:	lda	FILNUM
	call	RB		;get one byte from file
				;trashes all regs
	call	PTDERN		;let PTDOS explain any errors

	cpi	':'
	jnz	GETCOL

	lhld	RECCNT		;bump record count
	inx	h
	shld	RECCNT

;Loop to shift the hex record header into registers:
; c: Record byte count
; b: record type
; d: checksum so far
;hl: record address

	lxi	d,4		;d=reset checksum, e=loop 4

HRHLUP:	mov	c,h		;shift through regs so that
	mov	h,l		;..all is in place after 4
	mov	l,b		;..passes through the loop
	call	GETHEX		;Data is in b, cksum in a & d
	jnz	HRHLUP

;If this is the first record then remember this record's
;address as the input hex file's address offset and the
;output image file's load address

	lda	HRTYPE
	inr	a		;HRTYPE was initialized to -1
	jnz	NOTHR0		;first record?

	shld	IMGADR		;y:addr becomes image addr

NOTHR0:	mov	a,b		;remember this record's type
	sta	HRTYPE

	mov	a,c		;0-byte record?
	ora	a
	jz	RDREC		;y: address may be meaningless

	mov	b,e		;e=b=0 so bc=record byte count

	push	d		;temp save d=checksum

;Subtract file address offset from hl, check for buffer underflow
;hl = record address

	xchg			;record address to de
	lhld	IMGADR
	mov	a,e		;16-bit subtract
	sub	l
	mov	l,a
	mov	a,d
	sbb	h
	mov	h,a		;hl:=record address-IMGADR
	jc	OVRERR		;error if address below IMGADR

;compute last address for this record, check for buffer overflow
;bc = record byte count
;hl = record address-IMGADR

	push	h
	dad	b		;hl = byte number for the last byte
	shld	BYTCNT		;byte count at end of this record

	xchg
	lhld	NBFMAX		;-(max buffer characters)
	dad	b		;hl := hl - max buffer bytes
	jc	OVRERR		;error if hl>=max buffer bytes

	pop	h		;recover record address-IMGADR
	lxi	d,DATBUF	;compute address in buffer
	dad	d		;hl=record address-IMGADR+DATBUF
	
	pop	d		;recover checksum

;Now we know this record will fit in the buffer.
;Loop to get this hex record data into the buffer at hl
;Get checksum in a at the end.
; c = record byte count (needed at the end)
; d = record checksum so far
;hl = buffer address

RDREC:	mov	e,c		;loop count=record length

HRDLUP:	call	GETHEX		;data in b, checksum in a&d
	jm	CHKSUM		;loop till checksum byte

	mov	m,b		;enqueue new byte
	inx	h
	jmp	HRDLUP

;----------------------------------------------------------
;The hex load is done. Save the execution address from the
;hex file, unless the user provided an execution address.
;The file's execution address is the very first record's
;address, unless the very last record was a type 01 record,
;and that record's address was not 0000. In this case, the
;type 01 record's address is the execution address.
;hl=address field from the EOF record
;HRTYPE is the record type from the EOF record
;IMGADR is the address field from the very first record
;----------------------------------------------------------
HLDNE:	lda	EXEFLG		;did user override exec addr?
	dcr	a		;1 means yes
	jz	HLDN2		;y: nothing to do here.

	xchg			;de=EOF record's address
	lhld	IMGADR		;1st record's address

	lda	HRTYPE		;was the EOF record type 1?
	dcr	a
	jnz	HLDN1		;n: use 1st record's address

	mov	a,d		;was address in type 1 record 0?
	ora	e
	jz	HLDN1		;y: use 1st record's address

	xchg			;n: use address from type 1 rec

HLDN1:	shld	EXEADR		;save execution address
HLDN2:

;----------------------------------------------------------
;Close the input file before opening the output file. This
;minimizes the PTDOS buffer space requirements, so that the
;output file may have a larger block size. This trashes
;IFNAME, using it as a junk buffer.
;----------------------------------------------------------

;read up to 130 more chrs from the input file, to make device
;drivers such as XMODEM happy. (XMODEM blocks will be padded
;to an even 128 bytes.)

	mvi	h,13

FLUSH:	lda	FILNUM		;file number
	lxi	b,10		;room for 10 bytes
	lxi	d,IFNAME	;where to put it
	call	SYS
	db	RBLOP		;read from the file
	call	FLSHER		;error return: is error EOF?

	dcr	h
	jnz	FLUSH

	call	CLOSE		;close the input file

;----------------------------------
;Tell user about the file we loaded
;----------------------------------
	call	ILPRNT
	db	'Read',' '+80h
	lhld	RECCNT
	call	PHLHEX
	call	ILPRNT
	db	' hex record','s'+80h

;--------------------------------------------------------------
;IMGBUF is a data structure that already includes a 4-byte
;header, followed by the binary data from the hex file. If this
;file is executable, (which it will be unless the user said it
;was not), then install the 2-byte execution address following
;the data. (Page 5-8) Also set bc equal to the Image file size,
;accounting for the data, as well as the 4-byte header and (if
;executable) the 2-byte execution address at the end.
;--------------------------------------------------------------
	lhld	BYTCNT		;received file's byte count

;Compute bc=Image file byte count, accounting for the header

	mov	b,h		;bc gets disk file byte count
	mov	c,l		;..already in the header
	inx	b		;account for Image file header
	inx	b		;..in the Image file size
	inx	b
	inx	b		;bc=total Image file byte count

;Unless the user said that this file is not executable, install
;the execution address from EXEADR at end of the Image file
;and account for these 2 bytes in bc=Image file byte count.

	lda	EXEFLG		;did user say not executable?
	ora	a
	jz	EXEDON		;not executable: it is done

	lhld	EXEADR		;get user-supplied exec address
				;..or exec address from the hex
	xchg			;de=exec address

	lxi	h,IMGBUF	;compute end address
	dad	b
	mov	m,e		;install execution address
	inx	h		;..after the data
	mov	m,d

	inx	b		;account for exec address in the
	inx	b		;..Image file byte count in bc

 if VERBOS
	push	b
	call	CILPRT
	db	'File size','='+80h
	mov	h,b
	mov	l,c
	call	PHLHEX
	pop	b
 endif

EXEDON:	push	b		;bc = Image file size
	
;-----------------------------------------------------------
;Figure out the best disk block size. Full blocks are sized
;this way: SIZE=100h+(n*140h) where n is an integer. If the
;file is smaller than MAXBLK, then set the block size to the
;smallest size that fits the entire file. If the file is
;larger than MAXBLK, then try again, fitting the Image file
;in two blocks. Continue trying with more blocks until the
;file fits. top-of-stack = bc = Image file size here.
;-----------------------------------------------------------
	lhld	OBSIZE		;did user specify block size?
	mov	a,h
	ora	l
	jnz	DBSDON		;y: use given block size

	lxi	d,-1		;negative divisor
	push	d

;Calculate the minimum block size to fit the file into
;-de blocks. If so, we are done.

DBSL1:	lxi	d,140h

	lxi	h,100h-140h	;calculate a good block size

DBSL2:	dad	d		;next block size
	mov	a,l		;16-bit compare hl-bc
	sub	c
	mov	a,h
	sbb	b		;hl-bc
	jnc	GOTDBS		;loop 'til block size>=file size

	mvi	a,(MAXBLK-140h) and 0FFh ;16-bit compare
	sub	l
	mvi	a,(MAXBLK-140h)/256
	sbb	h		;(MAXBLK-140)-hl
	jnc	DBSL2		;loop until we reach max size

;The file is too big to do with this many blocks.
;Try again with one more block.

	pop	d		;de= -(last block size try)
	pop	h		;hl=file size
	push	h
	dcx	d		;next -divisor
	push	d

;hl = file size
;de = -(number of blocks to try)
;Compute bc = hl/(-de) rounded up

	lxi	b,0		;result will be in bc

DBSL3:	inx	b
	dad	d		;loop to divide hl by de
	jc	DBSL3

	jmp	DBSL1		;try again with more blocks

;hl = computed block size. Clean up stack and carry on

GOTDBS:	shld	OBSIZE
	pop	d		;remove junk from stack

DBSDON:

;----------------------------------------------------------
;Create the output file using the header at OFILE, and then
;open the created file, and get the file number from PTDOS
;top-of-stack = Image file size here.
;----------------------------------------------------------
	lxi	d,OFILE		;output file header
	call	SYS		;create file
	db	CREOP		;(page 7-7)
	call	PTDER		;Let PTDOS explain any error

	lxi	d,OFNAME	;file name is here
	lxi	h,0		;static buffering (page 5-7)
	call	SYS		;open the disk file
	db	OPEOP		;(page 7-12)
	call	PTDER		;Let PTDOS explain any error

	sta	FILNUM		;remember assigned file number

 if VERBOS
	call	CILPRT
	db	'Writing output fil','e'+80h
 endif
;---------------------------------------------------------------
;Ask PTDOS to write the entire file to disk, then close the file
;On Entry:
;  top-of-stack = total number of bytes in the Image file
;  Image file starts at IMGBUF
;---------------------------------------------------------------
	pop	b		;byte count
	lxi	d,IMGBUF	;Image file start address
	lda	FILNUM		;file number from PTDOS
	call	SYS		;write the entire buffer
	db	WBLOP		;(page 7-15)
	call	PTDER		;Let PTDOS explain any error

	call	CLOSE		;close the output file

;---------------------------------------------------
;If the file type specified by the user is 'D', then
;convert the file to a device driver. (PTDOS won't
;let you just create the file as a device driver.)
;---------------------------------------------------
	lda	OFTYPE		;device driver?
	cpi	'D'		;type 255
	jnz	NOTDD

 if VERBOS
	call	CILPRT
	db	'Converting image file to device drive','r'+80h
 endif

	lxi	d,OFNAME	;file name
	mvi	h,DDFTYP	;device driver
	call	SYS
	db	CHTOP		;change type
	call	PTDER		;Let PTDOS explain any error

NOTDD:

;----------------------------------
;Announce success and exit to PTDOS
;----------------------------------
	call	CILPRT
	db	'Created',' '+80h

	lhld	BYTCNT
	call	PHLHEX

	call	ILPRNT
	db	' byte',' '+80h

	lda	EXEFLG		;is the file executable?
	ora	a		;nz means yes
	push	psw		;remember z flag
	jnz	ANNC1

	call	ILPRNT
	db	'non-excutable',' '+80h

ANNC1:	lda	OFTYPE		;device driver?
	cpi	'D'		;type 255
	jnz	ANNC2

	call	ILPRNT
	db	'device drive','r'+80h

	jmp	ANNC3

ANNC2:	call	ILPRNT
	db	'image fil','e'+80h

ANNC3:	call	CILPRT
	db	'Loa','d'+80h
	lhld	IMGADR
	call	PADDR

	pop	psw		;is the file executable?
	jz	ANNC4		;z means no	

	call	CILPRT
	db	'Execut','e'+80h
	lhld	EXEADR
	call	PADDR
ANNC4:

 if VERBOS
	call	CILPRT
	db	'Disk block size:',' '+80h
	lhld	OBSIZE
	call	PHLHEX
 endif

;Exit normally

	call	SYS		;Return to PTDOS
	db	RETOP		;(page 7-16)

;======================================
;= Subroutines that are needed during =
;= and after the input file read      =
;======================================

;***Subroutine***********************
;Error reading from file duing flush
;If EOF, this is not a real error
;On Entry:
; a = error code
;************************************
FLSHER:	mvi	h,1		;force FLUSH loop end
	cpi	EREOF		;End of file?
	rz			;y: return & exit flush loop

	jmp	PTDER		;n: Let PTDOS explain error	

;***Subroutine*****************************************
;Get 2 hex digits from the input device, combine them
;into a binary byte, and add the result to the checksum
;On Entry:
;  d=checksum so far
;  e = loop count
;On Exit:
;  b = binary data byte
;  a = d = updated checksum
;  e := e-1
;  Z flag set if e=0
;  M flag set if e<0
;all other registers preserved
;******************************************************
GETHEX:	push	h
	push	d

	call	GETDIG		;get high nibble
	rlc			;shift nibble up
	rlc	
	rlc
	rlc	
	mov	b,a		;save high nibble

	call	GETDIG		;get low nibble
	add	b		;combine nibbles
	mov	b,a		;result in b for return

	pop	d
	pop	h

	add	d		;update checksum
	mov	d,a

	dcr	e		;bump/test loop count
	ret			;set flags for loop count

;***Subroutine**************************
;Get one hex digit from the input device
;and test if it is a colon instead
;On Exit:
;  New nibble is in a<3:0>
;  Abort on illegal hex
;trashes de,hl
;***************************************
GETDIG:	push	b

	lda	FILNUM
	call	RB		;get one byte from file
				;RB trashes all registers
	call	PTDERN		;let PTDOS explain any errors

	pop	b
	sui	'0'		;remove ASCII bias
	cpi	10		;0-9?
	rc			;y:done

	sui	'A'-'0'		;test for gap chr
	cpi	6		;..or too large
	jnc	HEXERR
	adi	10		;make 'A'-'F' into 0Ah-0Fh
	ret

;***Subroutine******
;Close the disk file
;trashes psw,hl
;*******************
CLOSE:	lxi	h,FILNUM
	mov	a,m		;get PTDOS file number
	mvi	m,0		;remember it's closed
	ora	a		;already closed?
	rz			;y:don't do it again

	call	SYS		;close file
	db	CLOOP		;(page 7-5)
	call	PTDER		;Let PTDOS explain any error

 if VERBOS
	call	CILPRT
	db	'File closed',CR,LF+80h
 endif
	ret

;***Subroutine*************************************
;Print CR,LF, inline string
;The call to CILPRT is followed by an ASCII string.
;The last string byte has its msb set.
;trashes psw
;**************************************************
CILPRT:	call	ILPRNT
	db	CR,LF+80h

;Fall into ILPRNT

;***Subroutine*************************************
;Print inline string
;The call to ILPRNT is followed by an ASCII string.
;The last string byte has its msb set.
;trashes psw
;**************************************************
ILPRNT:	xthl			;Save hl, get msg addr
	push	b

IPLOOP:	mov	a,m
	ani	7Fh		;strip end marker
	call	CONOUT		;print byte (trashes b)
	mov	a,m		;end?
	inx	h		;Next byte
	ora	a		;msb set?
	jp	IPLOOP		;Do all bytes of msg

	pop	b
	xthl			;Restore hl,
				;..get return address
	ret

;***Subroutine*****************************
;Print ' address: ' followed by hl as 4 hex
;digits, followed by 'h'
;On Entry
;  hl = value to print
;Trashes psw,b
;******************************************
PADDR:	call	ILPRNT
	db	' address:',' '+80h

;Fall into PHLHEX

;***Subroutine*****************************
;Print hl as 4 hex digits, followed by 'h'
;On Entry
;  hl = value to print
;Trashes psw,b
;******************************************
PHLHEX:	mov	a,h		;high byte 1st
	call	PAHEX
	mov	a,l		;then the low byte
	call	PAHEX

	mvi	a,'h'
	jmp	CONOUT

;***Subroutine**********
;Print a as 2 hex digits
;On Entry
;  a = value to print
;Trashes psw,b
;***********************
PAHEX:	push	psw		;save digit
	rrc			;high nibble 1st
	rrc
	rrc
	rrc
	call	PHXNIB
	pop	psw		;recover low nibble

;fall into PHXNIB

;----------------------------------------
;local subroutine to print one hex nibble
;On Entry:
;  Nibble in low 4 of a
;Trashes a,b
;----------------------------------------
PHXNIB:	ani	0Fh		;get low
	cpi	0Ah		;A-F?
	jc	PHXN1
	adi	'A'-'9'-1
PHXN1:	adi	'0'

	jmp	CONOUT

;=================================
;= Exits that are needed during  =
;= and after the input file read =
;=================================

;***Exit***********
;File overrun Error
;******************
OVRERR:	call	CILPRT
	db	'Buffer overflo','w'+80h
	jmp	REXIT		;close file and exit

;***Exit****************
;Hex file checksum Error
;***********************
CSERR:	call	CILPRT
	db	'Checksu','m'+80h
	jmp	RECERR

;***Exit************
;Hex character Error
;*******************
HEXERR:	call	CILPRT
	db	'ASCI','I'+80h

;Fall into RECERR

;***Exit**********************
;Hex record Error of some sort
;*****************************
RECERR:	call	ILPRNT
	db	' error in record',' '+80h
	lhld	RECCNT
	call	PHLHEX

;Fall into REXIT

;***Exit***********************************************
;Error exit 
;Use reset exit to abolish the rest of the command line
;******************************************************
REXIT:	call	SYS		;Reset-Return to PTDOS
	db	RESOP		;(page 7-16)

;***Exit*****************************************************
;Get PTDOS to explain a SYS error, close the open file,
;and return (with reset) to PTDOS
;(Pages 6-8 and 8-1)
;On Entry:
;  a = error code
;  top of stack = address of routine that generated the error
;************************************************************
PTDERN:	sta	ERMCD		;Store error code for UTIL
	mvi	a,0FFh		;no SYS opcode
	jmp	PTDER1

;***Exit*****************************************************
;Get PTDOS to explain a SYS error, close the open file,
;and return (with reset) to PTDOS
;(Pages 6-8 and 8-1)
;On Entry:
;  a = error code
;  top of stack = address of routine that generated the error
;  Memeory 3 bytes before address on top of stack contains
;  the SYS opcode that generated the error.
;************************************************************
PTDER:	sta	ERMCD		;Store error code for UTIL

	pop	h		;Get calling address
	dcx	h		;Back up and get SYS opcode
	dcx	h
	dcx	h
	dcx	h
	mov	a,m		;a=SYS opcode

PTDER1:	sta	COMCD		;Give SYS opcode to UTIL

	lxi	h,LHMSG		;'Called from' message
	mvi	a,82H		;UTIL ctrl word (see page 8-1)
				;Returns when done

	call	UTIL
	db	UXOP		;Explain error
	jmp	UERROR		;UTIL Error return
COMCD:	db	0FFh
ERMCD:	db	0FFh
				;UTIL normally returns here

UERROR:	call	CLOSE		;close the disk file
	jmp	REXIT

LHMSG:	db	'HEX2IMG',0

;===================================
;= Initialized flags and Variables =
;===================================
EXEFLG:	db	0FFh	;0FFh means do what the input file says
			;1 means executable, use given address
			;0 means force not executable
HRTYPE:	db	0FFh	;Hex record type, init to -1 to detect
			;..1st record
RECCNT:	dw	0	;hex record count
DELIM:	db	0FFh	;PSCAN delimiter

;==========================================================
;= Image file buffer and received file data buffer        =
;= This potentially overwrites everything up to PTDOS.    =
;= Note that the Image file written to disk will have 4   =
;= additional bytes preceeding DATBUF. Once the data have =
;= all been read, an execution address may get appended.  =
;==========================================================
IMGBUF:
BYTCNT:	dw	0		;hex file byte count
IMGADR:	dw	0		;hex file start address
DATBUF:	equ	$

;================================================
;= The following code all gets wiped out by the =
;= data buffer when the input file gets read.   =
;================================================

;===Init Subroutine=========================================
;Initialization
;On Exit:
;  NBFMAX = the negative of the max number of bytes allowed
;           in the data buffer (with room for an execution
;           address at the end)
;  EXEFLG = 0FFh if no 'E=' option from user
;         = 1 if user-supplied execution address
;         = 0 if 'E=N' option from user
;  EXEADR = user-supplied execution address, if provided
;  IFNAME = input file name, null terminated
;           (max 10 characters, including drive number)
;  FILNUM = file number assigned to the input file by PTDOS
; The output file descripor (OFILE) is set up
;  OFTYPE = user-supplied file type, if one was entered
;  OBSIZE = user-supplied block size, if one was supplied
;         = 0 otherwise (meaning compute an optimal block size)
;  OPATTR = PROATR (default protection attributes)
;  OFNAME = ouput file name, null-terminated
;           (max 10 characters, including drive number)
;  The input file is open and ready
;Jump to HLPEXT if '?' typed instead of input file name
;Rude exit with error message if bogus input
;===========================================================

;--------------------------------------------------
;Get PTDOS's lowest address, and use it to compute
;the buffer size, adjusting for the base address of
;the buffer, and leaving room for the (optional) 2
;bytes of execution address. Remember the size for
;checking buffer overflow during the hex load.
;--------------------------------------------------
INIT:	lhld	SYSGLO		;find PTDOS's lowest address
	lxi	d,GLLOW
	dad	d		;hl=GLLOW address
	mov	a,m
	inx	h
	mov	h,m
	mov	l,a		;hl=PTDOS's lowest address

	mvi	a,(DATBUF+2) and 0FFh ;16-bit subtract
	sub	l
	mov	l,a
	mvi	a,(DATBUF+2)/256
	sbb	h
	mov	h,a
	shld	NBFMAX		;-(max bytes in buffer)

;----------------------------------------------------------
;Get the input file name from the command line. Check for ?
;----------------------------------------------------------
	lxi	d,IFNAME	;input file name

	call	GOSCAN		;get item
	jz	DFNERR		;mandatory item

	dcr	a		;must be at least 1 chr
	cpi	DNLEN		;max PTDOS filename, with drive #
				;..leaves room for a unit number
	jnc	DFNERR		;name too big or too small?

	ldax	d
	cpi	'?'		;request for help?
	jz	HLPEXT

;----------------------------------------------
;Get the output file name from the command line
;----------------------------------------------
	lxi	d,OFNAME	;PTDOS Name goes here
	call	GOSCAN		;get item
	jz	DFNERR		;mandatory

	dcr	a		;must be at least 1 chr
	cpi	DNLEN		;max PTDOS filename, with drive #
				;..leaves room for a unit number
	jnc	DFNERR		;name too big or too small?

;----------------------------------------------------
;Initialize some default variables in the output file
;header. These may soon get overwritten by options.
;----------------------------------------------------
	mvi	a,FILTYP	;default file type
	sta	OFTYPE

	lxi	h,0		;default: HEX2IMG calculates
	shld	OBSIZE		;..the "best" block size

	mvi	a,PROATR	;file protection attributes
	sta	OPATTR

;------------------------------------------------------------
;Get all subsequent command line items, each will be one of:
; bang<bsize>
; E=<execution address>
; E=N
; T=<file type>
;Note: if <bsize>=0 then HEX2IMG will compute the "best" size
;------------------------------------------------------------
SCNOPS:	lxi	d,SCNBUF	;put result here
	call	GOSCAN
	jz	PARSDN		;done with the command line?

	lxi	h,SCNOPS	;create return address
	push	h

	ldax	d		;1st chr of this item
	mov	h,a
	cpi	'!'		;set disk block size?
	inx	d
	jz	GETDBS

	ldax	d		;2nd chr of this item
	cpi	'='		;'=' option?
	jnz	OPTERR		;nothing else is legal

	inx	d		;point past '='
	mov	a,h
	cpi	'T'		;File type?
	jz	GETFT

	cpi	'E'		;execution address?
	jz	 GETEA

;Fall into OPTERR

;===Exit=============
;Option Error
;====================
OPTERR:	call	ILPRNT
	db	'Bad optio','n'+80h
	jmp	HLPEXT

;-------------------------------------------------------------
;Now that the parameters are all scanned, open the input (Hex)
;file. (PTDOS will return with a=file number and b=file type.)
;-------------------------------------------------------------
PARSDN:

 if VERBOS
	call	CILPRT
	db	'Parse done. Opening input fil','e'+80h
 endif

	lxi	d,IFNAME
	lxi	h,0		;static buffering
	call	SYS
	db	OPEOP		;open file op (page 7-12)
	call	PTDER		;Let PTDOS explain any error

	sta	FILNUM		;remember the file number

	call	CILPRT
	db	'Reading input file',CR,LF+80h

	ret			;done with INIT

;===Init Subroutine=======================
;Parse an 'E=N' or 'E=hhhh' to disable or
;set the execution address
;On Entry:
;  c=byte count
; de=SCNBUF+2, jsut past the '='
;On Exit:
; EXEFLG = 1 if an address was provided
;        = 0 of the file is non-executable
; EXEADR = execution address, unless 'E=N'
;trashes all registers
;=========================================
GETEA:	mov	a,c
	sui	3		;min 3 chrs
	cpi	3+1		;max 6 characters: 'E=ABCD'
	jnc	OPTERR		;..otherwise it is bogus

	ldax	d		;is it 'N'?
	sui	'N'		;'N' means force no execution
	jz	SETEXE		;y: this file is not executable
				;n: hl points to hex value

	xchg			;address of hex to hl
	lxi	d,SCNBUF+20	;PSCAN needs an output buffer
	mvi	a,PSCN		;PSCAN convert hex to binary
	mvi	b,16		;Tell PSCAN the input is hex
	call	PSCAN		;result de=execution address
	jc	OPTERR		;(page 6-12)

	xchg			;hl=execution address
	shld	EXEADR

	mvi	a,1		;force file to be executable

SETEXE:	sta	EXEFLG		;set/clear executableness flag
	ret

;===Init Subroutine================
;Parse a 'T=n' to set the file type
;On Entry:
;  c=byte count
; de=SCNBUF+2, jsut past the '='
;On Exit:
;  OFTYPE=specified file type
;==================================
GETFT:	mov	a,c
	cpi	3		;exactly 3 characters: 'T=n'
	jnz	OPTERR		;..otherwise it is bogus

	ldax	d
	ani	7Fh		;make it an Image file
	sta	OFTYPE

	ret

;===Init Subroutine==================================
;Parse bang<bsize> to inspect and save the block size
;On Entry:
;  c=byte count
; de=SCNBUF+1, the location of the block size
;On Exit:
; DBSIZE=new block size
;trashes all registers
;====================================================
GETDBS:	mov	a,c		;at least one chr after bang?
	sui	2
	cpi	5		;no more than 4 byte size chrs?
	jnc	DBSERR

	xchg			;address of hex to hl
	lxi	d,SCNBUF+20	;PSCAN needs an output buffer
	mvi	a,PSCN		;PSCAN convert hex to binary
	mvi	b,16		;Tell PSCAN the input is hex
	call	PSCAN		;result de=execution address
	jc	DBSERR		;PSCAN error? (page 6-12)

	xchg			;hl=block size
	shld	OBSIZE
	mov	a,l
	sui	MAXBLK and 0FFh	;16-bit compare
	mov	a,h
	sbi	MAXBLK/256
	jnc	DBSERR		;error if too big

	ret

;===Init Subroutine=======================================
;Get next item from command line
;On Entry:
;  de = buffer address, where PSCAN will write
;  DELIM = last delimiter received
;  (will be ',' or ';' or CR)
;On Exit:
;  If no more input:
;    Z set
;  If normal item found:
;    Z is cleared
;    a=c= number of characters received (which could be 0)
;    DELIM = terminating delimiter
;    (de) = 0-terminated string from command line
;    (PTDOS installs the null - see "R1" routine in PTDOS
;    source code, file D14P#2.ALS)
;=========================================================
GOSCAN:	lda	DELIM		;already reach the end
	cpi	CR		;..of this command?
	rz

	cpi	';'		;may be more commands on
	rz			;..the same command line

	push	d		;buffer address
	mvi	a,PSOPT		;Scan one item
	call	PSCAN		;(page 6-10)
	pop	d

	jc	PSERR		;error if too many characters
	sta	DELIM		;remember delimiter
	ora	d		;clear z flag
	mov	a,c		;a=byte count for return

	ret			;carry and z cleared

;===Exit==============
;Disk block size Error
;=====================
DBSERR:	call	ILPRNT
	db	'Bad disk block siz','e'+80h
	jmp	HLPEXT

;===Exit=============
;Disk File Name Error
;====================
DFNERR:	call	ILPRNT
	db	'Bad disk file nam','e'+80h
	jmp	HLPEXT

;===Exit============
;Generic PSCAN Error
;===================
PSERR:	call	ILPRNT
	db	'Huh','?'+80h

;Fall into HLPEXT

;===Exit==========
;Print help screen
;=================
HLPEXT:	call	CILPRT
;    1234567890123456789012345678901234567890123456789012345678901234
 db '          HEX2IMG Version 1.07 by M. Eberhard'
 db CR,LF
 db 'This creates a PTDOS Image file from an Intel hex file.'
 db CR,LF,LF
 db 'HEX2IMG ifile,ofile[,!bsize][,T=ftype][,E={N/hhhh}]'
 db CR,LF,LF
 db 'ifile is an Intel hex file. ofile will be a PTDOS Image file.'
 db CR,LF
 db 'bsize sets the ofile disk block size (default: best-fit size).'
 db CR,LF
 db 'ftype is a 1-character Image file type (default: *). ftype=D'
 db CR,LF
 db 'creates a device driver. E=N makes a non-executable Image file.'
 db CR,LF
 db 'E=hhhh makes an Image file that executes at address hhhh (which'
 db CR,LF
 db 'cannot be 0). If no E= option then the execution address comes'
 db CR,LF
 db 'from the (non-zero) address in a Type 01 record, if it exits.'
 db CR,LF
 db 'Otherwise, execution is at the first address','.'+80h

;Use reset exit to abolish the rest of the command line

	jmp	REXIT

;=============================================
;Scan buffer, only used during option scanning
;=============================================
SCNBUF:	equ	$
	END


