• Hello MLAers! We've re-enabled auto-approval for accounts. If you are still waiting on account approval, please check this thread for more information.

Programmatically (in C) send MIDI out signal from modem port, SE/30

why does it get past the > 0 conditional?
why doesn't it detect information in the buffer?
If you set a breakpoint, you'll see that nbBytes is not zero.
You need to fix your printf statement.
%d is always for int but nbBytes must be a 32-bit long according to the documentation for SerGetBuf.
https://dev.os9.ca/techpubs/mac/Devices/Devices-323.html

Variadic functions like printf might push different number of bytes for int and long. In this case, it may push 4 bytes but %d only looks at 2 bytes. Since 68K is big endian, the two bytes it will access are the two most significant bytes which are probably zero (unless you're expecting more than 64K bytes of data which is unlikely).

%d might work for long in some other C compilers or architectures. Different headers for different build environments may define macros for printf format specifications. These are typically in a header named inttypes.h for C and cinttypes for C++. Does Think C have either of those files? Probably not since it was added for C99. Search for PRId32 in all the header files to find out. If it did, then you could do this:
Code:
printf("\n%" PRId32 " bytes waiting", nbBytes);
since we know that nbBytes is 32-bits. The PRId32 macro will be defined correctly for your environment to indicate a 32-bit quantity. In modern macOS, it would be defined as d but for your Think C, it should be ld unless Think C has a 32-bit ints option like Metrowerks?
 
<snip> These are typically in a header named inttypes.h for C and cinttypes for C++. Does Think C have either of those files? Probably not since it was added for C99.
[ @Mu0n ] THINK C 5 has <stdarg.h> for handling variable arguments. The example on page 268 of the User Manual (which I have a paper copy of):

C:
#include <stdarg.h>

int minimum(int count, ...)
{
    va_list xp; // Declare arg pointer.
    int x,min;
    va_start(xp, count); // init the pointer.
    min=va_arg(xp,int); // Get the first arg.
    while(--count) {
        x=va_arg(xp,int); // Get the next arg.
        if(x<min) min=x;
    }
    va_end(xp);
    return (min)
}

Search for PRId32 in all the header files to find out. If it did, then you could do this:
Code:
printf("\n%" PRId32 " bytes waiting", nbBytes);
since we know that nbBytes is 32-bits. The PRId32 macro will be defined correctly for your environment to indicate a 32-bit quantity. In modern macOS, it would be defined as d but for your Think C
THINK C doesn't have inttypes.h . However, format specifications are handled by the int vfprintf(FILE *fp, const char *fmt, va_list arg); function in the THINK C 5.0 Folder:C Libraries:sources folder.

it should be ld unless Think C has a 32-bit ints option like Metrowerks?
It does have a 32-bit int option. Edit:Options...:Compiler Settings:4-byte ints checkbox.

Consequently, vfprintf decodes %l , %L, %d, %i formats according to the current int size, e.g.:

C:
case 'd':
case 'i':
    if(F.lSize)
            n=va_arg(arg,long);
    else
            n=va_arg(arg,int);
    if(F.hSize)
            n=(short)n;
    // etc.

THINK C supports the '*' modifier which takes the field width from the next va_arg argument and the 'h' modifier (but not 'hh') for enforcing short arguments (which are always 16-bits). This means you can ensure correct 32-bit and 16-bit processing by using %l or %h regardless of the int setting.

I always define the more conventional <stdint.h> type types in THINK C these days:

C:
typedef char int8_t; // char is always signed in THINK C
typedef unsigned char uint8_t;
typedef short int16_t;    // short is always 16-bits in THINK C
typedef unsigned short uint16_t;
typedef long int32_t;    // long is always 32-bits in THINK C
typedef unsigned long uint32_t;
// long long isn't supported.

Hope this helps.
 
Last edited:
<snip> format specifications are handled by the int vfprintf(FILE *fp, const char *fmt, va_list arg); function in the THINK C 5.0 Folder:C Libraries:sources folder.
@Mu0n , @joevt . I did a bit more looking for how sprintf is handled. In <stdio.c> we have:

C:
int sprintf(char *s, const char *fmt, ...)
{
    return(vsprintf(s, fmt, __va(fmt)));
}

int vsprintf(char *s, const char *fmt, void *p)
{
    FILE f;
    int n;
    if((n=vfprintf(__strout(&f,s),fmt,p))>=0)
        s[n]=0;
    return(n);
}

So, basically, all printf type formatting is handled via vfprintf and vsprintf is handled by faking a file, which is a string and then calling vfprintf for that 'file'. This also tells you how to handle your own fmt with variable arguments.
 
THINK C supports the '*' modifier which takes the field width from the next va_arg argument and the 'h' modifier (but not 'hh') for enforcing short arguments (which are always 16-bits). This means you can ensure correct 32-bit and 16-bit processing by using %l or %h regardless of the int setting.
The '*' modifier for the field width usually refers to the width of the text in characters, rather than the byte width of the parameter?
hh is for char, but single byte quantities in 68K are always two bytes on the stack so it's not necessary?

%l and %h - you mean these:
Code:
     o   An optional length modifier, that specifies the size of the argument.
         The following length modifiers are valid for the d, i, n, o, u, x, or X
         conversion:

         Modifier                 d, i               o, u, x, X                n
         hh                       signed char        unsigned char             signed char *
         h                        short              unsigned short            short *
         l (ell)                  long               unsigned long             long *
         ll (ell ell)             long long          unsigned long long        long long *
         j                        intmax_t           uintmax_t                 intmax_t *
         t                        ptrdiff_t          (see note)                ptrdiff_t *
         z                        (see note)         size_t                    (see note)
         q (deprecated)           quad_t             u_quad_t                  quad_t *

Right. I believe
%ld will always refer to a long regardless of the size of long
%hd will always refer to a short regardless of the size of short
%d will always refer to a int regardless of the size of int
as long as the C standard library that does the printf has the some compile options as the code you are calling printf from.

The problem in this case was that nbBytes is not an int so %d should not be used.
But nbBytes might not be a long either so you can't always use %ld. The solution then is to explicitly type cast the parameter like this:
(long)nbBytes to ensure that %ld is always correct.
 
The '*' modifier for the field width usually refers to the width of the text in characters, rather than the byte width of the parameter?
Yes, that's what the code does. I can see it substitutes the next va_arg for the literal field width you'd otherwise expect.
hh is for char, but single byte quantities in 68K are always two bytes on the stack so it's not necessary?
That's a good point. You can't push a char on the 68K.
%l and %h - you mean these:
Code:
     o   An optional length modifier, that specifies the size of the argument.
         The following length modifiers are valid for the d, i, n, o, u, x, or X
         conversion:

         Modifier                 d, i               o, u, x, X                n
         hh                       signed char        unsigned char             signed char *
         h                        short              unsigned short            short *
         l (ell)                  long               unsigned long             long *
         ll (ell ell)             long long          unsigned long long        long long *
         j                        intmax_t           uintmax_t                 intmax_t *
         t                        ptrdiff_t          (see note)                ptrdiff_t *
         z                        (see note)         size_t                    (see note)
         q (deprecated)           quad_t             u_quad_t                  quad_t *
I can see it doesn't support all of the combinations, but I was only checking for 'l' and 'd'. There's no 'q' support, so it was already pre-deprecated ;-) !

