BartmanAbyss / vscode-amiga-debug

One-stop Visual Studio Code Extension to compile, debug and profile Amiga C/C++ programs compiled by the bundled gcc 12.2 with the bundled WinUAE/FS-UAE.
GNU General Public License v3.0
315 stars 39 forks source link

What is the bare minimum needed? #47

Closed nyteshade closed 3 years ago

nyteshade commented 3 years ago

So I am trying to pare down the minimal required code for an application to work well with the system. The supplied main.c is impressive but it's also got a lot going on. If I wanted to be a good Amiga citizen and follow the rules, and print hello world in the console, I was thinking it would look something like this.

#include "support/gcc8_c_support.h"

#include <exec/exec.h>

#include <dos/dos.h>
#include <dos/stdio.h>

#include <proto/dos.h>
#include <proto/exec.h>

volatile struct Custom *custom;
struct ExecBase *SysBase;
struct DosLibrary *DOSBase;

int main(int argc, char **argv) {
  SysBase = *((struct ExecBase**)4UL);
  custom = (struct Custom*)0xdff000;

  FPrintf(Output(), "Hello World\n");

  return 0;
}

From what I can tell we need to have the gcc8_c_support stuff, but what else is required? What is the struct Custom *custom bit? I haven't seen that before. This program, by the way, crashes when run in the emulator. Do you know why?

error

nyteshade commented 3 years ago

So this compiles without error

#include "support/gcc8_c_support.h"

#include <exec/exec.h>

#include <dos/dos.h>
#include <dos/stdio.h>

#include <proto/dos.h>
#include <proto/exec.h>

volatile struct Custom *custom;
struct ExecBase *SysBase;
struct DosLibrary *DOSBase;

int main(int argc, char **argv) {
  STRPTR message = "Hello World\n";

  SysBase = *((struct ExecBase**)4UL);
  custom = (struct Custom*)0xdff000;

  FPrintf(Output(), message);

  return 0;
}

But still GURUs upon running. However, this program builds and runs fine on an Amiga using GCC 2.95.3. What am I missing here?

#include <exec/exec.h>

#include <dos/dos.h>
#include <dos/stdio.h>

#include <proto/dos.h>
#include <proto/exec.h>

int main(int argc, char **argv) {
  STRPTR message = "Hello World\n";
  FPrintf(Output(), message);
  return 0;
}
nyteshade commented 3 years ago

As an aside, if support/gcc8_c_support.h is required and we have to define

volatile struct Custom *custom;
struct ExecBase *SysBase;

...and we have to place this at the top of main in all our applications...

SysBase = *((struct ExecBase**)4UL);
custom = (struct Custom*)0xdff000;

Then it might make sense to have something like the following

#include "support/gcc8_c_support.h"

// your includes

/* Required definitions */
GCC8_C_DEFINITIONS       // <- A bit that defines custom and SysBase

// your code

int main(int argc, char **argv) {
  InitializeGCC8Support();  // Assign constant values to SysBase and custom for those who can't remember

  // your code
}

With these snippets in comments in the default gcc8_c_support.h file. Thoughts?

BartmanAbyss commented 3 years ago

Hi, I do not know where you get FPrintF from.

Take a look at this minimal code that works to print out text. SysBase is required for OpenLibrary to work (that's from exec.library) DOSBase is required for Write, Output (they're from dos.library)

Technically, you could reduce gcc8_c_support.h to only memcpy, memset, memmove, as these are sometimes used by the C compiler even if you don't call them explicitly. Everything else in there is just convenience.

Also, please do remember that this extension does not support the C standard library, so you don't get argc, argv into your main, also you can't use stuff like printf, std::cout, etc.

#include "support/gcc8_c_support.h"
#include <proto/exec.h>
#include <proto/dos.h>

struct ExecBase *SysBase;
struct DosLibrary *DOSBase;

