diff --git a/apple1.h b/apple1.h new file mode 100644 index 0000000..3671d08 --- /dev/null +++ b/apple1.h @@ -0,0 +1,85 @@ +#ifdef APPLE1 + // APPLE1 + #pragma start_address(0x4000) + const word WOZMON = 0xFF1F; // enters monitor + const word ECHO = 0xFFEF; // output ascii character in A (A not destroyed) + const word PRBYTE = 0xFFDC; // print hex byte in A (A destroyed) + const word KEY_DATA = 0xd010; // read key + const word KEY_CTRL = 0xd011; // control port + const word TERM_DATA = 0xd012; // write ascii + const word TERM_CTRL = 0xd013; // control port +#else + // VIC20 + const word ECHO = 0xFFD2; // chrout routine in kernal rom + const word GETIN = 0xFFE4; // GETIN keyboard read routine +#endif + +// prints a hex byte using the WOZMON routine +void woz_put_hex(byte c) { + #ifdef APPLE1 + asm { + lda c + jsr PRBYTE + }; + #else + asm { + lda c + jsr ECHO + }; + #endif +} + +// puts a character on the apple1 screen using the WOZMON routine +void woz_putc(byte c) { + asm { + lda c + jsr ECHO + } +} + +// puts a 0-terminated string on the apple1 screen +void woz_puts(byte *s) { + byte c; + while(c=*s++) woz_putc(c); +} + +// returns to WOZMON prompt +void woz_mon() { + #ifdef APPLE1 + asm { + jmp WOZMON + } + #endif +} + +// returns nonzero if a key has been pressed +inline byte keypressed() { + #ifdef APPLE1 + return PEEK(KEY_DATA) & 0x80; + #else + return 0; + #endif +} + +// reads a key from the apple-1 keyboard +byte woz_getkey() { + #ifdef APPLE1 + asm { + __wait: + lda KEY_CTRL + bpl __wait + } + return PEEK(KEY_DATA) & 0x7f; + #else + byte key; + byte const *keyptr = &key; + kickasm(uses keyptr, uses GETIN) {{ + __wait: + jsr GETIN + cmp #0 + beq __wait + sta keyptr + }} + return key; + #endif +} diff --git a/bug/bug1.c b/bug/bug1.c new file mode 100644 index 0000000..65cde19 --- /dev/null +++ b/bug/bug1.c @@ -0,0 +1,28 @@ +#pragma zp_reserve(0,1,2) + +volatile word tick_counts; + +__interrupt(hardware_all) void interrupt_handler() { + tick_counts++; +} + +void install_interrupt() { + *((word *)0x0001) = (word) &interrupt_handler; +} + +void main() { + // make some use of "tick_counts" + *((word *)0x0001) = tick_counts; + + install_interrupt(); +} + +/* +const byte *VDP_DATA = 0xA000; // TMS9918 data port (VRAM) +#define TMS_READ_DATA (*VDP_DATA); + +void main() { + byte x = TMS_READ_DATA(); + *((byte *)0xFFFF) = x; +} +*/ \ No newline at end of file diff --git a/c.bat b/c.bat index 2a1d459..1a2b4c5 100644 --- a/c.bat +++ b/c.bat @@ -6,4 +6,12 @@ copy test.prg test_vic20.prg @echo ======================== APPLE 1 ================================================= call kickc -t asm6502 -D=APPLE1 test.c -o test_apple1.prg -e copy test.prg test_apple1.prg +del test_apple1.klog +del test_apple1.vs call node hexdump > test_apple1.woz + +del *.klog +del *.vs +del *.dbg +del test.prg + diff --git a/demo_amiga_hand.h b/demo_amiga_hand.h new file mode 100644 index 0000000..67c0fdb --- /dev/null +++ b/demo_amiga_hand.h @@ -0,0 +1,170 @@ +byte amiga_data[612] = { + 107,35,130,35, + 130,35,130,57, + 130,57,185,57, + 185,57,185,35, + 185,35,197,35, + 197,35,208,46, + 208,46,208,118, + 208,118,199,118, + 199,118,199,80, + 199,80,136,80, + 136,80,134,78, + 134,78,125,78, + 125,78,124,80, + 124,80,117,80, + 117,80,117,89, + 117,89,107,96, + 107,96,107,35, + 105,96,105,34, + 105,34,198,34, + 198,34,210,46, + 210,46,210,119, + 210,119,158,119, + 158,119,158,124, + 158,124,154,132, + 154,132,150,137, + 150,137,146,140, + 146,140,140,149, + 140,149,132,153, + 132,153,132,164, + 132,164,82,164, + 82,164,82,133, + 82,133,81,133, + 81,133,81,105, + 81,105,85,99, + 85,99,92,93, + 92,93,93,89, + 93,89,97,82, + 97,82,102,79, + 102,79,105,78, + 105,78,105,78, + 105,78,102,80, + 102,80,98,82, + 98,82,94,89, + 94,89,93,93, + 93,93,86,99, + 86,99,82,105, + 82,105,82,132, + 82,132,83,133, + 83,133,83,163, + 83,163,112,163, + 112,163,112,142, + 112,142,120,142, + 120,142,124,138, + 124,138,124,119, + 124,119,122,116, + 122,116,122,106, + 122,106,137,93, + 137,93,137,81, + 137,81,134,79, + 134,79,131,79, + 131,79,134,82, + 134,82,134,89, + 134,89,133,90, + 133,90,126,90, + 126,90,123,87, + 123,87,114,94, + 114,94,102,100, + 102,100,90,109, + 90,109,90,108, + 90,108,102,99, + 102,99,98,98, + 98,98,95,95, + 95,95,98,97, + 98,97,102,98, + 102,98,105,96, + 122,85,124,78, + 124,78,132,79, + 132,79,133,82, + 133,82,133,89, + 133,89,126,89, + 126,89,122,85, + 123,116,123,106, + 123,106,138,93, + 138,93,138,81, + 138,81,197,81, + 197,81,197,118, + 197,118,124,118, + 124,118,123,114, + 132,35,183,35, + 183,35,183,56, + 183,56,132,56, + 132,56,132,35, + 168,38,179,38, + 179,38,179,52, + 179,52,168,52, + 168,52,168,38, + 170,39,177,39, + 177,39,177,51, + 177,51,170,51, + 170,51,170,39, + 119,81,122,81, + 122,81,122,83, + 122,83,119,87, + 119,87,119,81, + 113,163,113,143, + 113,143,120,143, + 120,143,125,138, + 125,138,125,129, + 125,129,129,131, + 129,131,137,131, + 137,131,137,133, + 137,133,141,138, + 141,138,145,138, + 145,138,145,140, + 145,140,139,149, + 139,149,131,152, + 131,152,131,163, + 131,163,113,163, + 125,119,125,124, + 125,124,130,121, + 130,121,125,119, + 129,122,125,124, + 125,124,125,119, + 125,119,146,119, + 146,119,136,129, + 136,129,135,129, + 135,129,137,127, + 137,127,132,122, + 132,122,129,122, + 150,119,157,119, + 157,119,157,124, + 157,124,153,132, + 153,132,150,136, + 150,136,145,137, + 145,137,142,137, + 142,137,139,133, + 139,133,139,130, + 139,130,150,119, + 140,130,140,133, + 140,133,142,136, + 142,136,144,136, + 144,136,148,135, + 148,135,149,132, + 149,132,145,129, + 145,129,140,130, + 126,128,134,130, + 134,130,136,127, + 136,127,132,123, + 132,123,129,123, + 129,123,126,125, + 126,125,126,128, + 198,35,209,46, + 209,46,209,118 +}; + +void demo_amiga_hand() { + TMS_INIT(SCREEN2_TABLE); + SCREEN2_FILL(); + screen2_square_sprites(); + + SCREEN2_PUTS(0, 0, COLOR_BYTE(COLOR_BLACK,COLOR_WHITE), "*** P-LAB VIDEO CARD SYSTEM ***"); + SCREEN2_PUTS(0, 2, COLOR_BYTE(COLOR_BLACK,COLOR_WHITE), "16K VRAM BYTES FREE"); + SCREEN2_PUTS(0, 4, COLOR_BYTE(COLOR_BLACK,COLOR_WHITE), "READY."); + + for(word p=0;p<612;p+=4) { + vti_line(amiga_data[p],amiga_data[p+1],amiga_data[p+2],amiga_data[p+3]); + } + SCREEN2_PUTS(18, 12, COLOR_BYTE(COLOR_DARK_BLUE, COLOR_WHITE), "APPLE1"); +} diff --git a/demo_blank.h b/demo_blank.h new file mode 100644 index 0000000..7ab2f54 --- /dev/null +++ b/demo_blank.h @@ -0,0 +1,12 @@ +// TODO make it static once KickC bug is fixed +byte blank = 0; + +void flip_blank() { + blank ^= 1; + + if(blank) woz_puts("NORMAL\r"); + else woz_puts("BLANK\r"); + + // write "blank" bit + tms_blank(blank); +} diff --git a/demo_extvid.h b/demo_extvid.h new file mode 100644 index 0000000..196294c --- /dev/null +++ b/demo_extvid.h @@ -0,0 +1,19 @@ +// TODO make it static once KickC bug is fixed +byte external_input = 0; + +void flip_external_input() { + external_input ^= 1; + + if(external_input) woz_puts("EXT INPUT ON\r"); + else woz_puts("EXT INPUT OFF\r"); + + // fill color table with transparent color so that external input can be seen + set_vram_write_addr(SCREEN1_COLOR_TABLE); + for(byte i=32;i!=0;i--) { + TMS_WRITE_DATA_PORT(COLOR_BYTE(COLOR_DARK_YELLOW, COLOR_TRANSPARENT)); + } + + tms_external_video(external_input); // write "external video input" bit + set_color(0); // transparent color +} + diff --git a/demo_interrupt.h b/demo_interrupt.h new file mode 100644 index 0000000..76edbcc --- /dev/null +++ b/demo_interrupt.h @@ -0,0 +1,44 @@ +// TODO make it static once KickC bug is fixed +byte last_seconds = 0; + +void demo_interrupt() { + + // resets the watch to 00:00:00.0 + _ticks = 0; + _seconds = 0; + _minutes = 0; + _hours = 0; + + // installs the interrupt handler routine + install_interrupt(); + + // enables interrupts on the TMS9918 + tms_interrupt(1); + + woz_puts("INTERRUPT INSTALLED\r"); + woz_puts("0 TURNS OFF\r"); + woz_puts("1 TURNS ON\r"); + woz_puts("E EXIT TO MAIN MENU\r"); + + for(;;) { + if(keypressed()) { + byte k = woz_getkey(); + if(k=='1') tms_interrupt(1); + else if(k=='0') tms_interrupt(0); + else if(k=='E') break; + } + + if(last_seconds != _seconds) { + woz_put_hex(_hours); woz_putc(':'); + woz_put_hex(_minutes); woz_putc(':'); + woz_put_hex(_seconds); woz_putc('.'); + woz_put_hex(_ticks); woz_putc('\r'); + last_seconds = _seconds; + } + } + + // disables interrupts on the TMS9918 + tms_interrupt(0); + + woz_puts("INTERRUPT STOPPED\r"); +} diff --git a/laser500_font.ascii.c b/font8x8.h similarity index 98% rename from laser500_font.ascii.c rename to font8x8.h index 9621711..3697235 100644 --- a/laser500_font.ascii.c +++ b/font8x8.h @@ -1,3 +1,5 @@ +// font from LASER-500 (ASCII characters only) + byte FONT[768] = { 0 , 0 diff --git a/interrupt.h b/interrupt.h index 21b2841..2b5750e 100644 --- a/interrupt.h +++ b/interrupt.h @@ -1,32 +1,42 @@ -// reserve locations where the 6502 jumps on IRQ +// reserve locations 0,1,2 used by the APPLE1 IRQ jump vector +// these will contain a three byte instruction "JUMP " +// in order to make the interrupt routine not reside in zero page #pragma zp_reserve(0,1,2) -// reads the status register on the TMS9918 thus acknowledging the interrupt +// acknowledge the interrupt by reading the status register on the TMS9918 (see manual section 2.3.1) inline void acknowledge_interrupt() { asm { lda VDP_REG }; } -export __address(0) byte IRQ_JUMP_OPCODE; -export __address(1) word IRQ_JUMP_ADDRESS; +export __address(0) byte IRQ_JUMP_OPCODE; // location 0 contains the opcode for "JMP" +export __address(1) word IRQ_JUMP_ADDRESS; // location 1 and 2 contain the jump address -export volatile word tick_counts; +// storage for a simple watch timer +export volatile byte _ticks; +export volatile byte _seconds; +export volatile byte _minutes; +export volatile byte _hours; +// interrupt routine called every 1/60th by the CPU after TMS9918 sets the /INT pin export __interrupt(hardware_all) void interrupt_handler() { - tick_counts++; - - if(tick_counts == 60) { - tick_counts = 0; - woz_putc('T'); // print a T every second + // update the watch + if(++_ticks == 60) { + _ticks = 0; + if(++_seconds == 60) { + _seconds = 0; + if(++_minutes == 60) { + _minutes = 0; + _hours++; + } + } } - - // acknowledge interrupt by reading the status register - acknowledge_interrupt(); + acknowledge_interrupt(); // acknowledge interrupt by reading the status register } +// safely installs the interrupt routine void install_interrupt() { asm { sei }; // disable 6502 interrupts IRQ_JUMP_OPCODE = 0x4C; // $4C = JUMP opcode IRQ_JUMP_ADDRESS = (word) &interrupt_handler; // JUMP interrupt_handler asm { cli }; // re-enable 6502 interrupts - write_reg(1, 0xc0 | 32); // turn on IE bit (interrupt enable) on the TMS9918 } diff --git a/test.c b/test.c index 6078ce0..85a2619 100644 --- a/test.c +++ b/test.c @@ -1,685 +1,50 @@ -#pragma encoding(ascii) +// TODO make screen tables parametric (fixed values calculated by macros) -#ifdef APPLE1 - // APPLE1 - #pragma start_address(0x4000) - const word WOZMON = 0xFF1F; // enters monitor - const word ECHO = 0xFFEF; // output ascii character in A (A not destroyed) - const word PRBYTE = 0xFFDC; // print hex byte in A (A destroyed) - const word KEY_DATA = 0xd010; // read key - const word KEY_CTRL = 0xd011; // control port - const word TERM_DATA = 0xd012; // write ascii - const word TERM_CTRL = 0xd013; // control port - - const byte *VDP_DATA = 0xCC00; // TMS9918 data port (VRAM) - const byte *VDP_REG = 0xCC01; // TMS9918 register port (write) or status (read) -#else - // VIC20 - const word ECHO = 0xFFD2; // chrout routine in kernal rom - const word GETIN = 0xFFE4; // GETIN keyboard read routine - const byte *VDP_DATA = 0xA000; // TMS9918 data port (VRAM) - const byte *VDP_REG = 0xA001; // TMS9918 register port (write) or status (read) -#endif - -// typedef unsigned char byte; -// typedef unsigned int word; - -// TMS9918 interface flags -const byte WRITE_TO_REG = 0b10000000; -const byte WRITE_TO_VRAM = 0b01000000; -const byte READ_FROM_VRAM = 0b00000000; - -#define POKE(a,b) (*((byte *)(a))=(byte)(b)) -#define PEEK(a) (*((byte *)(a))) - -#define NOP asm { nop } - -#define TMS_WRITE_CTRL_PORT(a) (*VDP_REG=(byte)(a)) -#define TMS_WRITE_DATA_PORT(a) (*VDP_DATA=(byte)(a)) -#define TMS_READ_CTRL_PORT (*VDP_REG); -#define TMS_READ_DATA_PORT (*VDP_DATA); - -// status register -#define FRAME_BIT(a) ((a) & 0b10000000) -#define FIVESPR_BIT(a) ((a) & 0b01000000) -#define COLLISION_BIT(a) ((a) & 0b00100000) -#define SPRITE_NUM(a) ((a) & 0b00011111) - -// sets the VRAM write address on the TMS9918 -void set_vram_write_addr(word addr) { - TMS_WRITE_CTRL_PORT(addr & 0b00111111)|WRITE_TO_VRAM); -} - -// sets the VRAM read address on the TMS9918 -void set_vram_read_addr(word addr) { - TMS_WRITE_CTRL_PORT(addr & 0b00111111)|READ_FROM_VRAM); -} - -// writes a value to a TMS9918 register (0-7) -void write_reg(byte regnum, byte val) { - TMS_WRITE_CTRL_PORT(val); - TMS_WRITE_CTRL_PORT((regnum & 0b00001111)|WRITE_TO_REG); -} - -inline void set_color(byte col) { - write_reg(7, col); -} - -byte SCREEN1_TABLE[8] = { - 0x00, 0xc0, 0x05, 0x80, 0x01, 0x20, 0x00, 0x25 -}; - -byte SCREEN2_TABLE[8] = { - 0x02, 0xc0, 0x0e, 0xff, 0x03, 0x76, 0x03, 0x25 -}; - -/* -IRQ on the apple1 goes to 0 -- #pragma zp_reserve(0,1,2) -- POKE 0,$4C (JUMP) -- DOKE 1,address routine -*/ - -word screen1_cursor; - -#include "laser500_font.ascii.c" - -// 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 - -const word SCREEN1_PATTERN_TABLE = 0x0800; -const word SCREEN1_NAME_TABLE = 0x1400; -const word SCREEN1_COLOR_TABLE = 0x2000; -const word SCREEN1_SPRITE_PATTERNS = 0x0000; -const word SCREEN1_SPRITE_ATTRS = 0x1000; -const word SCREEN1_SIZE = (32*24); - -// loads the Laser 500 font on the pattern table -void SCREEN1_LOAD_FONT() { - - static byte *source = FONT; - static word i; - - // start writing into VRAM from space character (32..127) - set_vram_write_addr(SCREEN1_PATTERN_TABLE+(32*8)); - for(i=768;i!=0;i--) { - TMS_WRITE_DATA_PORT(*source++); - } - - // reverse font (32..127) - source = FONT; - set_vram_write_addr(SCREEN1_PATTERN_TABLE+((128+32)*8)); - for(i=768;i!=0;i--) { - TMS_WRITE_DATA_PORT(~(*source++)); - } -} - -// prints character to TMS (SCREEN 1 MODE) -void SCREEN1_PUTCHAR(byte c) { - set_vram_write_addr(screen1_cursor++); - TMS_WRITE_DATA_PORT(c); -} - -// prints 0 terminated string pointed by YA -void SCREEN1_PUTS(byte *s) { - byte c; - while(c=*s++) { - SCREEN1_PUTCHAR(c); - } -} - -void SCREEN1_HOME() { - screen1_cursor = SCREEN1_NAME_TABLE; -} - -void SCREEN1_LOCATEXY(byte x, byte y) { - screen1_cursor = SCREEN1_NAME_TABLE + ((word)y)*32 + x; -} - -void SCREEN1_FILL() { - // fills name table with spaces (32) - set_vram_write_addr(SCREEN1_NAME_TABLE); - for(word i=SCREEN1_SIZE;i!=0;i--) { - TMS_WRITE_DATA_PORT(32); - } - - // fill pattern table with 0 - set_vram_write_addr(SCREEN1_PATTERN_TABLE); - for(word i=256*8;i!=0;i--) { - TMS_WRITE_DATA_PORT(0); - } - - // fill color table with $1F - set_vram_write_addr(SCREEN1_COLOR_TABLE); - for(byte i=32;i!=0;i--) { - TMS_WRITE_DATA_PORT(0x1f); - } -} - -// SCREEN 2 VALUES - -// pattern table: $0000-$17FF (256*8*3) -// sprite patterns: $1800-$19FF -// color table: $2000-$27FF (256*8*3) -// name table: $3800-$3AFF (32*24 = 256*3 = 768) -// sprite attributes: $3B00-$3BFF -// unused $3C00-$3FFF -// - -const word SCREEN2_PATTERN_TABLE = 0x0000; -const word SCREEN2_NAME_TABLE = 0x3800; -const word SCREEN2_COLOR_TABLE = 0x2000; -const word SCREEN2_SPRITE_PATTERNS = 0x1800; -const word SCREEN2_SPRITE_ATTRS = 0x3b00; -const word SCREEN2_SIZE = (32*24); - - -void SCREEN_INIT(byte *table) { - for(byte i=0;i<8;i++) { - write_reg(i, table[i]); - } -} - -void SCREEN2_FILL() { - // fills name table x3 with increasing numbers - set_vram_write_addr(SCREEN2_NAME_TABLE); - for(word i=0;i= dy) { err += dy; if(ix) ++x0; else --x0; } /* e_xy+e_x > 0 */ - if (e2 <= dx) { err += dx; if(iy) ++y0; else --y0; } /* e_xy+e_y < 0 */ - } -} - -/* -// http://members.chello.at/~easyfilter/bresenham.html -void vti_ellipse_rect(byte _x0, byte _y0, byte _x1, byte _y1) -{ - //unsigned int x0,y0,x1,y1; - signed int x0 = (signed int) _x0; - signed int y0 = (signed int) _y0; - signed int x1 = (signed int) _x1; - signed int y1 = (signed int) _y1; - - signed int a = vti_abs(x1-x0), b = vti_abs(y1-y0); - signed int b1 = b&1; // values of diameter - signed int dx = 4*(1-a)*b*b, dy = 4*(b1+1)*a*a; // error increment - signed int err = dx+dy+b1*a*a, e2; // error of 1.step - - if (x0 > x1) { x0 = x1; x1 += a; } // if called with swapped points - if (y0 > y1) y0 = y1; // .. exchange them - y0 += (b+1)/2; y1 = y0-b1; // starting pixel - a *= 8*a; b1 = 8*b*b; - - do { - SCREEN2_PLOT((byte) x1, (byte) y0); // I. Quadrant - SCREEN2_PLOT((byte) x0, (byte) y0); // II. Quadrant - SCREEN2_PLOT((byte) x0, (byte) y1); // III. Quadrant - SCREEN2_PLOT((byte) x1, (byte) y1); // IV. Quadrant - e2 = 2*err; - if (e2 <= dy) { y0++; y1--; err += dy += a; } // y step - if (e2 >= dx || 2*err > dy) { x0++; x1--; err += dx += b1; } // x step - } while (x0 <= x1); - - while (y0-y1 < b) { // too early stop of flat ellipses a=1 - SCREEN2_PLOT((byte) x0-1, (byte) (y0)); // -> finish tip of ellipse - SCREEN2_PLOT((byte) x1+1, (byte) (y0++)); - SCREEN2_PLOT((byte) x0-1, (byte) (y1)); - SCREEN2_PLOT((byte) x1+1, (byte) (y1--)); - } -} -*/ +#pragma encoding(ascii) // encode strings in plain ascii +#include "utils.h" +#include "apple1.h" +#include "tms9918.h" +#include "font8x8.h" +#include "tms_screen1.h" +#include "tms_screen2.h" #include "interrupt.h" -void prova_interrupt() { - // la seguente linea รจ un workaround temporaneo a causa di un bug di KickC - // fa uso della variabile tick_counts in modo che non venga ottimizzata - //*((word *)0xFFFE) = tick_counts; +#include "demo_amiga_hand.h" +#include "demo_interrupt.h" +#include "demo_extvid.h" +#include "demo_blank.h" - install_interrupt(); -} - -byte external_input = 0; -void prova_external_input() { - external_input ^= 1; - - if(external_input == 1) { - woz_puts("EXT INPUT ON\r"); - } - else { - woz_puts("EXT INPUT OFF\r"); - } - - // fill color table with $F0 - set_vram_write_addr(SCREEN1_COLOR_TABLE); - for(byte i=32;i!=0;i--) { - TMS_WRITE_DATA_PORT(0xA0); // yellow on transparent - } - - write_reg(0, 0x00 | external_input); // write "external video input" bit - set_color(0); // transparent color -} - -byte blank = 0; - -void prova_blank() { - blank ^= 64; - - if(blank == 64) { - woz_puts("NORMAL\r"); - } - else { - woz_puts("BLANK\r"); - } - - write_reg(1, 0x80 | blank); // write "blank" bit +void help() { + woz_puts( + "\rTMS9918 DEMOS\r" + "=============\r" + "1 SCREEN1\r" + "2 SCREEN2\r" + "A AMIGA HAND\r" + "I INTERRUPT\r" + "E FLIP EXT VIDEO\r" + "B BLANK ON/OFF\r" + "H HELP\r" + "0 EXITS\r\r" + ); } void main() { - - //word pippo = mul8(3,2); - - byte key = '1'; + byte key = 'H'; for(;;) { if(key == '1') prova_screen1(); else if(key == '2') prova_screen2(); - else if(key == '3') prova_screen3(); - else if(key == '4') prova_interrupt(); - else if(key == 'E') prova_external_input(); - else if(key == 'B') prova_blank(); + else if(key == 'A') demo_amiga_hand(); + else if(key == 'I') demo_interrupt(); + else if(key == 'E') flip_external_input(); + else if(key == 'B') flip_blank(); + else if(key == 'H') help(); else if(key == '0') break; else woz_putc(key); key = woz_getkey(); } - woz_putc(42); + woz_puts("BYE\r"); woz_mon(); } - diff --git a/tms9918.h b/tms9918.h new file mode 100644 index 0000000..c41c020 --- /dev/null +++ b/tms9918.h @@ -0,0 +1,115 @@ +#ifdef APPLE1 + const byte *VDP_DATA = 0xCC00; // TMS9918 data port (VRAM) + const byte *VDP_REG = 0xCC01; // TMS9918 register port (write) or status (read) +#else + const byte *VDP_DATA = 0xA000; // TMS9918 data port (VRAM) + const byte *VDP_REG = 0xA001; // TMS9918 register port (write) or status (read) +#endif + +// control port bits +const byte WRITE_TO_VRAM = 0b01000000; // write to VRAM command +const byte READ_FROM_VRAM = 0b00000000; // read from VRAM command +const byte HIADDRESS_MASK = 0b00111111; // bit mask for the high byte of the address + +const byte WRITE_TO_REG = 0b10000000; // write to register command +const byte REGNUM_MASK = 0b00000111; // bit mask for register number (0-7) + +// register 1 masks +const byte REG0_M3_MASK = 0b00000010; +const byte REG0_EXTVID_MASK = 0b00000001; + +// register 1 masks +const byte REG1_16K_MASK = 0b10000000; +const byte REG1_BLANK_MASK = 0b01000000; +const byte REG1_IE_MASK = 0b00100000; +const byte REG1_M1M2_MASK = 0b00011000; +const byte REG1_UNUSED_MASK = 0b00000100; +const byte REG1_SIZE_MASK = 0b00000010; +const byte REG1_MAG_MASK = 0b00000001; + +// TMS9918 color palette +const byte COLOR_TRANSPARENT = 0x0; +const byte COLOR_BLACK = 0x1; +const byte COLOR_MEDIUM_GREEN = 0x2; +const byte COLOR_LIGHT_GREEN = 0x3; +const byte COLOR_DARK_BLUE = 0x4; +const byte COLOR_LIGHT_BLUE = 0x5; +const byte COLOR_DARK_RED = 0x6; +const byte COLOR_CYAN = 0x7; +const byte COLOR_MEDIUM_RED = 0x8; +const byte COLOR_LIGHT_RED = 0x9; +const byte COLOR_DARK_YELLOW = 0xA; +const byte COLOR_LIGHT_YELLOW = 0xB; +const byte COLOR_DARK_GREEN = 0xC; +const byte COLOR_MAGENTA = 0xD; +const byte COLOR_GRAY = 0xE; +const byte COLOR_WHITE = 0xF; + +#define COLOR_BYTE(f,b) (((f)<<4)|(b)) + +// status register bits (read only) +#define FRAME_BIT(a) ((a) & 0b10000000) +#define FIVESPR_BIT(a) ((a) & 0b01000000) +#define COLLISION_BIT(a) ((a) & 0b00100000) +#define SPRITE_NUM(a) ((a) & 0b00011111) + +// read/write to TMS9918 macros +#define TMS_WRITE_CTRL_PORT(a) (*VDP_REG=(byte)(a)) +#define TMS_WRITE_DATA_PORT(a) (*VDP_DATA=(byte)(a)) +#define TMS_READ_CTRL_PORT (*VDP_REG); +#define TMS_READ_DATA_PORT (*VDP_DATA); + +// sets the VRAM address on the TMS9918 for a write operation +void set_vram_write_addr(word addr) { + TMS_WRITE_CTRL_PORT(LOBYTE(addr)); + TMS_WRITE_CTRL_PORT((HIBYTE(addr) & HIADDRESS_MASK)|WRITE_TO_VRAM); +} + +// sets the VRAM address on the TMS9918 for a read operation +void set_vram_read_addr(word addr) { + TMS_WRITE_CTRL_PORT(LOBYTE(addr)); + TMS_WRITE_CTRL_PORT((HIBYTE(addr) & HIADDRESS_MASK)|READ_FROM_VRAM); +} + +// buffer containing the last register values, because TMS registers are write only +byte TMS_REGS_LATCH[8]; + +// writes a value to a TMS9918 register (0-7) +void TMS_WRITE_REG(byte regnum, byte val) { + TMS_WRITE_CTRL_PORT(val); + TMS_WRITE_CTRL_PORT((regnum & REGNUM_MASK)|WRITE_TO_REG); + TMS_REGS_LATCH[regnum] = val; // save value to buffer +} + +// sets border color and background for mode 0 +inline void set_color(byte col) { + TMS_WRITE_REG(7, col); +} + +// initialize all registers from a table +void TMS_INIT(byte *table) { + for(byte i=0;i<8;i++) { + TMS_WRITE_REG(i, table[i]); + } +} + +// turn on off interrupt enable bit on register 1 +void tms_interrupt(byte val) { + byte regvalue = TMS_REGS_LATCH[1] & (~REG1_IE_MASK); + if(val) regvalue |= REG1_IE_MASK; + TMS_WRITE_REG(1, regvalue); +} + +// turn on off blank bit on register 1 +void tms_blank(byte val) { + byte regvalue = TMS_REGS_LATCH[1] & (~REG1_BLANK_MASK); + if(val) regvalue |= REG1_BLANK_MASK; + TMS_WRITE_REG(1, regvalue); +} + +// turn on off external video bit on register 0 +void tms_external_video(byte val) { + byte regvalue = TMS_REGS_LATCH[0] & (~REG0_EXTVID_MASK); + if(val) regvalue |= REG0_EXTVID_MASK; + TMS_WRITE_REG(0, regvalue); +} diff --git a/tms_screen1.h b/tms_screen1.h new file mode 100644 index 0000000..a3061e2 --- /dev/null +++ b/tms_screen1.h @@ -0,0 +1,117 @@ +byte SCREEN1_TABLE[8] = { + 0x00, 0xc0, 0x05, 0x80, 0x01, 0x20, 0x00, 0x25 +}; + +// 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 + +const word SCREEN1_PATTERN_TABLE = 0x0800; +const word SCREEN1_NAME_TABLE = 0x1400; +const word SCREEN1_COLOR_TABLE = 0x2000; +const word SCREEN1_SPRITE_PATTERNS = 0x0000; +const word SCREEN1_SPRITE_ATTRS = 0x1000; +const word SCREEN1_SIZE = (32*24); + +// loads the a font on the pattern table +void SCREEN1_LOAD_FONT() { + + byte *source = FONT; + + // start writing into VRAM from space character (32..127) + set_vram_write_addr(SCREEN1_PATTERN_TABLE+(32*8)); + for(word i=768;i!=0;i--) { + TMS_WRITE_DATA_PORT(*source++); + } + + // reverse font (32..127) + source = FONT; + set_vram_write_addr(SCREEN1_PATTERN_TABLE+((128+32)*8)); + for(word i=768;i!=0;i--) { + TMS_WRITE_DATA_PORT(~(*source++)); + } +} + +// hold the cursor location for the putchar routine +word screen1_cursor; + +// prints character to TMS (SCREEN 1 MODE) +void SCREEN1_PUTCHAR(byte c) { + set_vram_write_addr(screen1_cursor++); + TMS_WRITE_DATA_PORT(c); +} + +// prints 0 terminated string pointed by YA +void SCREEN1_PUTS(byte *s) { + byte c; + while(c=*s++) { + SCREEN1_PUTCHAR(c); + } +} + +void SCREEN1_HOME() { + screen1_cursor = SCREEN1_NAME_TABLE; +} + +void SCREEN1_LOCATEXY(byte x, byte y) { + screen1_cursor = SCREEN1_NAME_TABLE + ((word)y)*32 + x; +} + +void SCREEN1_FILL() { + // fills name table with spaces (32) + set_vram_write_addr(SCREEN1_NAME_TABLE); + for(word i=SCREEN1_SIZE;i!=0;i--) { + TMS_WRITE_DATA_PORT(32); + } + + // fill pattern table with 0 + set_vram_write_addr(SCREEN1_PATTERN_TABLE); + for(word i=256*8;i!=0;i--) { + TMS_WRITE_DATA_PORT(0); + } + + // fill color table with black on white + set_vram_write_addr(SCREEN1_COLOR_TABLE); + for(byte i=32;i!=0;i--) { + TMS_WRITE_DATA_PORT(COLOR_BYTE(COLOR_BLACK,COLOR_WHITE)); + } +} + +void screen1_square_sprites() { + // fills first sprite pattern with 255 + set_vram_write_addr(SCREEN1_SPRITE_PATTERNS); // start writing in the sprite patterns + for(byte i=0;i<8;i++) { + TMS_WRITE_DATA_PORT(255); + } + + // set sprite coordinates + set_vram_write_addr(SCREEN1_SPRITE_ATTRS); // start writing in the sprite attribute + for(byte i=0;i<32;i++) { + TMS_WRITE_DATA_PORT((6+i)*8); NOP; NOP; NOP; NOP; // y coordinate + TMS_WRITE_DATA_PORT((6+i)*8); NOP; NOP; NOP; NOP; // x coordinate + TMS_WRITE_DATA_PORT(0); NOP; NOP; NOP; NOP; // name + TMS_WRITE_DATA_PORT(i); NOP; NOP; NOP; NOP; // color + } +} + +void prova_screen1() { + TMS_INIT(SCREEN1_TABLE); + 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(word i=0;i<256;i++) SCREEN1_PUTCHAR((byte)i); + + screen1_square_sprites(); +} diff --git a/tms_screen2.h b/tms_screen2.h new file mode 100644 index 0000000..eb9feaf --- /dev/null +++ b/tms_screen2.h @@ -0,0 +1,198 @@ +byte SCREEN2_TABLE[8] = { + 0x02, 0xc0, 0x0e, 0xff, 0x03, 0x76, 0x03, 0x25 +}; + +// SCREEN 2 VALUES + +// pattern table: $0000-$17FF (256*8*3) +// sprite patterns: $1800-$19FF +// color table: $2000-$27FF (256*8*3) +// name table: $3800-$3AFF (32*24 = 256*3 = 768) +// sprite attributes: $3B00-$3BFF +// unused $3C00-$3FFF +// + +const word SCREEN2_PATTERN_TABLE = 0x0000; +const word SCREEN2_NAME_TABLE = 0x3800; +const word SCREEN2_COLOR_TABLE = 0x2000; +const word SCREEN2_SPRITE_PATTERNS = 0x1800; +const word SCREEN2_SPRITE_ATTRS = 0x3b00; +const word SCREEN2_SIZE = (32*24); + +void SCREEN2_FILL() { + // fills name table x3 with increasing numbers + set_vram_write_addr(SCREEN2_NAME_TABLE); + for(word i=0;i= dy) { err += dy; if(ix) ++x0; else --x0; } /* e_xy+e_x > 0 */ + if (e2 <= dx) { err += dx; if(iy) ++y0; else --y0; } /* e_xy+e_y < 0 */ + } +} + +/* +// http://members.chello.at/~easyfilter/bresenham.html +void vti_ellipse_rect(byte _x0, byte _y0, byte _x1, byte _y1) +{ + //unsigned int x0,y0,x1,y1; + signed int x0 = (signed int) _x0; + signed int y0 = (signed int) _y0; + signed int x1 = (signed int) _x1; + signed int y1 = (signed int) _y1; + + signed int a = vti_abs(x1-x0), b = vti_abs(y1-y0); + signed int b1 = b&1; // values of diameter + signed int dx = 4*(1-a)*b*b, dy = 4*(b1+1)*a*a; // error increment + signed int err = dx+dy+b1*a*a, e2; // error of 1.step + + if (x0 > x1) { x0 = x1; x1 += a; } // if called with swapped points + if (y0 > y1) y0 = y1; // .. exchange them + y0 += (b+1)/2; y1 = y0-b1; // starting pixel + a *= 8*a; b1 = 8*b*b; + + do { + SCREEN2_PLOT((byte) x1, (byte) y0); // I. Quadrant + SCREEN2_PLOT((byte) x0, (byte) y0); // II. Quadrant + SCREEN2_PLOT((byte) x0, (byte) y1); // III. Quadrant + SCREEN2_PLOT((byte) x1, (byte) y1); // IV. Quadrant + e2 = 2*err; + if (e2 <= dy) { y0++; y1--; err += dy += a; } // y step + if (e2 >= dx || 2*err > dy) { x0++; x1--; err += dx += b1; } // x step + } while (x0 <= x1); + + while (y0-y1 < b) { // too early stop of flat ellipses a=1 + SCREEN2_PLOT((byte) x0-1, (byte) (y0)); // -> finish tip of ellipse + SCREEN2_PLOT((byte) x1+1, (byte) (y0++)); + SCREEN2_PLOT((byte) x0-1, (byte) (y1)); + SCREEN2_PLOT((byte) x1+1, (byte) (y1--)); + } +} +*/ + diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..7fc7ff0 --- /dev/null +++ b/utils.h @@ -0,0 +1,7 @@ +#define POKE(a,b) (*((byte *)(a))=(byte)(b)) +#define PEEK(a) (*((byte *)(a))) + +#define HIBYTE(c) (>(c)) +#define LOBYTE(c) (<(c)) + +#define NOP asm { nop }