This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

C51 Toolchain under Visual Studio IDE

I spent time integrating the C51 toolchain into a Makefile project that utilizes all the features of the VS2005 IDE, so I just wanted to share my info. This can be used as a starting point for any of the toolchains. It allows full support of the VS IDE, including double clicking on errors in the output window to jump to the appropriate file and line.

First, you need to create an empty VS2005 project using the NMAKE project type settings. Under the Project Properties->NMake options, set the "Build Command Line" option to

nmake -nologo -f makefile.mak all

Then create a file called makefile.mak and paste the following - read and edit the comments and make changes where appropriate for your project requirements:

# makefile.mak: The makefile for using KEIL C51 Toolchain in Microsoft Visual Studio .NET 2005 using NMAKE
# project type
#
# By Jonathan Kaufmann
# May 29, 2007

# Adjust this to point to your KEIL Bin installation as well as all other binaries used by this Makefile
# for converting to IIC (EEPROM) file
PATH=$(PATH);C:\Keil\C51\BIN\;

#VS51.EXE is my wrapper that takes all output from the C51.exe, A51.exe and BL51.exe tools and formats
#it appropriately for VS2005 (probably 2003 too) so it will recognize errors and warnings and so you can
#dbl-click and jump to file/line of error locations.
#It detects what type of tool you want based on the first argument.  If first arg is a .c file, then C51.exe -
#if .a51, then A51.exe - and if .obj, then BL51.exe - source is included and may be changed for different
#tools or conditions
LD=VS51.EXE
CC=VS51.EXE
AS=VS51.EXE

# Customize your flags appropriately
CFLAGS=OPTIMIZE (9,SIZE) REGFILE ($(OBJDIR)\$(TARGET).ORC) BROWSE DEFINE (MASTER="1",_DEBUG) DEBUG OBJECTEXTEND
AFLAGS=NOMOD51 SET (SMALL) DEBUG EP
LDFLAGS=TO "$(OBJDIR)\$(TARGET)" REGFILE ($(OBJDIR)\$(TARGET).ORC) RAMSIZE(256) DISABLEWARNING (15) CODE( 0X0800-0X3FFF ) XDATA( 0x0100-0x0800,0XE000-0XE1FF )

# Give an output dir, and source directory, this is relative to where this MAKE file resides
OBJDIR=Debug
SRCDIR=Source

# Set your target output name, (no extensions - will generate .hex, .iic, .m51, or whatever appropriate)
TARGET=OUTFILE

# List all of your source files used (both ASM and C files), but with a .obj extension
# also must add the comma separated version, which is identical but w/ commas between instead of spaces
# when changing any header (.h) files, you will have to rebuild all, not a big deal since projects are
# so small
OBJS=$(OBJDIR)\main.obj $(OBJDIR)\debug.obj $(OBJDIR)\drivers.obj $(OBJDIR)\dscr.obj $(OBJDIR)\gpif.obj $(OBJDIR)\lsb2msb.obj $(OBJDIR)\spi.obj $(OBJDIR)\USB_ISRs.obj $(OBJDIR)\USBJmpTb.obj
OBJC=$(OBJDIR)\main.obj,$(OBJDIR)\debug.obj,$(OBJDIR)\drivers.obj,$(OBJDIR)\dscr.obj,$(OBJDIR)\gpif.obj,$(OBJDIR)\lsb2msb.obj,$(OBJDIR)\spi.obj,$(OBJDIR)\USB_ISRs.obj,$(OBJDIR)\USBJmpTb.obj

.SUFFIXES : .iic .hex .obj .c .a51

#Note that this make file does not take into account header files, so if you change a header file,
#files dependent on it will not recompile.  To account for this, the clean command is added so every
#time you run "make all", every file will be recompiled, equivalent to a "make rebuild"
#This shouldn't be a big deal since most 8051 projects will probably build in less than 5 second on todays processors.
#For those not familiar with MAKE files and setting up dependencies, they will now only have to change
#the OBJS and OBJC variables above when adding new source files.  I feel w/ the time it takes to compile
#(at least for my projects) this is a time-saving decision in the long run.
all: clean iic    #remove clean to not force rebuild at every compile, you must then manually add .h dependencies

