After a couple more hours of work and I have my Abandoned Farmhouse text adventure game ported to CP/M using the BDS C compiler. The changes were similar to those needed for the Yum game, and included some changes for the available string functions and initialization of some large arrays.
I haven't played the game to completion but it seems to be working well. You can find source code and a CP/M binary here.
Below is a transcript of playing a short game.
B>advent
Abandoned Farmhouse Adventure
By Jeff Tranter
Your four-year-old grandson has gone missing and was last seen headed in the
direction of the abandoned family farm. It's a dangerous place to play. You
have to find him before he gets hurt, and it will be getting dark soon...
? help
Valid Commands:
Go east/west/north/south/up/down
Look
Use object
Examine object
Take object
Drop object
Inventory
Help
Quit
You can abbreviate commands and directions to the first letter.
Type just the first letter of a direction to move.
? i
You are carrying:
flashlight
? l
You are at the door to the house.
You see:
key
bottle
You can go: east west
? drop flashlight
Dropped flashlight.
? t key
Took key.
? t bottle
Took bottle.
? n
You can't go north from here.
? l
You are at the door to the house.
You see:
flashlight
You can go: east west
? w
You are in the kitchen.
? l
You are in the kitchen.
You see:
candybar
You can go: south east down
? t candybar
Took candybar.
? w
You can't go west from here.
? l
You are in the kitchen.
You see:
nothing special
You can go: south east down
? s
You are in the dining room.
? l
You are in the dining room.
You see:
stale meat
You can go: north west
? u stale meat
The meat looked and tasted bad. You feel very sick and pass out.
Game over after 7 turns.
Do you want to play again (y/n)? n
B>
Showing posts with label Briel. Show all posts
Showing posts with label Briel. Show all posts
Wednesday, April 16, 2014
Wednesday, March 19, 2014
The Briel Altair 8800: External Serial Port/Console
A new feature included in the most recent build of Altair 8800 systems is an external serial port. It allows the console to be a serial port rather than the on-board VGA or composite video display and PS/2 keyboard.
It consists of a small card which plugs into the expansion connector. The board can piggyback on top of the RAM card if you have one installed in the expansion connector (you don't need the RAM disk board to use it). The board has a small ribbon cable and 9-pin serial port connector which can be installed on the rear panel in a cutout which is provided in the most recent build of systems. (If you want to buy a serial board and back panel for an existing Altair 8800, contact Vince Briel about pricing and availability.)
The serial board circuitry only consists of a MAX232 level shifter and connectors. The actual serial port is on the main 8800 motherboard. The serial board just converts it to and from RS-232 voltage levels and routes it out to the rear connector.
To use it, connect the serial port to a serial terminal or computer serial port running terminal emulation software (I use the Minicom program on Linux). You will typically need a "null modem" serial cable to do this. You can use a USB to serial adaptor if your computer does not have a serial port. The serial port defaults to 9600 bps with no hardware flow control. You can change the port speed usingF7 (from the PS/2 keyboard). The setting is persistent.
You need to be running the latest (version 5.2) firmware on the Altair 8800 to support a serial console.
The serial port is the same one that is used between the Propeller and ATMEL AVR that emulates the 8080. If a PS/2 keyboard is not present, the Propeller chip "stays out of the way" and the external serial port can be used as a console.
On boot up you should see on the video display:
Keyboard offline (this means the PS/2 keyboard is not present)
Serial port off (this means the internal serial is off and will use an external console)
With a PS/2 keyboard attached you can also useF7 to turn the on board serial on or off. If a keyboard plugged in you will still see output to the serial port but the system will not use it for input. One implication of this is that you cannot use the TUTILS to transfer files between the SD card and RAM disk when using the serial console.
On powerup the ATMEL AVR chip tries to contact the Propeller chip. If a terminal is connected at that time it seems to confuse the system and cause it to flash an error code on the INP LED. The procedure that worked for me was the following steps:
The main purpose of this is to run the system, such as CP/M, from an external terminal or computer. If the terminal emulates the same terminal type as your software is configured, you can run applications like WordStar or SuperCalc.
You can transfer files between your PC and computer if your terminal emulator program supports file sending and receiving. It might also be interesting to try running a file transfer program like KERMIT or XMODEM to transfer files as was done in the old days of CP/M and BBS systems.
It consists of a small card which plugs into the expansion connector. The board can piggyback on top of the RAM card if you have one installed in the expansion connector (you don't need the RAM disk board to use it). The board has a small ribbon cable and 9-pin serial port connector which can be installed on the rear panel in a cutout which is provided in the most recent build of systems. (If you want to buy a serial board and back panel for an existing Altair 8800, contact Vince Briel about pricing and availability.)
The serial board circuitry only consists of a MAX232 level shifter and connectors. The actual serial port is on the main 8800 motherboard. The serial board just converts it to and from RS-232 voltage levels and routes it out to the rear connector.
To use it, connect the serial port to a serial terminal or computer serial port running terminal emulation software (I use the Minicom program on Linux). You will typically need a "null modem" serial cable to do this. You can use a USB to serial adaptor if your computer does not have a serial port. The serial port defaults to 9600 bps with no hardware flow control. You can change the port speed using
You need to be running the latest (version 5.2) firmware on the Altair 8800 to support a serial console.
The serial port is the same one that is used between the Propeller and ATMEL AVR that emulates the 8080. If a PS/2 keyboard is not present, the Propeller chip "stays out of the way" and the external serial port can be used as a console.
On boot up you should see on the video display:
Keyboard offline (this means the PS/2 keyboard is not present)
Serial port off (this means the internal serial is off and will use an external console)
With a PS/2 keyboard attached you can also use
On powerup the ATMEL AVR chip tries to contact the Propeller chip. If a terminal is connected at that time it seems to confuse the system and cause it to flash an error code on the INP LED. The procedure that worked for me was the following steps:
- Unplug PS/2 keyboard.
- Power up the Altair.
- Connect serial port to the PC.
The main purpose of this is to run the system, such as CP/M, from an external terminal or computer. If the terminal emulates the same terminal type as your software is configured, you can run applications like WordStar or SuperCalc.
You can transfer files between your PC and computer if your terminal emulator program supports file sending and receiving. It might also be interesting to try running a file transfer program like KERMIT or XMODEM to transfer files as was done in the old days of CP/M and BBS systems.
Tuesday, March 18, 2014
WordStar 3.0 on the Briel Altair 8800
In the next instalment of this blog series, supplementing my YouTube video The Briel Altair 8800 Kit, Part 5: Advanced Topics, I'll cover getting WordStar installed, configured, and running on the Briel Altair 8800 under CP/M.
You can download WordStar 3.0 from here. You need to download and unzip the file WS30.ZIP on a desktop system. Then copy the files to the CP/M system, which should be the following:
INSTALL.COM MAILMRGE.OVR MERGPRIN.OVR WIMSGS.OVR WS.COM WSMSGS.OVR WSOVLY1.OVR WSU.COM
As mentioned in the video, you need to "patch" WordStar to support a non-standard terminal like the VT100/ANSI terminal that the Briel Altair 8800 emulates. The basic procedure is to run INSTALL.COM and enter the codes. It is a little tedious, but once done you have a version that works with the Briel Altair 8800. A complete transcript of the session is given at the end of this blog post. Thanks go to one of the Briel forum users for coming up with the patch codes.
Here is the list of patch values:
Symbol Address Value(s)
HITE 0248 28
WID 0249 50
CLEAD1 024A 2,1B,5B
CLEAD2 0253 1,3B
CTRAIL 0258 1,48
LINOFF 025E 1
COLOFF 025F 1
ASCUR 0260 2
ERAEOL 026D 3,1B,5B,4B
LINDEL 0274 4,1B,5B,31,4D
LININS 027B 4,1B,5B,31,4C
IVON 0284 4,1B,5B,31,6D
IVOFF 028B 4,1B,5B,30,6D
TRMINI 0292 2,1B,63
IDTEX 018F 20,56,54,2D,31,30,30,20,20,20,20,20,20,20,20,20
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20
Once configured, run WS to launch WordStar.
A Google search will turn up copies of the complete WordStar 3 manuals.
You may also want a shorter command reference. A couple can be found here and here.
Don't forget to put the terminal in WordStar arrow key mode usingF6 .
You can download WordStar 3.0 from here. You need to download and unzip the file WS30.ZIP on a desktop system. Then copy the files to the CP/M system, which should be the following:
INSTALL.COM MAILMRGE.OVR MERGPRIN.OVR WIMSGS.OVR WS.COM WSMSGS.OVR WSOVLY1.OVR WSU.COM
As mentioned in the video, you need to "patch" WordStar to support a non-standard terminal like the VT100/ANSI terminal that the Briel Altair 8800 emulates. The basic procedure is to run INSTALL.COM and enter the codes. It is a little tedious, but once done you have a version that works with the Briel Altair 8800. A complete transcript of the session is given at the end of this blog post. Thanks go to one of the Briel forum users for coming up with the patch codes.
Here is the list of patch values:
Symbol Address Value(s)
HITE 0248 28
WID 0249 50
CLEAD1 024A 2,1B,5B
CLEAD2 0253 1,3B
CTRAIL 0258 1,48
LINOFF 025E 1
COLOFF 025F 1
ASCUR 0260 2
ERAEOL 026D 3,1B,5B,4B
LINDEL 0274 4,1B,5B,31,4D
LININS 027B 4,1B,5B,31,4C
IVON 0284 4,1B,5B,31,6D
IVOFF 028B 4,1B,5B,30,6D
TRMINI 0292 2,1B,63
IDTEX 018F 20,56,54,2D,31,30,30,20,20,20,20,20,20,20,20,20
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20
Once configured, run WS to launch WordStar.
A Google search will turn up copies of the complete WordStar 3 manuals.
You may also want a shorter command reference. A couple can be found here and here.
Don't forget to put the terminal in WordStar arrow key mode using
Using the CP/M BDS C Compiler
I'll continue giving more details on the programs I demonstrated in the YouTube video The Briel Altair 8800 Kit, Part 5: Advanced Topics.
This time, the BDS C Compiler.
Go to Leo Zolman's BDS C web page and download the release file bdsc-all.zip.
BDS C comes with extensive documentation in the file bdsc-guide.pdf, but for the purposes of a simple demo, you can do the following. Unzip the file and copy these files to an SD card, and then transfer them to a CP/M drive:
BDSCIO.H C.CCC CC.COM CC2.COM
CLINK.COM DEFF.CRL DEFF2.CRL STDIO.H
The example program TAIL.C used in the demonstration was found here.
Here is an even shorter example C program that I wrote. I entered it as the filename EX1.C:
/* BDS C example 1 */
#include
main(argc,argv)
char **argv;
{
int i;
printf("Example Program 1\n");
for (i=1; i <=10; i++) {
printf("%d\n", i);
}
return 0;
}
Here is a sample session showing EX1.C and TAIL.C being compiled, linked, and run (commands typed by the user are in bold).
B>dir
B: EX1 C : NQUEENS C : BDSCIO H : C CCC
B: CC COM : CC2 COM : CLINK COM : DEFF CRL
B: DEFF2 CRL : STDIO H : TAIL C
B>cc ex1
BD Software C Compiler v1.60 (part I)
37K elbowroom
BD Software C Compiler v1.60 (part II)
34K to spare
B>clink ex1
BD Software C Linker v1.60
Last code address: 0E8D
Externals start at 0E8E, occupy 0006 bytes, last byte at 0E93
Top of memory: E805
Stack space: D972
Writing output...
46K link space remaining
B>ex1
Example Program 1
1
2
3
4
5
6
7
8
9
10
B>cc tail
BD Software C Compiler v1.60 (part I)
36K elbowroom
BD Software C Compiler v1.60 (part II)
33K to spare
B>clink tail
BD Software C Linker v1.60
Last code address: 1402
Externals start at 1403, occupy 0006 bytes, last byte at 1408
Top of memory: E805
Stack space: D3FD
Writing output...
44K link space remaining
B>tail -10 tail.c
}
int lastchar(c)
char c;
{
return (!c) || (c == CPMEOF);
}
B>tail
Usage: tail [-#]
B>dir
B: TAIL CRL : TAIL COM : NQUEENS CRL : NQUEENS COM
B: EX1 C : NQUEENS C : BDSCIO H : C CCC
B: CC COM : CC2 COM : CLINK COM : DEFF CRL
B: DEFF2 CRL : STDIO H : TAIL C : EX1 CRL
B: EX1 COM
Finally, here is the N Queens example program shown in the YouTube video. It will take a while to run to completion. First the source code, followed by example output.
/* Solve the n queens problem */
#include
#define MAX_N 8
int board[MAX_N*MAX_N];
int tries;
int solutions;
int n;
int nsq;
int cache[MAX_N+1]; /* get some savings by caching last position */
/* display the board */
void print_board()
{
int i;
printf("+");
for (i = 0; i < n ; i++)
printf("---");
printf("+\n");
for (i = 0; i < nsq ; i++) {
if ((i % n) == 0)
printf("|");
if (board[i])
printf(" Q ");
else
printf(" . ");
if (((i+1) % n) == 0)
printf("|\n");
}
printf("+");
for (i = 0; i < n ; i++)
printf("---");
printf("+\n");
}
/* find position of a piece */
int piece(n)
{
int i;
/* first try the cache */
if (board[cache[n]] == n)
return cache[n];
for (i = 0 ; i < nsq ; i++)
if (board[i] == n) {
cache[n] = i; /* cache it for next time */
return i;
}
return -1;
}
/* see if board is a solution */
void check_board()
{
int i, p, r, c, j;
/* loop over all pieces */
for (i = 1 ; i <= n ; i++) {
p = piece(i);
/* not a solution if piece in same row */
r = p / n;
for (c = 0 ; c < n ; c++) {
if (board[r*n+c] != 0 && board[r*n+c] != i)
return;
}
/* not a solution if piece in same column */
c = p % n;
for (r = 0 ; r < n ; r++) {
if (board[r*n+c] != 0 && board[r*n+c] != i)
return;
}
/* not a solution if piece in same \ diagonal */
for (j = -n ; j < n ; j++) {
r = p / n + j;
c = p % n + j;
if ((r >= 0) && (r < n) && (c >= 0) && (c < n) &&
(board[r*n+c] != 0) && (board[r*n+c] != i))
return;
}
/* not a solution if piece in same / diagonal */
for (j = -n ; j < n ; j++) {
r = p / n + j;
c = p % n - j;
if ((r >= 0) && (r < n) && (c >= 0) && (c < n) &&
(board[r*n+c] != 0) && (board[r*n+c] != i))
return;
}
}
solutions++;
print_board();
}
/* move piece p to next position */
int next_pos(p)
{
int i, t;
if (p < 1)
return 0;
i = piece(p);
if (i < nsq-n+p-1) {
board[i] = 0;
board[i+1] = p;
return 1;
} else {
t = next_pos(p-1);
board[i] = 0;
board[piece(p-1)+1] = p;
return t;
}
}
/* generate the next possible solution */
int next_board()
{
return next_pos(n);
}
int main()
{
int i;
solutions = 0;
n = 5;
nsq = n*n; /* save n^2 to avoid calculating it again */
printf("\n\n\n\nSOLVING N QUEENS PROBLEM FOR N = %d\n", n);
/* clear the board */
for (i = 0 ; i < nsq ; i++)
board[i] = 0;
/* initially place the queens */
for (i = 0 ; i < n ; i++)
board[i] = i+1;
do {
tries++;
check_board();
} while (next_board());
printf("FOUND %d SOLUTIONS AFTER %ld TRIES.\n", solutions, tries);
return 0;
}
B>nqueens
SOLVING N QUEENS PROBLEM FOR N = 5
+---------------+
| Q . . . . |
| . . Q . . |
| . . . . Q |
| . Q . . . |
| . . . Q . |
+---------------+
+---------------+
| Q . . . . |
| . . . Q . |
| . Q . . . |
| . . . . Q |
| . . Q . . |
+---------------+
+---------------+
| . Q . . . |
| . . . Q . |
| Q . . . . |
| . . Q . . |
| . . . . Q |
+---------------+
+---------------+
| . Q . . . |
| . . . . Q |
| . . Q . . |
| Q . . . . |
| . . . Q . |
+---------------+
+---------------+
| . . Q . . |
| Q . . . . |
| . . . Q . |
| . Q . . . |
| . . . . Q |
+---------------+
+---------------+
| . . Q . . |
| . . . . Q |
| . Q . . . |
| . . . Q . |
| Q . . . . |
+---------------+
+---------------+
| . . . Q . |
| Q . . . . |
| . . Q . . |
| . . . . Q |
| . Q . . . |
+---------------+
+---------------+
| . . . Q . |
| . Q . . . |
| . . . . Q |
| . . Q . . |
| Q . . . . |
+---------------+
+---------------+
| . . . . Q |
| . Q . . . |
| . . . Q . |
| Q . . . . |
| . . Q . . |
+---------------+
+---------------+
| . . . . Q |
| . . Q . . |
| Q . . . . |
| . . . Q . |
| . Q . . . |
+---------------+
FOUND 10 SOLUTIONS AFTER -12406 TRIES.
B>
This time, the BDS C Compiler.
Go to Leo Zolman's BDS C web page and download the release file bdsc-all.zip.
BDS C comes with extensive documentation in the file bdsc-guide.pdf, but for the purposes of a simple demo, you can do the following. Unzip the file and copy these files to an SD card, and then transfer them to a CP/M drive:
BDSCIO.H C.CCC CC.COM CC2.COM
CLINK.COM DEFF.CRL DEFF2.CRL STDIO.H
The example program TAIL.C used in the demonstration was found here.
Here is an even shorter example C program that I wrote. I entered it as the filename EX1.C:
/* BDS C example 1 */
#include
main(argc,argv)
char **argv;
{
int i;
printf("Example Program 1\n");
for (i=1; i <=10; i++) {
printf("%d\n", i);
}
return 0;
}
Here is a sample session showing EX1.C and TAIL.C being compiled, linked, and run (commands typed by the user are in bold).
B>dir
B: EX1 C : NQUEENS C : BDSCIO H : C CCC
B: CC COM : CC2 COM : CLINK COM : DEFF CRL
B: DEFF2 CRL : STDIO H : TAIL C
B>cc ex1
BD Software C Compiler v1.60 (part I)
37K elbowroom
BD Software C Compiler v1.60 (part II)
34K to spare
B>clink ex1
BD Software C Linker v1.60
Last code address: 0E8D
Externals start at 0E8E, occupy 0006 bytes, last byte at 0E93
Top of memory: E805
Stack space: D972
Writing output...
46K link space remaining
B>ex1
Example Program 1
1
2
3
4
5
6
7
8
9
10
B>cc tail
BD Software C Compiler v1.60 (part I)
36K elbowroom
BD Software C Compiler v1.60 (part II)
33K to spare
B>clink tail
BD Software C Linker v1.60
Last code address: 1402
Externals start at 1403, occupy 0006 bytes, last byte at 1408
Top of memory: E805
Stack space: D3FD
Writing output...
44K link space remaining
B>tail -10 tail.c
}
int lastchar(c)
char c;
{
return (!c) || (c == CPMEOF);
}
B>tail
Usage: tail [-#]
B>dir
B: TAIL CRL : TAIL COM : NQUEENS CRL : NQUEENS COM
B: EX1 C : NQUEENS C : BDSCIO H : C CCC
B: CC COM : CC2 COM : CLINK COM : DEFF CRL
B: DEFF2 CRL : STDIO H : TAIL C : EX1 CRL
B: EX1 COM
Finally, here is the N Queens example program shown in the YouTube video. It will take a while to run to completion. First the source code, followed by example output.
/* Solve the n queens problem */
#include
#define MAX_N 8
int board[MAX_N*MAX_N];
int tries;
int solutions;
int n;
int nsq;
int cache[MAX_N+1]; /* get some savings by caching last position */
/* display the board */
void print_board()
{
int i;
printf("+");
for (i = 0; i < n ; i++)
printf("---");
printf("+\n");
for (i = 0; i < nsq ; i++) {
if ((i % n) == 0)
printf("|");
if (board[i])
printf(" Q ");
else
printf(" . ");
if (((i+1) % n) == 0)
printf("|\n");
}
printf("+");
for (i = 0; i < n ; i++)
printf("---");
printf("+\n");
}
/* find position of a piece */
int piece(n)
{
int i;
/* first try the cache */
if (board[cache[n]] == n)
return cache[n];
for (i = 0 ; i < nsq ; i++)
if (board[i] == n) {
cache[n] = i; /* cache it for next time */
return i;
}
return -1;
}
/* see if board is a solution */
void check_board()
{
int i, p, r, c, j;
/* loop over all pieces */
for (i = 1 ; i <= n ; i++) {
p = piece(i);
/* not a solution if piece in same row */
r = p / n;
for (c = 0 ; c < n ; c++) {
if (board[r*n+c] != 0 && board[r*n+c] != i)
return;
}
/* not a solution if piece in same column */
c = p % n;
for (r = 0 ; r < n ; r++) {
if (board[r*n+c] != 0 && board[r*n+c] != i)
return;
}
/* not a solution if piece in same \ diagonal */
for (j = -n ; j < n ; j++) {
r = p / n + j;
c = p % n + j;
if ((r >= 0) && (r < n) && (c >= 0) && (c < n) &&
(board[r*n+c] != 0) && (board[r*n+c] != i))
return;
}
/* not a solution if piece in same / diagonal */
for (j = -n ; j < n ; j++) {
r = p / n + j;
c = p % n - j;
if ((r >= 0) && (r < n) && (c >= 0) && (c < n) &&
(board[r*n+c] != 0) && (board[r*n+c] != i))
return;
}
}
solutions++;
print_board();
}
/* move piece p to next position */
int next_pos(p)
{
int i, t;
if (p < 1)
return 0;
i = piece(p);
if (i < nsq-n+p-1) {
board[i] = 0;
board[i+1] = p;
return 1;
} else {
t = next_pos(p-1);
board[i] = 0;
board[piece(p-1)+1] = p;
return t;
}
}
/* generate the next possible solution */
int next_board()
{
return next_pos(n);
}
int main()
{
int i;
solutions = 0;
n = 5;
nsq = n*n; /* save n^2 to avoid calculating it again */
printf("\n\n\n\nSOLVING N QUEENS PROBLEM FOR N = %d\n", n);
/* clear the board */
for (i = 0 ; i < nsq ; i++)
board[i] = 0;
/* initially place the queens */
for (i = 0 ; i < n ; i++)
board[i] = i+1;
do {
tries++;
check_board();
} while (next_board());
printf("FOUND %d SOLUTIONS AFTER %ld TRIES.\n", solutions, tries);
return 0;
}
B>nqueens
SOLVING N QUEENS PROBLEM FOR N = 5
+---------------+
| Q . . . . |
| . . Q . . |
| . . . . Q |
| . Q . . . |
| . . . Q . |
+---------------+
+---------------+
| Q . . . . |
| . . . Q . |
| . Q . . . |
| . . . . Q |
| . . Q . . |
+---------------+
+---------------+
| . Q . . . |
| . . . Q . |
| Q . . . . |
| . . Q . . |
| . . . . Q |
+---------------+
+---------------+
| . Q . . . |
| . . . . Q |
| . . Q . . |
| Q . . . . |
| . . . Q . |
+---------------+
+---------------+
| . . Q . . |
| Q . . . . |
| . . . Q . |
| . Q . . . |
| . . . . Q |
+---------------+
+---------------+
| . . Q . . |
| . . . . Q |
| . Q . . . |
| . . . Q . |
| Q . . . . |
+---------------+
+---------------+
| . . . Q . |
| Q . . . . |
| . . Q . . |
| . . . . Q |
| . Q . . . |
+---------------+
+---------------+
| . . . Q . |
| . Q . . . |
| . . . . Q |
| . . Q . . |
| Q . . . . |
+---------------+
+---------------+
| . . . . Q |
| . Q . . . |
| . . . Q . |
| Q . . . . |
| . . Q . . |
+---------------+
+---------------+
| . . . . Q |
| . . Q . . |
| Q . . . . |
| . . . Q . |
| . Q . . . |
+---------------+
FOUND 10 SOLUTIONS AFTER -12406 TRIES.
B>
Monday, March 17, 2014
Using the CP/M Assembler
I'll continue giving more details on the programs I demonstrated in the YouTube video The Briel Altair 8800 Kit, Part 5: Advanced Topics.
This time, the CP/M Assembler.
To follow the demos in the video, boot up a CP/M 2.2 image that has the assembler (ASM.COM), loader (LOAD.COM), and optionally the DDT debugger (DDT.COM). The Briel CPM22.DSK image on the CD will work.
If you use CP/M 3, there is a more sophisticated macro assembler called MAC.COM. I have not tried it with these examples.
Create a file HELLO.ASM with the following source code and copy it to your SD card. Use TREAD to copy it to the CP/M disk (or enter it locally on CP/M if you have a text editor such as ED or WordStar).
; This is an example of the "Hello World" program.
; Uses 8080 assembler mnemonics.
ORG 100h ; cpm programs start address.
JMP START ; go to program start.
; Variable storage space
MsgStr: DB 13,10,'Hello world.',13,10,0
Stack1: DW 0 ; place to save old stack.
Sbot: DS 32 ; temp stack for us to use.
; Constants
STOP: EQU $-1 ; top of our stack.
BDOS: EQU 5 ; address of BDOS entry.
; Start of code segment
START: LXI H, 0 ; HL = 0.
DAD SP ; HL = SP.
SHLD Stack1 ; save original stack.
LXI H, STOP ; HL = address of new stack.
SPHL ; stack pointer = our stack.
LXI H, MsgStr ; HL = address of string.
LOOP1: MOV A, M ; read string char.
ORA A ; set cpu flags.
JZ EXIT ; if char = 0 done.
MOV E, A ; E = char to send.
MVI C, 2 ; we want BDOS func 2.
PUSH H ; save HL register.
CALL BDOS ; call BDOS function.
POP H ; restore HL register
INX H ; point to next char.
JMP LOOP1 ; do next char.
; Exit and return code
EXIT: LHLD Stack1 ; HL = entry stack address.
SPHL ; SP = value on entry.
RET ; return control back to CPM.
END
Since I recorded the video, I found an even simpler hello world program which you can use instead if you like. The source code is below. I saved it as HELLO1.ASM and used it in the examples that follow.
org 100h
bdos equ 0005h ; BDOS entry point
start: mvi c,9 ; BDOS function: output string
lxi d,msg$ ; address of msg
call bdos
ret ; return to CCP
msg$: db 'Hello, world!$'
end
Here is an example session assembling, linking, and running it, and disassembling it with DDT. Commands typed by the user are in bold. Note that in the video, when I demonstrated DDT, I did not correctly load the file, so the disassembly in the video was incorrect. The example below is correct. Note that this session was run on drive B so I needed to specify the path to the tools on drive A.
B>a:asm hello1
CP/M ASSEMBLER - VER 2.0
0117
000H USE FACTOR
END OF ASSEMBLY
B>a:load hello1
FIRST ADDRESS 0100
LAST ADDRESS 0116
BYTES READ 0017
RECORDS WRITTEN 01
B>type hello1.hex
:100100000E09110901CD0500C948656C6C6F2C20E2
:07011000776F726C6421247B
:0000000000
B>type hello1.prn
0100 org 100h
0005 = bdos equ 0005h ; BDOS entry point
0100 0E09 start: mvi c,9 ; BDOS function: output string
0102 110901 lxi d,msg$ ; address of msg
0105 CD0500 call bdos
0108 C9 ret ; return to CCP
0109 48656C6C6Fmsg$: db 'Hello, world!$'
0117 end
B>a:ddt hello1.com
DDT VERS 2.2
NEXT PC
0180 0100
-l
0100 MVI C,09
0102 LXI D,0109
0105 CALL 0005
0108 RET
0109 MOV C,B
010A MOV H,L
010B MOV L,H
010C MOV L,H
010D MOV L,A
010E INR L
010F ??= 20
-^C
B>
Note that the assembler is for the Intel 8080. If you find CP/M programs for the Zilog Z80 and try to build them, they will not assemblem as it uses different mnemonics. They also won't run on the
Briel Altair 8800 as it only emulates Intel 8080 instructions.
The source for the "hello world" 8080 assembler program I listed above, as well as more information on ASM, can be found here.
This time, the CP/M Assembler.
To follow the demos in the video, boot up a CP/M 2.2 image that has the assembler (ASM.COM), loader (LOAD.COM), and optionally the DDT debugger (DDT.COM). The Briel CPM22.DSK image on the CD will work.
If you use CP/M 3, there is a more sophisticated macro assembler called MAC.COM. I have not tried it with these examples.
Create a file HELLO.ASM with the following source code and copy it to your SD card. Use TREAD to copy it to the CP/M disk (or enter it locally on CP/M if you have a text editor such as ED or WordStar).
; This is an example of the "Hello World" program.
; Uses 8080 assembler mnemonics.
ORG 100h ; cpm programs start address.
JMP START ; go to program start.
; Variable storage space
MsgStr: DB 13,10,'Hello world.',13,10,0
Stack1: DW 0 ; place to save old stack.
Sbot: DS 32 ; temp stack for us to use.
; Constants
STOP: EQU $-1 ; top of our stack.
BDOS: EQU 5 ; address of BDOS entry.
; Start of code segment
START: LXI H, 0 ; HL = 0.
DAD SP ; HL = SP.
SHLD Stack1 ; save original stack.
LXI H, STOP ; HL = address of new stack.
SPHL ; stack pointer = our stack.
LXI H, MsgStr ; HL = address of string.
LOOP1: MOV A, M ; read string char.
ORA A ; set cpu flags.
JZ EXIT ; if char = 0 done.
MOV E, A ; E = char to send.
MVI C, 2 ; we want BDOS func 2.
PUSH H ; save HL register.
CALL BDOS ; call BDOS function.
POP H ; restore HL register
INX H ; point to next char.
JMP LOOP1 ; do next char.
; Exit and return code
EXIT: LHLD Stack1 ; HL = entry stack address.
SPHL ; SP = value on entry.
RET ; return control back to CPM.
END
Since I recorded the video, I found an even simpler hello world program which you can use instead if you like. The source code is below. I saved it as HELLO1.ASM and used it in the examples that follow.
org 100h
bdos equ 0005h ; BDOS entry point
start: mvi c,9 ; BDOS function: output string
lxi d,msg$ ; address of msg
call bdos
ret ; return to CCP
msg$: db 'Hello, world!$'
end
Here is an example session assembling, linking, and running it, and disassembling it with DDT. Commands typed by the user are in bold. Note that in the video, when I demonstrated DDT, I did not correctly load the file, so the disassembly in the video was incorrect. The example below is correct. Note that this session was run on drive B so I needed to specify the path to the tools on drive A.
B>a:asm hello1
CP/M ASSEMBLER - VER 2.0
0117
000H USE FACTOR
END OF ASSEMBLY
B>a:load hello1
FIRST ADDRESS 0100
LAST ADDRESS 0116
BYTES READ 0017
RECORDS WRITTEN 01
B>type hello1.hex
:100100000E09110901CD0500C948656C6C6F2C20E2
:07011000776F726C6421247B
:0000000000
B>type hello1.prn
0100 org 100h
0005 = bdos equ 0005h ; BDOS entry point
0100 0E09 start: mvi c,9 ; BDOS function: output string
0102 110901 lxi d,msg$ ; address of msg
0105 CD0500 call bdos
0108 C9 ret ; return to CCP
0109 48656C6C6Fmsg$: db 'Hello, world!$'
0117 end
B>a:ddt hello1.com
DDT VERS 2.2
NEXT PC
0180 0100
-l
0100 MVI C,09
0102 LXI D,0109
0105 CALL 0005
0108 RET
0109 MOV C,B
010A MOV H,L
010B MOV L,H
010C MOV L,H
010D MOV L,A
010E INR L
010F ??= 20
-^C
B>
Note that the assembler is for the Intel 8080. If you find CP/M programs for the Zilog Z80 and try to build them, they will not assemblem as it uses different mnemonics. They also won't run on the
Briel Altair 8800 as it only emulates Intel 8080 instructions.
The source for the "hello world" 8080 assembler program I listed above, as well as more information on ASM, can be found here.
Labels:
Altair 8800,
assembler,
Briel,
CP/M,
Intel 8080
Running Microsoft Basic 5 on CP/M
I recently posted a series of YouTube videos on the Briel Altair 800 kit, the most recent instalment being The Briel Altair 8800 Kit, Part 5: Advanced Topics which I posted yesterday.
To supplement what I demonstrated there, I'm going to post more details and the files I used, to make it easier for others who want to try out the relevant software.
First, Microsoft BASIC.
Version 5.21 of Microsoft BASIC-80 dated 28-Jul-81 is included on the Briel Altair CD in the CP/M 3 disk image (CPM302GN.DSK) as the file MBASIC.COM. It will also run on CP/M version 2.2.
The Microsoft BASIC-80 ver 5.0 Reference Manual can be found on the Internet here.
Many of the programs from the David Ahl Creative Computing books on computer games can be found on the Briel Altair CD in the directory BASIC Software/creative computing.
Here is the source listing for the memory dump program I demonstrated. I wrote it quickly to demonstrate some of the BASIC-80 features like displaying numbers in hexadecimal.
100 REM Dump memory in hex and ASCII.
110 A = &H100 : REM Start address
120 A$ = "000" + HEX$(A)
130 A$ = RIGHT$(A$,4)
140 PRINT A$;": ";
150 FOR I = 0 TO 15
160 S$ = "0" +HEX$(PEEK(A+I))
170 S$ = RIGHT$(S$,2)
180 PRINT S$;" ";
190 NEXT I
200 FOR I = 0 TO 15
210 IF PEEK(A+I) >= 32 AND PEEK(A+I) <= 126 THEN PRINT CHR$(PEEK(A+I)); ELSE PRINT".";
220 NEXT I
230 PRINT
240 A = A +16
250 GOTO 120
To supplement what I demonstrated there, I'm going to post more details and the files I used, to make it easier for others who want to try out the relevant software.
First, Microsoft BASIC.
Version 5.21 of Microsoft BASIC-80 dated 28-Jul-81 is included on the Briel Altair CD in the CP/M 3 disk image (CPM302GN.DSK) as the file MBASIC.COM. It will also run on CP/M version 2.2.
The Microsoft BASIC-80 ver 5.0 Reference Manual can be found on the Internet here.
Many of the programs from the David Ahl Creative Computing books on computer games can be found on the Briel Altair CD in the directory BASIC Software/creative computing.
Here is the source listing for the memory dump program I demonstrated. I wrote it quickly to demonstrate some of the BASIC-80 features like displaying numbers in hexadecimal.
100 REM Dump memory in hex and ASCII.
110 A = &H100 : REM Start address
120 A$ = "000" + HEX$(A)
130 A$ = RIGHT$(A$,4)
140 PRINT A$;": ";
150 FOR I = 0 TO 15
160 S$ = "0" +HEX$(PEEK(A+I))
170 S$ = RIGHT$(S$,2)
180 PRINT S$;" ";
190 NEXT I
200 FOR I = 0 TO 15
210 IF PEEK(A+I) >= 32 AND PEEK(A+I) <= 126 THEN PRINT CHR$(PEEK(A+I)); ELSE PRINT".";
220 NEXT I
230 PRINT
240 A = A +16
250 GOTO 120
Labels:
Altair 8800,
BASIC,
Basic-80,
Briel,
CP/M,
Creative Computing,
Microsoft
Subscribe to:
Posts (Atom)