1
0
Fork 0

First CVS upload.

Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@80
This commit is contained in:
Sjoerd van der Berg 2002-07-27 13:08:48 +00:00
parent 7d1ca9bdd4
commit 42e5d0b779
158 changed files with 42940 additions and 0 deletions

11
src/hardware/Makefile.am Normal file
View file

@ -0,0 +1,11 @@
AM_CPPFLAGS = -I$(top_srcdir)/include
EXTRA_DIST = fmopl.c fmopl.h
noinst_LIBRARIES = libhardware.a
libhardware_a_SOURCES = adlib.cpp dma.cpp gameblaster.cpp hardware.cpp iohandler.cpp joystick.cpp keyboard.cpp \
memory.cpp mixer.cpp pcspeaker.cpp pic.cpp sblaster.cpp tandy_sound.cpp timer.cpp \
vga.cpp vga.h vga_attr.cpp vga_crtc.cpp vga_dac.cpp vga_draw.cpp vga_fonts.cpp vga_gfx.cpp \
vga_memory.cpp vga_misc.cpp vga_seq.cpp font-switch.h ega-switch.h

224
src/hardware/adlib.cpp Normal file
View file

@ -0,0 +1,224 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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 <stdlib.h>
#include <string.h>
#include <math.h>
#include "dosbox.h"
#include "inout.h"
#include "mixer.h"
#include "timer.h"
#include "hardware.h"
/*
Thanks to vdmsound for nice simple way to implement this
*/
namespace MAME {
/* Defines */
# define logerror(x)
/* Disable recurring warnings */
#pragma warning ( disable : 4018 )
#pragma warning ( disable : 4244 )
/* Bring in Tatsuyuki Satoh's OPL emulation */
#define HAS_YM3812 1
#include "fmopl.c"
}
struct OPLTimer_t {
bool isEnabled;
bool isMasked;
bool isOverflowed;
Bit32u count;
Bit32u base;
};
static OPLTimer_t timer1,timer2;
static MAME::FM_OPL * myopl;
static Bit8u regsel;
#define OPL_INTERNAL_FREQ 3600000 // The OPL operates at 3.6MHz
static MIXER_Channel * adlib_chan;
static void ADLIB_CallBack(Bit8u *stream, Bit32u len) {
/* Check for size to update and check for 1 ms updates to the opl registers */
/* Calculate teh machine ms we are at now */
/* update 1 ms of data */
MAME::YM3812UpdateOne(myopl,(MAME::INT16 *)stream,len);
}
static Bit8u read_p388(Bit32u port) {
Bit8u ret=0;
Bit32u new_ticks=GetTicks();
if (timer1.isEnabled) {
if ((new_ticks-timer1.base)>timer1.count) {
timer1.isOverflowed=true;
timer1.base=new_ticks;
}
if (timer1.isOverflowed || !timer1.isMasked) {
ret|=0xc0;
}
}
if (timer2.isEnabled) {
if ((new_ticks-timer2.base)>timer2.count) {
timer2.isOverflowed=true;
timer2.base=new_ticks;
}
if (timer2.isOverflowed || !timer2.isMasked) {
ret|=0xA0;
}
}
return ret;
}
static void write_p388(Bit32u port,Bit8u val) {
regsel=val;
}
static void write_p389(Bit32u port,Bit8u val) {
switch (regsel) {
case 0x02: /* Timer 1 */
timer1.count=(val*80/1000);
return;
case 0x03: /* Timer 2 */
timer2.count=(val*320/1000);
return;
case 0x04: /* IRQ clear / mask and Timer enable */
if (val&0x80) {
timer1.isOverflowed=false;
timer2.isOverflowed=false;
return;
}
if (val&0x40) {
timer1.isMasked=true;
} else {
timer1.isMasked=false;
timer1.isEnabled=((val&1)>0);
timer1.base=GetTicks();
}
if (val&0x20) {
timer2.isMasked=true;
} else {
timer2.isMasked=false;
timer2.isEnabled=((val&2)>0);
timer2.base=GetTicks();
}
return;
default: /* Normal OPL call queue it */
MAME::OPLWriteReg(myopl,regsel,val);
}
}
static HWBlock hw_adlib;
static bool adlib_enabled;
static void ADLIB_Enable(bool enable) {
if (enable) {
adlib_enabled=true;
MIXER_Enable(adlib_chan,true);
IO_RegisterWriteHandler(0x388,write_p388,"ADLIB Register select");
IO_RegisterWriteHandler(0x389,write_p389,"ADLIB Data Write");
IO_RegisterReadHandler(0x388,read_p388,"ADLIB Status");
IO_RegisterWriteHandler(0x220,write_p388,"ADLIB Register select");
IO_RegisterWriteHandler(0x221,write_p389,"ADLIB Data Write");
IO_RegisterReadHandler(0x220,read_p388,"ADLIB Status");
} else {
adlib_enabled=false;
MIXER_Enable(adlib_chan,false);
IO_FreeWriteHandler(0x220);
IO_FreeWriteHandler(0x221);
IO_FreeReadHandler(0x220);
IO_FreeWriteHandler(0x388);
IO_FreeWriteHandler(0x389);
IO_FreeReadHandler(0x388);
}
}
static void ADLIB_InputHandler(char * line) {
bool s_off=ScanCMDBool(line,"OFF");
bool s_on=ScanCMDBool(line,"ON");
char * rem=ScanCMDRemain(line);
if (rem) {
sprintf(line,"Illegal Switch");
return;
}
if (s_on && s_off) {
sprintf(line,"Can't use /ON and /OFF at the same time");
return;
}
if (s_on) {
ADLIB_Enable(true);
sprintf(line,"Adlib has been enabled");
return;
}
if (s_off) {
ADLIB_Enable(false);
sprintf(line,"Adlib has been disabled");
return;
}
return;
}
static void ADLIB_OutputHandler (char * towrite) {
if(adlib_enabled) {
sprintf(towrite,"IO %X",0x388);
} else {
sprintf(towrite,"Disabled");
}
};
void ADLIB_Init(void) {
timer1.isMasked=true;
timer1.base=0;
timer1.count=0;
timer1.isEnabled=false;
timer1.isOverflowed=false;
timer2.isMasked=true;
timer2.base=0;
timer2.count=0;
timer2.isEnabled=false;
timer2.isOverflowed=false;
#define ADLIB_FREQ 22050
myopl=MAME::OPLCreate(0,OPL_INTERNAL_FREQ,ADLIB_FREQ);
adlib_chan=MIXER_AddChannel(ADLIB_CallBack,ADLIB_FREQ,"ADLIB");
MIXER_SetMode(adlib_chan,MIXER_16MONO);
hw_adlib.dev_name="ADLIB";
hw_adlib.full_name="Adlib FM Synthesizer";
hw_adlib.next=0;
hw_adlib.help="/ON Enables Adlib\n/OFF Disables Adlib\n";
hw_adlib.get_input=ADLIB_InputHandler;
hw_adlib.show_status=ADLIB_OutputHandler;
HW_Register(&hw_adlib);
ADLIB_Enable(true);
};

225
src/hardware/dma.cpp Normal file
View file

@ -0,0 +1,225 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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.
*/
/*
Based the port handling from the bochs dma code.
*/
/*
Still need to implement reads from dma ports :)
Perhaps sometime also implement dma writes.
DMA transfer that get setup with a size 0 are also done wrong should be 0x10000 probably.
*/
#include <string.h>
#include "dosbox.h"
#include "mem.h"
#include "inout.h"
#include "dma.h"
#ifdef DEBUG_DMA
#define DMA_DEBUG LOG_DEBUG
#else
#define DMA_DEBUG
#endif
#define DMA_MODE_DEMAND 0
#define DMA_MODE_SINGLE 1
#define DMA_MODE_BLOCK 2
#define DMA_MODE_CASCADE 3
struct DMA_CHANNEL {
struct {
Bit8u mode_type;
Bit8u address_decrement;
Bit8u autoinit_enable;
Bit8u transfer_type;
} mode;
Bit16u base_address;
Bit16u base_count;
Bit16u current_address;
Bit32u current_count;
Bit8u page;
bool masked;
HostPt host_address;
bool addr_changed;
};
struct DMA_CONTROLLER {
bool flipflop;
Bit8u status_reg;
Bit8u command_reg;
DMA_CHANNEL chan[4];
};
static DMA_CONTROLLER dma[2];
static void write_dma(Bit32u port,Bit8u val) {
/* only use first dma for now */
DMA_CONTROLLER * cont=&dma[0];
DMA_CHANNEL * chan=&cont->chan[port>>1];
switch (port) {
case 0x00:case 0x02:case 0x04:case 0x06:
if (cont->flipflop) {
chan->base_address=val;
} else {
chan->base_address|=(val<<8);
}
cont->flipflop=!cont->flipflop;
chan->addr_changed=true;
break;
case 0x01:case 0x03:case 0x05:case 0x07:
if (cont->flipflop) {
chan->base_count=val;
} else {
chan->base_count|=(val<<8);
}
cont->flipflop=!cont->flipflop;
chan->addr_changed=true;
break;
case 0x08: /* Command Register */
if (val != 4) LOG_WARN("DMA1:Illegal command %2X",val);
cont->command_reg=val;
break;
case 0x09: /* Request Register */
if (val&4) {
/* Set Request bit */
} else {
Bitu channel = val & 0x03;
cont->status_reg &= ~(1 << (channel+4));
}
break;
case 0x0a: /* single mask bit register */
chan->masked=(val & 4)>0;
break;
case 0x0b: /* mode register */
chan->mode.mode_type = (val >> 6) & 0x03;
chan->mode.address_decrement = (val >> 5) & 0x01;
chan->mode.autoinit_enable = (val >> 4) & 0x01;
chan->mode.transfer_type = (val >> 2) & 0x03;
if (chan->mode.address_decrement) {
LOG_WARN("DMA:Address Decrease not supported yet");
}
break;
case 0x0c: /* Clear Flip/Flip */
cont->flipflop=true;
break;
};
};
static Bit8u channelindex[7] = {2, 3, 1, 0, 0, 0, 0};
void write_dma_page(Bit32u port,Bit8u val) {
switch (port) {
case 0x81: /* dma0 page register, channel 2 */
case 0x82: /* dma0 page register, channel 3 */
case 0x83: /* dma0 page register, channel 1 */
case 0x87: /* dma0 page register, channel 0 */
Bitu channel = channelindex[port - 0x81];
dma[0].chan[channel].page=val;
dma[0].chan[channel].addr_changed=true;
break;
}
}
void DMA_8_Read(Bit32u dmachan,Bit8u * buffer,Bit16u count) {
DMA_CHANNEL * chan=&dma[0].chan[dmachan];
/* DMA always does autoinit should work under normal situations */
if (chan->addr_changed) {
/* Calculate the new starting position for dma read*/
chan->addr_changed=false;
chan->host_address=memory+(chan->page << 16)+chan->base_address;
chan->current_count=chan->base_count+1;
chan->current_address=chan->base_address;
DMA_DEBUG("DMA:Transfer from %d size %d",(chan->page << 16)+chan->base_address,chan->current_count);
}
if (!count) return;
if (chan->current_count>=count) {
memcpy(buffer,chan->host_address,count);
chan->host_address+=count;
chan->current_address+=count;
chan->current_count-=count;
return;
} else {
/* Copy remaining piece of first buffer */
memcpy(buffer,chan->host_address,chan->current_count);
buffer+=chan->current_count;
count-=(Bit16u)chan->current_count;
/* Autoinit reset the dma channel */
chan->host_address=memory+(chan->page << 16)+chan->base_address;
chan->current_count=chan->base_count+1;
chan->current_address=chan->base_address;
/* Copy the rest of the buffer */
memcpy(buffer,chan->host_address,count);
chan->host_address+=count;
chan->current_address+=count;
chan->current_count-=count;
return;
}
};
void DMA_8_Write(Bit32u dmachan,Bit8u * buffer,Bit16u count) {
if (dma[0].chan[dmachan].addr_changed) {
/* Calculate the new starting position for dma read*/
dma[0].chan[dmachan].addr_changed=false;
dma[0].chan[dmachan].host_address=memory+(dma[0].chan[dmachan].page << 16)+dma[0].chan[dmachan].base_address;
dma[0].chan[dmachan].current_count=dma[0].chan[dmachan].base_count;
dma[0].chan[dmachan].current_count=dma[0].chan[dmachan].current_count;
}
if (dma[0].chan[dmachan].current_count>=count) {
memcpy(dma[0].chan[dmachan].host_address,buffer,count);
dma[0].chan[dmachan].host_address+=count;
dma[0].chan[dmachan].current_address+=count;
dma[0].chan[dmachan].current_count-=count;
return;
} else {
}
return;
};
void DMA_16_Read(Bit32u dmachan,Bit8u * buffer,Bit16u count) {
}
void DMA_16_Write(Bit32u dmachan,Bit8u * buffer,Bit16u count) {
}
void DMA_Init(void) {
for (Bit32u i=0;i<0x10;i++) {
IO_RegisterWriteHandler(i,write_dma,"DMA1");
}
IO_RegisterWriteHandler(0x81,write_dma_page,"DMA Pages");
IO_RegisterWriteHandler(0x82,write_dma_page,"DMA Pages");
IO_RegisterWriteHandler(0x83,write_dma_page,"DMA Pages");
IO_RegisterWriteHandler(0x87,write_dma_page,"DMA Pages");
}

9730
src/hardware/ega-switch.h Normal file

File diff suppressed because it is too large Load diff

1878
src/hardware/fmopl.c Normal file

File diff suppressed because it is too large Load diff

191
src/hardware/fmopl.h Normal file
View file

@ -0,0 +1,191 @@
#ifndef __FMOPL_H_
#define __FMOPL_H_
/* --- select emulation chips --- */
#define BUILD_YM3812 (HAS_YM3812)
#define BUILD_YM3526 (HAS_YM3526)
#define BUILD_Y8950 (HAS_Y8950)
/* --- system optimize --- */
/* select bit size of output : 8 or 16 */
#define OPL_SAMPLE_BITS 16
/* compiler dependence */
#ifndef OSD_CPU_H
#define OSD_CPU_H
typedef unsigned char UINT8; /* unsigned 8bit */
typedef unsigned short UINT16; /* unsigned 16bit */
typedef unsigned int UINT32; /* unsigned 32bit */
typedef signed char INT8; /* signed 8bit */
typedef signed short INT16; /* signed 16bit */
typedef signed int INT32; /* signed 32bit */
#endif
#if (OPL_SAMPLE_BITS==16)
typedef INT16 OPLSAMPLE;
#endif
#if (OPL_SAMPLE_BITS==8)
typedef unsigned char OPLSAMPLE;
#endif
#if BUILD_Y8950
#include "ymdeltat.h"
#endif
typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec);
typedef void (*OPL_IRQHANDLER)(int param,int irq);
typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us);
typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data);
typedef unsigned char (*OPL_PORTHANDLER_R)(int param);
/* !!!!! here is private section , do not access there member direct !!!!! */
#define OPL_TYPE_WAVESEL 0x01 /* waveform select */
#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */
#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */
#define OPL_TYPE_IO 0x08 /* I/O port */
/* Saving is necessary for member of the 'R' mark for suspend/resume */
/* ---------- OPL slot ---------- */
typedef struct fm_opl_slot {
const UINT32 *AR; /* attack rate tab :&eg_table[AR<<2]*/
const UINT32 *DR; /* decay rate tab :&eg_table[DR<<2]*/
const UINT32 *RR; /* release rate tab:&eg_table[RR<<2]*/
UINT8 KSR; /* key scale rate */
UINT8 ARval; /* current AR */
UINT8 ksl; /* keyscale level */
UINT8 ksr; /* key scale rate :kcode>>KSR */
UINT8 mul; /* multiple :ML_TABLE[ML] */
/* Phase Generator */
UINT32 Cnt; /* frequency count */
UINT32 Incr; /* frequency step */
/* Envelope Generator */
UINT8 eg_type; /* percussive/non-percussive mode */
UINT8 state; /* phase type */
UINT32 TL; /* total level :TL << 3 */
INT32 TLL; /* adjusted now TL */
INT32 volume; /* envelope counter */
UINT32 sl; /* sustain level :SL_TABLE[SL] */
UINT32 delta_ar; /* envelope step for Attack */
UINT32 delta_dr; /* envelope step for Decay */
UINT32 delta_rr; /* envelope step for Release */
UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */
/* LFO */
UINT32 AMmask; /* LFO Amplitude Modulation enable mask */
UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/
/* waveform select */
unsigned int *wavetable;
}OPL_SLOT;
/* ---------- OPL one of channel ---------- */
typedef struct fm_opl_channel {
OPL_SLOT SLOT[2];
UINT8 FB; /* feedback shift value */
INT32 *connect1; /* slot1 output pointer */
INT32 op1_out[2]; /* slot1 output for feedback */
/* phase generator state */
UINT32 block_fnum; /* block+fnum */
UINT32 fc; /* Freq. Increment base */
UINT32 ksl_base; /* KeyScaleLevel Base step */
UINT8 kcode; /* key code (for key scaling) */
UINT8 CON; /* connection (algorithm) type */
} OPL_CH;
/* OPL state */
typedef struct fm_opl_f {
/* FM channel slots */
OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels */
UINT8 rhythm; /* Rhythm mode */
UINT32 eg_tab[16+64+16]; /* EG rate table: 16 (dummy) + 64 rates + 16 RKS */
UINT32 fn_tab[1024]; /* fnumber -> increment counter */
/* LFO */
UINT8 lfo_am_depth;
UINT8 lfo_pm_depth_range;
UINT32 lfo_am_cnt;
UINT32 lfo_am_inc;
UINT32 lfo_pm_cnt;
UINT32 lfo_pm_inc;
UINT32 noise_rng; /* 23 bit noise shift register */
UINT32 noise_p; /* current noise 'phase' */
UINT32 noise_f; /* current noise period */
UINT8 wavesel; /* waveform select enable flag */
int T[2]; /* timer counters */
UINT8 st[2]; /* timer enable */
#if BUILD_Y8950
/* Delta-T ADPCM unit (Y8950) */
YM_DELTAT *deltat;
/* Keyboard / I/O interface unit*/
UINT8 portDirection;
UINT8 portLatch;
OPL_PORTHANDLER_R porthandler_r;
OPL_PORTHANDLER_W porthandler_w;
int port_param;
OPL_PORTHANDLER_R keyboardhandler_r;
OPL_PORTHANDLER_W keyboardhandler_w;
int keyboard_param;
#endif
/* external event callback handlers */
OPL_TIMERHANDLER TimerHandler; /* TIMER handler */
int TimerParam; /* TIMER parameter */
OPL_IRQHANDLER IRQHandler; /* IRQ handler */
int IRQParam; /* IRQ parameter */
OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */
int UpdateParam; /* stream update parameter */
UINT8 type; /* chip type */
UINT8 address; /* address register */
UINT8 status; /* status flag */
UINT8 statusmask; /* status mask */
UINT8 mode; /* Reg.08 : CSM,notesel,etc. */
int clock; /* master clock (Hz) */
int rate; /* sampling rate (Hz) */
double freqbase; /* frequency base */
double TimerBase; /* Timer base time (==sampling time)*/
} FM_OPL;
/* ---------- Generic interface section ---------- */
#define OPL_TYPE_YM3526 (0)
#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO)
FM_OPL *OPLCreate(int type, int clock, int rate);
void OPLDestroy(FM_OPL *OPL);
void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset);
void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param);
void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param);
/* Y8950 port handlers */
void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param);
void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param);
void OPLResetChip(FM_OPL *OPL);
int OPLWrite(FM_OPL *OPL,int a,int v);
unsigned char OPLRead(FM_OPL *OPL,int a);
int OPLTimerOver(FM_OPL *OPL,int c);
/* YM3626/YM3812 local section */
void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
#endif

