Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

TSE: About a possible 64 bits TSE version

19 views
Skip to first unread message

knud van eeden

unread,
Feb 3, 2025, 5:57:05 AMFeb 3
to SemWare TSE Pro Text Editor
FYIO:

I saw a mentioning of a 64 bit MingW compiler to compile C or C++ programs.

That triggered some more thoughts about 32 bits versus 64 bits and then applying this to TSE.

Now the Borland C++ version 5.5 compiler is 32 bits.
 
Also the Borland C++ version 10.2 compiler is 32 bits.

Then the question might come up, is there maybe a 64 bits successor to these compilers.

Borland and Embarcardero who bought Borland have the C++ Builder as their official product.

That C++ Builder includes (or included) also that C / C++ compiler.

It was long ago pronounced that a 64 bits version of the C / C++ compiler should be released, but
I could not get this confirmed by searching on the Internet about it yesterday.

So I asked the official distributor of C++ Builder if there was a 64 bits version now of this C++ compiler.

The response today was:

"The Embarcadero C++ Builder 12.2 Athens version includes a  new C++ compiler for 64bit Windows. 
There is a version in beta whereby also the IDE is 64bit.
It concerns here about the latest version of C++Builder and not about the Borland C++ Command line compiler."

It might be an option some time to download and install the trial version of Embarcadero C++ Builder and see if it can be used to compile...

I assume at this moment that its in-built C / C++ compiler should be backwards compatible with the original Borland C++ command line compiler version 5.5.

with friendly greetings
Knud van Eeden








S.E. Mitchell

unread,
Feb 3, 2025, 6:45:51 AMFeb 3
to sem...@googlegroups.com
From what I've read, it is based on the Clang compiler.
Thus, it would not help, as that compiler does not support the _pascal
calling convention (maybe the name, but not the semantics)
But, as noted in the read.me, sc32 now compiles and runs with both GNU
C 32-bit and Microsoft C 32-bit.
The editor itself also compiles with both, but almost immediately
crashes (as soon as a call is made from the macro language back into
native code). The case is the FFI, which needs to be rewritten.
Good news is I have a POC that works.
So once we have it working with both of those compilers, then we can
start working on the 64-bit versions of the same.
Big thanks to Rick Hodgkin for help in this.
> --
>
> ---
> You received this message because you are subscribed to the Google Groups "SemWare TSE Pro text editor" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to semware+u...@googlegroups.com.
> To view this discussion visit https://groups.google.com/d/msgid/semware/1001231116.7690783.1738580220847%40mail.yahoo.com.

Rick Hodgin

unread,
Feb 3, 2025, 7:50:11 AMFeb 3
to sem...@googlegroups.com
I had helped Sammy get TSE compiling in Visual Studio / Visual C/C++.  The issue is the internal calling convention.  BCC supports left to right, but most other C/C++ compilers do not.

I've developed a method to work around this issue, but am still debugging it.  I've been sick for three weeks, and have not done any programming on it.  And just this past Thursday I began working on a CPU design.  I've been spending my free time documenting and defining that idea so I can get to a stopping point.

I should be able to pick it back up this week.  It was partly working when I stopped.

My personal ultimate goal for TSE is to make it an Extension for Visual Studio so I can have g32 accessible directly in the IDE.  I have a placeholder version working right now.  I'm going to call it Visual TSE.

-- 
Rick C. Hodgin

knud van eeden

unread,
Feb 3, 2025, 9:23:01 AMFeb 3
to sem...@googlegroups.com
After asking for more details by me today, this answer was received from the official Embarcadero account:

"
It is correct that the current versions of C++ Builder use the CLang compiler.

It offers support for C++ 17 and uses the Dinkumware STL.

The backwards compatibility with the Borland C++ command line compiler is not present anymore in the newest versions of C++ Builder.

The focus is now on the use of the CLang-based compiler in order to be able to profit of the properties of this modern C++ standard.
"

knud van eeden

unread,
Feb 3, 2025, 9:49:21 AMFeb 3
to sem...@googlegroups.com
[source: DeepSeek version V3]

Conclusion: 

1. Manual reordering or assembly wrappers are required to emulate Pascal behavior.

otherwise maybe:

