kopia lustrzana https://github.com/Jean-MarcHarvengt/MCUME
446 wiersze
12 KiB
C++
446 wiersze
12 KiB
C++
#include "pico.h"
|
|
#include "pico/stdlib.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
extern "C" {
|
|
#include "emuapi.h"
|
|
#include "platform_config.h"
|
|
}
|
|
|
|
|
|
|
|
|
|
// Vic 20 emulation includes
|
|
#include "mos6502.h"
|
|
#include "MOS6561.h"
|
|
#include "MOS6522.h"
|
|
#include "MOS6502Memory.h"
|
|
#include "kernalromp.h"
|
|
#include "charrom.h"
|
|
#include "basicrom.h"
|
|
|
|
static mos6502 mos;
|
|
static MOS6561 mos6561;
|
|
static MOS6522 mos6522;
|
|
uint8_t vicmemory[0x10000];;
|
|
|
|
|
|
#define VIC20FREQBASE 65535
|
|
|
|
/* noise magic */
|
|
#define NSHIFT(v, n) (((v)<<(n))|((((v)>>(23-(n)))^(v>>(18-(n))))&((1<<(n))-1)))
|
|
#define NVALUE(v) (noiseLSB[v&0xff]|noiseMID[(v>>8)&0xff]|noiseMSB[(v>>16)&0xff])
|
|
#define NSEED 0x7ffff8
|
|
|
|
/* Noise tables */
|
|
#define NOISETABLESIZE 256
|
|
static uint8_t noiseMSB[NOISETABLESIZE];
|
|
static uint8_t noiseMID[NOISETABLESIZE];
|
|
static uint8_t noiseLSB[NOISETABLESIZE];
|
|
|
|
/* needed data for one voice */
|
|
typedef struct voice_s
|
|
{
|
|
/* counter value */
|
|
int f;
|
|
/* counter step / sample */
|
|
int fs;
|
|
/* noise shift register. Note! rv may be 0 to 15 shifts 'behind' the
|
|
real noise shift register value. Remaining shifts are done when
|
|
it is referenced */
|
|
int rv;
|
|
} voice_t;
|
|
|
|
/* needed data for SID */
|
|
struct sound_s
|
|
{
|
|
/* number of voices */
|
|
voice_t v[4];
|
|
/* 4-bit volume value */
|
|
uint8_t vol;
|
|
/* internal constant used for sample rate dependent calculations */
|
|
int speed1;
|
|
};
|
|
|
|
static sound_s psid;
|
|
|
|
static void VIC_VOICE_HANDLE(int voice, int value, int shift)
|
|
{
|
|
int div = 255 - value;
|
|
if (!div)
|
|
div = 127;
|
|
if (!(value & 0x80))
|
|
psid.v[voice].fs = 0;
|
|
else
|
|
psid.v[voice].fs = psid.speed1*VIC20FREQBASE*(1 << shift)/div;
|
|
};
|
|
|
|
|
|
void SND_Process(void * stream, int len) {
|
|
int o0, o1, o2, o3;
|
|
|
|
short * sndbuf = (short *)stream;
|
|
for (int i = 0; i < len/2; i++)
|
|
{
|
|
/* addfptrs */
|
|
psid.v[0].f += psid.v[0].fs;
|
|
psid.v[1].f += psid.v[1].fs;
|
|
psid.v[2].f += psid.v[2].fs;
|
|
psid.v[3].f += psid.v[3].fs;
|
|
/* noise */
|
|
if (psid.v[3].f < psid.v[3].fs)
|
|
psid.v[3].rv = NSHIFT(psid.v[3].rv, 16);
|
|
/* voices */
|
|
o0 = (psid.v[0].f & 0x80000000) >> 2;
|
|
o1 = (psid.v[1].f & 0x80000000) >> 2;
|
|
o2 = (psid.v[2].f & 0x80000000) >> 2;
|
|
o3 = (int)NVALUE(NSHIFT(psid.v[3].rv, psid.v[3].f >> 28)) << 22;
|
|
/* sample */
|
|
uint16_t s = ((int)((o0+o1+o2+o3)>>20)-0x800)*psid.vol;
|
|
*sndbuf++ =s;
|
|
*sndbuf++ =s;
|
|
}
|
|
}
|
|
|
|
static char * strext(char * filepath) {
|
|
int str_len = strlen(filepath);
|
|
for (int i=0; i<str_len; i++) {
|
|
if ( (filepath[i] == '.') && (i != (str_len-1) ) ) {
|
|
return &filepath[i+1];
|
|
}
|
|
}
|
|
return filepath;
|
|
}
|
|
|
|
static void loadROM(char * filepath, uint16_t atMemoryLocation) {
|
|
uint16_t loadAddress;
|
|
uint16_t endAddress;
|
|
int lSize = emu_FileSize(filepath);
|
|
int f = emu_FileOpen(filepath, "r+b");
|
|
char * ext = strext(filepath);
|
|
if ( (ext[0] == 'p') || (ext[0] == 'P') ) {
|
|
unsigned char buffer[2];
|
|
emu_FileRead((char *)&buffer[0], 2, f);
|
|
loadAddress = buffer[0] | buffer[1] << 8;
|
|
endAddress = loadAddress + lSize-2;
|
|
emu_FileRead((char *)&vicmemory[loadAddress], lSize-2, f);
|
|
}
|
|
else {
|
|
switch (ext[0]) {
|
|
case '2':
|
|
loadAddress = 0x2000;
|
|
break;
|
|
case '4':
|
|
loadAddress = 0x4000;
|
|
break;
|
|
case '6':
|
|
loadAddress = 0x6000;
|
|
break;
|
|
case 'a':
|
|
case 'A':
|
|
loadAddress = 0xA000;
|
|
break;
|
|
}
|
|
endAddress = loadAddress + lSize;
|
|
emu_FileRead((char *)&vicmemory[loadAddress], lSize, f);
|
|
}
|
|
emu_FileClose(f);
|
|
// Set basic memory pointers
|
|
|
|
silentWriteDWord(0x2B, loadAddress);
|
|
silentWriteDWord(0x2D, loadAddress);
|
|
silentWriteDWord(0x2F, endAddress);
|
|
silentWriteDWord(0x31, endAddress);
|
|
silentWriteDWord(0x33, endAddress);
|
|
silentWriteDWord(0x37, 0xFFFF);
|
|
silentWriteDWord(0xAE, endAddress);
|
|
|
|
}
|
|
|
|
|
|
/*VIC-20 KEYBOARD MATRIX
|
|
|
|
In my implementation, n = 0xF7EF
|
|
|
|
(80 40 20 10 08 04 02 01)
|
|
7f bf df ef f7 fb fd fe
|
|
__
|
|
01 fe 2 q CBM Space RunStop Control Lft_arr 1
|
|
02 fd 4 e s z Shift_L a w 3
|
|
04 fb 6 t f c x d r 5
|
|
08 f7 8 u h b v g y 7
|
|
10 ef 0 o k m n j i 9
|
|
20 df - @ : . , l p +
|
|
40 bf Home Up_arr = Shift_R / ; * GBP
|
|
80 7f F7 F5 F3 F1 Down Right Return Del
|
|
--
|
|
*/
|
|
|
|
const uint32_t ascii2scan[] = {
|
|
//0 1 2 3 4 5 6 7 8 9 A B C D E F
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0xfd7f,0,0, // return
|
|
// 17:down 29:right 31:RUNSTOP
|
|
0,0xf77f,0,0,0,0,0,0,0,0,0,0,0,0xfb7f,0,0xf7fe,
|
|
//sp ! " # $ % & ' ( ) * + , - . /
|
|
0xeffe ,0x1fefe,0x17ffe,0x1fe02,0x17ffd,0x1fefb,0x17ffb,0x1fef7,0x17ff7,0x1feef,0xfdbf ,0xfedf ,0xf7df ,0x7fDf ,0xefdf ,0xf7bf,
|
|
//0 1 2 3 4 5 6 7 8 9 : ; < = > ?
|
|
0x7fef ,0xfefe ,0x7ffe ,0xfefd ,0x7ffd ,0xfefb ,0x7ffb ,0xfef7 ,0x7ff7 ,0xfeef ,0xdfdf ,0xfbbf ,0x1f7df,0xdfbf ,0x1efdf,0x1f7bf,
|
|
//@ A B C D E F G H I J K L M N O
|
|
0xbfdf ,0xfbfd ,0xeff7 ,0xeffb ,0xfbfb ,0xbffd ,0xdffb ,0xfbf7 ,0xdff7 ,0xfdef ,0xfbef ,0xdfef ,0xfbdf ,0xefef ,0xf7ef ,0xbfef,
|
|
//P Q R S T U V W X Y Z [ \ ] ^ _
|
|
0xfddf ,0xbffe ,0xfdfb ,0xdffd ,0xbffb ,0xbff7 ,0xf7f7 ,0xfdfd ,0xf7fb ,0xfdf7 ,0xeffd ,0x1dfdf,0xffff ,0x1fbbf,0 ,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // ' a b c d e f g h i j k l m n o
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xfe7f,0, // p q r s t u v w x y z { | } ~ DEL
|
|
// 133:f1 f2 f3 f4 f5 f6 f7 f8
|
|
0,0,0,0,0,0xef7f,0x1ef7f,0xdf7f,0x1df7f,0xbf7f,0x1bf7f,0x7f7f,0x17f7f,0x00,0x00,0x00, // 128-143
|
|
// 145:up 157:left
|
|
0,0x1f77f,0,0,0,0,0,0,0,0,0,0,0,0x1fb7f,0,0 // 144-159
|
|
};
|
|
|
|
|
|
static int ik;
|
|
static int ihk;
|
|
static int pik=0;
|
|
|
|
|
|
void v20_Input(int bClick) {
|
|
ihk = emu_ReadI2CKeyboard();
|
|
ik = emu_GetPad();
|
|
}
|
|
|
|
void emu_KeyboardOnDown(int keymodifer, int key) {
|
|
}
|
|
|
|
void emu_KeyboardOnUp(int keymodifer, int key) {
|
|
}
|
|
|
|
|
|
/*
|
|
uint8_t readWord( uint16_t location) {
|
|
switch (location) {
|
|
case 0x9111:
|
|
case 0x911F:
|
|
mos6522.joy1Input();
|
|
break;
|
|
case 0x9120:
|
|
mos6522.joy2Input();
|
|
break;
|
|
case 0x9121:
|
|
//case 0x912F:
|
|
mos6522.keyboardInput();
|
|
break;
|
|
}
|
|
return vicmemory[location];
|
|
}
|
|
|
|
void writeWord( uint16_t location, uint8_t value){
|
|
vicmemory[location] = value;
|
|
}
|
|
*/
|
|
|
|
|
|
void v20_Init(void)
|
|
{
|
|
|
|
// Initialize emulation objects
|
|
mos.Reset();
|
|
mos6561.initialize();
|
|
mos6522.setCpu(&mos);
|
|
mos6522.initialize();
|
|
|
|
// clear memory
|
|
for(int i=0; i<sizeof(vicmemory); i++) {
|
|
vicmemory[i] = 0;
|
|
}
|
|
// load charrom
|
|
for(int i=0; i<4096; i++) {
|
|
vicmemory[0x8000+i] = charrom[i];
|
|
}
|
|
// load basic rom
|
|
for(int i=0; i<8192; i++) {
|
|
vicmemory[0xC000+i] = basicrom[i];
|
|
}
|
|
// load kernal rom
|
|
for(int i=0; i<8192; i++) {
|
|
vicmemory[0xE000+i] = kernalrom[i];
|
|
}
|
|
|
|
|
|
#ifdef HAS_SND
|
|
psid.speed1 = (1000000 << 8) / 22000;
|
|
psid.v[3].rv = NSEED;
|
|
for (int i = 0; i < NOISETABLESIZE; i++)
|
|
{
|
|
noiseLSB[i] = (((i>>(7-2))&0x04)|((i>>(4-1))&0x02)|((i>>(2-0))&0x01));
|
|
noiseMID[i] = (((i>>(13-8-4))&0x10)|((i<<(3-(11-8)))&0x08));
|
|
noiseMSB[i] = (((i<<(7-(22-16)))&0x80)|((i<<(6-(20-16)))&0x40)
|
|
|((i<<(5-(16-16)))&0x20));
|
|
}
|
|
emu_sndInit();
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
void v20_Start(char * filename)
|
|
{
|
|
loadROM(filename,0);
|
|
|
|
// Reset cpu
|
|
mos.Reset();
|
|
|
|
// Execute boot sequence before doing much else
|
|
mos.Run(20000);
|
|
}
|
|
|
|
void v20_Step(void)
|
|
{
|
|
do {
|
|
// bind emulation to clock speed
|
|
|
|
// Cpu clock is four times less than 6561 clock
|
|
//int cpuCycles = mos.executeInstruction();
|
|
uint64_t cpuCycles = mos.Run(1);
|
|
mos6561.tick(1);
|
|
mos6522.tick();
|
|
//mos6522.tick();
|
|
|
|
/*
|
|
uint64_t cpuCycles = mos.Run(mos6561.cyclesPerScanline);
|
|
mos6561.tick(mos6561.cyclesPerScanline);
|
|
mos6522.tick();
|
|
mos6522.tick();
|
|
mos6522.tick();
|
|
mos6522.tick();
|
|
mos6522.tick();
|
|
mos6522.tick();
|
|
*/
|
|
|
|
/*
|
|
// printf("cycles %d\n", cpuCycles);
|
|
while (cpuCycles) {
|
|
cpuCycles--;
|
|
mos6522.tick();
|
|
mos6522.tick();
|
|
}
|
|
*/
|
|
|
|
} while ((!mos6561.isFrameReady()));
|
|
|
|
// Swap buffers if frame ready
|
|
if (mos6561.isFrameReady()) {
|
|
mos6561.renderFrame();
|
|
}
|
|
|
|
// Top basic
|
|
// Unexpanded
|
|
//vicmemory[55] = 0;
|
|
//vicmemory[56] = 0x1E;
|
|
// +3k
|
|
//vicmemory[55] = 0;
|
|
//vicmemory[56] = 0x1E;
|
|
// +8k
|
|
//vicmemory[55] = 0;
|
|
//vicmemory[56] = 0x40;
|
|
// +16k
|
|
//vicmemory[55] = 0;
|
|
//vicmemory[56] = 0x60;
|
|
// +24k
|
|
//vicmemory[55] = 0;
|
|
//vicmemory[56] = 0x80;
|
|
// Bottom basic ???
|
|
//vicmemory[51] = 0;
|
|
//vicmemory[52] = 0x1E;
|
|
|
|
emu_DrawVsync();
|
|
|
|
int hk=ihk;
|
|
|
|
#if (defined(PICOMPUTER) || defined(PICOZX) )
|
|
if (hk) {
|
|
int scan = ascii2scan[hk];
|
|
if (scan & 0x10000) mos6522.setShiftPressed(true);
|
|
else mos6522.setShiftPressed(false);
|
|
mos6522.setKeyPressed(scan & 0xffff);
|
|
}
|
|
else {
|
|
mos6522.setShiftPressed(false);
|
|
mos6522.setKeyPressed(0);
|
|
}
|
|
#endif
|
|
|
|
|
|
int k=ik;
|
|
#if (defined(PICOMPUTER) || defined(PICOZX) )
|
|
// Ignore joypad if shift is pressed!!!
|
|
// if ( !(k & MASK_KEY_USER2) )
|
|
if ( hk == 0 )
|
|
#endif
|
|
{
|
|
if ( !(pik & MASK_JOY2_BTN) && (k & MASK_JOY2_BTN) ) {
|
|
mos6522.setJoyStickPressed(MOS6522::Vic20JoyStickButton::Fire, true);
|
|
}
|
|
else if ( (pik & MASK_JOY2_BTN) && !(k & MASK_JOY2_BTN) ) {
|
|
mos6522.setJoyStickPressed(MOS6522::Vic20JoyStickButton::Fire, false);
|
|
}
|
|
if ( !(pik & MASK_JOY2_UP) && (k & MASK_JOY2_UP) ) {
|
|
mos6522.setJoyStickPressed(MOS6522::Vic20JoyStickButton::Up, true);
|
|
}
|
|
else if ( (pik & MASK_JOY2_UP) && !(k & MASK_JOY2_UP) ) {
|
|
mos6522.setJoyStickPressed(MOS6522::Vic20JoyStickButton::Up, false);
|
|
}
|
|
if ( !(pik & MASK_JOY2_DOWN) && (k & MASK_JOY2_DOWN) ) {
|
|
mos6522.setJoyStickPressed(MOS6522::Vic20JoyStickButton::Down, true);
|
|
}
|
|
else if ( (pik & MASK_JOY2_DOWN) && !(k & MASK_JOY2_DOWN) ) {
|
|
mos6522.setJoyStickPressed(MOS6522::Vic20JoyStickButton::Down, false);
|
|
}
|
|
if ( !(pik & MASK_JOY2_RIGHT) && (k & MASK_JOY2_RIGHT) ) {
|
|
mos6522.setJoyStickPressed(MOS6522::Vic20JoyStickButton::Left, true);
|
|
}
|
|
else if ( (pik & MASK_JOY2_RIGHT) && !(k & MASK_JOY2_RIGHT) ) {
|
|
mos6522.setJoyStickPressed(MOS6522::Vic20JoyStickButton::Left, false);
|
|
}
|
|
if ( !(pik & MASK_JOY2_LEFT) && (k & MASK_JOY2_LEFT) ) {
|
|
mos6522.setJoyStickPressed(MOS6522::Vic20JoyStickButton::Right, true);
|
|
}
|
|
else if ( (pik & MASK_JOY2_LEFT) && !(k & MASK_JOY2_LEFT) ) {
|
|
mos6522.setJoyStickPressed(MOS6522::Vic20JoyStickButton::Right, false);
|
|
}
|
|
}
|
|
|
|
#if (defined(PICOMPUTER) || defined(PICOZX) )
|
|
#else
|
|
if ( !(pik & MASK_KEY_USER1) && (k & MASK_KEY_USER1) ) {
|
|
mos6522.setKeyPressed(0xEF7F);
|
|
mos6522.setShiftPressed(true);
|
|
}
|
|
else if ( (pik & MASK_KEY_USER1) && !(k & MASK_KEY_USER1) ) {
|
|
mos6522.setShiftPressed(false);
|
|
mos6522.setKeyPressed(0);
|
|
}
|
|
|
|
if ( !(pik & MASK_KEY_USER2) && (k & MASK_KEY_USER2) ) {
|
|
mos6522.setShiftPressed(false);
|
|
mos6522.setKeyPressed(ascii2scan['1']);
|
|
}
|
|
else if ( (pik & MASK_KEY_USER2) && !(k & MASK_KEY_USER2) ) {
|
|
mos6522.setShiftPressed(false);
|
|
mos6522.setKeyPressed(0);
|
|
}
|
|
#endif
|
|
pik = k;
|
|
|
|
|
|
VIC_VOICE_HANDLE(0, vicmemory[mos6561.Audio1Register], 0);
|
|
VIC_VOICE_HANDLE(1, vicmemory[mos6561.Audio2Register], 1);
|
|
VIC_VOICE_HANDLE(2, vicmemory[mos6561.Audio3Register], 2);
|
|
VIC_VOICE_HANDLE(3, vicmemory[mos6561.Audio4Register], 0);
|
|
int vol = (vicmemory[mos6561.AudioVolRegister] & 0x0f);
|
|
psid.vol = vol;
|
|
}
|
|
|
|
|
|
|