Writting New Software for an NEC PC-8401
As noted previously, the PC-8401 came sans BASIC interpreter, which was quite an anathema in the day. This lack of BASIC presents no real impediment in producing new software for the machine today, even back in the 80s a CPM targeted variety of BASIC could be sourced for the machine (if not a specific PC-8401 version). 30 years on, being equipt with a Z80A processor and the CPM 2.2 operating systems puts this laptop a good position for some software development.
Getting into BASIC
Both the versions of BASIC mentioned below are available from Gary J. Webes' excellent NEC PC-8201 support site WEB8201, where he holds a selection of software for the NEC PC-8401. NEC PC-8401. Check out the NEC 8401A / 8500 File Collection portion of the File Downloads page.
Microsoft BASIC
Unfortunately the size of MS BASIC makes it a non stater, it's more of a curiosity than something of practical usage. There are compilers available for MS BASIC, though a you still require libraries on the disk to run complied applications. Due to lack of disk space, compilation would need to be run on a host computer before moving the end product over to the PC-8401.
ZBAS
Interestingly and extra fortunately when saving files, ZBAS tokenises BASIC Commands, much like on a 128k ZX Spectrum. Thus allowing for some larger than expected applications to be saved on our rather limited 'solid' state storage.
If your going to leave BASIC laying around on the System, then ZBAS is clearly the one to have.
Moving to Modern 'C' Developement
While there are plenty of period development platforms and languages available for CPM, including 'C's and Pascals, you'll need to run these in a emulated environment as there is no room on the PC-8401 for such niceties. A load of CPM software and utilities are available on the retroarchive, although none of these programs are specific to the PC-8401.
Z88DK
Perhaps the easiest way to pursue software development for a CPM machine and the PC-8401 today is via the use of Z88dk. Z88dk is a 'C' compiler targeting TRS80s, Commodore 128s, Sinclair ZX81s and just about any machine containing a Z80 processor. Specifically for out purposes there is a dedicated CPM library.
In order to test Z88DK out with the PC-8401 I borrowed from an older ZX81 program I wrote up some time ago, "ZX Roman Numeral-Izer". The program is a simple roman numeral format checker, it takes Roman numerals as an argument and then correctly formats them to the agreed standards (Standards that even the Romans didn't always adhere to). After a couple of minor changes to remove some specific ZX81 extensions and put in some specific PC-8401-isations the project compiled perfectly.
If you're interested then the full source is below, bit of a rush and there will be better ways to achieve the same result I'm sure, still proof of concept proven.
NEC PC-8401 Roman Numeral-iser Listing
// To Compile for CPM do as below.
// zcc +cpm -lm -o rome.com rome.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef enum { true = 0, false = !true } bool;
static short isaNumArab[7] = {1, 5, 10,50,100,500,1000};
static char csaNumRome[8] = "ivxlcdm";
// Function Get Arabic value of Roman Numeral
short GetArabValue(char csMyNumerals[15]){
char csMyNumeral[2];
char csNumRome[2];
short iNumberNow = 0;
short iNumberLast = 0;
short iNumberTotal = 0;
short iCount1 = 0;
short iCount2 = 0;
bool bTest = false;
bool bNegative = false;
//Go through sMyNumerals input string backwards for easier rule matching
for (iCount1=strlen (csMyNumerals)-1; iCount1>=0; iCount1--){
strncpy(csMyNumeral, csMyNumerals+(iCount1*1), 1);
csMyNumeral[1]='\0';
for (iCount2=0; iCount2<=strlen(csaNumRome)-1; iCount2++){
strncpy(csNumRome, csaNumRome+(iCount2*1), 1);
csNumRome[1]='\0';
bTest = strnicmp(csMyNumeral, csNumRome,1);
if (bTest == true){
iNumberNow = isaNumArab[iCount2];
if ((iNumberNow > iNumberLast) || ((iNumberNow == iNumberLast) && (bNegative == false))){
iNumberTotal = iNumberTotal + iNumberNow;
bNegative = false;
} else {
iNumberTotal = iNumberTotal - iNumberNow;
bNegative = true;
}
}
iNumberLast = iNumberNow;
}
}
return iNumberTotal;
}
// Function Get Roman value of Arabic Numeral
void PrintRomeNumerals(int iMyNumberArab){
char csNumRome[16];
char csMyNumerals[16];
char csMyNumeral[2];
short iTemp[16];
short iNumberNow;
short iCount1 = 0;
short iCount2 = 0;
short iCount3 = 15;
memset(csNumRome,0,strlen(csNumRome));
memset(iTemp, 0, sizeof(iTemp));
if(iMyNumberArab>=4000){
strcpy(csNumRome,"Max Value 3999");
} else {
itoa(iMyNumberArab,csMyNumerals,10);
//csMyNumerals[15]='\0';
//Go through sMyNumerals input string backwards for easier rule matching
for (iCount1=strlen(csMyNumerals)-1; iCount1>=0; iCount1--){
strncpy(csMyNumeral, csMyNumerals+(iCount1*1), 1);
iNumberNow = atoi(csMyNumeral);
switch (iNumberNow) {
case 1: case 2: case 3:
for (iCount2=0;iCount2<iNumberNow;iCount2++){
iTemp[iCount3] = csaNumRome[(strlen(csMyNumerals)-1)*2-iCount1*2];
iCount3--;
}
break;
case 4:
iTemp[iCount3] = csaNumRome[(strlen(csMyNumerals)-1)*2-iCount1*2+1];
iTemp[iCount3-1] = csaNumRome[(strlen(csMyNumerals)-1)*2-iCount1*2];
iCount3=iCount3-2;
break;
case 5: case 6: case 7: case 8:
for (iCount2=0;iCount2<iNumberNow-5;iCount2++){
iTemp[iCount3] = csaNumRome[(strlen(csMyNumerals)-1)*2-iCount1*2];
iCount3--;
}
iTemp[iCount3] = csaNumRome[(strlen(csMyNumerals)-1)*2-iCount1*2+1];
iCount3--;
break;
case 9:
iTemp[iCount3] = csaNumRome[(strlen(csMyNumerals)-1)*2-iCount1*2+2];
iTemp[iCount3-1] = csaNumRome[(strlen(csMyNumerals)-1)*2-iCount1*2];
iCount3=iCount3-2;
break;
}
}
}
printf("\n Correct Format: ");
for (iCount2=0;iCount2<16;iCount2++){
if(iTemp[iCount2]!=0){
printf("%c",iTemp[iCount2]);
}
}
printf("\n");
return;
}
void BannerLine(short iLength, char cCharter){
short iCount1 = 0;
for (iCount1=0; iCount1<iLength; iCount1++){
printf("%c",cCharter);
}
}
void BannerSet(void){
short iCount1 = 0;
printf("\n");
BannerLine(32,149);
printf( "\n PC-8401 Roman Numeral-iser\n" );
BannerLine(32,149);
for(iCount1=0;iCount1<=strlen(csaNumRome)-1;iCount1++){
printf("\n Numeral: %c = Numeric: %d",csaNumRome[iCount1],isaNumArab[iCount1]);
if(iCount1 == strlen(csaNumRome)-1){
printf("\n");
}
}
BannerLine(32,149);
printf("\n Roman Numeral Format Check\n");
BannerLine(32,149);
}
short PressAnyKey(void){
char chr;
BannerLine(32,149);
printf("\n Input Another Numeral? Y/N: ");
chr = getchar();
if(chr == 'n' || chr == 'N'){
return 1;
} else {
return 0;
}
}
main(void)
{
char csMyNumerals[15];
bool PressKey = true;
char csMyNumberRome[15];
int iMyNumberArab = 0;
while( PressKey != false ) {
BannerSet();
printf("\n Roman Numerals: ");
fgets(csMyNumerals,15,stdin);
iMyNumberArab = GetArabValue(csMyNumerals);
printf(" Numeric Value: %d",iMyNumberArab);
PrintRomeNumerals(iMyNumberArab);
memset(csMyNumerals,0,strlen(csMyNumerals));
memset(csMyNumberRome,0,strlen(csMyNumberRome));
PressKey = PressAnyKey();
}
}
See RetroChallenge Intro, Part 1, Part_2, Part 3, Part 4, Part 5, Part 6, Part 7, Part 8
This is a great series, you have done a great job with your NEC.
ReplyDeleteMany years ago, a friend let me borrow his '8500, and I have been looking for one since then - hopefully soon...
WEB8201 has apparently undergone re-org, since the link you provide results in a 404 error. I looked a bit, and found the 8401/8500 files at: https://www.web8201.net/default.asp?content=filedirlist.asp
Thanks again, it was fun reading through your posts.
Thanks for pointing out the dead link, I've updated it. I've been on the hunt for a 8500 since this post, still no luck. Hopefully soon to :-)
Delete