EMU

Back when my only machine was an Amstrad PCW, I used it to take my first tentative steps onto the Internet. Within 24 hours of getting on-line, I realised that the VT100 emulation offered by programs such as COMM+ and QTERM was rather inadequate. Keyboard redefinition was missing, along with attributes like bold and underlining. Instead, these various attributes were all mapped to the single property of inverse video, making the use of programs like the popular text only Web browser Lynx very confusing. Furthermore, otherwise great programs like ZMP are let down by not having any VT100 emulation whatsoever.

And so EMU was born out of my own selfish need to browse the Web. Once I had an acceptable VT100 emulation working, I added support for the ISO-8859-1 character set, the preferred character set of the World Wide Web. The PCW actually already supports most of the Latin 1 characters, it's just that they're ordered differently within the PCW's BIOS, so using a hash table look-up system, I only needed to design 17 new characters.

Here, better late than never, is the source of the program. If you only want a binary version of the program, that's OK, too.

A note for users of QTERM: when making catch files, make sure that you don't use option 'J' to junk control characters, as this will remove important display codes from the catch file.

You can turn the VT100 emulation off and remove EMU from memory at any time by typing EMUOFF [RETURN].

If you use [STOP] to interrupt the display of a file containing VT100 escape codes, you may find that one or more highlighting attributes are still set. It is annoying to have to proceed with a combination of inverse video, bold and underlining, so EMU provides the EMURST command. If you type EMURST [RETURN], all textual emphasis will be turned off.

You may find that the remote system you connect with not only expects you to treat the data it sends you as VT100, but also expects to receive its input from you in VT100 form. This usually manifests itself in the refusal of the cursor keys to do anything useful in a menu on the remote system. Should this prove to be a problem, the EMUKEY command should provide the solution. Note that this will only work in combination with communications software that doesn't use the [EXIT] key for its own purposes. COMM+ and ZMP are examples of such programs, although the former allows an alternative key to be defined for this purpose.

If you use such a program, you will have to redefine the keys externally. The file EMU-B.KEY is provided for this purpose. Your CP/M master disc contains a program called SETKEYS, which you should use to install the keyboard definition file. Simply type SETKEYS EMU-B.KEY [RETURN] and the cursor keys will subsequently send the correct control codes.

Regardless of whether you use the EMUKEY command or the EMU-B.KEY file to redefine the keyboard, you will need to remove EMU from memory to restore the keys to their original state. Should it become necessary to return the keys to their default state whilst using EMU (e.g. because you have finished work on- line and now wish to use EMU off-line), you will first have to remove EMU and then reinstall it.

You should note that the built-in EMUOFF, EMURST and EMUKEY commands will not work from within a .SUB file or Z-system alias. If you wish to use any of these commands in a .SUB file or alias, you should instead use the program EMUSUB. This program takes a single parameter and has the following syntax:

Command Effect
EMUSUB /O send EMUOFF command to EMU
EMUSUB /R send EMURST command to EMU
EMUSUB /K send EMUKEY command to EMU


Technical notes

EMU works by redirecting the BIOS CONOUT routine to intercept all console output. This is necessary in order to make sure that the screen output of ALL programs is trapped and acted upon. Intercepting at the BDOS level is inadequate, because there can be no guarantee that another program will use the BDOS for its output. Unfortunately, this method has the disadvantage that output redirection can't be acted upon. It is thus impossible to use EMU to make a clean print-out of a file containing VT100 escape codes.

EMU's VT100 emulation isn't complete, but it is the best available for the PCW. Significant features not implemented are:

  1. Blink highlighting
  2. Alternate character sets
  3. Double width/height text
  4. Terminal interrogation responses

Although some remote systems attempt to interrogate the calling system as to its precise specification, a response does not appear to be essential. The interrogating system simply goes ahead with VT100 transmission anyway.

Bugs and incompatibility

  1. EMU causes a crash when ZMP (patched with ZMP15PCW.MAC dated 16/6/95) is run. This is because both EMU and ZMP make use of the alternate Z80 register set in their screen display routines. The version of ZMP15PCW.MAC dated 28/6/95 solves this complaint and is available by anonymous FTP from Demon. There should be no conflicts with other communications software, unless screen output goes directly via the BIOS without preserving the alternate registers.
  2. Any CP/M program which uses ESC M (delete line containing cursor and scroll rows below up one line) while EMU is installed will not behave as expected. This is because ESC M means 'reverse index' in VT100.
  3. The character produced by the sequence ESC ESC (lower case Tau) cannot be displayed while EMU is installed.

Acknowledgements

EMU's VT100 emulation is based on the specification outlined in the 'Digital VT100 Programming Reference Card'.

Details of the ISO-8859-1 specification were obtained from Martin Ramsch's excellent overview.

Further interesting details on ISO-8859-1 were obtained from the ISO-8859 FAQ by Michael Gschwind.

Assembling

EMU was written to be compiled with M$'s M80 assembler and linked with the standard Digital Research linker, LINK.COM. GENCOM is also needed to turn the relocatable code into an RSX, or TSR if you come from an M$-DOS background.

FYNDE is a CP/M implementation of UNIX's grep (or, if you prefer, M$-DOS's FIND) and is useful for locating entry points in the code to make sure they tally with the source. You will find it very useful if you intend on modifying the source to EMU.

In short, the following EMU.SUB file will make life somewhat easier when assembling. Note that it must be called EMU.SUB to work.


m80 =$0
link $0
m80 $02,$02=$02
link $02[op]
era $02.rsx
ren $02.rsx=$02.prl
gencom $0 $02
era $0?.rel
era $0?.sym
fynde $02.prn vtoffx:
fynde $02.prn emul:
fynde $02.prn conorg:

If you've got any questions about the code, you can drop me a line, but be warned that this was the last program for CP/M on the PCW that I ever programmed, and a few years have passed since then. In other words, the code looks barely any more familiar to me than to you.

