Anschrift eMail
Assembler-Beispiele für 80C166-Controller
Hard-/Software-Deklarationen
Ram-Initialisierung
Int-Vectoren initialisieren
Int-Vector setzen
Interrupts initialisieren
Traps initialisieren
Traps

ISR für falschen Int
ISR 1 MilliSekunde
ISR von Eingang
ISR Seriell TxD
ISR Seriell RxD
Programm-Start
Haupt-Programm-Anfang
Haupt-Programm-Schleife

Word, Byte, Nibl senden
String, Zeichen senden
Return/LineFeed senden
Leerzeichen senden

Timer-Routine 1 ms
Timer-Routine 10 ms
Timer-Routine 100 ms
Timer-Routine 1 Sek
Timer-Variablen decrementieren bis 0
Include-Datei
Variablen-Deklarationen
Haupt-Programm
Interrupt-Service-Routinen ISR
Linker-Datei

Register-Bänke deklarieren
Bits deklarieren
Variablen deklarieren

Section mit diversen Texten
Section mit Daten und Tabellen
Section mit Fehler-Texten
Section mit Tabellen
Das Prinzip des Programms:
Jede Millisekunde wird von Timer 3 ein Interrupt ausgelöst. In der ISR werden unterschiedliche Zähler-Variablen aktualisiert (1, 10, 100, 1000 ms) und Zeit-Variablen bis auf 0 dekrementiert. Damit stehen eine Vielzahl unterschiedlicher "Software-Timer" zu Verfügung.

Dieses Programm kann mit einem ASCII-Terminal-Programm kommunizieren. Die Kommunikation erfolgt über die asynchrone serielle Schnittstelle des 80C166 mit 9600 Baud, 8 Bit, ohne Parität.
Alle ankommenden Bytes lösen einen Interrupt aus und werden von der ISR in einem Empfangs-Ring-Puffer abgelegt. Von dort können sie "bei Gelegenheit" entnommen und weiter verarbeitet werden.
Ausgabe-Bytes werden in einem Sende-Ring-Puffer abgelegt. Alle 0,93 ms wird in der ISR überprüft, ob Bytes zum Versenden abgelegt wurden und ggf. an die USART übergeben.
Include-Datei "BAS_DEF.A66"
	$NOPAGING
	$PL	(68)
	$PW	(132)
	$TABS	(8)
	$NOCASE

	$MOD166
	$SEGMENTED

; =============================================================
; Register-Bänke
; =============================================================
REG_Mon		REGDEF R0-R15	; Registerbank für Monitor-Programm
REG_1ms		REGDEF R0-R15	; Registerbank für 1 ms-Interrupt

; =============================================================
; Port-Adressen (Decoder) von 3$8000h - 3$B800h
; =============================================================
A_IO		EQU	3$8000h

CS0		EQU	3$8000h
CS1		EQU	3$8800h
CS2		EQU	3$9000h
CS3		EQU	3$9800h
CS4		EQU	3$A000h
CS5		EQU	3$A800h
WatchDog	EQU	3$B000h	; WatchDog
UhrPort		EQU	3$B800h	; Uhrenbaustein RTC 27241

; =============================================================
; Deklarationen
; =============================================================
SSKDEF		1		; 128 Word Stacksize

BOS		EQU	0FC00h	; Bottom of Stack
TOS		EQU	0FB00h	; Top of Stack
SPAdr		EQU	BOS-2	; Stackpointer-Adresse
TrapAdr		EQU	0FA00h	; Puffer-Adresse für HW-Traps

VN_Sio		EQU	21h	; (T1) Sio-Interrupt
VN_1ms		EQU	23h	; (T3) 1 ms-Interrupt
VN_TxD		EQU	2Ah	; TxD-Interrupt
VN_RxD		EQU	2Bh	; RxD-Interrupt

LenRxD		EQU	508	; Länge RxD-Puffer
LenTxD		EQU	1300	; Länge TxD-Puffer

WaitEEP		EQU	6	; ms Wartezeit nach EEPROM-Write

Lf		EQU	10	; LineFeed
Cr		EQU	13	; Return
Lz		EQU	32	; Leerzeichen

Dip1		EQU	P3.4	; Dip-Schalter
Dip2		EQU	P3.5
Dip3		EQU	P3.6
Dip4		EQU	P3.7
Variablen-Datei "BAS_VAR.A66"
$INCLUDE	(BAS_DEF.A66)

; #############################################################
S_BIT	SECTION DATA BITADDRESSABLE PUBLIC

; ---------------------------------------------------------------
; Bereich für MONitor-Daten
; ---------------------------------------------------------------
MONBANK		DSW	16	; FD00 Registerbank für MON166
PrgRegBank	DSW	24	; FD20 Registerbank des Monitor-Hauptprogramms

; ---------------------------------------------------------------
; Bereich fuer User-Bits
; ---------------------------------------------------------------
PUBLIC	FLG_DIV
PUBLIC	F_Init,    F_OK
FLG_DIV		DSW	1	; Bitadressierbares Word

F_Init		BIT	FLG_DIV.0
F_OK		BIT	FLG_DIV.15

S_BIT	ENDS

; #############################################################
S_VAR	SECTION DATA PUBLIC

; ---------------------------------------------------------------
; Bereich fuer User-Variablen
; ---------------------------------------------------------------
PUBLIC	ZLR_1,     ZLR_10,    ZLR_100,   ZLR_1S
ZLR_1		DSW	1	; Incrementierer    1 ms
ZLR_10		DSW	1	; Decrementierer   10 ms
ZLR_100		DSW	1	; Decrementierer  100 ms
ZLR_1S		DSW	1	; Decrementierer    1 Sek

