/*
 * Copyright © 1994-1996 Marko Mäkelä and Olaf Seibert
 * Original Linux and Commodore 64/128/Vic-20 version by Marko Mäkelä
 * Ported to the PET and the Amiga series by Olaf Seibert
 * 
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 of the License, or
 *     (at your option) any later version.
 * 
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 * 
 *     You should have received a copy of the GNU General Public License
 *     along with this program; if not, write to the Free Software
 *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifdef __linux__
#include <unistd.h>
#include <asm/io.h>
#endif /* __linux__ */
#include "prtrans.h"

unsigned baseaddr, stataddr;

int prinit (void) {
#ifdef __linux__
  if (ioperm (baseaddr, 2, 1))
    return -1;
#endif /* __linux__ */

  outb(128, baseaddr); /* set the -FLAG signal to high */
  stataddr = baseaddr + 1;

  return 0;
}

void prclose (void) {
}

void output (unsigned char byte) {
  send (&byte, 1);
}

#ifndef MSDOS
void wait_output (unsigned char byte) {
  outb(byte & 0x0f, baseaddr);
  while (!(128 & inb(stataddr)))
    usleep (SLEEP_TIME); /* wait between each try */

  outb(128, baseaddr);
  outb(byte >> 4, baseaddr);
  while (128 & inb(stataddr));
  outb(128, baseaddr);
}
#endif /* !defined(MSDOS) */

unsigned input (void) {
  unsigned char byte;

  receive (&byte, 1);
  return byte;
}

#ifndef MSDOS
unsigned wait_input (void) {
  register unsigned char data;

  outb(0, baseaddr);
  while (!(128 & inb(stataddr)))
    usleep (SLEEP_TIME); /* wait between each try */

  outb(128, baseaddr);
  data = (inb(stataddr) & 0x78) >> 3;
  outb(0, baseaddr);
  while (128 & inb(stataddr));
  data |= (inb(stataddr) & 0x78) << 1;
  outb(128, baseaddr);

  return data;
}
#endif /* !defined(MSDOS) */

#if !(defined(__i386__) && defined(__GNU_C__)) && !defined(_MSC_VER)
void send (unsigned char *buffer, unsigned length) {
  register unsigned char byte;

  while (length--) {
    byte = *buffer++;

    outb(byte & 0x0f, baseaddr);
    while (!(128 & inb(stataddr)));
    outb(128, baseaddr);
    outb(byte >> 4, baseaddr);
    while (128 & inb(stataddr));
    outb(128, baseaddr);
  }
}
#elif defined(__i386__) && defined(__GNU_C__) /* __i386__ && __GNU_C__ */
void send (unsigned char *buffer, unsigned length) {
  asm ("push %%ebp
        movw _baseaddr,%%dx
        movl %1,%%ecx
        movl %0,%%ebp

sloop:  movb (%%ebp),%%al
        andb $0x0f,%%al
        outb %%al,%%dx
        incw %%dx

ewait:  inb %%dx,%%al
        testb $0x80,%%al
        jz ewait

        decw %%dx
        movb $0x80,%%al
        outb %%al,%%dx
        movb (%%ebp),%%al
        shrb $4,%%al
        outb %%al,%%dx
        incw %%dx

zwait:  inb %%dx,%%al
        testb $0x80,%%al
        jnz zwait

        decw %%dx
        movb $0x80,%%al
        outb %%al,%%dx

        inc %%ebp
        loop sloop
        pop %%ebp"
       : /* no outputs */
       : "g" (buffer), "g" (length)
       : "ax", "ecx", "dx");
}
#else /* _MSC_VER */
void send (unsigned char *buffer, unsigned len) {
  __asm {
    push ds
    mov dx,baseaddr
    mov cx,len
    mov ds,word ptr buffer+2
    mov bx,word ptr buffer
sloop:
    mov al,[bx]
    and al,0x0f
    out dx,al
    inc dx

ewait:
    in al,dx
    test al,0x80
    jnz newait
    in al,0x60  ; is the esc key pressed?
    dec al
    jnz ewait
    jz abort

newait:
    dec dx
    mov al,0x80
    out dx,al
    mov al,[bx]
#if defined(_M_I286) || defined(_M_I386)
    shr al,4
#else
    shr al,1
    shr al,1
    shr al,1
    shr al,1
#endif
    out dx,al
    inc dx

zwait:
    in al,dx
    test al,0x80
    jz nzwait
    in al,0x60  ; is the esc key pressed?
    dec al
    jnz zwait
    jz abort

nzwait:
    dec dx
    mov al,0x80
    out dx,al

    inc bx
    loop sloop
abort:
    pop ds
  }

  if (inb(0x60) == 1) exit(-1);
}
#endif

#if !(defined(__i386__) && defined(__GNU_C__)) && !defined(_MSC_VER)
void receive (unsigned char *buffer, unsigned length) {
  register unsigned char data;

  while (length--) {
    outb(0, baseaddr);
    while (!(128 & inb(stataddr)));
    outb(128, baseaddr); 
    data = (inb(stataddr) & 0x78) >> 3;
    outb(0, baseaddr);
    while (128 & inb(stataddr));
    *buffer++ = data | ((inb(stataddr) & 0x78) << 1);  
    outb(128, baseaddr);
  }
}
#elif defined(__i386__) && defined(__GNU_C__) /* __i386__ && __GNU_C__ */
void receive (unsigned char *buffer, unsigned length) {
  asm ("push %%ebp
        movw _baseaddr,%%dx
        movl %1,%%ecx
        movl %0,%%ebp

rloop:  xorb %%al,%%al
        outb %%al,%%dx
        incw %%dx

dwait:  inb %%dx,%%al
        testb $0x80,%%al        
        jz dwait

        andb $0x78,%%al
        shrb $3,%%al
        movb %%al,(%%ebp)
        decw %%dx
        movb $0x80,%%al
        outb %%al,%%dx
        xorb %%al,%%al
        outb %%al,%%dx
        incw %%dx

vwait:  inb %%dx,%%al
        testb $0x80,%%al
        jnz vwait

        andb $0x78,%%al
        shlb $1,%%al
        orb %%al,(%%ebp)
        decw %%dx
        movb $0x80,%%al
        outb %%al,%%dx

        inc %%ebp
        loop rloop
        pop %%ebp"
       : /* no outputs */
       : "g" (buffer), "g" (length)
       : "ax", "ecx", "dx");
}
#else /* _MSC_VER */
void receive (unsigned char *buffer, unsigned len) {
  __asm {
    push ds
    mov dx,baseaddr
    mov cx,len
    mov ds,word ptr buffer+2
    mov bx,word ptr buffer

rloop:
    xor al,al
    out dx,al
    inc dx

dwait:
    in al,dx
    test al,0x80
    jnz ndwait
    in al,0x60  ; is the esc key pressed?
    dec al
    jnz dwait
    jz abort

ndwait:
    and al,0x78
#if defined(_M_I286) || defined(_M_I386)
    shr al,3
#else
    shr al,1
    shr al,1
    shr al,1
#endif
    mov [bx],al
    dec dx
    mov al,0x80
    out dx,al
    xor al,al
    out dx,al
    inc dx

vwait:
    in al,dx
    test al,0x80
    jz nvwait
    in al,0x60  ; is the esc key pressed?
    dec al
    jnz vwait
    jz abort

nvwait:
    and al,0x78
    shl al,1
    or [bx],al
    dec dx
    mov al,0x80
    out dx,al

    inc bx
    loop rloop

abort:
    pop ds
  }

  if (inb(0x60) == 1) exit(-1);
}
#endif