2562
src/hardware/font-switch.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,256 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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 <math.h>
#include "dosbox.h"
#include "inout.h"
#include "mixer.h"
#include "mem.h"
#include "hardware.h"
#define CMS_RATE 22050
#define CMS_VOLUME 6000
#define FREQ_SHIFT 16
#define SIN_ENT 1024
#define SIN_MAX (SIN_ENT << FREQ_SHIFT)
#ifndef PI
#define PI 3.14159265358979323846
#endif
struct CMS {
struct {
Bit32u freq_pos;
Bit32u freq_add;
Bit16s * vol_left;
Bit16s * vol_right;
Bit8u octave;
Bit8u freq;
} chan[6];
struct {
Bit32u freq_pos;
Bit32u freq_add;
Bit32u random_val;
} noise[2];
Bit8u voice_enabled;
Bit8u noise_enabled;
Bit8u reg;
};
static Bit32u freq_table[256][8];
static Bit32u noise_freq[3];
static Bit16s vol_table[16];
static Bit16s sin_table[16][SIN_ENT];
static MIXER_Channel * cms_chan;
static CMS cms_block[2];
static void write_cms(Bit32u port,Bit8u val) {
Bit32u sel=(port>>1)&1;
CMS * cms=&cms_block[sel];
switch (port & 1) {
case 1: /* Register Select */
cms->reg=val;
break;
case 0: /* Write Register */
switch (cms->reg) {
case 0x00: case 0x01: case 0x02: /* Volume Select */
case 0x03: case 0x04: case 0x05:
cms->chan[cms->reg].vol_left=sin_table[val & 0xf];
cms->chan[cms->reg].vol_right=sin_table[(val>>4) & 0xf];
break;
case 0x08: case 0x09: case 0x0a: /* Frequency Select */
case 0x0b: case 0x0c: case 0x0d:
{
Bit8u chan=cms->reg-0x08;
cms->chan[chan].freq=val;
/* Get a new entry in the freq table */
cms->chan[chan].freq_add=freq_table[cms->chan[chan].freq][cms->chan[chan].octave];
break;
}
case 0x10: case 0x11: case 0x12: /* Octave Select */
{
Bit8u chan=(cms->reg-0x10)*2;
cms->chan[chan].octave=val&7;
cms->chan[chan].freq_add=freq_table[cms->chan[chan].freq][cms->chan[chan].octave];
chan++;
cms->chan[chan].octave=(val>>4)&7;
cms->chan[chan].freq_add=freq_table[cms->chan[chan].freq][cms->chan[chan].octave];
}
break;
case 0x14: /* Frequency enable */
cms->voice_enabled=val;
//TODO Check for enabling of speaker maybe
break;
case 0x15: /* Noise Enable */
cms->noise_enabled=val;
//TODO Check for enabling of speaker maybe
break;
case 0x16: /* Noise generator setup */
cms->noise[0].freq_add=noise_freq[val & 3];
cms->noise[1].freq_add=noise_freq[(val>>4) & 3];
break;
default:
LOG_ERROR("CMS %d:Illegal register %X2 Selected for write",sel,cms->reg);
break;
};
break;
}
};
static void CMS_CallBack(Bit8u * stream,Bit32u len) {
/* Generate the CMS wave */
/* Generate 12 channels of sound data this could be nice */
for (Bit32u l=0;l<len;l++) {
register Bit32s left,right;
left=right=0;
for (int c=0;c<2;c++) {
CMS * cms=&cms_block[c];
Bit8u use_voice=1;
for (int chan=0;chan<6;chan++) {
if (cms->noise_enabled & use_voice) {
} else if (cms->voice_enabled & use_voice) {
int pos=cms->chan[chan].freq_pos>>FREQ_SHIFT;
left+=cms->chan[chan].vol_left[pos];
right+=cms->chan[chan].vol_right[pos];
cms->chan[chan].freq_pos+=cms->chan[chan].freq_add;
if (cms->chan[chan].freq_pos>=SIN_MAX)
cms->chan[chan].freq_pos-=SIN_MAX;
}
use_voice<<=1;
}
}
if (left>MAX_AUDIO) *(Bit16s *)stream=MAX_AUDIO;
else if (left<MIN_AUDIO) *(Bit16s *)stream=MIN_AUDIO;
else *(Bit16s *)stream=(Bit16s)left;
stream+=2;
if (right>MAX_AUDIO) *(Bit16s *)stream=MAX_AUDIO;
else if (right<MIN_AUDIO) *(Bit16s *)stream=MIN_AUDIO;
else *(Bit16s *)stream=(Bit16s)right;
stream+=2;
}
}
static HWBlock hw_cms;
static bool cms_enabled;
static void CMS_Enable(bool enable) {
if (enable) {
cms_enabled=true;
MIXER_Enable(cms_chan,true);
IO_RegisterWriteHandler(0x220,write_cms,"CMS");
IO_RegisterWriteHandler(0x221,write_cms,"CMS");
IO_RegisterWriteHandler(0x222,write_cms,"CMS");
IO_RegisterWriteHandler(0x223,write_cms,"CMS");
} else {
cms_enabled=false;
MIXER_Enable(cms_chan,false);
IO_FreeWriteHandler(0x220);
IO_FreeWriteHandler(0x221);
IO_FreeWriteHandler(0x222);
IO_FreeWriteHandler(0x223);
}
}
static void CMS_InputHandler(char * line) {
bool s_off=ScanCMDBool(line,"OFF");
bool s_on=ScanCMDBool(line,"ON");
char * rem=ScanCMDRemain(line);
if (rem) {
sprintf(line,"Illegal Switch");
return;
}
if (s_on && s_off) {
sprintf(line,"Can't use /ON and /OFF at the same time");
return;
}
if (s_on) {
CMS_Enable(true);
sprintf(line,"Creative Music System has been enabled");
return;
}
if (s_off) {
CMS_Enable(false);
sprintf(line,"Creative Music System has been disabled");
return;
}
return;
}
static void CMS_OutputHandler (char * towrite) {
if(cms_enabled) {
sprintf(towrite,"IO %X",0x220);
} else {
sprintf(towrite,"Disabled");
}
};
void CMS_Init(void) {
Bits i;
/* Register the Mixer CallBack */
cms_chan=MIXER_AddChannel(CMS_CallBack,CMS_RATE,"CMS");
MIXER_SetMode(cms_chan,MIXER_16STEREO);
MIXER_Enable(cms_chan,false);
/* Register with the hardware setup tool */
hw_cms.dev_name="CMS";
hw_cms.full_name="Creative Music System";
hw_cms.next=0;
hw_cms.help="/ON Enables CMS\n/OFF Disables CMS\n";
hw_cms.get_input=CMS_InputHandler;
hw_cms.show_status=CMS_OutputHandler;
HW_Register(&hw_cms);
CMS_Enable(false);
/* Make the frequency/octave table */
double log_start=log10(27.34375);
double log_add=(log10(54.609375)-log10(27.34375))/256;
for (i=0;i<256;i++) {
double freq=pow(10,log_start);
for (int k=0;k<8;k++) {
freq_table[i][k]=(Bit32u)((double)SIN_MAX/(CMS_RATE/freq));
freq*=2;
}
log_start+=log_add;
}
// noise_freq[0]=(Bit32u)(FREQ_MAX/((float)CMS_RATE/(float)28000));
// noise_freq[1]=(Bit32u)(FREQ_MAX/((float)CMS_RATE/(float)14000));
// noise_freq[2]=(Bit32u)(FREQ_MAX/((float)CMS_RATE/(float)6800));
for (int s=0;s<SIN_ENT;s++) {
double out=sin( (2*PI/SIN_ENT)*s)*CMS_VOLUME;
for (i=15;i>=0;i--) {
sin_table[i][s]=(Bit16s)out;
// out /= (float)1.258925412; /* = 10 ^ (2/20) = 2dB */
out /= 1.1;
}
}
}

117
src/hardware/hardware.cpp Normal file
View file

@ -0,0 +1,117 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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.
*/
/*
This could do with a serious revision :)
*/
#include <string.h>
#include "dosbox.h"
#include "programs.h"
#include "hardware.h"
static HWBlock * firsthw=0;
class HWSET : public Program {
public:
HWSET(PROGRAM_Info * program_info);
void Run(void);
};
HWSET::HWSET(PROGRAM_Info * info):Program(info) {
}
void HW_Register(HWBlock * block) {
block->next=firsthw;
firsthw=block;
}
void HWSET::Run(void) {
/* Hopefull enough space */
char buf[1024];
HWBlock * loopblock;
if (!*prog_info->cmd_line) {
/* No command line given give overview of hardware */
loopblock=firsthw;
WriteOut("ÚÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿");
WriteOut("³Device ³Full Name ³Status ³");
WriteOut("ÃÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´");
while (loopblock) {
if (loopblock->show_status) {
loopblock->show_status(buf);
} else {
strcpy(buf,"No Status Handler");
}
WriteOut("³%-8s³%-25s³%-43s³",loopblock->dev_name,loopblock->full_name,buf);
loopblock=loopblock->next;
}
WriteOut("ÀÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ");
WriteOut("If you want to setup specific hardware use \"HWSET Device\" for information.\n");
return;
}
/* Command line given */
if (strcmp(prog_info->cmd_line,"/?")==0) {
WriteOut("Hardware setup tool for DOSBox.\n");
WriteOut("This program can be used to change the settings of internal hardware.\n\n"
"HWSET [device] [switches]\n\n"
"Using an empty command line gives you a list of hardware.\n"
"You can get list of switches for a device with HWSET device.\n"
);
return;
}
char * rest=strchr(prog_info->cmd_line,' ');
if (rest) *rest++=0;
loopblock=firsthw;
while (loopblock) {
if (strcasecmp(loopblock->dev_name,prog_info->cmd_line)==0) goto founddev;
loopblock=loopblock->next;
}
WriteOut("Device %s not found\n",prog_info->cmd_line);
return;
founddev:
/* Check for rest of line */
if (rest) {
strcpy(buf,rest);
loopblock->get_input(buf);
WriteOut(buf);
} else {
WriteOut("Command overview for %s\n%s",loopblock->full_name,loopblock->help);
}
return;
}
static void HWSET_ProgramStart(PROGRAM_Info * info) {
HWSET * tempHWSET=new HWSET(info);
tempHWSET->Run();
delete tempHWSET;
}
void HARDWARE_Init(void) {
PROGRAMS_MakeFile("HWSET.COM",HWSET_ProgramStart);
};

View file

@ -0,0 +1,91 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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 "dosbox.h"
#include "inout.h"
IO_ReadBlock IO_ReadTable[IO_MAX];
IO_WriteBlock IO_WriteTable[IO_MAX];
void IO_Write(Bitu num,Bit8u val) {
if (num<IO_MAX) IO_WriteTable[num].handler(num,val);
else LOG_ERROR("IO:Out or range write %X2 to port %4X",val,num);
}
Bit8u IO_Read(Bitu num) {
if (num<IO_MAX) return IO_ReadTable[num].handler(num);
else LOG_ERROR("IO:Out or range read from port %4X",num);
return 0xff;
}
static Bit8u IO_ReadBlocked(Bit32u port) {
return 0xff;
}
static void IO_WriteBlocked(Bit32u port,Bit8u val) {
}
static Bit8u IO_ReadDefault(Bit32u port) {
LOG_WARN("Reading from undefined port %04X",port);
IO_RegisterReadHandler(port,&IO_ReadBlocked,"Blocked Read");
return 0xff;
}
void IO_WriteDefault(Bit32u port,Bit8u val) {
LOG_WARN("Writing %02X to undefined port %04X",val,port);
IO_RegisterWriteHandler(port,&IO_WriteBlocked,"Blocked Write");
}
void IO_RegisterReadHandler(Bit32u port,IO_ReadHandler * handler,char * name) {
if (port<IO_MAX) {
IO_ReadTable[port].handler=handler;
IO_ReadTable[port].name=name;
}
}
void IO_RegisterWriteHandler(Bit32u port,IO_WriteHandler * handler,char * name) {
if (port<IO_MAX) {
IO_WriteTable[port].handler=handler;
IO_WriteTable[port].name=name;
}
}
void IO_FreeReadHandler(Bit32u port) {
if (port<IO_MAX) {
IO_RegisterReadHandler(port,&IO_ReadDefault,"Default Read");
}
}
void IO_FreeWriteHandler(Bit32u port) {
if (port<IO_MAX) {
IO_RegisterWriteHandler(port,&IO_WriteDefault,"Default Write");
}
}
void IO_Init(void) {
for (Bitu i=0;i<IO_MAX;i++) {
IO_RegisterReadHandler(i,&IO_ReadDefault,"Default Read");
IO_RegisterWriteHandler(i,&IO_WriteDefault,"Default Write");
}
}

101
src/hardware/joystick.cpp Normal file
View file

@ -0,0 +1,101 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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 "dosbox.h"
#include "inout.h"
#define RANGE 64
struct JoyStick {
bool enabled;
float xpos,ypos;
Bitu xcount,ycount;
bool button[2];
};
static JoyStick stick[2];
static Bit8u read_p201(Bit32u port) {
/** Format of the byte to be returned:
** | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
** +-------------------------------+
** | | | | | | | |
** Joystick B, Button 2 ---+ | | | | | | +--- Joystick A, X Axis
** Joystick B, Button 1 -------+ | | | | +------- Joystick A, Y Axis
** Joystick A, Button 2 -----------+ | | +----------- Joystick B, X Axis
** Joystick A, Button 1 ---------------+ +--------------- Joystick B, Y Axis
**/
Bit8u ret=0xff;
if (stick[0].enabled) {
if (stick[0].xcount) stick[0].xcount--; else ret&=~1;
if (stick[0].ycount) stick[0].ycount--; else ret&=~2;
if (stick[0].button[0]) ret&=16;
if (stick[0].button[1]) ret&=32;
}
if (stick[1].enabled) {
if (stick[1].xcount) stick[0].xcount--; else ret&=~4;
if (stick[1].ycount) stick[0].ycount--; else ret&=~8;
if (stick[1].button[0]) ret&=64;
if (stick[1].button[1]) ret&=128;
}
return ret;
}
static void write_p201(Bit32u port,Bit8u val) {
if (stick[0].enabled) {
stick[0].xcount=(Bitu)(stick[0].xpos*RANGE+RANGE*2);
stick[0].ycount=(Bitu)(stick[0].ypos*RANGE+RANGE*2);
}
if (stick[1].enabled) {
stick[1].xcount=(Bitu)(stick[1].xpos*RANGE+RANGE*2);
stick[1].ycount=(Bitu)(stick[1].ypos*RANGE+RANGE*2);
}
}
void JOYSTICK_Enable(Bitu which,bool enabled) {
if (which<2) stick[which].enabled=enabled;
}
void JOYSTICK_Button(Bitu which,Bitu num,bool pressed) {
if ((which<2) && (num<2)) stick[which].button[num]=pressed;
}
void JOYSTICK_Move_X(Bitu which,float x) {
if (which<2) {
stick[which].xpos=x;
}
}
void JOYSTICK_Move_Y(Bitu which,float y) {
if (which<2) {
stick[which].ypos=y;
}
}
void JOYSTICK_Init(void) {
IO_RegisterReadHandler(0x201,read_p201,"JOYSTICK");
IO_RegisterWriteHandler(0x201,write_p201,"JOYSTICK");
stick[0].enabled=false;
stick[1].enabled=false;
}

260
src/hardware/keyboard.cpp Normal file
View file

@ -0,0 +1,260 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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 "keyboard.h"
#include "inout.h"
#include "pic.h"
#include "mixer.h"
#define KEYBUFSIZE 32
struct KeyEvent {
Bitu type;
Bitu state;
KEYBOARD_EventHandler * handler;
KeyEvent * next;
};
static Bitu shift_state=0;
static Bit8u cur_scancode;
static Bit8u kbuf[KEYBUFSIZE];
static Bitu kbuf_pos;
static Bitu kbuf_used;
static Bit8u port_61_data;
//TODO Are these initialized at 0 at startup? Hope so :)
static KeyEvent * event_handlers[KBD_LAST];
static Bit8u read_p60(Bit32u port) {
if (kbuf_used>0) {
cur_scancode=kbuf[kbuf_pos];
};
return cur_scancode;
}
static void write_p60(Bit32u port,Bit8u val) {
//TODO Work this out ;)
LOG_DEBUG("Port 60 write with val %d",val);
}
static Bit8u read_p61(Bit32u port) {
return port_61_data;
};
static void write_p61(Bit32u port,Bit8u val) {
//TODO Enable spreaker through here :)
if ((val&128)) { /* Keyboard acknowledge */
kbuf_used--;
kbuf_pos++;
if (kbuf_pos>=KEYBUFSIZE) kbuf_pos=0;
if (kbuf_used>0) PIC_ActivateIRQ(1);
}
port_61_data=val;
if ((val & 3)==3) {
PCSPEAKER_Enable(true);
} else {
PCSPEAKER_Enable(false);
}
}
void KEYBOARD_AddCode(Bit8u code) {
//Now Raise the keyboard IRQ
//If the buffer is full just drop the scancode :)
if (kbuf_used<KEYBUFSIZE) {
Bit32u start=kbuf_used+kbuf_pos;
kbuf_used++;
if (start>=KEYBUFSIZE) start-=KEYBUFSIZE;
kbuf[start]=code;
}
PIC_ActivateIRQ(1);
}
void KEYBOARD_AddEvent(Bitu keytype,Bitu state,KEYBOARD_EventHandler * handler) {
KeyEvent * newevent=new KeyEvent;
/* Add the event in the correct key structure */
if (keytype>=KBD_LAST) {
LOG_ERROR("KEYBOARD:Illegal key %d for handler",keytype);
}
newevent->next=event_handlers[keytype];
event_handlers[keytype]=newevent;
newevent->type=keytype;
newevent->state=state;
newevent->handler=handler;
};
void KEYBOARD_AddKey(Bitu keytype,bool pressed) {
bool extend=false;
Bit8u ret=0;
switch (keytype) {
case KBD_esc:ret=1;break;
case KBD_1:ret=2;break;
case KBD_2:ret=3;break;
case KBD_3:ret=4;break;
case KBD_4:ret=5;break;
case KBD_5:ret=6;break;
case KBD_6:ret=7;break;
case KBD_7:ret=8;break;
case KBD_8:ret=9;break;
case KBD_9:ret=10;break;
case KBD_0:ret=11;break;
case KBD_minus:ret=12;break;
case KBD_equals:ret=13;break;
case KBD_backspace:ret=14;break;
case KBD_tab:ret=15;break;
case KBD_q:ret=16;break;
case KBD_w:ret=17;break;
case KBD_e:ret=18;break;
case KBD_r:ret=19;break;
case KBD_t:ret=20;break;
case KBD_y:ret=21;break;
case KBD_u:ret=22;break;
case KBD_i:ret=23;break;
case KBD_o:ret=24;break;
case KBD_p:ret=25;break;
case KBD_leftbracket:ret=26;break;
case KBD_rightbracket:ret=27;break;
case KBD_enter:ret=28;break;
case KBD_leftctrl:ret=29;
shift_state=(shift_state&~CTRL_PRESSED)|(pressed ? CTRL_PRESSED:0);
break;
case KBD_a:ret=30;break;
case KBD_s:ret=31;break;
case KBD_d:ret=32;break;
case KBD_f:ret=33;break;
case KBD_g:ret=34;break;
case KBD_h:ret=35;break;
case KBD_j:ret=36;break;
case KBD_k:ret=37;break;
case KBD_l:ret=38;break;
case KBD_semicolon:ret=39;break;
case KBD_quote:ret=40;break;
case KBD_grave:ret=41;break;
case KBD_leftshift:ret=42;
shift_state=(shift_state&~SHIFT_PRESSED)|(pressed ? SHIFT_PRESSED:0);
break;
case KBD_backslash:ret=43;break;
case KBD_z:ret=44;break;
case KBD_x:ret=45;break;
case KBD_c:ret=46;break;
case KBD_v:ret=47;break;
case KBD_b:ret=48;break;
case KBD_n:ret=49;break;
case KBD_m:ret=50;break;
case KBD_comma:ret=51;break;
case KBD_period:ret=52;break;
case KBD_slash:ret=53;break;
case KBD_rightshift:ret=54;break;
case KBD_kpmultiply:ret=55;break;
case KBD_leftalt:ret=56;
shift_state=(shift_state&~ALT_PRESSED)|(pressed ? ALT_PRESSED:0);
break;
case KBD_space:ret=57;break;
case KBD_capslock:ret=58;break;
case KBD_f1:ret=59;break;
case KBD_f2:ret=60;break;
case KBD_f3:ret=61;break;
case KBD_f4:ret=62;break;
case KBD_f5:ret=63;break;
case KBD_f6:ret=64;break;
case KBD_f7:ret=65;break;
case KBD_f8:ret=66;break;
case KBD_f9:ret=67;break;
case KBD_f10:ret=68;break;
case KBD_numlock:ret=69;break;
case KBD_scrolllock:ret=70;break;
case KBD_kp7:ret=71;break;
case KBD_kp8:ret=72;break;
case KBD_kp9:ret=73;break;
case KBD_kpminus:ret=74;break;
case KBD_kp4:ret=75;break;
case KBD_kp5:ret=76;break;
case KBD_kp6:ret=77;break;
case KBD_kpplus:ret=78;break;
case KBD_kp1:ret=79;break;
case KBD_kp2:ret=80;break;
case KBD_kp3:ret=81;break;
case KBD_kp0:ret=82;break;
case KBD_kpperiod:ret=83;break;
case KBD_f11:ret=87;break;
case KBD_f12:ret=88;break;
//The Extended keys
case KBD_kpenter:extend=true;ret=28;break;
case KBD_rightctrl:extend=true;ret=29;break;
case KBD_kpslash:extend=true;ret=53;break;
case KBD_rightalt:extend=true;ret=56;break;
case KBD_home:extend=true;ret=71;break;
case KBD_up:extend=true;ret=72;break;
case KBD_pageup:extend=true;ret=73;break;
case KBD_left:extend=true;ret=75;break;
case KBD_right:extend=true;ret=77;break;
case KBD_end:extend=true;ret=79;break;
case KBD_down:extend=true;ret=80;break;
case KBD_pagedown:extend=true;ret=81;break;
case KBD_insert:extend=true;ret=82;break;
case KBD_delete:extend=true;ret=83;break;
default:
E_Exit("Unsopperted key press");
break;
};
/* check for active key events */
KeyEvent * checkevent=event_handlers[keytype];
while (checkevent) {
if ((shift_state & checkevent->state)==checkevent->state) {
if (checkevent->type==keytype && pressed) {
(*checkevent->handler)();
return;
}
if (checkevent->type==keytype) return;
}
checkevent=checkevent->next;
}
if (extend) KEYBOARD_AddCode(224);
if (!pressed) ret+=128;
KEYBOARD_AddCode(ret);
};
void KEYBOARD_Init(void) {
kbuf_used=0;kbuf_pos=0;
IO_RegisterWriteHandler(0x60,write_p60,"Keyboard");
IO_RegisterReadHandler(0x60,read_p60,"Keyboard");
IO_RegisterWriteHandler(0x61,write_p61,"Keyboard");
IO_RegisterReadHandler(0x61,read_p61,"Keyboard");
port_61_data=1; /* Speaker control through PIT and speaker disabled */
// memset(&event_handlers,0,sizeof(event_handlers));
};

