make_shuttle2 is designed to be used at the command line with
redirection of input and output from and to DEBUG.EXE like in the following
way:
C:\TEMP> debug < make_shuttle2.npp > make_shuttle2_dbg.npp
So make_shuttle2 is a complex script that instructs DEBUG.EXE to build
SHUTTLE2.BIN. I prepared the file "make_shuttle2.npp" for you in the
DOWNLOAD AREA, but here I want to present how I organized the script and how it works. A first
overview of make_shuttle2 follows here.
-f 100 1000 3f
-
-n boot_sec.bin
-l 200
-
-n SHUTTEMP.BIN
-l 400
-
-
-a 600
137B:0600 ;234567890123456789012345678901234567890123456789012345678901234567890123456789
137B:0600 ;-------10--------20--------30--------40--------50--------60--------70-------79
137B:0600 ;##############################################################################
137B:0600 ; make_shuttle2:
137B:0600 ;
137B:0600 ; Copyright (C) 2020 - Michele Musci
137B:0600 ; Distributed under the GNU Affero General Public License version 3.
137B:0600 ; See https://www.gnu.org/licenses/agpl-3.0.txt
137B:0600 ;
137B:0600 ; This is the code that manipulates SHUTTEMP (template of SHUTTLE2) to produce
137B:0600 ; the customized version of SHUTTLE2 that is going to boot on the target PC.
137B:0600 ;
137B:0600 ; 1. Load the 512 bytes image of the boot sector (boot_sec.bin) starting
137B:0600 ; at address 0x200.
137B:0600 ; 2. Load the 512 bytes SHUTTEMP.BIN starting at address 0x400.
137B:0600 ;
137B:0600 ; Build it with command:
137B:0600 ; debug < make_shuttle2.npp > make_shuttle2_dbg.npp
137B:0600 ;##############################################################################
[...]
At the beginning of the script, you see that I prepared my working RAM area
with the command f 100 1000 3f, then I loaded
at 0x200 the original image of the first block from disk (boot_sec.bin file)1
and finally I loaded at 0x400 the SHUTTEMP.BIN template. At address 0x600, I
wrote an assembly code that reads the BIOS Parameter Block from address 0x200
and manipulates the SHUTTEMP at address 0x400 with all the necessary data and
code injections as described in the previous paragraph. I omit for the moment
the very assembly code of make_shuttle2 because I want to explain the
script first. You have to understand that the code written starting as address
0x600 is inactive in RAM until the very same script activates it.
[...]
-a 100
137B:0100 call 060c
137B:0103 jmp 100
137B:0105
-rip
IP 0100
:100
-
-p
AX=C700 BX=0000 CX=0004 DX=0000 SP=FFEE BP=0020 SI=08B5 DI=0129
DS=137B ES=137B SS=137B CS=137B IP=0103 NV UP EI PL ZR NA PE NC
137B:0103 EBFB JMP 0100
-
-d 100
137B:0100 E8 09 05 EB FB 3F 3F 3F-3F 3F 3F 3F 3F 3F 3F 3F .....???????????
137B:0110 53 48 55 54 54 4C 45 32-20 20 20 20 20 20 20 20 SHUTTLE2
137B:0120 49 53 20 52 45 41 44 59-21 3F 3F 3F 3F 3F 3F 3F IS READY!???????
137B:0130 3F 3F 3F 3F 3F 3F 3F 3F-3F 3F 3F 3F 3F 3F 3F 3F ????????????????
137B:0140 3F 3F 3F 3F 3F 3F 3F 3F-3F 3F 3F 3F 3F 3F 3F 3F ????????????????
137B:0150 3F 3F 3F 3F 3F 3F 3F 3F-3F 3F 3F 3F 3F 3F 3F 3F ????????????????
137B:0160 3F 3F 3F 3F 3F 3F 3F 3F-3F 3F 3F 3F 3F 3F 3F 3F ????????????????
137B:0170 3F 3F 3F 3F 3F 3F 3F 3F-3F 3F 3F 3F 3F 3F 3F 3F ????????????????
-
-d 400 5ff
137B:0400 EB 78 90 4D 53 44 4F 53-35 2E 30 00 02 04 08 00 .x.MSDOS5.0.....
137B:0410 02 00 02 00 00 F8 C8 00-3F 00 FF 00 00 58 E7 10 ........?....X..
137B:0420 00 20 03 00 80 00 29 F0-20 F8 04 4E 4F 20 4E 41 . ....). ..NO NA
137B:0430 4D 45 20 20 20 20 46 41-54 31 36 20 20 20 B4 0E ME FAT16 ..
137B:0440 BB 07 00 CD 10 AC 3C 00-75 F4 C3 04 30 3C 39 76 ......<.u...0<9v
137B:0450 02 04 07 C3 B1 04 D3 E8-D2 E8 E8 EE FF 88 44 01 ..............D.
137B:0460 88 E0 E8 E6 FF 88 44 00-E8 DA FF BE DB 7B E8 D4 ......D......{..
137B:0470 FF 31 C0 CD 16 EA 00 00-FF FF FA 31 DB 8E DB 8E .1.........1....
137B:0480 C3 8E D3 BC 00 78 FC BE-00 7C BF 00 7A B9 FF 00 .....x...|..z...
137B:0490 F3 A5 89 1E FE 7B FB EA-9C 7A 00 00 F8 BE F0 7B .....{...z.....{
137B:04A0 B2 80 B4 42 CD 13 BE B6-7B 72 A9 8B 16 11 7A BB ...B....{r....z.
137B:04B0 00 05 B9 0C 00 89 DF BE-95 7B F3 A6 E3 0B 83 C3 .........{......
137B:04C0 20 4A 75 EE BE 95 7B EB-9F 8B 45 0E 8B 2E EA 7B Ju...{...E....{
137B:04D0 8B 3E E8 7B 50 2D 02 00-B9 04 00 F7 E1 01 F8 11 .>.{P-..........
137B:04E0 EA 89 16 EA 7B A3 E8 7B-F8 BE E0 7B B2 80 B4 42 ....{..{...{...B
137B:04F0 CD 13 73 06 BE C2 7B E9-5A FF B8 80 00 01 06 E6 ..s...{.Z.......
137B:0500 7B 58 BB 02 00 B9 00 70-F7 E3 F7 F1 50 52 3A 06 {X.....p....PR:.
137B:0510 DD 7B 74 3A 8A 26 F2 7B-F6 E4 89 C3 B9 02 00 E3 .{t:.&.{........
137B:0520 27 49 A1 16 7A F7 E1 01-D8 83 D2 00 05 08 58 81 'I..z.........X.
137B:0530 D2 E7 10 A3 F8 7B 89 16-FA 7B F8 BE F0 7B B2 80 .....{...{...{..
137B:0540 B4 42 CD 13 73 08 EB D7-BE CE 7B E9 06 FF 5B 58 .B..s.....{...[X
137B:0550 A2 DD 7B 8B 87 00 05 3D-FF FF 74 19 3D 02 00 72 ..{....=..t.=..r
137B:0560 21 3D 94 C7 77 1C FF 0E-DE 7B 74 03 E9 65 FF BE !=..w....{t..e..
137B:0570 AB 7B E9 F3 FE B9 09 00-BF 00 7C BE 50 7B F3 A7 .{........|.P{..
137B:0580 E3 09 BE 91 7B 8C 5C 0F-E9 DD FE E9 82 00 3F 3F ....{.\.......??
137B:0590 3F 42 61 64 20 53 4F 46-54 57 41 52 45 42 49 47 ?Bad SOFTWAREBIG
137B:05A0 20 6E 6F 74 20 66 6F 75-6E 64 00 46 69 6C 65 20 not found.File
137B:05B0 74 6F 6F 20 62 69 67 00-68 3A 20 42 61 64 20 52 too big.h: Bad R
137B:05C0 4F 4F 54 00 68 3A 20 42-61 64 20 44 41 54 41 00 OOT.h: Bad DATA.
137B:05D0 68 3A 20 42 61 64 20 46-41 54 00 2E 00 FF 30 01 h: Bad FAT....0.
137B:05E0 10 00 04 00 00 00 C0 07-B8 59 E7 10 00 00 00 00 .........Y......
137B:05F0 10 00 38 00 00 05 00 00-98 59 E7 10 00 00 55 AA ..8......Y....U.
-
-
-
-rbx
BX 0000
:0
-rcx
CX 0004
:0200
-r
AX=C700 BX=0000 CX=0200 DX=0000 SP=FFEE BP=0020 SI=08B5 DI=0129
DS=137B ES=137B SS=137B CS=137B IP=0103 NV UP EI PL ZR NA PE NC
137B:0103 EBFB JMP 0100
-
-n SHUTTLE2.BIN
-w 400
Writing 00200 bytes
-q
After the omitted code of make_shuttle2, I continued with the script
and I coded at 0x100 the CALL instruction at
0x06C0 (entry point of MAIN) which I used, together with the command
p, to activate the code which builds
SHUTTLE2 in RAM. Remember that the command
p executes all instructions incurred from a
CALL to the next
RET instruction. For this reason, I had to code
somewhere outside the MAIN procedure a
CALL instruction to the entry point of the MAIN
and the same must terminate with a
RET instruction. Finally, I pointed the
instruction pointer (IP) to the
CALL instruction that activates the MAIN and I
used the p command to make the script run the
code automatically.
make_shuttle2 writes one message in RAM starting at address 0x110 in
such a way that I could have them in the "make_shuttle2_dbg.npp" protocol file
created with redirection of outputs2. Finally, I put, with the dump instruction
d 400 5ff, the SHUTTLE2 as it appears
in RAM and I wrote it to disk into the file SHUTTLE2.BIN.
Now it is time to have a closer look at the assembly software3
omitted before, however, I don't show all of it here in the post, since you
can download the complete file from the
DOWNLOAD AREA.
[...]
-a 600
137B:0600 ;234567890123456789012345678901234567890123456789012345678901234567890123456789
137B:0600 ;-------10--------20--------30--------40--------50--------60--------70-------79
137B:0600 ;##############################################################################
137B:0600 ; make_shuttle2:
137B:0600 ;
137B:0600 ; Copyright (C) 2020 - Michele Musci
137B:0600 ; Distributed under the GNU Affero General Public License version 3.
137B:0600 ; See https://www.gnu.org/licenses/agpl-3.0.txt
137B:0600 ;
137B:0600 ; This is the code that manipulates SHUTTEMP (template of SHUTTLE2) to produce
137B:0600 ; the customized version of SHUTTLE2 that is going to boot on the target PC.
137B:0600 ;
137B:0600 ; 1. Load the 512 bytes image of the boot sector (boot_sec.bin) starting
137B:0600 ; at address 0x200.
137B:0600 ; 2. Load the 512 bytes SHUTTEMP.BIN starting at address 0x400.
137B:0600 ;
137B:0600 ; Build it with command:
137B:0600 ; debug < make_shuttle2.npp > make_shuttle2_dbg.npp
137B:0600 ;##############################################################################
137B:0600 ;
137B:0600 ;##############################################################################
137B:0600 ;
137B:0600 ; SET_END_MSG: END OF PROCEDURE
137B:0600 ;
137B:0600 ;##############################################################################
137B:0600 ;
137B:0600 ; set_end_msg: <---
137B:0600 ;
137B:0600 mov di, 110 ; DI -> RAM area where to put the end message.
137B:0603 jmp 0606 ; JUMP msg_entry: --->
137B:0605 ;
137B:0605 ;
137B:0605 ; one_char_on_RAM: <---
137B:0605 ;
137B:0605 stosb ; ES:[DI] = AL
137B:0606 ; DI = DI + 1
137B:0606 ;
137B:0606 ;
137B:0606 ; msg_entry: <---
137B:0606 ;
137B:0606 lodsb ; AL = DS:[SI]
137B:0607 ; SI = SI + 1
137B:0607 cmp al, 00 ; flags = AL - 0x 00
137B:0609 ; Unsigned comparison:
137B:0609 jne 0605 ; JumpNotEqual one_char_on_RAM: --->
137B:060B ;
137B:060B ;------------------------------------------------------------------------------
137B:060B ; END OF PROCEDURE
137B:060B ;------------------------------------------------------------------------------
137B:060B ret
[...]
I began the software with the termination procedure. I similarly used this
termination procedure as I used
show_string, but this procedure writes bytes in memory rather than on screen. Another
important difference is that I activated this procedure with a
JUMP and not with a
CALL: this procedure is the common exit point
of make_shuttle2 that jumps here as soon as a problem is found or at
the very end when SHUTTLE2 is ready. In any case, I placed in register
SI the address of the first Byte of string that I wanted to copy to 0x110.
make_shuttle2 continues with a lot of plausibility checks across the
BIOS Parameter Block based on the information available in the
Microsoft's specification.
[...]
137B:060C ;##############################################################################
137B:060C ;
137B:060C ; START MAIN procedure
137B:060C ;
137B:060C ;##############################################################################
137B:060C ;
137B:060C ; start: <---
137B:060C ;
137B:060C ;##############################################################################
137B:060C ;
137B:060C ; CHECKS
137B:060C ;
137B:060C ;##############################################################################
137B:060C ;------------------------------------------------------------------------------
137B:060C ; BPB_BytsPerSec -> [BPB + 0x0b] (2 Bytes)
137B:060C ; Number of Bytes per block. Legal values are:
137B:060C ; 0x0200 -> 512 bytes,
137B:060C ; 0x0400 -> 1024 bytes,
137B:060C ; 0x0800 -> 2048 bytes,
137B:060C ; 0x1000 -> 4096 bytes.
137B:060C ;------------------------------------------------------------------------------
137B:060C mov ax, [200 + 0b] ; AX = BPB_BytsPerSec
137B:060F cmp ax, 0200 ; flags = AX - 0x 0200
137B:0612 ; Unsigned comparison:
137B:0612 je 063f ; JumpEqual isOK_A: --->
137B:0614 ;
137B:0614 cmp ax, 0400 ; flags = AX - 0x 0400
137B:0617 ; Unsigned comparison:
137B:0617 je 063f ; JumpEqual isOK_A: --->
137B:0619 ;
137B:0619 cmp ax, 0800 ; flags = AX - 0x 0800
137B:061C ; Unsigned comparison:
137B:061C je 063f ; JumpEqual isOK_A: --->
137B:061E ;
137B:061E ;
137B:061E cmp ax, 1000 ; flags = AX - 0x 1000
137B:0621 ; Unsigned comparison:
137B:0621 je 063f ; JumpEqual isOK_A: --->
137B:0623 ;
137B:0623 ;
137B:0623 ;-----------------------------
137B:0623 ; Error...
137B:0623 ;-----------------------------
137B:0623 mov si, 0628 ; SI -> "BPB_BytsPerSec is bad!"
137B:0626 jmp 0600 ; JUMP set_end_msg: --->
137B:0628 ;
137B:0628 DB 'BPB_BytsPerSec is bad!' 00
137B:063F ;
137B:063F ;
137B:063F ;
137B:063F ; isOK_A: <---
137B:063F ;
137B:063F ;------------------------------------------------------------------------------
137B:063F ; BPB_SecPerClus -> [BPB + 0x0d] (1 Bytes)
137B:063F ; Number of blocks per cluster of file. Legal values are:
137B:063F ; 0x01 (1), 0x02 (2), 0x04 (4), 0x08 (8),
137B:063F ; 0x10 (16), 0x20 (32), 0x40 (64), 0x80 (128).
137B:063F ; IMPORTANT!!!
137B:063F ; BPB_SecPerClus * BPB_BytsPerSec <= 0x8000 (32Kbyte).
137B:063F ;------------------------------------------------------------------------------
137B:063F xor bx, bx ; BH = 0x00
137B:0641 mov bl, [200 + 0d] ; BX = BPB_SecPerClus.
137B:0645 ; Remember that AX = BPB_BytsPerSec and we will
137B:0645 ; perform a MUL with BX that implies
137B:0645 ; the use of AX.
137B:0645 ;
137B:0645 mov dx, 01 ; DX = 0x01. Initial value that gets multiplied
137B:0648 ; at each step
137B:0648 mov cx, 8 ; We have 8 steps.
137B:064B ;
137B:064B ; next_chk: <---
137B:064B ;
137B:064B cmp bx, dx ; flags = BX - DX
137B:064D ; Unsigned comparison:
137B:064D je 066f ; JumpEqual isOK_B: --->
137B:064F ;
137B:064F shl dx, 1 ; DX = DX * 2
137B:0651 loop 064B ; dec CX LOOP next_chk: --->
137B:0653 ; JNZ
137B:0653 ;-----------------------------
137B:0653 ; Error...
137B:0653 ;-----------------------------
137B:0653 mov si, 0658 ; SI -> "BPB_SecPerClus is bad!"
137B:0656 jmp 0600 ; JUMP set_end_msg: --->
137B:0658 ;
137B:0658 DB 'BPB_SecPerClus is bad!' 00
137B:066F ;
137B:066F ;
137B:066F ;
137B:066F ; isOK_B: <---
137B:066F ;
[...]
As you can see, at every check either the software stops with an error
message, or it continues towards the next check. Stopping the software with an
error message is done always with the preparation of the string that is
pointed by register SI and then the jump goes at the termination part of the
procedure.
After almost all the checks, I could move on with the calculation of the
parameters and the injection in
SHUTTEMP. During the calculation of the parameters I still had to perform some
additional few checks, however, the vast majority of them was already done at
the beginning.
Now that I look back and I sum together the pages of this diary for the first
Space Shuttle
and
SHUTTLE2, I get myself astonished about the huge amount of know-how and experience
one may need just to write 512 bytes of data plus code. I had already a great
deal of respect and admiration for the people who wrote and maintains GRUB,
but since I tried to do myself these toy bootloader and I got aware of all the
things you have to take care of to make it work, my respect and admiration for the
people of GRUB has grown.
Final consideration
If you ask anybody if Assembly is a programming or a scripting language,
everybody will answer that Assembly is a programming language and so do I. But
wait a moment. All the software done until now with DEBUG.EXE is programmed in assembly
and runs later on in deep dark space (my metaphor for real mode bare bone
x86), all of them except this one. MAKESHUT is indeed a script to use
DEBUG.EXE in an automatized way that takes two binary files (the SHUTTEMP.BIN
and the boot_sec.bin) to produce a third binary file: the SHUTTLE2.BIN.
MAKESHUT doesn’t run in the deep dark space, but quite comfortable inside
Windows. I just used redirection of inputs and output to feed DEBUG.EXE with
commands in an automatized way. Then Assembly can be used as a scripting
language too!…. or not ?.... well let me just say… almost.
Comments
Post a Comment