dosbox-wii/src/hardware/vga_draw.cpp

407 lines
12 KiB
C++
Raw Normal View History

2009-05-02 23:03:37 +02:00
/*
2009-05-02 23:35:44 +02:00
* Copyright (C) 2002-2003 The DOSBox Team
2009-05-02 23:03:37 +02:00
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <string.h>
#include "dosbox.h"
#include "video.h"
2009-05-02 23:43:00 +02:00
#include "render.h"
2009-05-02 23:03:37 +02:00
#include "vga.h"
2009-05-02 23:43:00 +02:00
#include "pic.h"
2009-05-02 23:03:37 +02:00
2009-05-02 23:27:47 +02:00
//TODO Make the full draw like the vga really does from video memory.
2009-05-02 23:03:37 +02:00
2009-05-02 23:43:00 +02:00
static void VGA_CGA2_Draw(Bit8u * bitdata,Bitu pitch) {
Bit8u * reader=&vga.mem.linear[0];
Bit8u * flip=&vga.mem.linear[8*1024];
2009-05-02 23:12:18 +02:00
Bit8u * draw;
for (Bitu y=0;y<vga.draw.height;y++) {
Bit8u * tempread;
tempread=reader;
if (y&1) {
tempread+=8*1024;
reader+=80;
};
draw=bitdata;
//TODO Look up table like in 4color mode
2009-05-02 23:43:00 +02:00
for (Bitu x=vga.draw.width>>3;x>0;x--) {
2009-05-02 23:12:18 +02:00
Bit8u val=*(tempread++);
*(draw+0)=(val>>7)&1;
*(draw+1)=(val>>6)&1;
*(draw+2)=(val>>5)&1;
*(draw+3)=(val>>4)&1;
*(draw+4)=(val>>3)&1;
*(draw+5)=(val>>2)&1;
*(draw+6)=(val>>1)&1;
*(draw+7)=(val>>0)&1;
draw+=8;
}
bitdata+=pitch;
2009-05-02 23:27:47 +02:00
}
2009-05-02 23:12:18 +02:00
}
2009-05-02 23:03:37 +02:00
2009-05-02 23:43:00 +02:00
static void VGA_CGA4_Draw(Bit8u * bitdata,Bitu pitch) {
Bit8u * reader=&vga.mem.linear[0];
Bit8u * flip=&vga.mem.linear[8*1024];
2009-05-02 23:03:37 +02:00
Bit8u * draw;
for (Bitu y=0;y<vga.draw.height;y++) {
Bit8u * tempread;
tempread=reader;
if (y&1) {
tempread+=8*1024;
reader+=80;
2009-05-02 23:27:47 +02:00
if (reader>=flip) reader-=8*1024;
}
2009-05-02 23:03:37 +02:00
draw=bitdata;
2009-05-02 23:43:00 +02:00
for (Bitu x=0;x<vga.draw.width>>2;x++) {
2009-05-02 23:03:37 +02:00
Bit8u val=*(tempread++);
2009-05-02 23:43:00 +02:00
*(Bit32u *)draw=CGA_4_Table[val];
2009-05-02 23:03:37 +02:00
draw+=4;
}
2009-05-02 23:12:18 +02:00
bitdata+=pitch;
2009-05-02 23:27:47 +02:00
}
2009-05-02 23:03:37 +02:00
}
2009-05-02 23:43:00 +02:00
static void VGA_TANDY16_Draw(Bit8u * bitdata,Bitu pitch) {
Bit8u * reader=&vga.mem.linear[(vga.tandy.disp_bank << 14) + vga.config.display_start*2];
2009-05-02 23:03:37 +02:00
for (Bitu y=0;y<vga.draw.height;y++) {
2009-05-02 23:43:00 +02:00
Bit8u * tempread=reader+((y & 3) * 8 * 1024);
Bit8u * draw=bitdata;
2009-05-02 23:03:37 +02:00
for (Bitu x=0;x<vga.draw.width>>2;x++) {
2009-05-02 23:43:00 +02:00
Bit8u val1=*(tempread++);
Bit8u val2=*(tempread++);
Bit32u full=(val1 & 0x0f) << 8 |
(val1 & 0xf0) >> 4 |
(val2 & 0x0f) << 24 |
(val2 & 0xf0) << 12;
*(Bit32u *)draw=full;
draw+=4;
2009-05-02 23:03:37 +02:00
}
2009-05-02 23:43:00 +02:00
bitdata+=pitch;
if ((y & 3)==3)reader+=160;
2009-05-02 23:03:37 +02:00
}
2009-05-02 23:27:47 +02:00
}
2009-05-02 23:03:37 +02:00
2009-05-02 23:43:00 +02:00
void VGA_TEXT_Draw(Bit8u * bitdata,Bitu start,Bitu panning,Bitu rows) {
Bit8u * reader=&vga.mem.linear[start*2];
2009-05-02 23:27:47 +02:00
Bit8u * draw_start=bitdata;
2009-05-02 23:03:37 +02:00
/* Todo Blinking and high intensity colors */
2009-05-02 23:43:00 +02:00
Bitu next_charline=vga.draw.font_height*vga.draw.width;
Bitu next_line=vga.draw.width;
Bitu next_start=(vga.config.scan_len*2)-vga.draw.cols;
for (Bitu cy=rows;cy>0;cy--) {
2009-05-02 23:03:37 +02:00
Bit8u * draw_char=draw_start;
2009-05-02 23:43:00 +02:00
/* Do first character keeping track of panning */
{
Bit8u c=*(reader++);
Bit8u * findex=&vga.draw.font[c*32];
Bit8u col=*(reader++);
Bit8u fg=col & 0xF;
Bit8u bg=(col>> 4);
Bit8u * draw_line=draw_char;
Bit8u bit_index=1 << (7-panning);
for (Bitu y=vga.draw.font_height;y>0;y--) {
Bit8u * draw=draw_line;
draw_line+=next_line;
Bit8u bit=bit_index;
Bit8u bit_mask=*findex++;
while (bit) {
if (bit_mask & bit) *draw=fg;
else *draw=bg;
draw++;bit>>=1;
}
}
draw_char+=8-panning;
}
for (Bitu cx=vga.draw.cols-1;cx>0;cx--) {
2009-05-02 23:03:37 +02:00
Bit8u c=*(reader++);
2009-05-02 23:43:00 +02:00
Bit8u * findex=&vga.draw.font[c*32];
2009-05-02 23:03:37 +02:00
Bit8u col=*(reader++);
Bit8u fg=col & 0xF;
Bit8u bg=(col>> 4);
Bit8u * draw=draw_char;
2009-05-02 23:43:00 +02:00
for (Bitu y=vga.draw.font_height;y>0;y--) {
2009-05-02 23:03:37 +02:00
Bit8u bit_mask=*findex++;
#include "font-switch.h"
2009-05-02 23:43:00 +02:00
draw+=next_line;
}
2009-05-02 23:03:37 +02:00
draw_char+=8;
2009-05-02 23:27:47 +02:00
}
2009-05-02 23:43:00 +02:00
/* Do last character if needed */
if (panning) {
Bit8u c=*(reader);
Bit8u * findex=&vga.draw.font[c*32];
Bit8u col=*(reader+1);
Bit8u fg=col & 0xF;
Bit8u bg=(col>> 4);
Bit8u * draw_line=draw_char;
Bit8u bit_index=1 << panning;
for (Bitu y=vga.draw.font_height;y>0;y--) {
Bit8u * draw=draw_line;
draw_line+=next_line;
Bit8u bit=bit_index;
Bit8u bit_mask=*findex++;
while (bit) {
if (bit_mask & bit) *draw=fg;
else *draw=bg;
draw++;bit>>=1;
}
}
}
draw_start+=next_charline;
reader+=next_start;
}
/* Cursor handling */
vga.draw.cursor.count++;
if (vga.draw.cursor.count>16) vga.draw.cursor.count=0;
if(vga.draw.cursor.enabled && (vga.draw.cursor.count>8)) { /* Draw a cursor if enabled */
Bits cur_start=vga.config.cursor_start-start;
if (cur_start<0) return;
Bitu row=cur_start / (vga.config.scan_len*2);
Bitu col=cur_start % (vga.config.scan_len*2);
Bit32u att=vga.mem.linear[vga.config.cursor_start*2+1]&0xf;
att=(att << 8) | att;
att=(att << 16) | att;
if ((col*8)>=vga.draw.width) return;
if ((row*vga.draw.font_height)>=vga.draw.height) return;
if (vga.draw.cursor.sline>=vga.draw.font_height) return;
if (vga.draw.cursor.sline>vga.draw.cursor.eline) return;
Bit8u * cursor_draw=bitdata+(row*vga.draw.font_height+vga.draw.cursor.sline)*vga.draw.width+col*8;
for (Bits loop=vga.draw.cursor.eline-vga.draw.cursor.sline;loop>=0;loop--) {
*((Bit32u *)cursor_draw)=att;
*((Bit32u *)(cursor_draw+4))=att;
cursor_draw+=vga.draw.width;
}
2009-05-02 23:27:47 +02:00
}
2009-05-02 23:43:00 +02:00
}
static void EndRetrace(void) {
/* start the actual display update now */
RENDER_DoUpdate();
vga.config.retrace=false;
}
static void VGA_BlankTimer() {
PIC_AddEvent(VGA_BlankTimer,vga.draw.blank);
PIC_AddEvent(EndRetrace,667);
/* Setup a timer to destroy the vertical retrace bit in a few microseconds */
vga.config.real_start=vga.config.display_start;
vga.config.retrace=true;
}
void VGA_DrawHandler(RENDER_Part_Handler part_handler) {
Bit8u * buf,* bufsplit;
/* Draw the current frame */
if (!vga.draw.resizing) {
if (vga.config.line_compare<vga.draw.lines) {
Bitu stop=vga.config.line_compare;
if (vga.draw.double_height) stop/=2;
if (stop>=vga.draw.height){
LOG(LOG_VGAGFX,LOG_NORMAL)("Split at %d",stop);
goto drawnormal;
}
switch (vga.mode) {
case M_EGA16:
buf=&vga.mem.linear[512*1024+vga.config.real_start*8+vga.config.pel_panning];
bufsplit=&vga.mem.linear[512*1024];
break;
case M_VGA:
case M_LIN8:
buf=&vga.mem.linear[vga.config.real_start*4+vga.config.pel_panning];
bufsplit=vga.mem.linear;
break;
case M_TEXT16:
{
Bitu first_rows=stop/vga.draw.font_height;
if (vga.config.hlines_skip) first_rows++;
if (stop%vga.draw.font_height) first_rows++;
Bitu next_rows=(vga.draw.height-stop)/vga.draw.font_height;
if ((vga.draw.height-stop)%vga.draw.font_height) next_rows++;
VGA_TEXT_Draw(&vga.mem.linear[512*1024],vga.config.real_start,vga.config.pel_panning,first_rows);
VGA_TEXT_Draw(&vga.mem.linear[1024*1024],0,0,next_rows);
buf=&vga.mem.linear[512*1024+vga.config.hlines_skip*vga.draw.width];
bufsplit=&vga.mem.linear[1024*1024];
}
break;
default:
LOG(LOG_VGAGFX,LOG_NORMAL)("VGA:Unhandled split screen mode %d",vga.mode);
goto norender;
}
if (stop) part_handler(buf,0,0,vga.draw.width,stop);
if (vga.draw.height-stop) part_handler(bufsplit,0,stop,vga.draw.width,vga.draw.height-stop);
} else {
drawnormal:
switch (vga.mode) {
case M_CGA2:
VGA_CGA2_Draw(&vga.mem.linear[512*1024],vga.draw.width);
buf=&vga.mem.linear[512*1024];
break;
case M_CGA4:
VGA_CGA4_Draw(&vga.mem.linear[512*1024],vga.draw.width);
buf=&vga.mem.linear[512*1024];
break;
case M_TANDY16:
VGA_TANDY16_Draw(&vga.mem.linear[512*1024],vga.draw.width);
buf=&vga.mem.linear[512*1024];
break;
case M_EGA16:
buf=&vga.mem.linear[512*1024+vga.config.real_start*8+vga.config.pel_panning];
break;
case M_VGA:
case M_LIN8:
buf=&vga.mem.linear[vga.config.real_start*4+vga.config.pel_panning];
break;
case M_TEXT16:
{
Bitu rows=vga.draw.rows;
if (vga.config.hlines_skip) rows++;
VGA_TEXT_Draw(&vga.mem.linear[512*1024],vga.config.real_start,vga.config.pel_panning,rows);
buf=&vga.mem.linear[512*1024+vga.config.hlines_skip*vga.draw.width];
}
break;
default:
return;
}
part_handler(buf,0,0,vga.draw.width,vga.draw.height);
}
norender:;
}
}
2009-05-02 23:03:37 +02:00
2009-05-02 23:43:00 +02:00
void VGA_SetupDrawing(void) {
/* Calculate the FPS for this screen */
double fps;
Bitu vtotal=2 + (vga.crtc.vertical_total | ((vga.crtc.overflow & 1) << 8) | ((vga.crtc.overflow & 0x20) << 4) );
Bitu htotal=5 + vga.crtc.horizontal_total;
Bitu vdispend = 1 + (vga.crtc.vertical_display_end | ((vga.crtc.overflow & 2)<<7) | ((vga.crtc.overflow & 0x40) << 3) );
Bitu hdispend = 1 + (vga.crtc.horizontal_display_end);
Bitu hbstart = vga.crtc.start_horizontal_blanking;
Bitu vbstart = vga.crtc.start_vertical_blanking | ((vga.crtc.overflow & 0x08) << 5) | ((vga.crtc.maximum_scan_line & 0x20) << 4) ;
if (hbstart<hdispend)
hdispend=hbstart;
if (vbstart<vdispend)
vdispend=vbstart;
Bitu clock=(vga.misc_output >> 2) & 3;
clock=1000*S3_CLOCK(vga.s3.clk[clock].m,vga.s3.clk[clock].n,vga.s3.clk[clock].r);
/* Check for 8 for 9 character clock mode */
if (vga.seq.clocking_mode & 1 ) clock/=8; else clock/=9;
/* Check for pixel doubling, master clock/2 */
if (vga.seq.clocking_mode & 0x8) clock/=2;
/* Check for dual transfer whatever thing,master clock/2 */
if (vga.s3.pll.cmd & 0x10) clock/=2;
LOG(LOG_VGA,LOG_NORMAL)("H total %d, V Total %d",htotal,vtotal);
LOG(LOG_VGA,LOG_NORMAL)("H D End %d, V D End %d",hdispend,vdispend);
fps=clock/(vtotal*htotal);
vga.draw.resizing=false;
Bitu width,height,pitch,flags;
flags=0;
vga.draw.lines=height=vdispend;
width=hdispend;
vga.draw.double_height=vga.config.vline_double;
vga.draw.double_width=(vga.seq.clocking_mode & 0x8)>0;
vga.draw.font_height=vga.config.vline_height+1;
switch (vga.mode) {
case M_VGA:
vga.draw.double_width=true; //Hack since 256 color modes use 2 clocks for a pixel
/* Don't know might do this different sometime, will have to do for now */
if (!vga.draw.double_height) {
if (vga.config.vline_height&1) {
vga.draw.double_height=true;
vga.draw.font_height/=2;
}
}
width<<=2;
pitch=vga.config.scan_len*8;
break;
case M_LIN8:
width<<=3;
if (vga.draw.double_width) width>>=1;
if (!vga.draw.double_height) {
if (vga.config.vline_height&1) {
vga.draw.double_height=true;
vga.draw.font_height/=2;
}
}
pitch=vga.config.scan_len*8;
break;
case M_EGA16:
width<<=3;
pitch=vga.config.scan_len*16;
break;
case M_CGA4:
width<<=3;
pitch=width;
break;
case M_CGA2:
width<<=3;
pitch=width;
break;
case M_TANDY16:
width<<=3;
pitch=width;
break;
case M_TEXT16:
/* probably a 16-color text mode, got to detect mono mode somehow */
vga.draw.font_height=vga.config.vline_height+1;
vga.draw.cols=width;
vga.draw.rows=(height/vga.draw.font_height);
if (height % vga.draw.font_height) vga.draw.rows++;
width<<=3; /* 8 bit wide text font */
if (width>640) width=640;
if (height>480) height=480;
pitch=width;
break;
default:
LOG(LOG_VGA,LOG_ERROR)("Unhandled VGA type %d while checking for resolution");
};
if (vga.draw.double_height) {
flags|=DoubleHeight;
height/=2;
}
if (vga.draw.double_width) {
flags|=DoubleWidth;
/* Double width is dividing main clock, the width should be correct already for this */
}
if (( width != vga.draw.width) || (height != vga.draw.height) || (pitch != vga.draw.pitch)) {
PIC_RemoveEvents(VGA_BlankTimer);
vga.draw.width=width;
vga.draw.height=height;
vga.draw.pitch=pitch;
LOG(LOG_VGA,LOG_NORMAL)("Width %d, Height %d, Pitch %d",width,height,pitch);
LOG(LOG_VGA,LOG_NORMAL)("Flags %X, fps %f",flags,fps);
RENDER_SetSize(width,height,8,pitch,((float)width/(float)height),flags,&VGA_DrawHandler);
vga.draw.blank=(Bitu)(1000000/fps);
PIC_AddEvent(VGA_BlankTimer,vga.draw.blank);
}
};