分享

Tiny语言运行环境TM机源代码

 quasiceo 2015-01-18
分类: 编译原理 2014-05-17 13:14 392人阅读 评论(0) 收藏 举报

TM机就是TINY语言编译器编译之后的汇编代码的运行环境。TM机的主要功能是将TM的汇编代码读入和执行,它具有一般计算机类似的精简指令级RISC。TM汇编语言和一般的Intel汇编语言差不多,包括寄存器寻址、操作符等,很容易理解。一条典型的代码如:LD 0,10(1),这里面10(1)就是寄存器1中地址为基址,10为偏移地址,寻址结果放入寄存器0。三目操作符:MUL 0,1,0表示将寄存器1和寄存器2的相乘结果放入寄存器0.接下来开始一部分一部分的分析TM机的源代码。 TM源代码如下:

  1. /****************************************************/  
  2. /* File: tm.c                                       */  
  3. /* The TM ("Tiny Machine") computer                 */  
  4. /* Compiler Construction: Principles and Practice   */  
  5. /* Kenneth C. Louden                                */  
  6. /****************************************************/  
  7.   
  8. #include <stdio.h>  
  9. #include <stdlib.h>  
  10. #include <string.h>  
  11. #include <ctype.h>  
  12.   
  13. #ifndef TRUE  
  14. #define TRUE 1  
  15. #endif  
  16. #ifndef FALSE  
  17. #define FALSE 0  
  18. #endif  
  19.   
  20. /******* const *******/  
  21. #define   IADDR_SIZE  1024 /* increase for large programs */  
  22. #define   DADDR_SIZE  1024 /* increase for large programs */  
  23. #define   NO_REGS 8  
  24. #define   PC_REG  7  
  25.   
  26. #define   LINESIZE  121  
  27. #define   WORDSIZE  20  
  28.   
  29. /******* type  *******/  
  30.   
  31. typedef enum {  
  32.    opclRR,     /* reg operands r,s,t */  
  33.    opclRM,     /* reg r, mem d+s */  
  34.    opclRA      /* reg r, int d+s */  
  35.    } OPCLASS;  
  36.   
  37. typedef enum {  
  38.    /* RR instructions */  
  39.    opHALT,    /* RR     halt, operands are ignored */  
  40.    opIN,      /* RR     read into reg(r); s and t are ignored */  
  41.    opOUT,     /* RR     write from reg(r), s and t are ignored */  
  42.    opADD,    /* RR     reg(r) = reg(s)+reg(t) */  
  43.    opSUB,    /* RR     reg(r) = reg(s)-reg(t) */  
  44.    opMUL,    /* RR     reg(r) = reg(s)*reg(t) */  
  45.    opDIV,    /* RR     reg(r) = reg(s)/reg(t) */  
  46.    opRRLim,   /* limit of RR opcodes */  
  47.   
  48.    /* RM instructions */  
  49.    opLD,      /* RM     reg(r) = mem(d+reg(s)) */  
  50.    opST,      /* RM     mem(d+reg(s)) = reg(r) */  
  51.    opRMLim,   /* Limit of RM opcodes */  
  52.   
  53.    /* RA instructions */  
  54.    opLDA,     /* RA     reg(r) = d+reg(s) */  
  55.    opLDC,     /* RA     reg(r) = d ; reg(s) is ignored */  
  56.    opJLT,     /* RA     if reg(r)<0 then reg(7) = d+reg(s) */  
  57.    opJLE,     /* RA     if reg(r)<=0 then reg(7) = d+reg(s) */  
  58.    opJGT,     /* RA     if reg(r)>0 then reg(7) = d+reg(s) */  
  59.    opJGE,     /* RA     if reg(r)>=0 then reg(7) = d+reg(s) */  
  60.    opJEQ,     /* RA     if reg(r)==0 then reg(7) = d+reg(s) */  
  61.    opJNE,     /* RA     if reg(r)!=0 then reg(7) = d+reg(s) */  
  62.    opRALim    /* Limit of RA opcodes */  
  63.    } OPCODE;  
  64.   
  65. typedef enum {  
  66.    srOKAY,  
  67.    srHALT,  
  68.    srIMEM_ERR,  
  69.    srDMEM_ERR,  
  70.    srZERODIVIDE  
  71.    } STEPRESULT;  
  72.   
  73. typedef struct {  
  74.       int iop  ;  
  75.       int iarg1  ;  
  76.       int iarg2  ;  
  77.       int iarg3  ;  
  78.    } INSTRUCTION;  
  79.   
  80. /******** vars ********/  
  81. int iloc = 0 ;  
  82. int dloc = 0 ;  
  83. int traceflag = FALSE;  
  84. int icountflag = FALSE;  
  85.   
  86. INSTRUCTION iMem [IADDR_SIZE];  
  87. int dMem [DADDR_SIZE];  
  88. int reg [NO_REGS];  
  89.   
  90. char * opCodeTab[]  
  91.         = {"HALT","IN","OUT","ADD","SUB","MUL","DIV","????",  
  92.             /* RR opcodes */  
  93.            "LD","ST","????"/* RM opcodes */  
  94.            "LDA","LDC","JLT","JLE","JGT","JGE","JEQ","JNE","????"  
  95.            /* RA opcodes */  
  96.           };  
  97.   
  98. char * stepResultTab[]  
  99.         = {"OK","Halted","Instruction Memory Fault",  
  100.            "Data Memory Fault","Division by 0"  
  101.           };  
  102.   
  103. char pgmName[20];  
  104. FILE *pgm  ;  
  105.   
  106. char in_Line[LINESIZE] ;  
  107. int lineLen ;  
  108. int inCol  ;  
  109. int num  ;  
  110. char word[WORDSIZE] ;  
  111. char ch  ;  
  112. int done  ;  
  113.   
  114. /********************************************/  
  115. int opClass( int c )  
  116. if      ( c <= opRRLim) return ( opclRR );  
  117.   else if ( c <= opRMLim) return ( opclRM );  
  118.   else                    return ( opclRA );  
  119. /* opClass */  
  120.   
  121. /********************************************/  
  122. void writeInstruction ( int loc )  
  123. { printf( "%5d: ", loc) ;  
  124.   if ( (loc >= 0) && (loc < IADDR_SIZE) )  
  125.   { printf("%6s%3d,", opCodeTab[iMem[loc].iop], iMem[loc].iarg1);  
  126.     switch ( opClass(iMem[loc].iop) )  
  127.     { case opclRR: printf("%1d,%1d", iMem[loc].iarg2, iMem[loc].iarg3);  
  128.                    break;  
  129.       case opclRM:  
  130.       case opclRA: printf("%3d(%1d)", iMem[loc].iarg2, iMem[loc].iarg3);  
  131.                    break;  
  132.     }  
  133.     printf ("\n") ;  
  134.   }  
  135. /* writeInstruction */  
  136.   
  137. /********************************************/  
  138. void getCh (void)  
  139. if (++inCol < lineLen)  
  140.   ch = in_Line[inCol] ;  
  141.   else ch = ' ' ;  
  142. /* getCh */  
  143.   
  144. /********************************************/  
  145. int nonBlank (void)  
  146. while ((inCol < lineLen)  
  147.          && (in_Line[inCol] == ' ') )  
  148.     inCol++ ;  
  149.   if (inCol < lineLen)  
  150.   { ch = in_Line[inCol] ;  
  151.     return TRUE ; }  
  152.   else  
  153.   { ch = ' ' ;  
  154.     return FALSE ; }  
  155. /* nonBlank */  
  156.   
  157. /********************************************/  
  158. int getNum (void)  
  159. int sign;  
  160.   int term;  
  161.   int temp = FALSE;  
  162.   num = 0 ;  
  163.   do  
  164.   { sign = 1;  
  165.     while ( nonBlank() && ((ch == '+') || (ch == '-')) )  
  166.     { temp = FALSE ;  
  167.       if (ch == '-')  sign = - sign ;  
  168.       getCh();  
  169.     }  
  170.     term = 0 ;  
  171.     nonBlank();  
  172.     while (isdigit(ch))  
  173.     { temp = TRUE ;  
  174.       term = term * 10 + ( ch - '0' ) ;  
  175.       getCh();  
  176.     }  
  177.     num = num + (term * sign) ;  
  178.   } while ( (nonBlank()) && ((ch == '+') || (ch == '-')) ) ;  
  179.   return temp;  
  180. /* getNum */  
  181.   
  182. /********************************************/  
  183. int getWord (void)  
  184. int temp = FALSE;  
  185.   int length = 0;  
  186.   if (nonBlank ())  
  187.   { while (isalnum(ch))  
  188.     { if (length < WORDSIZE-1) word [length++] =  ch ;  
  189.       getCh() ;  
  190.     }  
  191.     word[length] = '\0';  
  192.     temp = (length != 0);  
  193.   }  
  194.   return temp;  
  195. /* getWord */  
  196.   
  197. /********************************************/  
  198. int skipCh ( char c  )  
  199. int temp = FALSE;  
  200.   if ( nonBlank() && (ch == c) )  
  201.   { getCh();  
  202.     temp = TRUE;  
  203.   }  
  204.   return temp;  
  205. /* skipCh */  
  206.   
  207. /********************************************/  
  208. int atEOL(void)  
  209. return ( ! nonBlank ());  
  210. /* atEOL */  
  211.   
  212. /********************************************/  
  213. int error( char * msg, int lineNo, int instNo)  
  214. { printf("Line %d",lineNo);  
  215.   if (instNo >= 0) printf(" (Instruction %d)",instNo);  
  216.   printf("   %s\n",msg);  
  217.   return FALSE;  
  218. /* error */  
  219.   
  220. /********************************************/  
  221. int readInstructions (void)  
  222. { OPCODE op;  
  223.   int arg1, arg2, arg3;  
  224.   int loc, regNo, lineNo;  
  225.   for (regNo = 0 ; regNo < NO_REGS ; regNo++)  
  226.       reg[regNo] = 0 ;  
  227.   dMem[0] = DADDR_SIZE - 1 ;  
  228.   for (loc = 1 ; loc < DADDR_SIZE ; loc++)  
  229.       dMem[loc] = 0 ;  
  230.   for (loc = 0 ; loc < IADDR_SIZE ; loc++)  
  231.   { iMem[loc].iop = opHALT ;  
  232.     iMem[loc].iarg1 = 0 ;  
  233.     iMem[loc].iarg2 = 0 ;  
  234.     iMem[loc].iarg3 = 0 ;  
  235.   }  
  236.   lineNo = 0 ;  
  237.   while (! feof(pgm))  
  238.   { fgets( in_Line, LINESIZE-2, pgm  ) ;  
  239.     inCol = 0 ;   
  240.     lineNo++;  
  241.     lineLen = strlen(in_Line)-1 ;  
  242.     if (in_Line[lineLen]=='\n') in_Line[lineLen] = '\0' ;  
  243.     else in_Line[++lineLen] = '\0';  
  244.     if ( (nonBlank()) && (in_Line[inCol] != '*') )  
  245.     { if (! getNum())  
  246.         return error("Bad location", lineNo,-1);  
  247.       loc = num;  
  248.       if (loc > IADDR_SIZE)  
  249.         return error("Location too large",lineNo,loc);  
  250.       if (! skipCh(':'))  
  251.         return error("Missing colon", lineNo,loc);  
  252.       if (! getWord ())  
  253.         return error("Missing opcode", lineNo,loc);  
  254.       op = opHALT ;  
  255.       while ((op < opRALim)  
  256.              && (strncmp(opCodeTab[op], word, 4) != 0) )  
  257.           op++ ;  
  258.       if (strncmp(opCodeTab[op], word, 4) != 0)  
  259.           return error("Illegal opcode", lineNo,loc);  
  260.       switch ( opClass(op) )  
  261.       { case opclRR :  
  262.         /***********************************/  
  263.         if ( (! getNum ()) || (num < 0) || (num >= NO_REGS) )  
  264.             return error("Bad first register", lineNo,loc);  
  265.         arg1 = num;  
  266.         if ( ! skipCh(','))  
  267.             return error("Missing comma", lineNo, loc);  
  268.         if ( (! getNum ()) || (num < 0) || (num >= NO_REGS) )  
  269.             return error("Bad second register", lineNo, loc);  
  270.         arg2 = num;  
  271.         if ( ! skipCh(','))   
  272.             return error("Missing comma", lineNo,loc);  
  273.         if ( (! getNum ()) || (num < 0) || (num >= NO_REGS) )  
  274.             return error("Bad third register", lineNo,loc);  
  275.         arg3 = num;  
  276.         break;  
  277.   
  278.         case opclRM :  
  279.         case opclRA :  
  280.         /***********************************/  
  281.         if ( (! getNum ()) || (num < 0) || (num >= NO_REGS) )  
  282.             return error("Bad first register", lineNo,loc);  
  283.         arg1 = num;  
  284.         if ( ! skipCh(','))  
  285.             return error("Missing comma", lineNo,loc);  
  286.         if (! getNum ())  
  287.             return error("Bad displacement", lineNo,loc);  
  288.         arg2 = num;  
  289.         if ( ! skipCh('(') && ! skipCh(',') )  
  290.             return error("Missing LParen", lineNo,loc);  
  291.         if ( (! getNum ()) || (num < 0) || (num >= NO_REGS))  
  292.             return error("Bad second register", lineNo,loc);  
  293.         arg3 = num;  
  294.         break;  
  295.         }  
  296.       iMem[loc].iop = op;  
  297.       iMem[loc].iarg1 = arg1;  
  298.       iMem[loc].iarg2 = arg2;  
  299.       iMem[loc].iarg3 = arg3;  
  300.     }  
  301.   }  
  302.   return TRUE;  
  303. /* readInstructions */  
  304.   
  305.   
  306. /********************************************/  
  307. STEPRESULT stepTM (void)  
  308. { INSTRUCTION currentinstruction  ;  
  309.   int pc  ;  
  310.   int r,s,t,m  ;  
  311.   int ok ;  
  312.   
  313.   pc = reg[PC_REG] ;  
  314.   if ( (pc < 0) || (pc > IADDR_SIZE)  )  
  315.       return srIMEM_ERR ;  
  316.   reg[PC_REG] = pc + 1 ;  
  317.   currentinstruction = iMem[ pc ] ;  
  318.   switch (opClass(currentinstruction.iop) )  
  319.   { case opclRR :  
  320.     /***********************************/  
  321.       r = currentinstruction.iarg1 ;  
  322.       s = currentinstruction.iarg2 ;  
  323.       t = currentinstruction.iarg3 ;  
  324.       break;  
  325.   
  326.     case opclRM :  
  327.     /***********************************/  
  328.       r = currentinstruction.iarg1 ;  
  329.       s = currentinstruction.iarg3 ;  
  330.       m = currentinstruction.iarg2 + reg[s] ;  
  331.       if ( (m < 0) || (m > DADDR_SIZE))  
  332.          return srDMEM_ERR ;  
  333.       break;  
  334.   
  335.     case opclRA :  
  336.     /***********************************/  
  337.       r = currentinstruction.iarg1 ;  
  338.       s = currentinstruction.iarg3 ;  
  339.       m = currentinstruction.iarg2 + reg[s] ;  
  340.       break;  
  341.   } /* case */  
  342.   
  343.   switch ( currentinstruction.iop)  
  344.   { /* RR instructions */  
  345.     case opHALT :  
  346.     /***********************************/  
  347.       printf("HALT: %1d,%1d,%1d\n",r,s,t);  
  348.       return srHALT ;  
  349.       /* break; */  
  350.   
  351.     case opIN :  
  352.     /***********************************/  
  353.       do  
  354.       { printf("Enter value for IN instruction: ") ;  
  355.         fflush (stdin);  
  356.         fflush (stdout);  
  357.         gets(in_Line);  
  358.         lineLen = strlen(in_Line) ;  
  359.         inCol = 0;  
  360.         ok = getNum();  
  361.         if ( ! ok ) printf ("Illegal value\n");  
  362.         else reg[r] = num;  
  363.       }  
  364.       while (! ok);  
  365.       break;  
  366.   
  367.     case opOUT :    
  368.       printf ("OUT instruction prints: %d\n", reg[r] ) ;  
  369.       break;  
  370.     case opADD :  reg[r] = reg[s] + reg[t] ;  break;  
  371.     case opSUB :  reg[r] = reg[s] - reg[t] ;  break;  
  372.     case opMUL :  reg[r] = reg[s] * reg[t] ;  break;  
  373.   
  374.     case opDIV :  
  375.     /***********************************/  
  376.       if ( reg[t] != 0 ) reg[r] = reg[s] / reg[t];  
  377.       else return srZERODIVIDE ;  
  378.       break;  
  379.   
  380.     /*************** RM instructions ********************/  
  381.     case opLD :    reg[r] = dMem[m] ;  break;  
  382.     case opST :    dMem[m] = reg[r] ;  break;  
  383.   
  384.     /*************** RA instructions ********************/  
  385.     case opLDA :    reg[r] = m ; break;  
  386.     case opLDC :    reg[r] = currentinstruction.iarg2 ;   break;  
  387.     case opJLT :    if ( reg[r] <  0 ) reg[PC_REG] = m ; break;  
  388.     case opJLE :    if ( reg[r] <=  0 ) reg[PC_REG] = m ; break;  
  389.     case opJGT :    if ( reg[r] >  0 ) reg[PC_REG] = m ; break;  
  390.     case opJGE :    if ( reg[r] >=  0 ) reg[PC_REG] = m ; break;  
  391.     case opJEQ :    if ( reg[r] == 0 ) reg[PC_REG] = m ; break;  
  392.     case opJNE :    if ( reg[r] != 0 ) reg[PC_REG] = m ; break;  
  393.   
  394.     /* end of legal instructions */  
  395.   } /* case */  
  396.   return srOKAY ;  
  397. /* stepTM */  
  398.   
  399. /********************************************/  
  400. int doCommand (void)  
  401. char cmd;  
  402.   int stepcnt=0, i;  
  403.   int printcnt;  
  404.   int stepResult;  
  405.   int regNo, loc;  
  406.   do  
  407.   { printf ("Enter command: ");  
  408.     fflush (stdin);  
  409.     fflush (stdout);  
  410.     gets(in_Line);  
  411.     lineLen = strlen(in_Line);  
  412.     inCol = 0;  
  413.   }  
  414.   while (! getWord ());  
  415.   
  416.   cmd = word[0] ;  
  417.   switch ( cmd )  
  418.   { case 't' :  
  419.     /***********************************/  
  420.       traceflag = ! traceflag ;  
  421.       printf("Tracing now ");  
  422.       if ( traceflag ) printf("on.\n"); else printf("off.\n");  
  423.       break;  
  424.   
  425.     case 'h' :  
  426.     /***********************************/  
  427.       printf("Commands are:\n");  
  428.       printf("   s(tep <n>      "\  
  429.              "Execute n (default 1) TM instructions\n");  
  430.       printf("   g(o            "\  
  431.              "Execute TM instructions until HALT\n");  
  432.       printf("   r(egs          "\  
  433.              "Print the contents of the registers\n");  
  434.       printf("   i(Mem <b <n>>  "\  
  435.              "Print n iMem locations starting at b\n");  
  436.       printf("   d(Mem <b <n>>  "\  
  437.              "Print n dMem locations starting at b\n");  
  438.       printf("   t(race         "\  
  439.              "Toggle instruction trace\n");  
  440.       printf("   p(rint         "\  
  441.              "Toggle print of total instructions executed"\  
  442.              " ('go' only)\n");  
  443.       printf("   c(lear         "\  
  444.              "Reset simulator for new execution of program\n");  
  445.       printf("   h(elp          "\  
  446.              "Cause this list of commands to be printed\n");  
  447.       printf("   q(uit          "\  
  448.              "Terminate the simulation\n");  
  449.       break;  
  450.   
  451.     case 'p' :  
  452.     /***********************************/  
  453.       icountflag = ! icountflag ;  
  454.       printf("Printing instruction count now ");  
  455.       if ( icountflag ) printf("on.\n"); else printf("off.\n");  
  456.       break;  
  457.   
  458.     case 's' :  
  459.     /***********************************/  
  460.       if ( atEOL ())  stepcnt = 1;  
  461.       else if ( getNum ())  stepcnt = abs(num);  
  462.       else   printf("Step count?\n");  
  463.       break;  
  464.   
  465.     case 'g' :   stepcnt = 1 ;     break;  
  466.   
  467.     case 'r' :  
  468.     /***********************************/  
  469.       for (i = 0; i < NO_REGS; i++)  
  470.       { printf("%1d: %4d    ", i,reg[i]);  
  471.         if ( (i % 4) == 3 ) printf ("\n");  
  472.       }  
  473.       break;  
  474.   
  475.     case 'i' :  
  476.     /***********************************/  
  477.       printcnt = 1 ;  
  478.       if ( getNum ())  
  479.       { iloc = num ;  
  480.         if ( getNum ()) printcnt = num ;  
  481.       }  
  482.       if ( ! atEOL ())  
  483.         printf ("Instruction locations?\n");  
  484.       else  
  485.       { while ((iloc >= 0) && (iloc < IADDR_SIZE)  
  486.                 && (printcnt > 0) )  
  487.         { writeInstruction(iloc);  
  488.           iloc++ ;  
  489.           printcnt-- ;  
  490.         }  
  491.       }  
  492.       break;  
  493.   
  494.     case 'd' :  
  495.     /***********************************/  
  496.       printcnt = 1 ;  
  497.       if ( getNum  ())  
  498.       { dloc = num ;  
  499.         if ( getNum ()) printcnt = num ;  
  500.       }  
  501.       if ( ! atEOL ())  
  502.         printf("Data locations?\n");  
  503.       else  
  504.       { while ((dloc >= 0) && (dloc < DADDR_SIZE)  
  505.                   && (printcnt > 0))  
  506.         { printf("%5d: %5d\n",dloc,dMem[dloc]);  
  507.           dloc++;  
  508.           printcnt--;  
  509.         }  
  510.       }  
  511.       break;  
  512.   
  513.     case 'c' :  
  514.     /***********************************/  
  515.       iloc = 0;  
  516.       dloc = 0;  
  517.       stepcnt = 0;  
  518.       for (regNo = 0;  regNo < NO_REGS ; regNo++)  
  519.             reg[regNo] = 0 ;  
  520.       dMem[0] = DADDR_SIZE - 1 ;  
  521.       for (loc = 1 ; loc < DADDR_SIZE ; loc++)  
  522.             dMem[loc] = 0 ;  
  523.       break;  
  524.   
  525.     case 'q' : return FALSE;  /* break; */  
  526.   
  527.     default : printf("Command %c unknown.\n", cmd); break;  
  528.   }  /* case */  
  529.   stepResult = srOKAY;  
  530.   if ( stepcnt > 0 )  
  531.   { if ( cmd == 'g' )  
  532.     { stepcnt = 0;  
  533.       while (stepResult == srOKAY)  
  534.       { iloc = reg[PC_REG] ;  
  535.         if ( traceflag ) writeInstruction( iloc ) ;  
  536.         stepResult = stepTM ();  
  537.         stepcnt++;  
  538.       }  
  539.       if ( icountflag )  
  540.         printf("Number of instructions executed = %d\n",stepcnt);  
  541.     }  
  542.     else  
  543.     { while ((stepcnt > 0) && (stepResult == srOKAY))  
  544.       { iloc = reg[PC_REG] ;  
  545.         if ( traceflag ) writeInstruction( iloc ) ;  
  546.         stepResult = stepTM ();  
  547.         stepcnt-- ;  
  548.       }  
  549.     }  
  550.     printf( "%s\n",stepResultTab[stepResult] );  
  551.   }  
  552.   return TRUE;  
  553. /* doCommand */  
  554.   
  555.   
  556. /********************************************/  
  557. /* E X E C U T I O N   B E G I N S   H E R E */  
  558. /********************************************/  
  559.   
  560. main( int argc, char * argv[] )  
  561. if (argc != 2)  
  562.   { printf("usage: %s <filename>\n",argv[0]);  
  563.     exit(1);  
  564.   }  
  565.   strcpy(pgmName,argv[1]) ;  
  566.   if (strchr (pgmName, '.') == NULL)  
  567.      strcat(pgmName,".tm");  
  568.   pgm = fopen(pgmName,"r");  
  569.   if (pgm == NULL)  
  570.   { printf("file '%s' not found\n",pgmName);  
  571.     exit(1);  
  572.   }  
  573.   
  574.   /* read the program */  
  575.   if ( ! readInstructions ())  
  576.          exit(1) ;  
  577.   /* switch input file to terminal */  
  578.   /* reset( input ); */  
  579.   /* read-eval-print */  
  580.   printf("TM  simulation (enter h for help)...\n");  
  581.   do  
  582.      done = ! doCommand ();  
  583.   while (! done );  
  584.   printf("Simulation done.\n");  
  585.   return 0;  

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多