I wanted to learn some Intel 8080 assembler programming, so I selected as a project to write a port of my JMON machine language monitor program that I wrote for the 6502 and Briel Apple 1 Replica.
After a couple of evenings of hacking I have a version running with six of the commands implemented. It is not so much a port, as an implementation from scratch of a subset of the commands and features of the 6502 version.
Some sample output is shown below (user input is in bold).
JMON Monitor 0.1 by Jeff Tranter
? ?
Valid commands:
C start end dest Copy memory
D address Dump memory
F start end data... Fill memory
G address Go
I Show info
K start end Checksum
L Clear screen
R Examine registers
S start end data... Search memory
T start end Test memory
V start end dest Verify memory
: address data... Write to memory
= address +/- address Hex math calculation
? Help
? I
JMON Monitor 0.1 by Jeff Tranter
CPU type: 8080
? R
A=00 BC=0000 DE=0000 HL=0000 F=00000110 SP=7000 PC=0000
? D 0000
0000: 32 9E 06 78 32 A0 06 79 32 A1 06 7A 32 A2 06 7B 2..x2..y2..z2..{
0010: 32 A3 06 7C 32 A4 06 7D 32 A5 06 F5 C1 79 32 9F 2..|2..}2....y2.
0020: 06 21 00 70 7C 32 A6 06 7D 32 A7 06 31 00 70 CD .!.p|2..}2..1.p.
0030: E5 02 21 A7 03 CD EC 02 3E 3F CD 92 02 CD FC 02 ..!......?......
0040: CD A1 02 CD 04 03 FE 43 C2 51 00 CD 85 02 C3 38 .......C.Q.....8
0050: 00 FE 44 C2 5C 00 CD EC 00 C3 38 00 FE 46 C2 67 ..D.\.....8..F.g
0060: 00 CD 85 02 C3 38 00 FE 47 C2 72 00 CD 4A 01 C3 .....8..G.r..J..
0070: 38 00 FE 49 C2 7D 00 CD 90 01 C3 38 00 FE 4B C2 8..I.}.....8..K.
0080: 88 00 CD 85 02 C3 38 00 FE 4C C2 93 00 CD 89 01 ......8..L......
0090: C3 38 00 FE 52 C2 9E 00 CD BC 01 C3 38 00 FE 53 .8..R.......8..S
00A0: C2 A9 00 CD 85 02 C3 38 00 FE 54 C2 B4 00 CD 85 .......8..T.....
00B0: 02 C3 38 00 FE 56 C2 BF 00 CD 85 02 C3 38 00 FE ..8..V.......8..
00C0: 3A C2 CA 00 CD 85 02 C3 38 00 FE 3D C2 D5 00 CD :.......8..=....
00D0: 85 02 C3 38 00 FE 3F C2 E0 00 CD 7B 02 C3 38 00 ...8..?....{..8.
00E0: CD AB 02 21 CA 03 CD EC 02 C3 38 00 CD 92 02 CD ...!......8.....
00F0: FC 02 CD 9C 03 D2 FC 00 CD AB 02 C9 CD AB 02 0E ................
0100: 18 E5 CD 43 03 3E 3A CD 92 02 06 10 CD FC 02 7E ...C..:........~
0110: CD 11 03 23 05 C2 0C 01 CD FC 02 E1 06 10 7E CD ...#..........~.
0120: D0 02 23 05 C2 1E 01 CD AB 02 0D C2 01 01 E5 21 ..#............!
0130: 51 06 CD EC 02 E1 CD A1 02 FE 1B C2 42 01 CD AB Q...........B...
0140: 02 C9 FE 20 CA FC 00 C3 36 01 CD 92 02 CD FC 02 ... ....6.......
0150: CD 9C 03 D2 5A 01 CD AB 02 C9 22 A8 06 CD AB 02 ....Z.....".....
0160: 2A 00 00 E5 3A A8 06 6F 3A A9 06 67 E5 3A A4 06 *...:..o:..g.:..
0170: 67 3A A5 06 6F 3A A2 06 57 3A A3 06 5F 3A A0 06 g:..o:..W:.._..
Press Space to continue, ESC to stop
? G 0000
It builds using the AS Macro Assembler. The binary can be put on an SD card and directly loaded into the Briel Altair 8800 memory and run.
I found debugging quite straightforward by using the front panel switches of the Briel Altair 8800. In particular, the ability to step, see disassembly on the console, and set a breakpoint address, were very useful. The JMON monitor makes some things a little easier than the front panel, like dumping the contents of memory.
While my goal was to learn 8080 assembly language programming, the monitor is somewhat useful in it's current form. You can find the source code here. It is licensed under the Apache 2.0 license so you are free to use it for any purpose. As time allows, I may implement more commands and features.
Being mostly familiar with the 6502, and its successors like the 6809 and 68000, it was interesting to see how the Intel 8080 compared. It has more registers and instructions than the 6502, making some things easier, but the learning curve is perhaps higher. It also has a few unusual instructions and quirks (for example, how do you clear the carry bit?).
Sunday, March 30, 2014
The AS Macro Assembler
I was recently writing some Intel 8080 assembly language code for the Briel Altair 8800 computer. Assembling programs by hand and toggling in the binary codes using the front panel gets tedious very quickly. I could write it under CP/M and use the CP/M assembler, but text editing is a little clumsy under CP/M, the assembler is slow, and I want to run the programs standalone on the Altair 8800 without using CP/M.
A search for a cross-assembler that would run on Linux and support the Intel 8080 identified the AS macro assembler. This is a full-featured, free (GPL licensed) cross-assembler that runs on Linux and other desktop operating systems. It actually supports about 75 different microprocessors -- quite an amazing accomplishment!
I built it from source on Linux without any problems and was soon assembling 8080 assembly language code. The documentation that comes with it is very extensive and complete and the assembler listings are easy to read (some representative listing code is shown below).
727/ 39F : ; GetAddress
728/ 39F : ; Gets a four character hex number from the keyboard.
729/ 39F : ; Ignores invalid characters. cancels and sets carry bit.
730/ 39F : ; Returns binary word in HL.
731/ 39F : ; Registers affected: A,B,H,L
732/ 39F :
733/ 39F : GetAddress:
734/ 39F : CD 90 03 call GetByte ; Get MSB
735/ 3A2 : D8 rc ; Exit if pressed
736/ 3A3 : 67 mov h,a ; Save MSB in H
737/ 3A4 : CD 90 03 call GetByte ; Get LSB
738/ 3A7 : D8 rc ; Exit if pressed
739/ 3A8 : 6F mov l,a ; Save LSB in L
740/ 3A9 : C9 ret
It can produce Intel Hex format files which can be transferred to CP/M, converted to .COM files using the CP/M LOAD program, and then executed.
I am using it to generate a binary file which can be copied to an SD card and then directly loaded and executed by the firmware on the Briel Altair 8800.
It is very fast to assemble programs under Linux, and seems to have all the features and more than I could want. If you are looking for a good cross-assembler for the 8080 or just about any processor, it may be just what you need.
A search for a cross-assembler that would run on Linux and support the Intel 8080 identified the AS macro assembler. This is a full-featured, free (GPL licensed) cross-assembler that runs on Linux and other desktop operating systems. It actually supports about 75 different microprocessors -- quite an amazing accomplishment!
I built it from source on Linux without any problems and was soon assembling 8080 assembly language code. The documentation that comes with it is very extensive and complete and the assembler listings are easy to read (some representative listing code is shown below).
727/ 39F : ; GetAddress
728/ 39F : ; Gets a four character hex number from the keyboard.
729/ 39F : ; Ignores invalid characters.
730/ 39F : ; Returns binary word in HL.
731/ 39F : ; Registers affected: A,B,H,L
732/ 39F :
733/ 39F : GetAddress:
734/ 39F : CD 90 03 call GetByte ; Get MSB
735/ 3A2 : D8 rc ; Exit if
736/ 3A3 : 67 mov h,a ; Save MSB in H
737/ 3A4 : CD 90 03 call GetByte ; Get LSB
738/ 3A7 : D8 rc ; Exit if
739/ 3A8 : 6F mov l,a ; Save LSB in L
740/ 3A9 : C9 ret
It can produce Intel Hex format files which can be transferred to CP/M, converted to .COM files using the CP/M LOAD program, and then executed.
I am using it to generate a binary file which can be copied to an SD card and then directly loaded and executed by the firmware on the Briel Altair 8800.
It is very fast to assemble programs under Linux, and seems to have all the features and more than I could want. If you are looking for a good cross-assembler for the 8080 or just about any processor, it may be just what you need.
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
SuperCalc on the Briel Altair 8800
In this last post of this blog series, supplementing my YouTube video The Briel Altair 8800 Kit, Part 5: Advanced Topics", I'll cover getting SuperCalc running on the Briel Altair 8800 under CP/M.
I found a copy of SuperCalc version 1.12 for CP/M on the Internet here. I also needed to download the file sc.hlp.
Unzip the archive files and copy the files to a CP/M drive. You should have the following files:
BALANCE.CAL BARRIER.CAL BRKEVN.CAL INSTALL.COM INSTALL.DAT INSTALL.OVL SC.COM SCHAZ15.COM SC.HLP SC.OVL
Like WordStar, you need to first run the install program and configure the terminal type. Fortunately, one of the choices is ANSI which is compatible with the VT100 terminal that the Briel Altair emulates. I also had to set the screen lines to 40 and columns to 80.
Below is a sample session running the install program. I haven't used the program a lot so there may be some additional tweaks that can be made to get it to work better. After configuration it you can run SuperCalc as "SC". The files BALANCE.CAL, BARRIER.CAL, and BRKEVN.CAL are sample spreadsheets that you can load.
I found a copy of SuperCalc version 1.12 for CP/M on the Internet here. I also needed to download the file sc.hlp.
Unzip the archive files and copy the files to a CP/M drive. You should have the following files:
BALANCE.CAL BARRIER.CAL BRKEVN.CAL INSTALL.COM INSTALL.DAT INSTALL.OVL SC.COM SCHAZ15.COM SC.HLP SC.OVL
Like WordStar, you need to first run the install program and configure the terminal type. Fortunately, one of the choices is ANSI which is compatible with the VT100 terminal that the Briel Altair emulates. I also had to set the screen lines to 40 and columns to 80.
Below is a sample session running the install program. I haven't used the program a lot so there may be some additional tweaks that can be made to get it to work better. After configuration it you can run SuperCalc as "SC". The files BALANCE.CAL, BARRIER.CAL, and BRKEVN.CAL are sample spreadsheets that you can load.
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.
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
Thursday, March 13, 2014
Building Apple II DOS From Source Code
I am currently reading the book Sophistication & Simplicity: The Life and Times of the Apple II Computer by Steven Weyhrich. It is a fascinating history of the Apple II series of computers, from the beginnings with the Apple I up to the present time.
Many of the books and historical articles about Apple do not give enough credit to the Apple II for getting the company started and generating the revenue and size of company that was need to develop later products like the Lisa, Macintosh, and iPhone.
Reading about Apple DOS reminded me that, recently, the original Apple II DOS source code was publicly released. It has quite an interesting history, which is covered both in the Steven Weyrich book as well as the Computer History Museum site where the materials can be downloaded.
There is both PDF format scans of the original assembler listing as well as a Microsoft Word file with the source code. I thought it would be fun to try to get the code to build using the CC65 assembler.
An evening's work was enough to get both the low-level disk routines source file as well as the full DOS source cross-compiled on a Linux system using CC65. Since I do not own an Apple II I am not able to test it. but the files assemble and appear to be correct.
I did not check every instruction in detail against the PDF source listing but the length of the code is correct and I checked a good number of the addresses. The Microsoft Word version was very helpful and was used as a starting point, but it did contain a number of errors which were resolved by looking at the scan of the assembly listing. Interestingly enough, the original source code assembler listing reports one assembly error in the code, which I believe I fixed correctly.
If you are interested in the ported code, it can be found here.
After doing the port, I found out that Scott LaBombard had also got the code built and even running on an Apple II. I plan to compare my binaries to his and see if there are any errors in my version that I missed.
I don't plan to do more with this, at least not unless I get my hands on an Apple II (I had an Apple //e and an Apple ][+ in the past but they were damaged a number of years ago in a basement flood).
References: