/*************************************************************************
 
    p8000_8.c driver.

   Copyright Matt Knoth 2008  p8000@knothusa.net

   - This source code is released as freeware for non-commercial purposes.
   - You are free to use and redistribute this code in modified or
     unmodified form, provided you list me in the credits.
   - If you modify this source code, you must add a notice to each modified
     source file that it has been changed.
   - The author of this copywritten work reserves the right to change the
     terms of its usage and license at any time, including retroactively
   - This entire notice must remain in the source code.

**************************************************************************/

#include <string.h>
#include "driver.h"
#include "ui.h"
#include "deprecat.h"
#include "cpu/z80/z80.h"
#include "cpu/z80/z80daisy.h"
#include "machine/z80sio.h"
#include "machine/i8272_floppies.h"
#include "machine/cqm_image.h"
#include "p8000_8.h"

extern UINT8 osd_read_ntv_keyboard(void);
extern void p8000_16_abstr_keyboard(UINT8 sio, UINT8 channel, UINT8 data);
extern void p8000_16_abstr_floppy_change_ready(  UINT8 which, const char* fname, UINT8 wrten );


#define TTY_DRAW_CHAR   1

typedef struct {
                 UINT8  term[24][80];   // terminal ram
                 UINT8  overflow;       // wrote beyond term
                 UINT8  curx;           // cursor x position
                 UINT8  cury;           // cursor y position
                 UINT8  flags;          // update flags
                 UINT8  cursorcnt;      // cursor counter
                 UINT8  cursorinv;      // invert cursor
                 UINT32 upd_line;       // line to be updated
                 UINT32 escapeseq;      // escape sequence 
                 UINT8* font;           // font rom selection
                 UINT8  sio_device;     // device number
                 UINT8  sio_channel;    // device channel
} tty_terminal;
            
UINT8* p8000_8_CharROM;
UINT8  p8000_8_present=0;               // run 8bit board 
UINT8  p8000_16_present=0;              // run 16bit board

static tty_terminal  terminal[8];       // all tty's
static tty_terminal* current_tty;       // current console display
static int           current_tty_no;    // current terminal number

