Материнська плата XT-класу, Juko ST (SuperTurbo) з’явилася в мене кілька років тому, завдяки віртуальному музею комп’ютерного андеграунду, але якщо сумісні з 8-бітовими ISA VGA картки в мене знайшлися, то працездатні контролери вводу-виводу – не вдавалося добути. Так і стояла вона ‘‘безхозною’’.
В Інтернеті практично тотожні плати трапляються під іменем Unique UX-12. На жаль, я поки не зміг розібратися, чи це клон – і тоді, хто чий клон, чи просто інша назва? Виглядає, що була доволі популярною – в Інеті, зокрема, на Vogons – багато постів про неї (1, 2, 3 – лише як початок).
З появою PicoMEM, зміг трохи з нею побавитися – підкинула пару цікавинок.
Опис
- Пізня плата у своєму класі, BIOS – версія від 1989. Гарантійна наклейка вказує серпень 1991 – ймовірно, час завершення гарантії.
- CPU NEC V20.
- 4.77 МГц в “основному” режимі – для сумісності, 12 МГц в turbo-режимі.
- На відміну від більшості фото таких плат з Інтернету, впаяний, а не в “ліжечку”.
- Керується і програмно і апаратно – див. далі.
- 640 Кб RAM на платі.
- Можна встановити 1 Мб – тоді 84 Кб, вищі за 640 Кб, можна буде використати як віртуальний диск, дуже хитрозбоченим способом – розкажу трохи нижче.
- BIOS версії 2.32, від 10-06-89, останню відому, в Інеті також можна знайти версію 2.31 від 05-30-89 та 2.30, від 1988 – дати не бачу.
- Наклейка на мікросхемі ROM каже:
JUKO ST
,1988
,NEL ELECTRONICS LTD
.
- Наклейка на мікросхемі ROM каже:
Інша периферія:
- Природно, потребує перехідника з клавіатури AT на XT.
- Використав 16-бітові відеокарту, сумісну з 8-бітовою ISA1: Trident 9016×2/4 LT2, 256 Кб2.
- Вона майже сучасна платі, 1990.
- В ролі контролера вводу-виводу, FDD i HDD, мишки, мережевої карти і додаткової пам’яті – PicoMEM.
Фото плати. Трохи припала пилюкою – не маю фото після миття, та не хочу діставати з комп’ютера. |
Гарантійна наклейка. |
Наклейка на BIOS. |
Завантаження – видно версію BIOS-ів та налаштування PicoMEM. |
Продуктивність
Вимірював CheckIt 4.10, як і раніше.
Freq., MHz | FPU | Dhrystones | Whetstones | BIOS video | Direct video |
---|---|---|---|---|---|
(цілочисельні) | (floating point) | CPS3 | CPS | ||
4.77 | - | 364 | 7100 | 695 | 4578 |
vs XT | 1.06 | 1.07 | 1.03 | 0.924 | |
12 (11.98) | - | 943 | 18400 | 1890 | 11708 |
vs XT | 2.74 | 2.79 | 2.79 | 2.34 | |
4.77 | 8087 | 364 | 120100 | ||
vs XT | 1.06 | 18.19 | |||
12 (11.98) | 8087 | 943 | 298000 | ||
vs XT | 2.74 | 45.16 |
- В турбо режимі – частота 12 МГц. В “режимі сумісності”, назвемо його так, лише 4.77 МГц.
- При цьому, продуктивність складає 1.06 від XT з 8088 – думаю, ця різниця у 6% спричинена більшою ефективністю V20.
- Продуктивність відеопідсистеми та цілочисельної арифметики не змінилася з появою співпроцесора.
- Співпроцесор, традиційно, дуже гріється. Всі мої 8087/80287 дуже гріються, як в DIP, так і напаяні на плату в інших корпусах.
Використаний співпроцесор, 8087-2. |
Максимальна отримана продуктивність. |
Пропрієтарні розширення
Шукав вирішення однієї проблеми, спричиненої моєю помилкою, та й натрапив на згадку дискети з системними утилітами від виробника: Unique (Juko ST) UX Turbo XT Mainboard - utility diskette. У ньому вдалося частково прочитати дискету (копія тут, інша копія, дещо повніша, образ диску, який, щоправда, не вантажиться, тут).
Вціліли:
TURBO.COM
таTURBO.DOC
– програмне керування turbo-режимом. Що цікаво, має пріоритет над апаратним перемикачем (джампером).CDISK.SYS
таCDISK.DOC
– драйвер віртуального диску, розташованого у пам’яті вище 640 Кб.- Той драйвер вирішував загадку, яка мене турбувала – плата підтримує 1 Mb RAM, але ДЕ буде та пам’ять?! Дизасемблювання показало – див. далі.
TIMER.COM
– підозрюю, керування RTC. На жаль, каже “No clock found”. Тому випробувати не зміг.TU.COM
– крихітний файл для керування циклами очікування (wait states), див. далі.- Всі файли першої копії мають трохи сміття в кінці – прочитані цілими секторами. Це не мало б заважати. Файли другої збігаються, але мають правильний розмір.
Дизасемблювання TURBO.COM
Окрім режимів ON, OFF, HARD (використовувати положення перемикача), має контроль швидкості диску. Дизасемблювання показало наступне: цей контроль швидкості вказує, вимикати турбо-режим перед звертанням до дисків (перериванням int 13h), чи ні. Ймовірно, це робилося для сумісності з якимись пристроями чи дисками.
Утиліта стає резидентом, перехоплює int 9h – для клавіатурних комбінацій і int 13h – для вмикання-вимикання режиму турбо перед викликом справжнього обробника.
- Ctrl Alt + – ввімкнути турбо,
- Ctrl Alt - – вимкнути,
- Ctrl Alt * – вибирати джампером.
- Клавіші +, -, * – з малої цифрової.
При цьому, починає надавати сервіс для переключення:
Int 13h/AX=FFFFh/BH=AAh
,- BL=00h – повертає в AX 1234h – перевірка інсталяції.
- BL=01h – Turbo On.
- BL=02h – Turbo Off.
- BL=03h – використовувати апаратний джампер Turbo.
- BL=04h – використовувати Turbo під час доступу до диску.
- BL=05h – використовувати нормальний режим під час доступу до диску.
Безпосередньо переключення здійснюється викликом BIOS:
Int 15h/AH=0DFh
,- AL=00h – Turbo On,
- AL=01h – Turbo Off,
- AL=02h – вибирати джампером.
І те, і те було документовано в Ralf Brown Iterrupt list, хоч і дещо дивно:
Дизасемблювання CDISK.SYS та цікава ‘‘віртуальна’’ пам’ять
Власне, дизасемблювати я взявся, щоб зрозуміти, ЯК воно тими зайвими 384 Кб керує, вони ж не можуть відображатися в адресний простір безпосередньо – там відеопам’ять, BIOS, та та ж PicoMEM. TURBO.COM під роздачу вже за компанію потрапив.
Все виявилося зовсім просто, для читання чи запису секторів на диск, драйвер здійснює наступне:
- Копіює передане у внутрішній буфер.
- Виводить у порт 0xE0 значення 1.
- Вважає, що віртуальний диск починається з адреси 0x2000:0 (лінійна адреса 0x20000).
- Це відповідає початку 128-го кілобайта.
- Копіює зі свого внутрішнього буфера чи в нього, з адреси 0x20000 + (номер початкового сектора) * 512.
- Оскільки це драйвер, він може бути впевнений, що його буфер знаходиться нижче 128-го кілобайта (на відміну від користувацького буфера). Мінус UMB, звичайно – але вони тут проблемою не будуть, оскільки вони вище 640Кб – вище 128+384 Кб. Чи не буде проблемою refill – заповнення відсутньої на материнській платі основної RAM, за допомогою ISA-плати – не скажу.
- Виводить в порт 0xE0 значення 0.
- Виглядає, що вивести в цей порт 1 – вмикає альтернативне відобаження пам’яті на платі, яка більша за 640 Кб, на адресу 128 Кб, а виведення нуля – повертає все назад.
- В Інтернеті не знайшов нічого про таке. Можливо, в новітній час я першим про це, якщо не зауважив, то пишу.
- В різних джерелах, 1, 2, цей порт вказаній як зарезервований. В 3 згадується, але для інших плат і з не схожими функціями.
Документація на порт E0h для плати Juko ST:
- Вивести 1 – відобразити Bank 3 (пам’ять від 640 Кб до 1 Мб) на адресу, починаючи з 128 Кб.
- Вивести 0 – повернути відображення за замовчуванням.
TU.COM
Крихітний файл:
org 100h
cli
in al, 70h
mov al, 95h ; 1001`0101b
out 70h, al
sti
sti
int 20h
db 0B1h, 0x02h, 0E8h, 0E2h, 0CEh
Зразу я підвис. Згідно опису доступу до CMOS і вмісту порта 0x70, можу хіба припустити, що він забороняє NMI ‘‘з кінцями’’. Однак, документація плати (стор. B-1) підказала, це керування циклами очікування пам’яті – для RAM, ROM та I/O, окремо – вбудованих та зовнішніх. Редагуючи в файлі 0x95 (п’ятий байт) можна змінювати використовуване програмою значення. І так, документація каже робити саме два sti.
Дизасембльовані файли
І драйвер і TURBO – цікавий приклад тогочасного асемблерного коду, я теж щось таке писав в другій половині 90-х, тільки мій був потворнішим. Тому вирішив поділитися.
Дизасемблював IDA Free 5.00, трішки почистив коментарі, але без фанатизму. Мої коментарі, у свою чергу, можуть містити помилки – особливо ретельним не був.
Результати – бази даних IDA та асемблерні файли є тут.
TURBO.COM
Компіляція (використав MASM 6.11 та exe2bin з Turbo C 2.00 – збіг з коментарем у файлі випадковий, але кумедний):
masm turbo.asm
link turbo.obj;
exe2bin turbo.exe turbo.com
Результат рекомпіляції збігається з оригінадлом, за винятком двох байт – MASM 6.11 використав інший опкод для cmp ax, 0FFFFh
.
;
; ╔═════════════════════════════════════════════════════════════════════════╗
; ║ This file is generated by The Interactive Disassembler (IDA) ║
; ║ Copyright (c) 2010 by Hex-Rays SA, <support@hex-rays.com> ║
; ║ Licensed to: Freeware version ║
; ╚═════════════════════════════════════════════════════════════════════════╝
;
; File Name : ......\Juko_util\TURBO.COM
; Format : MS-DOS COM-file
; Base Address: 1000h Range: 10100h-10900h Loaded length: 800h
.8086
.model tiny
; ═══════════════════════════════════════════════════════════════════════════
; Segment type: Pure code
seg000 segment byte public 'CODE'
assume cs:seg000
org 100h
assume es:nothing, ss:nothing, ds:seg000
; ███████████████ S U B R O U T I N E ███████████████████████████████████████
public start
start proc near
jmp start1
; ───────────────────────────────────────────────────────────────────────────
aTurboV2_0020Ma db 'TURBO V2.00 20 MAY 88',0Dh,0Ah
db 1Ah
; ───────────────────────────────────────────────────────────────────────────
new_int_9:
push ax
push bx
push cx
push dx
push ds
push es
push di
push si
push bp
cli
in al, 60h ; AT Keyboard controller 8042.
cmp al, 37h
jnz short nextch
call StarIsPressed
jmp short exit_int_9
; ───────────────────────────────────────────────────────────────────────────
nextch: ;
cmp al, 4Eh
jnz short nextch2
call PlusIsPressed
jmp short exit_int_9
; ───────────────────────────────────────────────────────────────────────────
nextch2: ;
cmp al, 4Ah
jnz short exit_int_9
call MinusIsPressed
exit_int_9: ; ...
pop bp
pop si
pop di
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
jmp dword ptr cs:old_int9_ofs
; ───────────────────────────────────────────────────────────────────────────
new_int_13: ;
pushf
cmp ax, 0FFFFh
jnz short notOurCode
cmp bx, 0AA00h
jnz short notInstCheck
mov ax, 1234h
jmp short exit_int_13
; ───────────────────────────────────────────────────────────────────────────
notInstCheck: ;
cmp bx, 0AA01h
jz short setTurboOn0
cmp bx, 0AA02h
jz short setTurboOff0
cmp bx, 0AA03h
jz short setTurboHard0
cmp bx, 0AA04h
jz short setTurboOnHDD0
cmp bx, 0AA05h
jnz short exit_int_13
mov byte ptr cs:setTurboHDDState, 0
nop
jmp short exit_int_13
; ───────────────────────────────────────────────────────────────────────────
setTurboOnHDD0: ;
mov byte ptr cs:setTurboHDDState, 0FFh
nop
jmp short exit_int_13
; ───────────────────────────────────────────────────────────────────────────
setTurboHard0: ;
call setTurboHardImpl
jmp short exit_int_13
; ───────────────────────────────────────────────────────────────────────────
setTurboOn0: ;
call setTurboOnImpl
jmp short exit_int_13
; ───────────────────────────────────────────────────────────────────────────
setTurboOff0: ;
call setTurboOff1
exit_int_13: ; ...
popf
iret
; ───────────────────────────────────────────────────────────────────────────
notOurCode: ;
cmp byte ptr cs:setTurboHDDState, 0
jnz short callOrigInt13NRestore
push ax
push bx
push cx
push dx
push ds
push es
push di
push si
push bp
call setTurboOffImpl
pop bp
pop si
pop di
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
callOrigInt13NRestore: ;
popf
pushf
call dword ptr cs:old_int13_ofs
pushf
push ax
push bx
push cx
push dx
push ds
push es
push di
push si
push bp
mov al, byte ptr cs:TSRTurboOn
cmp al, 0FFh
jz short restorRegsNExit
cmp al, 1
jz short TurboOnNExit
call setTurboOff1
jmp short restorRegsNExit
; ───────────────────────────────────────────────────────────────────────────
TurboOnNExit: ;
call setTurboOnImpl
restorRegsNExit: ;
pop bp
pop si
pop di
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
popf
retf 2
; ───────────────────────────────────────────────────────────────────────────
StarIsPressed: ;
call isCtrlAltPressed
jnz short skip
setTurboHardImpl:
mov ax, 0DF02h
int 15h
mov byte ptr cs:TSRTurboOn, 0FFh
skip: ;
retn
; ───────────────────────────────────────────────────────────────────────────
PlusIsPressed: ;
call isCtrlAltPressed
jnz short skip3
setTurboOnImpl:
mov ax, 0DF00h
int 15h
mov byte ptr cs:TSRTurboOn, 1
skip3: ;
retn
; ───────────────────────────────────────────────────────────────────────────
MinusIsPressed: ;
call isCtrlAltPressed
jnz short skip4
setTurboOff1:
mov byte ptr cs:TSRTurboOn, 0
setTurboOffImpl: ;
mov ax, 0DF01h
int 15h
skip4: ;
retn
; ───────────────────────────────────────────────────────────────────────────
isCtrlAltPressed:
xor ax, ax
mov ds, ax
assume ds:nothing
mov al, ds:417h ; 40:17 -- kbd status
and al, 0Ch
cmp al, 0Ch
retn
; ───────────────────────────────────────────────────────────────────────────
TSRTurboOn:
db 0FFh
setTurboHDDState:
db 0FFh
old_int9_ofs:
dw 0
old_int9_seg:
dw 0
old_int13_ofs:
dw 0
old_int13_seg:
dw 0
; ───────────────────────────────────────────────────────────────────────────
start1:
cld
mov ax, 0FFFFh
mov bx, 0AA00h
int 13h ; DISK -
cmp ax, 1234h
jz short loc_10275
mov ax, 3509h
int 21h ; DOS - 2+ - GET INTERRUPT VECTOR
; AL = interrupt number
; Return: ES:BX = value of interrupt vector
mov word ptr ds:old_int9_ofs, bx
mov word ptr ds:232h, es
mov dx, offset new_int_9
mov ax, 2509h
int 21h ; DOS - SET INTERRUPT VECTOR
; AL = interrupt number
; DS:DX = new vector to be used for specified interrupt
mov ax, 3513h
int 21h ; DOS - 2+ - GET INTERRUPT VECTOR
; AL = interrupt number
; Return: ES:BX = value of interrupt vector
mov word ptr ds:old_int13_ofs, bx
mov word ptr ds:old_int13_seg, es
mov dx, offset new_int_13
mov ax, 2513h
int 21h ; DOS - SET INTERRUPT VECTOR
; AL = interrupt number
; DS:DX = new vector to be used for specified interrupt
mov byte ptr ds:398h, 1
loc_10275:
mov cl, ds:80h ; Command line size
xor ch, ch
or cx, cx
jnz short scanForDskOpt
jmp no_args
; ───────────────────────────────────────────────────────────────────────────
scanForDskOpt:
mov si, 81h
nextSlash:
mov al, [si]
cmp al, '/'
jz short slashFound
and byte ptr [si], 0DFh
slashFound:
inc si
loop nextSlash
mov si, 81h
mov cx, 2
parseNext:
lodsb
cmp al, 0
jz short parseNext
cmp al, 9
jz short parseNext
cmp al, 'O'
jz short onOFound
cmp al, 'H'
jz short onHFound
cmp al, '/'
jz short slashFound0
cmp cx, 2
jz short noArgs0
jmp exitNoTSR
; ───────────────────────────────────────────────────────────────────────────
noArgs0:
jmp no_args
; ───────────────────────────────────────────────────────────────────────────
onOFound:
lodsb
cmp al, 'N'
jz short onONFound
cmp al, 'F'
jz short onF1Found
jmp unrecognOpt
; ───────────────────────────────────────────────────────────────────────────
onF1Found:
lodsb
cmp al, 'F'
jz short onOFFFound
jmp unrecognOpt
; ───────────────────────────────────────────────────────────────────────────
onOFFFound:
lodsb
cmp al, 0
jz short onOFFFound2
cmp al, 9
jz short onOFFFound2
cmp al, 0Dh
jz short onOFFFound2
jmp unrecognOpt
; ───────────────────────────────────────────────────────────────────────────
onOFFFound2:
mov ax, 0FFFFh
mov bx, 0AA02h
int 13h ; DISK -
mov dx, offset aTurboModeIsN_0 ; "\r\nTurbo mode is now off !\r\n$"
jmp short loc_10366
; ───────────────────────────────────────────────────────────────────────────
onONFound:
lodsb
cmp al, 0
jz short onONFound2
cmp al, 9
jz short onONFound2
cmp al, 0Dh
jnz short unrecognOpt
onONFound2:
mov ax, 0FFFFh
mov bx, 0AA01h
int 13h ; DISK -
mov dx, offset aTurboModeIsNow ; "\r\nTurbo mode is now on !\r\n$"
jmp short loc_10366
; ───────────────────────────────────────────────────────────────────────────
onHFound:
lodsb
cmp al, 41h
jnz short unrecognOpt
lodsb
cmp al, 52h
jnz short unrecognOpt
lodsb
cmp al, 44h
jnz short unrecognOpt
lodsb
cmp al, 0
jz short loc_1031E
cmp al, 9
jz short loc_1031E
cmp al, 0Dh
jnz short unrecognOpt
loc_1031E:
mov ax, 0FFFFh
mov bx, 0AA03h
int 13h ; DISK -
mov dx, offset aModeIsNowSetAc ; "\r\nMode is now set according to hard swi"...
jmp short loc_10366
; ───────────────────────────────────────────────────────────────────────────
slashFound0:
lodsb
cmp al, 'N'
jz short foundSlashN
cmp al, 'T'
jnz short unrecognOpt
lodsb
cmp al, 0 ; If last letter -- set option, otherwise -- error
jz short setDiskTurboSpeed
cmp al, 9
jz short setDiskTurboSpeed
cmp al, 0Dh
jnz short unrecognOpt
setDiskTurboSpeed:
mov ax, 0FFFFh
mov bx, 0AA04h
int 13h ; DISK -
mov dx, offset aDiskAccessIs_0 ; "\r\nDisk access is now in turbo speed !\r\n"...
jmp short loc_10366
; ───────────────────────────────────────────────────────────────────────────
foundSlashN:
lodsb
cmp al, 0
jz short setDiskNormalSpeed
cmp al, 9
jz short setDiskNormalSpeed
cmp al, 0Dh
jnz short unrecognOpt
setDiskNormalSpeed:
mov ax, 0FFFFh
mov bx, 0AA05h
int 13h ; DISK -
mov dx, offset aDiskAccessIsNo ; "\r\nDisk access is now in normal speed !\r"...
loc_10366:
mov ah, 9
int 21h ; DOS - PRINT STRING
; DS:DX -> string terminated by "$"
loop loc_1036E
jmp short exitNoTSR
; ───────────────────────────────────────────────────────────────────────────
loc_1036E:
jmp parseNext
; ───────────────────────────────────────────────────────────────────────────
unrecognOpt:
mov dx, offset aUnrecognizedPa ; "\r\nUnrecognized parameter found in comma"...
mov ah, 9
int 21h ; DOS - PRINT STRING
; DS:DX -> string terminated by "$"
jmp short exitNoTSR
; ───────────────────────────────────────────────────────────────────────────
no_args:
mov dx, offset aHelpMsg ; "\r\nTurbo switch V2.00, 20 May 88\r\n\r\nUSA"...
mov ah, 9
int 21h ; DOS - PRINT STRING
; DS:DX -> string terminated by "$"
exitNoTSR:
mov al, ds:byte_10398
or al, ds:byte_10398
jnz short ExitTSR
int 20h ; DOS - PROGRAM TERMINATION
; returns to DOS--identical to INT 21/AH=00h
; ───────────────────────────────────────────────────────────────────────────
ExitTSR:
mov dx, offset aTurboSwitchV2_ ; "\r\nTurbo switch V2.00 installed\r\n$"
mov ah, 9
int 21h ; DOS - PRINT STRING
; DS:DX -> string terminated by "$"
mov dx, offset start1
int 27h ; DOS - TERMINATE BUT STAY RESIDENT
start endp ; CS = current program segment
; DX = last program byte + 1
; ───────────────────────────────────────────────────────────────────────────
byte_10398 db 0
aTurboSwitchV2_ db 0Dh,0Ah
db 'Turbo switch V2.00 installed',0Dh,0Ah,'$'
aTurboModeIsNow db 0Dh,0Ah
db 'Turbo mode is now on !',0Dh,0Ah,'$'
aTurboModeIsN_0 db 0Dh,0Ah
db 'Turbo mode is now off !',0Dh,0Ah,'$'
aModeIsNowSetAc db 0Dh,0Ah
db 'Mode is now set according to hard switch !',0Dh,0Ah,'$'
aDiskAccessIsNo db 0Dh,0Ah
db 'Disk access is now in normal speed !',0Dh,0Ah,'$'
aDiskAccessIs_0 db 0Dh,0Ah
db 'Disk access is now in turbo speed !',0Dh,0Ah,'$'
aUnrecognizedPa db 0Dh,0Ah
db 'Unrecognized parameter found in command line !',0Dh,0Ah
aHelpMsg db 0Dh,0Ah
db 'Turbo switch V2.00, 20 May 88',0Dh,0Ah
db 0Dh,0Ah
db 'USAGE : TURBO [switch] [disk access speed control]',0Dh,0Ah
db 0Dh,0Ah
db 9,'[switch] = ON : Turn on turbo mode',0Dh,0Ah
db 9,' = OFF : Turn off turbo mode',0Dh,0Ah
db 9,' = HARD : Set according to hard switch',0Dh,0Ah
db 0Dh,0Ah
db 'To switch mode with keyboard',0Dh,0Ah
db 0Dh,0Ah
db 'Hold down "Ctrl" and "Alt" then press',0Dh,0Ah
db 0Dh,0Ah
db ' Grey + : Turn on turbo mode',0Dh,0Ah
db ' Grey - : Turn off turbo mode',0Dh,0Ah
db ' Grey * : Set according to hard switch',0Dh,0Ah
db 0Dh,0Ah
db 'Disk access speed control :',0Dh,0Ah
db 0Dh,0Ah
db ' /N : Normal',0Dh,0Ah
db ' /T : Turbo',0Dh,0Ah,'$'
seg000 ends
end start
CDISK.SYS
Компіляція:
masm cdisk.asm
link cdisk.obj;
exe2bin cdisk.exe cdisk.sys
Результат рекомпіляції повністю збігається з оригіналом. Знову ж, мої коментарі можуть містити помилки.
Окрім перемикання відображення пам’яті, цікаво глянути, як форматує диск. Та й навчальний приклад драйвера DOS непоганий, хоча чи ідеальний – не скажу.
;
; ╔═════════════════════════════════════════════════════════════════════════╗
; ║ This file is generated by The Interactive Disassembler (IDA) ║
; ║ Copyright (c) 2010 by Hex-Rays SA, <support@hex-rays.com> ║
; ║ Licensed to: Freeware version ║
; ╚═════════════════════════════════════════════════════════════════════════╝
;
; Input MD5 : 48753800C37485943415AFDE2D1B3A37
; File Name : D:\WorkWWW\Blog\ComputersHistory.S\CompMuseum\86_2\Juko_util\CDISK.SYS
; Format : MS-DOS SYS-file (perhaps device driver)
; Base Address: 0h Range: 0h-800h Loaded length: 800h
.8086
.model small
; ═══════════════════════════════════════════════════════════════════════════
; Segment type: Pure code
seg000 segment byte public 'CODE'
assume cs:seg000
assume es:nothing, ss:nothing, ds:nothing
NextDevice_0 dw 0FFFFh ; The last device
dw 0FFFFh
DevAttr_0 dw 2000h ; non-IBM block device
; block device
Strategy_0 dw offset Strategy_Routine_0
Interrupt_0 dw offset Interrupt_Routine_0
Subunits db 1 ; May be device number
db 0,0,0,0,0,0,0
ReqPtr dd 0 ;
BPB_data dw 200h ;
; Bytes per sector
db 1 ; Sectors per cluster
dw 1 ; Reserved sectors
db 2 ; FATs
dw 70h ; Root dir entries
dw 300h ; Total logical secs.
unk_20 db 0FEh ; Media descr.
dw 3 ; Sectors in FAT
BPB_ptr dw 16h ;
req_bytes_cnt dw 0 ;
doVerifyAftWr db 0 ;
req_sect_num dw 0 ;
VirtRAMBaseSeg dw 2000h ;
tranferAddrOff dw 0 ;
tranferAddrSeg dw 0 ;
VirtDiskBootBPB db 3 dup(0) ; 0 ;
aNelIntl_OEM db 'NEL INTL' ;
dw 200h ; Bytes per sector - 512d
db 1 ; Logical sectors per cluster
dw 1 ; Reserved logical sectors
db 2 ; Number of FATs
dw 40h ; Maximum number of root directory entries.
dw 300h ; Total logical sectors - 768, 384 Кб
db 0FCh ; № ; Media descriptor FC -- (?!) 5.25-inch single sided, 40 tracks per side, 9 sectors per track (180 KB)
dw 3 ; Logical sectors per FAT
DispatchTbl dw 1E0h ;
; Init
dw 39Ch ; Media_Chk
dw 3AFh ; Build_BPB
dw 3FEh ; Ioctl_Inp ; 3 = I/O ctrl read from dev
dw 40Ch ; Input ; 4 = normal destructive read
dw 3FEh ; Nd_Input ; 5 = non-destructive read,no wait
dw 3FEh ; Inp_Stat ; 6 = return current input status
dw 3FEh ; Inp_Flush ; 7 = flush device input buffers
dw 43Ah ; Output ; 8 = normal output to device
dw 478h ; Outp_Vfy ; 9 = output with verify
dw 3FEh ; Outp_Stat ; 10 = return current output status
dw 3FEh ; Outp_Flush ; 11 = flush output buffers
dw 3FEh ; Ioctl_Outp ; 12 = I/O control output
dw 3FEh ; Dev_Open ; 13 = device open (MS-DOS 3.x)
dw 3FEh ; Dev_Close ; 14 = device close (MS-DOS 3.x)
dw 3FEh ; Rem_Media ; 15 = removeable media (MS-DOS 3.x)
dw 3FEh
aJukoStRamdrive db 0Dh,0Ah
db '*** Juko ST RAMDrive 384K V2.10 ***',0Dh,0Ah
db ' 17 MAY 1988 '
db 1Ah
; ███████████████ S U B R O U T I N E ███████████████████████████████████████
ParseReq4IOv proc near
mov ax, es:[bx+0Eh] ; Save transfer addr. Lo from Req, hdr
mov cs:tranferAddrOff, ax
mov ax, es:[bx+10h] ; Save transfer addr. Hi from Req, hdr
mov cs:tranferAddrSeg, ax
mov ax, es:[bx+12h] ; Save sectors count (blk dev.)
xor ah, ah ; WTF? No more than 255 sectors = 127.5 Кб
mov cs:req_bytes_cnt, ax
retn
ParseReq4IOv endp
; ███████████████ S U B R O U T I N E ███████████████████████████████████████
; Parses IO request, on ret:
; ds -- initial sector offset in virtual mem
; cx -- bytes count, 0xFFFF if too many to fit in 16-bit reg
; si -- 0
SectNumToVirtMemSeg proc near
mov ax, cs:req_sect_num
mov cx, 20h ; ' '
mul cx
mov dx, cs:VirtRAMBaseSeg
add dx, ax
mov ds, dx
xor si, si
mov ax, cs:req_bytes_cnt
mov cx, 200h
mul cx
or ax, ax
jnz short loc_DE
mov ax, 0FFFFh
loc_DE:
xchg ax, cx
retn
SectNumToVirtMemSeg endp
; ███████████████ S U B R O U T I N E ███████████████████████████████████████
input_impl proc near
call SectNumToVirtMemSeg ; Parses IO request, on ret:
; ds -- initial sector offset in virtual mem
; cx -- bytes count, 0xFFFF if too many to fit in 16-bit reg
; si -- 0
mov es, cs:tranferAddrSeg
mov di, cs:tranferAddrOff
mov ax, di
add ax, cx
jnb short loc_FA
mov ax, 0FFFFh
sub ax, di
mov cx, ax
loc_FA:
mov ax, cx
mov cl, 9 ; : Div by 512 -- ax = sectors
shr ax, cl
loc_100: ; Put sectors to read to CX
mov cx, ax
mov bx, cs
copySectorToBuf:
push cx
cli
push es
push di
call EnablTrans
mov es, bx ; ES = CS (DS -- seg. in virtual mem, SI = 0)
mov di, offset Buffer1
mov cx, 100h
rep movsw ; Copy 256 words = 512 bytes, 1 sector,
; from virtual mem to internal buffer
call DisablTrans
pop di
pop es ; Now ES -- target buffer
sti
push ds
push si
mov ds, bx
mov si, offset Buffer1
mov cx, 100h
rep movsw ; Copy from internal buffer to target buffer
pop si
pop ds
pop cx
loop copySectorToBuf
retn
input_impl endp
; ███████████████ S U B R O U T I N E ███████████████████████████████████████
; More details -- see input_impl function
output_impl proc near
call SectNumToVirtMemSeg ; Parses IO request, on ret:
; ds -- initial sector offset in virtual mem
; cx -- bytes count, 0xFFFF if too many to fit in 16-bit reg
; si -- 0
push ds
pop es
mov di, si
mov ds, cs:tranferAddrSeg
mov si, cs:tranferAddrOff
mov ax, si
add ax, cx
jnb short loc_14B
mov ax, 0FFFFh
sub ax, si
mov cx, ax
loc_14B:
mov ax, cx
mov cl, 9
shr ax, cl
mov cx, ax
mov bx, cs
copyBufToSector:
push cx
push es
push di
mov es, bx
assume es:seg000
mov di, offset Buffer1
mov cx, 100h
rep movsw ; Copy sector from source to internal buffer
pop di
pop es
assume es:nothing
cli
call EnablTrans ; Copy from internal buffer to Virtual mem
push ds
push si
mov ds, bx
mov si, offset Buffer1
mov cx, 100h
rep movsw
pop si
pop ds
call DisablTrans
sti
pop cx
loop copyBufToSector
retn
output_impl endp
; ███████████████ S U B R O U T I N E ███████████████████████████████████████
EnablTrans proc near
mov al, 1
out 0E0h, al
retn
EnablTrans endp
; ███████████████ S U B R O U T I N E ███████████████████████████████████████
DisablTrans proc near
xor al, al
out 0E0h, al
retn
DisablTrans endp
; ███████████████ S U B R O U T I N E ███████████████████████████████████████
CheckVirtRam proc near
mov ax, es:[di]
push ax
mov word ptr es:[di], 0
call EnablTrans
mov word ptr es:[di], 1234h
cmp word ptr es:[di], 1234h
jnz short loc_1AF
call DisablTrans
pop ax
cmp word ptr es:[di], 0
mov es:[di], ax
jnz short loc_1B3
clc
retn
; ───────────────────────────────────────────────────────────────────────────
loc_1AF:
pop ax
mov es:[di], ax
loc_1B3:
stc
retn
CheckVirtRam endp
; ███████████████ S U B R O U T I N E ███████████████████████████████████████
Strategy_Routine_0 proc far ;
mov word ptr cs:ReqPtr+2, es ; ES:BX -> Device Request Block
mov word ptr cs:ReqPtr, bx
retf
Strategy_Routine_0 endp
; ███████████████ S U B R O U T I N E ███████████████████████████████████████
Interrupt_Routine_0 proc near ;
cld ; Device Request Block:
; 0 db length
; 1 db unit number
; 2 db command code
; 5 d? reserved
; 0D d? command specific data
push bp
push ds
push es
push ax
push bx
push cx
push dx
push di
push si
push cs
pop ds
assume ds:seg000
les bx, ReqPtr
mov al, es:[bx+2] ; Req block command code
rol al, 1 ; Index in DispatchTbl
lea di, DispatchTbl ; Init
xor ah, ah
add di, ax
jmp word ptr [di]
Interrupt_Routine_0 endp
; ███████████████ S U B R O U T I N E ███████████████████████████████████████
Init proc far
mov dx, cs ; Format of request block (depends on command code)
; https://www.delorie.com/djgpp/doc/rbinter/it/97/25.html
; See also: https://kunyichen.wordpress.com/2008/06/21/bookmark-a-template-code-for-ms-dos-installable-device-driver/
lea ax, aYYearEveryItem ; After the end
mov cl, 4
ror ax, cl
add dx, ax
inc dx
mov cs:VirtRAMBaseSeg, 2000h
mov word ptr es:[bx+0Eh], 0
mov es:[bx+10h], dx ; Save driver size into req. block
mov byte ptr es:[bx+0Dh], 1 ; 1 disk
lea dx, BPB_ptr
mov es:[bx+12h], dx ; Save ptr to BPB
mov word ptr es:[bx+14h], cs
mov bp, es
mov es, cs:VirtRAMBaseSeg
xor di, di
call CheckVirtRam
jnb short VirtRAMfound
mov es, bp
mov byte ptr es:[bx+0Dh], 0 ; No disks
mov word ptr es:[bx+10h], cs
mov dx, offset aVirtualRamNotP ; "Virtual RAM not present.\r\nRAM Disk not "...
jmp OnLastMesg1
; ───────────────────────────────────────────────────────────────────────────
VirtRAMfound:
push ds
mov ax, 40h ; '@'
mov ds, ax
assume ds:nothing
cmp word ptr ds:72h, 1234h ; "1234H means Ctrl+Alt+Del reboot is in progress"
; To preserve virt disk on Ctrl+Alt+Del reboot?
pop ds
assume ds:nothing
jnz short formatVirtDisk ; Not cold reboot
call EnablTrans
push ds
push es
push si
push di
mov si, 2000h ; Virt mem base seg. (Instead of using the corresponding var...)
mov ds, si
assume ds:nothing
mov si, cs
mov es, si
assume es:seg000
mov si, 3
mov di, offset aNelIntl_OEM ; "NEL INTL"
mov cx, 4
cld
repe cmpsw ; Compares "NEL INTL" string with the 3-11 bytes of the virtual mem
; OEM string in virtual disk boot sector?
pop di
pop si
pop es
assume es:nothing
pop ds
assume ds:nothing
jnz short formatVirtDisk ; No ID string
call DisablTrans
mov dx, offset aJukoStRamdri_1 ; "Juko ST RAMDrive PRESERVED.\r\n\r\n$"
jmp short OnLastMesg1
; ───────────────────────────────────────────────────────────────────────────
db 90h ; Р
; ───────────────────────────────────────────────────────────────────────────
formatVirtDisk:
call EnablTrans
lea si, VirtDiskBootBPB
mov cx, 18h
rep movsb ; Create boot sector
mov cs:req_sect_num, 1
mov cs:req_bytes_cnt, 3
call SectNumToVirtMemSeg ; Parses IO request, on ret:
; ds -- initial sector offset in virtual mem
; cx -- bytes count, 0xFFFF if too many to fit in 16-bit reg
; si -- 0
push ds
pop es
mov di, si
xor al, al
rep stosb ; Init FAT-1
mov byte ptr [si], 0FCh ; '№'
mov byte ptr [si+1], 0FFh
mov byte ptr [si+2], 0FFh
push ds
push si
mov cs:req_sect_num, 4
mov cs:req_bytes_cnt, 3
call SectNumToVirtMemSeg ; Parses IO request, on ret:
; ds -- initial sector offset in virtual mem
; cx -- bytes count, 0xFFFF if too many to fit in 16-bit reg
; si -- 0
push ds
pop es
mov di, si
pop si
pop ds
rep movsb ; Init FAT-2
mov cs:req_sect_num, 7
mov cs:req_bytes_cnt, 4
call SectNumToVirtMemSeg ; Parses IO request, on ret:
; ds -- initial sector offset in virtual mem
; cx -- bytes count, 0xFFFF if too many to fit in 16-bit reg
; si -- 0
xor al, al
push ds
pop es
xor di, di
push cs
pop ds
assume ds:seg000
mov si, offset virtDiskVolLabel ; "NEL DISK 01\bЖJЭ\x0F"
push cx
mov cx, 10h
rep movsw ; Create volume label
pop cx
sub cx, 20h ; ' '
rep stosb ; Create other part of root dir
call DisablTrans
mov dx, offset aJukoStRamdri_0 ; "Juko ST RAMDrive 384K V2.10 INSTALLED.\r"...
OnLastMesg1:
mov ah, 9
int 21h ; DOS - PRINT STRING
; DS:DX -> string terminated by "$"
mov es, word ptr cs:ReqPtr+2
mov bx, word ptr cs:ReqPtr
or word ptr es:[bx+3], 100h ; set status, "busy", https://www.delorie.com/djgpp/doc/rbinter/it/96/25.html
or word ptr es:[bx+3], 0
jmp ExitDriver
; ───────────────────────────────────────────────────────────────────────────
virtDiskVolLabel db 'NEL DISK 01',8,0,0,0,0,0,0,0,0,0,0,'ЖJЭ',0Fh,0,0,0,0,0,0
;
; Non-zero bytes in second half -- modification date-time
aJukoStRamdri_0 db 'Juko ST RAMDrive 384K V2.10 INSTALLED.',0Dh,0Ah ;
db 0Dh,0Ah,'$'
aJukoStRamdri_1 db 'Juko ST RAMDrive PRESERVED.',0Dh,0Ah ;
db 0Dh,0Ah,'$'
aVirtualRamNotP db 'Virtual RAM not present.',0Dh,0Ah ;
db 'RAM Disk not installed.',0Dh,0Ah,'$'
; ───────────────────────────────────────────────────────────────────────────
Media_Chk: ; Media not changed
mov byte ptr es:[bx+0Eh], 1
or word ptr es:[bx+3], 100h
or word ptr es:[bx+3], 0
jmp ExitDriver
; ───────────────────────────────────────────────────────────────────────────
Build_BPB:
push es
push bx
mov cs:req_sect_num, 0
mov cs:req_bytes_cnt, 1
call SectNumToVirtMemSeg ; Parses IO request, on ret:
; ds -- initial sector offset in virtual mem
; cx -- bytes count, 0xFFFF if too many to fit in 16-bit reg
; si -- 0
cli
call EnablTrans
push cs
pop es
assume es:seg000
lea di, BPB_data
add si, 0Bh
mov cx, 0Dh
rep movsb
pop bx
pop es
assume es:nothing
call DisablTrans
sti
lea dx, BPB_data
mov es:[bx+12h], dx
mov word ptr es:[bx+14h], cs
mov es:[bx+0Eh], dx
mov word ptr es:[bx+10h], cs
or word ptr es:[bx+3], 100h
or word ptr es:[bx+3], 0
push cs
pop ds
jmp ExitDriver
; ───────────────────────────────────────────────────────────────────────────
Ioctl_Inp:
or word ptr es:[bx+3], 100h
or word ptr es:[bx+3], 0
jmp short ExitDriver
; ───────────────────────────────────────────────────────────────────────────
db 90h ; Р
; ───────────────────────────────────────────────────────────────────────────
Input:
call ParseReq4IOv
mov ax, es:[bx+14h] ; Sector number
mov cs:req_sect_num, ax
mov ax, es:[bx+12h] ; Byte count
mov cs:req_bytes_cnt, ax
call input_impl
mov bx, word ptr cs:ReqPtr
mov es, word ptr cs:ReqPtr+2
or word ptr es:[bx+3], 100h
or word ptr es:[bx+3], 0
jmp short ExitDriver
; ───────────────────────────────────────────────────────────────────────────
db 90h ; Р
; ───────────────────────────────────────────────────────────────────────────
Output:
call ParseReq4IOv
mov ax, es:[bx+14h]
mov cs:req_sect_num, ax
mov ax, es:[bx+12h]
mov cs:req_bytes_cnt, ax
call output_impl ; More details -- see input_impl function
mov bx, word ptr cs:ReqPtr
mov es, word ptr cs:ReqPtr+2
cmp cs:doVerifyAftWr, 0
jz short VerifyAftW
mov cs:doVerifyAftWr, 0
jmp short Input
; ───────────────────────────────────────────────────────────────────────────
VerifyAftW:
or word ptr es:[bx+3], 100h
or word ptr es:[bx+3], 0 ;
jmp short ExitDriver
; ───────────────────────────────────────────────────────────────────────────
db 90h ; Р
; ───────────────────────────────────────────────────────────────────────────
Outp_Vfy:
mov cs:doVerifyAftWr, 1
jmp short Output
; ───────────────────────────────────────────────────────────────────────────
ExitDriver:
pop si
pop di
pop dx
pop cx
pop bx
pop ax
pop es
pop ds
assume ds:nothing
pop bp
retf
Init endp ; sp = 12h
; ───────────────────────────────────────────────────────────────────────────
db 0
db 0
db 0
db 0
db 0
db 0
Buffer1 db 100h dup(0) ; 0 ;
db 100h dup(0) ;
aYYearEveryItem:
; Looks like code below -- from the sector slack space
seg000 ends
end
Посилання
- Інструкція до плати, під іменем UX Turbo, ver. 44256. Цікаво, що в документації помилка на титулці – ‘‘UT Tubo’’.
- Трапляється також під назвою типу ‘‘JUKO BABY XT BXM/12’’ – або це теж клон.
Виноски
-
Джампер JP8, коли відкритий, дозволяє обрати автодетектування ширини ISA. ↩
-
На жаль, запит з допомогою int 10h не спрацював, тому покладаюся на вивід HWINFO16.EXE. ↩
-
Chars per second. ↩
-
І відеокарта сучасна, і процесор трохи швидший – то результат дивний… Більша ефективність BIOS? Труднощі відображення пам’яті? ↩