Showing posts with label Briel. Show all posts
Showing posts with label Briel. Show all posts

Wednesday, April 16, 2014

My Text Adventure Game Running on CP/M

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>

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 using F7 (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 use F7 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:

  1. Unplug PS/2 keyboard.
  2. Power up the Altair.
  3. 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 using F6.


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>

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.

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