TechQI 发布的文章

__xdata volatile int var0;
__xdata volatile int var1 = 1;

void main(void) {
    // 功能代码
}

以上这个简单到发指的程序,在STC8A8K64U中遇到个奇怪问题:var0的初始值有时候不是0,而var1的初始值也不是1,是一些奇怪的随机值。
第一感觉就是XRAM的初始化可能存在BUG,于是稍微了解了一下SDCC中XRAM初始化的原理,没想到还真发现了一些好玩的东西,给大家做个分享。

一些前置知识。

  1. 无论是Keil还是SDCC,在真正执行main函数之前,会自动插入一些初始化代码,这里面就包括RAM、IRAM和XRAM的初始化等。
  2. 在从C编译成汇编的过程中,编译器会按照功能和特性的不同,会生成很多辅助的段或者叫做小节。与本问题相关的小节主要有:
;--------------------------------------------------------
; uninitialized external ram data
;--------------------------------------------------------
    .area XSEG    (XDATA)
_var0::
    .ds 2
;--------------------------------------------------------
; initialized external ram data
;--------------------------------------------------------
    .area XISEG   (XDATA)
_var1::
    .ds 2
;--------------------------------------------------------
; external ram initial data
;--------------------------------------------------------
    .area XINIT   (CODE)
__xinit__var1:
    .byte #0x01, #0x00;

未初始化外部变量存储区XSEG,var0就属于这个区,因为它定义在外部扩展RAM中(__xdata),且代码中没有给它赋初始值;
已初始化外部变量存储区XISEG,var1就属于这个区,因为它定义在外部扩展RAM中(__xdata),且代码中有给它赋初始值;
已初始化外部变量初始值区XINIT,注意这个实际上是在代码区里面,是ROM不是RAM,最终会烧录在flash的特定位置。
另外,在编译过程中会生成一些辅助变量,s_是特定区的起始位置,l_是特定区的长度。如s_XSEG就是XSEG区的起始位置。

按照C语言规范,未初始化的全局变量(如var0)应当默认给初始值0,这个SDCC帮我们做了,代码在crtxclear.asm中

    .globl __XPAGE

__mcs51_genXRAMCLEAR::
    mov    r0,#l_PSEG
    mov    a,r0
    orl    a,#(l_PSEG >> 8)
    jz    00006$
    mov    r1,#s_PSEG
    mov    __XPAGE,#(s_PSEG >> 8)
    clr     a
00005$:    movx    @r1,a
    inc    r1
    djnz    r0,00005$

00006$:
    mov    r0,#l_XSEG
    mov    a,r0
    orl    a,#(l_XSEG >> 8)
    jz    00008$
    mov    r1,#((l_XSEG + 255) >> 8)
    mov    dptr,#s_XSEG
    clr     a
00007$:    movx    @dptr,a
    inc    dptr
    djnz    r0,00007$
    djnz    r1,00007$
00008$:

已初始化的全局变量(如var1),也需要执行一些操作才能有初始值,这个SDCC帮我们做了,代码在crtxinit.asm中

    .globl __XPAGE

__mcs51_genXINIT::
    mov    r1,#l_XINIT
    mov    a,r1
    orl    a,#(l_XINIT >> 8)
    jz    00003$
    mov    r2,#((l_XINIT+255) >> 8)
    mov    dptr,#s_XINIT
    mov    r0,#s_XISEG
    mov    __XPAGE,#(s_XISEG >> 8)
00001$:    clr    a
    movc    a,@a+dptr
    movx    @r0,a
    inc    dptr
    inc    r0
    cjne    r0,#0,00002$
    inc    __XPAGE
00002$:    djnz    r1,00001$
    djnz    r2,00001$
    mov    __XPAGE,#0xFF
00003$:

其实这里就已经能发现问题了,当给XRAM内存赋值时,使用的地址寄存是R0和R1。而R0和R1只是一个8位的寄存器,还需要其它方式组合出16位地址,才能完全访问STC8A8K64U完整8KB的XRAM。从代码里面看到使用__XPAGE做了高8位地址,那么就继续追查__XPAGE是个什么东西。最终是在crtpagesfr.asm里面

__XPAGE == 0xa0    ; 0xa0 is P2 on the original 8051

这个__XPAGE不就是P2端口么?所有问题豁然开朗。
在传统8052单片机中,如果要扩展RAM,是需要实实在在外接RAM芯片的。P2接RAM芯片的地址高8位,P0口接RAM芯片地址低8位和数据位。SDCC默认的实现正是基于这一点,用__XPAGE(也就是P2)锁存了XRAM地址高8位,然后只需要操作地址低8位和数据即可(都是P0)。
这在当时的硬件下应该能减少端口操作提升效率,但这并不适合现在STC系列的所有单片机,因为STC的XRAM其实也是内置的,和P2、P0没有丝毫关系,再按当年的方式来操作必然不能正确生效了。

到这里解决方案其实已经呼之欲出了,重写SDCC内置的方法就能解决问题。已知需要重写的有crtxclear.asm、crtxinit.asm和crtxstack.asm,这个等以后有空了再说吧!

执行激活脚本

slmbr.vbs /xpr
slmgr.vbs /upk
slmgr.vgs /ipk  {GVLK}
slmgr.vbs /skms {ADDR}
slmgr.vbs /ato

说明:

  1. 将脚本中的{GVLK}按系统版本替换成KMS密钥
  2. 将脚本中的{ADDR}替换成KMS服务器地址

通用批量许可证密钥 (GVLK)

在下表中,可找到 Windows 每个版本的 GVLK。 LTSC 是长期服务渠道,而 LTSB 是 Long-Term Servicing Branch 。

Windows Server(LTSC 版本)

Windows Server 2022

