/*
 * 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 <signal.h>
#include <unistd.h>
#include <asm/io.h>
#endif /* __linux__ */
#include "prtrans.h"

unsigned baseaddr, stataddr;

#ifdef __linux__
static void cleanup (int i);

static void cleanup (int i) {
  outb(0, baseaddr); /* this ensures that the server doesn't remain blocked */
  exit(-1);
}
#endif /* __linux__ */

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

  signal(SIGTERM, cleanup);
#endif /* __linux__ */
  outb(0, baseaddr);
  stataddr = baseaddr + 1;

  return 0;
}

void prclose (void) {
  outb(0, baseaddr);
}

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

#ifndef MSDOS
void wait_output (unsigned char byte) {
  outb(byte | 8, baseaddr);
  while(0x20 & inb(stataddr))
    usleep (SLEEP_TIME);

  outb(byte << 4, baseaddr);
  while(!(0x20 & inb(stataddr)));
}
#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(0xf0, baseaddr);
  while(0x20 & inb(stataddr))
    usleep (SLEEP_TIME);

  outb(0, baseaddr);

  while(!(0x20 & inb(stataddr)));
  data = (0xc0 & inb(stataddr)) >> 6;
  outb(8, baseaddr);

  while(0x20 & inb(stataddr));
  data |= (0xc0 & inb(stataddr)) >> 4;
  outb(0, baseaddr);

  while(!(0x20 & inb(stataddr)));
  data |= (0xc0 & inb(stataddr)) >> 2;
  outb(8, baseaddr);

  while(0x20 & inb(stataddr));
  data |= 0xc0 & inb(stataddr);
  outb(0, baseaddr);

  while(!(0x20 & inb(stataddr)));

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

#if !(defined(__i386__) && defined(__GNU_C__)) && !defined(_MSC_VER)
void send (unsigned char *buffer, unsigned length) {
  while (length--) {
    outb(*buffer | 8, baseaddr);
    while(0x20 & inb(stataddr));
    outb(*buffer++ << 4, baseaddr);
    while(!(0x20 & inb(stataddr)));
  }
}
#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
	orb $8,%%al
	outb %%al,%%dx

	incw %%dx
wait1:
        inb %%dx,%%al
        testb $32,%%al
        jne wait1

        movb %0,%%al
        salb $4,%%al
        decw %%dx
        outb %%al,%%dx

        incw %%dx
wait2:
        inb %%dx,%%al
        testb $32,%%al
        je wait2
	decw %%dx

	inc %%ebp
	loop sloop
	pop %%ebp"
       : /* no outputs */
       : "g" (buffer), "g" (length)
       : "al", "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]
    or al,8
    out dx,al

    inc dx
wait1:
    in al,dx
    test al,32
    je nwait1
    in al,0x60  ; is the esc key pressed?
    dec al
    jnz wait1
    jz abort

nwait1:
    mov al,[bx]
#if defined(_M_I286) || defined(_M_I386)
    shl al,4
#else
    shl al,1
    shl al,1
    shl al,1
    shl al,1
#endif
    dec dx
    out dx,al

    inc dx
wait2:
    in al,dx
    test al,32
    jne nwait2
    in al,0x60  ; is the esc key pressed?
    dec al
    jnz wait1
    jz abort
nwait2:
    dec dx

    inc bx
    loop sloop