EMUEQU.MAC

This file contains the equates used by all the other source files.


;EMUEQU.MAC - equates used by EMU1.MAC, EMU2.MAC, EMURST.MAC and EMUOFF.MAC

no	equ	0
yes	equ	not no
wrmboot	equ	0
BDOS	equ	5		;BDOS entry point
defDMA	equ	080h		;default DMA buffer
charram	equ	0b800h		;start of character matrix RAM
SCB	equ	0fb9ch		;System Control Block
emuins	equ	SCB+9		;4th user byte in SCB for use as installation status flag
conmode	equ	SCB+033h	;console mode. Bit 2 controls tab expansion
curDMA	equ	SCB+03ch	;address of current DMA buffer stored here
conout	equ	0fc0dh		;jump-table entry to BIOS CONOUT routine
userf	equ	0fc5ah		;address of BIOS USERFunction
teask	equ	0bfh		;offset to XBIOS TE ASK routine
setexp	equ	0d4h		;offset to XBIOS SET EXPAND routine
scrrun	equ	0e9h		;offset to XBIOS SCR RUN routine
print	equ	9
input	equ	10
nul	equ	0
beep	equ	7
tab	equ	9
lf	equ	10
cr	equ	13
ESC	equ	27
zpm3	equ	no		;set 'yes' if ZPM3 BDOS fitted and you're feeling lucky
iso8859	equ	yes		;set 'yes' if support for ISO-8859-1 (Latin 1) character
				;set required
