查看: 3119|回复: 8

[已解决] 关于AC128分页的一个疑问(已解决)

[复制链接]

该用户从未签到

196

主题

495

帖子

0

金牌会员

Rank: 6Rank: 6

积分
1482
最后登录
2020-12-3
发表于 2013-8-6 11:22:36 | 显示全部楼层 |阅读模式
之前用AC128时,遇到一个问题,这是工程的prm文件。下面是我有疑问的一段flash定义
 ROM                      =  READ_ONLY    0x20F0 TO 0x7FFF;
 PPAGE_0                  =  READ_ONLY    0x008000 TO 0x00A0EF; /* PAGE partially contained in ROM segment */
疑问就在这个注视上PAGE partially contained in ROM segment
这页和ROM是部分重合的吗?手册上有说吗?
这个重合是不是说 0x20F0 TO 0x7FFF 与0x008000 TO 0x00A0EF两段地址指向同一块flash?
而且,在实际应用中也出现过问题,当ROM和PPAGE_0都放程序的时候,程序跑不起来。
后来在prm中注视掉PPAGE_0,程序就可以跑起来了。
 
如果确实像我说的这样,为什么要这样设计呢?不是自找麻烦吗?
 
 
 
 
 
 
 
 
我知道答案 目前已有7人回答
回复

使用道具 举报

该用户从未签到

3

主题

18

帖子

0

注册会员

Rank: 2

积分
90
最后登录
1970-1-1
发表于 2013-8-6 16:04:17 | 显示全部楼层

RE:关于AC128分页的一个疑问

你好,
这种flash 大于64K的MCU, flash的访问模式有两种:分页模式和非分页模式。在一个项目中只能确定用一种模式来访问flash,在新建工程的时候会有一个选项来选定, 编译器会根据这个选项采用不同的地址译码。
如果在一个工程中指定用非分布模式却用了分页的地址那就会出错。
在您的描述中,前面ROM的定义地址是非分页模式的地址,实际是指向了前两页的flash,后面的ppage_0是分页模式地址,实际指向一部分第0页flash,所以在后面有注释说有部分重合。
不知道我的解释是否清楚? 有问题我们继续讨论。
回复 支持 反对

使用道具 举报

该用户从未签到

196

主题

495

帖子

0

金牌会员

Rank: 6Rank: 6

积分
1482
最后登录
2020-12-3
 楼主| 发表于 2013-8-6 16:55:30 | 显示全部楼层

回复:关于AC128分页的一个疑问

回复第 2 楼 于2013-08-06 16:04:17发表:
你好,
这种flash 大于64K的MCU, flash的访问模式有两种:分页模式和非分页模式。在一个项目中只能确定用一种模式来访问flash,在新建工程的时候会有一个选项来选定, 编译器会根据这个选项采用不同的地址译码。
如果在一个工程中指定用非分布模式却用了分页的地址那就会出错。
在您的描述中,前面ROM的定义地址是非分页模式的地址,实际是指向了前两页的flash,后面的ppage_0是分页模式地址,实际指向一部分第0页flash,所以在后面有注释说有部分重合。
不知道我的解释是否清楚? 有问题我们继续讨论。
 

你好,
首先,我在新建工程时,并没有看到有这么一个选项来设定flash的访问模式。
其次,下面是我的AC128工程中的prm文件的内容
/* This is a linker parameter file for the mc9s08ac128 */

NAMES END /* CodeWarrior will pass all the needed files to the linker by command line. But

here you may add your own files too. */

