package main

import "log"

// i8255 is a generic Intel 8255-compatible I/O controller chip.
//
// Only mode 0 is supported.
type i8255 struct {
	pa, pb, pc uint8

	onOut func(port i8255Port)
	onIn  func(port i8255Port)
}

type i8255Port string

const (
	PA i8255Port = "PA"
	PB i8255Port = "PB"
	PC i8255Port = "PC"
)

func (i *i8255) getPort(p i8255Port) uint8 {
	switch p {
	case PA:
		return i.pa
	case PB:
		return i.pb
	case PC:
		return i.pc
	default:
		log.Fatalf("invalid port")
		return 0
	}
}

func (i *i8255) Out(addr, value uint8) {
	switch addr {
	case 0:
		i.pa = value
		if i.onOut != nil {
			i.onOut(PA)
		}
	case 1:
		i.pb = value
		if i.onOut != nil {
			i.onOut(PB)
		}
	case 2:
		i.pc = value
		if i.onOut != nil {
			i.onOut(PC)
		}
	case 3:
		if (value >> 7) == 0 {
			// BSR mode
			bitno := (value >> 1) & 0b111
			bitval := value & 1
			if bitval == 1 {
				i.pc |= (1 << bitno)
			} else {
				i.pc &= 0xff ^ (1 << bitno)
			}
		} else {
			// Input/Output mode
			gaMode := (value >> 5) & 0b11
			gbMode := (value >> 5) & 0b11
			if gaMode != 0 {
				log.Fatalf("I8255 implementation only supports mode 0 on Port A")
			}
			if gbMode != 0 {
				log.Fatalf("I8255 implementation only supports mode 0 on Port B")
			}
			// Don't care about the rest (input/output settings per group/port).
		}
	default:
		log.Printf("I8255: invalid out")
	}
}

func (i *i8255) In(addr uint8) uint8 {
	switch addr {
	case 0:
		if i.onIn != nil {
			i.onIn(PA)
		}
		return i.pa
	case 1:
		if i.onIn != nil {
			i.onIn(PB)
		}
		return i.pb
	case 2:
		if i.onIn != nil {
			i.onIn(PC)
		}
		return i.pc
	default:
		log.Printf("I8255: invalid in")
		return 0
	}
}
