PC-8801

Development

PC-8001、PC-8801用のソフト開発環境が今ではほとんど手に入らないので、LinuxのFEDORAでZ80のクロス環境を構築することにした。 Z80のC言語及びアセンブラとしてSDCCをチョイスした。 インストールは下記の通り。
# yum install sdcc
アセンブル方法
# sdcc-sdasz80 -o test.o test.asm
Cソースコンパイル方法
# sdcc-sdcc test.c -mz80 --code-loc 0xe000 --no-std-crt0 -Wlmycrt.o
MAKEファイルサンプル
PROJECT_NAME	= test
SRCDIR		= src
BINDIR		= bin
OUTPUT_NAME	= $(BINDIR)/$(PROJECT_NAME).ihx
OBJ		= $(BINDIR)/$(PROJECT_NAME).o
MYCRT		= $(BINDIR)/mycrt.o

CC	= sdcc-sdcc
CFLAGS	= -mz80
LDFLAGS	= --no-std-crt0
LOCATION = --code-loc
ASM	= sdcc-sdasz80

all:	$(OUTPUT_NAME)

$(OUTPUT_NAME): $(OBJ) $(MYCRT)
	$(CC) $(SRCDIR)/*.c $(CFLAGS) $(LOCATION) 0xe000 $(LDFLAGS) -o $(OUTPUT_NAME) -Wl$(MYCRT)
	sdcc-packihx $(OUTPUT_NAME) > $(basename $(OUTPUT_NAME)).hex

$(MYCRT):
	$(ASM) -o $(MYCRT) $(SRCDIR)/$(notdir $*).asm

$(OBJ):
	$(CC) $(CFLAGS) -o $@ -c $(SRCDIR)/$(notdir $*).c

.PHONY: clean
clean:
	rm $(BINDIR)/*

PC-8801用 N88-DISKBASIC Intel HEX RS-232C受信プログラム (注)N88-BASICプログラムをメニュー5にてLinux機に送る場合は、 対象のBASICプログラムをアスキーセーブしておいてください。
1000 REM **********************************
1010 REM *   Trans Program
1020 REM **********************************
1030 CLEAR ,&HB800
1040 STARTADDRESS=0:ENDADDRESS=0
1050 REM ========= Menu ==========
1060 *START
1070 CLS 3
1080 ENDFLAG = 0
1090 PRINT "1.Recive Hex File"
1100 PRINT "2.Save Program"
1110 PRINT "3.Load Program"
1120 PRINT "4.Execute Program"
1130 PRINT "5.Send BASIC Program"
1140 PRINT "99.END"
1150 PRINT "---------------------------"
1160 INPUT "No : ";NO
1170 IF NO = 1 THEN GOSUB *RECIVE.HEX
1180 IF NO = 2 THEN GOSUB *SAVE.DATA
1190 IF NO = 3 THEN GOSUB *LOAD.DATA
1200 IF NO = 4 THEN GOSUB *EXEC.PROG
1210 IF NO = 5 THEN GOSUB *SEND.PROG
1220 IF NO = 99 THEN CLS 3:END
1230 GOTO *START
1240 REM ========== Initial ==========
1250 *RECIVE.HEX
1260 STARTADDRESS=0
1270 ENDADDRESS = 0
1280 CLS 3
1290 WORK$=""
1300 ON COM GOSUB *COM.TRAP
1310 OPEN "COM1:E71NN" AS #1
1320 COM ON
1330 PRINT "Ready"
1340 REM ========== Main Loop ==========
1350 IF ENDFLAG = 1 THEN RETURN
1360 GOTO 1340
1370 END
1380 REM ========== Read Data ==========
1390 *COM.TRAP
1400 WRITE #1,CHR$(&H0)
1410 IF LOC(1)=0 THEN RETURN
1420 WORK$=WORK$+INPUT$(LOC(1),1)
1430 'Read CR
1440 IF ASC(MID$(WORK$,LEN(WORK$),1))=&HA THEN GOSUB *ANZ.REC
1450 'Read EOF
1460 IF MID$(WORK$,1,1)=CHR$(&H1A) THEN GOSUB *READ.END
1470 RETURN
1480 REM ========== Analyze Record ==========
1490 *ANZ.REC
1500 LENGTH = VAL("&H"+MID$(WORK$,2,2))
1510 OFFSET = VAL("&H"+MID$(WORK$,4,4))
1520 RECTYPE = VAL(MID$(WORK$,8,2))
1530 IF RECTYPE = 0 THEN GOSUB *WRITE.DATA
1540 PRINT #1,CHR$(&H6)
1550 WORK$=""
1560 RETURN
1570 REM ========== Read End ==========
1580 *READ.END
1590 CLOSE #1
1600 ENDFLAG=1
1610 RETURN
1620 REM ========== Write Data ==========
1630 *WRITE.DATA
1640 IF STARTADDRESS=0 THEN STARTADDRESS=OFFSET
1650 INDEX=0
1660 FOR ADDRESS = 0 TO LENGTH-1
1670   IF INDEX=0 THEN PRINT HEX$(OFFSET+ADDRESS);" : ";
1680     PRINT MID$(WORK$,10+ADDRESS*2,2);" ";
1690     INDEX=INDEX+1
1700     IF INDEX > 15 THEN INDEX=0
1710     POKE OFFSET+ADDRESS, VAL("&H"+MID$(WORK$,10+ADDRESS*2,2))
1720 NEXT ADDRESS
1730 PRINT ""
1740 ENDADDRESS = OFFSET+ADDRESS
1750 RETURN
1760 REM ========== Save Data ==========
1770 *SAVE.DATA
1780 INPUT "FILENAME:";FILENAME$
1790 BSAVE FILENAME$,STARTADDRESS,ENDADDRESS-STARTADDRESS
1800 RETURN
1810 REM ========== Load Data ==========
1820 *LOAD.DATA
1830 INPUT "FILENAME:";FILENAME$
1840 INPUT "LOAD ADDRESS(HEX):";STADDR$
1850 STARTADDRESS = VAL("&H"+STADDR$)
1860 BLOAD FILENAME$,STARTADDRESS
1870 RETURN
1880 REM ========== EXECUTE PROGRAM ==========
1890 *EXEC.PROG
1900 CLS 3
1910 PROG = STARTADDRESS
1920 CALL PROG
1930 RETURN
1940 REM ========== Send Program ==========
1950 *SEND.PROG
1960 OPEN "COM1:E81NN" AS #1
1970 COM ON
1980 INPUT "FILENAME:";FILENAME$
1990 PRINT "Input Fle Name"
2000 OPEN FILENAME$ FOR INPUT AS #2
2010 *SEND.LOOP
2020 IF EOF(2) THEN *SEND.EXIT
2030 LINE INPUT #2,WORK$
2040 PRINT WORK$
2050 WRITE #1,WORK$
2060 GOTO 2020
2070 *SEND.EXIT
2080 WRITE #1,CHR$(&H1A)
2090 CLOSE #1
2100 CLOSE #2
2110 RETURN
Linux Intel HEX RS-232C送信プログラム
/*
 * File:   main.cpp
 * Author: kazunori.abe
 *
 * Created on 2015/06/18, 15:58
 */

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define PATH_MAX    256
#define STR_LENGTH     256
#define BUFF_MAX_COUNT  10
#define BAUDRATE    B9600