Right. I believe
%ld will always refer to a long regardless of the size of long
%hd will always refer to a short regardless of the size of short
%d will always refer to a int regardless of the size of int
as long as the C standard library that does the printf has the some compile options as the code you are calling printf from.
There's only one ANSI library.
In 2-byte int mode: printf("%d %d", 1234, 5678); displays 1234, 5678.
In 2-byte int mode: printf("%ld", 1234, 5678); displays 80877102.
In 4-byte int mode: printf("%d %d", 1234, 5678); displays 0, 1234.

This shows the ANSI library doesn't handle 4-byte ints automatically and there isn't a version for 4-byte ints. What you'd have to do is roll-your-own 4-byte int ANSI library and Source:Add... it to your project in place of the default one. I think that's possible, because the source code is included, as I've been browsing through it to find out this stuff. Of course, there could be other bugs in the library which prevent the expected behaviour from 4-byte ints.
The problem in this case was that nbBytes is not an int so %d should not be used.
But nbBytes might not be a long either so you can't always use %ld. The solution then is to explicitly type cast the parameter like this:
(long)nbBytes to ensure that %ld is always correct.
It would then work in 2-byte int and 4-byte int mode. The combination of nbBytes and %ld would also work in 4-byte int mode, but your solution is probably best.
 
This shows the ANSI library doesn't handle 4-byte ints automatically and there isn't a version for 4-byte ints. What you'd have to do is roll-your-own 4-byte int ANSI library and Source:Add... it to your project in place of the default one. I think that's possible, because the source code is included, as I've been browsing through it to find out this stuff. Of course, there could be other bugs in the library which prevent the expected behaviour from 4-byte ints.
Metrowerks CodeWarrior has different C standard library binaries for different compile options. "Build MSL Libraries.app" lets you choose which libraries to build. (4i means 4 byte ints). If you discover a bug in the source and make a fix, then you can recompile all the libraries.
Code:
MSL C.68K (2i_8d).A4.Lib
MSL C.68K (2i_8d).Lib
MSL C.68K (2i_F_8d).A4.Lib
MSL C.68K (2i_F_8d).Lib
MSL C.68K (2i_F).A4.Lib
MSL C.68K (2i_F).Lib
MSL C.68K (2i).A4.Lib
MSL C.68K (2i).Lib
MSL C.68K (4i_8d).A4.Lib
MSL C.68K (4i_8d).Lib
MSL C.68K (4i_F_8d).A4.Lib
MSL C.68K (4i_F_8d).Lib
MSL C.68K (4i_F).A4.Lib
MSL C.68K (4i_F).Lib
MSL C.68K (4i).A4.Lib
MSL C.68K (4i).Lib
MSL C.68K (NL_2i_8d).A4.Lib
MSL C.68K (NL_2i_8d).Lib
MSL C.68K (NL_2i_F_8d).A4.Lib
MSL C.68K (NL_2i_F_8d).Lib
MSL C.68K (NL_2i_F).A4.Lib
MSL C.68K (NL_2i_F).Lib
MSL C.68K (NL_2i).A4.Lib
MSL C.68K (NL_2i).Lib
MSL C.68K (NL_4i_8d).A4.Lib
MSL C.68K (NL_4i_8d).Lib
MSL C.68K (NL_4i_F_8d).A4.Lib
MSL C.68K (NL_4i_F_8d).Lib
MSL C.68K (NL_4i_F).A4.Lib
MSL C.68K (NL_4i_F).Lib
MSL C.68K (NL_4i).A4.Lib
MSL C.68K (NL_4i).Lib
MSL C.68K Fa(2i_8d).A4.Lib
MSL C.68K Fa(2i_8d).Lib
MSL C.68K Fa(2i_F_8d).A4.Lib
MSL C.68K Fa(2i_F_8d).Lib
MSL C.68K Fa(2i_F).A4.Lib
MSL C.68K Fa(2i_F).Lib
MSL C.68K Fa(2i).A4.Lib
MSL C.68K Fa(2i).Lib
MSL C.68K Fa(4i_8d).A4.Lib
MSL C.68K Fa(4i_8d).Lib
MSL C.68K Fa(4i_F_8d).A4.Lib
MSL C.68K Fa(4i_F_8d).Lib
MSL C.68K Fa(4i_F).A4.Lib
MSL C.68K Fa(4i_F).Lib
MSL C.68K Fa(4i).A4.Lib
MSL C.68K Fa(4i).Lib
MSL C.68K Fa(NL_2i_8d).A4.Lib
MSL C.68K Fa(NL_2i_8d).Lib
MSL C.68K Fa(NL_2i_F_8d).A4.Lib
MSL C.68K Fa(NL_2i_F_8d).Lib
MSL C.68K Fa(NL_2i_F).A4.Lib
MSL C.68K Fa(NL_2i_F).Lib
MSL C.68K Fa(NL_2i).A4.Lib
MSL C.68K Fa(NL_2i).Lib
MSL C.68K Fa(NL_4i_8d).A4.Lib
MSL C.68K Fa(NL_4i_8d).Lib
MSL C.68K Fa(NL_4i_F_8d).A4.Lib
MSL C.68K Fa(NL_4i_F_8d).Lib
MSL C.68K Fa(NL_4i_F).A4.Lib
MSL C.68K Fa(NL_4i_F).Lib
MSL C.68K Fa(NL_4i).A4.Lib
MSL C.68K Fa(NL_4i).Lib
MSL C.68K MPW.mcp
MSL C.68K MPW(NL_4i_8d).Lib
MSL C.68K MPW(NL_4i_F_8d).Lib
MSL C.68K MPW(NL_4i_F).Lib
MSL C.68K MPW(NL_4i).Lib
 
Wow guys, thanks for the high quality tutorial on variable types. I found very old documentation in which SerGetBuf uses ints instead of longs, maybe that was triggered the error for me. It's definitely a long in more recent documentation, including THINK Reference Viewer.

The bad formatter character for printf was a stupid mistake that I do occasionally.
The real mistake was not setting up the .AIn driver properly with the clock like I did for .AOut. There was no chance of getting anything other than garbage before this was done. Once this was fixed with the printf statement, I saw the buffer count slowly rise up to 1024 bytes, which was supremely satisfying because at that point, I had put back my own custom 1k input buffer handle, so I was witnessing saturation. Just leaving my keyboard controller idle makes these 0xFE stay alive midi bytes. I could push it up faster by mashing buttons.

Quickly after I got this behavior, I reacted to note on and off bytes (funelling them into a quick state machine hack to get all 3 bytes of both of these commands) and produced the last video above. In case you are against watching video results in a forum, here's a recap: it's showing my playing of the SQIII theme, which sends MIDI in bytes to my app, which then turns around and sends them to MIDI out to my MT-32. The 0xFE are simply read and ignored, but at least they're flushed out of the buffer.
 
Last edited:
Back
Top