当前位置: 首页 > news >正文

一生一芯学习:pa2.1 RTFM

一生一芯学习:pa2.1 RTFM

前面的内容后面再补,因为近期要C考核了,所以先准备下C考核所需的内容。

RTFSC(2)
整理一条指令在NEMU中的执行结果

从函数中跳转,宏嵌套中慢慢进入最终的代码,人肉gdb一下,
比如在sdb中执行一次si,那么根据cmd_si代码那我们会跳转到cpu_exec()中

static int cmd_si(char *args) {if (args == NULL) {cpu_exec(1);return 0;}int i = atoi(args);if (i <= 0) {printf("Invalid argument '%s'\n", args);} else {cpu_exec(i);}return 0;
}

在cpu_exec()中我们进入execute()函数中。

/* Simulate how the CPU works. */
void cpu_exec(uint64_t n) {g_print_step = (n < MAX_INST_TO_PRINT);//一次执行太多步就不打印了,bool类型的gprintstep就赋值为false//printf("%d\n",nemu_state.state);switch (nemu_state.state) {case NEMU_END: case NEMU_ABORT: case NEMU_QUIT:printf("Program execution has ended. To restart the program, exit NEMU and run again.\n");return;//如果状态是结束了,出错了,退出了就打印“退出nemu”。default: nemu_state.state = NEMU_RUNNING;//默认running}uint64_t timer_start = get_time();//获取执行指令前的时间execute(n);uint64_t timer_end = get_time();//获取执行指令后的时间g_timer += timer_end - timer_start; //看执行了多久。switch (nemu_state.state) { case NEMU_RUNNING: nemu_state.state = NEMU_STOP; break;case NEMU_END: case NEMU_ABORT:Log("nemu: %s at pc = " FMT_WORD,//nemu出错或者异常退出就用红色打印,正常退出就绿色打印。  (nemu_state.state == NEMU_ABORT ? ANSI_FMT("ABORT", ANSI_FG_RED) :(nemu_state.halt_ret == 0 ? ANSI_FMT("HIT GOOD TRAP", ANSI_FG_GREEN) :ANSI_FMT("HIT BAD TRAP", ANSI_FG_RED))),nemu_state.halt_pc);// fall throughcase NEMU_QUIT: statistic();}
}

在execute()中跳转到exec_once()中

static void execute(uint64_t n) {Decode s;initBuffer(&cb); // 初始化环形缓冲区,大小为BUFFER_SIZEfor (;n > 0; n --) {exec_once(&s, cpu.pc);g_nr_guest_inst ++;trace_and_difftest(&s, cpu.pc);if (nemu_state.state != NEMU_RUNNING) {break;}IFDEF(CONFIG_DEVICE, device_update());}/*条件编译宏,如果CONFIG_DEVICE被定义,则调用device_update函数,如果 CONFIG_DEVICE 没有被定义,这一行什么都不会生成(等价于被注释掉)。*/printBuffer(&cb);
}

从exec_once()中跳转到isa_exec_once()中。

static void exec_once(Decode *s, vaddr_t pc) {s->pc = pc;//当前指令地址s->snpc = pc;//静态下一条指令地址,默认为pc+4// if(s->pc == 0x80001480){//   printf("找到一场入口地址\n");//   nemu_state.state = NEMU_STOP;// }isa_exec_once(s);cpu.pc = s->dnpc;//动态下一条指令,可能跳转或者分支改变
#ifdef CONFIG_ITRACE//如果启用了 CONFIG_ITRACE,会记录指令的详细信息到日志缓冲区 s->logbuf:char *p = s->logbuf;p += snprintf(p, sizeof(s->logbuf), FMT_WORD ":", s->pc);//FMT_WORD:格式化字符串(如 "0x%08x"),用于输出 PC 地址。//printf("0x%08x\n",s->pc);//printf("0x%08x\n",s->snpc);int ilen = s->snpc - s->pc; //计算指令长度int i;uint8_t *inst = (uint8_t *)&s->isa.inst;// printf("inst = 0x%08x\n",s->isa.inst);// printf("inst ***= 0x%08x\n", *inst);
#ifdef CONFIG_ISA_x86for (i = 0; i < ilen; i ++) { //x86是小段,从低地址开始打印
#elsefor (i = ilen - 1; i >= 0; i --) {//riscv是大段,从高地址开始打印
#endifp += snprintf(p, 4, " %02x", inst[i]); //把指令打印出来}int ilen_max = MUXDEF(CONFIG_ISA_x86, 8, 4); //不是x86ilenmax就是4int space_len = ilen_max - ilen;   //计算需要填充的空格数if (space_len < 0) space_len = 0; //space_len = space_len * 3 + 1;memset(p, ' ', space_len);p += space_len;void disassemble(char *str, int size, uint64_t pc, uint8_t *code, int nbyte);//反汇编指令disassemble(p, s->logbuf + sizeof(s->logbuf) - p,   //将反汇编指令出来后传到logbuf里面MUXDEF(CONFIG_ISA_x86, s->snpc, s->pc), (uint8_t *)&s->isa.inst, ilen);//muxdef,有点像  ?:,enqueue(&cb, s->logbuf);#endif
}

