Close

Apple][+ Videx Videoterm Card (80 Columns) Emulation

augusto-baffaAugusto Baffa wrote 02/08/2024 at 22:01 • 18 min read • Like
// Videx Videoterm Card Emulator
// by Augusto Baffa feb 2024
// 
// Based on Apple II Simulator
// by Peter Koch, May 6, 1993 

int videx_slot = 3;
bool card_videx = true;
bool card_videx_mem_on = false;
bool card_videx_80col = false;

class VidexCard {


    /*
    //notes:

    // To turn on/off 80 columns mode:
    // Write at 0xC058 or 0xC059 
    //        card_videx_80col = (addr == 0xc059);
    //
    // To Control the card:
    //
    // IOSEL (0xC300-C3FFF - slot 3)
    // Read 
    //        r = videxcard.getRomIoSel(addr & 0x1FF);
    //        card_videx_mem_on = true;
    // Write
    //        card_videx_mem_on = true;
    //
    // DEVSEL (0xC0B0-C0BF - slot 3)
    // Read
    //        r = videxcard.getC0SLOTX(addr);
    //        
    // Write
    //        videxcard.putC0SLOTX(addr, val);
    //
    // IOSTB  (0xC800-CFFF)
    // Read
    //        if (((addr >> 9) & 0b11) == 0b11) card_videx_mem_on = false;
    //
    //        if (card_videx_mem_on && addr >= 0xC800 && addr <= 0xCDFF)
    //            r = videxcard.getSLOTC8XX(addr & 0x7ff);
    //
    // Write
    //        if (((addr >> 9) & 0b11) == 0b11) card_videx_mem_on = false;
    //
    //        if (card_videx_mem_on && addr >= 0xC800 && addr <= 0xCDFF) 
    //            videxcard.putSLOTC8XX(addr - 0xC800, val);   
    //
    // To Render:
    //
    //
    //for (int y = 0; y < 25; y++) {
    //    for (int x = 0; x < 80; x++) {
    //
    //        int vkeycode = videxcard.getCSLOTXX(x, y); // get ascii code from memory
    //        bool vinverse = videxcard.isCursorPosition(x, y); // check cursor position to inverse
    //
    //        for (int i = 0; i < 9; i++) {
    //
    //            int video_bits = videxcard.getVideoBits(vkeycode, i); // get bits for the current char/scanline
    //
    //            if (vinverse)
    //                display_bit_80col(renderer, x, y, ~video_bits, i); // render char/scanline
    //            else
    //                display_bit_80col(renderer, x, y, video_bits, i); // render char/scanline
    //
    //        }
    //    }
    //}
    */

private:
    uint8_t videx_rom[0x400]; // 0x0400 at C800-CBFF 
    uint8_t videx_chars[0xfff]; // 0x0-0x7ff rom 1 | 0x800-0xfff rom2 character rom (12x8) Matrix)  
    uint8_t videx_vram[2048]; // 0x0800 at CC00-CDFF in 4 banks of 0x1FF


    // internal variables 
    int videx_bankSLOT, videx_regvalSLOT;  // active memory bank, selected register 
    unsigned char videx_regSLOT[17];// registers of the CRT-controller 
    //int videx_oldcursorSLOT;      // old cursor position 
    //int videx_upperSLOT, videx_lowerSLOT;  // cursor size 
    //*******************************************************
    // Registers
    //    register r/w     normal value    Name              
    //    00:      w       7B              Horiz. total      
    //    01:      w       50              Horiz. displayed  
    //    02:      w       62              Horiz. sync pos   
    //    03:      w       29              Horiz. sync width 
    //    04:      w       1B              Vert. total       
    //    05:      w       08              Vert. adjust      
    //    06:      w       18              Vert. displayed   
    //    07:      w       19              Vert. sync pos    
    //    08:      w       00              Interlaced        
    //    09:      w       08              Max. scan line    
    //    10:0A    w       C0              Cursor upper      
    //    11:0B    w       08              Cursor lower      
    //    12:0C    w       00              Startpos Hi       
    //    13:0D    w       00              Startpos Lo       
    //    14:0E    r/w     00              Cursor Hi         
    //    15:0F    r/w     00              Cursor Lo         
    //    16:10    r       00              Lightpen Hi       
    //    17:11    r       00              Lightpen Lo       
    //
    // The registers are addressed as follows:
    //    To write                  To read
    //    LDA #$          LDA #$         
    //    STA $C0B0                 STA $C0B0                
    //    LDA #$             LDA $C0B1                
    //    STA $C0B1                                          
    //*******************************************************
    
public:
    int getC0SLOTX(int adr)// 0x0400 at C800-CBFF 
    {
        int value = 0x00;

        //define current memory bank
        videx_bankSLOT = (adr & 0x000c) >> 2;

        if (adr & 0x0001)
            // get current register value
            value = videx_regSLOT[videx_regvalSLOT];
        else 
            // define current register
            videx_regvalSLOT = value;

        return value;
    }


    void putC0SLOTX(int adr, int value)// 0x0400 at C800-CBFF 
    {

        //define current memory bank
        videx_bankSLOT = (adr & 0x000c) >> 2;

        if (adr & 0x0001)
        {
            // set current register value
            videx_regSLOT[videx_regvalSLOT] = value;

            /*
            //10:0A Cursor upper //11:0B Cursor lower
            if ((videx_regvalSLOT == 10) || (videx_regvalSLOT == 11)) 
                modifySLOT();

            //14:0E Cursor Hi //15:0F Cursor Lo
            if ((videx_regvalSLOT == 14) || (videx_regvalSLOT == 15)) 
                cursorSLOT();

            //13:0D Startpos Lo
            if (videx_regvalSLOT == 13) 
                redrawSLOT();
            
            //12:0C Startpos Hi
            */
            
        }
        else
            // define current register
            videx_regvalSLOT = value;
    }
    
    // IOSEL READS ROM
    // when reading using iosel, adds bit at A9
    uint8_t getRomIoSel(int addr) {
        
        addr |= 0b1000000000;
        if (addr < 0x0400)  return videx_rom[addr & 0x03ff];

        return 0;
    }

    // Read Video Memory using position x, y
    int8_t getCSLOTXX(int col, int row) {
        int vstart = ((videx_regSLOT[12] << 8) | videx_regSLOT[13]);
        int vcursor = ((videx_regSLOT[14] << 8) | videx_regSLOT[15]);

        int vaddr = (((row * 80) + col) + vstart) % 0x800;
        
        return videx_vram[vaddr];
    }
    
    // Read Character Rom (Char/scanline)
    int8_t getVideoBits(int8_t keycode, int line) {

        return videx_chars[keycode * 16 + line];
    }

    // Is cursor at defined x, y?
    bool isCursorPosition(int col, int row) {
        int vstart = ((videx_regSLOT[12] << 8) | videx_regSLOT[13]);
        int vcursor = ((videx_regSLOT[14] << 8) | videx_regSLOT[15]);
        return vcursor == (((row * 80) + col) + vstart);
    }

    // IOSTB - Get Rom or VRam 
    int getSLOTC8XX(int adr)  
    {
        if (adr < 0x0400) // Rom is at 0x0-0x3ff
            return(videx_rom[adr & 0x03ff]);
        else
            // VRAM is at 0x600-0x7FF
            // VRAM has 0x0800 at CC00-CDFF divided in 4 banks of 0x1FF
            if (adr < 0x0600) 
                return(videx_vram[(adr & 0x01ff) + videx_bankSLOT * 0x0200]);
            else //600 (or CE00-CF00)
                return(0);
    }

    // IOSTB - set VRam 
    void putSLOTC8XX(int adr, int value) 
    {
        // VRAM is at 0x600-0x7FF
        // VRAM has 0x0800 at CC00-CDFF divided in 4 banks of 0x1FF
        if ((adr >= 0x0400) && (adr < 0x0600))
        {
            int vadr;

            vadr = (adr & 0x01ff) + videx_bankSLOT * 0x0200;
            videx_vram[vadr] = value;
            
            //drawSLOT(vadr, value); // ignored 
        }
    }


    void card_videx_init() {

        FILE *g;
        int i;
        int value, bits, crow, ccol;

        // Read videx rom 
        printf("Loading rom: videx_firmware.rom\n");
        g = fopen("videx_firmware.rom", "r");
        if (g == 0)
        {
            fprintf(stderr, "can't find 'videx_firmware.rom'.\n");
            exit(1);
        }
        fread(videx_rom, 1, 0x0400, g);
        fclose(g);

        // Read videx character rom 
        printf("Loading rom: videx_chars.rom\n");
        g = fopen("videx_chars.rom", "r");
        if (g == 0)
        {
            fprintf(stderr, "can't find 'videx_chars.rom'.\n");
            exit(1);
        }
        fread(videx_chars, 1, 0x1000, g);
        fclose(g);


        // initializing registers
        videx_bankSLOT = 0;
        videx_regvalSLOT = 0;
        videx_regSLOT[0] = 0x7b;
        videx_regSLOT[1] = 0x50;
        videx_regSLOT[2] = 0x62;
        videx_regSLOT[3] = 0x29;
        videx_regSLOT[4] = 0x1b;
        videx_regSLOT[5] = 0x08;
        videx_regSLOT[6] = 0x18;
        videx_regSLOT[7] = 0x19;
        videx_regSLOT[8] = 0x0;
        videx_regSLOT[9] = 0x8;
        videx_regSLOT[10] = 0xc0;
        videx_regSLOT[11] = 0x8;
        videx_regSLOT[12] = 0x0;
        videx_regSLOT[13] = 0x0;
        videx_regSLOT[14] = 0x0;
        videx_regSLOT[15] = 0x0;
        videx_regSLOT[16] = 0x0;
        videx_regSLOT[17] = 0x0;
        //videx_upperSLOT = 0;
        //videx_lowerSLOT = 8;
        //videx_oldcursorSLOT = 0;

        for (i = 0; i < 2048; i++)
            videx_vram[i] = i & 0xff;
    }


    /*
    void drawSLOT(int adr, int value)
    {
        int sadr, crow, ccol;

        // update memory pixmap
        //XCopyArea(display, charsetSLOT, memorySLOT, gc, 8 * value, 0, 8, 12, adr * 8, 0);

        // update screen
        sadr = (0x800 + adr - 256 * videx_regSLOT[12] - videx_regSLOT[13]) & 0x7ff;
        crow = sadr / 80;
        ccol = sadr % 80;

        //printf("FRAMEBUFFER [W] %d x %d = %02X {%02x}\n", ccol, crow, value, ((crow * 80) + ccol) * 8 );
        //XCopyArea(display, charsetSLOT, wSLOT, gc, 8 * value, 0, 8, 12, ccol * 8, crow * 12);
    }

    void cursorSLOT()
    {
        int sadr, newcursor, crow, ccol;

        // remove old cursor
        drawSLOT(videx_oldcursorSLOT, videx_vram[videx_oldcursorSLOT]);

        // update cursor
        newcursor = (256 * videx_regSLOT[14] + videx_regSLOT[15]) & 0x7ff;
        sadr = (0x800 + newcursor - (256 * videx_regSLOT[12] + videx_regSLOT[13])) & 0x7ff;
        crow = sadr / 80;
        ccol = sadr % 80;

        //XFillRectangle(display, wSLOT, xgcSLOT, ccol * 8, crow * 12 + upperSLOT, 7, lowerSLOT - upperSLOT);
        //XFlush(display);

        // remember old cursor
        videx_oldcursorSLOT = newcursor;
    }


    void redrawSLOT()
    {
        int sadr, crow, ccol;

        sadr = (256 * videx_regSLOT[12] + videx_regSLOT[13]) & 0x7ff;
        for (crow = 0; crow < 24; crow++)
        {
            ccol = sadr + 80;
            if (ccol > 0x800)
            {
                ccol &= 0x7ff;
                //printf("%d x %d\n", ccol, crow);
                //XCopyArea(display, memorySLOT, wSLOT, gc, 8 * sadr, 0, 8 * (80 - ccol), 12, 0, crow * 12);
                //XCopyArea(display, memorySLOT, wSLOT, gc, 0, 0, 8 * ccol, 12, 8 * (80 - ccol), crow * 12);
            }
            else {
                //XCopyArea(display, memorySLOT, wSLOT,  gc, 8 * sadr, 0, 8 * 80, 12, 0, crow * 12);

            }
            sadr = ccol;
        }
        cursorSLOT();
    }
    void modifySLOT()
    {
        int val;

        // set upper cursor bound
        videx_upperSLOT = 0;
        val = videx_regSLOT[10] & 0x7f; // remove blinking-flag
        if (val)
            while ((val & 0x40) == 0)
            {
                videx_upperSLOT++;
                val = val << 1;
            }

        // set lower cursor bound
        videx_lowerSLOT = 12;
        val = videx_regSLOT[11];
        if (val)
            while ((val & 0x01) == 0)
            {
                videx_lowerSLOT--;
                val = val >> 1;
            }

        // if the setting is ridiculous
        if (videx_upperSLOT >= videx_lowerSLOT)
        {
            videx_upperSLOT = 0;
            videx_lowerSLOT = 12;
        }

        // draw new cursor
        cursorSLOT();
    }
*/
};
Like

Discussions