using namespace std;

char sBuf[BUFF_MAX_COUNT][STR_LENGTH];

/*
 * '"'の削除
 */
char* DelDem(char* sData)
{
    int nIndex=0;
    // '"'を削除
    if(sData[nIndex] == '\"')
    {
        for(;nIndex 0; nIndex--)
    {
        if(sData[nIndex] == '\"')
        {
            const int c_nMarkMax = 2;
            for(int nMarkMax=0;sData[nIndex] == '\"'; nIndex--,nMarkMax++)
            {
                if(nMarkMax >= c_nMarkMax)
                    break;

                sData[nIndex--] = 0x00;
            }
            break;
        }
    }

    for(int nIndex=0,nIndex2=0; nIndex < STR_LENGTH-1;nIndex++,nIndex2++)
    {
        for(;(sData[nIndex2] == 0x0d || sData[nIndex2] == 0x0a) && nIndex2 < STR_LENGTH;)nIndex2++;
        sData[nIndex] = sData[nIndex2];
    }

    return sData;
}

/*
 * 行分解
 */
int Split(char* sData)
{
    int nCount = 0;

    int nIndex=0;
    int nPoint= 0;
    while(1)
    {
        if(nCount >= BUFF_MAX_COUNT)
        {
            nCount--;
            break;
        }
        if(nPoint==0)
            memset(sBuf[nCount], 0x00, STR_LENGTH);

        sBuf[nCount][nPoint++] = sData[nIndex++];
        if(sBuf[nCount][nPoint-1] == 0x0a)
        {
            sBuf[nCount][nPoint-1] = sData[nIndex++];
            nCount++;
            nPoint=0;
        }
        else if(sBuf[nCount][nPoint-1] == 0x00)
        {
            break;
        }
        else if(nPoint >= STR_LENGTH)
        {
            sBuf[nCount][nPoint-1] = 0x00;
            nCount++;
            nPoint=0;
            break;
        }
    }

    return nCount+1;
}

/*
 * HEXファイル送信
 */
void Send(char* sFileName)
{
    int fd;
    FILE *fp;
    struct termios tio;
    int err;
    char readline[STR_LENGTH] = {'\0'};

    // Com1 open
    if ((fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NONBLOCK))<0) {
        printf("Device Open Error !!\r\n");
        return;
    }
    memset(&tio, 0x00, sizeof(tio));
    tio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
    tio.c_iflag = IGNPAR;
    tio.c_oflag = 0;
    tio.c_lflag = 0;
    tio.c_cc[VTIME] = 0;
    tio.c_cc[VMIN] = 1;

    tcflush(fd, TCIFLUSH);
    tcsetattr(fd, TCSANOW, &tio);
    fcntl(fd, F_SETFL, FNDELAY);

    // file open
    if ((fp = fopen(sFileName, "r")) == NULL) {
        fprintf(stderr, "%s open error.\n", sFileName);
        close(fd);
        return;
    }

    // ファイル読み込み
    while(fgets(readline, STR_LENGTH, fp) != NULL){
        sleep(1);
	printf(readline);

        // レコード送信
        err = write(fd, readline, strlen(readline));
        while(1){
            // 応答待ち
            char recv[STR_LENGTH];
            read(fd, recv, STR_LENGTH);
            if(recv[0]==0x06)
                break;
        }
    }

    // file close
    fclose( fp );

    // send eof
    sleep( 1 );
    sprintf(readline, "%c", 0x1a);
    write(fd, readline, strlen(readline));

    // com1 close
    close( fd );
}