从isa_exec_once中在跳转到decode_exec()函数中

int isa_exec_once(Decode *s) {s->isa.inst = inst_fetch(&s->snpc, 4);//return一个0回去,但是现在并没有使用这个返回值,可以忽略他。return decode_exec(s);
}

inst_fetch()是在snpc中获取一条长度为4字节的指令赋值给s->inst,然后执行一遍decode_exec()并返回decode_exec()的返回值。

先一次性将decode_exec()代码全部贴出,很长

static int decode_exec(Decode *s) {int rd = 0;word_t src1 = 0, src2 = 0, imm = 0;s->dnpc = s->snpc;#define INSTPAT_INST(s) ((s)->isa.inst)
#define INSTPAT_MATCH(s, name, type, ... /* execute body */ ) { \decode_operand(s, &rd, &src1, &src2, &imm, concat(TYPE_, type)); \__VA_ARGS__ ; \
}INSTPAT_START();//INSTPAT(模式字符串, 指令名称, 指令类型, 指令执行操作);INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc  , U, R(rd) = s->pc + imm); INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui    , U, R(rd) = imm);     INSTPAT("0000000 ????? ????? 101 ????? 00100 11", srli   , I, R(rd) = src1 >> BITS(imm, 5, 0)); INSTPAT("0000000 ????? ????? 001 ????? 00100 11", slli   , I, R(rd) = src1 << BITS(imm, 5, 0));INSTPAT("0100000 ????? ????? 101 ????? 00100 11", srai   , I, R(rd) = (int32_t)src1 >> BITS(imm , 4 , 0) ); INSTPAT("??????? ????? ????? 100 ????? 00000 11", lbu    , I, R(rd) = Mr(src1 + imm, 1));INSTPAT("??????? ????? ????? 000 ????? 00100 11", addi   , I, R(rd) = src1 + imm);INSTPAT("??????? ????? ????? 011 ????? 00100 11", sltiu  , I, R(rd) = (src1 < imm) ? 1 : 0); INSTPAT("??????? ????? ????? 010 ????? 00100 11", slti   , I, R(rd) = ((int32_t)src1 < ((int32_t)imm)) ? 1 : 0); INSTPAT("??????? ????? ????? 000 ????? 00000 11", lb     , I, R(rd) = SEXT(Mr(src1 + imm, 2),8));INSTPAT("??????? ????? ????? 001 ????? 00000 11", lh     , I, R(rd) = SEXT(Mr(src1 + imm, 2),16));INSTPAT("??????? ????? ????? 101 ????? 00000 11", lhu    , I, R(rd) = Mr(src1 + imm, 2));INSTPAT("??????? ????? ????? 010 ????? 00000 11", lw     , I, R(rd) = Mr(src1 + imm, 4)); INSTPAT("??????? ????? ????? 111 ????? 00100 11", andi   , I, R(rd) = src1 & imm); INSTPAT("??????? ????? ????? 100 ????? 00100 11", xori   , I, R(rd) = src1 ^ imm); INSTPAT("??????? ????? ????? 110 ????? 00100 11", ori    , I, R(rd) = src1 | imm);//CSR寄存器INSTPAT("??????? ????? ????? 001 ????? 11100 11", csrrw  , I,  if(imm == 0x305){  //mtvecR(rd) = cpu.mtvec;cpu.mtvec =  src1;};if(imm == 0x300){ //mstatusR(rd) = cpu.mstatus;cpu.mstatus =  src1;};if(imm == 0x341){ //mepcR(rd) = cpu.mepc;cpu.mepc =  src1;};if(imm == 0x342){ //mcauseR(rd) = cpu.mcause;cpu.mcause =  src1;};
);INSTPAT("0000000 00000 00000 000 00000 11100 11", ecall  , I, s->dnpc = isa_raise_intr(11,s->pc);etrace());INSTPAT("??????? ????? ????? 010 ????? 11100 11", csrrs  , I, if(imm == 0x305){  //mtvecR(rd) = cpu.mtvec;cpu.mtvec |=  src1;};if(imm == 0x300){ //mstatusR(rd) = cpu.mstatus;cpu.mstatus |=  src1;};if(imm == 0x341){ //mepcR(rd) = cpu.mepc;cpu.mepc |=  src1;};if(imm == 0x342){ //mcause// printf("??????????????????????????*****\n");R(rd) = cpu.mcause;cpu.mcause |=  src1;};
);INSTPAT("??????? ????? ????? 010 ????? 01000 11", sw     , S, Mw(src1 + imm, 4, src2));INSTPAT("??????? ????? ????? 001 ????? 01000 11", sh     , S, Mw(src1 + imm, 2, src2)); INSTPAT("??????? ????? ????? 000 ????? 01000 11", sb     , S, Mw(src1 + imm, 1, src2)); INSTPAT("??????? ????? ????? 011 ????? 01000 11", sd     , S, Mw(src1 + imm, 8, src2)); INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal    , J, R(rd) = s->pc + 4;s->dnpc = s->pc + imm;IFDEF(CONFIG_FTRACE, {if (rd == 1) {call_trace(s->pc, s->dnpc);}}));INSTPAT("??????? ????? ????? 000 ????? 11001 11", jalr   , I, R(rd) = s->pc + 4;s->dnpc = (src1 + imm) & (~1);IFDEF(CONFIG_FTRACE,{if (s->isa.inst == 0x00008067)ret_trace(s->pc);else if (rd == 1) {call_trace(s->pc, s->dnpc);} else if (rd == 0 && imm == 0) {call_trace(s->pc, s->dnpc);}}));INSTPAT("0000000 ????? ????? 101 ????? 01100 11", srl    , R, R(rd) = src1 >> BITS(src2, 4, 0));INSTPAT("0000000 ????? ????? 000 ????? 01100 11", add    , R, R(rd) = src1 + src2); INSTPAT("0000000 ????? ????? 001 ????? 01100 11", sll    , R, R(rd) = src1 <<  BITS(src2 , 4 , 0)); INSTPAT("0000000 ????? ????? 010 ????? 01100 11", slt    , R, R(rd) = ((int32_t)src1 < (int32_t)src2) ? 1 : 0);INSTPAT("0000000 ????? ????? 011 ????? 01100 11", sltu   , R, R(rd) = src1 < src2 ? 1 : 0); INSTPAT("0000000 ????? ????? 100 ????? 01100 11", xor    , R, R(rd) = src1 ^ src2); INSTPAT("0000000 ????? ????? 110 ????? 01100 11", or     , R, R(rd) = src1 | src2); INSTPAT("0000000 ????? ????? 111 ????? 01100 11", and    , R, R(rd) = src1 & src2); INSTPAT("0100000 ????? ????? 101 ????? 01100 11", sra    , R, R(rd) = (int32_t)src1 >> BITS(src2 , 4 , 0)); INSTPAT("0100000 ????? ????? 000 ????? 01100 11", sub    , R, R(rd) = src1 - src2); INSTPAT("0000001 ????? ????? 000 ????? 01100 11", mul    , R, R(rd) = (unsigned)src1 * (unsigned)src2);//INSTPAT("0000001 ????? ????? 100 ????? 01100 11", div    , R, R(rd) = src1 / src2);//INSTPAT("0000001 ????? ????? 110 ????? 01100 11", rem    , R, R(rd) = src1 % src2);//INSTPAT("0000001 ????? ????? 111 ????? 01100 11", remu   , R, R(rd) = (unsigned)src1 % (unsigned)src2);//INSTPAT("0000001 ????? ????? 101 ????? 01100 11", divu   , R, R(rd) = (unsigned)src1 / (unsigned)src2);INSTPAT("0000001 ????? ????? 001 ????? 01100 11", mulh   , R, R(rd) = ((int64_t)(int32_t)src1 * (int64_t)(int32_t)src2) >> 32;);INSTPAT("0000001 ????? ????? 010 ????? 01100 11", mulhsu , R, R(rd) = ((int64_t)(int32_t)src1 * (int64_t)(uint32_t)src2) >> 32;);INSTPAT("0000001 ????? ????? 011 ????? 01100 11", mulhu  , R, R(rd) = ((int64_t)(uint32_t)src1 * (int64_t)(uint32_t)src2) >> 32;);INSTPAT("0000001 ????? ????? 100 ????? 01100 11", div    , R, if (src2 == 0) R(rd) = -1;else if ((int32_t)src1 == INT32_MIN && (int32_t)src2 == -1) R(rd) = INT32_MIN;else R(rd) = (int32_t)src1 / (int32_t)src2;);INSTPAT("0000001 ????? ????? 101 ????? 01100 11", divu   , R, if (src2 == 0) R(rd) = 0xFFFFFFFF;else R(rd) = (uint32_t)src1 / (uint32_t)src2;);INSTPAT("0000001 ????? ????? 110 ????? 01100 11", rem    , R, if (src2 == 0) R(rd) = (int32_t)src1;else if ((int32_t)src1 == INT32_MIN && (int32_t)src2 == -1) R(rd) = 0;else R(rd) = (int32_t)src1 % (int32_t)src2;);INSTPAT("0000001 ????? ????? 111 ????? 01100 11", remu   , R, if (src2 == 0) R(rd) = (uint32_t)src1;else R(rd) = (uint32_t)src1 % (uint32_t)src2;);INSTPAT("0011000 00010 00000 000 0000 011100 11", mret   , R, s->dnpc = cpu.mepc);//div注释://匹配 div 指令(有符号除法)。//如果除数 src2 为 0,结果规定为 -1。//如果被除数是最小负数(INT32_MIN),除数为 -1,结果规定为 INT32_MIN(防止溢出)。//否则正常做有符号除法。//printf("mulh:%lx\n", (int64_t)(~src1+1) * (int64_t)src2));//正确的a5:0001 1001 1101 0010 1001 1010 1011 1001//INSTPAT("0000001 ????? ????? 001 ????? 01100 11", mulh   , R, R(rd) = SEXT(src1 * src2, 32));//把寄存器 x[rs2]乘到寄存器 x[rs1]上,都视为 2 的补码,将乘积的高位写入 x[rd]。INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq    , B, // if(s->pc == 0x800115c0){// printf("src1 =%d\n",src1);// printf("src2 =%d\n",src2);// printf("pc =0x%08x\n",s->pc);// printf("imm =0x%08x\n",imm);// printf("dnpc =0x%08x\n",s->dnpc);  // }// printf("src1 =%d\n",src1);printf("src2 =%d\n",src2);// printf("pc =0x%08x\n",s->pc);printf("imm =0x%08x\n",imm);// printf("dnpc =0x%08x\n",s->dnpc);if(src1 == src2) s->dnpc = s->pc + imm);INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne    , B, if(src1 != src2) s->dnpc = s->pc + imm);INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt    , B, s->dnpc = ((int32_t)src1< (int32_t)src2) ? s->pc + imm : s->dnpc); INSTPAT("??????? ????? ????? 101 ????? 11000 11", bge    , B, s->dnpc = ((int32_t)src1>=(int32_t)src2) ? s->pc + imm : s->dnpc); INSTPAT("??????? ????? ????? 110 ????? 11000 11", bltu   , B, s->dnpc = (src1< src2) ? s->pc + imm : s->dnpc); INSTPAT("??????? ????? ????? 111 ????? 11000 11", bgeu   , B, s->dnpc = (src1>=src2) ? s->pc + imm : s->dnpc); INSTPAT("0000000 00001 00000 000 00000 11100 11", ebreak , N, NEMUTRAP(s->pc, R(10))); // R(10) is $a0INSTPAT("??????? ????? ????? ??? ????? ????? ??", inv    , N, INV(s->pc));INSTPAT_END();R(0) = 0; // reset $zero to 0return 0;
}

