

;	------------------------------------------------------------
;	.REL files are encoded as a bitstream according to certain
;	rules which are spelled out in Microsoft's FORTRAN manual.
;	As a consequence a normal disk dump program will not be very
;	intelligible, even to people who have learned to recognize
;	8080 opcodes in hexadecimal form, or to spot ASCII constants
;	in a program. The purpose of READREL.CMD is to generate a
;	hexadecimal dump of a .REL file, interspersed with special
;	directives as they are produced by M80.COM, F80.COM or other
;	relocating assemblers using the same format.
;
;		     READREL - Copyright (C) 1983
;		    Universidad Autonoma de Puebla
;		          November 24, 1983
;
;	[Harold V. McIntosh, 24 November 1983]
;	------------------------------------------------------------


BDOS	equ	224		;CP/M interrupt to BDOS

TFCB	equ	005CH
TSIZ	equ	0080H		;CP/M's record size
DSIZ	equ	0100H		;size of .OUT buffer (must be 100H)
RSIZ	equ	0400H		;size of relfile buffer
LSIZ:	equ	16		;line length (maximum spaces per line)

LF	equ	0AH
CR	equ	0DH

;	-------------
	org	0100H
;	-------------

BEGN:	sto	#0000,BCTR		;byte counter
	sto	#DSIZ,DCTR		;output downcounter
	sto	#DBUF,DPTR		;output pointer
	sto	#0000,RCTR		;relfile counter
	sto	#RBUF,RPTR		;relfile pointer
	stob	#LSIZ,LCTR		;count 16 bytes/line
	stob	#00,COLM		;FF=column 1
	cmpb	TFCB+1,#' '
	jnz	NNUL
	ld	dx,#LOGO		;'UAP...'
	call	MSSG			;type text at (dx)
	ld	dx,#TUTO		;instructions for usage
	call	MSSG			;type text at (dx)
	jmp	GBYE			;normal exit

NNUL:	ld	cl,#021H		;count
	ld	dx,#TFCB		;source
	ld	bx,#RFCB		;.REL FCB = destination
	call	MIUC			;block move

	stob	#'R',RFCB+9
	stob	#'E',RFCB+10
	stob	#'L',RFCB+11
	stob	#00H,RFCB+12

	ld	cl,#15			;(0F) open file
	ld	dx,#RFCB		;.REL FCB
	int	BDOS
	cmpb	al,#0FFH
	ld	dx,#COSO		;'can''t open .REL file'
	jnz	ORLF
	jmp	TEMA			;type text at (dx), quit

ORLF:	stob	#00,DOUT		;z=no disk output
	cmpb	TFCB+17,#' '		;output filename
	jz	NOUT
	stob	#0FFH,DOUT		;z=no disk output

	ld	cl,#16
	ld	dx,#TFCB+16
	ld	bx,#TFCB	;CP/M's file control block (.OUT)
	call	MIUC

	cmpb	TFCB+9,#' '	;output extension
	jnz	YEXT
	stob	#'O',TFCB+9
	stob	#'U',TFCB+10
	stob	#'T',TFCB+11

YEXT:	ld	cx,#0003
	ld	bx,#TFCB+9
	ld	dx,#RFCB+9
YEXX:	xchg	bx,dx
	ld	al,[bx]
	xchg	bx,dx
	cmp	al,[bx]
	inc	bx
	inc	dx
	loopz	YEXX
	jnz	NEQL
	ld	dx,#MEQL	;'extensions equal'
	jmp	TEMA		;type text at (dx), quit

NEQL:	ld	cl,#19		;(13) delete file
	ld	dx,#TFCB	;CP/M's file control block (.OUT)
	int	BDOS

	ld	cl,#22		;(16) create file
	ld	dx,#TFCB	;CP/M's file control block (.OUT)
	int	BDOS

	ld	cl,#15		;(0F) open file
	ld	dx,#TFCB	;CP/M's file control block (.OUT)
	int	BDOS
	cmp	al,#0FFH
	ld	dx,#DDFU	;'no more directory'
	jnz	NOUT
	jmp	TEMA		;type text at (dx), quit