# Everything below this line should just work
iic: $(OBJDIR)\$(TARGET).IIC    #use iic build type for generating all (object file, Hex file, and IIC file)
hex: $(OBJDIR)\$(TARGET).HEX    #use hex build type for building just hex (and object file)
obj: $(OBJDIR)\$(TARGET)                #not sure what this is good for

$(OBJDIR)\$(TARGET).IIC: $(OBJDIR)\$(TARGET).hex
        Hex2bix.exe -F 0xC2 -I -M 16384 $(OBJDIR)\$(TARGET).hex -O $(OBJDIR)\$(TARGET).IIC

$(OBJDIR)\$(TARGET).hex: $(OBJDIR)\$(TARGET)
        OH51.EXE $(OBJDIR)\$(TARGET)

$(OBJDIR)\$(TARGET): $(OBJS)
        $(LD) $(OBJC) $(LDFLAGS)

{$(SRCDIR)\}.a51{$(OBJDIR)\}.obj:
        $(AS) $< $(AFLAGS)
        @move $(SRCDIR)\$(@F) $(OBJDIR)/
        @move $(SRCDIR)\$(@B).LST $(OBJDIR)/

{$(SRCDIR)\}.c{$(OBJDIR)\}.obj:
        $(CC) $< $(CFLAGS)
        @move $(SRCDIR)\$(@F) $(OBJDIR)/
        @move $(SRCDIR)\$(@B).LST $(OBJDIR)/

clean:
        del /Q $(OBJDIR)\$(TARGET).IIC
        del /Q $(OBJDIR)\$(TARGET).HEX
        del /Q $(OBJDIR)\$(TARGET).M51
        del /Q $(OBJDIR)\$(TARGET).ORC
        del /Q $(OBJDIR)\$(TARGET)
        del /Q $(OBJDIR)\*.obj
        del /Q $(OBJDIR)\*.LST

rebuild: clean all