其中INSTPAT()意思是
首先我们来解析一下里面的宏函数

  1. 先来看一下这个INSTPAT_MATCH()定义了一条模式匹配规则

INSTPAT(模式字符串, 指令名称, 指令类型, 指令执行操作);如果匹配上了,那就直接用C语言的goto跳转到INSTPAT_END表示为

#define INSTPAT_MATCH(s, name, type, ... /* execute body */ ) { \decode_operand(s, &rd, &src1, &src2, &imm, concat(TYPE_, type)); \__VA_ARGS__ ; \
}

其中的s是译码所需的信息比如pc,snpc,dnpc,isa,logbuf(用于ITRACE)
其中的decode_operand

static void decode_operand(Decode *s, int *rd, word_t *src1, word_t *src2, word_t *imm, int type) {uint32_t i = s->isa.inst;int rs1 = BITS(i, 19, 15);int rs2 = BITS(i, 24, 20);*rd     = BITS(i, 11, 7);switch (type) {case TYPE_I: src1R();          immI(); break;case TYPE_U:                   immU(); break;case TYPE_S: src1R(); src2R(); immS(); break;case TYPE_J:                   immJ(); break;case TYPE_B: src1R(); src2R(); immB(); break;case TYPE_R: src1R(); src2R();         break;}
}

