AngoLinux

Programma Assembler per scheda Micro e programma per download

- A cura del Prof. Stefano Salvi -


  1. Programma Assembler (istruzioni per il download)
  2. Programma C (istruzioni per il download)

Per poter svolgere l'esercizio nel quale si comunica con un dispositivo tramite un protocollo ocorre avere un dispositivo che comunichi con quel protocollo.

Il nostro laboratorio è dotato di schede con microprocessore 8051 e di schede di I/O con display LCD, da connettere alle prime (si veda la descrizione dell'hardware aggiuntivo nel laboratorio).

Naturalmente, per poter sfruttare questo hardware occorre un programma per 8051 che implementi il protocollo sulla scheda e che piloti adeguatamente il display ed i tasti. Dato che lo strumento di sviluppo utilizzato sull'8051 è l'assembler, questo programma dovrà essere scritto in assembly.

Per scaricare il programma, una volta assemblato, sulla scheda è possibile utilizzare il programma pcmon, ma sarebbe meglio utilizzare un programma specifico, da riga di comando, che consenta di scaricare nella scheda il protocollo senza ulteriori complicazioni.

Il programma assembler che implementa il protocollo e pilota il display ed i tasti è il seguente:
.org 0
; protocol.asm
; Stefano Salvi - 11 Novembre 2001
;
; Protocollo di comunicazione tra PC e scheda GPC2 con display LCD
;
; Il piedino P1.2 e' connesso al led giallo ed al cicalino
; il piedino P1.5 e' connesso al led rosso
;
; La scheda GPC2 possiede 32K di EPROM da 0 a 7FFF e 32 K di RAM da 8000 a f7FF.
;
; L'I/O aggiuntivo di GPC2 e' mappato come segue in memoria XDATA:
;
;	0FA00h .. 0FA3Fh	PPI 8255
;	0FA40h .. 0FA7Fh	TIMER 82C54 (primo)
;	0FA80h .. 0FABFh	TIMER 82C54 (secondo)
;	0FAC0h .. 0FAFFh	RTC 6242b
;
;	0FB00h .. 0FFFFh	BUS esterno (ABACO Bus)
;
; In particolare, per il PPI 8255 avremo:
;	0FA00h	->	PDA = Registro dati del port A
;	0FA01h	->	PDB = Registro dati del port B
;	0FA02h	->	PDB = Registro dati del port C
;	0FA03h	->	CNT = registro di controllo
;
; L'8255 ha tre modi di funzionamento (Mode 0 = Basic Input Output, Mode 1 = Strobed
; Input Output, Mode 3 = Bi-Directional Bus), che possono essere selezionati indipendentemente
; per il gruppo Port B, Port C 0..3 e Port A,Port C 4..7.
; Avendo connesso alla scheda il tastierino (TIO 16), il solo modo che ci interessa e' lo 0,
; nel quale ogni port puo' essere programmato come ingresso o uscita, per  entrambi i gruppi.
; Vediamo ora come programmare il PPI, tramite il port di controllo:
;
; Bit 0 -> Port C 0..3 : 1 = Input, 0 = Output
; Bit 1 -> Port B :  1 = Input, 0 = Output
; Bit 2 -> Modo Port B -> 0 = Mode 0, 1 = Mode 1
;
; Bit 3 ->  Port C 4..7 : 1 = Input, 0 = Output
; Bit 4 -> Port A :  1 = Input, 0 = Output
; Bit 5 -+
; Bit 6 -+> Modo port A: 00 -> Mode 0, 01 -> Mode 1, 10-11 Mode 3
;
; Bit 7 -> Programmazione Interrupt : 0 -> Interrupt, 1 -> Modo
;
; Non mi preoccupo della programmazione dll'interrupt, quindi dovremo sempre programmare il
; bit 7 a 1.
;
; Il Port A e' collegato ai piedini 1..8 del connettore CN2 (Tasti rossi)
; Il Port B e' collegato ai DIP Switch
; Il Port C e' collegato ai piedini 9..16 del connettore CN2 (0..3 Tasti gialli, 4..7 Tasti verdi)
;
; Scrivo un programma che:
; - Programmi il Port A in uscita, il Port B in ingresso, i bit 0..3 del Port C in uscita e i 
:    bit 4..7 in ingresso.
;
; Il display ha:
; - Bus dati (8 bit - connesso al port A)
; - Un segnale di Enable 'E' connesso a Port C, bit 3, attivo sul fronte di discesa
; - Cinque abilitazioni di 'colonna', attive alte (connesse ad un latch)
; - Un segnale 'D/I' che deve essere alto per scrivere dati, basso per inviare cdomandi
;   (connesso ad un latch)
;
; Le attivazioni delle colonne e D/I sono collegati ad un buffer:
; - Port C bit 1 e' l'enable del buffer (attivo alto/ latchato basso)
; - I bit da 0 a 4 del buffer sono le cinque clolnne (attive alte)
; - Il bit 5 del buffer e' il piedino D/I
; - Ai bit 4..7 di Port C sono connessi quattro pulsanti, Zero se premuti
;
	.org	8050h
