diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e6e18c..1d07050 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,3 +132,19 @@ jobs: with: name: arcem-${{ matrix.amiga.host }} path: ./arcem + + build-nds: + name: Nintendo DS + runs-on: ubuntu-latest + container: devkitpro/devkitarm:20241104 + + steps: + - uses: actions/checkout@v3 + - name: Configure + run: $DEVKITPRO/portlibs/nds/bin/arm-none-eabi-cmake -B build/ -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} . + - name: Build + run: cmake --build build/ --config ${{env.BUILD_TYPE}} --parallel + - uses: actions/upload-artifact@v3 + with: + name: arcem-nds + path: ./build/ArcEm.nds diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e9132a..08cf100 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,15 @@ set(ARCEM_X_SOURCES X/pseudo.c X/true.c ) +set(ARCEM_NDS_SOURCES + nds/ControlPane.c + nds/ControlPane.h + nds/DispKbd.c + nds/filecalls.c + nds/filecalls_internal.h + nds/KeyTable.h + nds/main.c +) set(ARCEM_VC_SOURCES vc/dirent.h ) @@ -83,16 +92,25 @@ set(ARCEM_WIN_SOURCES win/win.c win/win.h ) +set(ARCEM_EXTNROM_MODULES + support_modules/hostfs/hostfs,ffa + support_modules/hostfs/hostfsfiler,ffa + support_modules/modes/ArcemModes,ffa + support_modules/scrollwheel/scrollwheel,ffa + support_modules/support/support,ffa +) if(WIN32) set(DEFAULT_SYSTEM "win") +elseif(NINTENDO_DS) + set(DEFAULT_SYSTEM "nds") elseif(UNIX AND NOT APPLE AND NOT HAIKU) set(DEFAULT_SYSTEM "X") else() set(DEFAULT_SYSTEM "SDL2") endif() -set(SYSTEM ${DEFAULT_SYSTEM} CACHE STRING "System to compile for. Options: X SDL2 SDL1 win") -set_property(CACHE SYSTEM PROPERTY STRINGS X SDL2 SDL1 win) +set(SYSTEM ${DEFAULT_SYSTEM} CACHE STRING "System to compile for. Options: X SDL2 SDL1 nds win") +set_property(CACHE SYSTEM PROPERTY STRINGS X SDL2 SDL1 nds win) option(EXTNROM_SUPPORT "Build with Extension ROM support" ON) if(EXTNROM_SUPPORT) @@ -146,6 +164,23 @@ elseif(${SYSTEM} STREQUAL "X") find_package(Threads REQUIRED) target_link_libraries(Threads::Threads) endif() +elseif(${SYSTEM} STREQUAL "nds") + add_executable(arcem ${ARCEM_SOURCES} ${ARCEM_ARCH_SOURCES} ${ARCEM_INIH_SOURCES} ${ARCEM_NDS_SOURCES}) + target_include_directories(arcem PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/nds) + target_compile_definitions(arcem PRIVATE SYSTEM_nds USE_FAKEMAIN NO_OPEN64) + + target_compile_options(arcem PRIVATE -mthumb -mthumb-interwork) + target_link_options(arcem PRIVATE -Wl,--gc-sections -mthumb -mthumb-interwork) + + grit_add_binary_target(bg nds/img/bg.png) + grit_add_binary_target(font nds/img/font.png NO_MAP) + grit_add_binary_target(keys nds/img/keys.png NO_MAP) + dkp_add_embedded_binary_library(grit_data bg font keys) + target_link_libraries(arcem PRIVATE grit_data filesystem fat) + + if(CALICO_ROOT) + target_compile_definitions(arcem PRIVATE -D__CALICO__) + endif() elseif(${SYSTEM} STREQUAL "win") option(SOUND_SUPPORT "Build with sound support" ON) if(SOUND_SUPPORT) @@ -169,6 +204,25 @@ elseif(APPLE) OUTPUT_NAME "ArcEm" MACOSX_BUNDLE TRUE ) +elseif(NINTENDO_DS) + file(COPY ${ARCEM_EXTNROM_MODULES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/nitrofs/extnrom) + set_target_properties(arcem PROPERTIES OUTPUT_NAME "ArcEm") + if(CALICO_ROOT) + nds_create_rom(arcem + ARM7 lykoi + SUBTITLE1 "Archimedes Emulator" + SUBTITLE2 "WIP using Calico" + ICON nds/arc.bmp + NITROFS ${CMAKE_CURRENT_BINARY_DIR}/nitrofs + ) + else() + nds_create_rom(arcem + SUBTITLE1 "Archimedes Emulator" + SUBTITLE2 "WIP" + ICON nds/arc.bmp + NITROFS ${CMAKE_CURRENT_BINARY_DIR}/nitrofs + ) + endif() endif() target_include_directories(arcem PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/arch) @@ -194,6 +248,7 @@ source_group(src FILES ${ARCEM_SOURCES}) source_group(src\\arch FILES ${ARCEM_ARCH_SOURCES}) source_group(src\\X FILES ${ARCEM_X_SOURCES}) source_group(src\\SDL FILES ${ARCEM_SDL_SOURCES}) +source_group(src\\nds FILES ${ARCEM_NDS_SOURCES}) source_group(src\\vc FILES ${ARCEM_VC_SOURCES}) source_group(src\\win FILES ${ARCEM_WIN_SOURCES}) source_group(libs\\inih FILES ${ARCEM_INIH_SOURCES}) diff --git a/arch/ArcemConfig.c b/arch/ArcemConfig.c index 6766100..dee98e8 100644 --- a/arch/ArcemConfig.c +++ b/arch/ArcemConfig.c @@ -76,7 +76,11 @@ void ArcemConfig_SetupDefaults(ArcemConfig *pConfig) #if defined(EXTNROM_SUPPORT) /* The default directory is extnrom in the current working directory */ +#ifdef SYSTEM_nds + pConfig->sEXTNDirectory = arcemconfig_StringDuplicate("nitro:/extnrom"); +#else pConfig->sEXTNDirectory = arcemconfig_StringDuplicate("extnrom"); +#endif /* If we've run out of memory this early, something is very wrong */ if(NULL == pConfig->sEXTNDirectory) { ControlPane_Error(EXIT_FAILURE,"Failed to allocate memory for initial configuration. Please free up more memory.\n"); @@ -239,6 +243,7 @@ void ArcemConfig_ParseCommandLine(ArcemConfig *pConfig, int argc, char *argv[]) { unsigned int uValue; int iArgument = 0; +#ifndef SYSTEM_nds char sHelpString[] = "Arcem \n" " Where options are one or more of the following\n" @@ -270,6 +275,7 @@ void ArcemConfig_ParseCommandLine(ArcemConfig *pConfig, int argc, char *argv[]) " --menukeys - Specify which key numbers open the tweak menu\n" #endif /* SYSTEM_riscos_single */ ; +#endif /* No commandline arguments? */ if(0 == argc) { @@ -290,6 +296,7 @@ void ArcemConfig_ParseCommandLine(ArcemConfig *pConfig, int argc, char *argv[]) iArgument = 1; while(iArgument < argc) { +#ifndef SYSTEM_nds if(0 == strcmp("--version", argv[iArgument])) { printf("Arcem %s\n", Version); @@ -300,7 +307,9 @@ void ArcemConfig_ParseCommandLine(ArcemConfig *pConfig, int argc, char *argv[]) exit(EXIT_SUCCESS); } - else if(0 == strcmp("--config", argv[iArgument])) { + else +#endif + if(0 == strcmp("--config", argv[iArgument])) { if(iArgument+1 < argc) { /* Is there a following argument? */ ini_parse(argv[iArgument + 1], ArcemConfig_Handler, pConfig); iArgument += 2; diff --git a/arch/dbugsys.h b/arch/dbugsys.h index 874a01d..d9c151a 100644 --- a/arch/dbugsys.h +++ b/arch/dbugsys.h @@ -9,6 +9,7 @@ #ifndef _DBUGSYS_H_ #define _DBUGSYS_H_ +#include "c99.h" #include #include @@ -26,6 +27,21 @@ #undef DEBUG_HDC63463 #undef DEBUG_CONFIG +#if defined(SYSTEM_nds) && defined(NDEBUG) +#undef IOC_WARN +#undef WARN +#undef WARN_MEMC +#undef WARN_I2C +#undef WARN_DATA +#undef WARN_DMAWRITE +#undef WARN_DMAREAD +#undef WARN_INTS +#undef WARN_VIDC +#undef WARN_KEYBOARD +#undef WARN_FDC1772 +#undef WARN_HDC63463 +#undef WARN_CONFIG +#else #define IOC_WARN #define WARN #define WARN_MEMC @@ -39,6 +55,7 @@ #define WARN_FDC1772 #define WARN_HDC63463 #define WARN_CONFIG +#endif #define dbug_stdout printf diff --git a/arch/paldisplaydev.c b/arch/paldisplaydev.c index 21bbcba..b80c67d 100644 --- a/arch/paldisplaydev.c +++ b/arch/paldisplaydev.c @@ -108,6 +108,12 @@ void PDD_Name(Host_EndUpdate)(ARMul_State *state,PDD_Row *row) - End updating the region of the row + ARMword *PDD_Name(Host_TransferUpdate)(ARMul_State *state,PDD_Row *row, + unsigned int count,const ARMword *src) + - Write 'count' bits of the row to the screen. + - 'count' will always be a multiple of 32. + - This is only used in DS builds at the moment. + void PDD_Name(Host_AdvanceRow)(ARMul_State *state,PDD_Row *row, unsigned int count) - Advance the row pointer by 'count' bits @@ -211,7 +217,8 @@ struct PDD_Name(DisplayInfo) { #define ROWFUNC_FORCE 0x1 /* Force row to be fully redrawn */ #define ROWFUNC_UPDATED 0x2 /* Flag used to indicate whether anything was done */ -#define ROWFUNC_UNALIGNED 0x4 /* Flag that gets set if we know we can't use the byte-aligned rowfuncs */ +#define ROWFUNC_UNALIGNED_BYTE 0x4 /* Flag that gets set if we know we can't use the byte-aligned rowfuncs */ +#define ROWFUNC_UNALIGNED_WORD 0x8 /* Flag that gets set if we know we can't use the word-aligned rowfuncs */ /* @@ -303,6 +310,51 @@ static inline int PDD_Name(RowFunc1XSameByteAligned)(ARMul_State *state,PDD_Row return (flags & ROWFUNC_UPDATED); } +static inline int PDD_Name(RowFunc1XSameWordAligned)(ARMul_State *state,PDD_Row drow,int flags) +{ + uint32_t Vptr = DC.Vptr>>5; + uint32_t Vstart = MEMC.Vstart<<2; + uint32_t Vend = (MEMC.Vend+1)<<2; /* Point to pixel after end */ + const ARMword *RAM = MEMC.PhysRam; + int Remaining = DC.BitWidth>>5; + + /* Sanity checks to avoid looping forever */ + if((Vptr >= Vend) || (Vstart >= Vend)) + return 0; + if(Vptr >= Vend) + Vptr = Vstart; + + /* Process the row */ + while(Remaining > 0) + { + uint32_t FlagsOffset = Vptr/(UPDATEBLOCKSIZE/4); + int Available = MIN(Remaining,MIN(((FlagsOffset+1)*(UPDATEBLOCKSIZE/4))-Vptr,Vend-Vptr)); + + if((flags & ROWFUNC_FORCE) || (HD.UpdateFlags[FlagsOffset] != MEMC.UpdateFlags[FlagsOffset])) + { + /* Process the pixels in this region, stopping at end of row/update block/Vend */ +#ifndef SYSTEM_nds + int outoffset; + ARMword *out = PDD_Name(Host_BeginUpdate)(state,&drow,Available<<5,&outoffset); + EndianWordCpy(out+(outoffset>>5),RAM+Vptr,Available); + PDD_Name(Host_EndUpdate)(state,&drow); +#else + PDD_Name(Host_TransferUpdate)(state,&drow,Available<<5,RAM+Vptr); +#endif + flags |= ROWFUNC_UPDATED; + } + PDD_Name(Host_AdvanceRow)(state,&drow,Available<<5); + Vptr += Available; + Remaining -= Available; + if(Vptr >= Vend) + Vptr = Vstart; + } + + DC.Vptr = Vptr<<5; + + return (flags & ROWFUNC_UPDATED); +} + /* Row output via ExpandTable @@ -430,6 +482,45 @@ static inline void PDD_Name(RowFunc1XSameByteAlignedNoFlags)(ARMul_State *state, DC.Vptr = Vptr<<3; } +static inline void PDD_Name(RowFunc1XSameWordAlignedNoFlags)(ARMul_State *state,PDD_Row drow) +{ + uint32_t Vptr = DC.Vptr>>5; + uint32_t Vstart = MEMC.Vstart<<2; + uint32_t Vend = (MEMC.Vend+1)<<2; /* Point to pixel after end */ + const ARMword *RAM = MEMC.PhysRam; + int Remaining = DC.BitWidth>>5; + + /* Sanity checks to avoid looping forever */ + if((Vptr >= Vend) || (Vstart >= Vend)) + return; + if(Vptr >= Vend) + Vptr = Vstart; + + /* Process the row */ + while(Remaining > 0) + { + int Available = MIN(Remaining,Vend-Vptr); + + /* Process the pixels in this region, stopping at end of row/update block/Vend */ +#ifndef SYSTEM_nds + int outoffset; + ARMword *out = PDD_Name(Host_BeginUpdate)(state,&drow,Available<<5,&outoffset); + EndianWordCpy(out+(outoffset>>5),RAM+Vptr,Available); + PDD_Name(Host_EndUpdate)(state,&drow); +#else + PDD_Name(Host_TransferUpdate)(state,&drow,Available<<5,RAM+Vptr); +#endif + + PDD_Name(Host_AdvanceRow)(state,&drow,Available<<5); + Vptr += Available; + Remaining -= Available; + if(Vptr >= Vend) + Vptr = Vstart; + } + + DC.Vptr = Vptr<<5; +} + /* Row output via ExpandTable @@ -708,7 +799,9 @@ static void PDD_Name(EventFunc)(ARMul_State *state,CycleCount nowtime) /* We can test these values once here, so that it's only output alignment that we need to worry about during the loop */ if((DC.Vptr & 0x7) || ((Width*BPP)&0x7)) - flags |= ROWFUNC_UNALIGNED; + flags |= ROWFUNC_UNALIGNED_WORD | ROWFUNC_UNALIGNED_BYTE; + else if((DC.Vptr & 0x31) || ((Width*BPP)&0x31)) + flags |= ROWFUNC_UNALIGNED_WORD; if(DisplayDev_UseUpdateFlags) { @@ -732,7 +825,11 @@ static void PDD_Name(EventFunc)(ARMul_State *state,CycleCount nowtime) { updated = PDD_Name(RowFuncExpandTable)(state,hrow,flags); } - else if(!(flags & ROWFUNC_UNALIGNED) && !(alignment & 0x7)) + else if(!(flags & ROWFUNC_UNALIGNED_WORD) && !(alignment & 0x31)) + { + updated = PDD_Name(RowFunc1XSameWordAligned)(state,hrow,flags); + } + else if(!(flags & ROWFUNC_UNALIGNED_BYTE) && !(alignment & 0x7)) { updated = PDD_Name(RowFunc1XSameByteAligned)(state,hrow,flags); } @@ -783,7 +880,11 @@ static void PDD_Name(EventFunc)(ARMul_State *state,CycleCount nowtime) { PDD_Name(RowFuncExpandTableNoFlags)(state,hrow); } - else if(!(flags & ROWFUNC_UNALIGNED) && !(alignment & 0x7)) + else if(!(flags & ROWFUNC_UNALIGNED_WORD) && !(alignment & 0x31)) + { + PDD_Name(RowFunc1XSameWordAlignedNoFlags)(state,hrow); + } + else if(!(flags & ROWFUNC_UNALIGNED_BYTE) && !(alignment & 0x7)) { PDD_Name(RowFunc1XSameByteAlignedNoFlags)(state,hrow); } diff --git a/armdefs.h b/armdefs.h index 0dbe41c..7557954 100644 --- a/armdefs.h +++ b/armdefs.h @@ -269,7 +269,7 @@ struct ARMul_State { }; -#ifdef AMIGA +#if defined(AMIGA) || defined(SYSTEM_nds) extern void *state_alloc(int s); extern void state_free(void *p); #else diff --git a/c99.h b/c99.h index 641b2e5..37ba810 100644 --- a/c99.h +++ b/c99.h @@ -64,4 +64,22 @@ typedef unsigned char bool; #define inline __inline #endif +#ifdef SYSTEM_nds +/* Use integer only versions of printf and scanf to reduce the executable size */ +#include + +#define printf iprintf +#define fprintf fiprintf +#define sprintf siprintf +#define snprintf sniprintf + +#define vprintf viprintf +#define vfprintf vfiprintf +#define vsprintf vsiprintf +#define vsnprintf vsniprintf + +#define sscanf siscanf +#define fscanf fiscanf +#endif + #endif diff --git a/dagstandalone.c b/dagstandalone.c index 05ad6fc..539d8f8 100644 --- a/dagstandalone.c +++ b/dagstandalone.c @@ -39,6 +39,7 @@ /**************************************************************/ #ifndef _WIN32 #ifndef AMIGA +#ifndef SYSTEM_nds static void dagstandalone_handlesignal(int sig) { dbug("Terminate ARMulator - excecution\n"); #ifdef BENCHMARKEXIT @@ -53,6 +54,7 @@ static void dagstandalone_handlesignal(int sig) { } #endif #endif +#endif static void InitFail(int exitcode, char const *which) { ControlPane_Error(exitcode,"%s interface failed to initialise. Exiting\n", @@ -68,15 +70,12 @@ static void InitFail(int exitcode, char const *which) { * */ void dagstandalone(ArcemConfig *pConfig) { + ARMul_State *emu_state = NULL; #ifndef _WIN32 #ifndef AMIGA +#ifndef SYSTEM_nds struct sigaction action; -#endif -#endif - ARMul_State *emu_state = NULL; -#ifndef _WIN32 -#ifndef AMIGA /* Setup a signal handler for SIGUSR1 */ action.sa_handler = dagstandalone_handlesignal; sigemptyset (&action.sa_mask); @@ -84,6 +83,7 @@ static void InitFail(int exitcode, char const *which) { sigaction(SIGUSR1, &action, (struct sigaction *) 0); #endif +#endif #endif ARMul_EmulateInit(); diff --git a/hostfs.c b/hostfs.c index 5df0f85..3c677fe 100644 --- a/hostfs.c +++ b/hostfs.c @@ -221,6 +221,9 @@ dbug_hostfs(const char *format, ...) } #endif +#if defined(SYSTEM_nds) && defined(NDEBUG) +static inline void warn_hostfs(const char *format, ...) {} +#else static void warn_hostfs(const char *format, ...) { @@ -230,6 +233,7 @@ warn_hostfs(const char *format, ...) vfprintf(stderr, format, ap); va_end(ap); } +#endif static void path_construct(ARMul_State *state, const char *old_path, const char *ro_path, diff --git a/nds/ControlPane.c b/nds/ControlPane.c new file mode 100644 index 0000000..1c64def --- /dev/null +++ b/nds/ControlPane.c @@ -0,0 +1,700 @@ +/* Redraw and other services for the control pane */ +/* (c) David Alan Gilbert 1995 - see Readme file for copying info */ + + +#include "armdefs.h" +#include "archio.h" +#include "armarc.h" +#include "ControlPane.h" +#include "../arch/keyboard.h" + +#include "KeyTable.h" + +#include "bg_gfx.h" +#include "bg_map.h" +#include "bg_pal.h" +#include "font_gfx.h" +#include "font_pal.h" +#include "keys_gfx.h" + +#include +#include + +#include + +enum { + DRAG_NONE, + DRAG_MOUSE, + DRAG_WINDOW_1, + DRAG_WINDOW_2, + DRAG_WINDOW_3 +}; + +enum { + SIDEL = 1, + SIDER = 1 | TILE_FLIP_H, + SIDET = 2, + SIDEB = 2 | TILE_FLIP_V, + BARV = 3, + BARH = 4, + CORNERTL = 5, + CORNERTR = 5 | TILE_FLIP_H, + CORNERBL = 5 | TILE_FLIP_V, + CORNERBR = 5 | TILE_FLIP_H | TILE_FLIP_V, + PIPET = 7, + PIPEB = 7 | TILE_FLIP_V, + PIPEL = 8, + PIPER = 8 | TILE_FLIP_H, + PIPEIT = 9, + PIPEIB = 9 | TILE_FLIP_V, + PIPEIL = 10, + PIPEIR = 10 | TILE_FLIP_H, + PIPEC = 11, + + CLOSE = 0x84, + DOWN = 0x8a, + UP = 0x8b, + ELLIPSIS = 0x8c +}; + +typedef struct { + int px, py, pw, ph; + int bx, by, bw, bh; + int closex; + int layer; + u16 *map; +} Window; + +static int old_px = -1; +static int old_py = -1; +static int drag_mode = DRAG_NONE; + +static bool keys_caps = false; +static arch_key_id key_pressed = -1; + +static bool has_console = false; +static bool has_keyboard = false; +static bool has_windows = false; + +static Window windows[3]; + +static void ControlPane_InitKeyboard(ARMul_State *state); +static void ControlPane_InitWindows(void); + +static Window *ControlPane_MessageBox(int layer, const char *title, const char *message); + +static void draw_floppy_leds(unsigned int leds) +{ + unsigned int floppy; + + for (floppy = 0; floppy < 4; floppy++) { + BG_PALETTE_SUB[15 - floppy] = (leds & (1 << floppy)) ? RGB8(0xff, 0xbb, 0x00) : RGB8(0x99, 0x99, 0x99); + } +} + +static void draw_floppy(unsigned int floppy, bool inserted) { + if (inserted) { + BG_PALETTE_SUB[10 - (floppy * 2)] = RGB8(0xff, 0xbb, 0x00); + BG_PALETTE_SUB[11 - (floppy * 2)] = RGB8(0xdd, 0x00, 0x00); + } else { + BG_PALETTE_SUB[10 - (floppy * 2)] = 0; + BG_PALETTE_SUB[11 - (floppy * 2)] = 0; + } +} + +/*----------------------------------------------------------------------------*/ + +void ControlPane_Init(ARMul_State *state) +{ + static bool init = false; + if (init) + return; + init = true; + + videoSetModeSub(MODE_0_2D | DISPLAY_BG3_ACTIVE); + vramSetBankH(VRAM_H_SUB_BG); + vramSetBankI(VRAM_I_SUB_SPRITE); + oamInit(&oamSub, SpriteMapping_1D_128, false); + +#ifndef NDEBUG + scanKeys(); + + int held = keysHeld(); + if (held & KEY_SELECT) { + consoleInit(NULL, 3, BgType_Text4bpp, BgSize_T_256x256, 15, 0, false, true); + has_console = true; + } +#endif + + if (!has_console) + { + BGCTRL_SUB[3] = BG_TILE_BASE(0) | BG_MAP_BASE(5) | BG_COLOR_16 | BG_32x32; + + dmaCopy(bg_gfx, (void *)(CHAR_BASE_BLOCK_SUB(0) + (0x100 * 32)), bg_gfx_size); + dmaCopy(bg_map, (void *)SCREEN_BASE_BLOCK_SUB(5), bg_map_size); + dmaCopy(bg_pal, BG_PALETTE_SUB, bg_pal_size); + + ControlPane_InitWindows(); + ControlPane_InitKeyboard(state); + + if (state) + FDC_SetLEDsChangeFunc(draw_floppy_leds); + ControlPane_Redraw(); + } +} + +void ControlPane_Error(int code,const char *fmt,...) +{ + char msg[256]; + Window *w; + va_list args; + va_start(args,fmt); + + ControlPane_Init(NULL); + +#ifndef NDEBUG + if (has_console) { + /* Log it */ + vprintf(fmt,args); + va_end(args); + +#ifdef __CALICO__ + while(pmMainLoop()) +#else + while(1) +#endif + { + swiWaitForVBlank(); + scanKeys(); + if (keysDown() & KEY_START) + break; + } + + /* Quit */ + exit(code); + } +#endif + + vsnprintf(msg, 256, fmt, args); + va_end(args); + + w = ControlPane_MessageBox(0, "Error", msg); + +#ifdef __CALICO__ + while(pmMainLoop()) +#else + while(1) +#endif + { + if (w->layer < 0) + break; + swiWaitForVBlank(); + Kbd_PollHostKbd(NULL); + } + + /* Quit */ + exit(code); +} + +/*----------------------------------------------------------------------------*/ + +static void ControlPane_TextDrawChar(Window *window, u16 c, int x, int y) +{ + x += window->bx; + y += window->by; + window->map[x + y * 32] = (1 << 12) | c; +} + +static void ControlPane_TextDrawString(Window *window, const char *c, int x, int y, int w, bool centred) +{ + u16 *ptr, *last; + size_t len = strlen(c); + + x += window->bx; + y += window->by; + + ptr = window->map + (y * 32) + x; + last = ptr + w - 1; + + if (len < w && centred) + ptr += (w - len) / 2; + + while (*c) { + if (ptr == last && c[1] != 0) { + *ptr++ = (1 << 12) | ELLIPSIS; + break; + } + + *ptr++ = (1 << 12) | *c++; + } +} + +static void ControlPane_TextDrawBlock(Window *window, const u16 *c, int x, int y, int w, int h) +{ + int i, j; + x += window->bx; + y += window->by; + + for (i = y; i < y + h; i++) { + for (j = x; j < x + w; j++) { + window->map[(i * 32) + j] = (1 << 12) | *c++; + } + } +} + +static void ControlPane_TextDrawLineH(Window *window, u16 c, int x, int y, int w) +{ + int i; + x += window->bx; + y += window->by; + + for (i = x; i < x + w; i++) { + window->map[(y * 32) + i] = (1 << 12) | c; + } +} + +static void ControlPane_TextDrawLineV(Window *window, u16 c, int x, int y, int h) +{ + int i; + x += window->bx; + y += window->by; + + for (i = y; i < y + h; i++) { + window->map[(i * 32) + x] = (1 << 12) | c; + } +} + +static Window *ControlPane_CreateWindow(int layer, int w, int h, const char *title, bool scroll) +{ + static const u16 TL[1*3] = { + CORNERTL, + SIDEL, + PIPEL + }; + static const u16 BL[1*1] = { + CORNERBL + }; + static const u16 TR[3*3] = { + PIPET, SIDET, CORNERTR, + BARV, CLOSE, SIDER, + PIPEIB, BARH, PIPER + }; + static const u16 BR[1*1] = { + CORNERBR + }; + static const u16 TR_S[3*5] = { + PIPET, SIDET, CORNERTR, + BARV, CLOSE, SIDER, + PIPEC, BARH, PIPER, + + BARV, UP, SIDER, + PIPEIL, BARH, PIPER + }; + static const u16 BR_S[3*3] = { + PIPEIL, BARH, PIPER, + BARV, DOWN, SIDER, + PIPEB, SIDEB, CORNERBR + }; + vu16 *scrollXY = REG_BGOFFSETS_SUB + (layer * 2); + Window *window; + int i, mapbase; + + mapbase = 6 + (layer * 4); + BGCTRL_SUB[layer] = BG_TILE_BASE(0) | BG_MAP_BASE(mapbase) | BG_COLOR_16 | BG_64x64; + window = &windows[layer]; + window->map = (u16 *)SCREEN_BASE_BLOCK_SUB(mapbase); + window->layer = layer; + window->pw = (w + (scroll ? 4 : 2)) * 8; + window->ph = (h + 4) * 8; + window->px = (256 - window->pw) / 2; + window->py = (192 - window->ph) / 2; + window->bx = 1; + window->by = 3; + window->bw = w; + window->bh = h; + + dmaFillHalfWords(1<<12, window->map, 64*64*2); + + /* Left hand side */ + ControlPane_TextDrawBlock(window, TL, -1, -3, 1, 3); + ControlPane_TextDrawLineV(window, SIDEL, -1, 0, h); + ControlPane_TextDrawBlock(window, BL, -1, h, 1, 1); + + /* Middle */ + ControlPane_TextDrawLineH(window, SIDET, 0, -3, w); + ControlPane_TextDrawLineH(window, ' ', 0, -2, w); + ControlPane_TextDrawLineH(window, BARH, 0, -1, w); + for (i = 0; i < h; i++) + ControlPane_TextDrawLineH(window, ' ', 0, i, w); + ControlPane_TextDrawLineH(window, SIDEB, 0, h, w); + + + if (scroll) { + /* Title */ + ControlPane_TextDrawString(window, title, 0, -2, w, true); + + /* Right hand side */ + ControlPane_TextDrawBlock(window, TR_S, w + 0, -3, 3, 5); + ControlPane_TextDrawLineV(window, BARV, w + 0, 2, h - 3); + ControlPane_TextDrawLineV(window, ' ', w + 1, 2, h - 3); + ControlPane_TextDrawLineV(window, SIDER, w + 2, 2, h - 3); + ControlPane_TextDrawBlock(window, BR_S, w + 0, h - 2, 3, 3); + + window->closex = 1 + w; + } else { + /* Title */ + ControlPane_TextDrawString(window, title, 0, -2, w - 2, true); + + /* Right hand side */ + ControlPane_TextDrawBlock(window, TR, w - 2, -3, 3, 3); + ControlPane_TextDrawLineV(window, SIDER, w, 0, h); + ControlPane_TextDrawBlock(window, BR, w, h, 1, 1); + + window->closex = 1 + w - 2; + } + + scrollXY[0] = 512 - window->px; + scrollXY[1] = 512 - window->py; + + videoBgEnableSub(window->layer); + + return window; +} + +static void ControlPane_CloseWindow(Window *window) +{ + videoBgDisableSub(window->layer); + memset(window, 0, sizeof(Window)); + window->layer = -1; +} + +static void ControlPane_InitWindows(void) +{ + int i; + + decompress(font_gfx, (void *)CHAR_BASE_BLOCK_SUB(0), LZ77Vram); + dmaCopy(font_pal, BG_PALETTE_SUB + 16, font_pal_size); + + for (i = 0; i < sizeof(windows)/sizeof(*windows); i++) { + ControlPane_CloseWindow(&windows[i]); + } + + has_windows = true; +} + +static bool ControlPane_ClickWindow(ARMul_State *state, int px, int py) +{ + Window *w; + int i; + + for (i = 0; i < sizeof(windows)/sizeof(*windows); i++) { + w = &windows[i]; + /* Is the window open? */ + if (w->layer < 0) + continue; + + /* Are the co-ordinates within the window? */ + if (!(px >= w->px && px < w->px + w->pw && py >= w->py && py < w->py + w->ph)) + continue; + + /* Handle clicking on the title bar */ + if (py < w->py + (8 * w->by)) { + if (px > w->px + (8 * w->closex)) { + ControlPane_CloseWindow(w); + } else { + drag_mode = DRAG_WINDOW_1 + i; + old_px = px; + old_py = py; + } + } + + return true; + } + return false; +} + +static bool ControlPane_DragWindow(ARMul_State *state, int px, int py) +{ + int newMouseX, newMouseY, xdiff, ydiff; + Window *w = &windows[drag_mode - DRAG_WINDOW_1]; + vu16 *scrollXY = REG_BGOFFSETS_SUB + (w->layer * 2); + + xdiff = (px - old_px); + ydiff = (py - old_py); + + w->px += xdiff; + w->py += ydiff; + scrollXY[0] = 512 - w->px; + scrollXY[1] = 512 - w->py; + + old_px = px; + old_py = py; + return true; +} + +/*----------------------------------------------------------------------------*/ + +static Window *ControlPane_MessageBox(int layer, const char *title, const char *message) +{ + char line[17], *msg, *token, *saveptr; + size_t linepos = 0, toklen; + Window *w; + int y = 0; + + w = ControlPane_CreateWindow(layer, 16, 6, title, false); + + msg = strdup(message); + memset(line, 0, sizeof(line)); + + token = strtok_r(msg, " ", &saveptr); + while (token != NULL) { + toklen = strlen(token); + if (linepos + toklen + 1 > 16) { + ControlPane_TextDrawString(w, line, 0, y, 16, true); + memset(line, 0, sizeof(line)); + linepos = 0; + y++; + } + + if (linepos > 0) { + strcat(line, " "); + linepos += toklen; + } + + strcat(line, token); + linepos += toklen + 1; + token = strtok_r(NULL, " ", &saveptr); + } + + if (linepos > 0) + ControlPane_TextDrawString(w, line, 0, y, 16, true); + free(msg); + + return w; +} + +/*----------------------------------------------------------------------------*/ + +static void ControlPane_UpdateKeyboardLEDs(uint8_t leds); + +static void ControlPane_InitKeyboard(ARMul_State *state) +{ + static const u16 keys_pal[] = { + /* 0: Not pressed, shift off, leds off */ + 0x5FBD,0x6F7B,0x5EF7,0x7FFF,0x39CE,0x4E73,0x5EF7,0x0000, + 0x0000,0x0000,0x6F7B,0x39CE,0x5FBD,0x022A,0x02FF,0x7EE0, + /* 1: Not pressed, shift off, leds on */ + 0x5FBD,0x6F7B,0x5EF7,0x7FFF,0x39CE,0x4E73,0x5EF7,0x0000, + 0x0000,0x0000,0x6F7B,0x0320,0x5FBD,0x022A,0x02FF,0x7EE0, + /* 2: Not pressed, shift on, leds off */ + 0x5FBD,0x6F7B,0x5EF7,0x7FFF,0x39CE,0x4E73,0x5EF7,0x0000, + 0x6F7B,0x0000,0x0000,0x39CE,0x5FBD,0x022A,0x02FF,0x7EE0, + /* 3: Not pressed, shift on, leds on */ + 0x5FBD,0x6F7B,0x5EF7,0x7FFF,0x39CE,0x4E73,0x5EF7,0x0000, + 0x6F7B,0x0000,0x0000,0x0320,0x5FBD,0x022A,0x02FF,0x7EE0, + /* 4: Pressed, shift off, leds off */ + 0x5FBD,0x6F7B,0x7FFF,0x5EF7,0x5EF7,0x4E73,0x39CE,0x0000, + 0x0000,0x0000,0x6F7B,0x39CE,0x5FBD,0x022A,0x02FF,0x7EE0, + /* 5: Pressed, shift off, leds on */ + 0x5FBD,0x6F7B,0x7FFF,0x5EF7,0x5EF7,0x4E73,0x39CE,0x0000, + 0x0000,0x0000,0x6F7B,0x0320,0x5FBD,0x022A,0x02FF,0x7EE0, + /* 6: Pressed, shift on, leds off */ + 0x5FBD,0x6F7B,0x7FFF,0x5EF7,0x5EF7,0x4E73,0x39CE,0x0000, + 0x6F7B,0x0000,0x0000,0x39CE,0x5FBD,0x022A,0x02FF,0x7EE0, + /* 7: Pressed, shift on, leds on */ + 0x5FBD,0x6F7B,0x7FFF,0x5EF7,0x5EF7,0x4E73,0x39CE,0x0000, + 0x6F7B,0x0000,0x0000,0x0320,0x5FBD,0x022A,0x02FF,0x7EE0 + }; + int i; + + /* We manage sprite VRAM manually here instead of using oamAllocateGfx() */ + decompress(keys_gfx, SPRITE_GFX_SUB, LZ77Vram); + dmaCopy(keys_pal, SPRITE_PALETTE_SUB, sizeof(keys_pal)); + + if (state) + KBD.leds_changed = ControlPane_UpdateKeyboardLEDs; + + has_keyboard = true; +} + +static void ControlPane_UpdateKeyboardLEDs(uint8_t leds) +{ + keys_caps = (leds & KBD_LED_CAPSLOCK); +} + +static bool ControlPane_ClickKeyboard(ARMul_State *state, int px, int py) +{ + const dvk_to_vkeybd *ktvk; + + if (!state) + return false; + + for (ktvk = dvk_to_vkeybd_map; ktvk->sprite; ktvk++) { + uint width = ((ktvk->sprite) >> 8) * 16; + uint height = 16; + + if (px >= ktvk->x && px < ktvk->x + width && + py >= ktvk->y && py < ktvk->y + height) { + keyboard_key_changed(&KBD, ktvk->kid, 0); + key_pressed = ktvk->kid; + return true; + } + } + return false; +} + +static void ControlPane_ReleaseKeyboard(ARMul_State *state) +{ + if (key_pressed != -1) { + if (state) + keyboard_key_changed(&KBD, key_pressed, 1); + key_pressed = -1; + } +} + +static void ControlPane_DrawKeyboard(void) +{ + const dvk_to_vkeybd *ktvk; + uint i = 0; + + for (ktvk = dvk_to_vkeybd_map; ktvk->sprite; ktvk++) { + uint sprite = (ktvk->sprite) & 0xFF; + uint width = (ktvk->sprite) >> 8; + uint x = ktvk->x, y = ktvk->y; + uint palette = 0; + + if (keys_caps) palette |= 1; + if (ktvk->kid == key_pressed) palette |= 4; + + do { + oamSet(&oamSub, i++, x, y, 3, palette, SpriteSize_16x16, SpriteColorFormat_16Color, + &SPRITE_GFX_SUB[sprite++ * 64], -1, false, false, false, false, false); + x += 16; + } while (--width); + } +} + +/*----------------------------------------------------------------------------*/ + +static bool ControlPane_ClickFloppy(ARMul_State *state, int drive) +{ + const char *err; + char tmp[256]; + + if (!state) + return false; + + if (FDC_IsFloppyInserted(drive)) { + err = FDC_EjectFloppy(drive); + if (err == NULL) + draw_floppy(drive, false); + else + ControlPane_MessageBox(1, "Error", err); + } else { + sprintf(tmp, "FloppyImage%d", drive); + err = FDC_InsertFloppy(drive, tmp); + if (err == NULL) + draw_floppy(drive, true); + else + ControlPane_MessageBox(1, "Error", err); + } + return true; +} + +/*----------------------------------------------------------------------------*/ + +static bool ControlPane_ClickTouchpad(ARMul_State *state, int px, int py) +{ + if (!state) + return false; + drag_mode = DRAG_MOUSE; + old_px = px; + old_py = py; + return true; +} + +static bool ControlPane_DragTouchpad(ARMul_State *state, int px, int py) +{ + int newMouseX, newMouseY, xdiff, ydiff; + + xdiff = (px - old_px) << 2; + ydiff = (py - old_py) << 2; + + if (xdiff > 63) + xdiff = 63; + if (xdiff < -63) + xdiff = -63; + + if (ydiff > 63) + ydiff = 63; + if (ydiff < -63) + ydiff = -63; + + old_px += xdiff >> 2; + old_py += ydiff >> 2; + + if (state) { + KBD.MouseXCount = xdiff & 127; + KBD.MouseYCount = -ydiff & 127; + } + return true; +} + +/*----------------------------------------------------------------------------*/ + +bool ControlPane_ProcessTouchPressed(ARMul_State *state, int px, int py) +{ + if (has_windows && ControlPane_ClickWindow(state, px, py)) + return true; + if (has_keyboard && ControlPane_ClickKeyboard(state, px, py)) + return true; + + if (has_console) { + return ControlPane_ClickTouchpad(state, px, py); + } else if (py > 183) { + int drive = 3 - (px / 64); + return ControlPane_ClickFloppy(state, drive); + } else if (py < 78) { + return ControlPane_ClickTouchpad(state, px, py); + } + + return false; +} + +bool ControlPane_ProcessTouchHeld(ARMul_State *state, int px, int py) +{ + switch (drag_mode) { + case DRAG_MOUSE: + return ControlPane_DragTouchpad(state, px, py); + case DRAG_WINDOW_1: + case DRAG_WINDOW_2: + case DRAG_WINDOW_3: + return ControlPane_DragWindow(state, px, py); + } + + return false; +} + +bool ControlPane_ProcessTouchReleased(ARMul_State *state) +{ + bool retval = false; + + ControlPane_ReleaseKeyboard(state); + + if (drag_mode != DRAG_NONE) { + drag_mode = DRAG_NONE; + old_px = -1; + old_py = -1; + retval = true; + } + + return false; +} + +/*----------------------------------------------------------------------------*/ + +void ControlPane_Redraw(void) +{ + ControlPane_DrawKeyboard(); + oamUpdate(&oamSub); +} diff --git a/nds/ControlPane.h b/nds/ControlPane.h new file mode 100644 index 0000000..25f69d1 --- /dev/null +++ b/nds/ControlPane.h @@ -0,0 +1,16 @@ +/* (c) David Alan Gilbert 1995 - see Readme file for copying info */ +#ifndef CONTROLPANE_HEADER +#define CONTROLPANE_HEADER + +void ControlPane_Init(ARMul_State *state); + +/* Report an error and exit */ +void ControlPane_Error(int code,const char *fmt,...); + +bool ControlPane_ProcessTouchPressed(ARMul_State *state, int px, int py); +bool ControlPane_ProcessTouchHeld(ARMul_State *state, int px, int py); +bool ControlPane_ProcessTouchReleased(ARMul_State *state); + +void ControlPane_Redraw(void); + +#endif diff --git a/nds/DispKbd.c b/nds/DispKbd.c new file mode 100644 index 0000000..70dd5b1 --- /dev/null +++ b/nds/DispKbd.c @@ -0,0 +1,351 @@ +/* (c) David Alan Gilbert 1995-1999 - see Readme file for copying info + Nintendo DS version by Cameron Cawley, 2020-2023 */ +/* Display and keyboard interface for the Arc emulator */ + +#include + +#include "../armdefs.h" +#include "armarc.h" +#include "../arch/dbugsys.h" +#include "../arch/keyboard.h" +#include "displaydev.h" +#include "../armemu.h" +#include "ControlPane.h" + +#include + +ARMul_State nds_statestr DTCM_BSS; +void *state_alloc(int s) { return &nds_statestr; } +void state_free(void *p) {} + +static inline s32 calculate_scale(s32 width, s32 height) { + const s32 screenAspect = intToFixed(SCREEN_WIDTH, 8) / SCREEN_HEIGHT; + s32 rectAspect = intToFixed(width, 8) / height; + + s32 scaleFactor; + if (screenAspect > rectAspect) + scaleFactor = intToFixed(SCREEN_HEIGHT, 8) / height; + else + scaleFactor = intToFixed(SCREEN_WIDTH, 8) / width; + + return scaleFactor; +} + +static inline u16 ConvertPhysToColour(int phys) +{ + /* Convert to 5-bit component values */ + int r = (phys & 0x00f) << 1; + int g = (phys & 0x0f0) >> 3; + int b = (phys & 0xf00) >> 7; + /* May want to tweak this a bit at some point? */ + r |= r>>4; + g |= g>>4; + b |= b>>4; + return RGB15(r, g, b); +} + +static void RefreshMouse(ARMul_State *state); + +/*-----------------------------------------------------------------------------*/ +/* Palettised display code */ +#define PDD_Name(x) pdd_##x +#define PDD_MonitorWidth 1024 +#define PDD_MonitorHeight 512 +#define PDD_BgSize BgSize_B8_1024x512 + +static int background; + +static s32 xscale = intToFixed(1, 8), yscale = intToFixed(1, 8); +static s32 xoffset = 0, yoffset = 0; + +/** + * This uses an intermediate buffer since cache hits in main RAM are faster than VRAM writes. + * It would have been nice to put this into TCM, but that would prevent it from being used for DMA. + */ +static ARMword RowBuffer[PDD_MonitorWidth/4] ALIGN(8); + +typedef struct { + ARMword *src; + uint8_t *dst; + unsigned int count; +} PDD_Row; + + +static void PDD_Name(Host_ChangeMode)(ARMul_State *state,int width,int height,int depth,int hz); + +static inline void PDD_Name(Host_SetPaletteEntry)(ARMul_State *state,int i,uint_fast16_t phys) +{ + BG_PALETTE[i] = ConvertPhysToColour(phys); +} + +static inline void PDD_Name(Host_SetCursorPaletteEntry)(ARMul_State *state,int i,uint_fast16_t phys) +{ + // TODO: Implement this! +} + +static inline void PDD_Name(Host_SetBorderColour)(ARMul_State *state,uint_fast16_t phys) +{ + // TODO: Implement this! +} + +static inline PDD_Row PDD_Name(Host_BeginRow)(ARMul_State *state,int row,int offset,int *alignment) +{ + PDD_Row drow; + drow.dst = ((uint8_t *)bgGetGfxPtr(background)) + (row * PDD_MonitorWidth) + offset; + drow.src = RowBuffer; + drow.count = 0; + *alignment = 0; + return drow; +} + +static inline void PDD_Name(Host_EndRow)(ARMul_State *state,PDD_Row *row) +{ + while (dmaBusy(3)); +} + +static inline ARMword *PDD_Name(Host_BeginUpdate)(ARMul_State *state,PDD_Row *row,unsigned int count,int *outoffset) +{ + row->count = count; + *outoffset = 0; + return row->src; +} + +static inline void PDD_Name(Host_EndUpdate)(ARMul_State *state,PDD_Row *row) +{ + unsigned int count = row->count; + + DC_FlushRange(row->src, count>>3); + while (dmaBusy(3)); + dmaCopyWordsAsynch(3, row->src, row->dst, count>>3); + + row->src += count>>5; +} + +static inline void PDD_Name(Host_TransferUpdate)(ARMul_State *state,PDD_Row *row,unsigned int count,const ARMword *src) +{ + DC_FlushRange(src, count>>3); + while (dmaBusy(3)); + dmaCopyWordsAsynch(3, src, row->dst, count>>3); +} + +static inline void PDD_Name(Host_AdvanceRow)(ARMul_State *state,PDD_Row *row,unsigned int count) +{ + row->dst += count>>3; +} + +static inline void PDD_Name(Host_PollDisplay)(ARMul_State *state) +{ + RefreshMouse(state); + oamUpdate(&oamMain); + bgUpdate(); + ControlPane_Redraw(); +} + +static inline void PDD_Name(Host_DrawBorderRect)(ARMul_State *state,int x,int y,int width,int height) +{ + // TODO: Implement this! +} + +#include "../arch/paldisplaydev.c" + +static void PDD_Name(Host_ChangeMode)(ARMul_State *state,int width,int height,int depth,int hz) +{ + s32 xs, ys; + + if((width > PDD_MonitorWidth) || (height > PDD_MonitorHeight)) + { + warn("Mode %dx%d too big\n",width,height); + DC.ModeChanged = 1; + return; + } + + HD.Width = width; + HD.Height = height; + HD.XScale = 1; + HD.YScale = 1; + + /* Calculate expansion params */ + if(depth == 3) + { + /* No expansion */; + HD.ExpandTable = NULL; + } + else + { + /* Expansion! */ + static ARMword expandtable[256] DTCM_BSS; + HD.ExpandFactor = (3-depth); + HD.ExpandTable = expandtable; + GenExpandTable(HD.ExpandTable,1<= height*2) { + xscale = yscale = calculate_scale(width, height * 2); + yscale *= 2; + } else if (height >= width) { + xscale = yscale = calculate_scale(width * 2, height); + xscale *= 2; + } else { + xscale = yscale = calculate_scale(width, height); + } + + xs = (1<<16) / xscale; + ys = (1<<16) / yscale; + + xoffset = (SCREEN_WIDTH - fixedToInt(width * xscale, 8)) >> 1; + yoffset = (SCREEN_HEIGHT - fixedToInt(height * yscale, 8)) >> 1; + + bgSet(background, 0, xs, ys, -xoffset << 8, -yoffset << 8, 0, 0); + oamRotateScale(&oamMain, 0, 0, xs, ys); + + /* Screen is expected to be cleared */ +#ifdef __CALICO__ + memset(bgGetGfxPtr(background), 0, PDD_MonitorWidth * PDD_MonitorHeight); +#else + dmaFillHalfWords(0, bgGetGfxPtr(background), PDD_MonitorWidth * PDD_MonitorHeight); +#endif +} + + +/*-----------------------------------------------------------------------------*/ + +typedef struct { u16 data[8][2]; } Tile; +typedef struct { Tile data[8][4]; } Sprite; +Sprite *cursorData; +int oldHeight = 64; + +static inline u16 Convert2bppTo4bpp(u8 byte) { + return (byte & 0x03) | ((byte & 0x0C) << 2) | ((byte & 0x30) << 4) | ((byte & 0xC0) << 6); +} + +/*-----------------------------------------------------------------------------*/ +/* Refresh the mouse's image */ +static void RefreshMouse(ARMul_State *state) { + int x = 0, y = 0, i = 0; + int HorizPos = 0, VertPos = 0; + int Height = ((int)VIDC.Vert_CursorEnd - (int)VIDC.Vert_CursorStart)*HD.YScale; + if (Height < 0) Height = 0; + + DisplayDev_GetCursorPos(state,&HorizPos,&VertPos); + HorizPos = fixedToInt(HorizPos * xscale, 8); + VertPos = fixedToInt(VertPos * yscale, 8); + + if (Height > 64) Height = 64; + if (VertPos < 0) VertPos = 0; + + /* TODO: This might not be as fast as it could be */ + u8 *src = ((u8 *)MEMC.PhysRam) + (MEMC.Cinit * 16); + for (y = 0; y < Height; y++) { + int ytile = y / 8; + int ydata = y % 8; + for (x = 0; x < 32; x += 8) { + int xtile = x / 8; + cursorData->data[ytile][xtile].data[ydata][0] = Convert2bppTo4bpp(*src++); + cursorData->data[ytile][xtile].data[ydata][1] = Convert2bppTo4bpp(*src++); + } + } + + if (oldHeight > Height) { + for (y = Height; y < oldHeight; y++) { + int ytile = y / 8; + int ydata = y % 8; + for (x = 0; x < 32; x += 8) { + int xtile = x / 8; + cursorData->data[ytile][xtile].data[ydata][0] = 0; + cursorData->data[ytile][xtile].data[ydata][1] = 0; + } + } + } + oldHeight = Height; + + /* Cursor palette */ + SPRITE_PALETTE[0] = 0; + for(i = 0; i < 3; i++) { + SPRITE_PALETTE[i + 1] = ConvertPhysToColour(VIDC.CursorPalette[i]); + } + + oamSet(&oamMain, 0, HorizPos, VertPos, 0, 0, SpriteSize_32x64, SpriteColorFormat_16Color, + cursorData, -1, false, false, false, false, false); +}; /* RefreshMouse */ + +/*-----------------------------------------------------------------------------*/ +int +DisplayDev_Init(ARMul_State *state) +{ + /* Setup display and cursor bitmaps */ + videoSetMode(MODE_6_2D | DISPLAY_BG2_ACTIVE); + + vramSetPrimaryBanks(VRAM_A_MAIN_BG,VRAM_B_MAIN_BG,VRAM_C_MAIN_BG,VRAM_D_MAIN_BG); + background = bgInit(2, BgType_Bmp8, PDD_BgSize, 0, 0); + + vramSetBankE(VRAM_E_MAIN_SPRITE); + oamInit(&oamMain, SpriteMapping_1D_32, false); + cursorData = (Sprite *)oamAllocateGfx(&oamMain, SpriteSize_32x64, SpriteColorFormat_16Color); + + ControlPane_Init(state); + + return DisplayDev_Set(state,&PDD_DisplayDev); +} + +/*-----------------------------------------------------------------------------*/ + +typedef struct { + int sym; + arch_key_id kid; +} button_to_arch_key; + +/* TODO: Provide a GUI for remapping the buttons */ +static const button_to_arch_key button_to_arch_key_map[] = { + { KEY_Y, ARCH_KEY_left }, + { KEY_B, ARCH_KEY_down }, + { KEY_A, ARCH_KEY_right }, + { KEY_X, ARCH_KEY_up }, + { KEY_L, ARCH_KEY_shift_r }, + { KEY_R, ARCH_KEY_control_r }, + { KEY_LEFT, ARCH_KEY_button_1 }, + { KEY_DOWN, ARCH_KEY_button_2 }, + { KEY_RIGHT, ARCH_KEY_button_3 }, + { KEY_UP, ARCH_KEY_space }, + { 0, 0 } +}; + +static void ProcessButtons(ARMul_State *state, int pressed, int released) { + const button_to_arch_key *btak; + for (btak = button_to_arch_key_map; btak->sym; btak++) { + if ((pressed & btak->sym) && btak->kid != 0) + keyboard_key_changed(&KBD, btak->kid, 0); + if ((released & btak->sym) && btak->kid != 0) + keyboard_key_changed(&KBD, btak->kid, 1); + } +}; /* ProcessButtons */ + +/*-----------------------------------------------------------------------------*/ +int +Kbd_PollHostKbd(ARMul_State *state) +{ +#ifdef __CALICO__ + if (!pmMainLoop()) + exit(0); +#endif + + touchPosition touch; + scanKeys(); + + int pressed = keysDown(); + int released = keysUp(); + int held = keysHeld(); + ProcessButtons(state, pressed, released); + + if (pressed & KEY_TOUCH) { + touchRead(&touch); + ControlPane_ProcessTouchPressed(state, touch.px, touch.py); + } else if (held & KEY_TOUCH) { + touchRead(&touch); + ControlPane_ProcessTouchHeld(state, touch.px, touch.py); + } else if (released & KEY_TOUCH) { + ControlPane_ProcessTouchReleased(state); + } + + return 0; +} diff --git a/nds/KeyTable.h b/nds/KeyTable.h new file mode 100644 index 0000000..bc64c19 --- /dev/null +++ b/nds/KeyTable.h @@ -0,0 +1,176 @@ +/* Virtual Key codes */ + +typedef struct { + arch_key_id kid; + unsigned short sprite; + short x; + short y; +} dvk_to_vkeybd; + +enum { + spr_f1 = 0 | (1 << 8), + spr_f2 = 1 | (1 << 8), + spr_f3 = 2 | (1 << 8), + spr_f4 = 3 | (1 << 8), + spr_f5 = 4 | (1 << 8), + spr_f6 = 5 | (1 << 8), + spr_f7 = 6 | (1 << 8), + spr_f8 = 7 | (1 << 8), + spr_f9 = 8 | (1 << 8), + spr_f10 = 9 | (1 << 8), + spr_f11 = 10 | (1 << 8), + spr_f12 = 11 | (1 << 8), + spr_up = 12 | (1 << 8), + spr_down = 13 | (1 << 8), + spr_left = 14 | (1 << 8), + spr_right = 15 | (1 << 8), + + spr_shift_l = 16 | (3 << 8), + spr_shift_r = spr_shift_l, + spr_caps_lock = 19 | (2 << 8), + spr_tab = 21 | (2 << 8), + spr_escape = 23 | (1 << 8), + spr_alt_l = 24 | (2 << 8), + spr_alt_r = spr_alt_l, + spr_return = 26 | (2 << 8), + spr_control_r = 28 | (2 << 8), + spr_control_l = 30 | (2 << 8), + + spr_backspace = 32 | (1 << 8), + spr_space = 33 | (8 << 8), + spr_backslash = 41 | (2 << 8), + spr_grave = 43 | (1 << 8), + spr_1 = 44 | (1 << 8), + spr_2 = 45 | (1 << 8), + spr_3 = 46 | (1 << 8), + spr_4 = 47 | (1 << 8), + + spr_5 = 48 | (1 << 8), + spr_6 = 49 | (1 << 8), + spr_7 = 50 | (1 << 8), + spr_8 = 51 | (1 << 8), + spr_9 = 52 | (1 << 8), + spr_0 = 53 | (1 << 8), + spr_minus = 54 | (1 << 8), + spr_equal = 55 | (1 << 8), + spr_sterling = 56 | (1 << 8), + spr_q = 57 | (1 << 8), + spr_w = 58 | (1 << 8), + spr_e = 59 | (1 << 8), + spr_r = 60 | (1 << 8), + spr_t = 61 | (1 << 8), + spr_y = 62 | (1 << 8), + spr_u = 63 | (1 << 8), + + spr_i = 64 | (1 << 8), + spr_o = 65 | (1 << 8), + spr_p = 66 | (1 << 8), + spr_bracket_l = 67 | (1 << 8), + spr_bracket_r = 68 | (1 << 8), + spr_a = 69 | (1 << 8), + spr_s = 70 | (1 << 8), + spr_d = 71 | (1 << 8), + spr_f = 72 | (1 << 8), + spr_g = 73 | (1 << 8), + spr_h = 74 | (1 << 8), + spr_j = 75 | (1 << 8), + spr_k = 76 | (1 << 8), + spr_l = 77 | (1 << 8), + spr_semicolon = 78 | (1 << 8), + spr_apostrophe = 79 | (1 << 8), + + + spr_z = 80 | (1 << 8), + spr_x = 81 | (1 << 8), + spr_c = 82 | (1 << 8), + spr_v = 83 | (1 << 8), + spr_b = 84 | (1 << 8), + spr_n = 85 | (1 << 8), + spr_m = 86 | (1 << 8), + spr_comma = 87 | (1 << 8), + spr_period = 88 | (1 << 8), + spr_slash = 89 | (1 << 8), +}; + +#define X(kid, x, y) { ARCH_KEY_ ## kid, spr_ ## kid, x, y } +static const dvk_to_vkeybd dvk_to_vkeybd_map[] = { + X(escape, 1, 165-(5*17)), + X(f1, 30+(0*17), 165-(5*17)), + X(f2, 30+(1*17), 165-(5*17)), + X(f3, 30+(2*17), 165-(5*17)), + X(f4, 30+(3*17), 165-(5*17)), + X(f5, 41+(4*17), 165-(5*17)), + X(f6, 41+(5*17), 165-(5*17)), + X(f7, 41+(6*17), 165-(5*17)), + X(f8, 41+(7*17), 165-(5*17)), + X(f9, 52+(8*17), 165-(5*17)), + X(f10, 52+(9*17), 165-(5*17)), + X(f11, 52+(10*17), 165-(5*17)), + X(f12, 52+(11*17), 165-(5*17)), + + X(grave, 1+(0*17), 167-(4*17)), + X(1, 1+(1*17), 167-(4*17)), + X(2, 1+(2*17), 167-(4*17)), + X(3, 1+(3*17), 167-(4*17)), + X(4, 1+(4*17), 167-(4*17)), + X(5, 1+(5*17), 167-(4*17)), + X(6, 1+(6*17), 167-(4*17)), + X(7, 1+(7*17), 167-(4*17)), + X(8, 1+(8*17), 167-(4*17)), + X(9, 1+(9*17), 167-(4*17)), + X(0, 1+(10*17), 167-(4*17)), + X(minus, 1+(11*17), 167-(4*17)), + X(equal, 1+(12*17), 167-(4*17)), + X(sterling, 1+(13*17), 167-(4*17)), + X(backspace, 1+(14*17), 167-(4*17)), + + X(tab, 1, 167-(3*17)), + X(q, 27+(0*17), 167-(3*17)), + X(w, 27+(1*17), 167-(3*17)), + X(e, 27+(2*17), 167-(3*17)), + X(r, 27+(3*17), 167-(3*17)), + X(t, 27+(4*17), 167-(3*17)), + X(y, 27+(5*17), 167-(3*17)), + X(u, 27+(6*17), 167-(3*17)), + X(i, 27+(7*17), 167-(3*17)), + X(o, 27+(8*17), 167-(3*17)), + X(p, 27+(9*17), 167-(3*17)), + X(bracket_l, 27+(10*17), 167-(3*17)), + X(bracket_r, 27+(11*17), 167-(3*17)), + X(backslash, 27+(12*17), 167-(3*17)), + + X(control_l, 1, 167-(2*17)), + X(a, 35+(0*17), 167-(2*17)), + X(s, 35+(1*17), 167-(2*17)), + X(d, 35+(2*17), 167-(2*17)), + X(f, 35+(3*17), 167-(2*17)), + X(g, 35+(4*17), 167-(2*17)), + X(h, 35+(5*17), 167-(2*17)), + X(j, 35+(6*17), 167-(2*17)), + X(k, 35+(7*17), 167-(2*17)), + X(l, 35+(8*17), 167-(2*17)), + X(semicolon, 35+(9*17), 167-(2*17)), + X(apostrophe, 35+(10*17), 167-(2*17)), + X(return, 36+(11*17), 167-(2*17)), + + X(shift_l, 1, 167-(1*17)), + X(z, 43+(0*17), 167-(1*17)), + X(x, 43+(1*17), 167-(1*17)), + X(c, 43+(2*17), 167-(1*17)), + X(v, 43+(3*17), 167-(1*17)), + X(b, 43+(4*17), 167-(1*17)), + X(n, 43+(5*17), 167-(1*17)), + X(m, 43+(6*17), 167-(1*17)), + X(comma, 43+(7*17), 167-(1*17)), + X(period, 43+(8*17), 167-(1*17)), + X(slash, 43+(9*17), 167-(1*17)), + X(shift_r, 44+(10*17), 167-(1*17)), + + X(caps_lock, 1, 167-(0*17)), + X(alt_l, 47, 167-(0*17)), + X(space, 73, 167-(0*17)), + X(alt_r, 192, 167-(0*17)), + X(control_r, 231, 167-(0*17)), + + { 0, 0, 0, 0 } +}; diff --git a/nds/arc.bmp b/nds/arc.bmp new file mode 100644 index 0000000..4a47b0e Binary files /dev/null and b/nds/arc.bmp differ diff --git a/nds/filecalls.c b/nds/filecalls.c new file mode 100644 index 0000000..a3b1b73 --- /dev/null +++ b/nds/filecalls.c @@ -0,0 +1,181 @@ +/* filecalls.c posix implimentatation of the abstracted interface to accesing host + OS file and Directory functions. + Copyright (c) 2005 Peter Howkins, covered under the GNU GPL see file COPYING for more + details */ +/* ansi includes */ +#include +#include +#include +#include + +/* posix includes */ +#include +#include +#include + +/* application includes */ +#include "dbugsys.h" +#include "filecalls.h" + +static bool File_GetInfo(const char *sPath, FileInfo *phFileInfo); + +/** + * Directory_Open + * + * Open a directory so that it's contents can be scanned + * + * @param sPath Location of directory to scan + * @param hDir Pointer to a Directory struct to fill in + * @returns true on success false on failure + */ +bool Directory_Open(const char *sPath, Directory *hDirectory) +{ + assert(sPath); + assert(*sPath); + assert(hDirectory); + + hDirectory->sPathLen = strlen(sPath); + hDirectory->sPath = malloc(hDirectory->sPathLen + 1); + + if(NULL == hDirectory->sPath) { + return false; + } else { + strcpy(hDirectory->sPath, sPath); + } + + hDirectory->hDir = opendir(sPath); + + if(NULL == hDirectory->hDir) { + return false; + } else { + return true; + } +} + +/** + * Directory_Close + * + * Close a previously opened Directory + * + * @param hDirectory Directory to close + */ +void Directory_Close(Directory hDirectory) +{ + closedir(hDirectory.hDir); + free(hDirectory.sPath); +} + +/** + * Directory_GetNextEntry + * + * Get the next entry in a directory + * + * @param hDirectory pointer to Directory to get entry from + * @returns String of filename or NULL on EndOfDirectory + */ +char *Directory_GetNextEntry(Directory *hDirectory, FileInfo *phFileInfo) +{ + struct dirent *phDirEntry; + + assert(hDirectory); + + phDirEntry = readdir(hDirectory->hDir); + if(!phDirEntry) { + return NULL; + } + + if (phFileInfo) { + phFileInfo->bIsRegularFile = false; + phFileInfo->bIsDirectory = false; + +#ifdef _DIRENT_HAVE_D_TYPE + if (phDirEntry->d_type == DT_REG || phDirEntry->d_type == DT_DIR) { + phFileInfo->bIsRegularFile = (phDirEntry->d_type == DT_REG); + phFileInfo->bIsDirectory = (phDirEntry->d_type == DT_DIR); + } else +#endif + { + char *path = Directory_GetFullPath(hDirectory, phDirEntry->d_name); + if (path) { + File_GetInfo(path, phFileInfo); + free(path); + } + } + } + + return phDirEntry->d_name; +} + +/** + * Directory_GetFullPath + * + * Get the full path of a file in a directory + * + * @param hDirectory pointer to Directory to get the base path from + * @returns String of the full path or NULL on EndOfDirectory + */ +char *Directory_GetFullPath(Directory *hDirectory, const char *leaf) { + size_t len = hDirectory->sPathLen + strlen(leaf) + 1; + char *path = malloc(len + 1); + if (!path) { + return NULL; + } + + strcpy(path, hDirectory->sPath); + strcat(path, "/"); + strcat(path, leaf); + return path; +} + +/** + * File_OpenAppData + * + * Open the specified file in the application data directory + * + * @param sName Name of file to open + * @param sMode Mode to open the file with + * @returns File handle or NULL on failure + */ +FILE *File_OpenAppData(const char *sName, const char *sMode) +{ + return NULL; +} + +/** + * File_GetInfo + * + * Fills in lots of useful info about the passed in file + * + * @param sPath Path to file to check + * @param phFileInfo pointer to FileInfo struct to fill in + * @returns false on failure true on success + */ +static bool File_GetInfo(const char *sPath, FileInfo *phFileInfo) +{ + struct stat hEntryInfo; + + assert(sPath); + assert(phFileInfo); + + if (stat(sPath, &hEntryInfo) != 0) { + warn("Warning: could not stat() entry \'%s\': %s\n", + sPath, strerror(errno)); + return false; + } + + /* Initialise components */ + phFileInfo->bIsRegularFile = false; + phFileInfo->bIsDirectory = false; + + if (S_ISREG(hEntryInfo.st_mode)) { + phFileInfo->bIsRegularFile = true; + } + + if (S_ISDIR(hEntryInfo.st_mode)) { + phFileInfo->bIsDirectory = true; + } + + /* Success! */ + return true; +} + diff --git a/nds/filecalls_internal.h b/nds/filecalls_internal.h new file mode 100644 index 0000000..0c67167 --- /dev/null +++ b/nds/filecalls_internal.h @@ -0,0 +1,17 @@ +/* filecalls_internal.h + Copyright (c) 2005 Peter Howkins, covered under the GNU GPL see file COPYING for more + details */ + +#ifndef __FILECALLS_INTERNAL_H +#define __FILECALLS_INTERNAL_H + +#include +#include + +struct Directory_s { + DIR *hDir; + char *sPath; + size_t sPathLen; +}; + +#endif /* __FILECALLS_H */ diff --git a/nds/img/bg.grit b/nds/img/bg.grit new file mode 100644 index 0000000..df0e0c3 --- /dev/null +++ b/nds/img/bg.grit @@ -0,0 +1,17 @@ +# graphics in tile format +-gt + +# tile reduction by tiles, palette and hflip/vflip +-mRtf + +# graphics bit depth is 4 (16 color) +-gB4 + +# include palette data +-p + +# map layout standard bg format +-mLs + +# offset the bg tiles to fit alongside the font tiles +-ma256 diff --git a/nds/img/bg.png b/nds/img/bg.png new file mode 100644 index 0000000..3ef09fd Binary files /dev/null and b/nds/img/bg.png differ diff --git a/nds/img/font.grit b/nds/img/font.grit new file mode 100644 index 0000000..fa7eb42 --- /dev/null +++ b/nds/img/font.grit @@ -0,0 +1,14 @@ +# graphics in tile format +-gt + +# exclude map data +-m! + +# graphics bit depth is 4 (16 color) +-gB4 + +# include palette data +-p + +# use lz77 compression +-gzl diff --git a/nds/img/font.png b/nds/img/font.png new file mode 100644 index 0000000..75b9988 Binary files /dev/null and b/nds/img/font.png differ diff --git a/nds/img/keys.grit b/nds/img/keys.grit new file mode 100644 index 0000000..cf7fe60 --- /dev/null +++ b/nds/img/keys.grit @@ -0,0 +1,14 @@ +# exclude map data +-m! + +# graphics bit depth is 4 (16 color) +-gB4 + +# include palette data +-p + +# metatile is 2x2 tiles (16x16 pixels) +-Mw2 -Mh2 + +# use lz77 compression +-gzl diff --git a/nds/img/keys.png b/nds/img/keys.png new file mode 100644 index 0000000..c677cfc Binary files /dev/null and b/nds/img/keys.png differ diff --git a/nds/main.c b/nds/main.c new file mode 100644 index 0000000..1eb4d1b --- /dev/null +++ b/nds/main.c @@ -0,0 +1,50 @@ +#include "../dagstandalone.h" +#include "../prof.h" + +#include "../arch/ArcemConfig.h" +#include "../arch/ControlPane.h" + +#include +#if defined(EXTNROM_SUPPORT) +#include +#else +#include +#endif + +static ArcemConfig hArcemConfig; + +int main(int argc,char *argv[]) { + Prof_Init(); + +#if defined(EXTNROM_SUPPORT) + if (!nitroFSInit(NULL)) { + ControlPane_Error(EXIT_FAILURE, "Failed to initialise filesystem"); + } +#else + if (!fatInitDefault()) { + ControlPane_Error(EXIT_FAILURE, "Failed to initialise filesystem"); + } +#endif + + // Setup the default values for the config system + ArcemConfig_SetupDefaults(&hArcemConfig); + + // Parse the config file to overrule the defaults + ArcemConfig_ParseConfigFile(&hArcemConfig); + + // Parse any commandline arguments given to the program + // to overrule the defaults + ArcemConfig_ParseCommandLine(&hArcemConfig, argc, argv); + + if (isDSiMode()) { + if (hArcemConfig.eMemSize > MemSize_4M) + hArcemConfig.eMemSize = MemSize_4M; + } else { + if (hArcemConfig.eMemSize > MemSize_1M) + hArcemConfig.eMemSize = MemSize_1M; + } + + dagstandalone(&hArcemConfig); + + return EXIT_SUCCESS; +} diff --git a/support_modules/modes/ArcemModes,ffa b/support_modules/modes/ArcemModes,ffa index bff864a..b83306b 100755 Binary files a/support_modules/modes/ArcemModes,ffa and b/support_modules/modes/ArcemModes,ffa differ diff --git a/support_modules/modes/MakeModes,ffb b/support_modules/modes/MakeModes,ffb index 3183f8d..4e66907 100755 Binary files a/support_modules/modes/MakeModes,ffb and b/support_modules/modes/MakeModes,ffb differ diff --git a/support_modules/modes/ModeGen,ffb b/support_modules/modes/ModeGen,ffb index c59740d..10d8a32 100755 Binary files a/support_modules/modes/ModeGen,ffb and b/support_modules/modes/ModeGen,ffb differ diff --git a/support_modules/modes/README b/support_modules/modes/README index 1d13496..143815b 100755 --- a/support_modules/modes/README +++ b/support_modules/modes/README @@ -39,6 +39,8 @@ PDA style modes (I wanted to see what RISC OS would look like on an iPaq) 124 240 320 4 16 125 240 320 8 256 +126 256 192 8 256 + Developing and assembling -------------------------