void put_adm31_vt100( UINT8 term, UINT8 code )     // send a character to the terminal
{
     tty_terminal* tty;

     if(term>=8) return; 

     tty=&terminal[term];

     //////////////////////////////////////////////////////////////////////////////// escape sequences for ADM31 terminal
     if(tty->escapeseq) {
        tty->escapeseq = tty->escapeseq << 8;
        tty->escapeseq |=  code;

        if((tty->escapeseq & 0xffff00)==0x1b4700) {  // ESC G n  - character attributes
                                                     // ignored for now
            tty->escapeseq = 0;                      // reset escape mode

        } else if((tty->escapeseq & 0xffff0000)==0x1b3d0000)  { // ESC = Y X - cursor move

            tty->cury=((tty->escapeseq & 0xff00)>>8)-0x20;
            if(tty->cury>23) tty->curx=23;

            tty->curx=(tty->escapeseq & 0xff)-0x20;
            if(tty->curx>79) tty->curx=79;

            tty->escapeseq = 0;                                 // reset escape mode

        } else if((tty->escapeseq & 0x00ffffff)==0x001baa1e)  { // cm: clear screan and cursor home (50\E*^^)

            memset(&tty->term[0][0], 0x20, 80*24);              
            tty->curx=0;
            tty->cury=0;
            tty->escapeseq = 0;                                 // reset escape mode

        } else if((tty->escapeseq & 0xff00)==0x1b00)  { // ESC n - rest ADM31 sequences

            switch((UINT8)tty->escapeseq) {
            case 'G':                                           // ESC G n, not ready yet
            case '=':  return;                                  // ESC=XY,  not ready yet
            case 0xaa: return;                                  // ESC*^^,  not ready yet (actually 50\E 0xaa 0x1e)
            case 'i':  tty->curx&=0xfc;                         // align to 8
                       tty->curx+=8;                            // tab forward
                       if(tty->curx>79) { 
                          tty->curx=0; 
                          tty->cury++; 
                          if(tty->cury>23) {
                             tty->curx=79; 
                             tty->cury=23; 
                          }
                       }
                       break;      
            case 'I':  tty->curx&=0xfc;                 // align to 8
                       if(tty->curx>7)
                          tty->curx-=8;                 // tab backwards
                       else if(tty->cury) 
                          tty->cury--;

                       break;
            case '*':
            case ':':  memset(&tty->term[0][0], 0x20, 80*23);                                              // CLS
                       break;
            case 'T':  memset(&tty->term[tty->cury][tty->curx], 0x20, 80-tty->curx);                       // clear end of line
                       break;
            case 'Y':  memset(&tty->term[tty->cury][tty->curx], 0x20, (80*(23-tty->cury))+(80-tty->curx)); // clear end of page
                       break;
            case 'E':  if(tty->cury<23)
                          memmove(&tty->term[1+tty->cury][0],&tty->term[tty->cury][0],80*(23-tty->cury));  // line insert 
                       if(tty->cury<=23) memset(&tty->term[tty->cury][0], 0x20, 80);
                       break;
            case 'Q':  if(tty->curx<79)
                          memmove(&tty->term[tty->cury][1+tty->curx],&tty->term[tty->cury][tty->curx],     // insert character
                                   79-tty->curx);
                          tty->term[tty->cury][tty->curx]=0x20;
                       break;
            case 'R':  tty->curx=0;
                       if(tty->cury<23)
                          memmove(&tty->term[tty->cury][0],&tty->term[1+tty->cury][0],80*(23-tty->cury));  // line delete
                       memset(&tty->term[23][0], 0x20, 80);
                       break;
            case 'W':  if(tty->curx<79)
                          memmove(&tty->term[tty->cury][tty->curx],&tty->term[tty->cury][1+tty->curx],     // del character
                                   79-tty->curx);
                       tty->term[tty->cury][79]=0x20;
                       break;
            }

            tty->escapeseq = 0;                                 // reset escape mode

        } else {                                                // sequence not ready yet
            return;
        }

        tty->upd_line |= 1 << tty->cury;
        tty->flags = TTY_DRAW_CHAR;
        return;
     }
     code&=0x7f;                                                // cleanup unprintables
     //////////////////////////////////////////////////////////////////////////////// no escape sequence 
     if(code>0x1f) {
        tty->term[tty->cury][tty->curx++] = code;          // printable characters
        tty->upd_line |= 1 << tty->cury;
        if(tty->curx>79) {
           tty->curx=0;
           tty->cury++;
           if(tty->cury>23) {
              tty->cury=23;
              memcpy(&tty->term[0][0], &tty->term[1][0], 80*23);
              memset(&tty->term[23][0], 0x20, 80);
              tty->upd_line = 0x00ffffff;
           }
           tty->upd_line |= 1 << tty->cury;
        }
        tty->flags = TTY_DRAW_CHAR;

     } else {  ////////////////////////////////////////////////////////////////////// control characters

        switch(code) {
        case 0x07: fputc(0x07,stdout); fflush(stdout);                                 break; /* BELL */
        case 0x08: if(tty->curx) {                                                    /* BS   */
                      tty->curx--;
                   } else if(tty->cury) {
                             tty->cury--;
                             tty->curx=79;
                   }
                   break;
        case 0x09: tty->curx&=0xfc;                                                   /* HT   */
                   tty->curx+=8;
                   if(tty->curx>79) {
                      tty->curx=0;
                      tty->cury++;
                      if(tty->cury>23) {
                         tty->cury=23;
                         memmove(&tty->term[0][0],&tty->term[1][0], 80*23);
                         memset(&tty->term[23][0], 0x20, 80);
                         tty->upd_line = 0x00ffffff;
                         tty->flags = TTY_DRAW_CHAR;
                      }
                   }
                   break;
        case 0x0a: tty->cury++;                                                       /* LF   */
                   if(tty->cury>23) {
                      tty->cury=23;
                      memmove(&tty->term[0][0],&tty->term[1][0], 80*23);
                      memset(&tty->term[23][0], 0x20, 80);
                      tty->upd_line = 0x00ffffff;
                      tty->flags = TTY_DRAW_CHAR;
                   }
                   break;
        case 0x0b: if(tty->cury) tty->cury--;                                         /* VT   */
                   break;
        case 0x0c: tty->curx++;                                                       /* FF   */
                   if(tty->curx>79) {
                      tty->curx=0;
                      tty->cury++;
                      if(tty->cury>23) {
                         tty->cury=23;
                         memmove(&tty->term[0][0],&tty->term[1][0], 80*23);
                         memset(&tty->term[23][0], 0x20, 80);
                         tty->upd_line = 0x00ffffff;
                         tty->flags = TTY_DRAW_CHAR;
                      }
                   }
                   break;
        case 0x0d: tty->curx=0;                                                       /* CR   */
                   break;
        case 0x1b: tty->escapeseq = 0x1b;                                             /* ESC  */
                   break;
        case 0x1e: tty->curx=0; tty->cury=0;                                          /* HOME */
                   break;
        }
        tty->upd_line |= 1 << tty->cury;
        tty->flags = TTY_DRAW_CHAR;
    }
}

