summaryrefslogtreecommitdiff
path: root/system/shard-x86-at/7/src/I8250.ASM
blob: cb6923311fce970ec09bff7606d8571fa1a88508 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
;***************************************************************************
;*======= 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 <mov cl,shard:(si+devtype)>     ;type dazu
     ret




i8250_test:
     cmp bh,0                    ;abfrage
     ifnz <int 0bh>
     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 <ret>               ;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 <out (dx),al>          ;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 <call i8250_out_restart> ;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 <call i8250_out_restart> ;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