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);