SEGMENTS /* Here all RAM/ROM areas of the device are listed. Used in PLACEMENT below. */
    Z_RAM                    =  READ_WRITE   0x0080 TO 0x00FF;
    RAM                      =  READ_WRITE   0x0100 TO 0x17FF;
    RAM1                     =  READ_WRITE   0x1870 TO 0x20EF;
    /* unbanked FLASH ROM */
    ROM                      =  READ_ONLY    0x20F0 TO 0x7FFF;
    ROM1                     =  READ_ONLY    0xC000 TO 0xFF9B;
 /* INTVECTS                 =  READ_ONLY    0xFF9C TO 0xFFFF; Reserved for Interrupt

Vectors */
    /* banked FLASH ROM */
    PPAGE_0                  =  READ_ONLY    0x008000 TO 0x00A0EF; /* PAGE partially

contained in ROM segment */
    PPAGE_2                  =  READ_ONLY    0x028000 TO 0x02BFFF;
    PPAGE_4                  =  READ_ONLY    0x048000 TO 0x04BFFF;
    PPAGE_5                  =  READ_ONLY    0x058000 TO 0x05BFFF;
    PPAGE_6                  =  READ_ONLY    0x068000 TO 0x06BFFF;
    PPAGE_7                  =  READ_ONLY    0x078000 TO 0x07BFFF;
 /* PPAGE_1                  =  READ_ONLY    0x018000 TO 0x01BFFF; PAGE already contained in

segment at 0x4000-0x7FFF */
 /* PPAGE_3                  =  READ_ONLY    0x038000 TO 0x03BFFF; PAGE already contained in

segment at 0xC000-0xFFFF */
END

PLACEMENT /* Here all predefined and user segments are placed into the SEGMENTS defined

above. */
    DEFAULT_RAM,                        /* non-zero page variables */
                                        INTO  RAM,RAM1;

    _PRESTART,                          /* startup code */
    STARTUP,                            /* startup data structures */
    ROM_VAR,                            /* constant variables */
    STRINGS,                            /* string literals */
    VIRTUAL_TABLE_SEGMENT,              /* C++ virtual table segment */
    NON_BANKED,                         /* runtime routines which must not be banked */
    DEFAULT_ROM,
    COPY                                /* copy down information: how to initialize

variables */
                                        INTO  ROM,ROM1; /* ,ROM1: To use "ROM1" as well,

pass the option -OnB=b to the compiler */

    PAGED_ROM                           /* routines which can be banked */
                                        INTO 

PPAGE_0,PPAGE_2,PPAGE_4,PPAGE_5,PPAGE_6,PPAGE_7; //,ROM1;

    _DATA_ZEROPAGE,                     /* zero page variables */
    MY_ZEROPAGE                         INTO  Z_RAM;
END

STACKSIZE 0x200
这里是属于分页访问还是非分页访问?
另外,我感觉分页和非分页访问是同时存在的。比如ROM和ROM1他们是在非分页flash中,而PAGE5,6,7这些应该在分页flash中,
同一个程序中,访问分页flash和非分页flash的方法应该是不同的。而且访问的指令和速度都不同。
所以,对于你的解释还是无法理解。
回复 支持 反对

使用道具 举报

该用户从未签到

3

主题

18

帖子

0

注册会员

Rank: 2

积分
90
最后登录
1970-1-1
发表于 2013-8-6 17:37:27 | 显示全部楼层

回复:关于AC128分页的一个疑问

 
Memory Mode在这里指定:
memory mode.jpg
回复 支持 反对

使用道具 举报

该用户从未签到

3

主题

18

帖子

0

注册会员

Rank: 2

积分
90
最后登录
1970-1-1
发表于 2013-8-6 18:03:26 | 显示全部楼层

RE:关于AC128分页的一个疑问

