1 /*PC1640 video emulation.
2   Mostly standard EGA, but with CGA & Hercules emulation*/
3 #include <stdlib.h>
4 #include "ibm.h"
5 #include "device.h"
6 #include "io.h"
7 #include "mem.h"
8 #include "rom.h"
9 #include "timer.h"
10 #include "video.h"
11 #include "vid_cga.h"
12 #include "vid_ega.h"
13 #include "vid_pc1640.h"
14 
15 typedef struct pc1640_t
16 {
17         mem_mapping_t cga_mapping;
18         mem_mapping_t ega_mapping;
19 
20         cga_t cga;
21         ega_t ega;
22 
23         rom_t bios_rom;
24 
25         int cga_enabled;
26         int dispontime, dispofftime, vidtime;
27 } pc1640_t;
28 
pc1640_out(uint16_t addr,uint8_t val,void * p)29 void pc1640_out(uint16_t addr, uint8_t val, void *p)
30 {
31         pc1640_t *pc1640 = (pc1640_t *)p;
32 
33         switch (addr)
34         {
35                 case 0x3db:
36                 pc1640->cga_enabled = val & 0x40;
37                 if (pc1640->cga_enabled)
38                 {
39                         mem_mapping_enable(&pc1640->cga_mapping);
40                         mem_mapping_disable(&pc1640->ega_mapping);
41                 }
42                 else
43                 {
44                         mem_mapping_disable(&pc1640->cga_mapping);
45                         switch (pc1640->ega.gdcreg[6] & 0xc)
46                         {
47                                 case 0x0: /*128k at A0000*/
48                                 mem_mapping_set_addr(&pc1640->ega_mapping, 0xa0000, 0x20000);
49                                 break;
50                                 case 0x4: /*64k at A0000*/
51                                 mem_mapping_set_addr(&pc1640->ega_mapping, 0xa0000, 0x10000);
52                                 break;
53                                 case 0x8: /*32k at B0000*/
54                                 mem_mapping_set_addr(&pc1640->ega_mapping, 0xb0000, 0x08000);
55                                 break;
56                                 case 0xC: /*32k at B8000*/
57                                 mem_mapping_set_addr(&pc1640->ega_mapping, 0xb8000, 0x08000);
58                                 break;
59                         }
60                 }
61                 pclog("3DB write %02X\n", val);
62                 return;
63         }
64         if (pc1640->cga_enabled) cga_out(addr, val, &pc1640->cga);
65         else                     ega_out(addr, val, &pc1640->ega);
66 }
67 
pc1640_in(uint16_t addr,void * p)68 uint8_t pc1640_in(uint16_t addr, void *p)
69 {
70         pc1640_t *pc1640 = (pc1640_t *)p;
71 
72         switch (addr)
73         {
74         }
75 
76         if (pc1640->cga_enabled) return cga_in(addr, &pc1640->cga);
77         else                     return ega_in(addr, &pc1640->ega);
78 }
79 
pc1640_recalctimings(pc1640_t * pc1640)80 void pc1640_recalctimings(pc1640_t *pc1640)
81 {
82         cga_recalctimings(&pc1640->cga);
83         ega_recalctimings(&pc1640->ega);
84         if (pc1640->cga_enabled)
85         {
86                 pc1640->dispontime  = pc1640->cga.dispontime;
87                 pc1640->dispofftime = pc1640->cga.dispofftime;
88         }
89         else
90         {
91                 pc1640->dispontime  = pc1640->ega.dispontime;
92                 pc1640->dispofftime = pc1640->ega.dispofftime;
93         }
94 }
95 
pc1640_poll(void * p)96 void pc1640_poll(void *p)
97 {
98         pc1640_t *pc1640 = (pc1640_t *)p;
99         if (pc1640->cga_enabled)
100         {
101                 pc1640->cga.vidtime = pc1640->vidtime;
102                 cga_poll(&pc1640->cga);
103                 pc1640->vidtime = pc1640->cga.vidtime;
104         }
105         else
106         {
107                 pc1640->ega.vidtime = pc1640->vidtime;
108                 ega_poll(&pc1640->ega);
109                 pc1640->vidtime = pc1640->ega.vidtime;
110         }
111 }
112 
pc1640_init()113 void *pc1640_init()
114 {
115         pc1640_t *pc1640 = malloc(sizeof(pc1640_t));
116         cga_t *cga = &pc1640->cga;
117         ega_t *ega = &pc1640->ega;
118         memset(pc1640, 0, sizeof(pc1640_t));
119 
120         rom_init(&pc1640->bios_rom, "pc1640/40100", 0xc0000, 0x8000, 0x7fff, 0, 0);
121 
122         ega_init(&pc1640->ega, 9, 0);
123         pc1640->cga.vram = pc1640->ega.vram;
124         pc1640->cga_enabled = 1;
125         cga_init(&pc1640->cga);
126 
127         timer_add(pc1640_poll, &pc1640->vidtime, TIMER_ALWAYS_ENABLED, pc1640);
128         mem_mapping_add(&pc1640->cga_mapping, 0xb8000, 0x08000, cga_read, NULL, NULL, cga_write, NULL, NULL,  NULL, 0, cga);
129         mem_mapping_add(&pc1640->ega_mapping, 0,       0,       ega_read, NULL, NULL, ega_write, NULL, NULL,  NULL, 0, ega);
130         io_sethandler(0x03a0, 0x0040, pc1640_in, NULL, NULL, pc1640_out, NULL, NULL, pc1640);
131         return pc1640;
132 }
133 
pc1640_close(void * p)134 void pc1640_close(void *p)
135 {
136         pc1640_t *pc1640 = (pc1640_t *)p;
137 
138         free(pc1640->ega.vram);
139         free(pc1640);
140 }
141 
pc1640_speed_changed(void * p)142 void pc1640_speed_changed(void *p)
143 {
144         pc1640_t *pc1640 = (pc1640_t *)p;
145 
146         pc1640_recalctimings(pc1640);
147 }
148 
149 device_t pc1640_device =
150 {
151         "Amstrad PC1640 (video)",
152         0,
153         pc1640_init,
154         pc1640_close,
155         NULL,
156         pc1640_speed_changed,
157         NULL,
158         NULL
159 };
160