flat assembler
Message board for the users of flat assembler.
Index
> OS Construction > Finding the Volume Descriptor |
Author |
|
AnonymousUser 25 Jul 2014, 19:09
Nevermind I got it apparently I needed to load the segment offset address from the DAP into ES:DI and look for the Volume descriptor there
here is what i used to solve it if anyone is interested. Code: mov es, WORD[DiskAddressPacket.Segment] mov di, WORD[DiskAddressPacket.Offset] xor bx, bx .top mov al, BYTE[ES:DI+BX] cmp al, ' ' je .Done mov ah, 0xE int 0x010 inc bx jmp .top .Done: |
|||
25 Jul 2014, 19:09 |
|
smiddy 26 Jul 2014, 01:00
Have tried using FASM?
Here's some code for comparison: Code: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Project : smiddyOS (CD-ROM Booter) ;; ;; Author : smiddy ;; ;; Website : ;; ;; Date : March 8, 2012 ;; ;; Quick Information: Compact Disc bootloader beta to load bootable software. ;; ;; Filename : cdboot2.asm ;; ;; Assembler Command: Using FASMW IDE ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Useage: Using CDBOOT2.BIN as the boot loader from the created ISO from the ;; ;; CD-ROM creating CDROM.ASM from within FASMW IDE. ;; ;; ;; ;; See CDROM.ASM for further details. ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; v1.00 - Beta release; CD-ROM compatible booter, capable of booting bootable ;; ;; software, in this case SMIDDYOS.EXE. Change the file name to one you ;; ;; wish to load instead. The loader expects to be booted at 0060:0000 ;; ;; (600h linear) and the bootable software gets loaded at 0200:0000 ;; ;; (2000h linear) which is launched from 0202:0000 (2020h linear). ;; ;; ;; ;; Limitations include that the boot file needs to be in the root ;; ;; directory and the root directory can not be longer than one CD sector ;; ;; (2048 bytes) in size. ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; format binary as "bin" ; Tell FASM to make an BIN image (removed for ISO building) ORG 0 BootSector: ; Label in order to determine size of code for ; padding at the end. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Start Boot Procedure ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Begin: ; Begining of instructions CLI ; Turn off intterrupts mov ax,0060h ; Boot Segment (no need to copy there) mov ds,ax mov es,ax MOV [CDDriveNumber],DL ; Save boot drive, right away MOV AX,7000h MOV DS,AX ; Segment of saved variables for SmiddyOS MOV BX,530h ; Offset of boot drive saved variable MOV [DS:BX],DL ; Save the boot drive for SmiddyOS use later MOV AX,60h MOV DS,AX ; Reset segment XOR AX,AX ; Zero out the stack segment MOV SS,AX MOV SP,5FEh ; 0000:05FE is the stack for the loader STI ; Turn interrupts back on MOV SI,WelcomeMessage ; Tell everyone hello CALL PrintString ; Print the welcome message MOV SI,TheEndOfTheLine ; Go to the next line CALL PrintString mov si,DriveNumberReported call PrintString mov al,[CDDriveNumber] mov cl,8 call ToHex mov si,HexBuffer call PrintString MOV SI,TheEndOfTheLine ; Go to the next line CALL PrintString MOV AL,[CDDriveNumber] ; Load drive number in for comparison CMP AL,0 ; Compare to known BAD drive number for CD/DVD/BD JE Failure ; If it is, it failed. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Get Status (Bootable CD-ROM) ;; ;; Call with: AX = 4B01H ;; DL = Drive number ;; DS:SI = Empty specification packet (needs to be 13h in size) ;; ;; Return: If Function Successful, ;; Carry Flag = Clear ;; ;; If Function Unsuccessful, ;; Carry Flag = Set ;; AX = Status code ;; ;; DS:SI = Specification packet filled ;; Offset Size Description ;; 00h db size of packet in bytes (13h) ;; 01h db boot media type ;; Bit(s) Description ;; 3-0 media type. ;; 0000 no emulation. ;; 0001 1.2M diskette. ;; 0010 1.44M diskette. ;; 0011 2.88M diskette. ;; 0100 hard disk ;; Other reserved ;; 5-4 reserved (0) ;; 6 image contains ATAPI driver ;; 7 image contains SCSI driver(s) ;; 02h db drive number ;; 00h floppy image ;; 80h bootable hard disk ;; 81h-FFh nonbootable or no emulation drive ;; 03h db CD-ROM controller number ;; 04h dd Logical Block Address (LBA) of disk image to emulate ;; 08h dw device specification (same as boot media type plus) ;; (IDE) bit 0: Drive is slave instead of master ;; (SCSI) bits 7-0: LUN and PUN ;; bits 15-8: Bus number ;; 0Ah dw segment of 3K buffer for caching CD-ROM reads ;; 0Ch dw load segment for initial boot image (if 0000h, load at segment 07C0h; only valid for INT 13h/AH=4Ch) ;; 0Eh dw number of 512-byte virtual sectors to load (only valid for INT 13h/AH=4Ch) ;; 10h db low byte of cylinder count (for INT 13/AH=08h) ;; 11h db sector count, high bits of cylinder count (for INT 13h/AH=08h) ;; 12h db head count (for INT 13h/AH=08h) ;; ;; Comments: ;; ;; The Function is used to get the status of the CD/DVD/BD. This function is called ;; first for "no emulation" booting of optical media. If the Function is successful, ;; the carry flag is clear, else carry flag is set and AX register returns status ;; code. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; GetBootedCDStatus: MOV DL,[CDDriveNumber] ; Prepare to read CD-ROM, load boot drive MOV AH,4Bh ; Use function 4Bh MOV AL,1 MOV SI,DiskResultsBuffer ; Results buffer to load INT 13h ; Call Check Extensions Present? JC Failure ; Need to engage the Failure Procedure MOV AH,255 ; Load proposed error code (255 is our failure code for drive number miss detected) MOV DL,[DiskResultsBuffer+2] ; Load resultant drive information CMP [CDDriveNumber],DL ; Compare to see if they are the same (they should be) JNE Failure ; If they are not, Failure ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; More information can be gathered here if needed later, not needed at this time. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Check INT 13h Extensions Present ;; ;; Call with: AH = 41H ;; DL = Drive number (80h - FFh; but we're interested in CDs for this exercise) ;; BX = 55AAh (Signature?) ;; ;; Return: If Function Successful, ;; Carry Flag = Clear ;; BX = AA55h if installed ;; AH = major version of extensions ;; 01h = 1.x ;; 20h = 2.0 / EDD-1.0 ;; 21h = 2.1 / EDD-1.1 ;; 30h = EDD-3.0 ;; AL = internal use ;; CX = API subset support bitmap ;; 1 - Device Access using packet structure (INT 13h AH=42h-44h,47h,48h) ;; 2 - Drive Locking and Ejecting (INT 13h AH=45h,46h,48h,49h,INT 15h AH=52h) ;; 4 - Enhanced Disk Drive Support (INT 13h AH=48h,AH=4Eh) ;; DH = extension version (v2.0+ ??? -- not present in 1.x) ;; ;; If Function Unsuccessful, ;; Carry Flag = Set ;; AH = 01h (invalid function) ;; ;; Comments: ;; ;; The Function is used to see if INT 13h extensions are present (not really ;; needed if you can do the previous function, right?) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; GetINT13hExtenstions: MOV DL,[CDDriveNumber] ; Prepare to read CD-ROM, load boot drive MOV AH,41h ; Use function 41h: Check Extensions Present MOV BX,55AAh ; Signature? INT 13h ; Call Check Extensions Present? (This makes no sense if you try INT 13h Function 4B01h, but details are ; provided upon return if CF isn't set) JC Failure ; Nope, get out of here... ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Extended Read Function ;; ;; Call with AH=42h ;; DL = drive number ;; DS:SI = Disk Address Packet (DAP) ;; Offset Size Description ;; 00h db Size of packet (10h or 18h; we're using 10h here, see below) ;; 01h db Reserved (0) ;; 02h dw Number of sectors to read (max 007Fh for Phoenix EDD) ;; 04h dd Initial segment:offset where to load the read in sector(S) ;; 08h dq starting absolute sector number (for non-LBA devices, compute as (Cylinder*NumHeads + SelectedHead) * SectorPerTrack + SelectedSector - 1 ;; 10h dq (EDD-3.0, optional) 64-bit flat address of transfer buffer; used if dd at 04h is FFFFh:FFFFh ;; ;; Return: If Function Successful, ;; Carry Flag = Clear ;; AH = 00h ;; ;; If Function Unsuccessful, ;; Carry Flag = Set ;; AH = Error code ;; ;; Error code: ;; 00h successful completion ;; 01h invalid function in AH or invalid parameter ;; 02h address mark not found ;; 03h disk write-protected ;; 04h sector not found/read error ;; 05h reset failed (hard disk) ;; 05h data did not verify correctly (TI Professional PC) ;; 06h disk changed (floppy) ;; 07h drive parameter activity failed (hard disk) ;; 08h DMA overrun ;; 09h data boundary error (attempted DMA across 64K boundary or >80h sectors) ;; 0Ah bad sector detected (hard disk) ;; 0Bh bad track detected (hard disk) ;; 0Ch unsupported track or invalid media ;; 0Dh invalid number of sectors on format (PS/2 hard disk) ;; 0Eh control data address mark detected (hard disk) ;; 0Fh DMA arbitration level out of range (hard disk) ;; 10h uncorrectable CRC or ECC error on read ;; 11h data ECC corrected (hard disk) ;; 20h controller failure ;; 31h no media in drive (IBM/MS INT 13 extensions) ;; 32h incorrect drive type stored in CMOS (Compaq) ;; 40h seek failed ;; 80h timeout (not ready) ;; AAh drive not ready (hard disk) ;; B0h volume not locked in drive (INT 13 extensions) ;; B1h volume locked in drive (INT 13 extensions) ;; B2h volume not removable (INT 13 extensions) ;; B3h volume in use (INT 13 extensions) ;; B4h lock count exceeded (INT 13 extensions) ;; B5h valid eject request failed (INT 13 extensions) ;; B6h volume present but read protected (INT 13 extensions) ;; BBh undefined error (hard disk) ;; CCh write fault (hard disk) ;; E0h status register error (hard disk) ;; FFh sense operation failed (hard disk) ;; ;; Initial DAP: ;; ;; db 10h - Initial Volume Descriptor, which has the important data we need ;; to get to the root directory where our OS is stored. ;; db 0 - Unused, should be zero ;; dw 1 - Number of sectors to read (initial one should be one for Volume Descriptor) ;; dd 0200:0000 - Initial segment:offset where to load the read in sector(S) ;; dq 16 - Starting sector to read in, then we need to get to the root directory ;; ;; Comments: ;; ;; This functions is called three times: Initially to get the Initial ;; Volume Descriptor so we can find the root directory; then it gets used ;; again to read in the root directory, so we can scan for SmiddyOS; finally ;; once we recognize SmiddyOS we load it and jump to it, if all goes as it ;; should "on a good day" as expected (this is done in FindSmiddyOS). ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MOV DL,[CDDriveNumber] ; Set it up again MOV AH,42h ; Read from drive function MOV SI,DiskAddressPacket ; Load SI with address of the Disk Address Packet INT 13h ; Call read sector from drive JC Failure ; Nope, hosed, get out MOV ES,[DiskAddressPacket.Segment] ; This sets up so we can start analyzing the data MOV DI,[DiskAddressPacket.Offset] ; This sets up so we can start analyzing the data MOV BX,40 ; Load BX with Volume Label offset (Check CDROM.ASM or ISO 9660) VolumeLabelLoop: MOV CL,[ES:DI+BX] ; Grab a letter CMP CL,' ' ; Is it a space? (Assumes end of string is space, may run out) JE .VolumeLabelDone ; Yes, we are done MOV [VolumeLabel+BX-40],CL INC BX JMP VolumeLabelLoop ; Need to compare BX to length of Volume Label on CD (32?) .VolumeLabelDone: MOV byte [VolumeLabel+BX-40],0 ; End the string MOV EAX,[ES:DI+158] ; LBA of root directory, where all things start. MOV [DiskAddressPacket.End],EAX ; Load packet with new address on CD of the root directory MOV DL,[CDDriveNumber] ; Set it up again MOV AH,42h ; Read from drive function MOV SI,DiskAddressPacket ; Load SI with address of the Disk Address ; Packet INT 13h ; Call read sector from drive JC Failure ; Nope, hosed, get out CALL FindSmiddyOS MOV AH,0FEh ; If the FindSmiddyOS makes it back here, ; then the file was not found ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Failure: Displays failure with an error code number. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Failure: mov AL,AH ; Put the error into lowest bits mov cl,8 ; only 8 bits call ToHex ; Get what is in the XX to display MOV SI,FailureMessage ; reboot message CALL PrintString mov si,HexBuffer CALL PrintString MOV SI,AddTheHAtTheEnd CALL PrintString MOV SI,TheEndOfTheLine CALL PrintString mov SI,PressAnyKeyMessage CALL PrintString MOV AH,0 ; Reboot on error INT 16h ; BIOS GetKey INT 19h ; BIOS Reboot ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; FindSmiddyOS: Locates SmiddyOS in the root directory of the CD/DVD/BD and launchs ;; ;; it if it finds it. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; NumberOfSectorsToRead: dd 0 FindSmiddyOS: MOV ES,[DiskAddressPacket.Segment] MOV DI,[DiskAddressPacket.Offset] MOV SI,DriveDisplayMessage CALL PrintString CMP byte [VolumeLabel],0 JE .NoLabel MOV SI,VolumeLabel CALL PrintString JMP .LabelDone .NoLabel: MOV SI,HasNoLabel CALL PrintString .LabelDone: MOV SI,TheEndOfTheLine CALL PrintString MOV SI,TheEndOfTheLine CALL PrintString .GrabDirectoryEntry: MOV AL,[ES:DI] ; Length of the current directory entry MOV [DirectoryEntrySize],AL MOV EAX,[ES:DI+2] ; Starting sector of directory entry MOV [FileSector],EAX MOV EAX,[ES:DI+10] ; Size of directory entry on CD/DVD/BD MOV [FileSize],EAX MOV AL,[ES:DI+32] ; File's name length (see El Torito of ISO:9660 or CDROM.ASM) MOV [FileNameLength],AL XOR BX,BX ; Initialize BX XOR CX,CX ; Initialize CX MOV SI,DI ADD SI,33 MOV BX,FileNameEntry MOV AL,[FileNameLength] CMP AL,1 ; Check to see if a dot or dotdot entry JE .DotAndDotDotEntries .LoopFileNameEntry: ; Capture the filename entry for later manipulation MOV AL,[ES:SI] MOV [DS:BX],AL INC BX INC CX INC SI XOR AX,AX MOV AL,[FileNameLength] CMP CX,AX JB .LoopFileNameEntry MOV byte [DS:BX],0 JMP .FileNameEntryDisplay .DotAndDotDotEntries: MOV AL,[FileEntryNumber] CMP AL,0 JA .DoDotDotEntry MOV word [FileNameEntry],'.' ; First directory entry MOV byte [FileNameEntry+1],0 ; End the string with a termination JMP .FileNameEntryDisplay .DoDotDotEntry: MOV word [FileNameEntry],'..' ; Second directory entry MOV byte [FileNameEntry+2],0 ; End the string with a termination .FileNameEntryDisplay: MOV SI,FileNameEntry PUSH DI MOV DI,FileName CALL StringCompare POP DI JC .EndOfTheLine MOV EAX,[FileSector] MOV [DiskAddressPacket.End],EAX ; Save the starting sector into DAP MOV EAX,[FileSize] XOR EDX,EDX MOV EBX,2048 DIV EBX INC EAX MOV [NumberOfSectorsToRead],EAX ; This is the absolute number of sectors ; to be read. CMP AX,32 JA .UpdateSectorsToRead MOV [DiskAddressPacket.SectorsToRead],AX ; Save number of sectors to read JMP .PassedTheUpdate .UpdateSectorsToRead: MOV [DiskAddressPacket.SectorsToRead],word 32 SUB [NumberOfSectorsToRead],dword 32 .PassedTheUpdate: .ReadTheSectorsLoop: MOV DL,[CDDriveNumber] ; Set it up again MOV AH,42h ; Read from drive function MOV SI,DiskAddressPacket ; Load SI with address of the Disk Address Packet INT 13h ; Call read sector from drive JC Failure ; Nope, hosed, get out CMP [NumberOfSectorsToRead], dword 0 JE .StartTheOperatingSystem MOV EAX,[DiskAddressPacket.SectorsToRead] ; Add sectors read (32) to it, for the new ; starting sector ADD [DiskAddressPacket.End],EAX ; Update sector to start reading from on CD/DVD/BD MOV EBX,2048 MUL EBX ; Update EAX with amount of space used SHR EAX,4 ; Shift it right to update the segment to load next ADD [DiskAddressPacket.Segment], AX ; Segment to load next in memory CMP [NumberOfSectorsToRead],dword 32 ; Is this still too big (greater than 64k barrier? JAE .UpdateSectorsToRead ; If so, need to load another 32 sectors MOV EAX,[NumberOfSectorsToRead] ; Else MOV [DiskAddressPacket.SectorsToRead],AX ; We load what is left MOV [NumberOfSectorsToRead],dword 0 ; Zero out this bad boi! JMP .ReadTheSectorsLoop .StartTheOperatingSystem: MOV DL,[CDDriveNumber] ; Drive into DL for booting JMP 0202h:0h ; Start SmiddyOS (For standard DOS executeable) .EndOfTheLine: MOV SI,TheEndOfTheLine CALL PrintString XOR CX,CX ; Prepare CX to do math for DI MOV CL,[DirectoryEntrySize] ; Get the size of the directory entry ADD DI,CX ; Add that size to the DI to get to the next record CMP byte [ES:DI],0 ; Is the next entry = 0? JE .End ; If so, we're at the end of the directory, move on XOR CX,CX MOV BX,FileNameEntry .ClearFileNameEntry: MOV byte [DS:BX],0 ; Erase the begining of the INC BX INC CX CMP CX,254 JB .ClearFileNameEntry MOV byte [DS:BX],0 INC byte [FileEntryNumber] JMP .GrabDirectoryEntry ; If not, we have more processing to do .End: RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; PrintString ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PrintString: PUSHA ; Save Registers .Loop: LODSB ; Load SI into AL, increment SI one byte OR AL,AL ; AL = 0? JZ .Done ; If yes, get out MOV AH,0Eh MOV BH,0 MOV BL,7 ; character attribute INT 10h ; Display character in AL JMP .Loop ; Do it again .Done: POPA ; Replace registers RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ToHex ;; Loads HexBuffer with ASCII corresponding to 8, 16, or 32 bit interger in ;; hex. ;; Requires interger in AL, AX, or EAX depending on bit size ;; Requires the number of bits in the CL ;; Returns a full buffer or an empty buffer on error ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ToHex: pusha MOV BX,0 ; Load BX with pointer offset MOV [TheEAX],EAX ; Save the EAX MOV [TheCL],CL ; Save the CL CMP CL,8 ; Check for 8 bits JNE .Check16 JMP .Loop1 ; Start loading the buffer .Check16: CMP CL,10h ; Check for 16 bits JNE .Check32 JMP .Loop1 ; Start loading the buffer .Check32: CMP CL,20h ; Check for 32 bits JNE .ErrorBits .Loop1: MOV EAX,[TheEAX] ; Reload EAX with the converter SUB CL,4 ; Lower bit count by 4 bits SHR EAX,CL AND AL,0Fh ADD AL,'0' CMP AL,'9' JBE .LoadBuff1 ADD AL,'A'-'0'-10 ; Convert to "A" to "F" JMP .LoadBuff1 .Loop2: MOV EAX,[TheEAX] ; Reload EAX again SUB CL,4 ; Lower bit count by 8 bits SHR EAX,CL AND AL,0Fh ADD AL,'0' CMP AL,'9' JBE .LoadBuff2 ADD AL,'A'-'0'-10 ; Convert A,B,C,D,E,F JMP .LoadBuff2 .LoadBuff1: MOV [HexBuffer+BX],AL ; Load buffer with AL INC BX ; Increment buffer pointer JMP .Loop2 ; Do next byte .LoadBuff2: MOV [HexBuffer+BX],AL ; Load buffer with AL INC BX ; Increment buffer pointer CMP CL,0 ; Check if we're done JNE .Loop1 ; Do next Byte .ErrorBits: MOV AL,0 MOV [HexBuffer+BX],AL ; End the string with a zero popa RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; StringCompare ;; ;; ;; ;; Compares the equality of two strings, loaded in SI and DI. ;; ;; ;; ;; Sets the CF if not the same. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; StringCompare: PUSH SI ; Save ESI to be restored at the end PUSH DI ; Save EDI to be restored at the end .Loop: MOV AL,[SI] ; Grab byte from ESI MOV BL,[DI] ; Grab byte from EDI CMP AL,BL ; Compare if they are equal JNE .NotEqual ; They aren't equal CMP AL,0 ; Both bytes are null JE .Done INC DI ; Increment EDI INC SI ; Increment ESI JMP .Loop ; Start looping .NotEqual: STC ; Set the carry flag to indicate failure JMP .End .Done: CLC ; Clear the carry flag to indicate success .End: POP DI ; Restore EDI before returning to caller POP SI ; Restore ESI before returning to caller RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; StringLength ;; ;; ;; ;; Checks the length of the string in the SI and returns it in the AX. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; StringLength: XOR EAX,EAX .Loop: CMP byte [ESI+EAX],0 JE .End INC EAX JMP .Loop .End: RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Program Data ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DirectoryEntrySize: db 0 FileSector: dd 0 FileSize: dd 0 FileNameLength: db 0 FileNameEntry: TIMES 255 db ' ' FileEntryNumber: db 0 DriveDisplayMessage: db 'Volume in drive: ',0 ; Get label from Primary Volume Descriptor (loaded first) HasNoLabel: db 'no label',0 FileNamePointer: dw 0 DataSector: DW 0 CDDriveNumber: db 0 RootDirectorySector dd 0 FileName: DB 'SMIDDYOS.EXE',0 ; FAT Filename, 8.3, or 11 characters WelcomeMessage: db 13,10,'smiddy OS CD boot v1.00',225,13,10,0 ; Version of CD-Boot FailureMessage: db 'Failure: ',0 DriveNumberReported: db 'Drive number reported: ',0 PressAnyKeyMessage: db 'Press any key to reboot...',13,10,0 AddTheHAtTheEnd: db 'h',0 TheEndOfTheLine: db 13,10,0 ; Used to print the end of the line string SizeOfPacketInBytes: db 'The size of packet in bytes: ',0 BootMediaType: db 'The boot media type: ',0 DriveNumberFromPacket: db 'Drive number from packet: ',0 VolumeLabel: TIMES 33 db 0 ; Where the volume label is stored DiskAddressPacket: db 16,0 .SectorsToRead: dw 1 ; Number of sectors to read (read size of OS) .Offset: dw 0 ; Offset :0000 .Segment: dw 0200h ; Segment 0200: currerntly, will change to 0200:0000 for loading SmiddyOS .End: dq 16 ; Sector 16 or 10h on CD-ROM DiskResultsBuffer: times 30 db 0 ; Return buffer from int 13h ah=48h HexBuffer: DB 0,0,0,0,0,0,0,0,0 ; Buffer for hex text string TheEAX: dd 0 ; Saves EAX TheCL: db 0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 512 Byte Code Boot Signature ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; TIMES 2046-($-BootSector) DB 0 DW 0AA55h ; Bootloading sector signature (for CD/DVD/BD) I am not sure this is needed for optical media booting. |
|||
26 Jul 2014, 01:00 |
|
AnonymousUser 26 Jul 2014, 04:19
Well From what I gather FASM is not all that different from NASM at least not like GAS. And I have seen your code and It has helped me figure out a lot of things. But I am still confused with the volume descriptor and Have a lot of Questions. First How am I supposed to find the root directory how would I go about finding it and then sifting through it to find the next file to load the OS or to do a second stage bootloader? I have been looking everywhere for answers but the NASM forum can only help me so much and I am just starting out with OS DEV.
here is my full code just in case people need to build it: Code: BITS 16 ORG 0x00 Start: jmp main ;Colors for text %DEFINE TEAL 0x03 %DEFINE RED 0x04 %DEFINE PURPLE 0x05 COL: db 0 ROW: db 0 ;macro for print %macro Print 2 pusha xor ax, ax xor dx, dx mov dh, BYTE[ROW];puts the row into the dh register mov dl, BYTE[COL] xor bx, bx mov bl, %2 mov si, %1 call cPrint mov BYTE[COL], dl ;saves the rows for the next time we need to print popa %endmacro Print_ln: pusha mov dh, BYTE[ROW] mov ah, 0x02 ;set cursor pos mov bh, 0x00 ;page 00 inc dh ;row 00 mov dl, 0x00 ;col. 00 int 0x10 mov BYTE[ROW], dh mov BYTE[COL], 0 popa ret cPrint: ; Routine: output string in SI to screen .top: ;Paramaters for Input mov ah, 09h ; Must be 9 to print color mov cx, 0x01 ;x position lodsb ; Get character from string test al, al je .done ; If char is zero, end of string int 0x10 ; Otherwise, print it mov ah, 0x02 ;set cursor position mov bh, 0x00 ;page inc dl ;column int 0x10 ;changes the cursor position so the next char can be written at the new location jmp .top .done: ret ;clears the screen and sets the cursor position to the top left clear: mov ah, 0x0F ;get current video mode mov al, 0x00 ;reset register int 0x10 ;get video mode mov ah, 0x00 ;set video mode int 0x10 ;reset screen mov ah, 0x02 ;set cursor pos mov bh, 0x00 ;page 00 mov dh, 0x00 ;row 00 mov dl, 0x00 ;col. 00 int 0x10 ;set pos ret Read_Sectors: ;/* Read the sector into memory. */ .ForLoop: mov ah,042h xor al,al mov si, DiskAddressPacket mov dl, [CDDriveNumber] int 013h jnc .Success ; /* read error? */ Print Read_Sector_Error_MSG, RED cli hlt .Success: Print Progress_MSG , PURPLE inc WORD[DiskAddressPacket.SectorsToRead] loop .ForLoop call Print_ln ret main: cli mov ax, 0x07c0 ;adjust the segment registers mov ds, ax mov gs, ax mov fs, ax Create_Stack: xor ax, ax mov es, ax mov ss, ax mov sp ,0x0FFFE sti mov [CDDriveNumber],dl call clear Print W_MSG, TEAL;prints the loading message in colour call Print_ln ;First find the Signature of the CD LOAD_SIGNATURE: mov cx, 0x04 call Read_Sectors Print READ_SUCCESS, TEAL call Print_ln ;load the Volume descriptor to the Volume variable mov es, WORD[DiskAddressPacket.Segment] mov di, WORD[DiskAddressPacket.Offset] xor bx, bx .top: mov al, BYTE[ES:DI+BX] mov BYTE[VOLUME+BX], al inc bx cmp al, ' ' je .Done jmp .top .Done: ;see if the Volume descriptor contains the Signature xor BX, BX add BX, 0x01 xor cx, cx .toploop: xor ax, ax mov al, BYTE[VOLUME+BX] cmp al, BYTE[CD_Signature+BX-1] je .FOUND_IT jmp .Done2 inc CX .FOUND_IT: Print Progress_MSG, PURPLE inc BX jmp .toploop .Done2: cmp CX, 0 jne FAIL call Print_ln Print FOUND_CD, TEAL jmp LOAD_ROOT FAIL: Print FILE_NOT_FOUND, RED ;Now Load the Root Directory from the Volume Descriptor LOAD_ROOT: mov es, WORD[DiskAddressPacket.Segment] mov di, WORD[DiskAddressPacket.Offset] call Print_ln MOV AL,[ES:DI] ; Length of the current directory entry MOV [CD_dir_curr_size],AL Print CD_dir_curr_size, TEAL call Print_ln MOV EAX,[ES:DI+2] ; Starting sector of directory entry MOV [CD_root_dir_start],EAX Print CD_root_dir_start, TEAL call Print_ln MOV EAX,[ES:DI+10] ; Size of directory entry on CD/DVD/BD MOV [CD_root_dir_size],EAX Print CD_root_dir_size, TEAL call Print_ln MOV AL,[ES:DI+32] ; File's name length (see El Torito of ISO:9660 or CDROM.ASM) MOV [CD_FileNameLength],AL Print CD_FileNameLength, TEAL call Print_ln XOR BX,BX ; Initialize BX XOR CX,CX ; Initialize CX MOV SI,DI Sector_Size: dw 512 CDDriveNumber: db 0x080 CD_bytes_per_sect: dw 0 CD_root_dir_size: dd 0 CD_root_dir_sectors: dw 0 CD_root_dir_start: dd 0 CD_file_size: dd 0 CD_file_sectors: dw 0 CD_file_start: dd 0 CD_desc_sector: dd 0 CD_Signature: db "CD001" CD_FILE_VER: db 0x01 CD_FileNameLength: db 0x0 CD_dir_curr_size: db 0x0 joliet_signature: db 025h,02Fh,045h ;Disk Address Packet DiskAddressPacket: db 0x010,0 .SectorsToRead: dw 1 ; Number of sectors to read (read size of OS) .Offset: dw 0 ; Offset :0000 .Segment: dw 0x0200 ; Segment 0200 .End: dq 0x010 ; Sector 16 or 10h on CD-ROM VOLUME: DB 0 W_MSG: db "Loading Z-Boot", 0 KERNEL: db "KRNL.BIN" Read_Sector_Error_MSG: db "Error, failed to read sector",0 READ_SUCCESS: db "Sectors read correctly",0 Progress_MSG: db ".",0 FILE_NOT_FOUND: db "Error, file not found",0 FOUND_CD: db "Found the CD Signature", 0 times 2046 - ($ - $$) db 0; padd out the rest of the file to 0 DW 0xAA55; boot signature Here is the link to the other discussion I had on the nasm forum: http://forum.nasm.us/index.php?topic=1938.30 |
|||
26 Jul 2014, 04:19 |
|
AnonymousUser 26 Jul 2014, 05:18
Wow I think I just figured it out thanks for posting your'e code I am still in working on it but I think I am really close
here is my new code: Code: BITS 16 ORG 0x00 Start: jmp main ;Colors for text %DEFINE TEAL 0x03 %DEFINE RED 0x04 %DEFINE PURPLE 0x05 COL: db 0 ROW: db 0 ;macro for print %macro Print 2 pusha xor ax, ax xor dx, dx mov dh, BYTE[ROW];puts the row into the dh register mov dl, BYTE[COL] xor bx, bx mov bl, %2 mov si, %1 call cPrint mov BYTE[COL], dl ;saves the rows for the next time we need to print popa %endmacro Print_ln: pusha mov dh, BYTE[ROW] mov ah, 0x02 ;set cursor pos mov bh, 0x00 ;page 00 inc dh ;row 00 mov dl, 0x00 ;col. 00 int 0x10 mov BYTE[ROW], dh mov BYTE[COL], 0 popa ret cPrint: ; Routine: output string in SI to screen .top: ;Paramaters for Input mov ah, 09h ; Must be 9 to print color mov cx, 0x01 ;x position lodsb ; Get character from string test al, al je .done ; If char is zero, end of string int 0x10 ; Otherwise, print it mov ah, 0x02 ;set cursor position mov bh, 0x00 ;page inc dl ;column int 0x10 ;changes the cursor position so the next char can be written at the new location jmp .top .done: ret ;clears the screen and sets the cursor position to the top left clear: mov ah, 0x0F ;get current video mode mov al, 0x00 ;reset register int 0x10 ;get video mode mov ah, 0x00 ;set video mode int 0x10 ;reset screen mov ah, 0x02 ;set cursor pos mov bh, 0x00 ;page 00 mov dh, 0x00 ;row 00 mov dl, 0x00 ;col. 00 int 0x10 ;set pos ret Read_Sectors: ;/* Read the sector into memory. */ .ForLoop: mov ah,042h xor al,al mov si, DiskAddressPacket mov dl, [CDDriveNumber] int 013h jnc .Success ; /* read error? */ Print Read_Sector_Error_MSG, RED cli hlt .Success: Print Progress_MSG , PURPLE inc WORD[DiskAddressPacket.SectorsToRead] loop .ForLoop call Print_ln ret CHECK_DESC: Print CHECK_DESC_MSG, TEAL mov es, WORD[DiskAddressPacket.Segment] mov di, WORD[DiskAddressPacket.Offset] xor bx, bx .top: mov al, BYTE[ES:DI+BX] mov BYTE[VOLUME+BX], al inc bx cmp al, ' ' je .Done jmp .top .Done: ;see if the Volume descriptor contains the Signature xor BX, BX; clear out bx add BX, 0x01;move into bx the offset xor cx, cx;clear out cx .toploop: xor ax, ax mov al, BYTE[VOLUME+BX] cmp al, BYTE[CD_Signature+BX-1] je .FOUND_IT; Compare the letters Byte by Byte to see if they are the same jmp .Done2 inc CX;increments if even one letter is wrong .FOUND_IT: Print Progress_MSG, PURPLE inc BX;Increments the offset jmp .toploop .Done2: cmp CX, 0;if signatures don't match then stop the system and print an error Message jne .FAIL call Print_ln Print FOUND_CD, TEAL jmp .Done3 .FAIL: Print FILE_NOT_FOUND, RED cli hlt .Done3: call Print_ln ret READ_STAGE2: Print LOADING_STAGE2_MSG, TEAL call Print_ln mov es, [DiskAddressPacket.Segment] mov di, [DiskAddressPacket.Offset] xor BX, BX xor si, si .top: MOV AL,[ES:DI+BX] ;starting address cmp AL,BYTE[STAGE2] je .Done cmp AL,BYTE[STAGE2] je .FAIL INC BX jmp .top .Done: Print Found_Possible_FILE, TEAL call Print_ln XOR SI, SI;Clear out for use ;INC BX ;INC SI xor cx, cx;clear out for use as counter .top2: MOV AL, [ES:DI+BX] MOV AH, 0xE int 0x010 MOV AL, [ES:DI+BX] cmp AL, BYTE[STAGE2+SI] je .Success call Print_ln jmp .top .Success: Print Progress_MSG, PURPLE INC BX INC SI INC CX cmp CX, WORD[STAGE_2_LEN] jne .top2 Print File_Found, TEAL call Print_ln ADD DI, BX ;jump to where the file is located push WORD ES push WORD DI retf .FAIL: call Print_ln Print FILE_NOT_FOUND, RED cli hlt ret main: cli mov ax, 0x07c0 ;adjust the segment registers mov ds, ax mov gs, ax mov fs, ax Create_Stack: xor ax, ax mov es, ax mov ss, ax mov sp ,0x0FFFE sti mov [CDDriveNumber],dl call clear Print W_MSG, TEAL;prints the loading message in colour call Print_ln ;First find the Signature of the CD Print Reading_Sectors, TEAL LOAD_SIGNATURE: mov cx, 0x04 call Read_Sectors Print READ_SUCCESS, TEAL call Print_ln ;load the Volume descriptor to the Volume variable call CHECK_DESC ;Now Load the Root Directory from the Volume Descriptor LOAD_ROOT: ;Print Reading_Sectors, TEAL mov es, WORD[DiskAddressPacket.Segment] mov di, WORD[DiskAddressPacket.Offset] XOR BX, BX MOV BX, 40 ;move in the offset VolumeLabelLoop: MOV CL,[ES:DI+BX] ; Grab a letter CMP CL,' ' ; Is it a space? (Assumes end of string is space, may run out) JE .VolumeLabelDone ; Yes, we are done MOV [VOLUME+BX-40],CL INC BX JMP VolumeLabelLoop ; Need to compare BX to length of Volume Label on CD (32?) .VolumeLabelDone: MOV byte [VOLUME+BX-40],0 ; End the string MOV EAX,[ES:DI+158] ; LBA of root directory, where all things start. MOV [DiskAddressPacket.End],EAX ; Load packet with new address on CD of the root directory xor cx, cx mov cx, 0x01 Print Reading_Sectors, TEAL call Read_Sectors ; Call read sector from drive Print READ_SUCCESS, TEAL;if the program gets here it means it was a success call Print_ln LOAD_STAGE2: call READ_STAGE2 .FAILURE: Print LOADING_STAGE2_FAILED, RED;if it gets here the jump failed call Print_ln Sector_Size: dw 512 CDDriveNumber: db 0x080 CD_bytes_per_sect: dw 0 CD_root_dir_size: dd 0 CD_root_dir_sectors: dw 0 CD_root_dir_start: dd 0 CD_file_size: dd 0 CD_file_sectors: dw 0 CD_file_start: dd 0 CD_desc_sector: dd 0 CD_Signature: db "CD001" CD_FILE_VER: db 0x01 CD_FileNameLength: db 0x0 CD_dir_curr_size: db 0x0 joliet_signature: db 025h,02Fh,045h Reading_Sectors: db "Reading sectors", 0 CHECK_DESC_MSG: db "Checking for CD Signature",0 LOADING_STAGE2_MSG: db "Loading Stage 2 of boot loader",0 STAGE_2_LEN: db 0x0A File_Found: db "File for Stage 2 of the bootloader was successfully loaded!!",0 LOADING_STAGE2_FAILED: db "Failed to load Stage 2 of the boot loader !!!!!",0 Found_Possible_FILE: db "Found Possible File",0 ;Disk Address Packet DiskAddressPacket: db 0x010,0 .SectorsToRead: dw 1 ; Number of sectors to read (read size of OS) .Offset: dw 0 ; Offset :0000 .Segment: dw 0x0200 ; Segment 0200 .End: dq 0x010 ; Sector 16 or 10h on CD-ROM VOLUME: DB 0 W_MSG: db "Loading Z-Boot", 0 STAGE2: db "STAGE2.BIN" Read_Sector_Error_MSG: db "Error, failed to read sector",0 READ_SUCCESS: db "Sectors read correctly!",0 Progress_MSG: db ".",0 FILE_NOT_FOUND: db "Error, file not found!",0 FOUND_CD: db "Found the CD Signature!", 0 times 2046 - ($ - $$) db 0; padd out the rest of the file to 0 DW 0xAA55; boot signature Ok I found out that mkisofs copies the whole directory to the disk so Now all I have to do is find the File but that's proving to be a bit of a pickle when my current bootloader code runs it finds the file well I print out the name of the possible file and then if it isnt the correct one move on but It prints the file I want to jump to but the problem is it moves on it doesen't think it is the file any ideas on how to fix that? |
|||
26 Jul 2014, 05:18 |
|
smiddy 26 Jul 2014, 10:54
Check the specifics of the filenames themselves, if you are not considering UPPER CASE or lower case, or a combination there of, you will not get a string comparison correctly and move on. I don't use mkisofs, but I suspect it is saving your file names to another standard, ISO 9660 (and El Torrito) are very specific about the letter case, on the disc, but there are other implementations, and those implementations need to also be considered, and I suspect that may be your problem, don't know.
I create an ISO from FASM code, at the specifications of ISO 9660, with El Torrito to boot my OS. [reading your code now] Yep, I cannot make heads or tails of your code, I don't know NASM's conventions. I cannot help there. [/reading your code now] |
|||
26 Jul 2014, 10:54 |
|
AnonymousUser 26 Jul 2014, 19:10
Ok I figured out why it wasn't working correctly turns out I need to clear the screen and move onto the next page to actually see the Success Print. I also figured out how to jump to the file which I did or it looks like I did but it's not executing the code at the Adress any Idea as to why?
Code: READ_STAGE2: Print LOADING_STAGE2_MSG, TEAL call Print_ln mov es, [DiskAddressPacket.Segment] mov di, [DiskAddressPacket.Offset] xor BX, BX xor si, si .top: MOV AL,[ES:DI+BX] ;starting address cmp AL,BYTE[STAGE2] je .Done cmp AL,BYTE[STAGE2] je .FAIL INC BX jmp .top .Done: Print Found_Possible_FILE, TEAL call Print_ln XOR SI, SI;Clear out for use ;INC BX ;INC SI xor cx, cx;clear out for use as counter .top2: xor ax, ax MOV AL, BYTE[ES:DI+BX] cmp AL, BYTE[STAGE2+SI] je .Success call Print_ln jmp .top .Success: Print Progress_MSG, PURPLE INC BX INC SI INC CX cmp CX, WORD[STAGE_2_LEN] jne .top2 call clear Print File_Found, TEAL call Print_ln SUB BX, 10 ADD DI, BX Print Reading_Sectors, TEAL mov eax, [ES:DI] mov bx,0x0200 mov WORD[DiskAddressPacket.Segment], BX mov WORD[DiskAddressPacket.Offset], 0 mov cx, 0x04 call Read_Sectors mov dl, [CDDriveNumber] xor si,si Print READ_SUCCESS, TEAL call Print_ln ;/* Run the OS loader... */ jmp 0x200:0x0000 .FAIL: call Print_ln Print FILE_NOT_FOUND, RED cli hlt ret here is the Stage 2 code: Code: [BITS 16] [ORG 0x500] Start: jmp main %DEFINE TEAL 0x03 %DEFINE RED 0x04 %DEFINE PURPLE 0x05 COL: db 0 ROW: db 0 %macro Print 2 pusha xor ax, ax xor dx, dx mov dh, BYTE[ROW];puts the row into the dh register mov dl, BYTE[COL] xor bx, bx mov bl, %2 mov si, %1 call cPrint mov BYTE[COL], dl ;saves the rows for the next time we need to print popa %endmacro Print_ln: pusha mov dh, BYTE[ROW] mov ah, 0x02 ;set cursor pos mov bh, 0x00 ;page 00 inc dh ;row 00 mov dl, 0x00 ;col. 00 int 0x10 mov BYTE[ROW], dh mov BYTE[COL], 0 popa ret cPrint: ; Routine: output string in SI to screen .top: ;Paramaters for Input mov ah, 09h ; Must be 9 to print color mov cx, 0x01 ;x position lodsb ; Get character from string test al, al je .done ; If char is zero, end of string int 0x10 ; Otherwise, print it mov ah, 0x02 ;set cursor position mov bh, 0x00 ;page inc dl ;column int 0x10 ;changes the cursor position so the next char can be written at the new location jmp .top .done: ret ;clears the screen and sets the cursor position to the top left clear: mov ah, 0x0F ;get current video mode mov al, 0x00 ;reset register int 0x10 ;get video mode mov ah, 0x00 ;set video mode int 0x10 ;reset screen mov ah, 0x02 ;set cursor pos mov bh, 0x01 ;page 00 mov dh, 0x00 ;row 00 mov dl, 0x00 ;col. 00 int 0x10 ;set pos MOV BYTE[ROW], DH MOV BYTE[COL], DL ret main: ;first stage of bootloader is loaded at the address 0x07c0:0x0FFFE ;second stage of bootloader is loaded at address 0x9000:0x0FFFF cli mov ax, 0x9000 ;adjust the segment registers mov ds, ax mov gs, ax mov fs, ax Create_Stack: xor ax, ax mov es, ax mov ss, ax mov sp ,0x0FFFF sti call Print_ln Print LOAD_SUCCESS, TEAL call Print_ln LOAD_SUCCESS: db "Stage 2 of the bootloader has loaded successfully!",0 Last edited by AnonymousUser on 27 Jul 2014, 01:34; edited 4 times in total |
|||
26 Jul 2014, 19:10 |
|
smiddy 26 Jul 2014, 20:14
Kewl, you are on your way now!
|
|||
26 Jul 2014, 20:14 |
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.