Skip to content

Tooling 01 - #302

Draft
andrewbird wants to merge 2 commits into
dosemu2:masterfrom
andrewbird:tooling-01
Draft

Tooling 01#302
andrewbird wants to merge 2 commits into
dosemu2:masterfrom
andrewbird:tooling-01

Conversation

@andrewbird

Copy link
Copy Markdown
Member

So here's a first stab at converting the C source to use std printfs (or the recently reimplemented %P) without any GET_PTR / GET FP32 wrappers, to C++ with the wrappers applied during the build process. The bulk of the python work was AI generated, but I've since worked on it a bit both to make it work in the FDPP environment, and to get to know the code better.

I added a single line change to the makefile easily, but meson had to use an intermediate file (pipes didn't work if you wanted access to variables). Anyway it's functional but the stderr info lines are discarded unless the tool actually exits with error. Obviously I don't know meson at all, I'll continue avoiding it as much as possible, it may be easy to fix I don't know.

The second commit rolls back the changes that would have been necessary to get FreeDOS to work in the new object FAR ptr world of FDPP. This might not be wholly desirable, I don't know, it certainly makes the C code look less cluttered and it was good to test the script.

Unfortunately the build scripts for CI use meson, so you won't see the info in the build log

@andrewbird

Copy link
Copy Markdown
Member Author

@stsp as this a draft you probably didn't get a notification.

@stsp

stsp commented Jul 12, 2026

Copy link
Copy Markdown
Member

Why the script that removes
GET_PTR should be in the repo?
I would suggest to run that script
only once and forget. And then use
clang-tidy just for printf conversion.

Comment thread kernel/task.c

if (termNoComcom) {
fdloudprintf("Bad or missing Command Interpreter: %s\n", GET_PTR(Shell));
fdloudprintf("Bad or missing Command Interpreter: %S\n", Shell);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think adding new formats
like 'S' or whatever, is a good idea.
'%P' is enough for additions.

@andrewbird

Copy link
Copy Markdown
Member Author

I think I must have misunderstood the goal here. I thought the goal was to remove all of the C++isms from the C file so it could look like the C FreeDOS original.

Why the script that removes
GET_PTR should be in the repo?

It doesn't, it adds them where necessary in the conversion to C++, just like you did in mkfar.sh turning
struct sfttbl FAR *sp; into __FAR(struct sfttbl)sp;

I would suggest to run that script
only once and forget.

Then there's nothing to do, no need for a script, just turn on all the debug ifdefs see what doesn't compile and add GET_PTR() or GET_FP32 as required and modify the format string as necessary. That's essentially what's occurred already, just need to finish all the conversion on the currently disabled code.

I don't think adding new formats
like 'S' or whatever, is a good idea.
'%P' is enough for additions.

It was used in the FreeDOS kernel for far strings,
kernel/task.c: ProcDbgPrintf(("DosExeLoader. Loading '%S' at %04x\n", namep, mem));

If I don't have a distinct format specifier and use '%P' for both, how does the converter know whether the intent was to print the pointer address GET_FP32() or the string content itself GET_PTR()? The '%S' is never compiled, it becomes %s in the C++.

Anyway it looks like I got the wrong idea, an interesting investigation, but ultimately unnecessary.

@stsp

stsp commented Jul 12, 2026

Copy link
Copy Markdown
Member

I think I must have misunderstood the goal here. I thought the goal was to remove all of the
C++isms from the C file so it could look like the C FreeDOS original.

True, but not that way.
Simply returning GET_PTR() and
friends dynamically - is not an
improvement, eg it doesn't fix
the problem with gcc format
warnings. Its basically an unnecessary
change.
What I suggest is entirely different
approach, that actually makes us
more portable and gcc-friendly.
Here's the idea:

  • you remove GET_PTR() and make
    things look like freedos
  • you use clang-tidy to convert the
    format strings to modern C++,
    thus making va_args unneeded

This is light-years better than just
remove macros and get them back
by some script.

@andrewbird

Copy link
Copy Markdown
Member Author

I had a poke about with clang-tidy, but couldn't see if you can convert files to a new format, it suggested instead that you need to run it as part of the compilation with all the same flags etc. But assuming that can be figured out how would C++ new printing method deal with a FAR char *, how would it know whether you wanted to print the address or the string it pointed to?

@stsp

stsp commented Jul 12, 2026

Copy link
Copy Markdown
Member

instead that you need to run it as part of the compilation with all the same flags etc.

If so - you need to ask AI for
another script. :) Transforming
printfs to C++ is not very difficult.

how would C++ new printing method deal with a FAR char *, how would it know whether you
wanted to print the address or the string it pointed to?

Of course by looking into the format
specifiers, like %s, and replacing them
with C++ counterparts.

@andrewbird

Copy link
Copy Markdown
Member Author

I just asked Google to transform a pointer 'printf("format %p\n", p) into c++ formatting printing,
It gave me choices.

#include <print>

std::print("format {}\n", static_cast<const void*>(p));

or

#include <fmt/core.h>

// Automatically handles the void* conversion for any pointer type
fmt::print("format {}\n", fmt::ptr(p));

or

#include <fmt/printf.h>

// Works without a cast, using your exact original C format string!
fmt::printf("format %p\n", p);

do any of those look any good?

@stsp

stsp commented Jul 12, 2026

Copy link
Copy Markdown
Member

Nope.
std::print("Pointer: {:p}\n", ptr); // C++26

@andrewbird

Copy link
Copy Markdown
Member Author

so I added some test code to try out std formatting. I added a custom formatter to FarPtr

// Specialised std::formatter for FarPtr
template<typename T>
struct std::formatter<FarPtr<T>> {
    bool print_address = false;

    constexpr auto parse(std::format_parse_context& ctx) {
        auto it = ctx.begin();
        if (it != ctx.end() && *it == 'p') {
            print_address = true;
            ++it;
        }
        return it;
    }

    auto format(const FarPtr<T>& obj, std::format_context& ctx) const {
        if (print_address)
            return std::format_to(ctx.out(), "{:p}", &obj);
        else
            return std::format_to(ctx.out(), "{:04x}:{:04x}", obj.seg, obj.off);
    }
};

However when I try to test it with

void FAR *ptr = MK_FP(0x0000, 0x0040);

FDLOGSTDPRINT("Default: {0}, Address: {0:p}\n", ptr, ptr);
FDLOGSTDPRINT("Type: {}\n", typeid(ptr).name());

The type id is "Pv" a plain void pointer, so my formatter doesn't fire and I just print the address twice. What I am I missing? Is FarPtr the correct thing to have the custom formatter on?

@stsp

stsp commented Jul 15, 2026

Copy link
Copy Markdown
Member

Could you please provide a patch
(in git) for me to experiment? I think
the problem is the implicit conversion
operator that takes precedence over
formatter. But there might be some
magic words in C++ to change that.

@andrewbird

Copy link
Copy Markdown
Member Author

Okay, it's very rough, will need to tidy a little first.

@andrewbird

Copy link
Copy Markdown
Member Author

top two commits here https://github.com/andrewbird/fdpp/tree/printf-03

@stsp

stsp commented Jul 15, 2026

Copy link
Copy Markdown
Member

Ok, you assumed you can use
FAR * in fdpp, but you can't.
Move your experiment to
kernel/main.c to start with.
Let me know what misbehaves
next.

@andrewbird

Copy link
Copy Markdown
Member Author

I tried this, but I'm getting in a pickle with headers/templates etc. Will have to try later...

ajb@calypso:/clients/common/fdpp.git$ git diff
diff --git a/fdpp/thunks.cc b/fdpp/thunks.cc
index fda2aeb4..b91ff7d2 100644
--- a/fdpp/thunks.cc
+++ b/fdpp/thunks.cc
@@ -515,10 +515,6 @@ void RelocHook(UWORD old_seg, UWORD new_seg, UWORD offs, UDWORD len)
     uint8_t *start_p = (uint8_t *)so2lin(old_seg, offs);
     uint8_t *end_p = (uint8_t *)so2lin(old_seg + (len >> 4), (len & 0xf) + offs);
     uint16_t delta = new_seg - old_seg;
-    void FAR *tptr = MK_FP(0x0000, 0x0040);
-
-    FDLOGSTDPRINT("Default: {0}, Address: {0:p}\n", tptr, tptr);
-    FDLOGSTDPRINT("Type: {}\n", typeid(tptr).name());
 
 //    fdlogprintf("relocate %hx --> %hx:%hx, %x\n", old_seg, new_seg, offs, len);
     FDLOGSTDPRINT("relocate {:04x} --> {:04x}:{:04x}, {:x}\n", old_seg, new_seg, offs, len);
diff --git a/kernel/main.c b/kernel/main.c
index cb5b4d0c..d02c68ba 100644
--- a/kernel/main.c
+++ b/kernel/main.c
@@ -33,6 +33,7 @@
 #include "dyndata.h"
 #include "dosobj.h"
 #include "debug.h"
+#include "../fdpp/stdprint.hpp"
 
 #ifdef VERSION_STRINGS
 static const char *mainRcsId =
@@ -78,6 +79,8 @@ ASMREF(struct lol) LoL = __ASMADDR(DATASTART);
 struct _bprm bprm;
 #define TEXT_SIZE (_InitTextEnd - _HMATextStart)
 seg DOS_PSP;
+#define FDPP_PRINT_LOG 0
+#define FDLOGSTDPRINT(format, ...) fdstdprint(FDPP_PRINT_LOG, format, ##__VA_ARGS__)
 
 VOID ASMCFUNC FreeDOSmain(void)
 {
@@ -86,6 +89,11 @@ VOID ASMCFUNC FreeDOSmain(void)
   UWORD FAR *BootParamVer;
   struct _bprm FAR *b;
 
+  void FAR *tptr = MK_FP(0x0000, 0x0040);
+
+  FDLOGSTDPRINT("Default: {0}, Address: {0:p}\n", tptr, tptr);
+  FDLOGSTDPRINT("Type: {}\n", typeid(tptr).name());
+
 #ifdef _MSC_VER
   extern FAR prn_dev;
   DosDataSeg = (__segment) & DATASTART;

@stsp

stsp commented Jul 15, 2026

Copy link
Copy Markdown
Member

If you scroll upwards a lot, you'll
see something like std::formatter must be specialized for each type being formatted

@andrewbird

Copy link
Copy Markdown
Member Author

see something like std::formatter must be specialized for each type being formatted

yes, that was there. It turned out I had a couple of problems with my formatter
1/ I was using obj.seg, should have been using obj.seg()
2/ {:p} doesn't seem to be recognised by clang++ 18, even with -std=c++26 (that was was giving the above error)

I updated my test branch here https://github.com/andrewbird/fdpp/tree/printf-03

And the log it produces

fdpp: dispatch FreeDOSmain
fdpp: Type: 6FarPtrIvE
fdpp: Default: 0000:0040
fdpp: Address: 0x794cef5ffce8
fdpp: Default: 0000:0040, Address: 0x794cef5ffcc8
fdpp: relocate f910 --> 9d36:2700, 390

So I probably need to move some things around, is including "../fdpp/stdprint.hpp" the right thing to do in each c file?

@stsp

stsp commented Jul 15, 2026

Copy link
Copy Markdown
Member

So I probably need to move some things around, is including "../fdpp/stdprint.hpp" the right thing
to do in each c file?

No, why do you meed this?
I suggest to move formatters
directly to farptr.hpp, where
the relevant classes are defined,
or create another header for
formatters, but not clutter
stdprint.hpp.

@andrewbird

Copy link
Copy Markdown
Member Author

Formatters are already in farptr.hpp. I created one called fdstdprint.hpp for just this

void strvltodosemu(int prio, const char *format, ...);

template<typename... Args>
void fdstdprint(int prio, const std::string& format, Args... args)
{
    std::string s = std::vformat(format, std::make_format_args(args...));
    const char *cs = s.c_str();

    strvltodosemu(prio, "%s", cs);
}

Initially I had it in thunks.cc alongside fdvprintf() but compiler said it needed to be in a header and included in each translation unit it's used in as it's a templated function. It certainly doesn't feel right what I have at the moment, but as you know I'm only just starting out with c++ and templates, so I'm feeling my way.

@stsp

stsp commented Jul 15, 2026

Copy link
Copy Markdown
Member

Its certainly not right!
Pass "cs" directly, w/o any "%s",
to the existing printing fn.

@andrewbird

Copy link
Copy Markdown
Member Author

I'm still working on this quietly :) I have a question, what's the reasoning between some files in fdpp directory being .cc and other .cpp?

ls -1 fdpp/*.{cc,cpp}
fdpp/ctors.cpp
fdpp/dosobj.cc
fdpp/farhlp.cpp
fdpp/objhlp.cpp
fdpp/objlock.cpp
fdpp/objtrace.cpp
fdpp/thunks_a.cc
fdpp/thunks.cc
fdpp/thunks_c.cc
fdpp/thunks_p.cc

@stsp

stsp commented Jul 19, 2026

Copy link
Copy Markdown
Member

This means they are written
in C, but not DOSish C which
would be passed to mkfar.sh
script. The chose of .cc was
voluntary.

@andrewbird

Copy link
Copy Markdown
Member Author

So the FarPtr object wouldn't be usable in .cpp, but would in fdpp/*.cc?

@andrewbird

Copy link
Copy Markdown
Member Author

Or rather FAR * wouldn't be usable in .cpp, but would in fdpp/*.cc?

@stsp

stsp commented Jul 19, 2026

Copy link
Copy Markdown
Member

No, exactly the opposite.
"DOSish C which would be
passed to mkfar.sh" means
.c in kernel/

@andrewbird

Copy link
Copy Markdown
Member Author

.c in kernel/

Agreed

But what about fdpp/thunks.cc can we use FAR * there?

@stsp

stsp commented Jul 19, 2026 via email

Copy link
Copy Markdown
Member

@andrewbird

Copy link
Copy Markdown
Member Author

No, and why would that be needed?

Not saying it would, and I'm not trying to be difficult, I just want to understand why fdpp/thunks.cc was not fdpp/thunks.cpp

@stsp

stsp commented Jul 19, 2026 via email

Copy link
Copy Markdown
Member

@andrewbird

Copy link
Copy Markdown
Member Author

Cool, got it.

So this change (where fdlogstdprint() takes a format string and an Args...) in thunks.cc is desirable or not?

-   fdlogprintf("purge %p %x\n", ptr, len);
+   fdlogstdprint("purge {:p} {:x}\n", ptr, len);

@stsp

stsp commented Jul 19, 2026

Copy link
Copy Markdown
Member

Not sure its needed, you only
wanted to remove macros from
freedos code I think.
.cc code doesn't warn at formats
AFAIK.

@andrewbird

Copy link
Copy Markdown
Member Author

you only
wanted to remove macros from
freedos code I think.

True, that's how it started, then you introduced me to the idea of variadic type safety :)

.cc code doesn't warn at formats
AFAIK.

I haven't noticed any either.

@stsp

stsp commented Jul 19, 2026

Copy link
Copy Markdown
Member

True, that's how it started, then you introduced me to the idea of variadic type safety :)

So implement it for kernel?

@andrewbird

Copy link
Copy Markdown
Member Author

yes, will do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants