diff --git a/NOTES/PC-98 INT DCh/MS-DOS 5.00 boot144.md b/NOTES/PC-98 INT DCh/MS-DOS 5.00 boot144.md index a517db567..62644fc9e 100644 --- a/NOTES/PC-98 INT DCh/MS-DOS 5.00 boot144.md +++ b/NOTES/PC-98 INT DCh/MS-DOS 5.00 boot144.md @@ -1,1627 +1,1627 @@ -Entry point (MS-DOS 5.00) 1.44MB disk image (on my hard drive, boot144.dsk). Configuration menu option "nothing". 0ADC segment may change location based on configuration. - --- - - 0060:0020 WORD MS-DOS product number [see INT DCh CL=12h] - 0060:0022 WORD Internal revision number [see INT DCh CL=15h AH=0] - 0060:002E WORD INT DCh / ANSI segment (in this dump, 0ADCh) - 0060:0030 WORD ?? (in this dump, 3800h) - 0060:0032 WORD Text VRAM segment (A000h) - 0060:0037 BYTE ?? - 0060:0068 WORD RS-232C channel 0 AUX protocol [undocumented PC-98 webtech] [see INT DCh CL=0Ah, INT DCh CL=0Eh AH=01h] - 0060:008A BYTE kanji / graph mode flag - 0060:008B BYTE ?? - 0060:00A5 BYTE ?? - 0060:00A6 BYTE ?? - 0060:00B4 BYTE INT DCh in-progress flag [undocumented PC-98 webtech] - 0060:0110 BYTE Cursor Y coordinate - 0060:0111 BYTE Function row display (0=off 1=on 2=shift function keys) - 0060:0112 BYTE Scroll range lower limit - 0060:0113 BYTE Number of screen lines - 0060:0114 BYTE Erasure attribute (usually E1h) - 0060:0115 BYTE kanji upper byte storage flag - 0060:0116 BYTE kanji upper byte - 0060:0117 BYTE Line wrap flag - 0060:0118 BYTE Scroll speed (0=normal 1=slow) - 0060:0119 WORD Erasure character (usually 20h) - 0060:011B BYTE Cursor display state (0=off 1=on) - 0060:011C BYTE Cursor X coordinate - 0060:011D BYTE Character attribute (i.e. controlled by [m ) [INT DCh CL=10h AH=02h] - 0060:011E BYTE Scroll range upper limit - 0060:011F BYTE Scroll "weight" (delay, apparently?) (0001h = normal E000h = slow) - 0060:0124 WORD ACFh BX value (?)) - 0060:0128 BYTE ANSI escape handling state (0=normal 1=ESC 2>=ANSI processing) - 0060:0129 BYTE ANSI escape handling state (?) - 0060:012A BYTE ANSI escape handling state (?) 0 = ? 1 = ESC[> 2 = ESC[? 3 = ESC[= - 0060:012B BYTE Saved cursor attribute ( ESC [s ) - 0060:0134 WORD ANSI escape handling pointer of some kind (?) - 0060:0136 BYTE drive number last accessed by IO.SYS block driver [INT DCh CL=13h] - 0060:013C WORD display attribute in extended attribute mode [INT DCh CL=10h AL=02h] - 0060:013E WORD erasure attribute in extended attribute mode [INT DCh CL=10h AL=02h] - 0060:014E BYTE some sort of flag - 0060:0162 WORD ?? - 0060:0214 WORD:WORD 16-bit far pointer (0ADC:3126) - 0060:0268 WORD ?? - 0060:05DB WORD stored AX value from caller - 0060:05DD WORD stored SS value from caller - 0060:05DF WORD stored SP value from caller (after INT DCh int frame and PUSH DS) - 0060:05E1 WORD stored DS value from caller - 0060:05E3 WORD stored DX value from caller - 0060:05E5 WORD stored BX value from caller - 0060:0767 Stack pointer (from DOS segment), stack switches to on entry to procedure - 0060:17FA WORD ?? - 0060:1802 WORD ?? - 0060:197A BYTE ?? - 0060:1DC4 BYTE ?? - 0060:2852 BYTE x 0x28 ANSI escape parsing state (such as, numeric values from ESC[m ) - 0060:2A7A WORD ?? - 0060:2A7C WORD ?? - 0060:2C86 WORD x 0x1A ?? - 0060:36B3 INT DCh entry point - 0060:3B30 Subroutine called on INT DCh if 0060:014E is nonzero - --- - -INT DC = 60:36B3 - - 0060:36B3: - if BYTE PTR cs:[014E] == 0 jmp 0060:36BE - CALL 3B30h - 0060:36BE: - jmp far WORD PTR cs:[0214] (0ADC:3126) - --- - - 0060:3B30: (called by INT DCh entry point if BYTE PTR CS:[014E] != 0 - PUSH DS, ES, CX, SI, DI - DS:SI = WORD PTR cs:[3B28] (FFFF:0090) - ES:DI = WORD PTR cs:[3B2C] (0000:0080) - ZF = ((result=_fmemcmp(DS:SI,ES:DI,16)) == 0) (CX = 8, REP CMPSW) ; <- Testing A20 gate, apparently? - POP DI, SI, CX, ES, DS - IF result == 0 JMP 3B4Ch (if ZF=1) - return - 0060:3B4C: (if _fmemcmp(DS:SI,ES:DI,16) == 0) - PUSH AX, BX - AH = 5 - CALL FAR WORD PTR cs:[014F] ; <- ??? Pointer so far has been either 0000:0000 or FFFF:FFFF - POP BX, AX - return - ; ^ NOTE: Not sure, but this may be a call into HIMEM.SYS (XMS), in which case AH = 5 means Local Enable A20 - --- - - 0ADC:0030 WORD DOS kernel segment (60h) - --- - - 0ADC:0032: - PUSH DS - DS = WORD PTR CS:[0030] ; DOS kernel segment (60h) - IF (BYTE PTR DS:[197A] & 3) == 0 JMP 61h ; 0060:197A - 0ADC:003F: - ES = 0 - CX = WORD PTR ES:[05EA] ; 0000:05EA Disk 0 Parameter table segment?? - IF CX != WORD PTR DS:[002E] JMP 52h ; ?? - DI = 5EAh - WORD PTR ES:[DI] = AX, DI += 2 ; STOSW - 0ADC:0052: - CX = WORD PTR ES:[05EE] ; 0000:05EE Disk 1 Parameter table segment? - IF CX != WORD PTR DS:[002E] JMP 61h - DI = 5EEh - WORD PTR ES:[DI] = AX, DI += 2 ; STOSW - 0ADC:0061: - POP DS - ES = WORD PTR CS:[0030] ; DOS kernel segment (60h) - WORD PTR ES:[002E] = AX ; ?? - DI = 162h - CX = 36h - 0ADC:0071: - WORD PTR ES:[DI] = AX, DI += 4 ; STOSW ; INC DI ; INC DI - IF CX > 0 THEN CX--, JMP 71h ; LOOP 71h - 0ADC:0076: - return far ; RETF - --- - - 0ADC:0A00 Subroutine lookup table - Referred from 0ADC:0AAC, CL is single char or first byte of kanji - CL=10h AH=00h, search by CL register from caller, or normal CON output. - . . . . . . - 0ADC:00000A00 07 E7 10 08 19 11 09 F8 10 0A 49 11 0B 3A 11 0C ..........I..:.. - . . . . . - 0ADC:00000A10 8C 11 0D 5E 11 1A 7D 11 1B C1 10 1E 6B 11 00 B3 ...^..}.....k... - |END - 0ADC:00000A20 11 5B B2 0B 3D E4 0A 2A 84 0B 28 77 0B 44 90 0B .[..=..*..(w.D.. - - CL value | Subroutine address - CL = 0x07 0x10E7 ; CTRL+G / BEL - CL = 0x08 0x1119 ; CTRL+H / BACKSPACE / LEFT ARROW - CL = 0x09 0x10F8 ; CTRL+I / TAB - CL = 0x0A 0x1149 ; CTRL+J / DOWN ARROW - CL = 0x0B 0x113A ; CTRL+K / UP ARROW - CL = 0x0C 0x118C ; CTRL+L / RIGHT ARROW - CL = 0x0D 0x115E ; CTRL+M / CARRIAGE RETURN - CL = 0x1A 0x117D ; CTRL+Z / SUBSTITUE - CL = 0x1B 0x10C1 ; ESCAPE - CL = 0x1E 0x116B ; RECORD SEPARATOR - Any other (0) 0x11B3 - - The ESCAPE subroutine sets BYTE PTR [60:128] = 1, BYTE PTR [60:129] = 1, - and WORD PTR [60:134] = 0x2852 - --- - - 0ADC:0A21 Subroutine lookup table - Referred from 0ADC:0AC0, CL is byte from caller when (BYTE PTR DS:[0128] == 2) - In most cases this is called with CL the first byte after ANSI ESCAPE code. - - . . . . . - 0ADC:00000A20 11 5B B2 0B 3D E4 0A 2A 84 0B 28 77 0B 44 90 0B .[..=..*..(w.D.. - . . . . |END - 0ADC:00000A30 45 8D 0B 4D 99 0B 29 26 0B 00 93 0B 41 49 0C 42 E..M..)&....AI.B - - CL value | Subroutine address - CL = 0x5B 0x0BB2 ; ESC [ - CL = 0x3D 0x0AE4 ; ESC = - CL = 0x2A 0x0B84 ; ESC * - CL = 0x28 0x0B77 ; ESC ( - CL = 0x44 0x0B90 ; ESC D - CL = 0x45 0x0B8D ; ESC E - CL = 0x4D 0x0B99 ; ESC M - CL = 0x29 0x0B26 ; ESC ) - Any other (0) 0x0B93 ; ESC (anything else) - - The subroutine for (anything else) sets BYTE PTR [60:128] = 0, cancelling the - ESC handling and ignoring the byte. - --- - - 0ADC:0A3C Subroutine lookup table - Referred from ADC:0BDC, CL is the last byte of the ANSI ESC [ code. - This is called only on the final byte, not the intermediate digits and - semicolons of the code. - - . . . . . . - 0ADC:00000A3C 41 49 0C 42 6C 0C 43 93 0C 44 BA 0C 48 DD 0C 4A AI.Bl.C..D..H..J - . . . . . - 0ADC:00000A4C 25 0D 4B 69 0D 4C 3E 0E 4D 69 0E 6D BD 0D 66 DD %.Ki.L>.Mi.m..f. - . . . . . - 0ADC:00000A5C 0C 68 32 0F 6C 9A 0E 6E CB 0F 73 24 10 70 6F 10 .h2.l..n..s$.po. - . . . . . |END - 0ADC:00000A6C 75 48 10 3E 88 0E 3F 8E 0E 3D 94 0E 00 C0 10 90 uH.>..?..=...... - - CL value | Subroutine address - CL = 0x41 0x0C49 ; ESC [ A - CL = 0x42 0x0C6C ; ESC [ B - CL = 0x43 0x0C93 ; ESC [ C - CL = 0x44 0x0CBA ; ESC [ D - CL = 0x48 0x0CDD ; ESC [ H - CL = 0x4A 0x0D25 ; ESC [ J - CL = 0x4B 0x0D69 ; ESC [ K - CL = 0x4C 0x0E3E ; ESC [ L - CL = 0x4D 0x0E69 ; ESC [ M - CL = 0x6D 0x0DBD ; ESC [ m - CL = 0x66 0x0CDD ; ESC [ f - CL = 0x68 0x0F32 ; ESC [ h - CL = 0x6C 0x0E9A ; ESC [ l - CL = 0x6E 0x0FCB ; ESC [ n - CL = 0x73 0x1024 ; ESC [ s - CL = 0x70 0x106F ; ESC [ p - CL = 0x75 0x1048 ; ESC [ u - CL = 0x3E 0x0E88 ; ESC [ > - CL = 0x3F 0x0E8E ; ESC [ ? - CL = 0x3D 0x0E94 ; ESC [ = - Any other (0) 0x10C0 ; ESC [ (anything else) - - The anything else handler returns immediately (escape code is ignored) and - the calling code resets the flag and allows the console to proceed normally. - --- - - . . . . . . . . - 0ADC:00000A7C E2 12 F2 12 E2 12 CA 12 BC 12 F2 12 C3 12 D1 12 ................ - . . |END - 0ADC:00000A8C C3 12 D8 12 E8 01 00 CB B8 00 01 C3 E8 01 00 CB ................ - - 0ADC:0A7C table (routine 0ADC:1201 return from CALL 129Dh) - AX = 0x0000 DL = 0x00 0x12E2 - AX = 0x0000 DL = 0x01 0x12F2 - AX = 0x0001 DL = 0x00 0x12E2 - AX = 0x0001 DL = 0x01 0x12CA - AX = 0x0002 DL = 0x00 0x12BC - AX = 0x0002 DL = 0x01 0x12F2 - AX = 0x0003 DL = 0x00 0x12C3 - AX = 0x0003 DL = 0x01 0x12D1 - AX = 0x0004 DL = 0x00 0x12C3 - AX = 0x0004 DL = 0x01 0x12D8 - --- - - 0ADC:0A90: - CALL A94h - return far ; RETF - 0ADC:0A94: - MOV AX,0100h - return - --- - - 0ADC:0A98: - CALL A9Ch - return far ; RET - --- - - 0ADC:0A9C: (CL=10h AH=00h, at this time CL == caller's DL and DS = DOS segment 60h) - IF BYTE PTR DS:[0128] != 0 JMP AB5 (60:128) - IF CL < 0x20 JMP AACh - CALL 11B3h - 0ADC:0AAC: (CL < 0x20) - BX = 0A00h - CALL 0ACFh - CALL NEAR BX (BX comes from subroutine ACFh) - return - 0ADC:0AB5: (BYTE PTR DS:[0128] != 0) aka (60:128) - (BYTE DS:[0128])++ - IF BYTE PTR DS:[0128] != 2 JMP 0ACAh - 0ADC:0AC0: (BYTE PTR DS:[0128] == 2) - BX = 0A21h - CALL 0ACFh - WORD PTR DS:[0124] = BX (BX comes from subroutine ACFh) (60:124) - 0ADC:0ACA: (jmp here from BYTE PTR DS:[0128] != 2 compare under 0ADC:0AB5) - CALL NEAR WORD PTR DS:[0124] - return - --- - - ; Entry: BX = memory location of a lookup table, 3 bytes/entry in this format: - ; BYTE subroutine number - ; WORD subroutine address - ; CL = subroutine number to match - ; - ; Exit: BX = subroutine address - 0ADC:0ACF: - IF BYTE PTR CS:[BX] == 0 JMP ADFh - IF BYTE PTR CS:[BX] == CL JMP ADFh - BX += 3 - JMP ACFh - 0ADC:0ADF: - BX = WORD PTR CS:[BX+1] - return - --- - - ; Entry: CL = character code - ; - ; This is executed per character after ESC = - 0ADC:0AE4: - BX = WORD PTR DS:[0134] ; [0134] is often set to 2852h - WORD PTR DS:[BX],CL - WORD PTR DS:[0134] += 1 - IF BYTE PTR DS:[0128] < 0x04 JMP B18h - 0ADC:0AF5: - BX = 2853h ; ANSI escape parsing address + 1 - 0ADC:0AF8: - CX = WORD PTR DS:[BX] - 0ADC:0AFA: - AL = CL - AH = BYTE PTR DS:[0112] ; Scroll range lower limit - CALL B19h - BYTE PTR DS:[0110],AL ; Cursor Y position - AL = CH - AH = 0x4F - CALL B19h - BYTE PTR DS:[011C],AL ; Cursor X position - CALL 1535h ; update cursor position - BYTE PTR DS:[0128] = 0 - 0ADC:0B18: - return - --- - - 0ADC:0B19: - AL = ((AL >= 0x20) ? (AL - 0x20)) : 0 ; SUB AL, 20h ; JNC B1Fh ; MOV AL,00h - 0ADC:0B1F: - AL = ((AL > AH) ? AH : AL) ; CMP AL, AH ; JBE B25h ; MOV AL, AH - 0ADC:0B25: - return - --- - - ; Entry: CL = character code - ; - ; This is executed per character after ESC ) until the end of the ANSI code - 0ADC:0B26: - IF BYTE PTR DS:[0128] != 3 JMP B76h - IF CL == 0x33 JMP B43h - IF CL != 0x30 JMP B4Dh - 0ADC:0B37: - BYTE PTR DS:[008A] = 0x01 - BYTE PTR DS:[008B] = 0x20 - JMP B4Dh - 0ADC:0B43: - BYTE PTR DS:[008B] = 0x67 - BYTE PTR DS:[008A] = 0x00 - 0ADC:0B4D: - IF BYTE PTR DS:[0111] == 0 JMP B71h - 0ADC:0B54: - PUSH ES - ES = WORD PTR DS:[0032] ; video ram segment - DH = (BYTE PTR DS:[0112]) + 1 - DL = 0 - CALL 14F5h ; convert row DH col DL to video memory address - BX += 2 - AX = BYTE PTR DS:[008B] - WORD PTR DS:[BX] = AX - POP ES - 0ADC:0B71: - BYTE PTR DS:[0128] = 0 - 0ADC:0B76: - return - --- - - ; Entry: CL = character code - ; - ; This is executed per character after ESC ( until the end of the ANSI code - 0ADC:0B77: - IF BYTE PTR DS:[0128] < 3 JMP B83h - BYTE PTR DS:[0128] = 0 - 0ADC:0B83: - return - --- - - ; Entry: CL = character code - ; - ; This is executed per character after ESC * until the end of the ANSI code - 0ADC:0B84: - CALL 117Dh ; CTRL+Z handling - BYTE PTR DS:[0128] = 0 - 0ADC:0B8C: - return - --- - - ; Entry: CL = character code - ; - ; This is executed per character after ESC E until the end of the ANSI code - 0ADC:0B8D: - CALL 115Eh ; carriage return - 0ADC:0B90: - CALL 1149h ; line feed / down arrow - 0ADC:0B93: ; <- table jumps here for unknown escapes - BYTE PTR DS:[0128] = 0 - 0ADC:0B98: - return - --- - - ; Entry: CL = character code - ; - ; This is executed per character after ESC M - 0ADC:0B99: - IF BYTE PTR DS:[0110] == 0 JMP BA9h ; Move cursor up one row - BYTE PTR DS:[0110] -= 1 - CALL 1535h ; update cursor position - JMP BACh - 0ADC:0BA9: - CALL 13B0h ; scroll region down one line - 0ADC:0BAC: - BYTE PTR DS:[0128] = 0 - 0ADC:0BB1: - return - --- - - ; Entry: CL = character code - ; - ; This is executed per character after ESC [ until the end of the ANSI code - 0ADC:0BB2: - IF BYTE PTR DS:[0128] == 0x02 JMP BF3h - IF CL == 0x3B JMP C05h - IF CL == 0x27 JMP C1Bh - IF CL == 0x22 JMP C1Bh - IF BYTE PTR DS:[012A] == 0xFF JMP C2Ch - IF CL >= 0x3A JMP BDCh - CL = CL AND 0x0F - IF CL <= 0x09 JMP BF4h - 0ADC:0BDC: - BX = 0A3Ch - CALL ACFh - SI = 2852h - CALL NEAR BX - 0ADC:0BE7: - IF BYTE PTR DS:[012A] != 0 JMP BF3h - BYTE PTR DS:[0128] = 0 - 0ADC:0BF3: - return - --- - - ; ESC [ A - ; SI = ANSI escape area (2852h) - 0ADC:0C49: - IF BYTE PTR DS:[0129] != 1 JMP C6Bh - 0ADC:0C50: - CX = WORD PTR DS:[SI] ; numeric value before "A" character, parameter - 0ADC:0C52: - AX = BYTE PTR DS:[0110] ; Cursor Y position - IF CX == 0 THEN CX = 1 ; AND CX, CX ; JNE C5Eh ; MOV CX, 1 - 0ADC:0C5E: - IF CX > AX THEN CX = AX ; CMP AX, CX ; JNC C64h ; MOV CX, AX - 0ADC:0C64: - BYTE PTR DS:[0110] -= CL ; Cursor Y position -= parameter - CALL 1535h ; update cursor position on screen - 0ADC:0C6B: - return - --- - - ; ESC [ B - ; SI = ANSI escape area (2852h) - 0ADC:0C6C: - IF BYTE PTR DS:[0129] != 1 JMP C92h - CX = WORD PTR DS:[SI] - 0ADC:0C75: - AL = (BYTE PTR DS:[0112]) - (BYTE PTR DS:[0110]), AH = 0 - IF CX == 0 THEN CX = 1 ; AND CX, CX ; JNE C85h ; MOV CX, 1 - IF CX > AX THEN CX = AX ; CMP AX, CX ; JNC C8Bh ; MOV CX, AX - BYTE PTR DS:[0110] += CL ; Cursor Y position += CL - CALL 1535h ; update cursor position on screen - 0ADC:0C92: - return - --- - - ; ESC [ C - ; SI = ANSI escape area (2852h) - 0ADC:0C93: - IF BYTE PTR DS:[0129] != 1 JMP CB9h - CX = WORD PTR DS:[SI] - 0ADC:0C9C: - AX = 0x4F - if (AL -= BYTE PTR DS:[011C]) < 0 JMP CB6h ; SUB AL,[011C] ; JC CB6h - IF CX == 0 THEN CX = 1 ; AND CX, CX ; JNZ CACh - 0ADC:0CAC: - IF CX > AX THEN CX = AX ; CMP AX, CX ; JNC CB2h ; MOV CX, AX - BYTE PTR DS:[011C] += CL ; Cursor X position += CL - 0ADC:0CB6: - CALL 1535h ; update cursor position on screen - 0ADC:0CB9: - return - --- - - ; ESC [ D - ; SI = ANSI escape area (2852h) - 0ADC:0CBA: - IF BYTE PTR DS:[0129] != 1 JMP CDCh - CX = WORD PTR DS:[SI] - 0ADC:0CC3: - AX = BYTE PTR DS:[011C] ; Cursor X position - IF CX == 0 THEN CX = 1 ; AND CX, CX ; JNZ CCFh - IF CX > AX THEN CX = AX ; CMP AX, CX ; JNC CD5h ; MOV CX, AX - BYTE PTR DS:[011C] -= CL ; Cursor X position -= CL - 0ADC:0CD9: - CALL 1535h ; update cursor position on screen - 0ADC:0CDC: - return - --- - - ; ESC [ L - ; SI = ANSI escape area (2852h) - 0ADC:0E3E: - IF BYTE PTR DS:[012A] != 0 JMP E63h - IF BYTE PTR DS:[0129] != 1 JMP E63h - 0ADC:0E4C: - CX = WORD PTR DS:[SI] - 0ADC:0E4E: - IF CX == 0 THEN CX = 1 ; AND CX, CX ; JNZ E55h - 0ADC:0E55: - PUSH CX - AH = BYTE PTR DS:[0110] ; AH = Cursor Y position - CALL 13B4h - POP CX - IF CX > 0 THEN CX--, JMP E55h ; LOOP E55h - 0ADC:0E60: - CALL 115Eh ; carriage return - 0ADC:0E63: - BYTE PTR DS:[012A] = 0 - 0ADC:0E68: - return - --- - - ; ESC [ M - ; SI = ANSI escape area (2852h) - 0ADC:0E69: - IF BYTE PTR DS:[0129] != 1 JMP E87h - CX = WORD PTR DS:[SI] - 0ADC:0E72: - IF CX == 0 THEN CX = 1 ; AND CX, CX ; JNZ E55h - 0ADC:0E79: - PUSH CX - AH = BYTE PTR DS:[0110] ; AH = Cursor Y position - CALL 135Fh - POP CX - IF CX > 0 THEN CX--, JMP E79h ; LOOP E79h - 0ADC:0E84: - CALL 115Eh ; carriage return - 0ADC:0E87: - return - --- - - ; Entry: CL = character code - ; - ; This is executed on ESC [ > - 0ADC:0E88: - BYTE PTR DS:[012A] = 1 - return - --- - - ; Entry: CL = character code - ; - ; This is executed on ESC [ ? - 0ADC:0E8E: - BYTE PTR DS:[012A] = 2 - return - --- - - ; Entry: CL = character code - ; - ; This is executed on ESC [ = - 0ADC:0E94: - BYTE PTR DS:[012A] = 3 - return - --- - - ; Entry: CL = character code - ; - ; This is executed when the ESC is encountered outside of ANSI processing - - 0ADC:10C1: - PUSH ES - WORD PTR DS:[0134] = 0x2852 - BYTE PTR DS:[0128] = 0x01 - BYTE PTR DS:[0129] = 0x01 - DI = 0x2852 - AX = 0 - CX = 0x14 - DX = ES = DS - _fmemset(ES:DI,0,0x28) ; REP STOSW - POP ES - 0ADC:10E2: - return - --- - - ; ?? - 0ADC:10E3: - CALL 10E7h - 0ADC:10E6: - return far ; RETF - --- - - ; ASCII BEL 07h handling - 0ADC:10E7: - AH = 17h ; INT 18h AH=17h turn on bell - INT 18h - CX = 0x6E8C ; <- delay loop - 0ADC:10EE: - PUSH AX - NOP AX - POP AX - IF CX > 0 THEN CX--, JMP 10EEh ; LOOP 10EEh - 0ADC:10F3: - AH = 18h ; INT 18h AH=18h turn off bell - INT 18h - 0ADC:10F7: - return - --- - - ; ASCII TAB 09h handling - 0ADC:10F8: - AL = ((BYTE PTR DS:[011C] + 8) >> 3) * 8 ; move cursor X position to next multiple of 8 - BYTE PTR DS:[011C] = AL - IF AL < 0x4F JMP 1115h ; interesting bug here, AL would be 0x50 on overflow - 0ADC:110C: - BYTE PTR DS:[011C] = 0x4F ; keep cursor on-screen, set to last column - CALL 118Ch ; call function to advance one column to wrap around to next line - return - 0ADC:1115: - call 1535h ; update cursor position - 0ADC:1118: - return - --- - - ; ASCII BACKSPACE 08h / LEFT ARROW KEY - 0ADC:1119: - IF BYTE PTR DS:[011C] == 0 JMP 1126h - 0ADC:1120: - BYTE PTR DS:[011C] -= 1 - JMP 1136h - 0ADC:1126: - IF BYTE PTR DS:[0110] == 0 JMP 1139h - 0ADC:112D: - BYTE PTR DS:[0110] -= 1 - BYTE PTR DS:[011C] = 0x4F - 0ADC:1136: - CALL 1535h ; update cursor position on screen - 0ADC:1139: - return - --- - - ; ASCII UP ARROW 0Bh - 0ADC:113A: - IF BYTE PTR DS:[0110] == 0 JMP 1148h - BYTE PTR DS:[0110] -= 1 - CALL 1535h ; update cursor position on screen - 0ADC:1148: - return - --- - - ; ASCII DOWN ARROW 0Ah - 0ADC:1149: - AL = BYTE PTR DS:[0110] - IF AL == BYTE PTR DS:[0112] JMP 115Ah - BYTE PTR DS:[0110] += 1 - CALL 1535h ; update cursor position on screen - return - 0ADC:115A: - CALL 1348h ; scroll up screen region - return - --- - - ; ASCII CARRIAGE RETURN 0Dh - 0ADC:115E: - BYTE PTR DS:[011C] = 0 ; set X coordinate = 0 - CALL 1535h ; update cursor position - 0ADC:1166: - return - --- - - ; ?? - 0ADC:1167: - CALL 116Bh - 0ADC:116A: - return far ; RETF - --- - - ; ASCII RECORD SEPARATOR 1Eh - 0ADC:116B: - BYTE PTR DS:[0110] = 0 ; set Y pos = 0 - BYTE PTR DS:[011C] = 0 ; set X pos = 0 - CALL 1535h ; update cursor position - 0ADC:1178: - return - --- - - 0ADC:1179: - CALL 117Dh - 0ADC:117C: - return far ; RETF - --- - - 0ADC:117D: (CTRL+Z handling) - DI = 0 - CX = 0x9B0 ; 0x9B0 = 80*31? - CALL 1516h ; clear the screen (fill with erasure) - CALL 13FFh ; remove function row - CALL 116Bh ; call 0x1E RECORD SEPARATOR function - 0ADC:118B: - return - --- - - 0ADC:118C: (advance cursor one column. if past right edge of screen fall through to 1197h to advance one row) - BYTE PTR DS:[011C] += 1 (cursor X coordinate += 1) - IF BYTE PTR DS:[011C] <= 4Fh JMP 11AFh (if cursor X coordinate <= 4Fh then goto 11AFh) - 0ADC:1197: (advance cursor one row. if past lower scroll limit then fall through to 11A9h scroll up screen region) - BYTE PTR DS:[011C] = 0 (set cursor X coordinate = 0) - AL = BYTE PTR DS:[0110] (AL = (cursor Y coordinate)++) - BYTE PTR DS:[0110] += 1 - IF BYTE PTR DS:[0112] != AL JMP 11AFh (if cursor Y != scroll range lower limit then goto 11AFh) - 0ADC:11A9: (put cursor back on the bottom row and scroll screen region up) - BYTE PTR DS:[0110] = AL (set cursor Y coordinate to scroll range lower limit) - CALL 1348h - 0ADC:11AF: - CALL 1535h (update cursor position on screen) - 0ADC:11B2: - return - --- - - 0ADC:11B3: (CL=10h AH=00h, at this time CL == caller's DL and DS = DOS segment 60h) - IF BYTE PTR DS:[011C] < 0x50 JMP 11C7h ; (60:11C cursor X position) - IF BYTE PTR DS:[0117] == 0 JMP 11C2h ; (60:117 line wrap flag) - 0ADC:11C1: - return - 0ADC:11C2: - PUSH CX - CALL 118Ch - POP CX - 0ADC:11C7: (60:11C < 0x50) - IF BYTE PTR DS:[008A] == 0 JMP 1201h ; (60:8A kanji / graph mode flag) - IF BYTE PTR DS:[0115] != 0 JMP 11F3h ; (60:115 kanji upper byte storage flag) - IF CL < 0x81 JMP 1201h - IF CL < 0xA0 JMP 11E9h - IF CL < 0xE0 JMP 1201h - IF CL >= 0xFD JMP 1201h - BYTE PTR DS:[0116] = CL (60:116 kanji upper byte) - BYTE PTR DS:[0115] = 1 (60:115 kanji upper byte storage flag) - 0ADC:11E9: - return - 0ADC:11F3: (60:115 kanji upper byte storage flag set) - CH = DS:[0116] (60:116 kanji upper byte) - CALL 1236h - tCH = CH, tCL = CL, CL = tCH - 0x20, CH = tCL ; XCHG CL, CH ; SUB CL, 0x20 - JMP 1203h - 0ADC:1201: - CH = 0 - 0ADC:1203: - CALL 1260h - CALL 129Dh - DI = BX - AX = ((AX * 2) + DL) * 2 - BX = (AX + 0A7Ch) - BX = WORD PTR CS:[BX] - PUSH ES - ES = WORD PTR DS:[0032] ; (60:32 appears to be Text VRAM segment A000h) - AX = CX - CALL NEAR BX - POP ES - BYTE PTR DS:[0115] = 0 (60:115 upper byte storage flag) - BYTE PTR DS:[011C] += AL (60:11C cursor X coordinate) - CALL 1535h - 0ADC:1231: - return - --- - - 0ADC:1232: - CALL 1236h - 0ADC:1235: - return far ; RETF - --- - - 0ADC:1236: (CH = upper Kanji byte) - IF CH == 0x80 JMP 125Fh - IF CH >= 0xA0 JMP 1245h - CH -= 0x70 - JMP 1248h - 0ADC:1245: - CH -= 0xB0 - 0ADC:1248: - IF (CL & 0x80) != 0x00 THEN CL-- ; OR CL, CL ; JNS 124E ; DEC CL - 0ADC:124E: - CH = CH * 2 - IF CL < 0x9E JMP 125Ah - CL -= 0x5E - JMP 125Ch - 0ADC:125A: - CH-- - 0ADC:125C: - CL -= 0x1F - 0ADC:125F: - return - --- - - 0ADC:1260: - PUSH ES, CX - ES = WORD PTR DS:[0032] ; video ram segment (A000h) - DH = BYTE PTR DS:[0110] ; Cursor Y position - DL = BYTE PTR DS:[011C] ; Cursor X position - CALL 14F5h ; convert to video memory address (return in BX) - CX = WORD PTR ES:[BX] ; read from video memory - CALL 129Dh ; Determine cell width (single or double, indicated in DL) - AX = 2 - IF DL == 1 JMP 129Ah - DH = DL - CX = WORD PTR ES:[BX+2] - CALL 129Dh - AX = 0 - IF DH != 2 JMP 1294h - AX = 3 - 0ADC:1294: - IF DL == 0 JMP 129Ah - AX++ - 0ADC:129A: - POP CX, ES - 0ADC:129C: - return - --- - - 0ADC:129D: - DL = 0 - IF CH == 0x00 JMP 12BBh - IF CH == 0xFF JMP 12BBh - IF CL < 0x09 JMP 12B3h - IF CL < 0x0C JMP 12BBh - 0ADC:12B3: - DL = 1 - IF (CL & 0x80) != 0 JMP 12BBh - DL = 2 - 0ADC:12BB: - return - --- - - 0ADC:12BC: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) - CALL 1330h - CALL 12E2h ; write single-wide - 0ADC:12C2: - return - --- - - 0ADC:12C3: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) - CALL 1324h - CALL 12E2h ; write single-wide - 0ADC:12C9: - return - --- - - 0ADC:12CA: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) - CALL 133Ch - CALL 12F2h ; write double-wide - 0ADC:12D0: - return - --- - - 0ADC:12D1: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) - CALL 1324h - CALL 12F2h ; write double-wide - 0ADC:12D7: - return - --- - - 0ADC:12D8: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) - CALL 1324h - CALL 133Ch - CALL 12F2h ; write double-wide - 0ADC:12E1: - return - --- - - ; This appears to write a single-wide character - 0ADC:12E2: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) - WORD PTR ES:[DI] = AX ; write character code - DI += 0x2000 - WORD PTR ES:[DI] = WORD PTR DS:[013C] (60:13C display attribute in extended attribute mode) ; write attribute code - AL = 1 (this indicates to caller to move cursor X position 1 unit to the right) - 0ADC:12F1: - return - --- - - ; This appears to write a double-wide (two cell wide) character - 0ADC:12F2: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) - IF BYTE PTR DS:[011C] < 0x4F JMP 1311h ; if Cursor X coordinate < 0x4F - ; NTS: Cursor wraparound case (no room on last column). Write an empty cell then wrap to the next line and print char. - PUSH AX - CX = 0x20, WORD PTR ES:[DI] = CX - CALL 118Ch ; advance cursor one column - DH = BYTE PTR DS:[0110] ; DH = cursor Y - DL = BYTE PTR DS:[011C] ; DL = cursor X - CALL 14F5h ; Convert DH, DL to VRAM address in BX - DI = BX - POP AX - ; NTS: Notice the double-wide (usually Kanji) writing routine writes the code from AX, then - ; writes AX | 0x80. This has no effect on the display on real hardware, but follows - ; NEC PC-98 recommendations. - 0ADC:1311: - WORD PTR ES:[DI] = AX, DI += 2 ; STOSW - WORD PTR ES:[DI] = AX | 0x80, DI += 2 ; OR AL,80h ; STOSW - DI -= 4, DI += 0x2000 - AX = WORD PTR DS:[013C] ; AX = extended color attribute - WORD PTR ES:[DI] = AX, DI += 2 ; STOSW - WORD PTR ES:[DI] = AX, DI += 2 ; STOSW - AL = 2 - 0ADC:1323: - return - --- - - 0ADC:1324: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) - PUSH DI - DI -= 2 - CX = 0x20 - WORD PTR ES:[DI] = CX - POP DI - 0ADC:132F: - return - --- - - 0ADC:1330: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) - PUSH DI - DI += 2 - CX = 0x20 - WORD PTR ES:[DI] = CX - POP DI - 0ADC:133B: - return - --- - - 0ADC:133C: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) - PUSH DI - DI += 4 - CX = 0x20 - WORD PTR ES:[DI] = CX - POP DI - 0ADC:1347: - return - --- - - 0ADC:1348: (scroll up screen scroll region) - AH = BYTE PTR DS:[011E] ; scroll region upper limit - WORD PTR DS:[011F] = 0x0001 ; scroll "weight" aka delay - IF BYTE PTR DS:[0118] != 0 THEN WORD PTR DS:[011F] = 0xE000 ; if slow scroll mode set "weight" aka delay to larger value - 0ADC:135F: - CL = BYTE PTR DS:[0112] ; scroll region lower limit - CX = CL - AH ; lower limit - upper limit and zero extend to CX - if CX == 0 JMP 13A1h - 0ADC:1369: - PUSH ES, DS - DX = AH << 8 ; DH = AH, DL = 0 - CALL 14F5h ; take cursor position, convert to video RAM byte address, return in BX - ES = DS = WORD PTR DS:[0032] ; retrieve video (text) RAM segment - DX = BX + 0xA0 ; note 0xA0 = 160 = 80 * 2 - 0ADC:1381: - PUSH CX - ; This part uses REP MOVSW first on character data at A000:0000+x - ; then on attribute data at A000:2000+x - ; Move and swap is used to let REP MOVSW advance SI and DI, then - ; re-use the same starting SI and DI for the attribute data, before - ; then using the post REP MOVSW SI and DI values for the next line. - SI = DX - DI = BX - _fmemcpy(ES:DI,DS:SI,0xA0) ; CX = 0x50 REP MOVSW - SWAP(DX,SI), SI += 0x2000 - SWAP(BX,DI), DI += 0x2000 - _fmemcpy(ES:DI,DS:SI,0xA0) ; CX = 0x50 REP MOVSW - POP CX - IF CX > 0 THEN CX--, JMP 1381h ; LOOP 1381h - 0ADC:139F: - POP DS, ES - 0ADC:13A1: - DH = BYTE PTR DS:[0112] - call 1508h - CX = WORD PTR DS:[011F] - 0ADC:13AC: ; <- delay loop, to "weight" down the console scroll - NOP - IF CX > 0 THEN CX--, JMP 13ACh ; LOOP 13ACh - 0ADC:13AF: - return - --- - - 0ADC:13B0: (take scroll region, scroll down one line) - AH = BYTE PTR DS:[011E] ; scroll range upper limit - CL = BYTE PTR DS:[0112], CH = 0 ; scroll range lower limit - CL -= AH - IF CL == 0 THEN JMP 13F8h - 0ADC:13BE: - STD ; set direction flag, so that REP MOVSW works backwards - PUSH ES, DS - DH = BYTE PTR DS:[0112] - DL = 0x4F - CALL 14F5h ; convert scroll lower limit row, column 0x4F (79) to video ram address - ES = DS = WORD PTR DS:[0032] ; video RAM segment - DX = BX - 0xA0 ; DX = video RAM address one row up, BX = video RAM address - 0ADC:13D8: - PUSH CX - SI = DX, DI = BX - _fmemcpy_backwards(ES:DI, DS:SI, 0xA0) ; CX = 0x50, REP MOVSW, with DF=1 - XCHG DX, SI ; XCHG BX, DI ; SI,DI modified by REP MOVSW, so swap with copy to re-use original addr for next step - SI += 0x2000 ; DI += 0x2000 ; point at attribute RAM - _fmemcpy_backwards(ES:DI, DS:SI, 0xA0) ; CX = 0x50, REP MOVSW, with DF=1 - POP CX - IF CX > 0 THEN CX--, JMP 13D8h ; LOOP 13D8h - 0ADC:13F6: - POP DS, ES ; CLD - DH = AH ; DH = scroll range upper limit - CALL 1508h - 0ADC:13FE: - return - --- - - 0ADC:13FF: - IF BYTE PTR DS:[0111] != 0 JMP 1407h - 0ADC:1406: - return - 0ADC:1407: - (TODO) - --- - - 0ADC:14F5: (DH = Y coordinate DL = X coordinate return in BX = video RAM byte address) - BX = (WORD PTR DS:[(DH * 2) + 0x1814]) - DX = (DL * 2) - BX += DX - return ; 0ADC:1507 - --- - - 0ADC:1508: (DH = Scroll range lower limit) - DL = 0 - CALL 14F5h (get video RAM address of row DH from caller and column zero) - DI = BX - CX = 0x50 - CALL 1516h - return - 0ADC:1516: (CX = number of WORDs to write, DI = video ram address to fill) - PUSH ES - ES = WORD PTR DS:[0032] ; get video RAM (text) segment A000h - DX = CX ; save CX (for later) - BX = DI ; save DI (for later) - AX = WORD PTR DS:[0119] ; erasure character - _fmemset(ES:DI,cx*2) ; REP STOSW (CX from caller) - AX = WORD PTR DS:[013E] ; erasure attribute - CX = DX ; restore CX from caller - DI = BX + 0x2000 ; restore DI from caller and add 2000h - _fmemset(ES:DI,cx*2) ; REP STOSW (CX from caller) - POP ES - return - --- - - 0ADC:1534: - return - 0ADC:1535: (update cursor position on screen) - IF BYTE PTR DS:[011B] == 0 JMP 1534h (return immediately if cursor is turned off) - 0ADC:153C: - DH = BYTE PTR DS:[0110] - DL = BYTE PTR DS:[011C] - IF DL >= 0x50 THEN DL-- - 0ADC:154B: - CALL 14F5h (take cursor position, convert to video RAM byte address, return in BX) - AH = 0x13 (INT 18h AX=13h set cursor position) - DX = BX (byte position of cursor) - JMP 198Ah (calls INT 18h AX=13h to set cursor pos. function 198Ah then returns) - --- - - 0ADC:1814 Row value to video RAM address lookup table used by subroutine at 0ADC:14F5 - - VRAM_ADDRESS = WORD PTR [0ADC:1814 + (row * 2)] - - . . . . . . . . <- Cursor rows 0-7 - 0060:00001814 00 00 A0 00 40 01 E0 01 80 02 20 03 C0 03 60 04 ....@..... ...`. - . . . . . . . . <- Cursor rows 8-15 - 0060:00001824 00 05 A0 05 40 06 E0 06 80 07 20 08 C0 08 60 09 ....@..... ...`. - . . . . . . . . <- Cursor rows 16-23 - 0060:00001834 00 0A A0 0A 40 0B E0 0B 80 0C 20 0D C0 0D 60 0E ....@..... ...`. - . . . . . . . |END <- Cursor rows 24-30 - 0060:00001844 00 0F A0 0F 40 10 E0 10 80 11 20 12 C0 12 00 00 ....@..... ..... - --- - - 0ADC:198A (this code calls INT 18h in an elaborate way in order to make sure interrupts are disabled at entry point for some reason) - PUSH DS - DS = 0 - PUSHF, CLI, CALL FAR WORD PTR DS:[0060] (DS=0, CALL FAR 0000:0060, which is INT 18h but with interrupts disabled at entry) - POP DS - return - --- - - 0ADC:3126: - PUSH DS - DS = WORD PTR CS:[0030] = DOS segment 60h - WORD PTR DS:[05E1] = caller DS - Store caller AX, SS, SP, DX, BX into DS: [5DB], [5DD], [5DF], [5E3], [5E5] - SS:SP = WORD PTR CS:[0030] (DOS segment 60h : offset 767h) - CLD, STI - PUSH ES, BX, CX, DX, SI, DI - BYTE PTR DS:[00B4] = 1 - 0ADC:3154: - CL -= 9, JMP to 0ADC:3180 if carry (if caller CL < 9) - 0ADC:315E: - IF CL < 0x0D, JMP TO 0ADC:3173 (if caller CL < (0x16 = 0x0D+9)) - 0ADC:3163: - IF CL < 0x77, JMP TO 0ADC:3180 (if caller CL < (0x80 = 0x77+9)) - 0ADC:3168: - IF CL > 0x79, JMP TO 0ADC:3180 (if caller CL > (0x82 = 0x79+9)) - 0ADC:316D: (JMPed here if CL >= 0x80 && CL <= 0x82) - CL = (CL - 0x77) + 0x0D - 0ADC:3173: (JMPed here if CL >= 0x09 && CL <= 0x15) - SI = (CL * 2) + 0x3A5C - 0ADC:317D: - CALL NEAR WORD PTR 0ADC:[SI] - 0ADC:3180: - CALL NEAR 0ADC:4080 - 0ADC:3183: - POP DI, SI, DX, CX, BX, ES - DS = WORD PTR CS:[0030] = DOS segment 60h - Restore caller AX, SS, SP, DX, BX from [5DB], [5DD], [5DF], [5E3], [5E5] - POP DS (restore caller DS) - IRET - --- - - 0ADC:32DF: (CL=0Dh entry point) - ES = WORD PTR CS:[0030] ; MS-DOS segment (60h) - AX = WORD PTR DS:[05DB] ; AX from caller - DS = WORD PTR DS:[05E1] ; DS from caller - SI = DX ; DX from caller - IF AX > 0xFF JMP 32F7h ; CMP AX,00FF ; JA 32F7h - IF AX != 0xFF JMP 3310h ; ... JNE 3310h - JMP 3350h - 0ADC:32F7: (AX > 0xFF) - IF AX < 0x101 JMP 3304h ; CMP AX,0101 ; JC 3304h - IF AX == 0x101 JMP 3308h ; ... JE 3308h - IF AX == 0x102 JMP 330Ch ; CMP AX,0102 ; JE 330Ch - 0ADC:3303: - return - 0ADC:3304: (AX < 0x101) - CALL 3445h - return - 0ADC:3308: (AX == 0x101) - CALL 349Ch - return - 0ADC:330C: (AX == 0x102) - CALL 3538h - return - 0ADC:3310: (AX < 0xFF) - IF AX != 0 THEN AX--, JMP 3390h ; SUB AX, 1 ; JNC 3390h - 0ADC:3315: (AX == 0, DS = caller DS segment, SI = caller DX register, ES = MS-DOS segment 60h) - DI = 2D2Eh ; This appears to be the memory location that holds the function key row strings and attributes - DL = 0Ah ; There are 10 entries, 16 bytes apart - 0ADC:331A: - CALL 3547h ; - _fmemcpy(ES:DI, DS:SI, 0x0F) ; CX = 0x0F ; REP MOVSB - ; Side effect: SI += 0x0F, DI += 0x0F - SI++ - IF DL > 0 THEN DL--, JMP 331Ah ; DEC DL ; JNZ 331Ah - 0ADC:3327: - DI += 0x50 ; Reminder: 2D2Eh + (0x10 * 0x0A) + 0x50 = 2E1Eh - ; This (2E1Eh) appears to hold the text that is injected when the function key is pressed. - ; For example by default this table defines F1 -> "DIR A:\x0D" - DL = 0Ah - 0ADC:332C: - CALL 3547h - _fmemcpy(ES:DI, DS:SI, 0x0F) ; CX = 0x0F ; REP MOVSB - ; Side effect: SI += 0x0F, DI += 0x0F - SI++ - IF DL > 0 THEN DL--, JMP 332Ch ; DEC DL ; JNZ 332Ch - 0ADC:3339: - DI += 0x50 ; Reminder: 2E1Eh + (0x10 * 0x0A) + 0x50 = 2F0Eh - DL = 0x0B - 0ADC:333E: - CALL 3547h - _fmemcpy(ES:DI, DS:SI, 0x05) ; CX = 0x05 ; REP MOVSB - ; Side effect: SI += 0x05, DI += 0x05 - SI++ - DI += 2 - IF DL > 0 THEN DL--, JMP 333Eh ; DEC DL ; JNZ 333Eh - 0ADC:334E: - JMP 3386h - 0ADC:3350: (AX == 0xFF) - DI = 2D2Eh ; Memory location that appears to hold function key row strings - DL = 0x1E - 0ADC:3355: - CALL 3547h - _fmemcpy(ES:DI, DS:SI, 0x0F) ; CX = 0x0F ; REP MOVSB - ; Side effect: SI += 0x0F, DI += 0x0F - SI++ - IF DL > 0 THEN DL--, JMP 3355h - 0ADC:3362: - DL = 0x0B - 0ADC:3364: - CALL 3547h - _fmemcpy(ES:DI, DS:SI, 0x05) ; CX = 0x05 ; REP MOVSB - ; Side effect: SI += 0x05, DI += 0x05 - DI += 2 - IF DL > 0 THEN DL--, JMP 3364h - 0ADC:3374: - DI = 0x2F86 - DL = 0x0F - 0ADC:3379: - CALL 3547h - _fmemcpy(ES:DI, DS:SI, 0x0F) ; CX = 0x0F ; REP MOVSB - SI++ - IF DL > 0 THEN DL--, JMP 3379h - 0ADC:3386: - IF BYTE PTR ES:[0111] == 0 THEN JMP 33ECh ; ES = DOS segment 60h - JMP 33ECh ; Wait... what? - 0ADC:3390: - (TODO) - - 0ADC:33EC: - IF AX < 0x29 JMP 33E3h ; CMP AX, 29h ; JC 33E3h - IF AX > 0x39 JMP 33E3h ; CMP AX, 39h ; JA 33E3h - IF AX == 0x39 JMP 3417h ; ... JE 3417h - 0ADC:33F8: - IF AX <= 0x32 JMP 3405h ; CMP AX, 32h ; JBE 3405h - IF AX != 0x38 JMP 33E3h ; CMP AX, 38h ; JNE 33E3h - 0ADC:3402: - AX = 0x28 - 0ADC:3405: - DI = ((AX - 0x29) * 16) + 0x2F86 - CX = 0x000F - JMP 33DEh - 0ADC:3417: - AX = 0x38 - PUSH DS, SI, DI - CALL 33F8h - POP DI, SI, DS - SI += 0x10 - DI += 0x10 - CX = 0x0A - 0ADC:342C: - PUSH CX, DS, SI, DI - AX = 0x33 - CX - CALL 33F8h - POP DI, SI - DI += 0x10 - SI += 0x10 - POP DS, CX - IF CX > 0 THEN CX--, JMP 342Ch ; LOOP 342Ch - 0ADC:3444: - return - 0ADC:3445: - DI = 307Ah - SI += 2 - AX = BX = 0 - _fmemcpy(ES:DI, DS:SI, 0x200) ; CX = 0x200 ; REP MOVSB - 0ADC:3454: - DI = 307Ah - CX = DI - CX += 0x200 - IF DI >= CX JMP 346Fh ; CMP DI, CX ; JNC 346Fh - IF BYTE PTR ES:[DI] == 0 JMP 346Fh - AL = BYTE PTR ES:[DI] ; AH = 0 at this point - DI += AX - JMP 345Dh - 0ADC:346F: - WORD PTR ES:[3076] = DI - 0x307A ; SUB DI, 307Ah ; MOV ES:[3076] DI - DI = 3078h - (TODO) - --- - - 0ADC:355B: (CL=0Ah entry point) - IF WORD PTR DS:[1802] != 0 JMP 3563h - return - 0ADC:3563: (CL=0Ah and WORD PTR DS:[1802] == 0) - IF (BYTE PTR DS:[0037] & 8) != 0 JMP 357Eh - 0ADC:356A: - AH = 00h - INT 19h - CX = 0 - IF AH == 0 JMP 3579h - CX = 0xFFFF - 0ADC:3579: - WORD PTR DS:[05DB],CX (AX value to return to caller) - return - 0ADC:357E: - IF (AH & 0xF0) != 0 JMP 3597h - BYTE PTR DS:[0068],DH - PUSH DX - DL = (DL AND 0x0F) - BYTE PTR DS:[0069] = (BYTE PTR DS:[0069] & 0xF0) | DL - POP DX - JMP 35A4h - 0ADC:3597: - IF AH > 0x20 JMP 35A3h - IF WORD PTR DS:[17FA] != 0 JMP 35A4h - 0ADC:35A3: - return - 0ADC:35A4: - AH = (DL & 0x30) - BX = AH >> 3 - ES = WORD PTR DS:[1802] - DI = WORD PTR ES:[BX+0x40] - AH = AH | 7 - BX = 0 - IF (DH & 1) == 0 JMP 35C9h - BH = BH | 0x30 - 0ADC:35C9: - AL = (DL & 0x0F) + 1 - DH = ((DH & 0xFE) | 0x02) - 0ADC:35D5: - IF (AH & 0x30) != 0 JMP 35E1h - IF (AL & 0x08) != 0 JMP 35E1h - DH = DH | 1 - 0ADC:35E1: - CH = DH - CL = 37h - AL = AL - 2 - IF (CH & 0x04) != 0 JMP 35F1h - BH = BH | 0x40 - 0ADC:35F1: - DX = 0x0100 - IF WORD PTR [F800:7FFC] == 0 JMP 3605h (IF WORD at memory address 0xFFFFC == 0) - DX = DX + 1 - 0ADC:3605: - PUSH AX - INT 19h, RESULT = AH - POP AX - IF RESULT == 0 JMP 3614h - WORD PTR DS:[05DB] = 0xFFFF (returned to caller as AX) - return - 0ADC:3614: - WORD PTR DS:[05DB] = 0x0000 (returned to caller as AX) - return - --- - - 0ADC:378E: (CL=10h entry point) - BX = WORD PTR DS:[05DB] caller's AX value - BX = (BX >> 8) (BX = caller's AH value) - ADDR = (BX * 4) + 3A7Ch - AX = WORD PTR CS:[ADDR] (0ADC:[ADDR]) - BX = WORD PTR CS:[ADDR+2] - CALL NEAR AX - return - --- - - 0ADC:37A7: (CL=10h AH=00h entry point) - CL = DL - CALL NEAR BX (BX is 0A9C, no other case) - 0ADC:37AB: - return - --- - - 0ADC:37AC: (CL=10h AH=01h entry point) - ES = WORD PTR DS:[05E1] ; caller's DS segment - BX = DX ; caller's DX register - 0ADC:37B2: - CL = BYTE PTR ES:[BX] - IF CL == 0x24 JMP 37C2h ; stop at '$' - PUSH BX - CALL A9Ch - POP BX - INC BX - JMP 37B2h - 0ADC:37C2: - return - --- - - 0ADC:37C3: (CL=10h AH=02h entry point) - BYTE PTR DS:[011D],DL ; DL = caller's DL, assign to character attribute - WORD PTR DS:[013C],DX ; DX = caller's DX, assign to extended character attribute - return - --- - - 0ADC:37CC: (CL=10h AH=03h entry point) - DX += 0x2020 ; DX = caller's DX add 0x20 to both bytes - swap(DL,DH) ; XCHG DL, DH - CX = DX - CALL WORD PTR CS:[BX] ; BX = 0x0AFA in all cases, which then feeds the "ESC =" handling code - return - --- - - 0ADC:37D7: (CL=10h AH=04h entry point, where BX = 0x0B90 aka the "ESC D" handler) - (CL=10h AH=05h entry point, where BX = 0x0B99 aka the "ESC M" handler) - CALL WORD PTR CS:[BX] ; BX = 0x0B90 or BX = 0x0B99 - return - --- - - 0ADC:37DA: (CL=10h AH=06h entry point, where BX = 0x0C52 aka the "ESC [ A" handler) - (CL=10h AH=07h entry point, where BX = 0x0C75 aka the "ESC [ B" handler) - (CL=10h AH=08h entry point, where BX = 0x0C9C aka the "ESC [ C" handler) - (CL=10h AH=09h entry point, where BX = 0x0CC3 aka the "ESC [ D" handler) - (CL=10h AH=0Ch entry point, where BX = 0x0E4E aka the "ESC [ L" handler) - (CL=10h AH=0Dh entry point, where BX = 0x0E72 aka the "ESC [ M" handler) - DH = 0 - CX = DX - CALL WORD PTR CS:[BX] - return - --- - - 0ADC:37E1: (CL=10h AH=0Ah entry point, where BX = 0x0D2E) - (CL=10h AH=0Bh entry point, where BX = 0x0D72) - DH = 0 - AX = DX - CALL WORD PTR CS:[BX] - return - --- - - 0ADC:3A5C array of WORD values, offsets of procedures for each value of CL. - - 0ADC:3A5C table contents. - Note the INT DCh code maps: - CL = 0x09..0x15 to table index 0x00..0x0C - CL = 0x80..0x82 to table index 0x0D..0x0F - - . . . . . . . . - 0ADC:00003A5C B8 3A 5B 35 A4 31 A5 31 DF 32 1B 36 44 37 8E 37 - . . . . . . . . |END - 0ADC:00003A6C F0 37 6E 38 F7 38 85 3B 52 3C 27 39 B5 39 10 3A - - CL = 0x09 0x3AB8 - CL = 0x0A 0x355B - CL = 0x0B 0x31A4 - CL = 0x0C 0x31A5 - CL = 0x0D 0x32DF - CL = 0x0E 0x361B - CL = 0x0F 0x3744 - CL = 0x10 0x378E - CL = 0x11 0x37F0 - CL = 0x12 0x386E - CL = 0x13 0x38F7 - CL = 0x14 0x3B85 - CL = 0x15 0x3C52 - CL = 0x80 0x3927 - CL = 0x81 0x39B5 - CL = 0x82 0x3A10 - --- - - 0ADC:3A7C array of WORD value pairs (address, parameter). NOTE: Lack of range checking! - Lookup table for CL=10h, index by AH - - . . . . - 0ADC:00003A7C A7 37 9C 0A AC 37 00 00 C3 37 00 00 CC 37 FA 0A .7...7...7...7.. - . . . . - 0ADC:00003A8C D7 37 90 0B D7 37 99 0B DA 37 52 0C DA 37 75 0C .7...7...7R..7u. - . . . . - 0ADC:00003A9C DA 37 9C 0C DA 37 C3 0C E1 37 2E 0D E1 37 72 0D .7...7...7...7r. - . . . . |END? - 0ADC:00003AAC DA 37 4E 0E DA 37 72 0E E8 37 2D 0B 8B 16 E3 05 .7N..7r..7-..... - ? ? ? ? - 0ADC:00003ABC A1 DB 05 3D 00 00 74 10 3D 01 00 74 20 3D 10 00 ...=..t.=..t =.. - ? ? ? ? - 0ADC:00003ACC 74 4E 3D 11 00 74 49 C3 8E 06 E1 05 8B FA 2E 8E tN=..tI......... - - 0ADC:3A7C Call table (address, param) - - Calling convention on entry: AX = subroutine address, BX = param - - AH = 0x00 0x37A7 0x0A9C ; 0ADC:00003A7C - AH = 0x01 0x37AC 0x0000 - AH = 0x02 0x37C3 0x0000 - AH = 0x03 0x37CC 0x0AFA - AH = 0x04 0x37D7 0x0B90 ; 0ADC:00003A8C - AH = 0x05 0x37D7 0x0B99 - AH = 0x06 0x37DA 0x0C52 - AH = 0x07 0x37DA 0x0C75 - AH = 0x08 0x37DA 0x0C9C ; 0ADC:00003A9C - AH = 0x09 0x37DA 0x0CC3 - AH = 0x0A 0x37E1 0x0D2E - AH = 0x0B 0x37E1 0x0D72 - AH = 0x0C 0x37DA 0x0E4E ; 0ADC:00003AAC - AH = 0x0D 0x37DA 0x0E72 - AH = 0x0E 0x37E8 0x0B2D - AH = 0x0F 0x168B 0x05E3 - --- - - 0ADC:3AB8: (INT DCh CL=0x09) [DS=0x0060 DOS SEGMENT] - DX = WORD PTR DS:[05E3] (caller DX) - AX = WORD PTR DS:[05DB] (caller AX) - IF AX == 0x0000 JMP 3AD4h - IF AX == 0x0001 JMP 3AE9h - IF AX == 0x0010 JMP 3B1Ch - IF AX == 0x0011 JMP 3B1Ch - 0ADC:3AD3: - return - 0ADC:3AD4: (INT DCh CL=0x09 AX=0x0000) - ES = WORD PTR DS:[05E1] (caller DS) - DI = DX (caller DX) - DS = WORD PTR CS:[0030] (DOS segment 60h) - SI = 1DBBh - _fmemcpy(ES:DI,DS:SI,8) - return - 0ADC:3AE9: (INT DCh CL=0x09 AX=0x0001) - WORD PTR DS:[05DB] = 0xFFFF (caller AX set to 0xFFFF) - IF BYTE PTR DS:[1DC4] == 0 JMP 3B1Bh - DL = DL AND 0x0F - BX = 26Ah - CX = (WORD)BYTE PTR DS:[1DC4] * 4 - 0ADC:3B06: - IF BYTE PTR DS:[BX+2] != DL JMP 3B15h - BYTE PTR DS:[BX+1] = 2 - WORD PTR DS:[05DB] = 0x0000 (caller AX set to 0x0000) - BX += WORD PTR DS:[0268] - IF CX > 0 THEN CX--, JMP 3B06h - 0ADC:3B1B: - return - 0ADC:3B1C: (INT DCh CL=0x09 AX=0x0010 or AX=0x0011) - WORD PTR DS:[05DB] = 0xFFFF (caller AX set to 0xFFFF) - IF BYTE PTR DS:[1DC4] == 0 JMP 3B84h - DX = DX AND 0xFF - CX = 0x1A - BX = 2C86h - 0ADC:3B31: - IF BYTE PTR DS:[BX+1] == DL JMP 3B3Fh - BX += 2 - DH += 1 - IF CX > 0 THEN CX--, JMP 3B31h - JMP 3B84h - 0ADC:3B3F: - IF (BYTE PTR DS:[BX] AND 1) == 0 JMP 3B84h - BYTE PTR DS:[0136] = DH (set last access drive number) - IF AX != 0x0010 JMP 3B52h - CALL 5B07h - JMP 3B69h - 0ADC:3B52: - WORD PTR DS:[05DB] = 0x0001 (caller AX set to 0x0001) - BX = DL & 0x0F - IF BYTE PTR [BX+03E9h] == 0 JMP 3B84h - CALL 5B4Bh, RESULT = CARRY FLAG - 0ADC:3B69: - WORD PTR DS:[05DB] = 0x0000 - IF RESULT == 0 (CF=0) JMP 3B84h - WORD PTR DS:[05DB] = 0x0002 - IF BYTE PTR DS:[025B] == 2 JMP 3B84h - WORD PTR DS:[05DB] = 0x0003 - 0ADC:3B84: - return - --- - - Called from 0ADC:3180 (near) - - 0ADC:4080: - CLI - PUSH DS - DS = DOS kernel segment 60h from 0ADC:0030 - BYTE PTR DS:[00B4] = 0 - 0ADC:408C: - IF BYTE PTR DS:[00A5] != 01h JMP 40ACh - 0ADC:4093: - IF BYTE PTR DS:[00A6] == 01h JMP 40A2h - 0ADC:409A: - STI - PUSH AX - CALL 40AFh - POP AX - POP DS - return - 0ADC:40A2: - BYTE PTR DS:[00A5] = 0x00 - BYTE PTR DS:[00A6] = 0x00 - 0ADC:40AC: - STI - POP DS - return - 0ADC:40AF: - IF WORD PTR DS:[2A7C] == 0h JMP 40C6h - 0ADC:40B6: - PUSH DS - WORD PTR DS:[2A7A] = 0x18 - CALL FAR 0060h:3C6Fh - NOP - NOP - NOP - POP DS - 0ADC:40C6: - return - +Entry point (MS-DOS 5.00) 1.44MB disk image (on my hard drive, boot144.dsk). Configuration menu option "nothing". 0ADC segment may change location based on configuration. + +-- + + 0060:0020 WORD MS-DOS product number [see INT DCh CL=12h] + 0060:0022 WORD Internal revision number [see INT DCh CL=15h AH=0] + 0060:002E WORD INT DCh / ANSI segment (in this dump, 0ADCh) + 0060:0030 WORD ?? (in this dump, 3800h) + 0060:0032 WORD Text VRAM segment (A000h) + 0060:0037 BYTE ?? + 0060:0068 WORD RS-232C channel 0 AUX protocol [undocumented PC-98 webtech] [see INT DCh CL=0Ah, INT DCh CL=0Eh AH=01h] + 0060:008A BYTE kanji / graph mode flag + 0060:008B BYTE ?? + 0060:00A5 BYTE ?? + 0060:00A6 BYTE ?? + 0060:00B4 BYTE INT DCh in-progress flag [undocumented PC-98 webtech] + 0060:0110 BYTE Cursor Y coordinate + 0060:0111 BYTE Function row display (0=off 1=on 2=shift function keys) + 0060:0112 BYTE Scroll range lower limit + 0060:0113 BYTE Number of screen lines + 0060:0114 BYTE Erasure attribute (usually E1h) + 0060:0115 BYTE kanji upper byte storage flag + 0060:0116 BYTE kanji upper byte + 0060:0117 BYTE Line wrap flag + 0060:0118 BYTE Scroll speed (0=normal 1=slow) + 0060:0119 WORD Erasure character (usually 20h) + 0060:011B BYTE Cursor display state (0=off 1=on) + 0060:011C BYTE Cursor X coordinate + 0060:011D BYTE Character attribute (i.e. controlled by [m ) [INT DCh CL=10h AH=02h] + 0060:011E BYTE Scroll range upper limit + 0060:011F BYTE Scroll "weight" (delay, apparently?) (0001h = normal E000h = slow) + 0060:0124 WORD ACFh BX value (?)) + 0060:0128 BYTE ANSI escape handling state (0=normal 1=ESC 2>=ANSI processing) + 0060:0129 BYTE ANSI escape handling state (?) + 0060:012A BYTE ANSI escape handling state (?) 0 = ? 1 = ESC[> 2 = ESC[? 3 = ESC[= + 0060:012B BYTE Saved cursor attribute ( ESC [s ) + 0060:0134 WORD ANSI escape handling pointer of some kind (?) + 0060:0136 BYTE drive number last accessed by IO.SYS block driver [INT DCh CL=13h] + 0060:013C WORD display attribute in extended attribute mode [INT DCh CL=10h AL=02h] + 0060:013E WORD erasure attribute in extended attribute mode [INT DCh CL=10h AL=02h] + 0060:014E BYTE some sort of flag + 0060:0162 WORD ?? + 0060:0214 WORD:WORD 16-bit far pointer (0ADC:3126) + 0060:0268 WORD ?? + 0060:05DB WORD stored AX value from caller + 0060:05DD WORD stored SS value from caller + 0060:05DF WORD stored SP value from caller (after INT DCh int frame and PUSH DS) + 0060:05E1 WORD stored DS value from caller + 0060:05E3 WORD stored DX value from caller + 0060:05E5 WORD stored BX value from caller + 0060:0767 Stack pointer (from DOS segment), stack switches to on entry to procedure + 0060:17FA WORD ?? + 0060:1802 WORD ?? + 0060:197A BYTE ?? + 0060:1DC4 BYTE ?? + 0060:2852 BYTE x 0x28 ANSI escape parsing state (such as, numeric values from ESC[m ) + 0060:2A7A WORD ?? + 0060:2A7C WORD ?? + 0060:2C86 WORD x 0x1A ?? + 0060:36B3 INT DCh entry point + 0060:3B30 Subroutine called on INT DCh if 0060:014E is nonzero + +-- + +INT DC = 60:36B3 + + 0060:36B3: + if BYTE PTR cs:[014E] == 0 jmp 0060:36BE + CALL 3B30h + 0060:36BE: + jmp far WORD PTR cs:[0214] (0ADC:3126) + +-- + + 0060:3B30: (called by INT DCh entry point if BYTE PTR CS:[014E] != 0 + PUSH DS, ES, CX, SI, DI + DS:SI = WORD PTR cs:[3B28] (FFFF:0090) + ES:DI = WORD PTR cs:[3B2C] (0000:0080) + ZF = ((result=_fmemcmp(DS:SI,ES:DI,16)) == 0) (CX = 8, REP CMPSW) ; <- Testing A20 gate, apparently? + POP DI, SI, CX, ES, DS + IF result == 0 JMP 3B4Ch (if ZF=1) + return + 0060:3B4C: (if _fmemcmp(DS:SI,ES:DI,16) == 0) + PUSH AX, BX + AH = 5 + CALL FAR WORD PTR cs:[014F] ; <- ??? Pointer so far has been either 0000:0000 or FFFF:FFFF + POP BX, AX + return + ; ^ NOTE: Not sure, but this may be a call into HIMEM.SYS (XMS), in which case AH = 5 means Local Enable A20 + +-- + + 0ADC:0030 WORD DOS kernel segment (60h) + +-- + + 0ADC:0032: + PUSH DS + DS = WORD PTR CS:[0030] ; DOS kernel segment (60h) + IF (BYTE PTR DS:[197A] & 3) == 0 JMP 61h ; 0060:197A + 0ADC:003F: + ES = 0 + CX = WORD PTR ES:[05EA] ; 0000:05EA Disk 0 Parameter table segment?? + IF CX != WORD PTR DS:[002E] JMP 52h ; ?? + DI = 5EAh + WORD PTR ES:[DI] = AX, DI += 2 ; STOSW + 0ADC:0052: + CX = WORD PTR ES:[05EE] ; 0000:05EE Disk 1 Parameter table segment? + IF CX != WORD PTR DS:[002E] JMP 61h + DI = 5EEh + WORD PTR ES:[DI] = AX, DI += 2 ; STOSW + 0ADC:0061: + POP DS + ES = WORD PTR CS:[0030] ; DOS kernel segment (60h) + WORD PTR ES:[002E] = AX ; ?? + DI = 162h + CX = 36h + 0ADC:0071: + WORD PTR ES:[DI] = AX, DI += 4 ; STOSW ; INC DI ; INC DI + IF CX > 0 THEN CX--, JMP 71h ; LOOP 71h + 0ADC:0076: + return far ; RETF + +-- + + 0ADC:0A00 Subroutine lookup table + Referred from 0ADC:0AAC, CL is single char or first byte of kanji + CL=10h AH=00h, search by CL register from caller, or normal CON output. + . . . . . . + 0ADC:00000A00 07 E7 10 08 19 11 09 F8 10 0A 49 11 0B 3A 11 0C ..........I..:.. + . . . . . + 0ADC:00000A10 8C 11 0D 5E 11 1A 7D 11 1B C1 10 1E 6B 11 00 B3 ...^..}.....k... + |END + 0ADC:00000A20 11 5B B2 0B 3D E4 0A 2A 84 0B 28 77 0B 44 90 0B .[..=..*..(w.D.. + + CL value | Subroutine address + CL = 0x07 0x10E7 ; CTRL+G / BEL + CL = 0x08 0x1119 ; CTRL+H / BACKSPACE / LEFT ARROW + CL = 0x09 0x10F8 ; CTRL+I / TAB + CL = 0x0A 0x1149 ; CTRL+J / DOWN ARROW + CL = 0x0B 0x113A ; CTRL+K / UP ARROW + CL = 0x0C 0x118C ; CTRL+L / RIGHT ARROW + CL = 0x0D 0x115E ; CTRL+M / CARRIAGE RETURN + CL = 0x1A 0x117D ; CTRL+Z / SUBSTITUE + CL = 0x1B 0x10C1 ; ESCAPE + CL = 0x1E 0x116B ; RECORD SEPARATOR + Any other (0) 0x11B3 + + The ESCAPE subroutine sets BYTE PTR [60:128] = 1, BYTE PTR [60:129] = 1, + and WORD PTR [60:134] = 0x2852 + +-- + + 0ADC:0A21 Subroutine lookup table + Referred from 0ADC:0AC0, CL is byte from caller when (BYTE PTR DS:[0128] == 2) + In most cases this is called with CL the first byte after ANSI ESCAPE code. + + . . . . . + 0ADC:00000A20 11 5B B2 0B 3D E4 0A 2A 84 0B 28 77 0B 44 90 0B .[..=..*..(w.D.. + . . . . |END + 0ADC:00000A30 45 8D 0B 4D 99 0B 29 26 0B 00 93 0B 41 49 0C 42 E..M..)&....AI.B + + CL value | Subroutine address + CL = 0x5B 0x0BB2 ; ESC [ + CL = 0x3D 0x0AE4 ; ESC = + CL = 0x2A 0x0B84 ; ESC * + CL = 0x28 0x0B77 ; ESC ( + CL = 0x44 0x0B90 ; ESC D + CL = 0x45 0x0B8D ; ESC E + CL = 0x4D 0x0B99 ; ESC M + CL = 0x29 0x0B26 ; ESC ) + Any other (0) 0x0B93 ; ESC (anything else) + + The subroutine for (anything else) sets BYTE PTR [60:128] = 0, cancelling the + ESC handling and ignoring the byte. + +-- + + 0ADC:0A3C Subroutine lookup table + Referred from ADC:0BDC, CL is the last byte of the ANSI ESC [ code. + This is called only on the final byte, not the intermediate digits and + semicolons of the code. + + . . . . . . + 0ADC:00000A3C 41 49 0C 42 6C 0C 43 93 0C 44 BA 0C 48 DD 0C 4A AI.Bl.C..D..H..J + . . . . . + 0ADC:00000A4C 25 0D 4B 69 0D 4C 3E 0E 4D 69 0E 6D BD 0D 66 DD %.Ki.L>.Mi.m..f. + . . . . . + 0ADC:00000A5C 0C 68 32 0F 6C 9A 0E 6E CB 0F 73 24 10 70 6F 10 .h2.l..n..s$.po. + . . . . . |END + 0ADC:00000A6C 75 48 10 3E 88 0E 3F 8E 0E 3D 94 0E 00 C0 10 90 uH.>..?..=...... + + CL value | Subroutine address + CL = 0x41 0x0C49 ; ESC [ A + CL = 0x42 0x0C6C ; ESC [ B + CL = 0x43 0x0C93 ; ESC [ C + CL = 0x44 0x0CBA ; ESC [ D + CL = 0x48 0x0CDD ; ESC [ H + CL = 0x4A 0x0D25 ; ESC [ J + CL = 0x4B 0x0D69 ; ESC [ K + CL = 0x4C 0x0E3E ; ESC [ L + CL = 0x4D 0x0E69 ; ESC [ M + CL = 0x6D 0x0DBD ; ESC [ m + CL = 0x66 0x0CDD ; ESC [ f + CL = 0x68 0x0F32 ; ESC [ h + CL = 0x6C 0x0E9A ; ESC [ l + CL = 0x6E 0x0FCB ; ESC [ n + CL = 0x73 0x1024 ; ESC [ s + CL = 0x70 0x106F ; ESC [ p + CL = 0x75 0x1048 ; ESC [ u + CL = 0x3E 0x0E88 ; ESC [ > + CL = 0x3F 0x0E8E ; ESC [ ? + CL = 0x3D 0x0E94 ; ESC [ = + Any other (0) 0x10C0 ; ESC [ (anything else) + + The anything else handler returns immediately (escape code is ignored) and + the calling code resets the flag and allows the console to proceed normally. + +-- + + . . . . . . . . + 0ADC:00000A7C E2 12 F2 12 E2 12 CA 12 BC 12 F2 12 C3 12 D1 12 ................ + . . |END + 0ADC:00000A8C C3 12 D8 12 E8 01 00 CB B8 00 01 C3 E8 01 00 CB ................ + + 0ADC:0A7C table (routine 0ADC:1201 return from CALL 129Dh) + AX = 0x0000 DL = 0x00 0x12E2 + AX = 0x0000 DL = 0x01 0x12F2 + AX = 0x0001 DL = 0x00 0x12E2 + AX = 0x0001 DL = 0x01 0x12CA + AX = 0x0002 DL = 0x00 0x12BC + AX = 0x0002 DL = 0x01 0x12F2 + AX = 0x0003 DL = 0x00 0x12C3 + AX = 0x0003 DL = 0x01 0x12D1 + AX = 0x0004 DL = 0x00 0x12C3 + AX = 0x0004 DL = 0x01 0x12D8 + +-- + + 0ADC:0A90: + CALL A94h + return far ; RETF + 0ADC:0A94: + MOV AX,0100h + return + +-- + + 0ADC:0A98: + CALL A9Ch + return far ; RET + +-- + + 0ADC:0A9C: (CL=10h AH=00h, at this time CL == caller's DL and DS = DOS segment 60h) + IF BYTE PTR DS:[0128] != 0 JMP AB5 (60:128) + IF CL < 0x20 JMP AACh + CALL 11B3h + 0ADC:0AAC: (CL < 0x20) + BX = 0A00h + CALL 0ACFh + CALL NEAR BX (BX comes from subroutine ACFh) + return + 0ADC:0AB5: (BYTE PTR DS:[0128] != 0) aka (60:128) + (BYTE DS:[0128])++ + IF BYTE PTR DS:[0128] != 2 JMP 0ACAh + 0ADC:0AC0: (BYTE PTR DS:[0128] == 2) + BX = 0A21h + CALL 0ACFh + WORD PTR DS:[0124] = BX (BX comes from subroutine ACFh) (60:124) + 0ADC:0ACA: (jmp here from BYTE PTR DS:[0128] != 2 compare under 0ADC:0AB5) + CALL NEAR WORD PTR DS:[0124] + return + +-- + + ; Entry: BX = memory location of a lookup table, 3 bytes/entry in this format: + ; BYTE subroutine number + ; WORD subroutine address + ; CL = subroutine number to match + ; + ; Exit: BX = subroutine address + 0ADC:0ACF: + IF BYTE PTR CS:[BX] == 0 JMP ADFh + IF BYTE PTR CS:[BX] == CL JMP ADFh + BX += 3 + JMP ACFh + 0ADC:0ADF: + BX = WORD PTR CS:[BX+1] + return + +-- + + ; Entry: CL = character code + ; + ; This is executed per character after ESC = + 0ADC:0AE4: + BX = WORD PTR DS:[0134] ; [0134] is often set to 2852h + WORD PTR DS:[BX],CL + WORD PTR DS:[0134] += 1 + IF BYTE PTR DS:[0128] < 0x04 JMP B18h + 0ADC:0AF5: + BX = 2853h ; ANSI escape parsing address + 1 + 0ADC:0AF8: + CX = WORD PTR DS:[BX] + 0ADC:0AFA: + AL = CL + AH = BYTE PTR DS:[0112] ; Scroll range lower limit + CALL B19h + BYTE PTR DS:[0110],AL ; Cursor Y position + AL = CH + AH = 0x4F + CALL B19h + BYTE PTR DS:[011C],AL ; Cursor X position + CALL 1535h ; update cursor position + BYTE PTR DS:[0128] = 0 + 0ADC:0B18: + return + +-- + + 0ADC:0B19: + AL = ((AL >= 0x20) ? (AL - 0x20)) : 0 ; SUB AL, 20h ; JNC B1Fh ; MOV AL,00h + 0ADC:0B1F: + AL = ((AL > AH) ? AH : AL) ; CMP AL, AH ; JBE B25h ; MOV AL, AH + 0ADC:0B25: + return + +-- + + ; Entry: CL = character code + ; + ; This is executed per character after ESC ) until the end of the ANSI code + 0ADC:0B26: + IF BYTE PTR DS:[0128] != 3 JMP B76h + IF CL == 0x33 JMP B43h + IF CL != 0x30 JMP B4Dh + 0ADC:0B37: + BYTE PTR DS:[008A] = 0x01 + BYTE PTR DS:[008B] = 0x20 + JMP B4Dh + 0ADC:0B43: + BYTE PTR DS:[008B] = 0x67 + BYTE PTR DS:[008A] = 0x00 + 0ADC:0B4D: + IF BYTE PTR DS:[0111] == 0 JMP B71h + 0ADC:0B54: + PUSH ES + ES = WORD PTR DS:[0032] ; video ram segment + DH = (BYTE PTR DS:[0112]) + 1 + DL = 0 + CALL 14F5h ; convert row DH col DL to video memory address + BX += 2 + AX = BYTE PTR DS:[008B] + WORD PTR DS:[BX] = AX + POP ES + 0ADC:0B71: + BYTE PTR DS:[0128] = 0 + 0ADC:0B76: + return + +-- + + ; Entry: CL = character code + ; + ; This is executed per character after ESC ( until the end of the ANSI code + 0ADC:0B77: + IF BYTE PTR DS:[0128] < 3 JMP B83h + BYTE PTR DS:[0128] = 0 + 0ADC:0B83: + return + +-- + + ; Entry: CL = character code + ; + ; This is executed per character after ESC * until the end of the ANSI code + 0ADC:0B84: + CALL 117Dh ; CTRL+Z handling + BYTE PTR DS:[0128] = 0 + 0ADC:0B8C: + return + +-- + + ; Entry: CL = character code + ; + ; This is executed per character after ESC E until the end of the ANSI code + 0ADC:0B8D: + CALL 115Eh ; carriage return + 0ADC:0B90: + CALL 1149h ; line feed / down arrow + 0ADC:0B93: ; <- table jumps here for unknown escapes + BYTE PTR DS:[0128] = 0 + 0ADC:0B98: + return + +-- + + ; Entry: CL = character code + ; + ; This is executed per character after ESC M + 0ADC:0B99: + IF BYTE PTR DS:[0110] == 0 JMP BA9h ; Move cursor up one row + BYTE PTR DS:[0110] -= 1 + CALL 1535h ; update cursor position + JMP BACh + 0ADC:0BA9: + CALL 13B0h ; scroll region down one line + 0ADC:0BAC: + BYTE PTR DS:[0128] = 0 + 0ADC:0BB1: + return + +-- + + ; Entry: CL = character code + ; + ; This is executed per character after ESC [ until the end of the ANSI code + 0ADC:0BB2: + IF BYTE PTR DS:[0128] == 0x02 JMP BF3h + IF CL == 0x3B JMP C05h + IF CL == 0x27 JMP C1Bh + IF CL == 0x22 JMP C1Bh + IF BYTE PTR DS:[012A] == 0xFF JMP C2Ch + IF CL >= 0x3A JMP BDCh + CL = CL AND 0x0F + IF CL <= 0x09 JMP BF4h + 0ADC:0BDC: + BX = 0A3Ch + CALL ACFh + SI = 2852h + CALL NEAR BX + 0ADC:0BE7: + IF BYTE PTR DS:[012A] != 0 JMP BF3h + BYTE PTR DS:[0128] = 0 + 0ADC:0BF3: + return + +-- + + ; ESC [ A + ; SI = ANSI escape area (2852h) + 0ADC:0C49: + IF BYTE PTR DS:[0129] != 1 JMP C6Bh + 0ADC:0C50: + CX = WORD PTR DS:[SI] ; numeric value before "A" character, parameter + 0ADC:0C52: + AX = BYTE PTR DS:[0110] ; Cursor Y position + IF CX == 0 THEN CX = 1 ; AND CX, CX ; JNE C5Eh ; MOV CX, 1 + 0ADC:0C5E: + IF CX > AX THEN CX = AX ; CMP AX, CX ; JNC C64h ; MOV CX, AX + 0ADC:0C64: + BYTE PTR DS:[0110] -= CL ; Cursor Y position -= parameter + CALL 1535h ; update cursor position on screen + 0ADC:0C6B: + return + +-- + + ; ESC [ B + ; SI = ANSI escape area (2852h) + 0ADC:0C6C: + IF BYTE PTR DS:[0129] != 1 JMP C92h + CX = WORD PTR DS:[SI] + 0ADC:0C75: + AL = (BYTE PTR DS:[0112]) - (BYTE PTR DS:[0110]), AH = 0 + IF CX == 0 THEN CX = 1 ; AND CX, CX ; JNE C85h ; MOV CX, 1 + IF CX > AX THEN CX = AX ; CMP AX, CX ; JNC C8Bh ; MOV CX, AX + BYTE PTR DS:[0110] += CL ; Cursor Y position += CL + CALL 1535h ; update cursor position on screen + 0ADC:0C92: + return + +-- + + ; ESC [ C + ; SI = ANSI escape area (2852h) + 0ADC:0C93: + IF BYTE PTR DS:[0129] != 1 JMP CB9h + CX = WORD PTR DS:[SI] + 0ADC:0C9C: + AX = 0x4F + if (AL -= BYTE PTR DS:[011C]) < 0 JMP CB6h ; SUB AL,[011C] ; JC CB6h + IF CX == 0 THEN CX = 1 ; AND CX, CX ; JNZ CACh + 0ADC:0CAC: + IF CX > AX THEN CX = AX ; CMP AX, CX ; JNC CB2h ; MOV CX, AX + BYTE PTR DS:[011C] += CL ; Cursor X position += CL + 0ADC:0CB6: + CALL 1535h ; update cursor position on screen + 0ADC:0CB9: + return + +-- + + ; ESC [ D + ; SI = ANSI escape area (2852h) + 0ADC:0CBA: + IF BYTE PTR DS:[0129] != 1 JMP CDCh + CX = WORD PTR DS:[SI] + 0ADC:0CC3: + AX = BYTE PTR DS:[011C] ; Cursor X position + IF CX == 0 THEN CX = 1 ; AND CX, CX ; JNZ CCFh + IF CX > AX THEN CX = AX ; CMP AX, CX ; JNC CD5h ; MOV CX, AX + BYTE PTR DS:[011C] -= CL ; Cursor X position -= CL + 0ADC:0CD9: + CALL 1535h ; update cursor position on screen + 0ADC:0CDC: + return + +-- + + ; ESC [ L + ; SI = ANSI escape area (2852h) + 0ADC:0E3E: + IF BYTE PTR DS:[012A] != 0 JMP E63h + IF BYTE PTR DS:[0129] != 1 JMP E63h + 0ADC:0E4C: + CX = WORD PTR DS:[SI] + 0ADC:0E4E: + IF CX == 0 THEN CX = 1 ; AND CX, CX ; JNZ E55h + 0ADC:0E55: + PUSH CX + AH = BYTE PTR DS:[0110] ; AH = Cursor Y position + CALL 13B4h + POP CX + IF CX > 0 THEN CX--, JMP E55h ; LOOP E55h + 0ADC:0E60: + CALL 115Eh ; carriage return + 0ADC:0E63: + BYTE PTR DS:[012A] = 0 + 0ADC:0E68: + return + +-- + + ; ESC [ M + ; SI = ANSI escape area (2852h) + 0ADC:0E69: + IF BYTE PTR DS:[0129] != 1 JMP E87h + CX = WORD PTR DS:[SI] + 0ADC:0E72: + IF CX == 0 THEN CX = 1 ; AND CX, CX ; JNZ E55h + 0ADC:0E79: + PUSH CX + AH = BYTE PTR DS:[0110] ; AH = Cursor Y position + CALL 135Fh + POP CX + IF CX > 0 THEN CX--, JMP E79h ; LOOP E79h + 0ADC:0E84: + CALL 115Eh ; carriage return + 0ADC:0E87: + return + +-- + + ; Entry: CL = character code + ; + ; This is executed on ESC [ > + 0ADC:0E88: + BYTE PTR DS:[012A] = 1 + return + +-- + + ; Entry: CL = character code + ; + ; This is executed on ESC [ ? + 0ADC:0E8E: + BYTE PTR DS:[012A] = 2 + return + +-- + + ; Entry: CL = character code + ; + ; This is executed on ESC [ = + 0ADC:0E94: + BYTE PTR DS:[012A] = 3 + return + +-- + + ; Entry: CL = character code + ; + ; This is executed when the ESC is encountered outside of ANSI processing + + 0ADC:10C1: + PUSH ES + WORD PTR DS:[0134] = 0x2852 + BYTE PTR DS:[0128] = 0x01 + BYTE PTR DS:[0129] = 0x01 + DI = 0x2852 + AX = 0 + CX = 0x14 + DX = ES = DS + _fmemset(ES:DI,0,0x28) ; REP STOSW + POP ES + 0ADC:10E2: + return + +-- + + ; ?? + 0ADC:10E3: + CALL 10E7h + 0ADC:10E6: + return far ; RETF + +-- + + ; ASCII BEL 07h handling + 0ADC:10E7: + AH = 17h ; INT 18h AH=17h turn on bell + INT 18h + CX = 0x6E8C ; <- delay loop + 0ADC:10EE: + PUSH AX + NOP AX + POP AX + IF CX > 0 THEN CX--, JMP 10EEh ; LOOP 10EEh + 0ADC:10F3: + AH = 18h ; INT 18h AH=18h turn off bell + INT 18h + 0ADC:10F7: + return + +-- + + ; ASCII TAB 09h handling + 0ADC:10F8: + AL = ((BYTE PTR DS:[011C] + 8) >> 3) * 8 ; move cursor X position to next multiple of 8 + BYTE PTR DS:[011C] = AL + IF AL < 0x4F JMP 1115h ; interesting bug here, AL would be 0x50 on overflow + 0ADC:110C: + BYTE PTR DS:[011C] = 0x4F ; keep cursor on-screen, set to last column + CALL 118Ch ; call function to advance one column to wrap around to next line + return + 0ADC:1115: + call 1535h ; update cursor position + 0ADC:1118: + return + +-- + + ; ASCII BACKSPACE 08h / LEFT ARROW KEY + 0ADC:1119: + IF BYTE PTR DS:[011C] == 0 JMP 1126h + 0ADC:1120: + BYTE PTR DS:[011C] -= 1 + JMP 1136h + 0ADC:1126: + IF BYTE PTR DS:[0110] == 0 JMP 1139h + 0ADC:112D: + BYTE PTR DS:[0110] -= 1 + BYTE PTR DS:[011C] = 0x4F + 0ADC:1136: + CALL 1535h ; update cursor position on screen + 0ADC:1139: + return + +-- + + ; ASCII UP ARROW 0Bh + 0ADC:113A: + IF BYTE PTR DS:[0110] == 0 JMP 1148h + BYTE PTR DS:[0110] -= 1 + CALL 1535h ; update cursor position on screen + 0ADC:1148: + return + +-- + + ; ASCII DOWN ARROW 0Ah + 0ADC:1149: + AL = BYTE PTR DS:[0110] + IF AL == BYTE PTR DS:[0112] JMP 115Ah + BYTE PTR DS:[0110] += 1 + CALL 1535h ; update cursor position on screen + return + 0ADC:115A: + CALL 1348h ; scroll up screen region + return + +-- + + ; ASCII CARRIAGE RETURN 0Dh + 0ADC:115E: + BYTE PTR DS:[011C] = 0 ; set X coordinate = 0 + CALL 1535h ; update cursor position + 0ADC:1166: + return + +-- + + ; ?? + 0ADC:1167: + CALL 116Bh + 0ADC:116A: + return far ; RETF + +-- + + ; ASCII RECORD SEPARATOR 1Eh + 0ADC:116B: + BYTE PTR DS:[0110] = 0 ; set Y pos = 0 + BYTE PTR DS:[011C] = 0 ; set X pos = 0 + CALL 1535h ; update cursor position + 0ADC:1178: + return + +-- + + 0ADC:1179: + CALL 117Dh + 0ADC:117C: + return far ; RETF + +-- + + 0ADC:117D: (CTRL+Z handling) + DI = 0 + CX = 0x9B0 ; 0x9B0 = 80*31? + CALL 1516h ; clear the screen (fill with erasure) + CALL 13FFh ; remove function row + CALL 116Bh ; call 0x1E RECORD SEPARATOR function + 0ADC:118B: + return + +-- + + 0ADC:118C: (advance cursor one column. if past right edge of screen fall through to 1197h to advance one row) + BYTE PTR DS:[011C] += 1 (cursor X coordinate += 1) + IF BYTE PTR DS:[011C] <= 4Fh JMP 11AFh (if cursor X coordinate <= 4Fh then goto 11AFh) + 0ADC:1197: (advance cursor one row. if past lower scroll limit then fall through to 11A9h scroll up screen region) + BYTE PTR DS:[011C] = 0 (set cursor X coordinate = 0) + AL = BYTE PTR DS:[0110] (AL = (cursor Y coordinate)++) + BYTE PTR DS:[0110] += 1 + IF BYTE PTR DS:[0112] != AL JMP 11AFh (if cursor Y != scroll range lower limit then goto 11AFh) + 0ADC:11A9: (put cursor back on the bottom row and scroll screen region up) + BYTE PTR DS:[0110] = AL (set cursor Y coordinate to scroll range lower limit) + CALL 1348h + 0ADC:11AF: + CALL 1535h (update cursor position on screen) + 0ADC:11B2: + return + +-- + + 0ADC:11B3: (CL=10h AH=00h, at this time CL == caller's DL and DS = DOS segment 60h) + IF BYTE PTR DS:[011C] < 0x50 JMP 11C7h ; (60:11C cursor X position) + IF BYTE PTR DS:[0117] == 0 JMP 11C2h ; (60:117 line wrap flag) + 0ADC:11C1: + return + 0ADC:11C2: + PUSH CX + CALL 118Ch + POP CX + 0ADC:11C7: (60:11C < 0x50) + IF BYTE PTR DS:[008A] == 0 JMP 1201h ; (60:8A kanji / graph mode flag) + IF BYTE PTR DS:[0115] != 0 JMP 11F3h ; (60:115 kanji upper byte storage flag) + IF CL < 0x81 JMP 1201h + IF CL < 0xA0 JMP 11E9h + IF CL < 0xE0 JMP 1201h + IF CL >= 0xFD JMP 1201h + BYTE PTR DS:[0116] = CL (60:116 kanji upper byte) + BYTE PTR DS:[0115] = 1 (60:115 kanji upper byte storage flag) + 0ADC:11E9: + return + 0ADC:11F3: (60:115 kanji upper byte storage flag set) + CH = DS:[0116] (60:116 kanji upper byte) + CALL 1236h + tCH = CH, tCL = CL, CL = tCH - 0x20, CH = tCL ; XCHG CL, CH ; SUB CL, 0x20 + JMP 1203h + 0ADC:1201: + CH = 0 + 0ADC:1203: + CALL 1260h + CALL 129Dh + DI = BX + AX = ((AX * 2) + DL) * 2 + BX = (AX + 0A7Ch) + BX = WORD PTR CS:[BX] + PUSH ES + ES = WORD PTR DS:[0032] ; (60:32 appears to be Text VRAM segment A000h) + AX = CX + CALL NEAR BX + POP ES + BYTE PTR DS:[0115] = 0 (60:115 upper byte storage flag) + BYTE PTR DS:[011C] += AL (60:11C cursor X coordinate) + CALL 1535h + 0ADC:1231: + return + +-- + + 0ADC:1232: + CALL 1236h + 0ADC:1235: + return far ; RETF + +-- + + 0ADC:1236: (CH = upper Kanji byte) + IF CH == 0x80 JMP 125Fh + IF CH >= 0xA0 JMP 1245h + CH -= 0x70 + JMP 1248h + 0ADC:1245: + CH -= 0xB0 + 0ADC:1248: + IF (CL & 0x80) != 0x00 THEN CL-- ; OR CL, CL ; JNS 124E ; DEC CL + 0ADC:124E: + CH = CH * 2 + IF CL < 0x9E JMP 125Ah + CL -= 0x5E + JMP 125Ch + 0ADC:125A: + CH-- + 0ADC:125C: + CL -= 0x1F + 0ADC:125F: + return + +-- + + 0ADC:1260: + PUSH ES, CX + ES = WORD PTR DS:[0032] ; video ram segment (A000h) + DH = BYTE PTR DS:[0110] ; Cursor Y position + DL = BYTE PTR DS:[011C] ; Cursor X position + CALL 14F5h ; convert to video memory address (return in BX) + CX = WORD PTR ES:[BX] ; read from video memory + CALL 129Dh ; Determine cell width (single or double, indicated in DL) + AX = 2 + IF DL == 1 JMP 129Ah + DH = DL + CX = WORD PTR ES:[BX+2] + CALL 129Dh + AX = 0 + IF DH != 2 JMP 1294h + AX = 3 + 0ADC:1294: + IF DL == 0 JMP 129Ah + AX++ + 0ADC:129A: + POP CX, ES + 0ADC:129C: + return + +-- + + 0ADC:129D: + DL = 0 + IF CH == 0x00 JMP 12BBh + IF CH == 0xFF JMP 12BBh + IF CL < 0x09 JMP 12B3h + IF CL < 0x0C JMP 12BBh + 0ADC:12B3: + DL = 1 + IF (CL & 0x80) != 0 JMP 12BBh + DL = 2 + 0ADC:12BB: + return + +-- + + 0ADC:12BC: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) + CALL 1330h + CALL 12E2h ; write single-wide + 0ADC:12C2: + return + +-- + + 0ADC:12C3: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) + CALL 1324h + CALL 12E2h ; write single-wide + 0ADC:12C9: + return + +-- + + 0ADC:12CA: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) + CALL 133Ch + CALL 12F2h ; write double-wide + 0ADC:12D0: + return + +-- + + 0ADC:12D1: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) + CALL 1324h + CALL 12F2h ; write double-wide + 0ADC:12D7: + return + +-- + + 0ADC:12D8: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) + CALL 1324h + CALL 133Ch + CALL 12F2h ; write double-wide + 0ADC:12E1: + return + +-- + + ; This appears to write a single-wide character + 0ADC:12E2: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) + WORD PTR ES:[DI] = AX ; write character code + DI += 0x2000 + WORD PTR ES:[DI] = WORD PTR DS:[013C] (60:13C display attribute in extended attribute mode) ; write attribute code + AL = 1 (this indicates to caller to move cursor X position 1 unit to the right) + 0ADC:12F1: + return + +-- + + ; This appears to write a double-wide (two cell wide) character + 0ADC:12F2: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) + IF BYTE PTR DS:[011C] < 0x4F JMP 1311h ; if Cursor X coordinate < 0x4F + ; NTS: Cursor wraparound case (no room on last column). Write an empty cell then wrap to the next line and print char. + PUSH AX + CX = 0x20, WORD PTR ES:[DI] = CX + CALL 118Ch ; advance cursor one column + DH = BYTE PTR DS:[0110] ; DH = cursor Y + DL = BYTE PTR DS:[011C] ; DL = cursor X + CALL 14F5h ; Convert DH, DL to VRAM address in BX + DI = BX + POP AX + ; NTS: Notice the double-wide (usually Kanji) writing routine writes the code from AX, then + ; writes AX | 0x80. This has no effect on the display on real hardware, but follows + ; NEC PC-98 recommendations. + 0ADC:1311: + WORD PTR ES:[DI] = AX, DI += 2 ; STOSW + WORD PTR ES:[DI] = AX | 0x80, DI += 2 ; OR AL,80h ; STOSW + DI -= 4, DI += 0x2000 + AX = WORD PTR DS:[013C] ; AX = extended color attribute + WORD PTR ES:[DI] = AX, DI += 2 ; STOSW + WORD PTR ES:[DI] = AX, DI += 2 ; STOSW + AL = 2 + 0ADC:1323: + return + +-- + + 0ADC:1324: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) + PUSH DI + DI -= 2 + CX = 0x20 + WORD PTR ES:[DI] = CX + POP DI + 0ADC:132F: + return + +-- + + 0ADC:1330: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) + PUSH DI + DI += 2 + CX = 0x20 + WORD PTR ES:[DI] = CX + POP DI + 0ADC:133B: + return + +-- + + 0ADC:133C: (DS = DOS segment 60h, ES = Text VRAM segment A000h, AX = character code, DI = memory offset) + PUSH DI + DI += 4 + CX = 0x20 + WORD PTR ES:[DI] = CX + POP DI + 0ADC:1347: + return + +-- + + 0ADC:1348: (scroll up screen scroll region) + AH = BYTE PTR DS:[011E] ; scroll region upper limit + WORD PTR DS:[011F] = 0x0001 ; scroll "weight" aka delay + IF BYTE PTR DS:[0118] != 0 THEN WORD PTR DS:[011F] = 0xE000 ; if slow scroll mode set "weight" aka delay to larger value + 0ADC:135F: + CL = BYTE PTR DS:[0112] ; scroll region lower limit + CX = CL - AH ; lower limit - upper limit and zero extend to CX + if CX == 0 JMP 13A1h + 0ADC:1369: + PUSH ES, DS + DX = AH << 8 ; DH = AH, DL = 0 + CALL 14F5h ; take cursor position, convert to video RAM byte address, return in BX + ES = DS = WORD PTR DS:[0032] ; retrieve video (text) RAM segment + DX = BX + 0xA0 ; note 0xA0 = 160 = 80 * 2 + 0ADC:1381: + PUSH CX + ; This part uses REP MOVSW first on character data at A000:0000+x + ; then on attribute data at A000:2000+x + ; Move and swap is used to let REP MOVSW advance SI and DI, then + ; re-use the same starting SI and DI for the attribute data, before + ; then using the post REP MOVSW SI and DI values for the next line. + SI = DX + DI = BX + _fmemcpy(ES:DI,DS:SI,0xA0) ; CX = 0x50 REP MOVSW + SWAP(DX,SI), SI += 0x2000 + SWAP(BX,DI), DI += 0x2000 + _fmemcpy(ES:DI,DS:SI,0xA0) ; CX = 0x50 REP MOVSW + POP CX + IF CX > 0 THEN CX--, JMP 1381h ; LOOP 1381h + 0ADC:139F: + POP DS, ES + 0ADC:13A1: + DH = BYTE PTR DS:[0112] + call 1508h + CX = WORD PTR DS:[011F] + 0ADC:13AC: ; <- delay loop, to "weight" down the console scroll + NOP + IF CX > 0 THEN CX--, JMP 13ACh ; LOOP 13ACh + 0ADC:13AF: + return + +-- + + 0ADC:13B0: (take scroll region, scroll down one line) + AH = BYTE PTR DS:[011E] ; scroll range upper limit + CL = BYTE PTR DS:[0112], CH = 0 ; scroll range lower limit + CL -= AH + IF CL == 0 THEN JMP 13F8h + 0ADC:13BE: + STD ; set direction flag, so that REP MOVSW works backwards + PUSH ES, DS + DH = BYTE PTR DS:[0112] + DL = 0x4F + CALL 14F5h ; convert scroll lower limit row, column 0x4F (79) to video ram address + ES = DS = WORD PTR DS:[0032] ; video RAM segment + DX = BX - 0xA0 ; DX = video RAM address one row up, BX = video RAM address + 0ADC:13D8: + PUSH CX + SI = DX, DI = BX + _fmemcpy_backwards(ES:DI, DS:SI, 0xA0) ; CX = 0x50, REP MOVSW, with DF=1 + XCHG DX, SI ; XCHG BX, DI ; SI,DI modified by REP MOVSW, so swap with copy to re-use original addr for next step + SI += 0x2000 ; DI += 0x2000 ; point at attribute RAM + _fmemcpy_backwards(ES:DI, DS:SI, 0xA0) ; CX = 0x50, REP MOVSW, with DF=1 + POP CX + IF CX > 0 THEN CX--, JMP 13D8h ; LOOP 13D8h + 0ADC:13F6: + POP DS, ES ; CLD + DH = AH ; DH = scroll range upper limit + CALL 1508h + 0ADC:13FE: + return + +-- + + 0ADC:13FF: + IF BYTE PTR DS:[0111] != 0 JMP 1407h + 0ADC:1406: + return + 0ADC:1407: + (TODO) + +-- + + 0ADC:14F5: (DH = Y coordinate DL = X coordinate return in BX = video RAM byte address) + BX = (WORD PTR DS:[(DH * 2) + 0x1814]) + DX = (DL * 2) + BX += DX + return ; 0ADC:1507 + +-- + + 0ADC:1508: (DH = Scroll range lower limit) + DL = 0 + CALL 14F5h (get video RAM address of row DH from caller and column zero) + DI = BX + CX = 0x50 + CALL 1516h + return + 0ADC:1516: (CX = number of WORDs to write, DI = video ram address to fill) + PUSH ES + ES = WORD PTR DS:[0032] ; get video RAM (text) segment A000h + DX = CX ; save CX (for later) + BX = DI ; save DI (for later) + AX = WORD PTR DS:[0119] ; erasure character + _fmemset(ES:DI,cx*2) ; REP STOSW (CX from caller) + AX = WORD PTR DS:[013E] ; erasure attribute + CX = DX ; restore CX from caller + DI = BX + 0x2000 ; restore DI from caller and add 2000h + _fmemset(ES:DI,cx*2) ; REP STOSW (CX from caller) + POP ES + return + +-- + + 0ADC:1534: + return + 0ADC:1535: (update cursor position on screen) + IF BYTE PTR DS:[011B] == 0 JMP 1534h (return immediately if cursor is turned off) + 0ADC:153C: + DH = BYTE PTR DS:[0110] + DL = BYTE PTR DS:[011C] + IF DL >= 0x50 THEN DL-- + 0ADC:154B: + CALL 14F5h (take cursor position, convert to video RAM byte address, return in BX) + AH = 0x13 (INT 18h AX=13h set cursor position) + DX = BX (byte position of cursor) + JMP 198Ah (calls INT 18h AX=13h to set cursor pos. function 198Ah then returns) + +-- + + 0ADC:1814 Row value to video RAM address lookup table used by subroutine at 0ADC:14F5 + + VRAM_ADDRESS = WORD PTR [0ADC:1814 + (row * 2)] + + . . . . . . . . <- Cursor rows 0-7 + 0060:00001814 00 00 A0 00 40 01 E0 01 80 02 20 03 C0 03 60 04 ....@..... ...`. + . . . . . . . . <- Cursor rows 8-15 + 0060:00001824 00 05 A0 05 40 06 E0 06 80 07 20 08 C0 08 60 09 ....@..... ...`. + . . . . . . . . <- Cursor rows 16-23 + 0060:00001834 00 0A A0 0A 40 0B E0 0B 80 0C 20 0D C0 0D 60 0E ....@..... ...`. + . . . . . . . |END <- Cursor rows 24-30 + 0060:00001844 00 0F A0 0F 40 10 E0 10 80 11 20 12 C0 12 00 00 ....@..... ..... + +-- + + 0ADC:198A (this code calls INT 18h in an elaborate way in order to make sure interrupts are disabled at entry point for some reason) + PUSH DS + DS = 0 + PUSHF, CLI, CALL FAR WORD PTR DS:[0060] (DS=0, CALL FAR 0000:0060, which is INT 18h but with interrupts disabled at entry) + POP DS + return + +-- + + 0ADC:3126: + PUSH DS + DS = WORD PTR CS:[0030] = DOS segment 60h + WORD PTR DS:[05E1] = caller DS + Store caller AX, SS, SP, DX, BX into DS: [5DB], [5DD], [5DF], [5E3], [5E5] + SS:SP = WORD PTR CS:[0030] (DOS segment 60h : offset 767h) + CLD, STI + PUSH ES, BX, CX, DX, SI, DI + BYTE PTR DS:[00B4] = 1 + 0ADC:3154: + CL -= 9, JMP to 0ADC:3180 if carry (if caller CL < 9) + 0ADC:315E: + IF CL < 0x0D, JMP TO 0ADC:3173 (if caller CL < (0x16 = 0x0D+9)) + 0ADC:3163: + IF CL < 0x77, JMP TO 0ADC:3180 (if caller CL < (0x80 = 0x77+9)) + 0ADC:3168: + IF CL > 0x79, JMP TO 0ADC:3180 (if caller CL > (0x82 = 0x79+9)) + 0ADC:316D: (JMPed here if CL >= 0x80 && CL <= 0x82) + CL = (CL - 0x77) + 0x0D + 0ADC:3173: (JMPed here if CL >= 0x09 && CL <= 0x15) + SI = (CL * 2) + 0x3A5C + 0ADC:317D: + CALL NEAR WORD PTR 0ADC:[SI] + 0ADC:3180: + CALL NEAR 0ADC:4080 + 0ADC:3183: + POP DI, SI, DX, CX, BX, ES + DS = WORD PTR CS:[0030] = DOS segment 60h + Restore caller AX, SS, SP, DX, BX from [5DB], [5DD], [5DF], [5E3], [5E5] + POP DS (restore caller DS) + IRET + +-- + + 0ADC:32DF: (CL=0Dh entry point) + ES = WORD PTR CS:[0030] ; MS-DOS segment (60h) + AX = WORD PTR DS:[05DB] ; AX from caller + DS = WORD PTR DS:[05E1] ; DS from caller + SI = DX ; DX from caller + IF AX > 0xFF JMP 32F7h ; CMP AX,00FF ; JA 32F7h + IF AX != 0xFF JMP 3310h ; ... JNE 3310h + JMP 3350h + 0ADC:32F7: (AX > 0xFF) + IF AX < 0x101 JMP 3304h ; CMP AX,0101 ; JC 3304h + IF AX == 0x101 JMP 3308h ; ... JE 3308h + IF AX == 0x102 JMP 330Ch ; CMP AX,0102 ; JE 330Ch + 0ADC:3303: + return + 0ADC:3304: (AX < 0x101) + CALL 3445h + return + 0ADC:3308: (AX == 0x101) + CALL 349Ch + return + 0ADC:330C: (AX == 0x102) + CALL 3538h + return + 0ADC:3310: (AX < 0xFF) + IF AX != 0 THEN AX--, JMP 3390h ; SUB AX, 1 ; JNC 3390h + 0ADC:3315: (AX == 0, DS = caller DS segment, SI = caller DX register, ES = MS-DOS segment 60h) + DI = 2D2Eh ; This appears to be the memory location that holds the function key row strings and attributes + DL = 0Ah ; There are 10 entries, 16 bytes apart + 0ADC:331A: + CALL 3547h ; + _fmemcpy(ES:DI, DS:SI, 0x0F) ; CX = 0x0F ; REP MOVSB + ; Side effect: SI += 0x0F, DI += 0x0F + SI++ + IF DL > 0 THEN DL--, JMP 331Ah ; DEC DL ; JNZ 331Ah + 0ADC:3327: + DI += 0x50 ; Reminder: 2D2Eh + (0x10 * 0x0A) + 0x50 = 2E1Eh + ; This (2E1Eh) appears to hold the text that is injected when the function key is pressed. + ; For example by default this table defines F1 -> "DIR A:\x0D" + DL = 0Ah + 0ADC:332C: + CALL 3547h + _fmemcpy(ES:DI, DS:SI, 0x0F) ; CX = 0x0F ; REP MOVSB + ; Side effect: SI += 0x0F, DI += 0x0F + SI++ + IF DL > 0 THEN DL--, JMP 332Ch ; DEC DL ; JNZ 332Ch + 0ADC:3339: + DI += 0x50 ; Reminder: 2E1Eh + (0x10 * 0x0A) + 0x50 = 2F0Eh + DL = 0x0B + 0ADC:333E: + CALL 3547h + _fmemcpy(ES:DI, DS:SI, 0x05) ; CX = 0x05 ; REP MOVSB + ; Side effect: SI += 0x05, DI += 0x05 + SI++ + DI += 2 + IF DL > 0 THEN DL--, JMP 333Eh ; DEC DL ; JNZ 333Eh + 0ADC:334E: + JMP 3386h + 0ADC:3350: (AX == 0xFF) + DI = 2D2Eh ; Memory location that appears to hold function key row strings + DL = 0x1E + 0ADC:3355: + CALL 3547h + _fmemcpy(ES:DI, DS:SI, 0x0F) ; CX = 0x0F ; REP MOVSB + ; Side effect: SI += 0x0F, DI += 0x0F + SI++ + IF DL > 0 THEN DL--, JMP 3355h + 0ADC:3362: + DL = 0x0B + 0ADC:3364: + CALL 3547h + _fmemcpy(ES:DI, DS:SI, 0x05) ; CX = 0x05 ; REP MOVSB + ; Side effect: SI += 0x05, DI += 0x05 + DI += 2 + IF DL > 0 THEN DL--, JMP 3364h + 0ADC:3374: + DI = 0x2F86 + DL = 0x0F + 0ADC:3379: + CALL 3547h + _fmemcpy(ES:DI, DS:SI, 0x0F) ; CX = 0x0F ; REP MOVSB + SI++ + IF DL > 0 THEN DL--, JMP 3379h + 0ADC:3386: + IF BYTE PTR ES:[0111] == 0 THEN JMP 33ECh ; ES = DOS segment 60h + JMP 33ECh ; Wait... what? + 0ADC:3390: + (TODO) + + 0ADC:33EC: + IF AX < 0x29 JMP 33E3h ; CMP AX, 29h ; JC 33E3h + IF AX > 0x39 JMP 33E3h ; CMP AX, 39h ; JA 33E3h + IF AX == 0x39 JMP 3417h ; ... JE 3417h + 0ADC:33F8: + IF AX <= 0x32 JMP 3405h ; CMP AX, 32h ; JBE 3405h + IF AX != 0x38 JMP 33E3h ; CMP AX, 38h ; JNE 33E3h + 0ADC:3402: + AX = 0x28 + 0ADC:3405: + DI = ((AX - 0x29) * 16) + 0x2F86 + CX = 0x000F + JMP 33DEh + 0ADC:3417: + AX = 0x38 + PUSH DS, SI, DI + CALL 33F8h + POP DI, SI, DS + SI += 0x10 + DI += 0x10 + CX = 0x0A + 0ADC:342C: + PUSH CX, DS, SI, DI + AX = 0x33 - CX + CALL 33F8h + POP DI, SI + DI += 0x10 + SI += 0x10 + POP DS, CX + IF CX > 0 THEN CX--, JMP 342Ch ; LOOP 342Ch + 0ADC:3444: + return + 0ADC:3445: + DI = 307Ah + SI += 2 + AX = BX = 0 + _fmemcpy(ES:DI, DS:SI, 0x200) ; CX = 0x200 ; REP MOVSB + 0ADC:3454: + DI = 307Ah + CX = DI + CX += 0x200 + IF DI >= CX JMP 346Fh ; CMP DI, CX ; JNC 346Fh + IF BYTE PTR ES:[DI] == 0 JMP 346Fh + AL = BYTE PTR ES:[DI] ; AH = 0 at this point + DI += AX + JMP 345Dh + 0ADC:346F: + WORD PTR ES:[3076] = DI - 0x307A ; SUB DI, 307Ah ; MOV ES:[3076] DI + DI = 3078h + (TODO) + +-- + + 0ADC:355B: (CL=0Ah entry point) + IF WORD PTR DS:[1802] != 0 JMP 3563h + return + 0ADC:3563: (CL=0Ah and WORD PTR DS:[1802] == 0) + IF (BYTE PTR DS:[0037] & 8) != 0 JMP 357Eh + 0ADC:356A: + AH = 00h + INT 19h + CX = 0 + IF AH == 0 JMP 3579h + CX = 0xFFFF + 0ADC:3579: + WORD PTR DS:[05DB],CX (AX value to return to caller) + return + 0ADC:357E: + IF (AH & 0xF0) != 0 JMP 3597h + BYTE PTR DS:[0068],DH + PUSH DX + DL = (DL AND 0x0F) + BYTE PTR DS:[0069] = (BYTE PTR DS:[0069] & 0xF0) | DL + POP DX + JMP 35A4h + 0ADC:3597: + IF AH > 0x20 JMP 35A3h + IF WORD PTR DS:[17FA] != 0 JMP 35A4h + 0ADC:35A3: + return + 0ADC:35A4: + AH = (DL & 0x30) + BX = AH >> 3 + ES = WORD PTR DS:[1802] + DI = WORD PTR ES:[BX+0x40] + AH = AH | 7 + BX = 0 + IF (DH & 1) == 0 JMP 35C9h + BH = BH | 0x30 + 0ADC:35C9: + AL = (DL & 0x0F) + 1 + DH = ((DH & 0xFE) | 0x02) + 0ADC:35D5: + IF (AH & 0x30) != 0 JMP 35E1h + IF (AL & 0x08) != 0 JMP 35E1h + DH = DH | 1 + 0ADC:35E1: + CH = DH + CL = 37h + AL = AL - 2 + IF (CH & 0x04) != 0 JMP 35F1h + BH = BH | 0x40 + 0ADC:35F1: + DX = 0x0100 + IF WORD PTR [F800:7FFC] == 0 JMP 3605h (IF WORD at memory address 0xFFFFC == 0) + DX = DX + 1 + 0ADC:3605: + PUSH AX + INT 19h, RESULT = AH + POP AX + IF RESULT == 0 JMP 3614h + WORD PTR DS:[05DB] = 0xFFFF (returned to caller as AX) + return + 0ADC:3614: + WORD PTR DS:[05DB] = 0x0000 (returned to caller as AX) + return + +-- + + 0ADC:378E: (CL=10h entry point) + BX = WORD PTR DS:[05DB] caller's AX value + BX = (BX >> 8) (BX = caller's AH value) + ADDR = (BX * 4) + 3A7Ch + AX = WORD PTR CS:[ADDR] (0ADC:[ADDR]) + BX = WORD PTR CS:[ADDR+2] + CALL NEAR AX + return + +-- + + 0ADC:37A7: (CL=10h AH=00h entry point) + CL = DL + CALL NEAR BX (BX is 0A9C, no other case) + 0ADC:37AB: + return + +-- + + 0ADC:37AC: (CL=10h AH=01h entry point) + ES = WORD PTR DS:[05E1] ; caller's DS segment + BX = DX ; caller's DX register + 0ADC:37B2: + CL = BYTE PTR ES:[BX] + IF CL == 0x24 JMP 37C2h ; stop at '$' + PUSH BX + CALL A9Ch + POP BX + INC BX + JMP 37B2h + 0ADC:37C2: + return + +-- + + 0ADC:37C3: (CL=10h AH=02h entry point) + BYTE PTR DS:[011D],DL ; DL = caller's DL, assign to character attribute + WORD PTR DS:[013C],DX ; DX = caller's DX, assign to extended character attribute + return + +-- + + 0ADC:37CC: (CL=10h AH=03h entry point) + DX += 0x2020 ; DX = caller's DX add 0x20 to both bytes + swap(DL,DH) ; XCHG DL, DH + CX = DX + CALL WORD PTR CS:[BX] ; BX = 0x0AFA in all cases, which then feeds the "ESC =" handling code + return + +-- + + 0ADC:37D7: (CL=10h AH=04h entry point, where BX = 0x0B90 aka the "ESC D" handler) + (CL=10h AH=05h entry point, where BX = 0x0B99 aka the "ESC M" handler) + CALL WORD PTR CS:[BX] ; BX = 0x0B90 or BX = 0x0B99 + return + +-- + + 0ADC:37DA: (CL=10h AH=06h entry point, where BX = 0x0C52 aka the "ESC [ A" handler) + (CL=10h AH=07h entry point, where BX = 0x0C75 aka the "ESC [ B" handler) + (CL=10h AH=08h entry point, where BX = 0x0C9C aka the "ESC [ C" handler) + (CL=10h AH=09h entry point, where BX = 0x0CC3 aka the "ESC [ D" handler) + (CL=10h AH=0Ch entry point, where BX = 0x0E4E aka the "ESC [ L" handler) + (CL=10h AH=0Dh entry point, where BX = 0x0E72 aka the "ESC [ M" handler) + DH = 0 + CX = DX + CALL WORD PTR CS:[BX] + return + +-- + + 0ADC:37E1: (CL=10h AH=0Ah entry point, where BX = 0x0D2E) + (CL=10h AH=0Bh entry point, where BX = 0x0D72) + DH = 0 + AX = DX + CALL WORD PTR CS:[BX] + return + +-- + + 0ADC:3A5C array of WORD values, offsets of procedures for each value of CL. + + 0ADC:3A5C table contents. + Note the INT DCh code maps: + CL = 0x09..0x15 to table index 0x00..0x0C + CL = 0x80..0x82 to table index 0x0D..0x0F + + . . . . . . . . + 0ADC:00003A5C B8 3A 5B 35 A4 31 A5 31 DF 32 1B 36 44 37 8E 37 + . . . . . . . . |END + 0ADC:00003A6C F0 37 6E 38 F7 38 85 3B 52 3C 27 39 B5 39 10 3A + + CL = 0x09 0x3AB8 + CL = 0x0A 0x355B + CL = 0x0B 0x31A4 + CL = 0x0C 0x31A5 + CL = 0x0D 0x32DF + CL = 0x0E 0x361B + CL = 0x0F 0x3744 + CL = 0x10 0x378E + CL = 0x11 0x37F0 + CL = 0x12 0x386E + CL = 0x13 0x38F7 + CL = 0x14 0x3B85 + CL = 0x15 0x3C52 + CL = 0x80 0x3927 + CL = 0x81 0x39B5 + CL = 0x82 0x3A10 + +-- + + 0ADC:3A7C array of WORD value pairs (address, parameter). NOTE: Lack of range checking! + Lookup table for CL=10h, index by AH + + . . . . + 0ADC:00003A7C A7 37 9C 0A AC 37 00 00 C3 37 00 00 CC 37 FA 0A .7...7...7...7.. + . . . . + 0ADC:00003A8C D7 37 90 0B D7 37 99 0B DA 37 52 0C DA 37 75 0C .7...7...7R..7u. + . . . . + 0ADC:00003A9C DA 37 9C 0C DA 37 C3 0C E1 37 2E 0D E1 37 72 0D .7...7...7...7r. + . . . . |END? + 0ADC:00003AAC DA 37 4E 0E DA 37 72 0E E8 37 2D 0B 8B 16 E3 05 .7N..7r..7-..... + ? ? ? ? + 0ADC:00003ABC A1 DB 05 3D 00 00 74 10 3D 01 00 74 20 3D 10 00 ...=..t.=..t =.. + ? ? ? ? + 0ADC:00003ACC 74 4E 3D 11 00 74 49 C3 8E 06 E1 05 8B FA 2E 8E tN=..tI......... + + 0ADC:3A7C Call table (address, param) + + Calling convention on entry: AX = subroutine address, BX = param + + AH = 0x00 0x37A7 0x0A9C ; 0ADC:00003A7C + AH = 0x01 0x37AC 0x0000 + AH = 0x02 0x37C3 0x0000 + AH = 0x03 0x37CC 0x0AFA + AH = 0x04 0x37D7 0x0B90 ; 0ADC:00003A8C + AH = 0x05 0x37D7 0x0B99 + AH = 0x06 0x37DA 0x0C52 + AH = 0x07 0x37DA 0x0C75 + AH = 0x08 0x37DA 0x0C9C ; 0ADC:00003A9C + AH = 0x09 0x37DA 0x0CC3 + AH = 0x0A 0x37E1 0x0D2E + AH = 0x0B 0x37E1 0x0D72 + AH = 0x0C 0x37DA 0x0E4E ; 0ADC:00003AAC + AH = 0x0D 0x37DA 0x0E72 + AH = 0x0E 0x37E8 0x0B2D + AH = 0x0F 0x168B 0x05E3 + +-- + + 0ADC:3AB8: (INT DCh CL=0x09) [DS=0x0060 DOS SEGMENT] + DX = WORD PTR DS:[05E3] (caller DX) + AX = WORD PTR DS:[05DB] (caller AX) + IF AX == 0x0000 JMP 3AD4h + IF AX == 0x0001 JMP 3AE9h + IF AX == 0x0010 JMP 3B1Ch + IF AX == 0x0011 JMP 3B1Ch + 0ADC:3AD3: + return + 0ADC:3AD4: (INT DCh CL=0x09 AX=0x0000) + ES = WORD PTR DS:[05E1] (caller DS) + DI = DX (caller DX) + DS = WORD PTR CS:[0030] (DOS segment 60h) + SI = 1DBBh + _fmemcpy(ES:DI,DS:SI,8) + return + 0ADC:3AE9: (INT DCh CL=0x09 AX=0x0001) + WORD PTR DS:[05DB] = 0xFFFF (caller AX set to 0xFFFF) + IF BYTE PTR DS:[1DC4] == 0 JMP 3B1Bh + DL = DL AND 0x0F + BX = 26Ah + CX = (WORD)BYTE PTR DS:[1DC4] * 4 + 0ADC:3B06: + IF BYTE PTR DS:[BX+2] != DL JMP 3B15h + BYTE PTR DS:[BX+1] = 2 + WORD PTR DS:[05DB] = 0x0000 (caller AX set to 0x0000) + BX += WORD PTR DS:[0268] + IF CX > 0 THEN CX--, JMP 3B06h + 0ADC:3B1B: + return + 0ADC:3B1C: (INT DCh CL=0x09 AX=0x0010 or AX=0x0011) + WORD PTR DS:[05DB] = 0xFFFF (caller AX set to 0xFFFF) + IF BYTE PTR DS:[1DC4] == 0 JMP 3B84h + DX = DX AND 0xFF + CX = 0x1A + BX = 2C86h + 0ADC:3B31: + IF BYTE PTR DS:[BX+1] == DL JMP 3B3Fh + BX += 2 + DH += 1 + IF CX > 0 THEN CX--, JMP 3B31h + JMP 3B84h + 0ADC:3B3F: + IF (BYTE PTR DS:[BX] AND 1) == 0 JMP 3B84h + BYTE PTR DS:[0136] = DH (set last access drive number) + IF AX != 0x0010 JMP 3B52h + CALL 5B07h + JMP 3B69h + 0ADC:3B52: + WORD PTR DS:[05DB] = 0x0001 (caller AX set to 0x0001) + BX = DL & 0x0F + IF BYTE PTR [BX+03E9h] == 0 JMP 3B84h + CALL 5B4Bh, RESULT = CARRY FLAG + 0ADC:3B69: + WORD PTR DS:[05DB] = 0x0000 + IF RESULT == 0 (CF=0) JMP 3B84h + WORD PTR DS:[05DB] = 0x0002 + IF BYTE PTR DS:[025B] == 2 JMP 3B84h + WORD PTR DS:[05DB] = 0x0003 + 0ADC:3B84: + return + +-- + + Called from 0ADC:3180 (near) + + 0ADC:4080: + CLI + PUSH DS + DS = DOS kernel segment 60h from 0ADC:0030 + BYTE PTR DS:[00B4] = 0 + 0ADC:408C: + IF BYTE PTR DS:[00A5] != 01h JMP 40ACh + 0ADC:4093: + IF BYTE PTR DS:[00A6] == 01h JMP 40A2h + 0ADC:409A: + STI + PUSH AX + CALL 40AFh + POP AX + POP DS + return + 0ADC:40A2: + BYTE PTR DS:[00A5] = 0x00 + BYTE PTR DS:[00A6] = 0x00 + 0ADC:40AC: + STI + POP DS + return + 0ADC:40AF: + IF WORD PTR DS:[2A7C] == 0h JMP 40C6h + 0ADC:40B6: + PUSH DS + WORD PTR DS:[2A7A] = 0x18 + CALL FAR 0060h:3C6Fh + NOP + NOP + NOP + POP DS + 0ADC:40C6: + return + diff --git a/experiments/cairo/cairo1.cpp b/experiments/cairo/cairo1.cpp index a80c58b74..a0603c730 100644 --- a/experiments/cairo/cairo1.cpp +++ b/experiments/cairo/cairo1.cpp @@ -1,133 +1,133 @@ - -#include - -#include - -int main() { - double ang = 0,angv = (M_PI * 2) / 360,angstep = (M_PI * 2) / 3; - - if (SDL_Init(SDL_INIT_VIDEO) != 0) - return 1; - - SDL_Window *window = SDL_CreateWindow("Cairo",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,320,240,SDL_WINDOW_SHOWN); - if (window == NULL) - return 1; - - SDL_Surface *surface = SDL_GetWindowSurface(window); - if (surface == NULL) - return 1; - - cairo_format_t csurffmt = CAIRO_FORMAT_INVALID; - if (surface->format->BytesPerPixel == 4) { - csurffmt = CAIRO_FORMAT_ARGB32; - } - else if (surface->format->BytesPerPixel == 3) { - csurffmt = CAIRO_FORMAT_RGB24; - } - else if (surface->format->BytesPerPixel == 2) { - csurffmt = CAIRO_FORMAT_RGB16_565; - } - else { - return 1; - } - - cairo_surface_t* csurf = cairo_image_surface_create(csurffmt, 320, 240); - if (csurf == NULL) - return 1; - - cairo_t* cactx = cairo_create(csurf); - if (cactx == NULL) - return 1; - - bool run = true; - while (run) { - SDL_Event ev; - - while (SDL_PollEvent(&ev)) { - switch (ev.type) { - case SDL_WINDOWEVENT: - switch (ev.window.event) { - case SDL_WINDOWEVENT_CLOSE: - run = false; - break; - } - break; - case SDL_KEYDOWN: - switch (ev.key.keysym.sym) { - case SDLK_ESCAPE: - run = false; - break; - } - break; - } - } - - // clear - cairo_reset_clip(cactx); - cairo_set_source_rgb(cactx,0.25,0.25,0.25); - cairo_paint(cactx); - - // draw - cairo_set_line_width(cactx,8.0); - - { - unsigned int step=0; - - for (double a=0;a < (M_PI * 2);a += angstep,step++) { - double x = 160.0 + cos(a+ang)*100.0; - double y = 120.0 + sin(a+ang)*100.0; - - if (step == 0) - cairo_move_to(cactx,x,y); - else - cairo_line_to(cactx,x,y); - } - - cairo_close_path(cactx); - ang += angv; - } - - cairo_set_source_rgb(cactx,1.0,1.0,1.0); - cairo_fill_preserve(cactx); - - cairo_set_source_rgb(cactx,0.0,0.0,1.0); - cairo_stroke(cactx); - - // copy Cairo output to display - cairo_surface_flush(csurf); - { - unsigned char *cbuf = cairo_image_surface_get_data(csurf); - int width = cairo_image_surface_get_width(csurf); - int height = cairo_image_surface_get_height(csurf); - int stride = cairo_image_surface_get_stride(csurf); - - SDL_LockSurface(surface); - - if (cbuf != NULL && surface->pixels != NULL) { - if (width > surface->w) width = surface->w; - if (height > surface->h) height = surface->h; - - for (int y=0;y < height;y++) { - unsigned char *d = (unsigned char*)(surface->pixels) + (surface->pitch * y); - unsigned char *s = (unsigned char*)(cbuf) + (stride * y); - memcpy(d,s,width*surface->format->BytesPerPixel); - } - } - - SDL_UnlockSurface(surface); - SDL_UpdateWindowSurface(window); - } - - SDL_Delay(1000 / 30); - } - - cairo_surface_flush(csurf); - cairo_destroy(cactx); - cairo_surface_destroy(csurf); - - // do not free surface, owned by dinwo - SDL_DestroyWindow(window); - SDL_Quit(); - return 0; -} - + +#include + +#include + +int main() { + double ang = 0,angv = (M_PI * 2) / 360,angstep = (M_PI * 2) / 3; + + if (SDL_Init(SDL_INIT_VIDEO) != 0) + return 1; + + SDL_Window *window = SDL_CreateWindow("Cairo",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,320,240,SDL_WINDOW_SHOWN); + if (window == NULL) + return 1; + + SDL_Surface *surface = SDL_GetWindowSurface(window); + if (surface == NULL) + return 1; + + cairo_format_t csurffmt = CAIRO_FORMAT_INVALID; + if (surface->format->BytesPerPixel == 4) { + csurffmt = CAIRO_FORMAT_ARGB32; + } + else if (surface->format->BytesPerPixel == 3) { + csurffmt = CAIRO_FORMAT_RGB24; + } + else if (surface->format->BytesPerPixel == 2) { + csurffmt = CAIRO_FORMAT_RGB16_565; + } + else { + return 1; + } + + cairo_surface_t* csurf = cairo_image_surface_create(csurffmt, 320, 240); + if (csurf == NULL) + return 1; + + cairo_t* cactx = cairo_create(csurf); + if (cactx == NULL) + return 1; + + bool run = true; + while (run) { + SDL_Event ev; + + while (SDL_PollEvent(&ev)) { + switch (ev.type) { + case SDL_WINDOWEVENT: + switch (ev.window.event) { + case SDL_WINDOWEVENT_CLOSE: + run = false; + break; + } + break; + case SDL_KEYDOWN: + switch (ev.key.keysym.sym) { + case SDLK_ESCAPE: + run = false; + break; + } + break; + } + } + + // clear + cairo_reset_clip(cactx); + cairo_set_source_rgb(cactx,0.25,0.25,0.25); + cairo_paint(cactx); + + // draw + cairo_set_line_width(cactx,8.0); + + { + unsigned int step=0; + + for (double a=0;a < (M_PI * 2);a += angstep,step++) { + double x = 160.0 + cos(a+ang)*100.0; + double y = 120.0 + sin(a+ang)*100.0; + + if (step == 0) + cairo_move_to(cactx,x,y); + else + cairo_line_to(cactx,x,y); + } + + cairo_close_path(cactx); + ang += angv; + } + + cairo_set_source_rgb(cactx,1.0,1.0,1.0); + cairo_fill_preserve(cactx); + + cairo_set_source_rgb(cactx,0.0,0.0,1.0); + cairo_stroke(cactx); + + // copy Cairo output to display + cairo_surface_flush(csurf); + { + unsigned char *cbuf = cairo_image_surface_get_data(csurf); + int width = cairo_image_surface_get_width(csurf); + int height = cairo_image_surface_get_height(csurf); + int stride = cairo_image_surface_get_stride(csurf); + + SDL_LockSurface(surface); + + if (cbuf != NULL && surface->pixels != NULL) { + if (width > surface->w) width = surface->w; + if (height > surface->h) height = surface->h; + + for (int y=0;y < height;y++) { + unsigned char *d = (unsigned char*)(surface->pixels) + (surface->pitch * y); + unsigned char *s = (unsigned char*)(cbuf) + (stride * y); + memcpy(d,s,width*surface->format->BytesPerPixel); + } + } + + SDL_UnlockSurface(surface); + SDL_UpdateWindowSurface(window); + } + + SDL_Delay(1000 / 30); + } + + cairo_surface_flush(csurf); + cairo_destroy(cactx); + cairo_surface_destroy(csurf); + + // do not free surface, owned by dinwo + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; +} + diff --git a/experiments/cairo/cairo2.cpp b/experiments/cairo/cairo2.cpp index 33aae43ed..cfd233bdb 100644 --- a/experiments/cairo/cairo2.cpp +++ b/experiments/cairo/cairo2.cpp @@ -1,153 +1,153 @@ - -#include - -#include - -int main() { - double ang = 0,angv = (M_PI * 2) / 360,angstep = (M_PI * 2) / 4; - - if (SDL_Init(SDL_INIT_VIDEO) != 0) - return 1; - - SDL_Window *window = SDL_CreateWindow("Cairo",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,320,240,SDL_WINDOW_SHOWN); - if (window == NULL) - return 1; - - SDL_Surface *surface = SDL_GetWindowSurface(window); - if (surface == NULL) - return 1; - - cairo_format_t csurffmt = CAIRO_FORMAT_INVALID; - if (surface->format->BytesPerPixel == 4) { - csurffmt = CAIRO_FORMAT_ARGB32; - } - else if (surface->format->BytesPerPixel == 3) { - csurffmt = CAIRO_FORMAT_RGB24; - } - else if (surface->format->BytesPerPixel == 2) { - csurffmt = CAIRO_FORMAT_RGB16_565; - } - else { - return 1; - } - - cairo_surface_t* csurf = cairo_image_surface_create(csurffmt, 320, 240); - if (csurf == NULL) - return 1; - - cairo_t* cactx = cairo_create(csurf); - if (cactx == NULL) - return 1; - - bool run = true; - while (run) { - SDL_Event ev; - - while (SDL_PollEvent(&ev)) { - switch (ev.type) { - case SDL_WINDOWEVENT: - switch (ev.window.event) { - case SDL_WINDOWEVENT_CLOSE: - run = false; - break; - } - break; - case SDL_KEYDOWN: - switch (ev.key.keysym.sym) { - case SDLK_ESCAPE: - run = false; - break; - } - break; - } - } - - // clear - cairo_reset_clip(cactx); - cairo_set_source_rgb(cactx,0.25,0.25,0.25); - cairo_paint(cactx); - - // draw - cairo_set_line_width(cactx,8.0); - - { - unsigned int step=0; - - for (double a=0;a < (M_PI * 2);a += angstep,step++) { - double x = 160.0 + cos(a+ang)*100.0 + 20; - double y = 120.0 + sin(a+ang)*100.0 + 20; - - if (step == 0) - cairo_move_to(cactx,x,y); - else - cairo_line_to(cactx,x,y); - } - - cairo_close_path(cactx); - ang += angv; - } - - cairo_set_source_rgba(cactx,0,0,0,0.5); - cairo_fill(cactx); - - { - unsigned int step=0; - - for (double a=0;a < (M_PI * 2);a += angstep,step++) { - double x = 160.0 + cos(a+ang)*100.0; - double y = 120.0 + sin(a+ang)*100.0; - - if (step == 0) - cairo_move_to(cactx,x,y); - else - cairo_line_to(cactx,x,y); - } - - cairo_close_path(cactx); - ang += angv; - } - - cairo_set_source_rgb(cactx,1.0,1.0,1.0); - cairo_fill_preserve(cactx); - - cairo_set_source_rgb(cactx,0.0,0.0,1.0); - cairo_stroke(cactx); - - // copy Cairo output to display - cairo_surface_flush(csurf); - { - unsigned char *cbuf = cairo_image_surface_get_data(csurf); - int width = cairo_image_surface_get_width(csurf); - int height = cairo_image_surface_get_height(csurf); - int stride = cairo_image_surface_get_stride(csurf); - - SDL_LockSurface(surface); - - if (cbuf != NULL && surface->pixels != NULL) { - if (width > surface->w) width = surface->w; - if (height > surface->h) height = surface->h; - - for (int y=0;y < height;y++) { - unsigned char *d = (unsigned char*)(surface->pixels) + (surface->pitch * y); - unsigned char *s = (unsigned char*)(cbuf) + (stride * y); - memcpy(d,s,width*surface->format->BytesPerPixel); - } - } - - SDL_UnlockSurface(surface); - SDL_UpdateWindowSurface(window); - } - - SDL_Delay(1000 / 30); - } - - cairo_surface_flush(csurf); - cairo_destroy(cactx); - cairo_surface_destroy(csurf); - - // do not free surface, owned by dinwo - SDL_DestroyWindow(window); - SDL_Quit(); - return 0; -} - + +#include + +#include + +int main() { + double ang = 0,angv = (M_PI * 2) / 360,angstep = (M_PI * 2) / 4; + + if (SDL_Init(SDL_INIT_VIDEO) != 0) + return 1; + + SDL_Window *window = SDL_CreateWindow("Cairo",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,320,240,SDL_WINDOW_SHOWN); + if (window == NULL) + return 1; + + SDL_Surface *surface = SDL_GetWindowSurface(window); + if (surface == NULL) + return 1; + + cairo_format_t csurffmt = CAIRO_FORMAT_INVALID; + if (surface->format->BytesPerPixel == 4) { + csurffmt = CAIRO_FORMAT_ARGB32; + } + else if (surface->format->BytesPerPixel == 3) { + csurffmt = CAIRO_FORMAT_RGB24; + } + else if (surface->format->BytesPerPixel == 2) { + csurffmt = CAIRO_FORMAT_RGB16_565; + } + else { + return 1; + } + + cairo_surface_t* csurf = cairo_image_surface_create(csurffmt, 320, 240); + if (csurf == NULL) + return 1; + + cairo_t* cactx = cairo_create(csurf); + if (cactx == NULL) + return 1; + + bool run = true; + while (run) { + SDL_Event ev; + + while (SDL_PollEvent(&ev)) { + switch (ev.type) { + case SDL_WINDOWEVENT: + switch (ev.window.event) { + case SDL_WINDOWEVENT_CLOSE: + run = false; + break; + } + break; + case SDL_KEYDOWN: + switch (ev.key.keysym.sym) { + case SDLK_ESCAPE: + run = false; + break; + } + break; + } + } + + // clear + cairo_reset_clip(cactx); + cairo_set_source_rgb(cactx,0.25,0.25,0.25); + cairo_paint(cactx); + + // draw + cairo_set_line_width(cactx,8.0); + + { + unsigned int step=0; + + for (double a=0;a < (M_PI * 2);a += angstep,step++) { + double x = 160.0 + cos(a+ang)*100.0 + 20; + double y = 120.0 + sin(a+ang)*100.0 + 20; + + if (step == 0) + cairo_move_to(cactx,x,y); + else + cairo_line_to(cactx,x,y); + } + + cairo_close_path(cactx); + ang += angv; + } + + cairo_set_source_rgba(cactx,0,0,0,0.5); + cairo_fill(cactx); + + { + unsigned int step=0; + + for (double a=0;a < (M_PI * 2);a += angstep,step++) { + double x = 160.0 + cos(a+ang)*100.0; + double y = 120.0 + sin(a+ang)*100.0; + + if (step == 0) + cairo_move_to(cactx,x,y); + else + cairo_line_to(cactx,x,y); + } + + cairo_close_path(cactx); + ang += angv; + } + + cairo_set_source_rgb(cactx,1.0,1.0,1.0); + cairo_fill_preserve(cactx); + + cairo_set_source_rgb(cactx,0.0,0.0,1.0); + cairo_stroke(cactx); + + // copy Cairo output to display + cairo_surface_flush(csurf); + { + unsigned char *cbuf = cairo_image_surface_get_data(csurf); + int width = cairo_image_surface_get_width(csurf); + int height = cairo_image_surface_get_height(csurf); + int stride = cairo_image_surface_get_stride(csurf); + + SDL_LockSurface(surface); + + if (cbuf != NULL && surface->pixels != NULL) { + if (width > surface->w) width = surface->w; + if (height > surface->h) height = surface->h; + + for (int y=0;y < height;y++) { + unsigned char *d = (unsigned char*)(surface->pixels) + (surface->pitch * y); + unsigned char *s = (unsigned char*)(cbuf) + (stride * y); + memcpy(d,s,width*surface->format->BytesPerPixel); + } + } + + SDL_UnlockSurface(surface); + SDL_UpdateWindowSurface(window); + } + + SDL_Delay(1000 / 30); + } + + cairo_surface_flush(csurf); + cairo_destroy(cactx); + cairo_surface_destroy(csurf); + + // do not free surface, owned by dinwo + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; +} + diff --git a/experiments/cairo/cairo3.cpp b/experiments/cairo/cairo3.cpp index ab6fc8ace..fc4462524 100644 --- a/experiments/cairo/cairo3.cpp +++ b/experiments/cairo/cairo3.cpp @@ -1,147 +1,147 @@ - -#include - -#include - -int main() { - double ang = 0,angv = (M_PI * 2) / 360,angstep = (M_PI * 2) / 4; - - if (SDL_Init(SDL_INIT_VIDEO) != 0) - return 1; - - SDL_Window *window = SDL_CreateWindow("Cairo",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,320,240,SDL_WINDOW_SHOWN); - if (window == NULL) - return 1; - - SDL_Surface *surface = SDL_GetWindowSurface(window); - if (surface == NULL) - return 1; - - cairo_format_t csurffmt = CAIRO_FORMAT_INVALID; - if (surface->format->BytesPerPixel == 4) { - csurffmt = CAIRO_FORMAT_ARGB32; - } - else if (surface->format->BytesPerPixel == 3) { - csurffmt = CAIRO_FORMAT_RGB24; - } - else if (surface->format->BytesPerPixel == 2) { - csurffmt = CAIRO_FORMAT_RGB16_565; - } - else { - return 1; - } - - if (surface->pixels == NULL || SDL_MUSTLOCK(surface)) { - fprintf(stderr,"Surface not appropriate for Cairo\n"); - return 1; - } - - int csurfpitch = cairo_format_stride_for_width(csurffmt, 320); - fprintf(stderr,"Cairo stride %d\n",csurfpitch); - fprintf(stderr,"SDL stride %d\n",surface->pitch); - - if (csurfpitch != surface->pitch) { - fprintf(stderr,"Sorry, Cairo and SDL do not agree\n"); - return 1; - } - - cairo_surface_t* csurf = cairo_image_surface_create_for_data((unsigned char*)surface->pixels, csurffmt, 320, 240, csurfpitch); - if (csurf == NULL) - return 1; - - cairo_t* cactx = cairo_create(csurf); - if (cactx == NULL) - return 1; - - bool run = true; - while (run) { - SDL_Event ev; - - while (SDL_PollEvent(&ev)) { - switch (ev.type) { - case SDL_WINDOWEVENT: - switch (ev.window.event) { - case SDL_WINDOWEVENT_CLOSE: - run = false; - break; - } - break; - case SDL_KEYDOWN: - switch (ev.key.keysym.sym) { - case SDLK_ESCAPE: - run = false; - break; - } - break; - } - } - - // clear - cairo_reset_clip(cactx); - cairo_set_source_rgb(cactx,0.25,0.25,0.25); - cairo_paint(cactx); - - // draw - cairo_set_line_width(cactx,8.0); - - { - unsigned int step=0; - - for (double a=0;a < (M_PI * 2);a += angstep,step++) { - double x = 160.0 + cos(a+ang)*100.0 + 20; - double y = 120.0 + sin(a+ang)*100.0 + 20; - - if (step == 0) - cairo_move_to(cactx,x,y); - else - cairo_line_to(cactx,x,y); - } - - cairo_close_path(cactx); - ang += angv; - } - - cairo_set_source_rgba(cactx,0,0,0,0.5); - cairo_fill(cactx); - - { - unsigned int step=0; - - for (double a=0;a < (M_PI * 2);a += angstep,step++) { - double x = 160.0 + cos(a+ang)*100.0; - double y = 120.0 + sin(a+ang)*100.0; - - if (step == 0) - cairo_move_to(cactx,x,y); - else - cairo_line_to(cactx,x,y); - } - - cairo_close_path(cactx); - ang += angv; - } - - cairo_set_source_rgb(cactx,1.0,1.0,1.0); - cairo_fill_preserve(cactx); - - cairo_set_source_rgb(cactx,0.0,0.0,1.0); - cairo_stroke(cactx); - - // copy Cairo output to display - cairo_surface_flush(csurf); - - SDL_UpdateWindowSurface(window); - - SDL_Delay(1000 / 30); - } - - cairo_surface_flush(csurf); - cairo_destroy(cactx); - cairo_surface_destroy(csurf); - - // do not free surface, owned by dinwo - SDL_DestroyWindow(window); - SDL_Quit(); - return 0; -} - + +#include + +#include + +int main() { + double ang = 0,angv = (M_PI * 2) / 360,angstep = (M_PI * 2) / 4; + + if (SDL_Init(SDL_INIT_VIDEO) != 0) + return 1; + + SDL_Window *window = SDL_CreateWindow("Cairo",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,320,240,SDL_WINDOW_SHOWN); + if (window == NULL) + return 1; + + SDL_Surface *surface = SDL_GetWindowSurface(window); + if (surface == NULL) + return 1; + + cairo_format_t csurffmt = CAIRO_FORMAT_INVALID; + if (surface->format->BytesPerPixel == 4) { + csurffmt = CAIRO_FORMAT_ARGB32; + } + else if (surface->format->BytesPerPixel == 3) { + csurffmt = CAIRO_FORMAT_RGB24; + } + else if (surface->format->BytesPerPixel == 2) { + csurffmt = CAIRO_FORMAT_RGB16_565; + } + else { + return 1; + } + + if (surface->pixels == NULL || SDL_MUSTLOCK(surface)) { + fprintf(stderr,"Surface not appropriate for Cairo\n"); + return 1; + } + + int csurfpitch = cairo_format_stride_for_width(csurffmt, 320); + fprintf(stderr,"Cairo stride %d\n",csurfpitch); + fprintf(stderr,"SDL stride %d\n",surface->pitch); + + if (csurfpitch != surface->pitch) { + fprintf(stderr,"Sorry, Cairo and SDL do not agree\n"); + return 1; + } + + cairo_surface_t* csurf = cairo_image_surface_create_for_data((unsigned char*)surface->pixels, csurffmt, 320, 240, csurfpitch); + if (csurf == NULL) + return 1; + + cairo_t* cactx = cairo_create(csurf); + if (cactx == NULL) + return 1; + + bool run = true; + while (run) { + SDL_Event ev; + + while (SDL_PollEvent(&ev)) { + switch (ev.type) { + case SDL_WINDOWEVENT: + switch (ev.window.event) { + case SDL_WINDOWEVENT_CLOSE: + run = false; + break; + } + break; + case SDL_KEYDOWN: + switch (ev.key.keysym.sym) { + case SDLK_ESCAPE: + run = false; + break; + } + break; + } + } + + // clear + cairo_reset_clip(cactx); + cairo_set_source_rgb(cactx,0.25,0.25,0.25); + cairo_paint(cactx); + + // draw + cairo_set_line_width(cactx,8.0); + + { + unsigned int step=0; + + for (double a=0;a < (M_PI * 2);a += angstep,step++) { + double x = 160.0 + cos(a+ang)*100.0 + 20; + double y = 120.0 + sin(a+ang)*100.0 + 20; + + if (step == 0) + cairo_move_to(cactx,x,y); + else + cairo_line_to(cactx,x,y); + } + + cairo_close_path(cactx); + ang += angv; + } + + cairo_set_source_rgba(cactx,0,0,0,0.5); + cairo_fill(cactx); + + { + unsigned int step=0; + + for (double a=0;a < (M_PI * 2);a += angstep,step++) { + double x = 160.0 + cos(a+ang)*100.0; + double y = 120.0 + sin(a+ang)*100.0; + + if (step == 0) + cairo_move_to(cactx,x,y); + else + cairo_line_to(cactx,x,y); + } + + cairo_close_path(cactx); + ang += angv; + } + + cairo_set_source_rgb(cactx,1.0,1.0,1.0); + cairo_fill_preserve(cactx); + + cairo_set_source_rgb(cactx,0.0,0.0,1.0); + cairo_stroke(cactx); + + // copy Cairo output to display + cairo_surface_flush(csurf); + + SDL_UpdateWindowSurface(window); + + SDL_Delay(1000 / 30); + } + + cairo_surface_flush(csurf); + cairo_destroy(cactx); + cairo_surface_destroy(csurf); + + // do not free surface, owned by dinwo + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; +} + diff --git a/experiments/cairo/cairo4.cpp b/experiments/cairo/cairo4.cpp index a072548f4..3bc1b56bd 100644 --- a/experiments/cairo/cairo4.cpp +++ b/experiments/cairo/cairo4.cpp @@ -1,147 +1,147 @@ - -#include - -#include - -int main() { - double ang = 0,angv = (M_PI * 2) / 360,angstep = (M_PI * 2) / 4; - - if (SDL_Init(SDL_INIT_VIDEO) != 0) - return 1; - - SDL_Window *window = SDL_CreateWindow("Cairo",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,640,480,SDL_WINDOW_SHOWN); - if (window == NULL) - return 1; - - SDL_Surface *surface = SDL_GetWindowSurface(window); - if (surface == NULL) - return 1; - - cairo_format_t csurffmt = CAIRO_FORMAT_INVALID; - if (surface->format->BytesPerPixel == 4) { - csurffmt = CAIRO_FORMAT_ARGB32; - } - else if (surface->format->BytesPerPixel == 3) { - csurffmt = CAIRO_FORMAT_RGB24; - } - else if (surface->format->BytesPerPixel == 2) { - csurffmt = CAIRO_FORMAT_RGB16_565; - } - else { - return 1; - } - - if (surface->pixels == NULL || SDL_MUSTLOCK(surface)) { - fprintf(stderr,"Surface not appropriate for Cairo\n"); - return 1; - } - - int csurfpitch = cairo_format_stride_for_width(csurffmt, 640); - fprintf(stderr,"Cairo stride %d\n",csurfpitch); - fprintf(stderr,"SDL stride %d\n",surface->pitch); - - if (csurfpitch != surface->pitch) { - fprintf(stderr,"Sorry, Cairo and SDL do not agree\n"); - return 1; - } - - cairo_surface_t* csurf = cairo_image_surface_create_for_data((unsigned char*)surface->pixels, csurffmt, 640, 480, csurfpitch); - if (csurf == NULL) - return 1; - - cairo_t* cactx = cairo_create(csurf); - if (cactx == NULL) - return 1; - - bool run = true; - while (run) { - SDL_Event ev; - - while (SDL_PollEvent(&ev)) { - switch (ev.type) { - case SDL_WINDOWEVENT: - switch (ev.window.event) { - case SDL_WINDOWEVENT_CLOSE: - run = false; - break; - } - break; - case SDL_KEYDOWN: - switch (ev.key.keysym.sym) { - case SDLK_ESCAPE: - run = false; - break; - } - break; - } - } - - // clear - cairo_reset_clip(cactx); - cairo_set_source_rgb(cactx,0.25,0.25,0.25); - cairo_paint(cactx); - - // draw - cairo_set_line_width(cactx,8.0); - - { - unsigned int step=0; - - for (double a=0;a < (M_PI * 2);a += angstep,step++) { - double x = 320.0 + cos(a+ang)*200.0 + 40; - double y = 240.0 + sin(a+ang)*200.0 + 40; - - if (step == 0) - cairo_move_to(cactx,x,y); - else - cairo_line_to(cactx,x,y); - } - - cairo_close_path(cactx); - ang += angv; - } - - cairo_set_source_rgba(cactx,0,0,0,0.5); - cairo_fill(cactx); - - { - unsigned int step=0; - - for (double a=0;a < (M_PI * 2);a += angstep,step++) { - double x = 320.0 + cos(a+ang)*200.0; - double y = 240.0 + sin(a+ang)*200.0; - - if (step == 0) - cairo_move_to(cactx,x,y); - else - cairo_line_to(cactx,x,y); - } - - cairo_close_path(cactx); - ang += angv; - } - - cairo_set_source_rgb(cactx,1.0,1.0,1.0); - cairo_fill_preserve(cactx); - - cairo_set_source_rgb(cactx,0.0,0.0,1.0); - cairo_stroke(cactx); - - // copy Cairo output to display - cairo_surface_flush(csurf); - - SDL_UpdateWindowSurface(window); - - SDL_Delay(1000 / 30); - } - - cairo_surface_flush(csurf); - cairo_destroy(cactx); - cairo_surface_destroy(csurf); - - // do not free surface, owned by dinwo - SDL_DestroyWindow(window); - SDL_Quit(); - return 0; -} - + +#include + +#include + +int main() { + double ang = 0,angv = (M_PI * 2) / 360,angstep = (M_PI * 2) / 4; + + if (SDL_Init(SDL_INIT_VIDEO) != 0) + return 1; + + SDL_Window *window = SDL_CreateWindow("Cairo",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,640,480,SDL_WINDOW_SHOWN); + if (window == NULL) + return 1; + + SDL_Surface *surface = SDL_GetWindowSurface(window); + if (surface == NULL) + return 1; + + cairo_format_t csurffmt = CAIRO_FORMAT_INVALID; + if (surface->format->BytesPerPixel == 4) { + csurffmt = CAIRO_FORMAT_ARGB32; + } + else if (surface->format->BytesPerPixel == 3) { + csurffmt = CAIRO_FORMAT_RGB24; + } + else if (surface->format->BytesPerPixel == 2) { + csurffmt = CAIRO_FORMAT_RGB16_565; + } + else { + return 1; + } + + if (surface->pixels == NULL || SDL_MUSTLOCK(surface)) { + fprintf(stderr,"Surface not appropriate for Cairo\n"); + return 1; + } + + int csurfpitch = cairo_format_stride_for_width(csurffmt, 640); + fprintf(stderr,"Cairo stride %d\n",csurfpitch); + fprintf(stderr,"SDL stride %d\n",surface->pitch); + + if (csurfpitch != surface->pitch) { + fprintf(stderr,"Sorry, Cairo and SDL do not agree\n"); + return 1; + } + + cairo_surface_t* csurf = cairo_image_surface_create_for_data((unsigned char*)surface->pixels, csurffmt, 640, 480, csurfpitch); + if (csurf == NULL) + return 1; + + cairo_t* cactx = cairo_create(csurf); + if (cactx == NULL) + return 1; + + bool run = true; + while (run) { + SDL_Event ev; + + while (SDL_PollEvent(&ev)) { + switch (ev.type) { + case SDL_WINDOWEVENT: + switch (ev.window.event) { + case SDL_WINDOWEVENT_CLOSE: + run = false; + break; + } + break; + case SDL_KEYDOWN: + switch (ev.key.keysym.sym) { + case SDLK_ESCAPE: + run = false; + break; + } + break; + } + } + + // clear + cairo_reset_clip(cactx); + cairo_set_source_rgb(cactx,0.25,0.25,0.25); + cairo_paint(cactx); + + // draw + cairo_set_line_width(cactx,8.0); + + { + unsigned int step=0; + + for (double a=0;a < (M_PI * 2);a += angstep,step++) { + double x = 320.0 + cos(a+ang)*200.0 + 40; + double y = 240.0 + sin(a+ang)*200.0 + 40; + + if (step == 0) + cairo_move_to(cactx,x,y); + else + cairo_line_to(cactx,x,y); + } + + cairo_close_path(cactx); + ang += angv; + } + + cairo_set_source_rgba(cactx,0,0,0,0.5); + cairo_fill(cactx); + + { + unsigned int step=0; + + for (double a=0;a < (M_PI * 2);a += angstep,step++) { + double x = 320.0 + cos(a+ang)*200.0; + double y = 240.0 + sin(a+ang)*200.0; + + if (step == 0) + cairo_move_to(cactx,x,y); + else + cairo_line_to(cactx,x,y); + } + + cairo_close_path(cactx); + ang += angv; + } + + cairo_set_source_rgb(cactx,1.0,1.0,1.0); + cairo_fill_preserve(cactx); + + cairo_set_source_rgb(cactx,0.0,0.0,1.0); + cairo_stroke(cactx); + + // copy Cairo output to display + cairo_surface_flush(csurf); + + SDL_UpdateWindowSurface(window); + + SDL_Delay(1000 / 30); + } + + cairo_surface_flush(csurf); + cairo_destroy(cactx); + cairo_surface_destroy(csurf); + + // do not free surface, owned by dinwo + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; +} + diff --git a/experiments/iconv/Makefile b/experiments/iconv/Makefile index d303af865..f94ec6a2e 100644 --- a/experiments/iconv/Makefile +++ b/experiments/iconv/Makefile @@ -1,26 +1,26 @@ -all: ic1 ic2 ic3 ic4 ic5 icw1 - -ic1: ic1.cpp iconvpp.o - g++ -Wall -Wextra -pedantic -std=c++11 -o $@ $< iconvpp.o -liconv - -ic2: ic2.cpp iconvpp.o - g++ -Wall -Wextra -pedantic -std=c++11 -o $@ $< iconvpp.o -liconv - -ic3: ic3.cpp iconvpp.o - g++ -Wall -Wextra -pedantic -std=c++11 -o $@ $< iconvpp.o -liconv - -ic4: ic4.cpp iconvpp.o - g++ -Wall -Wextra -pedantic -std=c++11 -o $@ $< iconvpp.o -liconv - -ic5: ic5.cpp iconvpp.o - g++ -Wall -Wextra -pedantic -std=c++11 -o $@ $< iconvpp.o -liconv - -icw1: icw1.cpp iconvpp.o - g++ -Wall -Wextra -pedantic -std=c++11 -o $@ $< iconvpp.o -liconv - -iconvpp.o: iconvpp.cpp - g++ -Wall -Wextra -pedantic -std=c++11 -c -o $@ $< - -clean: - rm -f ic1 ic2 ic3 ic4 ic5 icw1 *.o - +all: ic1 ic2 ic3 ic4 ic5 icw1 + +ic1: ic1.cpp iconvpp.o + g++ -Wall -Wextra -pedantic -std=c++11 -o $@ $< iconvpp.o -liconv + +ic2: ic2.cpp iconvpp.o + g++ -Wall -Wextra -pedantic -std=c++11 -o $@ $< iconvpp.o -liconv + +ic3: ic3.cpp iconvpp.o + g++ -Wall -Wextra -pedantic -std=c++11 -o $@ $< iconvpp.o -liconv + +ic4: ic4.cpp iconvpp.o + g++ -Wall -Wextra -pedantic -std=c++11 -o $@ $< iconvpp.o -liconv + +ic5: ic5.cpp iconvpp.o + g++ -Wall -Wextra -pedantic -std=c++11 -o $@ $< iconvpp.o -liconv + +icw1: icw1.cpp iconvpp.o + g++ -Wall -Wextra -pedantic -std=c++11 -o $@ $< iconvpp.o -liconv + +iconvpp.o: iconvpp.cpp + g++ -Wall -Wextra -pedantic -std=c++11 -c -o $@ $< + +clean: + rm -f ic1 ic2 ic3 ic4 ic5 icw1 *.o + diff --git a/experiments/iconv/ic5.cpp b/experiments/iconv/ic5.cpp index 1f8a1a021..a268d67ca 100644 --- a/experiments/iconv/ic5.cpp +++ b/experiments/iconv/ic5.cpp @@ -1,135 +1,135 @@ - -#include "iconvpp.hpp" - -using namespace std; - -// borrowed from DOSLIB -#define THIS_IS_JAPANESE "\x82\xb1\x82\xea\x82\xcd\x93\xfa\x96\x7b\x8c\xea\x82\xc5\x82\xb7"/* UTF-8 to Shift-JIS of "これは日本語です" */ - -typedef uint16_t test_char_t; -typedef std::basic_string test_string; - -int main() { - _Iconv *x = _Iconv::create(/*FROM*/"SHIFT-JIS"); - if (x == NULL) { - cerr << "Failed to create context" << endl; - return 1; - } - - _Iconv *fx = _Iconv::create("UTF-8"); - if (fx == NULL) { - cerr << "Failed to create context" << endl; - return 1; - } - - { - test_char_t tmp[512]; - const char *src = THIS_IS_JAPANESE; - - x->set_src(src); - x->set_dest(tmp,sizeof(tmp)/sizeof(tmp[0])); - - int err = x->string_convert(); - - if (err < 0) { - cerr << "Conversion failed, " << Iconv::errstring(err) << endl; - return 1; - } - - cout << "Test 1: " << src << endl; - cout << " Res: " << fx->string_convert(tmp) << endl; - cout << " Read: " << x->get_src_last_read() << endl; - cout << " Wrote: " << x->get_dest_last_written() << endl; - - x->finish(); - } - - { - test_string dst; - const char *src = THIS_IS_JAPANESE; - - x->set_src(src); - - int err = x->string_convert_dest(dst); - - if (err < 0) { - cerr << "Conversion failed, " << Iconv::errstring(err) << endl; - return 1; - } - - cout << "Test 1: " << src << endl; - cout << " Res: " << fx->string_convert(dst) << endl; - cout << " Read: " << x->get_src_last_read() << endl; - cout << " Wrote: " << x->get_dest_last_written() << endl; - } - - { - test_char_t tmp[512]; - const char *src = THIS_IS_JAPANESE; - - x->set_dest(tmp,sizeof(tmp)/sizeof(tmp[0])); - - int err = x->string_convert_src(src); - - if (err < 0) { - cerr << "Conversion failed, " << Iconv::errstring(err) << endl; - return 1; - } - - cout << "Test 1: " << src << endl; - cout << " Res: " << fx->string_convert(tmp) << endl; - cout << " Read: " << x->get_src_last_read() << endl; - cout << " Wrote: " << x->get_dest_last_written() << endl; - } - - { - test_char_t tmp[512]; - const std::string src = THIS_IS_JAPANESE; - - x->set_dest(tmp,sizeof(tmp)/sizeof(tmp[0])); - - int err = x->string_convert_src(src); - - if (err < 0) { - cerr << "Conversion failed, " << Iconv::errstring(err) << endl; - return 1; - } - - cout << "Test 1: " << src << endl; - cout << " Res: " << fx->string_convert(tmp) << endl; - cout << " Read: " << x->get_src_last_read() << endl; - cout << " Wrote: " << x->get_dest_last_written() << endl; - } - - { - test_string dst; - const std::string src = THIS_IS_JAPANESE; - - int err = x->string_convert(dst,src); - - if (err < 0) { - cerr << "Conversion failed, " << Iconv::errstring(err) << endl; - return 1; - } - - cout << "Test 1: " << src << endl; - cout << " Res: " << fx->string_convert(dst) << endl; - cout << " Read: " << x->get_src_last_read() << endl; - cout << " Wrote: " << x->get_dest_last_written() << endl; - } - - { - const std::string src = THIS_IS_JAPANESE; - test_string dst = x->string_convert(src); - - cout << "Test 1: " << src << endl; - cout << " Res: " << fx->string_convert(dst) << endl; - cout << " Read: " << x->get_src_last_read() << endl; - cout << " Wrote: " << x->get_dest_last_written() << endl; - } - - delete fx; - delete x; - return 0; -} - + +#include "iconvpp.hpp" + +using namespace std; + +// borrowed from DOSLIB +#define THIS_IS_JAPANESE "\x82\xb1\x82\xea\x82\xcd\x93\xfa\x96\x7b\x8c\xea\x82\xc5\x82\xb7"/* UTF-8 to Shift-JIS of "これは日本語です" */ + +typedef uint16_t test_char_t; +typedef std::basic_string test_string; + +int main() { + _Iconv *x = _Iconv::create(/*FROM*/"SHIFT-JIS"); + if (x == NULL) { + cerr << "Failed to create context" << endl; + return 1; + } + + _Iconv *fx = _Iconv::create("UTF-8"); + if (fx == NULL) { + cerr << "Failed to create context" << endl; + return 1; + } + + { + test_char_t tmp[512]; + const char *src = THIS_IS_JAPANESE; + + x->set_src(src); + x->set_dest(tmp,sizeof(tmp)/sizeof(tmp[0])); + + int err = x->string_convert(); + + if (err < 0) { + cerr << "Conversion failed, " << Iconv::errstring(err) << endl; + return 1; + } + + cout << "Test 1: " << src << endl; + cout << " Res: " << fx->string_convert(tmp) << endl; + cout << " Read: " << x->get_src_last_read() << endl; + cout << " Wrote: " << x->get_dest_last_written() << endl; + + x->finish(); + } + + { + test_string dst; + const char *src = THIS_IS_JAPANESE; + + x->set_src(src); + + int err = x->string_convert_dest(dst); + + if (err < 0) { + cerr << "Conversion failed, " << Iconv::errstring(err) << endl; + return 1; + } + + cout << "Test 1: " << src << endl; + cout << " Res: " << fx->string_convert(dst) << endl; + cout << " Read: " << x->get_src_last_read() << endl; + cout << " Wrote: " << x->get_dest_last_written() << endl; + } + + { + test_char_t tmp[512]; + const char *src = THIS_IS_JAPANESE; + + x->set_dest(tmp,sizeof(tmp)/sizeof(tmp[0])); + + int err = x->string_convert_src(src); + + if (err < 0) { + cerr << "Conversion failed, " << Iconv::errstring(err) << endl; + return 1; + } + + cout << "Test 1: " << src << endl; + cout << " Res: " << fx->string_convert(tmp) << endl; + cout << " Read: " << x->get_src_last_read() << endl; + cout << " Wrote: " << x->get_dest_last_written() << endl; + } + + { + test_char_t tmp[512]; + const std::string src = THIS_IS_JAPANESE; + + x->set_dest(tmp,sizeof(tmp)/sizeof(tmp[0])); + + int err = x->string_convert_src(src); + + if (err < 0) { + cerr << "Conversion failed, " << Iconv::errstring(err) << endl; + return 1; + } + + cout << "Test 1: " << src << endl; + cout << " Res: " << fx->string_convert(tmp) << endl; + cout << " Read: " << x->get_src_last_read() << endl; + cout << " Wrote: " << x->get_dest_last_written() << endl; + } + + { + test_string dst; + const std::string src = THIS_IS_JAPANESE; + + int err = x->string_convert(dst,src); + + if (err < 0) { + cerr << "Conversion failed, " << Iconv::errstring(err) << endl; + return 1; + } + + cout << "Test 1: " << src << endl; + cout << " Res: " << fx->string_convert(dst) << endl; + cout << " Read: " << x->get_src_last_read() << endl; + cout << " Wrote: " << x->get_dest_last_written() << endl; + } + + { + const std::string src = THIS_IS_JAPANESE; + test_string dst = x->string_convert(src); + + cout << "Test 1: " << src << endl; + cout << " Res: " << fx->string_convert(dst) << endl; + cout << " Read: " << x->get_src_last_read() << endl; + cout << " Wrote: " << x->get_dest_last_written() << endl; + } + + delete fx; + delete x; + return 0; +} + diff --git a/experiments/iconv/iconvpp.cpp b/experiments/iconv/iconvpp.cpp index c027c3737..cad32693d 100644 --- a/experiments/iconv/iconvpp.cpp +++ b/experiments/iconv/iconvpp.cpp @@ -1,18 +1,18 @@ - -#include "iconvpp.hpp" - -const char *_Iconv_CommonBase::errstring(int x) { - if (x >= 0) - return "no error"; - else if (x == err_noinit) - return "not initialized"; - else if (x == err_noroom) - return "out of room"; - else if (x == err_notvalid) - return "illegal multibyte sequence or invalid state"; - else if (x == err_incomplete) - return "incomplete multibyte sequence"; - - return "?"; -} - + +#include "iconvpp.hpp" + +const char *_Iconv_CommonBase::errstring(int x) { + if (x >= 0) + return "no error"; + else if (x == err_noinit) + return "not initialized"; + else if (x == err_noroom) + return "out of room"; + else if (x == err_notvalid) + return "illegal multibyte sequence or invalid state"; + else if (x == err_incomplete) + return "incomplete multibyte sequence"; + + return "?"; +} + diff --git a/experiments/iconv/iconvpp.hpp b/experiments/iconv/iconvpp.hpp index 90b888b00..9137476ae 100644 --- a/experiments/iconv/iconvpp.hpp +++ b/experiments/iconv/iconvpp.hpp @@ -1,420 +1,420 @@ - -#include -#include -#include - -#include -#include -#include -#include -#if defined(__MINGW32__) -# define ICONV_LITTLE_ENDIAN 1234 -# define ICONV_BIG_ENDIAN 4321 -# define ICONV_BYTE_ORDER ICONV_LITTLE_ENDIAN -#elif defined(__APPLE__) -# include -# define ICONV_LITTLE_ENDIAN 1234 -# define ICONV_BIG_ENDIAN 4321 -# if defined(__LITTLE_ENDIAN__) -# define ICONV_BYTE_ORDER ICONV_LITTLE_ENDIAN -# elif defined(__BIG_ENDIAN__) -# define ICONV_BYTE_ORDER ICONV_BIG_ENDIAN -# else -# error Unable to determine byte order -# endif -#else -# include -# define ICONV_BYTE_ORDER BYTE_ORDER -# define ICONV_LITTLE_ENDIAN LITTLE_ENDIAN -# define ICONV_BIG_ENDIAN BIG_ENDIAN -#endif - -#if defined(_MSC_VER) -/* no */ -#else -# define ENABLE_ICONV 1 -#endif - -#if defined(_WIN32) -# define ENABLE_ICONV_WIN32 1 -#endif - -/* common code to any templated version of _IconvBase */ -class _Iconv_CommonBase { -public: - static const char *errstring(int x); - inline size_t get_src_last_read(void) const { /* in units of sizeof(srcT) */ - return src_adv; - } - inline size_t get_dest_last_written(void) const { /* in units of sizeof(dstT) */ - return dst_adv; - } -public: - size_t dst_adv = 0; - size_t src_adv = 0; -public: - static constexpr int err_noinit = -EBADF; - static constexpr int err_noroom = -E2BIG; - static constexpr int err_notvalid = -EILSEQ; - static constexpr int err_incomplete = -EINVAL; -protected: - static constexpr bool big_endian(void) { - return (ICONV_BYTE_ORDER == ICONV_BIG_ENDIAN); - } -}; - -template class _Iconv; - -/* base _Iconv implementation, common to all implementations */ -template class _IconvBase : public _Iconv_CommonBase { -public: - /* NTS: The C++ standard defines std::string as std::basic_string. - * These typedefs will match if srcT = char and dstT = char */ - typedef std::basic_string src_string; - typedef std::basic_string dst_string; -public: - _IconvBase() { } - virtual ~_IconvBase() { } -public: - void finish(void) { - dst_ptr = NULL; - dst_ptr_fence = NULL; - src_ptr = NULL; - src_ptr_fence = NULL; - } - - void set_dest(dstT * const dst,dstT * const dst_fence) { - if (dst == NULL || dst_fence == NULL || dst > dst_fence) - throw std::invalid_argument("Iconv set_dest pointer out of range"); - - dst_adv = 0; - dst_ptr = dst; - dst_ptr_fence = dst_fence; - } - void set_dest(dstT * const dst,const size_t len/*in units of sizeof(dstT)*/) { - set_dest(dst,dst+len); - } - void set_dest(dstT * const dst) = delete; /* <- NO! Prevent C-string calls to std::string &dst function! */ - - void set_src(const srcT * const src,const srcT * const src_fence) { - if (src == NULL || src_fence == NULL || src > src_fence) - throw std::invalid_argument("Iconv set_src pointer out of range"); - - src_adv = 0; - src_ptr = src; - src_ptr_fence = src_fence; - } - void set_src(const srcT * const src,const size_t len) { - set_src(src,src+len); - } - void set_src(const srcT * const src) { // C-string - set_src(src,my_strlen(src)); - } -public: - virtual int _do_convert(void) { - return err_noinit; - } - int string_convert(dst_string &dst,const src_string &src) { - dst.resize(std::max(dst.size(),((src.length()+4u)*4u)+2u)); // maximum 4 bytes/char expansion UTF-8 or bigger if caller resized already - set_dest(dst); /* will apply new size to dst/fence pointers */ - - int err = string_convert_src(src); - - dst.resize(get_dest_last_written()); - - finish(); - return err; - } - int string_convert(void) { - if (dst_ptr == NULL || src_ptr == NULL) - return err_notvalid; - if (dst_ptr > dst_ptr_fence) - return err_notvalid; - if (src_ptr > src_ptr_fence) - return err_notvalid; - - int ret = _do_convert(); - - if (ret >= 0) { - /* add NUL */ - if (dst_ptr >= dst_ptr_fence) - return err_noroom; - - *dst_ptr++ = 0; - } - - return ret; - } - int string_convert_dest(dst_string &dst) { - size_t srcl = (size_t)((uintptr_t)src_ptr_fence - (uintptr_t)src_ptr); - - dst.resize(std::max(dst.size(),((srcl+4u)*4u)+2u)); - set_dest(dst); - - int err = string_convert(); - - finish(); - return err; - } - int string_convert_src(const src_string &src) { - set_src(src); - - int err = string_convert(); - - finish(); - return err; - } - dst_string string_convert(const src_string &src) { - dst_string res; - - string_convert(res,src); - - return res; - } -public: - inline bool eof(void) const { - return src_ptr >= src_ptr_fence; - } - inline bool eof_dest(void) const { - return dst_ptr >= dst_ptr_fence; - } - inline const srcT *get_srcp(void) const { - return src_ptr; - } - inline const dstT *get_destp(void) const { - return dst_ptr; - } -protected: - static inline size_t my_strlen(const char *s) { - return strlen(s); - } - static inline size_t my_strlen(const wchar_t *s) { - return wcslen(s); - } - template static inline size_t my_strlen(const X *s) { - size_t c = 0; - - while ((*s++) != 0) c++; - - return c; - } -protected: - void set_dest(dst_string &dst) { /* PRIVATE: External use can easily cause use-after-free bugs */ - set_dest(&dst[0],dst.size()); - } - void set_src(const src_string &src) { /* PRIVATE: External use can easily cause use-after-free bugs */ - set_src(src.c_str(),src.length()); - } -protected: - dstT* dst_ptr = NULL; - dstT* dst_ptr_fence = NULL; - const srcT* src_ptr = NULL; - const srcT* src_ptr_fence = NULL; - - friend _Iconv; -}; - -#if defined(ENABLE_ICONV) -# include - -/* _Iconv implementation of _IconvBase using GNU libiconv or GLIBC iconv, for Linux and Mac OS X systems. */ -/* See also: "man iconv" - * See also: [http://man7.org/linux/man-pages/man3/iconv.3.html] */ -template class _Iconv : public _IconvBase { -protected: - using pclass = _IconvBase; -public: - explicit _Iconv(const iconv_t &ctx) : context(ctx) {/* takes ownership of ctx */ - } - _Iconv(const _Iconv *p) = delete; - _Iconv(const _Iconv &other) = delete; /* no copying */ - _Iconv(const _Iconv &&other) = delete; /* no moving */ - _Iconv() = delete; - virtual ~_Iconv() { - close(); - } -public: - virtual int _do_convert(void) { - if (context != NULL) { - dstT *i_dst = pclass::dst_ptr; - const srcT *i_src = pclass::src_ptr; - size_t src_left = (size_t)((uintptr_t)((char*)pclass::src_ptr_fence) - (uintptr_t)((char*)pclass::src_ptr)); - size_t dst_left = (size_t)((uintptr_t)((char*)pclass::dst_ptr_fence) - (uintptr_t)((char*)pclass::dst_ptr)); - - iconv(context,NULL,NULL,NULL,NULL); - - /* Ref: [http://man7.org/linux/man-pages/man3/iconv.3.html] */ - int ret = iconv(context,(char**)(&(pclass::src_ptr)),&src_left,(char**)(&(pclass::dst_ptr)),&dst_left); - - pclass::src_adv = (size_t)(pclass::src_ptr - i_src); - pclass::dst_adv = (size_t)(pclass::dst_ptr - i_dst); - - if (ret < 0) { - if (errno == E2BIG) - return pclass::err_noroom; - else if (errno == EILSEQ) - return pclass::err_notvalid; - else if (errno == EINVAL) - return pclass::err_incomplete; - - return pclass::err_notvalid; - } - - return ret; - } - - return pclass::err_noinit; - } -public: - static _Iconv *create(const char *nw) { /* factory function, wide to char, or char to wide */ - if (sizeof(dstT) == sizeof(char) && sizeof(srcT) > sizeof(char)) { - const char *wchar_encoding = _get_wchar_encoding(); - if (wchar_encoding == NULL) return NULL; - - iconv_t ctx = iconv_open(/*TO*/nw,/*FROM*/wchar_encoding); /* from wchar to codepage nw */ - if (ctx != iconv_t(-1)) return new(std::nothrow) _Iconv(ctx); - } - else if (sizeof(dstT) > sizeof(char) && sizeof(srcT) == sizeof(char)) { - const char *wchar_encoding = _get_wchar_encoding(); - if (wchar_encoding == NULL) return NULL; - - iconv_t ctx = iconv_open(/*TO*/wchar_encoding,/*FROM*/nw); /* from codepage new to wchar */ - if (ctx != iconv_t(-1)) return new(std::nothrow) _Iconv(ctx); - } - - return NULL; - } - static _Iconv *create(const char *to,const char *from) { /* factory function */ - if (sizeof(dstT) == sizeof(char) && sizeof(srcT) == sizeof(char)) { - iconv_t ctx = iconv_open(to,from); - if (ctx != iconv_t(-1)) return new(std::nothrow) _Iconv(ctx); - } - - return NULL; - } -protected: - void close(void) { - if (context != NULL) { - iconv_close(context); - context = NULL; - } - } - template static const char *_get_wchar_encoding(void) { - if (sizeof(W) == 4) - return pclass::big_endian() ? "UTF-32BE" : "UTF-32LE"; - else if (sizeof(W) == 2) - return pclass::big_endian() ? "UTF-16BE" : "UTF-16LE"; - - return NULL; - } -protected: - iconv_t context = NULL; -}; - -/* Most of the time the Iconv form will be used, for Mac OS X and Linux platforms where UTF-8 is common. - * - * Conversion to/from wchar is intended for platforms like Microsoft Windows 98/ME/2000/XP/Vista/7/8/10/etc - * where the Win32 API functions take WCHAR (UTF-16 or UCS-16), in which case, the code will continue to - * use UTF-8 internally but convert to WCHAR when needed. For example, Win32 function CreateFileW(). - * - * Note that because of the UTF-16 world of Windows, Microsoft C++ defines wchar_t as an unsigned 16-bit - * integer. - * - * Linux and other OSes however define wchar_t as a 32-bit integer, but do not use wchar_t APIs, and often - * instead use UTF-8 for unicode, so the wchar_t versions will not see much use there. */ -typedef _Iconv Iconv; -typedef _Iconv IconvToW; -typedef _Iconv IconvFromW; - -#endif // ENABLE_ICONV - -#if defined(ENABLE_ICONV_WIN32) -# include - -/* Alternative implementation (char to WCHAR, or WCHAR to char only) using Microsoft Win32 APIs instead of libiconv. - * For use with embedded or low memory Windows installations or environments where the added load of libiconv would - * be undesirable. */ - -/* _IconvWin32 implementation of _IconvBase using Microsoft Win32 code page and WCHAR support functions for Windows 2000/XP/Vista/7/8/10/etc */ -template class _IconvWin32 : public _IconvBase { -protected: - using pclass = _IconvBase; -public: - explicit _IconvWin32(const UINT _codepage) : codepage(_codepage) { - } - _IconvWin32(const _IconvWin32 *p) = delete; - _IconvWin32(const _IconvWin32 &other) = delete; /* no copying */ - _IconvWin32(const _IconvWin32 &&other) = delete; /* no moving */ - _IconvWin32() = delete; - virtual ~_IconvWin32() { - } -public: - virtual int _do_convert(void) { - if (codepage != 0u) { - size_t src_left = (size_t)((uintptr_t)((char*)pclass::src_ptr_fence) - (uintptr_t)((char*)pclass::src_ptr)); - size_t dst_left = (size_t)((uintptr_t)((char*)pclass::dst_ptr_fence) - (uintptr_t)((char*)pclass::dst_ptr)); - int ret; - - if (sizeof(dstT) == sizeof(char) && sizeof(srcT) == sizeof(WCHAR)) { - /* Convert wide char to multibyte using the Win32 API. - * See also: [https://docs.microsoft.com/en-us/windows/desktop/api/stringapiset/nf-stringapiset-widechartomultibyte] */ - ret = WideCharToMultiByte(codepage,0,(WCHAR*)pclass::src_ptr,src_left/sizeof(srcT),(char*)pclass::dst_ptr,dst_left,NULL,NULL); - pclass::src_adv = src_left; - pclass::src_ptr += pclass::src_adv; - pclass::dst_adv = ret; - pclass::dst_ptr += pclass::dst_adv; - } - else if (sizeof(dstT) == sizeof(WCHAR) && sizeof(srcT) == sizeof(char)) { - /* Convert multibyte to wide char using the Win32 API. - * See also: [https://docs.microsoft.com/en-us/windows/desktop/api/stringapiset/nf-stringapiset-multibytetowidechar] */ - ret = MultiByteToWideChar(codepage,0,(char*)pclass::src_ptr,src_left,(WCHAR*)pclass::dst_ptr,dst_left/sizeof(dstT)); - pclass::src_adv = src_left; - pclass::src_ptr += pclass::src_adv; - pclass::dst_adv = ret; - pclass::dst_ptr += pclass::dst_adv; - } - else { - pclass::src_adv = 0; - pclass::dst_adv = 0; - ret = 0; - } - - if (ret == 0) { - DWORD err = GetLastError(); - - if (err == ERROR_INSUFFICIENT_BUFFER) - return pclass::err_noroom; - else if (err == ERROR_NO_UNICODE_TRANSLATION) - return pclass::err_notvalid; - - return pclass::err_noinit; - } - - return 0; - } - - return pclass::err_noinit; - } -public: - static _IconvWin32 *create(const UINT codepage) { /* factory function, WCHAR to char or char to WCHAR */ - CPINFO cpi; - - /* Test whether the code page exists */ - if (!GetCPInfo(codepage,&cpi)) - return NULL; - - if ((sizeof(dstT) == sizeof(char) && sizeof(srcT) == sizeof(WCHAR)) || - (sizeof(dstT) == sizeof(WCHAR) && sizeof(srcT) == sizeof(char))) - return new(std::nothrow) _IconvWin32(codepage); - - return NULL; - } -protected: - UINT codepage = 0; -}; - -typedef _IconvWin32 IconvWin32ToW; -typedef _IconvWin32 IconvWin32FromW; - -#endif // ENABLE_ICONV_WIN32 - + +#include +#include +#include + +#include +#include +#include +#include +#if defined(__MINGW32__) +# define ICONV_LITTLE_ENDIAN 1234 +# define ICONV_BIG_ENDIAN 4321 +# define ICONV_BYTE_ORDER ICONV_LITTLE_ENDIAN +#elif defined(__APPLE__) +# include +# define ICONV_LITTLE_ENDIAN 1234 +# define ICONV_BIG_ENDIAN 4321 +# if defined(__LITTLE_ENDIAN__) +# define ICONV_BYTE_ORDER ICONV_LITTLE_ENDIAN +# elif defined(__BIG_ENDIAN__) +# define ICONV_BYTE_ORDER ICONV_BIG_ENDIAN +# else +# error Unable to determine byte order +# endif +#else +# include +# define ICONV_BYTE_ORDER BYTE_ORDER +# define ICONV_LITTLE_ENDIAN LITTLE_ENDIAN +# define ICONV_BIG_ENDIAN BIG_ENDIAN +#endif + +#if defined(_MSC_VER) +/* no */ +#else +# define ENABLE_ICONV 1 +#endif + +#if defined(_WIN32) +# define ENABLE_ICONV_WIN32 1 +#endif + +/* common code to any templated version of _IconvBase */ +class _Iconv_CommonBase { +public: + static const char *errstring(int x); + inline size_t get_src_last_read(void) const { /* in units of sizeof(srcT) */ + return src_adv; + } + inline size_t get_dest_last_written(void) const { /* in units of sizeof(dstT) */ + return dst_adv; + } +public: + size_t dst_adv = 0; + size_t src_adv = 0; +public: + static constexpr int err_noinit = -EBADF; + static constexpr int err_noroom = -E2BIG; + static constexpr int err_notvalid = -EILSEQ; + static constexpr int err_incomplete = -EINVAL; +protected: + static constexpr bool big_endian(void) { + return (ICONV_BYTE_ORDER == ICONV_BIG_ENDIAN); + } +}; + +template class _Iconv; + +/* base _Iconv implementation, common to all implementations */ +template class _IconvBase : public _Iconv_CommonBase { +public: + /* NTS: The C++ standard defines std::string as std::basic_string. + * These typedefs will match if srcT = char and dstT = char */ + typedef std::basic_string src_string; + typedef std::basic_string dst_string; +public: + _IconvBase() { } + virtual ~_IconvBase() { } +public: + void finish(void) { + dst_ptr = NULL; + dst_ptr_fence = NULL; + src_ptr = NULL; + src_ptr_fence = NULL; + } + + void set_dest(dstT * const dst,dstT * const dst_fence) { + if (dst == NULL || dst_fence == NULL || dst > dst_fence) + throw std::invalid_argument("Iconv set_dest pointer out of range"); + + dst_adv = 0; + dst_ptr = dst; + dst_ptr_fence = dst_fence; + } + void set_dest(dstT * const dst,const size_t len/*in units of sizeof(dstT)*/) { + set_dest(dst,dst+len); + } + void set_dest(dstT * const dst) = delete; /* <- NO! Prevent C-string calls to std::string &dst function! */ + + void set_src(const srcT * const src,const srcT * const src_fence) { + if (src == NULL || src_fence == NULL || src > src_fence) + throw std::invalid_argument("Iconv set_src pointer out of range"); + + src_adv = 0; + src_ptr = src; + src_ptr_fence = src_fence; + } + void set_src(const srcT * const src,const size_t len) { + set_src(src,src+len); + } + void set_src(const srcT * const src) { // C-string + set_src(src,my_strlen(src)); + } +public: + virtual int _do_convert(void) { + return err_noinit; + } + int string_convert(dst_string &dst,const src_string &src) { + dst.resize(std::max(dst.size(),((src.length()+4u)*4u)+2u)); // maximum 4 bytes/char expansion UTF-8 or bigger if caller resized already + set_dest(dst); /* will apply new size to dst/fence pointers */ + + int err = string_convert_src(src); + + dst.resize(get_dest_last_written()); + + finish(); + return err; + } + int string_convert(void) { + if (dst_ptr == NULL || src_ptr == NULL) + return err_notvalid; + if (dst_ptr > dst_ptr_fence) + return err_notvalid; + if (src_ptr > src_ptr_fence) + return err_notvalid; + + int ret = _do_convert(); + + if (ret >= 0) { + /* add NUL */ + if (dst_ptr >= dst_ptr_fence) + return err_noroom; + + *dst_ptr++ = 0; + } + + return ret; + } + int string_convert_dest(dst_string &dst) { + size_t srcl = (size_t)((uintptr_t)src_ptr_fence - (uintptr_t)src_ptr); + + dst.resize(std::max(dst.size(),((srcl+4u)*4u)+2u)); + set_dest(dst); + + int err = string_convert(); + + finish(); + return err; + } + int string_convert_src(const src_string &src) { + set_src(src); + + int err = string_convert(); + + finish(); + return err; + } + dst_string string_convert(const src_string &src) { + dst_string res; + + string_convert(res,src); + + return res; + } +public: + inline bool eof(void) const { + return src_ptr >= src_ptr_fence; + } + inline bool eof_dest(void) const { + return dst_ptr >= dst_ptr_fence; + } + inline const srcT *get_srcp(void) const { + return src_ptr; + } + inline const dstT *get_destp(void) const { + return dst_ptr; + } +protected: + static inline size_t my_strlen(const char *s) { + return strlen(s); + } + static inline size_t my_strlen(const wchar_t *s) { + return wcslen(s); + } + template static inline size_t my_strlen(const X *s) { + size_t c = 0; + + while ((*s++) != 0) c++; + + return c; + } +protected: + void set_dest(dst_string &dst) { /* PRIVATE: External use can easily cause use-after-free bugs */ + set_dest(&dst[0],dst.size()); + } + void set_src(const src_string &src) { /* PRIVATE: External use can easily cause use-after-free bugs */ + set_src(src.c_str(),src.length()); + } +protected: + dstT* dst_ptr = NULL; + dstT* dst_ptr_fence = NULL; + const srcT* src_ptr = NULL; + const srcT* src_ptr_fence = NULL; + + friend _Iconv; +}; + +#if defined(ENABLE_ICONV) +# include + +/* _Iconv implementation of _IconvBase using GNU libiconv or GLIBC iconv, for Linux and Mac OS X systems. */ +/* See also: "man iconv" + * See also: [http://man7.org/linux/man-pages/man3/iconv.3.html] */ +template class _Iconv : public _IconvBase { +protected: + using pclass = _IconvBase; +public: + explicit _Iconv(const iconv_t &ctx) : context(ctx) {/* takes ownership of ctx */ + } + _Iconv(const _Iconv *p) = delete; + _Iconv(const _Iconv &other) = delete; /* no copying */ + _Iconv(const _Iconv &&other) = delete; /* no moving */ + _Iconv() = delete; + virtual ~_Iconv() { + close(); + } +public: + virtual int _do_convert(void) { + if (context != NULL) { + dstT *i_dst = pclass::dst_ptr; + const srcT *i_src = pclass::src_ptr; + size_t src_left = (size_t)((uintptr_t)((char*)pclass::src_ptr_fence) - (uintptr_t)((char*)pclass::src_ptr)); + size_t dst_left = (size_t)((uintptr_t)((char*)pclass::dst_ptr_fence) - (uintptr_t)((char*)pclass::dst_ptr)); + + iconv(context,NULL,NULL,NULL,NULL); + + /* Ref: [http://man7.org/linux/man-pages/man3/iconv.3.html] */ + int ret = iconv(context,(char**)(&(pclass::src_ptr)),&src_left,(char**)(&(pclass::dst_ptr)),&dst_left); + + pclass::src_adv = (size_t)(pclass::src_ptr - i_src); + pclass::dst_adv = (size_t)(pclass::dst_ptr - i_dst); + + if (ret < 0) { + if (errno == E2BIG) + return pclass::err_noroom; + else if (errno == EILSEQ) + return pclass::err_notvalid; + else if (errno == EINVAL) + return pclass::err_incomplete; + + return pclass::err_notvalid; + } + + return ret; + } + + return pclass::err_noinit; + } +public: + static _Iconv *create(const char *nw) { /* factory function, wide to char, or char to wide */ + if (sizeof(dstT) == sizeof(char) && sizeof(srcT) > sizeof(char)) { + const char *wchar_encoding = _get_wchar_encoding(); + if (wchar_encoding == NULL) return NULL; + + iconv_t ctx = iconv_open(/*TO*/nw,/*FROM*/wchar_encoding); /* from wchar to codepage nw */ + if (ctx != iconv_t(-1)) return new(std::nothrow) _Iconv(ctx); + } + else if (sizeof(dstT) > sizeof(char) && sizeof(srcT) == sizeof(char)) { + const char *wchar_encoding = _get_wchar_encoding(); + if (wchar_encoding == NULL) return NULL; + + iconv_t ctx = iconv_open(/*TO*/wchar_encoding,/*FROM*/nw); /* from codepage new to wchar */ + if (ctx != iconv_t(-1)) return new(std::nothrow) _Iconv(ctx); + } + + return NULL; + } + static _Iconv *create(const char *to,const char *from) { /* factory function */ + if (sizeof(dstT) == sizeof(char) && sizeof(srcT) == sizeof(char)) { + iconv_t ctx = iconv_open(to,from); + if (ctx != iconv_t(-1)) return new(std::nothrow) _Iconv(ctx); + } + + return NULL; + } +protected: + void close(void) { + if (context != NULL) { + iconv_close(context); + context = NULL; + } + } + template static const char *_get_wchar_encoding(void) { + if (sizeof(W) == 4) + return pclass::big_endian() ? "UTF-32BE" : "UTF-32LE"; + else if (sizeof(W) == 2) + return pclass::big_endian() ? "UTF-16BE" : "UTF-16LE"; + + return NULL; + } +protected: + iconv_t context = NULL; +}; + +/* Most of the time the Iconv form will be used, for Mac OS X and Linux platforms where UTF-8 is common. + * + * Conversion to/from wchar is intended for platforms like Microsoft Windows 98/ME/2000/XP/Vista/7/8/10/etc + * where the Win32 API functions take WCHAR (UTF-16 or UCS-16), in which case, the code will continue to + * use UTF-8 internally but convert to WCHAR when needed. For example, Win32 function CreateFileW(). + * + * Note that because of the UTF-16 world of Windows, Microsoft C++ defines wchar_t as an unsigned 16-bit + * integer. + * + * Linux and other OSes however define wchar_t as a 32-bit integer, but do not use wchar_t APIs, and often + * instead use UTF-8 for unicode, so the wchar_t versions will not see much use there. */ +typedef _Iconv Iconv; +typedef _Iconv IconvToW; +typedef _Iconv IconvFromW; + +#endif // ENABLE_ICONV + +#if defined(ENABLE_ICONV_WIN32) +# include + +/* Alternative implementation (char to WCHAR, or WCHAR to char only) using Microsoft Win32 APIs instead of libiconv. + * For use with embedded or low memory Windows installations or environments where the added load of libiconv would + * be undesirable. */ + +/* _IconvWin32 implementation of _IconvBase using Microsoft Win32 code page and WCHAR support functions for Windows 2000/XP/Vista/7/8/10/etc */ +template class _IconvWin32 : public _IconvBase { +protected: + using pclass = _IconvBase; +public: + explicit _IconvWin32(const UINT _codepage) : codepage(_codepage) { + } + _IconvWin32(const _IconvWin32 *p) = delete; + _IconvWin32(const _IconvWin32 &other) = delete; /* no copying */ + _IconvWin32(const _IconvWin32 &&other) = delete; /* no moving */ + _IconvWin32() = delete; + virtual ~_IconvWin32() { + } +public: + virtual int _do_convert(void) { + if (codepage != 0u) { + size_t src_left = (size_t)((uintptr_t)((char*)pclass::src_ptr_fence) - (uintptr_t)((char*)pclass::src_ptr)); + size_t dst_left = (size_t)((uintptr_t)((char*)pclass::dst_ptr_fence) - (uintptr_t)((char*)pclass::dst_ptr)); + int ret; + + if (sizeof(dstT) == sizeof(char) && sizeof(srcT) == sizeof(WCHAR)) { + /* Convert wide char to multibyte using the Win32 API. + * See also: [https://docs.microsoft.com/en-us/windows/desktop/api/stringapiset/nf-stringapiset-widechartomultibyte] */ + ret = WideCharToMultiByte(codepage,0,(WCHAR*)pclass::src_ptr,src_left/sizeof(srcT),(char*)pclass::dst_ptr,dst_left,NULL,NULL); + pclass::src_adv = src_left; + pclass::src_ptr += pclass::src_adv; + pclass::dst_adv = ret; + pclass::dst_ptr += pclass::dst_adv; + } + else if (sizeof(dstT) == sizeof(WCHAR) && sizeof(srcT) == sizeof(char)) { + /* Convert multibyte to wide char using the Win32 API. + * See also: [https://docs.microsoft.com/en-us/windows/desktop/api/stringapiset/nf-stringapiset-multibytetowidechar] */ + ret = MultiByteToWideChar(codepage,0,(char*)pclass::src_ptr,src_left,(WCHAR*)pclass::dst_ptr,dst_left/sizeof(dstT)); + pclass::src_adv = src_left; + pclass::src_ptr += pclass::src_adv; + pclass::dst_adv = ret; + pclass::dst_ptr += pclass::dst_adv; + } + else { + pclass::src_adv = 0; + pclass::dst_adv = 0; + ret = 0; + } + + if (ret == 0) { + DWORD err = GetLastError(); + + if (err == ERROR_INSUFFICIENT_BUFFER) + return pclass::err_noroom; + else if (err == ERROR_NO_UNICODE_TRANSLATION) + return pclass::err_notvalid; + + return pclass::err_noinit; + } + + return 0; + } + + return pclass::err_noinit; + } +public: + static _IconvWin32 *create(const UINT codepage) { /* factory function, WCHAR to char or char to WCHAR */ + CPINFO cpi; + + /* Test whether the code page exists */ + if (!GetCPInfo(codepage,&cpi)) + return NULL; + + if ((sizeof(dstT) == sizeof(char) && sizeof(srcT) == sizeof(WCHAR)) || + (sizeof(dstT) == sizeof(WCHAR) && sizeof(srcT) == sizeof(char))) + return new(std::nothrow) _IconvWin32(codepage); + + return NULL; + } +protected: + UINT codepage = 0; +}; + +typedef _IconvWin32 IconvWin32ToW; +typedef _IconvWin32 IconvWin32FromW; + +#endif // ENABLE_ICONV_WIN32 + diff --git a/experiments/iconv/icw1.cpp b/experiments/iconv/icw1.cpp index ed1fc6eae..88092a0cd 100644 --- a/experiments/iconv/icw1.cpp +++ b/experiments/iconv/icw1.cpp @@ -1,134 +1,134 @@ - -#include "iconvpp.hpp" - -using namespace std; - -// borrowed from DOSLIB -#define THIS_IS_JAPANESE "\x82\xb1\x82\xea\x82\xcd\x93\xfa\x96\x7b\x8c\xea\x82\xc5\x82\xb7"/* UTF-8 to Shift-JIS of "これは日本語です" */ - -int main() { -#if defined(ENABLE_ICONV_WIN32) - IconvWin32ToW *x = IconvWin32ToW::create(/*FROM*/932); - if (x == NULL) { - cerr << "Failed to create context" << endl; - return 1; - } - - IconvWin32FromW *fx = IconvWin32FromW::create(CP_UTF8); - if (fx == NULL) { - cerr << "Failed to create context" << endl; - return 1; - } - - { - WCHAR tmp[512]; - const char *src = THIS_IS_JAPANESE; - - x->set_src(src); - x->set_dest(tmp,sizeof(tmp)); - - int err = x->string_convert(); - - if (err < 0) { - cerr << "Conversion failed, " << Iconv::errstring(err) << endl; - return 1; - } - - cout << "Test 1: " << src << endl; - cout << " Res: " << fx->string_convert(tmp) << endl; - cout << " Read: " << x->get_src_last_read() << endl; - cout << " Wrote: " << x->get_dest_last_written() << endl; - - x->finish(); - } - - { - std::wstring dst; - const char *src = THIS_IS_JAPANESE; - - x->set_src(src); - - int err = x->string_convert_dest(dst); - - if (err < 0) { - cerr << "Conversion failed, " << Iconv::errstring(err) << endl; - return 1; - } - - cout << "Test 1: " << src << endl; - cout << " Res: " << fx->string_convert(dst) << endl; - cout << " Read: " << x->get_src_last_read() << endl; - cout << " Wrote: " << x->get_dest_last_written() << endl; - } - - { - WCHAR tmp[512]; - const char *src = THIS_IS_JAPANESE; - - x->set_dest(tmp,sizeof(tmp)); - - int err = x->string_convert_src(src); - - if (err < 0) { - cerr << "Conversion failed, " << Iconv::errstring(err) << endl; - return 1; - } - - cout << "Test 1: " << src << endl; - cout << " Res: " << fx->string_convert(tmp) << endl; - cout << " Read: " << x->get_src_last_read() << endl; - cout << " Wrote: " << x->get_dest_last_written() << endl; - } - - { - WCHAR tmp[512]; - const std::string src = THIS_IS_JAPANESE; - - x->set_dest(tmp,sizeof(tmp)); - - int err = x->string_convert_src(src); - - if (err < 0) { - cerr << "Conversion failed, " << Iconv::errstring(err) << endl; - return 1; - } - - cout << "Test 1: " << src << endl; - cout << " Res: " << fx->string_convert(tmp) << endl; - cout << " Read: " << x->get_src_last_read() << endl; - cout << " Wrote: " << x->get_dest_last_written() << endl; - } - - { - std::wstring dst; - const std::string src = THIS_IS_JAPANESE; - - int err = x->string_convert(dst,src); - - if (err < 0) { - cerr << "Conversion failed, " << Iconv::errstring(err) << endl; - return 1; - } - - cout << "Test 1: " << src << endl; - cout << " Res: " << fx->string_convert(dst) << endl; - cout << " Read: " << x->get_src_last_read() << endl; - cout << " Wrote: " << x->get_dest_last_written() << endl; - } - - { - const std::string src = THIS_IS_JAPANESE; - std::wstring dst = x->string_convert(src); - - cout << "Test 1: " << src << endl; - cout << " Res: " << fx->string_convert(dst) << endl; - cout << " Read: " << x->get_src_last_read() << endl; - cout << " Wrote: " << x->get_dest_last_written() << endl; - } - - delete fx; - delete x; -#endif - return 0; -} - + +#include "iconvpp.hpp" + +using namespace std; + +// borrowed from DOSLIB +#define THIS_IS_JAPANESE "\x82\xb1\x82\xea\x82\xcd\x93\xfa\x96\x7b\x8c\xea\x82\xc5\x82\xb7"/* UTF-8 to Shift-JIS of "これは日本語です" */ + +int main() { +#if defined(ENABLE_ICONV_WIN32) + IconvWin32ToW *x = IconvWin32ToW::create(/*FROM*/932); + if (x == NULL) { + cerr << "Failed to create context" << endl; + return 1; + } + + IconvWin32FromW *fx = IconvWin32FromW::create(CP_UTF8); + if (fx == NULL) { + cerr << "Failed to create context" << endl; + return 1; + } + + { + WCHAR tmp[512]; + const char *src = THIS_IS_JAPANESE; + + x->set_src(src); + x->set_dest(tmp,sizeof(tmp)); + + int err = x->string_convert(); + + if (err < 0) { + cerr << "Conversion failed, " << Iconv::errstring(err) << endl; + return 1; + } + + cout << "Test 1: " << src << endl; + cout << " Res: " << fx->string_convert(tmp) << endl; + cout << " Read: " << x->get_src_last_read() << endl; + cout << " Wrote: " << x->get_dest_last_written() << endl; + + x->finish(); + } + + { + std::wstring dst; + const char *src = THIS_IS_JAPANESE; + + x->set_src(src); + + int err = x->string_convert_dest(dst); + + if (err < 0) { + cerr << "Conversion failed, " << Iconv::errstring(err) << endl; + return 1; + } + + cout << "Test 1: " << src << endl; + cout << " Res: " << fx->string_convert(dst) << endl; + cout << " Read: " << x->get_src_last_read() << endl; + cout << " Wrote: " << x->get_dest_last_written() << endl; + } + + { + WCHAR tmp[512]; + const char *src = THIS_IS_JAPANESE; + + x->set_dest(tmp,sizeof(tmp)); + + int err = x->string_convert_src(src); + + if (err < 0) { + cerr << "Conversion failed, " << Iconv::errstring(err) << endl; + return 1; + } + + cout << "Test 1: " << src << endl; + cout << " Res: " << fx->string_convert(tmp) << endl; + cout << " Read: " << x->get_src_last_read() << endl; + cout << " Wrote: " << x->get_dest_last_written() << endl; + } + + { + WCHAR tmp[512]; + const std::string src = THIS_IS_JAPANESE; + + x->set_dest(tmp,sizeof(tmp)); + + int err = x->string_convert_src(src); + + if (err < 0) { + cerr << "Conversion failed, " << Iconv::errstring(err) << endl; + return 1; + } + + cout << "Test 1: " << src << endl; + cout << " Res: " << fx->string_convert(tmp) << endl; + cout << " Read: " << x->get_src_last_read() << endl; + cout << " Wrote: " << x->get_dest_last_written() << endl; + } + + { + std::wstring dst; + const std::string src = THIS_IS_JAPANESE; + + int err = x->string_convert(dst,src); + + if (err < 0) { + cerr << "Conversion failed, " << Iconv::errstring(err) << endl; + return 1; + } + + cout << "Test 1: " << src << endl; + cout << " Res: " << fx->string_convert(dst) << endl; + cout << " Read: " << x->get_src_last_read() << endl; + cout << " Wrote: " << x->get_dest_last_written() << endl; + } + + { + const std::string src = THIS_IS_JAPANESE; + std::wstring dst = x->string_convert(src); + + cout << "Test 1: " << src << endl; + cout << " Res: " << fx->string_convert(dst) << endl; + cout << " Read: " << x->get_src_last_read() << endl; + cout << " Wrote: " << x->get_dest_last_written() << endl; + } + + delete fx; + delete x; +#endif + return 0; +} + diff --git a/experiments/squirrel/sq1.cpp b/experiments/squirrel/sq1.cpp index 0d97e33ce..446d24075 100644 --- a/experiments/squirrel/sq1.cpp +++ b/experiments/squirrel/sq1.cpp @@ -1,149 +1,149 @@ - -#include -#include -#include -#include -#include - -#include -#include - -using namespace std; - -extern "C" { -#include -#include -#include -#include -#include -#include -#include -#include -} - -#ifdef SQUNICODE -#define scfprintf fwprintf -#define scvprintf vfwprintf -#else -#define scfprintf fprintf -#define scvprintf vfprintf -#endif - -SQInteger hello1(HSQUIRRELVM v) { - printf("hello1 "); - SQInteger mx = sq_gettop(v); - for (SQInteger n=1;n <= mx;n++) { - switch (sq_gettype(v,n)) { - case OT_NULL: - printf("(null) "); - break; - case OT_INTEGER: { - SQInteger i = 0; - sq_getinteger(v,n,&i); - printf("%lld ",(long long)i); - } break; - case OT_FLOAT: { - SQFloat i = 0; - sq_getfloat(v,n,&i); - printf("%.3f ",(double)i); - } break; - case OT_STRING: { - const SQChar *i = NULL; - sq_getstring(v,n,&i); - printf("\"%s\" ",(const char*)i); - } break; - case OT_CLOSURE: { - printf("(closure) "); - } break; - case OT_NATIVECLOSURE: { - printf("(native closure) "); - } break; - default: - break; - }; - } - printf("\n"); - return 0; -} - -void printfunc(HSQUIRRELVM SQ_UNUSED_ARG(v),const SQChar *s,...) -{ - va_list vl; - va_start(vl, s); - scvprintf(stdout, s, vl); - va_end(vl); - (void)v; /* UNUSED */ -} - -void errorfunc(HSQUIRRELVM SQ_UNUSED_ARG(v),const SQChar *s,...) -{ - va_list vl; - va_start(vl, s); - scvprintf(stderr, s, vl); - va_end(vl); -} - -int main(int argc,char **argv) { - HSQUIRRELVM sq; - ifstream i; - - if (argc > 1) - i.open(argv[1],ios_base::in); - else - i.open("sq1.sq",ios_base::in); - - if (!i.is_open()) return 1; - - sq = sq_open(1024); - if (sq == NULL) return 1; - -// sqstd_register_bloblib(sq); <- Valgrind complains about out of range memory access -// sqstd_register_iolib(sq); <- Valgrind complains about out of range memory access - sqstd_register_systemlib(sq); - sqstd_register_mathlib(sq); - sqstd_register_stringlib(sq); - - sq_setprintfunc(sq,printfunc,errorfunc); - - sqstd_seterrorhandlers(sq); - - char *blob; - off_t sz; - - sz = 65536; - blob = new char[sz]; /* or throw a C++ exception on fail */ - - i.read(blob,sz-1); - { - streamsize rd = i.gcount(); - assert(rd < sz); - blob[rd] = 0; - } - - { - sq_pushroottable(sq); - sq_pushstring(sq,"hello1",-1); - sq_newclosure(sq,hello1,0); - sq_newslot(sq,-3,SQFalse); - sq_pop(sq,1); - } - - if (SQ_SUCCEEDED(sq_compilebuffer(sq,blob,strlen(blob),"file",SQTrue))) { - sq_pushroottable(sq); - if (SQ_SUCCEEDED(sq_call(sq,1,0,SQTrue))) { - } - else { - fprintf(stderr,"Failed to call function\n"); - } - } - else { - fprintf(stderr,"Failed to compile buffer\n"); - } - - delete[] blob; - - sq_close(sq); - i.close(); - return 0; -} - + +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; + +extern "C" { +#include +#include +#include +#include +#include +#include +#include +#include +} + +#ifdef SQUNICODE +#define scfprintf fwprintf +#define scvprintf vfwprintf +#else +#define scfprintf fprintf +#define scvprintf vfprintf +#endif + +SQInteger hello1(HSQUIRRELVM v) { + printf("hello1 "); + SQInteger mx = sq_gettop(v); + for (SQInteger n=1;n <= mx;n++) { + switch (sq_gettype(v,n)) { + case OT_NULL: + printf("(null) "); + break; + case OT_INTEGER: { + SQInteger i = 0; + sq_getinteger(v,n,&i); + printf("%lld ",(long long)i); + } break; + case OT_FLOAT: { + SQFloat i = 0; + sq_getfloat(v,n,&i); + printf("%.3f ",(double)i); + } break; + case OT_STRING: { + const SQChar *i = NULL; + sq_getstring(v,n,&i); + printf("\"%s\" ",(const char*)i); + } break; + case OT_CLOSURE: { + printf("(closure) "); + } break; + case OT_NATIVECLOSURE: { + printf("(native closure) "); + } break; + default: + break; + }; + } + printf("\n"); + return 0; +} + +void printfunc(HSQUIRRELVM SQ_UNUSED_ARG(v),const SQChar *s,...) +{ + va_list vl; + va_start(vl, s); + scvprintf(stdout, s, vl); + va_end(vl); + (void)v; /* UNUSED */ +} + +void errorfunc(HSQUIRRELVM SQ_UNUSED_ARG(v),const SQChar *s,...) +{ + va_list vl; + va_start(vl, s); + scvprintf(stderr, s, vl); + va_end(vl); +} + +int main(int argc,char **argv) { + HSQUIRRELVM sq; + ifstream i; + + if (argc > 1) + i.open(argv[1],ios_base::in); + else + i.open("sq1.sq",ios_base::in); + + if (!i.is_open()) return 1; + + sq = sq_open(1024); + if (sq == NULL) return 1; + +// sqstd_register_bloblib(sq); <- Valgrind complains about out of range memory access +// sqstd_register_iolib(sq); <- Valgrind complains about out of range memory access + sqstd_register_systemlib(sq); + sqstd_register_mathlib(sq); + sqstd_register_stringlib(sq); + + sq_setprintfunc(sq,printfunc,errorfunc); + + sqstd_seterrorhandlers(sq); + + char *blob; + off_t sz; + + sz = 65536; + blob = new char[sz]; /* or throw a C++ exception on fail */ + + i.read(blob,sz-1); + { + streamsize rd = i.gcount(); + assert(rd < sz); + blob[rd] = 0; + } + + { + sq_pushroottable(sq); + sq_pushstring(sq,"hello1",-1); + sq_newclosure(sq,hello1,0); + sq_newslot(sq,-3,SQFalse); + sq_pop(sq,1); + } + + if (SQ_SUCCEEDED(sq_compilebuffer(sq,blob,strlen(blob),"file",SQTrue))) { + sq_pushroottable(sq); + if (SQ_SUCCEEDED(sq_call(sq,1,0,SQTrue))) { + } + else { + fprintf(stderr,"Failed to call function\n"); + } + } + else { + fprintf(stderr,"Failed to compile buffer\n"); + } + + delete[] blob; + + sq_close(sq); + i.close(); + return 0; +} + diff --git a/experiments/squirrel/sq1.sq b/experiments/squirrel/sq1.sq index c8201c148..0ccc64a66 100644 --- a/experiments/squirrel/sq1.sq +++ b/experiments/squirrel/sq1.sq @@ -1,39 +1,39 @@ - -local a = 123; -local b = 123.456; -local c = "Hello world"; - -/* Comment */ - -print(a+"\n"); -print(b+"\n"); -print(c+"\n"); - -local d = [1,2,3,4] - -print(d[0]+"\n"); - -local e = { - a = 10, - b = 20, - c = "Hello", - [20] = "World" -} - -print(e.a+"\n"); -print(e["c"]+"\n"); -print(e[20]+"\n"); - -function aaa(x,y) { - return x*y; -} - -print(aaa(4,3)+"\n"); -hello1(); -hello1(1); -hello1(23.44); -hello1("Hello world"); -hello1(1,2,3,4); -hello1(1,2.3,"4"); -hello1(hello1); - + +local a = 123; +local b = 123.456; +local c = "Hello world"; + +/* Comment */ + +print(a+"\n"); +print(b+"\n"); +print(c+"\n"); + +local d = [1,2,3,4] + +print(d[0]+"\n"); + +local e = { + a = 10, + b = 20, + c = "Hello", + [20] = "World" +} + +print(e.a+"\n"); +print(e["c"]+"\n"); +print(e[20]+"\n"); + +function aaa(x,y) { + return x*y; +} + +print(aaa(4,3)+"\n"); +hello1(); +hello1(1); +hello1(23.44); +hello1("Hello world"); +hello1(1,2,3,4); +hello1(1,2.3,"4"); +hello1(hello1); + diff --git a/include/build_timestamp.h b/include/build_timestamp.h index f23b0c040..0755c2993 100644 --- a/include/build_timestamp.h +++ b/include/build_timestamp.h @@ -1,3 +1,3 @@ -/*auto-generated*/ -#define UPDATED_STR "Apr 2, 2019 4:40:18pm" -#define COPYRIGHT_END_YEAR "2019" +/*auto-generated*/ +#define UPDATED_STR "Apr 2, 2019 4:40:18pm" +#define COPYRIGHT_END_YEAR "2019" diff --git a/include/dos_inc.h b/include/dos_inc.h index 0d379e4fc..fb84b099c 100644 --- a/include/dos_inc.h +++ b/include/dos_inc.h @@ -1,731 +1,731 @@ -/* - * Copyright (C) 2002-2015 The DOSBox Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - - -#ifndef DOSBOX_DOS_INC_H -#define DOSBOX_DOS_INC_H - -#include - -#ifndef DOSBOX_DOS_SYSTEM_H -#include "dos_system.h" -#endif -#ifndef DOSBOX_MEM_H -#include "mem.h" -#endif - -#include //for offsetof - -#ifdef _MSC_VER -#pragma pack (1) -#endif -struct CommandTail{ - Bit8u count; /* number of bytes returned */ - char buffer[127]; /* the buffer itself */ -} GCC_ATTRIBUTE(packed); -#ifdef _MSC_VER -#pragma pack () -#endif - -extern Bit16u first_umb_seg; -extern Bit16u first_umb_size; - -bool MEM_unmap_physmem(Bitu start,Bitu end); -bool MEM_map_RAM_physmem(Bitu start,Bitu end); - -struct BuiltinFileBlob { - const char *recommended_file_name; - const unsigned char *data; - size_t length; -}; - -struct DOS_Date { - Bit16u year; - Bit8u month; - Bit8u day; -}; - -struct DOS_Version { - Bit8u major,minor,revision; -}; - -#ifndef MACOSX -#if defined (__APPLE__) -#define MACOSX 1 -#endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -typedef signed char INT8, *PINT8; -typedef signed short INT16, *PINT16; -typedef signed int INT32, *PINT32; -//typedef signed __int64 INT64, *PINT64; -typedef unsigned char UINT8, *PUINT8; -typedef unsigned short UINT16, *PUINT16; -typedef unsigned int UINT32, *PUINT32; -//typedef unsigned __int64 UINT64, *PUINT64; -#ifdef __cplusplus -} -#endif - -#define SECTOR_SIZE_MAX 2048 - -#ifdef _MSC_VER -#pragma pack (1) -#endif -union bootSector { - struct entries { - Bit8u jump[3]; - Bit8u oem_name[8]; - Bit16u bytesect; - Bit8u sectclust; - Bit16u reserve_sect; - } bootdata; - Bit8u rawdata[SECTOR_SIZE_MAX]; -} GCC_ATTRIBUTE(packed); -#ifdef _MSC_VER -#pragma pack () -#endif - - -enum { MCB_FREE=0x0000,MCB_DOS=0x0008 }; -enum { RETURN_EXIT=0,RETURN_CTRLC=1,RETURN_ABORT=2,RETURN_TSR=3}; - -extern Bitu DOS_FILES; - -#define DOS_DRIVES 26 -#define DOS_DEVICES 10 - - -#if 0 /* ORIGINAL DEFINES FOR REFERENCE */ -// dos swappable area is 0x320 bytes beyond the sysvars table -// device driver chain is inside sysvars -#define DOS_INFOBLOCK_SEG 0x80 // sysvars (list of lists) -#define DOS_CONDRV_SEG 0xa0 -#define DOS_CONSTRING_SEG 0xa8 -#define DOS_SDA_SEG 0xb2 // dos swappable area -#define DOS_SDA_OFS 0 -#define DOS_CDS_SEG 0x108 -#define DOS_MEM_START 0x158 // regression to r3437 fixes nascar 2 colors -//#define DOS_MEM_START 0x16f //First Segment that DOS can use - -#define DOS_PRIVATE_SEGMENT 0xc800 -#define DOS_PRIVATE_SEGMENT_END 0xd000 -#endif - -// dos swappable area is 0x320 bytes beyond the sysvars table -// device driver chain is inside sysvars -extern Bit16u DOS_INFOBLOCK_SEG;// 0x80 // sysvars (list of lists) -extern Bit16u DOS_CONDRV_SEG;// 0xa0 -extern Bit16u DOS_CONSTRING_SEG;// 0xa8 -extern Bit16u DOS_SDA_SEG;// 0xb2 // dos swappable area -extern Bit16u DOS_SDA_SEG_SIZE; -extern Bit16u DOS_SDA_OFS;// 0 -extern Bit16u DOS_CDS_SEG;// 0x108 -extern Bit16u DOS_MEM_START;// 0x158 // regression to r3437 fixes nascar 2 colors - -extern Bit16u DOS_PRIVATE_SEGMENT;// 0xc800 -extern Bit16u DOS_PRIVATE_SEGMENT_END;// 0xd000 - -/* internal Dos Tables */ - -extern DOS_File ** Files; -extern DOS_Drive * Drives[DOS_DRIVES]; -extern DOS_Device * Devices[DOS_DEVICES]; - -extern Bit8u dos_copybuf[0x10000]; - - -void DOS_SetError(Bit16u code); - -/* File Handling Routines */ - -enum { STDIN=0,STDOUT=1,STDERR=2,STDAUX=3,STDPRN=4}; -enum { HAND_NONE=0,HAND_FILE,HAND_DEVICE}; - -/* Routines for File Class */ -void DOS_SetupFiles (void); -bool DOS_ReadFile(Bit16u handle,Bit8u * data,Bit16u * amount); -bool DOS_WriteFile(Bit16u handle,Bit8u * data,Bit16u * amount); -bool DOS_SeekFile(Bit16u handle,Bit32u * pos,Bit32u type); -/* ert, 20100711: Locking extensions */ -bool DOS_LockFile(Bit16u entry,Bit8u mode,Bit32u pos,Bit32u size); -bool DOS_CloseFile(Bit16u handle); -bool DOS_FlushFile(Bit16u handle); -bool DOS_DuplicateEntry(Bit16u entry,Bit16u * newentry); -bool DOS_ForceDuplicateEntry(Bit16u entry,Bit16u newentry); -bool DOS_GetFileDate(Bit16u entry, Bit16u* otime, Bit16u* odate); -bool DOS_SetFileDate(Bit16u entry, Bit16u ntime, Bit16u ndate); - -/* Routines for Drive Class */ -bool DOS_OpenFile(char const * name,Bit8u flags,Bit16u * entry); -bool DOS_OpenFileExtended(char const * name, Bit16u flags, Bit16u createAttr, Bit16u action, Bit16u *entry, Bit16u* status); -bool DOS_CreateFile(char const * name,Bit16u attribute,Bit16u * entry); -bool DOS_UnlinkFile(char const * const name); -bool DOS_FindFirst(char *search,Bit16u attr,bool fcb_findfirst=false); -bool DOS_FindNext(void); -bool DOS_Canonicalize(char const * const name,char * const big); -bool DOS_CreateTempFile(char * const name,Bit16u * entry); -bool DOS_FileExists(char const * const name); - -/* Helper Functions */ -bool DOS_MakeName(char const * const name,char * const fullname,Bit8u * drive); -/* Drive Handing Routines */ -Bit8u DOS_GetDefaultDrive(void); -void DOS_SetDefaultDrive(Bit8u drive); -bool DOS_SetDrive(Bit8u drive); -bool DOS_GetCurrentDir(Bit8u drive,char * const buffer); -bool DOS_ChangeDir(char const * const dir); -bool DOS_MakeDir(char const * const dir); -bool DOS_RemoveDir(char const * const dir); -bool DOS_Rename(char const * const oldname,char const * const newname); -bool DOS_GetFreeDiskSpace(Bit8u drive,Bit16u * bytes,Bit8u * sectors,Bit16u * clusters,Bit16u * free); -bool DOS_GetFileAttr(char const * const name,Bit16u * attr); -bool DOS_SetFileAttr(char const * const name,Bit16u attr); - -/* IOCTL Stuff */ -bool DOS_IOCTL(void); -bool DOS_GetSTDINStatus(); -Bit8u DOS_FindDevice(char const * name); -void DOS_SetupDevices(void); - -/* Execute and new process creation */ -bool DOS_NewPSP(Bit16u pspseg,Bit16u size); -bool DOS_ChildPSP(Bit16u pspseg,Bit16u size); -bool DOS_Execute(char * name,PhysPt block,Bit8u flags); -void DOS_Terminate(Bit16u pspseg,bool tsr,Bit8u exitcode); - -/* Memory Handling Routines */ -void DOS_SetupMemory(void); -bool DOS_AllocateMemory(Bit16u * segment,Bit16u * blocks); -bool DOS_ResizeMemory(Bit16u segment,Bit16u * blocks); -bool DOS_FreeMemory(Bit16u segment); -void DOS_FreeProcessMemory(Bit16u pspseg); -Bit16u BIOS_GetMemory(Bit16u pages,const char *who=NULL); -Bit16u DOS_GetMemory(Bit16u pages,const char *who=NULL); -bool DOS_SetMemAllocStrategy(Bit16u strat); -Bit16u DOS_GetMemAllocStrategy(void); -void DOS_BuildUMBChain(bool umb_active,bool ems_active); -bool DOS_LinkUMBsToMemChain(Bit16u linkstate); - -/* FCB stuff */ -bool DOS_FCBOpen(Bit16u seg,Bit16u offset); -bool DOS_FCBCreate(Bit16u seg,Bit16u offset); -bool DOS_FCBClose(Bit16u seg,Bit16u offset); -bool DOS_FCBFindFirst(Bit16u seg,Bit16u offset); -bool DOS_FCBFindNext(Bit16u seg,Bit16u offset); -Bit8u DOS_FCBRead(Bit16u seg,Bit16u offset, Bit16u numBlocks); -Bit8u DOS_FCBWrite(Bit16u seg,Bit16u offset,Bit16u numBlocks); -Bit8u DOS_FCBRandomRead(Bit16u seg,Bit16u offset,Bit16u * numRec,bool restore); -Bit8u DOS_FCBRandomWrite(Bit16u seg,Bit16u offset,Bit16u * numRec,bool restore); -bool DOS_FCBGetFileSize(Bit16u seg,Bit16u offset); -bool DOS_FCBDeleteFile(Bit16u seg,Bit16u offset); -bool DOS_FCBRenameFile(Bit16u seg, Bit16u offset); -void DOS_FCBSetRandomRecord(Bit16u seg, Bit16u offset); -Bit8u FCB_Parsename(Bit16u seg,Bit16u offset,Bit8u parser ,char *string, Bit8u *change); -bool DOS_GetAllocationInfo(Bit8u drive,Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters); - -/* Extra DOS Interrupts */ -void DOS_SetupMisc(void); - -/* The DOS Tables */ -void DOS_SetupTables(void); - -/* Internal DOS Setup Programs */ -void DOS_SetupPrograms(void); - -/* Initialize Keyboard Layout */ -void DOS_KeyboardLayout_Init(Section* sec); - -bool DOS_LayoutKey(Bitu key, Bit8u flags1, Bit8u flags2, Bit8u flags3); - -enum { - KEYB_NOERROR=0, - KEYB_FILENOTFOUND, - KEYB_INVALIDFILE, - KEYB_LAYOUTNOTFOUND, - KEYB_INVALIDCPFILE -}; - - -static INLINE Bit16u long2para(Bit32u size) { - if (size>0xFFFF0) return 0xffff; - if (size&0xf) return (Bit16u)((size>>4)+1); - else return (Bit16u)(size>>4); -} - - -static INLINE Bit16u DOS_PackTime(Bit16u hour,Bit16u min,Bit16u sec) { - return (hour&0x1f)<<11 | (min&0x3f) << 5 | ((sec/2)&0x1f); -} - -static INLINE Bit16u DOS_PackDate(Bit16u year,Bit16u mon,Bit16u day) { - return ((year-1980)&0x7f)<<9 | (mon&0x3f) << 5 | (day&0x1f); -} - -/* fopen64, ftello64, fseeko64 */ -#if defined(__APPLE__) - #define fopen64 fopen - #define ftello64 ftell - #define fseeko64 fseek -#elif defined (__HAIKU__) - #define fopen64 fopen - #define ftello64 ftello - #define fseeko64 fseeko -#elif defined (_MSC_VER) - #define fopen64 fopen - #if (_MSC_VER >= 1400) - #define ftello64 _ftelli64 - #define fseeko64 _fseeki64 - #else - #define ftello64 ftell - #define fseeko64 fseek - #endif -#endif - -/* Dos Error Codes */ -#define DOSERR_NONE 0 -#define DOSERR_FUNCTION_NUMBER_INVALID 1 -#define DOSERR_FILE_NOT_FOUND 2 -#define DOSERR_PATH_NOT_FOUND 3 -#define DOSERR_TOO_MANY_OPEN_FILES 4 -#define DOSERR_ACCESS_DENIED 5 -#define DOSERR_INVALID_HANDLE 6 -#define DOSERR_MCB_DESTROYED 7 -#define DOSERR_INSUFFICIENT_MEMORY 8 -#define DOSERR_MB_ADDRESS_INVALID 9 -#define DOSERR_ENVIRONMENT_INVALID 10 -#define DOSERR_FORMAT_INVALID 11 -#define DOSERR_ACCESS_CODE_INVALID 12 -#define DOSERR_DATA_INVALID 13 -#define DOSERR_RESERVED 14 -#define DOSERR_FIXUP_OVERFLOW 14 -#define DOSERR_INVALID_DRIVE 15 -#define DOSERR_REMOVE_CURRENT_DIRECTORY 16 -#define DOSERR_NOT_SAME_DEVICE 17 -#define DOSERR_NO_MORE_FILES 18 -#define DOSERR_WRITE_PROTECTED 19 -#define DOSERR_FILE_ALREADY_EXISTS 80 - - -/* Remains some classes used to access certain things */ -#define sOffset(s,m) ((char*)&(((s*)NULL)->m)-(char*)NULL) -#define sGet(s,m) GetIt(sizeof(((s *)&pt)->m),(PhysPt)sOffset(s,m)) -#define sSave(s,m,val) SaveIt(sizeof(((s *)&pt)->m),(PhysPt)sOffset(s,m),val) - -class MemStruct { -public: - inline Bit32u GetIt(const Bit32u size, const PhysPt addr) { - switch (size) { - case 1:return mem_readb(pt+addr); - case 2:return mem_readw(pt+addr); - case 4:return mem_readd(pt+addr); - } - return 0; - } - inline void SaveIt(const Bit32u size, const PhysPt addr, const Bit32u val) { - switch (size) { - case 1:mem_writeb(pt+addr,(Bit8u)val);break; - case 2:mem_writew(pt+addr,(Bit16u)val);break; - case 4:mem_writed(pt+addr,(Bit32u)val);break; - } - } - inline void SetPt(const Bit16u seg) { pt=PhysMake(seg,0);} - inline void SetPt(const Bit16u seg, const Bit16u off) { pt=PhysMake(seg,off);} - inline void SetPt(const RealPt addr) { pt=Real2Phys(addr);} -protected: - PhysPt pt; -}; - -class DOS_PSP :public MemStruct { -public: - DOS_PSP (Bit16u segment) { SetPt(segment);seg=segment;}; - void MakeNew (Bit16u memSize); - void CopyFileTable (DOS_PSP* srcpsp,bool createchildpsp); - Bit16u FindFreeFileEntry (void); - void CloseFiles (void); - - void SaveVectors (void); - void RestoreVectors (void); - void SetSize (Bit16u size) { sSave(sPSP,next_seg,size); }; - Bit16u GetSize (void) { return (Bit16u)sGet(sPSP,next_seg); }; - void SetEnvironment (Bit16u envseg) { sSave(sPSP,environment,envseg); }; - Bit16u GetEnvironment (void) { return (Bit16u)sGet(sPSP,environment); }; - Bit16u GetSegment (void) { return seg; }; - void SetFileHandle (Bit16u index, Bit8u handle); - Bit8u GetFileHandle (Bit16u index); - void SetParent (Bit16u parent) { sSave(sPSP,psp_parent,parent); }; - Bit16u GetParent (void) { return (Bit16u)sGet(sPSP,psp_parent); }; - void SetStack (RealPt stackpt) { sSave(sPSP,stack,stackpt); }; - RealPt GetStack (void) { return sGet(sPSP,stack); }; - void SetInt22 (RealPt int22pt) { sSave(sPSP,int_22,int22pt); }; - RealPt GetInt22 (void) { return sGet(sPSP,int_22); }; - void SetFCB1 (RealPt src); - void SetFCB2 (RealPt src); - void SetCommandTail (RealPt src); - bool SetNumFiles (Bit16u fileNum); - Bit16u FindEntryByHandle (Bit8u handle); - -private: - #ifdef _MSC_VER - #pragma pack(1) - #endif - struct sPSP { - Bit8u exit[2]; /* CP/M-like exit poimt */ - Bit16u next_seg; /* Segment of first byte beyond memory allocated or program */ - Bit8u fill_1; /* single char fill */ - Bit8u far_call; /* far call opcode */ - RealPt cpm_entry; /* CPM Service Request address*/ - RealPt int_22; /* Terminate Address */ - RealPt int_23; /* Break Address */ - RealPt int_24; /* Critical Error Address */ - Bit16u psp_parent; /* Parent PSP Segment */ - Bit8u files[20]; /* File Table - 0xff is unused */ - Bit16u environment; /* Segment of evironment table */ - RealPt stack; /* SS:SP Save point for int 0x21 calls */ - Bit16u max_files; /* Maximum open files */ - RealPt file_table; /* Pointer to File Table PSP:0x18 */ - RealPt prev_psp; /* Pointer to previous PSP */ - Bit8u interim_flag; - Bit8u truename_flag; - Bit16u nn_flags; - Bit16u dos_version; - Bit8u fill_2[14]; /* Lot's of unused stuff i can't care aboue */ - Bit8u service[3]; /* INT 0x21 Service call int 0x21;retf; */ - Bit8u fill_3[9]; /* This has some blocks with FCB info */ - Bit8u fcb1[16]; /* first FCB */ - Bit8u fcb2[16]; /* second FCB */ - Bit8u fill_4[4]; /* unused */ - CommandTail cmdtail; - } GCC_ATTRIBUTE(packed); - #ifdef _MSC_VER - #pragma pack() - #endif - Bit16u seg; -public: - static Bit16u rootpsp; -}; - -class DOS_ParamBlock:public MemStruct { -public: - DOS_ParamBlock(PhysPt addr) {pt=addr;} - void Clear(void); - void LoadData(void); - void SaveData(void); /* Save it as an exec block */ - #ifdef _MSC_VER - #pragma pack (1) - #endif - struct sOverlay { - Bit16u loadseg; - Bit16u relocation; - } GCC_ATTRIBUTE(packed); - struct sExec { - Bit16u envseg; - RealPt cmdtail; - RealPt fcb1; - RealPt fcb2; - RealPt initsssp; - RealPt initcsip; - }GCC_ATTRIBUTE(packed); - #ifdef _MSC_VER - #pragma pack() - #endif - sExec exec; - sOverlay overlay; -}; - -class DOS_InfoBlock:public MemStruct { -public: - DOS_InfoBlock () {}; - void SetLocation(Bit16u seg); - void SetFirstMCB(Bit16u _first_mcb); - void SetBuffers(Bit16u x,Bit16u y); - void SetCurDirStruct(Bit32u _curdirstruct); - void SetFCBTable(Bit32u _fcbtable); - void SetDeviceChainStart(Bit32u _devchain); - void SetDiskBufferHeadPt(Bit32u _dbheadpt); - void SetStartOfUMBChain(Bit16u _umbstartseg); - void SetUMBChainState(Bit8u _umbchaining); - Bit16u GetStartOfUMBChain(void); - Bit8u GetUMBChainState(void); - RealPt GetPointer(void); - Bit32u GetDeviceChain(void); - - #ifdef _MSC_VER - #pragma pack(1) - #endif - struct sDIB { - Bit8u unknown1[4]; - Bit16u magicWord; // -0x22 needs to be 1 - Bit8u unknown2[8]; - Bit16u regCXfrom5e; // -0x18 CX from last int21/ah=5e - Bit16u countLRUcache; // -0x16 LRU counter for FCB caching - Bit16u countLRUopens; // -0x14 LRU counter for FCB openings - Bit8u stuff[6]; // -0x12 some stuff, hopefully never used.... - Bit16u sharingCount; // -0x0c sharing retry count - Bit16u sharingDelay; // -0x0a sharing retry delay - RealPt diskBufPtr; // -0x08 pointer to disk buffer - Bit16u ptrCONinput; // -0x04 pointer to con input - Bit16u firstMCB; // -0x02 first memory control block - RealPt firstDPB; // 0x00 first drive parameter block - RealPt firstFileTable; // 0x04 first system file table - RealPt activeClock; // 0x08 active clock device header - RealPt activeCon; // 0x0c active console device header - Bit16u maxSectorLength; // 0x10 maximum bytes per sector of any block device; - RealPt diskInfoBuffer; // 0x12 pointer to disk info buffer - RealPt curDirStructure; // 0x16 pointer to current array of directory structure - RealPt fcbTable; // 0x1a pointer to system FCB table - Bit16u protFCBs; // 0x1e protected fcbs - Bit8u blockDevices; // 0x20 installed block devices - Bit8u lastdrive; // 0x21 lastdrive - Bit32u nulNextDriver; // 0x22 NUL driver next pointer - Bit16u nulAttributes; // 0x26 NUL driver aattributes - Bit16u nulStrategy; // 0x28 NUL driver strategy routine - Bit16u nulInterrupt; // 0x2A NUL driver interrupt routine - Bit8u nulString[8]; // 0x2c NUL driver name string - Bit8u joindedDrives; // 0x34 joined drives - Bit16u specialCodeSeg; // 0x35 special code segment - RealPt setverPtr; // 0x37 pointer to setver - Bit16u a20FixOfs; // 0x3b a20 fix routine offset - Bit16u pspLastIfHMA; // 0x3d psp of last program (if dos in hma) - Bit16u buffers_x; // 0x3f x in BUFFERS x,y - Bit16u buffers_y; // 0x41 y in BUFFERS x,y - Bit8u bootDrive; // 0x43 boot drive - Bit8u useDwordMov; // 0x44 use dword moves - Bit16u extendedSize; // 0x45 size of extended memory - Bit32u diskBufferHeadPt; // 0x47 pointer to least-recently used buffer header - Bit16u dirtyDiskBuffers; // 0x4b number of dirty disk buffers - Bit32u lookaheadBufPt; // 0x4d pointer to lookahead buffer - Bit16u lookaheadBufNumber; // 0x51 number of lookahead buffers - Bit8u bufferLocation; // 0x53 workspace buffer location - Bit32u workspaceBuffer; // 0x54 pointer to workspace buffer - Bit8u unknown3[11]; // 0x58 - Bit8u chainingUMB; // 0x63 bit0: UMB chain linked to MCB chain - Bit16u minMemForExec; // 0x64 minimum paragraphs needed for current program - Bit16u startOfUMBChain; // 0x66 segment of first UMB-MCB - Bit16u memAllocScanStart; // 0x68 start paragraph for memory allocation - } GCC_ATTRIBUTE(packed); - #ifdef _MSC_VER - #pragma pack () - #endif - Bit16u seg; -}; - -class DOS_DTA:public MemStruct{ -public: - DOS_DTA(RealPt addr) { SetPt(addr); } - - void SetupSearch(Bit8u _sdrive,Bit8u _sattr,char * _pattern); - void SetResult(const char * _name,Bit32u _size,Bit16u _date,Bit16u _time,Bit8u _attr); - - Bit8u GetSearchDrive(void); - void GetSearchParams(Bit8u & _sattr,char * _spattern); - void GetResult(char * _name,Bit32u & _size,Bit16u & _date,Bit16u & _time,Bit8u & _attr); - - void SetDirID(Bit16u entry) { sSave(sDTA,dirID,entry); }; - void SetDirIDCluster(Bit16u entry) { sSave(sDTA,dirCluster,entry); }; - Bit16u GetDirID(void) { return (Bit16u)sGet(sDTA,dirID); }; - Bit16u GetDirIDCluster(void) { return (Bit16u)sGet(sDTA,dirCluster); }; -private: - #ifdef _MSC_VER - #pragma pack(1) - #endif - struct sDTA { - Bit8u sdrive; /* The Drive the search is taking place */ - Bit8u sname[8]; /* The Search pattern for the filename */ - Bit8u sext[3]; /* The Search pattern for the extenstion */ - Bit8u sattr; /* The Attributes that need to be found */ - Bit16u dirID; /* custom: dir-search ID for multiple searches at the same time */ - Bit16u dirCluster; /* custom (drive_fat only): cluster number for multiple searches at the same time */ - Bit8u fill[4]; - Bit8u attr; - Bit16u time; - Bit16u date; - Bit32u size; - char name[DOS_NAMELENGTH_ASCII]; - } GCC_ATTRIBUTE(packed); - #ifdef _MSC_VER - #pragma pack() - #endif -}; - -class DOS_FCB: public MemStruct { -public: - DOS_FCB(Bit16u seg,Bit16u off,bool allow_extended=true); - void Create(bool _extended); - void SetName(Bit8u _drive,char * _fname,char * _ext); - void SetSizeDateTime(Bit32u _size,Bit16u _date,Bit16u _time); - void GetSizeDateTime(Bit32u & _size,Bit16u & _date,Bit16u & _time); - void GetName(char * fillname); - void FileOpen(Bit8u _fhandle); - void FileClose(Bit8u & _fhandle); - void GetRecord(Bit16u & _cur_block,Bit8u & _cur_rec); - void SetRecord(Bit16u _cur_block,Bit8u _cur_rec); - void GetSeqData(Bit8u & _fhandle,Bit16u & _rec_size); - void GetRandom(Bit32u & _random); - void SetRandom(Bit32u _random); - Bit8u GetDrive(void); - bool Extended(void); - void GetAttr(Bit8u & attr); - void SetAttr(Bit8u attr); - void SetResultAttr(Bit8u attr); - bool Valid(void); -private: - bool extended; - PhysPt real_pt; - #ifdef _MSC_VER - #pragma pack (1) - #endif - struct sFCB { - Bit8u drive; /* Drive number 0=default, 1=A, etc */ - Bit8u filename[8]; /* Space padded name */ - Bit8u ext[3]; /* Space padded extension */ - Bit16u cur_block; /* Current Block */ - Bit16u rec_size; /* Logical record size */ - Bit32u filesize; /* File Size */ - Bit16u date; - Bit16u time; - /* Reserved Block should be 8 bytes */ - Bit8u sft_entries; - Bit8u share_attributes; - Bit8u extra_info; - Bit8u file_handle; - Bit8u reserved[4]; - /* end */ - Bit8u cur_rec; /* Current record in current block */ - Bit32u rndm; /* Current relative record number */ - } GCC_ATTRIBUTE(packed); - #ifdef _MSC_VER - #pragma pack () - #endif -}; - -class DOS_MCB : public MemStruct{ -public: - DOS_MCB(Bit16u seg) { SetPt(seg); } - void SetFileName(const char * const _name) { MEM_BlockWrite(pt+offsetof(sMCB,filename),_name,8); } - void GetFileName(char * const _name) { MEM_BlockRead(pt+offsetof(sMCB,filename),_name,8);_name[8]=0;} - void SetType(Bit8u _type) { sSave(sMCB,type,_type);} - void SetSize(Bit16u _size) { sSave(sMCB,size,_size);} - void SetPSPSeg(Bit16u _pspseg) { sSave(sMCB,psp_segment,_pspseg);} - Bit8u GetType(void) { return (Bit8u)sGet(sMCB,type);} - Bit16u GetSize(void) { return (Bit16u)sGet(sMCB,size);} - Bit16u GetPSPSeg(void) { return (Bit16u)sGet(sMCB,psp_segment);} -private: - #ifdef _MSC_VER - #pragma pack (1) - #endif - struct sMCB { - Bit8u type; - Bit16u psp_segment; - Bit16u size; - Bit8u unused[3]; - Bit8u filename[8]; - } GCC_ATTRIBUTE(packed); - #ifdef _MSC_VER - #pragma pack () - #endif -}; - -class DOS_SDA : public MemStruct { -public: - DOS_SDA(Bit16u _seg,Bit16u _offs) { SetPt(_seg,_offs); } - void Init(); - void SetDrive(Bit8u _drive) { sSave(sSDA,current_drive, _drive); } - void SetDTA(Bit32u _dta) { sSave(sSDA,current_dta, _dta); } - void SetPSP(Bit16u _psp) { sSave(sSDA,current_psp, _psp); } - Bit8u GetDrive(void) { return (Bit8u)sGet(sSDA,current_drive); } - Bit16u GetPSP(void) { return (Bit16u)sGet(sSDA,current_psp); } - Bit32u GetDTA(void) { return (Bit32u)sGet(sSDA,current_dta); } - - -private: - #ifdef _MSC_VER - #pragma pack (1) - #endif - struct sSDA { - Bit8u crit_error_flag; /* 0x00 Critical Error Flag */ - Bit8u inDOS_flag; /* 0x01 InDOS flag (count of active INT 21 calls) */ - Bit8u drive_crit_error; /* 0x02 Drive on which current critical error occurred or FFh */ - Bit8u locus_of_last_error; /* 0x03 locus of last error */ - Bit16u extended_error_code; /* 0x04 extended error code of last error */ - Bit8u suggested_action; /* 0x06 suggested action for last error */ - Bit8u error_class; /* 0x07 class of last error*/ - Bit32u last_error_pointer; /* 0x08 ES:DI pointer for last error */ - Bit32u current_dta; /* 0x0C current DTA (Disk Transfer Address) */ - Bit16u current_psp; /* 0x10 current PSP */ - Bit16u sp_int_23; /* 0x12 stores SP across an INT 23 */ - Bit16u return_code; /* 0x14 return code from last process termination (zerod after reading with AH=4Dh) */ - Bit8u current_drive; /* 0x16 current drive */ - Bit8u extended_break_flag; /* 0x17 extended break flag */ - Bit8u fill[2]; /* 0x18 flag: code page switching || flag: copy of previous byte in case of INT 24 Abort*/ - } GCC_ATTRIBUTE(packed); - #ifdef _MSC_VER - #pragma pack() - #endif -}; -extern DOS_InfoBlock dos_infoblock; - -struct DOS_Block { - DOS_Date date; - DOS_Version version; - Bit16u firstMCB; - Bit16u errorcode; - Bit16u psp();//{return DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).GetPSP();}; - void psp(Bit16u _seg);//{ DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetPSP(_seg);}; - RealPt dta();//{return DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).GetDTA();}; - void dta(RealPt _dta);//{DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetDTA(_dta);}; - Bit8u return_code,return_mode; - - Bit8u current_drive; - bool verify; - bool breakcheck; - bool echo; // if set to true dev_con::read will echo input - struct { - RealPt mediaid; - RealPt tempdta; - RealPt tempdta_fcbdelete; - RealPt dbcs; - RealPt filenamechar; - RealPt collatingseq; - RealPt upcase; - Bit8u* country;//Will be copied to dos memory. resides in real mem - Bit16u dpb; //Fake Disk parameter system using only the first entry so the drive letter matches - } tables; - Bit16u loaded_codepage; -}; - -extern DOS_Block dos; - -static INLINE Bit8u RealHandle(Bit16u handle) { - DOS_PSP psp(dos.psp()); - return psp.GetFileHandle(handle); -} - -struct DOS_GetMemLog_Entry { - Bit16u segbase; - Bit16u pages; - std::string who; -}; - -extern std::list DOS_GetMemLog; - -#endif +/* + * Copyright (C) 2002-2015 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef DOSBOX_DOS_INC_H +#define DOSBOX_DOS_INC_H + +#include + +#ifndef DOSBOX_DOS_SYSTEM_H +#include "dos_system.h" +#endif +#ifndef DOSBOX_MEM_H +#include "mem.h" +#endif + +#include //for offsetof + +#ifdef _MSC_VER +#pragma pack (1) +#endif +struct CommandTail{ + Bit8u count; /* number of bytes returned */ + char buffer[127]; /* the buffer itself */ +} GCC_ATTRIBUTE(packed); +#ifdef _MSC_VER +#pragma pack () +#endif + +extern Bit16u first_umb_seg; +extern Bit16u first_umb_size; + +bool MEM_unmap_physmem(Bitu start,Bitu end); +bool MEM_map_RAM_physmem(Bitu start,Bitu end); + +struct BuiltinFileBlob { + const char *recommended_file_name; + const unsigned char *data; + size_t length; +}; + +struct DOS_Date { + Bit16u year; + Bit8u month; + Bit8u day; +}; + +struct DOS_Version { + Bit8u major,minor,revision; +}; + +#ifndef MACOSX +#if defined (__APPLE__) +#define MACOSX 1 +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef signed char INT8, *PINT8; +typedef signed short INT16, *PINT16; +typedef signed int INT32, *PINT32; +//typedef signed __int64 INT64, *PINT64; +typedef unsigned char UINT8, *PUINT8; +typedef unsigned short UINT16, *PUINT16; +typedef unsigned int UINT32, *PUINT32; +//typedef unsigned __int64 UINT64, *PUINT64; +#ifdef __cplusplus +} +#endif + +#define SECTOR_SIZE_MAX 2048 + +#ifdef _MSC_VER +#pragma pack (1) +#endif +union bootSector { + struct entries { + Bit8u jump[3]; + Bit8u oem_name[8]; + Bit16u bytesect; + Bit8u sectclust; + Bit16u reserve_sect; + } bootdata; + Bit8u rawdata[SECTOR_SIZE_MAX]; +} GCC_ATTRIBUTE(packed); +#ifdef _MSC_VER +#pragma pack () +#endif + + +enum { MCB_FREE=0x0000,MCB_DOS=0x0008 }; +enum { RETURN_EXIT=0,RETURN_CTRLC=1,RETURN_ABORT=2,RETURN_TSR=3}; + +extern Bitu DOS_FILES; + +#define DOS_DRIVES 26 +#define DOS_DEVICES 10 + + +#if 0 /* ORIGINAL DEFINES FOR REFERENCE */ +// dos swappable area is 0x320 bytes beyond the sysvars table +// device driver chain is inside sysvars +#define DOS_INFOBLOCK_SEG 0x80 // sysvars (list of lists) +#define DOS_CONDRV_SEG 0xa0 +#define DOS_CONSTRING_SEG 0xa8 +#define DOS_SDA_SEG 0xb2 // dos swappable area +#define DOS_SDA_OFS 0 +#define DOS_CDS_SEG 0x108 +#define DOS_MEM_START 0x158 // regression to r3437 fixes nascar 2 colors +//#define DOS_MEM_START 0x16f //First Segment that DOS can use + +#define DOS_PRIVATE_SEGMENT 0xc800 +#define DOS_PRIVATE_SEGMENT_END 0xd000 +#endif + +// dos swappable area is 0x320 bytes beyond the sysvars table +// device driver chain is inside sysvars +extern Bit16u DOS_INFOBLOCK_SEG;// 0x80 // sysvars (list of lists) +extern Bit16u DOS_CONDRV_SEG;// 0xa0 +extern Bit16u DOS_CONSTRING_SEG;// 0xa8 +extern Bit16u DOS_SDA_SEG;// 0xb2 // dos swappable area +extern Bit16u DOS_SDA_SEG_SIZE; +extern Bit16u DOS_SDA_OFS;// 0 +extern Bit16u DOS_CDS_SEG;// 0x108 +extern Bit16u DOS_MEM_START;// 0x158 // regression to r3437 fixes nascar 2 colors + +extern Bit16u DOS_PRIVATE_SEGMENT;// 0xc800 +extern Bit16u DOS_PRIVATE_SEGMENT_END;// 0xd000 + +/* internal Dos Tables */ + +extern DOS_File ** Files; +extern DOS_Drive * Drives[DOS_DRIVES]; +extern DOS_Device * Devices[DOS_DEVICES]; + +extern Bit8u dos_copybuf[0x10000]; + + +void DOS_SetError(Bit16u code); + +/* File Handling Routines */ + +enum { STDIN=0,STDOUT=1,STDERR=2,STDAUX=3,STDPRN=4}; +enum { HAND_NONE=0,HAND_FILE,HAND_DEVICE}; + +/* Routines for File Class */ +void DOS_SetupFiles (void); +bool DOS_ReadFile(Bit16u handle,Bit8u * data,Bit16u * amount); +bool DOS_WriteFile(Bit16u handle,Bit8u * data,Bit16u * amount); +bool DOS_SeekFile(Bit16u handle,Bit32u * pos,Bit32u type); +/* ert, 20100711: Locking extensions */ +bool DOS_LockFile(Bit16u entry,Bit8u mode,Bit32u pos,Bit32u size); +bool DOS_CloseFile(Bit16u handle); +bool DOS_FlushFile(Bit16u handle); +bool DOS_DuplicateEntry(Bit16u entry,Bit16u * newentry); +bool DOS_ForceDuplicateEntry(Bit16u entry,Bit16u newentry); +bool DOS_GetFileDate(Bit16u entry, Bit16u* otime, Bit16u* odate); +bool DOS_SetFileDate(Bit16u entry, Bit16u ntime, Bit16u ndate); + +/* Routines for Drive Class */ +bool DOS_OpenFile(char const * name,Bit8u flags,Bit16u * entry); +bool DOS_OpenFileExtended(char const * name, Bit16u flags, Bit16u createAttr, Bit16u action, Bit16u *entry, Bit16u* status); +bool DOS_CreateFile(char const * name,Bit16u attribute,Bit16u * entry); +bool DOS_UnlinkFile(char const * const name); +bool DOS_FindFirst(char *search,Bit16u attr,bool fcb_findfirst=false); +bool DOS_FindNext(void); +bool DOS_Canonicalize(char const * const name,char * const big); +bool DOS_CreateTempFile(char * const name,Bit16u * entry); +bool DOS_FileExists(char const * const name); + +/* Helper Functions */ +bool DOS_MakeName(char const * const name,char * const fullname,Bit8u * drive); +/* Drive Handing Routines */ +Bit8u DOS_GetDefaultDrive(void); +void DOS_SetDefaultDrive(Bit8u drive); +bool DOS_SetDrive(Bit8u drive); +bool DOS_GetCurrentDir(Bit8u drive,char * const buffer); +bool DOS_ChangeDir(char const * const dir); +bool DOS_MakeDir(char const * const dir); +bool DOS_RemoveDir(char const * const dir); +bool DOS_Rename(char const * const oldname,char const * const newname); +bool DOS_GetFreeDiskSpace(Bit8u drive,Bit16u * bytes,Bit8u * sectors,Bit16u * clusters,Bit16u * free); +bool DOS_GetFileAttr(char const * const name,Bit16u * attr); +bool DOS_SetFileAttr(char const * const name,Bit16u attr); + +/* IOCTL Stuff */ +bool DOS_IOCTL(void); +bool DOS_GetSTDINStatus(); +Bit8u DOS_FindDevice(char const * name); +void DOS_SetupDevices(void); + +/* Execute and new process creation */ +bool DOS_NewPSP(Bit16u pspseg,Bit16u size); +bool DOS_ChildPSP(Bit16u pspseg,Bit16u size); +bool DOS_Execute(char * name,PhysPt block,Bit8u flags); +void DOS_Terminate(Bit16u pspseg,bool tsr,Bit8u exitcode); + +/* Memory Handling Routines */ +void DOS_SetupMemory(void); +bool DOS_AllocateMemory(Bit16u * segment,Bit16u * blocks); +bool DOS_ResizeMemory(Bit16u segment,Bit16u * blocks); +bool DOS_FreeMemory(Bit16u segment); +void DOS_FreeProcessMemory(Bit16u pspseg); +Bit16u BIOS_GetMemory(Bit16u pages,const char *who=NULL); +Bit16u DOS_GetMemory(Bit16u pages,const char *who=NULL); +bool DOS_SetMemAllocStrategy(Bit16u strat); +Bit16u DOS_GetMemAllocStrategy(void); +void DOS_BuildUMBChain(bool umb_active,bool ems_active); +bool DOS_LinkUMBsToMemChain(Bit16u linkstate); + +/* FCB stuff */ +bool DOS_FCBOpen(Bit16u seg,Bit16u offset); +bool DOS_FCBCreate(Bit16u seg,Bit16u offset); +bool DOS_FCBClose(Bit16u seg,Bit16u offset); +bool DOS_FCBFindFirst(Bit16u seg,Bit16u offset); +bool DOS_FCBFindNext(Bit16u seg,Bit16u offset); +Bit8u DOS_FCBRead(Bit16u seg,Bit16u offset, Bit16u numBlocks); +Bit8u DOS_FCBWrite(Bit16u seg,Bit16u offset,Bit16u numBlocks); +Bit8u DOS_FCBRandomRead(Bit16u seg,Bit16u offset,Bit16u * numRec,bool restore); +Bit8u DOS_FCBRandomWrite(Bit16u seg,Bit16u offset,Bit16u * numRec,bool restore); +bool DOS_FCBGetFileSize(Bit16u seg,Bit16u offset); +bool DOS_FCBDeleteFile(Bit16u seg,Bit16u offset); +bool DOS_FCBRenameFile(Bit16u seg, Bit16u offset); +void DOS_FCBSetRandomRecord(Bit16u seg, Bit16u offset); +Bit8u FCB_Parsename(Bit16u seg,Bit16u offset,Bit8u parser ,char *string, Bit8u *change); +bool DOS_GetAllocationInfo(Bit8u drive,Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters); + +/* Extra DOS Interrupts */ +void DOS_SetupMisc(void); + +/* The DOS Tables */ +void DOS_SetupTables(void); + +/* Internal DOS Setup Programs */ +void DOS_SetupPrograms(void); + +/* Initialize Keyboard Layout */ +void DOS_KeyboardLayout_Init(Section* sec); + +bool DOS_LayoutKey(Bitu key, Bit8u flags1, Bit8u flags2, Bit8u flags3); + +enum { + KEYB_NOERROR=0, + KEYB_FILENOTFOUND, + KEYB_INVALIDFILE, + KEYB_LAYOUTNOTFOUND, + KEYB_INVALIDCPFILE +}; + + +static INLINE Bit16u long2para(Bit32u size) { + if (size>0xFFFF0) return 0xffff; + if (size&0xf) return (Bit16u)((size>>4)+1); + else return (Bit16u)(size>>4); +} + + +static INLINE Bit16u DOS_PackTime(Bit16u hour,Bit16u min,Bit16u sec) { + return (hour&0x1f)<<11 | (min&0x3f) << 5 | ((sec/2)&0x1f); +} + +static INLINE Bit16u DOS_PackDate(Bit16u year,Bit16u mon,Bit16u day) { + return ((year-1980)&0x7f)<<9 | (mon&0x3f) << 5 | (day&0x1f); +} + +/* fopen64, ftello64, fseeko64 */ +#if defined(__APPLE__) + #define fopen64 fopen + #define ftello64 ftell + #define fseeko64 fseek +#elif defined (__HAIKU__) + #define fopen64 fopen + #define ftello64 ftello + #define fseeko64 fseeko +#elif defined (_MSC_VER) + #define fopen64 fopen + #if (_MSC_VER >= 1400) + #define ftello64 _ftelli64 + #define fseeko64 _fseeki64 + #else + #define ftello64 ftell + #define fseeko64 fseek + #endif +#endif + +/* Dos Error Codes */ +#define DOSERR_NONE 0 +#define DOSERR_FUNCTION_NUMBER_INVALID 1 +#define DOSERR_FILE_NOT_FOUND 2 +#define DOSERR_PATH_NOT_FOUND 3 +#define DOSERR_TOO_MANY_OPEN_FILES 4 +#define DOSERR_ACCESS_DENIED 5 +#define DOSERR_INVALID_HANDLE 6 +#define DOSERR_MCB_DESTROYED 7 +#define DOSERR_INSUFFICIENT_MEMORY 8 +#define DOSERR_MB_ADDRESS_INVALID 9 +#define DOSERR_ENVIRONMENT_INVALID 10 +#define DOSERR_FORMAT_INVALID 11 +#define DOSERR_ACCESS_CODE_INVALID 12 +#define DOSERR_DATA_INVALID 13 +#define DOSERR_RESERVED 14 +#define DOSERR_FIXUP_OVERFLOW 14 +#define DOSERR_INVALID_DRIVE 15 +#define DOSERR_REMOVE_CURRENT_DIRECTORY 16 +#define DOSERR_NOT_SAME_DEVICE 17 +#define DOSERR_NO_MORE_FILES 18 +#define DOSERR_WRITE_PROTECTED 19 +#define DOSERR_FILE_ALREADY_EXISTS 80 + + +/* Remains some classes used to access certain things */ +#define sOffset(s,m) ((char*)&(((s*)NULL)->m)-(char*)NULL) +#define sGet(s,m) GetIt(sizeof(((s *)&pt)->m),(PhysPt)sOffset(s,m)) +#define sSave(s,m,val) SaveIt(sizeof(((s *)&pt)->m),(PhysPt)sOffset(s,m),val) + +class MemStruct { +public: + inline Bit32u GetIt(const Bit32u size, const PhysPt addr) { + switch (size) { + case 1:return mem_readb(pt+addr); + case 2:return mem_readw(pt+addr); + case 4:return mem_readd(pt+addr); + } + return 0; + } + inline void SaveIt(const Bit32u size, const PhysPt addr, const Bit32u val) { + switch (size) { + case 1:mem_writeb(pt+addr,(Bit8u)val);break; + case 2:mem_writew(pt+addr,(Bit16u)val);break; + case 4:mem_writed(pt+addr,(Bit32u)val);break; + } + } + inline void SetPt(const Bit16u seg) { pt=PhysMake(seg,0);} + inline void SetPt(const Bit16u seg, const Bit16u off) { pt=PhysMake(seg,off);} + inline void SetPt(const RealPt addr) { pt=Real2Phys(addr);} +protected: + PhysPt pt; +}; + +class DOS_PSP :public MemStruct { +public: + DOS_PSP (Bit16u segment) { SetPt(segment);seg=segment;}; + void MakeNew (Bit16u memSize); + void CopyFileTable (DOS_PSP* srcpsp,bool createchildpsp); + Bit16u FindFreeFileEntry (void); + void CloseFiles (void); + + void SaveVectors (void); + void RestoreVectors (void); + void SetSize (Bit16u size) { sSave(sPSP,next_seg,size); }; + Bit16u GetSize (void) { return (Bit16u)sGet(sPSP,next_seg); }; + void SetEnvironment (Bit16u envseg) { sSave(sPSP,environment,envseg); }; + Bit16u GetEnvironment (void) { return (Bit16u)sGet(sPSP,environment); }; + Bit16u GetSegment (void) { return seg; }; + void SetFileHandle (Bit16u index, Bit8u handle); + Bit8u GetFileHandle (Bit16u index); + void SetParent (Bit16u parent) { sSave(sPSP,psp_parent,parent); }; + Bit16u GetParent (void) { return (Bit16u)sGet(sPSP,psp_parent); }; + void SetStack (RealPt stackpt) { sSave(sPSP,stack,stackpt); }; + RealPt GetStack (void) { return sGet(sPSP,stack); }; + void SetInt22 (RealPt int22pt) { sSave(sPSP,int_22,int22pt); }; + RealPt GetInt22 (void) { return sGet(sPSP,int_22); }; + void SetFCB1 (RealPt src); + void SetFCB2 (RealPt src); + void SetCommandTail (RealPt src); + bool SetNumFiles (Bit16u fileNum); + Bit16u FindEntryByHandle (Bit8u handle); + +private: + #ifdef _MSC_VER + #pragma pack(1) + #endif + struct sPSP { + Bit8u exit[2]; /* CP/M-like exit poimt */ + Bit16u next_seg; /* Segment of first byte beyond memory allocated or program */ + Bit8u fill_1; /* single char fill */ + Bit8u far_call; /* far call opcode */ + RealPt cpm_entry; /* CPM Service Request address*/ + RealPt int_22; /* Terminate Address */ + RealPt int_23; /* Break Address */ + RealPt int_24; /* Critical Error Address */ + Bit16u psp_parent; /* Parent PSP Segment */ + Bit8u files[20]; /* File Table - 0xff is unused */ + Bit16u environment; /* Segment of evironment table */ + RealPt stack; /* SS:SP Save point for int 0x21 calls */ + Bit16u max_files; /* Maximum open files */ + RealPt file_table; /* Pointer to File Table PSP:0x18 */ + RealPt prev_psp; /* Pointer to previous PSP */ + Bit8u interim_flag; + Bit8u truename_flag; + Bit16u nn_flags; + Bit16u dos_version; + Bit8u fill_2[14]; /* Lot's of unused stuff i can't care aboue */ + Bit8u service[3]; /* INT 0x21 Service call int 0x21;retf; */ + Bit8u fill_3[9]; /* This has some blocks with FCB info */ + Bit8u fcb1[16]; /* first FCB */ + Bit8u fcb2[16]; /* second FCB */ + Bit8u fill_4[4]; /* unused */ + CommandTail cmdtail; + } GCC_ATTRIBUTE(packed); + #ifdef _MSC_VER + #pragma pack() + #endif + Bit16u seg; +public: + static Bit16u rootpsp; +}; + +class DOS_ParamBlock:public MemStruct { +public: + DOS_ParamBlock(PhysPt addr) {pt=addr;} + void Clear(void); + void LoadData(void); + void SaveData(void); /* Save it as an exec block */ + #ifdef _MSC_VER + #pragma pack (1) + #endif + struct sOverlay { + Bit16u loadseg; + Bit16u relocation; + } GCC_ATTRIBUTE(packed); + struct sExec { + Bit16u envseg; + RealPt cmdtail; + RealPt fcb1; + RealPt fcb2; + RealPt initsssp; + RealPt initcsip; + }GCC_ATTRIBUTE(packed); + #ifdef _MSC_VER + #pragma pack() + #endif + sExec exec; + sOverlay overlay; +}; + +class DOS_InfoBlock:public MemStruct { +public: + DOS_InfoBlock () {}; + void SetLocation(Bit16u seg); + void SetFirstMCB(Bit16u _first_mcb); + void SetBuffers(Bit16u x,Bit16u y); + void SetCurDirStruct(Bit32u _curdirstruct); + void SetFCBTable(Bit32u _fcbtable); + void SetDeviceChainStart(Bit32u _devchain); + void SetDiskBufferHeadPt(Bit32u _dbheadpt); + void SetStartOfUMBChain(Bit16u _umbstartseg); + void SetUMBChainState(Bit8u _umbchaining); + Bit16u GetStartOfUMBChain(void); + Bit8u GetUMBChainState(void); + RealPt GetPointer(void); + Bit32u GetDeviceChain(void); + + #ifdef _MSC_VER + #pragma pack(1) + #endif + struct sDIB { + Bit8u unknown1[4]; + Bit16u magicWord; // -0x22 needs to be 1 + Bit8u unknown2[8]; + Bit16u regCXfrom5e; // -0x18 CX from last int21/ah=5e + Bit16u countLRUcache; // -0x16 LRU counter for FCB caching + Bit16u countLRUopens; // -0x14 LRU counter for FCB openings + Bit8u stuff[6]; // -0x12 some stuff, hopefully never used.... + Bit16u sharingCount; // -0x0c sharing retry count + Bit16u sharingDelay; // -0x0a sharing retry delay + RealPt diskBufPtr; // -0x08 pointer to disk buffer + Bit16u ptrCONinput; // -0x04 pointer to con input + Bit16u firstMCB; // -0x02 first memory control block + RealPt firstDPB; // 0x00 first drive parameter block + RealPt firstFileTable; // 0x04 first system file table + RealPt activeClock; // 0x08 active clock device header + RealPt activeCon; // 0x0c active console device header + Bit16u maxSectorLength; // 0x10 maximum bytes per sector of any block device; + RealPt diskInfoBuffer; // 0x12 pointer to disk info buffer + RealPt curDirStructure; // 0x16 pointer to current array of directory structure + RealPt fcbTable; // 0x1a pointer to system FCB table + Bit16u protFCBs; // 0x1e protected fcbs + Bit8u blockDevices; // 0x20 installed block devices + Bit8u lastdrive; // 0x21 lastdrive + Bit32u nulNextDriver; // 0x22 NUL driver next pointer + Bit16u nulAttributes; // 0x26 NUL driver aattributes + Bit16u nulStrategy; // 0x28 NUL driver strategy routine + Bit16u nulInterrupt; // 0x2A NUL driver interrupt routine + Bit8u nulString[8]; // 0x2c NUL driver name string + Bit8u joindedDrives; // 0x34 joined drives + Bit16u specialCodeSeg; // 0x35 special code segment + RealPt setverPtr; // 0x37 pointer to setver + Bit16u a20FixOfs; // 0x3b a20 fix routine offset + Bit16u pspLastIfHMA; // 0x3d psp of last program (if dos in hma) + Bit16u buffers_x; // 0x3f x in BUFFERS x,y + Bit16u buffers_y; // 0x41 y in BUFFERS x,y + Bit8u bootDrive; // 0x43 boot drive + Bit8u useDwordMov; // 0x44 use dword moves + Bit16u extendedSize; // 0x45 size of extended memory + Bit32u diskBufferHeadPt; // 0x47 pointer to least-recently used buffer header + Bit16u dirtyDiskBuffers; // 0x4b number of dirty disk buffers + Bit32u lookaheadBufPt; // 0x4d pointer to lookahead buffer + Bit16u lookaheadBufNumber; // 0x51 number of lookahead buffers + Bit8u bufferLocation; // 0x53 workspace buffer location + Bit32u workspaceBuffer; // 0x54 pointer to workspace buffer + Bit8u unknown3[11]; // 0x58 + Bit8u chainingUMB; // 0x63 bit0: UMB chain linked to MCB chain + Bit16u minMemForExec; // 0x64 minimum paragraphs needed for current program + Bit16u startOfUMBChain; // 0x66 segment of first UMB-MCB + Bit16u memAllocScanStart; // 0x68 start paragraph for memory allocation + } GCC_ATTRIBUTE(packed); + #ifdef _MSC_VER + #pragma pack () + #endif + Bit16u seg; +}; + +class DOS_DTA:public MemStruct{ +public: + DOS_DTA(RealPt addr) { SetPt(addr); } + + void SetupSearch(Bit8u _sdrive,Bit8u _sattr,char * _pattern); + void SetResult(const char * _name,Bit32u _size,Bit16u _date,Bit16u _time,Bit8u _attr); + + Bit8u GetSearchDrive(void); + void GetSearchParams(Bit8u & _sattr,char * _spattern); + void GetResult(char * _name,Bit32u & _size,Bit16u & _date,Bit16u & _time,Bit8u & _attr); + + void SetDirID(Bit16u entry) { sSave(sDTA,dirID,entry); }; + void SetDirIDCluster(Bit16u entry) { sSave(sDTA,dirCluster,entry); }; + Bit16u GetDirID(void) { return (Bit16u)sGet(sDTA,dirID); }; + Bit16u GetDirIDCluster(void) { return (Bit16u)sGet(sDTA,dirCluster); }; +private: + #ifdef _MSC_VER + #pragma pack(1) + #endif + struct sDTA { + Bit8u sdrive; /* The Drive the search is taking place */ + Bit8u sname[8]; /* The Search pattern for the filename */ + Bit8u sext[3]; /* The Search pattern for the extenstion */ + Bit8u sattr; /* The Attributes that need to be found */ + Bit16u dirID; /* custom: dir-search ID for multiple searches at the same time */ + Bit16u dirCluster; /* custom (drive_fat only): cluster number for multiple searches at the same time */ + Bit8u fill[4]; + Bit8u attr; + Bit16u time; + Bit16u date; + Bit32u size; + char name[DOS_NAMELENGTH_ASCII]; + } GCC_ATTRIBUTE(packed); + #ifdef _MSC_VER + #pragma pack() + #endif +}; + +class DOS_FCB: public MemStruct { +public: + DOS_FCB(Bit16u seg,Bit16u off,bool allow_extended=true); + void Create(bool _extended); + void SetName(Bit8u _drive,char * _fname,char * _ext); + void SetSizeDateTime(Bit32u _size,Bit16u _date,Bit16u _time); + void GetSizeDateTime(Bit32u & _size,Bit16u & _date,Bit16u & _time); + void GetName(char * fillname); + void FileOpen(Bit8u _fhandle); + void FileClose(Bit8u & _fhandle); + void GetRecord(Bit16u & _cur_block,Bit8u & _cur_rec); + void SetRecord(Bit16u _cur_block,Bit8u _cur_rec); + void GetSeqData(Bit8u & _fhandle,Bit16u & _rec_size); + void GetRandom(Bit32u & _random); + void SetRandom(Bit32u _random); + Bit8u GetDrive(void); + bool Extended(void); + void GetAttr(Bit8u & attr); + void SetAttr(Bit8u attr); + void SetResultAttr(Bit8u attr); + bool Valid(void); +private: + bool extended; + PhysPt real_pt; + #ifdef _MSC_VER + #pragma pack (1) + #endif + struct sFCB { + Bit8u drive; /* Drive number 0=default, 1=A, etc */ + Bit8u filename[8]; /* Space padded name */ + Bit8u ext[3]; /* Space padded extension */ + Bit16u cur_block; /* Current Block */ + Bit16u rec_size; /* Logical record size */ + Bit32u filesize; /* File Size */ + Bit16u date; + Bit16u time; + /* Reserved Block should be 8 bytes */ + Bit8u sft_entries; + Bit8u share_attributes; + Bit8u extra_info; + Bit8u file_handle; + Bit8u reserved[4]; + /* end */ + Bit8u cur_rec; /* Current record in current block */ + Bit32u rndm; /* Current relative record number */ + } GCC_ATTRIBUTE(packed); + #ifdef _MSC_VER + #pragma pack () + #endif +}; + +class DOS_MCB : public MemStruct{ +public: + DOS_MCB(Bit16u seg) { SetPt(seg); } + void SetFileName(const char * const _name) { MEM_BlockWrite(pt+offsetof(sMCB,filename),_name,8); } + void GetFileName(char * const _name) { MEM_BlockRead(pt+offsetof(sMCB,filename),_name,8);_name[8]=0;} + void SetType(Bit8u _type) { sSave(sMCB,type,_type);} + void SetSize(Bit16u _size) { sSave(sMCB,size,_size);} + void SetPSPSeg(Bit16u _pspseg) { sSave(sMCB,psp_segment,_pspseg);} + Bit8u GetType(void) { return (Bit8u)sGet(sMCB,type);} + Bit16u GetSize(void) { return (Bit16u)sGet(sMCB,size);} + Bit16u GetPSPSeg(void) { return (Bit16u)sGet(sMCB,psp_segment);} +private: + #ifdef _MSC_VER + #pragma pack (1) + #endif + struct sMCB { + Bit8u type; + Bit16u psp_segment; + Bit16u size; + Bit8u unused[3]; + Bit8u filename[8]; + } GCC_ATTRIBUTE(packed); + #ifdef _MSC_VER + #pragma pack () + #endif +}; + +class DOS_SDA : public MemStruct { +public: + DOS_SDA(Bit16u _seg,Bit16u _offs) { SetPt(_seg,_offs); } + void Init(); + void SetDrive(Bit8u _drive) { sSave(sSDA,current_drive, _drive); } + void SetDTA(Bit32u _dta) { sSave(sSDA,current_dta, _dta); } + void SetPSP(Bit16u _psp) { sSave(sSDA,current_psp, _psp); } + Bit8u GetDrive(void) { return (Bit8u)sGet(sSDA,current_drive); } + Bit16u GetPSP(void) { return (Bit16u)sGet(sSDA,current_psp); } + Bit32u GetDTA(void) { return (Bit32u)sGet(sSDA,current_dta); } + + +private: + #ifdef _MSC_VER + #pragma pack (1) + #endif + struct sSDA { + Bit8u crit_error_flag; /* 0x00 Critical Error Flag */ + Bit8u inDOS_flag; /* 0x01 InDOS flag (count of active INT 21 calls) */ + Bit8u drive_crit_error; /* 0x02 Drive on which current critical error occurred or FFh */ + Bit8u locus_of_last_error; /* 0x03 locus of last error */ + Bit16u extended_error_code; /* 0x04 extended error code of last error */ + Bit8u suggested_action; /* 0x06 suggested action for last error */ + Bit8u error_class; /* 0x07 class of last error*/ + Bit32u last_error_pointer; /* 0x08 ES:DI pointer for last error */ + Bit32u current_dta; /* 0x0C current DTA (Disk Transfer Address) */ + Bit16u current_psp; /* 0x10 current PSP */ + Bit16u sp_int_23; /* 0x12 stores SP across an INT 23 */ + Bit16u return_code; /* 0x14 return code from last process termination (zerod after reading with AH=4Dh) */ + Bit8u current_drive; /* 0x16 current drive */ + Bit8u extended_break_flag; /* 0x17 extended break flag */ + Bit8u fill[2]; /* 0x18 flag: code page switching || flag: copy of previous byte in case of INT 24 Abort*/ + } GCC_ATTRIBUTE(packed); + #ifdef _MSC_VER + #pragma pack() + #endif +}; +extern DOS_InfoBlock dos_infoblock; + +struct DOS_Block { + DOS_Date date; + DOS_Version version; + Bit16u firstMCB; + Bit16u errorcode; + Bit16u psp();//{return DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).GetPSP();}; + void psp(Bit16u _seg);//{ DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetPSP(_seg);}; + RealPt dta();//{return DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).GetDTA();}; + void dta(RealPt _dta);//{DOS_SDA(DOS_SDA_SEG,DOS_SDA_OFS).SetDTA(_dta);}; + Bit8u return_code,return_mode; + + Bit8u current_drive; + bool verify; + bool breakcheck; + bool echo; // if set to true dev_con::read will echo input + struct { + RealPt mediaid; + RealPt tempdta; + RealPt tempdta_fcbdelete; + RealPt dbcs; + RealPt filenamechar; + RealPt collatingseq; + RealPt upcase; + Bit8u* country;//Will be copied to dos memory. resides in real mem + Bit16u dpb; //Fake Disk parameter system using only the first entry so the drive letter matches + } tables; + Bit16u loaded_codepage; +}; + +extern DOS_Block dos; + +static INLINE Bit8u RealHandle(Bit16u handle) { + DOS_PSP psp(dos.psp()); + return psp.GetFileHandle(handle); +} + +struct DOS_GetMemLog_Entry { + Bit16u segbase; + Bit16u pages; + std::string who; +}; + +extern std::list DOS_GetMemLog; + +#endif diff --git a/src/dos/dev_con.h b/src/dos/dev_con.h index accac9aec..212a848bf 100644 --- a/src/dos/dev_con.h +++ b/src/dos/dev_con.h @@ -1,962 +1,962 @@ -/* - * Copyright (C) 2002-2015 The DOSBox Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - - -#include "dos_inc.h" -#include "../ints/int10.h" -#include -#include "inout.h" -#include "shiftjis.h" -#include "callback.h" - -#define NUMBER_ANSI_DATA 10 - -extern bool DOS_BreakFlag; - -Bitu INT10_Handler(void); -Bitu INT16_Handler_Wrap(void); - -ShiftJISDecoder con_sjis; - -Bit16u last_int16_code = 0; - -static size_t dev_con_pos=0,dev_con_max=0; -static char dev_con_readbuf[64]; - -Bit8u DefaultANSIAttr() { - return IS_PC98_ARCH ? 0xE1 : 0x07; -} - -class device_CON : public DOS_Device { -public: - device_CON(); - bool Read(Bit8u * data,Bit16u * size); - bool Write(const Bit8u * data,Bit16u * size); - bool Seek(Bit32u * pos,Bit32u type); - bool Close(); - void ClearAnsi(void); - Bit16u GetInformation(void); - bool ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) { (void)bufptr; (void)size; (void)retcode; return false; } - bool WriteToControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) { (void)bufptr; (void)size; (void)retcode; return false; } -private: - Bit8u readcache; - Bit8u lastwrite; - struct ansi { /* should create a constructor, which would fill them with the appropriate values */ - bool esc; - bool sci; - bool pc98rab; // PC-98 ESC [ > ... (right angle bracket) I will rename this variable if MS-DOS ANSI.SYS also supports this sequence - bool enabled; - Bit8u attr; // machine-specific - Bit8u data[NUMBER_ANSI_DATA]; - Bit8u numberofarg; - Bit16u nrows; - Bit16u ncols; - Bit8u savecol; - Bit8u saverow; - bool warned; - - void Disable() { - if (!IS_PC98_ARCH) - enabled = false; - - attr = DefaultANSIAttr(); - } - } ansi; - - static void Real_INT10_SetCursorPos(Bit8u row,Bit8u col,Bit8u page) { - Bit16u oldax,oldbx,olddx; - - oldax=reg_ax; - oldbx=reg_bx; - olddx=reg_dx; - - reg_ah=0x2; - reg_dh=row; - reg_dl=col; - reg_bh=page; - - /* FIXME: PC-98 emulation should eventually use CONIO emulation that - * better emulates the actual platform. The purpose of this - * hack is to allow our code to call into INT 10h without - * setting up an INT 10h vector */ - if (IS_PC98_ARCH) - INT10_Handler(); - else - CALLBACK_RunRealInt(0x10); - - reg_ax=oldax; - reg_bx=oldbx; - reg_dx=olddx; - } - - /* Common function to turn specific scan codes into ANSI codes. - * This is a separate function so that both Read() and GetInformation() can use it. - * GetInformation needs to handle the scan code on entry in order to correctly - * assert whether Read() will return data or not. Some scan codes are ignored by - * the CON driver, therefore even though the BIOS says there is key data, Read() - * will not return anything and will block. */ - bool CommonPC98ExtScanConversionToReadBuf(unsigned char code) { - switch (code) { - case 0x38: // INS - dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x50; dev_con_pos=0; dev_con_max=2; - break; - case 0x39: // DEL - dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x44; dev_con_pos=0; dev_con_max=2; - return true; - case 0x3A: // up arrow - dev_con_readbuf[0] = 0x0B; dev_con_pos=0; dev_con_max=1; - return true; - case 0x3B: // left arrow - dev_con_readbuf[0] = 0x08; dev_con_pos=0; dev_con_max=1; - return true; - case 0x3C: // right arrow - dev_con_readbuf[0] = 0x0C; dev_con_pos=0; dev_con_max=1; - return true; - case 0x3D: // down arrow - dev_con_readbuf[0] = 0x0A; dev_con_pos=0; dev_con_max=1; - return true; - case 0x62: // F1 - dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x53; dev_con_pos=0; dev_con_max=2; - return true; - case 0x63: // F2 - dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x54; dev_con_pos=0; dev_con_max=2; - return true; - case 0x64: // F3 - dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x55; dev_con_pos=0; dev_con_max=2; - return true; - case 0x65: // F4 - dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x56; dev_con_pos=0; dev_con_max=2; - return true; - case 0x66: // F5 - dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x57; dev_con_pos=0; dev_con_max=2; - return true; - case 0x67: // F6 - dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x45; dev_con_pos=0; dev_con_max=2; - return true; - case 0x68: // F7 - dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x4A; dev_con_pos=0; dev_con_max=2; - return true; - case 0x69: // F8 - dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x50; dev_con_pos=0; dev_con_max=2; - return true; - case 0x6A: // F9 - dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x51; dev_con_pos=0; dev_con_max=2; - return true; - case 0x6B: // F10 - dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x5A; dev_con_pos=0; dev_con_max=2; - return true; -#if 0 - // ROLL UP -- -- -- - // POLL DOWN-- -- -- - // COPY -- -- -- - // HOME/CLR 0x1A 0x1E -- - // HELP -- -- -- -#endif - } - - return false; - } - - static void Real_INT10_TeletypeOutput(Bit8u xChar,Bit8u xAttr) { - Bit16u oldax,oldbx; - - if (IS_PC98_ARCH) { - if (con_sjis.take(xChar)) { - BIOS_NCOLS; - Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); - Bit8u cur_row=CURSOR_POS_ROW(page); - Bit8u cur_col=CURSOR_POS_COL(page); - unsigned char cw = con_sjis.doublewide ? 2 : 1; - - /* FIXME: I'm not sure what NEC's ANSI driver does if a doublewide character is printed at column 79 */ - if ((cur_col+cw) > ncols) { - cur_col = (Bit8u)ncols; - AdjustCursorPosition(cur_col,cur_row); - } - - /* JIS conversion to WORD value appropriate for text RAM */ - if (con_sjis.b2 != 0) con_sjis.b1 -= 0x20; - - INT10_WriteChar((con_sjis.b2 << 8) + con_sjis.b1,xAttr,0,1,true); - - cur_col += cw; - AdjustCursorPosition(cur_col,cur_row); - Real_INT10_SetCursorPos(cur_row,cur_col,page); - } - } - else { - oldax=reg_ax; - oldbx=reg_bx; - - reg_ah=0xE; - reg_al=xChar; - reg_bl=xAttr; - - CALLBACK_RunRealInt(0x10); - - reg_ax=oldax; - reg_bx=oldbx; - } - } - - - static void Real_WriteChar(Bit8u cur_col,Bit8u cur_row, - Bit8u page,Bit8u chr,Bit8u attr,Bit8u useattr) { - //Cursor position - Real_INT10_SetCursorPos(cur_row,cur_col,page); - - //Write the character - Bit16u oldax,oldbx,oldcx; - oldax=reg_ax; - oldbx=reg_bx; - oldcx=reg_cx; - - reg_al=chr; - reg_bl=attr; - reg_bh=page; - reg_cx=1; - if(useattr) - reg_ah=0x9; - else reg_ah=0x0A; - - /* FIXME: PC-98 emulation should eventually use CONIO emulation that - * better emulates the actual platform. The purpose of this - * hack is to allow our code to call into INT 10h without - * setting up an INT 10h vector */ - if (IS_PC98_ARCH) - INT10_Handler(); - else - CALLBACK_RunRealInt(0x10); - - reg_ax=oldax; - reg_bx=oldbx; - reg_cx=oldcx; - }//static void Real_WriteChar(cur_col,cur_row,page,chr,attr,useattr) - - - static void AdjustCursorPosition(Bit8u& cur_col,Bit8u& cur_row) { - BIOS_NCOLS;BIOS_NROWS; - auto defattr = DefaultANSIAttr(); - //Need a new line? - if(cur_col==ncols) - { - cur_col=0; - cur_row++; - - if (!IS_PC98_ARCH) - Real_INT10_TeletypeOutput('\r',defattr); - } - - //Reached the bottom? - if(cur_row==nrows) - { - if (IS_PC98_ARCH) - INT10_ScrollWindow(0,0,(Bit8u)(nrows-1),(Bit8u)(ncols-1),-1,defattr,0); - else - Real_INT10_TeletypeOutput('\n',defattr); //Scroll up - - cur_row--; - } - } - - - void Real_INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr) { - //TODO Check if this page thing is correct - Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); -// BIOS_NCOLS;BIOS_NROWS; - Bit8u cur_row=CURSOR_POS_ROW(page); - Bit8u cur_col=CURSOR_POS_COL(page); - switch (chr) - { - case 7: { - // set timer (this should not be needed as the timer already is programmed - // with those values, but the speaker stays silent without it) - IO_Write(0x43,0xb6); - IO_Write(0x42,1320&0xff); - IO_Write(0x42,1320>>8); - // enable speaker - IO_Write(0x61,IO_Read(0x61)|0x3); - for(Bitu i=0; i < 333; i++) CALLBACK_Idle(); - IO_Write(0x61,IO_Read(0x61)&~0x3); - break; - } - case 8: - if(cur_col>0) - cur_col--; - break; - case '\r': - cur_col=0; - break; - case '\n': - cur_col=0; - cur_row++; - break; - case '\t': - do { - Real_INT10_TeletypeOutputAttr(' ',attr,useattr); - cur_row=CURSOR_POS_ROW(page); - cur_col=CURSOR_POS_COL(page); - } while(cur_col%8); - break; - default: - //* Draw the actual Character - if (IS_PC98_ARCH) { - if (con_sjis.take(chr)) { - BIOS_NCOLS; - unsigned char cw = con_sjis.doublewide ? 2 : 1; - - /* FIXME: I'm not sure what NEC's ANSI driver does if a doublewide character is printed at column 79 */ - if ((cur_col+cw) > ncols) { - cur_col = (Bit8u)ncols; - AdjustCursorPosition(cur_col,cur_row); - } - - /* JIS conversion to WORD value appropriate for text RAM */ - if (con_sjis.b2 != 0) con_sjis.b1 -= 0x20; - - INT10_WriteChar((con_sjis.b2 << 8) + con_sjis.b1,attr,0,1,true); - - cur_col += cw; - } - } - else { - Real_WriteChar(cur_col,cur_row,page,chr,attr,useattr); - cur_col++; - } - } - - AdjustCursorPosition(cur_col,cur_row); - Real_INT10_SetCursorPos(cur_row,cur_col,page); - }//void Real_INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr) -}; - -// NEC-PC98 keyboard input notes -// -// on a system with KKCFUNC.SYS, NECAIK1.SYS, NECAIK2.SYS, NECAI.SYS loaded -// -// Key Normal Shift CTRL -// ------------------------------------- -// ESC 0x1B 0x1B 0x1B -// TAB 0x09 0x09 0x09 -// F1 0x1B 0x53 -- -// F2 0x1B 0x54 -- -// F3 0x1B 0x55 -- -// F4 0x1B 0x56 Toggles 'g' -// F5 0x1B 0x57 -- -// F6 0x1B 0x45 Toggle 20/25-line text mode -// F7 0x1B 0x4A Toggle function row (C1/CU/etc, shortcuts, or off) -// F8 0x1B 0x50 Clear screen, home cursor -// F9 0x1B 0x51 -- -// F10 0x1B 0x5A -- -// INS 0x1B 0x50 0x1B 0x50 0x1B 0x50 -// DEL 0x1B 0x44 0x1B 0x44 0x1B 0x44 -// ROLL UP -- -- -- -// POLL DOWN-- -- -- -// COPY -- -- -- -// HOME/CLR 0x1A 0x1E -- -// HELP -- -- -- -// UP ARROW 0x0B 0x0B 0x0B -// LF ARROW 0x08 0x08 0x08 -// RT ARROW 0x0C 0x0C 0x0C -// DN ARROW 0x0A 0x0A 0x0A -// VF1 -- -- -- -// VF2 -- -- -- -// VF3 -- -- -- -// VF4 -- -- -- -// VF5 -- -- -- - -// TODO for PC-98 mode: -// -// According to: -// -// http://hackipedia.org/browse.cgi/Computer/Platform/PC%2c%20NEC%20PC%2d98/Collections/PC%2d9801%20Bible%20%e6%9d%b1%e4%ba%ac%e7%90%86%e7%a7%91%e5%a4%a7%e5%ad%a6EIC%20%281994%29%2epdf -// -// Section 4-8. -// -// The PDF documents ANSI codes defined on PC-98, which may or may not be a complete listing. - -bool device_CON::Read(Bit8u * data,Bit16u * size) { - Bit16u oldax=reg_ax; - Bit16u count=0; - auto defattr=DefaultANSIAttr(); - INT10_SetCurMode(); - if ((readcache) && (*size)) { - data[count++]=readcache; - if(dos.echo) Real_INT10_TeletypeOutput(readcache,defattr); - readcache=0; - } - while (*size>count) { - if (dev_con_pos < dev_con_max) { - data[count++] = (Bit8u)dev_con_readbuf[dev_con_pos++]; - continue; - } - - reg_ah=(IS_EGAVGA_ARCH)?0x10:0x0; - - /* FIXME: PC-98 emulation should eventually use CONIO emulation that - * better emulates the actual platform. The purpose of this - * hack is to allow our code to call into INT 16h without - * setting up an INT 16h vector */ - if (IS_PC98_ARCH) - INT16_Handler_Wrap(); - else - CALLBACK_RunRealInt(0x16); - - /* hack for DOSKEY emulation */ - last_int16_code = reg_ax; - - switch(reg_al) { - case 13: - data[count++]=0x0D; - if (*size>count) data[count++]=0x0A; // it's only expanded if there is room for it. (NO cache) - *size=count; - reg_ax=oldax; - if(dos.echo) { - Real_INT10_TeletypeOutput(13,defattr); //maybe don't do this ( no need for it actually ) (but it's compatible) - Real_INT10_TeletypeOutput(10,defattr); - } - return true; - break; - case 8: - if(*size==1) data[count++]=reg_al; //one char at the time so give back that BS - else if(count) { //Remove data if it exists (extended keys don't go right) - data[count--]=0; - Real_INT10_TeletypeOutput(8,defattr); - Real_INT10_TeletypeOutput(' ',defattr); - } else { - continue; //no data read yet so restart whileloop. - } - break; - case 0xe0: /* Extended keys in the int 16 0x10 case */ - if(!reg_ah) { /*extended key if reg_ah isn't 0 */ - data[count++] = reg_al; - } else { - data[count++] = 0; - if (*size>count) data[count++] = reg_ah; - else readcache = reg_ah; - } - break; - case 0: /* Extended keys in the int 16 0x0 case */ - if (IS_PC98_ARCH) { - /* PC-98 does NOT return scan code, but instead returns nothing or - * control/escape code */ - CommonPC98ExtScanConversionToReadBuf(reg_ah); - } - else { - /* IBM PC/XT/AT signals extended code by entering AL, AH. - * Arrow keys for example become 0x00 0x48, 0x00 0x50, etc. */ - data[count++]=reg_al; - if (*size>count) data[count++]=reg_ah; - else readcache=reg_ah; - } - break; - default: - data[count++]=reg_al; - break; - } - if(dos.echo) { //what to do if *size==1 and character is BS ????? - // TODO: If CTRL+C checking is applicable do not echo (reg_al == 3) - Real_INT10_TeletypeOutput(reg_al,defattr); - } - } - *size=count; - reg_ax=oldax; - return true; -} - -bool log_dev_con = false; -std::string log_dev_con_str; - -bool device_CON::Write(const Bit8u * data,Bit16u * size) { - Bit16u count=0; - Bitu i; - Bit8u col,row; - Bit8u tempdata; - INT10_SetCurMode(); - - if (IS_PC98_ARCH) { - ansi.enabled = true; // ANSI is enabled at all times - ansi.attr = mem_readb(0x71D); // 60:11D - } - - while (*size>count) { - if (log_dev_con) { - if (log_dev_con_str.size() >= 255 || data[count] == '\n' || data[count] == 27) { - LOG_MSG("DOS CON: %s",log_dev_con_str.c_str()); - log_dev_con_str.clear(); - } - - if (data[count] != '\n' && data[count] != '\r') - log_dev_con_str += (char)data[count]; - } - - if (!ansi.esc){ - // TODO: PC-98 MS-DOS ANSI driver accepts 0x1E (RECORD SEPARATOR) as a single char command - // to put the cursor in home position. - // TODO: PC-98 MS-DOS ANSI driver accepts CTRL+Z as a single char command to clear the - // screeen and move the cursor to home (upper left). - if(data[count]=='\033') { - /*clear the datastructure */ - ClearAnsi(); - /* start the sequence */ - ansi.esc=true; - count++; - continue; - } else { - /* Some sort of "hack" now that '\n' doesn't set col to 0 (int10_char.cpp old chessgame) */ - if((data[count] == '\n') && (lastwrite != '\r')) Real_INT10_TeletypeOutputAttr('\r',ansi.attr,ansi.enabled); - /* ansi attribute will be set to the default if ansi is disabled */ - Real_INT10_TeletypeOutputAttr(data[count],ansi.attr,true); - lastwrite = data[count++]; - continue; - } - } - - if(!ansi.sci){ - - switch(data[count]){ - case '[': - ansi.sci=true; - break; - case '*':/* PC-98: clear screen */ - if (IS_PC98_ARCH) { - /* NTS: Some reverse engineering of INT DCh ANSI handling shows that - * ESC * handling does nothing but execute the same code path - * as CTRL+Z handling, which clears the screen including removal - * of the function key row, then calls into the code path for - * 0x1E (RECORD SEPARATOR) handling which then positions the - * cursor to home position (upper left corner of the screen). - * - * boot144.dsk ref NOTES - * - * 0ADC:0B84 ESC * handling - * 0ADC:117D CTRL+Z handling - * 0ADC:1516 Fill (clear) the screen - * 0ADC:13FF Remove function key row - * 0ADC:116B 0x1E RECORD SEPARATOR handling - */ - Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); - - /* reverse engineering of a bootdisk shows that ESC * (and CTRL+Z) also remove the function key row */ - void update_pc98_function_row(bool enable); - update_pc98_function_row(false); - - INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page); - Real_INT10_SetCursorPos(0,0,page); - ClearAnsi(); - } - else { - LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unknown char %c after a esc",data[count]); /*prob () */ - ClearAnsi(); - } - break; - case '7': /* save cursor pos + attr TODO */ - case '8': /* restore this TODO */ - case 'D':/* scrolling DOWN TODO */ - case 'M':/* scrolling UP TODO */ - default: - LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unknown char %c after a esc",data[count]); /*prob () */ - ClearAnsi(); - break; - } - count++; - continue; - } - /*ansi.esc and ansi.sci are true */ - Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); - if (isdigit(data[count])) { - assert(ansi.numberofarg < NUMBER_ANSI_DATA); - ansi.data[ansi.numberofarg]=10*ansi.data[ansi.numberofarg]+(data[count]-'0'); - } - else if (data[count] == ';') { - if ((ansi.numberofarg+1) < NUMBER_ANSI_DATA) - ansi.numberofarg++; - } - else if (ansi.pc98rab) { - assert(IS_PC98_ARCH); - - switch(data[count]){ - case 'h': /* SET MODE (if code =7 enable linewrap) */ - case 'l': /* RESET MODE */ - switch (ansi.data[0]) { - case 1: // show/hide function key row - void update_pc98_function_row(bool enable); - update_pc98_function_row(data[count] == 'l'); - ansi.nrows = real_readb(0x60,0x112)+1; - break; - case 3: // clear screen (doesn't matter if l or h) - INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page); - Real_INT10_SetCursorPos(0,0,page); - break; - case 5: // show/hide cursor - void PC98_show_cursor(bool show); - PC98_show_cursor(data[count] == 'l'); - mem_writeb(0x71B,data[count] == 'l' ? 0x01 : 0x00); /* 60:11B cursor display state */ - break; - default: - LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unhandled esc [ > %d %c",ansi.data[0],data[count]); - break; - }; - - ClearAnsi(); - break; - default: - LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unhandled char %c in esc [ >",data[count]); - ClearAnsi(); - break; - } - } - else { - switch(data[count]){ - case 'm': /* SGR */ - // NEC's ANSI driver always resets at the beginning - if(IS_PC98_ARCH) { - ansi.attr = DefaultANSIAttr(); - } - for(i=0;i<=ansi.numberofarg;i++){ - const Bit8u COLORFLAGS[][8] = { - // Black Red Green Yellow Blue Pink Cyan White - { 0x0, 0x4, 0x2, 0x6, 0x1, 0x5, 0x3, 0x7 }, /* IBM */ - { 0x0, 0x40, 0x80, 0xC0, 0x20, 0x60, 0xA0, 0xE0 }, /* PC-98 */ - }; - const auto &flagset = COLORFLAGS[IS_PC98_ARCH]; - - if(IS_PC98_ARCH) { - // Convert alternate color codes to regular ones - if(ansi.data[i] >= 17 && ansi.data[i] <= 23) { - const Bit8u convtbl[] = { - 31, 34, 35, 32, 33, 36, 37 - }; - ansi.data[i] = convtbl[ansi.data[i] - 17]; - } - } - - ansi.enabled=true; - switch(ansi.data[i]){ - case 0: /* normal */ - //Real ansi does this as well. (should do current defaults) - ansi.Disable(); - break; - case 1: /* bold mode on*/ - // FIXME: According to http://www.ninton.co.jp/?p=11, this - // should set some sort of "highlight" flag in monochrome - // mode, but I have no idea how to even enter that mode. - ansi.attr |= IS_PC98_ARCH ? 0 : 0x08; - break; - case 2: /* PC-98 "Bit 4" */ - ansi.attr |= IS_PC98_ARCH ? 0x10 : 0; - break; - case 4: /* underline */ - if(IS_PC98_ARCH) { - ansi.attr |= 0x08; - } else { - LOG(LOG_IOCTL, LOG_NORMAL)("ANSI:no support for underline yet"); - } - break; - case 5: /* blinking */ - ansi.attr |= IS_PC98_ARCH ? 0x02 : 0x80; - break; - case 7: /* reverse */ - //Just like real ansi. (should do use current colors reversed) - if(IS_PC98_ARCH) { - ansi.attr |= 0x04; - } else { - ansi.attr = 0x70; - } - break; - case 8: /* PC-98 secret */ - case 16: - ansi.attr &= IS_PC98_ARCH ? 0xFE : 0xFF; - break; - case 30: /* fg color black */ - case 31: /* fg color red */ - case 32: /* fg color green */ - case 33: /* fg color yellow */ - case 34: /* fg color blue */ - case 35: /* fg color magenta */ - case 36: /* fg color cyan */ - case 37: /* fg color white */ - ansi.attr &= ~(flagset[7]); - ansi.attr |= (flagset[ansi.data[i] - 30]); - break; - case 40: - case 41: - case 42: - case 43: - case 44: - case 45: - case 46: - case 47: { - Bit8u shift = IS_PC98_ARCH ? 0 : 4; - ansi.attr &= ~(flagset[7] << shift); - ansi.attr |= (flagset[ansi.data[i] - 40] << shift); - ansi.attr |= IS_PC98_ARCH ? 0x04 : 0; - break; - } - default: - break; - } - } - if (IS_PC98_ARCH) mem_writeb(0x71D,ansi.attr); // 60:11D - ClearAnsi(); - break; - case 'f': - case 'H':/* Cursor Pos*/ - if(!ansi.warned) { //Inform the debugger that ansi is used. - ansi.warned = true; - LOG(LOG_IOCTL,LOG_WARN)("ANSI SEQUENCES USED"); - } - /* Turn them into positions that are on the screen */ - if(ansi.data[0] == 0) ansi.data[0] = 1; - if(ansi.data[1] == 0) ansi.data[1] = 1; - if(ansi.data[0] > ansi.nrows) ansi.data[0] = (Bit8u)ansi.nrows; - if(ansi.data[1] > ansi.ncols) ansi.data[1] = (Bit8u)ansi.ncols; - Real_INT10_SetCursorPos(--(ansi.data[0]),--(ansi.data[1]),page); /*ansi=1 based, int10 is 0 based */ - ClearAnsi(); - break; - /* cursor up down and forward and backward only change the row or the col not both */ - case 'A': /* cursor up*/ - col=CURSOR_POS_COL(page) ; - row=CURSOR_POS_ROW(page) ; - tempdata = (ansi.data[0]? ansi.data[0] : 1); - if(tempdata > row) { row=0; } - else { row-=tempdata;} - Real_INT10_SetCursorPos(row,col,page); - ClearAnsi(); - break; - case 'B': /*cursor Down */ - col=CURSOR_POS_COL(page) ; - row=CURSOR_POS_ROW(page) ; - tempdata = (ansi.data[0]? ansi.data[0] : 1); - if(tempdata + static_cast(row) >= ansi.nrows) - { row = ansi.nrows - 1;} - else { row += tempdata; } - Real_INT10_SetCursorPos(row,col,page); - ClearAnsi(); - break; - case 'C': /*cursor forward */ - col=CURSOR_POS_COL(page); - row=CURSOR_POS_ROW(page); - tempdata=(ansi.data[0]? ansi.data[0] : 1); - if(tempdata + static_cast(col) >= ansi.ncols) - { col = ansi.ncols - 1;} - else { col += tempdata;} - Real_INT10_SetCursorPos(row,col,page); - ClearAnsi(); - break; - case 'D': /*Cursor Backward */ - col=CURSOR_POS_COL(page); - row=CURSOR_POS_ROW(page); - tempdata=(ansi.data[0]? ansi.data[0] : 1); - if(tempdata > col) {col = 0;} - else { col -= tempdata;} - Real_INT10_SetCursorPos(row,col,page); - ClearAnsi(); - break; - case 'J': /*erase screen and move cursor home*/ - if(ansi.data[0]==0) ansi.data[0]=2; - if(ansi.data[0]!=2) {/* every version behaves like type 2 */ - LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: esc[%dJ called : not supported handling as 2",ansi.data[0]); - } - INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page); - ClearAnsi(); - Real_INT10_SetCursorPos(0,0,page); - break; - case 'h': /* SET MODE (if code =7 enable linewrap) */ - case 'I': /* RESET MODE */ - LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: set/reset mode called(not supported)"); - ClearAnsi(); - break; - case 'u': /* Restore Cursor Pos */ - Real_INT10_SetCursorPos(ansi.saverow,ansi.savecol,page); - ClearAnsi(); - break; - case 's': /* SAVE CURSOR POS */ - ansi.savecol=CURSOR_POS_COL(page); - ansi.saverow=CURSOR_POS_ROW(page); - ClearAnsi(); - break; - case 'K': /* erase till end of line (don't touch cursor) */ - col = CURSOR_POS_COL(page); - row = CURSOR_POS_ROW(page); - INT10_WriteChar(' ',ansi.attr,page,ansi.ncols-col,true); //Real_WriteChar(ansi.ncols-col,row,page,' ',ansi.attr,true); - - //for(i = col;i<(Bitu) ansi.ncols; i++) INT10_TeletypeOutputAttr(' ',ansi.attr,true); - Real_INT10_SetCursorPos(row,col,page); - ClearAnsi(); - break; - case 'M': /* delete line (NANSI) */ - col = CURSOR_POS_COL(page); - row = CURSOR_POS_ROW(page); - INT10_ScrollWindow(row,0,ansi.nrows-1,ansi.ncols-1,ansi.data[0]? -ansi.data[0] : -1,ansi.attr,0xFF); - ClearAnsi(); - break; - case '>':/* proprietary NEC PC-98 MS-DOS codes (??) */ - if (IS_PC98_ARCH) { - ansi.pc98rab = true; - } - else { - LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: ESC [ > not supported outside PC-98 mode"); - ClearAnsi(); - } - break; - case 'l':/* (if code =7) disable linewrap */ - case 'p':/* reassign keys (needs strings) */ - case 'i':/* printer stuff */ - default: - LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unhandled char %c in esc[",data[count]); - ClearAnsi(); - break; - } - } - count++; - } - *size=count; - return true; -} - -bool device_CON::Seek(Bit32u * pos,Bit32u type) { - (void)pos; // UNUSED - (void)type; // UNUSED - // seek is valid - *pos = 0; - return true; -} - -bool device_CON::Close() { - return true; -} - -extern bool dos_con_use_int16_to_detect_input; - -Bit16u device_CON::GetInformation(void) { - if (dos_con_use_int16_to_detect_input || IS_PC98_ARCH) { - Bit16u ret = 0x80D3; /* No Key Available */ - - /* DOSBox-X behavior: Use INT 16h AH=0x11 Query keyboard status/preview key. - * The reason we do this is some DOS programs actually rely on hooking INT 16h - * to manipulate, hide, or transform what the DOS CON driver sees as well as - * itself. Perhaps the most disgusting example of this behavior would be the - * SCANDISK.EXE utility in Microsoft MS-DOS 6.22, which apparently relies on - * hooking INT 16h in this way to catch the Escape, CTRL+C, and some other - * scan codes in order to "eat" the scan codes before they get back to DOS. - * The reason they can get away with it apparently and still respond properly - * to those keys, is because the MS-DOS 6.22 CON driver always calls INT 16h - * AH=0x11 first before calling INT 16h AH=0x10 to fetch the scan code. - * - * Without this fix, SCANDISK.EXE does not respond properly to Escape and - * a few other keys. Pressing Escape will do nothing until you hit any other - * key, at which point it suddenly acts upon the Escape key. - * - * Since Scandisk is using INT 21h AH=0x0B to query STDIN during this time, - * this implementation is a good "halfway" compromise in that this call - * will trigger the INT 16h AH=0x11 hook it relies on. */ - if (readcache || dev_con_pos < dev_con_max) return 0x8093; /* key available */ - - Bitu saved_ax = reg_ax; - - reg_ah = (IS_EGAVGA_ARCH)?0x11:0x1; // check for keystroke - - /* FIXME: PC-98 emulation should eventually use CONIO emulation that - * better emulates the actual platform. The purpose of this - * hack is to allow our code to call into INT 16h without - * setting up an INT 16h vector */ - if (IS_PC98_ARCH) - INT16_Handler_Wrap(); - else - CALLBACK_RunRealInt(0x16); - - if (!GETFLAG(ZF)) { /* key is present, waiting to be returned on AH=0x10 or AH=0x00 */ - if (IS_PC98_ARCH && reg_al == 0) { - /* some scan codes are ignored by CON, and wouldn't read anything. - * while we're at it, take the scan code and convert it into ANSI here - * so that Read() returns it immediately instead of doing this conversion itself. - * This way we never block when we SAID a key was available that gets ignored. */ - if (CommonPC98ExtScanConversionToReadBuf(reg_ah)) - ret = 0x8093; /* Key Available */ - else - ret = 0x80D3; /* No Key Available */ - - /* need to consume the key. if it generated anything it will be returned to Read() - * through dev_con_readbuf[] */ - reg_ah=0x0; - - /* FIXME: PC-98 emulation should eventually use CONIO emulation that - * better emulates the actual platform. The purpose of this - * hack is to allow our code to call into INT 16h without - * setting up an INT 16h vector */ - INT16_Handler_Wrap(); - } - else { - ret = 0x8093; /* Key Available */ - } - } - - reg_ax = saved_ax; - return ret; - } - else { - /* DOSBox mainline behavior: alternate "fast" way through direct manipulation of keyboard scan buffer */ - Bit16u head=mem_readw(BIOS_KEYBOARD_BUFFER_HEAD); - Bit16u tail=mem_readw(BIOS_KEYBOARD_BUFFER_TAIL); - - if ((head==tail) && !readcache) return 0x80D3; /* No Key Available */ - if (readcache || real_readw(0x40,head)) return 0x8093; /* Key Available */ - - /* remove the zero from keyboard buffer */ - Bit16u start=mem_readw(BIOS_KEYBOARD_BUFFER_START); - Bit16u end =mem_readw(BIOS_KEYBOARD_BUFFER_END); - head+=2; - if (head>=end) head=start; - mem_writew(BIOS_KEYBOARD_BUFFER_HEAD,head); - } - - return 0x80D3; /* No Key Available */ -} - -device_CON::device_CON() { - SetName("CON"); - readcache=0; - lastwrite=0; - ansi.Disable(); - if (IS_PC98_ARCH) { - // NTS: On real hardware, the BIOS does NOT manage the console at all. - // TTY handling is entirely handled by MS-DOS. - ansi.ncols=80; - ansi.nrows=25 - 1; - // the DOS kernel will call on this function to disable, and SDLmain - // will call on to enable - } - else { - ansi.ncols=real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS); //should be updated once set/reset mode is implemented - ansi.nrows=real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS) + 1; - } - ansi.saverow=0; - ansi.savecol=0; - ansi.warned=false; - ClearAnsi(); -} - -void device_CON::ClearAnsi(void){ - for(Bit8u i=0; i +#include "inout.h" +#include "shiftjis.h" +#include "callback.h" + +#define NUMBER_ANSI_DATA 10 + +extern bool DOS_BreakFlag; + +Bitu INT10_Handler(void); +Bitu INT16_Handler_Wrap(void); + +ShiftJISDecoder con_sjis; + +Bit16u last_int16_code = 0; + +static size_t dev_con_pos=0,dev_con_max=0; +static char dev_con_readbuf[64]; + +Bit8u DefaultANSIAttr() { + return IS_PC98_ARCH ? 0xE1 : 0x07; +} + +class device_CON : public DOS_Device { +public: + device_CON(); + bool Read(Bit8u * data,Bit16u * size); + bool Write(const Bit8u * data,Bit16u * size); + bool Seek(Bit32u * pos,Bit32u type); + bool Close(); + void ClearAnsi(void); + Bit16u GetInformation(void); + bool ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) { (void)bufptr; (void)size; (void)retcode; return false; } + bool WriteToControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) { (void)bufptr; (void)size; (void)retcode; return false; } +private: + Bit8u readcache; + Bit8u lastwrite; + struct ansi { /* should create a constructor, which would fill them with the appropriate values */ + bool esc; + bool sci; + bool pc98rab; // PC-98 ESC [ > ... (right angle bracket) I will rename this variable if MS-DOS ANSI.SYS also supports this sequence + bool enabled; + Bit8u attr; // machine-specific + Bit8u data[NUMBER_ANSI_DATA]; + Bit8u numberofarg; + Bit16u nrows; + Bit16u ncols; + Bit8u savecol; + Bit8u saverow; + bool warned; + + void Disable() { + if (!IS_PC98_ARCH) + enabled = false; + + attr = DefaultANSIAttr(); + } + } ansi; + + static void Real_INT10_SetCursorPos(Bit8u row,Bit8u col,Bit8u page) { + Bit16u oldax,oldbx,olddx; + + oldax=reg_ax; + oldbx=reg_bx; + olddx=reg_dx; + + reg_ah=0x2; + reg_dh=row; + reg_dl=col; + reg_bh=page; + + /* FIXME: PC-98 emulation should eventually use CONIO emulation that + * better emulates the actual platform. The purpose of this + * hack is to allow our code to call into INT 10h without + * setting up an INT 10h vector */ + if (IS_PC98_ARCH) + INT10_Handler(); + else + CALLBACK_RunRealInt(0x10); + + reg_ax=oldax; + reg_bx=oldbx; + reg_dx=olddx; + } + + /* Common function to turn specific scan codes into ANSI codes. + * This is a separate function so that both Read() and GetInformation() can use it. + * GetInformation needs to handle the scan code on entry in order to correctly + * assert whether Read() will return data or not. Some scan codes are ignored by + * the CON driver, therefore even though the BIOS says there is key data, Read() + * will not return anything and will block. */ + bool CommonPC98ExtScanConversionToReadBuf(unsigned char code) { + switch (code) { + case 0x38: // INS + dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x50; dev_con_pos=0; dev_con_max=2; + break; + case 0x39: // DEL + dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x44; dev_con_pos=0; dev_con_max=2; + return true; + case 0x3A: // up arrow + dev_con_readbuf[0] = 0x0B; dev_con_pos=0; dev_con_max=1; + return true; + case 0x3B: // left arrow + dev_con_readbuf[0] = 0x08; dev_con_pos=0; dev_con_max=1; + return true; + case 0x3C: // right arrow + dev_con_readbuf[0] = 0x0C; dev_con_pos=0; dev_con_max=1; + return true; + case 0x3D: // down arrow + dev_con_readbuf[0] = 0x0A; dev_con_pos=0; dev_con_max=1; + return true; + case 0x62: // F1 + dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x53; dev_con_pos=0; dev_con_max=2; + return true; + case 0x63: // F2 + dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x54; dev_con_pos=0; dev_con_max=2; + return true; + case 0x64: // F3 + dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x55; dev_con_pos=0; dev_con_max=2; + return true; + case 0x65: // F4 + dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x56; dev_con_pos=0; dev_con_max=2; + return true; + case 0x66: // F5 + dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x57; dev_con_pos=0; dev_con_max=2; + return true; + case 0x67: // F6 + dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x45; dev_con_pos=0; dev_con_max=2; + return true; + case 0x68: // F7 + dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x4A; dev_con_pos=0; dev_con_max=2; + return true; + case 0x69: // F8 + dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x50; dev_con_pos=0; dev_con_max=2; + return true; + case 0x6A: // F9 + dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x51; dev_con_pos=0; dev_con_max=2; + return true; + case 0x6B: // F10 + dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x5A; dev_con_pos=0; dev_con_max=2; + return true; +#if 0 + // ROLL UP -- -- -- + // POLL DOWN-- -- -- + // COPY -- -- -- + // HOME/CLR 0x1A 0x1E -- + // HELP -- -- -- +#endif + } + + return false; + } + + static void Real_INT10_TeletypeOutput(Bit8u xChar,Bit8u xAttr) { + Bit16u oldax,oldbx; + + if (IS_PC98_ARCH) { + if (con_sjis.take(xChar)) { + BIOS_NCOLS; + Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); + Bit8u cur_row=CURSOR_POS_ROW(page); + Bit8u cur_col=CURSOR_POS_COL(page); + unsigned char cw = con_sjis.doublewide ? 2 : 1; + + /* FIXME: I'm not sure what NEC's ANSI driver does if a doublewide character is printed at column 79 */ + if ((cur_col+cw) > ncols) { + cur_col = (Bit8u)ncols; + AdjustCursorPosition(cur_col,cur_row); + } + + /* JIS conversion to WORD value appropriate for text RAM */ + if (con_sjis.b2 != 0) con_sjis.b1 -= 0x20; + + INT10_WriteChar((con_sjis.b2 << 8) + con_sjis.b1,xAttr,0,1,true); + + cur_col += cw; + AdjustCursorPosition(cur_col,cur_row); + Real_INT10_SetCursorPos(cur_row,cur_col,page); + } + } + else { + oldax=reg_ax; + oldbx=reg_bx; + + reg_ah=0xE; + reg_al=xChar; + reg_bl=xAttr; + + CALLBACK_RunRealInt(0x10); + + reg_ax=oldax; + reg_bx=oldbx; + } + } + + + static void Real_WriteChar(Bit8u cur_col,Bit8u cur_row, + Bit8u page,Bit8u chr,Bit8u attr,Bit8u useattr) { + //Cursor position + Real_INT10_SetCursorPos(cur_row,cur_col,page); + + //Write the character + Bit16u oldax,oldbx,oldcx; + oldax=reg_ax; + oldbx=reg_bx; + oldcx=reg_cx; + + reg_al=chr; + reg_bl=attr; + reg_bh=page; + reg_cx=1; + if(useattr) + reg_ah=0x9; + else reg_ah=0x0A; + + /* FIXME: PC-98 emulation should eventually use CONIO emulation that + * better emulates the actual platform. The purpose of this + * hack is to allow our code to call into INT 10h without + * setting up an INT 10h vector */ + if (IS_PC98_ARCH) + INT10_Handler(); + else + CALLBACK_RunRealInt(0x10); + + reg_ax=oldax; + reg_bx=oldbx; + reg_cx=oldcx; + }//static void Real_WriteChar(cur_col,cur_row,page,chr,attr,useattr) + + + static void AdjustCursorPosition(Bit8u& cur_col,Bit8u& cur_row) { + BIOS_NCOLS;BIOS_NROWS; + auto defattr = DefaultANSIAttr(); + //Need a new line? + if(cur_col==ncols) + { + cur_col=0; + cur_row++; + + if (!IS_PC98_ARCH) + Real_INT10_TeletypeOutput('\r',defattr); + } + + //Reached the bottom? + if(cur_row==nrows) + { + if (IS_PC98_ARCH) + INT10_ScrollWindow(0,0,(Bit8u)(nrows-1),(Bit8u)(ncols-1),-1,defattr,0); + else + Real_INT10_TeletypeOutput('\n',defattr); //Scroll up + + cur_row--; + } + } + + + void Real_INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr) { + //TODO Check if this page thing is correct + Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); +// BIOS_NCOLS;BIOS_NROWS; + Bit8u cur_row=CURSOR_POS_ROW(page); + Bit8u cur_col=CURSOR_POS_COL(page); + switch (chr) + { + case 7: { + // set timer (this should not be needed as the timer already is programmed + // with those values, but the speaker stays silent without it) + IO_Write(0x43,0xb6); + IO_Write(0x42,1320&0xff); + IO_Write(0x42,1320>>8); + // enable speaker + IO_Write(0x61,IO_Read(0x61)|0x3); + for(Bitu i=0; i < 333; i++) CALLBACK_Idle(); + IO_Write(0x61,IO_Read(0x61)&~0x3); + break; + } + case 8: + if(cur_col>0) + cur_col--; + break; + case '\r': + cur_col=0; + break; + case '\n': + cur_col=0; + cur_row++; + break; + case '\t': + do { + Real_INT10_TeletypeOutputAttr(' ',attr,useattr); + cur_row=CURSOR_POS_ROW(page); + cur_col=CURSOR_POS_COL(page); + } while(cur_col%8); + break; + default: + //* Draw the actual Character + if (IS_PC98_ARCH) { + if (con_sjis.take(chr)) { + BIOS_NCOLS; + unsigned char cw = con_sjis.doublewide ? 2 : 1; + + /* FIXME: I'm not sure what NEC's ANSI driver does if a doublewide character is printed at column 79 */ + if ((cur_col+cw) > ncols) { + cur_col = (Bit8u)ncols; + AdjustCursorPosition(cur_col,cur_row); + } + + /* JIS conversion to WORD value appropriate for text RAM */ + if (con_sjis.b2 != 0) con_sjis.b1 -= 0x20; + + INT10_WriteChar((con_sjis.b2 << 8) + con_sjis.b1,attr,0,1,true); + + cur_col += cw; + } + } + else { + Real_WriteChar(cur_col,cur_row,page,chr,attr,useattr); + cur_col++; + } + } + + AdjustCursorPosition(cur_col,cur_row); + Real_INT10_SetCursorPos(cur_row,cur_col,page); + }//void Real_INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr) +}; + +// NEC-PC98 keyboard input notes +// +// on a system with KKCFUNC.SYS, NECAIK1.SYS, NECAIK2.SYS, NECAI.SYS loaded +// +// Key Normal Shift CTRL +// ------------------------------------- +// ESC 0x1B 0x1B 0x1B +// TAB 0x09 0x09 0x09 +// F1 0x1B 0x53 -- +// F2 0x1B 0x54 -- +// F3 0x1B 0x55 -- +// F4 0x1B 0x56 Toggles 'g' +// F5 0x1B 0x57 -- +// F6 0x1B 0x45 Toggle 20/25-line text mode +// F7 0x1B 0x4A Toggle function row (C1/CU/etc, shortcuts, or off) +// F8 0x1B 0x50 Clear screen, home cursor +// F9 0x1B 0x51 -- +// F10 0x1B 0x5A -- +// INS 0x1B 0x50 0x1B 0x50 0x1B 0x50 +// DEL 0x1B 0x44 0x1B 0x44 0x1B 0x44 +// ROLL UP -- -- -- +// POLL DOWN-- -- -- +// COPY -- -- -- +// HOME/CLR 0x1A 0x1E -- +// HELP -- -- -- +// UP ARROW 0x0B 0x0B 0x0B +// LF ARROW 0x08 0x08 0x08 +// RT ARROW 0x0C 0x0C 0x0C +// DN ARROW 0x0A 0x0A 0x0A +// VF1 -- -- -- +// VF2 -- -- -- +// VF3 -- -- -- +// VF4 -- -- -- +// VF5 -- -- -- + +// TODO for PC-98 mode: +// +// According to: +// +// http://hackipedia.org/browse.cgi/Computer/Platform/PC%2c%20NEC%20PC%2d98/Collections/PC%2d9801%20Bible%20%e6%9d%b1%e4%ba%ac%e7%90%86%e7%a7%91%e5%a4%a7%e5%ad%a6EIC%20%281994%29%2epdf +// +// Section 4-8. +// +// The PDF documents ANSI codes defined on PC-98, which may or may not be a complete listing. + +bool device_CON::Read(Bit8u * data,Bit16u * size) { + Bit16u oldax=reg_ax; + Bit16u count=0; + auto defattr=DefaultANSIAttr(); + INT10_SetCurMode(); + if ((readcache) && (*size)) { + data[count++]=readcache; + if(dos.echo) Real_INT10_TeletypeOutput(readcache,defattr); + readcache=0; + } + while (*size>count) { + if (dev_con_pos < dev_con_max) { + data[count++] = (Bit8u)dev_con_readbuf[dev_con_pos++]; + continue; + } + + reg_ah=(IS_EGAVGA_ARCH)?0x10:0x0; + + /* FIXME: PC-98 emulation should eventually use CONIO emulation that + * better emulates the actual platform. The purpose of this + * hack is to allow our code to call into INT 16h without + * setting up an INT 16h vector */ + if (IS_PC98_ARCH) + INT16_Handler_Wrap(); + else + CALLBACK_RunRealInt(0x16); + + /* hack for DOSKEY emulation */ + last_int16_code = reg_ax; + + switch(reg_al) { + case 13: + data[count++]=0x0D; + if (*size>count) data[count++]=0x0A; // it's only expanded if there is room for it. (NO cache) + *size=count; + reg_ax=oldax; + if(dos.echo) { + Real_INT10_TeletypeOutput(13,defattr); //maybe don't do this ( no need for it actually ) (but it's compatible) + Real_INT10_TeletypeOutput(10,defattr); + } + return true; + break; + case 8: + if(*size==1) data[count++]=reg_al; //one char at the time so give back that BS + else if(count) { //Remove data if it exists (extended keys don't go right) + data[count--]=0; + Real_INT10_TeletypeOutput(8,defattr); + Real_INT10_TeletypeOutput(' ',defattr); + } else { + continue; //no data read yet so restart whileloop. + } + break; + case 0xe0: /* Extended keys in the int 16 0x10 case */ + if(!reg_ah) { /*extended key if reg_ah isn't 0 */ + data[count++] = reg_al; + } else { + data[count++] = 0; + if (*size>count) data[count++] = reg_ah; + else readcache = reg_ah; + } + break; + case 0: /* Extended keys in the int 16 0x0 case */ + if (IS_PC98_ARCH) { + /* PC-98 does NOT return scan code, but instead returns nothing or + * control/escape code */ + CommonPC98ExtScanConversionToReadBuf(reg_ah); + } + else { + /* IBM PC/XT/AT signals extended code by entering AL, AH. + * Arrow keys for example become 0x00 0x48, 0x00 0x50, etc. */ + data[count++]=reg_al; + if (*size>count) data[count++]=reg_ah; + else readcache=reg_ah; + } + break; + default: + data[count++]=reg_al; + break; + } + if(dos.echo) { //what to do if *size==1 and character is BS ????? + // TODO: If CTRL+C checking is applicable do not echo (reg_al == 3) + Real_INT10_TeletypeOutput(reg_al,defattr); + } + } + *size=count; + reg_ax=oldax; + return true; +} + +bool log_dev_con = false; +std::string log_dev_con_str; + +bool device_CON::Write(const Bit8u * data,Bit16u * size) { + Bit16u count=0; + Bitu i; + Bit8u col,row; + Bit8u tempdata; + INT10_SetCurMode(); + + if (IS_PC98_ARCH) { + ansi.enabled = true; // ANSI is enabled at all times + ansi.attr = mem_readb(0x71D); // 60:11D + } + + while (*size>count) { + if (log_dev_con) { + if (log_dev_con_str.size() >= 255 || data[count] == '\n' || data[count] == 27) { + LOG_MSG("DOS CON: %s",log_dev_con_str.c_str()); + log_dev_con_str.clear(); + } + + if (data[count] != '\n' && data[count] != '\r') + log_dev_con_str += (char)data[count]; + } + + if (!ansi.esc){ + // TODO: PC-98 MS-DOS ANSI driver accepts 0x1E (RECORD SEPARATOR) as a single char command + // to put the cursor in home position. + // TODO: PC-98 MS-DOS ANSI driver accepts CTRL+Z as a single char command to clear the + // screeen and move the cursor to home (upper left). + if(data[count]=='\033') { + /*clear the datastructure */ + ClearAnsi(); + /* start the sequence */ + ansi.esc=true; + count++; + continue; + } else { + /* Some sort of "hack" now that '\n' doesn't set col to 0 (int10_char.cpp old chessgame) */ + if((data[count] == '\n') && (lastwrite != '\r')) Real_INT10_TeletypeOutputAttr('\r',ansi.attr,ansi.enabled); + /* ansi attribute will be set to the default if ansi is disabled */ + Real_INT10_TeletypeOutputAttr(data[count],ansi.attr,true); + lastwrite = data[count++]; + continue; + } + } + + if(!ansi.sci){ + + switch(data[count]){ + case '[': + ansi.sci=true; + break; + case '*':/* PC-98: clear screen */ + if (IS_PC98_ARCH) { + /* NTS: Some reverse engineering of INT DCh ANSI handling shows that + * ESC * handling does nothing but execute the same code path + * as CTRL+Z handling, which clears the screen including removal + * of the function key row, then calls into the code path for + * 0x1E (RECORD SEPARATOR) handling which then positions the + * cursor to home position (upper left corner of the screen). + * + * boot144.dsk ref NOTES + * + * 0ADC:0B84 ESC * handling + * 0ADC:117D CTRL+Z handling + * 0ADC:1516 Fill (clear) the screen + * 0ADC:13FF Remove function key row + * 0ADC:116B 0x1E RECORD SEPARATOR handling + */ + Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); + + /* reverse engineering of a bootdisk shows that ESC * (and CTRL+Z) also remove the function key row */ + void update_pc98_function_row(bool enable); + update_pc98_function_row(false); + + INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page); + Real_INT10_SetCursorPos(0,0,page); + ClearAnsi(); + } + else { + LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unknown char %c after a esc",data[count]); /*prob () */ + ClearAnsi(); + } + break; + case '7': /* save cursor pos + attr TODO */ + case '8': /* restore this TODO */ + case 'D':/* scrolling DOWN TODO */ + case 'M':/* scrolling UP TODO */ + default: + LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unknown char %c after a esc",data[count]); /*prob () */ + ClearAnsi(); + break; + } + count++; + continue; + } + /*ansi.esc and ansi.sci are true */ + Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); + if (isdigit(data[count])) { + assert(ansi.numberofarg < NUMBER_ANSI_DATA); + ansi.data[ansi.numberofarg]=10*ansi.data[ansi.numberofarg]+(data[count]-'0'); + } + else if (data[count] == ';') { + if ((ansi.numberofarg+1) < NUMBER_ANSI_DATA) + ansi.numberofarg++; + } + else if (ansi.pc98rab) { + assert(IS_PC98_ARCH); + + switch(data[count]){ + case 'h': /* SET MODE (if code =7 enable linewrap) */ + case 'l': /* RESET MODE */ + switch (ansi.data[0]) { + case 1: // show/hide function key row + void update_pc98_function_row(bool enable); + update_pc98_function_row(data[count] == 'l'); + ansi.nrows = real_readb(0x60,0x112)+1; + break; + case 3: // clear screen (doesn't matter if l or h) + INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page); + Real_INT10_SetCursorPos(0,0,page); + break; + case 5: // show/hide cursor + void PC98_show_cursor(bool show); + PC98_show_cursor(data[count] == 'l'); + mem_writeb(0x71B,data[count] == 'l' ? 0x01 : 0x00); /* 60:11B cursor display state */ + break; + default: + LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unhandled esc [ > %d %c",ansi.data[0],data[count]); + break; + }; + + ClearAnsi(); + break; + default: + LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unhandled char %c in esc [ >",data[count]); + ClearAnsi(); + break; + } + } + else { + switch(data[count]){ + case 'm': /* SGR */ + // NEC's ANSI driver always resets at the beginning + if(IS_PC98_ARCH) { + ansi.attr = DefaultANSIAttr(); + } + for(i=0;i<=ansi.numberofarg;i++){ + const Bit8u COLORFLAGS[][8] = { + // Black Red Green Yellow Blue Pink Cyan White + { 0x0, 0x4, 0x2, 0x6, 0x1, 0x5, 0x3, 0x7 }, /* IBM */ + { 0x0, 0x40, 0x80, 0xC0, 0x20, 0x60, 0xA0, 0xE0 }, /* PC-98 */ + }; + const auto &flagset = COLORFLAGS[IS_PC98_ARCH]; + + if(IS_PC98_ARCH) { + // Convert alternate color codes to regular ones + if(ansi.data[i] >= 17 && ansi.data[i] <= 23) { + const Bit8u convtbl[] = { + 31, 34, 35, 32, 33, 36, 37 + }; + ansi.data[i] = convtbl[ansi.data[i] - 17]; + } + } + + ansi.enabled=true; + switch(ansi.data[i]){ + case 0: /* normal */ + //Real ansi does this as well. (should do current defaults) + ansi.Disable(); + break; + case 1: /* bold mode on*/ + // FIXME: According to http://www.ninton.co.jp/?p=11, this + // should set some sort of "highlight" flag in monochrome + // mode, but I have no idea how to even enter that mode. + ansi.attr |= IS_PC98_ARCH ? 0 : 0x08; + break; + case 2: /* PC-98 "Bit 4" */ + ansi.attr |= IS_PC98_ARCH ? 0x10 : 0; + break; + case 4: /* underline */ + if(IS_PC98_ARCH) { + ansi.attr |= 0x08; + } else { + LOG(LOG_IOCTL, LOG_NORMAL)("ANSI:no support for underline yet"); + } + break; + case 5: /* blinking */ + ansi.attr |= IS_PC98_ARCH ? 0x02 : 0x80; + break; + case 7: /* reverse */ + //Just like real ansi. (should do use current colors reversed) + if(IS_PC98_ARCH) { + ansi.attr |= 0x04; + } else { + ansi.attr = 0x70; + } + break; + case 8: /* PC-98 secret */ + case 16: + ansi.attr &= IS_PC98_ARCH ? 0xFE : 0xFF; + break; + case 30: /* fg color black */ + case 31: /* fg color red */ + case 32: /* fg color green */ + case 33: /* fg color yellow */ + case 34: /* fg color blue */ + case 35: /* fg color magenta */ + case 36: /* fg color cyan */ + case 37: /* fg color white */ + ansi.attr &= ~(flagset[7]); + ansi.attr |= (flagset[ansi.data[i] - 30]); + break; + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: { + Bit8u shift = IS_PC98_ARCH ? 0 : 4; + ansi.attr &= ~(flagset[7] << shift); + ansi.attr |= (flagset[ansi.data[i] - 40] << shift); + ansi.attr |= IS_PC98_ARCH ? 0x04 : 0; + break; + } + default: + break; + } + } + if (IS_PC98_ARCH) mem_writeb(0x71D,ansi.attr); // 60:11D + ClearAnsi(); + break; + case 'f': + case 'H':/* Cursor Pos*/ + if(!ansi.warned) { //Inform the debugger that ansi is used. + ansi.warned = true; + LOG(LOG_IOCTL,LOG_WARN)("ANSI SEQUENCES USED"); + } + /* Turn them into positions that are on the screen */ + if(ansi.data[0] == 0) ansi.data[0] = 1; + if(ansi.data[1] == 0) ansi.data[1] = 1; + if(ansi.data[0] > ansi.nrows) ansi.data[0] = (Bit8u)ansi.nrows; + if(ansi.data[1] > ansi.ncols) ansi.data[1] = (Bit8u)ansi.ncols; + Real_INT10_SetCursorPos(--(ansi.data[0]),--(ansi.data[1]),page); /*ansi=1 based, int10 is 0 based */ + ClearAnsi(); + break; + /* cursor up down and forward and backward only change the row or the col not both */ + case 'A': /* cursor up*/ + col=CURSOR_POS_COL(page) ; + row=CURSOR_POS_ROW(page) ; + tempdata = (ansi.data[0]? ansi.data[0] : 1); + if(tempdata > row) { row=0; } + else { row-=tempdata;} + Real_INT10_SetCursorPos(row,col,page); + ClearAnsi(); + break; + case 'B': /*cursor Down */ + col=CURSOR_POS_COL(page) ; + row=CURSOR_POS_ROW(page) ; + tempdata = (ansi.data[0]? ansi.data[0] : 1); + if(tempdata + static_cast(row) >= ansi.nrows) + { row = ansi.nrows - 1;} + else { row += tempdata; } + Real_INT10_SetCursorPos(row,col,page); + ClearAnsi(); + break; + case 'C': /*cursor forward */ + col=CURSOR_POS_COL(page); + row=CURSOR_POS_ROW(page); + tempdata=(ansi.data[0]? ansi.data[0] : 1); + if(tempdata + static_cast(col) >= ansi.ncols) + { col = ansi.ncols - 1;} + else { col += tempdata;} + Real_INT10_SetCursorPos(row,col,page); + ClearAnsi(); + break; + case 'D': /*Cursor Backward */ + col=CURSOR_POS_COL(page); + row=CURSOR_POS_ROW(page); + tempdata=(ansi.data[0]? ansi.data[0] : 1); + if(tempdata > col) {col = 0;} + else { col -= tempdata;} + Real_INT10_SetCursorPos(row,col,page); + ClearAnsi(); + break; + case 'J': /*erase screen and move cursor home*/ + if(ansi.data[0]==0) ansi.data[0]=2; + if(ansi.data[0]!=2) {/* every version behaves like type 2 */ + LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: esc[%dJ called : not supported handling as 2",ansi.data[0]); + } + INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page); + ClearAnsi(); + Real_INT10_SetCursorPos(0,0,page); + break; + case 'h': /* SET MODE (if code =7 enable linewrap) */ + case 'I': /* RESET MODE */ + LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: set/reset mode called(not supported)"); + ClearAnsi(); + break; + case 'u': /* Restore Cursor Pos */ + Real_INT10_SetCursorPos(ansi.saverow,ansi.savecol,page); + ClearAnsi(); + break; + case 's': /* SAVE CURSOR POS */ + ansi.savecol=CURSOR_POS_COL(page); + ansi.saverow=CURSOR_POS_ROW(page); + ClearAnsi(); + break; + case 'K': /* erase till end of line (don't touch cursor) */ + col = CURSOR_POS_COL(page); + row = CURSOR_POS_ROW(page); + INT10_WriteChar(' ',ansi.attr,page,ansi.ncols-col,true); //Real_WriteChar(ansi.ncols-col,row,page,' ',ansi.attr,true); + + //for(i = col;i<(Bitu) ansi.ncols; i++) INT10_TeletypeOutputAttr(' ',ansi.attr,true); + Real_INT10_SetCursorPos(row,col,page); + ClearAnsi(); + break; + case 'M': /* delete line (NANSI) */ + col = CURSOR_POS_COL(page); + row = CURSOR_POS_ROW(page); + INT10_ScrollWindow(row,0,ansi.nrows-1,ansi.ncols-1,ansi.data[0]? -ansi.data[0] : -1,ansi.attr,0xFF); + ClearAnsi(); + break; + case '>':/* proprietary NEC PC-98 MS-DOS codes (??) */ + if (IS_PC98_ARCH) { + ansi.pc98rab = true; + } + else { + LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: ESC [ > not supported outside PC-98 mode"); + ClearAnsi(); + } + break; + case 'l':/* (if code =7) disable linewrap */ + case 'p':/* reassign keys (needs strings) */ + case 'i':/* printer stuff */ + default: + LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unhandled char %c in esc[",data[count]); + ClearAnsi(); + break; + } + } + count++; + } + *size=count; + return true; +} + +bool device_CON::Seek(Bit32u * pos,Bit32u type) { + (void)pos; // UNUSED + (void)type; // UNUSED + // seek is valid + *pos = 0; + return true; +} + +bool device_CON::Close() { + return true; +} + +extern bool dos_con_use_int16_to_detect_input; + +Bit16u device_CON::GetInformation(void) { + if (dos_con_use_int16_to_detect_input || IS_PC98_ARCH) { + Bit16u ret = 0x80D3; /* No Key Available */ + + /* DOSBox-X behavior: Use INT 16h AH=0x11 Query keyboard status/preview key. + * The reason we do this is some DOS programs actually rely on hooking INT 16h + * to manipulate, hide, or transform what the DOS CON driver sees as well as + * itself. Perhaps the most disgusting example of this behavior would be the + * SCANDISK.EXE utility in Microsoft MS-DOS 6.22, which apparently relies on + * hooking INT 16h in this way to catch the Escape, CTRL+C, and some other + * scan codes in order to "eat" the scan codes before they get back to DOS. + * The reason they can get away with it apparently and still respond properly + * to those keys, is because the MS-DOS 6.22 CON driver always calls INT 16h + * AH=0x11 first before calling INT 16h AH=0x10 to fetch the scan code. + * + * Without this fix, SCANDISK.EXE does not respond properly to Escape and + * a few other keys. Pressing Escape will do nothing until you hit any other + * key, at which point it suddenly acts upon the Escape key. + * + * Since Scandisk is using INT 21h AH=0x0B to query STDIN during this time, + * this implementation is a good "halfway" compromise in that this call + * will trigger the INT 16h AH=0x11 hook it relies on. */ + if (readcache || dev_con_pos < dev_con_max) return 0x8093; /* key available */ + + Bitu saved_ax = reg_ax; + + reg_ah = (IS_EGAVGA_ARCH)?0x11:0x1; // check for keystroke + + /* FIXME: PC-98 emulation should eventually use CONIO emulation that + * better emulates the actual platform. The purpose of this + * hack is to allow our code to call into INT 16h without + * setting up an INT 16h vector */ + if (IS_PC98_ARCH) + INT16_Handler_Wrap(); + else + CALLBACK_RunRealInt(0x16); + + if (!GETFLAG(ZF)) { /* key is present, waiting to be returned on AH=0x10 or AH=0x00 */ + if (IS_PC98_ARCH && reg_al == 0) { + /* some scan codes are ignored by CON, and wouldn't read anything. + * while we're at it, take the scan code and convert it into ANSI here + * so that Read() returns it immediately instead of doing this conversion itself. + * This way we never block when we SAID a key was available that gets ignored. */ + if (CommonPC98ExtScanConversionToReadBuf(reg_ah)) + ret = 0x8093; /* Key Available */ + else + ret = 0x80D3; /* No Key Available */ + + /* need to consume the key. if it generated anything it will be returned to Read() + * through dev_con_readbuf[] */ + reg_ah=0x0; + + /* FIXME: PC-98 emulation should eventually use CONIO emulation that + * better emulates the actual platform. The purpose of this + * hack is to allow our code to call into INT 16h without + * setting up an INT 16h vector */ + INT16_Handler_Wrap(); + } + else { + ret = 0x8093; /* Key Available */ + } + } + + reg_ax = saved_ax; + return ret; + } + else { + /* DOSBox mainline behavior: alternate "fast" way through direct manipulation of keyboard scan buffer */ + Bit16u head=mem_readw(BIOS_KEYBOARD_BUFFER_HEAD); + Bit16u tail=mem_readw(BIOS_KEYBOARD_BUFFER_TAIL); + + if ((head==tail) && !readcache) return 0x80D3; /* No Key Available */ + if (readcache || real_readw(0x40,head)) return 0x8093; /* Key Available */ + + /* remove the zero from keyboard buffer */ + Bit16u start=mem_readw(BIOS_KEYBOARD_BUFFER_START); + Bit16u end =mem_readw(BIOS_KEYBOARD_BUFFER_END); + head+=2; + if (head>=end) head=start; + mem_writew(BIOS_KEYBOARD_BUFFER_HEAD,head); + } + + return 0x80D3; /* No Key Available */ +} + +device_CON::device_CON() { + SetName("CON"); + readcache=0; + lastwrite=0; + ansi.Disable(); + if (IS_PC98_ARCH) { + // NTS: On real hardware, the BIOS does NOT manage the console at all. + // TTY handling is entirely handled by MS-DOS. + ansi.ncols=80; + ansi.nrows=25 - 1; + // the DOS kernel will call on this function to disable, and SDLmain + // will call on to enable + } + else { + ansi.ncols=real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS); //should be updated once set/reset mode is implemented + ansi.nrows=real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS) + 1; + } + ansi.saverow=0; + ansi.savecol=0; + ansi.warned=false; + ClearAnsi(); +} + +void device_CON::ClearAnsi(void){ + for(Bit8u i=0; i 10000) break; - if (c != 'M') break; - - mcb.GetFileName(name); - LOG_MSG(" Type=0x%02x(%c) Seg=0x%04x size=0x%04x name='%s'\n", - mcb.GetType(),c, - mcb_segment+1,mcb.GetSize(),name); - mcb_next.SetPt((Bit16u)(mcb_segment+mcb.GetSize()+1)); - mcb_segment+=mcb.GetSize()+1; - mcb.SetPt(mcb_segment); - } - - mcb.GetFileName(name); - c = (char)mcb.GetType(); if (c < 32) c = '.'; - LOG_MSG("FINAL: Type=0x%02x(%c) Seg=0x%04x size=0x%04x name='%s'\n", - mcb.GetType(),c,mcb_segment+1,mcb.GetSize(),name); - LOG_MSG("End dump\n"); - -#if C_DEBUG - LOG_MSG("DOS fatal memory error: %s",msg); - throw int(7); // DOS non-fatal error (restart when debugger runs again) -#else - E_Exit("%s",msg); -#endif -} - -void DOS_CompressMemory(Bit16u first_segment=0/*default*/) { - Bit16u mcb_segment=dos.firstMCB; - DOS_MCB mcb(mcb_segment); - DOS_MCB mcb_next(0); - Bitu counter=0; - - while (mcb.GetType()!='Z') { - if(counter++ > 10000000) DOS_Mem_E_Exit("DOS_CompressMemory: DOS MCB list corrupted."); - mcb_next.SetPt((Bit16u)(mcb_segment+mcb.GetSize()+1)); - if (GCC_UNLIKELY((mcb_next.GetType()!=0x4d) && (mcb_next.GetType()!=0x5a))) DOS_Mem_E_Exit("Corrupt MCB chain"); - if (mcb_segment >= first_segment && (mcb.GetPSPSeg()==MCB_FREE) && (mcb_next.GetPSPSeg()==MCB_FREE)) { - mcb.SetSize(mcb.GetSize()+mcb_next.GetSize()+1); - mcb.SetType(mcb_next.GetType()); - } else { - mcb_segment+=mcb.GetSize()+1; - mcb.SetPt(mcb_segment); - } - } -} - -void DOS_FreeProcessMemory(Bit16u pspseg) { - Bit16u mcb_segment=dos.firstMCB; - DOS_MCB mcb(mcb_segment); - Bitu counter = 0; - - for (;;) { - if(counter++ > 10000000) DOS_Mem_E_Exit("DOS_FreeProcessMemory: DOS MCB list corrupted."); - if (mcb.GetPSPSeg()==pspseg) { - mcb.SetPSPSeg(MCB_FREE); - } - if (mcb.GetType()==0x5a) { - /* check if currently last block reaches up to the PCJr graphics memory */ - if ((machine==MCH_PCJR) && (mcb_segment+mcb.GetSize()==0x17fe) && - (real_readb(0x17ff,0)==0x4d) && (real_readw(0x17ff,1)==8)) { - /* re-enable the memory past segment 0x2000 */ - mcb.SetType(0x4d); - } else break; - } - if (GCC_UNLIKELY(mcb.GetType()!=0x4d)) DOS_Mem_E_Exit("Corrupt MCB chain"); - mcb_segment+=mcb.GetSize()+1; - mcb.SetPt(mcb_segment); - } - - Bit16u umb_start=dos_infoblock.GetStartOfUMBChain(); - if (umb_start==UMB_START_SEG) { - DOS_MCB umb_mcb(umb_start); - for (;;) { - if (umb_mcb.GetPSPSeg()==pspseg) { - umb_mcb.SetPSPSeg(MCB_FREE); - } - if (umb_mcb.GetType()!=0x4d) break; - umb_start+=umb_mcb.GetSize()+1; - umb_mcb.SetPt(umb_start); - } - } else if (umb_start!=0xffff) LOG(LOG_DOSMISC,LOG_ERROR)("Corrupt UMB chain: %x",umb_start); - - DOS_CompressMemory(); -} - -Bit16u DOS_GetMemAllocStrategy() { - return memAllocStrategy; -} - -bool DOS_SetMemAllocStrategy(Bit16u strat) { - if ((strat&0x3f)<3) { - memAllocStrategy = strat; - return true; - } - /* otherwise an invalid allocation strategy was specified */ - return false; -} - -extern bool dbg_zero_on_dos_allocmem; - -void DOS_zeromem(uint16_t seg,uint16_t para) { - uint32_t ofs,cnt; - - if (para == 0) return; - - ofs = ((uint32_t)seg << 4); - cnt = ((uint32_t)para << 4); - if ((ofs+cnt) > 0x100000) E_Exit("DOS_zeromem out of range"); - while (cnt != 0) { - mem_writeb(ofs++,0); - cnt--; - } -} - -bool DOS_AllocateMemory(Bit16u * segment,Bit16u * blocks) { - DOS_CompressMemory(); - Bit16u bigsize=0; - Bit16u mem_strat=memAllocStrategy; - Bit16u mcb_segment=dos.firstMCB; - - Bit16u umb_start=dos_infoblock.GetStartOfUMBChain(); - if (umb_start==UMB_START_SEG) { - /* start with UMBs if requested (bits 7 or 6 set) */ - if (mem_strat&0xc0) mcb_segment=umb_start; - } else if (umb_start!=0xffff) LOG(LOG_DOSMISC,LOG_ERROR)("Corrupt UMB chain: %x",umb_start); - - DOS_MCB mcb(0); - DOS_MCB mcb_next(0); - DOS_MCB psp_mcb(dos.psp()-1); - char psp_name[9]; - psp_mcb.GetFileName(psp_name); - Bit16u found_seg=0,found_seg_size=0; - for (;;) { - mcb.SetPt(mcb_segment); - if (mcb.GetPSPSeg()==MCB_FREE) { - /* Check for enough free memory in current block */ - Bit16u block_size=mcb.GetSize(); - if (block_size<(*blocks)) { - if (bigsize total) - DOS_CompressMemory(segment-1); - - if (*blocks<=total) { - if (GCC_UNLIKELY(*blocks==total)) { - /* Nothing to do */ - DOS_CompressMemory(); - return true; - } - /* Shrinking MCB */ - DOS_MCB mcb_new_next(segment+(*blocks)); - mcb.SetSize(*blocks); - mcb_new_next.SetType(mcb.GetType()); - if (mcb.GetType()==0x5a) { - /* Further blocks follow */ - mcb.SetType(0x4d); - } - - mcb_new_next.SetSize(total-*blocks-1); - mcb_new_next.SetPSPSeg(MCB_FREE); - mcb.SetPSPSeg(dos.psp()); - DOS_CompressMemory(); - return true; - } - /* MCB will grow, try to join with following MCB */ - if (mcb.GetType()!=0x5a) { - if (mcb_next.GetPSPSeg()==MCB_FREE) { - total+=mcb_next.GetSize()+1; - } - } - if (*blockstotal, - in the second case resize block to maximum */ - - if ((mcb_next.GetPSPSeg()==MCB_FREE) && (mcb.GetType()!=0x5a)) { - /* adjust type of joined MCB */ - mcb.SetType(mcb_next.GetType()); - } - mcb.SetSize(total); - mcb.SetPSPSeg(dos.psp()); - DOS_CompressMemory(); - if (*blocks==total) return true; /* block fit exactly */ - - *blocks=total; /* return maximum */ - DOS_SetError(DOSERR_INSUFFICIENT_MEMORY); - return false; -} - - -bool DOS_FreeMemory(Bit16u segment) { -//TODO Check if allowed to free this segment - if (segment < DOS_MEM_START+1) { - LOG(LOG_DOSMISC,LOG_ERROR)("Program tried to free %X ---ERROR",segment); - DOS_SetError(DOSERR_MB_ADDRESS_INVALID); - return false; - } - - DOS_MCB mcb(segment-1); - if ((mcb.GetType()!=0x4d) && (mcb.GetType()!=0x5a)) { - DOS_SetError(DOSERR_MB_ADDRESS_INVALID); - return false; - } - -#ifdef DEBUG_ALLOC - LOG(LOG_MISC,LOG_DEBUG)("DOS_FreeMemory(seg=0x%04x)",segment); -#endif - - mcb.SetPSPSeg(MCB_FREE); -// DOS_CompressMemory(); - return true; -} - -Bitu GetEMSPageFrameSegment(void); - -void DOS_BuildUMBChain(bool umb_active,bool /*ems_active*/) { - unsigned int seg_limit = MEM_TotalPages()*256; - - /* UMBs are only possible if the machine has 1MB+64KB of RAM */ - if (umb_active && (machine!=MCH_TANDY) && seg_limit >= (0x10000+0x1000-1) && first_umb_seg < GetEMSPageFrameSegment()) { - /* XMS emulation sets UMB size now. - * PCjr mode disables UMB emulation */ -#if 0 - if (ems_active) { - /* we can use UMBs up to the EMS page frame */ - /* FIXME: when we make the EMS page frame configurable this will need to be updated */ - first_umb_size = GetEMSPageFrameSegment() - first_umb_seg; - } - else if (machine == MCH_PCJR) { - /* we can use UMBs up to where PCjr wants cartridge ROM */ - first_umb_size = 0xE000 - first_umb_seg; - } -#endif - - dos_infoblock.SetStartOfUMBChain(UMB_START_SEG); - dos_infoblock.SetUMBChainState(0); // UMBs not linked yet - - DOS_MCB umb_mcb(first_umb_seg); - umb_mcb.SetPSPSeg(0); // currently free - umb_mcb.SetSize(first_umb_size-1); - umb_mcb.SetType(0x5a); - - /* Scan MCB-chain for last block */ - Bit16u mcb_segment=dos.firstMCB; - DOS_MCB mcb(mcb_segment); - while (mcb.GetType()!=0x5a) { - mcb_segment+=mcb.GetSize()+1; - mcb.SetPt(mcb_segment); - } - - /* A system MCB has to cover the space between the - regular MCB-chain and the UMBs */ - Bit16u cover_mcb=(Bit16u)(mcb_segment+mcb.GetSize()+1); - mcb.SetPt(cover_mcb); - mcb.SetType(0x4d); - mcb.SetPSPSeg(0x0008); - mcb.SetSize(first_umb_seg-cover_mcb-1); - mcb.SetFileName("SC "); - - } else { - dos_infoblock.SetStartOfUMBChain(0xffff); - dos_infoblock.SetUMBChainState(0); - } -} - -bool DOS_LinkUMBsToMemChain(Bit16u linkstate) { - /* Get start of UMB-chain */ - Bit16u umb_start=dos_infoblock.GetStartOfUMBChain(); - if (umb_start!=UMB_START_SEG) { - if (umb_start!=0xffff) LOG(LOG_DOSMISC,LOG_ERROR)("Corrupt UMB chain: %x",umb_start); - return false; - } - - if ((linkstate&1)==(dos_infoblock.GetUMBChainState()&1)) return true; - - /* Scan MCB-chain for last block before UMB-chain */ - Bit16u mcb_segment=dos.firstMCB; - Bit16u prev_mcb_segment=dos.firstMCB; - DOS_MCB mcb(mcb_segment); - while ((mcb_segment!=umb_start) && (mcb.GetType()!=0x5a)) { - prev_mcb_segment=mcb_segment; - mcb_segment+=mcb.GetSize()+1; - mcb.SetPt(mcb_segment); - } - DOS_MCB prev_mcb(prev_mcb_segment); - - switch (linkstate) { - case 0x0000: // unlink - if ((prev_mcb.GetType()==0x4d) && (mcb_segment==umb_start)) { - prev_mcb.SetType(0x5a); - } - dos_infoblock.SetUMBChainState(0); - break; - case 0x0001: // link - if (mcb.GetType()==0x5a) { - if ((mcb_segment+mcb.GetSize()+1) != umb_start) { - LOG_MSG("MCB chain no longer goes to end of memory (corruption?), not linking in UMB!"); - return false; - } - mcb.SetType(0x4d); - dos_infoblock.SetUMBChainState(1); - } - break; - default: - /* NTS: Some programs apparently call this function incorrectly. - * The CauseWay extender in Open Watcom 1.9's installer for example - * calls this function with AX=0x5803 BX=0x58 */ - return false; - } - - return true; -} - - -static Bitu DOS_default_handler(void) { - LOG(LOG_CPU,LOG_ERROR)("DOS rerouted Interrupt Called %X",lastint); - return CBRET_NONE; -} - -#include - -extern Bit16u DOS_IHSEG; - -extern bool enable_dummy_device_mcb; -extern bool iret_only_for_debug_interrupts; - -static CALLBACK_HandlerObject callbackhandler; -void DOS_SetupMemory(void) { - unsigned int max_conv; - unsigned int seg_limit; - - max_conv = (unsigned int)mem_readw(BIOS_MEMORY_SIZE) << (10u - 4u); - seg_limit = MEM_TotalPages()*256; - if (seg_limit > max_conv) seg_limit = max_conv; - UMB_START_SEG = max_conv - 1; - - /* Let dos claim a few bios interrupts. Makes DOSBox more compatible with - * buggy games, which compare against the interrupt table. (probably a - * broken linked list implementation) */ - callbackhandler.Allocate(&DOS_default_handler,"DOS default int"); - Bit16u ihseg; - Bit16u ihofs; - - assert(DOS_IHSEG != 0); - ihseg = DOS_IHSEG; - ihofs = 0x08; - - real_writeb(ihseg,ihofs+0x00,(Bit8u)0xFE); //GRP 4 - real_writeb(ihseg,ihofs+0x01,(Bit8u)0x38); //Extra Callback instruction - real_writew(ihseg,ihofs+0x02,callbackhandler.Get_callback()); //The immediate word - real_writeb(ihseg,ihofs+0x04,(Bit8u)0xCF); //An IRET Instruction - - if (iret_only_for_debug_interrupts) { - //point at IRET, not the callback. Hack for paranoid games & demos that check for debugger presence. - RealSetVec(0x01,RealMake(ihseg,ihofs+4)); - RealSetVec(0x03,RealMake(ihseg,ihofs+4)); - } - else { - RealSetVec(0x01,RealMake(ihseg,ihofs)); //BioMenace (offset!=4) - RealSetVec(0x03,RealMake(ihseg,ihofs)); //Alien Incident (offset!=0) - } - if (machine != MCH_PCJR) RealSetVec(0x02,RealMake(ihseg,ihofs)); //BioMenace (segment<0x8000). Else, taken by BIOS NMI interrupt - RealSetVec(0x04,RealMake(ihseg,ihofs)); //Shadow President (lower byte of segment!=0) -// RealSetVec(0x0f,RealMake(ihseg,ihofs)); //Always a tricky one (soundblaster irq) - - Bit16u mcb_sizes=0; - - if (enable_dummy_device_mcb) { - // Create a dummy device MCB with PSPSeg=0x0008 - LOG_MSG("Dummy device MCB at segment 0x%x",DOS_MEM_START+mcb_sizes); - DOS_MCB mcb_devicedummy((Bit16u)DOS_MEM_START+mcb_sizes); - mcb_devicedummy.SetPSPSeg(MCB_DOS); // Devices - mcb_devicedummy.SetSize(16); - mcb_devicedummy.SetType(0x4d); // More blocks will follow - mcb_sizes+=1+16; - -// We DO need to mark this area as 'SD' but leaving it blank so far -// confuses MEM.EXE (shows ???????) which suggests other software -// might have a problem with it as well. -// mcb_devicedummy.SetFileName("SD "); - } - - DOS_MCB mcb((Bit16u)DOS_MEM_START+mcb_sizes); - mcb.SetPSPSeg(MCB_FREE); //Free - mcb.SetType(0x5a); //Last Block - if (machine==MCH_TANDY) { - if (seg_limit < ((384*1024)/16)) - E_Exit("Tandy requires at least 384K"); - /* memory up to 608k available, the rest (to 640k) is used by - the tandy graphics system's variable mapping of 0xb800 */ -/* - mcb.SetSize(0x9BFF - DOS_MEM_START - mcb_sizes); -*/ mcb.SetSize(/*0x9BFF*/(seg_limit-0x801) - DOS_MEM_START - mcb_sizes); - } else if (machine==MCH_PCJR) { - DOS_MCB mcb_devicedummy((Bit16u)0x2000); - - /* FIXME: The PCjr can have built-in either 64KB or 128KB of RAM. - * RAM beyond 128KB is made possible with expansion sidecars. - * DOSBox-X needs to support memsizekb=64 or memsizekb=128, - * and adjust video ram location appropriately. */ - - if (seg_limit < ((256*1024)/16)) - E_Exit("PCjr requires at least 256K"); - /* memory from 128k to 640k is available */ - mcb_devicedummy.SetPt((Bit16u)0x2000); - mcb_devicedummy.SetPSPSeg(MCB_FREE); - mcb_devicedummy.SetSize(/*0x9FFF*/(seg_limit-1) - 0x2000); - mcb_devicedummy.SetType(0x5a); - - /* exclude PCJr graphics region */ - mcb_devicedummy.SetPt((Bit16u)0x17ff); - mcb_devicedummy.SetPSPSeg(MCB_DOS); - mcb_devicedummy.SetSize(0x800); - mcb_devicedummy.SetType(0x4d); - - /* memory below 96k */ - mcb.SetSize(0x1800 - DOS_MEM_START - (2+mcb_sizes)); - mcb.SetType(0x4d); - } else { -#ifndef DEBUG_ALLOC - /* NTS: Testing suggests we can push as low as 4KB. However, Wikipedia and - * other sites suggest that the IBM PC only went as low as 16KB when - * it first sold, and even that wasn't too typical. But what the hell, - * we'll allow as little as 4KB if not for fun DOS hacking. */ - if (seg_limit < ((4*1024)/16)) - E_Exit("Standard PC requires at least 4K"); -#endif - - /* complete memory up to 640k available */ - /* last paragraph used to add UMB chain to low-memory MCB chain */ - mcb.SetSize(/*0x9FFE*/(seg_limit-2) - DOS_MEM_START - mcb_sizes); - } - - dos.firstMCB=DOS_MEM_START; - dos_infoblock.SetFirstMCB(DOS_MEM_START); -} - -void DOS_UnsetupMemory() { - callbackhandler.Uninstall(); -} - +/* + * Copyright (C) 2002-2015 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#include "dosbox.h" +#include "mem.h" +#include "bios.h" +#include "dos_inc.h" +#include "callback.h" + +// uncomment for alloc/free debug messages +#define DEBUG_ALLOC + +Bitu UMB_START_SEG = 0x9FFF; +/* FIXME: This should be a variable that reflects the last RAM segment. + * That means 0x9FFF if 640KB or more, or a lesser value if less than 640KB */ +//#define UMB_START_SEG 0x9fff + +Bit16u first_umb_seg = 0xd000; +Bit16u first_umb_size = 0x2000; + +static Bit16u memAllocStrategy = 0x00; + +static void DOS_Mem_E_Exit(const char *msg) { + Bit16u mcb_segment=dos.firstMCB; + DOS_MCB mcb(mcb_segment); + DOS_MCB mcb_next(0); + Bitu counter=0; + char name[10]; + char c; + + LOG_MSG("DOS MCB dump:\n"); + while ((c=(char)mcb.GetType()) != 'Z') { + if (counter++ > 10000) break; + if (c != 'M') break; + + mcb.GetFileName(name); + LOG_MSG(" Type=0x%02x(%c) Seg=0x%04x size=0x%04x name='%s'\n", + mcb.GetType(),c, + mcb_segment+1,mcb.GetSize(),name); + mcb_next.SetPt((Bit16u)(mcb_segment+mcb.GetSize()+1)); + mcb_segment+=mcb.GetSize()+1; + mcb.SetPt(mcb_segment); + } + + mcb.GetFileName(name); + c = (char)mcb.GetType(); if (c < 32) c = '.'; + LOG_MSG("FINAL: Type=0x%02x(%c) Seg=0x%04x size=0x%04x name='%s'\n", + mcb.GetType(),c,mcb_segment+1,mcb.GetSize(),name); + LOG_MSG("End dump\n"); + +#if C_DEBUG + LOG_MSG("DOS fatal memory error: %s",msg); + throw int(7); // DOS non-fatal error (restart when debugger runs again) +#else + E_Exit("%s",msg); +#endif +} + +void DOS_CompressMemory(Bit16u first_segment=0/*default*/) { + Bit16u mcb_segment=dos.firstMCB; + DOS_MCB mcb(mcb_segment); + DOS_MCB mcb_next(0); + Bitu counter=0; + + while (mcb.GetType()!='Z') { + if(counter++ > 10000000) DOS_Mem_E_Exit("DOS_CompressMemory: DOS MCB list corrupted."); + mcb_next.SetPt((Bit16u)(mcb_segment+mcb.GetSize()+1)); + if (GCC_UNLIKELY((mcb_next.GetType()!=0x4d) && (mcb_next.GetType()!=0x5a))) DOS_Mem_E_Exit("Corrupt MCB chain"); + if (mcb_segment >= first_segment && (mcb.GetPSPSeg()==MCB_FREE) && (mcb_next.GetPSPSeg()==MCB_FREE)) { + mcb.SetSize(mcb.GetSize()+mcb_next.GetSize()+1); + mcb.SetType(mcb_next.GetType()); + } else { + mcb_segment+=mcb.GetSize()+1; + mcb.SetPt(mcb_segment); + } + } +} + +void DOS_FreeProcessMemory(Bit16u pspseg) { + Bit16u mcb_segment=dos.firstMCB; + DOS_MCB mcb(mcb_segment); + Bitu counter = 0; + + for (;;) { + if(counter++ > 10000000) DOS_Mem_E_Exit("DOS_FreeProcessMemory: DOS MCB list corrupted."); + if (mcb.GetPSPSeg()==pspseg) { + mcb.SetPSPSeg(MCB_FREE); + } + if (mcb.GetType()==0x5a) { + /* check if currently last block reaches up to the PCJr graphics memory */ + if ((machine==MCH_PCJR) && (mcb_segment+mcb.GetSize()==0x17fe) && + (real_readb(0x17ff,0)==0x4d) && (real_readw(0x17ff,1)==8)) { + /* re-enable the memory past segment 0x2000 */ + mcb.SetType(0x4d); + } else break; + } + if (GCC_UNLIKELY(mcb.GetType()!=0x4d)) DOS_Mem_E_Exit("Corrupt MCB chain"); + mcb_segment+=mcb.GetSize()+1; + mcb.SetPt(mcb_segment); + } + + Bit16u umb_start=dos_infoblock.GetStartOfUMBChain(); + if (umb_start==UMB_START_SEG) { + DOS_MCB umb_mcb(umb_start); + for (;;) { + if (umb_mcb.GetPSPSeg()==pspseg) { + umb_mcb.SetPSPSeg(MCB_FREE); + } + if (umb_mcb.GetType()!=0x4d) break; + umb_start+=umb_mcb.GetSize()+1; + umb_mcb.SetPt(umb_start); + } + } else if (umb_start!=0xffff) LOG(LOG_DOSMISC,LOG_ERROR)("Corrupt UMB chain: %x",umb_start); + + DOS_CompressMemory(); +} + +Bit16u DOS_GetMemAllocStrategy() { + return memAllocStrategy; +} + +bool DOS_SetMemAllocStrategy(Bit16u strat) { + if ((strat&0x3f)<3) { + memAllocStrategy = strat; + return true; + } + /* otherwise an invalid allocation strategy was specified */ + return false; +} + +extern bool dbg_zero_on_dos_allocmem; + +void DOS_zeromem(uint16_t seg,uint16_t para) { + uint32_t ofs,cnt; + + if (para == 0) return; + + ofs = ((uint32_t)seg << 4); + cnt = ((uint32_t)para << 4); + if ((ofs+cnt) > 0x100000) E_Exit("DOS_zeromem out of range"); + while (cnt != 0) { + mem_writeb(ofs++,0); + cnt--; + } +} + +bool DOS_AllocateMemory(Bit16u * segment,Bit16u * blocks) { + DOS_CompressMemory(); + Bit16u bigsize=0; + Bit16u mem_strat=memAllocStrategy; + Bit16u mcb_segment=dos.firstMCB; + + Bit16u umb_start=dos_infoblock.GetStartOfUMBChain(); + if (umb_start==UMB_START_SEG) { + /* start with UMBs if requested (bits 7 or 6 set) */ + if (mem_strat&0xc0) mcb_segment=umb_start; + } else if (umb_start!=0xffff) LOG(LOG_DOSMISC,LOG_ERROR)("Corrupt UMB chain: %x",umb_start); + + DOS_MCB mcb(0); + DOS_MCB mcb_next(0); + DOS_MCB psp_mcb(dos.psp()-1); + char psp_name[9]; + psp_mcb.GetFileName(psp_name); + Bit16u found_seg=0,found_seg_size=0; + for (;;) { + mcb.SetPt(mcb_segment); + if (mcb.GetPSPSeg()==MCB_FREE) { + /* Check for enough free memory in current block */ + Bit16u block_size=mcb.GetSize(); + if (block_size<(*blocks)) { + if (bigsize total) + DOS_CompressMemory(segment-1); + + if (*blocks<=total) { + if (GCC_UNLIKELY(*blocks==total)) { + /* Nothing to do */ + DOS_CompressMemory(); + return true; + } + /* Shrinking MCB */ + DOS_MCB mcb_new_next(segment+(*blocks)); + mcb.SetSize(*blocks); + mcb_new_next.SetType(mcb.GetType()); + if (mcb.GetType()==0x5a) { + /* Further blocks follow */ + mcb.SetType(0x4d); + } + + mcb_new_next.SetSize(total-*blocks-1); + mcb_new_next.SetPSPSeg(MCB_FREE); + mcb.SetPSPSeg(dos.psp()); + DOS_CompressMemory(); + return true; + } + /* MCB will grow, try to join with following MCB */ + if (mcb.GetType()!=0x5a) { + if (mcb_next.GetPSPSeg()==MCB_FREE) { + total+=mcb_next.GetSize()+1; + } + } + if (*blockstotal, + in the second case resize block to maximum */ + + if ((mcb_next.GetPSPSeg()==MCB_FREE) && (mcb.GetType()!=0x5a)) { + /* adjust type of joined MCB */ + mcb.SetType(mcb_next.GetType()); + } + mcb.SetSize(total); + mcb.SetPSPSeg(dos.psp()); + DOS_CompressMemory(); + if (*blocks==total) return true; /* block fit exactly */ + + *blocks=total; /* return maximum */ + DOS_SetError(DOSERR_INSUFFICIENT_MEMORY); + return false; +} + + +bool DOS_FreeMemory(Bit16u segment) { +//TODO Check if allowed to free this segment + if (segment < DOS_MEM_START+1) { + LOG(LOG_DOSMISC,LOG_ERROR)("Program tried to free %X ---ERROR",segment); + DOS_SetError(DOSERR_MB_ADDRESS_INVALID); + return false; + } + + DOS_MCB mcb(segment-1); + if ((mcb.GetType()!=0x4d) && (mcb.GetType()!=0x5a)) { + DOS_SetError(DOSERR_MB_ADDRESS_INVALID); + return false; + } + +#ifdef DEBUG_ALLOC + LOG(LOG_MISC,LOG_DEBUG)("DOS_FreeMemory(seg=0x%04x)",segment); +#endif + + mcb.SetPSPSeg(MCB_FREE); +// DOS_CompressMemory(); + return true; +} + +Bitu GetEMSPageFrameSegment(void); + +void DOS_BuildUMBChain(bool umb_active,bool /*ems_active*/) { + unsigned int seg_limit = MEM_TotalPages()*256; + + /* UMBs are only possible if the machine has 1MB+64KB of RAM */ + if (umb_active && (machine!=MCH_TANDY) && seg_limit >= (0x10000+0x1000-1) && first_umb_seg < GetEMSPageFrameSegment()) { + /* XMS emulation sets UMB size now. + * PCjr mode disables UMB emulation */ +#if 0 + if (ems_active) { + /* we can use UMBs up to the EMS page frame */ + /* FIXME: when we make the EMS page frame configurable this will need to be updated */ + first_umb_size = GetEMSPageFrameSegment() - first_umb_seg; + } + else if (machine == MCH_PCJR) { + /* we can use UMBs up to where PCjr wants cartridge ROM */ + first_umb_size = 0xE000 - first_umb_seg; + } +#endif + + dos_infoblock.SetStartOfUMBChain(UMB_START_SEG); + dos_infoblock.SetUMBChainState(0); // UMBs not linked yet + + DOS_MCB umb_mcb(first_umb_seg); + umb_mcb.SetPSPSeg(0); // currently free + umb_mcb.SetSize(first_umb_size-1); + umb_mcb.SetType(0x5a); + + /* Scan MCB-chain for last block */ + Bit16u mcb_segment=dos.firstMCB; + DOS_MCB mcb(mcb_segment); + while (mcb.GetType()!=0x5a) { + mcb_segment+=mcb.GetSize()+1; + mcb.SetPt(mcb_segment); + } + + /* A system MCB has to cover the space between the + regular MCB-chain and the UMBs */ + Bit16u cover_mcb=(Bit16u)(mcb_segment+mcb.GetSize()+1); + mcb.SetPt(cover_mcb); + mcb.SetType(0x4d); + mcb.SetPSPSeg(0x0008); + mcb.SetSize(first_umb_seg-cover_mcb-1); + mcb.SetFileName("SC "); + + } else { + dos_infoblock.SetStartOfUMBChain(0xffff); + dos_infoblock.SetUMBChainState(0); + } +} + +bool DOS_LinkUMBsToMemChain(Bit16u linkstate) { + /* Get start of UMB-chain */ + Bit16u umb_start=dos_infoblock.GetStartOfUMBChain(); + if (umb_start!=UMB_START_SEG) { + if (umb_start!=0xffff) LOG(LOG_DOSMISC,LOG_ERROR)("Corrupt UMB chain: %x",umb_start); + return false; + } + + if ((linkstate&1)==(dos_infoblock.GetUMBChainState()&1)) return true; + + /* Scan MCB-chain for last block before UMB-chain */ + Bit16u mcb_segment=dos.firstMCB; + Bit16u prev_mcb_segment=dos.firstMCB; + DOS_MCB mcb(mcb_segment); + while ((mcb_segment!=umb_start) && (mcb.GetType()!=0x5a)) { + prev_mcb_segment=mcb_segment; + mcb_segment+=mcb.GetSize()+1; + mcb.SetPt(mcb_segment); + } + DOS_MCB prev_mcb(prev_mcb_segment); + + switch (linkstate) { + case 0x0000: // unlink + if ((prev_mcb.GetType()==0x4d) && (mcb_segment==umb_start)) { + prev_mcb.SetType(0x5a); + } + dos_infoblock.SetUMBChainState(0); + break; + case 0x0001: // link + if (mcb.GetType()==0x5a) { + if ((mcb_segment+mcb.GetSize()+1) != umb_start) { + LOG_MSG("MCB chain no longer goes to end of memory (corruption?), not linking in UMB!"); + return false; + } + mcb.SetType(0x4d); + dos_infoblock.SetUMBChainState(1); + } + break; + default: + /* NTS: Some programs apparently call this function incorrectly. + * The CauseWay extender in Open Watcom 1.9's installer for example + * calls this function with AX=0x5803 BX=0x58 */ + return false; + } + + return true; +} + + +static Bitu DOS_default_handler(void) { + LOG(LOG_CPU,LOG_ERROR)("DOS rerouted Interrupt Called %X",lastint); + return CBRET_NONE; +} + +#include + +extern Bit16u DOS_IHSEG; + +extern bool enable_dummy_device_mcb; +extern bool iret_only_for_debug_interrupts; + +static CALLBACK_HandlerObject callbackhandler; +void DOS_SetupMemory(void) { + unsigned int max_conv; + unsigned int seg_limit; + + max_conv = (unsigned int)mem_readw(BIOS_MEMORY_SIZE) << (10u - 4u); + seg_limit = MEM_TotalPages()*256; + if (seg_limit > max_conv) seg_limit = max_conv; + UMB_START_SEG = max_conv - 1; + + /* Let dos claim a few bios interrupts. Makes DOSBox more compatible with + * buggy games, which compare against the interrupt table. (probably a + * broken linked list implementation) */ + callbackhandler.Allocate(&DOS_default_handler,"DOS default int"); + Bit16u ihseg; + Bit16u ihofs; + + assert(DOS_IHSEG != 0); + ihseg = DOS_IHSEG; + ihofs = 0x08; + + real_writeb(ihseg,ihofs+0x00,(Bit8u)0xFE); //GRP 4 + real_writeb(ihseg,ihofs+0x01,(Bit8u)0x38); //Extra Callback instruction + real_writew(ihseg,ihofs+0x02,callbackhandler.Get_callback()); //The immediate word + real_writeb(ihseg,ihofs+0x04,(Bit8u)0xCF); //An IRET Instruction + + if (iret_only_for_debug_interrupts) { + //point at IRET, not the callback. Hack for paranoid games & demos that check for debugger presence. + RealSetVec(0x01,RealMake(ihseg,ihofs+4)); + RealSetVec(0x03,RealMake(ihseg,ihofs+4)); + } + else { + RealSetVec(0x01,RealMake(ihseg,ihofs)); //BioMenace (offset!=4) + RealSetVec(0x03,RealMake(ihseg,ihofs)); //Alien Incident (offset!=0) + } + if (machine != MCH_PCJR) RealSetVec(0x02,RealMake(ihseg,ihofs)); //BioMenace (segment<0x8000). Else, taken by BIOS NMI interrupt + RealSetVec(0x04,RealMake(ihseg,ihofs)); //Shadow President (lower byte of segment!=0) +// RealSetVec(0x0f,RealMake(ihseg,ihofs)); //Always a tricky one (soundblaster irq) + + Bit16u mcb_sizes=0; + + if (enable_dummy_device_mcb) { + // Create a dummy device MCB with PSPSeg=0x0008 + LOG_MSG("Dummy device MCB at segment 0x%x",DOS_MEM_START+mcb_sizes); + DOS_MCB mcb_devicedummy((Bit16u)DOS_MEM_START+mcb_sizes); + mcb_devicedummy.SetPSPSeg(MCB_DOS); // Devices + mcb_devicedummy.SetSize(16); + mcb_devicedummy.SetType(0x4d); // More blocks will follow + mcb_sizes+=1+16; + +// We DO need to mark this area as 'SD' but leaving it blank so far +// confuses MEM.EXE (shows ???????) which suggests other software +// might have a problem with it as well. +// mcb_devicedummy.SetFileName("SD "); + } + + DOS_MCB mcb((Bit16u)DOS_MEM_START+mcb_sizes); + mcb.SetPSPSeg(MCB_FREE); //Free + mcb.SetType(0x5a); //Last Block + if (machine==MCH_TANDY) { + if (seg_limit < ((384*1024)/16)) + E_Exit("Tandy requires at least 384K"); + /* memory up to 608k available, the rest (to 640k) is used by + the tandy graphics system's variable mapping of 0xb800 */ +/* + mcb.SetSize(0x9BFF - DOS_MEM_START - mcb_sizes); +*/ mcb.SetSize(/*0x9BFF*/(seg_limit-0x801) - DOS_MEM_START - mcb_sizes); + } else if (machine==MCH_PCJR) { + DOS_MCB mcb_devicedummy((Bit16u)0x2000); + + /* FIXME: The PCjr can have built-in either 64KB or 128KB of RAM. + * RAM beyond 128KB is made possible with expansion sidecars. + * DOSBox-X needs to support memsizekb=64 or memsizekb=128, + * and adjust video ram location appropriately. */ + + if (seg_limit < ((256*1024)/16)) + E_Exit("PCjr requires at least 256K"); + /* memory from 128k to 640k is available */ + mcb_devicedummy.SetPt((Bit16u)0x2000); + mcb_devicedummy.SetPSPSeg(MCB_FREE); + mcb_devicedummy.SetSize(/*0x9FFF*/(seg_limit-1) - 0x2000); + mcb_devicedummy.SetType(0x5a); + + /* exclude PCJr graphics region */ + mcb_devicedummy.SetPt((Bit16u)0x17ff); + mcb_devicedummy.SetPSPSeg(MCB_DOS); + mcb_devicedummy.SetSize(0x800); + mcb_devicedummy.SetType(0x4d); + + /* memory below 96k */ + mcb.SetSize(0x1800 - DOS_MEM_START - (2+mcb_sizes)); + mcb.SetType(0x4d); + } else { +#ifndef DEBUG_ALLOC + /* NTS: Testing suggests we can push as low as 4KB. However, Wikipedia and + * other sites suggest that the IBM PC only went as low as 16KB when + * it first sold, and even that wasn't too typical. But what the hell, + * we'll allow as little as 4KB if not for fun DOS hacking. */ + if (seg_limit < ((4*1024)/16)) + E_Exit("Standard PC requires at least 4K"); +#endif + + /* complete memory up to 640k available */ + /* last paragraph used to add UMB chain to low-memory MCB chain */ + mcb.SetSize(/*0x9FFE*/(seg_limit-2) - DOS_MEM_START - mcb_sizes); + } + + dos.firstMCB=DOS_MEM_START; + dos_infoblock.SetFirstMCB(DOS_MEM_START); +} + +void DOS_UnsetupMemory() { + callbackhandler.Uninstall(); +} + diff --git a/src/hardware/ide.cpp b/src/hardware/ide.cpp index 8ddfaaa3d..e9505d9c3 100644 --- a/src/hardware/ide.cpp +++ b/src/hardware/ide.cpp @@ -1,4004 +1,4004 @@ -/* - * IDE ATA/ATAPI and controller emulation for DOSBox-X - * (C) 2012 Jonathan Campbell - - * [insert open source license here] - */ - -/* $Id: ide.cpp,v 1.49 2009-04-10 09:53:04 c2woody Exp $ */ - -#include -#include -#include "dosbox.h" -#include "inout.h" -#include "pic.h" -#include "mem.h" -#include "cpu.h" -#include "ide.h" -#include "mixer.h" -#include "timer.h" -#include "setup.h" -#include "control.h" -#include "callback.h" -#include "bios_disk.h" -#include "../src/dos/cdrom.h" - -#if defined(_MSC_VER) -# pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */ -# pragma warning(disable:4305) /* truncation from double to float */ -#endif - -#ifdef _MSC_VER -# define MIN(a,b) ((a) < (b) ? (a) : (b)) -#else -# define MIN(a,b) std::min(a,b) -#endif - -#if defined(_MSC_VER) -# pragma warning(disable:4065) /* switch statement no case labels */ -#endif - -static unsigned char init_ide = 0; - -static const unsigned char IDE_default_IRQs[4] = { - 14, /* primary */ - 15, /* secondary */ - 11, /* tertiary */ - 10 /* quaternary */ -}; - -static const unsigned short IDE_default_bases[4] = { - 0x1F0, /* primary */ - 0x170, /* secondary */ - 0x1E8, /* tertiary */ - 0x168 /* quaternary */ -}; - -static const unsigned short IDE_default_alts[4] = { - 0x3F6, /* primary */ - 0x376, /* secondary */ - 0x3EE, /* tertiary */ - 0x36E /* quaternary */ -}; - -bool fdc_takes_port_3F7(); - -static void ide_altio_w(Bitu port,Bitu val,Bitu iolen); -static Bitu ide_altio_r(Bitu port,Bitu iolen); -static void ide_baseio_w(Bitu port,Bitu val,Bitu iolen); -static Bitu ide_baseio_r(Bitu port,Bitu iolen); -bool GetMSCDEXDrive(unsigned char drive_letter,CDROM_Interface **_cdrom); - -enum IDEDeviceType { - IDE_TYPE_NONE, - IDE_TYPE_HDD=1, - IDE_TYPE_CDROM -}; - -enum IDEDeviceState { - IDE_DEV_READY=0, - IDE_DEV_SELECT_WAIT, - IDE_DEV_CONFUSED, - IDE_DEV_BUSY, - IDE_DEV_DATA_READ, - IDE_DEV_DATA_WRITE, - IDE_DEV_ATAPI_PACKET_COMMAND, - IDE_DEV_ATAPI_BUSY -}; - -enum { - IDE_STATUS_BUSY=0x80, - IDE_STATUS_DRIVE_READY=0x40, - IDE_STATUS_DRIVE_SEEK_COMPLETE=0x10, - IDE_STATUS_DRQ=0x08, - IDE_STATUS_ERROR=0x01 -}; - -class IDEController; - -#if 0//unused -static inline bool drivehead_is_lba48(uint8_t val) { - return (val&0xE0) == 0x40; -} -#endif - -static inline bool drivehead_is_lba(uint8_t val) { - return (val&0xE0) == 0xE0; -} - -#if 0//unused -static inline bool drivehead_is_chs(uint8_t val) { - return (val&0xE0) == 0xA0; -} -#endif - -class IDEDevice { -public: - IDEController *controller; - uint16_t feature,count,lba[3]; /* feature = BASE+1 count = BASE+2 lba[3] = BASE+3,+4,+5 */ - uint8_t command,drivehead,status; /* command/status = BASE+7 drivehead = BASE+6 */ - enum IDEDeviceType type; - bool faked_command; /* if set, DOSBox is sending commands to itself */ - bool allow_writing; - bool motor_on; - bool asleep; - IDEDeviceState state; - /* feature: 0x1F1 (Word 00h in ATA specs) - count: 0x1F2 (Word 01h in ATA specs) - lba[3]: 0x1F3 (Word 02h) 0x1F4 (Word 03h) and 0x1F5 (Word 04h) - drivehead: 0x1F6 (copy of last value written) - command: 0x1F7 (Word 05h) - status: 0x1F7 (value read back to IDE controller, including busy and drive ready bits as well as error status) - - In C/H/S modes lba[3] becomes lba[0]=sector lba[1]=cylinder-low lba[2]=cylinder-high and - the code must read the 4-bit head number from drivehead[bits 3:0]. - - "drivehead" in this struct is always maintained as a device copy of the controller's - drivehead value. it is only updated on write, and not returned on read. - - "allow_writing" if set allows the DOS program/OS to write the registers. It is - clear during command execution, obviously, so the state of the device is not confused - while executing the command. - - Registers are 16-bit where applicable so future revisions of this code - can support LBA48 commands */ -public: - /* tweakable parameters */ - double ide_select_delay; /* time between writing 0x1F6 and drive readiness */ - double ide_spinup_delay; /* time it takes to spin the hard disk motor up to speed */ - double ide_spindown_delay; /* time it takes for hard disk motor to spin down */ - double ide_identify_command_delay; -public: - IDEDevice(IDEController *c); - virtual ~IDEDevice(); - virtual void host_reset_begin(); /* IDE controller -> upon setting bit 2 of alt (0x3F6) */ - virtual void host_reset_complete(); /* IDE controller -> upon setting bit 2 of alt (0x3F6) */ - virtual void select(uint8_t ndh,bool switched_to); - virtual void deselect(); - virtual void abort_error(); - virtual void abort_normal(); - virtual void interface_wakeup(); - virtual void writecommand(uint8_t cmd); - virtual Bitu data_read(Bitu iolen); /* read from 1F0h data port from IDE device */ - virtual void data_write(Bitu v,Bitu iolen);/* write to 1F0h data port to IDE device */ - virtual bool command_interruption_ok(uint8_t cmd); - virtual void abort_silent(); -}; - -class IDEATADevice:public IDEDevice { -public: - IDEATADevice(IDEController *c,unsigned char bios_disk_index); - virtual ~IDEATADevice(); - virtual void writecommand(uint8_t cmd); -public: - std::string id_serial; - std::string id_firmware_rev; - std::string id_model; - unsigned char bios_disk_index; - imageDisk *getBIOSdisk(); - void update_from_biosdisk(); - virtual Bitu data_read(Bitu iolen); /* read from 1F0h data port from IDE device */ - virtual void data_write(Bitu v,Bitu iolen);/* write to 1F0h data port to IDE device */ - virtual void generate_identify_device(); - virtual void prepare_read(Bitu offset,Bitu size); - virtual void prepare_write(Bitu offset,Bitu size); - virtual void io_completion(); - virtual bool increment_current_address(Bitu count=1); -public: - Bitu multiple_sector_max,multiple_sector_count; - Bitu heads,sects,cyls,headshr,progress_count; - Bitu phys_heads,phys_sects,phys_cyls; - unsigned char sector[512*128]; - Bitu sector_i,sector_total; - bool geo_translate; -}; - -enum { - LOAD_NO_DISC=0, - LOAD_INSERT_CD, /* user is "inserting" the CD */ - LOAD_IDLE, /* disc is stationary, not spinning */ - LOAD_DISC_LOADING, /* disc is "spinning up" */ - LOAD_DISC_READIED, /* disc just "became ready" */ - LOAD_READY -}; - -class IDEATAPICDROMDevice:public IDEDevice { -public: - IDEATAPICDROMDevice(IDEController *c,unsigned char drive_index); - virtual ~IDEATAPICDROMDevice(); - virtual void writecommand(uint8_t cmd); -public: - std::string id_serial; - std::string id_firmware_rev; - std::string id_model; - unsigned char drive_index; - CDROM_Interface *getMSCDEXDrive(); - void update_from_cdrom(); - virtual Bitu data_read(Bitu iolen); /* read from 1F0h data port from IDE device */ - virtual void data_write(Bitu v,Bitu iolen);/* write to 1F0h data port to IDE device */ - virtual void generate_identify_device(); - virtual void generate_mmc_inquiry(); - virtual void prepare_read(Bitu offset,Bitu size); - virtual void prepare_write(Bitu offset,Bitu size); - virtual void set_sense(unsigned char SK,unsigned char ASC=0,unsigned char ASCQ=0,unsigned int len=0); - virtual bool common_spinup_response(bool trigger,bool wait); - virtual void on_mode_select_io_complete(); - virtual void atapi_io_completion(); - virtual void io_completion(); - virtual void atapi_cmd_completion(); - virtual void on_atapi_busy_time(); - virtual void read_subchannel(); - virtual void play_audio_msf(); - virtual void pause_resume(); - virtual void play_audio10(); - virtual void mode_sense(); - virtual void read_toc(); -public: - bool atapi_to_host; /* if set, PACKET data transfer is to be read by host */ - double spinup_time; - double spindown_timeout; - double cd_insertion_time; - Bitu host_maximum_byte_count; /* host maximum byte count during PACKET transfer */ - std::string id_mmc_vendor_id; - std::string id_mmc_product_id; - std::string id_mmc_product_rev; - Bitu LBA,TransferLength; - int loading_mode; - bool has_changed; -public: - unsigned char sense[256]; - Bitu sense_length; - unsigned char atapi_cmd[12]; - unsigned char atapi_cmd_i,atapi_cmd_total; - unsigned char sector[512*128]; - Bitu sector_i,sector_total; -}; - -class IDEController:public Module_base{ -public: - int IRQ; - bool int13fakeio; /* on certain INT 13h calls, force IDE state as if BIOS had carried them out */ - bool int13fakev86io; /* on certain INT 13h calls in virtual 8086 mode, trigger fake CPU I/O traps */ - bool enable_pio32; /* enable 32-bit PIO (if disabled, attempts at 32-bit PIO are handled as if two 16-bit I/O) */ - bool ignore_pio32; /* if 32-bit PIO enabled, but ignored, writes do nothing, reads return 0xFFFFFFFF */ - bool register_pnp; - unsigned short alt_io; - unsigned short base_io; - unsigned char interface_index; - IO_ReadHandleObject ReadHandler[8],ReadHandlerAlt[2]; - IO_WriteHandleObject WriteHandler[8],WriteHandlerAlt[2]; -public: - IDEDevice* device[2]; /* IDE devices (master, slave) */ - Bitu select,status,drivehead; /* which is selected, status register (0x1F7) but ONLY if no device exists at selection, drive/head register (0x1F6) */ - bool interrupt_enable; /* bit 1 of alt (0x3F6) */ - bool host_reset; /* bit 2 of alt */ - bool irq_pending; - /* defaults for CD-ROM emulation */ - double spinup_time; - double spindown_timeout; - double cd_insertion_time; -public: - IDEController(Section* configuration,unsigned char index); - void register_isapnp(); - void install_io_port(); - void raise_irq(); - void lower_irq(); - ~IDEController(); -}; - -static IDEController* idecontroller[MAX_IDE_CONTROLLERS]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; - -static void IDE_DelayedCommand(Bitu idx/*which IDE controller*/); -static IDEController* GetIDEController(Bitu idx); - -static void IDE_ATAPI_SpinDown(Bitu idx/*which IDE controller*/) { - IDEController *ctrl = GetIDEController(idx); - if (ctrl == NULL) return; - - for (unsigned int i=0;i < 2;i++) { - IDEDevice *dev = ctrl->device[i]; - if (dev == NULL) continue; - - if (dev->type == IDE_TYPE_HDD) { - } - else if (dev->type == IDE_TYPE_CDROM) { - IDEATAPICDROMDevice *atapi = (IDEATAPICDROMDevice*)dev; - - if (atapi->loading_mode == LOAD_DISC_READIED || atapi->loading_mode == LOAD_READY) { - atapi->loading_mode = LOAD_IDLE; - LOG_MSG("ATAPI CD-ROM: spinning down\n"); - } - } - else { - LOG_MSG("Unknown ATAPI spinup callback\n"); - } - } -} - -static void IDE_ATAPI_SpinUpComplete(Bitu idx/*which IDE controller*/); - -static void IDE_ATAPI_CDInsertion(Bitu idx/*which IDE controller*/) { - IDEController *ctrl = GetIDEController(idx); - if (ctrl == NULL) return; - - for (unsigned int i=0;i < 2;i++) { - IDEDevice *dev = ctrl->device[i]; - if (dev == NULL) continue; - - if (dev->type == IDE_TYPE_HDD) { - } - else if (dev->type == IDE_TYPE_CDROM) { - IDEATAPICDROMDevice *atapi = (IDEATAPICDROMDevice*)dev; - - if (atapi->loading_mode == LOAD_INSERT_CD) { - atapi->loading_mode = LOAD_DISC_LOADING; - LOG_MSG("ATAPI CD-ROM: insert CD to loading\n"); - PIC_RemoveSpecificEvents(IDE_ATAPI_SpinDown,idx); - PIC_RemoveSpecificEvents(IDE_ATAPI_CDInsertion,idx); - PIC_AddEvent(IDE_ATAPI_SpinUpComplete,atapi->spinup_time/*ms*/,idx); - } - } - else { - LOG_MSG("Unknown ATAPI spinup callback\n"); - } - } -} - -static void IDE_ATAPI_SpinUpComplete(Bitu idx/*which IDE controller*/) { - IDEController *ctrl = GetIDEController(idx); - if (ctrl == NULL) return; - - for (unsigned int i=0;i < 2;i++) { - IDEDevice *dev = ctrl->device[i]; - if (dev == NULL) continue; - - if (dev->type == IDE_TYPE_HDD) { - } - else if (dev->type == IDE_TYPE_CDROM) { - IDEATAPICDROMDevice *atapi = (IDEATAPICDROMDevice*)dev; - - if (atapi->loading_mode == LOAD_DISC_LOADING) { - atapi->loading_mode = LOAD_DISC_READIED; - LOG_MSG("ATAPI CD-ROM: spinup complete\n"); - PIC_RemoveSpecificEvents(IDE_ATAPI_SpinDown,idx); - PIC_RemoveSpecificEvents(IDE_ATAPI_CDInsertion,idx); - PIC_AddEvent(IDE_ATAPI_SpinDown,atapi->spindown_timeout/*ms*/,idx); - } - } - else { - LOG_MSG("Unknown ATAPI spinup callback\n"); - } - } -} - -/* returns "true" if command should proceed as normal, "false" if sense data was set and command should not proceed. - * this function helps to enforce virtual "spin up" and "ready" delays. */ -bool IDEATAPICDROMDevice::common_spinup_response(bool trigger,bool wait) { - if (loading_mode == LOAD_IDLE) { - if (trigger) { - LOG_MSG("ATAPI CD-ROM: triggered to spin up from idle\n"); - loading_mode = LOAD_DISC_LOADING; - PIC_RemoveSpecificEvents(IDE_ATAPI_SpinDown,controller->interface_index); - PIC_RemoveSpecificEvents(IDE_ATAPI_CDInsertion,controller->interface_index); - PIC_AddEvent(IDE_ATAPI_SpinUpComplete,spinup_time/*ms*/,controller->interface_index); - } - } - else if (loading_mode == LOAD_READY) { - if (trigger) { - PIC_RemoveSpecificEvents(IDE_ATAPI_SpinDown,controller->interface_index); - PIC_RemoveSpecificEvents(IDE_ATAPI_CDInsertion,controller->interface_index); - PIC_AddEvent(IDE_ATAPI_SpinDown,spindown_timeout/*ms*/,controller->interface_index); - } - } - - switch (loading_mode) { - case LOAD_NO_DISC: - case LOAD_INSERT_CD: - set_sense(/*SK=*/0x02,/*ASC=*/0x3A); /* Medium Not Present */ - return false; - case LOAD_DISC_LOADING: - if (has_changed && !wait/*if command will block until LOADING complete*/) { - set_sense(/*SK=*/0x02,/*ASC=*/0x04,/*ASCQ=*/0x01); /* Medium is becoming available */ - return false; - } - break; - case LOAD_DISC_READIED: - loading_mode = LOAD_READY; - if (has_changed) { - if (trigger) has_changed = false; - set_sense(/*SK=*/0x02,/*ASC=*/0x28,/*ASCQ=*/0x00); /* Medium is ready (has changed) */ - return false; - } - break; - case LOAD_IDLE: - case LOAD_READY: - break; - default: - abort(); - }; - - return true; -} - -void IDEATAPICDROMDevice::read_subchannel() { -// unsigned char Format = atapi_cmd[2] & 0xF; -// unsigned char Track = atapi_cmd[6]; - unsigned char paramList = atapi_cmd[3]; - unsigned char attr,track,index; - bool SUBQ = !!(atapi_cmd[2] & 0x40); - bool TIME = !!(atapi_cmd[1] & 2); - unsigned char *write; - unsigned char astat; - bool playing,pause; - TMSF rel,abs; - - CDROM_Interface *cdrom = getMSCDEXDrive(); - if (cdrom == NULL) { - LOG_MSG("WARNING: ATAPI READ TOC unable to get CDROM drive\n"); - prepare_read(0,8); - return; - } - - if (paramList == 0 || paramList > 3) { - LOG_MSG("ATAPI READ SUBCHANNEL unknown param list\n"); - prepare_read(0,8); - return; - } - else if (paramList == 2) { - LOG_MSG("ATAPI READ SUBCHANNEL Media Catalog Number not supported\n"); - prepare_read(0,8); - return; - } - else if (paramList == 3) { - LOG_MSG("ATAPI READ SUBCHANNEL ISRC not supported\n"); - prepare_read(0,8); - return; - } - - /* get current subchannel position */ - if (!cdrom->GetAudioSub(attr,track,index,rel,abs)) { - LOG_MSG("ATAPI READ SUBCHANNEL unable to read current pos\n"); - prepare_read(0,8); - return; - } - - if (!cdrom->GetAudioStatus(playing,pause)) - playing = pause = false; - - if (playing) - astat = pause ? 0x12 : 0x11; - else - astat = 0x13; - - memset(sector,0,8); - write = sector; - *write++ = 0x00; - *write++ = astat;/* AUDIO STATUS */ - *write++ = 0x00;/* SUBCHANNEL DATA LENGTH */ - *write++ = 0x00; - - if (SUBQ) { - *write++ = 0x01; /* subchannel data format code */ - *write++ = (attr >> 4) | 0x10; /* ADR/CONTROL */ - *write++ = track; - *write++ = index; - if (TIME) { - *write++ = 0x00; - *write++ = abs.min; - *write++ = abs.sec; - *write++ = abs.fr; - *write++ = 0x00; - *write++ = rel.min; - *write++ = rel.sec; - *write++ = rel.fr; - } - else { - uint32_t sec; - - sec = (abs.min*60u*75u)+(abs.sec*75u)+abs.fr - 150u; - *write++ = (unsigned char)(sec >> 24u); - *write++ = (unsigned char)(sec >> 16u); - *write++ = (unsigned char)(sec >> 8u); - *write++ = (unsigned char)(sec >> 0u); - - sec = (rel.min*60u*75u)+(rel.sec*75u)+rel.fr - 150u; - *write++ = (unsigned char)(sec >> 24u); - *write++ = (unsigned char)(sec >> 16u); - *write++ = (unsigned char)(sec >> 8u); - *write++ = (unsigned char)(sec >> 0u); - } - } - - { - unsigned int x = (unsigned int)(write-sector) - 4; - sector[2] = x >> 8; - sector[3] = x; - } - - prepare_read(0,MIN((unsigned int)(write-sector),(unsigned int)host_maximum_byte_count)); -#if 0 - LOG_MSG("SUBCH "); - for (size_t i=0;i < sector_total;i++) LOG_MSG("%02x ",sector[i]); - LOG_MSG("\n"); -#endif -} - -void IDEATAPICDROMDevice::mode_sense() { - unsigned char PAGE = atapi_cmd[2] & 0x3F; -// unsigned char SUBPAGE = atapi_cmd[3]; - unsigned char *write; - unsigned int x; - - write = sector; - - /* some header. not well documented */ - *write++ = 0x00; /* ?? */ - *write++ = 0x00; /* length */ - *write++ = 0x00; /* ?? */ - *write++ = 0x00; - *write++ = 0x00; - *write++ = 0x00; - *write++ = 0x00; - *write++ = 0x00; - - *write++ = PAGE; /* page code */ - *write++ = 0x00; /* page length (fill in later) */ - switch (PAGE) { - case 0x01: /* Read error recovery */ - *write++ = 0x00; /* maximum error correction */ - *write++ = 3; /* read retry count */ - *write++ = 0x00; - *write++ = 0x00; - *write++ = 0x00; - *write++ = 0x00; - break; - case 0x0E: /* CD-ROM audio control */ - *write++ = 0x04; /* ?? */ - *write++ = 0x00; /* reserved @+3 */ - *write++ = 0x00; /* reserved @+4 */ - *write++ = 0x00; /* reserved @+5 */ - *write++ = 0x00; - *write++ = 75; /* logical blocks per second */ - - *write++ = 0x01; /* output port 0 selection */ - *write++ = 0xD8; /* output port 0 volume (?) */ - *write++ = 0x02; /* output port 1 selection */ - *write++ = 0xD8; /* output port 1 volume (?) */ - *write++ = 0x00; /* output port 2 selection */ - *write++ = 0x00; /* output port 2 volume (?) */ - *write++ = 0x00; /* output port 3 selection */ - *write++ = 0x00; /* output port 3 volume (?) */ - break; - case 0x2A: /* CD-ROM mechanical status */ - *write++ = 0x00; /* reserved @+2 ?? */ - *write++ = 0x00; /* reserved @+3 ?? */ - *write++ = 0xF1; /* multisession=0 mode2form2=1 mode2form=1 audioplay=1 */ - *write++ = 0xFF; /* ISRC=1 UPC=1 C2=1 RWDeinterleave=1 RWSupported=1 CDDAAccurate=1 CDDASupported=1 */ - *write++ = 0x29; /* loading mechanism type=tray eject=1 prevent jumper=0 lockstate=0 lock=1 */ - *write++ = 0x03; /* separate channel mute=1 separate channel volume levels=1 */ - - x = 176 * 8; /* maximum speed supported: 8X */ - *write++ = x>>8; - *write++ = x; - - x = 256; /* (?) */ - *write++ = x>>8; - *write++ = x; - - x = 6 * 256; /* (?) */ - *write++ = x>>8; - *write++ = x; - - x = 176 * 8; /* current speed supported: 8X */ - *write++ = x>>8; - *write++ = x; - break; - default: - memset(write,0,6); write += 6; - LOG_MSG("WARNING: MODE SENSE on page 0x%02x not supported\n",PAGE); - break; - }; - - /* fill in page length */ - sector[1] = (unsigned int)(write-sector) - 2; - sector[8+1] = (unsigned int)(write-sector) - 2 - 8; - - prepare_read(0,MIN((unsigned int)(write-sector),(unsigned int)host_maximum_byte_count)); -#if 0 - printf("SENSE "); - for (size_t i=0;i < sector_total;i++) printf("%02x ",sector[i]); - printf("\n"); -#endif -} - -void IDEATAPICDROMDevice::pause_resume() { - bool Resume = !!(atapi_cmd[8] & 1); - - CDROM_Interface *cdrom = getMSCDEXDrive(); - if (cdrom == NULL) { - LOG_MSG("WARNING: ATAPI READ TOC unable to get CDROM drive\n"); - sector_total = 0; - return; - } - - cdrom->PauseAudio(Resume); -} - -void IDEATAPICDROMDevice::play_audio_msf() { - uint32_t start_lba,end_lba; - - CDROM_Interface *cdrom = getMSCDEXDrive(); - if (cdrom == NULL) { - LOG_MSG("WARNING: ATAPI READ TOC unable to get CDROM drive\n"); - sector_total = 0; - return; - } - - if (atapi_cmd[3] == 0xFF && atapi_cmd[4] == 0xFF && atapi_cmd[5] == 0xFF) - start_lba = 0xFFFFFFFF; - else { - start_lba = (atapi_cmd[3] * 60u * 75u) + - (atapi_cmd[4] * 75u) + - atapi_cmd[5]; - - if (start_lba >= 150u) start_lba -= 150u; /* LBA sector 0 == M:S:F sector 0:2:0 */ - else end_lba = 0; - } - - if (atapi_cmd[6] == 0xFF && atapi_cmd[7] == 0xFF && atapi_cmd[8] == 0xFF) - end_lba = 0xFFFFFFFF; - else { - end_lba = (atapi_cmd[6] * 60u * 75u) + - (atapi_cmd[7] * 75u) + - atapi_cmd[8]; - - if (end_lba >= 150u) end_lba -= 150u; /* LBA sector 0 == M:S:F sector 0:2:0 */ - else end_lba = 0; - } - - if (start_lba == end_lba) { - /* The play length field specifies the number of contiguous logical blocks that shall - * be played. A play length of zero indicates that no audio operation shall occur. - * This condition is not an error. */ - /* TODO: How do we interpret that? Does that mean audio playback stops? Or does it - * mean we do nothing to the state of audio playback? */ - sector_total = 0; - return; - } - - /* LBA 0xFFFFFFFF means start playing wherever the optics of the CD sit */ - if (start_lba != 0xFFFFFFFF) - cdrom->PlayAudioSector(start_lba,end_lba - start_lba); - else - cdrom->PauseAudio(true); - - sector_total = 0; -} - -void IDEATAPICDROMDevice::play_audio10() { - uint16_t play_length; - uint32_t start_lba; - - CDROM_Interface *cdrom = getMSCDEXDrive(); - if (cdrom == NULL) { - LOG_MSG("WARNING: ATAPI READ TOC unable to get CDROM drive\n"); - sector_total = 0; - return; - } - - start_lba = ((uint32_t)atapi_cmd[2] << 24) + - ((uint32_t)atapi_cmd[3] << 16) + - ((uint32_t)atapi_cmd[4] << 8) + - ((uint32_t)atapi_cmd[5] << 0); - - play_length = ((uint16_t)atapi_cmd[7] << 8) + - ((uint16_t)atapi_cmd[8] << 0); - - if (play_length == 0) { - /* The play length field specifies the number of contiguous logical blocks that shall - * be played. A play length of zero indicates that no audio operation shall occur. - * This condition is not an error. */ - /* TODO: How do we interpret that? Does that mean audio playback stops? Or does it - * mean we do nothing to the state of audio playback? */ - sector_total = 0; - return; - } - - /* LBA 0xFFFFFFFF means start playing wherever the optics of the CD sit */ - if (start_lba != 0xFFFFFFFF) - cdrom->PlayAudioSector(start_lba,play_length); - else - cdrom->PauseAudio(true); - - sector_total = 0; -} - -#if 0 /* TODO move to library */ -static unsigned char dec2bcd(unsigned char c) { - return ((c / 10) << 4) + (c % 10); -} -#endif - -void IDEATAPICDROMDevice::read_toc() { - /* NTS: The SCSI MMC standards say we're allowed to indicate the return data - * is longer than it's allocation length. But here's the thing: some MS-DOS - * CD-ROM drivers will ask for the TOC but only provide enough room for one - * entry (OAKCDROM.SYS) and if we signal more data than it's buffer, it will - * reject our response and render the CD-ROM drive inaccessible. So to make - * this emulation work, we have to cut our response short to the driver's - * allocation length */ - unsigned int AllocationLength = ((unsigned int)atapi_cmd[7] << 8) + atapi_cmd[8]; - unsigned char Format = atapi_cmd[2] & 0xF; - unsigned char Track = atapi_cmd[6]; - bool TIME = !!(atapi_cmd[1] & 2); - unsigned char *write; - int first,last,track; - TMSF leadOut; - - CDROM_Interface *cdrom = getMSCDEXDrive(); - if (cdrom == NULL) { - LOG_MSG("WARNING: ATAPI READ TOC unable to get CDROM drive\n"); - prepare_read(0,8); - return; - } - - memset(sector,0,8); - - if (!cdrom->GetAudioTracks(first,last,leadOut)) { - LOG_MSG("WARNING: ATAPI READ TOC failed to get track info\n"); - prepare_read(0,8); - return; - } - - /* start 2 bytes out. we'll fill in the data length later */ - write = sector + 2; - - if (Format == 1) { /* Read multisession info */ - unsigned char attr; - TMSF start; - - *write++ = (unsigned char)1; /* @+2 first complete session */ - *write++ = (unsigned char)1; /* @+3 last complete session */ - - if (!cdrom->GetAudioTrackInfo(first,start,attr)) { - LOG_MSG("WARNING: ATAPI READ TOC unable to read track %u information\n",first); - attr = 0x41; /* ADR=1 CONTROL=4 */ - start.min = 0; - start.sec = 0; - start.fr = 0; - } - - LOG_MSG("Track %u attr=0x%02x\n",first,attr); - - *write++ = 0x00; /* entry+0 RESERVED */ - *write++ = (attr >> 4) | 0x10; /* entry+1 ADR=1 CONTROL=4 (DATA) */ - *write++ = (unsigned char)first;/* entry+2 TRACK */ - *write++ = 0x00; /* entry+3 RESERVED */ - - /* then, start address of first track in session */ - if (TIME) { - *write++ = 0x00; - *write++ = start.min; - *write++ = start.sec; - *write++ = start.fr; - } - else { - uint32_t sec = (start.min*60u*75u)+(start.sec*75u)+start.fr - 150u; - *write++ = (unsigned char)(sec >> 24u); - *write++ = (unsigned char)(sec >> 16u); - *write++ = (unsigned char)(sec >> 8u); - *write++ = (unsigned char)(sec >> 0u); - } - } - else if (Format == 0) { /* Read table of contents */ - *write++ = (unsigned char)first; /* @+2 */ - *write++ = (unsigned char)last; /* @+3 */ - - for (track=first;track <= last;track++) { - unsigned char attr; - TMSF start; - - if (!cdrom->GetAudioTrackInfo(track,start,attr)) { - LOG_MSG("WARNING: ATAPI READ TOC unable to read track %u information\n",track); - attr = 0x41; /* ADR=1 CONTROL=4 */ - start.min = 0; - start.sec = 0; - start.fr = 0; - } - - if (track < Track) - continue; - if ((write+8) > (sector+AllocationLength)) - break; - - LOG_MSG("Track %u attr=0x%02x\n",track,attr); - - *write++ = 0x00; /* entry+0 RESERVED */ - *write++ = (attr >> 4) | 0x10; /* entry+1 ADR=1 CONTROL=4 (DATA) */ - *write++ = (unsigned char)track;/* entry+2 TRACK */ - *write++ = 0x00; /* entry+3 RESERVED */ - if (TIME) { - *write++ = 0x00; - *write++ = start.min; - *write++ = start.sec; - *write++ = start.fr; - } - else { - uint32_t sec = (start.min*60u*75u)+(start.sec*75u)+start.fr - 150u; - *write++ = (unsigned char)(sec >> 24u); - *write++ = (unsigned char)(sec >> 16u); - *write++ = (unsigned char)(sec >> 8u); - *write++ = (unsigned char)(sec >> 0u); - } - } - - if ((write+8) <= (sector+AllocationLength)) { - *write++ = 0x00; - *write++ = 0x14; - *write++ = 0xAA;/*TRACK*/ - *write++ = 0x00; - if (TIME) { - *write++ = 0x00; - *write++ = leadOut.min; - *write++ = leadOut.sec; - *write++ = leadOut.fr; - } - else { - uint32_t sec = (leadOut.min*60u*75u)+(leadOut.sec*75u)+leadOut.fr - 150u; - *write++ = (unsigned char)(sec >> 24u); - *write++ = (unsigned char)(sec >> 16u); - *write++ = (unsigned char)(sec >> 8u); - *write++ = (unsigned char)(sec >> 0u); - } - } - } - else { - LOG_MSG("WARNING: ATAPI READ TOC Format=%u not supported\n",Format); - prepare_read(0,8); - return; - } - - /* update the TOC data length field */ - { - unsigned int x = (unsigned int)(write-sector) - 2; - sector[0] = x >> 8; - sector[1] = x & 0xFF; - } - - prepare_read(0,MIN(MIN((unsigned int)(write-sector),(unsigned int)host_maximum_byte_count),AllocationLength)); -} - -/* when the ATAPI command has been accepted, and the timeout has passed */ -void IDEATAPICDROMDevice::on_atapi_busy_time() { - /* if the drive is spinning up, then the command waits */ - if (loading_mode == LOAD_DISC_LOADING) { - switch (atapi_cmd[0]) { - case 0x00: /* TEST UNIT READY */ - case 0x03: /* REQUEST SENSE */ - allow_writing = true; - break; /* do not delay */ - default: - PIC_AddEvent(IDE_DelayedCommand,100/*ms*/,controller->interface_index); - return; - } - } - else if (loading_mode == LOAD_DISC_READIED) { - switch (atapi_cmd[0]) { - case 0x00: /* TEST UNIT READY */ - case 0x03: /* REQUEST SENSE */ - allow_writing = true; - break; /* do not delay */ - default: - if (!common_spinup_response(/*spin up*/true,/*wait*/false)) { - count = 0x03; - state = IDE_DEV_READY; - feature = ((sense[2]&0xF) << 4) | (sense[2]&0xF ? 0x04/*abort*/ : 0x00); - status = IDE_STATUS_DRIVE_READY|(sense[2]&0xF ? IDE_STATUS_ERROR:IDE_STATUS_DRIVE_SEEK_COMPLETE); - controller->raise_irq(); - allow_writing = true; - return; - } - break; - } - } - - switch (atapi_cmd[0]) { - case 0x03: /* REQUEST SENSE */ - prepare_read(0,MIN((unsigned int)sense_length,(unsigned int)host_maximum_byte_count)); - memcpy(sector,sense,sense_length); - - feature = 0x00; - state = IDE_DEV_DATA_READ; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ|IDE_STATUS_DRIVE_SEEK_COMPLETE; - - /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ - lba[2] = sector_total >> 8; - lba[1] = sector_total; - - controller->raise_irq(); - allow_writing = true; - break; - case 0x1E: /* PREVENT ALLOW MEDIUM REMOVAL */ - count = 0x03; - feature = 0x00; - sector_total = 0x00; - state = IDE_DEV_DATA_READ; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - - /* Don't care. Do nothing. */ - - /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ - lba[2] = sector_total >> 8; - lba[1] = sector_total; - - controller->raise_irq(); - allow_writing = true; - break; - case 0x25: /* READ CAPACITY */ { - const unsigned int secsize = 2048; - int first,last; - TMSF leadOut; - - CDROM_Interface *cdrom = getMSCDEXDrive(); - - if (!cdrom->GetAudioTracks(first,last,leadOut)) - LOG_MSG("WARNING: ATAPI READ TOC failed to get track info\n"); - - uint32_t sec = (leadOut.min*60u*75u)+(leadOut.sec*75u)+leadOut.fr - 150u; - - prepare_read(0,MIN((unsigned int)8,(unsigned int)host_maximum_byte_count)); - sector[0] = sec >> 24u; - sector[1] = sec >> 16u; - sector[2] = sec >> 8u; - sector[3] = sec & 0xFF; - sector[4] = secsize >> 24u; - sector[5] = secsize >> 16u; - sector[6] = secsize >> 8u; - sector[7] = secsize & 0xFF; -// LOG_MSG("sec=%lu secsize=%lu\n",sec,secsize); - - feature = 0x00; - state = IDE_DEV_DATA_READ; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ|IDE_STATUS_DRIVE_SEEK_COMPLETE; - - /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ - lba[2] = sector_total >> 8; - lba[1] = sector_total; - - controller->raise_irq(); - allow_writing = true; - } break; - case 0x2B: /* SEEK */ - count = 0x03; - feature = 0x00; - sector_total = 0x00; - state = IDE_DEV_DATA_READ; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - - /* Don't care. Do nothing. */ - - /* Except... Windows 95's CD player expects the SEEK command to interrupt CD audio playback. - * In fact it depends on it to the exclusion of commands explicitly standardized to... you know... - * stop or pause playback. Oh Microsoft, you twits... */ - { - CDROM_Interface *cdrom = getMSCDEXDrive(); - if (cdrom) { - bool playing,pause; - - if (!cdrom->GetAudioStatus(playing,pause)) - playing = true; - - if (playing) { - LOG_MSG("ATAPI: Interrupting CD audio playback due to SEEK\n"); - cdrom->StopAudio(); - } - } - } - - /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ - lba[2] = sector_total >> 8; - lba[1] = sector_total; - - controller->raise_irq(); - allow_writing = true; - break; - case 0x12: /* INQUIRY */ - /* NTS: the state of atapi_to_host doesn't seem to matter. */ - generate_mmc_inquiry(); - prepare_read(0,MIN((unsigned int)36,(unsigned int)host_maximum_byte_count)); - - feature = 0x00; - state = IDE_DEV_DATA_READ; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ|IDE_STATUS_DRIVE_SEEK_COMPLETE; - - /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ - lba[2] = sector_total >> 8; - lba[1] = sector_total; - - controller->raise_irq(); - allow_writing = true; - break; - case 0x28: /* READ(10) */ - case 0xA8: /* READ(12) */ - if (TransferLength == 0) { - /* this is legal. the SCSI MMC standards say so. - and apparently, MSCDEX.EXE issues READ(10) commands with transfer length == 0 - to test the drive, so we have to emulate this */ - feature = 0x00; - count = 0x03; /* no more transfer */ - sector_total = 0;/*nothing to transfer */ - state = IDE_DEV_READY; - status = IDE_STATUS_DRIVE_READY; - } - else { - /* OK, try to read */ - CDROM_Interface *cdrom = getMSCDEXDrive(); - bool res = (cdrom != NULL ? cdrom->ReadSectorsHost(/*buffer*/sector,false,LBA,TransferLength) : false); - if (res) { - prepare_read(0,MIN((unsigned int)(TransferLength*2048),(unsigned int)host_maximum_byte_count)); - feature = 0x00; - state = IDE_DEV_DATA_READ; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ|IDE_STATUS_DRIVE_SEEK_COMPLETE; - } - else { - feature = 0xF4; /* abort sense=0xF */ - count = 0x03; /* no more transfer */ - sector_total = 0;/*nothing to transfer */ - state = IDE_DEV_READY; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_ERROR; - LOG_MSG("ATAPI: Failed to read %lu sectors at %lu\n", - (unsigned long)TransferLength,(unsigned long)LBA); - /* TODO: write sense data */ - } - } - - /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ - lba[2] = sector_total >> 8; - lba[1] = sector_total; - - controller->raise_irq(); - allow_writing = true; - break; - case 0x42: /* READ SUB-CHANNEL */ - read_subchannel(); - - feature = 0x00; - state = IDE_DEV_DATA_READ; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ|IDE_STATUS_DRIVE_SEEK_COMPLETE; - - /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ - lba[2] = sector_total >> 8; - lba[1] = sector_total; - - controller->raise_irq(); - allow_writing = true; - break; - case 0x43: /* READ TOC */ - read_toc(); - - feature = 0x00; - state = IDE_DEV_DATA_READ; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ|IDE_STATUS_DRIVE_SEEK_COMPLETE; - - /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ - lba[2] = sector_total >> 8; - lba[1] = sector_total; - - controller->raise_irq(); - allow_writing = true; - break; - case 0x45: /* PLAY AUDIO(10) */ - play_audio10(); - - count = 0x03; - feature = 0x00; - sector_total = 0x00; - state = IDE_DEV_DATA_READ; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - - /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ - lba[2] = sector_total >> 8; - lba[1] = sector_total; - - controller->raise_irq(); - allow_writing = true; - break; - case 0x47: /* PLAY AUDIO MSF */ - play_audio_msf(); - - count = 0x03; - feature = 0x00; - sector_total = 0x00; - state = IDE_DEV_DATA_READ; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - - /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ - lba[2] = sector_total >> 8; - lba[1] = sector_total; - - controller->raise_irq(); - allow_writing = true; - break; - case 0x4B: /* PAUSE/RESUME */ - pause_resume(); - - count = 0x03; - feature = 0x00; - sector_total = 0x00; - state = IDE_DEV_DATA_READ; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - - /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ - lba[2] = sector_total >> 8; - lba[1] = sector_total; - - controller->raise_irq(); - allow_writing = true; - break; - case 0x55: /* MODE SELECT(10) */ - /* we need the data written first, will act in I/O completion routine */ - { - unsigned int x; - - x = (unsigned int)lba[1] + ((unsigned int)lba[2] << 8u); - - /* Windows 95 likes to set 0xFFFF here for whatever reason. - * Negotiate it down to a maximum of 512 for sanity's sake */ - if (x > 512) x = 512; - lba[2] = x >> 8u; - lba[1] = x; - -// LOG_MSG("MODE SELECT expecting %u bytes\n",x); - prepare_write(0,(x+1u)&(~1u)); - } - - feature = 0x00; - state = IDE_DEV_DATA_WRITE; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ|IDE_STATUS_DRIVE_SEEK_COMPLETE; - controller->raise_irq(); - allow_writing = true; - break; - case 0x5A: /* MODE SENSE(10) */ - mode_sense(); - - feature = 0x00; - state = IDE_DEV_DATA_READ; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ|IDE_STATUS_DRIVE_SEEK_COMPLETE; - - /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ - lba[2] = sector_total >> 8; - lba[1] = sector_total; - - controller->raise_irq(); - allow_writing = true; - break; - default: - LOG_MSG("Unknown ATAPI command after busy wait. Why?\n"); - abort_error(); - controller->raise_irq(); - allow_writing = true; - break; - }; - -} - -void IDEATAPICDROMDevice::set_sense(unsigned char SK,unsigned char ASC,unsigned char ASCQ,unsigned int len) { - if (len < 18) len = 18; - memset(sense,0,len); - sense_length = len; - - sense[0] = 0x70; /* RESPONSE CODE */ - sense[2] = SK&0xF; /* SENSE KEY */ - sense[7] = len - 18; /* additional sense length */ - sense[12] = ASC; - sense[13] = ASCQ; -} - -IDEATAPICDROMDevice::IDEATAPICDROMDevice(IDEController *c,unsigned char drive_index) : IDEDevice(c) { - this->drive_index = drive_index; - sector_i = sector_total = 0; - - memset(sense,0,sizeof(sense)); - set_sense(/*SK=*/0); - - /* FIXME: Spinup/down times should be dosbox.conf configurable, if the DOSBox gamers - * care more about loading times than emulation accuracy. */ - cd_insertion_time = 4000; /* a quick user that can switch CDs in 4 seconds */ - if (c->cd_insertion_time > 0) cd_insertion_time = c->cd_insertion_time; - - spinup_time = 1000; /* drive takes 1 second to spin up from idle */ - if (c->spinup_time > 0) spinup_time = c->spinup_time; - - spindown_timeout = 10000; /* drive spins down automatically after 10 seconds */ - if (c->spindown_timeout > 0) spindown_timeout = c->spindown_timeout; - - loading_mode = LOAD_IDLE; - has_changed = false; - - type = IDE_TYPE_CDROM; - id_serial = "123456789"; - id_firmware_rev = "0.74-X"; - id_model = "DOSBox Virtual CD-ROM"; - - /* INQUIRY strings */ - id_mmc_vendor_id = "DOSBox"; - id_mmc_product_id = "Virtual CD-ROM"; - id_mmc_product_rev = "0.74-X"; -} - -IDEATAPICDROMDevice::~IDEATAPICDROMDevice() { -} - -void IDEATAPICDROMDevice::on_mode_select_io_complete() { - unsigned int AllocationLength = ((unsigned int)atapi_cmd[7] << 8) + atapi_cmd[8]; - unsigned char *scan,*fence; - size_t i; - - /* the first 8 bytes are a mode parameter header. - * It's supposed to provide length, density, etc. or whatever the hell - * it means. Windows 95 seems to send all zeros there, so ignore it. - * - * we care about the bytes following it, which contain page_0 mode - * pages */ - - scan = sector + 8; - fence = sector + MIN((unsigned int)sector_total,(unsigned int)AllocationLength); - - while ((scan+2) < fence) { - unsigned char PAGE = *scan++; - unsigned int LEN = (unsigned int)(*scan++); - - if ((scan+LEN) > fence) { - LOG_MSG("ATAPI MODE SELECT warning, page_0 length extends %u bytes past buffer\n",(unsigned int)(scan+LEN-fence)); - break; - } - - LOG_MSG("ATAPI MODE SELECT, PAGE 0x%02x len=%u\n",PAGE,LEN); - LOG_MSG(" "); - for (i=0;i < LEN;i++) LOG_MSG("%02x ",scan[i]); - LOG_MSG("\n"); - - scan += LEN; - } -} - -void IDEATAPICDROMDevice::atapi_io_completion() { - /* for most ATAPI PACKET commands, the transfer is done and we need to clear - all indication of a possible data transfer */ - - if (count == 0x00) { /* the command was expecting data. now it can act on it */ - switch (atapi_cmd[0]) { - case 0x55: /* MODE SELECT(10) */ - on_mode_select_io_complete(); - break; - }; - } - - count = 0x03; /* no more data (command/data=1, input/output=1) */ - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - state = IDE_DEV_READY; - allow_writing = true; - - /* Apparently: real IDE ATAPI controllers fire another IRQ after the transfer. - And there are MS-DOS CD-ROM drivers that assume that. */ - controller->raise_irq(); -} - -void IDEATAPICDROMDevice::io_completion() { - /* lower DRQ */ - status &= ~IDE_STATUS_DRQ; - - /* depending on the command, either continue it or finish up */ - switch (command) { - case 0xA0:/*ATAPI PACKET*/ - atapi_io_completion(); - break; - default: /* most commands: signal drive ready, return to ready state */ - /* NTS: Some MS-DOS CD-ROM drivers will loop endlessly if we never set "drive seek complete" - because they like to hit the device with DEVICE RESET (08h) whether or not it's - a hard disk or CD-ROM drive */ - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - state = IDE_DEV_READY; - allow_writing = true; - break; - } -} - -bool IDEATADevice::increment_current_address(Bitu count) { - if (count == 0) return false; - - if (drivehead_is_lba(drivehead)) { - /* 28-bit LBA: - * drivehead: 27:24 - * lba[2]: 23:16 - * lba[1]: 15:8 - * lba[0]: 7:0 */ - do { - if (((++lba[0])&0xFF) == 0x00) { - lba[0] = 0x00; - if (((++lba[1])&0xFF) == 0x00) { - lba[1] = 0x00; - if (((++lba[2])&0xFF) == 0x00) { - lba[2] = 0x00; - if (((++drivehead)&0xF) == 0) { - drivehead -= 0x10; - return false; - } - } - } - } - } while ((--count) != 0); - } - else { - /* C/H/S increment with rollover */ - do { - /* increment sector */ - if (((++lba[0])&0xFF) == ((sects+1)&0xFF)) { - lba[0] = 1; - /* increment head */ - if (((++drivehead)&0xF) == (heads&0xF)) { - drivehead &= 0xF0; - if (heads == 16) drivehead -= 0x10; - /* increment cylinder */ - if (((++lba[1])&0xFF) == 0x00) { - if (((++lba[2])&0xFF) == 0x00) { - return false; - } - } - } - - } - } while ((--count) != 0); - } - - return true; -} - -void IDEATADevice::io_completion() { - /* lower DRQ */ - status &= ~IDE_STATUS_DRQ; - - /* depending on the command, either continue it or finish up */ - switch (command) { - case 0x20:/* READ SECTOR */ - /* OK, decrement count, increment address */ - /* NTS: Remember that count == 0 means the host wanted to transfer 256 sectors */ - progress_count++; - if ((count&0xFF) == 1) { - /* end of the transfer */ - count = 0; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - state = IDE_DEV_READY; - allow_writing = true; - return; - } - else if ((count&0xFF) == 0) count = 255; - else count--; - - if (!increment_current_address()) { - LOG_MSG("READ advance error\n"); - abort_error(); - return; - } - - /* cause another delay, another sector read */ - state = IDE_DEV_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,0.00001/*ms*/,controller->interface_index); - break; - case 0x30:/* WRITE SECTOR */ - /* this is where the drive has accepted the sector, lowers DRQ, and begins executing the command */ - state = IDE_DEV_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,((progress_count == 0 && !faked_command) ? 0.1 : 0.00001)/*ms*/,controller->interface_index); - break; - case 0xC4:/* READ MULTIPLE */ - /* OK, decrement count, increment address */ - /* NTS: Remember that count == 0 means the host wanted to transfer 256 sectors */ - for (unsigned int cc=0;cc < multiple_sector_count;cc++) { - progress_count++; - if ((count&0xFF) == 1) { - /* end of the transfer */ - count = 0; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - state = IDE_DEV_READY; - allow_writing = true; - return; - } - else if ((count&0xFF) == 0) count = 255; - else count--; - - if (!increment_current_address()) { - LOG_MSG("READ advance error\n"); - abort_error(); - return; - } - } - - /* cause another delay, another sector read */ - state = IDE_DEV_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,0.00001/*ms*/,controller->interface_index); - break; - case 0xC5:/* WRITE MULTIPLE */ - /* this is where the drive has accepted the sector, lowers DRQ, and begins executing the command */ - state = IDE_DEV_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,((progress_count == 0 && !faked_command) ? 0.1 : 0.00001)/*ms*/,controller->interface_index); - break; - default: /* most commands: signal drive ready, return to ready state */ - /* NTS: Some MS-DOS CD-ROM drivers will loop endlessly if we never set "drive seek complete" - because they like to hit the device with DEVICE RESET (08h) whether or not it's - a hard disk or CD-ROM drive */ - count = 0; - drivehead &= 0xF0; - lba[0] = 0; - lba[1] = lba[2] = 0; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - state = IDE_DEV_READY; - allow_writing = true; - break; - } -} - -Bitu IDEATAPICDROMDevice::data_read(Bitu iolen) { - Bitu w = ~0u; - - if (state != IDE_DEV_DATA_READ) - return 0xFFFFUL; - - if (!(status & IDE_STATUS_DRQ)) { - LOG_MSG("IDE: Data read when DRQ=0\n"); - return 0xFFFFUL; - } - - if (sector_i >= sector_total) - return 0xFFFFUL; - - if (iolen >= 4) { - w = host_readd(sector+sector_i); - sector_i += 4; - } - else if (iolen >= 2) { - w = host_readw(sector+sector_i); - sector_i += 2; - } - /* NTS: Some MS-DOS CD-ROM drivers like OAKCDROM.SYS use byte-wide I/O for the initial identification */ - else if (iolen == 1) { - w = sector[sector_i++]; - } - - if (sector_i >= sector_total) - io_completion(); - - return w; -} - -/* TODO: Your code should also be paying attention to the "transfer length" field - in many of the commands here. Right now it doesn't matter. */ -void IDEATAPICDROMDevice::atapi_cmd_completion() { -#if 0 - LOG_MSG("ATAPI command %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x to_host=%u\n", - atapi_cmd[ 0],atapi_cmd[ 1],atapi_cmd[ 2],atapi_cmd[ 3],atapi_cmd[ 4],atapi_cmd[ 5], - atapi_cmd[ 6],atapi_cmd[ 7],atapi_cmd[ 8],atapi_cmd[ 9],atapi_cmd[10],atapi_cmd[11], - atapi_to_host); -#endif - - switch (atapi_cmd[0]) { - case 0x00: /* TEST UNIT READY */ - if (common_spinup_response(/*spin up*/false,/*wait*/false)) - set_sense(0); /* <- nothing wrong */ - - count = 0x03; - state = IDE_DEV_READY; - feature = ((sense[2]&0xF) << 4) | (sense[2]&0xF ? 0x04/*abort*/ : 0x00); - status = IDE_STATUS_DRIVE_READY|(sense[2]&0xF ? IDE_STATUS_ERROR:IDE_STATUS_DRIVE_SEEK_COMPLETE); - controller->raise_irq(); - allow_writing = true; - break; - case 0x03: /* REQUEST SENSE */ - count = 0x02; - state = IDE_DEV_ATAPI_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); - break; - case 0x1E: /* PREVENT ALLOW MEDIUM REMOVAL */ - count = 0x02; - state = IDE_DEV_ATAPI_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); - break; - case 0x25: /* READ CAPACITY */ - count = 0x02; - state = IDE_DEV_ATAPI_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); - break; - case 0x2B: /* SEEK */ - if (common_spinup_response(/*spin up*/true,/*wait*/true)) { - set_sense(0); /* <- nothing wrong */ - count = 0x02; - state = IDE_DEV_ATAPI_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); - } - else { - count = 0x03; - state = IDE_DEV_READY; - feature = ((sense[2]&0xF) << 4) | (sense[2]&0xF ? 0x04/*abort*/ : 0x00); - status = IDE_STATUS_DRIVE_READY|(sense[2]&0xF ? IDE_STATUS_ERROR:IDE_STATUS_DRIVE_SEEK_COMPLETE); - controller->raise_irq(); - allow_writing = true; - } - break; - case 0x12: /* INQUIRY */ - count = 0x02; - state = IDE_DEV_ATAPI_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); - break; - case 0xA8: /* READ(12) */ - if (common_spinup_response(/*spin up*/true,/*wait*/true)) { - set_sense(0); /* <- nothing wrong */ - - /* FIXME: MSCDEX.EXE appears to test the drive by issuing READ(10) with transfer length == 0. - This is all well and good but our response seems to cause a temporary 2-3 second - pause for each attempt. Why? */ - LBA = ((Bitu)atapi_cmd[2] << 24UL) | - ((Bitu)atapi_cmd[3] << 16UL) | - ((Bitu)atapi_cmd[4] << 8UL) | - ((Bitu)atapi_cmd[5] << 0UL); - TransferLength = ((Bitu)atapi_cmd[6] << 24UL) | - ((Bitu)atapi_cmd[7] << 16UL) | - ((Bitu)atapi_cmd[8] << 8UL) | - ((Bitu)atapi_cmd[9]); - - /* FIXME: We actually should NOT be capping the transfer length, but instead should - be breaking the larger transfer into smaller DRQ block transfers like - most IDE ATAPI drives do. Writing the test IDE code taught me that if you - go to most drives and request a transfer length of 0xFFFE the drive will - happily set itself up to transfer that many sectors in one IDE command! */ - /* NTS: In case you're wondering, it's legal to issue READ(10) with transfer length == 0. - MSCDEX.EXE does it when starting up, for example */ - if ((TransferLength*2048) > sizeof(sector)) - TransferLength = sizeof(sector)/2048; - - count = 0x02; - state = IDE_DEV_ATAPI_BUSY; - status = IDE_STATUS_BUSY; - /* TODO: Emulate CD-ROM spin-up delay, and seek delay */ - PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 3)/*ms*/,controller->interface_index); - } - else { - count = 0x03; - state = IDE_DEV_READY; - feature = ((sense[2]&0xF) << 4) | (sense[2]&0xF ? 0x04/*abort*/ : 0x00); - status = IDE_STATUS_DRIVE_READY|(sense[2]&0xF ? IDE_STATUS_ERROR:IDE_STATUS_DRIVE_SEEK_COMPLETE); - controller->raise_irq(); - allow_writing = true; - } - break; - case 0x28: /* READ(10) */ - if (common_spinup_response(/*spin up*/true,/*wait*/true)) { - set_sense(0); /* <- nothing wrong */ - - /* FIXME: MSCDEX.EXE appears to test the drive by issuing READ(10) with transfer length == 0. - This is all well and good but our response seems to cause a temporary 2-3 second - pause for each attempt. Why? */ - LBA = ((Bitu)atapi_cmd[2] << 24UL) | - ((Bitu)atapi_cmd[3] << 16UL) | - ((Bitu)atapi_cmd[4] << 8UL) | - ((Bitu)atapi_cmd[5] << 0UL); - TransferLength = ((Bitu)atapi_cmd[7] << 8) | - ((Bitu)atapi_cmd[8]); - - /* FIXME: We actually should NOT be capping the transfer length, but instead should - be breaking the larger transfer into smaller DRQ block transfers like - most IDE ATAPI drives do. Writing the test IDE code taught me that if you - go to most drives and request a transfer length of 0xFFFE the drive will - happily set itself up to transfer that many sectors in one IDE command! */ - /* NTS: In case you're wondering, it's legal to issue READ(10) with transfer length == 0. - MSCDEX.EXE does it when starting up, for example */ - if ((TransferLength*2048) > sizeof(sector)) - TransferLength = sizeof(sector)/2048; - - count = 0x02; - state = IDE_DEV_ATAPI_BUSY; - status = IDE_STATUS_BUSY; - /* TODO: Emulate CD-ROM spin-up delay, and seek delay */ - PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 3)/*ms*/,controller->interface_index); - } - else { - count = 0x03; - state = IDE_DEV_READY; - feature = ((sense[2]&0xF) << 4) | (sense[2]&0xF ? 0x04/*abort*/ : 0x00); - status = IDE_STATUS_DRIVE_READY|(sense[2]&0xF ? IDE_STATUS_ERROR:IDE_STATUS_DRIVE_SEEK_COMPLETE); - controller->raise_irq(); - allow_writing = true; - } - break; - case 0x42: /* READ SUB-CHANNEL */ - if (common_spinup_response(/*spin up*/true,/*wait*/true)) { - set_sense(0); /* <- nothing wrong */ - - count = 0x02; - state = IDE_DEV_ATAPI_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); - } - else { - count = 0x03; - state = IDE_DEV_READY; - feature = ((sense[2]&0xF) << 4) | (sense[2]&0xF ? 0x04/*abort*/ : 0x00); - status = IDE_STATUS_DRIVE_READY|(sense[2]&0xF ? IDE_STATUS_ERROR:IDE_STATUS_DRIVE_SEEK_COMPLETE); - controller->raise_irq(); - allow_writing = true; - } - break; - case 0x43: /* READ TOC */ - if (common_spinup_response(/*spin up*/true,/*wait*/true)) { - set_sense(0); /* <- nothing wrong */ - - count = 0x02; - state = IDE_DEV_ATAPI_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); - } - else { - count = 0x03; - state = IDE_DEV_READY; - feature = ((sense[2]&0xF) << 4) | (sense[2]&0xF ? 0x04/*abort*/ : 0x00); - status = IDE_STATUS_DRIVE_READY|(sense[2]&0xF ? IDE_STATUS_ERROR:IDE_STATUS_DRIVE_SEEK_COMPLETE); - controller->raise_irq(); - allow_writing = true; - } - break; - case 0x45: /* PLAY AUDIO (1) */ - case 0x47: /* PLAY AUDIO MSF */ - case 0x4B: /* PAUSE/RESUME */ - if (common_spinup_response(/*spin up*/true,/*wait*/true)) { - set_sense(0); /* <- nothing wrong */ - - count = 0x02; - state = IDE_DEV_ATAPI_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); - } - else { - count = 0x03; - state = IDE_DEV_READY; - feature = ((sense[2]&0xF) << 4) | (sense[2]&0xF ? 0x04/*abort*/ : 0x00); - status = IDE_STATUS_DRIVE_READY|(sense[2]&0xF ? IDE_STATUS_ERROR:IDE_STATUS_DRIVE_SEEK_COMPLETE); - controller->raise_irq(); - allow_writing = true; - } - break; - case 0x55: /* MODE SELECT(10) */ - count = 0x00; /* we will be accepting data */ - state = IDE_DEV_ATAPI_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); - break; - case 0x5A: /* MODE SENSE(10) */ - count = 0x02; - state = IDE_DEV_ATAPI_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); - break; - default: - /* we don't know the command, immediately return an error */ - LOG_MSG("Unknown ATAPI command %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", - atapi_cmd[ 0],atapi_cmd[ 1],atapi_cmd[ 2],atapi_cmd[ 3],atapi_cmd[ 4],atapi_cmd[ 5], - atapi_cmd[ 6],atapi_cmd[ 7],atapi_cmd[ 8],atapi_cmd[ 9],atapi_cmd[10],atapi_cmd[11]); - - abort_error(); - count = 0x03; /* no more data (command/data=1, input/output=1) */ - feature = 0xF4; - controller->raise_irq(); - allow_writing = true; - break; - }; -} - -void IDEATAPICDROMDevice::data_write(Bitu v,Bitu iolen) { - if (state == IDE_DEV_ATAPI_PACKET_COMMAND) { - if (atapi_cmd_i < atapi_cmd_total) - atapi_cmd[atapi_cmd_i++] = v; - if (iolen >= 2 && atapi_cmd_i < atapi_cmd_total) - atapi_cmd[atapi_cmd_i++] = v >> 8; - if (iolen >= 4 && atapi_cmd_i < atapi_cmd_total) { - atapi_cmd[atapi_cmd_i++] = v >> 16; - atapi_cmd[atapi_cmd_i++] = v >> 24; - } - - if (atapi_cmd_i >= atapi_cmd_total) - atapi_cmd_completion(); - } - else { - if (state != IDE_DEV_DATA_WRITE) { - LOG_MSG("ide atapi warning: data write when device not in data_write state\n"); - return; - } - if (!(status & IDE_STATUS_DRQ)) { - LOG_MSG("ide atapi warning: data write with drq=0\n"); - return; - } - if ((sector_i+iolen) > sector_total) { - LOG_MSG("ide atapi warning: sector already full %lu / %lu\n",(unsigned long)sector_i,(unsigned long)sector_total); - return; - } - - if (iolen >= 4) { - host_writed(sector+sector_i,v); - sector_i += 4; - } - else if (iolen >= 2) { - host_writew(sector+sector_i,v); - sector_i += 2; - } - else if (iolen == 1) { - sector[sector_i++] = v; - } - - if (sector_i >= sector_total) - io_completion(); - } -} - -Bitu IDEATADevice::data_read(Bitu iolen) { - Bitu w = ~0u; - - if (state != IDE_DEV_DATA_READ) - return 0xFFFFUL; - - if (!(status & IDE_STATUS_DRQ)) { - LOG_MSG("IDE: Data read when DRQ=0\n"); - return 0xFFFFUL; - } - - if ((sector_i+iolen) > sector_total) { - LOG_MSG("ide ata warning: sector already read %lu / %lu\n",(unsigned long)sector_i,(unsigned long)sector_total); - return 0xFFFFUL; - } - - if (iolen >= 4) { - w = host_readd(sector+sector_i); - sector_i += 4; - } - else if (iolen >= 2) { - w = host_readw(sector+sector_i); - sector_i += 2; - } - /* NTS: Some MS-DOS CD-ROM drivers like OAKCDROM.SYS use byte-wide I/O for the initial identification */ - else if (iolen == 1) { - w = sector[sector_i++]; - } - - if (sector_i >= sector_total) - io_completion(); - - return w; -} - -void IDEATADevice::data_write(Bitu v,Bitu iolen) { - if (state != IDE_DEV_DATA_WRITE) { - LOG_MSG("ide ata warning: data write when device not in data_write state\n"); - return; - } - if (!(status & IDE_STATUS_DRQ)) { - LOG_MSG("ide ata warning: data write with drq=0\n"); - return; - } - if ((sector_i+iolen) > sector_total) { - LOG_MSG("ide ata warning: sector already full %lu / %lu\n",(unsigned long)sector_i,(unsigned long)sector_total); - return; - } - - if (iolen >= 4) { - host_writed(sector+sector_i,v); - sector_i += 4; - } - else if (iolen >= 2) { - host_writew(sector+sector_i,v); - sector_i += 2; - } - else if (iolen == 1) { - sector[sector_i++] = v; - } - - if (sector_i >= sector_total) - io_completion(); -} - -void IDEATAPICDROMDevice::prepare_read(Bitu offset,Bitu size) { - /* I/O must be WORD ALIGNED */ - assert((offset&1) == 0); -// assert((size&1) == 0); - - sector_i = offset; - sector_total = size; - assert(sector_i <= sector_total); - assert(sector_total <= sizeof(sector)); -} - -void IDEATAPICDROMDevice::prepare_write(Bitu offset,Bitu size) { - /* I/O must be WORD ALIGNED */ - assert((offset&1) == 0); -// assert((size&1) == 0); - - sector_i = offset; - sector_total = size; - assert(sector_i <= sector_total); - assert(sector_total <= sizeof(sector)); -} - -void IDEATADevice::prepare_write(Bitu offset,Bitu size) { - /* I/O must be WORD ALIGNED */ - assert((offset&1) == 0); -// assert((size&1) == 0); - - sector_i = offset; - sector_total = size; - assert(sector_i <= sector_total); - assert(sector_total <= sizeof(sector)); -} - -void IDEATADevice::prepare_read(Bitu offset,Bitu size) { - /* I/O must be WORD ALIGNED */ - assert((offset&1) == 0); -// assert((size&1) == 0); - - sector_i = offset; - sector_total = size; - assert(sector_i <= sector_total); - assert(sector_total <= sizeof(sector)); -} - -void IDEATAPICDROMDevice::generate_mmc_inquiry() { - Bitu i; - - /* IN RESPONSE TO ATAPI COMMAND 0x12: INQUIRY */ - memset(sector,0,36); - sector[0] = (0 << 5) | 5; /* Peripheral qualifier=0 device type=5 (CDROM) */ - sector[1] = 0x80; /* RMB=1 removable media */ - sector[3] = 0x21; - sector[4] = 36 - 5; /* additional length */ - - for (i=0;i < 8 && i < id_mmc_vendor_id.length();i++) - sector[i+8] = (unsigned char)id_mmc_vendor_id[i]; - for (;i < 8;i++) - sector[i+8] = ' '; - - for (i=0;i < 16 && i < id_mmc_product_id.length();i++) - sector[i+16] = (unsigned char)id_mmc_product_id[i]; - for (;i < 16;i++) - sector[i+16] = ' '; - - for (i=0;i < 4 && i < id_mmc_product_rev.length();i++) - sector[i+32] = (unsigned char)id_mmc_product_rev[i]; - for (;i < 4;i++) - sector[i+32] = ' '; -} - -void IDEATAPICDROMDevice::generate_identify_device() { - unsigned char csum; - Bitu i; - - /* IN RESPONSE TO IDENTIFY DEVICE (0xA1) - GENERATE 512-BYTE REPLY */ - memset(sector,0,512); - - host_writew(sector+(0*2),0x85C0U); /* ATAPI device, command set #5 (what the fuck does that mean?), removable, */ - - for (i=0;i < 20 && i < id_serial.length();i++) - sector[(i^1)+(10*2)] = (unsigned char)id_serial[i]; - for (;i < 20;i++) - sector[(i^1)+(10*2)] = ' '; - - for (i=0;i < 8 && i < id_firmware_rev.length();i++) - sector[(i^1)+(23*2)] = (unsigned char)id_firmware_rev[i]; - for (;i < 8;i++) - sector[(i^1)+(23*2)] = ' '; - - for (i=0;i < 40 && i < id_model.length();i++) - sector[(i^1)+(27*2)] = (unsigned char)id_model[i]; - for (;i < 40;i++) - sector[(i^1)+(27*2)] = ' '; - - host_writew(sector+(49*2), - 0x0800UL|/*IORDY supported*/ - 0x0200UL|/*must be one*/ - 0); - host_writew(sector+(50*2), - 0x4000UL); - host_writew(sector+(51*2), - 0x00F0UL); - host_writew(sector+(52*2), - 0x00F0UL); - host_writew(sector+(53*2), - 0x0006UL); - host_writew(sector+(64*2), /* PIO modes supported */ - 0x0003UL); - host_writew(sector+(67*2), /* PIO cycle time */ - 0x0078UL); - host_writew(sector+(68*2), /* PIO cycle time */ - 0x0078UL); - host_writew(sector+(80*2),0x007E); /* major version number. Here we say we support ATA-1 through ATA-8 */ - host_writew(sector+(81*2),0x0022); /* minor version */ - host_writew(sector+(82*2),0x4008); /* command set: NOP, DEVICE RESET[XXXXX], POWER MANAGEMENT */ - host_writew(sector+(83*2),0x0000); /* command set: LBA48[XXXX] */ - host_writew(sector+(85*2),0x4208); /* commands in 82 enabled */ - host_writew(sector+(86*2),0x0000); /* commands in 83 enabled */ - - /* ATA-8 integrity checksum */ - sector[510] = 0xA5; - csum = 0; for (i=0;i < 511;i++) csum += sector[i]; - sector[511] = 0 - csum; -} - -void IDEATADevice::generate_identify_device() { -// imageDisk *disk = getBIOSdisk(); - unsigned char csum; - uint64_t ptotal; - uint64_t total; - Bitu i; - - /* IN RESPONSE TO IDENTIFY DEVICE (0xEC) - GENERATE 512-BYTE REPLY */ - memset(sector,0,512); - - /* total disk capacity in sectors */ - total = sects * cyls * heads; - ptotal = phys_sects * phys_cyls * phys_heads; - - host_writew(sector+(0*2),0x0040); /* bit 6: 1=fixed disk */ - host_writew(sector+(1*2),phys_cyls); - host_writew(sector+(3*2),phys_heads); - host_writew(sector+(4*2),phys_sects * 512); /* unformatted bytes per track */ - host_writew(sector+(5*2),512); /* unformatted bytes per sector */ - host_writew(sector+(6*2),phys_sects); - - for (i=0;i < 20 && i < id_serial.length();i++) - sector[(i^1)+(10*2)] = (unsigned char)id_serial[i]; - for (;i < 20;i++) - sector[(i^1)+(10*2)] = ' '; - - host_writew(sector+(20*2),1); /* ATA-1: single-ported single sector buffer */ - host_writew(sector+(21*2),4); /* ATA-1: ECC bytes on read/write long */ - - for (i=0;i < 8 && i < id_firmware_rev.length();i++) - sector[(i^1)+(23*2)] = (unsigned char)id_firmware_rev[i]; - for (;i < 8;i++) - sector[(i^1)+(23*2)] = ' '; - - for (i=0;i < 40 && i < id_model.length();i++) - sector[(i^1)+(27*2)] = (unsigned char)id_model[i]; - for (;i < 40;i++) - sector[(i^1)+(27*2)] = ' '; - - if (multiple_sector_max != 0) - host_writew(sector+(47*2),0x80|multiple_sector_max); /* <- READ/WRITE MULTIPLE MAX SECTORS */ - - host_writew(sector+(48*2),0x0000); /* :0 0=we do not support doubleword (32-bit) PIO */ - host_writew(sector+(49*2),0x0A00); /* :13 0=Standby timer values managed by device */ - /* :11 1=IORDY supported */ - /* :10 0=IORDY not disabled */ - /* :9 1=LBA supported */ - /* :8 0=DMA not supported */ - host_writew(sector+(50*2),0x4000); /* FIXME: ??? */ - host_writew(sector+(51*2),0x00F0); /* PIO data transfer cycle timing mode */ - host_writew(sector+(52*2),0x00F0); /* DMA data transfer cycle timing mode */ - host_writew(sector+(53*2),0x0007); /* :2 1=the fields in word 88 are valid */ - /* :1 1=the fields in word (70:64) are valid */ - /* :0 1= ??? */ - host_writew(sector+(54*2),cyls); /* current cylinders */ - host_writew(sector+(55*2),heads); /* current heads */ - host_writew(sector+(56*2),sects); /* current sectors per track */ - host_writed(sector+(57*2),total); /* current capacity in sectors */ - - if (multiple_sector_count != 0) - host_writew(sector+(59*2),0x0100|multiple_sector_count); /* :8 multiple sector setting is valid */ - /* 7:0 current setting for number of log. sectors per DRQ of READ/WRITE MULTIPLE */ - - host_writed(sector+(60*2),ptotal); /* total user addressable sectors (LBA) */ - host_writew(sector+(62*2),0x0000); /* FIXME: ??? */ - host_writew(sector+(63*2),0x0000); /* :10 0=Multiword DMA mode 2 not selected */ - /* TODO: Basically, we don't do DMA. Fill out this comment */ - host_writew(sector+(64*2),0x0003); /* 7:0 PIO modes supported (FIXME ???) */ - host_writew(sector+(65*2),0x0000); /* FIXME: ??? */ - host_writew(sector+(66*2),0x0000); /* FIXME: ??? */ - host_writew(sector+(67*2),0x0078); /* FIXME: ??? */ - host_writew(sector+(68*2),0x0078); /* FIXME: ??? */ - host_writew(sector+(80*2),0x007E); /* major version number. Here we say we support ATA-1 through ATA-8 */ - host_writew(sector+(81*2),0x0022); /* minor version */ - host_writew(sector+(82*2),0x4208); /* command set: NOP, DEVICE RESET[XXXXX], POWER MANAGEMENT */ - host_writew(sector+(83*2),0x4000); /* command set: LBA48[XXXX] */ - host_writew(sector+(84*2),0x4000); /* FIXME: ??? */ - host_writew(sector+(85*2),0x4208); /* commands in 82 enabled */ - host_writew(sector+(86*2),0x4000); /* commands in 83 enabled */ - host_writew(sector+(87*2),0x4000); /* FIXME: ??? */ - host_writew(sector+(88*2),0x0000); /* FIXME: ??? */ - host_writew(sector+(93*3),0x0000); /* FIXME: ??? */ - - /* ATA-8 integrity checksum */ - sector[510] = 0xA5; - csum = 0; for (i=0;i < 511;i++) csum += sector[i]; - sector[511] = 0 - csum; -} - -IDEATADevice::IDEATADevice(IDEController *c,unsigned char bios_disk_index) : IDEDevice(c) { - this->bios_disk_index = bios_disk_index; - sector_i = sector_total = 0; - - headshr = 0; - type = IDE_TYPE_HDD; - id_serial = "8086"; - id_firmware_rev = "8086"; - id_model = "DOSBox IDE disk"; - multiple_sector_max = sizeof(sector) / 512; - multiple_sector_count = 1; - geo_translate = false; -} - -IDEATADevice::~IDEATADevice() { -} - -imageDisk *IDEATADevice::getBIOSdisk() { - if (bios_disk_index >= (2 + MAX_HDD_IMAGES)) return NULL; - return imageDiskList[bios_disk_index]; -} - -CDROM_Interface *IDEATAPICDROMDevice::getMSCDEXDrive() { - CDROM_Interface *cdrom=NULL; - - if (!GetMSCDEXDrive(drive_index,&cdrom)) - return NULL; - - return cdrom; -} - -void IDEATAPICDROMDevice::update_from_cdrom() { - CDROM_Interface *cdrom = getMSCDEXDrive(); - if (cdrom == NULL) { - LOG_MSG("WARNING: IDE update from CD-ROM failed, disk not available\n"); - return; - } -} - -void IDEATADevice::update_from_biosdisk() { - imageDisk *dsk = getBIOSdisk(); - if (dsk == NULL) { - LOG_MSG("WARNING: IDE update from BIOS disk failed, disk not available\n"); - return; - } - - headshr = 0; - geo_translate = false; - cyls = dsk->cylinders; - heads = dsk->heads; - sects = dsk->sectors; - - /* One additional correction: The disk image is probably using BIOS-style geometry - translation (such as C/H/S 1024/64/63) which is impossible given that the IDE - standard only allows up to 16 heads. So we have to translate the geometry. */ - while (heads > 16 && (heads & 1) == 0) { - cyls <<= 1U; - heads >>= 1U; - headshr++; - } - - /* If we can't divide the heads down, then pick a LBA-like mapping that is good enough. - * Note that if what we pick does not evenly map to the INT 13h geometry, and the partition - * contained within is not an LBA type FAT16/FAT32 partition, then Windows 95's IDE driver - * will ignore this device and fall back to using INT 13h. For user convenience we will - * print a warning to reminder the user of exactly that. */ - if (heads > 16) { - unsigned long tmp; - - geo_translate = true; - - tmp = heads * cyls * sects; - sects = 63; - heads = 16; - cyls = (tmp + ((63 * 16) - 1)) / (63 * 16); - LOG_MSG("WARNING: Unable to reduce heads to 16 and below\n"); - LOG_MSG("If at all possible, please consider using INT 13h geometry with a head\n"); - LOG_MSG("cound that is easier to map to the BIOS, like 240 heads or 128 heads/track.\n"); - LOG_MSG("Some OSes, such as Windows 95, will not enable their 32-bit IDE driver if\n"); - LOG_MSG("a clean mapping does not exist between IDE and BIOS geometry.\n"); - LOG_MSG("Mapping BIOS DISK C/H/S %u/%u/%u as IDE %u/%u/%u (non-straightforward mapping)\n", - (unsigned int)dsk->cylinders, - (unsigned int)dsk->heads, - (unsigned int)dsk->sectors, - (unsigned int)cyls, - (unsigned int)heads, - (unsigned int)sects); - } - else { - LOG_MSG("Mapping BIOS DISK C/H/S %u/%u/%u as IDE %u/%u/%u\n", - (unsigned int)dsk->cylinders, - (unsigned int)dsk->heads, - (unsigned int)dsk->sectors, - (unsigned int)cyls, - (unsigned int)heads, - (unsigned int)sects); - } - - phys_heads = heads; - phys_sects = sects; - phys_cyls = cyls; -} - -void IDE_Auto(signed char &index,bool &slave) { - IDEController *c; - unsigned int i; - - index = -1; - slave = false; - for (i=0;i < MAX_IDE_CONTROLLERS;i++) { - if ((c=idecontroller[i]) == NULL) continue; - index = (signed char)i; - - if (c->device[0] == NULL) { - slave = false; - break; - } - if (c->device[1] == NULL) { - slave = true; - break; - } - } -} - -/* drive_index = drive letter 0...A to 25...Z */ -void IDE_ATAPI_MediaChangeNotify(unsigned char drive_index) { - for (unsigned int ide=0;ide < MAX_IDE_CONTROLLERS;ide++) { - IDEController *c = idecontroller[ide]; - if (c == NULL) continue; - for (unsigned int ms=0;ms < 2;ms++) { - IDEDevice *dev = c->device[ms]; - if (dev == NULL) continue; - if (dev->type == IDE_TYPE_CDROM) { - IDEATAPICDROMDevice *atapi = (IDEATAPICDROMDevice*)dev; - if (drive_index == atapi->drive_index) { - LOG_MSG("IDE ATAPI acknowledge media change for drive %c\n",drive_index+'A'); - atapi->has_changed = true; - atapi->loading_mode = LOAD_INSERT_CD; - PIC_RemoveSpecificEvents(IDE_ATAPI_SpinDown,c->interface_index); - PIC_RemoveSpecificEvents(IDE_ATAPI_SpinUpComplete,c->interface_index); - PIC_RemoveSpecificEvents(IDE_ATAPI_CDInsertion,c->interface_index); - PIC_AddEvent(IDE_ATAPI_CDInsertion,atapi->cd_insertion_time/*ms*/,c->interface_index); - } - } - } - } -} - -/* drive_index = drive letter 0...A to 25...Z */ -void IDE_CDROM_Attach(signed char index,bool slave,unsigned char drive_index) { - IDEController *c; - IDEATAPICDROMDevice *dev; - - if (index < 0 || index >= MAX_IDE_CONTROLLERS) return; - c = idecontroller[index]; - if (c == NULL) return; - - if (c->device[slave?1:0] != NULL) { - LOG_MSG("IDE: Controller %u %s already taken\n",index,slave?"slave":"master"); - return; - } - - if (!GetMSCDEXDrive(drive_index,NULL)) { - LOG_MSG("IDE: Asked to attach CD-ROM that does not exist\n"); - return; - } - - dev = new IDEATAPICDROMDevice(c,drive_index); - if (dev == NULL) return; - dev->update_from_cdrom(); - c->device[slave?1:0] = (IDEDevice*)dev; -} - -/* drive_index = drive letter 0...A to 25...Z */ -void IDE_CDROM_Detach(unsigned char drive_index) { - for (int index = 0; index < MAX_IDE_CONTROLLERS; index++) { - IDEController *c = idecontroller[index]; - if (c) - for (int slave = 0; slave < 2; slave++) { - IDEATAPICDROMDevice *dev; - dev = dynamic_cast(c->device[slave]); - if (dev && dev->drive_index == drive_index) { - delete dev; - c->device[slave] = NULL; - } - } - } -} - -/* bios_disk_index = index into BIOS INT 13h disk array: imageDisk *imageDiskList[MAX_DISK_IMAGES]; */ -void IDE_Hard_Disk_Attach(signed char index,bool slave,unsigned char bios_disk_index/*not INT13h, the index into DOSBox's BIOS drive emulation*/) { - IDEController *c; - IDEATADevice *dev; - - if (index < 0 || index >= MAX_IDE_CONTROLLERS) return; - c = idecontroller[index]; - if (c == NULL) return; - - if (c->device[slave?1:0] != NULL) { - LOG_MSG("IDE: Controller %u %s already taken\n",index,slave?"slave":"master"); - return; - } - - if (imageDiskList[bios_disk_index] == NULL) { - LOG_MSG("IDE: Asked to attach bios disk that does not exist\n"); - return; - } - - dev = new IDEATADevice(c,bios_disk_index); - if (dev == NULL) return; - dev->update_from_biosdisk(); - c->device[slave?1:0] = (IDEDevice*)dev; -} - -/* bios_disk_index = index into BIOS INT 13h disk array: imageDisk *imageDiskList[MAX_DISK_IMAGES]; */ -void IDE_Hard_Disk_Detach(unsigned char bios_disk_index) { - for (int index = 0; index < MAX_IDE_CONTROLLERS; index++) { - IDEController *c = idecontroller[index]; - if (c) - for (int slave = 0; slave < 2; slave++) { - IDEATADevice *dev; - dev = dynamic_cast(c->device[slave]); - if (dev && dev->bios_disk_index == bios_disk_index) { - delete dev; - c->device[slave] = NULL; - } - } - } -} -static IDEController* GetIDEController(Bitu idx) { - if (idx >= MAX_IDE_CONTROLLERS) return NULL; - return idecontroller[idx]; -} - -static IDEDevice* GetIDESelectedDevice(IDEController *ide) { - if (ide == NULL) return NULL; - return ide->device[ide->select]; -} - -static bool IDE_CPU_Is_Vm86() { - return (cpu.pmode && ((GETFLAG_IOPLint13fakev86io && IDE_CPU_Is_Vm86()) { - /* Trigger I/O in virtual 8086 mode, where the OS can trap it and act on it. - * Windows 95 uses V86 traps to help "autodetect" what IDE drive and port the - * BIOS uses on INT 13h so that it's internal IDE driver can take over, which - * is the whole reason for this hack. */ - return CPU_ForceV86FakeIO_In(port,len); - } - else { - return ide_baseio_r(port,len); - } -} - -static void IDE_SelfIO_Out(IDEController *ide,Bitu port,Bitu val,Bitu len) { - if (ide->int13fakev86io && IDE_CPU_Is_Vm86()) { - /* Trigger I/O in virtual 8086 mode, where the OS can trap it and act on it. - * Windows 95 uses V86 traps to help "autodetect" what IDE drive and port the - * BIOS uses on INT 13h so that it's internal IDE driver can take over, which - * is the whole reason for this hack. */ - CPU_ForceV86FakeIO_Out(port,val,len); - } - else { - ide_baseio_w(port,val,len); - } -} - -/* INT 13h extensions */ -void IDE_EmuINT13DiskReadByBIOS_LBA(unsigned char disk,uint64_t lba) { - IDEController *ide; - IDEDevice *dev; - Bitu idx,ms; - - if (disk < 0x80) return; - if (lba >= (1ULL << 28ULL)) return; /* this code does not support LBA48 */ - - for (idx=0;idx < MAX_IDE_CONTROLLERS;idx++) { - ide = GetIDEController(idx); - if (ide == NULL) continue; - if (!ide->int13fakeio && !ide->int13fakev86io) continue; - - /* TODO: Print a warning message if the IDE controller is busy (debug/warning message) */ - - /* TODO: Force IDE state to readiness, abort command, etc. */ - - /* for master/slave device... */ - for (ms=0;ms < 2;ms++) { - dev = ide->device[ms]; - if (dev == NULL) continue; - - /* TODO: Print a warning message if the IDE device is busy or in the middle of a command */ - - /* TODO: Forcibly device-reset the IDE device */ - - /* Issue I/O to ourself to select drive */ - dev->faked_command = true; - IDE_SelfIO_In(ide,ide->base_io+7u,1); - IDE_SelfIO_Out(ide,ide->base_io+6u,0x00+(ms<<4u),1); - dev->faked_command = false; - - if (dev->type == IDE_TYPE_HDD) { - IDEATADevice *ata = (IDEATADevice*)dev; - static bool vm86_warned = false; -// static bool int13_fix_wrap_warned = false; - bool vm86 = IDE_CPU_Is_Vm86(); - - if ((ata->bios_disk_index-2) == (disk-0x80)) { -// imageDisk *dsk = ata->getBIOSdisk(); - - if (ide->int13fakev86io && vm86) { - dev->faked_command = true; - - /* we MUST clear interrupts. - * leaving them enabled causes Win95 (or DOSBox?) to recursively - * pagefault and DOSBox to crash. In any case it seems Win95's - * IDE driver assumes the BIOS INT 13h code will do this since - * it's customary for the BIOS to do it at some point, usually - * just before reading the sector data. */ - CPU_CLI(); - - /* We're in virtual 8086 mode and we're asked to fake I/O as if - * executing a BIOS subroutine. Some OS's like Windows 95 rely on - * executing INT 13h in virtual 8086 mode: on startup, the ESDI - * driver traps IDE ports and then executes INT 13h to watch what - * I/O ports it uses. It then uses that information to decide - * what IDE hard disk and controller corresponds to what DOS - * drive. So to get 32-bit disk access to work in Windows 95, - * we have to put on a good show to convince Windows 95 we're - * a legitimate BIOS INT 13h call doing it's job. */ - IDE_SelfIO_In(ide,ide->base_io+7u,1); /* dum de dum reading status */ - IDE_SelfIO_Out(ide,ide->base_io+6u,(ms<<4u)+0xE0u+(lba>>24u),1); /* drive and head */ - IDE_SelfIO_In(ide,ide->base_io+7u,1); /* dum de dum reading status */ - IDE_SelfIO_Out(ide,ide->base_io+2u,0x01,1); /* sector count */ - IDE_SelfIO_Out(ide,ide->base_io+3u,lba&0xFF,1); /* sector number */ - IDE_SelfIO_Out(ide,ide->base_io+4u,(lba>>8u)&0xFF,1); /* cylinder lo */ - IDE_SelfIO_Out(ide,ide->base_io+5u,(lba>>16u)&0xFF,1); /* cylinder hi */ - IDE_SelfIO_Out(ide,ide->base_io+6u,(ms<<4u)+0xE0+(lba>>24u),1); /* drive and head */ - IDE_SelfIO_In(ide,ide->base_io+7u,1); /* dum de dum reading status */ - IDE_SelfIO_Out(ide,ide->base_io+7u,0x20,1); /* issue READ */ - - do { - /* TODO: Timeout needed */ - unsigned int i = IDE_SelfIO_In(ide,ide->alt_io,1); - if ((i&0x80) == 0) break; - } while (1); - IDE_SelfIO_In(ide,ide->base_io+7u,1); - - /* for brevity assume it worked. we're here to bullshit Windows 95 after all */ - for (unsigned int i=0;i < 256;i++) - IDE_SelfIO_In(ide,ide->base_io+0u,2); /* 16-bit IDE data read */ - - /* one more */ - IDE_SelfIO_In(ide,ide->base_io+7u,1); /* dum de dum reading status */ - - /* assume IRQ happened and clear it */ - if (ide->IRQ >= 8) - IDE_SelfIO_Out(ide,0xA0,0x60u+(unsigned int)ide->IRQ-8u,1); /* specific EOI */ - else - IDE_SelfIO_Out(ide,0x20,0x60u+(unsigned int)ide->IRQ,1); /* specific EOI */ - - ata->abort_normal(); - dev->faked_command = false; - } - else { - /* hack IDE state as if a BIOS executing IDE disk routines. - * This is required if we want IDE emulation to work with Windows 3.11 Windows for Workgroups 32-bit disk access (WDCTRL), - * because the driver "tests" the controller by issuing INT 13h calls then reading back IDE registers to see if - * they match the C/H/S it requested */ - dev->feature = 0x00; /* clear error (WDCTRL test phase 5/C/13) */ - dev->count = 0x00; /* clear sector count (WDCTRL test phase 6/D/14) */ - dev->lba[0] = lba&0xFF; /* leave sector number the same (WDCTRL test phase 7/E/15) */ - dev->lba[1] = (lba>>8u)&0xFF; /* leave cylinder the same (WDCTRL test phase 8/F/16) */ - dev->lba[2] = (lba>>16u)&0xFF; /* ...ditto */ - ide->drivehead = dev->drivehead = 0xE0u | (ms<<4u) | (lba>>24u); /* drive head and master/slave (WDCTRL test phase 9/10/17) */ - dev->status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; /* status (WDCTRL test phase A/11/18) */ - dev->allow_writing = true; - - if (vm86 && !vm86_warned) { - LOG_MSG("IDE warning: INT 13h extensions read from virtual 8086 mode.\n"); - LOG_MSG(" If using Windows 95 OSR2, please set int13fakev86io=true for proper 32-bit disk access\n"); - vm86_warned = true; - } - } - - /* break out, we're done */ - idx = MAX_IDE_CONTROLLERS; - break; - } - } - } - } -} - -/* this is called after INT 13h AH=0x02 READ DISK to change IDE state to simulate the BIOS in action. - * this is needed for old "32-bit disk drivers" like WDCTRL in Windows 3.11 Windows for Workgroups, - * which issues INT 13h to read-test and then reads IDE registers to see if they match expectations */ -void IDE_EmuINT13DiskReadByBIOS(unsigned char disk,unsigned int cyl,unsigned int head,unsigned sect) { - IDEController *ide; - IDEDevice *dev; - Bitu idx,ms; - - if (disk < 0x80) return; - - for (idx=0;idx < MAX_IDE_CONTROLLERS;idx++) { - ide = GetIDEController(idx); - if (ide == NULL) continue; - if (!ide->int13fakeio && !ide->int13fakev86io) continue; - - /* TODO: Print a warning message if the IDE controller is busy (debug/warning message) */ - - /* TODO: Force IDE state to readiness, abort command, etc. */ - - /* for master/slave device... */ - for (ms=0;ms < 2;ms++) { - dev = ide->device[ms]; - if (dev == NULL) continue; - - /* TODO: Print a warning message if the IDE device is busy or in the middle of a command */ - - /* TODO: Forcibly device-reset the IDE device */ - - /* Issue I/O to ourself to select drive */ - dev->faked_command = true; - IDE_SelfIO_In(ide,ide->base_io+7u,1); - IDE_SelfIO_Out(ide,ide->base_io+6u,0x00u+(ms<<4u),1); - dev->faked_command = false; - - if (dev->type == IDE_TYPE_HDD) { - IDEATADevice *ata = (IDEATADevice*)dev; - static bool vm86_warned = false; - static bool int13_fix_wrap_warned = false; - bool vm86 = IDE_CPU_Is_Vm86(); - - if ((ata->bios_disk_index-2) == (disk-0x80)) { - imageDisk *dsk = ata->getBIOSdisk(); - - /* print warning if INT 13h is being called after the OS changed logical geometry */ - if (ata->sects != ata->phys_sects || ata->heads != ata->phys_heads || ata->cyls != ata->phys_cyls) - LOG_MSG("INT 13 WARNING: I/O issued on drive attached to IDE emulation with changed logical geometry!\n"); - - /* HACK: src/ints/bios_disk.cpp implementation doesn't correctly - * wrap sector numbers across tracks. it fullfills the read - * by counting sectors and reading from C,H,S+i which means - * that if the OS assumes the ability to read across track - * boundaries (as Windows 95 does) we will get invalid - * sector numbers, which in turn fouls up our emulation. - * - * Windows 95 OSR2 for example, will happily ask for 63 - * sectors starting at C/H/S 30/9/42 without regard for - * track boundaries. */ - if (sect > dsk->sectors) { - if (!int13_fix_wrap_warned) { - LOG_MSG("INT 13h implementation warning: we were given over-large sector number.\n"); - LOG_MSG("This is normally the fault of DOSBox INT 13h emulation that counts sectors\n"); - LOG_MSG("without consideration of track boundaries\n"); - int13_fix_wrap_warned = true; - } - - do { - sect -= dsk->sectors; - if ((++head) >= dsk->heads) { - head -= dsk->heads; - cyl++; - } - } while (sect > dsk->sectors); - } - - /* translate BIOS INT 13h geometry to IDE geometry */ - if (ata->headshr != 0 || ata->geo_translate) { - unsigned long lba; - - if (dsk == NULL) return; - lba = (head * dsk->sectors) + (cyl * dsk->sectors * dsk->heads) + sect - 1; - sect = (lba % ata->sects) + 1; - head = (lba / ata->sects) % ata->heads; - cyl = (lba / ata->sects / ata->heads); - } - - if (ide->int13fakev86io && vm86) { - dev->faked_command = true; - - /* we MUST clear interrupts. - * leaving them enabled causes Win95 (or DOSBox?) to recursively - * pagefault and DOSBox to crash. In any case it seems Win95's - * IDE driver assumes the BIOS INT 13h code will do this since - * it's customary for the BIOS to do it at some point, usually - * just before reading the sector data. */ - CPU_CLI(); - - /* We're in virtual 8086 mode and we're asked to fake I/O as if - * executing a BIOS subroutine. Some OS's like Windows 95 rely on - * executing INT 13h in virtual 8086 mode: on startup, the ESDI - * driver traps IDE ports and then executes INT 13h to watch what - * I/O ports it uses. It then uses that information to decide - * what IDE hard disk and controller corresponds to what DOS - * drive. So to get 32-bit disk access to work in Windows 95, - * we have to put on a good show to convince Windows 95 we're - * a legitimate BIOS INT 13h call doing it's job. */ - IDE_SelfIO_In(ide,ide->base_io+7u,1); /* dum de dum reading status */ - IDE_SelfIO_Out(ide,ide->base_io+6u,(ms<<4u)+0xA0u+head,1); /* drive and head */ - IDE_SelfIO_In(ide,ide->base_io+7u,1); /* dum de dum reading status */ - IDE_SelfIO_Out(ide,ide->base_io+2u,0x01,1); /* sector count */ - IDE_SelfIO_Out(ide,ide->base_io+3u,sect,1); /* sector number */ - IDE_SelfIO_Out(ide,ide->base_io+4u,cyl&0xFF,1); /* cylinder lo */ - IDE_SelfIO_Out(ide,ide->base_io+5u,(cyl>>8u)&0xFF,1); /* cylinder hi */ - IDE_SelfIO_Out(ide,ide->base_io+6u,(ms<<4u)+0xA0u+head,1); /* drive and head */ - IDE_SelfIO_In(ide,ide->base_io+7u,1); /* dum de dum reading status */ - IDE_SelfIO_Out(ide,ide->base_io+7u,0x20u,1); /* issue READ */ - - do { - /* TODO: Timeout needed */ - unsigned int i = IDE_SelfIO_In(ide,ide->alt_io,1); - if ((i&0x80) == 0) break; - } while (1); - IDE_SelfIO_In(ide,ide->base_io+7u,1); - - /* for brevity assume it worked. we're here to bullshit Windows 95 after all */ - for (unsigned int i=0;i < 256;i++) - IDE_SelfIO_In(ide,ide->base_io+0,2); /* 16-bit IDE data read */ - - /* one more */ - IDE_SelfIO_In(ide,ide->base_io+7u,1); /* dum de dum reading status */ - - /* assume IRQ happened and clear it */ - if (ide->IRQ >= 8) - IDE_SelfIO_Out(ide,0xA0,0x60u+(unsigned int)ide->IRQ-8u,1); /* specific EOI */ - else - IDE_SelfIO_Out(ide,0x20,0x60u+(unsigned int)ide->IRQ,1); /* specific EOI */ - - ata->abort_normal(); - dev->faked_command = false; - } - else { - /* hack IDE state as if a BIOS executing IDE disk routines. - * This is required if we want IDE emulation to work with Windows 3.11 Windows for Workgroups 32-bit disk access (WDCTRL), - * because the driver "tests" the controller by issuing INT 13h calls then reading back IDE registers to see if - * they match the C/H/S it requested */ - dev->feature = 0x00; /* clear error (WDCTRL test phase 5/C/13) */ - dev->count = 0x00; /* clear sector count (WDCTRL test phase 6/D/14) */ - dev->lba[0] = sect; /* leave sector number the same (WDCTRL test phase 7/E/15) */ - dev->lba[1] = cyl; /* leave cylinder the same (WDCTRL test phase 8/F/16) */ - dev->lba[2] = cyl >> 8u; /* ...ditto */ - ide->drivehead = dev->drivehead = 0xA0u | (ms<<4u) | head; /* drive head and master/slave (WDCTRL test phase 9/10/17) */ - dev->status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; /* status (WDCTRL test phase A/11/18) */ - dev->allow_writing = true; - - if (vm86 && !vm86_warned) { - LOG_MSG("IDE warning: INT 13h read from virtual 8086 mode.\n"); - LOG_MSG(" If using Windows 95, please set int13fakev86io=true for proper 32-bit disk access\n"); - vm86_warned = true; - } - } - - /* break out, we're done */ - idx = MAX_IDE_CONTROLLERS; - break; - } - } - } - } -} - -/* this is called by src/ints/bios_disk.cpp whenever INT 13h AH=0x00 is called on a hard disk. - * this gives us a chance to update IDE state as if the BIOS had gone through with a full disk reset as requested. */ -void IDE_ResetDiskByBIOS(unsigned char disk) { - IDEController *ide; - IDEDevice *dev; - Bitu idx,ms; - - if (disk < 0x80) return; - - for (idx=0;idx < MAX_IDE_CONTROLLERS;idx++) { - ide = GetIDEController(idx); - if (ide == NULL) continue; - if (!ide->int13fakeio && !ide->int13fakev86io) continue; - - /* TODO: Print a warning message if the IDE controller is busy (debug/warning message) */ - - /* TODO: Force IDE state to readiness, abort command, etc. */ - - /* for master/slave device... */ - for (ms=0;ms < 2;ms++) { - dev = ide->device[ms]; - if (dev == NULL) continue; - - /* TODO: Print a warning message if the IDE device is busy or in the middle of a command */ - - /* TODO: Forcibly device-reset the IDE device */ - - /* Issue I/O to ourself to select drive */ - IDE_SelfIO_In(ide,ide->base_io+7u,1); - IDE_SelfIO_Out(ide,ide->base_io+6u,0x00+(ms<<4u),1); - - /* TODO: Forcibly device-reset the IDE device */ - - if (dev->type == IDE_TYPE_HDD) { - IDEATADevice *ata = (IDEATADevice*)dev; - - if ((ata->bios_disk_index-2) == (disk-0x80)) { - LOG_MSG("IDE %d%c reset by BIOS disk 0x%02x\n", - (unsigned int)(idx+1), - ms?'s':'m', - (unsigned int)disk); - - if (ide->int13fakev86io && IDE_CPU_Is_Vm86()) { - /* issue the DEVICE RESET command */ - IDE_SelfIO_In(ide,ide->base_io+7u,1); - IDE_SelfIO_Out(ide,ide->base_io+7u,0x08,1); - - IDE_SelfIO_In(ide,ide->base_io+7u,1); - - /* assume IRQ happened and clear it */ - if (ide->IRQ >= 8) - IDE_SelfIO_Out(ide,0xA0,0x60u+(unsigned int)ide->IRQ-8u,1); /* specific EOI */ - else - IDE_SelfIO_Out(ide,0x20,0x60u+(unsigned int)ide->IRQ,1); /* specific EOI */ - } - else { - /* Windows 3.1 WDCTRL needs this, or else, it will read the - * status register and see something other than DRIVE_READY|SEEK_COMPLETE */ - dev->writecommand(0x08); - - /* and then immediately clear the IRQ */ - ide->lower_irq(); - } - } - } - } - } -} - -static void IDE_DelayedCommand(Bitu idx/*which IDE controller*/) { - IDEDevice *dev = GetIDESelectedDevice(GetIDEController(idx)); - if (dev == NULL) return; - - if (dev->type == IDE_TYPE_HDD) { - IDEATADevice *ata = (IDEATADevice*)dev; - uint32_t sectorn = 0;/* FIXME: expand to uint64_t when adding LBA48 emulation */ - unsigned int sectcount; - imageDisk *disk; -// int i; - - switch (dev->command) { - case 0x30:/* WRITE SECTOR */ - disk = ata->getBIOSdisk(); - if (disk == NULL) { - LOG_MSG("ATA READ fail, bios disk N/A\n"); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - - sectcount = ata->count & 0xFF; - if (sectcount == 0) sectcount = 256; - if (drivehead_is_lba(ata->drivehead)) { - /* LBA */ - sectorn = ((ata->drivehead & 0xFu) << 24u) | (unsigned int)ata->lba[0] | - ((unsigned int)ata->lba[1] << 8u) | - ((unsigned int)ata->lba[2] << 16u); - } - else { - /* C/H/S */ - if (ata->lba[0] == 0) { - LOG_MSG("ATA sector 0 does not exist\n"); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - else if ((unsigned int)(ata->drivehead & 0xFu) >= (unsigned int)ata->heads || - (unsigned int)ata->lba[0] > (unsigned int)ata->sects || - (unsigned int)(ata->lba[1] | (ata->lba[2] << 8u)) >= (unsigned int)ata->cyls) { - LOG_MSG("C/H/S %u/%u/%u out of bounds %u/%u/%u\n", - (unsigned int)(ata->lba[1] | (ata->lba[2] << 8u)), - (unsigned int)(ata->drivehead&0xFu), - (unsigned int)(ata->lba[0]), - (unsigned int)ata->cyls, - (unsigned int)ata->heads, - (unsigned int)ata->sects); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - - sectorn = ((ata->drivehead & 0xF) * ata->sects) + - (((unsigned int)ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)) * ata->sects * ata->heads) + - ((unsigned int)ata->lba[0] - 1u); - } - - if (disk->Write_AbsoluteSector(sectorn, ata->sector) != 0) { - LOG_MSG("Failed to write sector\n"); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - - /* NTS: the way this command works is that the drive writes ONE sector, then fires the IRQ - and lets the host read it, then reads another sector, fires the IRQ, etc. One - IRQ signal per sector. We emulate that here by adding another event to trigger this - call unless the sector count has just dwindled to zero, then we let it stop. */ - if ((ata->count&0xFF) == 1) { - /* end of the transfer */ - ata->count = 0; - ata->status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - dev->controller->raise_irq(); - ata->state = IDE_DEV_READY; - ata->allow_writing = true; - return; - } - else if ((ata->count&0xFF) == 0) ata->count = 255; - else ata->count--; - ata->progress_count++; - - if (!ata->increment_current_address()) { - LOG_MSG("READ advance error\n"); - ata->abort_error(); - return; - } - - /* begin another sector */ - dev->state = IDE_DEV_DATA_WRITE; - dev->status = IDE_STATUS_DRQ|IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - ata->prepare_write(0,512); - dev->controller->raise_irq(); - break; - - case 0x20:/* READ SECTOR */ - disk = ata->getBIOSdisk(); - if (disk == NULL) { - LOG_MSG("ATA READ fail, bios disk N/A\n"); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - - sectcount = ata->count & 0xFF; - if (sectcount == 0) sectcount = 256; - if (drivehead_is_lba(ata->drivehead)) { - /* LBA */ - sectorn = (((unsigned int)ata->drivehead & 0xFu) << 24u) | (unsigned int)ata->lba[0] | - ((unsigned int)ata->lba[1] << 8u) | - ((unsigned int)ata->lba[2] << 16u); - } - else { - /* C/H/S */ - if (ata->lba[0] == 0) { - LOG_MSG("WARNING C/H/S access mode and sector==0\n"); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - else if ((unsigned int)(ata->drivehead & 0xF) >= (unsigned int)ata->heads || - (unsigned int)ata->lba[0] > (unsigned int)ata->sects || - (unsigned int)(ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)) >= (unsigned int)ata->cyls) { - LOG_MSG("C/H/S %u/%u/%u out of bounds %u/%u/%u\n", - (unsigned int)(ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)), - (unsigned int)(ata->drivehead&0xF), - (unsigned int)ata->lba[0], - (unsigned int)ata->cyls, - (unsigned int)ata->heads, - (unsigned int)ata->sects); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - - sectorn = ((ata->drivehead & 0xFu) * ata->sects) + - (((unsigned int)ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)) * ata->sects * ata->heads) + - ((unsigned int)ata->lba[0] - 1u); - } - - if (disk->Read_AbsoluteSector(sectorn, ata->sector) != 0) { - LOG_MSG("ATA read failed\n"); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - - /* NTS: the way this command works is that the drive reads ONE sector, then fires the IRQ - and lets the host read it, then reads another sector, fires the IRQ, etc. One - IRQ signal per sector. We emulate that here by adding another event to trigger this - call unless the sector count has just dwindled to zero, then we let it stop. */ - /* NTS: The sector advance + count decrement is done in the I/O completion function */ - dev->state = IDE_DEV_DATA_READ; - dev->status = IDE_STATUS_DRQ|IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - ata->prepare_read(0,512); - dev->controller->raise_irq(); - break; - - case 0x40:/* READ SECTOR VERIFY WITH RETRY */ - case 0x41: /* READ SECTOR VERIFY WITHOUT RETRY */ - disk = ata->getBIOSdisk(); - if (disk == NULL) { - LOG_MSG("ATA READ fail, bios disk N/A\n"); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - - sectcount = ata->count & 0xFF; - if (sectcount == 0) sectcount = 256; - if (drivehead_is_lba(ata->drivehead)) { - /* LBA */ - sectorn = (((unsigned int)ata->drivehead & 0xFu) << 24u) | (unsigned int)ata->lba[0] | - ((unsigned int)ata->lba[1] << 8u) | - ((unsigned int)ata->lba[2] << 16u); - } - else { - /* C/H/S */ - if (ata->lba[0] == 0) { - LOG_MSG("WARNING C/H/S access mode and sector==0\n"); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - else if ((unsigned int)(ata->drivehead & 0xF) >= (unsigned int)ata->heads || - (unsigned int)ata->lba[0] > (unsigned int)ata->sects || - (unsigned int)(ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)) >= (unsigned int)ata->cyls) { - LOG_MSG("C/H/S %u/%u/%u out of bounds %u/%u/%u\n", - (unsigned int)(ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)), - (unsigned int)(ata->drivehead&0xFu), - (unsigned int)ata->lba[0], - (unsigned int)ata->cyls, - (unsigned int)ata->heads, - (unsigned int)ata->sects); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - - sectorn = (((unsigned int)ata->drivehead & 0xFu) * ata->sects) + - (((unsigned int)ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)) * ata->sects * ata->heads) + - ((unsigned int)ata->lba[0] - 1u); - } - - if (disk->Read_AbsoluteSector(sectorn, ata->sector) != 0) { - LOG_MSG("ATA read failed\n"); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - - if ((ata->count&0xFF) == 1) { - /* end of the transfer */ - ata->count = 0; - ata->status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - dev->controller->raise_irq(); - ata->state = IDE_DEV_READY; - ata->allow_writing = true; - return; - } - else if ((ata->count&0xFF) == 0) ata->count = 255; - else ata->count--; - ata->progress_count++; - - if (!ata->increment_current_address()) { - LOG_MSG("READ advance error\n"); - ata->abort_error(); - return; - } - - ata->state = IDE_DEV_BUSY; - ata->status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,0.00001/*ms*/,dev->controller->interface_index); - break; - - case 0xC4:/* READ MULTIPLE */ - disk = ata->getBIOSdisk(); - if (disk == NULL) { - LOG_MSG("ATA READ fail, bios disk N/A\n"); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - - sectcount = ata->count & 0xFF; - if (sectcount == 0) sectcount = 256; - if (drivehead_is_lba(ata->drivehead)) { - /* LBA */ - sectorn = (((unsigned int)ata->drivehead & 0xFu) << 24u) | (unsigned int)ata->lba[0] | - ((unsigned int)ata->lba[1] << 8u) | - ((unsigned int)ata->lba[2] << 16u); - } - else { - /* C/H/S */ - if (ata->lba[0] == 0) { - LOG_MSG("WARNING C/H/S access mode and sector==0\n"); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - else if ((unsigned int)(ata->drivehead & 0xF) >= (unsigned int)ata->heads || - (unsigned int)ata->lba[0] > (unsigned int)ata->sects || - (unsigned int)(ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)) >= (unsigned int)ata->cyls) { - LOG_MSG("C/H/S %u/%u/%u out of bounds %u/%u/%u\n", - (unsigned int)(ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)), - (unsigned int)(ata->drivehead&0xF), - (unsigned int)ata->lba[0], - (unsigned int)ata->cyls, - (unsigned int)ata->heads, - (unsigned int)ata->sects); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - - sectorn = ((ata->drivehead & 0xF) * ata->sects) + - (((unsigned int)ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)) * ata->sects * ata->heads) + - ((unsigned int)ata->lba[0] - 1); - } - - if ((512*ata->multiple_sector_count) > sizeof(ata->sector)) - E_Exit("SECTOR OVERFLOW"); - - for (unsigned int cc=0;cc < MIN((Bitu)ata->multiple_sector_count,(Bitu)sectcount);cc++) { - /* it would be great if the disk object had a "read multiple sectors" member function */ - if (disk->Read_AbsoluteSector(sectorn+cc, ata->sector+(cc*512)) != 0) { - LOG_MSG("ATA read failed\n"); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - } - - /* NTS: the way this command works is that the drive reads ONE sector, then fires the IRQ - and lets the host read it, then reads another sector, fires the IRQ, etc. One - IRQ signal per sector. We emulate that here by adding another event to trigger this - call unless the sector count has just dwindled to zero, then we let it stop. */ - /* NTS: The sector advance + count decrement is done in the I/O completion function */ - dev->state = IDE_DEV_DATA_READ; - dev->status = IDE_STATUS_DRQ|IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - ata->prepare_read(0,512*MIN((Bitu)ata->multiple_sector_count,(Bitu)sectcount)); - dev->controller->raise_irq(); - break; - - case 0xC5:/* WRITE MULTIPLE */ - disk = ata->getBIOSdisk(); - if (disk == NULL) { - LOG_MSG("ATA READ fail, bios disk N/A\n"); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - - sectcount = ata->count & 0xFF; - if (sectcount == 0) sectcount = 256; - if (drivehead_is_lba(ata->drivehead)) { - /* LBA */ - sectorn = (((unsigned int)ata->drivehead & 0xF) << 24) | (unsigned int)ata->lba[0] | - ((unsigned int)ata->lba[1] << 8) | - ((unsigned int)ata->lba[2] << 16); - } - else { - /* C/H/S */ - if (ata->lba[0] == 0) { - LOG_MSG("ATA sector 0 does not exist\n"); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - else if ((unsigned int)(ata->drivehead & 0xF) >= (unsigned int)ata->heads || - (unsigned int)ata->lba[0] > (unsigned int)ata->sects || - (unsigned int)(ata->lba[1] | ((unsigned int)ata->lba[2] << 8)) >= (unsigned int)ata->cyls) { - LOG_MSG("C/H/S %u/%u/%u out of bounds %u/%u/%u\n", - (unsigned int)(ata->lba[1] | ((unsigned int)ata->lba[2] << 8)), - (unsigned int)(ata->drivehead&0xF), - (unsigned int)ata->lba[0], - (unsigned int)ata->cyls, - (unsigned int)ata->heads, - (unsigned int)ata->sects); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - - sectorn = ((unsigned int)(ata->drivehead & 0xF) * ata->sects) + - (((unsigned int)ata->lba[1] | ((unsigned int)ata->lba[2] << 8)) * ata->sects * ata->heads) + - ((unsigned int)ata->lba[0] - 1); - } - - for (unsigned int cc=0;cc < MIN((Bitu)ata->multiple_sector_count,(Bitu)sectcount);cc++) { - /* it would be great if the disk object had a "write multiple sectors" member function */ - if (disk->Write_AbsoluteSector(sectorn+cc, ata->sector+(cc*512)) != 0) { - LOG_MSG("Failed to write sector\n"); - ata->abort_error(); - dev->controller->raise_irq(); - return; - } - } - - for (unsigned int cc=0;cc < MIN((Bitu)ata->multiple_sector_count,(Bitu)sectcount);cc++) { - if ((ata->count&0xFF) == 1) { - /* end of the transfer */ - ata->count = 0; - ata->status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - dev->controller->raise_irq(); - ata->state = IDE_DEV_READY; - ata->allow_writing = true; - return; - } - else if ((ata->count&0xFF) == 0) ata->count = 255; - else ata->count--; - ata->progress_count++; - - if (!ata->increment_current_address()) { - LOG_MSG("READ advance error\n"); - ata->abort_error(); - return; - } - } - - /* begin another sector */ - sectcount = ata->count & 0xFF; - if (sectcount == 0) sectcount = 256; - dev->state = IDE_DEV_DATA_WRITE; - dev->status = IDE_STATUS_DRQ|IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - ata->prepare_write(0,512*MIN((Bitu)ata->multiple_sector_count,(Bitu)sectcount)); - dev->controller->raise_irq(); - break; - - case 0xEC:/*IDENTIFY DEVICE (CONTINUED) */ - dev->state = IDE_DEV_DATA_READ; - dev->status = IDE_STATUS_DRQ|IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - ata->generate_identify_device(); - ata->prepare_read(0,512); - dev->count = 0x01; - dev->lba[0] = 0x00; - dev->feature = 0x00; - dev->lba[1] = 0x00; - dev->lba[2] = 0x00; - dev->controller->raise_irq(); - break; - default: - LOG_MSG("Unknown delayed IDE/ATA command\n"); - dev->abort_error(); - dev->controller->raise_irq(); - break; - } - } - else if (dev->type == IDE_TYPE_CDROM) { - IDEATAPICDROMDevice *atapi = (IDEATAPICDROMDevice*)dev; - - if (dev->state == IDE_DEV_ATAPI_BUSY) { - switch (dev->command) { - case 0xA0:/*ATAPI PACKET*/ - atapi->on_atapi_busy_time(); - break; - default: - LOG_MSG("Unknown delayed IDE/ATAPI busy wait command\n"); - dev->abort_error(); - dev->controller->raise_irq(); - break; - } - } - else { - switch (dev->command) { - case 0xA0:/*ATAPI PACKET*/ - dev->state = IDE_DEV_ATAPI_PACKET_COMMAND; - dev->status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE|IDE_STATUS_DRQ; - dev->count = 0x01; /* input/output == 0, command/data == 1 */ - atapi->atapi_cmd_total = 12; /* NTS: do NOT raise IRQ */ - atapi->atapi_cmd_i = 0; - break; - case 0xA1:/*IDENTIFY PACKET DEVICE (CONTINUED) */ - dev->state = IDE_DEV_DATA_READ; - dev->status = IDE_STATUS_DRQ|IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - atapi->generate_identify_device(); - atapi->prepare_read(0,512); - dev->controller->raise_irq(); - break; - default: - LOG_MSG("Unknown delayed IDE/ATAPI command\n"); - dev->abort_error(); - dev->controller->raise_irq(); - break; - } - } - } - else { - LOG_MSG("Unknown delayed command\n"); - dev->abort_error(); - dev->controller->raise_irq(); - } -} - -void IDEController::raise_irq() { - irq_pending = true; - if (IRQ >= 0 && interrupt_enable) PIC_ActivateIRQ((unsigned int)IRQ); -} - -void IDEController::lower_irq() { - irq_pending = false; - if (IRQ >= 0) PIC_DeActivateIRQ((unsigned int)IRQ); -} - -IDEController *match_ide_controller(Bitu port) { - unsigned int i; - - for (i=0;i < MAX_IDE_CONTROLLERS;i++) { - IDEController *ide = idecontroller[i]; - if (ide == NULL) continue; - if (ide->base_io != 0U && ide->base_io == (port&0xFFF8U)) return ide; - if (ide->alt_io != 0U && ide->alt_io == (port&0xFFFEU)) return ide; - } - - return NULL; -} - -Bitu IDEDevice::data_read(Bitu iolen) { - (void)iolen;//UNUSED - return 0xAAAAU; -} - -void IDEDevice::data_write(Bitu v,Bitu iolen) { - (void)iolen;//UNUSED - (void)v;//UNUSED -} - -IDEDevice::IDEDevice(IDEController *c) { - type = IDE_TYPE_NONE; - status = 0x00; - controller = c; - asleep = false; - motor_on = true; - allow_writing = true; - state = IDE_DEV_READY; - feature = count = lba[0] = lba[1] = lba[2] = command = drivehead = 0; - - faked_command = false; - ide_select_delay = 0.5; /* 500us */ - ide_spinup_delay = 3000; /* 3 seconds */ - ide_spindown_delay = 1000; /* 1 second */ - ide_identify_command_delay = 0.01; /* 10us */ -} - -/* IDE controller -> upon writing bit 2 of alt (0x3F6) */ -void IDEDevice::host_reset_complete() { - status = 0x00; - asleep = false; - allow_writing = true; - state = IDE_DEV_READY; -} - -void IDEDevice::host_reset_begin() { - status = 0xFF; - asleep = false; - allow_writing = true; - state = IDE_DEV_BUSY; -} - -IDEDevice::~IDEDevice() { -} - -void IDEDevice::abort_silent() { - assert(controller != NULL); - - /* a command was written while another is in progress */ - state = IDE_DEV_READY; - allow_writing = true; - command = 0x00; - status = IDE_STATUS_ERROR | IDE_STATUS_DRIVE_READY | IDE_STATUS_DRIVE_SEEK_COMPLETE; -} - -void IDEDevice::abort_error() { - assert(controller != NULL); - LOG_MSG("IDE abort dh=0x%02x with error on 0x%03x\n",drivehead,controller->base_io); - - /* a command was written while another is in progress */ - state = IDE_DEV_READY; - allow_writing = true; - command = 0x00; - status = IDE_STATUS_ERROR | IDE_STATUS_DRIVE_READY | IDE_STATUS_DRIVE_SEEK_COMPLETE; -} - -void IDEDevice::abort_normal() { - /* a command was written while another is in progress */ - state = IDE_DEV_READY; - allow_writing = true; - command = 0x00; - status = IDE_STATUS_DRIVE_READY | IDE_STATUS_DRIVE_SEEK_COMPLETE; -} - -void IDEDevice::interface_wakeup() { - if (asleep) { - asleep = false; - } -} - -bool IDEDevice::command_interruption_ok(uint8_t cmd) { - /* apparently this is OK, if the Linux kernel is doing it: - * writing the same command byte as the one in progress, OR, issuing - * Device Reset while another command is waiting for data read/write */ - if (cmd == command) return true; - if (state != IDE_DEV_READY && state != IDE_DEV_BUSY && cmd == 0x08) { - LOG_MSG("Device reset while another (%02x) is in progress (state=%u). Aborting current command to begin another\n",command,state); - abort_silent(); - return true; - } - - if (state != IDE_DEV_READY) { - LOG_MSG("Command %02x written while another (%02x) is in progress (state=%u). Aborting current command\n",cmd,command,state); - abort_error(); - return false; - } - - return true; -} - -void IDEDevice::writecommand(uint8_t cmd) { - if (!command_interruption_ok(cmd)) - return; - - /* if the drive is asleep, then writing a command wakes it up */ - interface_wakeup(); - - /* drive is ready to accept command */ - switch (cmd) { - default: - LOG_MSG("Unknown IDE command %02X\n",cmd); - abort_error(); - break; - } -} - -void IDEATAPICDROMDevice::writecommand(uint8_t cmd) { - if (!command_interruption_ok(cmd)) - return; - - /* if the drive is asleep, then writing a command wakes it up */ - interface_wakeup(); - - /* drive is ready to accept command */ - allow_writing = false; - command = cmd; - switch (cmd) { - case 0x08: /* DEVICE RESET */ - status = 0x00; - drivehead &= 0x10; controller->drivehead = drivehead; - count = 0x01; - lba[0] = 0x01; - feature = 0x01; - lba[1] = 0x14; /* <- magic ATAPI identification */ - lba[2] = 0xEB; - /* NTS: Testing suggests that ATAPI devices do NOT trigger an IRQ on receipt of this command */ - allow_writing = true; - break; - case 0x20: /* READ SECTOR */ - abort_normal(); - status = IDE_STATUS_ERROR|IDE_STATUS_DRIVE_READY; - drivehead &= 0x30; controller->drivehead = drivehead; - count = 0x01; - lba[0] = 0x01; - feature = 0x04; /* abort */ - lba[1] = 0x14; /* <- magic ATAPI identification */ - lba[2] = 0xEB; - controller->raise_irq(); - allow_writing = true; - break; - case 0xA0: /* ATAPI PACKET */ - if (feature & 1) { - /* this code does not support DMA packet commands */ - LOG_MSG("Attempted DMA transfer\n"); - abort_error(); - count = 0x03; /* no more data (command/data=1, input/output=1) */ - feature = 0xF4; - controller->raise_irq(); - } - else { - state = IDE_DEV_BUSY; - status = IDE_STATUS_BUSY; - atapi_to_host = (feature >> 2) & 1; /* 0=to device 1=to host */ - host_maximum_byte_count = ((unsigned int)lba[2] << 8) + (unsigned int)lba[1]; /* LBA field bits 23:8 are byte count */ - if (host_maximum_byte_count == 0) host_maximum_byte_count = 0x10000UL; - PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 0.25)/*ms*/,controller->interface_index); - } - break; - case 0xA1: /* IDENTIFY PACKET DEVICE */ - state = IDE_DEV_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : ide_identify_command_delay),controller->interface_index); - break; - case 0xEC: /* IDENTIFY DEVICE */ - /* "devices that implement the PACKET command set shall post command aborted and place PACKET command feature - set in the appropriate fields". We have to do this. Unlike OAKCDROM.SYS Windows 95 appears to autodetect - IDE devices by what they do when they're sent command 0xEC out of the blue---Microsoft didn't write their - IDE drivers to use command 0x08 DEVICE RESET. */ - abort_normal(); - status = IDE_STATUS_ERROR|IDE_STATUS_DRIVE_READY; - drivehead &= 0x30; controller->drivehead = drivehead; - count = 0x01; - lba[0] = 0x01; - feature = 0x04; /* abort */ - lba[1] = 0x14; /* <- magic ATAPI identification */ - lba[2] = 0xEB; - controller->raise_irq(); - allow_writing = true; - break; - default: - LOG_MSG("Unknown IDE/ATAPI command %02X\n",cmd); - abort_error(); - allow_writing = true; - count = 0x03; /* no more data (command/data=1, input/output=1) */ - feature = 0xF4; - controller->raise_irq(); - break; - } -} - -void IDEATADevice::writecommand(uint8_t cmd) { - if (!command_interruption_ok(cmd)) - return; - - if (!faked_command) { - if (drivehead_is_lba(drivehead)) { - uint64_t n; - - n = ((unsigned int)(drivehead&0xF)<<24)+((unsigned int)lba[2]<<16)+((unsigned int)lba[1]<<8)+(unsigned int)lba[0]; - LOG_MSG("IDE ATA command %02x dh=0x%02x count=0x%02x lba=%07llx/%07llx\n",cmd, - drivehead,count,(unsigned long long)n, - (unsigned long long)(phys_sects * phys_cyls * phys_heads)); - } - else { - LOG_MSG("IDE ATA command %02x dh=0x%02x count=0x%02x chs=%02x/%02x/%02x\n",cmd, - drivehead,count,((unsigned int)lba[2]<<8)+(unsigned int)lba[1],(unsigned int)(drivehead&0xF),(unsigned int)lba[0]); - } - - LOG(LOG_SB,LOG_NORMAL)("IDE ATA command %02x",cmd); - } - - /* if the drive is asleep, then writing a command wakes it up */ - interface_wakeup(); - - /* FIXME: OAKCDROM.SYS is sending the hard disk command 0xA0 (ATAPI packet) for some reason. Why? */ - - /* drive is ready to accept command */ - allow_writing = false; - command = cmd; - switch (cmd) { - case 0x00: /* NOP */ - feature = 0x04; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_ERROR; - controller->raise_irq(); - allow_writing = true; - break; - case 0x08: /* DEVICE RESET */ - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - drivehead &= 0x10; controller->drivehead = drivehead; - count = 0x01; lba[0] = 0x01; feature = 0x00; - lba[1] = lba[2] = 0; - /* NTS: Testing suggests that ATA hard drives DO fire an IRQ at this stage. - In fact, Windows 95 won't detect hard drives that don't fire an IRQ in desponse */ - controller->raise_irq(); - allow_writing = true; - break; - case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: /* RECALIBRATE (1xh) */ - case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: case 0x1F: - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - /* "if the command is executed in CHS mode, then ... sector number register shall be 1. - * if executed in LAB mode, then ... sector number register shall be 0" */ - if (drivehead_is_lba(drivehead)) lba[0] = 0x00; - else lba[0] = 0x01; - drivehead &= 0x10; controller->drivehead = drivehead; - lba[1] = lba[2] = 0; - feature = 0x00; - controller->raise_irq(); - allow_writing = true; - break; - case 0x20: /* READ SECTOR */ - progress_count = 0; - state = IDE_DEV_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 0.1)/*ms*/,controller->interface_index); - break; - case 0x30: /* WRITE SECTOR */ - /* the drive does NOT signal an interrupt. it sets DRQ and waits for a sector - * to be transferred to it before executing the command */ - progress_count = 0; - state = IDE_DEV_DATA_WRITE; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ; - prepare_write(0,512); - break; - case 0x40: /* READ SECTOR VERIFY WITH RETRY */ - case 0x41: /* READ SECTOR VERIFY WITHOUT RETRY */ - progress_count = 0; - state = IDE_DEV_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 0.1)/*ms*/,controller->interface_index); - break; - case 0x91: /* INITIALIZE DEVICE PARAMETERS */ - if ((unsigned int)count != (unsigned int)sects || (unsigned int)((drivehead&0xF)+1) != (unsigned int)heads) { - if (count == 0) { - LOG_MSG("IDE warning: OS attempted to change geometry to invalid H/S %u/%u\n", - count,(drivehead&0xF)+1); - abort_error(); - allow_writing = true; - return; - } - else { - unsigned int ncyls; - - ncyls = (phys_cyls * phys_heads * phys_sects); - ncyls += (count * ((unsigned int)(drivehead&0xF)+1u)) - 1u; - ncyls /= count * ((unsigned int)(drivehead&0xF)+1u); - - /* the OS is changing logical disk geometry, so update our head/sector count (needed for Windows ME) */ - LOG_MSG("IDE warning: OS is changing logical geometry from C/H/S %u/%u/%u to logical H/S %u/%u/%u\n", - (int)cyls,(int)heads,(int)sects, - (int)ncyls,(int)((drivehead&0xF)+1),(int)count); - LOG_MSG(" Compatibility issues may occur if the OS tries to use INT 13 at the same time!\n"); - - cyls = ncyls; - sects = count; - heads = (drivehead&0xFu)+1u; - } - } - - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - allow_writing = true; - break; - case 0xC4: /* READ MULTIPLE */ - progress_count = 0; - state = IDE_DEV_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 0.1)/*ms*/,controller->interface_index); - break; - case 0xC5: /* WRITE MULTIPLE */ - /* the drive does NOT signal an interrupt. it sets DRQ and waits for a sector - * to be transferred to it before executing the command */ - progress_count = 0; - state = IDE_DEV_DATA_WRITE; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ; - prepare_write(0UL,512UL*MIN((unsigned long)multiple_sector_count,(unsigned long)(count == 0 ? 256 : count))); - break; - case 0xC6: /* SET MULTIPLE MODE */ - /* only sector counts 1, 2, 4, 8, 16, 32, 64, and 128 are legal by standard. - * NTS: There's a bug in VirtualBox that makes 0 legal too! */ - if (count != 0 && count <= multiple_sector_max && is_power_of_2(count)) { - multiple_sector_count = count; - status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; - } - else { - feature = 0x04; /* abort error */ - abort_error(); - } - controller->raise_irq(); - allow_writing = true; - break; - case 0xA0:/*ATAPI PACKET*/ - /* We're not an ATAPI packet device! - * Windows 95 seems to issue this at startup to hard drives. Duh. */ - /* fall through */ - case 0xA1: /* IDENTIFY PACKET DEVICE */ - /* We are not an ATAPI packet device. - * Most MS-DOS drivers and Windows 95 like to issue both IDENTIFY ATA and IDENTIFY ATAPI commands. - * I also gather from some contributers on the github comments that people think our "Unknown IDE/ATA command" - * error message is part of some other error in the emulation. Rather than put up with that, we'll just - * silently abort the command with an error. */ - abort_normal(); - status = IDE_STATUS_ERROR|IDE_STATUS_DRIVE_READY; - drivehead &= 0x30; controller->drivehead = drivehead; - count = 0x01; - lba[0] = 0x01; - feature = 0x04; /* abort */ - lba[1] = 0x00; - lba[2] = 0x00; - controller->raise_irq(); - allow_writing = true; - break; - case 0xEC: /* IDENTIFY DEVICE */ - state = IDE_DEV_BUSY; - status = IDE_STATUS_BUSY; - PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : ide_identify_command_delay),controller->interface_index); - break; - default: - LOG_MSG("Unknown IDE/ATA command %02X\n",cmd); - abort_error(); - allow_writing = true; - controller->raise_irq(); - break; - } -} - -void IDEDevice::deselect() { -} - -/* the hard disk or CD-ROM class override of this member is responsible for checking - the head value and clamping within range if C/H/S mode is selected */ -void IDEDevice::select(uint8_t ndh,bool switched_to) { - (void)switched_to;//UNUSED - (void)ndh;//UNUSED - /* NTS: I thought there was some delay between selecting a drive and sending a command. - Apparently I was wrong. */ - if (allow_writing) drivehead = ndh; -// status = (!asleep)?(IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE):0; -// allow_writing = !asleep; -// state = IDE_DEV_READY; -} - -IDEController::IDEController(Section* configuration,unsigned char index):Module_base(configuration){ - Section_prop * section=static_cast(configuration); - int i; - - register_pnp = section->Get_bool("pnp"); - int13fakeio = section->Get_bool("int13fakeio"); - int13fakev86io = section->Get_bool("int13fakev86io"); - enable_pio32 = section->Get_bool("enable pio32"); - ignore_pio32 = section->Get_bool("ignore pio32"); - spinup_time = section->Get_int("cd-rom spinup time"); - spindown_timeout = section->Get_int("cd-rom spindown timeout"); - cd_insertion_time = section->Get_int("cd-rom insertion delay"); - - status = 0x00; - host_reset = false; - irq_pending = false; - interrupt_enable = true; - interface_index = index; - device[0] = NULL; - device[1] = NULL; - base_io = 0; - select = 0; - alt_io = 0; - IRQ = -1; - - i = section->Get_int("irq"); - if (i > 0 && i <= 15) IRQ = i; - - i = section->Get_hex("io"); - if (i >= 0x100 && i <= 0x3FF) base_io = (unsigned int)(i & ~7); - - i = section->Get_hex("altio"); - if (i >= 0x100 && i <= 0x3FF) alt_io = (unsigned int)(i & ~1); - - if (index < sizeof(IDE_default_IRQs)) { - if (IRQ < 0) IRQ = IDE_default_IRQs[index]; - if (alt_io == 0) alt_io = IDE_default_alts[index]; - if (base_io == 0) base_io = IDE_default_bases[index]; - } - else { - if (IRQ < 0 || alt_io == 0 || base_io == 0) - LOG_MSG("WARNING: IDE interface %u: Insufficient resources assigned by dosbox.conf, and no appropriate default resources for this interface.",index); - } -} - -void IDEController::register_isapnp() { - if (register_pnp && base_io > 0 && alt_io > 0) { - unsigned char tmp[256]; - unsigned int i; - - const unsigned char h1[9] = { - ISAPNP_SYSDEV_HEADER( - ISAPNP_ID('P','N','P',0x0,0x6,0x0,0x0), /* PNP0600 Generic ESDI/IDE/ATA compatible hard disk controller */ - ISAPNP_TYPE(0x01,0x01,0x00), /* type: Mass Storage Device / IDE / Generic */ - 0x0001 | 0x0002) - }; - - i = 0; - memcpy(tmp+i,h1,9); i += 9; /* can't disable, can't configure */ - /*----------allocated--------*/ - tmp[i+0] = (8 << 3) | 7; /* IO resource */ - tmp[i+1] = 0x01; /* 16-bit decode */ - host_writew(tmp+i+2,base_io); /* min */ - host_writew(tmp+i+4,base_io); /* max */ - tmp[i+6] = 0x08; /* align */ - tmp[i+7] = 0x08; /* length */ - i += 7+1; - - tmp[i+0] = (8 << 3) | 7; /* IO resource */ - tmp[i+1] = 0x01; /* 16-bit decode */ - host_writew(tmp+i+2,alt_io); /* min */ - host_writew(tmp+i+4,alt_io); /* max */ - tmp[i+6] = 0x01; /* align */ - if (alt_io == 0x3F6 && fdc_takes_port_3F7()) - tmp[i+7] = 0x01; /* length 1 (so as not to conflict with floppy controller at 0x3F7) */ - else - tmp[i+7] = 0x02; /* length 2 */ - i += 7+1; - - if (IRQ > 0) { - tmp[i+0] = (4 << 3) | 3; /* IRQ resource */ - host_writew(tmp+i+1,1 << IRQ); - tmp[i+3] = 0x09; /* HTE=1 LTL=1 */ - i += 3+1; - } - - tmp[i+0] = 0x79; /* END TAG */ - tmp[i+1] = 0x00; - i += 2; - /*-------------possible-----------*/ - tmp[i+0] = 0x79; /* END TAG */ - tmp[i+1] = 0x00; - i += 2; - /*-------------compatible---------*/ - tmp[i+0] = 0x79; /* END TAG */ - tmp[i+1] = 0x00; - i += 2; - - if (!ISAPNP_RegisterSysDev(tmp,i)) - LOG_MSG("ISAPNP register failed\n"); - } -} - -void IDEController::install_io_port(){ - unsigned int i; - - if (base_io != 0) { - for (i=0;i < 8;i++) { - WriteHandler[i].Install(base_io+i,ide_baseio_w,IO_MA); - ReadHandler[i].Install(base_io+i,ide_baseio_r,IO_MA); - } - } - - if (alt_io != 0) { - WriteHandlerAlt[0].Install(alt_io,ide_altio_w,IO_MA); - ReadHandlerAlt[0].Install(alt_io,ide_altio_r,IO_MA); - - /* the floppy controller might take port 0x3F7. - * don't claim it if so */ - if (alt_io == 0x3F6 && fdc_takes_port_3F7()) { - LOG_MSG("IDE: Not registering port 3F7h, FDC will occupy it.\n"); - } - else { - WriteHandlerAlt[1].Install(alt_io+1u,ide_altio_w,IO_MA); - ReadHandlerAlt[1].Install(alt_io+1u,ide_altio_r,IO_MA); - } - } -} - -IDEController::~IDEController() { - unsigned int i; - - for (i=0;i < 2;i++) { - if (device[i] != NULL) { - delete device[i]; - device[i] = NULL; - } - } -} - -static void ide_altio_w(Bitu port,Bitu val,Bitu iolen) { - IDEController *ide = match_ide_controller(port); - if (ide == NULL) { - LOG_MSG("WARNING: port read from I/O port not registered to IDE, yet callback triggered\n"); - return; - } - - if (!ide->enable_pio32 && iolen == 4) { - ide_altio_w(port,val&0xFFFF,2); - ide_altio_w(port+2u,val>>16u,2); - return; - } - else if (ide->ignore_pio32 && iolen == 4) - return; - - port &= 1; - if (port == 0) {/*3F6*/ - ide->interrupt_enable = (val&2u)?0:1; - if (ide->interrupt_enable) { - if (ide->irq_pending) ide->raise_irq(); - } - else { - if (ide->IRQ >= 0) PIC_DeActivateIRQ((unsigned int)ide->IRQ); - } - - if ((val&4) && !ide->host_reset) { - if (ide->device[0]) ide->device[0]->host_reset_begin(); - if (ide->device[1]) ide->device[1]->host_reset_begin(); - ide->host_reset=1; - } - else if (!(val&4) && ide->host_reset) { - if (ide->device[0]) ide->device[0]->host_reset_complete(); - if (ide->device[1]) ide->device[1]->host_reset_complete(); - ide->host_reset=0; - } - } -} - -static Bitu ide_altio_r(Bitu port,Bitu iolen) { - IDEController *ide = match_ide_controller(port); - IDEDevice *dev; - - if (ide == NULL) { - LOG_MSG("WARNING: port read from I/O port not registered to IDE, yet callback triggered\n"); - return ~(0UL); - } - - if (!ide->enable_pio32 && iolen == 4) - return ide_altio_r(port,2) + (ide_altio_r(port+2u,2) << 16u); - else if (ide->ignore_pio32 && iolen == 4) - return ~0ul; - - dev = ide->device[ide->select]; - port &= 1; - if (port == 0)/*3F6(R) status, does NOT clear interrupt*/ - return (dev != NULL) ? dev->status : ide->status; - else /*3F7(R) Drive Address Register*/ - return 0x80u|(ide->select==0?0u:1u)|(ide->select==1?0u:2u)| - ((dev != NULL) ? (((dev->drivehead&0xFu)^0xFu) << 2u) : 0x3Cu); - - return ~(0UL); -} - -static Bitu ide_baseio_r(Bitu port,Bitu iolen) { - IDEController *ide = match_ide_controller(port); - IDEDevice *dev; - Bitu ret = ~0ul; - - if (ide == NULL) { - LOG_MSG("WARNING: port read from I/O port not registered to IDE, yet callback triggered\n"); - return ~(0UL); - } - - if (!ide->enable_pio32 && iolen == 4) - return ide_baseio_r(port,2) + (ide_baseio_r(port+2,2) << 16); - else if (ide->ignore_pio32 && iolen == 4) - return ~0ul; - - dev = ide->device[ide->select]; - port &= 7; - switch (port) { - case 0: /* 1F0 */ - ret = (dev != NULL) ? dev->data_read(iolen) : 0xFFFFFFFFUL; - break; - case 1: /* 1F1 */ - ret = (dev != NULL) ? dev->feature : 0x00; - break; - case 2: /* 1F2 */ - ret = (dev != NULL) ? dev->count : 0x00; - break; - case 3: /* 1F3 */ - ret = (dev != NULL) ? dev->lba[0] : 0x00; - break; - case 4: /* 1F4 */ - ret = (dev != NULL) ? dev->lba[1] : 0x00; - break; - case 5: /* 1F5 */ - ret = (dev != NULL) ? dev->lba[2] : 0x00; - break; - case 6: /* 1F6 */ - ret = ide->drivehead; - break; - case 7: /* 1F7 */ - /* if an IDE device exists at selection return it's status, else return our status */ - if (dev && dev->status & IDE_STATUS_BUSY) { - } - else if (dev == NULL && ide->status & IDE_STATUS_BUSY) { - } - else { - ide->lower_irq(); - } - - ret = (dev != NULL) ? dev->status : ide->status; - break; - } - - return ret; -} - -static void ide_baseio_w(Bitu port,Bitu val,Bitu iolen) { - IDEController *ide = match_ide_controller(port); - IDEDevice *dev; - - if (ide == NULL) { - LOG_MSG("WARNING: port read from I/O port not registered to IDE, yet callback triggered\n"); - return; - } - - if (!ide->enable_pio32 && iolen == 4) { - ide_baseio_w(port,val&0xFFFF,2); - ide_baseio_w(port+2,val>>16,2); - return; - } - else if (ide->ignore_pio32 && iolen == 4) - return; - - dev = ide->device[ide->select]; - port &= 7; - - /* ignore I/O writes if the controller is busy */ - if (dev) { - if (dev->status & IDE_STATUS_BUSY) { - if (port == 6 && ((val>>4)&1) == ide->select) { - /* some MS-DOS drivers like ATAPICD.SYS are just very pedantic about writing to port +6 to ensure the right drive is selected */ - return; - } - else { - LOG_MSG("W-%03X %02X BUSY DROP [DEV]\n",(int)(port+ide->base_io),(int)val); - return; - } - } - } - else if (ide->status & IDE_STATUS_BUSY) { - if (port == 6 && ((val>>4)&1) == ide->select) { - /* some MS-DOS drivers like ATAPICD.SYS are just very pedantic about writing to port +6 to ensure the right drive is selected */ - return; - } - else { - LOG_MSG("W-%03X %02X BUSY DROP [IDE]\n",(int)(port+ide->base_io),(int)val); - return; - } - } - -#if 0 - if (ide == idecontroller[1]) - LOG_MSG("IDE: baseio write port %u val %02x\n",(unsigned int)port,(unsigned int)val); -#endif - - if (port >= 1 && port <= 5 && dev && !dev->allow_writing) { - LOG_MSG("IDE WARNING: Write to port %u val %02x when device not ready to accept writing\n", - (unsigned int)port,(unsigned int)val); - } - - switch (port) { - case 0: /* 1F0 */ - if (dev) dev->data_write(val,iolen); /* <- TODO: what about 32-bit PIO modes? */ - break; - case 1: /* 1F1 */ - if (dev && dev->allow_writing) /* TODO: LBA48 16-bit wide register */ - dev->feature = val; - break; - case 2: /* 1F2 */ - if (dev && dev->allow_writing) /* TODO: LBA48 16-bit wide register */ - dev->count = val; - break; - case 3: /* 1F3 */ - if (dev && dev->allow_writing) /* TODO: LBA48 16-bit wide register */ - dev->lba[0] = val; - break; - case 4: /* 1F4 */ - if (dev && dev->allow_writing) /* TODO: LBA48 16-bit wide register */ - dev->lba[1] = val; - break; - case 5: /* 1F5 */ - if (dev && dev->allow_writing) /* TODO: LBA48 16-bit wide register */ - dev->lba[2] = val; - break; - case 6: /* 1F6 */ - if (((val>>4)&1) != ide->select) { - ide->lower_irq(); - /* update select pointer if bit 4 changes. - also emulate IDE busy state when changing drives */ - if (dev) dev->deselect(); - ide->select = (val>>4)&1; - dev = ide->device[ide->select]; - if (dev) dev->select(val,1); - else ide->status = 0; /* NTS: if there is no drive there you're supposed to not have anything set */ - } - else if (dev) { - dev->select(val,0); - } - else { - ide->status = 0; /* NTS: if there is no drive there you're supposed to not have anything set */ - } - - ide->drivehead = val; - break; - case 7: /* 1F7 */ - if (dev) dev->writecommand(val); - break; - } -} - -static void IDE_Destroy(Section* sec) { - (void)sec;//UNUSED - for (unsigned int i=0;i < MAX_IDE_CONTROLLERS;i++) { - if (idecontroller[i] != NULL) { - delete idecontroller[i]; - idecontroller[i] = NULL; - } - } - - init_ide = 0; -} - -static void IDE_Init(Section* sec,unsigned char ide_interface) { - Section_prop *section=static_cast(sec); - IDEController *ide; - - assert(ide_interface < MAX_IDE_CONTROLLERS); - - if (!section->Get_bool("enable")) - return; - - if (!init_ide) { - AddExitFunction(AddExitFunctionFuncPair(IDE_Destroy)); - init_ide = 1; - } - - LOG(LOG_MISC,LOG_DEBUG)("Initializing IDE controller %u",ide_interface); - - if (idecontroller[ide_interface] != NULL) { - delete idecontroller[ide_interface]; - idecontroller[ide_interface] = NULL; - } - - ide = idecontroller[ide_interface] = new IDEController(sec,ide_interface); - ide->install_io_port(); - - PIC_SetIRQMask((unsigned int)ide->IRQ,false); -} - -void IDE_Primary_Init(Section *sec) { - IDE_Init(sec,0); -} - -void IDE_Secondary_Init(Section *sec) { - IDE_Init(sec,1); -} - -void IDE_Tertiary_Init(Section *sec) { - IDE_Init(sec,2); -} - -void IDE_Quaternary_Init(Section *sec) { - IDE_Init(sec,3); -} - -void IDE_Quinternary_Init(Section *sec) { - IDE_Init(sec,4); -} - -void IDE_Sexternary_Init(Section *sec) { - IDE_Init(sec,5); -} - -void IDE_Septernary_Init(Section *sec) { - IDE_Init(sec,6); -} - -void IDE_Octernary_Init(Section *sec) { - IDE_Init(sec,7); -} - -const char *ide_names[MAX_IDE_CONTROLLERS] = { - "ide, primary", - "ide, secondary", - "ide, tertiary", - "ide, quaternary", - "ide, quinternary", - "ide, sexternary", - "ide, septernary", - "ide, octernary" -}; -void (*ide_inits[MAX_IDE_CONTROLLERS])(Section *) = { - &IDE_Primary_Init, - &IDE_Secondary_Init, - &IDE_Tertiary_Init, - &IDE_Quaternary_Init, - &IDE_Quinternary_Init, - &IDE_Sexternary_Init, - &IDE_Septernary_Init, - &IDE_Octernary_Init -}; - -void IDE_OnReset(Section *sec) { - (void)sec;//UNUSED - if (IS_PC98_ARCH) return; - - for (size_t i=0;i < MAX_IDE_CONTROLLERS;i++) ide_inits[i](control->GetSection(ide_names[i])); -} - -void IDE_Init() { - LOG(LOG_MISC,LOG_DEBUG)("Initializing IDE controllers"); - - AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(IDE_OnReset)); -} - -void BIOS_Post_register_IDE() { - for (size_t i=0;i < MAX_IDE_CONTROLLERS;i++) { - if (idecontroller[i] != NULL) - idecontroller[i]->register_isapnp(); - } -} - +/* + * IDE ATA/ATAPI and controller emulation for DOSBox-X + * (C) 2012 Jonathan Campbell + + * [insert open source license here] + */ + +/* $Id: ide.cpp,v 1.49 2009-04-10 09:53:04 c2woody Exp $ */ + +#include +#include +#include "dosbox.h" +#include "inout.h" +#include "pic.h" +#include "mem.h" +#include "cpu.h" +#include "ide.h" +#include "mixer.h" +#include "timer.h" +#include "setup.h" +#include "control.h" +#include "callback.h" +#include "bios_disk.h" +#include "../src/dos/cdrom.h" + +#if defined(_MSC_VER) +# pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */ +# pragma warning(disable:4305) /* truncation from double to float */ +#endif + +#ifdef _MSC_VER +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#else +# define MIN(a,b) std::min(a,b) +#endif + +#if defined(_MSC_VER) +# pragma warning(disable:4065) /* switch statement no case labels */ +#endif + +static unsigned char init_ide = 0; + +static const unsigned char IDE_default_IRQs[4] = { + 14, /* primary */ + 15, /* secondary */ + 11, /* tertiary */ + 10 /* quaternary */ +}; + +static const unsigned short IDE_default_bases[4] = { + 0x1F0, /* primary */ + 0x170, /* secondary */ + 0x1E8, /* tertiary */ + 0x168 /* quaternary */ +}; + +static const unsigned short IDE_default_alts[4] = { + 0x3F6, /* primary */ + 0x376, /* secondary */ + 0x3EE, /* tertiary */ + 0x36E /* quaternary */ +}; + +bool fdc_takes_port_3F7(); + +static void ide_altio_w(Bitu port,Bitu val,Bitu iolen); +static Bitu ide_altio_r(Bitu port,Bitu iolen); +static void ide_baseio_w(Bitu port,Bitu val,Bitu iolen); +static Bitu ide_baseio_r(Bitu port,Bitu iolen); +bool GetMSCDEXDrive(unsigned char drive_letter,CDROM_Interface **_cdrom); + +enum IDEDeviceType { + IDE_TYPE_NONE, + IDE_TYPE_HDD=1, + IDE_TYPE_CDROM +}; + +enum IDEDeviceState { + IDE_DEV_READY=0, + IDE_DEV_SELECT_WAIT, + IDE_DEV_CONFUSED, + IDE_DEV_BUSY, + IDE_DEV_DATA_READ, + IDE_DEV_DATA_WRITE, + IDE_DEV_ATAPI_PACKET_COMMAND, + IDE_DEV_ATAPI_BUSY +}; + +enum { + IDE_STATUS_BUSY=0x80, + IDE_STATUS_DRIVE_READY=0x40, + IDE_STATUS_DRIVE_SEEK_COMPLETE=0x10, + IDE_STATUS_DRQ=0x08, + IDE_STATUS_ERROR=0x01 +}; + +class IDEController; + +#if 0//unused +static inline bool drivehead_is_lba48(uint8_t val) { + return (val&0xE0) == 0x40; +} +#endif + +static inline bool drivehead_is_lba(uint8_t val) { + return (val&0xE0) == 0xE0; +} + +#if 0//unused +static inline bool drivehead_is_chs(uint8_t val) { + return (val&0xE0) == 0xA0; +} +#endif + +class IDEDevice { +public: + IDEController *controller; + uint16_t feature,count,lba[3]; /* feature = BASE+1 count = BASE+2 lba[3] = BASE+3,+4,+5 */ + uint8_t command,drivehead,status; /* command/status = BASE+7 drivehead = BASE+6 */ + enum IDEDeviceType type; + bool faked_command; /* if set, DOSBox is sending commands to itself */ + bool allow_writing; + bool motor_on; + bool asleep; + IDEDeviceState state; + /* feature: 0x1F1 (Word 00h in ATA specs) + count: 0x1F2 (Word 01h in ATA specs) + lba[3]: 0x1F3 (Word 02h) 0x1F4 (Word 03h) and 0x1F5 (Word 04h) + drivehead: 0x1F6 (copy of last value written) + command: 0x1F7 (Word 05h) + status: 0x1F7 (value read back to IDE controller, including busy and drive ready bits as well as error status) + + In C/H/S modes lba[3] becomes lba[0]=sector lba[1]=cylinder-low lba[2]=cylinder-high and + the code must read the 4-bit head number from drivehead[bits 3:0]. + + "drivehead" in this struct is always maintained as a device copy of the controller's + drivehead value. it is only updated on write, and not returned on read. + + "allow_writing" if set allows the DOS program/OS to write the registers. It is + clear during command execution, obviously, so the state of the device is not confused + while executing the command. + + Registers are 16-bit where applicable so future revisions of this code + can support LBA48 commands */ +public: + /* tweakable parameters */ + double ide_select_delay; /* time between writing 0x1F6 and drive readiness */ + double ide_spinup_delay; /* time it takes to spin the hard disk motor up to speed */ + double ide_spindown_delay; /* time it takes for hard disk motor to spin down */ + double ide_identify_command_delay; +public: + IDEDevice(IDEController *c); + virtual ~IDEDevice(); + virtual void host_reset_begin(); /* IDE controller -> upon setting bit 2 of alt (0x3F6) */ + virtual void host_reset_complete(); /* IDE controller -> upon setting bit 2 of alt (0x3F6) */ + virtual void select(uint8_t ndh,bool switched_to); + virtual void deselect(); + virtual void abort_error(); + virtual void abort_normal(); + virtual void interface_wakeup(); + virtual void writecommand(uint8_t cmd); + virtual Bitu data_read(Bitu iolen); /* read from 1F0h data port from IDE device */ + virtual void data_write(Bitu v,Bitu iolen);/* write to 1F0h data port to IDE device */ + virtual bool command_interruption_ok(uint8_t cmd); + virtual void abort_silent(); +}; + +class IDEATADevice:public IDEDevice { +public: + IDEATADevice(IDEController *c,unsigned char bios_disk_index); + virtual ~IDEATADevice(); + virtual void writecommand(uint8_t cmd); +public: + std::string id_serial; + std::string id_firmware_rev; + std::string id_model; + unsigned char bios_disk_index; + imageDisk *getBIOSdisk(); + void update_from_biosdisk(); + virtual Bitu data_read(Bitu iolen); /* read from 1F0h data port from IDE device */ + virtual void data_write(Bitu v,Bitu iolen);/* write to 1F0h data port to IDE device */ + virtual void generate_identify_device(); + virtual void prepare_read(Bitu offset,Bitu size); + virtual void prepare_write(Bitu offset,Bitu size); + virtual void io_completion(); + virtual bool increment_current_address(Bitu count=1); +public: + Bitu multiple_sector_max,multiple_sector_count; + Bitu heads,sects,cyls,headshr,progress_count; + Bitu phys_heads,phys_sects,phys_cyls; + unsigned char sector[512*128]; + Bitu sector_i,sector_total; + bool geo_translate; +}; + +enum { + LOAD_NO_DISC=0, + LOAD_INSERT_CD, /* user is "inserting" the CD */ + LOAD_IDLE, /* disc is stationary, not spinning */ + LOAD_DISC_LOADING, /* disc is "spinning up" */ + LOAD_DISC_READIED, /* disc just "became ready" */ + LOAD_READY +}; + +class IDEATAPICDROMDevice:public IDEDevice { +public: + IDEATAPICDROMDevice(IDEController *c,unsigned char drive_index); + virtual ~IDEATAPICDROMDevice(); + virtual void writecommand(uint8_t cmd); +public: + std::string id_serial; + std::string id_firmware_rev; + std::string id_model; + unsigned char drive_index; + CDROM_Interface *getMSCDEXDrive(); + void update_from_cdrom(); + virtual Bitu data_read(Bitu iolen); /* read from 1F0h data port from IDE device */ + virtual void data_write(Bitu v,Bitu iolen);/* write to 1F0h data port to IDE device */ + virtual void generate_identify_device(); + virtual void generate_mmc_inquiry(); + virtual void prepare_read(Bitu offset,Bitu size); + virtual void prepare_write(Bitu offset,Bitu size); + virtual void set_sense(unsigned char SK,unsigned char ASC=0,unsigned char ASCQ=0,unsigned int len=0); + virtual bool common_spinup_response(bool trigger,bool wait); + virtual void on_mode_select_io_complete(); + virtual void atapi_io_completion(); + virtual void io_completion(); + virtual void atapi_cmd_completion(); + virtual void on_atapi_busy_time(); + virtual void read_subchannel(); + virtual void play_audio_msf(); + virtual void pause_resume(); + virtual void play_audio10(); + virtual void mode_sense(); + virtual void read_toc(); +public: + bool atapi_to_host; /* if set, PACKET data transfer is to be read by host */ + double spinup_time; + double spindown_timeout; + double cd_insertion_time; + Bitu host_maximum_byte_count; /* host maximum byte count during PACKET transfer */ + std::string id_mmc_vendor_id; + std::string id_mmc_product_id; + std::string id_mmc_product_rev; + Bitu LBA,TransferLength; + int loading_mode; + bool has_changed; +public: + unsigned char sense[256]; + Bitu sense_length; + unsigned char atapi_cmd[12]; + unsigned char atapi_cmd_i,atapi_cmd_total; + unsigned char sector[512*128]; + Bitu sector_i,sector_total; +}; + +class IDEController:public Module_base{ +public: + int IRQ; + bool int13fakeio; /* on certain INT 13h calls, force IDE state as if BIOS had carried them out */ + bool int13fakev86io; /* on certain INT 13h calls in virtual 8086 mode, trigger fake CPU I/O traps */ + bool enable_pio32; /* enable 32-bit PIO (if disabled, attempts at 32-bit PIO are handled as if two 16-bit I/O) */ + bool ignore_pio32; /* if 32-bit PIO enabled, but ignored, writes do nothing, reads return 0xFFFFFFFF */ + bool register_pnp; + unsigned short alt_io; + unsigned short base_io; + unsigned char interface_index; + IO_ReadHandleObject ReadHandler[8],ReadHandlerAlt[2]; + IO_WriteHandleObject WriteHandler[8],WriteHandlerAlt[2]; +public: + IDEDevice* device[2]; /* IDE devices (master, slave) */ + Bitu select,status,drivehead; /* which is selected, status register (0x1F7) but ONLY if no device exists at selection, drive/head register (0x1F6) */ + bool interrupt_enable; /* bit 1 of alt (0x3F6) */ + bool host_reset; /* bit 2 of alt */ + bool irq_pending; + /* defaults for CD-ROM emulation */ + double spinup_time; + double spindown_timeout; + double cd_insertion_time; +public: + IDEController(Section* configuration,unsigned char index); + void register_isapnp(); + void install_io_port(); + void raise_irq(); + void lower_irq(); + ~IDEController(); +}; + +static IDEController* idecontroller[MAX_IDE_CONTROLLERS]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; + +static void IDE_DelayedCommand(Bitu idx/*which IDE controller*/); +static IDEController* GetIDEController(Bitu idx); + +static void IDE_ATAPI_SpinDown(Bitu idx/*which IDE controller*/) { + IDEController *ctrl = GetIDEController(idx); + if (ctrl == NULL) return; + + for (unsigned int i=0;i < 2;i++) { + IDEDevice *dev = ctrl->device[i]; + if (dev == NULL) continue; + + if (dev->type == IDE_TYPE_HDD) { + } + else if (dev->type == IDE_TYPE_CDROM) { + IDEATAPICDROMDevice *atapi = (IDEATAPICDROMDevice*)dev; + + if (atapi->loading_mode == LOAD_DISC_READIED || atapi->loading_mode == LOAD_READY) { + atapi->loading_mode = LOAD_IDLE; + LOG_MSG("ATAPI CD-ROM: spinning down\n"); + } + } + else { + LOG_MSG("Unknown ATAPI spinup callback\n"); + } + } +} + +static void IDE_ATAPI_SpinUpComplete(Bitu idx/*which IDE controller*/); + +static void IDE_ATAPI_CDInsertion(Bitu idx/*which IDE controller*/) { + IDEController *ctrl = GetIDEController(idx); + if (ctrl == NULL) return; + + for (unsigned int i=0;i < 2;i++) { + IDEDevice *dev = ctrl->device[i]; + if (dev == NULL) continue; + + if (dev->type == IDE_TYPE_HDD) { + } + else if (dev->type == IDE_TYPE_CDROM) { + IDEATAPICDROMDevice *atapi = (IDEATAPICDROMDevice*)dev; + + if (atapi->loading_mode == LOAD_INSERT_CD) { + atapi->loading_mode = LOAD_DISC_LOADING; + LOG_MSG("ATAPI CD-ROM: insert CD to loading\n"); + PIC_RemoveSpecificEvents(IDE_ATAPI_SpinDown,idx); + PIC_RemoveSpecificEvents(IDE_ATAPI_CDInsertion,idx); + PIC_AddEvent(IDE_ATAPI_SpinUpComplete,atapi->spinup_time/*ms*/,idx); + } + } + else { + LOG_MSG("Unknown ATAPI spinup callback\n"); + } + } +} + +static void IDE_ATAPI_SpinUpComplete(Bitu idx/*which IDE controller*/) { + IDEController *ctrl = GetIDEController(idx); + if (ctrl == NULL) return; + + for (unsigned int i=0;i < 2;i++) { + IDEDevice *dev = ctrl->device[i]; + if (dev == NULL) continue; + + if (dev->type == IDE_TYPE_HDD) { + } + else if (dev->type == IDE_TYPE_CDROM) { + IDEATAPICDROMDevice *atapi = (IDEATAPICDROMDevice*)dev; + + if (atapi->loading_mode == LOAD_DISC_LOADING) { + atapi->loading_mode = LOAD_DISC_READIED; + LOG_MSG("ATAPI CD-ROM: spinup complete\n"); + PIC_RemoveSpecificEvents(IDE_ATAPI_SpinDown,idx); + PIC_RemoveSpecificEvents(IDE_ATAPI_CDInsertion,idx); + PIC_AddEvent(IDE_ATAPI_SpinDown,atapi->spindown_timeout/*ms*/,idx); + } + } + else { + LOG_MSG("Unknown ATAPI spinup callback\n"); + } + } +} + +/* returns "true" if command should proceed as normal, "false" if sense data was set and command should not proceed. + * this function helps to enforce virtual "spin up" and "ready" delays. */ +bool IDEATAPICDROMDevice::common_spinup_response(bool trigger,bool wait) { + if (loading_mode == LOAD_IDLE) { + if (trigger) { + LOG_MSG("ATAPI CD-ROM: triggered to spin up from idle\n"); + loading_mode = LOAD_DISC_LOADING; + PIC_RemoveSpecificEvents(IDE_ATAPI_SpinDown,controller->interface_index); + PIC_RemoveSpecificEvents(IDE_ATAPI_CDInsertion,controller->interface_index); + PIC_AddEvent(IDE_ATAPI_SpinUpComplete,spinup_time/*ms*/,controller->interface_index); + } + } + else if (loading_mode == LOAD_READY) { + if (trigger) { + PIC_RemoveSpecificEvents(IDE_ATAPI_SpinDown,controller->interface_index); + PIC_RemoveSpecificEvents(IDE_ATAPI_CDInsertion,controller->interface_index); + PIC_AddEvent(IDE_ATAPI_SpinDown,spindown_timeout/*ms*/,controller->interface_index); + } + } + + switch (loading_mode) { + case LOAD_NO_DISC: + case LOAD_INSERT_CD: + set_sense(/*SK=*/0x02,/*ASC=*/0x3A); /* Medium Not Present */ + return false; + case LOAD_DISC_LOADING: + if (has_changed && !wait/*if command will block until LOADING complete*/) { + set_sense(/*SK=*/0x02,/*ASC=*/0x04,/*ASCQ=*/0x01); /* Medium is becoming available */ + return false; + } + break; + case LOAD_DISC_READIED: + loading_mode = LOAD_READY; + if (has_changed) { + if (trigger) has_changed = false; + set_sense(/*SK=*/0x02,/*ASC=*/0x28,/*ASCQ=*/0x00); /* Medium is ready (has changed) */ + return false; + } + break; + case LOAD_IDLE: + case LOAD_READY: + break; + default: + abort(); + }; + + return true; +} + +void IDEATAPICDROMDevice::read_subchannel() { +// unsigned char Format = atapi_cmd[2] & 0xF; +// unsigned char Track = atapi_cmd[6]; + unsigned char paramList = atapi_cmd[3]; + unsigned char attr,track,index; + bool SUBQ = !!(atapi_cmd[2] & 0x40); + bool TIME = !!(atapi_cmd[1] & 2); + unsigned char *write; + unsigned char astat; + bool playing,pause; + TMSF rel,abs; + + CDROM_Interface *cdrom = getMSCDEXDrive(); + if (cdrom == NULL) { + LOG_MSG("WARNING: ATAPI READ TOC unable to get CDROM drive\n"); + prepare_read(0,8); + return; + } + + if (paramList == 0 || paramList > 3) { + LOG_MSG("ATAPI READ SUBCHANNEL unknown param list\n"); + prepare_read(0,8); + return; + } + else if (paramList == 2) { + LOG_MSG("ATAPI READ SUBCHANNEL Media Catalog Number not supported\n"); + prepare_read(0,8); + return; + } + else if (paramList == 3) { + LOG_MSG("ATAPI READ SUBCHANNEL ISRC not supported\n"); + prepare_read(0,8); + return; + } + + /* get current subchannel position */ + if (!cdrom->GetAudioSub(attr,track,index,rel,abs)) { + LOG_MSG("ATAPI READ SUBCHANNEL unable to read current pos\n"); + prepare_read(0,8); + return; + } + + if (!cdrom->GetAudioStatus(playing,pause)) + playing = pause = false; + + if (playing) + astat = pause ? 0x12 : 0x11; + else + astat = 0x13; + + memset(sector,0,8); + write = sector; + *write++ = 0x00; + *write++ = astat;/* AUDIO STATUS */ + *write++ = 0x00;/* SUBCHANNEL DATA LENGTH */ + *write++ = 0x00; + + if (SUBQ) { + *write++ = 0x01; /* subchannel data format code */ + *write++ = (attr >> 4) | 0x10; /* ADR/CONTROL */ + *write++ = track; + *write++ = index; + if (TIME) { + *write++ = 0x00; + *write++ = abs.min; + *write++ = abs.sec; + *write++ = abs.fr; + *write++ = 0x00; + *write++ = rel.min; + *write++ = rel.sec; + *write++ = rel.fr; + } + else { + uint32_t sec; + + sec = (abs.min*60u*75u)+(abs.sec*75u)+abs.fr - 150u; + *write++ = (unsigned char)(sec >> 24u); + *write++ = (unsigned char)(sec >> 16u); + *write++ = (unsigned char)(sec >> 8u); + *write++ = (unsigned char)(sec >> 0u); + + sec = (rel.min*60u*75u)+(rel.sec*75u)+rel.fr - 150u; + *write++ = (unsigned char)(sec >> 24u); + *write++ = (unsigned char)(sec >> 16u); + *write++ = (unsigned char)(sec >> 8u); + *write++ = (unsigned char)(sec >> 0u); + } + } + + { + unsigned int x = (unsigned int)(write-sector) - 4; + sector[2] = x >> 8; + sector[3] = x; + } + + prepare_read(0,MIN((unsigned int)(write-sector),(unsigned int)host_maximum_byte_count)); +#if 0 + LOG_MSG("SUBCH "); + for (size_t i=0;i < sector_total;i++) LOG_MSG("%02x ",sector[i]); + LOG_MSG("\n"); +#endif +} + +void IDEATAPICDROMDevice::mode_sense() { + unsigned char PAGE = atapi_cmd[2] & 0x3F; +// unsigned char SUBPAGE = atapi_cmd[3]; + unsigned char *write; + unsigned int x; + + write = sector; + + /* some header. not well documented */ + *write++ = 0x00; /* ?? */ + *write++ = 0x00; /* length */ + *write++ = 0x00; /* ?? */ + *write++ = 0x00; + *write++ = 0x00; + *write++ = 0x00; + *write++ = 0x00; + *write++ = 0x00; + + *write++ = PAGE; /* page code */ + *write++ = 0x00; /* page length (fill in later) */ + switch (PAGE) { + case 0x01: /* Read error recovery */ + *write++ = 0x00; /* maximum error correction */ + *write++ = 3; /* read retry count */ + *write++ = 0x00; + *write++ = 0x00; + *write++ = 0x00; + *write++ = 0x00; + break; + case 0x0E: /* CD-ROM audio control */ + *write++ = 0x04; /* ?? */ + *write++ = 0x00; /* reserved @+3 */ + *write++ = 0x00; /* reserved @+4 */ + *write++ = 0x00; /* reserved @+5 */ + *write++ = 0x00; + *write++ = 75; /* logical blocks per second */ + + *write++ = 0x01; /* output port 0 selection */ + *write++ = 0xD8; /* output port 0 volume (?) */ + *write++ = 0x02; /* output port 1 selection */ + *write++ = 0xD8; /* output port 1 volume (?) */ + *write++ = 0x00; /* output port 2 selection */ + *write++ = 0x00; /* output port 2 volume (?) */ + *write++ = 0x00; /* output port 3 selection */ + *write++ = 0x00; /* output port 3 volume (?) */ + break; + case 0x2A: /* CD-ROM mechanical status */ + *write++ = 0x00; /* reserved @+2 ?? */ + *write++ = 0x00; /* reserved @+3 ?? */ + *write++ = 0xF1; /* multisession=0 mode2form2=1 mode2form=1 audioplay=1 */ + *write++ = 0xFF; /* ISRC=1 UPC=1 C2=1 RWDeinterleave=1 RWSupported=1 CDDAAccurate=1 CDDASupported=1 */ + *write++ = 0x29; /* loading mechanism type=tray eject=1 prevent jumper=0 lockstate=0 lock=1 */ + *write++ = 0x03; /* separate channel mute=1 separate channel volume levels=1 */ + + x = 176 * 8; /* maximum speed supported: 8X */ + *write++ = x>>8; + *write++ = x; + + x = 256; /* (?) */ + *write++ = x>>8; + *write++ = x; + + x = 6 * 256; /* (?) */ + *write++ = x>>8; + *write++ = x; + + x = 176 * 8; /* current speed supported: 8X */ + *write++ = x>>8; + *write++ = x; + break; + default: + memset(write,0,6); write += 6; + LOG_MSG("WARNING: MODE SENSE on page 0x%02x not supported\n",PAGE); + break; + }; + + /* fill in page length */ + sector[1] = (unsigned int)(write-sector) - 2; + sector[8+1] = (unsigned int)(write-sector) - 2 - 8; + + prepare_read(0,MIN((unsigned int)(write-sector),(unsigned int)host_maximum_byte_count)); +#if 0 + printf("SENSE "); + for (size_t i=0;i < sector_total;i++) printf("%02x ",sector[i]); + printf("\n"); +#endif +} + +void IDEATAPICDROMDevice::pause_resume() { + bool Resume = !!(atapi_cmd[8] & 1); + + CDROM_Interface *cdrom = getMSCDEXDrive(); + if (cdrom == NULL) { + LOG_MSG("WARNING: ATAPI READ TOC unable to get CDROM drive\n"); + sector_total = 0; + return; + } + + cdrom->PauseAudio(Resume); +} + +void IDEATAPICDROMDevice::play_audio_msf() { + uint32_t start_lba,end_lba; + + CDROM_Interface *cdrom = getMSCDEXDrive(); + if (cdrom == NULL) { + LOG_MSG("WARNING: ATAPI READ TOC unable to get CDROM drive\n"); + sector_total = 0; + return; + } + + if (atapi_cmd[3] == 0xFF && atapi_cmd[4] == 0xFF && atapi_cmd[5] == 0xFF) + start_lba = 0xFFFFFFFF; + else { + start_lba = (atapi_cmd[3] * 60u * 75u) + + (atapi_cmd[4] * 75u) + + atapi_cmd[5]; + + if (start_lba >= 150u) start_lba -= 150u; /* LBA sector 0 == M:S:F sector 0:2:0 */ + else end_lba = 0; + } + + if (atapi_cmd[6] == 0xFF && atapi_cmd[7] == 0xFF && atapi_cmd[8] == 0xFF) + end_lba = 0xFFFFFFFF; + else { + end_lba = (atapi_cmd[6] * 60u * 75u) + + (atapi_cmd[7] * 75u) + + atapi_cmd[8]; + + if (end_lba >= 150u) end_lba -= 150u; /* LBA sector 0 == M:S:F sector 0:2:0 */ + else end_lba = 0; + } + + if (start_lba == end_lba) { + /* The play length field specifies the number of contiguous logical blocks that shall + * be played. A play length of zero indicates that no audio operation shall occur. + * This condition is not an error. */ + /* TODO: How do we interpret that? Does that mean audio playback stops? Or does it + * mean we do nothing to the state of audio playback? */ + sector_total = 0; + return; + } + + /* LBA 0xFFFFFFFF means start playing wherever the optics of the CD sit */ + if (start_lba != 0xFFFFFFFF) + cdrom->PlayAudioSector(start_lba,end_lba - start_lba); + else + cdrom->PauseAudio(true); + + sector_total = 0; +} + +void IDEATAPICDROMDevice::play_audio10() { + uint16_t play_length; + uint32_t start_lba; + + CDROM_Interface *cdrom = getMSCDEXDrive(); + if (cdrom == NULL) { + LOG_MSG("WARNING: ATAPI READ TOC unable to get CDROM drive\n"); + sector_total = 0; + return; + } + + start_lba = ((uint32_t)atapi_cmd[2] << 24) + + ((uint32_t)atapi_cmd[3] << 16) + + ((uint32_t)atapi_cmd[4] << 8) + + ((uint32_t)atapi_cmd[5] << 0); + + play_length = ((uint16_t)atapi_cmd[7] << 8) + + ((uint16_t)atapi_cmd[8] << 0); + + if (play_length == 0) { + /* The play length field specifies the number of contiguous logical blocks that shall + * be played. A play length of zero indicates that no audio operation shall occur. + * This condition is not an error. */ + /* TODO: How do we interpret that? Does that mean audio playback stops? Or does it + * mean we do nothing to the state of audio playback? */ + sector_total = 0; + return; + } + + /* LBA 0xFFFFFFFF means start playing wherever the optics of the CD sit */ + if (start_lba != 0xFFFFFFFF) + cdrom->PlayAudioSector(start_lba,play_length); + else + cdrom->PauseAudio(true); + + sector_total = 0; +} + +#if 0 /* TODO move to library */ +static unsigned char dec2bcd(unsigned char c) { + return ((c / 10) << 4) + (c % 10); +} +#endif + +void IDEATAPICDROMDevice::read_toc() { + /* NTS: The SCSI MMC standards say we're allowed to indicate the return data + * is longer than it's allocation length. But here's the thing: some MS-DOS + * CD-ROM drivers will ask for the TOC but only provide enough room for one + * entry (OAKCDROM.SYS) and if we signal more data than it's buffer, it will + * reject our response and render the CD-ROM drive inaccessible. So to make + * this emulation work, we have to cut our response short to the driver's + * allocation length */ + unsigned int AllocationLength = ((unsigned int)atapi_cmd[7] << 8) + atapi_cmd[8]; + unsigned char Format = atapi_cmd[2] & 0xF; + unsigned char Track = atapi_cmd[6]; + bool TIME = !!(atapi_cmd[1] & 2); + unsigned char *write; + int first,last,track; + TMSF leadOut; + + CDROM_Interface *cdrom = getMSCDEXDrive(); + if (cdrom == NULL) { + LOG_MSG("WARNING: ATAPI READ TOC unable to get CDROM drive\n"); + prepare_read(0,8); + return; + } + + memset(sector,0,8); + + if (!cdrom->GetAudioTracks(first,last,leadOut)) { + LOG_MSG("WARNING: ATAPI READ TOC failed to get track info\n"); + prepare_read(0,8); + return; + } + + /* start 2 bytes out. we'll fill in the data length later */ + write = sector + 2; + + if (Format == 1) { /* Read multisession info */ + unsigned char attr; + TMSF start; + + *write++ = (unsigned char)1; /* @+2 first complete session */ + *write++ = (unsigned char)1; /* @+3 last complete session */ + + if (!cdrom->GetAudioTrackInfo(first,start,attr)) { + LOG_MSG("WARNING: ATAPI READ TOC unable to read track %u information\n",first); + attr = 0x41; /* ADR=1 CONTROL=4 */ + start.min = 0; + start.sec = 0; + start.fr = 0; + } + + LOG_MSG("Track %u attr=0x%02x\n",first,attr); + + *write++ = 0x00; /* entry+0 RESERVED */ + *write++ = (attr >> 4) | 0x10; /* entry+1 ADR=1 CONTROL=4 (DATA) */ + *write++ = (unsigned char)first;/* entry+2 TRACK */ + *write++ = 0x00; /* entry+3 RESERVED */ + + /* then, start address of first track in session */ + if (TIME) { + *write++ = 0x00; + *write++ = start.min; + *write++ = start.sec; + *write++ = start.fr; + } + else { + uint32_t sec = (start.min*60u*75u)+(start.sec*75u)+start.fr - 150u; + *write++ = (unsigned char)(sec >> 24u); + *write++ = (unsigned char)(sec >> 16u); + *write++ = (unsigned char)(sec >> 8u); + *write++ = (unsigned char)(sec >> 0u); + } + } + else if (Format == 0) { /* Read table of contents */ + *write++ = (unsigned char)first; /* @+2 */ + *write++ = (unsigned char)last; /* @+3 */ + + for (track=first;track <= last;track++) { + unsigned char attr; + TMSF start; + + if (!cdrom->GetAudioTrackInfo(track,start,attr)) { + LOG_MSG("WARNING: ATAPI READ TOC unable to read track %u information\n",track); + attr = 0x41; /* ADR=1 CONTROL=4 */ + start.min = 0; + start.sec = 0; + start.fr = 0; + } + + if (track < Track) + continue; + if ((write+8) > (sector+AllocationLength)) + break; + + LOG_MSG("Track %u attr=0x%02x\n",track,attr); + + *write++ = 0x00; /* entry+0 RESERVED */ + *write++ = (attr >> 4) | 0x10; /* entry+1 ADR=1 CONTROL=4 (DATA) */ + *write++ = (unsigned char)track;/* entry+2 TRACK */ + *write++ = 0x00; /* entry+3 RESERVED */ + if (TIME) { + *write++ = 0x00; + *write++ = start.min; + *write++ = start.sec; + *write++ = start.fr; + } + else { + uint32_t sec = (start.min*60u*75u)+(start.sec*75u)+start.fr - 150u; + *write++ = (unsigned char)(sec >> 24u); + *write++ = (unsigned char)(sec >> 16u); + *write++ = (unsigned char)(sec >> 8u); + *write++ = (unsigned char)(sec >> 0u); + } + } + + if ((write+8) <= (sector+AllocationLength)) { + *write++ = 0x00; + *write++ = 0x14; + *write++ = 0xAA;/*TRACK*/ + *write++ = 0x00; + if (TIME) { + *write++ = 0x00; + *write++ = leadOut.min; + *write++ = leadOut.sec; + *write++ = leadOut.fr; + } + else { + uint32_t sec = (leadOut.min*60u*75u)+(leadOut.sec*75u)+leadOut.fr - 150u; + *write++ = (unsigned char)(sec >> 24u); + *write++ = (unsigned char)(sec >> 16u); + *write++ = (unsigned char)(sec >> 8u); + *write++ = (unsigned char)(sec >> 0u); + } + } + } + else { + LOG_MSG("WARNING: ATAPI READ TOC Format=%u not supported\n",Format); + prepare_read(0,8); + return; + } + + /* update the TOC data length field */ + { + unsigned int x = (unsigned int)(write-sector) - 2; + sector[0] = x >> 8; + sector[1] = x & 0xFF; + } + + prepare_read(0,MIN(MIN((unsigned int)(write-sector),(unsigned int)host_maximum_byte_count),AllocationLength)); +} + +/* when the ATAPI command has been accepted, and the timeout has passed */ +void IDEATAPICDROMDevice::on_atapi_busy_time() { + /* if the drive is spinning up, then the command waits */ + if (loading_mode == LOAD_DISC_LOADING) { + switch (atapi_cmd[0]) { + case 0x00: /* TEST UNIT READY */ + case 0x03: /* REQUEST SENSE */ + allow_writing = true; + break; /* do not delay */ + default: + PIC_AddEvent(IDE_DelayedCommand,100/*ms*/,controller->interface_index); + return; + } + } + else if (loading_mode == LOAD_DISC_READIED) { + switch (atapi_cmd[0]) { + case 0x00: /* TEST UNIT READY */ + case 0x03: /* REQUEST SENSE */ + allow_writing = true; + break; /* do not delay */ + default: + if (!common_spinup_response(/*spin up*/true,/*wait*/false)) { + count = 0x03; + state = IDE_DEV_READY; + feature = ((sense[2]&0xF) << 4) | (sense[2]&0xF ? 0x04/*abort*/ : 0x00); + status = IDE_STATUS_DRIVE_READY|(sense[2]&0xF ? IDE_STATUS_ERROR:IDE_STATUS_DRIVE_SEEK_COMPLETE); + controller->raise_irq(); + allow_writing = true; + return; + } + break; + } + } + + switch (atapi_cmd[0]) { + case 0x03: /* REQUEST SENSE */ + prepare_read(0,MIN((unsigned int)sense_length,(unsigned int)host_maximum_byte_count)); + memcpy(sector,sense,sense_length); + + feature = 0x00; + state = IDE_DEV_DATA_READ; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ|IDE_STATUS_DRIVE_SEEK_COMPLETE; + + /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ + lba[2] = sector_total >> 8; + lba[1] = sector_total; + + controller->raise_irq(); + allow_writing = true; + break; + case 0x1E: /* PREVENT ALLOW MEDIUM REMOVAL */ + count = 0x03; + feature = 0x00; + sector_total = 0x00; + state = IDE_DEV_DATA_READ; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + + /* Don't care. Do nothing. */ + + /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ + lba[2] = sector_total >> 8; + lba[1] = sector_total; + + controller->raise_irq(); + allow_writing = true; + break; + case 0x25: /* READ CAPACITY */ { + const unsigned int secsize = 2048; + int first,last; + TMSF leadOut; + + CDROM_Interface *cdrom = getMSCDEXDrive(); + + if (!cdrom->GetAudioTracks(first,last,leadOut)) + LOG_MSG("WARNING: ATAPI READ TOC failed to get track info\n"); + + uint32_t sec = (leadOut.min*60u*75u)+(leadOut.sec*75u)+leadOut.fr - 150u; + + prepare_read(0,MIN((unsigned int)8,(unsigned int)host_maximum_byte_count)); + sector[0] = sec >> 24u; + sector[1] = sec >> 16u; + sector[2] = sec >> 8u; + sector[3] = sec & 0xFF; + sector[4] = secsize >> 24u; + sector[5] = secsize >> 16u; + sector[6] = secsize >> 8u; + sector[7] = secsize & 0xFF; +// LOG_MSG("sec=%lu secsize=%lu\n",sec,secsize); + + feature = 0x00; + state = IDE_DEV_DATA_READ; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ|IDE_STATUS_DRIVE_SEEK_COMPLETE; + + /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ + lba[2] = sector_total >> 8; + lba[1] = sector_total; + + controller->raise_irq(); + allow_writing = true; + } break; + case 0x2B: /* SEEK */ + count = 0x03; + feature = 0x00; + sector_total = 0x00; + state = IDE_DEV_DATA_READ; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + + /* Don't care. Do nothing. */ + + /* Except... Windows 95's CD player expects the SEEK command to interrupt CD audio playback. + * In fact it depends on it to the exclusion of commands explicitly standardized to... you know... + * stop or pause playback. Oh Microsoft, you twits... */ + { + CDROM_Interface *cdrom = getMSCDEXDrive(); + if (cdrom) { + bool playing,pause; + + if (!cdrom->GetAudioStatus(playing,pause)) + playing = true; + + if (playing) { + LOG_MSG("ATAPI: Interrupting CD audio playback due to SEEK\n"); + cdrom->StopAudio(); + } + } + } + + /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ + lba[2] = sector_total >> 8; + lba[1] = sector_total; + + controller->raise_irq(); + allow_writing = true; + break; + case 0x12: /* INQUIRY */ + /* NTS: the state of atapi_to_host doesn't seem to matter. */ + generate_mmc_inquiry(); + prepare_read(0,MIN((unsigned int)36,(unsigned int)host_maximum_byte_count)); + + feature = 0x00; + state = IDE_DEV_DATA_READ; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ|IDE_STATUS_DRIVE_SEEK_COMPLETE; + + /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ + lba[2] = sector_total >> 8; + lba[1] = sector_total; + + controller->raise_irq(); + allow_writing = true; + break; + case 0x28: /* READ(10) */ + case 0xA8: /* READ(12) */ + if (TransferLength == 0) { + /* this is legal. the SCSI MMC standards say so. + and apparently, MSCDEX.EXE issues READ(10) commands with transfer length == 0 + to test the drive, so we have to emulate this */ + feature = 0x00; + count = 0x03; /* no more transfer */ + sector_total = 0;/*nothing to transfer */ + state = IDE_DEV_READY; + status = IDE_STATUS_DRIVE_READY; + } + else { + /* OK, try to read */ + CDROM_Interface *cdrom = getMSCDEXDrive(); + bool res = (cdrom != NULL ? cdrom->ReadSectorsHost(/*buffer*/sector,false,LBA,TransferLength) : false); + if (res) { + prepare_read(0,MIN((unsigned int)(TransferLength*2048),(unsigned int)host_maximum_byte_count)); + feature = 0x00; + state = IDE_DEV_DATA_READ; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ|IDE_STATUS_DRIVE_SEEK_COMPLETE; + } + else { + feature = 0xF4; /* abort sense=0xF */ + count = 0x03; /* no more transfer */ + sector_total = 0;/*nothing to transfer */ + state = IDE_DEV_READY; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_ERROR; + LOG_MSG("ATAPI: Failed to read %lu sectors at %lu\n", + (unsigned long)TransferLength,(unsigned long)LBA); + /* TODO: write sense data */ + } + } + + /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ + lba[2] = sector_total >> 8; + lba[1] = sector_total; + + controller->raise_irq(); + allow_writing = true; + break; + case 0x42: /* READ SUB-CHANNEL */ + read_subchannel(); + + feature = 0x00; + state = IDE_DEV_DATA_READ; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ|IDE_STATUS_DRIVE_SEEK_COMPLETE; + + /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ + lba[2] = sector_total >> 8; + lba[1] = sector_total; + + controller->raise_irq(); + allow_writing = true; + break; + case 0x43: /* READ TOC */ + read_toc(); + + feature = 0x00; + state = IDE_DEV_DATA_READ; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ|IDE_STATUS_DRIVE_SEEK_COMPLETE; + + /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ + lba[2] = sector_total >> 8; + lba[1] = sector_total; + + controller->raise_irq(); + allow_writing = true; + break; + case 0x45: /* PLAY AUDIO(10) */ + play_audio10(); + + count = 0x03; + feature = 0x00; + sector_total = 0x00; + state = IDE_DEV_DATA_READ; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + + /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ + lba[2] = sector_total >> 8; + lba[1] = sector_total; + + controller->raise_irq(); + allow_writing = true; + break; + case 0x47: /* PLAY AUDIO MSF */ + play_audio_msf(); + + count = 0x03; + feature = 0x00; + sector_total = 0x00; + state = IDE_DEV_DATA_READ; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + + /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ + lba[2] = sector_total >> 8; + lba[1] = sector_total; + + controller->raise_irq(); + allow_writing = true; + break; + case 0x4B: /* PAUSE/RESUME */ + pause_resume(); + + count = 0x03; + feature = 0x00; + sector_total = 0x00; + state = IDE_DEV_DATA_READ; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + + /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ + lba[2] = sector_total >> 8; + lba[1] = sector_total; + + controller->raise_irq(); + allow_writing = true; + break; + case 0x55: /* MODE SELECT(10) */ + /* we need the data written first, will act in I/O completion routine */ + { + unsigned int x; + + x = (unsigned int)lba[1] + ((unsigned int)lba[2] << 8u); + + /* Windows 95 likes to set 0xFFFF here for whatever reason. + * Negotiate it down to a maximum of 512 for sanity's sake */ + if (x > 512) x = 512; + lba[2] = x >> 8u; + lba[1] = x; + +// LOG_MSG("MODE SELECT expecting %u bytes\n",x); + prepare_write(0,(x+1u)&(~1u)); + } + + feature = 0x00; + state = IDE_DEV_DATA_WRITE; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ|IDE_STATUS_DRIVE_SEEK_COMPLETE; + controller->raise_irq(); + allow_writing = true; + break; + case 0x5A: /* MODE SENSE(10) */ + mode_sense(); + + feature = 0x00; + state = IDE_DEV_DATA_READ; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ|IDE_STATUS_DRIVE_SEEK_COMPLETE; + + /* ATAPI protocol also says we write back into LBA 23:8 what we're going to transfer in the block */ + lba[2] = sector_total >> 8; + lba[1] = sector_total; + + controller->raise_irq(); + allow_writing = true; + break; + default: + LOG_MSG("Unknown ATAPI command after busy wait. Why?\n"); + abort_error(); + controller->raise_irq(); + allow_writing = true; + break; + }; + +} + +void IDEATAPICDROMDevice::set_sense(unsigned char SK,unsigned char ASC,unsigned char ASCQ,unsigned int len) { + if (len < 18) len = 18; + memset(sense,0,len); + sense_length = len; + + sense[0] = 0x70; /* RESPONSE CODE */ + sense[2] = SK&0xF; /* SENSE KEY */ + sense[7] = len - 18; /* additional sense length */ + sense[12] = ASC; + sense[13] = ASCQ; +} + +IDEATAPICDROMDevice::IDEATAPICDROMDevice(IDEController *c,unsigned char drive_index) : IDEDevice(c) { + this->drive_index = drive_index; + sector_i = sector_total = 0; + + memset(sense,0,sizeof(sense)); + set_sense(/*SK=*/0); + + /* FIXME: Spinup/down times should be dosbox.conf configurable, if the DOSBox gamers + * care more about loading times than emulation accuracy. */ + cd_insertion_time = 4000; /* a quick user that can switch CDs in 4 seconds */ + if (c->cd_insertion_time > 0) cd_insertion_time = c->cd_insertion_time; + + spinup_time = 1000; /* drive takes 1 second to spin up from idle */ + if (c->spinup_time > 0) spinup_time = c->spinup_time; + + spindown_timeout = 10000; /* drive spins down automatically after 10 seconds */ + if (c->spindown_timeout > 0) spindown_timeout = c->spindown_timeout; + + loading_mode = LOAD_IDLE; + has_changed = false; + + type = IDE_TYPE_CDROM; + id_serial = "123456789"; + id_firmware_rev = "0.74-X"; + id_model = "DOSBox Virtual CD-ROM"; + + /* INQUIRY strings */ + id_mmc_vendor_id = "DOSBox"; + id_mmc_product_id = "Virtual CD-ROM"; + id_mmc_product_rev = "0.74-X"; +} + +IDEATAPICDROMDevice::~IDEATAPICDROMDevice() { +} + +void IDEATAPICDROMDevice::on_mode_select_io_complete() { + unsigned int AllocationLength = ((unsigned int)atapi_cmd[7] << 8) + atapi_cmd[8]; + unsigned char *scan,*fence; + size_t i; + + /* the first 8 bytes are a mode parameter header. + * It's supposed to provide length, density, etc. or whatever the hell + * it means. Windows 95 seems to send all zeros there, so ignore it. + * + * we care about the bytes following it, which contain page_0 mode + * pages */ + + scan = sector + 8; + fence = sector + MIN((unsigned int)sector_total,(unsigned int)AllocationLength); + + while ((scan+2) < fence) { + unsigned char PAGE = *scan++; + unsigned int LEN = (unsigned int)(*scan++); + + if ((scan+LEN) > fence) { + LOG_MSG("ATAPI MODE SELECT warning, page_0 length extends %u bytes past buffer\n",(unsigned int)(scan+LEN-fence)); + break; + } + + LOG_MSG("ATAPI MODE SELECT, PAGE 0x%02x len=%u\n",PAGE,LEN); + LOG_MSG(" "); + for (i=0;i < LEN;i++) LOG_MSG("%02x ",scan[i]); + LOG_MSG("\n"); + + scan += LEN; + } +} + +void IDEATAPICDROMDevice::atapi_io_completion() { + /* for most ATAPI PACKET commands, the transfer is done and we need to clear + all indication of a possible data transfer */ + + if (count == 0x00) { /* the command was expecting data. now it can act on it */ + switch (atapi_cmd[0]) { + case 0x55: /* MODE SELECT(10) */ + on_mode_select_io_complete(); + break; + }; + } + + count = 0x03; /* no more data (command/data=1, input/output=1) */ + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + state = IDE_DEV_READY; + allow_writing = true; + + /* Apparently: real IDE ATAPI controllers fire another IRQ after the transfer. + And there are MS-DOS CD-ROM drivers that assume that. */ + controller->raise_irq(); +} + +void IDEATAPICDROMDevice::io_completion() { + /* lower DRQ */ + status &= ~IDE_STATUS_DRQ; + + /* depending on the command, either continue it or finish up */ + switch (command) { + case 0xA0:/*ATAPI PACKET*/ + atapi_io_completion(); + break; + default: /* most commands: signal drive ready, return to ready state */ + /* NTS: Some MS-DOS CD-ROM drivers will loop endlessly if we never set "drive seek complete" + because they like to hit the device with DEVICE RESET (08h) whether or not it's + a hard disk or CD-ROM drive */ + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + state = IDE_DEV_READY; + allow_writing = true; + break; + } +} + +bool IDEATADevice::increment_current_address(Bitu count) { + if (count == 0) return false; + + if (drivehead_is_lba(drivehead)) { + /* 28-bit LBA: + * drivehead: 27:24 + * lba[2]: 23:16 + * lba[1]: 15:8 + * lba[0]: 7:0 */ + do { + if (((++lba[0])&0xFF) == 0x00) { + lba[0] = 0x00; + if (((++lba[1])&0xFF) == 0x00) { + lba[1] = 0x00; + if (((++lba[2])&0xFF) == 0x00) { + lba[2] = 0x00; + if (((++drivehead)&0xF) == 0) { + drivehead -= 0x10; + return false; + } + } + } + } + } while ((--count) != 0); + } + else { + /* C/H/S increment with rollover */ + do { + /* increment sector */ + if (((++lba[0])&0xFF) == ((sects+1)&0xFF)) { + lba[0] = 1; + /* increment head */ + if (((++drivehead)&0xF) == (heads&0xF)) { + drivehead &= 0xF0; + if (heads == 16) drivehead -= 0x10; + /* increment cylinder */ + if (((++lba[1])&0xFF) == 0x00) { + if (((++lba[2])&0xFF) == 0x00) { + return false; + } + } + } + + } + } while ((--count) != 0); + } + + return true; +} + +void IDEATADevice::io_completion() { + /* lower DRQ */ + status &= ~IDE_STATUS_DRQ; + + /* depending on the command, either continue it or finish up */ + switch (command) { + case 0x20:/* READ SECTOR */ + /* OK, decrement count, increment address */ + /* NTS: Remember that count == 0 means the host wanted to transfer 256 sectors */ + progress_count++; + if ((count&0xFF) == 1) { + /* end of the transfer */ + count = 0; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + state = IDE_DEV_READY; + allow_writing = true; + return; + } + else if ((count&0xFF) == 0) count = 255; + else count--; + + if (!increment_current_address()) { + LOG_MSG("READ advance error\n"); + abort_error(); + return; + } + + /* cause another delay, another sector read */ + state = IDE_DEV_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,0.00001/*ms*/,controller->interface_index); + break; + case 0x30:/* WRITE SECTOR */ + /* this is where the drive has accepted the sector, lowers DRQ, and begins executing the command */ + state = IDE_DEV_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,((progress_count == 0 && !faked_command) ? 0.1 : 0.00001)/*ms*/,controller->interface_index); + break; + case 0xC4:/* READ MULTIPLE */ + /* OK, decrement count, increment address */ + /* NTS: Remember that count == 0 means the host wanted to transfer 256 sectors */ + for (unsigned int cc=0;cc < multiple_sector_count;cc++) { + progress_count++; + if ((count&0xFF) == 1) { + /* end of the transfer */ + count = 0; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + state = IDE_DEV_READY; + allow_writing = true; + return; + } + else if ((count&0xFF) == 0) count = 255; + else count--; + + if (!increment_current_address()) { + LOG_MSG("READ advance error\n"); + abort_error(); + return; + } + } + + /* cause another delay, another sector read */ + state = IDE_DEV_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,0.00001/*ms*/,controller->interface_index); + break; + case 0xC5:/* WRITE MULTIPLE */ + /* this is where the drive has accepted the sector, lowers DRQ, and begins executing the command */ + state = IDE_DEV_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,((progress_count == 0 && !faked_command) ? 0.1 : 0.00001)/*ms*/,controller->interface_index); + break; + default: /* most commands: signal drive ready, return to ready state */ + /* NTS: Some MS-DOS CD-ROM drivers will loop endlessly if we never set "drive seek complete" + because they like to hit the device with DEVICE RESET (08h) whether or not it's + a hard disk or CD-ROM drive */ + count = 0; + drivehead &= 0xF0; + lba[0] = 0; + lba[1] = lba[2] = 0; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + state = IDE_DEV_READY; + allow_writing = true; + break; + } +} + +Bitu IDEATAPICDROMDevice::data_read(Bitu iolen) { + Bitu w = ~0u; + + if (state != IDE_DEV_DATA_READ) + return 0xFFFFUL; + + if (!(status & IDE_STATUS_DRQ)) { + LOG_MSG("IDE: Data read when DRQ=0\n"); + return 0xFFFFUL; + } + + if (sector_i >= sector_total) + return 0xFFFFUL; + + if (iolen >= 4) { + w = host_readd(sector+sector_i); + sector_i += 4; + } + else if (iolen >= 2) { + w = host_readw(sector+sector_i); + sector_i += 2; + } + /* NTS: Some MS-DOS CD-ROM drivers like OAKCDROM.SYS use byte-wide I/O for the initial identification */ + else if (iolen == 1) { + w = sector[sector_i++]; + } + + if (sector_i >= sector_total) + io_completion(); + + return w; +} + +/* TODO: Your code should also be paying attention to the "transfer length" field + in many of the commands here. Right now it doesn't matter. */ +void IDEATAPICDROMDevice::atapi_cmd_completion() { +#if 0 + LOG_MSG("ATAPI command %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x to_host=%u\n", + atapi_cmd[ 0],atapi_cmd[ 1],atapi_cmd[ 2],atapi_cmd[ 3],atapi_cmd[ 4],atapi_cmd[ 5], + atapi_cmd[ 6],atapi_cmd[ 7],atapi_cmd[ 8],atapi_cmd[ 9],atapi_cmd[10],atapi_cmd[11], + atapi_to_host); +#endif + + switch (atapi_cmd[0]) { + case 0x00: /* TEST UNIT READY */ + if (common_spinup_response(/*spin up*/false,/*wait*/false)) + set_sense(0); /* <- nothing wrong */ + + count = 0x03; + state = IDE_DEV_READY; + feature = ((sense[2]&0xF) << 4) | (sense[2]&0xF ? 0x04/*abort*/ : 0x00); + status = IDE_STATUS_DRIVE_READY|(sense[2]&0xF ? IDE_STATUS_ERROR:IDE_STATUS_DRIVE_SEEK_COMPLETE); + controller->raise_irq(); + allow_writing = true; + break; + case 0x03: /* REQUEST SENSE */ + count = 0x02; + state = IDE_DEV_ATAPI_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); + break; + case 0x1E: /* PREVENT ALLOW MEDIUM REMOVAL */ + count = 0x02; + state = IDE_DEV_ATAPI_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); + break; + case 0x25: /* READ CAPACITY */ + count = 0x02; + state = IDE_DEV_ATAPI_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); + break; + case 0x2B: /* SEEK */ + if (common_spinup_response(/*spin up*/true,/*wait*/true)) { + set_sense(0); /* <- nothing wrong */ + count = 0x02; + state = IDE_DEV_ATAPI_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); + } + else { + count = 0x03; + state = IDE_DEV_READY; + feature = ((sense[2]&0xF) << 4) | (sense[2]&0xF ? 0x04/*abort*/ : 0x00); + status = IDE_STATUS_DRIVE_READY|(sense[2]&0xF ? IDE_STATUS_ERROR:IDE_STATUS_DRIVE_SEEK_COMPLETE); + controller->raise_irq(); + allow_writing = true; + } + break; + case 0x12: /* INQUIRY */ + count = 0x02; + state = IDE_DEV_ATAPI_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); + break; + case 0xA8: /* READ(12) */ + if (common_spinup_response(/*spin up*/true,/*wait*/true)) { + set_sense(0); /* <- nothing wrong */ + + /* FIXME: MSCDEX.EXE appears to test the drive by issuing READ(10) with transfer length == 0. + This is all well and good but our response seems to cause a temporary 2-3 second + pause for each attempt. Why? */ + LBA = ((Bitu)atapi_cmd[2] << 24UL) | + ((Bitu)atapi_cmd[3] << 16UL) | + ((Bitu)atapi_cmd[4] << 8UL) | + ((Bitu)atapi_cmd[5] << 0UL); + TransferLength = ((Bitu)atapi_cmd[6] << 24UL) | + ((Bitu)atapi_cmd[7] << 16UL) | + ((Bitu)atapi_cmd[8] << 8UL) | + ((Bitu)atapi_cmd[9]); + + /* FIXME: We actually should NOT be capping the transfer length, but instead should + be breaking the larger transfer into smaller DRQ block transfers like + most IDE ATAPI drives do. Writing the test IDE code taught me that if you + go to most drives and request a transfer length of 0xFFFE the drive will + happily set itself up to transfer that many sectors in one IDE command! */ + /* NTS: In case you're wondering, it's legal to issue READ(10) with transfer length == 0. + MSCDEX.EXE does it when starting up, for example */ + if ((TransferLength*2048) > sizeof(sector)) + TransferLength = sizeof(sector)/2048; + + count = 0x02; + state = IDE_DEV_ATAPI_BUSY; + status = IDE_STATUS_BUSY; + /* TODO: Emulate CD-ROM spin-up delay, and seek delay */ + PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 3)/*ms*/,controller->interface_index); + } + else { + count = 0x03; + state = IDE_DEV_READY; + feature = ((sense[2]&0xF) << 4) | (sense[2]&0xF ? 0x04/*abort*/ : 0x00); + status = IDE_STATUS_DRIVE_READY|(sense[2]&0xF ? IDE_STATUS_ERROR:IDE_STATUS_DRIVE_SEEK_COMPLETE); + controller->raise_irq(); + allow_writing = true; + } + break; + case 0x28: /* READ(10) */ + if (common_spinup_response(/*spin up*/true,/*wait*/true)) { + set_sense(0); /* <- nothing wrong */ + + /* FIXME: MSCDEX.EXE appears to test the drive by issuing READ(10) with transfer length == 0. + This is all well and good but our response seems to cause a temporary 2-3 second + pause for each attempt. Why? */ + LBA = ((Bitu)atapi_cmd[2] << 24UL) | + ((Bitu)atapi_cmd[3] << 16UL) | + ((Bitu)atapi_cmd[4] << 8UL) | + ((Bitu)atapi_cmd[5] << 0UL); + TransferLength = ((Bitu)atapi_cmd[7] << 8) | + ((Bitu)atapi_cmd[8]); + + /* FIXME: We actually should NOT be capping the transfer length, but instead should + be breaking the larger transfer into smaller DRQ block transfers like + most IDE ATAPI drives do. Writing the test IDE code taught me that if you + go to most drives and request a transfer length of 0xFFFE the drive will + happily set itself up to transfer that many sectors in one IDE command! */ + /* NTS: In case you're wondering, it's legal to issue READ(10) with transfer length == 0. + MSCDEX.EXE does it when starting up, for example */ + if ((TransferLength*2048) > sizeof(sector)) + TransferLength = sizeof(sector)/2048; + + count = 0x02; + state = IDE_DEV_ATAPI_BUSY; + status = IDE_STATUS_BUSY; + /* TODO: Emulate CD-ROM spin-up delay, and seek delay */ + PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 3)/*ms*/,controller->interface_index); + } + else { + count = 0x03; + state = IDE_DEV_READY; + feature = ((sense[2]&0xF) << 4) | (sense[2]&0xF ? 0x04/*abort*/ : 0x00); + status = IDE_STATUS_DRIVE_READY|(sense[2]&0xF ? IDE_STATUS_ERROR:IDE_STATUS_DRIVE_SEEK_COMPLETE); + controller->raise_irq(); + allow_writing = true; + } + break; + case 0x42: /* READ SUB-CHANNEL */ + if (common_spinup_response(/*spin up*/true,/*wait*/true)) { + set_sense(0); /* <- nothing wrong */ + + count = 0x02; + state = IDE_DEV_ATAPI_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); + } + else { + count = 0x03; + state = IDE_DEV_READY; + feature = ((sense[2]&0xF) << 4) | (sense[2]&0xF ? 0x04/*abort*/ : 0x00); + status = IDE_STATUS_DRIVE_READY|(sense[2]&0xF ? IDE_STATUS_ERROR:IDE_STATUS_DRIVE_SEEK_COMPLETE); + controller->raise_irq(); + allow_writing = true; + } + break; + case 0x43: /* READ TOC */ + if (common_spinup_response(/*spin up*/true,/*wait*/true)) { + set_sense(0); /* <- nothing wrong */ + + count = 0x02; + state = IDE_DEV_ATAPI_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); + } + else { + count = 0x03; + state = IDE_DEV_READY; + feature = ((sense[2]&0xF) << 4) | (sense[2]&0xF ? 0x04/*abort*/ : 0x00); + status = IDE_STATUS_DRIVE_READY|(sense[2]&0xF ? IDE_STATUS_ERROR:IDE_STATUS_DRIVE_SEEK_COMPLETE); + controller->raise_irq(); + allow_writing = true; + } + break; + case 0x45: /* PLAY AUDIO (1) */ + case 0x47: /* PLAY AUDIO MSF */ + case 0x4B: /* PAUSE/RESUME */ + if (common_spinup_response(/*spin up*/true,/*wait*/true)) { + set_sense(0); /* <- nothing wrong */ + + count = 0x02; + state = IDE_DEV_ATAPI_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); + } + else { + count = 0x03; + state = IDE_DEV_READY; + feature = ((sense[2]&0xF) << 4) | (sense[2]&0xF ? 0x04/*abort*/ : 0x00); + status = IDE_STATUS_DRIVE_READY|(sense[2]&0xF ? IDE_STATUS_ERROR:IDE_STATUS_DRIVE_SEEK_COMPLETE); + controller->raise_irq(); + allow_writing = true; + } + break; + case 0x55: /* MODE SELECT(10) */ + count = 0x00; /* we will be accepting data */ + state = IDE_DEV_ATAPI_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); + break; + case 0x5A: /* MODE SENSE(10) */ + count = 0x02; + state = IDE_DEV_ATAPI_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 1)/*ms*/,controller->interface_index); + break; + default: + /* we don't know the command, immediately return an error */ + LOG_MSG("Unknown ATAPI command %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + atapi_cmd[ 0],atapi_cmd[ 1],atapi_cmd[ 2],atapi_cmd[ 3],atapi_cmd[ 4],atapi_cmd[ 5], + atapi_cmd[ 6],atapi_cmd[ 7],atapi_cmd[ 8],atapi_cmd[ 9],atapi_cmd[10],atapi_cmd[11]); + + abort_error(); + count = 0x03; /* no more data (command/data=1, input/output=1) */ + feature = 0xF4; + controller->raise_irq(); + allow_writing = true; + break; + }; +} + +void IDEATAPICDROMDevice::data_write(Bitu v,Bitu iolen) { + if (state == IDE_DEV_ATAPI_PACKET_COMMAND) { + if (atapi_cmd_i < atapi_cmd_total) + atapi_cmd[atapi_cmd_i++] = v; + if (iolen >= 2 && atapi_cmd_i < atapi_cmd_total) + atapi_cmd[atapi_cmd_i++] = v >> 8; + if (iolen >= 4 && atapi_cmd_i < atapi_cmd_total) { + atapi_cmd[atapi_cmd_i++] = v >> 16; + atapi_cmd[atapi_cmd_i++] = v >> 24; + } + + if (atapi_cmd_i >= atapi_cmd_total) + atapi_cmd_completion(); + } + else { + if (state != IDE_DEV_DATA_WRITE) { + LOG_MSG("ide atapi warning: data write when device not in data_write state\n"); + return; + } + if (!(status & IDE_STATUS_DRQ)) { + LOG_MSG("ide atapi warning: data write with drq=0\n"); + return; + } + if ((sector_i+iolen) > sector_total) { + LOG_MSG("ide atapi warning: sector already full %lu / %lu\n",(unsigned long)sector_i,(unsigned long)sector_total); + return; + } + + if (iolen >= 4) { + host_writed(sector+sector_i,v); + sector_i += 4; + } + else if (iolen >= 2) { + host_writew(sector+sector_i,v); + sector_i += 2; + } + else if (iolen == 1) { + sector[sector_i++] = v; + } + + if (sector_i >= sector_total) + io_completion(); + } +} + +Bitu IDEATADevice::data_read(Bitu iolen) { + Bitu w = ~0u; + + if (state != IDE_DEV_DATA_READ) + return 0xFFFFUL; + + if (!(status & IDE_STATUS_DRQ)) { + LOG_MSG("IDE: Data read when DRQ=0\n"); + return 0xFFFFUL; + } + + if ((sector_i+iolen) > sector_total) { + LOG_MSG("ide ata warning: sector already read %lu / %lu\n",(unsigned long)sector_i,(unsigned long)sector_total); + return 0xFFFFUL; + } + + if (iolen >= 4) { + w = host_readd(sector+sector_i); + sector_i += 4; + } + else if (iolen >= 2) { + w = host_readw(sector+sector_i); + sector_i += 2; + } + /* NTS: Some MS-DOS CD-ROM drivers like OAKCDROM.SYS use byte-wide I/O for the initial identification */ + else if (iolen == 1) { + w = sector[sector_i++]; + } + + if (sector_i >= sector_total) + io_completion(); + + return w; +} + +void IDEATADevice::data_write(Bitu v,Bitu iolen) { + if (state != IDE_DEV_DATA_WRITE) { + LOG_MSG("ide ata warning: data write when device not in data_write state\n"); + return; + } + if (!(status & IDE_STATUS_DRQ)) { + LOG_MSG("ide ata warning: data write with drq=0\n"); + return; + } + if ((sector_i+iolen) > sector_total) { + LOG_MSG("ide ata warning: sector already full %lu / %lu\n",(unsigned long)sector_i,(unsigned long)sector_total); + return; + } + + if (iolen >= 4) { + host_writed(sector+sector_i,v); + sector_i += 4; + } + else if (iolen >= 2) { + host_writew(sector+sector_i,v); + sector_i += 2; + } + else if (iolen == 1) { + sector[sector_i++] = v; + } + + if (sector_i >= sector_total) + io_completion(); +} + +void IDEATAPICDROMDevice::prepare_read(Bitu offset,Bitu size) { + /* I/O must be WORD ALIGNED */ + assert((offset&1) == 0); +// assert((size&1) == 0); + + sector_i = offset; + sector_total = size; + assert(sector_i <= sector_total); + assert(sector_total <= sizeof(sector)); +} + +void IDEATAPICDROMDevice::prepare_write(Bitu offset,Bitu size) { + /* I/O must be WORD ALIGNED */ + assert((offset&1) == 0); +// assert((size&1) == 0); + + sector_i = offset; + sector_total = size; + assert(sector_i <= sector_total); + assert(sector_total <= sizeof(sector)); +} + +void IDEATADevice::prepare_write(Bitu offset,Bitu size) { + /* I/O must be WORD ALIGNED */ + assert((offset&1) == 0); +// assert((size&1) == 0); + + sector_i = offset; + sector_total = size; + assert(sector_i <= sector_total); + assert(sector_total <= sizeof(sector)); +} + +void IDEATADevice::prepare_read(Bitu offset,Bitu size) { + /* I/O must be WORD ALIGNED */ + assert((offset&1) == 0); +// assert((size&1) == 0); + + sector_i = offset; + sector_total = size; + assert(sector_i <= sector_total); + assert(sector_total <= sizeof(sector)); +} + +void IDEATAPICDROMDevice::generate_mmc_inquiry() { + Bitu i; + + /* IN RESPONSE TO ATAPI COMMAND 0x12: INQUIRY */ + memset(sector,0,36); + sector[0] = (0 << 5) | 5; /* Peripheral qualifier=0 device type=5 (CDROM) */ + sector[1] = 0x80; /* RMB=1 removable media */ + sector[3] = 0x21; + sector[4] = 36 - 5; /* additional length */ + + for (i=0;i < 8 && i < id_mmc_vendor_id.length();i++) + sector[i+8] = (unsigned char)id_mmc_vendor_id[i]; + for (;i < 8;i++) + sector[i+8] = ' '; + + for (i=0;i < 16 && i < id_mmc_product_id.length();i++) + sector[i+16] = (unsigned char)id_mmc_product_id[i]; + for (;i < 16;i++) + sector[i+16] = ' '; + + for (i=0;i < 4 && i < id_mmc_product_rev.length();i++) + sector[i+32] = (unsigned char)id_mmc_product_rev[i]; + for (;i < 4;i++) + sector[i+32] = ' '; +} + +void IDEATAPICDROMDevice::generate_identify_device() { + unsigned char csum; + Bitu i; + + /* IN RESPONSE TO IDENTIFY DEVICE (0xA1) + GENERATE 512-BYTE REPLY */ + memset(sector,0,512); + + host_writew(sector+(0*2),0x85C0U); /* ATAPI device, command set #5 (what the fuck does that mean?), removable, */ + + for (i=0;i < 20 && i < id_serial.length();i++) + sector[(i^1)+(10*2)] = (unsigned char)id_serial[i]; + for (;i < 20;i++) + sector[(i^1)+(10*2)] = ' '; + + for (i=0;i < 8 && i < id_firmware_rev.length();i++) + sector[(i^1)+(23*2)] = (unsigned char)id_firmware_rev[i]; + for (;i < 8;i++) + sector[(i^1)+(23*2)] = ' '; + + for (i=0;i < 40 && i < id_model.length();i++) + sector[(i^1)+(27*2)] = (unsigned char)id_model[i]; + for (;i < 40;i++) + sector[(i^1)+(27*2)] = ' '; + + host_writew(sector+(49*2), + 0x0800UL|/*IORDY supported*/ + 0x0200UL|/*must be one*/ + 0); + host_writew(sector+(50*2), + 0x4000UL); + host_writew(sector+(51*2), + 0x00F0UL); + host_writew(sector+(52*2), + 0x00F0UL); + host_writew(sector+(53*2), + 0x0006UL); + host_writew(sector+(64*2), /* PIO modes supported */ + 0x0003UL); + host_writew(sector+(67*2), /* PIO cycle time */ + 0x0078UL); + host_writew(sector+(68*2), /* PIO cycle time */ + 0x0078UL); + host_writew(sector+(80*2),0x007E); /* major version number. Here we say we support ATA-1 through ATA-8 */ + host_writew(sector+(81*2),0x0022); /* minor version */ + host_writew(sector+(82*2),0x4008); /* command set: NOP, DEVICE RESET[XXXXX], POWER MANAGEMENT */ + host_writew(sector+(83*2),0x0000); /* command set: LBA48[XXXX] */ + host_writew(sector+(85*2),0x4208); /* commands in 82 enabled */ + host_writew(sector+(86*2),0x0000); /* commands in 83 enabled */ + + /* ATA-8 integrity checksum */ + sector[510] = 0xA5; + csum = 0; for (i=0;i < 511;i++) csum += sector[i]; + sector[511] = 0 - csum; +} + +void IDEATADevice::generate_identify_device() { +// imageDisk *disk = getBIOSdisk(); + unsigned char csum; + uint64_t ptotal; + uint64_t total; + Bitu i; + + /* IN RESPONSE TO IDENTIFY DEVICE (0xEC) + GENERATE 512-BYTE REPLY */ + memset(sector,0,512); + + /* total disk capacity in sectors */ + total = sects * cyls * heads; + ptotal = phys_sects * phys_cyls * phys_heads; + + host_writew(sector+(0*2),0x0040); /* bit 6: 1=fixed disk */ + host_writew(sector+(1*2),phys_cyls); + host_writew(sector+(3*2),phys_heads); + host_writew(sector+(4*2),phys_sects * 512); /* unformatted bytes per track */ + host_writew(sector+(5*2),512); /* unformatted bytes per sector */ + host_writew(sector+(6*2),phys_sects); + + for (i=0;i < 20 && i < id_serial.length();i++) + sector[(i^1)+(10*2)] = (unsigned char)id_serial[i]; + for (;i < 20;i++) + sector[(i^1)+(10*2)] = ' '; + + host_writew(sector+(20*2),1); /* ATA-1: single-ported single sector buffer */ + host_writew(sector+(21*2),4); /* ATA-1: ECC bytes on read/write long */ + + for (i=0;i < 8 && i < id_firmware_rev.length();i++) + sector[(i^1)+(23*2)] = (unsigned char)id_firmware_rev[i]; + for (;i < 8;i++) + sector[(i^1)+(23*2)] = ' '; + + for (i=0;i < 40 && i < id_model.length();i++) + sector[(i^1)+(27*2)] = (unsigned char)id_model[i]; + for (;i < 40;i++) + sector[(i^1)+(27*2)] = ' '; + + if (multiple_sector_max != 0) + host_writew(sector+(47*2),0x80|multiple_sector_max); /* <- READ/WRITE MULTIPLE MAX SECTORS */ + + host_writew(sector+(48*2),0x0000); /* :0 0=we do not support doubleword (32-bit) PIO */ + host_writew(sector+(49*2),0x0A00); /* :13 0=Standby timer values managed by device */ + /* :11 1=IORDY supported */ + /* :10 0=IORDY not disabled */ + /* :9 1=LBA supported */ + /* :8 0=DMA not supported */ + host_writew(sector+(50*2),0x4000); /* FIXME: ??? */ + host_writew(sector+(51*2),0x00F0); /* PIO data transfer cycle timing mode */ + host_writew(sector+(52*2),0x00F0); /* DMA data transfer cycle timing mode */ + host_writew(sector+(53*2),0x0007); /* :2 1=the fields in word 88 are valid */ + /* :1 1=the fields in word (70:64) are valid */ + /* :0 1= ??? */ + host_writew(sector+(54*2),cyls); /* current cylinders */ + host_writew(sector+(55*2),heads); /* current heads */ + host_writew(sector+(56*2),sects); /* current sectors per track */ + host_writed(sector+(57*2),total); /* current capacity in sectors */ + + if (multiple_sector_count != 0) + host_writew(sector+(59*2),0x0100|multiple_sector_count); /* :8 multiple sector setting is valid */ + /* 7:0 current setting for number of log. sectors per DRQ of READ/WRITE MULTIPLE */ + + host_writed(sector+(60*2),ptotal); /* total user addressable sectors (LBA) */ + host_writew(sector+(62*2),0x0000); /* FIXME: ??? */ + host_writew(sector+(63*2),0x0000); /* :10 0=Multiword DMA mode 2 not selected */ + /* TODO: Basically, we don't do DMA. Fill out this comment */ + host_writew(sector+(64*2),0x0003); /* 7:0 PIO modes supported (FIXME ???) */ + host_writew(sector+(65*2),0x0000); /* FIXME: ??? */ + host_writew(sector+(66*2),0x0000); /* FIXME: ??? */ + host_writew(sector+(67*2),0x0078); /* FIXME: ??? */ + host_writew(sector+(68*2),0x0078); /* FIXME: ??? */ + host_writew(sector+(80*2),0x007E); /* major version number. Here we say we support ATA-1 through ATA-8 */ + host_writew(sector+(81*2),0x0022); /* minor version */ + host_writew(sector+(82*2),0x4208); /* command set: NOP, DEVICE RESET[XXXXX], POWER MANAGEMENT */ + host_writew(sector+(83*2),0x4000); /* command set: LBA48[XXXX] */ + host_writew(sector+(84*2),0x4000); /* FIXME: ??? */ + host_writew(sector+(85*2),0x4208); /* commands in 82 enabled */ + host_writew(sector+(86*2),0x4000); /* commands in 83 enabled */ + host_writew(sector+(87*2),0x4000); /* FIXME: ??? */ + host_writew(sector+(88*2),0x0000); /* FIXME: ??? */ + host_writew(sector+(93*3),0x0000); /* FIXME: ??? */ + + /* ATA-8 integrity checksum */ + sector[510] = 0xA5; + csum = 0; for (i=0;i < 511;i++) csum += sector[i]; + sector[511] = 0 - csum; +} + +IDEATADevice::IDEATADevice(IDEController *c,unsigned char bios_disk_index) : IDEDevice(c) { + this->bios_disk_index = bios_disk_index; + sector_i = sector_total = 0; + + headshr = 0; + type = IDE_TYPE_HDD; + id_serial = "8086"; + id_firmware_rev = "8086"; + id_model = "DOSBox IDE disk"; + multiple_sector_max = sizeof(sector) / 512; + multiple_sector_count = 1; + geo_translate = false; +} + +IDEATADevice::~IDEATADevice() { +} + +imageDisk *IDEATADevice::getBIOSdisk() { + if (bios_disk_index >= (2 + MAX_HDD_IMAGES)) return NULL; + return imageDiskList[bios_disk_index]; +} + +CDROM_Interface *IDEATAPICDROMDevice::getMSCDEXDrive() { + CDROM_Interface *cdrom=NULL; + + if (!GetMSCDEXDrive(drive_index,&cdrom)) + return NULL; + + return cdrom; +} + +void IDEATAPICDROMDevice::update_from_cdrom() { + CDROM_Interface *cdrom = getMSCDEXDrive(); + if (cdrom == NULL) { + LOG_MSG("WARNING: IDE update from CD-ROM failed, disk not available\n"); + return; + } +} + +void IDEATADevice::update_from_biosdisk() { + imageDisk *dsk = getBIOSdisk(); + if (dsk == NULL) { + LOG_MSG("WARNING: IDE update from BIOS disk failed, disk not available\n"); + return; + } + + headshr = 0; + geo_translate = false; + cyls = dsk->cylinders; + heads = dsk->heads; + sects = dsk->sectors; + + /* One additional correction: The disk image is probably using BIOS-style geometry + translation (such as C/H/S 1024/64/63) which is impossible given that the IDE + standard only allows up to 16 heads. So we have to translate the geometry. */ + while (heads > 16 && (heads & 1) == 0) { + cyls <<= 1U; + heads >>= 1U; + headshr++; + } + + /* If we can't divide the heads down, then pick a LBA-like mapping that is good enough. + * Note that if what we pick does not evenly map to the INT 13h geometry, and the partition + * contained within is not an LBA type FAT16/FAT32 partition, then Windows 95's IDE driver + * will ignore this device and fall back to using INT 13h. For user convenience we will + * print a warning to reminder the user of exactly that. */ + if (heads > 16) { + unsigned long tmp; + + geo_translate = true; + + tmp = heads * cyls * sects; + sects = 63; + heads = 16; + cyls = (tmp + ((63 * 16) - 1)) / (63 * 16); + LOG_MSG("WARNING: Unable to reduce heads to 16 and below\n"); + LOG_MSG("If at all possible, please consider using INT 13h geometry with a head\n"); + LOG_MSG("cound that is easier to map to the BIOS, like 240 heads or 128 heads/track.\n"); + LOG_MSG("Some OSes, such as Windows 95, will not enable their 32-bit IDE driver if\n"); + LOG_MSG("a clean mapping does not exist between IDE and BIOS geometry.\n"); + LOG_MSG("Mapping BIOS DISK C/H/S %u/%u/%u as IDE %u/%u/%u (non-straightforward mapping)\n", + (unsigned int)dsk->cylinders, + (unsigned int)dsk->heads, + (unsigned int)dsk->sectors, + (unsigned int)cyls, + (unsigned int)heads, + (unsigned int)sects); + } + else { + LOG_MSG("Mapping BIOS DISK C/H/S %u/%u/%u as IDE %u/%u/%u\n", + (unsigned int)dsk->cylinders, + (unsigned int)dsk->heads, + (unsigned int)dsk->sectors, + (unsigned int)cyls, + (unsigned int)heads, + (unsigned int)sects); + } + + phys_heads = heads; + phys_sects = sects; + phys_cyls = cyls; +} + +void IDE_Auto(signed char &index,bool &slave) { + IDEController *c; + unsigned int i; + + index = -1; + slave = false; + for (i=0;i < MAX_IDE_CONTROLLERS;i++) { + if ((c=idecontroller[i]) == NULL) continue; + index = (signed char)i; + + if (c->device[0] == NULL) { + slave = false; + break; + } + if (c->device[1] == NULL) { + slave = true; + break; + } + } +} + +/* drive_index = drive letter 0...A to 25...Z */ +void IDE_ATAPI_MediaChangeNotify(unsigned char drive_index) { + for (unsigned int ide=0;ide < MAX_IDE_CONTROLLERS;ide++) { + IDEController *c = idecontroller[ide]; + if (c == NULL) continue; + for (unsigned int ms=0;ms < 2;ms++) { + IDEDevice *dev = c->device[ms]; + if (dev == NULL) continue; + if (dev->type == IDE_TYPE_CDROM) { + IDEATAPICDROMDevice *atapi = (IDEATAPICDROMDevice*)dev; + if (drive_index == atapi->drive_index) { + LOG_MSG("IDE ATAPI acknowledge media change for drive %c\n",drive_index+'A'); + atapi->has_changed = true; + atapi->loading_mode = LOAD_INSERT_CD; + PIC_RemoveSpecificEvents(IDE_ATAPI_SpinDown,c->interface_index); + PIC_RemoveSpecificEvents(IDE_ATAPI_SpinUpComplete,c->interface_index); + PIC_RemoveSpecificEvents(IDE_ATAPI_CDInsertion,c->interface_index); + PIC_AddEvent(IDE_ATAPI_CDInsertion,atapi->cd_insertion_time/*ms*/,c->interface_index); + } + } + } + } +} + +/* drive_index = drive letter 0...A to 25...Z */ +void IDE_CDROM_Attach(signed char index,bool slave,unsigned char drive_index) { + IDEController *c; + IDEATAPICDROMDevice *dev; + + if (index < 0 || index >= MAX_IDE_CONTROLLERS) return; + c = idecontroller[index]; + if (c == NULL) return; + + if (c->device[slave?1:0] != NULL) { + LOG_MSG("IDE: Controller %u %s already taken\n",index,slave?"slave":"master"); + return; + } + + if (!GetMSCDEXDrive(drive_index,NULL)) { + LOG_MSG("IDE: Asked to attach CD-ROM that does not exist\n"); + return; + } + + dev = new IDEATAPICDROMDevice(c,drive_index); + if (dev == NULL) return; + dev->update_from_cdrom(); + c->device[slave?1:0] = (IDEDevice*)dev; +} + +/* drive_index = drive letter 0...A to 25...Z */ +void IDE_CDROM_Detach(unsigned char drive_index) { + for (int index = 0; index < MAX_IDE_CONTROLLERS; index++) { + IDEController *c = idecontroller[index]; + if (c) + for (int slave = 0; slave < 2; slave++) { + IDEATAPICDROMDevice *dev; + dev = dynamic_cast(c->device[slave]); + if (dev && dev->drive_index == drive_index) { + delete dev; + c->device[slave] = NULL; + } + } + } +} + +/* bios_disk_index = index into BIOS INT 13h disk array: imageDisk *imageDiskList[MAX_DISK_IMAGES]; */ +void IDE_Hard_Disk_Attach(signed char index,bool slave,unsigned char bios_disk_index/*not INT13h, the index into DOSBox's BIOS drive emulation*/) { + IDEController *c; + IDEATADevice *dev; + + if (index < 0 || index >= MAX_IDE_CONTROLLERS) return; + c = idecontroller[index]; + if (c == NULL) return; + + if (c->device[slave?1:0] != NULL) { + LOG_MSG("IDE: Controller %u %s already taken\n",index,slave?"slave":"master"); + return; + } + + if (imageDiskList[bios_disk_index] == NULL) { + LOG_MSG("IDE: Asked to attach bios disk that does not exist\n"); + return; + } + + dev = new IDEATADevice(c,bios_disk_index); + if (dev == NULL) return; + dev->update_from_biosdisk(); + c->device[slave?1:0] = (IDEDevice*)dev; +} + +/* bios_disk_index = index into BIOS INT 13h disk array: imageDisk *imageDiskList[MAX_DISK_IMAGES]; */ +void IDE_Hard_Disk_Detach(unsigned char bios_disk_index) { + for (int index = 0; index < MAX_IDE_CONTROLLERS; index++) { + IDEController *c = idecontroller[index]; + if (c) + for (int slave = 0; slave < 2; slave++) { + IDEATADevice *dev; + dev = dynamic_cast(c->device[slave]); + if (dev && dev->bios_disk_index == bios_disk_index) { + delete dev; + c->device[slave] = NULL; + } + } + } +} +static IDEController* GetIDEController(Bitu idx) { + if (idx >= MAX_IDE_CONTROLLERS) return NULL; + return idecontroller[idx]; +} + +static IDEDevice* GetIDESelectedDevice(IDEController *ide) { + if (ide == NULL) return NULL; + return ide->device[ide->select]; +} + +static bool IDE_CPU_Is_Vm86() { + return (cpu.pmode && ((GETFLAG_IOPLint13fakev86io && IDE_CPU_Is_Vm86()) { + /* Trigger I/O in virtual 8086 mode, where the OS can trap it and act on it. + * Windows 95 uses V86 traps to help "autodetect" what IDE drive and port the + * BIOS uses on INT 13h so that it's internal IDE driver can take over, which + * is the whole reason for this hack. */ + return CPU_ForceV86FakeIO_In(port,len); + } + else { + return ide_baseio_r(port,len); + } +} + +static void IDE_SelfIO_Out(IDEController *ide,Bitu port,Bitu val,Bitu len) { + if (ide->int13fakev86io && IDE_CPU_Is_Vm86()) { + /* Trigger I/O in virtual 8086 mode, where the OS can trap it and act on it. + * Windows 95 uses V86 traps to help "autodetect" what IDE drive and port the + * BIOS uses on INT 13h so that it's internal IDE driver can take over, which + * is the whole reason for this hack. */ + CPU_ForceV86FakeIO_Out(port,val,len); + } + else { + ide_baseio_w(port,val,len); + } +} + +/* INT 13h extensions */ +void IDE_EmuINT13DiskReadByBIOS_LBA(unsigned char disk,uint64_t lba) { + IDEController *ide; + IDEDevice *dev; + Bitu idx,ms; + + if (disk < 0x80) return; + if (lba >= (1ULL << 28ULL)) return; /* this code does not support LBA48 */ + + for (idx=0;idx < MAX_IDE_CONTROLLERS;idx++) { + ide = GetIDEController(idx); + if (ide == NULL) continue; + if (!ide->int13fakeio && !ide->int13fakev86io) continue; + + /* TODO: Print a warning message if the IDE controller is busy (debug/warning message) */ + + /* TODO: Force IDE state to readiness, abort command, etc. */ + + /* for master/slave device... */ + for (ms=0;ms < 2;ms++) { + dev = ide->device[ms]; + if (dev == NULL) continue; + + /* TODO: Print a warning message if the IDE device is busy or in the middle of a command */ + + /* TODO: Forcibly device-reset the IDE device */ + + /* Issue I/O to ourself to select drive */ + dev->faked_command = true; + IDE_SelfIO_In(ide,ide->base_io+7u,1); + IDE_SelfIO_Out(ide,ide->base_io+6u,0x00+(ms<<4u),1); + dev->faked_command = false; + + if (dev->type == IDE_TYPE_HDD) { + IDEATADevice *ata = (IDEATADevice*)dev; + static bool vm86_warned = false; +// static bool int13_fix_wrap_warned = false; + bool vm86 = IDE_CPU_Is_Vm86(); + + if ((ata->bios_disk_index-2) == (disk-0x80)) { +// imageDisk *dsk = ata->getBIOSdisk(); + + if (ide->int13fakev86io && vm86) { + dev->faked_command = true; + + /* we MUST clear interrupts. + * leaving them enabled causes Win95 (or DOSBox?) to recursively + * pagefault and DOSBox to crash. In any case it seems Win95's + * IDE driver assumes the BIOS INT 13h code will do this since + * it's customary for the BIOS to do it at some point, usually + * just before reading the sector data. */ + CPU_CLI(); + + /* We're in virtual 8086 mode and we're asked to fake I/O as if + * executing a BIOS subroutine. Some OS's like Windows 95 rely on + * executing INT 13h in virtual 8086 mode: on startup, the ESDI + * driver traps IDE ports and then executes INT 13h to watch what + * I/O ports it uses. It then uses that information to decide + * what IDE hard disk and controller corresponds to what DOS + * drive. So to get 32-bit disk access to work in Windows 95, + * we have to put on a good show to convince Windows 95 we're + * a legitimate BIOS INT 13h call doing it's job. */ + IDE_SelfIO_In(ide,ide->base_io+7u,1); /* dum de dum reading status */ + IDE_SelfIO_Out(ide,ide->base_io+6u,(ms<<4u)+0xE0u+(lba>>24u),1); /* drive and head */ + IDE_SelfIO_In(ide,ide->base_io+7u,1); /* dum de dum reading status */ + IDE_SelfIO_Out(ide,ide->base_io+2u,0x01,1); /* sector count */ + IDE_SelfIO_Out(ide,ide->base_io+3u,lba&0xFF,1); /* sector number */ + IDE_SelfIO_Out(ide,ide->base_io+4u,(lba>>8u)&0xFF,1); /* cylinder lo */ + IDE_SelfIO_Out(ide,ide->base_io+5u,(lba>>16u)&0xFF,1); /* cylinder hi */ + IDE_SelfIO_Out(ide,ide->base_io+6u,(ms<<4u)+0xE0+(lba>>24u),1); /* drive and head */ + IDE_SelfIO_In(ide,ide->base_io+7u,1); /* dum de dum reading status */ + IDE_SelfIO_Out(ide,ide->base_io+7u,0x20,1); /* issue READ */ + + do { + /* TODO: Timeout needed */ + unsigned int i = IDE_SelfIO_In(ide,ide->alt_io,1); + if ((i&0x80) == 0) break; + } while (1); + IDE_SelfIO_In(ide,ide->base_io+7u,1); + + /* for brevity assume it worked. we're here to bullshit Windows 95 after all */ + for (unsigned int i=0;i < 256;i++) + IDE_SelfIO_In(ide,ide->base_io+0u,2); /* 16-bit IDE data read */ + + /* one more */ + IDE_SelfIO_In(ide,ide->base_io+7u,1); /* dum de dum reading status */ + + /* assume IRQ happened and clear it */ + if (ide->IRQ >= 8) + IDE_SelfIO_Out(ide,0xA0,0x60u+(unsigned int)ide->IRQ-8u,1); /* specific EOI */ + else + IDE_SelfIO_Out(ide,0x20,0x60u+(unsigned int)ide->IRQ,1); /* specific EOI */ + + ata->abort_normal(); + dev->faked_command = false; + } + else { + /* hack IDE state as if a BIOS executing IDE disk routines. + * This is required if we want IDE emulation to work with Windows 3.11 Windows for Workgroups 32-bit disk access (WDCTRL), + * because the driver "tests" the controller by issuing INT 13h calls then reading back IDE registers to see if + * they match the C/H/S it requested */ + dev->feature = 0x00; /* clear error (WDCTRL test phase 5/C/13) */ + dev->count = 0x00; /* clear sector count (WDCTRL test phase 6/D/14) */ + dev->lba[0] = lba&0xFF; /* leave sector number the same (WDCTRL test phase 7/E/15) */ + dev->lba[1] = (lba>>8u)&0xFF; /* leave cylinder the same (WDCTRL test phase 8/F/16) */ + dev->lba[2] = (lba>>16u)&0xFF; /* ...ditto */ + ide->drivehead = dev->drivehead = 0xE0u | (ms<<4u) | (lba>>24u); /* drive head and master/slave (WDCTRL test phase 9/10/17) */ + dev->status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; /* status (WDCTRL test phase A/11/18) */ + dev->allow_writing = true; + + if (vm86 && !vm86_warned) { + LOG_MSG("IDE warning: INT 13h extensions read from virtual 8086 mode.\n"); + LOG_MSG(" If using Windows 95 OSR2, please set int13fakev86io=true for proper 32-bit disk access\n"); + vm86_warned = true; + } + } + + /* break out, we're done */ + idx = MAX_IDE_CONTROLLERS; + break; + } + } + } + } +} + +/* this is called after INT 13h AH=0x02 READ DISK to change IDE state to simulate the BIOS in action. + * this is needed for old "32-bit disk drivers" like WDCTRL in Windows 3.11 Windows for Workgroups, + * which issues INT 13h to read-test and then reads IDE registers to see if they match expectations */ +void IDE_EmuINT13DiskReadByBIOS(unsigned char disk,unsigned int cyl,unsigned int head,unsigned sect) { + IDEController *ide; + IDEDevice *dev; + Bitu idx,ms; + + if (disk < 0x80) return; + + for (idx=0;idx < MAX_IDE_CONTROLLERS;idx++) { + ide = GetIDEController(idx); + if (ide == NULL) continue; + if (!ide->int13fakeio && !ide->int13fakev86io) continue; + + /* TODO: Print a warning message if the IDE controller is busy (debug/warning message) */ + + /* TODO: Force IDE state to readiness, abort command, etc. */ + + /* for master/slave device... */ + for (ms=0;ms < 2;ms++) { + dev = ide->device[ms]; + if (dev == NULL) continue; + + /* TODO: Print a warning message if the IDE device is busy or in the middle of a command */ + + /* TODO: Forcibly device-reset the IDE device */ + + /* Issue I/O to ourself to select drive */ + dev->faked_command = true; + IDE_SelfIO_In(ide,ide->base_io+7u,1); + IDE_SelfIO_Out(ide,ide->base_io+6u,0x00u+(ms<<4u),1); + dev->faked_command = false; + + if (dev->type == IDE_TYPE_HDD) { + IDEATADevice *ata = (IDEATADevice*)dev; + static bool vm86_warned = false; + static bool int13_fix_wrap_warned = false; + bool vm86 = IDE_CPU_Is_Vm86(); + + if ((ata->bios_disk_index-2) == (disk-0x80)) { + imageDisk *dsk = ata->getBIOSdisk(); + + /* print warning if INT 13h is being called after the OS changed logical geometry */ + if (ata->sects != ata->phys_sects || ata->heads != ata->phys_heads || ata->cyls != ata->phys_cyls) + LOG_MSG("INT 13 WARNING: I/O issued on drive attached to IDE emulation with changed logical geometry!\n"); + + /* HACK: src/ints/bios_disk.cpp implementation doesn't correctly + * wrap sector numbers across tracks. it fullfills the read + * by counting sectors and reading from C,H,S+i which means + * that if the OS assumes the ability to read across track + * boundaries (as Windows 95 does) we will get invalid + * sector numbers, which in turn fouls up our emulation. + * + * Windows 95 OSR2 for example, will happily ask for 63 + * sectors starting at C/H/S 30/9/42 without regard for + * track boundaries. */ + if (sect > dsk->sectors) { + if (!int13_fix_wrap_warned) { + LOG_MSG("INT 13h implementation warning: we were given over-large sector number.\n"); + LOG_MSG("This is normally the fault of DOSBox INT 13h emulation that counts sectors\n"); + LOG_MSG("without consideration of track boundaries\n"); + int13_fix_wrap_warned = true; + } + + do { + sect -= dsk->sectors; + if ((++head) >= dsk->heads) { + head -= dsk->heads; + cyl++; + } + } while (sect > dsk->sectors); + } + + /* translate BIOS INT 13h geometry to IDE geometry */ + if (ata->headshr != 0 || ata->geo_translate) { + unsigned long lba; + + if (dsk == NULL) return; + lba = (head * dsk->sectors) + (cyl * dsk->sectors * dsk->heads) + sect - 1; + sect = (lba % ata->sects) + 1; + head = (lba / ata->sects) % ata->heads; + cyl = (lba / ata->sects / ata->heads); + } + + if (ide->int13fakev86io && vm86) { + dev->faked_command = true; + + /* we MUST clear interrupts. + * leaving them enabled causes Win95 (or DOSBox?) to recursively + * pagefault and DOSBox to crash. In any case it seems Win95's + * IDE driver assumes the BIOS INT 13h code will do this since + * it's customary for the BIOS to do it at some point, usually + * just before reading the sector data. */ + CPU_CLI(); + + /* We're in virtual 8086 mode and we're asked to fake I/O as if + * executing a BIOS subroutine. Some OS's like Windows 95 rely on + * executing INT 13h in virtual 8086 mode: on startup, the ESDI + * driver traps IDE ports and then executes INT 13h to watch what + * I/O ports it uses. It then uses that information to decide + * what IDE hard disk and controller corresponds to what DOS + * drive. So to get 32-bit disk access to work in Windows 95, + * we have to put on a good show to convince Windows 95 we're + * a legitimate BIOS INT 13h call doing it's job. */ + IDE_SelfIO_In(ide,ide->base_io+7u,1); /* dum de dum reading status */ + IDE_SelfIO_Out(ide,ide->base_io+6u,(ms<<4u)+0xA0u+head,1); /* drive and head */ + IDE_SelfIO_In(ide,ide->base_io+7u,1); /* dum de dum reading status */ + IDE_SelfIO_Out(ide,ide->base_io+2u,0x01,1); /* sector count */ + IDE_SelfIO_Out(ide,ide->base_io+3u,sect,1); /* sector number */ + IDE_SelfIO_Out(ide,ide->base_io+4u,cyl&0xFF,1); /* cylinder lo */ + IDE_SelfIO_Out(ide,ide->base_io+5u,(cyl>>8u)&0xFF,1); /* cylinder hi */ + IDE_SelfIO_Out(ide,ide->base_io+6u,(ms<<4u)+0xA0u+head,1); /* drive and head */ + IDE_SelfIO_In(ide,ide->base_io+7u,1); /* dum de dum reading status */ + IDE_SelfIO_Out(ide,ide->base_io+7u,0x20u,1); /* issue READ */ + + do { + /* TODO: Timeout needed */ + unsigned int i = IDE_SelfIO_In(ide,ide->alt_io,1); + if ((i&0x80) == 0) break; + } while (1); + IDE_SelfIO_In(ide,ide->base_io+7u,1); + + /* for brevity assume it worked. we're here to bullshit Windows 95 after all */ + for (unsigned int i=0;i < 256;i++) + IDE_SelfIO_In(ide,ide->base_io+0,2); /* 16-bit IDE data read */ + + /* one more */ + IDE_SelfIO_In(ide,ide->base_io+7u,1); /* dum de dum reading status */ + + /* assume IRQ happened and clear it */ + if (ide->IRQ >= 8) + IDE_SelfIO_Out(ide,0xA0,0x60u+(unsigned int)ide->IRQ-8u,1); /* specific EOI */ + else + IDE_SelfIO_Out(ide,0x20,0x60u+(unsigned int)ide->IRQ,1); /* specific EOI */ + + ata->abort_normal(); + dev->faked_command = false; + } + else { + /* hack IDE state as if a BIOS executing IDE disk routines. + * This is required if we want IDE emulation to work with Windows 3.11 Windows for Workgroups 32-bit disk access (WDCTRL), + * because the driver "tests" the controller by issuing INT 13h calls then reading back IDE registers to see if + * they match the C/H/S it requested */ + dev->feature = 0x00; /* clear error (WDCTRL test phase 5/C/13) */ + dev->count = 0x00; /* clear sector count (WDCTRL test phase 6/D/14) */ + dev->lba[0] = sect; /* leave sector number the same (WDCTRL test phase 7/E/15) */ + dev->lba[1] = cyl; /* leave cylinder the same (WDCTRL test phase 8/F/16) */ + dev->lba[2] = cyl >> 8u; /* ...ditto */ + ide->drivehead = dev->drivehead = 0xA0u | (ms<<4u) | head; /* drive head and master/slave (WDCTRL test phase 9/10/17) */ + dev->status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; /* status (WDCTRL test phase A/11/18) */ + dev->allow_writing = true; + + if (vm86 && !vm86_warned) { + LOG_MSG("IDE warning: INT 13h read from virtual 8086 mode.\n"); + LOG_MSG(" If using Windows 95, please set int13fakev86io=true for proper 32-bit disk access\n"); + vm86_warned = true; + } + } + + /* break out, we're done */ + idx = MAX_IDE_CONTROLLERS; + break; + } + } + } + } +} + +/* this is called by src/ints/bios_disk.cpp whenever INT 13h AH=0x00 is called on a hard disk. + * this gives us a chance to update IDE state as if the BIOS had gone through with a full disk reset as requested. */ +void IDE_ResetDiskByBIOS(unsigned char disk) { + IDEController *ide; + IDEDevice *dev; + Bitu idx,ms; + + if (disk < 0x80) return; + + for (idx=0;idx < MAX_IDE_CONTROLLERS;idx++) { + ide = GetIDEController(idx); + if (ide == NULL) continue; + if (!ide->int13fakeio && !ide->int13fakev86io) continue; + + /* TODO: Print a warning message if the IDE controller is busy (debug/warning message) */ + + /* TODO: Force IDE state to readiness, abort command, etc. */ + + /* for master/slave device... */ + for (ms=0;ms < 2;ms++) { + dev = ide->device[ms]; + if (dev == NULL) continue; + + /* TODO: Print a warning message if the IDE device is busy or in the middle of a command */ + + /* TODO: Forcibly device-reset the IDE device */ + + /* Issue I/O to ourself to select drive */ + IDE_SelfIO_In(ide,ide->base_io+7u,1); + IDE_SelfIO_Out(ide,ide->base_io+6u,0x00+(ms<<4u),1); + + /* TODO: Forcibly device-reset the IDE device */ + + if (dev->type == IDE_TYPE_HDD) { + IDEATADevice *ata = (IDEATADevice*)dev; + + if ((ata->bios_disk_index-2) == (disk-0x80)) { + LOG_MSG("IDE %d%c reset by BIOS disk 0x%02x\n", + (unsigned int)(idx+1), + ms?'s':'m', + (unsigned int)disk); + + if (ide->int13fakev86io && IDE_CPU_Is_Vm86()) { + /* issue the DEVICE RESET command */ + IDE_SelfIO_In(ide,ide->base_io+7u,1); + IDE_SelfIO_Out(ide,ide->base_io+7u,0x08,1); + + IDE_SelfIO_In(ide,ide->base_io+7u,1); + + /* assume IRQ happened and clear it */ + if (ide->IRQ >= 8) + IDE_SelfIO_Out(ide,0xA0,0x60u+(unsigned int)ide->IRQ-8u,1); /* specific EOI */ + else + IDE_SelfIO_Out(ide,0x20,0x60u+(unsigned int)ide->IRQ,1); /* specific EOI */ + } + else { + /* Windows 3.1 WDCTRL needs this, or else, it will read the + * status register and see something other than DRIVE_READY|SEEK_COMPLETE */ + dev->writecommand(0x08); + + /* and then immediately clear the IRQ */ + ide->lower_irq(); + } + } + } + } + } +} + +static void IDE_DelayedCommand(Bitu idx/*which IDE controller*/) { + IDEDevice *dev = GetIDESelectedDevice(GetIDEController(idx)); + if (dev == NULL) return; + + if (dev->type == IDE_TYPE_HDD) { + IDEATADevice *ata = (IDEATADevice*)dev; + uint32_t sectorn = 0;/* FIXME: expand to uint64_t when adding LBA48 emulation */ + unsigned int sectcount; + imageDisk *disk; +// int i; + + switch (dev->command) { + case 0x30:/* WRITE SECTOR */ + disk = ata->getBIOSdisk(); + if (disk == NULL) { + LOG_MSG("ATA READ fail, bios disk N/A\n"); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + + sectcount = ata->count & 0xFF; + if (sectcount == 0) sectcount = 256; + if (drivehead_is_lba(ata->drivehead)) { + /* LBA */ + sectorn = ((ata->drivehead & 0xFu) << 24u) | (unsigned int)ata->lba[0] | + ((unsigned int)ata->lba[1] << 8u) | + ((unsigned int)ata->lba[2] << 16u); + } + else { + /* C/H/S */ + if (ata->lba[0] == 0) { + LOG_MSG("ATA sector 0 does not exist\n"); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + else if ((unsigned int)(ata->drivehead & 0xFu) >= (unsigned int)ata->heads || + (unsigned int)ata->lba[0] > (unsigned int)ata->sects || + (unsigned int)(ata->lba[1] | (ata->lba[2] << 8u)) >= (unsigned int)ata->cyls) { + LOG_MSG("C/H/S %u/%u/%u out of bounds %u/%u/%u\n", + (unsigned int)(ata->lba[1] | (ata->lba[2] << 8u)), + (unsigned int)(ata->drivehead&0xFu), + (unsigned int)(ata->lba[0]), + (unsigned int)ata->cyls, + (unsigned int)ata->heads, + (unsigned int)ata->sects); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + + sectorn = ((ata->drivehead & 0xF) * ata->sects) + + (((unsigned int)ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)) * ata->sects * ata->heads) + + ((unsigned int)ata->lba[0] - 1u); + } + + if (disk->Write_AbsoluteSector(sectorn, ata->sector) != 0) { + LOG_MSG("Failed to write sector\n"); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + + /* NTS: the way this command works is that the drive writes ONE sector, then fires the IRQ + and lets the host read it, then reads another sector, fires the IRQ, etc. One + IRQ signal per sector. We emulate that here by adding another event to trigger this + call unless the sector count has just dwindled to zero, then we let it stop. */ + if ((ata->count&0xFF) == 1) { + /* end of the transfer */ + ata->count = 0; + ata->status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + dev->controller->raise_irq(); + ata->state = IDE_DEV_READY; + ata->allow_writing = true; + return; + } + else if ((ata->count&0xFF) == 0) ata->count = 255; + else ata->count--; + ata->progress_count++; + + if (!ata->increment_current_address()) { + LOG_MSG("READ advance error\n"); + ata->abort_error(); + return; + } + + /* begin another sector */ + dev->state = IDE_DEV_DATA_WRITE; + dev->status = IDE_STATUS_DRQ|IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + ata->prepare_write(0,512); + dev->controller->raise_irq(); + break; + + case 0x20:/* READ SECTOR */ + disk = ata->getBIOSdisk(); + if (disk == NULL) { + LOG_MSG("ATA READ fail, bios disk N/A\n"); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + + sectcount = ata->count & 0xFF; + if (sectcount == 0) sectcount = 256; + if (drivehead_is_lba(ata->drivehead)) { + /* LBA */ + sectorn = (((unsigned int)ata->drivehead & 0xFu) << 24u) | (unsigned int)ata->lba[0] | + ((unsigned int)ata->lba[1] << 8u) | + ((unsigned int)ata->lba[2] << 16u); + } + else { + /* C/H/S */ + if (ata->lba[0] == 0) { + LOG_MSG("WARNING C/H/S access mode and sector==0\n"); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + else if ((unsigned int)(ata->drivehead & 0xF) >= (unsigned int)ata->heads || + (unsigned int)ata->lba[0] > (unsigned int)ata->sects || + (unsigned int)(ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)) >= (unsigned int)ata->cyls) { + LOG_MSG("C/H/S %u/%u/%u out of bounds %u/%u/%u\n", + (unsigned int)(ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)), + (unsigned int)(ata->drivehead&0xF), + (unsigned int)ata->lba[0], + (unsigned int)ata->cyls, + (unsigned int)ata->heads, + (unsigned int)ata->sects); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + + sectorn = ((ata->drivehead & 0xFu) * ata->sects) + + (((unsigned int)ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)) * ata->sects * ata->heads) + + ((unsigned int)ata->lba[0] - 1u); + } + + if (disk->Read_AbsoluteSector(sectorn, ata->sector) != 0) { + LOG_MSG("ATA read failed\n"); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + + /* NTS: the way this command works is that the drive reads ONE sector, then fires the IRQ + and lets the host read it, then reads another sector, fires the IRQ, etc. One + IRQ signal per sector. We emulate that here by adding another event to trigger this + call unless the sector count has just dwindled to zero, then we let it stop. */ + /* NTS: The sector advance + count decrement is done in the I/O completion function */ + dev->state = IDE_DEV_DATA_READ; + dev->status = IDE_STATUS_DRQ|IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + ata->prepare_read(0,512); + dev->controller->raise_irq(); + break; + + case 0x40:/* READ SECTOR VERIFY WITH RETRY */ + case 0x41: /* READ SECTOR VERIFY WITHOUT RETRY */ + disk = ata->getBIOSdisk(); + if (disk == NULL) { + LOG_MSG("ATA READ fail, bios disk N/A\n"); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + + sectcount = ata->count & 0xFF; + if (sectcount == 0) sectcount = 256; + if (drivehead_is_lba(ata->drivehead)) { + /* LBA */ + sectorn = (((unsigned int)ata->drivehead & 0xFu) << 24u) | (unsigned int)ata->lba[0] | + ((unsigned int)ata->lba[1] << 8u) | + ((unsigned int)ata->lba[2] << 16u); + } + else { + /* C/H/S */ + if (ata->lba[0] == 0) { + LOG_MSG("WARNING C/H/S access mode and sector==0\n"); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + else if ((unsigned int)(ata->drivehead & 0xF) >= (unsigned int)ata->heads || + (unsigned int)ata->lba[0] > (unsigned int)ata->sects || + (unsigned int)(ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)) >= (unsigned int)ata->cyls) { + LOG_MSG("C/H/S %u/%u/%u out of bounds %u/%u/%u\n", + (unsigned int)(ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)), + (unsigned int)(ata->drivehead&0xFu), + (unsigned int)ata->lba[0], + (unsigned int)ata->cyls, + (unsigned int)ata->heads, + (unsigned int)ata->sects); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + + sectorn = (((unsigned int)ata->drivehead & 0xFu) * ata->sects) + + (((unsigned int)ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)) * ata->sects * ata->heads) + + ((unsigned int)ata->lba[0] - 1u); + } + + if (disk->Read_AbsoluteSector(sectorn, ata->sector) != 0) { + LOG_MSG("ATA read failed\n"); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + + if ((ata->count&0xFF) == 1) { + /* end of the transfer */ + ata->count = 0; + ata->status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + dev->controller->raise_irq(); + ata->state = IDE_DEV_READY; + ata->allow_writing = true; + return; + } + else if ((ata->count&0xFF) == 0) ata->count = 255; + else ata->count--; + ata->progress_count++; + + if (!ata->increment_current_address()) { + LOG_MSG("READ advance error\n"); + ata->abort_error(); + return; + } + + ata->state = IDE_DEV_BUSY; + ata->status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,0.00001/*ms*/,dev->controller->interface_index); + break; + + case 0xC4:/* READ MULTIPLE */ + disk = ata->getBIOSdisk(); + if (disk == NULL) { + LOG_MSG("ATA READ fail, bios disk N/A\n"); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + + sectcount = ata->count & 0xFF; + if (sectcount == 0) sectcount = 256; + if (drivehead_is_lba(ata->drivehead)) { + /* LBA */ + sectorn = (((unsigned int)ata->drivehead & 0xFu) << 24u) | (unsigned int)ata->lba[0] | + ((unsigned int)ata->lba[1] << 8u) | + ((unsigned int)ata->lba[2] << 16u); + } + else { + /* C/H/S */ + if (ata->lba[0] == 0) { + LOG_MSG("WARNING C/H/S access mode and sector==0\n"); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + else if ((unsigned int)(ata->drivehead & 0xF) >= (unsigned int)ata->heads || + (unsigned int)ata->lba[0] > (unsigned int)ata->sects || + (unsigned int)(ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)) >= (unsigned int)ata->cyls) { + LOG_MSG("C/H/S %u/%u/%u out of bounds %u/%u/%u\n", + (unsigned int)(ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)), + (unsigned int)(ata->drivehead&0xF), + (unsigned int)ata->lba[0], + (unsigned int)ata->cyls, + (unsigned int)ata->heads, + (unsigned int)ata->sects); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + + sectorn = ((ata->drivehead & 0xF) * ata->sects) + + (((unsigned int)ata->lba[1] | ((unsigned int)ata->lba[2] << 8u)) * ata->sects * ata->heads) + + ((unsigned int)ata->lba[0] - 1); + } + + if ((512*ata->multiple_sector_count) > sizeof(ata->sector)) + E_Exit("SECTOR OVERFLOW"); + + for (unsigned int cc=0;cc < MIN((Bitu)ata->multiple_sector_count,(Bitu)sectcount);cc++) { + /* it would be great if the disk object had a "read multiple sectors" member function */ + if (disk->Read_AbsoluteSector(sectorn+cc, ata->sector+(cc*512)) != 0) { + LOG_MSG("ATA read failed\n"); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + } + + /* NTS: the way this command works is that the drive reads ONE sector, then fires the IRQ + and lets the host read it, then reads another sector, fires the IRQ, etc. One + IRQ signal per sector. We emulate that here by adding another event to trigger this + call unless the sector count has just dwindled to zero, then we let it stop. */ + /* NTS: The sector advance + count decrement is done in the I/O completion function */ + dev->state = IDE_DEV_DATA_READ; + dev->status = IDE_STATUS_DRQ|IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + ata->prepare_read(0,512*MIN((Bitu)ata->multiple_sector_count,(Bitu)sectcount)); + dev->controller->raise_irq(); + break; + + case 0xC5:/* WRITE MULTIPLE */ + disk = ata->getBIOSdisk(); + if (disk == NULL) { + LOG_MSG("ATA READ fail, bios disk N/A\n"); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + + sectcount = ata->count & 0xFF; + if (sectcount == 0) sectcount = 256; + if (drivehead_is_lba(ata->drivehead)) { + /* LBA */ + sectorn = (((unsigned int)ata->drivehead & 0xF) << 24) | (unsigned int)ata->lba[0] | + ((unsigned int)ata->lba[1] << 8) | + ((unsigned int)ata->lba[2] << 16); + } + else { + /* C/H/S */ + if (ata->lba[0] == 0) { + LOG_MSG("ATA sector 0 does not exist\n"); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + else if ((unsigned int)(ata->drivehead & 0xF) >= (unsigned int)ata->heads || + (unsigned int)ata->lba[0] > (unsigned int)ata->sects || + (unsigned int)(ata->lba[1] | ((unsigned int)ata->lba[2] << 8)) >= (unsigned int)ata->cyls) { + LOG_MSG("C/H/S %u/%u/%u out of bounds %u/%u/%u\n", + (unsigned int)(ata->lba[1] | ((unsigned int)ata->lba[2] << 8)), + (unsigned int)(ata->drivehead&0xF), + (unsigned int)ata->lba[0], + (unsigned int)ata->cyls, + (unsigned int)ata->heads, + (unsigned int)ata->sects); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + + sectorn = ((unsigned int)(ata->drivehead & 0xF) * ata->sects) + + (((unsigned int)ata->lba[1] | ((unsigned int)ata->lba[2] << 8)) * ata->sects * ata->heads) + + ((unsigned int)ata->lba[0] - 1); + } + + for (unsigned int cc=0;cc < MIN((Bitu)ata->multiple_sector_count,(Bitu)sectcount);cc++) { + /* it would be great if the disk object had a "write multiple sectors" member function */ + if (disk->Write_AbsoluteSector(sectorn+cc, ata->sector+(cc*512)) != 0) { + LOG_MSG("Failed to write sector\n"); + ata->abort_error(); + dev->controller->raise_irq(); + return; + } + } + + for (unsigned int cc=0;cc < MIN((Bitu)ata->multiple_sector_count,(Bitu)sectcount);cc++) { + if ((ata->count&0xFF) == 1) { + /* end of the transfer */ + ata->count = 0; + ata->status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + dev->controller->raise_irq(); + ata->state = IDE_DEV_READY; + ata->allow_writing = true; + return; + } + else if ((ata->count&0xFF) == 0) ata->count = 255; + else ata->count--; + ata->progress_count++; + + if (!ata->increment_current_address()) { + LOG_MSG("READ advance error\n"); + ata->abort_error(); + return; + } + } + + /* begin another sector */ + sectcount = ata->count & 0xFF; + if (sectcount == 0) sectcount = 256; + dev->state = IDE_DEV_DATA_WRITE; + dev->status = IDE_STATUS_DRQ|IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + ata->prepare_write(0,512*MIN((Bitu)ata->multiple_sector_count,(Bitu)sectcount)); + dev->controller->raise_irq(); + break; + + case 0xEC:/*IDENTIFY DEVICE (CONTINUED) */ + dev->state = IDE_DEV_DATA_READ; + dev->status = IDE_STATUS_DRQ|IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + ata->generate_identify_device(); + ata->prepare_read(0,512); + dev->count = 0x01; + dev->lba[0] = 0x00; + dev->feature = 0x00; + dev->lba[1] = 0x00; + dev->lba[2] = 0x00; + dev->controller->raise_irq(); + break; + default: + LOG_MSG("Unknown delayed IDE/ATA command\n"); + dev->abort_error(); + dev->controller->raise_irq(); + break; + } + } + else if (dev->type == IDE_TYPE_CDROM) { + IDEATAPICDROMDevice *atapi = (IDEATAPICDROMDevice*)dev; + + if (dev->state == IDE_DEV_ATAPI_BUSY) { + switch (dev->command) { + case 0xA0:/*ATAPI PACKET*/ + atapi->on_atapi_busy_time(); + break; + default: + LOG_MSG("Unknown delayed IDE/ATAPI busy wait command\n"); + dev->abort_error(); + dev->controller->raise_irq(); + break; + } + } + else { + switch (dev->command) { + case 0xA0:/*ATAPI PACKET*/ + dev->state = IDE_DEV_ATAPI_PACKET_COMMAND; + dev->status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE|IDE_STATUS_DRQ; + dev->count = 0x01; /* input/output == 0, command/data == 1 */ + atapi->atapi_cmd_total = 12; /* NTS: do NOT raise IRQ */ + atapi->atapi_cmd_i = 0; + break; + case 0xA1:/*IDENTIFY PACKET DEVICE (CONTINUED) */ + dev->state = IDE_DEV_DATA_READ; + dev->status = IDE_STATUS_DRQ|IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + atapi->generate_identify_device(); + atapi->prepare_read(0,512); + dev->controller->raise_irq(); + break; + default: + LOG_MSG("Unknown delayed IDE/ATAPI command\n"); + dev->abort_error(); + dev->controller->raise_irq(); + break; + } + } + } + else { + LOG_MSG("Unknown delayed command\n"); + dev->abort_error(); + dev->controller->raise_irq(); + } +} + +void IDEController::raise_irq() { + irq_pending = true; + if (IRQ >= 0 && interrupt_enable) PIC_ActivateIRQ((unsigned int)IRQ); +} + +void IDEController::lower_irq() { + irq_pending = false; + if (IRQ >= 0) PIC_DeActivateIRQ((unsigned int)IRQ); +} + +IDEController *match_ide_controller(Bitu port) { + unsigned int i; + + for (i=0;i < MAX_IDE_CONTROLLERS;i++) { + IDEController *ide = idecontroller[i]; + if (ide == NULL) continue; + if (ide->base_io != 0U && ide->base_io == (port&0xFFF8U)) return ide; + if (ide->alt_io != 0U && ide->alt_io == (port&0xFFFEU)) return ide; + } + + return NULL; +} + +Bitu IDEDevice::data_read(Bitu iolen) { + (void)iolen;//UNUSED + return 0xAAAAU; +} + +void IDEDevice::data_write(Bitu v,Bitu iolen) { + (void)iolen;//UNUSED + (void)v;//UNUSED +} + +IDEDevice::IDEDevice(IDEController *c) { + type = IDE_TYPE_NONE; + status = 0x00; + controller = c; + asleep = false; + motor_on = true; + allow_writing = true; + state = IDE_DEV_READY; + feature = count = lba[0] = lba[1] = lba[2] = command = drivehead = 0; + + faked_command = false; + ide_select_delay = 0.5; /* 500us */ + ide_spinup_delay = 3000; /* 3 seconds */ + ide_spindown_delay = 1000; /* 1 second */ + ide_identify_command_delay = 0.01; /* 10us */ +} + +/* IDE controller -> upon writing bit 2 of alt (0x3F6) */ +void IDEDevice::host_reset_complete() { + status = 0x00; + asleep = false; + allow_writing = true; + state = IDE_DEV_READY; +} + +void IDEDevice::host_reset_begin() { + status = 0xFF; + asleep = false; + allow_writing = true; + state = IDE_DEV_BUSY; +} + +IDEDevice::~IDEDevice() { +} + +void IDEDevice::abort_silent() { + assert(controller != NULL); + + /* a command was written while another is in progress */ + state = IDE_DEV_READY; + allow_writing = true; + command = 0x00; + status = IDE_STATUS_ERROR | IDE_STATUS_DRIVE_READY | IDE_STATUS_DRIVE_SEEK_COMPLETE; +} + +void IDEDevice::abort_error() { + assert(controller != NULL); + LOG_MSG("IDE abort dh=0x%02x with error on 0x%03x\n",drivehead,controller->base_io); + + /* a command was written while another is in progress */ + state = IDE_DEV_READY; + allow_writing = true; + command = 0x00; + status = IDE_STATUS_ERROR | IDE_STATUS_DRIVE_READY | IDE_STATUS_DRIVE_SEEK_COMPLETE; +} + +void IDEDevice::abort_normal() { + /* a command was written while another is in progress */ + state = IDE_DEV_READY; + allow_writing = true; + command = 0x00; + status = IDE_STATUS_DRIVE_READY | IDE_STATUS_DRIVE_SEEK_COMPLETE; +} + +void IDEDevice::interface_wakeup() { + if (asleep) { + asleep = false; + } +} + +bool IDEDevice::command_interruption_ok(uint8_t cmd) { + /* apparently this is OK, if the Linux kernel is doing it: + * writing the same command byte as the one in progress, OR, issuing + * Device Reset while another command is waiting for data read/write */ + if (cmd == command) return true; + if (state != IDE_DEV_READY && state != IDE_DEV_BUSY && cmd == 0x08) { + LOG_MSG("Device reset while another (%02x) is in progress (state=%u). Aborting current command to begin another\n",command,state); + abort_silent(); + return true; + } + + if (state != IDE_DEV_READY) { + LOG_MSG("Command %02x written while another (%02x) is in progress (state=%u). Aborting current command\n",cmd,command,state); + abort_error(); + return false; + } + + return true; +} + +void IDEDevice::writecommand(uint8_t cmd) { + if (!command_interruption_ok(cmd)) + return; + + /* if the drive is asleep, then writing a command wakes it up */ + interface_wakeup(); + + /* drive is ready to accept command */ + switch (cmd) { + default: + LOG_MSG("Unknown IDE command %02X\n",cmd); + abort_error(); + break; + } +} + +void IDEATAPICDROMDevice::writecommand(uint8_t cmd) { + if (!command_interruption_ok(cmd)) + return; + + /* if the drive is asleep, then writing a command wakes it up */ + interface_wakeup(); + + /* drive is ready to accept command */ + allow_writing = false; + command = cmd; + switch (cmd) { + case 0x08: /* DEVICE RESET */ + status = 0x00; + drivehead &= 0x10; controller->drivehead = drivehead; + count = 0x01; + lba[0] = 0x01; + feature = 0x01; + lba[1] = 0x14; /* <- magic ATAPI identification */ + lba[2] = 0xEB; + /* NTS: Testing suggests that ATAPI devices do NOT trigger an IRQ on receipt of this command */ + allow_writing = true; + break; + case 0x20: /* READ SECTOR */ + abort_normal(); + status = IDE_STATUS_ERROR|IDE_STATUS_DRIVE_READY; + drivehead &= 0x30; controller->drivehead = drivehead; + count = 0x01; + lba[0] = 0x01; + feature = 0x04; /* abort */ + lba[1] = 0x14; /* <- magic ATAPI identification */ + lba[2] = 0xEB; + controller->raise_irq(); + allow_writing = true; + break; + case 0xA0: /* ATAPI PACKET */ + if (feature & 1) { + /* this code does not support DMA packet commands */ + LOG_MSG("Attempted DMA transfer\n"); + abort_error(); + count = 0x03; /* no more data (command/data=1, input/output=1) */ + feature = 0xF4; + controller->raise_irq(); + } + else { + state = IDE_DEV_BUSY; + status = IDE_STATUS_BUSY; + atapi_to_host = (feature >> 2) & 1; /* 0=to device 1=to host */ + host_maximum_byte_count = ((unsigned int)lba[2] << 8) + (unsigned int)lba[1]; /* LBA field bits 23:8 are byte count */ + if (host_maximum_byte_count == 0) host_maximum_byte_count = 0x10000UL; + PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 0.25)/*ms*/,controller->interface_index); + } + break; + case 0xA1: /* IDENTIFY PACKET DEVICE */ + state = IDE_DEV_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : ide_identify_command_delay),controller->interface_index); + break; + case 0xEC: /* IDENTIFY DEVICE */ + /* "devices that implement the PACKET command set shall post command aborted and place PACKET command feature + set in the appropriate fields". We have to do this. Unlike OAKCDROM.SYS Windows 95 appears to autodetect + IDE devices by what they do when they're sent command 0xEC out of the blue---Microsoft didn't write their + IDE drivers to use command 0x08 DEVICE RESET. */ + abort_normal(); + status = IDE_STATUS_ERROR|IDE_STATUS_DRIVE_READY; + drivehead &= 0x30; controller->drivehead = drivehead; + count = 0x01; + lba[0] = 0x01; + feature = 0x04; /* abort */ + lba[1] = 0x14; /* <- magic ATAPI identification */ + lba[2] = 0xEB; + controller->raise_irq(); + allow_writing = true; + break; + default: + LOG_MSG("Unknown IDE/ATAPI command %02X\n",cmd); + abort_error(); + allow_writing = true; + count = 0x03; /* no more data (command/data=1, input/output=1) */ + feature = 0xF4; + controller->raise_irq(); + break; + } +} + +void IDEATADevice::writecommand(uint8_t cmd) { + if (!command_interruption_ok(cmd)) + return; + + if (!faked_command) { + if (drivehead_is_lba(drivehead)) { + uint64_t n; + + n = ((unsigned int)(drivehead&0xF)<<24)+((unsigned int)lba[2]<<16)+((unsigned int)lba[1]<<8)+(unsigned int)lba[0]; + LOG_MSG("IDE ATA command %02x dh=0x%02x count=0x%02x lba=%07llx/%07llx\n",cmd, + drivehead,count,(unsigned long long)n, + (unsigned long long)(phys_sects * phys_cyls * phys_heads)); + } + else { + LOG_MSG("IDE ATA command %02x dh=0x%02x count=0x%02x chs=%02x/%02x/%02x\n",cmd, + drivehead,count,((unsigned int)lba[2]<<8)+(unsigned int)lba[1],(unsigned int)(drivehead&0xF),(unsigned int)lba[0]); + } + + LOG(LOG_SB,LOG_NORMAL)("IDE ATA command %02x",cmd); + } + + /* if the drive is asleep, then writing a command wakes it up */ + interface_wakeup(); + + /* FIXME: OAKCDROM.SYS is sending the hard disk command 0xA0 (ATAPI packet) for some reason. Why? */ + + /* drive is ready to accept command */ + allow_writing = false; + command = cmd; + switch (cmd) { + case 0x00: /* NOP */ + feature = 0x04; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_ERROR; + controller->raise_irq(); + allow_writing = true; + break; + case 0x08: /* DEVICE RESET */ + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + drivehead &= 0x10; controller->drivehead = drivehead; + count = 0x01; lba[0] = 0x01; feature = 0x00; + lba[1] = lba[2] = 0; + /* NTS: Testing suggests that ATA hard drives DO fire an IRQ at this stage. + In fact, Windows 95 won't detect hard drives that don't fire an IRQ in desponse */ + controller->raise_irq(); + allow_writing = true; + break; + case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: /* RECALIBRATE (1xh) */ + case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: case 0x1F: + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + /* "if the command is executed in CHS mode, then ... sector number register shall be 1. + * if executed in LAB mode, then ... sector number register shall be 0" */ + if (drivehead_is_lba(drivehead)) lba[0] = 0x00; + else lba[0] = 0x01; + drivehead &= 0x10; controller->drivehead = drivehead; + lba[1] = lba[2] = 0; + feature = 0x00; + controller->raise_irq(); + allow_writing = true; + break; + case 0x20: /* READ SECTOR */ + progress_count = 0; + state = IDE_DEV_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 0.1)/*ms*/,controller->interface_index); + break; + case 0x30: /* WRITE SECTOR */ + /* the drive does NOT signal an interrupt. it sets DRQ and waits for a sector + * to be transferred to it before executing the command */ + progress_count = 0; + state = IDE_DEV_DATA_WRITE; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ; + prepare_write(0,512); + break; + case 0x40: /* READ SECTOR VERIFY WITH RETRY */ + case 0x41: /* READ SECTOR VERIFY WITHOUT RETRY */ + progress_count = 0; + state = IDE_DEV_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 0.1)/*ms*/,controller->interface_index); + break; + case 0x91: /* INITIALIZE DEVICE PARAMETERS */ + if ((unsigned int)count != (unsigned int)sects || (unsigned int)((drivehead&0xF)+1) != (unsigned int)heads) { + if (count == 0) { + LOG_MSG("IDE warning: OS attempted to change geometry to invalid H/S %u/%u\n", + count,(drivehead&0xF)+1); + abort_error(); + allow_writing = true; + return; + } + else { + unsigned int ncyls; + + ncyls = (phys_cyls * phys_heads * phys_sects); + ncyls += (count * ((unsigned int)(drivehead&0xF)+1u)) - 1u; + ncyls /= count * ((unsigned int)(drivehead&0xF)+1u); + + /* the OS is changing logical disk geometry, so update our head/sector count (needed for Windows ME) */ + LOG_MSG("IDE warning: OS is changing logical geometry from C/H/S %u/%u/%u to logical H/S %u/%u/%u\n", + (int)cyls,(int)heads,(int)sects, + (int)ncyls,(int)((drivehead&0xF)+1),(int)count); + LOG_MSG(" Compatibility issues may occur if the OS tries to use INT 13 at the same time!\n"); + + cyls = ncyls; + sects = count; + heads = (drivehead&0xFu)+1u; + } + } + + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + allow_writing = true; + break; + case 0xC4: /* READ MULTIPLE */ + progress_count = 0; + state = IDE_DEV_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : 0.1)/*ms*/,controller->interface_index); + break; + case 0xC5: /* WRITE MULTIPLE */ + /* the drive does NOT signal an interrupt. it sets DRQ and waits for a sector + * to be transferred to it before executing the command */ + progress_count = 0; + state = IDE_DEV_DATA_WRITE; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRQ; + prepare_write(0UL,512UL*MIN((unsigned long)multiple_sector_count,(unsigned long)(count == 0 ? 256 : count))); + break; + case 0xC6: /* SET MULTIPLE MODE */ + /* only sector counts 1, 2, 4, 8, 16, 32, 64, and 128 are legal by standard. + * NTS: There's a bug in VirtualBox that makes 0 legal too! */ + if (count != 0 && count <= multiple_sector_max && is_power_of_2(count)) { + multiple_sector_count = count; + status = IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE; + } + else { + feature = 0x04; /* abort error */ + abort_error(); + } + controller->raise_irq(); + allow_writing = true; + break; + case 0xA0:/*ATAPI PACKET*/ + /* We're not an ATAPI packet device! + * Windows 95 seems to issue this at startup to hard drives. Duh. */ + /* fall through */ + case 0xA1: /* IDENTIFY PACKET DEVICE */ + /* We are not an ATAPI packet device. + * Most MS-DOS drivers and Windows 95 like to issue both IDENTIFY ATA and IDENTIFY ATAPI commands. + * I also gather from some contributers on the github comments that people think our "Unknown IDE/ATA command" + * error message is part of some other error in the emulation. Rather than put up with that, we'll just + * silently abort the command with an error. */ + abort_normal(); + status = IDE_STATUS_ERROR|IDE_STATUS_DRIVE_READY; + drivehead &= 0x30; controller->drivehead = drivehead; + count = 0x01; + lba[0] = 0x01; + feature = 0x04; /* abort */ + lba[1] = 0x00; + lba[2] = 0x00; + controller->raise_irq(); + allow_writing = true; + break; + case 0xEC: /* IDENTIFY DEVICE */ + state = IDE_DEV_BUSY; + status = IDE_STATUS_BUSY; + PIC_AddEvent(IDE_DelayedCommand,(faked_command ? 0.000001 : ide_identify_command_delay),controller->interface_index); + break; + default: + LOG_MSG("Unknown IDE/ATA command %02X\n",cmd); + abort_error(); + allow_writing = true; + controller->raise_irq(); + break; + } +} + +void IDEDevice::deselect() { +} + +/* the hard disk or CD-ROM class override of this member is responsible for checking + the head value and clamping within range if C/H/S mode is selected */ +void IDEDevice::select(uint8_t ndh,bool switched_to) { + (void)switched_to;//UNUSED + (void)ndh;//UNUSED + /* NTS: I thought there was some delay between selecting a drive and sending a command. + Apparently I was wrong. */ + if (allow_writing) drivehead = ndh; +// status = (!asleep)?(IDE_STATUS_DRIVE_READY|IDE_STATUS_DRIVE_SEEK_COMPLETE):0; +// allow_writing = !asleep; +// state = IDE_DEV_READY; +} + +IDEController::IDEController(Section* configuration,unsigned char index):Module_base(configuration){ + Section_prop * section=static_cast(configuration); + int i; + + register_pnp = section->Get_bool("pnp"); + int13fakeio = section->Get_bool("int13fakeio"); + int13fakev86io = section->Get_bool("int13fakev86io"); + enable_pio32 = section->Get_bool("enable pio32"); + ignore_pio32 = section->Get_bool("ignore pio32"); + spinup_time = section->Get_int("cd-rom spinup time"); + spindown_timeout = section->Get_int("cd-rom spindown timeout"); + cd_insertion_time = section->Get_int("cd-rom insertion delay"); + + status = 0x00; + host_reset = false; + irq_pending = false; + interrupt_enable = true; + interface_index = index; + device[0] = NULL; + device[1] = NULL; + base_io = 0; + select = 0; + alt_io = 0; + IRQ = -1; + + i = section->Get_int("irq"); + if (i > 0 && i <= 15) IRQ = i; + + i = section->Get_hex("io"); + if (i >= 0x100 && i <= 0x3FF) base_io = (unsigned int)(i & ~7); + + i = section->Get_hex("altio"); + if (i >= 0x100 && i <= 0x3FF) alt_io = (unsigned int)(i & ~1); + + if (index < sizeof(IDE_default_IRQs)) { + if (IRQ < 0) IRQ = IDE_default_IRQs[index]; + if (alt_io == 0) alt_io = IDE_default_alts[index]; + if (base_io == 0) base_io = IDE_default_bases[index]; + } + else { + if (IRQ < 0 || alt_io == 0 || base_io == 0) + LOG_MSG("WARNING: IDE interface %u: Insufficient resources assigned by dosbox.conf, and no appropriate default resources for this interface.",index); + } +} + +void IDEController::register_isapnp() { + if (register_pnp && base_io > 0 && alt_io > 0) { + unsigned char tmp[256]; + unsigned int i; + + const unsigned char h1[9] = { + ISAPNP_SYSDEV_HEADER( + ISAPNP_ID('P','N','P',0x0,0x6,0x0,0x0), /* PNP0600 Generic ESDI/IDE/ATA compatible hard disk controller */ + ISAPNP_TYPE(0x01,0x01,0x00), /* type: Mass Storage Device / IDE / Generic */ + 0x0001 | 0x0002) + }; + + i = 0; + memcpy(tmp+i,h1,9); i += 9; /* can't disable, can't configure */ + /*----------allocated--------*/ + tmp[i+0] = (8 << 3) | 7; /* IO resource */ + tmp[i+1] = 0x01; /* 16-bit decode */ + host_writew(tmp+i+2,base_io); /* min */ + host_writew(tmp+i+4,base_io); /* max */ + tmp[i+6] = 0x08; /* align */ + tmp[i+7] = 0x08; /* length */ + i += 7+1; + + tmp[i+0] = (8 << 3) | 7; /* IO resource */ + tmp[i+1] = 0x01; /* 16-bit decode */ + host_writew(tmp+i+2,alt_io); /* min */ + host_writew(tmp+i+4,alt_io); /* max */ + tmp[i+6] = 0x01; /* align */ + if (alt_io == 0x3F6 && fdc_takes_port_3F7()) + tmp[i+7] = 0x01; /* length 1 (so as not to conflict with floppy controller at 0x3F7) */ + else + tmp[i+7] = 0x02; /* length 2 */ + i += 7+1; + + if (IRQ > 0) { + tmp[i+0] = (4 << 3) | 3; /* IRQ resource */ + host_writew(tmp+i+1,1 << IRQ); + tmp[i+3] = 0x09; /* HTE=1 LTL=1 */ + i += 3+1; + } + + tmp[i+0] = 0x79; /* END TAG */ + tmp[i+1] = 0x00; + i += 2; + /*-------------possible-----------*/ + tmp[i+0] = 0x79; /* END TAG */ + tmp[i+1] = 0x00; + i += 2; + /*-------------compatible---------*/ + tmp[i+0] = 0x79; /* END TAG */ + tmp[i+1] = 0x00; + i += 2; + + if (!ISAPNP_RegisterSysDev(tmp,i)) + LOG_MSG("ISAPNP register failed\n"); + } +} + +void IDEController::install_io_port(){ + unsigned int i; + + if (base_io != 0) { + for (i=0;i < 8;i++) { + WriteHandler[i].Install(base_io+i,ide_baseio_w,IO_MA); + ReadHandler[i].Install(base_io+i,ide_baseio_r,IO_MA); + } + } + + if (alt_io != 0) { + WriteHandlerAlt[0].Install(alt_io,ide_altio_w,IO_MA); + ReadHandlerAlt[0].Install(alt_io,ide_altio_r,IO_MA); + + /* the floppy controller might take port 0x3F7. + * don't claim it if so */ + if (alt_io == 0x3F6 && fdc_takes_port_3F7()) { + LOG_MSG("IDE: Not registering port 3F7h, FDC will occupy it.\n"); + } + else { + WriteHandlerAlt[1].Install(alt_io+1u,ide_altio_w,IO_MA); + ReadHandlerAlt[1].Install(alt_io+1u,ide_altio_r,IO_MA); + } + } +} + +IDEController::~IDEController() { + unsigned int i; + + for (i=0;i < 2;i++) { + if (device[i] != NULL) { + delete device[i]; + device[i] = NULL; + } + } +} + +static void ide_altio_w(Bitu port,Bitu val,Bitu iolen) { + IDEController *ide = match_ide_controller(port); + if (ide == NULL) { + LOG_MSG("WARNING: port read from I/O port not registered to IDE, yet callback triggered\n"); + return; + } + + if (!ide->enable_pio32 && iolen == 4) { + ide_altio_w(port,val&0xFFFF,2); + ide_altio_w(port+2u,val>>16u,2); + return; + } + else if (ide->ignore_pio32 && iolen == 4) + return; + + port &= 1; + if (port == 0) {/*3F6*/ + ide->interrupt_enable = (val&2u)?0:1; + if (ide->interrupt_enable) { + if (ide->irq_pending) ide->raise_irq(); + } + else { + if (ide->IRQ >= 0) PIC_DeActivateIRQ((unsigned int)ide->IRQ); + } + + if ((val&4) && !ide->host_reset) { + if (ide->device[0]) ide->device[0]->host_reset_begin(); + if (ide->device[1]) ide->device[1]->host_reset_begin(); + ide->host_reset=1; + } + else if (!(val&4) && ide->host_reset) { + if (ide->device[0]) ide->device[0]->host_reset_complete(); + if (ide->device[1]) ide->device[1]->host_reset_complete(); + ide->host_reset=0; + } + } +} + +static Bitu ide_altio_r(Bitu port,Bitu iolen) { + IDEController *ide = match_ide_controller(port); + IDEDevice *dev; + + if (ide == NULL) { + LOG_MSG("WARNING: port read from I/O port not registered to IDE, yet callback triggered\n"); + return ~(0UL); + } + + if (!ide->enable_pio32 && iolen == 4) + return ide_altio_r(port,2) + (ide_altio_r(port+2u,2) << 16u); + else if (ide->ignore_pio32 && iolen == 4) + return ~0ul; + + dev = ide->device[ide->select]; + port &= 1; + if (port == 0)/*3F6(R) status, does NOT clear interrupt*/ + return (dev != NULL) ? dev->status : ide->status; + else /*3F7(R) Drive Address Register*/ + return 0x80u|(ide->select==0?0u:1u)|(ide->select==1?0u:2u)| + ((dev != NULL) ? (((dev->drivehead&0xFu)^0xFu) << 2u) : 0x3Cu); + + return ~(0UL); +} + +static Bitu ide_baseio_r(Bitu port,Bitu iolen) { + IDEController *ide = match_ide_controller(port); + IDEDevice *dev; + Bitu ret = ~0ul; + + if (ide == NULL) { + LOG_MSG("WARNING: port read from I/O port not registered to IDE, yet callback triggered\n"); + return ~(0UL); + } + + if (!ide->enable_pio32 && iolen == 4) + return ide_baseio_r(port,2) + (ide_baseio_r(port+2,2) << 16); + else if (ide->ignore_pio32 && iolen == 4) + return ~0ul; + + dev = ide->device[ide->select]; + port &= 7; + switch (port) { + case 0: /* 1F0 */ + ret = (dev != NULL) ? dev->data_read(iolen) : 0xFFFFFFFFUL; + break; + case 1: /* 1F1 */ + ret = (dev != NULL) ? dev->feature : 0x00; + break; + case 2: /* 1F2 */ + ret = (dev != NULL) ? dev->count : 0x00; + break; + case 3: /* 1F3 */ + ret = (dev != NULL) ? dev->lba[0] : 0x00; + break; + case 4: /* 1F4 */ + ret = (dev != NULL) ? dev->lba[1] : 0x00; + break; + case 5: /* 1F5 */ + ret = (dev != NULL) ? dev->lba[2] : 0x00; + break; + case 6: /* 1F6 */ + ret = ide->drivehead; + break; + case 7: /* 1F7 */ + /* if an IDE device exists at selection return it's status, else return our status */ + if (dev && dev->status & IDE_STATUS_BUSY) { + } + else if (dev == NULL && ide->status & IDE_STATUS_BUSY) { + } + else { + ide->lower_irq(); + } + + ret = (dev != NULL) ? dev->status : ide->status; + break; + } + + return ret; +} + +static void ide_baseio_w(Bitu port,Bitu val,Bitu iolen) { + IDEController *ide = match_ide_controller(port); + IDEDevice *dev; + + if (ide == NULL) { + LOG_MSG("WARNING: port read from I/O port not registered to IDE, yet callback triggered\n"); + return; + } + + if (!ide->enable_pio32 && iolen == 4) { + ide_baseio_w(port,val&0xFFFF,2); + ide_baseio_w(port+2,val>>16,2); + return; + } + else if (ide->ignore_pio32 && iolen == 4) + return; + + dev = ide->device[ide->select]; + port &= 7; + + /* ignore I/O writes if the controller is busy */ + if (dev) { + if (dev->status & IDE_STATUS_BUSY) { + if (port == 6 && ((val>>4)&1) == ide->select) { + /* some MS-DOS drivers like ATAPICD.SYS are just very pedantic about writing to port +6 to ensure the right drive is selected */ + return; + } + else { + LOG_MSG("W-%03X %02X BUSY DROP [DEV]\n",(int)(port+ide->base_io),(int)val); + return; + } + } + } + else if (ide->status & IDE_STATUS_BUSY) { + if (port == 6 && ((val>>4)&1) == ide->select) { + /* some MS-DOS drivers like ATAPICD.SYS are just very pedantic about writing to port +6 to ensure the right drive is selected */ + return; + } + else { + LOG_MSG("W-%03X %02X BUSY DROP [IDE]\n",(int)(port+ide->base_io),(int)val); + return; + } + } + +#if 0 + if (ide == idecontroller[1]) + LOG_MSG("IDE: baseio write port %u val %02x\n",(unsigned int)port,(unsigned int)val); +#endif + + if (port >= 1 && port <= 5 && dev && !dev->allow_writing) { + LOG_MSG("IDE WARNING: Write to port %u val %02x when device not ready to accept writing\n", + (unsigned int)port,(unsigned int)val); + } + + switch (port) { + case 0: /* 1F0 */ + if (dev) dev->data_write(val,iolen); /* <- TODO: what about 32-bit PIO modes? */ + break; + case 1: /* 1F1 */ + if (dev && dev->allow_writing) /* TODO: LBA48 16-bit wide register */ + dev->feature = val; + break; + case 2: /* 1F2 */ + if (dev && dev->allow_writing) /* TODO: LBA48 16-bit wide register */ + dev->count = val; + break; + case 3: /* 1F3 */ + if (dev && dev->allow_writing) /* TODO: LBA48 16-bit wide register */ + dev->lba[0] = val; + break; + case 4: /* 1F4 */ + if (dev && dev->allow_writing) /* TODO: LBA48 16-bit wide register */ + dev->lba[1] = val; + break; + case 5: /* 1F5 */ + if (dev && dev->allow_writing) /* TODO: LBA48 16-bit wide register */ + dev->lba[2] = val; + break; + case 6: /* 1F6 */ + if (((val>>4)&1) != ide->select) { + ide->lower_irq(); + /* update select pointer if bit 4 changes. + also emulate IDE busy state when changing drives */ + if (dev) dev->deselect(); + ide->select = (val>>4)&1; + dev = ide->device[ide->select]; + if (dev) dev->select(val,1); + else ide->status = 0; /* NTS: if there is no drive there you're supposed to not have anything set */ + } + else if (dev) { + dev->select(val,0); + } + else { + ide->status = 0; /* NTS: if there is no drive there you're supposed to not have anything set */ + } + + ide->drivehead = val; + break; + case 7: /* 1F7 */ + if (dev) dev->writecommand(val); + break; + } +} + +static void IDE_Destroy(Section* sec) { + (void)sec;//UNUSED + for (unsigned int i=0;i < MAX_IDE_CONTROLLERS;i++) { + if (idecontroller[i] != NULL) { + delete idecontroller[i]; + idecontroller[i] = NULL; + } + } + + init_ide = 0; +} + +static void IDE_Init(Section* sec,unsigned char ide_interface) { + Section_prop *section=static_cast(sec); + IDEController *ide; + + assert(ide_interface < MAX_IDE_CONTROLLERS); + + if (!section->Get_bool("enable")) + return; + + if (!init_ide) { + AddExitFunction(AddExitFunctionFuncPair(IDE_Destroy)); + init_ide = 1; + } + + LOG(LOG_MISC,LOG_DEBUG)("Initializing IDE controller %u",ide_interface); + + if (idecontroller[ide_interface] != NULL) { + delete idecontroller[ide_interface]; + idecontroller[ide_interface] = NULL; + } + + ide = idecontroller[ide_interface] = new IDEController(sec,ide_interface); + ide->install_io_port(); + + PIC_SetIRQMask((unsigned int)ide->IRQ,false); +} + +void IDE_Primary_Init(Section *sec) { + IDE_Init(sec,0); +} + +void IDE_Secondary_Init(Section *sec) { + IDE_Init(sec,1); +} + +void IDE_Tertiary_Init(Section *sec) { + IDE_Init(sec,2); +} + +void IDE_Quaternary_Init(Section *sec) { + IDE_Init(sec,3); +} + +void IDE_Quinternary_Init(Section *sec) { + IDE_Init(sec,4); +} + +void IDE_Sexternary_Init(Section *sec) { + IDE_Init(sec,5); +} + +void IDE_Septernary_Init(Section *sec) { + IDE_Init(sec,6); +} + +void IDE_Octernary_Init(Section *sec) { + IDE_Init(sec,7); +} + +const char *ide_names[MAX_IDE_CONTROLLERS] = { + "ide, primary", + "ide, secondary", + "ide, tertiary", + "ide, quaternary", + "ide, quinternary", + "ide, sexternary", + "ide, septernary", + "ide, octernary" +}; +void (*ide_inits[MAX_IDE_CONTROLLERS])(Section *) = { + &IDE_Primary_Init, + &IDE_Secondary_Init, + &IDE_Tertiary_Init, + &IDE_Quaternary_Init, + &IDE_Quinternary_Init, + &IDE_Sexternary_Init, + &IDE_Septernary_Init, + &IDE_Octernary_Init +}; + +void IDE_OnReset(Section *sec) { + (void)sec;//UNUSED + if (IS_PC98_ARCH) return; + + for (size_t i=0;i < MAX_IDE_CONTROLLERS;i++) ide_inits[i](control->GetSection(ide_names[i])); +} + +void IDE_Init() { + LOG(LOG_MISC,LOG_DEBUG)("Initializing IDE controllers"); + + AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(IDE_OnReset)); +} + +void BIOS_Post_register_IDE() { + for (size_t i=0;i < MAX_IDE_CONTROLLERS;i++) { + if (idecontroller[i] != NULL) + idecontroller[i]->register_isapnp(); + } +} + diff --git a/src/ints/bios.cpp b/src/ints/bios.cpp index 57bee62fb..3ff453009 100644 --- a/src/ints/bios.cpp +++ b/src/ints/bios.cpp @@ -1,7936 +1,7936 @@ -/* - * Copyright (C) 2002-2015 The DOSBox Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include "dosbox.h" -#include "mem.h" -#include "cpu.h" -#include "bios.h" -#include "regs.h" -#include "cpu.h" -#include "callback.h" -#include "inout.h" -#include "pic.h" -#include "hardware.h" -#include "pci_bus.h" -#include "joystick.h" -#include "mouse.h" -#include "callback.h" -#include "setup.h" -#include "bios_disk.h" -#include "serialport.h" -#include "mapper.h" -#include "vga.h" -#include "pc98_gdc.h" -#include "pc98_gdc_const.h" -#include "regionalloctracking.h" -#include "build_timestamp.h" -extern bool PS1AudioCard; -#include "parport.h" -#include -#include - -#if C_EMSCRIPTEN -# include -#endif - -#if defined(_MSC_VER) -# pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */ -# pragma warning(disable:4305) /* truncation from double to float */ -#endif - -#if defined(WIN32) && !defined(S_ISREG) -# define S_ISREG(x) ((x & S_IFREG) == S_IFREG) -#endif - -/* mouse.cpp */ -extern bool en_bios_ps2mouse; -extern bool rom_bios_8x8_cga_font; -extern bool pcibus_enable; - -uint32_t Keyb_ig_status(); -bool VM_Boot_DOSBox_Kernel(); -Bit32u MEM_get_address_bits(); -Bitu bios_post_parport_count(); -Bitu bios_post_comport_count(); -bool KEYBOARD_Report_BIOS_PS2Mouse(); -bool MEM_map_ROM_alias_physmem(Bitu start,Bitu end); - -bool bochs_port_e9 = false; -bool isa_memory_hole_512kb = false; -bool int15_wait_force_unmask_irq = false; - -int unhandled_irq_method = UNHANDLED_IRQ_SIMPLE; - -Bit16u biosConfigSeg=0; - -Bitu BIOS_DEFAULT_IRQ0_LOCATION = ~0u; // (RealMake(0xf000,0xfea5)) -Bitu BIOS_DEFAULT_IRQ1_LOCATION = ~0u; // (RealMake(0xf000,0xe987)) -Bitu BIOS_DEFAULT_IRQ07_DEF_LOCATION = ~0u; // (RealMake(0xf000,0xff55)) -Bitu BIOS_DEFAULT_IRQ815_DEF_LOCATION = ~0u; // (RealMake(0xf000,0xe880)) - -Bitu BIOS_DEFAULT_HANDLER_LOCATION = ~0u; // (RealMake(0xf000,0xff53)) - -Bitu BIOS_VIDEO_TABLE_LOCATION = ~0u; // RealMake(0xf000,0xf0a4) -Bitu BIOS_VIDEO_TABLE_SIZE = 0u; - -Bitu BIOS_DEFAULT_RESET_LOCATION = ~0u; // RealMake(0xf000,0xe05b) - -bool allow_more_than_640kb = false; - -unsigned int APM_BIOS_connected_minor_version = 0;// what version the OS connected to us with. default to 1.0 -unsigned int APM_BIOS_minor_version = 2; // what version to emulate e.g to emulate 1.2 set this to 2 - -/* default bios type/version/date strings */ -const char* const bios_type_string = "IBM COMPATIBLE 486 BIOS COPYRIGHT The DOSBox Team."; -const char* const bios_version_string = "DOSBox FakeBIOS v1.0"; -const char* const bios_date_string = "01/01/92"; - -bool APM_inactivity_timer = true; - -RegionAllocTracking rombios_alloc; - -Bitu rombios_minimum_location = 0xF0000; /* minimum segment allowed */ -Bitu rombios_minimum_size = 0x10000; - -bool MEM_map_ROM_physmem(Bitu start,Bitu end); -bool MEM_unmap_physmem(Bitu start,Bitu end); - -static std::string bochs_port_e9_line; - -static void bochs_port_e9_flush() { - if (!bochs_port_e9_line.empty()) { - LOG_MSG("Bochs port E9h: %s",bochs_port_e9_line.c_str()); - bochs_port_e9_line.clear(); - } -} - -void bochs_port_e9_write(Bitu port,Bitu val,Bitu /*iolen*/) { - (void)port;//UNUSED - if (val == '\n' || val == '\r') { - bochs_port_e9_flush(); - } - else { - bochs_port_e9_line += (char)val; - if (bochs_port_e9_line.length() >= 256) - bochs_port_e9_flush(); - } -} - -void ROMBIOS_DumpMemory() { - rombios_alloc.logDump(); -} - -void ROMBIOS_SanityCheck() { - rombios_alloc.sanityCheck(); -} - -Bitu ROMBIOS_MinAllocatedLoc() { - Bitu r = rombios_alloc.getMinAddress(); - - if (r > (0x100000u - rombios_minimum_size)) - r = (0x100000u - rombios_minimum_size); - - return r & ~0xFFFu; -} - -void ROMBIOS_FreeUnusedMinToLoc(Bitu phys) { - Bitu new_phys; - - if (rombios_minimum_location & 0xFFF) E_Exit("ROMBIOS: FreeUnusedMinToLoc minimum location not page aligned"); - - phys &= ~0xFFFUL; - new_phys = rombios_alloc.freeUnusedMinToLoc(phys) & (~0xFFFUL); - assert(new_phys >= phys); - if (phys < new_phys) MEM_unmap_physmem(phys,new_phys-1); - rombios_minimum_location = new_phys; - ROMBIOS_SanityCheck(); - ROMBIOS_DumpMemory(); -} - -bool ROMBIOS_FreeMemory(Bitu phys) { - return rombios_alloc.freeMemory(phys); -} - -Bitu ROMBIOS_GetMemory(Bitu bytes,const char *who,Bitu alignment,Bitu must_be_at) { - return rombios_alloc.getMemory(bytes,who,alignment,must_be_at); -} - -void ROMBIOS_InitForCustomBIOS(void) { - rombios_alloc.initSetRange(0xD8000,0xE0000); -} - -static IO_Callout_t dosbox_int_iocallout = IO_Callout_t_none; - -static unsigned char dosbox_int_register_shf = 0; -static uint32_t dosbox_int_register = 0; -static unsigned char dosbox_int_regsel_shf = 0; -static uint32_t dosbox_int_regsel = 0; -static bool dosbox_int_error = false; -static bool dosbox_int_busy = false; -static const char *dosbox_int_version = "DOSBox-X integration device v1.0"; -static const char *dosbox_int_ver_read = NULL; - -struct dosbox_int_saved_state { - unsigned char dosbox_int_register_shf; - uint32_t dosbox_int_register; - unsigned char dosbox_int_regsel_shf; - uint32_t dosbox_int_regsel; - bool dosbox_int_error; - bool dosbox_int_busy; -}; - -#define DOSBOX_INT_SAVED_STATE_MAX 4 - -struct dosbox_int_saved_state dosbox_int_saved[DOSBOX_INT_SAVED_STATE_MAX]; -int dosbox_int_saved_sp = -1; - -/* for use with interrupt handlers in DOS/Windows that need to save IG state - * to ensure that IG state is restored on return in order to not interfere - * with anything userspace is doing (as an alternative to wrapping all access - * in CLI/STI or PUSHF/CLI/POPF) */ -bool dosbox_int_push_save_state(void) { - - if (dosbox_int_saved_sp >= (DOSBOX_INT_SAVED_STATE_MAX-1)) - return false; - - struct dosbox_int_saved_state *ss = &dosbox_int_saved[++dosbox_int_saved_sp]; - - ss->dosbox_int_register_shf = dosbox_int_register_shf; - ss->dosbox_int_register = dosbox_int_register; - ss->dosbox_int_regsel_shf = dosbox_int_regsel_shf; - ss->dosbox_int_regsel = dosbox_int_regsel; - ss->dosbox_int_error = dosbox_int_error; - ss->dosbox_int_busy = dosbox_int_busy; - return true; -} - -bool dosbox_int_pop_save_state(void) { - if (dosbox_int_saved_sp < 0) - return false; - - struct dosbox_int_saved_state *ss = &dosbox_int_saved[dosbox_int_saved_sp--]; - - dosbox_int_register_shf = ss->dosbox_int_register_shf; - dosbox_int_register = ss->dosbox_int_register; - dosbox_int_regsel_shf = ss->dosbox_int_regsel_shf; - dosbox_int_regsel = ss->dosbox_int_regsel; - dosbox_int_error = ss->dosbox_int_error; - dosbox_int_busy = ss->dosbox_int_busy; - return true; -} - -bool dosbox_int_discard_save_state(void) { - if (dosbox_int_saved_sp < 0) - return false; - - dosbox_int_saved_sp--; - return true; -} - -extern bool user_cursor_locked; -extern int user_cursor_x,user_cursor_y; -extern int user_cursor_sw,user_cursor_sh; - -static std::string dosbox_int_debug_out; - -/* read triggered, update the regsel */ -void dosbox_integration_trigger_read() { - dosbox_int_error = false; - - switch (dosbox_int_regsel) { - case 0: /* Identification */ - dosbox_int_register = 0xD05B0740; - break; - case 1: /* test */ - break; - case 2: /* version string */ - if (dosbox_int_ver_read == NULL) - dosbox_int_ver_read = dosbox_int_version; - - dosbox_int_register = 0; - for (Bitu i=0;i < 4;i++) { - if (*dosbox_int_ver_read == 0) { - dosbox_int_ver_read = dosbox_int_version; - break; - } - - dosbox_int_register += ((uint32_t)((unsigned char)(*dosbox_int_ver_read++))) << (uint32_t)(i * 8); - } - break; - case 3: /* version number */ - dosbox_int_register = (0x01U/*major*/) + (0x00U/*minor*/ << 8U) + (0x00U/*subver*/ << 16U) + (0x01U/*bump*/ << 24U); - break; - -// case 0x804200: /* keyboard input injection -- not supposed to read */ -// break; - - case 0x804201: /* keyboard status */ - dosbox_int_register = Keyb_ig_status(); - break; - - case 0x434D54: /* read user mouse status */ - dosbox_int_register = - (user_cursor_locked ? (1UL << 0UL) : 0UL); /* bit 0 = mouse capture lock */ - break; - - case 0x434D55: /* read user mouse cursor position */ - dosbox_int_register = ((user_cursor_y & 0xFFFFUL) << 16UL) | (user_cursor_x & 0xFFFFUL); - break; - - case 0x434D56: { /* read user mouse cursor position (normalized for Windows 3.x) */ - signed long long x = ((signed long long)user_cursor_x << 16LL) / (signed long long)(user_cursor_sw-1); - signed long long y = ((signed long long)user_cursor_y << 16LL) / (signed long long)(user_cursor_sh-1); - if (x < 0x0000LL) x = 0x0000LL; - if (x > 0xFFFFLL) x = 0xFFFFLL; - if (y < 0x0000LL) y = 0x0000LL; - if (y > 0xFFFFLL) y = 0xFFFFLL; - dosbox_int_register = ((unsigned int)y << 16UL) | (unsigned int)x; - } break; - - case 0xC54010: /* Screenshot/capture trigger */ - /* TODO: This should also be hidden behind an enable switch, so that rogue DOS development - * can't retaliate if the user wants to capture video or screenshots. */ -#if (C_SSHOT) - extern Bitu CaptureState; - dosbox_int_register = 0x00000000; // available - if (CaptureState & CAPTURE_IMAGE) - dosbox_int_register |= 1 << 0; // Image capture is in progress - if (CaptureState & CAPTURE_VIDEO) - dosbox_int_register |= 1 << 1; // Video capture is in progress - if (CaptureState & CAPTURE_WAVE) - dosbox_int_register |= 1 << 2; // WAVE capture is in progress -#else - dosbox_int_register = 0xC0000000; // not available (bit 31 set), not enabled (bit 30 set) -#endif - break; - - case 0xAA55BB66UL: /* interface reset result */ - break; - - default: - dosbox_int_register = 0xAA55AA55; - dosbox_int_error = true; - break; - }; - - LOG(LOG_MISC,LOG_DEBUG)("DOSBox integration read 0x%08lx got 0x%08lx (err=%u)\n", - (unsigned long)dosbox_int_regsel, - (unsigned long)dosbox_int_register, - dosbox_int_error?1:0); -} - -unsigned int mouse_notify_mode = 0; -// 0 = off -// 1 = trigger as PS/2 mouse interrupt - -/* write triggered */ -void dosbox_integration_trigger_write() { - dosbox_int_error = false; - - LOG(LOG_MISC,LOG_DEBUG)("DOSBox integration write 0x%08lx val 0x%08lx\n", - (unsigned long)dosbox_int_regsel, - (unsigned long)dosbox_int_register); - - switch (dosbox_int_regsel) { - case 1: /* test */ - break; - - case 2: /* version string */ - dosbox_int_ver_read = NULL; - break; - - case 0xDEB0: /* debug output (to log) */ - for (unsigned int b=0;b < 4;b++) { - unsigned char c = (unsigned char)(dosbox_int_register >> (b * 8U)); - if (c == '\n' || dosbox_int_debug_out.length() >= 200) { - LOG_MSG("Client debug message: %s\n",dosbox_int_debug_out.c_str()); - dosbox_int_debug_out.clear(); - } - else if (c != 0) { - dosbox_int_debug_out += ((char)c); - } - else { - break; - } - } - dosbox_int_register = 0; - break; - - case 0xDEB1: /* debug output clear */ - dosbox_int_debug_out.clear(); - break; - - case 0x52434D: /* release mouse capture 'MCR' */ - void GFX_ReleaseMouse(void); - GFX_ReleaseMouse(); - break; - - case 0x804200: /* keyboard input injection */ - void Mouse_ButtonPressed(Bit8u button); - void Mouse_ButtonReleased(Bit8u button); - void pc98_keyboard_send(const unsigned char b); - void Mouse_CursorMoved(float xrel,float yrel,float x,float y,bool emulate); - void KEYBOARD_AUX_Event(float x,float y,Bitu buttons,int scrollwheel); - void KEYBOARD_AddBuffer(Bit16u data); - - switch ((dosbox_int_register>>8)&0xFF) { - case 0x00: // keyboard - if (IS_PC98_ARCH) - pc98_keyboard_send(dosbox_int_register&0xFF); - else - KEYBOARD_AddBuffer(dosbox_int_register&0xFF); - break; - case 0x01: // AUX - if (!IS_PC98_ARCH) - KEYBOARD_AddBuffer((dosbox_int_register&0xFF)|0x100/*AUX*/); - else // no such interface in PC-98 mode - dosbox_int_error = true; - break; - case 0x08: // mouse button injection - if (dosbox_int_register&0x80) Mouse_ButtonPressed(dosbox_int_register&0x7F); - else Mouse_ButtonReleased(dosbox_int_register&0x7F); - break; - case 0x09: // mouse movement injection (X) - Mouse_CursorMoved(((dosbox_int_register>>16UL) / 256.0f) - 1.0f,0,0,0,true); - break; - case 0x0A: // mouse movement injection (Y) - Mouse_CursorMoved(0,((dosbox_int_register>>16UL) / 256.0f) - 1.0f,0,0,true); - break; - case 0x0B: // mouse scrollwheel injection - // TODO - break; - default: - dosbox_int_error = true; - break; - } - break; - -// case 0x804201: /* keyboard status do not write */ -// break; - - /* this command is used to enable notification of mouse movement over the windows even if the mouse isn't captured */ - case 0x434D55: /* read user mouse cursor position */ - case 0x434D56: /* read user mouse cursor position (normalized for Windows 3.x) */ - mouse_notify_mode = dosbox_int_register & 0xFF; - LOG(LOG_MISC,LOG_DEBUG)("Mouse notify mode=%u",mouse_notify_mode); - break; - - case 0xC54010: /* Screenshot/capture trigger */ -#if (C_SSHOT) - void CAPTURE_ScreenShotEvent(bool pressed); - void CAPTURE_VideoEvent(bool pressed); -#endif - void CAPTURE_WaveEvent(bool pressed); - - /* TODO: It would be wise to grant/deny access to this register through another dosbox.conf option - * so that rogue DOS development cannot shit-spam the capture folder */ -#if (C_SSHOT) - if (dosbox_int_register & 1) - CAPTURE_ScreenShotEvent(true); - if (dosbox_int_register & 2) - CAPTURE_VideoEvent(true); -#endif - if (dosbox_int_register & 4) - CAPTURE_WaveEvent(true); - break; - - default: - dosbox_int_register = 0x55AA55AA; - dosbox_int_error = true; - break; - }; -} - -/* PORT 0x28: Index - * 0x29: Data - * 0x2A: Status(R) or Command(W) - * 0x2B: Not yet assigned - * - * Registers are 32-bit wide. I/O to index and data rotate through the - * bytes of the register depending on I/O length, meaning that one 32-bit - * I/O read will read the entire register, while four 8-bit reads will - * read one byte out of 4. */ - -static Bitu dosbox_integration_port00_index_r(Bitu port,Bitu iolen) { - (void)port;//UNUSED - Bitu retb = 0; - Bitu ret = 0; - - while (iolen > 0) { - ret += ((dosbox_int_regsel >> (dosbox_int_regsel_shf * 8)) & 0xFFU) << (retb * 8); - if ((++dosbox_int_regsel_shf) >= 4) dosbox_int_regsel_shf = 0; - iolen--; - retb++; - } - - return ret; -} - -static void dosbox_integration_port00_index_w(Bitu port,Bitu val,Bitu iolen) { - (void)port;//UNUSED - uint32_t msk; - - while (iolen > 0) { - msk = 0xFFU << (dosbox_int_regsel_shf * 8); - dosbox_int_regsel = (dosbox_int_regsel & ~msk) + ((val & 0xFF) << (dosbox_int_regsel_shf * 8)); - if ((++dosbox_int_regsel_shf) >= 4) dosbox_int_regsel_shf = 0; - val >>= 8U; - iolen--; - } -} - -static Bitu dosbox_integration_port01_data_r(Bitu port,Bitu iolen) { - (void)port;//UNUSED - Bitu retb = 0; - Bitu ret = 0; - - while (iolen > 0) { - if (dosbox_int_register_shf == 0) dosbox_integration_trigger_read(); - ret += ((dosbox_int_register >> (dosbox_int_register_shf * 8)) & 0xFFU) << (retb * 8); - if ((++dosbox_int_register_shf) >= 4) dosbox_int_register_shf = 0; - iolen--; - retb++; - } - - return ret; -} - -static void dosbox_integration_port01_data_w(Bitu port,Bitu val,Bitu iolen) { - (void)port;//UNUSED - uint32_t msk; - - while (iolen > 0) { - msk = 0xFFU << (dosbox_int_register_shf * 8); - dosbox_int_register = (dosbox_int_register & ~msk) + ((val & 0xFF) << (dosbox_int_register_shf * 8)); - if ((++dosbox_int_register_shf) >= 4) dosbox_int_register_shf = 0; - if (dosbox_int_register_shf == 0) dosbox_integration_trigger_write(); - val >>= 8U; - iolen--; - } -} - -static Bitu dosbox_integration_port02_status_r(Bitu port,Bitu iolen) { - (void)iolen;//UNUSED - (void)port;//UNUSED - /* status */ - /* 7:6 = regsel byte index - * 5:4 = register byte index - * 3:2 = reserved - * 1 = error - * 0 = busy */ - return - ((unsigned int)dosbox_int_regsel_shf << 6u) + ((unsigned int)dosbox_int_register_shf << 4u) + - (dosbox_int_error ? 2u : 0u) + (dosbox_int_busy ? 1u : 0u); -} - -static void dosbox_integration_port02_command_w(Bitu port,Bitu val,Bitu iolen) { - (void)port; - (void)iolen; - switch (val) { - case 0x00: /* reset latch */ - dosbox_int_register_shf = 0; - dosbox_int_regsel_shf = 0; - break; - case 0x01: /* flush write */ - if (dosbox_int_register_shf != 0) { - dosbox_integration_trigger_write(); - dosbox_int_register_shf = 0; - } - break; - case 0x20: /* push state */ - if (dosbox_int_push_save_state()) { - dosbox_int_register_shf = 0; - dosbox_int_regsel_shf = 0; - dosbox_int_error = false; - dosbox_int_busy = false; - dosbox_int_regsel = 0xAA55BB66; - dosbox_int_register = 0xD05B0C5; - LOG(LOG_MISC,LOG_DEBUG)("DOSBOX IG state saved"); - } - else { - LOG(LOG_MISC,LOG_DEBUG)("DOSBOX IG unable to push state, stack overflow"); - dosbox_int_error = true; - } - break; - case 0x21: /* pop state */ - if (dosbox_int_pop_save_state()) { - LOG(LOG_MISC,LOG_DEBUG)("DOSBOX IG state restored"); - } - else { - LOG(LOG_MISC,LOG_DEBUG)("DOSBOX IG unable to pop state, stack underflow"); - dosbox_int_error = true; - } - break; - case 0x22: /* discard state */ - if (dosbox_int_discard_save_state()) { - LOG(LOG_MISC,LOG_DEBUG)("DOSBOX IG state discarded"); - } - else { - LOG(LOG_MISC,LOG_DEBUG)("DOSBOX IG unable to discard state, stack underflow"); - dosbox_int_error = true; - } - break; - case 0x23: /* discard all state */ - while (dosbox_int_discard_save_state()); - break; - case 0xFE: /* clear error */ - dosbox_int_error = false; - break; - case 0xFF: /* reset interface */ - dosbox_int_busy = false; - dosbox_int_error = false; - dosbox_int_regsel = 0xAA55BB66; - dosbox_int_register = 0xD05B0C5; - break; - default: - dosbox_int_error = true; - break; - } -} - -static IO_ReadHandler* const dosbox_integration_cb_ports_r[4] = { - dosbox_integration_port00_index_r, - dosbox_integration_port01_data_r, - dosbox_integration_port02_status_r, - NULL -}; - -static IO_ReadHandler* dosbox_integration_cb_port_r(IO_CalloutObject &co,Bitu port,Bitu iolen) { - (void)co; - (void)iolen; - return dosbox_integration_cb_ports_r[port&3]; -} - -static IO_WriteHandler* const dosbox_integration_cb_ports_w[4] = { - dosbox_integration_port00_index_w, - dosbox_integration_port01_data_w, - dosbox_integration_port02_command_w, - NULL -}; - -static IO_WriteHandler* dosbox_integration_cb_port_w(IO_CalloutObject &co,Bitu port,Bitu iolen) { - (void)co; - (void)iolen; - return dosbox_integration_cb_ports_w[port&3]; -} - -/* if mem_systems 0 then size_extended is reported as the real size else - * zero is reported. ems and xms can increase or decrease the other_memsystems - * counter using the BIOS_ZeroExtendedSize call */ -static Bit16u size_extended; -static unsigned int ISA_PNP_WPORT = 0x20B; -static unsigned int ISA_PNP_WPORT_BIOS = 0; -static IO_ReadHandleObject *ISAPNP_PNP_READ_PORT = NULL; /* 0x200-0x3FF range */ -static IO_WriteHandleObject *ISAPNP_PNP_ADDRESS_PORT = NULL; /* 0x279 */ -static IO_WriteHandleObject *ISAPNP_PNP_DATA_PORT = NULL; /* 0xA79 */ -static IO_WriteHandleObject *BOCHS_PORT_E9 = NULL; -//static unsigned char ISA_PNP_CUR_CSN = 0; -static unsigned char ISA_PNP_CUR_ADDR = 0; -static unsigned char ISA_PNP_CUR_STATE = 0; -enum { - ISA_PNP_WAIT_FOR_KEY=0, - ISA_PNP_SLEEP, - ISA_PNP_ISOLATE, - ISA_PNP_CONFIG -}; - -const unsigned char isa_pnp_init_keystring[32] = { - 0x6A,0xB5,0xDA,0xED,0xF6,0xFB,0x7D,0xBE, - 0xDF,0x6F,0x37,0x1B,0x0D,0x86,0xC3,0x61, - 0xB0,0x58,0x2C,0x16,0x8B,0x45,0xA2,0xD1, - 0xE8,0x74,0x3A,0x9D,0xCE,0xE7,0x73,0x39 -}; - -static RealPt INT15_apm_pmentry=0; -static unsigned char ISA_PNP_KEYMATCH=0; -static Bits other_memsystems=0; -static bool apm_realmode_connected = false; -void CMOS_SetRegister(Bitu regNr, Bit8u val); //For setting equipment word -bool enable_integration_device_pnp=false; -bool enable_integration_device=false; -bool ISAPNPBIOS=false; -bool APMBIOS=false; -bool APMBIOS_pnp=false; -bool APMBIOS_allow_realmode=false; -bool APMBIOS_allow_prot16=false; -bool APMBIOS_allow_prot32=false; -int APMBIOS_connect_mode=0; - -enum { - APMBIOS_CONNECT_REAL=0, - APMBIOS_CONNECT_PROT16, - APMBIOS_CONNECT_PROT32 -}; - -unsigned int APMBIOS_connected_already_err() { - switch (APMBIOS_connect_mode) { - case APMBIOS_CONNECT_REAL: return 0x02; - case APMBIOS_CONNECT_PROT16: return 0x05; - case APMBIOS_CONNECT_PROT32: return 0x07; - } - - return 0x00; -} - -ISAPnPDevice::ISAPnPDevice() { - CSN = 0; - logical_device = 0; - memset(ident,0,sizeof(ident)); - ident_bp = 0; - ident_2nd = 0; - resource_data_len = 0; - resource_data_pos = 0; - resource_data = NULL; - resource_ident = 0; - alloc_res = NULL; - alloc_write = 0; - alloc_sz = 0; -} - -bool ISAPnPDevice::alloc(size_t sz) { - if (sz == alloc_sz) - return true; - - if (alloc_res == resource_data) { - resource_data_len = 0; - resource_data_pos = 0; - resource_data = NULL; - } - if (alloc_res != NULL) - delete[] alloc_res; - - alloc_res = NULL; - alloc_write = 0; - alloc_sz = 0; - - if (sz == 0) - return true; - if (sz > 65536) - return false; - - alloc_res = new unsigned char[sz]; - if (alloc_res == NULL) return false; - memset(alloc_res,0xFF,sz); - alloc_sz = sz; - return true; -} - -ISAPnPDevice::~ISAPnPDevice() { - alloc(0); -} - -void ISAPnPDevice::begin_write_res() { - if (alloc_res == NULL) return; - - resource_data_pos = 0; - resource_data_len = 0; - resource_data = NULL; - alloc_write = 0; -} - -void ISAPnPDevice::write_byte(const unsigned char c) { - if (alloc_res == NULL || alloc_write >= alloc_sz) return; - alloc_res[alloc_write++] = c; -} - -void ISAPnPDevice::write_begin_SMALLTAG(const ISAPnPDevice::SmallTags stag,unsigned char len) { - if (len >= 8 || (unsigned int)stag >= 0x10) return; - write_byte(((unsigned char)stag << 3) + len); -} - -void ISAPnPDevice::write_begin_LARGETAG(const ISAPnPDevice::LargeTags stag,unsigned int len) { - if (len >= 4096) return; - write_byte(0x80 + ((unsigned char)stag)); - write_byte(len & 0xFF); - write_byte(len >> 8); -} - -void ISAPnPDevice::write_Device_ID(const char c1,const char c2,const char c3,const char c4,const char c5,const char c6,const char c7) { - write_byte((((unsigned char)c1 & 0x1FU) << 2) + (((unsigned char)c2 & 0x18U) >> 3)); - write_byte((((unsigned char)c2 & 0x07U) << 5) + (((unsigned char)c3 & 0x1FU) )); - write_byte((((unsigned char)c4 & 0x0FU) << 4) + (((unsigned char)c5 & 0x0FU) )); - write_byte((((unsigned char)c6 & 0x0FU) << 4) + (((unsigned char)c7 & 0x0FU) )); -} - -void ISAPnPDevice::write_Logical_Device_ID(const char c1,const char c2,const char c3,const char c4,const char c5,const char c6,const char c7) { - write_begin_SMALLTAG(SmallTags::LogicalDeviceID,5); - write_Device_ID(c1,c2,c3,c4,c5,c6,c7); - write_byte(0x00); -} - -void ISAPnPDevice::write_Compatible_Device_ID(const char c1,const char c2,const char c3,const char c4,const char c5,const char c6,const char c7) { - write_begin_SMALLTAG(SmallTags::CompatibleDeviceID,4); - write_Device_ID(c1,c2,c3,c4,c5,c6,c7); -} - -void ISAPnPDevice::write_IRQ_Format(const uint16_t IRQ_mask,const unsigned char IRQ_signal_type) { - bool write_irq_info = (IRQ_signal_type != 0); - - write_begin_SMALLTAG(SmallTags::IRQFormat,write_irq_info?3:2); - write_byte(IRQ_mask & 0xFF); - write_byte(IRQ_mask >> 8); - if (write_irq_info) write_byte(((unsigned char)IRQ_signal_type & 0x0F)); -} - -void ISAPnPDevice::write_DMA_Format(const uint8_t DMA_mask,const unsigned char transfer_type_preference,const bool is_bus_master,const bool byte_mode,const bool word_mode,const unsigned char speed_supported) { - write_begin_SMALLTAG(SmallTags::DMAFormat,2); - write_byte(DMA_mask); - write_byte( - (transfer_type_preference & 0x03) | - (is_bus_master ? 0x04 : 0x00) | - (byte_mode ? 0x08 : 0x00) | - (word_mode ? 0x10 : 0x00) | - ((speed_supported & 3) << 5)); -} - -void ISAPnPDevice::write_IO_Port(const uint16_t min_port,const uint16_t max_port,const uint8_t count,const uint8_t alignment,const bool full16bitdecode) { - write_begin_SMALLTAG(SmallTags::IOPortDescriptor,7); - write_byte((full16bitdecode ? 0x01 : 0x00)); - write_byte(min_port & 0xFF); - write_byte(min_port >> 8); - write_byte(max_port & 0xFF); - write_byte(max_port >> 8); - write_byte(alignment); - write_byte(count); -} - -void ISAPnPDevice::write_Dependent_Function_Start(const ISAPnPDevice::DependentFunctionConfig cfg,const bool force) { - bool write_cfg_byte = force || (cfg != ISAPnPDevice::DependentFunctionConfig::AcceptableDependentConfiguration); - - write_begin_SMALLTAG(SmallTags::StartDependentFunctions,write_cfg_byte ? 1 : 0); - if (write_cfg_byte) write_byte((unsigned char)cfg); -} - -void ISAPnPDevice::write_End_Dependent_Functions() { - write_begin_SMALLTAG(SmallTags::EndDependentFunctions,0); -} - -void ISAPnPDevice::write_nstring(const char *str,const size_t l) { - (void)l; - - if (alloc_res == NULL || alloc_write >= alloc_sz) return; - - while (*str != 0 && alloc_write < alloc_sz) - alloc_res[alloc_write++] = (unsigned char)(*str++); -} - -void ISAPnPDevice::write_Identifier_String(const char *str) { - const size_t l = strlen(str); - if (l > 4096) return; - - write_begin_LARGETAG(LargeTags::IdentifierStringANSI,(unsigned int)l); - if (l != 0) write_nstring(str,l); -} - -void ISAPnPDevice::write_ISAPnP_version(unsigned char major,unsigned char minor,unsigned char vendor) { - write_begin_SMALLTAG(SmallTags::PlugAndPlayVersionNumber,2); - write_byte((major << 4) + minor); - write_byte(vendor); -} - -void ISAPnPDevice::write_END() { - unsigned char sum = 0; - size_t i; - - write_begin_SMALLTAG(SmallTags::EndTag,/*length*/1); - - for (i=0;i < alloc_write;i++) sum += alloc_res[i]; - write_byte((0x100 - sum) & 0xFF); -} - -void ISAPnPDevice::end_write_res() { - if (alloc_res == NULL) return; - - write_END(); - - if (alloc_write >= alloc_sz) LOG(LOG_MISC,LOG_WARN)("ISA PNP generation overflow"); - - resource_data_pos = 0; - resource_data_len = alloc_sz; // the device usually has a reason for allocating the fixed size it does - resource_data = alloc_res; - alloc_write = 0; -} - -void ISAPnPDevice::config(Bitu val) { - (void)val; -} - -void ISAPnPDevice::wakecsn(Bitu val) { - (void)val; - ident_bp = 0; - ident_2nd = 0; - resource_data_pos = 0; - resource_ident = 0; -} - -void ISAPnPDevice::select_logical_device(Bitu val) { - (void)val; -} - -void ISAPnPDevice::checksum_ident() { - unsigned char checksum = 0x6a,bit; - int i,j; - - for (i=0;i < 8;i++) { - for (j=0;j < 8;j++) { - bit = (ident[i] >> j) & 1; - checksum = ((((checksum ^ (checksum >> 1)) & 1) ^ bit) << 7) | (checksum >> 1); - } - } - - ident[8] = checksum; -} - -void ISAPnPDevice::on_pnp_key() { - resource_ident = 0; -} - -uint8_t ISAPnPDevice::read(Bitu addr) { - (void)addr; - return 0x00; -} - -void ISAPnPDevice::write(Bitu addr,Bitu val) { - (void)addr; - (void)val; -} - -#define MAX_ISA_PNP_DEVICES 64 -#define MAX_ISA_PNP_SYSDEVNODES 256 - -static ISAPnPDevice *ISA_PNP_selected = NULL; -static ISAPnPDevice *ISA_PNP_devs[MAX_ISA_PNP_DEVICES] = {NULL}; /* FIXME: free objects on shutdown */ -static Bitu ISA_PNP_devnext = 0; - -static const unsigned char ISAPnPIntegrationDevice_sysdev[] = { - ISAPNP_IO_RANGE( - 0x01, /* decodes 16-bit ISA addr */ - 0x28,0x28, /* min-max range I/O port */ - 0x04,0x04), /* align=4 length=4 */ - ISAPNP_END -}; - -class ISAPnPIntegrationDevice : public ISAPnPDevice { - public: - ISAPnPIntegrationDevice() : ISAPnPDevice() { - resource_ident = 0; - resource_data = (unsigned char*)ISAPnPIntegrationDevice_sysdev; - resource_data_len = sizeof(ISAPnPIntegrationDevice_sysdev); - host_writed(ident+0,ISAPNP_ID('D','O','S',0x1,0x2,0x3,0x4)); /* DOS1234 test device */ - host_writed(ident+4,0xFFFFFFFFUL); - checksum_ident(); - } -}; - -ISAPnPIntegrationDevice *isapnpigdevice = NULL; - -class ISAPNP_SysDevNode { -public: - ISAPNP_SysDevNode(const unsigned char *ir,size_t len,bool already_alloc=false) { - if (already_alloc) { - raw = (unsigned char*)ir; - raw_len = len; - own = false; - } - else { - if (len > 65535) E_Exit("ISAPNP_SysDevNode data too long"); - raw = new unsigned char[(size_t)len+1u]; - if (ir == NULL) E_Exit("ISAPNP_SysDevNode cannot allocate buffer"); - memcpy(raw,ir,(size_t)len); - raw_len = len; - raw[len] = 0; - own = true; - } - } - virtual ~ISAPNP_SysDevNode() { - if (own) delete[] raw; - } -public: - unsigned char* raw; - size_t raw_len; - bool own; -}; - -static ISAPNP_SysDevNode* ISAPNP_SysDevNodes[MAX_ISA_PNP_SYSDEVNODES] = {NULL}; -static Bitu ISAPNP_SysDevNodeLargest=0; -static Bitu ISAPNP_SysDevNodeCount=0; - -void ISA_PNP_FreeAllSysNodeDevs() { - Bitu i; - - for (i=0;i < MAX_ISA_PNP_SYSDEVNODES;i++) { - if (ISAPNP_SysDevNodes[i] != NULL) delete ISAPNP_SysDevNodes[i]; - ISAPNP_SysDevNodes[i] = NULL; - } - - ISAPNP_SysDevNodeLargest=0; - ISAPNP_SysDevNodeCount=0; -} - -void ISA_PNP_FreeAllDevs() { - Bitu i; - - for (i=0;i < MAX_ISA_PNP_DEVICES;i++) { - if (ISA_PNP_devs[i] != NULL) { - delete ISA_PNP_devs[i]; - ISA_PNP_devs[i] = NULL; - } - } - for (i=0;i < MAX_ISA_PNP_SYSDEVNODES;i++) { - if (ISAPNP_SysDevNodes[i] != NULL) delete ISAPNP_SysDevNodes[i]; - ISAPNP_SysDevNodes[i] = NULL; - } - - ISAPNP_SysDevNodeLargest=0; - ISAPNP_SysDevNodeCount=0; -} - -void ISA_PNP_devreg(ISAPnPDevice *x) { - if (ISA_PNP_devnext < MAX_ISA_PNP_DEVICES) { - if (ISA_PNP_WPORT_BIOS == 0) ISA_PNP_WPORT_BIOS = ISA_PNP_WPORT; - ISA_PNP_devs[ISA_PNP_devnext++] = x; - x->CSN = ISA_PNP_devnext; - } -} - -static Bitu isapnp_read_port(Bitu port,Bitu /*iolen*/) { - (void)port;//UNUSED - Bitu ret=0xff; - - switch (ISA_PNP_CUR_ADDR) { - case 0x01: /* serial isolation */ - if (ISA_PNP_selected && ISA_PNP_selected->CSN == 0) { - if (ISA_PNP_selected->ident_bp < 72) { - if (ISA_PNP_selected->ident[ISA_PNP_selected->ident_bp>>3] & (1 << (ISA_PNP_selected->ident_bp&7))) - ret = ISA_PNP_selected->ident_2nd ? 0xAA : 0x55; - else - ret = 0xFF; - - if (++ISA_PNP_selected->ident_2nd >= 2) { - ISA_PNP_selected->ident_2nd = 0; - ISA_PNP_selected->ident_bp++; - } - } - } - else { - ret = 0xFF; - } - break; - case 0x04: /* read resource data */ - if (ISA_PNP_selected) { - if (ISA_PNP_selected->resource_ident < 9) - ret = ISA_PNP_selected->ident[ISA_PNP_selected->resource_ident++]; - else { - /* real-world hardware testing shows that devices act as if there was some fixed block of ROM, - * that repeats every 128, 256, 512, or 1024 bytes if you just blindly read from this port. */ - if (ISA_PNP_selected->resource_data_pos < ISA_PNP_selected->resource_data_len) - ret = ISA_PNP_selected->resource_data[ISA_PNP_selected->resource_data_pos++]; - - /* that means that if you read enough bytes the ROM loops back to returning the ident */ - if (ISA_PNP_selected->resource_data_pos >= ISA_PNP_selected->resource_data_len) { - ISA_PNP_selected->resource_data_pos = 0; - ISA_PNP_selected->resource_ident = 0; - } - } - } - break; - case 0x05: /* read resource status */ - if (ISA_PNP_selected) { - /* real-world hardware testing shows that devices act as if there was some fixed block of ROM, - * that repeats every 128, 256, 512, or 1024 bytes if you just blindly read from this port. - * therefore, there's always a byte to return. */ - ret = 0x01; /* TODO: simulate hardware slowness */ - } - break; - case 0x06: /* card select number */ - if (ISA_PNP_selected) ret = ISA_PNP_selected->CSN; - break; - case 0x07: /* logical device number */ - if (ISA_PNP_selected) ret = ISA_PNP_selected->logical_device; - break; - default: /* pass the rest down to the class */ - if (ISA_PNP_selected) ret = ISA_PNP_selected->read(ISA_PNP_CUR_ADDR); - break; - } - -// if (1) LOG_MSG("PnP read(%02X) = %02X\n",ISA_PNP_CUR_ADDR,ret); - return ret; -} - -void isapnp_write_port(Bitu port,Bitu val,Bitu /*iolen*/) { - Bitu i; - - if (port == 0x279) { -// if (1) LOG_MSG("PnP addr(%02X)\n",val); - if (val == isa_pnp_init_keystring[ISA_PNP_KEYMATCH]) { - if (++ISA_PNP_KEYMATCH == 32) { -// LOG_MSG("ISA PnP key -> going to sleep\n"); - ISA_PNP_CUR_STATE = ISA_PNP_SLEEP; - ISA_PNP_KEYMATCH = 0; - for (i=0;i < MAX_ISA_PNP_DEVICES;i++) { - if (ISA_PNP_devs[i] != NULL) { - ISA_PNP_devs[i]->on_pnp_key(); - } - } - } - } - else { - ISA_PNP_KEYMATCH = 0; - } - - ISA_PNP_CUR_ADDR = val; - } - else if (port == 0xA79) { -// if (1) LOG_MSG("PnP write(%02X) = %02X\n",ISA_PNP_CUR_ADDR,val); - switch (ISA_PNP_CUR_ADDR) { - case 0x00: { /* RD_DATA */ - unsigned int np = ((val & 0xFF) << 2) | 3; - if (np != ISA_PNP_WPORT) { - if (ISAPNP_PNP_READ_PORT != NULL) { - ISAPNP_PNP_READ_PORT = NULL; - delete ISAPNP_PNP_READ_PORT; - } - - if (np >= 0x200 && np <= 0x3FF) { /* allowable port I/O range according to spec */ - LOG_MSG("PNP OS changed I/O read port to 0x%03X (from 0x%03X)\n",np,ISA_PNP_WPORT); - - ISA_PNP_WPORT = np; - ISAPNP_PNP_READ_PORT = new IO_ReadHandleObject; - ISAPNP_PNP_READ_PORT->Install(ISA_PNP_WPORT,isapnp_read_port,IO_MB); - } - else { - LOG_MSG("PNP OS I/O read port disabled\n"); - - ISA_PNP_WPORT = 0; - } - - if (ISA_PNP_selected != NULL) { - ISA_PNP_selected->ident_bp = 0; - ISA_PNP_selected->ident_2nd = 0; - ISA_PNP_selected->resource_data_pos = 0; - } - } - } break; - case 0x02: /* config control */ - if (val & 4) { - /* ALL CARDS RESET CSN to 0 */ - for (i=0;i < MAX_ISA_PNP_DEVICES;i++) { - if (ISA_PNP_devs[i] != NULL) { - ISA_PNP_devs[i]->CSN = 0; - } - } - } - if (val & 2) ISA_PNP_CUR_STATE = ISA_PNP_WAIT_FOR_KEY; - if ((val & 1) && ISA_PNP_selected) ISA_PNP_selected->config(val); - for (i=0;i < MAX_ISA_PNP_DEVICES;i++) { - if (ISA_PNP_devs[i] != NULL) { - ISA_PNP_devs[i]->ident_bp = 0; - ISA_PNP_devs[i]->ident_2nd = 0; - ISA_PNP_devs[i]->resource_data_pos = 0; - } - } - break; - case 0x03: { /* wake[CSN] */ - ISA_PNP_selected = NULL; - for (i=0;ISA_PNP_selected == NULL && i < MAX_ISA_PNP_DEVICES;i++) { - if (ISA_PNP_devs[i] == NULL) - continue; - if (ISA_PNP_devs[i]->CSN == val) { - ISA_PNP_selected = ISA_PNP_devs[i]; - ISA_PNP_selected->wakecsn(val); - } - } - if (val == 0) - ISA_PNP_CUR_STATE = ISA_PNP_ISOLATE; - else - ISA_PNP_CUR_STATE = ISA_PNP_CONFIG; - } break; - case 0x06: /* card select number */ - if (ISA_PNP_selected) ISA_PNP_selected->CSN = val; - break; - case 0x07: /* logical device number */ - if (ISA_PNP_selected) ISA_PNP_selected->select_logical_device(val); - break; - default: /* pass the rest down to the class */ - if (ISA_PNP_selected) ISA_PNP_selected->write(ISA_PNP_CUR_ADDR,val); - break; - } - } -} - -static Bitu INT15_Handler(void); - -// FIXME: This initializes both APM BIOS and ISA PNP emulation! -// Need to separate APM BIOS init from ISA PNP init from ISA PNP BIOS init! -// It might also be appropriate to move this into the BIOS init sequence. -void ISAPNP_Cfg_Reset(Section *sec) { - (void)sec;//UNUSED - Section_prop * section=static_cast(control->GetSection("cpu")); - - LOG(LOG_MISC,LOG_DEBUG)("Initializing ISA PnP emulation"); - - enable_integration_device = section->Get_bool("integration device"); - enable_integration_device_pnp = section->Get_bool("integration device pnp"); - ISAPNPBIOS = section->Get_bool("isapnpbios"); - APMBIOS = section->Get_bool("apmbios"); - APMBIOS_pnp = section->Get_bool("apmbios pnp"); - APMBIOS_allow_realmode = section->Get_bool("apmbios allow realmode"); - APMBIOS_allow_prot16 = section->Get_bool("apmbios allow 16-bit protected mode"); - APMBIOS_allow_prot32 = section->Get_bool("apmbios allow 32-bit protected mode"); - - std::string apmbiosver = section->Get_string("apmbios version"); - - /* PC-98 does not have the IBM PC/AT APM BIOS interface */ - if (IS_PC98_ARCH) { - APMBIOS = false; - APMBIOS_pnp = false; - } - - if (apmbiosver == "1.0") - APM_BIOS_minor_version = 0; - else if (apmbiosver == "1.1") - APM_BIOS_minor_version = 1; - else if (apmbiosver == "1.2") - APM_BIOS_minor_version = 2; - else//auto - APM_BIOS_minor_version = 2; - - /* PC-98 does not have APM. - * I *think* it has Plug & Play, but probably different from ISA PnP and specific to the C-Bus interface, - * which I have no information on at this time --J.C. */ - if (IS_PC98_ARCH) - return; - - LOG(LOG_MISC,LOG_DEBUG)("APM BIOS allow: real=%u pm16=%u pm32=%u version=1.%u", - APMBIOS_allow_realmode, - APMBIOS_allow_prot16, - APMBIOS_allow_prot32, - APM_BIOS_minor_version); - - if (APMBIOS && (APMBIOS_allow_prot16 || APMBIOS_allow_prot32) && INT15_apm_pmentry == 0) { - Bitu cb,base; - - /* NTS: This is... kind of a terrible hack. It basically tricks Windows into executing our - * INT 15h handler as if the APM entry point. Except that instead of an actual INT 15h - * triggering the callback, a FAR CALL triggers the callback instead (CB_RETF not CB_IRET). */ - /* TODO: We should really consider moving the APM BIOS code in INT15_Handler() out into it's - * own function, then having the INT15_Handler() call it as well as directing this callback - * directly to it. If you think about it, this hack also lets the "APM entry point" invoke - * other arbitrary INT 15h calls which is not valid. */ - - cb = CALLBACK_Allocate(); - INT15_apm_pmentry = CALLBACK_RealPointer(cb); - LOG_MSG("Allocated APM BIOS pm entry point at %04x:%04x\n",INT15_apm_pmentry>>16,INT15_apm_pmentry&0xFFFF); - CALLBACK_Setup(cb,INT15_Handler,CB_RETF,"APM BIOS protected mode entry point"); - - /* NTS: Actually INT15_Handler is written to act like an interrupt (IRETF) type callback. - * Prior versions hacked this into something that responds by CB_RETF, however some - * poking around reveals that CALLBACK_SCF and friends still assume an interrupt - * stack, thus, the cause of random crashes in Windows was simply that we were - * flipping flag bits in the middle of the return address on the stack. The other - * source of random crashes is that the CF/ZF manipulation in INT 15h wasn't making - * it's way back to Windows, meaning that when APM BIOS emulation intended to return - * an error (by setting CF), Windows didn't get the memo (CF wasn't set on return) - * and acted as if the call succeeded, or worse, CF happened to be set on entry and - * was never cleared by APM BIOS emulation. - * - * So what we need is: - * - * PUSHF ; put flags in right place - * PUSH BP ; dummy FAR pointer - * PUSH BP ; again - * - * POP BP ; drop it - * POP BP ; drop it - * POPF - * RETF - * - * Then CALLBACK_SCF can work normally this way. - * - * NTS: We *still* need to separate APM BIOS calls from the general INT 15H emulation though... */ - base = Real2Phys(INT15_apm_pmentry); - LOG_MSG("Writing code to %05x\n",(unsigned int)base); - - phys_writeb(base+0x00,0x9C); /* pushf */ - phys_writeb(base+0x01,0x55); /* push (e)bp */ - phys_writeb(base+0x02,0x55); /* push (e)bp */ - - phys_writeb(base+0x03,(Bit8u)0xFE); //GRP 4 - phys_writeb(base+0x04,(Bit8u)0x38); //Extra Callback instruction - phys_writew(base+0x05,(Bit16u)cb); //The immediate word - - phys_writeb(base+0x07,0x5D); /* pop (e)bp */ - phys_writeb(base+0x08,0x5D); /* pop (e)bp */ - phys_writeb(base+0x09,0x9D); /* popf */ - phys_writeb(base+0x0A,0xCB); /* retf */ - } -} - -void ISAPNP_Cfg_Init() { - AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(ISAPNP_Cfg_Reset)); -} - -/* the PnP callback registered two entry points. One for real, one for protected mode. */ -static Bitu PNPentry_real,PNPentry_prot; - -static bool ISAPNP_Verify_BiosSelector(Bitu seg) { - if (!cpu.pmode || (reg_flags & FLAG_VM)) { - return (seg == 0xF000); - } else if (seg == 0) - return 0; - else { -#if 1 - /* FIXME: Always return true. But figure out how to ask DOSBox the linear->phys - mapping to determine whether the segment's base address maps to 0xF0000. - In the meantime disabling this check makes PnP BIOS emulation work with - Windows 95 OSR2 which appears to give us a segment mapped to a virtual - address rather than linearly mapped to 0xF0000 as Windows 95 original - did. */ - return true; -#else - Descriptor desc; - cpu.gdt.GetDescriptor(seg,desc); - - /* TODO: Check desc.Type() to make sure it's a writeable data segment */ - return (desc.GetBase() == 0xF0000); -#endif - } -} - -static bool ISAPNP_CPU_ProtMode() { - if (!cpu.pmode || (reg_flags & FLAG_VM)) - return 0; - - return 1; -} - -static Bitu ISAPNP_xlate_address(Bitu far_ptr) { - if (!cpu.pmode || (reg_flags & FLAG_VM)) - return Real2Phys(far_ptr); - else { - Descriptor desc; - cpu.gdt.GetDescriptor(far_ptr >> 16,desc); - - /* TODO: Check desc.Type() to make sure it's a writeable data segment */ - return (desc.GetBase() + (far_ptr & 0xFFFF)); - } -} - -static const unsigned char ISAPNP_sysdev_Keyboard[] = { - ISAPNP_SYSDEV_HEADER( - ISAPNP_ID('P','N','P',0x0,0x3,0x0,0x3), /* PNP0303 IBM Enhanced 101/102 key with PS/2 */ - ISAPNP_TYPE(0x09,0x00,0x00), /* type: input, keyboard */ - 0x0001 | 0x0002), /* can't disable, can't configure */ - /*----------allocated--------*/ - ISAPNP_IO_RANGE( - 0x01, /* decodes 16-bit ISA addr */ - 0x60,0x60, /* min-max range I/O port */ - 0x01,0x01), /* align=1 length=1 */ - ISAPNP_IO_RANGE( - 0x01, /* decodes 16-bit ISA addr */ - 0x64,0x64, /* min-max range I/O port */ - 0x01,0x01), /* align=1 length=1 */ - ISAPNP_IRQ_SINGLE( - 1, /* IRQ 1 */ - 0x09), /* HTE=1 LTL=1 */ - ISAPNP_END, - /*----------possible--------*/ - ISAPNP_END, - /*----------compatible--------*/ - ISAPNP_END -}; - -static const unsigned char ISAPNP_sysdev_Mouse[] = { - ISAPNP_SYSDEV_HEADER( - ISAPNP_ID('P','N','P',0x0,0xF,0x0,0xE), /* PNP0F0E Microsoft compatible PS/2 */ - ISAPNP_TYPE(0x09,0x02,0x00), /* type: input, keyboard */ - 0x0001 | 0x0002), /* can't disable, can't configure */ - /*----------allocated--------*/ - ISAPNP_IRQ_SINGLE( - 12, /* IRQ 12 */ - 0x09), /* HTE=1 LTL=1 */ - ISAPNP_END, - /*----------possible--------*/ - ISAPNP_END, - /*----------compatible--------*/ - ISAPNP_END -}; - -static const unsigned char ISAPNP_sysdev_DMA_Controller[] = { - ISAPNP_SYSDEV_HEADER( - ISAPNP_ID('P','N','P',0x0,0x2,0x0,0x0), /* PNP0200 AT DMA controller */ - ISAPNP_TYPE(0x08,0x01,0x00), /* type: input, keyboard */ - 0x0001 | 0x0002), /* can't disable, can't configure */ - /*----------allocated--------*/ - ISAPNP_IO_RANGE( - 0x01, /* decodes 16-bit ISA addr */ - 0x00,0x00, /* min-max range I/O port (DMA channels 0-3) */ - 0x10,0x10), /* align=16 length=16 */ - ISAPNP_IO_RANGE( - 0x01, /* decodes 16-bit ISA addr */ - 0x81,0x81, /* min-max range I/O port (DMA page registers) */ - 0x01,0x0F), /* align=1 length=15 */ - ISAPNP_IO_RANGE( - 0x01, /* decodes 16-bit ISA addr */ - 0xC0,0xC0, /* min-max range I/O port (AT DMA channels 4-7) */ - 0x20,0x20), /* align=32 length=32 */ - ISAPNP_DMA_SINGLE( - 4, /* DMA 4 */ - 0x01), /* 8/16-bit transfers, compatible speed */ - ISAPNP_END, - /*----------possible--------*/ - ISAPNP_END, - /*----------compatible--------*/ - ISAPNP_END -}; - -static const unsigned char ISAPNP_sysdev_PIC[] = { - ISAPNP_SYSDEV_HEADER( - ISAPNP_ID('P','N','P',0x0,0x0,0x0,0x0), /* PNP0000 Interrupt controller */ - ISAPNP_TYPE(0x08,0x00,0x01), /* type: ISA interrupt controller */ - 0x0001 | 0x0002), /* can't disable, can't configure */ - /*----------allocated--------*/ - ISAPNP_IO_RANGE( - 0x01, /* decodes 16-bit ISA addr */ - 0x20,0x20, /* min-max range I/O port */ - 0x01,0x02), /* align=1 length=2 */ - ISAPNP_IO_RANGE( - 0x01, /* decodes 16-bit ISA addr */ - 0xA0,0xA0, /* min-max range I/O port */ - 0x01,0x02), /* align=1 length=2 */ - ISAPNP_IRQ_SINGLE( - 2, /* IRQ 2 */ - 0x09), /* HTE=1 LTL=1 */ - ISAPNP_END, - /*----------possible--------*/ - ISAPNP_END, - /*----------compatible--------*/ - ISAPNP_END -}; - -static const unsigned char ISAPNP_sysdev_Timer[] = { - ISAPNP_SYSDEV_HEADER( - ISAPNP_ID('P','N','P',0x0,0x1,0x0,0x0), /* PNP0100 Timer */ - ISAPNP_TYPE(0x08,0x02,0x01), /* type: ISA timer */ - 0x0001 | 0x0002), /* can't disable, can't configure */ - /*----------allocated--------*/ - ISAPNP_IO_RANGE( - 0x01, /* decodes 16-bit ISA addr */ - 0x40,0x40, /* min-max range I/O port */ - 0x04,0x04), /* align=4 length=4 */ - ISAPNP_IRQ_SINGLE( - 0, /* IRQ 0 */ - 0x09), /* HTE=1 LTL=1 */ - ISAPNP_END, - /*----------possible--------*/ - ISAPNP_END, - /*----------compatible--------*/ - ISAPNP_END -}; - -static const unsigned char ISAPNP_sysdev_RTC[] = { - ISAPNP_SYSDEV_HEADER( - ISAPNP_ID('P','N','P',0x0,0xB,0x0,0x0), /* PNP0B00 Real-time clock */ - ISAPNP_TYPE(0x08,0x03,0x01), /* type: ISA real-time clock */ - 0x0001 | 0x0002), /* can't disable, can't configure */ - /*----------allocated--------*/ - ISAPNP_IO_RANGE( - 0x01, /* decodes 16-bit ISA addr */ - 0x70,0x70, /* min-max range I/O port */ - 0x01,0x02), /* align=1 length=2 */ - ISAPNP_IRQ_SINGLE( - 8, /* IRQ 8 */ - 0x09), /* HTE=1 LTL=1 */ - ISAPNP_END, - /*----------possible--------*/ - ISAPNP_END, - /*----------compatible--------*/ - ISAPNP_END -}; - -static const unsigned char ISAPNP_sysdev_PC_Speaker[] = { - ISAPNP_SYSDEV_HEADER( - ISAPNP_ID('P','N','P',0x0,0x8,0x0,0x0), /* PNP0800 PC speaker */ - ISAPNP_TYPE(0x04,0x01,0x00), /* type: PC speaker */ - 0x0001 | 0x0002), /* can't disable, can't configure */ - /*----------allocated--------*/ - ISAPNP_IO_RANGE( - 0x01, /* decodes 16-bit ISA addr */ - 0x61,0x61, /* min-max range I/O port */ - 0x01,0x01), /* align=1 length=1 */ - ISAPNP_END, - /*----------possible--------*/ - ISAPNP_END, - /*----------compatible--------*/ - ISAPNP_END -}; - -static const unsigned char ISAPNP_sysdev_Numeric_Coprocessor[] = { - ISAPNP_SYSDEV_HEADER( - ISAPNP_ID('P','N','P',0x0,0xC,0x0,0x4), /* PNP0C04 Numeric Coprocessor */ - ISAPNP_TYPE(0x0B,0x80,0x00), /* type: FPU */ - 0x0001 | 0x0002), /* can't disable, can't configure */ - /*----------allocated--------*/ - ISAPNP_IO_RANGE( - 0x01, /* decodes 16-bit ISA addr */ - 0xF0,0xF0, /* min-max range I/O port */ - 0x10,0x10), /* align=16 length=16 */ - ISAPNP_IRQ_SINGLE( - 13, /* IRQ 13 */ - 0x09), /* HTE=1 LTL=1 */ - ISAPNP_END, - /*----------possible--------*/ - ISAPNP_END, - /*----------compatible--------*/ - ISAPNP_END -}; - -static const unsigned char ISAPNP_sysdev_System_Board[] = { - ISAPNP_SYSDEV_HEADER( - ISAPNP_ID('P','N','P',0x0,0xC,0x0,0x1), /* PNP0C01 System board */ - ISAPNP_TYPE(0x08,0x80,0x00), /* type: System peripheral, Other */ - 0x0001 | 0x0002), /* can't disable, can't configure */ - /*----------allocated--------*/ - ISAPNP_IO_RANGE( - 0x01, /* decodes 16-bit ISA addr */ - 0x24,0x24, /* min-max range I/O port */ - 0x04,0x04), /* align=4 length=4 */ - ISAPNP_END, - /*----------possible--------*/ - ISAPNP_END, - /*----------compatible--------*/ - ISAPNP_END -}; - -/* NTS: If some of my late 1990's laptops are any indication, this resource list can be used - * as a hint that the motherboard supports Intel EISA/PCI controller DMA registers that - * allow ISA DMA to extend to 32-bit addresses instead of being limited to 24-bit */ -static const unsigned char ISAPNP_sysdev_General_ISAPNP[] = { - ISAPNP_SYSDEV_HEADER( - ISAPNP_ID('P','N','P',0x0,0xC,0x0,0x2), /* PNP0C02 General ID for reserving resources */ - ISAPNP_TYPE(0x08,0x80,0x00), /* type: System peripheral, Other */ - 0x0001 | 0x0002), /* can't disable, can't configure */ - /*----------allocated--------*/ - ISAPNP_IO_RANGE( - 0x01, /* decodes 16-bit ISA addr */ - 0x208,0x208, /* min-max range I/O port */ - 0x04,0x04), /* align=4 length=4 */ - ISAPNP_END, - /*----------possible--------*/ - ISAPNP_END, - /*----------compatible--------*/ - ISAPNP_END -}; - -/* PnP system entry to tell Windows 95 the obvious: That there's an ISA bus present */ -/* NTS: Examination of some old laptops of mine shows that these devices do not list any resources, - * or at least, an old Toshiba of mine lists the PCI registers 0xCF8-0xCFF as motherboard resources - * and defines no resources for the PCI Bus PnP device. */ -static const unsigned char ISAPNP_sysdev_ISA_BUS[] = { - ISAPNP_SYSDEV_HEADER( - ISAPNP_ID('P','N','P',0x0,0xA,0x0,0x0), /* PNP0A00 ISA Bus */ - ISAPNP_TYPE(0x06,0x04,0x00), /* type: System device, peripheral bus */ - 0x0001 | 0x0002), /* can't disable, can't configure */ - /*----------allocated--------*/ - ISAPNP_END, - /*----------possible--------*/ - ISAPNP_END, - /*----------compatible--------*/ - ISAPNP_END -}; - -/* PnP system entry to tell Windows 95 the obvious: That there's a PCI bus present */ -static const unsigned char ISAPNP_sysdev_PCI_BUS[] = { - ISAPNP_SYSDEV_HEADER( - ISAPNP_ID('P','N','P',0x0,0xA,0x0,0x3), /* PNP0A03 PCI Bus */ - ISAPNP_TYPE(0x06,0x04,0x00), /* type: System device, peripheral bus */ - 0x0001 | 0x0002), /* can't disable, can't configure */ - /*----------allocated--------*/ - ISAPNP_END, - /*----------possible--------*/ - ISAPNP_END, - /*----------compatible--------*/ - ISAPNP_END -}; - -/* to help convince Windows 95 that the APM BIOS is present */ -static const unsigned char ISAPNP_sysdev_APM_BIOS[] = { - ISAPNP_SYSDEV_HEADER( - ISAPNP_ID('P','N','P',0x0,0xC,0x0,0x5), /* PNP0C05 APM BIOS */ - ISAPNP_TYPE(0x08,0x80,0x00), /* type: FIXME is this right?? I can't find any examples or documentation */ - 0x0001 | 0x0002), /* can't disable, can't configure */ - /*----------allocated--------*/ - ISAPNP_END, - /*----------possible--------*/ - ISAPNP_END, - /*----------compatible--------*/ - ISAPNP_END -}; - -bool ISAPNP_RegisterSysDev(const unsigned char *raw,Bitu len,bool already) { - if (ISAPNP_SysDevNodeCount >= MAX_ISA_PNP_SYSDEVNODES) - return false; - - ISAPNP_SysDevNodes[ISAPNP_SysDevNodeCount] = new ISAPNP_SysDevNode(raw,len,already); - if (ISAPNP_SysDevNodes[ISAPNP_SysDevNodeCount] == NULL) - return false; - - ISAPNP_SysDevNodeCount++; - if (ISAPNP_SysDevNodeLargest < (len+3)) - ISAPNP_SysDevNodeLargest = len+3; - - return true; -} - -/* ISA PnP function calls have their parameters stored on the stack "C" __cdecl style. Parameters - * are either int, long, or FAR pointers. Like __cdecl an assembly language implementation pushes - * the function arguments on the stack BACKWARDS */ -static Bitu ISAPNP_Handler(bool protmode /* called from protected mode interface == true */) { - Bitu arg; - Bitu func,BiosSelector; - - /* I like how the ISA PnP spec says that the 16-bit entry points (real and protected) are given 16-bit data segments - * which implies that all segments involved might as well be 16-bit. - * - * Right? - * - * Well, guess what Windows 95 gives us when calling this entry point: - * - * Segment SS = DS = 0x30 base=0 limit=0xFFFFFFFF - * SS:SP = 0x30:0xC138BADF or something like that from within BIOS.VXD - * - * Yeah... for a 16-bit code segment call. Right. Typical Microsoft. >:( - * - * This might also explain why my early experiments with Bochs always had the perpetual - * APM BIOS that never worked but was always detected. - * - * ------------------------------------------------------------------------ - * Windows 95 OSR2: - * - * Windows 95 OSR2 however uses a 16-bit stack (where the stack segment is based somewhere - * around 0xC1xxxxxx), all we have to do to correctly access it is work through the page tables. - * This is within spec, but now Microsoft sends us a data segment that is based at virtual address - * 0xC2xxxxxx, which is why I had to disable the "verify selector" routine */ - arg = SegPhys(ss) + (reg_esp&cpu.stack.mask) + (2*2); /* entry point (real and protected) is 16-bit, expected to RETF (skip CS:IP) */ - - if (protmode != ISAPNP_CPU_ProtMode()) { - //LOG_MSG("ISA PnP %s entry point called from %s. On real BIOSes this would CRASH\n",protmode ? "Protected mode" : "Real mode", - // ISAPNP_CPU_ProtMode() ? "Protected mode" : "Real mode"); - reg_ax = 0x84;/* BAD_PARAMETER */ - return 0; - } - - func = mem_readw(arg); -// LOG_MSG("PnP prot=%u DS=%04x (base=0x%08lx) SS:ESP=%04x:%04x (base=0x%08lx phys=0x%08lx) function=0x%04x\n", -// (unsigned int)protmode,(unsigned int)SegValue(ds),(unsigned long)SegPhys(ds), -// (unsigned int)SegValue(ss),(unsigned int)reg_esp,(unsigned long)SegPhys(ss), -// (unsigned long)arg,(unsigned int)func); - - /* every function takes the form - * - * int __cdecl FAR (*entrypoint)(int Function...); - * - * so the first argument on the stack is an int that we read to determine what the caller is asking - * - * Dont forget in the real-mode world: - * sizeof(int) == 16 bits - * sizeof(long) == 32 bits - */ - switch (func) { - case 0: { /* Get Number of System Nodes */ - /* int __cdecl FAR (*entrypoint)(int Function,unsigned char FAR *NumNodes,unsigned int FAR *NodeSize,unsigned int BiosSelector); - * ^ +0 ^ +2 ^ +6 ^ +10 = 12 */ - Bitu NumNodes_ptr = mem_readd(arg+2); - Bitu NodeSize_ptr = mem_readd(arg+6); - BiosSelector = mem_readw(arg+10); - - if (!ISAPNP_Verify_BiosSelector(BiosSelector)) - goto badBiosSelector; - - if (NumNodes_ptr != 0) mem_writeb(ISAPNP_xlate_address(NumNodes_ptr),ISAPNP_SysDevNodeCount); - if (NodeSize_ptr != 0) mem_writew(ISAPNP_xlate_address(NodeSize_ptr),ISAPNP_SysDevNodeLargest); - - reg_ax = 0x00;/* SUCCESS */ - } break; - case 1: { /* Get System Device Node */ - /* int __cdecl FAR (*entrypoint)(int Function,unsigned char FAR *Node,struct DEV_NODE FAR *devNodeBuffer,unsigned int Control,unsigned int BiosSelector); - * ^ +0 ^ +2 ^ +6 ^ +10 ^ +12 = 14 */ - Bitu Node_ptr = mem_readd(arg+2); - Bitu devNodeBuffer_ptr = mem_readd(arg+6); - Bitu Control = mem_readw(arg+10); - BiosSelector = mem_readw(arg+12); - unsigned char Node; - Bitu i=0; - - if (!ISAPNP_Verify_BiosSelector(BiosSelector)) - goto badBiosSelector; - - /* control bits 0-1 must be '01' or '10' but not '00' or '11' */ - if (Control == 0 || (Control&3) == 3) { - LOG_MSG("ISAPNP Get System Device Node: Invalid Control value 0x%04x\n",(int)Control); - reg_ax = 0x84;/* BAD_PARAMETER */ - break; - } - - devNodeBuffer_ptr = ISAPNP_xlate_address(devNodeBuffer_ptr); - Node_ptr = ISAPNP_xlate_address(Node_ptr); - Node = mem_readb(Node_ptr); - if (Node >= ISAPNP_SysDevNodeCount) { - LOG_MSG("ISAPNP Get System Device Node: Invalid Node 0x%02x (max 0x%04x)\n",(int)Node,(int)ISAPNP_SysDevNodeCount); - reg_ax = 0x84;/* BAD_PARAMETER */ - break; - } - - ISAPNP_SysDevNode *nd = ISAPNP_SysDevNodes[Node]; - - mem_writew(devNodeBuffer_ptr+0,(Bit16u)(nd->raw_len+3)); /* Length */ - mem_writeb(devNodeBuffer_ptr+2,Node); /* on most PnP BIOS implementations I've seen "handle" is set to the same value as Node */ - for (i=0;i < (Bitu)nd->raw_len;i++) - mem_writeb(devNodeBuffer_ptr+i+3,nd->raw[i]); - -// LOG_MSG("ISAPNP OS asked for Node 0x%02x\n",Node); - - if (++Node >= ISAPNP_SysDevNodeCount) Node = 0xFF; /* no more nodes */ - mem_writeb(Node_ptr,Node); - - reg_ax = 0x00;/* SUCCESS */ - } break; - case 4: { /* Send Message */ - /* int __cdecl FAR (*entrypoint)(int Function,unsigned int Message,unsigned int BiosSelector); - * ^ +0 ^ +2 ^ +4 = 6 */ - Bitu Message = mem_readw(arg+2); - BiosSelector = mem_readw(arg+4); - - if (!ISAPNP_Verify_BiosSelector(BiosSelector)) - goto badBiosSelector; - - switch (Message) { - case 0x41: /* POWER_OFF */ - LOG_MSG("Plug & Play OS requested power off.\n"); - throw 1; /* NTS: Based on the Reboot handler code, causes DOSBox to cleanly shutdown and exit */ - reg_ax = 0; - break; - case 0x42: /* PNP_OS_ACTIVE */ - LOG_MSG("Plug & Play OS reports itself active\n"); - reg_ax = 0; - break; - case 0x43: /* PNP_OS_INACTIVE */ - LOG_MSG("Plug & Play OS reports itself inactive\n"); - reg_ax = 0; - break; - default: - LOG_MSG("Unknown ISA PnP message 0x%04x\n",(int)Message); - reg_ax = 0x82;/* FUNCTION_NOT_SUPPORTED */ - break; - } - } break; - case 0x40: { /* Get PnP ISA configuration */ - /* int __cdecl FAR (*entrypoint)(int Function,unsigned char far *struct,unsigned int BiosSelector); - * ^ +0 ^ +2 ^ +6 = 8 */ - Bitu struct_ptr = mem_readd(arg+2); - BiosSelector = mem_readw(arg+6); - - if (!ISAPNP_Verify_BiosSelector(BiosSelector)) - goto badBiosSelector; - - /* struct isapnp_pnp_isa_cfg { - uint8_t revision; - uint8_t total_csn; - uint16_t isa_pnp_port; - uint16_t reserved; - }; */ - - if (struct_ptr != 0) { - Bitu ph = ISAPNP_xlate_address(struct_ptr); - mem_writeb(ph+0,0x01); /* ->revision = 0x01 */ - mem_writeb(ph+1,ISA_PNP_devnext); /* ->total_csn */ - mem_writew(ph+2,ISA_PNP_WPORT_BIOS); /* ->isa_pnp_port */ - mem_writew(ph+4,0); /* ->reserved */ - } - - reg_ax = 0x00;/* SUCCESS */ - } break; - default: - //LOG_MSG("Unsupported ISA PnP function 0x%04x\n",func); - reg_ax = 0x82;/* FUNCTION_NOT_SUPPORTED */ - break; - }; - - return 0; -badBiosSelector: - /* return an error. remind the user (possible developer) how lucky he is, a real - * BIOS implementation would CRASH when misused like this */ - LOG_MSG("ISA PnP function 0x%04x called with incorrect BiosSelector parameter 0x%04x\n",(int)func,(int)BiosSelector); - LOG_MSG(" > STACK %04X %04X %04X %04X %04X %04X %04X %04X\n", - mem_readw(arg), mem_readw(arg+2), mem_readw(arg+4), mem_readw(arg+6), - mem_readw(arg+8), mem_readw(arg+10), mem_readw(arg+12), mem_readw(arg+14)); - - reg_ax = 0x84;/* BAD_PARAMETER */ - return 0; -} - -static Bitu ISAPNP_Handler_PM(void) { - return ISAPNP_Handler(true); -} - -static Bitu ISAPNP_Handler_RM(void) { - return ISAPNP_Handler(false); -} - -static Bitu INT70_Handler(void) { - /* Acknowledge irq with cmos */ - IO_Write(0x70,0xc); - IO_Read(0x71); - if (mem_readb(BIOS_WAIT_FLAG_ACTIVE)) { - Bit32u count=mem_readd(BIOS_WAIT_FLAG_COUNT); - if (count>997) { - mem_writed(BIOS_WAIT_FLAG_COUNT,count-997); - } else { - mem_writed(BIOS_WAIT_FLAG_COUNT,0); - PhysPt where=Real2Phys(mem_readd(BIOS_WAIT_FLAG_POINTER)); - mem_writeb(where,mem_readb(where)|0x80); - mem_writeb(BIOS_WAIT_FLAG_ACTIVE,0); - mem_writed(BIOS_WAIT_FLAG_POINTER,RealMake(0,BIOS_WAIT_FLAG_TEMP)); - IO_Write(0x70,0xb); - IO_Write(0x71,IO_Read(0x71)&~0x40); - } - } - /* Signal EOI to both pics */ - IO_Write(0xa0,0x20); - IO_Write(0x20,0x20); - return 0; -} - -CALLBACK_HandlerObject* tandy_DAC_callback[2]; -static struct { - Bit16u port; - Bit8u irq; - Bit8u dma; -} tandy_sb; -static struct { - Bit16u port; - Bit8u irq; - Bit8u dma; -} tandy_dac; - -static bool Tandy_InitializeSB() { - /* see if soundblaster module available and at what port/IRQ/DMA */ - Bitu sbport, sbirq, sbdma; - if (SB_Get_Address(sbport, sbirq, sbdma)) { - tandy_sb.port=(Bit16u)(sbport&0xffff); - tandy_sb.irq =(Bit8u)(sbirq&0xff); - tandy_sb.dma =(Bit8u)(sbdma&0xff); - return true; - } else { - /* no soundblaster accessible, disable Tandy DAC */ - tandy_sb.port=0; - return false; - } -} - -static bool Tandy_InitializeTS() { - /* see if Tandy DAC module available and at what port/IRQ/DMA */ - Bitu tsport, tsirq, tsdma; - if (TS_Get_Address(tsport, tsirq, tsdma)) { - tandy_dac.port=(Bit16u)(tsport&0xffff); - tandy_dac.irq =(Bit8u)(tsirq&0xff); - tandy_dac.dma =(Bit8u)(tsdma&0xff); - return true; - } else { - /* no Tandy DAC accessible */ - tandy_dac.port=0; - return false; - } -} - -/* check if Tandy DAC is still playing */ -static bool Tandy_TransferInProgress(void) { - if (real_readw(0x40,0xd0)) return true; /* not yet done */ - if (real_readb(0x40,0xd4)==0xff) return false; /* still in init-state */ - - Bit8u tandy_dma = 1; - if (tandy_sb.port) tandy_dma = tandy_sb.dma; - else if (tandy_dac.port) tandy_dma = tandy_dac.dma; - - IO_Write(0x0c,0x00); - Bit16u datalen=(Bit8u)(IO_ReadB(tandy_dma*2u+1u)&0xffu); - datalen|=(IO_ReadB(tandy_dma*2u+1u)<<8u); - if (datalen==0xffff) return false; /* no DMA transfer */ - else if ((datalen<0x10) && (real_readb(0x40,0xd4)==0x0f) && (real_readw(0x40,0xd2)==0x1c)) { - /* stop already requested */ - return false; - } - return true; -} - -static void Tandy_SetupTransfer(PhysPt bufpt,bool isplayback) { - Bitu length=real_readw(0x40,0xd0); - if (length==0) return; /* nothing to do... */ - - if ((tandy_sb.port==0) && (tandy_dac.port==0)) return; - - Bit8u tandy_irq = 7; - if (tandy_sb.port) tandy_irq = tandy_sb.irq; - else if (tandy_dac.port) tandy_irq = tandy_dac.irq; - Bit8u tandy_irq_vector = tandy_irq; - if (tandy_irq_vector<8) tandy_irq_vector += 8; - else tandy_irq_vector += (0x70-8); - - /* revector IRQ-handler if necessary */ - RealPt current_irq=RealGetVec(tandy_irq_vector); - if (current_irq!=tandy_DAC_callback[0]->Get_RealPointer()) { - real_writed(0x40,0xd6,current_irq); - RealSetVec(tandy_irq_vector,tandy_DAC_callback[0]->Get_RealPointer()); - } - - Bit8u tandy_dma = 1; - if (tandy_sb.port) tandy_dma = tandy_sb.dma; - else if (tandy_dac.port) tandy_dma = tandy_dac.dma; - - if (tandy_sb.port) { - IO_Write(tandy_sb.port+0xcu,0xd0); /* stop DMA transfer */ - IO_Write(0x21,IO_Read(0x21)&(~(1u<>16u)&0xff); - IO_Write(tandy_dma*2u,(Bit8u)(bufpt&0xff)); - IO_Write(tandy_dma*2u,(Bit8u)((bufpt>>8u)&0xff)); - switch (tandy_dma) { - case 0: IO_Write(0x87,bufpage); break; - case 1: IO_Write(0x83,bufpage); break; - case 2: IO_Write(0x81,bufpage); break; - case 3: IO_Write(0x82,bufpage); break; - } - real_writeb(0x40,0xd4,bufpage); - - /* calculate transfer size (respects segment boundaries) */ - Bit32u tlength=length; - if (tlength+(bufpt&0xffff)>0x10000) tlength=0x10000-(bufpt&0xffff); - real_writew(0x40,0xd0,(Bit16u)(length-tlength)); /* remaining buffer length */ - tlength--; - - /* set transfer size */ - IO_Write(tandy_dma*2u+1u,(Bit8u)(tlength&0xffu)); - IO_Write(tandy_dma*2u+1u,(Bit8u)((tlength>>8u)&0xffu)); - - Bit16u delay=(Bit16u)(real_readw(0x40,0xd2)&0xfff); - Bit8u amplitude=(Bit8u)(((unsigned int)real_readw(0x40,0xd2)>>13u)&0x7u); - if (tandy_sb.port) { - IO_Write(0x0a,tandy_dma); /* enable DMA channel */ - /* set frequency */ - IO_Write(tandy_sb.port+0xcu,0x40); - IO_Write(tandy_sb.port+0xcu,256u - delay*100u/358u); - /* set playback type to 8bit */ - if (isplayback) IO_Write(tandy_sb.port+0xcu,0x14u); - else IO_Write(tandy_sb.port+0xcu,0x24u); - /* set transfer size */ - IO_Write(tandy_sb.port+0xcu,(Bit8u)(tlength&0xffu)); - IO_Write(tandy_sb.port+0xcu,(Bit8u)((tlength>>8)&0xffu)); - } else { - if (isplayback) IO_Write(tandy_dac.port,(IO_Read(tandy_dac.port)&0x7cu) | 0x03u); - else IO_Write(tandy_dac.port,(IO_Read(tandy_dac.port)&0x7cu) | 0x02u); - IO_Write(tandy_dac.port+2u,(Bit8u)(delay&0xffu)); - IO_Write(tandy_dac.port+3u,(Bit8u)((((unsigned int)delay>>8u)&0xfu) | ((unsigned int)amplitude<<5u))); - if (isplayback) IO_Write(tandy_dac.port,(IO_Read(tandy_dac.port)&0x7cu) | 0x1fu); - else IO_Write(tandy_dac.port,(IO_Read(tandy_dac.port)&0x7c) | 0x1e); - IO_Write(0x0a,tandy_dma); /* enable DMA channel */ - } - - if (!isplayback) { - /* mark transfer as recording operation */ - real_writew(0x40,0xd2,(Bit16u)(delay|0x1000)); - } -} - -static Bitu IRQ_TandyDAC(void) { - if (tandy_dac.port) { - IO_Read(tandy_dac.port); - } - if (real_readw(0x40,0xd0)) { /* play/record next buffer */ - /* acknowledge IRQ */ - IO_Write(0x20,0x20); - if (tandy_sb.port) { - IO_Read(tandy_sb.port+0xeu); - } - - /* buffer starts at the next page */ - Bit8u npage=real_readb(0x40,0xd4)+1u; - real_writeb(0x40,0xd4,npage); - - Bitu rb=real_readb(0x40,0xd3); - if (rb&0x10) { - /* start recording */ - real_writeb(0x40,0xd3,rb&0xefu); - Tandy_SetupTransfer((unsigned int)npage<<16u,false); - } else { - /* start playback */ - Tandy_SetupTransfer((unsigned int)npage<<16u,true); - } - } else { /* playing/recording is finished */ - Bit8u tandy_irq = 7u; - if (tandy_sb.port) tandy_irq = tandy_sb.irq; - else if (tandy_dac.port) tandy_irq = tandy_dac.irq; - Bit8u tandy_irq_vector = tandy_irq; - if (tandy_irq_vector<8u) tandy_irq_vector += 8u; - else tandy_irq_vector += (0x70u-8u); - - RealSetVec(tandy_irq_vector,real_readd(0x40,0xd6)); - - /* turn off speaker and acknowledge soundblaster IRQ */ - if (tandy_sb.port) { - IO_Write(tandy_sb.port+0xcu,0xd3u); - IO_Read(tandy_sb.port+0xeu); - } - - /* issue BIOS tandy sound device busy callout */ - SegSet16(cs, RealSeg(tandy_DAC_callback[1]->Get_RealPointer())); - reg_ip = RealOff(tandy_DAC_callback[1]->Get_RealPointer()); - } - return CBRET_NONE; -} - -static void TandyDAC_Handler(Bit8u tfunction) { - if ((!tandy_sb.port) && (!tandy_dac.port)) return; - switch (tfunction) { - case 0x81: /* Tandy sound system check */ - if (tandy_dac.port) { - reg_ax=tandy_dac.port; - } else { - reg_ax=0xc4; - } - CALLBACK_SCF(Tandy_TransferInProgress()); - break; - case 0x82: /* Tandy sound system start recording */ - case 0x83: /* Tandy sound system start playback */ - if (Tandy_TransferInProgress()) { - /* cannot play yet as the last transfer isn't finished yet */ - reg_ah=0x00; - CALLBACK_SCF(true); - break; - } - /* store buffer length */ - real_writew(0x40,0xd0,reg_cx); - /* store delay and volume */ - real_writew(0x40,0xd2,(reg_dx&0xfff)|((reg_al&7)<<13)); - Tandy_SetupTransfer(PhysMake(SegValue(es),reg_bx),reg_ah==0x83); - reg_ah=0x00; - CALLBACK_SCF(false); - break; - case 0x84: /* Tandy sound system stop playing */ - reg_ah=0x00; - - /* setup for a small buffer with silence */ - real_writew(0x40,0xd0,0x0a); - real_writew(0x40,0xd2,0x1c); - Tandy_SetupTransfer(PhysMake(0xf000,0xa084),true); - CALLBACK_SCF(false); - break; - case 0x85: /* Tandy sound system reset */ - if (tandy_dac.port) { - IO_Write(tandy_dac.port,(Bit8u)(IO_Read(tandy_dac.port)&0xe0)); - } - reg_ah=0x00; - CALLBACK_SCF(false); - break; - } -} - -extern bool date_host_forced; -static Bit8u ReadCmosByte (Bitu index) { - IO_Write(0x70, index); - return IO_Read(0x71); -} - -static void WriteCmosByte (Bitu index, Bitu val) { - IO_Write(0x70, index); - IO_Write(0x71, val); -} - -static bool RtcUpdateDone () { - while ((ReadCmosByte(0x0a) & 0x80) != 0) CALLBACK_Idle(); - return true; // cannot fail in DOSbox -} - -static void InitRtc () { - WriteCmosByte(0x0a, 0x26); // default value (32768Hz, 1024Hz) - - // leave bits 6 (pirq), 5 (airq), 0 (dst) untouched - // reset bits 7 (freeze), 4 (uirq), 3 (sqw), 2 (bcd) - // set bit 1 (24h) - WriteCmosByte(0x0b, (ReadCmosByte(0x0b) & 0x61u) | 0x02u); - - ReadCmosByte(0x0c); // clear any bits set -} - -static Bitu INT1A_Handler(void) { - CALLBACK_SIF(true); - switch (reg_ah) { - case 0x00: /* Get System time */ - { - Bit32u ticks=mem_readd(BIOS_TIMER); - reg_al=mem_readb(BIOS_24_HOURS_FLAG); - mem_writeb(BIOS_24_HOURS_FLAG,0); // reset the "flag" - reg_cx=(Bit16u)(ticks >> 16u); - reg_dx=(Bit16u)(ticks & 0xffff); - break; - } - case 0x01: /* Set System time */ - mem_writed(BIOS_TIMER,((unsigned int)reg_cx<<16u)|reg_dx); - break; - case 0x02: /* GET REAL-TIME CLOCK TIME (AT,XT286,PS) */ - if(date_host_forced) { - InitRtc(); // make sure BCD and no am/pm - if (RtcUpdateDone()) { // make sure it's safe to read - reg_ch = ReadCmosByte(0x04); // hours - reg_cl = ReadCmosByte(0x02); // minutes - reg_dh = ReadCmosByte(0x00); // seconds - reg_dl = ReadCmosByte(0x0b) & 0x01; // daylight saving time - } - CALLBACK_SCF(false); - break; - } - IO_Write(0x70,0x04); //Hours - reg_ch=IO_Read(0x71); - IO_Write(0x70,0x02); //Minutes - reg_cl=IO_Read(0x71); - IO_Write(0x70,0x00); //Seconds - reg_dh=IO_Read(0x71); - reg_dl=0; //Daylight saving disabled - CALLBACK_SCF(false); - break; - case 0x03: // set RTC time - if(date_host_forced) { - InitRtc(); // make sure BCD and no am/pm - WriteCmosByte(0x0b, ReadCmosByte(0x0b) | 0x80u); // prohibit updates - WriteCmosByte(0x04, reg_ch); // hours - WriteCmosByte(0x02, reg_cl); // minutes - WriteCmosByte(0x00, reg_dh); // seconds - WriteCmosByte(0x0b, (ReadCmosByte(0x0b) & 0x7eu) | (reg_dh & 0x01u)); // dst + implicitly allow updates - } - break; - case 0x04: /* GET REAL-TIME ClOCK DATE (AT,XT286,PS) */ - if(date_host_forced) { - InitRtc(); // make sure BCD and no am/pm - if (RtcUpdateDone()) { // make sure it's safe to read - reg_ch = ReadCmosByte(0x32); // century - reg_cl = ReadCmosByte(0x09); // year - reg_dh = ReadCmosByte(0x08); // month - reg_dl = ReadCmosByte(0x07); // day - } - CALLBACK_SCF(false); - break; - } - IO_Write(0x70,0x32); //Centuries - reg_ch=IO_Read(0x71); - IO_Write(0x70,0x09); //Years - reg_cl=IO_Read(0x71); - IO_Write(0x70,0x08); //Months - reg_dh=IO_Read(0x71); - IO_Write(0x70,0x07); //Days - reg_dl=IO_Read(0x71); - CALLBACK_SCF(false); - break; - case 0x05: // set RTC date - if(date_host_forced) { - InitRtc(); // make sure BCD and no am/pm - WriteCmosByte(0x0b, ReadCmosByte(0x0b) | 0x80); // prohibit updates - WriteCmosByte(0x32, reg_ch); // century - WriteCmosByte(0x09, reg_cl); // year - WriteCmosByte(0x08, reg_dh); // month - WriteCmosByte(0x07, reg_dl); // day - WriteCmosByte(0x0b, (ReadCmosByte(0x0b) & 0x7f)); // allow updates - } - break; - case 0x80: /* Pcjr Setup Sound Multiplexer */ - LOG(LOG_BIOS,LOG_ERROR)("INT1A:80:Setup tandy sound multiplexer to %d",reg_al); - break; - case 0x81: /* Tandy sound system check */ - case 0x82: /* Tandy sound system start recording */ - case 0x83: /* Tandy sound system start playback */ - case 0x84: /* Tandy sound system stop playing */ - case 0x85: /* Tandy sound system reset */ - TandyDAC_Handler(reg_ah); - break; - case 0xb1: /* PCI Bios Calls */ - if (pcibus_enable) { - LOG(LOG_BIOS,LOG_WARN)("INT1A:PCI bios call %2X",reg_al); - switch (reg_al) { - case 0x01: // installation check - if (PCI_IsInitialized()) { - reg_ah=0x00; - reg_al=0x01; // cfg space mechanism 1 supported - reg_bx=0x0210; // ver 2.10 - reg_cx=0x0000; // only one PCI bus - reg_edx=0x20494350; - reg_edi=PCI_GetPModeInterface(); - CALLBACK_SCF(false); - } else { - CALLBACK_SCF(true); - } - break; - case 0x02: { // find device - Bitu devnr=0u; - Bitu count=0x100u; - Bit32u devicetag=((unsigned int)reg_cx<<16u)|reg_dx; - Bits found=-1; - for (Bitu i=0; i<=count; i++) { - IO_WriteD(0xcf8,0x80000000u|(i<<8u)); // query unique device/subdevice entries - if (IO_ReadD(0xcfc)==devicetag) { - if (devnr==reg_si) { - found=(Bits)i; - break; - } else { - // device found, but not the SIth device - devnr++; - } - } - } - if (found>=0) { - reg_ah=0x00; - reg_bh=0x00; // bus 0 - reg_bl=(Bit8u)(found&0xff); - CALLBACK_SCF(false); - } else { - reg_ah=0x86; // device not found - CALLBACK_SCF(true); - } - } - break; - case 0x03: { // find device by class code - Bitu devnr=0; - Bitu count=0x100u; - Bit32u classtag=reg_ecx&0xffffffu; - Bits found=-1; - for (Bitu i=0; i<=count; i++) { - IO_WriteD(0xcf8,0x80000000u|(i<<8u)); // query unique device/subdevice entries - if (IO_ReadD(0xcfc)!=0xffffffffu) { - IO_WriteD(0xcf8,0x80000000u|(i<<8u)|0x08u); - if ((IO_ReadD(0xcfc)>>8u)==classtag) { - if (devnr==reg_si) { - found=(Bits)i; - break; - } else { - // device found, but not the SIth device - devnr++; - } - } - } - } - if (found>=0) { - reg_ah=0x00; - reg_bh=0x00; // bus 0 - reg_bl=(Bit8u)(found&0xffu); - CALLBACK_SCF(false); - } else { - reg_ah=0x86; // device not found - CALLBACK_SCF(true); - } - } - break; - case 0x08: // read configuration byte - IO_WriteD(0xcf8,0x80000000u|((unsigned int)reg_bx<<8u)|(reg_di&0xfcu)); - reg_cl=IO_ReadB(0xcfc+(reg_di&3u)); - CALLBACK_SCF(false); - reg_ah=0x00; - break; - case 0x09: // read configuration word - IO_WriteD(0xcf8,0x80000000u|((unsigned int)reg_bx<<8u)|(reg_di&0xfcu)); - reg_cx=IO_ReadW(0xcfc+(reg_di&2u)); - CALLBACK_SCF(false); - reg_ah=0x00; - break; - case 0x0a: // read configuration dword - IO_WriteD(0xcf8,0x80000000u|((unsigned int)reg_bx<<8u)|(reg_di&0xfcu)); - reg_ecx=IO_ReadD(0xcfc+(reg_di&3u)); - CALLBACK_SCF(false); - reg_ah=0x00; - break; - case 0x0b: // write configuration byte - IO_WriteD(0xcf8,0x80000000u|((unsigned int)reg_bx<<8u)|(reg_di&0xfcu)); - IO_WriteB(0xcfc+(reg_di&3u),reg_cl); - CALLBACK_SCF(false); - reg_ah=0x00; - break; - case 0x0c: // write configuration word - IO_WriteD(0xcf8,0x80000000u|((unsigned int)reg_bx<<8u)|(reg_di&0xfcu)); - IO_WriteW(0xcfc+(reg_di&2u),reg_cx); - CALLBACK_SCF(false); - reg_ah=0x00; - break; - case 0x0d: // write configuration dword - IO_WriteD(0xcf8,0x80000000u|((unsigned int)reg_bx<<8u)|(reg_di&0xfcu)); - IO_WriteD(0xcfc+((unsigned int)reg_di&3u),reg_ecx); - CALLBACK_SCF(false); - reg_ah=0x00; - break; - default: - LOG(LOG_BIOS,LOG_ERROR)("INT1A:PCI BIOS: unknown function %x (%x %x %x)", - reg_ax,reg_bx,reg_cx,reg_dx); - CALLBACK_SCF(true); - break; - } - } - else { - CALLBACK_SCF(true); - } - break; - default: - LOG(LOG_BIOS,LOG_ERROR)("INT1A:Undefined call %2X",reg_ah); - } - return CBRET_NONE; -} - -bool INT16_get_key(Bit16u &code); -bool INT16_peek_key(Bit16u &code); - -extern uint8_t GDC_display_plane; -extern uint8_t GDC_display_plane_pending; - -unsigned char prev_pc98_mode42 = 0; - -bool pc98_function_row = false; - -const char *pc98_func_key[10] = { - " C1 ", - " CU ", - " CA ", - " S1 ", - " SU ", - - " VOID ", - " NWL ", - " INS ", - " REP ", - " ^Z " -}; - -// shortcuts offered by SHIFT F1-F10. You can bring this onscreen using CTRL+F7. This row shows '*' in col 2. -// [0] is onscreen display, [1] is what is entered to STDIN. -const char *pc98_shcut_key[10][2] = { - {"dir a:", "dir a:\x0D"}, - {"dir b:", "dir b:\x0D"}, - {"copy ", "copy "}, - {"del ", "del "}, - {"ren ", "ren "}, - - {"chkdsk", "chkdsk a:\x0D"}, - {"chkdsk", "chkdsk b:\x0D"}, - {"type ", "type "}, - {"date\x0D ","date\x0D"}, // display includes CR - {"time\x0D ","time\x0D"} -}; - -#include "int10.h" - -void update_pc98_function_row(bool enable) { - pc98_function_row = enable; - - real_writeb(0x60,0x112,25 - 1 - (pc98_function_row ? 1 : 0)); - - unsigned char c = real_readb(0x60,0x11C); - unsigned char r = real_readb(0x60,0x110); - unsigned int o = 80 * 24; - - if (pc98_function_row) { - if (r > 23) r = 23; - - /* draw the function row. - * based on on real hardware: - * - * The function key is 72 chars wide. 4 blank chars on each side of the screen. - * It is divided into two halves, 36 chars each. - * Within each half, aligned to it's side, is 5 x 7 regions. - * 6 of the 7 are inverted. centered in the white block is the function key. */ - for (unsigned int i=0;i < 40;) { - mem_writew(0xA0000+((o+i)*2),0x0000); - mem_writeb(0xA2000+((o+i)*2),0xE1); - - mem_writew(0xA0000+((o+(79-i))*2),0x0000); - mem_writeb(0xA2000+((o+(79-i))*2),0xE1); - - if (i >= 3 && i < 38) - i += 7; - else - i++; - } - - for (unsigned int i=0;i < 5u;i++) { - unsigned int co = 4u + (i * 7u); - const char *str = pc98_func_key[i]; - - for (unsigned int j=0;j < 6u;j++) { - mem_writew(0xA0000+((o+co+j)*2u),(unsigned char)str[j]); - mem_writeb(0xA2000+((o+co+j)*2u),0xE5); // white reverse visible - } - } - - for (unsigned int i=5;i < 10;i++) { - unsigned int co = 42u + ((i - 5u) * 7u); - const char *str = pc98_func_key[i]; - - for (unsigned int j=0;j < 6u;j++) { - mem_writew(0xA0000+((o+co+j)*2u),(unsigned char)str[j]); - mem_writeb(0xA2000+((o+co+j)*2u),0xE5); // white reverse visible - } - } - } - else { - /* erase the function row */ - for (unsigned int i=0;i < 80;i++) { - mem_writew(0xA0000+((o+i)*2),0x0000); - mem_writeb(0xA2000+((o+i)*2),0xE1); - } - } - - real_writeb(0x60,0x11C,c); - real_writeb(0x60,0x110,r); - - real_writeb(0x60,0x111,pc98_function_row ? 0x01 : 0x00);/* function key row display status */ - - void vga_pc98_direct_cursor_pos(Bit16u address); - vga_pc98_direct_cursor_pos((r*80)+c); -} - -void pc98_set_digpal_entry(unsigned char ent,unsigned char grb); -void PC98_show_cursor(bool show); - -extern bool gdc_5mhz_mode; -extern bool enable_pc98_egc; -extern bool enable_pc98_grcg; -extern bool enable_pc98_16color; -extern bool enable_pc98_188usermod; -extern bool pc98_31khz_mode; -extern bool pc98_attr4_graphic; - -extern unsigned char pc98_text_first_row_scanline_start; /* port 70h */ -extern unsigned char pc98_text_first_row_scanline_end; /* port 72h */ -extern unsigned char pc98_text_row_scanline_blank_at; /* port 74h */ -extern unsigned char pc98_text_row_scroll_lines; /* port 76h */ -extern unsigned char pc98_text_row_scroll_count_start; /* port 78h */ -extern unsigned char pc98_text_row_scroll_num_lines; /* port 7Ah */ - -void pc98_update_text_layer_lineheight_from_bda(void) { -// unsigned char c = mem_readb(0x53C); - unsigned char lineheight = mem_readb(0x53B) + 1; - - pc98_gdc[GDC_MASTER].force_fifo_complete(); - pc98_gdc[GDC_MASTER].row_height = lineheight; - - if (lineheight > 16) { // usually 20 - pc98_text_first_row_scanline_start = 0x1E; - pc98_text_first_row_scanline_end = lineheight - 3; - pc98_text_row_scanline_blank_at = 16; - } - else { - pc98_text_first_row_scanline_start = 0; - pc98_text_first_row_scanline_end = lineheight - 1; - pc98_text_row_scanline_blank_at = lineheight; - } - - pc98_text_row_scroll_lines = 0; - pc98_text_row_scroll_count_start = 0; - pc98_text_row_scroll_num_lines = 0; - - vga.crtc.cursor_start = 0; - vga.draw.cursor.sline = 0; - - vga.crtc.cursor_end = lineheight - 1; - vga.draw.cursor.eline = lineheight - 1; -} - -void pc98_update_text_lineheight_from_bda(void) { - unsigned char c = mem_readb(0x53C); - unsigned char lineheight; - - if (c & 0x01)/*20-line mode*/ - lineheight = 20; - else /*25-line mode*/ - lineheight = 16; - - mem_writeb(0x53B,lineheight - 1); -} - -static Bitu INT18_PC98_Handler(void) { - Bit16u temp16; - -#if 0 - if (reg_ah >= 0x0A) { - LOG_MSG("PC-98 INT 18h unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", - reg_ax, - reg_bx, - reg_cx, - reg_dx, - reg_si, - reg_di, - SegValue(ds), - SegValue(es)); - } -#endif - - /* NTS: Based on information gleaned from Neko Project II source code including comments which - * I've run through GNU iconv to convert from SHIFT-JIS to UTF-8 here in case Google Translate - * got anything wrong. */ - switch (reg_ah) { - case 0x00: /* Reading of key data (キー・データの読みだし) */ - /* FIXME: We use the IBM PC/AT keyboard buffer to fake this call. - * This will be replaced with PROPER emulation once the PC-98 keyboard handler has been - * updated to write the buffer the way PC-98 BIOSes do it. - * - * IBM PC/AT keyboard buffer at 40:1E-40:3D - * - * PC-98 keyboard buffer at 50:02-50:21 */ - /* This call blocks until keyboard input */ - if (INT16_get_key(temp16)) { - reg_ax = temp16; - } - else { - /* Keyboard checks. - * If the interrupt got masked, unmask it. - * If the keyboard has data waiting, make sure the interrupt signal is active in case the last interrupt handler - * handled the keyboard interrupt and never read the keyboard (Quarth). - * - * TODO: Is this what real PC-98 BIOSes do? */ - void check_keyboard_fire_IRQ1(void); - check_keyboard_fire_IRQ1(); - IO_WriteB(0x02,IO_ReadB(0x02) & (~(1u << /*IRQ*/1u))); // unmask IRQ1 - - reg_ip += 1; /* step over IRET, to NOPs which then JMP back to callback */ - } - break; - case 0x01: /* Sense of key buffer state (キー・バッファ状態のセンス) */ - /* This call returns whether or not there is input waiting. - * The waiting data is read, but NOT discarded from the buffer. */ - if (INT16_peek_key(temp16)) { - reg_ax = temp16; - reg_bh = 1; - } - else { - /* Keyboard checks. - * If the interrupt got masked, unmask it. - * If the keyboard has data waiting, make sure the interrupt signal is active in case the last interrupt handler - * handled the keyboard interrupt and never read the keyboard (Quarth). - * - * TODO: Is this what real PC-98 BIOSes do? */ - void check_keyboard_fire_IRQ1(void); - check_keyboard_fire_IRQ1(); - IO_WriteB(0x02,IO_ReadB(0x02) & (~(1u << /*IRQ*/1u))); // unmask IRQ1 - - reg_bh = 0; - } - break; - case 0x02: /* Sense of shift key state (シフト・キー状態のセンス) */ - reg_al = mem_readb(0x52A + 0x0E); /* FIXME: Seems to match 14th bitmap byte. Does real hardware do this?? */ - break; - case 0x03: /* Initialization of keyboard interface (キーボード・インタフェイスの初期化) */ - /* TODO */ - break; - case 0x04: /* Sense of key input state (キー入力状態のセンス) */ - reg_ah = mem_readb(0x52A + (unsigned int)(reg_al & 0x0Fu)); - /* Hack for "Shangrlia" by Elf: The game's regulation of animation speed seems to depend on - * INT 18h AH=0x04 taking some amount of time. If we do not do this, animation will run way - * too fast and everyone will be talking/moving at a million miles a second. - * - * This is based on comparing animation speed vs the same game on real Pentium-class PC-98 - * hardware. - * - * Looking at the software loop involved during opening cutscenes, the game is constantly - * polling INT 18h AH=04h (keyboard state) and INT 33h AH=03h (mouse button/position state) - * while animating the characters on the screen. Without this delay, animation runs way too - * fast. - * - * This guess is also loosely based on a report by the Touhou Community Reliant Automatic Patcher - * that Touhou Project directly reads this byte but delays by 0.6ms to handle the fact that - * the bit in question may toggle while the key is held down due to the scan codes returned by - * the keyboard. - * - * This is a guess, but it seems to help animation speed match that of real hardware regardless - * of cycle count in DOSBox-X. */ - CPU_Cycles -= (unsigned int)(CPU_CycleMax * 0.006); - break; - case 0x05: /* Key input sense (キー入力センス) */ - /* This appears to return a key from the buffer (and remove from - * buffer) or return BH == 0 to signal no key was pending. */ - if (INT16_get_key(temp16)) { - reg_ax = temp16; - reg_bh = 1; - } - else { - /* Keyboard checks. - * If the interrupt got masked, unmask it. - * If the keyboard has data waiting, make sure the interrupt signal is active in case the last interrupt handler - * handled the keyboard interrupt and never read the keyboard (Quarth). - * - * TODO: Is this what real PC-98 BIOSes do? */ - void check_keyboard_fire_IRQ1(void); - check_keyboard_fire_IRQ1(); - IO_WriteB(0x02,IO_ReadB(0x02) & (~(1u << /*IRQ*/1u))); // unmask IRQ1 - - reg_bh = 0; - } - break; - case 0x0A: /* set CRT mode */ - /* bit off on - 0 25lines 20lines - 1 80cols 40cols - 2 v.lines simp.graphics - 3 K-CG access mode(not used in PC-98) */ - - //TODO: set 25/20 lines mode and 80/40 columns mode. - //Attribute bit (bit 2) - pc98_attr4_graphic = !!(reg_al & 0x04); - - mem_writeb(0x53C,reg_al); - - if (reg_al & 2) - LOG_MSG("INT 18H AH=0Ah warning: 40-column PC-98 text mode not supported"); - if (reg_al & 8) - LOG_MSG("INT 18H AH=0Ah warning: K-CG dot access mode not supported"); - - pc98_update_text_lineheight_from_bda(); - pc98_update_text_layer_lineheight_from_bda(); - - /* Apparently, this BIOS call also hides the cursor */ - PC98_show_cursor(0); - break; - case 0x0B: /* get CRT mode */ - /* bit off on - 0 25lines 20lines - 1 80cols 40cols - 2 v.lines simp.graphics - 3 K-CG access mode(not used in PC-98) - 7 std CRT hi-res CRT */ - /* NTS: I assume that real hardware doesn't offer a way to read back the state of these bits, - * so the BIOS's only option is to read the mode byte back from the data area. - * Neko Project II agrees. */ - reg_al = mem_readb(0x53C); - break; - // TODO: "Edge" is using INT 18h AH=06h, what is that? - // Neko Project is also unaware of such a call. - case 0x0C: /* text layer enable */ - pc98_gdc[GDC_MASTER].force_fifo_complete(); - pc98_gdc[GDC_MASTER].display_enable = true; - break; - case 0x0D: /* text layer disable */ - pc98_gdc[GDC_MASTER].force_fifo_complete(); - pc98_gdc[GDC_MASTER].display_enable = false; - break; - case 0x0E: /* set text display area (DX=byte offset) */ - pc98_gdc[GDC_MASTER].force_fifo_complete(); - pc98_gdc[GDC_MASTER].param_ram[0] = (reg_dx >> 1) & 0xFF; - pc98_gdc[GDC_MASTER].param_ram[1] = (reg_dx >> 9) & 0xFF; - pc98_gdc[GDC_MASTER].param_ram[2] = (400 << 4) & 0xFF; - pc98_gdc[GDC_MASTER].param_ram[3] = (400 << 4) >> 8; - break; - case 0x11: /* show cursor */ - PC98_show_cursor(true); - break; - case 0x12: /* hide cursor */ - PC98_show_cursor(false); - break; - case 0x13: /* set cursor position (DX=byte position) */ - void vga_pc98_direct_cursor_pos(Bit16u address); - - pc98_gdc[GDC_MASTER].force_fifo_complete(); - vga_pc98_direct_cursor_pos(reg_dx >> 1); - break; - case 0x14: /* read FONT RAM */ - { - unsigned int i,o,r; - - /* DX = code (must be 0x76xx or 0x7700) - * BX:CX = 34-byte region to write to */ - if (reg_dh == 0x80u) { /* 8x16 ascii */ - i = ((unsigned int)reg_bx << 4u) + reg_cx + 2u; - mem_writew(i-2u,0x0102u); - for (r=0;r < 16u;r++) { - o = (reg_dl*16u)+r; - - assert((o+2u) <= sizeof(vga.draw.font)); - - mem_writeb(i+r,vga.draw.font[o]); - } - } - else if ((reg_dh & 0xFC) == 0x28) { /* 8x16 kanji */ - i = ((unsigned int)reg_bx << 4u) + reg_cx + 2u; - mem_writew(i-2u,0x0102u); - for (r=0;r < 16u;r++) { - o = (((((reg_dl & 0x7Fu)*128u)+((reg_dh - 0x20u) & 0x7Fu))*16u)+r)*2u; - - assert((o+2u) <= sizeof(vga.draw.font)); - - mem_writeb(i+r+0u,vga.draw.font[o+0u]); - } - } - else if (reg_dh != 0) { /* 16x16 kanji */ - i = ((unsigned int)reg_bx << 4u) + reg_cx + 2u; - mem_writew(i-2u,0x0202u); - for (r=0;r < 16u;r++) { - o = (((((reg_dl & 0x7Fu)*128u)+((reg_dh - 0x20u) & 0x7Fu))*16u)+r)*2u; - - assert((o+2u) <= sizeof(vga.draw.font)); - - mem_writeb(i+(r*2u)+0u,vga.draw.font[o+0u]); - mem_writeb(i+(r*2u)+1u,vga.draw.font[o+1u]); - } - } - else { - LOG_MSG("PC-98 INT 18h AH=14h font RAM read ignored, code 0x%04x not supported",reg_dx); - } - } - break; - case 0x16: /* fill screen with chr + attr */ - { - /* DL = character - * DH = attribute */ - unsigned int i; - - for (i=0;i < 0x2000;i += 2) { - vga.mem.linear[i+0] = reg_dl; - vga.mem.linear[i+1] = 0x00; - } - for ( ;i < 0x3FE0;i += 2) { - vga.mem.linear[i+0] = reg_dh; - vga.mem.linear[i+1] = 0x00; - } - } - break; - case 0x17: /* BELL ON */ - IO_WriteB(0x37,0x06); - break; - case 0x18: /* BELL OFF */ - IO_WriteB(0x37,0x07); - break; - case 0x1A: /* load FONT RAM */ - { - unsigned int i,o,r; - - /* DX = code (must be 0x76xx or 0x7700) - * BX:CX = 34-byte region to read from */ - if ((reg_dh & 0x7Eu) == 0x76u) { - i = ((unsigned int)reg_bx << 4u) + reg_cx + 2u; - for (r=0;r < 16u;r++) { - o = (((((reg_dl & 0x7Fu)*128u)+((reg_dh - 0x20u) & 0x7Fu))*16u)+r)*2u; - - assert((o+2u) <= sizeof(vga.draw.font)); - - vga.draw.font[o+0u] = mem_readb(i+(r*2u)+0u); - vga.draw.font[o+1u] = mem_readb(i+(r*2u)+1u); - } - } - else { - LOG_MSG("PC-98 INT 18h AH=1Ah font RAM load ignored, code 0x%04x out of range",reg_dx); - } - } - break; - case 0x31: /* Return display mode and status */ - if (enable_pc98_egc) { /* FIXME: INT 18h AH=31/30h availability is tied to EGC enable */ - unsigned char b597 = mem_readb(0x597); - unsigned char tstat = mem_readb(0x53C); - /* Return values: - * - * AL = - * bit [7:7] = ? - * bit [6:6] = ? - * bit [5:5] = ? - * bit [4:4] = ? - * bit [3:2] = horizontal sync - * 00 = 15.98KHz - * 01 = ? - * 10 = 24.83KHz - * 11 = 31.47KHz - * bit [1:1] = ? - * bit [0:0] = interlaced (1=yes 0=no) - * BH = - * bit [7:7] = ? - * bit [6:6] = ? - * bit [5:4] = graphics video mode - * 00 = 640x200 (upper half) - * 01 = 640x200 (lower half) - * 10 = 640x400 - * 11 = 640x480 - * bit [3:3] = ? - * bit [2:2] = ? - * bit [1:0] = number of text rows - * 00 = 20 rows - * 01 = 25 rows - * 10 = 30 rows - * 11 = ? - */ - reg_al = - ((pc98_31khz_mode ? 3 : 2) << 2)/*hsync*/; - reg_bh = - ((b597 & 3) << 4)/*graphics video mode*/; - if (tstat & 0x10) - reg_bh |= 2;/*30 rows*/ - else if ((tstat & 0x01) == 0) - reg_bh |= 1;/*25 rows*/ - } - break; - /* From this point on the INT 18h call list appears to wander off from the keyboard into CRT/GDC/display management. */ - case 0x40: /* Start displaying the graphics screen (グラフィック画面の表示開始) */ - pc98_gdc[GDC_SLAVE].force_fifo_complete(); - pc98_gdc[GDC_SLAVE].display_enable = true; - - { - unsigned char b = mem_readb(0x54C/*MEMB_PRXCRT*/); - mem_writeb(0x54C/*MEMB_PRXCRT*/,b | 0x80); - } - break; - case 0x41: /* Stop displaying the graphics screen (グラフィック画面の表示終了) */ - pc98_gdc[GDC_SLAVE].force_fifo_complete(); - pc98_gdc[GDC_SLAVE].display_enable = false; - - { - unsigned char b = mem_readb(0x54C/*MEMB_PRXCRT*/); - mem_writeb(0x54C/*MEMB_PRXCRT*/,b & (~0x80)); - } - break; - case 0x42: /* Display area setup (表示領域の設定) */ - pc98_gdc[GDC_MASTER].force_fifo_complete(); - pc98_gdc[GDC_SLAVE].force_fifo_complete(); - /* reset scroll area of graphics */ - if ((reg_ch & 0xC0) == 0x40) { /* 640x200 G-RAM upper half */ - pc98_gdc[GDC_SLAVE].param_ram[0] = (200*40) & 0xFF; - pc98_gdc[GDC_SLAVE].param_ram[1] = (200*40) >> 8; - } - else { - pc98_gdc[GDC_SLAVE].param_ram[0] = 0; - pc98_gdc[GDC_SLAVE].param_ram[1] = 0; - } - pc98_gdc[GDC_SLAVE].param_ram[2] = 0xF0; - pc98_gdc[GDC_SLAVE].param_ram[3] = 0x3F; - pc98_gdc[GDC_SLAVE].active_display_words_per_line = 40; /* 40 x 16 = 640 pixel wide graphics */ - - // CH - // [7:6] = G-RAM setup - // 00 = no graphics (?) - // 01 = 640x200 upper half - // 10 = 640x200 lower half - // 11 = 640x400 - // [5:5] = CRT - // 0 = color - // 1 = monochrome - // [4:4] = Display bank - - // FIXME: This is a guess. I have no idea as to actual behavior, yet. - // This seems to help with clearing the text layer when games start the graphics. - // This is ALSO how we will detect games that switch on the 200-line double-scan mode vs 400-line mode. - if ((reg_ch & 0xC0) != 0) { - pc98_gdc[GDC_SLAVE].doublescan = ((reg_ch & 0xC0) == 0x40) || ((reg_ch & 0xC0) == 0x80); - pc98_gdc[GDC_SLAVE].row_height = pc98_gdc[GDC_SLAVE].doublescan ? 2 : 1; - - /* update graphics mode bits */ - { - unsigned char b = mem_readb(0x597); - - b &= ~3; - b |= ((reg_ch >> 6) - 1) & 3; - - mem_writeb(0x597,b); - } - } - else { - pc98_gdc[GDC_SLAVE].doublescan = false; - pc98_gdc[GDC_SLAVE].row_height = 1; - } - - { - unsigned char b = mem_readb(0x54C/*MEMB_PRXCRT*/); - - // Real hardware behavior: graphics selection updated by BIOS to reflect MEMB_PRXCRT state - pc98_gdc[GDC_SLAVE].display_enable = !!(b & 0x80); - } - - pc98_gdc_vramop &= ~(1 << VOPBIT_ACCESS); - GDC_display_plane = GDC_display_plane_pending = (reg_ch & 0x10) ? 1 : 0; - - prev_pc98_mode42 = reg_ch; - - LOG_MSG("PC-98 INT 18 AH=42h CH=0x%02X",reg_ch); - break; - case 0x43: // Palette register settings? Only works in digital mode? --leonier - // - // This is said to fix Thexder's GAME ARTS logo. --Jonathan C. - // - // TODO: Validate this against real PC-98 hardware and BIOS - { - unsigned int gbcpc = SegValue(ds)*0x10u + reg_bx; - for(unsigned int i=0;i<4;i++) - { - unsigned char p=mem_readb(gbcpc+4u+i); - pc98_set_digpal_entry(7u-2u*i, p&0xFu); - pc98_set_digpal_entry(6u-2u*i, p>>4u); - } - LOG_MSG("PC-98 INT 18 AH=43h CX=0x%04X DS=0x%04X", reg_cx, SegValue(ds)); - break; - } - default: - LOG_MSG("PC-98 INT 18h unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", - reg_ax, - reg_bx, - reg_cx, - reg_dx, - reg_si, - reg_di, - SegValue(ds), - SegValue(es)); - break; - }; - - /* FIXME: What do actual BIOSes do when faced with an unknown INT 18h call? */ - return CBRET_NONE; -} - -#define PC98_FLOPPY_HIGHDENSITY 0x01 -#define PC98_FLOPPY_2HEAD 0x02 -#define PC98_FLOPPY_RPM_3MODE 0x04 -#define PC98_FLOPPY_RPM_IBMPC 0x08 - -unsigned char PC98_BIOS_FLOPPY_BUFFER[32768]; /* 128 << 8 */ - -static unsigned int PC98_FDC_SZ_TO_BYTES(unsigned int sz) { - return 128U << sz; -} - -int PC98_BIOS_SCSI_POS(imageDisk *floppy,Bit32u §or) { - if (reg_al & 0x80) { - Bit32u img_heads=0,img_cyl=0,img_sect=0,img_ssz=0; - - floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz); - - /* DL = sector - * DH = head - * CX = track */ - if (reg_dl >= img_sect || - reg_dh >= img_heads || - reg_cx >= img_cyl) { - return (reg_ah=0x60); - } - - sector = reg_cx; - sector *= img_heads; - sector += reg_dh; - sector *= img_sect; - sector += reg_dl; - -// LOG_MSG("Sector CHS %u/%u/%u -> %u (geo %u/%u/%u)",reg_cx,reg_dh,reg_dl,sector,img_cyl,img_heads,img_sect); - } - else { - /* Linear LBA addressing */ - sector = ((unsigned int)reg_dl << 16UL) + reg_cx; - /* TODO: SASI caps at 0x1FFFFF according to NP2 */ - } - - return 0; -} - -void PC98_BIOS_SCSI_CALL(void) { - Bit32u img_heads=0,img_cyl=0,img_sect=0,img_ssz=0; - Bit32u memaddr,size,ssize; - imageDisk *floppy; - unsigned int i; - Bit32u sector; - int idx; - -#if 0 - LOG_MSG("PC-98 INT 1Bh SCSI BIOS call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", - reg_ax, - reg_bx, - reg_cx, - reg_dx, - reg_si, - reg_di, - SegValue(ds), - SegValue(es)); -#endif - - idx = (reg_al & 0xF) + 2; - if (idx < 0 || idx >= MAX_DISK_IMAGES) { - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - - floppy = imageDiskList[idx]; - if (floppy == NULL) { - CALLBACK_SCF(true); - reg_ah = 0x60; - return; - } - - /* what to do is in the lower 4 bits of AH */ - switch (reg_ah & 0x0F) { - case 0x05: /* write */ - if (PC98_BIOS_SCSI_POS(floppy,/*&*/sector) == 0) { - floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz); - assert(img_ssz != 0); - - size = reg_bx; - if (size == 0) size = 0x10000U; - memaddr = ((unsigned int)SegValue(es) << 4u) + reg_bp; - - reg_ah = 0; - CALLBACK_SCF(false); - -// LOG_MSG("WRITE memaddr=0x%lx size=0x%x sector=0x%lx ES:BP=%04x:%04X", -// (unsigned long)memaddr,(unsigned int)size,(unsigned long)sector,SegValue(es),reg_bp); - - while (size != 0) { - ssize = size; - if (ssize > img_ssz) ssize = img_ssz; - -// LOG_MSG(" ... memaddr=0x%lx ssize=0x%x sector=0x%lx", -// (unsigned long)memaddr,(unsigned int)ssize,(unsigned long)sector); - - for (i=0;i < ssize;i++) PC98_BIOS_FLOPPY_BUFFER[i] = mem_readb(memaddr+i); - - if (floppy->Write_AbsoluteSector(sector,PC98_BIOS_FLOPPY_BUFFER) == 0) { - } - else { - reg_ah = 0xD0; - CALLBACK_SCF(true); - break; - } - - sector++; - size -= ssize; - memaddr += ssize; - } - } - else { - CALLBACK_SCF(true); - } - break; - case 0x06: /* read */ - if (PC98_BIOS_SCSI_POS(floppy,/*&*/sector) == 0) { - floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz); - assert(img_ssz != 0); - - size = reg_bx; - if (size == 0) size = 0x10000U; - memaddr = ((unsigned int)SegValue(es) << 4u) + reg_bp; - - reg_ah = 0; - CALLBACK_SCF(false); - -// LOG_MSG("READ memaddr=0x%lx size=0x%x sector=0x%lx ES:BP=%04x:%04X", -// (unsigned long)memaddr,(unsigned int)size,(unsigned long)sector,SegValue(es),reg_bp); - - while (size != 0) { - ssize = size; - if (ssize > img_ssz) ssize = img_ssz; - -// LOG_MSG(" ... memaddr=0x%lx ssize=0x%x sector=0x%lx", -// (unsigned long)memaddr,(unsigned int)ssize,(unsigned long)sector); - - if (floppy->Read_AbsoluteSector(sector,PC98_BIOS_FLOPPY_BUFFER) == 0) { - for (i=0;i < ssize;i++) mem_writeb(memaddr+i,PC98_BIOS_FLOPPY_BUFFER[i]); - } - else { - reg_ah = 0xD0; - CALLBACK_SCF(true); - break; - } - - sector++; - size -= ssize; - memaddr += ssize; - } - } - else { - CALLBACK_SCF(true); - } - break; - case 0x07: /* unknown, always succeeds */ - reg_ah = 0x00; - CALLBACK_SCF(false); - break; - case 0x0E: /* unknown, always fails */ - reg_ah = 0x40; - CALLBACK_SCF(true); - break; - case 0x04: /* drive status */ - if (reg_ah == 0x84) { - floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz); - - reg_dl = img_sect; - reg_dh = img_heads; /* Max 16 */ - reg_cx = img_cyl; /* Max 4096 */ - reg_bx = img_ssz; - - reg_ah = 0x00; - CALLBACK_SCF(false); - break; - } - else if (reg_ah == 0x04 || reg_ah == 0x14) { - reg_ah = 0x00; - CALLBACK_SCF(false); - } - else { - goto default_goto; - } - default: - default_goto: - LOG_MSG("PC-98 INT 1Bh unknown SCSI BIOS call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", - reg_ax, - reg_bx, - reg_cx, - reg_dx, - reg_si, - reg_di, - SegValue(ds), - SegValue(es)); - CALLBACK_SCF(true); - break; - }; -} - -void PC98_BIOS_FDC_CALL_GEO_UNPACK(unsigned int &fdc_cyl,unsigned int &fdc_head,unsigned int &fdc_sect,unsigned int &fdc_sz) { - fdc_cyl = reg_cl; - fdc_head = reg_dh; - fdc_sect = reg_dl; - fdc_sz = reg_ch; - if (fdc_sz > 8) fdc_sz = 8; -} - -/* NTS: FDC calls reset IRQ 0 timer to a specific fixed interval, - * because the real BIOS likely does the same in the act of - * controlling the floppy drive. - * - * Resetting the interval is required to prevent Ys II from - * crashing after disk swap (divide by zero/overflow) because - * Ys II reads the timer after INT 1Bh for whatever reason - * and the upper half of the timer byte later affects a divide - * by 3 in the code. */ - -void PC98_Interval_Timer_Continue(void); - -bool enable_fdc_timer_hack = false; - -void FDC_WAIT_TIMER_HACK(void) { - unsigned int v,pv; - unsigned int c=0; - - // Explanation: - // - // Originally the FDC code here changed the timer interval back to the stock 100hz - // normally used in PC-98, to fix Ys II. However that seems to break other booter - // games that hook IRQ 0 directly and set the timer ONCE, then access the disk. - // - // For example, "Angelus" ran WAY too slow with the timer hack because it programs - // the timer to a 600hz interval and expects it to stay that way. - // - // So the new method to satisfy both games is to loop here until the timer - // count is below the maximum that would occur if the 100hz tick count were - // still in effect, even if the timer interval was reprogrammed. - // - // NTS: Writing port 0x77 to relatch the timer also seems to break games - // - // TODO: As a safety against getting stuck, perhaps PIC_FullIndex() should be used - // to break out of the loop if this runs for more than 1 second, since that - // is a sign the timer is in an odd state that will never terminate this loop. - - v = ~0U; - c = 10; - do { - void CALLBACK_Idle(void); - CALLBACK_Idle(); - - pv = v; - - v = IO_ReadB(0x71); - v |= IO_ReadB(0x71) << 8; - - if (v > pv) { - /* if the timer rolled around, we might have missed the narrow window we're watching for */ - if (--c == 0) break; - } - } while (v >= 0x60); -} - -void PC98_BIOS_FDC_CALL(unsigned int flags) { - static unsigned int fdc_cyl[2]={0,0},fdc_head[2]={0,0},fdc_sect[2]={0,0},fdc_sz[2]={0,0}; // FIXME: Rename and move out. Making "static" is a hack here. - Bit32u img_heads=0,img_cyl=0,img_sect=0,img_ssz=0; - unsigned int drive; - unsigned int status; - unsigned int size,accsize,unitsize; - unsigned long memaddr; - imageDisk *floppy; - - /* AL bits[1:0] = which floppy drive */ - if ((reg_al & 3) >= 2) { - /* This emulation only supports up to 2 floppy drives */ - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - - floppy = GetINT13FloppyDrive(drive=(reg_al & 3)); - - /* what to do is in the lower 4 bits of AH */ - switch (reg_ah & 0x0F) { - /* TODO: 0x00 = seek to track (in CL) */ - /* TODO: 0x01 = test read? */ - /* TODO: 0x03 = equipment flags? */ - /* TODO: 0x04 = format detect? */ - /* TODO: 0x05 = write disk */ - /* TODO: 0x07 = recalibrate (seek to track 0) */ - /* TODO: 0x0A = Read ID */ - /* TODO: 0x0D = Format track */ - /* TODO: 0x0E = ?? */ - case 0x03: /* equipment flags update (?) */ - // TODO: Update the disk equipment flags in BDA. - // For now, make Alantia happy by returning success. - reg_ah = 0x00; - CALLBACK_SCF(false); - break; - case 0x00: /* seek */ - /* CL = track */ - if (floppy == NULL) { - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - - if (enable_fdc_timer_hack) { - // Hack for Ys II - FDC_WAIT_TIMER_HACK(); - } - - fdc_cyl[drive] = reg_cl; - - reg_ah = 0x00; - CALLBACK_SCF(false); - break; - case 0x01: /* test read */ - /* AH bits[4:4] = If set, seek to track specified */ - /* CL = cylinder (track) */ - /* CH = sector size (0=128 1=256 2=512 3=1024 etc) */ - /* DL = sector number (1-based) */ - /* DH = head */ - /* BX = size (in bytes) of data to read */ - /* ES:BP = buffer to read data into */ - if (floppy == NULL) { - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz); - - if (enable_fdc_timer_hack) { - // Hack for Ys II - FDC_WAIT_TIMER_HACK(); - } - - /* Prevent reading 1.44MB floppyies using 1.2MB read commands and vice versa. - * FIXME: It seems MS-DOS 5.0 booted from a HDI image has trouble understanding - * when Drive A: (the first floppy) is a 1.44MB drive or not and fails - * because it only attempts it using 1.2MB format read commands. */ - if (flags & PC98_FLOPPY_RPM_IBMPC) { - if (img_ssz == 1024) { /* reject 1.2MB 3-mode format */ - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - } - else { - if (img_ssz == 512) { /* reject IBM PC 1.44MB format */ - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - } - - PC98_BIOS_FDC_CALL_GEO_UNPACK(/*&*/fdc_cyl[drive],/*&*/fdc_head[drive],/*&*/fdc_sect[drive],/*&*/fdc_sz[drive]); - unitsize = PC98_FDC_SZ_TO_BYTES(fdc_sz[drive]); - if (0/*unitsize != img_ssz || img_heads == 0 || img_cyl == 0 || img_sect == 0*/) { - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - - size = reg_bx; - while (size > 0) { - accsize = size > unitsize ? unitsize : size; - - if (floppy->Read_Sector(fdc_head[drive],fdc_cyl[drive],fdc_sect[drive],PC98_BIOS_FLOPPY_BUFFER,unitsize) != 0) { - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - - size -= accsize; - - if (size == 0) break; - - if ((++fdc_sect[drive]) > img_sect && img_sect != 0) { - fdc_sect[drive] = 1; - if ((++fdc_head[drive]) >= img_heads && img_heads != 0) { - fdc_head[drive] = 0; - fdc_cyl[drive]++; - } - } - } - - reg_ah = 0x00; - CALLBACK_SCF(false); - break; - case 0x02: /* read sectors */ - case 0x06: /* read sectors (what's the difference from 0x02?) */ - /* AH bits[4:4] = If set, seek to track specified */ - /* CL = cylinder (track) */ - /* CH = sector size (0=128 1=256 2=512 3=1024 etc) */ - /* DL = sector number (1-based) */ - /* DH = head */ - /* BX = size (in bytes) of data to read */ - /* ES:BP = buffer to read data into */ - if (floppy == NULL) { - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz); - - if (enable_fdc_timer_hack) { - // Hack for Ys II - FDC_WAIT_TIMER_HACK(); - } - - /* Prevent reading 1.44MB floppyies using 1.2MB read commands and vice versa. - * FIXME: It seems MS-DOS 5.0 booted from a HDI image has trouble understanding - * when Drive A: (the first floppy) is a 1.44MB drive or not and fails - * because it only attempts it using 1.2MB format read commands. */ - if (flags & PC98_FLOPPY_RPM_IBMPC) { - if (img_ssz == 1024) { /* reject 1.2MB 3-mode format */ - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - } - else { - if (img_ssz == 512) { /* reject IBM PC 1.44MB format */ - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - } - - PC98_BIOS_FDC_CALL_GEO_UNPACK(/*&*/fdc_cyl[drive],/*&*/fdc_head[drive],/*&*/fdc_sect[drive],/*&*/fdc_sz[drive]); - unitsize = PC98_FDC_SZ_TO_BYTES(fdc_sz[drive]); - if (0/*unitsize != img_ssz || img_heads == 0 || img_cyl == 0 || img_sect == 0*/) { - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - - size = reg_bx; - memaddr = ((unsigned int)SegValue(es) << 4U) + reg_bp; - while (size > 0) { - accsize = size > unitsize ? unitsize : size; - - if (floppy->Read_Sector(fdc_head[drive],fdc_cyl[drive],fdc_sect[drive],PC98_BIOS_FLOPPY_BUFFER,unitsize) != 0) { - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - - for (unsigned int i=0;i < accsize;i++) - mem_writeb(memaddr+i,PC98_BIOS_FLOPPY_BUFFER[i]); - - memaddr += accsize; - size -= accsize; - - if (size == 0) break; - - if ((++fdc_sect[drive]) > img_sect && img_sect != 0) { - fdc_sect[drive] = 1; - if ((++fdc_head[drive]) >= img_heads && img_heads != 0) { - fdc_head[drive] = 0; - fdc_cyl[drive]++; - } - } - } - - reg_ah = 0x00; - CALLBACK_SCF(false); - break; - case 0x04: /* drive status */ - status = 0; - - /* TODO: bit 4 is set if write protected */ - - if (reg_al & 0x80) { /* high density */ - status |= 0x01; - } - else { /* double density */ - /* TODO: */ - status |= 0x01; - } - - if ((reg_ax & 0x8F40) == 0x8400) { - status |= 8; /* 1MB/640KB format, spindle speed for 3-mode */ - if (reg_ah & 0x40) /* DOSBox-X always supports 1.44MB */ - status |= 4; /* 1.44MB format, spindle speed for IBM PC format */ - } - - if (floppy == NULL) - status |= 0xC0; - - reg_ah = status; - CALLBACK_SCF(false); - break; - /* TODO: 0x00 = seek to track (in CL) */ - /* TODO: 0x01 = test read? */ - /* TODO: 0x03 = equipment flags? */ - /* TODO: 0x04 = format detect? */ - /* TODO: 0x05 = write disk */ - /* TODO: 0x07 = recalibrate (seek to track 0) */ - /* TODO: 0x0A = Read ID */ - /* TODO: 0x0D = Format track */ - /* TODO: 0x0E = ?? */ - case 0x05: /* write sectors */ - /* AH bits[4:4] = If set, seek to track specified */ - /* CL = cylinder (track) */ - /* CH = sector size (0=128 1=256 2=512 3=1024 etc) */ - /* DL = sector number (1-based) */ - /* DH = head */ - /* BX = size (in bytes) of data to read */ - /* ES:BP = buffer to write data from */ - if (floppy == NULL) { - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz); - - if (enable_fdc_timer_hack) { - // Hack for Ys II - FDC_WAIT_TIMER_HACK(); - } - - /* TODO: Error if write protected */ - - PC98_BIOS_FDC_CALL_GEO_UNPACK(/*&*/fdc_cyl[drive],/*&*/fdc_head[drive],/*&*/fdc_sect[drive],/*&*/fdc_sz[drive]); - unitsize = PC98_FDC_SZ_TO_BYTES(fdc_sz[drive]); - if (0/*unitsize != img_ssz || img_heads == 0 || img_cyl == 0 || img_sect == 0*/) { - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - - size = reg_bx; - memaddr = ((unsigned int)SegValue(es) << 4U) + reg_bp; - while (size > 0) { - accsize = size > unitsize ? unitsize : size; - - for (unsigned int i=0;i < accsize;i++) - PC98_BIOS_FLOPPY_BUFFER[i] = mem_readb(memaddr+i); - - if (floppy->Write_Sector(fdc_head[drive],fdc_cyl[drive],fdc_sect[drive],PC98_BIOS_FLOPPY_BUFFER,unitsize) != 0) { - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - - memaddr += accsize; - size -= accsize; - - if (size == 0) break; - - if ((++fdc_sect[drive]) > img_sect && img_sect != 0) { - fdc_sect[drive] = 1; - if ((++fdc_head[drive]) >= img_heads && img_heads != 0) { - fdc_head[drive] = 0; - fdc_cyl[drive]++; - } - } - } - - reg_ah = 0x00; - CALLBACK_SCF(false); - break; - case 0x07: /* recalibrate (seek to track 0) */ - if (floppy == NULL) { - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - - if (enable_fdc_timer_hack) { - // Hack for Ys II - FDC_WAIT_TIMER_HACK(); - } - - reg_ah = 0x00; - CALLBACK_SCF(false); - break; - case 0x0D: /* format track */ - if (floppy == NULL) { - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - - PC98_BIOS_FDC_CALL_GEO_UNPACK(/*&*/fdc_cyl[drive],/*&*/fdc_head[drive],/*&*/fdc_sect[drive],/*&*/fdc_sz[drive]); - unitsize = PC98_FDC_SZ_TO_BYTES(fdc_sz[drive]); - - if (enable_fdc_timer_hack) { - // Hack for Ys II - FDC_WAIT_TIMER_HACK(); - } - - LOG_MSG("WARNING: INT 1Bh FDC format track command not implemented. Formatting is faked, for now on C/H/S/sz %u/%u/%u/%u drive %c.", - (unsigned int)fdc_cyl[drive], - (unsigned int)fdc_head[drive], - (unsigned int)fdc_sect[drive], - (unsigned int)unitsize, - drive + 'A'); - - reg_ah = 0x00; - CALLBACK_SCF(false); - break; - case 0x0A: /* read ID */ - /* NTS: PC-98 "MEGDOS" used by some games seems to rely heavily on this call to - * verify the floppy head is where it thinks it should be! */ - if (floppy == NULL) { - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - - floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz); - - if (enable_fdc_timer_hack) { - // Hack for Ys II - FDC_WAIT_TIMER_HACK(); - } - - if (reg_ah & 0x10) { // seek to track number in CL - if (img_cyl != 0 && reg_cl >= img_cyl) { - CALLBACK_SCF(true); - reg_ah = 0x00; - /* TODO? Error code? */ - return; - } - - fdc_cyl[drive] = reg_cl; - } - - if (fdc_sect[drive] == 0) - fdc_sect[drive] = 1; - - if (img_ssz >= 1024) - fdc_sz[drive] = 3; - else if (img_ssz >= 512) - fdc_sz[drive] = 2; - else if (img_ssz >= 256) - fdc_sz[drive] = 1; - else - fdc_sz[drive] = 0; - - reg_cl = fdc_cyl[drive]; - reg_dh = fdc_head[drive]; - reg_dl = fdc_sect[drive]; - /* ^ FIXME: A more realistic emulation would return a random number from 1 to N - * where N=sectors/track because the floppy motor is running and tracks - * are moving past the head. */ - reg_ch = fdc_sz[drive]; - - /* per read ID call, increment the sector through the range on disk. - * This is REQUIRED or else MEGDOS will not attempt to read this disk. */ - if (img_sect != 0) { - if ((++fdc_sect[drive]) > img_sect) - fdc_sect[drive] = 1; - } - - reg_ah = 0x00; - CALLBACK_SCF(false); - break; - default: - LOG_MSG("PC-98 INT 1Bh unknown FDC BIOS call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", - reg_ax, - reg_bx, - reg_cx, - reg_dx, - reg_si, - reg_di, - SegValue(ds), - SegValue(es)); - CALLBACK_SCF(true); - break; - }; -} - -static Bitu INT19_PC98_Handler(void) { - LOG_MSG("PC-98 INT 19h unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", - reg_ax, - reg_bx, - reg_cx, - reg_dx, - reg_si, - reg_di, - SegValue(ds), - SegValue(es)); - - return CBRET_NONE; -} - -static Bitu INT1A_PC98_Handler(void) { - /* HACK: This makes the "test" program in DOSLIB work. - * We'll remove this when we implement INT 1Ah */ - if (reg_ax == 0x1000) { - CALLBACK_SCF(false); - reg_ax = 0; - } - - LOG_MSG("PC-98 INT 1Ah unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", - reg_ax, - reg_bx, - reg_cx, - reg_dx, - reg_si, - reg_di, - SegValue(ds), - SegValue(es)); - - return CBRET_NONE; -} - -static Bitu INT1B_PC98_Handler(void) { - /* As BIOS interfaces for disk I/O go, this is fairly unusual */ - switch (reg_al & 0xF0) { - /* floppy disk access */ - /* AL bits[1:0] = floppy drive number */ - /* Uses INT42 if high density, INT41 if double density */ - /* AH bits[3:0] = command */ - case 0x90: /* 1.2MB HD */ - PC98_BIOS_FDC_CALL(PC98_FLOPPY_HIGHDENSITY|PC98_FLOPPY_2HEAD|PC98_FLOPPY_RPM_3MODE); - break; - case 0x30: /* 1.44MB HD (NTS: not supported until the early 1990s) */ - case 0xB0: - PC98_BIOS_FDC_CALL(PC98_FLOPPY_HIGHDENSITY|PC98_FLOPPY_2HEAD|PC98_FLOPPY_RPM_IBMPC); - break; - case 0x70: /* 720KB DD (??) */ - case 0xF0: - PC98_BIOS_FDC_CALL(PC98_FLOPPY_2HEAD|PC98_FLOPPY_RPM_3MODE); // FIXME, correct?? - break; - case 0x20: /* SCSI hard disk BIOS */ - case 0xA0: /* SCSI hard disk BIOS */ - case 0x00: /* SASI hard disk BIOS */ - case 0x80: /* SASI hard disk BIOS */ - PC98_BIOS_SCSI_CALL(); - break; - /* TODO: Other disk formats */ - /* TODO: Future SASI/SCSI BIOS emulation for hard disk images */ - default: - LOG_MSG("PC-98 INT 1Bh unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", - reg_ax, - reg_bx, - reg_cx, - reg_dx, - reg_si, - reg_di, - SegValue(ds), - SegValue(es)); - CALLBACK_SCF(true); - break; - }; - - return CBRET_NONE; -} - -void PC98_Interval_Timer_Continue(void) { - /* assume: interrupts are disabled */ - IO_WriteB(0x71,0x00); - // TODO: What time interval is this supposed to be? - if (PIT_TICK_RATE == PIT_TICK_RATE_PC98_8MHZ) - IO_WriteB(0x71,0x4E); - else - IO_WriteB(0x71,0x60); - - IO_WriteB(0x02,IO_ReadB(0x02) & (~(1u << /*IRQ*/0u))); // unmask IRQ0 -} - -unsigned char pc98_dec2bcd(unsigned char c) { - return ((c / 10u) << 4u) + (c % 10u); -} - -static Bitu INT1C_PC98_Handler(void) { - if (reg_ah == 0x00) { /* get time and date */ - time_t curtime; - struct tm *loctime; - curtime = time (NULL); - loctime = localtime (&curtime); - - unsigned char tmp[6]; - - tmp[0] = pc98_dec2bcd((unsigned int)loctime->tm_year % 100u); - tmp[1] = (((unsigned int)loctime->tm_mon + 1u) << 4u) + (unsigned int)loctime->tm_wday; - tmp[2] = pc98_dec2bcd(loctime->tm_mday); - tmp[3] = pc98_dec2bcd(loctime->tm_hour); - tmp[4] = pc98_dec2bcd(loctime->tm_min); - tmp[5] = pc98_dec2bcd(loctime->tm_sec); - - unsigned long mem = ((unsigned int)SegValue(es) << 4u) + reg_bx; - - for (unsigned int i=0;i < 6;i++) - mem_writeb(mem+i,tmp[i]); - } - else if (reg_ah == 0x02) { /* set interval timer (single event) */ - /* es:bx = interrupt handler to execute - * cx = timer interval in ticks (FIXME: what units of time?) */ - mem_writew(0x1C,reg_bx); - mem_writew(0x1E,SegValue(es)); - mem_writew(0x58A,reg_cx); - - IO_WriteB(0x77,0x36); /* mode 3, binary, low-byte high-byte 16-bit counter */ - - PC98_Interval_Timer_Continue(); - } - else if (reg_ah == 0x03) { /* continue interval timer */ - PC98_Interval_Timer_Continue(); - } - /* TODO: According to the PDF at - * - * http://hackipedia.org/browse.cgi/Computer/Platform/PC%2c%20NEC%20PC%2d98/Collections/PC%2d9801%20Bible%20%e6%9d%b1%e4%ba%ac%e7%90%86%e7%a7%91%e5%a4%a7%e5%ad%a6EIC%20%281994%29%2epdf - * - * There are additional functions - * - * AH = 04h - * ES:BX = ? - * - * --- - * - * AH = 05h - * ES:BX = ? - * - * --- - * - * AH = 06h - * CX = ? (1-FFFFh) - * DX = ? (20h-8000h Hz) - * - * If any PC-98 games or applications rely on this, let me know. Adding a case for them is easy enough if anyone is interested. --J.C. - */ - else { - LOG_MSG("PC-98 INT 1Ch unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", - reg_ax, - reg_bx, - reg_cx, - reg_dx, - reg_si, - reg_di, - SegValue(ds), - SegValue(es)); - } - - return CBRET_NONE; -} - -static Bitu INT1D_PC98_Handler(void) { - LOG_MSG("PC-98 INT 1Dh unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", - reg_ax, - reg_bx, - reg_cx, - reg_dx, - reg_si, - reg_di, - SegValue(ds), - SegValue(es)); - - return CBRET_NONE; -} - -static Bitu INT1E_PC98_Handler(void) { - LOG_MSG("PC-98 INT 1Eh unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", - reg_ax, - reg_bx, - reg_cx, - reg_dx, - reg_si, - reg_di, - SegValue(ds), - SegValue(es)); - - return CBRET_NONE; -} - -void PC98_EXTMEMCPY(void) { - bool enabled = MEM_A20_Enabled(); - MEM_A20_Enable(true); - - Bitu bytes = ((reg_cx - 1u) & 0xFFFFu) + 1u; // bytes, except that 0 == 64KB - PhysPt data = SegPhys(es)+reg_bx; - PhysPt source = (mem_readd(data+0x12u) & 0x00FFFFFFu) + ((unsigned int)mem_readb(data+0x17u)<<24u); - PhysPt dest = (mem_readd(data+0x1Au) & 0x00FFFFFFu) + ((unsigned int)mem_readb(data+0x1Fu)<<24u); - - LOG_MSG("PC-98 memcpy: src=0x%x dst=0x%x data=0x%x count=0x%x", - (unsigned int)source,(unsigned int)dest,(unsigned int)data,(unsigned int)bytes); - - MEM_BlockCopy(dest,source,bytes); - MEM_A20_Enable(enabled); - Segs.limit[cs] = 0xFFFF; - Segs.limit[ds] = 0xFFFF; - Segs.limit[es] = 0xFFFF; - Segs.limit[ss] = 0xFFFF; - - CALLBACK_SCF(false); -} - -static Bitu INT1F_PC98_Handler(void) { - switch (reg_ah) { - case 0x90: - /* Copy extended memory */ - PC98_EXTMEMCPY(); - break; - default: - LOG_MSG("PC-98 INT 1Fh unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", - reg_ax, - reg_bx, - reg_cx, - reg_dx, - reg_si, - reg_di, - SegValue(ds), - SegValue(es)); - CALLBACK_SCF(true); - break; - }; - - return CBRET_NONE; -} - -static Bitu INTGEN_PC98_Handler(void) { - LOG_MSG("PC-98 INT stub unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", - reg_ax, - reg_bx, - reg_cx, - reg_dx, - reg_si, - reg_di, - SegValue(ds), - SegValue(es)); - - return CBRET_NONE; -} - -/* This interrupt should only exist while the DOS kernel is active. - * On actual PC-98 MS-DOS this is a direct interface to MS-DOS's built-in ANSI CON driver. - * - * CL = major function call number - * AH = minor function call number - * DX = data?? */ -extern bool dos_kernel_disabled; - -void PC98_INTDC_WriteChar(unsigned char b); - -static Bitu INTDC_PC98_Handler(void) { - if (dos_kernel_disabled) goto unknown; - - switch (reg_cl) { - case 0x10: - if (reg_ah == 0x00) { /* CL=0x10 AH=0x00 DL=char write char to CON */ - PC98_INTDC_WriteChar(reg_dl); - goto done; - } - else if (reg_ah == 0x01) { /* CL=0x10 AH=0x01 DS:DX write string to CON */ - /* According to the example at http://tepe.tec.fukuoka-u.ac.jp/HP98/studfile/grth/gt10.pdf - * the string ends in '$' just like the main DOS string output function. */ - Bit16u ofs = reg_dx; - do { - unsigned char c = real_readb(SegValue(ds),ofs++); - if (c == '$') break; - PC98_INTDC_WriteChar(c); - } while (1); - goto done; - } - else if (reg_ah == 0x02) { /* CL=0x10 AH=0x02 DL=attribute set console output attribute */ - /* Ref: https://nas.jmc/jmcs/docs/browse/Computer/Platform/PC%2c%20NEC%20PC%2d98/Collections/Undocumented%209801%2c%209821%20Volume%202%20%28webtech.co.jp%29%20English%20translation/memdos%2eenglish%2dgoogle%2dtranslate%2etxt - * - * DL is the attribute byte (in the format written directly to video RAM, not the ANSI code) - * - * NTS: Reverse engineering INT DCh shows it sets both 71Dh and 73Ch as below */ - mem_writeb(0x71D,reg_dl); /* 60:11D */ - mem_writeb(0x73C,reg_dx); /* 60:13C */ - goto done; - } - else if (reg_ah == 0x03) { /* CL=0x10 AH=0x03 CL=Y-coord CH=X-coord set cursor position */ - /* Reverse engineered from INT DCh. Note that the code path is the same taken for ESC = */ - goto unknown; /* TODO: */ - } - else if (reg_ah == 0x04) { /* CL=0x10 AH=0x04 Move cursor down one line */ - /* Reverse engineered from INT DCh. Note that the code path is the same taken for ESC E */ - goto unknown; /* TODO: */ - } - else if (reg_ah == 0x05) { /* CL=0x10 AH=0x05 Move cursor up one line */ - /* Reverse engineered from INT DCh. Note that the code path is the same taken for ESC M */ - goto unknown; /* TODO: */ - } - goto unknown; - default: /* some compilers don't like not having a default case */ - goto unknown; - }; - -done: - return CBRET_NONE; - -unknown: - LOG_MSG("PC-98 INT DCh unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", - reg_ax, - reg_bx, - reg_cx, - reg_dx, - reg_si, - reg_di, - SegValue(ds), - SegValue(es)); - - return CBRET_NONE; -} - -static Bitu INTF2_PC98_Handler(void) { - LOG_MSG("PC-98 INT F2h unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", - reg_ax, - reg_bx, - reg_cx, - reg_dx, - reg_si, - reg_di, - SegValue(ds), - SegValue(es)); - - return CBRET_NONE; -} - -static Bitu PC98_BIOS_LIO(void) { - /* on entry, AL (from our BIOS code) is set to the call number that lead here */ - LOG_MSG("PC-98 BIOS LIO graphics call 0x%02x with AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", - reg_al, - reg_ax, - reg_bx, - reg_cx, - reg_dx, - reg_si, - reg_di, - SegValue(ds), - SegValue(es)); - - // from Yksoft1's patch - reg_ah = 0; - - return CBRET_NONE; -} - -static Bitu INT11_Handler(void) { - reg_ax=mem_readw(BIOS_CONFIGURATION); - return CBRET_NONE; -} -/* - * Define the following define to 1 if you want dosbox to check - * the system time every 5 seconds and adjust 1/2 a second to sync them. - */ -#ifndef DOSBOX_CLOCKSYNC -#define DOSBOX_CLOCKSYNC 0 -#endif - -static void BIOS_HostTimeSync() { - /* Setup time and date */ - struct timeb timebuffer; - ftime(&timebuffer); - - struct tm *loctime; - loctime = localtime (&timebuffer.time); - - /* - loctime->tm_hour = 23; - loctime->tm_min = 59; - loctime->tm_sec = 45; - loctime->tm_mday = 28; - loctime->tm_mon = 2-1; - loctime->tm_year = 2007 - 1900; - */ - - dos.date.day=(Bit8u)loctime->tm_mday; - dos.date.month=(Bit8u)loctime->tm_mon+1; - dos.date.year=(Bit16u)loctime->tm_year+1900; - - Bit32u ticks=(Bit32u)(((double)( - loctime->tm_hour*3600*1000+ - loctime->tm_min*60*1000+ - loctime->tm_sec*1000+ - timebuffer.millitm))*(((double)PIT_TICK_RATE/65536.0)/1000.0)); - mem_writed(BIOS_TIMER,ticks); -} - -// TODO: make option -bool enable_bios_timer_synchronize_keyboard_leds = true; - -void KEYBOARD_SetLEDs(Bit8u bits); - -void BIOS_KEYBOARD_SetLEDs(Bitu state) { - Bitu x = mem_readb(BIOS_KEYBOARD_LEDS); - - x &= ~7u; - x |= (state & 7u); - mem_writeb(BIOS_KEYBOARD_LEDS,x); - KEYBOARD_SetLEDs(state); -} - -/* PC-98 IRQ 0 system timer */ -static Bitu INT8_PC98_Handler(void) { - Bit16u counter = mem_readw(0x58A) - 1; - mem_writew(0x58A,counter); - - /* NTS 2018/02/23: I just confirmed from the ROM BIOS of an actual - * PC-98 system that this implementation and Neko Project II - * are 100% accurate to what the BIOS actually does. - * INT 07h really is the "timer tick" interrupt called - * from INT 08h / IRQ 0, and the BIOS really does call - * INT 1Ch AH=3 from INT 08h if the tick count has not - * yet reached zero. - * - * I'm guessing NEC's BIOS developers invented this prior - * to the Intel 80286 and it's INT 07h - * "Coprocessor not present" exception. */ - - if (counter == 0) { - /* mask IRQ 0 */ - IO_WriteB(0x02,IO_ReadB(0x02) | 0x01); - /* ack IRQ 0 */ - IO_WriteB(0x00,0x20); - /* INT 07h */ - CPU_Interrupt(7,CPU_INT_SOFTWARE,reg_eip); - } - else { - /* ack IRQ 0 */ - IO_WriteB(0x00,0x20); - /* make sure it continues ticking */ - PC98_Interval_Timer_Continue(); - } - - return CBRET_NONE; -} - -static Bitu INT8_Handler(void) { - /* Increase the bios tick counter */ - Bit32u value = mem_readd(BIOS_TIMER) + 1; - if(value >= 0x1800B0) { - // time wrap at midnight - mem_writeb(BIOS_24_HOURS_FLAG,mem_readb(BIOS_24_HOURS_FLAG)+1); - value=0; - } - - /* Legacy BIOS behavior: This isn't documented at all but most BIOSes - check the BIOS data area for LED keyboard status. If it sees that - value change, then it sends it to the keyboard. This is why on - older DOS machines you could change LEDs by writing to 40:17. - We have to emulate this also because Windows 3.1/9x seems to rely on - it when handling the keyboard from it's own driver. Their driver does - hook the keyboard and handles keyboard I/O by itself, but it still - allows the BIOS to do the keyboard magic from IRQ 0 (INT 8h). Yech. */ - if (enable_bios_timer_synchronize_keyboard_leds) { - Bitu should_be = (mem_readb(BIOS_KEYBOARD_STATE) >> 4) & 7; - Bitu led_state = (mem_readb(BIOS_KEYBOARD_LEDS) & 7); - - if (should_be != led_state) - BIOS_KEYBOARD_SetLEDs(should_be); - } - -#if DOSBOX_CLOCKSYNC - static bool check = false; - if((value %50)==0) { - if(((value %100)==0) && check) { - check = false; - time_t curtime;struct tm *loctime; - curtime = time (NULL);loctime = localtime (&curtime); - Bit32u ticksnu = (Bit32u)((loctime->tm_hour*3600+loctime->tm_min*60+loctime->tm_sec)*(float)PIT_TICK_RATE/65536.0); - Bit32s bios = value;Bit32s tn = ticksnu; - Bit32s diff = tn - bios; - if(diff>0) { - if(diff < 18) { diff = 0; } else diff = 9; - } else { - if(diff > -18) { diff = 0; } else diff = -9; - } - - value += diff; - } else if((value%100)==50) check = true; - } -#endif - mem_writed(BIOS_TIMER,value); - - /* decrease floppy motor timer */ - Bit8u val = mem_readb(BIOS_DISK_MOTOR_TIMEOUT); - if (val) mem_writeb(BIOS_DISK_MOTOR_TIMEOUT,val-1); - /* and running drive */ - mem_writeb(BIOS_DRIVE_RUNNING,mem_readb(BIOS_DRIVE_RUNNING) & 0xF0); - return CBRET_NONE; -} -#undef DOSBOX_CLOCKSYNC - -static Bitu INT1C_Handler(void) { - return CBRET_NONE; -} - -static Bitu INT12_Handler(void) { - reg_ax=mem_readw(BIOS_MEMORY_SIZE); - return CBRET_NONE; -} - -static Bitu INT17_Handler(void) { - if (reg_ah > 0x2 || reg_dx > 0x2) { // 0-2 printer port functions - // and no more than 3 parallel ports - LOG_MSG("BIOS INT17: Unhandled call AH=%2X DX=%4x",reg_ah,reg_dx); - return CBRET_NONE; - } - - switch(reg_ah) { - case 0x00: // PRINTER: Write Character - if(parallelPortObjects[reg_dx]!=0) { - if(parallelPortObjects[reg_dx]->Putchar(reg_al)) - reg_ah=parallelPortObjects[reg_dx]->getPrinterStatus(); - else reg_ah=1; - } - break; - case 0x01: // PRINTER: Initialize port - if(parallelPortObjects[reg_dx]!= 0) { - parallelPortObjects[reg_dx]->initialize(); - reg_ah=parallelPortObjects[reg_dx]->getPrinterStatus(); - } - break; - case 0x02: // PRINTER: Get Status - if(parallelPortObjects[reg_dx] != 0) - reg_ah=parallelPortObjects[reg_dx]->getPrinterStatus(); - //LOG_MSG("printer status: %x",reg_ah); - break; - }; - return CBRET_NONE; -} - -static bool INT14_Wait(Bit16u port, Bit8u mask, Bit8u timeout, Bit8u* retval) { - double starttime = PIC_FullIndex(); - double timeout_f = timeout * 1000.0; - while (((*retval = IO_ReadB(port)) & mask) != mask) { - if (starttime < (PIC_FullIndex() - timeout_f)) { - return false; - } - CALLBACK_Idle(); - } - return true; -} - -static Bitu INT4B_Handler(void) { - /* TODO: This is where the Virtual DMA specification is accessed on modern systems. - * When we implement that, move this to EMM386 emulation code. */ - - if (reg_ax >= 0x8102 && reg_ax <= 0x810D) { - LOG(LOG_MISC,LOG_DEBUG)("Guest OS attempted Virtual DMA specification call (INT 4Bh AX=%04x BX=%04x CX=%04x DX=%04x", - reg_ax,reg_bx,reg_cx,reg_dx); - } - else if (reg_ah == 0x80) { - LOG(LOG_MISC,LOG_DEBUG)("Guest OS attempted IBM SCSI interface call"); - } - else if (reg_ah <= 0x02) { - LOG(LOG_MISC,LOG_DEBUG)("Guest OS attempted TI Professional PC parallel port function AH=%02x",reg_ah); - } - else { - LOG(LOG_MISC,LOG_DEBUG)("Guest OS attempted unknown INT 4Bh call AX=%04x",reg_ax); - } - - /* Oh, I'm just a BIOS that doesn't know what the hell you're doing. CF=1 */ - CALLBACK_SCF(true); - return CBRET_NONE; -} - -static Bitu INT14_Handler(void) { - if (reg_ah > 0x3 || reg_dx > 0x3) { // 0-3 serial port functions - // and no more than 4 serial ports - LOG_MSG("BIOS INT14: Unhandled call AH=%2X DX=%4x",reg_ah,reg_dx); - return CBRET_NONE; - } - - Bit16u port = real_readw(0x40,reg_dx * 2u); // DX is always port number - Bit8u timeout = mem_readb((PhysPt)((unsigned int)BIOS_COM1_TIMEOUT + (unsigned int)reg_dx)); - if (port==0) { - LOG(LOG_BIOS,LOG_NORMAL)("BIOS INT14: port %d does not exist.",reg_dx); - return CBRET_NONE; - } - switch (reg_ah) { - case 0x00: { - // Initialize port - // Parameters: Return: - // AL: port parameters AL: modem status - // AH: line status - - // set baud rate - Bitu baudrate = 9600u; - Bit16u baudresult; - Bitu rawbaud=(Bitu)reg_al>>5u; - - if (rawbaud==0){ baudrate=110u;} - else if (rawbaud==1){ baudrate=150u;} - else if (rawbaud==2){ baudrate=300u;} - else if (rawbaud==3){ baudrate=600u;} - else if (rawbaud==4){ baudrate=1200u;} - else if (rawbaud==5){ baudrate=2400u;} - else if (rawbaud==6){ baudrate=4800u;} - else if (rawbaud==7){ baudrate=9600u;} - - baudresult = (Bit16u)(115200u / baudrate); - - IO_WriteB(port+3u, 0x80u); // enable divider access - IO_WriteB(port, (Bit8u)baudresult&0xffu); - IO_WriteB(port+1u, (Bit8u)(baudresult>>8u)); - - // set line parameters, disable divider access - IO_WriteB(port+3u, reg_al&0x1Fu); // LCR - - // disable interrupts - IO_WriteB(port+1u, 0u); // IER - - // get result - reg_ah=(Bit8u)(IO_ReadB(port+5u)&0xffu); - reg_al=(Bit8u)(IO_ReadB(port+6u)&0xffu); - CALLBACK_SCF(false); - break; - } - case 0x01: // Transmit character - // Parameters: Return: - // AL: character AL: unchanged - // AH: 0x01 AH: line status from just before the char was sent - // (0x80 | unpredicted) in case of timeout - // [undoc] (0x80 | line status) in case of tx timeout - // [undoc] (0x80 | modem status) in case of dsr/cts timeout - - // set DTR & RTS on - IO_WriteB(port+4u,0x3u); - // wait for DSR & CTS - if (INT14_Wait(port+6u, 0x30u, timeout, ®_ah)) { - // wait for TX buffer empty - if (INT14_Wait(port+5u, 0x20u, timeout, ®_ah)) { - // fianlly send the character - IO_WriteB(port,reg_al); - } else - reg_ah |= 0x80u; - } else // timed out - reg_ah |= 0x80u; - - CALLBACK_SCF(false); - break; - case 0x02: // Read character - // Parameters: Return: - // AH: 0x02 AL: received character - // [undoc] will be trashed in case of timeout - // AH: (line status & 0x1E) in case of success - // (0x80 | unpredicted) in case of timeout - // [undoc] (0x80 | line status) in case of rx timeout - // [undoc] (0x80 | modem status) in case of dsr timeout - - // set DTR on - IO_WriteB(port+4u,0x1u); - - // wait for DSR - if (INT14_Wait(port+6, 0x20, timeout, ®_ah)) { - // wait for character to arrive - if (INT14_Wait(port+5, 0x01, timeout, ®_ah)) { - reg_ah &= 0x1E; - reg_al = IO_ReadB(port); - } else - reg_ah |= 0x80; - } else - reg_ah |= 0x80; - - CALLBACK_SCF(false); - break; - case 0x03: // get status - reg_ah=(Bit8u)(IO_ReadB(port+5u)&0xffu); - reg_al=(Bit8u)(IO_ReadB(port+6u)&0xffu); - CALLBACK_SCF(false); - break; - - } - return CBRET_NONE; -} - -Bits HLT_Decode(void); -void KEYBOARD_AUX_Write(Bitu val); -unsigned char KEYBOARD_AUX_GetType(); -unsigned char KEYBOARD_AUX_DevStatus(); -unsigned char KEYBOARD_AUX_Resolution(); -unsigned char KEYBOARD_AUX_SampleRate(); -void KEYBOARD_ClrBuffer(void); - -static Bitu INT15_Handler(void) { - if( ( machine==MCH_AMSTRAD ) && ( reg_ah<0x07 ) ) { - switch(reg_ah) { - case 0x00: - // Read/Reset Mouse X/Y Counts. - // CX = Signed X Count. - // DX = Signed Y Count. - // CC. - case 0x01: - // Write NVR Location. - // AL = NVR Address to be written (0-63). - // BL = NVR Data to be written. - - // AH = Return Code. - // 00 = NVR Written Successfully. - // 01 = NVR Address out of range. - // 02 = NVR Data write error. - // CC. - case 0x02: - // Read NVR Location. - // AL = NVR Address to be read (0-63). - - // AH = Return Code. - // 00 = NVR read successfully. - // 01 = NVR Address out of range. - // 02 = NVR checksum error. - // AL = Byte read from NVR. - // CC. - default: - LOG(LOG_BIOS,LOG_NORMAL)("INT15 Unsupported PC1512 Call %02X",reg_ah); - return CBRET_NONE; - case 0x03: - // Write VDU Colour Plane Write Register. - vga.amstrad.write_plane = reg_al & 0x0F; - CALLBACK_SCF(false); - break; - case 0x04: - // Write VDU Colour Plane Read Register. - vga.amstrad.read_plane = reg_al & 0x03; - CALLBACK_SCF(false); - break; - case 0x05: - // Write VDU Graphics Border Register. - vga.amstrad.border_color = reg_al & 0x0F; - CALLBACK_SCF(false); - break; - case 0x06: - // Return ROS Version Number. - reg_bx = 0x0001; - CALLBACK_SCF(false); - break; - } - } - switch (reg_ah) { - case 0x06: - LOG(LOG_BIOS,LOG_NORMAL)("INT15 Unkown Function 6 (Amstrad?)"); - break; - case 0xC0: /* Get Configuration*/ - CPU_SetSegGeneral(es,biosConfigSeg); - reg_bx = 0; - reg_ah = 0; - CALLBACK_SCF(false); - break; - case 0x4f: /* BIOS - Keyboard intercept */ - /* Carry should be set but let's just set it just in case */ - CALLBACK_SCF(true); - break; - case 0x83: /* BIOS - SET EVENT WAIT INTERVAL */ - { - if(reg_al == 0x01) { /* Cancel it */ - mem_writeb(BIOS_WAIT_FLAG_ACTIVE,0); - IO_Write(0x70,0xb); - IO_Write(0x71,IO_Read(0x71)&~0x40); - CALLBACK_SCF(false); - break; - } - if (mem_readb(BIOS_WAIT_FLAG_ACTIVE)) { - reg_ah=0x80; - CALLBACK_SCF(true); - break; - } - Bit32u count=((Bit32u)reg_cx<<16u)|reg_dx; - mem_writed(BIOS_WAIT_FLAG_POINTER,RealMake(SegValue(es),reg_bx)); - mem_writed(BIOS_WAIT_FLAG_COUNT,count); - mem_writeb(BIOS_WAIT_FLAG_ACTIVE,1); - /* Reprogram RTC to start */ - IO_Write(0x70,0xb); - IO_Write(0x71,IO_Read(0x71)|0x40); - CALLBACK_SCF(false); - } - break; - case 0x84: /* BIOS - JOYSTICK SUPPORT (XT after 11/8/82,AT,XT286,PS) */ - if (reg_dx == 0x0000) { - // Get Joystick button status - if (JOYSTICK_IsEnabled(0) || JOYSTICK_IsEnabled(1)) { - reg_al = IO_ReadB(0x201)&0xf0; - CALLBACK_SCF(false); - } else { - // dos values - reg_ax = 0x00f0; reg_dx = 0x0201; - CALLBACK_SCF(true); - } - } else if (reg_dx == 0x0001) { - if (JOYSTICK_IsEnabled(0)) { - reg_ax = (Bit16u)(JOYSTICK_GetMove_X(0)*127+128); - reg_bx = (Bit16u)(JOYSTICK_GetMove_Y(0)*127+128); - if(JOYSTICK_IsEnabled(1)) { - reg_cx = (Bit16u)(JOYSTICK_GetMove_X(1)*127+128); - reg_dx = (Bit16u)(JOYSTICK_GetMove_Y(1)*127+128); - } - else { - reg_cx = reg_dx = 0; - } - CALLBACK_SCF(false); - } else if (JOYSTICK_IsEnabled(1)) { - reg_ax = reg_bx = 0; - reg_cx = (Bit16u)(JOYSTICK_GetMove_X(1)*127+128); - reg_dx = (Bit16u)(JOYSTICK_GetMove_Y(1)*127+128); - CALLBACK_SCF(false); - } else { - reg_ax = reg_bx = reg_cx = reg_dx = 0; - CALLBACK_SCF(true); - } - } else { - LOG(LOG_BIOS,LOG_ERROR)("INT15:84:Unknown Bios Joystick functionality."); - } - break; - case 0x86: /* BIOS - WAIT (AT,PS) */ - { - if (mem_readb(BIOS_WAIT_FLAG_ACTIVE)) { - reg_ah=0x83; - CALLBACK_SCF(true); - break; - } - Bit8u t; - Bit32u count=((Bit32u)reg_cx<<16u)|reg_dx; - mem_writed(BIOS_WAIT_FLAG_POINTER,RealMake(0,BIOS_WAIT_FLAG_TEMP)); - mem_writed(BIOS_WAIT_FLAG_COUNT,count); - mem_writeb(BIOS_WAIT_FLAG_ACTIVE,1); - - /* if the user has not set the option, warn if IRQs are masked */ - if (!int15_wait_force_unmask_irq) { - /* make sure our wait function works by unmasking IRQ 2, and IRQ 8. - * (bugfix for 1993 demo Yodel "Mayday" demo. this demo keeps masking IRQ 2 for some stupid reason.) */ - if ((t=IO_Read(0x21)) & (1 << 2)) { - LOG(LOG_BIOS,LOG_ERROR)("INT15:86:Wait: IRQ 2 masked during wait. " - "Consider adding 'int15 wait force unmask irq=true' to your dosbox.conf"); - } - if ((t=IO_Read(0xA1)) & (1 << 0)) { - LOG(LOG_BIOS,LOG_ERROR)("INT15:86:Wait: IRQ 8 masked during wait. " - "Consider adding 'int15 wait force unmask irq=true' to your dosbox.conf"); - } - } - - /* Reprogram RTC to start */ - IO_Write(0x70,0xb); - IO_Write(0x71,IO_Read(0x71)|0x40); - while (mem_readd(BIOS_WAIT_FLAG_COUNT)) { - if (int15_wait_force_unmask_irq) { - /* make sure our wait function works by unmasking IRQ 2, and IRQ 8. - * (bugfix for 1993 demo Yodel "Mayday" demo. this demo keeps masking IRQ 2 for some stupid reason.) */ - if ((t=IO_Read(0x21)) & (1 << 2)) { - LOG(LOG_BIOS,LOG_WARN)("INT15:86:Wait: IRQ 2 masked during wait. " - "This condition might result in an infinite wait on " - "some BIOSes. Unmasking IRQ to keep things moving along."); - IO_Write(0x21,t & ~(1 << 2)); - - } - if ((t=IO_Read(0xA1)) & (1 << 0)) { - LOG(LOG_BIOS,LOG_WARN)("INT15:86:Wait: IRQ 8 masked during wait. " - "This condition might result in an infinite wait on some " - "BIOSes. Unmasking IRQ to keep things moving along."); - IO_Write(0xA1,t & ~(1 << 0)); - } - } - - CALLBACK_Idle(); - } - CALLBACK_SCF(false); - break; - } - case 0x87: /* Copy extended memory */ - { - bool enabled = MEM_A20_Enabled(); - MEM_A20_Enable(true); - Bitu bytes = reg_cx * 2u; - PhysPt data = SegPhys(es)+reg_si; - PhysPt source = (mem_readd(data+0x12u) & 0x00FFFFFFu) + ((unsigned int)mem_readb(data+0x17u)<<24u); - PhysPt dest = (mem_readd(data+0x1Au) & 0x00FFFFFFu) + ((unsigned int)mem_readb(data+0x1Fu)<<24u); - MEM_BlockCopy(dest,source,bytes); - reg_ax = 0x00; - MEM_A20_Enable(enabled); - Segs.limit[cs] = 0xFFFF; - Segs.limit[ds] = 0xFFFF; - Segs.limit[es] = 0xFFFF; - Segs.limit[ss] = 0xFFFF; - CALLBACK_SCF(false); - break; - } - case 0x88: /* SYSTEM - GET EXTENDED MEMORY SIZE (286+) */ - /* This uses the 16-bit value read back from CMOS which is capped at 64MB */ - reg_ax=other_memsystems?0:size_extended; - LOG(LOG_BIOS,LOG_NORMAL)("INT15:Function 0x88 Remaining %04X kb",reg_ax); - CALLBACK_SCF(false); - break; - case 0x89: /* SYSTEM - SWITCH TO PROTECTED MODE */ - { - IO_Write(0x20,0x10);IO_Write(0x21,reg_bh);IO_Write(0x21,0);IO_Write(0x21,0xFF); - IO_Write(0xA0,0x10);IO_Write(0xA1,reg_bl);IO_Write(0xA1,0);IO_Write(0xA1,0xFF); - MEM_A20_Enable(true); - PhysPt table=SegPhys(es)+reg_si; - CPU_LGDT(mem_readw(table+0x8),mem_readd(table+0x8+0x2) & 0xFFFFFF); - CPU_LIDT(mem_readw(table+0x10),mem_readd(table+0x10+0x2) & 0xFFFFFF); - CPU_SET_CRX(0,CPU_GET_CRX(0)|1); - CPU_SetSegGeneral(ds,0x18); - CPU_SetSegGeneral(es,0x20); - CPU_SetSegGeneral(ss,0x28); - Bitu ret = mem_readw(SegPhys(ss)+reg_sp); - reg_sp+=6; //Clear stack of interrupt frame - CPU_SetFlags(0,FMASK_ALL); - reg_ax=0; - CPU_JMP(false,0x30,ret,0); - } - break; - case 0x8A: /* EXTENDED MEMORY SIZE */ - { - Bitu sz = MEM_TotalPages()*4; - if (sz >= 1024) sz -= 1024; - else sz = 0; - reg_ax = sz & 0xFFFF; - reg_dx = sz >> 16; - CALLBACK_SCF(false); - } - break; - case 0x90: /* OS HOOK - DEVICE BUSY */ - CALLBACK_SCF(false); - reg_ah=0; - break; - case 0x91: /* OS HOOK - DEVICE POST */ - CALLBACK_SCF(false); - reg_ah=0; - break; - case 0xc2: /* BIOS PS2 Pointing Device Support */ - /* TODO: Our reliance on AUX emulation means that at some point, AUX emulation - * must always be enabled if BIOS PS/2 emulation is enabled. Future planned change: - * - * If biosps2=true and aux=true, carry on what we're already doing now: emulate INT 15h by - * directly writing to the AUX port of the keyboard controller. - * - * If biosps2=false, the aux= setting enables/disables AUX emulation as it already does now. - * biosps2=false implies that we're emulating a keyboard controller with AUX but no BIOS - * support for it (however rare that might be). This gives the user of DOSBox-X the means - * to test that scenario especially in case he/she is developing a homebrew OS and needs - * to ensure their code can handle cases like that gracefully. - * - * If biosps2=true and aux=false, AUX emulation is enabled anyway, but the keyboard emulation - * must act as if the AUX port is not there so the guest OS cannot control it. Again, not - * likely on real hardware, but a useful test case for homebrew OS developers. - * - * If you the user set aux=false, then you obviously want to test a system configuration - * where the keyboard controller has no AUX port. If you set biosps2=true, then you want to - * test an OS that uses BIOS functions to setup the "pointing device" but you do not want the - * guest OS to talk directly to the AUX port on the keyboard controller. - * - * Logically that's not likely to happen on real hardware, but we like giving the end-user - * options to play with, so instead, if aux=false and biosps2=true, DOSBox-X should print - * a warning stating that INT 15h mouse emulation with a PS/2 port is nonstandard and may - * cause problems with OSes that need to talk directly to hardware. - * - * It is noteworthy that PS/2 mouse support in MS-DOS mouse drivers as well as Windows 3.x, - * Windows 95, and Windows 98, is carried out NOT by talking directly to the AUX port but - * instead by relying on the BIOS INT 15h functions here to do the dirty work. For those - * scenarios, biosps2=true and aux=false is perfectly safe and does not cause issues. - * - * OSes that communicate directly with the AUX port however (Linux, Windows NT) will not work - * unless aux=true. */ - if (en_bios_ps2mouse) { -// LOG_MSG("INT 15h AX=%04x BX=%04x\n",reg_ax,reg_bx); - switch (reg_al) { - case 0x00: // enable/disable - if (reg_bh==0) { // disable - KEYBOARD_AUX_Write(0xF5); - Mouse_SetPS2State(false); - reg_ah=0; - CALLBACK_SCF(false); - KEYBOARD_ClrBuffer(); - } else if (reg_bh==0x01) { //enable - if (!Mouse_SetPS2State(true)) { - reg_ah=5; - CALLBACK_SCF(true); - break; - } - KEYBOARD_AUX_Write(0xF4); - KEYBOARD_ClrBuffer(); - reg_ah=0; - CALLBACK_SCF(false); - } else { - CALLBACK_SCF(true); - reg_ah=1; - } - break; - case 0x01: // reset - KEYBOARD_AUX_Write(0xFF); - Mouse_SetPS2State(false); - KEYBOARD_ClrBuffer(); - reg_bx=0x00aa; // mouse - // fall through - case 0x05: // initialize - if (reg_bh >= 3 && reg_bh <= 4) { - /* TODO: BIOSes remember this value as the number of bytes to store before - * calling the device callback. Setting this value to "1" is perfectly - * valid if you want a byte-stream like mode (at the cost of one - * interrupt per byte!). Most OSes will call this with BH=3 for standard - * PS/2 mouse protocol. You can also call this with BH=4 for Intellimouse - * protocol support, though testing (so far with VirtualBox) shows the - * device callback still only gets the first three bytes on the stack. - * Contrary to what you might think, the BIOS does not interpret the - * bytes at all. - * - * The source code of CuteMouse 1.9 seems to suggest some BIOSes take - * pains to repack the 4th byte in the upper 8 bits of one of the WORDs - * on the stack in Intellimouse mode at the cost of shifting the W and X - * fields around. I can't seem to find any source on who does that or - * if it's even true, so I disregard the example at this time. - * - * Anyway, you need to store off this value somewhere and make use of - * it in src/ints/mouse.cpp device callback emulation to reframe the - * PS/2 mouse bytes coming from AUX (if aux=true) or emulate the - * re-framing if aux=false to emulate this protocol fully. */ - LOG_MSG("INT 15h mouse initialized to %u-byte protocol\n",reg_bh); - KEYBOARD_AUX_Write(0xF6); /* set defaults */ - Mouse_SetPS2State(false); - KEYBOARD_ClrBuffer(); - CALLBACK_SCF(false); - reg_ah=0; - } - else { - CALLBACK_SCF(false); - reg_ah=0x02; /* invalid input */ - } - break; - case 0x02: { // set sampling rate - static const unsigned char tbl[7] = {10,20,40,60,80,100,200}; - KEYBOARD_AUX_Write(0xF3); - if (reg_bl > 6) reg_bl = 6; - KEYBOARD_AUX_Write(tbl[reg_bh]); - KEYBOARD_ClrBuffer(); - CALLBACK_SCF(false); - reg_ah=0; - } break; - case 0x03: // set resolution - KEYBOARD_AUX_Write(0xE8); - KEYBOARD_AUX_Write(reg_bh&3); - KEYBOARD_ClrBuffer(); - CALLBACK_SCF(false); - reg_ah=0; - break; - case 0x04: // get type - reg_bh=KEYBOARD_AUX_GetType(); // ID - LOG_MSG("INT 15h reporting mouse device ID 0x%02x\n",reg_bh); - KEYBOARD_ClrBuffer(); - CALLBACK_SCF(false); - reg_ah=0; - break; - case 0x06: // extended commands - if (reg_bh == 0x00) { - /* Read device status and info. - * Windows 9x does not appear to use this, but Windows NT 3.1 does (prior to entering - * full 32-bit protected mode) */ - CALLBACK_SCF(false); - reg_bx=KEYBOARD_AUX_DevStatus(); - reg_cx=KEYBOARD_AUX_Resolution(); - reg_dx=KEYBOARD_AUX_SampleRate(); - KEYBOARD_ClrBuffer(); - reg_ah=0; - } - else if ((reg_bh==0x01) || (reg_bh==0x02)) { /* set scaling */ - KEYBOARD_AUX_Write(0xE6u+reg_bh-1u); /* 0xE6 1:1 or 0xE7 2:1 */ - KEYBOARD_ClrBuffer(); - CALLBACK_SCF(false); - reg_ah=0; - } else { - CALLBACK_SCF(true); - reg_ah=1; - } - break; - case 0x07: // set callback - Mouse_ChangePS2Callback(SegValue(es),reg_bx); - CALLBACK_SCF(false); - reg_ah=0; - break; - default: - LOG_MSG("INT 15h unknown mouse call AX=%04x\n",reg_ax); - CALLBACK_SCF(true); - reg_ah=1; - break; - } - } - else { - reg_ah=0x86; - CALLBACK_SCF(true); - if ((IS_EGAVGA_ARCH) || (machine==MCH_CGA)) { - /* relict from comparisons, as int15 exits with a retf2 instead of an iret */ - CALLBACK_SZF(false); - } - } - break; - case 0xc3: /* set carry flag so BorlandRTM doesn't assume a VECTRA/PS2 */ - reg_ah=0x86; - CALLBACK_SCF(true); - break; - case 0xc4: /* BIOS POS Programm option Select */ - LOG(LOG_BIOS,LOG_NORMAL)("INT15:Function %X called, bios mouse not supported",reg_ah); - CALLBACK_SCF(true); - break; - case 0x53: // APM BIOS - if (APMBIOS) { - LOG(LOG_BIOS,LOG_DEBUG)("APM BIOS call AX=%04x BX=0x%04x CX=0x%04x\n",reg_ax,reg_bx,reg_cx); - switch(reg_al) { - case 0x00: // installation check - reg_ah = 1; // major - reg_al = APM_BIOS_minor_version; // minor - reg_bx = 0x504d; // 'PM' - reg_cx = (APMBIOS_allow_prot16?0x01:0x00) + (APMBIOS_allow_prot32?0x02:0x00); - // 32-bit interface seems to be needed for standby in win95 - CALLBACK_SCF(false); - break; - case 0x01: // connect real mode interface - if(!APMBIOS_allow_realmode) { - LOG_MSG("APM BIOS: OS attemped real-mode connection, which is disabled in your dosbox.conf\n"); - reg_ah = 0x86; // APM not present - CALLBACK_SCF(true); - break; - } - if(reg_bx != 0x0) { - reg_ah = 0x09; // unrecognized device ID - CALLBACK_SCF(true); - break; - } - if(!apm_realmode_connected) { // not yet connected - LOG_MSG("APM BIOS: Connected to real-mode interface\n"); - CALLBACK_SCF(false); - APMBIOS_connect_mode = APMBIOS_CONNECT_REAL; - apm_realmode_connected=true; - } else { - LOG_MSG("APM BIOS: OS attempted to connect to real-mode interface when already connected\n"); - reg_ah = APMBIOS_connected_already_err(); // interface connection already in effect - CALLBACK_SCF(true); - } - APM_BIOS_connected_minor_version = 0; - break; - case 0x02: // connect 16-bit protected mode interface - if(!APMBIOS_allow_prot16) { - LOG_MSG("APM BIOS: OS attemped 16-bit protected mode connection, which is disabled in your dosbox.conf\n"); - reg_ah = 0x06; // not supported - CALLBACK_SCF(true); - break; - } - if(reg_bx != 0x0) { - reg_ah = 0x09; // unrecognized device ID - CALLBACK_SCF(true); - break; - } - if(!apm_realmode_connected) { // not yet connected - /* NTS: We use the same callback address for both 16-bit and 32-bit - * because only the DOS callback and RETF instructions are involved, - * which can be executed as either 16-bit or 32-bit code without problems. */ - LOG_MSG("APM BIOS: Connected to 16-bit protected mode interface\n"); - CALLBACK_SCF(false); - reg_ax = INT15_apm_pmentry >> 16; // AX = 16-bit code segment (real mode base) - reg_bx = INT15_apm_pmentry & 0xFFFF; // BX = offset of entry point - reg_cx = INT15_apm_pmentry >> 16; // CX = 16-bit data segment (NTS: doesn't really matter) - reg_si = 0xFFFF; // SI = code segment length - reg_di = 0xFFFF; // DI = data segment length - APMBIOS_connect_mode = APMBIOS_CONNECT_PROT16; - apm_realmode_connected=true; - } else { - LOG_MSG("APM BIOS: OS attempted to connect to 16-bit protected mode interface when already connected\n"); - reg_ah = APMBIOS_connected_already_err(); // interface connection already in effect - CALLBACK_SCF(true); - } - APM_BIOS_connected_minor_version = 0; - break; - case 0x03: // connect 32-bit protected mode interface - // Note that Windows 98 will NOT talk to the APM BIOS unless the 32-bit protected mode connection is available. - // And if you lie about it in function 0x00 and then fail, Windows 98 will fail with a "Windows protection error". - if(!APMBIOS_allow_prot32) { - LOG_MSG("APM BIOS: OS attemped 32-bit protected mode connection, which is disabled in your dosbox.conf\n"); - reg_ah = 0x08; // not supported - CALLBACK_SCF(true); - break; - } - if(reg_bx != 0x0) { - reg_ah = 0x09; // unrecognized device ID - CALLBACK_SCF(true); - break; - } - if(!apm_realmode_connected) { // not yet connected - LOG_MSG("APM BIOS: Connected to 32-bit protected mode interface\n"); - CALLBACK_SCF(false); - /* NTS: We use the same callback address for both 16-bit and 32-bit - * because only the DOS callback and RETF instructions are involved, - * which can be executed as either 16-bit or 32-bit code without problems. */ - reg_ax = INT15_apm_pmentry >> 16; // AX = 32-bit code segment (real mode base) - reg_ebx = INT15_apm_pmentry & 0xFFFF; // EBX = offset of entry point - reg_cx = INT15_apm_pmentry >> 16; // CX = 16-bit code segment (real mode base) - reg_dx = INT15_apm_pmentry >> 16; // DX = data segment (real mode base) (?? what size?) - reg_esi = 0xFFFFFFFF; // ESI = upper word: 16-bit code segment len lower word: 32-bit code segment length - reg_di = 0xFFFF; // DI = data segment length - APMBIOS_connect_mode = APMBIOS_CONNECT_PROT32; - apm_realmode_connected=true; - } else { - LOG_MSG("APM BIOS: OS attempted to connect to 32-bit protected mode interface when already connected\n"); - reg_ah = APMBIOS_connected_already_err(); // interface connection already in effect - CALLBACK_SCF(true); - } - APM_BIOS_connected_minor_version = 0; - break; - case 0x04: // DISCONNECT INTERFACE - if(reg_bx != 0x0) { - reg_ah = 0x09; // unrecognized device ID - CALLBACK_SCF(true); - break; - } - if(apm_realmode_connected) { - LOG_MSG("APM BIOS: OS disconnected\n"); - CALLBACK_SCF(false); - apm_realmode_connected=false; - } else { - reg_ah = 0x03; // interface not connected - CALLBACK_SCF(true); - } - APM_BIOS_connected_minor_version = 0; - break; - case 0x05: // CPU IDLE - if(!apm_realmode_connected) { - reg_ah = 0x03; - CALLBACK_SCF(true); - break; - } - - // Trigger CPU HLT instruction. - // NTS: For whatever weird reason, NOT emulating HLT makes Windows 95 - // crashy when the APM driver is active! There's something within - // the Win95 kernel that apparently screws up really badly if - // the APM IDLE call returns immediately. The best case scenario - // seems to be that Win95's APM driver has some sort of timing - // logic to it that if it detects an immediate return, immediately - // shuts down and powers off the machine. Windows 98 also seems - // to require a HLT, and will act erratically without it. - // - // Also need to note that the choice of "HLT" is not arbitrary - // at all. The APM BIOS standard mentions CPU IDLE either stopping - // the CPU clock temporarily or issuing HLT as a valid method. - // - // TODO: Make this a dosbox.conf configuration option: what do we do - // on APM idle calls? Allow selection between "nothing" "hlt" - // and "software delay". - if (!(reg_flags&0x200)) { - LOG_MSG("APM BIOS warning: CPU IDLE called with IF=0, not HLTing\n"); - } - else if (cpudecoder == &HLT_Decode) { /* do not re-execute HLT, it makes DOSBox hang */ - LOG_MSG("APM BIOS warning: CPU IDLE HLT within HLT (DOSBox core failure)\n"); - } - else { - CPU_HLT(reg_eip); - } - break; - case 0x06: // CPU BUSY - if(!apm_realmode_connected) { - reg_ah = 0x03; - CALLBACK_SCF(true); - break; - } - - /* OK. whatever. system no longer idle */ - CALLBACK_SCF(false); - break; - case 0x07: - if(reg_bx != 0x1) { - reg_ah = 0x09; // wrong device ID - CALLBACK_SCF(true); - break; - } - if(!apm_realmode_connected) { - reg_ah = 0x03; - CALLBACK_SCF(true); - break; - } - switch(reg_cx) { - case 0x3: // power off - throw(0); - break; - default: - reg_ah = 0x0A; // invalid parameter value in CX - CALLBACK_SCF(true); - break; - } - break; - case 0x08: // ENABLE/DISABLE POWER MANAGEMENT - if(reg_bx != 0x0 && reg_bx != 0x1) { - reg_ah = 0x09; // unrecognized device ID - CALLBACK_SCF(true); - break; - } else if(!apm_realmode_connected) { - reg_ah = 0x03; - CALLBACK_SCF(true); - break; - } - if(reg_cx==0x0) LOG_MSG("disable APM for device %4x",reg_bx); - else if(reg_cx==0x1) LOG_MSG("enable APM for device %4x",reg_bx); - else { - reg_ah = 0x0A; // invalid parameter value in CX - CALLBACK_SCF(true); - } - break; - case 0x0a: // GET POWER STATUS - if (!apm_realmode_connected) { - reg_ah = 0x03; // interface not connected - CALLBACK_SCF(true); - break; - } - if (reg_bx != 0x0001 && reg_bx != 0x8001) { - reg_ah = 0x09; // unrecognized device ID - CALLBACK_SCF(true); - break; - } - /* FIXME: Allow configuration and shell commands to dictate whether or - * not we emulate a laptop with a battery */ - reg_bh = 0x01; // AC line status (1=on-line) - reg_bl = 0xFF; // Battery status (unknown) - reg_ch = 0x80; // Battery flag (no system battery) - reg_cl = 0xFF; // Remaining battery charge (unknown) - reg_dx = 0xFFFF; // Remaining battery life (unknown) - reg_si = 0; // Number of battery units (if called with reg_bx == 0x8001) - CALLBACK_SCF(false); - break; - case 0x0b: // GET PM EVENT - if (!apm_realmode_connected) { - reg_ah = 0x03; // interface not connected - CALLBACK_SCF(true); - break; - } - reg_ah = 0x80; // no power management events pending - CALLBACK_SCF(true); - break; - case 0x0d: - // NTS: NOT implementing this call can cause Windows 98's APM driver to crash on startup - if(reg_bx != 0x0 && reg_bx != 0x1) { - reg_ah = 0x09; // unrecognized device ID - CALLBACK_SCF(true); - break; - } else if(!apm_realmode_connected) { - reg_ah = 0x03; - CALLBACK_SCF(true); - break; - } - if(reg_cx==0x0) { - LOG_MSG("disable APM for device %4x",reg_bx); - CALLBACK_SCF(false); - } - else if(reg_cx==0x1) { - LOG_MSG("enable APM for device %4x",reg_bx); - CALLBACK_SCF(false); - } - else { - reg_ah = 0x0A; // invalid parameter value in CX - CALLBACK_SCF(true); - } - break; - case 0x0e: - if (APM_BIOS_minor_version != 0) { // APM 1.1 or higher only - if(reg_bx != 0x0) { - reg_ah = 0x09; // unrecognized device ID - CALLBACK_SCF(true); - break; - } else if(!apm_realmode_connected) { - reg_ah = 0x03; // interface not connected - CALLBACK_SCF(true); - break; - } - reg_ah = reg_ch; /* we are called with desired version in CH,CL, return actual version in AH,AL */ - reg_al = reg_cl; - if(reg_ah != 1) reg_ah = 1; // major - if(reg_al > APM_BIOS_minor_version) reg_al = APM_BIOS_minor_version; // minor - APM_BIOS_connected_minor_version = reg_al; // what we decided becomes the interface we emulate - LOG_MSG("APM BIOS negotiated to v1.%u",APM_BIOS_connected_minor_version); - CALLBACK_SCF(false); - } - else { // APM 1.0 does not recognize this call - reg_ah = 0x0C; // function not supported - CALLBACK_SCF(true); - } - break; - case 0x0f: - if(reg_bx != 0x0 && reg_bx != 0x1) { - reg_ah = 0x09; // unrecognized device ID - CALLBACK_SCF(true); - break; - } else if(!apm_realmode_connected) { - reg_ah = 0x03; - CALLBACK_SCF(true); - break; - } - if(reg_cx==0x0) { - LOG_MSG("disengage APM for device %4x",reg_bx); - CALLBACK_SCF(false); - } - else if(reg_cx==0x1) { - LOG_MSG("engage APM for device %4x",reg_bx); - CALLBACK_SCF(false); - } - else { - reg_ah = 0x0A; // invalid parameter value in CX - CALLBACK_SCF(true); - } - break; - case 0x10: - if (!apm_realmode_connected) { - reg_ah = 0x03; // interface not connected - CALLBACK_SCF(true); - break; - } - if (reg_bx != 0) { - reg_ah = 0x09; // unrecognized device ID - CALLBACK_SCF(true); - break; - } - reg_ah = 0; - reg_bl = 0; // number of battery units - reg_cx = 0x03; // can enter suspend/standby and will post standby/resume events - CALLBACK_SCF(false); - break; - case 0x13://enable/disable/query timer based requests - // NTS: NOT implementing this call can cause Windows 98's APM driver to crash on startup - if (!apm_realmode_connected) { - reg_ah = 0x03; // interface not connected - CALLBACK_SCF(true); - break; - } - if (reg_bx != 0) { - reg_ah = 0x09; // unrecognized device ID - CALLBACK_SCF(true); - break; - } - - if (reg_cx == 0) { // disable - APM_inactivity_timer = false; - reg_cx = 0; - CALLBACK_SCF(false); - } - else if (reg_cx == 1) { // enable - APM_inactivity_timer = true; - reg_cx = 1; - CALLBACK_SCF(false); - } - else if (reg_cx == 2) { // get enabled status - reg_cx = APM_inactivity_timer ? 1 : 0; - CALLBACK_SCF(false); - } - else { - reg_ah = 0x0A; // invalid parameter value in CX - CALLBACK_SCF(true); - } - break; - default: - LOG_MSG("Unknown APM BIOS call AX=%04x\n",reg_ax); - if (!apm_realmode_connected) { - reg_ah = 0x03; // interface not connected - CALLBACK_SCF(true); - break; - } - reg_ah = 0x0C; // function not supported - CALLBACK_SCF(true); - break; - } - } - else { - reg_ah=0x86; - CALLBACK_SCF(true); - LOG_MSG("APM BIOS call attempted. set apmbios=1 if you want power management\n"); - if ((IS_EGAVGA_ARCH) || (machine==MCH_CGA) || (machine==MCH_AMSTRAD)) { - /* relict from comparisons, as int15 exits with a retf2 instead of an iret */ - CALLBACK_SZF(false); - } - } - break; - case 0xe8: - switch (reg_al) { - case 0x01: { /* E801: memory size */ - Bitu sz = MEM_TotalPages()*4; - if (sz >= 1024) sz -= 1024; - else sz = 0; - reg_ax = reg_cx = (sz > 0x3C00) ? 0x3C00 : sz; /* extended memory between 1MB and 16MB in KBs */ - sz -= reg_ax; - sz /= 64; /* extended memory size from 16MB in 64KB blocks */ - if (sz > 65535) sz = 65535; - reg_bx = reg_dx = sz; - CALLBACK_SCF(false); - } - break; - case 0x20: { /* E820: MEMORY LISTING */ - if (reg_edx == 0x534D4150 && reg_ecx >= 20 && (MEM_TotalPages()*4) >= 24000) { - /* return a minimalist list: - * - * 0) 0x000000-0x09EFFF Free memory - * 1) 0x0C0000-0x0FFFFF Reserved - * 2) 0x100000-... Free memory (no ACPI tables) */ - if (reg_ebx < 3) { - uint32_t base,len,type; - Bitu seg = SegValue(es); - - assert((MEM_TotalPages()*4096) >= 0x100000); - - switch (reg_ebx) { - case 0: base=0x000000; len=0x09F000; type=1; break; - case 1: base=0x0C0000; len=0x040000; type=2; break; - case 2: base=0x100000; len=(MEM_TotalPages()*4096)-0x100000; type=1; break; - default: E_Exit("Despite checks EBX is wrong value"); /* BUG! */ - }; - - /* write to ES:DI */ - real_writed(seg,reg_di+0x00,base); - real_writed(seg,reg_di+0x04,0); - real_writed(seg,reg_di+0x08,len); - real_writed(seg,reg_di+0x0C,0); - real_writed(seg,reg_di+0x10,type); - reg_ecx = 20; - - /* return EBX pointing to next entry. wrap around, as most BIOSes do. - * the program is supposed to stop on CF=1 or when we return EBX == 0 */ - if (++reg_ebx >= 3) reg_ebx = 0; - } - else { - CALLBACK_SCF(true); - } - - reg_eax = 0x534D4150; - } - else { - reg_eax = 0x8600; - CALLBACK_SCF(true); - } - } - break; - default: - LOG(LOG_BIOS,LOG_ERROR)("INT15:Unknown call ah=E8, al=%2X",reg_al); - reg_ah=0x86; - CALLBACK_SCF(true); - if ((IS_EGAVGA_ARCH) || (machine==MCH_CGA) || (machine==MCH_AMSTRAD)) { - /* relict from comparisons, as int15 exits with a retf2 instead of an iret */ - CALLBACK_SZF(false); - } - } - break; - default: - LOG(LOG_BIOS,LOG_ERROR)("INT15:Unknown call ax=%4X",reg_ax); - reg_ah=0x86; - CALLBACK_SCF(true); - if ((IS_EGAVGA_ARCH) || (machine==MCH_CGA) || (machine==MCH_AMSTRAD)) { - /* relict from comparisons, as int15 exits with a retf2 instead of an iret */ - CALLBACK_SZF(false); - } - } - return CBRET_NONE; -} - -void BIOS_UnsetupKeyboard(void); -void BIOS_SetupKeyboard(void); -void BIOS_UnsetupDisks(void); -void BIOS_SetupDisks(void); -void CPU_Snap_Back_To_Real_Mode(); -void CPU_Snap_Back_Restore(); - -static Bitu IRQ14_Dummy(void) { - /* FIXME: That's it? Don't I EOI the PIC? */ - return CBRET_NONE; -} - -static Bitu IRQ15_Dummy(void) { - /* FIXME: That's it? Don't I EOI the PIC? */ - return CBRET_NONE; -} - -void On_Software_CPU_Reset(); - -static Bitu INT18_Handler(void) { - LOG_MSG("Restart by INT 18h requested\n"); - On_Software_CPU_Reset(); - /* does not return */ - return CBRET_NONE; -} - -static Bitu INT19_Handler(void) { - LOG_MSG("Restart by INT 19h requested\n"); - /* FIXME: INT 19h is sort of a BIOS boot BIOS reset-ish thing, not really a CPU reset at all. */ - On_Software_CPU_Reset(); - /* does not return */ - return CBRET_NONE; -} - -void bios_enable_ps2() { - mem_writew(BIOS_CONFIGURATION, - mem_readw(BIOS_CONFIGURATION)|0x04); /* PS/2 mouse */ -} - -void BIOS_ZeroExtendedSize(bool in) { - if(in) other_memsystems++; - else other_memsystems--; - if(other_memsystems < 0) other_memsystems=0; - - if (IS_PC98_ARCH) { - Bitu mempages = MEM_TotalPages(); /* in 4KB pages */ - - /* What applies to IBM PC/AT (zeroing out the extended memory size) - * also applies to PC-98, when HIMEM.SYS is loaded */ - if (in) mempages = 0; - - /* extended memory size (286 systems, below 16MB) */ - if (mempages > (1024UL/4UL)) { - unsigned int ext = ((mempages - (1024UL/4UL)) * 4096UL) / (128UL * 1024UL); /* convert to 128KB units */ - - /* extended memory, up to 16MB capacity (for 286 systems?) - * - * MS-DOS drivers will "allocate" for themselves by taking from the top of - * extended memory then subtracting from this value. - * - * capacity does not include conventional memory below 1MB, nor any memory - * above 16MB. - * - * PC-98 systems may reserve the top 1MB, limiting the top to 15MB instead. - * - * 0x70 = 128KB * 0x70 = 14MB - * 0x78 = 128KB * 0x70 = 15MB */ - if (ext > 0x78) ext = 0x78; - - mem_writeb(0x401,ext); - } - else { - mem_writeb(0x401,0x00); - } - - /* extended memory size (386 systems, at or above 16MB) */ - if (mempages > ((1024UL*16UL)/4UL)) { - unsigned int ext = ((mempages - ((1024UL*16UL)/4UL)) * 4096UL) / (1024UL * 1024UL); /* convert to MB */ - - /* extended memory, at or above 16MB capacity (for 386+ systems?) - * - * MS-DOS drivers will "allocate" for themselves by taking from the top of - * extended memory then subtracting from this value. - * - * capacity does not include conventional memory below 1MB, nor any memory - * below 16MB. */ - if (ext > 0xFFFE) ext = 0xFFFE; - - mem_writew(0x594,ext); - } - else { - mem_writew(0x594,0x00); - } - } -} - -unsigned char do_isapnp_chksum(unsigned char *d,int i) { - unsigned char sum = 0; - - while (i-- > 0) - sum += *d++; - - return (0x100 - sum) & 0xFF; -} - -void MEM_ResetPageHandler_Unmapped(Bitu phys_page, Bitu pages); - -unsigned int dos_conventional_limit = 0; - -bool AdapterROM_Read(Bitu address,unsigned long *size) { - unsigned char chksum=0; - unsigned char c[3]; - unsigned int i; - - if ((address & 0x1FF) != 0) { - LOG(LOG_MISC,LOG_DEBUG)("AdapterROM_Read: Caller attempted ROM scan not aligned to 512-byte boundary"); - return false; - } - - for (i=0;i < 3;i++) - c[i] = mem_readb(address+i); - - if (c[0] == 0x55 && c[1] == 0xAA) { - *size = (unsigned long)c[2] * 512UL; - for (i=0;i < (unsigned int)(*size);i++) chksum += mem_readb(address+i); - if (chksum != 0) { - LOG(LOG_MISC,LOG_WARN)("AdapterROM_Read: Found ROM at 0x%lx but checksum failed\n",(unsigned long)address); - return false; - } - - return true; - } - - return false; -} - -#include "src/gui/dosbox.vga16.bmp.h" -#include "src/gui/dosbox.cga640.bmp.h" - -void DrawDOSBoxLogoCGA6(unsigned int x,unsigned int y) { - unsigned char *s = dosbox_cga640_bmp; - unsigned char *sf = s + sizeof(dosbox_cga640_bmp); - uint32_t width,height; - unsigned int dx,dy; - uint32_t vram; - uint32_t off; - uint32_t sz; - - if (memcmp(s,"BM",2)) return; - sz = host_readd(s+2); // size of total bitmap - off = host_readd(s+10); // offset of bitmap - if ((s+sz) > sf) return; - if ((s+14+40) > sf) return; - - sz = host_readd(s+34); // biSize - if ((s+off+sz) > sf) return; - if (host_readw(s+26) != 1) return; // biBitPlanes - if (host_readw(s+28) != 1) return; // biBitCount - - width = host_readd(s+18); - height = host_readd(s+22); - if (width > (640-x) || height > (200-y)) return; - - LOG(LOG_MISC,LOG_DEBUG)("Drawing CGA logo (%u x %u)",(int)width,(int)height); - for (dy=0;dy < height;dy++) { - vram = ((y+dy) >> 1) * 80; - vram += ((y+dy) & 1) * 0x2000; - vram += (x / 8); - s = dosbox_cga640_bmp + off + ((height-(dy+1))*((width+7)/8)); - for (dx=0;dx < width;dx += 8) { - mem_writeb(0xB8000+vram,*s); - vram++; - s++; - } - } -} - -/* HACK: Re-use the VGA logo */ -void DrawDOSBoxLogoPC98(unsigned int x,unsigned int y) { - unsigned char *s = dosbox_vga16_bmp; - unsigned char *sf = s + sizeof(dosbox_vga16_bmp); - unsigned int bit,dx,dy; - uint32_t width,height; - unsigned char p[4]; - unsigned char c; - uint32_t vram; - uint32_t off; - uint32_t sz; - - if (memcmp(s,"BM",2)) return; - sz = host_readd(s+2); // size of total bitmap - off = host_readd(s+10); // offset of bitmap - if ((s+sz) > sf) return; - if ((s+14+40) > sf) return; - - sz = host_readd(s+34); // biSize - if ((s+off+sz) > sf) return; - if (host_readw(s+26) != 1) return; // biBitPlanes - if (host_readw(s+28) != 4) return; // biBitCount - - width = host_readd(s+18); - height = host_readd(s+22); - if (width > (640-x) || height > (350-y)) return; - - // EGA/VGA Write Mode 2 - LOG(LOG_MISC,LOG_DEBUG)("Drawing VGA logo as PC-98 (%u x %u)",(int)width,(int)height); - for (dy=0;dy < height;dy++) { - vram = ((y+dy) * 80) + (x / 8); - s = dosbox_vga16_bmp + off + ((height-(dy+1))*((width+1)/2)); - for (dx=0;dx < width;dx += 8) { - p[0] = p[1] = p[2] = p[3] = 0; - for (bit=0;bit < 8;) { - c = (*s >> 4); - p[0] |= ((c >> 0) & 1) << (7 - bit); - p[1] |= ((c >> 1) & 1) << (7 - bit); - p[2] |= ((c >> 2) & 1) << (7 - bit); - p[3] |= ((c >> 3) & 1) << (7 - bit); - bit++; - - c = (*s++) & 0xF; - p[0] |= ((c >> 0) & 1) << (7 - bit); - p[1] |= ((c >> 1) & 1) << (7 - bit); - p[2] |= ((c >> 2) & 1) << (7 - bit); - p[3] |= ((c >> 3) & 1) << (7 - bit); - bit++; - } - - mem_writeb(0xA8000+vram,p[0]); - mem_writeb(0xB0000+vram,p[1]); - mem_writeb(0xB8000+vram,p[2]); - mem_writeb(0xE0000+vram,p[3]); - vram++; - } - } -} - -void DrawDOSBoxLogoVGA(unsigned int x,unsigned int y) { - unsigned char *s = dosbox_vga16_bmp; - unsigned char *sf = s + sizeof(dosbox_vga16_bmp); - unsigned int bit,dx,dy; - uint32_t width,height; - uint32_t vram; - uint32_t off; - uint32_t sz; - - if (memcmp(s,"BM",2)) return; - sz = host_readd(s+2); // size of total bitmap - off = host_readd(s+10); // offset of bitmap - if ((s+sz) > sf) return; - if ((s+14+40) > sf) return; - - sz = host_readd(s+34); // biSize - if ((s+off+sz) > sf) return; - if (host_readw(s+26) != 1) return; // biBitPlanes - if (host_readw(s+28) != 4) return; // biBitCount - - width = host_readd(s+18); - height = host_readd(s+22); - if (width > (640-x) || height > (350-y)) return; - - // EGA/VGA Write Mode 2 - LOG(LOG_MISC,LOG_DEBUG)("Drawing VGA logo (%u x %u)",(int)width,(int)height); - IO_Write(0x3CE,0x05); // graphics mode - IO_Write(0x3CF,0x02); // read=0 write=2 odd/even=0 shift=0 shift256=0 - IO_Write(0x3CE,0x03); // data rotate - IO_Write(0x3CE,0x00); // no rotate, no XOP - for (bit=0;bit < 8;bit++) { - const unsigned char shf = ((bit & 1) ^ 1) * 4; - - IO_Write(0x3CE,0x08); // bit mask - IO_Write(0x3CF,0x80 >> bit); - - for (dy=0;dy < height;dy++) { - vram = ((y+dy) * 80) + (x / 8); - s = dosbox_vga16_bmp + off + (bit/2) + ((height-(dy+1))*((width+1)/2)); - for (dx=bit;dx < width;dx += 8) { - mem_readb(0xA0000+vram); // load VGA latches - mem_writeb(0xA0000+vram,(*s >> shf) & 0xF); - vram++; - s += 4; - } - } - } - // restore write mode 0 - IO_Write(0x3CE,0x05); // graphics mode - IO_Write(0x3CF,0x00); // read=0 write=0 odd/even=0 shift=0 shift256=0 - IO_Write(0x3CE,0x08); // bit mask - IO_Write(0x3CF,0xFF); -} - -static int bios_pc98_posx = 0; - -static void BIOS_Int10RightJustifiedPrint(const int x,int &y,const char *msg) { - const char *s = msg; - - if (machine != MCH_PC98) { - while (*s != 0) { - if (*s == '\n') { - y++; - reg_eax = 0x0200u; // set cursor pos - reg_ebx = 0; // page zero - reg_dh = y; // row 4 - reg_dl = x; // column 20 - CALLBACK_RunRealInt(0x10); - s++; - } - else { - reg_eax = 0x0E00u | ((unsigned char)(*s++)); - reg_ebx = 0x07u; - CALLBACK_RunRealInt(0x10); - } - } - } - else { - unsigned int bo; - - while (*s != 0) { - if (*s == '\n') { - y++; - s++; - bios_pc98_posx = x; - - bo = (((unsigned int)y * 80u) + (unsigned int)bios_pc98_posx) * 2u; - } - else if (*s == '\r') { - s++; /* ignore */ - continue; - } - else { - bo = (((unsigned int)y * 80u) + (unsigned int)(bios_pc98_posx++)) * 2u; /* NTS: note the post increment */ - - mem_writew(0xA0000+bo,(unsigned char)(*s++)); - mem_writeb(0xA2000+bo,0xE1); - - bo += 2; /* and keep the cursor following the text */ - } - - reg_eax = 0x1300; // set cursor pos (PC-98) - reg_edx = bo; // byte position - CALLBACK_RunRealInt(0x18); - } - } -} - -static Bitu ulimit = 0; -static Bitu t_conv = 0; -static bool bios_first_init=true; -static bool bios_has_exec_vga_bios=false; -static Bitu adapter_scan_start; - -/* FIXME: At global scope their destructors are called after the rest of DOSBox has shut down. Move back into BIOS scope. */ -static CALLBACK_HandlerObject int4b_callback; -static CALLBACK_HandlerObject callback[20]; /* <- fixme: this is stupid. just declare one per interrupt. */ -static CALLBACK_HandlerObject cb_bios_post; -static CALLBACK_HandlerObject callback_pc98_lio; - -Bitu call_pnp_r = ~0UL; -Bitu call_pnp_rp = 0; - -Bitu call_pnp_p = ~0UL; -Bitu call_pnp_pp = 0; - -Bitu isapnp_biosstruct_base = 0; - -Bitu BIOS_boot_code_offset = 0; -Bitu BIOS_bootfail_code_offset = 0; - -bool bios_user_reset_vector_blob_run = false; -Bitu bios_user_reset_vector_blob = 0; - -Bitu bios_user_boot_hook = 0; - -void CALLBACK_DeAllocate(Bitu in); - -void BIOS_OnResetComplete(Section *x); - -Bitu call_irq0 = 0; -Bitu call_irq07default = 0; -Bitu call_irq815default = 0; - -void write_FFFF_PC98_signature() { - /* this may overwrite the existing signature. - * PC-98 systems DO NOT have an ASCII date at F000:FFF5 - * and the WORD value at F000:FFFE is said to be a checksum of the BIOS */ - - // The farjump at the processor reset entry point (jumps to POST routine) - phys_writeb(0xffff0,0xEA); // FARJMP - phys_writew(0xffff1,RealOff(BIOS_DEFAULT_RESET_LOCATION)); // offset - phys_writew(0xffff3,RealSeg(BIOS_DEFAULT_RESET_LOCATION)); // segment - - // write nothing (not used) - for(Bitu i = 0; i < 9; i++) phys_writeb(0xffff5+i,0); - - // fake BIOS checksum - phys_writew(0xffffe,0xABCD); -} - -void gdc_egc_enable_update_vars(void) { - unsigned char b; - - b = mem_readb(0x54D); - b &= ~0x40; - if (enable_pc98_egc) b |= 0x40; - mem_writeb(0x54D,b); - - b = mem_readb(0x597); - b &= ~0x04; - if (enable_pc98_egc) b |= 0x04; - mem_writeb(0x597,b); - - if (!enable_pc98_egc) - pc98_gdc_vramop &= ~(1 << VOPBIT_EGC); -} - -void gdc_grcg_enable_update_vars(void) { - unsigned char b; - - b = mem_readb(0x54C); - b &= ~0x02; - if (enable_pc98_grcg) b |= 0x02; - mem_writeb(0x54C,b); - - //TODO: How to reset GRCG? -} - - -void gdc_16color_enable_update_vars(void) { - unsigned char b; - - b = mem_readb(0x54C); - b &= ~0x04; - if (enable_pc98_16color) b |= 0x04; - mem_writeb(0x54C,b); - - if(!enable_pc98_16color) {//force switch to 8-colors mode - void pc98_port6A_command_write(unsigned char b); - pc98_port6A_command_write(0x00); - } -} - -/* NTS: Remember the 8259 is non-sentient, and the term "slave" is used in a computer programming context */ -static Bitu Default_IRQ_Handler_Cooperative_Slave_Pic(void) { - /* PC-98 style IRQ 8-15 handling. - * - * This mimics the recommended procedure [https://www.webtech.co.jp/company/doc/undocumented_mem/io_pic.txt] - * - * mov al,20h ;Send EOI to SLAVE - * out 0008h,al - * jmp $+2 ;I/O WAIT - * mov al,0Bh ;ISR read mode set(slave) - * out 0008h,al - * jmp $+2 ;I/O WAIT - * in al,0008h ;ISR read(slave) - * cmp al,00h ;slave pic in-service ? - * jne EoiEnd - * mov al,20h ;Send EOI to MASTER - * out 0000h,al - */ - IO_WriteB(IS_PC98_ARCH ? 0x08 : 0xA0,0x20); // send EOI to slave - IO_WriteB(IS_PC98_ARCH ? 0x08 : 0xA0,0x0B); // ISR read mode set - if (IO_ReadB(IS_PC98_ARCH ? 0x08 : 0xA0) == 0) // if slave pic in service.. - IO_WriteB(IS_PC98_ARCH ? 0x00 : 0x20,0x20); // then EOI the master - - return CBRET_NONE; -} - -class BIOS:public Module_base{ -private: - static Bitu cb_bios_post__func(void) { - void TIMER_BIOS_INIT_Configure(); -#if C_DEBUG - void DEBUG_CheckCSIP(); - -# if C_HEAVY_DEBUG - /* the game/app obviously crashed, which is way more important - * to log than what we do here in the BIOS at POST */ - void DEBUG_StopLog(void); - DEBUG_StopLog(); -# endif -#endif - - if (bios_first_init) { - /* clear the first 1KB-32KB */ - for (Bit16u i=0x400;i<0x8000;i++) real_writeb(0x0,i,0); - } - - if (IS_PC98_ARCH) { - for (unsigned int i=0;i < 20;i++) callback[i].Uninstall(); - - /* clear out 0x50 segment (TODO: 0x40 too?) */ - for (unsigned int i=0;i < 0x100;i++) phys_writeb(0x500+i,0); - - write_FFFF_PC98_signature(); - BIOS_ZeroExtendedSize(false); - - unsigned char memsize_real_code = 0; - Bitu mempages = MEM_TotalPages(); /* in 4KB pages */ - - /* NTS: Fill in the 3-bit code in FLAGS1 that represents - * how much lower conventional memory is in the system. - * - * Note that MEM.EXE requires this value, or else it - * will complain about broken UMB linkage and fail - * to show anything else. */ - /* TODO: In the event we eventually support "high resolution mode" - * we can indicate 768KB here, code == 5, meaning that - * the RAM extends up to 0xBFFFF instead of 0x9FFFF */ - if (mempages >= (640UL/4UL)) /* 640KB */ - memsize_real_code = 4; - else if (mempages >= (512UL/4UL)) /* 512KB */ - memsize_real_code = 3; - else if (mempages >= (384UL/4UL)) /* 384KB */ - memsize_real_code = 2; - else if (mempages >= (256UL/4UL)) /* 256KB */ - memsize_real_code = 1; - else /* 128KB */ - memsize_real_code = 0; - - void pc98_msw3_set_ramsize(const unsigned char b); - pc98_msw3_set_ramsize(memsize_real_code); - - /* CRT status */ - /* bit[7:6] = 00=conventional compatible 01=extended attr JEH 10=extended attr EGH - * bit[5:5] = Single event timer in use flag 1=busy 0=not used - * bit[4:4] = ? - * bit[3:3] = raster scan 1=non-interlaced 0=interlaced - * bit[2:2] = Content ruled line color 1=I/O set value 0=attributes of VRAM - * bit[1:1] = ? - * bit[0:0] = 480-line mode 1=640x480 0=640x400 or 640x200 */ - mem_writeb(0x459,0x08/*non-interlaced*/); - - /* CPU/Display */ - /* bit[7:7] = 486SX equivalent (?) 1=yes - * bit[6:6] = PC-9821 Extended Graph Architecture supported (FIXME: Is this the same as having EGC?) 1=yes - * bit[5:5] = LCD display is color 1=yes 0=no - * bit[4:4] = ? - * bit[3:3] = ROM drive allow writing - * bit[2:2] = 98 NOTE PC-9801N-08 expansion I/O box connected - * bit[1:1] = 98 NOTE prohibit transition to power saving mode - * bit[0:0] = 98 NOTE coprocessor function available */ - mem_writeb(0x45C,(enable_pc98_egc ? 0x40/*Extended Graphics*/ : 0x00)); - - /* Keyboard type */ - /* bit[7:7] = ? - * bit[6:6] = keyboard type bit 1 - * bit[5:5] = EMS page frame at B0000h 1=present 0=none - * bit[4:4] = EMS page frame at B0000h 1=page frame 0=G-VRAM - * bit[3:3] = keyboard type bit 0 - * bit[2:2] = High resolution memory window available - * bit[1:1] = ? - * bit[0:0] = ? - * - * keyboard bits[1:0] from bit 6 as bit 1 and bit 3 as bit 0 combined: - * 11 = new keyboard (NUM key, DIP switch 2-7 OFF) - * 10 = new keyboard (without NUM key) - * 01 = new keyboard (NUM key, DIP switch 2-7 ON) - * 00 = old keyboard - * - * The old keyboard is documented not to support software control of CAPS and KANA states */ - /* TODO: Make this a dosbox.conf option. Default is new keyboard without NUM key because that is what - * keyboard emulation currently acts like anyway. */ - mem_writeb(0x481,0x40/*bit 6=1 bit 3=0 new keyboard without NUM key*/); - - /* BIOS flags */ - /* bit[7:7] = Startup 1=hot start 0=cold start - * bit[6:6] = BASIC type ?? - * bit[5:5] = Keyboard beep 1=don't beep 0=beep ... when buffer full - * bit[4:4] = Expansion conv RAM 1=present 0=absent - * bit[3:3] = ?? - * bit[2:2] = ?? - * bit[1:1] = HD mode 1=1MB mode 0=640KB mode ... of the floppy drive - * bit[0:0] = Model 1=other 0=PC-9801 original */ - /* NTS: MS-DOS 5.0 appears to reduce it's BIOS calls and render the whole - * console as green IF bit 0 is clear. - * - * If bit 0 is set, INT 1Ah will be hooked by MS-DOS and, for some odd reason, - * MS-DOS's hook proc will call to our INT 1Ah + 0x19 bytes. */ - mem_writeb(0x500,0x01 | 0x02/*high density drive*/); - - /* BIOS flags */ - /* timer setup will set/clear bit 7 */ - /* bit[7:7] = system clock freq 1=8MHz 0=5/10Mhz - * = timer clock freq 1=1.9968MHz 0=2.4576MHz - * bit[6:6] = CPU 1=V30 0=Intel (8086 through Pentium) - * bit[5:5] = Model info 1=Other model 0=PC-9801 Muji, PC-98XA - * bit[4:4] = Model info ... - * bit[3:3] = Model info 1=High res 0=normal - * bit[2:0] = Realmode memsize - * 000=128KB 001=256KB - * 010=384KB 011=512KB - * 100=640KB 101=768KB - * - * Ref: http://hackipedia.org/browse/Computer/Platform/PC,%20NEC%20PC-98/Collections/Undocumented%209801,%209821%20Volume%202%20(webtech.co.jp)/memsys.txt */ - /* NTS: High resolution means 640x400, not the 1120x750 mode known as super high resolution mode. - * DOSBox-X does not yet emulate super high resolution nor does it emulate the 15khz 200-line "standard" mode. - * ref: https://github.com/joncampbell123/dosbox-x/issues/906#issuecomment-434513930 - * ref: https://jisho.org/search?utf8=%E2%9C%93&keyword=%E8%B6%85 */ - mem_writeb(0x501,0x20 | memsize_real_code); - - /* keyboard buffer */ - mem_writew(0x524/*tail*/,0x502); - mem_writew(0x526/*tail*/,0x502); - - /* number of scanlines per text row - 1 */ - mem_writeb(0x53B,0x0F); // CRT_RASTER, 640x400 24.83KHz-hsync 56.42Hz-vsync - - /* Text screen status. - * Note that most of the bits are used verbatim in INT 18h AH=0Ah/AH=0Bh */ - /* bit[7:7] = High resolution display 1=yes 0=no (standard) NOT super high res - * bit[6:6] = vsync 1=VSYNC wait 0=end of vsync handling - * bit[5:5] = unused - * bit[4:4] = Number of lines 1=30 lines 0=20/25 lines - * bit[3:3] = K-CG access mode 1=dot access 0=code access - * bit[2:2] = Attribute mode (how to handle bit 4) 1=Simp. graphic 0=Vertical line - * bit[1:1] = Number of columns 1=40 cols 0=80 cols - * bit[0:0] = Number of lines 1=20/30 lines 0=25 lines */ - mem_writeb(0x53C,(true/*TODO*/ ? 0x80/*high res*/ : 0x00/*standard*/)); - - /* BIOS raster location */ - mem_writew(0x54A,0x1900); - - /* BIOS flags */ - /* bit[7:7] = Graphics display state 1=Visible 0=Blanked (hidden) - * bit[6:6] = CRT type 1=high res 0=standard NOT super high res - * bit[5:5] = Horizontal sync rate 1=31.47KHz 0=24.83KHz - * bit[4:4] = CRT line mode 1=480-line 0=400-line - * bit[3:3] = Number of user-defined characters 1=188+ 0=63 - * bit[2:2] = Extended graphics RAM (for 16-color) 1=present 0=absent - * bit[1:1] = Graphics Charger is present 1=present 0=absent - * bit[0:0] = DIP switch 1-8 at startup 1=ON 0=OFF (?) */ - mem_writeb(0x54C,(true/*TODO*/ ? 0x40/*high res*/ : 0x00/*standard*/) | (enable_pc98_grcg ? 0x02 : 0x00) | (enable_pc98_16color ? 0x04 : 0x00) | (pc98_31khz_mode ? 0x20/*31khz*/ : 0x00/*24khz*/) | (enable_pc98_188usermod ? 0x08 : 0x00)); // PRXCRT, 16-color G-VRAM, GRCG - - /* BIOS flags */ - /* bit[7:7] = 256-color board present (PC-H98) - * bit[6:6] = Enhanced Graphics Charger (EGC) is present - * bit[5:5] = GDC at 5.0MHz at boot up (copy of DIP switch 2-8 at startup) 1=yes 0=no - * bit[4:4] = Always "flickerless" drawing mode - * bit[3:3] = Drawing mode with flicker - * bit[2:2] = GDC clock 1=5MHz 0=2.5MHz - * bit[1:0] = Drawing mode of the GDC - * 00 = REPLACE - * 01 = COMPLEMENT - * 10 = CLEAR - * 11 = SET */ - mem_writeb(0x54D,(enable_pc98_egc ? 0x40 : 0x00) | (gdc_5mhz_mode ? 0x20 : 0x00) | (gdc_5mhz_mode ? 0x04 : 0x00)); // EGC - - /* BIOS flags */ - /* bit[7:7] = INT 18h AH=30h/31h support enabled - * bit[6:3] = 0 (unused) - * bit[2:2] = Enhanced Graphics Mode (EGC) supported - * bit[1:0] = Graphic resolution - * 00 = 640x200 upper half (2/8/16-color mode) - * 01 = 640x200 lower half (2/8/16-color mode) - * 10 = 640x400 (2/8/16/256-color mode) - * 11 = 640x480 256-color mode */ - mem_writeb(0x597,(enable_pc98_egc ? 0x04 : 0x00)/*EGC*/ | - (enable_pc98_egc ? 0x80 : 0x00)/*supports INT 18h AH=30h and AH=31h*/ | - 2/*640x400*/); - /* TODO: I would like to eventually add a dosbox.conf option that controls whether INT 18h AH=30h and 31h - * are enabled, so that retro-development can test code to see how it acts on a newer PC-9821 - * that supports it vs an older PC-9821 that doesn't. - * - * If the user doesn't set the option, then it is "auto" and determined by machine= PC-98 model and - * by another option in dosbox.conf that determines whether 31khz support is enabled. - * - * NOTED: Neko Project II determines INT 18h AH=30h availability by whether or not it was compiled - * with 31khz hsync support (SUPPORT_CRT31KHZ) */ - } - - if (bios_user_reset_vector_blob != 0 && !bios_user_reset_vector_blob_run) { - LOG_MSG("BIOS POST: Running user reset vector blob at 0x%lx",(unsigned long)bios_user_reset_vector_blob); - bios_user_reset_vector_blob_run = true; - - assert((bios_user_reset_vector_blob&0xF) == 0); /* must be page aligned */ - - SegSet16(cs,bios_user_reset_vector_blob>>4); - reg_eip = 0; - -#if C_DEBUG - /* help the debugger reflect the new instruction pointer */ - DEBUG_CheckCSIP(); -#endif - - return CBRET_NONE; - } - - if (cpu.pmode) E_Exit("BIOS error: POST function called while in protected/vm86 mode"); - - CPU_CLI(); - - /* we need A20 enabled for BIOS boot-up */ - void A20Gate_OverrideOn(Section *sec); - void MEM_A20_Enable(bool enabled); - A20Gate_OverrideOn(NULL); - MEM_A20_Enable(true); - - BIOS_OnResetComplete(NULL); - - adapter_scan_start = 0xC0000; - bios_has_exec_vga_bios = false; - LOG(LOG_MISC,LOG_DEBUG)("BIOS: executing POST routine"); - - // TODO: Anything we can test in the CPU here? - - // initialize registers - SegSet16(ds,0x0000); - SegSet16(es,0x0000); - SegSet16(fs,0x0000); - SegSet16(gs,0x0000); - SegSet16(ss,0x0000); - - { - Bitu sz = MEM_TotalPages(); - - /* The standard BIOS is said to put it's stack (at least at OS boot time) 512 bytes past the end of the boot sector - * meaning that the boot sector loads to 0000:7C00 and the stack is set grow downward from 0000:8000 */ - - if (sz > 8) sz = 8; /* 4KB * 8 = 32KB = 0x8000 */ - sz *= 4096; - reg_esp = sz - 4; - reg_ebp = 0; - LOG(LOG_MISC,LOG_DEBUG)("BIOS: POST stack set to 0000:%04x",reg_esp); - } - - if (dosbox_int_iocallout != IO_Callout_t_none) { - IO_FreeCallout(dosbox_int_iocallout); - dosbox_int_iocallout = IO_Callout_t_none; - } - - if (isapnp_biosstruct_base != 0) { - ROMBIOS_FreeMemory(isapnp_biosstruct_base); - isapnp_biosstruct_base = 0; - } - - if (BOCHS_PORT_E9) { - delete BOCHS_PORT_E9; - BOCHS_PORT_E9=NULL; - } - if (ISAPNP_PNP_ADDRESS_PORT) { - delete ISAPNP_PNP_ADDRESS_PORT; - ISAPNP_PNP_ADDRESS_PORT=NULL; - } - if (ISAPNP_PNP_DATA_PORT) { - delete ISAPNP_PNP_DATA_PORT; - ISAPNP_PNP_DATA_PORT=NULL; - } - if (ISAPNP_PNP_READ_PORT) { - delete ISAPNP_PNP_READ_PORT; - ISAPNP_PNP_READ_PORT=NULL; - } - - extern Bitu call_default,call_default2; - - if (IS_PC98_ARCH) { - /* INT 00h-FFh generic stub routine */ - /* NTS: MS-DOS on PC-98 will fill all yet-unused interrupt vectors with a stub. - * No vector is left at 0000:0000. On a related note, PC-98 games apparently - * like to call INT 33h (mouse functions) without first checking that the - * vector is non-null. */ - callback[18].Uninstall(); - callback[18].Install(&INTGEN_PC98_Handler,CB_IRET,"Int stub ???"); - for (unsigned int i=0x00;i < 0x100;i++) RealSetVec(i,callback[18].Get_RealPointer()); - - for (unsigned int i=0x00;i < 0x08;i++) - real_writed(0,i*4,CALLBACK_RealPointer(call_default)); - } - else { - /* Clear the vector table */ - for (Bit16u i=0x70*4;i<0x400;i++) real_writeb(0x00,i,0); - - /* Only setup default handler for first part of interrupt table */ - for (Bit16u ct=0;ct<0x60;ct++) { - real_writed(0,ct*4,CALLBACK_RealPointer(call_default)); - } - for (Bit16u ct=0x68;ct<0x70;ct++) { - real_writed(0,ct*4,CALLBACK_RealPointer(call_default)); - } - - // default handler for IRQ 2-7 - for (Bit16u ct=0x0A;ct <= 0x0F;ct++) - RealSetVec(ct,BIOS_DEFAULT_IRQ07_DEF_LOCATION); - } - - if (unhandled_irq_method == UNHANDLED_IRQ_COOPERATIVE_2ND) { - // PC-98 style: Master PIC ack with 0x20 for IRQ 0-7. - // For the slave PIC, ack with 0x20 on the slave, then only ack the master (cascade interrupt) - // if the ISR register on the slave indicates none are in service. - CALLBACK_Setup(call_irq07default,NULL,CB_IRET_EOI_PIC1,Real2Phys(BIOS_DEFAULT_IRQ07_DEF_LOCATION),"bios irq 0-7 default handler"); - CALLBACK_Setup(call_irq815default,Default_IRQ_Handler_Cooperative_Slave_Pic,CB_IRET,Real2Phys(BIOS_DEFAULT_IRQ815_DEF_LOCATION),"bios irq 8-15 default handler"); - } - else { - // IBM PC style: Master PIC ack with 0x20, slave PIC ack with 0x20, no checking - CALLBACK_Setup(call_irq07default,NULL,CB_IRET_EOI_PIC1,Real2Phys(BIOS_DEFAULT_IRQ07_DEF_LOCATION),"bios irq 0-7 default handler"); - CALLBACK_Setup(call_irq815default,NULL,CB_IRET_EOI_PIC2,Real2Phys(BIOS_DEFAULT_IRQ815_DEF_LOCATION),"bios irq 8-15 default handler"); - } - - if (IS_PC98_ARCH) { - BIOS_UnsetupKeyboard(); - BIOS_UnsetupDisks(); - - /* no such INT 4Bh */ - int4b_callback.Uninstall(); - - /* remove some IBM-style BIOS interrupts that don't exist on PC-98 */ - /* IRQ to INT arrangement - * - * IBM PC-98 IRQ - * -------------------------------- - * 0x08 0x08 0 - * 0x09 0x09 1 - * 0x0A CASCADE 0x0A 2 - * 0x0B 0x0B 3 - * 0x0C 0x0C 4 - * 0x0D 0x0D 5 - * 0x0E 0x0E 6 - * 0x0F 0x0F CASCADE 7 - * 0x70 0x10 8 - * 0x71 0x11 9 - * 0x72 0x12 10 - * 0x73 0x13 11 - * 0x74 0x14 12 - * 0x75 0x15 13 - * 0x76 0x16 14 - * 0x77 0x17 15 - * - * As part of the change the IRQ cascade emulation needs to change for PC-98 as well. - * IBM uses IRQ 2 for cascade. - * PC-98 uses IRQ 7 for cascade. */ - - void INT10_EnterPC98(Section *sec); - INT10_EnterPC98(NULL); /* INT 10h */ - - callback_pc98_lio.Uninstall(); - - callback[1].Uninstall(); /* INT 11h */ - callback[2].Uninstall(); /* INT 12h */ - callback[3].Uninstall(); /* INT 14h */ - callback[4].Uninstall(); /* INT 15h */ - callback[5].Uninstall(); /* INT 17h */ - callback[6].Uninstall(); /* INT 1Ah */ - callback[7].Uninstall(); /* INT 1Ch */ - callback[10].Uninstall(); /* INT 19h */ - callback[11].Uninstall(); /* INT 76h: IDE IRQ 14 */ - callback[12].Uninstall(); /* INT 77h: IDE IRQ 15 */ - callback[15].Uninstall(); /* INT 18h: Enter BASIC */ - - /* IRQ 6 is nothing special */ - callback[13].Uninstall(); /* INT 0Eh: IDE IRQ 6 */ - callback[13].Install(NULL,CB_IRET_EOI_PIC1,"irq 6"); - - /* IRQ 8 is nothing special */ - callback[8].Uninstall(); - callback[8].Install(NULL,CB_IRET_EOI_PIC2,"irq 8"); - - /* IRQ 9 is nothing special */ - callback[9].Uninstall(); - callback[9].Install(NULL,CB_IRET_EOI_PIC2,"irq 9"); - - /* INT 18h keyboard and video display functions */ - callback[1].Install(&INT18_PC98_Handler,CB_INT16,"Int 18 keyboard and display"); - callback[1].Set_RealVec(0x18,/*reinstall*/true); - - /* INT 19h *STUB* */ - callback[2].Install(&INT19_PC98_Handler,CB_IRET,"Int 19 ???"); - callback[2].Set_RealVec(0x19,/*reinstall*/true); - - /* INT 1Ah *STUB* */ - callback[3].Install(&INT1A_PC98_Handler,CB_IRET,"Int 1A ???"); - callback[3].Set_RealVec(0x1A,/*reinstall*/true); - - /* MS-DOS 5.0 FIXUP: - * - For whatever reason, if we set bits in the BIOS data area that - * indicate we're NOT the original model of the PC-98, MS-DOS will - * hook our INT 1Ah and then call down to 0x19 bytes into our - * INT 1Ah procedure. If anyone can explain this, I'd like to hear it. --J.C. - * - * NTS: On real hardware, the BIOS appears to have an INT 1Ah, a bunch of NOPs, - * then at 0x19 bytes into the procedure, the actual handler. This is what - * MS-DOS is pointing at. - * - * But wait, there's more. - * - * MS-DOS calldown pushes DS and DX onto the stack (after the IRET frame) - * before JMPing into the BIOS. - * - * Apparently the function at INT 1Ah + 0x19 is expected to do this: - * - * - * POP DX - * POP DS - * IRET - * - * I can only imaging what a headache this might have caused NEC when - * maintaining the platform and compatibility! */ - { - Bitu addr = callback[3].Get_RealPointer(); - addr = ((addr >> 16) << 4) + (addr & 0xFFFF); - - /* to make this work, we need to pop the two regs, then JMP to our - * callback and proceed as normal. */ - phys_writeb(addr + 0x19,0x5A); // POP DX - phys_writeb(addr + 0x1A,0x1F); // POP DS - phys_writeb(addr + 0x1B,0xEB); // jmp short ... - phys_writeb(addr + 0x1C,0x100 - 0x1D); - } - - /* INT 1Bh *STUB* */ - callback[4].Install(&INT1B_PC98_Handler,CB_IRET,"Int 1B ???"); - callback[4].Set_RealVec(0x1B,/*reinstall*/true); - - /* INT 1Ch *STUB* */ - callback[5].Install(&INT1C_PC98_Handler,CB_IRET,"Int 1C ???"); - callback[5].Set_RealVec(0x1C,/*reinstall*/true); - - /* INT 1Dh *STUB* */ - /* Place it in the PC-98 int vector area at FD80:0000 to satisfy some DOS games - * that detect PC-98 from the segment value of the vector (issue #927). - * Note that on real hardware (PC-9821) INT 1Dh appears to be a stub that IRETs immediately. */ - callback[6].Install(&INT1D_PC98_Handler,CB_IRET,"Int 1D ???"); -// callback[6].Set_RealVec(0x1D,/*reinstall*/true); - { - Bitu ofs = 0xFD813; /* 0xFD80:0013 try not to look like a phony address */ - unsigned int vec = 0x1D; - Bit32u target = callback[6].Get_RealPointer(); - - phys_writeb(ofs+0,0xEA); // JMP FAR - phys_writed(ofs+1,target); - - phys_writew((vec*4)+0,(ofs-0xFD800)); - phys_writew((vec*4)+2,0xFD80); - } - - /* INT 1Eh *STUB* */ - callback[7].Install(&INT1E_PC98_Handler,CB_IRET,"Int 1E ???"); - callback[7].Set_RealVec(0x1E,/*reinstall*/true); - - /* INT 1Fh *STUB* */ - callback[10].Install(&INT1F_PC98_Handler,CB_IRET,"Int 1F ???"); - callback[10].Set_RealVec(0x1F,/*reinstall*/true); - - /* INT DCh *STUB* */ - callback[16].Install(&INTDC_PC98_Handler,CB_IRET,"Int DC ???"); - callback[16].Set_RealVec(0xDC,/*reinstall*/true); - - /* INT F2h *STUB* */ - callback[17].Install(&INTF2_PC98_Handler,CB_IRET,"Int F2 ???"); - callback[17].Set_RealVec(0xF2,/*reinstall*/true); - - // default handler for IRQ 2-7 - for (Bit16u ct=0x0A;ct <= 0x0F;ct++) - RealSetVec(ct,BIOS_DEFAULT_IRQ07_DEF_LOCATION); - - // default handler for IRQ 8-15 - for (Bit16u ct=0;ct < 8;ct++) - RealSetVec(ct+(IS_PC98_ARCH ? 0x10 : 0x70),BIOS_DEFAULT_IRQ815_DEF_LOCATION); - - // LIO graphics interface (number of entry points, unknown WORD value and offset into the segment). - { - callback_pc98_lio.Install(&PC98_BIOS_LIO,CB_IRET,"LIO graphics library"); - - Bitu ofs = 0xF990u << 4u; // F990:0000... - unsigned int entrypoints = 0x11; - Bitu final_addr = callback_pc98_lio.Get_RealPointer(); - - /* NTS: Based on GAME/MAZE 999 behavior, these numbers are interrupt vector numbers. - * The entry point marked 0xA0 is copied by the game to interrupt vector A0 and - * then called with INT A0h even though it blindly assumes the numbers are - * sequential from 0xA0-0xAF. */ - unsigned char entrypoint_indexes[0x11] = { - 0xA0, 0xA1, 0xA2, 0xA3, // +0x00 - 0xA4, 0xA5, 0xA6, 0xA7, // +0x04 - 0xA8, 0xA9, 0xAA, 0xAB, // +0x08 - 0xAC, 0xAD, 0xAE, 0xAF, // +0x0C - 0xCE // +0x10 - }; - - assert(((entrypoints * 4) + 4) <= 0x50); - assert((50 + (entrypoints * 7)) <= 0x100); // a 256-byte region is set aside for this! - - phys_writed(ofs+0,entrypoints); - for (unsigned int ent=0;ent < entrypoints;ent++) { - /* each entry point is "MOV AL, ; JMP FAR " */ - /* Yksoft1's patch suggests a segment offset of 0x50 which I'm OK with */ - unsigned int ins_ofs = ofs + 0x50 + (ent * 7); - - phys_writew(ofs+4+(ent*4)+0,entrypoint_indexes[ent]); - phys_writew(ofs+4+(ent*4)+2,ins_ofs - ofs); - - phys_writeb(ins_ofs+0,0xB0); // MOV AL,(entrypoint index) - phys_writeb(ins_ofs+1,entrypoint_indexes[ent]); - phys_writeb(ins_ofs+2,0xEA); // JMP FAR - phys_writed(ins_ofs+3,final_addr); - // total: ins_ofs+7 - } - } - } - - // setup a few interrupt handlers that point to bios IRETs by default - if (!IS_PC98_ARCH) - real_writed(0,0x0e*4,CALLBACK_RealPointer(call_default2)); //design your own railroad - - if (IS_PC98_ARCH) { - real_writew(0,0x58A,0x0000U); // countdown timer value - PIC_SetIRQMask(0,true); /* PC-98 keeps the timer off unless INT 1Ch is called to set a timer interval */ - } - - real_writed(0,0x66*4,CALLBACK_RealPointer(call_default)); //war2d - real_writed(0,0x67*4,CALLBACK_RealPointer(call_default)); - real_writed(0,0x68*4,CALLBACK_RealPointer(call_default)); - real_writed(0,0x5c*4,CALLBACK_RealPointer(call_default)); //Network stuff - //real_writed(0,0xf*4,0); some games don't like it - - bios_first_init = false; - - DispatchVMEvent(VM_EVENT_BIOS_INIT); - - TIMER_BIOS_INIT_Configure(); - - void INT10_Startup(Section *sec); - INT10_Startup(NULL); - - if (!IS_PC98_ARCH) { - extern Bit8u BIOS_tandy_D4_flag; - real_writeb(0x40,0xd4,BIOS_tandy_D4_flag); - } - - /* INT 13 Bios Disk Support */ - BIOS_SetupDisks(); - - /* INT 16 Keyboard handled in another file */ - BIOS_SetupKeyboard(); - - if (!IS_PC98_ARCH) { - int4b_callback.Set_RealVec(0x4B,/*reinstall*/true); - callback[1].Set_RealVec(0x11,/*reinstall*/true); - callback[2].Set_RealVec(0x12,/*reinstall*/true); - callback[3].Set_RealVec(0x14,/*reinstall*/true); - callback[4].Set_RealVec(0x15,/*reinstall*/true); - callback[5].Set_RealVec(0x17,/*reinstall*/true); - callback[6].Set_RealVec(0x1A,/*reinstall*/true); - callback[7].Set_RealVec(0x1C,/*reinstall*/true); - callback[8].Set_RealVec(0x70,/*reinstall*/true); - callback[9].Set_RealVec(0x71,/*reinstall*/true); - callback[10].Set_RealVec(0x19,/*reinstall*/true); - callback[11].Set_RealVec(0x76,/*reinstall*/true); - callback[12].Set_RealVec(0x77,/*reinstall*/true); - callback[13].Set_RealVec(0x0E,/*reinstall*/true); - callback[15].Set_RealVec(0x18,/*reinstall*/true); - } - - // FIXME: We're using IBM PC memory size storage even in PC-98 mode. - // This cannot be removed, because the DOS kernel uses this variable even in PC-98 mode. - mem_writew(BIOS_MEMORY_SIZE,t_conv); - - RealSetVec(0x08,BIOS_DEFAULT_IRQ0_LOCATION); - // pseudocode for CB_IRQ0: - // sti - // callback INT8_Handler - // push ds,ax,dx - // int 0x1c - // cli - // mov al, 0x20 - // out 0x20, al - // pop dx,ax,ds - // iret - - if (!IS_PC98_ARCH) { - mem_writed(BIOS_TIMER,0); //Calculate the correct time - phys_writew(Real2Phys(RealGetVec(0x12))+0x12,0x20); //Hack for Jurresic - } - - phys_writeb(Real2Phys(BIOS_DEFAULT_HANDLER_LOCATION),0xcf); /* bios default interrupt vector location -> IRET */ - - if (!IS_PC98_ARCH) { - // tandy DAC setup - bool use_tandyDAC=(real_readb(0x40,0xd4)==0xff); - - tandy_sb.port=0; - tandy_dac.port=0; - if (use_tandyDAC) { - /* tandy DAC sound requested, see if soundblaster device is available */ - Bitu tandy_dac_type = 0; - if (Tandy_InitializeSB()) { - tandy_dac_type = 1; - } else if (Tandy_InitializeTS()) { - tandy_dac_type = 2; - } - if (tandy_dac_type) { - real_writew(0x40,0xd0,0x0000); - real_writew(0x40,0xd2,0x0000); - real_writeb(0x40,0xd4,0xff); /* tandy DAC init value */ - real_writed(0x40,0xd6,0x00000000); - /* install the DAC callback handler */ - tandy_DAC_callback[0]=new CALLBACK_HandlerObject(); - tandy_DAC_callback[1]=new CALLBACK_HandlerObject(); - tandy_DAC_callback[0]->Install(&IRQ_TandyDAC,CB_IRET,"Tandy DAC IRQ"); - tandy_DAC_callback[1]->Install(NULL,CB_TDE_IRET,"Tandy DAC end transfer"); - // pseudocode for CB_TDE_IRET: - // push ax - // mov ax, 0x91fb - // int 15 - // cli - // mov al, 0x20 - // out 0x20, al - // pop ax - // iret - - Bit8u tandy_irq = 7; - if (tandy_dac_type==1) tandy_irq = tandy_sb.irq; - else if (tandy_dac_type==2) tandy_irq = tandy_dac.irq; - Bit8u tandy_irq_vector = tandy_irq; - if (tandy_irq_vector<8) tandy_irq_vector += 8; - else tandy_irq_vector += (0x70-8); - - RealPt current_irq=RealGetVec(tandy_irq_vector); - real_writed(0x40,0xd6,current_irq); - for (Bit16u i=0; i<0x10; i++) phys_writeb(PhysMake(0xf000,0xa084+i),0x80); - } else real_writeb(0x40,0xd4,0x00); - } - } - - if (!IS_PC98_ARCH) { - /* Setup some stuff in 0x40 bios segment */ - - // Disney workaround - // Bit16u disney_port = mem_readw(BIOS_ADDRESS_LPT1); - // port timeouts - // always 1 second even if the port does not exist - // BIOS_SetLPTPort(0, disney_port); - for(Bitu i = 1; i < 3; i++) BIOS_SetLPTPort(i, 0); - mem_writeb(BIOS_COM1_TIMEOUT,1); - mem_writeb(BIOS_COM2_TIMEOUT,1); - mem_writeb(BIOS_COM3_TIMEOUT,1); - mem_writeb(BIOS_COM4_TIMEOUT,1); - - void BIOS_Post_register_parports(); - BIOS_Post_register_parports(); - - void BIOS_Post_register_comports(); - BIOS_Post_register_comports(); - } - - if (!IS_PC98_ARCH) { - /* Setup equipment list */ - // look http://www.bioscentral.com/misc/bda.htm - - //Bit16u config=0x4400; //1 Floppy, 2 serial and 1 parallel - Bit16u config = 0x0; - - config |= bios_post_parport_count() << 14; - config |= bios_post_comport_count() << 9; - -#if (C_FPU) - extern bool enable_fpu; - - //FPU - if (enable_fpu) - config|=0x2; -#endif - switch (machine) { - case MCH_MDA: - case MCH_HERC: - //Startup monochrome - config|=0x30; - break; - case EGAVGA_ARCH_CASE: - case MCH_CGA: - case MCH_MCGA: - case TANDY_ARCH_CASE: - case MCH_AMSTRAD: - //Startup 80x25 color - config|=0x20; - break; - default: - //EGA VGA - config|=0; - break; - } - - // PS2 mouse - if (KEYBOARD_Report_BIOS_PS2Mouse()) - config |= 0x04; - - // Gameport - config |= 0x1000; - mem_writew(BIOS_CONFIGURATION,config); - CMOS_SetRegister(0x14,(Bit8u)(config&0xff)); //Should be updated on changes - } - - if (!IS_PC98_ARCH) { - /* Setup extended memory size */ - IO_Write(0x70,0x30); - size_extended=IO_Read(0x71); - IO_Write(0x70,0x31); - size_extended|=(IO_Read(0x71) << 8); - BIOS_HostTimeSync(); - } - else { - /* Provide a valid memory size anyway */ - size_extended=MEM_TotalPages()*4; - if (size_extended >= 1024) size_extended -= 1024; - else size_extended = 0; - } - - if (!IS_PC98_ARCH) { - /* PS/2 mouse */ - void BIOS_PS2Mouse_Startup(Section *sec); - BIOS_PS2Mouse_Startup(NULL); - } - - if (!IS_PC98_ARCH) { - /* this belongs HERE not on-demand from INT 15h! */ - biosConfigSeg = ROMBIOS_GetMemory(16/*one paragraph*/,"BIOS configuration (INT 15h AH=0xC0)",/*paragraph align*/16)>>4; - if (biosConfigSeg != 0) { - PhysPt data = PhysMake(biosConfigSeg,0); - phys_writew(data,8); // 8 Bytes following - if (IS_TANDY_ARCH) { - if (machine==MCH_TANDY) { - // Model ID (Tandy) - phys_writeb(data+2,0xFF); - } else { - // Model ID (PCJR) - phys_writeb(data+2,0xFD); - } - phys_writeb(data+3,0x0A); // Submodel ID - phys_writeb(data+4,0x10); // Bios Revision - /* Tandy doesn't have a 2nd PIC, left as is for now */ - phys_writeb(data+5,(1<<6)|(1<<5)|(1<<4)); // Feature Byte 1 - } else { - if (machine==MCH_MCGA) { - /* PC/2 model 30 model */ - phys_writeb(data+2,0xFA); - phys_writeb(data+3,0x00); // Submodel ID (PS/2) model 30 - } else if (PS1AudioCard) { /* FIXME: Won't work because BIOS_Init() comes before PS1SOUND_Init() */ - phys_writeb(data+2,0xFC); // Model ID (PC) - phys_writeb(data+3,0x0B); // Submodel ID (PS/1). - } else { - phys_writeb(data+2,0xFC); // Model ID (PC) - phys_writeb(data+3,0x00); // Submodel ID - } - phys_writeb(data+4,0x01); // Bios Revision - phys_writeb(data+5,(1<<6)|(1<<5)|(1<<4)); // Feature Byte 1 - } - phys_writeb(data+6,(1<<6)); // Feature Byte 2 - phys_writeb(data+7,0); // Feature Byte 3 - phys_writeb(data+8,0); // Feature Byte 4 - phys_writeb(data+9,0); // Feature Byte 5 - } - } - - // ISA Plug & Play I/O ports - if (!IS_PC98_ARCH) { - ISAPNP_PNP_ADDRESS_PORT = new IO_WriteHandleObject; - ISAPNP_PNP_ADDRESS_PORT->Install(0x279,isapnp_write_port,IO_MB); - ISAPNP_PNP_DATA_PORT = new IO_WriteHandleObject; - ISAPNP_PNP_DATA_PORT->Install(0xA79,isapnp_write_port,IO_MB); - ISAPNP_PNP_READ_PORT = new IO_ReadHandleObject; - ISAPNP_PNP_READ_PORT->Install(ISA_PNP_WPORT,isapnp_read_port,IO_MB); - LOG(LOG_MISC,LOG_DEBUG)("Registered ISA PnP read port at 0x%03x",ISA_PNP_WPORT); - } - - if (enable_integration_device) { - /* integration device callout */ - if (dosbox_int_iocallout == IO_Callout_t_none) - dosbox_int_iocallout = IO_AllocateCallout(IO_TYPE_MB); - if (dosbox_int_iocallout == IO_Callout_t_none) - E_Exit("Failed to get dosbox integration IO callout handle"); - - { - IO_CalloutObject *obj = IO_GetCallout(dosbox_int_iocallout); - if (obj == NULL) E_Exit("Failed to get dosbox integration IO callout"); - obj->Install(0x28,IOMASK_Combine(IOMASK_FULL,IOMASK_Range(4)),dosbox_integration_cb_port_r,dosbox_integration_cb_port_w); - IO_PutCallout(obj); - } - - /* DOSBox integration device */ - if (!IS_PC98_ARCH && isapnpigdevice == NULL && enable_integration_device_pnp) { - isapnpigdevice = new ISAPnPIntegrationDevice; - ISA_PNP_devreg(isapnpigdevice); - } - } - - // ISA Plug & Play BIOS entrypoint - // NTS: Apparently, Windows 95, 98, and ME will re-enumerate and re-install PnP devices if our entry point changes it's address. - if (!IS_PC98_ARCH && ISAPNPBIOS) { - Bitu base; - unsigned int i; - unsigned char c,tmp[256]; - - isapnp_biosstruct_base = base = ROMBIOS_GetMemory(0x21,"ISA Plug & Play BIOS struct",/*paragraph alignment*/0x10); - - if (base == 0) E_Exit("Unable to allocate ISA PnP struct"); - LOG_MSG("ISA Plug & Play BIOS enabled"); - - call_pnp_r = CALLBACK_Allocate(); - call_pnp_rp = PNPentry_real = CALLBACK_RealPointer(call_pnp_r); - CALLBACK_Setup(call_pnp_r,ISAPNP_Handler_RM,CB_RETF,"ISA Plug & Play entry point (real)"); - //LOG_MSG("real entry pt=%08lx\n",PNPentry_real); - - call_pnp_p = CALLBACK_Allocate(); - call_pnp_pp = PNPentry_prot = CALLBACK_RealPointer(call_pnp_p); - CALLBACK_Setup(call_pnp_p,ISAPNP_Handler_PM,CB_RETF,"ISA Plug & Play entry point (protected)"); - //LOG_MSG("prot entry pt=%08lx\n",PNPentry_prot); - - phys_writeb(base+0,'$'); - phys_writeb(base+1,'P'); - phys_writeb(base+2,'n'); - phys_writeb(base+3,'P'); - phys_writeb(base+4,0x10); /* Version: 1.0 */ - phys_writeb(base+5,0x21); /* Length: 0x21 bytes */ - phys_writew(base+6,0x0000); /* Control field: Event notification not supported */ - /* skip checksum atm */ - phys_writed(base+9,0); /* Event notify flag addr: (none) */ - phys_writed(base+0xD,call_pnp_rp); /* Real-mode entry point */ - phys_writew(base+0x11,call_pnp_pp&0xFFFF); /* Protected mode offset */ - phys_writed(base+0x13,(call_pnp_pp >> 12) & 0xFFFF0); /* Protected mode code segment base */ - phys_writed(base+0x17,ISAPNP_ID('D','O','S',0,7,4,0)); /* OEM device identifier (DOSBox 0.740, get it?) */ - phys_writew(base+0x1B,0xF000); /* real-mode data segment */ - phys_writed(base+0x1D,0xF0000); /* protected mode data segment address */ - /* run checksum */ - c=0; - for (i=0;i < 0x21;i++) { - if (i != 8) c += phys_readb(base+i); - } - phys_writeb(base+8,0x100-c); /* checksum value: set so that summing bytes across the struct == 0 */ - - /* input device (keyboard) */ - if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_Keyboard,sizeof(ISAPNP_sysdev_Keyboard),true)) - LOG_MSG("ISAPNP register failed\n"); - - /* input device (mouse) */ - if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_Mouse,sizeof(ISAPNP_sysdev_Mouse),true)) - LOG_MSG("ISAPNP register failed\n"); - - /* DMA controller */ - if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_DMA_Controller,sizeof(ISAPNP_sysdev_DMA_Controller),true)) - LOG_MSG("ISAPNP register failed\n"); - - /* Interrupt controller */ - if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_PIC,sizeof(ISAPNP_sysdev_PIC),true)) - LOG_MSG("ISAPNP register failed\n"); - - /* Timer */ - if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_Timer,sizeof(ISAPNP_sysdev_Timer),true)) - LOG_MSG("ISAPNP register failed\n"); - - /* Realtime clock */ - if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_RTC,sizeof(ISAPNP_sysdev_RTC),true)) - LOG_MSG("ISAPNP register failed\n"); - - /* PC speaker */ - if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_PC_Speaker,sizeof(ISAPNP_sysdev_PC_Speaker),true)) - LOG_MSG("ISAPNP register failed\n"); - - /* System board */ - if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_System_Board,sizeof(ISAPNP_sysdev_System_Board),true)) - LOG_MSG("ISAPNP register failed\n"); - - /* Motherboard PNP resources and general */ - if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_General_ISAPNP,sizeof(ISAPNP_sysdev_General_ISAPNP),true)) - LOG_MSG("ISAPNP register failed\n"); - - /* ISA bus, meaning, a computer with ISA slots. - * The purpose of this device is to convince Windows 95 to automatically install it's - * "ISA Plug and Play bus" so that PnP devices are recognized automatically */ - if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_ISA_BUS,sizeof(ISAPNP_sysdev_ISA_BUS),true)) - LOG_MSG("ISAPNP register failed\n"); - - if (pcibus_enable) { - /* PCI bus, meaning, a computer with PCI slots. - * The purpose of this device is to tell Windows 95 that a PCI bus is present. Without - * this entry, PCI devices will not be recognized until you manually install the PCI driver. */ - if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_PCI_BUS,sizeof(ISAPNP_sysdev_PCI_BUS),true)) - LOG_MSG("ISAPNP register failed\n"); - } - - /* APM BIOS device. To help Windows 95 see our APM BIOS. */ - if (APMBIOS && APMBIOS_pnp) { - LOG_MSG("Registering APM BIOS as ISA Plug & Play BIOS device node"); - if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_APM_BIOS,sizeof(ISAPNP_sysdev_APM_BIOS),true)) - LOG_MSG("ISAPNP register failed\n"); - } - -#if (C_FPU) - /* Numeric Coprocessor */ - if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_Numeric_Coprocessor,sizeof(ISAPNP_sysdev_Numeric_Coprocessor),true)) - LOG_MSG("ISAPNP register failed\n"); -#endif - - /* RAM resources. we have to construct it */ - /* NTS: We don't do this here, but I have an old Toshiba laptop who's PnP BIOS uses - * this device ID to report both RAM and ROM regions. */ - { - Bitu max = MEM_TotalPages() * 4096; - const unsigned char h1[9] = { - ISAPNP_SYSDEV_HEADER( - ISAPNP_ID('P','N','P',0x0,0xC,0x0,0x1), /* PNP0C01 System device, motherboard resources */ - ISAPNP_TYPE(0x05,0x00,0x00), /* type: Memory, RAM, general */ - 0x0001 | 0x0002) - }; - - i = 0; - memcpy(tmp+i,h1,9); i += 9; /* can't disable, can't configure */ - /*----------allocated--------*/ - tmp[i+0] = 0x80 | 6; /* 32-bit memory range */ - tmp[i+1] = 9; /* length=9 */ - tmp[i+2] = 0; - tmp[i+3] = 0x01; /* writeable, no cache, 8-bit, not shadowable, not ROM */ - host_writed(tmp+i+4,0x00000); /* base */ - host_writed(tmp+i+8,max > 0xA0000 ? 0xA0000 : 0x00000); /* length */ - i += 9+3; - - if (max > 0x100000) { - tmp[i+0] = 0x80 | 6; /* 32-bit memory range */ - tmp[i+1] = 9; /* length=9 */ - tmp[i+2] = 0; - tmp[i+3] = 0x01; - host_writed(tmp+i+4,0x100000); /* base */ - host_writed(tmp+i+8,max-0x100000); /* length */ - i += 9+3; - } - - tmp[i+0] = 0x79; /* END TAG */ - tmp[i+1] = 0x00; - i += 2; - /*-------------possible-----------*/ - tmp[i+0] = 0x79; /* END TAG */ - tmp[i+1] = 0x00; - i += 2; - /*-------------compatible---------*/ - tmp[i+0] = 0x79; /* END TAG */ - tmp[i+1] = 0x00; - i += 2; - - if (!ISAPNP_RegisterSysDev(tmp,i)) - LOG_MSG("ISAPNP register failed\n"); - } - - /* register parallel ports */ - for (Bitu portn=0;portn < 3;portn++) { - Bitu port = mem_readw(BIOS_ADDRESS_LPT1+(portn*2)); - if (port != 0) { - const unsigned char h1[9] = { - ISAPNP_SYSDEV_HEADER( - ISAPNP_ID('P','N','P',0x0,0x4,0x0,0x0), /* PNP0400 Standard LPT printer port */ - ISAPNP_TYPE(0x07,0x01,0x00), /* type: General parallel port */ - 0x0001 | 0x0002) - }; - - i = 0; - memcpy(tmp+i,h1,9); i += 9; /* can't disable, can't configure */ - /*----------allocated--------*/ - tmp[i+0] = (8 << 3) | 7; /* IO resource */ - tmp[i+1] = 0x01; /* 16-bit decode */ - host_writew(tmp+i+2,port); /* min */ - host_writew(tmp+i+4,port); /* max */ - tmp[i+6] = 0x10; /* align */ - tmp[i+7] = 0x03; /* length */ - i += 7+1; - - /* TODO: If/when LPT emulation handles the IRQ, add IRQ resource here */ - - tmp[i+0] = 0x79; /* END TAG */ - tmp[i+1] = 0x00; - i += 2; - /*-------------possible-----------*/ - tmp[i+0] = 0x79; /* END TAG */ - tmp[i+1] = 0x00; - i += 2; - /*-------------compatible---------*/ - tmp[i+0] = 0x79; /* END TAG */ - tmp[i+1] = 0x00; - i += 2; - - if (!ISAPNP_RegisterSysDev(tmp,i)) - LOG_MSG("ISAPNP register failed\n"); - } - } - - void BIOS_Post_register_comports_PNP(); - BIOS_Post_register_comports_PNP(); - - void BIOS_Post_register_IDE(); - BIOS_Post_register_IDE(); - - void BIOS_Post_register_FDC(); - BIOS_Post_register_FDC(); - } - - if (IS_PC98_ARCH) { - /* initialize IRQ0 timer to default tick interval. - * PC-98 does not pre-initialize timer 0 of the PIT to 0xFFFF the way IBM PC/XT/AT do */ - PC98_Interval_Timer_Continue(); - PIC_SetIRQMask(0,true); /* PC-98 keeps the timer off unless INT 1Ch is called to set a timer interval */ - } - - CPU_STI(); - - return CBRET_NONE; - } - CALLBACK_HandlerObject cb_bios_scan_video_bios; - static Bitu cb_bios_scan_video_bios__func(void) { - unsigned long size; - - /* NTS: As far as I can tell, video is integrated into the PC-98 BIOS and there is no separate BIOS */ - if (IS_PC98_ARCH) return CBRET_NONE; - - if (cpu.pmode) E_Exit("BIOS error: VIDEO BIOS SCAN function called while in protected/vm86 mode"); - - if (!bios_has_exec_vga_bios) { - bios_has_exec_vga_bios = true; - if (IS_EGAVGA_ARCH) { - /* make sure VGA BIOS is there at 0xC000:0x0000 */ - if (AdapterROM_Read(0xC0000,&size)) { - LOG(LOG_MISC,LOG_DEBUG)("BIOS VIDEO ROM SCAN found VGA BIOS (size=%lu)",size); - adapter_scan_start = 0xC0000 + size; - - // step back into the callback instruction that triggered this call - reg_eip -= 4; - - // FAR CALL into the VGA BIOS - CPU_CALL(false,0xC000,0x0003,reg_eip); - return CBRET_NONE; - } - else { - LOG(LOG_MISC,LOG_WARN)("BIOS VIDEO ROM SCAN did not find VGA BIOS"); - } - } - else { - // CGA, MDA, Tandy, PCjr. No video BIOS to scan for - } - } - - return CBRET_NONE; - } - CALLBACK_HandlerObject cb_bios_adapter_rom_scan; - static Bitu cb_bios_adapter_rom_scan__func(void) { - unsigned long size; - Bit32u c1; - - /* FIXME: I have no documentation on how PC-98 scans for adapter ROM or even if it supports it */ - if (IS_PC98_ARCH) return CBRET_NONE; - - if (cpu.pmode) E_Exit("BIOS error: ADAPTER ROM function called while in protected/vm86 mode"); - - while (adapter_scan_start < 0xF0000) { - if (AdapterROM_Read(adapter_scan_start,&size)) { - Bit16u segm = (Bit16u)(adapter_scan_start >> 4); - - LOG(LOG_MISC,LOG_DEBUG)("BIOS ADAPTER ROM scan found ROM at 0x%lx (size=%lu)",(unsigned long)adapter_scan_start,size); - - c1 = mem_readd(adapter_scan_start+3); - adapter_scan_start += size; - if (c1 != 0UL) { - LOG(LOG_MISC,LOG_DEBUG)("Running ADAPTER ROM entry point"); - - // step back into the callback instruction that triggered this call - reg_eip -= 4; - - // FAR CALL into the VGA BIOS - CPU_CALL(false,segm,0x0003,reg_eip); - return CBRET_NONE; - } - else { - LOG(LOG_MISC,LOG_DEBUG)("FIXME: ADAPTER ROM entry point does not exist"); - } - } - else { - if (IS_EGAVGA_ARCH) // supposedly newer systems only scan on 2KB boundaries by standard? right? - adapter_scan_start = (adapter_scan_start | 2047UL) + 1UL; - else // while older PC/XT systems scanned on 512-byte boundaries? right? - adapter_scan_start = (adapter_scan_start | 511UL) + 1UL; - } - } - - LOG(LOG_MISC,LOG_DEBUG)("BIOS ADAPTER ROM scan complete"); - return CBRET_NONE; - } - CALLBACK_HandlerObject cb_bios_startup_screen; - static Bitu cb_bios_startup_screen__func(void) { - const char *msg = PACKAGE_STRING " (C) 2002-" COPYRIGHT_END_YEAR " The DOSBox Team\nA fork of DOSBox 0.74 by TheGreatCodeholio\nFor more info visit http://dosbox-x.com\nBased on DOSBox (http://dosbox.com)\n\n"; - int logo_x,logo_y,x,y,rowheight=8; - - y = 2; - x = 2; - logo_y = 2; - logo_x = 80 - 2 - (224/8); - - if (cpu.pmode) E_Exit("BIOS error: STARTUP function called while in protected/vm86 mode"); - - // TODO: For those who would rather not use the VGA graphical modes, add a configuration option to "disable graphical splash". - // We would then revert to a plain text copyright and status message here (and maybe an ASCII art version of the DOSBox logo). - if (IS_VGA_ARCH) { - rowheight = 16; - reg_eax = 18; // 640x480 16-color - CALLBACK_RunRealInt(0x10); - DrawDOSBoxLogoVGA((unsigned int)logo_x*8u,(unsigned int)logo_y*(unsigned int)rowheight); - } - else if (machine == MCH_EGA) { - rowheight = 14; - reg_eax = 16; // 640x350 16-color - CALLBACK_RunRealInt(0x10); - - // color correction: change Dark Puke Yellow to brown - IO_Read(0x3DA); IO_Read(0x3BA); - IO_Write(0x3C0,0x06); - IO_Write(0x3C0,0x14); // red=1 green=1 blue=0 - IO_Read(0x3DA); IO_Read(0x3BA); - IO_Write(0x3C0,0x20); - - DrawDOSBoxLogoVGA((unsigned int)logo_x*8u,(unsigned int)logo_y*(unsigned int)rowheight); - } - else if (machine == MCH_CGA || machine == MCH_MCGA || machine == MCH_PCJR || machine == MCH_AMSTRAD || machine == MCH_TANDY) { - rowheight = 8; - reg_eax = 6; // 640x200 2-color - CALLBACK_RunRealInt(0x10); - - DrawDOSBoxLogoCGA6((unsigned int)logo_x*8u,(unsigned int)logo_y*(unsigned int)rowheight); - } - else if (machine == MCH_PC98) { - // clear the graphics layer - for (unsigned int i=0;i < (80*400);i++) { - mem_writeb(0xA8000+i,0); // B - mem_writeb(0xB0000+i,0); // G - mem_writeb(0xB8000+i,0); // R - mem_writeb(0xE0000+i,0); // E - } - - reg_eax = 0x0C00; // enable text layer (PC-98) - CALLBACK_RunRealInt(0x18); - - reg_eax = 0x1100; // show cursor (PC-98) - CALLBACK_RunRealInt(0x18); - - reg_eax = 0x1300; // set cursor pos (PC-98) - reg_edx = 0x0000; // byte position - CALLBACK_RunRealInt(0x18); - - bios_pc98_posx = x; - - reg_eax = 0x4200; // setup 640x400 graphics - reg_ecx = 0xC000; - CALLBACK_RunRealInt(0x18); - - // enable the 4th bitplane, for 16-color analog graphics mode. - // TODO: When we allow the user to emulate only the 8-color BGR digital mode, - // logo drawing should use an alternate drawing method. - IO_Write(0x6A,0x01); // enable 16-color analog mode (this makes the 4th bitplane appear) - IO_Write(0x6A,0x04); // but we don't need the EGC graphics - // If we caught a game mid-page flip, set the display and VRAM pages back to zero - IO_Write(0xA4,0x00); // display page 0 - IO_Write(0xA6,0x00); // write to page 0 - - // program a VGA-like color palette so we can re-use the VGA logo - for (unsigned int i=0;i < 16;i++) { - unsigned int bias = (i & 8) ? 0x5 : 0x0; - - IO_Write(0xA8,i); // DAC index - if (i != 6) { - IO_Write(0xAA,((i & 2) ? 0xA : 0x0) + bias); // green - IO_Write(0xAC,((i & 4) ? 0xA : 0x0) + bias); // red - IO_Write(0xAE,((i & 1) ? 0xA : 0x0) + bias); // blue - } - else { // brown #6 instead of puke yellow - IO_Write(0xAA,((i & 2) ? 0x5 : 0x0) + bias); // green - IO_Write(0xAC,((i & 4) ? 0xA : 0x0) + bias); // red - IO_Write(0xAE,((i & 1) ? 0xA : 0x0) + bias); // blue - } - } - - DrawDOSBoxLogoPC98((unsigned int)logo_x*8u,(unsigned int)logo_y*(unsigned int)rowheight); - - reg_eax = 0x4000; // show the graphics layer (PC-98) so we can render the DOSBox logo - CALLBACK_RunRealInt(0x18); - } - else { - reg_eax = 3; // 80x25 text - CALLBACK_RunRealInt(0x10); - - // TODO: For CGA, PCjr, and Tandy, we could render a 4-color CGA version of the same logo. - // And for MDA/Hercules, we could render a monochromatic ASCII art version. - } - - if (machine != MCH_PC98) { - reg_eax = 0x0200; // set cursor pos - reg_ebx = 0; // page zero - reg_dh = y; // row 4 - reg_dl = x; // column 20 - CALLBACK_RunRealInt(0x10); - } - - BIOS_Int10RightJustifiedPrint(x,y,msg); - - { - uint64_t sz = (uint64_t)MEM_TotalPages() * (uint64_t)4096; - char tmp[512]; - - if (sz >= ((uint64_t)128 << (uint64_t)20)) - sprintf(tmp,"%uMB memory installed\r\n",(unsigned int)(sz >> (uint64_t)20)); - else - sprintf(tmp,"%uKB memory installed\r\n",(unsigned int)(sz >> (uint64_t)10)); - - BIOS_Int10RightJustifiedPrint(x,y,tmp); - } - - { - char tmp[512]; - const char *card = "?"; - - switch (machine) { - case MCH_CGA: - card = "IBM Color Graphics Adapter"; - break; - case MCH_MCGA: - card = "IBM Multi Color Graphics Adapter"; - break; - case MCH_MDA: - card = "IBM Monochrome Display Adapter"; - break; - case MCH_HERC: - card = "IBM Monochrome Display Adapter (Hercules)"; - break; - case MCH_EGA: - card = "IBM Enhanced Graphics Adapter"; - break; - case MCH_PCJR: - card = "PCjr graohics adapter"; - break; - case MCH_TANDY: - card = "Tandy graohics adapter"; - break; - case MCH_VGA: - switch (svgaCard) { - case SVGA_TsengET4K: - card = "Tseng ET4000 SVGA"; - break; - case SVGA_TsengET3K: - card = "Tseng ET3000 SVGA"; - break; - case SVGA_ParadisePVGA1A: - card = "Paradise SVGA"; - break; - case SVGA_S3Trio: - card = "S3 Trio SVGA"; - break; - default: - card = "Standard VGA"; - break; - } - - break; - case MCH_PC98: - card = "PC98 graphics"; - break; - case MCH_AMSTRAD: - card = "Amstrad graphics"; - break; - default: - abort(); // should not happen - break; - }; - - sprintf(tmp,"Video card is %s\n",card); - BIOS_Int10RightJustifiedPrint(x,y,tmp); - } - - { - char tmp[512]; - const char *cpu = "?"; - - switch (CPU_ArchitectureType) { - case CPU_ARCHTYPE_8086: - cpu = "8086"; - break; - case CPU_ARCHTYPE_80186: - cpu = "80186"; - break; - case CPU_ARCHTYPE_286: - cpu = "286"; - break; - case CPU_ARCHTYPE_386: - cpu = "386"; - break; - case CPU_ARCHTYPE_486OLD: - cpu = "486 (older generation)"; - break; - case CPU_ARCHTYPE_486NEW: - cpu = "486 (later generation)"; - break; - case CPU_ARCHTYPE_PENTIUM: - cpu = "Pentium"; - break; - case CPU_ARCHTYPE_P55CSLOW: - cpu = "Pentium MMX"; - break; - }; - - extern bool enable_fpu; - - sprintf(tmp,"%s CPU present",cpu); - BIOS_Int10RightJustifiedPrint(x,y,tmp); - if (enable_fpu) BIOS_Int10RightJustifiedPrint(x,y," with FPU"); - BIOS_Int10RightJustifiedPrint(x,y,"\n"); - } - - if (APMBIOS) { - BIOS_Int10RightJustifiedPrint(x,y,"Advanced Power Management interface active\n"); - } - - if (ISAPNPBIOS) { - BIOS_Int10RightJustifiedPrint(x,y,"ISA Plug & Play BIOS active\n"); - } - -#if !defined(C_EMSCRIPTEN) - BIOS_Int10RightJustifiedPrint(x,y,"\nHit SPACEBAR to pause at this screen\n"); - y--; /* next message should overprint */ - if (machine != MCH_PC98) { - reg_eax = 0x0200; // set cursor pos - reg_ebx = 0; // page zero - reg_dh = y; // row 4 - reg_dl = x; // column 20 - CALLBACK_RunRealInt(0x10); - } - else { - reg_eax = 0x1300u; // set cursor pos (PC-98) - reg_edx = (((unsigned int)y * 80u) + (unsigned int)x) * 2u; // byte position - CALLBACK_RunRealInt(0x18); - } -#endif - - // TODO: Then at this screen, we can print messages demonstrating the detection of - // IDE devices, floppy, ISA PnP initialization, anything of importance. - // I also envision adding the ability to hit DEL or F2 at this point to enter - // a "BIOS setup" screen where all DOSBox configuration options can be - // modified, with the same look and feel of an old BIOS. - -#if C_EMSCRIPTEN - Bit32u lasttick=GetTicks(); - while ((GetTicks()-lasttick)<1000) { - void CALLBACK_Idle(void); - CALLBACK_Idle(); - emscripten_sleep_with_yield(100); - } -#else - if (!control->opt_fastbioslogo) { - bool wait_for_user = false; - Bit32u lasttick=GetTicks(); - while ((GetTicks()-lasttick)<1000) { - if (machine == MCH_PC98) { - reg_eax = 0x0100; // sense key - CALLBACK_RunRealInt(0x18); - SETFLAGBIT(ZF,reg_bh == 0); - } - else { - reg_eax = 0x0100; - CALLBACK_RunRealInt(0x16); - } - - if (!GETFLAG(ZF)) { - if (machine == MCH_PC98) { - reg_eax = 0x0000; // read key - CALLBACK_RunRealInt(0x18); - } - else { - reg_eax = 0x0000; - CALLBACK_RunRealInt(0x16); - } - - if (reg_al == 32) { // user hit space - BIOS_Int10RightJustifiedPrint(x,y,"Hit ENTER or ESC to continue \n"); // overprint - wait_for_user = true; - break; - } - } - } - - while (wait_for_user) { - if (machine == MCH_PC98) { - reg_eax = 0x0000; // read key - CALLBACK_RunRealInt(0x18); - } - else { - reg_eax = 0x0000; - CALLBACK_RunRealInt(0x16); - } - - if (reg_al == 27/*ESC*/ || reg_al == 13/*ENTER*/) - break; - } - } -#endif - - if (machine == MCH_PC98) { - reg_eax = 0x4100; // hide the graphics layer (PC-98) - CALLBACK_RunRealInt(0x18); - - // clear the graphics layer - for (unsigned int i=0;i < (80*400);i++) { - mem_writeb(0xA8000+i,0); // B - mem_writeb(0xB0000+i,0); // G - mem_writeb(0xB8000+i,0); // R - mem_writeb(0xE0000+i,0); // E - } - - IO_Write(0x6A,0x00); // switch back to 8-color mode - } - else { - // restore 80x25 text mode - reg_eax = 3; - CALLBACK_RunRealInt(0x10); - } - - return CBRET_NONE; - } - CALLBACK_HandlerObject cb_bios_boot; - CALLBACK_HandlerObject cb_bios_bootfail; - CALLBACK_HandlerObject cb_pc98_rombasic; - static Bitu cb_pc98_entry__func(void) { - /* the purpose of this function is to say "N88 ROM BASIC NOT FOUND" */ - int x,y; - - x = y = 0; - - /* PC-98 MS-DOS boot sector may RETF back to the BIOS, and this is where execution ends up */ - BIOS_Int10RightJustifiedPrint(x,y,"N88 ROM BASIC NOT IMPLEMENTED"); - - return CBRET_NONE; - } - static Bitu cb_bios_bootfail__func(void) { - int x,y; - - x = y = 0; - - /* PC-98 MS-DOS boot sector may RETF back to the BIOS, and this is where execution ends up */ - BIOS_Int10RightJustifiedPrint(x,y,"Guest OS failed to boot, returned failure"); - - /* and then after this call, there is a JMP $ to loop endlessly */ - return CBRET_NONE; - } - static Bitu cb_bios_boot__func(void) { - /* Reset/power-on overrides the user's A20 gate preferences. - * It's time to revert back to what the user wants. */ - void A20Gate_TakeUserSetting(Section *sec); - void MEM_A20_Enable(bool enabled); - A20Gate_TakeUserSetting(NULL); - MEM_A20_Enable(false); - - if (cpu.pmode) E_Exit("BIOS error: BOOT function called while in protected/vm86 mode"); - DispatchVMEvent(VM_EVENT_BIOS_BOOT); - - // TODO: If instructed to, follow the INT 19h boot pattern, perhaps follow the BIOS Boot Specification, etc. - - // TODO: If instructed to boot a guest OS... - - /* wipe out the stack so it's not there to interfere with the system */ - reg_esp = 0; - reg_eip = 0; - CPU_SetSegGeneral(cs, 0x60); - CPU_SetSegGeneral(ss, 0x60); - - for (Bitu i=0;i < 0x400;i++) mem_writeb(0x7C00+i,0); - - // Begin booting the DOSBox shell. NOTE: VM_Boot_DOSBox_Kernel will change CS:IP instruction pointer! - if (!VM_Boot_DOSBox_Kernel()) E_Exit("BIOS error: BOOT function failed to boot DOSBox kernel"); - return CBRET_NONE; - } -public: - void write_FFFF_signature() { - /* write the signature at 0xF000:0xFFF0 */ - - // The farjump at the processor reset entry point (jumps to POST routine) - phys_writeb(0xffff0,0xEA); // FARJMP - phys_writew(0xffff1,RealOff(BIOS_DEFAULT_RESET_LOCATION)); // offset - phys_writew(0xffff3,RealSeg(BIOS_DEFAULT_RESET_LOCATION)); // segment - - // write system BIOS date - for(Bitu i = 0; i < strlen(bios_date_string); i++) phys_writeb(0xffff5+i,(Bit8u)bios_date_string[i]); - - /* model byte */ - if (machine==MCH_TANDY || machine==MCH_AMSTRAD) phys_writeb(0xffffe,0xff); /* Tandy model */ - else if (machine==MCH_PCJR) phys_writeb(0xffffe,0xfd); /* PCJr model */ - else if (machine==MCH_MCGA) phys_writeb(0xffffe,0xfa); /* PC/2 model 30 model */ - else phys_writeb(0xffffe,0xfc); /* PC (FIXME: This is listed as model byte PS/2 model 60) */ - - // signature - phys_writeb(0xfffff,0x55); - } - BIOS(Section* configuration):Module_base(configuration){ - /* tandy DAC can be requested in tandy_sound.cpp by initializing this field */ - Bitu wo; - - isapnp_biosstruct_base = 0; - - { // TODO: Eventually, move this to BIOS POST or init phase - Section_prop * section=static_cast(control->GetSection("dosbox")); - - bochs_port_e9 = section->Get_bool("bochs debug port e9"); - - // TODO: motherboard init, especially when we get around to full Intel Triton/i440FX chipset emulation - isa_memory_hole_512kb = section->Get_bool("isa memory hole at 512kb"); - - // FIXME: Erm, well this couldv'e been named better. It refers to the amount of conventional memory - // made available to the operating system below 1MB, which is usually DOS. - dos_conventional_limit = (unsigned int)section->Get_int("dos mem limit"); - - // for PC-98: When accessing the floppy through INT 1Bh, when enabled, run through a waiting loop to make sure - // the timer count is not too high on exit (Ys II) - enable_fdc_timer_hack = section->Get_bool("pc-98 int 1b fdc timer wait"); - - { - std::string s = section->Get_string("unhandled irq handler"); - - if (s == "simple") - unhandled_irq_method = UNHANDLED_IRQ_SIMPLE; - else if (s == "cooperative_2nd") - unhandled_irq_method = UNHANDLED_IRQ_COOPERATIVE_2ND; - // pick default - else if (IS_PC98_ARCH) - unhandled_irq_method = UNHANDLED_IRQ_COOPERATIVE_2ND; - else - unhandled_irq_method = UNHANDLED_IRQ_SIMPLE; - } - } - - if (bochs_port_e9) { - if (BOCHS_PORT_E9 == NULL) { - BOCHS_PORT_E9 = new IO_WriteHandleObject; - BOCHS_PORT_E9->Install(0xE9,bochs_port_e9_write,IO_MB); - } - LOG(LOG_MISC,LOG_DEBUG)("Bochs port E9h emulation is active"); - } - else { - if (BOCHS_PORT_E9 != NULL) { - delete BOCHS_PORT_E9; - BOCHS_PORT_E9 = NULL; - } - } - - /* pick locations */ - BIOS_DEFAULT_RESET_LOCATION = PhysToReal416(ROMBIOS_GetMemory(64/*several callbacks*/,"BIOS default reset location",/*align*/4)); - BIOS_DEFAULT_HANDLER_LOCATION = PhysToReal416(ROMBIOS_GetMemory(1/*IRET*/,"BIOS default handler location",/*align*/4)); - BIOS_DEFAULT_IRQ0_LOCATION = PhysToReal416(ROMBIOS_GetMemory(0x13/*see callback.cpp for IRQ0*/,"BIOS default IRQ0 location",/*align*/4)); - BIOS_DEFAULT_IRQ1_LOCATION = PhysToReal416(ROMBIOS_GetMemory(0x15/*see callback.cpp for IRQ1*/,"BIOS default IRQ1 location",/*align*/4)); - BIOS_DEFAULT_IRQ07_DEF_LOCATION = PhysToReal416(ROMBIOS_GetMemory(7/*see callback.cpp for EOI_PIC1*/,"BIOS default IRQ2-7 location",/*align*/4)); - BIOS_DEFAULT_IRQ815_DEF_LOCATION = PhysToReal416(ROMBIOS_GetMemory(9/*see callback.cpp for EOI_PIC1*/,"BIOS default IRQ8-15 location",/*align*/4)); - - write_FFFF_signature(); - - /* Setup all the interrupt handlers the bios controls */ - - /* INT 8 Clock IRQ Handler */ - call_irq0=CALLBACK_Allocate(); - if (IS_PC98_ARCH) - CALLBACK_Setup(call_irq0,INT8_PC98_Handler,CB_IRET,Real2Phys(BIOS_DEFAULT_IRQ0_LOCATION),"IRQ 0 Clock"); - else - CALLBACK_Setup(call_irq0,INT8_Handler,CB_IRQ0,Real2Phys(BIOS_DEFAULT_IRQ0_LOCATION),"IRQ 0 Clock"); - - /* INT 11 Get equipment list */ - callback[1].Install(&INT11_Handler,CB_IRET,"Int 11 Equipment"); - - /* INT 12 Memory Size default at 640 kb */ - callback[2].Install(&INT12_Handler,CB_IRET,"Int 12 Memory"); - - ulimit = 640; - t_conv = MEM_TotalPages() << 2; /* convert 4096/byte pages -> 1024/byte KB units */ - if (allow_more_than_640kb) { - if (machine == MCH_CGA) - ulimit = 736; /* 640KB + 64KB + 32KB 0x00000-0xB7FFF */ - else if (machine == MCH_HERC || machine == MCH_MDA) - ulimit = 704; /* 640KB + 64KB = 0x00000-0xAFFFF */ - - if (t_conv > ulimit) t_conv = ulimit; - if (t_conv > 640) { /* because the memory emulation has already set things up */ - bool MEM_map_RAM_physmem(Bitu start,Bitu end); - MEM_map_RAM_physmem(0xA0000,(t_conv<<10)-1); - memset(GetMemBase()+(640<<10),0,(t_conv-640)<<10); - } - } - else { - if (t_conv > 640) t_conv = 640; - } - - if (IS_TANDY_ARCH) { - /* reduce reported memory size for the Tandy (32k graphics memory - at the end of the conventional 640k) */ - if (machine==MCH_TANDY && t_conv > 624) t_conv = 624; - } - - /* allow user to further limit the available memory below 1MB */ - if (dos_conventional_limit != 0 && t_conv > dos_conventional_limit) - t_conv = dos_conventional_limit; - - // TODO: Allow dosbox.conf to specify an option to add an EBDA (Extended BIOS Data Area) - // at the top of the DOS conventional limit, which we then reduce further to hold - // it. Most BIOSes past 1992 or so allocate an EBDA. - - /* if requested to emulate an ISA memory hole at 512KB, further limit the memory */ - if (isa_memory_hole_512kb && t_conv > 512) t_conv = 512; - - /* and then unmap RAM between t_conv and ulimit */ - if (t_conv < ulimit) { - Bitu start = (t_conv+3)/4; /* start = 1KB to page round up */ - Bitu end = ulimit/4; /* end = 1KB to page round down */ - if (start < end) MEM_ResetPageHandler_Unmapped(start,end-start); - } - - /* INT 4B. Now we can safely signal error instead of printing "Invalid interrupt 4B" - * whenever we install Windows 95. Note that Windows 95 would call INT 4Bh because - * that is where the Virtual DMA API lies in relation to EMM386.EXE */ - int4b_callback.Install(&INT4B_Handler,CB_IRET,"INT 4B"); - - /* INT 14 Serial Ports */ - callback[3].Install(&INT14_Handler,CB_IRET_STI,"Int 14 COM-port"); - - /* INT 15 Misc Calls */ - callback[4].Install(&INT15_Handler,CB_IRET,"Int 15 Bios"); - - /* INT 17 Printer Routines */ - callback[5].Install(&INT17_Handler,CB_IRET_STI,"Int 17 Printer"); - - /* INT 1A TIME and some other functions */ - callback[6].Install(&INT1A_Handler,CB_IRET_STI,"Int 1a Time"); - - /* INT 1C System Timer tick called from INT 8 */ - callback[7].Install(&INT1C_Handler,CB_IRET,"Int 1c Timer"); - - /* IRQ 8 RTC Handler */ - callback[8].Install(&INT70_Handler,CB_IRET,"Int 70 RTC"); - - /* Irq 9 rerouted to irq 2 */ - callback[9].Install(NULL,CB_IRQ9,"irq 9 bios"); - - // INT 19h: Boot function - callback[10].Install(&INT19_Handler,CB_IRET,"int 19"); - - // INT 76h: IDE IRQ 14 - // This is just a dummy IRQ handler to prevent crashes when - // IDE emulation fires the IRQ and OS's like Win95 expect - // the BIOS to handle the interrupt. - // FIXME: Shouldn't the IRQ send an ACK to the PIC as well?!? - callback[11].Install(&IRQ14_Dummy,CB_IRET_EOI_PIC2,"irq 14 ide"); - - // INT 77h: IDE IRQ 15 - // This is just a dummy IRQ handler to prevent crashes when - // IDE emulation fires the IRQ and OS's like Win95 expect - // the BIOS to handle the interrupt. - // FIXME: Shouldn't the IRQ send an ACK to the PIC as well?!? - callback[12].Install(&IRQ15_Dummy,CB_IRET_EOI_PIC2,"irq 15 ide"); - - // INT 0Eh: IDE IRQ 6 - callback[13].Install(&IRQ15_Dummy,CB_IRET_EOI_PIC1,"irq 6 floppy"); - - // INT 18h: Enter BASIC - // Non-IBM BIOS would display "NO ROM BASIC" here - callback[15].Install(&INT18_Handler,CB_IRET,"int 18"); - - init_vm86_fake_io(); - - /* Irq 2-7 */ - call_irq07default=CALLBACK_Allocate(); - - /* Irq 8-15 */ - call_irq815default=CALLBACK_Allocate(); - - /* BIOS boot stages */ - cb_bios_post.Install(&cb_bios_post__func,CB_RETF,"BIOS POST"); - cb_bios_scan_video_bios.Install(&cb_bios_scan_video_bios__func,CB_RETF,"BIOS Scan Video BIOS"); - cb_bios_adapter_rom_scan.Install(&cb_bios_adapter_rom_scan__func,CB_RETF,"BIOS Adapter ROM scan"); - cb_bios_startup_screen.Install(&cb_bios_startup_screen__func,CB_RETF,"BIOS Startup screen"); - cb_bios_boot.Install(&cb_bios_boot__func,CB_RETF,"BIOS BOOT"); - cb_bios_bootfail.Install(&cb_bios_bootfail__func,CB_RETF,"BIOS BOOT FAIL"); - cb_pc98_rombasic.Install(&cb_pc98_entry__func,CB_RETF,"N88 ROM BASIC"); - - // Compatible POST routine location: jump to the callback - { - Bitu wo_fence; - - wo = Real2Phys(BIOS_DEFAULT_RESET_LOCATION); - wo_fence = wo + 64; - - // POST - phys_writeb(wo+0x00,(Bit8u)0xFE); //GRP 4 - phys_writeb(wo+0x01,(Bit8u)0x38); //Extra Callback instruction - phys_writew(wo+0x02,(Bit16u)cb_bios_post.Get_callback()); //The immediate word - wo += 4; - - // video bios scan - phys_writeb(wo+0x00,(Bit8u)0xFE); //GRP 4 - phys_writeb(wo+0x01,(Bit8u)0x38); //Extra Callback instruction - phys_writew(wo+0x02,(Bit16u)cb_bios_scan_video_bios.Get_callback()); //The immediate word - wo += 4; - - // adapter ROM scan - phys_writeb(wo+0x00,(Bit8u)0xFE); //GRP 4 - phys_writeb(wo+0x01,(Bit8u)0x38); //Extra Callback instruction - phys_writew(wo+0x02,(Bit16u)cb_bios_adapter_rom_scan.Get_callback()); //The immediate word - wo += 4; - - // startup screen - phys_writeb(wo+0x00,(Bit8u)0xFE); //GRP 4 - phys_writeb(wo+0x01,(Bit8u)0x38); //Extra Callback instruction - phys_writew(wo+0x02,(Bit16u)cb_bios_startup_screen.Get_callback()); //The immediate word - wo += 4; - - // user boot hook - if (bios_user_boot_hook != 0) { - phys_writeb(wo+0x00,0x9C); //PUSHF - phys_writeb(wo+0x01,0x9A); //CALL FAR - phys_writew(wo+0x02,0x0000); //seg:0 - phys_writew(wo+0x04,bios_user_boot_hook>>4); - wo += 6; - } - - // boot - BIOS_boot_code_offset = wo; - phys_writeb(wo+0x00,(Bit8u)0xFE); //GRP 4 - phys_writeb(wo+0x01,(Bit8u)0x38); //Extra Callback instruction - phys_writew(wo+0x02,(Bit16u)cb_bios_boot.Get_callback()); //The immediate word - wo += 4; - - // boot fail - BIOS_bootfail_code_offset = wo; - phys_writeb(wo+0x00,(Bit8u)0xFE); //GRP 4 - phys_writeb(wo+0x01,(Bit8u)0x38); //Extra Callback instruction - phys_writew(wo+0x02,(Bit16u)cb_bios_bootfail.Get_callback()); //The immediate word - wo += 4; - - /* fence */ - phys_writeb(wo++,0xEB); // JMP $-2 - phys_writeb(wo++,0xFE); - - if (wo > wo_fence) E_Exit("BIOS boot callback overrun"); - - if (IS_PC98_ARCH) { - /* Boot disks that run N88 basic, stopgap */ - PhysPt bo = 0xE8002; // E800:0002 - - phys_writeb(bo+0x00,(Bit8u)0xFE); //GRP 4 - phys_writeb(bo+0x01,(Bit8u)0x38); //Extra Callback instruction - phys_writew(bo+0x02,(Bit16u)cb_pc98_rombasic.Get_callback()); //The immediate word - - phys_writeb(bo+0x04,0xEB); // JMP $-2 - phys_writeb(bo+0x05,0xFE); - } - } - } - ~BIOS(){ - /* snap the CPU back to real mode. this code thinks in terms of 16-bit real mode - * and if allowed to do it's thing in a 32-bit guest OS like WinNT, will trigger - * a page fault. */ - CPU_Snap_Back_To_Real_Mode(); - - if (BOCHS_PORT_E9) { - delete BOCHS_PORT_E9; - BOCHS_PORT_E9=NULL; - } - if (ISAPNP_PNP_ADDRESS_PORT) { - delete ISAPNP_PNP_ADDRESS_PORT; - ISAPNP_PNP_ADDRESS_PORT=NULL; - } - if (ISAPNP_PNP_DATA_PORT) { - delete ISAPNP_PNP_DATA_PORT; - ISAPNP_PNP_DATA_PORT=NULL; - } - if (ISAPNP_PNP_READ_PORT) { - delete ISAPNP_PNP_READ_PORT; - ISAPNP_PNP_READ_PORT=NULL; - } - if (isapnpigdevice) { - /* ISA PnP will auto-free it */ - isapnpigdevice=NULL; - } - - if (dosbox_int_iocallout != IO_Callout_t_none) { - IO_FreeCallout(dosbox_int_iocallout); - dosbox_int_iocallout = IO_Callout_t_none; - } - - /* abort DAC playing */ - if (tandy_sb.port) { - IO_Write(tandy_sb.port+0xcu,0xd3u); - IO_Write(tandy_sb.port+0xcu,0xd0u); - } - real_writeb(0x40,0xd4,0x00); - if (tandy_DAC_callback[0]) { - Bit32u orig_vector=real_readd(0x40,0xd6); - if (orig_vector==tandy_DAC_callback[0]->Get_RealPointer()) { - /* set IRQ vector to old value */ - Bit8u tandy_irq = 7; - if (tandy_sb.port) tandy_irq = tandy_sb.irq; - else if (tandy_dac.port) tandy_irq = tandy_dac.irq; - Bit8u tandy_irq_vector = tandy_irq; - if (tandy_irq_vector<8) tandy_irq_vector += 8; - else tandy_irq_vector += (0x70-8); - - RealSetVec(tandy_irq_vector,real_readd(0x40,0xd6)); - real_writed(0x40,0xd6,0x00000000); - } - delete tandy_DAC_callback[0]; - delete tandy_DAC_callback[1]; - tandy_DAC_callback[0]=NULL; - tandy_DAC_callback[1]=NULL; - } - - /* encourage the callback objects to uninstall HERE while we're in real mode, NOT during the - * destructor stage where we're back in protected mode */ - for (unsigned int i=0;i < 20;i++) callback[i].Uninstall(); - - /* assume these were allocated */ - CALLBACK_DeAllocate(call_irq0); - CALLBACK_DeAllocate(call_irq07default); - CALLBACK_DeAllocate(call_irq815default); - - /* done */ - CPU_Snap_Back_Restore(); - } -}; - -void BIOS_Enter_Boot_Phase(void) { - /* direct CS:IP right to the instruction that leads to the boot process */ - /* note that since it's a callback instruction it doesn't really matter - * what CS:IP is as long as it points to the instruction */ - reg_eip = BIOS_boot_code_offset & 0xFUL; - CPU_SetSegGeneral(cs, BIOS_boot_code_offset >> 4UL); -} - -void BIOS_SetCOMPort(Bitu port, Bit16u baseaddr) { - switch(port) { - case 0: - mem_writew(BIOS_BASE_ADDRESS_COM1,baseaddr); - mem_writeb(BIOS_COM1_TIMEOUT, 10); // FIXME: Right?? - break; - case 1: - mem_writew(BIOS_BASE_ADDRESS_COM2,baseaddr); - mem_writeb(BIOS_COM2_TIMEOUT, 10); // FIXME: Right?? - break; - case 2: - mem_writew(BIOS_BASE_ADDRESS_COM3,baseaddr); - mem_writeb(BIOS_COM3_TIMEOUT, 10); // FIXME: Right?? - break; - case 3: - mem_writew(BIOS_BASE_ADDRESS_COM4,baseaddr); - mem_writeb(BIOS_COM4_TIMEOUT, 10); // FIXME: Right?? - break; - } -} - -void BIOS_SetLPTPort(Bitu port, Bit16u baseaddr) { - switch(port) { - case 0: - mem_writew(BIOS_ADDRESS_LPT1,baseaddr); - mem_writeb(BIOS_LPT1_TIMEOUT, 10); - break; - case 1: - mem_writew(BIOS_ADDRESS_LPT2,baseaddr); - mem_writeb(BIOS_LPT2_TIMEOUT, 10); - break; - case 2: - mem_writew(BIOS_ADDRESS_LPT3,baseaddr); - mem_writeb(BIOS_LPT3_TIMEOUT, 10); - break; - } -} - -void BIOS_PnP_ComPortRegister(Bitu port,Bitu irq) { - /* add to PnP BIOS */ - if (ISAPNPBIOS) { - unsigned char tmp[256]; - unsigned int i; - - /* register serial ports */ - const unsigned char h1[9] = { - ISAPNP_SYSDEV_HEADER( - ISAPNP_ID('P','N','P',0x0,0x5,0x0,0x1), /* PNP0501 16550A-compatible COM port */ - ISAPNP_TYPE(0x07,0x00,0x02), /* type: RS-232 communcations device, 16550-compatible */ - 0x0001 | 0x0002) - }; - - i = 0; - memcpy(tmp+i,h1,9); i += 9; /* can't disable, can't configure */ - /*----------allocated--------*/ - tmp[i+0] = (8 << 3) | 7; /* IO resource */ - tmp[i+1] = 0x01; /* 16-bit decode */ - host_writew(tmp+i+2,port); /* min */ - host_writew(tmp+i+4,port); /* max */ - tmp[i+6] = 0x10; /* align */ - tmp[i+7] = 0x08; /* length */ - i += 7+1; - - if (irq > 0) { - tmp[i+0] = (4 << 3) | 3; /* IRQ resource */ - host_writew(tmp+i+1,1 << irq); - tmp[i+3] = 0x09; /* HTL=1 LTL=1 */ - i += 3+1; - } - - tmp[i+0] = 0x79; /* END TAG */ - tmp[i+1] = 0x00; - i += 2; - /*-------------possible-----------*/ - tmp[i+0] = 0x79; /* END TAG */ - tmp[i+1] = 0x00; - i += 2; - /*-------------compatible---------*/ - tmp[i+0] = 0x79; /* END TAG */ - tmp[i+1] = 0x00; - i += 2; - - if (!ISAPNP_RegisterSysDev(tmp,i)) { - //LOG_MSG("ISAPNP register failed\n"); - } - } -} - -static BIOS* test = NULL; - -void BIOS_Destroy(Section* /*sec*/){ - ROMBIOS_DumpMemory(); - ISA_PNP_FreeAllDevs(); - if (test != NULL) { - delete test; - test = NULL; - } -} - -void BIOS_OnPowerOn(Section* sec) { - (void)sec;//UNUSED - if (test) delete test; - test = new BIOS(control->GetSection("joystick")); -} - -void swapInNextDisk(bool pressed); -void swapInNextCD(bool pressed); - -void INT10_OnResetComplete(); -void CALLBACK_DeAllocate(Bitu in); - -void MOUSE_Unsetup_DOS(void); -void MOUSE_Unsetup_BIOS(void); - -void BIOS_OnResetComplete(Section *x) { - (void)x;//UNUSED - INT10_OnResetComplete(); - - if (IS_PC98_ARCH) { - void PC98_BIOS_Bank_Switch_Reset(void); - PC98_BIOS_Bank_Switch_Reset(); - } - - if (biosConfigSeg != 0u) { - ROMBIOS_FreeMemory((Bitu)(biosConfigSeg << 4u)); /* remember it was alloc'd paragraph aligned, then saved >> 4 */ - biosConfigSeg = 0u; - } - - call_pnp_rp = 0; - if (call_pnp_r != ~0UL) { - CALLBACK_DeAllocate(call_pnp_r); - call_pnp_r = ~0UL; - } - - call_pnp_pp = 0; - if (call_pnp_p != ~0UL) { - CALLBACK_DeAllocate(call_pnp_p); - call_pnp_p = ~0UL; - } - - MOUSE_Unsetup_DOS(); - MOUSE_Unsetup_BIOS(); - - ISA_PNP_FreeAllSysNodeDevs(); -} - -void BIOS_Init() { - DOSBoxMenu::item *item; - - LOG(LOG_MISC,LOG_DEBUG)("Initializing BIOS"); - - /* make sure the array is zeroed */ - ISAPNP_SysDevNodeCount = 0; - ISAPNP_SysDevNodeLargest = 0; - for (int i=0;i < MAX_ISA_PNP_SYSDEVNODES;i++) ISAPNP_SysDevNodes[i] = NULL; - - /* make sure CD swap and floppy swap mapper events are available */ - MAPPER_AddHandler(swapInNextDisk,MK_d,MMODHOST|MMOD1,"swapimg","SwapFloppy",&item); /* Originally "Swap Image" but this version does not swap CDs */ - item->set_text("Swap floppy"); - - MAPPER_AddHandler(swapInNextCD,MK_c,MMODHOST|MMOD1,"swapcd","SwapCD",&item); /* Variant of "Swap Image" for CDs */ - item->set_text("Swap CD"); - - /* NTS: VM_EVENT_BIOS_INIT this callback must be first. */ - AddExitFunction(AddExitFunctionFuncPair(BIOS_Destroy),false); - AddVMEventFunction(VM_EVENT_POWERON,AddVMEventFunctionFuncPair(BIOS_OnPowerOn)); - AddVMEventFunction(VM_EVENT_RESET_END,AddVMEventFunctionFuncPair(BIOS_OnResetComplete)); -} - -void write_ID_version_string() { - Bitu str_id_at,str_ver_at; - size_t str_id_len,str_ver_len; - - /* NTS: We can't move the version and ID strings, it causes programs like MSD.EXE to lose - * track of the "IBM compatible blahblahblah" string. Which means that apparently - * programs looking for this information have the address hardcoded ALTHOUGH - * experiments show you can move the version string around so long as it's - * +1 from a paragraph boundary */ - /* TODO: *DO* allow dynamic relocation however if the dosbox.conf indicates that the user - * is not interested in IBM BIOS compatability. Also, it would be really cool if - * dosbox.conf could override these strings and the user could enter custom BIOS - * version and ID strings. Heh heh heh.. :) */ - str_id_at = 0xFE00E; - str_ver_at = 0xFE061; - str_id_len = strlen(bios_type_string)+1; - str_ver_len = strlen(bios_version_string)+1; - if (!IS_PC98_ARCH) { - /* need to mark these strings off-limits so dynamic allocation does not overwrite them */ - ROMBIOS_GetMemory((Bitu)str_id_len+1,"BIOS ID string",1,str_id_at); - ROMBIOS_GetMemory((Bitu)str_ver_len+1,"BIOS version string",1,str_ver_at); - } - if (str_id_at != 0) { - for (size_t i=0;i < str_id_len;i++) phys_writeb(str_id_at+(PhysPt)i,(Bit8u)bios_type_string[i]); - } - if (str_ver_at != 0) { - for (size_t i=0;i < str_ver_len;i++) phys_writeb(str_ver_at+(PhysPt)i,(Bit8u)bios_version_string[i]); - } -} - -extern Bit8u int10_font_08[256 * 8]; - -/* NTS: Do not use callbacks! This function is called before CALLBACK_Init() */ -void ROMBIOS_Init() { - Section_prop * section=static_cast(control->GetSection("dosbox")); - Bitu oi,i; - - // log - LOG(LOG_MISC,LOG_DEBUG)("Initializing ROM BIOS"); - - oi = (Bitu)section->Get_int("rom bios minimum size"); /* in KB */ - oi = (oi + 3u) & ~3u; /* round to 4KB page */ - if (oi > 128u) oi = 128u; - if (oi == 0u) { - if (IS_PC98_ARCH) - oi = 96u; // BIOS standard range is E8000-FFFFF - else - oi = 64u; - } - if (oi < 8) oi = 8; /* because of some of DOSBox's fixed ROM structures we can only go down to 8KB */ - rombios_minimum_size = (oi << 10); /* convert to minimum, using size coming downward from 1MB */ - - oi = (Bitu)section->Get_int("rom bios allocation max"); /* in KB */ - oi = (oi + 3u) & ~3u; /* round to 4KB page */ - if (oi > 128u) oi = 128u; - if (oi == 0u) { - if (IS_PC98_ARCH) - oi = 96u; - else - oi = 64u; - } - if (oi < 8u) oi = 8u; /* because of some of DOSBox's fixed ROM structures we can only go down to 8KB */ - oi <<= 10u; - if (oi < rombios_minimum_size) oi = rombios_minimum_size; - rombios_minimum_location = 0x100000ul - oi; /* convert to minimum, using size coming downward from 1MB */ - - LOG(LOG_BIOS,LOG_DEBUG)("ROM BIOS range: 0x%05X-0xFFFFF",(int)rombios_minimum_location); - LOG(LOG_BIOS,LOG_DEBUG)("ROM BIOS range according to minimum size: 0x%05X-0xFFFFF",(int)(0x100000 - rombios_minimum_size)); - - if (IS_PC98_ARCH && rombios_minimum_location > 0xE8000) - LOG(LOG_BIOS,LOG_DEBUG)("Caution: Minimum ROM base higher than E8000 will prevent use of actual PC-98 BIOS image or N88 BASIC"); - - if (!MEM_map_ROM_physmem(rombios_minimum_location,0xFFFFF)) E_Exit("Unable to map ROM region as ROM"); - - /* and the BIOS alias at the top of memory (TODO: what about 486/Pentium emulation where the BIOS at the 4GB top is different - * from the BIOS at the legacy 1MB boundary because of shadowing and/or decompressing from ROM at boot? */ - { - uint64_t top = (uint64_t)1UL << (uint64_t)MEM_get_address_bits(); - if (top >= ((uint64_t)1UL << (uint64_t)21UL)) { /* 2MB or more */ - unsigned long alias_base,alias_end; - - alias_base = (unsigned long)top + (unsigned long)rombios_minimum_location - (unsigned long)0x100000UL; - alias_end = (unsigned long)top - (unsigned long)1UL; - - LOG(LOG_BIOS,LOG_DEBUG)("ROM BIOS also mapping alias to 0x%08lx-0x%08lx",alias_base,alias_end); - if (!MEM_map_ROM_alias_physmem(alias_base,alias_end)) { - void MEM_cut_RAM_up_to(Bitu addr); - - /* it's possible if memory aliasing is set that memsize is too large to make room. - * let memory emulation know where the ROM BIOS starts so it can unmap the RAM pages, - * reduce the memory reported to the OS, and try again... */ - LOG(LOG_BIOS,LOG_DEBUG)("No room for ROM BIOS alias, reducing reported memory and unmapping RAM pages to make room"); - MEM_cut_RAM_up_to(alias_base); - - if (!MEM_map_ROM_alias_physmem(alias_base,alias_end)) - E_Exit("Unable to map ROM region as ROM alias"); - } - } - } - - /* set up allocation */ - rombios_alloc.name = "ROM BIOS"; - rombios_alloc.topDownAlloc = true; - rombios_alloc.initSetRange(rombios_minimum_location,0xFFFF0 - 1); - - write_ID_version_string(); - - /* some structures when enabled are fixed no matter what */ - if (rom_bios_8x8_cga_font && !IS_PC98_ARCH) { - /* line 139, int10_memory.cpp: the 8x8 font at 0xF000:FA6E, first 128 chars. - * allocate this NOW before other things get in the way */ - if (ROMBIOS_GetMemory(128*8,"BIOS 8x8 font (first 128 chars)",1,0xFFA6E) == 0) { - LOG_MSG("WARNING: Was not able to mark off 0xFFA6E off-limits for 8x8 font"); - } - } - - /* PC-98 BIOS vectors appear to reside at segment 0xFD80. This is so common some games - * use it (through INT 1Dh) to detect whether they are running on PC-98 or not (issue #927). - * - * Note that INT 1Dh is one of the few BIOS interrupts not intercepted by PC-98 MS-DOS */ - if (IS_PC98_ARCH) { - if (ROMBIOS_GetMemory(128,"PC-98 INT vector stub segment 0xFD80",1,0xFD800) == 0) { - LOG_MSG("WARNING: Was not able to mark off 0xFD800 off-limits for PC-98 int vector stubs"); - } - } - - /* PC-98 BIOSes have a LIO interface at segment F990 with graphic subroutines for Microsoft BASIC */ - if (IS_PC98_ARCH) { - if (ROMBIOS_GetMemory(256,"PC-98 LIO graphic ROM BIOS library",1,0xF9900) == 0) { - LOG_MSG("WARNING: Was not able to mark off 0xF9900 off-limits for PC-98 LIO graphics library"); - } - } - - /* install the font */ - if (rom_bios_8x8_cga_font) { - for (i=0;i<128*8;i++) { - phys_writeb(PhysMake(0xf000,0xfa6e)+i,int10_font_08[i]); - } - } - - /* we allow dosbox.conf to specify a binary blob to load into ROM BIOS and execute after reset. - * we allow this for both hacker curiosity and automated CPU testing. */ - { - std::string path = section->Get_string("call binary on reset"); - struct stat st; - - if (!path.empty() && stat(path.c_str(),&st) == 0 && S_ISREG(st.st_mode) && st.st_size <= (128u*1024u)) { - Bitu base = ROMBIOS_GetMemory((Bitu)st.st_size,"User reset vector binary",16u/*page align*/,0u); - - if (base != 0) { - FILE *fp = fopen(path.c_str(),"rb"); - - if (fp != NULL) { - /* NTS: Make sure memory base != NULL, and that it fits within 1MB. - * memory code allocates a minimum 1MB of memory even if - * guest memory is less than 1MB because ROM BIOS emulation - * depends on it. */ - assert(GetMemBase() != NULL); - assert((base+(Bitu)st.st_size) <= 0x100000ul); - fread(GetMemBase()+base,(size_t)st.st_size,1u,fp); - fclose(fp); - - LOG_MSG("User reset vector binary '%s' loaded at 0x%lx",path.c_str(),(unsigned long)base); - bios_user_reset_vector_blob = base; - } - else { - LOG_MSG("WARNING: Unable to open file to load user reset vector binary '%s' into ROM BIOS memory",path.c_str()); - } - } - else { - LOG_MSG("WARNING: Unable to load user reset vector binary '%s' into ROM BIOS memory",path.c_str()); - } - } - } - - /* we allow dosbox.conf to specify a binary blob to load into ROM BIOS and execute just before boot. - * we allow this for both hacker curiosity and automated CPU testing. */ - { - std::string path = section->Get_string("call binary on boot"); - struct stat st; - - if (!path.empty() && stat(path.c_str(),&st) == 0 && S_ISREG(st.st_mode) && st.st_size <= (128u*1024u)) { - Bitu base = ROMBIOS_GetMemory((Bitu)st.st_size,"User boot hook binary",16u/*page align*/,0u); - - if (base != 0) { - FILE *fp = fopen(path.c_str(),"rb"); - - if (fp != NULL) { - /* NTS: Make sure memory base != NULL, and that it fits within 1MB. - * memory code allocates a minimum 1MB of memory even if - * guest memory is less than 1MB because ROM BIOS emulation - * depends on it. */ - assert(GetMemBase() != NULL); - assert((base+(Bitu)st.st_size) <= 0x100000ul); - fread(GetMemBase()+base,(size_t)st.st_size,1u,fp); - fclose(fp); - - LOG_MSG("User boot hook binary '%s' loaded at 0x%lx",path.c_str(),(unsigned long)base); - bios_user_boot_hook = base; - } - else { - LOG_MSG("WARNING: Unable to open file to load user boot hook binary '%s' into ROM BIOS memory",path.c_str()); - } - } - else { - LOG_MSG("WARNING: Unable to load user boot hook binary '%s' into ROM BIOS memory",path.c_str()); - } - } - } -} - -//! \brief Updates the state of a lockable key. -void UpdateKeyWithLed(int nVirtKey, int flagAct, int flagLed); - -void BIOS_SynchronizeNumLock() -{ -#if defined(WIN32) - UpdateKeyWithLed(VK_NUMLOCK, BIOS_KEYBOARD_FLAGS1_NUMLOCK_ACTIVE, BIOS_KEYBOARD_LEDS_NUM_LOCK); -#endif -} - -void BIOS_SynchronizeCapsLock() -{ -#if defined(WIN32) - UpdateKeyWithLed(VK_CAPITAL, BIOS_KEYBOARD_FLAGS1_CAPS_LOCK_ACTIVE, BIOS_KEYBOARD_LEDS_CAPS_LOCK); -#endif -} - -void BIOS_SynchronizeScrollLock() -{ -#if defined(WIN32) - UpdateKeyWithLed(VK_SCROLL, BIOS_KEYBOARD_FLAGS1_SCROLL_LOCK_ACTIVE, BIOS_KEYBOARD_LEDS_SCROLL_LOCK); -#endif -} - -void UpdateKeyWithLed(int nVirtKey, int flagAct, int flagLed) -{ -#if defined(WIN32) - - const auto state = GetKeyState(nVirtKey); - - const auto flags1 = BIOS_KEYBOARD_FLAGS1; - const auto flags2 = BIOS_KEYBOARD_LEDS; - - auto flag1 = mem_readb(flags1); - auto flag2 = mem_readb(flags2); - - if (state & 1) - { - flag1 |= flagAct; - flag2 |= flagLed; - } - else - { - flag1 &= ~flagAct; - flag2 &= ~flagLed; - } - - mem_writeb(flags1, flag1); - mem_writeb(flags2, flag2); - -#else - - (void)nVirtKey; - (void)flagAct; - (void)flagLed; - -#endif -} +/* + * Copyright (C) 2002-2015 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include "dosbox.h" +#include "mem.h" +#include "cpu.h" +#include "bios.h" +#include "regs.h" +#include "cpu.h" +#include "callback.h" +#include "inout.h" +#include "pic.h" +#include "hardware.h" +#include "pci_bus.h" +#include "joystick.h" +#include "mouse.h" +#include "callback.h" +#include "setup.h" +#include "bios_disk.h" +#include "serialport.h" +#include "mapper.h" +#include "vga.h" +#include "pc98_gdc.h" +#include "pc98_gdc_const.h" +#include "regionalloctracking.h" +#include "build_timestamp.h" +extern bool PS1AudioCard; +#include "parport.h" +#include +#include + +#if C_EMSCRIPTEN +# include +#endif + +#if defined(_MSC_VER) +# pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */ +# pragma warning(disable:4305) /* truncation from double to float */ +#endif + +#if defined(WIN32) && !defined(S_ISREG) +# define S_ISREG(x) ((x & S_IFREG) == S_IFREG) +#endif + +/* mouse.cpp */ +extern bool en_bios_ps2mouse; +extern bool rom_bios_8x8_cga_font; +extern bool pcibus_enable; + +uint32_t Keyb_ig_status(); +bool VM_Boot_DOSBox_Kernel(); +Bit32u MEM_get_address_bits(); +Bitu bios_post_parport_count(); +Bitu bios_post_comport_count(); +bool KEYBOARD_Report_BIOS_PS2Mouse(); +bool MEM_map_ROM_alias_physmem(Bitu start,Bitu end); + +bool bochs_port_e9 = false; +bool isa_memory_hole_512kb = false; +bool int15_wait_force_unmask_irq = false; + +int unhandled_irq_method = UNHANDLED_IRQ_SIMPLE; + +Bit16u biosConfigSeg=0; + +Bitu BIOS_DEFAULT_IRQ0_LOCATION = ~0u; // (RealMake(0xf000,0xfea5)) +Bitu BIOS_DEFAULT_IRQ1_LOCATION = ~0u; // (RealMake(0xf000,0xe987)) +Bitu BIOS_DEFAULT_IRQ07_DEF_LOCATION = ~0u; // (RealMake(0xf000,0xff55)) +Bitu BIOS_DEFAULT_IRQ815_DEF_LOCATION = ~0u; // (RealMake(0xf000,0xe880)) + +Bitu BIOS_DEFAULT_HANDLER_LOCATION = ~0u; // (RealMake(0xf000,0xff53)) + +Bitu BIOS_VIDEO_TABLE_LOCATION = ~0u; // RealMake(0xf000,0xf0a4) +Bitu BIOS_VIDEO_TABLE_SIZE = 0u; + +Bitu BIOS_DEFAULT_RESET_LOCATION = ~0u; // RealMake(0xf000,0xe05b) + +bool allow_more_than_640kb = false; + +unsigned int APM_BIOS_connected_minor_version = 0;// what version the OS connected to us with. default to 1.0 +unsigned int APM_BIOS_minor_version = 2; // what version to emulate e.g to emulate 1.2 set this to 2 + +/* default bios type/version/date strings */ +const char* const bios_type_string = "IBM COMPATIBLE 486 BIOS COPYRIGHT The DOSBox Team."; +const char* const bios_version_string = "DOSBox FakeBIOS v1.0"; +const char* const bios_date_string = "01/01/92"; + +bool APM_inactivity_timer = true; + +RegionAllocTracking rombios_alloc; + +Bitu rombios_minimum_location = 0xF0000; /* minimum segment allowed */ +Bitu rombios_minimum_size = 0x10000; + +bool MEM_map_ROM_physmem(Bitu start,Bitu end); +bool MEM_unmap_physmem(Bitu start,Bitu end); + +static std::string bochs_port_e9_line; + +static void bochs_port_e9_flush() { + if (!bochs_port_e9_line.empty()) { + LOG_MSG("Bochs port E9h: %s",bochs_port_e9_line.c_str()); + bochs_port_e9_line.clear(); + } +} + +void bochs_port_e9_write(Bitu port,Bitu val,Bitu /*iolen*/) { + (void)port;//UNUSED + if (val == '\n' || val == '\r') { + bochs_port_e9_flush(); + } + else { + bochs_port_e9_line += (char)val; + if (bochs_port_e9_line.length() >= 256) + bochs_port_e9_flush(); + } +} + +void ROMBIOS_DumpMemory() { + rombios_alloc.logDump(); +} + +void ROMBIOS_SanityCheck() { + rombios_alloc.sanityCheck(); +} + +Bitu ROMBIOS_MinAllocatedLoc() { + Bitu r = rombios_alloc.getMinAddress(); + + if (r > (0x100000u - rombios_minimum_size)) + r = (0x100000u - rombios_minimum_size); + + return r & ~0xFFFu; +} + +void ROMBIOS_FreeUnusedMinToLoc(Bitu phys) { + Bitu new_phys; + + if (rombios_minimum_location & 0xFFF) E_Exit("ROMBIOS: FreeUnusedMinToLoc minimum location not page aligned"); + + phys &= ~0xFFFUL; + new_phys = rombios_alloc.freeUnusedMinToLoc(phys) & (~0xFFFUL); + assert(new_phys >= phys); + if (phys < new_phys) MEM_unmap_physmem(phys,new_phys-1); + rombios_minimum_location = new_phys; + ROMBIOS_SanityCheck(); + ROMBIOS_DumpMemory(); +} + +bool ROMBIOS_FreeMemory(Bitu phys) { + return rombios_alloc.freeMemory(phys); +} + +Bitu ROMBIOS_GetMemory(Bitu bytes,const char *who,Bitu alignment,Bitu must_be_at) { + return rombios_alloc.getMemory(bytes,who,alignment,must_be_at); +} + +void ROMBIOS_InitForCustomBIOS(void) { + rombios_alloc.initSetRange(0xD8000,0xE0000); +} + +static IO_Callout_t dosbox_int_iocallout = IO_Callout_t_none; + +static unsigned char dosbox_int_register_shf = 0; +static uint32_t dosbox_int_register = 0; +static unsigned char dosbox_int_regsel_shf = 0; +static uint32_t dosbox_int_regsel = 0; +static bool dosbox_int_error = false; +static bool dosbox_int_busy = false; +static const char *dosbox_int_version = "DOSBox-X integration device v1.0"; +static const char *dosbox_int_ver_read = NULL; + +struct dosbox_int_saved_state { + unsigned char dosbox_int_register_shf; + uint32_t dosbox_int_register; + unsigned char dosbox_int_regsel_shf; + uint32_t dosbox_int_regsel; + bool dosbox_int_error; + bool dosbox_int_busy; +}; + +#define DOSBOX_INT_SAVED_STATE_MAX 4 + +struct dosbox_int_saved_state dosbox_int_saved[DOSBOX_INT_SAVED_STATE_MAX]; +int dosbox_int_saved_sp = -1; + +/* for use with interrupt handlers in DOS/Windows that need to save IG state + * to ensure that IG state is restored on return in order to not interfere + * with anything userspace is doing (as an alternative to wrapping all access + * in CLI/STI or PUSHF/CLI/POPF) */ +bool dosbox_int_push_save_state(void) { + + if (dosbox_int_saved_sp >= (DOSBOX_INT_SAVED_STATE_MAX-1)) + return false; + + struct dosbox_int_saved_state *ss = &dosbox_int_saved[++dosbox_int_saved_sp]; + + ss->dosbox_int_register_shf = dosbox_int_register_shf; + ss->dosbox_int_register = dosbox_int_register; + ss->dosbox_int_regsel_shf = dosbox_int_regsel_shf; + ss->dosbox_int_regsel = dosbox_int_regsel; + ss->dosbox_int_error = dosbox_int_error; + ss->dosbox_int_busy = dosbox_int_busy; + return true; +} + +bool dosbox_int_pop_save_state(void) { + if (dosbox_int_saved_sp < 0) + return false; + + struct dosbox_int_saved_state *ss = &dosbox_int_saved[dosbox_int_saved_sp--]; + + dosbox_int_register_shf = ss->dosbox_int_register_shf; + dosbox_int_register = ss->dosbox_int_register; + dosbox_int_regsel_shf = ss->dosbox_int_regsel_shf; + dosbox_int_regsel = ss->dosbox_int_regsel; + dosbox_int_error = ss->dosbox_int_error; + dosbox_int_busy = ss->dosbox_int_busy; + return true; +} + +bool dosbox_int_discard_save_state(void) { + if (dosbox_int_saved_sp < 0) + return false; + + dosbox_int_saved_sp--; + return true; +} + +extern bool user_cursor_locked; +extern int user_cursor_x,user_cursor_y; +extern int user_cursor_sw,user_cursor_sh; + +static std::string dosbox_int_debug_out; + +/* read triggered, update the regsel */ +void dosbox_integration_trigger_read() { + dosbox_int_error = false; + + switch (dosbox_int_regsel) { + case 0: /* Identification */ + dosbox_int_register = 0xD05B0740; + break; + case 1: /* test */ + break; + case 2: /* version string */ + if (dosbox_int_ver_read == NULL) + dosbox_int_ver_read = dosbox_int_version; + + dosbox_int_register = 0; + for (Bitu i=0;i < 4;i++) { + if (*dosbox_int_ver_read == 0) { + dosbox_int_ver_read = dosbox_int_version; + break; + } + + dosbox_int_register += ((uint32_t)((unsigned char)(*dosbox_int_ver_read++))) << (uint32_t)(i * 8); + } + break; + case 3: /* version number */ + dosbox_int_register = (0x01U/*major*/) + (0x00U/*minor*/ << 8U) + (0x00U/*subver*/ << 16U) + (0x01U/*bump*/ << 24U); + break; + +// case 0x804200: /* keyboard input injection -- not supposed to read */ +// break; + + case 0x804201: /* keyboard status */ + dosbox_int_register = Keyb_ig_status(); + break; + + case 0x434D54: /* read user mouse status */ + dosbox_int_register = + (user_cursor_locked ? (1UL << 0UL) : 0UL); /* bit 0 = mouse capture lock */ + break; + + case 0x434D55: /* read user mouse cursor position */ + dosbox_int_register = ((user_cursor_y & 0xFFFFUL) << 16UL) | (user_cursor_x & 0xFFFFUL); + break; + + case 0x434D56: { /* read user mouse cursor position (normalized for Windows 3.x) */ + signed long long x = ((signed long long)user_cursor_x << 16LL) / (signed long long)(user_cursor_sw-1); + signed long long y = ((signed long long)user_cursor_y << 16LL) / (signed long long)(user_cursor_sh-1); + if (x < 0x0000LL) x = 0x0000LL; + if (x > 0xFFFFLL) x = 0xFFFFLL; + if (y < 0x0000LL) y = 0x0000LL; + if (y > 0xFFFFLL) y = 0xFFFFLL; + dosbox_int_register = ((unsigned int)y << 16UL) | (unsigned int)x; + } break; + + case 0xC54010: /* Screenshot/capture trigger */ + /* TODO: This should also be hidden behind an enable switch, so that rogue DOS development + * can't retaliate if the user wants to capture video or screenshots. */ +#if (C_SSHOT) + extern Bitu CaptureState; + dosbox_int_register = 0x00000000; // available + if (CaptureState & CAPTURE_IMAGE) + dosbox_int_register |= 1 << 0; // Image capture is in progress + if (CaptureState & CAPTURE_VIDEO) + dosbox_int_register |= 1 << 1; // Video capture is in progress + if (CaptureState & CAPTURE_WAVE) + dosbox_int_register |= 1 << 2; // WAVE capture is in progress +#else + dosbox_int_register = 0xC0000000; // not available (bit 31 set), not enabled (bit 30 set) +#endif + break; + + case 0xAA55BB66UL: /* interface reset result */ + break; + + default: + dosbox_int_register = 0xAA55AA55; + dosbox_int_error = true; + break; + }; + + LOG(LOG_MISC,LOG_DEBUG)("DOSBox integration read 0x%08lx got 0x%08lx (err=%u)\n", + (unsigned long)dosbox_int_regsel, + (unsigned long)dosbox_int_register, + dosbox_int_error?1:0); +} + +unsigned int mouse_notify_mode = 0; +// 0 = off +// 1 = trigger as PS/2 mouse interrupt + +/* write triggered */ +void dosbox_integration_trigger_write() { + dosbox_int_error = false; + + LOG(LOG_MISC,LOG_DEBUG)("DOSBox integration write 0x%08lx val 0x%08lx\n", + (unsigned long)dosbox_int_regsel, + (unsigned long)dosbox_int_register); + + switch (dosbox_int_regsel) { + case 1: /* test */ + break; + + case 2: /* version string */ + dosbox_int_ver_read = NULL; + break; + + case 0xDEB0: /* debug output (to log) */ + for (unsigned int b=0;b < 4;b++) { + unsigned char c = (unsigned char)(dosbox_int_register >> (b * 8U)); + if (c == '\n' || dosbox_int_debug_out.length() >= 200) { + LOG_MSG("Client debug message: %s\n",dosbox_int_debug_out.c_str()); + dosbox_int_debug_out.clear(); + } + else if (c != 0) { + dosbox_int_debug_out += ((char)c); + } + else { + break; + } + } + dosbox_int_register = 0; + break; + + case 0xDEB1: /* debug output clear */ + dosbox_int_debug_out.clear(); + break; + + case 0x52434D: /* release mouse capture 'MCR' */ + void GFX_ReleaseMouse(void); + GFX_ReleaseMouse(); + break; + + case 0x804200: /* keyboard input injection */ + void Mouse_ButtonPressed(Bit8u button); + void Mouse_ButtonReleased(Bit8u button); + void pc98_keyboard_send(const unsigned char b); + void Mouse_CursorMoved(float xrel,float yrel,float x,float y,bool emulate); + void KEYBOARD_AUX_Event(float x,float y,Bitu buttons,int scrollwheel); + void KEYBOARD_AddBuffer(Bit16u data); + + switch ((dosbox_int_register>>8)&0xFF) { + case 0x00: // keyboard + if (IS_PC98_ARCH) + pc98_keyboard_send(dosbox_int_register&0xFF); + else + KEYBOARD_AddBuffer(dosbox_int_register&0xFF); + break; + case 0x01: // AUX + if (!IS_PC98_ARCH) + KEYBOARD_AddBuffer((dosbox_int_register&0xFF)|0x100/*AUX*/); + else // no such interface in PC-98 mode + dosbox_int_error = true; + break; + case 0x08: // mouse button injection + if (dosbox_int_register&0x80) Mouse_ButtonPressed(dosbox_int_register&0x7F); + else Mouse_ButtonReleased(dosbox_int_register&0x7F); + break; + case 0x09: // mouse movement injection (X) + Mouse_CursorMoved(((dosbox_int_register>>16UL) / 256.0f) - 1.0f,0,0,0,true); + break; + case 0x0A: // mouse movement injection (Y) + Mouse_CursorMoved(0,((dosbox_int_register>>16UL) / 256.0f) - 1.0f,0,0,true); + break; + case 0x0B: // mouse scrollwheel injection + // TODO + break; + default: + dosbox_int_error = true; + break; + } + break; + +// case 0x804201: /* keyboard status do not write */ +// break; + + /* this command is used to enable notification of mouse movement over the windows even if the mouse isn't captured */ + case 0x434D55: /* read user mouse cursor position */ + case 0x434D56: /* read user mouse cursor position (normalized for Windows 3.x) */ + mouse_notify_mode = dosbox_int_register & 0xFF; + LOG(LOG_MISC,LOG_DEBUG)("Mouse notify mode=%u",mouse_notify_mode); + break; + + case 0xC54010: /* Screenshot/capture trigger */ +#if (C_SSHOT) + void CAPTURE_ScreenShotEvent(bool pressed); + void CAPTURE_VideoEvent(bool pressed); +#endif + void CAPTURE_WaveEvent(bool pressed); + + /* TODO: It would be wise to grant/deny access to this register through another dosbox.conf option + * so that rogue DOS development cannot shit-spam the capture folder */ +#if (C_SSHOT) + if (dosbox_int_register & 1) + CAPTURE_ScreenShotEvent(true); + if (dosbox_int_register & 2) + CAPTURE_VideoEvent(true); +#endif + if (dosbox_int_register & 4) + CAPTURE_WaveEvent(true); + break; + + default: + dosbox_int_register = 0x55AA55AA; + dosbox_int_error = true; + break; + }; +} + +/* PORT 0x28: Index + * 0x29: Data + * 0x2A: Status(R) or Command(W) + * 0x2B: Not yet assigned + * + * Registers are 32-bit wide. I/O to index and data rotate through the + * bytes of the register depending on I/O length, meaning that one 32-bit + * I/O read will read the entire register, while four 8-bit reads will + * read one byte out of 4. */ + +static Bitu dosbox_integration_port00_index_r(Bitu port,Bitu iolen) { + (void)port;//UNUSED + Bitu retb = 0; + Bitu ret = 0; + + while (iolen > 0) { + ret += ((dosbox_int_regsel >> (dosbox_int_regsel_shf * 8)) & 0xFFU) << (retb * 8); + if ((++dosbox_int_regsel_shf) >= 4) dosbox_int_regsel_shf = 0; + iolen--; + retb++; + } + + return ret; +} + +static void dosbox_integration_port00_index_w(Bitu port,Bitu val,Bitu iolen) { + (void)port;//UNUSED + uint32_t msk; + + while (iolen > 0) { + msk = 0xFFU << (dosbox_int_regsel_shf * 8); + dosbox_int_regsel = (dosbox_int_regsel & ~msk) + ((val & 0xFF) << (dosbox_int_regsel_shf * 8)); + if ((++dosbox_int_regsel_shf) >= 4) dosbox_int_regsel_shf = 0; + val >>= 8U; + iolen--; + } +} + +static Bitu dosbox_integration_port01_data_r(Bitu port,Bitu iolen) { + (void)port;//UNUSED + Bitu retb = 0; + Bitu ret = 0; + + while (iolen > 0) { + if (dosbox_int_register_shf == 0) dosbox_integration_trigger_read(); + ret += ((dosbox_int_register >> (dosbox_int_register_shf * 8)) & 0xFFU) << (retb * 8); + if ((++dosbox_int_register_shf) >= 4) dosbox_int_register_shf = 0; + iolen--; + retb++; + } + + return ret; +} + +static void dosbox_integration_port01_data_w(Bitu port,Bitu val,Bitu iolen) { + (void)port;//UNUSED + uint32_t msk; + + while (iolen > 0) { + msk = 0xFFU << (dosbox_int_register_shf * 8); + dosbox_int_register = (dosbox_int_register & ~msk) + ((val & 0xFF) << (dosbox_int_register_shf * 8)); + if ((++dosbox_int_register_shf) >= 4) dosbox_int_register_shf = 0; + if (dosbox_int_register_shf == 0) dosbox_integration_trigger_write(); + val >>= 8U; + iolen--; + } +} + +static Bitu dosbox_integration_port02_status_r(Bitu port,Bitu iolen) { + (void)iolen;//UNUSED + (void)port;//UNUSED + /* status */ + /* 7:6 = regsel byte index + * 5:4 = register byte index + * 3:2 = reserved + * 1 = error + * 0 = busy */ + return + ((unsigned int)dosbox_int_regsel_shf << 6u) + ((unsigned int)dosbox_int_register_shf << 4u) + + (dosbox_int_error ? 2u : 0u) + (dosbox_int_busy ? 1u : 0u); +} + +static void dosbox_integration_port02_command_w(Bitu port,Bitu val,Bitu iolen) { + (void)port; + (void)iolen; + switch (val) { + case 0x00: /* reset latch */ + dosbox_int_register_shf = 0; + dosbox_int_regsel_shf = 0; + break; + case 0x01: /* flush write */ + if (dosbox_int_register_shf != 0) { + dosbox_integration_trigger_write(); + dosbox_int_register_shf = 0; + } + break; + case 0x20: /* push state */ + if (dosbox_int_push_save_state()) { + dosbox_int_register_shf = 0; + dosbox_int_regsel_shf = 0; + dosbox_int_error = false; + dosbox_int_busy = false; + dosbox_int_regsel = 0xAA55BB66; + dosbox_int_register = 0xD05B0C5; + LOG(LOG_MISC,LOG_DEBUG)("DOSBOX IG state saved"); + } + else { + LOG(LOG_MISC,LOG_DEBUG)("DOSBOX IG unable to push state, stack overflow"); + dosbox_int_error = true; + } + break; + case 0x21: /* pop state */ + if (dosbox_int_pop_save_state()) { + LOG(LOG_MISC,LOG_DEBUG)("DOSBOX IG state restored"); + } + else { + LOG(LOG_MISC,LOG_DEBUG)("DOSBOX IG unable to pop state, stack underflow"); + dosbox_int_error = true; + } + break; + case 0x22: /* discard state */ + if (dosbox_int_discard_save_state()) { + LOG(LOG_MISC,LOG_DEBUG)("DOSBOX IG state discarded"); + } + else { + LOG(LOG_MISC,LOG_DEBUG)("DOSBOX IG unable to discard state, stack underflow"); + dosbox_int_error = true; + } + break; + case 0x23: /* discard all state */ + while (dosbox_int_discard_save_state()); + break; + case 0xFE: /* clear error */ + dosbox_int_error = false; + break; + case 0xFF: /* reset interface */ + dosbox_int_busy = false; + dosbox_int_error = false; + dosbox_int_regsel = 0xAA55BB66; + dosbox_int_register = 0xD05B0C5; + break; + default: + dosbox_int_error = true; + break; + } +} + +static IO_ReadHandler* const dosbox_integration_cb_ports_r[4] = { + dosbox_integration_port00_index_r, + dosbox_integration_port01_data_r, + dosbox_integration_port02_status_r, + NULL +}; + +static IO_ReadHandler* dosbox_integration_cb_port_r(IO_CalloutObject &co,Bitu port,Bitu iolen) { + (void)co; + (void)iolen; + return dosbox_integration_cb_ports_r[port&3]; +} + +static IO_WriteHandler* const dosbox_integration_cb_ports_w[4] = { + dosbox_integration_port00_index_w, + dosbox_integration_port01_data_w, + dosbox_integration_port02_command_w, + NULL +}; + +static IO_WriteHandler* dosbox_integration_cb_port_w(IO_CalloutObject &co,Bitu port,Bitu iolen) { + (void)co; + (void)iolen; + return dosbox_integration_cb_ports_w[port&3]; +} + +/* if mem_systems 0 then size_extended is reported as the real size else + * zero is reported. ems and xms can increase or decrease the other_memsystems + * counter using the BIOS_ZeroExtendedSize call */ +static Bit16u size_extended; +static unsigned int ISA_PNP_WPORT = 0x20B; +static unsigned int ISA_PNP_WPORT_BIOS = 0; +static IO_ReadHandleObject *ISAPNP_PNP_READ_PORT = NULL; /* 0x200-0x3FF range */ +static IO_WriteHandleObject *ISAPNP_PNP_ADDRESS_PORT = NULL; /* 0x279 */ +static IO_WriteHandleObject *ISAPNP_PNP_DATA_PORT = NULL; /* 0xA79 */ +static IO_WriteHandleObject *BOCHS_PORT_E9 = NULL; +//static unsigned char ISA_PNP_CUR_CSN = 0; +static unsigned char ISA_PNP_CUR_ADDR = 0; +static unsigned char ISA_PNP_CUR_STATE = 0; +enum { + ISA_PNP_WAIT_FOR_KEY=0, + ISA_PNP_SLEEP, + ISA_PNP_ISOLATE, + ISA_PNP_CONFIG +}; + +const unsigned char isa_pnp_init_keystring[32] = { + 0x6A,0xB5,0xDA,0xED,0xF6,0xFB,0x7D,0xBE, + 0xDF,0x6F,0x37,0x1B,0x0D,0x86,0xC3,0x61, + 0xB0,0x58,0x2C,0x16,0x8B,0x45,0xA2,0xD1, + 0xE8,0x74,0x3A,0x9D,0xCE,0xE7,0x73,0x39 +}; + +static RealPt INT15_apm_pmentry=0; +static unsigned char ISA_PNP_KEYMATCH=0; +static Bits other_memsystems=0; +static bool apm_realmode_connected = false; +void CMOS_SetRegister(Bitu regNr, Bit8u val); //For setting equipment word +bool enable_integration_device_pnp=false; +bool enable_integration_device=false; +bool ISAPNPBIOS=false; +bool APMBIOS=false; +bool APMBIOS_pnp=false; +bool APMBIOS_allow_realmode=false; +bool APMBIOS_allow_prot16=false; +bool APMBIOS_allow_prot32=false; +int APMBIOS_connect_mode=0; + +enum { + APMBIOS_CONNECT_REAL=0, + APMBIOS_CONNECT_PROT16, + APMBIOS_CONNECT_PROT32 +}; + +unsigned int APMBIOS_connected_already_err() { + switch (APMBIOS_connect_mode) { + case APMBIOS_CONNECT_REAL: return 0x02; + case APMBIOS_CONNECT_PROT16: return 0x05; + case APMBIOS_CONNECT_PROT32: return 0x07; + } + + return 0x00; +} + +ISAPnPDevice::ISAPnPDevice() { + CSN = 0; + logical_device = 0; + memset(ident,0,sizeof(ident)); + ident_bp = 0; + ident_2nd = 0; + resource_data_len = 0; + resource_data_pos = 0; + resource_data = NULL; + resource_ident = 0; + alloc_res = NULL; + alloc_write = 0; + alloc_sz = 0; +} + +bool ISAPnPDevice::alloc(size_t sz) { + if (sz == alloc_sz) + return true; + + if (alloc_res == resource_data) { + resource_data_len = 0; + resource_data_pos = 0; + resource_data = NULL; + } + if (alloc_res != NULL) + delete[] alloc_res; + + alloc_res = NULL; + alloc_write = 0; + alloc_sz = 0; + + if (sz == 0) + return true; + if (sz > 65536) + return false; + + alloc_res = new unsigned char[sz]; + if (alloc_res == NULL) return false; + memset(alloc_res,0xFF,sz); + alloc_sz = sz; + return true; +} + +ISAPnPDevice::~ISAPnPDevice() { + alloc(0); +} + +void ISAPnPDevice::begin_write_res() { + if (alloc_res == NULL) return; + + resource_data_pos = 0; + resource_data_len = 0; + resource_data = NULL; + alloc_write = 0; +} + +void ISAPnPDevice::write_byte(const unsigned char c) { + if (alloc_res == NULL || alloc_write >= alloc_sz) return; + alloc_res[alloc_write++] = c; +} + +void ISAPnPDevice::write_begin_SMALLTAG(const ISAPnPDevice::SmallTags stag,unsigned char len) { + if (len >= 8 || (unsigned int)stag >= 0x10) return; + write_byte(((unsigned char)stag << 3) + len); +} + +void ISAPnPDevice::write_begin_LARGETAG(const ISAPnPDevice::LargeTags stag,unsigned int len) { + if (len >= 4096) return; + write_byte(0x80 + ((unsigned char)stag)); + write_byte(len & 0xFF); + write_byte(len >> 8); +} + +void ISAPnPDevice::write_Device_ID(const char c1,const char c2,const char c3,const char c4,const char c5,const char c6,const char c7) { + write_byte((((unsigned char)c1 & 0x1FU) << 2) + (((unsigned char)c2 & 0x18U) >> 3)); + write_byte((((unsigned char)c2 & 0x07U) << 5) + (((unsigned char)c3 & 0x1FU) )); + write_byte((((unsigned char)c4 & 0x0FU) << 4) + (((unsigned char)c5 & 0x0FU) )); + write_byte((((unsigned char)c6 & 0x0FU) << 4) + (((unsigned char)c7 & 0x0FU) )); +} + +void ISAPnPDevice::write_Logical_Device_ID(const char c1,const char c2,const char c3,const char c4,const char c5,const char c6,const char c7) { + write_begin_SMALLTAG(SmallTags::LogicalDeviceID,5); + write_Device_ID(c1,c2,c3,c4,c5,c6,c7); + write_byte(0x00); +} + +void ISAPnPDevice::write_Compatible_Device_ID(const char c1,const char c2,const char c3,const char c4,const char c5,const char c6,const char c7) { + write_begin_SMALLTAG(SmallTags::CompatibleDeviceID,4); + write_Device_ID(c1,c2,c3,c4,c5,c6,c7); +} + +void ISAPnPDevice::write_IRQ_Format(const uint16_t IRQ_mask,const unsigned char IRQ_signal_type) { + bool write_irq_info = (IRQ_signal_type != 0); + + write_begin_SMALLTAG(SmallTags::IRQFormat,write_irq_info?3:2); + write_byte(IRQ_mask & 0xFF); + write_byte(IRQ_mask >> 8); + if (write_irq_info) write_byte(((unsigned char)IRQ_signal_type & 0x0F)); +} + +void ISAPnPDevice::write_DMA_Format(const uint8_t DMA_mask,const unsigned char transfer_type_preference,const bool is_bus_master,const bool byte_mode,const bool word_mode,const unsigned char speed_supported) { + write_begin_SMALLTAG(SmallTags::DMAFormat,2); + write_byte(DMA_mask); + write_byte( + (transfer_type_preference & 0x03) | + (is_bus_master ? 0x04 : 0x00) | + (byte_mode ? 0x08 : 0x00) | + (word_mode ? 0x10 : 0x00) | + ((speed_supported & 3) << 5)); +} + +void ISAPnPDevice::write_IO_Port(const uint16_t min_port,const uint16_t max_port,const uint8_t count,const uint8_t alignment,const bool full16bitdecode) { + write_begin_SMALLTAG(SmallTags::IOPortDescriptor,7); + write_byte((full16bitdecode ? 0x01 : 0x00)); + write_byte(min_port & 0xFF); + write_byte(min_port >> 8); + write_byte(max_port & 0xFF); + write_byte(max_port >> 8); + write_byte(alignment); + write_byte(count); +} + +void ISAPnPDevice::write_Dependent_Function_Start(const ISAPnPDevice::DependentFunctionConfig cfg,const bool force) { + bool write_cfg_byte = force || (cfg != ISAPnPDevice::DependentFunctionConfig::AcceptableDependentConfiguration); + + write_begin_SMALLTAG(SmallTags::StartDependentFunctions,write_cfg_byte ? 1 : 0); + if (write_cfg_byte) write_byte((unsigned char)cfg); +} + +void ISAPnPDevice::write_End_Dependent_Functions() { + write_begin_SMALLTAG(SmallTags::EndDependentFunctions,0); +} + +void ISAPnPDevice::write_nstring(const char *str,const size_t l) { + (void)l; + + if (alloc_res == NULL || alloc_write >= alloc_sz) return; + + while (*str != 0 && alloc_write < alloc_sz) + alloc_res[alloc_write++] = (unsigned char)(*str++); +} + +void ISAPnPDevice::write_Identifier_String(const char *str) { + const size_t l = strlen(str); + if (l > 4096) return; + + write_begin_LARGETAG(LargeTags::IdentifierStringANSI,(unsigned int)l); + if (l != 0) write_nstring(str,l); +} + +void ISAPnPDevice::write_ISAPnP_version(unsigned char major,unsigned char minor,unsigned char vendor) { + write_begin_SMALLTAG(SmallTags::PlugAndPlayVersionNumber,2); + write_byte((major << 4) + minor); + write_byte(vendor); +} + +void ISAPnPDevice::write_END() { + unsigned char sum = 0; + size_t i; + + write_begin_SMALLTAG(SmallTags::EndTag,/*length*/1); + + for (i=0;i < alloc_write;i++) sum += alloc_res[i]; + write_byte((0x100 - sum) & 0xFF); +} + +void ISAPnPDevice::end_write_res() { + if (alloc_res == NULL) return; + + write_END(); + + if (alloc_write >= alloc_sz) LOG(LOG_MISC,LOG_WARN)("ISA PNP generation overflow"); + + resource_data_pos = 0; + resource_data_len = alloc_sz; // the device usually has a reason for allocating the fixed size it does + resource_data = alloc_res; + alloc_write = 0; +} + +void ISAPnPDevice::config(Bitu val) { + (void)val; +} + +void ISAPnPDevice::wakecsn(Bitu val) { + (void)val; + ident_bp = 0; + ident_2nd = 0; + resource_data_pos = 0; + resource_ident = 0; +} + +void ISAPnPDevice::select_logical_device(Bitu val) { + (void)val; +} + +void ISAPnPDevice::checksum_ident() { + unsigned char checksum = 0x6a,bit; + int i,j; + + for (i=0;i < 8;i++) { + for (j=0;j < 8;j++) { + bit = (ident[i] >> j) & 1; + checksum = ((((checksum ^ (checksum >> 1)) & 1) ^ bit) << 7) | (checksum >> 1); + } + } + + ident[8] = checksum; +} + +void ISAPnPDevice::on_pnp_key() { + resource_ident = 0; +} + +uint8_t ISAPnPDevice::read(Bitu addr) { + (void)addr; + return 0x00; +} + +void ISAPnPDevice::write(Bitu addr,Bitu val) { + (void)addr; + (void)val; +} + +#define MAX_ISA_PNP_DEVICES 64 +#define MAX_ISA_PNP_SYSDEVNODES 256 + +static ISAPnPDevice *ISA_PNP_selected = NULL; +static ISAPnPDevice *ISA_PNP_devs[MAX_ISA_PNP_DEVICES] = {NULL}; /* FIXME: free objects on shutdown */ +static Bitu ISA_PNP_devnext = 0; + +static const unsigned char ISAPnPIntegrationDevice_sysdev[] = { + ISAPNP_IO_RANGE( + 0x01, /* decodes 16-bit ISA addr */ + 0x28,0x28, /* min-max range I/O port */ + 0x04,0x04), /* align=4 length=4 */ + ISAPNP_END +}; + +class ISAPnPIntegrationDevice : public ISAPnPDevice { + public: + ISAPnPIntegrationDevice() : ISAPnPDevice() { + resource_ident = 0; + resource_data = (unsigned char*)ISAPnPIntegrationDevice_sysdev; + resource_data_len = sizeof(ISAPnPIntegrationDevice_sysdev); + host_writed(ident+0,ISAPNP_ID('D','O','S',0x1,0x2,0x3,0x4)); /* DOS1234 test device */ + host_writed(ident+4,0xFFFFFFFFUL); + checksum_ident(); + } +}; + +ISAPnPIntegrationDevice *isapnpigdevice = NULL; + +class ISAPNP_SysDevNode { +public: + ISAPNP_SysDevNode(const unsigned char *ir,size_t len,bool already_alloc=false) { + if (already_alloc) { + raw = (unsigned char*)ir; + raw_len = len; + own = false; + } + else { + if (len > 65535) E_Exit("ISAPNP_SysDevNode data too long"); + raw = new unsigned char[(size_t)len+1u]; + if (ir == NULL) E_Exit("ISAPNP_SysDevNode cannot allocate buffer"); + memcpy(raw,ir,(size_t)len); + raw_len = len; + raw[len] = 0; + own = true; + } + } + virtual ~ISAPNP_SysDevNode() { + if (own) delete[] raw; + } +public: + unsigned char* raw; + size_t raw_len; + bool own; +}; + +static ISAPNP_SysDevNode* ISAPNP_SysDevNodes[MAX_ISA_PNP_SYSDEVNODES] = {NULL}; +static Bitu ISAPNP_SysDevNodeLargest=0; +static Bitu ISAPNP_SysDevNodeCount=0; + +void ISA_PNP_FreeAllSysNodeDevs() { + Bitu i; + + for (i=0;i < MAX_ISA_PNP_SYSDEVNODES;i++) { + if (ISAPNP_SysDevNodes[i] != NULL) delete ISAPNP_SysDevNodes[i]; + ISAPNP_SysDevNodes[i] = NULL; + } + + ISAPNP_SysDevNodeLargest=0; + ISAPNP_SysDevNodeCount=0; +} + +void ISA_PNP_FreeAllDevs() { + Bitu i; + + for (i=0;i < MAX_ISA_PNP_DEVICES;i++) { + if (ISA_PNP_devs[i] != NULL) { + delete ISA_PNP_devs[i]; + ISA_PNP_devs[i] = NULL; + } + } + for (i=0;i < MAX_ISA_PNP_SYSDEVNODES;i++) { + if (ISAPNP_SysDevNodes[i] != NULL) delete ISAPNP_SysDevNodes[i]; + ISAPNP_SysDevNodes[i] = NULL; + } + + ISAPNP_SysDevNodeLargest=0; + ISAPNP_SysDevNodeCount=0; +} + +void ISA_PNP_devreg(ISAPnPDevice *x) { + if (ISA_PNP_devnext < MAX_ISA_PNP_DEVICES) { + if (ISA_PNP_WPORT_BIOS == 0) ISA_PNP_WPORT_BIOS = ISA_PNP_WPORT; + ISA_PNP_devs[ISA_PNP_devnext++] = x; + x->CSN = ISA_PNP_devnext; + } +} + +static Bitu isapnp_read_port(Bitu port,Bitu /*iolen*/) { + (void)port;//UNUSED + Bitu ret=0xff; + + switch (ISA_PNP_CUR_ADDR) { + case 0x01: /* serial isolation */ + if (ISA_PNP_selected && ISA_PNP_selected->CSN == 0) { + if (ISA_PNP_selected->ident_bp < 72) { + if (ISA_PNP_selected->ident[ISA_PNP_selected->ident_bp>>3] & (1 << (ISA_PNP_selected->ident_bp&7))) + ret = ISA_PNP_selected->ident_2nd ? 0xAA : 0x55; + else + ret = 0xFF; + + if (++ISA_PNP_selected->ident_2nd >= 2) { + ISA_PNP_selected->ident_2nd = 0; + ISA_PNP_selected->ident_bp++; + } + } + } + else { + ret = 0xFF; + } + break; + case 0x04: /* read resource data */ + if (ISA_PNP_selected) { + if (ISA_PNP_selected->resource_ident < 9) + ret = ISA_PNP_selected->ident[ISA_PNP_selected->resource_ident++]; + else { + /* real-world hardware testing shows that devices act as if there was some fixed block of ROM, + * that repeats every 128, 256, 512, or 1024 bytes if you just blindly read from this port. */ + if (ISA_PNP_selected->resource_data_pos < ISA_PNP_selected->resource_data_len) + ret = ISA_PNP_selected->resource_data[ISA_PNP_selected->resource_data_pos++]; + + /* that means that if you read enough bytes the ROM loops back to returning the ident */ + if (ISA_PNP_selected->resource_data_pos >= ISA_PNP_selected->resource_data_len) { + ISA_PNP_selected->resource_data_pos = 0; + ISA_PNP_selected->resource_ident = 0; + } + } + } + break; + case 0x05: /* read resource status */ + if (ISA_PNP_selected) { + /* real-world hardware testing shows that devices act as if there was some fixed block of ROM, + * that repeats every 128, 256, 512, or 1024 bytes if you just blindly read from this port. + * therefore, there's always a byte to return. */ + ret = 0x01; /* TODO: simulate hardware slowness */ + } + break; + case 0x06: /* card select number */ + if (ISA_PNP_selected) ret = ISA_PNP_selected->CSN; + break; + case 0x07: /* logical device number */ + if (ISA_PNP_selected) ret = ISA_PNP_selected->logical_device; + break; + default: /* pass the rest down to the class */ + if (ISA_PNP_selected) ret = ISA_PNP_selected->read(ISA_PNP_CUR_ADDR); + break; + } + +// if (1) LOG_MSG("PnP read(%02X) = %02X\n",ISA_PNP_CUR_ADDR,ret); + return ret; +} + +void isapnp_write_port(Bitu port,Bitu val,Bitu /*iolen*/) { + Bitu i; + + if (port == 0x279) { +// if (1) LOG_MSG("PnP addr(%02X)\n",val); + if (val == isa_pnp_init_keystring[ISA_PNP_KEYMATCH]) { + if (++ISA_PNP_KEYMATCH == 32) { +// LOG_MSG("ISA PnP key -> going to sleep\n"); + ISA_PNP_CUR_STATE = ISA_PNP_SLEEP; + ISA_PNP_KEYMATCH = 0; + for (i=0;i < MAX_ISA_PNP_DEVICES;i++) { + if (ISA_PNP_devs[i] != NULL) { + ISA_PNP_devs[i]->on_pnp_key(); + } + } + } + } + else { + ISA_PNP_KEYMATCH = 0; + } + + ISA_PNP_CUR_ADDR = val; + } + else if (port == 0xA79) { +// if (1) LOG_MSG("PnP write(%02X) = %02X\n",ISA_PNP_CUR_ADDR,val); + switch (ISA_PNP_CUR_ADDR) { + case 0x00: { /* RD_DATA */ + unsigned int np = ((val & 0xFF) << 2) | 3; + if (np != ISA_PNP_WPORT) { + if (ISAPNP_PNP_READ_PORT != NULL) { + ISAPNP_PNP_READ_PORT = NULL; + delete ISAPNP_PNP_READ_PORT; + } + + if (np >= 0x200 && np <= 0x3FF) { /* allowable port I/O range according to spec */ + LOG_MSG("PNP OS changed I/O read port to 0x%03X (from 0x%03X)\n",np,ISA_PNP_WPORT); + + ISA_PNP_WPORT = np; + ISAPNP_PNP_READ_PORT = new IO_ReadHandleObject; + ISAPNP_PNP_READ_PORT->Install(ISA_PNP_WPORT,isapnp_read_port,IO_MB); + } + else { + LOG_MSG("PNP OS I/O read port disabled\n"); + + ISA_PNP_WPORT = 0; + } + + if (ISA_PNP_selected != NULL) { + ISA_PNP_selected->ident_bp = 0; + ISA_PNP_selected->ident_2nd = 0; + ISA_PNP_selected->resource_data_pos = 0; + } + } + } break; + case 0x02: /* config control */ + if (val & 4) { + /* ALL CARDS RESET CSN to 0 */ + for (i=0;i < MAX_ISA_PNP_DEVICES;i++) { + if (ISA_PNP_devs[i] != NULL) { + ISA_PNP_devs[i]->CSN = 0; + } + } + } + if (val & 2) ISA_PNP_CUR_STATE = ISA_PNP_WAIT_FOR_KEY; + if ((val & 1) && ISA_PNP_selected) ISA_PNP_selected->config(val); + for (i=0;i < MAX_ISA_PNP_DEVICES;i++) { + if (ISA_PNP_devs[i] != NULL) { + ISA_PNP_devs[i]->ident_bp = 0; + ISA_PNP_devs[i]->ident_2nd = 0; + ISA_PNP_devs[i]->resource_data_pos = 0; + } + } + break; + case 0x03: { /* wake[CSN] */ + ISA_PNP_selected = NULL; + for (i=0;ISA_PNP_selected == NULL && i < MAX_ISA_PNP_DEVICES;i++) { + if (ISA_PNP_devs[i] == NULL) + continue; + if (ISA_PNP_devs[i]->CSN == val) { + ISA_PNP_selected = ISA_PNP_devs[i]; + ISA_PNP_selected->wakecsn(val); + } + } + if (val == 0) + ISA_PNP_CUR_STATE = ISA_PNP_ISOLATE; + else + ISA_PNP_CUR_STATE = ISA_PNP_CONFIG; + } break; + case 0x06: /* card select number */ + if (ISA_PNP_selected) ISA_PNP_selected->CSN = val; + break; + case 0x07: /* logical device number */ + if (ISA_PNP_selected) ISA_PNP_selected->select_logical_device(val); + break; + default: /* pass the rest down to the class */ + if (ISA_PNP_selected) ISA_PNP_selected->write(ISA_PNP_CUR_ADDR,val); + break; + } + } +} + +static Bitu INT15_Handler(void); + +// FIXME: This initializes both APM BIOS and ISA PNP emulation! +// Need to separate APM BIOS init from ISA PNP init from ISA PNP BIOS init! +// It might also be appropriate to move this into the BIOS init sequence. +void ISAPNP_Cfg_Reset(Section *sec) { + (void)sec;//UNUSED + Section_prop * section=static_cast(control->GetSection("cpu")); + + LOG(LOG_MISC,LOG_DEBUG)("Initializing ISA PnP emulation"); + + enable_integration_device = section->Get_bool("integration device"); + enable_integration_device_pnp = section->Get_bool("integration device pnp"); + ISAPNPBIOS = section->Get_bool("isapnpbios"); + APMBIOS = section->Get_bool("apmbios"); + APMBIOS_pnp = section->Get_bool("apmbios pnp"); + APMBIOS_allow_realmode = section->Get_bool("apmbios allow realmode"); + APMBIOS_allow_prot16 = section->Get_bool("apmbios allow 16-bit protected mode"); + APMBIOS_allow_prot32 = section->Get_bool("apmbios allow 32-bit protected mode"); + + std::string apmbiosver = section->Get_string("apmbios version"); + + /* PC-98 does not have the IBM PC/AT APM BIOS interface */ + if (IS_PC98_ARCH) { + APMBIOS = false; + APMBIOS_pnp = false; + } + + if (apmbiosver == "1.0") + APM_BIOS_minor_version = 0; + else if (apmbiosver == "1.1") + APM_BIOS_minor_version = 1; + else if (apmbiosver == "1.2") + APM_BIOS_minor_version = 2; + else//auto + APM_BIOS_minor_version = 2; + + /* PC-98 does not have APM. + * I *think* it has Plug & Play, but probably different from ISA PnP and specific to the C-Bus interface, + * which I have no information on at this time --J.C. */ + if (IS_PC98_ARCH) + return; + + LOG(LOG_MISC,LOG_DEBUG)("APM BIOS allow: real=%u pm16=%u pm32=%u version=1.%u", + APMBIOS_allow_realmode, + APMBIOS_allow_prot16, + APMBIOS_allow_prot32, + APM_BIOS_minor_version); + + if (APMBIOS && (APMBIOS_allow_prot16 || APMBIOS_allow_prot32) && INT15_apm_pmentry == 0) { + Bitu cb,base; + + /* NTS: This is... kind of a terrible hack. It basically tricks Windows into executing our + * INT 15h handler as if the APM entry point. Except that instead of an actual INT 15h + * triggering the callback, a FAR CALL triggers the callback instead (CB_RETF not CB_IRET). */ + /* TODO: We should really consider moving the APM BIOS code in INT15_Handler() out into it's + * own function, then having the INT15_Handler() call it as well as directing this callback + * directly to it. If you think about it, this hack also lets the "APM entry point" invoke + * other arbitrary INT 15h calls which is not valid. */ + + cb = CALLBACK_Allocate(); + INT15_apm_pmentry = CALLBACK_RealPointer(cb); + LOG_MSG("Allocated APM BIOS pm entry point at %04x:%04x\n",INT15_apm_pmentry>>16,INT15_apm_pmentry&0xFFFF); + CALLBACK_Setup(cb,INT15_Handler,CB_RETF,"APM BIOS protected mode entry point"); + + /* NTS: Actually INT15_Handler is written to act like an interrupt (IRETF) type callback. + * Prior versions hacked this into something that responds by CB_RETF, however some + * poking around reveals that CALLBACK_SCF and friends still assume an interrupt + * stack, thus, the cause of random crashes in Windows was simply that we were + * flipping flag bits in the middle of the return address on the stack. The other + * source of random crashes is that the CF/ZF manipulation in INT 15h wasn't making + * it's way back to Windows, meaning that when APM BIOS emulation intended to return + * an error (by setting CF), Windows didn't get the memo (CF wasn't set on return) + * and acted as if the call succeeded, or worse, CF happened to be set on entry and + * was never cleared by APM BIOS emulation. + * + * So what we need is: + * + * PUSHF ; put flags in right place + * PUSH BP ; dummy FAR pointer + * PUSH BP ; again + * + * POP BP ; drop it + * POP BP ; drop it + * POPF + * RETF + * + * Then CALLBACK_SCF can work normally this way. + * + * NTS: We *still* need to separate APM BIOS calls from the general INT 15H emulation though... */ + base = Real2Phys(INT15_apm_pmentry); + LOG_MSG("Writing code to %05x\n",(unsigned int)base); + + phys_writeb(base+0x00,0x9C); /* pushf */ + phys_writeb(base+0x01,0x55); /* push (e)bp */ + phys_writeb(base+0x02,0x55); /* push (e)bp */ + + phys_writeb(base+0x03,(Bit8u)0xFE); //GRP 4 + phys_writeb(base+0x04,(Bit8u)0x38); //Extra Callback instruction + phys_writew(base+0x05,(Bit16u)cb); //The immediate word + + phys_writeb(base+0x07,0x5D); /* pop (e)bp */ + phys_writeb(base+0x08,0x5D); /* pop (e)bp */ + phys_writeb(base+0x09,0x9D); /* popf */ + phys_writeb(base+0x0A,0xCB); /* retf */ + } +} + +void ISAPNP_Cfg_Init() { + AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(ISAPNP_Cfg_Reset)); +} + +/* the PnP callback registered two entry points. One for real, one for protected mode. */ +static Bitu PNPentry_real,PNPentry_prot; + +static bool ISAPNP_Verify_BiosSelector(Bitu seg) { + if (!cpu.pmode || (reg_flags & FLAG_VM)) { + return (seg == 0xF000); + } else if (seg == 0) + return 0; + else { +#if 1 + /* FIXME: Always return true. But figure out how to ask DOSBox the linear->phys + mapping to determine whether the segment's base address maps to 0xF0000. + In the meantime disabling this check makes PnP BIOS emulation work with + Windows 95 OSR2 which appears to give us a segment mapped to a virtual + address rather than linearly mapped to 0xF0000 as Windows 95 original + did. */ + return true; +#else + Descriptor desc; + cpu.gdt.GetDescriptor(seg,desc); + + /* TODO: Check desc.Type() to make sure it's a writeable data segment */ + return (desc.GetBase() == 0xF0000); +#endif + } +} + +static bool ISAPNP_CPU_ProtMode() { + if (!cpu.pmode || (reg_flags & FLAG_VM)) + return 0; + + return 1; +} + +static Bitu ISAPNP_xlate_address(Bitu far_ptr) { + if (!cpu.pmode || (reg_flags & FLAG_VM)) + return Real2Phys(far_ptr); + else { + Descriptor desc; + cpu.gdt.GetDescriptor(far_ptr >> 16,desc); + + /* TODO: Check desc.Type() to make sure it's a writeable data segment */ + return (desc.GetBase() + (far_ptr & 0xFFFF)); + } +} + +static const unsigned char ISAPNP_sysdev_Keyboard[] = { + ISAPNP_SYSDEV_HEADER( + ISAPNP_ID('P','N','P',0x0,0x3,0x0,0x3), /* PNP0303 IBM Enhanced 101/102 key with PS/2 */ + ISAPNP_TYPE(0x09,0x00,0x00), /* type: input, keyboard */ + 0x0001 | 0x0002), /* can't disable, can't configure */ + /*----------allocated--------*/ + ISAPNP_IO_RANGE( + 0x01, /* decodes 16-bit ISA addr */ + 0x60,0x60, /* min-max range I/O port */ + 0x01,0x01), /* align=1 length=1 */ + ISAPNP_IO_RANGE( + 0x01, /* decodes 16-bit ISA addr */ + 0x64,0x64, /* min-max range I/O port */ + 0x01,0x01), /* align=1 length=1 */ + ISAPNP_IRQ_SINGLE( + 1, /* IRQ 1 */ + 0x09), /* HTE=1 LTL=1 */ + ISAPNP_END, + /*----------possible--------*/ + ISAPNP_END, + /*----------compatible--------*/ + ISAPNP_END +}; + +static const unsigned char ISAPNP_sysdev_Mouse[] = { + ISAPNP_SYSDEV_HEADER( + ISAPNP_ID('P','N','P',0x0,0xF,0x0,0xE), /* PNP0F0E Microsoft compatible PS/2 */ + ISAPNP_TYPE(0x09,0x02,0x00), /* type: input, keyboard */ + 0x0001 | 0x0002), /* can't disable, can't configure */ + /*----------allocated--------*/ + ISAPNP_IRQ_SINGLE( + 12, /* IRQ 12 */ + 0x09), /* HTE=1 LTL=1 */ + ISAPNP_END, + /*----------possible--------*/ + ISAPNP_END, + /*----------compatible--------*/ + ISAPNP_END +}; + +static const unsigned char ISAPNP_sysdev_DMA_Controller[] = { + ISAPNP_SYSDEV_HEADER( + ISAPNP_ID('P','N','P',0x0,0x2,0x0,0x0), /* PNP0200 AT DMA controller */ + ISAPNP_TYPE(0x08,0x01,0x00), /* type: input, keyboard */ + 0x0001 | 0x0002), /* can't disable, can't configure */ + /*----------allocated--------*/ + ISAPNP_IO_RANGE( + 0x01, /* decodes 16-bit ISA addr */ + 0x00,0x00, /* min-max range I/O port (DMA channels 0-3) */ + 0x10,0x10), /* align=16 length=16 */ + ISAPNP_IO_RANGE( + 0x01, /* decodes 16-bit ISA addr */ + 0x81,0x81, /* min-max range I/O port (DMA page registers) */ + 0x01,0x0F), /* align=1 length=15 */ + ISAPNP_IO_RANGE( + 0x01, /* decodes 16-bit ISA addr */ + 0xC0,0xC0, /* min-max range I/O port (AT DMA channels 4-7) */ + 0x20,0x20), /* align=32 length=32 */ + ISAPNP_DMA_SINGLE( + 4, /* DMA 4 */ + 0x01), /* 8/16-bit transfers, compatible speed */ + ISAPNP_END, + /*----------possible--------*/ + ISAPNP_END, + /*----------compatible--------*/ + ISAPNP_END +}; + +static const unsigned char ISAPNP_sysdev_PIC[] = { + ISAPNP_SYSDEV_HEADER( + ISAPNP_ID('P','N','P',0x0,0x0,0x0,0x0), /* PNP0000 Interrupt controller */ + ISAPNP_TYPE(0x08,0x00,0x01), /* type: ISA interrupt controller */ + 0x0001 | 0x0002), /* can't disable, can't configure */ + /*----------allocated--------*/ + ISAPNP_IO_RANGE( + 0x01, /* decodes 16-bit ISA addr */ + 0x20,0x20, /* min-max range I/O port */ + 0x01,0x02), /* align=1 length=2 */ + ISAPNP_IO_RANGE( + 0x01, /* decodes 16-bit ISA addr */ + 0xA0,0xA0, /* min-max range I/O port */ + 0x01,0x02), /* align=1 length=2 */ + ISAPNP_IRQ_SINGLE( + 2, /* IRQ 2 */ + 0x09), /* HTE=1 LTL=1 */ + ISAPNP_END, + /*----------possible--------*/ + ISAPNP_END, + /*----------compatible--------*/ + ISAPNP_END +}; + +static const unsigned char ISAPNP_sysdev_Timer[] = { + ISAPNP_SYSDEV_HEADER( + ISAPNP_ID('P','N','P',0x0,0x1,0x0,0x0), /* PNP0100 Timer */ + ISAPNP_TYPE(0x08,0x02,0x01), /* type: ISA timer */ + 0x0001 | 0x0002), /* can't disable, can't configure */ + /*----------allocated--------*/ + ISAPNP_IO_RANGE( + 0x01, /* decodes 16-bit ISA addr */ + 0x40,0x40, /* min-max range I/O port */ + 0x04,0x04), /* align=4 length=4 */ + ISAPNP_IRQ_SINGLE( + 0, /* IRQ 0 */ + 0x09), /* HTE=1 LTL=1 */ + ISAPNP_END, + /*----------possible--------*/ + ISAPNP_END, + /*----------compatible--------*/ + ISAPNP_END +}; + +static const unsigned char ISAPNP_sysdev_RTC[] = { + ISAPNP_SYSDEV_HEADER( + ISAPNP_ID('P','N','P',0x0,0xB,0x0,0x0), /* PNP0B00 Real-time clock */ + ISAPNP_TYPE(0x08,0x03,0x01), /* type: ISA real-time clock */ + 0x0001 | 0x0002), /* can't disable, can't configure */ + /*----------allocated--------*/ + ISAPNP_IO_RANGE( + 0x01, /* decodes 16-bit ISA addr */ + 0x70,0x70, /* min-max range I/O port */ + 0x01,0x02), /* align=1 length=2 */ + ISAPNP_IRQ_SINGLE( + 8, /* IRQ 8 */ + 0x09), /* HTE=1 LTL=1 */ + ISAPNP_END, + /*----------possible--------*/ + ISAPNP_END, + /*----------compatible--------*/ + ISAPNP_END +}; + +static const unsigned char ISAPNP_sysdev_PC_Speaker[] = { + ISAPNP_SYSDEV_HEADER( + ISAPNP_ID('P','N','P',0x0,0x8,0x0,0x0), /* PNP0800 PC speaker */ + ISAPNP_TYPE(0x04,0x01,0x00), /* type: PC speaker */ + 0x0001 | 0x0002), /* can't disable, can't configure */ + /*----------allocated--------*/ + ISAPNP_IO_RANGE( + 0x01, /* decodes 16-bit ISA addr */ + 0x61,0x61, /* min-max range I/O port */ + 0x01,0x01), /* align=1 length=1 */ + ISAPNP_END, + /*----------possible--------*/ + ISAPNP_END, + /*----------compatible--------*/ + ISAPNP_END +}; + +static const unsigned char ISAPNP_sysdev_Numeric_Coprocessor[] = { + ISAPNP_SYSDEV_HEADER( + ISAPNP_ID('P','N','P',0x0,0xC,0x0,0x4), /* PNP0C04 Numeric Coprocessor */ + ISAPNP_TYPE(0x0B,0x80,0x00), /* type: FPU */ + 0x0001 | 0x0002), /* can't disable, can't configure */ + /*----------allocated--------*/ + ISAPNP_IO_RANGE( + 0x01, /* decodes 16-bit ISA addr */ + 0xF0,0xF0, /* min-max range I/O port */ + 0x10,0x10), /* align=16 length=16 */ + ISAPNP_IRQ_SINGLE( + 13, /* IRQ 13 */ + 0x09), /* HTE=1 LTL=1 */ + ISAPNP_END, + /*----------possible--------*/ + ISAPNP_END, + /*----------compatible--------*/ + ISAPNP_END +}; + +static const unsigned char ISAPNP_sysdev_System_Board[] = { + ISAPNP_SYSDEV_HEADER( + ISAPNP_ID('P','N','P',0x0,0xC,0x0,0x1), /* PNP0C01 System board */ + ISAPNP_TYPE(0x08,0x80,0x00), /* type: System peripheral, Other */ + 0x0001 | 0x0002), /* can't disable, can't configure */ + /*----------allocated--------*/ + ISAPNP_IO_RANGE( + 0x01, /* decodes 16-bit ISA addr */ + 0x24,0x24, /* min-max range I/O port */ + 0x04,0x04), /* align=4 length=4 */ + ISAPNP_END, + /*----------possible--------*/ + ISAPNP_END, + /*----------compatible--------*/ + ISAPNP_END +}; + +/* NTS: If some of my late 1990's laptops are any indication, this resource list can be used + * as a hint that the motherboard supports Intel EISA/PCI controller DMA registers that + * allow ISA DMA to extend to 32-bit addresses instead of being limited to 24-bit */ +static const unsigned char ISAPNP_sysdev_General_ISAPNP[] = { + ISAPNP_SYSDEV_HEADER( + ISAPNP_ID('P','N','P',0x0,0xC,0x0,0x2), /* PNP0C02 General ID for reserving resources */ + ISAPNP_TYPE(0x08,0x80,0x00), /* type: System peripheral, Other */ + 0x0001 | 0x0002), /* can't disable, can't configure */ + /*----------allocated--------*/ + ISAPNP_IO_RANGE( + 0x01, /* decodes 16-bit ISA addr */ + 0x208,0x208, /* min-max range I/O port */ + 0x04,0x04), /* align=4 length=4 */ + ISAPNP_END, + /*----------possible--------*/ + ISAPNP_END, + /*----------compatible--------*/ + ISAPNP_END +}; + +/* PnP system entry to tell Windows 95 the obvious: That there's an ISA bus present */ +/* NTS: Examination of some old laptops of mine shows that these devices do not list any resources, + * or at least, an old Toshiba of mine lists the PCI registers 0xCF8-0xCFF as motherboard resources + * and defines no resources for the PCI Bus PnP device. */ +static const unsigned char ISAPNP_sysdev_ISA_BUS[] = { + ISAPNP_SYSDEV_HEADER( + ISAPNP_ID('P','N','P',0x0,0xA,0x0,0x0), /* PNP0A00 ISA Bus */ + ISAPNP_TYPE(0x06,0x04,0x00), /* type: System device, peripheral bus */ + 0x0001 | 0x0002), /* can't disable, can't configure */ + /*----------allocated--------*/ + ISAPNP_END, + /*----------possible--------*/ + ISAPNP_END, + /*----------compatible--------*/ + ISAPNP_END +}; + +/* PnP system entry to tell Windows 95 the obvious: That there's a PCI bus present */ +static const unsigned char ISAPNP_sysdev_PCI_BUS[] = { + ISAPNP_SYSDEV_HEADER( + ISAPNP_ID('P','N','P',0x0,0xA,0x0,0x3), /* PNP0A03 PCI Bus */ + ISAPNP_TYPE(0x06,0x04,0x00), /* type: System device, peripheral bus */ + 0x0001 | 0x0002), /* can't disable, can't configure */ + /*----------allocated--------*/ + ISAPNP_END, + /*----------possible--------*/ + ISAPNP_END, + /*----------compatible--------*/ + ISAPNP_END +}; + +/* to help convince Windows 95 that the APM BIOS is present */ +static const unsigned char ISAPNP_sysdev_APM_BIOS[] = { + ISAPNP_SYSDEV_HEADER( + ISAPNP_ID('P','N','P',0x0,0xC,0x0,0x5), /* PNP0C05 APM BIOS */ + ISAPNP_TYPE(0x08,0x80,0x00), /* type: FIXME is this right?? I can't find any examples or documentation */ + 0x0001 | 0x0002), /* can't disable, can't configure */ + /*----------allocated--------*/ + ISAPNP_END, + /*----------possible--------*/ + ISAPNP_END, + /*----------compatible--------*/ + ISAPNP_END +}; + +bool ISAPNP_RegisterSysDev(const unsigned char *raw,Bitu len,bool already) { + if (ISAPNP_SysDevNodeCount >= MAX_ISA_PNP_SYSDEVNODES) + return false; + + ISAPNP_SysDevNodes[ISAPNP_SysDevNodeCount] = new ISAPNP_SysDevNode(raw,len,already); + if (ISAPNP_SysDevNodes[ISAPNP_SysDevNodeCount] == NULL) + return false; + + ISAPNP_SysDevNodeCount++; + if (ISAPNP_SysDevNodeLargest < (len+3)) + ISAPNP_SysDevNodeLargest = len+3; + + return true; +} + +/* ISA PnP function calls have their parameters stored on the stack "C" __cdecl style. Parameters + * are either int, long, or FAR pointers. Like __cdecl an assembly language implementation pushes + * the function arguments on the stack BACKWARDS */ +static Bitu ISAPNP_Handler(bool protmode /* called from protected mode interface == true */) { + Bitu arg; + Bitu func,BiosSelector; + + /* I like how the ISA PnP spec says that the 16-bit entry points (real and protected) are given 16-bit data segments + * which implies that all segments involved might as well be 16-bit. + * + * Right? + * + * Well, guess what Windows 95 gives us when calling this entry point: + * + * Segment SS = DS = 0x30 base=0 limit=0xFFFFFFFF + * SS:SP = 0x30:0xC138BADF or something like that from within BIOS.VXD + * + * Yeah... for a 16-bit code segment call. Right. Typical Microsoft. >:( + * + * This might also explain why my early experiments with Bochs always had the perpetual + * APM BIOS that never worked but was always detected. + * + * ------------------------------------------------------------------------ + * Windows 95 OSR2: + * + * Windows 95 OSR2 however uses a 16-bit stack (where the stack segment is based somewhere + * around 0xC1xxxxxx), all we have to do to correctly access it is work through the page tables. + * This is within spec, but now Microsoft sends us a data segment that is based at virtual address + * 0xC2xxxxxx, which is why I had to disable the "verify selector" routine */ + arg = SegPhys(ss) + (reg_esp&cpu.stack.mask) + (2*2); /* entry point (real and protected) is 16-bit, expected to RETF (skip CS:IP) */ + + if (protmode != ISAPNP_CPU_ProtMode()) { + //LOG_MSG("ISA PnP %s entry point called from %s. On real BIOSes this would CRASH\n",protmode ? "Protected mode" : "Real mode", + // ISAPNP_CPU_ProtMode() ? "Protected mode" : "Real mode"); + reg_ax = 0x84;/* BAD_PARAMETER */ + return 0; + } + + func = mem_readw(arg); +// LOG_MSG("PnP prot=%u DS=%04x (base=0x%08lx) SS:ESP=%04x:%04x (base=0x%08lx phys=0x%08lx) function=0x%04x\n", +// (unsigned int)protmode,(unsigned int)SegValue(ds),(unsigned long)SegPhys(ds), +// (unsigned int)SegValue(ss),(unsigned int)reg_esp,(unsigned long)SegPhys(ss), +// (unsigned long)arg,(unsigned int)func); + + /* every function takes the form + * + * int __cdecl FAR (*entrypoint)(int Function...); + * + * so the first argument on the stack is an int that we read to determine what the caller is asking + * + * Dont forget in the real-mode world: + * sizeof(int) == 16 bits + * sizeof(long) == 32 bits + */ + switch (func) { + case 0: { /* Get Number of System Nodes */ + /* int __cdecl FAR (*entrypoint)(int Function,unsigned char FAR *NumNodes,unsigned int FAR *NodeSize,unsigned int BiosSelector); + * ^ +0 ^ +2 ^ +6 ^ +10 = 12 */ + Bitu NumNodes_ptr = mem_readd(arg+2); + Bitu NodeSize_ptr = mem_readd(arg+6); + BiosSelector = mem_readw(arg+10); + + if (!ISAPNP_Verify_BiosSelector(BiosSelector)) + goto badBiosSelector; + + if (NumNodes_ptr != 0) mem_writeb(ISAPNP_xlate_address(NumNodes_ptr),ISAPNP_SysDevNodeCount); + if (NodeSize_ptr != 0) mem_writew(ISAPNP_xlate_address(NodeSize_ptr),ISAPNP_SysDevNodeLargest); + + reg_ax = 0x00;/* SUCCESS */ + } break; + case 1: { /* Get System Device Node */ + /* int __cdecl FAR (*entrypoint)(int Function,unsigned char FAR *Node,struct DEV_NODE FAR *devNodeBuffer,unsigned int Control,unsigned int BiosSelector); + * ^ +0 ^ +2 ^ +6 ^ +10 ^ +12 = 14 */ + Bitu Node_ptr = mem_readd(arg+2); + Bitu devNodeBuffer_ptr = mem_readd(arg+6); + Bitu Control = mem_readw(arg+10); + BiosSelector = mem_readw(arg+12); + unsigned char Node; + Bitu i=0; + + if (!ISAPNP_Verify_BiosSelector(BiosSelector)) + goto badBiosSelector; + + /* control bits 0-1 must be '01' or '10' but not '00' or '11' */ + if (Control == 0 || (Control&3) == 3) { + LOG_MSG("ISAPNP Get System Device Node: Invalid Control value 0x%04x\n",(int)Control); + reg_ax = 0x84;/* BAD_PARAMETER */ + break; + } + + devNodeBuffer_ptr = ISAPNP_xlate_address(devNodeBuffer_ptr); + Node_ptr = ISAPNP_xlate_address(Node_ptr); + Node = mem_readb(Node_ptr); + if (Node >= ISAPNP_SysDevNodeCount) { + LOG_MSG("ISAPNP Get System Device Node: Invalid Node 0x%02x (max 0x%04x)\n",(int)Node,(int)ISAPNP_SysDevNodeCount); + reg_ax = 0x84;/* BAD_PARAMETER */ + break; + } + + ISAPNP_SysDevNode *nd = ISAPNP_SysDevNodes[Node]; + + mem_writew(devNodeBuffer_ptr+0,(Bit16u)(nd->raw_len+3)); /* Length */ + mem_writeb(devNodeBuffer_ptr+2,Node); /* on most PnP BIOS implementations I've seen "handle" is set to the same value as Node */ + for (i=0;i < (Bitu)nd->raw_len;i++) + mem_writeb(devNodeBuffer_ptr+i+3,nd->raw[i]); + +// LOG_MSG("ISAPNP OS asked for Node 0x%02x\n",Node); + + if (++Node >= ISAPNP_SysDevNodeCount) Node = 0xFF; /* no more nodes */ + mem_writeb(Node_ptr,Node); + + reg_ax = 0x00;/* SUCCESS */ + } break; + case 4: { /* Send Message */ + /* int __cdecl FAR (*entrypoint)(int Function,unsigned int Message,unsigned int BiosSelector); + * ^ +0 ^ +2 ^ +4 = 6 */ + Bitu Message = mem_readw(arg+2); + BiosSelector = mem_readw(arg+4); + + if (!ISAPNP_Verify_BiosSelector(BiosSelector)) + goto badBiosSelector; + + switch (Message) { + case 0x41: /* POWER_OFF */ + LOG_MSG("Plug & Play OS requested power off.\n"); + throw 1; /* NTS: Based on the Reboot handler code, causes DOSBox to cleanly shutdown and exit */ + reg_ax = 0; + break; + case 0x42: /* PNP_OS_ACTIVE */ + LOG_MSG("Plug & Play OS reports itself active\n"); + reg_ax = 0; + break; + case 0x43: /* PNP_OS_INACTIVE */ + LOG_MSG("Plug & Play OS reports itself inactive\n"); + reg_ax = 0; + break; + default: + LOG_MSG("Unknown ISA PnP message 0x%04x\n",(int)Message); + reg_ax = 0x82;/* FUNCTION_NOT_SUPPORTED */ + break; + } + } break; + case 0x40: { /* Get PnP ISA configuration */ + /* int __cdecl FAR (*entrypoint)(int Function,unsigned char far *struct,unsigned int BiosSelector); + * ^ +0 ^ +2 ^ +6 = 8 */ + Bitu struct_ptr = mem_readd(arg+2); + BiosSelector = mem_readw(arg+6); + + if (!ISAPNP_Verify_BiosSelector(BiosSelector)) + goto badBiosSelector; + + /* struct isapnp_pnp_isa_cfg { + uint8_t revision; + uint8_t total_csn; + uint16_t isa_pnp_port; + uint16_t reserved; + }; */ + + if (struct_ptr != 0) { + Bitu ph = ISAPNP_xlate_address(struct_ptr); + mem_writeb(ph+0,0x01); /* ->revision = 0x01 */ + mem_writeb(ph+1,ISA_PNP_devnext); /* ->total_csn */ + mem_writew(ph+2,ISA_PNP_WPORT_BIOS); /* ->isa_pnp_port */ + mem_writew(ph+4,0); /* ->reserved */ + } + + reg_ax = 0x00;/* SUCCESS */ + } break; + default: + //LOG_MSG("Unsupported ISA PnP function 0x%04x\n",func); + reg_ax = 0x82;/* FUNCTION_NOT_SUPPORTED */ + break; + }; + + return 0; +badBiosSelector: + /* return an error. remind the user (possible developer) how lucky he is, a real + * BIOS implementation would CRASH when misused like this */ + LOG_MSG("ISA PnP function 0x%04x called with incorrect BiosSelector parameter 0x%04x\n",(int)func,(int)BiosSelector); + LOG_MSG(" > STACK %04X %04X %04X %04X %04X %04X %04X %04X\n", + mem_readw(arg), mem_readw(arg+2), mem_readw(arg+4), mem_readw(arg+6), + mem_readw(arg+8), mem_readw(arg+10), mem_readw(arg+12), mem_readw(arg+14)); + + reg_ax = 0x84;/* BAD_PARAMETER */ + return 0; +} + +static Bitu ISAPNP_Handler_PM(void) { + return ISAPNP_Handler(true); +} + +static Bitu ISAPNP_Handler_RM(void) { + return ISAPNP_Handler(false); +} + +static Bitu INT70_Handler(void) { + /* Acknowledge irq with cmos */ + IO_Write(0x70,0xc); + IO_Read(0x71); + if (mem_readb(BIOS_WAIT_FLAG_ACTIVE)) { + Bit32u count=mem_readd(BIOS_WAIT_FLAG_COUNT); + if (count>997) { + mem_writed(BIOS_WAIT_FLAG_COUNT,count-997); + } else { + mem_writed(BIOS_WAIT_FLAG_COUNT,0); + PhysPt where=Real2Phys(mem_readd(BIOS_WAIT_FLAG_POINTER)); + mem_writeb(where,mem_readb(where)|0x80); + mem_writeb(BIOS_WAIT_FLAG_ACTIVE,0); + mem_writed(BIOS_WAIT_FLAG_POINTER,RealMake(0,BIOS_WAIT_FLAG_TEMP)); + IO_Write(0x70,0xb); + IO_Write(0x71,IO_Read(0x71)&~0x40); + } + } + /* Signal EOI to both pics */ + IO_Write(0xa0,0x20); + IO_Write(0x20,0x20); + return 0; +} + +CALLBACK_HandlerObject* tandy_DAC_callback[2]; +static struct { + Bit16u port; + Bit8u irq; + Bit8u dma; +} tandy_sb; +static struct { + Bit16u port; + Bit8u irq; + Bit8u dma; +} tandy_dac; + +static bool Tandy_InitializeSB() { + /* see if soundblaster module available and at what port/IRQ/DMA */ + Bitu sbport, sbirq, sbdma; + if (SB_Get_Address(sbport, sbirq, sbdma)) { + tandy_sb.port=(Bit16u)(sbport&0xffff); + tandy_sb.irq =(Bit8u)(sbirq&0xff); + tandy_sb.dma =(Bit8u)(sbdma&0xff); + return true; + } else { + /* no soundblaster accessible, disable Tandy DAC */ + tandy_sb.port=0; + return false; + } +} + +static bool Tandy_InitializeTS() { + /* see if Tandy DAC module available and at what port/IRQ/DMA */ + Bitu tsport, tsirq, tsdma; + if (TS_Get_Address(tsport, tsirq, tsdma)) { + tandy_dac.port=(Bit16u)(tsport&0xffff); + tandy_dac.irq =(Bit8u)(tsirq&0xff); + tandy_dac.dma =(Bit8u)(tsdma&0xff); + return true; + } else { + /* no Tandy DAC accessible */ + tandy_dac.port=0; + return false; + } +} + +/* check if Tandy DAC is still playing */ +static bool Tandy_TransferInProgress(void) { + if (real_readw(0x40,0xd0)) return true; /* not yet done */ + if (real_readb(0x40,0xd4)==0xff) return false; /* still in init-state */ + + Bit8u tandy_dma = 1; + if (tandy_sb.port) tandy_dma = tandy_sb.dma; + else if (tandy_dac.port) tandy_dma = tandy_dac.dma; + + IO_Write(0x0c,0x00); + Bit16u datalen=(Bit8u)(IO_ReadB(tandy_dma*2u+1u)&0xffu); + datalen|=(IO_ReadB(tandy_dma*2u+1u)<<8u); + if (datalen==0xffff) return false; /* no DMA transfer */ + else if ((datalen<0x10) && (real_readb(0x40,0xd4)==0x0f) && (real_readw(0x40,0xd2)==0x1c)) { + /* stop already requested */ + return false; + } + return true; +} + +static void Tandy_SetupTransfer(PhysPt bufpt,bool isplayback) { + Bitu length=real_readw(0x40,0xd0); + if (length==0) return; /* nothing to do... */ + + if ((tandy_sb.port==0) && (tandy_dac.port==0)) return; + + Bit8u tandy_irq = 7; + if (tandy_sb.port) tandy_irq = tandy_sb.irq; + else if (tandy_dac.port) tandy_irq = tandy_dac.irq; + Bit8u tandy_irq_vector = tandy_irq; + if (tandy_irq_vector<8) tandy_irq_vector += 8; + else tandy_irq_vector += (0x70-8); + + /* revector IRQ-handler if necessary */ + RealPt current_irq=RealGetVec(tandy_irq_vector); + if (current_irq!=tandy_DAC_callback[0]->Get_RealPointer()) { + real_writed(0x40,0xd6,current_irq); + RealSetVec(tandy_irq_vector,tandy_DAC_callback[0]->Get_RealPointer()); + } + + Bit8u tandy_dma = 1; + if (tandy_sb.port) tandy_dma = tandy_sb.dma; + else if (tandy_dac.port) tandy_dma = tandy_dac.dma; + + if (tandy_sb.port) { + IO_Write(tandy_sb.port+0xcu,0xd0); /* stop DMA transfer */ + IO_Write(0x21,IO_Read(0x21)&(~(1u<>16u)&0xff); + IO_Write(tandy_dma*2u,(Bit8u)(bufpt&0xff)); + IO_Write(tandy_dma*2u,(Bit8u)((bufpt>>8u)&0xff)); + switch (tandy_dma) { + case 0: IO_Write(0x87,bufpage); break; + case 1: IO_Write(0x83,bufpage); break; + case 2: IO_Write(0x81,bufpage); break; + case 3: IO_Write(0x82,bufpage); break; + } + real_writeb(0x40,0xd4,bufpage); + + /* calculate transfer size (respects segment boundaries) */ + Bit32u tlength=length; + if (tlength+(bufpt&0xffff)>0x10000) tlength=0x10000-(bufpt&0xffff); + real_writew(0x40,0xd0,(Bit16u)(length-tlength)); /* remaining buffer length */ + tlength--; + + /* set transfer size */ + IO_Write(tandy_dma*2u+1u,(Bit8u)(tlength&0xffu)); + IO_Write(tandy_dma*2u+1u,(Bit8u)((tlength>>8u)&0xffu)); + + Bit16u delay=(Bit16u)(real_readw(0x40,0xd2)&0xfff); + Bit8u amplitude=(Bit8u)(((unsigned int)real_readw(0x40,0xd2)>>13u)&0x7u); + if (tandy_sb.port) { + IO_Write(0x0a,tandy_dma); /* enable DMA channel */ + /* set frequency */ + IO_Write(tandy_sb.port+0xcu,0x40); + IO_Write(tandy_sb.port+0xcu,256u - delay*100u/358u); + /* set playback type to 8bit */ + if (isplayback) IO_Write(tandy_sb.port+0xcu,0x14u); + else IO_Write(tandy_sb.port+0xcu,0x24u); + /* set transfer size */ + IO_Write(tandy_sb.port+0xcu,(Bit8u)(tlength&0xffu)); + IO_Write(tandy_sb.port+0xcu,(Bit8u)((tlength>>8)&0xffu)); + } else { + if (isplayback) IO_Write(tandy_dac.port,(IO_Read(tandy_dac.port)&0x7cu) | 0x03u); + else IO_Write(tandy_dac.port,(IO_Read(tandy_dac.port)&0x7cu) | 0x02u); + IO_Write(tandy_dac.port+2u,(Bit8u)(delay&0xffu)); + IO_Write(tandy_dac.port+3u,(Bit8u)((((unsigned int)delay>>8u)&0xfu) | ((unsigned int)amplitude<<5u))); + if (isplayback) IO_Write(tandy_dac.port,(IO_Read(tandy_dac.port)&0x7cu) | 0x1fu); + else IO_Write(tandy_dac.port,(IO_Read(tandy_dac.port)&0x7c) | 0x1e); + IO_Write(0x0a,tandy_dma); /* enable DMA channel */ + } + + if (!isplayback) { + /* mark transfer as recording operation */ + real_writew(0x40,0xd2,(Bit16u)(delay|0x1000)); + } +} + +static Bitu IRQ_TandyDAC(void) { + if (tandy_dac.port) { + IO_Read(tandy_dac.port); + } + if (real_readw(0x40,0xd0)) { /* play/record next buffer */ + /* acknowledge IRQ */ + IO_Write(0x20,0x20); + if (tandy_sb.port) { + IO_Read(tandy_sb.port+0xeu); + } + + /* buffer starts at the next page */ + Bit8u npage=real_readb(0x40,0xd4)+1u; + real_writeb(0x40,0xd4,npage); + + Bitu rb=real_readb(0x40,0xd3); + if (rb&0x10) { + /* start recording */ + real_writeb(0x40,0xd3,rb&0xefu); + Tandy_SetupTransfer((unsigned int)npage<<16u,false); + } else { + /* start playback */ + Tandy_SetupTransfer((unsigned int)npage<<16u,true); + } + } else { /* playing/recording is finished */ + Bit8u tandy_irq = 7u; + if (tandy_sb.port) tandy_irq = tandy_sb.irq; + else if (tandy_dac.port) tandy_irq = tandy_dac.irq; + Bit8u tandy_irq_vector = tandy_irq; + if (tandy_irq_vector<8u) tandy_irq_vector += 8u; + else tandy_irq_vector += (0x70u-8u); + + RealSetVec(tandy_irq_vector,real_readd(0x40,0xd6)); + + /* turn off speaker and acknowledge soundblaster IRQ */ + if (tandy_sb.port) { + IO_Write(tandy_sb.port+0xcu,0xd3u); + IO_Read(tandy_sb.port+0xeu); + } + + /* issue BIOS tandy sound device busy callout */ + SegSet16(cs, RealSeg(tandy_DAC_callback[1]->Get_RealPointer())); + reg_ip = RealOff(tandy_DAC_callback[1]->Get_RealPointer()); + } + return CBRET_NONE; +} + +static void TandyDAC_Handler(Bit8u tfunction) { + if ((!tandy_sb.port) && (!tandy_dac.port)) return; + switch (tfunction) { + case 0x81: /* Tandy sound system check */ + if (tandy_dac.port) { + reg_ax=tandy_dac.port; + } else { + reg_ax=0xc4; + } + CALLBACK_SCF(Tandy_TransferInProgress()); + break; + case 0x82: /* Tandy sound system start recording */ + case 0x83: /* Tandy sound system start playback */ + if (Tandy_TransferInProgress()) { + /* cannot play yet as the last transfer isn't finished yet */ + reg_ah=0x00; + CALLBACK_SCF(true); + break; + } + /* store buffer length */ + real_writew(0x40,0xd0,reg_cx); + /* store delay and volume */ + real_writew(0x40,0xd2,(reg_dx&0xfff)|((reg_al&7)<<13)); + Tandy_SetupTransfer(PhysMake(SegValue(es),reg_bx),reg_ah==0x83); + reg_ah=0x00; + CALLBACK_SCF(false); + break; + case 0x84: /* Tandy sound system stop playing */ + reg_ah=0x00; + + /* setup for a small buffer with silence */ + real_writew(0x40,0xd0,0x0a); + real_writew(0x40,0xd2,0x1c); + Tandy_SetupTransfer(PhysMake(0xf000,0xa084),true); + CALLBACK_SCF(false); + break; + case 0x85: /* Tandy sound system reset */ + if (tandy_dac.port) { + IO_Write(tandy_dac.port,(Bit8u)(IO_Read(tandy_dac.port)&0xe0)); + } + reg_ah=0x00; + CALLBACK_SCF(false); + break; + } +} + +extern bool date_host_forced; +static Bit8u ReadCmosByte (Bitu index) { + IO_Write(0x70, index); + return IO_Read(0x71); +} + +static void WriteCmosByte (Bitu index, Bitu val) { + IO_Write(0x70, index); + IO_Write(0x71, val); +} + +static bool RtcUpdateDone () { + while ((ReadCmosByte(0x0a) & 0x80) != 0) CALLBACK_Idle(); + return true; // cannot fail in DOSbox +} + +static void InitRtc () { + WriteCmosByte(0x0a, 0x26); // default value (32768Hz, 1024Hz) + + // leave bits 6 (pirq), 5 (airq), 0 (dst) untouched + // reset bits 7 (freeze), 4 (uirq), 3 (sqw), 2 (bcd) + // set bit 1 (24h) + WriteCmosByte(0x0b, (ReadCmosByte(0x0b) & 0x61u) | 0x02u); + + ReadCmosByte(0x0c); // clear any bits set +} + +static Bitu INT1A_Handler(void) { + CALLBACK_SIF(true); + switch (reg_ah) { + case 0x00: /* Get System time */ + { + Bit32u ticks=mem_readd(BIOS_TIMER); + reg_al=mem_readb(BIOS_24_HOURS_FLAG); + mem_writeb(BIOS_24_HOURS_FLAG,0); // reset the "flag" + reg_cx=(Bit16u)(ticks >> 16u); + reg_dx=(Bit16u)(ticks & 0xffff); + break; + } + case 0x01: /* Set System time */ + mem_writed(BIOS_TIMER,((unsigned int)reg_cx<<16u)|reg_dx); + break; + case 0x02: /* GET REAL-TIME CLOCK TIME (AT,XT286,PS) */ + if(date_host_forced) { + InitRtc(); // make sure BCD and no am/pm + if (RtcUpdateDone()) { // make sure it's safe to read + reg_ch = ReadCmosByte(0x04); // hours + reg_cl = ReadCmosByte(0x02); // minutes + reg_dh = ReadCmosByte(0x00); // seconds + reg_dl = ReadCmosByte(0x0b) & 0x01; // daylight saving time + } + CALLBACK_SCF(false); + break; + } + IO_Write(0x70,0x04); //Hours + reg_ch=IO_Read(0x71); + IO_Write(0x70,0x02); //Minutes + reg_cl=IO_Read(0x71); + IO_Write(0x70,0x00); //Seconds + reg_dh=IO_Read(0x71); + reg_dl=0; //Daylight saving disabled + CALLBACK_SCF(false); + break; + case 0x03: // set RTC time + if(date_host_forced) { + InitRtc(); // make sure BCD and no am/pm + WriteCmosByte(0x0b, ReadCmosByte(0x0b) | 0x80u); // prohibit updates + WriteCmosByte(0x04, reg_ch); // hours + WriteCmosByte(0x02, reg_cl); // minutes + WriteCmosByte(0x00, reg_dh); // seconds + WriteCmosByte(0x0b, (ReadCmosByte(0x0b) & 0x7eu) | (reg_dh & 0x01u)); // dst + implicitly allow updates + } + break; + case 0x04: /* GET REAL-TIME ClOCK DATE (AT,XT286,PS) */ + if(date_host_forced) { + InitRtc(); // make sure BCD and no am/pm + if (RtcUpdateDone()) { // make sure it's safe to read + reg_ch = ReadCmosByte(0x32); // century + reg_cl = ReadCmosByte(0x09); // year + reg_dh = ReadCmosByte(0x08); // month + reg_dl = ReadCmosByte(0x07); // day + } + CALLBACK_SCF(false); + break; + } + IO_Write(0x70,0x32); //Centuries + reg_ch=IO_Read(0x71); + IO_Write(0x70,0x09); //Years + reg_cl=IO_Read(0x71); + IO_Write(0x70,0x08); //Months + reg_dh=IO_Read(0x71); + IO_Write(0x70,0x07); //Days + reg_dl=IO_Read(0x71); + CALLBACK_SCF(false); + break; + case 0x05: // set RTC date + if(date_host_forced) { + InitRtc(); // make sure BCD and no am/pm + WriteCmosByte(0x0b, ReadCmosByte(0x0b) | 0x80); // prohibit updates + WriteCmosByte(0x32, reg_ch); // century + WriteCmosByte(0x09, reg_cl); // year + WriteCmosByte(0x08, reg_dh); // month + WriteCmosByte(0x07, reg_dl); // day + WriteCmosByte(0x0b, (ReadCmosByte(0x0b) & 0x7f)); // allow updates + } + break; + case 0x80: /* Pcjr Setup Sound Multiplexer */ + LOG(LOG_BIOS,LOG_ERROR)("INT1A:80:Setup tandy sound multiplexer to %d",reg_al); + break; + case 0x81: /* Tandy sound system check */ + case 0x82: /* Tandy sound system start recording */ + case 0x83: /* Tandy sound system start playback */ + case 0x84: /* Tandy sound system stop playing */ + case 0x85: /* Tandy sound system reset */ + TandyDAC_Handler(reg_ah); + break; + case 0xb1: /* PCI Bios Calls */ + if (pcibus_enable) { + LOG(LOG_BIOS,LOG_WARN)("INT1A:PCI bios call %2X",reg_al); + switch (reg_al) { + case 0x01: // installation check + if (PCI_IsInitialized()) { + reg_ah=0x00; + reg_al=0x01; // cfg space mechanism 1 supported + reg_bx=0x0210; // ver 2.10 + reg_cx=0x0000; // only one PCI bus + reg_edx=0x20494350; + reg_edi=PCI_GetPModeInterface(); + CALLBACK_SCF(false); + } else { + CALLBACK_SCF(true); + } + break; + case 0x02: { // find device + Bitu devnr=0u; + Bitu count=0x100u; + Bit32u devicetag=((unsigned int)reg_cx<<16u)|reg_dx; + Bits found=-1; + for (Bitu i=0; i<=count; i++) { + IO_WriteD(0xcf8,0x80000000u|(i<<8u)); // query unique device/subdevice entries + if (IO_ReadD(0xcfc)==devicetag) { + if (devnr==reg_si) { + found=(Bits)i; + break; + } else { + // device found, but not the SIth device + devnr++; + } + } + } + if (found>=0) { + reg_ah=0x00; + reg_bh=0x00; // bus 0 + reg_bl=(Bit8u)(found&0xff); + CALLBACK_SCF(false); + } else { + reg_ah=0x86; // device not found + CALLBACK_SCF(true); + } + } + break; + case 0x03: { // find device by class code + Bitu devnr=0; + Bitu count=0x100u; + Bit32u classtag=reg_ecx&0xffffffu; + Bits found=-1; + for (Bitu i=0; i<=count; i++) { + IO_WriteD(0xcf8,0x80000000u|(i<<8u)); // query unique device/subdevice entries + if (IO_ReadD(0xcfc)!=0xffffffffu) { + IO_WriteD(0xcf8,0x80000000u|(i<<8u)|0x08u); + if ((IO_ReadD(0xcfc)>>8u)==classtag) { + if (devnr==reg_si) { + found=(Bits)i; + break; + } else { + // device found, but not the SIth device + devnr++; + } + } + } + } + if (found>=0) { + reg_ah=0x00; + reg_bh=0x00; // bus 0 + reg_bl=(Bit8u)(found&0xffu); + CALLBACK_SCF(false); + } else { + reg_ah=0x86; // device not found + CALLBACK_SCF(true); + } + } + break; + case 0x08: // read configuration byte + IO_WriteD(0xcf8,0x80000000u|((unsigned int)reg_bx<<8u)|(reg_di&0xfcu)); + reg_cl=IO_ReadB(0xcfc+(reg_di&3u)); + CALLBACK_SCF(false); + reg_ah=0x00; + break; + case 0x09: // read configuration word + IO_WriteD(0xcf8,0x80000000u|((unsigned int)reg_bx<<8u)|(reg_di&0xfcu)); + reg_cx=IO_ReadW(0xcfc+(reg_di&2u)); + CALLBACK_SCF(false); + reg_ah=0x00; + break; + case 0x0a: // read configuration dword + IO_WriteD(0xcf8,0x80000000u|((unsigned int)reg_bx<<8u)|(reg_di&0xfcu)); + reg_ecx=IO_ReadD(0xcfc+(reg_di&3u)); + CALLBACK_SCF(false); + reg_ah=0x00; + break; + case 0x0b: // write configuration byte + IO_WriteD(0xcf8,0x80000000u|((unsigned int)reg_bx<<8u)|(reg_di&0xfcu)); + IO_WriteB(0xcfc+(reg_di&3u),reg_cl); + CALLBACK_SCF(false); + reg_ah=0x00; + break; + case 0x0c: // write configuration word + IO_WriteD(0xcf8,0x80000000u|((unsigned int)reg_bx<<8u)|(reg_di&0xfcu)); + IO_WriteW(0xcfc+(reg_di&2u),reg_cx); + CALLBACK_SCF(false); + reg_ah=0x00; + break; + case 0x0d: // write configuration dword + IO_WriteD(0xcf8,0x80000000u|((unsigned int)reg_bx<<8u)|(reg_di&0xfcu)); + IO_WriteD(0xcfc+((unsigned int)reg_di&3u),reg_ecx); + CALLBACK_SCF(false); + reg_ah=0x00; + break; + default: + LOG(LOG_BIOS,LOG_ERROR)("INT1A:PCI BIOS: unknown function %x (%x %x %x)", + reg_ax,reg_bx,reg_cx,reg_dx); + CALLBACK_SCF(true); + break; + } + } + else { + CALLBACK_SCF(true); + } + break; + default: + LOG(LOG_BIOS,LOG_ERROR)("INT1A:Undefined call %2X",reg_ah); + } + return CBRET_NONE; +} + +bool INT16_get_key(Bit16u &code); +bool INT16_peek_key(Bit16u &code); + +extern uint8_t GDC_display_plane; +extern uint8_t GDC_display_plane_pending; + +unsigned char prev_pc98_mode42 = 0; + +bool pc98_function_row = false; + +const char *pc98_func_key[10] = { + " C1 ", + " CU ", + " CA ", + " S1 ", + " SU ", + + " VOID ", + " NWL ", + " INS ", + " REP ", + " ^Z " +}; + +// shortcuts offered by SHIFT F1-F10. You can bring this onscreen using CTRL+F7. This row shows '*' in col 2. +// [0] is onscreen display, [1] is what is entered to STDIN. +const char *pc98_shcut_key[10][2] = { + {"dir a:", "dir a:\x0D"}, + {"dir b:", "dir b:\x0D"}, + {"copy ", "copy "}, + {"del ", "del "}, + {"ren ", "ren "}, + + {"chkdsk", "chkdsk a:\x0D"}, + {"chkdsk", "chkdsk b:\x0D"}, + {"type ", "type "}, + {"date\x0D ","date\x0D"}, // display includes CR + {"time\x0D ","time\x0D"} +}; + +#include "int10.h" + +void update_pc98_function_row(bool enable) { + pc98_function_row = enable; + + real_writeb(0x60,0x112,25 - 1 - (pc98_function_row ? 1 : 0)); + + unsigned char c = real_readb(0x60,0x11C); + unsigned char r = real_readb(0x60,0x110); + unsigned int o = 80 * 24; + + if (pc98_function_row) { + if (r > 23) r = 23; + + /* draw the function row. + * based on on real hardware: + * + * The function key is 72 chars wide. 4 blank chars on each side of the screen. + * It is divided into two halves, 36 chars each. + * Within each half, aligned to it's side, is 5 x 7 regions. + * 6 of the 7 are inverted. centered in the white block is the function key. */ + for (unsigned int i=0;i < 40;) { + mem_writew(0xA0000+((o+i)*2),0x0000); + mem_writeb(0xA2000+((o+i)*2),0xE1); + + mem_writew(0xA0000+((o+(79-i))*2),0x0000); + mem_writeb(0xA2000+((o+(79-i))*2),0xE1); + + if (i >= 3 && i < 38) + i += 7; + else + i++; + } + + for (unsigned int i=0;i < 5u;i++) { + unsigned int co = 4u + (i * 7u); + const char *str = pc98_func_key[i]; + + for (unsigned int j=0;j < 6u;j++) { + mem_writew(0xA0000+((o+co+j)*2u),(unsigned char)str[j]); + mem_writeb(0xA2000+((o+co+j)*2u),0xE5); // white reverse visible + } + } + + for (unsigned int i=5;i < 10;i++) { + unsigned int co = 42u + ((i - 5u) * 7u); + const char *str = pc98_func_key[i]; + + for (unsigned int j=0;j < 6u;j++) { + mem_writew(0xA0000+((o+co+j)*2u),(unsigned char)str[j]); + mem_writeb(0xA2000+((o+co+j)*2u),0xE5); // white reverse visible + } + } + } + else { + /* erase the function row */ + for (unsigned int i=0;i < 80;i++) { + mem_writew(0xA0000+((o+i)*2),0x0000); + mem_writeb(0xA2000+((o+i)*2),0xE1); + } + } + + real_writeb(0x60,0x11C,c); + real_writeb(0x60,0x110,r); + + real_writeb(0x60,0x111,pc98_function_row ? 0x01 : 0x00);/* function key row display status */ + + void vga_pc98_direct_cursor_pos(Bit16u address); + vga_pc98_direct_cursor_pos((r*80)+c); +} + +void pc98_set_digpal_entry(unsigned char ent,unsigned char grb); +void PC98_show_cursor(bool show); + +extern bool gdc_5mhz_mode; +extern bool enable_pc98_egc; +extern bool enable_pc98_grcg; +extern bool enable_pc98_16color; +extern bool enable_pc98_188usermod; +extern bool pc98_31khz_mode; +extern bool pc98_attr4_graphic; + +extern unsigned char pc98_text_first_row_scanline_start; /* port 70h */ +extern unsigned char pc98_text_first_row_scanline_end; /* port 72h */ +extern unsigned char pc98_text_row_scanline_blank_at; /* port 74h */ +extern unsigned char pc98_text_row_scroll_lines; /* port 76h */ +extern unsigned char pc98_text_row_scroll_count_start; /* port 78h */ +extern unsigned char pc98_text_row_scroll_num_lines; /* port 7Ah */ + +void pc98_update_text_layer_lineheight_from_bda(void) { +// unsigned char c = mem_readb(0x53C); + unsigned char lineheight = mem_readb(0x53B) + 1; + + pc98_gdc[GDC_MASTER].force_fifo_complete(); + pc98_gdc[GDC_MASTER].row_height = lineheight; + + if (lineheight > 16) { // usually 20 + pc98_text_first_row_scanline_start = 0x1E; + pc98_text_first_row_scanline_end = lineheight - 3; + pc98_text_row_scanline_blank_at = 16; + } + else { + pc98_text_first_row_scanline_start = 0; + pc98_text_first_row_scanline_end = lineheight - 1; + pc98_text_row_scanline_blank_at = lineheight; + } + + pc98_text_row_scroll_lines = 0; + pc98_text_row_scroll_count_start = 0; + pc98_text_row_scroll_num_lines = 0; + + vga.crtc.cursor_start = 0; + vga.draw.cursor.sline = 0; + + vga.crtc.cursor_end = lineheight - 1; + vga.draw.cursor.eline = lineheight - 1; +} + +void pc98_update_text_lineheight_from_bda(void) { + unsigned char c = mem_readb(0x53C); + unsigned char lineheight; + + if (c & 0x01)/*20-line mode*/ + lineheight = 20; + else /*25-line mode*/ + lineheight = 16; + + mem_writeb(0x53B,lineheight - 1); +} + +static Bitu INT18_PC98_Handler(void) { + Bit16u temp16; + +#if 0 + if (reg_ah >= 0x0A) { + LOG_MSG("PC-98 INT 18h unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", + reg_ax, + reg_bx, + reg_cx, + reg_dx, + reg_si, + reg_di, + SegValue(ds), + SegValue(es)); + } +#endif + + /* NTS: Based on information gleaned from Neko Project II source code including comments which + * I've run through GNU iconv to convert from SHIFT-JIS to UTF-8 here in case Google Translate + * got anything wrong. */ + switch (reg_ah) { + case 0x00: /* Reading of key data (キー・データの読みだし) */ + /* FIXME: We use the IBM PC/AT keyboard buffer to fake this call. + * This will be replaced with PROPER emulation once the PC-98 keyboard handler has been + * updated to write the buffer the way PC-98 BIOSes do it. + * + * IBM PC/AT keyboard buffer at 40:1E-40:3D + * + * PC-98 keyboard buffer at 50:02-50:21 */ + /* This call blocks until keyboard input */ + if (INT16_get_key(temp16)) { + reg_ax = temp16; + } + else { + /* Keyboard checks. + * If the interrupt got masked, unmask it. + * If the keyboard has data waiting, make sure the interrupt signal is active in case the last interrupt handler + * handled the keyboard interrupt and never read the keyboard (Quarth). + * + * TODO: Is this what real PC-98 BIOSes do? */ + void check_keyboard_fire_IRQ1(void); + check_keyboard_fire_IRQ1(); + IO_WriteB(0x02,IO_ReadB(0x02) & (~(1u << /*IRQ*/1u))); // unmask IRQ1 + + reg_ip += 1; /* step over IRET, to NOPs which then JMP back to callback */ + } + break; + case 0x01: /* Sense of key buffer state (キー・バッファ状態のセンス) */ + /* This call returns whether or not there is input waiting. + * The waiting data is read, but NOT discarded from the buffer. */ + if (INT16_peek_key(temp16)) { + reg_ax = temp16; + reg_bh = 1; + } + else { + /* Keyboard checks. + * If the interrupt got masked, unmask it. + * If the keyboard has data waiting, make sure the interrupt signal is active in case the last interrupt handler + * handled the keyboard interrupt and never read the keyboard (Quarth). + * + * TODO: Is this what real PC-98 BIOSes do? */ + void check_keyboard_fire_IRQ1(void); + check_keyboard_fire_IRQ1(); + IO_WriteB(0x02,IO_ReadB(0x02) & (~(1u << /*IRQ*/1u))); // unmask IRQ1 + + reg_bh = 0; + } + break; + case 0x02: /* Sense of shift key state (シフト・キー状態のセンス) */ + reg_al = mem_readb(0x52A + 0x0E); /* FIXME: Seems to match 14th bitmap byte. Does real hardware do this?? */ + break; + case 0x03: /* Initialization of keyboard interface (キーボード・インタフェイスの初期化) */ + /* TODO */ + break; + case 0x04: /* Sense of key input state (キー入力状態のセンス) */ + reg_ah = mem_readb(0x52A + (unsigned int)(reg_al & 0x0Fu)); + /* Hack for "Shangrlia" by Elf: The game's regulation of animation speed seems to depend on + * INT 18h AH=0x04 taking some amount of time. If we do not do this, animation will run way + * too fast and everyone will be talking/moving at a million miles a second. + * + * This is based on comparing animation speed vs the same game on real Pentium-class PC-98 + * hardware. + * + * Looking at the software loop involved during opening cutscenes, the game is constantly + * polling INT 18h AH=04h (keyboard state) and INT 33h AH=03h (mouse button/position state) + * while animating the characters on the screen. Without this delay, animation runs way too + * fast. + * + * This guess is also loosely based on a report by the Touhou Community Reliant Automatic Patcher + * that Touhou Project directly reads this byte but delays by 0.6ms to handle the fact that + * the bit in question may toggle while the key is held down due to the scan codes returned by + * the keyboard. + * + * This is a guess, but it seems to help animation speed match that of real hardware regardless + * of cycle count in DOSBox-X. */ + CPU_Cycles -= (unsigned int)(CPU_CycleMax * 0.006); + break; + case 0x05: /* Key input sense (キー入力センス) */ + /* This appears to return a key from the buffer (and remove from + * buffer) or return BH == 0 to signal no key was pending. */ + if (INT16_get_key(temp16)) { + reg_ax = temp16; + reg_bh = 1; + } + else { + /* Keyboard checks. + * If the interrupt got masked, unmask it. + * If the keyboard has data waiting, make sure the interrupt signal is active in case the last interrupt handler + * handled the keyboard interrupt and never read the keyboard (Quarth). + * + * TODO: Is this what real PC-98 BIOSes do? */ + void check_keyboard_fire_IRQ1(void); + check_keyboard_fire_IRQ1(); + IO_WriteB(0x02,IO_ReadB(0x02) & (~(1u << /*IRQ*/1u))); // unmask IRQ1 + + reg_bh = 0; + } + break; + case 0x0A: /* set CRT mode */ + /* bit off on + 0 25lines 20lines + 1 80cols 40cols + 2 v.lines simp.graphics + 3 K-CG access mode(not used in PC-98) */ + + //TODO: set 25/20 lines mode and 80/40 columns mode. + //Attribute bit (bit 2) + pc98_attr4_graphic = !!(reg_al & 0x04); + + mem_writeb(0x53C,reg_al); + + if (reg_al & 2) + LOG_MSG("INT 18H AH=0Ah warning: 40-column PC-98 text mode not supported"); + if (reg_al & 8) + LOG_MSG("INT 18H AH=0Ah warning: K-CG dot access mode not supported"); + + pc98_update_text_lineheight_from_bda(); + pc98_update_text_layer_lineheight_from_bda(); + + /* Apparently, this BIOS call also hides the cursor */ + PC98_show_cursor(0); + break; + case 0x0B: /* get CRT mode */ + /* bit off on + 0 25lines 20lines + 1 80cols 40cols + 2 v.lines simp.graphics + 3 K-CG access mode(not used in PC-98) + 7 std CRT hi-res CRT */ + /* NTS: I assume that real hardware doesn't offer a way to read back the state of these bits, + * so the BIOS's only option is to read the mode byte back from the data area. + * Neko Project II agrees. */ + reg_al = mem_readb(0x53C); + break; + // TODO: "Edge" is using INT 18h AH=06h, what is that? + // Neko Project is also unaware of such a call. + case 0x0C: /* text layer enable */ + pc98_gdc[GDC_MASTER].force_fifo_complete(); + pc98_gdc[GDC_MASTER].display_enable = true; + break; + case 0x0D: /* text layer disable */ + pc98_gdc[GDC_MASTER].force_fifo_complete(); + pc98_gdc[GDC_MASTER].display_enable = false; + break; + case 0x0E: /* set text display area (DX=byte offset) */ + pc98_gdc[GDC_MASTER].force_fifo_complete(); + pc98_gdc[GDC_MASTER].param_ram[0] = (reg_dx >> 1) & 0xFF; + pc98_gdc[GDC_MASTER].param_ram[1] = (reg_dx >> 9) & 0xFF; + pc98_gdc[GDC_MASTER].param_ram[2] = (400 << 4) & 0xFF; + pc98_gdc[GDC_MASTER].param_ram[3] = (400 << 4) >> 8; + break; + case 0x11: /* show cursor */ + PC98_show_cursor(true); + break; + case 0x12: /* hide cursor */ + PC98_show_cursor(false); + break; + case 0x13: /* set cursor position (DX=byte position) */ + void vga_pc98_direct_cursor_pos(Bit16u address); + + pc98_gdc[GDC_MASTER].force_fifo_complete(); + vga_pc98_direct_cursor_pos(reg_dx >> 1); + break; + case 0x14: /* read FONT RAM */ + { + unsigned int i,o,r; + + /* DX = code (must be 0x76xx or 0x7700) + * BX:CX = 34-byte region to write to */ + if (reg_dh == 0x80u) { /* 8x16 ascii */ + i = ((unsigned int)reg_bx << 4u) + reg_cx + 2u; + mem_writew(i-2u,0x0102u); + for (r=0;r < 16u;r++) { + o = (reg_dl*16u)+r; + + assert((o+2u) <= sizeof(vga.draw.font)); + + mem_writeb(i+r,vga.draw.font[o]); + } + } + else if ((reg_dh & 0xFC) == 0x28) { /* 8x16 kanji */ + i = ((unsigned int)reg_bx << 4u) + reg_cx + 2u; + mem_writew(i-2u,0x0102u); + for (r=0;r < 16u;r++) { + o = (((((reg_dl & 0x7Fu)*128u)+((reg_dh - 0x20u) & 0x7Fu))*16u)+r)*2u; + + assert((o+2u) <= sizeof(vga.draw.font)); + + mem_writeb(i+r+0u,vga.draw.font[o+0u]); + } + } + else if (reg_dh != 0) { /* 16x16 kanji */ + i = ((unsigned int)reg_bx << 4u) + reg_cx + 2u; + mem_writew(i-2u,0x0202u); + for (r=0;r < 16u;r++) { + o = (((((reg_dl & 0x7Fu)*128u)+((reg_dh - 0x20u) & 0x7Fu))*16u)+r)*2u; + + assert((o+2u) <= sizeof(vga.draw.font)); + + mem_writeb(i+(r*2u)+0u,vga.draw.font[o+0u]); + mem_writeb(i+(r*2u)+1u,vga.draw.font[o+1u]); + } + } + else { + LOG_MSG("PC-98 INT 18h AH=14h font RAM read ignored, code 0x%04x not supported",reg_dx); + } + } + break; + case 0x16: /* fill screen with chr + attr */ + { + /* DL = character + * DH = attribute */ + unsigned int i; + + for (i=0;i < 0x2000;i += 2) { + vga.mem.linear[i+0] = reg_dl; + vga.mem.linear[i+1] = 0x00; + } + for ( ;i < 0x3FE0;i += 2) { + vga.mem.linear[i+0] = reg_dh; + vga.mem.linear[i+1] = 0x00; + } + } + break; + case 0x17: /* BELL ON */ + IO_WriteB(0x37,0x06); + break; + case 0x18: /* BELL OFF */ + IO_WriteB(0x37,0x07); + break; + case 0x1A: /* load FONT RAM */ + { + unsigned int i,o,r; + + /* DX = code (must be 0x76xx or 0x7700) + * BX:CX = 34-byte region to read from */ + if ((reg_dh & 0x7Eu) == 0x76u) { + i = ((unsigned int)reg_bx << 4u) + reg_cx + 2u; + for (r=0;r < 16u;r++) { + o = (((((reg_dl & 0x7Fu)*128u)+((reg_dh - 0x20u) & 0x7Fu))*16u)+r)*2u; + + assert((o+2u) <= sizeof(vga.draw.font)); + + vga.draw.font[o+0u] = mem_readb(i+(r*2u)+0u); + vga.draw.font[o+1u] = mem_readb(i+(r*2u)+1u); + } + } + else { + LOG_MSG("PC-98 INT 18h AH=1Ah font RAM load ignored, code 0x%04x out of range",reg_dx); + } + } + break; + case 0x31: /* Return display mode and status */ + if (enable_pc98_egc) { /* FIXME: INT 18h AH=31/30h availability is tied to EGC enable */ + unsigned char b597 = mem_readb(0x597); + unsigned char tstat = mem_readb(0x53C); + /* Return values: + * + * AL = + * bit [7:7] = ? + * bit [6:6] = ? + * bit [5:5] = ? + * bit [4:4] = ? + * bit [3:2] = horizontal sync + * 00 = 15.98KHz + * 01 = ? + * 10 = 24.83KHz + * 11 = 31.47KHz + * bit [1:1] = ? + * bit [0:0] = interlaced (1=yes 0=no) + * BH = + * bit [7:7] = ? + * bit [6:6] = ? + * bit [5:4] = graphics video mode + * 00 = 640x200 (upper half) + * 01 = 640x200 (lower half) + * 10 = 640x400 + * 11 = 640x480 + * bit [3:3] = ? + * bit [2:2] = ? + * bit [1:0] = number of text rows + * 00 = 20 rows + * 01 = 25 rows + * 10 = 30 rows + * 11 = ? + */ + reg_al = + ((pc98_31khz_mode ? 3 : 2) << 2)/*hsync*/; + reg_bh = + ((b597 & 3) << 4)/*graphics video mode*/; + if (tstat & 0x10) + reg_bh |= 2;/*30 rows*/ + else if ((tstat & 0x01) == 0) + reg_bh |= 1;/*25 rows*/ + } + break; + /* From this point on the INT 18h call list appears to wander off from the keyboard into CRT/GDC/display management. */ + case 0x40: /* Start displaying the graphics screen (グラフィック画面の表示開始) */ + pc98_gdc[GDC_SLAVE].force_fifo_complete(); + pc98_gdc[GDC_SLAVE].display_enable = true; + + { + unsigned char b = mem_readb(0x54C/*MEMB_PRXCRT*/); + mem_writeb(0x54C/*MEMB_PRXCRT*/,b | 0x80); + } + break; + case 0x41: /* Stop displaying the graphics screen (グラフィック画面の表示終了) */ + pc98_gdc[GDC_SLAVE].force_fifo_complete(); + pc98_gdc[GDC_SLAVE].display_enable = false; + + { + unsigned char b = mem_readb(0x54C/*MEMB_PRXCRT*/); + mem_writeb(0x54C/*MEMB_PRXCRT*/,b & (~0x80)); + } + break; + case 0x42: /* Display area setup (表示領域の設定) */ + pc98_gdc[GDC_MASTER].force_fifo_complete(); + pc98_gdc[GDC_SLAVE].force_fifo_complete(); + /* reset scroll area of graphics */ + if ((reg_ch & 0xC0) == 0x40) { /* 640x200 G-RAM upper half */ + pc98_gdc[GDC_SLAVE].param_ram[0] = (200*40) & 0xFF; + pc98_gdc[GDC_SLAVE].param_ram[1] = (200*40) >> 8; + } + else { + pc98_gdc[GDC_SLAVE].param_ram[0] = 0; + pc98_gdc[GDC_SLAVE].param_ram[1] = 0; + } + pc98_gdc[GDC_SLAVE].param_ram[2] = 0xF0; + pc98_gdc[GDC_SLAVE].param_ram[3] = 0x3F; + pc98_gdc[GDC_SLAVE].active_display_words_per_line = 40; /* 40 x 16 = 640 pixel wide graphics */ + + // CH + // [7:6] = G-RAM setup + // 00 = no graphics (?) + // 01 = 640x200 upper half + // 10 = 640x200 lower half + // 11 = 640x400 + // [5:5] = CRT + // 0 = color + // 1 = monochrome + // [4:4] = Display bank + + // FIXME: This is a guess. I have no idea as to actual behavior, yet. + // This seems to help with clearing the text layer when games start the graphics. + // This is ALSO how we will detect games that switch on the 200-line double-scan mode vs 400-line mode. + if ((reg_ch & 0xC0) != 0) { + pc98_gdc[GDC_SLAVE].doublescan = ((reg_ch & 0xC0) == 0x40) || ((reg_ch & 0xC0) == 0x80); + pc98_gdc[GDC_SLAVE].row_height = pc98_gdc[GDC_SLAVE].doublescan ? 2 : 1; + + /* update graphics mode bits */ + { + unsigned char b = mem_readb(0x597); + + b &= ~3; + b |= ((reg_ch >> 6) - 1) & 3; + + mem_writeb(0x597,b); + } + } + else { + pc98_gdc[GDC_SLAVE].doublescan = false; + pc98_gdc[GDC_SLAVE].row_height = 1; + } + + { + unsigned char b = mem_readb(0x54C/*MEMB_PRXCRT*/); + + // Real hardware behavior: graphics selection updated by BIOS to reflect MEMB_PRXCRT state + pc98_gdc[GDC_SLAVE].display_enable = !!(b & 0x80); + } + + pc98_gdc_vramop &= ~(1 << VOPBIT_ACCESS); + GDC_display_plane = GDC_display_plane_pending = (reg_ch & 0x10) ? 1 : 0; + + prev_pc98_mode42 = reg_ch; + + LOG_MSG("PC-98 INT 18 AH=42h CH=0x%02X",reg_ch); + break; + case 0x43: // Palette register settings? Only works in digital mode? --leonier + // + // This is said to fix Thexder's GAME ARTS logo. --Jonathan C. + // + // TODO: Validate this against real PC-98 hardware and BIOS + { + unsigned int gbcpc = SegValue(ds)*0x10u + reg_bx; + for(unsigned int i=0;i<4;i++) + { + unsigned char p=mem_readb(gbcpc+4u+i); + pc98_set_digpal_entry(7u-2u*i, p&0xFu); + pc98_set_digpal_entry(6u-2u*i, p>>4u); + } + LOG_MSG("PC-98 INT 18 AH=43h CX=0x%04X DS=0x%04X", reg_cx, SegValue(ds)); + break; + } + default: + LOG_MSG("PC-98 INT 18h unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", + reg_ax, + reg_bx, + reg_cx, + reg_dx, + reg_si, + reg_di, + SegValue(ds), + SegValue(es)); + break; + }; + + /* FIXME: What do actual BIOSes do when faced with an unknown INT 18h call? */ + return CBRET_NONE; +} + +#define PC98_FLOPPY_HIGHDENSITY 0x01 +#define PC98_FLOPPY_2HEAD 0x02 +#define PC98_FLOPPY_RPM_3MODE 0x04 +#define PC98_FLOPPY_RPM_IBMPC 0x08 + +unsigned char PC98_BIOS_FLOPPY_BUFFER[32768]; /* 128 << 8 */ + +static unsigned int PC98_FDC_SZ_TO_BYTES(unsigned int sz) { + return 128U << sz; +} + +int PC98_BIOS_SCSI_POS(imageDisk *floppy,Bit32u §or) { + if (reg_al & 0x80) { + Bit32u img_heads=0,img_cyl=0,img_sect=0,img_ssz=0; + + floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz); + + /* DL = sector + * DH = head + * CX = track */ + if (reg_dl >= img_sect || + reg_dh >= img_heads || + reg_cx >= img_cyl) { + return (reg_ah=0x60); + } + + sector = reg_cx; + sector *= img_heads; + sector += reg_dh; + sector *= img_sect; + sector += reg_dl; + +// LOG_MSG("Sector CHS %u/%u/%u -> %u (geo %u/%u/%u)",reg_cx,reg_dh,reg_dl,sector,img_cyl,img_heads,img_sect); + } + else { + /* Linear LBA addressing */ + sector = ((unsigned int)reg_dl << 16UL) + reg_cx; + /* TODO: SASI caps at 0x1FFFFF according to NP2 */ + } + + return 0; +} + +void PC98_BIOS_SCSI_CALL(void) { + Bit32u img_heads=0,img_cyl=0,img_sect=0,img_ssz=0; + Bit32u memaddr,size,ssize; + imageDisk *floppy; + unsigned int i; + Bit32u sector; + int idx; + +#if 0 + LOG_MSG("PC-98 INT 1Bh SCSI BIOS call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", + reg_ax, + reg_bx, + reg_cx, + reg_dx, + reg_si, + reg_di, + SegValue(ds), + SegValue(es)); +#endif + + idx = (reg_al & 0xF) + 2; + if (idx < 0 || idx >= MAX_DISK_IMAGES) { + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + + floppy = imageDiskList[idx]; + if (floppy == NULL) { + CALLBACK_SCF(true); + reg_ah = 0x60; + return; + } + + /* what to do is in the lower 4 bits of AH */ + switch (reg_ah & 0x0F) { + case 0x05: /* write */ + if (PC98_BIOS_SCSI_POS(floppy,/*&*/sector) == 0) { + floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz); + assert(img_ssz != 0); + + size = reg_bx; + if (size == 0) size = 0x10000U; + memaddr = ((unsigned int)SegValue(es) << 4u) + reg_bp; + + reg_ah = 0; + CALLBACK_SCF(false); + +// LOG_MSG("WRITE memaddr=0x%lx size=0x%x sector=0x%lx ES:BP=%04x:%04X", +// (unsigned long)memaddr,(unsigned int)size,(unsigned long)sector,SegValue(es),reg_bp); + + while (size != 0) { + ssize = size; + if (ssize > img_ssz) ssize = img_ssz; + +// LOG_MSG(" ... memaddr=0x%lx ssize=0x%x sector=0x%lx", +// (unsigned long)memaddr,(unsigned int)ssize,(unsigned long)sector); + + for (i=0;i < ssize;i++) PC98_BIOS_FLOPPY_BUFFER[i] = mem_readb(memaddr+i); + + if (floppy->Write_AbsoluteSector(sector,PC98_BIOS_FLOPPY_BUFFER) == 0) { + } + else { + reg_ah = 0xD0; + CALLBACK_SCF(true); + break; + } + + sector++; + size -= ssize; + memaddr += ssize; + } + } + else { + CALLBACK_SCF(true); + } + break; + case 0x06: /* read */ + if (PC98_BIOS_SCSI_POS(floppy,/*&*/sector) == 0) { + floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz); + assert(img_ssz != 0); + + size = reg_bx; + if (size == 0) size = 0x10000U; + memaddr = ((unsigned int)SegValue(es) << 4u) + reg_bp; + + reg_ah = 0; + CALLBACK_SCF(false); + +// LOG_MSG("READ memaddr=0x%lx size=0x%x sector=0x%lx ES:BP=%04x:%04X", +// (unsigned long)memaddr,(unsigned int)size,(unsigned long)sector,SegValue(es),reg_bp); + + while (size != 0) { + ssize = size; + if (ssize > img_ssz) ssize = img_ssz; + +// LOG_MSG(" ... memaddr=0x%lx ssize=0x%x sector=0x%lx", +// (unsigned long)memaddr,(unsigned int)ssize,(unsigned long)sector); + + if (floppy->Read_AbsoluteSector(sector,PC98_BIOS_FLOPPY_BUFFER) == 0) { + for (i=0;i < ssize;i++) mem_writeb(memaddr+i,PC98_BIOS_FLOPPY_BUFFER[i]); + } + else { + reg_ah = 0xD0; + CALLBACK_SCF(true); + break; + } + + sector++; + size -= ssize; + memaddr += ssize; + } + } + else { + CALLBACK_SCF(true); + } + break; + case 0x07: /* unknown, always succeeds */ + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + case 0x0E: /* unknown, always fails */ + reg_ah = 0x40; + CALLBACK_SCF(true); + break; + case 0x04: /* drive status */ + if (reg_ah == 0x84) { + floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz); + + reg_dl = img_sect; + reg_dh = img_heads; /* Max 16 */ + reg_cx = img_cyl; /* Max 4096 */ + reg_bx = img_ssz; + + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + } + else if (reg_ah == 0x04 || reg_ah == 0x14) { + reg_ah = 0x00; + CALLBACK_SCF(false); + } + else { + goto default_goto; + } + default: + default_goto: + LOG_MSG("PC-98 INT 1Bh unknown SCSI BIOS call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", + reg_ax, + reg_bx, + reg_cx, + reg_dx, + reg_si, + reg_di, + SegValue(ds), + SegValue(es)); + CALLBACK_SCF(true); + break; + }; +} + +void PC98_BIOS_FDC_CALL_GEO_UNPACK(unsigned int &fdc_cyl,unsigned int &fdc_head,unsigned int &fdc_sect,unsigned int &fdc_sz) { + fdc_cyl = reg_cl; + fdc_head = reg_dh; + fdc_sect = reg_dl; + fdc_sz = reg_ch; + if (fdc_sz > 8) fdc_sz = 8; +} + +/* NTS: FDC calls reset IRQ 0 timer to a specific fixed interval, + * because the real BIOS likely does the same in the act of + * controlling the floppy drive. + * + * Resetting the interval is required to prevent Ys II from + * crashing after disk swap (divide by zero/overflow) because + * Ys II reads the timer after INT 1Bh for whatever reason + * and the upper half of the timer byte later affects a divide + * by 3 in the code. */ + +void PC98_Interval_Timer_Continue(void); + +bool enable_fdc_timer_hack = false; + +void FDC_WAIT_TIMER_HACK(void) { + unsigned int v,pv; + unsigned int c=0; + + // Explanation: + // + // Originally the FDC code here changed the timer interval back to the stock 100hz + // normally used in PC-98, to fix Ys II. However that seems to break other booter + // games that hook IRQ 0 directly and set the timer ONCE, then access the disk. + // + // For example, "Angelus" ran WAY too slow with the timer hack because it programs + // the timer to a 600hz interval and expects it to stay that way. + // + // So the new method to satisfy both games is to loop here until the timer + // count is below the maximum that would occur if the 100hz tick count were + // still in effect, even if the timer interval was reprogrammed. + // + // NTS: Writing port 0x77 to relatch the timer also seems to break games + // + // TODO: As a safety against getting stuck, perhaps PIC_FullIndex() should be used + // to break out of the loop if this runs for more than 1 second, since that + // is a sign the timer is in an odd state that will never terminate this loop. + + v = ~0U; + c = 10; + do { + void CALLBACK_Idle(void); + CALLBACK_Idle(); + + pv = v; + + v = IO_ReadB(0x71); + v |= IO_ReadB(0x71) << 8; + + if (v > pv) { + /* if the timer rolled around, we might have missed the narrow window we're watching for */ + if (--c == 0) break; + } + } while (v >= 0x60); +} + +void PC98_BIOS_FDC_CALL(unsigned int flags) { + static unsigned int fdc_cyl[2]={0,0},fdc_head[2]={0,0},fdc_sect[2]={0,0},fdc_sz[2]={0,0}; // FIXME: Rename and move out. Making "static" is a hack here. + Bit32u img_heads=0,img_cyl=0,img_sect=0,img_ssz=0; + unsigned int drive; + unsigned int status; + unsigned int size,accsize,unitsize; + unsigned long memaddr; + imageDisk *floppy; + + /* AL bits[1:0] = which floppy drive */ + if ((reg_al & 3) >= 2) { + /* This emulation only supports up to 2 floppy drives */ + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + + floppy = GetINT13FloppyDrive(drive=(reg_al & 3)); + + /* what to do is in the lower 4 bits of AH */ + switch (reg_ah & 0x0F) { + /* TODO: 0x00 = seek to track (in CL) */ + /* TODO: 0x01 = test read? */ + /* TODO: 0x03 = equipment flags? */ + /* TODO: 0x04 = format detect? */ + /* TODO: 0x05 = write disk */ + /* TODO: 0x07 = recalibrate (seek to track 0) */ + /* TODO: 0x0A = Read ID */ + /* TODO: 0x0D = Format track */ + /* TODO: 0x0E = ?? */ + case 0x03: /* equipment flags update (?) */ + // TODO: Update the disk equipment flags in BDA. + // For now, make Alantia happy by returning success. + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + case 0x00: /* seek */ + /* CL = track */ + if (floppy == NULL) { + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + + if (enable_fdc_timer_hack) { + // Hack for Ys II + FDC_WAIT_TIMER_HACK(); + } + + fdc_cyl[drive] = reg_cl; + + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + case 0x01: /* test read */ + /* AH bits[4:4] = If set, seek to track specified */ + /* CL = cylinder (track) */ + /* CH = sector size (0=128 1=256 2=512 3=1024 etc) */ + /* DL = sector number (1-based) */ + /* DH = head */ + /* BX = size (in bytes) of data to read */ + /* ES:BP = buffer to read data into */ + if (floppy == NULL) { + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz); + + if (enable_fdc_timer_hack) { + // Hack for Ys II + FDC_WAIT_TIMER_HACK(); + } + + /* Prevent reading 1.44MB floppyies using 1.2MB read commands and vice versa. + * FIXME: It seems MS-DOS 5.0 booted from a HDI image has trouble understanding + * when Drive A: (the first floppy) is a 1.44MB drive or not and fails + * because it only attempts it using 1.2MB format read commands. */ + if (flags & PC98_FLOPPY_RPM_IBMPC) { + if (img_ssz == 1024) { /* reject 1.2MB 3-mode format */ + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + } + else { + if (img_ssz == 512) { /* reject IBM PC 1.44MB format */ + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + } + + PC98_BIOS_FDC_CALL_GEO_UNPACK(/*&*/fdc_cyl[drive],/*&*/fdc_head[drive],/*&*/fdc_sect[drive],/*&*/fdc_sz[drive]); + unitsize = PC98_FDC_SZ_TO_BYTES(fdc_sz[drive]); + if (0/*unitsize != img_ssz || img_heads == 0 || img_cyl == 0 || img_sect == 0*/) { + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + + size = reg_bx; + while (size > 0) { + accsize = size > unitsize ? unitsize : size; + + if (floppy->Read_Sector(fdc_head[drive],fdc_cyl[drive],fdc_sect[drive],PC98_BIOS_FLOPPY_BUFFER,unitsize) != 0) { + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + + size -= accsize; + + if (size == 0) break; + + if ((++fdc_sect[drive]) > img_sect && img_sect != 0) { + fdc_sect[drive] = 1; + if ((++fdc_head[drive]) >= img_heads && img_heads != 0) { + fdc_head[drive] = 0; + fdc_cyl[drive]++; + } + } + } + + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + case 0x02: /* read sectors */ + case 0x06: /* read sectors (what's the difference from 0x02?) */ + /* AH bits[4:4] = If set, seek to track specified */ + /* CL = cylinder (track) */ + /* CH = sector size (0=128 1=256 2=512 3=1024 etc) */ + /* DL = sector number (1-based) */ + /* DH = head */ + /* BX = size (in bytes) of data to read */ + /* ES:BP = buffer to read data into */ + if (floppy == NULL) { + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz); + + if (enable_fdc_timer_hack) { + // Hack for Ys II + FDC_WAIT_TIMER_HACK(); + } + + /* Prevent reading 1.44MB floppyies using 1.2MB read commands and vice versa. + * FIXME: It seems MS-DOS 5.0 booted from a HDI image has trouble understanding + * when Drive A: (the first floppy) is a 1.44MB drive or not and fails + * because it only attempts it using 1.2MB format read commands. */ + if (flags & PC98_FLOPPY_RPM_IBMPC) { + if (img_ssz == 1024) { /* reject 1.2MB 3-mode format */ + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + } + else { + if (img_ssz == 512) { /* reject IBM PC 1.44MB format */ + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + } + + PC98_BIOS_FDC_CALL_GEO_UNPACK(/*&*/fdc_cyl[drive],/*&*/fdc_head[drive],/*&*/fdc_sect[drive],/*&*/fdc_sz[drive]); + unitsize = PC98_FDC_SZ_TO_BYTES(fdc_sz[drive]); + if (0/*unitsize != img_ssz || img_heads == 0 || img_cyl == 0 || img_sect == 0*/) { + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + + size = reg_bx; + memaddr = ((unsigned int)SegValue(es) << 4U) + reg_bp; + while (size > 0) { + accsize = size > unitsize ? unitsize : size; + + if (floppy->Read_Sector(fdc_head[drive],fdc_cyl[drive],fdc_sect[drive],PC98_BIOS_FLOPPY_BUFFER,unitsize) != 0) { + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + + for (unsigned int i=0;i < accsize;i++) + mem_writeb(memaddr+i,PC98_BIOS_FLOPPY_BUFFER[i]); + + memaddr += accsize; + size -= accsize; + + if (size == 0) break; + + if ((++fdc_sect[drive]) > img_sect && img_sect != 0) { + fdc_sect[drive] = 1; + if ((++fdc_head[drive]) >= img_heads && img_heads != 0) { + fdc_head[drive] = 0; + fdc_cyl[drive]++; + } + } + } + + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + case 0x04: /* drive status */ + status = 0; + + /* TODO: bit 4 is set if write protected */ + + if (reg_al & 0x80) { /* high density */ + status |= 0x01; + } + else { /* double density */ + /* TODO: */ + status |= 0x01; + } + + if ((reg_ax & 0x8F40) == 0x8400) { + status |= 8; /* 1MB/640KB format, spindle speed for 3-mode */ + if (reg_ah & 0x40) /* DOSBox-X always supports 1.44MB */ + status |= 4; /* 1.44MB format, spindle speed for IBM PC format */ + } + + if (floppy == NULL) + status |= 0xC0; + + reg_ah = status; + CALLBACK_SCF(false); + break; + /* TODO: 0x00 = seek to track (in CL) */ + /* TODO: 0x01 = test read? */ + /* TODO: 0x03 = equipment flags? */ + /* TODO: 0x04 = format detect? */ + /* TODO: 0x05 = write disk */ + /* TODO: 0x07 = recalibrate (seek to track 0) */ + /* TODO: 0x0A = Read ID */ + /* TODO: 0x0D = Format track */ + /* TODO: 0x0E = ?? */ + case 0x05: /* write sectors */ + /* AH bits[4:4] = If set, seek to track specified */ + /* CL = cylinder (track) */ + /* CH = sector size (0=128 1=256 2=512 3=1024 etc) */ + /* DL = sector number (1-based) */ + /* DH = head */ + /* BX = size (in bytes) of data to read */ + /* ES:BP = buffer to write data from */ + if (floppy == NULL) { + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz); + + if (enable_fdc_timer_hack) { + // Hack for Ys II + FDC_WAIT_TIMER_HACK(); + } + + /* TODO: Error if write protected */ + + PC98_BIOS_FDC_CALL_GEO_UNPACK(/*&*/fdc_cyl[drive],/*&*/fdc_head[drive],/*&*/fdc_sect[drive],/*&*/fdc_sz[drive]); + unitsize = PC98_FDC_SZ_TO_BYTES(fdc_sz[drive]); + if (0/*unitsize != img_ssz || img_heads == 0 || img_cyl == 0 || img_sect == 0*/) { + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + + size = reg_bx; + memaddr = ((unsigned int)SegValue(es) << 4U) + reg_bp; + while (size > 0) { + accsize = size > unitsize ? unitsize : size; + + for (unsigned int i=0;i < accsize;i++) + PC98_BIOS_FLOPPY_BUFFER[i] = mem_readb(memaddr+i); + + if (floppy->Write_Sector(fdc_head[drive],fdc_cyl[drive],fdc_sect[drive],PC98_BIOS_FLOPPY_BUFFER,unitsize) != 0) { + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + + memaddr += accsize; + size -= accsize; + + if (size == 0) break; + + if ((++fdc_sect[drive]) > img_sect && img_sect != 0) { + fdc_sect[drive] = 1; + if ((++fdc_head[drive]) >= img_heads && img_heads != 0) { + fdc_head[drive] = 0; + fdc_cyl[drive]++; + } + } + } + + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + case 0x07: /* recalibrate (seek to track 0) */ + if (floppy == NULL) { + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + + if (enable_fdc_timer_hack) { + // Hack for Ys II + FDC_WAIT_TIMER_HACK(); + } + + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + case 0x0D: /* format track */ + if (floppy == NULL) { + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + + PC98_BIOS_FDC_CALL_GEO_UNPACK(/*&*/fdc_cyl[drive],/*&*/fdc_head[drive],/*&*/fdc_sect[drive],/*&*/fdc_sz[drive]); + unitsize = PC98_FDC_SZ_TO_BYTES(fdc_sz[drive]); + + if (enable_fdc_timer_hack) { + // Hack for Ys II + FDC_WAIT_TIMER_HACK(); + } + + LOG_MSG("WARNING: INT 1Bh FDC format track command not implemented. Formatting is faked, for now on C/H/S/sz %u/%u/%u/%u drive %c.", + (unsigned int)fdc_cyl[drive], + (unsigned int)fdc_head[drive], + (unsigned int)fdc_sect[drive], + (unsigned int)unitsize, + drive + 'A'); + + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + case 0x0A: /* read ID */ + /* NTS: PC-98 "MEGDOS" used by some games seems to rely heavily on this call to + * verify the floppy head is where it thinks it should be! */ + if (floppy == NULL) { + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + + floppy->Get_Geometry(&img_heads, &img_cyl, &img_sect, &img_ssz); + + if (enable_fdc_timer_hack) { + // Hack for Ys II + FDC_WAIT_TIMER_HACK(); + } + + if (reg_ah & 0x10) { // seek to track number in CL + if (img_cyl != 0 && reg_cl >= img_cyl) { + CALLBACK_SCF(true); + reg_ah = 0x00; + /* TODO? Error code? */ + return; + } + + fdc_cyl[drive] = reg_cl; + } + + if (fdc_sect[drive] == 0) + fdc_sect[drive] = 1; + + if (img_ssz >= 1024) + fdc_sz[drive] = 3; + else if (img_ssz >= 512) + fdc_sz[drive] = 2; + else if (img_ssz >= 256) + fdc_sz[drive] = 1; + else + fdc_sz[drive] = 0; + + reg_cl = fdc_cyl[drive]; + reg_dh = fdc_head[drive]; + reg_dl = fdc_sect[drive]; + /* ^ FIXME: A more realistic emulation would return a random number from 1 to N + * where N=sectors/track because the floppy motor is running and tracks + * are moving past the head. */ + reg_ch = fdc_sz[drive]; + + /* per read ID call, increment the sector through the range on disk. + * This is REQUIRED or else MEGDOS will not attempt to read this disk. */ + if (img_sect != 0) { + if ((++fdc_sect[drive]) > img_sect) + fdc_sect[drive] = 1; + } + + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + default: + LOG_MSG("PC-98 INT 1Bh unknown FDC BIOS call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", + reg_ax, + reg_bx, + reg_cx, + reg_dx, + reg_si, + reg_di, + SegValue(ds), + SegValue(es)); + CALLBACK_SCF(true); + break; + }; +} + +static Bitu INT19_PC98_Handler(void) { + LOG_MSG("PC-98 INT 19h unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", + reg_ax, + reg_bx, + reg_cx, + reg_dx, + reg_si, + reg_di, + SegValue(ds), + SegValue(es)); + + return CBRET_NONE; +} + +static Bitu INT1A_PC98_Handler(void) { + /* HACK: This makes the "test" program in DOSLIB work. + * We'll remove this when we implement INT 1Ah */ + if (reg_ax == 0x1000) { + CALLBACK_SCF(false); + reg_ax = 0; + } + + LOG_MSG("PC-98 INT 1Ah unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", + reg_ax, + reg_bx, + reg_cx, + reg_dx, + reg_si, + reg_di, + SegValue(ds), + SegValue(es)); + + return CBRET_NONE; +} + +static Bitu INT1B_PC98_Handler(void) { + /* As BIOS interfaces for disk I/O go, this is fairly unusual */ + switch (reg_al & 0xF0) { + /* floppy disk access */ + /* AL bits[1:0] = floppy drive number */ + /* Uses INT42 if high density, INT41 if double density */ + /* AH bits[3:0] = command */ + case 0x90: /* 1.2MB HD */ + PC98_BIOS_FDC_CALL(PC98_FLOPPY_HIGHDENSITY|PC98_FLOPPY_2HEAD|PC98_FLOPPY_RPM_3MODE); + break; + case 0x30: /* 1.44MB HD (NTS: not supported until the early 1990s) */ + case 0xB0: + PC98_BIOS_FDC_CALL(PC98_FLOPPY_HIGHDENSITY|PC98_FLOPPY_2HEAD|PC98_FLOPPY_RPM_IBMPC); + break; + case 0x70: /* 720KB DD (??) */ + case 0xF0: + PC98_BIOS_FDC_CALL(PC98_FLOPPY_2HEAD|PC98_FLOPPY_RPM_3MODE); // FIXME, correct?? + break; + case 0x20: /* SCSI hard disk BIOS */ + case 0xA0: /* SCSI hard disk BIOS */ + case 0x00: /* SASI hard disk BIOS */ + case 0x80: /* SASI hard disk BIOS */ + PC98_BIOS_SCSI_CALL(); + break; + /* TODO: Other disk formats */ + /* TODO: Future SASI/SCSI BIOS emulation for hard disk images */ + default: + LOG_MSG("PC-98 INT 1Bh unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", + reg_ax, + reg_bx, + reg_cx, + reg_dx, + reg_si, + reg_di, + SegValue(ds), + SegValue(es)); + CALLBACK_SCF(true); + break; + }; + + return CBRET_NONE; +} + +void PC98_Interval_Timer_Continue(void) { + /* assume: interrupts are disabled */ + IO_WriteB(0x71,0x00); + // TODO: What time interval is this supposed to be? + if (PIT_TICK_RATE == PIT_TICK_RATE_PC98_8MHZ) + IO_WriteB(0x71,0x4E); + else + IO_WriteB(0x71,0x60); + + IO_WriteB(0x02,IO_ReadB(0x02) & (~(1u << /*IRQ*/0u))); // unmask IRQ0 +} + +unsigned char pc98_dec2bcd(unsigned char c) { + return ((c / 10u) << 4u) + (c % 10u); +} + +static Bitu INT1C_PC98_Handler(void) { + if (reg_ah == 0x00) { /* get time and date */ + time_t curtime; + struct tm *loctime; + curtime = time (NULL); + loctime = localtime (&curtime); + + unsigned char tmp[6]; + + tmp[0] = pc98_dec2bcd((unsigned int)loctime->tm_year % 100u); + tmp[1] = (((unsigned int)loctime->tm_mon + 1u) << 4u) + (unsigned int)loctime->tm_wday; + tmp[2] = pc98_dec2bcd(loctime->tm_mday); + tmp[3] = pc98_dec2bcd(loctime->tm_hour); + tmp[4] = pc98_dec2bcd(loctime->tm_min); + tmp[5] = pc98_dec2bcd(loctime->tm_sec); + + unsigned long mem = ((unsigned int)SegValue(es) << 4u) + reg_bx; + + for (unsigned int i=0;i < 6;i++) + mem_writeb(mem+i,tmp[i]); + } + else if (reg_ah == 0x02) { /* set interval timer (single event) */ + /* es:bx = interrupt handler to execute + * cx = timer interval in ticks (FIXME: what units of time?) */ + mem_writew(0x1C,reg_bx); + mem_writew(0x1E,SegValue(es)); + mem_writew(0x58A,reg_cx); + + IO_WriteB(0x77,0x36); /* mode 3, binary, low-byte high-byte 16-bit counter */ + + PC98_Interval_Timer_Continue(); + } + else if (reg_ah == 0x03) { /* continue interval timer */ + PC98_Interval_Timer_Continue(); + } + /* TODO: According to the PDF at + * + * http://hackipedia.org/browse.cgi/Computer/Platform/PC%2c%20NEC%20PC%2d98/Collections/PC%2d9801%20Bible%20%e6%9d%b1%e4%ba%ac%e7%90%86%e7%a7%91%e5%a4%a7%e5%ad%a6EIC%20%281994%29%2epdf + * + * There are additional functions + * + * AH = 04h + * ES:BX = ? + * + * --- + * + * AH = 05h + * ES:BX = ? + * + * --- + * + * AH = 06h + * CX = ? (1-FFFFh) + * DX = ? (20h-8000h Hz) + * + * If any PC-98 games or applications rely on this, let me know. Adding a case for them is easy enough if anyone is interested. --J.C. + */ + else { + LOG_MSG("PC-98 INT 1Ch unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", + reg_ax, + reg_bx, + reg_cx, + reg_dx, + reg_si, + reg_di, + SegValue(ds), + SegValue(es)); + } + + return CBRET_NONE; +} + +static Bitu INT1D_PC98_Handler(void) { + LOG_MSG("PC-98 INT 1Dh unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", + reg_ax, + reg_bx, + reg_cx, + reg_dx, + reg_si, + reg_di, + SegValue(ds), + SegValue(es)); + + return CBRET_NONE; +} + +static Bitu INT1E_PC98_Handler(void) { + LOG_MSG("PC-98 INT 1Eh unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", + reg_ax, + reg_bx, + reg_cx, + reg_dx, + reg_si, + reg_di, + SegValue(ds), + SegValue(es)); + + return CBRET_NONE; +} + +void PC98_EXTMEMCPY(void) { + bool enabled = MEM_A20_Enabled(); + MEM_A20_Enable(true); + + Bitu bytes = ((reg_cx - 1u) & 0xFFFFu) + 1u; // bytes, except that 0 == 64KB + PhysPt data = SegPhys(es)+reg_bx; + PhysPt source = (mem_readd(data+0x12u) & 0x00FFFFFFu) + ((unsigned int)mem_readb(data+0x17u)<<24u); + PhysPt dest = (mem_readd(data+0x1Au) & 0x00FFFFFFu) + ((unsigned int)mem_readb(data+0x1Fu)<<24u); + + LOG_MSG("PC-98 memcpy: src=0x%x dst=0x%x data=0x%x count=0x%x", + (unsigned int)source,(unsigned int)dest,(unsigned int)data,(unsigned int)bytes); + + MEM_BlockCopy(dest,source,bytes); + MEM_A20_Enable(enabled); + Segs.limit[cs] = 0xFFFF; + Segs.limit[ds] = 0xFFFF; + Segs.limit[es] = 0xFFFF; + Segs.limit[ss] = 0xFFFF; + + CALLBACK_SCF(false); +} + +static Bitu INT1F_PC98_Handler(void) { + switch (reg_ah) { + case 0x90: + /* Copy extended memory */ + PC98_EXTMEMCPY(); + break; + default: + LOG_MSG("PC-98 INT 1Fh unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", + reg_ax, + reg_bx, + reg_cx, + reg_dx, + reg_si, + reg_di, + SegValue(ds), + SegValue(es)); + CALLBACK_SCF(true); + break; + }; + + return CBRET_NONE; +} + +static Bitu INTGEN_PC98_Handler(void) { + LOG_MSG("PC-98 INT stub unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", + reg_ax, + reg_bx, + reg_cx, + reg_dx, + reg_si, + reg_di, + SegValue(ds), + SegValue(es)); + + return CBRET_NONE; +} + +/* This interrupt should only exist while the DOS kernel is active. + * On actual PC-98 MS-DOS this is a direct interface to MS-DOS's built-in ANSI CON driver. + * + * CL = major function call number + * AH = minor function call number + * DX = data?? */ +extern bool dos_kernel_disabled; + +void PC98_INTDC_WriteChar(unsigned char b); + +static Bitu INTDC_PC98_Handler(void) { + if (dos_kernel_disabled) goto unknown; + + switch (reg_cl) { + case 0x10: + if (reg_ah == 0x00) { /* CL=0x10 AH=0x00 DL=char write char to CON */ + PC98_INTDC_WriteChar(reg_dl); + goto done; + } + else if (reg_ah == 0x01) { /* CL=0x10 AH=0x01 DS:DX write string to CON */ + /* According to the example at http://tepe.tec.fukuoka-u.ac.jp/HP98/studfile/grth/gt10.pdf + * the string ends in '$' just like the main DOS string output function. */ + Bit16u ofs = reg_dx; + do { + unsigned char c = real_readb(SegValue(ds),ofs++); + if (c == '$') break; + PC98_INTDC_WriteChar(c); + } while (1); + goto done; + } + else if (reg_ah == 0x02) { /* CL=0x10 AH=0x02 DL=attribute set console output attribute */ + /* Ref: https://nas.jmc/jmcs/docs/browse/Computer/Platform/PC%2c%20NEC%20PC%2d98/Collections/Undocumented%209801%2c%209821%20Volume%202%20%28webtech.co.jp%29%20English%20translation/memdos%2eenglish%2dgoogle%2dtranslate%2etxt + * + * DL is the attribute byte (in the format written directly to video RAM, not the ANSI code) + * + * NTS: Reverse engineering INT DCh shows it sets both 71Dh and 73Ch as below */ + mem_writeb(0x71D,reg_dl); /* 60:11D */ + mem_writeb(0x73C,reg_dx); /* 60:13C */ + goto done; + } + else if (reg_ah == 0x03) { /* CL=0x10 AH=0x03 CL=Y-coord CH=X-coord set cursor position */ + /* Reverse engineered from INT DCh. Note that the code path is the same taken for ESC = */ + goto unknown; /* TODO: */ + } + else if (reg_ah == 0x04) { /* CL=0x10 AH=0x04 Move cursor down one line */ + /* Reverse engineered from INT DCh. Note that the code path is the same taken for ESC E */ + goto unknown; /* TODO: */ + } + else if (reg_ah == 0x05) { /* CL=0x10 AH=0x05 Move cursor up one line */ + /* Reverse engineered from INT DCh. Note that the code path is the same taken for ESC M */ + goto unknown; /* TODO: */ + } + goto unknown; + default: /* some compilers don't like not having a default case */ + goto unknown; + }; + +done: + return CBRET_NONE; + +unknown: + LOG_MSG("PC-98 INT DCh unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", + reg_ax, + reg_bx, + reg_cx, + reg_dx, + reg_si, + reg_di, + SegValue(ds), + SegValue(es)); + + return CBRET_NONE; +} + +static Bitu INTF2_PC98_Handler(void) { + LOG_MSG("PC-98 INT F2h unknown call AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", + reg_ax, + reg_bx, + reg_cx, + reg_dx, + reg_si, + reg_di, + SegValue(ds), + SegValue(es)); + + return CBRET_NONE; +} + +static Bitu PC98_BIOS_LIO(void) { + /* on entry, AL (from our BIOS code) is set to the call number that lead here */ + LOG_MSG("PC-98 BIOS LIO graphics call 0x%02x with AX=%04X BX=%04X CX=%04X DX=%04X SI=%04X DI=%04X DS=%04X ES=%04X", + reg_al, + reg_ax, + reg_bx, + reg_cx, + reg_dx, + reg_si, + reg_di, + SegValue(ds), + SegValue(es)); + + // from Yksoft1's patch + reg_ah = 0; + + return CBRET_NONE; +} + +static Bitu INT11_Handler(void) { + reg_ax=mem_readw(BIOS_CONFIGURATION); + return CBRET_NONE; +} +/* + * Define the following define to 1 if you want dosbox to check + * the system time every 5 seconds and adjust 1/2 a second to sync them. + */ +#ifndef DOSBOX_CLOCKSYNC +#define DOSBOX_CLOCKSYNC 0 +#endif + +static void BIOS_HostTimeSync() { + /* Setup time and date */ + struct timeb timebuffer; + ftime(&timebuffer); + + struct tm *loctime; + loctime = localtime (&timebuffer.time); + + /* + loctime->tm_hour = 23; + loctime->tm_min = 59; + loctime->tm_sec = 45; + loctime->tm_mday = 28; + loctime->tm_mon = 2-1; + loctime->tm_year = 2007 - 1900; + */ + + dos.date.day=(Bit8u)loctime->tm_mday; + dos.date.month=(Bit8u)loctime->tm_mon+1; + dos.date.year=(Bit16u)loctime->tm_year+1900; + + Bit32u ticks=(Bit32u)(((double)( + loctime->tm_hour*3600*1000+ + loctime->tm_min*60*1000+ + loctime->tm_sec*1000+ + timebuffer.millitm))*(((double)PIT_TICK_RATE/65536.0)/1000.0)); + mem_writed(BIOS_TIMER,ticks); +} + +// TODO: make option +bool enable_bios_timer_synchronize_keyboard_leds = true; + +void KEYBOARD_SetLEDs(Bit8u bits); + +void BIOS_KEYBOARD_SetLEDs(Bitu state) { + Bitu x = mem_readb(BIOS_KEYBOARD_LEDS); + + x &= ~7u; + x |= (state & 7u); + mem_writeb(BIOS_KEYBOARD_LEDS,x); + KEYBOARD_SetLEDs(state); +} + +/* PC-98 IRQ 0 system timer */ +static Bitu INT8_PC98_Handler(void) { + Bit16u counter = mem_readw(0x58A) - 1; + mem_writew(0x58A,counter); + + /* NTS 2018/02/23: I just confirmed from the ROM BIOS of an actual + * PC-98 system that this implementation and Neko Project II + * are 100% accurate to what the BIOS actually does. + * INT 07h really is the "timer tick" interrupt called + * from INT 08h / IRQ 0, and the BIOS really does call + * INT 1Ch AH=3 from INT 08h if the tick count has not + * yet reached zero. + * + * I'm guessing NEC's BIOS developers invented this prior + * to the Intel 80286 and it's INT 07h + * "Coprocessor not present" exception. */ + + if (counter == 0) { + /* mask IRQ 0 */ + IO_WriteB(0x02,IO_ReadB(0x02) | 0x01); + /* ack IRQ 0 */ + IO_WriteB(0x00,0x20); + /* INT 07h */ + CPU_Interrupt(7,CPU_INT_SOFTWARE,reg_eip); + } + else { + /* ack IRQ 0 */ + IO_WriteB(0x00,0x20); + /* make sure it continues ticking */ + PC98_Interval_Timer_Continue(); + } + + return CBRET_NONE; +} + +static Bitu INT8_Handler(void) { + /* Increase the bios tick counter */ + Bit32u value = mem_readd(BIOS_TIMER) + 1; + if(value >= 0x1800B0) { + // time wrap at midnight + mem_writeb(BIOS_24_HOURS_FLAG,mem_readb(BIOS_24_HOURS_FLAG)+1); + value=0; + } + + /* Legacy BIOS behavior: This isn't documented at all but most BIOSes + check the BIOS data area for LED keyboard status. If it sees that + value change, then it sends it to the keyboard. This is why on + older DOS machines you could change LEDs by writing to 40:17. + We have to emulate this also because Windows 3.1/9x seems to rely on + it when handling the keyboard from it's own driver. Their driver does + hook the keyboard and handles keyboard I/O by itself, but it still + allows the BIOS to do the keyboard magic from IRQ 0 (INT 8h). Yech. */ + if (enable_bios_timer_synchronize_keyboard_leds) { + Bitu should_be = (mem_readb(BIOS_KEYBOARD_STATE) >> 4) & 7; + Bitu led_state = (mem_readb(BIOS_KEYBOARD_LEDS) & 7); + + if (should_be != led_state) + BIOS_KEYBOARD_SetLEDs(should_be); + } + +#if DOSBOX_CLOCKSYNC + static bool check = false; + if((value %50)==0) { + if(((value %100)==0) && check) { + check = false; + time_t curtime;struct tm *loctime; + curtime = time (NULL);loctime = localtime (&curtime); + Bit32u ticksnu = (Bit32u)((loctime->tm_hour*3600+loctime->tm_min*60+loctime->tm_sec)*(float)PIT_TICK_RATE/65536.0); + Bit32s bios = value;Bit32s tn = ticksnu; + Bit32s diff = tn - bios; + if(diff>0) { + if(diff < 18) { diff = 0; } else diff = 9; + } else { + if(diff > -18) { diff = 0; } else diff = -9; + } + + value += diff; + } else if((value%100)==50) check = true; + } +#endif + mem_writed(BIOS_TIMER,value); + + /* decrease floppy motor timer */ + Bit8u val = mem_readb(BIOS_DISK_MOTOR_TIMEOUT); + if (val) mem_writeb(BIOS_DISK_MOTOR_TIMEOUT,val-1); + /* and running drive */ + mem_writeb(BIOS_DRIVE_RUNNING,mem_readb(BIOS_DRIVE_RUNNING) & 0xF0); + return CBRET_NONE; +} +#undef DOSBOX_CLOCKSYNC + +static Bitu INT1C_Handler(void) { + return CBRET_NONE; +} + +static Bitu INT12_Handler(void) { + reg_ax=mem_readw(BIOS_MEMORY_SIZE); + return CBRET_NONE; +} + +static Bitu INT17_Handler(void) { + if (reg_ah > 0x2 || reg_dx > 0x2) { // 0-2 printer port functions + // and no more than 3 parallel ports + LOG_MSG("BIOS INT17: Unhandled call AH=%2X DX=%4x",reg_ah,reg_dx); + return CBRET_NONE; + } + + switch(reg_ah) { + case 0x00: // PRINTER: Write Character + if(parallelPortObjects[reg_dx]!=0) { + if(parallelPortObjects[reg_dx]->Putchar(reg_al)) + reg_ah=parallelPortObjects[reg_dx]->getPrinterStatus(); + else reg_ah=1; + } + break; + case 0x01: // PRINTER: Initialize port + if(parallelPortObjects[reg_dx]!= 0) { + parallelPortObjects[reg_dx]->initialize(); + reg_ah=parallelPortObjects[reg_dx]->getPrinterStatus(); + } + break; + case 0x02: // PRINTER: Get Status + if(parallelPortObjects[reg_dx] != 0) + reg_ah=parallelPortObjects[reg_dx]->getPrinterStatus(); + //LOG_MSG("printer status: %x",reg_ah); + break; + }; + return CBRET_NONE; +} + +static bool INT14_Wait(Bit16u port, Bit8u mask, Bit8u timeout, Bit8u* retval) { + double starttime = PIC_FullIndex(); + double timeout_f = timeout * 1000.0; + while (((*retval = IO_ReadB(port)) & mask) != mask) { + if (starttime < (PIC_FullIndex() - timeout_f)) { + return false; + } + CALLBACK_Idle(); + } + return true; +} + +static Bitu INT4B_Handler(void) { + /* TODO: This is where the Virtual DMA specification is accessed on modern systems. + * When we implement that, move this to EMM386 emulation code. */ + + if (reg_ax >= 0x8102 && reg_ax <= 0x810D) { + LOG(LOG_MISC,LOG_DEBUG)("Guest OS attempted Virtual DMA specification call (INT 4Bh AX=%04x BX=%04x CX=%04x DX=%04x", + reg_ax,reg_bx,reg_cx,reg_dx); + } + else if (reg_ah == 0x80) { + LOG(LOG_MISC,LOG_DEBUG)("Guest OS attempted IBM SCSI interface call"); + } + else if (reg_ah <= 0x02) { + LOG(LOG_MISC,LOG_DEBUG)("Guest OS attempted TI Professional PC parallel port function AH=%02x",reg_ah); + } + else { + LOG(LOG_MISC,LOG_DEBUG)("Guest OS attempted unknown INT 4Bh call AX=%04x",reg_ax); + } + + /* Oh, I'm just a BIOS that doesn't know what the hell you're doing. CF=1 */ + CALLBACK_SCF(true); + return CBRET_NONE; +} + +static Bitu INT14_Handler(void) { + if (reg_ah > 0x3 || reg_dx > 0x3) { // 0-3 serial port functions + // and no more than 4 serial ports + LOG_MSG("BIOS INT14: Unhandled call AH=%2X DX=%4x",reg_ah,reg_dx); + return CBRET_NONE; + } + + Bit16u port = real_readw(0x40,reg_dx * 2u); // DX is always port number + Bit8u timeout = mem_readb((PhysPt)((unsigned int)BIOS_COM1_TIMEOUT + (unsigned int)reg_dx)); + if (port==0) { + LOG(LOG_BIOS,LOG_NORMAL)("BIOS INT14: port %d does not exist.",reg_dx); + return CBRET_NONE; + } + switch (reg_ah) { + case 0x00: { + // Initialize port + // Parameters: Return: + // AL: port parameters AL: modem status + // AH: line status + + // set baud rate + Bitu baudrate = 9600u; + Bit16u baudresult; + Bitu rawbaud=(Bitu)reg_al>>5u; + + if (rawbaud==0){ baudrate=110u;} + else if (rawbaud==1){ baudrate=150u;} + else if (rawbaud==2){ baudrate=300u;} + else if (rawbaud==3){ baudrate=600u;} + else if (rawbaud==4){ baudrate=1200u;} + else if (rawbaud==5){ baudrate=2400u;} + else if (rawbaud==6){ baudrate=4800u;} + else if (rawbaud==7){ baudrate=9600u;} + + baudresult = (Bit16u)(115200u / baudrate); + + IO_WriteB(port+3u, 0x80u); // enable divider access + IO_WriteB(port, (Bit8u)baudresult&0xffu); + IO_WriteB(port+1u, (Bit8u)(baudresult>>8u)); + + // set line parameters, disable divider access + IO_WriteB(port+3u, reg_al&0x1Fu); // LCR + + // disable interrupts + IO_WriteB(port+1u, 0u); // IER + + // get result + reg_ah=(Bit8u)(IO_ReadB(port+5u)&0xffu); + reg_al=(Bit8u)(IO_ReadB(port+6u)&0xffu); + CALLBACK_SCF(false); + break; + } + case 0x01: // Transmit character + // Parameters: Return: + // AL: character AL: unchanged + // AH: 0x01 AH: line status from just before the char was sent + // (0x80 | unpredicted) in case of timeout + // [undoc] (0x80 | line status) in case of tx timeout + // [undoc] (0x80 | modem status) in case of dsr/cts timeout + + // set DTR & RTS on + IO_WriteB(port+4u,0x3u); + // wait for DSR & CTS + if (INT14_Wait(port+6u, 0x30u, timeout, ®_ah)) { + // wait for TX buffer empty + if (INT14_Wait(port+5u, 0x20u, timeout, ®_ah)) { + // fianlly send the character + IO_WriteB(port,reg_al); + } else + reg_ah |= 0x80u; + } else // timed out + reg_ah |= 0x80u; + + CALLBACK_SCF(false); + break; + case 0x02: // Read character + // Parameters: Return: + // AH: 0x02 AL: received character + // [undoc] will be trashed in case of timeout + // AH: (line status & 0x1E) in case of success + // (0x80 | unpredicted) in case of timeout + // [undoc] (0x80 | line status) in case of rx timeout + // [undoc] (0x80 | modem status) in case of dsr timeout + + // set DTR on + IO_WriteB(port+4u,0x1u); + + // wait for DSR + if (INT14_Wait(port+6, 0x20, timeout, ®_ah)) { + // wait for character to arrive + if (INT14_Wait(port+5, 0x01, timeout, ®_ah)) { + reg_ah &= 0x1E; + reg_al = IO_ReadB(port); + } else + reg_ah |= 0x80; + } else + reg_ah |= 0x80; + + CALLBACK_SCF(false); + break; + case 0x03: // get status + reg_ah=(Bit8u)(IO_ReadB(port+5u)&0xffu); + reg_al=(Bit8u)(IO_ReadB(port+6u)&0xffu); + CALLBACK_SCF(false); + break; + + } + return CBRET_NONE; +} + +Bits HLT_Decode(void); +void KEYBOARD_AUX_Write(Bitu val); +unsigned char KEYBOARD_AUX_GetType(); +unsigned char KEYBOARD_AUX_DevStatus(); +unsigned char KEYBOARD_AUX_Resolution(); +unsigned char KEYBOARD_AUX_SampleRate(); +void KEYBOARD_ClrBuffer(void); + +static Bitu INT15_Handler(void) { + if( ( machine==MCH_AMSTRAD ) && ( reg_ah<0x07 ) ) { + switch(reg_ah) { + case 0x00: + // Read/Reset Mouse X/Y Counts. + // CX = Signed X Count. + // DX = Signed Y Count. + // CC. + case 0x01: + // Write NVR Location. + // AL = NVR Address to be written (0-63). + // BL = NVR Data to be written. + + // AH = Return Code. + // 00 = NVR Written Successfully. + // 01 = NVR Address out of range. + // 02 = NVR Data write error. + // CC. + case 0x02: + // Read NVR Location. + // AL = NVR Address to be read (0-63). + + // AH = Return Code. + // 00 = NVR read successfully. + // 01 = NVR Address out of range. + // 02 = NVR checksum error. + // AL = Byte read from NVR. + // CC. + default: + LOG(LOG_BIOS,LOG_NORMAL)("INT15 Unsupported PC1512 Call %02X",reg_ah); + return CBRET_NONE; + case 0x03: + // Write VDU Colour Plane Write Register. + vga.amstrad.write_plane = reg_al & 0x0F; + CALLBACK_SCF(false); + break; + case 0x04: + // Write VDU Colour Plane Read Register. + vga.amstrad.read_plane = reg_al & 0x03; + CALLBACK_SCF(false); + break; + case 0x05: + // Write VDU Graphics Border Register. + vga.amstrad.border_color = reg_al & 0x0F; + CALLBACK_SCF(false); + break; + case 0x06: + // Return ROS Version Number. + reg_bx = 0x0001; + CALLBACK_SCF(false); + break; + } + } + switch (reg_ah) { + case 0x06: + LOG(LOG_BIOS,LOG_NORMAL)("INT15 Unkown Function 6 (Amstrad?)"); + break; + case 0xC0: /* Get Configuration*/ + CPU_SetSegGeneral(es,biosConfigSeg); + reg_bx = 0; + reg_ah = 0; + CALLBACK_SCF(false); + break; + case 0x4f: /* BIOS - Keyboard intercept */ + /* Carry should be set but let's just set it just in case */ + CALLBACK_SCF(true); + break; + case 0x83: /* BIOS - SET EVENT WAIT INTERVAL */ + { + if(reg_al == 0x01) { /* Cancel it */ + mem_writeb(BIOS_WAIT_FLAG_ACTIVE,0); + IO_Write(0x70,0xb); + IO_Write(0x71,IO_Read(0x71)&~0x40); + CALLBACK_SCF(false); + break; + } + if (mem_readb(BIOS_WAIT_FLAG_ACTIVE)) { + reg_ah=0x80; + CALLBACK_SCF(true); + break; + } + Bit32u count=((Bit32u)reg_cx<<16u)|reg_dx; + mem_writed(BIOS_WAIT_FLAG_POINTER,RealMake(SegValue(es),reg_bx)); + mem_writed(BIOS_WAIT_FLAG_COUNT,count); + mem_writeb(BIOS_WAIT_FLAG_ACTIVE,1); + /* Reprogram RTC to start */ + IO_Write(0x70,0xb); + IO_Write(0x71,IO_Read(0x71)|0x40); + CALLBACK_SCF(false); + } + break; + case 0x84: /* BIOS - JOYSTICK SUPPORT (XT after 11/8/82,AT,XT286,PS) */ + if (reg_dx == 0x0000) { + // Get Joystick button status + if (JOYSTICK_IsEnabled(0) || JOYSTICK_IsEnabled(1)) { + reg_al = IO_ReadB(0x201)&0xf0; + CALLBACK_SCF(false); + } else { + // dos values + reg_ax = 0x00f0; reg_dx = 0x0201; + CALLBACK_SCF(true); + } + } else if (reg_dx == 0x0001) { + if (JOYSTICK_IsEnabled(0)) { + reg_ax = (Bit16u)(JOYSTICK_GetMove_X(0)*127+128); + reg_bx = (Bit16u)(JOYSTICK_GetMove_Y(0)*127+128); + if(JOYSTICK_IsEnabled(1)) { + reg_cx = (Bit16u)(JOYSTICK_GetMove_X(1)*127+128); + reg_dx = (Bit16u)(JOYSTICK_GetMove_Y(1)*127+128); + } + else { + reg_cx = reg_dx = 0; + } + CALLBACK_SCF(false); + } else if (JOYSTICK_IsEnabled(1)) { + reg_ax = reg_bx = 0; + reg_cx = (Bit16u)(JOYSTICK_GetMove_X(1)*127+128); + reg_dx = (Bit16u)(JOYSTICK_GetMove_Y(1)*127+128); + CALLBACK_SCF(false); + } else { + reg_ax = reg_bx = reg_cx = reg_dx = 0; + CALLBACK_SCF(true); + } + } else { + LOG(LOG_BIOS,LOG_ERROR)("INT15:84:Unknown Bios Joystick functionality."); + } + break; + case 0x86: /* BIOS - WAIT (AT,PS) */ + { + if (mem_readb(BIOS_WAIT_FLAG_ACTIVE)) { + reg_ah=0x83; + CALLBACK_SCF(true); + break; + } + Bit8u t; + Bit32u count=((Bit32u)reg_cx<<16u)|reg_dx; + mem_writed(BIOS_WAIT_FLAG_POINTER,RealMake(0,BIOS_WAIT_FLAG_TEMP)); + mem_writed(BIOS_WAIT_FLAG_COUNT,count); + mem_writeb(BIOS_WAIT_FLAG_ACTIVE,1); + + /* if the user has not set the option, warn if IRQs are masked */ + if (!int15_wait_force_unmask_irq) { + /* make sure our wait function works by unmasking IRQ 2, and IRQ 8. + * (bugfix for 1993 demo Yodel "Mayday" demo. this demo keeps masking IRQ 2 for some stupid reason.) */ + if ((t=IO_Read(0x21)) & (1 << 2)) { + LOG(LOG_BIOS,LOG_ERROR)("INT15:86:Wait: IRQ 2 masked during wait. " + "Consider adding 'int15 wait force unmask irq=true' to your dosbox.conf"); + } + if ((t=IO_Read(0xA1)) & (1 << 0)) { + LOG(LOG_BIOS,LOG_ERROR)("INT15:86:Wait: IRQ 8 masked during wait. " + "Consider adding 'int15 wait force unmask irq=true' to your dosbox.conf"); + } + } + + /* Reprogram RTC to start */ + IO_Write(0x70,0xb); + IO_Write(0x71,IO_Read(0x71)|0x40); + while (mem_readd(BIOS_WAIT_FLAG_COUNT)) { + if (int15_wait_force_unmask_irq) { + /* make sure our wait function works by unmasking IRQ 2, and IRQ 8. + * (bugfix for 1993 demo Yodel "Mayday" demo. this demo keeps masking IRQ 2 for some stupid reason.) */ + if ((t=IO_Read(0x21)) & (1 << 2)) { + LOG(LOG_BIOS,LOG_WARN)("INT15:86:Wait: IRQ 2 masked during wait. " + "This condition might result in an infinite wait on " + "some BIOSes. Unmasking IRQ to keep things moving along."); + IO_Write(0x21,t & ~(1 << 2)); + + } + if ((t=IO_Read(0xA1)) & (1 << 0)) { + LOG(LOG_BIOS,LOG_WARN)("INT15:86:Wait: IRQ 8 masked during wait. " + "This condition might result in an infinite wait on some " + "BIOSes. Unmasking IRQ to keep things moving along."); + IO_Write(0xA1,t & ~(1 << 0)); + } + } + + CALLBACK_Idle(); + } + CALLBACK_SCF(false); + break; + } + case 0x87: /* Copy extended memory */ + { + bool enabled = MEM_A20_Enabled(); + MEM_A20_Enable(true); + Bitu bytes = reg_cx * 2u; + PhysPt data = SegPhys(es)+reg_si; + PhysPt source = (mem_readd(data+0x12u) & 0x00FFFFFFu) + ((unsigned int)mem_readb(data+0x17u)<<24u); + PhysPt dest = (mem_readd(data+0x1Au) & 0x00FFFFFFu) + ((unsigned int)mem_readb(data+0x1Fu)<<24u); + MEM_BlockCopy(dest,source,bytes); + reg_ax = 0x00; + MEM_A20_Enable(enabled); + Segs.limit[cs] = 0xFFFF; + Segs.limit[ds] = 0xFFFF; + Segs.limit[es] = 0xFFFF; + Segs.limit[ss] = 0xFFFF; + CALLBACK_SCF(false); + break; + } + case 0x88: /* SYSTEM - GET EXTENDED MEMORY SIZE (286+) */ + /* This uses the 16-bit value read back from CMOS which is capped at 64MB */ + reg_ax=other_memsystems?0:size_extended; + LOG(LOG_BIOS,LOG_NORMAL)("INT15:Function 0x88 Remaining %04X kb",reg_ax); + CALLBACK_SCF(false); + break; + case 0x89: /* SYSTEM - SWITCH TO PROTECTED MODE */ + { + IO_Write(0x20,0x10);IO_Write(0x21,reg_bh);IO_Write(0x21,0);IO_Write(0x21,0xFF); + IO_Write(0xA0,0x10);IO_Write(0xA1,reg_bl);IO_Write(0xA1,0);IO_Write(0xA1,0xFF); + MEM_A20_Enable(true); + PhysPt table=SegPhys(es)+reg_si; + CPU_LGDT(mem_readw(table+0x8),mem_readd(table+0x8+0x2) & 0xFFFFFF); + CPU_LIDT(mem_readw(table+0x10),mem_readd(table+0x10+0x2) & 0xFFFFFF); + CPU_SET_CRX(0,CPU_GET_CRX(0)|1); + CPU_SetSegGeneral(ds,0x18); + CPU_SetSegGeneral(es,0x20); + CPU_SetSegGeneral(ss,0x28); + Bitu ret = mem_readw(SegPhys(ss)+reg_sp); + reg_sp+=6; //Clear stack of interrupt frame + CPU_SetFlags(0,FMASK_ALL); + reg_ax=0; + CPU_JMP(false,0x30,ret,0); + } + break; + case 0x8A: /* EXTENDED MEMORY SIZE */ + { + Bitu sz = MEM_TotalPages()*4; + if (sz >= 1024) sz -= 1024; + else sz = 0; + reg_ax = sz & 0xFFFF; + reg_dx = sz >> 16; + CALLBACK_SCF(false); + } + break; + case 0x90: /* OS HOOK - DEVICE BUSY */ + CALLBACK_SCF(false); + reg_ah=0; + break; + case 0x91: /* OS HOOK - DEVICE POST */ + CALLBACK_SCF(false); + reg_ah=0; + break; + case 0xc2: /* BIOS PS2 Pointing Device Support */ + /* TODO: Our reliance on AUX emulation means that at some point, AUX emulation + * must always be enabled if BIOS PS/2 emulation is enabled. Future planned change: + * + * If biosps2=true and aux=true, carry on what we're already doing now: emulate INT 15h by + * directly writing to the AUX port of the keyboard controller. + * + * If biosps2=false, the aux= setting enables/disables AUX emulation as it already does now. + * biosps2=false implies that we're emulating a keyboard controller with AUX but no BIOS + * support for it (however rare that might be). This gives the user of DOSBox-X the means + * to test that scenario especially in case he/she is developing a homebrew OS and needs + * to ensure their code can handle cases like that gracefully. + * + * If biosps2=true and aux=false, AUX emulation is enabled anyway, but the keyboard emulation + * must act as if the AUX port is not there so the guest OS cannot control it. Again, not + * likely on real hardware, but a useful test case for homebrew OS developers. + * + * If you the user set aux=false, then you obviously want to test a system configuration + * where the keyboard controller has no AUX port. If you set biosps2=true, then you want to + * test an OS that uses BIOS functions to setup the "pointing device" but you do not want the + * guest OS to talk directly to the AUX port on the keyboard controller. + * + * Logically that's not likely to happen on real hardware, but we like giving the end-user + * options to play with, so instead, if aux=false and biosps2=true, DOSBox-X should print + * a warning stating that INT 15h mouse emulation with a PS/2 port is nonstandard and may + * cause problems with OSes that need to talk directly to hardware. + * + * It is noteworthy that PS/2 mouse support in MS-DOS mouse drivers as well as Windows 3.x, + * Windows 95, and Windows 98, is carried out NOT by talking directly to the AUX port but + * instead by relying on the BIOS INT 15h functions here to do the dirty work. For those + * scenarios, biosps2=true and aux=false is perfectly safe and does not cause issues. + * + * OSes that communicate directly with the AUX port however (Linux, Windows NT) will not work + * unless aux=true. */ + if (en_bios_ps2mouse) { +// LOG_MSG("INT 15h AX=%04x BX=%04x\n",reg_ax,reg_bx); + switch (reg_al) { + case 0x00: // enable/disable + if (reg_bh==0) { // disable + KEYBOARD_AUX_Write(0xF5); + Mouse_SetPS2State(false); + reg_ah=0; + CALLBACK_SCF(false); + KEYBOARD_ClrBuffer(); + } else if (reg_bh==0x01) { //enable + if (!Mouse_SetPS2State(true)) { + reg_ah=5; + CALLBACK_SCF(true); + break; + } + KEYBOARD_AUX_Write(0xF4); + KEYBOARD_ClrBuffer(); + reg_ah=0; + CALLBACK_SCF(false); + } else { + CALLBACK_SCF(true); + reg_ah=1; + } + break; + case 0x01: // reset + KEYBOARD_AUX_Write(0xFF); + Mouse_SetPS2State(false); + KEYBOARD_ClrBuffer(); + reg_bx=0x00aa; // mouse + // fall through + case 0x05: // initialize + if (reg_bh >= 3 && reg_bh <= 4) { + /* TODO: BIOSes remember this value as the number of bytes to store before + * calling the device callback. Setting this value to "1" is perfectly + * valid if you want a byte-stream like mode (at the cost of one + * interrupt per byte!). Most OSes will call this with BH=3 for standard + * PS/2 mouse protocol. You can also call this with BH=4 for Intellimouse + * protocol support, though testing (so far with VirtualBox) shows the + * device callback still only gets the first three bytes on the stack. + * Contrary to what you might think, the BIOS does not interpret the + * bytes at all. + * + * The source code of CuteMouse 1.9 seems to suggest some BIOSes take + * pains to repack the 4th byte in the upper 8 bits of one of the WORDs + * on the stack in Intellimouse mode at the cost of shifting the W and X + * fields around. I can't seem to find any source on who does that or + * if it's even true, so I disregard the example at this time. + * + * Anyway, you need to store off this value somewhere and make use of + * it in src/ints/mouse.cpp device callback emulation to reframe the + * PS/2 mouse bytes coming from AUX (if aux=true) or emulate the + * re-framing if aux=false to emulate this protocol fully. */ + LOG_MSG("INT 15h mouse initialized to %u-byte protocol\n",reg_bh); + KEYBOARD_AUX_Write(0xF6); /* set defaults */ + Mouse_SetPS2State(false); + KEYBOARD_ClrBuffer(); + CALLBACK_SCF(false); + reg_ah=0; + } + else { + CALLBACK_SCF(false); + reg_ah=0x02; /* invalid input */ + } + break; + case 0x02: { // set sampling rate + static const unsigned char tbl[7] = {10,20,40,60,80,100,200}; + KEYBOARD_AUX_Write(0xF3); + if (reg_bl > 6) reg_bl = 6; + KEYBOARD_AUX_Write(tbl[reg_bh]); + KEYBOARD_ClrBuffer(); + CALLBACK_SCF(false); + reg_ah=0; + } break; + case 0x03: // set resolution + KEYBOARD_AUX_Write(0xE8); + KEYBOARD_AUX_Write(reg_bh&3); + KEYBOARD_ClrBuffer(); + CALLBACK_SCF(false); + reg_ah=0; + break; + case 0x04: // get type + reg_bh=KEYBOARD_AUX_GetType(); // ID + LOG_MSG("INT 15h reporting mouse device ID 0x%02x\n",reg_bh); + KEYBOARD_ClrBuffer(); + CALLBACK_SCF(false); + reg_ah=0; + break; + case 0x06: // extended commands + if (reg_bh == 0x00) { + /* Read device status and info. + * Windows 9x does not appear to use this, but Windows NT 3.1 does (prior to entering + * full 32-bit protected mode) */ + CALLBACK_SCF(false); + reg_bx=KEYBOARD_AUX_DevStatus(); + reg_cx=KEYBOARD_AUX_Resolution(); + reg_dx=KEYBOARD_AUX_SampleRate(); + KEYBOARD_ClrBuffer(); + reg_ah=0; + } + else if ((reg_bh==0x01) || (reg_bh==0x02)) { /* set scaling */ + KEYBOARD_AUX_Write(0xE6u+reg_bh-1u); /* 0xE6 1:1 or 0xE7 2:1 */ + KEYBOARD_ClrBuffer(); + CALLBACK_SCF(false); + reg_ah=0; + } else { + CALLBACK_SCF(true); + reg_ah=1; + } + break; + case 0x07: // set callback + Mouse_ChangePS2Callback(SegValue(es),reg_bx); + CALLBACK_SCF(false); + reg_ah=0; + break; + default: + LOG_MSG("INT 15h unknown mouse call AX=%04x\n",reg_ax); + CALLBACK_SCF(true); + reg_ah=1; + break; + } + } + else { + reg_ah=0x86; + CALLBACK_SCF(true); + if ((IS_EGAVGA_ARCH) || (machine==MCH_CGA)) { + /* relict from comparisons, as int15 exits with a retf2 instead of an iret */ + CALLBACK_SZF(false); + } + } + break; + case 0xc3: /* set carry flag so BorlandRTM doesn't assume a VECTRA/PS2 */ + reg_ah=0x86; + CALLBACK_SCF(true); + break; + case 0xc4: /* BIOS POS Programm option Select */ + LOG(LOG_BIOS,LOG_NORMAL)("INT15:Function %X called, bios mouse not supported",reg_ah); + CALLBACK_SCF(true); + break; + case 0x53: // APM BIOS + if (APMBIOS) { + LOG(LOG_BIOS,LOG_DEBUG)("APM BIOS call AX=%04x BX=0x%04x CX=0x%04x\n",reg_ax,reg_bx,reg_cx); + switch(reg_al) { + case 0x00: // installation check + reg_ah = 1; // major + reg_al = APM_BIOS_minor_version; // minor + reg_bx = 0x504d; // 'PM' + reg_cx = (APMBIOS_allow_prot16?0x01:0x00) + (APMBIOS_allow_prot32?0x02:0x00); + // 32-bit interface seems to be needed for standby in win95 + CALLBACK_SCF(false); + break; + case 0x01: // connect real mode interface + if(!APMBIOS_allow_realmode) { + LOG_MSG("APM BIOS: OS attemped real-mode connection, which is disabled in your dosbox.conf\n"); + reg_ah = 0x86; // APM not present + CALLBACK_SCF(true); + break; + } + if(reg_bx != 0x0) { + reg_ah = 0x09; // unrecognized device ID + CALLBACK_SCF(true); + break; + } + if(!apm_realmode_connected) { // not yet connected + LOG_MSG("APM BIOS: Connected to real-mode interface\n"); + CALLBACK_SCF(false); + APMBIOS_connect_mode = APMBIOS_CONNECT_REAL; + apm_realmode_connected=true; + } else { + LOG_MSG("APM BIOS: OS attempted to connect to real-mode interface when already connected\n"); + reg_ah = APMBIOS_connected_already_err(); // interface connection already in effect + CALLBACK_SCF(true); + } + APM_BIOS_connected_minor_version = 0; + break; + case 0x02: // connect 16-bit protected mode interface + if(!APMBIOS_allow_prot16) { + LOG_MSG("APM BIOS: OS attemped 16-bit protected mode connection, which is disabled in your dosbox.conf\n"); + reg_ah = 0x06; // not supported + CALLBACK_SCF(true); + break; + } + if(reg_bx != 0x0) { + reg_ah = 0x09; // unrecognized device ID + CALLBACK_SCF(true); + break; + } + if(!apm_realmode_connected) { // not yet connected + /* NTS: We use the same callback address for both 16-bit and 32-bit + * because only the DOS callback and RETF instructions are involved, + * which can be executed as either 16-bit or 32-bit code without problems. */ + LOG_MSG("APM BIOS: Connected to 16-bit protected mode interface\n"); + CALLBACK_SCF(false); + reg_ax = INT15_apm_pmentry >> 16; // AX = 16-bit code segment (real mode base) + reg_bx = INT15_apm_pmentry & 0xFFFF; // BX = offset of entry point + reg_cx = INT15_apm_pmentry >> 16; // CX = 16-bit data segment (NTS: doesn't really matter) + reg_si = 0xFFFF; // SI = code segment length + reg_di = 0xFFFF; // DI = data segment length + APMBIOS_connect_mode = APMBIOS_CONNECT_PROT16; + apm_realmode_connected=true; + } else { + LOG_MSG("APM BIOS: OS attempted to connect to 16-bit protected mode interface when already connected\n"); + reg_ah = APMBIOS_connected_already_err(); // interface connection already in effect + CALLBACK_SCF(true); + } + APM_BIOS_connected_minor_version = 0; + break; + case 0x03: // connect 32-bit protected mode interface + // Note that Windows 98 will NOT talk to the APM BIOS unless the 32-bit protected mode connection is available. + // And if you lie about it in function 0x00 and then fail, Windows 98 will fail with a "Windows protection error". + if(!APMBIOS_allow_prot32) { + LOG_MSG("APM BIOS: OS attemped 32-bit protected mode connection, which is disabled in your dosbox.conf\n"); + reg_ah = 0x08; // not supported + CALLBACK_SCF(true); + break; + } + if(reg_bx != 0x0) { + reg_ah = 0x09; // unrecognized device ID + CALLBACK_SCF(true); + break; + } + if(!apm_realmode_connected) { // not yet connected + LOG_MSG("APM BIOS: Connected to 32-bit protected mode interface\n"); + CALLBACK_SCF(false); + /* NTS: We use the same callback address for both 16-bit and 32-bit + * because only the DOS callback and RETF instructions are involved, + * which can be executed as either 16-bit or 32-bit code without problems. */ + reg_ax = INT15_apm_pmentry >> 16; // AX = 32-bit code segment (real mode base) + reg_ebx = INT15_apm_pmentry & 0xFFFF; // EBX = offset of entry point + reg_cx = INT15_apm_pmentry >> 16; // CX = 16-bit code segment (real mode base) + reg_dx = INT15_apm_pmentry >> 16; // DX = data segment (real mode base) (?? what size?) + reg_esi = 0xFFFFFFFF; // ESI = upper word: 16-bit code segment len lower word: 32-bit code segment length + reg_di = 0xFFFF; // DI = data segment length + APMBIOS_connect_mode = APMBIOS_CONNECT_PROT32; + apm_realmode_connected=true; + } else { + LOG_MSG("APM BIOS: OS attempted to connect to 32-bit protected mode interface when already connected\n"); + reg_ah = APMBIOS_connected_already_err(); // interface connection already in effect + CALLBACK_SCF(true); + } + APM_BIOS_connected_minor_version = 0; + break; + case 0x04: // DISCONNECT INTERFACE + if(reg_bx != 0x0) { + reg_ah = 0x09; // unrecognized device ID + CALLBACK_SCF(true); + break; + } + if(apm_realmode_connected) { + LOG_MSG("APM BIOS: OS disconnected\n"); + CALLBACK_SCF(false); + apm_realmode_connected=false; + } else { + reg_ah = 0x03; // interface not connected + CALLBACK_SCF(true); + } + APM_BIOS_connected_minor_version = 0; + break; + case 0x05: // CPU IDLE + if(!apm_realmode_connected) { + reg_ah = 0x03; + CALLBACK_SCF(true); + break; + } + + // Trigger CPU HLT instruction. + // NTS: For whatever weird reason, NOT emulating HLT makes Windows 95 + // crashy when the APM driver is active! There's something within + // the Win95 kernel that apparently screws up really badly if + // the APM IDLE call returns immediately. The best case scenario + // seems to be that Win95's APM driver has some sort of timing + // logic to it that if it detects an immediate return, immediately + // shuts down and powers off the machine. Windows 98 also seems + // to require a HLT, and will act erratically without it. + // + // Also need to note that the choice of "HLT" is not arbitrary + // at all. The APM BIOS standard mentions CPU IDLE either stopping + // the CPU clock temporarily or issuing HLT as a valid method. + // + // TODO: Make this a dosbox.conf configuration option: what do we do + // on APM idle calls? Allow selection between "nothing" "hlt" + // and "software delay". + if (!(reg_flags&0x200)) { + LOG_MSG("APM BIOS warning: CPU IDLE called with IF=0, not HLTing\n"); + } + else if (cpudecoder == &HLT_Decode) { /* do not re-execute HLT, it makes DOSBox hang */ + LOG_MSG("APM BIOS warning: CPU IDLE HLT within HLT (DOSBox core failure)\n"); + } + else { + CPU_HLT(reg_eip); + } + break; + case 0x06: // CPU BUSY + if(!apm_realmode_connected) { + reg_ah = 0x03; + CALLBACK_SCF(true); + break; + } + + /* OK. whatever. system no longer idle */ + CALLBACK_SCF(false); + break; + case 0x07: + if(reg_bx != 0x1) { + reg_ah = 0x09; // wrong device ID + CALLBACK_SCF(true); + break; + } + if(!apm_realmode_connected) { + reg_ah = 0x03; + CALLBACK_SCF(true); + break; + } + switch(reg_cx) { + case 0x3: // power off + throw(0); + break; + default: + reg_ah = 0x0A; // invalid parameter value in CX + CALLBACK_SCF(true); + break; + } + break; + case 0x08: // ENABLE/DISABLE POWER MANAGEMENT + if(reg_bx != 0x0 && reg_bx != 0x1) { + reg_ah = 0x09; // unrecognized device ID + CALLBACK_SCF(true); + break; + } else if(!apm_realmode_connected) { + reg_ah = 0x03; + CALLBACK_SCF(true); + break; + } + if(reg_cx==0x0) LOG_MSG("disable APM for device %4x",reg_bx); + else if(reg_cx==0x1) LOG_MSG("enable APM for device %4x",reg_bx); + else { + reg_ah = 0x0A; // invalid parameter value in CX + CALLBACK_SCF(true); + } + break; + case 0x0a: // GET POWER STATUS + if (!apm_realmode_connected) { + reg_ah = 0x03; // interface not connected + CALLBACK_SCF(true); + break; + } + if (reg_bx != 0x0001 && reg_bx != 0x8001) { + reg_ah = 0x09; // unrecognized device ID + CALLBACK_SCF(true); + break; + } + /* FIXME: Allow configuration and shell commands to dictate whether or + * not we emulate a laptop with a battery */ + reg_bh = 0x01; // AC line status (1=on-line) + reg_bl = 0xFF; // Battery status (unknown) + reg_ch = 0x80; // Battery flag (no system battery) + reg_cl = 0xFF; // Remaining battery charge (unknown) + reg_dx = 0xFFFF; // Remaining battery life (unknown) + reg_si = 0; // Number of battery units (if called with reg_bx == 0x8001) + CALLBACK_SCF(false); + break; + case 0x0b: // GET PM EVENT + if (!apm_realmode_connected) { + reg_ah = 0x03; // interface not connected + CALLBACK_SCF(true); + break; + } + reg_ah = 0x80; // no power management events pending + CALLBACK_SCF(true); + break; + case 0x0d: + // NTS: NOT implementing this call can cause Windows 98's APM driver to crash on startup + if(reg_bx != 0x0 && reg_bx != 0x1) { + reg_ah = 0x09; // unrecognized device ID + CALLBACK_SCF(true); + break; + } else if(!apm_realmode_connected) { + reg_ah = 0x03; + CALLBACK_SCF(true); + break; + } + if(reg_cx==0x0) { + LOG_MSG("disable APM for device %4x",reg_bx); + CALLBACK_SCF(false); + } + else if(reg_cx==0x1) { + LOG_MSG("enable APM for device %4x",reg_bx); + CALLBACK_SCF(false); + } + else { + reg_ah = 0x0A; // invalid parameter value in CX + CALLBACK_SCF(true); + } + break; + case 0x0e: + if (APM_BIOS_minor_version != 0) { // APM 1.1 or higher only + if(reg_bx != 0x0) { + reg_ah = 0x09; // unrecognized device ID + CALLBACK_SCF(true); + break; + } else if(!apm_realmode_connected) { + reg_ah = 0x03; // interface not connected + CALLBACK_SCF(true); + break; + } + reg_ah = reg_ch; /* we are called with desired version in CH,CL, return actual version in AH,AL */ + reg_al = reg_cl; + if(reg_ah != 1) reg_ah = 1; // major + if(reg_al > APM_BIOS_minor_version) reg_al = APM_BIOS_minor_version; // minor + APM_BIOS_connected_minor_version = reg_al; // what we decided becomes the interface we emulate + LOG_MSG("APM BIOS negotiated to v1.%u",APM_BIOS_connected_minor_version); + CALLBACK_SCF(false); + } + else { // APM 1.0 does not recognize this call + reg_ah = 0x0C; // function not supported + CALLBACK_SCF(true); + } + break; + case 0x0f: + if(reg_bx != 0x0 && reg_bx != 0x1) { + reg_ah = 0x09; // unrecognized device ID + CALLBACK_SCF(true); + break; + } else if(!apm_realmode_connected) { + reg_ah = 0x03; + CALLBACK_SCF(true); + break; + } + if(reg_cx==0x0) { + LOG_MSG("disengage APM for device %4x",reg_bx); + CALLBACK_SCF(false); + } + else if(reg_cx==0x1) { + LOG_MSG("engage APM for device %4x",reg_bx); + CALLBACK_SCF(false); + } + else { + reg_ah = 0x0A; // invalid parameter value in CX + CALLBACK_SCF(true); + } + break; + case 0x10: + if (!apm_realmode_connected) { + reg_ah = 0x03; // interface not connected + CALLBACK_SCF(true); + break; + } + if (reg_bx != 0) { + reg_ah = 0x09; // unrecognized device ID + CALLBACK_SCF(true); + break; + } + reg_ah = 0; + reg_bl = 0; // number of battery units + reg_cx = 0x03; // can enter suspend/standby and will post standby/resume events + CALLBACK_SCF(false); + break; + case 0x13://enable/disable/query timer based requests + // NTS: NOT implementing this call can cause Windows 98's APM driver to crash on startup + if (!apm_realmode_connected) { + reg_ah = 0x03; // interface not connected + CALLBACK_SCF(true); + break; + } + if (reg_bx != 0) { + reg_ah = 0x09; // unrecognized device ID + CALLBACK_SCF(true); + break; + } + + if (reg_cx == 0) { // disable + APM_inactivity_timer = false; + reg_cx = 0; + CALLBACK_SCF(false); + } + else if (reg_cx == 1) { // enable + APM_inactivity_timer = true; + reg_cx = 1; + CALLBACK_SCF(false); + } + else if (reg_cx == 2) { // get enabled status + reg_cx = APM_inactivity_timer ? 1 : 0; + CALLBACK_SCF(false); + } + else { + reg_ah = 0x0A; // invalid parameter value in CX + CALLBACK_SCF(true); + } + break; + default: + LOG_MSG("Unknown APM BIOS call AX=%04x\n",reg_ax); + if (!apm_realmode_connected) { + reg_ah = 0x03; // interface not connected + CALLBACK_SCF(true); + break; + } + reg_ah = 0x0C; // function not supported + CALLBACK_SCF(true); + break; + } + } + else { + reg_ah=0x86; + CALLBACK_SCF(true); + LOG_MSG("APM BIOS call attempted. set apmbios=1 if you want power management\n"); + if ((IS_EGAVGA_ARCH) || (machine==MCH_CGA) || (machine==MCH_AMSTRAD)) { + /* relict from comparisons, as int15 exits with a retf2 instead of an iret */ + CALLBACK_SZF(false); + } + } + break; + case 0xe8: + switch (reg_al) { + case 0x01: { /* E801: memory size */ + Bitu sz = MEM_TotalPages()*4; + if (sz >= 1024) sz -= 1024; + else sz = 0; + reg_ax = reg_cx = (sz > 0x3C00) ? 0x3C00 : sz; /* extended memory between 1MB and 16MB in KBs */ + sz -= reg_ax; + sz /= 64; /* extended memory size from 16MB in 64KB blocks */ + if (sz > 65535) sz = 65535; + reg_bx = reg_dx = sz; + CALLBACK_SCF(false); + } + break; + case 0x20: { /* E820: MEMORY LISTING */ + if (reg_edx == 0x534D4150 && reg_ecx >= 20 && (MEM_TotalPages()*4) >= 24000) { + /* return a minimalist list: + * + * 0) 0x000000-0x09EFFF Free memory + * 1) 0x0C0000-0x0FFFFF Reserved + * 2) 0x100000-... Free memory (no ACPI tables) */ + if (reg_ebx < 3) { + uint32_t base,len,type; + Bitu seg = SegValue(es); + + assert((MEM_TotalPages()*4096) >= 0x100000); + + switch (reg_ebx) { + case 0: base=0x000000; len=0x09F000; type=1; break; + case 1: base=0x0C0000; len=0x040000; type=2; break; + case 2: base=0x100000; len=(MEM_TotalPages()*4096)-0x100000; type=1; break; + default: E_Exit("Despite checks EBX is wrong value"); /* BUG! */ + }; + + /* write to ES:DI */ + real_writed(seg,reg_di+0x00,base); + real_writed(seg,reg_di+0x04,0); + real_writed(seg,reg_di+0x08,len); + real_writed(seg,reg_di+0x0C,0); + real_writed(seg,reg_di+0x10,type); + reg_ecx = 20; + + /* return EBX pointing to next entry. wrap around, as most BIOSes do. + * the program is supposed to stop on CF=1 or when we return EBX == 0 */ + if (++reg_ebx >= 3) reg_ebx = 0; + } + else { + CALLBACK_SCF(true); + } + + reg_eax = 0x534D4150; + } + else { + reg_eax = 0x8600; + CALLBACK_SCF(true); + } + } + break; + default: + LOG(LOG_BIOS,LOG_ERROR)("INT15:Unknown call ah=E8, al=%2X",reg_al); + reg_ah=0x86; + CALLBACK_SCF(true); + if ((IS_EGAVGA_ARCH) || (machine==MCH_CGA) || (machine==MCH_AMSTRAD)) { + /* relict from comparisons, as int15 exits with a retf2 instead of an iret */ + CALLBACK_SZF(false); + } + } + break; + default: + LOG(LOG_BIOS,LOG_ERROR)("INT15:Unknown call ax=%4X",reg_ax); + reg_ah=0x86; + CALLBACK_SCF(true); + if ((IS_EGAVGA_ARCH) || (machine==MCH_CGA) || (machine==MCH_AMSTRAD)) { + /* relict from comparisons, as int15 exits with a retf2 instead of an iret */ + CALLBACK_SZF(false); + } + } + return CBRET_NONE; +} + +void BIOS_UnsetupKeyboard(void); +void BIOS_SetupKeyboard(void); +void BIOS_UnsetupDisks(void); +void BIOS_SetupDisks(void); +void CPU_Snap_Back_To_Real_Mode(); +void CPU_Snap_Back_Restore(); + +static Bitu IRQ14_Dummy(void) { + /* FIXME: That's it? Don't I EOI the PIC? */ + return CBRET_NONE; +} + +static Bitu IRQ15_Dummy(void) { + /* FIXME: That's it? Don't I EOI the PIC? */ + return CBRET_NONE; +} + +void On_Software_CPU_Reset(); + +static Bitu INT18_Handler(void) { + LOG_MSG("Restart by INT 18h requested\n"); + On_Software_CPU_Reset(); + /* does not return */ + return CBRET_NONE; +} + +static Bitu INT19_Handler(void) { + LOG_MSG("Restart by INT 19h requested\n"); + /* FIXME: INT 19h is sort of a BIOS boot BIOS reset-ish thing, not really a CPU reset at all. */ + On_Software_CPU_Reset(); + /* does not return */ + return CBRET_NONE; +} + +void bios_enable_ps2() { + mem_writew(BIOS_CONFIGURATION, + mem_readw(BIOS_CONFIGURATION)|0x04); /* PS/2 mouse */ +} + +void BIOS_ZeroExtendedSize(bool in) { + if(in) other_memsystems++; + else other_memsystems--; + if(other_memsystems < 0) other_memsystems=0; + + if (IS_PC98_ARCH) { + Bitu mempages = MEM_TotalPages(); /* in 4KB pages */ + + /* What applies to IBM PC/AT (zeroing out the extended memory size) + * also applies to PC-98, when HIMEM.SYS is loaded */ + if (in) mempages = 0; + + /* extended memory size (286 systems, below 16MB) */ + if (mempages > (1024UL/4UL)) { + unsigned int ext = ((mempages - (1024UL/4UL)) * 4096UL) / (128UL * 1024UL); /* convert to 128KB units */ + + /* extended memory, up to 16MB capacity (for 286 systems?) + * + * MS-DOS drivers will "allocate" for themselves by taking from the top of + * extended memory then subtracting from this value. + * + * capacity does not include conventional memory below 1MB, nor any memory + * above 16MB. + * + * PC-98 systems may reserve the top 1MB, limiting the top to 15MB instead. + * + * 0x70 = 128KB * 0x70 = 14MB + * 0x78 = 128KB * 0x70 = 15MB */ + if (ext > 0x78) ext = 0x78; + + mem_writeb(0x401,ext); + } + else { + mem_writeb(0x401,0x00); + } + + /* extended memory size (386 systems, at or above 16MB) */ + if (mempages > ((1024UL*16UL)/4UL)) { + unsigned int ext = ((mempages - ((1024UL*16UL)/4UL)) * 4096UL) / (1024UL * 1024UL); /* convert to MB */ + + /* extended memory, at or above 16MB capacity (for 386+ systems?) + * + * MS-DOS drivers will "allocate" for themselves by taking from the top of + * extended memory then subtracting from this value. + * + * capacity does not include conventional memory below 1MB, nor any memory + * below 16MB. */ + if (ext > 0xFFFE) ext = 0xFFFE; + + mem_writew(0x594,ext); + } + else { + mem_writew(0x594,0x00); + } + } +} + +unsigned char do_isapnp_chksum(unsigned char *d,int i) { + unsigned char sum = 0; + + while (i-- > 0) + sum += *d++; + + return (0x100 - sum) & 0xFF; +} + +void MEM_ResetPageHandler_Unmapped(Bitu phys_page, Bitu pages); + +unsigned int dos_conventional_limit = 0; + +bool AdapterROM_Read(Bitu address,unsigned long *size) { + unsigned char chksum=0; + unsigned char c[3]; + unsigned int i; + + if ((address & 0x1FF) != 0) { + LOG(LOG_MISC,LOG_DEBUG)("AdapterROM_Read: Caller attempted ROM scan not aligned to 512-byte boundary"); + return false; + } + + for (i=0;i < 3;i++) + c[i] = mem_readb(address+i); + + if (c[0] == 0x55 && c[1] == 0xAA) { + *size = (unsigned long)c[2] * 512UL; + for (i=0;i < (unsigned int)(*size);i++) chksum += mem_readb(address+i); + if (chksum != 0) { + LOG(LOG_MISC,LOG_WARN)("AdapterROM_Read: Found ROM at 0x%lx but checksum failed\n",(unsigned long)address); + return false; + } + + return true; + } + + return false; +} + +#include "src/gui/dosbox.vga16.bmp.h" +#include "src/gui/dosbox.cga640.bmp.h" + +void DrawDOSBoxLogoCGA6(unsigned int x,unsigned int y) { + unsigned char *s = dosbox_cga640_bmp; + unsigned char *sf = s + sizeof(dosbox_cga640_bmp); + uint32_t width,height; + unsigned int dx,dy; + uint32_t vram; + uint32_t off; + uint32_t sz; + + if (memcmp(s,"BM",2)) return; + sz = host_readd(s+2); // size of total bitmap + off = host_readd(s+10); // offset of bitmap + if ((s+sz) > sf) return; + if ((s+14+40) > sf) return; + + sz = host_readd(s+34); // biSize + if ((s+off+sz) > sf) return; + if (host_readw(s+26) != 1) return; // biBitPlanes + if (host_readw(s+28) != 1) return; // biBitCount + + width = host_readd(s+18); + height = host_readd(s+22); + if (width > (640-x) || height > (200-y)) return; + + LOG(LOG_MISC,LOG_DEBUG)("Drawing CGA logo (%u x %u)",(int)width,(int)height); + for (dy=0;dy < height;dy++) { + vram = ((y+dy) >> 1) * 80; + vram += ((y+dy) & 1) * 0x2000; + vram += (x / 8); + s = dosbox_cga640_bmp + off + ((height-(dy+1))*((width+7)/8)); + for (dx=0;dx < width;dx += 8) { + mem_writeb(0xB8000+vram,*s); + vram++; + s++; + } + } +} + +/* HACK: Re-use the VGA logo */ +void DrawDOSBoxLogoPC98(unsigned int x,unsigned int y) { + unsigned char *s = dosbox_vga16_bmp; + unsigned char *sf = s + sizeof(dosbox_vga16_bmp); + unsigned int bit,dx,dy; + uint32_t width,height; + unsigned char p[4]; + unsigned char c; + uint32_t vram; + uint32_t off; + uint32_t sz; + + if (memcmp(s,"BM",2)) return; + sz = host_readd(s+2); // size of total bitmap + off = host_readd(s+10); // offset of bitmap + if ((s+sz) > sf) return; + if ((s+14+40) > sf) return; + + sz = host_readd(s+34); // biSize + if ((s+off+sz) > sf) return; + if (host_readw(s+26) != 1) return; // biBitPlanes + if (host_readw(s+28) != 4) return; // biBitCount + + width = host_readd(s+18); + height = host_readd(s+22); + if (width > (640-x) || height > (350-y)) return; + + // EGA/VGA Write Mode 2 + LOG(LOG_MISC,LOG_DEBUG)("Drawing VGA logo as PC-98 (%u x %u)",(int)width,(int)height); + for (dy=0;dy < height;dy++) { + vram = ((y+dy) * 80) + (x / 8); + s = dosbox_vga16_bmp + off + ((height-(dy+1))*((width+1)/2)); + for (dx=0;dx < width;dx += 8) { + p[0] = p[1] = p[2] = p[3] = 0; + for (bit=0;bit < 8;) { + c = (*s >> 4); + p[0] |= ((c >> 0) & 1) << (7 - bit); + p[1] |= ((c >> 1) & 1) << (7 - bit); + p[2] |= ((c >> 2) & 1) << (7 - bit); + p[3] |= ((c >> 3) & 1) << (7 - bit); + bit++; + + c = (*s++) & 0xF; + p[0] |= ((c >> 0) & 1) << (7 - bit); + p[1] |= ((c >> 1) & 1) << (7 - bit); + p[2] |= ((c >> 2) & 1) << (7 - bit); + p[3] |= ((c >> 3) & 1) << (7 - bit); + bit++; + } + + mem_writeb(0xA8000+vram,p[0]); + mem_writeb(0xB0000+vram,p[1]); + mem_writeb(0xB8000+vram,p[2]); + mem_writeb(0xE0000+vram,p[3]); + vram++; + } + } +} + +void DrawDOSBoxLogoVGA(unsigned int x,unsigned int y) { + unsigned char *s = dosbox_vga16_bmp; + unsigned char *sf = s + sizeof(dosbox_vga16_bmp); + unsigned int bit,dx,dy; + uint32_t width,height; + uint32_t vram; + uint32_t off; + uint32_t sz; + + if (memcmp(s,"BM",2)) return; + sz = host_readd(s+2); // size of total bitmap + off = host_readd(s+10); // offset of bitmap + if ((s+sz) > sf) return; + if ((s+14+40) > sf) return; + + sz = host_readd(s+34); // biSize + if ((s+off+sz) > sf) return; + if (host_readw(s+26) != 1) return; // biBitPlanes + if (host_readw(s+28) != 4) return; // biBitCount + + width = host_readd(s+18); + height = host_readd(s+22); + if (width > (640-x) || height > (350-y)) return; + + // EGA/VGA Write Mode 2 + LOG(LOG_MISC,LOG_DEBUG)("Drawing VGA logo (%u x %u)",(int)width,(int)height); + IO_Write(0x3CE,0x05); // graphics mode + IO_Write(0x3CF,0x02); // read=0 write=2 odd/even=0 shift=0 shift256=0 + IO_Write(0x3CE,0x03); // data rotate + IO_Write(0x3CE,0x00); // no rotate, no XOP + for (bit=0;bit < 8;bit++) { + const unsigned char shf = ((bit & 1) ^ 1) * 4; + + IO_Write(0x3CE,0x08); // bit mask + IO_Write(0x3CF,0x80 >> bit); + + for (dy=0;dy < height;dy++) { + vram = ((y+dy) * 80) + (x / 8); + s = dosbox_vga16_bmp + off + (bit/2) + ((height-(dy+1))*((width+1)/2)); + for (dx=bit;dx < width;dx += 8) { + mem_readb(0xA0000+vram); // load VGA latches + mem_writeb(0xA0000+vram,(*s >> shf) & 0xF); + vram++; + s += 4; + } + } + } + // restore write mode 0 + IO_Write(0x3CE,0x05); // graphics mode + IO_Write(0x3CF,0x00); // read=0 write=0 odd/even=0 shift=0 shift256=0 + IO_Write(0x3CE,0x08); // bit mask + IO_Write(0x3CF,0xFF); +} + +static int bios_pc98_posx = 0; + +static void BIOS_Int10RightJustifiedPrint(const int x,int &y,const char *msg) { + const char *s = msg; + + if (machine != MCH_PC98) { + while (*s != 0) { + if (*s == '\n') { + y++; + reg_eax = 0x0200u; // set cursor pos + reg_ebx = 0; // page zero + reg_dh = y; // row 4 + reg_dl = x; // column 20 + CALLBACK_RunRealInt(0x10); + s++; + } + else { + reg_eax = 0x0E00u | ((unsigned char)(*s++)); + reg_ebx = 0x07u; + CALLBACK_RunRealInt(0x10); + } + } + } + else { + unsigned int bo; + + while (*s != 0) { + if (*s == '\n') { + y++; + s++; + bios_pc98_posx = x; + + bo = (((unsigned int)y * 80u) + (unsigned int)bios_pc98_posx) * 2u; + } + else if (*s == '\r') { + s++; /* ignore */ + continue; + } + else { + bo = (((unsigned int)y * 80u) + (unsigned int)(bios_pc98_posx++)) * 2u; /* NTS: note the post increment */ + + mem_writew(0xA0000+bo,(unsigned char)(*s++)); + mem_writeb(0xA2000+bo,0xE1); + + bo += 2; /* and keep the cursor following the text */ + } + + reg_eax = 0x1300; // set cursor pos (PC-98) + reg_edx = bo; // byte position + CALLBACK_RunRealInt(0x18); + } + } +} + +static Bitu ulimit = 0; +static Bitu t_conv = 0; +static bool bios_first_init=true; +static bool bios_has_exec_vga_bios=false; +static Bitu adapter_scan_start; + +/* FIXME: At global scope their destructors are called after the rest of DOSBox has shut down. Move back into BIOS scope. */ +static CALLBACK_HandlerObject int4b_callback; +static CALLBACK_HandlerObject callback[20]; /* <- fixme: this is stupid. just declare one per interrupt. */ +static CALLBACK_HandlerObject cb_bios_post; +static CALLBACK_HandlerObject callback_pc98_lio; + +Bitu call_pnp_r = ~0UL; +Bitu call_pnp_rp = 0; + +Bitu call_pnp_p = ~0UL; +Bitu call_pnp_pp = 0; + +Bitu isapnp_biosstruct_base = 0; + +Bitu BIOS_boot_code_offset = 0; +Bitu BIOS_bootfail_code_offset = 0; + +bool bios_user_reset_vector_blob_run = false; +Bitu bios_user_reset_vector_blob = 0; + +Bitu bios_user_boot_hook = 0; + +void CALLBACK_DeAllocate(Bitu in); + +void BIOS_OnResetComplete(Section *x); + +Bitu call_irq0 = 0; +Bitu call_irq07default = 0; +Bitu call_irq815default = 0; + +void write_FFFF_PC98_signature() { + /* this may overwrite the existing signature. + * PC-98 systems DO NOT have an ASCII date at F000:FFF5 + * and the WORD value at F000:FFFE is said to be a checksum of the BIOS */ + + // The farjump at the processor reset entry point (jumps to POST routine) + phys_writeb(0xffff0,0xEA); // FARJMP + phys_writew(0xffff1,RealOff(BIOS_DEFAULT_RESET_LOCATION)); // offset + phys_writew(0xffff3,RealSeg(BIOS_DEFAULT_RESET_LOCATION)); // segment + + // write nothing (not used) + for(Bitu i = 0; i < 9; i++) phys_writeb(0xffff5+i,0); + + // fake BIOS checksum + phys_writew(0xffffe,0xABCD); +} + +void gdc_egc_enable_update_vars(void) { + unsigned char b; + + b = mem_readb(0x54D); + b &= ~0x40; + if (enable_pc98_egc) b |= 0x40; + mem_writeb(0x54D,b); + + b = mem_readb(0x597); + b &= ~0x04; + if (enable_pc98_egc) b |= 0x04; + mem_writeb(0x597,b); + + if (!enable_pc98_egc) + pc98_gdc_vramop &= ~(1 << VOPBIT_EGC); +} + +void gdc_grcg_enable_update_vars(void) { + unsigned char b; + + b = mem_readb(0x54C); + b &= ~0x02; + if (enable_pc98_grcg) b |= 0x02; + mem_writeb(0x54C,b); + + //TODO: How to reset GRCG? +} + + +void gdc_16color_enable_update_vars(void) { + unsigned char b; + + b = mem_readb(0x54C); + b &= ~0x04; + if (enable_pc98_16color) b |= 0x04; + mem_writeb(0x54C,b); + + if(!enable_pc98_16color) {//force switch to 8-colors mode + void pc98_port6A_command_write(unsigned char b); + pc98_port6A_command_write(0x00); + } +} + +/* NTS: Remember the 8259 is non-sentient, and the term "slave" is used in a computer programming context */ +static Bitu Default_IRQ_Handler_Cooperative_Slave_Pic(void) { + /* PC-98 style IRQ 8-15 handling. + * + * This mimics the recommended procedure [https://www.webtech.co.jp/company/doc/undocumented_mem/io_pic.txt] + * + * mov al,20h ;Send EOI to SLAVE + * out 0008h,al + * jmp $+2 ;I/O WAIT + * mov al,0Bh ;ISR read mode set(slave) + * out 0008h,al + * jmp $+2 ;I/O WAIT + * in al,0008h ;ISR read(slave) + * cmp al,00h ;slave pic in-service ? + * jne EoiEnd + * mov al,20h ;Send EOI to MASTER + * out 0000h,al + */ + IO_WriteB(IS_PC98_ARCH ? 0x08 : 0xA0,0x20); // send EOI to slave + IO_WriteB(IS_PC98_ARCH ? 0x08 : 0xA0,0x0B); // ISR read mode set + if (IO_ReadB(IS_PC98_ARCH ? 0x08 : 0xA0) == 0) // if slave pic in service.. + IO_WriteB(IS_PC98_ARCH ? 0x00 : 0x20,0x20); // then EOI the master + + return CBRET_NONE; +} + +class BIOS:public Module_base{ +private: + static Bitu cb_bios_post__func(void) { + void TIMER_BIOS_INIT_Configure(); +#if C_DEBUG + void DEBUG_CheckCSIP(); + +# if C_HEAVY_DEBUG + /* the game/app obviously crashed, which is way more important + * to log than what we do here in the BIOS at POST */ + void DEBUG_StopLog(void); + DEBUG_StopLog(); +# endif +#endif + + if (bios_first_init) { + /* clear the first 1KB-32KB */ + for (Bit16u i=0x400;i<0x8000;i++) real_writeb(0x0,i,0); + } + + if (IS_PC98_ARCH) { + for (unsigned int i=0;i < 20;i++) callback[i].Uninstall(); + + /* clear out 0x50 segment (TODO: 0x40 too?) */ + for (unsigned int i=0;i < 0x100;i++) phys_writeb(0x500+i,0); + + write_FFFF_PC98_signature(); + BIOS_ZeroExtendedSize(false); + + unsigned char memsize_real_code = 0; + Bitu mempages = MEM_TotalPages(); /* in 4KB pages */ + + /* NTS: Fill in the 3-bit code in FLAGS1 that represents + * how much lower conventional memory is in the system. + * + * Note that MEM.EXE requires this value, or else it + * will complain about broken UMB linkage and fail + * to show anything else. */ + /* TODO: In the event we eventually support "high resolution mode" + * we can indicate 768KB here, code == 5, meaning that + * the RAM extends up to 0xBFFFF instead of 0x9FFFF */ + if (mempages >= (640UL/4UL)) /* 640KB */ + memsize_real_code = 4; + else if (mempages >= (512UL/4UL)) /* 512KB */ + memsize_real_code = 3; + else if (mempages >= (384UL/4UL)) /* 384KB */ + memsize_real_code = 2; + else if (mempages >= (256UL/4UL)) /* 256KB */ + memsize_real_code = 1; + else /* 128KB */ + memsize_real_code = 0; + + void pc98_msw3_set_ramsize(const unsigned char b); + pc98_msw3_set_ramsize(memsize_real_code); + + /* CRT status */ + /* bit[7:6] = 00=conventional compatible 01=extended attr JEH 10=extended attr EGH + * bit[5:5] = Single event timer in use flag 1=busy 0=not used + * bit[4:4] = ? + * bit[3:3] = raster scan 1=non-interlaced 0=interlaced + * bit[2:2] = Content ruled line color 1=I/O set value 0=attributes of VRAM + * bit[1:1] = ? + * bit[0:0] = 480-line mode 1=640x480 0=640x400 or 640x200 */ + mem_writeb(0x459,0x08/*non-interlaced*/); + + /* CPU/Display */ + /* bit[7:7] = 486SX equivalent (?) 1=yes + * bit[6:6] = PC-9821 Extended Graph Architecture supported (FIXME: Is this the same as having EGC?) 1=yes + * bit[5:5] = LCD display is color 1=yes 0=no + * bit[4:4] = ? + * bit[3:3] = ROM drive allow writing + * bit[2:2] = 98 NOTE PC-9801N-08 expansion I/O box connected + * bit[1:1] = 98 NOTE prohibit transition to power saving mode + * bit[0:0] = 98 NOTE coprocessor function available */ + mem_writeb(0x45C,(enable_pc98_egc ? 0x40/*Extended Graphics*/ : 0x00)); + + /* Keyboard type */ + /* bit[7:7] = ? + * bit[6:6] = keyboard type bit 1 + * bit[5:5] = EMS page frame at B0000h 1=present 0=none + * bit[4:4] = EMS page frame at B0000h 1=page frame 0=G-VRAM + * bit[3:3] = keyboard type bit 0 + * bit[2:2] = High resolution memory window available + * bit[1:1] = ? + * bit[0:0] = ? + * + * keyboard bits[1:0] from bit 6 as bit 1 and bit 3 as bit 0 combined: + * 11 = new keyboard (NUM key, DIP switch 2-7 OFF) + * 10 = new keyboard (without NUM key) + * 01 = new keyboard (NUM key, DIP switch 2-7 ON) + * 00 = old keyboard + * + * The old keyboard is documented not to support software control of CAPS and KANA states */ + /* TODO: Make this a dosbox.conf option. Default is new keyboard without NUM key because that is what + * keyboard emulation currently acts like anyway. */ + mem_writeb(0x481,0x40/*bit 6=1 bit 3=0 new keyboard without NUM key*/); + + /* BIOS flags */ + /* bit[7:7] = Startup 1=hot start 0=cold start + * bit[6:6] = BASIC type ?? + * bit[5:5] = Keyboard beep 1=don't beep 0=beep ... when buffer full + * bit[4:4] = Expansion conv RAM 1=present 0=absent + * bit[3:3] = ?? + * bit[2:2] = ?? + * bit[1:1] = HD mode 1=1MB mode 0=640KB mode ... of the floppy drive + * bit[0:0] = Model 1=other 0=PC-9801 original */ + /* NTS: MS-DOS 5.0 appears to reduce it's BIOS calls and render the whole + * console as green IF bit 0 is clear. + * + * If bit 0 is set, INT 1Ah will be hooked by MS-DOS and, for some odd reason, + * MS-DOS's hook proc will call to our INT 1Ah + 0x19 bytes. */ + mem_writeb(0x500,0x01 | 0x02/*high density drive*/); + + /* BIOS flags */ + /* timer setup will set/clear bit 7 */ + /* bit[7:7] = system clock freq 1=8MHz 0=5/10Mhz + * = timer clock freq 1=1.9968MHz 0=2.4576MHz + * bit[6:6] = CPU 1=V30 0=Intel (8086 through Pentium) + * bit[5:5] = Model info 1=Other model 0=PC-9801 Muji, PC-98XA + * bit[4:4] = Model info ... + * bit[3:3] = Model info 1=High res 0=normal + * bit[2:0] = Realmode memsize + * 000=128KB 001=256KB + * 010=384KB 011=512KB + * 100=640KB 101=768KB + * + * Ref: http://hackipedia.org/browse/Computer/Platform/PC,%20NEC%20PC-98/Collections/Undocumented%209801,%209821%20Volume%202%20(webtech.co.jp)/memsys.txt */ + /* NTS: High resolution means 640x400, not the 1120x750 mode known as super high resolution mode. + * DOSBox-X does not yet emulate super high resolution nor does it emulate the 15khz 200-line "standard" mode. + * ref: https://github.com/joncampbell123/dosbox-x/issues/906#issuecomment-434513930 + * ref: https://jisho.org/search?utf8=%E2%9C%93&keyword=%E8%B6%85 */ + mem_writeb(0x501,0x20 | memsize_real_code); + + /* keyboard buffer */ + mem_writew(0x524/*tail*/,0x502); + mem_writew(0x526/*tail*/,0x502); + + /* number of scanlines per text row - 1 */ + mem_writeb(0x53B,0x0F); // CRT_RASTER, 640x400 24.83KHz-hsync 56.42Hz-vsync + + /* Text screen status. + * Note that most of the bits are used verbatim in INT 18h AH=0Ah/AH=0Bh */ + /* bit[7:7] = High resolution display 1=yes 0=no (standard) NOT super high res + * bit[6:6] = vsync 1=VSYNC wait 0=end of vsync handling + * bit[5:5] = unused + * bit[4:4] = Number of lines 1=30 lines 0=20/25 lines + * bit[3:3] = K-CG access mode 1=dot access 0=code access + * bit[2:2] = Attribute mode (how to handle bit 4) 1=Simp. graphic 0=Vertical line + * bit[1:1] = Number of columns 1=40 cols 0=80 cols + * bit[0:0] = Number of lines 1=20/30 lines 0=25 lines */ + mem_writeb(0x53C,(true/*TODO*/ ? 0x80/*high res*/ : 0x00/*standard*/)); + + /* BIOS raster location */ + mem_writew(0x54A,0x1900); + + /* BIOS flags */ + /* bit[7:7] = Graphics display state 1=Visible 0=Blanked (hidden) + * bit[6:6] = CRT type 1=high res 0=standard NOT super high res + * bit[5:5] = Horizontal sync rate 1=31.47KHz 0=24.83KHz + * bit[4:4] = CRT line mode 1=480-line 0=400-line + * bit[3:3] = Number of user-defined characters 1=188+ 0=63 + * bit[2:2] = Extended graphics RAM (for 16-color) 1=present 0=absent + * bit[1:1] = Graphics Charger is present 1=present 0=absent + * bit[0:0] = DIP switch 1-8 at startup 1=ON 0=OFF (?) */ + mem_writeb(0x54C,(true/*TODO*/ ? 0x40/*high res*/ : 0x00/*standard*/) | (enable_pc98_grcg ? 0x02 : 0x00) | (enable_pc98_16color ? 0x04 : 0x00) | (pc98_31khz_mode ? 0x20/*31khz*/ : 0x00/*24khz*/) | (enable_pc98_188usermod ? 0x08 : 0x00)); // PRXCRT, 16-color G-VRAM, GRCG + + /* BIOS flags */ + /* bit[7:7] = 256-color board present (PC-H98) + * bit[6:6] = Enhanced Graphics Charger (EGC) is present + * bit[5:5] = GDC at 5.0MHz at boot up (copy of DIP switch 2-8 at startup) 1=yes 0=no + * bit[4:4] = Always "flickerless" drawing mode + * bit[3:3] = Drawing mode with flicker + * bit[2:2] = GDC clock 1=5MHz 0=2.5MHz + * bit[1:0] = Drawing mode of the GDC + * 00 = REPLACE + * 01 = COMPLEMENT + * 10 = CLEAR + * 11 = SET */ + mem_writeb(0x54D,(enable_pc98_egc ? 0x40 : 0x00) | (gdc_5mhz_mode ? 0x20 : 0x00) | (gdc_5mhz_mode ? 0x04 : 0x00)); // EGC + + /* BIOS flags */ + /* bit[7:7] = INT 18h AH=30h/31h support enabled + * bit[6:3] = 0 (unused) + * bit[2:2] = Enhanced Graphics Mode (EGC) supported + * bit[1:0] = Graphic resolution + * 00 = 640x200 upper half (2/8/16-color mode) + * 01 = 640x200 lower half (2/8/16-color mode) + * 10 = 640x400 (2/8/16/256-color mode) + * 11 = 640x480 256-color mode */ + mem_writeb(0x597,(enable_pc98_egc ? 0x04 : 0x00)/*EGC*/ | + (enable_pc98_egc ? 0x80 : 0x00)/*supports INT 18h AH=30h and AH=31h*/ | + 2/*640x400*/); + /* TODO: I would like to eventually add a dosbox.conf option that controls whether INT 18h AH=30h and 31h + * are enabled, so that retro-development can test code to see how it acts on a newer PC-9821 + * that supports it vs an older PC-9821 that doesn't. + * + * If the user doesn't set the option, then it is "auto" and determined by machine= PC-98 model and + * by another option in dosbox.conf that determines whether 31khz support is enabled. + * + * NOTED: Neko Project II determines INT 18h AH=30h availability by whether or not it was compiled + * with 31khz hsync support (SUPPORT_CRT31KHZ) */ + } + + if (bios_user_reset_vector_blob != 0 && !bios_user_reset_vector_blob_run) { + LOG_MSG("BIOS POST: Running user reset vector blob at 0x%lx",(unsigned long)bios_user_reset_vector_blob); + bios_user_reset_vector_blob_run = true; + + assert((bios_user_reset_vector_blob&0xF) == 0); /* must be page aligned */ + + SegSet16(cs,bios_user_reset_vector_blob>>4); + reg_eip = 0; + +#if C_DEBUG + /* help the debugger reflect the new instruction pointer */ + DEBUG_CheckCSIP(); +#endif + + return CBRET_NONE; + } + + if (cpu.pmode) E_Exit("BIOS error: POST function called while in protected/vm86 mode"); + + CPU_CLI(); + + /* we need A20 enabled for BIOS boot-up */ + void A20Gate_OverrideOn(Section *sec); + void MEM_A20_Enable(bool enabled); + A20Gate_OverrideOn(NULL); + MEM_A20_Enable(true); + + BIOS_OnResetComplete(NULL); + + adapter_scan_start = 0xC0000; + bios_has_exec_vga_bios = false; + LOG(LOG_MISC,LOG_DEBUG)("BIOS: executing POST routine"); + + // TODO: Anything we can test in the CPU here? + + // initialize registers + SegSet16(ds,0x0000); + SegSet16(es,0x0000); + SegSet16(fs,0x0000); + SegSet16(gs,0x0000); + SegSet16(ss,0x0000); + + { + Bitu sz = MEM_TotalPages(); + + /* The standard BIOS is said to put it's stack (at least at OS boot time) 512 bytes past the end of the boot sector + * meaning that the boot sector loads to 0000:7C00 and the stack is set grow downward from 0000:8000 */ + + if (sz > 8) sz = 8; /* 4KB * 8 = 32KB = 0x8000 */ + sz *= 4096; + reg_esp = sz - 4; + reg_ebp = 0; + LOG(LOG_MISC,LOG_DEBUG)("BIOS: POST stack set to 0000:%04x",reg_esp); + } + + if (dosbox_int_iocallout != IO_Callout_t_none) { + IO_FreeCallout(dosbox_int_iocallout); + dosbox_int_iocallout = IO_Callout_t_none; + } + + if (isapnp_biosstruct_base != 0) { + ROMBIOS_FreeMemory(isapnp_biosstruct_base); + isapnp_biosstruct_base = 0; + } + + if (BOCHS_PORT_E9) { + delete BOCHS_PORT_E9; + BOCHS_PORT_E9=NULL; + } + if (ISAPNP_PNP_ADDRESS_PORT) { + delete ISAPNP_PNP_ADDRESS_PORT; + ISAPNP_PNP_ADDRESS_PORT=NULL; + } + if (ISAPNP_PNP_DATA_PORT) { + delete ISAPNP_PNP_DATA_PORT; + ISAPNP_PNP_DATA_PORT=NULL; + } + if (ISAPNP_PNP_READ_PORT) { + delete ISAPNP_PNP_READ_PORT; + ISAPNP_PNP_READ_PORT=NULL; + } + + extern Bitu call_default,call_default2; + + if (IS_PC98_ARCH) { + /* INT 00h-FFh generic stub routine */ + /* NTS: MS-DOS on PC-98 will fill all yet-unused interrupt vectors with a stub. + * No vector is left at 0000:0000. On a related note, PC-98 games apparently + * like to call INT 33h (mouse functions) without first checking that the + * vector is non-null. */ + callback[18].Uninstall(); + callback[18].Install(&INTGEN_PC98_Handler,CB_IRET,"Int stub ???"); + for (unsigned int i=0x00;i < 0x100;i++) RealSetVec(i,callback[18].Get_RealPointer()); + + for (unsigned int i=0x00;i < 0x08;i++) + real_writed(0,i*4,CALLBACK_RealPointer(call_default)); + } + else { + /* Clear the vector table */ + for (Bit16u i=0x70*4;i<0x400;i++) real_writeb(0x00,i,0); + + /* Only setup default handler for first part of interrupt table */ + for (Bit16u ct=0;ct<0x60;ct++) { + real_writed(0,ct*4,CALLBACK_RealPointer(call_default)); + } + for (Bit16u ct=0x68;ct<0x70;ct++) { + real_writed(0,ct*4,CALLBACK_RealPointer(call_default)); + } + + // default handler for IRQ 2-7 + for (Bit16u ct=0x0A;ct <= 0x0F;ct++) + RealSetVec(ct,BIOS_DEFAULT_IRQ07_DEF_LOCATION); + } + + if (unhandled_irq_method == UNHANDLED_IRQ_COOPERATIVE_2ND) { + // PC-98 style: Master PIC ack with 0x20 for IRQ 0-7. + // For the slave PIC, ack with 0x20 on the slave, then only ack the master (cascade interrupt) + // if the ISR register on the slave indicates none are in service. + CALLBACK_Setup(call_irq07default,NULL,CB_IRET_EOI_PIC1,Real2Phys(BIOS_DEFAULT_IRQ07_DEF_LOCATION),"bios irq 0-7 default handler"); + CALLBACK_Setup(call_irq815default,Default_IRQ_Handler_Cooperative_Slave_Pic,CB_IRET,Real2Phys(BIOS_DEFAULT_IRQ815_DEF_LOCATION),"bios irq 8-15 default handler"); + } + else { + // IBM PC style: Master PIC ack with 0x20, slave PIC ack with 0x20, no checking + CALLBACK_Setup(call_irq07default,NULL,CB_IRET_EOI_PIC1,Real2Phys(BIOS_DEFAULT_IRQ07_DEF_LOCATION),"bios irq 0-7 default handler"); + CALLBACK_Setup(call_irq815default,NULL,CB_IRET_EOI_PIC2,Real2Phys(BIOS_DEFAULT_IRQ815_DEF_LOCATION),"bios irq 8-15 default handler"); + } + + if (IS_PC98_ARCH) { + BIOS_UnsetupKeyboard(); + BIOS_UnsetupDisks(); + + /* no such INT 4Bh */ + int4b_callback.Uninstall(); + + /* remove some IBM-style BIOS interrupts that don't exist on PC-98 */ + /* IRQ to INT arrangement + * + * IBM PC-98 IRQ + * -------------------------------- + * 0x08 0x08 0 + * 0x09 0x09 1 + * 0x0A CASCADE 0x0A 2 + * 0x0B 0x0B 3 + * 0x0C 0x0C 4 + * 0x0D 0x0D 5 + * 0x0E 0x0E 6 + * 0x0F 0x0F CASCADE 7 + * 0x70 0x10 8 + * 0x71 0x11 9 + * 0x72 0x12 10 + * 0x73 0x13 11 + * 0x74 0x14 12 + * 0x75 0x15 13 + * 0x76 0x16 14 + * 0x77 0x17 15 + * + * As part of the change the IRQ cascade emulation needs to change for PC-98 as well. + * IBM uses IRQ 2 for cascade. + * PC-98 uses IRQ 7 for cascade. */ + + void INT10_EnterPC98(Section *sec); + INT10_EnterPC98(NULL); /* INT 10h */ + + callback_pc98_lio.Uninstall(); + + callback[1].Uninstall(); /* INT 11h */ + callback[2].Uninstall(); /* INT 12h */ + callback[3].Uninstall(); /* INT 14h */ + callback[4].Uninstall(); /* INT 15h */ + callback[5].Uninstall(); /* INT 17h */ + callback[6].Uninstall(); /* INT 1Ah */ + callback[7].Uninstall(); /* INT 1Ch */ + callback[10].Uninstall(); /* INT 19h */ + callback[11].Uninstall(); /* INT 76h: IDE IRQ 14 */ + callback[12].Uninstall(); /* INT 77h: IDE IRQ 15 */ + callback[15].Uninstall(); /* INT 18h: Enter BASIC */ + + /* IRQ 6 is nothing special */ + callback[13].Uninstall(); /* INT 0Eh: IDE IRQ 6 */ + callback[13].Install(NULL,CB_IRET_EOI_PIC1,"irq 6"); + + /* IRQ 8 is nothing special */ + callback[8].Uninstall(); + callback[8].Install(NULL,CB_IRET_EOI_PIC2,"irq 8"); + + /* IRQ 9 is nothing special */ + callback[9].Uninstall(); + callback[9].Install(NULL,CB_IRET_EOI_PIC2,"irq 9"); + + /* INT 18h keyboard and video display functions */ + callback[1].Install(&INT18_PC98_Handler,CB_INT16,"Int 18 keyboard and display"); + callback[1].Set_RealVec(0x18,/*reinstall*/true); + + /* INT 19h *STUB* */ + callback[2].Install(&INT19_PC98_Handler,CB_IRET,"Int 19 ???"); + callback[2].Set_RealVec(0x19,/*reinstall*/true); + + /* INT 1Ah *STUB* */ + callback[3].Install(&INT1A_PC98_Handler,CB_IRET,"Int 1A ???"); + callback[3].Set_RealVec(0x1A,/*reinstall*/true); + + /* MS-DOS 5.0 FIXUP: + * - For whatever reason, if we set bits in the BIOS data area that + * indicate we're NOT the original model of the PC-98, MS-DOS will + * hook our INT 1Ah and then call down to 0x19 bytes into our + * INT 1Ah procedure. If anyone can explain this, I'd like to hear it. --J.C. + * + * NTS: On real hardware, the BIOS appears to have an INT 1Ah, a bunch of NOPs, + * then at 0x19 bytes into the procedure, the actual handler. This is what + * MS-DOS is pointing at. + * + * But wait, there's more. + * + * MS-DOS calldown pushes DS and DX onto the stack (after the IRET frame) + * before JMPing into the BIOS. + * + * Apparently the function at INT 1Ah + 0x19 is expected to do this: + * + * + * POP DX + * POP DS + * IRET + * + * I can only imaging what a headache this might have caused NEC when + * maintaining the platform and compatibility! */ + { + Bitu addr = callback[3].Get_RealPointer(); + addr = ((addr >> 16) << 4) + (addr & 0xFFFF); + + /* to make this work, we need to pop the two regs, then JMP to our + * callback and proceed as normal. */ + phys_writeb(addr + 0x19,0x5A); // POP DX + phys_writeb(addr + 0x1A,0x1F); // POP DS + phys_writeb(addr + 0x1B,0xEB); // jmp short ... + phys_writeb(addr + 0x1C,0x100 - 0x1D); + } + + /* INT 1Bh *STUB* */ + callback[4].Install(&INT1B_PC98_Handler,CB_IRET,"Int 1B ???"); + callback[4].Set_RealVec(0x1B,/*reinstall*/true); + + /* INT 1Ch *STUB* */ + callback[5].Install(&INT1C_PC98_Handler,CB_IRET,"Int 1C ???"); + callback[5].Set_RealVec(0x1C,/*reinstall*/true); + + /* INT 1Dh *STUB* */ + /* Place it in the PC-98 int vector area at FD80:0000 to satisfy some DOS games + * that detect PC-98 from the segment value of the vector (issue #927). + * Note that on real hardware (PC-9821) INT 1Dh appears to be a stub that IRETs immediately. */ + callback[6].Install(&INT1D_PC98_Handler,CB_IRET,"Int 1D ???"); +// callback[6].Set_RealVec(0x1D,/*reinstall*/true); + { + Bitu ofs = 0xFD813; /* 0xFD80:0013 try not to look like a phony address */ + unsigned int vec = 0x1D; + Bit32u target = callback[6].Get_RealPointer(); + + phys_writeb(ofs+0,0xEA); // JMP FAR + phys_writed(ofs+1,target); + + phys_writew((vec*4)+0,(ofs-0xFD800)); + phys_writew((vec*4)+2,0xFD80); + } + + /* INT 1Eh *STUB* */ + callback[7].Install(&INT1E_PC98_Handler,CB_IRET,"Int 1E ???"); + callback[7].Set_RealVec(0x1E,/*reinstall*/true); + + /* INT 1Fh *STUB* */ + callback[10].Install(&INT1F_PC98_Handler,CB_IRET,"Int 1F ???"); + callback[10].Set_RealVec(0x1F,/*reinstall*/true); + + /* INT DCh *STUB* */ + callback[16].Install(&INTDC_PC98_Handler,CB_IRET,"Int DC ???"); + callback[16].Set_RealVec(0xDC,/*reinstall*/true); + + /* INT F2h *STUB* */ + callback[17].Install(&INTF2_PC98_Handler,CB_IRET,"Int F2 ???"); + callback[17].Set_RealVec(0xF2,/*reinstall*/true); + + // default handler for IRQ 2-7 + for (Bit16u ct=0x0A;ct <= 0x0F;ct++) + RealSetVec(ct,BIOS_DEFAULT_IRQ07_DEF_LOCATION); + + // default handler for IRQ 8-15 + for (Bit16u ct=0;ct < 8;ct++) + RealSetVec(ct+(IS_PC98_ARCH ? 0x10 : 0x70),BIOS_DEFAULT_IRQ815_DEF_LOCATION); + + // LIO graphics interface (number of entry points, unknown WORD value and offset into the segment). + { + callback_pc98_lio.Install(&PC98_BIOS_LIO,CB_IRET,"LIO graphics library"); + + Bitu ofs = 0xF990u << 4u; // F990:0000... + unsigned int entrypoints = 0x11; + Bitu final_addr = callback_pc98_lio.Get_RealPointer(); + + /* NTS: Based on GAME/MAZE 999 behavior, these numbers are interrupt vector numbers. + * The entry point marked 0xA0 is copied by the game to interrupt vector A0 and + * then called with INT A0h even though it blindly assumes the numbers are + * sequential from 0xA0-0xAF. */ + unsigned char entrypoint_indexes[0x11] = { + 0xA0, 0xA1, 0xA2, 0xA3, // +0x00 + 0xA4, 0xA5, 0xA6, 0xA7, // +0x04 + 0xA8, 0xA9, 0xAA, 0xAB, // +0x08 + 0xAC, 0xAD, 0xAE, 0xAF, // +0x0C + 0xCE // +0x10 + }; + + assert(((entrypoints * 4) + 4) <= 0x50); + assert((50 + (entrypoints * 7)) <= 0x100); // a 256-byte region is set aside for this! + + phys_writed(ofs+0,entrypoints); + for (unsigned int ent=0;ent < entrypoints;ent++) { + /* each entry point is "MOV AL, ; JMP FAR " */ + /* Yksoft1's patch suggests a segment offset of 0x50 which I'm OK with */ + unsigned int ins_ofs = ofs + 0x50 + (ent * 7); + + phys_writew(ofs+4+(ent*4)+0,entrypoint_indexes[ent]); + phys_writew(ofs+4+(ent*4)+2,ins_ofs - ofs); + + phys_writeb(ins_ofs+0,0xB0); // MOV AL,(entrypoint index) + phys_writeb(ins_ofs+1,entrypoint_indexes[ent]); + phys_writeb(ins_ofs+2,0xEA); // JMP FAR + phys_writed(ins_ofs+3,final_addr); + // total: ins_ofs+7 + } + } + } + + // setup a few interrupt handlers that point to bios IRETs by default + if (!IS_PC98_ARCH) + real_writed(0,0x0e*4,CALLBACK_RealPointer(call_default2)); //design your own railroad + + if (IS_PC98_ARCH) { + real_writew(0,0x58A,0x0000U); // countdown timer value + PIC_SetIRQMask(0,true); /* PC-98 keeps the timer off unless INT 1Ch is called to set a timer interval */ + } + + real_writed(0,0x66*4,CALLBACK_RealPointer(call_default)); //war2d + real_writed(0,0x67*4,CALLBACK_RealPointer(call_default)); + real_writed(0,0x68*4,CALLBACK_RealPointer(call_default)); + real_writed(0,0x5c*4,CALLBACK_RealPointer(call_default)); //Network stuff + //real_writed(0,0xf*4,0); some games don't like it + + bios_first_init = false; + + DispatchVMEvent(VM_EVENT_BIOS_INIT); + + TIMER_BIOS_INIT_Configure(); + + void INT10_Startup(Section *sec); + INT10_Startup(NULL); + + if (!IS_PC98_ARCH) { + extern Bit8u BIOS_tandy_D4_flag; + real_writeb(0x40,0xd4,BIOS_tandy_D4_flag); + } + + /* INT 13 Bios Disk Support */ + BIOS_SetupDisks(); + + /* INT 16 Keyboard handled in another file */ + BIOS_SetupKeyboard(); + + if (!IS_PC98_ARCH) { + int4b_callback.Set_RealVec(0x4B,/*reinstall*/true); + callback[1].Set_RealVec(0x11,/*reinstall*/true); + callback[2].Set_RealVec(0x12,/*reinstall*/true); + callback[3].Set_RealVec(0x14,/*reinstall*/true); + callback[4].Set_RealVec(0x15,/*reinstall*/true); + callback[5].Set_RealVec(0x17,/*reinstall*/true); + callback[6].Set_RealVec(0x1A,/*reinstall*/true); + callback[7].Set_RealVec(0x1C,/*reinstall*/true); + callback[8].Set_RealVec(0x70,/*reinstall*/true); + callback[9].Set_RealVec(0x71,/*reinstall*/true); + callback[10].Set_RealVec(0x19,/*reinstall*/true); + callback[11].Set_RealVec(0x76,/*reinstall*/true); + callback[12].Set_RealVec(0x77,/*reinstall*/true); + callback[13].Set_RealVec(0x0E,/*reinstall*/true); + callback[15].Set_RealVec(0x18,/*reinstall*/true); + } + + // FIXME: We're using IBM PC memory size storage even in PC-98 mode. + // This cannot be removed, because the DOS kernel uses this variable even in PC-98 mode. + mem_writew(BIOS_MEMORY_SIZE,t_conv); + + RealSetVec(0x08,BIOS_DEFAULT_IRQ0_LOCATION); + // pseudocode for CB_IRQ0: + // sti + // callback INT8_Handler + // push ds,ax,dx + // int 0x1c + // cli + // mov al, 0x20 + // out 0x20, al + // pop dx,ax,ds + // iret + + if (!IS_PC98_ARCH) { + mem_writed(BIOS_TIMER,0); //Calculate the correct time + phys_writew(Real2Phys(RealGetVec(0x12))+0x12,0x20); //Hack for Jurresic + } + + phys_writeb(Real2Phys(BIOS_DEFAULT_HANDLER_LOCATION),0xcf); /* bios default interrupt vector location -> IRET */ + + if (!IS_PC98_ARCH) { + // tandy DAC setup + bool use_tandyDAC=(real_readb(0x40,0xd4)==0xff); + + tandy_sb.port=0; + tandy_dac.port=0; + if (use_tandyDAC) { + /* tandy DAC sound requested, see if soundblaster device is available */ + Bitu tandy_dac_type = 0; + if (Tandy_InitializeSB()) { + tandy_dac_type = 1; + } else if (Tandy_InitializeTS()) { + tandy_dac_type = 2; + } + if (tandy_dac_type) { + real_writew(0x40,0xd0,0x0000); + real_writew(0x40,0xd2,0x0000); + real_writeb(0x40,0xd4,0xff); /* tandy DAC init value */ + real_writed(0x40,0xd6,0x00000000); + /* install the DAC callback handler */ + tandy_DAC_callback[0]=new CALLBACK_HandlerObject(); + tandy_DAC_callback[1]=new CALLBACK_HandlerObject(); + tandy_DAC_callback[0]->Install(&IRQ_TandyDAC,CB_IRET,"Tandy DAC IRQ"); + tandy_DAC_callback[1]->Install(NULL,CB_TDE_IRET,"Tandy DAC end transfer"); + // pseudocode for CB_TDE_IRET: + // push ax + // mov ax, 0x91fb + // int 15 + // cli + // mov al, 0x20 + // out 0x20, al + // pop ax + // iret + + Bit8u tandy_irq = 7; + if (tandy_dac_type==1) tandy_irq = tandy_sb.irq; + else if (tandy_dac_type==2) tandy_irq = tandy_dac.irq; + Bit8u tandy_irq_vector = tandy_irq; + if (tandy_irq_vector<8) tandy_irq_vector += 8; + else tandy_irq_vector += (0x70-8); + + RealPt current_irq=RealGetVec(tandy_irq_vector); + real_writed(0x40,0xd6,current_irq); + for (Bit16u i=0; i<0x10; i++) phys_writeb(PhysMake(0xf000,0xa084+i),0x80); + } else real_writeb(0x40,0xd4,0x00); + } + } + + if (!IS_PC98_ARCH) { + /* Setup some stuff in 0x40 bios segment */ + + // Disney workaround + // Bit16u disney_port = mem_readw(BIOS_ADDRESS_LPT1); + // port timeouts + // always 1 second even if the port does not exist + // BIOS_SetLPTPort(0, disney_port); + for(Bitu i = 1; i < 3; i++) BIOS_SetLPTPort(i, 0); + mem_writeb(BIOS_COM1_TIMEOUT,1); + mem_writeb(BIOS_COM2_TIMEOUT,1); + mem_writeb(BIOS_COM3_TIMEOUT,1); + mem_writeb(BIOS_COM4_TIMEOUT,1); + + void BIOS_Post_register_parports(); + BIOS_Post_register_parports(); + + void BIOS_Post_register_comports(); + BIOS_Post_register_comports(); + } + + if (!IS_PC98_ARCH) { + /* Setup equipment list */ + // look http://www.bioscentral.com/misc/bda.htm + + //Bit16u config=0x4400; //1 Floppy, 2 serial and 1 parallel + Bit16u config = 0x0; + + config |= bios_post_parport_count() << 14; + config |= bios_post_comport_count() << 9; + +#if (C_FPU) + extern bool enable_fpu; + + //FPU + if (enable_fpu) + config|=0x2; +#endif + switch (machine) { + case MCH_MDA: + case MCH_HERC: + //Startup monochrome + config|=0x30; + break; + case EGAVGA_ARCH_CASE: + case MCH_CGA: + case MCH_MCGA: + case TANDY_ARCH_CASE: + case MCH_AMSTRAD: + //Startup 80x25 color + config|=0x20; + break; + default: + //EGA VGA + config|=0; + break; + } + + // PS2 mouse + if (KEYBOARD_Report_BIOS_PS2Mouse()) + config |= 0x04; + + // Gameport + config |= 0x1000; + mem_writew(BIOS_CONFIGURATION,config); + CMOS_SetRegister(0x14,(Bit8u)(config&0xff)); //Should be updated on changes + } + + if (!IS_PC98_ARCH) { + /* Setup extended memory size */ + IO_Write(0x70,0x30); + size_extended=IO_Read(0x71); + IO_Write(0x70,0x31); + size_extended|=(IO_Read(0x71) << 8); + BIOS_HostTimeSync(); + } + else { + /* Provide a valid memory size anyway */ + size_extended=MEM_TotalPages()*4; + if (size_extended >= 1024) size_extended -= 1024; + else size_extended = 0; + } + + if (!IS_PC98_ARCH) { + /* PS/2 mouse */ + void BIOS_PS2Mouse_Startup(Section *sec); + BIOS_PS2Mouse_Startup(NULL); + } + + if (!IS_PC98_ARCH) { + /* this belongs HERE not on-demand from INT 15h! */ + biosConfigSeg = ROMBIOS_GetMemory(16/*one paragraph*/,"BIOS configuration (INT 15h AH=0xC0)",/*paragraph align*/16)>>4; + if (biosConfigSeg != 0) { + PhysPt data = PhysMake(biosConfigSeg,0); + phys_writew(data,8); // 8 Bytes following + if (IS_TANDY_ARCH) { + if (machine==MCH_TANDY) { + // Model ID (Tandy) + phys_writeb(data+2,0xFF); + } else { + // Model ID (PCJR) + phys_writeb(data+2,0xFD); + } + phys_writeb(data+3,0x0A); // Submodel ID + phys_writeb(data+4,0x10); // Bios Revision + /* Tandy doesn't have a 2nd PIC, left as is for now */ + phys_writeb(data+5,(1<<6)|(1<<5)|(1<<4)); // Feature Byte 1 + } else { + if (machine==MCH_MCGA) { + /* PC/2 model 30 model */ + phys_writeb(data+2,0xFA); + phys_writeb(data+3,0x00); // Submodel ID (PS/2) model 30 + } else if (PS1AudioCard) { /* FIXME: Won't work because BIOS_Init() comes before PS1SOUND_Init() */ + phys_writeb(data+2,0xFC); // Model ID (PC) + phys_writeb(data+3,0x0B); // Submodel ID (PS/1). + } else { + phys_writeb(data+2,0xFC); // Model ID (PC) + phys_writeb(data+3,0x00); // Submodel ID + } + phys_writeb(data+4,0x01); // Bios Revision + phys_writeb(data+5,(1<<6)|(1<<5)|(1<<4)); // Feature Byte 1 + } + phys_writeb(data+6,(1<<6)); // Feature Byte 2 + phys_writeb(data+7,0); // Feature Byte 3 + phys_writeb(data+8,0); // Feature Byte 4 + phys_writeb(data+9,0); // Feature Byte 5 + } + } + + // ISA Plug & Play I/O ports + if (!IS_PC98_ARCH) { + ISAPNP_PNP_ADDRESS_PORT = new IO_WriteHandleObject; + ISAPNP_PNP_ADDRESS_PORT->Install(0x279,isapnp_write_port,IO_MB); + ISAPNP_PNP_DATA_PORT = new IO_WriteHandleObject; + ISAPNP_PNP_DATA_PORT->Install(0xA79,isapnp_write_port,IO_MB); + ISAPNP_PNP_READ_PORT = new IO_ReadHandleObject; + ISAPNP_PNP_READ_PORT->Install(ISA_PNP_WPORT,isapnp_read_port,IO_MB); + LOG(LOG_MISC,LOG_DEBUG)("Registered ISA PnP read port at 0x%03x",ISA_PNP_WPORT); + } + + if (enable_integration_device) { + /* integration device callout */ + if (dosbox_int_iocallout == IO_Callout_t_none) + dosbox_int_iocallout = IO_AllocateCallout(IO_TYPE_MB); + if (dosbox_int_iocallout == IO_Callout_t_none) + E_Exit("Failed to get dosbox integration IO callout handle"); + + { + IO_CalloutObject *obj = IO_GetCallout(dosbox_int_iocallout); + if (obj == NULL) E_Exit("Failed to get dosbox integration IO callout"); + obj->Install(0x28,IOMASK_Combine(IOMASK_FULL,IOMASK_Range(4)),dosbox_integration_cb_port_r,dosbox_integration_cb_port_w); + IO_PutCallout(obj); + } + + /* DOSBox integration device */ + if (!IS_PC98_ARCH && isapnpigdevice == NULL && enable_integration_device_pnp) { + isapnpigdevice = new ISAPnPIntegrationDevice; + ISA_PNP_devreg(isapnpigdevice); + } + } + + // ISA Plug & Play BIOS entrypoint + // NTS: Apparently, Windows 95, 98, and ME will re-enumerate and re-install PnP devices if our entry point changes it's address. + if (!IS_PC98_ARCH && ISAPNPBIOS) { + Bitu base; + unsigned int i; + unsigned char c,tmp[256]; + + isapnp_biosstruct_base = base = ROMBIOS_GetMemory(0x21,"ISA Plug & Play BIOS struct",/*paragraph alignment*/0x10); + + if (base == 0) E_Exit("Unable to allocate ISA PnP struct"); + LOG_MSG("ISA Plug & Play BIOS enabled"); + + call_pnp_r = CALLBACK_Allocate(); + call_pnp_rp = PNPentry_real = CALLBACK_RealPointer(call_pnp_r); + CALLBACK_Setup(call_pnp_r,ISAPNP_Handler_RM,CB_RETF,"ISA Plug & Play entry point (real)"); + //LOG_MSG("real entry pt=%08lx\n",PNPentry_real); + + call_pnp_p = CALLBACK_Allocate(); + call_pnp_pp = PNPentry_prot = CALLBACK_RealPointer(call_pnp_p); + CALLBACK_Setup(call_pnp_p,ISAPNP_Handler_PM,CB_RETF,"ISA Plug & Play entry point (protected)"); + //LOG_MSG("prot entry pt=%08lx\n",PNPentry_prot); + + phys_writeb(base+0,'$'); + phys_writeb(base+1,'P'); + phys_writeb(base+2,'n'); + phys_writeb(base+3,'P'); + phys_writeb(base+4,0x10); /* Version: 1.0 */ + phys_writeb(base+5,0x21); /* Length: 0x21 bytes */ + phys_writew(base+6,0x0000); /* Control field: Event notification not supported */ + /* skip checksum atm */ + phys_writed(base+9,0); /* Event notify flag addr: (none) */ + phys_writed(base+0xD,call_pnp_rp); /* Real-mode entry point */ + phys_writew(base+0x11,call_pnp_pp&0xFFFF); /* Protected mode offset */ + phys_writed(base+0x13,(call_pnp_pp >> 12) & 0xFFFF0); /* Protected mode code segment base */ + phys_writed(base+0x17,ISAPNP_ID('D','O','S',0,7,4,0)); /* OEM device identifier (DOSBox 0.740, get it?) */ + phys_writew(base+0x1B,0xF000); /* real-mode data segment */ + phys_writed(base+0x1D,0xF0000); /* protected mode data segment address */ + /* run checksum */ + c=0; + for (i=0;i < 0x21;i++) { + if (i != 8) c += phys_readb(base+i); + } + phys_writeb(base+8,0x100-c); /* checksum value: set so that summing bytes across the struct == 0 */ + + /* input device (keyboard) */ + if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_Keyboard,sizeof(ISAPNP_sysdev_Keyboard),true)) + LOG_MSG("ISAPNP register failed\n"); + + /* input device (mouse) */ + if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_Mouse,sizeof(ISAPNP_sysdev_Mouse),true)) + LOG_MSG("ISAPNP register failed\n"); + + /* DMA controller */ + if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_DMA_Controller,sizeof(ISAPNP_sysdev_DMA_Controller),true)) + LOG_MSG("ISAPNP register failed\n"); + + /* Interrupt controller */ + if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_PIC,sizeof(ISAPNP_sysdev_PIC),true)) + LOG_MSG("ISAPNP register failed\n"); + + /* Timer */ + if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_Timer,sizeof(ISAPNP_sysdev_Timer),true)) + LOG_MSG("ISAPNP register failed\n"); + + /* Realtime clock */ + if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_RTC,sizeof(ISAPNP_sysdev_RTC),true)) + LOG_MSG("ISAPNP register failed\n"); + + /* PC speaker */ + if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_PC_Speaker,sizeof(ISAPNP_sysdev_PC_Speaker),true)) + LOG_MSG("ISAPNP register failed\n"); + + /* System board */ + if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_System_Board,sizeof(ISAPNP_sysdev_System_Board),true)) + LOG_MSG("ISAPNP register failed\n"); + + /* Motherboard PNP resources and general */ + if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_General_ISAPNP,sizeof(ISAPNP_sysdev_General_ISAPNP),true)) + LOG_MSG("ISAPNP register failed\n"); + + /* ISA bus, meaning, a computer with ISA slots. + * The purpose of this device is to convince Windows 95 to automatically install it's + * "ISA Plug and Play bus" so that PnP devices are recognized automatically */ + if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_ISA_BUS,sizeof(ISAPNP_sysdev_ISA_BUS),true)) + LOG_MSG("ISAPNP register failed\n"); + + if (pcibus_enable) { + /* PCI bus, meaning, a computer with PCI slots. + * The purpose of this device is to tell Windows 95 that a PCI bus is present. Without + * this entry, PCI devices will not be recognized until you manually install the PCI driver. */ + if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_PCI_BUS,sizeof(ISAPNP_sysdev_PCI_BUS),true)) + LOG_MSG("ISAPNP register failed\n"); + } + + /* APM BIOS device. To help Windows 95 see our APM BIOS. */ + if (APMBIOS && APMBIOS_pnp) { + LOG_MSG("Registering APM BIOS as ISA Plug & Play BIOS device node"); + if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_APM_BIOS,sizeof(ISAPNP_sysdev_APM_BIOS),true)) + LOG_MSG("ISAPNP register failed\n"); + } + +#if (C_FPU) + /* Numeric Coprocessor */ + if (!ISAPNP_RegisterSysDev(ISAPNP_sysdev_Numeric_Coprocessor,sizeof(ISAPNP_sysdev_Numeric_Coprocessor),true)) + LOG_MSG("ISAPNP register failed\n"); +#endif + + /* RAM resources. we have to construct it */ + /* NTS: We don't do this here, but I have an old Toshiba laptop who's PnP BIOS uses + * this device ID to report both RAM and ROM regions. */ + { + Bitu max = MEM_TotalPages() * 4096; + const unsigned char h1[9] = { + ISAPNP_SYSDEV_HEADER( + ISAPNP_ID('P','N','P',0x0,0xC,0x0,0x1), /* PNP0C01 System device, motherboard resources */ + ISAPNP_TYPE(0x05,0x00,0x00), /* type: Memory, RAM, general */ + 0x0001 | 0x0002) + }; + + i = 0; + memcpy(tmp+i,h1,9); i += 9; /* can't disable, can't configure */ + /*----------allocated--------*/ + tmp[i+0] = 0x80 | 6; /* 32-bit memory range */ + tmp[i+1] = 9; /* length=9 */ + tmp[i+2] = 0; + tmp[i+3] = 0x01; /* writeable, no cache, 8-bit, not shadowable, not ROM */ + host_writed(tmp+i+4,0x00000); /* base */ + host_writed(tmp+i+8,max > 0xA0000 ? 0xA0000 : 0x00000); /* length */ + i += 9+3; + + if (max > 0x100000) { + tmp[i+0] = 0x80 | 6; /* 32-bit memory range */ + tmp[i+1] = 9; /* length=9 */ + tmp[i+2] = 0; + tmp[i+3] = 0x01; + host_writed(tmp+i+4,0x100000); /* base */ + host_writed(tmp+i+8,max-0x100000); /* length */ + i += 9+3; + } + + tmp[i+0] = 0x79; /* END TAG */ + tmp[i+1] = 0x00; + i += 2; + /*-------------possible-----------*/ + tmp[i+0] = 0x79; /* END TAG */ + tmp[i+1] = 0x00; + i += 2; + /*-------------compatible---------*/ + tmp[i+0] = 0x79; /* END TAG */ + tmp[i+1] = 0x00; + i += 2; + + if (!ISAPNP_RegisterSysDev(tmp,i)) + LOG_MSG("ISAPNP register failed\n"); + } + + /* register parallel ports */ + for (Bitu portn=0;portn < 3;portn++) { + Bitu port = mem_readw(BIOS_ADDRESS_LPT1+(portn*2)); + if (port != 0) { + const unsigned char h1[9] = { + ISAPNP_SYSDEV_HEADER( + ISAPNP_ID('P','N','P',0x0,0x4,0x0,0x0), /* PNP0400 Standard LPT printer port */ + ISAPNP_TYPE(0x07,0x01,0x00), /* type: General parallel port */ + 0x0001 | 0x0002) + }; + + i = 0; + memcpy(tmp+i,h1,9); i += 9; /* can't disable, can't configure */ + /*----------allocated--------*/ + tmp[i+0] = (8 << 3) | 7; /* IO resource */ + tmp[i+1] = 0x01; /* 16-bit decode */ + host_writew(tmp+i+2,port); /* min */ + host_writew(tmp+i+4,port); /* max */ + tmp[i+6] = 0x10; /* align */ + tmp[i+7] = 0x03; /* length */ + i += 7+1; + + /* TODO: If/when LPT emulation handles the IRQ, add IRQ resource here */ + + tmp[i+0] = 0x79; /* END TAG */ + tmp[i+1] = 0x00; + i += 2; + /*-------------possible-----------*/ + tmp[i+0] = 0x79; /* END TAG */ + tmp[i+1] = 0x00; + i += 2; + /*-------------compatible---------*/ + tmp[i+0] = 0x79; /* END TAG */ + tmp[i+1] = 0x00; + i += 2; + + if (!ISAPNP_RegisterSysDev(tmp,i)) + LOG_MSG("ISAPNP register failed\n"); + } + } + + void BIOS_Post_register_comports_PNP(); + BIOS_Post_register_comports_PNP(); + + void BIOS_Post_register_IDE(); + BIOS_Post_register_IDE(); + + void BIOS_Post_register_FDC(); + BIOS_Post_register_FDC(); + } + + if (IS_PC98_ARCH) { + /* initialize IRQ0 timer to default tick interval. + * PC-98 does not pre-initialize timer 0 of the PIT to 0xFFFF the way IBM PC/XT/AT do */ + PC98_Interval_Timer_Continue(); + PIC_SetIRQMask(0,true); /* PC-98 keeps the timer off unless INT 1Ch is called to set a timer interval */ + } + + CPU_STI(); + + return CBRET_NONE; + } + CALLBACK_HandlerObject cb_bios_scan_video_bios; + static Bitu cb_bios_scan_video_bios__func(void) { + unsigned long size; + + /* NTS: As far as I can tell, video is integrated into the PC-98 BIOS and there is no separate BIOS */ + if (IS_PC98_ARCH) return CBRET_NONE; + + if (cpu.pmode) E_Exit("BIOS error: VIDEO BIOS SCAN function called while in protected/vm86 mode"); + + if (!bios_has_exec_vga_bios) { + bios_has_exec_vga_bios = true; + if (IS_EGAVGA_ARCH) { + /* make sure VGA BIOS is there at 0xC000:0x0000 */ + if (AdapterROM_Read(0xC0000,&size)) { + LOG(LOG_MISC,LOG_DEBUG)("BIOS VIDEO ROM SCAN found VGA BIOS (size=%lu)",size); + adapter_scan_start = 0xC0000 + size; + + // step back into the callback instruction that triggered this call + reg_eip -= 4; + + // FAR CALL into the VGA BIOS + CPU_CALL(false,0xC000,0x0003,reg_eip); + return CBRET_NONE; + } + else { + LOG(LOG_MISC,LOG_WARN)("BIOS VIDEO ROM SCAN did not find VGA BIOS"); + } + } + else { + // CGA, MDA, Tandy, PCjr. No video BIOS to scan for + } + } + + return CBRET_NONE; + } + CALLBACK_HandlerObject cb_bios_adapter_rom_scan; + static Bitu cb_bios_adapter_rom_scan__func(void) { + unsigned long size; + Bit32u c1; + + /* FIXME: I have no documentation on how PC-98 scans for adapter ROM or even if it supports it */ + if (IS_PC98_ARCH) return CBRET_NONE; + + if (cpu.pmode) E_Exit("BIOS error: ADAPTER ROM function called while in protected/vm86 mode"); + + while (adapter_scan_start < 0xF0000) { + if (AdapterROM_Read(adapter_scan_start,&size)) { + Bit16u segm = (Bit16u)(adapter_scan_start >> 4); + + LOG(LOG_MISC,LOG_DEBUG)("BIOS ADAPTER ROM scan found ROM at 0x%lx (size=%lu)",(unsigned long)adapter_scan_start,size); + + c1 = mem_readd(adapter_scan_start+3); + adapter_scan_start += size; + if (c1 != 0UL) { + LOG(LOG_MISC,LOG_DEBUG)("Running ADAPTER ROM entry point"); + + // step back into the callback instruction that triggered this call + reg_eip -= 4; + + // FAR CALL into the VGA BIOS + CPU_CALL(false,segm,0x0003,reg_eip); + return CBRET_NONE; + } + else { + LOG(LOG_MISC,LOG_DEBUG)("FIXME: ADAPTER ROM entry point does not exist"); + } + } + else { + if (IS_EGAVGA_ARCH) // supposedly newer systems only scan on 2KB boundaries by standard? right? + adapter_scan_start = (adapter_scan_start | 2047UL) + 1UL; + else // while older PC/XT systems scanned on 512-byte boundaries? right? + adapter_scan_start = (adapter_scan_start | 511UL) + 1UL; + } + } + + LOG(LOG_MISC,LOG_DEBUG)("BIOS ADAPTER ROM scan complete"); + return CBRET_NONE; + } + CALLBACK_HandlerObject cb_bios_startup_screen; + static Bitu cb_bios_startup_screen__func(void) { + const char *msg = PACKAGE_STRING " (C) 2002-" COPYRIGHT_END_YEAR " The DOSBox Team\nA fork of DOSBox 0.74 by TheGreatCodeholio\nFor more info visit http://dosbox-x.com\nBased on DOSBox (http://dosbox.com)\n\n"; + int logo_x,logo_y,x,y,rowheight=8; + + y = 2; + x = 2; + logo_y = 2; + logo_x = 80 - 2 - (224/8); + + if (cpu.pmode) E_Exit("BIOS error: STARTUP function called while in protected/vm86 mode"); + + // TODO: For those who would rather not use the VGA graphical modes, add a configuration option to "disable graphical splash". + // We would then revert to a plain text copyright and status message here (and maybe an ASCII art version of the DOSBox logo). + if (IS_VGA_ARCH) { + rowheight = 16; + reg_eax = 18; // 640x480 16-color + CALLBACK_RunRealInt(0x10); + DrawDOSBoxLogoVGA((unsigned int)logo_x*8u,(unsigned int)logo_y*(unsigned int)rowheight); + } + else if (machine == MCH_EGA) { + rowheight = 14; + reg_eax = 16; // 640x350 16-color + CALLBACK_RunRealInt(0x10); + + // color correction: change Dark Puke Yellow to brown + IO_Read(0x3DA); IO_Read(0x3BA); + IO_Write(0x3C0,0x06); + IO_Write(0x3C0,0x14); // red=1 green=1 blue=0 + IO_Read(0x3DA); IO_Read(0x3BA); + IO_Write(0x3C0,0x20); + + DrawDOSBoxLogoVGA((unsigned int)logo_x*8u,(unsigned int)logo_y*(unsigned int)rowheight); + } + else if (machine == MCH_CGA || machine == MCH_MCGA || machine == MCH_PCJR || machine == MCH_AMSTRAD || machine == MCH_TANDY) { + rowheight = 8; + reg_eax = 6; // 640x200 2-color + CALLBACK_RunRealInt(0x10); + + DrawDOSBoxLogoCGA6((unsigned int)logo_x*8u,(unsigned int)logo_y*(unsigned int)rowheight); + } + else if (machine == MCH_PC98) { + // clear the graphics layer + for (unsigned int i=0;i < (80*400);i++) { + mem_writeb(0xA8000+i,0); // B + mem_writeb(0xB0000+i,0); // G + mem_writeb(0xB8000+i,0); // R + mem_writeb(0xE0000+i,0); // E + } + + reg_eax = 0x0C00; // enable text layer (PC-98) + CALLBACK_RunRealInt(0x18); + + reg_eax = 0x1100; // show cursor (PC-98) + CALLBACK_RunRealInt(0x18); + + reg_eax = 0x1300; // set cursor pos (PC-98) + reg_edx = 0x0000; // byte position + CALLBACK_RunRealInt(0x18); + + bios_pc98_posx = x; + + reg_eax = 0x4200; // setup 640x400 graphics + reg_ecx = 0xC000; + CALLBACK_RunRealInt(0x18); + + // enable the 4th bitplane, for 16-color analog graphics mode. + // TODO: When we allow the user to emulate only the 8-color BGR digital mode, + // logo drawing should use an alternate drawing method. + IO_Write(0x6A,0x01); // enable 16-color analog mode (this makes the 4th bitplane appear) + IO_Write(0x6A,0x04); // but we don't need the EGC graphics + // If we caught a game mid-page flip, set the display and VRAM pages back to zero + IO_Write(0xA4,0x00); // display page 0 + IO_Write(0xA6,0x00); // write to page 0 + + // program a VGA-like color palette so we can re-use the VGA logo + for (unsigned int i=0;i < 16;i++) { + unsigned int bias = (i & 8) ? 0x5 : 0x0; + + IO_Write(0xA8,i); // DAC index + if (i != 6) { + IO_Write(0xAA,((i & 2) ? 0xA : 0x0) + bias); // green + IO_Write(0xAC,((i & 4) ? 0xA : 0x0) + bias); // red + IO_Write(0xAE,((i & 1) ? 0xA : 0x0) + bias); // blue + } + else { // brown #6 instead of puke yellow + IO_Write(0xAA,((i & 2) ? 0x5 : 0x0) + bias); // green + IO_Write(0xAC,((i & 4) ? 0xA : 0x0) + bias); // red + IO_Write(0xAE,((i & 1) ? 0xA : 0x0) + bias); // blue + } + } + + DrawDOSBoxLogoPC98((unsigned int)logo_x*8u,(unsigned int)logo_y*(unsigned int)rowheight); + + reg_eax = 0x4000; // show the graphics layer (PC-98) so we can render the DOSBox logo + CALLBACK_RunRealInt(0x18); + } + else { + reg_eax = 3; // 80x25 text + CALLBACK_RunRealInt(0x10); + + // TODO: For CGA, PCjr, and Tandy, we could render a 4-color CGA version of the same logo. + // And for MDA/Hercules, we could render a monochromatic ASCII art version. + } + + if (machine != MCH_PC98) { + reg_eax = 0x0200; // set cursor pos + reg_ebx = 0; // page zero + reg_dh = y; // row 4 + reg_dl = x; // column 20 + CALLBACK_RunRealInt(0x10); + } + + BIOS_Int10RightJustifiedPrint(x,y,msg); + + { + uint64_t sz = (uint64_t)MEM_TotalPages() * (uint64_t)4096; + char tmp[512]; + + if (sz >= ((uint64_t)128 << (uint64_t)20)) + sprintf(tmp,"%uMB memory installed\r\n",(unsigned int)(sz >> (uint64_t)20)); + else + sprintf(tmp,"%uKB memory installed\r\n",(unsigned int)(sz >> (uint64_t)10)); + + BIOS_Int10RightJustifiedPrint(x,y,tmp); + } + + { + char tmp[512]; + const char *card = "?"; + + switch (machine) { + case MCH_CGA: + card = "IBM Color Graphics Adapter"; + break; + case MCH_MCGA: + card = "IBM Multi Color Graphics Adapter"; + break; + case MCH_MDA: + card = "IBM Monochrome Display Adapter"; + break; + case MCH_HERC: + card = "IBM Monochrome Display Adapter (Hercules)"; + break; + case MCH_EGA: + card = "IBM Enhanced Graphics Adapter"; + break; + case MCH_PCJR: + card = "PCjr graohics adapter"; + break; + case MCH_TANDY: + card = "Tandy graohics adapter"; + break; + case MCH_VGA: + switch (svgaCard) { + case SVGA_TsengET4K: + card = "Tseng ET4000 SVGA"; + break; + case SVGA_TsengET3K: + card = "Tseng ET3000 SVGA"; + break; + case SVGA_ParadisePVGA1A: + card = "Paradise SVGA"; + break; + case SVGA_S3Trio: + card = "S3 Trio SVGA"; + break; + default: + card = "Standard VGA"; + break; + } + + break; + case MCH_PC98: + card = "PC98 graphics"; + break; + case MCH_AMSTRAD: + card = "Amstrad graphics"; + break; + default: + abort(); // should not happen + break; + }; + + sprintf(tmp,"Video card is %s\n",card); + BIOS_Int10RightJustifiedPrint(x,y,tmp); + } + + { + char tmp[512]; + const char *cpu = "?"; + + switch (CPU_ArchitectureType) { + case CPU_ARCHTYPE_8086: + cpu = "8086"; + break; + case CPU_ARCHTYPE_80186: + cpu = "80186"; + break; + case CPU_ARCHTYPE_286: + cpu = "286"; + break; + case CPU_ARCHTYPE_386: + cpu = "386"; + break; + case CPU_ARCHTYPE_486OLD: + cpu = "486 (older generation)"; + break; + case CPU_ARCHTYPE_486NEW: + cpu = "486 (later generation)"; + break; + case CPU_ARCHTYPE_PENTIUM: + cpu = "Pentium"; + break; + case CPU_ARCHTYPE_P55CSLOW: + cpu = "Pentium MMX"; + break; + }; + + extern bool enable_fpu; + + sprintf(tmp,"%s CPU present",cpu); + BIOS_Int10RightJustifiedPrint(x,y,tmp); + if (enable_fpu) BIOS_Int10RightJustifiedPrint(x,y," with FPU"); + BIOS_Int10RightJustifiedPrint(x,y,"\n"); + } + + if (APMBIOS) { + BIOS_Int10RightJustifiedPrint(x,y,"Advanced Power Management interface active\n"); + } + + if (ISAPNPBIOS) { + BIOS_Int10RightJustifiedPrint(x,y,"ISA Plug & Play BIOS active\n"); + } + +#if !defined(C_EMSCRIPTEN) + BIOS_Int10RightJustifiedPrint(x,y,"\nHit SPACEBAR to pause at this screen\n"); + y--; /* next message should overprint */ + if (machine != MCH_PC98) { + reg_eax = 0x0200; // set cursor pos + reg_ebx = 0; // page zero + reg_dh = y; // row 4 + reg_dl = x; // column 20 + CALLBACK_RunRealInt(0x10); + } + else { + reg_eax = 0x1300u; // set cursor pos (PC-98) + reg_edx = (((unsigned int)y * 80u) + (unsigned int)x) * 2u; // byte position + CALLBACK_RunRealInt(0x18); + } +#endif + + // TODO: Then at this screen, we can print messages demonstrating the detection of + // IDE devices, floppy, ISA PnP initialization, anything of importance. + // I also envision adding the ability to hit DEL or F2 at this point to enter + // a "BIOS setup" screen where all DOSBox configuration options can be + // modified, with the same look and feel of an old BIOS. + +#if C_EMSCRIPTEN + Bit32u lasttick=GetTicks(); + while ((GetTicks()-lasttick)<1000) { + void CALLBACK_Idle(void); + CALLBACK_Idle(); + emscripten_sleep_with_yield(100); + } +#else + if (!control->opt_fastbioslogo) { + bool wait_for_user = false; + Bit32u lasttick=GetTicks(); + while ((GetTicks()-lasttick)<1000) { + if (machine == MCH_PC98) { + reg_eax = 0x0100; // sense key + CALLBACK_RunRealInt(0x18); + SETFLAGBIT(ZF,reg_bh == 0); + } + else { + reg_eax = 0x0100; + CALLBACK_RunRealInt(0x16); + } + + if (!GETFLAG(ZF)) { + if (machine == MCH_PC98) { + reg_eax = 0x0000; // read key + CALLBACK_RunRealInt(0x18); + } + else { + reg_eax = 0x0000; + CALLBACK_RunRealInt(0x16); + } + + if (reg_al == 32) { // user hit space + BIOS_Int10RightJustifiedPrint(x,y,"Hit ENTER or ESC to continue \n"); // overprint + wait_for_user = true; + break; + } + } + } + + while (wait_for_user) { + if (machine == MCH_PC98) { + reg_eax = 0x0000; // read key + CALLBACK_RunRealInt(0x18); + } + else { + reg_eax = 0x0000; + CALLBACK_RunRealInt(0x16); + } + + if (reg_al == 27/*ESC*/ || reg_al == 13/*ENTER*/) + break; + } + } +#endif + + if (machine == MCH_PC98) { + reg_eax = 0x4100; // hide the graphics layer (PC-98) + CALLBACK_RunRealInt(0x18); + + // clear the graphics layer + for (unsigned int i=0;i < (80*400);i++) { + mem_writeb(0xA8000+i,0); // B + mem_writeb(0xB0000+i,0); // G + mem_writeb(0xB8000+i,0); // R + mem_writeb(0xE0000+i,0); // E + } + + IO_Write(0x6A,0x00); // switch back to 8-color mode + } + else { + // restore 80x25 text mode + reg_eax = 3; + CALLBACK_RunRealInt(0x10); + } + + return CBRET_NONE; + } + CALLBACK_HandlerObject cb_bios_boot; + CALLBACK_HandlerObject cb_bios_bootfail; + CALLBACK_HandlerObject cb_pc98_rombasic; + static Bitu cb_pc98_entry__func(void) { + /* the purpose of this function is to say "N88 ROM BASIC NOT FOUND" */ + int x,y; + + x = y = 0; + + /* PC-98 MS-DOS boot sector may RETF back to the BIOS, and this is where execution ends up */ + BIOS_Int10RightJustifiedPrint(x,y,"N88 ROM BASIC NOT IMPLEMENTED"); + + return CBRET_NONE; + } + static Bitu cb_bios_bootfail__func(void) { + int x,y; + + x = y = 0; + + /* PC-98 MS-DOS boot sector may RETF back to the BIOS, and this is where execution ends up */ + BIOS_Int10RightJustifiedPrint(x,y,"Guest OS failed to boot, returned failure"); + + /* and then after this call, there is a JMP $ to loop endlessly */ + return CBRET_NONE; + } + static Bitu cb_bios_boot__func(void) { + /* Reset/power-on overrides the user's A20 gate preferences. + * It's time to revert back to what the user wants. */ + void A20Gate_TakeUserSetting(Section *sec); + void MEM_A20_Enable(bool enabled); + A20Gate_TakeUserSetting(NULL); + MEM_A20_Enable(false); + + if (cpu.pmode) E_Exit("BIOS error: BOOT function called while in protected/vm86 mode"); + DispatchVMEvent(VM_EVENT_BIOS_BOOT); + + // TODO: If instructed to, follow the INT 19h boot pattern, perhaps follow the BIOS Boot Specification, etc. + + // TODO: If instructed to boot a guest OS... + + /* wipe out the stack so it's not there to interfere with the system */ + reg_esp = 0; + reg_eip = 0; + CPU_SetSegGeneral(cs, 0x60); + CPU_SetSegGeneral(ss, 0x60); + + for (Bitu i=0;i < 0x400;i++) mem_writeb(0x7C00+i,0); + + // Begin booting the DOSBox shell. NOTE: VM_Boot_DOSBox_Kernel will change CS:IP instruction pointer! + if (!VM_Boot_DOSBox_Kernel()) E_Exit("BIOS error: BOOT function failed to boot DOSBox kernel"); + return CBRET_NONE; + } +public: + void write_FFFF_signature() { + /* write the signature at 0xF000:0xFFF0 */ + + // The farjump at the processor reset entry point (jumps to POST routine) + phys_writeb(0xffff0,0xEA); // FARJMP + phys_writew(0xffff1,RealOff(BIOS_DEFAULT_RESET_LOCATION)); // offset + phys_writew(0xffff3,RealSeg(BIOS_DEFAULT_RESET_LOCATION)); // segment + + // write system BIOS date + for(Bitu i = 0; i < strlen(bios_date_string); i++) phys_writeb(0xffff5+i,(Bit8u)bios_date_string[i]); + + /* model byte */ + if (machine==MCH_TANDY || machine==MCH_AMSTRAD) phys_writeb(0xffffe,0xff); /* Tandy model */ + else if (machine==MCH_PCJR) phys_writeb(0xffffe,0xfd); /* PCJr model */ + else if (machine==MCH_MCGA) phys_writeb(0xffffe,0xfa); /* PC/2 model 30 model */ + else phys_writeb(0xffffe,0xfc); /* PC (FIXME: This is listed as model byte PS/2 model 60) */ + + // signature + phys_writeb(0xfffff,0x55); + } + BIOS(Section* configuration):Module_base(configuration){ + /* tandy DAC can be requested in tandy_sound.cpp by initializing this field */ + Bitu wo; + + isapnp_biosstruct_base = 0; + + { // TODO: Eventually, move this to BIOS POST or init phase + Section_prop * section=static_cast(control->GetSection("dosbox")); + + bochs_port_e9 = section->Get_bool("bochs debug port e9"); + + // TODO: motherboard init, especially when we get around to full Intel Triton/i440FX chipset emulation + isa_memory_hole_512kb = section->Get_bool("isa memory hole at 512kb"); + + // FIXME: Erm, well this couldv'e been named better. It refers to the amount of conventional memory + // made available to the operating system below 1MB, which is usually DOS. + dos_conventional_limit = (unsigned int)section->Get_int("dos mem limit"); + + // for PC-98: When accessing the floppy through INT 1Bh, when enabled, run through a waiting loop to make sure + // the timer count is not too high on exit (Ys II) + enable_fdc_timer_hack = section->Get_bool("pc-98 int 1b fdc timer wait"); + + { + std::string s = section->Get_string("unhandled irq handler"); + + if (s == "simple") + unhandled_irq_method = UNHANDLED_IRQ_SIMPLE; + else if (s == "cooperative_2nd") + unhandled_irq_method = UNHANDLED_IRQ_COOPERATIVE_2ND; + // pick default + else if (IS_PC98_ARCH) + unhandled_irq_method = UNHANDLED_IRQ_COOPERATIVE_2ND; + else + unhandled_irq_method = UNHANDLED_IRQ_SIMPLE; + } + } + + if (bochs_port_e9) { + if (BOCHS_PORT_E9 == NULL) { + BOCHS_PORT_E9 = new IO_WriteHandleObject; + BOCHS_PORT_E9->Install(0xE9,bochs_port_e9_write,IO_MB); + } + LOG(LOG_MISC,LOG_DEBUG)("Bochs port E9h emulation is active"); + } + else { + if (BOCHS_PORT_E9 != NULL) { + delete BOCHS_PORT_E9; + BOCHS_PORT_E9 = NULL; + } + } + + /* pick locations */ + BIOS_DEFAULT_RESET_LOCATION = PhysToReal416(ROMBIOS_GetMemory(64/*several callbacks*/,"BIOS default reset location",/*align*/4)); + BIOS_DEFAULT_HANDLER_LOCATION = PhysToReal416(ROMBIOS_GetMemory(1/*IRET*/,"BIOS default handler location",/*align*/4)); + BIOS_DEFAULT_IRQ0_LOCATION = PhysToReal416(ROMBIOS_GetMemory(0x13/*see callback.cpp for IRQ0*/,"BIOS default IRQ0 location",/*align*/4)); + BIOS_DEFAULT_IRQ1_LOCATION = PhysToReal416(ROMBIOS_GetMemory(0x15/*see callback.cpp for IRQ1*/,"BIOS default IRQ1 location",/*align*/4)); + BIOS_DEFAULT_IRQ07_DEF_LOCATION = PhysToReal416(ROMBIOS_GetMemory(7/*see callback.cpp for EOI_PIC1*/,"BIOS default IRQ2-7 location",/*align*/4)); + BIOS_DEFAULT_IRQ815_DEF_LOCATION = PhysToReal416(ROMBIOS_GetMemory(9/*see callback.cpp for EOI_PIC1*/,"BIOS default IRQ8-15 location",/*align*/4)); + + write_FFFF_signature(); + + /* Setup all the interrupt handlers the bios controls */ + + /* INT 8 Clock IRQ Handler */ + call_irq0=CALLBACK_Allocate(); + if (IS_PC98_ARCH) + CALLBACK_Setup(call_irq0,INT8_PC98_Handler,CB_IRET,Real2Phys(BIOS_DEFAULT_IRQ0_LOCATION),"IRQ 0 Clock"); + else + CALLBACK_Setup(call_irq0,INT8_Handler,CB_IRQ0,Real2Phys(BIOS_DEFAULT_IRQ0_LOCATION),"IRQ 0 Clock"); + + /* INT 11 Get equipment list */ + callback[1].Install(&INT11_Handler,CB_IRET,"Int 11 Equipment"); + + /* INT 12 Memory Size default at 640 kb */ + callback[2].Install(&INT12_Handler,CB_IRET,"Int 12 Memory"); + + ulimit = 640; + t_conv = MEM_TotalPages() << 2; /* convert 4096/byte pages -> 1024/byte KB units */ + if (allow_more_than_640kb) { + if (machine == MCH_CGA) + ulimit = 736; /* 640KB + 64KB + 32KB 0x00000-0xB7FFF */ + else if (machine == MCH_HERC || machine == MCH_MDA) + ulimit = 704; /* 640KB + 64KB = 0x00000-0xAFFFF */ + + if (t_conv > ulimit) t_conv = ulimit; + if (t_conv > 640) { /* because the memory emulation has already set things up */ + bool MEM_map_RAM_physmem(Bitu start,Bitu end); + MEM_map_RAM_physmem(0xA0000,(t_conv<<10)-1); + memset(GetMemBase()+(640<<10),0,(t_conv-640)<<10); + } + } + else { + if (t_conv > 640) t_conv = 640; + } + + if (IS_TANDY_ARCH) { + /* reduce reported memory size for the Tandy (32k graphics memory + at the end of the conventional 640k) */ + if (machine==MCH_TANDY && t_conv > 624) t_conv = 624; + } + + /* allow user to further limit the available memory below 1MB */ + if (dos_conventional_limit != 0 && t_conv > dos_conventional_limit) + t_conv = dos_conventional_limit; + + // TODO: Allow dosbox.conf to specify an option to add an EBDA (Extended BIOS Data Area) + // at the top of the DOS conventional limit, which we then reduce further to hold + // it. Most BIOSes past 1992 or so allocate an EBDA. + + /* if requested to emulate an ISA memory hole at 512KB, further limit the memory */ + if (isa_memory_hole_512kb && t_conv > 512) t_conv = 512; + + /* and then unmap RAM between t_conv and ulimit */ + if (t_conv < ulimit) { + Bitu start = (t_conv+3)/4; /* start = 1KB to page round up */ + Bitu end = ulimit/4; /* end = 1KB to page round down */ + if (start < end) MEM_ResetPageHandler_Unmapped(start,end-start); + } + + /* INT 4B. Now we can safely signal error instead of printing "Invalid interrupt 4B" + * whenever we install Windows 95. Note that Windows 95 would call INT 4Bh because + * that is where the Virtual DMA API lies in relation to EMM386.EXE */ + int4b_callback.Install(&INT4B_Handler,CB_IRET,"INT 4B"); + + /* INT 14 Serial Ports */ + callback[3].Install(&INT14_Handler,CB_IRET_STI,"Int 14 COM-port"); + + /* INT 15 Misc Calls */ + callback[4].Install(&INT15_Handler,CB_IRET,"Int 15 Bios"); + + /* INT 17 Printer Routines */ + callback[5].Install(&INT17_Handler,CB_IRET_STI,"Int 17 Printer"); + + /* INT 1A TIME and some other functions */ + callback[6].Install(&INT1A_Handler,CB_IRET_STI,"Int 1a Time"); + + /* INT 1C System Timer tick called from INT 8 */ + callback[7].Install(&INT1C_Handler,CB_IRET,"Int 1c Timer"); + + /* IRQ 8 RTC Handler */ + callback[8].Install(&INT70_Handler,CB_IRET,"Int 70 RTC"); + + /* Irq 9 rerouted to irq 2 */ + callback[9].Install(NULL,CB_IRQ9,"irq 9 bios"); + + // INT 19h: Boot function + callback[10].Install(&INT19_Handler,CB_IRET,"int 19"); + + // INT 76h: IDE IRQ 14 + // This is just a dummy IRQ handler to prevent crashes when + // IDE emulation fires the IRQ and OS's like Win95 expect + // the BIOS to handle the interrupt. + // FIXME: Shouldn't the IRQ send an ACK to the PIC as well?!? + callback[11].Install(&IRQ14_Dummy,CB_IRET_EOI_PIC2,"irq 14 ide"); + + // INT 77h: IDE IRQ 15 + // This is just a dummy IRQ handler to prevent crashes when + // IDE emulation fires the IRQ and OS's like Win95 expect + // the BIOS to handle the interrupt. + // FIXME: Shouldn't the IRQ send an ACK to the PIC as well?!? + callback[12].Install(&IRQ15_Dummy,CB_IRET_EOI_PIC2,"irq 15 ide"); + + // INT 0Eh: IDE IRQ 6 + callback[13].Install(&IRQ15_Dummy,CB_IRET_EOI_PIC1,"irq 6 floppy"); + + // INT 18h: Enter BASIC + // Non-IBM BIOS would display "NO ROM BASIC" here + callback[15].Install(&INT18_Handler,CB_IRET,"int 18"); + + init_vm86_fake_io(); + + /* Irq 2-7 */ + call_irq07default=CALLBACK_Allocate(); + + /* Irq 8-15 */ + call_irq815default=CALLBACK_Allocate(); + + /* BIOS boot stages */ + cb_bios_post.Install(&cb_bios_post__func,CB_RETF,"BIOS POST"); + cb_bios_scan_video_bios.Install(&cb_bios_scan_video_bios__func,CB_RETF,"BIOS Scan Video BIOS"); + cb_bios_adapter_rom_scan.Install(&cb_bios_adapter_rom_scan__func,CB_RETF,"BIOS Adapter ROM scan"); + cb_bios_startup_screen.Install(&cb_bios_startup_screen__func,CB_RETF,"BIOS Startup screen"); + cb_bios_boot.Install(&cb_bios_boot__func,CB_RETF,"BIOS BOOT"); + cb_bios_bootfail.Install(&cb_bios_bootfail__func,CB_RETF,"BIOS BOOT FAIL"); + cb_pc98_rombasic.Install(&cb_pc98_entry__func,CB_RETF,"N88 ROM BASIC"); + + // Compatible POST routine location: jump to the callback + { + Bitu wo_fence; + + wo = Real2Phys(BIOS_DEFAULT_RESET_LOCATION); + wo_fence = wo + 64; + + // POST + phys_writeb(wo+0x00,(Bit8u)0xFE); //GRP 4 + phys_writeb(wo+0x01,(Bit8u)0x38); //Extra Callback instruction + phys_writew(wo+0x02,(Bit16u)cb_bios_post.Get_callback()); //The immediate word + wo += 4; + + // video bios scan + phys_writeb(wo+0x00,(Bit8u)0xFE); //GRP 4 + phys_writeb(wo+0x01,(Bit8u)0x38); //Extra Callback instruction + phys_writew(wo+0x02,(Bit16u)cb_bios_scan_video_bios.Get_callback()); //The immediate word + wo += 4; + + // adapter ROM scan + phys_writeb(wo+0x00,(Bit8u)0xFE); //GRP 4 + phys_writeb(wo+0x01,(Bit8u)0x38); //Extra Callback instruction + phys_writew(wo+0x02,(Bit16u)cb_bios_adapter_rom_scan.Get_callback()); //The immediate word + wo += 4; + + // startup screen + phys_writeb(wo+0x00,(Bit8u)0xFE); //GRP 4 + phys_writeb(wo+0x01,(Bit8u)0x38); //Extra Callback instruction + phys_writew(wo+0x02,(Bit16u)cb_bios_startup_screen.Get_callback()); //The immediate word + wo += 4; + + // user boot hook + if (bios_user_boot_hook != 0) { + phys_writeb(wo+0x00,0x9C); //PUSHF + phys_writeb(wo+0x01,0x9A); //CALL FAR + phys_writew(wo+0x02,0x0000); //seg:0 + phys_writew(wo+0x04,bios_user_boot_hook>>4); + wo += 6; + } + + // boot + BIOS_boot_code_offset = wo; + phys_writeb(wo+0x00,(Bit8u)0xFE); //GRP 4 + phys_writeb(wo+0x01,(Bit8u)0x38); //Extra Callback instruction + phys_writew(wo+0x02,(Bit16u)cb_bios_boot.Get_callback()); //The immediate word + wo += 4; + + // boot fail + BIOS_bootfail_code_offset = wo; + phys_writeb(wo+0x00,(Bit8u)0xFE); //GRP 4 + phys_writeb(wo+0x01,(Bit8u)0x38); //Extra Callback instruction + phys_writew(wo+0x02,(Bit16u)cb_bios_bootfail.Get_callback()); //The immediate word + wo += 4; + + /* fence */ + phys_writeb(wo++,0xEB); // JMP $-2 + phys_writeb(wo++,0xFE); + + if (wo > wo_fence) E_Exit("BIOS boot callback overrun"); + + if (IS_PC98_ARCH) { + /* Boot disks that run N88 basic, stopgap */ + PhysPt bo = 0xE8002; // E800:0002 + + phys_writeb(bo+0x00,(Bit8u)0xFE); //GRP 4 + phys_writeb(bo+0x01,(Bit8u)0x38); //Extra Callback instruction + phys_writew(bo+0x02,(Bit16u)cb_pc98_rombasic.Get_callback()); //The immediate word + + phys_writeb(bo+0x04,0xEB); // JMP $-2 + phys_writeb(bo+0x05,0xFE); + } + } + } + ~BIOS(){ + /* snap the CPU back to real mode. this code thinks in terms of 16-bit real mode + * and if allowed to do it's thing in a 32-bit guest OS like WinNT, will trigger + * a page fault. */ + CPU_Snap_Back_To_Real_Mode(); + + if (BOCHS_PORT_E9) { + delete BOCHS_PORT_E9; + BOCHS_PORT_E9=NULL; + } + if (ISAPNP_PNP_ADDRESS_PORT) { + delete ISAPNP_PNP_ADDRESS_PORT; + ISAPNP_PNP_ADDRESS_PORT=NULL; + } + if (ISAPNP_PNP_DATA_PORT) { + delete ISAPNP_PNP_DATA_PORT; + ISAPNP_PNP_DATA_PORT=NULL; + } + if (ISAPNP_PNP_READ_PORT) { + delete ISAPNP_PNP_READ_PORT; + ISAPNP_PNP_READ_PORT=NULL; + } + if (isapnpigdevice) { + /* ISA PnP will auto-free it */ + isapnpigdevice=NULL; + } + + if (dosbox_int_iocallout != IO_Callout_t_none) { + IO_FreeCallout(dosbox_int_iocallout); + dosbox_int_iocallout = IO_Callout_t_none; + } + + /* abort DAC playing */ + if (tandy_sb.port) { + IO_Write(tandy_sb.port+0xcu,0xd3u); + IO_Write(tandy_sb.port+0xcu,0xd0u); + } + real_writeb(0x40,0xd4,0x00); + if (tandy_DAC_callback[0]) { + Bit32u orig_vector=real_readd(0x40,0xd6); + if (orig_vector==tandy_DAC_callback[0]->Get_RealPointer()) { + /* set IRQ vector to old value */ + Bit8u tandy_irq = 7; + if (tandy_sb.port) tandy_irq = tandy_sb.irq; + else if (tandy_dac.port) tandy_irq = tandy_dac.irq; + Bit8u tandy_irq_vector = tandy_irq; + if (tandy_irq_vector<8) tandy_irq_vector += 8; + else tandy_irq_vector += (0x70-8); + + RealSetVec(tandy_irq_vector,real_readd(0x40,0xd6)); + real_writed(0x40,0xd6,0x00000000); + } + delete tandy_DAC_callback[0]; + delete tandy_DAC_callback[1]; + tandy_DAC_callback[0]=NULL; + tandy_DAC_callback[1]=NULL; + } + + /* encourage the callback objects to uninstall HERE while we're in real mode, NOT during the + * destructor stage where we're back in protected mode */ + for (unsigned int i=0;i < 20;i++) callback[i].Uninstall(); + + /* assume these were allocated */ + CALLBACK_DeAllocate(call_irq0); + CALLBACK_DeAllocate(call_irq07default); + CALLBACK_DeAllocate(call_irq815default); + + /* done */ + CPU_Snap_Back_Restore(); + } +}; + +void BIOS_Enter_Boot_Phase(void) { + /* direct CS:IP right to the instruction that leads to the boot process */ + /* note that since it's a callback instruction it doesn't really matter + * what CS:IP is as long as it points to the instruction */ + reg_eip = BIOS_boot_code_offset & 0xFUL; + CPU_SetSegGeneral(cs, BIOS_boot_code_offset >> 4UL); +} + +void BIOS_SetCOMPort(Bitu port, Bit16u baseaddr) { + switch(port) { + case 0: + mem_writew(BIOS_BASE_ADDRESS_COM1,baseaddr); + mem_writeb(BIOS_COM1_TIMEOUT, 10); // FIXME: Right?? + break; + case 1: + mem_writew(BIOS_BASE_ADDRESS_COM2,baseaddr); + mem_writeb(BIOS_COM2_TIMEOUT, 10); // FIXME: Right?? + break; + case 2: + mem_writew(BIOS_BASE_ADDRESS_COM3,baseaddr); + mem_writeb(BIOS_COM3_TIMEOUT, 10); // FIXME: Right?? + break; + case 3: + mem_writew(BIOS_BASE_ADDRESS_COM4,baseaddr); + mem_writeb(BIOS_COM4_TIMEOUT, 10); // FIXME: Right?? + break; + } +} + +void BIOS_SetLPTPort(Bitu port, Bit16u baseaddr) { + switch(port) { + case 0: + mem_writew(BIOS_ADDRESS_LPT1,baseaddr); + mem_writeb(BIOS_LPT1_TIMEOUT, 10); + break; + case 1: + mem_writew(BIOS_ADDRESS_LPT2,baseaddr); + mem_writeb(BIOS_LPT2_TIMEOUT, 10); + break; + case 2: + mem_writew(BIOS_ADDRESS_LPT3,baseaddr); + mem_writeb(BIOS_LPT3_TIMEOUT, 10); + break; + } +} + +void BIOS_PnP_ComPortRegister(Bitu port,Bitu irq) { + /* add to PnP BIOS */ + if (ISAPNPBIOS) { + unsigned char tmp[256]; + unsigned int i; + + /* register serial ports */ + const unsigned char h1[9] = { + ISAPNP_SYSDEV_HEADER( + ISAPNP_ID('P','N','P',0x0,0x5,0x0,0x1), /* PNP0501 16550A-compatible COM port */ + ISAPNP_TYPE(0x07,0x00,0x02), /* type: RS-232 communcations device, 16550-compatible */ + 0x0001 | 0x0002) + }; + + i = 0; + memcpy(tmp+i,h1,9); i += 9; /* can't disable, can't configure */ + /*----------allocated--------*/ + tmp[i+0] = (8 << 3) | 7; /* IO resource */ + tmp[i+1] = 0x01; /* 16-bit decode */ + host_writew(tmp+i+2,port); /* min */ + host_writew(tmp+i+4,port); /* max */ + tmp[i+6] = 0x10; /* align */ + tmp[i+7] = 0x08; /* length */ + i += 7+1; + + if (irq > 0) { + tmp[i+0] = (4 << 3) | 3; /* IRQ resource */ + host_writew(tmp+i+1,1 << irq); + tmp[i+3] = 0x09; /* HTL=1 LTL=1 */ + i += 3+1; + } + + tmp[i+0] = 0x79; /* END TAG */ + tmp[i+1] = 0x00; + i += 2; + /*-------------possible-----------*/ + tmp[i+0] = 0x79; /* END TAG */ + tmp[i+1] = 0x00; + i += 2; + /*-------------compatible---------*/ + tmp[i+0] = 0x79; /* END TAG */ + tmp[i+1] = 0x00; + i += 2; + + if (!ISAPNP_RegisterSysDev(tmp,i)) { + //LOG_MSG("ISAPNP register failed\n"); + } + } +} + +static BIOS* test = NULL; + +void BIOS_Destroy(Section* /*sec*/){ + ROMBIOS_DumpMemory(); + ISA_PNP_FreeAllDevs(); + if (test != NULL) { + delete test; + test = NULL; + } +} + +void BIOS_OnPowerOn(Section* sec) { + (void)sec;//UNUSED + if (test) delete test; + test = new BIOS(control->GetSection("joystick")); +} + +void swapInNextDisk(bool pressed); +void swapInNextCD(bool pressed); + +void INT10_OnResetComplete(); +void CALLBACK_DeAllocate(Bitu in); + +void MOUSE_Unsetup_DOS(void); +void MOUSE_Unsetup_BIOS(void); + +void BIOS_OnResetComplete(Section *x) { + (void)x;//UNUSED + INT10_OnResetComplete(); + + if (IS_PC98_ARCH) { + void PC98_BIOS_Bank_Switch_Reset(void); + PC98_BIOS_Bank_Switch_Reset(); + } + + if (biosConfigSeg != 0u) { + ROMBIOS_FreeMemory((Bitu)(biosConfigSeg << 4u)); /* remember it was alloc'd paragraph aligned, then saved >> 4 */ + biosConfigSeg = 0u; + } + + call_pnp_rp = 0; + if (call_pnp_r != ~0UL) { + CALLBACK_DeAllocate(call_pnp_r); + call_pnp_r = ~0UL; + } + + call_pnp_pp = 0; + if (call_pnp_p != ~0UL) { + CALLBACK_DeAllocate(call_pnp_p); + call_pnp_p = ~0UL; + } + + MOUSE_Unsetup_DOS(); + MOUSE_Unsetup_BIOS(); + + ISA_PNP_FreeAllSysNodeDevs(); +} + +void BIOS_Init() { + DOSBoxMenu::item *item; + + LOG(LOG_MISC,LOG_DEBUG)("Initializing BIOS"); + + /* make sure the array is zeroed */ + ISAPNP_SysDevNodeCount = 0; + ISAPNP_SysDevNodeLargest = 0; + for (int i=0;i < MAX_ISA_PNP_SYSDEVNODES;i++) ISAPNP_SysDevNodes[i] = NULL; + + /* make sure CD swap and floppy swap mapper events are available */ + MAPPER_AddHandler(swapInNextDisk,MK_d,MMODHOST|MMOD1,"swapimg","SwapFloppy",&item); /* Originally "Swap Image" but this version does not swap CDs */ + item->set_text("Swap floppy"); + + MAPPER_AddHandler(swapInNextCD,MK_c,MMODHOST|MMOD1,"swapcd","SwapCD",&item); /* Variant of "Swap Image" for CDs */ + item->set_text("Swap CD"); + + /* NTS: VM_EVENT_BIOS_INIT this callback must be first. */ + AddExitFunction(AddExitFunctionFuncPair(BIOS_Destroy),false); + AddVMEventFunction(VM_EVENT_POWERON,AddVMEventFunctionFuncPair(BIOS_OnPowerOn)); + AddVMEventFunction(VM_EVENT_RESET_END,AddVMEventFunctionFuncPair(BIOS_OnResetComplete)); +} + +void write_ID_version_string() { + Bitu str_id_at,str_ver_at; + size_t str_id_len,str_ver_len; + + /* NTS: We can't move the version and ID strings, it causes programs like MSD.EXE to lose + * track of the "IBM compatible blahblahblah" string. Which means that apparently + * programs looking for this information have the address hardcoded ALTHOUGH + * experiments show you can move the version string around so long as it's + * +1 from a paragraph boundary */ + /* TODO: *DO* allow dynamic relocation however if the dosbox.conf indicates that the user + * is not interested in IBM BIOS compatability. Also, it would be really cool if + * dosbox.conf could override these strings and the user could enter custom BIOS + * version and ID strings. Heh heh heh.. :) */ + str_id_at = 0xFE00E; + str_ver_at = 0xFE061; + str_id_len = strlen(bios_type_string)+1; + str_ver_len = strlen(bios_version_string)+1; + if (!IS_PC98_ARCH) { + /* need to mark these strings off-limits so dynamic allocation does not overwrite them */ + ROMBIOS_GetMemory((Bitu)str_id_len+1,"BIOS ID string",1,str_id_at); + ROMBIOS_GetMemory((Bitu)str_ver_len+1,"BIOS version string",1,str_ver_at); + } + if (str_id_at != 0) { + for (size_t i=0;i < str_id_len;i++) phys_writeb(str_id_at+(PhysPt)i,(Bit8u)bios_type_string[i]); + } + if (str_ver_at != 0) { + for (size_t i=0;i < str_ver_len;i++) phys_writeb(str_ver_at+(PhysPt)i,(Bit8u)bios_version_string[i]); + } +} + +extern Bit8u int10_font_08[256 * 8]; + +/* NTS: Do not use callbacks! This function is called before CALLBACK_Init() */ +void ROMBIOS_Init() { + Section_prop * section=static_cast(control->GetSection("dosbox")); + Bitu oi,i; + + // log + LOG(LOG_MISC,LOG_DEBUG)("Initializing ROM BIOS"); + + oi = (Bitu)section->Get_int("rom bios minimum size"); /* in KB */ + oi = (oi + 3u) & ~3u; /* round to 4KB page */ + if (oi > 128u) oi = 128u; + if (oi == 0u) { + if (IS_PC98_ARCH) + oi = 96u; // BIOS standard range is E8000-FFFFF + else + oi = 64u; + } + if (oi < 8) oi = 8; /* because of some of DOSBox's fixed ROM structures we can only go down to 8KB */ + rombios_minimum_size = (oi << 10); /* convert to minimum, using size coming downward from 1MB */ + + oi = (Bitu)section->Get_int("rom bios allocation max"); /* in KB */ + oi = (oi + 3u) & ~3u; /* round to 4KB page */ + if (oi > 128u) oi = 128u; + if (oi == 0u) { + if (IS_PC98_ARCH) + oi = 96u; + else + oi = 64u; + } + if (oi < 8u) oi = 8u; /* because of some of DOSBox's fixed ROM structures we can only go down to 8KB */ + oi <<= 10u; + if (oi < rombios_minimum_size) oi = rombios_minimum_size; + rombios_minimum_location = 0x100000ul - oi; /* convert to minimum, using size coming downward from 1MB */ + + LOG(LOG_BIOS,LOG_DEBUG)("ROM BIOS range: 0x%05X-0xFFFFF",(int)rombios_minimum_location); + LOG(LOG_BIOS,LOG_DEBUG)("ROM BIOS range according to minimum size: 0x%05X-0xFFFFF",(int)(0x100000 - rombios_minimum_size)); + + if (IS_PC98_ARCH && rombios_minimum_location > 0xE8000) + LOG(LOG_BIOS,LOG_DEBUG)("Caution: Minimum ROM base higher than E8000 will prevent use of actual PC-98 BIOS image or N88 BASIC"); + + if (!MEM_map_ROM_physmem(rombios_minimum_location,0xFFFFF)) E_Exit("Unable to map ROM region as ROM"); + + /* and the BIOS alias at the top of memory (TODO: what about 486/Pentium emulation where the BIOS at the 4GB top is different + * from the BIOS at the legacy 1MB boundary because of shadowing and/or decompressing from ROM at boot? */ + { + uint64_t top = (uint64_t)1UL << (uint64_t)MEM_get_address_bits(); + if (top >= ((uint64_t)1UL << (uint64_t)21UL)) { /* 2MB or more */ + unsigned long alias_base,alias_end; + + alias_base = (unsigned long)top + (unsigned long)rombios_minimum_location - (unsigned long)0x100000UL; + alias_end = (unsigned long)top - (unsigned long)1UL; + + LOG(LOG_BIOS,LOG_DEBUG)("ROM BIOS also mapping alias to 0x%08lx-0x%08lx",alias_base,alias_end); + if (!MEM_map_ROM_alias_physmem(alias_base,alias_end)) { + void MEM_cut_RAM_up_to(Bitu addr); + + /* it's possible if memory aliasing is set that memsize is too large to make room. + * let memory emulation know where the ROM BIOS starts so it can unmap the RAM pages, + * reduce the memory reported to the OS, and try again... */ + LOG(LOG_BIOS,LOG_DEBUG)("No room for ROM BIOS alias, reducing reported memory and unmapping RAM pages to make room"); + MEM_cut_RAM_up_to(alias_base); + + if (!MEM_map_ROM_alias_physmem(alias_base,alias_end)) + E_Exit("Unable to map ROM region as ROM alias"); + } + } + } + + /* set up allocation */ + rombios_alloc.name = "ROM BIOS"; + rombios_alloc.topDownAlloc = true; + rombios_alloc.initSetRange(rombios_minimum_location,0xFFFF0 - 1); + + write_ID_version_string(); + + /* some structures when enabled are fixed no matter what */ + if (rom_bios_8x8_cga_font && !IS_PC98_ARCH) { + /* line 139, int10_memory.cpp: the 8x8 font at 0xF000:FA6E, first 128 chars. + * allocate this NOW before other things get in the way */ + if (ROMBIOS_GetMemory(128*8,"BIOS 8x8 font (first 128 chars)",1,0xFFA6E) == 0) { + LOG_MSG("WARNING: Was not able to mark off 0xFFA6E off-limits for 8x8 font"); + } + } + + /* PC-98 BIOS vectors appear to reside at segment 0xFD80. This is so common some games + * use it (through INT 1Dh) to detect whether they are running on PC-98 or not (issue #927). + * + * Note that INT 1Dh is one of the few BIOS interrupts not intercepted by PC-98 MS-DOS */ + if (IS_PC98_ARCH) { + if (ROMBIOS_GetMemory(128,"PC-98 INT vector stub segment 0xFD80",1,0xFD800) == 0) { + LOG_MSG("WARNING: Was not able to mark off 0xFD800 off-limits for PC-98 int vector stubs"); + } + } + + /* PC-98 BIOSes have a LIO interface at segment F990 with graphic subroutines for Microsoft BASIC */ + if (IS_PC98_ARCH) { + if (ROMBIOS_GetMemory(256,"PC-98 LIO graphic ROM BIOS library",1,0xF9900) == 0) { + LOG_MSG("WARNING: Was not able to mark off 0xF9900 off-limits for PC-98 LIO graphics library"); + } + } + + /* install the font */ + if (rom_bios_8x8_cga_font) { + for (i=0;i<128*8;i++) { + phys_writeb(PhysMake(0xf000,0xfa6e)+i,int10_font_08[i]); + } + } + + /* we allow dosbox.conf to specify a binary blob to load into ROM BIOS and execute after reset. + * we allow this for both hacker curiosity and automated CPU testing. */ + { + std::string path = section->Get_string("call binary on reset"); + struct stat st; + + if (!path.empty() && stat(path.c_str(),&st) == 0 && S_ISREG(st.st_mode) && st.st_size <= (128u*1024u)) { + Bitu base = ROMBIOS_GetMemory((Bitu)st.st_size,"User reset vector binary",16u/*page align*/,0u); + + if (base != 0) { + FILE *fp = fopen(path.c_str(),"rb"); + + if (fp != NULL) { + /* NTS: Make sure memory base != NULL, and that it fits within 1MB. + * memory code allocates a minimum 1MB of memory even if + * guest memory is less than 1MB because ROM BIOS emulation + * depends on it. */ + assert(GetMemBase() != NULL); + assert((base+(Bitu)st.st_size) <= 0x100000ul); + fread(GetMemBase()+base,(size_t)st.st_size,1u,fp); + fclose(fp); + + LOG_MSG("User reset vector binary '%s' loaded at 0x%lx",path.c_str(),(unsigned long)base); + bios_user_reset_vector_blob = base; + } + else { + LOG_MSG("WARNING: Unable to open file to load user reset vector binary '%s' into ROM BIOS memory",path.c_str()); + } + } + else { + LOG_MSG("WARNING: Unable to load user reset vector binary '%s' into ROM BIOS memory",path.c_str()); + } + } + } + + /* we allow dosbox.conf to specify a binary blob to load into ROM BIOS and execute just before boot. + * we allow this for both hacker curiosity and automated CPU testing. */ + { + std::string path = section->Get_string("call binary on boot"); + struct stat st; + + if (!path.empty() && stat(path.c_str(),&st) == 0 && S_ISREG(st.st_mode) && st.st_size <= (128u*1024u)) { + Bitu base = ROMBIOS_GetMemory((Bitu)st.st_size,"User boot hook binary",16u/*page align*/,0u); + + if (base != 0) { + FILE *fp = fopen(path.c_str(),"rb"); + + if (fp != NULL) { + /* NTS: Make sure memory base != NULL, and that it fits within 1MB. + * memory code allocates a minimum 1MB of memory even if + * guest memory is less than 1MB because ROM BIOS emulation + * depends on it. */ + assert(GetMemBase() != NULL); + assert((base+(Bitu)st.st_size) <= 0x100000ul); + fread(GetMemBase()+base,(size_t)st.st_size,1u,fp); + fclose(fp); + + LOG_MSG("User boot hook binary '%s' loaded at 0x%lx",path.c_str(),(unsigned long)base); + bios_user_boot_hook = base; + } + else { + LOG_MSG("WARNING: Unable to open file to load user boot hook binary '%s' into ROM BIOS memory",path.c_str()); + } + } + else { + LOG_MSG("WARNING: Unable to load user boot hook binary '%s' into ROM BIOS memory",path.c_str()); + } + } + } +} + +//! \brief Updates the state of a lockable key. +void UpdateKeyWithLed(int nVirtKey, int flagAct, int flagLed); + +void BIOS_SynchronizeNumLock() +{ +#if defined(WIN32) + UpdateKeyWithLed(VK_NUMLOCK, BIOS_KEYBOARD_FLAGS1_NUMLOCK_ACTIVE, BIOS_KEYBOARD_LEDS_NUM_LOCK); +#endif +} + +void BIOS_SynchronizeCapsLock() +{ +#if defined(WIN32) + UpdateKeyWithLed(VK_CAPITAL, BIOS_KEYBOARD_FLAGS1_CAPS_LOCK_ACTIVE, BIOS_KEYBOARD_LEDS_CAPS_LOCK); +#endif +} + +void BIOS_SynchronizeScrollLock() +{ +#if defined(WIN32) + UpdateKeyWithLed(VK_SCROLL, BIOS_KEYBOARD_FLAGS1_SCROLL_LOCK_ACTIVE, BIOS_KEYBOARD_LEDS_SCROLL_LOCK); +#endif +} + +void UpdateKeyWithLed(int nVirtKey, int flagAct, int flagLed) +{ +#if defined(WIN32) + + const auto state = GetKeyState(nVirtKey); + + const auto flags1 = BIOS_KEYBOARD_FLAGS1; + const auto flags2 = BIOS_KEYBOARD_LEDS; + + auto flag1 = mem_readb(flags1); + auto flag2 = mem_readb(flags2); + + if (state & 1) + { + flag1 |= flagAct; + flag2 |= flagLed; + } + else + { + flag1 &= ~flagAct; + flag2 &= ~flagLed; + } + + mem_writeb(flags1, flag1); + mem_writeb(flags2, flag2); + +#else + + (void)nVirtKey; + (void)flagAct; + (void)flagLed; + +#endif +} diff --git a/src/ints/bios_disk.cpp b/src/ints/bios_disk.cpp index 3ddab5128..a9e393a9e 100644 --- a/src/ints/bios_disk.cpp +++ b/src/ints/bios_disk.cpp @@ -1,2244 +1,2244 @@ -/* - * Copyright (C) 2002-2015 The DOSBox Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include "dosbox.h" -#include "callback.h" -#include "bios.h" -#include "bios_disk.h" -#include "regs.h" -#include "mem.h" -#include "dos_inc.h" /* for Drives[] */ -#include "../dos/drives.h" -#include "mapper.h" -#include "ide.h" - -#if defined(_MSC_VER) -# pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */ -#endif - -extern bool int13_extensions_enable; - -diskGeo DiskGeometryList[] = { - { 160, 8, 1, 40, 0, 512, 64, 1, 0xFE}, // IBM PC double density 5.25" single-sided 160KB - { 180, 9, 1, 40, 0, 512, 64, 2, 0xFC}, // IBM PC double density 5.25" single-sided 180KB - { 200, 10, 1, 40, 0, 512, 0, 0, 0}, // DEC Rainbow double density 5.25" single-sided 200KB (I think...) - { 320, 8, 2, 40, 1, 512, 112, 2, 0xFF}, // IBM PC double density 5.25" double-sided 320KB - { 360, 9, 2, 40, 1, 512, 112, 2, 0xFD}, // IBM PC double density 5.25" double-sided 360KB - { 400, 10, 2, 40, 1, 512, 0, 0, 0}, // DEC Rainbow double density 5.25" double-sided 400KB (I think...) - { 640, 8, 2, 80, 3, 512, 112, 2, 0xFB}, // IBM PC double density 3.5" double-sided 640KB - { 720, 9, 2, 80, 3, 512, 112, 2, 0xF9}, // IBM PC double density 3.5" double-sided 720KB - {1200, 15, 2, 80, 2, 512, 224, 1, 0xF9}, // IBM PC double density 5.25" double-sided 1.2MB - {1440, 18, 2, 80, 4, 512, 224, 1, 0xF0}, // IBM PC high density 3.5" double-sided 1.44MB - {2880, 36, 2, 80, 6, 512, 240, 2, 0xF0}, // IBM PC high density 3.5" double-sided 2.88MB - - {1232, 8, 2, 77, 7, 1024,192, 1, 0xFE}, // NEC PC-98 high density 3.5" double-sided 1.2MB "3-mode" - - { 0, 0, 0, 0, 0, 0, 0, 0, 0} -}; - -Bitu call_int13 = 0; -Bitu diskparm0 = 0, diskparm1 = 0; -static Bit8u last_status; -static Bit8u last_drive; -Bit16u imgDTASeg; -RealPt imgDTAPtr; -DOS_DTA *imgDTA; -bool killRead; -static bool swapping_requested; - -void CMOS_SetRegister(Bitu regNr, Bit8u val); //For setting equipment word - -/* 2 floppys and 2 harddrives, max */ -bool imageDiskChange[MAX_DISK_IMAGES]={false}; -imageDisk *imageDiskList[MAX_DISK_IMAGES]={NULL}; -imageDisk *diskSwap[MAX_SWAPPABLE_DISKS]={NULL}; -Bits swapPosition; - -imageDisk *GetINT13FloppyDrive(unsigned char drv) { - if (drv >= 2) - return NULL; - return imageDiskList[drv]; -} - -imageDisk *GetINT13HardDrive(unsigned char drv) { - if (drv < 0x80 || drv >= (0x80+MAX_DISK_IMAGES-2)) - return NULL; - - return imageDiskList[drv-0x80]; -} - -void FreeBIOSDiskList() { - for (int i=0;i < MAX_DISK_IMAGES;i++) { - if (imageDiskList[i] != NULL) { - if (i >= 2) IDE_Hard_Disk_Detach(i); - imageDiskList[i]->Release(); - imageDiskList[i] = NULL; - } - } - - for (int j=0;j < MAX_SWAPPABLE_DISKS;j++) { - if (diskSwap[j] != NULL) { - diskSwap[j]->Release(); - diskSwap[j] = NULL; - } - } -} - -//update BIOS disk parameter tables for first two hard drives -void updateDPT(void) { - Bit32u tmpheads, tmpcyl, tmpsect, tmpsize; - PhysPt dpphysaddr[2] = { CALLBACK_PhysPointer(diskparm0), CALLBACK_PhysPointer(diskparm1) }; - for (int i = 0; i < 2; i++) { - tmpheads = 0; tmpcyl = 0; tmpsect = 0; tmpsize = 0; - if (imageDiskList[i + 2] != NULL) { - imageDiskList[i + 2]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize); - } - phys_writew(dpphysaddr[i], (Bit16u)tmpcyl); - phys_writeb(dpphysaddr[i] + 0x2, (Bit8u)tmpheads); - phys_writew(dpphysaddr[i] + 0x3, 0); - phys_writew(dpphysaddr[i] + 0x5, tmpcyl == 0 ? 0 : (Bit16u)-1); - phys_writeb(dpphysaddr[i] + 0x7, 0); - phys_writeb(dpphysaddr[i] + 0x8, tmpcyl == 0 ? 0 : (0xc0 | (((tmpheads) > 8) << 3))); - phys_writeb(dpphysaddr[i] + 0x9, 0); - phys_writeb(dpphysaddr[i] + 0xa, 0); - phys_writeb(dpphysaddr[i] + 0xb, 0); - phys_writew(dpphysaddr[i] + 0xc, (Bit16u)tmpcyl); - phys_writeb(dpphysaddr[i] + 0xe, (Bit8u)tmpsect); - } -} - -void incrementFDD(void) { - Bit16u equipment=mem_readw(BIOS_CONFIGURATION); - if(equipment&1) { - Bitu numofdisks = (equipment>>6)&3; - numofdisks++; - if(numofdisks > 1) numofdisks=1;//max 2 floppies at the moment - equipment&=~0x00C0; - equipment|=(numofdisks<<6); - } else equipment|=1; - mem_writew(BIOS_CONFIGURATION,equipment); - CMOS_SetRegister(0x14, (Bit8u)(equipment&0xff)); -} - -int swapInDisksSpecificDrive = -1; -// -1 = swap across A: and B: (DOSBox / DOSBox-X default behavior) -// 0 = swap across A: only -// 1 = swap across B: only - -void swapInDisks(void) { - bool allNull = true; - Bits diskcount = 0; - Bits diskswapcount = 2; - Bits diskswapdrive = 0; - Bits swapPos = swapPosition; - int i; - - /* Check to make sure that there is at least one setup image */ - for(i=0;i= 0 && swapInDisksSpecificDrive <= 1) { - diskswapdrive = swapInDisksSpecificDrive; - diskswapcount = 1; - } - - /* If only one disk is loaded, this loop will load the same disk in dive A and drive B */ - while(diskcount < diskswapcount) { - if(diskSwap[swapPos] != NULL) { - LOG_MSG("Loaded drive %d disk %d from swaplist position %d - \"%s\"", (int)diskswapdrive, (int)diskcount, (int)swapPos, diskSwap[swapPos]->diskname.c_str()); - - if (imageDiskList[diskswapdrive] != NULL) - imageDiskList[diskswapdrive]->Release(); - - imageDiskList[diskswapdrive] = diskSwap[swapPos]; - imageDiskList[diskswapdrive]->Addref(); - - imageDiskChange[diskswapdrive] = true; - - diskcount++; - diskswapdrive++; - } - - swapPos++; - if(swapPos>=MAX_SWAPPABLE_DISKS) swapPos=0; - } -} - -bool getSwapRequest(void) { - bool sreq=swapping_requested; - swapping_requested = false; - return sreq; -} - -void swapInNextDisk(bool pressed) { - if (!pressed) - return; - DriveManager::CycleAllDisks(); - /* Hack/feature: rescan all disks as well */ - LOG_MSG("Diskcaching reset for floppy drives."); - for(Bitu i=0;i<2;i++) { /* Swap A: and B: where DOSBox mainline would run through ALL drive letters */ - if (Drives[i] != NULL) { - Drives[i]->EmptyCache(); - Drives[i]->MediaChange(); - } - } - swapPosition++; - if(diskSwap[swapPosition] == NULL) swapPosition = 0; - swapInDisks(); - swapping_requested = true; -} - -void swapInNextCD(bool pressed) { - if (!pressed) - return; - DriveManager::CycleAllCDs(); - /* Hack/feature: rescan all disks as well */ - LOG_MSG("Diskcaching reset for normal mounted drives."); - for(Bitu i=2;iEmptyCache(); - Drives[i]->MediaChange(); - } - } -} - - -Bit8u imageDisk::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data,unsigned int req_sector_size) { - Bit32u sectnum; - - if (req_sector_size == 0) - req_sector_size = sector_size; - if (req_sector_size != sector_size) - return 0x05; - - sectnum = ( (cylinder * heads + head) * sectors ) + sector - 1L; - - return Read_AbsoluteSector(sectnum, data); -} - -Bit8u imageDisk::Read_AbsoluteSector(Bit32u sectnum, void * data) { - Bit64u bytenum,res; - int got; - - bytenum = (Bit64u)sectnum * (Bit64u)sector_size; - if ((bytenum + sector_size) > this->image_length) { - LOG_MSG("Attempt to read invalid sector in Read_AbsoluteSector for sector %lu.\n", (unsigned long)sectnum); - return 0x05; - } - bytenum += image_base; - - //LOG_MSG("Reading sectors %ld at bytenum %I64d", sectnum, bytenum); - - fseeko64(diskimg,(off_t)bytenum,SEEK_SET); - res = (Bit64u)ftello64(diskimg); - if (res != bytenum) { - LOG_MSG("fseek() failed in Read_AbsoluteSector for sector %lu. Want=%llu Got=%llu\n", - (unsigned long)sectnum,(unsigned long long)bytenum,(unsigned long long)res); - return 0x05; - } - - got = (int)fread(data, 1, sector_size, diskimg); - if ((unsigned int)got != sector_size) { - LOG_MSG("fread() failed in Read_AbsoluteSector for sector %lu. Want=%u got=%d\n", - (unsigned long)sectnum,(unsigned int)sector_size,(unsigned int)got); - return 0x05; - } - - return 0x00; -} - -Bit8u imageDisk::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,const void * data,unsigned int req_sector_size) { - Bit32u sectnum; - - if (req_sector_size == 0) - req_sector_size = sector_size; - if (req_sector_size != sector_size) - return 0x05; - - sectnum = ( (cylinder * heads + head) * sectors ) + sector - 1L; - - return Write_AbsoluteSector(sectnum, data); -} - - -Bit8u imageDisk::Write_AbsoluteSector(Bit32u sectnum, const void *data) { - Bit64u bytenum; - - bytenum = (Bit64u)sectnum * sector_size; - if ((bytenum + sector_size) > this->image_length) { - LOG_MSG("Attempt to read invalid sector in Write_AbsoluteSector for sector %lu.\n", (unsigned long)sectnum); - return 0x05; - } - bytenum += image_base; - - //LOG_MSG("Writing sectors to %ld at bytenum %d", sectnum, bytenum); - - fseeko64(diskimg,(off_t)bytenum,SEEK_SET); - if ((Bit64u)ftello64(diskimg) != bytenum) - LOG_MSG("WARNING: fseek() failed in Write_AbsoluteSector for sector %lu\n",(unsigned long)sectnum); - - size_t ret=fwrite(data, sector_size, 1, diskimg); - - return ((ret>0)?0x00:0x05); - -} - -void imageDisk::Set_Reserved_Cylinders(Bitu resCyl) { - reserved_cylinders = resCyl; -} - -Bit32u imageDisk::Get_Reserved_Cylinders() { - return reserved_cylinders; -} - -imageDisk::imageDisk(IMAGE_TYPE class_id) { - heads = 0; - cylinders = 0; - image_base = 0; - sectors = 0; - refcount = 0; - sector_size = 512; - image_length = 0; - reserved_cylinders = 0; - diskimg = NULL; - this->class_id = class_id; - active = false; - hardDrive = false; -} - -imageDisk::imageDisk(FILE* diskimg, const char* diskName, Bit32u cylinders, Bit32u heads, Bit32u sectors, Bit32u sector_size, bool hardDrive) { - if (diskName) this->diskname = diskName; - this->cylinders = cylinders; - this->heads = heads; - this->sectors = sectors; - image_base = 0; - this->image_length = (Bit64u)cylinders * heads * sectors * sector_size; - refcount = 0; - this->sector_size = sector_size; - this->diskSizeK = this->image_length / 1024; - reserved_cylinders = 0; - this->diskimg = diskimg; - class_id = ID_BASE; - active = true; - this->hardDrive = hardDrive; -} - -/* .HDI and .FDI header (NP2) */ -#pragma pack(push,1) -typedef struct { - uint8_t dummy[4]; // +0x00 - uint8_t hddtype[4]; // +0x04 - uint8_t headersize[4]; // +0x08 - uint8_t hddsize[4]; // +0x0C - uint8_t sectorsize[4]; // +0x10 - uint8_t sectors[4]; // +0x14 - uint8_t surfaces[4]; // +0x18 - uint8_t cylinders[4]; // +0x1C -} HDIHDR; // =0x20 - -typedef struct { - uint8_t dummy[4]; // +0x00 - uint8_t fddtype[4]; // +0x04 - uint8_t headersize[4]; // +0x08 - uint8_t fddsize[4]; // +0x0C - uint8_t sectorsize[4]; // +0x10 - uint8_t sectors[4]; // +0x14 - uint8_t surfaces[4]; // +0x18 - uint8_t cylinders[4]; // +0x1C -} FDIHDR; // =0x20 - -typedef struct { - char sig[16]; // +0x000 - char comment[0x100]; // +0x010 - UINT8 headersize[4]; // +0x110 - uint8_t prot; // +0x114 - uint8_t nhead; // +0x115 - uint8_t _unknown_[10]; // +0x116 -} NFDHDR; // =0x120 - -typedef struct { - char sig[16]; // +0x000 - char comment[0x100]; // +0x010 - UINT8 headersize[4]; // +0x110 - uint8_t prot; // +0x114 - uint8_t nhead; // +0x115 - uint8_t _unknown_[10]; // +0x116 - uint32_t trackheads[164]; // +0x120 - uint32_t addinfo; // +0x3b0 - uint8_t _unknown2_[12]; // +0x3b4 -} NFDHDRR1; // =0x3c0 - -typedef struct { - uint8_t log_cyl; // +0x0 - uint8_t log_head; // +0x1 - uint8_t log_rec; // +0x2 - uint8_t sec_len_pow2; // +0x3 sz = 128 << len_pow2 - uint8_t flMFM; // +0x4 - uint8_t flDDAM; // +0x5 - uint8_t byStatus; // +0x6 - uint8_t bySTS0; // +0x7 - uint8_t bySTS1; // +0x8 - uint8_t bySTS2; // +0x9 - uint8_t byRetry; // +0xA - uint8_t byPDA; // +0xB - uint8_t _unknown_[4]; // +0xC -} NFDHDR_ENTRY; // =0x10 - -typedef struct { - char szFileID[15]; // 識別ID "T98HDDIMAGE.R0" - char Reserve1[1]; // 予約 - char szComment[0x100]; // イメージコメント(ASCIIz) - uint32_t dwHeadSize; // ヘッダ部のサイズ - uint32_t dwCylinder; // シリンダ数 - uint16_t wHead; // ヘッド数 - uint16_t wSect; // 1トラックあたりのセクタ数 - uint16_t wSectLen; // セクタ長 - char Reserve2[2]; // 予約 - char Reserve3[0xe0]; // 予約 -}NHD_FILE_HEAD,*LP_NHD_FILE_HEAD; -#pragma pack(pop) - -imageDisk::imageDisk(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk) { - heads = 0; - cylinders = 0; - image_base = 0; - image_length = (Bit64u)imgSizeK * (Bit64u)1024; - sectors = 0; - refcount = 0; - sector_size = 512; - reserved_cylinders = 0; - diskimg = imgFile; - class_id = ID_BASE; - diskSizeK = imgSizeK; - - if (imgName != NULL) - diskname = (const char*)imgName; - - active = false; - hardDrive = isHardDisk; - if(!isHardDisk) { - Bit8u i=0; - bool founddisk = false; - - if (imgName != NULL) { - char *ext = strrchr((char*)imgName,'.'); - if (ext != NULL) { - if (!strcasecmp(ext,".fdi")) { - if (imgSizeK >= 160) { - FDIHDR fdihdr; - - // PC-98 .FDI images appear to be 4096 bytes of a short header and many zeros. - // followed by a straight sector dump of the disk. The header is NOT NECESSARILY - // 4KB in size, but usually is. - LOG_MSG("Image file has .FDI extension, assuming FDI image and will take on parameters in header."); - - assert(sizeof(fdihdr) == 0x20); - if (fseek(imgFile,0,SEEK_SET) == 0 && ftell(imgFile) == 0 && - fread(&fdihdr,sizeof(fdihdr),1,imgFile) == 1) { - uint32_t ofs = host_readd(fdihdr.headersize); - uint32_t fddsize = host_readd(fdihdr.fddsize); /* includes header */ - uint32_t sectorsize = host_readd(fdihdr.sectorsize); - - if (sectorsize != 0 && ((sectorsize & (sectorsize - 1)) == 0/*is power of 2*/) && - sectorsize >= 256 && sectorsize <= 1024 && - ofs != 0 && (ofs % sectorsize) == 0/*offset is nonzero and multiple of sector size*/ && - (ofs % 1024) == 0/*offset is a multiple of 1024 because of imgSizeK*/ && - fddsize >= sectorsize && (fddsize/1024) <= (imgSizeK+4)) { - - founddisk = true; - sector_size = sectorsize; - imgSizeK -= (ofs / 1024); - image_base = ofs; - image_length -= ofs; - LOG_MSG("FDI header: sectorsize is %u bytes/sector, header is %u bytes, fdd size (plus header) is %u bytes", - (unsigned int)sectorsize,(unsigned int)ofs,(unsigned int)fddsize); - - /* take on the geometry. */ - sectors = host_readd(fdihdr.sectors); - heads = host_readd(fdihdr.surfaces); - cylinders = host_readd(fdihdr.cylinders); - LOG_MSG("FDI: Geometry is C/H/S %u/%u/%u", - (unsigned int)cylinders,(unsigned int)heads,(unsigned int)sectors); - } - else { - LOG_MSG("FDI header rejected. sectorsize=%u headersize=%u fddsize=%u", - (unsigned int)sectorsize,(unsigned int)ofs,(unsigned int)fddsize); - } - } - else { - LOG_MSG("Unable to read .FDI header"); - } - } - } - } - } - - if (sectors == 0 && heads == 0 && cylinders == 0) { - while (DiskGeometryList[i].ksize!=0x0) { - if ((DiskGeometryList[i].ksize==imgSizeK) || - (DiskGeometryList[i].ksize+1==imgSizeK)) { - if (DiskGeometryList[i].ksize!=imgSizeK) - LOG_MSG("ImageLoader: image file with additional data, might not load!"); - founddisk = true; - active = true; - floppytype = i; - heads = DiskGeometryList[i].headscyl; - cylinders = DiskGeometryList[i].cylcount; - sectors = DiskGeometryList[i].secttrack; - sector_size = DiskGeometryList[i].bytespersect; - LOG_MSG("Identified '%s' as C/H/S %u/%u/%u %u bytes/sector", - imgName,cylinders,heads,sectors,sector_size); - break; - } - i++; - } - } - if(!founddisk) { - active = false; - } - } - else { /* hard disk */ - if (imgName != NULL) { - char *ext = strrchr((char*)imgName,'.'); - if (ext != NULL) { - if (!strcasecmp(ext,".nhd")) { - if (imgSizeK >= 160) { - NHD_FILE_HEAD nhdhdr; - - LOG_MSG("Image file has .NHD extension, assuming NHD image and will take on parameters in header."); - - assert(sizeof(nhdhdr) == 0x200); - if (fseek(imgFile,0,SEEK_SET) == 0 && ftell(imgFile) == 0 && - fread(&nhdhdr,sizeof(nhdhdr),1,imgFile) == 1 && - host_readd((ConstHostPt)(&nhdhdr.dwHeadSize)) >= 0x200 && - !memcmp(nhdhdr.szFileID,"T98HDDIMAGE.R0\0",15)) { - uint32_t ofs = host_readd((ConstHostPt)(&nhdhdr.dwHeadSize)); - uint32_t sectorsize = host_readw((ConstHostPt)(&nhdhdr.wSectLen)); - - if (sectorsize != 0 && ((sectorsize & (sectorsize - 1)) == 0/*is power of 2*/) && - sectorsize >= 256 && sectorsize <= 1024 && - ofs != 0 && (ofs % sectorsize) == 0/*offset is nonzero and multiple of sector size*/) { - - sector_size = sectorsize; - imgSizeK -= (ofs / 1024); - image_base = ofs; - image_length -= ofs; - LOG_MSG("NHD header: sectorsize is %u bytes/sector, header is %u bytes", - (unsigned int)sectorsize,(unsigned int)ofs); - - /* take on the geometry. - * PC-98 IPL1 support will need it to make sense of the partition table. */ - sectors = host_readw((ConstHostPt)(&nhdhdr.wSect)); - heads = host_readw((ConstHostPt)(&nhdhdr.wHead)); - cylinders = host_readd((ConstHostPt)(&nhdhdr.dwCylinder)); - LOG_MSG("NHD: Geometry is C/H/S %u/%u/%u", - (unsigned int)cylinders,(unsigned int)heads,(unsigned int)sectors); - } - else { - LOG_MSG("NHD header rejected. sectorsize=%u headersize=%u", - (unsigned int)sectorsize,(unsigned int)ofs); - } - } - else { - LOG_MSG("Unable to read .NHD header"); - } - } - } - if (!strcasecmp(ext,".hdi")) { - if (imgSizeK >= 160) { - HDIHDR hdihdr; - - // PC-98 .HDI images appear to be 4096 bytes of a short header and many zeros. - // followed by a straight sector dump of the disk. The header is NOT NECESSARILY - // 4KB in size, but usually is. - LOG_MSG("Image file has .HDI extension, assuming HDI image and will take on parameters in header."); - - assert(sizeof(hdihdr) == 0x20); - if (fseek(imgFile,0,SEEK_SET) == 0 && ftell(imgFile) == 0 && - fread(&hdihdr,sizeof(hdihdr),1,imgFile) == 1) { - uint32_t ofs = host_readd(hdihdr.headersize); - uint32_t hddsize = host_readd(hdihdr.hddsize); /* includes header */ - uint32_t sectorsize = host_readd(hdihdr.sectorsize); - - if (sectorsize != 0 && ((sectorsize & (sectorsize - 1)) == 0/*is power of 2*/) && - sectorsize >= 256 && sectorsize <= 1024 && - ofs != 0 && (ofs % sectorsize) == 0/*offset is nonzero and multiple of sector size*/ && - (ofs % 1024) == 0/*offset is a multiple of 1024 because of imgSizeK*/ && - hddsize >= sectorsize && (hddsize/1024) <= (imgSizeK+4)) { - - sector_size = sectorsize; - imgSizeK -= (ofs / 1024); - image_base = ofs; - image_length -= ofs; - LOG_MSG("HDI header: sectorsize is %u bytes/sector, header is %u bytes, hdd size (plus header) is %u bytes", - (unsigned int)sectorsize,(unsigned int)ofs,(unsigned int)hddsize); - - /* take on the geometry. - * PC-98 IPL1 support will need it to make sense of the partition table. */ - sectors = host_readd(hdihdr.sectors); - heads = host_readd(hdihdr.surfaces); - cylinders = host_readd(hdihdr.cylinders); - LOG_MSG("HDI: Geometry is C/H/S %u/%u/%u", - (unsigned int)cylinders,(unsigned int)heads,(unsigned int)sectors); - } - else { - LOG_MSG("HDI header rejected. sectorsize=%u headersize=%u hddsize=%u", - (unsigned int)sectorsize,(unsigned int)ofs,(unsigned int)hddsize); - } - } - else { - LOG_MSG("Unable to read .HDI header"); - } - } - } - } - } - - if (sectors == 0 || heads == 0 || cylinders == 0) - active = false; - } -} - -void imageDisk::Set_Geometry(Bit32u setHeads, Bit32u setCyl, Bit32u setSect, Bit32u setSectSize) { - Bitu bigdisk_shift = 0; - - if (IS_PC98_ARCH) { - /* TODO: PC-98 has it's own 4096 cylinder limit */ - } - else { - if(setCyl > 16384) LOG_MSG("This disk image is too big."); - else if(setCyl > 8192) bigdisk_shift = 4; - else if(setCyl > 4096) bigdisk_shift = 3; - else if(setCyl > 2048) bigdisk_shift = 2; - else if(setCyl > 1024) bigdisk_shift = 1; - } - - heads = setHeads << bigdisk_shift; - cylinders = setCyl >> bigdisk_shift; - sectors = setSect; - sector_size = setSectSize; - active = true; -} - -void imageDisk::Get_Geometry(Bit32u * getHeads, Bit32u *getCyl, Bit32u *getSect, Bit32u *getSectSize) { - *getHeads = heads; - *getCyl = cylinders; - *getSect = sectors; - *getSectSize = sector_size; -} - -Bit8u imageDisk::GetBiosType(void) { - if(!hardDrive) { - return (Bit8u)DiskGeometryList[floppytype].biosval; - } else return 0; -} - -Bit32u imageDisk::getSectSize(void) { - return sector_size; -} - -static Bitu GetDosDriveNumber(Bitu biosNum) { - switch(biosNum) { - case 0x0: - return 0x0; - case 0x1: - return 0x1; - case 0x80: - return 0x2; - case 0x81: - return 0x3; - case 0x82: - return 0x4; - case 0x83: - return 0x5; - default: - return 0x7f; - } -} - -static bool driveInactive(Bitu driveNum) { - if(driveNum>=(2 + MAX_HDD_IMAGES)) { - LOG(LOG_BIOS,LOG_ERROR)("Disk %d non-existant", (int)driveNum); - last_status = 0x01; - CALLBACK_SCF(true); - return true; - } - if(imageDiskList[driveNum] == NULL) { - LOG(LOG_BIOS,LOG_ERROR)("Disk %d not active", (int)driveNum); - last_status = 0x01; - CALLBACK_SCF(true); - return true; - } - if(!imageDiskList[driveNum]->active) { - LOG(LOG_BIOS,LOG_ERROR)("Disk %d not active", (int)driveNum); - last_status = 0x01; - CALLBACK_SCF(true); - return true; - } - return false; -} - -static struct { - Bit8u sz; - Bit8u res; - Bit16u num; - Bit16u off; - Bit16u seg; - Bit32u sector; -} dap; - -static void readDAP(Bit16u seg, Bit16u off) { - dap.sz = real_readb(seg,off++); - dap.res = real_readb(seg,off++); - dap.num = real_readw(seg,off); off += 2; - dap.off = real_readw(seg,off); off += 2; - dap.seg = real_readw(seg,off); off += 2; - - /* Although sector size is 64-bit, 32-bit 2TB limit should be more than enough */ - dap.sector = real_readd(seg,off); off +=4; - - if (real_readd(seg,off)) { - E_Exit("INT13: 64-bit sector addressing not supported"); - } -} - -void IDE_ResetDiskByBIOS(unsigned char disk); -void IDE_EmuINT13DiskReadByBIOS(unsigned char disk,unsigned int cyl,unsigned int head,unsigned sect); -void IDE_EmuINT13DiskReadByBIOS_LBA(unsigned char disk,uint64_t lba); - -static Bitu INT13_DiskHandler(void) { - Bit16u segat, bufptr; - Bit8u sectbuf[512]; - Bitu drivenum; - Bitu i,t; - last_drive = reg_dl; - drivenum = GetDosDriveNumber(reg_dl); - bool any_images = false; - for(i = 0;i < MAX_DISK_IMAGES;i++) { - if(imageDiskList[i]) any_images=true; - } - - // unconditionally enable the interrupt flag - CALLBACK_SIF(true); - - /* map out functions 0x40-0x48 if not emulating INT 13h extensions */ - if (!int13_extensions_enable && reg_ah >= 0x40 && reg_ah <= 0x48) { - LOG_MSG("Warning: Guest is attempting to use INT 13h extensions (AH=0x%02X). Set 'int 13 extensions=1' if you want to enable them.\n",reg_ah); - reg_ah=0xff; - CALLBACK_SCF(true); - return CBRET_NONE; - } - - //drivenum = 0; - //LOG_MSG("INT13: Function %x called on drive %x (dos drive %d)", reg_ah, reg_dl, drivenum); - switch(reg_ah) { - case 0x0: /* Reset disk */ - { - /* if there aren't any diskimages (so only localdrives and virtual drives) - * always succeed on reset disk. If there are diskimages then and only then - * do real checks - */ - if (any_images && driveInactive(drivenum)) { - /* driveInactive sets carry flag if the specified drive is not available */ - if ((machine==MCH_CGA) || (machine==MCH_AMSTRAD) || (machine==MCH_PCJR)) { - /* those bioses call floppy drive reset for invalid drive values */ - if (((imageDiskList[0]) && (imageDiskList[0]->active)) || ((imageDiskList[1]) && (imageDiskList[1]->active))) { - if (machine!=MCH_PCJR && reg_dl<0x80) reg_ip++; - last_status = 0x00; - CALLBACK_SCF(false); - } - } - return CBRET_NONE; - } - if (machine!=MCH_PCJR && reg_dl<0x80) reg_ip++; - if (reg_dl >= 0x80) IDE_ResetDiskByBIOS(reg_dl); - last_status = 0x00; - CALLBACK_SCF(false); - } - break; - case 0x1: /* Get status of last operation */ - - if(last_status != 0x00) { - reg_ah = last_status; - CALLBACK_SCF(true); - } else { - reg_ah = 0x00; - CALLBACK_SCF(false); - } - break; - case 0x2: /* Read sectors */ - if (reg_al==0) { - reg_ah = 0x01; - CALLBACK_SCF(true); - return CBRET_NONE; - } - if (!any_images) { - // Inherit the Earth cdrom (uses it as disk test) - if (((reg_dl&0x80)==0x80) && (reg_dh==0) && ((reg_cl&0x3f)==1)) { - reg_ah = 0; - CALLBACK_SCF(false); - return CBRET_NONE; - } - } - if (driveInactive(drivenum)) { - reg_ah = 0xff; - CALLBACK_SCF(true); - return CBRET_NONE; - } - - /* INT 13h is limited to 512 bytes/sector (as far as I know). - * The sector buffer in this function is limited to 512 bytes/sector, - * so this is also a protection against overruning the stack if you - * mount a PC-98 disk image (1024 bytes/sector) and try to read it with INT 13h. */ - if (imageDiskList[drivenum]->sector_size > sizeof(sectbuf)) { - LOG(LOG_MISC,LOG_DEBUG)("INT 13h: Read failed because disk bytes/sector on drive %c is too large",(char)drivenum+'A'); - - imageDiskChange[drivenum] = false; - - reg_ah = 0x80; /* timeout */ - CALLBACK_SCF(true); - return CBRET_NONE; - } - - /* If the disk changed, the first INT 13h read will signal an error and set AH = 0x06 to indicate disk change */ - if (drivenum < 2 && imageDiskChange[drivenum]) { - LOG(LOG_MISC,LOG_DEBUG)("INT 13h: Failing first read of drive %c to indicate disk change",(char)drivenum+'A'); - - imageDiskChange[drivenum] = false; - - reg_ah = 0x06; /* diskette changed or removed */ - CALLBACK_SCF(true); - return CBRET_NONE; - } - - segat = SegValue(es); - bufptr = reg_bx; - for(i=0;iRead_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)((reg_cl & 63)+i), sectbuf); - - /* IDE emulation: simulate change of IDE state that would occur on a real machine after INT 13h */ - IDE_EmuINT13DiskReadByBIOS(reg_dl, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)reg_dh, (Bit32u)((reg_cl & 63)+i)); - - if((last_status != 0x00) || (killRead)) { - LOG_MSG("Error in disk read"); - killRead = false; - reg_ah = 0x04; - CALLBACK_SCF(true); - return CBRET_NONE; - } - for(t=0;t<512;t++) { - real_writeb(segat,bufptr,sectbuf[t]); - bufptr++; - } - } - reg_ah = 0x00; - CALLBACK_SCF(false); - break; - case 0x3: /* Write sectors */ - - if(driveInactive(drivenum)) { - reg_ah = 0xff; - CALLBACK_SCF(true); - return CBRET_NONE; - } - - /* INT 13h is limited to 512 bytes/sector (as far as I know). - * The sector buffer in this function is limited to 512 bytes/sector, - * so this is also a protection against overruning the stack if you - * mount a PC-98 disk image (1024 bytes/sector) and try to read it with INT 13h. */ - if (imageDiskList[drivenum]->sector_size > sizeof(sectbuf)) { - LOG(LOG_MISC,LOG_DEBUG)("INT 13h: Write failed because disk bytes/sector on drive %c is too large",(char)drivenum+'A'); - - imageDiskChange[drivenum] = false; - - reg_ah = 0x80; /* timeout */ - CALLBACK_SCF(true); - return CBRET_NONE; - } - - bufptr = reg_bx; - for(i=0;igetSectSize();t++) { - sectbuf[t] = real_readb(SegValue(es),bufptr); - bufptr++; - } - - last_status = imageDiskList[drivenum]->Write_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0) << 2)), (Bit32u)((reg_cl & 63) + i), §buf[0]); - if(last_status != 0x00) { - CALLBACK_SCF(true); - return CBRET_NONE; - } - } - reg_ah = 0x00; - CALLBACK_SCF(false); - break; - case 0x04: /* Verify sectors */ - if (reg_al==0) { - reg_ah = 0x01; - CALLBACK_SCF(true); - return CBRET_NONE; - } - if(driveInactive(drivenum)) return CBRET_NONE; - - /* TODO: Finish coding this section */ - /* - segat = SegValue(es); - bufptr = reg_bx; - for(i=0;iRead_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)((reg_cl & 63)+i), sectbuf); - if(last_status != 0x00) { - LOG_MSG("Error in disk read"); - CALLBACK_SCF(true); - return CBRET_NONE; - } - for(t=0;t<512;t++) { - real_writeb(segat,bufptr,sectbuf[t]); - bufptr++; - } - }*/ - reg_ah = 0x00; - //Qbix: The following codes don't match my specs. al should be number of sector verified - //reg_al = 0x10; /* CRC verify failed */ - //reg_al = 0x00; /* CRC verify succeeded */ - CALLBACK_SCF(false); - - break; - case 0x05: /* Format track */ - /* ignore it. I just fucking want FORMAT.COM to write the FAT structure for God's sake */ - LOG_MSG("WARNING: Format track ignored\n"); - CALLBACK_SCF(false); - reg_ah = 0x00; - break; - case 0x06: /* Format track set bad sector flags */ - /* ignore it. I just fucking want FORMAT.COM to write the FAT structure for God's sake */ - LOG_MSG("WARNING: Format track set bad sector flags ignored (6)\n"); - CALLBACK_SCF(false); - reg_ah = 0x00; - break; - case 0x07: /* Format track set bad sector flags */ - /* ignore it. I just fucking want FORMAT.COM to write the FAT structure for God's sake */ - LOG_MSG("WARNING: Format track set bad sector flags ignored (7)\n"); - CALLBACK_SCF(false); - reg_ah = 0x00; - break; - case 0x08: /* Get drive parameters */ - if(driveInactive(drivenum)) { - last_status = 0x07; - reg_ah = last_status; - CALLBACK_SCF(true); - return CBRET_NONE; - } - reg_ax = 0x00; - reg_bl = imageDiskList[drivenum]->GetBiosType(); - Bit32u tmpheads, tmpcyl, tmpsect, tmpsize; - imageDiskList[drivenum]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize); - if (tmpcyl==0) LOG(LOG_BIOS,LOG_ERROR)("INT13 DrivParm: cylinder count zero!"); - else tmpcyl--; // cylinder count -> max cylinder - if (tmpheads==0) LOG(LOG_BIOS,LOG_ERROR)("INT13 DrivParm: head count zero!"); - else tmpheads--; // head count -> max head - - /* older BIOSes were known to subtract 1 or 2 additional "reserved" cylinders. - * some code, such as Windows 3.1 WDCTRL, might assume that fact. emulate that here */ - { - Bit32u reserv = imageDiskList[drivenum]->Get_Reserved_Cylinders(); - if (tmpcyl > reserv) tmpcyl -= reserv; - else tmpcyl = 0; - } - - reg_ch = (Bit8u)(tmpcyl & 0xff); - reg_cl = (Bit8u)(((tmpcyl >> 2) & 0xc0) | (tmpsect & 0x3f)); - reg_dh = (Bit8u)tmpheads; - last_status = 0x00; - if (reg_dl&0x80) { // harddisks - reg_dl = 0; - for (int index = 2; index < MAX_DISK_IMAGES; index++) { - if (imageDiskList[index] != NULL) reg_dl++; - } - } else { // floppy disks - reg_dl = 0; - if(imageDiskList[0] != NULL) reg_dl++; - if(imageDiskList[1] != NULL) reg_dl++; - } - CALLBACK_SCF(false); - break; - case 0x11: /* Recalibrate drive */ - reg_ah = 0x00; - CALLBACK_SCF(false); - break; - case 0x17: /* Set disk type for format */ - /* Pirates! needs this to load */ - killRead = true; - reg_ah = 0x00; - CALLBACK_SCF(false); - break; - case 0x41: /* Check Extensions Present */ - if ((reg_bx == 0x55aa) && !(driveInactive(drivenum))) { - LOG_MSG("INT13: Check Extensions Present for drive: 0x%x", reg_dl); - reg_ah=0x1; /* 1.x extension supported */ - reg_bx=0xaa55; /* Extensions installed */ - reg_cx=0x1; /* Extended disk access functions (AH=42h-44h,47h,48h) supported */ - CALLBACK_SCF(false); - break; - } - LOG_MSG("INT13: AH=41h, Function not supported 0x%x for drive: 0x%x", reg_bx, reg_dl); - CALLBACK_SCF(true); - break; - case 0x42: /* Extended Read Sectors From Drive */ - /* Read Disk Address Packet */ - readDAP(SegValue(ds),reg_si); - - if (dap.num==0) { - reg_ah = 0x01; - CALLBACK_SCF(true); - return CBRET_NONE; - } - if (!any_images) { - // Inherit the Earth cdrom (uses it as disk test) - if (((reg_dl&0x80)==0x80) && (reg_dh==0) && ((reg_cl&0x3f)==1)) { - reg_ah = 0; - CALLBACK_SCF(false); - return CBRET_NONE; - } - } - if (driveInactive(drivenum)) { - reg_ah = 0xff; - CALLBACK_SCF(true); - return CBRET_NONE; - } - - segat = dap.seg; - bufptr = dap.off; - for(i=0;iRead_AbsoluteSector(dap.sector+i, sectbuf); - - IDE_EmuINT13DiskReadByBIOS_LBA(reg_dl,dap.sector+i); - - if((last_status != 0x00) || (killRead)) { - LOG_MSG("Error in disk read"); - killRead = false; - reg_ah = 0x04; - CALLBACK_SCF(true); - return CBRET_NONE; - } - for(t=0;t<512;t++) { - real_writeb(segat,bufptr,sectbuf[t]); - bufptr++; - } - } - reg_ah = 0x00; - CALLBACK_SCF(false); - break; - case 0x43: /* Extended Write Sectors to Drive */ - if(driveInactive(drivenum)) { - reg_ah = 0xff; - CALLBACK_SCF(true); - return CBRET_NONE; - } - - /* Read Disk Address Packet */ - readDAP(SegValue(ds),reg_si); - bufptr = dap.off; - for(i=0;igetSectSize();t++) { - sectbuf[t] = real_readb(dap.seg,bufptr); - bufptr++; - } - - last_status = imageDiskList[drivenum]->Write_AbsoluteSector(dap.sector+i, §buf[0]); - if(last_status != 0x00) { - CALLBACK_SCF(true); - return CBRET_NONE; - } - } - reg_ah = 0x00; - CALLBACK_SCF(false); - break; - case 0x48: { /* get drive parameters */ - uint16_t bufsz; - - if(driveInactive(drivenum)) { - reg_ah = 0xff; - CALLBACK_SCF(true); - return CBRET_NONE; - } - - segat = SegValue(ds); - bufptr = reg_si; - bufsz = real_readw(segat,bufptr+0); - if (bufsz < 0x1A) { - reg_ah = 0xff; - CALLBACK_SCF(true); - return CBRET_NONE; - } - if (bufsz > 0x1E) bufsz = 0x1E; - else bufsz = 0x1A; - - Bit32u tmpheads, tmpcyl, tmpsect, tmpsize; - imageDiskList[drivenum]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize); - - real_writew(segat,bufptr+0x00,bufsz); - real_writew(segat,bufptr+0x02,0x0003); /* C/H/S valid, DMA boundary errors handled */ - real_writed(segat,bufptr+0x04,tmpcyl); - real_writed(segat,bufptr+0x08,tmpheads); - real_writed(segat,bufptr+0x0C,tmpsect); - real_writed(segat,bufptr+0x10,tmpcyl*tmpheads*tmpsect); - real_writed(segat,bufptr+0x14,0); - real_writew(segat,bufptr+0x18,512); - if (bufsz >= 0x1E) - real_writed(segat,bufptr+0x1A,0xFFFFFFFF); /* no EDD information available */ - - reg_ah = 0x00; - CALLBACK_SCF(false); - } break; - default: - LOG(LOG_BIOS,LOG_ERROR)("INT13: Function %x called on drive %x (dos drive %d)", (int)reg_ah, (int)reg_dl, (int)drivenum); - reg_ah=0xff; - CALLBACK_SCF(true); - } - return CBRET_NONE; -} - -void CALLBACK_DeAllocate(Bitu in); - -void BIOS_UnsetupDisks(void) { - if (call_int13 != 0) { - CALLBACK_DeAllocate(call_int13); - RealSetVec(0x13,0); /* zero INT 13h for now */ - call_int13 = 0; - } - if (diskparm0 != 0) { - CALLBACK_DeAllocate(diskparm0); - diskparm0 = 0; - } - if (diskparm1 != 0) { - CALLBACK_DeAllocate(diskparm1); - diskparm1 = 0; - } -} - -void BIOS_SetupDisks(void) { - unsigned int i; - - if (IS_PC98_ARCH) { - // TODO - return; - } - -/* TODO Start the time correctly */ - call_int13=CALLBACK_Allocate(); - CALLBACK_Setup(call_int13,&INT13_DiskHandler,CB_INT13,"Int 13 Bios disk"); - RealSetVec(0x13,CALLBACK_RealPointer(call_int13)); - - //release the drives after a soft reset - FreeBIOSDiskList(); - - /* FIXME: Um... these aren't callbacks. Why are they allocated as callbacks? We have ROM general allocation now. */ - diskparm0 = CALLBACK_Allocate(); - CALLBACK_SetDescription(diskparm0,"BIOS Disk 0 parameter table"); - diskparm1 = CALLBACK_Allocate(); - CALLBACK_SetDescription(diskparm1,"BIOS Disk 1 parameter table"); - swapPosition = 0; - - RealSetVec(0x41,CALLBACK_RealPointer(diskparm0)); - RealSetVec(0x46,CALLBACK_RealPointer(diskparm1)); - - PhysPt dp0physaddr=CALLBACK_PhysPointer(diskparm0); - PhysPt dp1physaddr=CALLBACK_PhysPointer(diskparm1); - for(i=0;i<16;i++) { - phys_writeb(dp0physaddr+i,0); - phys_writeb(dp1physaddr+i,0); - } - - imgDTASeg = 0; - -/* Setup the Bios Area */ - mem_writeb(BIOS_HARDDISK_COUNT,2); - - killRead = false; - swapping_requested = false; -} - -// VFD *.FDD floppy disk format support - -Bit8u imageDiskVFD::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data,unsigned int req_sector_size) { - vfdentry *ent; - - if (req_sector_size == 0) - req_sector_size = sector_size; - -// LOG_MSG("VFD read sector: CHS %u/%u/%u sz=%u",cylinder,head,sector,req_sector_size); - - ent = findSector(head,cylinder,sector,req_sector_size); - if (ent == NULL) return 0x05; - if (ent->getSectorSize() != req_sector_size) return 0x05; - - if (ent->hasSectorData()) { - fseek(diskimg,ent->data_offset,SEEK_SET); - if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05; - if (fread(data,req_sector_size,1,diskimg) != 1) return 0x05; - return 0; - } - else if (ent->hasFill()) { - memset(data,ent->fillbyte,req_sector_size); - return 0x00; - } - - return 0x05; -} - -Bit8u imageDiskVFD::Read_AbsoluteSector(Bit32u sectnum, void * data) { - unsigned int c,h,s; - - if (sectors == 0 || heads == 0) - return 0x05; - - s = (sectnum % sectors) + 1; - h = (sectnum / sectors) % heads; - c = (sectnum / sectors / heads); - return Read_Sector(h,c,s,data); -} - -imageDiskVFD::vfdentry *imageDiskVFD::findSector(Bit8u head,Bit8u track,Bit8u sector/*TODO: physical head?*/,unsigned int req_sector_size) { - std::vector::iterator i = dents.begin(); - unsigned char szb=0xFF; - - if (req_sector_size == 0) - req_sector_size = sector_size; - - if (req_sector_size != ~0U) { - unsigned int c = req_sector_size; - while (c >= 128U) { - c >>= 1U; - szb++; - } - -// LOG_MSG("req=%u c=%u szb=%u",req_sector_size,c,szb); - - if (szb > 8 || c != 64U) - return NULL; - } - - while (i != dents.end()) { - imageDiskVFD::vfdentry &ent = *i; - - if (ent.head == head && - ent.track == track && - ent.sector == sector && - (ent.sizebyte == szb || req_sector_size == ~0U)) - return &(*i); - - i++; - } - - return NULL; -} - -Bit8u imageDiskVFD::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,const void * data,unsigned int req_sector_size) { - unsigned long new_offset; - unsigned char tmp[12]; - vfdentry *ent; - -// LOG_MSG("VFD write sector: CHS %u/%u/%u",cylinder,head,sector); - - if (req_sector_size == 0) - req_sector_size = sector_size; - - ent = findSector(head,cylinder,sector,req_sector_size); - if (ent == NULL) return 0x05; - if (ent->getSectorSize() != req_sector_size) return 0x05; - - if (ent->hasSectorData()) { - fseek(diskimg,ent->data_offset,SEEK_SET); - if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05; - if (fwrite(data,req_sector_size,1,diskimg) != 1) return 0x05; - return 0; - } - else if (ent->hasFill()) { - bool isfill = false; - - /* well, is the data provided one character repeated? - * note the format cannot represent a fill byte of 0xFF */ - if (((unsigned char*)data)[0] != 0xFF) { - unsigned int i=1; - - do { - if (((unsigned char*)data)[i] == ((unsigned char*)data)[0]) { - if ((++i) == req_sector_size) { - isfill = true; - break; // yes! - } - } - else { - break; // nope - } - } while (1); - } - - if (ent->entry_offset == 0) return 0x05; - - if (isfill) { - fseek(diskimg,ent->entry_offset,SEEK_SET); - if ((uint32_t)ftell(diskimg) != ent->entry_offset) return 0x05; - if (fread(tmp,12,1,diskimg) != 1) return 0x05; - - tmp[0x04] = ((unsigned char*)data)[0]; // change the fill byte - - LOG_MSG("VFD write: 'fill' sector changing fill byte to 0x%x",tmp[0x04]); - - fseek(diskimg,ent->entry_offset,SEEK_SET); - if ((uint32_t)ftell(diskimg) != ent->entry_offset) return 0x05; - if (fwrite(tmp,12,1,diskimg) != 1) return 0x05; - } - else { - fseek(diskimg,0,SEEK_END); - new_offset = (unsigned long)ftell(diskimg); - - /* we have to change it from a fill sector to an actual sector */ - LOG_MSG("VFD write: changing 'fill' sector to one with data (data at %lu)",(unsigned long)new_offset); - - fseek(diskimg,ent->entry_offset,SEEK_SET); - if ((uint32_t)ftell(diskimg) != ent->entry_offset) return 0x05; - if (fread(tmp,12,1,diskimg) != 1) return 0x05; - - tmp[0x00] = ent->track; - tmp[0x01] = ent->head; - tmp[0x02] = ent->sector; - tmp[0x03] = ent->sizebyte; - tmp[0x04] = 0xFF; // no longer a fill byte - tmp[0x05] = 0x00; // TODO ?? - tmp[0x06] = 0x00; // TODO ?? - tmp[0x07] = 0x00; // TODO ?? - *((uint32_t*)(tmp+8)) = new_offset; - ent->fillbyte = 0xFF; - ent->data_offset = (uint32_t)new_offset; - - fseek(diskimg,ent->entry_offset,SEEK_SET); - if ((uint32_t)ftell(diskimg) != ent->entry_offset) return 0x05; - if (fwrite(tmp,12,1,diskimg) != 1) return 0x05; - - fseek(diskimg,ent->data_offset,SEEK_SET); - if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05; - if (fwrite(data,req_sector_size,1,diskimg) != 1) return 0x05; - } - } - - return 0x05; -} - -Bit8u imageDiskVFD::Write_AbsoluteSector(Bit32u sectnum,const void *data) { - unsigned int c,h,s; - - if (sectors == 0 || heads == 0) - return 0x05; - - s = (sectnum % sectors) + 1; - h = (sectnum / sectors) % heads; - c = (sectnum / sectors / heads); - return Write_Sector(h,c,s,data); -} - -imageDiskVFD::imageDiskVFD(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk) : imageDisk(ID_VFD) { - (void)isHardDisk;//UNUSED - unsigned char tmp[16]; - - heads = 1; - cylinders = 0; - image_base = 0; - sectors = 0; - active = false; - sector_size = 0; - reserved_cylinders = 0; - diskSizeK = imgSizeK; - diskimg = imgFile; - - if (imgName != NULL) - diskname = (const char*)imgName; - - // NOTES: - // - // +0x000: "VFD1.00" - // +0x0DC: array of 12-byte entries each describing a sector - // - // Each entry: - // +0x0: track - // +0x1: head - // +0x2: sector - // +0x3: sector size (128 << this byte) - // +0x4: fill byte, or 0xFF - // +0x5: unknown - // +0x6: unknown - // +0x7: unknown - // +0x8: absolute data offset (32-bit integer) or 0xFFFFFFFF if the entire sector is that fill byte - fseek(diskimg,0,SEEK_SET); - memset(tmp,0,8); - fread(tmp,1,8,diskimg); - - if (!memcmp(tmp,"VFD1.",5)) { - Bit8u i=0; - bool founddisk = false; - uint32_t stop_at = 0xC3FC; - unsigned long entof; - - // load table. - // we have to determine as we go where to stop reading. - // the source of info I read assumes the whole header (and table) - // is 0xC3FC bytes. I'm not inclined to assume that, so we go by - // that OR the first sector offset whichever is smaller. - // the table seems to trail off into a long series of 0xFF at the end. - fseek(diskimg,0xDC,SEEK_SET); - while ((entof=((unsigned long)ftell(diskimg)+12ul)) <= stop_at) { - memset(tmp,0xFF,12); - fread(tmp,12,1,diskimg); - - if (!memcmp(tmp,"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",12)) - continue; - if (!memcmp(tmp,"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",12)) - continue; - - struct vfdentry v; - - v.track = tmp[0]; - v.head = tmp[1]; - v.sector = tmp[2]; - v.sizebyte = tmp[3]; - v.fillbyte = tmp[4]; - v.data_offset = *((uint32_t*)(tmp+8)); - v.entry_offset = (uint32_t)entof; - - // maybe the table can end sooner than 0xC3FC? - // if we see sectors appear at an offset lower than our stop_at point - // then adjust the stop_at point. assume the table cannot mix with - // sector data. - if (v.hasSectorData()) { - if (stop_at > v.data_offset) - stop_at = v.data_offset; - } - - dents.push_back(v); - - LOG_MSG("VFD entry: track=%u head=%u sector=%u size=%u fill=0x%2X has_data=%u has_fill=%u entoff=%lu dataoff=%lu", - v.track, - v.head, - v.sector, - v.getSectorSize(), - v.fillbyte, - v.hasSectorData(), - v.hasFill(), - (unsigned long)v.entry_offset, - (unsigned long)v.data_offset); - } - - if (!dents.empty()) { - /* okay, now to figure out what the geometry of the disk is. - * we cannot just work from an "absolute" disk image model - * because there's no VFD header to just say what the geometry is. - * Like the IBM PC BIOS, we have to look at the disk and figure out - * which geometry to apply to it, even if the FDD format allows - * sectors on other tracks to have wild out of range sector, track, - * and head numbers or odd sized sectors. - * - * First, determine sector size according to the boot sector. */ - vfdentry *ent; - - ent = findSector(/*head*/0,/*track*/0,/*sector*/1,~0U); - if (ent != NULL) { - if (ent->sizebyte <= 3) /* x <= 1024 */ - sector_size = ent->getSectorSize(); - } - - /* oh yeah right, sure. - * I suppose you're one of those FDD images where the sector size is 128 bytes/sector - * in the boot sector and the rest is 256 bytes/sector elsewhere. I have no idea why - * but quite a few FDD images have this arrangement. */ - if (sector_size != 0 && sector_size < 512) { - ent = findSector(/*head*/0,/*track*/1,/*sector*/1,~0U); - if (ent != NULL) { - if (ent->sizebyte <= 3) { /* x <= 1024 */ - unsigned int nsz = ent->getSectorSize(); - if (sector_size != nsz) - LOG_MSG("VFD warning: sector size changes between track 0 and 1"); - if (sector_size < nsz) - sector_size = nsz; - } - } - } - - if (sector_size != 0) { - i=0; - while (DiskGeometryList[i].ksize != 0) { - diskGeo &diskent = DiskGeometryList[i]; - - if (diskent.bytespersect == sector_size) { - ent = findSector(0,0,diskent.secttrack); - if (ent != NULL) { - LOG_MSG("VFD disk probe: %u/%u/%u exists",0,0,diskent.secttrack); - if (sectors < diskent.secttrack) - sectors = diskent.secttrack; - } - } - - i++; - } - } - - if (sector_size != 0 && sectors != 0) { - i=0; - while (DiskGeometryList[i].ksize != 0) { - diskGeo &diskent = DiskGeometryList[i]; - - if (diskent.bytespersect == sector_size && diskent.secttrack >= sectors) { - ent = findSector(0,diskent.cylcount-1,sectors); - if (ent != NULL) { - LOG_MSG("VFD disk probe: %u/%u/%u exists",0,diskent.cylcount-1,sectors); - if (cylinders < diskent.cylcount) - cylinders = diskent.cylcount; - } - } - - i++; - } - } - - if (sector_size != 0 && sectors != 0 && cylinders != 0) { - ent = findSector(1,0,sectors); - if (ent != NULL) { - LOG_MSG("VFD disk probe: %u/%u/%u exists",1,0,sectors); - heads = 2; - } - } - - // TODO: drive_fat.cpp should use an extension to this API to allow changing the sectors/track - // according to what it reads from the MS-DOS BIOS parameter block, just like real MS-DOS. - // This would allow better representation of strange disk formats such as the "extended" - // floppy format that Microsoft used to use for Word 95 and Windows 95 install floppies. - - LOG_MSG("VFD geometry detection: C/H/S %u/%u/%u %u bytes/sector", - cylinders, heads, sectors, sector_size); - - if (sector_size != 0 && sectors != 0 && cylinders != 0 && heads != 0) - founddisk = true; - - if(!founddisk) { - active = false; - } else { - incrementFDD(); - } - } - } -} - -imageDiskVFD::~imageDiskVFD() { - if(diskimg != NULL) { - fclose(diskimg); - diskimg=NULL; - } -} - -// D88 *.D88 floppy disk format support - -enum { - D88_TRACKMAX = 164, - D88_HEADERSIZE = 0x20 + (D88_TRACKMAX * 4) -}; - -#pragma pack(push,1) -typedef struct D88HEAD { - char fd_name[17]; // +0x00 Disk Name - unsigned char reserved1[9]; // +0x11 Reserved - unsigned char protect; // +0x1A Write Protect bit:4 - unsigned char fd_type; // +0x1B Disk Format - uint32_t fd_size; // +0x1C Disk Size - uint32_t trackp[D88_TRACKMAX]; // +0x20 164 x 4 = 656 = 0x290 -} D88HEAD; // =0x2B0 total - -typedef struct D88SEC { - unsigned char c; // +0x00 - unsigned char h; // +0x01 - unsigned char r; // +0x02 - unsigned char n; // +0x03 - uint16_t sectors; // +0x04 Sector Count - unsigned char mfm_flg; // +0x06 sides - unsigned char del_flg; // +0x07 DELETED DATA - unsigned char stat; // +0x08 STATUS (FDC ret) - unsigned char seektime; // +0x09 Seek Time - unsigned char reserved[3]; // +0x0A Reserved - unsigned char rpm_flg; // +0x0D rpm 0:1.2 1:1.44 - uint16_t size; // +0x0E Sector Size - // -} D88SEC; // =0x10 total -#pragma pack(pop) - -Bit8u imageDiskD88::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data,unsigned int req_sector_size) { - vfdentry *ent; - - if (req_sector_size == 0) - req_sector_size = sector_size; - -// LOG_MSG("D88 read sector: CHS %u/%u/%u sz=%u",cylinder,head,sector,req_sector_size); - - ent = findSector(head,cylinder,sector,req_sector_size); - if (ent == NULL) return 0x05; - if (ent->getSectorSize() != req_sector_size) return 0x05; - - fseek(diskimg,ent->data_offset,SEEK_SET); - if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05; - if (fread(data,req_sector_size,1,diskimg) != 1) return 0x05; - return 0; -} - -Bit8u imageDiskD88::Read_AbsoluteSector(Bit32u sectnum, void * data) { - unsigned int c,h,s; - - if (sectors == 0 || heads == 0) - return 0x05; - - s = (sectnum % sectors) + 1; - h = (sectnum / sectors) % heads; - c = (sectnum / sectors / heads); - return Read_Sector(h,c,s,data); -} - -imageDiskD88::vfdentry *imageDiskD88::findSector(Bit8u head,Bit8u track,Bit8u sector/*TODO: physical head?*/,unsigned int req_sector_size) { - if ((size_t)track >= dents.size()) - return NULL; - - std::vector::iterator i = dents.begin(); - - if (req_sector_size == 0) - req_sector_size = sector_size; - - while (i != dents.end()) { - imageDiskD88::vfdentry &ent = *i; - - if (ent.head == head && - ent.track == track && - ent.sector == sector && - (ent.sector_size == req_sector_size || req_sector_size == ~0U)) - return &(*i); - - i++; - } - - return NULL; -} - -Bit8u imageDiskD88::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,const void * data,unsigned int req_sector_size) { - vfdentry *ent; - - if (req_sector_size == 0) - req_sector_size = sector_size; - -// LOG_MSG("D88 read sector: CHS %u/%u/%u sz=%u",cylinder,head,sector,req_sector_size); - - ent = findSector(head,cylinder,sector,req_sector_size); - if (ent == NULL) return 0x05; - if (ent->getSectorSize() != req_sector_size) return 0x05; - - fseek(diskimg,ent->data_offset,SEEK_SET); - if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05; - if (fwrite(data,req_sector_size,1,diskimg) != 1) return 0x05; - return 0; -} - -Bit8u imageDiskD88::Write_AbsoluteSector(Bit32u sectnum,const void *data) { - unsigned int c,h,s; - - if (sectors == 0 || heads == 0) - return 0x05; - - s = (sectnum % sectors) + 1; - h = (sectnum / sectors) % heads; - c = (sectnum / sectors / heads); - return Write_Sector(h,c,s,data); -} - -imageDiskD88::imageDiskD88(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk) : imageDisk(ID_D88) { - (void)isHardDisk;//UNUSED - D88HEAD head; - - fd_type_major = DISKTYPE_2D; - fd_type_minor = 0; - - assert(sizeof(D88HEAD) == 0x2B0); - assert(sizeof(D88SEC) == 0x10); - - heads = 0; - cylinders = 0; - image_base = 0; - sectors = 0; - active = false; - sector_size = 0; - reserved_cylinders = 0; - diskSizeK = imgSizeK; - diskimg = imgFile; - active = false; - - if (imgName != NULL) - diskname = (const char*)imgName; - - // NOTES: - // - // +0x000: D88 header - // +0x020: Offset of D88 tracks, per track - // +0x2B0: - // - // Track offsets are sequential, always - // - // Each track is an array of: - // - // ENTRY: - // - // - // - // Array of ENTRY from offset until next track - fseek(diskimg,0,SEEK_END); - off_t fsz = ftell(diskimg); - - fseek(diskimg,0,SEEK_SET); - if (fread(&head,sizeof(head),1,diskimg) != 1) return; - - // validate fd_size - if ((uint32_t)host_readd((ConstHostPt)(&head.fd_size)) > (uint32_t)fsz) return; - - fd_type_major = head.fd_type >> 4U; - fd_type_minor = head.fd_type & 0xFU; - - // validate that none of the track offsets extend past the file - { - for (unsigned int i=0;i < D88_TRACKMAX;i++) { - uint32_t trackoff = host_readd((ConstHostPt)(&head.trackp[i])); - - if (trackoff == 0) continue; - - if ((trackoff + 16U) > (uint32_t)fsz) { - LOG_MSG("D88: track starts past end of file"); - return; - } - } - } - - // read each track - for (unsigned int track=0;track < D88_TRACKMAX;track++) { - uint32_t trackoff = host_readd((ConstHostPt)(&head.trackp[track])); - - if (trackoff != 0) { - fseek(diskimg, trackoff, SEEK_SET); - if (ftell(diskimg) != trackoff) continue; - - D88SEC s; - unsigned int count = 0; - - do { - if (fread(&s,sizeof(s),1,diskimg) != 1) break; - - uint16_t sector_count = host_readw((ConstHostPt)(&s.sectors)); - uint16_t sector_size = host_readw((ConstHostPt)(&s.size)); - - if (sector_count == 0U || sector_size < 128U) break; - if (sector_count > 128U || sector_size > 16384U) break; - if (s.n > 8U) s.n = 8U; - - vfdentry vent; - vent.sector_size = 128 << s.n; - vent.data_offset = (uint32_t)ftell(diskimg); - vent.entry_offset = vent.data_offset - (uint32_t)16; - vent.track = s.c; - vent.head = s.h; - vent.sector = s.r; - - LOG_MSG("D88: trackindex=%u C/H/S/sz=%u/%u/%u/%u data-at=0x%lx", - track,vent.track,vent.head,vent.sector,vent.sector_size,(unsigned long)vent.data_offset); - - dents.push_back(vent); - if ((++count) >= sector_count) break; - - fseek(diskimg, sector_size, SEEK_CUR); - } while (1); - } - } - - if (!dents.empty()) { - /* okay, now to figure out what the geometry of the disk is. - * we cannot just work from an "absolute" disk image model - * because there's no D88 header to just say what the geometry is. - * Like the IBM PC BIOS, we have to look at the disk and figure out - * which geometry to apply to it, even if the FDD format allows - * sectors on other tracks to have wild out of range sector, track, - * and head numbers or odd sized sectors. - * - * First, determine sector size according to the boot sector. */ - bool founddisk = false; - vfdentry *ent; - - ent = findSector(/*head*/0,/*track*/0,/*sector*/1,~0U); - if (ent != NULL) { - if (ent->getSectorSize() <= 1024) /* x <= 1024 */ - sector_size = ent->getSectorSize(); - } - - /* oh yeah right, sure. - * I suppose you're one of those FDD images where the sector size is 128 bytes/sector - * in the boot sector and the rest is 256 bytes/sector elsewhere. I have no idea why - * but quite a few FDD images have this arrangement. */ - if (sector_size != 0 && sector_size < 512) { - ent = findSector(/*head*/0,/*track*/1,/*sector*/1,~0U); - if (ent != NULL) { - if (ent->getSectorSize() <= 1024) { /* x <= 1024 */ - unsigned int nsz = ent->getSectorSize(); - if (sector_size != nsz) - LOG_MSG("D88 warning: sector size changes between track 0 and 1"); - if (sector_size < nsz) - sector_size = nsz; - } - } - } - - if (sector_size != 0) { - unsigned int i = 0; - while (DiskGeometryList[i].ksize != 0) { - diskGeo &diskent = DiskGeometryList[i]; - - if (diskent.bytespersect == sector_size) { - ent = findSector(0,0,diskent.secttrack); - if (ent != NULL) { - LOG_MSG("D88 disk probe: %u/%u/%u exists",0,0,diskent.secttrack); - if (sectors < diskent.secttrack) - sectors = diskent.secttrack; - } - } - - i++; - } - } - - if (sector_size != 0 && sectors != 0) { - unsigned int i = 0; - while (DiskGeometryList[i].ksize != 0) { - diskGeo &diskent = DiskGeometryList[i]; - - if (diskent.bytespersect == sector_size && diskent.secttrack >= sectors) { - ent = findSector(0,diskent.cylcount-1,sectors); - if (ent != NULL) { - LOG_MSG("D88 disk probe: %u/%u/%u exists",0,diskent.cylcount-1,sectors); - if (cylinders < diskent.cylcount) - cylinders = diskent.cylcount; - } - } - - i++; - } - } - - if (sector_size != 0 && sectors != 0 && cylinders != 0) { - ent = findSector(1,0,sectors); - if (ent != NULL) { - LOG_MSG("D88 disk probe: %u/%u/%u exists",1,0,sectors); - heads = 2; - } - } - - // TODO: drive_fat.cpp should use an extension to this API to allow changing the sectors/track - // according to what it reads from the MS-DOS BIOS parameter block, just like real MS-DOS. - // This would allow better representation of strange disk formats such as the "extended" - // floppy format that Microsoft used to use for Word 95 and Windows 95 install floppies. - - LOG_MSG("D88 geometry detection: C/H/S %u/%u/%u %u bytes/sector", - cylinders, heads, sectors, sector_size); - - if (sector_size != 0 && sectors != 0 && cylinders != 0 && heads != 0) - founddisk = true; - - if(!founddisk) { - active = false; - } else { - incrementFDD(); - } - } -} - -imageDiskD88::~imageDiskD88() { - if(diskimg != NULL) { - fclose(diskimg); - diskimg=NULL; - } -} - -/*--------------------------------*/ - -Bit8u imageDiskNFD::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data,unsigned int req_sector_size) { - vfdentry *ent; - - if (req_sector_size == 0) - req_sector_size = sector_size; - -// LOG_MSG("NFD read sector: CHS %u/%u/%u sz=%u",cylinder,head,sector,req_sector_size); - - ent = findSector(head,cylinder,sector,req_sector_size); - if (ent == NULL) return 0x05; - if (ent->getSectorSize() != req_sector_size) return 0x05; - - fseek(diskimg,ent->data_offset,SEEK_SET); - if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05; - if (fread(data,req_sector_size,1,diskimg) != 1) return 0x05; - return 0; -} - -Bit8u imageDiskNFD::Read_AbsoluteSector(Bit32u sectnum, void * data) { - unsigned int c,h,s; - - if (sectors == 0 || heads == 0) - return 0x05; - - s = (sectnum % sectors) + 1; - h = (sectnum / sectors) % heads; - c = (sectnum / sectors / heads); - return Read_Sector(h,c,s,data); -} - -imageDiskNFD::vfdentry *imageDiskNFD::findSector(Bit8u head,Bit8u track,Bit8u sector/*TODO: physical head?*/,unsigned int req_sector_size) { - if ((size_t)track >= dents.size()) - return NULL; - - std::vector::iterator i = dents.begin(); - - if (req_sector_size == 0) - req_sector_size = sector_size; - - while (i != dents.end()) { - imageDiskNFD::vfdentry &ent = *i; - - if (ent.head == head && - ent.track == track && - ent.sector == sector && - (ent.sector_size == req_sector_size || req_sector_size == ~0U)) - return &(*i); - - i++; - } - - return NULL; -} - -Bit8u imageDiskNFD::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,const void * data,unsigned int req_sector_size) { - vfdentry *ent; - - if (req_sector_size == 0) - req_sector_size = sector_size; - -// LOG_MSG("NFD read sector: CHS %u/%u/%u sz=%u",cylinder,head,sector,req_sector_size); - - ent = findSector(head,cylinder,sector,req_sector_size); - if (ent == NULL) return 0x05; - if (ent->getSectorSize() != req_sector_size) return 0x05; - - fseek(diskimg,ent->data_offset,SEEK_SET); - if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05; - if (fwrite(data,req_sector_size,1,diskimg) != 1) return 0x05; - return 0; -} - -Bit8u imageDiskNFD::Write_AbsoluteSector(Bit32u sectnum,const void *data) { - unsigned int c,h,s; - - if (sectors == 0 || heads == 0) - return 0x05; - - s = (sectnum % sectors) + 1; - h = (sectnum / sectors) % heads; - c = (sectnum / sectors / heads); - return Write_Sector(h,c,s,data); -} - -imageDiskNFD::imageDiskNFD(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk, unsigned int revision) : imageDisk(ID_NFD) { - (void)isHardDisk;//UNUSED - union { - NFDHDR head; - NFDHDRR1 headr1; - }; // these occupy the same location of memory - - assert(sizeof(NFDHDR) == 0x120); - assert(sizeof(NFDHDRR1) == 0x3C0); - assert(sizeof(NFDHDR_ENTRY) == 0x10); - - heads = 0; - cylinders = 0; - image_base = 0; - sectors = 0; - active = false; - sector_size = 0; - reserved_cylinders = 0; - diskSizeK = imgSizeK; - diskimg = imgFile; - active = false; - - if (imgName != NULL) - diskname = (const char*)imgName; - - // NOTES: - // - // +0x000: NFD header - // +0x020: Offset of NFD tracks, per track - // +0x2B0: - // - // Track offsets are sequential, always - // - // Each track is an array of: - // - // ENTRY: - // - // - // - // Array of ENTRY from offset until next track - fseek(diskimg,0,SEEK_END); - off_t fsz = ftell(diskimg); - - fseek(diskimg,0,SEEK_SET); - if (revision == 0) { - if (fread(&head,sizeof(head),1,diskimg) != 1) return; - } - else if (revision == 1) { - if (fread(&headr1,sizeof(headr1),1,diskimg) != 1) return; - } - else { - abort(); - } - - // validate fd_size - if ((uint32_t)host_readd((ConstHostPt)(&head.headersize)) < sizeof(head)) return; - if ((uint32_t)host_readd((ConstHostPt)(&head.headersize)) > (uint32_t)fsz) return; - - unsigned int data_offset = host_readd((ConstHostPt)(&head.headersize)); - - std::vector< std::pair > seclist; - - if (revision == 0) { - unsigned int secents = (host_readd((ConstHostPt)(&head.headersize)) - sizeof(head)) / sizeof(NFDHDR_ENTRY); - if (secents == 0) return; - secents--; - if (secents == 0) return; - - for (unsigned int i=0;i < secents;i++) { - uint32_t ofs = (uint32_t)ftell(diskimg); - NFDHDR_ENTRY e; - - if (fread(&e,sizeof(e),1,diskimg) != 1) return; - seclist.push_back( std::pair(ofs,e) ); - - if (e.log_cyl == 0xFF || e.log_head == 0xFF || e.log_rec == 0xFF || e.sec_len_pow2 > 7) - continue; - - LOG_MSG("NFD %u/%u: ofs=%lu data=%lu cyl=%u head=%u sec=%u len=%u", - (unsigned int)i, - (unsigned int)secents, - (unsigned long)ofs, - (unsigned long)data_offset, - e.log_cyl, - e.log_head, - e.log_rec, - 128 << e.sec_len_pow2); - - vfdentry vent; - vent.sector_size = 128 << e.sec_len_pow2; - vent.data_offset = (uint32_t)data_offset; - vent.entry_offset = (uint32_t)ofs; - vent.track = e.log_cyl; - vent.head = e.log_head; - vent.sector = e.log_rec; - dents.push_back(vent); - - data_offset += 128u << e.sec_len_pow2; - if (data_offset > (unsigned int)fsz) return; - } - } - else { - /* R1 has an array of offsets to where each tracks begins. - * The end of the track is an entry like 0x1A 0x00 0x00 0x00 0x00 0x00 0x00 .... */ - /* The R1 images I have as reference always have offsets in ascending order. */ - for (unsigned int ti=0;ti < 164;ti++) { - uint32_t trkoff = host_readd((ConstHostPt)(&headr1.trackheads[ti])); - - if (trkoff == 0) break; - - fseek(diskimg,trkoff,SEEK_SET); - if (ftell(diskimg) != trkoff) return; - - NFDHDR_ENTRY e; - - // track id - if (fread(&e,sizeof(e),1,diskimg) != 1) return; - unsigned int sectors = host_readw((ConstHostPt)(&e) + 0); - unsigned int diagcount = host_readw((ConstHostPt)(&e) + 2); - - LOG_MSG("NFD R1 track ent %u offset %lu sectors %u diag %u",ti,(unsigned long)trkoff,sectors,diagcount); - - for (unsigned int s=0;s < sectors;s++) { - uint32_t ofs = (uint32_t)ftell(diskimg); - - if (fread(&e,sizeof(e),1,diskimg) != 1) return; - - LOG_MSG("NFD %u/%u: ofs=%lu data=%lu cyl=%u head=%u sec=%u len=%u rep=%u", - (unsigned int)s, - (unsigned int)sectors, - (unsigned long)ofs, - (unsigned long)data_offset, - e.log_cyl, - e.log_head, - e.log_rec, - 128 << e.sec_len_pow2, - e.byRetry); - - vfdentry vent; - vent.sector_size = 128 << e.sec_len_pow2; - vent.data_offset = (uint32_t)data_offset; - vent.entry_offset = (uint32_t)ofs; - vent.track = e.log_cyl; - vent.head = e.log_head; - vent.sector = e.log_rec; - dents.push_back(vent); - - data_offset += 128u << e.sec_len_pow2; - if (data_offset > (unsigned int)fsz) return; - } - - for (unsigned int d=0;d < diagcount;d++) { - if (fread(&e,sizeof(e),1,diskimg) != 1) return; - - unsigned int retry = e.byRetry; - unsigned int len = host_readd((ConstHostPt)(&e) + 10); - - LOG_MSG("NFD diag %u/%u: retry=%u len=%u data=%lu",d,diagcount,retry,len,(unsigned long)data_offset); - - data_offset += (1+retry) * len; - } - } - } - - if (!dents.empty()) { - /* okay, now to figure out what the geometry of the disk is. - * we cannot just work from an "absolute" disk image model - * because there's no NFD header to just say what the geometry is. - * Like the IBM PC BIOS, we have to look at the disk and figure out - * which geometry to apply to it, even if the FDD format allows - * sectors on other tracks to have wild out of range sector, track, - * and head numbers or odd sized sectors. - * - * First, determine sector size according to the boot sector. */ - bool founddisk = false; - vfdentry *ent; - - ent = findSector(/*head*/0,/*track*/0,/*sector*/1,~0U); - if (ent != NULL) { - if (ent->getSectorSize() <= 1024) /* x <= 1024 */ - sector_size = ent->getSectorSize(); - } - - /* oh yeah right, sure. - * I suppose you're one of those FDD images where the sector size is 128 bytes/sector - * in the boot sector and the rest is 256 bytes/sector elsewhere. I have no idea why - * but quite a few FDD images have this arrangement. */ - if (sector_size != 0 && sector_size < 512) { - ent = findSector(/*head*/0,/*track*/1,/*sector*/1,~0U); - if (ent != NULL) { - if (ent->getSectorSize() <= 1024) { /* x <= 1024 */ - unsigned int nsz = ent->getSectorSize(); - if (sector_size != nsz) - LOG_MSG("NFD warning: sector size changes between track 0 and 1"); - if (sector_size < nsz) - sector_size = nsz; - } - } - } - - if (sector_size != 0) { - unsigned int i = 0; - while (DiskGeometryList[i].ksize != 0) { - diskGeo &diskent = DiskGeometryList[i]; - - if (diskent.bytespersect == sector_size) { - ent = findSector(0,0,diskent.secttrack); - if (ent != NULL) { - LOG_MSG("NFD disk probe: %u/%u/%u exists",0,0,diskent.secttrack); - if (sectors < diskent.secttrack) - sectors = diskent.secttrack; - } - } - - i++; - } - } - - if (sector_size != 0 && sectors != 0) { - unsigned int i = 0; - while (DiskGeometryList[i].ksize != 0) { - diskGeo &diskent = DiskGeometryList[i]; - - if (diskent.bytespersect == sector_size && diskent.secttrack >= sectors) { - ent = findSector(0,diskent.cylcount-1,sectors); - if (ent != NULL) { - LOG_MSG("NFD disk probe: %u/%u/%u exists",0,diskent.cylcount-1,sectors); - if (cylinders < diskent.cylcount) - cylinders = diskent.cylcount; - } - } - - i++; - } - } - - if (sector_size != 0 && sectors != 0 && cylinders != 0) { - ent = findSector(1,0,sectors); - if (ent != NULL) { - LOG_MSG("NFD disk probe: %u/%u/%u exists",1,0,sectors); - heads = 2; - } - } - - // TODO: drive_fat.cpp should use an extension to this API to allow changing the sectors/track - // according to what it reads from the MS-DOS BIOS parameter block, just like real MS-DOS. - // This would allow better representation of strange disk formats such as the "extended" - // floppy format that Microsoft used to use for Word 95 and Windows 95 install floppies. - - LOG_MSG("NFD geometry detection: C/H/S %u/%u/%u %u bytes/sector", - cylinders, heads, sectors, sector_size); - - if (sector_size != 0 && sectors != 0 && cylinders != 0 && heads != 0) - founddisk = true; - - if(!founddisk) { - active = false; - } else { - incrementFDD(); - } - } -} - -imageDiskNFD::~imageDiskNFD() { - if(diskimg != NULL) { - fclose(diskimg); - diskimg=NULL; - } -} - +/* + * Copyright (C) 2002-2015 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "dosbox.h" +#include "callback.h" +#include "bios.h" +#include "bios_disk.h" +#include "regs.h" +#include "mem.h" +#include "dos_inc.h" /* for Drives[] */ +#include "../dos/drives.h" +#include "mapper.h" +#include "ide.h" + +#if defined(_MSC_VER) +# pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */ +#endif + +extern bool int13_extensions_enable; + +diskGeo DiskGeometryList[] = { + { 160, 8, 1, 40, 0, 512, 64, 1, 0xFE}, // IBM PC double density 5.25" single-sided 160KB + { 180, 9, 1, 40, 0, 512, 64, 2, 0xFC}, // IBM PC double density 5.25" single-sided 180KB + { 200, 10, 1, 40, 0, 512, 0, 0, 0}, // DEC Rainbow double density 5.25" single-sided 200KB (I think...) + { 320, 8, 2, 40, 1, 512, 112, 2, 0xFF}, // IBM PC double density 5.25" double-sided 320KB + { 360, 9, 2, 40, 1, 512, 112, 2, 0xFD}, // IBM PC double density 5.25" double-sided 360KB + { 400, 10, 2, 40, 1, 512, 0, 0, 0}, // DEC Rainbow double density 5.25" double-sided 400KB (I think...) + { 640, 8, 2, 80, 3, 512, 112, 2, 0xFB}, // IBM PC double density 3.5" double-sided 640KB + { 720, 9, 2, 80, 3, 512, 112, 2, 0xF9}, // IBM PC double density 3.5" double-sided 720KB + {1200, 15, 2, 80, 2, 512, 224, 1, 0xF9}, // IBM PC double density 5.25" double-sided 1.2MB + {1440, 18, 2, 80, 4, 512, 224, 1, 0xF0}, // IBM PC high density 3.5" double-sided 1.44MB + {2880, 36, 2, 80, 6, 512, 240, 2, 0xF0}, // IBM PC high density 3.5" double-sided 2.88MB + + {1232, 8, 2, 77, 7, 1024,192, 1, 0xFE}, // NEC PC-98 high density 3.5" double-sided 1.2MB "3-mode" + + { 0, 0, 0, 0, 0, 0, 0, 0, 0} +}; + +Bitu call_int13 = 0; +Bitu diskparm0 = 0, diskparm1 = 0; +static Bit8u last_status; +static Bit8u last_drive; +Bit16u imgDTASeg; +RealPt imgDTAPtr; +DOS_DTA *imgDTA; +bool killRead; +static bool swapping_requested; + +void CMOS_SetRegister(Bitu regNr, Bit8u val); //For setting equipment word + +/* 2 floppys and 2 harddrives, max */ +bool imageDiskChange[MAX_DISK_IMAGES]={false}; +imageDisk *imageDiskList[MAX_DISK_IMAGES]={NULL}; +imageDisk *diskSwap[MAX_SWAPPABLE_DISKS]={NULL}; +Bits swapPosition; + +imageDisk *GetINT13FloppyDrive(unsigned char drv) { + if (drv >= 2) + return NULL; + return imageDiskList[drv]; +} + +imageDisk *GetINT13HardDrive(unsigned char drv) { + if (drv < 0x80 || drv >= (0x80+MAX_DISK_IMAGES-2)) + return NULL; + + return imageDiskList[drv-0x80]; +} + +void FreeBIOSDiskList() { + for (int i=0;i < MAX_DISK_IMAGES;i++) { + if (imageDiskList[i] != NULL) { + if (i >= 2) IDE_Hard_Disk_Detach(i); + imageDiskList[i]->Release(); + imageDiskList[i] = NULL; + } + } + + for (int j=0;j < MAX_SWAPPABLE_DISKS;j++) { + if (diskSwap[j] != NULL) { + diskSwap[j]->Release(); + diskSwap[j] = NULL; + } + } +} + +//update BIOS disk parameter tables for first two hard drives +void updateDPT(void) { + Bit32u tmpheads, tmpcyl, tmpsect, tmpsize; + PhysPt dpphysaddr[2] = { CALLBACK_PhysPointer(diskparm0), CALLBACK_PhysPointer(diskparm1) }; + for (int i = 0; i < 2; i++) { + tmpheads = 0; tmpcyl = 0; tmpsect = 0; tmpsize = 0; + if (imageDiskList[i + 2] != NULL) { + imageDiskList[i + 2]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize); + } + phys_writew(dpphysaddr[i], (Bit16u)tmpcyl); + phys_writeb(dpphysaddr[i] + 0x2, (Bit8u)tmpheads); + phys_writew(dpphysaddr[i] + 0x3, 0); + phys_writew(dpphysaddr[i] + 0x5, tmpcyl == 0 ? 0 : (Bit16u)-1); + phys_writeb(dpphysaddr[i] + 0x7, 0); + phys_writeb(dpphysaddr[i] + 0x8, tmpcyl == 0 ? 0 : (0xc0 | (((tmpheads) > 8) << 3))); + phys_writeb(dpphysaddr[i] + 0x9, 0); + phys_writeb(dpphysaddr[i] + 0xa, 0); + phys_writeb(dpphysaddr[i] + 0xb, 0); + phys_writew(dpphysaddr[i] + 0xc, (Bit16u)tmpcyl); + phys_writeb(dpphysaddr[i] + 0xe, (Bit8u)tmpsect); + } +} + +void incrementFDD(void) { + Bit16u equipment=mem_readw(BIOS_CONFIGURATION); + if(equipment&1) { + Bitu numofdisks = (equipment>>6)&3; + numofdisks++; + if(numofdisks > 1) numofdisks=1;//max 2 floppies at the moment + equipment&=~0x00C0; + equipment|=(numofdisks<<6); + } else equipment|=1; + mem_writew(BIOS_CONFIGURATION,equipment); + CMOS_SetRegister(0x14, (Bit8u)(equipment&0xff)); +} + +int swapInDisksSpecificDrive = -1; +// -1 = swap across A: and B: (DOSBox / DOSBox-X default behavior) +// 0 = swap across A: only +// 1 = swap across B: only + +void swapInDisks(void) { + bool allNull = true; + Bits diskcount = 0; + Bits diskswapcount = 2; + Bits diskswapdrive = 0; + Bits swapPos = swapPosition; + int i; + + /* Check to make sure that there is at least one setup image */ + for(i=0;i= 0 && swapInDisksSpecificDrive <= 1) { + diskswapdrive = swapInDisksSpecificDrive; + diskswapcount = 1; + } + + /* If only one disk is loaded, this loop will load the same disk in dive A and drive B */ + while(diskcount < diskswapcount) { + if(diskSwap[swapPos] != NULL) { + LOG_MSG("Loaded drive %d disk %d from swaplist position %d - \"%s\"", (int)diskswapdrive, (int)diskcount, (int)swapPos, diskSwap[swapPos]->diskname.c_str()); + + if (imageDiskList[diskswapdrive] != NULL) + imageDiskList[diskswapdrive]->Release(); + + imageDiskList[diskswapdrive] = diskSwap[swapPos]; + imageDiskList[diskswapdrive]->Addref(); + + imageDiskChange[diskswapdrive] = true; + + diskcount++; + diskswapdrive++; + } + + swapPos++; + if(swapPos>=MAX_SWAPPABLE_DISKS) swapPos=0; + } +} + +bool getSwapRequest(void) { + bool sreq=swapping_requested; + swapping_requested = false; + return sreq; +} + +void swapInNextDisk(bool pressed) { + if (!pressed) + return; + DriveManager::CycleAllDisks(); + /* Hack/feature: rescan all disks as well */ + LOG_MSG("Diskcaching reset for floppy drives."); + for(Bitu i=0;i<2;i++) { /* Swap A: and B: where DOSBox mainline would run through ALL drive letters */ + if (Drives[i] != NULL) { + Drives[i]->EmptyCache(); + Drives[i]->MediaChange(); + } + } + swapPosition++; + if(diskSwap[swapPosition] == NULL) swapPosition = 0; + swapInDisks(); + swapping_requested = true; +} + +void swapInNextCD(bool pressed) { + if (!pressed) + return; + DriveManager::CycleAllCDs(); + /* Hack/feature: rescan all disks as well */ + LOG_MSG("Diskcaching reset for normal mounted drives."); + for(Bitu i=2;iEmptyCache(); + Drives[i]->MediaChange(); + } + } +} + + +Bit8u imageDisk::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data,unsigned int req_sector_size) { + Bit32u sectnum; + + if (req_sector_size == 0) + req_sector_size = sector_size; + if (req_sector_size != sector_size) + return 0x05; + + sectnum = ( (cylinder * heads + head) * sectors ) + sector - 1L; + + return Read_AbsoluteSector(sectnum, data); +} + +Bit8u imageDisk::Read_AbsoluteSector(Bit32u sectnum, void * data) { + Bit64u bytenum,res; + int got; + + bytenum = (Bit64u)sectnum * (Bit64u)sector_size; + if ((bytenum + sector_size) > this->image_length) { + LOG_MSG("Attempt to read invalid sector in Read_AbsoluteSector for sector %lu.\n", (unsigned long)sectnum); + return 0x05; + } + bytenum += image_base; + + //LOG_MSG("Reading sectors %ld at bytenum %I64d", sectnum, bytenum); + + fseeko64(diskimg,(off_t)bytenum,SEEK_SET); + res = (Bit64u)ftello64(diskimg); + if (res != bytenum) { + LOG_MSG("fseek() failed in Read_AbsoluteSector for sector %lu. Want=%llu Got=%llu\n", + (unsigned long)sectnum,(unsigned long long)bytenum,(unsigned long long)res); + return 0x05; + } + + got = (int)fread(data, 1, sector_size, diskimg); + if ((unsigned int)got != sector_size) { + LOG_MSG("fread() failed in Read_AbsoluteSector for sector %lu. Want=%u got=%d\n", + (unsigned long)sectnum,(unsigned int)sector_size,(unsigned int)got); + return 0x05; + } + + return 0x00; +} + +Bit8u imageDisk::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,const void * data,unsigned int req_sector_size) { + Bit32u sectnum; + + if (req_sector_size == 0) + req_sector_size = sector_size; + if (req_sector_size != sector_size) + return 0x05; + + sectnum = ( (cylinder * heads + head) * sectors ) + sector - 1L; + + return Write_AbsoluteSector(sectnum, data); +} + + +Bit8u imageDisk::Write_AbsoluteSector(Bit32u sectnum, const void *data) { + Bit64u bytenum; + + bytenum = (Bit64u)sectnum * sector_size; + if ((bytenum + sector_size) > this->image_length) { + LOG_MSG("Attempt to read invalid sector in Write_AbsoluteSector for sector %lu.\n", (unsigned long)sectnum); + return 0x05; + } + bytenum += image_base; + + //LOG_MSG("Writing sectors to %ld at bytenum %d", sectnum, bytenum); + + fseeko64(diskimg,(off_t)bytenum,SEEK_SET); + if ((Bit64u)ftello64(diskimg) != bytenum) + LOG_MSG("WARNING: fseek() failed in Write_AbsoluteSector for sector %lu\n",(unsigned long)sectnum); + + size_t ret=fwrite(data, sector_size, 1, diskimg); + + return ((ret>0)?0x00:0x05); + +} + +void imageDisk::Set_Reserved_Cylinders(Bitu resCyl) { + reserved_cylinders = resCyl; +} + +Bit32u imageDisk::Get_Reserved_Cylinders() { + return reserved_cylinders; +} + +imageDisk::imageDisk(IMAGE_TYPE class_id) { + heads = 0; + cylinders = 0; + image_base = 0; + sectors = 0; + refcount = 0; + sector_size = 512; + image_length = 0; + reserved_cylinders = 0; + diskimg = NULL; + this->class_id = class_id; + active = false; + hardDrive = false; +} + +imageDisk::imageDisk(FILE* diskimg, const char* diskName, Bit32u cylinders, Bit32u heads, Bit32u sectors, Bit32u sector_size, bool hardDrive) { + if (diskName) this->diskname = diskName; + this->cylinders = cylinders; + this->heads = heads; + this->sectors = sectors; + image_base = 0; + this->image_length = (Bit64u)cylinders * heads * sectors * sector_size; + refcount = 0; + this->sector_size = sector_size; + this->diskSizeK = this->image_length / 1024; + reserved_cylinders = 0; + this->diskimg = diskimg; + class_id = ID_BASE; + active = true; + this->hardDrive = hardDrive; +} + +/* .HDI and .FDI header (NP2) */ +#pragma pack(push,1) +typedef struct { + uint8_t dummy[4]; // +0x00 + uint8_t hddtype[4]; // +0x04 + uint8_t headersize[4]; // +0x08 + uint8_t hddsize[4]; // +0x0C + uint8_t sectorsize[4]; // +0x10 + uint8_t sectors[4]; // +0x14 + uint8_t surfaces[4]; // +0x18 + uint8_t cylinders[4]; // +0x1C +} HDIHDR; // =0x20 + +typedef struct { + uint8_t dummy[4]; // +0x00 + uint8_t fddtype[4]; // +0x04 + uint8_t headersize[4]; // +0x08 + uint8_t fddsize[4]; // +0x0C + uint8_t sectorsize[4]; // +0x10 + uint8_t sectors[4]; // +0x14 + uint8_t surfaces[4]; // +0x18 + uint8_t cylinders[4]; // +0x1C +} FDIHDR; // =0x20 + +typedef struct { + char sig[16]; // +0x000 + char comment[0x100]; // +0x010 + UINT8 headersize[4]; // +0x110 + uint8_t prot; // +0x114 + uint8_t nhead; // +0x115 + uint8_t _unknown_[10]; // +0x116 +} NFDHDR; // =0x120 + +typedef struct { + char sig[16]; // +0x000 + char comment[0x100]; // +0x010 + UINT8 headersize[4]; // +0x110 + uint8_t prot; // +0x114 + uint8_t nhead; // +0x115 + uint8_t _unknown_[10]; // +0x116 + uint32_t trackheads[164]; // +0x120 + uint32_t addinfo; // +0x3b0 + uint8_t _unknown2_[12]; // +0x3b4 +} NFDHDRR1; // =0x3c0 + +typedef struct { + uint8_t log_cyl; // +0x0 + uint8_t log_head; // +0x1 + uint8_t log_rec; // +0x2 + uint8_t sec_len_pow2; // +0x3 sz = 128 << len_pow2 + uint8_t flMFM; // +0x4 + uint8_t flDDAM; // +0x5 + uint8_t byStatus; // +0x6 + uint8_t bySTS0; // +0x7 + uint8_t bySTS1; // +0x8 + uint8_t bySTS2; // +0x9 + uint8_t byRetry; // +0xA + uint8_t byPDA; // +0xB + uint8_t _unknown_[4]; // +0xC +} NFDHDR_ENTRY; // =0x10 + +typedef struct { + char szFileID[15]; // 識別ID "T98HDDIMAGE.R0" + char Reserve1[1]; // 予約 + char szComment[0x100]; // イメージコメント(ASCIIz) + uint32_t dwHeadSize; // ヘッダ部のサイズ + uint32_t dwCylinder; // シリンダ数 + uint16_t wHead; // ヘッド数 + uint16_t wSect; // 1トラックあたりのセクタ数 + uint16_t wSectLen; // セクタ長 + char Reserve2[2]; // 予約 + char Reserve3[0xe0]; // 予約 +}NHD_FILE_HEAD,*LP_NHD_FILE_HEAD; +#pragma pack(pop) + +imageDisk::imageDisk(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk) { + heads = 0; + cylinders = 0; + image_base = 0; + image_length = (Bit64u)imgSizeK * (Bit64u)1024; + sectors = 0; + refcount = 0; + sector_size = 512; + reserved_cylinders = 0; + diskimg = imgFile; + class_id = ID_BASE; + diskSizeK = imgSizeK; + + if (imgName != NULL) + diskname = (const char*)imgName; + + active = false; + hardDrive = isHardDisk; + if(!isHardDisk) { + Bit8u i=0; + bool founddisk = false; + + if (imgName != NULL) { + char *ext = strrchr((char*)imgName,'.'); + if (ext != NULL) { + if (!strcasecmp(ext,".fdi")) { + if (imgSizeK >= 160) { + FDIHDR fdihdr; + + // PC-98 .FDI images appear to be 4096 bytes of a short header and many zeros. + // followed by a straight sector dump of the disk. The header is NOT NECESSARILY + // 4KB in size, but usually is. + LOG_MSG("Image file has .FDI extension, assuming FDI image and will take on parameters in header."); + + assert(sizeof(fdihdr) == 0x20); + if (fseek(imgFile,0,SEEK_SET) == 0 && ftell(imgFile) == 0 && + fread(&fdihdr,sizeof(fdihdr),1,imgFile) == 1) { + uint32_t ofs = host_readd(fdihdr.headersize); + uint32_t fddsize = host_readd(fdihdr.fddsize); /* includes header */ + uint32_t sectorsize = host_readd(fdihdr.sectorsize); + + if (sectorsize != 0 && ((sectorsize & (sectorsize - 1)) == 0/*is power of 2*/) && + sectorsize >= 256 && sectorsize <= 1024 && + ofs != 0 && (ofs % sectorsize) == 0/*offset is nonzero and multiple of sector size*/ && + (ofs % 1024) == 0/*offset is a multiple of 1024 because of imgSizeK*/ && + fddsize >= sectorsize && (fddsize/1024) <= (imgSizeK+4)) { + + founddisk = true; + sector_size = sectorsize; + imgSizeK -= (ofs / 1024); + image_base = ofs; + image_length -= ofs; + LOG_MSG("FDI header: sectorsize is %u bytes/sector, header is %u bytes, fdd size (plus header) is %u bytes", + (unsigned int)sectorsize,(unsigned int)ofs,(unsigned int)fddsize); + + /* take on the geometry. */ + sectors = host_readd(fdihdr.sectors); + heads = host_readd(fdihdr.surfaces); + cylinders = host_readd(fdihdr.cylinders); + LOG_MSG("FDI: Geometry is C/H/S %u/%u/%u", + (unsigned int)cylinders,(unsigned int)heads,(unsigned int)sectors); + } + else { + LOG_MSG("FDI header rejected. sectorsize=%u headersize=%u fddsize=%u", + (unsigned int)sectorsize,(unsigned int)ofs,(unsigned int)fddsize); + } + } + else { + LOG_MSG("Unable to read .FDI header"); + } + } + } + } + } + + if (sectors == 0 && heads == 0 && cylinders == 0) { + while (DiskGeometryList[i].ksize!=0x0) { + if ((DiskGeometryList[i].ksize==imgSizeK) || + (DiskGeometryList[i].ksize+1==imgSizeK)) { + if (DiskGeometryList[i].ksize!=imgSizeK) + LOG_MSG("ImageLoader: image file with additional data, might not load!"); + founddisk = true; + active = true; + floppytype = i; + heads = DiskGeometryList[i].headscyl; + cylinders = DiskGeometryList[i].cylcount; + sectors = DiskGeometryList[i].secttrack; + sector_size = DiskGeometryList[i].bytespersect; + LOG_MSG("Identified '%s' as C/H/S %u/%u/%u %u bytes/sector", + imgName,cylinders,heads,sectors,sector_size); + break; + } + i++; + } + } + if(!founddisk) { + active = false; + } + } + else { /* hard disk */ + if (imgName != NULL) { + char *ext = strrchr((char*)imgName,'.'); + if (ext != NULL) { + if (!strcasecmp(ext,".nhd")) { + if (imgSizeK >= 160) { + NHD_FILE_HEAD nhdhdr; + + LOG_MSG("Image file has .NHD extension, assuming NHD image and will take on parameters in header."); + + assert(sizeof(nhdhdr) == 0x200); + if (fseek(imgFile,0,SEEK_SET) == 0 && ftell(imgFile) == 0 && + fread(&nhdhdr,sizeof(nhdhdr),1,imgFile) == 1 && + host_readd((ConstHostPt)(&nhdhdr.dwHeadSize)) >= 0x200 && + !memcmp(nhdhdr.szFileID,"T98HDDIMAGE.R0\0",15)) { + uint32_t ofs = host_readd((ConstHostPt)(&nhdhdr.dwHeadSize)); + uint32_t sectorsize = host_readw((ConstHostPt)(&nhdhdr.wSectLen)); + + if (sectorsize != 0 && ((sectorsize & (sectorsize - 1)) == 0/*is power of 2*/) && + sectorsize >= 256 && sectorsize <= 1024 && + ofs != 0 && (ofs % sectorsize) == 0/*offset is nonzero and multiple of sector size*/) { + + sector_size = sectorsize; + imgSizeK -= (ofs / 1024); + image_base = ofs; + image_length -= ofs; + LOG_MSG("NHD header: sectorsize is %u bytes/sector, header is %u bytes", + (unsigned int)sectorsize,(unsigned int)ofs); + + /* take on the geometry. + * PC-98 IPL1 support will need it to make sense of the partition table. */ + sectors = host_readw((ConstHostPt)(&nhdhdr.wSect)); + heads = host_readw((ConstHostPt)(&nhdhdr.wHead)); + cylinders = host_readd((ConstHostPt)(&nhdhdr.dwCylinder)); + LOG_MSG("NHD: Geometry is C/H/S %u/%u/%u", + (unsigned int)cylinders,(unsigned int)heads,(unsigned int)sectors); + } + else { + LOG_MSG("NHD header rejected. sectorsize=%u headersize=%u", + (unsigned int)sectorsize,(unsigned int)ofs); + } + } + else { + LOG_MSG("Unable to read .NHD header"); + } + } + } + if (!strcasecmp(ext,".hdi")) { + if (imgSizeK >= 160) { + HDIHDR hdihdr; + + // PC-98 .HDI images appear to be 4096 bytes of a short header and many zeros. + // followed by a straight sector dump of the disk. The header is NOT NECESSARILY + // 4KB in size, but usually is. + LOG_MSG("Image file has .HDI extension, assuming HDI image and will take on parameters in header."); + + assert(sizeof(hdihdr) == 0x20); + if (fseek(imgFile,0,SEEK_SET) == 0 && ftell(imgFile) == 0 && + fread(&hdihdr,sizeof(hdihdr),1,imgFile) == 1) { + uint32_t ofs = host_readd(hdihdr.headersize); + uint32_t hddsize = host_readd(hdihdr.hddsize); /* includes header */ + uint32_t sectorsize = host_readd(hdihdr.sectorsize); + + if (sectorsize != 0 && ((sectorsize & (sectorsize - 1)) == 0/*is power of 2*/) && + sectorsize >= 256 && sectorsize <= 1024 && + ofs != 0 && (ofs % sectorsize) == 0/*offset is nonzero and multiple of sector size*/ && + (ofs % 1024) == 0/*offset is a multiple of 1024 because of imgSizeK*/ && + hddsize >= sectorsize && (hddsize/1024) <= (imgSizeK+4)) { + + sector_size = sectorsize; + imgSizeK -= (ofs / 1024); + image_base = ofs; + image_length -= ofs; + LOG_MSG("HDI header: sectorsize is %u bytes/sector, header is %u bytes, hdd size (plus header) is %u bytes", + (unsigned int)sectorsize,(unsigned int)ofs,(unsigned int)hddsize); + + /* take on the geometry. + * PC-98 IPL1 support will need it to make sense of the partition table. */ + sectors = host_readd(hdihdr.sectors); + heads = host_readd(hdihdr.surfaces); + cylinders = host_readd(hdihdr.cylinders); + LOG_MSG("HDI: Geometry is C/H/S %u/%u/%u", + (unsigned int)cylinders,(unsigned int)heads,(unsigned int)sectors); + } + else { + LOG_MSG("HDI header rejected. sectorsize=%u headersize=%u hddsize=%u", + (unsigned int)sectorsize,(unsigned int)ofs,(unsigned int)hddsize); + } + } + else { + LOG_MSG("Unable to read .HDI header"); + } + } + } + } + } + + if (sectors == 0 || heads == 0 || cylinders == 0) + active = false; + } +} + +void imageDisk::Set_Geometry(Bit32u setHeads, Bit32u setCyl, Bit32u setSect, Bit32u setSectSize) { + Bitu bigdisk_shift = 0; + + if (IS_PC98_ARCH) { + /* TODO: PC-98 has it's own 4096 cylinder limit */ + } + else { + if(setCyl > 16384) LOG_MSG("This disk image is too big."); + else if(setCyl > 8192) bigdisk_shift = 4; + else if(setCyl > 4096) bigdisk_shift = 3; + else if(setCyl > 2048) bigdisk_shift = 2; + else if(setCyl > 1024) bigdisk_shift = 1; + } + + heads = setHeads << bigdisk_shift; + cylinders = setCyl >> bigdisk_shift; + sectors = setSect; + sector_size = setSectSize; + active = true; +} + +void imageDisk::Get_Geometry(Bit32u * getHeads, Bit32u *getCyl, Bit32u *getSect, Bit32u *getSectSize) { + *getHeads = heads; + *getCyl = cylinders; + *getSect = sectors; + *getSectSize = sector_size; +} + +Bit8u imageDisk::GetBiosType(void) { + if(!hardDrive) { + return (Bit8u)DiskGeometryList[floppytype].biosval; + } else return 0; +} + +Bit32u imageDisk::getSectSize(void) { + return sector_size; +} + +static Bitu GetDosDriveNumber(Bitu biosNum) { + switch(biosNum) { + case 0x0: + return 0x0; + case 0x1: + return 0x1; + case 0x80: + return 0x2; + case 0x81: + return 0x3; + case 0x82: + return 0x4; + case 0x83: + return 0x5; + default: + return 0x7f; + } +} + +static bool driveInactive(Bitu driveNum) { + if(driveNum>=(2 + MAX_HDD_IMAGES)) { + LOG(LOG_BIOS,LOG_ERROR)("Disk %d non-existant", (int)driveNum); + last_status = 0x01; + CALLBACK_SCF(true); + return true; + } + if(imageDiskList[driveNum] == NULL) { + LOG(LOG_BIOS,LOG_ERROR)("Disk %d not active", (int)driveNum); + last_status = 0x01; + CALLBACK_SCF(true); + return true; + } + if(!imageDiskList[driveNum]->active) { + LOG(LOG_BIOS,LOG_ERROR)("Disk %d not active", (int)driveNum); + last_status = 0x01; + CALLBACK_SCF(true); + return true; + } + return false; +} + +static struct { + Bit8u sz; + Bit8u res; + Bit16u num; + Bit16u off; + Bit16u seg; + Bit32u sector; +} dap; + +static void readDAP(Bit16u seg, Bit16u off) { + dap.sz = real_readb(seg,off++); + dap.res = real_readb(seg,off++); + dap.num = real_readw(seg,off); off += 2; + dap.off = real_readw(seg,off); off += 2; + dap.seg = real_readw(seg,off); off += 2; + + /* Although sector size is 64-bit, 32-bit 2TB limit should be more than enough */ + dap.sector = real_readd(seg,off); off +=4; + + if (real_readd(seg,off)) { + E_Exit("INT13: 64-bit sector addressing not supported"); + } +} + +void IDE_ResetDiskByBIOS(unsigned char disk); +void IDE_EmuINT13DiskReadByBIOS(unsigned char disk,unsigned int cyl,unsigned int head,unsigned sect); +void IDE_EmuINT13DiskReadByBIOS_LBA(unsigned char disk,uint64_t lba); + +static Bitu INT13_DiskHandler(void) { + Bit16u segat, bufptr; + Bit8u sectbuf[512]; + Bitu drivenum; + Bitu i,t; + last_drive = reg_dl; + drivenum = GetDosDriveNumber(reg_dl); + bool any_images = false; + for(i = 0;i < MAX_DISK_IMAGES;i++) { + if(imageDiskList[i]) any_images=true; + } + + // unconditionally enable the interrupt flag + CALLBACK_SIF(true); + + /* map out functions 0x40-0x48 if not emulating INT 13h extensions */ + if (!int13_extensions_enable && reg_ah >= 0x40 && reg_ah <= 0x48) { + LOG_MSG("Warning: Guest is attempting to use INT 13h extensions (AH=0x%02X). Set 'int 13 extensions=1' if you want to enable them.\n",reg_ah); + reg_ah=0xff; + CALLBACK_SCF(true); + return CBRET_NONE; + } + + //drivenum = 0; + //LOG_MSG("INT13: Function %x called on drive %x (dos drive %d)", reg_ah, reg_dl, drivenum); + switch(reg_ah) { + case 0x0: /* Reset disk */ + { + /* if there aren't any diskimages (so only localdrives and virtual drives) + * always succeed on reset disk. If there are diskimages then and only then + * do real checks + */ + if (any_images && driveInactive(drivenum)) { + /* driveInactive sets carry flag if the specified drive is not available */ + if ((machine==MCH_CGA) || (machine==MCH_AMSTRAD) || (machine==MCH_PCJR)) { + /* those bioses call floppy drive reset for invalid drive values */ + if (((imageDiskList[0]) && (imageDiskList[0]->active)) || ((imageDiskList[1]) && (imageDiskList[1]->active))) { + if (machine!=MCH_PCJR && reg_dl<0x80) reg_ip++; + last_status = 0x00; + CALLBACK_SCF(false); + } + } + return CBRET_NONE; + } + if (machine!=MCH_PCJR && reg_dl<0x80) reg_ip++; + if (reg_dl >= 0x80) IDE_ResetDiskByBIOS(reg_dl); + last_status = 0x00; + CALLBACK_SCF(false); + } + break; + case 0x1: /* Get status of last operation */ + + if(last_status != 0x00) { + reg_ah = last_status; + CALLBACK_SCF(true); + } else { + reg_ah = 0x00; + CALLBACK_SCF(false); + } + break; + case 0x2: /* Read sectors */ + if (reg_al==0) { + reg_ah = 0x01; + CALLBACK_SCF(true); + return CBRET_NONE; + } + if (!any_images) { + // Inherit the Earth cdrom (uses it as disk test) + if (((reg_dl&0x80)==0x80) && (reg_dh==0) && ((reg_cl&0x3f)==1)) { + reg_ah = 0; + CALLBACK_SCF(false); + return CBRET_NONE; + } + } + if (driveInactive(drivenum)) { + reg_ah = 0xff; + CALLBACK_SCF(true); + return CBRET_NONE; + } + + /* INT 13h is limited to 512 bytes/sector (as far as I know). + * The sector buffer in this function is limited to 512 bytes/sector, + * so this is also a protection against overruning the stack if you + * mount a PC-98 disk image (1024 bytes/sector) and try to read it with INT 13h. */ + if (imageDiskList[drivenum]->sector_size > sizeof(sectbuf)) { + LOG(LOG_MISC,LOG_DEBUG)("INT 13h: Read failed because disk bytes/sector on drive %c is too large",(char)drivenum+'A'); + + imageDiskChange[drivenum] = false; + + reg_ah = 0x80; /* timeout */ + CALLBACK_SCF(true); + return CBRET_NONE; + } + + /* If the disk changed, the first INT 13h read will signal an error and set AH = 0x06 to indicate disk change */ + if (drivenum < 2 && imageDiskChange[drivenum]) { + LOG(LOG_MISC,LOG_DEBUG)("INT 13h: Failing first read of drive %c to indicate disk change",(char)drivenum+'A'); + + imageDiskChange[drivenum] = false; + + reg_ah = 0x06; /* diskette changed or removed */ + CALLBACK_SCF(true); + return CBRET_NONE; + } + + segat = SegValue(es); + bufptr = reg_bx; + for(i=0;iRead_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)((reg_cl & 63)+i), sectbuf); + + /* IDE emulation: simulate change of IDE state that would occur on a real machine after INT 13h */ + IDE_EmuINT13DiskReadByBIOS(reg_dl, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)reg_dh, (Bit32u)((reg_cl & 63)+i)); + + if((last_status != 0x00) || (killRead)) { + LOG_MSG("Error in disk read"); + killRead = false; + reg_ah = 0x04; + CALLBACK_SCF(true); + return CBRET_NONE; + } + for(t=0;t<512;t++) { + real_writeb(segat,bufptr,sectbuf[t]); + bufptr++; + } + } + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + case 0x3: /* Write sectors */ + + if(driveInactive(drivenum)) { + reg_ah = 0xff; + CALLBACK_SCF(true); + return CBRET_NONE; + } + + /* INT 13h is limited to 512 bytes/sector (as far as I know). + * The sector buffer in this function is limited to 512 bytes/sector, + * so this is also a protection against overruning the stack if you + * mount a PC-98 disk image (1024 bytes/sector) and try to read it with INT 13h. */ + if (imageDiskList[drivenum]->sector_size > sizeof(sectbuf)) { + LOG(LOG_MISC,LOG_DEBUG)("INT 13h: Write failed because disk bytes/sector on drive %c is too large",(char)drivenum+'A'); + + imageDiskChange[drivenum] = false; + + reg_ah = 0x80; /* timeout */ + CALLBACK_SCF(true); + return CBRET_NONE; + } + + bufptr = reg_bx; + for(i=0;igetSectSize();t++) { + sectbuf[t] = real_readb(SegValue(es),bufptr); + bufptr++; + } + + last_status = imageDiskList[drivenum]->Write_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0) << 2)), (Bit32u)((reg_cl & 63) + i), §buf[0]); + if(last_status != 0x00) { + CALLBACK_SCF(true); + return CBRET_NONE; + } + } + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + case 0x04: /* Verify sectors */ + if (reg_al==0) { + reg_ah = 0x01; + CALLBACK_SCF(true); + return CBRET_NONE; + } + if(driveInactive(drivenum)) return CBRET_NONE; + + /* TODO: Finish coding this section */ + /* + segat = SegValue(es); + bufptr = reg_bx; + for(i=0;iRead_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)((reg_cl & 63)+i), sectbuf); + if(last_status != 0x00) { + LOG_MSG("Error in disk read"); + CALLBACK_SCF(true); + return CBRET_NONE; + } + for(t=0;t<512;t++) { + real_writeb(segat,bufptr,sectbuf[t]); + bufptr++; + } + }*/ + reg_ah = 0x00; + //Qbix: The following codes don't match my specs. al should be number of sector verified + //reg_al = 0x10; /* CRC verify failed */ + //reg_al = 0x00; /* CRC verify succeeded */ + CALLBACK_SCF(false); + + break; + case 0x05: /* Format track */ + /* ignore it. I just fucking want FORMAT.COM to write the FAT structure for God's sake */ + LOG_MSG("WARNING: Format track ignored\n"); + CALLBACK_SCF(false); + reg_ah = 0x00; + break; + case 0x06: /* Format track set bad sector flags */ + /* ignore it. I just fucking want FORMAT.COM to write the FAT structure for God's sake */ + LOG_MSG("WARNING: Format track set bad sector flags ignored (6)\n"); + CALLBACK_SCF(false); + reg_ah = 0x00; + break; + case 0x07: /* Format track set bad sector flags */ + /* ignore it. I just fucking want FORMAT.COM to write the FAT structure for God's sake */ + LOG_MSG("WARNING: Format track set bad sector flags ignored (7)\n"); + CALLBACK_SCF(false); + reg_ah = 0x00; + break; + case 0x08: /* Get drive parameters */ + if(driveInactive(drivenum)) { + last_status = 0x07; + reg_ah = last_status; + CALLBACK_SCF(true); + return CBRET_NONE; + } + reg_ax = 0x00; + reg_bl = imageDiskList[drivenum]->GetBiosType(); + Bit32u tmpheads, tmpcyl, tmpsect, tmpsize; + imageDiskList[drivenum]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize); + if (tmpcyl==0) LOG(LOG_BIOS,LOG_ERROR)("INT13 DrivParm: cylinder count zero!"); + else tmpcyl--; // cylinder count -> max cylinder + if (tmpheads==0) LOG(LOG_BIOS,LOG_ERROR)("INT13 DrivParm: head count zero!"); + else tmpheads--; // head count -> max head + + /* older BIOSes were known to subtract 1 or 2 additional "reserved" cylinders. + * some code, such as Windows 3.1 WDCTRL, might assume that fact. emulate that here */ + { + Bit32u reserv = imageDiskList[drivenum]->Get_Reserved_Cylinders(); + if (tmpcyl > reserv) tmpcyl -= reserv; + else tmpcyl = 0; + } + + reg_ch = (Bit8u)(tmpcyl & 0xff); + reg_cl = (Bit8u)(((tmpcyl >> 2) & 0xc0) | (tmpsect & 0x3f)); + reg_dh = (Bit8u)tmpheads; + last_status = 0x00; + if (reg_dl&0x80) { // harddisks + reg_dl = 0; + for (int index = 2; index < MAX_DISK_IMAGES; index++) { + if (imageDiskList[index] != NULL) reg_dl++; + } + } else { // floppy disks + reg_dl = 0; + if(imageDiskList[0] != NULL) reg_dl++; + if(imageDiskList[1] != NULL) reg_dl++; + } + CALLBACK_SCF(false); + break; + case 0x11: /* Recalibrate drive */ + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + case 0x17: /* Set disk type for format */ + /* Pirates! needs this to load */ + killRead = true; + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + case 0x41: /* Check Extensions Present */ + if ((reg_bx == 0x55aa) && !(driveInactive(drivenum))) { + LOG_MSG("INT13: Check Extensions Present for drive: 0x%x", reg_dl); + reg_ah=0x1; /* 1.x extension supported */ + reg_bx=0xaa55; /* Extensions installed */ + reg_cx=0x1; /* Extended disk access functions (AH=42h-44h,47h,48h) supported */ + CALLBACK_SCF(false); + break; + } + LOG_MSG("INT13: AH=41h, Function not supported 0x%x for drive: 0x%x", reg_bx, reg_dl); + CALLBACK_SCF(true); + break; + case 0x42: /* Extended Read Sectors From Drive */ + /* Read Disk Address Packet */ + readDAP(SegValue(ds),reg_si); + + if (dap.num==0) { + reg_ah = 0x01; + CALLBACK_SCF(true); + return CBRET_NONE; + } + if (!any_images) { + // Inherit the Earth cdrom (uses it as disk test) + if (((reg_dl&0x80)==0x80) && (reg_dh==0) && ((reg_cl&0x3f)==1)) { + reg_ah = 0; + CALLBACK_SCF(false); + return CBRET_NONE; + } + } + if (driveInactive(drivenum)) { + reg_ah = 0xff; + CALLBACK_SCF(true); + return CBRET_NONE; + } + + segat = dap.seg; + bufptr = dap.off; + for(i=0;iRead_AbsoluteSector(dap.sector+i, sectbuf); + + IDE_EmuINT13DiskReadByBIOS_LBA(reg_dl,dap.sector+i); + + if((last_status != 0x00) || (killRead)) { + LOG_MSG("Error in disk read"); + killRead = false; + reg_ah = 0x04; + CALLBACK_SCF(true); + return CBRET_NONE; + } + for(t=0;t<512;t++) { + real_writeb(segat,bufptr,sectbuf[t]); + bufptr++; + } + } + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + case 0x43: /* Extended Write Sectors to Drive */ + if(driveInactive(drivenum)) { + reg_ah = 0xff; + CALLBACK_SCF(true); + return CBRET_NONE; + } + + /* Read Disk Address Packet */ + readDAP(SegValue(ds),reg_si); + bufptr = dap.off; + for(i=0;igetSectSize();t++) { + sectbuf[t] = real_readb(dap.seg,bufptr); + bufptr++; + } + + last_status = imageDiskList[drivenum]->Write_AbsoluteSector(dap.sector+i, §buf[0]); + if(last_status != 0x00) { + CALLBACK_SCF(true); + return CBRET_NONE; + } + } + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + case 0x48: { /* get drive parameters */ + uint16_t bufsz; + + if(driveInactive(drivenum)) { + reg_ah = 0xff; + CALLBACK_SCF(true); + return CBRET_NONE; + } + + segat = SegValue(ds); + bufptr = reg_si; + bufsz = real_readw(segat,bufptr+0); + if (bufsz < 0x1A) { + reg_ah = 0xff; + CALLBACK_SCF(true); + return CBRET_NONE; + } + if (bufsz > 0x1E) bufsz = 0x1E; + else bufsz = 0x1A; + + Bit32u tmpheads, tmpcyl, tmpsect, tmpsize; + imageDiskList[drivenum]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize); + + real_writew(segat,bufptr+0x00,bufsz); + real_writew(segat,bufptr+0x02,0x0003); /* C/H/S valid, DMA boundary errors handled */ + real_writed(segat,bufptr+0x04,tmpcyl); + real_writed(segat,bufptr+0x08,tmpheads); + real_writed(segat,bufptr+0x0C,tmpsect); + real_writed(segat,bufptr+0x10,tmpcyl*tmpheads*tmpsect); + real_writed(segat,bufptr+0x14,0); + real_writew(segat,bufptr+0x18,512); + if (bufsz >= 0x1E) + real_writed(segat,bufptr+0x1A,0xFFFFFFFF); /* no EDD information available */ + + reg_ah = 0x00; + CALLBACK_SCF(false); + } break; + default: + LOG(LOG_BIOS,LOG_ERROR)("INT13: Function %x called on drive %x (dos drive %d)", (int)reg_ah, (int)reg_dl, (int)drivenum); + reg_ah=0xff; + CALLBACK_SCF(true); + } + return CBRET_NONE; +} + +void CALLBACK_DeAllocate(Bitu in); + +void BIOS_UnsetupDisks(void) { + if (call_int13 != 0) { + CALLBACK_DeAllocate(call_int13); + RealSetVec(0x13,0); /* zero INT 13h for now */ + call_int13 = 0; + } + if (diskparm0 != 0) { + CALLBACK_DeAllocate(diskparm0); + diskparm0 = 0; + } + if (diskparm1 != 0) { + CALLBACK_DeAllocate(diskparm1); + diskparm1 = 0; + } +} + +void BIOS_SetupDisks(void) { + unsigned int i; + + if (IS_PC98_ARCH) { + // TODO + return; + } + +/* TODO Start the time correctly */ + call_int13=CALLBACK_Allocate(); + CALLBACK_Setup(call_int13,&INT13_DiskHandler,CB_INT13,"Int 13 Bios disk"); + RealSetVec(0x13,CALLBACK_RealPointer(call_int13)); + + //release the drives after a soft reset + FreeBIOSDiskList(); + + /* FIXME: Um... these aren't callbacks. Why are they allocated as callbacks? We have ROM general allocation now. */ + diskparm0 = CALLBACK_Allocate(); + CALLBACK_SetDescription(diskparm0,"BIOS Disk 0 parameter table"); + diskparm1 = CALLBACK_Allocate(); + CALLBACK_SetDescription(diskparm1,"BIOS Disk 1 parameter table"); + swapPosition = 0; + + RealSetVec(0x41,CALLBACK_RealPointer(diskparm0)); + RealSetVec(0x46,CALLBACK_RealPointer(diskparm1)); + + PhysPt dp0physaddr=CALLBACK_PhysPointer(diskparm0); + PhysPt dp1physaddr=CALLBACK_PhysPointer(diskparm1); + for(i=0;i<16;i++) { + phys_writeb(dp0physaddr+i,0); + phys_writeb(dp1physaddr+i,0); + } + + imgDTASeg = 0; + +/* Setup the Bios Area */ + mem_writeb(BIOS_HARDDISK_COUNT,2); + + killRead = false; + swapping_requested = false; +} + +// VFD *.FDD floppy disk format support + +Bit8u imageDiskVFD::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data,unsigned int req_sector_size) { + vfdentry *ent; + + if (req_sector_size == 0) + req_sector_size = sector_size; + +// LOG_MSG("VFD read sector: CHS %u/%u/%u sz=%u",cylinder,head,sector,req_sector_size); + + ent = findSector(head,cylinder,sector,req_sector_size); + if (ent == NULL) return 0x05; + if (ent->getSectorSize() != req_sector_size) return 0x05; + + if (ent->hasSectorData()) { + fseek(diskimg,ent->data_offset,SEEK_SET); + if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05; + if (fread(data,req_sector_size,1,diskimg) != 1) return 0x05; + return 0; + } + else if (ent->hasFill()) { + memset(data,ent->fillbyte,req_sector_size); + return 0x00; + } + + return 0x05; +} + +Bit8u imageDiskVFD::Read_AbsoluteSector(Bit32u sectnum, void * data) { + unsigned int c,h,s; + + if (sectors == 0 || heads == 0) + return 0x05; + + s = (sectnum % sectors) + 1; + h = (sectnum / sectors) % heads; + c = (sectnum / sectors / heads); + return Read_Sector(h,c,s,data); +} + +imageDiskVFD::vfdentry *imageDiskVFD::findSector(Bit8u head,Bit8u track,Bit8u sector/*TODO: physical head?*/,unsigned int req_sector_size) { + std::vector::iterator i = dents.begin(); + unsigned char szb=0xFF; + + if (req_sector_size == 0) + req_sector_size = sector_size; + + if (req_sector_size != ~0U) { + unsigned int c = req_sector_size; + while (c >= 128U) { + c >>= 1U; + szb++; + } + +// LOG_MSG("req=%u c=%u szb=%u",req_sector_size,c,szb); + + if (szb > 8 || c != 64U) + return NULL; + } + + while (i != dents.end()) { + imageDiskVFD::vfdentry &ent = *i; + + if (ent.head == head && + ent.track == track && + ent.sector == sector && + (ent.sizebyte == szb || req_sector_size == ~0U)) + return &(*i); + + i++; + } + + return NULL; +} + +Bit8u imageDiskVFD::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,const void * data,unsigned int req_sector_size) { + unsigned long new_offset; + unsigned char tmp[12]; + vfdentry *ent; + +// LOG_MSG("VFD write sector: CHS %u/%u/%u",cylinder,head,sector); + + if (req_sector_size == 0) + req_sector_size = sector_size; + + ent = findSector(head,cylinder,sector,req_sector_size); + if (ent == NULL) return 0x05; + if (ent->getSectorSize() != req_sector_size) return 0x05; + + if (ent->hasSectorData()) { + fseek(diskimg,ent->data_offset,SEEK_SET); + if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05; + if (fwrite(data,req_sector_size,1,diskimg) != 1) return 0x05; + return 0; + } + else if (ent->hasFill()) { + bool isfill = false; + + /* well, is the data provided one character repeated? + * note the format cannot represent a fill byte of 0xFF */ + if (((unsigned char*)data)[0] != 0xFF) { + unsigned int i=1; + + do { + if (((unsigned char*)data)[i] == ((unsigned char*)data)[0]) { + if ((++i) == req_sector_size) { + isfill = true; + break; // yes! + } + } + else { + break; // nope + } + } while (1); + } + + if (ent->entry_offset == 0) return 0x05; + + if (isfill) { + fseek(diskimg,ent->entry_offset,SEEK_SET); + if ((uint32_t)ftell(diskimg) != ent->entry_offset) return 0x05; + if (fread(tmp,12,1,diskimg) != 1) return 0x05; + + tmp[0x04] = ((unsigned char*)data)[0]; // change the fill byte + + LOG_MSG("VFD write: 'fill' sector changing fill byte to 0x%x",tmp[0x04]); + + fseek(diskimg,ent->entry_offset,SEEK_SET); + if ((uint32_t)ftell(diskimg) != ent->entry_offset) return 0x05; + if (fwrite(tmp,12,1,diskimg) != 1) return 0x05; + } + else { + fseek(diskimg,0,SEEK_END); + new_offset = (unsigned long)ftell(diskimg); + + /* we have to change it from a fill sector to an actual sector */ + LOG_MSG("VFD write: changing 'fill' sector to one with data (data at %lu)",(unsigned long)new_offset); + + fseek(diskimg,ent->entry_offset,SEEK_SET); + if ((uint32_t)ftell(diskimg) != ent->entry_offset) return 0x05; + if (fread(tmp,12,1,diskimg) != 1) return 0x05; + + tmp[0x00] = ent->track; + tmp[0x01] = ent->head; + tmp[0x02] = ent->sector; + tmp[0x03] = ent->sizebyte; + tmp[0x04] = 0xFF; // no longer a fill byte + tmp[0x05] = 0x00; // TODO ?? + tmp[0x06] = 0x00; // TODO ?? + tmp[0x07] = 0x00; // TODO ?? + *((uint32_t*)(tmp+8)) = new_offset; + ent->fillbyte = 0xFF; + ent->data_offset = (uint32_t)new_offset; + + fseek(diskimg,ent->entry_offset,SEEK_SET); + if ((uint32_t)ftell(diskimg) != ent->entry_offset) return 0x05; + if (fwrite(tmp,12,1,diskimg) != 1) return 0x05; + + fseek(diskimg,ent->data_offset,SEEK_SET); + if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05; + if (fwrite(data,req_sector_size,1,diskimg) != 1) return 0x05; + } + } + + return 0x05; +} + +Bit8u imageDiskVFD::Write_AbsoluteSector(Bit32u sectnum,const void *data) { + unsigned int c,h,s; + + if (sectors == 0 || heads == 0) + return 0x05; + + s = (sectnum % sectors) + 1; + h = (sectnum / sectors) % heads; + c = (sectnum / sectors / heads); + return Write_Sector(h,c,s,data); +} + +imageDiskVFD::imageDiskVFD(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk) : imageDisk(ID_VFD) { + (void)isHardDisk;//UNUSED + unsigned char tmp[16]; + + heads = 1; + cylinders = 0; + image_base = 0; + sectors = 0; + active = false; + sector_size = 0; + reserved_cylinders = 0; + diskSizeK = imgSizeK; + diskimg = imgFile; + + if (imgName != NULL) + diskname = (const char*)imgName; + + // NOTES: + // + // +0x000: "VFD1.00" + // +0x0DC: array of 12-byte entries each describing a sector + // + // Each entry: + // +0x0: track + // +0x1: head + // +0x2: sector + // +0x3: sector size (128 << this byte) + // +0x4: fill byte, or 0xFF + // +0x5: unknown + // +0x6: unknown + // +0x7: unknown + // +0x8: absolute data offset (32-bit integer) or 0xFFFFFFFF if the entire sector is that fill byte + fseek(diskimg,0,SEEK_SET); + memset(tmp,0,8); + fread(tmp,1,8,diskimg); + + if (!memcmp(tmp,"VFD1.",5)) { + Bit8u i=0; + bool founddisk = false; + uint32_t stop_at = 0xC3FC; + unsigned long entof; + + // load table. + // we have to determine as we go where to stop reading. + // the source of info I read assumes the whole header (and table) + // is 0xC3FC bytes. I'm not inclined to assume that, so we go by + // that OR the first sector offset whichever is smaller. + // the table seems to trail off into a long series of 0xFF at the end. + fseek(diskimg,0xDC,SEEK_SET); + while ((entof=((unsigned long)ftell(diskimg)+12ul)) <= stop_at) { + memset(tmp,0xFF,12); + fread(tmp,12,1,diskimg); + + if (!memcmp(tmp,"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",12)) + continue; + if (!memcmp(tmp,"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",12)) + continue; + + struct vfdentry v; + + v.track = tmp[0]; + v.head = tmp[1]; + v.sector = tmp[2]; + v.sizebyte = tmp[3]; + v.fillbyte = tmp[4]; + v.data_offset = *((uint32_t*)(tmp+8)); + v.entry_offset = (uint32_t)entof; + + // maybe the table can end sooner than 0xC3FC? + // if we see sectors appear at an offset lower than our stop_at point + // then adjust the stop_at point. assume the table cannot mix with + // sector data. + if (v.hasSectorData()) { + if (stop_at > v.data_offset) + stop_at = v.data_offset; + } + + dents.push_back(v); + + LOG_MSG("VFD entry: track=%u head=%u sector=%u size=%u fill=0x%2X has_data=%u has_fill=%u entoff=%lu dataoff=%lu", + v.track, + v.head, + v.sector, + v.getSectorSize(), + v.fillbyte, + v.hasSectorData(), + v.hasFill(), + (unsigned long)v.entry_offset, + (unsigned long)v.data_offset); + } + + if (!dents.empty()) { + /* okay, now to figure out what the geometry of the disk is. + * we cannot just work from an "absolute" disk image model + * because there's no VFD header to just say what the geometry is. + * Like the IBM PC BIOS, we have to look at the disk and figure out + * which geometry to apply to it, even if the FDD format allows + * sectors on other tracks to have wild out of range sector, track, + * and head numbers or odd sized sectors. + * + * First, determine sector size according to the boot sector. */ + vfdentry *ent; + + ent = findSector(/*head*/0,/*track*/0,/*sector*/1,~0U); + if (ent != NULL) { + if (ent->sizebyte <= 3) /* x <= 1024 */ + sector_size = ent->getSectorSize(); + } + + /* oh yeah right, sure. + * I suppose you're one of those FDD images where the sector size is 128 bytes/sector + * in the boot sector and the rest is 256 bytes/sector elsewhere. I have no idea why + * but quite a few FDD images have this arrangement. */ + if (sector_size != 0 && sector_size < 512) { + ent = findSector(/*head*/0,/*track*/1,/*sector*/1,~0U); + if (ent != NULL) { + if (ent->sizebyte <= 3) { /* x <= 1024 */ + unsigned int nsz = ent->getSectorSize(); + if (sector_size != nsz) + LOG_MSG("VFD warning: sector size changes between track 0 and 1"); + if (sector_size < nsz) + sector_size = nsz; + } + } + } + + if (sector_size != 0) { + i=0; + while (DiskGeometryList[i].ksize != 0) { + diskGeo &diskent = DiskGeometryList[i]; + + if (diskent.bytespersect == sector_size) { + ent = findSector(0,0,diskent.secttrack); + if (ent != NULL) { + LOG_MSG("VFD disk probe: %u/%u/%u exists",0,0,diskent.secttrack); + if (sectors < diskent.secttrack) + sectors = diskent.secttrack; + } + } + + i++; + } + } + + if (sector_size != 0 && sectors != 0) { + i=0; + while (DiskGeometryList[i].ksize != 0) { + diskGeo &diskent = DiskGeometryList[i]; + + if (diskent.bytespersect == sector_size && diskent.secttrack >= sectors) { + ent = findSector(0,diskent.cylcount-1,sectors); + if (ent != NULL) { + LOG_MSG("VFD disk probe: %u/%u/%u exists",0,diskent.cylcount-1,sectors); + if (cylinders < diskent.cylcount) + cylinders = diskent.cylcount; + } + } + + i++; + } + } + + if (sector_size != 0 && sectors != 0 && cylinders != 0) { + ent = findSector(1,0,sectors); + if (ent != NULL) { + LOG_MSG("VFD disk probe: %u/%u/%u exists",1,0,sectors); + heads = 2; + } + } + + // TODO: drive_fat.cpp should use an extension to this API to allow changing the sectors/track + // according to what it reads from the MS-DOS BIOS parameter block, just like real MS-DOS. + // This would allow better representation of strange disk formats such as the "extended" + // floppy format that Microsoft used to use for Word 95 and Windows 95 install floppies. + + LOG_MSG("VFD geometry detection: C/H/S %u/%u/%u %u bytes/sector", + cylinders, heads, sectors, sector_size); + + if (sector_size != 0 && sectors != 0 && cylinders != 0 && heads != 0) + founddisk = true; + + if(!founddisk) { + active = false; + } else { + incrementFDD(); + } + } + } +} + +imageDiskVFD::~imageDiskVFD() { + if(diskimg != NULL) { + fclose(diskimg); + diskimg=NULL; + } +} + +// D88 *.D88 floppy disk format support + +enum { + D88_TRACKMAX = 164, + D88_HEADERSIZE = 0x20 + (D88_TRACKMAX * 4) +}; + +#pragma pack(push,1) +typedef struct D88HEAD { + char fd_name[17]; // +0x00 Disk Name + unsigned char reserved1[9]; // +0x11 Reserved + unsigned char protect; // +0x1A Write Protect bit:4 + unsigned char fd_type; // +0x1B Disk Format + uint32_t fd_size; // +0x1C Disk Size + uint32_t trackp[D88_TRACKMAX]; // +0x20 164 x 4 = 656 = 0x290 +} D88HEAD; // =0x2B0 total + +typedef struct D88SEC { + unsigned char c; // +0x00 + unsigned char h; // +0x01 + unsigned char r; // +0x02 + unsigned char n; // +0x03 + uint16_t sectors; // +0x04 Sector Count + unsigned char mfm_flg; // +0x06 sides + unsigned char del_flg; // +0x07 DELETED DATA + unsigned char stat; // +0x08 STATUS (FDC ret) + unsigned char seektime; // +0x09 Seek Time + unsigned char reserved[3]; // +0x0A Reserved + unsigned char rpm_flg; // +0x0D rpm 0:1.2 1:1.44 + uint16_t size; // +0x0E Sector Size + // +} D88SEC; // =0x10 total +#pragma pack(pop) + +Bit8u imageDiskD88::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data,unsigned int req_sector_size) { + vfdentry *ent; + + if (req_sector_size == 0) + req_sector_size = sector_size; + +// LOG_MSG("D88 read sector: CHS %u/%u/%u sz=%u",cylinder,head,sector,req_sector_size); + + ent = findSector(head,cylinder,sector,req_sector_size); + if (ent == NULL) return 0x05; + if (ent->getSectorSize() != req_sector_size) return 0x05; + + fseek(diskimg,ent->data_offset,SEEK_SET); + if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05; + if (fread(data,req_sector_size,1,diskimg) != 1) return 0x05; + return 0; +} + +Bit8u imageDiskD88::Read_AbsoluteSector(Bit32u sectnum, void * data) { + unsigned int c,h,s; + + if (sectors == 0 || heads == 0) + return 0x05; + + s = (sectnum % sectors) + 1; + h = (sectnum / sectors) % heads; + c = (sectnum / sectors / heads); + return Read_Sector(h,c,s,data); +} + +imageDiskD88::vfdentry *imageDiskD88::findSector(Bit8u head,Bit8u track,Bit8u sector/*TODO: physical head?*/,unsigned int req_sector_size) { + if ((size_t)track >= dents.size()) + return NULL; + + std::vector::iterator i = dents.begin(); + + if (req_sector_size == 0) + req_sector_size = sector_size; + + while (i != dents.end()) { + imageDiskD88::vfdentry &ent = *i; + + if (ent.head == head && + ent.track == track && + ent.sector == sector && + (ent.sector_size == req_sector_size || req_sector_size == ~0U)) + return &(*i); + + i++; + } + + return NULL; +} + +Bit8u imageDiskD88::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,const void * data,unsigned int req_sector_size) { + vfdentry *ent; + + if (req_sector_size == 0) + req_sector_size = sector_size; + +// LOG_MSG("D88 read sector: CHS %u/%u/%u sz=%u",cylinder,head,sector,req_sector_size); + + ent = findSector(head,cylinder,sector,req_sector_size); + if (ent == NULL) return 0x05; + if (ent->getSectorSize() != req_sector_size) return 0x05; + + fseek(diskimg,ent->data_offset,SEEK_SET); + if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05; + if (fwrite(data,req_sector_size,1,diskimg) != 1) return 0x05; + return 0; +} + +Bit8u imageDiskD88::Write_AbsoluteSector(Bit32u sectnum,const void *data) { + unsigned int c,h,s; + + if (sectors == 0 || heads == 0) + return 0x05; + + s = (sectnum % sectors) + 1; + h = (sectnum / sectors) % heads; + c = (sectnum / sectors / heads); + return Write_Sector(h,c,s,data); +} + +imageDiskD88::imageDiskD88(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk) : imageDisk(ID_D88) { + (void)isHardDisk;//UNUSED + D88HEAD head; + + fd_type_major = DISKTYPE_2D; + fd_type_minor = 0; + + assert(sizeof(D88HEAD) == 0x2B0); + assert(sizeof(D88SEC) == 0x10); + + heads = 0; + cylinders = 0; + image_base = 0; + sectors = 0; + active = false; + sector_size = 0; + reserved_cylinders = 0; + diskSizeK = imgSizeK; + diskimg = imgFile; + active = false; + + if (imgName != NULL) + diskname = (const char*)imgName; + + // NOTES: + // + // +0x000: D88 header + // +0x020: Offset of D88 tracks, per track + // +0x2B0: + // + // Track offsets are sequential, always + // + // Each track is an array of: + // + // ENTRY: + // + // + // + // Array of ENTRY from offset until next track + fseek(diskimg,0,SEEK_END); + off_t fsz = ftell(diskimg); + + fseek(diskimg,0,SEEK_SET); + if (fread(&head,sizeof(head),1,diskimg) != 1) return; + + // validate fd_size + if ((uint32_t)host_readd((ConstHostPt)(&head.fd_size)) > (uint32_t)fsz) return; + + fd_type_major = head.fd_type >> 4U; + fd_type_minor = head.fd_type & 0xFU; + + // validate that none of the track offsets extend past the file + { + for (unsigned int i=0;i < D88_TRACKMAX;i++) { + uint32_t trackoff = host_readd((ConstHostPt)(&head.trackp[i])); + + if (trackoff == 0) continue; + + if ((trackoff + 16U) > (uint32_t)fsz) { + LOG_MSG("D88: track starts past end of file"); + return; + } + } + } + + // read each track + for (unsigned int track=0;track < D88_TRACKMAX;track++) { + uint32_t trackoff = host_readd((ConstHostPt)(&head.trackp[track])); + + if (trackoff != 0) { + fseek(diskimg, trackoff, SEEK_SET); + if (ftell(diskimg) != trackoff) continue; + + D88SEC s; + unsigned int count = 0; + + do { + if (fread(&s,sizeof(s),1,diskimg) != 1) break; + + uint16_t sector_count = host_readw((ConstHostPt)(&s.sectors)); + uint16_t sector_size = host_readw((ConstHostPt)(&s.size)); + + if (sector_count == 0U || sector_size < 128U) break; + if (sector_count > 128U || sector_size > 16384U) break; + if (s.n > 8U) s.n = 8U; + + vfdentry vent; + vent.sector_size = 128 << s.n; + vent.data_offset = (uint32_t)ftell(diskimg); + vent.entry_offset = vent.data_offset - (uint32_t)16; + vent.track = s.c; + vent.head = s.h; + vent.sector = s.r; + + LOG_MSG("D88: trackindex=%u C/H/S/sz=%u/%u/%u/%u data-at=0x%lx", + track,vent.track,vent.head,vent.sector,vent.sector_size,(unsigned long)vent.data_offset); + + dents.push_back(vent); + if ((++count) >= sector_count) break; + + fseek(diskimg, sector_size, SEEK_CUR); + } while (1); + } + } + + if (!dents.empty()) { + /* okay, now to figure out what the geometry of the disk is. + * we cannot just work from an "absolute" disk image model + * because there's no D88 header to just say what the geometry is. + * Like the IBM PC BIOS, we have to look at the disk and figure out + * which geometry to apply to it, even if the FDD format allows + * sectors on other tracks to have wild out of range sector, track, + * and head numbers or odd sized sectors. + * + * First, determine sector size according to the boot sector. */ + bool founddisk = false; + vfdentry *ent; + + ent = findSector(/*head*/0,/*track*/0,/*sector*/1,~0U); + if (ent != NULL) { + if (ent->getSectorSize() <= 1024) /* x <= 1024 */ + sector_size = ent->getSectorSize(); + } + + /* oh yeah right, sure. + * I suppose you're one of those FDD images where the sector size is 128 bytes/sector + * in the boot sector and the rest is 256 bytes/sector elsewhere. I have no idea why + * but quite a few FDD images have this arrangement. */ + if (sector_size != 0 && sector_size < 512) { + ent = findSector(/*head*/0,/*track*/1,/*sector*/1,~0U); + if (ent != NULL) { + if (ent->getSectorSize() <= 1024) { /* x <= 1024 */ + unsigned int nsz = ent->getSectorSize(); + if (sector_size != nsz) + LOG_MSG("D88 warning: sector size changes between track 0 and 1"); + if (sector_size < nsz) + sector_size = nsz; + } + } + } + + if (sector_size != 0) { + unsigned int i = 0; + while (DiskGeometryList[i].ksize != 0) { + diskGeo &diskent = DiskGeometryList[i]; + + if (diskent.bytespersect == sector_size) { + ent = findSector(0,0,diskent.secttrack); + if (ent != NULL) { + LOG_MSG("D88 disk probe: %u/%u/%u exists",0,0,diskent.secttrack); + if (sectors < diskent.secttrack) + sectors = diskent.secttrack; + } + } + + i++; + } + } + + if (sector_size != 0 && sectors != 0) { + unsigned int i = 0; + while (DiskGeometryList[i].ksize != 0) { + diskGeo &diskent = DiskGeometryList[i]; + + if (diskent.bytespersect == sector_size && diskent.secttrack >= sectors) { + ent = findSector(0,diskent.cylcount-1,sectors); + if (ent != NULL) { + LOG_MSG("D88 disk probe: %u/%u/%u exists",0,diskent.cylcount-1,sectors); + if (cylinders < diskent.cylcount) + cylinders = diskent.cylcount; + } + } + + i++; + } + } + + if (sector_size != 0 && sectors != 0 && cylinders != 0) { + ent = findSector(1,0,sectors); + if (ent != NULL) { + LOG_MSG("D88 disk probe: %u/%u/%u exists",1,0,sectors); + heads = 2; + } + } + + // TODO: drive_fat.cpp should use an extension to this API to allow changing the sectors/track + // according to what it reads from the MS-DOS BIOS parameter block, just like real MS-DOS. + // This would allow better representation of strange disk formats such as the "extended" + // floppy format that Microsoft used to use for Word 95 and Windows 95 install floppies. + + LOG_MSG("D88 geometry detection: C/H/S %u/%u/%u %u bytes/sector", + cylinders, heads, sectors, sector_size); + + if (sector_size != 0 && sectors != 0 && cylinders != 0 && heads != 0) + founddisk = true; + + if(!founddisk) { + active = false; + } else { + incrementFDD(); + } + } +} + +imageDiskD88::~imageDiskD88() { + if(diskimg != NULL) { + fclose(diskimg); + diskimg=NULL; + } +} + +/*--------------------------------*/ + +Bit8u imageDiskNFD::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data,unsigned int req_sector_size) { + vfdentry *ent; + + if (req_sector_size == 0) + req_sector_size = sector_size; + +// LOG_MSG("NFD read sector: CHS %u/%u/%u sz=%u",cylinder,head,sector,req_sector_size); + + ent = findSector(head,cylinder,sector,req_sector_size); + if (ent == NULL) return 0x05; + if (ent->getSectorSize() != req_sector_size) return 0x05; + + fseek(diskimg,ent->data_offset,SEEK_SET); + if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05; + if (fread(data,req_sector_size,1,diskimg) != 1) return 0x05; + return 0; +} + +Bit8u imageDiskNFD::Read_AbsoluteSector(Bit32u sectnum, void * data) { + unsigned int c,h,s; + + if (sectors == 0 || heads == 0) + return 0x05; + + s = (sectnum % sectors) + 1; + h = (sectnum / sectors) % heads; + c = (sectnum / sectors / heads); + return Read_Sector(h,c,s,data); +} + +imageDiskNFD::vfdentry *imageDiskNFD::findSector(Bit8u head,Bit8u track,Bit8u sector/*TODO: physical head?*/,unsigned int req_sector_size) { + if ((size_t)track >= dents.size()) + return NULL; + + std::vector::iterator i = dents.begin(); + + if (req_sector_size == 0) + req_sector_size = sector_size; + + while (i != dents.end()) { + imageDiskNFD::vfdentry &ent = *i; + + if (ent.head == head && + ent.track == track && + ent.sector == sector && + (ent.sector_size == req_sector_size || req_sector_size == ~0U)) + return &(*i); + + i++; + } + + return NULL; +} + +Bit8u imageDiskNFD::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,const void * data,unsigned int req_sector_size) { + vfdentry *ent; + + if (req_sector_size == 0) + req_sector_size = sector_size; + +// LOG_MSG("NFD read sector: CHS %u/%u/%u sz=%u",cylinder,head,sector,req_sector_size); + + ent = findSector(head,cylinder,sector,req_sector_size); + if (ent == NULL) return 0x05; + if (ent->getSectorSize() != req_sector_size) return 0x05; + + fseek(diskimg,ent->data_offset,SEEK_SET); + if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05; + if (fwrite(data,req_sector_size,1,diskimg) != 1) return 0x05; + return 0; +} + +Bit8u imageDiskNFD::Write_AbsoluteSector(Bit32u sectnum,const void *data) { + unsigned int c,h,s; + + if (sectors == 0 || heads == 0) + return 0x05; + + s = (sectnum % sectors) + 1; + h = (sectnum / sectors) % heads; + c = (sectnum / sectors / heads); + return Write_Sector(h,c,s,data); +} + +imageDiskNFD::imageDiskNFD(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk, unsigned int revision) : imageDisk(ID_NFD) { + (void)isHardDisk;//UNUSED + union { + NFDHDR head; + NFDHDRR1 headr1; + }; // these occupy the same location of memory + + assert(sizeof(NFDHDR) == 0x120); + assert(sizeof(NFDHDRR1) == 0x3C0); + assert(sizeof(NFDHDR_ENTRY) == 0x10); + + heads = 0; + cylinders = 0; + image_base = 0; + sectors = 0; + active = false; + sector_size = 0; + reserved_cylinders = 0; + diskSizeK = imgSizeK; + diskimg = imgFile; + active = false; + + if (imgName != NULL) + diskname = (const char*)imgName; + + // NOTES: + // + // +0x000: NFD header + // +0x020: Offset of NFD tracks, per track + // +0x2B0: + // + // Track offsets are sequential, always + // + // Each track is an array of: + // + // ENTRY: + // + // + // + // Array of ENTRY from offset until next track + fseek(diskimg,0,SEEK_END); + off_t fsz = ftell(diskimg); + + fseek(diskimg,0,SEEK_SET); + if (revision == 0) { + if (fread(&head,sizeof(head),1,diskimg) != 1) return; + } + else if (revision == 1) { + if (fread(&headr1,sizeof(headr1),1,diskimg) != 1) return; + } + else { + abort(); + } + + // validate fd_size + if ((uint32_t)host_readd((ConstHostPt)(&head.headersize)) < sizeof(head)) return; + if ((uint32_t)host_readd((ConstHostPt)(&head.headersize)) > (uint32_t)fsz) return; + + unsigned int data_offset = host_readd((ConstHostPt)(&head.headersize)); + + std::vector< std::pair > seclist; + + if (revision == 0) { + unsigned int secents = (host_readd((ConstHostPt)(&head.headersize)) - sizeof(head)) / sizeof(NFDHDR_ENTRY); + if (secents == 0) return; + secents--; + if (secents == 0) return; + + for (unsigned int i=0;i < secents;i++) { + uint32_t ofs = (uint32_t)ftell(diskimg); + NFDHDR_ENTRY e; + + if (fread(&e,sizeof(e),1,diskimg) != 1) return; + seclist.push_back( std::pair(ofs,e) ); + + if (e.log_cyl == 0xFF || e.log_head == 0xFF || e.log_rec == 0xFF || e.sec_len_pow2 > 7) + continue; + + LOG_MSG("NFD %u/%u: ofs=%lu data=%lu cyl=%u head=%u sec=%u len=%u", + (unsigned int)i, + (unsigned int)secents, + (unsigned long)ofs, + (unsigned long)data_offset, + e.log_cyl, + e.log_head, + e.log_rec, + 128 << e.sec_len_pow2); + + vfdentry vent; + vent.sector_size = 128 << e.sec_len_pow2; + vent.data_offset = (uint32_t)data_offset; + vent.entry_offset = (uint32_t)ofs; + vent.track = e.log_cyl; + vent.head = e.log_head; + vent.sector = e.log_rec; + dents.push_back(vent); + + data_offset += 128u << e.sec_len_pow2; + if (data_offset > (unsigned int)fsz) return; + } + } + else { + /* R1 has an array of offsets to where each tracks begins. + * The end of the track is an entry like 0x1A 0x00 0x00 0x00 0x00 0x00 0x00 .... */ + /* The R1 images I have as reference always have offsets in ascending order. */ + for (unsigned int ti=0;ti < 164;ti++) { + uint32_t trkoff = host_readd((ConstHostPt)(&headr1.trackheads[ti])); + + if (trkoff == 0) break; + + fseek(diskimg,trkoff,SEEK_SET); + if (ftell(diskimg) != trkoff) return; + + NFDHDR_ENTRY e; + + // track id + if (fread(&e,sizeof(e),1,diskimg) != 1) return; + unsigned int sectors = host_readw((ConstHostPt)(&e) + 0); + unsigned int diagcount = host_readw((ConstHostPt)(&e) + 2); + + LOG_MSG("NFD R1 track ent %u offset %lu sectors %u diag %u",ti,(unsigned long)trkoff,sectors,diagcount); + + for (unsigned int s=0;s < sectors;s++) { + uint32_t ofs = (uint32_t)ftell(diskimg); + + if (fread(&e,sizeof(e),1,diskimg) != 1) return; + + LOG_MSG("NFD %u/%u: ofs=%lu data=%lu cyl=%u head=%u sec=%u len=%u rep=%u", + (unsigned int)s, + (unsigned int)sectors, + (unsigned long)ofs, + (unsigned long)data_offset, + e.log_cyl, + e.log_head, + e.log_rec, + 128 << e.sec_len_pow2, + e.byRetry); + + vfdentry vent; + vent.sector_size = 128 << e.sec_len_pow2; + vent.data_offset = (uint32_t)data_offset; + vent.entry_offset = (uint32_t)ofs; + vent.track = e.log_cyl; + vent.head = e.log_head; + vent.sector = e.log_rec; + dents.push_back(vent); + + data_offset += 128u << e.sec_len_pow2; + if (data_offset > (unsigned int)fsz) return; + } + + for (unsigned int d=0;d < diagcount;d++) { + if (fread(&e,sizeof(e),1,diskimg) != 1) return; + + unsigned int retry = e.byRetry; + unsigned int len = host_readd((ConstHostPt)(&e) + 10); + + LOG_MSG("NFD diag %u/%u: retry=%u len=%u data=%lu",d,diagcount,retry,len,(unsigned long)data_offset); + + data_offset += (1+retry) * len; + } + } + } + + if (!dents.empty()) { + /* okay, now to figure out what the geometry of the disk is. + * we cannot just work from an "absolute" disk image model + * because there's no NFD header to just say what the geometry is. + * Like the IBM PC BIOS, we have to look at the disk and figure out + * which geometry to apply to it, even if the FDD format allows + * sectors on other tracks to have wild out of range sector, track, + * and head numbers or odd sized sectors. + * + * First, determine sector size according to the boot sector. */ + bool founddisk = false; + vfdentry *ent; + + ent = findSector(/*head*/0,/*track*/0,/*sector*/1,~0U); + if (ent != NULL) { + if (ent->getSectorSize() <= 1024) /* x <= 1024 */ + sector_size = ent->getSectorSize(); + } + + /* oh yeah right, sure. + * I suppose you're one of those FDD images where the sector size is 128 bytes/sector + * in the boot sector and the rest is 256 bytes/sector elsewhere. I have no idea why + * but quite a few FDD images have this arrangement. */ + if (sector_size != 0 && sector_size < 512) { + ent = findSector(/*head*/0,/*track*/1,/*sector*/1,~0U); + if (ent != NULL) { + if (ent->getSectorSize() <= 1024) { /* x <= 1024 */ + unsigned int nsz = ent->getSectorSize(); + if (sector_size != nsz) + LOG_MSG("NFD warning: sector size changes between track 0 and 1"); + if (sector_size < nsz) + sector_size = nsz; + } + } + } + + if (sector_size != 0) { + unsigned int i = 0; + while (DiskGeometryList[i].ksize != 0) { + diskGeo &diskent = DiskGeometryList[i]; + + if (diskent.bytespersect == sector_size) { + ent = findSector(0,0,diskent.secttrack); + if (ent != NULL) { + LOG_MSG("NFD disk probe: %u/%u/%u exists",0,0,diskent.secttrack); + if (sectors < diskent.secttrack) + sectors = diskent.secttrack; + } + } + + i++; + } + } + + if (sector_size != 0 && sectors != 0) { + unsigned int i = 0; + while (DiskGeometryList[i].ksize != 0) { + diskGeo &diskent = DiskGeometryList[i]; + + if (diskent.bytespersect == sector_size && diskent.secttrack >= sectors) { + ent = findSector(0,diskent.cylcount-1,sectors); + if (ent != NULL) { + LOG_MSG("NFD disk probe: %u/%u/%u exists",0,diskent.cylcount-1,sectors); + if (cylinders < diskent.cylcount) + cylinders = diskent.cylcount; + } + } + + i++; + } + } + + if (sector_size != 0 && sectors != 0 && cylinders != 0) { + ent = findSector(1,0,sectors); + if (ent != NULL) { + LOG_MSG("NFD disk probe: %u/%u/%u exists",1,0,sectors); + heads = 2; + } + } + + // TODO: drive_fat.cpp should use an extension to this API to allow changing the sectors/track + // according to what it reads from the MS-DOS BIOS parameter block, just like real MS-DOS. + // This would allow better representation of strange disk formats such as the "extended" + // floppy format that Microsoft used to use for Word 95 and Windows 95 install floppies. + + LOG_MSG("NFD geometry detection: C/H/S %u/%u/%u %u bytes/sector", + cylinders, heads, sectors, sector_size); + + if (sector_size != 0 && sectors != 0 && cylinders != 0 && heads != 0) + founddisk = true; + + if(!founddisk) { + active = false; + } else { + incrementFDD(); + } + } +} + +imageDiskNFD::~imageDiskNFD() { + if(diskimg != NULL) { + fclose(diskimg); + diskimg=NULL; + } +} + diff --git a/vs2015/dosbox-x.vcxproj b/vs2015/dosbox-x.vcxproj index aecdc18e0..4b3e2647b 100644 --- a/vs2015/dosbox-x.vcxproj +++ b/vs2015/dosbox-x.vcxproj @@ -358,7 +358,7 @@ - winmm.lib;imm32.lib;opengl32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;Ws2_32.lib;$(SolutionDir)..\obj\SDL\$(Platform)\$(Configuration)\SDL.lib;$(SolutionDir)..\obj\SDLmain\$(Platform)\$(Configuration)\SDLmain.lib;$(SolutionDir)..\obj\SDL_net\$(Platform)\$(Configuration)\SDL_net.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\Debug\freetype.lib + winmm.lib;imm32.lib;opengl32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;Ws2_32.lib;$(SolutionDir)..\obj\SDL\$(Platform)\$(Configuration)\SDL.lib;$(SolutionDir)..\obj\SDLmain\$(Platform)\$(Configuration)\SDLmain.lib;$(SolutionDir)..\obj\SDL_net\$(Platform)\$(Configuration)\SDL_net.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\$(Configuration)\freetype.lib LinkVerboseLib $(OutDir)/$(ProjectName).exe true @@ -416,7 +416,7 @@ copy "$(SolutionDir)\..\CHANGELOG" "$(OutputPath)\changelog.txt" - winmm.lib;imm32.lib;opengl32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;$(SolutionDir)..\obj\SDL2\$(Platform)\$(Configuration)\sdl2.lib;$(SolutionDir)..\obj\SDL2main\$(Platform)\$(Configuration)\sdl2main.lib;Ws2_32.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\Debug\freetype.lib + winmm.lib;imm32.lib;opengl32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;$(SolutionDir)..\obj\SDL2\$(Platform)\$(Configuration)\sdl2.lib;$(SolutionDir)..\obj\SDL2main\$(Platform)\$(Configuration)\sdl2main.lib;Ws2_32.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\$(Configuration)\freetype.lib LinkVerboseLib $(OutDir)/$(ProjectName).exe true @@ -478,7 +478,7 @@ copy "$(SolutionDir)\..\CHANGELOG" "$(OutputPath)\changelog.txt" - winmm.lib;imm32.lib;opengl32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;Ws2_32.lib;$(SolutionDir)..\obj\SDL\$(Platform)\$(Configuration)\SDL.lib;$(SolutionDir)..\obj\SDLmain\$(Platform)\$(Configuration)\SDLmain.lib;$(SolutionDir)..\obj\SDL_net\$(Platform)\$(Configuration)\SDL_net.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\Debug\freetype.lib + winmm.lib;imm32.lib;opengl32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;Ws2_32.lib;$(SolutionDir)..\obj\SDL\$(Platform)\$(Configuration)\SDL.lib;$(SolutionDir)..\obj\SDLmain\$(Platform)\$(Configuration)\SDLmain.lib;$(SolutionDir)..\obj\SDL_net\$(Platform)\$(Configuration)\SDL_net.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\$(Configuration)\freetype.lib LinkVerboseLib $(OutDir)/$(ProjectName).exe true @@ -540,7 +540,7 @@ copy "$(SolutionDir)\..\CHANGELOG" "$(OutputPath)\changelog.txt" /QIfist /utf-8 - winmm.lib;comdlg32.lib;shell32.lib;gdi32.lib;ole32.lib;oleaut32.lib;Advapi32.lib;imm32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;Ws2_32.lib;$(SolutionDir)..\obj\SDL\$(Platform)\$(Configuration)\SDL.lib;$(SolutionDir)..\obj\SDLmain\$(Platform)\$(Configuration)\SDLmain.lib;$(SolutionDir)..\obj\SDL_net\$(Platform)\$(Configuration)\SDL_net.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\Debug\freetype.lib + winmm.lib;comdlg32.lib;shell32.lib;gdi32.lib;ole32.lib;oleaut32.lib;Advapi32.lib;imm32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;Ws2_32.lib;$(SolutionDir)..\obj\SDL\$(Platform)\$(Configuration)\SDL.lib;$(SolutionDir)..\obj\SDLmain\$(Platform)\$(Configuration)\SDLmain.lib;$(SolutionDir)..\obj\SDL_net\$(Platform)\$(Configuration)\SDL_net.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\$(Configuration)\freetype.lib LinkVerboseLib $(OutDir)/$(ProjectName).exe true @@ -601,7 +601,7 @@ copy "$(SolutionDir)\..\CHANGELOG" "$(OutputPath)\changelog.txt" /QIfist /utf-8 - winmm.lib;comdlg32.lib;shell32.lib;gdi32.lib;ole32.lib;oleaut32.lib;Advapi32.lib;imm32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;Ws2_32.lib;$(SolutionDir)..\obj\SDL\$(Platform)\$(Configuration)\SDL.lib;$(SolutionDir)..\obj\SDLmain\$(Platform)\$(Configuration)\SDLmain.lib;$(SolutionDir)..\obj\SDL_net\$(Platform)\$(Configuration)\SDL_net.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\Debug\freetype.lib + winmm.lib;comdlg32.lib;shell32.lib;gdi32.lib;ole32.lib;oleaut32.lib;Advapi32.lib;imm32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;Ws2_32.lib;$(SolutionDir)..\obj\SDL\$(Platform)\$(Configuration)\SDL.lib;$(SolutionDir)..\obj\SDLmain\$(Platform)\$(Configuration)\SDLmain.lib;$(SolutionDir)..\obj\SDL_net\$(Platform)\$(Configuration)\SDL_net.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\$(Configuration)\freetype.lib LinkVerboseLib $(OutDir)/$(ProjectName).exe true @@ -661,7 +661,7 @@ copy "$(SolutionDir)\..\CHANGELOG" "$(OutputPath)\changelog.txt" - winmm.lib;imm32.lib;opengl32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;$(SolutionDir)..\obj\SDL2\$(Platform)\$(Configuration)\sdl2.lib;$(SolutionDir)..\obj\SDL2main\$(Platform)\$(Configuration)\sdl2main.lib;Ws2_32.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\Debug\freetype.lib + winmm.lib;imm32.lib;opengl32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;$(SolutionDir)..\obj\SDL2\$(Platform)\$(Configuration)\sdl2.lib;$(SolutionDir)..\obj\SDL2main\$(Platform)\$(Configuration)\sdl2main.lib;Ws2_32.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\$(Configuration)\freetype.lib LinkVerboseLib $(OutDir)/$(ProjectName).exe true @@ -723,7 +723,7 @@ copy "$(SolutionDir)\..\CHANGELOG" "$(OutputPath)\changelog.txt" - winmm.lib;comdlg32.lib;shell32.lib;gdi32.lib;ole32.lib;oleaut32.lib;Advapi32.lib;imm32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;$(SolutionDir)..\obj\SDL2\$(Platform)\$(Configuration)\sdl2.lib;$(SolutionDir)..\obj\SDL2main\$(Platform)\$(Configuration)\sdl2main.lib;Ws2_32.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\Debug\freetype.lib + winmm.lib;comdlg32.lib;shell32.lib;gdi32.lib;ole32.lib;oleaut32.lib;Advapi32.lib;imm32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;$(SolutionDir)..\obj\SDL2\$(Platform)\$(Configuration)\sdl2.lib;$(SolutionDir)..\obj\SDL2main\$(Platform)\$(Configuration)\sdl2main.lib;Ws2_32.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\$(Configuration)\freetype.lib LinkVerboseLib $(OutDir)/$(ProjectName).exe true @@ -784,7 +784,7 @@ copy "$(SolutionDir)\..\CHANGELOG" "$(OutputPath)\changelog.txt" - winmm.lib;comdlg32.lib;shell32.lib;gdi32.lib;ole32.lib;oleaut32.lib;Advapi32.lib;imm32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;$(SolutionDir)..\obj\SDL2\$(Platform)\$(Configuration)\sdl2.lib;$(SolutionDir)..\obj\SDL2main\$(Platform)\$(Configuration)\sdl2main.lib;Ws2_32.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\Debug\freetype.lib + winmm.lib;comdlg32.lib;shell32.lib;gdi32.lib;ole32.lib;oleaut32.lib;Advapi32.lib;imm32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;$(SolutionDir)..\obj\SDL2\$(Platform)\$(Configuration)\sdl2.lib;$(SolutionDir)..\obj\SDL2main\$(Platform)\$(Configuration)\sdl2main.lib;Ws2_32.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\$(Configuration)\freetype.lib LinkVerboseLib $(OutDir)/$(ProjectName).exe true @@ -839,7 +839,7 @@ copy "$(SolutionDir)\..\CHANGELOG" "$(OutputPath)\changelog.txt" - winmm.lib;imm32.lib;opengl32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;Ws2_32.lib;$(SolutionDir)..\obj\SDL\$(Platform)\$(Configuration)\SDL.lib;$(SolutionDir)..\obj\SDLmain\$(Platform)\$(Configuration)\SDLmain.lib;$(SolutionDir)..\obj\SDL_net\$(Platform)\$(Configuration)\SDL_net.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\Release\freetype.lib + winmm.lib;imm32.lib;opengl32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;Ws2_32.lib;$(SolutionDir)..\obj\SDL\$(Platform)\$(Configuration)\SDL.lib;$(SolutionDir)..\obj\SDLmain\$(Platform)\$(Configuration)\SDLmain.lib;$(SolutionDir)..\obj\SDL_net\$(Platform)\$(Configuration)\SDL_net.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\$(Configuration)\freetype.lib LinkVerboseLib $(OutDir)/$(ProjectName).exe true @@ -901,7 +901,7 @@ copy "$(SolutionDir)\..\CHANGELOG" "$(OutputPath)\changelog.txt" false - winmm.lib;imm32.lib;opengl32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;$(SolutionDir)..\obj\SDL2\$(Platform)\$(Configuration)\sdl2.lib;$(SolutionDir)..\obj\SDL2main\$(Platform)\$(Configuration)\sdl2main.lib;Ws2_32.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\Release\freetype.lib + winmm.lib;imm32.lib;opengl32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;$(SolutionDir)..\obj\SDL2\$(Platform)\$(Configuration)\sdl2.lib;$(SolutionDir)..\obj\SDL2main\$(Platform)\$(Configuration)\sdl2main.lib;Ws2_32.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\$(Configuration)\freetype.lib LinkVerboseLib $(OutDir)/$(ProjectName).exe true @@ -965,7 +965,7 @@ copy "$(SolutionDir)\..\CHANGELOG" "$(OutputPath)\changelog.txt" - winmm.lib;imm32.lib;opengl32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;Ws2_32.lib;$(SolutionDir)..\obj\SDL\$(Platform)\$(Configuration)\SDL.lib;$(SolutionDir)..\obj\SDLmain\$(Platform)\$(Configuration)\SDLmain.lib;$(SolutionDir)..\obj\SDL_net\$(Platform)\$(Configuration)\SDL_net.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\Release\freetype.lib + winmm.lib;imm32.lib;opengl32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;Ws2_32.lib;$(SolutionDir)..\obj\SDL\$(Platform)\$(Configuration)\SDL.lib;$(SolutionDir)..\obj\SDLmain\$(Platform)\$(Configuration)\SDLmain.lib;$(SolutionDir)..\obj\SDL_net\$(Platform)\$(Configuration)\SDL_net.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\$(Configuration)\freetype.lib LinkVerboseLib $(OutDir)/$(ProjectName).exe true @@ -1029,7 +1029,7 @@ copy "$(SolutionDir)\..\CHANGELOG" "$(OutputPath)\changelog.txt" /QIfist /utf-8 - winmm.lib;comdlg32.lib;shell32.lib;gdi32.lib;ole32.lib;oleaut32.lib;Advapi32.lib;imm32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;Ws2_32.lib;$(SolutionDir)..\obj\SDL\$(Platform)\$(Configuration)\SDL.lib;$(SolutionDir)..\obj\SDLmain\$(Platform)\$(Configuration)\SDLmain.lib;$(SolutionDir)..\obj\SDL_net\$(Platform)\$(Configuration)\SDL_net.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\Release\freetype.lib + winmm.lib;comdlg32.lib;shell32.lib;gdi32.lib;ole32.lib;oleaut32.lib;Advapi32.lib;imm32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;Ws2_32.lib;$(SolutionDir)..\obj\SDL\$(Platform)\$(Configuration)\SDL.lib;$(SolutionDir)..\obj\SDLmain\$(Platform)\$(Configuration)\SDLmain.lib;$(SolutionDir)..\obj\SDL_net\$(Platform)\$(Configuration)\SDL_net.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\$(Configuration)\freetype.lib LinkVerboseLib $(OutDir)/$(ProjectName).exe true @@ -1092,7 +1092,7 @@ copy "$(SolutionDir)\..\CHANGELOG" "$(OutputPath)\changelog.txt" /QIfist /utf-8 - winmm.lib;comdlg32.lib;shell32.lib;gdi32.lib;ole32.lib;oleaut32.lib;Advapi32.lib;imm32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;Ws2_32.lib;$(SolutionDir)..\obj\SDL\$(Platform)\$(Configuration)\SDL.lib;$(SolutionDir)..\obj\SDLmain\$(Platform)\$(Configuration)\SDLmain.lib;$(SolutionDir)..\obj\SDL_net\$(Platform)\$(Configuration)\SDL_net.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\Release\freetype.lib + winmm.lib;comdlg32.lib;shell32.lib;gdi32.lib;ole32.lib;oleaut32.lib;Advapi32.lib;imm32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;Ws2_32.lib;$(SolutionDir)..\obj\SDL\$(Platform)\$(Configuration)\SDL.lib;$(SolutionDir)..\obj\SDLmain\$(Platform)\$(Configuration)\SDLmain.lib;$(SolutionDir)..\obj\SDL_net\$(Platform)\$(Configuration)\SDL_net.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\$(Configuration)\freetype.lib LinkVerboseLib $(OutDir)/$(ProjectName).exe true @@ -1155,7 +1155,7 @@ copy "$(SolutionDir)\..\CHANGELOG" "$(OutputPath)\changelog.txt" false - winmm.lib;imm32.lib;opengl32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;$(SolutionDir)..\obj\SDL2\$(Platform)\$(Configuration)\sdl2.lib;$(SolutionDir)..\obj\SDL2main\$(Platform)\$(Configuration)\sdl2main.lib;Ws2_32.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\Release\freetype.lib + winmm.lib;imm32.lib;opengl32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;$(SolutionDir)..\obj\SDL2\$(Platform)\$(Configuration)\sdl2.lib;$(SolutionDir)..\obj\SDL2main\$(Platform)\$(Configuration)\sdl2main.lib;Ws2_32.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\$(Configuration)\freetype.lib LinkVerboseLib $(OutDir)/$(ProjectName).exe true @@ -1220,7 +1220,7 @@ copy "$(SolutionDir)\..\CHANGELOG" "$(OutputPath)\changelog.txt" false - winmm.lib;comdlg32.lib;shell32.lib;gdi32.lib;ole32.lib;oleaut32.lib;Advapi32.lib;imm32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;$(SolutionDir)..\obj\SDL2\$(Platform)\$(Configuration)\sdl2.lib;$(SolutionDir)..\obj\SDL2main\$(Platform)\$(Configuration)\sdl2main.lib;Ws2_32.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\Release\freetype.lib + winmm.lib;comdlg32.lib;shell32.lib;gdi32.lib;ole32.lib;oleaut32.lib;Advapi32.lib;imm32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;$(SolutionDir)..\obj\SDL2\$(Platform)\$(Configuration)\sdl2.lib;$(SolutionDir)..\obj\SDL2main\$(Platform)\$(Configuration)\sdl2main.lib;Ws2_32.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\$(Configuration)\freetype.lib LinkVerboseLib $(OutDir)/$(ProjectName).exe true @@ -1284,7 +1284,7 @@ copy "$(SolutionDir)\..\CHANGELOG" "$(OutputPath)\changelog.txt" false - winmm.lib;comdlg32.lib;shell32.lib;gdi32.lib;ole32.lib;oleaut32.lib;Advapi32.lib;imm32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;$(SolutionDir)..\obj\SDL2\$(Platform)\$(Configuration)\sdl2.lib;$(SolutionDir)..\obj\SDL2main\$(Platform)\$(Configuration)\sdl2main.lib;Ws2_32.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\Release\freetype.lib + winmm.lib;comdlg32.lib;shell32.lib;gdi32.lib;ole32.lib;oleaut32.lib;Advapi32.lib;imm32.lib;dxguid.lib;SetupAPI.lib;version.lib;Iphlpapi.lib;$(SolutionDir)..\obj\libpdcurses\$(Platform)\$(Configuration)\libpdcurses.lib;$(SolutionDir)..\obj\SDL2\$(Platform)\$(Configuration)\sdl2.lib;$(SolutionDir)..\obj\SDL2main\$(Platform)\$(Configuration)\sdl2main.lib;Ws2_32.lib;%(AdditionalDependencies);$(SolutionDir)..\obj\zlib\$(Platform)\$(Configuration)\zlib.lib;$(SolutionDir)..\obj\libpng\$(Platform)\$(Configuration)\libpng.lib;$(SolutionDir)..\obj\freetype\$(Platform)\$(Configuration)\freetype.lib LinkVerboseLib $(OutDir)/$(ProjectName).exe true diff --git a/vs2015/freetype/builds/windows/vc2010/freetype.vcxproj b/vs2015/freetype/builds/windows/vc2010/freetype.vcxproj index 285d13a46..dfa5f1400 100644 --- a/vs2015/freetype/builds/windows/vc2010/freetype.vcxproj +++ b/vs2015/freetype/builds/windows/vc2010/freetype.vcxproj @@ -1,923 +1,923 @@ - - - - - Debug SDL2 - ARM - - - Debug SDL2 - ARM64 - - - Debug SDL2 - Win32 - - - Debug SDL2 - x64 - - - Debug - ARM - - - Debug - ARM64 - - - Debug - Win32 - - - Debug - x64 - - - Release SDL2 - ARM - - - Release SDL2 - ARM64 - - - Release SDL2 - Win32 - - - Release SDL2 - x64 - - - Release - ARM - - - Release - ARM64 - - - Release - Win32 - - - Release - x64 - - - - - - 4.0 - - - - v100 - - - - v120 - - - - v140 - - - - v141 - - - - {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B} - FreeType - 10.0.17134.0 - - - - StaticLibrary - false - MultiByte - - - StaticLibrary - false - MultiByte - - - StaticLibrary - false - MultiByte - - - StaticLibrary - false - MultiByte - - - StaticLibrary - false - MultiByte - - - StaticLibrary - false - MultiByte - - - StaticLibrary - false - MultiByte - true - - - StaticLibrary - false - MultiByte - true - - - StaticLibrary - false - MultiByte - - - StaticLibrary - false - MultiByte - - - StaticLibrary - false - MultiByte - - - StaticLibrary - false - MultiByte - - - StaticLibrary - false - MultiByte - - - StaticLibrary - false - MultiByte - - - StaticLibrary - false - MultiByte - true - - - StaticLibrary - false - MultiByte - true - - - - - - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - AllRules.ruleset - - - $(ProjectName) - - - - - Disabled - $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - true - Level4 - ProgramDatabase - Default - 4001 - - - - - $(OutDir)$(TargetName).pdb - Disabled - - - _DEBUG;$(UserDefines);%(PreprocessorDefinitions) - 0x0409 - - - true - MachineX86 - - - - - true - $(IntDir)$(TargetName)$(TargetExt) - - - - - Disabled - $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - true - Level4 - ProgramDatabase - Default - 4001 - - - - - $(OutDir)$(TargetName).pdb - Disabled - - - _DEBUG;$(UserDefines);%(PreprocessorDefinitions) - 0x0409 - - - true - MachineX86 - - - - - true - $(IntDir)$(TargetName)$(TargetExt) - - - - - Disabled - $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - false - Level4 - ProgramDatabase - Default - $(DisableSpecificWarnings) - - - - - $(OutDir)$(TargetName).pdb - Disabled - - - _DEBUG;$(UserDefines);%(PreprocessorDefinitions) - 0x0409 - - - true - MachineX64 - - - - - - - $(IntDir)$(TargetName)$(TargetExt) - - - - - Disabled - $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - false - Level4 - ProgramDatabase - Default - $(DisableSpecificWarnings) - - - - - $(OutDir)$(TargetName).pdb - Disabled - - - _DEBUG;$(UserDefines);%(PreprocessorDefinitions) - 0x0409 - - - true - MachineX64 - - - - - - - $(IntDir)$(TargetName)$(TargetExt) - - - - - Disabled - $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - false - Level4 - ProgramDatabase - Default - $(DisableSpecificWarnings) - - - - - $(OutDir)$(TargetName).pdb - Disabled - - - _DEBUG;$(UserDefines);%(PreprocessorDefinitions) - 0x0409 - - - true - MachineARM64 - - - - - - - $(IntDir)$(TargetName)$(TargetExt) - - - - - Disabled - $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - false - Level4 - ProgramDatabase - Default - $(DisableSpecificWarnings) - - - - - $(OutDir)$(TargetName).pdb - Disabled - - - _DEBUG;$(UserDefines);%(PreprocessorDefinitions) - 0x0409 - - - true - MachineARM64 - - - - - - - $(IntDir)$(TargetName)$(TargetExt) - - - - - Disabled - $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - false - Level4 - ProgramDatabase - Default - $(DisableSpecificWarnings) - - - - - $(OutDir)$(TargetName).pdb - Disabled - - - _DEBUG;$(UserDefines);%(PreprocessorDefinitions) - 0x0409 - - - true - MachineARM - - - - - - - $(IntDir)$(TargetName)$(TargetExt) - - - - - Disabled - $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - false - Level4 - ProgramDatabase - Default - $(DisableSpecificWarnings) - - - - - $(OutDir)$(TargetName).pdb - Disabled - - - _DEBUG;$(UserDefines);%(PreprocessorDefinitions) - 0x0409 - - - true - MachineARM - - - - - - - $(IntDir)$(TargetName)$(TargetExt) - - - - - MaxSpeed - AnySuitable - $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) - true - MultiThreaded - true - true - Level4 - Default - 4001 - - - false - StreamingSIMDExtensions - - - - - false - - - - - true - - - true - Neither - true - false - false - - - NDEBUG;$(UserDefines);%(PreprocessorDefinitions) - 0x0409 - - - true - true - MachineX86 - $(UserLibraryDirectories);%(AdditionalLibraryDirectories) - $(UserDependencies);%(AdditionalDependencies) - true - $(IntDir)$(TargetName)$(TargetExt) - - - - - MaxSpeed - AnySuitable - $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) - true - MultiThreaded - true - true - Level4 - Default - 4001 - - - false - StreamingSIMDExtensions - - - - - false - - - - - true - - - true - Neither - true - false - false - - - NDEBUG;$(UserDefines);%(PreprocessorDefinitions) - 0x0409 - - - true - true - MachineX86 - $(UserLibraryDirectories);%(AdditionalLibraryDirectories) - $(UserDependencies);%(AdditionalDependencies) - true - $(IntDir)$(TargetName)$(TargetExt) - - - - - MaxSpeed - AnySuitable - $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) - true - MultiThreaded - true - true - Level4 - Default - 4001 - - - false - - - - - - - false - - - - - true - - - true - Neither - true - false - false - - - NDEBUG;$(UserDefines);%(PreprocessorDefinitions) - 0x0409 - - - true - true - MachineX64 - $(UserLibraryDirectories);%(AdditionalLibraryDirectories) - $(UserDependencies);%(AdditionalDependencies) - true - $(IntDir)$(TargetName)$(TargetExt) - - - - - MaxSpeed - AnySuitable - $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) - true - MultiThreaded - true - true - Level4 - Default - 4001 - - - false - - - - - - - false - - - - - true - - - true - Neither - true - false - false - - - NDEBUG;$(UserDefines);%(PreprocessorDefinitions) - 0x0409 - - - true - true - MachineX64 - $(UserLibraryDirectories);%(AdditionalLibraryDirectories) - $(UserDependencies);%(AdditionalDependencies) - true - $(IntDir)$(TargetName)$(TargetExt) - - - - - MaxSpeed - AnySuitable - $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) - true - MultiThreaded - true - true - Level4 - Default - 4001 - - - false - - - - - - - false - - - - - true - - - true - Neither - true - false - false - - - NDEBUG;$(UserDefines);%(PreprocessorDefinitions) - 0x0409 - - - true - true - MachineARM64 - $(UserLibraryDirectories);%(AdditionalLibraryDirectories) - $(UserDependencies);%(AdditionalDependencies) - true - $(IntDir)$(TargetName)$(TargetExt) - - - - - MaxSpeed - AnySuitable - $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) - true - MultiThreaded - true - true - Level4 - Default - 4001 - - - false - - - - - - - false - - - - - true - - - true - Neither - true - false - false - - - NDEBUG;$(UserDefines);%(PreprocessorDefinitions) - 0x0409 - - - true - true - MachineARM64 - $(UserLibraryDirectories);%(AdditionalLibraryDirectories) - $(UserDependencies);%(AdditionalDependencies) - true - $(IntDir)$(TargetName)$(TargetExt) - - - - - MaxSpeed - AnySuitable - $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) - true - MultiThreaded - true - true - Level4 - Default - 4001 - - - false - - - - - - - false - - - - - true - - - true - Neither - true - false - false - - - NDEBUG;$(UserDefines);%(PreprocessorDefinitions) - 0x0409 - - - true - true - MachineARM - $(UserLibraryDirectories);%(AdditionalLibraryDirectories) - $(UserDependencies);%(AdditionalDependencies) - true - $(IntDir)$(TargetName)$(TargetExt) - - - - - MaxSpeed - AnySuitable - $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) - true - MultiThreaded - true - true - Level4 - Default - 4001 - - - false - - - - - - - false - - - - - true - - - true - Neither - true - false - false - - - NDEBUG;$(UserDefines);%(PreprocessorDefinitions) - 0x0409 - - - true - true - MachineARM - $(UserLibraryDirectories);%(AdditionalLibraryDirectories) - $(UserDependencies);%(AdditionalDependencies) - true - $(IntDir)$(TargetName)$(TargetExt) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - - - - - - - - - + + + + + Debug SDL2 + ARM + + + Debug SDL2 + ARM64 + + + Debug SDL2 + Win32 + + + Debug SDL2 + x64 + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release SDL2 + ARM + + + Release SDL2 + ARM64 + + + Release SDL2 + Win32 + + + Release SDL2 + x64 + + + Release + ARM + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + + + 4.0 + + + + v100 + + + + v120 + + + + v140 + + + + v141 + + + + {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B} + FreeType + 10.0.17134.0 + + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + true + + + StaticLibrary + false + MultiByte + true + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + true + + + StaticLibrary + false + MultiByte + true + + + + + + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + AllRules.ruleset + + + $(ProjectName) + + + + + Disabled + $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + true + Level4 + ProgramDatabase + Default + 4001 + + + + + $(OutDir)$(TargetName).pdb + Disabled + + + _DEBUG;$(UserDefines);%(PreprocessorDefinitions) + 0x0409 + + + true + MachineX86 + + + + + true + $(IntDir)$(TargetName)$(TargetExt) + + + + + Disabled + $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + true + Level4 + ProgramDatabase + Default + 4001 + + + + + $(OutDir)$(TargetName).pdb + Disabled + + + _DEBUG;$(UserDefines);%(PreprocessorDefinitions) + 0x0409 + + + true + MachineX86 + + + + + true + $(IntDir)$(TargetName)$(TargetExt) + + + + + Disabled + $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + false + Level4 + ProgramDatabase + Default + $(DisableSpecificWarnings) + + + + + $(OutDir)$(TargetName).pdb + Disabled + + + _DEBUG;$(UserDefines);%(PreprocessorDefinitions) + 0x0409 + + + true + MachineX64 + + + + + + + $(IntDir)$(TargetName)$(TargetExt) + + + + + Disabled + $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + false + Level4 + ProgramDatabase + Default + $(DisableSpecificWarnings) + + + + + $(OutDir)$(TargetName).pdb + Disabled + + + _DEBUG;$(UserDefines);%(PreprocessorDefinitions) + 0x0409 + + + true + MachineX64 + + + + + + + $(IntDir)$(TargetName)$(TargetExt) + + + + + Disabled + $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + false + Level4 + ProgramDatabase + Default + $(DisableSpecificWarnings) + + + + + $(OutDir)$(TargetName).pdb + Disabled + + + _DEBUG;$(UserDefines);%(PreprocessorDefinitions) + 0x0409 + + + true + MachineARM64 + + + + + + + $(IntDir)$(TargetName)$(TargetExt) + + + + + Disabled + $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + false + Level4 + ProgramDatabase + Default + $(DisableSpecificWarnings) + + + + + $(OutDir)$(TargetName).pdb + Disabled + + + _DEBUG;$(UserDefines);%(PreprocessorDefinitions) + 0x0409 + + + true + MachineARM64 + + + + + + + $(IntDir)$(TargetName)$(TargetExt) + + + + + Disabled + $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + false + Level4 + ProgramDatabase + Default + $(DisableSpecificWarnings) + + + + + $(OutDir)$(TargetName).pdb + Disabled + + + _DEBUG;$(UserDefines);%(PreprocessorDefinitions) + 0x0409 + + + true + MachineARM + + + + + + + $(IntDir)$(TargetName)$(TargetExt) + + + + + Disabled + $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + false + Level4 + ProgramDatabase + Default + $(DisableSpecificWarnings) + + + + + $(OutDir)$(TargetName).pdb + Disabled + + + _DEBUG;$(UserDefines);%(PreprocessorDefinitions) + 0x0409 + + + true + MachineARM + + + + + + + $(IntDir)$(TargetName)$(TargetExt) + + + + + MaxSpeed + AnySuitable + $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) + true + MultiThreaded + true + true + Level4 + Default + 4001 + + + false + StreamingSIMDExtensions + + + + + false + + + + + true + + + true + Neither + true + false + false + + + NDEBUG;$(UserDefines);%(PreprocessorDefinitions) + 0x0409 + + + true + true + MachineX86 + $(UserLibraryDirectories);%(AdditionalLibraryDirectories) + $(UserDependencies);%(AdditionalDependencies) + true + $(IntDir)$(TargetName)$(TargetExt) + + + + + MaxSpeed + AnySuitable + $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) + true + MultiThreaded + true + true + Level4 + Default + 4001 + + + false + StreamingSIMDExtensions + + + + + false + + + + + true + + + true + Neither + true + false + false + + + NDEBUG;$(UserDefines);%(PreprocessorDefinitions) + 0x0409 + + + true + true + MachineX86 + $(UserLibraryDirectories);%(AdditionalLibraryDirectories) + $(UserDependencies);%(AdditionalDependencies) + true + $(IntDir)$(TargetName)$(TargetExt) + + + + + MaxSpeed + AnySuitable + $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) + true + MultiThreaded + true + true + Level4 + Default + 4001 + + + false + + + + + + + false + + + + + true + + + true + Neither + true + false + false + + + NDEBUG;$(UserDefines);%(PreprocessorDefinitions) + 0x0409 + + + true + true + MachineX64 + $(UserLibraryDirectories);%(AdditionalLibraryDirectories) + $(UserDependencies);%(AdditionalDependencies) + true + $(IntDir)$(TargetName)$(TargetExt) + + + + + MaxSpeed + AnySuitable + $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) + true + MultiThreaded + true + true + Level4 + Default + 4001 + + + false + + + + + + + false + + + + + true + + + true + Neither + true + false + false + + + NDEBUG;$(UserDefines);%(PreprocessorDefinitions) + 0x0409 + + + true + true + MachineX64 + $(UserLibraryDirectories);%(AdditionalLibraryDirectories) + $(UserDependencies);%(AdditionalDependencies) + true + $(IntDir)$(TargetName)$(TargetExt) + + + + + MaxSpeed + AnySuitable + $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) + true + MultiThreaded + true + true + Level4 + Default + 4001 + + + false + + + + + + + false + + + + + true + + + true + Neither + true + false + false + + + NDEBUG;$(UserDefines);%(PreprocessorDefinitions) + 0x0409 + + + true + true + MachineARM64 + $(UserLibraryDirectories);%(AdditionalLibraryDirectories) + $(UserDependencies);%(AdditionalDependencies) + true + $(IntDir)$(TargetName)$(TargetExt) + + + + + MaxSpeed + AnySuitable + $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) + true + MultiThreaded + true + true + Level4 + Default + 4001 + + + false + + + + + + + false + + + + + true + + + true + Neither + true + false + false + + + NDEBUG;$(UserDefines);%(PreprocessorDefinitions) + 0x0409 + + + true + true + MachineARM64 + $(UserLibraryDirectories);%(AdditionalLibraryDirectories) + $(UserDependencies);%(AdditionalDependencies) + true + $(IntDir)$(TargetName)$(TargetExt) + + + + + MaxSpeed + AnySuitable + $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) + true + MultiThreaded + true + true + Level4 + Default + 4001 + + + false + + + + + + + false + + + + + true + + + true + Neither + true + false + false + + + NDEBUG;$(UserDefines);%(PreprocessorDefinitions) + 0x0409 + + + true + true + MachineARM + $(UserLibraryDirectories);%(AdditionalLibraryDirectories) + $(UserDependencies);%(AdditionalDependencies) + true + $(IntDir)$(TargetName)$(TargetExt) + + + + + MaxSpeed + AnySuitable + $(UserOptionDirectory);..\..\..\include;$(UserIncludeDirectories);%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FT2_BUILD_LIBRARY;$(UserDefines);%(PreprocessorDefinitions) + true + MultiThreaded + true + true + Level4 + Default + 4001 + + + false + + + + + + + false + + + + + true + + + true + Neither + true + false + false + + + NDEBUG;$(UserDefines);%(PreprocessorDefinitions) + 0x0409 + + + true + true + MachineARM + $(UserLibraryDirectories);%(AdditionalLibraryDirectories) + $(UserDependencies);%(AdditionalDependencies) + true + $(IntDir)$(TargetName)$(TargetExt) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + \ No newline at end of file diff --git a/vs2015/sdl2/VisualC/SDL/SDL.vcxproj b/vs2015/sdl2/VisualC/SDL/SDL.vcxproj index c30adccdc..7dd192923 100644 --- a/vs2015/sdl2/VisualC/SDL/SDL.vcxproj +++ b/vs2015/sdl2/VisualC/SDL/SDL.vcxproj @@ -1,839 +1,839 @@ - - - - - Debug SDL2 - ARM - - - Debug SDL2 - ARM64 - - - Debug SDL2 - Win32 - - - Debug SDL2 - x64 - - - Release SDL2 - ARM - - - Release SDL2 - ARM64 - - - Release SDL2 - Win32 - - - Release SDL2 - x64 - - - - SDL2 - {CB7E474E-04D9-42D2-8159-40A56C2939BE} - SDL - 10.0.17134.0 - - - - StaticLibrary - MultiByte - v141 - - - StaticLibrary - MultiByte - v141 - - - StaticLibrary - MultiByte - v141 - - - StaticLibrary - MultiByte - v141 - - - StaticLibrary - MultiByte - v141 - true - - - StaticLibrary - MultiByte - v141 - - - StaticLibrary - MultiByte - v141 - - - StaticLibrary - MultiByte - v141 - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.40219.1 - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - AllRules.ruleset - - - AllRules.ruleset - AllRules.ruleset - AllRules.ruleset - - - - - - - AllRules.ruleset - - - AllRules.ruleset - AllRules.ruleset - AllRules.ruleset - - - - - - - C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Lib\x86;$(LibraryPath) - - - $(VC_IncludePath);$(WindowsSDK_IncludePath); - - - $(VC_IncludePath);$(WindowsSDK_IncludePath); - - - $(VC_IncludePath);$(WindowsSDK_IncludePath); - - - $(VC_IncludePath);$(WindowsSDK_IncludePath); - - - $(VC_IncludePath);$(WindowsSDK_IncludePath); - - - $(VC_IncludePath);$(WindowsSDK_IncludePath); - - - $(VC_IncludePath);$(WindowsSDK_IncludePath); - - - $(VC_IncludePath);$(WindowsSDK_IncludePath); - - - - - - - - _DEBUG;%(PreprocessorDefinitions) - true - true - Win32 - .\Debug/SDL.tlb - - - Disabled - $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) - %(AdditionalUsingDirectories) - _DEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreadedDebug - false - StreamingSIMDExtensions - Level3 - ProgramDatabase - true - Default - true - EnableFastChecks - - - _DEBUG;%(PreprocessorDefinitions) - - - setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) - true - true - Windows - - - MachineX86 - $(IntDir)$(TargetName)$(TargetExt) - - - - - _DEBUG;%(PreprocessorDefinitions) - true - true - X64 - .\Debug/SDL.tlb - - - Disabled - $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) - %(AdditionalUsingDirectories) - _DEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreadedDebug - false - StreamingSIMDExtensions - Level3 - ProgramDatabase - true - Default - true - false - EnableFastChecks - - - _DEBUG;%(PreprocessorDefinitions) - - - setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) - true - true - Windows - - - $(IntDir)$(TargetName)$(TargetExt) - - - - - _DEBUG;%(PreprocessorDefinitions) - true - true - .\Debug/SDL.tlb - - - Disabled - $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) - %(AdditionalUsingDirectories) - _DEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreadedDebug - false - StreamingSIMDExtensions - Level3 - ProgramDatabase - true - Default - true - false - EnableFastChecks - - - _DEBUG;%(PreprocessorDefinitions) - - - setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) - true - true - Windows - - - $(IntDir)$(TargetName)$(TargetExt) - - - - - _DEBUG;%(PreprocessorDefinitions) - true - true - .\Debug/SDL.tlb - - - Disabled - $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) - %(AdditionalUsingDirectories) - _DEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreadedDebug - false - StreamingSIMDExtensions - Level3 - ProgramDatabase - true - Default - true - false - EnableFastChecks - - - _DEBUG;%(PreprocessorDefinitions) - - - setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) - true - true - Windows - - - $(IntDir)$(TargetName)$(TargetExt) - - - - - - - - - NDEBUG;%(PreprocessorDefinitions) - true - true - Win32 - .\Release/SDL.tlb - - - $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) - %(AdditionalUsingDirectories) - NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreaded - false - StreamingSIMDExtensions - Level3 - ProgramDatabase - true - Default - true - Speed - true - true - true - false - true - - - NDEBUG;%(PreprocessorDefinitions) - - - setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) - true - true - Windows - true - true - - - MachineX86 - $(IntDir)$(TargetName)$(TargetExt) - - - - - NDEBUG;%(PreprocessorDefinitions) - true - true - X64 - .\Release/SDL.tlb - - - $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) - %(AdditionalUsingDirectories) - NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreaded - false - - - Level3 - ProgramDatabase - true - Default - true - Speed - true - true - true - false - true - - - NDEBUG;%(PreprocessorDefinitions) - - - setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) - true - true - Windows - true - true - - - $(IntDir)$(TargetName)$(TargetExt) - - - - - NDEBUG;%(PreprocessorDefinitions) - true - true - .\Release/SDL.tlb - - - $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) - %(AdditionalUsingDirectories) - NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreaded - false - - - Level3 - ProgramDatabase - true - Default - true - Speed - true - true - true - false - true - - - NDEBUG;%(PreprocessorDefinitions) - - - setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) - true - true - Windows - true - true - - - $(IntDir)$(TargetName)$(TargetExt) - - - - - NDEBUG;%(PreprocessorDefinitions) - true - true - .\Release/SDL.tlb - - - $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) - %(AdditionalUsingDirectories) - NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreaded - false - - - Level3 - ProgramDatabase - true - Default - true - Speed - true - true - true - false - true - - - NDEBUG;%(PreprocessorDefinitions) - - - setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) - true - true - Windows - true - true - - - $(IntDir)$(TargetName)$(TargetExt) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug SDL2 + ARM + + + Debug SDL2 + ARM64 + + + Debug SDL2 + Win32 + + + Debug SDL2 + x64 + + + Release SDL2 + ARM + + + Release SDL2 + ARM64 + + + Release SDL2 + Win32 + + + Release SDL2 + x64 + + + + SDL2 + {CB7E474E-04D9-42D2-8159-40A56C2939BE} + SDL + 10.0.17134.0 + + + + StaticLibrary + MultiByte + v141 + + + StaticLibrary + MultiByte + v141 + + + StaticLibrary + MultiByte + v141 + + + StaticLibrary + MultiByte + v141 + + + StaticLibrary + MultiByte + v141 + true + + + StaticLibrary + MultiByte + v141 + + + StaticLibrary + MultiByte + v141 + + + StaticLibrary + MultiByte + v141 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + AllRules.ruleset + + + AllRules.ruleset + AllRules.ruleset + AllRules.ruleset + + + + + + + AllRules.ruleset + + + AllRules.ruleset + AllRules.ruleset + AllRules.ruleset + + + + + + + C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Lib\x86;$(LibraryPath) + + + $(VC_IncludePath);$(WindowsSDK_IncludePath); + + + $(VC_IncludePath);$(WindowsSDK_IncludePath); + + + $(VC_IncludePath);$(WindowsSDK_IncludePath); + + + $(VC_IncludePath);$(WindowsSDK_IncludePath); + + + $(VC_IncludePath);$(WindowsSDK_IncludePath); + + + $(VC_IncludePath);$(WindowsSDK_IncludePath); + + + $(VC_IncludePath);$(WindowsSDK_IncludePath); + + + $(VC_IncludePath);$(WindowsSDK_IncludePath); + + + + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug/SDL.tlb + + + Disabled + $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDebug + false + StreamingSIMDExtensions + Level3 + ProgramDatabase + true + Default + true + EnableFastChecks + + + _DEBUG;%(PreprocessorDefinitions) + + + setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) + true + true + Windows + + + MachineX86 + $(IntDir)$(TargetName)$(TargetExt) + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + X64 + .\Debug/SDL.tlb + + + Disabled + $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDebug + false + StreamingSIMDExtensions + Level3 + ProgramDatabase + true + Default + true + false + EnableFastChecks + + + _DEBUG;%(PreprocessorDefinitions) + + + setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) + true + true + Windows + + + $(IntDir)$(TargetName)$(TargetExt) + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + .\Debug/SDL.tlb + + + Disabled + $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDebug + false + StreamingSIMDExtensions + Level3 + ProgramDatabase + true + Default + true + false + EnableFastChecks + + + _DEBUG;%(PreprocessorDefinitions) + + + setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) + true + true + Windows + + + $(IntDir)$(TargetName)$(TargetExt) + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + .\Debug/SDL.tlb + + + Disabled + $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDebug + false + StreamingSIMDExtensions + Level3 + ProgramDatabase + true + Default + true + false + EnableFastChecks + + + _DEBUG;%(PreprocessorDefinitions) + + + setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) + true + true + Windows + + + $(IntDir)$(TargetName)$(TargetExt) + + + + + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/SDL.tlb + + + $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreaded + false + StreamingSIMDExtensions + Level3 + ProgramDatabase + true + Default + true + Speed + true + true + true + false + true + + + NDEBUG;%(PreprocessorDefinitions) + + + setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) + true + true + Windows + true + true + + + MachineX86 + $(IntDir)$(TargetName)$(TargetExt) + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + X64 + .\Release/SDL.tlb + + + $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreaded + false + + + Level3 + ProgramDatabase + true + Default + true + Speed + true + true + true + false + true + + + NDEBUG;%(PreprocessorDefinitions) + + + setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) + true + true + Windows + true + true + + + $(IntDir)$(TargetName)$(TargetExt) + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + .\Release/SDL.tlb + + + $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreaded + false + + + Level3 + ProgramDatabase + true + Default + true + Speed + true + true + true + false + true + + + NDEBUG;%(PreprocessorDefinitions) + + + setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) + true + true + Windows + true + true + + + $(IntDir)$(TargetName)$(TargetExt) + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + .\Release/SDL.tlb + + + $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreaded + false + + + Level3 + ProgramDatabase + true + Default + true + Speed + true + true + true + false + true + + + NDEBUG;%(PreprocessorDefinitions) + + + setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) + true + true + Windows + true + true + + + $(IntDir)$(TargetName)$(TargetExt) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vs2015/sdl2/VisualC/SDLmain/SDLmain.vcxproj b/vs2015/sdl2/VisualC/SDLmain/SDLmain.vcxproj index 5b5d531ca..219e22ecc 100644 --- a/vs2015/sdl2/VisualC/SDLmain/SDLmain.vcxproj +++ b/vs2015/sdl2/VisualC/SDLmain/SDLmain.vcxproj @@ -1,365 +1,365 @@ - - - - - Debug SDL2 - ARM - - - Debug SDL2 - ARM64 - - - Debug SDL2 - Win32 - - - Debug SDL2 - x64 - - - Release SDL2 - ARM - - - Release SDL2 - ARM64 - - - Release SDL2 - Win32 - - - Release SDL2 - x64 - - - - SDL2main - {47281679-339B-4D01-8556-A49384BFE812} - SDLmain - 10.0.17134.0 - - - - StaticLibrary - MultiByte - v141 - - - StaticLibrary - MultiByte - v141 - - - StaticLibrary - MultiByte - v141 - - - StaticLibrary - MultiByte - v141 - - - StaticLibrary - MultiByte - v141 - true - - - StaticLibrary - MultiByte - v141 - - - StaticLibrary - MultiByte - v141 - - - StaticLibrary - MultiByte - v141 - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.40219.1 - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ - AllRules.ruleset - - - AllRules.ruleset - AllRules.ruleset - AllRules.ruleset - - - - - - - AllRules.ruleset - - - AllRules.ruleset - AllRules.ruleset - AllRules.ruleset - - - - - - - - - - - - - - $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) - %(AdditionalUsingDirectories) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - MultiThreaded - false - StreamingSIMDExtensions - Level3 - ProgramDatabase - true - true - Speed - true - true - true - false - - - MachineX86 - $(IntDir)$(TargetName)$(TargetExt) - - - - - X64 - - - $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) - %(AdditionalUsingDirectories) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - MultiThreaded - false - - - Level3 - ProgramDatabase - true - true - Speed - true - true - true - false - - - $(IntDir)$(TargetName)$(TargetExt) - - - - - - $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) - %(AdditionalUsingDirectories) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - MultiThreaded - false - - - Level3 - ProgramDatabase - true - true - Speed - true - true - true - false - - - $(IntDir)$(TargetName)$(TargetExt) - - - - - - $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) - %(AdditionalUsingDirectories) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - MultiThreaded - false - - - Level3 - ProgramDatabase - true - true - Speed - true - true - true - false - - - $(IntDir)$(TargetName)$(TargetExt) - - - - - - - - - Disabled - $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) - %(AdditionalUsingDirectories) - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreadedDebug - false - StreamingSIMDExtensions - Level3 - ProgramDatabase - true - true - EnableFastChecks - - - MachineX86 - $(IntDir)$(TargetName)$(TargetExt) - - - - - X64 - - - Disabled - $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) - %(AdditionalUsingDirectories) - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreadedDebug - false - StreamingSIMDExtensions - Level3 - ProgramDatabase - true - true - false - EnableFastChecks - - - $(IntDir)$(TargetName)$(TargetExt) - - - - - - Disabled - $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) - %(AdditionalUsingDirectories) - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreadedDebug - false - StreamingSIMDExtensions - Level3 - ProgramDatabase - true - true - false - EnableFastChecks - - - $(IntDir)$(TargetName)$(TargetExt) - - - - - - Disabled - $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) - %(AdditionalUsingDirectories) - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreadedDebug - false - StreamingSIMDExtensions - Level3 - ProgramDatabase - true - true - false - EnableFastChecks - - - $(IntDir)$(TargetName)$(TargetExt) - - - - - - - - + + + + + Debug SDL2 + ARM + + + Debug SDL2 + ARM64 + + + Debug SDL2 + Win32 + + + Debug SDL2 + x64 + + + Release SDL2 + ARM + + + Release SDL2 + ARM64 + + + Release SDL2 + Win32 + + + Release SDL2 + x64 + + + + SDL2main + {47281679-339B-4D01-8556-A49384BFE812} + SDLmain + 10.0.17134.0 + + + + StaticLibrary + MultiByte + v141 + + + StaticLibrary + MultiByte + v141 + + + StaticLibrary + MultiByte + v141 + + + StaticLibrary + MultiByte + v141 + + + StaticLibrary + MultiByte + v141 + true + + + StaticLibrary + MultiByte + v141 + + + StaticLibrary + MultiByte + v141 + + + StaticLibrary + MultiByte + v141 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + $(SolutionDir)..\obj\$(ProjectName)\$(Platform)\$(Configuration)\ + AllRules.ruleset + + + AllRules.ruleset + AllRules.ruleset + AllRules.ruleset + + + + + + + AllRules.ruleset + + + AllRules.ruleset + AllRules.ruleset + AllRules.ruleset + + + + + + + + + + + + + + $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + MultiThreaded + false + StreamingSIMDExtensions + Level3 + ProgramDatabase + true + true + Speed + true + true + true + false + + + MachineX86 + $(IntDir)$(TargetName)$(TargetExt) + + + + + X64 + + + $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + MultiThreaded + false + + + Level3 + ProgramDatabase + true + true + Speed + true + true + true + false + + + $(IntDir)$(TargetName)$(TargetExt) + + + + + + $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + MultiThreaded + false + + + Level3 + ProgramDatabase + true + true + Speed + true + true + true + false + + + $(IntDir)$(TargetName)$(TargetExt) + + + + + + $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + MultiThreaded + false + + + Level3 + ProgramDatabase + true + true + Speed + true + true + true + false + + + $(IntDir)$(TargetName)$(TargetExt) + + + + + + + + + Disabled + $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDebug + false + StreamingSIMDExtensions + Level3 + ProgramDatabase + true + true + EnableFastChecks + + + MachineX86 + $(IntDir)$(TargetName)$(TargetExt) + + + + + X64 + + + Disabled + $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDebug + false + StreamingSIMDExtensions + Level3 + ProgramDatabase + true + true + false + EnableFastChecks + + + $(IntDir)$(TargetName)$(TargetExt) + + + + + + Disabled + $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDebug + false + StreamingSIMDExtensions + Level3 + ProgramDatabase + true + true + false + EnableFastChecks + + + $(IntDir)$(TargetName)$(TargetExt) + + + + + + Disabled + $(ProjectDir)/../../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDebug + false + StreamingSIMDExtensions + Level3 + ProgramDatabase + true + true + false + EnableFastChecks + + + $(IntDir)$(TargetName)$(TargetExt) + + + + + + + + \ No newline at end of file