Keil C51 特定功能
1. 如何產生 'RLC A' 指令: (Keil C51 適用)
CY = ACC & 0X80;
那 RRC A 呢? 還沒找到, 只能先用 {asm}
2. 如何把 C51 程式移值到別的 CPU 架構?
在 header file 中定義類似以下的 define, 同時程式中只能用這些定義 (也就是把程式中的 data, xdata... 先都改成 _DATA_, _XDATA_, ...). 因為別的 CPU 沒有這些 '記憶體空間修飾字'.
註:
- 我把 _BIT_ 定義成: 不是用 KEIL_C51 時轉成 BOOL 但使用 KEIL_C51 時, 對應到 bit. 建議你在 C51 的程式中 (變數或函式回傳值) 明確的區分 _BIT_ 和 BOOL, 因為用 bit 可以讓你的程式小一些, 也快一些. (但是 bit 只有 128 個, 所以要區分一下)
- reentrant 是 Keil C51 特有的修飾字.
typedef unsigned char BOOL
// While the compiler is not Keil C51,
// Comment out the following line
#define KEIL_C51
#ifdef KEIL_C51
#define _BIT_ bit
#define _BDATA_ bdata
#define _DATA_ data
#define _IDATA_ idata
#define _XDATA_ xdata
#define _CODE_ code
#define _FAR_ far
#define _REENTRANT_ reentrant
#else
#define _BIT_ BOOL
#define _BDATA_
#define _DATA_
#define _IDATA_
#define _XDATA_
#define _CODE_
#define _FAR_
#define _REENTRANT_
#endif
3. 如何消除 Keil C51 對未使用之函數參數之告警
無法以修改原型宣告的方式來取消函數之未用參數, 但又想消除 Keil C51 編譯它所產生的告警時, 可以用如下的方法:
#define UNUSED(x) if(x){}
void f(int x, int y) {
UNUSED(x); // x 是沒有用到的參數
...
}
4. 如何指定 Keil C51 指標變數 所使用的記憶體空間
由於 8051 的記憶體可分為 程式空間(code space)及資料空間(data space), 資料空間又有
- 外部記憶體
- 可直接定址的內部暫存器
- 只能間接定址的內部暫存器
- 特殊功能暫存器
你可以依據實際上的需要把變數安置在不同的記憶體中. 所以,定義指標變數來指向這些變數時問題會有一點複雜.
一般變數的宣告語法:
Data_type [Variable_Memory_Space] Variable_name;
指標變數的宣告語法:
Data_type [Data_type_Memory_Space] * [Variable_Memory_Space] Variable_name;
Example:
Keil C51 指定指標變數所使用的記憶體空間
[Data_type_Memory_Space]及[Variable_Memory_Space]是多出來的記憶體空間修飾字, 一般的 C 語言沒有這二個修飾字(所以需要移值到別的 CPU 需要利用上面第二點所提的方法)
- [Data_type_Memory_Space] 是指定指標變數指到哪一種記憶體空間
- [Variable_Memory_Space] 是指定(指標/一般)變數本身所在的記憶體空間
所以, 我們可以依據需要把上面的 [Data_type_Memory_Space] 及 [Variable_Memory_Space] 換成下表中的修飾字, 或者是空白. 如果是用空白的話, 就是使用預設值. 各種變數依照你使用的記憶體模型 (memory model) 不同, 有不同的預設值. 另外, 如果 [Data_type_Memory_Space] 是空白的話, 意指該指標變數是通用指標, 也就是這個指標可以指向任何一種記憶體空間.
Keil C51 所支援的記憶體空間修飾字:
| 修飾字 | 範圍 | 說明 |
|---|---|---|
| code | 64KB | accessed by MOVC @A+DPTR, program memory |
| data | 128B | direct accessable, internal data memory |
| idata | 256B | indirect accessable, internal data memory, access by MOV @Rn |
| bdata | 16B | bit-addressable internal data memory |
| xdata | 64KB | external data memory |
| far | 16MB | extended RAM/ROM memory (DS390擴充) |
| pdata | 256B | accessed by MOVX @Rn, external data memory |
| model | 函數參數及 |
預設 |
預設 |
預設指標 |
|---|---|---|---|---|
| small | data | data | data | 3 Bytes |
| compact | pdata | pdata | pdata | 3 Bytes |
| large | xdata | xdata | xdata | 3 Bytes |
| 類別 | 有效定址範圍 | SIZE |
|---|---|---|
| BIT | I:000020h.0~I:00002Fh.7 | 128bit |
| CODE | C:000000h ~C:00FFFFh | 64KB |
| CONST | C:000000h ~C:00FFFFh | 64KB |
| SROM | C:000000h ~C:00FFFFh | 64KB |
| ECODE | C:000000h ~C:FFFFFFh | 16MB |
| HCONST | C:000000h ~C:FFFFFFh | 16MB |
| DATA | D:000000h ~D:00007Fh | 128B |
| IDATA | D:000000h ~D:0000FFh | 256B |
| XDATA | X:000000h ~X:00FFFFh | 64KB |
| HDATA | X:000000h ~X:FFFFFFh | 16MB |
| 語法 | 變數指向 | 變數存放在 |
|---|---|---|
| unsigned char xdata * data ptr; | 外部記憶體 | 內部記憶體 |
| unsigned char data * xdata ptr; | 內部記憶體 | 外部記憶體 |
| unsigned char code * xdata ptr; | 程式記憶體 | 外部記憶體 |
| unsigned char code * code ptr; | 程式記憶體 | 程式記憶體,唯讀,必須先設定初始值。 |
| 通用指標用法:(generic pointer) | ||
| unsigned char * xdata ptr; | 任何記憶體 | 外部記憶體 |
| unsigned char * ptr; | 任何記憶體 | 由 memory model 決定 |
| 函數指標變數用法:只能指向程式記憶體空間。 | ||
| char (* data fptr)(void); | 程式記憶體 | 內部記憶體 |
| 備註: 由於內部記憶體指標讀取都是使用間接定址 (indirect addressing),所以無法存取 SFR 空間的記憶體內容。 |
指標變數佔用的記憶體大小是由其所指向的記憶體空間決定
| 指標變數指向 | 大小 |
|---|---|
| data | 1 Byte |
| xdata | 2 Bytes |
| code | 2 Bytes |
| generic pointer | 3 Bytes |
generic pointer 是設計用來可以做通用指標變數使用,可以任意變換指標指向任何一種位址空間 (data, idata, xdata, hdata, code, ecode, ...),但是相對的需要佔用 3 個 bytes 的位置,而且在做讀取寫入時,需要另外執行一個函數來轉換位址,沒必要就少用一點吧。