;
;
;			ExLxB -> E -> Al display; L -> Al LATCH
.equ	E,		1000B	;0000B
.equ	NONE,		0000B	;1000B
.equ	LATCH,		0010B	;1010B
.equ	NONLATCH,	0000B	;1000B
;
; *****************************************************************************
; Definizione Indirizzi Assoluti Registri 'Timer' (registri di RB2)
.equ	BlinkTout,	17H	; R7B2 Divisore Lampeggio Led
.equ	RxTout,		16H	; R6B2 Registro timeout Ricezione Caratteri
.equ	TransmitTout,	15H	; R5B2 Registro timeout Ritrasmissioni trasmettitore
.equ	DeBounceTout,	14H	; R4B2 Registro timeout debounce
.equ	RetryCount,	13H	; R3B2 Retry ancora da inviare
;
; *****************************************************************************
; Definizione Indirizzi assoluti e Costanti Interrupt Seriale
;    USA	RB1
.equ	RxPtrLo,	0Fh	; R7B1	PTR RX Buffer Lo
.equ	RxCommand,	0Eh	; R6B1	Comando ricevuto
.equ	RxSt,		0Dh	; R5B1	Stato RX
.equ	RxCks,		0Ch	; R4B1	CKS RX
.equ	TxCnt,		0Bh	; R3B1	Conteggio Caratteri TX
.equ	TxSt,		0Ah	; R2B1	Stato TX
.equ	TxCks,		09h	; R1B1	CKS TX
.equ	TxPtr,		08h	; R0B1	PRT TX Buffer
;
; Codifica Stati Ricevitore
.equ	StIdle,		0	; Tra un 'flag' e l'altro
.equ	StFirst,	1	; Primo carattere ricevuto (solo RX, gestione doppio FLAG)
.equ	StStd,		2	; Carattere normale
.equ	StMask,		3	; Carattere mascherato
.equ	StCks,		4	; Checksum (solo trasmissione)
.equ	StCFlg,		5	; Flag di chiusura (solo trasmissione)
.equ	StBufOvfl,	6	; Buffer overflow - attende fine pacchetto e termina
.equ	StCksMask,	7	; Invia il checksum mascherato (solo trasmissione)
.equ	StEnding,	8	; Fine Trasmissione (trasmesso anche Flag Chiusura - Solo Trasmissione)
;
.equ	FLAGc,		7Eh	; Carattere 'Flag'
.equ	ESCAPE,		7Dh	; Carattere di Trasparenza
.equ	MASK,		20h	; Carattere con cui mascherare
;
.equ	CONFIRM,	0FFh	; Conferma (comando ricevuto)
;
.equ	SENDTO,		132	; 660 mS / 5 mS -> numero di tick timeout di trasmissione
;
; *****************************************************************************
; Definizione Variabili BIT
.equ	CmdRecvd,	0	; Flag Comando Ricevuto
.equ	CommandPending,	1	; Flag spedizione Comando in corso (con ritrasmissioni)
.equ	CommandSending,	2	; Flag spedizione Comando in corso
.equ	RispSending,	3	; Flag Risposta da spedire
.equ	RispOrCommand,	4	; Flag: sto inviando Comando o Risposta?
.equ	DeBouncheTO,	5	; Flag: debounche in corso
;
; *****************************************************************************
; Definizione Variabili DATA
;
.equ	CURCOL,		30h	; Variabile: colonna corrente
.equ	CURBAND,	31h	; Variabile: Banda corrente (gruppo di 50 colonne)
.equ	CURROW,		32h	; Variabile: Riga corrente (gia' shiftata nei bit 6 e 7)
.equ	RxBufLen,	33h	; Variabile: Lunghezza dei dati ricevuti
.equ	TxRispLen,	34h	; Variabile: Lunghezza della risposta da inviare
.equ	TxRispBuff,	35h	; Buffer: Risposta (38 byte max - 26 hex)
.equ	TxRispDato,	36h	; Secondo carattere del buffer: parametro
.equ	TxCommLen,	5Bh	; Lunghezza Comando da inviare
.equ	TxCommBuf,	5Ch	; Comando da inviare (stato pulsanti - 2 caratteri)
.equ	TxCommTStat,	5Dh	; posto per lo stato dei bottoni nel buffer Tx
.equ	CurButtons,	5Eh	; Stato attuale bottoni
.equ	StackStart,	5Fh	; Inizio Area di stack
;
; *****************************************************************************
; Definizione Variabili XDATA
;
.equ	RxBuff,		0F700h	; Buffer di ricezione. Ho 256 byte a disposizione
.equ	RxComm,		0F700h	; Comando Ricevuto
.equ	RxCol,		0F701h	; Se il comando e' 'scrivi ...', colonna iniziale
.equ	RxRow,		0F702h	; Se il comando e' 'scrivi ...', riga
.equ	RxData,		0F703h	; Inizio dell'area dati
.equ	RxBufPage,	0F7h	; Parte alta dell'indirizzo
;
.equ	DREG,		0FA00h	; Registro Port A (dato display)
.equ	CREG,		0fa03h	; Registro di controllo PPI
;
; *****************************************************************************
;
	sjmp	START	; Salto a START, perche' adesso devo metter i vettori di TI0 e SI
;
	.org	8053h	; External Interrupt 0
;
	.org	805Bh	; Timer 0 Interrupt
	ajmp	TIMINT
;
	.org	8063h	; External Interrupt 1
;
	.org	806bh	; Timer 1 Interrupt
;
	.org	8073h	; Serial Interrupt
	sjmp	SERINT
;
START:
        mov	SP,#StackStart
	acall	ClrScr		; Inizializza il display e lo cancella
;
	mov	PSW,#0		; Seleziona RB0 per main
;
; Inizializzazione Variabili
	mov	RxSt,#StIdle	; RxSeriale inizialmente 'idle'
	mov	TxSt,#StIdle	; TxSeriale inizialmente 'idle'
	mov	BlinkTout,#200	; Conteggio iniziale Lampeggiatore
	mov	RxTout,#0	; Rx Timeout fermo
	mov	TransmitTout,#0	; ReTransmit Timeout fermo
	mov	DeBounceTout,#0	; Debounch Timeout fermo
	mov	CurButtons,#0	; Tasti inizialmente rilasciati
	clr	CmdRecvd	; Nessun comando ancora ricevuto
	clr	CommandPending	; Nessun comando in corso di invio
	clr	CommandSending	; Flag spedizione Comando in corso
	clr	RispSending	; Flag Risposta da spedire
	clr	DeBouncheTO	; Flag: debounche in corso
;
; Impostazione Seriale
;
        mov     IE,#0           ; nessun interrupt
	mov	IP,#0		; tutti a priorita' bassa
        mov     SCON,#52H       ; Modo 2, REN, TI
        mov     TMOD,#21H       ; No Gate, Timer, Modo 2;No Gate, Timer, Modo 1 
        mov     TCON,#50H       ; T1 running, T0 Running 
        mov     TH1,#0FDH       ; 9600 baud
;
; Impostazione Timer 0: divisore 4608 -> impostare a 60928 = EE00  
;
	mov	TH0,#0EEH	; Parte alta divisore
	mov	TL0,#00H	; Parte bassa divisore
	mov	BlinkTout,#200	; Conteggio iniziale
	mov	IE,#92H		; EA, ES, ET0
;
	cpl	P1.5		; Fa lampeggiare il led
;
	ajmp	main
;
;*****************************************************************************
;
; Rotuine Interrupt Seriale
;    USA	RB1
;	R7	PTR RX Buffer Lo (la parte alta e' fissa, funge anche da contatore)
; 	R6	Comando ricevuto
;	R5	Stato RX
;	R4	CKS RX
;	R3	Conteggio Caratteri TX
;	R2	Stato TX
;	R1	CKS TX
;	R0	PRT TX Buffer
; ----------------------------------------------------------------------------
;
SERINT:
	push	acc
	push	PSW
	mov	PSW,#08H	; Seleziona RB1
;
; +++++++++++++++++++++++++++++++++++++++++++++
; Rx
; +++++++++++++++++++++++++++++++++++++++++++++
;
	jnb	RI,norx
	mov	a,sbuf
	clr	RI
;
	cjne	R5,#StIdle,StFirstQ
;
; +++++++++++ Stato 'Idle'
;
	cjne	a,#FLAGc,norx	; Scarta tutto, eccetto al 'flag'
FLAGREC:			; Ha ricevuto un 'flag', inizia ricezione
	mov	R5,#StFirst	; Cambia stato
	mov	R7,#0		; Inizializza conteggio/puntatore
	mov	R4,#0		; Inizializza checksum
	mov	RxTout,#22	; Timeout di 110mS (22 * 5)
;
	sjmp	norx
;
; +++++++++++ Stato 'First'
;
StFirstQ:
	cpl	P1.5		; Fa lampeggiare il led
	cjne	R5,#StFirst,StStdQ
;
	cjne	a,#FLAGc,NORMCHAR	; Se non e' un secondo 'FLAG', gestione normale
	sjmp	FLAGREC			; Se e' un 'FLAG', gestisce come fosse il primo
NORMCHAR:
	mov	R6,a			; Ripone il comando per poi
	mov	R5,#StStd		; Se non e' un 'FLAG', passa a carattere normale
;
; +++++++++++ Stato 'Std'
;
StStdQ:
	cjne	R5,#StStd,StMaskQ
;
	cjne	a,#FLAGc,ESCAPEQ	; Se non e' un 'FLAG' di chiusura, controlla con MASK
;
;	Ha ricevuto un FLAG: chiusura pacchetto
;
	mov	R5,#StIdle		; Ritorna Idle
	mov	RxTout,#0		; Ferma il timer
	cjne	R4,#0,norx		; Il checksum deve essere 0, altrimenti pacchetto errato
;
; Pacchetto corretto
;
	cjne	R6,#CONFIRM,COMMAND	; Se e' un comando ricevuto, lo gestisce
;
; Conferma
;
	clr	CommandPending		; Indica che non c'e' piu' alcun comando pendente
	mov	TransmitTout,#0		; Ferma il timer dei ritentativi
	cpl	P1.2			; Lampeggia Led Giallo
	sjmp	norx
COMMAND:
	mov	a,R7			; Recupera il conteggio
	dec	a			; elimina il checksum
	mov	RxBufLen,a		; inserisce lunghezza nella variabile opportuna
	setb	CmdRecvd
	sjmp	norx
;
ESCAPEQ:
	cjne	a,#ESCAPE,NORMC		; Se non e' un 'ESCAPE' e' un carattere normale
;
	mov	R5,#StMask		; Cambia banalmente lo stato
	sjmp	norx
;
NORMC:
	acall	AddChar
	sjmp	norx
;
; +++++++++++ Stato 'Mask'
;
StMaskQ:
	cjne	R5,#StMask,StBufOvflQ
;
	cjne	a,#FLAGc,NORMEXC	; Se non e' un 'FLAG' allora OK
;	sjmp	NORMEXC
;	Ha ricevuto un FLAG: errore !!!
RXERROR:
	mov	R5,#StIdle		; Ritorna Idle
	mov	RxTout,#0		; Ferma il timer
	sjmp	norx
;
NORMEXC:
	mov	R5,#StStd		; Ritorna a stato normale
	xrl	a,#MASK		; Ripristina il carattere
	acall	AddChar			; e lo 'sistema'
	sjmp	norx
;
; +++++++++++ Stato 'Idle'
;
StBufOvflQ:
	cjne	R5,#StBufOvfl,RXERROR
;
	cjne	a,#FLAGc,norx	; Se non e' un 'FLAG' allora OK
;	Ha ricevuto un FLAG: Fine pacchetto - sbagliato
	sjmp	RXERROR
;
norx:
; +++++++++++++++++++++++++++++++++++++++++++++
; Tx
; +++++++++++++++++++++++++++++++++++++++++++++
;
	jnb	TI,notx
	clr	TI
;
; +++++++++++ Stato 'Std'
;
	cjne	R2,#StStd,TxEscQ
;
	mov	a,@R0			; Legge il carattere corrente
	add	a,R1			; Aggiunge a Checksum
	mov	R1,a			; Rimette a posto il checksum
	mov	a,@R0			; Legge il carattere corrente di nuovo
	cjne	a,#FLAGc,TxEscTs	; Se e' 7e (flag) o 7d (esc)
	sjmp	TxMaskCh
TxEscTs:
	cjne	a,#ESCAPE,TxStCh
TxMaskCh:
	mov	sbuf,#ESCAPE	; Spedisce il carattere
	mov	R2,#StMask	; Cambia stato
	sjmp	notx
;
TxStCh:
	mov	sbuf,a		; Spedisce il carattere
	inc	R0
;
	djnz	R3,notx		; Non ha finito i caratteri, OK
;
	mov	R2,#StCks	; Caratteri finiti: Cambia stato
;
	sjmp	notx
;
; +++++++++++ Stato 'Esc'
;
TxEscQ:
	cjne	R2,#StMask,StCksQ
;
	mov	a,@R0		; Legge il carattere corrente
	xrl	a,#MASK		; Lo maschera
	mov	R2,#StStd	; Cambia stato - ritorna a carattere normale
	sjmp	TxStCh		; Continua come con carattere normale
;
; +++++++++++ Stato 'Cks'
;
StCksQ:
	cjne	R2,#StCks,StCksMaskQ
;
	mov	a,R1		; Recupera il checksum
	cpl	a
	inc	a		; Calcola -Checksum
	cjne	a,#FLAGc,TxEscCk	; Se e' 7e (flag) o 7d (esc)
	sjmp	TxMaskTsCk
TxEscCk:
	cjne	a,#ESCAPE,TxStChk
TxMaskTsCk:
	mov	R1,a		; Ripone il checksum calcolato
	mov	sbuf,#ESCAPE	; Spedisce il carattere
	mov	R2,#StCksMask	; Cambia stato
	sjmp	notx
;
TxStChk:
	mov	sbuf,A		; Spedisce il carattere
	mov	R2,#StCFlg	; Cambia stato -> Invio Flag chiusura
;
	sjmp	notx
;
; +++++++++++ Stato 'CksMask'
;
StCksMaskQ:
	cjne	R2,#StCksMask,StCFlgQ
;
	mov	a,R1		; Recupera il checksum calcolato (gia' invertito)
	xrl	a,#MASK		; Lo maschera
	mov	sbuf,A		; Spedisce il carattere mascherato
	mov	R2,#StCFlg	; Cambia stato -> Invio Flag chiusura
;
	sjmp	notx
;
; +++++++++++ Stato 'CFlg'
;
StCFlgQ:
	cjne	R2,#StCFlg,StEndingQ
;
	mov	sbuf,#FLAGc	; Spedisce il flag di chiusura
	mov	R2,#StEnding	; Cambia stato -> Attendo fine
;
	sjmp	notx
;
; +++++++++++ Stato 'Ending' (default)
;
StEndingQ:
	mov	R2,#StIdle	; Cambia stato -> Fine
;
	jnb	RispOrCommand,EndingCommand
;
	clr	RispSending		; Fine invio
	jnb	CommandSending,notx	; Se non deve inviare un comando
;
	mov	a,TxCommLen		; Recupera dimensione buffer
	acall	ReSendCommand		; Avvia la spedizione
;
	sjmp	notx		; Fine
;
EndingCommand:
;
	clr	CommandSending		; Indica che ha finito la trasmissione
	jnb	RispSending,notx	; Se non deve inviare un comando
;
	mov	a,TxRispLen		; Recupera dimensione buffer
	acall	SendPktReal		; Avvia la spedizione
;
notx:
;
	pop	psw
	pop	acc
	reti			; Fine Interrupt Handler
;
; Routine di appoggio: agginge un carattere al buffer, calcolandone il checksum
;
AddChar:
	push	DPH
	push	DPL
	mov	DPL,R7		; Parte bassa indirizzo
	mov	DPH,#RxBufPage	; Parte alta, costante
	movx	@dptr,a		; mette via il dato
	pop	DPL
	pop	DPH
	inc	R7		; incrementa il puntatore
	cjne	R7,#253,NOOVFL	; Se non ha fatto overflov, niente
	mov	R5,#StBufOvfl	; Va in 'buffer overflow
NOOVFL:
	add	a,R4		; calcola il checksum
	mov	R4,a		; e rimette via il risultato
	ret
;
;*****************************************************************************
;
; Routine Interrupt Timer 0
;
;	Usa	RB2
;	R7	17H	Divisore Lampeggio Led
;	R6	16H	Timeout RxSeriale
;	R5	15H	Timeout Trasmettitore
;	R4	14H	Timeout Debounce
;	R3	13H	Retry ancora da inviare
;
TIMINT:
	push	acc
	push	PSW
	mov	PSW,#10H	; Seleziona RB2
;
	clr	TR0		; 1 Ferma timer 0
	mov	acc,TL0		; 1 - preleva la parte bassa
	add	a,#07H		; 1 - 00h + 7 cicli di adesso
	mov	TL0,acc		; 1 - rimette a posto
	mov	a,TH0		; 1 - preleva la parte alta
	addc	a,#0EEh		; 1 - somma con riporto (a 16 bit)
	mov	TH0,a		; 1 - risistema
	setb	TR0		; 1 Riavvia Timer 0
;
	djnz	R7,NOBLINK
	mov	R7,#200		; 200 * 5 mS
	cpl	P1.5		; Fa lampeggiare il led
NOBLINK:
;
; Timeout Rx Seriale
;
	mov	A,R6		; R6 = timeout Tx Seriale
	jz	NORXTO		; se e' gia' scaduto, sta fermo
	djnz	R6,NORXTO	; conta i ticks
;
;	Timeout Rx Seriale scaduto
	mov	RxSt,#StIdle	; Riporta lo stato a 'idle' senza indicare la ricezione
;
NORXTO:
;
; Timeout Trasmettitore
;
	mov	A,R5		; R5 = timeout Trasmettitore
	jz	NOTRASMTO	; se e' gia' scaduto, sta fermo
	djnz	R5,NOTRASMTO	; conta i ticks
;
;	Timeout Trasmettitore scaduto
;
	djnz	R3,Retry	; Conta le riprove effettuate
	clr	CommandPending	; Fine riprove
	sjmp	NOTRASMTO	; termina
;
Retry:
;
	mov	a,TxCommLen	; Recuper dimensione buffer
	acall	ReSendCommand	; Ripete la spedizione
	sjmp	NOTRASMTO	; termina
;
NOTRASMTO:
;
; Timeout Debounch
;
	mov	A,R4		; R5 = timeout Debounch
	jz	NODEBNCHTO	; se e' gia' scaduto, sta fermo
	djnz	R4,NODEBNCHTO	; conta i ticks
;
;	Timeout Debounch scaduto
;
	clr	DeBouncheTO	; timeout scaduto
;
NODEBNCHTO:
;
	pop	psw
	pop	acc
	reti			; Fine Interrupt Handler
;
;*****************************************************************************
;
; ---- Funzioni di gestione del pacchetto da inviare ----
SendConf:
	mov	TxRispBuff,#0FFh	; Mette nel buffer 'conferma'
	mov	a,#1			; Lunghezza Conferma - poi continua con SendPkt
;
; Funzione che invia un pacchetto di risposta
;  Il pacchetto da ritornare deve gia' essere nel buffer
;  a contiene la dimensione del buffer
;
SendPkt:
	mov	TxRispLen,a		; Immagazzina la lunghezza
	setb	RispSending		; Indica risposta pendente
	jb	CommandSending,SendPktEnd ; Se sta gia' trasmettendo, non avvia
;
;	Avvia trasmissione, inviando kickoff
;
SendPktReal:
	setb	RispOrCommand	; Indica che sta trasmettendo una risposta
	mov	sbuf,#FLAGc	; Invia il flag
	mov	TxCnt,a		; Inizializza il conteggio
	mov	TxCks,#0	; Inizializza il checksum
	mov	TxPtr,#TxRispBuff ; Punta al buffer da trasmettere
	mov	TxSt,#StStd	; Stato carattere normale
;
SendPktEnd:
	ret
;
SendCommand:
	cpl	P1.2
	jb	CommandPending,SendCommandEnd ; Se sta facendo ancora i ritentativi, non manda
;
	mov	TxCommLen,a		; Imposta la lunghezza del pacchetto
	setb	CommandPending		; Pacchetto in invio (ritrasmissioni)
	mov	RetryCount,#5		; Imposta il numero di riprove
ReSendCommand:
	setb	CommandSending		; Pacchetto in invio (pacchetto)
;
	jb	RispSending,SendCommandEnd ; Se sta gia' trasmettendo, non avvia
;
	mov	TransmitTout,#SENDTO	; Imposta il timeout di trasmissione
	clr	RispOrCommand	; Indica che sta trasmettendo un comando
	mov	sbuf,#FLAGc	; Invia il flag
	mov	TxCnt,a		; Inizializza il conteggio
	mov	TxCks,#0	; Inizializza il checksum
	mov	TxPtr,#TxCommBuf ; Punta al buffer da trasmettere
	mov	TxSt,#StStd	; Stato carattere normale
;
SendCommandEnd:
	ret
;
;*****************************************************************************
;
; ---- Funzioni di gestione del display ----
;
;-----------------------------------------------------------------
; Cancella il display - Inizializza il PPI, inizializza il display
ClrScr:
	mov	CURCOL,#0		; Variabile: colonna corrente
	mov	CURBAND,#1		; Variabile: Banda corrente (gruppo di 50 colonne)
	mov	CURROW,#0		; Variabile: Riga corrente (gia' shiftata nei bit 6 e 7)
	mov	DPTR,#CREG		; Port CNT di PPI
	mov	A,#10001010b	; Mode 0 per A e B, A in OUT, 
						; Chi in IN, B in in, Clo in OUT
	movx	@dptr,a			; Setta il PPI
	mov	DPL,#0			; Sempre puntato sul port dati
;
	mov	r7,#3bh			; Up Mode
	acall	outControl
	mov	r7,#03eh		; Start Page 0
	acall	outControl
;
	mov	R6,#0
;
CSlp0:
	mov	A,R6
	swap	a
	rl	a
	rl	a
	mov	R7,A
	acall	outControl		; Seleziono il banco (gotoXY)
	acall	setAllData		; Seleziona tutti i banchi insieme
	mov	r5,#50			; 50 colonne
CSlp1:
	mov	a,#0
	acall	outByte
	djnz	r5,CSlp1
;
	inc	R6
	cjne	R6,#4,CSlp0		; Loop per i quattro banchi
;
	mov	r7,#39h			; Display on
	acall	outControl
	ret
;	
;-----------------------------------------------------------------
; Si posiziona alla colonna (assoluta) ed alla riga indicata
; INPUT:
;	A -> Colonna (0..249)
;	R7-> Riga (0..2)
; Utilizza:
;	A, R7
gotoXY:
	mov	CURCOL,A
	mov	A,R7		; R7 = Riga
	swap	A
	rl	A
	rl	A			; A = R7 * 64
	mov	CURROW,A	; Riga corrente (bit 6 e 7)
	mov	R7,#1		; Inizializza conteggio bande
bandlp:
	mov	A,CURCOL
	add	A,#-50		; toglie una banda
	jnc	endband		; NC -> CURCOL < 50 -> Esce
	mov	CURCOL,A	; Mette il resto in CURCOL
	mov	A,R7
	clr	c
	rlc	A			; Passa alla banda successiva
	mov	R7,A
	sjmp	bandlp		; Loop per tutte le bande
endband:				; Fine loop bande
	mov	CURBAND,R7	; E mette nella variabile
realgoto:
	mov	A,CURROW	; Prende la riga
	anl	a,#11000000B; Tiene solo riga (toglie eventuale sporco)
	orl	A,CURCOL	; Aggiunge la colonna
	mov	R7,A
	acall	outControl	; invia il comando (gotoXY)
	mov	a,#00100000B; Setta bit B/I (Dato)
	orl	A,CURBAND	; Banda selezionata
	acall	outSel		; Seleziona la banda
	ret
;
;-----------------------------------------------------------------
; Invia un carattere al display
; Input:
;	A -> carattere (troncato a 7 bit)
; Utilizza:
;	A, R7, R6, R5, R4
;
outChar:
	mov	DPTR,#chargen	; Punta alla tabella
noremap:
;
	anl	A,#7fh		; scarta l'eccesso' dell'indice
	mov	B,#8
	mul	AB		; moltiplica il codice per 8
	add	A,DPL		; Somma la base della tabella
	mov	R6,A		; Parte bassa indirizzo in R6
	mov	A,B			; Recupera parte alta del prodotto
	addc	A,DPH		; Somma la base (con riporto)
	mov	R5,A		; Parte alta indirizzo in R5
	mov	R4,#0		; Colonna
outClp:
	mov	DPL,R6
	mov	DPH,R5
	mov	A,R4		; Colonna
	movc	A,@A+DPTR	; Codice
	mov	DPTR,#0FA00h	;DREG	; 0FA00H	; Punta al registro dati
	acall	outDato		; Spara fuori il dato
	inc	R4
	cjne	R4,#6,outClp
	ret
;
;-----------------------------------------------------------------
; Invia un byte di dato al display, in maniera sequenziale, 
; passando da una 'banda' alla successiva
; Input:
;	A -> Dato
;	DPTR -> Port A
; Utilizza:
;	R7, A, DPTR
;
outDato:
	acall	outByte			; Invia il dato corrente
;
	inc	CURCOL			; conta il dato
	mov	A,CURCOL		; guarda dove e' arrivato
	cjne	A,#50,outDnorm	; Non e' arrivato a 50
; CURCOL = 50 (cambia banda)
	mov	CURCOL,#0 		; Ricomincia da 0
	mov	A,CURBAND		; Banda selezionata
	rl	A				; passa alla banda successiva
	mov	CURBAND,A		; Rimette a posto la nuova abanda
	acall	realgoto		; va alla posizione scelta
outDnorm:
	ret; 
;
;-----------------------------------------------------------------
; Invia la parola di selezione al latch esterno
;	B0 .. B4 -> Colonna (Attiva alta)
;	B5	-> D/I Alto = Dato, basso = Comando
; Input:
;	A -> Parola di selezione
;	DPTR -> Port A
; Sporca A
outSel:
	movx	@DPTR,A		; Manda fuori la selezione
	mov	DPL,#2		; Port C
	mov	A,#LATCH
        movx	@DPTR,A		; Alza l'enable (fa passare)
	mov	A,#NONLATCH
	movx	@DPTR,A		; Riabbassa l'enable (latcha)
	mov	DPL,#0		; Ripristina DPTR
	ret
;
;-----------------------------------------------------------------
; Invia un byte al display, sulla porta dati (gestisce il segnale 'E')
; Input: A = Dato
;	DPTR -> Port A
; Sporca: A
outByte:
	movx	@DPTR,A
	mov	DPL,#2		; Si posiziona sul Port C
	mov	A,#E		; Alza 'E'
	movx	@DPTR,A
	mov	A,#NONE		; Riabbassa 'E'
	movx	@DPTR,A
	mov	DPL,#0		; Si riposiziona sul Port A
	ret
;
;-----------------------------------------------------------------
; Invia un comando al display
; Input: R7 = comando
;	DPTR -> Port A
; Sporca: A
outControl:
	mov	A,#011111B
	acall	outSel
	mov	A,R7
	acall	outByte
	ret
;
;-----------------------------------------------------------------
setAllData:
	mov	A,#111111B
	acall	outSel
	ret
;
;
; ---- Funzioni applicative ----
;

main:
;
mainlp:
;
	jnb	CmdRecvd,nomess1 ; Se non e' arrivato alcun comando, salta
	clr	CmdRecvd	; Cancella il flag: esguo comando
	mov	a,RxCommand	; Recupera il comando
;
	cjne	a,#10h,RstQ		; Riciesta Tasti
;
	mov	a,#90h			; Codice risposta
	mov	TxRispBuff,a		; Deposita nel buffer della risposta
	mov	a,CurButtons		; Stato corrente bottoni (non letto, ma gestito)
	mov	TxRispDato,a		; Deposita nel buffer
	mov	a,#2			; lunghezza risposta
	acall	SendPkt			; invia la risposta
;
nomess1:
	ajmp	nomess
;
RstQ:
	cjne	a,#52h,WrCQ		; Reset Display
;
	acall	ClrScr			; Inizializza il display e lo cancella
	acall	SendConf		; Conferma il comando
;
	ajmp	nomess
;
WrCQ:
	cjne	a,#21h,WrGQ		; Scrivi Caratteri
;
; Posso usare R3, R2, R1 ed R0 (outChar)
;
	mov	DPTR,#RxCol
	movx	a,@DPTR
	mov	B,#6			; Dimensione carattere
	mul	ab			; moltiplica la colonna per 6
	mov	r0,a			; ripone il dato
	add	a,#6			; sottrae 250
	jc	EndWr			; supera 250, errore
	mov	a,b			; parte alta moltiplicazione
	jnz	EndWr			; non e' 0 : eccede
	inc	dptr			; passa a riga
	movx	a,@DPTR			; legge riga
	mov	r7,a			; Riga in R7
	mov	a,r0			; colonna in A
	mov	DPTR,#DREG
	acall	GotoXY			; si posiziona
;
	mov	r0,#3			; offset primo carattere
	mov	a,RxBufLen		; recupera dimensione buffer
	add	a,#-3			; toglie 'comando', 'colonna' e 'riga'
	mov	r1,a			; r1 funge da contatore
;
;	Loop scrittura caratteri
;
WrCLp:
	mov	DPH,#RxBufPage		; Prepara parte alta puntatore
	mov	DPL,R0			; parte bassa
	inc	R0			; incrementa puntatore
	movx	a,@DPTR			; recupera carattere
	mov	DPTR,#DREG		; ripunta DPTR per operazioni con caratteri
	acall	outChar			; invia il carattere letto
	djnz	r1,WrCLp		; loop fino a che non ha finito
;
EndWr:
	mov	DPTR,#DREG
	acall	SendConf		; Conferma il comando
;
	sjmp	nomess
;
WrGQ:
	cjne	a,#22h,RqIdQ		; Scrivi Grafica
;
; Posso usare R3, R2, R1 ed R0 (outChar)
;
	mov	DPTR,#RxCol
	movx	a,@DPTR
	mov	r0,a			; ripone il dato
	add	a,#6			; sottrae 250
	jc	EndWrG			; supera 250, errore
	inc	dptr			; passa a riga
	movx	a,@DPTR			; legge riga
	mov	r7,a			; Riga in R7
	mov	a,r0			; colonna in A
	mov	DPTR,#DREG
	acall	GotoXY			; si posiziona
;
	mov	r0,#3			; offset primo carattere
	mov	a,RxBufLen		; recupera dimensione buffer
	add	a,#-3			; toglie 'comando', 'colonna' e 'riga'
	mov	r1,a			; r1 funge da contatore
;
;	Loop scrittura caratteri
;
WrGLp:
	mov	DPH,#RxBufPage		; Prepara parte alta puntatore
	mov	DPL,R0			; parte bassa
	inc	R0			; incrementa puntatore
	movx	a,@DPTR			; recupera carattere
	mov	DPTR,#DREG		; ripunta DPTR per operazioni con caratteri
	acall	outDato			; invia il carattere letto
	djnz	r1,WrGLp		; loop fino a che non ha finito
;
EndWrG:
	mov	DPTR,#DREG
	acall	SendConf		; Conferma il comando
;
	sjmp	nomess
;
RqIdQ:
	cjne	a,#44h,nomess		; Ritorna Identificazione
;
;	Risponde con stringa di versione
;
	mov	r0,#TxRispBuff		; Punta al buffer destinazione
	mov	DPTR,#IdStr		; Punta alla stringa da copiare
	mov	R1,#0			; Inizializza contatore
RqIdLp:
	mov	a,R1			; Contatore = offset nella stringa
	movc	a,@DPTR+a		; Legge il carattere
	mov	@r0,a			; lo copia nel buffer
	inc	R0			; incrementa il puntatore
	inc	R1			; incrementa l'indice
	jnz	RqIdLp			; Continua fino a che non ha copiato lo 0
	mov	DPTR,#DREG		; Riposiziona il registro dati
	mov	a,R1			; mette in a la lunghezza del buffer
	acall	SendPkt			; Invia il pacchetto di risposta
;
nomess:
	jb	DeBouncheTO,nobutton	; Se il timeout non e' scaduto, non ricontrolla
;
	mov	DPL,#2
	movx	A,@DPTR		; Legge i tasti
	mov	DPL,#0		; Ripunta ai dati
	swap	a		; Mette i tasti nella parte bassa
	cpl	a		; inverte il dato (1 = tasto premuto)
	anl	a,#0fh		; Scara i dati 'non tastiera'
;
	mov	r1,a		; salva il dato della tastiera
	xrl	a,CurButtons	; controlla se il contenuto e' cambiato
	jz	nobutton	; no, nessuna azione
;
;	Evento bottoni !!!
;
	setb	DeBouncheTO	; Indica che il timeout e' in corso
	mov	DeBounceTout,#20 ; imposta timeout di 100 mS (20 * 5)
	mov	TxCommBuf,#16h	; comando 'evento tastiera'
	mov	a,R1		; recupera stato tasti
	mov	CurButtons,a	; aggiorna stato
	mov	TxCommTStat,a	; prepara il dato dei tasti
	mov	a,#2		; lunghezza dato
	acall	SendCommand	; Invia il comando
;
nobutton:
;
	ajmp	mainlp
;
; Identificazione: preceduto dal codice della risposta, 38 Caratteri con il 'null' ed il comando
IdStr:
	.db	0c4h, "Display~31 - {Stefano Salvi} :v.1,0",0
;
; Generatore di caratteri: set ASCII completo - 128 codici - matrice 5*7 (5*8 con le discendenti)
; i caratteri sono ruotati (sono orizzontali, con l'alto a destra).
; MSB e' la 'discendente'
;
chargen:
; 00 NU
	.db	0001111b
	.db	0111010b
	.db	1001111b
	.db	1000000b
	.db	0111000b
	.db	0, 0, 0
; 01 SH
	.db	0010010b
	.db	0010101b
	.db	1111101b
	.db	0010000b
	.db	1111100b
	.db	0, 0, 0
; 02 SX
	.db	0010010b
	.db	0010101b
	.db	1001001b
	.db	0110000b
	.db	1001000b
	.db	0, 0, 0
; 03 EX
	.db	0011111b
	.db	0010101b
	.db	1011101b
	.db	0110000b
	.db	1001000b
	.db	0, 0, 0
; 04 ET
	.db	0011111b
	.db	0010101b
	.db	0011101b
	.db	1111000b
	.db	0001000b
	.db	0, 0, 0
; 05 EQ
	.db	0011111b
	.db	0010101b
	.db	0110101b
	.db	1101000b
	.db	0110000b
	.db	0, 0, 0
; 06 AK
	.db	0011110b
	.db	0000101b
	.db	1111110b
	.db	0100000b
	.db	1010000b
	.db	0, 0, 0
; 07 BL
	.db	0011111b
	.db	0010101b
	.db	1111010b
	.db	1000000b
	.db	1000000b
	.db	0, 0, 0
; 08 BS
	.db	0011111b
	.db	0010101b
	.db	1011010b
	.db	1010100b
	.db	0100100b
	.db	0, 0, 0
; 09 HT
	.db	0011111b
	.db	0000100b
	.db	0011111b
	.db	1111000b
	.db	0001000b
	.db	0, 0, 0
; 0A LF
	.db	0011111b
	.db	0010000b
	.db	1111100b
	.db	0010100b
	.db	0000100b
	.db	0, 0, 0
; 0B VT
	.db	0001111b
	.db	0010000b
	.db	0001111b
	.db	1111100b
	.db	0000100b
	.db	0, 0, 0
; 0C FF
	.db	0011111b
	.db	0000101b
	.db	1111101b
	.db	0010100b
	.db	0000100b
	.db	0, 0, 0
; 0D CR
	.db	0001110b
	.db	0010001b
	.db	1111101b
	.db	0010100b
	.db	1101000b
	.db	0, 0, 0
; 0E SO
	.db	0010010b
	.db	0010101b
	.db	0111101b
	.db	1000100b
	.db	0111000b
	.db	0, 0, 0
; 0F SI
	.db	0010010b
	.db	0010101b
	.db	1001101b
	.db	1111100b
	.db	1000100b
	.db	0, 0, 0
; 10 DL
	.db	0011111b
	.db	0010001b
	.db	1111110b
	.db	1000000b
	.db	1000000b
	.db	0, 0, 0
; 11 D1
	.db	0011111b
	.db	0010001b
	.db	1001110b
	.db	1111100b
	.db	1001000b
	.db	0, 0, 0
; 12 D2
	.db	0011111b
	.db	0010001b
	.db	1001110b
	.db	1100100b
	.db	1011000b
	.db	0, 0, 0
; 13 D3
	.db	0011111b
	.db	0010001b
	.db	1001110b
	.db	1010100b
	.db	0101000b
	.db	0, 0, 0
; 14 D4
	.db	0011111b
	.db	0010001b
	.db	0101110b
	.db	1111100b
	.db	0110000b
	.db	0, 0, 0
; 15 NK
	.db	0001111b
	.db	0000010b
	.db	1111111b
	.db	0100000b
	.db	1010000b
	.db	0, 0, 0
; 16 SY
	.db	0010010b
	.db	0010101b
	.db	0001001b
	.db	1110000b
	.db	0001000b
	.db	0, 0, 0
; 17 EB
	.db	0011111b
	.db	0010101b
	.db	1111101b
	.db	1010100b
	.db	0101000b
	.db	0, 0, 0
; 18 CN
	.db	0001110b
	.db	0010001b
	.db	1111001b
	.db	0010000b
	.db	1111000b
	.db	0, 0, 0
; 19 EM
	.db	0011111b
	.db	0010101b
	.db	1111101b
	.db	0011000b
	.db	1111100b
	.db	0, 0, 0
; 1A SB
	.db	0010010b
	.db	0010101b
	.db	1111101b
	.db	1010100b
	.db	0101000b
	.db	0, 0, 0
; 1B EC
	.db	0011111b
	.db	0010101b
	.db	0111001b
	.db	1000100b
	.db	1000100b
	.db	0, 0, 0
; 1C FS
	.db	0011111b
	.db	0000101b
	.db	1011001b
	.db	1010100b
	.db	0100100b
	.db	0, 0, 0
; 1D GS
	.db	0001110b
	.db	0010001b
	.db	1011101b
	.db	1010100b
	.db	0100100b
	.db	0, 0, 0
; 1E RS
	.db	0011111b
	.db	0000101b
	.db	1011010b
	.db	1010100b
	.db	0100100b
	.db	0, 0, 0
; 1F US
	.db	0000111b
	.db	0001000b
	.db	1001111b
	.db	1010100b
	.db	0100100b
	.db	0, 0, 0
;
; 20 
	.db	0000000b
	.db	0000000b
	.db	0000000b
	.db	0000000b
	.db	0000000b
	.db	0, 0, 0
; 21 !
	.db	0000000b
	.db	0000000b
	.db	1011111b
	.db	0000000b
	.db	0000000b
	.db	0, 0, 0
; 22 "
	.db	0000000b
	.db	0000111b
	.db	0000000b
	.db	0000111b
	.db	0000000b
	.db	0, 0, 0
; 23 #
	.db	0010100b
	.db	1111111b
	.db	0010100b
	.db	1111111b
	.db	0010100b
	.db	0, 0, 0
; 24 $
	.db	0100100b
	.db	0101010b
	.db	1101011b
	.db	0101010b
	.db	001001b
	.db	0, 0, 0
; 25 %
	.db	1100011b
	.db	0010011b
	.db	0001000b
	.db	1100100b
	.db	1100011b
	.db	0, 0, 0
; 26 &
	.db	0110110b
	.db	1001001b
	.db	1010110b
	.db	0100000b
	.db	1010000b
	.db	0, 0, 0
; 27 '
	.db	0000000b
	.db	0000000b
	.db	0000111b
	.db	0000000b
	.db	0000000b
	.db	0, 0, 0
; 28 (
	.db	0000000b
	.db	0011100b
	.db	0100010b
	.db	1000001b
	.db	0000000b
	.db	0, 0, 0
; 29 )
	.db	0000000b
	.db	1000001b
	.db	0100010b
	.db	0011100b
	.db	0000000b
	.db	0, 0, 0
; 2A *
	.db	0101010b
	.db	0011100b
	.db	0111110b
	.db	0011100b
	.db	0101010b
	.db	0, 0, 0
; 2B +
	.db	0001000b
	.db	0001000b
	.db	0111110b
	.db	0001000b
	.db	0001000b
	.db	0, 0, 0
; 2C ,
	.db	0000000b
	.db	1011000b
	.db	0111000b
	.db	0000000b
	.db	0000000b
	.db	0, 0, 0
; 2D -
	.db	0001000b
	.db	0001000b
	.db	0001000b
	.db	0001000b
	.db	0001000b
	.db	0, 0, 0
; 2E .
	.db	0000000b
	.db	1100000b
	.db	1100000b
	.db	0000000b
	.db	0000000b
	.db	0, 0, 0
; 2F /
	.db	1100000b
	.db	0010000b
	.db	0001000b
	.db	0000100b
	.db	0000011b
	.db	0, 0, 0
; 30 0
	.db	0111110b
	.db	1010001b
	.db	1001001b
	.db	1000101b
	.db	0111110b
	.db	0, 0, 0
; 31 1
	.db	0000000b
	.db	1000010b
	.db	1111111b
	.db	1000000b
	.db	0000000b
	.db	0, 0, 0
; 32 2
	.db	1110010b
	.db	1001001b
	.db	1001001b
	.db	1001001b
	.db	1000110b
	.db	0, 0, 0
; 33 3
	.db	0100010b
	.db	1000001b
	.db	1001001b
	.db	1001001b
	.db	0110110b
	.db	0, 0, 0
; 34 4
	.db	0011000b
	.db	0010100b
	.db	0010010b
	.db	1111111b
	.db	0010000b
	.db	0, 0, 0
; 35 5
	.db	0100111b
	.db	1000101b
	.db	1000101b
	.db	1000101b
	.db	0111001b
	.db	0, 0, 0
; 36 6
	.db	0111100b
	.db	1001010b
	.db	1001001b
	.db	1001001b
	.db	0110000b
	.db	0, 0, 0
; 37 7
	.db	1100001b
	.db	0010001b
	.db	0001001b
	.db	0000101b
	.db	0000011b
	.db	0, 0, 0
; 38 8
	.db	0110110b
	.db	1001001b
	.db	1001001b
	.db	1001001b
	.db	0110110b
	.db	0, 0, 0
; 39 9
	.db	0000110b
	.db	1001001b
	.db	1001001b
	.db	0101001b
	.db	0011110b
	.db	0, 0, 0
; 3A :
	.db	0000000b
	.db	0110110b
	.db	0110110b
	.db	0000000b
	.db	0000000b
	.db	0, 0, 0
; 3B ;
	.db	0000000b
	.db	1011011b
	.db	0111011b
	.db	0000000b
	.db	0000000b
	.db	0, 0, 0
; 3C <
	.db	0001000b
	.db	0010100b
	.db	0100010b
	.db	1000001b
	.db	0000000b
	.db	0, 0, 0
; 3D =
	.db	0010100b
	.db	0010100b
	.db	0010100b
	.db	0010100b
	.db	0010100b
	.db	0, 0, 0
; 3E >
	.db	1000001b
	.db	0100010b
	.db	0010100b
	.db	0001000b
	.db	0000000b
	.db	0, 0, 0
; 3F ?
	.db	0000010b
	.db	0000001b
	.db	1011001b
	.db	0000111b
	.db	0000000b
	.db	0, 0, 0
; 40 @
	.db	0011100b
	.db	0101010b
	.db	1010101b
	.db	1010101b
	.db	1001110b
	.db	0, 0, 0
; 41 A
	.db	1111100b
	.db	0010010b
	.db	0010001b
	.db	0010010b
	.db	1111100b
	.db	0, 0, 0
; 42 B
	.db	1000001b
	.db	1111111b
	.db	1001001b
	.db	1001001b
	.db	0110110b
	.db	0, 0, 0
; 43 C
	.db	0111110b
	.db	1000001b
	.db	1000001b
	.db	1000001b
	.db	0100010b
	.db	0, 0, 0
; 44 D
	.db	1000001b
	.db	1111111b
	.db	1000001b
	.db	1000001b
	.db	0111110b
	.db	0, 0, 0
; 45 E
	.db	1111111b
	.db	1001001b
	.db	1001001b
	.db	1001001b
	.db	1000001b
	.db	0, 0, 0
; 46 F
	.db	1111111b
	.db	0001001b
	.db	0001001b
	.db	0001001b
	.db	0000001b
	.db	0, 0, 0
; 47 G
	.db	0111110b
	.db	1000001b
	.db	1000001b
	.db	1001001b
	.db	0111010b
	.db	0, 0, 0
; 48 H
	.db	1111111b
	.db	0001000b
	.db	0001000b
	.db	0001000b
	.db	1111111b
	.db	0, 0, 0
; 49 I
	.db	0000000b
	.db	1000001b
	.db	1111111b
	.db	1000001b
	.db	0000000b
	.db	0, 0, 0
; 4A J
	.db	0100000b
	.db	1000000b
	.db	1000000b
	.db	1000000b
	.db	0111111b
	.db	0, 0, 0
; 4B K
	.db	1111111b
	.db	0001000b
	.db	0010100b
	.db	0100010b
	.db	1000001b
	.db	0, 0, 0
; 4C L
	.db	1111111b
	.db	1000000b
	.db	1000000b
	.db	1000000b
	.db	1000000b
	.db	0, 0, 0
; 4D M
	.db	1111111b
	.db	0000010b
	.db	0001100b
	.db	0000010b
	.db	1111111b
	.db	0, 0, 0
; 4E N
	.db	1111111b
	.db	0000010b
	.db	0000100b
	.db	0001000b
	.db	1111111b
	.db	0, 0, 0
; 4F O
	.db	0111110b
	.db	1000001b
	.db	1000001b
	.db	1000001b
	.db	0111110b
	.db	0, 0, 0
; 50 P
	.db	1111111b
	.db	0001001b
	.db	0001001b
	.db	0001001b
	.db	0000110b
	.db	0, 0, 0
; 51 Q
	.db	0111110b
	.db	1000001b
	.db	1010001b
	.db	0100001b
	.db	1011110b
	.db	0, 0, 0
; 52 R
	.db	1111111b
	.db	0001001b
	.db	0011001b
	.db	0101001b
	.db	1000110b
	.db	0, 0, 0
; 53 S
	.db	0100010b
	.db	1000101b
	.db	1001001b
	.db	1010001b
	.db	0100010b
	.db	0, 0, 0
; 54 T
	.db	0000001b
	.db	0000001b
	.db	1111111b
	.db	0000001b
	.db	0000001b
	.db	0, 0, 0
; 55 U
	.db	0111111b
	.db	1000000b
	.db	1000000b
	.db	1000000b
	.db	0111111b
	.db	0, 0, 0
; 56 V
	.db	0000111b
	.db	0011000b
	.db	1100000b
	.db	0011000b
	.db	0000111b
	.db	0, 0, 0
; 57 W
	.db	1111111b
	.db	0100000b
	.db	0010000b
	.db	0100000b
	.db	1111111b
	.db	0, 0, 0
; 58 X
	.db	1100011b
	.db	0010100b
	.db	0001000b
	.db	0010100b
	.db	1100011b
	.db	0, 0, 0
; 59 Y
	.db	0000011b
	.db	0000100b
	.db	1111000b
	.db	0000100b
	.db	0000011b
	.db	0, 0, 0
; 5A Z
	.db	1100001b
	.db	1010001b
	.db	1001001b
	.db	1000101b
	.db	1000011b
	.db	0, 0, 0
; 5B [
	.db	0000000b
	.db	1111111b
	.db	1000001b
	.db	1000001b
	.db	0000000b
	.db	0, 0, 0
; 5C \
	.db	0000011b
	.db	0000100b
	.db	0001000b
	.db	0010000b
	.db	1100000b
	.db	0, 0, 0
; 5D ]
	.db	0000000b
	.db	1000001b
	.db	1000001b
	.db	1111111b
	.db	0000000b
	.db	0, 0, 0
; 5E ^
	.db	0000000b
	.db	0000010b
	.db	0000001b
	.db	0000010b
	.db	0000000b
	.db	0, 0, 0
; 5F _
	.db	1000000b
	.db	1000000b
	.db	1000000b
	.db	1000000b
	.db	1000000b
	.db	0, 0, 0
; 60 @
	.db	0000000b
	.db	0000001b
	.db	0000010b
	.db	0000100b
	.db	0000000b
	.db	0, 0, 0
; 61 a
	.db	0100000b
	.db	1010100b
	.db	1010100b
	.db	1010100b
	.db	1111000b
	.db	0, 0, 0
; 62 b
	.db	1111111b
	.db	1000100b
	.db	1000100b
	.db	1000100b
	.db	0111000b
	.db	0, 0, 0
; 63 c
	.db	0111000b
	.db	1000100b
	.db	1000100b
	.db	1000100b
	.db	1000100b
	.db	0, 0, 0
; 64 d
	.db	0111000b
	.db	1000100b
	.db	1000100b
	.db	1000100b
	.db	1111111b
	.db	0, 0, 0
; 65 e
	.db	0111000b
	.db	1010100b
	.db	1010100b
	.db	1010100b
	.db	0011000b
	.db	0, 0, 0
; 66 f
	.db	0000000b
	.db	0001000b
	.db	1111110b
	.db	0001001b
	.db	0000000b
	.db	0, 0, 0
; 67 g
	.db	0011000b
	.db	0100100b
	.db    10100100b
	.db    10100100b
	.db	1111100b
	.db	0, 0, 0
; 68 h
	.db	1111111b
	.db	0001000b
	.db	0001000b
	.db	0001000b
	.db	1110000b
	.db	0, 0, 0
; 69 i
	.db	0000000b
	.db	1000100b
	.db	1111101b
	.db	1000000b
	.db	0000000b
	.db	0, 0, 0
; 6A j
	.db	1000000b
	.db    10000000b
	.db    10000000b
	.db	1110100b
	.db	0000000b
	.db	0, 0, 0
; 6B k
	.db	0000000b
	.db	1111111b
	.db	0010000b
	.db	0101000b
	.db	1000100b
	.db	0, 0, 0
; 6C l
	.db	0000000b
	.db	1000001b
	.db	1111111b
	.db	1000000b
	.db	0000000b
	.db	0, 0, 0
; 6D m
	.db	1111100b
	.db	0000100b
	.db	0001000b
	.db	0000100b
	.db	1111000b
	.db	0, 0, 0
; 6E n
	.db	1111100b
	.db	0000100b
	.db	0000100b
	.db	0000100b
	.db	1111000b
	.db	0, 0, 0
; 6F o
	.db	0111000b
	.db	1000100b
	.db	1000100b
	.db	1000100b
	.db	0111000b
	.db	0, 0, 0
; 70 p
	.db    11111100b
	.db	0100100b
	.db	0100100b
	.db	0100100b
	.db	0011000b
	.db	0, 0, 0
; 71 q
	.db	0011000b
	.db	0100100b
	.db	0100100b
	.db	0100100b
	.db    11111100b
	.db	0, 0, 0
; 72 r
	.db	0000000b
	.db	1111100b
	.db	0001000b
	.db	0000100b
	.db	0000100b
	.db	0, 0, 0
; 73 s
	.db	1001000b
	.db	1010100b
	.db	1010100b
	.db	1010100b
	.db	0100100b
	.db	0, 0, 0
; 74 t
	.db	0000000b
	.db	0000100b
	.db	0111110b
	.db	1000100b
	.db	0000000b
	.db	0, 0, 0
; 75 u
	.db	0111100b
	.db	1000000b
	.db	1000000b
	.db	1000000b
	.db	0111100b
	.db	0, 0, 0
; 76 v
	.db	0011100b
	.db	0100000b
	.db	1000000b
	.db	0100000b
	.db	0011100b
	.db	0, 0, 0
; 77 w
	.db	0111100b
	.db	1000000b
	.db	0110000b
	.db	100000b
	.db	0111100b
	.db	0, 0, 0
; 78 x
	.db	1000100b
	.db	0101000b
	.db	0010000b
	.db	0101000b
	.db	1000100b
	.db	0, 0, 0
; 79 y
	.db	0011100b
	.db	0100000b
	.db    10100000b
	.db    10100000b
	.db	1111100b
	.db	0, 0, 0
; 7A z
	.db	1000100b
	.db	1100100b
	.db	1010100b
	.db	1001100b
	.db	1000100b
	.db	0, 0, 0
; 7B {
	.db	0000000b
	.db	0001000b
	.db	0110110b
	.db	1000001b
	.db	0000000b
	.db	0, 0, 0
; 7C |
	.db	0000000b
	.db	0000000b
	.db	1110111b
	.db	0000000b
	.db	0000000b
	.db	0, 0, 0
; 7D }
	.db	0000000b
	.db	1000001b
	.db	0110110b
	.db	0001000b
	.db	0000000b
	.db	0, 0, 0
; 7E ~
	.db	0000100b
	.db	0000010b
	.db	0000010b
	.db	0000010b
	.db	0000001b
	.db	0, 0, 0
; 7F //
	.db	0101010b
	.db	1010101b
	.db	0101010b
	.db	1010101b
	.db	0101010b
	.db	0, 0, 0
;

	

Per provare il programma, scaricare il sorgente, assemblarlo con il comando as31 protocol.asm, oppure scaricarlo direttamente asemblato (protocol.hex), e scaricarlo sulla scheda con il comando ./upload (dopo aver scaricato anche tale comando).

Il programma upload per scaricare il protocollo sulla scheda è il seguente:
/* upload.c
 * Stefano Salvi - 8/12/2002
 * Programma per scaricare sulla scheda GPC2 un file .hex da riga di comando.
 * per default scarica il file "protocol.hex" ed usa la seriale "/dev/ttyS0"
 * Accetta i seguenti paramentri:
 *  -f<file sa scaricare> indica che file '.hex' scaricare (default protocol.hex)
 *  -s<seriale> indica il percorso per il dispositivo seriale (default /dev/ttyS0)
 *  -d non far partire il file scaricato (scarica e basta)
 *  -r non scaricare alcun file (avvia il programma sulla scheda)
 */

#include <sched.h>
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

int com;        // handle della seriale

/* Invia una stringa sulla seriale, verso la scheda
 */
void schPuts (const char *s)
{
  write (com, s, strlen (s));
}

/* Invia un singolo carattere sulla seriale, alla scheda
 */
void schPutc (const char c)
{
  write (com, &c, 1);
}

/* Ritorna un carattere dalla pipe della scheda
 * con un timeout di 3 secondi
 * Se il carattere non arriva, ritorna -1;
 */
#define SCHWAIT        5
int schGetc ()
{
fd_set set;
struct timeval timeout;
unsigned char c;

  /* Initialize the file descriptor set. */
  FD_ZERO (&set);
  FD_SET (com, &set);
     
  /* Initialize the timeout data structure. */
  timeout.tv_sec = SCHWAIT;
  timeout.tv_usec = 0;
  /* `select' returns 0 if timeout, 1 if input available, -1 if error. */
  if (!select (FD_SETSIZE, &set, NULL, NULL, &timeout))
  {
    schPutc (0x1b);        // Se si e' interrotto per errore, interrompe il comando
    printf ("La scheda non risponde\n");
    exit (-1);
  }
  read (com,&c,1);
  return c;
}

/* Ritorna una linea letta dalla scheda (terminata da \r),
 * lunga al massimo 'max' caratteri.
 * Non inserisce il '\r' o '\n' nella stringa
 * Ritorna:
 *   true -> errore;
 *   false -> OK;
 */
int schGets (char *str,int max)
{
int rec;
  do {
    rec = schGetc ();
    if (rec != '\n' && rec != '\r')
      *str++ = rec;
  } while (rec >= 0 && rec != '\r' && --max);
  *str = 0;
  return rec < 0;
}

/* Attende un particolare carattere ('c') dalla scheda.
 * Se arriva il carattere 'c' ritorna 'false'.
 * Se avviene un timeout (nessun carattere per almeno 5 sec) ritorna 'true'
 */
int schWaitc (char c)
{
int rec;
  do {
    rec = schGetc ();
  } while (rec >= 0 && rec != c);
  return rec < 0;
}

/* Invia alla scheda un comando 'go' all'indirizzo 'where'
 */
void doRun (int where)
{
char gobuf [6];
int i;

    sprintf (gobuf,"G%04X",where);
    schPuts (gobuf);        // Comando 'go'
    // Risucchia l'eco dei cinque caratteri stampati
    for (i = 0; i<5; i++)
      if (schGetc () < 0)
        break;
}

/* Invia alla scheda un comando 'go' all'indirizzo 0x8050 (inizio programmi)
 */
void runStart ()
{
  doRun (0x8050);
}

/* Attende il prompt della scheda ('*').
 * Ritorna -1 se il prompt non arriva (timeout)
 * Ritorna 0 se il prompt arriva
 */
int schPrompt (void)
{ 
  if (schWaitc ('*')) {
    return -1;
  } 
  return 0; 
}

#define BUFLEN        512

/* Attende la fine del comando di upload (o di una sua parte)
 */
void waitEndOfCommand ()
{
char buf [BUFLEN];        // Buffer per leggere il file
  if (schWaitc ('\r') ||        // scarta il comando
    schGets (buf,79) ||
    schPrompt ())
  {
    fprintf (stderr, "La scheda non risponde\n");
    exit (3);
  } else {
    if (buf [0] == '?') {
      fprintf (stderr, "Errore nel file '%s'\n" , buf);
      exit (3);
    }
  }
}

/* Invia il file il cui nome e' '*file' (che deve essere in formato 'hex intel')
 * alla scheda
 */
void doUpload (char *file)
{
int hexfile;                // File handle del file da inviare
char buf [BUFLEN];        // Buffer per leggere il file
int letti;                // Dimensione dell'ultimo blocco letto
int i;                        // indice nel buffer
int l;                        // numero di linee
int f;                        // Indice della stringa trovata
char *endHex = ":00000001FF\r\n";
    
  hexfile = open (file,O_RDONLY);        // Apre il file
  if (hexfile == EOF)
  {
    fprintf (stderr, "Non trovo il file '%s' da scaricare\n", file);
    exit (3);
  }

  schPuts ("L\r");        // Comando 'upload'
  l = 0;
  f = 0;

  /* Ciclo di lettura del file e di invio alla scheda
   */
  do {
    letti = read (hexfile,buf,BUFLEN);        // Legge il prossimo blocco
    for (i = 0;i < letti;i++)                // Ciclo per i byte letti
    {
      if (buf [i] != '\r')                // Elimina gli eventuali \r (CR)
      {
        if (buf [i] == '\n') {                // Se incontra un \n (LF)
          write (com,"\r",1);                // Ci mette prima un \r (CR)
//          schPuts ("\r");
        }
        write (com,buf + i,1);                // Invia il carattere dal file
        if (endHex [f] != '\r')                // Se non ha ancora trovato al stringa
        {
          if (endHex [f] == buf [i])        // Se la stringa corrisponde ancora
          {
            f ++;                        // Va al prossimo carattere
          } else {
            f = 0;                        // Se no, torna a 0
          }
        }
        if (buf [i] == '\n') {                // Se ha spedito un \n
          if (l ++ > 30)                // Ogni 30 righe 'chiude e riapre'
          {
            l = 0;
            if (endHex [f] != '\r')     // Se non ha ancora inviato 'fine file hex'
            {
              write (com,endHex,strlen (endHex)); // Invia il carattere dal file
              waitEndOfCommand ();        // attendo conferma
              schPuts ("L\r");                // Nuovo comando 'upload'

              putchar ('+');
              fflush (stdout);
            } else {
              putchar ('*');
              fflush (stdout);
            }
          } else {
            if (endHex [f] == '\r')     // Se non ha ancora inviato 'fine file hex'
            {
              putchar ('*');
              fflush (stdout);
            } else {
              putchar ('.');
              fflush (stdout);
            }
          }
          usleep (10000);                // fa una pausa (la scheda e' lenta....)
          f = 0;
        }
      }
    }
  } while (letti==BUFLEN);                // Fintantoche' ha letto dati

  printf ("\nFine upload\n");
  fflush (stdout);

  waitEndOfCommand ();
}

/* Apre la seriale indicata dal parametro e la imposta in modo non canonico
 */
int setupCom (char *comname)
{
struct termios tattr;

    com = open (comname,O_RDWR | O_SYNC);
    if (com == -1)
    {
        perror ("Non posso aprire la seriale");
        exit (2);
    }

    /* Set the funny terminal modes. */
    tcgetattr (com, &tattr);
    tattr.c_iflag &= ~(INLCR|IGNCR|ICRNL|IXON); /*  */
    tattr.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET); /*  */
    tattr.c_cflag = CS8 | CREAD | CLOCAL; 
    tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
    tattr.c_cc[VMIN] = 1;
    tattr.c_cc[VTIME] = 0;
    cfsetispeed (&tattr, B9600);
    cfsetospeed (&tattr, B9600);
    tcsetattr (com, TCSAFLUSH, &tattr);

    return com;
}

char fileToLoad [] = "protocol.hex";
char ttyName [] = "/dev/ttyS0";

/* Stampa messaggio di errore per i parametri e termina
 */
void parerr (char *nome)
{
  fprintf (stderr, "Errore. Uso:\n%s [-f<file sa scaricare>] [-s<seriale>] [-d] [-r]\n"
    "-f<file sa scaricare> indica che file '.hex' scaricare (default %s)\n"
    "-s<seriale> indica il percorso per il dispositivo seriale (default %s)\n"
    "-d non far partire il file scaricato (scarica e basta)\n"
    "-r non scaricare alcun file (avvia il programma sulla scheda)\n",
    nome, fileToLoad, ttyName);
  exit (1);
}

/* Programma principale */
int main (int argc, char **argv)
{
int i;
int download = -1;                // Se 'true', esegue il download
int start = -1;                        // Se 'true', avvia il programma
char *toLoad = fileToLoad;        // Nome del file da scaricare
char *ttyN = ttyName;                // Nome del dispositivo della seriale

  // Loop di analisi dei parametri
  for (i = 1; i < argc; i++) {
    if (argv [i][0] == '-') {
      switch (argv [i][1]) {
        case 'f' :
          toLoad = argv [i] + 2;
          break;
        case 's' :
          ttyN = argv [i] + 2;
          break;
        case 'd' :
          start = 0;
          break;
        case 'r' :
          download = 0;
          start = -1;
          break;
        default :
          parerr (argv [0]);
      }
    } else {
      parerr (argv [0]);
    }
  }

  com = setupCom (ttyN);                // Imposta la seriale

  if (download) {
    doUpload (toLoad);                        // Se richieso, scarica il file nella scheda
  }

  if (start) {
    runStart ();                        // Se richiesto, fa paritre il programma sulla scheda
  }

  return 0;
}

Per provare il programma, scaricare il sorgente, compilarlo con il comando cc upload.c -o upload ed eseguirlo con il comando ./upload.

È anche possibile scaricare direttamente l'archivio protocol.tgz (20147 byte) che contiene i due programmi sia in forma sorgente che eseguibile. L'archivio si decomprime con il comando tar -xvzf protocol.tgz


[Home Page dell'ITIS "Fermi"] [Indice Quinta] [Precedente]

© Ing. Stefano Salvi - Released under GPL licence