--- hatari/src/falcon/videl.c 2019/04/09 08:47:21 1.1.1.4 +++ hatari/src/falcon/videl.c 2019/04/09 08:49:37 1.1.1.6 @@ -11,18 +11,71 @@ This file originally came from the Aranym project and has been heavily modified to work for Hatari (but the kudos for the great Videl emulation code goes to the people from the Aranym project of course). + + Videl can run at 2 frequencies : 25.175 Mhz or 32 MHz + + Hardware I/O registers: + + $FFFF8006 (byte) : monitor type + + $FFFF8201 (byte) : VDL_VBH - Video Base Hi + $FFFF8203 (byte) : VDL_VBM - Video Base Mi + $FFFF8205 (byte) : VDL_VCH - Video Count Hi + $FFFF8207 (byte) : VDL_VCM - Video Count Mi + $FFFF8209 (byte) : VDL_VCL - Video Count Lo + $FFFF820A (byte) : VDL_SYM - Sync mode + $FFFF820D (byte) : VDL_VBL - Video Base Lo + $FFFF820E (word) : VDL_LOF - Offset to next line + $FFFF8210 (word) : VDL_LWD - Line Wide in Words + + $FFFF8240 (word) : VDL_STC - ST Palette Register 00 + ......... + $FFFF825E (word) : VDL_STC - ST Palette Register 15 + + $FFFF8260 (byte) : ST shift mode + $FFFF8265 (byte) : Horizontal scroll register + $FFFF8266 (word) : Falcon shift mode + + $FFFF8280 (word) : HHC - Horizontal Hold Counter + $FFFF8282 (word) : HHT - Horizontal Hold Timer + $FFFF8284 (word) : HBB - Horizontal Border Begin + $FFFF8286 (word) : HBE - Horizontal Border End + $FFFF8288 (word) : HDB - Horizontal Display Begin + $FFFF828A (word) : HDE - Horizontal Display End + $FFFF828C (word) : HSS - Horizontal SS + $FFFF828E (word) : HFS - Horizontal FS + $FFFF8290 (word) : HEE - Horizontal EE + + $FFFF82A0 (word) : VFC - Vertical Frequency Counter + $FFFF82A2 (word) : VFT - Vertical Frequency Timer + $FFFF82A4 (word) : VBB - Vertical Border Begin + $FFFF82A6 (word) : VBE - Vertical Border End + $FFFF82A8 (word) : VDB - Vertical Display Begin + $FFFF82AA (word) : VDE - Vertical Display End + $FFFF82AC (word) : VSS - Vertical SS + + $FFFF82C0 (word) : VCO - Video control + $FFFF82C2 (word) : VMD - Video mode + + $FFFF9800 (long) : VDL_PAL - Videl palette Register 000 + ........... + $FFFF98FC (long) : VDL_PAL - Videl palette Register 255 */ + const char VIDEL_fileid[] = "Hatari videl.c : " __DATE__ " " __TIME__; +#include +#include #include "main.h" #include "configuration.h" +#include "memorySnapShot.h" #include "ioMem.h" +#include "log.h" #include "hostscreen.h" #include "screen.h" #include "stMemory.h" #include "video.h" #include "videl.h" -#include #define handleRead(a) IoMem_ReadByte(a) @@ -40,27 +93,45 @@ const char VIDEL_fileid[] = "Hatari vide #define HW 0xff8200 #define VIDEL_COLOR_REGS_BEGIN 0xff9800 +struct videl_s { + bool bUseSTShifter; /* whether to use ST or Falcon palette */ + Uint8 reg_ffff8006_save; /* save reg_ffff8006 as it's a read only register */ + Uint8 monitor_type; /* 00 Monochrome (SM124) / 01 Color (SC1224) / 10 VGA Color / 11 Television ($FFFF8006) */ +}; + +static struct videl_s videl; -static int width, height, bpp, since_last_change; +/* TODO: put these to some struct so that it's easier to see + * they're VIDEL global + */ +static int width, height, bpp; static bool hostColorsSync; +Uint16 vfc_counter; /* counter for VFC register $ff82a0 (to be internalized when VIDEL emulation is complete) */ + /* Autozoom */ static int zoomwidth, prev_scrwidth; static int zoomheight, prev_scrheight; static int *zoomxtable; static int *zoomytable; + + static void VIDEL_renderScreenNoZoom(void); static void VIDEL_renderScreenZoom(void); - + // Called upon startup and when CPU encounters a RESET instruction. void VIDEL_reset(void) { - since_last_change = 0; - - hostColorsSync = false; + videl.bUseSTShifter = false; /* Use Falcon color palette by default */ + videl.reg_ffff8006_save = IoMem_ReadByte(0xff8006); + videl.monitor_type = videl.reg_ffff8006_save & 0xc0; + + hostColorsSync = false; + vfc_counter = 0; + /* Autozoom */ zoomwidth=prev_scrwidth=0; zoomheight=prev_scrheight=0; @@ -77,18 +148,331 @@ void VIDEL_reset(void) IoMem_WriteWord(0xff8264, 0); /* Horizontal scroll */ } -// monitor write access to Falcon and ST/E color palette registers +/** + * Save/Restore snapshot of local variables ('MemorySnapShot_Store' handles type) + */ +void VIDEL_MemorySnapShot_Capture(bool bSave) +{ + /* Save/Restore details */ + MemorySnapShot_Store(&videl, sizeof(videl)); + MemorySnapShot_Store(&vfc_counter, sizeof(vfc_counter)); +} + +/** + * Monitor write access to ST/E color palette registers + */ void VIDEL_ColorRegsWrite(void) { hostColorsSync = false; } -void VIDEL_ShiftModeWriteWord(void) +/** + * VIDEL_Monitor_WriteByte : Contains memory and monitor configuration. + * This register is read only. + */ +void VIDEL_Monitor_WriteByte(void) +{ + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff8006 Monitor and memory conf write (Read only)\n"); + /* Restore hardware value */ + IoMem_WriteByte(0xff8006, videl.reg_ffff8006_save); +} + +/** + * Write to video address base high, med and low register (0xff8201/03/0d). + * On Falcon, when a program writes to high or med registers, base low register + * is reset to zero. + */ +void VIDEL_ScreenBase_WriteByte(void) { - Dprintf(("VIDEL f_shift: %06x = 0x%x\n", IoAccessBaseAddress, handleReadW(HW+0x66))); - bUseSTShifter = false; + Uint32 screenBase; + + if ((IoAccessCurrentAddress == 0xff8201) || (IoAccessCurrentAddress == 0xff8203)) { + /* Reset screen base low register */ + IoMem[0xff820d] = 0; + } + + screenBase = (IoMem[0xff8201]<<16)+(IoMem[0xff8203]<<8)+IoMem[0xff820d]; + + LOG_TRACE(TRACE_VIDEL, "Videl : $%04x Screen base write: 0x%01x\t (screen: 0x%04x)\n", + IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress], screenBase); } +/** + VIDEL_ST_ShiftModeWriteByte : + $FFFF8260 [R/W] B ______10 ST Shift Mode + || + || others vga + || $FF8210 $FF82C2 $FF82C2 + 00--4BP/320 Pixels=> $0050 $0000 $0005 + 01--2BP/640 Pixels=> $0050 $0004 $0009 + 10--1BP/640 Pixels=> $0028 $0006 $0008 + 11--???/320 Pixels=> $0050 $0000 $0000 + + Writing to this register does the following things: + - activate STE palette + - sets line width ($ffff8210) + - sets video mode in $ffff82c2 (double lines/interlace & cycles/pixel) + */ +void VIDEL_ST_ShiftModeWriteByte(void) +{ + Uint16 line_width, video_mode; + Uint8 st_shiftMode; + + st_shiftMode = IoMem_ReadByte(0xff8260); + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff8260 ST Shift Mode (STSHIFT) write: 0x%x\n", st_shiftMode); + + /* Activate STE palette */ + videl.bUseSTShifter = true; + + /* Compute line width and video mode */ + switch (st_shiftMode & 0x3) { + case 0: /* 4BP/320 Pixels */ + line_width = 0x50; + /* half pixels + double lines vs. no scaling */ + video_mode = videl.monitor_type == FALCON_MONITOR_VGA ? 0x5 : 0x0; + break; + case 1: /* 2BP/640 Pixels */ + line_width = 0x50; + /* quarter pixels + double lines vs. half pixels */ + video_mode = videl.monitor_type == FALCON_MONITOR_VGA ? 0x9 : 0x4; + break; + case 2: /* 1BP/640 Pixels */ + line_width = 0x28; + if (videl.monitor_type == FALCON_MONITOR_MONO) { + video_mode = 0x0; + break; + } + /* quarter pixels vs. half pixels + interlace */ + video_mode = videl.monitor_type == FALCON_MONITOR_VGA ? 0x8 : 0x6; + break; + case 3: /* ???/320 Pixels */ + line_width = 0x50; + video_mode = 0x0; + break; + } + + /* Set line width ($FFFF8210) */ + IoMem_WriteWord(0xff8210, line_width); + + /* Set video mode ($FFFF82C2) */ + IoMem_WriteWord(0xff82c2, video_mode); +} + +/** + VIDEL_FALCON_ShiftModeWriteWord : + $FFFF8266 [R/W] W _____A98_6543210 Falcon Shift Mode (SPSHIFT) + ||| ||||||| + ||| |||++++- 0..15: Colourbank setting in 8BP + ||| ||+----- 0: 8 Bitplanes (256 Colors) off + ||| ||+----- 1: 8 Bitplanes (256 Colors) on + ||| |+------ 0: internal Vertical Sync + ||| | 1: external Vertical Sync + ||| +------- 0: internal Horizontal Sync + ||| 1: external Horizontal Sync + ||+--------- 0: True-Color-Mode off + || 1: True-Color-Mode on + |+---------- 0: Overlay-Mode off + | 1: Overlay-Mode on + +----------- 0: 2-Color-Mode off + 1: 2-Color-Mode on + + Writing to this register does the following things: + - activate Falcon palette + - if you set Bits A/8/4 == 0, it selects 16-Color-Falcon-Mode (NOT the + same as ST LOW since Falcon palette is used!) + - $8260 register is ignored, you don't need to write here anything + + Note: 4-Color-Mode isn't realisable with Falcon palette. + */ +void VIDEL_FALC_ShiftModeWriteWord(void) +{ + Uint16 falc_shiftMode = IoMem_ReadWord(0xff8266); + + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff8266 Falcon Shift Mode (SPSHIFT) write: 0x%02x\n", falc_shiftMode); + + videl.bUseSTShifter = false; +} + +/** + * Write Horizontal Hold Counter (HHC) + */ +void VIDEL_HHC_WriteWord(void) +{ + Uint16 hhc = IoMem_ReadWord(0xff8280); + + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff8280 Horizontal Hold Counter (HHC) write: 0x%02x\n", hhc); +} + +/** + * Write Horizontal Hold Timer (HHT) + */ +void VIDEL_HHT_WriteWord(void) +{ + Uint16 hht = IoMem_ReadWord(0xff8282); + + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff8282 Horizontal Hold Timer (HHT) write: 0x%02x\n", hht); +} + +/** + * Write Horizontal Border Begin (HBB) + */ +void VIDEL_HBB_WriteWord(void) +{ + Uint16 hbb = IoMem_ReadWord(0xff8284); + + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff8284 Horizontal Border Begin (HBB) write: 0x%02x\n", hbb); +} + +/** + * Write Horizontal Border End (HBE) + */ +void VIDEL_HBE_WriteWord(void) +{ + Uint16 hbe = IoMem_ReadWord(0xff8286); + + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff8286 Horizontal Border End (HBE) write: 0x%02x\n", hbe); +} + +/** + * Write Horizontal Display Begin (HDB) + */ +void VIDEL_HDB_WriteWord(void) +{ + Uint16 hdb = IoMem_ReadWord(0xff8288); + + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff8288 Horizontal Display Begin (HDB) write: 0x%02x\n", hdb); +} + +/** + * Write Horizontal Display End (HDE) + */ +void VIDEL_HDE_WriteWord(void) +{ + Uint16 hde = IoMem_ReadWord(0xff828a); + + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff828a Horizontal Display End (HDE) write: 0x%02x\n", hde); +} + +/** + * Write Horizontal SS (HSS) + */ +void VIDEL_HSS_WriteWord(void) +{ + Uint16 hss = IoMem_ReadWord(0xff828c); + + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff828c Horizontal SS (HSS) write: 0x%02x\n", hss); +} + +/** + * Write Horizontal FS (HFS) + */ +void VIDEL_HFS_WriteWord(void) +{ + Uint16 hfs = IoMem_ReadWord(0xff828e); + + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff828e Horizontal FS (HFS) write: 0x%02x\n", hfs); +} + +/** + * Write Horizontal EE (HEE) + */ +void VIDEL_HEE_WriteWord(void) +{ + Uint16 hee = IoMem_ReadWord(0xff8290); + + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff8290 Horizontal EE (HEE) write: 0x%02x\n", hee); +} + +/** + * Write Vertical Frequency Counter (VFC) + */ +void VIDEL_VFC_ReadWord(void) +{ + IoMem_WriteWord(0xff82a0, vfc_counter); + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff82a0 Vertical Frequency Counter (VFC) read: 0x%02x\n", vfc_counter); +} + +/** + * Write Vertical Frequency Timer (VFT) + */ +void VIDEL_VFT_WriteWord(void) +{ + Uint16 vft = IoMem_ReadWord(0xff82a2); + + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff82a2 Vertical Frequency Timer (VFT) write: 0x%02x\n", vft); +} + +/** + * Write Vertical Border Begin (VBB) + */ +void VIDEL_VBB_WriteWord(void) +{ + Uint16 vbb = IoMem_ReadWord(0xff82a4); + + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff82a4 Vertical Border Begin (VBB) write: 0x%02x\n", vbb); +} + +/** + * Write Vertical Border End (VBE) + */ +void VIDEL_VBE_WriteWord(void) +{ + Uint16 vbe = IoMem_ReadWord(0xff82a6); + + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff82a6 Vertical Border End (VBE) write: 0x%02x\n", vbe); +} + +/** + * Write Vertical Display Begin (VDB) + */ +void VIDEL_VDB_WriteWord(void) +{ + Uint16 vdb = IoMem_ReadWord(0xff82a8); + + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff82a8 Vertical Display Begin (VDB) write: 0x%02x\n", vdb); +} + +/** + * Write Vertical Display End (VDE) + */ +void VIDEL_VDE_WriteWord(void) +{ + Uint16 vde = IoMem_ReadWord(0xff82aa); + + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff82aa Vertical Display End (VDE) write: 0x%02x\n", vde); +} + +/** + * Write Vertical SS (VSS) + */ +void VIDEL_VSS_WriteWord(void) +{ + Uint16 vss = IoMem_ReadWord(0xff82ac); + + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff82ac Vertical SS (VSS) write: 0x%02x\n", vss); +} + +/** + * Write Video Control (VCO) + */ +void VIDEL_VCO_WriteWord(void) +{ + Uint16 vco = IoMem_ReadWord(0xff82c0); + + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff82c0 Video control (VCO) write: 0x%02x\n", vco); +} + +/** + * Write Video Mode (VDM) + */ +void VIDEL_VMD_WriteWord(void) +{ + Uint16 vdm = IoMem_ReadWord(0xff82c2); + + LOG_TRACE(TRACE_VIDEL, "Videl : $ffff82c2 Video Mode (VDM) write: 0x%02x\n", vdm); +} + + static long VIDEL_getVideoramAddress(void) { return (handleRead(HW + 1) << 16) | (handleRead(HW + 3) << 8) | handleRead(HW + 0x0d); @@ -113,7 +497,7 @@ static int VIDEL_getScreenBpp(void) bits_per_pixel = 16; else if (f_shift & 0x010) /* Falcon: 8 bitplanes */ bits_per_pixel = 8; - else if (!bUseSTShifter) /* Falcon: 4 bitplanes */ + else if (!videl.bUseSTShifter) /* Falcon: 4 bitplanes */ bits_per_pixel = 4; else if (st_shift == 0) bits_per_pixel = 4; @@ -152,6 +536,60 @@ static int VIDEL_getScreenHeight(void) return yres; } +#if 0 +/* this is easier & more robustly done in hostscreen.c just by + * comparing requested screen width & height to each other. + */ +static void VIDEL_getMonitorScale(int *sx, int *sy) +{ + /* Videl video mode register bits and resulting desktop resolution: + * + * quarter, half, interlace, double: pixels: -> zoom: + * rgb: + * 0 0 0 0 320x200 -> 2 x 2 + * 0 0 1 0 320x400 -> 2 x 1 + * 0 1 0 0 640x200 -> 1 x 2 ! + * 0 1 1 0 640x400 -> 1 x 1 + * vga: + * 0 0 0 1 (just double ?) + * 0 0 1 1 (double & interlace ???) + * 0 1 0 0 320x480 -> 2 x 1 ! + * 0 1 0 1 320x240 -> 2 x 2 + * 0 1 1 1 (double + interlace ???) + * 1 0 0 0 640x480 -> 1 x 1 + * 1 0 0 1 640x240 -> 1 x 2 + */ + int vmode = handleReadW(HW + 0xc2); + + /* half pixel seems to have opposite meaning on + * VGA and RGB monitor, so they need to handled separately + */ + if (videl.monitor_type) == FALCON_MONITOR_VGA) { + if (vmode & 0x08) { // quarter pixel + *sx = 1; + } else { + *sx = 2; + } + if (vmode & 0x01) { // double line + *sy = 2; + } else { + *sy = 1; + } + } else { + if (vmode & 0x04) { // half pixel + *sx = 1; + } else { + *sx = 2; + } + if (vmode & 0x02) { // interlace used only on RGB? + *sy = 1; + } else { + *sy = 2; + } + } +} +#endif + /** map the correct colortable into the correct pixel format */ @@ -164,7 +602,7 @@ static void VIDEL_updateColors(void) #define F_COLORS(i) handleRead(VIDEL_COLOR_REGS_BEGIN + (i)) #define STE_COLORS(i) handleRead(0xff8240 + (i)) - if (!bUseSTShifter) { + if (!videl.bUseSTShifter) { for (i = 0; i < colors; i++) { int offset = i << 2; r = F_COLORS(offset) & 0xfc; @@ -209,36 +647,32 @@ bool VIDEL_renderScreen(void) int vw = VIDEL_getScreenWidth(); int vh = VIDEL_getScreenHeight(); int vbpp = VIDEL_getScreenBpp(); + bool change = false; - if (since_last_change > 2) { - if (vw > 0 && vw != width) { - Dprintf(("CH width %d\n", width)); - width = vw; - since_last_change = 0; - } - if (vh > 0 && vh != height) { - Dprintf(("CH height %d\n", width)); - height = vh; - since_last_change = 0; - } - if (vbpp != bpp) { - Dprintf(("CH bpp %d\n", vbpp)); - bpp = vbpp; - since_last_change = 0; - } + if (vw > 0 && vw != width) { + Dprintf(("CH width %d\n", width)); + width = vw; + change = true; } - if (since_last_change == 3) { - HostScreen_setWindowSize(width, height, bpp == 16 ? 16 : ConfigureParams.Screen.nForceBpp); + if (vh > 0 && vh != height) { + Dprintf(("CH height %d\n", width)); + height = vh; + change = true; } - if (since_last_change < 4) { - since_last_change++; - return false; + if (vbpp != bpp) { + Dprintf(("CH bpp %d\n", vbpp)); + bpp = vbpp; + change = true; + } + if (change) { + LOG_TRACE(TRACE_VIDEL, "Videl : video mode change to %dx%d@%d\n", width, height, bpp); + HostScreen_setWindowSize(width, height, bpp == 16 ? 16 : ConfigureParams.Screen.nForceBpp); } if (!HostScreen_renderBegin()) return false; - if (ConfigureParams.Screen.bZoomLowRes) { + if (nScreenZoomX * nScreenZoomY != 1) { VIDEL_renderScreenZoom(); } else { VIDEL_renderScreenNoZoom(); @@ -425,6 +859,7 @@ void VIDEL_ConvertScreenNoZoom(int vw, i Uint16 *fvram = (Uint16 *) Atari2HostAddr(atariVideoRAM); Uint8 *hvram = HostScreen_getVideoramAddress(); + SDL_PixelFormat *scrfmt = HostScreen_getFormat(); int hscrolloffset = (handleRead(HW + 0x65) & 0x0f); @@ -639,14 +1074,14 @@ void VIDEL_ConvertScreenNoZoom(int vw, i int w; for (w = 0; w < vw_clip; w++) { - int data = *fvram_column++; + Uint16 srcword = *fvram_column++; *hvram_column++ = - HostScreen_getColor( - (Uint8) (data & 0xf8), - (Uint8) ( ((data & 0x07) << 5) | - ((data >> 11) & 0x3c)), - (Uint8) ((data >> 5) & 0xf8)); + SDL_MapRGB(scrfmt, + (srcword & 0xf8), + (((srcword & 0x07) << 5) | + ((srcword >> 11) & 0x3c)), + ((srcword >> 5) & 0xf8)); } hvram_line += scrpitch>>2; @@ -691,6 +1126,7 @@ void VIDEL_ConvertScreenZoom(int vw, int int scrwidth = HostScreen_getWidth(); int scrheight = HostScreen_getHeight(); int scrbpp = HostScreen_getBpp(); + SDL_PixelFormat *scrfmt = HostScreen_getFormat(); Uint8 *hvram = (Uint8 *) HostScreen_getVideoramAddress(); int hscrolloffset = (handleRead(HW + 0x65) & 0x0f); @@ -799,7 +1235,7 @@ void VIDEL_ConvertScreenZoom(int vw, int /* One complete planar 2 chunky line */ Uint16 *p2cline = malloc(sizeof(Uint16)*vw); - Uint16 *fvram_line = fvram; + Uint16 *fvram_line; Uint16 *hvram_line = (Uint16 *)hvram; for (h = 0; h < scrheight; h++) { @@ -995,11 +1431,11 @@ void VIDEL_ConvertScreenZoom(int vw, int srcword = fvram_column[zoomxtable[w]]; *hvram_column++ = - HostScreen_getColor( - (Uint8) (srcword & 0xf8), - (Uint8) ( ((srcword & 0x07) << 5) | - ((srcword >> 11) & 0x3c)), - (Uint8) ((srcword >> 5) & 0xf8)); + SDL_MapRGB(scrfmt, + (srcword & 0xf8), + (((srcword & 0x07) << 5) | + ((srcword >> 11) & 0x3c)), + ((srcword >> 5) & 0xf8)); } }