您好,我想請問一個問題,我有看過以下這種寫法: unsigned char code * ptr; 意思是指標指向程式記憶體,但指標變數本身存在任何記憶體? 另外若是 unsigned char code ptr,這意思是變數本身存在程式記憶體?
Q1 的答案要依據你設定的記憶體模型而定: small: data compact: pdata large: xdata 你可以從 compiler/linker 輸出的 map 檔看到每一個變數被放在哪一個區域的哪一個地址. (好像要開啟 cross reference 旗標) Q2: 是的. 一般 compiler 輸出 (在未指定區塊名稱時) 只決定整體變數放在 .bss, .data, .text 中的哪一區塊. * 沒有 const, 未指定初值: 放在 .bss * 沒有 const, 有指定初值: 放在 .data * 有 const: 放在 .text linker 才會真的決定區塊放在哪一個位置. 不過 51 的 cmpiler/linker 相對陽春 (應該是年代久遠一直沒有重大更新), 所以只有在變數前加上 'code' 這個 keyword 才會真的把變數放在 '程式記憶體' 然後用 MOVC @A+DPTR 去把資料取出. 同時該變數自動變成唯讀的 constant.
請教 _at_該怎麼修改?
"_at_" 是 keil C51 Compiler 特有的 key word, 一般是用來定義特別的地址. (可以是硬體相關 或者 軟體相關也 OK) 硬體相關: 一般我們都是直接用廠商定義好的地址, 而不會去修改 _at_ 相關的地址的定義. 例如: (from Keil C51\INC\Cypress\FX2regs.H) EXTERN xdata volatile BYTE CPUCS _at_ 0xE600; // Control & Status 軟體相關: 一般也是用不到, 除非是程式分成二個專案, 個自分開編譯, 但二者之間又需要傳遞參數, 在 linker 無法幫忙之下只好用 _at_ 定義經由 "固定的特別的地址" 來傳遞參數. 例如: U8_T IDATA CONFIG_UPDATE _at_ 0x7F; 所以你說 "該怎麼修改" ? 因為不知道你的意圖, 所以我也不知道該怎麼修改? 我上次修改 _at_ 相關的定義只是 type casting 所以也不算是修改
謝謝您的回覆,抱歉沒說清楚,是對應文章中的主題2,請教該如何在其他cpu定義 _at_?
其他 CPU ? 一般這一部份是沒辦法直接改的. 你需要讀懂它的用途: 如果是硬體相關的, 那就看新的 CPU 有沒有相關的硬體可以用, 然後用新 CPU 的驅動程式重新 '包裝' (一般是重寫啦) 一份舊的驅動程式. (這樣可以只改驅動程式不必修改主程式) 或者新定義一份 API, 整份程式改用新的 API. 再依不同的 CPU 實作 API 出來. 如果是軟體相關的那就拿掉重寫吧! 因為大部份狀況是你沒有另一部份的原始程式 (要有的話就不會用這種方式整合了), 所以你也沒辦法直接移植到新的 CPU 上.