int main() {
    SysBase = *((struct ExecBase**)4UL);

    DOSBase = (struct DosLibrary*)OpenLibrary((CONST_STRPTR)"dos.library", 0);
    if (!DOSBase)
        Exit(0);

    Write(Output(), (APTR)"Hello console!\n", 15);
    Delay(50);

    CloseLibrary((struct Library*)DOSBase);
    return 0;
}
nyteshade commented 3 years ago

The include #include <dos/stdio.h> is an Amiga header that uses Amiga library calls to simulate things like printf. The call FPrintf(), lowercase f on the end, relies on BPTR and STRPTR style exec types and is entirely Amiga.

@BartmanAbyss

nyteshade commented 3 years ago

Also, please do remember that this extension does not support the C standard library, so you don't get argc, argv into your main, also you can't use stuff like printf, std::cout, etc.

This raises so many questions. So how does one get input from the user with program written here? I don't know assembly, but I assume there is a way to get those values using assembly.

Also, how do I prevent the emulation environment from force quitting after the application ends? I want to play around with the environment there a bit?

nyteshade commented 3 years ago

Also shouldn't this program work without the inline versions of the functions present? I notice that if I define _NO_INLINE at the top, even your base example fails. The <proto/dos.h> and <proto/exec.h> should include <clib/dos_protos.h> and <dos/exec_protos.h> by default but even with those the program fails to link. Is this because of not being in an Amiga environment?