NOUT:	sto	#0FF00H,ROBY	;rotating byte, bit counter
	call	REFI		;process the .REL file
	cmpb	DOUT,#00	;z=no disk output
	jz	FINI

;	Fill the remainder of the output file with ^Z's, write
;	the tag end to disk, and close the file.

	ld	cx,DCTR		;output downcounter
	ld	bx,DPTR		;output pointer
	ld	al,#1AH		;^Z
	jcxz	CLOP
CLOO:	sto	al,[bx]
	loop	CLOO

CLOP:	cmpb	DBUF,#1AH	;^Z
	jz	CLOS
	ld	cl,#26		;(1A) set DMA address
	ld	dx,#DBUF
	int	BDOS

	call	BUDK		;record to disk

	cmpb	DBUF+TSIZ,#1AH	;^Z
	jz	CLOS
	ld	cl,#26		;(1A) set DMA address
	ld	dx,#DBUF+TSIZ
	int	BDOS

	call	BUDK		;record to disk

CLOS:	ld	cl,#16		;(10) close file
	ld	dx,#TFCB	;CP/M's file control block (.OUT)
	int	BDOS
	cmp	al,#0FFH
	ld	dx,#NCLO	;'can''t close file'
	jnz	FINI
	jmp	EDSK		;delete .OUT file, type messages

FINI:	call	CRLF		;CR,LF
GBYE:	ld	dl,#00
	ld	cl,#00
	int	BDOS

;	Type CR, LF.

CRLF:	ld	al,#CR
	call	CONA		;console from A
	ld	al,#LF
	call	CONA		;console from A
	stob	#LSIZ,LCTR	;count 16 bytes/line
	stob	#0FFH,COLM	;FF=column 1
	ld	cl,#11		;(0B) console status
	int	BDOS
	or	al,al
	jz	CRLR
	ld	cl,#1		;(01) read console
	int	BDOS
	ld	dx,#IRRP	;'analysis interrupted'
	call	MSSG
	cmpb	DOUT,#00	;z=no disk output
	jz	GBYE
	jmp	EDSQ		;delete .OUT file, type messages
CRLR:	ret

;	word: type dx as four nibbles.
;	byte: type A as two nibbles.

WORD:	mov	al,dh
	call	BYTE		;type A as two nibbles
	mov	al,dl
BYTE:	call	TADR		;type address if column 1
BEIT:	push	ax
	rcr	al
	rcr	al
	rcr	al
	rcr	al
	call	NIBL		;type A as hex
	pop	ax
NIBL:	and	al,#0FH
	add	al,#90H
	daa
	adc	al,#40H
	daa
CONA:	push	bx
	push	dx
	push	ax
	ld	cl,#2		;(02) write console
	mov	dl,al
	int	BDOS
	pop	ax
	cmpb	DOUT,#00	;z=no disk output
	jz	CONB
	call	DDDA
CONB:	pop	dx
	pop	bx
	ret

;	Type several spaces.

DUBL:	call	SNGL		;type one space
SNGL:	decb	LCTR		;count 16 bytes/line
	jnz	NOCR
	call	CRLF
NOCR:	ld	al,#' '
	jmp	CONA

;	Type relative address if at start of a new line.

TADR:	push	bx
	incb	COLM		;FF=column 1
	jnz	TRET
	push	ax
	ld	al,#'('
	call	CONA
	ld	bx,BCTR
	mov	al,bh
	call	BEIT
	mov	al,bl
	call	BEIT
	ld	al,#')'
	call	CONA
	call	SNGL
	pop	ax
TRET:	pop	bx
	ret

;	Type message at (dx).

MSSG:	xchg	bx,dx
MSSH:	ld	al,[bx]
	cmp	al,#'$'
	jz	MSSR
	call	CONA
	inc	bx
	jmp	MSSH
MSSR:	ret

;	Type message, erase .OUT file, quit.

EDSK:	call	MSSG
EDSQ:	ld	cl,#19		;(13) delete file
	ld	dx,#TFCB	;CP.M's file control block (.OUT)
	int	BDOS		;don't leave a partial file
	ld	dx,#ERAD	;'output file erased'
