I hope there's some people that visit here that are familiar with D3D9...
I'm wanting to port some emulators to the 360. Having no idea about how an emulator works I decided to code a basic one from scratch so I could learn the process, at least then I'd have some background knowledge when it comes to porting something a bit more complex.
I decided to start with the Chip 8, some old school 1970's thing that plays shit like pong.
So I have this thing coded and working minus the display. In my debug logs I can see it processing all the opcodes etc - I haven't coded proper input yet (That will be piss easy once I have a display) so for now every time it check for input I just return an up or down key press.
I have a screen buffer for the emulator, it's a char array, 1 = White, 0 = black.
CODE
unsigned char screen[64*32]
Now I need to take this buffer and translate that to D3D9 instructions.
What was suggested to me was to create a texture, then each update lock it, clear it out and individually fill in the pixels depending on what I had in my screen buffer, then unlock it and display it.
Vincent helped me for a few hours and nothing he suggested was working and then he came across this little gem in the sdk docs:
QUOTE
Textures are stored in a device dependent format on the Xbox 360. The Windows trick of locating a pixel by computing row*pitch+col*texelsize does not work because the textures are stored in a tiled format.
QUOTE
The only way to find a pixel is to use the appropriate XGraphics function (XGAddress2DTiledExtent, XGAddress2DTiledOffset, XGAddress2DTiledX, or XGAddress2DTiledY)
Vincent's Xbox has rrod so he can't test things out himself unfortunately.
So forging ahead by myself now I figure I need to use
CODE
XGAddress2DTiledOffset(
UINT x, //X-coordinate of the block.
UINT y, //X-coordinate of the block.
UINT Width, //Pitch of the image, in blocks.
UINT TexelPitch //Size of an image block, in bytes.
)
Which should return the offset of the specified pixel within the texture. My problem is... I'm useless with this and am ready to murder who ever invented D3D9.
Honestly all I need to do is to figure out how to make a pixel either white or black, I can figure out the code to do either or depending on whats in my screen buffer but for now if someone can just show me how to locate 1 pixel and make it either white or black I would be thrilled!
The closest I've got so far is accidentally making the top half of my rectangle black when I was tyring to make the whole thing white (When it's created it's filled with random data so if I don't odify it then it's just a series of random colours)
Here's my code:
CODE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "xfilecache.h"
#include
#include "AtgApp.h"
#include "AtgFont.h"
#include "AtgHelp.h"
#include "AtgInput.h"
#include "AtgResource.h"
#include "AtgSignIn.h"
#include "AtgUtil.h"
#include "AtgSimpleShaders.h"
#include "AtgDebugDraw.h"
using namespace std;
typedef unsigned char u8;
typedef unsigned char byte;
typedef unsigned short u16;
u8 V[16] = {0x0}; // 16 8-bit registers V0 - VF
u16 I = 0x0; // Memory address register
u8 SP = 0x0; // Stack pointer is used to point to the topmost level of the stack.
u16 Stack[16] = {0x0}; // The stack is an array of 16 16-bit values, used to store the address that the interpreter shoud return to when finished with a subroutine
u16 PC = 0x200; // Program counter starting at mem address 0x200 because on the original console the OS took up the first 200 bytes
u8 Memory[0xFFF] = {0x0}; // 4KB of memory
u16 Opcode = 0x0; // Current instruction
byte dtime; // Delay timer
byte stime; // Sound timer
byte keyval; // Key press
byte vx, yline, data, vxval, vyval; // Screen data
byte screen[2049]; // New screen to display
byte old_screen[2049]; // Old screen just displayed
int count = 0;
bool end = false;
bool debugEnabled = true;
IDirect3DTexture9* display;
D3DLOCKED_RECT rect;
void debugLog(char* output)
{
if (debugEnabled)
{
ofstream writeLog;
writeLog.open("game:\\debug.log",ofstream::app);
if (writeLog.is_open())
{
writeLog.write(output,strlen(output));
writeLog.write("\n",1);
}
writeLog.close();
}
}
// Controls //
byte check_keys()
{
byte keyval = 0;
int xx = rand();
if (xx > 100)
{
keyval = 5;
}
else
{
keyval = 8;
}
return keyval;
}
// CPU //
void cpu(){
Opcode = ((Memory[PC]<<8) + Memory[PC+1]);
switch (Opcode&0xF000)
{
case 0x0000:
switch(Opcode&0x00FF)
{
case 0x00E0:
debugLog("00E0\n");
for(int i=0; i <2047; i++ ){
screen = 0;
}
PC += 2;
break;
case 0x00EE:
debugLog("00EE\n");
SP -= 1;
PC = Stack[SP];
PC += 2;
break;
case 0X00FF:
debugLog("00FF\n");
PC+=2;
break;
default:
debugLog("Opcode not found\n");
debugLog((char*)PC);
end = true;
break;
}
break;
case 0x1000:
debugLog("1NNN\n");
PC = (Opcode&0x0FFF);
break;
case 0x2000:
debugLog("2NNN\n");
Stack[SP] = PC;
SP += 1;
PC = (Opcode&0x0FFF);
break;
case 0x3000:
debugLog("3XNN\n");
if(V[((Opcode&0x0F00)>>8)] == (Opcode&0x00FF))
{
PC += 4;
}
else
{
PC +=2;
}
break;
case 0x4000:
debugLog("4XNN\n");
if(V[((Opcode&0x0F00)>>8)] != (Opcode&0x00FF))
{
PC += 4;
}
else
{
PC +=2;
}
break;
case 0x5000:
debugLog("5XY0\n");
if(V[((Opcode&0x0F00)>>8)] == V[((Opcode&0x00F0)>>4)])
{
PC += 4;
}
else
{
PC +=2;
}
break;
case 0x6000:
debugLog("6XNN\n");
V[((Opcode&0x0F00)>>8)] = (Opcode&0x00FF);
PC+=2;
break;
case 0x7000:
debugLog("7XNN\n");
V[((Opcode&0x0F00)>>8)]=(V[((Opcode&0x0F00)>>8)]+(Opcode&0x00FF));
PC+=2;
break;
case 0x8000:
switch (Opcode&0x000F){
case 0x0:
debugLog("8XY0\n");
V[((Opcode&0x0F00)>>8)]=V[((Opcode&0x00F0)>>4)];
PC+=2;
break;
case 0x1:
debugLog("8XY1\n");
V[((Opcode&0x0F00)>>8)]=(V[((Opcode&0x0F00)>>8)]|V[((Opcode&0x00F0)>>4)]);
PC+=2;
break;
case 0x2:
debugLog("8XY2\n");
V[((Opcode&0x0F00)>>8)]=(V[((Opcode&0x0F00)>>8)]&V[((Opcode&0x00F0)>>4)]);
PC+=2;
break;
case 0x3:
debugLog("8XY3\n");
V[((Opcode&0x0F00)>>8)]=(V[((Opcode&0x0F00)>>8)]^V[((Opcode&0x00F0)>>4)]);
PC+=2;
break;
case 0x4:
debugLog("8XY4\n");
if((V[((Opcode&0x00F0)>>4)]+V[((Opcode&0x0F00)>>8)]) > 0xFF)
{
V[0xF] = 0x01;
}
else
{
V[0xF] = 0x00;
}
V[((Opcode&0x0F00)>>8)]+=V[((Opcode&0x00F0)>>4)];
PC+=2;
break;
case 0x5:
debugLog("8XY5\n");
if (V[((Opcode&0x00F0)>>4)]>(V[((Opcode&0x0F00)>>8)]))
{
V[0xF]=00;
}
else
{
V[0xF]=1;
}
V[((Opcode&0x0F00)>>8)]-=V[((Opcode&0x00F0)>>4)];
PC+=2;
break;
case 0x6:
debugLog("8XY6\n");
V[0xF]=(V[((Opcode&0x0F00)>>8)]&0x1);
V[((Opcode&0x0F00)>>8)]>>=1;
PC+=2;
break;
case 0x7:
debugLog("8XY7\n");
if(V[((Opcode&0x00F0)>>4)] < V[((Opcode&0x0F00)>>8)])
{
V[0xF] = 0x00;
}
else
{
V[0xF] = 0x01;
}
V[((Opcode&0x0F00)>>8)] = V[((Opcode&0x00F0)>>4)] - V[((Opcode&0x0F00)>>8)];
PC+=2;
break;
case 0xE:
debugLog("8XYE\n");
V[0xF] = ((V[((Opcode&0x0F00)>>8)]&0x80)>>7);
V[((Opcode&0x0F00)>>8)] <<=1;
PC+=2;
break;
default:
debugLog("Opcode not found\n");
debugLog((char*)PC);
end = true;
break;
}
break;
case 0x9000:
debugLog("9XY0\n");
if (V[((Opcode&0x0F00)>>8)] != V[((Opcode&0x00F0)>>4)])
{
PC += 4;
}
else
{
PC += 2;
}
break;
case 0xA000:
debugLog("ANNN\n");
I = (Opcode&0x0FFF);
PC += 2;
break;
case 0xB000:
debugLog("BNNN\n");
PC = (Opcode&0x0FFF) + V[0x0];
break;
case 0xC000:
debugLog("CXNN\n");
V[((Opcode&0x0F00)>>8)] = (rand()&(Opcode&0x00FF));
PC += 2;
break;
case 0xD000:
debugLog("DXYN\n");
vxval = V[(Opcode&0x0F00)>>8];
vyval = V[(Opcode&0x00F0)>>4];
if ((Opcode&0x000F)==0)
for(yline=0; yline<16; yline++ ){
data = Memory[I+yline];
for(vx=0; vx<16; vx++){
if((data&(0x80>>vx))!=0){
if(screen[ vx + vxval + ((vyval+yline) * 64)] == 1)
V[15] = 1;
screen[ vx + vxval + ((vyval+yline) * 64)] ^= 1;
}
}
}
else{
for (yline=0;yline<(Opcode&0x000F);yline++){
data = (Memory[I+yline]);
for(vx=0;vx<8;vx++){
if ((data&(0x80>>vx))!=0){
if ( screen[ vx + vxval + (( vyval + yline) * 64)]==1)
V[15]=1;
screen[ vx + vxval + ( (vyval + yline) * 64)]^=1;
}
}
}
}
PC+=2;
// RenderGame( pDevice );
for(yline=0; yline<32; yline++)
for(vx=0; vx<64; vx++)
old_screen[vx + (yline *64)] = screen[vx + (yline *64)];
break;
case 0xE000:
switch (Opcode&0x00FF){
case 0x9E:
debugLog("EX9E\n");
V[((Opcode&0x0F00)>>8)] = check_keys();
PC +=2;
break;
case 0xA1:
debugLog("EXA1\n");
V[((Opcode&0x0F00)>>8)] = check_keys();
PC +=2;
break;
}
break;
case 0xF000:
switch (Opcode&0x00FF){
case 0x07:
debugLog("FX07\n");
V[((Opcode&0x0F00)>>8)] = dtime;
PC+=2;
break;
case 0x0A:
debugLog("FX0A\n");
keyval=check_keys();
V[((Opcode&0x0F00)>>8)]=keyval;
PC+=2;
break;
case 0x15:
debugLog("FX15\n");
dtime = V[((Opcode&0x0F00)>>8)];
PC+=2;
break;
case 0x18:
debugLog("FX18\n");
stime = V[((Opcode&0x0F00)>>8)];
PC+=2;
break;
case 0x1E:
debugLog("FX1E\n");
I += V[((Opcode&0x0F00)>>8)];
PC+=2;
break;
case 0x29:
debugLog("FX29\n");
I = (V[((Opcode&0x0F00)>>8)]*5);
PC+=2;
break;
case 0x30:
debugLog("FX30\n");
I = (V[((Opcode&0x0F00)>>8)]*10);
PC+=2;
break;
case 0x33:
debugLog("FX33\n");
Memory = (V[((Opcode&0x0F00)>>8)]/100);
Memory[I+1] = ((V[((Opcode&0x0F00)>>8)]/10)%10);
Memory[I+2] = ((V[((Opcode&0x0F00)>>8)]%100)%10);
PC+=2;
break;
case 0x55:
debugLog("FX55\n");
for(int i=0; i<=((Opcode&0x0F00)>>8); i++ ){
Memory[I+i] = V;
}
PC+=2;
break;
case 0x65:
debugLog("FX65\n");
for(int i=0; i<=((Opcode&0x0F00)>>8); i++ ){
V = Memory[I+i];
}
PC+=2;
break;
default:
debugLog("Opcode not found\n");
debugLog((char*)PC);
end = true;
break;
}
break;
default:
debugLog("Opcode not found\n");
debugLog((char*)PC);
end = true;
break;
}
}
class Sample : public ATG::Application
{
ATG::Font m_Font;
DWORD m_dwFirstUserIndex;
XINPUT_STATE m_InputState;
private:
virtual HRESULT Initialize();
virtual HRESULT Update();
virtual HRESULT Render();
};
void DrawTextureScaled( LPDIRECT3DTEXTURE9 curTexture, int x, int y, int w, int h )
{
D3DRECT Rect;
Rect.x1 = x;
Rect.y1 = y;
Rect.x2 = x + w;
Rect.y2 = y + h;
ATG::DebugDraw::DrawScreenSpaceTexturedRect( Rect, curTexture, 0 );
}
HRESULT Sample::Initialize()
{
ifstream inFile;
inFile.open("game:\\pong.ch8",ifstream::binary);
if(inFile.is_open()){debugLog("pong open");}
inFile.read((char*)&Memory[0x200],0xFFF);
inFile.close();
int x, y;
for(y=0; y<32; y++)
for(x=0; x<64; x++)
{
old_screen[x + (y *64)] = 0;
screen[x + (y *64)] = 0;
}
if( FAILED( m_Font.Create( "game:\\Media\\Fonts\\Arial_16.xpr" ) ) )
return ATGAPPERR_MEDIANOTFOUND;
m_Font.SetWindow( ATG::GetTitleSafeArea() );
ATG::SimpleShaders::Initialize( NULL, NULL );
m_pd3dDevice ->CreateTexture(64, 32, 1, 0, D3DFMT_R8G8_B8G8 , D3DPOOL_DEFAULT, &display, 0);
return S_OK;
}
HRESULT Sample::Update()
{
display->LockRect(0,&rect,NULL, NULL); ////////////////////////////////////In between lock and unlock is where the magic needs to happen!!!!
BYTE* pByte =(BYTE*)rect.pBits;
for (BYTE y=0;y<32;++y)
{
for (BYTE x=0;x<64;++x)
{
//pByte[XGAddress2DTiledOffset(x,y,256,4)] = (BYTE)1;
//pByte[XGAddress2DTiledOffset(x,y,256,4)+1] = (BYTE)1;
//pByte[XGAddress2DTiledOffset(x,y,256,4)+2] = (BYTE)1;
//pByte[XGAddress2DTiledOffset(x,y,256,4)+3] = (BYTE)1;
}
}
display->UnlockRect(0);
cpu();
if (dtime>0)dtime-=1;
if (stime>0)stime-=1;
return S_OK;
}
HRESULT Sample::Render()
{
ATG::RenderBackground( 0xFFFFFFFF, 0x00000000 );
DrawTextureScaled(display,20,20,64*10,32*10);
m_pd3dDevice->Present( NULL, NULL, NULL, NULL );
return S_OK;
}
VOID __cdecl main()
{
Sample atgApp;
ATG::GetVideoSettings( &atgApp.m_d3dpp.BackBufferWidth, &atgApp.m_d3dpp.BackBufferHeight );
atgApp.Run();
}
This is what I get on screen with the code above (The shit in the rectangle is just due to the random data inside the texture when it's created - I'm not modifying anything as I don't know how:
(IMG:http://i18.photobucket.com/albums/b145/Powerslayer/IMG_2051.jpg)
And here's my debug log so I know that in the background everyhting is processing fine (This is A LOT bigger, I've just taken a bit from the top of it):
CODE
pong open
6XNN
6XNN
6XNN
6XNN
ANNN
DXYN
DXYN
6XNN
2NNN
ANNN
FX33
FX65
FX29
6XNN
6XNN
DXYN
7XNN
FX29
DXYN
00EE
It's been a while since I used d3d9 but I will give it a shot. The main thing is you are using D3DFMT_R8G8_B8G8 which is a 32 bit texture format you need to use an array of 32, not BYTE
CODE
IDirect3DSurface9 * surface = 0;
display->GetSurfaceLevel(0, &surface);
surface->LockRect(0,&rect,NULL, D3DUSAGE_WRITEONLY); // I am not sure if you need write only but I added it anyway...
int* pInt =(int*)rect.pBits;
for (BYTE y=0;y<32;++y)
{
for (BYTE x=0;x<64;++)
pInt[y*32+x] = (int)1; // 0xff ... ?
}
surface->UnlockRect();
surface->Release();
This post has been edited by Sascoo: Jan 23 2010, 12:03 AM
Thank you lantus! That info about the Lin format made everything work (IMG:style_emoticons/default/smile.gif) I have to thank Vincent and Fallen93 though because they put up with me for hours trying to sort it out, just that texture format was the key (IMG:style_emoticons/default/smile.gif)
CODE
m_pd3dDevice ->CreateTexture(64, 32, 1, 0, D3DFMT_LIN_A8R8G8B8, D3DPOOL_DEFAULT, &display, 0);
display->LockRect(0,&rect,NULL, NULL);
BYTE* pByte =(BYTE*)rect.pBits;
for (DWORD y=0;y<32;++y)
{
for (DWORD x=0;x<64;++x)
{
DWORD index = (x*4)+(y*rect.Pitch);
pByte[index] = (BYTE)(screen[(y*64) + x] == 0 ? 0x00 : 0xFF);
pByte[index+1] = (BYTE)(screen[(y*64) + x] == 0 ? 0x00 : 0xFF);
pByte[index+2] = (BYTE)(screen[(y*64) + x] == 0 ? 0x00 : 0xFF);
pByte[index+3] = (BYTE)(screen[(y*64) + x] == 0 ? 0x00 : 0xFF);
}
}
display->UnlockRect(0);
(IMG:http://i18.photobucket.com/albums/b145/Powerslayer/IMG_2060.jpg)