你要重点看这段:
_PRESTART,                          /* startup code */
    STARTUP,                            /* startup data structures */
    ROM_VAR,                            /* constant variables */
    STRINGS,                            /* string literals */
    VIRTUAL_TABLE_SEGMENT,              /* C++ virtual table segment */
    NON_BANKED,                         /* runtime routines which must not be banked */
    DEFAULT_ROM,
    COPY                                /* copy down information: how to initialize
variables */
                                        INTO  ROM,ROM1; /* ,ROM1: To use "ROM1" as well,
如果不用pragam指定, 编译器就会把程序放到Default ROM里, 在这段里Default ROM 放在了ROM和ROM1里, ROM和ROM1是非分页的。
所以基本上可以确定你的程序是非分页模式。
后面那些PAGED_ROM并没有用来放程序,除非你在程序中用pragam 强行把程序放进去,但那样程序运行很可能会出错,因为之前编译器已经被指定按非分页模式编码。
在分页模式下,编译器会调用长跳转指令LCALL来实现跳转, 这条指令会自动使用PPAGE的值来计算跳转地址。 而在非分页模式下,编译器会使用JSR或JUMP来跳转,PPAGE的值是不用管的。 设想一下如果在非分页模式下,如果我们把程序放到了PPAGE = 4,第4页的某个位置,比如0x48010, JSR或JUMP跳转的时候只会跳到0x8010, 而不会跳到0x48010. 如果使用LCALL,那CPU就知道是第4页的flash地址。
回复 支持 反对

使用道具 举报

该用户从未签到

196

主题

495

帖子

0

金牌会员

Rank: 6Rank: 6

积分
1482
最后登录
2020-12-3
 楼主| 发表于 2013-8-7 10:06:46 | 显示全部楼层

回复:关于AC128分页的一个疑问

回复第 5 楼 于2013-08-06 18:03:26发表:
你要重点看这段:
_PRESTART,                          /* startup code */
STARTUP,                            /* startup data structures */
ROM_VAR,                            /* constant variables */
STRINGS,                            /* string literals */
VIRTUAL_TABLE_SEGMENT,              /* C++ virtual table segment */
NON_BANKED,                         /* runtime routines which must not be banked */
DEFAULT_ROM,
COPY                                /* copy down information: how to initialize
variables */
INTO  ROM,ROM1; /* ,ROM1: To use "ROM1" as well,
如果不用pragam指定, 编译器就会把程序放到Default ROM里, 在这段里Default ROM 放在了ROM和ROM1里, ROM和ROM1是非分页的。
所以基本上可以确定你的程序是非分页模式。
后面那些PAGED_ROM并没有用来放程序,除非你在程序中用pragam 强行把程序放进去,但那样程序运行很可能会出错,因为之前编译器已经被指定按非分页模式编码。
在分页模式下,编译器会调用长跳转指令LCALL来实现跳转, 这条指令会自动使用PPAGE的值来计算跳转地址。 而在非分页模式下,编译器会使用JSR或JUMP来跳转,PPAGE的值是不用管的。 设想一下如果在非分页模式下,如果我们把程序放到了PPAGE = 4,第4页的某个位置,比如0x48010, JSR或JUMP跳转的时候只会跳到0x8010, 而不会跳到0x48010. 如果使用LCALL,那CPU就知道是第4页的flash地址。 

明白你的意思。但是事实还是让我有很多疑问。确实之前的程序我是采用非分页模式,然后用pragam强行将程序放到PAGED_ROM
中,程序开始是无法运行的。但是后来我仿真看原因是main函数被编译器放到ROM中,而将另外的某个函数我放到了PAGED_0中,
如那段注释所说,这两段地址指向了同一片flash,导致main程序被破坏了。所以,我就在PAGED_ROM中注释掉了PAGE0,程序就可以正常运行了。如果这样可以运行的话,说明编译器并没有忽略PPAGE的值。
我按照你的方法生成了一个新的prm文件
/* This is a linker parameter file for the mc9s08dz128 */

NAMES END /* CodeWarrior will pass all the needed files to the linker by command line. But here you may add your own files too. */

SEGMENTS /* Here all RAM/ROM areas of the device are listed. Used in PLACEMENT below. */
    Z_RAM                    =  READ_WRITE   0x0080 TO 0x00FF;
    RAM                      =  READ_WRITE   0x0100 TO 0x17FF;
    RAM1                     =  READ_WRITE   0x1900 TO 0x217F;
    /* unbanked FLASH ROM */
    ROM                      =  READ_ONLY    0x4000 TO 0x7FFF;
    ROM1                     =  READ_ONLY    0x2180 TO 0x3BFF;
    ROM2                     =  READ_ONLY    0xC000 TO 0xFF7F;
    EEPROM                   =  READ_ONLY    0x3C00 TO 0x3FFF;
 /* INTVECTS                 =  READ_ONLY    0xFF80 TO 0xFFFF; Reserved for Interrupt Vectors */
    /* banked FLASH ROM */
    PPAGE_0                  =  READ_ONLY    0x008000 TO 0x00A17F; /* PAGE partially contained in ROM segment */
    PPAGE_0_1                =  READ_ONLY    0x00BC00 TO 0x00BFFF;
    PPAGE_2                  =  READ_ONLY    0x028000 TO 0x02BFFF;
    PPAGE_4                  =  READ_ONLY    0x048000 TO 0x04BFFF;
    PPAGE_5                  =  READ_ONLY    0x058000 TO 0x05BFFF;
    PPAGE_6                  =  READ_ONLY    0x068000 TO 0x06BFFF;
    PPAGE_7                  =  READ_ONLY    0x078000 TO 0x07BFFF;
 /* PPAGE_1                  =  READ_ONLY    0x018000 TO 0x01BFFF; PAGE already contained in segment at 0x4000-0x7FFF */
 /* PPAGE_3                  =  READ_ONLY    0x038000 TO 0x03BFFF; PAGE already contained in segment at 0xC000-0xFFFF */