299
src/hardware/memory.cpp Normal file
View file

@ -0,0 +1,299 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "dosbox.h"
#include "mem.h"
#define MEM_MAXSIZE 16 /* The Size of memory used to get size of page table */
#define memsize 8 /* 8 mb of memory */
#define EMM_HANDLECOUNT 250
EMM_Handle EMM_Handles[EMM_HANDLECOUNT];
PageEntry * PageEntries[MEM_MAXSIZE*1024*16]; /* Number of pages */
Bit8u * memory=0;
bool MEMORY_TestSpecial(PhysPt off) {
return (PageEntries[off >> 12]>0);
}
void MEMORY_SetupHandler(Bit32u page,Bit32u pages,PageEntry * entry) {
for (Bit32u i=page;i<page+pages;i++) {
PageEntries[i]=entry;
}
}
void MEMORY_ResetHandler(Bit32u page,Bit32u pages) {
for (Bit32u i=page;i<page+pages;i++) {
PageEntries[i]=0;
}
};
void MEM_BlockRead(PhysPt off,void * data,Bitu size) {
Bitu c;
Bit8u * idata=(Bit8u *)data;
for (c=1;c<=(size>>2);c++) {
writed(idata,mem_readd(off));
idata+=4;off+=4;
}
for (c=1;c<=(size&3);c++) {
writeb(idata,mem_readb(off));
idata+=1;off+=1;
}
}
void MEM_BlockWrite(PhysPt off,void * data,Bitu size) {
Bitu c;
Bit8u * idata=(Bit8u *)data;
for (c=1;c<=(size>>2);c++) {
mem_writed(off,readd(idata));
idata+=4;off+=4;
}
for (c=1;c<=(size&3);c++) {
mem_writeb(off,readb(idata));
idata+=1;off+=1;
}
}
void MEM_BlockCopy(PhysPt dest,PhysPt src,Bitu size) {
Bitu c;
for (c=1;c<=(size>>2);c++) {
mem_writed(dest,mem_readd(src));
dest+=4;src+=4;
}
for (c=1;c<=(size&3);c++) {
mem_writeb(dest,mem_readb(src));
dest+=1;src+=1;
}
};
void MEM_StrCopy(PhysPt off,char * data,Bitu size) {
Bit8u c;
while ((c=mem_readb(off)) && size) {
*data=c;
off++;data++;size--;
}
*data='\0';
}
/* TODO Maybe check for page boundaries but that would be wasting lot's of time */
void mem_writeb(PhysPt off,Bit8u val) {
PageEntry * entry=PageEntries[off >> 12];
if (!entry) { writeb(memory+off,val);return; }
switch (entry->type) {
case MEMORY_RELOCATE:
writeb(entry->relocate+(off-entry->base),val);
break;
case MEMORY_HANDLER:
entry->handler.write(off-entry->base,val);
break;
default:
E_Exit("Write to Illegal Memory Address %4x",off);
}
}
void mem_writew(PhysPt off,Bit16u val) {
PageEntry * entry=PageEntries[off >> 12];
if (!entry) { writew(memory+off,val);return; }
switch (entry->type) {
case MEMORY_RELOCATE:
writew(entry->relocate+(off-entry->base),val);
break;
case MEMORY_HANDLER:
entry->handler.write(off-entry->base,(val & 0xFF));
entry->handler.write(off-entry->base+1,(val >> 8));
break;
default:
E_Exit("Write to Illegal Memory Address %4x",off);
}
}
void mem_writed(PhysPt off,Bit32u val) {
PageEntry * entry=PageEntries[off >> 12];
if (!entry) { writed(memory+off,val);return; }
switch (entry->type) {
case MEMORY_RELOCATE:
writed(entry->relocate+(off-entry->base),val);
break;
case MEMORY_HANDLER:
entry->handler.write(off-entry->base, (Bit8u)(val & 0xFF));
entry->handler.write(off-entry->base+1,(Bit8u)(val >> 8) & 0xFF);
entry->handler.write(off-entry->base+2,(Bit8u)(val >> 16) & 0xFF);
entry->handler.write(off-entry->base+3,(Bit8u)(val >> 24) & 0xFF);
break;
default:
E_Exit("Write to Illegal Memory Address %4x",off);
}
}
Bit8u mem_readb(PhysPt off) {
PageEntry * entry=PageEntries[off >> 12];
if (!entry) { return readb(memory+off);}
switch (entry->type) {
case MEMORY_RELOCATE:
return readb(entry->relocate+(off-entry->base));
case MEMORY_HANDLER:
return entry->handler.read(off-entry->base);
break;
default:
E_Exit("Read from Illegal Memory Address %4x",off);
}
return 0; /* Keep compiler happy */
}
Bit16u mem_readw(PhysPt off) {
PageEntry * entry=PageEntries[off >> 12];
if (!entry) { return readw(memory+off);}
switch (entry->type) {
case MEMORY_RELOCATE:
return readw(entry->relocate+(off-entry->base));
case MEMORY_HANDLER:
return entry->handler.read(off-entry->base) |
(entry->handler.read(off-entry->base+1) << 8);
break;
default:
E_Exit("Read from Illegal Memory Address %4x",off);
}
return 0; /* Keep compiler happy */
}
Bit32u mem_readd(PhysPt off) {
PageEntry * entry=PageEntries[off >> 12];
if (!entry) { return readd(memory+off);}
switch (entry->type) {
case MEMORY_RELOCATE:
return readd(entry->relocate+(off-entry->base));
case MEMORY_HANDLER:
return entry->handler.read(off-entry->base) |
(entry->handler.read(off-entry->base+1) << 8) |
(entry->handler.read(off-entry->base+2) << 16)|
(entry->handler.read(off-entry->base+3) << 24);
break;
default:
E_Exit("Read from Illegal Memory Address %4x",off);
}
return 0; /* Keep compiler happy */
}
/* The EMM Allocation Part */
/* If this returns 0 we got and error since 0 is always taken */
static Bit16u EMM_GetFreeHandle(void) {
Bit16u i=0;
while (i<EMM_HANDLECOUNT) {
if (!EMM_Handles[i].active) return i;
i++;
}
E_Exit("MEMORY:Out of EMM Memory handles");
return 0;
}
void EMM_GetFree(Bit16u * maxblock,Bit16u * total) {
Bit32u index=0;
*maxblock=0;*total=0;
while (EMM_Handles[index].active) {
if (EMM_Handles[index].free) {
if(EMM_Handles[index].size>*maxblock) *maxblock=EMM_Handles[index].size;
*total+=EMM_Handles[index].size;
}
if (EMM_Handles[index].next) index=EMM_Handles[index].next;
else break;
}
}
void EMM_Allocate(Bit16u size,Bit16u * handle) {
Bit16u index=0;*handle=0;
while (EMM_Handles[index].active) {
if (EMM_Handles[index].free) {
/* Use entire block */
if(EMM_Handles[index].size==size) {
EMM_Handles[index].free=false;
*handle=index;
break;
}
/* Split up block */
if(EMM_Handles[index].size>size) {
Bit16u newindex=EMM_GetFreeHandle();
EMM_Handles[newindex].active=true;
EMM_Handles[newindex].phys_base=EMM_Handles[newindex].phys_base+size*4096;
EMM_Handles[newindex].size=EMM_Handles[index].size-size;
EMM_Handles[newindex].free=true;
EMM_Handles[newindex].next=EMM_Handles[index].next;
EMM_Handles[index].next=newindex;
EMM_Handles[index].free=false;
EMM_Handles[index].size=size;
*handle=index;
break;
}
}
if (EMM_Handles[index].next) index=EMM_Handles[index].next;
else break;
}
}
void EMM_Free(Bit16u handle) {
if (!EMM_Handles[handle].active) E_Exit("EMM:Tried to free illegal handle");
EMM_Handles[handle].free=true;
//TODO join memory blocks
}
PageEntry HMA_PageEntry;
void MEM_Init(void) {
memset((void *)&PageEntries,0,sizeof(PageEntries));
memory=(Bit8u *)malloc(memsize*1024*1024);
if (!memory) {
E_Exit("Can't allocate memory for memory");
}
/* Setup the HMA to wrap */
HMA_PageEntry.type=MEMORY_RELOCATE;;
HMA_PageEntry.base=1024*1024;
HMA_PageEntry.relocate=memory;
Bitu i;
for (i=0;i<16;i++) {
PageEntries[i+256]=&HMA_PageEntry;
}
/* Setup the EMM Structures */
for (i=0;i<EMM_HANDLECOUNT;i++) {
EMM_Handles[i].active=false;
EMM_Handles[i].size=0;
}
/* Setup the first handle with free and max memory */
EMM_Handles[0].active=true;
EMM_Handles[0].free=false;
EMM_Handles[0].phys_base=0x110000;
EMM_Handles[0].next=1;
if (memsize>1) {
EMM_Handles[1].size=(memsize-1)*256-16;
} else {
EMM_Handles[0].size=0;;
}
EMM_Handles[1].active=true;
EMM_Handles[1].free=true;
EMM_Handles[1].phys_base=0x110000;
};

254
src/hardware/mixer.cpp Normal file
View file

@ -0,0 +1,254 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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.
*/
/*
Remove the sdl code from here and have it handeld in the sdlmain.
That should call the mixer start from there or something.
*/
#include <string.h>
#include <SDL/SDL.h>
#include "dosbox.h"
#include "mixer.h"
#include "timer.h"
#define MIXER_MAXCHAN 8
#define MIXER_BLOCKSIZE 1024
#define MIXER_BUFSIZE MIXER_BLOCKSIZE*8
#define MIXER_SSIZE 4
#define MIXER_SHIFT 16
#define MIXER_REMAIN ((1<<MIXER_SHIFT)-1)
#define MIXER_FREQ 22050
struct MIXER_Channel {
Bit8u volume;
Bit8u mode;
Bit32u freq;
char * name;
MIXER_MixHandler handler;
Bit32u sample_add;
Bit32u sample_remain;
Bit16s sample_data[2];
bool playing;
MIXER_Channel * next;
};
static MIXER_Channel * first_channel;
static union {
Bit16s temp_m16[MIXER_BUFSIZE][1];
Bit16s temp_s16[MIXER_BUFSIZE][2];
Bit8u temp_m8[MIXER_BUFSIZE][1];
Bit8u temp_s8[MIXER_BUFSIZE][2];
};
static Bit16s mix_bufout[MIXER_BUFSIZE][2];
static Bit16s mix_buftemp[MIXER_BUFSIZE][2];
static Bit32s mix_bufextra;
static Bit32u mix_writepos;
static Bit32u mix_readpos;
static Bit32u mix_ticks;
static Bit32u mix_add;
static Bit32u mix_remain;
MIXER_Channel * MIXER_AddChannel(MIXER_MixHandler handler,Bit32u freq,char * name) {
//TODO Find a free channel
MIXER_Channel * chan=new MIXER_Channel;
if (!chan) return 0;
chan->playing=false;
chan->volume=255;
chan->mode=MIXER_16STEREO;
chan->handler=handler;
chan->name=name;
chan->sample_add=(freq<<MIXER_SHIFT)/MIXER_FREQ;
chan->next=first_channel;
first_channel=chan;
return chan;
};
void MIXER_SetFreq(MIXER_Channel * chan,Bit32u freq) {
if (chan) {
chan->freq=freq;
/* Calculate the new addition value */
chan->sample_add=(freq<<MIXER_SHIFT)/MIXER_FREQ;
}
}
void MIXER_SetMode(MIXER_Channel * chan,Bit8u mode) {
if (chan) chan->mode=mode;
};
void MIXER_SetVolume(MIXER_Channel * chan,Bit8u vol) {
if (chan) chan->volume=vol;
}
void MIXER_Enable(MIXER_Channel * chan,bool enable) {
if (chan) chan->playing=enable;
}
/* Mix a certain amount of new samples */
static void MIXER_MixData(Bit32u samples) {
/* This Should mix the channels */
if (!samples) return;
if (samples>MIXER_BUFSIZE) samples=MIXER_BUFSIZE;
/* 16-bit stereo is 4 bytes per sample */
memset((void *)&mix_buftemp,0,samples*MIXER_SSIZE);
MIXER_Channel * chan=first_channel;
while (chan) {
if (chan->playing) {
Bit32u chan_samples=samples*chan->sample_add;
Bit32u real_samples=chan_samples>>MIXER_SHIFT;
if (chan_samples & MIXER_REMAIN) real_samples++;
(chan->handler)((Bit8u*)&temp_m8,real_samples);
switch (chan->mode) {
case MIXER_8MONO:
/* Mix a 8 bit mono stream into the final 16 bit stereo output stream */
{
/* Mix the data with output buffer */
Bit32s newsample;Bit32u sample_read=0;Bit32u sample_add=chan->sample_add;
for (Bit32u mix=0;mix<samples;mix++) {
Bit32u pos=sample_read >> MIXER_SHIFT;
sample_read+=sample_add;
newsample=mix_buftemp[mix][0]+((Bit8s)(temp_m8[pos][0]^0x80) << 8);
if (newsample>MAX_AUDIO) mix_buftemp[mix][0]=MAX_AUDIO;
else if (newsample<MIN_AUDIO) mix_buftemp[mix][0]=MIN_AUDIO;
else mix_buftemp[mix][0]=(Bit16s)newsample;
newsample=mix_buftemp[mix][1]+((Bit8s)(temp_m8[pos][0]^0x80) << 8);
if (newsample>MAX_AUDIO) mix_buftemp[mix][1]=MAX_AUDIO;
else if (newsample<MIN_AUDIO) mix_buftemp[mix][1]=MIN_AUDIO;
else mix_buftemp[mix][1]=(Bit16s)newsample;
}
break;
}
case MIXER_16MONO:
/* Mix a 16 bit mono stream into the final 16 bit stereo output stream */
{
Bit32s newsample;Bit32u sample_read=0;Bit32u sample_add=chan->sample_add;
for (Bit32u mix=0;mix<samples;mix++) {
Bit32u pos=sample_read >> MIXER_SHIFT;
sample_read+=sample_add;
newsample=mix_buftemp[mix][0]+temp_m16[pos][0];
if (newsample>MAX_AUDIO) mix_buftemp[mix][0]=MAX_AUDIO;
else if (newsample<MIN_AUDIO) mix_buftemp[mix][0]=MIN_AUDIO;
else mix_buftemp[mix][0]=(Bit16s)newsample;
newsample=mix_buftemp[mix][1]+temp_m16[pos][0];
if (newsample>MAX_AUDIO) mix_buftemp[mix][1]=MAX_AUDIO;
else if (newsample<MIN_AUDIO) mix_buftemp[mix][1]=MIN_AUDIO;
else mix_buftemp[mix][1]=(Bit16s)newsample;
}
break;
}
case MIXER_16STEREO:
/* Mix a 16 bit stereo stream into the final 16 bit stereo output stream */
{
Bit32s newsample;Bit32u sample_read=0;Bit32u sample_add=chan->sample_add;
for (Bit32u mix=0;mix<samples;mix++) {
Bit32u pos=sample_read >> MIXER_SHIFT;
sample_read+=sample_add;
newsample=mix_buftemp[mix][0]+temp_s16[pos][0];
if (newsample>MAX_AUDIO) mix_buftemp[mix][0]=MAX_AUDIO;
else if (newsample<MIN_AUDIO) mix_buftemp[mix][0]=MIN_AUDIO;
else mix_buftemp[mix][0]=(Bit16s)newsample;
newsample=mix_buftemp[mix][1]+temp_s16[pos][1];
if (newsample>MAX_AUDIO) mix_buftemp[mix][1]=MAX_AUDIO;
else if (newsample<MIN_AUDIO) mix_buftemp[mix][1]=MIN_AUDIO;
else mix_buftemp[mix][1]=(Bit16s)newsample;
}
break;
}
default:
E_Exit("MIXER:Illegal sound mode %2X",chan->mode);
}
}
chan=chan->next;
}
Bit32u buf_remain=MIXER_BUFSIZE-mix_writepos;
/* Fill the samples size buffer with 0's */
if (buf_remain>samples) {
memcpy(&mix_bufout[mix_writepos][0],&mix_buftemp[0][0],samples*MIXER_SSIZE);
mix_writepos+=samples;
} else {
memcpy(&mix_bufout[mix_writepos][0],&mix_buftemp[0][0],buf_remain*MIXER_SSIZE);
memcpy(&mix_bufout[0][0],&mix_buftemp[buf_remain][0],(samples-buf_remain)*MIXER_SSIZE);
mix_writepos=(mix_writepos+samples)-MIXER_BUFSIZE;
}
}
void MIXER_Mix(Bitu ticks) {
/* Check for 1 ms of sound to mix */
Bitu count=(ticks*mix_add)+mix_remain;
mix_remain=count&((1<<10)-1);
count>>=10;
Bit32u size=MIXER_BUFSIZE+mix_writepos-mix_readpos;
if (size>=MIXER_BUFSIZE) size-=MIXER_BUFSIZE;
if (size>MIXER_BLOCKSIZE+2048) return;
MIXER_MixData(count);
}
static Bit32u last_pos;
static void MIXER_CallBack(void * userdata, Uint8 *stream, int len) {
/* Copy data from buf_out to the stream */
Bit32u remain=MIXER_BUFSIZE-mix_readpos;
if (remain>=len/MIXER_SSIZE) {
memcpy((void *)stream,(void *)&mix_bufout[mix_readpos][0],len);
} else {
memcpy((void *)stream,(void *)&mix_bufout[mix_readpos][0],remain*MIXER_SSIZE);
stream+=remain*MIXER_SSIZE;
memcpy((void *)stream,(void *)&mix_bufout[0][0],(len)-remain*MIXER_SSIZE);
}
mix_readpos+=(len/MIXER_SSIZE);
if (mix_readpos>=MIXER_BUFSIZE) mix_readpos-=MIXER_BUFSIZE;
}
void MIXER_Init(void) {
/* Initialize the internal stuff */
first_channel=0;
mix_ticks=GetTicks();
mix_bufextra=0;
mix_writepos=0;
mix_readpos=0;
/* Start the Mixer using SDL Sound at 22 khz */
SDL_AudioSpec spec;
SDL_AudioSpec obtained;
mix_add=((MIXER_FREQ) << 10)/1000;
mix_remain=0;
spec.freq=MIXER_FREQ;
spec.format=AUDIO_S16SYS;
spec.channels=2;
spec.callback=MIXER_CallBack;
spec.userdata=NULL;
spec.samples=MIXER_BLOCKSIZE;
TIMER_RegisterTickHandler(MIXER_Mix);
if ( SDL_OpenAudio(&spec, &obtained) < 0 ) {
LOG_MSG("No sound output device found, starting in no sound mode");
} else {
MIXER_MixData(MIXER_BLOCKSIZE/MIXER_SSIZE);
SDL_PauseAudio(0);
}
}

View file

@ -0,0 +1,88 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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 <dosbox.h>
#include <mixer.h>
#include <math.h>
#ifndef PI
#define PI 3.14159265358979323846
#endif
#define SPKR_RATE 22050
#define SPKR_VOLUME 5000
#define FREQ_SHIFT 16
#define FREQ_MAX (2 << FREQ_SHIFT)
#define FREQ_HALF (FREQ_MAX >> 1)
struct Speaker {
Bit32u freq_add;
Bit32u freq_pos;
Bit16s volume;
MIXER_Channel * chan;
bool enabled;
};
static Speaker spkr;
void PCSPEAKER_SetFreq(Bit32u freq) {
spkr.freq_add=(Bit32u)(FREQ_MAX/((float)SPKR_RATE/(float)freq));
spkr.freq_pos=0;
}
void PCSPEAKER_Enable(bool enable) {
spkr.enabled=enable;
MIXER_Enable(spkr.chan,enable);
}
static void PCSPEAKER_CallBack(Bit8u * stream,Bit32u len) {
/* Generate the speaker wave, TODO Improve :) */
for (Bit32u c=0;c<len;c++) {
spkr.freq_pos+=spkr.freq_add;
if (spkr.freq_pos>=FREQ_MAX) spkr.freq_pos-=FREQ_MAX;
if (spkr.freq_pos>=FREQ_HALF) {
*(Bit16s*)(stream)=spkr.volume;
} else {
*(Bit16s*)(stream)=-spkr.volume;
}
/*
if (spkr.freq_pos>=FREQ_HALF) {
spkr.freq_pos-=FREQ_HALF;
spkr.volume=-spkr.volume;
};
*(Bit16s*)(stream)=spkr.volume;
*/
stream+=2;
}
}
void PCSPEAKER_Init(void) {
spkr.chan=MIXER_AddChannel(&PCSPEAKER_CallBack,SPKR_RATE,"PC-SPEAKER");
MIXER_Enable(spkr.chan,false);
MIXER_SetMode(spkr.chan,MIXER_16MONO);
spkr.volume=SPKR_VOLUME;
spkr.enabled=false;
}

369
src/hardware/pic.cpp Normal file
View file

@ -0,0 +1,369 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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 "dosbox.h"
#include "inout.h"
#include "cpu.h"
#include "pic.h"
struct IRQ_Block {
bool masked;
bool active;
bool inservice;
Bit8u vector;
char * name;
PIC_EOIHandler * handler;
};
Bit32u PIC_IRQCheck;
static IRQ_Block irqs[16];
static Bit8u pic0_icws=0;
static Bit8u pic1_icws=0;
static Bit8u pic0_icw_state=0;
static Bit8u pic1_icw_state=0;
static bool pic0_request_iisr=0;
static bool pic1_request_iisr=0;
//TODO Special mask mode in ocw3
//TODO maybe check for illegal modes that don't work on pc too and exit the emu
//Pic 0 command port
static void write_p20(Bit32u port,Bit8u val) {
Bit32u i;
switch (val) {
case 0x0A: /* select read interrupt request register */
pic0_request_iisr=false;
break;
case 0x0B: /* select read interrupt in-service register */
pic0_request_iisr=true;
break;
case 0x10: /* ICW1 */
pic0_icws=2;
pic0_icw_state=1;
break;
case 0x11: /* ICW1 + need for ICW4 */
pic0_icws=3;
pic0_icw_state=1;
break;
case 0x20: /* end of interrupt command */
/* clear highest current in service bit */
for (i=0;i<=7;i++) {
if (irqs[i].inservice) {
irqs[i].inservice=false;
if (irqs[i].handler!=0) irqs[i].handler();
break;
};
};
break;
case 0x60: /* specific EOI 0 */
case 0x61: /* specific EOI 1 */
case 0x62: /* specific EOI 2 */
case 0x63: /* specific EOI 3 */
case 0x64: /* specific EOI 4 */
case 0x65: /* specific EOI 5 */
case 0x66: /* specific EOI 6 */
case 0x67: /* specific EOI 7 */
if (irqs[val-0x60].inservice) {
irqs[val-0x60].inservice=false;
if (irqs[val-0x60].handler!=0) irqs[val-0x60].handler();
};
break;
// IRQ lowest priority commands
case 0xC0: // 0 7 6 5 4 3 2 1
case 0xC1: // 1 0 7 6 5 4 3 2
case 0xC2: // 2 1 0 7 6 5 4 3
case 0xC3: // 3 2 1 0 7 6 5 4
case 0xC4: // 4 3 2 1 0 7 6 5
case 0xC5: // 5 4 3 2 1 0 7 6
case 0xC6: // 6 5 4 3 2 1 0 7
case 0xC7: // 7 6 5 4 3 2 1 0
// ignore for now TODO
break;
default:
E_Exit("PIC0:Unhandled command %02X",val);
}
}
//Pic 0 Interrupt mask
static void write_p21(Bit32u port,Bit8u val) {
Bit8u i;
switch(pic0_icw_state) {
case 0: /* mask register */
for (i=0;i<=7;i++) {
irqs[i].masked=(val&(1<<i))>0;
if (irqs[i].active && !irqs[i].masked) PIC_IRQCheck|=(1 << 1);
else PIC_IRQCheck&=~(1 << i);
};
break;
case 1: /* icw2 */
for (i=0;i<=7;i++) {
irqs[i].vector=(val&0xf8)+i;
};
default: /* icw2, 3, and 4*/
if(pic0_icw_state++ >= pic0_icws) pic0_icw_state=0;
}
}
static Bit8u read_p20(Bit32u port) {
Bit8u ret=0;
Bit32u i;
Bit8u b=1;
if (pic0_request_iisr) {
for (i=0;i<=7;i++) {
if (irqs[i].inservice) ret|=b;
b <<= 1;
}
} else {
for (i=0;i<=7;i++) {
if (irqs[i].active) ret|=b;
b <<= 1;
}
};
return ret;
}
static Bit8u read_p21(Bit32u port) {
Bit8u ret=0;
Bit32u i;
Bit8u b=1;
for (i=0;i<=7;i++) {
if (irqs[i].masked) ret|=b;
b <<= 1;
}
return ret;
}
static void write_pa0(Bit32u port,Bit8u val) {
Bit32u i;
switch (val) {
case 0x0A: /* select read interrupt request register */
pic1_request_iisr=false;
break;
case 0x0B: /* select read interrupt in-service register */
pic1_request_iisr=true;
break;
case 0x10: /* ICW1 */
/* Clear everything set full mask and clear all inservice */
for (i=0;i<=7;i++) {
irqs[i].masked=true;
irqs[i].active=false;
irqs[i].inservice=false;
}
pic1_icws=2;
pic1_icw_state=1;
break;
case 0x11: /* ICW1 + need for ICW4 */
pic1_icws=3;
pic1_icw_state=1;
break;
case 0x20: /* end of interrupt command */
/* clear highest current in service bit */
for (i=8;i<=15;i++) {
if (irqs[i].inservice) {
irqs[i].inservice=false;
if (irqs[i].handler!=0) irqs[i].handler();
break;
};
};
break;
case 0x60: /* specific EOI 0 */
case 0x61: /* specific EOI 1 */
case 0x62: /* specific EOI 2 */
case 0x63: /* specific EOI 3 */
case 0x64: /* specific EOI 4 */
case 0x65: /* specific EOI 5 */
case 0x66: /* specific EOI 6 */
case 0x67: /* specific EOI 7 */
if (irqs[val-0x60+8].inservice) {
irqs[val-0x60+8].inservice=false;
if (irqs[val-0x60+8].handler!=0) irqs[val-0x60+8].handler();
};
break;
// IRQ lowest priority commands
case 0xC0: // 0 7 6 5 4 3 2 1
case 0xC1: // 1 0 7 6 5 4 3 2
case 0xC2: // 2 1 0 7 6 5 4 3
case 0xC3: // 3 2 1 0 7 6 5 4
case 0xC4: // 4 3 2 1 0 7 6 5
case 0xC5: // 5 4 3 2 1 0 7 6
case 0xC6: // 6 5 4 3 2 1 0 7
case 0xC7: // 7 6 5 4 3 2 1 0
//TODO Maybe does it even matter?
break;
default:
E_Exit("Unhandled command %04X sent to port A0",val);
}
}
static void write_pa1(Bit32u port,Bit8u val) {
Bit8u i;
switch(pic1_icw_state) {
case 0: /* mask register */
for (i=0;i<=7;i++) {
irqs[i+8].masked=(val&1 <<i)>0;
};
break;
case 1: /* icw2 */
for (i=0;i<=7;i++) {
irqs[i+8].vector=(val&0xf8)+i;
};
default: /* icw2, 3, and 4*/
if(pic1_icw_state++ >= pic1_icws) pic1_icw_state=0;
}
}
static Bit8u read_pa0(Bit32u port) {
Bit8u ret=0;
Bit32u i;
Bit8u b=1;
if (pic1_request_iisr) {
for (i=0;i<=7;i++) {
if (irqs[i+8].inservice) ret|=b;
b <<= 1;
}
} else {
for (i=0;i<=7;i++) {
if (irqs[i+8].active) ret|=b;
b <<= 1;
}
};
return ret;
}
static Bit8u read_pa1(Bit32u port) {
Bit8u ret=0;
Bit32u i;
Bit8u b=1;
for (i=0;i<=7;i++) {
if (irqs[i+8].masked) ret|=b;
b <<= 1;
}
return ret;
}
void PIC_RegisterIRQ(Bit32u irq,PIC_EOIHandler handler,char * name) {
if (irq>15) E_Exit("PIC:Illegal IRQ");
irqs[irq].name=name;
irqs[irq].handler=handler;
}
void PIC_FreeIRQ(Bit32u irq) {
if (irq>15) E_Exit("PIC:Illegal IRQ");
irqs[irq].name=0;
irqs[irq].handler=0;
irqs[irq].active=0;
irqs[irq].inservice=0;
PIC_IRQCheck&=~(1 << irq);
}
void PIC_ActivateIRQ(Bit32u irq) {
if (irq<16) {
irqs[irq].active=true;
if (!irqs[irq].masked) {
PIC_IRQCheck|=(1 << irq);
}
}
}
void PIC_DeActivateIRQ(Bit32u irq) {
if (irq<16) {
irqs[irq].active=false;
PIC_IRQCheck&=~(1 << irq);
}
}
bool PIC_IRQActive(Bit32u irq) {
if (irq<16) {
return irqs[irq].active;
}
return true;
}
#define PIC_MAXQUEUE 16
static PIC_Function * pic_queue[PIC_MAXQUEUE];
static Bit32u pic_queuesize;
void PIC_QueueFunction(PIC_Function * function) {
if (pic_queuesize<PIC_MAXQUEUE) {
pic_queue[pic_queuesize]=function;
pic_queuesize++;
} else {
E_Exit("PIC Queue OverFlow");
}
}
//TODO check for IRQ 2 being masked before checking 8-15 but then again do i need so many irqs
void PIC_runIRQs(void) {
Bit32u i;
if (!flags.intf) goto noirqs;
if (!PIC_IRQCheck) goto noirqs;
for (i=0;i<=15;i++) {
if (i!=2) {
if (!irqs[i].masked && irqs[i].active /* && !irqs[i].inservice */) {
irqs[i].inservice=true;
irqs[i].active=false;
PIC_IRQCheck&=~(1 << i);
Interrupt(irqs[i].vector);
break;
};
};
};
noirqs:
/* This also runs special hardware functions that can queue themselves here */
for (;pic_queuesize>0;) {
(*pic_queue[--pic_queuesize])();
}
};
void PIC_Init(void) {
/* Setup pic0 and pic1 with initial values like DOS has normally */
PIC_IRQCheck=0;
Bit8u i;
for (i=0;i<=7;i++) {
irqs[i].active=false;
irqs[i].masked=true;
irqs[i].inservice=false;
irqs[i+8].active=false;
irqs[i+8].masked=true;
irqs[i+8].inservice=false;
irqs[i].vector=0x8+i;
irqs[i+8].vector=0x70+i;
};
irqs[0].masked=false; /* Enable system timer */
irqs[1].masked=false; /* Enable Keyboard IRQ */
irqs[2].masked=false; /* Enable 2nd PIC Although i can't care if this is masked */
irqs[12].masked=false;
IO_RegisterReadHandler(0x20,read_p20,"Master PIC Command");
IO_RegisterReadHandler(0x21,read_p21,"Master PIC Data");
IO_RegisterWriteHandler(0x20,write_p20,"Master PIC Command");
IO_RegisterWriteHandler(0x21,write_p21,"Master PIC Data");
IO_RegisterReadHandler(0xa0,read_pa0,"Slave PIC Command");
IO_RegisterReadHandler(0xa1,read_pa1,"Slave PIC Data");
IO_RegisterWriteHandler(0xa0,write_pa0,"Slave PIC Command");
IO_RegisterWriteHandler(0xa1,write_pa1,"Slave PIC Data");
};

613
src/hardware/sblaster.cpp Normal file
View file

