#ifndef TMS9918_H #define TMS9918_H #define TMS9918 1 // TODO a bitmapped text writing routine (double size ecc) // TODO console like text output in screen 2 // TODO more fonts (C64 and PET/VIC20) // TODO how to switch fonts? // TODO do fonts need to start from 32 or 0? // TODO sprite routines // TODO test the interrupt routines // TODO finalize hexdump.js and update README.md // TODO allow redefinition of I/O ports // TODO wait_end_of_frame() // TODO screen1() and screen2() helpers #include "utils.h" #ifdef APPLE1 byte *const VDP_DATA = (byte *) 0xCC00; // TMS9918 data port (VRAM) byte *const VDP_REG = (byte *) 0xCC01; // TMS9918 register port (write) or status (read) #else byte *const VDP_DATA = (byte *) 0xA000; // TMS9918 data port (VRAM) byte *const VDP_REG = (byte *) 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 0 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_GREY = 0xE; const byte COLOR_WHITE = 0xF; // The library has a fixed VRAM memory layout valid for SCREEN 1 and 2 // // 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 TMS_NAME_TABLE = 0x3800; const word TMS_COLOR_TABLE = 0x2000; const word TMS_PATTERN_TABLE = 0x0000; const word TMS_SPRITE_ATTRS = 0x3b00; const word TMS_SPRITE_PATTERNS = 0x1800; // macro for combining foreground and background into a single byte value #define FG_BG(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) // buffer containing the last register values, because TMS registers are write only byte tms_regs_latch[8]; // cursor coordinates for the console functions byte tms_cursor_x; byte tms_cursor_y; byte tms_reverse; // sets the VRAM address on the TMS9918 for a write operation void tms_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 tms_set_vram_read_addr(word addr) { TMS_WRITE_CTRL_PORT(LOBYTE(addr)); TMS_WRITE_CTRL_PORT((HIBYTE(addr) & HIADDRESS_MASK)|READ_FROM_VRAM); } // 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 tms_set_color(byte col) { tms_write_reg(7, col); } // initialize all registers from a table void tms_init_regs(byte *table) { for(byte i=0;i<8;i++) { tms_write_reg(i, table[i]); } } const byte INTERRUPT_ENABLED = 1; const byte INTERRUPT_DISABLED = 0; // sets the interrupt enable bit on register 1 void tms_set_interrupt_bit(byte val) { byte regvalue = tms_regs_latch[1] & (~REG1_IE_MASK); if(val) regvalue |= REG1_IE_MASK; tms_write_reg(1, regvalue); } const byte BLANK_ON = 0; const byte BLANK_OFF = 1; // sets the blank bit on register 1 void tms_set_blank(byte val) { byte regvalue = tms_regs_latch[1] & (~REG1_BLANK_MASK); if(val) regvalue |= REG1_BLANK_MASK; tms_write_reg(1, regvalue); } // sets the external video input bit on register 0 void tms_set_external_video(byte val) { byte regvalue = tms_regs_latch[0] & (~REG0_EXTVID_MASK); if(val) regvalue |= REG0_EXTVID_MASK; tms_write_reg(0, regvalue); } inline void tms_wait_end_of_frame() { while(!FRAME_BIT(TMS_READ_CTRL_PORT)); } // loads the font on the pattern table void tms_copy_to_vram(byte *source, word size, word dest) { // start writing into VRAM from space character (32..127) tms_set_vram_write_addr(dest); for(word i=size;i!=0;i--) { TMS_WRITE_DATA_PORT(*source++); } } #include "apple1.h" //#include "font8x8.h" #include "c64font.h" #include "sprites.h" #include "screen1.h" #include "screen2.h" #include "interrupt.h" #endif