diff --git a/src/compiler/fb.bas b/src/compiler/fb.bas index c050c49c1..3467b2945 100644 --- a/src/compiler/fb.bas +++ b/src/compiler/fb.bas @@ -233,6 +233,38 @@ dim shared as FBTARGET targetinfo(0 to FB_COMPTARGETS-1) = _ 0 or FB_TARGETOPT_UNIX _ or FB_TARGETOPT_CALLEEPOPSHIDDENPTR _ or FB_TARGETOPT_RETURNINREGS _ + ), _ + ( _ + @"amiga", _ + FB_DATATYPE_ULONG, _ + FB_FUNCMODE_CDECL, _ + FB_FUNCMODE_STDCALL_MS, _ + 0 or FB_TARGETOPT_CALLEEPOPSHIDDENPTR _ + ), _ + ( _ + @"aros", _ + FB_DATATYPE_ULONG, _ + FB_FUNCMODE_CDECL, _ + FB_FUNCMODE_STDCALL_MS, _ + 0 or FB_TARGETOPT_UNIX _ + or FB_TARGETOPT_CALLEEPOPSHIDDENPTR _ + or FB_TARGETOPT_ELF _ + ), _ + ( _ + @"morphos", _ + FB_DATATYPE_ULONG, _ + FB_FUNCMODE_CDECL, _ + FB_FUNCMODE_STDCALL_MS, _ + 0 or FB_TARGETOPT_CALLEEPOPSHIDDENPTR _ + or FB_TARGETOPT_ELF _ + ), _ + ( _ + @"amigaos4", _ + FB_DATATYPE_ULONG, _ + FB_FUNCMODE_CDECL, _ + FB_FUNCMODE_STDCALL_MS, _ + 0 or FB_TARGETOPT_CALLEEPOPSHIDDENPTR _ + or FB_TARGETOPT_ELF _ ) _ } @@ -250,7 +282,8 @@ dim shared as FBCPUFAMILYINFO cpufamilyinfo(0 to FB_CPUFAMILY__COUNT-1) = _ (@"powerpc" , FB_DEFAULT_CPUTYPE_PPC ), _ (@"powerpc64" , FB_DEFAULT_CPUTYPE_PPC64 ), _ (@"powerpc64le", FB_DEFAULT_CPUTYPE_PPC64LE), _ - (@"asmjs" , FB_DEFAULT_CPUTYPE_ASMJS ) _ + (@"asmjs" , FB_DEFAULT_CPUTYPE_ASMJS ), _ + (@"m68k" , FB_DEFAULT_CPUTYPE_M68K ) _ } type FBCPUTYPEINFO @@ -286,7 +319,8 @@ dim shared as FBCPUTYPEINFO cputypeinfo(0 to FB_CPUTYPE__COUNT-1) = _ ( NULL , @"powerpc" , FB_CPUFAMILY_PPC , 32, TRUE ), _ '' FB_CPUTYPE_PPC ( NULL , @"powerpc64" , FB_CPUFAMILY_PPC64 , 64, TRUE ), _ '' FB_CPUTYPE_PPC64 ( NULL , @"powerpc64le" , FB_CPUFAMILY_PPC64LE, 64, FALSE ), _ '' FB_CPUTYPE_PPC64LE - ( NULL , @"asmjs" , FB_CPUFAMILY_ASMJS , 32, FALSE ) _ '' FB_CPUTYPE_ASMJS + ( NULL , @"asmjs" , FB_CPUFAMILY_ASMJS , 32, FALSE ), _ '' FB_CPUTYPE_ASMJS + ( @"68020" , @"m68k" , FB_CPUFAMILY_M68K , 32, TRUE ) _ '' FB_CPUTYPE_M68K } '':::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: diff --git a/src/compiler/fb.bi b/src/compiler/fb.bi index af39ff8fc..a36313f7b 100644 --- a/src/compiler/fb.bi +++ b/src/compiler/fb.bi @@ -182,6 +182,7 @@ enum FB_CPUTYPE FB_CPUTYPE_PPC64 FB_CPUTYPE_PPC64LE FB_CPUTYPE_ASMJS + FB_CPUTYPE_M68K FB_CPUTYPE__COUNT end enum @@ -194,6 +195,7 @@ enum FB_CPUFAMILY_PPC64 FB_CPUFAMILY_PPC64LE FB_CPUFAMILY_ASMJS + FB_CPUFAMILY_M68K FB_CPUFAMILY__COUNT end enum @@ -247,6 +249,10 @@ enum FB_COMPTARGET FB_COMPTARGET_DARWIN FB_COMPTARGET_NETBSD FB_COMPTARGET_JS + FB_COMPTARGET_AMIGA + FB_COMPTARGET_AROS + FB_COMPTARGET_MORPHOS + FB_COMPTARGET_AMIGAOS4 FB_COMPTARGETS end enum @@ -464,6 +470,7 @@ const FB_DEFAULT_CPUTYPE_PPC = FB_CPUTYPE_PPC const FB_DEFAULT_CPUTYPE_PPC64 = FB_CPUTYPE_PPC64 const FB_DEFAULT_CPUTYPE_PPC64LE = FB_CPUTYPE_PPC64LE const FB_DEFAULT_CPUTYPE_ASMJS = FB_CPUTYPE_ASMJS +const FB_DEFAULT_CPUTYPE_M68K = FB_CPUTYPE_M68K '' default 32 and 64 bit CPU's (based on defaults above) #ifdef __FB_ARM__ diff --git a/src/compiler/fbc.bas b/src/compiler/fbc.bas index 61b77df3f..fd6e44700 100644 --- a/src/compiler/fbc.bas +++ b/src/compiler/fbc.bas @@ -305,10 +305,10 @@ private function hGet1stOutputLineFromCommand( byref cmd as string ) as string end if dim ln as string - input #f, ln + line input #f, ln close f - return ln + return trim( ln ) end function '' Pass some arguments to gcc/clang and read the results. Returns an empty string on @@ -840,7 +840,14 @@ private function hLinkFiles( ) as integer case FB_CPUFAMILY_ARM '' fixme: this is clearly too specific ldcline += "-arch armv6 " + case FB_CPUFAMILY_AARCH64 + ldcline += "-arch arm64 " end select + + '' Amiga-like targets: no special ld emulation flags needed + case FB_COMPTARGET_AMIGA, FB_COMPTARGET_AROS, _ + FB_COMPTARGET_MORPHOS, FB_COMPTARGET_AMIGAOS4 + end select '' Set executable name @@ -1123,9 +1130,11 @@ private function hLinkFiles( ) as integer wend end scope - '' And the sysroot + '' And the sysroot (Darwin uses -syslibroot, handled separately) if( len( fbc.sysroot ) ) then - ldcline += " --sysroot=" + fbc.sysroot + if( fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_DARWIN ) then + ldcline += " --sysroot=" + fbc.sysroot + end if end if '' crt begin objects @@ -1167,6 +1176,8 @@ private function hLinkFiles( ) as integer FB_COMPTARGET_NETBSD, FB_COMPTARGET_DRAGONFLY, FB_COMPTARGET_SOLARIS if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_EXECUTABLE) then + '' Darwin doesn't need CRT startup objects on modern macOS + if (fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_DARWIN) then if( fbGetOption( FB_COMPOPT_PROFILE ) ) then select case as const fbGetOption( FB_COMPOPT_TARGET ) case FB_COMPTARGET_OPENBSD, FB_COMPTARGET_NETBSD @@ -1182,6 +1193,7 @@ private function hLinkFiles( ) as integer ldcline += hFindLib( "crt1.o" ) end select end if + end if end if if (fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_DARWIN) then @@ -1323,7 +1335,29 @@ private function hLinkFiles( ) as integer end select if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_DARWIN ) then - ldcline += " -macosx_version_min 10.4" + scope + '' Fallback SDK version for cross-compilation when xcrun is unavailable. + '' Overridden at runtime by xcrun --show-sdk-version on native macOS. + dim as string sdkver = "14.0" + dim as string sysroot + if( len( fbc.sysroot ) > 0 ) then + sysroot = fbc.sysroot +#ifdef __FB_DARWIN__ + else + sysroot = hGet1stOutputLineFromCommand( "xcrun --show-sdk-path" ) + dim as string v = hGet1stOutputLineFromCommand( "xcrun --show-sdk-version" ) + if( len( v ) > 0 ) then sdkver = v +#endif + end if + if( fbGetCpuFamily( ) = FB_CPUFAMILY_AARCH64 ) then + ldcline += " -platform_version macos 11.0.0 " + sdkver + else + ldcline += " -platform_version macos 10.4.0 " + sdkver + end if + if( len( sysroot ) > 0 ) then + ldcline += " -syslibroot " + QUOTE + sysroot + QUOTE + end if + end scope end if '' This is required for 64-bit modules on *nix-y platforms @@ -1332,8 +1366,7 @@ private function hLinkFiles( ) as integer select case as const fbGetOption( FB_COMPOPT_TARGET ) case FB_COMPTARGET_LINUX, FB_COMPTARGET_FREEBSD, _ FB_COMPTARGET_OPENBSD, FB_COMPTARGET_NETBSD, _ - FB_COMPTARGET_DRAGONFLY, FB_COMPTARGET_SOLARIS, _ - FB_COMPTARGET_DARWIN + FB_COMPTARGET_DRAGONFLY, FB_COMPTARGET_SOLARIS dim as long outtype = fbGetOption( FB_COMPOPT_OUTTYPE ) if outtype = FB_OUTTYPE_EXECUTABLE OrElse outtype = FB_OUTTYPE_DYNAMICLIB Then dim as long cpufamily = fbGetCpuFamily( ) @@ -1656,7 +1689,11 @@ dim shared as FBGNUOSINFO gnuosmap(0 to ...) => _ (@"solaris" , FB_COMPTARGET_SOLARIS ), _ (@"netbsd" , FB_COMPTARGET_NETBSD ), _ (@"openbsd" , FB_COMPTARGET_OPENBSD ), _ - (@"xbox" , FB_COMPTARGET_XBOX ) _ + (@"xbox" , FB_COMPTARGET_XBOX ), _ + (@"amigaos4" , FB_COMPTARGET_AMIGAOS4 ), _ + (@"amigaos" , FB_COMPTARGET_AMIGA ), _ + (@"aros" , FB_COMPTARGET_AROS ), _ + (@"morphos" , FB_COMPTARGET_MORPHOS ) _ } '' Architectures recognized when parsing GNU triplets (-target option) @@ -1681,7 +1718,8 @@ dim shared as FBGNUARCHINFO gnuarchmap(0 to ...) => _ (@"ppc64 " , FB_DEFAULT_CPUTYPE_PPC64 ), _ (@"powerpc64" , FB_DEFAULT_CPUTYPE_PPC64 ), _ (@"ppc64le " , FB_DEFAULT_CPUTYPE_PPC64LE), _ - (@"powerpc64le", FB_DEFAULT_CPUTYPE_PPC64LE) _ + (@"powerpc64le", FB_DEFAULT_CPUTYPE_PPC64LE), _ + (@"m68k" , FB_DEFAULT_CPUTYPE_M68K ) _ } '' Identify OS (FB_COMPTARGET_*) and architecture (FB_CPUTYPE_*) in a GNU @@ -1764,7 +1802,11 @@ dim shared as FBOSARCHINFO fbosarchmap(0 to ...) => _ (@"linux" , FB_COMPTARGET_LINUX , FB_DEFAULT_CPUTYPE ), _ (@"android", FB_COMPTARGET_ANDROID, FB_CPUTYPE_ARMV7A ), _ (@"netbsd" , FB_COMPTARGET_NETBSD , FB_DEFAULT_CPUTYPE ), _ - (@"openbsd", FB_COMPTARGET_OPENBSD, FB_DEFAULT_CPUTYPE ) _ + (@"openbsd", FB_COMPTARGET_OPENBSD, FB_DEFAULT_CPUTYPE ), _ + (@"amiga" , FB_COMPTARGET_AMIGA , FB_DEFAULT_CPUTYPE_M68K ), _ + (@"aros" , FB_COMPTARGET_AROS , FB_DEFAULT_CPUTYPE ), _ + (@"morphos" , FB_COMPTARGET_MORPHOS , FB_DEFAULT_CPUTYPE_PPC ), _ + (@"amigaos4", FB_COMPTARGET_AMIGAOS4, FB_DEFAULT_CPUTYPE_PPC ) _ } '' @@ -3684,7 +3726,7 @@ private function hCompileStage2Module( byval module as FBCIOFILE ptr ) as intege '' GCC doesn't recognize the -march option and PowerPC combination '' and recommendeds the -mcpu option be used for PowerPC. select case fbGetCpuFamily( ) - case FB_CPUFAMILY_PPC, FB_CPUFAMILY_PPC64, FB_CPUFAMILY_PPC64LE + case FB_CPUFAMILY_PPC, FB_CPUFAMILY_PPC64, FB_CPUFAMILY_PPC64LE, FB_CPUFAMILY_M68K if( fbc.cputype_is_native ) then ln += "-mcpu=native " else @@ -4308,7 +4350,6 @@ private sub hAddDefaultLibs( ) end if case FB_COMPTARGET_DARWIN - fbcAddDefLib( "gcc" ) fbcAddDefLib( "System" ) fbcAddDefLib( "pthread" ) fbcAddDefLib( "ncurses" ) @@ -4427,6 +4468,28 @@ private sub hAddDefaultLibs( ) fbcAddDefLib( "gmon" ) end if + case FB_COMPTARGET_AMIGA + fbcAddDefLib( "gcc" ) + fbcAddDefLib( "amiga" ) + fbcAddDefLib( "m" ) + + case FB_COMPTARGET_AROS + fbcAddDefLib( "gcc" ) + fbcAddDefLib( "arosc" ) + fbcAddDefLib( "autoinit" ) + fbcAddDefLib( "m" ) + + case FB_COMPTARGET_MORPHOS + fbcAddDefLib( "gcc" ) + fbcAddDefLib( "c" ) + fbcAddDefLib( "m" ) + + case FB_COMPTARGET_AMIGAOS4 + fbcAddDefLib( "gcc" ) + fbcAddDefLib( "c" ) + fbcAddDefLib( "m" ) + fbcAddDefLib( "auto" ) + end select end sub diff --git a/src/gfxlib2/amiga/gfx_amiga.c b/src/gfxlib2/amiga/gfx_amiga.c new file mode 100644 index 000000000..4a5eb3694 --- /dev/null +++ b/src/gfxlib2/amiga/gfx_amiga.c @@ -0,0 +1,203 @@ +/* FreeBASIC gfxlib2 driver for AmigaOS via intuition.library + graphics.library */ + +#include "../fb_gfx.h" + +#if defined(HOST_AMIGAOS) +#include +#include +#include +#include + +extern struct ExecBase *SysBase; +static struct IntuitionBase *IntuitionBase; +static struct GfxBase *GfxBase; +static struct Screen *amiga_screen; +static struct Window *amiga_window; +static int mouse_x, mouse_y, mouse_z, mouse_buttons; +static struct RastPort tmp_rp; +static struct BitMap *tmp_bm; +static int tmp_bm_width; + + +/* Amiga scancode to FreeBASIC (PC Set 1) scancode mapping */ +static const unsigned char amiga_to_fb_scancode[128] = { + /* 0x00-0x0F: ` 1 2 3 4 5 6 7 8 9 0 - = \ x Del */ + SC_TILDE, SC_1, SC_2, SC_3, SC_4, SC_5, SC_6, SC_7, + SC_8, SC_9, SC_0, SC_MINUS, SC_EQUALS, SC_BACKSLASH, 0, SC_DELETE, + /* 0x10-0x1F: Q W E R T Y U I O P [ ] x x x KP0 */ + SC_Q, SC_W, SC_E, SC_R, SC_T, SC_Y, SC_U, SC_I, + SC_O, SC_P, SC_LEFTBRACKET, SC_RIGHTBRACKET, 0, 0, 0, SC_KEYPAD0, + /* 0x20-0x2F: A S D F G H J K L ; ' x x x x KP. */ + SC_A, SC_S, SC_D, SC_F, SC_G, SC_H, SC_J, SC_K, + SC_L, SC_SEMICOLON, SC_QUOTE, 0, 0, 0, 0, SC_KEYPADDOT, + /* 0x30-0x3F: x Z X C V B N M , . / x x x x x */ + 0, SC_Z, SC_X, SC_C, SC_V, SC_B, SC_N, SC_M, + SC_COMMA, SC_PERIOD, SC_SLASH, 0, 0, 0, 0, 0, + /* 0x40-0x4F: Space BS Tab Enter Ret Esc x x x x KP- x Up Down Right Left */ + SC_SPACE, SC_BACKSPACE, SC_TAB, SC_ENTER, SC_ENTER, SC_ESCAPE, 0, 0, + 0, 0, SC_KEYPADMINUS, 0, SC_UP, SC_DOWN, SC_RIGHT, SC_LEFT, + /* 0x50-0x5F: F1-F10 x x */ + SC_F1, SC_F2, SC_F3, SC_F4, SC_F5, SC_F6, SC_F7, SC_F8, + SC_F9, SC_F10, 0, 0, 0, 0, 0, 0, + /* 0x60-0x6F: LShift RShift CapsLk Ctrl LAlt RAlt LAmiga RAmiga ... */ + SC_LSHIFT, SC_RSHIFT, SC_CAPSLOCK, SC_CONTROL, SC_LALT, SC_RALT, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x70-0x7F: unused */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; +static int driver_init(char *title, int w, int h, int depth, int refresh_rate, int flags) { + if (flags & DRIVER_OPENGL) return -1; + if (depth > 8) return -1; /* Amiga chipset only supports up to 8bpp */ + if (amiga_screen) return -1; /* Already initialized */ + + IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 36); + GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 40); + if (!IntuitionBase || !GfxBase) goto fail; + + amiga_screen = OpenScreenTags(NULL, + SA_Width, w, SA_Height, h, SA_Depth, depth, + SA_Title, (ULONG)(title ? title : "FreeBASIC"), + SA_ShowTitle, FALSE, SA_Quiet, TRUE, TAG_DONE); + if (!amiga_screen) goto fail; + + amiga_window = OpenWindowTags(NULL, + WA_CustomScreen, (ULONG)amiga_screen, + WA_Left, 0, WA_Top, 0, WA_Width, w, WA_Height, h, + WA_Borderless, TRUE, WA_Activate, TRUE, WA_RMBTrap, TRUE, + WA_IDCMP, IDCMP_RAWKEY | IDCMP_MOUSEBUTTONS | IDCMP_MOUSEMOVE, + WA_Flags, WFLG_BACKDROP | WFLG_BORDERLESS | WFLG_REPORTMOUSE, + TAG_DONE); + if (!amiga_window) goto fail; + + return 0; +fail: + if (amiga_screen) { CloseScreen(amiga_screen); amiga_screen = NULL; } + if (GfxBase) { CloseLibrary((struct Library *)GfxBase); GfxBase = NULL; } + if (IntuitionBase) { CloseLibrary((struct Library *)IntuitionBase); IntuitionBase = NULL; } + return -1; +} + +extern void fb_GfxJoystickExit(void); + +static void driver_exit(void) { + fb_GfxJoystickExit(); + if (tmp_bm) { FreeBitMap(tmp_bm); tmp_bm = NULL; } + if (amiga_window) { CloseWindow(amiga_window); amiga_window = NULL; } + if (amiga_screen) { CloseScreen(amiga_screen); amiga_screen = NULL; } + if (GfxBase) { CloseLibrary((struct Library *)GfxBase); GfxBase = NULL; } + if (IntuitionBase) { CloseLibrary((struct Library *)IntuitionBase); IntuitionBase = NULL; } +} + +static void driver_lock(void) { } +static void driver_unlock(void) { } + +static void driver_set_palette(int index, int r, int g, int b) { + if (amiga_screen && GfxBase) { + + /* Scale 8-bit (0-255) to 32-bit by replicating across all bytes */ + ULONG r32 = ((ULONG)r << 24) | ((ULONG)r << 16) | ((ULONG)r << 8) | (ULONG)r; + ULONG g32 = ((ULONG)g << 24) | ((ULONG)g << 16) | ((ULONG)g << 8) | (ULONG)g; + ULONG b32 = ((ULONG)b << 24) | ((ULONG)b << 16) | ((ULONG)b << 8) | (ULONG)b; + SetRGB32(&amiga_screen->ViewPort, index, r32, g32, b32); + } +} + +static void driver_wait_vsync(void) { + if (GfxBase) WaitTOF(); +} + +static int driver_get_mouse(int *x, int *y, int *z, int *buttons, int *clip) { + *x = mouse_x; *y = mouse_y; *z = mouse_z; + *buttons = mouse_buttons; *clip = 0; + return 0; +} + +static void driver_set_mouse(int x, int y, int cursor, int clip) { + /* Not easily supported without pointer sprite manipulation */ +} + +static void driver_set_window_title(char *title) { + if (amiga_window) SetWindowTitles(amiga_window, (UBYTE *)title, (UBYTE *)-1); +} + +static void driver_update(void) { + if (!amiga_window || !__fb_gfx) return; + + struct RastPort *rp = amiga_window->RPort; + unsigned char *src = __fb_gfx->framebuffer; + int w = __fb_gfx->w, h = __fb_gfx->h; + + /* Allocate/reallocate temp bitmap for WritePixelArray8 */ + if (!tmp_bm || tmp_bm_width != w) { + if (tmp_bm) FreeBitMap(tmp_bm); + tmp_bm = AllocBitMap(w, h, amiga_screen->RastPort.BitMap->Depth, 0, + amiga_screen->RastPort.BitMap); + if (!tmp_bm) { tmp_bm_width = 0; return; } + InitRastPort(&tmp_rp); + tmp_rp.BitMap = tmp_bm; + tmp_bm_width = w; + } + + WritePixelArray8(rp, 0, 0, w - 1, h - 1, src, &tmp_rp); +} + +static void driver_poll_events(void) { + if (!amiga_window || !__fb_gfx) return; + struct IntuiMessage *msg; + while ((msg = (struct IntuiMessage *)GetMsg(amiga_window->UserPort))) { + switch (msg->Class) { + case IDCMP_MOUSEMOVE: + mouse_x = msg->MouseX; + mouse_y = msg->MouseY; + break; + case IDCMP_MOUSEBUTTONS: + if (msg->Code == SELECTDOWN) mouse_buttons |= 1; + else if (msg->Code == SELECTUP) mouse_buttons &= ~1; + else if (msg->Code == MENUDOWN) mouse_buttons |= 2; + else if (msg->Code == MENUUP) mouse_buttons &= ~2; + break; + case IDCMP_RAWKEY: { + int amiga_sc = msg->Code & 0x7F; + int pressed = !(msg->Code & 0x80); + int fb_sc = amiga_to_fb_scancode[amiga_sc]; + if (fb_sc) { + __fb_gfx->key[fb_sc] = pressed ? TRUE : FALSE; + if (pressed) fb_hPostKey(fb_sc); + } + break; + } + } + ReplyMsg((struct Message *)msg); + } +} + +const GFXDRIVER fb_gfxDriverAmiga = { + "Amiga", + driver_init, + driver_exit, + driver_lock, + driver_unlock, + driver_set_palette, + driver_wait_vsync, + driver_get_mouse, + driver_set_mouse, + driver_set_window_title, + NULL, /* set_window_pos */ + NULL, /* fetch_modes */ + NULL, /* flip */ + driver_poll_events, + driver_update +}; + +/* Driver list for Amiga */ +const GFXDRIVER *__fb_gfx_drivers_list[] = { + &fb_gfxDriverAmiga, + NULL +}; + +void fb_hScreenInfo(ssize_t *width, ssize_t *height, ssize_t *depth, ssize_t *refresh) { + *width = 640; *height = 480; *depth = 8; *refresh = 50; +} + +#endif /* HOST_AMIGAOS */ diff --git a/src/gfxlib2/amiga/gfx_joystick.c b/src/gfxlib2/amiga/gfx_joystick.c new file mode 100644 index 000000000..4c636d119 --- /dev/null +++ b/src/gfxlib2/amiga/gfx_joystick.c @@ -0,0 +1,60 @@ +/* AmigaOS joystick via lowlevel.library */ +#include "../fb_gfx.h" + +#if defined(HOST_AMIGAOS) +#include +#include +#include + +extern struct ExecBase *SysBase; +static struct Library *LowLevelBase; + +FBCALL int fb_GfxGetJoystick(int id, ssize_t *buttons, float *a1, float *a2, float *a3, + float *a4, float *a5, float *a6, float *a7, float *a8) { + if (id < 0 || id > 1) { + *buttons = 0; *a1 = *a2 = *a3 = *a4 = *a5 = *a6 = *a7 = *a8 = 0.0f; + return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); + } + + if (!LowLevelBase) { + LowLevelBase = OpenLibrary("lowlevel.library", 40); + if (!LowLevelBase) { + *buttons = 0; *a1 = *a2 = *a3 = *a4 = *a5 = *a6 = *a7 = *a8 = 0.0f; + return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); + } + } + + ULONG state = ReadJoyPort(id); + + /* Buttons */ + *buttons = 0; + if (state & JPF_BUTTON_RED) *buttons |= 1; + if (state & JPF_BUTTON_BLUE) *buttons |= 2; + if (state & JPF_BUTTON_GREEN) *buttons |= 4; + + /* Axes: digital joystick mapped to -1/0/+1 */ + *a1 = 0.0f; *a2 = 0.0f; + if (state & JPF_JOY_LEFT) *a1 = -1.0f; + if (state & JPF_JOY_RIGHT) *a1 = 1.0f; + if (state & JPF_JOY_UP) *a2 = -1.0f; + if (state & JPF_JOY_DOWN) *a2 = 1.0f; + + /* Unused axes */ + *a3 = *a4 = *a5 = *a6 = *a7 = *a8 = 0.0f; + + return fb_ErrorSetNum(FB_RTERROR_OK); +} + +#else +/* AROS/MorphOS - stub */ +FBCALL int fb_GfxGetJoystick(int id, ssize_t *buttons, float *a1, float *a2, float *a3, + float *a4, float *a5, float *a6, float *a7, float *a8) { + return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); +} +#endif + +void fb_GfxJoystickExit(void) { +#if defined(HOST_AMIGAOS) + if (LowLevelBase) { CloseLibrary(LowLevelBase); LowLevelBase = NULL; } +#endif +} diff --git a/src/rtlib/amiga/console_stubs.c b/src/rtlib/amiga/console_stubs.c new file mode 100644 index 000000000..762e9d5bb --- /dev/null +++ b/src/rtlib/amiga/console_stubs.c @@ -0,0 +1,247 @@ +/* AmigaOS console implementation via ANSI escape sequences + dos.library */ +#include "../fb.h" +#include +#include + +#if defined(HOST_AMIGAOS) +#include +extern struct DosLibrary *DOSBase; + +static long dos_Input(void) { return (long)Input(); } +static long dos_Output(void) { return (long)Output(); } +static long dos_Read(long fh, void *buf, long len) { return Read((BPTR)fh, buf, len); } +static void dos_Write(long fh, const void *buf, long len) { Write((BPTR)fh, (APTR)buf, len); } +/* WaitForChar: timeout in microseconds (0 = poll, -1 = wait forever) */ +static long dos_WaitForChar(long fh, long timeout) { return WaitForChar((BPTR)fh, timeout); } +static void dos_Delay(long ticks) { Delay(ticks); } + +static void con_write(const char *s) { + dos_Write(dos_Output(), s, strlen(s)); +} + + + +#else +/* AROS/MorphOS - use POSIX stdio */ +#include +#include +#include +#define con_write(s) fputs(s, stdout) +#define dos_Input() STDIN_FILENO +#define dos_Output() STDOUT_FILENO +#define dos_Read(fh, buf, len) read(STDIN_FILENO, buf, len) +static int dos_WaitForChar_posix(int fd, long timeout_us) { + struct timeval tv; + fd_set fds; + tv.tv_sec = timeout_us / 1000000; + tv.tv_usec = timeout_us % 1000000; + FD_ZERO(&fds); + FD_SET(fd, &fds); + return select(fd + 1, &fds, NULL, NULL, timeout_us < 0 ? NULL : &tv) > 0; +} +#define dos_WaitForChar(fh, t) dos_WaitForChar_posix(STDIN_FILENO, t) +#define dos_Delay(t) usleep((t) * 20000) +#endif + +/* --- Console state --- */ +static int con_x = 1, con_y = 1; +static int con_fg = 7, con_bg = 0; +static int con_w = 80, con_h = 25; + +/* --- Size --- */ +FBCALL void fb_ConsoleGetSize(int *cols, int *rows) { + if (cols) *cols = con_w; + if (rows) *rows = con_h; +} + +/* --- Position --- */ +FBCALL void fb_ConsoleGetXY(int *col, int *row) { + if (col) *col = con_x; + if (row) *row = con_y; +} + +int fb_ConsoleGetX(void) { return con_x; } +int fb_ConsoleGetY(void) { return con_y; } + +int fb_ConsoleLocate(int row, int col, int cursor) { + /* row/col of 0 means "keep current position" */ + int r = (row > 0) ? row : con_y; + int cl = (col > 0) ? col : con_x; + char buf[32]; + snprintf(buf, sizeof(buf), "\033[%d;%dH", r, cl); + con_write(buf); + con_y = r; con_x = cl; + return 0; +} + +/* --- Color (ANSI) --- */ +unsigned int fb_ConsoleColor(unsigned int fc, unsigned int bc, int flags) { + unsigned int old = con_fg | (con_bg << 4); + /* Map FB colors (0-15) to ANSI (30-37 + bright) */ + static const int ansi_fg[] = {30,34,32,36,31,35,33,37,90,94,92,96,91,95,93,97}; + static const int ansi_bg[] = {40,44,42,46,41,45,43,47,100,104,102,106,101,105,103,107}; + if (fc <= 15) { con_fg = fc; char buf[12]; snprintf(buf, sizeof(buf), "\033[%dm", ansi_fg[fc]); con_write(buf); } + if (bc <= 15) { con_bg = bc; char buf[12]; snprintf(buf, sizeof(buf), "\033[%dm", ansi_bg[bc]); con_write(buf); } + return old; +} + +unsigned int fb_ConsoleGetColorAtt(void) { return con_fg | (con_bg << 4); } + +/* --- Clear --- */ +void fb_ConsoleClear(int mode) { + con_write("\033[2J\033[H"); + con_x = 1; con_y = 1; +} + +/* --- Scroll --- */ +void fb_ConsoleScroll(int nrows) { + int i; + if (nrows > 0) { for (i = 0; i < nrows; i++) con_write("\033[S"); } + else if (nrows < 0) { for (i = 0; i < -nrows; i++) con_write("\033[T"); } +} + +/* --- Width --- */ +int fb_ConsoleWidth(int cols, int rows) { + if (cols > 0) con_w = cols; + if (rows > 0) con_h = rows; + return 0; +} + +/* --- View --- */ +FBCALL int fb_ConsoleView(int toprow, int botrow) { return 0; } +int fb_ConsoleViewEx(int toprow, int botrow, int set_cursor) { return 0; } +void fb_ConsoleGetView(int *toprow, int *botrow) { if (toprow) *toprow = 1; if (botrow) *botrow = con_h; } +int fb_ConsoleGetMaxRow(void) { return con_h; } +void fb_ConsoleViewUpdate(void) { } +int fb_ConsoleGetTopRow(void) { return 1; } +int fb_ConsoleGetBotRow(void) { return con_h; } +void fb_ConsoleSetTopBotRows(int top, int bot) { } + +/* --- Keyboard Input --- */ +int fb_ConsoleGetkey(void) { + long in = dos_Input(); + if (!in) return 0; + char c; + if (dos_Read(in, &c, 1) != 1) return 0; + if (c != 27) return (unsigned char)c; + /* Escape sequence: parse CSI */ + if (!dos_WaitForChar(in, 20000)) return 27; + if (dos_Read(in, &c, 1) != 1) return 27; + if (c != '[') return 27; + if (!dos_WaitForChar(in, 20000)) return 27; + if (dos_Read(in, &c, 1) != 1) return 27; + /* Map ANSI arrow/function keys to FB extended codes (256 + scancode) */ + switch (c) { + case 'A': return 0x4800; /* Up */ + case 'B': return 0x5000; /* Down */ + case 'C': return 0x4D00; /* Right */ + case 'D': return 0x4B00; /* Left */ + case 'H': return 0x4700; /* Home */ + case 'F': return 0x4F00; /* End */ + default: return 27; + } +} + +FBSTRING *fb_ConsoleInkey(void) { + long in = dos_Input(); + if (!in || !dos_WaitForChar(in, 0)) + return fb_StrAllocTempDescZEx(NULL, 0); + char c; + if (dos_Read(in, &c, 1) != 1) + return fb_StrAllocTempDescZEx(NULL, 0); + if (c != 27) + return fb_StrAllocTempDescZEx(&c, 1); + /* Parse escape sequence */ + if (!dos_WaitForChar(in, 20000)) + return fb_StrAllocTempDescZEx(&c, 1); /* Just ESC */ + char c2; + if (dos_Read(in, &c2, 1) != 1) + return fb_StrAllocTempDescZEx(&c, 1); + if (c2 != '[') { + char ext[2] = { (char)255, c2 }; + return fb_StrAllocTempDescZEx(ext, 2); + } + if (!dos_WaitForChar(in, 20000)) + return fb_StrAllocTempDescZEx(NULL, 0); + char c3; + if (dos_Read(in, &c3, 1) != 1) + return fb_StrAllocTempDescZEx(NULL, 0); + /* Map ANSI CSI to FB extended key (CHR$(255) + scancode) */ + char ext[2] = { (char)255, 0 }; + switch (c3) { + case 'A': ext[1] = 0x48; break; /* Up */ + case 'B': ext[1] = 0x50; break; /* Down */ + case 'C': ext[1] = 0x4D; break; /* Right */ + case 'D': ext[1] = 0x4B; break; /* Left */ + case 'H': ext[1] = 0x47; break; /* Home */ + case 'F': ext[1] = 0x4F; break; /* End */ + default: return fb_StrAllocTempDescZEx(NULL, 0); + } + return fb_StrAllocTempDescZEx(ext, 2); +} + +int fb_ConsoleKeyHit(void) { + long in = dos_Input(); + return (in && dos_WaitForChar(in, 0)) ? FB_TRUE : FB_FALSE; +} + +/* --- ReadStr (for INPUT/LINE INPUT from console) --- */ +char *fb_ConsoleReadStr(char *buffer, ssize_t len) { + if (!buffer || len <= 0) return buffer; + if (len == 1) { buffer[0] = '\0'; return buffer; } + long in = dos_Input(); + if (!in) { buffer[0] = '\0'; return buffer; } + ssize_t i = 0; + while (i < len - 1) { + char c; + long r = dos_Read(in, &c, 1); + if (r <= 0 || c == '\n') break; + if (c == '\r') continue; + if (c == 8 || c == 127) { /* Backspace/Delete */ + if (i > 0) { i--; con_write("\010 \010"); } + continue; + } + buffer[i++] = c; + dos_Write(dos_Output(), &c, 1); /* Echo single char */ + } + buffer[i] = '\0'; + con_write("\n"); + return buffer; +} + +/* --- Sleep --- */ +void fb_ConsoleSleep(int msecs) { + if (msecs < 0) { + /* Sleep until keypress */ + long in = dos_Input(); + if (in) { char c; dos_Read(in, &c, 1); } + } else if (msecs == 0) { + /* Yield CPU timeslice */ + dos_Delay(1); + } else { + long ticks = msecs / 20; + if (ticks < 1) ticks = 1; + dos_Delay(ticks); + } +} + +/* --- Misc --- */ +FBCALL unsigned int fb_ConsoleReadXY(int col, int row, int colorflag) { return 32; } +int fb_ConsoleIsRedirected(int is_input) { +#if defined(HOST_AMIGAOS) + extern struct Library *DOSBase; + long fh = is_input ? dos_Input() : dos_Output(); + if (!fh) return 1; + /* dos.library IsInteractive() offset -216 */ + register struct Library *a6 __asm("a6") = DOSBase; + register long d1 __asm("d1") = fh; + register long res __asm("d0"); + __asm volatile ("jsr -216(%%a6)" : "=r"(res) : "r"(a6), "r"(d1) : "a0","a1","memory"); + return res ? 0 : 1; /* interactive = not redirected */ +#else + int fd = is_input ? STDIN_FILENO : STDOUT_FILENO; + return isatty(fd) ? 0 : 1; +#endif +} +int fb_ConsolePageCopy(int src, int dst) { return 0; } +int fb_ConsolePageSet(int active, int visible) { return 0; } diff --git a/src/rtlib/amiga/fb_amiga.h b/src/rtlib/amiga/fb_amiga.h new file mode 100644 index 000000000..96633a17b --- /dev/null +++ b/src/rtlib/amiga/fb_amiga.h @@ -0,0 +1,32 @@ +#ifndef __FB_AMIGA_H__ +#define __FB_AMIGA_H__ + +/* AmigaOS/AROS/MorphOS platform definitions for the FreeBASIC runtime library */ + +#include +#include +#include + +#define FBCALL + +#define FB_NEWLINE "\n" +#define FB_NEWLINE_WSTR _LC("\n") +#define FB_BINARY_NEWLINE "\r\n" +#define FB_BINARY_NEWLINE_WSTR _LC("\r\n") + +#define FB_LL_FMTMOD "ll" +#define FB_HOST_PATHDIV "/" +#define FB_CONSOLE_MAXPAGES 1 + +/* File offset type */ +#if defined(HOST_AROS) && defined(HOST_64BIT) +typedef long long fb_off_t; +#else +typedef long fb_off_t; +#endif + +/* No background thread locking (no MT support) */ +#define BG_LOCK() +#define BG_UNLOCK() + +#endif diff --git a/src/rtlib/amiga/hinit.c b/src/rtlib/amiga/hinit.c new file mode 100644 index 000000000..f1820a3c6 --- /dev/null +++ b/src/rtlib/amiga/hinit.c @@ -0,0 +1,28 @@ +/* libfb init/end for AmigaOS - DOSBase opened by startup.s */ +#include "../fb.h" + +void fb_hInit(void) { } +void fb_hEnd(int errlevel) { + (void)errlevel; +#if defined(HOST_AMIGA) + extern void fb_hIntlExit(void); + fb_hIntlExit(); +#endif +} + +#if defined(HOST_AMIGAOS) +#include +#include +extern struct ExecBase *SysBase; +#endif + +FBCALL void fb_Beep(void) { +#if defined(HOST_AMIGAOS) + /* Open intuition.library transiently for DisplayBeep */ + struct IntuitionBase *ib = (struct IntuitionBase *)OpenLibrary("intuition.library", 36); + if (ib) { + DisplayBeep(NULL); + CloseLibrary((struct Library *)ib); + } +#endif +} diff --git a/src/rtlib/amiga/intl_ops.c b/src/rtlib/amiga/intl_ops.c new file mode 100644 index 000000000..241d42703 --- /dev/null +++ b/src/rtlib/amiga/intl_ops.c @@ -0,0 +1,107 @@ +/* Internationalization via locale.library */ +#include "../fb.h" +#include + +#if defined(HOST_AMIGAOS) +#include +#include +#include +extern struct ExecBase *SysBase; +struct Library *LocaleBase; +static struct Locale *locale; + +static void ensure_locale(void) { + if (!LocaleBase) { + LocaleBase = OpenLibrary("locale.library", 38); + if (LocaleBase) locale = OpenLocale(NULL); + } +} + +void fb_hIntlExit(void) { + if (locale) { CloseLocale(locale); locale = NULL; } + if (LocaleBase) { CloseLibrary(LocaleBase); LocaleBase = NULL; } +} +#endif + +#if !defined(HOST_AMIGAOS) +void fb_hIntlExit(void) { } +#endif + +int fb_DrvIntlGet(int item) { +#if defined(HOST_AMIGAOS) + ensure_locale(); + if (!locale) return 0; + switch (item) { + case 0: return (locale->loc_DecimalPoint && locale->loc_DecimalPoint[0]) ? locale->loc_DecimalPoint[0] : '.'; + case 1: return (locale->loc_DateSep && locale->loc_DateSep[0]) ? locale->loc_DateSep[0] : '/'; + case 2: return (locale->loc_TimeSep && locale->loc_TimeSep[0]) ? locale->loc_TimeSep[0] : ':'; + } +#endif + return 0; +} + +int fb_DrvIntlGetDateFormat(char *buf, size_t buflen) { + if (buflen == 0) return 0; +#if defined(HOST_AMIGAOS) + ensure_locale(); + if (locale && locale->loc_DateFormat) { + strncpy(buf, locale->loc_DateFormat, buflen - 1); + buf[buflen - 1] = '\0'; + return 0; + } +#endif + strncpy(buf, "dd/mm/yyyy", buflen - 1); + buf[buflen - 1] = '\0'; + return 0; +} + +int fb_DrvIntlGetTimeFormat(char *buf, size_t buflen) { + if (buflen == 0) return 0; +#if defined(HOST_AMIGAOS) + ensure_locale(); + if (locale && locale->loc_TimeFormat) { + strncpy(buf, locale->loc_TimeFormat, buflen - 1); + buf[buflen - 1] = '\0'; + return 0; + } +#endif + strncpy(buf, "hh:mm:ss", buflen - 1); + buf[buflen - 1] = '\0'; + return 0; +} + +int fb_DrvIntlGetMonthName(int month, int abbreviated, char *buf, size_t buflen) { + static const char *months[] = {"","January","February","March","April","May","June", + "July","August","September","October","November","December"}; + static const char *abbr[] = {"","Jan","Feb","Mar","Apr","May","Jun", + "Jul","Aug","Sep","Oct","Nov","Dec"}; + if (buflen == 0) return -1; + if (month < 1 || month > 12) { buf[0] = 0; return -1; } +#if defined(HOST_AMIGAOS) + ensure_locale(); + if (locale) { + /* Use named constants from libraries/locale.h */ + const char *s = GetLocaleStr(locale, abbreviated ? (ABMON_1 + month - 1) : (MON_1 + month - 1)); + if (s && s[0]) { strncpy(buf, s, buflen - 1); buf[buflen-1] = '\0'; return 0; } + } +#endif + strncpy(buf, abbreviated ? abbr[month] : months[month], buflen - 1); buf[buflen-1] = '\0'; + return 0; +} + +int fb_DrvIntlGetWeekdayName(int weekday, int abbreviated, char *buf, size_t buflen) { + static const char *days[] = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}; + static const char *abbr[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; + if (buflen == 0) return -1; + if (weekday < 0 || weekday > 6) { buf[0] = 0; return -1; } +#if defined(HOST_AMIGAOS) + ensure_locale(); + if (locale) { + /* Use named constants from libraries/locale.h */ + const char *s = GetLocaleStr(locale, abbreviated ? (ABDAY_1 + weekday) : (DAY_1 + weekday)); + if (s && s[0]) { strncpy(buf, s, buflen - 1); buf[buflen-1] = '\0'; return 0; } + } +#endif + strncpy(buf, abbreviated ? abbr[weekday] : days[weekday], buflen - 1); buf[buflen-1] = '\0'; + return 0; +} diff --git a/src/rtlib/amiga/io_mouse.c b/src/rtlib/amiga/io_mouse.c new file mode 100644 index 000000000..1e1ab8988 --- /dev/null +++ b/src/rtlib/amiga/io_mouse.c @@ -0,0 +1,11 @@ +#include "../fb.h" + +int fb_ConsoleGetMouse(int *x, int *y, int *z, int *buttons, int *clip) { + if (x) *x = -1; if (y) *y = -1; if (z) *z = -1; + if (buttons) *buttons = -1; if (clip) *clip = -1; + return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); +} + +int fb_ConsoleSetMouse(int x, int y, int cursor, int clip) { + return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); +} diff --git a/src/rtlib/amiga/io_multikey.c b/src/rtlib/amiga/io_multikey.c new file mode 100644 index 000000000..0841331f4 --- /dev/null +++ b/src/rtlib/amiga/io_multikey.c @@ -0,0 +1,6 @@ +#include "../fb.h" + +int fb_ConsoleMultikey(int scancode) { + fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); + return FB_FALSE; +} diff --git a/src/rtlib/amiga/io_printbuff.c b/src/rtlib/amiga/io_printbuff.c new file mode 100644 index 000000000..eb8565bb0 --- /dev/null +++ b/src/rtlib/amiga/io_printbuff.c @@ -0,0 +1,47 @@ +/* Console output for AmigaOS/AROS/MorphOS */ +#include "../fb.h" +#include +#include + +#if defined(HOST_AMIGAOS) +#include +extern struct DosLibrary *DOSBase; + +void fb_ConsolePrintBufferEx(const void *buffer, size_t len, int mask) { + BPTR out = Output(); + if (out && len > 0) Write(out, (APTR)buffer, (LONG)len); +} + +#else +/* AROS/MorphOS/AmigaOS4: have working stdio */ +#include + +void fb_ConsolePrintBufferEx(const void *buffer, size_t len, int mask) { + fwrite(buffer, 1, len, stdout); + fflush(stdout); +} + +#endif + +void fb_ConsolePrintBuffer(const char *buffer, int mask) { + fb_ConsolePrintBufferEx(buffer, strlen(buffer), mask); +} + +void fb_ConsolePrintBufferWstrEx(const FB_WCHAR *buffer, size_t len, int mask) { + if (!buffer || len == 0) return; + char stack_buf[256]; + char *tmp = (len < sizeof(stack_buf)) ? stack_buf : (char *)malloc(len + 1); + if (!tmp) return; + size_t i; + for (i = 0; i < len; i++) + tmp[i] = (buffer[i] < 256) ? (char)buffer[i] : '?'; + tmp[i] = 0; + fb_ConsolePrintBufferEx(tmp, i, mask); + if (tmp != stack_buf) free(tmp); +} + +void fb_ConsolePrintBufferWstr(const FB_WCHAR *buffer, int mask) { + size_t len = 0; + if (buffer) while (buffer[len]) len++; + fb_ConsolePrintBufferWstrEx(buffer, len, mask); +} diff --git a/src/rtlib/amiga/io_printer.c b/src/rtlib/amiga/io_printer.c new file mode 100644 index 000000000..7565fa769 --- /dev/null +++ b/src/rtlib/amiga/io_printer.c @@ -0,0 +1,51 @@ +/* Printer support via PRT: device */ +#include "../fb.h" +#include + +#if defined(HOST_AMIGAOS) +#include +extern struct DosLibrary *DOSBase; +static BPTR printer_fh; +#endif + +int fb_PrinterOpen(void) { +#if defined(HOST_AMIGAOS) + if (printer_fh) Close(printer_fh); /* Close previous if still open */ + printer_fh = Open("PRT:", MODE_NEWFILE); + return printer_fh ? 0 : -1; +#else + return -1; +#endif +} + +int fb_PrinterWrite(const void *data, size_t len) { +#if defined(HOST_AMIGAOS) + if (!printer_fh) return -1; + long written = Write(printer_fh, (APTR)data, len); + return (written == (long)len) ? 0 : -1; +#else + return -1; +#endif +} + +int fb_PrinterWriteWstr(const FB_WCHAR *data, size_t len) { + /* Convert to narrow and print */ + if (!data || len == 0) return 0; + char *tmp = malloc(len + 1); + if (!tmp) return -1; + size_t i; + for (i = 0; i < len; i++) tmp[i] = (char)(data[i] & 0xFF); + tmp[i] = 0; + int ret = fb_PrinterWrite(tmp, len); + free(tmp); + return ret; +} + +int fb_PrinterClose(void) { +#if defined(HOST_AMIGAOS) + if (printer_fh) { Close(printer_fh); printer_fh = 0; } + return 0; +#else + return -1; +#endif +} diff --git a/src/rtlib/amiga/io_serial.c b/src/rtlib/amiga/io_serial.c new file mode 100644 index 000000000..ba87118b8 --- /dev/null +++ b/src/rtlib/amiga/io_serial.c @@ -0,0 +1,119 @@ +/* AmigaOS serial port via serial.device */ +#include "../fb.h" + +#if defined(HOST_AMIGAOS) +#include +#include + +extern struct ExecBase *SysBase; + +typedef struct { + struct MsgPort *port; + struct IOExtSer *io; + int is_open; +} AMIGA_SERIAL; + +int fb_SerialOpen(FB_FILE *handle, int iPort, FB_SERIAL_OPTIONS *options, + const char *pszDevice, void **ppvHandle) { + AMIGA_SERIAL *ser = (AMIGA_SERIAL *)calloc(1, sizeof(AMIGA_SERIAL)); + if (!ser) return fb_ErrorSetNum(FB_RTERROR_OUTOFMEM); + + ser->port = CreateMsgPort(); + if (!ser->port) { free(ser); return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); } + + ser->io = (struct IOExtSer *)CreateIORequest(ser->port, sizeof(struct IOExtSer)); + if (!ser->io) { DeleteMsgPort(ser->port); free(ser); return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); } + + const char *devname = (pszDevice && pszDevice[0]) ? pszDevice : "serial.device"; + if (OpenDevice(devname, iPort, (struct IORequest *)ser->io, 0)) { + DeleteIORequest((struct IORequest *)ser->io); + DeleteMsgPort(ser->port); + free(ser); + return fb_ErrorSetNum(FB_RTERROR_FILENOTFOUND); + } + + /* Configure serial parameters */ + if (options) { + if (options->speed > 0) ser->io->io_Baud = options->speed; + if (options->DataBits >= 5 && options->DataBits <= 8) ser->io->io_ReadLen = ser->io->io_WriteLen = options->DataBits; + if (options->StopBits == 2) ser->io->io_StopBits = 2; + if (options->Parity == 1) ser->io->io_SerFlags |= SERF_PARTY_ON | SERF_PARTY_ODD; + else if (options->Parity == 2) ser->io->io_SerFlags |= SERF_PARTY_ON; + ser->io->IOSer.io_Command = SDCMD_SETPARAMS; + if (DoIO((struct IORequest *)ser->io) != 0) { + CloseDevice((struct IORequest *)ser->io); + DeleteIORequest((struct IORequest *)ser->io); + DeleteMsgPort(ser->port); + free(ser); + return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); + } + } + + ser->is_open = 1; + *ppvHandle = ser; + return fb_ErrorSetNum(FB_RTERROR_OK); +} + +int fb_SerialWrite(FB_FILE *handle, void *pvHandle, const void *data, size_t length) { + AMIGA_SERIAL *ser = (AMIGA_SERIAL *)pvHandle; + if (!ser || !ser->is_open) return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); + + ser->io->IOSer.io_Command = CMD_WRITE; + ser->io->IOSer.io_Data = (APTR)data; + ser->io->IOSer.io_Length = length; + if (DoIO((struct IORequest *)ser->io) != 0) + return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); + return fb_ErrorSetNum(FB_RTERROR_OK); +} + +int fb_SerialRead(FB_FILE *handle, void *pvHandle, void *data, size_t *pLength) { + AMIGA_SERIAL *ser = (AMIGA_SERIAL *)pvHandle; + if (!ser || !ser->is_open) return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); + + ser->io->IOSer.io_Command = CMD_READ; + ser->io->IOSer.io_Data = data; + ser->io->IOSer.io_Length = *pLength; + if (DoIO((struct IORequest *)ser->io) != 0) + return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); + *pLength = ser->io->IOSer.io_Actual; + return fb_ErrorSetNum(FB_RTERROR_OK); +} + +int fb_SerialGetRemaining(FB_FILE *handle, void *pvHandle, fb_off_t *pLength) { + AMIGA_SERIAL *ser = (AMIGA_SERIAL *)pvHandle; + if (!ser || !ser->is_open) { *pLength = 0; return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); } + + ser->io->IOSer.io_Command = SDCMD_QUERY; + if (DoIO((struct IORequest *)ser->io) != 0) { + *pLength = 0; + return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); + } + *pLength = ser->io->IOSer.io_Actual; + return fb_ErrorSetNum(FB_RTERROR_OK); +} + +int fb_SerialClose(FB_FILE *handle, void *pvHandle) { + AMIGA_SERIAL *ser = (AMIGA_SERIAL *)pvHandle; + if (!ser) return fb_ErrorSetNum(FB_RTERROR_OK); + + if (ser->is_open) { + CloseDevice((struct IORequest *)ser->io); + ser->is_open = 0; + } + DeleteIORequest((struct IORequest *)ser->io); + DeleteMsgPort(ser->port); + free(ser); + return fb_ErrorSetNum(FB_RTERROR_OK); +} + +#else +/* AROS/MorphOS - stub for now */ +int fb_SerialOpen(FB_FILE *handle, int iPort, FB_SERIAL_OPTIONS *options, + const char *pszDevice, void **ppvHandle) { + return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); +} +int fb_SerialWrite(FB_FILE *handle, void *pvHandle, const void *data, size_t length) { return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); } +int fb_SerialRead(FB_FILE *handle, void *pvHandle, void *data, size_t *pLength) { return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); } +int fb_SerialGetRemaining(FB_FILE *handle, void *pvHandle, fb_off_t *pLength) { return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); } +int fb_SerialClose(FB_FILE *handle, void *pvHandle) { return fb_ErrorSetNum(FB_RTERROR_ILLEGALFUNCTIONCALL); } +#endif diff --git a/src/rtlib/amiga/startup.s b/src/rtlib/amiga/startup.s new file mode 100644 index 000000000..7b4dcf439 --- /dev/null +++ b/src/rtlib/amiga/startup.s @@ -0,0 +1,94 @@ +| FreeBASIC AmigaOS startup - opens dos.library, calls _main, cleans up + .text + .globl _start +_start: + move.l a0,_cmdline | Save command line pointer + move.l d0,_cmdlen | Save command line length + move.l 4.w,a6 + move.l a6,_SysBase + + lea dosname(pc),a1 + moveq #36,d0 + jsr -552(a6) + move.l d0,_DOSBase + beq.s .fail + + | Run global constructors + jsr ___fb_ctor_run + + | Call main(argc, argv) + | FreeBASIC runtime reads _cmdline/_cmdlen directly via fb_hParseArgs, + | so we pass argc=0, argv=NULL here. The runtime handles arg parsing. + pea 0 + pea 0 + jsr _main + addq.l #8,sp + move.l d0,d2 + + | Run global destructors + jsr ___fb_dtor_run + + | CloseLibrary(DOSBase) + move.l _SysBase,a6 + move.l _DOSBase,a1 + jsr -414(a6) + + move.l d2,d0 + rts + +.fail: + moveq #20,d0 + rts + +dosname: .asciz "dos.library" + .align 2 + + .globl _SysBase + .globl _DOSBase + .globl _LocaleBase + .bss +_SysBase: .long 0 +_cmdline: .long 0 +_cmdlen: .long 0 + .globl _cmdline + .globl _cmdlen +_DOSBase: .long 0 +_LocaleBase: .long 0 +| __impure_ptr: newlib reentrancy pointer. Initialized by crt0 if using newlib. +| Set to 0 here; newlib's startup will override if linked. +__impure_ptr: .long 0 + .globl __impure_ptr + +| Constructor/destructor list runners +| GCC puts function pointers in .ctors/.dtors sections + .weak ___CTOR_LIST__ + .weak ___CTOR_END__ + .weak ___DTOR_LIST__ + .weak ___DTOR_END__ + + .text + .globl ___fb_ctor_run +___fb_ctor_run: + lea ___CTOR_LIST__,a0 + lea ___CTOR_END__,a1 + bra.s .ctor_check +.ctor_loop: + move.l (a0)+,a2 + jsr (a2) +.ctor_check: + cmp.l a0,a1 + bne.s .ctor_loop + rts + + .globl ___fb_dtor_run +___fb_dtor_run: + lea ___DTOR_LIST__,a0 + lea ___DTOR_END__,a1 + bra.s .dtor_check +.dtor_loop: + move.l (a0)+,a2 + jsr (a2) +.dtor_check: + cmp.l a0,a1 + bne.s .dtor_loop + rts diff --git a/src/rtlib/amiga/sys_fmem.c b/src/rtlib/amiga/sys_fmem.c new file mode 100644 index 000000000..9d17b3727 --- /dev/null +++ b/src/rtlib/amiga/sys_fmem.c @@ -0,0 +1,17 @@ +#include "../fb.h" + +#ifdef HOST_AMIGAOS +#include +extern struct ExecBase *SysBase; +#endif + +FBCALL size_t fb_GetMemAvail(int mode) { +#ifdef HOST_AMIGAOS + /* FB FRE: mode 0 = largest free block, mode 2 = total free memory */ + if (mode == 0 || mode == 1) + return (size_t)AvailMem(MEMF_ANY | MEMF_LARGEST); + return (size_t)AvailMem(MEMF_ANY); /* total free */ +#else + return 0; +#endif +} diff --git a/src/rtlib/amiga/sys_getexename.c b/src/rtlib/amiga/sys_getexename.c new file mode 100644 index 000000000..b76bb700b --- /dev/null +++ b/src/rtlib/amiga/sys_getexename.c @@ -0,0 +1,19 @@ +#include "../fb.h" +#include + +#if defined(HOST_AMIGAOS) +#include +extern struct DosLibrary *DOSBase; +#endif + +char *fb_hGetExeName(char *dst, ssize_t maxlen) { + if (maxlen <= 0) return dst; +#if defined(HOST_AMIGAOS) + if (!GetProgramName(dst, maxlen)) { + dst[0] = '\0'; + } +#else + dst[0] = '\0'; +#endif + return dst; +} diff --git a/src/rtlib/amiga/sys_getexepath.c b/src/rtlib/amiga/sys_getexepath.c new file mode 100644 index 000000000..774f8a5cc --- /dev/null +++ b/src/rtlib/amiga/sys_getexepath.c @@ -0,0 +1,31 @@ +#include "../fb.h" +#include + +#if defined(HOST_AMIGAOS) +#include +extern struct DosLibrary *DOSBase; +#endif + +char *fb_hGetExePath(char *dst, ssize_t maxlen) { + if (maxlen <= 0) return dst; +#if defined(HOST_AMIGAOS) + /* Use GetProgramDir() + NameFromLock() */ + BPTR lock = GetProgramDir(); + if (lock) { + if (!NameFromLock(lock, dst, maxlen)) + dst[0] = '\0'; + } else { + if (maxlen > 1) { + strncpy(dst, "PROGDIR:", maxlen - 1); + dst[maxlen - 1] = '\0'; + } else { + dst[0] = '\0'; + } + } +#else + /* AROS/MorphOS have getcwd or similar */ + strncpy(dst, "PROGDIR:", maxlen - 1); + dst[maxlen - 1] = '\0'; +#endif + return dst; +} diff --git a/src/rtlib/amiga/thread_stubs.c b/src/rtlib/amiga/thread_stubs.c new file mode 100644 index 000000000..10b0b4d89 --- /dev/null +++ b/src/rtlib/amiga/thread_stubs.c @@ -0,0 +1,37 @@ +/* Threading stubs for AmigaOS + * + * AmigaOS 3.x: No pthreads. Has exec.library CreateTask/CreateProcess + * but they're incompatible with FB's threading model. + * AROS: Has pthreads (uses unix/thread_core.c when built with ENABLE_MT) + * MorphOS: Has pthreads via ixemul + * AmigaOS4: Has pthreads via newlib + * + * When ENABLE_MT is not defined (default for AmigaOS 3.x), these stubs + * provide graceful no-op behavior so programs that reference threading + * functions still link and run (single-threaded). + */ +#include "../fb.h" +#include "../fb_private_thread.h" + +#ifndef ENABLE_MT + +FBCALL FBTHREAD *fb_ThreadCreate(FB_THREADPROC proc, void *param, ssize_t stack_size) { + (void)proc; (void)param; (void)stack_size; + return NULL; /* Cannot create threads */ +} + +FBCALL void fb_ThreadWait(FBTHREAD *thread) { (void)thread; } +FBCALL void fb_ThreadDetach(FBTHREAD *thread) { (void)thread; } + +FBCALL FBMUTEX *fb_MutexCreate(void) { return NULL; } +FBCALL void fb_MutexDestroy(FBMUTEX *mutex) { (void)mutex; } +FBCALL void fb_MutexLock(FBMUTEX *mutex) { (void)mutex; } +FBCALL void fb_MutexUnlock(FBMUTEX *mutex) { (void)mutex; } + +FBCALL FBCOND *fb_CondCreate(void) { return NULL; } +FBCALL void fb_CondDestroy(FBCOND *cond) { (void)cond; } +FBCALL void fb_CondSignal(FBCOND *cond) { (void)cond; } +FBCALL void fb_CondBroadcast(FBCOND *cond) { (void)cond; } +FBCALL void fb_CondWait(FBCOND *cond, FBMUTEX *mutex) { (void)cond; (void)mutex; } + +#endif /* !ENABLE_MT */ diff --git a/src/rtlib/fb.h b/src/rtlib/fb.h index b9eb40944..cd4ccccfb 100644 --- a/src/rtlib/fb.h +++ b/src/rtlib/fb.h @@ -117,6 +117,8 @@ #include "win32/fb_win32.h" #elif defined HOST_XBOX #include "xbox/fb_xbox.h" +#elif defined HOST_AMIGA + #include "amiga/fb_amiga.h" #endif #if defined HOST_SOLARIS diff --git a/src/rtlib/fb_config.h b/src/rtlib/fb_config.h index 6c63d261e..cb43eac7a 100644 --- a/src/rtlib/fb_config.h +++ b/src/rtlib/fb_config.h @@ -46,6 +46,18 @@ #elif defined __EMSCRIPTEN__ #define HOST_JS #define HOST_UNIX +#elif defined(__amigaos4__) + #define HOST_AMIGAOS4 + #define HOST_AMIGA +#elif defined(__AROS__) + #define HOST_AROS + #define HOST_AMIGA +#elif defined(__MORPHOS__) + #define HOST_MORPHOS + #define HOST_AMIGA +#elif defined(AMIGA) || defined(__amigaos__) || defined(__AMIGA__) + #define HOST_AMIGAOS + #define HOST_AMIGA #else #error "Couldn't identify target system!" #endif @@ -55,7 +67,9 @@ #define _FILE_OFFSET_BITS 64 #endif -#if defined __i386__ +#if defined __m68k__ + #define HOST_M68K +#elif defined __i386__ #define HOST_X86 #elif defined __x86_64__ #define HOST_X86_64 diff --git a/src/rtlib/fb_private_thread.h b/src/rtlib/fb_private_thread.h index 73a5b2c7d..3eec1fe9f 100644 --- a/src/rtlib/fb_private_thread.h +++ b/src/rtlib/fb_private_thread.h @@ -62,6 +62,8 @@ struct _FBTHREAD { HANDLE id; #elif defined HOST_XBOX HANDLE id; +#elif defined HOST_AMIGA + int id; #else #error Unexpected target #endif diff --git a/src/rtlib/init.c b/src/rtlib/init.c index 53fc4d257..b0e802f93 100644 --- a/src/rtlib/init.c +++ b/src/rtlib/init.c @@ -24,7 +24,9 @@ void fb_hRtInit( void ) #ifdef ENABLE_MT fb_TlsInit( ); #endif +#ifndef HOST_AMIGA fb_AllocateMainFBThread(); +#endif /** * With the default "C" locale (which is just plain 7-bit ASCII), @@ -110,10 +112,10 @@ FBCALL void fb_Init( int argc, char **argv, int lang ) __fb_ctx.argv = argv; __fb_ctx.lang = lang; -#ifdef HOST_JS - // global constructors and destructors are not supported by emscripten - fb_hRtInit(); -#endif // HOST_JS +#if defined(HOST_JS) || defined(HOST_AMIGA) + /* No CRT constructor support - call init/exit directly */ + fb_hRtInit(); +#endif } /* called by FB program, @@ -123,10 +125,9 @@ FBCALL void fb_End( int errlevel ) if( __fb_ctx.exit_gfxlib2 ) __fb_ctx.exit_gfxlib2( ); -#ifdef HOST_JS - // global constructors and destructors are not supported by emscripten - fb_hRtExit(); -#endif // HOST_JS +#if defined(HOST_JS) || defined(HOST_AMIGA) + fb_hRtExit(); +#endif exit( errlevel ); } diff --git a/src/rtlib/profile_cycles.c b/src/rtlib/profile_cycles.c index c6e2a7327..87305b462 100644 --- a/src/rtlib/profile_cycles.c +++ b/src/rtlib/profile_cycles.c @@ -31,9 +31,11 @@ #endif #endif -/* profile section data */ +/* profile section data (ELF section boundary symbols, not available on Mach-O) */ +#if !defined(HOST_DARWIN) extern char __start_fb_profilecycledata[]; extern char __stop_fb_profilecycledata; +#endif /* profiler record ids - these indicate what the record is */ enum FB_PROFILE_REDORD_ID @@ -97,12 +99,18 @@ typedef struct _FB_PROFILER_CYCLES /* FIXME: creating a library with other sections causes dxe3gen to fail ** when building the DXE dynamic link library support for DOS */ -#if !defined(HOST_DOS) +#if !defined(HOST_DOS) && !defined(HOST_DARWIN) +/* Cycle profiling uses ELF __start_/__stop_ section boundary symbols + which are not available on Mach-O (Darwin). */ /* make sure there is at least one record in the profile data section */ static FB_PROFILE_RECORD_VERSION __attribute__ ((aligned (16))) prof_data_version +#if defined(HOST_DARWIN) +__attribute__((section("__DATA,fb_profcycdat"), used)) = +#else __attribute__((section("fb_profilecycledata"), used)) = +#endif { sizeof( FB_PROFILE_RECORD_VERSION ), FB_PROFILE_RECORD_VERSION_ID, @@ -301,8 +309,13 @@ static void hProfilerWriteReport( FB_PROFILER_CYCLES *prof ) fprintf( f, "Total program execution time: %5.4g seconds\n", fb_Timer() - prof->start_time ); } +#if !defined(HOST_DARWIN) data = (unsigned char *)&__start_fb_profilecycledata[0]; length = (ssize_t)&__stop_fb_profilecycledata - (ssize_t)&__start_fb_profilecycledata[0]; +#else + data = NULL; + length = 0; +#endif count = hProfilerCountProcs( data, length ); if( count ) { diff --git a/src/rtlib/signals.c b/src/rtlib/signals.c index e5144f87f..d9d73e752 100644 --- a/src/rtlib/signals.c +++ b/src/rtlib/signals.c @@ -1,7 +1,7 @@ /* signal handlers */ // Emscripten doesn't have signals -#ifndef HOST_JS +#if !defined(HOST_JS) && !defined(HOST_AMIGA) #include "fb.h" #include @@ -85,3 +85,7 @@ FBCALL void fb_InitSignals( void ) } #endif + +#if defined(HOST_AMIGA) +FBCALL void fb_InitSignals(void) { } +#endif