wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Mostly done after the Scitech Bios emulation |
| 3 | * Written by Hans-Jörg Frieden |
| 4 | * Hyperion Entertainment |
| 5 | */ |
| 6 | #include "x86emu.h" |
| 7 | #include "glue.h" |
| 8 | |
| 9 | #undef DEBUG |
| 10 | #ifdef DEBUG |
| 11 | #define PRINTF(fmt, args...) printf(fmt, ## args) |
| 12 | #else |
| 13 | #define PRINTF(fmt, args...) |
| 14 | #endif |
| 15 | |
| 16 | #define BIOS_SEG 0xFFF0 |
| 17 | #define PCIBIOS_SUCCESSFUL 0 |
| 18 | #define PCIBIOS_DEVICE_NOT_FOUND 0x86 |
| 19 | |
| 20 | typedef unsigned char UBYTE; |
| 21 | typedef unsigned short UWORD; |
| 22 | typedef unsigned long ULONG; |
| 23 | |
| 24 | typedef char BYTE; |
| 25 | typedef short WORT; |
| 26 | typedef long LONG; |
| 27 | |
| 28 | static inline UBYTE read_byte(volatile UBYTE* from) |
| 29 | { |
| 30 | int x; |
| 31 | asm volatile ("lbz %0,%1\n eieio" : "=r" (x) : "m" (*from)); |
| 32 | return (UBYTE)x; |
| 33 | } |
| 34 | |
| 35 | static inline void write_byte(volatile UBYTE *to, int x) |
| 36 | { |
| 37 | asm volatile ("stb %1,%0\n eieio" : "=m" (*to) : "r" (x)); |
| 38 | } |
| 39 | |
| 40 | static inline UWORD read_word_little(volatile UWORD *from) |
| 41 | { |
| 42 | int x; |
| 43 | asm volatile ("lhbrx %0,0,%1\n eieio" : "=r" (x) : "r" (from), "m" (*from)); |
| 44 | return (UWORD)x; |
| 45 | } |
| 46 | |
| 47 | static inline UWORD read_word_big(volatile UWORD *from) |
| 48 | { |
| 49 | int x; |
| 50 | asm volatile ("lhz %0,%1\n eieio" : "=r" (x) : "m" (*from)); |
| 51 | return (UWORD)x; |
| 52 | } |
| 53 | |
| 54 | static inline void write_word_little(volatile UWORD *to, int x) |
| 55 | { |
| 56 | asm volatile ("sthbrx %1,0,%2\n eieio" : "=m" (*to) : "r" (x), "r" (to)); |
| 57 | } |
| 58 | |
| 59 | static inline void write_word_big(volatile UWORD *to, int x) |
| 60 | { |
| 61 | asm volatile ("sth %1,%0\n eieio" : "=m" (*to) : "r" (x)); |
| 62 | } |
| 63 | |
| 64 | static inline ULONG read_long_little(volatile ULONG *from) |
| 65 | { |
| 66 | unsigned long x; |
| 67 | asm volatile ("lwbrx %0,0,%1\n eieio" : "=r" (x) : "r" (from), "m"(*from)); |
| 68 | return (ULONG)x; |
| 69 | } |
| 70 | |
| 71 | static inline ULONG read_long_big(volatile ULONG *from) |
| 72 | { |
| 73 | unsigned long x; |
| 74 | asm volatile ("lwz %0,%1\n eieio" : "=r" (x) : "m" (*from)); |
| 75 | return (ULONG)x; |
| 76 | } |
| 77 | |
| 78 | static inline void write_long_little(volatile ULONG *to, ULONG x) |
| 79 | { |
| 80 | asm volatile ("stwbrx %1,0,%2\n eieio" : "=m" (*to) : "r" (x), "r" (to)); |
| 81 | } |
| 82 | |
| 83 | static inline void write_long_big(volatile ULONG *to, ULONG x) |
| 84 | { |
| 85 | asm volatile ("stw %1,%0\n eieio" : "=m" (*to) : "r" (x)); |
| 86 | } |
| 87 | |
| 88 | #define port_to_mem(from) (0xFE000000|(from)) |
| 89 | #define in_byte(from) read_byte( (UBYTE *)port_to_mem(from)) |
| 90 | #define in_word(from) read_word_little((UWORD *)port_to_mem(from)) |
| 91 | #define in_long(from) read_long_little((ULONG *)port_to_mem(from)) |
| 92 | #define out_byte(to, val) write_byte((UBYTE *)port_to_mem(to), val) |
| 93 | #define out_word(to, val) write_word_little((UWORD *)port_to_mem(to), val) |
| 94 | #define out_long(to, val) write_long_little((ULONG *)port_to_mem(to), val) |
| 95 | |
| 96 | static void X86API undefined_intr(int intno) |
| 97 | { |
| 98 | extern u16 A1_rdw(u32 addr); |
| 99 | if (A1_rdw(intno * 4 + 2) == BIOS_SEG) |
| 100 | { |
| 101 | PRINTF("Undefined interrupt %xh called AX = %xh, BX = %xh, CX = %xh, DX = %xh\n", |
| 102 | intno, M.x86.R_AX, M.x86.R_BX, M.x86.R_CX, M.x86.R_DX); |
| 103 | X86EMU_halt_sys(); |
| 104 | } |
| 105 | else |
| 106 | { |
| 107 | PRINTF("Calling interrupt %xh, AL=%xh, AH=%xh\n", intno, M.x86.R_AL, M.x86.R_AH); |
| 108 | X86EMU_prepareForInt(intno); |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | static void X86API int42(int intno); |
| 113 | static void X86API int15(int intno); |
| 114 | |
| 115 | static void X86API int10(int intno) |
| 116 | { |
| 117 | if (A1_rdw(intno*4+2) == BIOS_SEG) |
| 118 | int42(intno); |
| 119 | else |
| 120 | { |
| 121 | PRINTF("int10: branching to %04X:%04X, AL=%xh, AH=%xh\n", A1_rdw(intno*4+2), A1_rdw(intno*4), |
| 122 | M.x86.R_AL, M.x86.R_AH); |
| 123 | X86EMU_prepareForInt(intno); |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | static void X86API int1A(int intno) |
| 128 | { |
| 129 | int device; |
| 130 | |
| 131 | switch(M.x86.R_AX) |
| 132 | { |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 133 | case 0xB101: /* PCI Bios Present? */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 134 | M.x86.R_AL = 0x00; |
| 135 | M.x86.R_EDX = 0x20494350; |
| 136 | M.x86.R_BX = 0x0210; |
| 137 | M.x86.R_CL = 3; |
| 138 | CLEAR_FLAG(F_CF); |
| 139 | break; |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 140 | case 0xB102: /* Find device */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 141 | device = mypci_find_device(M.x86.R_DX, M.x86.R_CX, M.x86.R_SI); |
| 142 | if (device != -1) |
| 143 | { |
| 144 | M.x86.R_AH = PCIBIOS_SUCCESSFUL; |
| 145 | M.x86.R_BH = mypci_bus(device); |
| 146 | M.x86.R_BL = mypci_devfn(device); |
| 147 | } |
| 148 | else |
| 149 | { |
| 150 | M.x86.R_AH = PCIBIOS_DEVICE_NOT_FOUND; |
| 151 | } |
| 152 | CONDITIONAL_SET_FLAG((M.x86.R_AH != PCIBIOS_SUCCESSFUL), F_CF); |
| 153 | break; |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 154 | case 0xB103: /* Find PCI class code */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 155 | M.x86.R_AH = PCIBIOS_DEVICE_NOT_FOUND; |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 156 | /*printf("Find by class not yet implmented"); */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 157 | CONDITIONAL_SET_FLAG((M.x86.R_AH != PCIBIOS_SUCCESSFUL), F_CF); |
| 158 | break; |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 159 | case 0xB108: /* read config byte */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 160 | M.x86.R_CL = mypci_read_cfg_byte(M.x86.R_BH, M.x86.R_BL, M.x86.R_DI); |
| 161 | M.x86.R_AH = PCIBIOS_SUCCESSFUL; |
| 162 | CONDITIONAL_SET_FLAG((M.x86.R_AH != PCIBIOS_SUCCESSFUL), F_CF); |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 163 | /*printf("read_config_byte %x,%x,%x -> %x\n", M.x86.R_BH, M.x86.R_BL, M.x86.R_DI, */ |
| 164 | /* M.x86.R_CL); */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 165 | break; |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 166 | case 0xB109: /* read config word */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 167 | M.x86.R_CX = mypci_read_cfg_word(M.x86.R_BH, M.x86.R_BL, M.x86.R_DI); |
| 168 | M.x86.R_AH = PCIBIOS_SUCCESSFUL; |
| 169 | CONDITIONAL_SET_FLAG((M.x86.R_AH != PCIBIOS_SUCCESSFUL), F_CF); |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 170 | /*printf("read_config_word %x,%x,%x -> %x\n", M.x86.R_BH, M.x86.R_BL, M.x86.R_DI, */ |
| 171 | /* M.x86.R_CX); */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 172 | break; |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 173 | case 0xB10A: /* read config dword */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 174 | M.x86.R_ECX = mypci_read_cfg_long(M.x86.R_BH, M.x86.R_BL, M.x86.R_DI); |
| 175 | M.x86.R_AH = PCIBIOS_SUCCESSFUL; |
| 176 | CONDITIONAL_SET_FLAG((M.x86.R_AH != PCIBIOS_SUCCESSFUL), F_CF); |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 177 | /*printf("read_config_long %x,%x,%x -> %x\n", M.x86.R_BH, M.x86.R_BL, M.x86.R_DI, */ |
| 178 | /* M.x86.R_ECX); */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 179 | break; |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 180 | case 0xB10B: /* write config byte */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 181 | mypci_write_cfg_byte(M.x86.R_BH, M.x86.R_BL, M.x86.R_DI, M.x86.R_CL); |
| 182 | M.x86.R_AH = PCIBIOS_SUCCESSFUL; |
| 183 | CONDITIONAL_SET_FLAG((M.x86.R_AH != PCIBIOS_SUCCESSFUL), F_CF); |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 184 | /*printf("write_config_byte %x,%x,%x <- %x\n", M.x86.R_BH, M.x86.R_BL, M.x86.R_DI, */ |
| 185 | /* M.x86.R_CL); */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 186 | break; |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 187 | case 0xB10C: /* write config word */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 188 | mypci_write_cfg_word(M.x86.R_BH, M.x86.R_BL, M.x86.R_DI, M.x86.R_CX); |
| 189 | M.x86.R_AH = PCIBIOS_SUCCESSFUL; |
| 190 | CONDITIONAL_SET_FLAG((M.x86.R_AH != PCIBIOS_SUCCESSFUL), F_CF); |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 191 | /*printf("write_config_word %x,%x,%x <- %x\n", M.x86.R_BH, M.x86.R_BL, M.x86.R_DI, */ |
| 192 | /* M.x86.R_CX); */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 193 | break; |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 194 | case 0xB10D: /* write config dword */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 195 | mypci_write_cfg_long(M.x86.R_BH, M.x86.R_BL, M.x86.R_DI, M.x86.R_ECX); |
| 196 | M.x86.R_AH = PCIBIOS_SUCCESSFUL; |
| 197 | CONDITIONAL_SET_FLAG((M.x86.R_AH != PCIBIOS_SUCCESSFUL), F_CF); |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 198 | /*printf("write_config_long %x,%x,%x <- %x\n", M.x86.R_BH, M.x86.R_BL, M.x86.R_DI, */ |
| 199 | /* M.x86.R_ECX); */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 200 | break; |
| 201 | default: |
| 202 | PRINTF("BIOS int %xh: Unknown function AX=%04xh\n", intno, M.x86.R_AX); |
| 203 | |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | void bios_init(void) |
| 208 | { |
| 209 | int i; |
| 210 | X86EMU_intrFuncs bios_intr_tab[256]; |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 211 | |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 212 | for (i=0; i<256; i++) |
| 213 | { |
| 214 | write_long_little(M.mem_base+i*4, BIOS_SEG<<16); |
| 215 | bios_intr_tab[i] = undefined_intr; |
| 216 | } |
| 217 | |
| 218 | bios_intr_tab[0x10] = int10; |
| 219 | bios_intr_tab[0x1A] = int1A; |
| 220 | bios_intr_tab[0x42] = int42; |
| 221 | bios_intr_tab[0x15] = int15; |
| 222 | |
| 223 | bios_intr_tab[0x6D] = int42; |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 224 | |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 225 | X86EMU_setupIntrFuncs(bios_intr_tab); |
| 226 | video_init(); |
| 227 | } |
| 228 | |
| 229 | unsigned char setup_40x25[] = |
| 230 | { |
| 231 | 0x38, 0x28, 0x2d, 0x0a, 0x1f, 6, 0x19, |
| 232 | 0x1c, 2, 7, 6, 7, 0, 0, 0, 0 |
| 233 | }; |
| 234 | |
| 235 | unsigned char setup_80x25[] = |
| 236 | { |
| 237 | 0x71, 0x50, 0x5a, 0x0a, 0x1f, 6, 0x19, |
| 238 | 0x1c, 2, 7, 6, 7, 0, 0, 0, 0 |
| 239 | }; |
| 240 | |
| 241 | unsigned char setup_graphics[] = |
| 242 | { |
| 243 | 0x38, 0x28, 0x20, 0x0a, 0x7f, 6, 0x64, |
| 244 | 0x70, 2, 1, 6, 7, 0, 0, 0, 0 |
| 245 | }; |
| 246 | |
| 247 | unsigned char setup_bw[] = |
| 248 | { |
| 249 | 0x61, 0x50, 0x52, 0x0f, 0x19, 6, 0x19, |
| 250 | 0x19, 2, 0x0d, 0x0b, 0x0c, 0, 0, 0, 0 |
| 251 | }; |
| 252 | |
| 253 | unsigned char * setup_modes[] = |
| 254 | { |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 255 | setup_40x25, /* mode 0: 40x25 bw text */ |
| 256 | setup_40x25, /* mode 1: 40x25 col text */ |
| 257 | setup_80x25, /* mode 2: 80x25 bw text */ |
| 258 | setup_80x25, /* mode 3: 80x25 col text */ |
| 259 | setup_graphics, /* mode 4: 320x200 col graphics */ |
| 260 | setup_graphics, /* mode 5: 320x200 bw graphics */ |
| 261 | setup_graphics, /* mode 6: 640x200 bw graphics */ |
| 262 | setup_bw /* mode 7: 80x25 mono text */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 263 | }; |
| 264 | |
| 265 | unsigned int setup_cols[] = |
| 266 | { |
| 267 | 40, 40, 80, 80, 40, 40, 80, 80 |
| 268 | }; |
| 269 | |
| 270 | unsigned char setup_modesets[] = |
| 271 | { |
| 272 | 0x2C, 0x28, 0x2D, 0x29, 0x2A, 0x2E, 0x1E, 0x29 |
| 273 | }; |
| 274 | |
| 275 | unsigned int setup_bufsize[] = |
| 276 | { |
| 277 | 2048, 2048, 4096, 2096, 16384, 16384, 16384, 4096 |
| 278 | }; |
| 279 | |
| 280 | void bios_set_mode(int mode) |
| 281 | { |
| 282 | int i; |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 283 | unsigned char mode_set = setup_modesets[mode]; /* Control register value */ |
| 284 | unsigned char *setup_regs = setup_modes[mode]; /* Register 3D4 Array */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 285 | |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 286 | /* Switch video off */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 287 | out_byte(0x3D8, mode_set & 0x37); |
| 288 | |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 289 | /* Set up parameters at 3D4h */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 290 | for (i=0; i<16; i++) |
| 291 | { |
| 292 | out_byte(0x3D4, (unsigned char)i); |
| 293 | out_byte(0x3D5, *setup_regs); |
| 294 | setup_regs++; |
| 295 | } |
| 296 | |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 297 | /* Enable video */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 298 | out_byte(0x3D8, mode_set); |
| 299 | |
wdenk | 57b2d80 | 2003-06-27 21:31:46 +0000 | [diff] [blame] | 300 | /* Set overscan */ |
wdenk | 452cfd6 | 2002-11-19 11:04:11 +0000 | [diff] [blame] | 301 | if (mode == 6) out_byte(0x3D9, 0x3F); |
| 302 | else out_byte(0x3D9, 0x30); |
| 303 | } |
| 304 | |
| 305 | static void bios_print_string(void) |
| 306 | { |
| 307 | extern void video_bios_print_string(char *string, int x, int y, int attr, int count); |
| 308 | char *s = (char *)(M.x86.R_ES<<4) + M.x86.R_BP; |
| 309 | int attr; |
| 310 | if (M.x86.R_AL & 0x02) attr = - 1; |
| 311 | else attr = M.x86.R_BL; |
| 312 | video_bios_print_string(s, M.x86.R_DH, M.x86.R_DL, attr, M.x86.R_CX); |
| 313 | } |
| 314 | |
| 315 | static void X86API int42(int intno) |
| 316 | { |
| 317 | switch (M.x86.R_AH) |
| 318 | { |
| 319 | case 0x00: |
| 320 | bios_set_mode(M.x86.R_AL); |
| 321 | break; |
| 322 | case 0x13: |
| 323 | bios_print_string(); |
| 324 | break; |
| 325 | default: |
| 326 | PRINTF("Warning: VIDEO BIOS interrupt %xh unimplemented function %xh, AL = %xh\n", |
| 327 | intno, M.x86.R_AH, M.x86.R_AL); |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | static void X86API int15(int intno) |
| 332 | { |
| 333 | PRINTF("Called interrupt 15h: AX = %xh, BX = %xh, CX = %xh, DX = %xh\n", |
| 334 | M.x86.R_AX, M.x86.R_BX, M.x86.R_CX, M.x86.R_DX); |
| 335 | } |