Note
Just to inform you that I have just started with a new blog series in which I will be implementing a NES emulator in JavaScript.Here is the link:
http://nesemujs.blogspot.com
Enjoy!
... case WAVE_TRI: if (v->ring) output = TriTable[(v->count ^ (v->mod_by->count & 0x800000)) >> 11]; else output = TriTable[v->count >> 11]; break; case WAVE_SAW: output = v->count >> 8; break; case WAVE_RECT: if (v->count > (uint32)(v->pw << 12)) output = 0xffff; else output = 0; break; ... case WAVE_NOISE: if (v->count > 0x100000) { output = v->noise = sid_random() << 8; v->count &= 0xfffff; } else output = v->noise; break; ...
Note that too large buffers will not workThe resulting size of the audio buffer that Frodo uses is 512 samples.
very well: The speed of the C64 is slowed down to an average speed of
100% by the blocking write() call in EmulateLine(). If you use a buffer
of, say 4096 bytes, that will happen only about every 4 frames, which
means that the emulation runs much faster in some frames, and much
slower in others.
On really fast machines, it might make sense to use an even smaller
buffer size.
public void initAudio(int freq, int bits, int sound_packet_length) { if (audio == null) { audio = new AudioTrack(AudioManager.STREAM_MUSIC, freq, AudioFormat.CHANNEL_CONFIGURATION_MONO, bits == 8?AudioFormat.ENCODING_PCM_8BIT: AudioFormat.ENCODING_PCM_16BIT, freq==44100?32*1024:16*1024, AudioTrack.MODE_STREAM); audio.play(); } } public void sendAudio(short data []) { if (audio != null) { long startTime = System.currentTimeMillis(); audio.write(data, 0, data.length); long endTime = System.currentTimeMillis(); System.out.println(endTime - startTime); } }
jint JNI_OnLoad(JavaVM* aVm, void* aReserved) { gJavaVM = aVm; return JNI_VERSION_1_6; }
void EmulateLine() { ... if (global_env == NULL) { (*gJavaVM)->AttachCurrentThread (gJavaVM, &global_env, NULL); } ... }
... jobject currentActivity; jmethodID initAudio; jmethodID sendAudio; ... void Java_com_johan_emulator_engine_Emu6502_setMainActivityObject(JNIEnv* env, jobject pObj, jobject activity) { currentActivity = (*env)->NewGlobalRef(env,activity); jclass thisClass = (*env)->GetObjectClass(env,currentActivity); initAudio = (*env)->GetMethodID(env, thisClass, "initAudio", "(III)V"); sendAudio = (*env)->GetMethodID(env, thisClass, "sendAudio", "([S)V"); }
void EmulateLine() { static int divisor = 0; static int to_output = 0; static int buffer_pos = 0; static int loop_n = 2; static int loop_c = 0; if (!ready) return; if (global_env == NULL) { (*gJavaVM)->AttachCurrentThread (gJavaVM, &global_env, NULL); } sample_buf[sample_in_ptr] = volume; sample_in_ptr = (sample_in_ptr + 1) % SAMPLE_BUF_SIZE; // Now see how many samples have to be added for this line divisor += SAMPLE_FREQ; while (divisor >= 0) divisor -= 312*50, to_output++; // Calculate the sound data only when we have enough to fill // the buffer entirely if ((buffer_pos + to_output) >= sndbufsize) { int datalen = sndbufsize - buffer_pos; to_output -= datalen; calc_buffer(sound_buffer + buffer_pos, datalen*2); if (!audioarray) { jshortArray tempArrayRef = (*global_env)->NewShortArray(global_env, sndbufsize*loop_n); audioarray = (*global_env)->NewGlobalRef(global_env,tempArrayRef); } (*global_env)->SetShortArrayRegion(global_env, audioarray, loop_c*sndbufsize, sndbufsize, sound_buffer); loop_c++; if (loop_c == loop_n) { (*global_env)->CallVoidMethod(global_env, currentActivity, sendAudio, audioarray); loop_c = 0; } buffer_pos = 0; } }
... <android.opengl.GLSurfaceView android:id="@+id/Video" android:layout_width="match_parent" android:layout_height="100px" /> ...
... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_front); GLSurfaceView mGLSurfaceView = (GLSurfaceView) findViewById(R.id.Video); mGLSurfaceView.setEGLContextClientVersion(2); MyGL20Renderer myRenderer = new MyGL20Renderer(this); mGLSurfaceView.setRenderer(myRenderer); ... } ...
public void onDrawFrame(GL10 unused) { GLES20.glDisable(GLES20.GL_DEPTH_TEST); GLES20.glEnable(GLES20.GL_BLEND); GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); GLES20.glDepthMask(false); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); //Set the camera position (View Matrix) Matrix.setLookAtM(mVMatrix, 0, 0, 0, 3, 0f, 0f, 0f, 0f, 1.0f, 0.0f); //Calculate the projection and view transformation Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0); //Create a rotation transformation for the triangle Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f); //Combine the rotation matrix with the projection and camera view Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0); GLES20.glEnable(GLES20.GL_BLEND); emuInstance.clearDisplayBuffer(); emuInstance.runBatch(0); sprite.Draw(mMVPMatrix, byteBuffer); } public void onSurfaceChanged(GL10 unused, int width, int height) { GLES20.glViewport(0, 0, width, height); float ratio = (float) width / height; Matrix.orthoM(mProjMatrix,0, -ratio, ratio, -1, 1, 3, 7); }
final float[] cubeTextureCoordinateData = { //Sprite Foreground 0.0f, 0.0f, // top left 0.0f, 1.0f, // bottom left 0.258426966f, 1.0f, // bottom right 0.258426966f, 0.0f, //top right //Foreground 0.258426966f, 0.0f, // top left 0.258426966f, 1.0f, // bottom left 0.516853932f, 1.0f, // bottom right 0.516853932f, 0.0f, //top right //Sprite Background 0.516853932f, 0.0f, // top left 0.516853932f, 1.0f, // bottom left 0.775280898f, 1.0f, // bottom right 0.775280898f, 0.0f, //top right //Background 0.775280898f, 0.166666f, // top left 0.775280898f, 0.833333f, // bottom left 1.0f, 0.833333f, // bottom right 1.0f, 0.166666f //top right };
... @Override protected void onCreate(Bundle savedInstanceState) { ... mByteBuffer = ByteBuffer.allocateDirect( (368 + //foreground sprite 368 + //front 368 + //backround sprite 320 //background ) * (300) * 4); ... } ...
... #define STRIDE 368 + 368 + 368 + 320 jchar colors_RGB_888[16][3] = { {0, 0, 0}, {255, 255, 255}, {136, 0, 0}, {170, 255, 238}, {204, 68, 204}, {0, 204, 85}, {0, 0, 170}, {238, 238, 119}, {221, 136, 85}, {102, 68, 0}, {255, 119, 119}, {51, 51, 51}, {119, 119, 119}, {170, 255, 102}, {0, 136, 255}, {187, 187, 187} }; jchar colors_RGB_565[16]; jint colors_RGB_8888[16]; void initialise_video() { int i; ... for (i=0; i < 16; i++) { colors_RGB_8888[i] = (255 << 24) | (colors_RGB_888[i][2] << 16) | (colors_RGB_888[i][1] << 8) | (colors_RGB_888[i][0] << 0); //colors_RGB_8888[i] = (255); } } ...
... int startOfLineTxtBuffer = 0; int startOfFrontSpriteBuffer = 0; int startOfBackgroundSpriteBuffer = 0; int posInFrontBuffer = 0; int posInBackgroundBuffer = 0; ... static inline void processLine() { if (line_count > 299) return; posInFrontBuffer = startOfLineTxtBuffer + 368; posInBackgroundBuffer = startOfLineTxtBuffer + 368 + 368 + 368; startOfFrontSpriteBuffer = startOfLineTxtBuffer; startOfBackgroundSpriteBuffer = startOfLineTxtBuffer + 368 + 368; ... startOfLineTxtBuffer = startOfLineTxtBuffer + STRIDE; }
struct sprite_data_struct { int sprite_data; int sprite_type; //bit 1: xExpanded bit 0: multicolor int isForegroundSprite; int color_tablet[4]; int sprite_x_pos; int number_pixels_to_draw; };
int processSprite(int spriteNum, int lineNumber, struct sprite_data_struct * sprite_data) { if (!(IOUnclaimed[0x15] & (1 << spriteNum))) return 0; int spriteY = IOUnclaimed[(spriteNum << 1) | 1]; int yExpanded = IOUnclaimed[0x17] & (1 << spriteNum); int ySpriteDimension = yExpanded ? 42 : 21; int spriteYMax = spriteY + ySpriteDimension; if (!((lineNumber >= spriteY) && (lineNumber < spriteYMax))) return 0; int spriteX = (IOUnclaimed[spriteNum << 1] & 0xff); if (IOUnclaimed[0x10] & (1 << spriteNum)) spriteX = 256 | spriteX; if (spriteX > 367) return 0; int xExpanded = IOUnclaimed[0x1d] & (1 << spriteNum); int xSpriteDimension = xExpanded ? 48 : 24; int spriteXMax = spriteX + xSpriteDimension; if (spriteXMax > 367) xSpriteDimension = 368 - spriteX; sprite_data->sprite_x_pos = spriteX; sprite_data->number_pixels_to_draw = xSpriteDimension; sprite_data->sprite_type = 0; if (xExpanded) sprite_data->sprite_type = sprite_data->sprite_type | 2; if (IOUnclaimed[0x1c] & (1 << spriteNum)) { sprite_data->sprite_type = sprite_data->sprite_type | 1; sprite_data->color_tablet[1] = IOUnclaimed[0x25] & 0xf; sprite_data->color_tablet[2] = IOUnclaimed[0x27 + spriteNum] & 0xf; sprite_data->color_tablet[3] = IOUnclaimed[0x26] & 0xf; } else { sprite_data->color_tablet[1] = IOUnclaimed[0x27 + spriteNum] & 0xf; } if (IOUnclaimed[0x1b] & (1 << spriteNum)) sprite_data->isForegroundSprite = IOUnclaimed[0x1b] & (1 << spriteNum) ? 0 : 1; int memPointer = IOUnclaimed[0x18]; int spritePointerAddress = memPointer & 0xf0; spritePointerAddress = spritePointerAddress << 6; spritePointerAddress = ((~IOUnclaimed[0xd00] & 3) << 14) | spritePointerAddress; spritePointerAddress = spritePointerAddress + 0x400 -8 + spriteNum; int spriteBaseAddress = mainMem[spritePointerAddress] << 6; int spriteLineNumber = lineNumber - spriteY; if (yExpanded) spriteLineNumber = spriteLineNumber >> 1; int posInSpriteData = (spriteLineNumber << 1) + (spriteLineNumber) + spriteBaseAddress; sprite_data->sprite_data = (mainMem[posInSpriteData + 0] << 16) | (mainMem[posInSpriteData + 1] << 8) | (mainMem[posInSpriteData + 2] << 0); return 1; }
void processSprites() { int i; struct sprite_data_struct currentSpriteData; for (i = 0; i < 8; i++) { int currentSpriteNum = 7 - i; if (processSprite(currentSpriteNum, line_count, ¤tSpriteData)) { spriteFunctions[currentSpriteData.sprite_type] (currentSpriteData); } } } static inline void processLine() { if (line_count > 299) return; posInFrontBuffer = startOfLineTxtBuffer + 368; posInBackgroundBuffer = startOfLineTxtBuffer + 368 + 368 + 368; startOfFrontSpriteBuffer = startOfLineTxtBuffer; startOfBackgroundSpriteBuffer = startOfLineTxtBuffer + 368 + 368; updatelineCharPos(); fillColor(24, memory_unclaimed_io_read(0xd020) & 0xf); int screenEnabled = (memory_unclaimed_io_read(0xd011) & 0x10) ? 1 : 0; if (screenLineRegion && screenEnabled) { processSprites(); ... } ... }
... void (*spriteFunctions[4]) (struct sprite_data_struct spriteData); void drawExpandedMulticolorSpriteLine(struct sprite_data_struct spriteData); void drawUnExpandedMulticolorSpriteLine(struct sprite_data_struct spriteData); void drawExpandedNormalSpriteLine(struct sprite_data_struct spriteData); void drawUnExpandedNormalSpriteLine(struct sprite_data_struct spriteData); ... void initialise_video() { ... spriteFunctions[0] = &drawUnExpandedNormalSpriteLine; spriteFunctions[1] = &drawUnExpandedMulticolorSpriteLine; spriteFunctions[2] = &drawExpandedNormalSpriteLine; spriteFunctions[3] = &drawExpandedMulticolorSpriteLine; } ...
void drawUnExpandedNormalSpriteLine(struct sprite_data_struct currentSpriteData) { int currentPosInSpriteBuffer; if (currentSpriteData.isForegroundSprite) currentPosInSpriteBuffer = startOfFrontSpriteBuffer; else currentPosInSpriteBuffer = startOfBackgroundSpriteBuffer; currentPosInSpriteBuffer = currentPosInSpriteBuffer + currentSpriteData.sprite_x_pos; int j; int spriteData = currentSpriteData.sprite_data; int upperLimit = currentPosInSpriteBuffer + currentSpriteData.number_pixels_to_draw; for (j = currentPosInSpriteBuffer; j < (upperLimit); j++) { if (spriteData & 0x800000) { g_buffer[currentPosInSpriteBuffer] = colors_RGB_8888[currentSpriteData.color_tablet[1]]; } spriteData = (spriteData << 1) & 0xffffff; currentPosInSpriteBuffer++; } } void drawExpandedNormalSpriteLine(struct sprite_data_struct currentSpriteData) { int currentPosInSpriteBuffer; if (currentSpriteData.isForegroundSprite) currentPosInSpriteBuffer = startOfFrontSpriteBuffer; else currentPosInSpriteBuffer = startOfBackgroundSpriteBuffer; currentPosInSpriteBuffer = currentPosInSpriteBuffer + currentSpriteData.sprite_x_pos; int j; int spriteData = currentSpriteData.sprite_data; for (j = 0; j < (currentSpriteData.number_pixels_to_draw >> 1); j++) { if (spriteData & 0x800000) { g_buffer[currentPosInSpriteBuffer + 0] = colors_RGB_8888[currentSpriteData.color_tablet[1]]; g_buffer[currentPosInSpriteBuffer + 1] = colors_RGB_8888[currentSpriteData.color_tablet[1]]; } currentPosInSpriteBuffer = currentPosInSpriteBuffer + 2; spriteData = (spriteData << 1) & 0xffffff; } } void drawUnExpandedMulticolorSpriteLine(struct sprite_data_struct currentSpriteData) { int currentPosInSpriteBuffer; if (currentSpriteData.isForegroundSprite) currentPosInSpriteBuffer = startOfFrontSpriteBuffer; else currentPosInSpriteBuffer = startOfBackgroundSpriteBuffer; currentPosInSpriteBuffer = currentPosInSpriteBuffer + currentSpriteData.sprite_x_pos; int j; int spriteData = currentSpriteData.sprite_data; for (j = 0; j < (currentSpriteData.number_pixels_to_draw >> 1); j++) { int pixels = (spriteData & 0xC00000) >> 22; if (pixels > 0) { g_buffer[currentPosInSpriteBuffer + 0] = colors_RGB_8888[currentSpriteData.color_tablet[pixels]]; g_buffer[currentPosInSpriteBuffer + 1] = colors_RGB_8888[currentSpriteData.color_tablet[pixels]]; } currentPosInSpriteBuffer = currentPosInSpriteBuffer + 2; spriteData = (spriteData << 2) & 0xffffff; } } void drawExpandedMulticolorSpriteLine(struct sprite_data_struct currentSpriteData) { int currentPosInSpriteBuffer; if (currentSpriteData.isForegroundSprite) currentPosInSpriteBuffer = startOfFrontSpriteBuffer; else currentPosInSpriteBuffer = startOfBackgroundSpriteBuffer; currentPosInSpriteBuffer = currentPosInSpriteBuffer + currentSpriteData.sprite_x_pos; int j; int spriteData = currentSpriteData.sprite_data; for (j = 0; j < (currentSpriteData.number_pixels_to_draw >> 2); j++) { int pixels = (spriteData & 0xC00000) >> 22; if (pixels > 0) { g_buffer[currentPosInSpriteBuffer + 0] = colors_RGB_8888[currentSpriteData.color_tablet[pixels]]; g_buffer[currentPosInSpriteBuffer + 1] = colors_RGB_8888[currentSpriteData.color_tablet[pixels]]; g_buffer[currentPosInSpriteBuffer + 2] = colors_RGB_8888[currentSpriteData.color_tablet[pixels]]; g_buffer[currentPosInSpriteBuffer + 3] = colors_RGB_8888[currentSpriteData.color_tablet[pixels]]; } currentPosInSpriteBuffer = currentPosInSpriteBuffer + 4; spriteData = (spriteData << 2) & 0xffffff; } }
void Java_com_johan_emulator_engine_Emu6502_setFireButton(JNIEnv* pEnv, jobject pObj, jint fireButtonStatus) { if (fireButtonStatus) joystickStatus = joystickStatus | 16; else joystickStatus = joystickStatus & 0xef; } void Java_com_johan_emulator_engine_Emu6502_setJoystickDirectionButton(JNIEnv* pEnv, jobject pObj, jint fireButtonStatus) { //start at north, then goes clockwise till again at north joystickStatus = joystickStatus & 0xf8; switch (fireButtonStatus) { case 0: //North joystickStatus = joystickStatus | JOYSTICK_UP; break; case 1: //North East joystickStatus = joystickStatus | JOYSTICK_UP | JOYSTICK_RIGHT; break; case 2: //East joystickStatus = joystickStatus | JOYSTICK_RIGHT; break; case 3: //South East joystickStatus = joystickStatus | JOYSTICK_DOWN | JOYSTICK_RIGHT; break; case 4: //South joystickStatus = joystickStatus | JOYSTICK_DOWN; break; case 5: //South West joystickStatus = joystickStatus | JOYSTICK_DOWN | JOYSTICK_LEFT; break; case 6: //West joystickStatus = joystickStatus | JOYSTICK_LEFT; break; case 7: //North West joystickStatus = joystickStatus | JOYSTICK_LEFT | JOYSTICK_UP; break; default: break; } }
jchar cia1_read(int address) { jchar result = 0; switch (address) { case 0xdc00: result = ~joystickStatus & 0xff ; break; case 0xdc01: result = getKeyPortByte(mainMem[0xdc00]); break;
... }
... }
int memPointer = memory_unclaimed_io_read(0xd018); int videoMemoryBase = memPointer & 0xf0; videoMemoryBase = videoMemoryBase << 6; int charROMBase = memPointer & 0xe; charROMBase = charROMBase << 10;
void memory_read_batch(int *batch, int address, int count) { address = ((~IOUnclaimed[0xd00] & 3) << 14) | address; int i; for (i = 0; i < count; i++) { if ((address >= 0x1000 && address < 0x2000) || (address >= 0x9000 && address < 0xa000)) batch[i] = charRom[address & 0xfff]; else batch[i] = mainMem[address + i]; } } jchar memory_read_vic_model(int address) { address = ((~IOUnclaimed[0xd00] & 3) << 14) | address; if ((address >= 0x1000 && address < 0x2000) || (address >= 0x9000 && address < 0xa000)) return charRom[address & 0xfff]; else return mainMem[address]; }
static inline void processLine() { if (line_count > 299) return; updatelineCharPos(); fillColor(24, memory_unclaimed_io_read(0xd020) & 0xf); int screenEnabled = (memory_unclaimed_io_read(0xd011) & 0x10) ? 1 : 0; if (screenLineRegion && screenEnabled) { jchar bitmapMode = (memory_unclaimed_io_read(0xd011) & 0x20) ? 1 : 0; jchar multiColorMode = (memory_unclaimed_io_read(0xd016) & 0x10) ? 1 : 0; jchar screenMode = (bitmapMode << 1) | (multiColorMode); switch (screenMode) { case 0: //Normal texmode drawScreenLineNormalText(); break; case 1: //Multi color text mode drawScreenLineMultiColorText(); break; case 2: //Standard bitmap mode break; case 3: //Multicolor bitmap drawScreenLineMultiColorBitmap(); break; } } else { fillColor(320, memory_unclaimed_io_read(0xd020) & 0xf); } fillColor(24, memory_unclaimed_io_read(0xd020) & 0xf); }
static inline void drawScreenLineMultiColorText() { int i; int batchCharMem[40]; int batchColorMem[40]; int color_tablet[4]; int memPointer = memory_unclaimed_io_read(0xd018); int videoMemoryBase = memPointer & 0xf0; videoMemoryBase = videoMemoryBase << 6; int charROMBase = memPointer & 0xe; charROMBase = charROMBase << 10; int backgroundColor = memory_unclaimed_io_read(0xd021) & 0xf; memory_read_batch(batchCharMem, videoMemoryBase + posInCharMem, 40); memory_read_batch_io_unclaimed(batchColorMem, 0xd800 + posInCharMem, 40); for (i = 0; i < 40; i++) { jchar charcode = batchCharMem[i];//memory_read(1024 + i + posInCharMem); int bitmapDataRow = memory_read_vic_model(((charcode << 3) | (line_in_visible & 7)) + charROMBase); int j; int foregroundColor = batchColorMem[i] & 0xf;//memory_read(0xd800 + i + posInCharMem) & 0xf; if (foregroundColor & 8) { foregroundColor = foregroundColor & 7; color_tablet[0] = backgroundColor; color_tablet[1] = memory_unclaimed_io_read(0xd022) & 0xf; color_tablet[2] = memory_unclaimed_io_read(0xd023) & 0xf; color_tablet[3] = foregroundColor; for (j = 0; j < 4; j++) { int pixelSet = bitmapDataRow & 0xc0; pixelSet = pixelSet >> 6; g_buffer[posInBuffer] = colors_RGB_565[color_tablet[pixelSet]]; posInBuffer++; g_buffer[posInBuffer] = colors_RGB_565[color_tablet[pixelSet]]; posInBuffer++; bitmapDataRow = bitmapDataRow << 2; } } else { for (j = 0; j < 8; j++) { foregroundColor = foregroundColor & 7; int pixelSet = bitmapDataRow & 0x80; if (pixelSet) { g_buffer[posInBuffer] = colors_RGB_565[foregroundColor]; } else { g_buffer[posInBuffer] = colors_RGB_565[backgroundColor]; } posInBuffer++; bitmapDataRow = bitmapDataRow << 1; } } } }
static inline void drawScreenLineMultiColorBitmap() { int i; int batchCharMem[40]; int batchColorMem[40]; int memPointer = memory_unclaimed_io_read(0xd018); int videoMemoryBase = memPointer & 0xf0; videoMemoryBase = videoMemoryBase << 6; int charROMBase = memPointer & 0xe; charROMBase = charROMBase << 10; int color_tablet[4]; color_tablet[0] = memory_unclaimed_io_read(0xd021) & 0xf; //int backgroundColor = memory_unclaimed_io_read(0xd021) & 0xf; memory_read_batch(batchCharMem, videoMemoryBase + posInCharMem, 40); memory_read_batch_io_unclaimed(batchColorMem, 0xd800 + posInCharMem, 40); for (i = 0; i < 40; i++) { jchar charcode = batchCharMem[i];//memory_read(1024 + i + posInCharMem); color_tablet[1] = charcode >> 4; color_tablet[2] = charcode & 0xf; color_tablet[3] = batchColorMem[i] & 0xf; int bitmapDataRow = memory_read_vic_model((((posInCharMem + i) << 3) | (line_in_visible & 7)) + charROMBase); int j; //int foregroundColor = batchColorMem[i] & 0xf;//memory_read(0xd800 + i + posInCharMem) & 0xf; for (j = 0; j < 4; j++) { int pixelSet = bitmapDataRow & 0xc0; pixelSet = pixelSet >> 6; g_buffer[posInBuffer] = colors_RGB_565[color_tablet[pixelSet]]; posInBuffer++; g_buffer[posInBuffer] = colors_RGB_565[color_tablet[pixelSet]]; posInBuffer++; bitmapDataRow = bitmapDataRow << 2; } } }
... jchar vic_interrupt = 0; ... int raster_int_enabled() { return (memory_unclaimed_io_read(0xd01a) & 1) ? 1 : 0; } void video_line_expired(struct timer_struct *tdev) { tdev->remainingCycles = 63; processLine(); line_count++; jchar RST_0_7 = memory_unclaimed_io_read(0xd012); jchar RST_8 = (memory_unclaimed_io_read(0xd011) & 0x80) << 1; int targetRasterLine = RST_8 | RST_0_7; if (line_count > 310) { line_count = 0; frameFinished = 1; posInBuffer = 0; } if ((targetRasterLine == line_count) && raster_int_enabled()) vic_interrupt = vic_interrupt | 1 | 128; }
int vic_raster_int_occured() { return (vic_interrupt > 128) ? 1 : 0; }
... void process_interrupts() { if (interruptFlag == 1) return; if ((trigger_irq() == 0) && (vic_raster_int_occured() == 0)) return; pushWord(pc); breakFlag = 0; Push(getStatusFlagsAsByte()); breakFlag = 1; interruptFlag = 1; int tempVal = memory_read(0xffff) * 256; tempVal = tempVal + memory_read(0xfffe); pc = tempVal; } ...
int read_vic_int_reg () { return vic_interrupt; } void write_vic_int_reg(jchar value) { value = ~value & 0x7f; vic_interrupt = vic_interrupt & value; if (vic_interrupt > 0) vic_interrupt = vic_interrupt | 128; }
... jchar memory_read(int address) { if ((address >=0xa000) && (address < 0xc000) && basicROMEnabled()) return basicROM[address & 0x1fff]; else if ((address >=0xe000) && (address < 0x10000) && kernalROMEnabled()) return kernalROM[address & 0x1fff]; else if (address == 1) return read_port_1(); else if ((address >=0xd000) && (address < 0xe000) && IOEnabled()) { if ((address >=0xdc00) && (address < 0xdc10)) return cia1_read(address); else if (address == 0xd011) { int tempValue = IOUnclaimed[address & 0xfff] & 0x7f; tempValue = tempValue | ((line_count & 0x100) >> 1); return tempValue; } else if (address == 0xd012) return line_count & 0xff; else if (address == 0xd019) return read_vic_int_reg(); else return IOUnclaimed[address & 0xfff]; } else return mainMem[address]; } ... void memory_write(int address, jchar value) { //if (((address >= 0xa000) && (address < 0xc000)) | // ((address >= 0xe000) && (address < 0x10000))) // return; if (address == 1) write_port_1(value); else if ((address >=0xd000) && (address < 0xe000) && IOEnabled()) { if((address >=0xdc00) & (address < 0xdc10)) cia1_write(address, value); else if (address == 0xd019) write_vic_int_reg(value); else IOUnclaimed[address & 0xfff] = value; } else mainMem[address] = value; } ...
void video_line_expired(struct timer_struct *tdev) { tdev->remainingCycles = 63; } struct timer_struct getVideoInstance() { struct timer_struct myVideo; myVideo.expiredevent = &video_line_expired; myVideo.remainingCycles = 63; myVideo.started = 1; return myVideo; }
void Java_com_johan_emulator_engine_Emu6502_memoryInit(JNIEnv* pEnv, jobject pObj) { timerA = getTimerInstanceA(); add_timer_to_list(&timerA); timerB = getTimerInstanceB(); add_timer_to_list(&timerB); tape_timer = getTapeInstance(); add_timer_to_list(&tape_timer); video_timer = getVideoInstance(); add_timer_to_list(&video_timer); }
jchar colors_RGB_888[16][3] = { {0, 0, 0}, {255, 255, 255}, {136, 0, 0}, {170, 255, 238}, {204, 68, 204}, {0, 204, 85}, {0, 0, 170}, {238, 238, 119}, {221, 136, 85}, {102, 68, 0}, {255, 119, 119}, {51, 51, 51}, {119, 119, 119}, {170, 255, 102}, {0, 136, 255}, {187, 187, 187} };
... jchar colors_RGB_565[16]; ... void initialise_video() { int i; for (i=0; i < 16; i++) { int red = colors_RGB_888[i][0] >> 3; int green = colors_RGB_888[i][1] >> 2; int blue = colors_RGB_888[i][2] >> 3; colors_RGB_565[i] = (red << 11) | (green << 5) | (blue << 0); } } ...
... int line_count = 0; ... extern int frameFinished; ... void video_line_expired(struct timer_struct *tdev) { tdev->remainingCycles = 63; processLine(); line_count++; if (line_count > 310) { line_count = 0; frameFinished = 1; } }
... int frameFinished = 0; ... int runBatch(int address) { //remainingCycles = 20000; frameFinished = 0; int lastResult = 0; while ((!frameFinished) && (lastResult == 0)) { lastResult = step(); if (lastResult != 0) break; if ((address > 0) && (pc == address)) { lastResult = -1; break; } processAlarms(); } return lastResult; }
static inline void processLine() { if (line_count > 299) return; updatelineCharPos(); fillColor(24, memory_read(0xd020) & 0xf); int screenEnabled = (memory_read(0xd011) & 0x10) ? 1 : 0; if (screenLineRegion && screenEnabled) { drawScreenLine(); } else { fillColor(320, memory_read(0xd020) & 0xf); } fillColor(24, memory_read(0xd020) & 0xf); }
inline void fillColor(int count, int colorEntryNumber) { int currentPos; for (currentPos = 0; currentPos < count; currentPos++) { g_buffer[posInBuffer] = colors_RGB_565[colorEntryNumber]; posInBuffer++; } }
static inline void drawScreenLine() { int i; for (i = 0; i < 40; i++) { jchar charcode = memory_read(1024 + i + posInCharMem); int bitmapDataRow = charRom[(charcode << 3) | (line_in_visible & 7)]; int j; int foregroundColor = memory_read(0xd800 + i + posInCharMem) & 0xf; int backgroundColor = memory_read(0xd021) & 0xf; for (j = 0; j < 8; j++) { int pixelSet = bitmapDataRow & 0x80; if (pixelSet) { g_buffer[posInBuffer] = colors_RGB_565[foregroundColor]; } else { g_buffer[posInBuffer] = colors_RGB_565[backgroundColor]; } posInBuffer++; bitmapDataRow = bitmapDataRow << 1; } } }
#define BASIC_VISIBLE 1 #define KERNAL_VISIBLE 2 #define CHAR_ROM_VISIBLE 4 #define IO_VISIBLE 8 int bank_visibility[8] = { 0,//000 CHAR_ROM_VISIBLE,//001 CHAR_ROM_VISIBLE | KERNAL_VISIBLE,//010 BASIC_VISIBLE | KERNAL_VISIBLE | CHAR_ROM_VISIBLE,//011 0,//100 IO_VISIBLE,//101 IO_VISIBLE | KERNAL_VISIBLE,//110 BASIC_VISIBLE | KERNAL_VISIBLE | IO_VISIBLE//111 }; ... inline int kernalROMEnabled() { int bankBits = mainMem[1] & 7; return (bank_visibility[bankBits] & KERNAL_VISIBLE) ? 1 : 0; } inline int basicROMEnabled() { int bankBits = mainMem[1] & 7; return (bank_visibility[bankBits] & BASIC_VISIBLE) ? 1 : 0; } inline int IOEnabled() { int bankBits = mainMem[1] & 7; return (bank_visibility[bankBits] & IO_VISIBLE) ? 1 : 0; }
... jchar IOUnclaimed[4096]; ... jchar memory_read(int address) { if ((address >=0xa000) && (address < 0xc000) && basicROMEnabled()) return basicROM[address & 0x1fff]; else if ((address >=0xe000) && (address < 0x10000) && kernalROMEnabled()) return kernalROM[address & 0x1fff]; else if (address == 1) return read_port_1(); else if ((address >=0xd000) && (address < 0xe000) && IOEnabled()) { if ((address >=0xdc00) && (address < 0xdc10)) return cia1_read(address); else return IOUnclaimed[address & 0xfff]; } else return mainMem[address]; } void memory_write(int address, jchar value) { //if (((address >= 0xa000) && (address < 0xc000)) | // ((address >= 0xe000) && (address < 0x10000))) // return; if (address == 1) write_port_1(value); else if ((address >=0xd000) && (address < 0xe000) && IOEnabled()) { if((address >=0xdc00) & (address < 0xdc10)) cia1_write(address, value); else IOUnclaimed[address & 0xfff] = value; } else mainMem[address] = value; }
static inline void drawScreenLine() { int i; int batchCharMem[40]; int batchColorMem[40]; int backgroundColor = memory_unclaimed_io_read(0xd021) & 0xf; memory_read_batch(batchCharMem, 1024 + posInCharMem, 40); memory_read_batch_io_unclaimed(batchColorMem, 0xd800 + posInCharMem, 40); for (i = 0; i < 40; i++) { jchar charcode = batchCharMem[i];//memory_read(1024 + i + posInCharMem); int bitmapDataRow = charRom[(charcode << 3) | (line_in_visible & 7)]; int j; int foregroundColor = batchColorMem[i] & 0xf;//memory_read(0xd800 + i + posInCharMem) & 0xf; for (j = 0; j < 8; j++) { int pixelSet = bitmapDataRow & 0x80; if (pixelSet) { g_buffer[posInBuffer] = colors_RGB_565[foregroundColor]; } else { g_buffer[posInBuffer] = colors_RGB_565[backgroundColor]; } posInBuffer++; bitmapDataRow = bitmapDataRow << 1; } } }
void memory_read_batch(int *batch, int address, int count) { int i; for (i = 0; i < count; i++) { batch[i] = mainMem[address + i]; } } void memory_read_batch_io_unclaimed(int *batch, int address, int count) { int i; address = address & 0xfff; for (i = 0; i < count; i++) { batch[i] = IOUnclaimed[address + i]; } }
public void getfile(View view){ Intent intent1 = new Intent(this, FileChooser.class); startActivityForResult(intent1,REQUEST_PATH); }
private void onFileClick(Item o) { //Toast.makeText(this, "Folder Clicked: "+ currentDir, Toast.LENGTH_SHORT).show(); Intent intent = new Intent(); intent.putExtra("GetPath",currentDir.toString()); intent.putExtra("GetFileName",o.getName()); setResult(RESULT_OK, intent); finish(); }
protected void onActivityResult(int requestCode, int resultCode, Intent data){ // See which child activity is calling us back. if (requestCode == REQUEST_PATH){ if (resultCode == RESULT_OK) { curFileName = data.getStringExtra("GetPath") + "/" +data.getStringExtra("GetFileName"); edittext.setText(curFileName); } } }
@Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_stop) { switchToDebug = true; return true; } else if (id == R.id.action_attach) { Intent i = new Intent(this, FileDialogueActivity.class); startActivityForResult(i, 1); return true; } return super.onOptionsItemSelected(item); }
public void onAttachClick(View v) { Intent intent = new Intent(); intent.putExtra("GetFullPath",curFileName); setResult(RESULT_OK, intent); finish(); }
protected void onActivityResult(int requestCode, int resultCode, Intent data){ // See which child activity is calling us back. if (requestCode == 1){ if (resultCode == RESULT_OK) { String curFileName = data.getStringExtra("GetFullPath"); ... } } }
... private ByteBuffer mTape; ... protected void onActivityResult(int requestCode, int resultCode, Intent data){ // See which child activity is calling us back. if (requestCode == 1){ if (resultCode == RESULT_OK) { String curFileName = data.getStringExtra("GetFullPath"); try { RandomAccessFile file = new RandomAccessFile(curFileName, "r"); FileChannel inChannel = file.getChannel(); long fileSize = inChannel.size(); mTape = ByteBuffer.allocateDirect((int) fileSize); inChannel.read(mTape); mTape.rewind(); inChannel.close(); file.close(); emuInstance.attachNewTape(mTape); } catch (Exception e) { e.printStackTrace(); } } } }
struct timer_struct getTapeInstance() { struct timer_struct mytape; mytape.expiredevent = &tape_pulse_expired; mytape.remainingCycles = 0x0; mytape.started = 0; mytape.interrupt = &interrupt_flag; return mytape; }
... struct timer_struct tape_timer; ... void Java_com_johan_emulator_engine_Emu6502_memoryInit(JNIEnv* pEnv, jobject pObj) { timerA = getTimerInstanceA(); add_timer_to_list(&timerA); timerB = getTimerInstanceB(); add_timer_to_list(&timerB); tape_timer = getTapeInstance(); add_timer_to_list(&tape_timer); } ...
void Java_com_johan_emulator_engine_Emu6502_attachNewTape(JNIEnv* pEnv, jobject pObj, jobject oBuf) { jbyte * tape_image = (jbyte *) (*pEnv)->GetDirectBufferAddress(pEnv, oBuf); attachNewTape(tape_image, &tape_timer); }
... jbyte* tape_image; int posInTape; ... void attachNewTape(jbyte* buffer, struct timer_struct *tdev) { tape_image = buffer; posInTape = 0x14; ... }
void update_remaining(struct timer_struct *tdev) { int temp = tape_image[posInTape]; if (temp != 0) { tdev->remainingCycles = temp << 3; posInTape++; } else { tdev->remainingCycles = tape_image[posInTape + 1] | (tape_image[posInTape + 2] << 8) | (tape_image[posInTape + 3] << 16); posInTape = posInTape + 4; } } void attachNewTape(jbyte* buffer, struct timer_struct *tdev) { tape_image = buffer; posInTape = 0x14; update_remaining(tdev); } void tape_pulse_expired(struct timer_struct *tdev) { update_remaining(tdev); interrupt_flag(); }
void interrupt_flag() { interrupts_occured = interrupts_occured | 16; }
... int playDown = 0; ... void Java_com_johan_emulator_engine_Emu6502_togglePlay() { playDown = !playDown; } ... int isPlayDownBit() { return (playDown != 0) ? 0 : 1; } ...
void setMotorOn(struct timer_struct *tdev, int motorBit) { tdev -> started = (motorBit == 0) ? 1 : 0; } int getMotorOnBit(struct timer_struct *tdev) { return (tdev -> started == 1) ? 0 : 1; }
jchar read_port_1() { jchar result = mainMem[1] & 0xcf; result = result | (getMotorOnBit(&tape_timer) << 5); result = result | (isPlayDownBit() << 4); return result; } void write_port_1(jchar value) { mainMem[1] = value; int motorStatus = (value & (1 << 5)) >> 5; setMotorOn(&tape_timer, motorStatus); } jchar memory_read(int address) { if (address == 1) return read_port_1(); else if ((address >=0xdc00) & (address < 0xdc10)) return cia1_read(address); else return mainMem[address]; } void memory_write(int address, jchar value) { if (((address >= 0xa000) && (address < 0xc000)) | ((address >= 0xe000) && (address < 0x10000))) return; if (address == 1) write_port_1(value); else if ((address >=0xdc00) & (address < 0xdc10)) cia1_write(address, value); else mainMem[address] = value; }
<com.johan.emulator.view.C64SurfaceView android:id="@+id/Video" android:layout_width="640px" android:layout_height="400px" />
emuInstance.populateFrame(); mByteBuffer.rewind(); mBitmap.copyPixelsFromBuffer(mByteBuffer); canvas.save(); canvas.scale(1.5f, 1.5f); canvas.setDrawFilter(filter); canvas.drawBitmap(mBitmap,0,0, paint); canvas.restore(); holder.unlockCanvasAndPost(canvas);