void draw_cursor(mame_bitmap* bitmap, UINT8 posx, UINT8 posy)
{
     UINT16 x, xval, yval;
     xval=(((UINT16)posx)<<3);
     yval=((((UINT16)posy)<<4)|0x0f);
     for(x=0; x<8; x++) {
         *BITMAP_ADDR16(bitmap, yval, xval+x) = 0x03e0;
     }
}

void draw_character(mame_bitmap* bitmap, UINT8 character, UINT8 posx, UINT8 posy, UINT8* font) 
{
     UINT16 x, y, xval, yval;
     UINT8  code;

     for(y=0; y<16; y++) {
         code = font[0x07ff & ((((UINT16)character)<<4) + y)];

         xval=(((UINT16)posx)<<3);
         yval=((((UINT16)posy)<<4)+y);
         for(x=0; x<8; x++) {
            *BITMAP_ADDR16(bitmap, yval, xval+x) = (code & 0x80)? 0x03e0 : 0x0000;
             code=code<<1;
         }
     }
}

static void floppy_change_ready_callback(  UINT8 which, const char* fname, UINT8 wrten )
{
     while(osd_read_ntv_keyboard());             // remove menu keys from buffer

     if(!wrten) {                                // if wrten just change write enable
        switch((int)fname) {
        case   0:   if(which) output_set_value("floppy1",0); else output_set_value("floppy0",0); break;
        case  -1:   if(which) output_set_value("floppy1",1); else output_set_value("floppy0",1); break;
        case  -2:                                                                                break;  // Menu exit w/o floppy changes
        default :   if(which) output_set_value("floppy1",1); else output_set_value("floppy0",1); break;
        }
     }

     if((int)fname!=-2) {
             if(p8000_8_present)           i8272_floppy_change_ready(which, fname, wrten);
        else if(p8000_16_present) p8000_16_abstr_floppy_change_ready(which, fname, wrten);
     }
}

static void tty_change_callback(  UINT8 which, const char* fname, UINT8 wrten )
{
     while(osd_read_ntv_keyboard());             // remove menu keys from buffer

     if(wrten<8) {                               // set terminal number
        current_tty_no = wrten;
        current_tty=&terminal[wrten];
     } else if(wrten==9) {                      // German font selected
        if(current_tty->font==p8000_8_CharROM) {
           current_tty->font+=0x0800;           // German ROM 2K upwards
           current_tty_no |=0x10;               // set German marker
        } else {
           current_tty->font=p8000_8_CharROM;   // English at ROM base
           current_tty_no &=0x07;               // reset German marker
        }
     }

     if(current_tty->font==p8000_8_CharROM)     // set current language rom
        current_tty_no &=0x07;                  // English ROM selected
     else
        current_tty_no |=0x10;                  // German ROM selected

     current_tty->escapeseq=0;                  // clear pending TTY escapes
     current_tty->upd_line = 0x00ffffff;        // set to update all
     current_tty->flags = TTY_DRAW_CHAR;        // request update
}