操作系统版本KMS 客户端产品密钥
Windows Server 2022 DatacenterWX4NM-KYWYW-QJJR4-XV3QB-6VM33
Windows Server 2022 Datacenter Azure EditionNTBV8-9K7Q8-V27C6-M2BTV-KHMXV
Windows Server 2022 StandardVDYBN-27WPP-V4HQT-9VMD4-VMK7H

Windows Server 2019

操作系统版本KMS 客户端产品密钥
Windows Server 2019 DatacenterWMDGN-G9PQG-XVVXX-R3X43-63DFG
Windows Server 2019 StandardN69G4-B89J2-4G8F4-WWYCC-J464C
Windows Server 2019 EssentialsWVDHN-86M7X-466P6-VHXV7-YY726

Windows Server 2016

操作系统版本KMS 客户端产品密钥
Windows Server 2016 DatacenterCB7KF-BWN84-R7R2Y-793K2-8XDDG
Windows Server 2016 StandardWC2BQ-8NRM3-FDDYY-2BFGV-KHKQY
Windows Server 2016 EssentialsJCKRF-N37P4-C2D82-9YXRT-4M63B

Windows 11 和 Windows 10(半年频道版本)

有关受支持的版本和服务终止日期的信息,请参阅 Windows 生命周期情况说明书

操作系统版本KMS 客户端产品密钥
Windows 11 专业版 Windows 10 专业版W269N-WFGWX-YVC9B-4J6C9-T83GX
Windows 11 专业版 N Windows 10 专业版 NMH37W-N47XK-V7XM9-C7227-GCQG9
Windows 11 专业工作站版 Windows 10 专业工作站版NRG8B-VKK3Q-CXVCJ-9G2XF-6Q84J
Windows 11 专业工作站版 N Windows 10 专业工作站版 N9FNHH-K3HBT-3W4TD-6383H-6XYWF
Windows 11 专业教育版 Windows 10 专业教育版6TP4R-GNPTD-KYYHQ-7B7DP-J447Y
Windows 11 专业教育版 N Windows 10 专业教育版 NYVWGF-BXNMC-HTQYQ-CPQ99-66QFC
Windows 11 教育版 Windows 10 教育版NW6C2-QMPVW-D7KKK-3GKT6-VCFB2
Windows 11 教育版 N Windows 10 教育版 N2WH4N-8QGBV-H22JP-CT43Q-MDWWJ
Windows 11 企业版 Windows 10 企业版NPPR9-FWDCX-D2C8J-H872K-2YT43
Windows 11 企业版 N Windows 10 企业版 NDPH2V-TTNVB-4X9Q3-TJR4H-KHJW4
Windows 11 企业版 G Windows 10 企业版 GYYVX9-NTFWV-6MDM3-9PT4T-4M68B
Windows 11 企业版 G N Windows 10 企业版 G N44RPN-FTY23-9VTTB-MP9BX-T84FV

Windows 10(LTSC/LTSB 版本)

Windows 10 LTSC 2021 和 2019

操作系统版本KMS 客户端产品密钥
Windows 10 企业版 LTSC 2021 Windows 10 企业版 LTSC 2019M7XTQ-FN8P6-TTKYV-9D4CC-J462D
Windows 10 企业版 N LTSC 2021 Windows 10 企业版 N LTSC 201992NFX-8DJQP-P6BBQ-THF9C-7CG2H

Windows 10 LTSB 2016

操作系统版本KMS 客户端产品密钥
Windows 10 企业版 LTSB 2016DCPHK-NFMTC-H88MJ-PFHPY-QJ4BJ
Windows 10 企业版 N LTSB 2016QFFDN-GRT3P-VKWWX-X7T3R-8B639

Windows 10 LTSB 2015

操作系统版本KMS 客户端产品密钥
Windows 10 企业版 2015 LTSBWNMTR-4C88C-JK8YV-HQ7T2-76DF9
Windows 10 企业版 2015 LTSB N2F77B-TNFGY-69QQF-B8YKP-D69TJ

{
    "Dhcp4": {
        "server-tag": "DHCPv4-A",
        "interfaces-config": {
            "interfaces": [
                "enpXsY"
            ],
            "dhcp-socket-type": "raw"
        },
        "control-socket": {
            "socket-type": "unix",
            "socket-name": "/tmp/kea-dhcp4-ctrl.sock"
        },
        "hooks-libraries": [
            {
                "library": "/usr/lib64/kea/hooks/libdhcp_mysql_cb.so"
            }
        ],
        "config-control": {
            "config-databases": [
                {
                    "type": "mysql",
                    "name": "kea",
                    "user": "kea",
                    "password": "******",
                    "host": "host",
                    "port": 3306
                }
            ],
            "config-fetch-wait-time": 20
        },
        "lease-database": {
            "type": "mysql",
            "name": "kea",
            "user": "kea",
            "password": "******",
            "host": "host",
            "port": 3306
        },
        "hosts-database": {
            "type": "mysql",
            "name": "kea",
            "user": "kea",
            "password": "******",
            "host": "host",
            "port": 3306
        },
        "loggers": [
            {
                "name": "kea-dhcp4",
                "output_options": [
                    {
                        "output": "/var/log/kea/dhcp4.log"
                    }
                ],
                "severity": "INFO",
                "debuglevel": 0
            }
        ]
    }
}

dnf install epel-release
dnf config-manager --set-enabled crb

dnf install cmake automake autoconf g++ libtool flex
dnf install openssl-devel libdnet-devel hwloc-devel luajit-devel libpcap-devel pcre-devel xz-devel zlib-devel

git clone https://github.com/snort3/libdaq.git
./bootstrap
./configure
make install
ldconfig


git clone https://github.com/snort3/snort3.git
./configure_cmake.sh
cd build
make -j $(nproc)
make install