/kernel/trunk/data32.inc |
---|
148,7 → 148,7 |
msg_CR db 13,10,0 |
szHwMouse db 'ATI2D',0 |
szPS2MDriver db '/rd/1/drivers/PS2MOUSE.SYS',0 |
szPS2MDriver db 'PS2MOUSE',0 |
;szCOM_MDriver db 'COM_MOUSE',0 |
szVidintel db '/rd/1/drivers/vidintel.sys',0 |
szUSB db 'USB',0 |
/kernel/trunk/kernel.asm |
---|
1058,7 → 1058,7 |
; Load PS/2 mouse driver |
stdcall load_pe_driver, szPS2MDriver, 0 |
stdcall load_driver, szPS2MDriver |
mov esi, boot_setmouse |
call boot_log |
/kernel/trunk/drivers/vidintel.asm |
---|
0,0 → 1,465 |
; Stub of videodriver for Intel videocards. |
; (c) CleverMouse |
; When the start procedure gots control, |
; it tries to detect preferred resolution, |
; sets the detected resolution assuming 32-bpp VESA mode and exits |
; (without registering a service). |
; Detection can be overloaded with compile-time settings |
; use_predefined_mode/predefined_width/predefined_height. |
; set predefined resolution here |
use_predefined_mode = 0;1 |
predefined_width = 0;1366 |
predefined_height = 0;768 |
; standard driver stuff |
format MS COFF |
DEBUG = 1 |
include 'proc32.inc' |
include 'imports.inc' |
public START |
public version |
section '.flat' code readable align 16 |
; the start procedure (see the description above) |
START: |
; 1. Detect device. Abort if not found. |
push esi |
call DetectDevice |
test esi, esi |
jz .return0 |
; 2. Detect optimal mode unless the mode is given explicitly. Abort if failed. |
if use_predefined_mode = 0 |
call DetectMode |
end if |
cmp [width], 0 |
jz .return0_cleanup |
; 3. Set the detected mode. |
call SetMode |
; 4. Cleanup and return. |
.return0_cleanup: |
stdcall FreeKernelSpace, esi |
.return0: |
pop esi |
xor eax, eax |
ret 4 |
; check that there is Intel videocard |
; if so, map MMIO registers and set internal variables |
; esi points to MMIO block; NULL means no device |
DetectDevice: |
; 1. Sanity check: check that we are dealing with Intel videocard. |
; Integrated video device for Intel is always at PCI:0:2:0. |
xor esi, esi ; initialize return value to NULL |
; 1a. Get PCI VendorID and DeviceID. |
push esi |
push 10h |
push esi |
call PciRead32 |
; 1b. loword(eax) = ax = VendorID, hiword(eax) = DeviceID. |
; Test whether we have Intel chipset. |
cmp ax, 8086h |
jnz .return |
; 1c. Say hi including DeviceID. |
shr eax, 10h |
push edi |
pusha |
mov edi, pciid_text |
call WriteWord |
mov esi, hellomsg |
call SysMsgBoardStr |
popa |
; 1d. Test whether we know this DeviceID. |
; If this is the case, remember the position of the device in line of Intel cards; |
; this knowledge will be useful later. |
; Tested on devices with id: 8086:0046, partially 8086:2A02. |
mov ecx, pciids_num |
mov edi, pciids |
repnz scasw |
pop edi |
jnz .return_unknown_pciid |
sub ecx, pciids_num - 1 |
neg ecx |
mov [deviceType], ecx |
; 1e. Continue saying hi with positive intonation. |
pusha |
mov esi, knownmsg |
call SysMsgBoardStr |
popa |
; 2. Prepare MMIO region to control the card. |
; 2a. Read MMIO physical address from PCI config space. |
push 10h |
cmp ecx, i9xx_start |
jae @f |
mov byte [esp], 14h |
@@: |
push 10h |
push esi |
call PciRead32 |
; 2b. Mask out PCI region type, lower 4 bits. |
and al, not 0xF |
; 2c. Create virtual mapping of the physical memory. |
push 1Bh |
push 100000h |
push eax |
call MapIoMem |
; 3. Return. |
xchg esi, eax |
.return: |
ret |
; 1f. If we do not know DeviceID, continue saying hi with negative intonation. |
.return_unknown_pciid: |
pusha |
mov esi, unknownmsg |
call SysMsgBoardStr |
popa |
ret |
; Convert word in ax to hexadecimal text in edi, advance edi. |
WriteWord: |
; 1. Convert high byte. |
push eax |
mov al, ah |
call WriteByte |
pop eax |
; 2. Convert low byte. |
; Fall through to WriteByte; ret from WriteByte is ret from WriteWord too. |
; Convert byte in al to hexadecimal text in edi, advance edi. |
WriteByte: |
; 1. Convert high nibble. |
push eax |
shr al, 4 |
call WriteNibble |
pop eax |
; 2. Convert low nibble. |
and al, 0xF |
; Fall through to WriteNibble; ret from WriteNibble is ret from WriteByte too. |
; Convert nibble in al to hexadecimal text in edi, advance edi. |
WriteNibble: |
; Obvious, isn't it? |
cmp al, 10 |
sbb al, 69h |
das |
stosb |
ret |
if use_predefined_mode = 0 |
; detect resolution of the flat panel |
DetectMode: |
push esi edi |
; 1. Get the location of block of GMBUS* registers. |
; Starting with Ironlake, GMBUS* registers were moved. |
add esi, 5100h |
cmp [deviceType], ironlake_start |
jb @f |
add esi, 0xC0000 |
@@: |
; 2. Initialize GMBUS engine. |
mov edi, edid |
mov ecx, 0x10000 |
@@: |
test byte [esi+8+1], 80h |
loopnz @b |
jnz .fail |
mov dword [esi], 3 |
test byte [esi+8+1], 4 |
jz .noreset |
call ResetGMBus |
jnz .fail |
.noreset: |
; 3. Send read command. |
and dword [esi+20h], 0 |
mov dword [esi+4], 4E8000A1h |
; 4. Wait for data, writing to the buffer as data arrive. |
.getdata: |
mov ecx, 0x10000 |
@@: |
test byte [esi+8+1], 8 |
loopz @b |
test byte [esi+8+1], 4 |
jz .dataok |
call ResetGMBus |
jmp .fail |
.dataok: |
mov eax, [esi+0Ch] |
stosd |
cmp edi, edid+80h |
jb .getdata |
; 5. Wait for bus idle. |
mov ecx, 0x10000 |
@@: |
test byte [esi+8+1], 2 |
loopnz @b |
; 6. We got EDID; dump it if DEBUG. |
if DEBUG |
pusha |
xor ecx, ecx |
mov esi, edid |
mov edi, edid_text |
.dumploop: |
lodsb |
call WriteByte |
mov al, ' ' |
stosb |
inc cl |
test cl, 15 |
jnz @f |
mov byte [edi-1], 13 |
mov al, 10 |
stosb |
@@: |
test cl, cl |
jns .dumploop |
mov esi, edidmsg |
call SysMsgBoardStr |
popa |
end if |
; 7. Test whether EDID is good. |
; 7a. Signature: 00 FF FF FF FF FF FF 00. |
mov esi, edid |
cmp dword [esi], 0xFFFFFF00 |
jnz .fail |
cmp dword [esi+4], 0x00FFFFFF |
jnz .fail |
; 7b. Checksum must be zero. |
xor edx, edx |
mov ecx, 80h |
@@: |
lodsb |
add dl, al |
loop @b |
jnz .fail |
; 8. Get width and height from EDID. |
xor eax, eax |
mov ah, [esi-80h+3Ah] |
shr ah, 4 |
mov al, [esi-80h+38h] |
mov [width], eax |
mov ah, [esi-80h+3Dh] |
shr ah, 4 |
mov al, [esi-80h+3Bh] |
mov [height], eax |
; 9. Return. |
.fail: |
pop edi esi |
ret |
; reset bus, clear all errors |
ResetGMBus: |
; look into the PRM |
mov dword [esi+4], 80000000h |
mov dword [esi+4], 0 |
mov ecx, 0x10000 |
@@: |
test byte [esi+8+1], 2 |
loopnz @b |
ret |
end if |
; set resolution [width]*[height] |
SetMode: |
; 1. Program the registers of videocard. |
; look into the PRM |
cli |
; or byte [esi+7000Ah], 0Ch ; PIPEACONF: disable Display+Cursor Planes |
; or byte [esi+7100Ah], 0Ch ; PIPEBCONF: disable Display+Cursor Planes |
xor eax, eax |
xor edx, edx |
cmp [deviceType], i965_start |
jb @f |
mov dl, 9Ch - 84h |
@@: |
; or byte [esi+71403h], 80h ; VGACNTRL: VGA Display Disable |
and byte [esi+70080h], not 27h ; CURACNTR: disable cursor A |
mov dword [esi+70084h], eax ; CURABASE: force write to CURA* regs |
and byte [esi+700C0h], not 27h ; CURBCNTR: disable cursor B |
mov dword [esi+700C4h], eax ; CURBBASE: force write to CURB* regs |
and byte [esi+70183h], not 80h ; DSPACNTR: disable Primary A Plane |
mov dword [esi+edx+70184h], eax ; DSPALINOFF/DSPASURF: force write to DSPA* regs |
and byte [esi+71183h], not 80h ; DSPBCNTR: disable Primary B Plane |
mov dword [esi+edx+71184h], eax ; DSPBLINOFF/DSPBSURF: force write to DSPB* regs |
if 1 |
cmp [deviceType], ironlake_start |
jae .disable_pipes |
mov edx, 10000h |
or byte [esi+70024h], 2 ; PIPEASTAT: clear VBLANK status |
or byte [esi+71024h], 2 ; PIPEBSTAT: clear VBLANK status |
.wait_vblank_preironlake1: |
mov ecx, 1000h |
loop $ |
test byte [esi+7000Bh], 80h ; PIPEACONF: pipe A active? |
jz @f |
test byte [esi+70024h], 2 ; PIPEASTAT: got VBLANK? |
jz .wait_vblank_preironlake2 |
@@: |
test byte [esi+7100Bh], 80h ; PIPEBCONF: pipe B active? |
jz .disable_pipes |
test byte [esi+71024h], 2 ; PIPEBSTAT: got VBLANK? |
jnz .disable_pipes |
.wait_vblank_preironlake2: |
dec edx |
jnz .wait_vblank_preironlake1 |
jmp .not_disabled |
.disable_pipes: |
end if |
and byte [esi+7000Bh], not 80h ; PIPEACONF: disable pipe |
and byte [esi+7100Bh], not 80h ; PIPEBCONF: disable pipe |
cmp [deviceType], gen4_start |
jb .wait_watching_scanline |
; g45 and later: use special flag from PIPE*CONF |
mov edx, 10000h |
@@: |
mov ecx, 1000h |
loop $ |
test byte [esi+7000Bh], 40h ; PIPEACONF: wait until pipe disabled |
jz @f |
dec edx |
jnz @b |
jmp .not_disabled |
@@: |
test byte [esi+7100Bh], 40h ; PIPEBCONF: wait until pipe disabled |
jz .disabled |
mov ecx, 1000h |
loop $ |
dec edx |
jnz @b |
jmp .not_disabled |
; pineview and before: wait while scanline still changes |
.wait_watching_scanline: |
mov edx, 1000h |
.dis1: |
push dword [esi+71000h] |
push dword [esi+70000h] |
mov ecx, 10000h |
loop $ |
pop eax |
xor eax, [esi+70000h] |
and eax, 1FFFh |
pop eax |
jnz .notdis1 |
xor eax, [esi+71000h] |
and eax, 1FFFh |
jz .disabled |
.notdis1: |
dec edx |
jnz .dis1 |
.not_disabled: |
sti |
jmp .return |
.disabled: |
lea eax, [esi+61183h] |
cmp [deviceType], ironlake_start |
jb @f |
add eax, 0xE0000 - 0x60000 |
@@: |
lea edx, [esi+60000h] |
test byte [eax], 40h |
jz @f |
add edx, 1000h |
@@: |
mov eax, [width] |
dec eax |
shl eax, 16 |
mov ax, word [height] |
dec eax |
mov dword [edx+1Ch], eax ; PIPEASRC: set source image size |
ror eax, 16 |
mov dword [edx+10190h], eax ; for old cards |
mov ecx, [width] |
add ecx, 15 |
and ecx, not 15 |
shl ecx, 2 |
mov dword [edx+10188h], ecx ; DSPASTRIDE: set scanline length |
mov dword [edx+10184h], 0 ; DSPALINOFF: force write to DSPA* registers |
and byte [esi+61233h], not 80h ; PFIT_CONTROL: disable panel fitting |
or byte [edx+1000Bh], 80h ; PIPEACONF: enable pipe |
; and byte [edx+1000Ah], not 0Ch ; PIPEACONF: enable Display+Cursor Planes |
or byte [edx+10183h], 80h ; DSPACNTR: enable Display Plane A |
sti |
; 2. Notify the kernel that resolution has changed. |
call GetDisplay |
mov edx, [width] |
mov dword [eax+8], edx |
mov edx, [height] |
mov dword [eax+0Ch], edx |
mov [eax+18h], ecx |
mov eax, [width] |
dec eax |
dec edx |
call SetScreen |
.return: |
ret |
align 4 |
hellomsg db 'Intel videocard detected, PciId=8086:' |
pciid_text db '0000' |
db ', which is ', 0 |
knownmsg db 'known',13,10,0 |
unknownmsg db 'unknown',13,10,0 |
if DEBUG |
edidmsg db 'EDID successfully read:',13,10 |
edid_text rb 8*(16*3+1) |
db 0 |
end if |
version: |
dd 0x50005 |
width dd predefined_width |
height dd predefined_height |
pciids: |
dw 0x3577 ; i830m |
dw 0x2562 ; 845g |
dw 0x3582 ; i855gm |
i865_start = ($ - pciids) / 2 |
dw 0x2572 ; i865g |
i9xx_start = ($ - pciids) / 2 |
dw 0x2582 ; i915g |
dw 0x258a ; e7221g (i915g) |
dw 0x2592 ; i915gm |
dw 0x2772 ; i945g |
dw 0x27a2 ; i945gm |
dw 0x27ae ; i945gme |
i965_start = ($ - pciids) / 2 |
dw 0x2972 ; i946qz (i965g) |
dw 0x2982 ; g35g (i965g) |
dw 0x2992 ; i965q (i965g) |
dw 0x29a2 ; i965g |
dw 0x29b2 ; q35g |
dw 0x29c2 ; g33g |
dw 0x29d2 ; q33g |
dw 0xa001 ; pineview |
dw 0xa011 ; pineview |
gen4_start = ($ - pciids) / 2 |
dw 0x2a02 ; i965gm |
dw 0x2a12 ; i965gm |
dw 0x2a42 ; gm45 |
dw 0x2e02 ; g45 |
dw 0x2e12 ; g45 |
dw 0x2e22 ; g45 |
dw 0x2e32 ; g45 |
dw 0x2e42 ; g45 |
dw 0x2e92 ; g45 |
ironlake_start = ($ - pciids) / 2 |
dw 0x0042 ; ironlake_d |
dw 0x0046 ; ironlake_m |
dw 0x0102 ; sandybridge_d |
dw 0x0112 ; sandybridge_d |
dw 0x0122 ; sandybridge_d |
dw 0x0106 ; sandybridge_m |
dw 0x0116 ; sandybridge_m |
dw 0x0126 ; sandybridge_m |
dw 0x010A ; sandybridge_d |
pciids_num = ($ - pciids) / 2 |
align 4 |
deviceType dd ? |
edid rb 0x80 |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/drivers/tmpdisk_fat.inc |
---|
0,0 → 1,327 |
; FAT-specific code for tmpdisk.asm. |
; Formats a disk to FAT16 or FAT32, depending on size. |
; Note: formatting is adjusted for memory-based disks. Although the resulting |
; image is a valid FAT32 volume, it has no "spare" sectors, e.g. second copy |
; of FAT or place for second sector of MS FAT32 bootloader. |
; Some constants |
FAT16_ROOTDIR_SECTORS = 16 ; can be changed, but why not? |
; FAT16: |
; 1 bootsector, |
; min 0xFF5 sectors for data, |
; min (0xFF5*2/512) = 16 sectors per FAT, we use only one copy, |
; FAT16_ROOTDIR_SECTORS for root directory |
MIN_FAT16_SIZE = 1 + 16 + FAT16_ROOTDIR_SECTORS + 0xFF5 |
; FAT32: |
; 1 bootsector, |
; 1 sector for fsinfo, |
; min 0xFFF5 sectors for data, |
; min (0xFFF5*4/512) = 512 sectors per FAT, we use only one copy |
MIN_FAT32_SIZE = 1 + 1 + 512 + 0xFFF5 |
MAX_SIZE = 1 shl (30 - 9) ; 1G in 512-byte sectors |
; Initializes FATxx structures on the disk. |
; Called with edi = pointer to disk data, esi = size of disk. |
proc format_disk |
; Determine FAT type and jump to the corresponding handler. |
cmp esi, MIN_FAT32_SIZE |
jae format_disk_fat32 |
; Fall through to format_disk_fat16. |
endp |
; Structure of FAT16 bootsector. Field names are from MS spec. |
struc FAT16BOOT |
{ |
.BS_jmpBoot rb 3 |
.BS_OEMName rb 8 |
.BPB_BytsPerSec dw ? |
.BPB_SecsPerClus db ? |
.BPB_RsvdSecCnt dw ? |
.BPB_NumFATs db ? |
.BPB_RootEntCnt dw ? |
.BPB_TotSec16 dw ? |
.BPB_Media db ? |
.BPB_FATSz16 dw ? |
.BPB_SecPerTrk dw ? |
.BPB_NumHeads dw ? |
.BPB_HiddSec dd ? |
.BPB_TotSec32 dd ? |
.BS_DrvNum db ? |
.BS_Reserved1 db ? |
.BS_BootSig db ? |
.BS_VolID dd ? |
.BS_VolLab rb 11 |
.BS_FilSysType rb 8 |
} |
virtual at 0 |
FAT16BOOT FAT16BOOT |
end virtual |
; Initializes FAT16 structures on the disk. |
; Called with edi = pointer to disk data, esi = size of disk. |
format_disk_fat16: |
; 1. Calculate number of clusters. |
; 1a. There are fixed-sized areas and there are data+FAT; |
; every cluster uses 512 bytes in data area and 2 bytes in FAT area. |
lea eax, [esi-1-FAT16_ROOTDIR_SECTORS] |
; two following lines are equivalent to edx = floor(eax*512/514) |
mov ecx, 0xFF00FF01 |
mul ecx ; edx = number of clusters |
; 1b. Force the number be less than 0xfff5. |
mov eax, 0xFFF4 |
cmp edx, eax |
jb @f |
mov edx, eax |
@@: |
; 2. Zero all system areas on the disk. |
lea ecx, [256*(1+FAT16_ROOTDIR_SECTORS)+edx+255] |
and ecx, not 255 |
shr ecx, 1 |
xor eax, eax |
push edi |
rep stosd |
pop edi |
; 3. Generate the bootsector. |
; 3a. Copy static stub. |
push esi edi |
mov esi, fat16bootsector_stub |
mov ecx, fat16bootsector_stub_size |
rep movsb |
pop edi esi |
mov word [edi+510], 0xAA55 |
; 3b. Set fields which depend on size. |
cmp esi, 0x10000 |
jae .size_is_32bit |
mov [edi+FAT16BOOT.BPB_TotSec16], si |
jmp .size_written |
.size_is_32bit: |
mov [edi+FAT16BOOT.BPB_TotSec32], esi |
.size_written: |
lea eax, [edx+255] |
shr eax, 8 |
mov [edi+FAT16BOOT.BPB_FATSz16], ax |
; 3c. Generate volume ID. |
call generate_volume_id |
mov [edi+FAT16BOOT.BS_VolID], eax |
; 4. Initialize FAT. |
mov dword [edi+512], 0xFFFFFFF8 |
; 5. Return. |
ret |
; Structure of FAT32 bootsector. Field names are from MS spec. |
struc FAT32BOOT |
{ |
.BS_jmpBoot rb 3 |
.BS_OEMName rb 8 |
.BPB_BytsPerSec dw ? |
.BPB_SecsPerClus db ? |
.BPB_RsvdSecCnt dw ? |
.BPB_NumFATs db ? |
.BPB_RootEntCnt dw ? |
.BPB_TotSec16 dw ? |
.BPB_Media db ? |
.BPB_FATSz16 dw ? |
.BPB_SecPerTrk dw ? |
.BPB_NumHeads dw ? |
.BPB_HiddSec dd ? |
.BPB_TotSec32 dd ? |
.BPB_FATSz32 dd ? |
.BPB_ExtFlags dw ? |
.BPB_FSVer dw ? |
.BPB_RootClus dd ? |
.BPB_FSInfo dw ? |
.BPB_BkBootSec dw ? |
.BPB_Reserved rb 12 |
.BS_DrvNum db ? |
.BS_Reserved1 db ? |
.BS_BootSig db ? |
.BS_VolID dd ? |
.BS_VolLab rb 11 |
.BS_FilSysType rb 8 |
} |
virtual at 0 |
FAT32BOOT FAT32BOOT |
end virtual |
; Initializes FAT32 structures on the disk. |
; Called with edi = pointer to disk data, esi = size of disk. |
format_disk_fat32: |
; 1. Calculate number of clusters. |
; 1a. There is fixed-sized area and there are data+FAT; |
; every cluster uses 512 bytes in data area and 4 bytes in FAT area. |
lea eax, [esi-1-1] |
; two following lines are equivalent to edx=floor(eax*512/516) if eax<10000000h |
mov ecx, 0xFE03F810 |
mul ecx ; edx = number of clusters |
; 2. Zero all system areas on the disk and first cluster of data, |
; used for root directory. |
lea ecx, [128*(1+1+1)+edx+127] |
and ecx, not 127 |
xor eax, eax |
push edi |
rep stosd |
pop edi |
; 3. Generate the bootsector. |
; 3a. Copy static stub. |
push esi edi |
mov esi, fat32bootsector_stub |
mov ecx, fat32bootsector_stub_size |
rep movsb |
pop edi esi |
mov word [edi+510], 0xAA55 |
; 3b. Set fields which depend on size. |
mov [edi+FAT32BOOT.BPB_TotSec32], esi |
lea eax, [edx+127] |
shr eax, 7 |
mov [edi+FAT32BOOT.BPB_FATSz32], eax |
; 3c. Generate volume ID. |
call generate_volume_id |
mov [edi+FAT32BOOT.BS_VolID], eax |
; 4. Initialize fsinfo sector. |
mov dword [edi+512], 'RRaA' |
mov dword [edi+512+484], 'rrAa' |
dec edx ; one cluster is occupied by root dir |
mov dword [edi+512+488], edx ; free count |
mov byte [edi+512+492], 3 ; first free cluster |
mov word [edi+512+510], 0xAA55 |
; 5. Initialize FAT. |
mov dword [edi+512*2], 0x0FFFFFF8 |
mov dword [edi+512*2+4], 0x0FFFFFFF |
mov dword [edi+512*2+8], 0x0FFFFFFF |
; 6. Return. |
ret |
; Generate volume serial number, which should try to be unique for each volume. |
; Use CMOS date+time, copy-pasted from fat32.inc. |
generate_volume_id: |
call get_time_for_file |
mov cx, ax |
call get_date_for_file |
shl eax, 16 |
mov ax, cx |
ret |
; Three following procedures are copy-pasted from fat32.inc. |
bcd2bin: |
;---------------------------------- |
; input : AL=BCD number (eg. 0x11) |
; output : AH=0 |
; AL=decimal number (eg. 11) |
;---------------------------------- |
xor ah, ah |
shl ax, 4 |
shr al, 4 |
aad |
ret |
get_date_for_file: |
;----------------------------------------------------- |
; Get date from CMOS and pack day,month,year in AX |
; DATE bits 0..4 : day of month 0..31 |
; 5..8 : month of year 1..12 |
; 9..15 : count of years from 1980 |
;----------------------------------------------------- |
mov al, 0x7 ;day |
out 0x70, al |
in al, 0x71 |
call bcd2bin |
ror eax, 5 |
mov al, 0x8 ;month |
out 0x70, al |
in al, 0x71 |
call bcd2bin |
ror eax, 4 |
mov al, 0x9 ;year |
out 0x70, al |
in al, 0x71 |
call bcd2bin |
add ax, 20 ;because CMOS return only the two last |
;digit (eg. 2000 -> 00 , 2001 -> 01) and we |
rol eax, 9 ;need the difference with 1980 (eg. 2001-1980) |
ret |
get_time_for_file: |
;----------------------------------------------------- |
; Get time from CMOS and pack hour,minute,second in AX |
; TIME bits 0..4 : second (the low bit is lost) |
; 5..10 : minute 0..59 |
; 11..15 : hour 0..23 |
;----------------------------------------------------- |
mov al, 0x0 ;second |
out 0x70, al |
in al, 0x71 |
call bcd2bin |
ror eax, 6 |
mov al, 0x2 ;minute |
out 0x70, al |
in al, 0x71 |
call bcd2bin |
ror eax, 6 |
mov al, 0x4 ;hour |
out 0x70, al |
in al, 0x71 |
call bcd2bin |
rol eax, 11 |
ret |
; some data |
fat16bootsector_stub: |
db 0EBh, 3Ch, 90h ; BS_jmpBoot |
db 'KOLIBRI ' ; BS_OEMName |
dw 512 ; BPB_BytsPerSec |
db 1 ; BPB_SecsPerClus |
dw 1 ; BPB_RsvdSecCnt |
db 1 ; BPB_NumFATs |
dw FAT16_ROOTDIR_SECTORS*16 ; BPB_RootEntCnt |
dw 0 ; BPB_TotSec16, filled in format_disk_fat16 |
db 0F8h ; BPB_Media |
dw 0 ; BPB_FATSz16, filled in format_disk_fat16 |
dw 32 ; BPB_SecPerTrk |
dw 128 ; BPB_NumHeads |
dd 0 ; BPB_HiddSec |
dd 0 ; BPB_TotSec32, filled in format_disk_fat16 |
db 80h ; BS_DrvNum |
db 0 ; BS_Reserved1 |
db 29h ; BS_BootSig |
dd 0 ; BS_VolID, filled in format_disk_fat16 |
db 'NO NAME ' ; BS_VolLab |
db 'FAT16 ' ; BS_FilSysType |
; just in case add some meaningful bytes if someone tries to boot |
db 0CDh, 19h, 0EBh, 0FEh ; int 19h, jmp $ |
fat16bootsector_stub_size = $ - fat16bootsector_stub |
fat32bootsector_stub: |
db 0EBh, 58h, 90h ; BS_jmpBoot |
db 'KOLIBRI ' ; BS_OEMName |
dw 512 ; BPB_BytsPerSec |
db 1 ; BPB_SecsPerClus |
dw 2 ; BPB_RsvdSecCnt |
db 1 ; BPB_NumFATs |
dw 0 ; BPB_RootEntCnt |
dw 0 ; BPB_TotSec16 |
db 0F8h ; BPB_Media |
dw 0 ; BPB_FATSz16 |
dw 32 ; BPB_SecPerTrk |
dw 128 ; BPB_NumHeads |
dd 0 ; BPB_HiddSec |
dd 0 ; BPB_TotSec32, filled in format_disk_fat32 |
dd 0 ; BPB_FATSz32, filled in format_disk_fat32 |
dw 0 ; BPB_ExtFlags |
dw 0 ; BPB_FSVer |
dd 2 ; BPB_RootClus |
dw 1 ; BPB_FSInfo |
dw 0 ; BPB_BkBootSec |
rb 12 ; BPB_Reserved |
db 80h ; BS_DrvNum |
db 0 ; BS_Reserved1 |
db 29h ; BS_BootSig |
dd 0 ; BS_VolID, filled in format_disk_fat32 |
db 'NO NAME ' ; BS_VolLab |
db 'FAT32 ' ; BS_FilSysType |
; same bytes as in fat16bootsector_stub |
db 0CDh, 19h, 0EBh, 0FEh ; int 19h, jmp $ |
fat32bootsector_stub_size = $ - fat32bootsector_stub |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/drivers/tmpdisk.asm |
---|
0,0 → 1,295 |
; Disk driver to create FAT16/FAT32 memory-based temporary disk aka RAM disk. |
; (c) CleverMouse |
; Note: in the ideal world, a disk driver should not care about a file system |
; on it. In the current world, however, there is no way to format a disk in |
; FAT, so this part of file-system-specific operations is included in the |
; driver. |
; When this driver is loading, it registers itself in the system and does |
; nothing more. When loaded, this driver controls pseudo-disk devices |
; named /tmp#/, where # is a digit from 0 to 9. The driver does not create |
; any device by itself, waiting for instructions from an application. |
; The driver responds to the following IOCTLs from a control application: |
SRV_GETVERSION equ 0 ; input ignored, |
; output = dword API_VERSION |
DEV_ADD_DISK equ 1 ; input = structure add_disk_struc, |
; no output |
DEV_DEL_DISK equ 2 ; input = structure del_disk_struc, |
; no output |
; For all IOCTLs the driver returns one of the following error codes: |
NO_ERROR equ 0 |
ERROR_INVALID_IOCTL equ 1 ; unknown IOCTL code, wrong input/output size... |
ERROR_INVALID_ID equ 2 ; .DiskId must be from 0 to 9 |
ERROR_SIZE_TOO_LARGE equ 3 ; .DiskSize is too large |
ERROR_SIZE_TOO_SMALL equ 4 ; .DiskSize is too small |
ERROR_NO_MEMORY equ 5 ; memory allocation failed |
API_VERSION equ 1 |
; Input structures: |
struc add_disk_struc |
{ |
.DiskSize dd ? ; disk size in sectors, 1 sector = 512 bytes |
; Note: .DiskSize is the full size, including FAT service data. |
; Size for useful data is slightly less than this number. |
.DiskId db ? ; from 0 to 9 |
.sizeof: |
} |
virtual at 0 |
add_disk_struc add_disk_struc |
end virtual |
struc del_disk_struc |
{ |
.DiskId db ? ; from 0 to 9 |
.sizeof: |
} |
virtual at 0 |
del_disk_struc del_disk_struc |
end virtual |
max_num_disks equ 10 |
; standard driver stuff |
format MS COFF |
DEBUG equ 0 |
include 'proc32.inc' |
include 'imports.inc' |
public START |
public version |
struc IOCTL |
{ |
.handle dd ? |
.io_code dd ? |
.input dd ? |
.inp_size dd ? |
.output dd ? |
.out_size dd ? |
} |
virtual at 0 |
IOCTL IOCTL |
end virtual |
section '.flat' code readable align 16 |
; the start procedure (see the description above) |
proc START |
; This procedure is called in two situations: |
; when the driver is loading and when the system is shutting down. |
; 1. Check that the driver is loading; do nothing unless so. |
xor eax, eax ; set return value in case we will do nothing |
cmp dword [esp+4], 1 |
jne .nothing |
; 2. Register the driver in the system. |
stdcall RegService, my_service, service_proc |
; 3. Return the value returned by RegService back to the system. |
.nothing: |
retn 4 |
endp |
; Service procedure for the driver - handle all IOCTL requests for the driver. |
; The description of handled IOCTLs is located in the start of this file. |
proc service_proc |
; 1. Save used registers to be stdcall. |
; Note: this shifts esp, so the first parameter [esp+4] becomes [esp+16]. |
; Note: edi is used not by this procedure itself, but by worker procedures. |
push ebx esi edi |
; 2. Get parameter from the stack: [esp+16] is the first parameter, |
; pointer to IOCTL structure. |
mov edx, [esp+16] ; edx -> IOCTL |
; 3. Set the return value to 'invalid IOCTL'. |
; Now, if one of conditions for IOCTL does not met, the code |
; can simply return the value already loaded. |
mov al, ERROR_INVALID_IOCTL |
; 4. Get request code and select a handler for the code. |
mov ecx, [edx+IOCTL.io_code] |
test ecx, ecx ; check for SRV_GETVERSION |
jnz .no.srv_getversion |
; 4. This is SRV_GETVERSION request, no input, 4 bytes output, API_VERSION. |
; 4a. Output size must be at least 4 bytes. |
cmp [edx+IOCTL.out_size], 4 |
jl .return |
; 4b. Write result to the output buffer. |
mov eax, [edx+IOCTL.output] |
mov dword [eax], API_VERSION |
; 4c. Return success. |
xor eax, eax |
jmp .return |
.no.srv_getversion: |
dec ecx ; check for DEV_ADD_DISK |
jnz .no.dev_add_disk |
; 5. This is DEV_ADD_DISK request, input is add_disk_struc, output is 1 byte |
; 5a. Input size must be exactly add_disk_struc.sizeof bytes. |
cmp [edx+IOCTL.inp_size], add_disk_struc.sizeof |
jnz .return |
; 5b. Load input parameters and call the worker procedure. |
mov eax, [edx+IOCTL.input] |
movzx ebx, [eax+add_disk_struc.DiskId] |
mov esi, [eax+add_disk_struc.DiskSize] |
call add_disk |
; 5c. Return back to the caller the value from the worker procedure. |
jmp .return |
.no.dev_add_disk: |
dec ecx ; check for DEV_DEL_DISK |
jnz .return |
; 6. This is DEV_DEL_DISK request, input is del_disk_struc |
; 6a. Input size must be exactly del_disk_struc.sizeof bytes. |
cmp [edx+IOCTL.inp_size], del_disk_struc.sizeof |
jnz .return |
; 6b. Load input parameters and call the worker procedure. |
mov eax, [edx+IOCTL.input] |
movzx ebx, [eax+del_disk_struc.DiskId] |
call del_disk |
; 6c. Return back to the caller the value from the worker procedure. |
.return: |
; 7. Exit. |
; 7a. The code above returns a value in al for efficiency, |
; propagate it to eax. |
movzx eax, al |
; 7b. Restore used registers to be stdcall. |
pop edi esi ebx |
; 7c. Return, popping one argument. |
retn 4 |
endp |
; The worker procedure for DEV_ADD_DISK request. |
; Creates a memory-based disk of given size and formats it in FAT16/32. |
; Called with ebx = disk id, esi = disk size, |
; returns error code in al. |
proc add_disk |
; 1. Check that disk id is correct and free. |
; Otherwise, return the corresponding error code. |
mov al, ERROR_INVALID_ID |
cmp ebx, max_num_disks |
jae .return |
cmp [disk_pointers+ebx*4], 0 |
jnz .return |
; 2. Check that the size is reasonable. |
; Otherwise, return the corresponding error code. |
mov al, ERROR_SIZE_TOO_LARGE |
cmp esi, MAX_SIZE |
ja .return |
mov al, ERROR_SIZE_TOO_SMALL |
cmp esi, MIN_FAT16_SIZE |
jb .return |
; 3. Allocate memory for the disk, store the pointer in edi. |
; If failed, return the corresponding error code. |
mov eax, esi |
shl eax, 9 |
stdcall KernelAlloc, eax |
mov edi, eax |
test eax, eax |
mov al, ERROR_NO_MEMORY |
jz .return |
; 4. Store the pointer and the size in the global variables. |
; It is possible, though very unlikely, that two threads |
; have called this function in parallel with the same id, |
; so [disk_pointers+ebx*4] could be filled by another thread. |
; Play extra safe and store new value only if old value is zero. |
xor eax, eax |
lock cmpxchg [disk_pointers+ebx*4], edi |
jz @f |
; Otherwise, free the allocated memory and return the corresponding error code. |
stdcall KernelFree, edi |
mov al, ERROR_INVALID_ID |
jmp .return |
@@: |
mov [disk_sizes+ebx*4], esi |
; 5. Call the worker procedure for formatting this disk. |
; It should not fail. |
call format_disk |
; 6. Register the disk in the system. |
; 6a. Generate name as /tmp#, where # = ebx + '0'. Use two dwords in the stack. |
push 0 |
push 'tmp' |
mov eax, esp ; eax points to 'tmp' + zero byte + zero dword |
lea ecx, [ebx+'0'] ; ecx = digit |
mov [eax+3], cl ; eax points to 'tmp#' + zero dword |
; 6b. Call the kernel API. Use disk id as 'userdata' parameter for callbacks. |
stdcall DiskAdd, disk_functions, eax, ebx, 0 |
; 6c. Restore the stack after 6a. |
pop ecx ecx |
; 6c. Check the result. If DiskAdd has failed, cleanup and return |
; ERROR_NO_MEMORY, this is the most probable or even the only reason to fail. |
test eax, eax |
jnz @f |
mov [disk_sizes+ebx*4], 0 |
mov [disk_pointers+ebx*4], 0 |
stdcall KernelFree, edi |
mov al, ERROR_NO_MEMORY |
jmp .return |
@@: |
push eax |
; 6d. Notify the kernel that media is inserted. |
stdcall DiskMediaChanged, eax, 1 |
; 6e. Disk is fully configured; store its handle in the global variable |
; and return success. |
pop [disk_handles+ebx*4] |
xor eax, eax |
; 7. Return. |
.return: |
retn |
endp |
; The worker procedure for DEV_DEL_DISK request. |
; Deletes a previously created memory-based disk. |
; Called with ebx = disk id, |
; returns error code in al. |
proc del_disk |
; 1. Check that disk id is correct. |
; Otherwise, return the corresponding error code. |
mov al, ERROR_INVALID_ID |
cmp ebx, max_num_disks |
jae .return |
; 2. Get the disk handle, simultaneously clearing the global variable. |
xor edx, edx |
xchg edx, [disk_handles+ebx*4] |
; 3. Check that the handle is non-zero. |
; Otherwise, return the corresponding error code. |
test edx, edx |
jz .return |
; 4. Delete the disk from the system. |
stdcall DiskDel, edx |
; 5. Return success. |
; Note that we can't free memory yet; it will be done in tmpdisk_close. |
xor eax, eax |
.return: |
retn |
endp |
; Include implementation of tmpdisk_* callbacks. |
include 'tmpdisk_work.inc' |
; Include FAT-specific code. |
include 'tmpdisk_fat.inc' |
; initialized data |
align 4 |
disk_functions: |
dd disk_functions_end - disk_functions |
dd tmpdisk_close |
dd 0 ; no need in .closemedia |
dd tmpdisk_querymedia |
dd tmpdisk_read |
dd tmpdisk_write |
dd 0 ; no need in .flush |
dd tmpdisk_adjust_cache_size |
disk_functions_end: |
; disk_handles = array of values for Disk* kernel functions |
label disk_handles dword |
times max_num_disks dd 0 |
; disk_pointers = array of pointers to disk data |
label disk_pointers dword |
times max_num_disks dd 0 |
; disk_sizes = array of disk sizes |
label disk_sizes dword |
times max_num_disks dd 0 |
version dd 0x00060006 |
my_service db 'tmpdisk',0 |
; uninitialized data |
; actually, not used here |
;section '.data' data readable writable align 16 ; standard driver stuff |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/drivers/tmpdisk_work.inc |
---|
0,0 → 1,144 |
; Callbacks which implement tmpdisk-specific disk functions for tmpdisk.asm. |
; The first argument of every callback is .userdata = userdata arg of AddDisk. |
; For tmpdisk, .userdata is the disk id, one of 0,...,max_num_disks-1. |
DISK_STATUS_OK = 0 ; success |
DISK_STATUS_GENERAL_ERROR = -1; if no other code is suitable |
DISK_STATUS_INVALID_CALL = 1 ; invalid input parameters |
DISK_STATUS_NO_MEDIA = 2 ; no media present |
DISK_STATUS_END_OF_MEDIA = 3 ; end of media while reading/writing data |
; The last function that is called for the given disk. The kernel calls it when |
; the kernel has finished all operations with the disk and it is safe to free |
; all driver-specific data identified by 'userdata'. |
proc tmpdisk_close |
virtual at esp+4 |
.userdata dd ? |
end virtual |
; Free the memory for disk and zero global variables. |
mov edx, [.userdata] |
mov [disk_sizes+edx*4], 0 |
xor eax, eax |
xchg eax, [disk_pointers+edx*4] |
stdcall KernelFree, eax |
retn 4 |
endp |
struc DISKMEDIAINFO |
{ |
.flags dd ? |
DISK_MEDIA_READONLY = 1 |
.sectorsize dd ? |
.capacity dq ? |
} |
virtual at 0 |
DISKMEDIAINFO DISKMEDIAINFO |
end virtual |
; Returns information about disk media. |
proc tmpdisk_querymedia |
virtual at esp+4 |
.userdata dd ? |
.info dd ? |
end virtual |
; Media is always present, sector size is always 512 bytes, |
; the size of disk in sectors is stored in a global variable. |
mov edx, [.userdata] |
mov ecx, [.info] |
mov [ecx+DISKMEDIAINFO.flags], 0 |
mov [ecx+DISKMEDIAINFO.sectorsize], 512 |
mov eax, [disk_sizes+edx*4] |
mov dword [ecx+DISKMEDIAINFO.capacity], eax |
mov dword [ecx+DISKMEDIAINFO.capacity+4], 0 |
; Return zero as an indicator of success. |
xor eax, eax |
retn 8 |
endp |
; Reads one or more sectors from the device. |
tmpdisk_read: |
xor edx, edx ; 0 = reading |
jmp tmpdisk_readwrite |
; Writes one or more sectors to the device. |
tmpdisk_write: |
mov dl, 1 ; 1 = writing |
; Fall through to tmpdisk_readwrite. |
; Common procedure for reading and writing. |
; dl = 0 for reading, dl = 1 for writing. |
; Arguments of tmpdisk_read and tmpdisk_write are the same, |
; they continue to be stack arguments of this procedure. |
proc tmpdisk_readwrite \ |
userdata:dword, \ |
buffer:dword, \ |
start_sector:qword, \ |
numsectors_ptr:dword |
; 1. Save used registers to be stdcall. |
push esi edi |
mov esi, [userdata] |
mov edi, [numsectors_ptr] |
; 1. Determine number of sectors to be transferred. |
; This is either the requested number of sectors or number of sectors |
; up to the disk boundary, depending of what is less. |
xor ecx, ecx |
; 1a. Test whether [start_sector] is less than [disk_sizes] for selected disk. |
; If so, calculate number of sectors between [start_sector] and [disk_sizes]. |
; Otherwise, the actual number of sectors is zero. |
cmp dword [start_sector+4], ecx |
jnz .got_number |
mov eax, [disk_sizes+esi*4] |
sub eax, dword [start_sector] |
jbe .got_number |
; 1b. Get the requested number of sectors. |
mov ecx, [edi] |
; 1c. If it is greater than number of sectors calculated in 1a, use the value |
; from 1a. |
cmp ecx, eax |
jb .got_number |
mov ecx, eax |
.got_number: |
; 2. Compare the actual number of sectors with requested. If they are |
; equal, set eax (it will be the returned value) to zero. Otherwise, |
; use DISK_STATUS_END_OF_MEDIA. |
xor eax, eax |
cmp ecx, [edi] |
jz @f |
mov al, DISK_STATUS_END_OF_MEDIA |
@@: |
; 3. Store the actual number of sectors. |
mov [edi], ecx |
; 4. Calculate source and destination addresses. |
mov edi, dword [start_sector] |
shl edi, 9 |
add edi, [disk_pointers+esi*4] |
mov esi, [buffer] |
; 5. Calculate number of dwords to be transferred. |
shl ecx, 9-2 |
; 6. Now esi = [buffer], edi = pointer inside disk. |
; This is normal for write operations; |
; exchange esi and edi for read operations. |
test dl, dl |
jnz @f |
xchg esi, edi |
@@: |
; 7. Copy data. |
rep movsd |
; 8. Restore used registers to be stdcall and return. |
; The value in eax was calculated in step 2. |
pop edi esi |
ret |
endp |
; The kernel calls this function when initializing cache subsystem for |
; the media. This call allows the driver to adjust the cache size. |
proc tmpdisk_adjust_cache_size |
virtual at esp+4 |
.userdata dd ? |
.suggested_size dd ? |
end virtual |
; Since tmpdisk does not need cache, just return 0. |
xor eax, eax |
retn 8 |
endp |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |