[ บทความ : เขียนโปรแกรมมอนิเตอร์ด้วยภาษา C ]

เมื่อหลายเดือนก่อน ผมได้เข้าไป download ตัวอย่างโปรแกรมสำหรับ MCS-51 จาก WEBSITE แห่งหนึ่ง ก็มีตัวอย่างที่น่าสนใจมาก ก็คือ ตัวอย่าง MONITOR ที่เขียนด้วยภาษา C พอเห็นปุ๊บ ผมก็รีบ DOWNLOAD มาทันที แต่พอเปิดออกมา ก็พบว่า มันทำงานไม่ได้นี่นา แล้วผมก็วางมันทิ้งไปเลย ... มาถึงช่วงนี้ ผมอยากลองเขียนโปรแกรม มอนิเตอร์ดูเอง ประกอบกับเห็นว่า ผม download โปรแกรมของเขามาด้วยนี่นา ก็น่าจะเอาโปรแกรมของเขามาแก้ไข พร้อมทั้งให้เครดิตเขาด้วย(ตามมารยาทที่ดี) ดังนั้น ตัวอย่างโปรแกรมในวันนี้จะเป็นตัวอย่าง โปรแกรมมอนิเตอร์ที่ เขียนด้วย Micro-C51 ซึ่งตัวโปรแกรมมอนิเตอร์เดิมนั้น เท่าที่ผมดูจากโค้ดส่วนของ Assembly ก็พบว่า จริงๆแล้ว มันเป็นมอนิเตอร์สำหรับ Z80 เลยทำให้ต้องมีการแก้ไขหลายส่วนเลยทีเดียว แต่ก็ไม่ลำบากมากนักครับ

หน้าตาของโปรแกรมเมื่อผมโหลดเข้าบอร์ด ET-BOARD V6 (MCS-51 MODE) ก็จะแสดงผลที่โปรแกรม สื่อสารอนุกรมดังรูปด้านล่างนี้ครับ