2. - **Watcom-64 Fork**: An experimental fork exists ([Watcom-64](https://github.com/open-watcom/watcom-64)) to explore 64-bit code generation, but it remains **incomplete** and **not production-ready**. It is not part of the main OpenWatcom project.


===

> Which C / C++ compilers support the Pascal convention?

---

The Pascal calling convention, characterized by left-to-right argument
passing and callee stack cleanup, is supported by specific C/C++
compilers, primarily for legacy or interoperability purposes. Here is a
breakdown:

### **1. Borland Compilers (Turbo C++, C++Builder, Embarcadero)**
- **Support**: Explicit via the `pascal` keyword.
- **Use Case**: Interoperability with Delphi (Object Pascal) code.
- **Example**:

  void pascal MyFunction(int a, int b);

### **2. OpenWatcom C/C++**
- **Support**: Uses the `pascal` keyword.
- **Use Case**: Legacy DOS/Windows development.
- **Example**:

  void pascal MyFunction(int a, int b);

### **3. Older 16-bit Compilers (e.g., MSVC 16-bit, Turbo C for DOS)**
- **Support**: Historically via `__pascal` (16-bit only).
-
**Note**: Modern 32/64-bit MSVC **does not** support `__pascal`; it was
replaced by `__stdcall` (which uses right-to-left argument order).

### **4. GCC and Clang**

- **Support**: No native support.

  Use `__attribute__((stdcall))` for callee cleanup,
  but argument order remains right-to-left (C-style).
  Manual reordering or assembly wrappers are required to emulate Pascal behavior.

### **Key Differences from `stdcall`**
- **Pascal**: Left-to-right arguments, callee cleans stack.
- **`stdcall`**: Right-to-left arguments, callee cleans stack (common in Win32 APIs).

### **When to Use**
- Legacy codebases or interfacing with Pascal/Delphi libraries (e.g., in C++Builder).
- Avoid in modern code; prefer standard conventions like `stdcall` or `cdecl`.

### **Code Example (Borland/OpenWatcom)**

#include <stdio.h>

void pascal MyPascalFunc(int a, int b) {
  printf("a = %d, b = %d\n", a, b);
}

int main() {
  MyPascalFunc(1, 2); // Arguments pushed as 1, 2 (left-to-right).
  return 0;
}

For modern development, use platform-standard conventions (e.g.,
`__stdcall` on Windows).


===

> Does a 64 bits OpenWatcom compiler exist?

As of **July 2024**, there is **no official 64-bit version of
OpenWatcom C/C++**. The OpenWatcom project primarily focuses on
**16/32-bit legacy platforms** (e.g., DOS, Win16, OS/2, and 32-bit
Windows via Win386 extenders). Here is a detailed breakdown:

---

### **1. OpenWatcom's Target Platforms**
- **Officially Supported**:
  - **16-bit**: DOS, Windows 3.x (Win16).
  - **32-bit**: OS/2, Windows NT (via `386` mode with DOS extenders like DOS/4GW).
- **No Native 64-bit Support**: The compiler backend lacks x86_64 code generation, and there are no official plans to add 64-bit targets.

---

### **2. Community Efforts**
- **Watcom-64 Fork**: An experimental fork exists ([Watcom-64](https://github.com/open-watcom/watcom-64)) to explore 64-bit code generation, but it remains **incomplete** and **not production-ready**. It is not part of the main OpenWatcom project.
- **Limitations**: Even if 64-bit support were added, OpenWatcom's runtime libraries (e.g., for I/O, memory management) would need significant updates to work with modern 64-bit OS APIs (Windows/Linux).

---

### **3. Why No 64-bit Support?**
- **Legacy Focus**: OpenWatcom is maintained for retrocomputing and legacy system compatibility (e.g., DOS games, OS/2 applications).
- **Resource Constraints**: Modern 64-bit compiler development requires substantial engineering effort (ABI compliance, register allocation, optimization), which the OpenWatcom community lacks.
- **Modern Alternatives**: Compilers like **GCC**, **Clang**, and **MSVC** dominate 64-bit development, making OpenWatcom's niche largely historical.

---

### **4. Workarounds for 64-bit Development**
If you need a 64-bit compiler:
- **GCC/Clang**: Use `__attribute__((sysv_abi))` or `__vectorcall` for specific calling conventions (though Pascal-style left-to-right argument passing is not natively supported).
- **MSVC**: Use `__stdcall` or `__fastcall` (right-to-left order, callee cleanup).
- **Interfacing with Pascal**: For Delphi/Free Pascal interoperability on 64-bit systems, use `extern "C"` with explicit `cdecl`/`stdcall` conventions and reorder arguments manually.

---

### **5. Pascal Calling Convention in 64-bit Systems**
- **Irrelevant on x86_64**: Modern 64-bit ABIs (e.g., **Microsoft x64** and **System V AMD64**) use **register-based parameter passing** (not stack-based), making Pascal-style conventions obsolete. The first few arguments are passed via registers (RCX, RDX, R8, R9 on Windows; RDI, RSI, RDX, RCX on Linux/macOS).

---

### **Summary**
- OpenWatcom remains a **16/32-bit compiler** for retro platforms.
- For 64-bit development, use **GCC**, **Clang**, or **MSVC**.
- The Pascal calling convention is largely obsolete in 64-bit code due to register-based ABIs.

If you are working with legacy code, OpenWatcom is still useful for DOS/OS/2/Win16 targets. 
For modern systems, stick with mainstream compilers.



knud van eeden

unread,
Feb 3, 2025, 10:04:59 AMFeb 3
to sem...@googlegroups.com
How to write an assembly wrapper for the Pascal convention? 

Writing an assembly wrapper to emulate the **Pascal calling
convention** (left-to-right argument order, callee stack cleanup) is
necessary when interfacing with legacy Pascal code or when your
compiler lacks direct support. Below is a step-by-step guide for
**32-bit x86** assembly, along with explanations for common scenarios.

---

### **Key Concepts**
1. **Pascal Convention**: Arguments are pushed left-to-right, and the callee cleans up the stack.
2. **C/C++ Default (cdecl)**: Arguments are pushed right-to-left, and the caller cleans up the stack.
3. **Wrapper Goal**: Reorder arguments from C's right-to-left to Pascal's left-to-right and ensure proper stack cleanup.

---

### **Example Scenario**
Assume a Pascal function `PascalAdd` that adds two integers:
```pascal
function PascalAdd(a: Integer; b: Integer): Integer; pascal;
```
In C, you want to call it via a wrapper `PascalAddWrapper`:
```c
int PascalAddWrapper(int b, int a); // C code passes args as (b, a)
```

---

### **Assembly Wrapper (32-bit x86 NASM/GAS Syntax)**
Here is how to write the wrapper in assembly:

#### **1. NASM (Windows/Linux)**
```nasm
; For 32-bit targets
section .text
global _PascalAddWrapper  ; Export symbol for C

_PascalAddWrapper:
    ; Prologue
    push ebp
    mov ebp, esp

    ; Reorder arguments:
    ; Original stack (cdecl): [ebp+8] = b, [ebp+12] = a
    ; Push them left-to-right for Pascal: a, b
    push dword [ebp+12]   ; Push 'a' first (leftmost)
    push dword [ebp+8]    ; Push 'b' next

    ; Call the Pascal function (callee cleans up the stack)
    call _PascalAdd

    ; Epilogue
    mov esp, ebp
    pop ebp
    ret                   ; Caller (C code) cleans up original args
```

#### **2. GAS (GCC Assembly Syntax)**
```assembly
.global PascalAddWrapper
PascalAddWrapper:
    push %ebp
    mov %esp, %ebp

    # Push arguments in Pascal order
    push 12(%ebp)   # Push 'a' first
    push 8(%ebp)    # Push 'b' next

    call PascalAdd  # PascalAdd cleans up the stack

    mov %ebp, %esp
    pop %ebp
    ret             # C caller cleans up original args
```

---

### **Explanation**
1. **Stack Layout Before Wrapper Call** (cdecl):
   ```
   ESP -> Return Address
   ESP+4 -> b (first C argument)
   ESP+8 -> a (second C argument)
   ```
2. **Wrapper Action**:
   - Repush arguments in **left-to-right** order (`a` then `b`).
   - Call `PascalAdd`, which removes its own arguments via `ret n` (callee cleanup).
3. **Post-Call Stack**:
   - The wrapper's `ret` returns to the C caller, which cleans up the **original** `b` and `a` (now harmless since the Pascal function already cleaned up its copy).

---

### **Handling Return Values**
If the Pascal function returns a value (e.g., in `EAX` for integers), the wrapper preserves it:
```nasm
_PascalAddWrapper:
    ... ; Same as above
    call _PascalAdd  ; Result is in EAX
    ; EAX is retained; no need for cleanup
    mov esp, ebp
    pop ebp
    ret
```

---

### **Variable Arguments**
The Pascal convention **does not support variadic functions** (e.g., `printf`). Use `cdecl` for variadic calls.

---

### **64-bit Systems (x86_64)**
On 64-bit platforms, **register-based ABIs** (e.g., Microsoft x64/System V) replace stack-based conventions. Pascal-style wrappers are obsolete, but you can reorder registers:
```nasm
; Example: Redirect RCX, RDX (Windows x64)
global PascalAddWrapper
PascalAddWrapper:
    mov r8, rcx   ; Reorder RCX (b) to R8
    mov rcx, rdx  ; Reorder RDX (a) to RCX (leftmost)
    jmp PascalAdd ; Tail call (no stack cleanup needed)
```

---

### **Final Notes**
- **Testing**: Use a debugger to verify stack/register states.
- **Legacy Use**: This is primarily for 32-bit systems or backward compatibility.
- **Performance**: Minimal overhead for small wrappers.

This approach ensures seamless interoperability between C/C++ and Pascal code.


knud van eeden

unread,
Feb 3, 2025, 10:07:28 AMFeb 3
to sem...@googlegroups.com
Write it instead for 64 bits C / C++ compilers only.

For **64-bit C/C++ compilers**, the Pascal calling convention
(left-to-right argument order, callee cleanup) is obsolete because
modern 64-bit ABIs (e.g., **Microsoft x64** on Windows or **System V
AMD64** on Linux/macOS) use **register-based parameter passing** for
efficiency. However, if you need to interface with legacy code or a
custom Pascal-like function expecting left-to-right ordering, you can
write an assembly wrapper to reorder arguments. Below are examples for
both Windows and Linux:

---

### **Scenario**
Assume a hypothetical Pascal function `PascalAdd` that expects two integers in **left-to-right order** (e.g., `a` first, `b` second). The C/C++ code passes them in the standard ABI order (e.g., `b` in the second register). The wrapper reorders the arguments.

---

### **1. Windows x64 (Microsoft ABI)**
**Calling Convention**:
- First 4 integer args: `RCX`, `RDX`, `R8`, `R9` (left-to-right in C/C++ code).
- Callee may use "shadow space" (32 bytes reserved on the stack).

**Assembly Wrapper (MASM Syntax)**:
```asm
; PascalAdd.asm
; Assume PascalAdd expects a in RCX, b in RDX (same as C order here).
; If PascalAdd expects a different order, adjust registers accordingly.

_TEXT segment

; Wrapper for int PascalAddWrapper(int a, int b)
PascalAddWrapper proc
  ; Reorder registers (example: swap RCX and RDX if needed)
  ; mov r10, rcx   ; Save a (if reordering)
  ; mov rcx, rdx   ; Move b to RCX
  ; mov rdx, r10   ; Move a to RDX

  ; No reordering needed if Pascal uses the same registers as C.
  ; Call PascalAdd directly (tail call optimization).
  jmp PascalAdd

PascalAddWrapper endp

_TEXT ends
end
```

**C/C++ Code**:
```cpp
// Declare the wrapper and Pascal function
extern "C" int PascalAddWrapper(int a, int b);
extern "C" int PascalAdd(int a, int b);

int main() {
  int result = PascalAddWrapper(1, 2); // Calls via wrapper
  return 0;
}
```

---

### **2. Linux/macOS (System V AMD64 ABI)**
**Calling Convention**:
- First 6 integer args: `RDI`, `RSI`, `RDX`, `RCX`, `R8`, `R9` (left-to-right in C/C++ code).

**Assembly Wrapper (GAS Syntax)**:
```assembly
# PascalAdd.s
.global PascalAddWrapper

PascalAddWrapper:
  # Reorder registers if Pascal expects a different order (example: swap RDI and RSI)
  # mov %rdi, %r10   # Save a
  # mov %rsi, %rdi   # Move b to RDI
  # mov %r10, %rsi   # Move a to RSI

  # Tail call to PascalAdd (no stack cleanup needed)
  jmp PascalAdd
```

**C/C++ Code**:
```cpp
extern "C" int PascalAddWrapper(int a, int b);
extern "C" int PascalAdd(int a, int b);

int main() {
  int result = PascalAddWrapper(1, 2);
  return 0;
}
```

---

### **Key Notes**
1. **No Stack Cleanup**: In 64-bit code, arguments passed via registers do not require stack cleanup.
2. **Tail Call Optimization**: Use `jmp` instead of `call` to avoid creating a new stack frame.
3. **Register Reordering**: Adjust registers in the wrapper if the Pascal function expects a non-standard order (e.g., swapping `RCX` and `RDX` on Windows).
4. **Shadow Space (Windows)**: If your Pascal function uses shadow space, reserve 32 bytes on the stack before calling it:
   ```asm
   PascalAddWrapper proc
     sub rsp, 28h       ; Reserve shadow space (32 bytes) + align stack
     call PascalAdd
     add rsp, 28h       ; Restore stack
     ret
   PascalAddWrapper endp
   ```

---

### **Handling More Arguments**
For functions with >4 arguments (Windows) or >6 arguments (Linux), additional arguments are passed on the stack. Reorder them if necessary:
```asm
; Windows example: 5th argument is on the stack at [rsp+28h] (after shadow space)
PascalAddWrapper proc
  mov r10, [rsp+28h]  ; Load 5th argument
  ; Reorder stack arguments here if needed
  jmp PascalAdd
PascalAddWrapper endp
```

---

### **When to Use This**
- Interfacing with legacy Pascal code compiled for 64-bit with non-standard conventions.
- Debugging custom calling conventions.
- **Avoid** for new code; use standard ABIs instead.

This approach ensures compatibility while leveraging the efficiency of register-based argument passing.


Rick Hodgin

unread,
Feb 3, 2025, 11:20:33 AMFeb 3
to sem...@googlegroups.com
The problem is you have to know how many parameters, and these are dispatched by SAL in left to right order, so recompiling macros allows us to inject that information for the fixup.

It will work, just need to debug it.  Give me a couple weeks and I should have it.  Full 64-bit version is right behind that one.

Special thanks to Sammy for trusting me with his golden code.  I've known him online since the 90s.  Drove to meet him one time, but SemWare was closed for the day when I arrived.

I've used TSE every day I've done programming since 1987 when I first discovered QEdit on a BBS.  I bought a copy, got the manual, and never looked back.

Will be great to have macro abilities again in Visual Studio.  It's the main reason I still use TSE.

-- 
Rick C. Hodgin

S.E. Mitchell

unread,
Feb 3, 2025, 12:21:40 PMFeb 3
to sem...@googlegroups.com
On my lunch break :)
Note that I'd like to not use assembly language, if at all possible,
as I'd like the code to be portable to other (e.g., non-Intel)
processors. Below is a POC I was playing with a while back. Works
with 64-bit compilers on Windows and Linux - doubles cause a problem,
but TSE doesn't use those for now:

#define __USE_MINGW_ANSI_STDIO
#include <stdio.h>
#include <limits.h>
#include <inttypes.h>

/*------------------------------------------------------------------------
Apparently can't mix in doubles.
In 64-bit land:
On Linux, long is 64-bit
On Windows, long is 32-bit

On Windows,
%I64d generates warnings, use __USE_MINGW_ANSI_STDIO and "%" PRId64
------------------------------------------------------------------------*/

#if defined(__x86_64__) || defined(_M_X64)
typedef long long int_t;
#elif defined(__i386) || defined(_M_IX86)
typedef int int_t;
#else
#error Only 64-bit and 32-bit supported
#endif

// Define a macro for the format specifier
#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
// #define LL_F "%I64d"
#define LL_F "%" PRId64
#else
#define LL_F "%lld"
#endif

#if defined(__x86_64__) || defined(_M_X64)
int_t foo(char c, int i, long j, long long x, char *s) {
#elif defined(__i386) || defined(_M_IX86)
int_t foo(char c, int i, long j, long x, char *s) {
#endif
#if defined(__x86_64__) || defined(_M_X64)
#if defined(__MINGW64__)
//printf("char: %c int: %d long: %ld long long: %I64d string:
%s\n", c, i, j, x, s);
printf("char: %c int: %d long: %ld long long: " LL_F " string:
%s\n", c, i, j, x, s);
#else
//printf("char: %c int: %d long: %ld long long: %lld string:
%s\n", c, i, j, x, s);
printf("char: %c int: %d long: %ld long long: " LL_F " string:
%s\n", c, i, j, x, s);
#endif
#elif defined(__i386) || defined(_M_IX86)
printf("char %c int %d long %d string %s\n", c, i, x, s);
#endif
return 42;
}

int main() {
int_t rc, pa;

#if defined(__x86_64__) || defined(_M_X64)
printf("Compiling as 64-bit sizeof int_t: %d sizeof int %d sizeof
long %d sizeof double %d\n", (int)sizeof(int_t), (int)sizeof(int),
(int)sizeof(long), (int)sizeof(double));
#elif defined(__i386) || defined(_M_IX86)
printf("Compiling as 32-bit sizeof int_t is %d\n", sizeof(int_t));
#endif
pa = (int_t)foo;
rc=(int_t)(* (int_t(*)(int_t, int_t, int_t, int_t, int_t))pa)('c',
INT_MAX, LONG_MAX, LLONG_MAX, (int_t)"hello, world");
if (rc != 42)
printf("expectd %d, got %d\n", 42, (int)rc);
else
printf("got %d as expected\n", (int)rc);
return rc;
> --
>
> ---
> You received this message because you are subscribed to the Google Groups "SemWare TSE Pro text editor" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to semware+u...@googlegroups.com.
> To view this discussion visit https://groups.google.com/d/msgid/semware/CAOtq5xptSAepV9zUrYcrDAHvaEKhRBJZwAAZEH9%2BQeeEL9QrkQ%40mail.gmail.com.

knud van eeden

unread,
Feb 3, 2025, 12:22:16 PMFeb 3
to sem...@googlegroups.com
I once had a telephone conversation with Sammy after I bought my copy of TSE.

Contemplated before also if visiting one day (I have visited 61 countries totally, e.g. 26 countries in 31 days in 2023, speaking something like 10 natural languages, so should be able to get around).

with friendly greetings
Knud van Eeden


--

---
You received this message because you are subscribed to the Google Groups "SemWare TSE Pro text editor" group.
To unsubscribe from this group and stop receiving emails from it, send an email to semware+u...@googlegroups.com.
To view this discussion visit

Rick Hodgin

unread,
Feb 3, 2025, 5:15:09 PMFeb 3
to sem...@googlegroups.com
Version 1 will use assembly.  Version 2 we'll see.

-- 
Rick C. Hodgin

knud van eeden

unread,
Feb 3, 2025, 6:39:18 PMFeb 3
to sem...@googlegroups.com
On Monday, February 3, 2025 at 06:21:46 PM GMT+1, S.E. Mitchell <sammy.m...@gmail.com> wrote:
 Works
with 64-bit compilers on Windows and Linux - doubles cause a problem,
but TSE doesn't use those for now:

#define __USE_MINGW_ANSI_STDIO
#include <stdio.h>
#include <limits.h>
#include <inttypes.h>
....


FYIO:

Compiles here from within TSE out of the box with:

===

1. Embarcadero BCC 10.2: Yes:

>ddd.exe

Compiling as 32-bit sizeof int_t is 4
char c int 2147483647 long -1 string hello, world
got 42 as expected

===

2. Microsoft Visual Studio 2022: no

===

3. Borland BCC 5.5: no

===

4. GNU MingW gcc 64 bits: no

===

5. GNU Cygwin gcc 32 bits: no
Reply all
Reply to author
Forward
0 new messages