理解为译码的预处理,BITS()的用法有点像verilog中的变量[1:0]的味道,也就是取这个区间的位宽,在看riscv手册中提到,指令中的源寄存器,目的寄存器,立即数基本上都有对应的位置。如果检测到一条指令是什么类型的便把需要的值赋给rs1,rs2,*rd,imm即可。此外SEXT()用于符号扩展。在匹配过程最后有一条inv的规则,如果前面所有的匹配模式都没有成功那就把此指令视为非法指令

此时取指、译码功能便实现了,接下来就是执行,访存、回写。
INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc , U, R(rd) = s->pc + imm);
都在后面的R(rd) = s->pc + imm实现了。

这个操作懂了之后,后面运行更多的程序你就知道怎么做了。

首先程序会被riscv工具链交叉编译之后生成机器码之后将一串数据放到img中,你的nemu就根据img的值不断取指、译码、执行、访存、回写等等。那你此时只需要去看am的cpu-test被汇编成什么指令,比如展示一下dummy.c汇编出来的东西放在了dummy-riscv32-nemu.txt,你就只需看你少了什么指令然后补充上去就可以了。

下一期介绍一下cpu-test一堆例程是如何跑在nemu上运行的,顺便复习一下对AM的理解。

相关文章:

一生一芯学习:pa2.1 RTFM

