/* * atari_ps2.c - Sony PlayStation 2 port code * * Copyright (c) 2005 Troy Ayers and Piotr Fusik * Copyright (c) 2005-2014 Atari800 development team (see DOC/CREDITS) * * This file is part of the Atari800 emulator project which emulates * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers. * * Atari800 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Atari800 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Atari800; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ //TODO: map the following keys //control <>: (suits) //akey_clear //AKEY_BREAK //all ALT+key shortcut keys //Atari OPTION key (temple of apshai, others?) //keyboard repeat (when shift nor control pressed) //mouse support //autodetect pal vs ntsc //allow user to map own keyboard keys to controller //enable cdfs, hdd, mass, and host support. #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "atari.h" #include "colours.h" #include "input.h" #include "akey.h" #include "log.h" #include "monitor.h" #include "screen.h" #include "ui.h" #include "util.h" //#ifdef SOUND #include #include "pokeysnd.h" #include "sound.h" extern unsigned char audsrv[]; extern unsigned int size_audsrv; //#endif // define to use T0 and T1 timers #define USE_TIMERS extern unsigned char usbd[]; extern unsigned int size_usbd; extern unsigned char ps2kbd[]; extern unsigned int size_ps2kbd; static GSGLOBAL *gsGlobal = NULL; static int clut[256] __attribute__((aligned(16))); //int PS2KbdCAPS = 0; int PS2KbdSHIFT = 0; int PS2KbdCONTROL = 0; int PS2KbdALT = 0; #define PAD_PORT 0 #define PAD_SLOT 0 static char padBuf[256] __attribute__((aligned(64))); #ifdef USE_TIMERS /* We use T0 for PLATFORM_Time() and T1 for PLATFORM_Sleep(). Note that both timers are just 16-bit. */ #define T0_COUNT (*(volatile unsigned long *) 0x10000000) #define T0_MODE (*(volatile unsigned long *) 0x10000010) #define T0_COMP (*(volatile unsigned long *) 0x10000020) #define T1_COUNT (*(volatile unsigned long *) 0x10000800) #define T1_MODE (*(volatile unsigned long *) 0x10000810) #define T1_COMP (*(volatile unsigned long *) 0x10000820) #define INTC_TIM0 9 #define INTC_TIM1 10 static int t0_interrupt_id = -1; static int t1_interrupt_id = -1; /* The range of int is enough for about 7 years of continuous running. */ static volatile int timer_interrupt_ticks = 0; static int sleeping_thread_id = 0; static int t0_interrupt_handler(int ca) { timer_interrupt_ticks++; T0_MODE |= 0x800; // clear overflow status // __asm__ volatile("sync.l; ei"); // XXX: necessary? return -1; // XXX: or 0? what does it mean? } static int t1_interrupt_handler(int ca) { iWakeupThread(sleeping_thread_id); T1_MODE = 0; // disable // __asm__ volatile("sync.l; ei"); // XXX: necessary? return -1; // XXX: or 0? what does it mean? } static void timer_initialize(void) { T0_MODE = 0; // disable t0_interrupt_id = AddIntcHandler(INTC_TIM0, t0_interrupt_handler, 0); EnableIntc(INTC_TIM0); T0_COUNT = 0; T0_MODE = 0x002 // 576000 Hz clock + 0x080 // start counting + 0x200; // generate interrupt on overflow T1_MODE = 0; // disable t1_interrupt_id = AddIntcHandler(INTC_TIM1, t1_interrupt_handler, 0); EnableIntc(INTC_TIM1); } static void timer_shutdown(void) { T0_MODE = 0; if (t0_interrupt_id >= 0) { DisableIntc(INTC_TIM0); RemoveIntcHandler(INTC_TIM0, t0_interrupt_id); t0_interrupt_id = -1; } T1_MODE = 0; if (t1_interrupt_id >= 0) { DisableIntc(INTC_TIM1); RemoveIntcHandler(INTC_TIM1, t1_interrupt_id); t1_interrupt_id = -1; } } #endif /* USE_TIMERS */ double PLATFORM_Time(void) { #ifdef USE_TIMERS /* AFAIK, multiplication is faster than division, on every CPU architecture */ return (timer_interrupt_ticks * 65536.0 + T0_COUNT) * (1.0 / 576000); #else static double fake_timer = 0; return fake_timer++; #endif } /* this PLATFORM_Sleep() supports times only up to 0.11 sec, which is enough for Atari800 purposes */ /* void PLATFORM_Sleep(double s) { #ifdef USE_TIMERS unsigned long count = 65536 - (unsigned long) (s * 576000); // do nothing if s is less than one T1 tick if (count >= 65536) return; sleeping_thread_id = GetThreadId(); T1_COUNT = count; T1_MODE = 0x002 // 576000 Hz clock + 0x080 // start counting + 0x200; // generate interrupt on overflow // SleepThread(); #endif } */ //volatile int locked = 1; //void wakeup(s32 id, u16 time, void *arg) //{ // locked = 0; //} // //void PLATFORM_Sleep(double s) //{ // // /* 15734 is around 1 second on NTSC */ // /* is about 1ms? */ // SetAlarm(15734 * s, wakeup, 0); // while (locked); // locked = 1; //} void PLATFORM_Sleep(double s) { if (UI_is_active){ int i,ret; for (i=0;i> 16) + (c & 0xff00) + ((c & 0xff) << 16); // swap bits 3 and 4 to workaround a bug in gsKit clut[(i ^ i * 2) & 16 ? i ^ 24 : i] = (c >> 16) + (c & 0xff00) + ((c & 0xff) << 16); } // Clear debug from screen init_scr(); // Initialize graphics gsGlobal = gsKit_init_global(GS_MODE_NTSC); dmaKit_init(D_CTRL_RELE_ON, D_CTRL_MFD_OFF, D_CTRL_STS_UNSPEC, D_CTRL_STD_OFF, D_CTRL_RCYC_8); dmaKit_chan_init(DMA_CHANNEL_GIF); gsGlobal->PSM = GS_PSM_CT32; gsGlobal->Test->ZTE = 0; gsKit_init_screen(gsGlobal); // Init joypad padInit(0); padPortOpen(PAD_PORT, PAD_SLOT, padBuf); #ifdef USE_TIMERS timer_initialize(); #endif #ifdef SOUND if (!Sound_Initialise(argc, argv)) return FALSE; #endif return TRUE; } int PLATFORM_Exit(int run_monitor) { // TODO: shutdown graphics mode Log_flushlog(); #if 0 if (run_monitor && MONITOR_Run()) { // TODO: reinitialize graphics mode return TRUE; } #endif #ifdef USE_TIMERS timer_shutdown(); #endif /* TODO Sound_Exit should not be called here! It only stays here now because the next step restarts the PS2 without ever returning from this function to Atari800_Exit, so the call to Sound_Exit located in the latter function is never invoked. So, should the LoadExecPS2call below get removed, so should this call to Sound_Exit. */ #ifdef SOUND Sound_Exit(); #endif //zzz temp exit procedure //Hard coded to go back to ulaunch fioExit(); SifExitRpc(); LoadExecPS2("mc0:/BOOT/BOOT.ELF", 0, NULL); //zzz end return FALSE; } void PLATFORM_DisplayScreen(void) { GSTEXTURE tex; tex.Width = Screen_WIDTH; tex.Height = Screen_HEIGHT; tex.PSM = GS_PSM_T8; tex.Mem = (UBYTE *) Screen_atari; tex.Clut = clut; tex.Vram = 0x200000; tex.VramClut = 0x280000; tex.Filter = GS_FILTER_LINEAR; // TODO: upload clut just once gsKit_texture_upload(gsGlobal, &tex); gsKit_prim_sprite_texture(gsGlobal, &tex, 0, 0, 32, 0, 640, 480, 32 + 320, 240, 0, 0x80808080); #if 0 gsKit_sync_flip(gsGlobal); #else // flip without vsync // this is a copy of gsKit_sync_flip() code with just gsKit_vsync() call removed GS_SET_DISPFB2(gsGlobal->ScreenBuffer[gsGlobal->ActiveBuffer & 1] / 8192, gsGlobal->Width / 64, gsGlobal->PSM, 0, 0 ); gsGlobal->ActiveBuffer ^= 1; gsGlobal->PrimContext ^= 1; gsGlobal->EvenOrOdd = ((GSREG *) GS_CSR)->FIELD; gsKit_setactive(gsGlobal); #endif } static int PadButtons(void) { struct padButtonStatus buttons; for (;;) { int ret = padGetState(PAD_PORT, PAD_SLOT); if (ret == PAD_STATE_STABLE || ret == PAD_STATE_FINDCTP1) break; if (ret == PAD_STATE_DISCONN) return 0; } padRead(PAD_PORT, PAD_SLOT, &buttons); return ~buttons.btns; } int PLATFORM_Keyboard(void) { int new_pad = PadButtons(); PS2KbdRawKey key; INPUT_key_consol = INPUT_CONSOL_NONE; if (UI_is_active) { if (new_pad & PAD_CROSS) return AKEY_RETURN; if (new_pad & PAD_CIRCLE) return AKEY_ESCAPE; if (new_pad & PAD_LEFT) return AKEY_LEFT; if (new_pad & PAD_RIGHT) return AKEY_RIGHT; if (new_pad & PAD_UP) return AKEY_UP; if (new_pad & PAD_DOWN) return AKEY_DOWN; if (new_pad & PAD_L1) return AKEY_COLDSTART; if (new_pad & PAD_R1) return AKEY_WARMSTART; } //PAD_CROSS is used for PLATFORM_TRIG(). if (new_pad & PAD_TRIANGLE) return AKEY_UI; if (new_pad & PAD_SQUARE) return AKEY_SPACE; if (new_pad & PAD_CIRCLE) return AKEY_RETURN; if (new_pad & PAD_L1) return AKEY_COLDSTART; if (new_pad & PAD_R1) return AKEY_WARMSTART; if (Atari800_machine_type == Atari800_MACHINE_5200) { if (new_pad & PAD_START) return AKEY_5200_START; } else { if (new_pad & PAD_START) INPUT_key_consol ^= INPUT_CONSOL_START; if (new_pad & PAD_SELECT) INPUT_key_consol ^= INPUT_CONSOL_SELECT; if (new_pad & PAD_CROSS) return AKEY_HELP; } if (Atari800_machine_type != Atari800_MACHINE_5200 || UI_is_active) { while (PS2KbdReadRaw(&key) != 0) { if (key.state == PS2KBD_RAWKEY_DOWN) { switch (key.key) { case EOF: Atari800_Exit(FALSE); exit(0); break; case 0x28: return AKEY_RETURN; case 0x29: return AKEY_ESCAPE; case 0x2A: return AKEY_BACKSPACE; case 0x2B: return AKEY_TAB; case 0x2C: return AKEY_SPACE; case 0x46://Print Screen Button return AKEY_SCREENSHOT; case 0x4F: return AKEY_RIGHT; case 0x50: return AKEY_LEFT; case 0x51: return AKEY_DOWN; case 0x52: return AKEY_UP; case 0x58: return AKEY_RETURN; case 0xE0: PS2KbdCONTROL = 1; return AKEY_NONE; case 0xE4: PS2KbdCONTROL = 1; return AKEY_NONE; case 0xE1: PS2KbdSHIFT = 1; return AKEY_NONE; case 0xE2: PS2KbdALT = 1; return AKEY_NONE; case 0xE5: PS2KbdSHIFT = 1; return AKEY_NONE; case 0xE6: PS2KbdALT = 1; return AKEY_NONE; default: break; } } if ((key.state == PS2KBD_RAWKEY_DOWN) && !PS2KbdSHIFT && !PS2KbdALT) { switch (key.key) { case 0x1E: return AKEY_1; case 0X1F: return AKEY_2; case 0x20: return AKEY_3; case 0x21: return AKEY_4; case 0x22: return AKEY_5; case 0x23: return AKEY_6; case 0x24: return AKEY_7; case 0x25: return AKEY_8; case 0x26: return AKEY_9; case 0x27: return AKEY_0; case 0x2D: return AKEY_MINUS; case 0x2E: return AKEY_EQUAL; case 0x2F: return AKEY_BRACKETLEFT; case 0x30: return AKEY_BRACKETRIGHT; case 0x31: return AKEY_BACKSLASH; case 0x33: return AKEY_SEMICOLON; case 0x34: return AKEY_QUOTE; case 0x35: return AKEY_ATARI; case 0x36: return AKEY_COMMA; case 0x37: return AKEY_FULLSTOP; case 0x38: return AKEY_SLASH; case 0x3A://F1 return AKEY_UI; case 0x3E://F5 return AKEY_WARMSTART; case 0x42://F9 return AKEY_EXIT; case 0x43://F10 return AKEY_SCREENSHOT; default: break; } } if ((key.state == PS2KBD_RAWKEY_DOWN) && PS2KbdSHIFT && !PS2KbdCONTROL && !PS2KbdALT) { switch (key.key) { case 0x4: return AKEY_A; case 0x5: return AKEY_B; case 0x6: return AKEY_C; case 0x7: return AKEY_D; case 0x8: return AKEY_E; case 0x9: return AKEY_F; case 0xA: return AKEY_G; case 0xB: return AKEY_H; case 0xC: return AKEY_I; case 0xD: return AKEY_J; case 0xE: return AKEY_K; case 0xF: return AKEY_L; case 0x10: return AKEY_M; case 0x11: return AKEY_N; case 0x12: return AKEY_O; case 0x13: return AKEY_P; case 0x14: return AKEY_Q; case 0x15: return AKEY_R; case 0x16: return AKEY_S; case 0x17: return AKEY_T; case 0x18: return AKEY_U; case 0x19: return AKEY_V; case 0x1A: return AKEY_W; case 0x1B: return AKEY_X; case 0x1C: return AKEY_Y; case 0x1D: return AKEY_Z; case 0x1E: return AKEY_EXCLAMATION; case 0X1F: return AKEY_AT; case 0x20: return AKEY_HASH; case 0x21: return AKEY_DOLLAR; case 0x22: return AKEY_PERCENT; case 0x23: // return AKEY_CIRCUMFLEX; return AKEY_CARET; case 0x24: return AKEY_AMPERSAND; case 0x25: return AKEY_ASTERISK; case 0x26: return AKEY_PARENLEFT; case 0x27: return AKEY_PARENRIGHT; case 0x2B: return AKEY_SETTAB; case 0x2D: return AKEY_UNDERSCORE; case 0x2E: return AKEY_PLUS; case 0x31: return AKEY_BAR; case 0x33: return AKEY_COLON; case 0x34: return AKEY_DBLQUOTE; case 0x36: return AKEY_LESS; case 0x37: return AKEY_GREATER; case 0x38: return AKEY_QUESTION; case 0x3E://Shift+F5 return AKEY_COLDSTART; case 0x43://Shift+F10 return AKEY_SCREENSHOT_INTERLACE; case 0x49://Shift+Insert key return AKEY_INSERT_LINE; case 0x4C://Shift+Backspace Key return AKEY_DELETE_LINE; default: break; } } if ((key.state == PS2KBD_RAWKEY_DOWN) && !PS2KbdSHIFT && !PS2KbdCONTROL && !PS2KbdALT) { switch (key.key) { case 0x4: return AKEY_a; case 0x5: return AKEY_b; case 0x6: return AKEY_c; case 0x7: return AKEY_d; case 0x8: return AKEY_e; case 0x9: return AKEY_f; case 0xA: return AKEY_g; case 0xB: return AKEY_h; case 0xC: return AKEY_i; case 0xD: return AKEY_j; case 0xE: return AKEY_k; case 0xF: return AKEY_l; case 0x10: return AKEY_m; case 0x11: return AKEY_n; case 0x12: return AKEY_o; case 0x13: return AKEY_p; case 0x14: return AKEY_q; case 0x15: return AKEY_r; case 0x16: return AKEY_s; case 0x17: return AKEY_t; case 0x18: return AKEY_u; case 0x19: return AKEY_v; case 0x1A: return AKEY_w; case 0x1B: return AKEY_x; case 0x1C: return AKEY_y; case 0x1D: return AKEY_z; case 0x49: return AKEY_INSERT_CHAR; case 0x4C: return AKEY_DELETE_CHAR; default: break; } } if ((key.state == PS2KBD_RAWKEY_DOWN) && PS2KbdCONTROL && !PS2KbdALT) { switch(key.key) { case 0x4: return AKEY_CTRL_a; case 0x5: return AKEY_CTRL_b; case 0x6: return AKEY_CTRL_c; case 0x7: return AKEY_CTRL_d; case 0x8: return AKEY_CTRL_e; case 0x9: return AKEY_CTRL_f; case 0xA: return AKEY_CTRL_g; case 0xB: return AKEY_CTRL_h; case 0xC: return AKEY_CTRL_i; case 0xD: return AKEY_CTRL_j; case 0xE: return AKEY_CTRL_k; case 0xF: return AKEY_CTRL_l; case 0x10: return AKEY_CTRL_m; case 0x11: return AKEY_CTRL_n; case 0x12: return AKEY_CTRL_o; case 0x13: return AKEY_CTRL_p; case 0x14: return AKEY_CTRL_q; case 0x15: return AKEY_CTRL_r; case 0x16: return AKEY_CTRL_s; case 0x17: return AKEY_CTRL_t; case 0x18: return AKEY_CTRL_u; case 0x19: return AKEY_CTRL_v; case 0x1A: return AKEY_CTRL_w; case 0x1B: return AKEY_CTRL_x; case 0x1C: return AKEY_CTRL_y; case 0x1D: return AKEY_CTRL_z; case 0x1E: return AKEY_CTRL_1; case 0x1F: return AKEY_CTRL_2; case 0x20: return AKEY_CTRL_3; case 0x21: return AKEY_CTRL_4; case 0x22: return AKEY_CTRL_5; case 0x23: return AKEY_CTRL_6; case 0x24: return AKEY_CTRL_7; case 0x25: return AKEY_CTRL_8; case 0x26: return AKEY_CTRL_9; case 0x27: return AKEY_CTRL_0; case 0x2B: return AKEY_CLRTAB; case 0x33: return AKEY_SEMICOLON | AKEY_CTRL; case 0x36: return AKEY_LESS | AKEY_CTRL; case 0x37: return AKEY_GREATER | AKEY_CTRL; default: break; } } if ((key.state == PS2KBD_RAWKEY_DOWN) && PS2KbdALT) { switch(key.key) { //case dcr ylsa case 0x7: UI_alt_function = UI_MENU_DISK; return AKEY_UI; case 0x6: UI_alt_function = UI_MENU_CARTRIDGE; return AKEY_UI; case 0x15: UI_alt_function = UI_MENU_RUN; return AKEY_UI; case 0x1C: UI_alt_function = UI_MENU_SYSTEM; return AKEY_UI; case 0xF: UI_alt_function = UI_MENU_LOADSTATE; return AKEY_UI; case 0x16: UI_alt_function = UI_MENU_SAVESTATE; return AKEY_UI; case 0x17: UI_alt_function = UI_MENU_CASSETTE; return AKEY_UI; case 0x4: UI_alt_function = UI_MENU_ABOUT; return AKEY_UI; default: break; } } if (key.state == PS2KBD_RAWKEY_UP) { switch (key.key) { case 0x39: return AKEY_CAPSTOGGLE; case 0xE0: PS2KbdCONTROL = 0; return AKEY_NONE; case 0xE4: PS2KbdCONTROL = 0; return AKEY_NONE; case 0xE1: PS2KbdSHIFT = 0; return AKEY_NONE; case 0xE2: PS2KbdALT = 0; return AKEY_NONE; case 0xE5: PS2KbdSHIFT = 0; return AKEY_NONE; case 0xE6: PS2KbdALT = 0; return AKEY_NONE; default: break; } } } } return AKEY_NONE; } int PLATFORM_PORT(int num) { int ret = 0xff; if (num == 0) { int pad = PadButtons(); if (pad & PAD_LEFT) ret &= 0xf0 | INPUT_STICK_LEFT; if (pad & PAD_RIGHT) ret &= 0xf0 | INPUT_STICK_RIGHT; if (pad & PAD_UP) ret &= 0xf0 | INPUT_STICK_FORWARD; if (pad & PAD_DOWN) ret &= 0xf0 | INPUT_STICK_BACK; } return ret; } int PLATFORM_TRIG(int num) { if (num == 0 && PadButtons() & PAD_CROSS) return 0; return 1; } char dir_path[FILENAME_MAX]; static int dir_n; static int dir_i; // XXX: use followup calls to get directory entries one-by-one? #define MAX_FILES_PER_DIR 1000 static mcTable mcDir[MAX_FILES_PER_DIR]; int Atari_OpenDir(const char *filename) { // TODO: support other devices if (strncmp(filename, "mc0:/", 5) != 0) return FALSE; dir_n = mcGetDir(0, 0, filename + 4, 0 /* followup flag */, MAX_FILES_PER_DIR, mcDir); mcSync(0,NULL,&dir_n); if (dir_n < 0) return FALSE; dir_i = 0; // XXX: does it know (and needs to know) that "mc0:/" is a root directory? Util_splitpath(filename, dir_path, NULL); return TRUE; } int Atari_ReadDir(char *fullpath, char *filename, int *isdir, int *readonly, int *size, char *timetext) { const mcTable *p; if (dir_i >= dir_n) return FALSE; p = mcDir + dir_i; if (fullpath != NULL) Util_catpath(fullpath, dir_path, p->name); if (filename != NULL) strcpy(filename, p->name); if (isdir != NULL) *isdir = (p->attrFile & MC_ATTR_SUBDIR) ? TRUE : FALSE; if (readonly != NULL) *readonly = (p->attrFile & MC_ATTR_WRITEABLE) ? FALSE : TRUE; // XXX: MC_ATTR_PROTECTED ? if (size != NULL) *size = (int) (p->fileSizeByte); if (timetext != NULL) { // FIXME: adjust from GMT to local time int hour = p->_modify.hour; char ampm = 'a'; if (hour >= 12) { hour -= 12; ampm = 'p'; } if (hour == 0) hour = 12; sprintf(timetext, "%2d-%02d-%02d %2d:%02d%c", p->_modify.month, p->_modify.day, p->_modify.year % 100, hour, p->_modify.sec, ampm); } dir_i++; return TRUE; } #ifdef SOUND int Sound_Initialise(int *argc, char *argv[]) { if (audsrv_init() != 0) Log_print("failed to initialize audsrv: %s", audsrv_get_error_string()); else { struct audsrv_fmt_t format; format.bits = 8; format.freq = 44100; format.channels = 1; audsrv_set_format(&format); audsrv_set_volume(MAX_VOLUME); POKEYSND_Init(POKEYSND_FREQ_17_EXACT, 44100, 1, 0); } return TRUE; } void Sound_Exit(void) { audsrv_quit(); } void Sound_Update(void) { static char buffer[44100 / 50]; unsigned int nsamples = (Atari800_tv_mode == Atari800_TV_NTSC) ? (44100 / 60) : (44100 / 50); POKEYSND_Process(buffer, nsamples); audsrv_wait_audio(nsamples); audsrv_play_audio(buffer, nsamples); } void Sound_Pause(void) { audsrv_stop_audio(); } void Sound_Continue(void) { if (audsrv_init() != 0) Log_print("failed to initialize audsrv: %s", audsrv_get_error_string()); else { struct audsrv_fmt_t format; format.bits = 8; format.freq = 44100; format.channels = 1; audsrv_set_format(&format); audsrv_set_volume(MAX_VOLUME); } } #endif /* SOUND */ int main(int argc, char **argv) { loadModules(); /* initialise Atari800 core */ if (!Atari800_Initialise(&argc, argv)) return 3; /* main loop */ for (;;) { INPUT_key_code = PLATFORM_Keyboard(); Atari800_Frame(); if (Atari800_display_screen){ PLATFORM_DisplayScreen();} } }