/*
 * 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, ctrladdr;
unsigned ack;

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

  outb(8, baseaddr); /* set the -FLAG signal to high */
  stataddr = baseaddr + 1;
  ctrladdr = baseaddr + 2;
  ack = 0x80 & inb(stataddr); /* get the current state of the BUSY line */

  return 0;
}

void prclose (void) {
}

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

#ifndef MSDOS
void wait_output (unsigned char byte) {
  byte ^= 0x0b; /* invert bits 0, 1 and 3 */
  outb(byte, ctrladdr);
  outb(byte & ~8, baseaddr); /* drop the -FLAG to low */
  while (ack == (0x80 & inb(stataddr)))
    usleep (SLEEP_TIME); /* wait for BUSY to change */

  outb(8, baseaddr); /* raise -FLAG again */
  ack ^= 0x80; /* store the new state of -BUSY */
}
#endif

unsigned input (void) {
  unsigned char byte;

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

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

  outb(4, ctrladdr); /* set the bidirectional signals to input */
  outb(0, baseaddr); /* drop -FLAG */

  while (ack == (0x80 & inb(stataddr))) /* wait for -BUSY to change */
    usleep (SLEEP_TIME); /* wait before retrying */

  outb(8, baseaddr); /* raise -FLAG */

  data = inb(ctrladdr) & 0x0f; /* read the low nybble */

  outb(0, baseaddr); /* drop -FLAG */
  while (ack != (0x80 & inb(stataddr))); /* wait for -BUSY to change */

  data |= inb(ctrladdr) << 4; /* read the high nybble */

  outb(8, baseaddr); /* raise -FLAG */

  return data ^ 0xbb;
}
#endif

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

  while (length--) {
    byte = *buffer++ ^ 0x0b; /* invert bits 0, 1 and 3 */
    outb(byte, ctrladdr);
    outb(byte & ~8, baseaddr); /* drop the -FLAG to low */
    while (ack == (0x80 & inb(stataddr))); /* wait for BUSY to change */
    outb(8, baseaddr); /* raise -FLAG again */
    ack ^= 0x80; /* store the new state of -BUSY */
  }
}
#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
        movb _ack,%%ah

sloop:  movb (%%ebp),%%al
        xorb $0xb,%%al
        addw $2,%%dx
        outb %%al,%%dx
        subw $2,%%dx
        andb $0xf7,%%al
        outb %%al,%%dx
        incw %%dx

owait:  inb %%dx,%%al
        andb $0x80,%%al
        cmpb %%al,%%ah
        je owait

        decw %%dx
        xorb $0x80,%%ah
        movb $8,%%al
        outb %%al,%%dx

        inc %%ebp
        loop sloop
        movb %%ah,_ack
        pop %%ebp"
       : /* no outputs */
       : "g" (buffer), "g" (length)
       : "ax", "ecx", "dx");
}
#else /* _MSC_VER */
void send (unsigned char *buffer, unsigned len) {
  __asm {
    mov dx,baseaddr
    mov cx,len
    mov ah,byte ptr ack

    push ds
    mov bx,word ptr buffer
    mov ds,word ptr buffer+2
sloop:
    mov al,[bx]
    xor al,0xb
    add dx,2
    out dx,al
    sub dx,2
    and al,0xf7
    out dx,al
    inc dx

owait:
    in al,dx
    and al,0x80
    cmp al,ah
    jne nowait
    in al,0x60
    cmp al,1    /* esc pressed? */
    jne owait
    je abort

nowait:
    dec dx
    mov ah,al
    mov al,8
    out dx,al

    inc bx
    loop sloop

abort:
    pop ds
    mov byte ptr ack,ah
  }

  if (inb(0x60) == 1) exit(-1);
}
#endif /* __i386__ && __GNU_C__ */

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

  while (length--) {
    outb(4, ctrladdr); /* set the bidirectional signals to input */
    outb(0, baseaddr); /* drop -FLAG */
    while (ack == (0x80 & inb(stataddr))); /* wait for -BUSY to change */
    outb(8, baseaddr); /* raise -FLAG */

    data = inb(ctrladdr) & 0x0f; /* read the low nybble */

    outb(0, baseaddr); /* drop -FLAG */
    while (ack != (0x80 & inb(stataddr))); /* wait for -BUSY to change */

    data |= inb(ctrladdr) << 4; /* read the high nybble */

    outb(8, baseaddr); /* raise -FLAG */

    *buffer++ = data ^ 0xbb;
  }
}
#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
        movb _ack,%%ah

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

iwait1: inb %%dx,%%al
        andb $0x80,%%al
        cmpb %%ah,%%al
        je iwait1

        decw %%dx
        movb $8,%%al
        outb %%al,%%dx
        addw $2,%%dx
        movb $4,%%al
        outb %%al,%%dx
        inb %%dx,%%al
        and $0xf,%%al
        movb %%al,(%%ebp)

        subw $2,%%dx
        xorb %%al,%%al
        outb %%al,%%dx
        incw %%dx

iwait2: inb %%dx,%%al
        andb $0x80,%%al
        cmpb %%ah,%%al
        jne iwait2

        decw %%dx
        movb $8,%%al
        outb %%al,%%dx

        addw $2,%%dx
        inb %%dx,%%al
        subw $2,%%dx
        salb $4,%%al
        orb (%%ebp),%%al
        xorb $0xbb,%%al
        movb %%al,(%%ebp)

        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 {
    mov dx,baseaddr
    mov cx,len
    mov ah,byte ptr ack

    push ds
    mov bx,word ptr buffer
    mov ds,word ptr buffer+2

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

iwait1:
    in al,dx
    and al,0x80
    cmp al,ah
    jne niwait1
    in al,0x60  ; is the esc key pressed?
    dec al
    jnz iwait1
    jz abort

niwait1:
    dec dx
    mov al,8
    out dx,al
    add dx,2
    mov al,4
    out dx,al
    in al,dx
    and al,0xf
    mov [bx],al

    sub dx,2
    xor al,al
    out dx,al
    inc dx

iwait2:
    in al,dx
    and al,0x80
    cmp al,ah
    je niwait2
    in al,0x60  ; is the esc key pressed?
    dec al
    jnz iwait2
    jz abort

niwait2:
    dec dx
    mov al,8
    out dx,al

    add dx,2
    in al,dx
    sub dx,2
        
#if defined(_M_I286) || defined(_M_I386)
    sal al,4
#else
    sal al,1
    sal al,1
    sal al,1
    sal al,1
#endif
    or al,[bx]
    xor al,0xbb
    mov [bx],al

    inc bx
    loop rloop
abort:
    pop ds
  }

  if (inb(0x60) == 1) exit(-1);
}
#endif /* __i386__ && __GNU_C__ */
