Source file: /~heha/basteln/PC/USB2LPT/lpt2usb.zip/lpt2usb.a90

;Firmware für ATmega16 auf LPT2USB, Parallel-zu-USB-Konverter
;für Drucker und (später) USB-Speicher - haftmann#software
;Zu übersetzen mit `avrasm2 -fI -FD"%d.%m.%y" lpt2usb.a90´
;Zu brennen mit PonyProg2000, alle CKSEL-Häkchen entfernen
;ECP, EPP zz. nicht unterstützt!
;Rote LED stets an, grüne LED blinkt nicht bei Datenverkehr
;Anzeigen FIFO-Füllstand (mit MIN und MAX) wären nett...

;060825	Erste lauffähige Version
;060826	IEEE1284-Aushandlung und Geräte-ID
;060827	Datenrate-Anzeige

#define DEBUG
#define FULL_FRAME	;1-ms-Rahmen komplett ausnutzen
;Hardware:
;Pinzuordnung:
;PortA:	EzHost-Datenport
;	0*: Spannungsüberwachung (zusätzlich; analog)
;PortB:	LPT-Datenport
;PortC:
.equ BSY = 7	;7-3: LPT-Statusport Bits 7-3
.equ ACK = 6
.equ P_E = 5
.equ ONL = 4
.equ ERR = 3
.equ SEL = 2	;2-0: LPT-Steuerport Bits 3-1
.equ INI = 1	;mit /RESET verbunden per Lötbrücke
.equ A_F = 0
;PortD:
.equ LedG= 7	;*?LED grün (Verbindung)
.equ WR  = 6	;EzHost /WR
.equ RD  = 5	;EzHost /RD
.equ A0  = 4	;EzHost A0
.equ LedR= 3	;LED rot (Spannung)
.equ STB = 2	;*LPT-Steuerport Bit 0 (Strobe = INT0)
;	1*: serielles Port TxD
;	0*: serielles Port RxD	
;* = Verwendung der pinspezifischen Sonderfunktion

.equ	CPUCLK = 12000000
.equ	BaudRate = 38400	;Baudrate serielle Schnittstelle (Debug)
#define ZERO YH
#define UPPERCOLOR 0x67		;Farbe in oberer Bildhälfte: Grau auf Zyan

;Tastenfunktionen im HyperTerminal zz:
;^L: Bild neu aufbauen
;^R: Mikrocontroller-Reset (in etwa)
; r: Byte-Zähler rücksetzen
; p: Strobe auslösen, 1 Byte "drucken"
; d: 1284-Geräte-ID lesen und anzeigen (Voraussetzung: Puffer leer)
; e: EzBits anzeigen

.NOLIST
.include "m16def.inc"
.include "makros.i90"
.LIST

.def	EzBits = r22	;verschiedene Melde-Bits (für SL811)
.equ	LOW_SPEED = 0
.equ	SLAVE_FOUND = 1
;.equ	SLAVE_ENUMERATED = 2
.equ	BUFFER_EMPTY = 3	;LPT: Sektorpuffer leer
.equ	BUFFER_FULL = 4		;LPT: Sektorpuffer voll (8fach Ringpuffer)
.equ	HAVE_1284_ID = 5	;Sektor enthält 1284-ID-String
.equ	USBMEM_FOUND = 6	;Emulation eines Remote-PC für Total Commander
.equ	PRINTER_FOUND = 7	;Emulation eines Paralleldruckers

.def	UART_InChar = r23
.def	ONES = r12	;lauter Einsen
;Bei allen "aktiven" Portzuständen bleiben die LEDs aus...


;Zur schnellen Reaktion auf Strobe-Impulse:
.def	DataCatch	= r14	;Aufgeschnappter Wert bei Strobe H-L
.def	StatusResp	= r15	;PortC-Ausgabe bei eintretendem Interrupt

.dseg	;AVR-ASM fängt freundlicherweise bei 60h an zu zählen
#define MAX_DEVDAT 256	;Bytes Platz für Geräte-Infos, Gerätezahl ca. 32
#define MAX_DESC 512	;Puffergröße für das Holen von Deskriptoren (1 Sektor)
#define LPTTIMEO 10	;Millisekunden?
;Bei 1 KB Platz muss etwas gehaushaltet werden.
;Wir brauchen ja auch noch einen Sektorpuffer (512 Bytes)
;und Verwaltungsinfo für den FAT-Zugriff.

SetupDat:	.byte 8		;Fester Transferpuffer für Setup EP0
DevPrinter:	.byte 2		;Zeiger auf Drucker-DevDat (oder USB-Stick)
EpOutIdx:	.byte 1		;Index des OUT-Endpoints
EpInIdx:	.byte 1		;Index des IN-Endpoints (später)
LptStatus:	.byte 1		;Puffer für "GET_PORT_STATUS"-Request
LptTimeOut:	.byte 1		;TimeOut-Zähler
LptSent:	.byte 4		;Byte-Zähler
LptRecv:	.byte 4		;Byte-Zähler
Zyklus:		.byte 1		;Drehstrich-Zähler sowie Aktualisierungsbits
LastStatus:	.byte 1		;Statusanzeige-Cache
LastSent:	.byte 3		;24-bit-Zahlen zur Datenratemessung
LastRecv:	.byte 3

.def LptPtrRdL = r8		;Sektor-Zeiger zum USB-Transfer
.def LptPtrRdH = r9
.def LptPtrWrL = r10		;Sektor-Zeiger zum Einschreiben
.def LptPtrWrH = r11

;Verwaltungsdaten für USB-Speicher
#define cbwSignature		0
#define cbwTag			4
#define cbwDataTransferLength	8
#define cbwFlags		12
#define cbwLun			13
#define cbwLength		14
#define cbwCB			15
#define cbwStructSize		31
#define cswSignature		0
#define cswTag			4
#define cswDataResidue		8
#define cswStatus		12
#define cswStructSize		13
gCBW:		.byte cbwStructSize	;"Command Block Wrapper"
gCSW:		.byte cswStructSize	;"Command Status Wrapper"

;Gerätedaten (aus Geräte-Beschreibern)
DevDat:		.byte MAX_DEVDAT
.org 0x200
Sektor:		.byte 512	;Sektorpuffer 200h..3FFh
SektorE:			;Endadresse+1 (0x400)
Stack:		.byte 0x60	;Stack(s) 96 Byte


.cseg
;Adresse Null
	rjmp	main
	.dw	(__YEAR__+20)*512+__MONTH__*32+__DAY__


;Interruptserviceroutine zum Auffangen der Daten bei Strobe (ohne rjmp)
.org INT0addr	;=2
	in	DataCatch,PinB	;Zustand der Datenleitungen auffangen
	out	PortC,StatusResp;BUSY auf High, ACK auf Low
	reti
	
;Warten in ganzen Millisekunden
;PE: R16 = Anzahl Millisekunden
Wait_ms:
	push	r16
;Energie sparen - mit sleep?
#if 1
wms1:	 rcall	Idle
	 in	r16,TIFR
	 jbrc	r16,1,wms1	;Warte auf Output-Compare-Interrupt
	 outi	TIFR,1<<1	;Interruptanforderung löschen
#else
	push	r17
	 ldi	r17,0x0D
wms1:	 rcall	HostRead	;SOF-Interrupt
	 jbrc	r16,4,wms1
	 ldi	r16,1<<4	;SOF-Bit löschen
	 rcall	HostWrite
	pop	r17
#endif
	pop	r16
	djnz	r16,Wait_ms
	ret
	
;=============================
;== EzHost SL811-spezifisch ==
;=============================

ClearInterrupts:
;Häufig benutzter Ausgabebefehl
;PE: -
;VR: R16,R17
	ldi	r17,0x0D
	ldi	r16,0xFF
HostWrite:
;Schreiben zum Host-Controller, Signalverlauf:
;    Daten schreiben         Daten lesen
;    __ __________ ______    __ __________    ___    __
;D   __X____A_____X__D___    __X____A_____>--<_D_>--<__
;    ____        ________    ____        ______________
;A0      \______/                \______/              
;    ______    _____    _    ______    ________________
;WR        \__/     \__/           \__/          
;    ____________________    _______________        ___
;RD                                         \______/ 
;PE: R17=Adresse
;    R16=Daten
;VR: -
;Ta: 18 (~ 2 µs)
	out	PortA,r17	;(1)
	cbi	PortD,A0	;(2)
	cbi	PortD,WR	;(2)
	sbi	PortD,WR	;(2)
	sbi	PortD,A0	;(2)
	out	PortA,r16	;(1)
;SL811DataWriteCycle:
	cbi	PortD,WR	;(2)
	sbi	PortD,WR	;(2)
	ret			;(4)
	
HostRead:
;Lesen vom Host-Controller, Signalverlauf siehe oben
;PE: R17 = Adresse
;PA: R16 = Daten
;VR: R16
;Ta: 21 (~ 2 µs)
	out	PortA,r17	;(1)
	cbi	PortD,A0	;(2)
	cbi	PortD,WR	;(2)
	sbi	PortD,WR	;(2)
	sbi	PortD,A0	;(2)
	out	DDRA, ZERO	;(1) auf Eingabe schalten
	cbi	PortD,RD	;(2)
	nop			;(1)
	nop
	in	r16,  PinA	;(1)
	sbi	PortD,RD	;(2)
	out	DDRA, ONES	;(1) zurück auf Ausgabe schalten
	ret			;(4)

.macro SL811WriteK	;Adresse, Datenbyte (beides Konstanten)
	ldi	r17,@0
	ldi	r16,@1
	rcall	HostWrite
.endm
.macro SL811Write	;Adresse (Konstante), Datenbyte in R16
	ldi	r17,@0
	rcall	HostWrite
.endm
.macro SL811Read	;Adresse (Konstante), Ergebnis in R16
	ldi	r17,@0
	rcall	HostRead
.endm
.macro EZUSB_Delay	;Millisekunden (Konstante)
	ldi	r16,@0
	rcall	Wait_ms
.endm

;Blocktransfer vom Host zum Speicher:
;PE: X=Speicheradresse, R17=SL811-Startadresse, R18=Transferlänge
;VR: X+=R18, R16, R17+=R18
HostInS:
	tst	r18
	breq	hi_e
	push	r18
hi_l:	 rcall	HostRead	;(25)
	 st	X+,r16		;(2)
	 inc	r17		;(1)
	 djnz	r18,hi_l	;(3) -> 31 Takte pro Byte (= 2,6 µs)
	pop	r18
hi_e:	ret

;Blocktransfer vom Speicher zum Host:
;PE: X=Speicheradresse, R17=SL811-Startadresse, R18=Transferlänge
;VR: X+=R18, R16, R17+=R18
HostOutS:
	tst	r18
	breq	ho_e
	push	r18
ho_l:	 ld	r16,X+		;(2)
	 rcall	HostWrite	;(22)
	 inc	r17		;(1)
	 djnz	r18,ho_l	;(3) -> 28 Tykte pro Byte (= 2,3 µs)
	pop	r18
ho_e:	ret

UsbReset:
;Rücksetz-Zustand auf dem USB für 50 ms halten
	SL811Read 0x05
	sbr	r16,0b00001000
	rcall	HostWrite
	push	r16
	 EZUSB_Delay 50
	pop	r16
	cbr	r16,0b00001000
	rcall	HostWrite
	rjmp	ClearInterrupts

SL811_Init:
;Initialisiert den Chip
	SL811WriteK 0x0F,0xAE
	SL811WriteK 0x0E,0xE0		;SOF-Zähler (1 ms)
	rjmp	ClearInterrupts

speed_detect:
;Aufruf nur mit EzBits=0
	cbi	PortD,LedG	;LED aufblitzen lassen
;hier: SL811 mit Datenzyklus "wachküssen", Suspend-Bit löschen
	SL811WriteK 0x0D,1<<6	;Bit 6 löschen
	rcall	HostRead	;R17 steht noch
	jbrc	r16,6,sd1	;Gerät entdeckt!
sd5:
	EZUSB_Delay 2
	sbi	PortD,LedG	;LED nach 2 ms ausschalten
;hier: SL811 Suspend-Bit setzen -> Tiefschlaf
	;SL811WriteK 0x05,0x40
	EZUSB_Delay 198		;LED 198 ms ausgeschaltet lassen
	ret
sd1:
;Zunächst sollte etwas gewartet und noch einmal Präsenz geprüft werden.
	EZUSB_Delay 10
;Bei wackeligem Zustand würde das Bit wieder 1 werden, das ist so gewollt!
	rcall	HostRead	;R17 steht noch
	jbrs	r16,6,sd5	;zurück wenn nicht sicher gesteckt!
#ifdef DEBUG
	push	r16
	 rcall	SendStrAfter
	 .db	"Attach ",0
	pop	r16
#endif
;Gerät gefunden: erst mal Speed ermitteln
	sbrs	r16,7		;0=Low-Speed, 1=Full-Speed
	 sbr	EzBits,1<<LOW_SPEED
;Speed einstellen, USB-Reset ausführen
	ldi	r16,0xAE
	sbrc	EzBits,LOW_SPEED
	 ldi	r16,0xEE		;Low-Speed: D+ und D- tauschen
	SL811Write 0x0F
	ldi	r16,0b00001000		;Bus-Reset (SE0)
	sbrc	EzBits,LOW_SPEED
	 ldi	r16,0b00101000		;Low-Speed
	SL811Write 0x05
	EZUSB_Delay 50
	ldi	r16,0b00000001		;SOF-Freigabe
	sbrc	EzBits,LOW_SPEED
	 ldi	r16,0b00100001		;Low-Speed
	rcall	HostWrite		;R17 steht noch
	EZUSB_Delay 10
	;LED schalten?
	sbr	EzBits,1<<SLAVE_FOUND
	;cbr	EzBits,1<<SLAVE_ENUMERATED
;Braucht man das folgende überhaupt???
	SL811WriteK 0x00,0x01	;start generate SOF or EOP
	EZUSB_Delay 25		;Hub required approx. 24.1ms
;Ein Bus-Reset erzeugt ebenfalls Interrupts der Marke "Nonpresent Device"
	rcall	ClearInterrupts
#ifdef DEBUG
	jbrs	EzBits,LOW_SPEED,sd2
	rcall	SendStrAfter
	.db	"Full",0,0
	rjmp	sd3
sd2:	rcall	SendStrAfter
	.db	"Low",0
sd3:	rcall	SendStrAfter
	.db	"-Speed",0,0
#endif
	ret
	
#define PID_SETUP   0xD0
#define PID_IN	    0x90
#define PID_OUT	    0x10

;Ep-Struktur
;4 Bytes Endpoint-Nummern
;	7 Richtung (1 = IN)
;	6 DataToggle-Bit (nur bei Bulk)
;	5-4 Typ 00=control, 01=iso, 10=bulk, 11=int
;	3-0 Endpoint-Nummer 0..15
;4 Bytes Endpoint-Längen

;Dev-Struktur (bei Multifunktionsgeräten gibt's durchaus mehrere!
;1 Byte USB-Adresse; Bit7=Low-Speed-Bit
;3 Byte Klasse-Subklasse-Protokoll
;3 Byte Config-Interface-AltSetting
;1 Byte Anzahl Endpoints (außer EP0)
;(n+1)*2 Bytes Ep-Strukturen
// Anstelle von "typedef struct": Komponenten-Offsets
#define devUsbAddr 0	;Bit7=Preamble (Low-Speed), Bit6-0=USB-Adresse
#define devClass 1	;zur Auffindung der Geräte-Art
#define devSubclass 2
#define devProtocol 3
#define devConfiguration 4	;zum Setzen der Konfiguration
#define devInterface 5
#define devAltSetting 6
#define devNumEndpoints 7	;EP0 nicht mitzählend!
#define devStructSize 8
#define epNumber 0	;epNumber siehe unten!
#define epLength 1	;16-bit-FIFO-Größen kann der SL811 ohnehin nicht
#define epStructSize 2
;epNumber: Bitfeld:
;	7: Richtung (1 = IN) - nicht ausgewertet!
;	6: Togglebit (bei ISO nicht ausgewertet)
;	5-4: Transfertyp (0=CONTROL, 1=ISO, 2=BULK, 3=INT)
;	3-0: Endpoint-Nummer

#define DATA0START 0x10	;nur ein Hostsystem wird zz. verwendet

TogToggle:
;Toggle-Bit in EP-Struktur sowie R19 kippen
;PE: Z=Ep-Zeiger
;    R19=Kopie davon
;PA: epNumber sowie R19 geändert
;VR: R16,R17,R19
	ldi	r16,0b01000000	;Data0-Toggle
	ldd	r17,Z+epNumber
	eor	r17,r16
	std	Z+epNumber,r17	;Data0 toggeln
	eor	r19,r16
	ret

.def pid=r20

ux_PreOut:
;Vorbereitung für OUT- oder SETUP-Transfer
;PE: Z=Ep-Zeiger
;    X=Daten
;    r18=Datenlänge (Maximum aus FIFO-Größe und W=wDataLen)
;    r19=epNumber (umgearbeitet)
;    pid=r20
;PA: r16=0T0100111, T=Togglebit, X vorgerückt
;VR: R16,R17,X
;Wenn Daten vorhanden, dann in Puffer schreiben (immer bei EP0)
	ldi	r17,DATA0START	;Daten-Bereich
	rcall	HostOutS
	ldi	r16,0b00100111
	sbrc	r19,6		;Momentaner Data-Toggle-Wert
	 sbr	r16,0b01000000	;Data-Toggle einsetzen
	ret
	
ux_PostOut:
;bei erfolgreicher Ausführung eines OUT- oder SETUP-Transfers
;PE: R18=Geschriebene Bytes
;    R19=epNumber
;    Z=Ep-Zeiger
;PA: W=verbleibende Bytes
;    ZF=1 wenn W==0
;    Togglebit getoggelt (außer bei ISO)
;VR: R16,R17;R19(Togglebit)
	sbrs	r19,4		;Bei ISO nicht toggeln
	 rcall	TogToggle
	sub	WL,r18
	sbc	WH,ZERO		;ZF=1 wenn W=0 (Besonderheit von ATmega SBC!)
	ret

ux_PostIn:
;bei erfolgreicher Ausführung eines IN-Transfers (Toggle-Bit bereits geprüft)
;PE: R18=Zu lesende Bytes
;    R19=epNumber
;    Z=Ep-Zeiger
;    X=Datenpuffer
;PA: W=verbleibende Bytes
;    ZF=1 wenn W==0 (Puffer voll) oder verkürzt gelesen
;    X vorgerückt
;    Togglebit getoggelt (außer bei ISO)
;VR: R16,R17,R18;R19(Togglebit)
	SL811Read 0x04		;Verbliebener Puffer-Platz
	push	r16		;Bei Rest (r16!=0) ZF=1 liefern
	 sub	r18,r16		;Übertragene Bytes
	 brcc	ux9
#ifdef DEBUG
	 mov	r16,r18		;Zu viel Rest!
	 rcall	ohex
	 ldi	r16,'!'
	 rcall	putchar
#endif
	 clr	r18
ux9:	 ldi	r17,DATA0START	;Daten-Bereich
	 rcall	HostInS
	 rcall	ux_PostOut
	pop	r16
	breq	ux_e		;ZF=1 für Puffer voll
	cpi	r16,1		;Rest verblieben? (via CY!)
	brcs	ux_e		;Bei R16=0 ist CY=1 und ZF=0
	sez			;ansonsten Z setzen
ux_e:	ret

usbXfer:
;USB-Transfer (alle Arten), zz. wird nur die erste USB-Engine des SL811 genutzt
;Routine kümmert sich nicht um initiale Togglebits und blockiert bis Ende
;PE: Z=Dev-Zeiger
;    X=Daten-Zeiger (bei pid==PID_SETUP faktisch immer SetupDat)
;    W=Datenlänge (16 bit, bei pid==PID_SETUP immer 8)
;    r18=Endpoint-Index, 0=EP0, 1=EPx, bei pid==PID_SETUP immer 0)
;    r20=pid (PID_IN, PID_OUT oder PID_SETUP)
;PA: X=Daten-Zeiger vorgerückt
;    W=Datenlänge=0 oder Rest (bei verkürztem PID_IN, oder NAK)
;    CY=1 bei Fehler, dann R16=USB-Transferstatus (SL811-Register 03h)
;VR: R16-R17,W,X
;Im Innern:
;	R16 = Kommando oder Status
;	R18 = Teiltransferlänge
;	R19 = (etwaige) Kopie von epNumber
;	R21 = Wiederholungszähler
#ifdef DEBUG
	ldi	r16,'X'
	;rcall	putchar
#endif
	pushhl	Z
	push	r18
	push	r19
	push	r21
	 ldi	r21,50		;Wiederholungen für EP0 NAK und TimeOut
	 ldd	r16,Z+devUsbAddr
;Ep-Zeiger ermitteln
	 add	r18,r18		;*epStructSize
	 addi	r18,devStructSize
	 add	ZL,r18
	 adc	ZH,ZERO		;Z = Ep-Zeiger
;ISO-Modus extrahieren: nach R19.4
	 ldd	r19,Z+epNumber
	 sbrc	r19,5
	  cbr	r19,1<<4	;bei BULK oder INT das Bit4 löschen
;Preamble-Bit umkopieren
	 cbr	r19,1<<7	;DIR-Bit wird nicht benötigt!
	 sbrc	r16,7		;Ab jetzt R19.4=ISO-Bit, R19.3-0=Endpoint
	  sbr	r19,1<<7	;sowie R19.7=Preamble und R19.6=Togglebit
;USB-Adresse setzen
	 cbr	r16,1<<7	;Preamble-Bit ausschalten
	 SL811Write 0x04	;USB-Adresse setzen
uxOuterLoop:
;Teil-Transferlänge ermitteln
	 ldd	r18,Z+epLength
	 cp	WL,r18		;Wie schön, dass man CP verketten kann!
	 cpc	WH,ZERO
	 brcc	ux0		;Gesamtlänge >= FIFO-Länge
	 mov	r18,WL		;Gesamtlänge <  FIFO-Länge: einsetzen!
ux0:
;Hier müsste anhand der Ressourcen-Verfügbarkeit USB-A oder USB-B
;ausgewählt werden. Da müsste die USB-Adresse erst hier gesetzt werden?
;Ob der SL811 so "schlau" ist, die Reihenfolge einzuhalten??!!
	 mov	r16,r19
	 andi	r16,0b00001111	;EP
	 or	r16,pid
	 SL811Write 0x03	;Endpoint+PID setzen
	 ldi	r16,DATA0START
	 SL811Write 0x01	;Transferadresse setzen
	 mov	r16,r18
	 SL811Write 0x02	;Transferlänge setzen
;Je nach pid (r20) Kommandobyte ermitteln
	 ldi	r16,0b00100011	;svw. "ux_PreIn": nur "cmd" liefern
	 cpi	pid,PID_IN
	 breq	ux1
	  rcall	ux_PreOut	;OUT/SETUP vorbereiten sowie "cmd" liefern
ux1:
;SyncSOF könnte auch entfernt werden, wenn genug Zeit für Bulktransfer bleibt
;Bei Wiederholungen ist's allerdings Quatsch!
#ifdef FULL_FRAME
	 jbrs	EzBits,LOW_SPEED,ux7
	 cpi	pid,PID_SETUP
	 breq	ux8		;hier: Setup-Transfers immer hinter SOF
	 cpi	r21,50		;Eine Wiederholung?
	 brne	ux8		;SyncSOF stehen lassen!
;if (SL811Read(0x0F)>=((r18+8?)*10)>>6) r16&=~0x20;
;Ohne diese Prüfung kann es zu "Babble" kommen.
;Bei Verwendung beider USB-Einheiten muss der andere Kanal eingerechnet werden!
	 mov	r17,r18
	 addi	r17,8		;fraglich!
	 pushw	r1,r0
	 push	r16
	  ldi	r16,10*4
	  mul	r16,r17
	  SL811Read 0x0F	;R16 = Rest-Zeit
	  cp	r16,r1
	 pop	r16
	 popw	r1,r0
	 brcs	ux8		;Nicht genügend Zeit: Bit stehen lassen
ux7:	 cbr	r16,0b00100000	;SyncSOF-Bit entfernen
ux8:
#else
	 sbrc	EzBits,LOW_SPEED
	  cbr	r16,0b00100000	;SyncSOF-Bit entfernen
#endif	 
	 sbrc	r19,7		;Low-Speed-Gerät (hinter HUB)?
	  sbr	r16,0b10000000	;Preamble-Bit setzen
	 sbrc	r19,4		;ISO?
	  sbr	r16,0b00010000	;ISO-Bit setzen
;Für Low-Speed muss man hier alle Interrupts löschen,
;sonst gibt's (zumindest) einen Disconnect-Interrupt. Oder SE0/Idle??
	 push	r16
	  rcall	ClearInterrupts
	 pop	r16
	 SL811Write 0x00	;Los geht's! Start-Kommando ausgeben
uxInnerLoop:
;Alle Warteschleifen werden mit einem Aufruf von "Idle" garniert,
;um die parallele Schnittstelle zu bedienen.
	 rcall	Idle
	 SL811Read 0x0D
	 mov	r17,r16
	 andi	r17,0b01100000	;Resume oder Remove
	 brne	uxe0		;raus mit Fehler!
	 jbrc	r16,0,uxInnerLoop	;in Schleife bleiben bis DONE USB-A
	 SL811WriteK 0x0D,0x01	;Diesen Interrupt löschen
	 SL811Read 0x03		;Status lesen
	 jbrc	r16,0,no_ack
;ACK auswerten (der "Glücksfall")
	 cpi	pid,PID_IN
	 brne	uxAckOut	;bei OUT: nächste Runde?
;Prüfen des Togglebits (nicht bei ISO)
;Das Togglebit bei IN-Transfers ist ohnehin fehlerhaft beim SL811 implementiert!
;Wie soll man den "falschen" IN-Transfer ignorieren können?
;(Lt. USB-Spec. darf der SL811 kein ACK senden, wenn das Togglebit falsch ist.)
	 jbrs	r19,4,uxAckIn	;ISO: Toggle-Bit ignorieren
	 swap	r16
	 lsr	r16		;Sequence-Bit auf Bit6 bringen
	 eor	r16,r19		;Erwartetes Togglebit einsetzen
	 jbrs	r16,6,uxe	;muss Null sein, sonst Fehler (r16=??)
uxAckIn:			;Problem: Host hat bereits geACKt.
	 rcall	ux_PostIn	;Daten aus SL811 abholen
	 rjmp	ux4		;mit ZF=1 wenn fertig
uxAckOut:
	 rcall	ux_PostOut
ux4:	 brne	uxol		;weitere Runde wenn nicht vollständig versendet
ux5:	 clc			;alles OK
	 rjmp	uxp
uxe0:	 ldi	r16,1<<4	;Bit4: Gerät entfernt
uxe:				;Fehlermeldung (CY=1) - USB-Baum rücksetzen!?
#ifdef DEBUG
	push	r16
	 ldi	r16,'<'
	 rcall	putchar
	pop	r16
	push	r16
	 rcall	ohex
	 ldi	r16,'>'
	 rcall	putchar
	pop	r16
#endif
	sec
uxp:	pop	r21
	pop	r19
	pop	r18
	pophl	Z
	ret
no_ack:
	 cpi	pid,PID_IN
	 breq	uxi
;Zur Wiederholung Datenzeiger zurück
;(alternativ könnte man das erneute Laden unterdrücken...)
	 sub	XL,r18
	 sbc	XH,ZERO
;Bei EP0 Wiederholungen versuchen (sonst nicht)
uxi:	 mov	r17,r19
	 andi	r17,0b00001111	;EP0?
	 brne	uxe		;nein, raus mit Fehler
	 jbrs	r16,6,uxo	;NAK? Wiederholung!
	 jbrc	r16,2,uxe	;Kein TimeOut? Fehler!
uxo:	 dec	r21		;6 Versuche
	 breq	uxe		;Fehler
#ifdef DEBUG
	 cpi	r21,1		;Letzte Wiederholung?
	 brne	uxol
	 ldi	r16,'r'
	 rcall	putchar		;anzeigen!
#endif
uxol:	 rjmp	uxOuterLoop

ep0Xfer:
;PE: X=Datenpuffer
;    SetupDat-Puffer gefüllt
;    Z=Dev-Zeiger
;PA: W=Residuum (tmp.)
;VR: R16-R19,W
	push	r20
	pushhl	X
	 std	Z+devStructSize+epNumber,ZERO	;Data-Toggle stets 0
	 clr	r18		;EP0
	 pushhl	X
	  ldihl	X,SetupDat
	  ld	r19,X		;bmRequestType
	  andi	r19,0x80	;Bit7=Richtungsbit, 1=IN
	  ori	r19,PID_OUT	;künftige PID ermitteln
	  ldihl	W,8
	  ldi	pid,PID_SETUP
	  rcall	usbXfer
	  ld	WH,-X		;wLengthH
	  ld	WL,-X		;wLengthL
	 pophl	X
	 brcs	e0e		;Raus bei Fehler
	 mov	r16,WL
	 or	r16,WH
	 ldi	pid,PID_IN	;Richtung für Status immer IN, wenn keine Daten
	 breq	e0z
	 mov	pid,r19		;PID_OUT oder PID_IN, je nachdem
	 rcall	usbXfer
	 brcs	e0e		;Raus bei Fehler
	 ldi	pid,PID_OUT^PID_IN	;80h
	 eor	pid,r19		;Richtung für Status
e0z:	 pushhl	W
	  ldihl	W,0		;keine Daten
	  ldi	r16,0b01000000	;Data-Toggle stets 1
	  std	Z+devStructSize+epNumber,r16
	  rcall	usbXfer
	 pophl	W
e0e:
#ifdef DEBUG
	push	r16
	 ldi	r16,'e'
	 brcc	eee
	 ldi	r16,'f'
eee:	 ;rcall	putchar
	pop	r16
#endif
	pophl	X
	pop	r20
	ret
.undef pid

ep0Xfer5:	;5x Wiederholung (eigentlich für STALL oder Sequence)
	push	r20
	 ldi	r20,5
ex2:	 rcall	ep0Xfer
	 brcc	ex1
	 dec	r20
	 sbrs	r16,4		;Abgezogen? Nicht wiederholen!
	  brne	ex2
ex1:	pop	r20
#ifdef DEBUG
	brcc	ex3
	mov	r18,r16		;Fehlerkode retten
	pushhl	Z
	 rcall	SendStrAfter
	 .db	"EP0-Transfer",0,0
	 ldi	YL,SetupDat
	 ldi	r17,8
ex4:	 rcall	ospac
	 ld	r16,Y+
	 rcall	ohex		;SetupDat-Puffer ausgeben
	 djnz	r17,ex4
	 rcall	SendStrAfter
	 .db	" Fehler Kode ",0
	 mov	r16,r18
	 rcall	ohex
	 rcall	ocrlf
	pophl	Z
	mov	r16,r18
ex3:
#endif
	ret

;====================
;== USB-spezifisch ==
;====================

OutDeviceRequest:
	clr	r16
	clr	r19
SimpleDeviceRequest:
;Einfache (datenlose) Requests ausführen
;PE: R16=bmRequestType
;    R17=bRequest
;    R18=wValueL
;    R19=wIndexL
	ldi	YL,SetupDat
	st	Y+,r16		;bmRequestType
	st	Y+,r17		;bRequest
	st	Y+,r18		;wValueL = Adresse
	clr	r16
	st	Y+,r16		;wValueH = 0
	st	Y+,r19		;wIndexL
	st	Y+,r16		;wIndexH
	st	Y+,r16		;wLengthL
	st	Y+,r16		;wLengthH
	rjmp	ep0Xfer5	;X ist ohne Daten egal

SetAddress:
;Adresse des USB-Gerätes setzen (es hat zurzeit die USB-Adresse 0)
;PE: R18=Adresse (1..127)
;    Z=Dev-Zeiger
;PA: Z->devUsbAddr modifiziert
;VR: X,R16-R20
	push	r18
	 ldi	r17,5		;bRequest = SET_ADDRESS
	 rcall	OutDeviceRequest
	pop	r18
	brcs	sae		;raus bei Fehler
	ldd	r17,Z+devUsbAddr
	or	r17,r18		;Low-Speed-Bit stehen lassen
	std	Z+devUsbAddr,r17
	EZUSB_Delay 2
sae:	ret

GetDesc:
;Beschreiber beschaffen
;PE: R19=Beschreiber-Typ, 1=Gerät(18 Bytes), 2=Konfiguration (lang)
;    R18=String-Nummer (bei String)
;    W=Anzahl erwartete Bytes
;    Z=Dev-Zeiger
;PA: CY=1: Fehler
;    X=Descriptor (Puffer und Zeiger bereit zum Parsen)
;    W=Anzahl freigebliebener Bytes
;VR: R16-R20,X
	ldi	r16,0x80	;bmRequestType
	ldi	r17,0x06	;bRequest
	ldi	r20,0		;wIndexL
	ldi	r21,0		;wIndexH
GetD2:	ldihl	X,Sektor
GetD3:	ldi	YL,SetupDat
	st	Y+,r16		;bmRequestType
	st	Y+,r17		;bRequest
	st	Y+,r18
	st	Y+,r19
	st	Y+,r20
	st	Y+,r21
	st	Y+,WL
	st	Y+,WH
	rcall	ep0Xfer5
	ret

ParseEp:
;Endpoint-Beschreiber (7 Bytes) parsen
;PE: X=Zeiger auf Endpoint-Descriptor
;    Z=Ep-Zeiger (Speicherbereich mit Null initialisiert)
;PA: X um 7 Bytes vorgerückt wenn OK (ZF=1)
;    ZF=0 wenn kein Endpoint-Beschreiber
;VR: R16,R17,X
	ld	r16,X+		;bLength müsste 7 sein
	ld	r17,X+		;bDescriptorType
	cpi	r17,5		;"Endpoint"?
	brne	pepe		;Ende mit Parsen
	ld	r16,X+		;bEndpointAddress (Bit7=IN)
	ld	r17,X+		;bmAttributes (ISO=1, BULK=2, INT=3)
	swap	r17
	or	r16,r17
	std	Z+epNumber,r16	;mit Togglebit=0
	ld	r16,X+		;wMaxPacketSizeL
	ld	r17,X+		;wMaxPacketSizeH
	tst	r17
	brne	limit
	cpi	r16,240		;absolut maximale Transfergröße für EzHost
	brcs	nolimit
limit:	ldi	r16,240
nolimit:std	Z+epLength,r16
	ld	r16,X+		;bInterval ignorieren
	sez
pepe:	ret

EnumUsbDev:
;PE: DevDat gelöscht
	rcall	UsbReset	;mit 2. Reset wird beim Stick aus TimeOut ein CRC-Fehler
	EZUSB_Delay 50
#ifdef DEBUG
	rcall	SendStrAfter
	.db	", Enum ",0
#endif
	clr	r2		;USB-Adressen-Zähler
	ldihl	Z,DevDat
NextAddress:	;Umsonstmarke, zz. keine Hub-Unterstützung
	inc	r2		;Beginnend mit Adresse 1
;Wäre es nicht richtiger, erst mal eine Adresse zu setzen??
	;ldi	r16,64		;Vorgabegröße für EP0, Full-Speed
	;sbrc	EzBits,LOW_SPEED
	; ldi	r16,8		;Vorgabegröße für EP0, Low-Speed
	stdi	Z+devStructSize+epLength,64	;Vorgabegröße für EP0
;Low-Speed-Geräte hinter HUBs liefern ebenfalls kurze EP0-Datenblöcke!
	clr	r3		;Konfigurations-Zähler
;Gerätebeschreiber lesen
	clr	r18
	ldi	r19,1		;DEVICE
	ldihl	W,8
	rcall	GetDesc		;Beschreiber beschaffen (erste 8 Bytes)
	brcs	eue		;Fehler!
#ifdef DEBUG
	ldi	r16,'d'
	rcall	putchar
#endif
	adiw	XH:XL,4
	ld	r16,X+
	std	Z+devClass,r16
	ld	r16,X+
	std	Z+devSubclass,r16
	ld	r16,X+
	std	Z+devProtocol,r16
	ld	r16,X+		;EP0-Größe setzen
	std	Z+devStructSize+epLength,r16

	mov	r18,r2
	rcall	SetAddress
	brcs	eue
#ifdef DEBUG
	ldi	r16,'a'
	rcall	putchar
#endif
;Gerätebeschreiber lesen (noch einmal, für NumConfigurations)
	clr	r18
	ldi	r19,1		;DEVICE
	ldihl	W,18
	rcall	GetDesc		;Beschreiber beschaffen
	brcs	eue		;Fehler!
#ifdef DEBUG
	ldi	r16,'d'
	rcall	putchar
#endif
	 
;Gerätebeschreiber parsen (nur die "interessanten" Felder)
	adiw	XH:XL,17
	ld	r4,X		;bNumConfigurations (normal: 1)
NextConfiguration:
;Konfigurationsbeschreiber lesen (in einem Rutsch)
	mov	r18,r3
	ldi	r19,2		;CONFIGURATION
	ldihl	W,MAX_DESC	;Descriptor-Platz
	rcall	GetDesc
	brcs	eue
#ifdef DEBUG
	ldi	r16,'c'
	rcall	putchar
#endif
	ld	r16,X+
	cpi	r16,9
	brne	eue
	ld	r16,X+
	cpi	r16,2
	brne	eue
;Konfigurationsbeschreiber parsen
	clr	WL		;beim Start ist Z=gültiger Dev-Zeiger
	clr	WH
	adiw	XH:XL,2
	ld	r6,X+		;bNumInterfaces
	ld	r16,X+		;bConfigurationValue
	std	Z+devConfiguration,r16
	adiw	XH:XL,3		;iConfiguration,bmAttributes,MaxPower überspr.
	clr	r7
NextInterface:
	;Hier müsste ein Interface-Descriptor sein!
	ld	r16,X+		;bLength (=9)
	ld	r17,X+		;bDescriptorType
	cpi	r17,4		;"Interface"
	breq	pok
pee:	inc	r3		;Nächste Konfiguration (selten)
	djnz	r4,NextConfiguration
	rcall	ospac
	clc			;Ende mit Parsen
	ret
eue:	rcall	SendStrAfter
	.db	"Fehler!",0
	rcall	ocrlf
	sec
	ret
pok:	mov	r16,WL
	or	r16,WH
	breq	ncop		;Das erste Mal ist W=0
	pushhl	X		;Bei jeder weiteren Runde Z=Dev-Zeiger machen
	pushhl	Z
	movwhl	X,W		;Quelle
	ldi	r16,devStructSize+epStructSize
cop:	ld	r17,X+	;Eigentlich braucht man nur die Adresse und FIFO-Größe kopieren
	st	Z+,r17
	djnz	r16,cop
	pophl	Z
	pophl	X
ncop:	movwhl	W,Z		;retten zur Kopie
	ld	r16,X+		;bInterfaceNumber
	std	Z+devInterface,r16
	ld	r16,X+		;bAlternateSetting
	std	Z+devAltSetting,r16
	ld	r5,X+		;bNumEndpoints
	std	Z+devNumEndpoints,r5
	ld	r16,X+		;bInterfaceClass
	tst	r16
	breq	pcl
	std	Z+devClass,r16
pcl:	ld	r16,X+		;bInterfaceSubclass
	tst	r16
	breq	psc
	std	Z+devSubclass,r16
psc:	ld	r16,X+		;bInterfaceProtocol
	tst	r16
	breq	ppr
	std	Z+devProtocol,r16
ppr:	ld	r16,X+		;iInterface (ignorieren)
	;Hier müssten jetzt <r5> Endpoint-Descriptoren folgen
	adiw	ZH:ZL,devStructSize+epStructSize	;hinter EP0
	tst	r5
	breq	pq
NextEndpoint:
	rcall	ParseEp
	brne	pee
	adiw	ZH:ZL,2		;nächster Endpoint
	djnz	r5,NextEndpoint
pq:	rjmp	NextInterface	;nächstes Interface (Multifunktion oder AltSets)

;=========================================
;== Gelesene USB-Strukturen durchkämmen ==
;=========================================

;LocateDevice: Gerät in den Device-Daten suchen
;Sucht die erste Struktur mit devClass=R17, devSubclass=R18 und devProtocol=R19
;PE: Z=DevDat
;PA: CY=0: Z=Dev-Zeiger auf Drucker-Interface, sonst CY=1
;VR: R16,Z
lp_n:	ldd	r16,Z+devNumEndpoints
	add	r16,r16		;*epStructSize
	addi	r16,devStructSize+epStructSize
	add	ZL,r16
	adc	ZH,ZERO
LocateDevice:
	ldd	r16,Z+devUsbAddr
	cpi	r16,1
	brcs	lp_e		;Ende der Liste erreicht
	ldd	r16,Z+devClass
	cp	r16,r17
	brne	lp_n
	ldd	r16,Z+devSubclass
	cp	r16,r18
	brne	lp_n
	ldd	r16,Z+devProtocol
	cp	r16,r19
	brne	lp_n
lp_e:	ret
	
LocateEndpoint:
;Sucht Endpoint nach Richtung und Typ
;PE: R19=Richtung und Typ (Bit 7 sowie 5-4), Z=DevDat
;PA: CY=0: R18=Endpoint-Index, sonst CY=1
;VR: Z,r16
// BYTE NumEp = Dev->NumEndpoints;
// BYTE i;
// EpStruct *Ep = Dev->Endpoints;
// for (i=0; i != NumEp; ) {
//  i++; Ep++;
//  if (Ep->Number&0xB0 == 0x20) {	// Bulk Out
//   EpOutIdx=i;
//   goto raus;
//  }
// }
// goto Weitersuchen;
//raus:
	ldd	r16,Z+devNumEndpoints
	adiw	ZH:ZL,devStructSize
	clr	r18		;Ep-Index-Zähler
	rjmp	le_f		;am Fuß anfangen
le_l:	inc 	r18
	adiw	ZH:ZL,epStructSize
	ldd	r17,Z+epNumber
	eor	r17,r19		;muss in Richtung und Typ stimmen
	andi	r17,0b10110000	;Nummer und Togglebit ausblenden
	breq	le_e
le_f:	cp	r16,r18
	brne	le_l		;mit CY=0
	clr	r18
	sec
le_e:	ret
	
LocateEndpoints:
;OUT- und IN-Endpoint suchen (IN "optional")
;PE: Z=Dev-Zeiger
;PA: CY=1: Kein OUT-Endpoint
;    [EpOutIdx]=Out-Index (meistens 1)
;    [EpInIdx] =In-Index (meistens 2, 0 wenn nicht vorhanden)
;OUT-Endpoint suchen
	movwhl	W,Z
	ldi	r19,0b00100000	;BULK OUT
	rcall	LocateEndpoint
	sts	EpOutIdx,r18	;Index speichern
	brcs	les_e
#ifdef DEBUG
	ldd	r18,Z+epLength
	rcall	SendStrAfter
	.db	", Bulk-Out(",0
	mov	r16,r18
	rcall	SendByte
	ldi	r16,')'
	rcall	putchar
#endif
;IN-Endpoint suchen
	movwhl	Z,W
	ldi	r19,0b10100000	;BULK IN
	rcall	LocateEndpoint
	sts	EpInIdx,r18
	brcs	les_i		;wenn nicht vorhanden, ignorieren
#ifdef DEBUG
	ldd	r18,Z+epLength
	rcall	SendStrAfter
	.db	"+Bulk-In(",0
	mov	r16,r18
	rcall	SendByte
	ldi	r16,')'
	rcall	putchar
#endif
les_i:
#ifdef DEBUG
	rcall	SendStrAfter
	.db	" entdeckt",0
#endif
	movwhl	Z,W
	clc
les_e:	ret

SetConfig_AltSetting:
;Setzt Konfiguration (aktiviert Gerät) und wählt alternative Einstellung
;PE: Z=Dev-Zeiger
	ldi	r17,9		;Set_Configuration
	ldd	r18,Z+devConfiguration
	rcall	OutDeviceRequest
	brcs	sca_e
	ldi	r16,1		;bmRequestType=OUT,Interface
	ldi	r17,11		;Set_Interface (Alt.Setting)
	ldd	r18,Z+devAltSetting
	ldd	r19,Z+devInterface
	rcall	SimpleDeviceRequest
	brcc	sca_e
	ldd	r18,Z+devAltSetting	;Bei AltSetting=0 Fehler ignorieren
	cp	ZERO,r18		;Bei AltSetting<>0 Fehler melden
#ifdef DEBUG
	brcs	sca_e
	movwhl	W,Z
	rcall	SendStrAfter
	.db	", aktiviert",0
	movwhl	Z,W
	clc
#endif
sca_e:	ret

DeviceRequest_Reset:
;Gemeinsames Rücksetzen für Massenspeicher und Drucker
;PE: R17=bRequest (einzig unterschiedlich)
;PA: Fehler kann offenbar ignoriert werden
	ldi	r16,0b00100001	;bmRequestType
	clr	r18
	ldd	r19,Z+devInterface
	rcall	SimpleDeviceRequest
#ifdef DEBUG
	brcs	sp_j		;Reset-Fehler ignorieren
	movwhl	W,Z
	rcall	SendStrAfter
	.db	", r�ckgesetzt",0
	movwhl	Z,W
sp_j:
#endif
	ret

#ifdef DEBUG
SendStr_gefunden:
	rcall	SendStrAfter
	.db	" gefunden",0
	ret
#endif	

ScanKnownDevices:
;Sucht in den Strukturen die erste mit devClass=7
;PE: Z=DevDat
;PA: Z=Dev-Zeiger auf Drucker-Interface (insbes. bei Multifunktionsgeräten)
;    PRINTER_FOUND gesetzt
;    kein PRINTER_FOUND wenn nicht gefunden
;VR: R16,Z
	ldi	r17,7		;Class=7
	ldi	r18,1		;Subclass=1
	ldi	r19,2		;Protocol=2 bevorzugen
	movwhl	W,Z		;Anfang retten
	rcall	LocateDevice
	brcc	InitPrinter
	movwhl	Z,W		;Anfang herstellen
	ldi	r19,1		;Protocol=1 alternativ
	rcall	LocateDevice
	brcc	InitPrinter
	movwhl	Z,W
	ldi	r17,8		;Class=8
	ldi	r18,6		;Subclass=6 (Kamera QV2800UX: 5)
	ldi	r19,0x50	;Protocol=0x50: Bulk-Only Transport (QV2800: 0)
	rcall	LocateDevice
	brcc	InitMassStorage
	rcall	SendStrAfter
	.db	"Kein Drucker!",0	;Meldung
	rjmp	ocrlf
	
InitPrinter:
;PE: Z=DevDat-Zeiger auf geeignetstes (möglichst bidirektionales)
;      Drucker-Interface
#ifdef DEBUG
	movwhl	W,Z		;retten
	rcall	SendStrAfter
	.db	"Drucker",0
	rcall	SendStr_gefunden
	movwhl	Z,W
#endif
	rcall	LocateEndpoints
	brcs	sp_e
	stshl	DevPrinter,Z
	rcall	SetConfig_AltSetting
	brcs	sp_e
	ldi	r17,2		;bRequest
	rcall	DeviceRequest_Reset
	sbr	EzBits,(1<<PRINTER_FOUND)|(1<<BUFFER_EMPTY)|(1<<BUFFER_FULL)
	ldihl	X,Sektor	;Zeiger initialisieren
	movwhl	LptPtrRd,X
	movwhl	LptPtrWr,X
	outi	DDRC,0b11111000	;Statusleitungen Ausgänge!
	cbi	PortC,7		;BSY low: bereit zum Datenempfang
	rcall	UpdateStatus
	rcall	Read1284DevId
	cbr	EzBits,1<<BUFFER_FULL
sp_ok:
#ifdef DEBUG
	rcall	SendStrAfter
	.db	", OK.",0
	rcall	ocrlf
#endif
	ret
sp_e:
#ifdef DEBUG
	rcall	SendStrAfter
	.db	" - Fehler!",0,0
	rcall	ocrlf
#endif
	ret

InitMassStorage:
#ifdef DEBUG
	movwhl	W,Z		;retten
	rcall	SendStrAfter
	.db	"Massenspeicher",0,0
	rcall	SendStr_gefunden
	movwhl	Z,W
#endif
	rcall	LocateEndpoints
	brcs	sp_e
	lds	r16,EpInIdx
	cpi	r16,1
	brcs	sp_e		;Fehler, wenn kein IN-Endpoint vorhanden
	stshl	DevPrinter,Z
	ldi	r17,0b11111111	;bRequest
	rcall	DeviceRequest_Reset
;LUN interessiert hier nicht. Seriennummer evtl.
	sbr	EzBits,1<<USBMEM_FOUND
	ldi	YL,gCBW
	ldihl	Z,gDefaultCBW
	ldi	r17,15
ims_1:	lpm	r16,Z+
	st	Y+,r16
	djnz	r17,ims_1
	rjmp	sp_ok		;OK ausgeben
	
gDefaultCBW:
	.dd	0x43425355	;Signatur
	.dd	0x08154711	;Tag (beliebig)
	.dd	0x00000200	;1 Sektor
	.db	0x80,0		;Flags (IN), LUN
	.db	0x10,0		;Länge des CB, Füllbyte

DoEnumSlave:
;Versuch der Enumeration; Aufruf mit SLAVE_FOUND=0
	rcall	speed_detect
	sbrs	EzBits,SLAVE_FOUND
	 ret
	ldihl	Z,DevDat+MAX_DEVDAT
	ldihl	W,MAX_DEVDAT
loesch:	st	-Z,ZERO
	sbiw	WH:WL,1
	brne	loesch
	rcall	EnumUsbDev	;mit gelöschtem DevDat-Puffer
	brcs	des1
#ifdef DEBUG
	ldihl	Z,DevDat
	rcall	ShowDevDat
#endif
	ldihl	Z,DevDat
	rcall	ScanKnownDevices	;Ersten Drucker lokalisieren
	sbrc	EzBits,PRINTER_FOUND
	 rjmp	ShowPrinterDevDat
	sbrs	EzBits,USBMEM_FOUND
des1:	sbi	PortD,LedG	;LED aus bei Enum-Fehler (vorerst)
	ret
	
;=============================
;== (Hyper)Terminal-Anzeige ==
;=============================

HideDevId:
;Löschen der 1284-ID vom Bildschirm; LeaveScroll vorausgesetzt!
	ldi	r16,13
	ldi	r17,4
	rcall	GotoXY
	rcall	SendLineDel	;Von da bis Zeilenende löschen
	rcall	hd_1		;Zwei weitere Zeilen löschen
hd_1:	rcall	ocrlf
	rjmp	SendLineDel

ShowDevId:
;1284-Geräte-ID anzeigen
;Voraussetzung: HAVE_1284_ID gesetzt, kein LeaveScroll!
	rcall	LeaveScroll
	ldi	r17,UPPERCOLOR^0x0C
	rcall	SendColor
	rcall	HideDevId	;Ggf. alte Anzeige weglöschen
	ldi	r16,13
	ldi	r17,4
	rcall	GotoXY
	ldihl	X,Sektor
	ld	WH,X+
	ld	WL,X+
	sbiw	WH:WL,2	
rd_l:	ld	r16,X+		;String-Ausgabe
	rcall	putchar
	sbiw	WH:WL,1
	brne	rd_l
	rjmp	EnterScroll

CheckSlaveDetach:
;Prüfen, ob Verbindung besteht
	SL811Read 0x0D		;Interruptstatusregister
	sbrs	r16,6
	 ret			;0: Gerät hängt noch dran!
;hier: SL811 Suspend-Bit setzen -> Tiefschlaf
	;SL811WriteK 0x05,0x40	funktioniert nicht
	rcall	SL811_Init	;wieder auf Full-Speed
;Sonst wird nach dem Anstecken der Maus kein Full-Speed-Gerät erkannt
#ifdef DEBUG
	rcall	SendStrAfter
	.db	" Detach",0
	rcall	ocrlf
#endif
	jbrc	EzBits,PRINTER_FOUND,csd1
	rcall	LeaveScroll	;Angezeigten Deskriptor löschen
	ldi	r17,UPPERCOLOR^0x0C
	rcall	SendColor
	ldi	r16,12
	ldi	r17,3
	rcall	GotoXY
	rcall	SendLineDel	;Löschen vom Kursor zum Zeilenende
	rcall	HideDevId
	rcall	EnterScroll
csd1:	clr	EzBits
	;hier BUSY-Bit setzen
	out	DDRC, ZERO	;alles hochohmig machen (wie Stecker ziehen)
	out	PortC,ONES	;überall Pullups
	sbi	PortD,LedG	;LED aus
	out	DDRB, ZERO	;Eingänge (falls bidirektional)
	ret

UpdateStatus:
;Druckerstatus holen und an die drei Statuspins legen
	ldshl	Z,DevPrinter
	ldi	r16,0b10100001	;bmRequestType: klassenspezifisch
	ldi	r17,1		;bRequest = GET_PORT_STATUS
	ldi	r18,0		;wValueL
	ldi	r19,0		;wValueH
	ldd	r20,Z+devInterface;wIndexL
	ldi	r21,0		;wIndexH
	ldihl	W,1		;wLength
	ldihl	X,LptStatus	;Ziel-Puffer
	rcall	GetD3		;Status beschaffen
	brcs	use		;Fehler! (Was tun?)
	or	WL,WH		;Restlänge Null (1 Byte geliefert?)
	brne	use		;nein (Was tun?)
	ld	r16,X		;Da ist der Status
	andi	r16,0b00111000
	mov	r17,StatusResp
	andi	r17,0b11000111
	or	r17,r16
	mov	StatusResp,r17
;Umständliches Übertragen dreier Bits, ohne Interrupts sperren zu müssen:
	sbrc	r17,3
	 sbi	PortC,3
	sbrs	r17,3
	 cbi	PortC,3
	sbrc	r17,4
	 sbi	PortC,4
	sbrs	r17,4
	 cbi	PortC,4
	sbrc	r17,5
	 sbi	PortC,5
	sbrs	r17,5
	 cbi	PortC,5
use:	ret

ShowOneDevDat:
;Einen DevDat-Eintrag anzeigen
;PE: Z=DevDat-Zeiger
	ldd	r17,Z+devNumEndpoints
	lsl	r17		;*epStructSize
	addi	r17,devStructSize+epStructSize
sdd1:	rcall	ospac
	ld	r16,Z+
	rcall	ohex
	djnz	r17,sdd1
	ret

#ifdef DEBUG
sdd2:	rcall	ShowOneDevDat
ShowDevDat:
	rcall	ocrlf
	ldd	r16,Z+devUsbAddr
	tst	r16		;Null?
	brne	sdd2		;Eintrag gültig (enumeriert)
	ret
#endif

ShowPrinterDevDat:
	rcall	LeaveScroll	;Angezeigten Deskriptor löschen
	ldi	r17,UPPERCOLOR^0x0C
	rcall	SendColor
	ldi	r16,12
	ldi	r17,3
	rcall	GotoXY
	ldshl	Z,DevPrinter
	rcall	ShowOneDevDat
	rcall	EnterScroll
	ret
	
;=============================
;== Parallelport-spezifisch ==
;=============================

;Angekommenes Drucker-Byte zum Puffer durchreichen
;Voraussetzung des Aufrufs: PRINTER_FOUND, BSY=High
;VR: X
;Könnte eigentlich auch die ISR erledigen, oder?
;Ta: ca.36 (3µs), BSY-High-Zeit min. 18 (1,5µs)
HandleLptByte:
	sbrc	EzBits,BUFFER_FULL;(2)
	 ret			;    Puffer voll: Nichts tun!
	movwhl	X,LptPtrWr	;(1) Schreibzeiger
	st	X+,DataCatch	;(2) Byte abspeichern
	cbr	XH,0x04		;(1) Bei Pufferende "zurückklappen"
	sbr	XH,0x02		;(1)
	movwhl	LptPtrWr,X	;(1) abspeichern
	eor	XL,LptPtrRdL	;(1) Voll?
	eor	XH,LptPtrRdH	;(1)
	or	XH,XL		;(1)
	breq	hl1		;(1) Nicht voll
	cbi	PortC,BSY	;(2) BUSY auf Low, bereit für nächstes Byte
	sbi	PortC,ACK	;(2) /ACK auf High (löst im PC Interrupt aus)
	rjmp	hl2		;(2)
hl1:	sbr	EzBits,1<<BUFFER_FULL	;Voll ab jetzt
;(ab hier ist etwas Zeit für den PC, die ISR kann arbeiten)
hl2:	cbr	EzBits,(1<<BUFFER_EMPTY)|(1<<HAVE_1284_ID)
;Bytes zählen
	push	r0		;(2)
	 lds	r0,LptRecv	;(2)
	 inc	r0		;(1)
	 sts	LptRecv,r0	;(2)
	 brne	hl4		;(2)
	 lds	r0,LptRecv+1
	 inc	r0
	 sts	LptRecv+1,r0
	 brne	hl4
	 lds	r0,LptRecv+2
	 inc	r0
	 sts	LptRecv+2,r0
	 brne	hl4
	 lds	r0,LptRecv+3
	 inc	r0
	 sts	LptRecv+3,r0
hl4:	 lds	XL,Zyklus	;(2)
	 sbr	XL,1<<7		;(1)
	 sts	Zyklus,XL	;(2)
	pop	r0		;(2)
	jbis	PortC,BSY,HandleLptByte	;(3) Flinker PC? Kurzschluss!
	ret			;(4)

HandleUsbTransfer:
;Voraussetzung: PRINTER_FOUND gesetzt
	sbrc	EzBits,BUFFER_EMPTY
	 ret			;Nichts tun wenn leer
;Länge des anstehenden Transfers ermitteln (max. 40h = EP-Größe)
	movwhl	X,LptPtrRd	;Lesezeiger
	movwhl	W,LptPtrWr	;Schreibzeiger
	sub	WL,XL
	sbc	WH,XH		;Differenz = Länge, 0 entspricht 200h
	sbrs	EzBits,BUFFER_FULL
	 brcc	hu2
	ldihl	W,SektorE	;Bei CY=1 oder VOLL Länge bis Pufferende ansetzen
	sub	WL,XL		;Hier kann niemals 0 herauskommen!
	sbc	WH,XH
hu2:
;Begrenzung der Länge auf 40h (weil Senderoutine eh' blockiert)
#if 0
	mov	r16,WH
	rcall	ohex
	mov	r16,WL
	rcall	ohex
	ldi	r16,'#'
	rcall	putchar
#endif
	;tst	WH		;Achtung: ATmega's SBC "verkettet" ZF!
	;brne	hu3		;High-Teil=1 (256..511) oder=2 (512)
	cpi	WL,0x40		;eigentlich: FIFO-Größe benutzen!
	cpc	WH,ZERO
	brcc	hu3		;bei >=40h sofort senden
;Bei kleinen Mengen abwarten per TimeOut, nicht sofort senden
	lds	r16,LptTimeOut
	dec	r16
	breq	hu4		;Null: Transfer ausführen
	sts	LptTimeOut,r16
	ret			;Beim nächsten Mal...
hu3:	ldi	WL,0x40
	mov	WH,ZERO
hu4:	ldi	r16,LPTTIMEO
	sts	LptTimeOut,r16	;Timeout-Zähler auf Maximum
	;Übertragung des Datenblocks (NAK ist ebenfalls möglich! Und üblich.)
	ldshl	Z,DevPrinter
	lds	r18,EpOutIdx
	ldi	r20,PID_OUT
	push	WL
;hier eigentlich LED-Blinken anstoßen
	 sbi	PortD,LedG
	 rcall	UsbXfer		;hier kann sich per Yield LptPtrWr ändern!
	 cbi	PortD,LedG
	pop	WL
	brcs	hue		;NAK o.ä.
#if 0
	mov	r16,WL
	rcall	ohex
#endif
	cbr	XH,0x04		;vorgerücktes X ggf. auf Sektoranfang
	sbr	XH,0x02		;(2->2, 3->3, 4->2)
	movwhl	LptPtrRd,X
;Puffer nicht mehr voll
	jbrc	EzBits,BUFFER_FULL,hu6
	cbi	PortC,BSY	;nächstes Byte erwarten
	sbi	PortC,ACK	;PC-Interrupt auslösen
	cbr	EzBits,1<<BUFFER_FULL
hu6:
;Noch Bytes im Puffer? Wenn nein, BUFFER_EMPTY setzen
	eor	XL,LptPtrWrL
	eor	XH,LptPtrWrH
	or	XH,XL
	brne	hu7
	sbr	EzBits,1<<BUFFER_EMPTY
;Statistik aktualisieren (eigentlich bräuchte man noch Bytes/Sekunde)
hu7:	ldi	YL,LptSent
	ldi	r17,4		;DWORD
	clc
hul:	ld	r16,Y
	adc	r16,WL
	st	Y+,r16
	clr	WL
	djnz	r17,hul
SetZyklusBit7:
	ldi	r16,1<<7
SetZyklusBit:
	lds	r17,Zyklus
	or	r17,r16
	sts	Zyklus,r17
hue:	ret

;==========================
;== IEEE1284-Aushandlung ==
;==========================

Read1284DevId:
;1284-DevID lesen und anzeigen (kein Platz im RAM zum Zwischenpuffern)
;Voraussetzung: PRINTER_FOUND, BUFFER_EMPTY und(!) BUFFER_FULL gesetzt
;PA: CY=0: geglückt, und String besteht aus mindestens 1 Zeichen
;VR: R16-R20, Z - Benutzt Sektorpuffer!
	ldshl	Z,DevPrinter
	ldi	r16,0b10100001	;bmRequestType: klassenspezifisch
	ldi	r17,0		;bRequest = GET_DEVICE_ID
	ldd	r18,Z+devConfiguration	;wValueL
	dec	r18		;0-basiert (unsauber!!)
	ldi	r19,0		;wValueH
	ldd	r20,Z+devAltSetting;wIndexL
	ldd	r21,Z+devInterface;wIndexH
	ldihl	W,MAX_DESC	;wLength
	rcall	GetD2		;ID beschaffen
	brcs	rd_e
	ld	WH,X+		;Länge in Big-Endian am Anfang
	ld	WL,X+
	sbiw	WH:WL,3		;Diese Bytes zählen mit
	brcs	rd_e		;Länge zu kurz
	adiw	WH:WL,2
	cpi	WH,2
	brcc	rd_e		;Zu lang
	sbr	EzBits,1<<HAVE_1284_ID
	rcall	ShowDevId
	clc
	ret
rd_e:	sec
	ret

TwoNibbles:
;PE: R16=auszugebendes Byte (Host taktet mit AF, Device antwortet mit ACK)
;PA: CY=1: Zeitüberschreitung, oder Host beendet Modus mit SEL=L
;VR: Z,R17 (R16 wenn CY=1)
	rcall	Nibble
	brcs	hn_e
Nibble:
;Gibt das Low-Nibble von R16 aus
;PA: CY=1 wenn AF zu langsam ODER SEL=Low geht; R16 "geswapt"
	swap	r16		;Mit dem Low-Teil beginnen
	mov	r17,r16
	asr	r17		;Bit7 stehen lassen, übrige Bits 1 runter
	ori	r17,0b01000111	;ACK auf High, Pull-Ups belassen
;warten bis AF low wird, max. 10 ms
	ldihl	Z,CPUCLK/7/100	;10ms {7 = Takte pro Runde}
tn_w:	sbiw	ZH:ZL,1		;(2)
	jbic	PinC,SEL,tn_e	;(2)
	sbic	PinC,A_F	;(1)
	 brne	tn_w		;(2)
	breq	tn_e		;Zeit abgelaufen
;Jetzt ACK auf Low und Daten anlegen
	cbr	r17,1<<ACK
	out	PortC,r17	;Nibble ausgeben, ACK=LOW
;warten bis AF high wird, max. 10 ms
	ldihl	Z,CPUCLK/7/100	;10ms {7 = Takte pro Runde}
tn_v:	sbiw	ZH:ZL,1		;(2)
	jbic	PinC,SEL,tn_e	;(2)
	sbis	PinC,A_F	;(1)
	 brne	tn_v		;(2)
	breq	tn_e		;Zeit abgelaufen
;Jetzt ACK auf High legen
	ldi	r17,0b01010111	;Windows will es unbedingt SO haben!
	out	PortC,r17
	ret			;CY sollte 0 sein
tn_e:
#ifdef DEBUG
	in	r16,PinC	;Leitungszustand zur Fehlersuche ausgeben
	rcall	ohex
#endif
	sec
hn_e:	ret

HandleNegotiation:
;Voraussetzung: PRINTER_FOUND gesetzt
;Bei leerem Puffer kann die Device-ID per Nibble-Mode abgefragt werden
;Bei nicht-leerem Puffer kann kein IEEE1284-GeräteID-String beschafft werden.
;I.d.R. ist dies keine Einschränkung.
	sbrs	EzBits,BUFFER_EMPTY
	 ret			;nicht wenn Puffer gefüllt!
	in	r17,PinB	;Daten lesen (Extensibility Byte)
;Steuerleitungen prüfen
	in	r16,PinC
	andi	r16,7		;untere 3 Bits
	cpi	r16,0b110	;SEL=H, INI=H, AF=L?
	brne	hn_e
;Jetzt (schon) String einlesen, später ist die Zeit zu knapp!
;In der Regel ist auch diese Zeit zu knapp, aber Windows versucht es ja
;zweimal. Dann ist der String beim zweiten Versuch da.
	sbr	EzBits,1<<BUFFER_FULL	;Idle totlegen (auch: Zeichenausgabe!)
;Information per USB beschaffen (Idle ist totgelegt mit BUFFER_FULL)
	jbrs	EzBits,HAVE_1284_ID,hn_s
	rcall	Read1284DevId
	brcs	hn_e
;Statusleitungen setzen
hn_s:	push	StatusResp	;aktuellen Zustand retten
	 ldi	r16,0b10111111
	 mov	StatusResp,r16
	 ldi	r16,0b00111111
	 out	PortC,r16	;BSY+ACK low, alles andere high
;warten auf STB\-Interrupt (kein IDLE-Aufruf hier!) 10 ms lang
	 ldihl	Z,CPUCLK/7/100	;10ms {7 = Takte pro Runde}
hn_w:	 sbiw	ZH:ZL,1		;(2)
	 jbic	PinC,SEL,hn_x	;(2)
	 sbis	PortC,BSY	;(1) Interrupt gekommen?
	  brne	hn_w		;(2)
	 breq	hn_x		;kein Interrupt? Raus!
	 mov	r16,DataCatch
	 cp	r16,r17		;Gleich geblieben?
	 brne	hn_x		;raus!
;warten auf STB/ (Low-High-Flanke) 10 ms lang
	 ldihl	Z,CPUCLK/7/100
hn_v:	 sbiw	ZH:ZL,1		;(2)
	 jbic	PinC,SEL,hn_x	;(2)
	 sbis	PinD,STB	;(1)
	  brne	hn_v		;(2)
	 breq	hn_x		;keine Rückflanke? Raus!
	 cbi	PortC,BSY	;BUSY wieder wegnehmen
;Steuerleitungen prüfen
	 in	r16,PinC
	 com	r16		;SEL=H, INI=H, AF=H?
	 andi	r16,7		;untere 3 Bits
	 breq	hn_y		;Jetzt ist die Aushandlung prinzipiell OK.
;Ende der Aushandlung
hn_x:	pop	StatusResp
	cbr	EzBits,1<<BUFFER_FULL
	mov	r16,StatusResp
	cbr	r16,1<<BSY
	sbr	r16,1<<ACK
	out	PortC,r16
	ret	
hn_y:
;Art der Aushandlung prüfen und entsprechend rückmelden
	 ldi	r16,0b01010111
	 cpi	r17,4		;Using Nibble Mode?
	 breq	hn_1
	 cbr	r16,1<<ONL	;Nicht unterstützter Modus
;Statusleitungen setzen
hn_1:	 out	PortC,r16
	 ldi	r16,0b01000111	;bei (irrtümlichem) STB ONL=L setzen lassen,
	 mov	StatusResp,r16	; BSY low lassen, ACK high lassen
#ifdef DEBUG
	 rcall	ocrlf
	 rcall	SendStrAfter
	 .db	"IEEE1284:GetDevId[",0,0
	 mov	r16,r17
	 rcall	ohex
	 ldi	r16,']'
	 rcall	putchar
	 jbis	PortC,ONL,hn_k
	 rcall	SendStrAfter
	 .db	" - nicht unterst�tzt!",0
hn_k:
#endif
	 jbic	PortC,ONL,hn_r	;Nicht unterstützter Modus
;Daten nibbleweise ausgeben
	 ldihl	X,Sektor+2
	 ld	WL,-X
	 ld	WH,-X		;mindestens 3, höchstens 512
hn_l:	 ld	r16,X+
	 rcall	TwoNibbles
	 brcs	hn_f		;Zeitüberschreitungsfehler
	 sbiw	WH:WL,1
	 brne	hn_l
#ifdef DEBUG
	 rcall	SendStrAfter
	 .db	" Abholung OK.",0
#endif
	 rjmp	hn_r		;kein Fehler anzeigen
hn_f:
	 cbi	PortC,ONL	;Fehlersituation anzeigen
;etwas warten, bis Host SEL zurücknimmt
hn_r:	 ldihl	Z,0		;16000000/5/65536 -> 20 ms
hn_u:	 sbiw	WH:WL,1		;(2)
	 sbic	PinC,SEL	;(1)
	  brne	hn_u		;(2)
#ifdef DEBUG
	 rcall	ocrlf
#endif
	 rjmp	hn_x		;zum Ende

;===============
;== Sonstiges ==
;===============

#if 0	
ToggleC:
	in	r17,PortC
	eor	r17,r16
	out	PortC,r17
	ret
#endif	
SpannungMessen:
;Einfache Betriebsspannungsmessung
;PA: R17:R16 = Betriebsspannung in 10-mV-Schritten
	cbi	DDRA, 0		;Eingang
	cbi	PortA,0		;kein Pull-Up
	outi	ADMUX,0b11000000;Uref einschalten, Kanal 0
	outi	ADCSRA,0b11000110;ADC EIN, Teiler 64
sm_1:	jbis	ADCSRA,6,sm_1	;1x wandeln
	sbi	ADCSRA,6
sm_2:	jbis	ADCSRA,6,sm_2	;noch einmal wandeln
	inhl	r17,r16,ADC	;1023->5,115V
	lsrw	r17,r16		; 511->5,11V (halbieren)
	out	ADCSRA,ZERO
	out	ADMUX,ZERO
	sbi	DDRA, 0		;wieder Ausgang
	ret
	
Idle:
;Drucker-Byte zum Puffer schaffen (nicht zum USB)
;VR: -
	sbrs	EzBits,PRINTER_FOUND
	 ret
	sbis	PortC,BSY	;BUSY high?
	 ret
	pushhl	X
	push	r16
	in	r16,SREG
	 rcall	HandleLptByte
	out	SREG,r16
	pop	r16
	pophl	X
	ret
	 
;============================
;== Serielle Schnittstelle ==
;============================

ospac:	;Leerzeichen
	ldi	r16,' '
putchar:
;Zeichen auf serielle Schnittstelle ausgeben
;PE: R16=Zeichen
;Verändert Flags nicht!
pch1:	rcall	Idle
	jbic	UCSRA,UDRE,pch1	;Warte bis Sendehalteregister leer
	out	UDR,r16
	ret
#if 0	
peekchar:
;"Tastenabfrage" von Terminalemulation - diese Routine blockiert nicht!
;PE: -
;PA: Z=0: UART_InChar = Zeichen (wird abgeholt)
;    Z=1: kein Zeichen da
	sez
	sbis	UCSRA,RXC
	 ret
	in	UART_InChar,UDR
	clz
	ret
#endif
getchar:	;Blockierende Tastenabfrage
	rcall	Idle
	jbic	UCSRA,RXC,getchar
	in	UART_InChar,UDR
	ret

ocrlf:	;Wagenrücklauf+Zeilenvorschub
	ldi	r16,0x0D
	rcall	putchar
	ldi	r16,0x0A
	rjmp	putchar
ohex:	;Hex-Byte-Ausgabe
	push	r16
	 swap	r16
	 rcall	onib
	pop	r16
onib:	;Low-Nibble-Ausgabe
	andi	r16,0x0F
	addi	r16,'0'
	cpi	r16,'0'+10
	brcs	putchar
	addi	r16,7
	rjmp	putchar

.include "vt100.i90"
.include "avrstr.i90"
.include "itoa.i90"

LineDel:
;PE: R16=Zeilenummer, R17=Farbe
	push	r16
	 rcall	SendColor
	pop	r17
	clr	r16
	rcall	GotoXY
SendLineDel:
	ldi	r17,'K'
SendCSIz:	;CSI und 1 Folgezeichen (r17) senden
	rcall	SendCSI
	mov	r16,r17
	rjmp	putchar
	
LineDel2:
;PE: R18=Zeilenummer, R19=Farbe
	movw	r17:r16,r19:r18
	rcall	LineDel
	inc	r18
	ret

ScreenInit:
	ldi	r17,0x07
	rcall	SendColor
	rcall	SendStrAfter
	.db	27,"[2J", 0,0
	ldi	r18,0
	ldi	r19,0x70
	rcall	LineDel2
	rcall	SendStrAfter
	.db	" LPT2USB - Konverter parallel -> USB-Drucker  h#s",0
	
	ldi	r19,UPPERCOLOR
lo1:	rcall	LineDel2
	cpi	r18,12
	brne	lo1
	ldi	r16,0
	ldi	r17,2
	rcall	GotoXY
	rcall	SendStrAfter
 .db   " Betriebsspannung:	V",13,10
 .db   " Deskriptor:",13,10
 .db   " 1284 DevID:",13,10,10,10
 .db   " Status: ",27,"[4mERR",27,"[24m=X ONL=X PE=X ",27,"[4mACK",27,"[24m=X BSY=",13,10
 .db   " Gesendete/Empfangene Bytes:				/	  kByte/s",13,10,0
	ldi	r18,23
	ldi	r19,0x70
	rcall	LineDel2
	rcall	SendStrAfter
	.db	" Firmware-Erstellung: ",__DATE__," ",__TIME__,0

	ldi	r17,0x07
	rcall	SendColor
	rcall	SendStrAfter
	.db	0x1B,"[13;23r", 0x1B,"[13;0H", 0
	ret
	
EnterScroll:
;In Rollbereich zurückkehren: Kursorposition und Farbe(!)
	ldi	r17,'u'
	rcall	SendCSIz
	ldi	r17,0x07
	rcall	SendColor
	ret
LeaveScroll:
;Rollbereich verlassen: Kursorposition retten
	ldi	r17,'s'
	rcall	SendCSIz
	ret
	
SendNumberY:
;32-Bit-Zahl bei Y ausgeben
	ld	r2,Y+
	ld	r3,Y+
	ld	r4,Y+
	ld	r5,Y+
	rjmp	SendNumberDezIntMin

SendNumberX4:
;24-Bit-Zahl x4 und 3 Nachkommastellen ausgeben
	ldi	r18,3	;rechtsbündig
SendNumberX4a:		;für linksbündig
	clr	r5
	lslw	r3,r2
	rolw	r5,r4
	lslw	r3,r2
	rolw	r5,r4
	ldi	r19,8	;7 Ziffern + Komma
	rjmp	SendNumberDez

Actualize:
;Obere Bildhälfte zyklisch aktualisieren
;Spannung anzeigen
	ldi	r16,19
	ldi	r17,2
	rcall	GotoXY
	rcall	SpannungMessen
	movw	r3:r2,r17:r16
	clr	r4
	clr	r5
	ldi	r18,2	;Nachkommastellen
	ldi	r19,4	;Feldbreite
	rcall	SendNumberDez
;Status anzeigen (falls geändert, für weniger Gezappel)
	in	r16,PortC
	lds	r17,LastStatus
	cp	r16,r17
	breq	ac_1
	sts	LastStatus,r16
	ldi	r16,13	;ERR-Status
	ldi	r17,7
	rcall	GotoXY
	ldi	r16,'L'
	sbic	PortC,3
	 ldi	r16,'H'
	sbis	DDRC,3
	 ldi	r16,'Z'
	rcall	putchar
	ldi	r16,19	;SEL-Status
	ldi	r17,7
	rcall	GotoXY
	ldi	r16,'L'
	sbic	PortC,4
	 ldi	r16,'H'
	sbis	DDRC,4
	 ldi	r16,'Z'
	rcall	putchar
	ldi	r16,24	;PE-Status
	ldi	r17,7
	rcall	GotoXY
	ldi	r16,'L'
	sbic	PortC,5
	 ldi	r16,'H'
	sbis	DDRC,5
	 ldi	r16,'Z'
	rcall	putchar
	ldi	r16,30	;ACK-Status
	ldi	r17,7
	rcall	GotoXY
	ldi	r16,'L'
	sbic	PortC,6
	 ldi	r16,'H'
	sbis	DDRC,6
	 ldi	r16,'Z'
	rcall	putchar
	ldi	r16,36	;BSY-Status
	ldi	r17,7
	rcall	GotoXY
	ldi	r16,'L'
	sbic	PortC,7
	 ldi	r16,'H'
	sbis	DDRC,7
	 ldi	r16,'Z'
	rcall	putchar
ac_1:
;Gesendete Bytes anzeigen (falls geändert)
	lds	r20,Zyklus
	jbrc	r20,7,ac_2
	ldi	r16,29
	ldi	r17,8
	rcall	GotoXY
	ldi	YL,LptSent
	rcall	SendNumberY
	ldi	r17,UPPERCOLOR
	rcall	SendColor
	ldi	r16,'/'
	rcall	putchar
	ldi	r17,UPPERCOLOR^0x0C
	rcall	SendColor
	rcall	SendNumberY	;LptRecv
ac_2:
;Datenrate anzeigen
	ldi	YL,LptSent
	ldihl	X,LastSent	;Differenzen bilden (24 bit)
	clr	r17
	ld	r2,Y+
	ld	r16,X
	st	X+,r2
	sub	r2,r16
	ld	r3,Y+
	ld	r16,X
	st	X+,r3
	sbc	r3,r16
	ld	r4,Y+
	ld	r16,X
	st	X+,r4
	sbc	r4,r16
	breq	ac_3
	sbr	r17,1<<6	;Ergebnis R4:R3:R2 nicht Null
ac_3:	inc	YL
	ld	r6,Y+
	ld	r16,X
	st	X+,r6
	sub	r6,r16
	ld	r7,Y+
	ld	r16,X
	st	X+,r7
	sbc	r7,r16
	ld	r1,Y
	ld	r16,X
	st	X,r1
	sbc	r1,r16
	breq	ac_4
	sbr	r17,1<<6	;Ergebnis R1:R7:R6 nicht Null
ac_4:
;Feststellen, ob eine Ausgabe nötig ist:
;* wenn ungleich Null
;* wenn vorher ungleich Null (und jetzt Null)
	mov	r16,r17
	or	r16,r20
	andi	r16,1<<6
	breq	ac_5		;Keine Aktualisierung von 0/0
	cbr	r20,1<<6	;Null-Bit übertragen
	sbrc	r17,6
	 sbr	r20,1<<6
;Werte vervierfachen (Byte/s) weil Messung mit 4 Hz
	ldi	r16,48
	ldi	r17,8
	rcall	GotoXY
	push	r1
	 rcall	SendNumberX4
	pop	r4
	movw	r3:r2,r7:r6
	ldi	r17,'C'
	rcall	SendCSIz	;Kursor rechts (grauen "/" übergehen)
	ldi	r18,0x23	;linksbündig
	rcall	SendNumberX4a	;an Position 57
ac_5:	cbr	r20,1<<7
	sts	Zyklus,r20
	ret

;Drehstrich rechts oben darstellen (Aktivitätsanzeige)
Drehstriche:
	.db	"-\|/"
Drehstrich:
	ldi	r17,0x74
	rcall	SendColor
	ldi	r16,79
	ldi	r17,0
	rcall	GotoXY
	lds	r16,Zyklus
	inc	r16
	cbr	r16,4
	sts	Zyklus,r16
	andi	r16,3
	ldihl	Z,Drehstriche*2	;Byteadresse
	add	ZL,r16
	adc	ZH,ZERO
	lpm	r16,Z
	rjmp	putchar
	
;Initialisierung
main:	outihl	SP,RAMEND
	clr	ZERO
	clr	EzBits
	ldi_	ONES, 0b11111111
	outi	OCR0, CPUCLK/64/1000	;Zählerende: ca. 1 ms
	outi	TCCR0,0b00001011	;Timer0: CTC, Vorteiler 64
	out	DDRA, ONES		;PortA standardmäßig Ausgabe (kein ADU)
	;out	PortB,ONES		;Pullups aktivieren? (Frisst Strom!)
	out	PortC,ONES		;Defaults und Pullups
	;outi	DDRC, 0b11111000	;Statusleitungen später Ausgänge!
	outi	PortD,0b11100101	;Defaults, Pullup für STB, rote LED EIN
	outi	DDRD, 0b11111010	;TxD ist auch Ausgang
	outi	MCUCR,0b00000010	;INT0 bei fallender Flanke
	outi	GICR, 0b01000000	;INT0 freigeben
	outihl	OCR1A,CPUCLK/64/4-1	;62499
	outi   TCCR1B,0b00001011	;Zeitgeber1 liefert 4Hz
	clr	r16			;(Aktualisierung bei HyperTerminal)
	ldihl	Z,SRAM_START
	ldihl	W,SRAM_SIZE
loeschram:
	st	Z+,r16			;gesamten RAM löschen
	sbiw	WH:WL,1
	brne	loeschram

;Energie sparen - mit sleep?
	sei
	outihl	UBRR, (CPUCLK/8/BaudRate-1)/2	;Baudrate (12 bit) mit Rundung
	outi	UCSRB,0b00011000	;Sender und Empfänger freigeben
	ldi_	StatusResp,0b10010101	;Das gesetzte BUSY-Bit ist Signal...
	
	
	rcall	SL811_Init

	EZUSB_Delay 100		;Ohne Delay geht Schnittstelle nicht!?
scrini:	rcall	ScreenInit
	stsi	Zyklus,0xC0
	sts	LastStatus,ZERO
	jbrc	EzBits,PRINTER_FOUND,mainloop
	rcall	ShowPrinterDevDat
	sbrc	EzBits,HAVE_1284_ID
	 rcall	ShowDevId
	
;Hauptschleife (die Idle-Routine kümmert sich um parallelen Dateneingang)
mainloop:
	jbrc	EzBits,PRINTER_FOUND,ml1
	 rcall	HandleUsbTransfer
	 rcall	HandleNegotiation
	 rcall	UpdateStatus	;Drei Statusbits vom USB holen und reflektieren
ml1:
;Testen: Statusänderung am EzHost?
	sbrs	EzBits,SLAVE_FOUND
	 rcall	DoEnumSlave
	sbrc	EzBits,SLAVE_FOUND
	 rcall	CheckSlaveDetach
	in	r16,TIFR
	jbrc	r16,4,ml2	;Überlauf-Interrupt Zähler1 (4 Hz)
	outi	TIFR,1<<4	;Interruptanforderung löschen
	rcall	LeaveScroll
	rcall	Drehstrich
	ldi	r17,UPPERCOLOR^0x0C
	rcall	SendColor
	rcall	Actualize
	rcall	EnterScroll
ml2:	jbic	UCSRA,RXC,mainloop	;svw. PeekChar
	rcall	GetKey
	cpi	UART_InChar,12		;Bild-Neuaufbau (^L)
	breq	scrini
	cpi	UART_InChar,'R'-'@'	;^R = Reset
	brne	ml4
	cli
	rjmp	main
ml4:	cpi	UART_InChar,'p'		;Simulation eines Bytes
	brne	ml3
	sbi	DDRD,2		;Ausgabe
	cbi	PortD,2		;LOW, müsste Interrupt auslösen
	cbi	DDRD,2		;Eingabe
	sbi	PortD,2		;PullUp
ml3:	cpi	UART_InChar,'d'	;1284-ID lesen
	brne	ml5
	jbrc	EzBits,PRINTER_FOUND,mle1	;Nur wenn Drucker gefunden!
	jbrc	EzBits,BUFFER_EMPTY,mle1	;Nur wenn Puffer leer
	sbi	PortC,BSY		;"Beschäftigt": PC ruhig
	sbr	EzBits,1<<BUFFER_FULL	;"Puffer voll": Idle ruhig
	rcall	Read1284DevId
	cbr	EzBits,1<<BUFFER_FULL
	cbi	PortC,BSY
	brcc	mainloop
mle1:	ldi	r16,7		;Pieps
	rcall	putchar
	rjmp	mainloop
ml5:	cpi	UART_InChar,'e'	;EzBits anzeigen
	brne	ml6
	rcall	SendStrAfter
	.db	"EzBits=",0
	mov	r16,EzBits
	rcall	ohex
ml6:	cpi	UART_InChar,'i'	;Info anzeigen
	brne	ml7
	rcall	ocrlf
	rcall	SendStrAfter
	.db	"Info: <40>,<42>=NAK, <80>=STALL, <10>=Disconnect...",0
	rcall	ocrlf
ml7:	cpi	UART_InChar,'r'		;Bytezähler rücksetzen
	brne	mle
	ldi	YL,LptSent
	clr	r0
	st	Y+,r0
	st	Y+,r0
	st	Y+,r0
	st	Y+,r0
	st	Y+,r0
	st	Y+,r0
	st	Y+,r0
	st	Y+,r0
	ldi	YL,LastSent
	st	Y+,r0
	st	Y+,r0
	st	Y+,r0
	st	Y+,r0
	st	Y+,r0
	st	Y+,r0
	rcall	SetZyklusBit7
	rcall	LeaveScroll
	ldi	r17,UPPERCOLOR^0x0C
	rcall	SendColor
	ldi	r16,29
	ldi	r17,8
	rcall	GotoXY
	ldi	r17,18
mll1:	rcall	ospac		;Löschen vom Kursor
	djnz	r17,mll1
	rcall	EnterScroll
mle:	rjmp	mainloop
	
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded