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
/// 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...?