@ -0,0 +1,613 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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 "inout.h"
#include "mixer.h"
#include "dma.h"
#include "pic.h"
#include "hardware.h"
#define SB_BASE 0x220
#define SB_IRQ 5
#define SB_DMA 1
#define DSP_RESET 0x06
#define DSP_READ_DATA 0x0A
#define DSP_WRITE_DATA 0x0C
#define DSP_WRITE_STATUS 0x0C
#define DSP_READ_STATUS 0x0E
#define DSP_NO_COMMAND 0
#define DSP_MAJOR 2
#define DSP_MINOR 0
#define DSP_BUFSIZE 64
#define DSP_DACSIZE 4096
enum {DSP_S_RESET,DSP_S_NORMAL,DSP_S_HIGHSPEED};
enum {
MODE_NONE,MODE_DAC,
MODE_PCM_8S,MODE_PCM_8A,
MODE_ADPCM_4S
};
#ifdef DEBUG_SBLASTER
#define SB_DEBUG LOG_DEBUG
#else
#define SB_DEBUG
#endif
struct SB_INFO {
Bit16u freq;
Bitu samples_total;
Bitu samples_left;
bool speaker;
Bit8u time_constant;
bool use_time_constant;
Bit8u output_mode;
/* DSP Stuff */
Bit8u mode;
Bit8u state;
Bit8u cmd;
Bit8u cmd_len;
Bit8u cmd_in_pos;
Bit8u cmd_in[DSP_BUFSIZE];
Bit8u data_out[DSP_BUFSIZE];
Bit8u data_out_pos;
Bit8u data_out_used;
Bit8u dac_data[DSP_DACSIZE];
Bit32u dac_used;
Bit8u test_register;
/*ADPCM Part */
Bits adpcm_reference;
Bits adpcm_scale;
Bits adpcm_remain;
/* Hardware setup part */
Bit32u base;
Bit8u irq;
Bit8u dma;
bool enabled;
HWBlock hwblock;
MIXER_Channel * chan;
};
static SB_INFO sb;
static Bit8u e2_value;
static Bit8u e2_count;
static char * copyright_string="COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
static Bit8u DSP_cmd_len[256] = {
0,0,0,0, 0,2,0,0, 0,0,0,0, 0,0,0,0, // 0x00
1,0,0,0, 2,0,2,2, 0,0,0,0, 0,0,0,0, // 0x10
0,0,0,0, 2,0,0,0, 0,0,0,0, 0,0,0,0, // 0x20
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x30
1,2,2,0, 0,0,0,0, 2,0,0,0, 0,0,0,0, // 0x40
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x50
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x60
0,0,0,0, 2,2,2,2, 0,0,0,0, 0,0,0,0, // 0x70
2,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x80
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0x90
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0xa0
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0xb0
3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, // 0xc0
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // 0xd0
1,0,1,0, 1,0,0,0, 0,0,0,0, 0,0,0,0, // 0xe0
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 // 0xf0
};
static int E2_incr_table[4][9] = {
{ 0x01, -0x02, -0x04, 0x08, -0x10, 0x20, 0x40, -0x80, -106 },
{ -0x01, 0x02, -0x04, 0x08, 0x10, -0x20, 0x40, -0x80, 165 },
{ -0x01, 0x02, 0x04, -0x08, 0x10, -0x20, -0x40, 0x80, -151 },
{ 0x01, -0x02, 0x04, -0x08, -0x10, 0x20, -0x40, 0x80, 90 }
};
#ifndef max
#define max(a,b) ((a)>(b)?(a):(b))
#endif
#ifndef min
#define min(a,b) ((a)<(b)?(a):(b))
#endif
static void DSP_SetSpeaker(bool how) {
/* This should just set the mixer value */
MIXER_Enable(sb.chan,how);
sb.speaker=how;
}
static void DSP_HaltDMA(void) {
}
static INLINE void DSP_FlushData(void) {
sb.data_out_used=0;
sb.data_out_pos=0;
}
static void DSP_SetSampleRate(Bit32u rate) {
/* This directly changes the mixer */
}
static void DSP_StopDMA(void) {
sb.mode=MODE_NONE;
// MIXER_SetMode(sb.chan,MIXER_8MONO);
// MIXER_SetFreq(sb.chan,22050);
}
static void DSP_StartDMATranfser(Bit8u mode) {
sb.samples_left=sb.samples_total;
if (sb.use_time_constant) {
sb.freq=(1000000 / (256 - sb.time_constant));
};
switch (mode) {
case MODE_PCM_8S:
MIXER_SetFreq(sb.chan,sb.freq);
SB_DEBUG("DSP:PCM 8 bit single cycle rate %d size %d",sb.freq,sb.samples_total);
break;
case MODE_PCM_8A:
MIXER_SetFreq(sb.chan,sb.freq);
SB_DEBUG("DSP:PCM 8 bit auto init rate %d size %d",sb.freq,sb.samples_total);
break;
case MODE_ADPCM_4S:
MIXER_SetFreq(sb.chan,sb.freq*2);
SB_DEBUG("DSP:ADPCM 4 bit single cycle rate %d size %X",sb.freq,sb.samples_total);
break;
default:
LOG_ERROR("DSP:Illegal transfer mode %d",mode);
return;
}
/* Hack to enable dma transfer when game has speaker disabled */
DSP_SetSpeaker(true);
sb.mode=mode;
}
static void DSP_AddData(Bit8u val) {
if (sb.data_out_used<DSP_BUFSIZE) {
Bit32u start=sb.data_out_used+sb.data_out_pos;
if (start>=DSP_BUFSIZE) start-=DSP_BUFSIZE;
sb.data_out[start]=val;
sb.data_out_used++;
} else {
LOG_ERROR("SB:DSP:Data Output buffer full this is weird");
}
}
static void DSP_Reset(void) {
sb.mode=MODE_NONE;
sb.cmd_len=0;
sb.cmd_in_pos=0;
sb.use_time_constant=false;
sb.dac_used=0;
e2_value=0xaa;
e2_count=0;
DSP_HaltDMA();
MIXER_SetFreq(sb.chan,22050);
MIXER_SetMode(sb.chan,MIXER_8MONO);
DSP_SetSpeaker(false);
}
static void DSP_DoReset(Bit8u val) {
if (val&1!=0) {
//TODO Get out of highspeed mode
DSP_Reset();
sb.state=DSP_S_RESET;
} else {
DSP_FlushData();
DSP_AddData(0xaa);
sb.state=DSP_S_NORMAL;
}
};
static bool dac_warn=false;
static void DSP_DoCommand(void) {
switch (sb.cmd) {
case 0x10: /* Direct DAC */
sb.mode=MODE_DAC;
if (sb.dac_used<DSP_DACSIZE) {
sb.dac_data[sb.dac_used++]=sb.cmd_in[0];
}
break;
case 0x14: /* Singe Cycle 8-Bit DMA */
/* Set the length of the transfer */
sb.samples_total=1+sb.cmd_in[0]+(sb.cmd_in[1] << 8);
DSP_StartDMATranfser(MODE_PCM_8S);
break;
case 0x1c: /* Auto Init 8-bit DMA */
DSP_StartDMATranfser(MODE_PCM_8A);
break;
case 0x40: /* Set Timeconstant */
sb.use_time_constant=true;
sb.time_constant=sb.cmd_in[0];
break;
case 0x48: /* Set DMA Block Size */
sb.samples_total=1+sb.cmd_in[0]+(sb.cmd_in[1] << 8);
break;
case 0x75: /* 075h : Single Cycle 4-bit ADPCM Reference */
sb.adpcm_scale=0;
sb.adpcm_reference=-1;
sb.adpcm_remain=-1;
case 0x74: /* 074h : Single Cycle 4-bit ADPCM */
sb.samples_total=1+sb.cmd_in[0]+(sb.cmd_in[1] << 8);
DSP_StartDMATranfser(MODE_ADPCM_4S);
break;
case 0xd0: /* Halt 8-bit DMA */
DSP_HaltDMA();
break;
case 0xd1: /* Enable Speaker */
DSP_SetSpeaker(true);
break;
case 0xd3: /* Disable Speaker */
DSP_SetSpeaker(false);
break;
case 0xe0: /* DSP Identification - SB2.0+ */
DSP_FlushData();
DSP_AddData(~sb.cmd_in[0]);
break;
case 0xe1: /* Get DSP Version */
DSP_FlushData();
DSP_AddData(DSP_MAJOR);
DSP_AddData(DSP_MINOR);
break;
case 0xe2: /* Weird DMA identification write routine */
{
/*
for (Bit8u i = 0; i < 8; i++)
if ((sb.cmd_in[0] >> i) & 0x01) m_E2Value += E2_incr_table[m_E2Count % 4][i];
m_E2Value += E2_incr_table[m_E2Count % 4][8];
m_E2Count++;
*/
//TODO Ofcourse :)
}
break;
case 0xe3: /* DSP Copyright */
{
DSP_FlushData();
for (Bit32u i=0;i<=strlen(copyright_string);i++) {
DSP_AddData(copyright_string[i]);
}
}
break;
case 0xe4: /* Write Test Register */
sb.test_register=sb.cmd_in[0];
break;
case 0xe8: /* Read Test Register */
DSP_FlushData();
DSP_AddData(sb.test_register);;
break;
case 0xf2: /* Trigger 8bit IRQ */
DSP_FlushData();
DSP_AddData(0xaa);
PIC_ActivateIRQ(sb.irq);
break;
default:
LOG_WARN("SB:DSP:Unhandled command %2X",sb.cmd);
}
sb.cmd=DSP_NO_COMMAND;
sb.cmd_len=0;
sb.cmd_in_pos=0;
}
static void DSP_DoWrite(Bit8u val) {
switch (sb.cmd) {
case DSP_NO_COMMAND:
sb.cmd=val;
sb.cmd_len=DSP_cmd_len[val];
sb.cmd_in_pos=0;
if (!sb.cmd_len) DSP_DoCommand();
break;
default:
sb.cmd_in[sb.cmd_in_pos]=val;
sb.cmd_in_pos++;
if (sb.cmd_in_pos>=sb.cmd_len) DSP_DoCommand();
}
}
static Bit8u DSP_ReadData(void) {
Bit8u data=0;
if (sb.data_out_used) {
data=sb.data_out[sb.data_out_pos];
sb.data_out_pos++;
if (sb.data_out_pos>=DSP_BUFSIZE) sb.data_out_pos-=DSP_BUFSIZE;
sb.data_out_used--;
}
return data;
}
static Bit8u read_sb(Bit32u port) {
switch (port-sb.base) {
case DSP_READ_DATA:
return DSP_ReadData();
case DSP_READ_STATUS:
//TODO Acknowledge 8bit irq
//TODO See for high speed dma :)
if (sb.data_out_used) return 0xff;
else return 0x7f;
case DSP_WRITE_STATUS:
switch (sb.state) {
case DSP_S_NORMAL:
return 0x7f;
case DSP_S_RESET:
return 0xff;
}
return 0xff;
/* For now loop FM Stuff to 0x388 */
case 0x00: case 0x02: case 0x08:
return IO_Read(0x388);
case DSP_RESET:
return 0xff;
default:
LOG_WARN("SB:Unhandled read from SB Port %4X",port);
break;
}
return 0xff;
}
static void write_sb(Bit32u port,Bit8u val) {
switch (port-sb.base) {
case DSP_RESET:
DSP_DoReset(val);
break;
case DSP_WRITE_DATA:
DSP_DoWrite(val);
break;
/* For now loop FM Stuff to 0x388 */
case 0x00: case 0x02: case 0x08:
IO_Write(0x388,val);
break;
case 0x01: case 0x03: case 0x09:
IO_Write(0x389,val);
break;
default:
LOG_WARN("SB:Unhandled write to SB Port %4X",port);
break;
}
}
INLINE Bit8u decode_ADPCM_4_sample(
Bit8u sample,
Bits& reference,
Bits& scale)
{
static int scaleMap[8] = { -2, -1, 0, 0, 1, 1, 1, 1 };
if (sample & 0x08) {
reference = max(0x00, reference - ((sample & 0x07) << scale));
} else {
reference = min(0xff, reference + ((sample & 0x07) << scale));
}
scale = max(2, min(6, scaleMap[sample & 0x07]));
return (Bit8u)reference;
}
static void SBLASTER_CallBack(Bit8u * stream,Bit32u len) {
unsigned char tmpbuf[65536];
if (!len) return;
switch (sb.mode) {
case MODE_NONE:
/* If there isn't a mode it's 8 bit mono mode speaker should be disabled normally */
memset(stream,0x80,len);
break;
case MODE_DAC:
/* Stretch the inputted dac data over len samples */
{
Bit32u dac_add=(sb.dac_used<<16)/len;
Bit32u dac_pos=0;
while (len-->0) {
*(stream++)=sb.dac_data[dac_pos>>16];
dac_pos+=dac_add;
}
}
sb.dac_used=0;
sb.mode=MODE_NONE;
break;
case MODE_PCM_8A:
DMA_8_Read(sb.dma,stream,(Bit16u)len);
if (sb.samples_left>len) {
sb.samples_left-=len;
} else {
if (len>(sb.samples_total+sb.samples_left)) sb.samples_left=sb.samples_total;
else sb.samples_left=sb.samples_total+sb.samples_left-len;
PIC_ActivateIRQ(sb.irq);
}
break;
case MODE_PCM_8S:
if (sb.samples_left>=len) {
DMA_8_Read(sb.dma,stream,(Bit16u)len);
sb.samples_left-=len;
} else if (sb.samples_left && (sb.samples_left<len)) {
DMA_8_Read(sb.dma,stream,(Bit16u)sb.samples_left);
memset(stream+sb.samples_left,0x80,len-sb.samples_left);
sb.samples_left=0;
}
if (sb.samples_left==0) {
DSP_StopDMA();
PIC_ActivateIRQ(sb.irq);
}
break;
case MODE_ADPCM_4S:
{
if (sb.adpcm_remain>=0) {
*stream++=decode_ADPCM_4_sample((Bit8u)sb.adpcm_remain,sb.adpcm_reference,sb.adpcm_scale);
len--;
}
Bitu dma_size=len/2+(len&1); //Amount of bytes that need to be transferred
Bit8u * decode_pos=tmpbuf;
if (sb.adpcm_reference < 0) {
dma_size++;
}
/* Read from the DMA Channel */
if (sb.samples_left>=dma_size) {
DMA_8_Read(sb.dma,decode_pos,(Bit16u)dma_size);
sb.samples_left-=dma_size;
} else if (sb.samples_left<dma_size) {
DMA_8_Read(sb.dma,decode_pos,(Bit16u)sb.samples_left);
//Could go wrong with the reference byte i think.
memset(stream+sb.samples_left*2,0x80,len-sb.samples_left*2);
len=sb.samples_left*2;
sb.samples_left=0;
}
if (sb.samples_left==0) {
// if (sb.mode==MODE_PCM_8A) sb.samples_left=sb.samples_total;
// else
DSP_StopDMA();
PIC_ActivateIRQ(sb.irq);
}
if (sb.adpcm_reference < 0) {
sb.adpcm_reference=*decode_pos++;
}
for (Bitu i=len/2;i>0;i--) {
*stream++=decode_ADPCM_4_sample(*decode_pos >> 4,sb.adpcm_reference,sb.adpcm_scale);
*stream++=decode_ADPCM_4_sample(*decode_pos++ ,sb.adpcm_reference,sb.adpcm_scale);
}
if (len & 1) {
*stream++=decode_ADPCM_4_sample(*decode_pos >> 4,sb.adpcm_reference,sb.adpcm_scale);
sb.adpcm_remain=*decode_pos & 0xf;
} else {
sb.adpcm_remain=-1;
}
}
}
}
static bool SB_enabled;
static void SB_Enable(bool enable) {
Bitu i;
if (enable) {
sb.enabled=true;
for (i=sb.base+4;i<sb.base+0xf;i++) {
IO_RegisterReadHandler(i,read_sb,"SB");
IO_RegisterWriteHandler(i,write_sb,"SB");
}
PIC_RegisterIRQ(sb.irq,0,"SB");
DSP_Reset();
} else {
sb.enabled=false;
PIC_FreeIRQ(sb.irq);
for (i=sb.base+4;i<sb.base+0xf;i++){
IO_FreeReadHandler(i);
IO_FreeWriteHandler(i);
};
}
}
static void SB_InputHandler(char * line) {
bool s_off,s_on,s_base,s_irq,s_dma;
Bits n_base,n_irq,n_dma;
s_off=ScanCMDBool(line,"OFF");
s_on=ScanCMDBool(line,"ON");
s_base=ScanCMDHex(line,"BASE",&n_base);
s_irq=ScanCMDHex(line,"IRQ",&n_irq);
s_dma=ScanCMDHex(line,"DMA",&n_dma);
char * rem=ScanCMDRemain(line);
if (rem) {
sprintf(line,"Illegal Switch");
return;
}
if (s_on && s_off) {
sprintf(line,"Can't use /ON and /OFF at the same time");
return;
}
bool was_enabled=sb.enabled;
if (sb.enabled) SB_Enable(false);
if (s_base) {
sb.base=n_base;
}
if (s_irq) {
sb.irq=n_irq;
}
if (s_dma) {
sb.dma=n_dma;
}
if (s_on) {
SB_Enable(true);
}
if (s_off) {
SB_Enable(false);
sprintf(line,"Sound Blaster has been disabled");
return;
}
if (was_enabled) SB_Enable(true);
sprintf(line,"Sound Blaster enabled with IO %X IRQ %X DMA %X",sb.base,sb.irq,sb.dma);
return;
}
static void SB_OutputHandler (char * towrite) {
if(sb.enabled) {
sprintf(towrite,"IO %X IRQ %X DMA %X",sb.base,sb.irq,sb.dma);
} else {
sprintf(towrite,"Disabled");
}
};
void SBLASTER_Init(void) {
sb.chan=MIXER_AddChannel(&SBLASTER_CallBack,22050,"SBLASTER");
MIXER_Enable(sb.chan,false);
sb.state=DSP_S_NORMAL;
/* Setup the hardware handler part */
sb.base=SB_BASE;
sb.irq=SB_IRQ;
sb.dma=SB_DMA;
SB_Enable(true);
sb.hwblock.dev_name="SB";
sb.hwblock.full_name="Sound Blaster 1.5";
sb.hwblock.next=0;
sb.hwblock.help=
"/ON Enables SB\n"
"/OFF Disables SB\n"
"/BASE XXX Set Base Addres 200-260\n"
"/IRQ X Set IRQ 1-9\n"
"/DMA X Set 8-Bit DMA Channel 0-3\n";
sb.hwblock.get_input=SB_InputHandler;
sb.hwblock.show_status=SB_OutputHandler;
HW_Register(&sb.hwblock);
SHELL_AddAutoexec("SET BLASTER=A%3X I%d D%d T3",sb.base,sb.irq,sb.dma);
}

View file

@ -0,0 +1,143 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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.
*/
/*
Probably just use the mame code for the same chip sometime
*/
#include <math.h>
#include "dosbox.h"
#include "inout.h"
#include "mixer.h"
#include "mem.h"
#define TANDY_DIV 111860
#define TANDY_RATE 22050
#define BIT_SHIFT 16
#define TANDY_VOLUME 10000
static MIXER_Channel * tandy_chan;
struct TandyChannel {
Bit32u div;
Bit32u freq_add;
Bit32u freq_pos;
};
struct TandyBlock {
TandyChannel chan[3];
Bit32s volume[4];
Bit8u reg;
};
static TandyBlock tandy;
#define REG_CHAN0DIV 0 /* 0 0 0 */
#define REG_CHAN0ATT 1 /* 0 0 1 */
#define REG_CHAN1DIV 2 /* 0 1 0 */
#define REG_CHAN1ATT 3 /* 0 1 1 */
#define REG_CHAN2DIV 4 /* 1 0 0 */
#define REG_CHAN2ATT 5 /* 1 0 1 */
#define REG_NOISEATT 7 /* 1 1 1 */
//TODO a db volume table :)
static Bit32s vol_table[16];
static void write_pc0(Bit32u port,Bit8u val) {
/* Test for a command byte */
if (val & 0x80) {
tandy.reg=(val>>4) & 7;
switch (tandy.reg) {
case REG_CHAN0DIV:
case REG_CHAN1DIV:
case REG_CHAN2DIV:
tandy.chan[tandy.reg>>1].div=val&15;
break;
case REG_CHAN0ATT:
case REG_CHAN1ATT:
case REG_CHAN2ATT:
case REG_NOISEATT:
tandy.volume[tandy.reg>>1]=vol_table[val&15];
if (tandy.volume[0] || tandy.volume[1] || tandy.volume[2] || tandy.volume[3]) {
MIXER_Enable(tandy_chan,true);
} else {
MIXER_Enable(tandy_chan,false);
}
break;
default:
// LOG_WARN("TANDY:Illegal register %d selected",tandy.reg);
break;
}
} else {
/* Dual byte command */
switch (tandy.reg) {
#define MAKE_ADD(DIV)(Bit32u)((2 << BIT_SHIFT)/((float)TANDY_RATE/((float)TANDY_DIV/(float)DIV)));
case REG_CHAN0DIV:
case REG_CHAN1DIV:
case REG_CHAN2DIV:
tandy.chan[tandy.reg>>1].div|=(val & 63)<<4;
tandy.chan[tandy.reg>>1].freq_add=MAKE_ADD(tandy.chan[tandy.reg>>1].div);
// tandy.chan[tandy.reg>>1].freq_pos=0;
break;
default:
LOG_WARN("TANDY:Illegal dual byte reg %d",tandy.reg);
};
}
}
static void TANDYSOUND_CallBack(Bit8u * stream,Bit32u len) {
for (Bit32u i=0;i<len;i++) {
Bit32s sample=0;
/* Generate the sound from the 3 channels */
for (Bit32u c=0;c<3;c++) {
if (tandy.volume[c]) {
if (tandy.chan[c].freq_pos<(1 << BIT_SHIFT)) sample+=tandy.volume[c];
else sample-=tandy.volume[c];
tandy.chan[c].freq_pos+=tandy.chan[c].freq_add;
if (tandy.chan[c].freq_pos>=(2 << BIT_SHIFT)) tandy.chan[c].freq_pos-=(2 << BIT_SHIFT);
}
}
/* Generate the noise channel */
if (sample>MAX_AUDIO) *(Bit16s *)stream=MAX_AUDIO;
else if (sample<MIN_AUDIO) *(Bit16s *)stream=MIN_AUDIO;
else *(Bit16s *)stream=(Bit16s)sample;
stream+=2;
}
};
void TANDY_Init(void) {
IO_RegisterWriteHandler(0xc0,write_pc0,"Tandy Sound");
tandy_chan=MIXER_AddChannel(&TANDYSOUND_CallBack,TANDY_RATE,"TANDY");
MIXER_Enable(tandy_chan,false);
MIXER_SetMode(tandy_chan,MIXER_16MONO);
/* Calculate the volume table */
float out=TANDY_VOLUME;
for (Bit32u i=0;i<15;i++) {
vol_table[i]=(Bit32s)out;
out /= (float)1.258925412; /* = 10 ^ (2/20) = 2dB */
}
vol_table[15]=0;
/* Setup a byte for tandy detection */
real_writeb(0xffff,0xe,0xfd);
}

300
src/hardware/timer.cpp Normal file
View file

@ -0,0 +1,300 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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 <list>
#include "dosbox.h"
#include "inout.h"
#include "pic.h"
#include "bios.h"
#include "mem.h"
#include "dosbox.h"
#include "mixer.h"
#include "timer.h"
struct PIT_Block {
Bit8u mode; /* Current Counter Mode */
Bit32s cntr;
Bit8u latch_mode;
Bit8u read_state;
Bit16s read_latch;
Bit8u write_state;
Bit16u write_latch;
Bit32u last_ticks;
};
static PIT_Block pit[3];
static Bit32u pit_ticks; /* The amount of pit ticks one host tick is bit shifted */
static Bit32u timer_ticks; /* The amount of pit ticks bitshifted one timer cycle needs */
static Bit32u timer_buildup; /* The amount of pit ticks waiting */
#define PIT_TICK_RATE 1193182
#define PIT_SHIFT 9
#define MAX_PASSED ((PIT_TICK_RATE/4) << PIT_SHIFT) /* Alow 1/4 second of timer build up */
static void counter_latch(Bitu counter) {
/* Fill the read_latch of the selected counter with current count */
PIT_Block * p=&pit[counter];
//TODO Perhaps make it a bit64u for accuracy :)
Bit32u ticks=(((LastTicks - p->last_ticks) * pit_ticks) >> PIT_SHIFT) % p->cntr ;
switch (p->mode) {
case 2:
case 3:
p->read_latch=(Bit16u)ticks;
break;
default:
LOG_ERROR("PIT:Illegal Mode %d for reading counter %d",p->mode,counter);
p->read_latch=(Bit16u)ticks;
break;
}
}
static void write_latch(Bit32u port,Bit8u val) {
Bit32u counter=port-0x40;
PIT_Block * p=&pit[counter];
switch (p->write_state) {
case 0:
p->write_latch = p->write_latch | ((val & 0xff) << 8);
p->write_state = 3;
break;
case 3:
p->write_latch = val & 0xff;
p->write_state = 0;
break;
case 1:
p->write_latch = val & 0xff;
break;
case 2:
p->write_latch = (val & 0xff) << 8;
break;
}
if (p->write_state != 0) {
if (p->write_latch == 0) p->cntr = 0x10000;
else p->cntr = p->write_latch;
p->last_ticks=LastTicks;
switch (counter) {
case 0x00: /* Timer hooked to IRQ 0 */
PIC_DeActivateIRQ(0);
timer_ticks=p->cntr << PIT_SHIFT;
timer_buildup=0;
LOG_DEBUG("PIT 0 Timer at %.3g Hz mode %d",PIT_TICK_RATE/(double)p->cntr,p->mode);
break;
case 0x02: /* Timer hooked to PC-Speaker */
PCSPEAKER_SetFreq(PIT_TICK_RATE/p->cntr);
break;
default:
LOG_ERROR("PIT:Illegal timer selected for writing");
}
}
}
static Bit8u read_latch(Bit32u port) {
Bit32u counter=port-0x40;
if (pit[counter].read_latch == -1)
counter_latch(counter);
Bit8u ret;
switch (pit[counter].read_state) {
case 0: /* read MSB & return to state 3 */
ret=(pit[counter].read_latch >> 8) & 0xff;
pit[counter].read_state = 3;
pit[counter].read_latch = -1;
break;
case 3: /* read LSB followed by MSB */
ret = (pit[counter].read_latch & 0xff);
if (pit[counter].mode & 0x80) pit[counter].mode &= 7; /* moved here */
else
pit[counter].read_state = 0;
break;
case 1: /* read MSB */
ret = (pit[counter].read_latch >> 8) & 0xff;
pit[counter].read_latch = -1;
break;
case 2: /* read LSB */
ret = (pit[counter].read_latch & 0xff);
pit[counter].read_latch = -1;
break;
default:
ret=0;
E_Exit("Timer.cpp: error in readlatch");
break;
}
return ret;
}
static void write_p43(Bit32u port,Bit8u val) {
if (val & 1) {
E_Exit("PIT:BCD Counter not supported");
}
Bitu latch=(val >> 6) & 0x03;
switch (latch) {
case 0:
case 1:
case 2:
if ((val & 0x30) == 0) {
/* Counter latch command */
counter_latch(latch);
} else {
pit[latch].read_state = (val >> 4) & 0x03;
pit[latch].write_state = (val >> 4) & 0x03;
pit[latch].mode = (val >> 1) & 0x07;
}
break;
case 3:
E_Exit("Special PIT Latch Read out thing");
}
}
/* The TIMER Part */
enum { T_TICK,T_MICRO,T_DELAY};
struct Timer {
Bitu type;
union {
struct {
TIMER_TickHandler handler;
} tick;
struct{
Bitu count;
Bitu total;
TIMER_MicroHandler handler;
} micro;
struct {
Bitu end;
TIMER_DelayHandler handler;
} delay;
};
};
static Timer * first_timer=0;
static std::list<Timer *> Timers;
TIMER_Block * TIMER_RegisterTickHandler(TIMER_TickHandler handler) {
Timer * new_timer=new(Timer);
new_timer->type=T_TICK;
new_timer->tick.handler=handler;
Timers.push_front(new_timer);
return (TIMER_Block *)new_timer;
}
TIMER_Block * TIMER_RegisterMicroHandler(TIMER_MicroHandler handler,Bitu micro) {
Timer * new_timer=new(Timer);
new_timer->type=T_MICRO;
new_timer->micro.handler=handler;
new_timer->micro.total=micro;
new_timer->micro.count=0;
Timers.push_front(new_timer);
return (TIMER_Block *)new_timer;
}
TIMER_Block * TIMER_RegisterDelayHandler(TIMER_DelayHandler handler,Bitu delay) {
//Todo maybe check for a too long delay
Timer * new_timer=new(Timer);
new_timer->type=T_DELAY;
new_timer->delay.handler=handler;
new_timer->delay.end=LastTicks+delay;
Timers.push_front(new_timer);
return (TIMER_Block *)new_timer;
}
void TIMER_SetNewMicro(TIMER_Block * block,Bitu micro) {
Timer * timer=(Timer *)block;
if (timer->type!=T_MICRO) E_Exit("TIMER:Illegal handler type");
timer->micro.count=0;
timer->micro.total=micro;
}
void TIMER_AddTicks(Bit32u ticks) {
/* This will run through registered handlers and handle the PIT ticks */
timer_buildup+=ticks*pit_ticks;
if (timer_buildup>MAX_PASSED) timer_buildup=MAX_PASSED;
Bitu add_micro=ticks*1000;
std::list<Timer *>::iterator i;
for(i=Timers.begin(); i != Timers.end(); ++i) {
Timer * timer=(*i);
switch (timer->type) {
case T_TICK:
timer->tick.handler(ticks);
break;
case T_MICRO:
timer->micro.count+=add_micro;
if (timer->micro.count>=timer->micro.total) {
timer->micro.count-=timer->micro.total;
timer->micro.handler();
}
break;
case T_DELAY:
/* Also unregister the timer handler from the list */
if (LastTicks>timer->delay.end) {
std::list<Timer *>::iterator remove;
timer->delay.handler();
remove=i++;
Timers.erase(remove);
}
break;
default:
E_Exit("TIMER:Illegal handler type");
};
};
}
void TIMER_CheckPIT(void) {
if (timer_buildup>timer_ticks) {
timer_buildup-=timer_ticks;
PIC_ActivateIRQ(0);
return;
}
}
void TIMER_Init(void) {
IO_RegisterWriteHandler(0x40,write_latch,"PIT Timer 0");
IO_RegisterWriteHandler(0x42,write_latch,"PIT Timer 2");
IO_RegisterWriteHandler(0x43,write_p43,"PIT Mode Control");
IO_RegisterReadHandler(0x40,read_latch,"PIT Timer 0");
// IO_RegisterReadHandler(0x41,read_p41,"PIT Timer 1");
IO_RegisterReadHandler(0x42,read_latch,"PIT Timer 2");
/* Setup Timer 0 */
pit[0].cntr=0x10000;
pit[0].write_state = 3;
pit[0].read_state = 3;
pit[0].read_latch=-1;
pit[0].write_latch=0;
pit[0].mode=3;
timer_ticks=pit[0].cntr << PIT_SHIFT;
timer_buildup=0;
// first_timer=0;
pit_ticks=(PIT_TICK_RATE << PIT_SHIFT)/1000;
PIC_RegisterIRQ(0,&TIMER_CheckPIT,"PIT 0 Timer");
}

