SuperH-based fx calculators
fx-CG20, Display Driver, LCD-DMA-hook example

When the OS transfers the VRAM content to the LCD GRAM (syscalls 0x025F and 0x0260), it uses the processors DMA-controller. The syscalls simply wait for the DMA-controller to be ready until the transfer is completed. It is possible to implement a transfer function, which calls a hook-procedure during the wait-loop. Though, the OS is not designed for this stunt. The possible operations performed during the LCD DMA-hook are most probably rather limited. Any access to the DMA-controller or the LCD-controller inside of the LCD DMA-hook will terminate the process, leading to unpredictable situations. Any access to the processor's hardware-modules and the file system must be avoided inside of the LCD DMA-hook. Syscalls, which do such things, must be avoided as well.

The first thing, which is needed, is an appropriate assembler function:

	.MACRO SYS_CALL FUNO
	mov.l \FUNO', r0
	mov.l #h'80020070, r2
	jsr @r2
	.ENDM
SYNCO: 		.DEFINE ".DATA.W h'00AB"
STACKFRAME 	.DEFINE "h'18"
VRAM		.DEFINE "h'A8000000"
; Module Stop Register 0
MSTPCR0 	.DEFINE	"h'A4150030"
; DMA0 operation register
DMA0_DMAOR 	.DEFINE "h'FE008060"
DMA0_SAR_0	.DEFINE "h'FE008020"
; DMA register offsets
; destination address register_0 
DAR_0		.DEFINE "h'4"
; transfer count register_0 
TCR_0		.DEFINE "h'8"
;channel control register_0
CHCR_0		.DEFINE "h'C"
LCD_BASE	.DEFINE "h'B4000000"
LCD_GRAM	.DEFINE "h'202"
	.export _LCD_StripeToGRAM
	.SECTION P,CODE,ALIGN=4
	