xcls	equ	no		;ANSI systems expect the 'clear screen' sequence
				;(ESC [ 2 J) to additionally home the cursor. Some
				;systems also expect ASCII 12 to do this.
				;set 'yes' if you need support for such a system

EMU.MAC

We try and do as much as possible in this file, since this is the transient part of EMU. When run, this part does some sanity checking and character redefinition, and then exits, leaving EMU2 loaded as an RSX and the BIOS's CONOUT routine redirected through it.


title	EMU1 v1.02 2/12/95
subttl	(c) Ian Macdonald (ianmacd@xs4all.nl)

.comment |

EMU provides a rudimentary VT100 emulation for the Amstrad PCW's VT52 terminal. Whilst
the VT100 implementation is not complete, it is superior to that offered by comms
programs such as COMM+ and QTERM.

Commands available:

EMU 	installs the RSX and turns VT100 emulation on.
EMURST	resets highlighting attributes.
EMUOFF	resets highlighting attributes, turns VT100 emulation off and removes the RSX.
EMUKEY  configures cursor keys to produce VT100 keys.

v1.00 14/7/95 - first official release (identical to v0.14 beta)
v1.01  5/8/95 - bug which caused unimplemented escape codes to affect screen display
		under certain circumstances fixed
v1.02 2/12/95 - EMUKEY command added for redefining cursor keys to VT100
|

include	EMUEQU.MAC		;get equates

;The VTOFF, EMUL and CONORG entry points move according to the ZPM3 and ISO8859 equates
;in EMUEQU.MAC (which control conditional assembly in EMU2.MAC). The VTOFFX, EMUL and
;CONORG equates in this file control assembly of the correct addresses.

vtoffx	set	0c9h		;jump offset to relevant part of routine in RSX
emul	set	0d2h		;jump offset to routine in RSX
conorg	set	02e0h		;jump offset to routine in RSX

if zpm3
conorg	set	conorg+2
if1
.printx	* Assembling ZPM3 version of EMU1... *
endif
endif

if iso8859
vtoffx	set	vtoffx+00fh
emul	set	emul+031h
conorg	set	conorg+050h
if1
.printx	* Assembling ISO-8859-1 (Latin 1) character set version of EMU1... *
endif
endif

if xcls
conorg	set	conorg+10
if1
.printx * Assembling with non-standard 'clear screen' sequences... *
endif
endif

.z80
	ld	de,signon
	call	display		;display sign-on message
	ld	a,(emuins)
	cp	'E'		;is EMU already installed?
	jr	z,fail1		;if so, abort
	ld	a,(BDOS+2)
	cp	0c1h		;have we got enough common memory to play with?
	jr	c,fail2		;if not, bye-bye
success:ld	a,'E'
	ld	(emuins),a	;set installation flag
if iso8859
	ld	de,charram+180*8 ;180 is first character to replace
	ld	hl,newchar	;start of new character data
	ld	bc,6*8		;6 characters of each 8 bytes
	ld	a,082h
	di			;disable interrupts
	out	(0f2h),a	;switch in block 2 at 8000h-bfffh
	ldir			;load characters 180 to 185
	ld	de,charram+187*8 ;187 is next character to replace
	ld	c,2*8		;2 characters of each 8 bytes
	ldir			;load characters 187 to 188
	ld	de,charram+191*8 ;191 is next character to replace
	ld	c,1*8		;1 character of 8 bytes
	ldir			;load character 191
	ld	de,charram+220*8 ;220 is next character to replace
	ld	c,4*8		;4 characters of each 8 bytes
	ldir			;load characters 220 to 223
	ld	de,charram+252*8 ;252 is next character to replace
	ld	c,4*8		;4 characters of each 8 bytes
	ldir			;load characters 252 to 255
	ld	a,086h
	out	(0f2h),a	;replace block 6
	ei
endif
	ld	de,conorg+1
	ld	a,(BDOS+2)
	ld	h,a
	ld	l,0
	add	hl,de		;get actual address of CONORG in HL
	ld	(corg+1),hl	;store it
	ld	hl,(conout)
corg:	ld	(0000),hl	;save original CONOUT address
	ld	de,emul
	ld	h,a
	ld	l,0
	add	hl,de		;get address of RSX's EMUL routine in HL
	ld	(conout),hl	;redirect CONOUT via EMU
	ld	de,instal	;display installation message
display:ld	c,print
	jp	BDOS
fail1:	ld	de,alinst
	jr	exit
fail2:	ld	de,nomem
exit:	call	display		;display installation failure message
	ld	a,(BDOS+2)
	ld	h,a
	ld	l,vtoffx	;get address of relevant part of RSX's VTOFF routine
	jp	(hl)		;execute it

if iso8859
newchar:db	195,60,102,102,102,60,195,0	;international currency symbol
	db	0,0,0,126,6,0,0,0		;negation sign (logical NOT)
	db	126,0,0,0,0,0,0,0		;spacing macron
	db	0,24,24,126,24,24,126,0		;+/- sign
	db	24,36,8,16,60,0,0,0		;superscript 2
	db	24,36,8,36,24,0,0,0		;superscript 3
	db	0,0,204,204,204,204,246,192	;micro sign (lower case Mu)
	db	0,0,0,0,0,0,24,56		;spacing cedilla
	db	16,48,16,16,56,0,0,0		;superscript 1
	db	248,108,102,246,102,108,248,0	;upper case Eth
	db	0,0,0,108,56,108,0,0		;multiplication sign
	db	12,24,102,60,24,24,24,0		;upper case Y-acute
	db	224,124,102,102,102,124,224,0	;upper case Thorn
	db	192,96,252,24,124,198,124,0	;lower case Eth
	db	0,0,24,0,126,0,24,0		;division sign
	db	12,24,102,102,102,62,6,124	;lower case Y-acute
	db	0,240,96,124,102,124,96,96	;lower case Thorn
endif
signon:	db	'EMU '
if xcls
	db	'(ANSI) '
else
if not iso8859
	db	'(ASCII) '
endif
endif
	db	'v1.02 2/12/95 (c) Ian Macdonald (ianmacd@xs4all.nl)'
	db	cr,lf,lf,'$'
instal:	db	'VT100 emulation now on. Commands available:',cr,lf,lf
	db	'EMUOFF - uninstall emulator.',cr,lf
	db	'EMURST - reset highlighting attributes.',cr,lf
	db	'EMUKEY - install VT100 cursor keys.',cr,lf,'$'
alinst:	db	beep,'ERROR: EMU already installed.',cr,lf,'$'
nomem:	db	beep,'ERROR: not enough memory. Please remove some RSXes.',cr,lf,'$'

	end

EMU2.MAC

OK, this is the beef of the program. This part is the RSX that gets installed when EMU is run and handles all the emulation, command handling and restoration when EMU is removed from memory.

It's not always very pretty and I've probably broken some golden rules, but it had to be as fast as possible, so I optimised for speed instead of legibility.


title	EMU2 v1.02 2/12/95
subttl	(c) Ian Macdonald (ianmacd@xs4all.nl)

.comment |

EMU provides a rudimentary VT100 emulation for the Amstrad PCW's VT52 terminal. Whilst
the VT100 implementation is not complete, it is superior to that offered by comms
programs such as COMM+ and QTERM.

Commands available:

EMU 	installs the RSX and turns VT100 emulation on.
EMURST	resets highlighting attributes.
EMUOFF	resets highlighting attributes, turns VT100 emulation off and removes the RSX.
EMUKEY  configures cursor keys to produce VT100 keys.

v1.00 14/7/95 - first official release (identical to v0.14 beta)
v1.01  5/8/95 - bug which caused unimplemented escape codes to affect screen display
		under certain circumstances fixed
v1.02 2/12/95 - EMUKEY command added for redefining cursor keys to VT100

Further information:

1. Conditional assembly is provided to support the entire ISO-8859-1 (Latin 1) character
   set. This is the preferred character set on the Internet. The HTTP protocol used by
   World Wide Web browsers guarantees the integrity of 8 bit transmission. Furthermore,
   some people break the 'rule' of only using true ASCII (ISO-646) characters in e-mail
   and Usenet messages. As a consequence, characters with an ASCII value greater than 159
   are almost always incorrectly displayed by the PCW, which has a non-standard character
   set. Set the 'iso8859' equate in EMUEQU.MAC if required.
2. If you assemble EMU with support for the ISO-8859-1 character set (see point 1 above),
   then you will be unable to correctly display native PCW documents containing
   characters with ASCII values greater than 159. This is because these values are
   remapped by EMU to display the Latin 1 characters. Some characters are even redefined.
3. Conditional assembly is provided for the implementation of bold. If you have installed
   Simeon Cran's ZPM3 replacement BDOS, you have the option of setting the 'zpm3' equate
   in EMUEQU.MAC. This will give you a faster bold routine, but also makes the program
   slightly unstable. When assembled in this way, EMU may cause a crash if you
   subsequently run a program which extends into memory between 8000h and BFFFh. In
   practice, there will only be a danger of this if the program in question is larger
   than 32k. COMM+ (36k) seems to work without problems. If using EMU in this way, I
   strongly recommend that you make a point of immediately uninstalling EMU after using
   any application that requires it. However, I would strongly suggest that you don't use
   this option at all! Note that setting the 'zpm3' equate _guarantees_ a crash on
   systems still using the original Digital Research BDOS. In short, set this equate at
   your own risk!
4. Some systems erroneously expect the 'clear screen' sequence (ESC [ 2 J) to
   additionally home the cursor. I am also informed that some systems expect ASCII 12 to
   do the same. If you encounter such a system, set the 'xcls' equate in EMUEQU.MAC.
   These non-standard features will then be supported.
5. The 4th SCB user byte is used as an installation status flag. This could cause
   conflicts if also used by other programs. As a consequence, there is a very remote
   chance that EMU may erroneously report that it is already installed when you attempt
   to install it. Likewise, there is a slightly greater chance that double installation
   may occur. A software reset is highly recommended in this latter case.

Further improvements to be made:

Any features not required by the Lynx WWW browser have been omitted for speed.

1. Highlighting attribute 5 (blink) would be nice, but probably impossible on the PCW.
2. Alternate character sets would be nice, but what software needs them?
3. Double height/width would be nice, but again, what software needs them?
4. Terminal interrogation support, particularly 'What are you?' (ESC [ c or ESC [ 0 c).
   This is used by some BBS systems, but doesn't appear to be essential. The
   interrogating system simply goes ahead with VT100 transmission anyway after receiving
   a null response.
5. Implementation of scrolling regions, but probably superfluous. This would also be
   destructive if EMU was installed under a program which defined its own windows, as the
   PCW cannot cope with multiple windows.

Bugs and incompatibility:

1. EMU causes a crash when ZMP patched with ZMP15PCW.MAC dated 16/6/95 is run. This is
   because both EMU and ZMP make use of the Z80 alternate register set in their screen
   display routines. The version of ZMP15PCW.MAC dated 28/6/95 solves this. There should
   be no conflicts with other communications software, unless screen output goes directly
   via the BIOS, using the alternate registers without preserving them.
2. Any CP/M program which uses ESC M (delete line containing cursor and scroll rows below
   up one line) while EMU is installed will not behave as expected. This is because ESC M
   means 'reverse index' in VT100.
3. The character produced by the sequence ESC ESC (lower case Tau) cannot be displayed
   while EMU is installed.
|

include	EMUEQU.MAC		;get equates

if zpm3
  if1
    .printx	* Assembling ZPM3 version of EMU2... *
  endif
endif

if iso8859
  if1
    .printx	* Assembling ISO-8859-1 (Latin 1) character set version of EMU2... *
  endif
endif

if xcls
  if1
    .printx	* Assembling with non-standard 'clear screen' sequences... *
  endif
endif

.z80
	;standard RSX header

serial:	db	0,0,0,0,0,0
start:	jp	rsx
next:	jp	next
prev:	dw	7
remove:	db	no
nonbank:db	0
name:	db	'EMU     '
loader:	db	0
spare:	ds	2

	;general housekeeping

rsx:	ld	hl,conmode
	set	2,(hl)		;turn off tab expansion (just doing it is much quicker
				;than testing whether a print function is being called).
				;we must repeatedly turn it off in case another program
				;turns it back on
	ld	a,c
	cp	10		;BDOS 10 ?
	jr	nz,next		;if not, pass to BDOS (or next RSX)
	ld	(stadd),sp	;save calling program's stack
	ld	sp,tstack	;create our own
	push	de		;save BDOS 10 buffer location
	call	next		;get line input
	pop	hl		;get buffer location in HL
	ld	sp,(stadd)	;restore stack
	ld	a,h
	or	l		;is buffer at 0 ?
	jr	nz,getnr	;if not, standard call used
	ld	hl,(curDMA)	;otherwise, use current DMA buffer
getnr:	inc	hl
	ld	a,(hl)		;get number of bytes in buffer
	cp	6		;EMUOFF and EMURST commands = 6 characters each
	ret	nz		;if different, return to calling program
	ex	de,hl		;get buffer in DE
	ld	hl,command	;start of command table
	ld	bc,6		;6 bytes in each command
loop1:	call	testch		;test for valid command
	jr	z,vtoff		;if fully matched, EMUOFF
	inc	hl
	inc	hl		;otherwise, bump up command table pointer
	ld	bc,3		;3 characters to test (RST)
	dec	de		;prepare buffer pointer
loop2:	call	testch		;test for valid command
if iso8859
	jp	z,exit		;matched EMURST: turn attributes off and exit
else
	jr	z,exit		;matched EMURST: turn attributes off and exit
endif
	inc	hl             
	inc	hl		;otherwise, bump up command table pointer
	ld	bc,0303h	;C (decremented by CPI) mustn't get in the way of B 
loop3:	ld	a,(de)		;get input character
	or	32		;convert to lower case
	cpi			;compare with command table
	ret	nz		;return to calling program if no match
	inc	de		;bump up buffer pointer
	djnz	loop3		;loop if we've still got a match
emukey:	ld	bc,09103h	;expansion token 091h, 3 characters long
	ld	hl,curup	;address of new data
	call	setkey		;install token
	ld	bc,09303h	;expansion token 093h, 3 characters long
	ld	hl,curlft	;address of new data
	call	setkey		;install token
	ld	bc,09403h	;expansion token 094h, 3 characters long
	ld	hl,currgt	;address of new data
	call	setkey		;install token
	ld	bc,09603h	;expansion token 096h, 3 characters long
	ld	hl,curdn 	;address of new data
	call	setkey		;install token
	rst	wrmboot		;return to CP/M via a warm boot
setkey:	call	userf
	dw	setexp
	ret
testch:	inc	de		;bump up buffer pointer
	ld	a,(de)		;get input character
	or	32		;convert to lower case
	cpi			;compare with command table
	ret	po		;exit loop if we've had all the characters (Z flag set)
	jr	z,testch	;loop if we've still got a match
	ret			;otherwise, return (Z flag not set)
vtoff:
if iso8859
	ld	de,charram+180*8 ;180 is first character to replace
	ld	hl,orgchar	;start of original character data
	ld	bc,restore	;address of routine to restore characters
	call	userf		;call it
	dw	scrrun
endif
defkey:	ld	bc,09101h	;expansion token 091h, 1 character long
	ld	hl,defup
	call	setkey		;restore token
	ld	bc,09301h	;expansion token 093h, 1 character long
	ld	hl,deflft
	call	setkey		;restore token
	ld	bc,09401h	;expansion token 094h, 1 character long
	ld	hl,defrgt
	call	setkey		;restore token
	ld	bc,09601h	;expansion token 096h, 1 character long
	ld	hl,defdn
	call	setkey		;restore token
	ld	hl,(conorg+1)
	ld	(conout),hl	;restore original CONOUT address
	xor	a
	ld	(emuins),a	;set uninstallation flag
vtoffx:	ld	a,yes
	ld	(remove),a	;set flag to remove RSX
exit:	call	noatt		;turn all highlighting attributes off
	rst	wrmboot		;do a warm boot

if iso8859
restore:ld	bc,6*8		;6 characters of each 8 bytes
	ldir			;load characters 180 to 185
	ld	de,charram+187*8 ;187 is next character to replace
	ld	c,2*8		;2 characters of each 8 bytes
	ldir			;load characters 187 to 188
	ld	de,charram+191*8 ;191 is next character to replace
	ld	c,1*8		;1 characters of 8 bytes
	ldir			;load character 191
	ld	de,charram+220*8 ;220 is next character to replace
	ld	c,4*8		;4 characters of each 8 bytes
	ldir			;load characters 220 to 223
	ld	de,charram+252*8 ;252 is next character to replace
	ld	c,4*8		;4 characters of each 8 bytes
	ldir			;load characters 252 to 255
	ret
endif
	;start of the emulator proper

emul:	ld	hl,hadESC
	ld	a,c		;get character in A
	cp	ESC		;is it an ESCape?
	jr	z,isESC		;if so, skip ahead
	ld	a,(hl)
	or	a		;are we in an ESCape sequence?
	jp	z,norm		;if not, display it as per usual
	ld	a,(lstch)	;get previous character
	cp	'?'		;was it a question-mark?
	jr	nz,ftest	;if not, conduct further tests
	ld	a,c		;get current character
	cp	'9'+1		;is it probably a digit?
	ret	c		;if so, return
	jp	resfgs		;otherwise, end of ESC [ ? sequence
isESC:	ld	(hl),a		;otherwise, flag ESC
	ret			;return without displaying

	;discover from previous character in ESC sequence what to do with current one

ftest:	cp	'['
	jr	z,square
	cp	'#'
	jp	z,hash
	cp	'('
	jp	z,lftbr
	cp	')'
	jp	z,rgtbr
	ld	a,c		;if none of the above, get current character
	ld	(lstch),a	;store it as last character

	;if current character is 2nd character in longer ESCape sequence, return without
	;displaying it

	cp	'['
	ret	z
	cp	'£'
	ret	z
	cp	'('
	ret	z
	cp	')'
	ret	z
	;cp	'E'		;commented out for VT52 compatibility
	;jr	z,nline
	cp	'M'
	jr	z,revidx	;reverse index
	cp	'7'
	jr	z,savcur	;save cursor position
	cp	'8'
	jr	z,rescur	;restore cursor position
	cp	'<'
	jr	c,dochar	;less than '<'
	cp	'>'+1
	jp	c,resfgs	;'>' or less (weed out ESC <, ESC = and ESC > )

dochar:	exx			;save character to be printed (in C)
	ld	c,ESC
	call	enorm		;send ESC
	exx			;retrieve control character
	call	enorm		;print it
	jp	resfgs		;reset flags

nline:	;ld	c,'B'
	;jr	dochar		;commented out for VT52 compatibility
revidx:	ld	c,'I'
	jr	dochar		;reverse index
savcur:	ld	c,'j'
	jr	dochar		;save cursor position
rescur:	ld	c,'k'
	jr	dochar		;restore cursor position

square:	ld	a,c		;get current character in A
	cp	'0'
	jr	c,notdgt	;less than a digit
	cp	'9'+1
	jr	nc,notdgt	;more than a digit
	sub	'0'		;convert to real figure
	ld	c,a		;save in C
	ld	hl,(lstdgt)	;get address of last digit
	ld	a,(haddgt)
	or	a		;already had a digit?
	jr	z,stordgt	;if not, store this one
	ld	a,(hl)		;otherwise, get last one, which must represent tens
	add	a,a		;x 2
	ld	b,a		;save result in B
	add	a,a		;x 4
	add	a,a		;x 8
	add	a,b		;x 10
	add	a,c		;+ units
	ld	(hl),a		;store the new figure
	ret
stordgt:ld	(hl),c		;store this digit
	inc	a
	ld	(haddgt),a	;set flag to signify presence of digit
	ld	hl,nrdgts
	inc	(hl)		;bump digit count
	ret

notdgt:	ld	a,(nrdgts)
	or	a
	ld	a,c		;get current character in A
	jr	z,nodgt		;no digit pending
	cp	';'
	jr	z,semic		;digit separator
	cp	'm'
	jr	z,chatt		;set character attributes
	cp	'A'
	ret	c		;less than 'A': ignore
	cp	'D'+1
	jp	c,cursor	;A, B, C or D: move cursor
	cp	'H'
	jp	z,curxy		;move cursor to X,Y
	cp	'J'
	jp	z,erascr	;erase section of screen
	cp	'K'
	jp	z,eraline	;erase section of line
	cp	'f'
	jp	z,curxy		;move cursor to X,Y
	;ret			;ignore any other codes
	jp	resfgs		;ignore any other codes

nodgt:	cp	'H'
	jr	z,dochar
	cp	'J'
	jr	z,dochar	;send straight to screen (same as VT52)
	cp	'K'
	jr	z,dochar	;send straight to screen (same as VT52)
	cp	'm'
	jr	z,noatt

	cp	';'
	ret	z		;digit separator
	cp	'?'
	jr	z,qmark

	cp	'A'
	;ret	c		;less than 'A'
	jp	c,resfgs	;less than 'A'
	cp	'D'+1
	;ret	nc		;greater than 'D'
	jp	nc,resfgs	;greater than 'D'
	jp	dochar

semic:	ld	hl,(lstdgt)
	inc	hl		;bump pointer to last digit
	ld	(lstdgt),hl
	xor	a
	ld	(haddgt),a	;reset 'had a digit' flag
	ret

chatt:	ld	a,(nrdgts)
	ld	b,a		;get number of attributes in B
	ld	hl,dgtbuf	;get start of attribute list in HL
attlp:	ld	a,(hl)		;get attribute
	cp	1		;bold?
	jr	nz,ntest	;if not, conduct next test
	ld	(bold),a	;bold on
	jr	natt		;get next attribute
ntest:	cp	5		;blink?
	jr	z,natt		;if so, get next attribute: blink not implemented
	cp	8		;attribute > 7 ?
	jr	nc,natt		;if so, ignore
	ld	(curatt),hl	;save address of current attribute
	ld	(hl),b		;convenient place to save attribute count
	or	a		;attribute 0 ? (all attributes off)
	jr	nz,ntest2	;if not, next test
	call	noatt		;turn all attributes off
	jr	recov		;recover attribute address and count
ntest2:	cp	7		;highlight?
	jr	z,revon		;if so, turn on
	ld	c,'r'		;otherwise, assume attribute 4 (underline on)
	jr	dchar
revon:	ld	c,'p'		;inverse video
dchar:	call	dochar		;set attribute
recov:	ld	hl,(curatt)	;recover current attribute address
	ld	b,(hl)		;recover attribute count
natt:	inc	hl		;address of next attribute
	djnz	attlp		;get next attribute
	jp	resfgs		;reset flags

noatt:	xor	a
	ld	(bold),a	;bold off
	ld	c,'u'
	call	dochar		;underline off
	ld	c,'q'
	jp	dochar		;inverse video off

qmark:	ld	(lstch),a	;mark as last char. ('?' ESCapes not implemented)
	ret

curxy:	ld	c,'Y'
	call	dochar		;send ESC for X,Y sequence
	ld	a,(dgtbuf)	;get Y co-ordinate
	call	setco		;set Y co-ordinate
	ld	a,(dgtbuf+1)	;get X co-ordinate
setco:	add	a,31		;add offset
	ld	c,a
	jp	enorm		;set X co-ordinate
				;flags already reset by ESC Y

cursor:	ld	(direct+1),a	;store cursor direction
	ld	a,(dgtbuf)
curtab:	ld	b,a		;get number of positions to move
loop4:	exx			;save position count
	ld	c,ESC
	call	enorm		;send ESCape
direct:	ld	c,'?'		;cursor direction is patched here
	call	enorm		;move cursor
	exx			;retrieve position count
	djnz	loop4		;go around again
	jr	resfgs		;reset flags

eraline:ld	a,(dgtbuf)
	or	a
	jp	z,dochar	;ESC [ 0 K means erase to end of line
	dec	a
	jr	z,eralcur	;ESC [ 1 K means erase line up to cursor
eralb:	ld	c,'l'
	jp	dochar		;ESC [ 2 K means erase entire line containing cursor
eralcur:ld	c,'o'
	jp	dochar

erascr:	ld	a,(dgtbuf)
	or	a
	jp	z,dochar	;ESC [ 0 J means erase to end of screen
	dec	a
	jr	z,scrcur	;ESC [ 1 J means erase from top of screen to cursor
erasb:
if xcls
	ld	c,'H'
	call	dochar
endif
	ld	c,'E'
	jp	dochar		;ESC [ 2 J means erase entire screen
scrcur:	ld	c,'d'
	jp	dochar
	
	;none of the following 3 ESCapes is implemented, so we let them drop into the
	;register reset routine

hash:	;jr	resfgs
lftbr:	;jr	resfgs
rgtbr:	;jr	resfgs		

resfgs:	xor	a
	ld	hl,hadESC
	ld	(hl),a		;zero hadESC
	inc	hl
	ld	(hl),a		;zero haddgt
	inc	hl
	ld	(hl),a		;zero lstch
	inc	hl
	ld	(hl),a		;zero nrdgts
	inc	hl		;HL = lstdgt
	ld	de,dgtbuf
	ld	(hl),e
	inc	hl
	ld	(hl),d		;reset last digit pointer to start of digit buffer
	ret

norm:	ld	a,c
	cp	tab		;is character a tab?
	jr	z,istab		;if so, take care of our own tab expansion
if xcls
	cp	12
	jp	z,erasb
endif
	cp	' '+1		;is it a control character or a space?
	jr	c,enorm		;if so, don't bother to make it bold
if iso8859
	sub	160		;value of first ISO-8859-1 character
	jr	c,tbold		;if lower, skip ahead
	ld	d,0
	ld	b,32
	sub	b		;ASCII > 191 ?
	jr	nc,accent	;if so, accented character
	add	a,b		;otherwise, readjust
	ld	hl,lookup	;start of look-up table
	jr	zerob		;get character
accent:	inc	a		;no upper case Scharfes S
	ld	hl,accents	;start of accented characters in look-up table
	cp	b		;is it lower case?
	jr	c,upcase	;if not, skip ahead
	sub	b		;adjust for correct position in look-up table
zerob:	ld	b,d		;zero B for lower case
upcase:	ld	e,a		;offset to character in DE
	add	hl,de		;get address in look-up table
	ld	a,(hl)		;look up accented character
	sub	b		;if necessary, make it upper case
	ld	c,a		;put it in C
tbold:
endif
	ld	a,(bold)
	or	a
	jr	z,enorm		;bold not on: process immediately
	ld	de,charram	;start of character RAM
	ld	a,c		;get character in A
	ld	h,0
	ld	l,a		;get character in HL
	add	hl,hl
	add	hl,hl
	add	hl,hl		;x 8 (multiply ASCII value by 8)
	add	hl,de		;get address of bit-map in character RAM
	ld	(chadd),hl	;save it
	ld	de,cchar	;address to save existing bit-map
if zpm3
	ld	bc,8		;8 bytes of character data
	ex	af,af'		;save ASCII value
	ld	a,082h
	di			;disable interrupts
	out	(0f2h),a	;switch in block 2 at 8000h-bfffh
	ldir			;save bit-map
	ld	hl,(chadd)	;restore its address in character RAM to HL
	ld	d,h
	ld	e,l		;make a copy of the address in DE
	ld	bc,0808h	;8 bytes to make bold (C is for later)
mbold:	ld	a,(hl)		;get character data
	rra			;rotate data out of true
	or	(hl)		;superimpose original
	ld	(hl),a		;save it
	inc	hl		;get next byte of character data
	djnz	mbold		;loop
	ld	a,086h
	out	(0f2h),a	;replace block 6
	ei
	ex	af,af'		;get ASCII value back in A
	exx			;save BC and DE
	ld	c,a		;get character in C
	call	enorm		;display bold character
	exx			;restore BC and DE
	ld	hl,cchar	;start of saved bit-map
	ld	a,082h
	di			;disable interrupts
	out	(0f2h),a	;switch in block 2 at 8000h-bfffh
	ldir			;restore original bit-map
	ld	a,086h
	out	(0f2h),a	;replace block 6
	ei
	ret
else
	ld	bc,scrrt1
	call	userf		;save bit-map of character and make bold
	dw	scrrun
	call	enorm		;display bold character
	ld	bc,scrrt2
	call	userf		;restore original bit-map
	dw	scrrun
	ret
scrrt1:	ld	bc,8		;8 bytes of character data
	ldir			;save bit-map
	ld	c,a		;save ASCII value of character in C
	ld	hl,(chadd)	;restore its address in character RAM to HL
	ld	b,8		;8 bytes to make bold
mbold:	ld	a,(hl)		;get character data
	rra			;rotate data out of true
	or	(hl)		;superimpose original
	ld	(hl),a		;save it
	inc	hl		;get next byte of character data
	djnz	mbold		;loop
	ret
scrrt2:	ld	de,(chadd)	;address in character RAM to copy back to
	ld	hl,cchar	;start of saved bit-map
	ld	bc,8		;8 bytes to restore
	ldir			;restore original bit-map
	ret
endif
istab:	ld	a,'C'
	ld	(direct+1),a	;set direction for 'cursor right'
	call	userf
	dw	teask		;get current cursor column in L
	ld	a,l
	cpl			;complement to obtain 255 minus L
	and	00000111b	;only least significant 3 bits are important (7 minus L)
	inc	a		;add one to obtain true figure (8 minus L)
	jp	curtab		;move cursor to next tab stop (assuming 8 columns apart -
				;tab setting/clearing not implemented)
enorm:	
conorg:	jp	0000		;jump to BIOS CONOUT (address patched by EMU1)

command:db	'emuoffrstkey'	;3 commands: EMUOFF, EMURST and EMUKEY
curup:	db	ESC,'[A'	;VT100 cursor control codes
curlft:	db	ESC,'[D'
currgt:	db	ESC,'[C'
curdn:	db	ESC,'[B'
defup:	db	'_' and 01fh	;PCW cursor control codes
deflft:	db	'A' and 01fh
defrgt:	db	'F' and 01fh
defdn:	db	'^' and 01fh

if iso8859
.comment |
	Look-up table for remapping ISO-8859-1 (Latin 1) characters.
	Upper case accented letters are obtained by subtracting 32
	from the values in the table.

	        PCW		 ISO-8859-1	Character
	        ===		 ==========	=========
|
lookup:	db	' '		;    160	non-breaking space
	db	175		;    161	inverted exclamation mark
	db	177		;    162	cent sign
	db	163		;    163	pound sign
	db	180		;    164	international currency symbol
	db	189		;    165	yen sign
	db	'|'		;    166	broken vertical bar
	db	166		;    167	section sign
	db	178		;    168	spacing diaeresis
	db	164		;    169	copyright sign
	db	160		;    170	feminine ordinal indicator
	db	171		;    171	left double guillemet
	db	181		;    172	negation sign (logical NOT)
	db	'-'		;    173	soft hyphen
	db	190		;    174	registered sign
	db	182		;    175	spacing macron
	db	162		;    176	degree sign
	db	183		;    177	+/- sign
	db	184		;    178	superscript 2
	db	185		;    179	superscript 3
	db	179		;    180	spacing acute
	db	187		;    181	micro sign (lower case Mu)
	db	165		;    182	paragraph sign
	db	144		;    183	middle dot
	db	188		;    184	spacing cedilla
	db	191		;    185	superscript 1
	db	161		;    186	masculine ordinal indicator
	db	172		;    187	right double guillemet
	db	168		;    188	1/4 fraction
	db	169		;    189	1/2 fraction
	db	170		;    190	3/4 fraction
	db	174		;    191	inverted question mark
accents:db	186		;    223	Scharfes S
	db	234		;    224	A-grave
	db	224		;    225	A-acute
	db	229		;    226	A-circumflex
	db	250		;    227	A-tilde
	db	240		;    228	A-diaeresis
	db	247		;    229	A-ring
	db	246		;    230	AE-diphthong
	db	245		;    231	C-cedilla
	db	235		;    232	E-grave
	db	225		;    233	E-acute
	db	230		;    234	E-circumflex
	db	241		;    235	E-diaeresis
	db	236		;    236	I-grave
	db	226		;    237	I-acute
	db	231		;    238	I-circumflex
	db	242		;    239	I-diaeresis
	db	252		;    240	Icelandic Eth
	db	249		;    241	N-tilde
	db	237		;    242	O-grave
	db	227		;    243	O-acute
	db	232		;    244	O-circumflex
	db	251		;    245	O-tilde
	db	243		;    246	O-diaeresis
	db	253		;    247	division sign
	db	248		;    248	O-slash
	db	238		;    249	U-grave
	db	228		;    250	U-acute
	db	233		;    251	U-circumflex
	db	244		;    252	U-diaeresis
	db	254		;    253	Y-acute
	db	255		;    254	Icelandic Thorn
	db	239		;    255	Y-diaeresis
orgchar:db	16,56,108,198,0,0,0,0		;spacing circumflex
	db	0,198,204,24,32,91,219,0	;per thousandth
	db	64,192,70,73,70,9,6,0		;1/8 fraction
	db	224,16,102,25,230,9,6,0		;3/8 fraction
	db	240,128,230,25,230,9,6,0	;5/8 fraction
	db	240,16,38,73,134,9,6,0		;7/8 fraction
	db	0,56,108,198,198,108,56,0	;open circle
	db	0,56,124,254,254,124,56,0	;full circle
	db	251,85,81,81,0,0,0,0		;trademark sign
	db	192,48,12,48,204,48,192,0	;greater than or equal to
	db	6,24,96,24,102,24,6,0		;less than or equal to
	db	6,12,126,24,126,48,96,0		;not equal to
	db	0,0,50,76,0,126,0,0		;approximately equal to
	db	8,12,254,7,254,12,8,0		;double shafted right arrow
	db	16,48,127,224,127,48,16,0	;double shafted left arrow
	db	0,36,126,195,126,36,0,0		;double shafted left/right arrow
	db	0,126,0,126,0,126,0,0		;equivalent to
endif
hadESC:	ds	1		;flag for ESCape character
haddgt:	ds	1		;flag for 'had a digit'
lstch:	ds	1		;store for last character
nrdgts:	ds	1		;store for number of digits in buffer
lstdgt:	dw	dgtbuf		;pointer to current digit in buffer
dgtbuf: ds	4		;digit buffer
curatt:	ds	2		;pointer to current attribute
bold:	ds	1		;flag for bold attribute
chadd:	ds	2		;address in character RAM of bold character
stadd:	ds	2		;address of calling program's stack
cchar:	ds	8		;store for character data when displaying in bold
	ds	8		;stack used by BDOS 10
tstack:

	end

EMUOFF.MAC


title	EMUOFF v1.00 23/6/95
subttl	(c) Ian Macdonald (ianmacd@xs4all.nl)

.comment |

Program to uninstall EMU from within a .SUB file or alias.

|

include	EMUEQU.MAC		;get equates

.z80
	ld	de,defDMA
	ld	hl,ststr
	ld	bc,endstr-ststr
	ldir			;relocate EMUOFF command in default DMA buffer
	ld	c,input
	ld	de,0		;use current DMA, i.e. default
	jp	BDOS		;send EMUOFF command to RSX

ststr:	db	6,0,'emuoff',cr,nul
endstr:

	end

EMURESET.MAC


title	EMURESET v1.00 23/6/95
subttl	(c) Ian Macdonald (ianmacd@xs4all.nl)

.comment |

Program to reset EMU's highlighting attributes from within a .SUB file or alias

|

include	EMUEQU.MAC		;get equates

.z80
	ld	c,print
	ld	de,signon
	jp	BDOS		;turn off attributes and display sign-on message

signon:	db	ESC,'[m'	;VT100 escape code for 'turn all attributes off'

	db	'EMURESET v1.00 23/6/95 (c) Ian Macdonald (ianmacd@xs4all.nl)',cr,lf
	db	lf,'Highlighting attributes now reset.',cr,lf,'$'

	end

EMUSUB.MAC


title	EMUSUB v1.00 3/12/95
subttl	(c) Ian Macdonald (ianmacd@xs4all.nl)

.comment |

Program to send commands to EMU from within a .SUB file or Z-system alias.

|

include	EMUEQU.MAC		;get equates

.z80

	ld	hl,defDMA
	ld	a,(hl)		;get number of characters in command tail
	cp	3		;is it 3?
	jr	nz,help		;if not, display help text
	inc	hl
	inc	hl		;move pointer to first character in command tail
	ld	a,(hl)
	cp	'/'		;is it '/'?
	jr	nz,help		;if not, display help text
	inc	hl		;move pointer to parameter
	ld	a,(hl)
	cp	'O'		;is it 'O'?
	jr	z,emuoff	;if so, EMUOFF
	cp	'R'		;is it 'R'?
	jr	z,emurst	;if so, EMURST
	cp	'K'		;is it 'K'?
	jr	z,emukey	;if so, EMUKEY
				;otherwise, display help text
help:	ld	de,htxt
prt:	ld	c,print
	jp	BDOS		;turn off attributes and display sign-on message

emuoff:	ld	hl,str1
emuk:	ld	de,defDMA
	ld	bc,str2-str1	;string length always the same
	ldir			;relocate command in default DMA buffer
	ld	c,input
	ld	de,0		;use current DMA, i.e. default
	jp	BDOS		;send command to RSX

emukey:	ld	hl,str2
	jr	emuk

emurst:	ld	de,rsttxt
	jr	prt

str1:	db	6,0,'emuoff',cr,nul
str2:	db	6,0,'emukey',cr,nul
rsttxt:	db	ESC,'[m$'	;VT100 escape code for 'turn all attributes off'

htxt:	db	'EMUSUB v1.00 3/12/95 (c) Ian Macdonald (ianmacd@xs4all.nl)',cr,lf,lf
	db	'Commands available:',cr,lf,lf
	db	'EMUSUB /O - uninstall emulator.',cr,lf
	db	'EMUSUB /R - reset highlighting attributes.',cr,lf
	db	'EMUSUB /K - install VT100 cursor keys.',cr,lf,'$'

	end

© 1998-2000. Page last updated 24th January 2000