Parents Reply Children
  • Here is the source code

    ///////////////////////////////////////////////////////////////////////////////
    ///
    /// Original filename: vs51.cpp
    /// Project : vs51
    /// Date of creation : 2007-11-01
    /// Author(s) :
    ///
    /// Purpose : A wrapper for XX51.exe to allow compilation with NMAKE in MS Visual Studio.
    /// Insert this file in a Win32 Console Application and compile.
    ///
    /// Revisions:
    /// 0000 [2007-11-01] Initial revision.
    ///
    ///////////////////////////////////////////////////////////////////////////////

    // $Id$

    ///////////////////////////////////////////////////////////////////////////////
    // #define UNICODE
    // #define _UNICODE
    // These two defines are given implicitly through the settings of C_DEFINES in
    // the SOURCES file of the project. Hence change them there and there only.
    ///////////////////////////////////////////////////////////////////////////////

    #include "stdafx.h"
    #include <stdio.h>
    #include <string>

    // Hopefully SDCC won't output a line > 10K bytes
    #define MAX_LINE_LEN 10240

    int FatalError()
    { printf("Project:1: error: Unexpected output from C51.EXE"); return 1;
    }

    int main(int argc, char * argv[])
    { FILE *fp; std::string args = ""; std::string command_line; char buffer[MAX_LINE_LEN], *target_name = NULL; bool gotError = false; int i; bool isCFile = false; bool isAFile = false;

    if (argc <= 1) return 1;

    // change argv[1] to lower case - this should be a filename for (int i = 0; i < (int)strlen(argv[1]); ++i) if (argv[1][i] > 'A' && argv[1][i] < 'Z') argv[1][i] -= 'A' - 'a';

    // Find the file type // And while we're at it, try to find out what the target will be called. bool isLinker = false; if (strstr(argv[1], ".c")) command_line = std::string("C51.exe "); else if (strstr(argv[1], ".a51")) command_line = std::string("A51.exe "); else if (strstr(argv[1], ".obj")) { isLinker = true; command_line = std::string("BL51.exe "); } else { printf("Project : Error : File passed to vs51.EXE was not a C or assembly source."); return 1; }

    // Build the command line for sdcc.exe for (i = 1; i < argc; i++) { args += std::string(argv[i]); args += " "; } command_line += args;

    // Run the command and capture output fp = _popen(command_line.c_str(), "r"); if (!fp) { printf("Project : fatal error : Unable to run %s, make sure it's in PATH\n", argv[0]); return 1; }

    for (int i = 0; i < 3; ++i) { if (!fgets(buffer, sizeof(buffer), fp)) { _pclose(fp); return 0; } }

    if (!fgets(buffer, sizeof(buffer), fp)) { _pclose(fp); return 0; }

    // Work with the output do { // Empty line, spit out an empty line too if (!*buffer || *buffer == '\r' || *buffer == '\n') { printf("\n"); } else { // Try to parse the output... should look like "file:lineno: warning/error: blah blah blah..." char *ptr = buffer; //Format of errors is "*** ERROR C129 IN LINE 16 OF VARIABLES.H: BLAHBLAH" if (!isLinker) { if (!strncmp(ptr, "*** ", 4)) //Error or warning { char filename[255]; //Determine if error or warning bool isError; ptr = buffer + 4; if (!strncmp(ptr, "WARNING", 7)) isError = false; else { isError = true; gotError = true; } //Get Error number ptr = strstr(ptr, "C"); //Find C of error number (i.e. C129) if (!ptr) return FatalError(); ptr++; int errornum = atoi(ptr); //Get line number ptr = strstr(ptr, "LINE "); if (!ptr) return FatalError(); ptr += 5; int linenum = atoi(ptr); //Get file name ptr = strstr(ptr, "OF "); if (!ptr) return FatalError(); ptr += 3; char *temp = strstr(ptr, ":"); if (!temp) return FatalError(); temp[0] = 0; strcpy_s(filename, 255, ptr); ptr = temp + 2; if (isError) printf("%s(%d) : error C%d : %s\n", filename, linenum, errornum, ptr); else printf("%s(%d) : warning C%d : %s\n", filename, linenum, errornum, ptr); } } else //Parse output of BL51.exe { if (!strncmp(ptr, "*** ", 4)) //Error or warning { //Determine if error or warning bool isError; ptr = buffer + 4; if (!strncmp(ptr, "WARNING", 7)) isError = false; else { isError = true; gotError = true; } //Not sure what we can do here, no file to point to, so just output error message if (isError) printf("Project : error : %s", ptr + 6); else printf("Project : warning : %s", ptr + 8); if (!fgets(buffer, sizeof(buffer), fp)) break; while (!strncmp(buffer, " ", 4)) { printf(buffer); if (!fgets(buffer, sizeof(buffer), fp)) break; } continue; } //also print program size else if (!strncmp(buffer, "Program Size:", 13)) { if (gotError) { printf("Errors encountered while linking\r\n"); } else { printf(buffer); } } } } // if not empty line if (!fgets(buffer, sizeof(buffer), fp)) break; } while (!feof(fp)); _pclose(fp);

    // Standard practice is return 0 if everything is okay, another code on error. return gotError ? 1 : 0;
    }

  • Please re-post, taking care to follow the instructions: www.danlhenry.com/.../keil_code.png

  • /// Original filename: vs51.cpp
    /// Project          : vs51
    /// Date of creation : 2007-11-01
    /// Author(s)        :
    ///
    /// Purpose          : A wrapper for XX51.exe to allow compilation with NMAKE in MS Visual Studio.
    ///                    Insert this file in a Win32 Console Application and compile.
    
    #include "stdafx.h"
    #include <stdio.h>
    #include <string>
    
    // Hopefully SDCC won't output a line > 10K bytes
    #define MAX_LINE_LEN 10240
    
    int FatalError()
    {
        printf("Project:1: error: Unexpected output from C51.EXE");
        return 1;
    }
    
    int main(int argc, char * argv[])
    {
        FILE *fp;
        std::string args = "";
        std::string command_line;
        char buffer[MAX_LINE_LEN], *target_name = NULL;
        bool gotError = false;
        int i;
        bool isCFile = false;
        bool isAFile = false;
    
            if (argc <= 1)
            return 1;
    
            // change argv[1] to lower case - this should be a filename
        for (int i = 0; i < (int)strlen(argv[1]); ++i)
            if (argv[1][i] > 'A' && argv[1][i] < 'Z')
                argv[1][i] -= 'A' - 'a';
    
            // Find the file type
        // And while we're at it, try to find out what the target will be called.
        bool isLinker = false;
        if (strstr(argv[1], ".c"))
            command_line = std::string("C51.exe ");
        else if (strstr(argv[1], ".a51"))
            command_line = std::string("A51.exe ");
        else if (strstr(argv[1], ".obj"))
        {
            isLinker = true;
            command_line = std::string("BL51.exe ");
        }
        else
        {
            printf("Project : Error : File passed to vs51.EXE was not a C or assembly source.");
            return 1;
        }
    
            // Build the command line for sdcc.exe
        for (i = 1; i < argc; i++)
        {
            args += std::string(argv[i]);
            args += " ";
        }
        command_line += args;
    
            // Run the command and capture output
        fp = _popen(command_line.c_str(), "r");
        if (!fp)
        {
            printf("Project : fatal error : Unable to run %s, make sure it's in PATH\n", argv[0]);
            return 1;
        }
    
        for (int i = 0; i < 3; ++i)
        {
            if (!fgets(buffer, sizeof(buffer), fp))
            {
                _pclose(fp);
                return 0;
            }
        }
    
        if (!fgets(buffer, sizeof(buffer), fp))
        {
            _pclose(fp);
            return 0;
        }
    
            // Work with the output
        do
        {
            // Empty line, spit out an empty line too
            if (!*buffer || *buffer == '\r' || *buffer == '\n')
            {
                printf("\n");
            }
            else
            {
                // Try to parse the output... should look like "file:lineno: warning/error: blah blah blah..."
                char *ptr = buffer;
                //Format of errors is "*** ERROR C129 IN LINE 16 OF VARIABLES.H: BLAHBLAH"
                if (!isLinker)
                {
                    if (!strncmp(ptr, "*** ", 4))     //Error or warning
                    {
                        char filename[255];
                                                      //Determine if error or warning
                        bool isError;
                        ptr = buffer + 4;
                        if (!strncmp(ptr, "WARNING", 7))
                            isError = false;
                        else
                        {
                            isError = true;
                            gotError = true;
                        }
                                                      //Get Error number
                        ptr = strstr(ptr, "C");       //Find C of error number (i.e. C129)
                        if (!ptr)
                            return FatalError();
                        ptr++;
                        int errornum = atoi(ptr);
                                                      //Get line number
                        ptr = strstr(ptr, "LINE ");
                        if (!ptr)
                            return FatalError();
                        ptr += 5;
                        int linenum = atoi(ptr);
                                                      //Get file name
                        ptr = strstr(ptr, "OF ");
                        if (!ptr)
                            return FatalError();
                        ptr += 3;
                        char *temp = strstr(ptr, ":");
                        if (!temp)
                            return FatalError();
                        temp[0] = 0;
                        strcpy_s(filename, 255, ptr);
                        ptr = temp + 2;
                        if (isError)
                            printf("%s(%d) : error C%d : %s\n", filename, linenum, errornum, ptr);
                        else
                            printf("%s(%d) : warning C%d : %s\n", filename, linenum, errornum, ptr);
                    }
                }
                else                                  //Parse output of BL51.exe
                {
                    if (!strncmp(ptr, "*** ", 4))     //Error or warning
                    {
                                                      //Determine if error or warning
                        bool isError;
                        ptr = buffer + 4;
                        if (!strncmp(ptr, "WARNING", 7))
                            isError = false;
                        else
                        {
                            isError = true;
                            gotError = true;
                        }
                                                      //Not sure what we can do here, no file to point to, so just output error message
                        if (isError)
                            printf("Project : error : %s", ptr + 6);
                        else
                            printf("Project : warning : %s", ptr + 8);
                        if (!fgets(buffer, sizeof(buffer), fp))
                            break;
                        while (!strncmp(buffer, "    ", 4))
                        {
                            printf(buffer);
                            if (!fgets(buffer, sizeof(buffer), fp))
                                break;
                        }
                        continue;
                    }
                                                      //also print program size
                    else if (!strncmp(buffer, "Program Size:", 13))
                    {
                        if (gotError)
                        {
                            printf("Errors encountered while linking\r\n");
                        }
                        else
                        {
                            printf(buffer);
                        }
                    }
                }
            }                                         // if not empty line
            if (!fgets(buffer, sizeof(buffer), fp))
                break;
        } while (!feof(fp));
        _pclose(fp);
    
        // Standard practice is return 0 if everything is okay, another code on error.
        return gotError ? 1 : 0;
    }
    

  • That's better!

    :-)

    /// Purpose          : A wrapper for XX51.exe to allow compilation with NMAKE in MS Visual Studio.
    

    Is it actually specific to just the '51 tools, or should it work for any...?