commit 7fcabc9ff285d9aaf017e2a297be746ffed5127b
parent dc362b52be53620265aaed93b39a5f7bb49f3c74
Author: Georges Dupéron <georges.duperon@gmail.com>
Date: Sun, 8 Jul 2018 16:48:53 +0200
Chameleon: the OS is now also an ISO9660 CD-ROM image
Diffstat:
8 files changed, 178 insertions(+), 34 deletions(-)
diff --git a/.travis.yml b/.travis.yml
@@ -20,6 +20,7 @@ addons:
# example-os build dependencies:
- nasm
- mtools
+ - genisoimage
matrix:
include:
@@ -35,7 +36,15 @@ matrix:
- qemu-system-arm
- bochs-sdl
- dosbox
- - env: MODE=qemu-system-i386
+ - env: MODE=qemu-system-i386-floppy
+ sudo: false
+ addons:
+ apt:
+ packages:
+ - *common_apt_packages
+ - qemu
+ - qemu-system-x86
+ - env: MODE=qemu-system-i386-cdrom
sudo: false
addons:
apt:
diff --git a/README.md b/README.md
@@ -4,9 +4,13 @@ This Travis configuration runs an operating system in various emulators. It can
Below are screenshots of an example operating system. This example merely displays a gradient and does not process any user input.
-## QEMU
+## QEMU (floppy disk)
-
+
+
+## QEMU (CD-ROM)
+
+
## VirtualBox
diff --git a/example-os/.gitignore b/example-os/.gitignore
@@ -7,3 +7,10 @@
/os.fdisk
/os.arm.disasm
/os.zip
+/os.zip.adjusted
+/os.iso
+/os.32k
+/os.fat12
+/os.offsets
+/iso_files/os.zip
+/iso_files/boot/iso_boot.sys
diff --git a/example-os/Makefile b/example-os/Makefile
@@ -1,5 +1,7 @@
os_filename = os.bat
-tests = test/qemu-system-i386 test/qemu-system-arm test/virtualbox test/bochs test/gui-sh test/dosbox
+tests = test/qemu-system-i386-floppy test/qemu-system-i386-cdrom test/qemu-system-arm test/virtualbox test/bochs test/gui-sh test/dosbox
+built_files = $(os_filename) os.ndisasm.disasm os.reasm.asm os.reasm os.reasm.disasm os.file os.fdisk os.arm.disasm os.zip os.zip.adjusted os.iso os.32k os.fat12 os.offsets iso_files/os.zip iso_files/boot/iso_boot.sys
+built_directories = iso_files/boot iso_files
os_image_size_kb = 1440
os_partition_start_sectors = 3
@@ -10,24 +12,105 @@ os_floppy_chs_s = 9
.PHONY: all
# all: os.arm.disasm
-all: $(os_filename) os.ndisasm.disasm os.reasm.asm os.reasm os.file os.fdisk .gitignore Makefile
+all: $(os_filename) os.ndisasm.disasm os.reasm.asm os.file os.fdisk os.offsets .gitignore Makefile
../deploy-screenshots: Makefile
mkdir -p $@
touch $@
-$(os_filename): os.asm ../deploy-screenshots Makefile
- rm -f $@
+# 32k header of the ISO9660 image
+os.32k: os.asm Makefile
nasm -o $@ $<
- mformat -v "Example OS" \
- -T $$(($(os_partition_size_sectors))) \
+
+os.iso: iso_files/os.zip iso_files/boot/iso_boot.sys Makefile
+ mkisofs \
+ --input-charset utf-8 \
+ -rock \
+ -joliet \
+ -eltorito-catalog boot/boot.cat \
+ -eltorito-boot boot/iso_boot.sys \
+ -no-emul-boot \
+ -boot-load-size 4 \
+ -pad \
+ -output os.iso \
+ ./iso_files/
+
+sector_size = 512
+# should be exact
+os_total_size_sectors = ( $(os_image_size_kb)*1024 / $(sector_size) )
+os_total_size_tracks = ( $(os_total_size_sectors) / $(os_floppy_chs_s) )
+# round up
+iso_size_sectors = ( ( $$(wc -c os.iso | cut -d ' ' -f 1) + $(sector_size) - 1 ) / $(sector_size) )
+iso_size_tracks = ( ( $(iso_size_sectors) + $(os_floppy_chs_s) - 1 ) / $(os_floppy_chs_s) )
+# round up
+zip_size_sectors = ( ( $$(wc -c os.zip | cut -d ' ' -f 1) + $(sector_size) - 1 ) / $(sector_size) )
+zip_size_tracks = ( ( $(zip_size_sectors) + $(os_floppy_chs_s) - 1 ) / $(os_floppy_chs_s) )
+# allocate the remaining sectors, aligned on tracks
+fat12_size_tracks = ( ( $(os_total_size_tracks) - $(iso_size_tracks) - $(zip_size_tracks) ) )
+fat12_size_sectors = ( $(fat12_size_tracks) * $(os_floppy_chs_s) )
+# zip should probably have its end aligned, not its start
+space_before_zip_bytes = ( $(os_image_size_kb)*1024 - $$(wc -c os.zip | cut -d ' ' -f 1) )
+
+mbr_start = 0
+mbr_end = 512
+header_32k_start = 0
+header_32k_end = ( 32 * 1024 )
+header_32k_size = ( $(header_32k_end) - $(header_32k_start) )
+iso_start = ( 32 * 1024 )
+iso_end = ( $(iso_size_sectors) * $(sector_size) )
+fat12_start = ( $(iso_size_tracks) * $(os_floppy_chs_s) * $(sector_size) )
+fat12_size = ( $(fat12_size_sectors) * $(sector_size) )
+fat12_end = ( $(fat12_start) + $(fat12_size) )
+zip_start = $(space_before_zip_bytes)
+zip_end = ( $(os_total_size_sectors) * $(sector_size) )
+
+os_fat12_partition = "$@@@$$(( $(fat12_start) ))"
+os.fat12: os.zip os.iso Makefile
+ set -x; dd if=/dev/zero bs=$(sector_size) count=$$(( $(os_total_size_sectors) )) of=$@
+ set -x; mformat -v "Example OS" \
+ -T $$(( $(fat12_size_sectors) )) \
-h $(os_floppy_chs_h) \
-s $(os_floppy_chs_s) \
- -i "$@@@$$(($(os_partition_start_sectors)*512))"
-# mcopy … "$@@@$$(($(os_partition_start_sectors)*512))"
- zip os.zip $@
- dd seek=$$(($(os_image_size_kb)*1024-$$(wc -c os.zip | cut -d ' ' -f 1))) bs=1 if=os.zip of=$@
+ -i $(os_fat12_partition)
+ set -x; mcopy -i $(os_fat12_partition) os.zip "::os.zip"
+
+iso_files: Makefile
+ mkdir -p $@
+
+iso_files/boot: iso_files Makefile
+ mkdir -p $@
+
+iso_files/os.zip: os.zip iso_files Makefile
+# TODO: make it so that the various file formats are mutual quines:
+# * the ISO should contain the original file
+# * the ZIP should contain the original file
+# * the FAT12 should contain the original file
+ cp $< $@
+
+# 4 sectors loaded when booting from optical media (CD-ROM, …):
+iso_files/boot/iso_boot.sys: os.32k iso_files/boot Makefile
+ dd if=$< bs=512 count=4 of=$@
+
+os.zip: os.32k Makefile
+ zip $@ $<
+
+os.zip.adjusted: os.zip Makefile
+ set -x; dd if=/dev/zero bs=1 count=$$(( $(space_before_zip_bytes) )) of=$@
+ cat $< >> $@
zip --adjust-sfx $@
+
+$(os_filename): os.32k os.iso os.fat12 os.zip os.zip.adjusted ../deploy-screenshots Makefile
+ rm -f $@
+# start with the .iso
+ cp os.iso $@
+# splice in the first 32k (bootsector and partition table)
+ set -x; dd skip=$$(( $(header_32k_start) )) seek=$$(( $(header_32k_start) )) bs=1 count=$$(( $(header_32k_size) )) conv=notrunc if=os.32k of=$@
+# splice in fat12
+ set -x; dd skip=$$(( $(fat12_start) )) seek=$$(( $(fat12_start) )) bs=1 count=$$(( $(fat12_size) )) conv=notrunc if=os.fat12 of=$@
+# splice in zip at the end
+ set -x; dd skip=$$(( $(space_before_zip_bytes) )) seek=$$(( $(space_before_zip_bytes) )) bs=1 conv=notrunc if=os.zip.adjusted of=$@
+# patch the partition table
+ printf "p\nd\nn\np\n1\n$$(( $(fat12_start) / $(sector_size) ))\n$$(( $(fat12_size_sectors) ))\nt\n01\na\n1\np\nw\nq\n" | fdisk $@
chmod a+x-w $@
os.file: $(os_filename) Makefile
@@ -36,7 +119,20 @@ os.file: $(os_filename) Makefile
os.fdisk: $(os_filename) Makefile
fdisk -l $< > $@
-os.ndisasm.disasm: $(os_filename) Makefile
+os.offsets: $(os_filename) os.32k os.iso os.fat12 os.zip Makefile
+ printf 'total_size = 0x%08x\n' $$(( $(os_total_size_sectors) * $(sector_size) )) > $@
+ printf 'mbr_start = 0x%08x\n' $$(( $(mbr_start) )) >> $@
+ printf 'mbr_end = 0x%08x\n' $$(( $(mbr_end) )) >> $@
+ printf '32k_start = 0x%08x\n' $$(( $(header_32k_start) )) >> $@
+ printf '32k_end = 0x%08x\n' $$(( $(header_32k_end) )) >> $@
+ printf 'iso_start = 0x%08x\n' $$(( $(iso_start) )) >> $@
+ printf 'iso_end = 0x%08x\n' $$(( $(iso_end) )) >> $@
+ printf 'fat12_start = 0x%08x\n' $$(( $(fat12_start) )) >> $@
+ printf 'fat12_end = 0x%08x\n' $$(( $(fat12_end) )) >> $@
+ printf 'zip_start = 0x%08x\n' $$(( $(zip_start) )) >> $@
+ printf 'zip_end = 0x%08x\n' $$(( $(zip_end) )) >> $@
+
+os.ndisasm.disasm: $(os_filename) Makefile ../utils/compact-ndisasm.sh
../utils/compact-ndisasm.sh $< $@
os.reasm.asm: os.ndisasm.disasm Makefile
@@ -49,33 +145,38 @@ os.reasm: os.reasm.asm $(os_filename) Makefile
&& echo "[1;32mRe-assembled file is identical to $(os_filename)[m" \
|| (../utils/compact-ndisasm.sh $@ os.reasm.disasm; \
echo "[0;33mRe-assembled file is different from $(os_filename). Use meld os.ndisasm.disasm os.reasm.disasm to see differences.[m"; \
- exit 1)
+ exit 0) # For now ignore this error, since we cannot have a reliable re-assembly of arbitrary data.
#os.arm.disasm: $(os_filename) Makefile
# arm-none-eabi-objdump --endian=little -marm -b binary -D --adjust-vma=0x8000 $< > $@
.PHONY: clean
clean: Makefile
- rm -f $(os_filename) os.ndisasm.disasm os.reasm.asm os.reasm os.file os.arm.disasm os.zip
+ rm -f $(built_files)
+ for d in $(built_directories); do if test -e "$$d"; then rmdir "$$d"; fi; done
.gitignore: Makefile
- echo /$(os_filename) > $@
- echo /os.ndisasm.disasm >> $@
- echo /os.reasm.asm >> $@
- echo /os.reasm >> $@
- echo /os.reasm.disasm >> $@
- echo /os.file >> $@
- echo /os.fdisk >> $@
- echo /os.arm.disasm >> $@
- echo /os.zip >> $@
+ for f in $(built_files); do echo "/$$f"; done > $@
.PHONY: test
-test: $(tests) test/zip all Makefile
+test: $(tests) test/noemu all Makefile
.PHONY: $(tests)
$(tests): $(os_filename) ../deploy-screenshots Makefile
cd .. && ./utils/gui-wrapper.sh 800x600x24 ./$@.sh example-os/$<
+.PHONY: test/noemu
+test/noemu: test/zip os.reasm test/sizes test/fat12_contents Makefile
+
.PHONY: test/zip
-test/zip:
- unzip -t os.zip
+test/zip: $(os_filename) Makefile
+ unzip -t $(os_filename)
+
+.PHONY: test/sizes
+test/sizes: os.32k $(os_filename) Makefile
+ test "$$(wc -c os.32k)" = "$$((32*1024)) os.32k"
+ test "$$(wc -c $(os_filename))" = "$$((1440*1024)) $(os_filename)"
+
+# check that the fat filesystem has the correct contents
+test/fat12_contents: $(os_filename)
+ mdir -i "$<@@$$(( $(fat12_start) ))" :: | grep -E "^os\s+zip\s+"
diff --git a/example-os/os.asm b/example-os/os.asm
@@ -45,7 +45,7 @@ end:
;;; Fill the remaining bytes with 0 and write a partition table
times 0x1b8-($-$$) db 0
-db "ExOSxx" ;; 0x1b8 unique disk ID (4 bytes, can be any value)
+db "ExOSxx" ;; 0x1b8 unique disk ID (4-6 bytes? Can be any value)
;;; Partition table entries follow this format:
;;; 1 byte Bootable flag (0x00 = no, 0x80 = yes)
;;; 3 bytes Start Head Sector Cylinder (8 bits + 6 bits + 10 bits)
@@ -53,7 +53,9 @@ db "ExOSxx" ;; 0x1b8 unique disk ID (4 bytes, can be any value)
;;; 3 bytes End Head Sector Cylinder (8 bits + 6 bits + 10 bits)
;;; 4 bytes LBA offset
;;; 4 bytes LBA length
-db 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x0b, 0x00, 0x00, 0x3d, 0x0b, 0x00, 0x00 ;; 0x1be p1
+;;;0x1be 0x1c1 0x1c2 0x1c5 0x1c6 0x1c9 0x1ca 0x1cd
+;;; This is filled with dummy values, and later patched with fdisk.
+db 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0x9b, 0x05, 0x00, 0x00 ;; 0x1be p1
db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ;; 0x1ce p2
db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ;; 0x1de p3
db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ;; 0x1ee p4
@@ -80,5 +82,5 @@ db `exit\n`
db `:loop\n`
db `GOTO loop\n`
-;;; Fill up to 1.44M with 0
-times (1440*1024)-($-$$) db 0
+;;; Fill up to 32k with 0. This constitutes the first 32k of the ISO image.
+times (32*1024)-($-$$) db 0
diff --git a/test/qemu-system-i386-cdrom.sh b/test/qemu-system-i386-cdrom.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+set -e
+
+if test $# -ne 1 || test "$1" = '-h' -o "$1" = '--help'; then
+ echo "Usage: $0 operating_system_file"
+fi
+os_filename="$1"
+
+qemu-system-i386 -drive format=raw,readonly,file=${os_filename},index=0,if=ide,index=1,media=cdrom -boot d &
+pid=$!
+runsikulix -r test/check-gradient.sikuli && exitcode=$? || exitcode=$?
+
+./utils/take-screenshots.sh "./deploy-screenshots/$(basename "$0" .sh).png"
+
+kill $pid
+
+exit $exitcode
diff --git a/test/qemu-system-i386.sh b/test/qemu-system-i386-floppy.sh
diff --git a/utils/compact-ndisasm.sh b/utils/compact-ndisasm.sh
@@ -1,14 +1,18 @@
+#!/bin/sh
+
+set -e
+
in=$1
out=$2
# Add -s 0x1234 to te ndisasm command-line to specify that there is an
# instruction starting at 0x1234. Use this when ndisasm misinterprets
# some instructions due to db.
-skip=""
+skip="-s 0x16F40C -s 0x16F415 -s 0x16FBD0"
for i in `seq 0 255`; do skip="$skip -s 0x7e$(printf %02x $i)"; done
(echo "[BITS 16]"; \
echo "[ORG 0x7c00]"; \
- ndisasm -s 0x7c78 $skip -o 0x7C00 "$in" \
+ ndisasm -s 0x7c78 $skip -o 0x7C00 -b 16 "$in" \
| uniq -s 8 -c \
| sed -r -e 's/^\s*1 //; t; s/^\s*([0-9]+) ([^ ]+\s+[^ ]+\s+)/\2times \1 /' \
| sed -r -e 's/([^ ]+\s+[0-9A-F]{4}\s+j[^ ]+ )0x/\1short 0x/') > "$out"