END

PLACEMENT /* Here all predefined and user segments are placed into the SEGMENTS defined above. */
    DEFAULT_RAM,                        /* non-zero page variables */
                                        INTO  RAM,RAM1;

    _PRESTART,                          /* startup code */
    STARTUP,                            /* startup data structures */
    ROM_VAR,                            /* constant variables */
    STRINGS,                            /* string literals */
    VIRTUAL_TABLE_SEGMENT,              /* C++ virtual table segment */
    NON_BANKED,                         /* runtime routines which must not be banked */
   
    COPY                                /* copy down information: how to initialize variables */
                                        INTO  ROM; /* ,ROM1,ROM2: To use "ROM1,ROM2" as well, pass the option -OnB=b to the compiler */

    DEFAULT_ROM, PAGED_ROM              /* routines which can be banked */
                                        INTO  PPAGE_0,PPAGE_0_1,PPAGE_2,PPAGE_4,PPAGE_5,PPAGE_6,PPAGE_7,ROM1,ROM2;

    _DATA_ZEROPAGE,                     /* zero page variables */
    MY_ZEROPAGE                         INTO  Z_RAM;
END

STACKSIZE 0x200

VECTOR 0 _Startup /* Reset vector: this is the default entry point for an application. */
 
我观察了一下,发现区别就在于将DEFAULT_ROM换了位置。就如你前面所说,编译器默认的位置变了。这个效果和我用pragram
来选择存放位置效果一样。
同时/* PAGE partially contained in ROM segment */仍然存在,是不是我在ROM和PAGE0中同时存放程序仍然会出错?
注释中说的部分地址,具体是哪些地址?手册中有说到吗?在那一页?
还有一个问题,在这种分页的MCU中,是不是一定要将中断服务函数放到非分页区?因为我之前用XEP100时,就一定要这么做,
否则编译器都会报错的。
 
回复 支持 反对

使用道具 举报

该用户从未签到

8

主题

299

帖子

0

高级会员

Rank: 4

积分
653
最后登录
2018-12-14
发表于 2013-8-7 13:14:44 | 显示全部楼层

回复:关于AC128分页的一个疑问

回复第 6 楼 于2013-08-07 10:06:46发表:
回复第 5 楼 于2013-08-06 18:03:26发表:
你要重点看这段:
_PRESTART, /* startup code */
STARTUP, /* startup data structures */
ROM_VAR, /* constant variables */
STRINGS, /* string literals */
VIRTUAL_TABLE_SEGMENT, /* C++ virtual table segment */
NON_BANKED, /* runtime routines which must not be banked */
DEFAULT_ROM,
COPY /* copy down information: how to initialize
variables */
INTO ROM,ROM1; /* ,ROM1: To use "ROM1" as well,
如果不用pragam指定, 编译器就会把程序放到Default ROM里, 在这段里Default ROM 放在了ROM和ROM1里, ROM和ROM1是非分页的。
所以基本上可以确定你的程序是非分页模式。
后面那些PAGED_ROM并没有用来放程序,除非你在程序中用pragam 强行把程序放进去,但那样程序运行很可能会出错,因为之前编译器已经被指定按非分页模式编码。
在分页模式下,编译器会调用长跳转指令LCALL来实现跳转, 这条指令会自动使用PPAGE的值来计算跳转地址。 而在非分页模式下,编译器会使用JSR或JUMP来跳转,PPAGE的值是不用管的。 设想一下如果在非分页模式下,如果我们把程序放到了PPAGE = 4,第4页的某个位置,比如0x48010, JSR或JUMP跳转的时候只会跳到0x8010, 而不会跳到0x48010. 如果使用LCALL,那CPU就知道是第4页的flash地址。 

明白你的意思。但是事实还是让我有很多疑问。确实之前的程序我是采用非分页模式,然后用pragam强行将程序放到PAGED_ROM
中,程序开始是无法运行的。但是后来我仿真看原因是main函数被编译器放到ROM中,而将另外的某个函数我放到了PAGED_0中,
如那段注释所说,这两段地址指向了同一片flash,导致main程序被破坏了。所以,我就在PAGED_ROM中注释掉了PAGE0,程序就可以正常运行了。如果这样可以运行的话,说明编译器并没有忽略PPAGE的值。
我按照你的方法生成了一个新的prm文件
/* This is a linker parameter file for the mc9s08dz128 */