165
src/hardware/vga.cpp Normal file
View file

@ -0,0 +1,165 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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 <stdlib.h>
#include <string.h>
#include "dosbox.h"
#include "video.h"
#include "pic.h"
#include "render.h"
#include "timer.h"
#include "vga.h"
VGA_Type vga;
Bit32u CGAWriteTable[256];
Bit32u ExpandTable[256];
Bit32u FillTable[16]={
0x00000000,0x000000ff,0x0000ff00,0x0000ffff,
0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff,
0xff000000,0xff0000ff,0xff00ff00,0xff00ffff,
0xffff0000,0xffff00ff,0xffffff00,0xffffffff
};
static PageEntry VGA_PageEntry;
void VGA_Render_GFX_4(Bit8u * * data);
void VGA_Render_GFX_16(Bit8u * * data);
void VGA_Render_GFX_256C(Bit8u * * data);
void VGA_Render_GFX_256U(Bit8u * * data);
void VGA_Render_TEXT_16(Bit8u * * data);
void VGA_FindSettings(void) {
/* Sets up the correct memory handler from the vga.mode setting */
MEMORY_ResetHandler(0xA0000/4096,128*1024/4096);
VGA_PageEntry.type=MEMORY_HANDLER;
/* Detect the kind of video mode this is */
if (vga.config.gfxmode) {
if (vga.config.vga_enabled) {
if (vga.config.chained) {
/* 256 color chained vga */
vga.mode=GFX_256C;
//Doesn't need a memory handler
} else {
/* 256 color unchained vga */
vga.mode=GFX_256U;
VGA_PageEntry.base=0xA0000;
VGA_PageEntry.handler.read=VGA_NormalReadHandler;
VGA_PageEntry.handler.write=VGA_GFX_256U_WriteHandler;
MEMORY_SetupHandler(0xA0000/4096,16,&VGA_PageEntry);
}
} else if (vga.config.cga_enabled) {
/* 4 color cga */
//TODO Detect hercules modes, probably set them up in bios too
vga.mode=GFX_4;
// VGA_PageEntry.base=0xB8000;
// VGA_PageEntry.handler.read=VGA_GFX_4_ReadHandler;
// VGA_PageEntry.handler.write=VGA_GFX_4_WriteHandler;
// MEMORY_SetupHandler(0xB8000/4096,8,&VGA_PageEntry);
} else {
/* 16 color ega */
vga.mode=GFX_16;
VGA_PageEntry.base=0xA0000;
VGA_PageEntry.handler.read=VGA_NormalReadHandler;
VGA_PageEntry.handler.write=VGA_GFX_16_WriteHandler;
MEMORY_SetupHandler(0xA0000/4096,16,&VGA_PageEntry);
}
} else {
vga.mode=TEXT_16;
}
VGA_StartResize();
}
static void VGA_DoResize(void) {
vga.draw.resizing=false;
Bitu width,height,pitch;
RENDER_Handler * renderer;
height=vga.config.vdisplayend+1;
if (vga.config.vline_height>0) {
height/=(vga.config.vline_height+1);
}
if (vga.config.vline_double) height>>=1;
width=vga.config.hdisplayend;
switch (vga.mode) {
case GFX_256C:
renderer=&VGA_Render_GFX_256C;
width<<=2;
pitch=vga.config.scan_len*8;
break;
case GFX_256U:
width<<=2;
pitch=vga.config.scan_len*8;
renderer=&VGA_Render_GFX_256U;
break;
case GFX_16:
width<<=3;
pitch=vga.config.scan_len*16;
renderer=&VGA_Render_GFX_16;
break;
case GFX_4:
width<<=3;
height<<=1;
pitch=width;
renderer=&VGA_Render_GFX_4;
break;
case TEXT_16:
/* probably a 16-color text mode, got to detect mono mode somehow */
width<<=3; /* 8 bit wide text font */
height<<=4; /* 16 bit font height */
if (width>640) width=640;
if (height>480) height=480;
pitch=width;
renderer=&VGA_Render_TEXT_16;
};
vga.draw.width=width;
vga.draw.height=height;
RENDER_SetSize(width,height,8,pitch,((float)width/(float)height),0,renderer);
};
void VGA_StartResize(void) {
if (!vga.draw.resizing) {
vga.draw.resizing=true;
/* Start a resize after 50 ms */
TIMER_RegisterDelayHandler(VGA_DoResize,50);
}
}
void VGA_Init() {
vga.draw.resizing=false;
VGA_SetupMemory();
VGA_SetupMisc();
VGA_SetupDAC();
VGA_SetupCRTC();
VGA_SetupGFX();
VGA_SetupSEQ();
VGA_SetupAttr();
/* Generate tables */
Bit32u i;
for (i=0;i<256;i++) {
ExpandTable[i]=i | (i << 8)| (i <<16) | (i << 24);
CGAWriteTable[i]=((i>>6)&3) | (((i>>4)&3) << 8)| (((i>>2)&3) <<16) | (((i>>0)&3) << 24);
}
}

255
src/hardware/vga.h Normal file
View file

@ -0,0 +1,255 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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.
*/
#ifndef VGA_H_
#define VGA_H_
#include <mem.h>
enum { TEXT, GRAPH };
enum { GFX_256C,GFX_256U,GFX_16,GFX_4,GFX_2, TEXT_16 };
typedef struct {
bool attrindex;
} VGA_Internal;
typedef struct {
/* Video drawing */
Bit16u display_start;
Bit16u real_start;
bool retrace; /* A retrace has started */
Bitu scan_len;
/* Screen resolution and memory mode */
Bitu vdisplayend;
Bitu hdisplayend;
bool chained; /* Enable or Disabled Chain 4 Mode */
bool gfxmode; /* Yes or No Easy no */
bool blinking; /* Attribute bit 7 is blinking */
bool vga_enabled;
bool cga_enabled;
bool vline_double;
Bit8u vline_height;
/* Pixel Scrolling */
Bit8u pel_panning; /* Amount of pixels to skip when starting horizontal line */
Bit8u hlines_skip;
Bit8u bytes_skip;
/* Specific stuff memory write/read handling */
Bit8u read_mode;
Bit8u write_mode;
Bit8u read_map_select;
Bit8u color_dont_care;
Bit8u color_compare;
Bit8u data_rotate;
Bit8u raster_op;
Bit8u enable_set_reset;
Bit8u set_reset;
Bit32u full_bit_mask;
Bit32u full_map_mask;
} VGA_Config;
typedef struct {
bool resizing;
Bitu width;
Bitu height;
Bit8u font_height;
Bit8u cursor_enable;
Bit8u cursor_row;
Bit8u cursor_col;
Bit8u cursor_count;
} VGA_Draw;
typedef struct {
Bit8u index;
Bit8u reset;
Bit8u clocking_mode;
Bit8u map_mask;
Bit8u character_map_select;
Bit8u memory_mode;
} VGA_Seq;
typedef struct {
Bit8u palette[16];
Bit8u mode_control;
Bit8u horizontal_pel_panning;
Bit8u overscan_color;
Bit8u color_plane_enable;
Bit8u color_select;
Bit8u index;
} VGA_Attr;
typedef struct {
Bit8u horizontal_total;
Bit8u horizontal_display_end;
Bit8u start_horizontal_blanking;
Bit8u end_horizontal_blanking;
Bit8u start_horizontal_retrace;
Bit8u end_horizontal_retrace;
Bit8u vertical_total;
Bit8u overflow;
Bit8u preset_row_scan;
Bit8u maximum_scan_line;
Bit8u cursor_start;
Bit8u cursor_end;
Bit8u start_address_high;
Bit8u start_address_low;
Bit8u cursor_location_high;
Bit8u cursor_location_low;
Bit8u vertical_retrace_start;
Bit8u vertical_retrace_end;
Bit8u vertical_display_end;
Bit8u offset;
Bit8u underline_location;
Bit8u start_vertical_blank;
Bit8u end_vertical_blank;
Bit8u mode_control;
Bit8u line_compare;
Bit8u index;
} VGA_Crtc;
typedef struct {
Bit8u index;
Bit8u set_reset;
Bit8u enable_set_reset;
Bit8u color_compare;
Bit8u data_rotate;
Bit8u read_map_select;
Bit8u mode;
Bit8u miscellaneous;
Bit8u color_dont_care;
Bit8u bit_mask;
} VGA_Gfx;
struct RGBEntry {
Bit8u red;
Bit8u green;
Bit8u blue;
Bit8u attr_entry;
};
typedef struct {
Bit8u bits; /* DAC bits, usually 6 or 8 */
Bit8u pel_mask;
Bit8u pel_index;
Bit8u state;
Bit8u index;
Bitu first_changed;
RGBEntry rgb[0x100];
} VGA_Dac;
union VGA_Latch {
Bit32u d;
Bit8u b[4];
};
union VGA_Memory {
Bit8u linear[64*1024*4];
Bit8u paged[64*1024][4];
VGA_Latch latched[64*1024];
};
typedef struct {
Bitu mode; /* The mode the vga system is in */
VGA_Draw draw;
VGA_Config config;
VGA_Internal internal;
/* Internal module groups */
VGA_Seq seq;
VGA_Attr attr;
VGA_Crtc crtc;
VGA_Gfx gfx;
VGA_Dac dac;
VGA_Latch latch;
VGA_Memory mem;
//Special little hack to let the memory run over into the buffer
Bit8u buffer[1024*1024];
} VGA_Type;
/* Functions for different resolutions */
//void VGA_FindSize(void);
void VGA_FindSettings(void);
void VGA_StartResize(void);
/* The Different Drawing functions */
void VGA_DrawTEXT(Bit8u * bitdata,Bitu next_line);
void VGA_DrawGFX256_Fast(Bit8u * bitdata,Bitu next_line);
void VGA_DrawGFX256_Full(Bit8u * bitdata,Bitu next_line);
void VGA_DrawGFX16_Full(Bit8u * bitdata,Bitu next_line);
void VGA_DrawGFX4_Full(Bit8u * bitdata,Bitu next_line);
/* The Different Memory Read/Write Handlers */
Bit8u VGA_NormalReadHandler(Bit32u start);
void VGA_NormalWriteHandler(Bit32u start,Bit8u val);
void VGA_GFX_256U_WriteHandler(Bit32u start,Bit8u val);
void VGA_GFX_16_WriteHandler(Bit32u start,Bit8u val);
void VGA_GFX_4_WriteHandler(Bit32u start,Bit8u val);
Bit8u VGA_ChainedReadHandler(Bit32u start);
void VGA_ChainedWriteHandler(Bit32u start,Bit8u val);
Bit8u VGA_GFX_4_ReadHandler(Bit32u start);
/* Some support functions */
void VGA_DAC_CombineColor(Bit8u attr,Bit8u pal);
/* The VGA Subfunction startups */
void VGA_SetupAttr(void);
void VGA_SetupMemory(void);
void VGA_SetupDAC(void);
void VGA_SetupCRTC(void);
void VGA_SetupMisc(void);
void VGA_SetupGFX(void);
void VGA_SetupSEQ(void);
/* Some Support Functions */
void VGA_DACSetEntirePalette(void);
extern VGA_Type vga;
extern Bit8u vga_rom_8[256 * 8];
extern Bit8u vga_rom_14[256 * 14];
extern Bit8u vga_rom_16[256 * 16];
//extern Bit8u vga_buffer[1024*1024];
extern Bit32u ExpandTable[256];
extern Bit32u FillTable[16];
extern Bit32u CGAWriteTable[256];
#endif

165
src/hardware/vga_attr.cpp Normal file
View file

@ -0,0 +1,165 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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 "dosbox.h"
#include "inout.h"
#include "vga.h"
#define attr(blah) vga.attr.blah
void write_p3c0(Bit32u port,Bit8u val) {
if (!vga.internal.attrindex) {
attr(index)=val & 0x1F;
vga.internal.attrindex=true;
/*
0-4 Address of data register to write to port 3C0h or read from port 3C1h
If set screen output is enabled and the palette can not be modified,
if clear screen output is disabled and the palette can be modified.
*/
return;
} else {
vga.internal.attrindex=false;
switch (attr(index)) {
/* Palette */
case 0x00: case 0x01: case 0x02: case 0x03:
case 0x04: case 0x05: case 0x06: case 0x07:
case 0x08: case 0x09: case 0x0a: case 0x0b:
case 0x0c: case 0x0d: case 0x0e: case 0x0f:
attr(palette[attr(index)])=val;
VGA_DAC_CombineColor(attr(index),val);
/*
0-5 Index into the 256 color DAC table. May be modified by 3C0h index
10h and 14h.
*/
break;
case 0x10: /* Mode Control Register */
attr(mode_control)=val;
vga.config.gfxmode=val&1;
vga.config.vga_enabled=(val & 64)>0;
VGA_FindSettings();
//TODO Monochrome mode
//TODO 9 bit characters
//TODO line wrapping split screen shit see bit 5
//TODO index 14h weirdo dac switch bits
/*
0 Graphics mode if set, Alphanumeric mode else.
1 Monochrome mode if set, color mode else.
2 9-bit wide characters if set.
The 9th bit of characters C0h-DFh will be the same as
the 8th bit. Otherwise it will be the background color.
3 If set Attribute bit 7 is blinking, else high intensity.
5 If set the PEL panning register (3C0h index 13h) is temporarily set
to 0 from when the line compare causes a wrap around until the next
vertical retrace when the register is automatically reloaded with
the old value, else the PEL panning register ignores line compares.
6 If set pixels are 8 bits wide. Used in 256 color modes.
7 If set bit 4-5 of the index into the DAC table are taken from port
3C0h index 14h bit 0-1, else the bits in the palette register are
used.
*/
if (val&128) {
E_Exit("VGA:Special 16 colour DAC Shift");
}
break;
case 0x11: /* Overscan Color Register */
attr(overscan_color)=val;
/* 0-5 Color of screen border. Color is defined as in the palette registers. */
break;
case 0x12: /* Color Plane Enable Register */
/* Why disable colour planes? */
attr(color_plane_enable)=val;
/*
0 Bit plane 0 is enabled if set.
1 Bit plane 1 is enabled if set.
2 Bit plane 2 is enabled if set.
3 Bit plane 3 is enabled if set.
4-5 Video Status MUX. Diagnostics use only.
Two attribute bits appear on bits 4 and 5 of the Input Status
Register 1 (3dAh). 0: Bit 2/0, 1: Bit 5/4, 2: bit 3/1, 3: bit 7/6
*/
break;
case 0x13: /* Horizontal PEL Panning Register */
attr(horizontal_pel_panning)=val & 0xF;
vga.config.pel_panning=val & 0xF;
/*
0-3 Indicates number of pixels to shift the display left
Value 9bit textmode 256color mode Other modes
0 1 0 0
1 2 n/a 1
2 3 1 2
3 4 n/a 3
4 5 2 4
5 6 n/a 5
6 7 3 6
7 8 n/a 7
8 0 n/a n/a
*/
break;
case 0x14: /* Color Select Register */
attr(color_select)=val;
/*
0-1 If 3C0h index 10h bit 7 is set these 2 bits are used as bits 4-5 of
the index into the DAC table.
2-3 These 2 bits are used as bit 6-7 of the index into the DAC table
except in 256 color mode.
Note: this register does not affect 256 color modes.
*/
if (val) LOG_DEBUG("VGA:ATTR:DAC index set to %d",val);
break;
default:
LOG_ERROR("VGA:ATTR:Write to unkown Index %2X",attr(index));
break;
}
}
}
Bit8u read_p3c1(Bit32u port) {
switch (attr(index)) {
/* Palette */
case 0x00: case 0x01: case 0x02: case 0x03:
case 0x04: case 0x05: case 0x06: case 0x07:
case 0x08: case 0x09: case 0x0a: case 0x0b:
case 0x0c: case 0x0d: case 0x0e: case 0x0f:
return attr(palette[attr(index)]);
case 0x10: /* Mode Control Register */
return attr(mode_control);
case 0x11: /* Overscan Color Register */
return attr(overscan_color);
case 0x12: /* Color Plane Enable Register */
return attr(color_plane_enable);
case 0x13: /* Horizontal PEL Panning Register */
return attr(horizontal_pel_panning);
case 0x14: /* Color Select Register */
return attr(color_select);
default:
LOG_ERROR("VGA:ATTR:Read from unkown Index %2X",attr(index));
}
return 0;
};
void VGA_SetupAttr(void) {
IO_RegisterWriteHandler(0x3c0,write_p3c0,"VGA Attribute controller");
IO_RegisterReadHandler(0x3c1,read_p3c1,"VGA Attribute Read");
}

332
src/hardware/vga_crtc.cpp Normal file
View file