一生一芯学习:pa2.1 RTFM前面的内容后面再补,因为近期要C考核了,所以先准备下C考核所需的内容。 RTFSC(2) 整理一条指令在NEMU中的执行结果 从函数中跳转,宏嵌套中慢慢进入最终的代码,人肉gdb一下, 比如在sdb中执行一次si,那么根据cmd_si代码那我们会跳转到cpu_exec()中…...

Linux网络:初识网络 - 详解

Linux网络:初识网络 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important; font-si…...

20250909比赛总结

T1 唐诗题目,维护最大与次大,就做完了,我边界错了(1为最有删除时不对),然后我加了一个1-100的暴力,就过了(浪费nan分钟) 下次写的时候多模拟边界,不要只关注后面的,而且只有打太久(>2h)就下一道...

又寸入生白勺司烤

我一直觉得,我们的人生就像一个凸包,时而高超,时而学习之。人类,一直在这样的高超和低谷下徘徊着,疑惑着,找寻着。那么,在低谷的时候,我们却让洛谷成为了人生的跳楼机,让我们反复升空又落地,这样一来,高超就更为容易了。但又会更快的降落,望着跳楼机窗外的暗红色的…...

Ubuntu 安装 GIPM

一行命令即可 sudo apt-get install gimp安装完后得到的软件:...

手动下载vscode扩展的方法

主要用于离线环境下安装。比如,要下载cpptools,可以使用以下url:https://marketplace.visualstudio.com/_apis/public/gallery/publishers/ms-vscode/vsextensions/cpptools/1.13.9/vspackage?targetPlatform=win32-x64在url中指定版本号和目标平台即可。目前支持的平台如下…...

GAS_Aura-Aura Projectile

1创建了一个火球Actor...

CF1583F Defender of Childhood Dreams

构造比较非人类。 先每 \(k\) 个点分一组,染上同一种颜色,再每 \(k^2\) 个点一组,组内染上同一种颜色,然后依次类推,发现满足条件,可以证明其下界是为 \(\log_k n\) 的。...

scrollArea无法滚动

问题本质 ScrollArea(如 Radix UI、shadcn/ui 等)无法滚动的根本原因是缺乏明确的高度约束。ScrollArea 需要知道自己的确切高度才能计算是否需要显示滚动条。 常见错误模式 // ❌ 错误:ScrollArea 无法获得明确高度 <div className="container"><Scroll…...

时间序列分析(1)

Time Series Problems and Challenges 时间序列问题和挑战 Time series data are a very important source of information. People always tried to analyze time series data to understand the nature of events. Since ancient times, mankind has begun to wonder what li…...

一行代码没写,做了一个小程序

我花了三天时间,准确地说应该是8个小时不到(因为期间《天龙八部》看到四十多回了,停不下来)做了一个微信小程序。 起因是和一个朋友聊到彩票,他说他都是攒好多张了之后,然后一起手动核对有没有中奖。当时我就萌生了做一个小程序的想法,扫一下就能知道是不是又做慈善了。…...