PUBLIC	TIM_1,     TIM_10,    TIM_100,   TIM_1S
TIM_1		DSW	1	;   1 ms-Timer
TIM_10		DSW	1	;  10 ms-Timer
TIM_100		DSW	1	; 100 ms-Timer
TIM_1S		DSW	1	;   1 Sek-Timer

PUBLIC	ZLR_Inp
ZLR_Inp		DSW	1	; Zähler von Eingang 1 / 2

PUBLIC	EINGP1,    AUSGP1
EINGP1		DSW	1	; Aktueller Zustand der Eingänge
AUSGP1		DSW	1	; Soll-Zustand der Ausgänge

PUBLIC	PNT_RXD,   POS_RXD,   PUF_RXD
PNT_RXD		DSW	1	; RxD Pointer Empfangs-Daten
POS_RXD		DSW	1	; RxD Position Empfangs-Daten
PUF_RXD		DSB	LenRxD	; RxD Puffer
EVEN

PUBLIC	PNT_TXD,   POS_TXD,   PUF_TXD
PNT_TXD		DSW	1	; TxD Pointer Sende-Daten
POS_TXD		DSW	1	; TxD Position Sende-Daten
PUF_TXD		DSB	LenTxD	; TxD Puffer
EVEN

S_VAR	ENDS

END
Hauptprogramm-Datei "BASIS.A66"
; =============================================================
; .... Programm-Beschreibung
; =============================================================
	$TITLE		("Titel des Programms")

$INCLUDE	(BAS_DEF.A66)

NAME		BASIS

PUBLIC	SendStr,	SendChr,	SendCrLf
PUBLIC	SendNibl,	SendWord
PUBLIC	Tim_1ms

EXTERN	InitTrap :near,	InitInt  :near,	InitIntVect:near

; #############################################################

S_BIT	SECTION DATA BITADDRESSABLE PUBLIC

; ---------------------------------------------------------------
; Externe Bits
; ---------------------------------------------------------------
EXTERN	FLG_DIV :word

EXTERN	F_Init  :bit
EXTERN	F_OK    :bit

S_BIT	ENDS

; #############################################################
S_VAR	SECTION DATA PUBLIC

; ---------------------------------------------------------------
; Externe Variablen
; ---------------------------------------------------------------
EXTERN	ZLR_1   :word,	ZLR_10  :word,	ZLR_100 :word,	ZLR_1S  :word
EXTERN	TIM_1   :word,	TIM_10  :word,	TIM_100 :word,	TIM_1S  :word
EXTERN	ZLR_Inp :word
EXTERN	EINGP1  :word,	AUSGP1  :word

EXTERN	PNT_RXD :word,	POS_RXD :word,	PUF_RXD :byte
EXTERN	PNT_TXD :word,	POS_TXD :word,	PUF_TXD :byte

S_VAR	ENDS

; #############################################################

PAGE3		DGROUP SYSTEM, S_BIT, S_VAR
ASSUME		DPP0:NOTHING
ASSUME		DPP1:NOTHING
ASSUME		DPP2:NOTHING
ASSUME		DPP3:PAGE3

; #############################################################

S_COD	SECTION CODE PUBLIC

