/** --------------------------------------------------------------------------- \brief calculate instruction length of Addr \param \return \code \endcode -----------------------------------------------------------------------------*/ int GetInstructionLength(BYTE* Addr) { #ifdef _USE_LIBDASM_LIB INSTRUCTION instr = {0}; int Len = get_instruction(&instr, Addr, MODE_32); // check illegal opcode if (0 == Len) { _ASSERTE(!"get_instruction"); return -1; } #ifdef _DEBUG char string[256] = {0}; get_instruction_string(&instr, FORMAT_INTEL, 0, string, sizeof(string)); _tprintf(TEXT("%s\n"), string); #endif return Len; #else DISASSEMBLER Disassembler; INSTRUCTION * Instruction = NULL; if (TRUE != InitDisassembler(&Disassembler, ARCH_X86)) { _ASSERTE(!"InitDisassembler"); return -1; } ULONG Flags = DISASM_DISASSEMBLE | DISASM_DECODE | DISASM_STOPONERROR | DISASM_STOPONANOMALY | DISASM_STOPONRETURN; Instruction = GetInstruction(&Disassembler, (ULONG)Addr, (PBYTE)Addr, Flags); if (!Instruction) { _ASSERTE(!"GetInstruction"); CloseDisassembler(&Disassembler); return -1; } #ifdef _DEBUG DumpInstruction(Instruction, TRUE, TRUE); #endif int Len = Instruction->Length; CloseDisassembler(&Disassembler); return Len; #endif//_USE_LIBDASM_LIB }
void DumpExecuteBuffer(LPDIRECT3DEXECUTEBUFFER executeBuffer) { std::ostringstream str; D3DEXECUTEDATA data = {}; data.dwSize = sizeof(D3DEXECUTEDATA); D3DEXECUTEBUFFERDESC desc = {}; desc.dwSize = sizeof(D3DEXECUTEBUFFERDESC); executeBuffer->GetExecuteData(&data); executeBuffer->Lock(&desc); LogObjObject(data.dwInstructionOffset, data.dwInstructionLength, desc.dwBufferSize); str << "\tExecute Buffer: "; LPD3DTLVERTEX pVertex = (LPD3DTLVERTEX)((char*)desc.lpData + data.dwVertexOffset); for (DWORD index = 0; index < data.dwVertexCount; index++) { LogObjVertex(pVertex->sx, pVertex->sy, pVertex->sz); str << std::endl; str << "\t" << index << ":"; str << "\t( " << (int)pVertex->sx << "\t; " << (int)pVertex->sy << "\t; " << (int)pVertex->sz << " )"; str << "\t" << pVertex->rhw; str << "\t " << (void*)pVertex->color; str << "\t " << (void*)pVertex->specular; str << "\t( " << pVertex->tu << "\t; " << pVertex->tv << " )"; pVertex++; } char* pData = (char*)desc.lpData + data.dwInstructionOffset; char* pDataEnd = pData + data.dwInstructionLength; DWORD instructionIndex = 0; while (pData < pDataEnd) { LPD3DINSTRUCTION instruction = (LPD3DINSTRUCTION)pData; str << std::endl; str << "\t" << instructionIndex << ":"; DumpInstruction(str, instruction); pData += sizeof(D3DINSTRUCTION) + instruction->bSize * instruction->wCount; instructionIndex++; } executeBuffer->Unlock(); LogText(str.str()); }
void DumpExecuteBuffer(IDirect3DExecuteBuffer* executeBuffer) { std::ostringstream str; D3DEXECUTEDATA data; data.dwSize = sizeof(D3DEXECUTEDATA); D3DEXECUTEBUFFERDESC desc; desc.dwSize = sizeof(D3DEXECUTEBUFFERDESC); executeBuffer->GetExecuteData(&data); executeBuffer->Lock(&desc); str << "\tExecute Buffer:"; if (DumpExecuteBufferHasTriangling(data, desc)) { str << " triangling"; } LPD3DTLVERTEX pVertex = (LPD3DTLVERTEX)((char*)desc.lpData + data.dwVertexOffset); for (DWORD index = 0; index < data.dwVertexCount; index++) { D3DTLVERTEX v = *pVertex; pVertex++; v.sx = v.sx; v.sy = v.sy; v.sz = 1.0f - v.sz; v.rhw = 1.0f; str << std::endl; str << "\t" << index << ":"; str << "\t( " << v.sx << "\t; " << v.sy << "\t; " << v.sz << " )"; str << "\t" << v.rhw; str << "\t " << (void*)v.color; str << "\t " << (void*)v.specular; str << "\t( " << v.tu << "\t; " << v.tv << " )"; } char* pData = (char*)desc.lpData + data.dwInstructionOffset; char* pDataEnd = pData + data.dwInstructionLength; DWORD instructionIndex = 0; while (pData < pDataEnd) { LPD3DINSTRUCTION instruction = (LPD3DINSTRUCTION)pData; str << std::endl; str << "\t" << instructionIndex << ":"; DumpInstruction(str, instruction); pData += sizeof(D3DINSTRUCTION) + instruction->bSize * instruction->wCount; instructionIndex++; } executeBuffer->Unlock(); LogText(str.str()); }
int main( int numArgs, char ** args ) { Chip8 chip8; Api api; api.Initialise( ); FILE * fh = NULL; if ( fopen_s( &fh, "Pong.ch8", "rb" ) == 0 ) { fseek( fh, 0L, SEEK_END ); size_t fileSize = ftell( fh ); fseek( fh, 0L, SEEK_SET ); assert( sizeof( chip8.Memory ) - 0x200 >= fileSize ); int numRead = fread( &chip8.Memory[ 0x200 ], fileSize, 1, fh ); assert( numRead == 1 ); fclose( fh ); } else { assert( 0 ); return -1; } // Previous instruction (for debugging). address lastInstruction = 0x0000; // Last time we did our 60Hz update. Uint32 last60HzTime = SDL_GetTicks( ); // Instructions processed since last 60hz interval. Uint8 instructionsProcessedSinceLastUpdate = 0; // Loop forever. for ( ; ; ) { // Get time (in milliseconds). Uint32 timeNow = SDL_GetTicks( ); if ( instructionsProcessedSinceLastUpdate >= 15 ) { while ( timeNow - last60HzTime <= 1000.f / 60.f ) { SDL_Delay( 0 ); timeNow = SDL_GetTicks( ); } instructionsProcessedSinceLastUpdate = 0; } // If it has been 60Hz since our last update... if ( timeNow - last60HzTime > 1000.f / 60.f ) { // Decrement delay register. if ( chip8.Cpu.Regs.delay > 0 ) chip8.Cpu.Regs.delay--; // Decrement sound register. if ( chip8.Cpu.Regs.sound > 0 ) chip8.Cpu.Regs.sound--; // Update to know when next 60Hz timer should be issued. last60HzTime = timeNow; // Update API (render to screen, process keys, etc.) api.Tick( ); } // Play bleeping sound. api.SetSound( chip8.Cpu.Regs.sound > 0 ); Uint16 instruction = ( ( ( address )chip8.Memory[ chip8.Cpu.Regs.pc ] ) << 8 ) | ( address )chip8.Memory[ chip8.Cpu.Regs.pc + 1 ]; switch ( instruction ) { case 0x00e0: DumpInstruction( "Screen clear" ); api.ClearScreen( ); break; case 0x00ee: DumpInstruction( "Return from sub routine" ); // Pop current PC from stack. assert( chip8.Cpu.StackLevel ); chip8.Cpu.Regs.pc = chip8.Cpu.Stack[ --chip8.Cpu.StackLevel ]; break; default: switch ( instruction & 0xf000 ) { case 0x0000: { address addr = instruction & 0x0fff; DumpInstruction( "Calls RCA 1802 program at address 0x%x", addr ); // http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#0nnn // This instruction is only used on the old computers on which Chip-8 was originally implemented. It is ignored by modern interpreters. } break; case 0x1000: { address addr = instruction & 0x0fff; DumpInstruction( "Jump to 0x%x", addr ); // Set PC, we do -2 as PC is incremented by two after each instruction. chip8.Cpu.Regs.pc = addr - 2; } break; case 0x2000: { address addr = instruction & 0x0fff; DumpInstruction( "Call sub routine to 0x%x", addr ); // Store current PC on stack. chip8.Cpu.Stack[ chip8.Cpu.StackLevel++ ] = chip8.Cpu.Regs.pc; assert( chip8.Cpu.StackLevel < 16 ); // Set PC, we do -2 as PC is incremented by two after each instruction. chip8.Cpu.Regs.pc = addr - 2; } break; case 0x3000: { int reg = ( instruction & 0x0f00 ) >> 8; int val = instruction & 0x00ff; DumpInstruction( "Skips next instruction if v%02d equals %d", reg, val ); if ( chip8.Cpu.Regs.v[ reg ] == val ) { chip8.Cpu.Regs.pc += 2; } } break; case 0x4000: { int reg = ( instruction & 0x0f00 ) >> 8; int val = instruction & 0x00ff; DumpInstruction( "Skips next instruction if v%02d does not equals %d", reg, val ); if ( chip8.Cpu.Regs.v[ reg ] != val ) { chip8.Cpu.Regs.pc += 2; } } break; case 0x5000: { int reg1 = ( instruction & 0x0f00 ) >> 8; int reg2 = ( instruction & 0x00f0 ) >> 4; DumpInstruction( "Skips next instruction if v%02d equals v%02d", reg1, reg2 ); if ( chip8.Cpu.Regs.v[ reg1 ] == chip8.Cpu.Regs.v[ reg2 ] ) { chip8.Cpu.Regs.pc += 2; } } break; case 0x6000: { int reg = ( instruction & 0x0f00 ) >> 8; int val = instruction & 0x00ff; DumpInstruction( "Set v%02d to %d", reg, val ); chip8.Cpu.Regs.v[ reg ] = val; } break; case 0x7000: { int reg = ( instruction & 0x0f00 ) >> 8; int val = instruction & 0x00ff; DumpInstruction( "Add %d to v%02d", val, reg ); chip8.Cpu.Regs.v[ reg ] += val; } break; case 0x8000: { int reg1 = ( instruction & 0x0f00 ) >> 8; int reg2 = ( instruction & 0x00f0 ) >> 4; switch ( instruction & 0x000f ) { case 0: DumpInstruction( "Sets v%02d to v%02d", reg1, reg2 ); chip8.Cpu.Regs.v[ reg1 ] = chip8.Cpu.Regs.v[ reg2 ]; break; case 1: DumpInstruction( "Sets v%02d to v%02d OR v%02d", reg1, reg1, reg2 ); chip8.Cpu.Regs.v[ reg1 ] = chip8.Cpu.Regs.v[ reg1 ] | chip8.Cpu.Regs.v[ reg2 ]; break; case 2: DumpInstruction( "Sets v%02d to v%02d AND v%02d", reg1, reg1, reg2 ); chip8.Cpu.Regs.v[ reg1 ] = chip8.Cpu.Regs.v[ reg1 ] & chip8.Cpu.Regs.v[ reg2 ]; break; case 3: DumpInstruction( "Sets v%02d to v%02d XOR v%02d", reg1, reg1, reg2 ); chip8.Cpu.Regs.v[ reg1 ] = chip8.Cpu.Regs.v[ reg1 ] ^ chip8.Cpu.Regs.v[ reg2 ]; break; case 4: { DumpInstruction( "Sets v%02d to v%02d + v%02d [VF = 1 when there is a carry, 0 when not]", reg1, reg1, reg2 ); unsigned __int16 result = chip8.Cpu.Regs.v[ reg1 ] + chip8.Cpu.Regs.v[ reg2 ]; chip8.Cpu.Regs.v[ reg1 ] = result & 0xff; chip8.Cpu.Regs.v[ 0xf ] = ( result > 0xff ) ? 1 : 0; } break; case 5: DumpInstruction( "Sets v%02d to v%02d - v%02d [VF = 0 when there is a borrow, 1 when not]", reg1, reg1, reg2 ); chip8.Cpu.Regs.v[ 0xf ] = ( chip8.Cpu.Regs.v[ reg1 ] >= chip8.Cpu.Regs.v[ reg2 ] ) ? 1 : 0; chip8.Cpu.Regs.v[ reg1 ] = chip8.Cpu.Regs.v[ reg1 ] - chip8.Cpu.Regs.v[ reg2 ]; break; case 6: DumpInstruction( "Shift v%02d right by one [VF is set to the least significant bit of v%02d before the shift]", reg1, reg1 ); chip8.Cpu.Regs.v[ 0xf ] = chip8.Cpu.Regs.v[ reg1 ] & 0x1; chip8.Cpu.Regs.v[ reg1 ] >>= 1; break; case 7: DumpInstruction( "Sets v%02d to v%02d - v%02d [VF = 0 when there is a borrow, 1 when not]", reg1, reg2, reg1 ); chip8.Cpu.Regs.v[ 0xf ] = ( chip8.Cpu.Regs.v[ reg2 ] >= chip8.Cpu.Regs.v[ reg1 ] ) ? 1 : 0; chip8.Cpu.Regs.v[ reg1 ] = chip8.Cpu.Regs.v[ reg2 ] - chip8.Cpu.Regs.v[ reg1 ]; break; case 0xe: DumpInstruction( "Shift v%02d left by one [VF is set to the most significant bit of v%02d before the shift]", reg1, reg1 ); chip8.Cpu.Regs.v[ 0xf ] = ( chip8.Cpu.Regs.v[ reg1 ] & 0x80 ) ? 1 : 0; chip8.Cpu.Regs.v[ reg1 ] <<= 1; break; default: assert( 0 ); } } break; case 0x9000: { int reg1 = ( instruction & 0x0f00 ) >> 8; int reg2 = ( instruction & 0x00f0 ) >> 4; DumpInstruction( "Skips next instruction if v%02d does not equals v%02d", reg1, reg2 ); if ( chip8.Cpu.Regs.v[ reg1 ] != chip8.Cpu.Regs.v[ reg2 ] ) { chip8.Cpu.Regs.pc += 2; } } break; case 0xa000: { address addr = instruction & 0x0fff; DumpInstruction( "Set i to 0x%x", addr ); chip8.Cpu.Regs.i = addr; } break; case 0xb000: { address addr = instruction & 0x0fff; DumpInstruction( "Jump to address 0x%x plus v0", addr ); // Set PC, we do -2 as PC is incremented by two after each instruction. chip8.Cpu.Regs.pc = chip8.Cpu.Regs.v[ 0 ] + addr - 2; } break; case 0xc000: { int reg = ( instruction & 0x0f00 ) >> 8; int val = instruction & 0x00ff; DumpInstruction( "Set v%02d to random number and %d", reg, val ); chip8.Cpu.Regs.v[ reg ] = ( rand( ) % 255 ) & val; } break; case 0xd000: { int reg1 = ( instruction & 0x0f00 ) >> 8; int reg2 = ( instruction & 0x00f0 ) >> 4; int val = instruction & 0x000f; DumpInstruction( "Draw sprite at v%02d,v%02d width 8, height %d", reg1, reg2, val ); if ( api.DrawAt( chip8.Cpu.Regs.v[ reg1 ], chip8.Cpu.Regs.v[ reg2 ], val, &chip8.Memory[ chip8.Cpu.Regs.i ] ) ) { chip8.Cpu.Regs.v[ 0xf ] = 1; } else { chip8.Cpu.Regs.v[ 0xf ] = 0; } // Each row drawn from address in i register (a bit rows are 8 bits in i register, MSB on left). // If any pixels are turned off from this v[15] is set to 1, otherwise v[15] is set to 0. } break; case 0xe000: { int reg = ( instruction & 0x0f00 ) >> 8; switch ( instruction & 0x00ff ) { case 0x9e: DumpInstruction( "Skip next instruction if key stored in v%02d is pressed", reg ); if ( api.IsKeyDown( chip8.Cpu.Regs.v[ reg ] ) ) { chip8.Cpu.Regs.pc += 2; } break; case 0xa1: DumpInstruction( "Skip next instruction if key stored in v%02d is not pressed", reg ); if ( ! api.IsKeyDown( chip8.Cpu.Regs.v[ reg ] ) ) { chip8.Cpu.Regs.pc += 2; } break; default: assert( 0 ); } } break; case 0xf000: { int reg = ( instruction & 0x0f00 ) >> 8; switch ( instruction & 0x00ff ) { case 0x7: DumpInstruction( "Set v%02d to value of the delay timer", reg ); chip8.Cpu.Regs.v[ reg ] = chip8.Cpu.Regs.delay; break; case 0xa: DumpInstruction( "Key press awaited and then stored in v%02d", reg ); assert( 0 ); break; case 0x15: DumpInstruction( "Set delay timer to v%02d", reg ); chip8.Cpu.Regs.delay = chip8.Cpu.Regs.v[ reg ]; break; case 0x18: DumpInstruction( "Set sound timer to v%02d", reg ); chip8.Cpu.Regs.sound = chip8.Cpu.Regs.v[ reg ]; break; case 0x1e: DumpInstruction( "Sets i to i + v%02d", reg ); chip8.Cpu.Regs.i += chip8.Cpu.Regs.v[ reg ]; break; case 0x29: // Characters 0-F (in hexadecimal) are represented by a 4x5 font. DumpInstruction( "Sets i to the location of the sprite for the character in v%02d", reg ); chip8.Cpu.Regs.i = chip8.Cpu.Regs.v[ reg ] * 5; break; case 0x33: { // With the most significant of three digits at the address in I, the middle digit at I plus 1, and the least significant digit at I plus 2. // In other words, take the decimal representation of VX, place the hundreds digit in memory at location in I, the tens digit at location I+1, and the ones digit at location I+2. DumpInstruction( "Stores Binary-coded decimal representation of v%02d in i", reg ); int hundreds = chip8.Cpu.Regs.v[ reg ] / 100; int tens = ( chip8.Cpu.Regs.v[ reg ] / 10 ) % 10; int units = chip8.Cpu.Regs.v[ reg ] % 10; chip8.Memory[ chip8.Cpu.Regs.i + 0 ] = hundreds; chip8.Memory[ chip8.Cpu.Regs.i + 1 ] = tens; chip8.Memory[ chip8.Cpu.Regs.i + 2 ] = units; } break; case 0x55: DumpInstruction( "Stores v0 to v%02d in memory starting at i", reg ); for ( int ix = 0; ix < reg + 1; ++ix ) { chip8.Memory[ chip8.Cpu.Regs.i + ix ] = chip8.Cpu.Regs.v[ ix ]; } break; case 0x65: DumpInstruction( "Fills v0 to v%02d with memory starting at i", reg ); for ( int ix = 0; ix < reg + 1; ++ix ) { chip8.Cpu.Regs.v[ ix ] = chip8.Memory[ chip8.Cpu.Regs.i + ix ]; } break; default: assert( 0 ); } } break; default: assert( 0 ); } } // Update last instruction (for debugging). lastInstruction = instruction; // Jump forward to next instruction. chip8.Cpu.Regs.pc += 2; // Increment instruction counter. instructionsProcessedSinceLastUpdate++; } api.Destroy( ); return 0; }