first commit

This commit is contained in:
nino-porcino 2021-11-26 23:41:51 +01:00
parent 6e702e38cc
commit fef4219ef9
7 changed files with 1111 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.lst
*.prg

14
README.md Normal file
View File

@ -0,0 +1,14 @@
# apple1-videocard-lib
Library and demos for the Apple-1 TMS9918 video card by P-LAB.
# USAGE (Windows)
- `env.bat` sets CC65 path variable (run it once)
- `c.bat` compiles `test.c` for both VIC-20 and Apple-1
- `test_vic20.prg` runs on the "hybrid" VIC-20 emulator
- `node hexdump` gives the output in WOZMON format for the Apple-1
Note: Apple-1 start address is $4000, with TMS address range $C000-$C001

4
c.bat Normal file
View File

@ -0,0 +1,4 @@
cl65 -D APPLE1 --target none --start-addr $4000 -O test.c -o test_apple1.prg
cl65 --listing test.lst -D VIC20 --target vic20 -O test.c -o test_vic20.prg

1
env.bat Normal file
View File

@ -0,0 +1 @@
PATH %PATH%;C:\Programmi\cc65-snapshot-win32\bin

32
hexdump.js Normal file
View File

@ -0,0 +1,32 @@
const fs = require('fs');
let prg = fs.readFileSync("test_apple1.prg");
//prg = prg.slice(2); // skip header
let s = appledump(prg, 0x4000);
console.log(s);
function appledump(bytes, start) {
let rows=16;
let s="\r\n";
for(let r=0;r<bytes.length;r+=rows) {
s+= hex(r+start, 4) + ": ";
for(let c=0;c<rows;c++) {
let index = r+c;
if(index < bytes.length) {
const byte = bytes[r+c];
s+= hex(byte)+" ";
}
}
s+="\n";
}
return s;
}
function hex(value, size) {
if(size === undefined) size = 2;
let s = "0000" + value.toString(16);
return s.substr(s.length - size);
}

856
laser500_font.ascii.c Normal file
View File

