Add mapper support: MMC1, UNROM, CNROM, MMC3, Color Dreams, FFE Copier, Jaleco SS88006; Sound support, Initial GUI menu stuff

This commit is contained in:
Alec Murphy
2019-05-17 17:49:00 -04:00
parent ed791dd811
commit d6eb903e68
17 changed files with 3700 additions and 330 deletions
+2
View File
@@ -0,0 +1,2 @@
uncrustify.cfg
.vscode/
Executable
+76
View File
@@ -0,0 +1,76 @@
// vim: set ft=c:
I64 start_buf_num;
U0 (*fp_old_fill_buf)(SND_OUT_CONTAINER *buf,I64 buf_num)=NULL;
U0 AudioFillBuf(SND_OUT_CONTAINER *buf,I64 buf_num)
{
I64 j=0,k;
I64 intL, intR;
U8 *buf2;
if (paused)
{
while (j<SND_BUF_LEN)
{
buf[j++]=0;
buf[j++]=0;
}
return;
}
while (j<SND_BUF_LEN)
{
// TODO: fix this - dirty hack to keep audio in sync
if (FifoI64Cnt(audio_fifo_L)>8192)
{
while (FifoI64Cnt(audio_fifo_L)>7680)
{
FifoI64Rem(audio_fifo_L, &k);
FifoI64Rem(audio_fifo_R, &k);
}
}
if (FifoI64Cnt(audio_fifo_L))
{
FifoI64Rem(audio_fifo_L, &intL);
}
else
{
intL = 0;
}
intL *= 0xFFFF;
if (intL < 0)
{
intL += 0x1000000;
}
if (FifoI64Cnt(audio_fifo_R))
{
FifoI64Rem(audio_fifo_R, &intR);
}
else
{
intR = 0;
}
intR *= 0xFFFF;
if (intR < 0)
{
intR += 0x1000000;
}
buf2 = buf+j;
buf2[0] = 0;
buf2[1] = intL;
buf2[2] = intL >> 8;
buf2[3] = intL >> 16;
j++;
buf2 = buf+j;
buf2[0] = 0;
buf2[1] = intR;
buf2[2] = intR >> 8;
buf2[3] = intR >> 16;
j++;
}
}
Regular → Executable
+18 -9
View File
@@ -26,8 +26,8 @@ U0 saveaccum(I64 n) {
//helper variables
U32 instructions = 0; //keep track of total instructions executed
I32 clockticks6502 = 0, clockgoal6502 = 0;
U16 oldpc, ea, reladdr, value, result;
U8 opcode, oldcpustatus, useaccum;
U16 oldpc=0, ea=0, reladdr=0, value=0, result=0;
U8 opcode=0, oldcpustatus=0, useaccum=0;
//flag modifier functions (converted from macros)
U0 setcarry() {
@@ -105,17 +105,27 @@ U8 pull8() {
U0 reset6502() {
pc = readRAM(0xFFFC) (U16) | (readRAM(0xFFFD) (U16) << 8);
instructions = 0;
clockgoal6502 = 0;
clockticks6502 = 0;
a = 0;
x = 0;
y = 0;
sp = 0xFD;
//cpustatus |= FLAG_CONSTANT;
cpustatus = FLAG_CONSTANT;
cpustatus |= FLAG_CONSTANT;
/*
writeRAM(0x4017, 00);
writeRAM(0x4015, 00);
I64 m=0;
for (m=0x4000;m<0x4010;m++)
{
writeRAM(m, 0);
}
for (m=0;m<0x800;m++)
{
writeRAM(m, 0);
}
*/
}
//addressing mode functions, calculates effective addresses
@@ -658,6 +668,7 @@ U0 tya() {
}
//undocumented instructions
#define UNDOCUMENTED
#ifdef UNDOCUMENTED
U0 lax() {
lda;
@@ -715,7 +726,6 @@ U0 nmi6502() {
push16(pc);
push8(cpustatus);
cpustatus |= FLAG_INTERRUPT;
//cpustatus &= FLAG_INTERRUPT;
pc = readRAM(0xFFFA) (U16) | (readRAM(0xFFFB) (U16) << 8);
}
@@ -1378,5 +1388,4 @@ U16 getpc() {
U8 getop() {
return(opcode);
}
}
Executable
+107
View File
@@ -0,0 +1,107 @@
// vim: set ft=c:
#ifndef GUI_FONT
#define GUI_FONT
U8 *gui_font_str = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-_~./<>[]:& #=`|*?%+,()@a!b;c^{}";
asm {
GUI_FONT_TBL::
DU8 0b0, 0b0, 0b0, 0b0, 0b0;
DU8 0b01110000, 0b10011000, 0b10101000, 0b11001000, 0b01110000;
DU8 0b00100000, 0b01100000, 0b00100000, 0b00100000, 0b01110000;
DU8 0b01110000, 0b10001000, 0b00110000, 0b01000000, 0b11111000;
DU8 0b01110000, 0b10001000, 0b00110000, 0b10001000, 0b01110000;
DU8 0b01010000, 0b10010000, 0b11111000, 0b00010000, 0b00010000;
DU8 0b11111000, 0b10000000, 0b11110000, 0b00001000, 0b11110000;
DU8 0b01110000, 0b10000000, 0b11110000, 0b10001000, 0b01110000;
DU8 0b11111000, 0b00001000, 0b00010000, 0b00010000, 0b00010000;
DU8 0b01110000, 0b10001000, 0b01110000, 0b10001000, 0b01110000;
DU8 0b01110000, 0b10001000, 0b01111000, 0b00001000, 0b01110000;
DU8 0b01110000, 0b10001000, 0b11111000, 0b10001000, 0b10001000;
DU8 0b11110000, 0b10001000, 0b11110000, 0b10001000, 0b11110000;
DU8 0b01110000, 0b10001000, 0b10000000, 0b10001000, 0b01110000;
DU8 0b11110000, 0b10001000, 0b10001000, 0b10001000, 0b11110000;
DU8 0b11111000, 0b10000000, 0b11110000, 0b10000000, 0b11111000;
DU8 0b11111000, 0b10000000, 0b11110000, 0b10000000, 0b10000000;
DU8 0b01111000, 0b10000000, 0b10011000, 0b10001000, 0b01110000;
DU8 0b10001000, 0b10001000, 0b11111000, 0b10001000, 0b10001000;
DU8 0b11111000, 0b00100000, 0b00100000, 0b00100000, 0b11111000;
DU8 0b01111000, 0b00010000, 0b00010000, 0b10010000, 0b01100000;
DU8 0b10010000, 0b10100000, 0b11100000, 0b10010000, 0b10001000;
DU8 0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b11111000;
DU8 0b11011000, 0b10101000, 0b10101000, 0b10101000, 0b10001000;
DU8 0b11001000, 0b10101000, 0b10101000, 0b10101000, 0b10011000;
DU8 0b01110000, 0b10001000, 0b10001000, 0b10001000, 0b01110000;
DU8 0b11110000, 0b10001000, 0b11110000, 0b10000000, 0b10000000;
DU8 0b01110000, 0b10001000, 0b10101000, 0b10010000, 0b01101000;
DU8 0b11110000, 0b10001000, 0b11110000, 0b10010000, 0b10001000;
DU8 0b01111000, 0b10000000, 0b01110000, 0b00001000, 0b11110000;
DU8 0b11111000, 0b00100000, 0b00100000, 0b00100000, 0b00100000;
DU8 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b01110000;
DU8 0b10001000, 0b10001000, 0b01010000, 0b01010000, 0b00100000;
DU8 0b10001000, 0b10101000, 0b10101000, 0b10101000, 0b01010000;
DU8 0b10001000, 0b01010000, 0b00100000, 0b01010000, 0b10001000;
DU8 0b10001000, 0b01010000, 0b00100000, 0b00100000, 0b00100000;
DU8 0b11111000, 0b00010000, 0b00100000, 0b01000000, 0b11111000;
DU8 0b00000000, 0b00000000, 0b11111000, 0b00000000, 0b00000000;
DU8 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b11111000;
DU8 0b01101000, 0b10010000, 0b00000000, 0b00000000, 0b00000000;
DU8 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00100000;
DU8 0b00001000, 0b00010000, 0b00100000, 0b01000000, 0b10000000;
DU8 0b00010000, 0b00100000, 0b01000000, 0b00100000, 0b00010000;
DU8 0b01000000, 0b00100000, 0b00010000, 0b00100000, 0b01000000;
DU8 0b01110000, 0b01000000, 0b01000000, 0b01000000, 0b01110000;
DU8 0b01110000, 0b00010000, 0b00010000, 0b00010000, 0b01110000;
DU8 0b00000000, 0b00100000, 0b00000000, 0b00100000, 0b00000000;
DU8 0b01100000, 0b10011000, 0b01110000, 0b10011000, 0b01101000;
DU8 0b00100000, 0b00100000, 0b10101000, 0b01110000, 0b00100000;
DU8 0b01010000, 0b11111000, 0b01010000, 0b11111000, 0b01010000;
DU8 0b00000000, 0b11111000, 0b00000000, 0b11111000, 0b00000000;
DU8 0b01001000, 0b10010000, 0b00000000, 0b00000000, 0b00000000;
DU8 0b10000000, 0b01000000, 0b00100000, 0b00010000, 0b00001000;
DU8 0b10101000, 0b01110000, 0b11111000, 0b01110000, 0b10101000;
DU8 0b01110000, 0b10001000, 0b00110000, 0b00000000, 0b00100000;
DU8 0b10001000, 0b00010000, 0b00100000, 0b01000000, 0b10001000;
DU8 0b00100000, 0b00100000, 0b11111000, 0b00100000, 0b00100000;
DU8 0b00000000, 0b00000000, 0b00000000, 0b00100000, 0b01000000;
DU8 0b00110000, 0b01000000, 0b01000000, 0b01000000, 0b00110000;
DU8 0b01100000, 0b00010000, 0b00010000, 0b00010000, 0b01100000;
DU8 0b01110000, 0b10011000, 0b10111000, 0b10000000, 0b01110000;
DU8 0b00100000, 0b01000000, 0b00000000, 0b00000000, 0b00000000;
DU8 0b00100000, 0b00100000, 0b00100000, 0b00000000, 0b00100000;
DU8 0b01111000, 0b10100000, 0b01110000, 0b00101000, 0b11110000;
DU8 0b00000000, 0b00100000, 0b00000000, 0b00100000, 0b01000000;
DU8 0b01000000, 0b00100000, 0b00000000, 0b00000000, 0b00000000;
DU8 0b00100000, 0b01010000, 0b00000000, 0b00000000, 0b00000000;
DU8 0b00110000, 0b01000000, 0b11000000, 0b01000000, 0b00110000;
DU8 0b01100000, 0b00010000, 0b00011000, 0b00010000, 0b01100000;
DU8 0b00100000, 0b00100000, 0b01110000, 0b01110000, 0b11111000;
DU8 0b11111000, 0b01110000, 0b01110000, 0b00100000, 0b00100000;
DU8 0b00001000, 0b00111000, 0b11111000, 0b00111000, 0b00001000;
DU8 0b10000000, 0b11100000, 0b11111000, 0b11100000, 0b10000000;
DU8 0b00100000, 0b01100000, 0b11111000, 0b01100000, 0b00100000;
DU8 0b00111000, 0b00100000, 0b00110000, 0b00001000, 0b10110000;
DU8 0b11111100, 0b10000100, 0b11111100, 0b00000000, 0b00000000;
DU8 0b00000000, 0b11111100, 0b00000000, 0b00000000, 0b00000000;
DU8 0b11111000, 0b10001000, 0b10001000, 0b10001000, 0b11111000;
DU8 0b00000000, 0b00000000, 0b00100000, 0b01010000, 0b00100000;
DU8 0b01110000, 0b01000000, 0b01000000, 0b01000000, 0b00000000;
DU8 0b00000000, 0b00010000, 0b00010000, 0b00010000, 0b01110000;
DU8 0b00000000, 0b00000000, 0b00000000, 0b01000000, 0b00100000;
DU8 0b00000000, 0b00100000, 0b01110000, 0b00100000, 0b00000000;
DU8 0b11111000, 0b00001000, 0b11110000, 0b00100000, 0b11000000;
DU8 0b00000000, 0b11111000, 0b01010000, 0b01100000, 0b01000000;
DU8 0b00000000, 0b00010000, 0b00100000, 0b11100000, 0b00100000;
DU8 0b00000000, 0b00100000, 0b11111000, 0b10001000, 0b00110000;
DU8 0b00000000, 0b00000000, 0b11111000, 0b00100000, 0b11111000;
DU8 0b00000000, 0b00010000, 0b11111000, 0b00110000, 0b11010000;
DU8 0b00000000, 0b01000000, 0b11111000, 0b01010000, 0b01000000;
DU8 0b00000000, 0b00000000, 0b11110000, 0b00010000, 0b11111000;
DU8 0b00000000, 0b11111000, 0b00001000, 0b01111000, 0b11111000;
DU8 0b00000000, 0b10101000, 0b10101000, 0b00010000, 0b01100000;
DU8 0b00000000, 0b10000000, 0b01111000, 0b00000000, 0b00000000;
DU8 0b11111000, 0b00101000, 0b00110000, 0b00100000, 0b11000000;
DU8 0b00001000, 0b00110000, 0b11100000, 0b00100000, 0b00100000;
}
#endif
Regular → Executable
+103 -29
View File
@@ -1,7 +1,23 @@
// vim: set ft=c:
extern U0 initCart(U8 *rom_filename);
#define GUI_IDLE_TIMEOUT 30
class GUIModal
{
U8 *title;
I64 x,y;
I64 w,h;
};
GUIModal *modal_about=CAlloc(sizeof(GUIModal));
modal_about->title = "ABOUT";
modal_about->x = 40;
modal_about->y = 40;
modal_about->w = 120;
modal_about->h = 60;
U32 frame_count = 0;
CDC *ms_pointer = GRRead("Pointer.GR");
@@ -9,6 +25,7 @@ DCColorChg(ms_pointer, LTPURPLE, 255);
Bool gui_focus = FALSE;
GUIModal *modal_focus = NULL;
I64 menu_focus = -1;
I64 item_clicked = -1;
I64 mouse_old_x, mouse_old_y;
@@ -16,44 +33,91 @@ I64 mouse_new_x, mouse_new_y;
I64 idle_ctr = 0;
U8 **menu_bar=CAlloc(sizeof(U64)*6);
menu_bar[0] = "Game";
menu_bar[1] = "Config";
menu_bar[2] = "Cheats";
menu_bar[3] = "Misc";
menu_bar[4] = "Help";
menu_bar[0] = "GAME";
menu_bar[1] = "CONFIG";
menu_bar[2] = "CHEATS";
menu_bar[3] = "MISC";
menu_bar[4] = "HELP";
U8 **menu_items=NULL;
U8 **menu_game=CAlloc(sizeof(U64)*10);
menu_game[0] = "Load ROM";
menu_game[1] = "Reset";
menu_game[0] = "LOAD ROM";
menu_game[1] = "RESET";
menu_game[2] = "";
menu_game[3] = "Load State";
menu_game[4] = "Save State";
menu_game[3] = "LOAD STATE";
menu_game[4] = "SAVE STATE";
menu_game[5] = "";
menu_game[6] = "Exit";
menu_game[6] = "EXIT";
U8 **menu_config=CAlloc(sizeof(U64)*10);
menu_config[0] = "Joypad 1";
menu_config[1] = "Joypad 2";
menu_config[0] = "JOYPAD 1";
menu_config[1] = "JOYPAD 2";
U8 **menu_cheats=CAlloc(sizeof(U64)*10);
menu_cheats[0] = "Add";
menu_cheats[1] = "View/Edit";
menu_cheats[2] = "Toggle";
menu_cheats[0] = "ADD";
menu_cheats[1] = "VIEW/EDIT";
menu_cheats[2] = "TOGGLE";
U8 **menu_misc=CAlloc(sizeof(U64)*10);
menu_misc[0] = "Scale2Fit";
menu_misc[0] = "SCALE2FIT";
U8 **menu_help=CAlloc(sizeof(U64)*10);
menu_help[0] = "About";
menu_help[0] = "ABOUT";
U0 GrPrintShadow(CDC *dc, I64 x, I64 y, I64 c1=15, I64 c2=0, U8 *str)
I64 GUIFontChr(I64 ch)
{
I64 i=0;
while (gui_font_str[i])
{
if (gui_font_str[i]==ch)
{
return 5*i;
}
i++;
}
return 0;
}
U0 GUIPrint(CDC *dc, I64 x, I64 y, U8 *str)
{
I64 i,j,k=0;
U8 *font_ptr;
while (str[k])
{
font_ptr = GUI_FONT_TBL + GUIFontChr(str[k]);
for (j=0; j<5; j++)
{
for (i=0; i<8; i++)
{
if (font_ptr[j] & 1 << i)
{
GrPlot(dc, x+(7-i), y+j);
}
}
}
k++;
x+=8;
}
}
U0 GUIPrintShadow(CDC *dc, I64 x, I64 y, I64 c1=15, I64 c2=0, U8 *str)
{
dc->color=c2;
GrPrint(dc, x+1, y+1, str);
GUIPrint(dc, x+1, y+1, str);
dc->color=c1;
GrPrint(dc, x, y, str);
GUIPrint(dc, x, y, str);
}
U0 GUIDrawModal()
{
GUIModal *m=modal_focus;
if (m)
{
//testing
TG_Canvas->color=12;
GrRect(TG_Canvas,m->x,m->y,m->w,m->h);
}
}
U0 GUIDrawMenuHeader()
@@ -68,18 +132,18 @@ U0 GUIDrawMenuHeader()
{
if (i+1==menu_focus)
{
GrPrintShadow(TG_Canvas, (64*i) - 64, 2, YELLOW, 0, menu_bar[i++]);
GUIPrintShadow(TG_Canvas, (64*i) - 64, 2, YELLOW, 0, menu_bar[i++]);
}
else
{
if (mouse_new_x > (64*i+1) && mouse_new_x < (64*i+1)+64 &&
mouse_new_y > -1 && mouse_new_y < 12)
{
GrPrintShadow(TG_Canvas, (64*i) - 64, 2, CYAN, 0, menu_bar[i++]);
GUIPrintShadow(TG_Canvas, (64*i) - 64, 2, CYAN, 0, menu_bar[i++]);
}
else
{
GrPrintShadow(TG_Canvas, (64*i) - 64, 2, 15, 0, menu_bar[i++]);
GUIPrintShadow(TG_Canvas, (64*i) - 64, 2, 15, 0, menu_bar[i++]);
}
}
}
@@ -136,11 +200,11 @@ U0 GUIDrawMenuFocused()
if (mouse_new_x > (64*menu_focus)-64 && mouse_new_x < (64*menu_focus) + 32 &&
mouse_new_y > 4+((i+1)*10) && mouse_new_y < 4+((i+1)*10)+10)
{
GrPrintShadow(TG_Canvas, (64*menu_focus)-62, 4+(i*10), CYAN, 0, menu_items[i++]);
GUIPrintShadow(TG_Canvas, (64*menu_focus)-62, 4+(i*10), CYAN, 0, menu_items[i++]);
}
else
{
GrPrintShadow(TG_Canvas, (64*menu_focus)-62, 4+(i*10), 15, 0, menu_items[i++]);
GUIPrintShadow(TG_Canvas, (64*menu_focus)-62, 4+(i*10), 15, 0, menu_items[i++]);
}
}
}
@@ -201,17 +265,23 @@ U0 UpdateGUI()
case 0:
switch (menu_focus)
{
case 1:
modal_focus = NULL;
break;
case 4:
fit_screen = !fit_screen;
if (fit_screen)
{
menu_misc[0] = "Scale2Fit +";
menu_misc[0] = "SCALE2FIT +";
}
else
{
menu_misc[0] = "Scale2Fit";
menu_misc[0] = "SCALE2FIT";
}
break;
case 5:
modal_focus = modal_about;
break;
default:
break;
}
@@ -245,12 +315,12 @@ U0 UpdateGUI()
}
}
if (menu_focus<1)
if (menu_focus<1 && !modal_focus)
{
gui_focus = FALSE;
}
if (menu_focus>0)
if (menu_focus>0 || modal_focus)
{
paused = TRUE;
}
@@ -274,6 +344,10 @@ U0 UpdateGUI()
if (!idle_ctr || (frame_count < idle_ctr + GUI_IDLE_TIMEOUT) || gui_focus)
{ //Draw mouse pointer
if (modal_focus)
{
GUIDrawModal;
}
GUIDrawMenuHeader;
GUIDrawMenuFocused;
GUIDrawPointer;
Executable
+32
View File
@@ -0,0 +1,32 @@
// vim: set ft=c:
#define GP_TX_PORT 0x0378
#define GP_RX_PORT 0x0379
#define GP_SNES_DELAY 0
#define GP_SNES_POWER 0xFC
#define GP_SNES_CLOCK 0x01
#define GP_SNES_LATCH 0x02
U8 gp_data[16];
MemSet(&gp_data, 16, 0);
I64 gp_ctr;
U0 updateGamepad()
{
return;
OutU8(GP_TX_PORT,GP_SNES_POWER|GP_SNES_CLOCK|GP_SNES_LATCH);
Sleep(GP_SNES_DELAY*2);
OutU8(GP_TX_PORT,GP_SNES_POWER|GP_SNES_CLOCK);
gp_ctr=0;
while (gp_ctr<12)
{
Sleep(GP_SNES_DELAY);
OutU8(GP_TX_PORT,GP_SNES_POWER);
gp_data[gp_ctr]=InU8(GP_RX_PORT)^0x7F;
Sleep(GP_SNES_DELAY);
OutU8(GP_TX_PORT,GP_SNES_POWER|GP_SNES_CLOCK);
gp_ctr++;
}
}
+972
View File
@@ -0,0 +1,972 @@
U8 *Mem2MegAlloc(I64 *_pages2Meg,CBlkPool *bp=NULL)
{/*Alloc 2Meg pages from BlkPool. Don't link to task.
(Linking to a task means they will be freed when the task dies.)
It might give you more than you asked for
so a ptr to a page count is passed.
Return: NULL if out of memory.
*/
I64 i,j,*pte,num=*_pages2Meg;
CMemBlk *res=NULL,*m,*m1;
if (!bp) bp=sys_code_bp;
PUSHFD
CLI
while (LBts(&bp->locked_flags,BPlf_LOCKED))
PAUSE
num<<=21-MEM_PAG_BITS;
m=&bp->mem_free_2meg_lst;
while (TRUE) {
if (!(res=m->next))
break;
if (res->pags<num)
m=res;
else {
if (res->pags==num) {
m->next=res->next;
goto am_done;
} else {
res->pags-=num;
res(U8 *)+=res->pags<<MEM_PAG_BITS;
res->pags=num;
goto am_done;
}
}
}
m=&bp->mem_free_lst;
while (TRUE) {
if (!(res=m->next)) {
num=0;
res=NULL; //Out of memory
goto am_done;
}
if (res->pags<num)
m=res;
else {
if (res->pags==num) {
if (res(U8 *)&0x1FFFFF)
m=res;
else {
m->next=res->next;
goto am_done;
}
} else {
if (i=(res(U8 *)&0x1FFFFF)>>MEM_PAG_BITS) {
j=1<<(21-MEM_PAG_BITS)-i;
if (res->pags<num+j)
m=res;
else if (res->pags==num+j) {
res->pags-=num;
res(U8 *)+=res->pags<<MEM_PAG_BITS;
res->pags=num;
goto am_done;
} else {
m1=res;
res(U8 *)+=j<<MEM_PAG_BITS;
res->pags=num;
m=res(U8 *)+num<<MEM_PAG_BITS;
m->pags=m1->pags-num-j;
m1->pags=j;
m->next=m1->next;
m1->next=m;
m->mb_signature=MBS_UNUSED_SIGNATURE_VAL;
goto am_done;
}
} else {
m=m->next=res(U8 *)+num<<MEM_PAG_BITS;
m->next=res->next;
m->pags=res->pags-num;
m->mb_signature=MBS_UNUSED_SIGNATURE_VAL;
res->pags=num;
goto am_done;
}
}
}
}
am_done:
i=num<<MEM_PAG_BITS;
bp->used_u8s+=i;
num>>=21-MEM_PAG_BITS;
*_pages2Meg=num;
m=res;
m1=m(U8 *)+i;
while (m<m1) {
pte=MemPageTable(m);
*pte &= ~0x18;
InvlPg(m);
m(U8 *)+=0x200000;
}
LBtr(&bp->locked_flags,BPlf_LOCKED);
POPFD
return res;
}
U8 *Mem2MegUncachedAlloc(I64 *_pages2Meg,CBlkPool *bp=NULL)
{/*Alloc 2Meg pages from BlkPool. Don't link to task.
(Linking to a task means they will be freed when the task dies.)
It will be marked uncached. It might give you more than you asked for
so a ptr to a page count is passed.
Return: NULL if out of memory.
*/
CMemBlk *res,*m,*m1;
I64 num=*_pages2Meg,*pte;
if (res=Mem2MegAlloc(_pages2Meg,bp)) {
num=*_pages2Meg;
m=res;
m1=m(U8 *)+num<<21;
while (m<m1) {
pte=MemPageTable(m);
*pte= *pte& ~0x18 |0x10;
InvlPg(m);
m(U8 *)+=0x200000;
}
}
return res;
}
CHeapCtrl *HeapCtrlBPInit(CBlkPool *bp,I64 pags)
{//Make mem chunk into HeapCtrl and BlkPool.
I64 num;
CMemBlk *m;
CHeapCtrl *hc;
MemSet(bp,0,sizeof(CBlkPool)+sizeof(CHeapCtrl));
hc=HeapCtrlInit(bp(U8 *)+sizeof(CBlkPool),,bp);
m=(bp(U8 *)+sizeof(CBlkPool)+sizeof(CHeapCtrl)+MEM_PAG_SIZE-1)&
~(MEM_PAG_SIZE-1);
num=(bp(U8 *)+pags<<MEM_PAG_BITS-m(U8 *))>>MEM_PAG_BITS;
bp->alloced_u8s=(pags-num)<<MEM_PAG_BITS;
BlkPoolAdd(bp,m,num);
return hc;
}
class CSndWaveCtrl
{
I64 sample_rate,sample_bits,channels;
F64 freq_multiplier,amp_multiplier;
F64 phase,last_y,last_dydt,next_y;
};
#define WF_NULL 0
#define WF_SQUARE 1
#define WF_SINE 2
#define WF_TRI 3
#define WF_SAWTOOTH 4
#define WF_NOISE 5
#define WF_WAVEFORMS_NUM 6
//snd devs
#define SD_PC_SPEAKER 0
#define SD_HD_AUDIO 1
#define SND_SAMPLE_RATE 48000
#define SND_SAMPLE_BITS 16
#define SND_OCHANNELS 2
#define SND_ICHANNELS 2
#define SND_OUT_CONTAINER U32
#define SND_IN_CONTAINER I16
#define SND_BUF_LEN 0x400
#define SND_BUF_TIME_mS (SND_BUF_LEN/SND_OCHANNELS*1000.0/\
SND_SAMPLE_RATE)
F64 snd_freq=0;
I64 snd_dev=SD_PC_SPEAKER;
Bool snd_record=FALSE;
F64 snd_vol=0.1;
U0 (*fp_snd)(F64 freq,I64 waveform,F64 amp)=NULL;
U0 (*fp_snd_record)(F64 freq,I64 waveform,F64 amp)=NULL;
U0 (*fp_snd_fill_buf)(SND_OUT_CONTAINER *buf,I64 buf_num)=NULL;
U0 (*fp_snd_copy_buf)(SND_IN_CONTAINER *buf,I64 buf_num)=NULL;
I64 snd_obuf_num,snd_ibuf_num;
#define Sf_FILLING_OUT 0
I64 snd_flags;
#define HD_1_CHAN 0
#define HD_2_CHAN 1
#define HD_3_CHAN 2
#define HD_4_CHAN 3
#define HD_8_BIT 0
#define HD_16_BIT 1
#define HD_20_BIT 2
#define HD_24_BIT 3
#define HD_32_BIT 4
#define HD_48kHz 0
#define HD_DFT_OUT_FMT (HD_2_CHAN+HD_16_BIT<<4+HD_48kHz<<8)
#define HD_DFT_IN_FMT (HD_2_CHAN+HD_16_BIT<<4+HD_48kHz<<8)
#define HD_POS_BUF_MULTIPLES 0x1000
#define HD_CORB_ENTRIES 256
#define HD_RIRB_ENTRIES 256
#define HD_BDL_ENTRIES 256
#define HD_GCTL 0x08
#define HD_STATESTS 0x0E
#define HD_GSTS 0x10
#define HD_CORBLBASE 0x40
#define HD_CORBUBASE 0x44
#define HD_CORBWP 0x48
#define HD_CORBRP 0x4A
#define HD_CORBCTL 0x4C
#define HD_CORBST 0x4D
#define HD_RIRBLBASE 0x50
#define HD_RIRBUBASE 0x54
#define HD_RIRBWP 0x58
#define HD_RIRBCTL 0x5C
#define HD_RIRBSTS 0x5D
#define STRCTL 0x00
#define STRSTS 0x03
#define STRLPIB 0x04
#define STRCBL 0x08
#define STRLVI 0x0C
#define STRFIFOW 0x0E
#define STRFIFOS 0x10
#define STRFMT 0x12
#define STRBDPL 0x18
#define STRBDPU 0x1C
#define ISTR0 0x080
#define ISTR1 0x0A0
#define ISTR2 0x0C0
#define ISTR3 0x0E0
#define OSTR0 0x100
#define OSTR1 0x120
#define OSTR2 0x140
#define OSTR3 0x160
#define VERB_GET_PARAM 0xF0000
#define VERB_CONNECT_SEL_GET 0xF0100
#define VERB_CONNECT_SEL_SET 0x70100
#define VERB_GET_CONNECT_LST 0xF0200
#define VERB_PROCESS_STATE_GET 0xF0300
#define VERB_PROCESS_STATE_SET 0x70300
#define VERB_COEFF_IDX_GET 0xD0000
#define VERB_COEFF_IDX_SET 0x50000
#define VERB_PROCESS_COEFF_GET 0xC0000
#define VERB_PROCESS_COEFF_SET 0x40000
#define VERB_AMPLIFIER_GAIN_GET 0xB0000
#define VERB_AMPLIFIER_GAIN_SET 0x30000
#define VERB_STREAM_FMT_GET 0xA0000
#define VERB_STREAM_FMT_SET 0x20000
#define VERB_DIGIT_CONVERT1_GET 0xF0D00
#define VERB_DIGIT_CONVERT1_SET 0x70D00
#define VERB_DIGIT_CONVERT2_GET 0xF0D00
#define VERB_DIGIT_CONVERT2_SET 0x70E00
#define VERB_POWER_STATE_GET 0xF0500
#define VERB_POWER_STATE_SET 0x70500
#define VERB_CHAN_STREAM_ID_GET 0xF0600
#define VERB_CHAN_STREAM_ID_SET 0x70600
#define VERB_SDI_SEL_GET 0xF0400
#define VERB_SDI_SEL_SET 0x70400
#define VERB_PIN_WIDGET_CTL_GET 0xF0700
#define VERB_PIN_WIDGET_CTL_SET 0x70700
#define VERB_UNSOL_ENABLE_GET 0xF0800
#define VERB_UNSOL_ENABLE_SET 0x70800
#define VERB_PIN_SENSE_GET 0xF0900
#define VERB_PIN_SENSE_SET 0x70900
#define VERB_EAPDBTL_ENABLE_GET 0xF0C00
#define VERB_EAPDBTL_ENABLE_SET 0x70C00
#define VERB_BEEP_CTL_GET 0xF0A00
#define VERB_BEEP_CTL_SET 0x70A00
#define VERB_GPI_CTRL0_GET 0xF1000
#define VERB_GPI_CTRL0_SET 0x71000
#define VERB_GPI_CTRL1_GET 0xF1100
#define VERB_GPI_CTRL1_SET 0x71100
#define VERB_GPI_CTRL2_GET 0xF1200
#define VERB_GPI_CTRL2_SET 0x71200
#define VERB_GPI_CTRL3_GET 0xF1300
#define VERB_GPI_CTRL3_SET 0x71300
#define VERB_GPI_CTRL4_GET 0xF1400
#define VERB_GPI_CTRL4_SET 0x71400
#define VERB_GPI_CTRL5_GET 0xF1500
#define VERB_GPI_CTRL5_SET 0x71500
#define VERB_GPI_CTRL6_GET 0xF1600
#define VERB_GPI_CTRL6_SET 0x71600
#define VERB_GPI_CTRL7_GET 0xF1700
#define VERB_GPI_CTRL7_SET 0x71700
#define VERB_GPI_CTRL8_GET 0xF1800
#define VERB_GPI_CTRL8_SET 0x71800
#define VERB_GPI_CTRL9_GET 0xF1900
#define VERB_GPI_CTRL9_SET 0x71900
#define VERB_GPI_CTRLA_GET 0xF1A00
#define VERB_GPI_CTRLA_SET 0x71A00
#define VERB_VOL_CTL_GET 0xF0F00
#define VERB_VOL_CTL_SET 0x70F00
#define VERB_SUB_SYS_ID0_GET 0xF2000
#define VERB_SUB_SYS_ID0_SET 0x72000
#define VERB_SUB_SYS_ID1_GET 0xF2000
#define VERB_SUB_SYS_ID1_SET 0x72100
#define VERB_SUB_SYS_ID2_GET 0xF2000
#define VERB_SUB_SYS_ID2_SET 0x72200
#define VERB_SUB_SYS_ID3_GET 0xF2000
#define VERB_SUB_SYS_ID3_SET 0x72300
#define VERB_CFG_DFT0_GET 0xF1C00
#define VERB_CFG_DFT0_SET 0x71C00
#define VERB_CFG_DFT1_GET 0xF1C00
#define VERB_CFG_DFT1_SET 0x71D00
#define VERB_CFG_DFT2_GET 0xF1C00
#define VERB_CFG_DFT2_SET 0x71E00
#define VERB_CFG_DFT3_GET 0xF1C00
#define VERB_CFG_DFT3_SET 0x71F00
#define VERB_STRIPE_CTL_GET 0xF2400
#define VERB_STRIPE_CTL_SET 0x72400
#define VERB_RST 0x7FF00
//Parameters
#define P_VENDOR_ID 0x00
#define P_REVISION_ID 0x02
#define P_SUBNODE_CNT 0x04
#define P_FUN_GRP_TYPE 0x05
#define P_AUDIO_FUN_CAP 0x08
#define P_AUDIO_WIDGET_CAP 0x09
#define P_SAMPLE_SIZE_RATE_CAP 0x0A
#define P_STREAM_FMTS 0x0B
#define P_PIN_CAP 0x0C
#define P_INPUT_AMP_CAP 0x0D
#define P_OUTPUT_AMP_CAP 0x12
#define P_CONNECT_LST_LEN 0x0E
#define P_POWER_STATES 0x0F
#define P_PROCESSING_CAP 0x10
#define P_GPIO_CNT 0x11
#define P_VOL_KNOB_CAP 0x13
//Function Group Types
//00 reserved
#define FGT_AUDIO 1
#define FGT_VENDOR_MODEM 2
//03-7F reserved
//80-FF vendor function group
//Audio Widget Types
#define AWT_OUTPUT 0x0
#define AWT_INPUT 0x1
#define AWT_MIXER 0x2
#define AWT_SELECTOR 0x3
#define AWT_PIN_COMPLEX 0x4
#define AWT_POWER_WIDGET 0x5
#define AWT_VOL_KNOB_WIDGET 0x6
#define AWT_BEEP_GEN_WIDGET 0x7
#define AWT_VENDOR 0xF
#define AWT_NODE 0x10
DefineLstLoad("ST_AUDIO_WIDGET_TYPES",
"Output\0Input\0Mixer\0Sellector\0Pin Complex\0"
"Power Widget\0Vol Knob\0Beep Gen\0\0\0\0\0\0\0\0Vendor\0Node\0");
class CHDBufDesc
{
I32 *buf;
U32 len;
U32 ctrl;
};
#define HD_TONES 8
class CHDAudioCtrl
{
U8 *bar;
CBlkPool *bp;
CHeapCtrl *hc;
I64 cad;
U32 *corb;
I64 *rirb;
CHDBufDesc *ostr0_bdl,*istr0_bdl;
SND_OUT_CONTAINER *ostr0_buf[2],*o_tmp_buf;
SND_IN_CONTAINER *istr0_buf[2];
CTask *task;
I64 waveform;
F64 freq,amp;
CSndWaveCtrl *tone_swcs[HD_TONES];
U8 rirb_rp,corb_wp;
Bool audio_task_started,in_running,out_running;
} hda;
MemSet(&hda,0,sizeof(CHDAudioCtrl));
F64 SinPhaseCont(F64 last_y,F64 last_dydt,
F64 current_amp,F64 phase_offset)
{//Next sample of sin waveform.
F64 phase;
phase=last_y/current_amp;
if (phase>1.0) phase=1.0;
if (phase<-1.0) phase=-1.0;
if (last_dydt<0)
phase=pi-ASin(phase);
else
phase=ASin(phase);
return phase-phase_offset;
}
public CSndWaveCtrl *SndWaveCtrlNew(I64 sample_rate=8000,I64 sample_bits=24,
I64 channels=2,CTask *mem_task=NULL)
{//MAlloc ctrl struct for generating waveforms.
CSndWaveCtrl *swc=CAlloc(sizeof(CSndWaveCtrl),mem_task);
swc->freq_multiplier=1.0;
swc->amp_multiplier=1.0;
swc->sample_rate=sample_rate;
swc->sample_bits=sample_bits;
swc->channels=channels;
swc->last_dydt=1.0;
return swc;
}
public U0 SndWaveCtrlDel(CSndWaveCtrl *swc)
{//Free waveform ctrl.
Free(swc);
}
public U0 SndWaveAddBuf(CSndWaveCtrl *swc,U8 *buf,I64 num_samples,
F64 _freq,I64 _waveform=WF_SQUARE,F64 _amp=1.0,F64 _left=1.0, F64 _right=1.0)
{//Add waveform to buffer.
//num_samples is multiplied by channels to get buf_len.
//left,right range from 0.0-1.0
//Supports 16,24 and 32 bits
I64 reg i,reg j,reg k;
F64 a,f,amp,reg phase;
if (!swc) return;
_freq*=swc->freq_multiplier;
_amp*=swc->amp_multiplier;
if (!_freq||!_amp) {
swc->last_y=swc->phase=0;
swc->last_dydt=1.0;
} else {
phase=swc->phase;
i=0;
amp=Min(I32_MAX,I32_MAX*_amp);
f=2*pi/swc->sample_rate*_freq;
switch (_waveform) {
case WF_NOISE:
a=2.0/pi*amp;
break;
case WF_SAWTOOTH:
a=amp/pi;
break;
case WF_SINE:
phase=SinPhaseCont(swc->last_y,swc->last_dydt,amp,0.0);
break;
}
while (phase<0)
phase+=2*pi;
while (phase>=2*pi)
phase-=2*pi;
num_samples*=swc->channels;
while (i<num_samples) {
switch (_waveform) {
case WF_SQUARE:
if (phase>=pi)
j=-amp;
else
j=amp;
break;
case WF_SINE:
j=amp*Sin(phase);
break;
case WF_TRI:
if (phase>=pi) {
swc->last_y=swc->next_y;
swc->next_y=-amp*Sign(swc->last_y)+.00001;
phase-=pi;
}
j=(swc->last_y*(pi-phase)+swc->next_y*phase)/pi;
break;
case WF_SAWTOOTH:
j=a*(phase-pi);
break;
case WF_NOISE:
if (phase<pi) {
if (phase<f) {
swc->last_y=swc->next_y;
swc->next_y=a*RandI16/U16_MAX;
}
j=swc->last_y*(pi-phase)+swc->next_y*phase;
} else {
if (phase-pi<f) {
swc->last_y=swc->next_y;
swc->next_y=a*RandI16/U16_MAX;
}
j=swc->last_y*(2.0*pi-phase)+swc->next_y*(phase-pi);
}
break;
}
//left channel
k=j*_left;
if (swc->sample_bits==16) {
k>>=16;
buf(I16 *)[i++]+=k;
} else {
if (swc->sample_bits==24)
k&=0xFFFFFF00;
buf(I32 *)[i++]+=k;
}
//right channel
if (swc->channels==2) {
k=j*_right;
if (swc->sample_bits==16) {
k>>=16;
buf(I16 *)[i++]+=k;
} else {
if (swc->sample_bits==24)
k&=0xFFFFFF00;
buf(I32 *)[i++]+=k;
}
}
phase+=f;
while (phase>=2*pi)
phase-=2*pi;
}
if (_waveform==WF_SINE) {
swc->last_y=amp*Sin(phase);
swc->last_dydt=Cos(phase);
}
swc->phase=phase;
}
}
U0 HDSyncCORB()
{
U16 *wp,*rp;
wp =hda.bar+HD_CORBWP;
*wp=hda.corb_wp;
rp =hda.bar+HD_CORBRP;
while (*rp&255!=hda.corb_wp)
Yield;
}
U0 HDWriteCORB(I64 cad,I64 nid,U32 val)
{
val|=cad<<28+nid<<20;
hda.corb[++hda.corb_wp]=val;
}
I64 HDSyncRIRB()
{
U16 *_w;
I64 wp,res=0;
_w=hda.bar+HD_RIRBWP;
wp=*_w;
while (hda.rirb_rp!=wp)
res=hda.rirb[++hda.rirb_rp];
return res;
}
I64 HDReadRIRB()
{
U16 *_w;
I64 wp,res=0;
_w=hda.bar+HD_RIRBWP;
do {
Yield;
wp=*_w;
} while (wp==hda.rirb_rp);
res=hda.rirb[++hda.rirb_rp];
return res;
}
I64 HDWriteCORBSync(I64 cad,I64 nid,U32 val)
{
HDSyncCORB;
HDSyncRIRB;
HDWriteCORB(cad,nid,val);
HDSyncCORB;
return HDReadRIRB;
}
Bool HDTestCORBSync(I64 cad,I64 nid,U32 val)
{ //Checks for a response
U16 *_w;
I64 wp;
HDSyncCORB;
HDSyncRIRB;
HDWriteCORB(cad,nid,val);
HDSyncCORB;
Sleep(1);
_w=hda.bar+HD_RIRBWP;
wp=*_w;
if (wp==hda.rirb_rp)
return FALSE;
HDReadRIRB;
return TRUE;
}
U0 HDTraverse(I64 cad,I64 nid)
{
I64 i,len,aud_cap,type;
HDWriteCORBSync(cad,nid,VERB_POWER_STATE_SET+0x00); //0 is on
HDWriteCORBSync(cad,nid,VERB_EAPDBTL_ENABLE_SET+0x02);
HDWriteCORBSync(cad,nid,VERB_PROCESS_STATE_SET+0x02);
HDWriteCORBSync(cad,nid,VERB_CONNECT_SEL_SET+0x00);
aud_cap=HDWriteCORBSync(cad,nid,VERB_GET_PARAM+P_SUBNODE_CNT);
if (aud_cap.u16[0]) {
for (i=aud_cap.u16[1];i<aud_cap.u16[1]+aud_cap.u16[0];i++)
HDTraverse(cad,i);
} else {
aud_cap=HDWriteCORBSync(cad,nid,VERB_GET_PARAM+P_AUDIO_WIDGET_CAP);
type=aud_cap>>20&15;
if (Bt(&aud_cap,8))
len=HDWriteCORBSync(cad,nid,VERB_GET_PARAM+P_CONNECT_LST_LEN)&127;
else
len=0;
HDWriteCORBSync(cad,nid,VERB_AMPLIFIER_GAIN_SET+0xF07F); //set I/O amp #0
for (i=1;i<len;i++)
//Set IN amps to mute
HDWriteCORBSync(cad,nid,VERB_AMPLIFIER_GAIN_SET+0x7080+i<<8);
switch (type) {
case AWT_OUTPUT:
if (FALSE) //if disabled
HDWriteCORBSync(cad,nid,VERB_CHAN_STREAM_ID_SET+0x00);
else
HDWriteCORBSync(cad,nid,VERB_CHAN_STREAM_ID_SET+0x10);
HDWriteCORBSync(cad,nid,VERB_STREAM_FMT_SET+HD_DFT_OUT_FMT);
HDWriteCORBSync(cad,nid,VERB_PROCESS_STATE_SET+0x01);
break;
case AWT_INPUT:
if (TRUE) //if disabled
HDWriteCORBSync(cad,nid,VERB_CHAN_STREAM_ID_SET+0x00);
else
HDWriteCORBSync(cad,nid,VERB_CHAN_STREAM_ID_SET+0x20);
HDWriteCORBSync(cad,nid,VERB_STREAM_FMT_SET+HD_DFT_IN_FMT);
HDWriteCORBSync(cad,nid,VERB_PROCESS_STATE_SET+0x01);
break;
case AWT_PIN_COMPLEX:
HDWriteCORBSync(cad,nid,VERB_PIN_WIDGET_CTL_SET+0xE2);
break;
}
}
}
U0 HDRun(Bool in,Bool out)
{
U32 *_d;
if (hda.bar) {
if (out) {
_d=hda.bar+OSTR0+STRCTL;
*_d=0x100002;
hda.out_running=TRUE;
}
if (in) {
_d=hda.bar+ISTR0+STRCTL;
*_d=0x200002;
hda.in_running=TRUE;
}
}
}
U0 HDStop(Bool in,Bool out)
{
U32 *_d;
if (hda.bar) {
if (out) {
_d=hda.bar+OSTR0+STRCTL;
*_d=0;
hda.out_running=FALSE;
}
if (in) {
_d=hda.bar+ISTR0+STRCTL;
*_d=0;
hda.in_running=FALSE;
}
}
}
U0 HDSnd(F64 freq,I64 waveform=WF_SQUARE,F64 amp=1.0)
{
hda.waveform=waveform;
hda.amp=amp;
hda.freq=freq;
}
U0 HDFillBuf(SND_OUT_CONTAINER *buf,I64)
{
I64 i,size=SND_BUF_LEN*sizeof(SND_OUT_CONTAINER);
if (!hda.o_tmp_buf)
hda.o_tmp_buf=AMAlloc(size);
MemSet(hda.o_tmp_buf,0,size);
for (i=0;i<HD_TONES;i++)
SndWaveAddBuf(hda.tone_swcs[i],hda.o_tmp_buf,
SND_BUF_LEN/SND_OCHANNELS,hda.freq,
hda.waveform,snd_vol*hda.amp);
MemCpy(buf,hda.o_tmp_buf,size);
}
U0 HDAudioTaskEndCB()
{
I64 i;
HDStop(FALSE,TRUE);
fp_snd=NULL;
for (i=0;i<HD_TONES;i++) {
SndWaveCtrlDel(hda.tone_swcs[i]);
hda.tone_swcs[i]=NULL;
}
Exit;
}
public U0 HDTonesInit()
{
I64 i;
if (hda.bar) {
for (i=0;i<HD_TONES;i++) {
hda.tone_swcs[i]->freq_multiplier=1.0;
hda.tone_swcs[i]->amp_multiplier=0;
}
hda.tone_swcs[0]->amp_multiplier=1.0;
}
}
U0 HDAudioTask(I64)
{
//I didn't feel like messing around with PCI interrupts
//so this task polls every millisecond to know when to
//switch buffers.
I64 i,next_obuf_trigger=SND_BUF_LEN*sizeof(SND_OUT_CONTAINER)/2,
obuf_rollover=0,
next_ibuf_trigger=SND_BUF_LEN*sizeof(SND_IN_CONTAINER),
ibuf_rollover=0;
U32 *pos_in_obuf=hda.bar+OSTR0+STRLPIB,
*pos_in_ibuf=hda.bar+ISTR0+STRLPIB;
Fs->task_end_cb=&HDAudioTaskEndCB;
for (i=0;i<HD_TONES;i++)
hda.tone_swcs[i]=SndWaveCtrlNew;
HDTonesInit;
hda.freq=0;
Snd;
fp_snd=&HDSnd;
fp_snd_fill_buf=&HDFillBuf;
fp_snd_copy_buf=NULL;
snd_obuf_num=1;
snd_ibuf_num=1;
HDRun(FALSE,TRUE);
hda.audio_task_started=TRUE; //This flag is probably not necessary
while (TRUE) {
if (next_obuf_trigger-obuf_rollover<=*pos_in_obuf<
next_obuf_trigger-obuf_rollover+
(HD_POS_BUF_MULTIPLES-1)*SND_BUF_LEN*sizeof(SND_OUT_CONTAINER)) {
next_obuf_trigger+=SND_BUF_LEN*sizeof(SND_OUT_CONTAINER);
if (next_obuf_trigger-obuf_rollover>=
HD_POS_BUF_MULTIPLES*SND_BUF_LEN*sizeof(SND_OUT_CONTAINER))
obuf_rollover+=HD_POS_BUF_MULTIPLES*SND_BUF_LEN
*sizeof(SND_OUT_CONTAINER);
if (fp_snd_fill_buf) {
LBts(&snd_flags,Sf_FILLING_OUT);
(*fp_snd_fill_buf)(hda.ostr0_buf[snd_obuf_num&1],snd_obuf_num);
if (IsMute)
MemSet(hda.ostr0_buf[snd_obuf_num&1],0,
SND_BUF_LEN*sizeof(SND_OUT_CONTAINER));
LBtr(&snd_flags,Sf_FILLING_OUT);
}
snd_obuf_num++;
}
if (next_ibuf_trigger-ibuf_rollover<=*pos_in_ibuf<
next_ibuf_trigger-ibuf_rollover+(HD_POS_BUF_MULTIPLES-1)
*SND_BUF_LEN*sizeof(SND_IN_CONTAINER)) {
next_ibuf_trigger+=SND_BUF_LEN*sizeof(SND_IN_CONTAINER);
if (next_ibuf_trigger-ibuf_rollover>=
HD_POS_BUF_MULTIPLES*SND_BUF_LEN*sizeof(SND_IN_CONTAINER))
ibuf_rollover+=HD_POS_BUF_MULTIPLES*SND_BUF_LEN
 *sizeof(SND_IN_CONTAINER);
if (fp_snd_copy_buf)
(*fp_snd_copy_buf)(hda.istr0_buf[snd_obuf_num&1],snd_ibuf_num);
snd_ibuf_num++;
}
Sleep(1);
}
}
U0 HDRst()
{
U32 d,*_d;
HDStop(TRUE,TRUE);
_d=hda.bar+HD_GCTL;
*_d=0; //rst
do {
Sleep(1);
d=*_d;
} while (d & 1);
*_d=1;
do {
Sleep(1);
d=*_d;
} while (!(d & 1));
Sleep(1);
}
public U0 HDAudioEnd(Bool rst=TRUE)
{
snd_dev=SD_PC_SPEAKER;
if (hda.bar) {
Kill(hda.task);
hda.task=NULL;
if (rst)
HDRst;
Free(hda.corb);
Free(hda.rirb);
Free(hda.o_tmp_buf);
Free(hda.ostr0_buf[0]);
Free(hda.ostr0_buf[1]);
Free(hda.istr0_buf[0]);
Free(hda.istr0_buf[1]);
Free(hda.ostr0_bdl);
Free(hda.istr0_bdl);
Mem32DevFree(hda.bar);
hda.bar=NULL;
}
}
U0 HDAudioUncachedInit()
{
I64 shared_blks=1;
hda.bp=Mem2MegUncachedAlloc(&shared_blks);
hda.hc=HeapCtrlBPInit(hda.bp,shared_blks<<12);
}
public Bool HDAudioInit(I64 hd_bus,I64 hd_dev,I64 hd_fun)
{
I64 i;
U32 *_d;
U16 w,*_w;
U8 *_b;
if (hda.bar)
HDAudioEnd;
else
HDAudioUncachedInit;
if (PCIReadU16(hd_bus,hd_dev,hd_fun,0)==0x8086 &&
(hda.bar=PCIReadU32(hd_bus,hd_dev,hd_fun,0x10) & ~(0x1F))) {
PCIWriteU16(hd_bus,hd_dev,hd_fun,0x04,
PCIReadU16(hd_bus,hd_dev,hd_fun,0x04)|0x406);
HDRst;
hda.corb=CAllocAligned(HD_CORB_ENTRIES*sizeof(U32),128,hda.hc);
_d=hda.bar+HD_CORBLBASE;
*_d=hda.corb(I64).u32[0];
_d=hda.bar+HD_CORBUBASE;
*_d=hda.corb(I64).u32[1];
hda.rirb=CAllocAligned(HD_RIRB_ENTRIES*sizeof(I64),128,hda.hc);
_d=hda.bar+HD_RIRBLBASE;
*_d=hda.rirb(I64).u32[0];
_d=hda.bar+HD_RIRBUBASE;
*_d=hda.rirb(I64).u32[1];
_w=hda.bar+HD_CORBRP;
/*
*_w=0x8000; //Rst read ptr
do {
Yield;
w=*_w;
} while (!(w&0x8000));
*/
*_w=0x0000; //Rst read ptr
do {
Yield;
w=*_w;
} while (w&0x8000);
_w=hda.bar+HD_RIRBWP;
*_w=0x8000; //Rst write ptr
_b=hda.bar+HD_CORBCTL;
*_b=0x02; //Run
_b=hda.bar+HD_RIRBCTL;
*_b=0x02; //Run
_w=hda.bar+HD_CORBWP;
hda.corb_wp=*_w;
_w=hda.bar+HD_RIRBWP;
hda.rirb_rp=*_w;
hda.ostr0_bdl =CAllocAligned(
HD_BDL_ENTRIES*sizeof(CHDBufDesc),128,hda.hc);
_d=hda.bar+OSTR0+STRBDPL;
*_d=hda.ostr0_bdl(I64).u32[0];
_d=hda.bar+OSTR0+STRBDPU;
*_d=hda.ostr0_bdl(I64).u32[1];
for (i=0;i<2;i++) {
hda.ostr0_bdl[i].buf=hda.ostr0_buf[i]=
CAllocAligned(
SND_BUF_LEN*sizeof(SND_OUT_CONTAINER),128,hda.hc);
hda.ostr0_bdl[i].len=SND_BUF_LEN*sizeof(SND_OUT_CONTAINER);
hda.ostr0_bdl[i].ctrl=1;
}
hda.istr0_bdl =CAllocAligned(
HD_BDL_ENTRIES*sizeof(CHDBufDesc),128,hda.hc);
_d=hda.bar+ISTR0+STRBDPL;
*_d=hda.istr0_bdl(I64).u32[0];
_d=hda.bar+ISTR0+STRBDPU;
*_d=hda.istr0_bdl(I64).u32[1];
for (i=0;i<2;i++) {
hda.istr0_bdl[i].buf=hda.istr0_buf[i]=CAllocAligned(
SND_BUF_LEN*sizeof(SND_IN_CONTAINER),128,hda.hc);
hda.istr0_bdl[i].len=SND_BUF_LEN*sizeof(SND_IN_CONTAINER);
hda.istr0_bdl[i].ctrl=1;
}
_w=hda.bar+HD_STATESTS;
w=*_w;
while (w) {
hda.cad=Bsf(w);
if (HDTestCORBSync(hda.cad,0,VERB_GET_PARAM+P_SUBNODE_CNT)) {
HDTraverse(hda.cad,0);
_d=hda.bar+OSTR0+STRLPIB;
*_d=0;
_d=hda.bar+OSTR0+STRCBL;
*_d=HD_POS_BUF_MULTIPLES*SND_BUF_LEN*sizeof(SND_OUT_CONTAINER);
_w=hda.bar+OSTR0+STRLVI;
*_w=1; //last valid idx
_w=hda.bar+OSTR0+STRFMT;
*_w=HD_DFT_OUT_FMT;
_d=hda.bar+ISTR0+STRLPIB;
*_d=0;
_d=hda.bar+ISTR0+STRCBL;
*_d=HD_POS_BUF_MULTIPLES*SND_BUF_LEN*sizeof(SND_IN_CONTAINER);
_w=hda.bar+ISTR0+STRLVI;
*_w=1; //last valid idx
_w=hda.bar+ISTR0+STRFMT;
*_w=HD_DFT_IN_FMT;
LBts(&sys_semas[SEMA_SND],0); //turn off until cfg completed
LBtr(&snd_flags,Sf_FILLING_OUT);
hda.audio_task_started=FALSE;
if (mp_cnt>1)
hda.task=Spawn(&HDAudioTask,NULL,"HD Audio",mp_cnt-1);
else
hda.task=Spawn(&HDAudioTask,NULL,"HD Audio");
while (!hda.audio_task_started)
Yield;
snd_dev=SD_HD_AUDIO;
return TRUE;
}
Btr(&w,hda.cad);
}
HDAudioEnd(FALSE);
} else
hda.bar=NULL;
return FALSE;
}
Bool HDAudioScan()
{
I64 i=-1,j;
while (TRUE) {
j=PCIClassFind(0x040300,++i);
if (j<0)
return FALSE;
if (HDAudioInit(j.u8[2],j.u8[1],j.u8[0]))
return TRUE;
}
}
HDAudioScan;
Kill(hda.task);
HDAudioScan;
Regular → Executable
View File
Regular → Executable
+191 -37
View File
@@ -1,12 +1,32 @@
#include "HDAudio";
// vim: set ft=c:
#ifndef SND_BUF_LEN
#define SND_BUF_LEN 0
U64 snd_obuf_num;
U64 fp_snd_fill_buf;
#endif
#ifndef SND_OUT_CONTAINER
#define SND_OUT_CONTAINER U32
#endif
U8 *rom_filename=NULL;
Bool rom_select=FALSE;
CDoc *doc_tmp=DocNew;
CDoc *doc_prev=Fs->put_doc;
CTask *draw_task=NULL;
CTask *emu_task=NULL;
CTask *sys_task=NULL;
CSprite *vid;
CDC *gameCanvas=DCNew(320,240);
CDC *Canvas16=DCNew(320,240);
DCFill(gameCanvas,0);
DCFill(Canvas16);
CDC *scr_pillar = DCNew(32,200);
DCFill(scr_pillar,0);
@@ -36,12 +56,13 @@ U8 frame_finished=0;
U8 tmp_str[256];
#include "TOSGame";
#include "Font";
#include "GUI";
//Hide 64-bit reg var compiler warnings.
Fs->put_doc=doc_tmp;
//Fs->put_doc=doc_tmp;
U8 *cartridgebuffer;
U8 *cartridgebuffer=NULL;
I64 numPRGROM;
I64 numCHRROM;
@@ -50,44 +71,97 @@ I64 controlByte2;
I64 numRAM;
I64 trainer;
I64 mapper;
I64 mirroringType;
Bool papu_request_irq = FALSE;
Bool mapper_request_irq = FALSE;
#define MIRR_HORZ 0
#define MIRR_VERT 1
#define MIRR_SINGLE 2
#include "Gamepad";
#include "Joypad";
#include "Mappers";
#include "PAPU";
#include "MMU";
#include "CPU";
#include "PPU";
#include "Audio";
U0 handleResetButton()
initMMU();
U8 *system_state=MAlloc(0x200FD);
/*
U0 SaveInitSystemState()
{
if (TG_KeyDown(Char2ScanCode('r')))
{
frame_count=0;
reset6502;
MemCpy(system_state, MMU.RAM, 0xFFFF);
MemCpy(system_state+0xFFFF, MMU.VRAM, 0xFFFF);
MemCpy(system_state+(0xFFFF*2), MMU.OAM, 0xFF);
FileWrite("E:/Home/Src/templenes/State.BIN.Z",system_state,0x200FD);
}
*/
//initalize the PPU
initPPU2C02(&PPU_state);
//initalize the Joypad
initJoypad(&NES_Joypad);
}
if (TG_KeyDown(Char2ScanCode('d')))
U0 LoadInitSystemState()
{
if (system_state)
{
TG_Exit;
Dbg;
//Free(system_state);
}
system_state=FileRead("E:/Home/Src/templenes/State.BIN.Z");
MemCpy(MMU.RAM, system_state, 0xFFFF);
MemCpy(MMU.VRAM, system_state+0xFFFF, 0xFFFF);
MemCpy(MMU.OAM, system_state+(0xFFFF*2), 0xFF);
MemCpy(system_state+0xFFFF, MMU.VRAM, 0xFFFF);
MemCpy(system_state+(0xFFFF*2), MMU.OAM, 0xFF);
//Free(system_state);
system_state=NULL;
}
U0 resetSystem()
{
frame_count=0;
reset6502;
//initalize the PPU
initPPU2C02(&PPU_state);
//initalize the Joypad
initJoypad(&NES_Joypad);
//initialize the pAPU
if (SND_BUF_LEN>0)
{
PAPU_reset(&PAPU);
}
}
U0 handleCmdButtons()
{
if (TG_KeyDown(SC_ESC))
{
quit=TRUE;
}
if (TG_KeyDown(Char2ScanCode('r')))
{
resetSystem;
}
if (TG_KeyDown(Char2ScanCode('d')))
{
paused = TRUE;
fp_snd_fill_buf=fp_old_fill_buf;
Sleep(100);//Give time to switch audio callback
TG_Exit;
Dbg;
fp_snd_fill_buf=&AudioFillBuf;
paused = FALSE;
}
}
U0 doScreenUpdate()
{
if (MMU.RAM[0x2000] & (1 << 5) != 0)
{
GrBlot(gameCanvas, 0, -8, Canvas16);
DCFill(Canvas16);
}
if (fit_screen)
{
vid=DC2Sprite(gameCanvas);
@@ -108,7 +182,12 @@ U0 drawScreen()
{
while (1)
{
WinMsUpdate;
KbdMsHndlr(FALSE, FALSE);
if( frame_finished ) {
updateGamepad;
frame_count += 1;
doScreenUpdate;
}
@@ -119,22 +198,17 @@ U0 drawScreen()
}
}
I64 TempleNES(U8 *rom_filename)
U0 initCart(U8 *rom_filename)
{
CDirEntry *chk_file=FilesFind(rom_filename);
if( !chk_file ) {
PrintErr("iNES ROM file not found.\n");
return 1;
}
DirTreeDel(chk_file);
cartridgebuffer = FileRead(rom_filename);
//if the file is not an iNES-file, abort
if(cartridgebuffer[0] != 'N' || cartridgebuffer[1] != 'E' || cartridgebuffer[2] != 'S' || cartridgebuffer[3] != 0x1a) {
PrintErr("File is not an iNES-file.\n");
return 1;
}
mirroringType = MIRR_HORZ;
numPRGROM = cartridgebuffer[4];
numCHRROM = cartridgebuffer[5];
controlByte1 = cartridgebuffer[6];
@@ -143,9 +217,14 @@ I64 TempleNES(U8 *rom_filename)
trainer = (controlByte1 & (1 << 2));
if (controlByte1 & 1)
{
mirroringType = MIRR_VERT;
}
mapper = ( (controlByte2 & 0xF0) | ((controlByte1 & 0xF0) >> 4));
initMMU();
//LoadInitSystemState;
switch (mapper)
{
@@ -158,18 +237,67 @@ I64 TempleNES(U8 *rom_filename)
//Copy the ROM into the PPU's memory
MemCpy(MMU.VRAM, cartridgebuffer+0x10+0x4000*numPRGROM, 0x2000*numCHRROM);
break;
case 1:
case 1:// MMC1
case 2:// UNROM
//Load first PRG ROM bank
MemCpy(MMU.RAM+0x8000, cartridgebuffer+0x10, 0x4000);
//and last PRG ROM bank
MemCpy(MMU.RAM+0xC000, cartridgebuffer+0x10+0x4000*(numPRGROM-1), 0x4000);
//Copy the ROM into the PPU's memory
MemCpy(MMU.VRAM, cartridgebuffer+0x10+0x4000*numPRGROM, 0x2000*numCHRROM);
if (numCHRROM)
{
MemCpy(MMU.VRAM, cartridgebuffer+0x10+0x4000*numPRGROM, 0x2000*numCHRROM);
}
break;
case 3:// CNROM
//Copy the ROM into the CPU's memory
MemCpy(MMU.RAM+0x8000, cartridgebuffer+0x10, numPRGROM*0x4000);
if(numPRGROM == 1) {
MemCpy(MMU.RAM+0xC000, cartridgebuffer+0x10, numPRGROM*0x4000);
}
//Copy the ROM into the PPU's memory
MemCpy(MMU.VRAM, cartridgebuffer+0x10+0x4000*numPRGROM, 0x2000);
break;
case 4:// MMC3
MMC1_load8kRomBank((numPRGROM - 1) * 2, 0xc000);
MMC1_load8kRomBank((numPRGROM - 1) * 2 + 1, 0xe000);
MMC1_load8kRomBank(0, 0x8000);
MMC1_load8kRomBank(1, 0xa000);
//Copy the ROM into the PPU's memory
MemCpy(MMU.VRAM, cartridgebuffer+0x10+0x4000*numPRGROM, 0x2000);
break;
case 11:// Color Dreams
//Load first PRG ROM bank
MemCpy(MMU.RAM+0x8000, cartridgebuffer+0x10, 0x8000);
//Copy the ROM into the PPU's memory
MemCpy(MMU.VRAM, cartridgebuffer+0x10+0x4000*numPRGROM, 0x2000);
break;
case 17:// FFE Copier
//Load first PRG ROM bank
MemCpy(MMU.RAM+0x8000, cartridgebuffer+0x10, 0x8000);
//Copy the ROM into the PPU's memory
MemCpy(MMU.VRAM, cartridgebuffer+0x10+0x4000*numPRGROM, 0x2000);
break;
case 18:// Jaleco SS88006
//Load first/last PRG ROM bank
MemCpy(MMU.RAM+0x8000, cartridgebuffer+0x10, 0x4000);
MemCpy(MMU.RAM+0xC000, cartridgebuffer+0x10+(0x4000*(numPRGROM-1)), 0x4000);
//Copy the ROM into the PPU's memory
MemCpy(MMU.VRAM, cartridgebuffer+0x10+0x4000*numPRGROM, 0x2000);
break;
default:
PrintErr("iNES mapper not supported.\n");
//Free(cartridgebuffer);
return 1;
break;
}
}
I64 TempleNES(U8 *rom_file)
{
initCart(rom_file);
//initalize the CPU
reset6502;
@@ -179,6 +307,12 @@ I64 TempleNES(U8 *rom_filename)
//initalize the Joypad
initJoypad(&NES_Joypad);
//initialize the pAPU
if (SND_BUF_LEN>0)
{
initPAPU(&PAPU);
}
//Initalize TOSGame
TG_Start;
@@ -187,38 +321,58 @@ I64 TempleNES(U8 *rom_filename)
draw_task = Spawn(&drawScreen,,,1);
while(!quit) {
WinMsUpdate;
KbdMsHndlr(FALSE, FALSE);
start_buf_num=snd_obuf_num;
fp_old_fill_buf=fp_snd_fill_buf;
fp_snd_fill_buf=&AudioFillBuf;
while(!quit && !TG_KeyDown(SC_ESC)) {
//emulate CPU and PPU
frame_finished = 0;
if( paused == 0 ) {
if( paused == 0) {
if (mapper_request_irq)
{
irq6502;
mapper_request_irq = FALSE;
}
if (papu_request_irq)
{
irq6502;
papu_request_irq = FALSE;
}
exec6502(1);
cycles = ticktable[opcode];
if (SND_BUF_LEN>0)
{
PAPU_clockFrameCounter(&PAPU, cycles);
}
loop = cycles*3;
while( loop != 0 )
{
frame_finished |= PPUcycle(&PPU_state);
loop -= 1;
}
I64 ii;
for (ii=0;ii<1024;ii++){} // Delay loop
}
if (reset)
{
resetSystem;
reset = FALSE;
}
handleResetButton;
handleCmdButtons;
handleInput(&NES_Joypad);
}
quit = FALSE;
fp_snd_fill_buf=fp_old_fill_buf;
Kill(draw_task);
DocClear;
Free(cartridgebuffer);
//Free(cartridgebuffer);
TG_Exit;
return 0;
}
Fs->put_doc=doc_prev;
Fs->put_doc=doc_prev;
start_buf_num=snd_obuf_num;
fp_old_fill_buf=fp_snd_fill_buf;
Regular → Executable
+117 -216
View File
@@ -8,6 +8,8 @@ U8 byte2;
U8 shift_register_0;
U8 shift_register_1;
U8 shift_register_2;
U8 shift_register_3;
U8 attribute;
U8 x;
};
@@ -29,234 +31,33 @@ U16 nametable_base;
U16 bitmap_shift_0_latch;
U16 bitmap_shift_1_latch;
U16 bitmap_shift_2_latch;
U16 bitmap_shift_3_latch;
U16 bitmap_shift_0;
U16 bitmap_shift_1;
U16 bitmap_shift_2;
U16 bitmap_shift_3;
U16 AT_shift_0_latch;
U16 AT_shift_1_latch;
U16 AT_shift_0;
U16 AT_shift_1;
U16 AT_shift_2_latch;
U16 AT_shift_3_latch;
U16 AT_shift_2;
U16 AT_shift_3;
U8 num_sprites;
PPUsprite sprites[8];
};
PPU2C02state PPU_state;
class memory_manager {
U8 *RAM;
U8 *VRAM;
U8 *OAM;
U8 w;
U16 t;
U16 x;
U16 y;
U16 VRAM_address;
U8 internal_buffer;
};
memory_manager MMU;
// Mapper #1 [MMC1]
// 5-bit buffer:
I64 MMC1_regBuffer = 0;
I64 MMC1_regBufferCounter = 0;
// Register 0:
I64 MMC1_mirroring = 0;
I64 MMC1_oneScreenMirroring = 0;
I64 MMC1_prgSwitchingArea = 1;
I64 MMC1_prgSwitchingSize = 1;
I64 MMC1_vromSwitchingSize = 0;
// Register 1:
I64 MMC1_romSelectionReg0 = 0;
// Register 2:
I64 MMC1_romSelectionReg1 = 0;
// Register 3:
I64 MMC1_romBankSelect = 0;
I64 MMC1_getRegNumber(U16 address)
{
if (address >= 0x8000 && address <= 0x9fff) {
return 0;
} else if (address >= 0xa000 && address <= 0xbfff) {
return 1;
} else if (address >= 0xc000 && address <= 0xdfff) {
return 2;
} else {
return 3;
}
}
U0 MMC1_loadRomBank(I64 bank, U16 address)
{
}
U0 MMC1_loadVromBank(I64 bank, U16 address)
{
}
U0 MMC1_load32kRomBank(I64 bank, U16 address)
{
}
U0 MMC1_load8kVromBank(I64 bank, U16 address)
{
}
U0 MMC1_setReg(I64 _reg, I64 value)
{
I64 tmp;
switch (_reg) {
case 0:
// Mirroring:
tmp = value & 3;
if (tmp != MMC1_mirroring) {
// Set mirroring:
MMC1_mirroring = tmp;
/* TODO: mirroring
if ((MMC1_mirroring & 2) == 0) {
// SingleScreen mirroring overrides the other setting:
MMC1_nes.ppu.setMirroring(MMC1_nes.rom.SINGLESCREEN_MIRRORING);
} else if ((MMC1_mirroring & 1) != 0) {
// Not overridden by SingleScreen mirroring.
MMC1_nes.ppu.setMirroring(MMC1_nes.rom.HORIZONTAL_MIRRORING);
} else {
MMC1_nes.ppu.setMirroring(MMC1_nes.rom.VERTICAL_MIRRORING);
}
*/
}
// PRG Switching Area;
MMC1_prgSwitchingArea = (value >> 2) & 1;
// PRG Switching Size:
MMC1_prgSwitchingSize = (value >> 3) & 1;
// VROM Switching Size:
MMC1_vromSwitchingSize = (value >> 4) & 1;
break;
case 1:
// ROM selection:
MMC1_romSelectionReg0 = (value >> 4) & 1;
// Check whether the cart has VROM:
if (numCHRROM > 0) {
// Select VROM bank at 0x0000:
if (MMC1_vromSwitchingSize == 0) {
// Swap 8kB VROM:
if (MMC1_romSelectionReg0 == 0) {
MMC1_load8kVromBank(value & 0xf, 0x0000);
} else {
MMC1_load8kVromBank(
Floor(numCHRROM / 2) + (value & 0xf),
0x0000
);
}
} else {
// Swap 4kB VROM:
if (MMC1_romSelectionReg0 == 0) {
MMC1_loadVromBank(value & 0xf, 0x0000);
} else {
MMC1_loadVromBank(
Floor(numCHRROM / 2) + (value & 0xf),
0x0000
);
}
}
}
break;
case 2:
// ROM selection:
MMC1_romSelectionReg1 = (value >> 4) & 1;
// Check whether the cart has VROM:
if (numCHRROM > 0) {
// Select VROM bank at 0x1000:
if (MMC1_vromSwitchingSize == 1) {
// Swap 4kB of VROM:
if (MMC1_romSelectionReg1 == 0) {
MMC1_loadVromBank(value & 0xf, 0x1000);
} else {
MMC1_loadVromBank(
Floor(numCHRROM / 2) + (value & 0xf),
0x1000
);
}
}
}
break;
default:
// Select ROM bank:
// -------------------------
tmp = value & 0xf;
I64 bank;
I64 baseBank = 0;
if (numPRGROM >= 32) {
// 1024 kB cart
if (MMC1_vromSwitchingSize == 0) {
if (MMC1_romSelectionReg0 == 1) {
baseBank = 16;
}
} else {
baseBank =
(MMC1_romSelectionReg0 | (MMC1_romSelectionReg1 << 1)) << 3;
}
} else if (numPRGROM >= 16) {
// 512 kB cart
if (MMC1_romSelectionReg0 == 1) {
baseBank = 8;
}
}
if (MMC1_prgSwitchingSize == 0) {
// 32kB
bank = baseBank + (value & 0xf);
MMC1_load32kRomBank(bank, 0x8000);
} else {
// 16kB
bank = baseBank * 2 + (value & 0xf);
if (MMC1_prgSwitchingArea == 0) {
MMC1_loadRomBank(bank, 0xc000);
} else {
MMC1_loadRomBank(bank, 0x8000);
}
}
}
}
U0 MMC1_Write(U16 address, U8 value)
{
// See what should be done with the written value:
if ((value & 128) != 0) {
// Reset buffering:
MMC1_regBufferCounter = 0;
MMC1_regBuffer = 0;
// Reset register:
if (MMC1_getRegNumber(address) == 0) {
MMC1_prgSwitchingArea = 1;
MMC1_prgSwitchingSize = 1;
}
} else {
// Continue buffering:
//regBuffer = (regBuffer & (0xFF-(1<<regBufferCounter))) | ((value & (1<<regBufferCounter))<<regBufferCounter);
MMC1_regBuffer =
(MMC1_regBuffer & (0xff - (1 << MMC1_regBufferCounter))) |
((value & 1) << MMC1_regBufferCounter);
MMC1_regBufferCounter++;
if (MMC1_regBufferCounter == 5) {
// Use the buffered value:
MMC1_setReg(MMC1_getRegNumber(address), MMC1_regBuffer);
// Reset buffer:
MMC1_regBuffer = 0;
MMC1_regBufferCounter = 0;
}
}
}
U0 initMMU() {
MemSet(&PPU_state,0,sizeof(PPU2C02state));
MMU.RAM = MAlloc(0xFFFF);
MMU.VRAM = MAlloc(0xFFFF);
MMU.OAM = MAlloc(0xFF);
@@ -271,7 +72,32 @@ U0 initMMU() {
}
U0 writeVRAM(U16 address, U8 value) {
//assert(address <= 0x3F20);
if (address > 0x2FFF && address < 0x3F00)
{ //Nametable mirror
address -= 0x1000;
}
if (mirroringType == MIRR_HORZ)
{
if (address > 0x23FF && address < 0x2800)
{
address -= 0x400;
}
if (address > 0x2BFF && address < 0x3000)
{
address -= 0x400;
}
}
if (mirroringType == MIRR_VERT)
{
if (address > 0x1FFF && address < 0x3000)
{
address &= 0x27FF;
}
}
MMU.VRAM[address] = value;
if( address == 0x3F10 || address == 0x3F14 || address == 0x3F18 || address == 0x3F1C ) {
address -= 0x10;
@@ -281,7 +107,35 @@ U0 writeVRAM(U16 address, U8 value) {
}
U8 readVRAM(U16 address) {
//assert(address <= 0x3F20);
if (address > 0x2FFF && address < 0x3F00)
{ //Nametable mirror
address -= 0x1000;
}
if (mirroringType == MIRR_HORZ)
{
if (address > 0x23FF && address < 0x2800)
{
address -= 0x400;
return MMU.VRAM[address];
}
if (address > 0x2BFF && address < 0x3000)
{
address -= 0x400;
return MMU.VRAM[address];
}
}
if (mirroringType == MIRR_VERT)
{
if (address > 0x1FFF && address < 0x3000)
{
address &= 0x27FF;
return MMU.VRAM[address];
}
}
if( address == 0x3F10 || address == 0x3F14 || address == 0x3F18 || address == 0x3F1C ) {
address -= 0x10;
}
@@ -331,7 +185,6 @@ U8 writeRAM(U16 address, U8 value) {
MMU.t &= ~31; // 31 = 11111b
MMU.t |= ((value&248) >> 3);
MMU.x = (value & 7);
//printf("setting coarse X to %d on scanline %d\n", ((value&248) >> 3), PPU_state.scanline);
}
else {
//t: CBA..HG FED..... = d: HGFEDCBA
@@ -380,11 +233,34 @@ U8 writeRAM(U16 address, U8 value) {
return 513 + PPU_state.odd_frame; //I think?
}
//PAPU
if ((address > 0x3FFF && address < 0x4016) || address == 0x4017)
{
if (SND_BUF_LEN>0)
{
PAPU_writeReg(&PAPU, address, value);
}
}
//Joypad 1
else if(address == 0x4016) {
writeJoypad(&NES_Joypad, value);
}
else if(address > 0x44FF && address < 0x451D && mapper==17) {
FFE_Write(address, value);
}
else if (mapper == 18 && ((address > 0x7FFF && address < 0x8004) ||
(address > 0x8FFF && address < 0x9002) ||
(address > 0x9FFF && address < 0xA004) ||
(address > 0xAFFF && address < 0xB004) ||
(address > 0xBFFF && address < 0xC004) ||
(address > 0xCFFF && address < 0xD004)))
{
SS88006_Write(address, value);
}
else if(address > 0x7FFF) {
switch (mapper)
{
@@ -392,6 +268,22 @@ U8 writeRAM(U16 address, U8 value) {
MMC1_Write(address, value);
return 0;
break;
case 2:
UNROM_Write(address, value);
return 0;
break;
case 3:
CNROM_Write(address, value);
return 0;
break;
case 4:
MMC3_Write(address, value);
return 0;
break;
case 11:
ColorDreams_Write(address, value);
return 0;
break;
default:
break;
}
@@ -402,6 +294,7 @@ U8 writeRAM(U16 address, U8 value) {
}
U8 readRAM(U16 address) {
while(address >= 0x2008 && address < 0x4000) {
address -= 8; //handle mirroring
}
@@ -451,6 +344,14 @@ U8 readRAM(U16 address) {
retVal = getNextButton(&NES_Joypad);
}
//PAPU
if ((address > 0x3FFF && address < 0x4016))
{
if (SND_BUF_LEN>0)
{
return PAPU_readReg(&PAPU, address);
}
}
return retVal;
}
@@ -493,4 +394,4 @@ U0 dumpVRAM() {
"%X, ", MMU.VRAM[address];
count += 1;
}
}
}
Executable
+549
View File
@@ -0,0 +1,549 @@
// vim: set ft=c:
class memory_manager {
U8 *RAM;
U8 *VRAM;
U8 *OAM;
U8 w;
U16 t;
U16 x;
U16 y;
U16 VRAM_address;
U8 internal_buffer;
};
memory_manager MMU;
// Mapper #1 [MMC1]
// 5-bit buffer:
I64 MMC1_regBuffer = 0;
I64 MMC1_regBufferCounter = 0;
// Register 0:
I64 MMC1_mirroring = 0;
I64 MMC1_oneScreenMirroring = 0;
I64 MMC1_prgSwitchingArea = 1;
I64 MMC1_prgSwitchingSize = 1;
I64 MMC1_vromSwitchingSize = 0;
// Register 1:
I64 MMC1_romSelectionReg0 = 0;
// Register 2:
I64 MMC1_romSelectionReg1 = 0;
// Register 3:
I64 MMC1_romBankSelect = 0;
I64 MMC1_getRegNumber(U16 address)
{
if (address >= 0x8000 && address <= 0x9fff) {
return 0;
} else if (address >= 0xa000 && address <= 0xbfff) {
return 1;
} else if (address >= 0xc000 && address <= 0xdfff) {
return 2;
} else {
return 3;
}
}
U0 MMC1_loadRomBank(I64 bank, U16 address)
{
// Swap in the given PRG-ROM bank:
MemCpy(MMU.RAM+address, cartridgebuffer+0x10+0x4000*(bank), 0x4000);
}
U0 MMC1_loadVromBank(I64 bank, U16 address)
{
//Copy the ROM into the PPU's memory
I64 bank4k = Floor(bank / 4) % numCHRROM;
I64 bankoffset = (bank % 4) * 1024;
MemCpy(MMU.VRAM+address, cartridgebuffer+0x10+(0x4000*numPRGROM)+(0x1000*(bankoffset)), 1024);
}
U0 MMC1_load1kVromBank(I64 bank, U16 address)
{
//Copy the ROM into the PPU's memory
MemCpy(MMU.VRAM+address, cartridgebuffer+0x10+(0x4000*numPRGROM)+(0x400*(bank)), 0x400);
}
U0 MMC1_load8kRomBank(I64 bank, U16 address)
{
// Swap in the given PRG-ROM bank:
MemCpy(MMU.RAM+address, cartridgebuffer+0x10+0x2000*(bank), 0x2000);
}
U0 MMC1_load32kRomBank(I64 bank, U16 address)
{
// Swap in the given PRG-ROM bank:
MemCpy(MMU.RAM+address, cartridgebuffer+0x10+0x4000*(bank), 0x8000);
}
U0 MMC1_load8kVromBank(I64 bank, U16 address)
{
//Copy the ROM into the PPU's memory
MemCpy(MMU.VRAM+address, cartridgebuffer+0x10+(0x4000*numPRGROM)+(0x1000*(bank)), 0x2000);
}
U0 MMC1_setReg(I64 _reg, I64 value)
{
I64 tmp;
switch (_reg) {
case 0:
// Mirroring:
tmp = value & 3;
if (tmp != MMC1_mirroring) {
// Set mirroring:
MMC1_mirroring = tmp;
if ((MMC1_mirroring & 2) == 0) {
// SingleScreen mirroring overrides the other setting:
mirroringType = MIRR_SINGLE;
} else if ((MMC1_mirroring & 1) != 0) {
// Not overridden by SingleScreen mirroring.
mirroringType = MIRR_HORZ;
} else {
mirroringType = MIRR_VERT;
}
}
// PRG Switching Area;
MMC1_prgSwitchingArea = (value >> 2) & 1;
// PRG Switching Size:
MMC1_prgSwitchingSize = (value >> 3) & 1;
// VROM Switching Size:
MMC1_vromSwitchingSize = (value >> 4) & 1;
break;
case 1:
// ROM selection:
MMC1_romSelectionReg0 = (value >> 4) & 1;
// Check whether the cart has VROM:
if (numCHRROM > 0) {
// Select VROM bank at 0x0000:
if (MMC1_vromSwitchingSize == 0) {
// Swap 8kB VROM:
if (MMC1_romSelectionReg0 == 0) {
MMC1_load8kVromBank(value & 0xf, 0x0000);
} else {
MMC1_load8kVromBank(
Floor(numCHRROM / 2) + (value & 0xf),
0x0000
);
}
} else {
// Swap 4kB VROM:
if (MMC1_romSelectionReg0 == 0) {
MMC1_loadVromBank(value & 0xf, 0x0000);
} else {
MMC1_loadVromBank(
Floor(numCHRROM / 2) + (value & 0xf),
0x0000
);
}
}
}
break;
case 2:
// ROM selection:
MMC1_romSelectionReg1 = (value >> 4) & 1;
// Check whether the cart has VROM:
if (numCHRROM > 0) {
// Select VROM bank at 0x1000:
if (MMC1_vromSwitchingSize == 1) {
// Swap 4kB of VROM:
if (MMC1_romSelectionReg1 == 0) {
MMC1_loadVromBank(value & 0xf, 0x1000);
} else {
MMC1_loadVromBank(
Floor(numCHRROM / 2) + (value & 0xf),
0x1000
);
}
}
}
break;
default:
// Select ROM bank:
// -------------------------
tmp = value & 0xf;
I64 bank;
I64 baseBank = 0;
if (numPRGROM >= 32) {
// 1024 kB cart
if (MMC1_vromSwitchingSize == 0) {
if (MMC1_romSelectionReg0 == 1) {
baseBank = 16;
}
} else {
baseBank =
(MMC1_romSelectionReg0 | (MMC1_romSelectionReg1 << 1)) << 3;
}
} else if (numPRGROM >= 16) {
// 512 kB cart
if (MMC1_romSelectionReg0 == 1) {
baseBank = 8;
}
}
if (MMC1_prgSwitchingSize == 0) {
// 32kB
bank = baseBank + (value & 0xf);
MMC1_load32kRomBank(bank, 0x8000);
} else {
// 16kB
bank = baseBank * 2 + (value & 0xf);
if (MMC1_prgSwitchingArea == 0) {
MMC1_loadRomBank(bank, 0xc000);
} else {
MMC1_loadRomBank(bank, 0x8000);
}
}
}
}
U0 MMC1_Write(U16 address, U8 value)
{
// See what should be done with the written value:
if ((value & 128) != 0) {
// Reset buffering:
MMC1_regBufferCounter = 0;
MMC1_regBuffer = 0;
// Reset register:
if (MMC1_getRegNumber(address) == 0) {
MMC1_prgSwitchingArea = 1;
MMC1_prgSwitchingSize = 1;
}
} else {
// Continue buffering:
//regBuffer = (regBuffer & (0xFF-(1<<regBufferCounter))) | ((value & (1<<regBufferCounter))<<regBufferCounter);
MMC1_regBuffer =
(MMC1_regBuffer & (0xff - (1 << MMC1_regBufferCounter))) |
((value & 1) << MMC1_regBufferCounter);
MMC1_regBufferCounter++;
if (MMC1_regBufferCounter == 5) {
// Use the buffered value:
MMC1_setReg(MMC1_getRegNumber(address), MMC1_regBuffer);
// Reset buffer:
MMC1_regBuffer = 0;
MMC1_regBufferCounter = 0;
}
}
}
// Mapper #2 [UNROM]
U0 UNROM_Write(U16 address, U8 value)
{
// Swap in the given PRG-ROM bank:
MemCpy(MMU.RAM+0x8000, cartridgebuffer+0x10+0x4000*(value), 0x4000);
}
// Mapper #3 [CNROM]
U0 CNROM_Write(U16 address, U8 value)
{
I64 bank = value & 3;
//Copy the ROM into the PPU's memory
MemCpy(MMU.VRAM, cartridgebuffer+0x10+(0x4000*numPRGROM)+(0x2000*(bank)), 0x2000);
}
// Mapper #4 [MMC3]
#define MMC3_CMD_SEL_2_1K_VROM_0000 0
#define MMC3_CMD_SEL_2_1K_VROM_0800 1
#define MMC3_CMD_SEL_1K_VROM_1000 2
#define MMC3_CMD_SEL_1K_VROM_1400 3
#define MMC3_CMD_SEL_1K_VROM_1800 4
#define MMC3_CMD_SEL_1K_VROM_1C00 5
#define MMC3_CMD_SEL_ROM_PAGE1 6
#define MMC3_CMD_SEL_ROM_PAGE2 7
I64 MMC3_command = NULL;
I64 MMC3_prgAddressSelect = NULL;
I64 MMC3_chrAddressSelect = NULL;
I64 MMC3_pageNumber = NULL;
I64 MMC3_irqCounter = NULL;
I64 MMC3_irqLatchValue = NULL;
I64 MMC3_irqEnable = NULL;
I64 MMC3_prgAddressChanged = FALSE;
U0 MMC3_executeCommand(I64 cmd, I64 arg) {
switch (cmd)
{
case MMC3_CMD_SEL_2_1K_VROM_0000:
// Select 2 1KB VROM pages at 0x0000:
if (MMC3_chrAddressSelect == 0) {
MMC1_load1kVromBank(arg, 0x0000);
MMC1_load1kVromBank(arg + 1, 0x0400);
} else {
MMC1_load1kVromBank(arg, 0x1000);
MMC1_load1kVromBank(arg + 1, 0x1400);
}
break;
case MMC3_CMD_SEL_2_1K_VROM_0800:
// Select 2 1KB VROM pages at 0x0800:
if (MMC3_chrAddressSelect == 0) {
MMC1_load1kVromBank(arg, 0x0800);
MMC1_load1kVromBank(arg + 1, 0x0c00);
} else {
MMC1_load1kVromBank(arg, 0x1800);
MMC1_load1kVromBank(arg + 1, 0x1c00);
}
break;
case MMC3_CMD_SEL_1K_VROM_1000:
// Select 1K VROM Page at 0x1000:
if (MMC3_chrAddressSelect == 0) {
MMC1_load1kVromBank(arg, 0x1000);
} else {
MMC1_load1kVromBank(arg, 0x0000);
}
break;
case MMC3_CMD_SEL_1K_VROM_1400:
// Select 1K VROM Page at 0x1400:
if (MMC3_chrAddressSelect == 0) {
MMC1_load1kVromBank(arg, 0x1400);
} else {
MMC1_load1kVromBank(arg, 0x0400);
}
break;
case MMC3_CMD_SEL_1K_VROM_1800:
// Select 1K VROM Page at 0x1800:
if (MMC3_chrAddressSelect == 0) {
MMC1_load1kVromBank(arg, 0x1800);
} else {
MMC1_load1kVromBank(arg, 0x0800);
}
break;
case MMC3_CMD_SEL_1K_VROM_1C00:
// Select 1K VROM Page at 0x1C00:
if (MMC3_chrAddressSelect == 0) {
MMC1_load1kVromBank(arg, 0x1c00);
} else {
MMC1_load1kVromBank(arg, 0x0c00);
}
break;
case MMC3_CMD_SEL_ROM_PAGE1:
if (MMC3_prgAddressChanged) {
// Load the two hardwired banks:
if (MMC3_prgAddressSelect == 0) {
MMC1_load8kRomBank((numPRGROM - 1) * 2, 0xc000);
} else {
MMC1_load8kRomBank((numPRGROM - 1) * 2, 0x8000);
}
MMC3_prgAddressChanged = FALSE;
}
// Select first switchable ROM page:
if (MMC3_prgAddressSelect == 0) {
MMC1_load8kRomBank(arg, 0x8000);
} else {
MMC1_load8kRomBank(arg, 0xc000);
}
break;
case MMC3_CMD_SEL_ROM_PAGE2:
// Select second switchable ROM page:
MMC1_load8kRomBank(arg, 0xa000);
// hardwire appropriate bank:
if (MMC3_prgAddressChanged) {
// Load the two hardwired banks:
if (MMC3_prgAddressSelect == 0) {
MMC1_load8kRomBank((numPRGROM - 1) * 2, 0xc000);
} else {
MMC1_load8kRomBank((numPRGROM - 1) * 2, 0x8000);
}
MMC3_prgAddressChanged = FALSE;
}
}
};
U0 MMC3_Write(U16 address, U8 value)
{
switch (address)
{
case 0x8000:
// Command/Address Select register
MMC3_command = value & 7;
I64 tmp = (value >> 6) & 1;
if (tmp != MMC3_prgAddressSelect) {
MMC3_prgAddressChanged = TRUE;
}
MMC3_prgAddressSelect = tmp;
MMC3_chrAddressSelect = (value >> 7) & 1;
break;
case 0x8001:
// Page number for command
MMC3_executeCommand(MMC3_command, value);
break;
case 0xa000:
// Mirroring select
if ((value & 1) != 0) {
mirroringType = MIRR_HORZ;
} else {
mirroringType = MIRR_VERT;
}
break;
case 0xa001:
// SaveRAM Toggle
// TODO
//nes.getRom().setSaveState((value&1)!=0);
break;
case 0xc000:
// IRQ Counter register
MMC3_irqCounter = value;
//nes.ppu.mapperIrqCounter = 0;
break;
case 0xc001:
// IRQ Latch register
MMC3_irqLatchValue = value;
break;
case 0xe000:
// IRQ Control Reg 0 (disable)
//irqCounter = irqLatchValue;
MMC3_irqEnable = 0;
break;
case 0xe001:
// IRQ Control Reg 1 (enable)
MMC3_irqEnable = 1;
break;
default:
// Not a MMC3 register.
// The game has probably crashed,
// since it tries to write to ROM..
// IGNORE.
}
}
U0 MMC3_clockIrqCounter()
{
if (MMC3_irqEnable == 1) {
MMC3_irqCounter--;
if (MMC3_irqCounter < 0)
{
// Trigger IRQ:
mapper_request_irq=TRUE;
MMC3_irqCounter = MMC3_irqLatchValue;
}
}
}
// Mapper #11 [Color Dreams]
U0 ColorDreams_Write(U16 address, U8 value)
{
// Swap in the given PRG-ROM bank:
I64 prgbank = ((value & 0xf) * 2) % numPRGROM;
MemCpy(MMU.RAM+0x8000, cartridgebuffer+0x10+0x4000*(prgbank), 0x8000);
if (numCHRROM > 0) {
// Swap in the given VROM bank at 0x0000:
I64 bank = ((value >> 4) * 2);
MemCpy(MMU.VRAM, cartridgebuffer+0x10+(0x4000*numPRGROM)+(0x1000*(bank)), 0x2000);
}
}
// Mapper #17 [FFE Copier]
U0 FFE_Write(U16 address, U8 value)
{
switch (address)
{
case 0x4500://Config register
break;
case 0x4501://Disable IRQ
break;
case 0x4502://IRQ Counter low byte
break;
case 0x4503://IRQ Counter high byte
break;
case 0x4504...0x4507://Switch PRG bank
I64 prgbank = address-0x4504;
MemCpy(MMU.RAM+0x8000+(prgbank*0x2000), cartridgebuffer+0x10+0x2000*(prgbank), 0x2000);
break;
case 0x4510...0x451B://Switch CHR bank
I64 chrbank = address-0x4510;
MemCpy(MMU.VRAM+(chrbank*0x400), cartridgebuffer+0x10+(0x4000*numPRGROM)+(chrbank*0x400), 0x400);
break;
default:
break;
}
}
// Mapper #18 [Jaleco SS88006]
U8 SS88006_lo = 0;
Bool SS88006_loWrite = FALSE;
U8 SS88006_hi = 0;
Bool SS88006_hiWrite = FALSE;
U8 SS88006_irqctr;
U0 SS88006_Write(U16 address, U8 value)
{
I64 bank;
if (address & 1)
{
SS88006_hi = value << 4;
SS88006_hiWrite = TRUE;
}
else
{
SS88006_lo = value;
SS88006_loWrite = TRUE;
}
if (SS88006_loWrite && SS88006_hiWrite)
{
bank = SS88006_lo + SS88006_hi;
switch (address)
{
case 0x8000...0x8001:
MemCpy(MMU.RAM+0x8000, cartridgebuffer+0x10+0x2000*(bank), 0x2000);
break;
case 0x8002...0x8003:
MemCpy(MMU.RAM+0xA000, cartridgebuffer+0x10+0x2000*(bank), 0x2000);
break;
case 0x9000...0x9001:
MemCpy(MMU.RAM+0xC000, cartridgebuffer+0x10+0x2000*(bank), 0x2000);
break;
case 0xA000...0xA001:
MemCpy(MMU.VRAM+0x0000, cartridgebuffer+0x10+(0x4000*numPRGROM)+(bank*0x400), 0x400);
break;
case 0xA002...0xA003:
MemCpy(MMU.VRAM+0x0400, cartridgebuffer+0x10+(0x4000*numPRGROM)+(bank*0x400), 0x400);
break;
case 0xB000...0xB001:
MemCpy(MMU.VRAM+0x0800, cartridgebuffer+0x10+(0x4000*numPRGROM)+(bank*0x400), 0x400);
break;
case 0xB002...0xB003:
MemCpy(MMU.VRAM+0x0C00, cartridgebuffer+0x10+(0x4000*numPRGROM)+(bank*0x400), 0x400);
break;
case 0xC000...0xC001:
MemCpy(MMU.VRAM+0x1000, cartridgebuffer+0x10+(0x4000*numPRGROM)+(bank*0x400), 0x400);
break;
case 0xC002...0xC003:
MemCpy(MMU.VRAM+0x1400, cartridgebuffer+0x10+(0x4000*numPRGROM)+(bank*0x400), 0x400);
break;
case 0xD000...0xD001:
MemCpy(MMU.VRAM+0x1800, cartridgebuffer+0x10+(0x4000*numPRGROM)+(bank*0x400), 0x400);
break;
case 0xD002...0xD003:
MemCpy(MMU.VRAM+0x1C00, cartridgebuffer+0x10+(0x4000*numPRGROM)+(bank*0x400), 0x400);
break;
default:
break;
}
SS88006_loWrite = FALSE;
SS88006_hiWrite = FALSE;
}
}
Executable
+1359
View File
File diff suppressed because it is too large Load Diff
Regular → Executable
+169 -34
View File
@@ -1,7 +1,5 @@
// vim: set ft=c:
#define FRAME_RATE 60
U32 pixelWidth = 2;
U32 pixelHeight = 2;
U32 ppu_colors[64] =
@@ -56,20 +54,19 @@ U0 verinc() {
}
}
U0 setPixelColor(I64 x, I64 y, I64 color) {
U8 *pixel=TG_Canvas->body;
U0 setPixelColor(I64 x, I64 y, I64 color, CDC *dc=gameCanvas) {
I64 dx = 0, dy=0;
for(dx=0; dx<pixelWidth; ++dx) {
for(dy=0; dy<pixelHeight; ++dy) {
if (fit_screen)
{
gameCanvas->color = 16+color.u8[0];
GrPlot(gameCanvas, x+32, y);
dc->color = 16+color.u8[0];
GrPlot(dc, x+32, y);
}
else
{
gameCanvas->color = 16+color.u8[0];
GrPlot(gameCanvas, x+32, y-24);
dc->color = 16+color.u8[0];
GrPlot(dc, x+32, y-24);
}
}
}
@@ -90,6 +87,21 @@ I64 getActiveSpriteIndex(PPU2C02state *state) {
return -1;
}
I64 getActiveSpriteIndex16(PPU2C02state *state) {
U8 i;
for(i=0; i<state->num_sprites; ++i) {
if( state->sprites[i].x == 0 && state->sprites[i].shifts_remaining > 0 ) {
U8 bit_0 = (state->sprites[i].shift_register_2 & (1 << 7)) >> 7;
U8 bit_1 = (state->sprites[i].shift_register_3 & (1 << 7)) >> 7;
U8 bg_color_index = (bit_1 << 1) | bit_0;
if( bg_color_index > 0 ) {
return i;
}
}
}
return -1;
}
/******************
* fetching values
******************/
@@ -130,6 +142,18 @@ U0 renderPixel(PPU2C02state *state) {
bit_1 = (state->AT_shift_1 & (1 << shift)) >> shift;
U8 bg_at_index = (bit_1 << 1) | bit_0;
U16 palette_base;
U8 color_value;
U32 color;
I64 ofs = 0;
if (MMU.RAM[0x2000] & (1 << 5) != 0)
{
ofs = 8;
}
//8x8
//get sprite color index and active sprite
U8 sprite_color_index = 0;
I64 active_sprite_index = getActiveSpriteIndex(state);
@@ -139,27 +163,19 @@ U0 renderPixel(PPU2C02state *state) {
sprite_color_index = (bit_1 << 1) | bit_0;
}
U16 palette_base;
U8 color_value;
U32 color;
//draw the pixel on the screen, depending on color and priority
if( bg_color_index == 0 && sprite_color_index == 0 ) {
//setPixelColor(state->dot, state->scanline, ppu_colors[MMU.VRAM[0x3F00]]);
setPixelColor(state->dot, state->scanline, MMU.VRAM[0x3F00]);
}
else if( (sprite_color_index != 0 && bg_color_index == 0) ||
(sprite_color_index != 0 && bg_color_index != 0 && (state->sprites[active_sprite_index].byte2 & (1<<5)) == 0) ) {
//assert( active_sprite_index != -1 );
palette_base = getSpritePaletteBase(state->sprites[active_sprite_index].attribute);
color_value = readVRAM(palette_base + sprite_color_index);
//color = ppu_colors[color_value];
setPixelColor(state->dot, state->scanline, color_value);
}
else {
palette_base = getBackgroundPaletteBase(bg_at_index);
color_value = readVRAM(palette_base + bg_color_index);
//color = ppu_colors[color_value];
setPixelColor(state->dot, state->scanline, color_value);
}
@@ -167,6 +183,30 @@ U0 renderPixel(PPU2C02state *state) {
if( active_sprite_index != -1 && state->sprites[active_sprite_index].sprite_index == 0 && sprite_color_index != 0 && bg_color_index == 0 ) {
state->sprite_zero_hit = 1;
}
if (MMU.RAM[0x2000] & (1 << 5) != 0)
{ //8x16
sprite_color_index = 0;
active_sprite_index = getActiveSpriteIndex16(state);
if( active_sprite_index != -1 )
{
bit_0 = (state->sprites[active_sprite_index].shift_register_2 & (1 << 7)) >> 7;
bit_1 = (state->sprites[active_sprite_index].shift_register_3 & (1 << 7)) >> 7;
sprite_color_index = (bit_1 << 1) | bit_0;
}
if (sprite_color_index)
{
palette_base = getSpritePaletteBase(state->sprites[active_sprite_index].attribute);
color_value = readVRAM(palette_base + sprite_color_index);
setPixelColor(state->dot, state->scanline, color_value, Canvas16);
}
}
//handle sprite zero hit
if( active_sprite_index != -1 && state->sprites[active_sprite_index].sprite_index == 0 && sprite_color_index != 0 && bg_color_index == 0 ) {
state->sprite_zero_hit = 1;
}
}
/******************
@@ -179,39 +219,90 @@ U0 loadScanlineSprites(PPU2C02state *state) {
for(i=0; i<8; ++i) {
state->sprites[i].shift_register_0 = 0;
state->sprites[i].shift_register_1 = 0;
state->sprites[i].shift_register_2 = 0;
state->sprites[i].shift_register_3 = 0;
}
for(i=0x00; i<0xFF; i+=4) {
//assert( (MMU.RAM[0x2000] & (1 << 5)) == 0 ); //only allow 8x8 sprites
U8 y = readSPRRAM(i+0)+1;
U8 pattern_index = readSPRRAM(i+1);
U8 byte2 = readSPRRAM(i+2);
U8 x = readSPRRAM(i+3);
if (MMU.RAM[0x2000] & (1 << 5) != 0)
{
y += 8;
}
if( y <= state->scanline && y+8 > state->scanline ) {
U16 pattern_base = 0x0000;
if( (MMU.RAM[0x2000] & (1 << 3)) ) {
pattern_base = 0x1000;
if (MMU.RAM[0x2000] & (1 << 5) != 0)
{ //8x16
if( (pattern_index & 1) ) {
pattern_base = 0x1000;
}
else
{
pattern_base += 16;
}
}
else
{ //8x8
if( (MMU.RAM[0x2000] & (1 << 3)) ) {
pattern_base = 0x1000;
}
}
U16 palette_base = getSpritePaletteBase(byte2 & 3);
U8 pattern_0;
U8 pattern_1;
U8 pattern_2;
U8 pattern_3;
I64 row = state->scanline-y;
U8 pattern_0 = MMU.VRAM[pattern_base + (pattern_index*16+row)];
U8 pattern_1 = MMU.VRAM[pattern_base + (pattern_index*16+row+8)];
if (MMU.RAM[0x2000] & (1 << 5) != 0)
{
pattern_0 = readVRAM(pattern_base + (pattern_index*16+row));
pattern_1 = readVRAM(pattern_base + (pattern_index*16+row+8));
pattern_2 = readVRAM(pattern_base + (pattern_index*16+row-16));
pattern_3 = readVRAM(pattern_base + (pattern_index*16+row-8));
}
else
{
pattern_0 = readVRAM(pattern_base + (pattern_index*16+row));
pattern_1 = readVRAM(pattern_base + (pattern_index*16+row+8));
pattern_2 = readVRAM(pattern_base + (pattern_index*16+row-16));
pattern_3 = readVRAM(pattern_base + (pattern_index*16+row-8));
}
//flip y
if( byte2 & (1 << 7) ) {
pattern_0 = MMU.VRAM[pattern_base + (pattern_index*16+(7-row))];
pattern_1 = MMU.VRAM[pattern_base + (pattern_index*16+(7-row)+8)];
if (MMU.RAM[0x2000] & (1 << 5) != 0)
{
pattern_2 = readVRAM(pattern_base + (pattern_index*16+(7-row)));
pattern_3 = readVRAM(pattern_base + (pattern_index*16+(7-row)+8));
pattern_0 = readVRAM(pattern_base + (pattern_index*16+(7-row)-16));
pattern_1 = readVRAM(pattern_base + (pattern_index*16+(7-row)-8));
}
else
{
pattern_0 = readVRAM(pattern_base + (pattern_index*16+(7-row)));
pattern_1 = readVRAM(pattern_base + (pattern_index*16+(7-row)+8));
pattern_2 = readVRAM(pattern_base + (pattern_index*16+(7-row)-16));
pattern_3 = readVRAM(pattern_base + (pattern_index*16+(7-row)-8));
}
}
//flip x, reverse the bits in the patterns
if(byte2 & (1 << 6) ) {
U8 new_pattern_0 = 0;
U8 new_pattern_1 = 0;
U8 new_pattern_2 = 0;
U8 new_pattern_3 = 0;
I64 bit;
for(bit=0; bit<8; ++bit) {
new_pattern_0 <<= 1;
@@ -220,9 +311,17 @@ U0 loadScanlineSprites(PPU2C02state *state) {
new_pattern_1 <<= 1;
new_pattern_1 |= (pattern_1&1);
pattern_1 >>= 1;
new_pattern_2 <<= 1;
new_pattern_2 |= (pattern_2&1);
pattern_2 >>= 1;
new_pattern_3 <<= 1;
new_pattern_3 |= (pattern_3&1);
pattern_3 >>= 1;
}
pattern_0 = new_pattern_0;
pattern_1 = new_pattern_1;
pattern_2 = new_pattern_2;
pattern_3 = new_pattern_3;
}
state->sprites[state->num_sprites].sprite_index = i;
@@ -230,6 +329,8 @@ U0 loadScanlineSprites(PPU2C02state *state) {
state->sprites[state->num_sprites].attribute = (byte2 & 3);
state->sprites[state->num_sprites].shift_register_0 = pattern_0;
state->sprites[state->num_sprites].shift_register_1 = pattern_1;
state->sprites[state->num_sprites].shift_register_2 = pattern_2;
state->sprites[state->num_sprites].shift_register_3 = pattern_3;
state->sprites[state->num_sprites].shifts_remaining = 8;
state->sprites[state->num_sprites].byte2 = byte2;
@@ -248,16 +349,28 @@ U0 updatePPUrenderingData(PPU2C02state *state) {
state->bitmap_shift_0 &= ~1;
state->bitmap_shift_1 &= ~1;
state->bitmap_shift_2 <<= 1;
state->bitmap_shift_3 <<= 1;
state->bitmap_shift_2 &= ~1;
state->bitmap_shift_3 &= ~1;
state->AT_shift_0 <<= 1;
state->AT_shift_1 <<= 1;
state->AT_shift_0 &= ~1;
state->AT_shift_1 &= ~1;
state->AT_shift_2 <<= 1;
state->AT_shift_3 <<= 1;
state->AT_shift_2 &= ~1;
state->AT_shift_3 &= ~1;
I64 i;
for(i=0; i<state->num_sprites; ++i) {
if( state->sprites[i].x == 0 && state->sprites[i].shifts_remaining > 0) {
state->sprites[i].shift_register_0 <<= 1;
state->sprites[i].shift_register_1 <<= 1;
state->sprites[i].shift_register_2 <<= 1;
state->sprites[i].shift_register_3 <<= 1;
state->sprites[i].shifts_remaining -= 1;
}
else {
@@ -306,15 +419,19 @@ U0 fetchAttribute(PPU2C02state *state) {
U8 at = getAttributeTableValue(attribute_address, (MMU.VRAM_address & 0x001F)*8, ((MMU.VRAM_address & (0x001F << 5)) >> 5)*8);
if(at & 1) {
state->AT_shift_0_latch = 0xFF;
state->AT_shift_2_latch = 0xFF;
}
else {
state->AT_shift_0_latch = 0x00;
state->AT_shift_2_latch = 0x00;
}
if(at & 2) {
state->AT_shift_1_latch = 0xFF;
state->AT_shift_3_latch = 0xFF;
}
else {
state->AT_shift_1_latch = 0x00;
state->AT_shift_3_latch = 0x00;
}
}
@@ -334,7 +451,7 @@ U0 handleVisibleScanline(PPU2C02state *state) {
loadScanlineSprites(state);
return;
}
if( !rendering_enabled() ) {
if( !rendering_enabled ) {
return;
}
@@ -347,7 +464,9 @@ U0 handleVisibleScanline(PPU2C02state *state) {
if( MMU.RAM[0x2000] & (1 << 4) ) {
pattern_base = 0x1000;
}
U16 pattern_index = MMU.VRAM[state->nametable_base];
U16 pattern_index = readVRAM(state->nametable_base);
U8 row = ((MMU.VRAM_address&0x7000) >> 12);
if( state->dot < 256 || (state->dot > 320 && state->dot <= 336) ) {
@@ -357,9 +476,13 @@ U0 handleVisibleScanline(PPU2C02state *state) {
case 0:
state->bitmap_shift_0 |= state->bitmap_shift_0_latch;
state->bitmap_shift_1 |= state->bitmap_shift_1_latch;
state->bitmap_shift_2 |= state->bitmap_shift_2_latch;
state->bitmap_shift_3 |= state->bitmap_shift_3_latch;
state->AT_shift_0 |= state->AT_shift_0_latch;
state->AT_shift_1 |= state->AT_shift_1_latch;
state->AT_shift_2 |= state->AT_shift_2_latch;
state->AT_shift_3 |= state->AT_shift_3_latch;
horinc();
break;
@@ -370,13 +493,14 @@ U0 handleVisibleScanline(PPU2C02state *state) {
fetchAttribute(state);
break;
case 5:
state->bitmap_shift_0_latch = MMU.VRAM[pattern_base + pattern_index*16+row];
state->bitmap_shift_0_latch = readVRAM(pattern_base + pattern_index*16+row);
state->bitmap_shift_2_latch = readVRAM(pattern_base + pattern_index*16+row-16);
break;
case 7:
state->bitmap_shift_1_latch = MMU.VRAM[pattern_base + pattern_index*16+row+8];
state->bitmap_shift_1_latch = readVRAM(pattern_base + pattern_index*16+row+8);
state->bitmap_shift_3_latch = readVRAM(pattern_base + pattern_index*16+row-8);
break;
}
}
if( state->dot == 256 ) {
@@ -395,7 +519,6 @@ U8 PPUcycle(PPU2C02state *state) {
if( state->nmi_output && state->nmi_occurred ) {
state->nmi_occurred = 0;
//NMI(&CPU_state);
nmi6502;
}
@@ -404,15 +527,28 @@ U8 PPUcycle(PPU2C02state *state) {
if(state->dot == 341) {
state->scanline += 1;
state->dot = 0;
if (mapper == 18)
{
SS88006_irqctr--;
if (!SS88006_irqctr)
{
irq6502;
}
}
}
state->scanline %= 262;
//skip first cycle on odd frames
if(state->scanline == 0 && state->dot == 0 && state->odd_frame == 1 && rendering_enabled()) {
if(state->scanline == 0 && state->dot == 0 && state->odd_frame == 1 && rendering_enabled) {
state->dot = 1;
}
if ( state->scanline == 20 && state->dot == 0 && rendering_enabled && mapper == 4)
{
MMC3_clockIrqCounter;
}
//visible scanlines
if( state->scanline < 240 && rendering_enabled() ) {
if( state->scanline < 240 && rendering_enabled ) {
handleVisibleScanline(state);
//visible cycles
@@ -441,7 +577,7 @@ U8 PPUcycle(PPU2C02state *state) {
state->nmi_occurred = 0;
}
else if(state->dot >= 280 && state->dot <= 304 && rendering_enabled()) {
else if(state->dot >= 280 && state->dot <= 304 && rendering_enabled) {
//v: IHGF.ED CBA..... = t: IHGF.ED CBA.....
MMU.VRAM_address &= 1055; //1055 = 0000010000011111b
MMU.VRAM_address |= (MMU.t & ~1055 & ~(1<<15) );
@@ -449,5 +585,4 @@ U8 PPUcycle(PPU2C02state *state) {
}
return 0;
}
}
Regular → Executable
View File
+3 -5
View File
@@ -1,7 +1,7 @@
# templenes
NES Emulator for TempleOS
This is a work in progress. Currently, only Mapper #0 games work, and things are buggy. Tested on bare-metal and VirtualBox 6.0, YMMV.
This is a work in progress, and things are buggy. Tested on bare-metal and VirtualBox 6.0, YMMV.
The emulator runs in 320x200 256 color video mode. Since NES display resolution exceeds these boundaries, there is an option to view 1:1 (topmost pixels not visible) or Scale2Fit, which scales the image to the 320x200 viewport, albeit with loss of quality.
@@ -19,10 +19,8 @@ PPU/MMU is a modified version of [NESlig](https://github.com/toblu302/NESlig) co
# TODO
- Mappers
- Sound
- More mappers
- GUI Dialog boxes and other menu options
- Everything else
- Everything else
Regular → Executable
+2
View File
@@ -1,3 +1,5 @@
// vim: set ft=c:
#include "Load";
//TempleNES("some_rom_file.nes");
Regular → Executable
View File