/*
 * プログラム受信
 */
void Recv(char* sFileName)
{
    int fd;
    FILE *fp;
    struct termios tio;
    int err;
    char readline[STR_LENGTH] = {'\0'};

    // Com1 open
    if ((fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NONBLOCK))<0) {
        printf("Device Open Error !!\r\n");
        return;
    }
    memset(&tio, 0x00, sizeof(tio));
    tio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
    tio.c_iflag = IGNPAR;
    tio.c_oflag = 0;
    tio.c_lflag = 0;
    tio.c_cc[VTIME] = 0;
    tio.c_cc[VMIN] = 1;

    tcflush(fd, TCIFLUSH);
    tcsetattr(fd, TCSANOW, &tio);
    fcntl(fd, F_SETFL, FNDELAY);

    // file open
    if ((fp = fopen(sFileName, "w")) == NULL) {
        fprintf(stderr, "%s open error.\n", sFileName);
        close(fd);
        return;
    }

    int size = 0;
    memset(readline, 0x00, STR_LENGTH);
    while(1){
        size = read(fd, readline, STR_LENGTH);

        int nLastIndex = 0;
        if(size > 0)
        {
            int nCount = Split(readline);
            for(int nIndex = 0; nIndex < nCount; nIndex++)
            {
                DelDem(sBuf[nIndex]);
                if(sBuf[nIndex][0] == 0x1a)
                {
                    nLastIndex = nIndex;
                    break;
                }
                printf("%s\r\n", sBuf[nIndex]);
                fprintf(fp, "%s\r\n",sBuf[nIndex]);
                fflush( fp );
                memset(readline, 0x00, STR_LENGTH);
            }
        }

        if(sBuf[nLastIndex][0] == 0x1a)
            break;
        sleep(1);
    }

    // file close
    fclose( fp );

    // send eof
    sleep( 1 );

    printf("--- END ---\r\n");

    // com1 close
    close( fd );
}


/*
 * メイン
 */
int main(int argc, char** argv) {

    int nNo = 0;
    char sFileName[PATH_MAX];

    do {

        printf("1.Send Intel Hex\r\n");
        printf("2.Recv Program\r\n");
        printf("99.End\r\n");
        printf("No : ");
        scanf("%d" , &nNo);

        switch(nNo) {
            case 1:
                // Program Send
                printf("Intel Hex FileName : ");
                scanf("%s", sFileName);
                Send(sFileName);
                break;
            case 2:
                // Program Recive
                printf("Save FileName : ");
                scanf("%s", sFileName);
                Recv(sFileName);
                break;
            case 99:
                // End Program
                break;
            default:
                break;
        }

        printf("%d\r\n",nNo);
    } while(nNo != 99);

    return 0;
}