lxapigen.pl
author Ryan C. Gordon <icculus@icculus.org>
Thu, 07 Jun 2018 23:48:39 -0400
changeset 123 3cd5b72c7f8e
parent 119 5a0e9fe5af19
child 134 352d3fac725f
permissions -rwxr-xr-x
Bunch more work on 16-bit entry points.

Can run testargv16.exe now. This means Watcom C startup code, and Watcom's
printf(), both work.
#!/usr/bin/perl -w

use warnings;
use strict;

my %typesizes = (
    'LONG' => 4,
    'ULONG' => 4,
    'SHORT' => 2,
    'USHORT' => 2,
    'HVIO' => 2,
    'HKBD' => 2,
);

sub typesize {
    my $t = shift;
    return 4 if ($t =~ /\AP/);  # pointers are 4 bytes (16:16).
    die("Unknown type '$t', please update \%typesizes\n") if not defined $typesizes{$t};
    return $typesizes{$t};
}

my $dirname = defined $ARGV[0] ? $ARGV[0] : 'native';
opendir(DIRH, $dirname) or die("Failed to opendir '$dirname': $!\n");

while (readdir(DIRH)) {
    next if not /\.h\Z/;
    next if /\Aos2/;
    next if /\-lx\.h\Z/;

    my $module = $_;
    my $header = "$dirname/$module";
    $module =~ s/\.h\Z//;
    open(IN, '<', $header) or die("Failed to open '$header' for reading: $!\n");

    print("$module ...\n");

    my %ordinalmap = ();
    my $has16bitfns = 0;

    while (<IN>) {
        chomp;
        next if not /OS2APIINFO/;
        my $line = $_;

        if (/\AOS2EXPORT\s+(.*?)\s+(OS2API|OS2API16)\s+(.*?)\((.*?)\)\s+OS2APIINFO\((.*?)\);/) {
            my %table = (
                'rettype' => $1,
                'apitype' => $2,
                'fn' => $3,
                'args' => $4,
            );
            my $apiinfo = $5;

            my $is16bit = $table{'apitype'} eq 'OS2API16';
            $table{'is16bit'} = $is16bit;
            $has16bitfns |= $is16bit;

            #print("rettype='$rettype' fn='$fn' args='$args' apiinfo='$apiinfo'\n");

            my $fn = $table{'fn'};
            my $ordinal;
            if ($apiinfo =~ /\A(\d+)\Z/) {
                $ordinal = int($1);
            } else {
                die("bogus OS2APIINFO for '$fn'\n");
            }

            $table{'ordinal'} = $ordinal;

            if (defined $ordinalmap{$ordinal}) {
                my $dupfn = $ordinalmap{$ordinal}{'fn'};
                die("Duplicate ordinal #$ordinal between '$fn' and '$dupfn'\n");
            }

            $ordinalmap{$ordinal} = \%table;
        } else {
            die ("Couldn't parse:\n\n  $line\n\n")
        }
    }

    close(IN);

    next if (not %ordinalmap);  # no exported items?

    #use Data::Dumper qw(Dumper); print Dumper \%ordinalmap;


    # Here we go...

    my $finalfname = "$dirname/$module-lx.h";
    my $outfname = "$finalfname-new";
    open(OUT, '>', $outfname) or die("Failed to open '$outfname' for writing: $!\n");

    print OUT <<EOF
/**
 * 2ine; an OS/2 emulator for Linux.
 *
 * Please see the file LICENSE.txt in the source's root directory.
 */

/* THIS FILE IS AUTOGENERATED. DO NOT EDIT BY HAND. see lxapigen.pl */

/* This is glue code for OS/2 binaries. Native binaries don't need this. */
#if LX_LEGACY

EOF
;

    foreach (sort { $a <=> $b } keys(%ordinalmap) ) {
        my $tableref = $ordinalmap{$_};
        my $fn = $tableref->{'fn'};
        my $argstr = $tableref->{'args'};
        my $rettype = $tableref->{'rettype'};
        my $is16bit = $tableref->{'is16bit'};
        my @args = ();

        if (($argstr ne 'VOID') and ($argstr ne 'void')) {
            @args = split /,/, $argstr;
        }

        # Build a little wrapper that'll pull arguments off the stack and
        #  convert to whatever the native calling conventions are.
        if ($is16bit) {
            print OUT "static $rettype bridge16to32_$fn(uint8 *args) {\n";
            #foreach (@args) {
            # arguments are listed backwards here.
            for my $i (reverse 0..$#args) {
                my $arg = $args[$i];
                my ($t, $n) = $arg =~ /\A\s*(.*?)\s+(.*?)\s*\Z/;
                #print("arg='$_' t='$t' n='$n'\n");
                my $a = ($t =~ /\AP/) ? 'PTRARG' : 'ARG';  # it's a pointer?
                print OUT "    LX_NATIVE_MODULE_16BIT_BRIDGE_$a($t, $n);\n";
            }

            print OUT "    ";
            if (($rettype ne 'void') && ($rettype ne 'VOID')) {
                # Currently it's reasonable to assume the retval will land in EAX.
                print OUT "return ";
            }

            print OUT "$fn(";
            my $comma = '';
            foreach (@args) {
                my ($t, $n) = /\A\s*(.*?)\s+(.*?)\s*\Z/;
                print OUT "$comma$n";
                $comma = ', ';
            }
            print OUT ");\n";

            print OUT "}\n\n";
        }
    }

    if ($has16bitfns) {
        print OUT "LX_NATIVE_MODULE_16BIT_SUPPORT()\n";
        foreach (sort { $a <=> $b } keys(%ordinalmap) ) {
            my $tableref = $ordinalmap{$_};
            next if not $tableref->{'is16bit'};
            my $fn = $tableref->{'fn'};
            print OUT "    LX_NATIVE_MODULE_16BIT_API($fn)\n";
        }

        print OUT "LX_NATIVE_MODULE_16BIT_SUPPORT_END()\n";
        print OUT "\n";
        print OUT "LX_NATIVE_MODULE_DEINIT({\n";
        print OUT "    LX_NATIVE_MODULE_DEINIT_16BIT_SUPPORT();\n";
        print OUT "})\n";
        print OUT "\n";
        print OUT "static int init16_$module(void) {\n";
        print OUT "    LX_NATIVE_MODULE_INIT_16BIT_SUPPORT()\n";

        foreach (sort { $a <=> $b } keys(%ordinalmap) ) {
            my $tableref = $ordinalmap{$_};
            next if not $tableref->{'is16bit'};
            my $fn = $tableref->{'fn'};
            my $argstr = $tableref->{'args'};
            my $argbytes = 0;
            my @args = ();

            if (($argstr ne 'VOID') and ($argstr ne 'void')) {
                @args = split /,/, $argstr;
            }

            foreach (@args) {
                my ($t, $n) = /\A\s*(.*?)\s+(.*?)\s*\Z/;
                $argbytes += typesize($t);
            }
            print OUT "        LX_NATIVE_INIT_16BIT_BRIDGE($fn, $argbytes)\n";
        }

        print OUT "    LX_NATIVE_MODULE_INIT_16BIT_SUPPORT_END()\n";
        print OUT "    return 1;\n";
        print OUT "}\n\n";
    }

    print OUT "LX_NATIVE_MODULE_INIT(";
    if ($has16bitfns) {
        print OUT "{ if (!init16_$module()) return 0; }";
    }
    print OUT ")\n";

    my $comma = '';
    foreach (sort { $a <=> $b } keys(%ordinalmap) ) {
        my $tableref = $ordinalmap{$_};
        my $fn = $tableref->{'fn'};
        my $ordinal = $tableref->{'ordinal'};
        my $suffix = $tableref->{'is16bit'} ? '16' : '';

        print OUT $comma;
        # This is a hack for tcpip32, which doesn't want to step on BSD sockets symbols.
        if ($fn =~ /\AOS2_(.*)\Z/) {
            print OUT "    LX_NATIVE_EXPORT_DIFFERENT_NAME($fn, \"$1\", $ordinal)";
        } else {
            print OUT "    LX_NATIVE_EXPORT$suffix($fn, $ordinal)";
        }
        $comma = ",\n";
    }
    print OUT "\n";

    print OUT <<EOF
LX_NATIVE_MODULE_INIT_END()

#endif /* LX_LEGACY */

/* end of $module-lx.h ... */

EOF
;

    close(OUT);

    if ((system("diff -u '$finalfname' '$outfname'") == -1) || ($? != 0)) {
        rename($outfname, $finalfname) or die("Failed to rename '$outfname' to '$finalfname': $!\n");
    } else {
        unlink($outfname);  # they match, just delete the new copy so build system doesn't rebuild everything.
    }
}

closedir(DIRH);

# end of lxapigen.pl ...