Compiling main.c
Linking a.mingw.elf
c:/users/nytes/.vscode/extensions/bartmanabyss.amiga-debug-1.1.0-preview32/bin/opt/bin/../lib/gcc/m68k-amiga-elf/10.1.0/../../../../m68k-amiga-elf/bin/ld.exe: obj/main.o: in function `main':
C:/Users/nytes/Desktop/t/main.c:31: undefined reference to `OpenLibrary'
c:/users/nytes/.vscode/extensions/bartmanabyss.amiga-debug-1.1.0-preview32/bin/opt/bin/../lib/gcc/m68k-amiga-elf/10.1.0/../../../../m68k-amiga-elf/bin/ld.exe: C:/Users/nytes/Desktop/t/main.c:35: undefined reference to `Output'
c:/users/nytes/.vscode/extensions/bartmanabyss.amiga-debug-1.1.0-preview32/bin/opt/bin/../lib/gcc/m68k-amiga-elf/10.1.0/../../../../m68k-amiga-elf/bin/ld.exe: C:/Users/nytes/Desktop/t/main.c:35: undefined reference to `Write'
c:/users/nytes/.vscode/extensions/bartmanabyss.amiga-debug-1.1.0-preview32/bin/opt/bin/../lib/gcc/m68k-amiga-elf/10.1.0/../../../../m68k-amiga-elf/bin/ld.exe: C:/Users/nytes/Desktop/t/main.c:41: undefined reference to `Delay'
c:/users/nytes/.vscode/extensions/bartmanabyss.amiga-debug-1.1.0-preview32/bin/opt/bin/../lib/gcc/m68k-amiga-elf/10.1.0/../../../../m68k-amiga-elf/bin/ld.exe: C:/Users/nytes/Desktop/t/main.c:50: undefined reference to `CloseLibrary'
c:/users/nytes/.vscode/extensions/bartmanabyss.amiga-debug-1.1.0-preview32/bin/opt/bin/../lib/gcc/m68k-amiga-elf/10.1.0/../../../../m68k-amiga-elf/bin/ld.exe: C:/Users/nytes/Desktop/t/main.c:33: undefined reference to `Exit'
c:/users/nytes/.vscode/extensions/bartmanabyss.amiga-debug-1.1.0-preview32/bin/opt/bin/../lib/gcc/m68k-amiga-elf/10.1.0/../../../../m68k-amiga-elf/bin/ld.exe: C:/Users/nytes/Desktop/t/main.c:35: undefined reference to `Output'
c:/users/nytes/.vscode/extensions/bartmanabyss.amiga-debug-1.1.0-preview32/bin/opt/bin/../lib/gcc/m68k-amiga-elf/10.1.0/../../../../m68k-amiga-elf/bin/ld.exe: C:/Users/nytes/Desktop/t/main.c:35: undefined reference to `Write'
c:/users/nytes/.vscode/extensions/bartmanabyss.amiga-debug-1.1.0-preview32/bin/opt/bin/../lib/gcc/m68k-amiga-elf/10.1.0/../../../../m68k-amiga-elf/bin/ld.exe: C:/Users/nytes/Desktop/t/main.c:41: undefined reference to `Delay'
c:/users/nytes/.vscode/extensions/bartmanabyss.amiga-debug-1.1.0-preview32/bin/opt/bin/../lib/gcc/m68k-amiga-elf/10.1.0/../../../../m68k-amiga-elf/bin/ld.exe: C:/Users/nytes/Desktop/t/main.c:50: undefined reference to `CloseLibrary'
collect2.exe: error: ld returned 1 exit status
nyteshade commented 3 years ago

In fact I've always wondered how the Amiga shared libraries (*.library) work for linking. With something like StormC, SAS/C, GCC 2.95.3 or other native Amiga C compilers, how do they link the binary when there isn't a fixed library file there to reference?

BartmanAbyss commented 3 years ago

The include #include <dos/stdio.h> is an Amiga header that uses Amiga library calls to simulate things like printf. The call FPrintf(), lowercase f on the end, relies on BPTR and STRPTR style exec types and is entirely Amiga.

Ah ok, I see it's defined in inline/dos.h, however it may be because this is a function that uses vargs that it itsn't supported. I will test and let you know.

BartmanAbyss commented 3 years ago

Also shouldn't this program work without the inline versions of the functions present? I notice that if I define _NO_INLINE at the top, even your base example fails. The <proto/dos.h> and <proto/exec.h> should include <clib/dos_protos.h> and <dos/exec_protos.h> by default but even with those the program fails to link. Is this because of not being in an Amiga environment?

_NO_INLINE is supported with gcc (it only works with linker libraries from old amiga compilers). The amiga system includes come with support for various compilers, and gcc only supports the inline/* variant.

BartmanAbyss commented 3 years ago

In fact I've always wondered how the Amiga shared libraries (*.library) work for linking. With something like StormC, SAS/C, GCC 2.95.3 or other native Amiga C compilers, how do they link the binary when there isn't a fixed library file there to reference?

The .library file is loaded when you execute a LoadLibrary call. Amiga libraries are a bit different than what you're probably used to on Linux or Windows. Essentially LoadLibrary just returns a pointer (like a vtbl), and all functions you call on the loaded library just call into an offset of that pointer. So there's no C-style .lib file involved during linking.

BartmanAbyss commented 3 years ago

This raises so many questions. So how does one get input from the user with program written here? I don't know assembly, but I assume there is a way to get those values using assembly.

No need to do assembly. You can call all AmigaOS functions from C. See here

Also, how do I prevent the emulation environment from force quitting after the application ends? I want to play around with the environment there a bit?

I'm not sure, I tried that minimal sample I posted above, and it just returned to the CLI and sat there. Nothing crashed.

BartmanAbyss commented 3 years ago

The include #include <dos/stdio.h> is an Amiga header that uses Amiga library calls to simulate things like printf. The call FPrintf(), lowercase f on the end, relies on BPTR and STRPTR style exec types and is entirely Amiga.

Ah ok, I see it's defined in inline/dos.h, however it may be because this is a function that uses vargs that it itsn't supported. I will test and let you know.

So, I just tried it and it turns out that VPrintf is not supported on Kickstart 1.3, see the (V36) in the link above? V36 is Kickstart 2.0 (see here. It works fine with an A1200-config and Kick 3.1

penarthur commented 3 years ago

How can I add references to the math library etc to the linker?

I keep getting undefined reference to `__mulsf3' in the Linking a/mingw.elf stage, have tried adding -lm almost wherever I can?

BartmanAbyss commented 3 years ago

Math library is not supported. Also, 68000 has no FPU. But you can use amiga IEEE libraries (they emulate float)