@ -0,0 +1,332 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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 "dosbox.h"
#include "inout.h"
#include "vga.h"
#define crtc(blah) vga.crtc.blah
void write_p3d4(Bit32u port,Bit8u val) {
crtc(index)=val;
}
void write_p3d5(Bit32u port,Bit8u val) {
switch(crtc(index)) {
case 0x00: /* Horizontal Total Register */
crtc(horizontal_total)=val;
/* 0-7 Horizontal Total Character Clocks-5 */
break;
case 0x01: /* Horizontal Display End Register */
crtc(horizontal_display_end)=val;
vga.config.hdisplayend=val+1;
VGA_FindSettings();
/* 0-7 Number of Character Clocks Displayed -1 */
break;
case 0x02: /* Start Horizontal Blanking Register */
crtc(start_horizontal_blanking)=val;
/* 0-7 The count at which Horizontal Blanking starts */
break;
case 0x03: /* End Horizontal Blanking Register */
crtc(end_horizontal_blanking)=val;
/*
0-4 Horizontal Blanking ends when the last 6 bits of the character
counter equals this field. Bit 5 is at 3d4h index 5 bit 7.
5-6 Number of character clocks to delay start of display after Horizontal
Total has been reached.
7 Access to Vertical Retrace registers if set. If clear reads to 3d4h
index 10h and 11h access the Lightpen read back registers ??
*/
break;
case 0x04: /* Start Horizontal Retrace Register */
crtc(start_horizontal_retrace)=val;
/* 0-7 Horizontal Retrace starts when the Character Counter reaches this value. */
break;
case 0x05: /* End Horizontal Retrace Register */
crtc(end_horizontal_retrace)=val;
/*
0-4 Horizontal Retrace ends when the last 5 bits of the character counter
equals this value.
5-6 Number of character clocks to delay start of display after Horizontal
Retrace.
7 bit 5 of the End Horizontal Blanking count (See 3d4h index 3 bit 0-4)
*/
break;
case 0x06: /* Vertical Total Register */
crtc(vertical_total)=val;
/* 0-7 Lower 8 bits of the Vertical Total. Bit 8 is found in 3d4h index 7
bit 0. Bit 9 is found in 3d4h index 7 bit 5.
Note: For the VGA this value is the number of scan lines in the display -2.
*/
break;
case 0x07: /* Overflow Register */
crtc(overflow)=val;
vga.config.vdisplayend=(vga.config.vdisplayend&0xFF)|((val&2)<<7)|((val&64)<<2);
VGA_FindSettings();
/*
0 Bit 8 of Vertical Total (3d4h index 6)
1 Bit 8 of Vertical Display End (3d4h index 12h)
2 Bit 8 of Vertical Retrace Start (3d4h index 10h)
3 Bit 8 of Start Vertical Blanking (3d4h index 15h)
4 Bit 8 of Line Compare Register (3d4h index 18h)
5 Bit 9 of Vertical Total (3d4h index 6)
6 Bit 9 of Vertical Display End (3d4h index 12h)
7 Bit 9 of Vertical Retrace Start (3d4h index 10h)
*/
break;
case 0x08: /* Preset Row Scan Register */
crtc(preset_row_scan)=val;
vga.config.hlines_skip=val&31;
vga.config.bytes_skip=(val>>5)&3;
// LOG_DEBUG("Skip lines %d bytes %d",vga.config.hlines_skip,vga.config.bytes_skip);
/*
0-4 Number of lines we have scrolled down in the first character row.
Provides Smooth Vertical Scrolling.b
5-6 Number of bytes to skip at the start of scanline. Provides Smooth
Horizontal Scrolling together with the Horizontal Panning Register
(3C0h index 13h).
*/
break;
case 0x09: /* Maximum Scan Line Register */
crtc(maximum_scan_line)=val;
vga.config.vline_double=(val & 128)>1;
vga.config.vline_height=(val & 0xf);
VGA_FindSettings();
/*
0-4 Number of scan lines in a character row -1. In graphics modes this is
the number of times (-1) the line is displayed before passing on to
the next line (0: normal, 1: double, 2: triple...).
This is independent of bit 7, except in CGA modes which seems to
require this field to be 1 and bit 7 to be set to work.
5 Bit 9 of Start Vertical Blanking
6 Bit 9 of Line Compare Register
7 Doubles each scan line if set. I.e. displays 200 lines on a 400 display.
*/
break;
case 0x0A: /* Cursor Start Register */
crtc(cursor_start)=val;
/*
0-4 First scanline of cursor within character.
5 Turns Cursor off if set
*/
break;
case 0x0B: /* Cursor End Register */
crtc(cursor_end)=val;
/*
0-4 Last scanline of cursor within character
5-6 Delay of cursor data in character clocks.
*/
break;
case 0x0C: /* Start Address High Register */
crtc(start_address_high)=val;
vga.config.display_start=(vga.config.display_start & 0x00FF)| (val << 8);
/* 0-7 Upper 8 bits of the start address of the display buffer */
break;
case 0x0D: /* Start Address Low Register */
crtc(start_address_low)=val;
vga.config.display_start=(vga.config.display_start & 0xFF00)| val;
/* 0-7 Lower 8 bits of the start address of the display buffer */
break;
case 0x0E: /*Cursor Location High Register */
crtc(cursor_location_high)=val;
if (vga.config.scan_len<2) break;
vga.draw.cursor_row=(crtc(cursor_location_high)<<8|crtc(cursor_location_low))/(vga.config.scan_len*2);
vga.draw.cursor_col=(crtc(cursor_location_high)<<8|crtc(cursor_location_low))%(vga.config.scan_len*2);
/* 0-7 Upper 8 bits of the address of the cursor */
break;
case 0x0F: /* Cursor Location Low Register */
//TODO update cursor on screen
crtc(cursor_location_low)=val;
if (vga.config.scan_len<2) break;
vga.draw.cursor_row=(crtc(cursor_location_high)<<8|crtc(cursor_location_low))/(vga.config.scan_len*2);
vga.draw.cursor_col=(crtc(cursor_location_high)<<8|crtc(cursor_location_low))%(vga.config.scan_len*2);
/* 0-7 Lower 8 bits of the address of the cursor */
break;
case 0x10: /* Vertical Retrace Start Register */
crtc(vertical_retrace_start)=val;
/*
0-7 Lower 8 bits of Vertical Retrace Start. Vertical Retrace starts when
the line counter reaches this value. Bit 8 is found in 3d4h index 7
bit 2. Bit 9 is found in 3d4h index 7 bit 7.
*/
break;
case 0x11: /* Vertical Retrace End Register */
crtc(vertical_retrace_end)=val;
/*
0-3 Vertical Retrace ends when the last 4 bits of the line counter equals
this value.
4 if clear Clears pending Vertical Interrupts.
5 Vertical Interrupts (IRQ 2) disabled if set. Can usually be left
disabled, but some systems (including PS/2) require it to be enabled.
6 If set selects 5 refresh cycles per scanline rather than 3.
7 Disables writing to registers 0-7 if set 3d4h index 7 bit 4 is not
affected by this bit.
*/
break;
case 0x12: /* Vertical Display End Register */
crtc(vertical_display_end)=val;
vga.config.vdisplayend=(vga.config.vdisplayend & 0x300)|val;
VGA_FindSettings();
/*
0-7 Lower 8 bits of Vertical Display End. The display ends when the line
counter reaches this value. Bit 8 is found in 3d4h index 7 bit 1.
Bit 9 is found in 3d4h index 7 bit 6.
*/
break;
case 0x13: /* Offset register */
crtc(offset)=val;
vga.config.scan_len=val;
VGA_FindSettings();
/*
0-7 Number of bytes in a scanline / K. Where K is 2 for byte mode, 4 for
word mode and 8 for Double Word mode.
*/
break;
case 0x14: /* Underline Location Register */
crtc(underline_location)=val;
/*
0-4 Position of underline within Character cell.
5 If set memory address is only changed every fourth character clock.
6 Double Word mode addressing if set
*/
break;
case 0x15: /* Start Vertical Blank Register */
crtc(start_vertical_blank)=val;
/*
0-7 Lower 8 bits of Vertical Blank Start. Vertical blanking starts when
the line counter reaches this value. Bit 8 is found in 3d4h index 7
bit 3.
*/
break;
case 0x16: /* End Vertical Blank Register */
crtc(end_vertical_blank)=val;
/*
0-6 Vertical blanking stops when the lower 7 bits of the line counter
equals this field. Some SVGA chips uses all 8 bits!
*/
break;
case 0x17: /* Mode Control Register */
crtc(mode_control)=val;
vga.config.cga_enabled=!((val&1)>0);
VGA_FindSettings();
/*
0 If clear use CGA compatible memory addressing system
by substituting character row scan counter bit 0 for address bit 13,
thus creating 2 banks for even and odd scan lines.
1 If clear use Hercules compatible memory addressing system by
substituting character row scan counter bit 1 for address bit 14,
thus creating 4 banks.
2 If set increase scan line counter only every second line.
3 If set increase memory address counter only every other character clock.
5 When in Word Mode bit 15 is rotated to bit 0 if this bit is set else
bit 13 is rotated into bit 0.
6 If clear system is in word mode. Addresses are rotated 1 position up
bringing either bit 13 or 15 into bit 0.
7 Clearing this bit will reset the display system until the bit is set again.
*/
break;
case 0x18: /* Line Compare Register */
crtc(line_compare)=val;
/*
0-7 Lower 8 bits of the Line Compare. When the Line counter reaches this
value, the display address wraps to 0. Provides Split Screen
facilities. Bit 8 is found in 3d4h index 7 bit 4.
Bit 9 is found in 3d4h index 9 bit 6.
*/
break;
default:
LOG_ERROR("VGA:CRTC:Write to unknown index %2X",val,crtc(index));
}
}
Bit8u read_p3d5(Bit32u port) {
switch(crtc(index)) {
case 0x00: /* Horizontal Total Register */
return crtc(horizontal_total);
case 0x01: /* Horizontal Display End Register */
return crtc(horizontal_display_end);
case 0x02: /* Start Horizontal Blanking Register */
return crtc(start_horizontal_blanking);
case 0x03: /* End Horizontal Blanking Register */
return crtc(end_horizontal_blanking);
case 0x04: /* Start Horizontal Retrace Register */
return crtc(start_horizontal_retrace);
case 0x05: /* End Horizontal Retrace Register */
return crtc(end_horizontal_retrace);
case 0x06: /* Vertical Total Register */
return crtc(vertical_total);
case 0x07: /* Overflow Register */
return crtc(overflow);
case 0x08: /* Preset Row Scan Register */
return crtc(preset_row_scan);
case 0x09: /* Maximum Scan Line Register */
return crtc(maximum_scan_line);
case 0x0A: /* Cursor Start Register */
return crtc(cursor_start);
case 0x0B: /* Cursor End Register */
return crtc(cursor_end);
case 0x0C: /* Start Address High Register */
return crtc(start_address_high);
case 0x0D: /* Start Address Low Register */
return crtc(start_address_low);
case 0x0E: /*Cursor Location High Register */
return crtc(cursor_location_high);
case 0x0F: /* Cursor Location Low Register */
return crtc(cursor_location_low);
case 0x10: /* Vertical Retrace Start Register */
return crtc(vertical_retrace_start);
case 0x11: /* Vertical Retrace End Register */
return crtc(vertical_retrace_end);
case 0x12: /* Vertical Display End Register */
return crtc(vertical_display_end);
case 0x13: /* Offset register */
return crtc(offset);
case 0x14: /* Underline Location Register */
return crtc(underline_location);
case 0x15: /* Start Vertical Blank Register */
return crtc(start_vertical_blank);
case 0x16: /* End Vertical Blank Register */
return crtc(end_vertical_blank);
case 0x17: /* Mode Control Register */
return crtc(mode_control);
case 0x18: /* Line Compare Register */
return crtc(line_compare);
default:
LOG_ERROR("VGA:CRTC:Read from unknown index %X",crtc(index));
}
return 0;
}
void VGA_SetupCRTC(void) {
IO_RegisterWriteHandler(0x3d4,write_p3d4,"VGA:CRTC Index Select");
IO_RegisterWriteHandler(0x3d5,write_p3d5,"VGA:CRTC Data Register");
IO_RegisterReadHandler(0x3d5,read_p3d5,"VGA:CRTC Data Register");
// IO_RegisterWriteHandler(0x3b4,write_p3d4,"VGA:CRTC Index Select");
// IO_RegisterWriteHandler(0x3b5,write_p3d5,"VGA:CRTC Data Register");
// IO_RegisterReadHandler(0x3b5,read_p3d5,"VGA:CRTC Data Register");
}

160
src/hardware/vga_dac.cpp Normal file
View file

@ -0,0 +1,160 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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 "dosbox.h"
#include "inout.h"
#include "render.h"
#include "vga.h"
/*
//TODO PEL Mask Maybe
//TODO Find some way to get palette lookups in groups
3C6h (R/W): PEL Mask
bit 0-7 This register is anded with the palette index sent for each dot.
Should be set to FFh.
3C7h (R): DAC State Register
bit 0-1 0 indicates the DAC is in Write Mode and 3 indicates Read mode.
3C7h (W): PEL Address Read Mode
bit 0-7 The PEL data register (0..255) to be read from 3C9h.
Note: After reading the 3 bytes at 3C9h this register will increment,
pointing to the next data register.
3C8h (R/W): PEL Address Write Mode
bit 0-7 The PEL data register (0..255) to be written to 3C9h.
Note: After writing the 3 bytes at 3C9h this register will increment, pointing
to the next data register.
3C9h (R/W): PEL Data Register
bit 0-5 Color value
Note: Each read or write of this register will cycle through first the
registers for Red, Blue and Green, then increment the appropriate
address register, thus the entire palette can be loaded by writing 0 to
the PEL Address Write Mode register 3C8h and then writing all 768 bytes
of the palette to this register.
*/
enum {DAC_READ,DAC_WRITE};
static void write_p3c6(Bit32u port,Bit8u val) {
if (val!=0xff) LOG_ERROR("VGA:Pel Mask not 0xff");
}
static void write_p3c7(Bit32u port,Bit8u val) {
vga.dac.index=val;
vga.dac.pel_index=0;
vga.dac.state=DAC_READ;
}
static void write_p3c8(Bit32u port,Bit8u val) {
vga.dac.index=val;
vga.dac.pel_index=0;
vga.dac.state=DAC_WRITE;
}
static void write_p3c9(Bit32u port,Bit8u val) {
switch (vga.dac.pel_index) {
case 0:
vga.dac.rgb[vga.dac.index].red=val;
vga.dac.pel_index=1;
break;
case 1:
vga.dac.rgb[vga.dac.index].green=val;
vga.dac.pel_index=2;
break;
case 2:
vga.dac.rgb[vga.dac.index].blue=val;
switch (vga.mode) {
case GFX_256C:
case GFX_256U:
RENDER_SetPal(vga.dac.index,
vga.dac.rgb[vga.dac.index].red << 2,
vga.dac.rgb[vga.dac.index].green << 2,
vga.dac.rgb[vga.dac.index].blue << 2
);
break;
default:
/* Check for attributes and DAC entry link */
if (vga.dac.rgb[vga.dac.index].attr_entry>15) return;
if (vga.attr.palette[vga.dac.rgb[vga.dac.index].attr_entry]==vga.dac.index) {
RENDER_SetPal(vga.dac.rgb[vga.dac.index].attr_entry,
vga.dac.rgb[vga.dac.index].red << 2,
vga.dac.rgb[vga.dac.index].green << 2,
vga.dac.rgb[vga.dac.index].blue << 2
);
}
}
vga.dac.index++;
vga.dac.pel_index=0;
break;
default:
LOG_ERROR("VGA:DAC:Illegal Pel Index"); //If this can actually happen that will be the day
};
}
static Bit8u read_p3c9(Bit32u port) {
Bit8u ret;
switch (vga.dac.pel_index) {
case 0:
ret=vga.dac.rgb[vga.dac.index].red;
vga.dac.pel_index=1;
break;
case 1:
ret=vga.dac.rgb[vga.dac.index].green;
vga.dac.pel_index=2;
break;
case 2:
ret=vga.dac.rgb[vga.dac.index].blue;
vga.dac.index++;
vga.dac.pel_index=0;
break;
default:
LOG_ERROR("VGA:DAC:Illegal Pel Index"); //If this can actually happen that will be the day
}
return ret;
}
void VGA_DAC_CombineColor(Bit8u attr,Bit8u pal) {
/* Check if this is a new color */
vga.dac.rgb[pal].attr_entry=attr;
RENDER_SetPal(attr,
vga.dac.rgb[pal].red << 2,
vga.dac.rgb[pal].green << 2,
vga.dac.rgb[pal].blue << 2
);
}
void VGA_SetupDAC(void) {
vga.dac.first_changed=256;
vga.dac.bits=6;
vga.dac.pel_mask=0xff;
vga.dac.pel_index=0;
vga.dac.state=DAC_READ;
/* Setup the DAC IO port Handlers */
IO_RegisterWriteHandler(0x3c6,write_p3c6,"PEL Mask");
IO_RegisterWriteHandler(0x3c7,write_p3c7,"PEL Read Mode");
IO_RegisterWriteHandler(0x3c8,write_p3c8,"PEL Write Mode");
IO_RegisterWriteHandler(0x3c9,write_p3c9,"PEL Data");
IO_RegisterReadHandler(0x3c9,read_p3c9,"PEL Data");
};

165
src/hardware/vga_draw.cpp Normal file
View file

@ -0,0 +1,165 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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"
#include "vga.h"
/* This Should draw a complete 16 colour screen */
void VGA_Render_GFX_4(Bit8u * * data) {
*data=vga.buffer;
VGA_DrawGFX4_Full(vga.buffer,vga.draw.width);
vga.config.retrace=true;
}
void VGA_Render_GFX_16(Bit8u * * data) {
*data=&vga.buffer[vga.config.display_start*8+vga.config.pel_panning];
vga.config.retrace=true;
}
void VGA_Render_GFX_256U(Bit8u * * data) {
*data=&vga.mem.linear[vga.config.display_start*4+vga.config.pel_panning];
vga.config.retrace=true;
}
void VGA_Render_GFX_256C(Bit8u * * data) {
*data=memory+0xa0000;
vga.config.retrace=true;
}
void VGA_Render_TEXT_16(Bit8u * * data) {
*data=vga.buffer;
VGA_DrawTEXT(vga.buffer,vga.draw.width);
vga.config.retrace=true;
}
void VGA_DrawGFX4_Full(Bit8u * bitdata,Bitu next_line) {
//TODO use vga memory handler
Bit8u * reader=real_host(0xB800,0);
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;
for (Bit32u x=0;x<vga.draw.width>>2;x++) {
Bit8u val=*(tempread++);
/*
*(draw+0)=(val>>6)&3;
*(draw+1)=(val>>4)&3;
*(draw+2)=(val>>2)&3;
*(draw+3)=(val)&3;
draw+=4;
*/
*(Bit32u *)draw=CGAWriteTable[val];
draw+=4;
}
//TODO use scanline length and dword mode crap
bitdata+=next_line;
};
vga.config.retrace=true;
}
void VGA_DrawGFX16_Full(Bit8u * bitdata,Bitu next_line) {
Bit8u * reader=&vga.buffer[vga.config.display_start*8+vga.config.pel_panning];
for (Bitu y=0;y<vga.draw.height;y++) {
memcpy((void *)bitdata,(void *)reader,vga.draw.width);
bitdata+=vga.draw.width+next_line;
reader+=vga.config.scan_len*16;
}
vga.config.retrace=true;
};
/* This Should draw a complete 256 colour screen */
void VGA_DrawGFX256_Full(Bit8u * bitdata,Bitu next_line) {
Bit16u yreader=vga.config.display_start*1;
/* Now add pel panning */
for (Bitu y=0;y<vga.draw.height;y++) {
Bit16u xreader=yreader;
for (Bitu x=0;x<vga.draw.width>>2;x++) {
for (Bit32u dx=0;dx<4;dx++) {
(*bitdata++)=vga.mem.paged[xreader][dx];
}
xreader++;
}
//TODO use scanline length and dword mode crap
yreader+=vga.config.scan_len*2;
bitdata+=next_line;
};
vga.config.retrace=true;
};
void VGA_DrawGFX256_Fast(Bit8u * bitdata,Bitu next_line) {
/* For now just copy 64 kb of memory with pitch support */
Bit8u * reader=memory+0xa0000;
for (Bitu y=0;y<vga.draw.height;y++) {
memcpy((void *)bitdata,(void *)reader,vga.draw.width);
bitdata+=vga.draw.width+next_line;
reader+=vga.draw.width;
}
//memcpy((void *)bitdata,(void *)(memory+0xa0000),320*200);
vga.config.retrace=true;
};
void VGA_DrawTEXT(Bit8u * bitdata,Bitu next_line) {
Bit8u * reader=real_off(0xB800,0);
Bit8u * draw_start=bitdata;;
/* Todo Blinking and high intensity colors */
for (Bitu cy=0;cy<(vga.draw.height/16);cy++) {
Bit8u * draw_char=draw_start;
for (Bitu cx=0;cx<(vga.draw.width/8);cx++) {
Bit8u c=*(reader++);
Bit8u * findex=&vga_rom_16[c*16];
Bit8u col=*(reader++);
Bit8u fg=col & 0xF;
Bit8u bg=(col>> 4);
Bit8u * draw=draw_char;
for (Bitu y=0;y<16;y++) {
Bit8u bit_mask=*findex++;
#include "font-switch.h"
draw+=+next_line;
};
draw_char+=8;
};
draw_start+=16*next_line;
};
vga.config.retrace=true;
/* Draw a cursor */
if ((vga.draw.cursor_col*8)>=vga.draw.width) return;
if ((vga.draw.cursor_row*16)>=vga.draw.height) return;
Bit8u * cursor_draw=bitdata+(vga.draw.cursor_row*16+15)*vga.draw.width+vga.draw.cursor_col*8;
if (vga.draw.cursor_count>8) {
for (Bit8u loop=0;loop<8;loop++) *cursor_draw++=15;
}
vga.draw.cursor_count++;
if (vga.draw.cursor_count>16) vga.draw.cursor_count=0;
};

1254
src/hardware/vga_fonts.cpp Normal file

File diff suppressed because it is too large Load diff

221
src/hardware/vga_gfx.cpp Normal file
View file