【置顶】欢迎来到 ziyaojia 的主页

这是 HN-CS 最菜的 OIer,你这个神犇敢和他对视 3 秒吗? 洛谷账号:ziyaojia...

copyparty 是一款使用单个 Python 材料实现的内网文件共享软件,具有跨平台、低资源占用等特点,适合需要本地化文件管理的场景

copyparty 是一款使用单个 Python 材料实现的内网文件共享软件,具有跨平台、低资源占用等特点,适合需要本地化文件管理的场景pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: &quo…...

ZYNQ Ultrascale+系列部署yolo v10(暂定,若过于艰难则考虑降级或FQ)

YOLO V10模型分析与优化 2.1 YOLO V10模型获取与环境准备 步骤1:创建工作目录结构 # 打开终端,创建项目根目录 mkdir -p ~/yolo_v10_fpga_project cd ~/yolo_v10_fpga_project# 创建详细的目录结构 mkdir -p models/original # 存放原始模型 mkdir -p models/onnx …...

【EF Core】再谈普通实体关系与 Owned 关系的区别

在很多个世纪前,老周曾写过实体之间普通关系(一对一,一对多,多对多)与 Owned 关系的区别。不过,那次写得比较粗浅,逼格不够高,于是,老周厚着脸皮地决定重新写一下。 首先,为什么这次老周用原单词 Owned 呢,官方文档目前的翻译(怀疑是机器干的)为“从属”,这种说法…...

qoj6104 Building Bombing

题意 有 \(n\) 栋建筑,第 \(i\) 栋建筑的高度为 \(a_i\),一座建筑能从左侧看到仅当它左侧的建筑高度都小于它,问你最少需要爆破几座房子,才能使第 \(l\) 座房子成为能看到的第 \(k\) 高建筑。 \(n\le 10^5,k\le 10\)。 思路 首先 \(l\) 要能被看到,因此先把 \(l\) 左边高度…...

必知必会:使用serializers.Serializer在views.py视图文件中序列化和反序列化过程的开发模板