TEMA:	call	MSSG
	jmp	GBYE

;	Block move.

MIUC:	xchg bx,dx
	ld	al,[bx]
	xchg	bx,dx
	sto	al,[bx]
	inc	bx
	inc	dx
	dec	cl
	jnz	MIUC		;block move
	ret

;	Place disk output in buffer, store the buffer in the
;	disk as it is filled and reinitialize it.

DDDA:	cmp	DCTR,#0000
	jnz	DDDB
	call	BUDI		;output downcounter
DDDB:	dec	DCTR
	ld	bx,DPTR		;output pointer
	sto	al,[bx]
	inc	DPTR		;output downcounter
	ret

;	Send DBUF to disk.

BUDI:	push	ax
	call	BUDJ
	sto	#DBUF,DPTR	;output pointer
	sto	#DSIZ,DCTR
	pop	ax
	ret

BUDJ:	ld	cl,#26		;(1A) set DMA address
	ld	dx,#DBUF	;output buffer
	int	BDOS

	call	BUDK		;record to disk

	ld	cl,#26		;(1A) set DMA address
	ld	dx,#DBUF+TSIZ	;CP/M's record size
	int	BDOS

BUDK:	ld	cl,#21		;(15) write one record
	ld	dx,#TFCB	;CP/M's file control block (.OUT)
	int	BDOS
	cmp	al,#00H
	ld	dx,#DIWR	;'disk write error'
	jnz	EDSK		;delete .OUT file, type messages
	ret

;	Get next bit.

GEBI:	incb	ROBY+1
	jnz	GEBJ
	call	NXHX
	stob	al,ROBY
	stob	#0F8H,ROBY+1	;-8
GEBJ:	rclb	ROBY
	ret

;	Read sixteen bits from the .REL file, leave them in dx.

GEWO:	call	GEBY
	push	ax
	call	GEBY
	pop	dx
	mov	dh,al
	ret

;	Read a full byte from the .REL file, leave it in al.

GEBY:	ld	cl,#08
BITS:	ld	ch,#00
MORE:	call	GEBI		;get next bit
	rcl	ch
	dec	cl
	jnz	MORE
	mov	al,ch
	ret

;	Get next relfile element.

NXHX:	push	cx
	push	bx
	cmp	RCTR,#0000	;relfile counter
	jnz	NXHV		;byte available
	sto	#RBUF,RPTR
NXHY:	cmp	RPTR,#(RBUF+RSIZ)
	jnc	NXHU		;reset relfile, read 1st element
	ld	cl,#26		;(1A) set DMA address
	ld	dx,RPTR		;relfile buffer
	int	BDOS
	ld	cl,#20		;(14) read one record
	ld	dx,#RFCB	;.REL FCB
	int	BDOS
	cmp	al,#000H
	jnz	NXHZ
	add	RPTR,#TSIZ
	add	RCTR,#TSIZ
	jmp	NXHY

NXHZ:	cmp	al,#001H
	ld	dx,#DIRD	;'disk read error'
	jz	NXHU
	jmp	TEMA		;type text at (dx), quit

NXHU:	sto	#RBUF,RPTR
NXHV:	dec	RCTR
	ld	bx,RPTR
	inc	RPTR
	ld	al,[bx]
	pop	bx
	pop	cx
	ret

;	-------------------
;	Read the .REL file.
;	-------------------


;	The first bit read determines whether a single byte
;	follows (bit = 0), to be used without alteration, or
;	whether the following information may be relocatable
;	(bit = 1).

REFI:	call	GEBI		;get next bit
	jc	RELA		;not absolute byte
	call	GEBY		;full byte from .REL
	call	BYTE		;type A as two nibbles
	call	SNGL		;type one space
	inc	BCTR		;byte counter
	jmp	REFI		;.REL read loop

;	When the leadin bit is 1, two more bits are read to
;	distinguish four alternatives: 00 for further analysis,
;	01 for program segment, 10 for data segment, or 11 for
;	common segment. In the latter three cases, a two-byte
;	address will be read, which a loader would assign to its
;	proper memory segment.