abort:
    pop ds
  }

  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(0xf0, baseaddr);
    while(0x20 & inb(stataddr));
    outb(0, baseaddr);

    while(!(0x20 & inb(stataddr)));
    data = (0xc0 & inb(stataddr)) >> 6;
    outb(8, baseaddr);

    while(0x20 & inb(stataddr));
    data |= (0xc0 & inb(stataddr)) >> 4;
    outb(0, baseaddr);

    while(!(0x20 & inb(stataddr)));
    data |= (0xc0 & inb(stataddr)) >> 2;
    outb(8, baseaddr);

    while(0x20 & inb(stataddr));
    data |= 0xc0 & inb(stataddr);
    outb(0, baseaddr);

    *buffer++ = data ^ 0xaa;

    while(!(0x20 & inb(stataddr)));
  }
}
#elif defined(__i386__) && defined(__GNU_C__) /* __i386__ && __GNU_C__ */
void receive (unsigned char *buffer, unsigned length) {
  asm ("movw _baseaddr,%%dx
	push %%ebp
	movl %1,%%ecx
	movl %0,%%ebp

rloop:	movb $0xf0,%%al
	outb %%al,%%dx
	incw %%dx
input0:
	inb %%dx,%%al
	testb $32,%%al
	jne input0
	decw %%dx

	xorb %%al,%%al
	outb %%al,%%dx
	incw %%dx

input1:
	inb %%dx,%%al
	testb $32,%%al
	je input1
	decw %%dx
	andb $0xc0,%%al
	shrb $6,%%al
	mov %%al,(%%ebp)

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

input2:
	inb %%dx,%%al
	testb $32,%%al
	jne input2
	decw %%dx
	andb $0xc0,%%al
	shrb $4,%%al
	orb %%al,(%%ebp)

	xorb %%al,%%al
	outb %%al,%%dx
	incw %%dx

input3:
	inb %%dx,%%al
	testb $32,%%al
	je input3
	decw %%dx
	andb $0xc0,%%al
	shrb $2,%%al
	orb %%al,(%%ebp)

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

input4:
	inb %%dx,%%al
	testb $32,%%al
	jne input4
	decw %%dx
	andb $0xc0,%%al
	xorb $0xaa,%%al
	xorb %%al,(%%ebp)

	xorb %%al,%%al
	outb %%al,%%dx
	incw %%dx

input5:
	inb %%dx,%%al
	testb $32,%%al
	je input5

	decw %%dx

	inc %%ebp
	loop rloop
	pop %%ebp"
       : /* no outputs */
       : "g" (buffer), "g" (length)
       : "ax", "cx", "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:
    mov al,0xf0
    out dx,al
    inc dx
input0:
    in al,dx
    test al,32
    je ninput0
    in al,0x60  ; is the esc key pressed?
    dec al
    jnz input0
    jmp abort

ninput0:
    dec dx

    xor al,al
    out dx,al
    inc dx

input1:
    in al,dx
    test al,32
    jne ninput1
    in al,0x60  ; is the esc key pressed?
    dec al
    jnz input1
    jz abort
ninput1:
    dec dx
    and al,0xc0
#if defined(_M_I286) || defined(_M_I386)
    shr al,6
#else
    shr al,1
    shr al,1
    shr al,1
    shr al,1
    shr al,1
    shr al,1
#endif
    mov [bx],al

    mov al,8
    out dx,al
    inc dx

input2:
    in al,dx
    test al,32
    je ninput2
    in al,0x60  ; is the esc key pressed?
    dec al
    jnz input2
    jz abort
ninput2:
    dec dx
    and al,0xc0
#if defined(_M_I286) || defined(_M_I386)
    shr al,4
#else
    shr al,1
    shr al,1
    shr al,1
    shr al,1
#endif
    or [bx],al

    xor al,al
    out dx,al
    inc dx

input3:
    in al,dx
    test al,32
    jne ninput3
    in al,0x60  ; is the esc key pressed?
    dec al
    jnz input3
    jz abort
ninput3:
    dec dx
    and al,0xc0
#if defined(_M_I286) || defined(_M_I386)
    shr al,2
#else
    shr al,1
    shr al,1
#endif
    or [bx],al

    mov al,8
    out dx,al
    inc dx

input4:
    in al,dx
    test al,32
    je ninput4
    in al,0x60  ; is the esc key pressed?
    dec al
    jnz input4
    jz abort
ninput4:
    dec dx
    and al,0xc0
    xor al,0xaa
    xor [bx],al

    xor al,al
    out dx,al
    inc dx

input5:
    in al,dx
    test al,32
    jne ninput5
    in al,0x60  ; is the esc key pressed?
    dec al
    jnz input5
    jz abort
rlop:
    jmp rloop	; otherwise the branch would be out of range
ninput5:
    dec dx

    inc bx
    loop rlop	; otherwise the branch would be out of range
abort:
    pop ds
  }

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