From 236ea33c61de0c0c4da88fff15d520e8c1e9d134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Quei=C3=9Fner?= Date: Sun, 26 Jun 2016 16:55:42 +0200 Subject: [PATCH] Adds CharacterDevice base class for SerialPort. --- prototypes-rework/config.mk | 4 +- prototypes-rework/include/chardev.hpp | 94 +++++++++++++ prototypes-rework/include/driver/serial.hpp | 8 +- prototypes-rework/libbase/numeric.cpp | 133 +++++++++++++++++++ prototypes-rework/libbase/{src => }/string.c | 0 prototypes-rework/libchardev/Makefile | 12 ++ prototypes-rework/libchardev/chardev.cpp | 105 +++++++++++++++ prototypes-rework/libserial/serial.cpp | 4 +- prototypes-rework/video/Makefile | 2 +- prototypes-rework/video/src/init.cpp | 7 +- 10 files changed, 358 insertions(+), 11 deletions(-) create mode 100644 prototypes-rework/include/chardev.hpp create mode 100644 prototypes-rework/libbase/numeric.cpp rename prototypes-rework/libbase/{src => }/string.c (100%) create mode 100644 prototypes-rework/libchardev/Makefile create mode 100644 prototypes-rework/libchardev/chardev.cpp diff --git a/prototypes-rework/config.mk b/prototypes-rework/config.mk index 7975fa0..6519d76 100644 --- a/prototypes-rework/config.mk +++ b/prototypes-rework/config.mk @@ -7,4 +7,6 @@ CC=/opt/bin/i686-elf-gcc CXX=/opt/bin/i686-elf-g++ LD=/opt/i686-elf/bin/ld AR=/opt/i686-elf/bin/ar -AS=/opt/bin/i686-elf-gcc \ No newline at end of file +AS=/opt/bin/i686-elf-gcc + +LIBS += -L/opt/lib/gcc/i686-elf/6.1.0/ \ No newline at end of file diff --git a/prototypes-rework/include/chardev.hpp b/prototypes-rework/include/chardev.hpp new file mode 100644 index 0000000..f2e458b --- /dev/null +++ b/prototypes-rework/include/chardev.hpp @@ -0,0 +1,94 @@ +#pragma once + +#include +#include + +template +struct NumericFormat +{ + T value; + uint32_t base; + + /** + * Applies a padding to the number. + * A positive number will apply padding the right side, + * a negative number will pad the left side. + */ + int32_t padding = 0; + + char padchar = ' '; + + NumericFormat(T value) : value(value), base(10) { } + NumericFormat(T value, uint32_t base) : value(value), base(base) { } +}; + +namespace utils +{ + template + auto hex(T value) { return NumericFormat(value, 16); } + + template + auto dec(T value) { return NumericFormat(value, 10); } + + template + auto oct(T value) { return NumericFormat(value, 8); } + + template + auto bin(T value) { return NumericFormat(value, 2); } + + template + auto nbase(T value, uint32_t base) { return NumericFormat(value, base); } + + template + auto pad(NumericFormat value, int32_t padding, char c = ' ') { + value.padding = padding; + return value; + } + + template + auto pad(T value, int32_t padding, char c = ' ') { + return pad(NumericFormat(value), padding, c); + } +} + +class CharacterDevice +{ +private: + /** + * Prints the prefix for a given numeric base. + * @returns the prefix length. + */ + uint32_t printNumericPrefix(uint32_t base); + + /** + * Prints a character several times. + */ + void putrep(char c, uint32_t repetitions); +public: + virtual void write(char c) = 0; + + inline CharacterDevice & operator << (char c) + { + this->write(c); + return *this; + } + + inline CharacterDevice & operator << (const char *str) + { + while(*str) { + *this << *str++; + } + return *this; + } + + CharacterDevice & operator << (uint32_t value); + + CharacterDevice & operator << (int32_t value); + + CharacterDevice & operator << (void *value); + + CharacterDevice & operator << (bool value); + + template + CharacterDevice & operator << (const NumericFormat &fmt); +}; \ No newline at end of file diff --git a/prototypes-rework/include/driver/serial.hpp b/prototypes-rework/include/driver/serial.hpp index 94b8595..70fc2bc 100644 --- a/prototypes-rework/include/driver/serial.hpp +++ b/prototypes-rework/include/driver/serial.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #define SERIAL_COM1 0x3F8 #define SERIAL_COM2 0x2F8 @@ -16,7 +17,8 @@ enum class Partiy Low = 0x7, }; -class SerialPort +class SerialPort : + public CharacterDevice { private: uint16_t mBase; @@ -31,7 +33,7 @@ public: bool isReceiveEmpty() const; - void write(uint8_t c); + void write(char c) override; - uint8_t read(); + char read(); }; \ No newline at end of file diff --git a/prototypes-rework/libbase/numeric.cpp b/prototypes-rework/libbase/numeric.cpp new file mode 100644 index 0000000..492e055 --- /dev/null +++ b/prototypes-rework/libbase/numeric.cpp @@ -0,0 +1,133 @@ +#include + +static char getDigit(uint32_t i) +{ + if(i >= 0 && i <= 9) { + return '0' + i; + } + return 'A' + (i-10); +} + +size_t Numeric::toString( + char *buffer, + size_t length, + uint32_t number, + uint32_t radix) +{ + if(length == 0) { + return 0; + } + if(number == 0) { + buffer[0] = '0'; + return 1; + } + + size_t len = 0; + while(number > 0) + { + buffer[len++] = getDigit(number % radix); + if(len >= length) + break; + number /= radix; + } + + int half = len / 2; + for(int i = 0; i < half; i++) + { + char c = buffer[i]; + buffer[i] = buffer[len - i - 1]; + buffer[len - i - 1] = c; + } + + return len; +} + +size_t Numeric::toString( + char *buffer, + size_t length, + int32_t number, + uint32_t radix) +{ + if(length == 0) { + return 0; + } + if(number == 0) { + buffer[0] = '0'; + return 1; + } + if(number < 0) { + buffer[0] = '-'; + length -= 1; + if(length == 0) { + return 0; + } + return Numeric::toString(&buffer[1], length, static_cast(-number), radix) + 1; + } + else { + return Numeric::toString(buffer, length, static_cast(number), radix); + } +} + + + + + +size_t Numeric::toString( + char *buffer, + size_t length, + uint64_t number, + uint32_t radix) +{ + if(length == 0) { + return 0; + } + if(number == 0) { + buffer[0] = '0'; + return 1; + } + + size_t len = 0; + while(number > 0) + { + buffer[len++] = getDigit(number % radix); + if(len >= length) + break; + number /= radix; + } + + int half = len / 2; + for(int i = 0; i < half; i++) + { + char c = buffer[i]; + buffer[i] = buffer[len - i - 1]; + buffer[len - i - 1] = c; + } + + return len; +} + +size_t Numeric::toString( + char *buffer, + size_t length, + int64_t number, + uint32_t radix) +{ + if(length == 0) { + return 0; + } + if(number == 0) { + buffer[0] = '0'; + return 1; + } + if(number < 0) { + buffer[0] = '-'; + length -= 1; + if(length == 0) { + return 0; + } + return Numeric::toString(&buffer[1], length, static_cast(-number), radix) + 1; + } + else { + return Numeric::toString(buffer, length, static_cast(number), radix); + } +} \ No newline at end of file diff --git a/prototypes-rework/libbase/src/string.c b/prototypes-rework/libbase/string.c similarity index 100% rename from prototypes-rework/libbase/src/string.c rename to prototypes-rework/libbase/string.c diff --git a/prototypes-rework/libchardev/Makefile b/prototypes-rework/libchardev/Makefile new file mode 100644 index 0000000..a31923b --- /dev/null +++ b/prototypes-rework/libchardev/Makefile @@ -0,0 +1,12 @@ +## +# Build libchardev. +## + +include ../config.mk + +LIBRARY = libchardev.a + +all: builddir $(LIBRARY) + +include ../common.mk +include ../library.mk \ No newline at end of file diff --git a/prototypes-rework/libchardev/chardev.cpp b/prototypes-rework/libchardev/chardev.cpp new file mode 100644 index 0000000..0418ca9 --- /dev/null +++ b/prototypes-rework/libchardev/chardev.cpp @@ -0,0 +1,105 @@ +#include + +#include + + +CharacterDevice & CharacterDevice::operator << (uint32_t value) +{ + char buffer[12]; + size_t len = Numeric::toString(buffer, sizeof(buffer), value, 10); + for(size_t i = 0; i < len; i++) { + this->write(buffer[i]); + } + return *this; +} + +CharacterDevice & CharacterDevice::operator << (int32_t value) +{ + char buffer[13]; + size_t len = Numeric::toString(buffer, sizeof(buffer), value, 10); + for(size_t i = 0; i < len; i++) { + this->write(buffer[i]); + } + return *this; +} + +CharacterDevice & CharacterDevice::operator << (void *value) +{ + char buffer[13]; + size_t len = Numeric::toString(buffer, sizeof(buffer), reinterpret_cast(value), 16); + for(size_t i = 0; i < len; i++) { + this->write(buffer[i]); + } + return *this; +} + +CharacterDevice & CharacterDevice::operator << (bool value) +{ + if(value == true) { + *this << "true"; + } else { + *this << "false"; + } + return *this; +} + + +#define NUMERIC_FMT_HANDLER char buffer[13]; \ + size_t prefixlen = this->printNumericPrefix(fmt.base); \ + size_t len = Numeric::toString(buffer, sizeof(buffer), fmt.value, fmt.base); \ + int delta = prefixlen + len; \ + if(fmt.padding < 0 && delta < -fmt.padding) { \ + this->putrep(fmt.padchar, -fmt.padding - delta); \ + } \ + for(size_t i = 0; i < len; i++) { \ + this->write(buffer[i]); \ + } \ + if(fmt.padding > 0 && delta < fmt.padding) { \ + this->putrep(fmt.padchar, fmt.padding - delta); \ + } \ + return *this + +template<> +CharacterDevice & CharacterDevice::operator << (const NumericFormat & fmt) +{ + NUMERIC_FMT_HANDLER; +} + +template<> +CharacterDevice & CharacterDevice::operator << (const NumericFormat & fmt) +{ + NUMERIC_FMT_HANDLER; +} + +template<> +CharacterDevice & CharacterDevice::operator << (const NumericFormat & fmt) +{ + NUMERIC_FMT_HANDLER; +} + +template<> +CharacterDevice & CharacterDevice::operator << (const NumericFormat & fmt) +{ + NUMERIC_FMT_HANDLER; +} + +uint32_t CharacterDevice::printNumericPrefix(uint32_t base) +{ + switch(base) { + case 2: *this << "0b"; return 2; + case 8: *this << "0o"; return 2; + case 10: return 0; + case 16: *this << "0x"; return 2; + default: + *this << "[" << base << "]x"; + if(base < 10) return 4; + if(base < 100) return 5; + return 6; + } +} + +void CharacterDevice::putrep(char c, uint32_t repetitions) +{ + for(uint32_t i = 0; i < repetitions; i++) + this->write(c); +} \ No newline at end of file diff --git a/prototypes-rework/libserial/serial.cpp b/prototypes-rework/libserial/serial.cpp index 381c5fa..11f76ee 100644 --- a/prototypes-rework/libserial/serial.cpp +++ b/prototypes-rework/libserial/serial.cpp @@ -53,13 +53,13 @@ bool SerialPort::isReceiveEmpty() const return (inb(this->mBase + LSR) & 1) == 0; } -void SerialPort::write(uint8_t c) +void SerialPort::write(char c) { while (this->isTransmitEmpty() == false); outb(this->mBase, c); } -uint8_t SerialPort::read() +char SerialPort::read() { while (this->isReceiveEmpty()); return inb(this->mBase); diff --git a/prototypes-rework/video/Makefile b/prototypes-rework/video/Makefile index 5d30401..592594a 100644 --- a/prototypes-rework/video/Makefile +++ b/prototypes-rework/video/Makefile @@ -5,7 +5,7 @@ include ../config.mk KERNEL = video.ker -LIBS = -lboot -lvideo -lserial +LIBS += -lboot -lvideo -lchardev -lserial -lbase -lgcc all: builddir $(KERNEL) diff --git a/prototypes-rework/video/src/init.cpp b/prototypes-rework/video/src/init.cpp index 1087f3b..cf7e03f 100644 --- a/prototypes-rework/video/src/init.cpp +++ b/prototypes-rework/video/src/init.cpp @@ -8,10 +8,7 @@ extern "C" void init(multiboot::Structure const & data) { SerialPort serial(SERIAL_COM1); - - serial.write('H'); - serial.write('i'); - serial.write('\n'); + serial << "Hi!\n"; VideoScreen video(*data.vbe.modeInfo); for(uint32_t y = 0; y < video.height(); y++) @@ -22,5 +19,7 @@ extern "C" void init(multiboot::Structure const & data) } } + serial << "w=" << video.width() << " h=" << video.height() << "\n"; + while(true); }