; ---------------------------------------------------------------------------
; interface int LCD_StripeToGRAM( int y1, int y2, void*VRAM, void*hook )
; y1 : start row
; y2 : end row
; VRAM : source address
; hook : address of the hook while the function waits for the DMA controller
; returns DMA0_DMAOR (if bit 2 is set, an address error has occured)
; ---------------------------------------------------------------------------
_LCD_StripeToGRAM:
; initialization start
		mov.l	r11, @-r15
		mov.l	r12, @-r15
		mov.l	r13, @-r15
		mov.l	r14, @-r15
		sts.l	pr, @-r15
		add	#-h'10, r15
		
		; save parameters
		mov.l	r4, @r15				; P1 (y1)
		mov.l	r5, @(4,r15)				; P2 last row
		mov.l	r6, r12					; P3 VRAM
		mov.l	r7, r11					; P4 hook
		
		; set bit ORG of LCD register "Entry Mode (R003h)" (see page 45 of R61509 manual 1.01)
		SYS_CALL #h'01A6				; sys01a6_Bdisp_WriteDDRegister3_bit7
		mov	#1, r4
		
		; define the LCD-GRAM range
		mov	#-h'29, r7     				; 215 max rows
		mov.l	@(4,r15), r13				; P2 last row
		extu.b	r7, r7					; 215 max rows
		mov.l	@r15, r6				; P1 first row
		mov	#6, r4					; first	column
		mov.w	#h'185, r5					; 389 last column
		mov	r7, r1						; 215 max rows
		sub		r13, r1						; 215 -	last row (P2)
		SYS_CALL #h'01A3					; sys01a3_DD_SetCoords
		sub		r1, r7						; last row (P2)
		
		; define the LCD GRAM as physical target for access to address 0xB4000000
		mov.w	#LCD_GRAM, r4				; Write Data to GRAM (R202h)
		SYS_CALL #h'01A2					; sys01a2_Bdisp_DDRegisterSelect
		nop
		; enable DMA clock	
		mov.l	#MSTPCR0, r13	     		; 
		mov.l	#h'FFDFFFFF, r7				; 1111 1111 1101 1111 1111 1111 1111 1111 (MSTPCR0 bit 21; DMA clock)
		mov.l	@r13, r1					; @MSTPCR0
		and		r7, r1
		mov.l	r1, @r13					; @MSTPCR0
		
		; setup the DMA-controller
		mov.l	#DMA0_DMAOR, r13 			; 
		mov.l	#DMA0_SAR_0, r14    		; 
		mov		#-2, r1						; FFFF FFFE (...1110)
		mov.l	@(CHCR_0,r14), r4  			; DMA0 channel control register_0 DMA0_CHCR_0
		and		r1, r4
		mov.l	r4, @(CHCR_0,r14)  			; DMA0_CHCR_0 (disable DMA)
		
		mov.w	@r13, r5				; @DMA0_DMAOR
		mov		#0, r7
		mov.w	r7, @r13				; @DMA0_DMAOR
		
		; setup DMA source address
		mov.l	@r15, r1				; P1 (y1)
		mov	r1, r6					; y1
		shll	r6					; 2*y1
		add	r1, r6					; 3*y1
		mov.l	r12, r7					; P3 source address
		shll8	r6					; 2*384*y1
		add	r6, r7					; source address + 2*384*y1
		
		; setup DMA target address
		shll	r6							; 4*384*y1
		mov.l	#LCD_BASE, r5				; MPU/LCD driver interface address
		add		r5, r6						; target address 0xB4000000 + 4*384*y1
		
		mov.l	#h'1FFFFFFF, r2				; DMA addresses bits mask
		and		r2, r7						; strip off some bits (source address)
		mov.l	r7, @r14					; write source address to DMA-controller
		
		and		r2, r6						; strip off some bits (target address)
		mov.l	r6, @(DAR_0,r14)			; write target address to DMA-controller
		
		; bytecount of one row
		mov		#3, r5
		shll8	r5							; 0x300 = 2*384
		
		; calculate the amount of 32-byte blocks to transfer	
		mov.l	@(4,r15), r2				; y2
		sub		r1, r2						; y2-y1
		mov		r2, r1						; y2-y1
		shll	r2							; (y2-y1)*2
		add		r1, r2						; (y2-y1)*3
		shll8	r2							; (y2-y1)*2*384
		add		r5, r2						; (y2-y1+1)*2*384
		mov		#-5, r6
		shad	r6, r2						; (y2-y1+1)*2*384 / 32
		
		mov.l	r2, @(TCR_0,r14)			; write DMA transfer count register_0
		mov.l	#h'00101400, r2				; 0000 0000 0001 0000 0001 0100 0000 0000
		; X   |LCKN|X   |X		0000
		; RPT2|RPT1|RPT0|DA		0000
		; DO  |X   |TS3 |TS2	0001		; bitmask in REJ09B0560-0100 Rev. 1.00 (chapter 16.3.7) is erroneous
		; HE  |HIE |AM  |AL		0000
		; DM1 |DM0 |SM1 |SM0	0001
		; RS3 |RS2 |RS1 |RS0	0100
		; DL  |DS  |TB  |TS1	0000
		; TS0 |IE  |TE  |DE		0000
		; TS 0100	: 32-byte units transfer
		; DM 00		: Fixed destination address
		; SM 01		: Source address is incremented
		; RS 0100	: Auto request
		mov.l	r2, @(CHCR_0,r14)			; DMA0 channel control register_0
		
		mov.w	@r13, r0					; DMA0_DMAOR
		or		#1, r0						; enable master DMA (DME)
		mov.w	r0, @r13
		
		mov.w	@r13, r2					; DMA0_DMAOR
		mov		#-7, r6						; FFFF FFF9 (...1001)
		and		r6, r2						; strip off NMI- and AE-flags
		mov.w	r2, @r13
		
		mov.l	@(CHCR_0,r14), r0
		or		#1, r0
		mov.l	r0, @(CHCR_0,r14)  			; DMA0_CHCR_0 (enable DMA)
; initialization end		
; ---------------------------------------------------------------------------
; wait loop start
?DMA_loop:
		tst		r11, r11					; P4 : hook assigned?
		bt		?DMA_end_hook				; ignore hook, if not assigned
		
		; enter hook procedure
		; the hook's procedure mustn't disturb the running DMA process!
		jsr		@r11
		nop
		
