;*************************************************************************** ;*======= Copyright (C) 1985,86 Martin Schoenbeck, Spenge =================* ;* * ;* Routinen fuer 8250 UART im EUMEL - System * ;* * ;* * ;*************************************************************************** i8250_data equ 0 i8250_ier equ 1 ;interrupt enable register i8250_iir equ 2 ;interrupt indicator register i8250_lcr equ 3 ;line control register i8250_mcr equ 4 ;modem control register i8250_lsr equ 5 ;line status register i8250_msr equ 6 ;modem status register device i8250 dtcbroutines iocontrol routine 1,i8250_devicetype routine 2,frout routine 3,i8250_stop routine 4,i8250_weiter routine 5,nil_size routine 6,priv_op_question routine 8,priv_op_question routine 9,priv_op_question routine -2,frout routine -3,i8250_status routine -4,stream_in_count routine -5,stream_out_count routine -6,i8250_sendbreak routine -10,i8250_i_stop routine -11,i8250_i_weiter routine -1,unknowncontrol dtcbroutines control32 routine 6,i8250_flow routine 8,i8250_baud routine 9,i8250_bits routine -2,i8250_init routine -3,i8250_test routine -1,no_channel_setup dtcbroutines blockin dtcbroutines blockout routine -1,unknowncontrol dtcbparams i8250_output,3 ;typ = nur stream io ;****************************************************************** ;* der macro i8250_ccb muss fuer jeden 8250 im system einmal ;* aufgerufen werden ;* ;* parameter: i8250_ccb macro i8250,kanal i8250&buf db 100 DUP (0ffh) startccb i8250&ccb,kanal stream 100,i8250&buf ;;die 8250 routinen benutzen stream routinen ccbentry i8250_stat db 0 ccbentry i8250_statusandmask db 0 ;;keine statusleitungen abfragen ccbentry i8250_statusxormask db 0 ccbentry i8250_errmask db 0 ;;keine fehler auswerten ccbentry i8250_errflags db 0 ccbentry i8250_irq_line db i8250&irq ccbentry i8250_base dw i8250&base ccbentry i8250_next_ccb dw 0 ccbentry i8250_int_entry call i8250_interrupt endm ;*** bits in i8250_stat: i8250_rtscts equ 1 i8250_exists equ 2 i8250_baud_table: dw 2304 ;50 dw 1536 ;75 dw 1047 ;110 dw 857 ;134.5 dw 768 ;150 dw 384 ;300 dw 192 ;600 dw 96 ;1200 dw 64 ;1800 dw 48 ;2400 dw 32 ;3600 dw 24 ;4800 dw 16 ;7200 dw 12 ;9600 i8250_devicetype: mov cx,0 ;erstmal 0 setzen test byte ptr [di+i8250_stat],i8250_exists ;ist da einer ifnz ;type dazu ret i8250_test: cmp bh,0 ;abfrage ifnz mov dx,(di+i8250_base) add dx,i8250_iir ;auf interrupt indicator register in al,(dx) mov cl,al mov ch,1 ret i8250_init: mov ax,0 mov es,ax ; pruefen, ob ueberhaupt vorhanden mov dx,(di+i8250_base) add dx,i8250_iir ;interrupt indicate register holen jmp short $+2 in al,dx nop ;der in befehl erwischt einen von diesen nop ;codes, wenn auf der adresse kein port ist nop nop nop test al,0f8h ;alle bits weg, die nicht da sein koennen ifnz ;keine schnittstelle da or byte ptr [di+i8250_stat],i8250_exists ;da ist einer mov bx,first_ictlr_int add bl,(di+i8250_irq_line) ;an welchem pin des controllers haengt er ;carry kann hier nicht auftreten mov byte ptr i8250_initint,bl ;fuer passenden initialisierungsint basteln add bx,bx ;*2 als wortadresse mov dx,word ptr (i8250_i_tab-((3+first_ictlr_int)*2))[bx] ;letzten ccb holen mov word ptr (i8250_i_tab-((3+first_ictlr_int)*2))[bx],di ;neuen eintragen mov (di+i8250_next_ccb),dx ;alten selbst merken add bx,bx ;*4 mov word ptr es:[bx+2],cs mov dx,di ;adresse ccb holen add dx,i8250_int_entry ;adresse interrupt routine errechnen mov word ptr es:[bx],dx ;eintragen mov cl,(di+i8250_irq_line) ;nochmal bit im controller inc cl ;mindestens einmal shiften stc mov ch,0 ;mit nichts anfangen rcl ch,cl in al,int_ctlr+1 ;interrupt enable register holen or al,ch ;bit fuer i8250 setzen xor al,ch ;und freigeben out int_ctlr+1,al mov dx,(di+i8250_base) add dx,i8250_ier ;auf interrupt enable register mov al,0fh ;alle interrupts an out dx,al ;interrupt enable add dx,i8250_mcr-i8250_ier ;auf modem control register mov al,0bh ;rts, dtr, int enable out dx,al ; ret i8250_initint = $+1 int 12 ret i8250_i_tab: dw 0 ;int 3 dw 0 ;int 4 dw 0 ;int 5 dw 0 ;int 6 dw 0 ;int 7 i8250_interrupt: push ds push cx push di push bx push dx push ax mov ax,cs mov ds,ax ;ds = cs setzen mov bx,sp ;auf stack zeigen mov di,ss:[bx+12] ;return adresse im ccb holen sub di,i8250_int_entry+3 ;auf anfang ccb rechnen i8250_to_first_ccb: push di ;ersten ccb merken mov ah,1 ;bis jetzt keinen port gefunden i8250_check_same_int: mov dx,(di+i8250_base) add dx,i8250_iir ;interrupt indicate register lesen in al,(dx) test al,1 ;war interrupt auf diesem kanal jnz i8250_int_end mov ah,0 ;ax als index, gleichzeitig ah loeschen push ax mov bx,ax call word ptr i8250_int_table[bx] ;passende service routine aufrufen pop ax jmp i8250_check_same_int i8250_int_end: mov di,(di+i8250_next_ccb) ;naechsten port fuer diesen vektor holen or di,di ;ende eintrag? jnz i8250_check_same_int pop di ;ersten ccb holen or ah,ah ;haben wir im letzten durchlauf einen gefunden jz i8250_to_first_ccb ;ja, dann weiter suchen mov al,20h ;end of interrupt out int_ctlr,al pop ax pop dx pop bx pop di pop cx pop ds pop cs:[i8250_ret_dummy] ;return adresse im ccb vergessen iret ;fertig i8250_ret_dummy dw 0 i8250_int_table: dw offset i8250_out_restart ;bei ext. status wechsel oder bei tx empty dw offset i8250_out_restart ;nur output ggf. neu starten dw offset i8250_rec_int dw offset i8250_error_int i8250_baud: cmp bh,15 ;negativer wert oder > 15 jnc i8250_not_ok cmp bh,0 jz i8250_not_ok test bl,1 ;abfage? jnz i8250_ok ;ja, wir koennen alles mov dx,(di+i8250_base) add dx,i8250_lcr ;line control register cli ;nichts dazwischen lassen in al,dx ;alten wert holen push ax mov al,80h out dx,al ;auf baudrate register schalten sub dx,i8250_lcr ;wieder auf base mov bl,bh ;baudrate schluessel nach bx ausdehnen mov bh,0 sal bx,1 ;ein baudrate eintrag ist zwei byte mov ax,word ptr i8250_baud_table-2[bx] ;passenden baudrate eintrag holen out dx,al ;low byte raus mov al,ah inc dx out dx,al ;high byte raus pop ax add dx,i8250_lcr-1 ;wieder auf lcr out dx,al ;alte lcr wieder setzen sti ;jetzt darf er wieder jmp short i8250_ok ;alles klar ret i8250_bits: test bh,0a0h ;negativer wert oder 1.5 Stopbits jnz i8250_not_ok test bh,4 ;bitzahl >= 5 jz i8250_not_ok ;nein, muss aber test bl,1 ;abfrage jnz i8250_ok mov al,bh ;anfoderung nach al test al,10h ;gerade paritaet? jz i8250_not_even or al,8 ;dann paritaet auch enablen i8250_not_even: test al,40h ;2 stopbits jnz i8250_not_two ;nein, das bit steht schon and al,0ffh-4 ;bit ausknipsen i8250_not_two: and al,1fh ;alle unbenutzten loeschen mov dx,(di+i8250_base) add dx,i8250_lcr ;auf line control register out dx,al mov cl,bh ;anzahl bits nach cl and cl,7 ;ausblenden inc cl ;aus 0-7 1-8 machen mov dx,0ffh ;von 0 bits ausgehen shl dl,cl ;bits anzahl nullen reinziehen xor dl,0ffh ;und 1 und 0 tauschen call set_out_mask call set_inp_mask call set_inp_errmask i8250_ok: mov cx,0 ret i8250_not_ok: mov cx,1 ret i8250_flow: test bh,80h ;negativer wert? jnz i8250_not_ok cmp bh,3 jnc i8250_not_ok ;oder > 2 test bl,1 ;abfrage jnz i8250_ok ;ja cli mov byte ptr (di+i8250_statusxormask),0 ;beim status nichts abfragen mov byte ptr (di+i8250_statusandmask),0 and byte ptr (di+i8250_stat),0ffh-i8250_rtscts ;handshake ausschalten dec bh jnz i8250_not_xonxoff call enablexon jmp i8250_flow_end i8250_not_xonxoff: call disablexon dec bh jnz i8250_flow_end mov byte ptr (di+i8250_statusandmask),10h ;cts abfragen mov byte ptr (di+i8250_statusxormask),10h ;auf gesetzt or byte ptr (di+i8250_stat),i8250_rtscts i8250_flow_end: call i8250_out_restart ;immer probieren, ob jetzt output moeglich sti jmp i8250_ok i8250_output: call fillbuffer pushf jz i8250_no_orest call i8250_out_restart i8250_no_orest: popf ret ;* out_restart kann jederzeit aufgerufen werden, da der status jedesmal ;* abgefragt wird i8250_out_restart: mov dx,(di+i8250_base) ;commandport laden add dx,i8250_lsr ;adresse line status register cli in al,(dx) ;status holen test al,20h ;tx buffer empty lahf ;modem status register immer lesen inc dx ;auf modem status register in al,(dx) ;holen sahf jz i8250_stiret ;nein, sti und zurueck and al,(di+i8250_statusandmask) ;gewuenschte bits ausblenden xor al,(di+i8250_statusxormask) jnz i8250_stiret call getnextchar ;zeichen holen, xon/xoff etc. abhandeln mov dx,(di+i8250_base) ;port holen ifnz ;wenn was da, ausgeben i8250_stiret: sti ret ;fertig i8250_rec_int: mov dx,(di+i8250_base) in al,(dx) ;zeichen holen call input ;zeichen uebergeben, xon/xoff etc. abhandeln jz i8250_out_restart ;ggf. output neu starten ret i8250_error_int: mov dx,(di+i8250_base) add dx,i8250_lsr ;line status register holen in al,(dx) or (di+i8250_errflags),al ;alte errorflags dazu test al,10h ;break detected jnz i8250_break and al,(di+i8250_errmask) ;welche fehlerbits sollen behandelt werden jz i8250_rec_int ;keine, normal einlesen mov dx,(di+i8250_base) in al,(dx) ;zeichen holen call errorinput ;uebergeben jz i8250_out_restart ret i8250_break: call breakinput ;break zeichen uebergeben jz i8250_out_restart ret i8250_stop: call stream_stop ifnz ;output ggf neu starten test byte ptr (di+i8250_stat),i8250_rtscts jz i8250_stop_end i8250_i_stop: mov dx,(di+i8250_base) add dx,i8250_mcr ;auf modem control register mov al,9 ;rts wegnehmen out (dx),al i8250_stop_end: ret i8250_weiter: call stream_weiter ifnz ;output ggf. neu starten test byte ptr (di+i8250_stat),i8250_rtscts jz i8250_stop_end i8250_i_weiter: mov dx,(di+i8250_base) add dx,i8250_mcr ;auf modem control register mov al,0bh ;rts wieder setzen out (dx),al ret i8250_status: cli mov cl,(di+i8250_errflags) ;fehler holen mov byte ptr (di+i8250_errflags),0 ;loeschen mov dx,(di+i8250_base) add dx,i8250_lsr in al,dx mov ch,al sti ret i8250_sendbreak: cli mov dx,(di+i8250_base) add dx,i8250_lcr in al,dx and al,10111111b ;switch breakbit off and bl,1 ;nur ein bit behalten ror bl,1 ror bl,1 ;auf bit 6 position or al,bl ;send break or not out dx,al sti ret