ส่วนโค้ดโปรแกรมเป็นดังด้านล่างนี่เลยครับ ... อ้อ เนื่องจากผมแปลงโปรแกรมมาใช้บน MCS-51 ซึ่ง เจ้า MCS-51 นั้นมีรายละเอียดหลายอย่างที่ไม่เหมือนกับ Z80 โดยเฉพาะเรื่องระบบหน่วยความจำที่ MCS-51 มีทั้ง หน่วยความจำภายใน , หน่วยความจำพอร์ต, หน่วยความจำขยายภายใน (สำหรับ 8032/8052) , หน่วยความจำโปรแกรม และ หน่วยความจำข้อมูล ... แต่โปรแกรมมอนิเตอร์ที่เป็นตัวอย่างจะอ่านเฉพาะหน่วยความจำข้อมูลเท่านั้น เวลาที่จะปรับปรุง ความสามารถของโปรแกรม จะต้องเพิ่มส่วนของภาษาแอสเซมบลี เข้าไปเพื่อบังคับให้มีการอ่าน หน่วยความจำเฉพาะส่วน ที่เราต้องการ ...

	/* 
		Copyright 1990 Compouter Design Solutions Inc. 
		This code may be used by anyone in anyway. 
		CDS Inc. grants full rights to code to all users. 
	 
		Computer Design Solutions Inc. 
		P.O. Box 127 
		Statesville NC	28677 
		704-876-2346	Fax 704-876-2348 

	/----------------------------------------------------------------
		Modify by Supachai Budsaratij (raek@se-ed.net)

	        Supachai Budsaratij

	        School of Computer and Advanced Technologies
	        Rajabhat Institute Phetchaburi
	        Phetchaburi
	        Thailand
	        76000

	        Date      : 10 ตุลาคม 2543, October 10, 2000
	        Processor : 8051 (Original is support z80)
	        Compiler  : Micro-C/51 (www.dunfield.com)
	        New feature :
	                    1. Help command
	                    2. Display message before exit
	                    3. Invalid command
 
	*/ 

	#include <8051reg.h>
	#include <8051io.h>
 
	#define TRUE	1 
	#define FALSE	0 
	#define CR      0x0d 
 
	/*	Flag definations	*/ 
	#define OK      0 
	#define	NODATA	1		/* Returns from get_v when no data found */ 
 
 
	unsigned int  m_start; 
	unsigned int  m_end; 
	unsigned int  m_value; 
	unsigned int  m_last; 
	unsigned char temp[16]; 
	unsigned char cin;
	unsigned char flag;
	unsigned char *p; 
 
	main() 
	{ 
	/*  Sign on and say hello  */ 
	 
	    serinit(9600);
	    putstr("/n/rCDS Small Monitor Version 1.0 (1990)/z80"); 
	    putstr("/n/rDR  Small Monitor Version 1.0 (2000)/51"); 
	
	/* init vars    */
	    m_start=0;
	    m_end=0;
	    m_value=0;
	    m_last=0;
	 
	/*  Loop forever or until control C  */ 
	 
	    while(1)  {
	
	/*  Get Command  */ 
	 
	        crlf(); 
	        putstr("DR>");
	        cin=toupper(getchr());
	        putch(cin);
	 
	        switch(cin)   {        
	            case 'F':                       /* Fill Memory */ 
	                    f_mem(); 
	                    break; 
	            case 'D':                       /* Dump Memory */ 
	                    d_mem(); 
	                    break;   
	            case 'E':                       /* Examine Memory */ 
	                    e_mem(); 
	                    break; 
	            case 'H':
	            case '?':
	                    help();
	                    break;
	            case 3:                         /* Ctl C  Exit  */
	                    putstr("/n-EXIT from MONITOR-/n");
	                    exit_mon(); 
	            default: 
	                    putstr("/n Invalid command , press H or ? for HELP/n");
	                    break;
	 
	        } 
	 
	    } 
	        
	} 
	 
	
	exit_mon()
	/*	Not normally used. This is for testing on a CPM machine */ 
	asm {
	        ljmp      0000
	} 
	
	help()
	{
	    putstr("/n ----------HELP----------/n");
	    putstr("    F  - Fill memory/n");
	    putstr("    D  - Dump memory/n");
	    putstr("    E  - Examine memory/n");
	    putstr("    H  - Help message/n");
	    putstr("    ?  - Help message/n");
	    putstr("    ^C - Exit to address 0/n");
	}
	
	
	e_mem() 
	/* 	Examine memory	*/ 
	{ 
	    putstr("xamine  "); 
	
	/*  Get address */
	    m_start=get_v(); 
	    cin=' '; 
	    p=m_start; 
	    flag=OK; 
 
	/*  Loop till CR */ 
 
	    while(flag==OK)     { 
	        crlf(); 
	/*  Print address  */ 
	        phex(p,4,2); 
	        putstr("=  "); 
	/*  Print contents  */ 
	        phex(*p,2,2); 
	/*  Get new value */ 
	        m_value=get_v();
	        *p=m_value; 
	/*  Next address  */ 
	        p++; 
	    } 
	} 
 
	f_mem() 
	/*	Fill memory 	*/ 
	{ 
	    putstr("ill "); 
	/*  Get start, end, and data pattern */ 
	 
	    m_start=get_v(); 
	    if(flag==NODATA) {
	        fer();
	    }
	    m_end=get_v(); 
	    if(flag==NODATA)  {
	        fer();
	    }
	    m_value=get_v(); 
	    rcheck(); 
	    fill(m_start,m_end,m_value); 
	} 
 
	fer()
	{
	    error("Fill needs more data"); 
	} 
 
	d_mem() 
	/*	Dump memory	*/ 
	{ 
	    clear(); 
	    putstr("ump "); 
	/*  Get Start and end */ 
 
	    m_start=get_v(); 
	    if(cin!=CR)  {
	        m_end=get_v();
	    }
	    crlf(); 
	    rcheck(); 
	    dump(m_start,m_end); 
	} 
 
 
	error(s) 
	/*	Error processing	*/ 
	char *s; 
	{ 
	    crlf(); 
	    bell(); 
	    putstr(s); 
	    crlf(); 
	    main();                 /* sort of silly but works      */ 
	} 
 
	bell() 
	/*	Ring the bell		*/ 
	{ 
	    putch(7); 
	} 
 
 
	rcheck() 
	/*	Range check start to end	*/ 
	{ 
	    if(m_end <=m_start) {
	        m_end=m_start+16;
	    }
	}

	clear() 
	/*	Clear start,end,value,cin	*/ 
	{ 
	    m_start=0;
	    m_end=0;
	    m_value=0;
	    cin=0; 
	} 
 
 
	get_v() 
	/* get a hexadecimal value from the keyboard	*/ 
	{ 
	    unsigned int val; 
	    unsigned char cnt; 

	    cnt=0;
	    cin=0;

	    while((cin!=CR) && (cin!=','))  {
	        cin=getchr();
	        putchr(cin);

	        if(cnt==0) {
	            flag=NODATA;
	        }
	        else {
	            flag=OK;
	        }
		temp[cnt++]=cin; 
	        if(cnt>5)  { 
	            bell(); 
	            cnt--; 
	        } 
 
		/*	Back space occured	*/ 
 
	        if(cin=='/b')  { 
	            if(cnt>0) {
	                cnt--;
	            }
	            else {
	                bell();
	            }
	            putstr(" /b"); 
	        } 
 
	    } 
	    temp[--cnt]=0; 
	    upcase(temp);    
	    val=htob(temp,16); 

	    if(flag==NODATA) {
	        return(m_last);
	    }
	    m_last=val; 
	    return(val); 
	} 
  
 
 
	fill(start,end,data) 
	/* 	Fill from ad1 to ad2 with data				*/ 
	unsigned int  start; 
	unsigned int  end;
	unsigned char data;
	{ 
	    char *p; 
	    for(p=start;p<end;)  {
	        *p++=data;
	    }
	} 
 
 
	dump(start,end) 
	/*	Hex dump with address on left and ASCII on right	*/ 
	/*	Start is the begin address and end is the end		*/ 
	int start;
	int end;
	{ 
	    int c_cnt; 
	
	    crlf();
	    for (p=start;p<end;)  {
	/* Print the address */ 
	        phex(p,4,4); 
	        for(c_cnt=0;c_cnt<16;c_cnt++)  	{ 
	            phex(*p++,2,1); 
		} 
		p-=16;				/* reset */ 
	/* Print the ASCII      */ 
	        for(c_cnt=0;c_cnt < 16;c_cnt++)  	{ 
	            pascii(*p++,'.',0); 
	         }	 
	        crlf(); 
	    } 
	} 
 
 

	crlf() 
	/*      Print a new line seq.   */ 
	{ 
	    putstr("/n/r"); 
	} 
 
 

	rept(s,n) 
	/*	rept will print char. s n times 	*/ 
	char s;
	char n;
	{ 
	    if(n==0) {
	        return;
	    }	
	    while(n--)  {
	        putch(s);
	    }
	} 
 
 
 
	pascii(chr,fill,spc) 
	/* pascii(char,fill,spc) print ascii or fill and spc traiing spaces */ 
	unsigned char chr; 
	unsigned char fill; 
	unsigned char spc; 
	{ 
	    if(chr < ' ' || chr > '}')  {
	        putch(fill);
	    }
	    else  {
	        putch(chr);
	    }
	    space(spc); 
	} 
 
 
 

	phex(n,lgth,spc) 
	/* phex(n,len,spc) 
		print in hex the number (n) 0 filled of len with spc trailing 
		spaces. phex(100,4,2) will print 0064  . */ 
	unsigned int n; 
	unsigned char lgth; 
	unsigned char spc; 
	{ 
	    itob(n,temp,16); 
	    rept('0',(lgth-len(temp)));     /* 0 fill       */ 
	    putstr(temp); 
	    space(spc);                     /* print sep.    */ 
	                                    /* Print the 16 hex digits */ 
	} 
	 
 
 
	space(x) 
	/*       space(x)   will print x spaces */ 
	char x; 
	{ 
	    if(x==0) {
	        return;
	    }
	    while (x--)  {
	        putch(' ');
	    }
	} 
 


	len(s) 
	/* len returns the length of a string */ 
	char *s; 
	{ 
	    char i; 

	    i=0;
	    while(*s++) {
		i++;
	    }
	    return i; 
	} 
 
 
 
	upcase(str)
	/* convert a string to uppercase */
	char *str; 
	{ 
	   char *start; 
	
	   start = str;
	   while (*str) { 
	      if (islower(*str)) {
	          *str = toupper(*str);
	      }
	      str++;
	   }
	   return(start); 
	} 
 
 
	itob(n, s, base) 
	/* itob -- convert an integer to a string in any base (2-36) where 
		n is the number to convert 
		s is a temp. string 
		base is the number base 
	*/ 
	unsigned int n;
	char *s;
	unsigned int base;
	{ 
	    unsigned int u; 
	    char *p, *q; 
	    int negative, c; 
 
	    if (n < 0 && base == -10) { 
	        negative = TRUE; 
	        u = -n; 
	    } 
	    else { 
	        negative = FALSE; 
	        u = n; 
	    } 
	    if (base == -10) {       /* signals signed conversion */ 
	        base = 10;
	    }
	    p = q = s; 
	    do {   /* generate digits in reverse order */ 
	        if ((*p = u % base + '0') > '9') {
	            *p += ('A' - ('9' + 1));
	        }
	        ++p; 
	    } while ((u /= base) > 0); 

	    if (negative) {
	        *p++ = '-';
	    }
	    *p = '/0';                      /* terminate the string */ 
	    while (q < --p) {               /* reverse the digits */ 
	        c = *q; 
	        *q++ = *p; 
	        *p = c; 
	    } 
	    return s; 
	} 
 
 
	int htob(str,radix)
	/* convert string to integer */
	char *str; 
	unsigned int radix; 
	{ 
	   unsigned int digit, number; 

	   number=0;
	   while (*str) { 
	      if (isdigit(*str)) {
	          digit = *str - '0';
	      }
	      else if (islower(digit)) {
	          digit = *str - 'a' + 10;
	      }
	      else {
	          digit = *str - 'A' + 10;
	      }
	      number = number * radix + digit; 
	      str++;
	   }
	   return(number); 
	} 

