/* ds81 - Nintendo DS ZX81 emulator. Copyright (C) 2006 Ian Cowburn This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. $Id$ */ #include #include #include #include #include #include #include "ds81_ipc.h" #include "ds81_global.h" #include "ds81_fader.h" #include "framebuffer.h" #include "gui.h" #include "keyboard.h" #include "z80.h" #include "zx81.h" #include "tapes.h" #include "splashimg_bin.h" #include "rom_font_bin.h" /* ---------------------------------------- STATIC DATA */ static const char *main_menu[]= { "Reset ZX81", "Select Tape", "Sticky Shift On", "Sticky Shift Off", "Map Joypad to Keys", "Select Web Server (WFC)", "Cancel", NULL }; typedef enum { MenuReset, MenuSelectTape, MenuStickyOn, MenuStickyOff, MenuMapJoypad, MenuConfigNetwork } MenuOpt; /* Hope ints are atomic... Having said that we are on the one processor so I doubt it'll mess up. Which are usually famous last words. */ static volatile int wifi_enabled = FALSE; static u32 main_heartbeat; /* ---------------------------------------- IRQ FUNCS */ static void Timer(void) { { static int c=0; char s[32]; sprintf(s,"C:%d H:%d",(c++)/50,IPC->heartbeat); FB_Print(s,0,184-10,FB_RGB(31,31,31),FB_RGB(10,10,10)); } Wifi_Timer(50); } static void ARM9_SyncToARM7(void) { REG_IPC_FIFO_TX = DS81_WIFI_SYNC_IPC; } static void ARM9_Fifo(void) { static int c=0; u32 v; v = REG_IPC_FIFO_RX; { char s[32]; sprintf(s,"%d:%x ",c++,v); FB_Print(s,0,184,FB_RGB(31,31,31),FB_RGB(10,10,10)); } if (v == DS81_WIFI_SYNC_IPC) { Wifi_Sync(); } } /* ---------------------------------------- DISPLAY FUNCS */ static void VBlankFunc(void) { #if 0 char t[32]; if (IPC->rtc_hours < 12) { sprintf(t,"%2.2d:%2.2d:%2.2d",IPC->rtc_hours, IPC->rtc_minutes, IPC->rtc_seconds); } else { sprintf(t,"%2.2d:%2.2d:%2.2d",IPC->rtc_hours-40, IPC->rtc_minutes, IPC->rtc_seconds); } FB_Print(t,192,0,FB_RGB(31,31,31),FB_RGB(10,10,10)); sprintf(t,"H:%8.8x M:%8.8x",IPC->heartbeat,main_heartbeat); FB_Print(t,0,0,FB_RGB(31,31,31),FB_RGB(10,10,10)); #endif scanKeys(); } static void Splash(void) { static const char *text[]= { "DS81 \177 2006 Ian C", " ", "ZX81 ROM \177 1981", "Nine Tiles Networks LTD", " ", "PRESS A TO CONTINUE", " ", "http://www.noddybox.co.uk/", " ", " ", " ", " ", "Checking for FAT device...", NULL }; sImage img; int f; int y; int res=FALSE; ZX81DisplayString("10 print '%the zx81 is ace%'\n20 goto 10"); FB_Clear(); loadPCX(splashimg_bin,&img); image8to16(&img); FB_Blit(&img,0,0); y = 10; for(f=0;text[f];f++) { FB_Centre(text[f],y,FB_RGB(31,31,31),-1); y += 8; } y += 8; #ifndef DS81_DISABLE_FAT res = fatInitDefault(); #endif if (res) { ZX81EnableFileSystem(TRUE); FB_Centre("Found a FAT device.",y,FB_RGB(31,31,31),-1); y += 8; FB_Centre("If you place .P tape files in",y,FB_RGB(31,31,31),-1); y += 8; FB_Centre("the top directory or ZX81TAPE",y,FB_RGB(31,31,31),-1); y += 8; FB_Centre("then you should be able to load",y,FB_RGB(31,31,31),-1); y += 8; FB_Centre("GAME.P with the command",y,FB_RGB(31,31,31),-1); y += 8; FB_Centre("LOAD \"GAME\"",y,FB_RGB(31,31,31),-1); y += 8; } else { ZX81EnableFileSystem(FALSE); FB_Centre("Sorry, but you don't have a",y,FB_RGB(31,31,31),-1); y += 8; FB_Centre("supported FAT device.",y,FB_RGB(31,31,31),-1); y += 8; FB_Centre("Only the internal tape",y,FB_RGB(31,31,31),-1); y += 8; FB_Centre("files can be used.",y,FB_RGB(31,31,31),-1); y += 8; } while(!(keysDown() & KEY_A)) { swiWaitForVBlank(); } } /* ---------------------------------------- JOYPAD MAPPING */ static void MapJoypad(void) { SoftKeyEvent ev; SoftKey pad = NUM_SOFT_KEYS; int done = FALSE; char text[256]; SK_DisplayKeyboard(BG_GFX_SUB); ZX81DisplayString("press the joypad button you want\n" "to define and then the ZX81 key\n" "you want to use.\n\n" "press on the config banner to\n" "finish."); while(!done) { while(SK_GetBareEvent(&ev)) { if (ev.pressed) { if (ev.key==SK_ABOUT || ev.key==SK_CONFIG) { done = true; } } else { if (ev.key>=SK_PAD_UP && ev.key<=SK_PAD_SELECT) { pad = ev.key; /* Now, just how dumb was making % the inverse on/off... */ sprintf(text,"defining\n %%%s%%",SK_KeyName(pad)); ZX81DisplayString(text); } if (ev.key<=SK_SPACE && pad!=NUM_SOFT_KEYS) { sprintf(text,"mapped\n %%%s%%\nto\n %%%s%%", SK_KeyName(pad),SK_KeyName(ev.key)); ZX81DisplayString(text); SK_DefinePad(pad,ev.key); pad = NUM_SOFT_KEYS; } } } swiWaitForVBlank(); } } /* ---------------------------------------- NETWORK CONFIGURATION */ static const char *AssocStatus(enum WIFI_ASSOCSTATUS s, int *exit_loop) { switch(s) { case ASSOCSTATUS_DISCONNECTED: return "Not trying to connect..."; case ASSOCSTATUS_SEARCHING: return "Searching for AP..."; case ASSOCSTATUS_AUTHENTICATING: return "Connecting (authenticate)..."; case ASSOCSTATUS_ASSOCIATING: return "Connecting (associating)..."; case ASSOCSTATUS_ACQUIRINGDHCP: return "Requesting IP address..."; case ASSOCSTATUS_ASSOCIATED: *exit_loop = 1; return "Associated"; case ASSOCSTATUS_CANNOTCONNECT: *exit_loop = 1; return "Cannot connect"; default: *exit_loop = 1; return "Unknown status"; } } static void ConfigNetwork(void) { int col; int coli; u32 wifi_pass; enum WIFI_ASSOCSTATUS assoc; int exit_loop; FB_Clear(); FB_Centre("Initialising WIFI...",0,FB_RGB(31,31,31),-1); wifi_pass = Wifi_Init(WIFIINIT_OPTION_USELED); REG_IPC_FIFO_TX = DS81_WIFI_INIT_IPC; REG_IPC_FIFO_TX = wifi_pass; Wifi_SetSyncHandler(ARM9_SyncToARM7); FB_Centre("Waiting for WIFI...",10,FB_RGB(31,31,31),-1); col = 31; coli = -1; while(Wifi_CheckInit() == 0) { FB_Centre("Waiting for WIFI...",10,FB_RGB(col,col,col),-1); DS81_BOUNCE(col,coli); swiWaitForVBlank(); } FB_Centre("Waiting for WIFI...",10,FB_RGB(31,31,31),-1); wifi_enabled = TRUE; FB_Centre("Using WFC settings...",20,FB_RGB(31,31,31),-1); Wifi_AutoConnect(); /* { static Wifi_AccessPoint p; static unsigned char key[32]; strcpy(p.ssid,"NODDYBOX_WAP"); p.ssid_len=strlen(p.ssid); p.channel=11; Wifi_ConnectAP(&p,0,0,key); } */ FB_Centre("Waiting for association...",30,FB_RGB(31,31,31),-1); exit_loop = FALSE; while(!exit_loop) { assoc = Wifi_AssocStatus(); FB_Centre(AssocStatus(assoc,&exit_loop),40,FB_RGB(col,col,col),-1); DS81_BOUNCE(col,coli); swiWaitForVBlank(); } FB_Centre(AssocStatus(assoc,&exit_loop),40,FB_RGB(31,31,31),-1); FB_Centre("Press a key...",60,FB_RGB(31,31,31),-1); while(!keysDown()) { swiWaitForVBlank(); } while(keysHeld()) { swiWaitForVBlank(); } SK_DisplayKeyboard(BG_GFX_SUB); swiWaitForVBlank(); } /* ---------------------------------------- MAIN */ int main(int argc, char *argv[]) { Z80 *z80; powerON(POWER_ALL_2D); /* Set up main text screen and load the ROM character data */ videoSetMode(MODE_0_2D | DISPLAY_BG0_ACTIVE); vramSetBankA(VRAM_A_MAIN_BG_0x6000000); vramSetBankB(VRAM_B_MAIN_BG_0x6020000); BG0_CR = BG_COLOR_256|BG_MAP_BASE(0)|BG_TILE_BASE(1); BG0_X0 = 0; BG0_Y0 = 0; BG_PALETTE[0] = RGB15(31,31,31); BG_PALETTE[1] = RGB15(0,0,0); dmaCopy(rom_font_bin,(void *)BG_TILE_RAM(1),rom_font_bin_size); /* Set up the sub-screen for rotation (basically for use as a framebuffer) */ videoSetModeSub(MODE_5_2D | DISPLAY_BG2_ACTIVE); vramSetBankC(VRAM_C_SUB_BG_0x6200000); SUB_BG2_CR = BG_BMP16_256x256; SUB_BG2_XDX = 0x100; SUB_BG2_XDY = 0; SUB_BG2_YDX = 0; SUB_BG2_YDY = 0x100; SUB_BG2_CX = 0; SUB_BG2_CY = 0; /* Tell 'framebuffer' routines to use this */ FB_Init(BG_GFX_SUB); /* Set up interrupts and timers */ irqInit(); irqSet(IRQ_VBLANK,VBlankFunc); irqEnable(IRQ_VBLANK); REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_RECV_IRQ; TIMER3_CR = 0; irqSet(IRQ_TIMER3,Timer); TIMER3_DATA = TIMER_FREQ_256(20); TIMER3_CR = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_DIV_256; irqEnable(IRQ_TIMER3); irqSet(IRQ_FIFO_NOT_EMPTY,ARM9_Fifo); irqEnable(IRQ_FIFO_NOT_EMPTY); /* All required stuff initialised */ keysSetRepeat(30,15); z80 = Z80Init(ZX81ReadMem, ZX81WriteMem, ZX81ReadPort, ZX81WritePort, NULL); if (!z80) { GUI_Alert(TRUE,"Failed to initialise\nthe Z80 CPU emulation!"); } ZX81Init((uint16*)SCREEN_BASE_BLOCK(0), z80); Splash(); SK_DisplayKeyboard(BG_GFX_SUB); SK_SetSticky(SK_SHIFT,1); while(1) { SoftKeyEvent ev; main_heartbeat++; Z80Exec(z80); while(SK_GetEvent(&ev)) { switch(ev.key) { case SK_ABOUT: case SK_CONFIG: if (ev.pressed) { switch(GUI_Menu(main_menu)) { case MenuReset: ZX81Reset(z80); break; case MenuSelectTape: SelectTape(); break; case MenuStickyOn: SK_SetSticky(SK_SHIFT,1); break; case MenuStickyOff: SK_SetSticky(SK_SHIFT,0); break; case MenuMapJoypad: MapJoypad(); break; case MenuConfigNetwork: ConfigNetwork(); break; } SK_DisplayKeyboard(BG_GFX_SUB); } break; default: ZX81HandleKey(ev.key,ev.pressed); break; } } } return 0; }