/***************************************************************************************1* Genesis Plus2* CD graphics processor3*4* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)5*6* Redistribution and use of this code or any derivative works are permitted7* provided that the following conditions are met:8*9* - Redistributions may not be sold, nor may they be used in a commercial10* product or activity.11*12* - Redistributions that are modified from the original source must include the13* complete source code, including the source code for all components used by a14* binary built from the modified sources. However, as a special exception, the15* source code distributed need not include anything that is normally distributed16* (in either source or binary form) with the major components (compiler, kernel,17* and so on) of the operating system on which the executable runs, unless that18* component itself accompanies the executable.19*20* - Redistributions must reproduce the above copyright notice, this list of21* conditions and the following disclaimer in the documentation and/or other22* materials provided with the distribution.23*24* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"25* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE26* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE27* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE28* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR29* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF30* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS31* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN32* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)33* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE34* POSSIBILITY OF SUCH DAMAGE.35*36****************************************************************************************/37#include "shared.h"3839/***************************************************************/40/* WORD-RAM DMA interfaces (1M & 2M modes) */41/***************************************************************/4243void word_ram_0_dma_w(unsigned int words)44{45uint16 data;4647/* CDC buffer source address */48uint16 src_index = cdc.dac.w & 0x3ffe;4950/* WORD-RAM destination address*/51uint32 dst_index = (scd.regs[0x0a>>1].w << 3) & 0x1fffe;5253/* update DMA destination address */54scd.regs[0x0a>>1].w += (words >> 2);5556/* update DMA source address */57cdc.dac.w += (words << 1);5859/* DMA transfer */60while (words--)61{62/* read 16-bit word from CDC buffer */63data = *(uint16 *)(cdc.ram + src_index);6465#ifdef LSB_FIRST66/* source data is stored in big endian format */67data = ((data >> 8) | (data << 8)) & 0xffff;68#endif6970/* write 16-bit word to WORD-RAM */71*(uint16 *)(scd.word_ram[0] + dst_index) = data ;7273/* increment CDC buffer source address */74src_index = (src_index + 2) & 0x3ffe;7576/* increment WORD-RAM destination address */77dst_index = (dst_index + 2) & 0x1fffe;78}79}8081void word_ram_1_dma_w(unsigned int words)82{83uint16 data;8485/* CDC buffer source address */86uint16 src_index = cdc.dac.w & 0x3ffe;8788/* WORD-RAM destination address*/89uint32 dst_index = ((scd.regs[0x0a>>1].w << 3) & 0x1fffe);9091/* update DMA destination address */92scd.regs[0x0a>>1].w += (words >> 2);9394/* update DMA source address */95cdc.dac.w += (words << 1);9697/* DMA transfer */98while (words--)99{100/* read 16-bit word from CDC buffer */101data = *(uint16 *)(cdc.ram + src_index);102103#ifdef LSB_FIRST104/* source data is stored in big endian format */105data = ((data >> 8) | (data << 8)) & 0xffff;106#endif107108/* write 16-bit word to WORD-RAM */109*(uint16 *)(scd.word_ram[1] + dst_index) = data ;110111/* increment CDC buffer source address */112src_index = (src_index + 2) & 0x3ffe;113114/* increment WORD-RAM destination address */115dst_index = (dst_index + 2) & 0x1fffe;116}117}118119void word_ram_2M_dma_w(unsigned int words)120{121uint16 data;122123/* CDC buffer source address */124uint16 src_index = cdc.dac.w & 0x3ffe;125126/* WORD-RAM destination address*/127uint32 dst_index = (scd.regs[0x0a>>1].w << 3) & 0x3fffe;128129/* update DMA destination address */130scd.regs[0x0a>>1].w += (words >> 2);131132/* update DMA source address */133cdc.dac.w += (words << 1);134135/* DMA transfer */136while (words--)137{138/* read 16-bit word from CDC buffer */139data = *(uint16 *)(cdc.ram + src_index);140141#ifdef LSB_FIRST142/* source data is stored in big endian format */143data = ((data >> 8) | (data << 8)) & 0xffff;144#endif145146/* write 16-bit word to WORD-RAM */147*(uint16 *)(scd.word_ram_2M + dst_index) = data ;148149/* increment CDC buffer source address */150src_index = (src_index + 2) & 0x3ffe;151152/* increment WORD-RAM destination address */153dst_index = (dst_index + 2) & 0x3fffe;154}155}156157158/***************************************************************/159/* WORD-RAM 0 & 1 DOT image SUB-CPU interface (1M Mode) */160/***************************************************************/161162unsigned int dot_ram_0_read16(unsigned int address)163{164uint8 data = READ_BYTE(scd.word_ram[0], (address >> 1) & 0x1ffff);165return ((data & 0x0f) | ((data << 4) & 0xf00));166}167168unsigned int dot_ram_1_read16(unsigned int address)169{170uint8 data = READ_BYTE(scd.word_ram[1], (address >> 1) & 0x1ffff);171return ((data & 0x0f) | ((data << 4) & 0xf00));172}173174void dot_ram_0_write16(unsigned int address, unsigned int data)175{176uint8 prev;177address = (address >> 1) & 0x1ffff;178prev = READ_BYTE(scd.word_ram[0], address);179data = (data & 0x0f) | ((data >> 4) & 0xf0);180data = gfx.lut_prio[(scd.regs[0x02>>1].w >> 3) & 0x03][prev][data];181WRITE_BYTE(scd.word_ram[0], address, data);182}183184void dot_ram_1_write16(unsigned int address, unsigned int data)185{186uint8 prev;187address = (address >> 1) & 0x1ffff;188prev = READ_BYTE(scd.word_ram[1], address);189data = (data & 0x0f) | ((data >> 4) & 0xf0);190data = gfx.lut_prio[(scd.regs[0x02>>1].w >> 3) & 0x03][prev][data];191WRITE_BYTE(scd.word_ram[1], address, data);192}193194unsigned int dot_ram_0_read8(unsigned int address)195{196uint8 data = READ_BYTE(scd.word_ram[0], (address >> 1) & 0x1ffff);197198if (address & 1)199{200return (data & 0x0f);201}202203return (data >> 4);204}205206unsigned int dot_ram_1_read8(unsigned int address)207{208uint8 data = READ_BYTE(scd.word_ram[1], (address >> 1) & 0x1ffff);209210if (address & 1)211{212return (data & 0x0f);213}214215return (data >> 4);216}217218void dot_ram_0_write8(unsigned int address, unsigned int data)219{220uint8 prev = READ_BYTE(scd.word_ram[0], (address >> 1) & 0x1ffff);221222if (address & 1)223{224data = (prev & 0xf0) | (data & 0x0f);225}226else227{228data = (prev & 0x0f) | (data << 4);229}230231data = gfx.lut_prio[(scd.regs[0x02>>1].w >> 3) & 0x03][prev][data];232WRITE_BYTE(scd.word_ram[0], (address >> 1) & 0x1ffff, data);233}234235void dot_ram_1_write8(unsigned int address, unsigned int data)236{237uint8 prev = READ_BYTE(scd.word_ram[1], (address >> 1) & 0x1ffff);238239if (address & 1)240{241data = (prev & 0xf0) | (data & 0x0f);242}243else244{245data = (prev & 0x0f) | (data << 4);246}247248data = gfx.lut_prio[(scd.regs[0x02>>1].w >> 3) & 0x03][prev][data];249WRITE_BYTE(scd.word_ram[1], (address >> 1) & 0x1ffff, data);250}251252253/***************************************************************/254/* WORD-RAM 0 & 1 CELL image MAIN-CPU interface (1M Mode) */255/***************************************************************/256257unsigned int cell_ram_0_read16(unsigned int address)258{259address = gfx.lut_offset[(address >> 2) & 0x7fff] | (address & 0x10002);260return *(uint16 *)(scd.word_ram[0] + address);261}262263unsigned int cell_ram_1_read16(unsigned int address)264{265address = gfx.lut_offset[(address >> 2) & 0x7fff] | (address & 0x10002);266return *(uint16 *)(scd.word_ram[1] + address);267}268269void cell_ram_0_write16(unsigned int address, unsigned int data)270{271address = gfx.lut_offset[(address >> 2) & 0x7fff] | (address & 0x10002);272*(uint16 *)(scd.word_ram[0] + address) = data;273}274275void cell_ram_1_write16(unsigned int address, unsigned int data)276{277address = gfx.lut_offset[(address >> 2) & 0x7fff] | (address & 0x10002);278*(uint16 *)(scd.word_ram[1] + address) = data;279}280281unsigned int cell_ram_0_read8(unsigned int address)282{283address = gfx.lut_offset[(address >> 2) & 0x7fff] | (address & 0x10003);284return READ_BYTE(scd.word_ram[0], address);285}286287unsigned int cell_ram_1_read8(unsigned int address)288{289address = gfx.lut_offset[(address >> 2) & 0x7fff] | (address & 0x10003);290return READ_BYTE(scd.word_ram[1], address);291}292293void cell_ram_0_write8(unsigned int address, unsigned int data)294{295address = gfx.lut_offset[(address >> 2) & 0x7fff] | (address & 0x10003);296WRITE_BYTE(scd.word_ram[0], address, data);297}298299void cell_ram_1_write8(unsigned int address, unsigned int data)300{301address = gfx.lut_offset[(address >> 2) & 0x7fff] | (address & 0x10003);302WRITE_BYTE(scd.word_ram[1], address, data);303}304305306/***************************************************************/307/* Rotation / Scaling operation (2M Mode) */308/***************************************************************/309310void gfx_init(void)311{312int i, j;313uint16 offset;314uint8 mask, row, col, temp;315316memset(&gfx, 0, sizeof(gfx_t));317318/* Initialize cell image lookup table */319/* $220000-$22FFFF corresponds to $200000-$20FFFF */320for (i=0; i<0x4000; i++)321{322offset = (i & 0x07) << 8; /* cell vline (0-7) */323offset = offset | (((i >> 8) & 0x3f) << 2); /* cell x offset (0-63) */324offset = offset | (((i >> 3) & 0x1f) << 11); /* cell y offset (0-31) */325gfx.lut_offset[i] = offset;326}327328/* $230000-$237FFF corresponds to $210000-$217FFF */329for (i=0x4000; i<0x6000; i++)330{331offset = (i & 0x07) << 8; /* cell vline (0-7) */332offset = offset | (((i >> 7) & 0x3f) << 2); /* cell x offset (0-63) */333offset = offset | (((i >> 3) & 0x0f) << 11); /* cell y offset (0-15) */334gfx.lut_offset[i] = offset;335}336337/* $238000-$23BFFF corresponds to $218000-$21BFFF */338for (i=0x6000; i<0x7000; i++)339{340offset = (i & 0x07) << 8; /* cell vline (0-7) */341offset = offset | (((i >> 6) & 0x3f) << 2); /* cell x offset (0-63) */342offset = offset | (((i >> 3) & 0x07) << 11); /* cell y offset (0-7) */343gfx.lut_offset[i] = offset | 0x8000;344}345346/* $23C000-$23DFFF corresponds to $21C000-$21DFFF */347for (i=0x7000; i<0x7800; i++)348{349offset = (i & 0x07) << 8; /* cell vline (0-7) */350offset = offset | (((i >> 5) & 0x3f) << 2); /* cell x offset (0-63) */351offset = offset | (((i >> 3) & 0x03) << 11); /* cell y offset (0-3) */352gfx.lut_offset[i] = offset | 0xc000;353}354355/* $23E000-$23FFFF corresponds to $21E000-$21FFFF */356for (i=0x7800; i<0x8000; i++)357{358offset = (i & 0x07) << 8; /* cell vline (0-7) */359offset = offset | (((i >> 5) & 0x3f) << 2); /* cell x offset (0-63) */360offset = offset | (((i >> 3) & 0x03) << 11); /* cell y offset (0-3) */361gfx.lut_offset[i] = offset | 0xe000;362}363364/* Initialize priority modes lookup table */365for (i=0; i<0x100; i++)366{367for (j=0; j<0x100; j++)368{369/* normal */370gfx.lut_prio[0][i][j] = j;371/* underwrite */372gfx.lut_prio[1][i][j] = ((i & 0x0f) ? (i & 0x0f) : (j & 0x0f)) | ((i & 0xf0) ? (i & 0xf0) : (j & 0xf0));373/* overwrite */374gfx.lut_prio[2][i][j] = ((j & 0x0f) ? (j & 0x0f) : (i & 0x0f)) | ((j & 0xf0) ? (j & 0xf0) : (i & 0xf0));375/* invalid */376gfx.lut_prio[3][i][j] = i;377}378}379380/* Initialize cell lookup table */381/* table entry = yyxxshrr (8 bits) */382/* with: yy = cell row (0-3) */383/* xx = cell column (0-3) */384/* s = stamp size (0=16x16, 1=32x32) */385/* hrr = HFLIP & ROTATION bits */386for (i=0; i<0x100; i++)387{388/* one stamp = 2x2 cells (16x16) or 4x4 cells (32x32) */389mask = (i & 8) ? 3 : 1;390row = (i >> 6) & mask;391col = (i >> 4) & mask;392393if (i & 4) { col = col ^ mask; } /* HFLIP (always first) */394if (i & 2) { col = col ^ mask; row = row ^ mask; } /* ROLL1 */395if (i & 1) { temp = col; col = row ^ mask; row = temp; } /* ROLL0 */396397/* cell offset (0-3 or 0-15) */398gfx.lut_cell[i] = row + col * (mask + 1);399}400401/* Initialize pixel lookup table */402/* table entry = yyyxxxhrr (9 bits) */403/* with: yyy = pixel row (0-7) */404/* xxx = pixel column (0-7) */405/* hrr = HFLIP & ROTATION bits */406for (i=0; i<0x200; i++)407{408/* one cell = 8x8 pixels */409row = (i >> 6) & 7;410col = (i >> 3) & 7;411412if (i & 4) { col = col ^ 7; } /* HFLIP (always first) */413if (i & 2) { col = col ^ 7; row = row ^ 7; } /* ROLL1 */414if (i & 1) { temp = col; col = row ^ 7; row = temp; } /* ROLL0 */415416/* pixel offset (0-63) */417gfx.lut_pixel[i] = col + row * 8;418}419}420421void gfx_reset(void)422{423/* Reset cycle counter */424gfx.cycles = 0;425}426427INLINE void gfx_render(uint32 bufferIndex, uint32 width)428{429uint8 pixel_in, pixel_out;430uint16 stamp_data;431uint32 stamp_index;432433/* pixel map start position for current line (13.3 format converted to 13.11) */434uint32 xpos = *gfx.tracePtr++ << 8;435uint32 ypos = *gfx.tracePtr++ << 8;436437/* pixel map offset values for current line (5.11 format) */438uint32 xoffset = (int16) *gfx.tracePtr++;439uint32 yoffset = (int16) *gfx.tracePtr++;440441/* process all dots */442while (width--)443{444/* check if stamp map is repeated */445if (scd.regs[0x58>>1].byte.l & 0x01)446{447/* stamp map range */448xpos &= gfx.dotMask;449ypos &= gfx.dotMask;450}451else452{453/* 24-bit range */454xpos &= 0xffffff;455ypos &= 0xffffff;456}457458/* check if pixel is outside stamp map */459if ((xpos | ypos) & ~gfx.dotMask)460{461/* force pixel output to 0 */462pixel_out = 0x00;463}464else465{466/* read stamp map table data */467stamp_data = gfx.mapPtr[(xpos >> gfx.stampShift) | ((ypos >> gfx.stampShift) << gfx.mapShift)];468469/* stamp generator base index */470/* sss ssssssss ccyyyxxx (16x16) or sss sssssscc ccyyyxxx (32x32) */471/* with: s = stamp number (1 stamp = 16x16 or 32x32 pixels) */472/* c = cell offset (0-3 for 16x16, 0-15 for 32x32) */473/* yyy = line offset (0-7) */474/* xxx = pixel offset (0-7) */475stamp_index = (stamp_data & 0x7ff) << 8;476477if (stamp_index)478{479/* extract HFLIP & ROTATION bits */480stamp_data = (stamp_data >> 13) & 7;481482/* cell offset (0-3 or 0-15) */483/* table entry = yyxxshrr (8 bits) */484/* with: yy = cell row (0-3) = (ypos >> (11 + 3)) & 3 */485/* xx = cell column (0-3) = (xpos >> (11 + 3)) & 3 */486/* s = stamp size (0=16x16, 1=32x32) */487/* hrr = HFLIP & ROTATION bits */488stamp_index |= gfx.lut_cell[stamp_data | ((scd.regs[0x58>>1].byte.l & 0x02) << 2 ) | ((ypos >> 8) & 0xc0) | ((xpos >> 10) & 0x30)] << 6;489490/* pixel offset (0-63) */491/* table entry = yyyxxxhrr (9 bits) */492/* with: yyy = pixel row (0-7) = (ypos >> 11) & 7 */493/* xxx = pixel column (0-7) = (xpos >> 11) & 7 */494/* hrr = HFLIP & ROTATION bits */495stamp_index |= gfx.lut_pixel[stamp_data | ((xpos >> 8) & 0x38) | ((ypos >> 5) & 0x1c0)];496497/* read pixel pair (2 pixels/byte) */498pixel_out = READ_BYTE(scd.word_ram_2M, stamp_index >> 1);499500/* extract left or rigth pixel */501if (stamp_index & 1)502{503pixel_out &= 0x0f;504}505else506{507pixel_out >>= 4;508}509}510else511{512/* stamp 0 is not used: force pixel output to 0 */513pixel_out = 0x00;514}515}516517/* read out paired pixel data */518pixel_in = READ_BYTE(scd.word_ram_2M, bufferIndex >> 1);519520/* update left or rigth pixel */521if (bufferIndex & 1)522{523pixel_out |= (pixel_in & 0xf0);524}525else526{527pixel_out = (pixel_out << 4) | (pixel_in & 0x0f);528}529530/* priority mode write */531pixel_out = gfx.lut_prio[(scd.regs[0x02>>1].w >> 3) & 0x03][pixel_in][pixel_out];532533/* write data to image buffer */534WRITE_BYTE(scd.word_ram_2M, bufferIndex >> 1, pixel_out);535536/* check current pixel position */537if ((bufferIndex & 7) != 7)538{539/* next pixel */540bufferIndex++;541}542else543{544/* next cell: increment image buffer offset by one column (minus 7 pixels) */545bufferIndex += gfx.bufferOffset;546}547548/* increment pixel position */549xpos += xoffset;550ypos += yoffset;551}552}553554void gfx_start(unsigned int base, int cycles)555{556/* make sure 2M mode is enabled */557if (!(scd.regs[0x02>>1].byte.l & 0x04))558{559uint32 mask;560561/* trace vector pointer */562gfx.tracePtr = (uint16 *)(scd.word_ram_2M + ((base << 2) & 0x3fff8));563564/* stamps & stamp map size */565switch ((scd.regs[0x58>>1].byte.l >> 1) & 0x03)566{567case 0:568gfx.dotMask = 0x07ffff; /* 256x256 dots/map */569gfx.stampShift = 11 + 4; /* 16x16 dots/stamps */570gfx.mapShift = 4; /* 16x16 stamps/map */571mask = 0x3fe00; /* 512 bytes/table */572break;573574case 1:575gfx.dotMask = 0x07ffff; /* 256x256 dots/map */576gfx.stampShift = 11 + 5; /* 32x32 dots/stamps */577gfx.mapShift = 3; /* 8x8 stamps/map */578mask = 0x3ff80; /* 128 bytes/table */579break;580581case 2:582gfx.dotMask = 0x7fffff; /* 4096*4096 dots/map */583gfx.stampShift = 11 + 4; /* 16x16 dots/stamps */584gfx.mapShift = 8; /* 256x256 stamps/map */585mask = 0x20000; /* 131072 bytes/table */586break;587588case 3:589gfx.dotMask = 0x7fffff; /* 4096*4096 dots/map */590gfx.stampShift = 11 + 5; /* 32x32 dots/stamps */591gfx.mapShift = 7; /* 128x128 stamps/map */592mask = 0x38000; /* 32768 bytes/table */593break;594}595596/* stamp map table base address */597gfx.mapPtr = (uint16 *)(scd.word_ram_2M + ((scd.regs[0x5a>>1].w << 2) & mask));598599/* image buffer column offset (64 pixels/cell, minus 7 pixels to restart at cell beginning) */600gfx.bufferOffset = (((scd.regs[0x5c>>1].byte.l & 0x1f) + 1) << 6) - 7;601602/* image buffer start index in dot units (2 pixels/byte) */603gfx.bufferStart = (scd.regs[0x5e>>1].w << 3) & 0x7ffc0;604605/* add image buffer horizontal dot offset */606gfx.bufferStart += (scd.regs[0x60>>1].byte.l & 0x3f);607608/* reset GFX chip cycle counter */609gfx.cycles = cycles;610611/* update GFX chip timings (see AC3:Thunderhawk / Thunderstrike) */612gfx.cyclesPerLine = 4 * 5 * scd.regs[0x62>>1].w;613614/* start graphics operation */615scd.regs[0x58>>1].byte.h = 0x80;616}617}618619void gfx_update(int cycles)620{621/* synchronize GFX chip with SUB-CPU */622cycles -= gfx.cycles;623624/* make sure SUB-CPU is ahead */625if (cycles > 0)626{627/* number of lines to process */628unsigned int lines = (cycles + gfx.cyclesPerLine - 1) / gfx.cyclesPerLine;629630/* check against remaining lines */631if (lines < scd.regs[0x64>>1].byte.l)632{633/* update Vdot remaining size */634scd.regs[0x64>>1].byte.l -= lines;635636/* increment cycle counter */637gfx.cycles += lines * gfx.cyclesPerLine;638}639else640{641/* process remaining lines */642lines = scd.regs[0x64>>1].byte.l;643644/* clear Vdot remaining size */645scd.regs[0x64>>1].byte.l = 0;646647/* end of graphics operation */648scd.regs[0x58>>1].byte.h = 0;649650/* SUB-CPU idle on register $58 polling ? */651if (s68k.stopped & (1<<0x08))652{653/* sync SUB-CPU with GFX chip */654s68k.cycles = scd.cycles;655656/* restart SUB-CPU */657s68k.stopped = 0;658#ifdef LOG_SCD659error("s68k started from %d cycles\n", s68k.cycles);660#endif661}662663/* level 1 interrupt enabled ? */664if (scd.regs[0x32>>1].byte.l & 0x02)665{666/* trigger level 1 interrupt */667scd.pending |= (1 << 1);668669/* update IRQ level */670s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);671}672}673674/* render lines */675while (lines--)676{677/* process dots to image buffer */678gfx_render(gfx.bufferStart, scd.regs[0x62>>1].w);679680/* increment image buffer start index for next line (8 pixels/line) */681gfx.bufferStart += 8;682}683}684}685686687