允许在旧式嵌入式 c/c++ 代码上使用现代代码分析工具

Enable usage of modern code analysis tools on old-ish embedded c/c++ code

如何在老式嵌入式 c/c++ 源代码上使用现代代码分析工具,例如 SourceTrail,最初用于编译器,例如 Hi-Tech C、PIC C、IAR Workbench 适用于许多微控制器,不仅限于 Microchip 的 PIC、PIC16 和 PIC18 系列。

为了支持微型微控制器的有限架构,嵌入式编译器的供应商不得不提出 c/c++ 语言的扩展,这些语言已经(或尚未)在c语言规范.

这导致微控制器特定的头文件包含如下内容:

// Register: ANSELA
extern volatile unsigned char           ANSELA              @ 0xF38;
#ifndef _LIB_BUILD
asm("ANSELA equ 0F38h");
#endif

typedef union {
    struct {
        unsigned ANSB0                  :1;
        unsigned ANSB1                  :1;
        unsigned ANSB2                  :1;
        unsigned ANSB3                  :1;
        unsigned ANSB4                  :1;
        unsigned ANSB5                  :1;
    };
} ANSELBbits_t;
extern volatile ANSELBbits_t ANSELBbits @ 0xF39;

extern volatile unsigned short long     TBLPTR              @ 0xFF6;

extern volatile __bit                   ABDEN1              @ (((unsigned) &BAUDCON1)*8) + 0;

代码文件包括如下内容:

void interrupt high_priority InterruptVectorHigh(void) 
{
}

void interrupt low_priority InterruptVectorLow(void)
{
}

用现代工具支持此源代码,同时确保源代码仍可用于原始编译器的最简单方法是什么?

编辑:

下面提供了答案。

下面的修复将使任何支持 C18 或 C2x 规范的编译器都能理解 c 代码。我(还)没有机会使用 C++ 进行测试,因此它们可能不完全符合任何 C++ 规范。

感谢@Antti Haapala、@Clifford 和@anastaciu 等人回答了我的相关问题 here and here 并启用了这个更完整的答案。

short long类型

首先,24 位 short long 类型是个问题,因为 c-specifications 中不存在等价物,而且该类型的两个字无法用 [=16] 寻址=].起初,我使用 Perl 简单地将字符串 short long 修改为所有 vendor-specific header 文件的 long,如下所示:

perl -pi -e "s/(short long)/long/g" .h

Note, for the Microchip MPLAB CX8 compiler on Windows the header files are located in the following folder and sub-folders: c:\Program Files (x86)\Microchip\xc8\v1.33\include

但后来我意识到 short 类型从来没有单独使用过,所以我决定使用 #define short 简单地删除 short 部分。请注意,这会影响使用 short 的所有内容,因此我在此答案中保留了这两种方法。

用@

定义的寄存器位和字节地址

@-signs 是一个特定的问题,因为它们不能使用 #define 重新定义,所以 perl 再次来拯救,这次使用两次传递来解决两种不同的语法:

perl -pi -e "s/@\s*([0-9a-fA-FxX]+)/AT()/g" .h
perl -pi -e "s/[@] ?+([^;]*)/AT()/g" .h

这些本质上是将 AT()@ 之后的任何内容包装起来,允许对其进行正常定义操作。

额外的关键字

最后一步是在编译器供应商提供的每个 header 文件中插入一个宏 header。我最终得到了以下宏 header:

// Hack to allow SourceTrail to be used on this source
#if defined __XC8
  #define AT(address) @ address
#else
  #define AT(address)
  #define __bit _Bool
  #define asm(assembly)
  #define interrupt
  #define short
  #define high_priority
  #define low_priority
#endif

可以看出,任何 non-standard 都被简单地删除了,除非 header 文件被 MPLAB XC8 编译器使用。唯一的例外是 __bit 类型,它被重新定义为 _Bool 类型 - 它似乎可以工作。

在 windows

上对 运行 的批处理脚本的完整修复

因为我 运行 在 windows 上完成所有这些,Perl one-liners 并没有像在 Linux 上那样工作,所以为了处理每个每个 header 文件,我都必须将 Perl 命令包装在批处理 for-loop 中,这非常慢。为了弥补这一点,我将所有内容组合在一个名为 fix.cmd 的批次中,该批次位于 include 文件夹中(参见上面的路径):

:: Fix to allow SourceTrail to analyze MPLAB CX8 source code.
@echo off
setlocal enabledelayedexpansion

:: Run in the folder where the script exists.
pushd "%~dp0"

echo:Fixing MPLAB global include files to be used by SourceTrail and other analysis tools.

:: Loop each directory recrusively
set DirCounter=0
set FileCounter=0
for /r %%d in (.) do (
    set /A DirCounter=DirCounter+1
    pushd %%d
    echo | set /p=Processing:
    cd
    
    for %%f in (*.h) do (
        set /A FileCounter=FileCounter+1
        set /A ModValue=FileCounter%%25
        if !ModValue!==0 ( echo | set /p=* )
        call :ProcessFile %%f
    )
    
    popd
    echo *
)
echo:Processed %FileCounter% files in %DirCounter% folders.
echo Done   
exit /b 0


:ProcessFile
:: filename is in %1
    
:: Remove short from short long. (Done with a define instead)
::  perl -pi -e "s/(short long)/long/g" %1

:: Replace the simple @ lines with AT().
    perl -pi -e "s/@\s*([0-9a-fA-FxX]+)/AT()/g" %1

:: Exchange @ and wrap in parenthesis for any substring starting with @ and ending with ; in each header file.
    perl -pi -e "s/[@] ?+([^;]*)/AT()/g" %1

:: Insert defines before first line in each header files:
    perl -pi -e "print \"// Hack to allow SourceTrail to be used on this source\n#if defined __XC8\n  #define AT(address) @ address\n#else\n  #define AT(address)\n  #define __bit _Bool\n  #define asm(assembly)\n  #define interrupt\n  #define short\n#define high_priority\n  #define low_priority\n#endif\n\n\" if $. == 1" %1

::Exit subroutine   
exit /b

要执行修改,请打开提升的提示符,cd 到包含文件,然后执行 fix.cmd

先决条件

必须在 Windows 计算机上安装 Perl。我使用 StrawberryPerl

编辑: 主要是固定错别字。 阐明了如何处理 short long

有两个选项