NAMES END /* CodeWarrior will pass all the needed files to the linker by command line. But here you may add your own files too. */

SEGMENTS /* Here all RAM/ROM areas of the device are listed. Used in PLACEMENT below. */
    Z_RAM                    =  READ_WRITE   0x0080 TO 0x00FF;
    RAM                      =  READ_WRITE   0x0100 TO 0x17FF;
    RAM1                     =  READ_WRITE   0x1900 TO 0x217F;
    /* unbanked FLASH ROM */
    ROM                      =  READ_ONLY    0x4000 TO 0x7FFF;
    ROM1                     =  READ_ONLY    0x2180 TO 0x3BFF;
    ROM2                     =  READ_ONLY    0xC000 TO 0xFF7F;
    EEPROM                   =  READ_ONLY    0x3C00 TO 0x3FFF;
 /* INTVECTS                 =  READ_ONLY    0xFF80 TO 0xFFFF; Reserved for Interrupt Vectors */
    /* banked FLASH ROM */
    PPAGE_0                  =  READ_ONLY    0x008000 TO 0x00A17F; /* PAGE partially contained in ROM segment */
    PPAGE_0_1                =  READ_ONLY    0x00BC00 TO 0x00BFFF;
    PPAGE_2                  =  READ_ONLY    0x028000 TO 0x02BFFF;
    PPAGE_4                  =  READ_ONLY    0x048000 TO 0x04BFFF;
    PPAGE_5                  =  READ_ONLY    0x058000 TO 0x05BFFF;
    PPAGE_6                  =  READ_ONLY    0x068000 TO 0x06BFFF;
    PPAGE_7                  =  READ_ONLY    0x078000 TO 0x07BFFF;
 /* PPAGE_1                  =  READ_ONLY    0x018000 TO 0x01BFFF; PAGE already contained in segment at 0x4000-0x7FFF */
 /* PPAGE_3                  =  READ_ONLY    0x038000 TO 0x03BFFF; PAGE already contained in segment at 0xC000-0xFFFF */
END

PLACEMENT /* Here all predefined and user segments are placed into the SEGMENTS defined above. */
    DEFAULT_RAM,                        /* non-zero page variables */
                                        INTO  RAM,RAM1;

    _PRESTART,                          /* startup code */
    STARTUP,                            /* startup data structures */
    ROM_VAR,                            /* constant variables */
    STRINGS,                            /* string literals */
    VIRTUAL_TABLE_SEGMENT,              /* C++ virtual table segment */
    NON_BANKED,                         /* runtime routines which must not be banked */
   
    COPY                                /* copy down information: how to initialize variables */
                                        INTO  ROM; /* ,ROM1,ROM2: To use "ROM1,ROM2" as well, pass the option -OnB=b to the compiler */

    DEFAULT_ROM, PAGED_ROM              /* routines which can be banked */
                                        INTO  PPAGE_0,PPAGE_0_1,PPAGE_2,PPAGE_4,PPAGE_5,PPAGE_6,PPAGE_7,ROM1,ROM2;

    _DATA_ZEROPAGE,                     /* zero page variables */
    MY_ZEROPAGE                         INTO  Z_RAM;
END

STACKSIZE 0x200

VECTOR 0 _Startup /* Reset vector: this is the default entry point for an application. */
 
我观察了一下,发现区别就在于将DEFAULT_ROM换了位置。就如你前面所说,编译器默认的位置变了。这个效果和我用pragram
来选择存放位置效果一样。
同时/* PAGE partially contained in ROM segment */仍然存在,是不是我在ROM和PAGE0中同时存放程序仍然会出错?
注释中说的部分地址,具体是哪些地址?手册中有说到吗?在那一页?
还有一个问题,在这种分页的MCU中,是不是一定要将中断服务函数放到非分页区?因为我之前用XEP100时,就一定要这么做,
否则编译器都会报错的。
 
 

