Go to file
nino-porcino 03cc4e48d6 Update README.md 2021-12-16 18:35:45 +01:00
demo rename symbols, add string input demo 2021-12-16 18:33:56 +01:00
docs add programmer's reference guide 2021-12-16 11:05:21 +01:00
kickc separate apple1 target files; include defines in target files 2021-12-16 18:32:31 +01:00
lib string input; reunite screen1 and screen 2 constants 2021-12-16 18:35:25 +01:00
tetris rename symbol 2021-12-16 18:35:34 +01:00
tools separate apple1 target files; include defines in target files 2021-12-16 18:32:31 +01:00
.gitattributes Initial commit 2021-11-26 20:06:41 +01:00
.gitignore Update .gitignore 2021-12-08 19:51:18 +01:00
README.md Update README.md 2021-12-16 18:35:45 +01:00
env_kickc.bat move KickC folder 2021-12-15 16:52:16 +01:00

README.md

apple1-videocard-lib

Library and demos for the "Apple-1 Graphic Card" by P-LAB, featuring the TMS9918 Video Display Processo by Texas Instruments.

Repo structure

demo     a demo program that makes use of the library
docs     TMS9918 and Apple1 manuals
kickc    target configuration files for KickC
lib      the library files to include in your project
tetris   a demo game
tools    some build tools

Introduction

The library is a set of C routines that make very easy to use the TMS9918 on the Apple-1. It is intended to work with the "Apple-1 Graphic Card" board by P-LAB or any other video card that maps the TMS9918 in the $CC00-$CC01 memory range of the Apple-1.

The library is written in C with KickC which is a very efficient 6502 C compiler.

Choice of the screen mode

The library only supports screen modes 1 and 2 (screen 0 and screen 3 not being very useful). Both are 256x192 pixels but there are some differences you should consider when evaluating which mode to use:

  • Screen 1: there are 256 tiles that can be written very quickly on the screen, but the color choice is limited to 8 consecutive tiles for each color in the palette. It's commonly used for text applications with limited colors.

  • Screen 2: you can address every pixel on the screen with a limitation of 2 colors per line within an 8x8 tile grid. It's commonly used for bitmapped graphic or slow-but-colorful text.

Both screen modes support 32 sprites.

Working with screen 1

Some example code:

// *** first we set the SCREEN 1 mode

tms_init_regs(SCREEN1_TABLE);   // initializes the registers with SCREEN 1 precalculated values
screen1_prepare();              // prepares the screen to be used, loads a useful 8x8 ASCII font
tms_set_color(COLOR_VIOLET);    // sets border color to violet (see tms9918.h for the list of all colors)

// *** then we can use it

screen1_cls();                  // clears the screen 
screen1_putc('A');              // writes the character "A"
screen1_putc(CHR_BACKSPACE);    // goes back 1 character
screen1_putc('B');              // overwrites a "B" over it

// writes "A" in the bottom-right corner, causing the screen to scroll
screen1_locate(31,23);
screen1_putc("A");

// some printing
screen1_puts(CLS "Hello world");                  // CLS, REVERSE_ON, REVERSE_OFF
screen1_puts(REVERSE_ON "reverse" REVERSE_OFF);   // are macros defined in tms_screen1.h 
screen1_puts("Line1\nLine2");                     // '\n' is also supported

// simple string input from the keyboard (editing with CTRL-H is also supported)
char buffer[32];
screen1_strinput(buffer, 32);
screen1_prints("you wrote:");
screen1_prints(buffer);

Working with screen 2

Some example code:

// initializes the registers with SCREEN 2 precalculated values
tms_init_regs(SCREEN2_TABLE);   

// sometimes two colors need to be packed into a single byte
// you can easily do that with the FG_BG() macro:
byte mycolor = FG_BG(COLOR_BLACK,COLOR_WHITE);

// prepares the screeen to be used as a bitmap with default colors black on white
screen2_init_bitmap(mycolor);

// plots a pixel in the middle of the screen
screen2_plot(128,96);

// and erases it:   
screen2_plot_mode = PLOT_MODE_RESET;  // PLOT_MODE_INVERT is also supported
screen2_plot(128,96);
screen2_plot_mode = PLOT_MODE_SET;

// draws a diagonal line
screen2_line(0,0,255,191);

// writes a character from the embedded FONT
byte col = FB_BG(COLOR_DARK_RED,COLOR_LIGH_YELLOW);
screen2_putc('A', 31, 23, col);

