Back to firmware
Look at a test program for a screen dump on the printer. I never tested this tool so I do not know if it's working in the given form.
Joyce screen memory

An article so exciting it'll roake your toes curl

By Cliff Lawson, 1986 Amstrad Consuroer Electronics plc.

Oh no, they haven't let him loose again have they ?
I'll assume you've already read the first bit about redefining Joyce characters. So you already know what USERF and SCR RUN ROUTINE are. In that same bank of memory as the character matrices is a small chunk of memory known as the roller RAM and a bigger chunk that holds the screen pixel information. The roller RAM sits at #B600 and consists of 256 word addresses which are the encoded start addresses for each pixel line on the screen. That is

B600 address of pixel line 1
B602 address of pixel line 2
B604 address of pixel line 3
:
:
B7FE address of pixel line 256

These addresses are actually a packed 17bit address, this is possible because bit 3 would always zero. In order to get the absolute address in the screen environment, one takes the roller RAM entry, masks out the bottom 3 bits, shifts the resultant one position left then puts back the original bottom three bits. The following is a routine that can be used to move a block of memory from TPA to screen memory.


As text file - 8080 or Z80
; The following is a routine which will be mved into common memory
; and later executed by SCR RUN ROUTINE. If included within an RSX
; then it is not necessary to move it to comnon (because it will lie
;  there anyway)
;
;
; entry conditions:
;
;   hl=address of data for one line of characters
;    e=character line nunber
;

roller  equ     0B600h
linelen equ     720

docommon:
	push	h
	mvi	h,0
	mov	l,e	;hl=line num
	dad	h	;hl=2*e
	dad	h	;hl=4*e
	dad	h	;hl=8*e
	dad	h	;hl=16*e
	lxi	d,roller
	dad	d	;hl=roller+16*line num
;
	mov	e,m
	inx	h
	mov	d,m	;de=encoded address
;
	mov	a,e	;hold on to bottom few bits
	xchg		;hl=encoded address
	dad	h	;hl=(encoded address)*2
	ani	7	;a=botton 3 bits of original encoded addr.
	ora	l	;combine with bottom of doubled encoded addr.
	mov	l,a	;put this back into hl.
	xchg		;transfer result to de - destination
;
	pop	h	;recover source address
	lxi	b,linelen
	ldir		;move line from buffer to screen
	ret
;
This general routine can be used for either transfer to or from the screen memory. A slightly modified version is included in the enclosed Joyce screen dump program. It should be noted that the roller RAM can be interrogated to find absolute screen addresses but it would not be wise to attempt to change it as CP/M may becorae very canfused, particularly when it comes to produce System prompts on line 31 ... 'Retry, Ignore or Cancel ?' etc.

To understand exactly how the screen memory appears to the programmer it is necessary to know that the display circuitry scans every eigth byte from a given pixel line start address. So if 8 consecutive roller RAM entries point at eight successive bytes in the screen memory then the display will appear characterwise. That is :

                        Column 0   Column 1  ....  Column 89

Top row of character    Byte 0     Byte 8          Byte 712
                        Byte 1     Byte 9          Byte 713
                        :          :               :
                        Byte 6     Byte 14         Byte 718
Bottom row of char.     Byte 7     Byte 15         Byte 719

And the roller RAM would be :

ROLLER       address of Byte 0
ROLLER+2     address of Byte 1
ROLLER+4     address of Byte 2
:
ROLLER+12    address of Byte 6
ROLLER+14    address of Byte 7

ROLLER+16    address of Byte 720
ROLLER+18    address of Byte 721

The actual bytes of pixel information are held in exactly the same form as those for the character matrices so that the most significant bit is for the leftmost pixel in that group of eigth and the least significant bit is for the rightmost one.

An interesting side effect of all this and something you might like to try if you have already got the character redefinition programs working is to change the equate that defines 'charmat' from its current value (#B800, the address of the character matrices) to #9000, somewhere in the middle of the screen. When the program is executed you should see those character definitions instantly appear an the screen. This is because the layout of the character matrices (which every one understands !) is identical to the screen memory layout (which no one understood until 1 minute ago when I just told you !).

Rumour has it that the commodore and bbc also use a similar scheme, though I've never actually heard of these manufacturers names before (Of course).
As mentioned above, I have included a screen dump program here that shows an example of reading bytes from the screen memory. To actually make use of this the following steps should be followed. Now I know some of you will have two drives so I'm not going to attempt to explain which discs go where. I assume if you've got this far then you can work it out anyway. :

1) Create a file containing the listing

2) Assemble it using :

RMAC B:JOYCEDMP $PZSZRM

3) Link it to generate a page relocatable file :