from django.views import Viewclass ProjectTestView(View):"""a.获取所有数据b.创建一条数据"""def get(self, request):"""序列化输出到前端的过程规则:a.创建模型对象b.将上面已创建好的的模型对象传递给序列化器类的instan…...

Cursor小程序实战五:Cursor对接微信两大核心问题

一、对话内容非技术人员的语言 程序员/技术人员的语言比如:回调地址,api,发送请求 ..... 二、微信的两大问题:授权、微信支付 目的是对于非技术人员能够输出一段比较精确的提示词,方便完成功能的开发 1)微信授权登陆 1、永远绕不开用户登陆流程用户登录 手机号验证码…...

电商系统的Mysql表设计是怎么样呢

一、前述 问题1: 电商系统创建订单的逻辑中,如果订单使用了优惠券的话,是会把优惠券直接标在订单表么,还是会单独创建一个表里记录订单和多个优惠券之间的关系 问题2: 如何设计一套mysql数据库的表,可以将订单信息、支付信息、优惠券信息以及商品信息之间关联起来二、实战演练…...

Docker应用 - CloudSaver

CloudSaver 是一个网盘搜索、转存工具。首次部署需要先注册用户,默认管理员注册码 230713。注册进入后可以在设置里修改管理员和用户注册码。用户配置处,可以登录多种网盘授权(可惜现在不支持百度了)。不设置也可以搜索,但不能直接转存。在常见问题可以查找搜索频道(密码…...

SQL查找是否存在,别再count了! - DAYTOY

根据某一条件从数据库表中查询 『有』与『没有』,只有两种状态,那为什么在写SQL的时候,还要SELECT count(*) 呢?无论是刚入道的程序员新星,还是精湛沙场多年的程序员老白,都是一如既往的count 1 目前多数人的写法 多次REVIEW代码时,发现如现现象:业务代码中,需要根据一…...

Cursor小程序实战系列二:如何从原型界面到小程序界面

一、原型界面的生成 在cursor中用以下提示词我想开发一个小程序 目标用户: • 主要用户:25-45岁的家长,没时间来教小孩怎么写作文,也可能不知道还在对应年级作文的写作规范和要求 • 用户痛点:1、家长难以针对孩子写作弱点提供定制化提升方案2、无法获得实时写作建议 我目…...

Cursor小程序实战系列三: 前后端对接保姆级拆解

一、先理解下什么是前后端,为什么叫对接? 二、谁来定义API接口 1、传统的模式 先后端出接口定义,前端人员按照接口定义自己搭建模拟数据,这样的好处是 前端,后端都能同时开发,互不影响 2、cursor模式 三、生成API接口文档 1、最好的方式,在rules中定义接口变化同时更新…...

课前问题思考2

1.方法相关问题 public class Main {static void changeStr(String x) {x = "xyz";}static void changeArr(String[] strs) {for (int i = 0; i < strs.length; i++) {strs[i] = strs[i]+""+i;}}public static void main(String[] args) { String x …...

Cursor小程序实战四:如何让AI写好后端代码

五、用好mermaid流程画图工具...

Web 3

Web 3 更改前题目 题目没有做记录,卡在了最后一步正则绕过,前七个绕过挺简单的 ?A[]=1&B[]=2&C=s878926199a&D[]=0&E=5201.1&F=0xDEADC0DE正则绕过: if (!preg_match(/ls|dir|nl|nc|cat|tail|more|flag|sh|cut|awk|strings|od|curl|ping|\*|sort|ch|zi…...

Cursor小程序实战系列一:0到1开发一个小程序,需求整理、小程序注册备案

一、需求的诞生 某书发表了一片笔记: 家长使用多维表格+DeepSeek对小孩进行作文辅导。痛点两到三个核心功能点这些内容对使用Cursor来生成页面很有帮助AI作文辅导一般的家长辅导孩子作文非常困难,主要原因如下:1、家长难以针对孩子写作弱点提供定制化提升方案 2、无法获得实时…...

深入解析:MySQL 数据类型与运算符详解

深入解析:MySQL 数据类型与运算符详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !importan…...

【前端Vue】如何优雅地在vue中引入ace-editor编辑器 - 指南

【前端Vue】如何优雅地在vue中引入ace-editor编辑器 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …...

USACO08 OPEN Roads Around the Farm S (递归)

(我太垃了,得写点题解提升一下) P2907 [USACO08OPEN] Roads Around The Farm S 题目描述 Farmer John 的奶牛对探索农场周围的领地产生了兴趣。最初,所有 $N$ 头奶牛($1 \leq N \leq 10^9$)以一个大群体的形式开始沿着一条道路旅行。当遇到岔路时,群体有时会选择分成两个…...

dubbo-go example学习记录

dubbo-go example仓库地址: https://github.com/apache/dubbo-go-samples你可以在 https://github.com/apache/dubbo-go-samples/blob/main/README_CN.md 查看中文的README文档快速上手 这个是通过 https://cn.dubbo.apache.org/zh-cn/blog/2021/01/14/快速上手-dubbo-go/ 官方…...

org.apache.commons.lang3.StringUtils工具类中 isEmpty 和 isBlank 的区别 - DAYTOY

1 isEmpty系列 1.1 StringUtils.isEmpty() 说明:是否为空. 可以看到 " " 空格是会绕过这种空判断,因为是一个空格,并不是严格的空值,会导致 isEmpty(" ")=falseStringUtils.isEmpty(null) = true StringUtils.isEmpty("") = true StringUtils.i…...

ubuntu如何查看是否有显卡,显卡型号

在 Ubuntu 系统中,查看是否有显卡以及显卡型号的方法有多种。以下是几种实用的命令,方便你快速获取显卡信息。1. 使用 lspci 查看显卡信息 lspci 命令会列出所有 PCI 设备,包括显卡。 命令bashlspci | grep -i vga输出示例 00:02.0 VGA compatible controller: Intel Corpor…...

赛题

A U607526 「Monkey Mountine Round I」乔迁新居 题目背景 天大的喜事,游荡几十载之后,猴王找到了水帘洞! 但是,洞口较小,不知猴族老小和辎重几次能运完。善武不通文的猴王一代广招贤才,找你算算。 题目描述 共有 \(n\) 只猴子,\(m\) 车辎重。洞口每次可以进入 \(x\) 只…...

JavaScript生成随机数的方法

在JavaScript中,创建随机数可以通过内置的 Math 对象实现。我们将会探讨一些生成随机数的方法,它们在不同场合下都显示出色。 基本的随机数生成 // 生成0到1之间的随机数(不包含1) let randomNumber = Math.random(); Math.random() 函数返回一个浮点数,该数值在0(包括0)…...

LiveOS 的制作简介

LiveOS 用途:便捷启动 LIVEOS 不占用磁盘空间,系统完全在内存文件系统中运行 常用于 linux 系统救援(文件系统异常导致系统进入紧急模式) 常用于制作 PXE 生产测试 DIAG 系统 开源,免费,可定制化程度高制作工具 lorax lorax 项目是 红帽企业版 OS 的安装器的开源项目 套件包…...

.gitignore 文件

...

目标检测 | 基于Weiler–Atherton算法的IoU求解

**交并比(Intersection over Union, IoU)** 是计算机视觉领域中常用的一个评价指标,尤其在目标检测与图像分割任务中,用于衡量预测结果与真实标注之间的重合程度。目标检测 | 基于Weiler–Atherton算法的IoU求解 IoU 交并比(Intersection over Union, IoU) 是计算机视觉领…...

对比Java学习Go——函数、集合和OOP

Go语言的函数支持声明与调用,具备多返回值、命名返回值等特性,结合`func`关键字与类型后置语法,使函数定义简洁直观。函数可作为一等公民传递、赋值或作为参数,支持匿名函数与闭包。Go通过组合与接口实现面向对象编程,结构体定义数据,方法定义行为,接口实现多态,体现了…...

MySQL集群高可用架构 - 指南

MySQL集群高可用架构 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important; font-si…...

【WRF-VPRM 预处理器】HEG 安装(服务器)-MRT专业的工具替代

【WRF-VPRM 预处理器】HEG 安装(服务器)-MRT专业的工具替代pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New"…...

如何在Spring MVC中处理请求参数

在Spring MVC中处理请求参数是通过使用各种注解来实现的。以下是在Spring MVC中处理不同类型请求参数的方法。 使用 @RequestParam注解 当你想要从查询字符串中获取单个参数值时,你可以使用 @RequestParam注解。例如: @GetMapping("/search") public String search…...

redis实现缓存2-解决缓存穿透,缓存击穿

具体实现: ShopServiceImpl package com.hmdp.service.impl;import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.hmdp.dto.Result; import com.hmdp.entity.Shop; …...

单克隆抗体人源化:从鼠源缺陷到全人源突破,3 大阶段破解临床应用难题

单克隆抗体(McAb)凭借高特异性、强靶向性,在疾病预防、诊断与治疗中占据核心地位。1975 年,Khler 和 Milstein 创立杂交瘤技术,首次实现人工制备 McAb,为生物医药领域开辟新路径。但初代鼠源性 McAb 存在两大关键缺陷,严重限制临床应用:一是免疫原性高,进入人体后易被…...

在Kubernetes中DaemonSet无法在master节点调度的问题

在Kubernetes中,DaemonSet确保全部(或某些特定)Node运行一个Pod的副本。当有Node加入集群时,DaemonSet会自动在新加入的Node上部署Pod。这对于运行像日志收集器、监控代理或其他形式的守护进程非常有用。 默认情况下,出于安全性的考虑,Kubernetes master节点不允许调度普…...

9 12-

9 12改一道题改了一天,自闭了,总结无法描述,还是自己太糖了/ll P8776 线段树优化DP转移9 13模拟赛唯一一场没有睡着的模拟赛 T1很快想到了换根DP,秒掉 T2很快想到了 \(N^2\) 的暴力,然后经过我的观察发现转移形似杨辉三角,就推了出来 T3T4毫无思路剩下一个半小时直接跑路…...

桌面客户端的主要类型和技术方案

桌面客户端开发已经不再是传统的单一技术栈,而是衍生出了多种方案,各有优劣。下图清晰地展示了这些技术方案的演进与分类: flowchart TD A[桌面客户端技术方案] --> B1["原生开发<br>(Native App)"] A --> B2["跨平台开发<br>(Cross-Platf…...

AGX Orin平台RTC驱动导致reboot系统卡住障碍调试

AGX Orin平台RTC驱动导致reboot系统卡住障碍调试pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace …...

C 语言实现动态数组、链表、栈与队列

从代码到原理:C语言实现动态数组、链表、栈与队列 在数据结构的世界里,线性结构是构建复杂算法的基石。动态数组、链表、栈和队列作为最经典的线性结构,各自拥有独特的存储方式与操作特性,适用于不同的业务场景。本文将结合C语言实现代码,从结构定义、核心操作到实际应用,…...

git reset

在一个文件夹内,初始化其为 git 本地仓库,然后新建一个文件,提交至本地仓库,再修改这个文件,再提交至本地仓库。此时此刻的提交记录:如果用 git reset 命令回到当前所在位置,是不会有任何变化的。用 git reset 命令回到位于当前提交之前的提交,这一步操作也可以复原:如…...