diff --git a/CHANGELOG.md b/CHANGELOG.md
index 115de48..eb63d19 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,18 @@ All notable version changes will be recorded in this file.
 
 ***
 
+### [v3.21.1] revision
+
+**New**:
+  - `Map View`: Support show `map.view` file for SDCC and KEIL_C51 toolchain.
+  - `Scatter File Highlight`: Support a simple arm scatter file (`.sct`) language support.
+
+**Fix**:
+  - `Program Matcher`: Sometimes armcc problem matcher missed matchs.
+  - `CMSIS Config Wizard`: Missed skip value for `<e>` tag.
+
+***
+
 ### [v3.21.0] update
 
 **New**:
diff --git a/lang/syntax/sct.language-configuration.json b/lang/syntax/sct.language-configuration.json
new file mode 100644
index 0000000..b8fe471
--- /dev/null
+++ b/lang/syntax/sct.language-configuration.json
@@ -0,0 +1,23 @@
+{
+	"comments": {
+		"lineComment": ";",
+		"blockComment": ["/*", "*/"],
+	},
+	"brackets": [
+		["{", "}"],
+		["[", "]"],
+		["(", ")"]
+	],
+	"autoClosingPairs": [
+		{ "open": "{", "close": "}" },
+		{ "open": "[", "close": "]" },
+		{ "open": "(", "close": ")" },
+		{ "open": "'", "close": "'", "notIn": ["string", "comment"] },
+		{ "open": "\"", "close": "\"", "notIn": ["string"] },
+		{ "open": "/**", "close": " */", "notIn": ["string"] }
+	],
+	"indentationRules": {
+		"increaseIndentPattern": "^\\s*{",
+		"decreaseIndentPattern": "^\\s*}"
+	}
+}
\ No newline at end of file
diff --git a/lang/syntax/sct.tmLanguage.json b/lang/syntax/sct.tmLanguage.json
new file mode 100644
index 0000000..c644294
--- /dev/null
+++ b/lang/syntax/sct.tmLanguage.json
@@ -0,0 +1,108 @@
+{
+    "information_for_contributors": [
+        "arm scatter file"
+    ],
+    "version": "1.0.0",
+    "name": "sct",
+    "scopeName": "source.arm.sct",
+    "patterns": [
+        {
+            "include": "#commandline"
+        },
+        {
+            "include": "#line_comment"
+        },
+        {
+            "include": "#block_comment"
+        },
+        {
+            "include": "#string"
+        },
+        {
+            "include": "#number"
+        },
+        {
+            "include": "#operators"
+        },
+        {
+            "include": "#preprocessor"
+        },
+        {
+            "include": "#keyword.load_region"
+        },
+        {
+            "include": "#keyword.exec_region"
+        }
+    ],
+    "repository": {
+        "commandline": {
+            "match": "^#!.*$",
+            "captures": {
+                "0": {
+                    "name": "markup.italic"
+                }
+            }
+        },
+        "line_comment": {
+            "match": "(;|\\/\\/).*",
+            "captures": {
+                "0": {
+                    "name": "comment.line"
+                }
+            }
+        },
+        "block_comment": {
+            "begin": "\\/\\*",
+            "end"  : "\\*\\/",
+            "name": "comment.block"
+        },
+        "string": {
+            "match": "\"([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\"",
+            "captures": {
+                "0": {
+                    "name": "string.quoted.double"
+                }
+            }
+        },
+        "number": {
+            "match": "(?i)\\b([0-9]+|0x[0-9a-f]+)\\b",
+            "captures": {
+                "1": {
+                    "name": "constant.numeric"
+                }
+            }
+        },
+        "operators": {
+            "match": "\\+|-|\\*|\\/(?!\\/)|\\+=|-=|=|==|<=|>=|!=",
+            "captures": {
+                "0": {
+                    "name": "keyword.operator"
+                }
+            }
+        },
+        "preprocessor": {
+            "match": "^\\s*(#define|#undef|#include|#if|#ifdef|#ifndef|#elif|#else|#endif)",
+            "captures": {
+                "1": {
+                    "name": "keyword.control.define"
+                }
+            }
+        },
+        "keyword.load_region": {
+            "match": "\\b(ABSOLUTE|ALIGN|NOCOMPRESS|OVERLAY|PI|PROTECTED|RELOC)\\b",
+            "captures": {
+                "1": {
+                    "name": "keyword"
+                }
+            }
+        },
+        "keyword.exec_region": {
+            "match": "\\b(ABSOLUTE|ALIGN|NOCOMPRESS|OVERLAY|PI|ALIGNALL|ANY_SIZE|EMPTY|FILL|FIXED|PADVALUE|SORTTYPE|UNINIT|ZEROPAD)\\b",
+            "captures": {
+                "1": {
+                    "name": "keyword"
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 2031532..e8ef1ef 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
     "name": "eide",
-    "version": "3.21.0",
+    "version": "3.21.1",
     "lockfileVersion": 2,
     "requires": true,
     "packages": {
         "": {
             "name": "eide",
-            "version": "3.21.0",
+            "version": "3.21.1",
             "license": "MIT",
             "dependencies": {
                 "iconv-lite": "^0.5.0",
diff --git a/package.json b/package.json
index 89b1d1d..78bc73d 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,7 @@
     "homepage": "https://em-ide.com",
     "license": "MIT",
     "description": "A mcu development environment for 8051/AVR/STM8/Cortex-M/MIPS/RISC-V",
-    "version": "3.21.0",
+    "version": "3.21.1",
     "preview": false,
     "engines": {
         "vscode": "^1.67.0"
@@ -1748,6 +1748,19 @@
                     "**/*.lkf"
                 ],
                 "configuration": "./lang/syntax/lkf.language-configuration.json"
+            },
+            {
+                "id": "sct",
+                "aliases": [
+                    "ARM Scatter File"
+                ],
+                "extensions": [
+                    ".sct"
+                ],
+                "filenamePatterns": [
+                    "**/*.sct"
+                ],
+                "configuration": "./lang/syntax/sct.language-configuration.json"
             }
         ],
         "jsonValidation": [
@@ -1829,6 +1842,11 @@
                 "language": "lkf",
                 "scopeName": "source.cosmic.lkf",
                 "path": "./lang/syntax/lkf.tmLanguage.json"
+            },
+            {
+                "language": "sct",
+                "scopeName": "source.arm.sct",
+                "path": "./lang/syntax/sct.tmLanguage.json"
             }
         ],
         "taskDefinitions": [
diff --git a/res/tools/darwin/unify_builder/arm64/unify_builder.deps.json b/res/tools/darwin/unify_builder/arm64/unify_builder.deps.json
index a02f1b7..6e0cadd 100644
--- a/res/tools/darwin/unify_builder/arm64/unify_builder.deps.json
+++ b/res/tools/darwin/unify_builder/arm64/unify_builder.deps.json
@@ -7,7 +7,7 @@
   "targets": {
     ".NETCoreApp,Version=v6.0": {},
     ".NETCoreApp,Version=v6.0/osx-arm64": {
-      "unify_builder/3.9.1": {
+      "unify_builder/3.9.2": {
         "dependencies": {
           "CommandLineParser": "2.9.1",
           "ConsoleTableExt": "3.1.9",
@@ -60,7 +60,7 @@
     }
   },
   "libraries": {
-    "unify_builder/3.9.1": {
+    "unify_builder/3.9.2": {
       "type": "project",
       "serviceable": false,
       "sha512": ""
diff --git a/res/tools/darwin/unify_builder/arm64/unify_builder.dll b/res/tools/darwin/unify_builder/arm64/unify_builder.dll
index 3dbb77f..7226e82 100644
Binary files a/res/tools/darwin/unify_builder/arm64/unify_builder.dll and b/res/tools/darwin/unify_builder/arm64/unify_builder.dll differ
diff --git a/res/tools/darwin/unify_builder/x86_64/unify_builder.deps.json b/res/tools/darwin/unify_builder/x86_64/unify_builder.deps.json
index ab9385a..61cffe7 100644
--- a/res/tools/darwin/unify_builder/x86_64/unify_builder.deps.json
+++ b/res/tools/darwin/unify_builder/x86_64/unify_builder.deps.json
@@ -7,7 +7,7 @@
   "targets": {
     ".NETCoreApp,Version=v6.0": {},
     ".NETCoreApp,Version=v6.0/osx-x64": {
-      "unify_builder/3.9.1": {
+      "unify_builder/3.9.2": {
         "dependencies": {
           "CommandLineParser": "2.9.1",
           "ConsoleTableExt": "3.1.9",
@@ -60,7 +60,7 @@
     }
   },
   "libraries": {
-    "unify_builder/3.9.1": {
+    "unify_builder/3.9.2": {
       "type": "project",
       "serviceable": false,
       "sha512": ""
diff --git a/res/tools/darwin/unify_builder/x86_64/unify_builder.dll b/res/tools/darwin/unify_builder/x86_64/unify_builder.dll
index 21d0d54..bcc5c01 100644
Binary files a/res/tools/darwin/unify_builder/x86_64/unify_builder.dll and b/res/tools/darwin/unify_builder/x86_64/unify_builder.dll differ
diff --git a/res/tools/linux/unify_builder/unify_builder.deps.json b/res/tools/linux/unify_builder/unify_builder.deps.json
index 38f8a50..a571e3a 100644
--- a/res/tools/linux/unify_builder/unify_builder.deps.json
+++ b/res/tools/linux/unify_builder/unify_builder.deps.json
@@ -7,7 +7,7 @@
   "targets": {
     ".NETCoreApp,Version=v6.0": {},
     ".NETCoreApp,Version=v6.0/linux-x64": {
-      "unify_builder/3.9.1": {
+      "unify_builder/3.9.2": {
         "dependencies": {
           "CommandLineParser": "2.9.1",
           "ConsoleTableExt": "3.1.9",
@@ -60,7 +60,7 @@
     }
   },
   "libraries": {
-    "unify_builder/3.9.1": {
+    "unify_builder/3.9.2": {
       "type": "project",
       "serviceable": false,
       "sha512": ""
diff --git a/res/tools/linux/unify_builder/unify_builder.dll b/res/tools/linux/unify_builder/unify_builder.dll
index 21d0d54..bcc5c01 100644
Binary files a/res/tools/linux/unify_builder/unify_builder.dll and b/res/tools/linux/unify_builder/unify_builder.dll differ
diff --git a/res/tools/win32/unify_builder/unify_builder.deps.json b/res/tools/win32/unify_builder/unify_builder.deps.json
index d474f12..faa8b45 100644
--- a/res/tools/win32/unify_builder/unify_builder.deps.json
+++ b/res/tools/win32/unify_builder/unify_builder.deps.json
@@ -7,7 +7,7 @@
   "targets": {
     ".NETCoreApp,Version=v6.0": {},
     ".NETCoreApp,Version=v6.0/win-x64": {
-      "unify_builder/3.9.1": {
+      "unify_builder/3.9.2": {
         "dependencies": {
           "CommandLineParser": "2.9.1",
           "ConsoleTableExt": "3.1.9",
@@ -60,7 +60,7 @@
     }
   },
   "libraries": {
-    "unify_builder/3.9.1": {
+    "unify_builder/3.9.2": {
       "type": "project",
       "serviceable": false,
       "sha512": ""
diff --git a/res/tools/win32/unify_builder/unify_builder.dll b/res/tools/win32/unify_builder/unify_builder.dll
index eababf8..16fa202 100644
Binary files a/res/tools/win32/unify_builder/unify_builder.dll and b/res/tools/win32/unify_builder/unify_builder.dll differ
diff --git a/res/tools/win32/unify_builder/unify_builder.exe b/res/tools/win32/unify_builder/unify_builder.exe
index 191b714..dcdb832 100644
Binary files a/res/tools/win32/unify_builder/unify_builder.exe and b/res/tools/win32/unify_builder/unify_builder.exe differ
diff --git a/src/CmsisConfigParser.ts b/src/CmsisConfigParser.ts
index ecbfd09..0756561 100644
--- a/src/CmsisConfigParser.ts
+++ b/src/CmsisConfigParser.ts
@@ -401,7 +401,7 @@ interface ParserContext {
 
 const fieldMatcher: TagMatcher = {
     'group': { start: /^\/\/\s*<h>\s*(?<name>.+)/, end: /^\/\/\s*<\/h>/ },
-    'section': { start: /^\/\/\s*<e(?:\.(?<var_mod_bit_s>[\d]+))?(?: (?<var_name>\w+))?>\s*(?<name>.+?)(?<desc>(?:\s+[-]+\s*.*)?)$/, end: /^\/\/\s*<\/e>/ },
+    'section': { start: /^\/\/\s*<e(?<var_skip_val>\d+)?(?:\.(?<var_mod_bit_s>[\d]+))?(?: (?<var_name>\w+))?>\s*(?<name>.+?)(?<desc>(?:\s+[-]+\s*.*)?)$/, end: /^\/\/\s*<\/e>/ },
     'tooltip': { start: /^\/\/\s*<i>\s*(?<detail>.+)\s*$/ },
     'defval': { start: /^\/\/\s*<d>\s*(?<var_def_val>.+)\s*$/ },
     'code': { start: /^\/\/\s*<!?c(?<var_skip_val>\d+)?>\s*(?<name>.+?)(?<desc>(?:\s+[-]+\s*.*)?)$/, end: /^\/\/\s*<\/[!]?c>/ },
diff --git a/src/CodeBuilder.ts b/src/CodeBuilder.ts
index aa67585..19ef1c0 100644
--- a/src/CodeBuilder.ts
+++ b/src/CodeBuilder.ts
@@ -128,7 +128,7 @@ export abstract class CodeBuilder {
         this._event.emit(event, arg);
     }
 
-    protected genSourceInfo(prevBuilderParams: BuilderParams | undefined): {
+    protected genSourceInfo(): {
         sources: string[],
         params?: { [name: string]: string; }
     } {
@@ -175,18 +175,6 @@ export abstract class CodeBuilder {
                     const parttenInfo = options?.files;
                     matcher(parttenInfo, 'path');
                 }
-
-                // if src options is modified to null but old is not null,
-                // we need make source recompile
-                if (prevBuilderParams) {
-                    const oldSrcParams = prevBuilderParams.sourceParams;
-                    for (const path in oldSrcParams) {
-                        if (srcParams[path] == undefined && oldSrcParams[path] != undefined &&
-                            oldSrcParams[path] != '') {
-                            srcParams[path] = ""; // make it empty to trigger recompile 
-                        }
-                    }
-                }
             }
 
         } catch (err) {
@@ -375,9 +363,7 @@ export abstract class CodeBuilder {
         const paramsPath = this.project.ToAbsolutePath(outDir + File.sep + this.paramsFileName);
         const compileOptions: BuilderOptions = this.project.GetConfiguration().compileConfigModel.getOptions();
         const memMaxSize = this.getMcuMemorySize();
-        const oldParamsPath = `${paramsPath}.old`;
-        const prevParams: BuilderParams | undefined = File.IsFile(oldParamsPath) ? JSON.parse(fs.readFileSync(oldParamsPath, 'utf8')) : undefined;
-        const sourceInfo = this.genSourceInfo(prevParams);
+        const sourceInfo = this.genSourceInfo();
         const builderModeList: string[] = []; // build mode
 
         const builderOptions: BuilderParams = {
@@ -497,37 +483,6 @@ export abstract class CodeBuilder {
 
         // set build mode
         {
-            /** --------------------------------------------------------------
-             * @note After unify_builder v3.9.0, this function is deprecated 
-             * because we have built-in it in the latest unify_builder
-             * ---------------------------------------------------------------
-             */
-            // // generate hash for compiler options
-            // builderOptions.sha = this.genHashFromCompilerOptions(builderOptions);
-            // // check whether need rebuild project
-            // if (this.isRebuild() == false && prevParams) {
-            //     try {
-            //         // not found hash from old params file
-            //         if (prevParams.sha == undefined) {
-            //             this.enableRebuild();
-            //         }
-
-            //         // check hash obj by specifies keys
-            //         else {
-            //             const keyList = ['global', 'c/cpp-defines', 'c/cpp-compiler', 'asm-compiler'];
-            //             for (const key of keyList) {
-            //                 if (!this.compareHashObj(key, prevParams.sha, builderOptions.sha)) {
-            //                     this.enableRebuild();
-            //                     break;
-            //                 }
-            //             }
-            //         }
-            //     } catch (error) {
-            //         this.enableRebuild(); // make rebuild
-            //         GlobalEvent.emit('msg', ExceptionToMessage(error, 'Hidden'));
-            //     }
-            // }
-
             if (config.toolchain === 'Keil_C51') {
                 builderModeList.push('normal'); // disable increment build for Keil C51
             } else {
diff --git a/src/EIDEProject.ts b/src/EIDEProject.ts
index 7d7be20..2db107f 100644
--- a/src/EIDEProject.ts
+++ b/src/EIDEProject.ts
@@ -2877,12 +2877,20 @@ $(OUT_DIR):
                     }
                 }
             } catch (error) {
+                GlobalEvent.log_warn(<Error>error);
                 const msg = [
                     `${toolchain.getToolchainPrefix()}gcc not avaliable:\n${(<Error>error).message}`,
                     `Please check your package.json and run 'xpm install' to install xpack dependences.`
                 ].join(os.EOL);
-                GlobalEvent.emit('msg', newMessage('Error', msg));
-                GlobalEvent.log_warn(<Error>error);
+                vscode.window.showErrorMessage(msg, 'Install', 'Later')
+                    .then((value) => {
+                        if (value == 'Install') {
+                            runShellCommand('xpm install', 'xpm install', {
+                                useTerminal: true,
+                                cwd: this.getRootDir().path
+                            });
+                        }
+                    });
                 return false
             }
         }
diff --git a/src/ProblemMatcher.ts b/src/ProblemMatcher.ts
index b0a4787..c970557 100644
--- a/src/ProblemMatcher.ts
+++ b/src/ProblemMatcher.ts
@@ -103,38 +103,53 @@ export type CompilerDiagnostics = { [path: string]: vscode.Diagnostic[]; }
 
 export function parseArmccCompilerLog(projApi: ProjectBaseApi, logFile: File): CompilerDiagnostics {
 
-    const pattern = {
-        "regexp": "^\"([^\"]+)\", line (\\d+): (Error|Warning):\\s+#([^\\s]+):\\s+(.+)$",
-        "file": 1,
-        "line": 2,
-        "severity": 3,
-        "code": 4,
-        "message": 5
-    };
+    /* examples:
+        ".\source\main.c", line 68: Error: At end of source:  #67: expected a "}"
+    */
+    const patterns = [
+        {
+            "groupLen": 5,
+            "regexp": /^"([^"]+)", line (\d+): (Error|Warning):\s+#([^\s]+):\s+(.+)$/i,
+            "file": 1,
+            "line": 2,
+            "severity": 3,
+            "code": 4,
+            "message": 5
+        },
+        {
+            "groupLen": 4,
+            "regexp": /^"([^"]+)", line (\d+): (Error|Warning|\w+):\s+(.+)$/i,
+            "file": 1,
+            "line": 2,
+            "severity": 3,
+            "message": 4
+        }
+    ];
 
-    const matcher = new RegExp(pattern.regexp, 'i');
     const result: { [path: string]: vscode.Diagnostic[] } = {};
     const ccLogLines = parseLogLines(logFile);
 
     for (let idx = 0; idx < ccLogLines.length; idx++) {
         const line = ccLogLines[idx];
-        const m = matcher.exec(line);
-        if (m && m.length > 5) {
-
-            const fspath = projApi.toAbsolutePath(m[pattern.file]);
-            const line = parseInt(m[pattern.line]);
-            const severity = m[pattern.severity];
-            const errCode = m[pattern.code].trim();
-            const message = m[pattern.message].trim();
-
-            const diags = result[fspath] || [];
-            if (result[fspath] == undefined) result[fspath] = diags;
-
-            const vscDiag = new vscode.Diagnostic(
-                newVscFileRange(line, 0, 10), message, toVscServerity(severity));
-            vscDiag.code = errCode;
-            vscDiag.source = 'armcc';
-            diags.push(vscDiag);
+        for (const pattern of patterns) {
+            const m = pattern.regexp.exec(line);
+            if (m && m.length > pattern.groupLen) {
+                const fspath = projApi.toAbsolutePath(m[pattern.file]);
+                const line = parseInt(m[pattern.line]);
+                const severity = m[pattern.severity];
+                const errCode = pattern.code ? m[pattern.code].trim() : undefined;
+                const message = m[pattern.message].trim();
+
+                const diags = result[fspath] || [];
+                if (result[fspath] == undefined) result[fspath] = diags;
+
+                const vscDiag = new vscode.Diagnostic(
+                    newVscFileRange(line, 0, 10), message, toVscServerity(severity));
+                vscDiag.code = errCode;
+                vscDiag.source = 'armcc';
+                diags.push(vscDiag);
+                break;
+            }
         }
     }
 
diff --git a/src/ToolchainManager.ts b/src/ToolchainManager.ts
index 327367b..52ece6b 100644
--- a/src/ToolchainManager.ts
+++ b/src/ToolchainManager.ts
@@ -40,6 +40,7 @@ import * as os from 'os';
 import { ArmBaseBuilderConfigData, ArmBaseCompileData } from "./EIDEProjectModules";
 import * as utility from "./utility";
 
+//! 名称应该是大写,但由于历史因素,其中 'Keil_C51' 大小写暂时无法更正(避免旧的项目出现问题) 
 export type ToolchainName =
     'SDCC' | 'Keil_C51' | 'IAR_STM8' | 'GNU_SDCC_STM8' | 'COSMIC_STM8' |
     'AC5' | 'AC6' | 'GCC' | 'IAR_ARM' |
@@ -223,21 +224,21 @@ export class ToolchainManager {
 
         switch (prjType) {
             case 'RISC-V':
-                res = this.toolchainMap.get('RISCV_GCC');
+                res = this.getToolchainByName('RISCV_GCC');
                 break;
             case 'ANY-GCC':
-                res = this.toolchainMap.get('ANY_GCC');
+                res = this.getToolchainByName('ANY_GCC');
                 break;
             case 'ARM':
                 switch (toolchainName) {
                     case 'None':
-                        res = this.toolchainMap.get('AC5');
+                        res = this.getToolchainByName('AC5');
                         break;
                     default:
                         if (this.toolchainNames['ARM'].includes(toolchainName)) {
-                            res = this.toolchainMap.get(toolchainName);
+                            res = this.getToolchainByName(toolchainName);
                         } else {
-                            res = this.toolchainMap.get('AC5');
+                            res = this.getToolchainByName('AC5');
                             GlobalEvent.emit('msg', newMessage('Warning',
                                 'Invalid toolchain name \'' + toolchainName + '\' !, use default toolchain.'));
                         }
@@ -246,21 +247,21 @@ export class ToolchainManager {
             case 'C51':
                 switch (toolchainName) {
                     case 'None':
-                        res = this.toolchainMap.get('Keil_C51');
+                        res = this.getToolchainByName('Keil_C51');
                         break;
                     default:
                         if (this.toolchainNames['C51'].includes(toolchainName)) {
-                            res = this.toolchainMap.get(toolchainName);
+                            res = this.getToolchainByName(toolchainName);
                         }
                         else {
-                            res = this.toolchainMap.get('Keil_C51');
+                            res = this.getToolchainByName('Keil_C51');
                             GlobalEvent.emit('msg', newMessage('Warning',
                                 'Invalid toolchain name \'' + toolchainName + '\' !, use default toolchain.'));
                         }
                 }
                 break;
             case 'MIPS':
-                res = this.toolchainMap.get('MTI_GCC');
+                res = this.getToolchainByName('MTI_GCC');
                 break;
             default:
                 throw new Error('Invalid project type \'' + prjType + '\'');
@@ -274,6 +275,9 @@ export class ToolchainManager {
     }
 
     getToolchainByName(name: ToolchainName): IToolchian | undefined {
+        //! 'Keil_C51' 大小写问题的补丁
+        if (name.toLowerCase() == 'keil_c51')
+            return this.toolchainMap.get('Keil_C51');
         return this.toolchainMap.get(name);
     }
 
@@ -426,7 +430,7 @@ export class ToolchainManager {
     }
 
     getToolchainPrefix(toolchainName: ToolchainName): string | undefined {
-        const toolchain = this.toolchainMap.get(toolchainName);
+        const toolchain = this.getToolchainByName(toolchainName);
         if (toolchain && toolchain.getToolchainPrefix) {
             return toolchain.getToolchainPrefix();
         }
@@ -532,6 +536,121 @@ class KeilC51 implements IToolchian {
         }
     }
 
+    /*
+    Program Size: data=82.4 xdata=0 const=19 code=1644
+    */
+    private _parseMap(mapPath: string): {
+        sections: string[],
+        objDic: {
+            [name: string]: { [section: string]: number }
+        },
+    } {
+
+        const objDic: any = {};
+        const secList: string[] = [ 'Size' ];
+
+        const lines = fs.readFileSync(mapPath).toString().split(/\r\n|\n/);
+        for (const line of lines) {
+            const m = /Program Size: data=(?<data>[\d+\.]+) xdata=(?<xdata>\d+) const=(?<const>\d+) code=(?<code>\d+)/.exec(line);
+            if (m && m.groups) {
+                for (const key in m.groups) {
+                    const val = m.groups[key];
+                    const size = val.includes('.') ? parseFloat(val) : parseInt(val);
+                    if (!objDic[key]) objDic[key] = {};
+                    objDic[key]['Size'] = size;
+                }
+                break;
+            }
+        }
+
+        return {
+            sections: secList,
+            objDic: objDic
+        };
+    };
+
+    parseMapFile(mapPath: string): string[] | Error {
+
+        if (!File.IsFile(mapPath))
+            return new Error(`No such file: ${mapPath}`);
+
+        const mapInfo = this._parseMap(mapPath);
+        const secList = mapInfo.sections;
+        const objDic = mapInfo.objDic;
+
+        let oldObjDic: any = {};
+        if (File.IsFile(mapPath + '.old')) {
+            const inf = this._parseMap(mapPath + '.old');
+            oldObjDic = inf.objDic;
+        }
+
+        const tableRows: string[][] = [];
+
+        // push header
+        let header: string[] = [];
+        header.push('Section');
+        header = header.concat(secList);
+        tableRows.push(header);
+
+        let objTotalSize: any = { new: 0, old: 0 };
+        let secTotalSize: any = {};
+        for (const objpath in objDic) {
+
+            const objInfo = objDic[objpath];
+            const row: string[] = [objpath];
+
+            let totalSize = 0;
+            for (const key in objInfo) {
+                totalSize += objInfo[key];
+            }
+
+            let oldInfo: any = {};
+            if (oldObjDic[objpath]) {
+                oldInfo = oldObjDic[objpath];
+            }
+
+            let oldTotalSize = 0;
+            for (const key in oldInfo) {
+                oldTotalSize += oldInfo[key];
+            }
+
+            objTotalSize.new += totalSize;
+            objTotalSize.old += oldTotalSize;
+
+            for (const sec of secList) {
+
+                const oldSecSize = oldInfo[sec] ? oldInfo[sec] : 0;
+                const nowSecSize = objInfo[sec] ? objInfo[sec] : 0;
+
+                const diffSize = nowSecSize - oldSecSize;
+                if (diffSize.toString().indexOf(".") != -1) {
+                    row.push(nowSecSize.toString() + `(${diffSize > 0 ? '+' : ''}${diffSize.toFixed(1)})`);
+                } else {
+                    row.push(nowSecSize.toString() + `(${diffSize > 0 ? '+' : ''}${diffSize.toString()})`);
+                }
+
+                if (secTotalSize[sec] == undefined) {
+                    secTotalSize[sec] = {
+                        new: 0,
+                        old: 0
+                    };
+                };
+
+                secTotalSize[sec].new += nowSecSize;
+                secTotalSize[sec].old += oldSecSize;
+            }
+
+            tableRows.push(row);
+        }
+
+        const tableLines = utility.makeTextTable(tableRows);
+        if (tableLines == undefined) {
+            return new Error(`Nothing for this map: ${mapPath} !`);
+        }
+
+        return tableLines;
+    }
+
     getInternalDefines<T extends BuilderConfigData>(builderCfg: T, builderOpts: BuilderOptions): string[] {
         return [];
     }
@@ -706,6 +825,135 @@ class SDCC implements IToolchian {
         options["asm-compiler"]['$toolName'] = asmName;
     }
 
+    /*
+        Area                                    Addr        Size        Decimal Bytes (Attributes)
+        --------------------------------        ----        ----        ------- ----- ------------
+        GSINIT0                             00000006    00000003 =           3. bytes (REL,CON,CODE)
+
+            Value  Global                              Global Defined In Module
+            -----  --------------------------------   ------------------------
+        C:   00000006  __sdcc_gsinit_startup              
+    */
+    private _parseMap(mapPath: string): {
+        sections: string[],
+        objDic: {
+            [name: string]: { [section: string]: number }
+        },
+    } {
+
+        const objDic: any = {};
+        const secList: string[] = [ 'Size' ];
+
+        const lines = fs.readFileSync(mapPath).toString().split(/\r\n|\n/);
+        let counter = 0;
+        for (const line of lines) {
+            if (counter == 2) {
+                const m = /^\s*(?<name>\w+)\s+(?<addr>(?:0x)?[a-f\d]+)\s+(?<size>(?:0x)?[a-f\d]+)\s+=\s+(?<size_dec>\d+)\./i.exec(line);
+                if (m && m.groups) {
+                    const name = m.groups['name'];
+                    const size_dec = m.groups['size_dec'];
+                    if (size_dec) {
+                        if (!objDic[name]) objDic[name] = {};
+                        objDic[name]['Size'] = parseInt(size_dec);
+                    }
+                }
+                counter = 0;
+            } else if (counter == 1) {
+                if (/^\s*----/.test(line))
+                    counter = 2;
+                else
+                    counter = 0;
+            } else {
+                if (/^\s*Area\s+Addr/.test(line))
+                    counter = 1;
+                else
+                    counter = 0;
+            }
+        }
+
+        return {
+            sections: secList,
+            objDic: objDic
+        };
+    };
+
+    parseMapFile(mapPath: string): string[] | Error {
+
+        if (!File.IsFile(mapPath))
+            return new Error(`No such file: ${mapPath}`);
+
+        const mapInfo = this._parseMap(mapPath);
+        const secList = mapInfo.sections;
+        const objDic = mapInfo.objDic;
+
+        let oldObjDic: any = {};
+        if (File.IsFile(mapPath + '.old')) {
+            const inf = this._parseMap(mapPath + '.old');
+            oldObjDic = inf.objDic;
+        }
+
+        const tableRows: string[][] = [];
+
+        // push header
+        let header: string[] = [];
+        header.push('Section');
+        header = header.concat(secList);
+        tableRows.push(header);
+
+        let objTotalSize: any = { new: 0, old: 0 };
+        let secTotalSize: any = {};
+        for (const objpath in objDic) {
+
+            const objInfo = objDic[objpath];
+            const row: string[] = [objpath];
+
+            let totalSize = 0;
+            for (const key in objInfo) {
+                totalSize += objInfo[key];
+            }
+
+            let oldInfo: any = {};
+            if (oldObjDic[objpath]) {
+                oldInfo = oldObjDic[objpath];
+            }
+
+            let oldTotalSize = 0;
+            for (const key in oldInfo) {
+                oldTotalSize += oldInfo[key];
+            }
+
+            objTotalSize.new += totalSize;
+            objTotalSize.old += oldTotalSize;
+
+            for (const sec of secList) {
+
+                const oldSecSize = oldInfo[sec] ? oldInfo[sec] : 0;
+                const nowSecSize = objInfo[sec] ? objInfo[sec] : 0;
+                const diffSize = nowSecSize - oldSecSize;
+                row.push(nowSecSize.toString() + `(${diffSize > 0 ? '+' : ''}${diffSize.toString()})`);
+
+                if (secTotalSize[sec] == undefined) {
+                    secTotalSize[sec] = {
+                        new: 0,
+                        old: 0
+                    };
+                };
+
+                secTotalSize[sec].new += nowSecSize;
+                secTotalSize[sec].old += oldSecSize;
+            }
+
+            tableRows.push(row);
+        }
+
+        const tableLines = utility.makeTextTable(tableRows);
+        if (tableLines == undefined) {
+            return new Error(`Nothing for this map: ${mapPath} !`);
+        }
+
+        return tableLines;
+    }
+
     private parseCodeModel(conf: string): string | undefined {
         const mType = /\s*--model-(\w+)\s*/i.exec(conf);
         if (mType && mType.length > 1) {
diff --git a/src/extension.ts b/src/extension.ts
index 95a0e0a..cd24e07 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1728,8 +1728,10 @@ class MapViewEditorProvider implements vscode.CustomTextEditorProvider {
                     for (let index = 0; index < lines.length; index++) {
                         const line = lines[index];
                         lines[index] = line
-                            .replace(/\((\+[^0]\d*)\)/g, `(<span class="success">$1</span>)`)
-                            .replace(/\((\-[^0]\d*)\)/g, `(<span class="error">$1</span>)`)
+                            .replace(/\((\+[^0][\d\.]*)\)/g, `(<span class="success">$1</span>)`)
+                            .replace(/\((\-[^0][\d\.]*)\)/g, `(<span class="error">$1</span>)`)
+                            .replace(/\((\+0\.\d+)\)/g, `(<span class="success">$1</span>)`)
+                            .replace(/\((\-0\.\d+)\)/g, `(<span class="error">$1</span>)`)
                             .replace(/^(\s*\|\s*)(Subtotals)/, `$1<span class="info">$2</span>`)
                             .replace(/^(\s*Total)/, `\n$1`);
                     }