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)