@ -0,0 +1,221 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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 "dosbox.h"
#include "inout.h"
#include "vga.h"
#define gfx(blah) vga.gfx.blah
static bool index9warned=false;
void write_p3ce(Bit32u port,Bit8u val) {
gfx(index)=val & 0x0f;
}
Bit8u read_p3ce(Bit32u port) {
return gfx(index);
}
void write_p3cf(Bit32u port,Bit8u val) {
switch (gfx(index)) {
case 0: /* Set/Reset Register */
gfx(set_reset)=val & 0x0f;
vga.config.set_reset=val & 0x0f;
/*
0 If in Write Mode 0 and bit 0 of 3CEh index 1 is set a write to
display memory will set all the bits in plane 0 of the byte to this
bit, if the corresponding bit is set in the Map Mask Register (3CEh
index 8).
1 Same for plane 1 and bit 1 of 3CEh index 1.
2 Same for plane 2 and bit 2 of 3CEh index 1.
3 Same for plane 3 and bit 3 of 3CEh index 1.
*/
// LOG_DEBUG("Set Reset = %2X",val);
break;
case 1: /* Enable Set/Reset Register */
gfx(enable_set_reset)=val & 0x0f;
vga.config.enable_set_reset=val & 0x0f;
/*
0 If set enables Set/reset of plane 0 in Write Mode 0.
1 Same for plane 1.
2 Same for plane 2.
3 Same for plane 3.
*/
// LOG_DEBUG("Enable Set Reset = %2X",val);
break;
case 2: /* Color Compare Register */
gfx(color_compare)=val & 0x0f;
/*
0-3 In Read Mode 1 each pixel at the address of the byte read is compared
to this color and the corresponding bit in the output set to 1 if
they match, 0 if not. The Color Don't Care Register (3CEh index 7)
can exclude bitplanes from the comparison.
*/
vga.config.color_compare=val & 0xf;
// LOG_DEBUG("Color Compare = %2X",val);
break;
case 3: /* Data Rotate */
gfx(data_rotate)=val;
vga.config.data_rotate=val & 7;
vga.config.raster_op=(val>>3) & 3;
/*
0-2 Number of positions to rotate data right before it is written to
display memory. Only active in Write Mode 0.
3-4 In Write Mode 2 this field controls the relation between the data
written from the CPU, the data latched from the previous read and the
data written to display memory:
0: CPU Data is written unmodified
1: CPU data is ANDed with the latched data
2: CPU data is ORed with the latch data.
3: CPU data is XORed with the latched data.
*/
// if (vga.config.data_rotate || vga.config.raster_op ) LOG_DEBUG("Data Rotate = %2X Raster op %2X",val & 7,(val>>3) & 3 );
break;
case 4: /* Read Map Select Register */
/* 0-1 number of the plane Read Mode 0 will read from */
gfx(read_map_select)=val & 0x03;
vga.config.read_map_select=val & 0x03;
// LOG_DEBUG("Read Map %2X",val);
break;
case 5: /* Mode Register */ /* Important one very */
gfx(mode)=val;
vga.config.write_mode=val & 3;
vga.config.read_mode=(val >> 3) & 1;
// LOG_DEBUG("Write Mode %d Read Mode %d val %d",vga.config.write_mode,vga.config.read_mode,val);
/*
0-1 Write Mode: Controls how data from the CPU is transformed before
being written to display memory:
0: Mode 0 works as a Read-Modify-Write operation.
First a read access loads the data latches of the VGA with the
value in video memory at the addressed location. Then a write
access will provide the destination address and the CPU data
byte. The data written is modified by the function code in the
Data Rotate register (3CEh index 3) as a function of the CPU
data and the latches, then data is rotated as specified by the
same register.
1: Mode 1 is used for video to video transfers.
A read access will load the data latches with the contents of
the addressed byte of video memory. A write access will write
the contents of the latches to the addressed byte. Thus a single
MOVSB instruction can copy all pixels in the source address byte
to the destination address.
2: Mode 2 writes a color to all pixels in the addressed byte of
video memory. Bit 0 of the CPU data is written to plane 0 et
cetera. Individual bits can be enabled or disabled through the
Bit Mask register (3CEh index 8).
3: Mode 3 can be used to fill an area with a color and pattern. The
CPU data is rotated according to 3CEh index 3 bits 0-2 and anded
with the Bit Mask Register (3CEh index 8). For each bit in the
result the corresponding pixel is set to the color in the
Set/Reset Register (3CEh index 0 bits 0-3) if the bit is set and
to the contents of the processor latch if the bit is clear.
3 Read Mode
0: Data is read from one of 4 bit planes depending on the Read Map
Select Register (3CEh index 4).
1: Data returned is a comparison between the 8 pixels occupying the
read byte and the color in the Color Compare Register (3CEh
index 2). A bit is set if the color of the corresponding pixel
matches the register.
4 Enables Odd/Even mode if set (See 3C4h index 4 bit 2).
5 Enables CGA style 4 color pixels using even/odd bit pairs if set.
6 Enables 256 color mode if set.
*/
break;
case 6: /* Miscellaneous Register */
gfx(miscellaneous)=val;
/*
0 Indicates Graphics Mode if set, Alphanumeric mode else.
1 Enables Odd/Even mode if set.
2-3 Memory Mapping:
0: use A000h-BFFFh
1: use A000h-AFFFh VGA Graphics modes
2: use B000h-B7FFh Monochrome modes
3: use B800h-BFFFh CGA modes
*/
break;
case 7: /* Color Don't Care Register */
gfx(color_dont_care)=val & 0x0f;
/*
0 Ignore bit plane 0 in Read mode 1 if clear.
1 Ignore bit plane 1 in Read mode 1 if clear.
2 Ignore bit plane 2 in Read mode 1 if clear.
3 Ignore bit plane 3 in Read mode 1 if clear.
*/
vga.config.color_dont_care=val & 0xf;
// LOG_DEBUG("Color don't care = %2X",val);
break;
case 8: /* Bit Mask Register */
gfx(bit_mask)=val;
vga.config.full_bit_mask=ExpandTable[val];
// LOG_DEBUG("Bit mask %2X",val);
/*
0-7 Each bit if set enables writing to the corresponding bit of a byte in
display memory.
*/
break;
case 9: /* Unknown */
/* Crystal Dreams seems to like to write tothis register very weird */
if (!index9warned) {
LOG_WARN("VGA:3CF:Write %2X to illegal index 9",val);
index9warned=true;
}
break;
default:
LOG_WARN("VGA:3CF:Write %2X to illegal index %2X",val,gfx(index));
break;
}
}
Bit8u read_p3cf(Bit32u port) {
switch (gfx(index)) {
case 0: /* Set/Reset Register */
return gfx(set_reset);
case 1: /* Enable Set/Reset Register */
return gfx(enable_set_reset);
case 2: /* Color Compare Register */
return gfx(color_compare);
case 3: /* Data Rotate */
return gfx(data_rotate);
case 4: /* Read Map Select Register */
return gfx(read_map_select);
case 5: /* Mode Register */
return gfx(mode);
case 6: /* Miscellaneous Register */
return gfx(miscellaneous);
case 7: /* Color Don't Care Register */
return gfx(color_dont_care);
case 8: /* Bit Mask Register */
return gfx(bit_mask);
default:
LOG_WARN("Reading from illegal index %2X in port %4X",gfx(index),port);
}
return 0; /* Compiler happy */
}
void VGA_SetupGFX(void) {
IO_RegisterWriteHandler(0x3ce,write_p3ce,"VGA Graphics Index");
IO_RegisterWriteHandler(0x3cf,write_p3cf,"VGA Graphics Data");
IO_RegisterReadHandler(0x3ce,read_p3ce,"Vga Graphics Index");
IO_RegisterReadHandler(0x3cf,read_p3cf,"Vga Graphics Data");
}

214
src/hardware/vga_memory.cpp Normal file
View file

@ -0,0 +1,214 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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 <stdlib.h>
#include <string.h>
#include "dosbox.h"
#include "mem.h"
#include "vga.h"
Bit8u VGA_ChainedReadHandler(Bit32u start) {
return vga.mem.linear[start];
}
void VGA_ChainedWriteHandler(Bit32u start,Bit8u val) {
vga.mem.linear[start]=val;
};
Bit8u VGA_NormalReadHandler(Bit32u start) {
vga.latch.d=vga.mem.latched[start].d;
switch (vga.config.read_mode) {
case 0:
return(vga.latch.b[vga.config.read_map_select]);
case 1:
VGA_Latch templatch;
templatch.d=(vga.latch.d & FillTable[vga.config.color_dont_care]) ^ FillTable[vga.config.color_compare & vga.config.color_dont_care];
return ~(templatch.b[0] | templatch.b[1] | templatch.b[2] | templatch.b[3]);
}
return 0;
}
//Nice one from DosEmu
INLINE Bit32u RasterOp(Bit32u input,Bit32u mask) {
switch (vga.config.raster_op) {
case 0x00: /* None */
return (input & mask) | (vga.latch.d & ~mask);
case 0x01: /* AND */
return (input | ~mask) & vga.latch.d;
case 0x02: /* OR */
return (input & mask) | vga.latch.d;
case 0x03: /* XOR */
return (input & mask) ^ vga.latch.d;
};
return 0;
}
Bit8u VGA_GFX_4_ReadHandler(Bit32u start) {
return vga.mem.linear[start];
}
void VGA_GFX_4_WriteHandler(Bit32u start,Bit8u val) {
vga.mem.linear[start]=val;
Bitu line=start / 320;
Bitu x=start % 320;
Bit8u * draw=&vga.buffer[start<<2];
/* TODO Could also use a Bit32u lookup table for this */
*(draw+0)=(val>>6)&3;
*(draw+1)=(val>>4)&3;
*(draw+2)=(val>>2)&3;
*(draw+3)=(val)&3;
}
void VGA_GFX_16_WriteHandler(Bit32u start,Bit8u val) {
VGA_Latch new_latch;
Bitu bit_mask;
switch (vga.config.write_mode) {
case 0x00:
// Write Mode 0: In this mode, the host data is first rotated as per the Rotate Count field, then the Enable Set/Reset mechanism selects data from this or the Set/Reset field. Then the selected Logical Operation is performed on the resulting data and the data in the latch register. Then the Bit Mask field is used to select which bits come from the resulting data and which come from the latch register. Finally, only the bit planes enabled by the Memory Plane Write Enable field are written to memory.
// val=(val >> vga.config.data_rotate) | (val << (8-vga.config.data_rotate));
//Todo could also include the rotation in the table :)
new_latch.d=ExpandTable[val];
{
Bit32u resetmask=FillTable[vga.config.enable_set_reset];
new_latch.d=(new_latch.d & ~resetmask) | ( FillTable[vga.config.set_reset] & resetmask);
};
new_latch.d=RasterOp(new_latch.d,vga.config.full_bit_mask);
bit_mask=vga.gfx.bit_mask;
break;
case 0x01:
// Write Mode 1: In this mode, data is transferred directly from the 32 bit latch register to display memory, affected only by the Memory Plane Write Enable field. The host data is not used in this mode.
new_latch.d=vga.latch.d;
bit_mask=0xff;
break;
case 0x02:
//Write Mode 2: In this mode, the bits 3-0 of the host data are replicated across all 8 bits of their respective planes. Then the selected Logical Operation is performed on the resulting data and the data in the latch register. Then the Bit Mask field is used to select which bits come from the resulting data and which come from the latch register. Finally, only the bit planes enabled by the Memory Plane Write Enable field are written to memory.
new_latch.d=RasterOp(FillTable[val&0xF],vga.config.full_bit_mask);
bit_mask=vga.gfx.bit_mask;
break;
case 0x03:
// Write Mode 3: In this mode, the data in the Set/Reset field is used as if the Enable Set/Reset field were set to 1111b. Then the host data is first rotated as per the Rotate Count field, then logical ANDed with the value of the Bit Mask field. The resulting value is used on the data obtained from the Set/Reset field in the same way that the Bit Mask field would ordinarily be used. to select which bits come from the expansion of the Set/Reset field and which come from the latch register. Finally, only the bit planes enabled by the Memory Plane Write Enable field are written to memory.
new_latch.d=ExpandTable[val];
new_latch.d&=vga.config.full_bit_mask; //Dunno would anyone use this seems a bit pointless
bit_mask=new_latch.b[0];
new_latch.d=RasterOp(FillTable[vga.config.set_reset],new_latch.d);
break;
default:
LOG_ERROR("VGA:Unsupported write mode %d",vga.config.write_mode);
}
/* Update video memory and the pixel buffer */
VGA_Latch pixels;
pixels.d=vga.mem.latched[start].d;
pixels.d&=~vga.config.full_map_mask;
pixels.d|=(new_latch.d & vga.config.full_map_mask);
vga.mem.latched[start].d=pixels.d;
Bit8u * write_pixels=&vga.buffer[start<<3];
#if 1
Bit8u sel=128;
do {
if (bit_mask & sel) {
Bitu color;
color=0;
if (pixels.b[0] & sel) color|=1;
if (pixels.b[1] & sel) color|=2;
if (pixels.b[2] & sel) color|=4;
if (pixels.b[3] & sel) color|=8;
*write_pixels=color;
*(write_pixels+512*1024)=color;
}
write_pixels++;
sel>>=1;
} while (sel);
#else
#include "ega-switch.h"
#endif
}
void VGA_GFX_256U_WriteHandler(Bit32u start,Bit8u val) {
VGA_Latch new_latch;
switch (vga.config.write_mode) {
case 0x00:
/* This should be no problem with big or little endian */
// Write Mode 0: In this mode, the host data is first rotated as per the Rotate Count field, then the Enable Set/Reset mechanism selects data from this or the Set/Reset field. Then the selected Logical Operation is performed on the resulting data and the data in the latch register. Then the Bit Mask field is used to select which bits come from the resulting data and which come from the latch register. Finally, only the bit planes enabled by the Memory Plane Write Enable field are written to memory.
// val=(val >> vga.config.data_rotate) | (val << (8-vga.config.data_rotate));
//Todo could also include the rotation in the table :)
new_latch.d=ExpandTable[val];
{
Bit32u resetmask=FillTable[vga.config.enable_set_reset];
new_latch.d=(new_latch.d & ~resetmask) | ( FillTable[vga.config.set_reset] & resetmask);
};
new_latch.d=RasterOp(new_latch.d,vga.config.full_bit_mask);
break;
case 0x01:
// Write Mode 1: In this mode, data is transferred directly from the 32 bit latch register to display memory, affected only by the Memory Plane Write Enable field. The host data is not used in this mode.
new_latch.d=vga.latch.d;
break;
case 0x02:
//TODO this mode also has Raster op
//Write Mode 2: In this mode, the bits 3-0 of the host data are replicated across all 8 bits of their respective planes. Then the selected Logical Operation is performed on the resulting data and the data in the latch register. Then the Bit Mask field is used to select which bits come from the resulting data and which come from the latch register. Finally, only the bit planes enabled by the Memory Plane Write Enable field are written to memory.
new_latch.d=RasterOp(FillTable[val&0xF],vga.config.full_bit_mask);
break;
case 0x03:
// Write Mode 3: In this mode, the data in the Set/Reset field is used as if the Enable Set/Reset field were set to 1111b. Then the host data is first rotated as per the Rotate Count field, then logical ANDed with the value of the Bit Mask field. The resulting value is used on the data obtained from the Set/Reset field in the same way that the Bit Mask field would ordinarily be used. to select which bits come from the expansion of the Set/Reset field and which come from the latch register. Finally, only the bit planes enabled by the Memory Plane Write Enable field are written to memory.
new_latch.d=ExpandTable[val];
new_latch.d&=vga.config.full_bit_mask; //Dunno would anyone use this seems a bit pointless
new_latch.d=RasterOp(FillTable[vga.config.set_reset],new_latch.d);
break;
default:
E_Exit("VGA:Unsupported write mode %d",vga.config.write_mode);
}
VGA_Latch pixels;
pixels.d=vga.mem.latched[start].d;
pixels.d&=~vga.config.full_map_mask;
pixels.d|=(new_latch.d & vga.config.full_map_mask);
vga.mem.latched[start].d=pixels.d;
vga.mem.latched[start+64*1024].d=pixels.d;
};
void VGA_SetupMemory() {
memset((void *)&vga.mem,0,256*1024);
/* Alocate Video Memory */
/* Not needed for VGA memory it gets allocated together with emulator maybe
later for VESA memory */
}

70
src/hardware/vga_misc.cpp Normal file
View file

@ -0,0 +1,70 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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 "dosbox.h"
#include "inout.h"
#include "timer.h"
#include "vga.h"
static Bit8u flip=0;
static Bit32u keep_vretrace;
static bool keeping=false;
static Bit8u p3c2data;
static Bit8u read_p3da(Bit32u port) {
vga.internal.attrindex=false;
if (vga.config.retrace) {
vga.config.retrace=false;
vga.config.real_start=vga.config.display_start;
keep_vretrace=LastTicks+1;
keeping=true;
flip=0;
return 9;
}
if (keeping) {
if (LastTicks>(keep_vretrace)) keeping=false;
return 9;
} else {
flip++;
if (flip>10) flip=0;
if (flip>5) return 1;
return 0;
}
};
static void write_p3d8(Bit32u port,Bit8u val) {
return;
}
static void write_p3c2(Bit32u port,Bit8u val) {
p3c2data=val;
}
static Bit8u read_p3c2(Bit32u port) {
return p3c2data;
}
void VGA_SetupMisc(void) {
IO_RegisterReadHandler(0x3da,read_p3da,"VGA Input Status 1");
// IO_RegisterWriteHandler(0x3d8,write_p3d8,"VGA Mode Control");
IO_RegisterWriteHandler(0x3c2,write_p3c2,"VGA Misc Output");
IO_RegisterReadHandler(0x3c2,read_p3c2,"VGA Misc Output");
}

125
src/hardware/vga_seq.cpp Normal file
View file

@ -0,0 +1,125 @@
/*
* Copyright (C) 2002 The DOSBox Team
*
* 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 "dosbox.h"
#include "inout.h"
#include "vga.h"
#define seq(blah) vga.seq.blah
Bit8u read_p3c4(Bit32u port) {
return seq(index);
}
void write_p3c4(Bit32u port,Bit8u val) {
seq(index)=val;
};
void write_p3c5(Bit32u port,Bit8u val) {
switch(seq(index)) {
case 0: /* Reset */
seq(reset)=val;
break;
case 1: /* Clocking Mode */
seq(clocking_mode)=val;
/* TODO Figure this out :)
0 If set character clocks are 8 dots wide, else 9.
2 If set loads video serializers every other character
clock cycle, else every one.
3 If set the Dot Clock is Master Clock/2, else same as Master Clock
(See 3C2h bit 2-3). (Doubles pixels). Note: on some SVGA chipsets
this bit also affects the Sequencer mode.
4 If set loads video serializers every fourth character clock cycle,
else every one.
5 if set turns off screen and gives all memory cycles to the CPU
interface.
*/
break;
case 2: /* Map Mask */
seq(map_mask)=val & 15;
vga.config.full_map_mask=FillTable[val & 15];
/*
0 Enable writes to plane 0 if set
1 Enable writes to plane 1 if set
2 Enable writes to plane 2 if set
3 Enable writes to plane 3 if set
*/
break;
case 3: /* Character Map Select */
seq(character_map_select)=val;
/*
0,1,4 Selects VGA Character Map (0..7) if bit 3 of the character
attribute is clear.
2,3,5 Selects VGA Character Map (0..7) if bit 3 of the character
attribute is set.
Note: Character Maps are placed as follows:
Map 0 at 0k, 1 at 16k, 2 at 32k, 3: 48k, 4: 8k, 5: 24k, 6: 40k, 7: 56k
*/
break;
case 4: /* Memory Mode */
/*
0 Set if in an alphanumeric mode, clear in graphics modes.
1 Set if more than 64kbytes on the adapter.
2 Enables Odd/Even addressing mode if set. Odd/Even mode places all odd
bytes in plane 1&3, and all even bytes in plane 0&2.
3 If set address bit 0-1 selects video memory planes (256 color mode),
rather than the Map Mask and Read Map Select Registers.
*/
seq(memory_mode)=val;
/* Changing this means changing the VGA Memory Read/Write Handler */
if (val&0x08) vga.config.chained=true;
else vga.config.chained=false;
VGA_FindSettings();
break;
default:
LOG_ERROR("VGA:SEQ:Write to illegal index %2X",seq(index));
};
};
Bit8u read_p3c5(Bit32u port) {
switch(seq(index)) {
case 0: /* Reset */
return seq(reset);
break;
case 1: /* Clocking Mode */
return seq(clocking_mode);
break;
case 2: /* Map Mask */
return seq(map_mask);
break;
case 3: /* Character Map Select */
return seq(character_map_select);
break;
case 4: /* Memory Mode */
return seq(memory_mode);
default:
LOG_ERROR("VGA:SEQ:Read from illegal index %2X",seq(index));
};
return 0;
};
void VGA_SetupSEQ(void) {
IO_RegisterWriteHandler(0x3c4,write_p3c4,"VGA:Sequencer Index");
IO_RegisterWriteHandler(0x3c5,write_p3c5,"VGA:Sequencer Data");
IO_RegisterReadHandler(0x3c4,read_p3c4,"VGA:Sequencer Index");
IO_RegisterReadHandler(0x3c5,read_p3c5,"VGA:Sequencer Data");
}