?DMA_end_hook:
		mov.l	@(CHCR_0,r14), r0  			; DMA0_CHCR_0
		tst		#2, r0						; TE Transfer End Flag
		bf/s	?DMA_quit
		mov.l	r0, @(h'C,r15)
		mov.w	@r13, r0					; DMA0_DMAOR
		and		#4, r0						; AE Address Error Flag
		cmp/pl	r0
		bf	?DMA_loop
?DMA_quit:	SYNCO
; wait loop end
; ---------------------------------------------------------------------------
; finalization start
		mov.l	@(CHCR_0,r14), r6
		mov		#-2, r1						; FFFF FFFE (...1110)
		and		r1, r6
		mov.l	r6, @(CHCR_0,r14)			; disable DMA
		
		mov.w	@r13, r7					; DMA0_DMAOR
		mov		#0, r5
		mov.w	r5, @r13					; disable master DMA
		add	#h'10, r15
		lds.l	@r15+, pr
		mov.l	@r15+, r14
		mov.l	@r15+, r13
		mov.l	@r15+, r12
		rts
		mov.l	@r15+, r11
; finalization end
		
	.END

The code has been successfully assembled with the old CASIO SDK assembler ASMSH. Hence the SH-4A-instruction SYNCO had to be emulated by

 SYNCO: .DEFINE ".DATA.W h'00AB".

The following C-code demonstrates the use of the above shown assembler procedure. As simple example the hook-procedure increments a counter-variable.

LCD_StripeToGRAM( 0, 107, (void*)VRAM2, (void*)&DMA_Hook );
This call performs the transfer of the upper half (row 0 to row 107) of VRAM2 to LCD GRAM.

LCD_StripeToGRAM( 108, 215, (void*)VRAM2, (void*)&DMA_Hook );
This call performs the transfer of the lower half (row 108 to row 215) of VRAM2 to LCD GRAM.

During each transfer the procedure DMA_Hook is called about 2500 times.

Hint: normally VRAM ist used as source address. In this example VRAM2 (some part of the addin-stack, which is not reached by the hosting addin) is abused for testing purposes.

 

#define VRAM2 0xA81A0000
#define VRAM 0xA8000000
int DMA_Hook_counter;
//
void DMA_Hook( void ){
	DMA_Hook_counter++;
}
//
void F85_Handler(){
int ticks;
int i, i1, i2, i3;
unsigned char hb[25];
unsigned int key;	
	DMA_Hook_counter = 0;

	// set some test color-ranges
	for ( i = 0;i<384*215;i++) *(unsigned short*)(VRAM2+2*i) = 0xF800;
	for ( i = 0;i<384*100;i++) *(unsigned short*)(VRAM2+2*i) = 0x07E0;
	for ( i = 0;i<384*3;i++) *(unsigned short*)(VRAM2+2*i) = 0x001F;

	// transfer upper half
	i1 = LCD_StripeToGRAM( 0, 107, (void*)VRAM2, (void*)&DMA_Hook );
	i3 = DMA_Hook_counter;
	ticks = RTC_GetTicks();	// current count of 1/128 s ticks since midnight.
	while (!RTC_Elapsed_ms( ticks, 2000 ));

	// transfer lower half
	i2 = LCD_StripeToGRAM( 108, 215, (void*)VRAM2, (void*)&DMA_Hook );
	ticks = RTC_GetTicks();	// current count of 1/128 s ticks since midnight.
	while (!RTC_Elapsed_ms( ticks, 2000 ));

	// display some results
	MsgBoxPush( 4 );
	locate( 3, 2 );
	LongToAscHex( i1, hb, 4 );
	Print( hb );
	locate( 3, 3 );
	LongToAscHex( i2, hb, 4 );
	Print( hb );
	locate( 3, 4 );
	LongToAscHex( i3, hb, 4 );
	Print( hb );
	locate( 3, 5 );
	LongToAscHex( DMA_Hook_counter, hb, 4 );
	Print( hb );
	GetKey( &key );
	MsgBoxPop();
}

 

 

 

(14.10.2012 06:23:45)