การคอมๆพล์โปรแกรม จะต้องสั่งดังนี้ครับ

		cc51 mon -piomf m=l
สาเหตุที่ต้องคอมไพล์ให้เป็นแบบ LARGE ก็เพราะว่าเราเรียกใช้ฟังก์ชันที่เกี่ยวข้องกับ String ... นอกจากนี้ ผมกำหนดให้โปรแกรมเริ่มต้นที่ 0x8000 นะครับ ทั้งนี้เพราะต้องการให้โปรแกรมทำงานบนมอนิเตอร์ของ ETT อีกทีหนึ่ง เพื่อความสะดวกในหลายๆ ด้าน เช่น จะได้ไม่ต้องถอดตัวมอนิเตอร์ของ ETT ออกจากบอร์ด เพราะถ้าถอดแล้ว ผมเองก็ไม่ มีเครื่องเขียน FLASH memory อยู่ดี ...

จะเห็นว่าโปรแกรมยาวพอควรทีเดียวครับ ... งั้นถ้าไม่อยากพิมพ์ ก็ download โปรแกรมไปได้เลยครับ (DOWNLOAD : 6.73KB) สำหรับบทความเรื่องโปรแกรมมอนิเตอร์ คงจบกันเพียงเท่านี้ ก่อนนะครับ หวังว่า บทความนี้คงพอจะเป็นแนวทางสำหรับผู้ที่อยากเขียนโปรแกรมมอนิเตอร์ด้วยตนเองได้บ้างนะครับ ... ตอนนี้สวัสดีครับ


เขียนโดย : ศุภชัย บุศราทิจ
Author : Supachai Budsaratij
e-mail : raek@se-ed.net
วันที่ทำการปรับปรุง : ๑๐ พ.ย. ๒๕๔๓