313 lines
10 KiB
ArmAsm
Executable file
313 lines
10 KiB
ArmAsm
Executable file
|
|
// ****************************************************************************
|
|
//
|
|
// VGA render
|
|
//
|
|
// ****************************************************************************
|
|
|
|
#include "define.h" // common definitions of C and ASM
|
|
|
|
.syntax unified
|
|
.section .time_critical.Render, "ax"
|
|
.cpu cortex-m0plus
|
|
.thumb // use 16-bit instructions
|
|
|
|
.extern pScreen // sScreen* pScreen; // pointer to current video screen
|
|
.extern LineBuf0 // u8 LineBuf0[BLACK_MAX]; // line buffer with black color
|
|
|
|
// extern "C" u32* Render(u32* cbuf, u8* dbuf, int line, int pixnum);
|
|
|
|
// render scanline
|
|
// cbuf ... control buffer
|
|
// dbuf ... data buffer (pixel data)
|
|
// line ... current scanline 0..
|
|
// pixnum ... total pixels (must be multiple of 4)
|
|
// Returns new pointer to control buffer
|
|
|
|
.thumb_func
|
|
.global Render
|
|
Render:
|
|
|
|
// push registers
|
|
push {r4-r7,lr}
|
|
|
|
// prepare local variables
|
|
// SP+0: input argument of render functions
|
|
// SP+4: R0 control buffer
|
|
// SP+8: R1 data buffer (pixel data)
|
|
// SP+12: R2 current scanline 0..
|
|
// SP+16: R3 total pixels
|
|
// SP+20: R4
|
|
// SP+24: R5
|
|
// SP+28: R6
|
|
// SP+32: R7
|
|
// SP+36: LR
|
|
|
|
sub sp,#20
|
|
str r0,[sp,#4] // control buffer
|
|
str r1,[sp,#8] // data buffer
|
|
str r3,[sp,#16] // total pixels
|
|
|
|
// ---- prepare pointer to current screen
|
|
// sScreen* s = pScreen;
|
|
// if (s != NULL) {
|
|
|
|
// prepare pointer to current screen
|
|
ldr r4,Render_pScreenAddr // pointer to pointer to current video Screen (variable pScreen)
|
|
ldr r4,[r4,#0] // pointer to current video Screen
|
|
cmp r4,#0 // is pointer valid?
|
|
beq Render_Clear // pointer is not valid, clear rest of line (display is OFF)
|
|
|
|
// ---- find video strip with current scanline
|
|
// int stripnum = s->num;
|
|
// sStrip* t = &s->strip[0];
|
|
// for (; stripnum > 0; stripnum--) {
|
|
|
|
// loop through video strips
|
|
ldrh r5,[r4,#SSCREEN_NUM] // u16 number of video strips
|
|
tst r5,r5 // check number of video strips
|
|
beq Render_Clear // no video strips, return
|
|
adds r4,#SSCREEN_STRIP // pointer to first video strip
|
|
|
|
// R2 ... current scanline
|
|
// R4 ... pointer to video strip
|
|
// R5 ... counter of video strips
|
|
|
|
Render_StripLoop:
|
|
|
|
// chek if current scanline has been found
|
|
// if (line < t->height) {
|
|
ldrh r3,[r4,#SSTRIP_HEIGHT] // u16 height of this video strip
|
|
cmp r2,r3 // check if current scanline fits into this video strip
|
|
blo Render_StripOK // scanline < strip height, this strip is OK
|
|
|
|
// subtract video strip height from scanline number (to be relative to start of strip)
|
|
// line -= t->height;
|
|
subs r2,r3 // subtract strip height from scanline number
|
|
|
|
// next video strip
|
|
// t++;
|
|
// for (; stripnum > 0; stripnum--)
|
|
adds r4,#SSTRIP_SIZE // shift pointer to next video strip
|
|
subs r5,#1 // counter of video strips
|
|
bne Render_StripLoop // next video strip
|
|
b Render_Clear // video strip not found
|
|
|
|
// ---- process all video segments
|
|
|
|
Render_StripOK:
|
|
|
|
// prepare first video segment
|
|
// sSegm* g = &t->seg[0];
|
|
// int segnum = t->num;
|
|
// for (; segnum > 0; segnum--) {
|
|
str r2,[sp,#12] // save current scanline
|
|
ldrh r5,[r4,#SSTRIP_NUM] // u16 number of video segments
|
|
tst r5,r5 // check number of video segments
|
|
beq Render_Clear // no video strips, return
|
|
adds r4,#SSTRIP_SEG // pointer to first video segment
|
|
|
|
// R4 ... pointer to video segment
|
|
// R5 ... counter of video segments
|
|
|
|
Render_SegmLoop:
|
|
|
|
// get number of remaining pixels
|
|
ldr r2,[sp,#16] // get remaining pixels
|
|
tst r2,r2 // check number of pixels
|
|
beq Render_Clear // end of scanline, stop rendering
|
|
|
|
// get segment width -> R3
|
|
// int w = g->width;
|
|
// if (w > pixnum) w = pixnum;
|
|
// if (w > 0) {
|
|
ldrh r3,[r4,#SSEGM_WIDTH] // get segment width
|
|
cmp r3,r2 // check width
|
|
blo 2f // width is OK
|
|
mov r3,r2 // limit width by total width
|
|
2: tst r3,r3 // check width
|
|
beq Render_SegmNext // this segment is invisible, skip it
|
|
|
|
// update remaining pixels
|
|
// pixnum -= w;
|
|
subs r2,r3 // decrease remaining width
|
|
str r2,[sp,#16] // store new remaining pixels
|
|
|
|
// get Y coordinate -> R2
|
|
// int y = g->offy + line;
|
|
ldrh r2,[r4,#SSEGM_OFFY] // get offset at Y direction
|
|
sxth r2,r2 // expand to signed
|
|
ldr r1,[sp,#12] // get current scanline
|
|
add r2,r1 // add Y offset and current scanline
|
|
|
|
// double lines
|
|
// if (g->dbly) y /= 2;
|
|
ldrb r1,[r4,#SSEGM_DBLY] // get dbly flag
|
|
tst r1,r1 // is dbly flag set?
|
|
beq 2f // dbly flag not set
|
|
asrs r2,#1 // Y coordinate / 2
|
|
|
|
// wrap Y coordinate
|
|
// int wy = g->wrapy;
|
|
// while (y < 0) y += wy;
|
|
// while (y >= wy) y -= wy;
|
|
2: ldrh r1,[r4,#SSEGM_WRAPY] // get wrapy
|
|
3: subs r2,r1 // subtract wrapy
|
|
bpl 3b // repeat
|
|
4: adds r2,r1 // add wrapy
|
|
bmi 4b // repeat
|
|
|
|
// get X coordinate -> R1
|
|
// int x = g->offx;
|
|
6: ldrh r1,[r4,#SSEGM_OFFX] // get offset at X direction
|
|
sxth r1,r1 // expand to signed
|
|
|
|
// wrap X coordinate
|
|
// int wx = g->wrapx;
|
|
// while (x < 0) x += wx;
|
|
// while (x >= wx) x -= wx;
|
|
ldrh r0,[r4,#SSEGM_WRAPX] // get wrapx
|
|
3: subs r1,r0 // subtract wrapx
|
|
bpl 3b // repeat
|
|
4: adds r1,r0 // add wrapx
|
|
bmi 4b // repeat
|
|
|
|
// ---- process 1st format group: GF_COLOR
|
|
|
|
// get format -> R0
|
|
6: ldrb r0,[r4,#SSEGM_FORM] // get current format
|
|
|
|
// serve format GF_COLOR
|
|
tst r0,r0 // format GF_COLOR ?
|
|
bne 7f // no
|
|
|
|
// u32 par = ((y & 1) == 0) ? g->par : g->par2
|
|
lsrs r2,#1 // check bit 0 of Y coordinate
|
|
ldr r1,[r4,#SSEGM_PAR] // get par for even line
|
|
bcc 2f // even line
|
|
ldr r1,[r4,#SSEGM_PAR2] // get par2 for odd line
|
|
|
|
// *cbuf++ = w/4; // number of pixels/4
|
|
2: lsrs r2,r3,#2 // width/4
|
|
ldr r6,[sp,#4] // get pointer to control buffer
|
|
stmia r6!,{r2} // store width/4
|
|
|
|
// *cbuf++ = (u32)dbuf; // pointer to data buffer
|
|
ldr r0,[sp,#8] // get pointer to data buffer
|
|
stmia r6!,{r0} // store pointer to data
|
|
str r6,[sp,#4] // save new pointer to control buffer
|
|
|
|
// dbuf = RenderColor(dbuf, par, w/4);
|
|
bl RenderColor
|
|
str r0,[sp,#8] // store new pointer to data buffer
|
|
b Render_SegmNext
|
|
|
|
// ---- process 2nd format group: using control buffer cbuf
|
|
|
|
// prepare input argument video segment -> [SP+0]
|
|
7: str r4,[sp,#0] // prepare 4th argument - current video segment
|
|
|
|
// prepare function addres -> R7
|
|
adr r7,Render_FncAddr // get address of jump table
|
|
lsls r6,r0,#2 // format * 4
|
|
ldr r7,[r7,r6] // load function address -> R7
|
|
|
|
// check 2nd format group
|
|
cmp r0,#GF_GRP2MAX // check 2nd format group
|
|
bhi 2f // > 2nd group
|
|
|
|
// cbuf = RenderGraph8(cbuf, x, y, w, g);
|
|
ldr r0,[sp,#4] // get pointer to control buffer
|
|
blx r7 // call render function
|
|
str r0,[sp,#4] // save new pointer to control buffer
|
|
b Render_SegmNext
|
|
|
|
// ---- process 3rd format group: using data buffer dbuf
|
|
|
|
// *cbuf++ = w/4; // number of pixels/4
|
|
2: lsrs r0,r3,#2 // width/4
|
|
ldr r6,[sp,#4] // get pointer to control buffer
|
|
stmia r6!,{r0} // store width/4
|
|
|
|
// *cbuf++ = (u32)dbuf; // pointer to data buffer
|
|
ldr r0,[sp,#8] // get pointer to data buffer
|
|
stmia r6!,{r0} // store pointer to data
|
|
str r6,[sp,#4] // save new pointer to control buffer
|
|
|
|
// dbuf = RenderColor(dbuf, par, w/4);
|
|
blx r7 // call render function
|
|
str r0,[sp,#8] // store new pointer to data buffer
|
|
|
|
Render_SegmNext:
|
|
|
|
// next video segment
|
|
adds r4,#SSEGM_SIZE // shift pointer to next video segment
|
|
subs r5,#1 // counter of video segments
|
|
bne Render_SegmLoop // next video segment
|
|
|
|
// ---- clear rest of line, write pointer to control buffer
|
|
|
|
Render_Clear:
|
|
|
|
// return current control buffer
|
|
ldr r0,[sp,#4] // control buffer
|
|
|
|
// check if some pixels left
|
|
ldr r1,[sp,#16] // number of remaining pixels
|
|
lsrs r1,#2 // number of pixels/4 (= number of 4-pixels)
|
|
beq 9f // no pixels left
|
|
|
|
// write size and address to control buffer
|
|
ldr r2,Render_LineBuf0Addr // data buffer with black color
|
|
stmia r0!,{r1,r2} // write number of 4-pixels and pointer to data buffer to control buffer
|
|
|
|
// pop registers and return (return control buffer in r0)
|
|
9: add sp,#20
|
|
pop {r4-r7,pc}
|
|
|
|
.align 2
|
|
|
|
// pointer to pointer with current video screen
|
|
Render_pScreenAddr:
|
|
.word pScreen
|
|
|
|
// pointer to buffer with black color
|
|
Render_LineBuf0Addr:
|
|
.word LineBuf0
|
|
|
|
// poiners to render functions
|
|
Render_FncAddr:
|
|
// 1st format group
|
|
.word RenderColor // GF_COLOR simple color (par=color pattern 4-pixels even line, par2=color pattern 4-pixels odd line)
|
|
|
|
// 2nd format group
|
|
.word RenderGraph8 // GF_GRAPH8 native 8-bit graphics (X1Y1R2G2B2) - fast, transfers "as is" to PIO
|
|
.word RenderTile // GF_TILE tiles
|
|
.word RenderTile2 // GF_TILE alternate tiles
|
|
.word RenderProgress // GF_PROGRESS horizontal progress indicator
|
|
.word RenderGrad1 // render gradient with 1 line GF_GRAD1
|
|
.word RenderGrad2 // render gradient with 2 lines GF_GRAD2
|
|
|
|
// 3rd format group
|
|
.word RenderGraph4 // GF_GRAPH4 4-bit graphics
|
|
.word RenderGraph2 // GF_GRAPH2 2-bit graphics
|
|
.word RenderGraph1 // GF_GRAPH1 1-bit graphics
|
|
.word RenderMText // GF_MTEXT 8-pixel mono text
|
|
.word RenderAText // GF_ATEXT 8-pixel attribute text, character + 2x4 bit attributes
|
|
.word RenderFText // GF_FTEXT 8-pixel foreground color text, character + foreground color
|
|
.word RenderCText // GF_CTEXT 8-pixel color text, character + background color + foreground color
|
|
.word RenderGText // GF_GTEXT 8-pixel gradient text (par = pointer to 1-bit font, par2 = pointer to color array)
|
|
.word RenderDText // GF_DTEXT 8-pixel double gradient text (par = pointer to 1-bit font, par2 = pointer to color array)
|
|
.word RenderLevel // GF_LEVEL level graph
|
|
.word RenderLevelGrad // GF_LEVELGRAD level gradient graph
|
|
.word RenderOscil // GF_OSCIL oscilloscope pixel graph
|
|
.word RenderOscLine // GF_OSCLINE oscilloscope line graph
|
|
.word RenderPlane2 // GF_PLANE2 4 colors on 2 graphic planes
|
|
.word RenderAttrib8 // GF_ATTRIB8 2x4 bit color attribute per 8x8 pixel sample
|
|
.word RenderGraph8Mat // GF_GRAPH8MAT 8-bit graphics with 2D matrix transformation
|
|
.word RenderGraph8Persp // GF_GRAPH8PERSP 8-bit graphics with perspective projection
|
|
.word RenderTilePersp // GF_TILEPERSP tiles with perspective
|
|
.word RenderTilePersp15 // GF_TILEPERSP15 tiles with perspective, 1.5 pixels
|
|
.word RenderTilePersp2 // GF_TILEPERSP2 tiles with perspective, double pixels
|
|
.word RenderTilePersp3 // GF_TILEPERSP3 tiles with perspective, triple pixels
|
|
.word RenderTilePersp4 // GF_TILEPERSP4 tiles with perspective, quadruple pixels
|