// writes a string
screen2_puts(16, 12, col, "HELLO");

// note: screen2_putc() and screen2_puts() are fast but they
// can only print characters aligned within the 8x8 grid

Working with VRAM directly

Some example code:

// writes the value 42 at the VRAM location 8000
tms_set_vram_write_addr(8000);
TMS_WRITE_DATA_PORT(42);

// and re-reads it
tms_set_vram_read_addr(8000);
byte val = TMS_READ_DATA_PORT;

When using the default values that came with SCREEN1_TABLE[] and SCREEN2_TABLE[], the VRAM is organized according the following memory map:

// ZONE                RANGE          NAME YOU CAN USE IN C
// =========================================================== 
// pattern table       $0000-$17FF    TMS_PATTERN_TABLE       
// sprite patterns     $1800-$19FF    TMS_SPRITE_PATTERNS  
// color table         $2000-$27FF    TMS_COLOR_TABLE      
// name table          $3800-$3AFF    TMS_NAME_TABLE      
// sprite attributes   $3B00-$3BFF    TMS_SPRITE_ATTRS        

// example: writes the bitmap value 10101010 on row 3 of pattern 4
tms_set_vram_write_addr(TMS_PATTERN_TABLE+4*8+3);
TMS_WRITE_DATA_PORT(0b10101010);

Working on a more low-level

Setting the TMS9918 registers

// you can set a TMS9918 register directly with:
tms_write_reg(7, 0x1F);

// which also saves the written value to a buffer
// because the TMS does not allow to read from 
// its registers (they are write-only)
byte oldvalue = tms_regs_latch[7];

Working directly with the I/O chip interface

If you want to program the VDP directly you can use the following utility functions:

TMS_WRITE_CTRL_PORT(value);       // writes a byte to the control port ($CC01)
TMS_WRITE_DATA_PORT(value);       // writes a byte to the data port ($CC00)
byte value = TMS_READ_CTRL_PORT;  // reads the status register ($CC01)
byte value = TMS_READ_DATA_PORT;  // reads a byte from the data port ($CC00)

Apple-1 utility functions

There are also utility functions to interact with the Apple-1 screen and keyboard:

// prints hex "F3" on the Apple-1 screen
woz_print_hex(0xF3)

// prints "A" on the Apple-1 screen
woz_putc('A');

// prints "HELLO" on the Apple-1 screen
woz_puts("HELLO");

// gets a key from the keyboard (waits for it)
byte k = apple1_getkey();

// non blocking keyboard read: do something until RETURN is hit
while(1) {
   if(apple1_iskeypressed()) {
      if(apple1_readkey()==0x0d) break;
   }
   else do_something_else();
}                

Building the source code

To link the library, simply #include the .h files from the lib/ directory in your C source files.

Compile your sources with the KickC compiler, the tools/ directory contains a build.bat script example for Windows.

There are three configurations you can target with the KickC compiler switches -t target -targetdir thisrepopath/kickc:

  • apple1
  • apple1_jukebox
  • vic20

Target "apple1"

With this target, the compiled program will start at $280 in the free RAM on the Apple-1 (please make sure you have enough RAM).

TODO: add reference to hexdump.js

Target "apple1_jukebox"

This target is for expansion Cards that provide a ROM storage in the range $4000-$7FFF, as:

  • the "CodeTank" EEPROM daughterboard of the "Apple-1 Graphic Card"
  • "Juke-Box Card" FLASH

In this target configuration, the program is split into two segments:

  • the "Code" that resides in ROM at $4000
  • the "Data" that resides in RAM at $0280 (this because the program needs to change the "Data").

The only issue is that the "Data" segment needs to be initialized with the startup values (for example, the value that a global int variable takes before it's used).

The "Data" initialization needs to be done manually in the C program by explicitly calling apple1_eprom_init() in main(). The function will copy the ROM portion $7582-$7FFF into the "Data" segment at $0280-$0FFF.

The initialization values in the ROM range $7582-$7FFF are generated with the build script mkeprom.js which creates a fixed-length 16K binary file to be put on the EEPROM/FLASH.

Below is a recap of the memory map for this target:

$0000-$00FF zero page: holds some C program variables $0280-$0FFF RAM: C program "Data" segment $4000-$7581 ROM: C program "Code" segment $7582-$7FFF ROM: C program "Data" segment (startup values)

Target "vic20"

This target has been used during the development of the library where a custom made VIC-20 emulator was interfaced with an emulated TMS9918, thus allowing running tests when the real Apple-1 machine was not available.