@ -0,0 +1,856 @@
byte FONT[768] = {
0
, 0
, 0
, 0
, 0
, 0
, 0
, 0
, 8
, 8
, 8
, 8
, 8
, 0
, 8
, 0
, 20
, 20
, 20
, 0
, 0
, 0
, 0
, 0
, 20
, 20
, 62
, 20
, 62
, 20
, 20
, 0
, 8
, 30
, 40
, 28
, 10
, 60
, 8
, 0
, 48
, 50
, 4
, 8
, 16
, 38
, 6
, 0
, 16
, 40
, 40
, 16
, 42
, 36
, 26
, 0
, 8
, 8
, 8
, 0
, 0
, 0
, 0
, 0
, 8
, 16
, 32
, 32
, 32
, 16
, 8
, 0
, 8
, 4
, 2
, 2
, 2
, 4
, 8
, 0
, 8
, 42
, 28
, 8
, 28
, 42
, 8
, 0
, 0
, 8
, 8
, 62
, 8
, 8
, 0
, 0
, 0
, 0
, 0
, 0
, 8
, 8
, 16
, 0
, 0
, 0
, 0
, 62
, 0
, 0
, 0
, 0
, 0
, 0
, 0
, 0
, 0
, 0
, 8
, 0
, 0
, 2
, 4
, 8
, 16
, 32
, 0
, 0
, 28
, 34
, 38
, 42
, 50
, 34
, 28
, 0
, 8
, 24
, 8
, 8
, 8
, 8
, 28
, 0
, 28
, 34
, 2
, 12
, 16
, 32
, 62
, 0
, 62
, 2
, 4
, 12
, 2
, 34
, 28
, 0
, 4
, 12
, 20
, 36
, 62
, 4
, 4
, 0
, 62
, 32
, 60
, 2
, 2
, 34
, 28
, 0
, 14
, 16
, 32
, 60
, 34
, 34
, 28
, 0
, 62
, 2
, 4
, 8
, 16
, 32
, 32
, 0
, 28
, 34
, 34
, 28
, 34
, 34
, 28
, 0
, 28
, 34
, 34
, 30
, 2
, 4
, 56
, 0
, 0
, 0
, 8
, 0
, 8
, 0
, 0
, 0
, 0
, 0
, 8
, 0
, 8
, 8
, 16
, 0
, 4
, 8
, 16
, 32
, 16
, 8
, 4
, 0
, 0
, 0
, 62
, 0
, 62
, 0
, 0
, 0
, 16
, 8
, 4
, 2
, 4
, 8
, 16
, 0
, 28
, 34
, 4
, 8
, 8
, 0
, 8
, 0
, 28
, 34
, 42
, 46
, 44
, 32
, 28
, 0
, 8
, 20
, 34
, 34
, 62
, 34
, 34
, 0
, 60
, 34
, 34
, 60
, 34
, 34
, 60
, 0
, 28
, 34
, 32
, 32
, 32
, 34
, 28
, 0
, 56
, 36
, 34
, 34
, 34
, 36
, 56
, 0
, 62
, 32
, 32
, 56
, 32
, 32
, 62
, 0
, 62
, 32
, 32
, 56
, 32
, 32
, 32
, 0
, 30
, 32
, 32
, 32
, 38
, 34
, 30
, 0
, 34
, 34
, 34
, 62
, 34
, 34
, 34
, 0
, 28
, 8
, 8
, 8
, 8
, 8
, 28
, 0
, 2
, 2
, 2
, 2
, 2
, 34
, 28
, 0
, 34
, 36
, 40
, 48
, 40
, 36
, 34
, 0
, 32
, 32
, 32
, 32
, 32
, 32
, 62
, 0
, 34
, 54
, 42
, 42
, 34
, 34
, 34
, 0
, 34
, 34
, 50
, 42
, 38
, 34
, 34
, 0
, 28
, 34
, 34
, 34
, 34
, 34
, 28
, 0
, 60
, 34
, 34
, 60
, 32
, 32
, 32
, 0
, 28
, 34
, 34
, 34
, 42
, 36
, 26
, 0
, 60
, 34
, 34
, 60
, 40
, 36
, 34
, 0
, 28
, 34
, 32
, 28
, 2
, 34
, 28
, 0
, 62
, 8
, 8
, 8
, 8
, 8
, 8
, 0
, 34
, 34
, 34
, 34
, 34
, 34
, 28
, 0
, 34
, 34
, 34
, 34
, 34
, 20
, 8
, 0
, 34
, 34
, 34
, 42
, 42
, 54
, 34
, 0
, 34
, 34
, 20
, 8
, 20
, 34
, 34
, 0
, 34
, 34
, 20
, 8
, 8
, 8
, 8
, 0
, 62
, 2
, 4
, 8
, 16
, 32
, 62
, 0
, 62
, 48
, 48
, 48
, 48
, 48
, 62
, 0
, 0
, 32
, 16
, 8
, 4
, 2
, 0
, 0
, 62
, 6
, 6
, 6
, 6
, 6
, 62
, 0
, 8
, 20
, 34
, 0
, 0
, 0
, 0
, 0
, 0
, 0
, 0
, 0
, 0
, 0
, 0
, 62
, 16
, 8
, 4
, 0
, 0
, 0
, 0
, 0
, 0
, 0
, 28
, 2
, 30
, 34
, 30
, 0
, 32
, 32
, 60
, 34
, 34
, 34
, 60
, 0
, 0
, 0
, 30
, 32
, 32
, 32
, 30
, 0
, 2
, 2
, 30
, 34
, 34
, 34
, 30
, 0
, 0
, 0
, 28
, 34
, 62
, 32
, 30
, 0
, 12
, 18
, 16
, 60
, 16
, 16
, 16
, 0
, 0
, 0
, 28
, 34
, 34
, 30
, 2
, 28
, 32
, 32
, 60
, 34
, 34
, 34
, 34
, 0
, 8
, 0
, 24
, 8
, 8
, 8
, 28
, 0
, 2
, 0
, 6
, 2
, 2
, 2
, 18
, 12
, 32
, 32
, 34
, 36
, 56
, 36
, 34
, 0
, 24
, 8
, 8
, 8
, 8
, 8
, 28
, 0
, 0
, 0
, 52
, 42
, 42
, 42
, 34
, 0
, 0
, 0
, 60
, 34
, 34
, 34
, 34
, 0
, 0
, 0
, 28
, 34
, 34
, 34
, 28
, 0
, 0
, 0
, 60
, 34
, 34
, 60
, 32
, 32
, 0
, 0
, 30
, 34
, 34
, 30
, 2
, 2
, 0
, 0
, 46
, 48
, 32
, 32
, 32
, 0
, 0
, 0
, 30
, 32
, 28
, 2
, 60
, 0
, 16
, 16
, 60
, 16
, 16
, 18
, 12
, 0
, 0
, 0
, 34
, 34
, 34
, 38
, 26
, 0
, 0
, 0
, 34
, 34
, 34
, 20
, 8
, 0
, 0
, 0
, 34
, 34
, 42
, 42
, 20
, 0
, 0
, 0
, 34
, 20
, 8
, 20
, 34
, 0
, 0
, 0
, 34
, 36
, 20
, 24
, 8
, 48
, 0
, 0
, 62
, 4
, 8
, 16
, 62
, 0
, 6
, 8
, 8
, 48
, 8
, 8
, 6
, 0
, 8
, 8
, 8
, 8
, 8
, 8
, 8
, 8
, 48
, 8
, 8
, 6
, 8
, 8
, 48
, 0
, 26
, 44
, 0
, 0
, 0
, 0
, 0
, 0
};

