]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/vmx/io.c
vmx: emulate ps/2 intellimouse scrolling
[plan9front.git] / sys / src / cmd / vmx / io.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <draw.h>
5 #include <cursor.h>
6 #include <mouse.h>
7 #include "dat.h"
8 #include "fns.h"
9
10 uchar cmos[0x30] = {
11         [1] 0xff, [3] 0xff, [5] 0xff, 
12         [0xa] 0x26,
13         [0xb] 1<<1,
14         [0xd] 1<<7, /* cmos valid */
15         [0xf] 0x56, /* cmos tests pass */
16         [0x11] 0x80, /* mouse enabled */
17         [0x14] 0x2e, /* cga 80-column */
18         [0x2d] 0x1c, /* caches + fpu enabled */
19 };
20 static vlong rtcnext = -1;
21
22 static uchar
23 bcd(uchar m, uchar c)
24 {
25         return (m & 1) != 0 ? c : c / 10 << 4 | c % 10;
26 }
27
28 void
29 rtcadvance(void)
30 {
31         vlong t;
32         
33         if(rtcnext != -1){
34                 t = nanosec();
35                 if(t >= rtcnext){
36                         cmos[0xc] |= 0x40;
37                         rtcnext = -1;
38                 }else
39                         settimer(rtcnext);
40         }
41         irqline(8, (cmos[0xc] & cmos[0xb] & 0x70) != 0); 
42 }
43
44 static void
45 rtcset(void)
46 {
47         vlong t, b;
48         int d;
49
50         rtcadvance();
51         if((cmos[0xa] >> 4) > 2 || (cmos[0xa] & 15) == 0 || (cmos[0xc] & 0x40) != 0){
52                 rtcnext = -1;
53                 return;
54         }
55         switch(cmos[0xa]){
56         case 0x21: d = 12; break;
57         case 0x22: d = 13; break;
58         default: d = 4 + (cmos[0xa] & 0xf);
59         }
60         b = (1000000000ULL << d) / 1048576;
61         t = nanosec();
62         rtcnext = t + b - t % b;
63         settimer(rtcnext);
64 }
65
66 static u32int
67 rtcio(int isin, u16int port, u32int val, int sz, void *)
68 {
69         static u8int addr;
70         uintptr basemem, extmem;
71
72         static int cmosinit;
73         int i, s;
74         Tm *tm;
75         
76         if(cmosinit == 0){
77                 basemem = gavail(gptr(0, 0)) >> 10;
78                 if(basemem > 640) basemem = 640;
79                 extmem = gavail(gptr(1<<20, 0)) >> 10;
80                 if(extmem >= 65535) extmem = 65535;
81                 cmos[0x15] = basemem;
82                 cmos[0x16] = basemem >> 8;
83                 cmos[0x17] = extmem;
84                 cmos[0x18] = extmem >> 8;
85                 s = 0;
86                 for(i = 0x10; i < 0x2e; i++)
87                         s += cmos[i];
88                 cmos[0x2e] = s >> 8;
89                 cmos[0x2f] = s;
90                 cmosinit = 1;
91         }
92         if(sz != 1) vmerror("rtc: access size %d != 1", sz);
93         val = (u8int) val;
94         switch(isin << 16 | port){
95         case 0x70: addr = val; return 0;
96         case 0x71:
97                 switch(addr){
98                 case 0xa: cmos[addr] = val & 0x7f; rtcset(); break;
99                 case 0xb: cmos[addr] = val | 2; rtcadvance(); break;
100                 case 0xc: case 0xd: goto no;
101                 default:
102                         if(addr < nelem(cmos))
103                                 cmos[addr] = val;
104                         else no:
105                                 vmerror("rtc: write to unknown address %#x (val=%#x)", addr, val);
106                         return 0;
107                 }
108         case 0x10070: return addr;
109         case 0x10071:
110                 tm = gmtime(time(nil));
111                 switch(addr){
112                 case 0x00: return bcd(cmos[0xb], tm->sec);
113                 case 0x02: return bcd(cmos[0xb], tm->min);
114                 case 0x04: return bcd(cmos[0xb], tm->hour);
115                 case 0x06: return bcd(cmos[0xb], tm->wday + 1);
116                 case 0x07: return bcd(cmos[0xb], tm->mday);
117                 case 0x08: return bcd(cmos[0xb], tm->mon + 1);
118                 case 0x09: return bcd(cmos[0xb], tm->year % 100);
119                 case 0x0c:
120                         i = cmos[0xc] | ((cmos[0xc] & cmos[0xb] & 0x70) != 0) << 7;
121                         cmos[0xc] = 0;
122                         rtcset();
123                         return i;
124                 case 0x32: return bcd(cmos[0xb], tm->year / 100 + 19);
125                 default:
126                         if(addr < nelem(cmos))
127                                 return cmos[addr];
128                         vmerror("rtc: read from unknown address %#x", addr);
129                         return 0;
130                 }
131         }
132         return iowhine(isin, port, val, sz, "rtc");
133 }
134
135 typedef struct Pic Pic;
136 struct Pic {
137         enum {
138                 AEOI = 1,
139                 ROTAEOI = 2,
140                 MASKMODE = 4,
141                 POLL = 8,
142                 READSR = 16,
143         } flags;
144         u8int lines;
145         u8int irr, isr;
146         u8int imr;
147         u8int elcr;
148         u8int init;
149         u8int prio;
150         u8int base;
151 } pic[2];
152 int irqactive = -1;
153
154 static u8int
155 picprio(u8int v, u8int p, u8int *n)
156 {
157         p++;
158         v = v >> p | v << 8 - p;
159         v &= -v;
160         v = v << p | v >> 8 - p;
161         if(n != nil)
162                 *n = ((v & 0xf0) != 0) << 2 | ((v & 0xcc) != 0) << 1 | (v & 0xaa) != 0;
163         return v;
164 }
165
166 static u8int
167 piccheck(Pic *p, u8int *n)
168 {
169         u8int s;
170         
171         s = p->isr;
172         if((p->flags & MASKMODE) != 0 && p->imr != 0)
173                 s = 0;
174         return picprio(p->irr & ~p->imr | s, p->prio, n) & ~s;
175 }
176
177 static void
178 picaeoi(Pic *p, u8int b)
179 {
180         if((p->flags & AEOI) == 0)
181                 return;
182         p->isr &= ~(1<<b);
183         if((p->flags & ROTAEOI) != 0)
184                 p->prio = b;
185 }
186
187 static void
188 picupdate(Pic *p)
189 {
190         u8int m, n;
191         
192         if(p->init != 4) return;
193         m = piccheck(p, &n);
194         if(p == &pic[1])
195                 irqline(2, m != 0);
196         else{
197                 if(m != 0 && n == 2){
198                         m = piccheck(&pic[1], &n);
199                         n |= pic[1].base;
200                 }else
201                         n |= p->base;
202                 if(m != 0 && irqactive != n){
203                         if(ctl("irq %d", n) < 0)
204                                 sysfatal("ctl: %r");
205                         if(state == VMHALT)
206                                 state = VMRUNNING;
207                         irqactive = n;
208                 }else if(m == 0 && irqactive >= 0){
209                         if(ctl("irq") < 0)
210                                 sysfatal("ctl: %r");
211                         irqactive = -1;
212                 }
213         }
214 }
215
216 void
217 irqline(int n, int s)
218 {
219         Pic *p;
220         u8int ol, m;
221         
222         assert(n >= 0 && n <= 15);
223         p = &pic[n / 8];
224         n %= 8;
225         ol = p->lines;
226         m = 1<<n;
227         switch(s){
228         case 1: case IRQLLOHI: p->lines |= m; break;
229         case 0: p->lines &= ~m; break;
230         case IRQLTOGGLE: p->lines ^= m; break;
231         default: assert(0);
232         }
233         if((p->elcr & m) != 0)
234                 p->irr = p->irr & ~m | ~p->lines & m;
235         else
236                 p->irr |= p->lines & ~ol & m;
237         if(s == IRQLLOHI && (p->elcr & m) == 0)
238                 p->irr |= m;
239         picupdate(p);
240 }
241
242 void
243 irqack(int n)
244 {
245         Pic *p;
246         extern int nextexit;
247         
248         irqactive = -1;
249         if((n & ~7) == pic[0].base)
250                 p = &pic[0];
251         else if((n & ~7) == pic[1].base)
252                 p = &pic[1];
253         else
254                 return;
255         if(p == &pic[1]){
256                 irqack(pic[0].base + 2);
257                 irqline(2, 0);
258         }
259         n &= 7;
260         p->irr &= ~(1<<n);
261         p->isr |= 1<<n;
262         picaeoi(p, n);
263         picupdate(p);
264 }
265
266 void
267 elcr(u16int a)
268 {
269         pic[0].elcr = a;
270         pic[1].elcr = a >> 8;
271 }
272
273 static u32int
274 picio(int isin, u16int port, u32int val, int sz, void *)
275 {
276         Pic *p;
277         u8int m, b;
278         
279         p = &pic[(port & 0x80) != 0];
280         val = (u8int)val;
281         switch(isin << 16 | port){
282         case 0x20:
283         case 0xa0:
284                 if((val & 1<<4) != 0){ /* ICW1 */
285                         if(irqactive >= 0){
286                                 if(ctl("irq") < 0)
287                                         sysfatal("ctl: %r");
288                                 irqactive = -1;
289                         }
290                         p->irr = 0;
291                         p->isr = 0;
292                         p->imr = 0;
293                         p->prio = 7;
294                         p->flags = 0;
295                         if((val & 0x0b) != 0x01) vmerror("PIC%zd ICW1 with unsupported value %#ux", p-pic, (u32int)val);
296                         p->init = 1;
297                         return 0;
298                 }
299                 if((val & 0x18) == 0){ /* OCW2 */
300                         switch(val >> 5){
301                         case 0: /* rotate in automatic eoi mode (clear) */
302                                 p->flags &= ~ROTAEOI;
303                                 break;
304                         case 1: /* non-specific eoi command */
305                                 p->isr &= ~picprio(p->isr, p->prio, nil);
306                                 break;
307                         case 2: /* no operation */
308                                 break;
309                         case 3: /* specific eoi command */
310                                 p->isr &= ~(1<<(val & 7));
311                                 break;
312                         case 4: /* rotate in automatic eoi mode (set) */
313                                 p->flags |= ROTAEOI;
314                                 break;
315                         case 5: /* rotate on non-specific eoi command */
316                                 p->isr &= ~picprio(p->isr, p->prio, &p->prio);
317                                 break;
318                         case 6: /* set priority */
319                                 p->prio = val & 7;
320                                 break;
321                         case 7: /* rotate on specific eoi command */
322                                 p->isr &= ~(1<<(val & 7));
323                                 p->prio = val & 7;
324                                 break;
325                         }
326                         picupdate(p);
327                         return 0;
328                 }
329                 if((val & 0x98) == 8){ /* OCW3 */
330                         if((val & 0x40) != 0)
331                                 if((val & 0x20) != 0)
332                                         p->flags |= MASKMODE;
333                                 else
334                                         p->flags &= ~MASKMODE;
335                         if((val & 4) != 0)
336                                 p->flags |= POLL;
337                         if((val & 2) != 0)
338                                 if((val & 10) != 0)
339                                         p->flags |= READSR;
340                                 else
341                                         p->flags &= ~READSR;
342                         picupdate(p);
343                         
344                 }
345                 return 0;
346         case 0x21:
347         case 0xa1:
348                 switch(p->init){
349                 default:
350                         vmerror("write to PIC%zd in init=%d state", p-pic, p->init);
351                         return 0;
352                 case 1:
353                         p->base = val;
354                         p->init = 2;
355                         return 0;
356                 case 2:
357                         if(p == &pic[0] && val != 4 || p == &pic[1] && val != 2)
358                                 vmerror("PIC%zd ICW3 with unsupported value %#ux", p-pic, val);
359                         p->init = 3;
360                         return 0;
361                 case 3:
362                         if((val & 0xfd) != 1) vmerror("PIC%zd ICW4 with unsupported value %#ux", p-pic, val);
363                         if((val & 2) != 0) p->flags |= AEOI;
364                         p->init = 4;
365                         picupdate(p);
366                         return 0;
367                 case 0:
368                 case 4:
369                         p->imr = val;
370                         picupdate(p); 
371                         return 0;
372                 }
373                 break;
374         case 0x10020:
375         case 0x100a0:
376                 if((p->flags & READSR) != 0)
377                         return p->isr;
378                 if((p->flags & POLL) != 0){
379                         p->flags &= ~POLL;
380                         m = piccheck(p, &b);
381                         if(m != 0){
382                                 p->irr &= ~m;
383                                 p->isr |= m;
384                                 picaeoi(p, b);
385                                 picupdate(p);
386                                 return 1<<7 | b;
387                         }
388                         return 0;
389                 }
390                 return p->irr;
391         case 0x10021:
392         case 0x100a1:
393                 return p->imr;
394         case 0x4d0:
395         case 0x4d1:
396                 pic[port & 1].elcr = val;
397                 return 0;
398         case 0x104d0:
399         case 0x104d1:
400                 return pic[port & 1].elcr;
401         }
402         return iowhine(isin, port, val, sz, "pic");
403 }
404
405 typedef struct PITChannel PITChannel;
406
407 struct PITChannel {
408         u8int mode;
409         u8int bcd;
410         u8int access;
411         u8int state;
412         u16int count, reload;
413         int latch;
414         enum { READLO, READHI, READLATLO, READLATHI } readstate;
415         u8int writestate;
416         vlong lastnsec;
417         u8int output;
418 };
419 PITChannel pit[3] = {
420         [0] { .state 1 },
421 };
422 u8int port61;
423 enum { PERIOD = 838 };
424
425 void
426 settimer(vlong targ)
427 {
428         extern vlong timerevent;
429         extern Lock timerlock;
430         extern int timerid;
431         int sendint;
432
433         sendint = 0;
434         lock(&timerlock);
435         if(targ < timerevent){
436                 timerevent = targ;
437                 sendint = 1;
438         }
439         unlock(&timerlock);
440         if(sendint)
441                 threadint(timerid);
442 }
443
444 static void
445 pitout(int n, int v)
446 {
447         if(n == 0)
448                 irqline(0, v);
449         switch(v){
450         case IRQLLOHI: case 1: pit[n].output = 1; break;
451         case 0: pit[n].output = 0; break;
452         case IRQLTOGGLE: pit[n].output ^= 1; break;
453         }
454 }
455
456 void
457 pitadvance(void)
458 {
459         int i;
460         int nc;
461         PITChannel *p;
462         vlong nt, t;
463         int rel;
464
465         for(i = 0; i < 3; i++){
466                 p = &pit[i];
467                 nt = nanosec();
468                 t = nt - p->lastnsec;
469                 p->lastnsec = nt;
470                 switch(p->mode){
471                 case 0:
472                         if(p->state != 0){
473                                 nc = t / PERIOD;
474                                 if(p->count <= nc)
475                                         pitout(i, 1);
476                                 p->count -= nc;
477                                 p->lastnsec -= t % PERIOD;
478                                 if(!p->output)
479                                         settimer(p->lastnsec + p->count * PERIOD);
480                         }
481                         break;
482                 case 2:
483                         if(p->state != 0){
484                                 nc = t / PERIOD;
485                                 if(p->count == 0 || p->count - 1 > nc)
486                                         p->count -= nc;
487                                 else{
488                                         rel = p->reload - 1;
489                                         if(rel <= 0) rel = 65535;
490                                         nc -= p->count - 1;
491                                         nc %= rel;
492                                         p->count = rel - nc + 1;
493                                         pitout(i, IRQLLOHI);
494                                 }
495                                 p->lastnsec -= t % PERIOD;
496                                 settimer(p->lastnsec + p->count * PERIOD);
497                         }
498                         break;
499                 case 3:
500                         if(p->state != 0){
501                                 nc = 2 * (t / PERIOD);
502                                 if(p->count > nc)
503                                         p->count -= nc;
504                                 else{
505                                         rel = p->reload;
506                                         if(rel <= 1) rel = 65536;
507                                         nc -= p->count;
508                                         nc %= rel;
509                                         p->count = rel - nc;
510                                         pitout(i, IRQLTOGGLE);
511                                 }
512                                 p->lastnsec -= t % PERIOD;
513                                 settimer(p->lastnsec + p->count / 2 * PERIOD);
514                         }
515                         break;
516                 }
517         }
518 }
519
520 static void
521 pitsetreload(int n, int hi, u8int v)
522 {
523         PITChannel *p;
524         
525         p = &pit[n];
526         if(hi)
527                 p->reload = p->reload >> 8 | v << 8;
528         else
529                 p->reload = p->reload & 0xff00 | v;
530         switch(p->mode){
531         case 0:
532                 pitout(n, 0);
533                 if(p->access != 3 || hi){
534                         p->count = p->reload;
535                         p->state = 1;
536                         p->lastnsec = nanosec();
537                         settimer(p->lastnsec + p->count * PERIOD);
538                 }else
539                         p->state = 0;
540                 break;
541         case 2:
542         case 3:
543                 if(p->state == 0 && (p->access != 3 || hi)){
544                         p->count = p->reload;
545                         p->state = 1;
546                         p->lastnsec = nanosec();
547                         pitadvance();
548                 }
549                 break;
550         default:
551                 vmerror("PIT reload in mode %d not implemented", p->mode);
552                 break;
553         }       
554 }
555
556 static u32int
557 pitio(int isin, u16int port, u32int val, int sz, void *)
558 {
559         int n;
560
561         val = (u8int) val;
562         pitadvance();
563         switch(isin << 16 | port){
564         case 0x10040:
565         case 0x10041:
566         case 0x10042:
567                 n = port & 3;
568                 switch(pit[n].readstate){
569                 case READLO:
570                         if(pit[n].access == 3)
571                                 pit[n].readstate = READHI;
572                         return pit[n].count;
573                 case READHI:
574                         if(pit[n].access == 3)
575                                 pit[n].readstate = READLO;
576                         return pit[n].count >> 8;
577                 case READLATLO:
578                         pit[n].readstate = READLATHI;
579                         return pit[n].latch;
580                 case READLATHI:
581                         pit[n].readstate = pit[n].access == 1 ? READHI : READLO;
582                         return pit[n].latch >> 8;
583                 }
584                 return 0;
585         case 0x10061: return port61 | pit[2].output << 5;
586         case 0x40:
587         case 0x41:
588         case 0x42:
589                 n = port & 3;
590                 switch(pit[n].writestate){
591                 case READLO:
592                         if(pit[n].access == 3)
593                                 pit[n].writestate = READHI;
594                         pitsetreload(n, 0, val);
595                         break;
596                 case READHI:
597                         if(pit[n].access == 3)
598                                 pit[n].writestate = READLO;
599                         pitsetreload(n, 1, val);
600                         break;
601                 }
602                 return 0;
603         case 0x43:
604                 n = val >> 6;
605                 if(n == 3) return 0;
606                 if((val & ~0xc0) == 0){
607                         pit[n].latch = pit[n].count;
608                         pit[n].readstate = READLATLO;
609                 }else{
610                         pit[n].mode = val >> 1 & 7;
611                         pit[n].access = val >> 4 & 3;
612                         pit[n].bcd = val & 1;
613                         if(pit[n].bcd != 0)
614                                 vmerror("pit: bcd mode not implemented");
615                         switch(pit[n].mode){
616                         case 0: case 2: case 3: break;
617                         default: vmerror("pit: mode %d not implemented", pit[n].mode);
618                         }
619                         pit[n].state = 0;
620                         pit[n].count = 0;
621                         pit[n].reload = 0;
622                         pit[n].readstate = pit[n].access == 1 ? READHI : READLO;
623                         pit[n].writestate = pit[n].access == 1 ? READHI : READLO;
624                         pit[n].lastnsec = nanosec();
625                         switch(pit[n].mode){
626                         case 0:
627                                 pitout(n, 0);
628                                 break;
629                         default:
630                                 pitout(n, 1);
631                         }
632                 }
633                 return 0;
634         case 0x61:
635                 port61 = port61 & 0xf0 | val & 0x0f;
636                 return 0;
637         }
638         return iowhine(isin, port, val, sz, "pit");
639 }
640
641 typedef struct I8042 I8042;
642 struct I8042 {
643         u8int cfg, stat, oport;
644         int cmd;
645         u16int buf; /* |0x100 == kbd, |0x200 == mouse, |0x400 == cmd */
646 } i8042 = {
647         .cfg 0x74,
648         .stat 0x10,
649         .oport 0x01,
650         .cmd -1,
651 };
652 Channel *kbdch, *mousech;
653 u8int mouseactive;
654 typedef struct PCKeyb PCKeyb;
655 struct PCKeyb {
656         u8int buf[64];
657         u8int bufr, bufw;
658         u8int actcmd;
659         u8int quiet;
660 } kbd;
661 typedef struct PCMouse PCMouse;
662 struct PCMouse {
663         Mouse;
664         u8int gotmouse;
665         enum {
666                 MOUSERESET,
667                 MOUSESTREAM,
668                 MOUSEREMOTE,
669                 MOUSEREP = 0x10,
670                 MOUSEWRAP = 0x20,
671         } state;
672         u8int buf[64];
673         u8int bufr, bufw;
674         u8int actcmd, id;
675         u8int scaling21, res;
676         u8int ratepp, ratep, rate;
677         int scroll;
678 } mouse = {
679         .res = 2,
680         .rate = 100
681 };
682 #define keyputc(c) kbd.buf[kbd.bufw++ & 63] = (c)
683 #define mouseputc(c) mouse.buf[mouse.bufw++ & 63] = (c)
684
685 static void
686 i8042putbuf(u16int val)
687 {
688         i8042.buf = val;
689         i8042.stat = i8042.stat & ~0x20 | val >> 4 & 0x20;
690         if((i8042.cfg & 1) != 0 && (val & 0x100) != 0){
691                 irqline(1, 1);
692                 i8042.oport |= 0x10;
693         }
694         if((i8042.cfg & 2) != 0 && (val & 0x200) != 0){
695                 irqline(12, 1);
696                 i8042.oport |= 0x20;
697         }
698         if(val == 0){
699                 irqline(1, 0);
700                 irqline(12, 0);
701                 i8042.oport &= ~0x30;
702                 i8042.stat &= ~1;
703                 i8042kick(nil);
704         }else
705                 i8042.stat |= 1;
706 }
707
708 static void
709 kbdcmd(u8int val)
710 {
711         switch(kbd.actcmd){
712         case 0xf0: /* set scancode set */
713                 keyputc(0xfa);
714                 if(val == 0) keyputc(1);
715                 kbd.actcmd = 0;
716                 break;
717         case 0xed: /* set leds */
718                 keyputc(0xfa);
719                 kbd.actcmd = 0;
720                 break;
721         default:
722                 switch(val){
723                 case 0xed: case 0xf0: kbd.actcmd = val; keyputc(0xfa); break;
724
725                 case 0xff: keyputc(0xfa); keyputc(0xaa); break; /* reset */
726                 case 0xf5: kbd.quiet = 1; keyputc(0xfa); break; /* disable scanning */
727                 case 0xf4: kbd.quiet = 0; keyputc(0xfa); break; /* enable scanning */
728                 case 0xf2: keyputc(0xfa); keyputc(0xab); keyputc(0x41); break; /* keyboard id */
729                 case 0xee: keyputc(0xee); break; /* echo */
730                 default:
731                         vmdebug("unknown kbd command %#ux", val);
732                         keyputc(0xfe);
733                 }
734         }
735         i8042kick(nil);
736 }
737
738 static void
739 updatemouse(void)
740 {
741         Mouse m;
742         
743         while(nbrecv(mousech, &m) > 0){
744                 mouse.xy = addpt(mouse.xy, m.xy);
745                 mouse.buttons = m.buttons;
746                 if(m.buttons & 24)
747                         mouse.scroll += (m.buttons>>2 & 6) - 3;
748                 mouse.gotmouse = 1;
749         }
750 }
751
752 static void
753 clearmouse(void)
754 {
755         updatemouse();
756         mouse.xy = Pt(0, 0);
757         mouse.scroll = 0;
758         mouse.gotmouse = 0;
759 }
760
761 static void
762 mousepacket(int force)
763 {
764         int dx, dy, dz;
765         u8int b0;
766
767         updatemouse();
768         if(!mouse.gotmouse && !force)
769                 return;
770         dx = mouse.xy.x;
771         dy = -mouse.xy.y;
772         dz = MIN(7, MAX(-8, mouse.scroll));
773         b0 = 8;
774         if((ulong)(dx + 256) > 511) dx = dx >> 31 ^ 0xff;
775         if((ulong)(dy + 256) > 511) dy = dy >> 31 ^ 0xff;
776         b0 |= dx >> 5 & 0x10 | dy >> 4 & 0x20;
777         b0 |= (mouse.buttons * 0x111 & 0x421) % 7;
778         mouseputc(b0);
779         mouseputc((u8int)dx);
780         mouseputc((u8int)dy);
781         if(mouse.id == 3)
782                 mouseputc((u8int)dz);
783         mouse.xy.x -= dx;
784         mouse.xy.y += dy;
785         mouse.scroll -= dz;
786         mouse.gotmouse = mouse.xy.x != 0 || mouse.xy.y != 0 || mouse.scroll != 0;
787 }
788
789 static void
790 mousedefaults(void)
791 {
792         clearmouse();
793         mouse.res = 2;
794         mouse.rate = 100;
795 }
796
797 static void
798 mousecmd(u8int val)
799 {
800         if((mouse.state & MOUSEWRAP) != 0 && val != 0xec && val != 0xff){
801                 mouseputc(val);
802                 i8042kick(nil);
803                 return;
804         }
805         switch(mouse.actcmd){
806         case 0xe8: /* set resolution */
807                 mouse.res = val;
808                 mouseputc(0xfa);
809                 mouse.actcmd = 0;
810                 break;
811         case 0xf3: /* set sampling rate */
812                 mouse.ratepp = mouse.ratep;
813                 mouse.ratep = mouse.rate;
814                 mouse.rate = val;
815                 if(mouse.ratepp == 200 && mouse.ratep == 100 && mouse.rate == 80)
816                         mouse.id = 3; /* magic sequence for IntelliMouse */
817                 mouseputc(0xfa);
818                 mouse.actcmd = 0;
819                 break;
820         default:
821                 switch(val){
822                 case 0xf3: case 0xe8: mouseputc(0xfa); mouse.actcmd = val; break;
823                 case 0xff: mouseputc(0xfa); mousedefaults(); mouse.state = MOUSERESET; break; /* reset */
824                 case 0xf6: mouseputc(0xfa); mousedefaults(); mouse.state = mouse.state & ~0xf | MOUSESTREAM; break; /* set defaults */
825                 case 0xf5: mouseputc(0xfa); clearmouse(); if((mouse.state&0xf) == MOUSESTREAM) mouse.state &= ~MOUSEREP; break; /* disable reporting */
826                 case 0xf4: mouseputc(0xfa); clearmouse(); if((mouse.state&0xf) == MOUSESTREAM) mouse.state |= MOUSEREP; break; /* enable reporting */
827                 case 0xf2: mouseputc(0xfa); mouseputc(mouse.id); clearmouse(); break; /* report device id */
828                 case 0xf0: mouseputc(0xfa); clearmouse(); mouse.state = mouse.state & ~0xf | MOUSEREMOTE; break; /* set remote mode */
829                 case 0xee: mouseputc(0xfa); clearmouse(); mouse.state |= MOUSEWRAP; break; /* set wrap mode */
830                 case 0xec: mouseputc(0xfa); clearmouse(); mouse.state &= ~MOUSEWRAP; break; /* reset wrap mode */
831                 case 0xeb: mouseputc(0xfa); mousepacket(1); break; /* read data */
832                 case 0xea: mouseputc(0xfa); clearmouse(); mouse.state = mouse.state & ~0xf | MOUSESTREAM; break; /* set stream mode */
833                 case 0xe9: /* status request */
834                         mouseputc(0xfa);
835                         mouseputc(((mouse.state & 0xf) == MOUSEREMOTE) << 6 | ((mouse.state & MOUSEREP) != 0) << 5 | mouse.scaling21 << 4 | (mouse.buttons * 0x111 & 0x142) % 7);
836                         mouseputc(mouse.res);
837                         mouseputc(mouse.rate);
838                         break;
839                 case 0xe7: mouseputc(0xfa); mouse.scaling21 = 1; break; /* set 2:1 scaling */
840                 case 0xe6: mouseputc(0xfa); mouse.scaling21 = 0; break; /* set 1:1 scaling */
841                 
842                 case 0x88: case 0x00: case 0x0a: /* sentelic & cypress */
843                 case 0xe1: /* trackpoint */
844                         mouseputc(0xfe);
845                         break;
846
847                 default: vmerror("unknown mouse command %#ux", val); mouseputc(0xfe);
848                 }
849         }
850         i8042kick(nil);
851 }
852
853 static void
854 mousekick(void)
855 {       
856         switch(mouse.state){
857         case MOUSERESET:
858                 mouseputc(0xaa);
859                 mouseputc(0);
860                 mouse.state = MOUSESTREAM;
861                 break;
862         case MOUSESTREAM | MOUSEREP:
863                 if(mouse.actcmd == 0)
864                         mousepacket(0);
865                 break;
866         }
867 }
868
869
870 void
871 i8042kick(void *)
872 {
873         ulong ch;
874         
875         if((i8042.cfg & 0x10) == 0 && i8042.buf == 0)
876                 if(kbd.bufr != kbd.bufw)
877                         i8042putbuf(0x100 | kbd.buf[kbd.bufr++ & 63]);
878                 else if(!kbd.quiet && nbrecv(kbdch, &ch) > 0)
879                         i8042putbuf(0x100 | (u8int)ch);
880         if((i8042.cfg & 0x20) == 0 && i8042.buf == 0){
881                 if(mouse.bufr == mouse.bufw)
882                         mousekick();
883                 if(mouse.bufr != mouse.bufw)
884                         i8042putbuf(0x200 | mouse.buf[mouse.bufr++ & 63]);
885         }
886 }
887
888 static u32int
889 i8042io(int isin, u16int port, u32int val, int sz, void *)
890 {
891         int rc;
892
893         val = (u8int)val;
894         switch(isin << 16 | port){
895         case 0x60:
896                 i8042.stat &= ~8;
897                 switch(i8042.cmd){
898                 case 0x60: i8042.cfg = val; mouseactive = (val & 0x20) == 0; break;
899                 case 0xd1:
900                         i8042.oport = val;
901                         irqline(1, i8042.oport >> 4 & 1);
902                         irqline(12, i8042.oport >> 5 & 1);
903                         break;
904                 case 0xd2: i8042putbuf(0x100 | val); break;
905                 case 0xd3: i8042putbuf(0x200 | val); break;
906                 case 0xd4: mousecmd(val); break;
907                 case -1: kbdcmd(val); break;
908                 }
909                 i8042.cmd = -1;
910                 return 0;
911         case 0x10060:
912                 i8042kick(nil);
913                 rc = i8042.buf;
914                 i8042putbuf(0);
915                 return rc;
916         case 0x64:
917                 i8042.stat |= 8;
918                 switch(val){
919                 case 0x20: i8042putbuf(0x400 | i8042.cfg); return 0;
920                 case 0xa1: i8042putbuf(0x4f1); return 0; /* no keyboard password */
921                 case 0xa7: i8042.cfg |= 1<<5; mouseactive = 0; return 0;
922                 case 0xa8: i8042.cfg &= ~(1<<5); mouseactive = 1; return 0;
923                 case 0xa9: i8042putbuf(0x400); return 0; /* test second port */
924                 case 0xaa: i8042putbuf(0x455); return 0; /* test controller */
925                 case 0xab: i8042putbuf(0x400); return 0; /* test first port */
926                 case 0xad: i8042.cfg |= 1<<4; return 0;
927                 case 0xae: i8042.cfg &= ~(1<<4); return 0;
928                 case 0xd0: i8042putbuf(0x400 | i8042.oport); return 0;
929                 case 0x60: case 0xd1: case 0xd2: case 0xd3: case 0xd4:
930                         i8042.cmd = val;
931                         return 0;
932                 case 0xf0: case 0xf2: case 0xf4: case 0xf6: /* pulse reset line */
933                 case 0xf8: case 0xfa: case 0xfc: case 0xfe:
934                         sysfatal("i8042: system reset");
935                 case 0xf1: case 0xf3: case 0xf5: case 0xf7: /* no-op */
936                 case 0xf9: case 0xfb: case 0xfd: case 0xff:
937                         return 0;
938                 }
939                 vmerror("unknown i8042 command %#ux", val);
940                 return 0;
941         case 0x10064:
942                 i8042kick(nil);
943                 return i8042.stat | i8042.cfg & 4;
944         }
945         return iowhine(isin, port, val, sz, "i8042");
946 }
947
948 typedef struct UART UART;
949 struct UART {
950         u8int ier, fcr, lcr, lsr, mcr, scr, dll, dlh;
951         u8int rbr, tbr;
952         enum {
953                 UARTTXIRQ = 2,
954                 UARTRXIRQ = 1,
955         } irq;
956         int infd, outfd;
957         Channel *inch, *outch;
958 } uart[2] = { { .lsr = 0x60 }, { .lsr = 0x60 } };
959
960 static void
961 uartkick(UART *p)
962 {
963         char c;
964
965         irqline(4 - (p - uart), (p->irq & p->ier) != 0);
966         if((p->irq & UARTRXIRQ) == 0 && p->inch != nil && nbrecv(p->inch, &c) > 0){
967                 p->rbr = c;
968                 p->irq |= UARTRXIRQ;
969         }
970         if((p->lsr & 1<<5) == 0){
971                 if(p->outch == nil){
972                         p->lsr |= 3<<5;
973                         p->irq |= UARTTXIRQ;
974                 }else if(nbsend(p->outch, &p->tbr) > 0){
975                         p->tbr = 0;
976                         p->lsr |= 3<<5;
977                         p->irq |= UARTTXIRQ;
978                 }
979         }
980         irqline(4 - (p - uart), (p->irq & p->ier) != 0);
981 }
982
983 static u32int
984 uartio(int isin, u16int port, u32int val, int sz, void *)
985 {
986         UART *p;
987         int rc;
988
989         if((port & 0xff8) == 0x3f8) p = &uart[0];
990         else if((port & 0xff8) == 0x2f8) p = &uart[1];
991         else return 0;
992         
993         val = (u8int) val;
994         switch(isin << 4 | port & 7){
995         case 0x00:
996                 if((p->lcr & 1<<7) != 0)
997                         p->dll = val;
998                 else{ /* transmit byte */
999                         if((p->mcr & 1<<4) != 0){
1000                                 p->irq |= UARTRXIRQ;
1001                                 p->rbr = val;
1002                                 p->lsr |= 3<<5;
1003                         }else{
1004                                 p->tbr = val;
1005                                 p->lsr &= ~(3<<5);
1006                                 p->irq &= ~UARTTXIRQ;
1007                         }
1008                         uartkick(p);
1009                 }
1010                 return 0;
1011         case 0x01:
1012                 if((p->lcr & 1<<7) != 0)
1013                         p->dlh = val;
1014                 else
1015                         p->ier = val & 15;
1016                 return 0;
1017         case 0x02: p->fcr = val; return 0;
1018         case 0x03: p->lcr = val; return 0;
1019         case 0x04: p->mcr = val & 0x1f; return 0;
1020         case 0x07: p->scr = val; return 0;
1021         case 0x10:
1022                 if((p->lcr & 1<<7) != 0) return p->dll;
1023                 p->irq &= ~UARTRXIRQ;
1024                 rc = p->rbr;
1025                 uartkick(p);
1026                 return rc;
1027         case 0x11:
1028                 if((p->lcr & 1<<7) != 0) return p->dlh;
1029                 return p->ier;
1030         case 0x12:
1031                 rc = 0;
1032                 uartkick(p);
1033                 if((p->irq & UARTRXIRQ) != 0)
1034                         return rc | 4;
1035                 else if((p->irq & UARTTXIRQ) != 0){
1036                         p->irq &= ~UARTTXIRQ;
1037                         uartkick(p);
1038                         return rc | 2;
1039                 }else
1040                         return rc | 1;
1041         case 0x13: return p->lcr;
1042         case 0x14: return p->mcr;
1043         case 0x15:
1044                 uartkick(p);
1045                 rc = p->lsr; /* line status */
1046                 if((p->irq & UARTRXIRQ) != 0)
1047                         rc |= 1;
1048                 return rc;
1049         case 0x16: /* modem status */
1050                 if((p->mcr & 0x10) != 0)
1051                         return (p->mcr << 1 & 2 | p->mcr >> 1 & 1 | p->mcr & 0xc) << 4;
1052                 return 0x90; /* DCD + CTS asserted */
1053         case 0x17: return p->scr;
1054         }
1055         return iowhine(isin, port, val, sz, "uart");
1056 }
1057
1058 static void
1059 uartrxproc(void *uv)
1060 {
1061         UART *u;
1062         char buf[128], *p;
1063         int rc;
1064         int eofctr;
1065         
1066         threadsetname("uart rx");
1067         u = uv;
1068         eofctr = 0;
1069         for(;;){
1070                 rc = read(u->infd, buf, sizeof(buf));
1071                 if(rc < 0){
1072                         vmerror("read(uartrx): %r");
1073                         threadexits("read: %r");
1074                 }
1075                 if(rc == 0){
1076                         if(++eofctr == 100){ /* keep trying but give up eventually */
1077                                 vmerror("read(uartrx): eof");
1078                                 threadexits("read: eof");
1079                         }
1080                         continue;
1081                 }else eofctr = 0;
1082                 for(p = buf; p < buf + rc; p++){
1083                         send(u->inch, p);
1084                         sendnotif((void(*)(void*))uartkick, u);
1085                 }
1086         }
1087 }
1088
1089 static void
1090 uarttxproc(void *uv)
1091 {
1092         UART *u;
1093         char buf[128], *p;
1094         
1095         threadsetname("uart tx");
1096         u = uv;
1097         for(;;){
1098                 p = buf;
1099                 recv(u->outch, p);
1100                 p++;
1101                 sendnotif((void(*)(void*))uartkick, u);
1102                 sleep(1);
1103                 while(sendnotif((void(*)(void*))uartkick, u), p < buf+sizeof(buf) && nbrecv(u->outch, p) > 0)
1104                         p++;
1105                 if(write(u->outfd, buf, p - buf) < p - buf)
1106                         vmerror("write(uarttx): %r");
1107         }
1108 }
1109
1110 void
1111 uartinit(int n, char *cfg)
1112 {
1113         char *p, *infn, *outfn;
1114         
1115         p = strchr(cfg, ',');
1116         if(p == nil){
1117                 infn = cfg;
1118                 outfn = cfg;
1119         }else{
1120                 *p = 0;
1121                 infn = cfg;
1122                 outfn = p + 1;
1123         }
1124         if(infn != nil && *infn != 0){
1125                 uart[n].infd = open(infn, OREAD);
1126                 if(uart[n].infd < 0)
1127                         sysfatal("open: %r");
1128                 uart[n].inch = chancreate(sizeof(char), 256);
1129                 proccreate(uartrxproc, &uart[n], 4096);
1130         }
1131         if(outfn != nil && *outfn != 0){
1132                 uart[n].outfd = open(outfn, OWRITE);
1133                 if(uart[n].outfd < 0)
1134                         sysfatal("open: %r");
1135                 uart[n].outch = chancreate(sizeof(char), 256);
1136                 proccreate(uarttxproc, &uart[n], 4096);
1137         }
1138 }
1139
1140
1141 /* floppy dummy controller */
1142 typedef struct Floppy Floppy;
1143 struct Floppy {
1144         u8int dor;
1145         u8int dump, irq;
1146         u8int fdc;
1147         u8int inq[16];
1148         u8int inqr, inqw;
1149 } fdc;
1150 #define fdcputc(c) (fdc.inq[fdc.inqw++ & 15] = (c))
1151
1152 static void
1153 fdccmd(u8int val)
1154 {
1155         vmdebug("fdc: cmd %#x", val);
1156         switch(val){
1157         case 0x03:
1158                 fdc.dump = 2;
1159                 break;
1160         case 0x07:
1161                 fdc.dump = 1;
1162                 fdc.irq = 1;
1163                 break;
1164         case 0x08:
1165                 irqline(6, 0);
1166                 fdcputc(0x80);
1167                 fdcputc(0);
1168                 break;
1169         default:
1170                 vmerror("unknown fdc command %.2x", val);
1171         }
1172 }
1173
1174 static u32int
1175 fdcio(int isin, u16int port, u32int val, int sz, void *)
1176 {
1177         u8int rc;
1178
1179         if(sz != 1) vmerror("fdc: access size %d != 1", sz);
1180         val = (u8int) val;
1181         switch(isin << 4 | port & 7){
1182         case 0x02: fdc.dor = val; return 0;
1183         case 0x05:
1184                 if(fdc.dump > 0){
1185                         if(--fdc.dump == 0 && fdc.inqr == fdc.inqw && fdc.irq != 0){
1186                                 irqline(6, 1);
1187                                 fdc.irq = 0;
1188                         }
1189                 }else if(fdc.inqr == fdc.inqw)
1190                         fdccmd(val);
1191                 return 0;
1192         case 0x12: return fdc.dor;
1193         case 0x14:
1194                 rc = 0x80;
1195                 if(fdc.dump == 0 && fdc.inqr != fdc.inqw)
1196                         rc |= 0x40;
1197                 if(fdc.dump != 0 || fdc.inqr != fdc.inqw)
1198                         rc |= 0x10;
1199                 return rc;
1200         case 0x15:
1201                 if(fdc.dump == 0 && fdc.inqr != fdc.inqw)
1202                         return fdc.inq[fdc.inqr++ & 15];
1203                 return 0;
1204         }
1205         return iowhine(isin, port, val, sz, "fdc");
1206 }
1207
1208 static u32int
1209 nopio(int, u16int, u32int, int, void *)
1210 {
1211         return -1;
1212 }
1213
1214 u32int
1215 iowhine(int isin, u16int port, u32int val, int sz, void *mod)
1216 {
1217         if(isin)
1218                 vmdebug("%s%sread from unknown i/o port %#ux ignored (sz=%d, pc=%#ullx)", mod != nil ? mod : "", mod != nil ? ": " : "", port, sz, rget(RPC));
1219         else
1220                 vmdebug("%s%swrite to unknown i/o port %#ux ignored (val=%#ux, sz=%d, pc=%#ullx)", mod != nil ? mod : "", mod != nil ? ": " : "", port, val, sz, rget(RPC));
1221         return -1;
1222 }
1223
1224 typedef struct IOHandler IOHandler;
1225 struct IOHandler {
1226         u16int lo, hi;
1227         u32int (*io)(int, u16int, u32int, int, void *);
1228         void *aux;
1229 };
1230
1231 u32int vgaio(int, u16int, u32int, int, void *);
1232 u32int pciio(int, u16int, u32int, int, void *);
1233 u32int vesaio(int, u16int, u32int, int, void *);
1234 u32int ideio(int, u16int, u32int, int, void *);
1235 IOHandler handlers[] = {
1236         0x20, 0x21, picio, nil,
1237         0x40, 0x43, pitio, nil,
1238         0x70, 0x71, rtcio, nil,
1239         0xa0, 0xa1, picio, nil,
1240         0x60, 0x60, i8042io, nil,
1241         0x61, 0x61, pitio, nil, /* pc speaker */
1242         0x64, 0x64, i8042io, nil,
1243         0x2f8, 0x2ff, uartio, nil,
1244         0x3b0, 0x3bb, vgaio, nil,
1245         0x3c0, 0x3df, vgaio, nil,
1246         0x3f8, 0x3ff, uartio, nil,
1247         0x4d0, 0x4d1, picio, nil,
1248         0xcf8, 0xcff, pciio, nil,
1249         0xfee0, 0xfeef, vesaio, nil,
1250
1251         0x170, 0x177, ideio, nil, /* ide secondary */
1252         0x376, 0x376, ideio, nil, /* ide secondary (aux) */
1253         0x1f0, 0x1f7, ideio, nil, /* ide primary */
1254         0x3f6, 0x3f6, ideio, nil, /* ide primary (aux) */
1255         0x3f0, 0x3f7, fdcio, nil, /* floppy */
1256
1257         0x080, 0x080, nopio, nil, /* dma -- used by linux for delay by dummy write */
1258         0x084, 0x084, nopio, nil, /* dma -- used by openbsd for delay by dummy read */
1259         0x100, 0x110, nopio, nil, /* elnk3 */
1260         0x240, 0x25f, nopio, nil, /* ne2000 */
1261         0x278, 0x27a, nopio, nil, /* LPT1 / ISA PNP */
1262         0x280, 0x29f, nopio, nil, /* ne2000 */
1263         0x2e8, 0x2ef, nopio, nil, /* COM4 */
1264         0x300, 0x31f, nopio, nil, /* ne2000 */
1265         0x320, 0x32f, nopio, nil, /* etherexpress */
1266         0x330, 0x33f, nopio, nil, /* uha scsi */
1267         0x340, 0x35f, nopio, nil, /* adaptec scsi */
1268         0x360, 0x373, nopio, nil, /* isolan */
1269         0x378, 0x37a, nopio, nil, /* LPT1 */
1270         0x3e0, 0x3e5, nopio, nil, /* cardbus or isa pci bridges */
1271         0x3e8, 0x3ef, nopio, nil, /* COM3 */
1272         0x650, 0x65f, nopio, nil, /* 3c503 ethernet */
1273         0x778, 0x77a, nopio, nil, /* LPT1 (ECP) */
1274         0xa79, 0xa79, nopio, nil, /* isa pnp */
1275 };
1276
1277 static u32int
1278 io0(int dir, u16int port, u32int val, int size)
1279 {
1280         IOHandler *h;
1281         extern PCIBar iobars;
1282         PCIBar *p;
1283
1284         for(h = handlers; h < handlers + nelem(handlers); h++)
1285                 if(port >= h->lo && port <= h->hi)
1286                         return h->io(dir, port, val, size, h->aux);
1287         for(p = iobars.busnext; p != &iobars; p = p->busnext)
1288                 if(port >= p->addr && port < p->addr + p->length)
1289                         return p->io(dir, port - p->addr, val, size, p->aux);
1290         return iowhine(dir, port, val, size, nil);
1291 }
1292
1293 u32int iodebug[32];
1294
1295 u32int
1296 io(int isin, u16int port, u32int val, int sz)
1297 {
1298         int dbg;
1299         
1300         dbg = port < 0x400 && (iodebug[port >> 5] >> (port & 31) & 1) != 0;
1301         if(isin){
1302                 val = io0(isin, port, val, sz);
1303                 if(sz == 1) val = (u8int)val;
1304                 else if(sz == 2) val = (u16int)val;
1305                 if(dbg)
1306                         vmdebug("in  %#.4ux <- %#.*ux", port, sz*2, val);
1307                 return val;
1308         }else{
1309                 if(sz == 1) val = (u8int)val;
1310                 else if(sz == 2) val = (u16int)val;
1311                 io0(isin, port, val, sz);
1312                 if(dbg)
1313                         vmdebug("out %#.4ux <- %#.*ux", port, sz*2, val);
1314                 return 0;
1315         }
1316 }