RELA:	ld	cl,#2
	call	BITS		;get C bits from .REL
	or	al,al
	jz	SPEC		;'special LINK element'
	call	TADR		;type address if column 1
	call	GTYQ		;fetch segment descriptor
	call	MSSG		;type text at (dx)
	call	GEWO		;sixteen bits from .REL
	call	WORD		;type dx as four nibbles
	call	CRLF		;CR, LF
	ld	bx,BCTR		;byte counter
	inc	bx
	inc	bx
	sto	bx,BCTR		;byte counter
	jmp	REFI		;.REL read loop

;	There are sixteen 'special LINK elements' ranging from
;	entry point definitions to the EOF indicator. They fall
;	into three or four general groups, depending on whether
;	they are associated with an eight-byte ASCII label, a
;	relocatable address, both, or neither. Since they represent
;	parenthetic information, they are set off on a line by
;	themselves.

SPEC:	cmpb	LCTR,#LSIZ	;max spaces/line
	jz	SPED
	call	CRLF		;CR,LF
SPED:	stob	#0,COLM		;FF=column 1
	ld	cl,#4
	call	BITS		;get C bits from .REL
	ld	ah,#0
	add	ax,ax		;case number indexes two tables
	mov	bx,ax
	ld	di,#META	;case table for messages
	ld	dx,[bx+di]
	ld	di,#JUTA	;case table for action addresses
	push	[bx+di]
	jmp	MSSG		;type text at (dx)

;	Message table, to correspond to the 'special' relocation
;	elements, of which there are sixteen. These are the
;	descriptive messages inserted into the output stream.

META:	dw	ME0		;'entry symbol'
	dw	ME1		;'COMMON block'
	dw	ME2		;'program name'
	dw	ME3		;'request library search'
	dw	ME4		; reserved
	dw	ME5		;'COMMON size'
	dw	ME6		;'chain external'
	dw	ME7		;'define entry'
	dw	ME8		; reserved
	dw	ME9		;'external add offset'
	dw	MEA		;'size of DATA area'
	dw	MEB		;'loading location counter'
	dw	MEC		;'chain address'
	dw	MED		;'define program size'
	dw	MEE		;'end of program'
	dw	MEF		;'end of file'

;	There may be a label or a further address associated
;	with each 'special LINK item.' These subroutines take
;	the appropriate action in each case.

JUTA:	dw	TEXT		;ASCII label
	dw	TEXT		;ASCII label
	dw	TEXT		;ASCII label
	dw	TEXT		;ASCII label
	dw	TEXT		;ASCII label
	dw	STYP		;storage type
	dw	STYP		;storage type
	dw	STYP		;storage type
	dw	STYP		;storage type
	dw	PHUE		;storage type w/ address
	dw	PHUE		;storage type w/ address
	dw	PHUE		;storage type w/ address
	dw	PHUE		;storage type w/ address
	dw	PHUE		;storage type w/ address
	dw	PHOO		;program end - flush byte
	dw	EOFI		;EOF

;	The 'special LINK item' includes an 8-byte label
;	or similar ASCII string. 8 bytes is maximum, there
;	must be at least 1, 8 is taken for 0.

TEXT:	ld	cl,#3
	call	BITS		;get C bits from .REL
	or	al,al		;take 0 to mean 8
	jnz	X2X
	ld	al,#8
X2X:	push	ax
	call	GEBY		;full byte from .REL
	call	CONA
	pop	ax
	dec	al
	jnz	X2X
	call	CRLF		;CR,LF
	jmp	REFI		;.REL read loop

;	The 'special LINK item' requires identification via
;	the following two bits, as to whether it is absolute
;	(00), code (01), data (10), or common (11).

STYP:	call	GTYP		;ascertain storage type
	call	MSSG		;type text at (dx)
	call	TWRD		;read & type word from .REL
	call	DUBL		;type one space
	jmp	TEXT		;ASCII label

;	Read two bits to ascertain storage type, then load
;	address of appropriate descriptor for console typing.

GTYP:	ld	cl,#2
	call	BITS		;get C bits from .REL