请查看an3730.
另外中断向量是16位地址 要放在非分页区,否则会出错

AN3730.pdf

165.41 KB, 下载次数: 12

回复 支持 反对

使用道具 举报

该用户从未签到

196

主题

495

帖子

0

金牌会员

Rank: 6Rank: 6

积分
1482
最后登录
2020-12-3
 楼主| 发表于 2013-8-7 16:06:38 | 显示全部楼层

回复:关于AC128分页的一个疑问

明白了中断函数为什么放在非分页区了。但是对于PAGE0为什么要这样设计还是不明白.为什么同一片flash却要两个地址共用。
这不是增加出错的机会吗?
另外,我在建工程时,选择非分页模式也是可以使用分页的flash的。这又怎么解释?
回复 支持 反对

使用道具 举报

该用户从未签到

3

主题

18

帖子

0

注册会员

Rank: 2

积分
90
最后登录
1970-1-1
发表于 2013-8-8 18:55:45 | 显示全部楼层

RE:关于AC128分页的一个疑问(正解)

恩,这个分页模式开始理解起来是有点痛苦。我前面也说得不是特别清楚,再补充说几点
1. 我们前面说的分页模式跟非分页模式只是针对编译器,不是CPU。
CPU在任何时候都会遵循下面的原则:
1). 如果CPU发现地址寄存器里的地址是0x20F0-0x7FFF,0xC000-0xFFFF中的,那PPAGE的值会被忽略,直接跳到这个地址,所以我们说这些地址是非分页地址。
2). 如果CPU发现地址寄存器里的地址是0x8000-0xBFFF(访问窗口), 那PPAGE里的值就要参与进来产生一个新的17位的地址。
2. 编译器在分页模式下生成的代码地址都在0x8000-0xBFFF之间(PC的值),跳转指令都用CALL,CALL有3个操作器,两个是PC值,一个是PPAGE值,调用的时候就会相应的值写到PC和PPAGE. 而在非分页模式下,编译器生成的代码跳转不调用CALL, 所以PPAGE不会被修改,会一直是default 值2. 您可以调试的时候观察一下。
我推荐你看下编译器手册,里面专门有一章讲这个,还有相应的memory map的图示,您看完应该会有更深刻的理解。

下面说下您的问题:
1.但是后来我仿真看原因是main函数被编译器放到ROM中,而将另外的某个函数我放到了PAGED_0中,
如那段注释所说,这两段地址指向了同一片flash,导致main程序被破坏了。所以,我就在PAGED_ROM中注释掉了PAGE0,程序就可以正常运行了。如果这样可以运行的话,说明编译器并没有忽略PPAGE的值。
A: 您的main函数应该没有被破坏,只是在跳转的时候跑飞了,程序的实际位置在第0页,跳转的时候因为ppage寄存器是2,CPU跑到第2页去了。
PPAGE的reset值是2 如果去掉PAGE0,那放程序的地方就是PAGE2, 正好对上了,如果你去掉PAGE2,那又会出错。
2. 同时/* PAGE partially contained in ROM segment */仍然存在,是不是我在ROM和PAGE0中同时存放程序仍然会出错?
不会出错,ROM和PAGE0没有重叠,这英文的意思是第0页有一部分在ROM里。
地址换算很简单ppage * 0x4000)+(Window Addr-0x8000).
page0因为ppage是0,那换算更简单了。Page0的地址减去0x8000就是对应的Nonbank地址。比如0xA0EF对应的地址就是0xA0EF-0x8000=0x20EF.你可以看到这个正好是ROM起始位置的前一个地址。如果把ROM的起始地址改小的话就有可能出问题。
3. 还有一个问题,在这种分页的MCU中,是不是一定要将中断服务函数放到非分页区?因为我之前用XEP100时,就一定要这么做,否则编译器都会报错的。
A:是的,因为中断向量只有16位,我们都要求把ISR放到非分页区。8位的编译器好像不会报错的。

 
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /3 下一条

Archiver|手机版|小黑屋|恩智浦技术社区

GMT+8, 2025-7-20 11:52 , Processed in 0.112899 second(s), 30 queries , MemCache On.

Powered by Discuz! X3.4

Copyright © 2001-2024, Tencent Cloud.

快速回复 返回顶部 返回列表