VIDEO_START( p8000_8 )
{
   int i;

   for(i=0; i<8; i++) {
       current_tty = &terminal[i];
       current_tty->flags=0;
       current_tty->curx=0;
       current_tty->cury=0;
       current_tty->upd_line=0;
       current_tty->cursorcnt=10;
       current_tty->cursorinv=0;
       current_tty->escapeseq=0;
       current_tty->font=p8000_8_CharROM;
       memset(&current_tty->term[0][0], 0x20, 80*24);
   }

   if(p8000_8_present) {
      terminal[0].sio_device = SIO0; terminal[0].sio_channel= 0; // native sio
      terminal[1].sio_device = SIO0; terminal[1].sio_channel= 1;
      terminal[2].sio_device = SIO1; terminal[2].sio_channel= 0;
      terminal[3].sio_device = SIO1; terminal[3].sio_channel= 1;
   } else {
      terminal[0].sio_device =    2; terminal[0].sio_channel= 0; // 8bit abstraction layer
      terminal[1].sio_device =    2; terminal[1].sio_channel= 1;
      terminal[2].sio_device =    3; terminal[2].sio_channel= 0;
      terminal[3].sio_device =    3; terminal[3].sio_channel= 1;
   }

   terminal[4].sio_device = SIO0; terminal[4].sio_channel= 0;  // 16bit board
   terminal[5].sio_device = SIO0; terminal[5].sio_channel= 1;  // 16bit board
   terminal[6].sio_device = SIO1; terminal[6].sio_channel= 0;  // 16bit board
   terminal[7].sio_device = SIO1; terminal[7].sio_channel= 1;  // 16bit board

   current_tty_no = 1;           // set console tty1
   current_tty = &terminal[1];   // set console tty1
   current_tty->upd_line = 0x00ffffff;
   current_tty->flags = TTY_DRAW_CHAR;

}

VIDEO_UPDATE( p8000_8 )
{ int x, y, res;


  UINT8 keycode=osd_read_ntv_keyboard();                        // read keyboard
  if(keycode) {
    if(!(keycode & 0x80)) {
         if(p8000_8_present | (current_tty->sio_device<2)) 
               z80sio_receive_data(current_tty->sio_device,current_tty->sio_channel,keycode);       // send character to 8bit board
         else  p8000_16_abstr_keyboard(current_tty->sio_device-2,current_tty->sio_channel,keycode); // send charatcer to 8bit board abstraction layer
    } else if((keycode & 0xf0)==0xa0) {                                              
           UINT8 key = keycode;

           switch(keycode) {               // cursor keys
           case 0xa4:     key=0x1e; break; // HOME
           case 0xa5:     key=0x08; break; // BS
           case 0xa6:     key=0x0b; break; // VT
           case 0xa7:     key=0x0c; break; // FF
           case 0xa8:     key=0x0a; break; // LF
           case 0xae:     key=0x7f; break; // DEL
           }

           if(p8000_8_present | (current_tty->sio_device<2)) 
                z80sio_receive_data(current_tty->sio_device,current_tty->sio_channel,key);
           else p8000_16_abstr_keyboard(current_tty->sio_device-2,current_tty->sio_channel,key);

    } else switch(keycode) {                                        // F1-F10 characters
           case 0xf0: ui_show_about_menu(0);                        // F1 about menu
                      break;
           case 0xf1: mame_pause(Machine, TRUE);                    // F2 tty change menu
                      ui_show_tty_menu(tty_change_callback, current_tty_no);                   
                      break;
           case 0xf2: mame_pause(Machine, TRUE);                    // F3 exit menu
                      ui_show_on_exit_menu(0);
                      break;
           case 0xf4: if(p8000_8_present ) p8000_reset_button();    // F5 - RESET button
                      if(p8000_16_present) p8000_16_reset_button(); 
                      break;
           case 0xf5: if(p8000_8_present ) p8000_nmi_button();      // F6 - NMI button
                      if(p8000_16_present) p8000_16_nmi_button();   
                      break;
           case 0xf6: mame_pause(Machine, TRUE);                    // floppy drive zero
                      ui_show_floppy_menu(floppy_change_ready_callback,0);                   
                      break;
           case 0xf7: mame_pause(Machine, TRUE);                    // floppy drive one
                      ui_show_floppy_menu(floppy_change_ready_callback,1);
                      break;
         }
  } 

  res=0;
  switch(current_tty->flags) {
  case (0                            ):  res=1; break;
  case (TTY_DRAW_CHAR                ):  for(y=0; y<24; y++) {
                                             for(x=0; x<80; x++) {
                                                 draw_character(bitmap, current_tty->term[y][x], x, y, current_tty->font);
                                             }
                                         }
                                         draw_cursor(bitmap, current_tty->curx, current_tty->cury);
                                         current_tty->flags = 0;
                                         current_tty->upd_line = 0;
                                         break;
  default                             :  res=1;
  }

  return res;
}