GTYQ:	ld	dx,#MABS	;'ABS:'
	or	al,al
	jz	GTYR
	ld	dx,#MPRG	;'PREL'
	dec	al
	jz	GTYR
	ld	dx,#MDAT	;'DREL'
	dec	al
	jz	GTYR
	ld	dx,#MCOM	;'CREL'
GTYR:	ret

;	This class of 'special LINK items' require a two-bit
;	storage classigication followed by a two-byte address.

PHUE:	call	GTYP		;ascertain storage type
	call	MSSG		;type text at (dx)
	call	TWRD		;read & type word from .REL
	call	CRLF		;CR,LF
	jmp	REFI		;.REL read loop

;	At the program end, the bit stream should be squared
;	off at a byte boundary. A start address may be present.

PHOO:	call	GTYP		;ascertain storage type
	call	MSSG		;type text at (dx)
	call	TWRD		;read & type word from .REL
	call	CRLF		;CR,LF
	sto	#0FF00H,ROBY	;rotating byte, bit counter
	jmp	REFI		;.REL read loop

;	End of File.

EOFI:	ret

;	Read a word from .REL and type it.

TWRD:	call	GEWO		;sixteen bits from .REL
	jmp	WORD		;type dx as four nibbles

;	-------------------------------------------------------


LOGO:	db	CR,LF
	db	'        READREL/ICUAP',CR,LF
	db	'Universidad Autonoma de Puebla',CR,LF
	db	'      November 24, 1983',CR,LF,'$'

TUTO:	db	CR,LF
	db	'READREL.CMD will decompose the bitstream which',CR,LF
	db	'constitutes a .REL file into hexadecimal bytes',CR,LF
	db	'and addresses interspersed with commentaries',CR,LF
	db	'derived from the ''special LINK elements'' and',CR,LF
	db	'other clarifying information. The command:',CR,LF,CR,LF
	db	'    READREL [D:]FILE[.REL] [[D:]LIST[.OUT]]',CR,LF,CR,LF
	db	'will analyze the requested file. If no extension',CR,LF
	db	'is given, .REL will be used. The analysis will',CR,LF
	db	'always appear at the console. When a second file is',CR,LF
	db	'given (with default extension .OUT), it will gather',CR,LF
	db	'in the output listing. Pressing any key will stop',CR,LF
	db	'the program.',CR,LF
	db	'$'

ME0:	db	'entry symbol  $'
ME1:	db	'COMMON block  $'
ME2:	db	'program name  $'
ME3:	db	'req lib srch  $'
ME4:	db	'reserved      $'
ME5:	db	'COM  size $'
ME6:	db	'chain ext $'
ME7:	db	'def entry $'
ME8:	db	'reserved  $'
ME9:	db	'extnl + offset $'
MEA:	db	'size DATA area $'
MEB:	db	'ldng locn cntr $'
MEC:	db	'chain address  $'
MED:	db	'def prgrm size $'
MEE:	db	'end of program $'
MEF:	db	'EOF$'
MABS:	db	'ABS: $'
MPRG:	db	'PREL $'
MDAT:	db	'DREL $'
MCOM:	db	'CREL $'
DIRD:	db	'Disk read error.$'
DIWR:	db	'Disk write error.$'
COSO:	db	'Can''t open .REL file.$'
DDFU:	db	'Disk or Directory full.$'
NCLO:	db	'Cannot close file.$'
MEQL:	db	'Duplicate extensions.$'
IRRP:	db	'-- Analysis interrupted --',CR,LF,'$'
ERAD:	db	'- Output file suppressed -$'

RFCB:	ds	21H		;.REL FCB
RBUF:	ds	RSIZ		;relfile buffer (400H)
RPTR:	ds	2		;relfile pointer
RCTR:	ds	2		;relfile counter
ROBY:	ds	2		;rotating byte, bit counter
DOUT:	ds	1		;z=no disk output
DCTR:	ds	2		;output downcounter
DPTR:	ds	2		;output pointer
LCTR:	ds	1		;count 16 bytes/line
COLM:	ds	1		;FF=column 1
BCTR:	ds	2		;byte counter
DBUF:	ds	DSIZ		;.OUT buffer

	end