P_INI	PROC NEAR

		DISWDT			; Watchdog abschalten
		MOV	DPP0,#0		; Page 0 = 0$0000h - 0$3FFFh
		MOV	DPP1,#1		; Page 1 = 0$4000h - 0$7FFFh
		MOV	DPP2,#2		; Page 2 = 0$8000h - 0$BFFFh
		MOV	DPP3,#3		; Page 3 = 0$C000h - 0$FA00h
					;  und System-Speicher

		MOV	STKUN,#BOS	; Stack-Untergrenze setzen
		MOV	STKOV,#TOS	; Stack-Obergrenze setzen
		MOV	SP,#SPAdr	; Stack-Pointer setzen

		; -------------------------------------
		; SYSCON Register
		; -------------------------------------
		BSET	P3.12		; BHE mit 1 vorbesetzen
		BSET	DP3.12		; BHE = Ausgang

		BSET	P3.13		; WRITE mit 1 vorbesetzen
		BSET	DP3.13		; WRITE = Ausgang

	;;	BSET	DP3.15		; P3.15 = 1 = Ausgang CLK

		BFLDL	SYSCON,#0001$1111b,#0010$1110b
			; BTYP  00|| |  |   00|| |  | = xx
			; MTTC    0| |  |     1| |  | = 1 (no Delay)
			; RWDC     1 |  |      0 |  | = 0 (disabled)
			; MCTC       1111        1110 = 1110 (1 WS)

		BFLDH		SYSCON,#0111$1011b,#0010$0000b
			; STKSZ  11| ||||    01| |||| = 01 (128 Word)
			; RDYEN    1 ||||      0 |||| = 0 (aus)
			; SGTDIS     0|||        0||| = x
			; BUSACT      0||         0|| = x
			; BYTDIS       1|          0| = 0 (enabled)
			; CLKEN         1           0 = 0 (disabled)

		; -------------------------------------
		; BUSCON1 Register
		; Initialisierung für EEPROM und I/O im Segment 3
		; mit 3 Wait-States = 250 ns
		; -------------------------------------

		MOV	ADDRSEL1,#000000$1100000$011b
			; Beginn         11----- ||| Ab Adr. 3$xxxx
			; Länge                  011 64 kB
			; Ab Adresse 3$0000h  64kB mit n Wait-States

		MOV	BUSCON1,SYSCON
		BFLDL	BUSCON1,#0011$1111b,#0010$1100b
			; BTYP   00|| |  |   00|| |  | = xx
			; MTTC1    1| |  |     1| |  | = 1 (no Delay)
			; RWDC1     1 |  |      0 |  | = 0 (disabled)
			; MCTC        1111        1100 = 1100 (3 WS)

		BFLDH	BUSCON1,#0001$0110b,#0000$0100b
			;(STKSZ)  00| ||||    00| |||| = xx
			; RDYEN1    1 ||||      0 |||| = 0 (disabled)
			;(SGTDIS)     0|||        0||| = x
			; BUSACT1      1||         1|| = 1 (enabled)
			; ALECTL1       1|          0| = 0 (disabled)
			;(CLKEN)         0           0 = x

		; -------------------------------------
		; Port 3
		; P3.0 = T0IN   E 0     15a    Eingang T0
		; P3.1 = T6OUT  A 1     15bc   Ausgang T6
		; P3.2 = CAPIN  E 0     14a    Eingang CAP
		; P3.3 = T3OUT  A 1     14bc   Ausgang T3
		; P3.4 = T3EUD  E 0     13a    Dip-Switch 1
		; P3.5 = T4IN   E 0     13bc   Dip-Switch 2
		; P3.6 = T3IN   E 0     12a    Dip-Switch 3
		; P3.7 = T2IN   E 0     12bc   Dip-Switch 4
		;*P3.8 = TXD1   A x Mon  2bc   TxD Monitor
		;*P3.9 = RXD1   E x Mon  3bc   RxD Monitor
		; P3.10= TXD0   A 1      5bc   TxD Programm
		; P3.11= RXD0   E 0      6bc   RxD Programm
		;*P3.12= BHE#   A x Sys BHE# (ist bereits gesetzt)
		;*P3.13= WR#    A x Sys WR#  (ist bereits gesetzt)
		;*P3.14= READY# E ? Sys READY#
		;*P3.15= CLK    A ? Sys 10bc (20 MHz)
		; -------------------------------------
		OR	P3, #0000$0100$0000$1010b ; Port 3 vorbesetzen
		OR	DP3,#0000$0100$0000$1010b ; 1 = Ausgang / 0 = Eingang

		; -------------------------------------
		; Port 2
		; P2.0 = CC0    E 0     20a    Eingang 1
		; P2.1 = CC1    E 0     20bc   Eingang 2
		; P2.2 = CC2    E 0     19a    Eingang 3
		; P2.3 = CC3    E 0     19bc   Eingang 4
		; P2.4 = CC4    E 0     18a    Eingang 5
		; P2.5 = CC5    E 0     18bc   Eingang 6
		; P2.6 = CC6    E 0     17a    Eingang 7
		; P2.7 = CC7    E 0     17bc   Eingang 8
		; P2.8 = CC8    E 0     16a    Eingang 9
		; P2.9 = CC9    E 0     16bc   Eingang 10
		; P2.10= CC10   E 0      8bc   SCLK (bidir)
		; P2.11= CC11   E 0     (6a)   CTS0
		;*P2.12= CC12   E 0 Mon (3a)   CTS1
		; P2.13= BREQ#  A 1     10a    Ausgang
		; P2.14= HLDA#  A 1     11bc   Ausgang
		; P2.15= HOLD#  A 1     11a    Ausgang
		; -------------------------------------
		MOV	P2, #1110$0000$0000$0000b ; Port 2 vorbesetzen
		MOV	DP2,#1110$0000$0000$0000b ; 1 = Ausgang / 0 = Eingang

		; -------------------------------------
		; Port 1
		; P1.0 = A0     A 1     31a    Ausgang 1
		; P1.1 = A1     A 1     31bc   Ausgang 2
		; P1.2 = A2     A 1     30a    Ausgang 3
		; P1.3 = A3     A 1     30bc   Ausgang 4
		; P1.4 = A4     A 1     29a    Ausgang 5
		; P1.5 = A5     A 1     29bc   Ausgang 6
		; P1.6 = A6     A 1     28a    Ausgang 7
		; P1.7 = A7     A 1     28bc   Ausgang 8
		; P1.8 = A8     A 1     27a    Ausgang 9
		; P1.9 = A9     A 1     27bc   Ausgang 10
		; P1.10= A10    E 0      8bc   SDAT (bidir.)
		; P1.11= A11    A 1     (5bc)  RTS0
		;*P1.12= A12    A 1 Mon (2a)   RTS1
		; P1.13= A13    A 1     74HC123
		; P1.14= A14    E 0     EEPROM Ready 1
		; P1.15= A15    E 0     EEPROM Ready 2
		; -------------------------------------
		MOV	P1, #0010$1011$1111$1111b ; Port 1 vorbesetzen
		MOV	DP1,#0010$1011$1111$1111b ; 1 = Ausgang / 0 = Eingang

		; -------------------------------------
		; SIO 0 initialisieren
		; (SIO 1 wird vom Monitor-Programm benutzt)
		; -------------------------------------
		MOV	S0BG,#0040h	; = 9615 Baud
		MOV	S0CON,#10000000$00010001b
			;      |           ||001 Asynchron 8 Bit
			;      |           |0    1 Stopbit
			;      |           1     Empfang ein
			;      1                 Baudrate-Generator ein
		BSET	S0TIR	; Sendepuffer leer

	; .... Weitere Initialisierungen

		JMP	FAR ProgAnf	; Programm auf SEG X

P_INI	ENDP


; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
P_COD	PROC NEAR

; =============================================================
; Haupt-Programm
; =============================================================
ProgAnf:	EINIT			; RSTOUT# = 1 (ROM-Spiegelung aus)
		CALL	InitRam		; Init Variablen
		CALL	InitTrap	; Init Hardware-Traps
		CALL	InitIntVect	; Init Interrupt-Vektoren
		CALL	InitInt		; Init Interrupts

	; .... weitere Initialisierungen

		; -------------------------------------
		; Interrupts starten
		; -------------------------------------
		BCLR	T1IR		; Sio-Request rücksetzen
		BSET	T1IE		; Sio-Interrupt freigeben
		BSET	T1R		; Timer 1 starten

		BCLR	T3IR		; 1 ms-Request rücksetzen
		BSET	T3IE		; 1 ms-Interrupt freigeben
		BSET	T3R		; Timer 3 starten
		NOP
		BSET	IEN		; Alle Interrupts zulassen
					; RxD-Interrupt
		MOV	S0RIC,#00000000$00$0001$11b ; mit Priorität 1.3
		BCLR	S0RIR		; RxD-Request rücksetzen
		BSET	S0RIE		; RxD-Interrupt freigeben

; =============================================================
; Haupt-Programm Schleife
; =============================================================
ProgLoop:	MOV	R1,PNT_RXD	; Byte empfangen ?
		CMP	R1,POS_RXD
		JMP  cc_Z,ProgLoop1	; ... nein
		MOVB	RL0,[R1+#PUF_RXD] ; Byte laden
		CMPI1	R1,#LenRxD-1	; Offset + 1 = Ende ?
		JMP  cc_NZ,Ausw_RxD1	; ... nein
		MOV	R1,#0		; Wieder bei 0 beginnen
Ausw_RxD1:	MOV	PNT_RXD,R1	; Neuen Offset speichern

	; .... Byte auswerten

ProgLoop1:

	; .... Zeitunkritische Anwender-Routinen

		JMP	ProgLoop	; Zum Schleifen-Anfang

; =============================================================
; Variablen initialisieren
; =============================================================
InitRam:	SCXT	R1,#1		; R1=1

		MOV	FLG_DIV,ZEROS
		MOV	ZLR_1,ZEROS	; Zähler setzen
		MOV	ZLR_10,R1
		MOV	ZLR_100,R1
		MOV	ZLR_1S,R1

		MOV	TIM_1,ZEROS	; Timer löschen
		MOV	TIM_10,ZEROS
		MOV	TIM_100,ZEROS
		MOV	TIM_1S,ZEROS

		MOV	PNT_RXD,ZEROS	; Zeiger löschen
		MOV	POS_RXD,ZEROS
		MOV	PNT_TXD,ZEROS
		MOV	POS_TXD,ZEROS

		MOV	ZLR_Inp,ZEROS	; Zähler löschen
		MOV	AUSGP1,ZEROS	; Alle Ausgänge aus

		POP	R1
		RET

; =============================================================
; Word / Byte / Nibl als Hex-Zahl ausgeben
; Eingang:	R0	Wert
; =============================================================
SendWord:	PUSH	R0
		MOVB	RL0,RH0		; High-Byte ausgeben
		CALL	SendByte
		POP	R0		; Low-Byte ausgeben

; -----------------------------------------------
SendByte:	PUSH	R0
		SHR	R0,#4		; Bits 4...7
		CALL	SendNibl
		POP	R0		; Bits 0...3

; -----------------------------------------------
SendNibl:	PUSH	R0
		ANDB	RL0,#0Fh	; Bits 0...3 maskieren
		ADDB	RL0,#'0'	; + ASCII-0
		CMPB	RL0,#'9'	; 0...9 ?
		JMP  cc_ULE,SendNibl1	; ... ja
		ADDB	RL0,#7		; = A...F
SendNibl1:	CALL	SendChr
		POP	R0
		RET

; =============================================================
; String an SIO ausgeben (Stringende = 0)
; Eingang:	R2/R3	PP Stringadresse
; Ausgang:	R2/R3	PP + Len String
; =============================================================
SendStr:	PUSH	DPP0
		PUSH	R0
		MOV	DPP0,R3		; DPP0 setzen

SendStr1:	MOVB	RL0,[R2+]	; Byte laden; String-Ende ?
		JMP  cc_Z,SendStrE	; ... ja
		CALL	SendChr		; Byte ausgeben
		JMP	SendStr1

SendStrE:	POP	R0
		POP	DPP0
		RET

; =============================================================
; Byte ausgeben
; =============================================================
SendChr:	PUSH	R1
		MOV	R1,PNT_TXD	; Pointer laden
		MOVB	[R1+#PUF_TXD],RL0 ; Byte ablegen
		CMPI1	R1,#LenTxD-1	; Puffer-Ende ?
		JMP  cc_NZ,SendChr9	; ... nein
		MOV	R1,#0		; Mit 0 beginnen
SendChr9:	MOV	PNT_TXD,R1	; Pointer speichern
		POP	R1
		RET

; =============================================================
; Return und Linefeed ausgeben
; =============================================================
SendCrLf:	PUSH	R0
		MOVB	RL0,#Cr		; Return laden
		CALL	SendChr		;  und ausgeben
		MOVB	RL0,#Lf		; LineFeed laden
		JMP	SendChrX	;  und ausgeben

; =============================================================
; 1 Leerzeichen ausgeben
; =============================================================
SendLz:		PUSH	R0
		MOVB	RL0,#Lz		; Leerzeichen laden

; - - - - - - - - - - - - - - - - - - - - - - - -
SendChrX:	CALL	SendChr		;  und ausgeben
		POP	R0
		RET


; *************************************************************
; TIMER-ROUTINEN
; *************************************************************
; =============================================================
; Timer 1 ms
; =============================================================
Tim_1ms:	SUB	ZLR_1,ONES	; 1 ms-Zähler + 1
		ADD	ZLR_10,ONES	; 10 ms-Zähler - 1 abgelaufen ?
		CALL  cc_Z,Tim_10ms	; ... ja
		MOV	R2,#POF TblTim1	; R2/R3 = Tabellen-Adresse
		MOV	R3,#PAG TblTim1
		CALL	DecTimer	; Timer testen / decrementieren

		MOV	R0,P2		; Eingänge aktualisieren
		AND	R0,#000000$1111111111b
		MOV	EINGP1,R0

		MOV	R0,AUSGP1	; Ausgänge aktualisieren
		CPL	R0		; Ein = 0 / Aus = 1 (Reset-Zustand)
		AND	R0,#000000$1111111111b
		MOV	R1,P1		; Port-Zustand laden
		AND	R1,#111111$0000000000b
		OR	R1,R0
		MOV	P1,R1		; Ausgänge neu setzen

	; .... Weitere Befehle

		RET

; =============================================================
; Timer 10 ms
; =============================================================
Tim_10ms:	MOV	R0,#10		; 10 ms-Zähler neu laden
		MOV	ZLR_10,R0
		ADD	ZLR_100,ONES	; 100 ms-Zähler - 1 abgelaufen ?
		CALL  cc_Z,Tim_100ms	; ... ja
		MOV	R2,#POF TblTim10 ; R2/R3 = Tabellen-Adresse
		MOV	R3,#PAG TblTim10
		CALL	DecTimer	; Timer testen / decrementieren

	; .... Weitere Befehle

		RET

; =============================================================
; Timer 100 ms
; =============================================================
Tim_100ms:	MOV	R0,#10		; 100 ms-Zähler neu laden
		MOV	ZLR_100,R0
		ADD	ZLR_1S,ONES	; 1 Sek-Zähler - 1 abgelaufen ?
		CALL  cc_Z,Tim_1Sek	; ... ja
		MOV	R2,#POF TblTim100 ; R2/R3 = Tabellen-Adresse
		MOV	R3,#PAG TblTim100
		CALL	DecTimer	; Timer testen / decrementieren

	; .... Weitere Befehle

		; -------------------------------------
		; Hardware-WatchDog rücksetzen
		; -------------------------------------
		MOV	DPP1,#PAG A_IO
		NOP			; Pipeline-Delay
		MOV	DPP1:POF WatchDog,R0
		RET

; =============================================================
; Timer 1 Sek
; =============================================================
Tim_1Sek:	MOV	R0,#10		; 1 Sek-Zähler neu laden
		MOV	ZLR_1S,R0

		MOV	R2,#POF TblTim1Sek ; R2/R3 = Tabellen-Adresse
		MOV	R3,#PAG TblTim1Sek
		CALL	DecTimer	; Timer testen / decrementieren

	; .... Weitere Befehle

		RET

; =============================================================
; Timer-Variablen decrementieren und bei Erreichen
; von 0 evtl. Routine aufrufen
; Eingang:	R2/R3	PP Tabellen-Adresse
; Tabelle:	Routinen-Adr., DPP3:Timer-Adr.
; =============================================================
DecTimer:	MOV	DPP0,R3		; DPP0 setzen
		NOP			; Pipeline-Delay

DecTimer1:	MOV	R4,[R2+]	; R4 = Routinen-Adresse
		JMP  cc_Z,DecTimerR	; ... Tabellen-Ende

		MOV	R1,[R2+]	; R1 = Timer-Adr
		MOV	R0,[R1]		; R0 = Timer-Wert = 0 ?
		JMP  cc_Z,DecTimer1	; ... ja
		SUB	R0,#1		; Timer-Wert - 1

		MOV	[R1],R0		; und speichern; jetzt auf 0 ?
		JMP  cc_NZ,DecTimer1	; ... nein
		CMP	R4,ONES		; Gültige Adresse ?
		JMP  cc_Z,DecTimer1	; ... nein
		PUSH	R2
		PUSH	DPP0
		CALL	cc_UC,[R4]	; Routine aufrufen
		POP	DPP0
		POP	R2
		JMP  DecTimer1

DecTimerR:	RET

P_COD	ENDP

S_COD	ENDS

; #############################################################

S_TXT	SECTION DATA PUBLIC

; -----------------------------------------------
; Texte für User
; -----------------------------------------------
Text1		DB	'Dieses ist Text 1', 0
Text2		DB	'Dieses ist Text 2', 0
Text3		DB	'Dieses ist Text 3', 0
EVEN

S_TXT	ENDS

; #############################################################

S_TBL	SECTION DATA PUBLIC

; -----------------------------------------------
; Tabellen für Timer
; Ist eine Routinen-Adresse angegeben, wird Diese bei
; erreichen von 0 ausgeführt.
; -----------------------------------------------

TblTim1:
DW	-1,	SOF TIM_1
DW	0

TblTim10:
DW	-1,	SOF TIM_10
;;DW	Routine, SOF TIM_xxx	; Beispiel
DW	0

TblTim100:
DW	-1,	SOF TIM_100
DW	0

TblTim1Sek:
DW	-1,	SOF TIM_1S
DW	0

S_TBL	ENDS

; #############################################################

	END
Interrupt-Datei "BAS_INT.A66"
$INCLUDE	(BAS_DEF.A66)

PUBLIC	InitTrap,	InitInt,	InitIntVect

EXTERN	SendStr  :near,	SendChr  :near,	SendCrLf :near
EXTERN	SendNibl :near,	SendWord :near

EXTERN	Tim_1ms  :near

; #############################################################

S_BIT	SECTION DATA BITADDRESSABLE PUBLIC

; ---------------------------------------------------------------
; Externe Bits
; ---------------------------------------------------------------
EXTERN	FLG_DIV :word

S_BIT	ENDS

; #############################################################

S_VAR	SECTION DATA PUBLIC

; ---------------------------------------------------------------
; Externe Variablen
; ---------------------------------------------------------------
EXTERN	ZLR_Inp  :word

EXTERN	POS_RXD  :word,	PNT_RXD  :word,	PUF_RXD  :byte
EXTERN	POS_TXD  :word,	PNT_TXD  :word,	PUF_TXD  :byte

S_VAR	ENDS

; #############################################################

PAGE3		DGROUP SYSTEM, S_BIT, S_VAR
ASSUME		DPP0:NOTHING
ASSUME		DPP1:NOTHING
ASSUME		DPP2:NOTHING
ASSUME		DPP3:PAGE3

S_COD	SECTION CODE PUBLIC

P_TRAP	PROC NEAR

; =============================================================
; Interrupt-Vektoren 10h bis 29h initialisieren
; =============================================================
InitIntVect:	MOV	R0,#10h		; 1. Vektor (CC0INT)
		MOV	R1,#SOF Int_Fehl ; Adr Fehler-Interrupt
		MOV	R2,#SEG Int_Fehl

InitIntVect1:	CALL	SetIntVect
		CMPI1	R0,#29h		; Alles initialisiert ?
		JMP  cc_NZ,InitIntVect1	; ... nein
		RET

; =============================================================
; Interrupt-Vektor setzen
; Eingang:	R0	Vektor-Nr. (0...7Fh)
;	  	R1	SOF Adresse Int-Routine
;	  	R2	SEG Adresse Int-Routine
; Ausgang:	R0	Nächste Vektor-Adresse
; =============================================================
SetIntVect:	PUSH	R0
		PUSH	R2
		SCXT	DPP0,#0		; DPP0 auf Page 0 setzen
		SHL	R0,#2		; Vektor-Nummer * 4
		MOVB	RH2,RL2		; SEG-Adr. Bit 7..0 => 15..8
		MOVB	RL2,#0FAh	; Befehl "JMPS SegNr,..."
		MOV	[R0],R2		; Befehl eintragen
		ADD	R0,#2
		MOV	[R0],R1		; Int-Adresse eintragen
		POP	DPP0
		POP	R2
		POP	R0
		RET

; =============================================================
; Service-Adressen für Interrupts setzen
; =============================================================
IR_Inp1		EQU	CC0IR
IE_Inp1		EQU	CC0IE
VN_Inp1		EQU	10h

IR_Inp2		EQU	CC1IR
IE_Inp2		EQU	CC1IE
VN_Inp2		EQU	11h

InitInt:	PUSH	R8
		SCXT	DPP0,#PAG TblInitInt
		MOV	R8,#POF TblInitInt

InitInt1:	MOV	R0,[R8+]	; R0=Vektor-Nummer
		JMP  cc_Z,InitInt2	; ... Tabellen-Ende
		MOV	R1,[R8+]	; R1=SOF Adress
		MOV	R2,[R8+]	; R2=SEG Adress
		CALL	SetIntVect	; Vektor setzen
		JMP	InitInt1

InitInt2:
		; -------------------------------------
		; CC0 für Interrupt Eingang 1 mit steigender Flanke
		; CC1 für Interrupt Eingang 2 mit fallender Flanke
		; -------------------------------------
			        ; 3    2    1    0  ; Capture bei:
		MOV	CCM0, #0000$0000$0010$0001b ; 0001 = steigende Flanke
			        ; 7    6    5    4  ; 0010 = fallende Flanke
		MOV	CCM1, #0000$0000$0000$0000b ; 0011 = beide Flanken
			        ;11   10    9    8  ; 0100 = COMPARE-Mode 0
		MOV	CCM2, #0000$0000$0000$0000b
			        ;15   14   13   12
		MOV	CCM3, #0000$0000$0000$0000b

		MOV	CC0IC,#00000000$01$1100$11b ; mit Priorität 12.3
		MOV	CC1IC,#00000000$01$1101$11b ; mit Priorität 13.3
			        ;       01	     ; enabled
		; -------------------------------------
		; Timer T1 für SIO initialisieren
		; T1 = 40 MHz : 64 : 580 = 1077 Hz = 0,93 ms
		; -------------------------------------
				;     T1        T0
		MOV	T01CON,#00000$010$00000$000b
			        ;     | |       000 = T0 Teiler 16
			        ;     010           = T1 Teiler 64
		MOV	T1,#-580
		MOV	T1REL,#-580	; = SIO  Reload-Wert
		MOV	T1IC, #00000000$00$1001$11b ; mit Priorität 9.3

		; -------------------------------------
		; Timer T3 für 1 ms-Interrupt
		; T3 = 40 MHz : 64 : 625 = 1000 Hz = 1 ms
		; -------------------------------------
		MOV	T3CON,#00000$010100$00$010b
			        ;     |||   || 010 = Teiler 64
			        ;     |||   00     = Timer-Funktion
			        ;     |01          = T3UD Abwärts zählen
			        ;     1            = T3OE Toggle an P3.3
		MOV	T3,#625	; = 1 ms
		MOV	T3IC, #00000000$00$1000$11b ; mit Priorität 8.3

		; -------------------------------------
		; Timer T4 als Reload-Register für T3
		; -------------------------------------
		MOV	T4CON,#00000000$00$100$111b
			        ;       || | | 111 = Reload bei Toggle T3OTL
			        ;       || 100     = Reload-Funktion
			        ;       00         = Timer deaktiviert
		MOV	T4,#625	; 1 ms Reload-Wert
		POP	DPP0
		POP	R8
		RET

; =============================================================
; Initialisierung Hardware-Traps
; =============================================================
InitTrap:	PUSH	R0
		PUSH	R1
		PUSH	R2

		; -------------------------------------
		; Stack-Overflow-Trap
		; -------------------------------------
		MOV	R0,#04h		; STKOF
		MOV	R1,#SOF IntSTKO
		MOV	R2,#SEG IntSTKO
		CALL	SetIntVect

		; -------------------------------------
		; Stack-Underflow-Trap
		; -------------------------------------
		MOV	R0,#06h		; STKUF
		MOV	R1,#SOF IntSTKU
		MOV	R2,#SEG IntSTKU
		CALL	SetIntVect

		; -------------------------------------
		; Traps Klasse B
		; -------------------------------------
		MOV	R0,#0Ah		; Trap Klasse B
		MOV	R1,#SOF IntTRAPB
		MOV	R2,#SEG IntTRAPB
		CALL	SetIntVect

		POP	R2
		POP	R1
		POP	R0
		RET

; TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
; TRAPS
; TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
IntSTKO:	MOV	SP,#SPAdr	; STACK-POINTER
		BCLR	STKOF
		SCXT	R2,#POF TxtSTKO	; Stack Überlauf
		SCXT	R3,#PAG TxtSTKO
		JMP	IntTRAPx

IntSTKU:	MOV	SP,#SPAdr	; STACK-POINTER
		BCLR	STKUF
		SCXT	R2,#POF TxtSTKU	; Stack Unterlauf
		SCXT	R3,#PAG TxtSTKU
		JMP	IntTRAPx

IntTRAPB:	MOV	DPP3:POF TrapAdr,R0 ; Save R0
		POP	R0		; Get InstructionPointer
		MOV	DPP3:POF TrapAdr+0Eh,R0
		POP	R0		; Get CodeSegmentPointer
		MOV	DPP3:POF TrapAdr+0Ch,R0
		POP	R0		; Get ProgramStatusWord
		MOV	DPP3:POF TrapAdr+0Ah,R0
		MOV	R0,DPP3:POF TrapAdr ; Restore R0

		PUSH	R2
		PUSH	R3
		JBC	UNDOPC,IntTRAP7
		JBC	PRTFLT,IntTRAP3
		JBC	ILLOPA,IntTRAP2
		JBC	ILLINA,IntTRAP1
		JBC	ILLBUS,IntTRAP0
		JMP	IntTRAPx

IntTRAP7:	MOV	R2,#POF TxtUO	; Undefinierter Opcode
		MOV	R3,#PAG TxtUO
		JMP	IntTRAPx

IntTRAP3:	MOV	R2,#POF TxtGB	; Geschützter Befehl
		MOV	R3,#PAG TxtGB
		JMP	IntTRAPx

IntTRAP2:	MOV	R2,#POF TxtFWZ	; Falscher Wort-Zufgriff
		MOV	R3,#PAG TxtFWZ
		JMP	IntTRAPx

IntTRAP1:	MOV	R2,#POF TxtFBZ	; Falscher Befehls-Zugriff
		MOV	R3,#PAG TxtFBZ
		JMP	IntTRAPx

IntTRAP0:	MOV	R2,#POF TxtVBZ	; Verbotener Bus-Zugriff
		MOV	R3,#PAG TxtVBZ

; - - - - - - - - - - - - - - - - - - - - - - - -
IntTRAPx:	PUSH	R0
		BCLR	T3R		; Timer 3 stoppen
		BCLR	T3IE		; 1 ms-Interrupt sperren
		MOV	PSW,#0
		BSET	IEN		; Alle Interrupts freigeben
		CALL	SendStr

		MOVB	RL0,#' '
		CALL	SendChr
		MOVB	RL0,#'('
		CALL	SendChr
		MOV	R0,DPP3:POF TrapAdr+0Ch ; CSP anzeigen
		CALL	SendNibl
		MOV	R0,DPP3:POF TrapAdr+0Eh ; IP anzeigen
		CALL	SendWord
		MOVB	RL0,#'h'
		CALL	SendChr
		MOVB	RL0,#')'
		CALL	SendChr
		CALL	SendCrLf	; Return / LinFeed senden
		POP	R0
		POP	R3
		POP	R2

IntTRAPx1:	JMP	IntTRAPx1

P_TRAP	ENDP

; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
; INTERRUPT-SERVICE-ROUTINEN
; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Int_Fehl	PROC	INTERRUPT = 7Fh

		RET
Int_Fehl	ENDP

; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
; 1 ms-Interrupt
; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Int_1ms		PROC	INTERRUPT = VN_1ms USING REG_1ms
		SCXT	CP,#REG_1ms	; Register-Bank 1ms
		SCXT	MDC,#0		; Mul/Div-Register retten
		NOP			; Pipeline-Delay !!!
		PUSH	MDH
		PUSH	MDL
		MOV	R0,FLG_DIV	; Diverse Flags
		PUSH	R0		;  retten
		PUSH	DPP0		; DPP0 retten
		PUSH	DPP1		; DPP1 retten
		SCXT	DPP2,#PAG A_IO	; DPP2 retten und setzen

		CALL	Tim_1ms		; Unterroutine aufrufen

		POP	DPP2		; DPP2 zurück
		POP	DPP1		; DPP1 zurück
		POP	DPP0		; DPP0 zurück
		POP	R0		; Diverse Flags
		MOV	FLG_DIV,R0	;  zurück
		POP	MDL		; Mul/Div-Register zurück
		POP	MDH
		POP	MDC
		POP	CP		; Register-Bank zurück
		RET
Int_1ms		ENDP

; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
; Bei steigender Flanke an Eingang 1 Zähler + 1
; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Int_Inp1	PROC	INTERRUPT = VN_Inp1
		SUB	ZLR_Inp,ONES	; Zähler + 1
Int_Inp1	ENDP

; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
; Bei fallender Flanke an Eingang 2 Zähler - 1
; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Int_Inp2	PROC	INTERRUPT = VN_Inp2
		ADD	ZLR_Inp,ONES	; Zähler - 1
Int_Inp2	ENDP

; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
; Serielles Senden
; Werte aus einem Ringpuffer auslesen.
; Eingang:	POS_TXD	= Offset für Ringpuffer
; Ausgang:	POS_TXD	+ 1
; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Int_Sio		PROC	INTERRUPT = VN_Sio ; T1
		PUSH	R0
		PUSH	R1

		MOV	R1,POS_TXD
		CMP	R1,PNT_TXD	; Pointer verändert ?
		JMP  cc_Z,Int_Sio9	; ... nein
		JNB  S0TIR,Int_Sio9	; ... nicht bereit zum Senden

		MOVB	RL0,[R1+#PUF_TXD] ; Byte laden
		MOVB	S0TBUF,RL0	; und senden
		BCLR	S0TIR
		CMPI1	R1,#LenTxD-1	; Puffer-Ende erreicht ?
		JMP  cc_NZ,Int_Sio5	; ... nein
		MOV	R1,#0		; Wieder bei 0 beginnen

Int_Sio5:	MOV	POS_TXD,R1	; Neuen Offset speichern

Int_Sio9:	POP	R1
		POP	R0
		RET
Int_Sio		ENDP

; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
; Serieller Empfang
; Werte werden in einem Ringpuffer abgelegt.
; Eingang:	POS_RXD	= Offset für Ringpuffer
; Ausgang:	POS_RXD	+ 1
; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Int_RxD		PROC	INTERRUPT = VN_RxD
		PUSH	R0
		MOVB	RL0,S0RBUF	; Byte laden
		BCLR	S0RIR		; Interrupt-Request löschen

		ORB	RL0,RL0		; Byte = 0 ?
		JMP  cc_Z,Int_RxDE	; ... ja: nicht speichern
		CMPB	RL0,ONES	; Byte = 0FFh ?
		JMP  cc_Z,Int_RxDE	; ... ja: nicht speichern
		PUSH	R1
		MOV	R1,POS_RXD	; R1 = Puffer-Offset
		MOVB	[R1+#PUF_RXD],RL0 ; Byte speichern
		CMPI1	R1,#LenRxD-1	; Offset + 1 = Ende ?
		JMP  cc_NZ,Int_RxD1	; ... nein
		MOV	R1,#0		; Wieder bei 0 beginnen

Int_RxD1:	MOV	POS_RXD,R1	; Neuen Offset speichern
		POP	R1

Int_RxDE:	POP	R0
		RET
Int_RxD		ENDP

; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
; Serielles Senden (Wird von Int_Sio übernommen)
; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Int_TxD		PROC	INTERRUPT = VN_TxD

		RET
Int_TxD		ENDP

S_COD	ENDS

; #############################################################

S_TXT	SECTION DATA PUBLIC

; -----------------------------------------------
; Texte für System-Fehler
; -----------------------------------------------
TxtSTKO		DB	'Stack-Ueberlauf', 0
TxtSTKU		DB	'Stack-Unterlauf', 0
TxtUO		DB	'Undefinierter Opcode', 0
TxtGB		DB	'Geschuetzter Befehl', 0
TxtFWZ		DB	'Falscher Wort-Zugriff', 0
TxtFBZ		DB	'Falscher Befehls-Zugriff', 0
TxtVBZ		DB	'Verbotener Bus-Zugriff', 0
EVEN

S_TXT	ENDS

; #############################################################

S_TBL	SECTION DATA PUBLIC

; -----------------------------------------------
; Tabelle für Interrupt-Vektoren
; -----------------------------------------------
TblInitInt:
DW	VN_Sio,	 SOF Int_Sio,	SEG Int_Sio
DW	VN_TxD,	 SOF Int_TxD,	SEG Int_TxD
DW	VN_RxD,	 SOF Int_RxD,	SEG Int_RxD
DW	VN_1ms,	 SOF Int_1ms,	SEG Int_1ms
DW	VN_Inp1, SOF Int_Inp1,	SEG Int_Inp1
DW	VN_Inp2, SOF Int_Inp2,	SEG Int_Inp2
DW	0

S_TBL	ENDS

; #############################################################

	END
Linker-Datei "BASIS.LNK"
basis.obj, bas_int.obj, bas_var.obj TO basis.abs
SECTIONS (	S_COD	(0x01000),
	&	S_TXT	(0x02000),
	&	S_TBL	(0x03000),
	&	S_VAR	(0x0C000) )
IXREF