202
test.c Normal file
View File

@ -0,0 +1,202 @@
#ifdef APPLE1
#define WOZMON 0xFF1F // enters monitor
#define ECHO 0xFFEF // output ascii character in A (A not destroyed)
#define PRBYTE 0xFFDC // print hex byte in A (A destroyed)
#define VDP_DATA 0xC000 // TMS9918 data port (VRAM)
#define VDP_REG 0xC001 // TMS9918 register port (write) or status (read)
#endif
#ifdef VIC20
#define ECHO 0xFFD2 // chrout routine in kernal rom
#define VDP_DATA 0xA000 // TMS9918 data port (VRAM)
#define VDP_REG 0xA001 // TMS9918 register port (write) or status (read)
#endif
#define WRITE_TO_REG 0b10000000
#define WRITE_TO_VRAM 0b01000000
#define READ_FROM_VRAM 0b00000000
typedef unsigned char byte;
typedef unsigned int word;
#define POKE(a,b) (*((byte *)(a))=(byte)(b))
// puts a character on the apple1 screen using the WOZMON routine
void fastcall woz_putc(byte c) {
asm("jsr %w", ECHO);
}
// puts a character on the apple1 screen using the WOZMON routine
void woz_mon() {
#ifdef APPLE1
asm("jmp %w", WOZMON);
#endif
}
// sets the VRAM address on the TMS9918
void fastcall set_vram_addr(word addr) {
asm("sta %w", VDP_REG); // write address low byte
// nops here ?
asm("txa"); // X = addres high byte
asm("and #%b", 0b00111111); // mask address high byte
asm("ora #%b", WRITE_TO_VRAM); // set "write to vram" flag bits "01" upper bits ("00" for read)
asm("sta %w", VDP_REG); // write flags and address high byte
// nops here ?
}
// writes a value to a TMS9918 register (0-7)
void write_reg(byte num, byte val) {
POKE(VDP_REG, val);
// nops here?
POKE(VDP_REG, (num & 0b00001111)|WRITE_TO_REG);
// nops here?
}
// SCREEN 1 VALUES
// sprite patterns: $0000
// pattern table: $0800 (256*8)
// sprite attributes: $1000
// unused: $1080
// name table: $1400 (32*24)
// unused: $1800
// color table: $2000 (32)
// unused $2020-$3FFF
#define PATTERN_TABLE 0x0800
#define NAME_TABLE 0x1400
#define COLOR_TABLE 0x2000
#define SPRITE_PATTERNS 0x0000
#define SPRITE_ATTRS 0x1000
#define SCREEN1_SIZE (32*24)
byte SCREEN1_TABLE[8] = {
0x00, 0xc0, 0x05, 0x80, 0x01, 0x20, 0x00, 0x25
};
#include "laser500_font.ascii.c"
// loads the Laser 500 font on the pattern table
void load_font() {
static byte *source = FONT;
static word i;
// start writing into VRAM from "space" character
set_vram_addr(PATTERN_TABLE+(32*8));
for(i=0;i<768;i++) {
POKE(VDP_DATA, *source);
source++;
}
source = FONT;
set_vram_addr(PATTERN_TABLE+((128+32)*8));
for(i=0;i<768;i++) {
POKE(VDP_DATA, ~(*source));
source++;
}
// TODO: inverted characters ?
}
static word tms_cursor;
// prints character to TMS (SCREEN 1 MODE)
void fastcall SCREEN1_PUTCHAR(byte c) {
set_vram_addr(tms_cursor);
POKE(VDP_DATA, c);
tms_cursor++;
}
// prints 0 terminated string pointed by YA
void SCREEN1_PUTS(char *s) {
byte c;
while(1) {
c = *s & 127; // TODO: explain why???
if(c==0) return;
SCREEN1_PUTCHAR(c);
s++;
}
}
void SCREEN1_HOME() {
tms_cursor = NAME_TABLE;
}
void SCREEN1_LOCATEXY(byte x, byte y) {
tms_cursor = NAME_TABLE + ((word)y)*32 + x;
}
void INIT_SCREEN1() {
static byte i;
for(i=0;i<8;i++) {
write_reg(i, SCREEN1_TABLE[i]);
}
}
void FILL_SCREEN1() {
static word i;
// fills name table with spaces (32)
set_vram_addr(NAME_TABLE);
for(i=0;i<SCREEN1_SIZE;i++) {
POKE(VDP_DATA, 32);
// nops here?
}
// fill pattern table with 0
set_vram_addr(PATTERN_TABLE);
for(i=0;i<256*8;i++) {
POKE(VDP_DATA, 0);
// nops here?
}
// fill color table with $1F
set_vram_addr(COLOR_TABLE);
for(i=0;i<32;i++) {
POKE(VDP_DATA, 0x1f);
// nops here?
}
}
void square_sprites() {
static byte i;
// fills first sprite pattern with 255
set_vram_addr(SPRITE_PATTERNS); // start writing in the sprite patterns
for(i=0;i<8;i++) {
POKE(VDP_DATA, 255);
}
// set sprite coordinates
set_vram_addr(SPRITE_ATTRS); // start writing in the sprite attribute
for(i=0;i<32;i++) {
POKE(VDP_DATA,(6+i)*8); // y coordinate
POKE(VDP_DATA,(6+i)*8); // x coordinate
POKE(VDP_DATA,0); // name
POKE(VDP_DATA,i); // color
}
}
void main() {
word i;
INIT_SCREEN1();
FILL_SCREEN1();
load_font();
SCREEN1_HOME(); SCREEN1_PUTS("*** P-LAB VIDEO CARD SYSTEM ***");
SCREEN1_LOCATEXY(0, 2); SCREEN1_PUTS("16K VRAM BYTES FREE");
SCREEN1_LOCATEXY(0, 4); SCREEN1_PUTS("READY.");
SCREEN1_LOCATEXY(0, 10);
for(i=0;i<256;i++) SCREEN1_PUTCHAR(i);
square_sprites();
woz_putc(42);
woz_mon();
}