LINK M:JOYCEDMP[OP $SZOB]

4) Rename the .PRL file to become a .RSX file :

REN JOYCEDMP.RSX=JOYCEDMP.PRL

5) Generate a null .COM file with :

GENCOM B:JOYCEDMP [NULL]

6) Install the RSX by typing :

JOYCEDMP

7) Dump the screen by typing :

[ALT] [ O [RETURN]

8) The dump can be stopped by holding a key down

9) The lighter version can be started with :

[ALT] [ P [RETURN]

Another hyper exciting installment reaches its mind bending conclusion and so we leave you, heavenly choirs rejoicing in the firmamnent as the two lovers ride towards a sun draped horizon in there haevily bejewelled coach and four. A tear forms in the eye of one or two of the on lookers and then the skies open up and a large foot callously crushes the lot of them. TTFN.
As text file - 8080 or Z80
;test

;
; Joyce screen dump using ESC L n1 n2 to give about 3/4 width dump
;
; program by Cliff Lawson, 1986 Amstrad Consumer Electronics plc.
;
; This is inplemented as a null .COM file with attached RSX that is set
; to renain in memory. It intercepts BOOS function 2 and if an escape
; character is printed then the dump springs into life. Two types of dump
; are supported. ESC O gives a heavy print while ESC P is more of a "draft"
; quality dump. The heavy one works by printing the info, once, performing
; a fractional line feed then overprinting followed by the rest of a conplete
; line feed.
; A new BDOS call 73 (as always) is implemented so that the RSX can be
; removed.
;
; Use  RMAC B:JOYCEDMP $PZSZRM
;      LINK M:JOYCEDMP[OP $SZRB]
;      REN JOYCEDMP.RSX=JOYCEDMP.PRL
;      GENCOM B:JOYCEDMP [NULL]
;

bdos		equ	5
listout		equ	5
cr		equ	00dh
lf		equ	00ah
esc		equ	27
heavy		equ	'O'
light		equ	'P'
offsetuserf	equ	87
scrrunroutine	equ	00e9h
bytesperline	equ	720
roller		equ	0b600h

ldir	macro
	db	0edh,0b0h
	endm

	db	0,0,0,0,0,0	;'ole faithful RSX prefix
	jmp	start
next:	db	0c3h
	dw	0		;next chain entry filled by LOADER
prev:	dw	0		;previous entry     "    "    "
remov:	db	000h		;leave in memory
nbank:	db	0		;not non-banked
	db	'SCRNDMP'
loader:	db	0
	db	0,0

start
	mov	a,c
	cpi	2
	jz	begin
	cpi	73
	jz	takeaway
	jmp	next

takeaway
	mvi	a,0ffh
	sta	remov
	ret

begin
	lda	flagesc
	cpi	0ffh
	jz	contest		;just had an escape
	mov	a,e
	cpi	esc		;escape O or P Starts dump
	jz	setflag
	jmp	next		;nothing to do with us

setflag
	mvi	a,0ffh
	sta	flagesc
	ret

contest
	mov	a,e
	cpi	heavy		;heavy dump
	jz	goforit
	cpi	light		;light dump
	jz	goforit
	xra	a
	sta	flagesc		;forget that last ESC
	push	d
	push	b
	mvi	c,2
	mvi	e,esc
	call	next		;but esc wasn't ours so make amends
	pop	b
	pop	d
	jmp	next		;pass current char down the thain

goforit
	sta	dtype		;save 'O' or 'P' that deterwine dump type
	xra	a
	sta	flagesc		;esc has been used so reset

	lxi	h,0
	dad	sp
	shld	oldstak		;save CCP stack
	lxi	sp,mystak	;use my own 64 byte stack

	lhld	1
	lxi	d,offsetuserf
	dad	d
	shld	userf+1		;that famous ole routine

	mvi	a,esc
	call	prntbyte
	mvi	a,'3'	
	call	prntbyte
	mvi	a,25		;fiddle factor
	call	prntbyte

	mvi	a,0		;start at line 0
dloop	
	push	psw		;keep line count safe
dark
	push	psw		;save line count while sussin lf setting

	lda	dtype
	cpi	light
	jz	fred
	mvi	a,esc		;only set this fractional line feed lf
	call	prntbyte	;we're going to overtype for heavy dump
	mvi	a,'3'
	call	prntbyte
	mvi	a,2
	call	prntbyte	;set first line feed to 2/216"
fred
	pop	psw		;ok to retrieve line count for grabyte
	lxi	b,grabyte
	call	userf
	dw	scrrunroutine
	call	dumpaline
	lda	dtype
	cpi	light
	jz	justone		;If ESC P then dont do line again
	mvi	a,esc		;second pass, lf now to be set to 23/216"
	call	prntbyte
	mvi	a,'3'
	call	prntbyte
	mvi	a,23		;previous 2/216 + 23/216 = 25/216
	call	prntbyte
	pop	psw
	push	psw		;what was that line count ?
	lxi	b,grabyte	;need to grab bytes cos dump is destructive
	call	userf
	dw	scrrunroutine
	call	dumpaline	;overtype what's just been printed
justone
	mvi	c,6
	mvi	e,0ffh
	call	next		;attempt to get keyboard char
	cpi	0
	jnz	pleasereleaseme
	pop	psw		;let's have that line count back again
	inr	a
	cpi	32		;done line 31 yet ?
	jnz	dloop
eric
	lda	dtype
	cpi	light
	jz	dontreset	;cos it hasn't changed
	mvi	a,esc
	call	prntbyte
	mvi	a,'2'		;set line feed Back to 1/6"
	call	prntbyte
dontreset
	lhld	oldstak		;let Mr. CCP have his own stack back
	sphl
	ret			;an ave it away an our toes

pleasereleaseme		
	pop	psw		;don't you just luv a bit of structure ?
	jmp	eric

;=======
grabyte			
;=======
;
; Go get one lines worth of bytes fror screen memory
;
; entry : a contains the character line number
; exit  : all corrupt
;
	mov	e,a
;
;The following is a routine whith already sits in common memory
; and is executed by scr_run_routine
;
; this routine courtesy Roland Perry
;
; entry conditions:
;
;    e=line number
;
docommon:
	lxi	h,buffer
	push	h
	mvi	h,0
	mov	l,e		;hl=e
	dad	h		;hl=2*e
	dad	h		;hl=4*e
	dad	h		;hl=8*e
	dad	h		;hl=16*e
	lxi	d,roller	;that mysterious region of the universe
	dad	d		;hl=roller+16+e
;
	mov	e,m
	inx	h
	mov	d,m		;de=encoded address
;
	mov	a,e
	ani	7		;also clears carry
	mov	l,a		;l = 3 lsb
	mov	a,e
	ral			;lp8 to carry
	mov	e,a		;lp7-4/lp2-lp0/carry
	mov	a,d
	ral			;lsb=lp8
	mov	d,a
	mov	a,e
	ani	0F0h		;lp7-lp4
	ora	l
	mov	e,a		;don't you just love meaningful comments
;
	pop	h
	lxi	b,bytesperline
	xchg			;move from screen to buffer
	ldir
	ret
;

;=========
dumpaline:
;=========
;
; Dump one lines worth of byte to printer. There are 90 characters each
; composed of eight bytes so HL points at buffer containing 720 bytes.
;
; entry : no conditions	
; exit  : all registers corrupt	
;         (and Buffer contents destroyed)
;
	lxi	h,buffer
	mvi	a,esc		;let Mr. printer know whats happening
	call	prntbyte
	mvi	a,'L'
	call	prntbyte
	mvi	a,(bytesperline	and 0ffh)
	call	prntbyte
	mvi	a,(bytesperline/256)
	call	prntbyte

	mvi	b,0		;character count (0..89)
dstart
	push	b
	mvi	b,8		;number of shifts count (8)
ateshft
	xra	a		;clear A and carry
	mvi	c,8		;number of bytes in a group (8)
	push	h
atebit
	db	0cbh,026h	;Z80 SLA (HL)
				;drawback of this is that it can't be single
				;stepped in SID. So put NOP after to hold a
				;RST 6 if needed
;	nop
	ral			;so the 8080 does have rotates after all
	inx	h
	dcr	c
	jnz	atebit		;have we got all 8 top bits ?
	call	prntbyte	;yup, so print that character
	pop	h
	dcr	b
	jnz	ateshft		;have the current group been shifted 8 times
	lxi	d,8
	dad	d		;step hl onto next group of 8
	pop	b
	inr	b
	mov	a,b
	cpi	90		;hit the 90th group of 8 yet ?
	jnz	dstart
	mvi	a,cr		;Guess it uould be a guod idea to start
	call	prntbyte	;next line at l.h. margin
	mvi	a,lf
	call	prntbyte	;either 2/216,23/216 or just 25/216
	ret

prntbyte
	push	h
	push	d
	push	b
	mov	e,a
	mvi	c,listout
	call	bdos
	pop	b
	pop	d
	pop	h
	ret

userf:	db	0c3h 		;what do you mean you've never heard of it
	dw	0

	dseg

flagesc	db	0
dtype	db	0

oldstak	dw	0

buffer	ds	720
	ds	64
mystak

	end