[fa.info-mac] assembly i/o package

info-mac@uw-beaver (12/22/84)

From: winkler@harvard.ARPA (Dan Winkler)

Here is a little assembly language i/o package developed for our
assembly language courses that use the Mac.  It was written by Jeff
Miller and is used by his QuickPrint and MacMelody programs.  

We were using an early version of the assembler then, but this should
work just fine with the newer versions.  The only difference I know of
is that the new assembler names its linker files .link but the old one
named them .ld.

Dan. (winkler@harvard)


 ; io.asm
 ;  "Frame" program and I/O subroutines for general purpose assembly language
 ;  programs.  To use this package:
 ;  1. The first line in your file should be "include iodefs.txt".
 ;  2. Your main program must begin with the label "main" and end with rts.
 ;  3. Your .ld file should look like this, if your source file is called
 ;     myprog.asm:
 ;			!start
 ;			io
 ;			myprog
 ;			$
 ;  4. Pass arguments to subroutines in this package, and receive return
 ;     values, in D0 or A0 as specified.  All registers are preserved (but
 ;     subroutine rdchar does not preserve A0).
 ;  5. Always call subroutine makewindow at the beginning of your main
 ;     program, passing the address of your title string in A0.
 
 ; Include all necessary traps files
 
 include	m68klib.d
 include systraps.txt
 include quicktraps.txt
 include tooltraps.txt
 
 WINDWIDTH equ 492	; window dimensions
 WINDHEIGHT equ 280	
 X1	equ 8		; upper-left and lower-right coordinates
 Y1	equ 48		; for bounds rectangle for window
 X2	equ X1+WINDWIDTH
 Y2	equ Y1+WINDHEIGHT
 LMARG	equ 6		  ; text margins - left,
 RMARG	equ WINDWIDTH-10  ; right,
 TMARG	equ 15		  ; top,
 BMARG	equ WINDHEIGHT-15 ; bottom
 CR	equ 13		; ascii carriage return
 BS	equ 8		; ascii backspace
 DASH	equ 45		; ascii minus sign
 ZERO	equ 48		; ascii character for digit zero
 NINE	equ 57		; ascii character for digit nine
 PROGFONT equ 4		; Monaco font code
 ASCENT	equ 0		; offsets of fields in fontinfo record
 DESCENT	equ 2
 WIDMAX	equ 4
 LEADING	equ 6
 BUFSIZE equ 256		; size of input buffer
 
 xdef	start
 xref	main
 
 ;  Preserve registers in safe place; initialize graphics, font manager,
 ;  windows, cursor, and textedit; call main program; restore registers
 ;  and exit.
 
 start:	movem.l	D0-D7/A0-A6, -(SP)	; save all registers
 	lea	saveregs, A0
 	move.l	A6, (A0)
 	move.l	A7, 4(A0)
 	
 	move.l	A5, D0			; set up quickdraw
 	sub.l	#4, D0
 	move.l	D0, -(SP)
 	_INITGRAF
 	_INITFONT
 	_INITWINDOW
 	_INITCURSOR
 	_TEINIT
 	
 	bsr main			; call the main program
 	
 	lea	saveregs, A0
 	move.l	(A0), A6
 	move.l	4(A0), A7
 	movem.l	(SP)+, D0-D7/A0-A6
 	_EXITTOSHELL
 	
 
 ; Subroutine makewindow ( StringAddress )
 ;  Receives argument in A0
 ;	Creates and displays a desk-accessory style window with title
 ;	specified by string whose address is passed; flushes event queue
 ;	and initializes cursor position for text I/O
 
 xdef	makewindow
 
 makewindow:
 	movem.l	D0-D2/A0-A1, -(SP)	; save registers
 	clr.l	-(SP)			; space for return window pointer
 	clr.l	-(SP)			; nil wstorage pointer - window 
 					;  record storage will be allocated
 					;  on heap
 	pea	boundsrect		; determines window size and loc
 	move.l	A0, -(SP)		; window title
 	st	-(SP)			; visible flag true - draw window
 	move	#16, -(SP)		; procid - desk accessory type window
 	move.l	#-1, -(SP)		; behind pointer -1 means in front
 					;  of all other windows
 	sf	-(SP)			; goaway flag false - no goaway box
 	clr.l	-(SP)			; refcon (not used)
 	_NEWWINDOW
 	move.l	(SP), window(A5)
 	_SETPORT
 	clr.l	-(SP)
 	_NEWRGN				; create a region
 	lea	scrollrgnh, A0
 	move.l	(SP), (A0)
 	pea	scrollrect		; with same bounds as boundsrect
 	_RECTRGN			; (required later for scrolling)
 	move.w	#PROGFONT, -(SP)	; set font to Geneva (monofont)
 	_TEXTFONT
 	pea	fontinfo		; get information on font
 	_GETFONTINFO			;  dimension parameters
 	clr.w	D0			; accumulate line height in D0
 	add.w	fontinfo+ASCENT, D0
 	add.w	fontinfo+DESCENT, D0
 	add.w	fontinfo+LEADING, D0
 	lea	lnht, A0
 	move.w	D0, (A0)		; store in lnht
 	move.l	D0, -(SP)		; flush event queue
 	move.l	#$0000FFFF, D0
 	_FLUSHEVENTS
 	move.l	(SP)+, D0
 	move	#LMARG, -(SP)		; initialize cursor position
 	move	#TMARG, -(SP)
 	_MOVETO
 	movem.l	(SP)+, D0-D2/A0-A1	; restore registers
 	rts
 	
 ; Subroutine clearwindow
 ;	Erases contents of window and 'homes' pen to original position in
 ;	upper left corner.
 
 xdef	clearwindow
 
 clearwindow:
 	movem.l	D0-D2/A0-A1, -(SP)	; save registers
 	pea	scrollrect		; erase window contents
 	_ERASERECT
 	move.w	#LMARG, -(SP)		; home pen
 	move.w	#TMARG, -(SP)
 	_MOVETO
 	movem.l	(SP)+, D0-D2/A0-A1	; restore registers
 	rts
 	
 ; Subroutine rdchar 
 ;   Returns single character result in D0.
 ;	Waits for next keyboard character from event queue, echoes it on
 ;	screen and returns it in D0.  If character is CR or right margin has
 ;	been reached, moves cursor to left margin one line down.
 
 xdef	rdchar
 
 rdchar: lea	regbufe, A0
 	movem.l	D1-D2/A1, -(A0)		; save registers (but not A0)
 	move.w	#40, -(SP)		; mask for key-down or auto-key event
 	pea	evbuf			; pass address of event buffer
 	_GETNEXTEVENT
 	lea	evbuf, A0		; any characters?
 	tst.w	(A0)
 	beq	rdchar			; if not, keep waiting
 	clr.l	D0
 	move.b	5(A0), D0		; pass character to wrtchar
 	jsr	wrtchar
 	lea	regbuf, A0
 	movem.l	(A0)+, D1-D2/A1		; restore registers
 	rts
 	
 ; Subroutine wrtchar ( character )
 ;   Receives single character argument in D0
 ;	Draws character on screen; if character is CR or right margin has
 ;	been reached, moves pen to left margin one line down; if bottom
 ;	has been reached, scrolls up one line.
 
 xdef	wrtchar
 
 wrtchar:
 	movem.l	D1-D2/D5-D7/A0-A1, -(SP) ; save registers
 	move.l	D0, D5			; store character in D5
 	pea	penloc			; check pen location
 	_GETPEN
 	move.w	penloc+2, D7		; store x value of pen in D7
 	move.w	penloc, D6		; store y value of pen in D6
 	cmp.b	#BS, D5			; backspace typed?
 	bne	wrtchar1		; if not, skip code to erase char.
 	cmp.w	#LMARG, D7		; already at left margin?
 	beq	wrtchar4		; if so, skip code to erase char.
 	lea	fontinfo, A0		; compute and push inverse of
 	move.w	WIDMAX(A0), D0		; character width
 	neg.w	D0
 	move.w	D0, -(SP)
 	move.w	#0, -(SP)		; don't change pen's y coord
 	_MOVE				; move pen back one character pos.
 	move.w	D7, -(SP)		; push x coord for lower right corner
 	move.w	D6, D0			; add descent amount to y coord
 	add.w	fontinfo+DESCENT, D0	;  of lower right corner
 	move.w	D0, -(SP)		; of erase rect.
 	sub.w	fontinfo+ASCENT, D6	; compute coords for upper left
 	sub.w	fontinfo+WIDMAX, D7	; corner of erase rect
 	move.w	D7, -(SP)		; and push them
 	move.w	D6, -(SP)		
 	move.l	SP, A0			; push addr of erase rect
 	move.l	A0, -(SP)
 	_ERASERECT			; erase deleted character
 	add	#8, SP			; remove rectangle coords from stack
 	bra	wrtchar4		; skip ahead to return backspace
 wrtchar1:
 	cmp.b	#CR, D5			; carriage return?
 	beq	wrtchar2			; if so, skip to new-line generation
 	cmp.w	#RMARG, D7		; past right margin?
 	blt	wrtchar3			; if not, skip new-line generation
 wrtchar2: 				; generate new line
 	sub.w	#LMARG, D7		; compute delta x
 	neg.w	D7
 	move.w	D7, -(SP)
 	lea	lnht, A0
 	move.w	(A0), -(SP)
 	_MOVE				; position cursor for new line
 	cmp.w	#BMARG, D6		; past bottom margin?
 	blt	wrtchar3			; if not, skip past code for scroll
 	pea	scrollrect
 	move.w	#0, -(SP)
 	lea	lnht, A0
 	move.w	(A0), -(SP)
 	neg.w	(SP)
 	lea	scrollrgnh, A0
 	move.l	(A0), -(SP)		; else, scroll up by one
 	_SCROLLRECT			; line height
 	move.w	#0, -(SP)
 	lea	lnht, A0
 	move.w	(A0), -(SP)		; move pen up accordingly
 	neg.w	(SP)
 	_MOVE
 wrtchar3:
 	move.w	D5, -(SP)
 	_DRAWCHAR			; display character
 wrtchar4:
 	move.l	D5, D0			; return character in D0
 	movem.l (SP)+, D1-D2/D5-D7/A0-A1 ; restore registers
 	rts
 	
 ; Subroutine wrtdec ( num: integer )
 ;   Receives integer argument in D0.
 ;	If argument is negative, negates it and outputs a minus sign; calls
 ;	subroutine prtdec to output digits of number
 
 xdef	wrtdec
 
 wrtdec:	movem.l D0-D2/A0-A1, -(SP)	; save registers
 	tst.w	D0			; negative?
 	bge	wrtdec1			
 	neg.w	D0			; if so, negate num
 	move.w	D0, -(SP)		; save num
 	;clr.w	D0
 	move.b	#DASH, D0		; output minus sign
 	jsr	wrtchar
 	move.w	(SP)+, D0		; restore num to D0
 wrtdec1:
 	swap	D0
 	clr.w	D0
 	swap	D0
 	bsr	prtdec			; output num
 	movem.l (SP)+, D0-D2/A0-A1	; restore registers
 	rts
 	
 ; Subroutine prtdec (num: integer)
 ;   Receives integer argument in D0.
 ;	Recursively outputs digits of integer in decimal to terminal.
 
 prtdec:	divs	#10, D0			; num <- num DIV 10
 	tst.w	D0			; result equals zero?
 	beq	prtdec1
 	move.l	D0, -(SP)		; if not, call prtdec recursively
 	swap	D0
 	clr.w	D0			; (first getting rid of remainder)
 	swap	D0
 	bsr	prtdec			; with num DIV 10
 	move.l	(SP)+, D0
 prtdec1:
 	swap	D0			; num MOD 10
 	add.b	#ZERO, D0		; compute ascii code for digit
 	jsr	wrtchar			; output digit
 	rts
 	
 ; Subroutine rddec 
 ;   Returns integer result in D0.
 ;	Inputs signed word integer value (decimal), ignores all non-numeric
 ;	characters except for minus sign, returns result on encountering
 ;	CR.
 
 xdef	rddec
 
 rddec:	movem.l D1-D5/A0-A1, -(SP)	; save registers
 	lea	inbuf, A0
 	jsr	rdstrz			; input line to buffer
 	clr.w	D0			; sum = 0
 	clr.w	D3			; character holder = 0
 	clr.b	D4			; digits read = false
 	move.w	#1, D5			; sign = +
 rddec1: move.b	(A0)+, D3		; end of input line?
 	beq 	rddec3			; if so, return result
 	cmp.b	#DASH, D3		; minus sign?
 	bne	rddec2
 	tst.b	D4			; if so, any digits read yet?
 	bne	rddec1			; if so, ignore minus sign
 	neg.w	D5			; else, sign = -
 	bra	rddec1
 rddec2: cmp.b	#ZERO, D3		; less than '0'?
 	blt	rddec1			; if so, ignore
 	cmp.b	#NINE, D3		; greater than '9'?
 	bgt	rddec1			; if so, ignore
 	move.b	#1, D4			; digits read = true
 	muls	#10, D0			; sum = sum * 10
 	sub.w	#ZERO, D3		; subtract ascii code for 0 from char
 					;  to get digit value
 	add.w	D3, D0			; sum = sum + digit
 	bra	rddec1			; get next character
 rddec3:	muls	D5, D0			; sum = sum * sign
 	movem.l (SP)+, D1-D5/A0-A1	; restore registers
 	rts
 	
 ; Subroutine rdstrz ( BufferAddress )
 ;   Receives address of destination buffer in A0.
 ;	Inputs characters to buffer specified by destination address
 ;	until CR is read; terminates string with zero byte; if backspace
 ;	encountered, deletes last character in buffer, backspace not stored.
 ;	Inputs maximum of 255 characters to buffer (plus zero byte), flushes
 ;	excessive input until next CR encountered (for buffers less than
 ;	256 bytes long, buffer overflow is a possibility).
 
 xdef	rdstrz
 
 rdstrz:	movem.l D0-D3/A0-A2, -(SP)	; save registers
 	clr.w	D3			; initialize counter
 	movea.l	A0, A2			; save addr of buffer in A2
 rdstrz1:
 	jsr	rdchar			; input next character
 	cmp.b	#CR, D0			; CR?
 	beq	rdstrz5			; if so, terminate string
 	cmp.b	#BS, D0			; backspace?
 	bne	rdstrz2	
 	tst.w	D3			; if so, is buffer empty?
 	beq	rdstrz1			; if so, get next character
 	clr.b	-(A2)			; else, delete last character
 	sub.w	#1, D3			; decrement counter
 	bra	rdstrz1			; input another character
 rdstrz2:
 	cmp.w	#BUFSIZE-1, D3		; buffer full?
 	bne	rdstrz4
 rdstrz3:
 	jsr	rdchar			; if so,
 	cmp.b	#CR, D0			; discard input characters till
 	bne	rdstrz3			; CR found
 	bra	rdstrz5			; and terminate string
 rdstrz4:
 	add.w	#1, D3			; else, increment character count
 	move.b	D0, (A2)+		; and move input character to
 					;  next buffer position
 	bra	rdstrz1			; input another character
 rdstrz5:
 	clr.b	(A2)			; zero terminate
 	movem.l (SP)+, D0-D3/A0-A2	; restore registers
 	rts
 	
 ; Subroutine wrtstrz ( BufferAddress )
 ;   Receives address of zero-terminated string in A0.
 ;	Outputs characters of string on screen until zero byte is reached.
 
 xdef	wrtstrz
 
 wrtstrz:
 	movem.l D0/A0, -(SP)		; save registers
 wrtstrz1:
 	move.b	(A0)+, D0		; pass char to wrtchar
 	beq	wrtstrz2		; return when zero byte reached
 	jsr	wrtchar			; output character
 	bra	wrtstrz1		; get next byte
 wrtstrz2:
 	movem.l (SP)+, D0/A0		; restore registers
 	rts
 	
 ; Subroutine rdstrc ( BufferAddress )
 ;   Receives address of destination buffer in A0.
 ;	Inputs characters to buffer specified by destination address
 ;	until CR is read; places count byte at start of string; if backspace
 ;	encountered, deletes last character in buffer, backspace not stored.
 ;	Inputs maximum of 255 characters to buffer (plus count byte), flushes
 ;	excessive input until next CR encountered (for buffers less than
 ;	256 bytes long, buffer overflow is a possibility).
 
 xdef	rdstrc
 
 rdstrc:	movem.l D0-D3/A0-A3, -(SP)	; save registers
 	movea.l	A0, A2			; save addr of buffer in A2
 	movea.l	A0, A3			; and A3
 	tst.b	(A2)+			; increment pointer past count byte
 	clr.b	D3			; initialize counter
 rdstrc1:
 	jsr	rdchar			; input next character
 	cmp.b	#CR, D0			; CR?
 	beq	rdstrc5			; if so, terminate string
 	cmp.b	#BS, D0			; backspace?
 	bne	rdstrc2	
 	tst.b	D3			; if so, is buffer empty?
 	beq	rdstrc1			; if so, get next character
 	clr.b	-(A2)			; else, delete last character
 	sub.b	#1, D3			; decrement counter
 	bra	rdstrc1			; input another character
 rdstrc2:
 	cmp.b	#BUFSIZE-1, D3		; buffer full?
 	bne	rdstrc4
 rdstrc3:
 	jsr	rdchar			; if so,
 	cmp.b	#CR, D0			; discard input characters till
 	bne	rdstrc3			; CR found
 	bra	rdstrc5			; and terminate string
 rdstrc4:
 	add.b	#1, D3			; else, increment character count
 	move.b	D0, (A2)+		; and move input character to
 					;  next buffer position
 	bra	rdstrc1			; input another character
 rdstrc5:
 	move.b	D3, (A3)		; insert count byte
 	movem.l (SP)+, D0-D3/A0-A3	; restore registers
 	rts
 	
 ; Subroutine wrtstrc ( BufferAddress )
 ;   Receives address of count string in A0.
 ;	Outputs number of characters specified by count-byte.
 
 xdef	wrtstrc
 
 wrtstrc:
 	movem.l D0/D3/A0, -(SP)		; save registers
 	move.b	(A0)+, D3		; store count in D3
 wrtstrc1:
 	tst.b	D3			; all characters output?
 	beq	wrtstrc2		; then quit
 	move.b	(A0)+, D0		; pass char to wrtchar
 	jsr	wrtchar			; output character
 	sub.b	#1, D3			; decrement counter
 	bra	wrtstrc1		; get next byte
 wrtstrc2:
 	movem.l (SP)+, D0/D3/A0		; restore registers
 	rts
 	
 
 ; data for shell program
 
 saveregs: dcb.l	2, 0	; space for storing saved registers
 evbuf:	dcb.l	4, 0	; event buffer
 
 ; data for subroutine makewindow
 
 boundsrect:	dc.w	Y1, X1, Y2, X2		; the window rectangle in
 						;  global coordinates
 scrollrect:	dc.w	0, 0, WINDHEIGHT, WINDWIDTH ; the window rectangle
 						;  in local coordinates
 scrollrgnh:	dc.l	0			; handle for scroll region
 fontinfo:	dcb.w	4, 0			; buffer for record returned
 						;  by _GETFONTINFO
 lnht:		dc.w	0			; buffer for height of line
 						;  of text
 ; data for subroutine rdchar
 
 penloc:	dc.l	0		; storage for pen location coordinates
 regbuf:	dcb.l	3, 0		; storage for saved registers
 regbufe:
 
 ; data for subroutine rddec
 
 inbuf:	dcb.b	BUFSIZE, 0	; input buffer
 
 ; global storage
 
 window:	ds.l	1
 
 	end

info-mac@uw-beaver (12/22/84)

From: winkler@harvard.ARPA (Dan Winkler)

Here is a little assembly language i/o package developed for our
assembly language courses that use the Mac.  It was written by Jeff
Miller and is used by his QuickPrint and MacMelody programs.  

We were using an early version of the assembler then, but this should
work just fine with the newer versions.  The only difference I know of
is that the new assembler names its linker files .link but the old one
named them .ld.

Dan. (winkler@harvard)

[filed on <info-mac>io.asm]