From c99bd68ec3fe7346e248a0c18971f635df31ef59 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 17 Sep 2026 16:22:31 +0800 Subject: [PATCH 1/2] 0.7.0 --- follow openkal 0.13 The specification adds declarations and alters none that this package uses; the exact version requirement moves so that this package resolves in one graph with the rest of the 0.13 wave. Comments carry no emoji. --- .github/workflows/ci.yml | 66 ++++++++++++++++++------------------- README.md | 6 ++-- examples/hello/mcpp.toml | 4 +-- examples/hello/src/main.cpp | 2 +- mcpp.toml | 6 ++-- src/kal.cpp | 12 +++---- src/uefi.h | 4 +-- 7 files changed, 50 insertions(+), 50 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c3168f..fd436dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: - name: Install mcpp run: | - # ⚠️ A LOOP, BECAUSE ONE `xlings update` CAN RETURN A STALE INDEX + # A LOOP, BECAUSE ONE `xlings update` CAN RETURN A STALE INDEX # WITHOUT SAYING SO. # # The index is published as an artifact behind a pointer, and that @@ -57,7 +57,7 @@ jobs: # of the two situations it is. for attempt in 1 2 3 4 5 6; do xlings update > /dev/null 2>&1 || true - if # ⚠️ THE PIN MAY NAME THE RELEASE THIS RUN IS VALIDATING, which does + if # THE PIN MAY NAME THE RELEASE THIS RUN IS VALIDATING, which does # not exist yet — that is the whole point of MCPP_SOURCE_REF. Bootstrap # from whatever the index has; the step below replaces it with the # build under review, and the pin is what an ordinary run tests. @@ -73,7 +73,7 @@ jobs: echo "the index has not caught up yet (attempt $attempt of 6); waiting 60s" sleep 60 done - # ⚠️⚠️ TRANSITION: GIVE THE BOOTSTRAP THE glibc ITS BINDING NAMES. + # TRANSITION: GIVE THE BOOTSTRAP THE glibc ITS BINDING NAMES. # # `xim:glibc`'s `latest` moved from `2.44` to `2.44.2`. A payload # directory is named after the version a request RESOLVED to, while a @@ -85,13 +85,13 @@ jobs: # error: selected RuntimeBinding glibc@2.44 requires payload # '…/xpkgs/xim-x-glibc/2.44', but it is not installed # - # ⚠️ On every NEW machine and on none that already existed, which is why + # On every NEW machine and on none that already existed, which is why # it is invisible from a developer's own. Measured on `main` as readily # as on any branch — the index records the same failure verbatim in # `pkgs/g/glibc.lua` and states the rule it broke: "The index is DATA # and the client is a PROGRAM: the consumer ships first." # - # ⭐ REMOVE THIS once a released mcpp resolves it. `mcpp 2026.8.27.1` + # REMOVE THIS once a released mcpp resolves it. `mcpp 2026.8.27.1` # accepts an installed payload whose version REFINES the requested one # (`payload_dir_for_version`), so a bootstrap from it needs nothing # here. Until then the missing payload is simply installed. @@ -103,13 +103,13 @@ jobs: fi mcpp --version mcpp self config --mirror GLOBAL - # ⭐⭐ CROSS-VALIDATION: BUILD THE mcpp UNDER REVIEW AND USE THAT ONE. + # CROSS-VALIDATION: BUILD THE mcpp UNDER REVIEW AND USE THAT ONE. # # Empty in the ordinary run, so this job keeps testing the RELEASED # mcpp the pin above names. Set it — `workflow_dispatch` input, or the # repository variable — and the same job runs against that source. # - # ⚠️ THIS EXISTS BECAUSE THE ORDER USED TO BE WRONG. Several mcpp + # THIS EXISTS BECAUSE THE ORDER USED TO BE WRONG. Several mcpp # releases went out green and only then turned this ecosystem red: the # engine's own CI cannot see a defect that appears only in a real # dependency graph, and this repository could not see the engine until @@ -122,7 +122,7 @@ jobs: [ -d "$src" ] || git clone --quiet --depth 1 \ --branch "$MCPP_SOURCE_REF" \ https://github.com/mcpp-community/mcpp.git "$src" - # ⚠️ THE CLONE'S OWN WORKSPACE PIN MUST NOT DECIDE WHICH mcpp + # THE CLONE'S OWN WORKSPACE PIN MUST NOT DECIDE WHICH mcpp # BUILDS IT. `.xlings.json` at mcpp's root pins the mcpp that # compiles mcpp, and that pin does not move when mcpp is released — # so a build inside the checkout obeys it and tries to install a @@ -135,7 +135,7 @@ jobs: # above, which is exactly what removing the file leaves. rm -f "$src/.xlings.json" ( cd "$src" && mcpp build --release ) - # ⚠️ BOTH SPELLINGS, AND NO `-perm`. The matrix reaches Windows and + # BOTH SPELLINGS, AND NO `-perm`. The matrix reaches Windows and # macOS runners too: on Windows the artefact is `mcpp.exe`, and # `-perm -u+x` is not a question that filesystem answers the way this # expects. Measured: `Finished release [optimized] in 173.44s` @@ -149,13 +149,13 @@ jobs: \( -name mcpp -o -name mcpp.exe \) | head -1) [ -n "$built" ] || { echo "::error::mcpp did not build from $MCPP_SOURCE_REF"; exit 1; } echo "$(cd "$(dirname "$built")" && pwd)" >> "$GITHUB_PATH" - # ⚠️ Reported, because a PATH entry that does not win looks exactly + # Reported, because a PATH entry that does not win looks exactly # like one that does until something built with the wrong engine. echo "under review: $("$built" --version) (from $MCPP_SOURCE_REF)" fi - # ⚠️⚠️ A JOB THAT RESOLVES THE PUBLISHED SPECIFICATION CANNOT REVIEW A + # A JOB THAT RESOLVES THE PUBLISHED SPECIFICATION CANNOT REVIEW A # CHANGE TO ONE. # # Another step in this file already clones the specification and @@ -167,7 +167,7 @@ jobs: # synced index ... the index is current, so this name is either # wrong or not published yet # - # ⭐⭐ THE UNIT IS THE JOB, NOT THE REPOSITORY. Measured 2026-08-28 across + # THE UNIT IS THE JOB, NOT THE REPOSITORY. Measured 2026-08-28 across # the eight repositories of this ecosystem while one change spanned all of # them: seven jobs in three repositories had this shape, and every one of # those repositories ALSO had a job that substituted correctly --- which is @@ -187,11 +187,11 @@ jobs: else echo "the specification has no $OPENKAL_BRANCH; its default branch is used" fi - # ⚠️ EVERY MANIFEST THAT NAMES IT, AND BY THE SAME FORM. mcpp refuses + # EVERY MANIFEST THAT NAMES IT, AND BY THE SAME FORM. mcpp refuses # a graph in which one package reaches a dependency by version and # another by path, so substituting only the root leaves the build # refusing for a second reason instead of the first. - # ⚠️ NOT `sed -i'. This step runs on macOS and on Windows too, and + # NOT `sed -i'. This step runs on macOS and on Windows too, and # BSD sed requires an argument to -i that GNU sed refuses. A temporary # file is the spelling that holds on all three. subst() { # subst @@ -212,7 +212,7 @@ jobs: mcpp build EXE=$(find target -name "hello.exe" | head -1) - # ⚠️ THE TOOLCHAIN THAT BUILT THE IMAGE, NOT LLVM. + # THE TOOLCHAIN THAT BUILT THE IMAGE, NOT LLVM. # # This target resolves to mingw-cross-gcc, so on a machine that has # never built anything else there is no LLVM to borrow `llvm-readobj` @@ -224,7 +224,7 @@ jobs: OD=$(ls -d "$HOME"/.mcpp/registry/data/xpkgs/xim-x-mingw-cross-gcc/*/bin/x86_64-w64-mingw32-objdump | head -1) test -x "$OD" - # ⚠️ Subsystem 10 is what makes firmware load it. A Windows console + # Subsystem 10 is what makes firmware load it. A Windows console # subsystem would build, link, and never be executed by UEFI. # Measured spelling from this objdump: `Subsystem 0000000a (EFI application)`. "$OD" -p "$EXE" | grep -qE 'Subsystem[[:space:]]+0000000a' @@ -259,7 +259,7 @@ jobs: # A package that has only ever been built from Linux is a package whose # consumers must use Linux, and nothing in these sources says so. # - # ⚠️ A TOOLCHAIN AXIS IS ABSENT HERE, AND THAT IS MEASURED RATHER THAN + # A TOOLCHAIN AXIS IS ABSENT HERE, AND THAT IS MEASURED RATHER THAN # ASSUMED. The row for a bare-metal triple names its compiler, and the # command-line override does not displace it: `--toolchain gcc@16.1.0` on a # `riscv64-none-elf` build resolves llvm@22.1.8 regardless. A matrix over @@ -268,7 +268,7 @@ jobs: # choice is real — openkal, whose declarations are compiled by three families # on three systems. # - # ⚠️ BUILD ONLY, AND DELIBERATELY. Behaviour is asserted once, above, under an + # BUILD ONLY, AND DELIBERATELY. Behaviour is asserted once, above, under an # emulator. Booting the same image from three systems would be a statement # about the emulator rather than about this package, and "the image does what # the README says" does not become more true for having been observed from @@ -312,7 +312,7 @@ jobs: - name: Install mcpp run: | - # ⚠️ A LOOP, BECAUSE ONE `xlings update` CAN RETURN A STALE INDEX + # A LOOP, BECAUSE ONE `xlings update` CAN RETURN A STALE INDEX # WITHOUT SAYING SO. # # The index is published as an artifact behind a pointer, and that @@ -333,7 +333,7 @@ jobs: # of the two situations it is. for attempt in 1 2 3 4 5 6; do xlings update > /dev/null 2>&1 || true - if # ⚠️ THE PIN MAY NAME THE RELEASE THIS RUN IS VALIDATING, which does + if # THE PIN MAY NAME THE RELEASE THIS RUN IS VALIDATING, which does # not exist yet — that is the whole point of MCPP_SOURCE_REF. Bootstrap # from whatever the index has; the step below replaces it with the # build under review, and the pin is what an ordinary run tests. @@ -349,7 +349,7 @@ jobs: echo "the index has not caught up yet (attempt $attempt of 6); waiting 60s" sleep 60 done - # ⚠️⚠️ TRANSITION: GIVE THE BOOTSTRAP THE glibc ITS BINDING NAMES. + # TRANSITION: GIVE THE BOOTSTRAP THE glibc ITS BINDING NAMES. # # `xim:glibc`'s `latest` moved from `2.44` to `2.44.2`. A payload # directory is named after the version a request RESOLVED to, while a @@ -361,13 +361,13 @@ jobs: # error: selected RuntimeBinding glibc@2.44 requires payload # '…/xpkgs/xim-x-glibc/2.44', but it is not installed # - # ⚠️ On every NEW machine and on none that already existed, which is why + # On every NEW machine and on none that already existed, which is why # it is invisible from a developer's own. Measured on `main` as readily # as on any branch — the index records the same failure verbatim in # `pkgs/g/glibc.lua` and states the rule it broke: "The index is DATA # and the client is a PROGRAM: the consumer ships first." # - # ⭐ REMOVE THIS once a released mcpp resolves it. `mcpp 2026.8.27.1` + # REMOVE THIS once a released mcpp resolves it. `mcpp 2026.8.27.1` # accepts an installed payload whose version REFINES the requested one # (`payload_dir_for_version`), so a bootstrap from it needs nothing # here. Until then the missing payload is simply installed. @@ -379,13 +379,13 @@ jobs: fi mcpp --version mcpp self config --mirror GLOBAL - # ⭐⭐ CROSS-VALIDATION: BUILD THE mcpp UNDER REVIEW AND USE THAT ONE. + # CROSS-VALIDATION: BUILD THE mcpp UNDER REVIEW AND USE THAT ONE. # # Empty in the ordinary run, so this job keeps testing the RELEASED # mcpp the pin above names. Set it — `workflow_dispatch` input, or the # repository variable — and the same job runs against that source. # - # ⚠️ THIS EXISTS BECAUSE THE ORDER USED TO BE WRONG. Several mcpp + # THIS EXISTS BECAUSE THE ORDER USED TO BE WRONG. Several mcpp # releases went out green and only then turned this ecosystem red: the # engine's own CI cannot see a defect that appears only in a real # dependency graph, and this repository could not see the engine until @@ -398,7 +398,7 @@ jobs: [ -d "$src" ] || git clone --quiet --depth 1 \ --branch "$MCPP_SOURCE_REF" \ https://github.com/mcpp-community/mcpp.git "$src" - # ⚠️ THE CLONE'S OWN WORKSPACE PIN MUST NOT DECIDE WHICH mcpp + # THE CLONE'S OWN WORKSPACE PIN MUST NOT DECIDE WHICH mcpp # BUILDS IT. `.xlings.json` at mcpp's root pins the mcpp that # compiles mcpp, and that pin does not move when mcpp is released — # so a build inside the checkout obeys it and tries to install a @@ -411,7 +411,7 @@ jobs: # above, which is exactly what removing the file leaves. rm -f "$src/.xlings.json" ( cd "$src" && mcpp build --release ) - # ⚠️ BOTH SPELLINGS, AND NO `-perm`. The matrix reaches Windows and + # BOTH SPELLINGS, AND NO `-perm`. The matrix reaches Windows and # macOS runners too: on Windows the artefact is `mcpp.exe`, and # `-perm -u+x` is not a question that filesystem answers the way this # expects. Measured: `Finished release [optimized] in 173.44s` @@ -425,13 +425,13 @@ jobs: \( -name mcpp -o -name mcpp.exe \) | head -1) [ -n "$built" ] || { echo "::error::mcpp did not build from $MCPP_SOURCE_REF"; exit 1; } echo "$(cd "$(dirname "$built")" && pwd)" >> "$GITHUB_PATH" - # ⚠️ Reported, because a PATH entry that does not win looks exactly + # Reported, because a PATH entry that does not win looks exactly # like one that does until something built with the wrong engine. echo "under review: $("$built" --version) (from $MCPP_SOURCE_REF)" fi - # ⚠️⚠️ A JOB THAT RESOLVES THE PUBLISHED SPECIFICATION CANNOT REVIEW A + # A JOB THAT RESOLVES THE PUBLISHED SPECIFICATION CANNOT REVIEW A # CHANGE TO ONE. # # Another step in this file already clones the specification and @@ -443,7 +443,7 @@ jobs: # synced index ... the index is current, so this name is either # wrong or not published yet # - # ⭐⭐ THE UNIT IS THE JOB, NOT THE REPOSITORY. Measured 2026-08-28 across + # THE UNIT IS THE JOB, NOT THE REPOSITORY. Measured 2026-08-28 across # the eight repositories of this ecosystem while one change spanned all of # them: seven jobs in three repositories had this shape, and every one of # those repositories ALSO had a job that substituted correctly --- which is @@ -463,11 +463,11 @@ jobs: else echo "the specification has no $OPENKAL_BRANCH; its default branch is used" fi - # ⚠️ EVERY MANIFEST THAT NAMES IT, AND BY THE SAME FORM. mcpp refuses + # EVERY MANIFEST THAT NAMES IT, AND BY THE SAME FORM. mcpp refuses # a graph in which one package reaches a dependency by version and # another by path, so substituting only the root leaves the build # refusing for a second reason instead of the first. - # ⚠️ NOT `sed -i'. This step runs on macOS and on Windows too, and + # NOT `sed -i'. This step runs on macOS and on Windows too, and # BSD sed requires an argument to -i that GNU sed refuses. A temporary # file is the spelling that holds on all three. subst() { # subst @@ -479,7 +479,7 @@ jobs: subst mcpp.toml .spec - name: The EFI application cross-builds run: | - # ⚠️ TWICE, AND THE FIRST IS ALLOWED TO FAIL — every row of this + # TWICE, AND THE FIRST IS ALLOWED TO FAIL — every row of this # matrix is a machine that has never targeted this triple, which is # precisely where mcpp's lazy install of the target C library shows. # The first build compiles sources needing its headers before they diff --git a/README.md b/README.md index 4dac704..a548b64 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ openkal = "0.9.0" openkal-uefi = "0.3.0" ``` -## ⚠️ The target is `x86_64-windows-gnu`, and that is not a workaround +## The target is `x86_64-windows-gnu`, and that is not a workaround A UEFI application is PE/COFF with subsystem 10, entered through the Microsoft x64 calling convention. Both are properties this toolchain already has, so @@ -38,7 +38,7 @@ way. This backend is mostly forwarding: | `memory` | `AllocatePool` / `FreePool` | | `abort` | `Exit` | -⚠️ `process` and `task` are absent because UEFI has no process model. An +`process` and `task` are absent because UEFI has no process model. An application is the only thing running, and an interface provided in part would be worse than one provided not at all — `import openkal.process;` does not resolve, which is the honest answer rather than a set of calls that always fail. @@ -66,7 +66,7 @@ bytes. A stricter request is satisfied by over-allocating and storing the original pointer immediately before the aligned address, which is what a C library does where the platform lacks `aligned_alloc`. -**Input.** ⚠️ `kal_stream_read` reports end of input rather than pretending. +**Input.** `kal_stream_read` reports end of input rather than pretending. UEFI's console input is a key-stroke protocol with a wait event, not a byte stream; presenting it as one would give a reader something that appears to work and silently loses every key that is not a plain character. diff --git a/examples/hello/mcpp.toml b/examples/hello/mcpp.toml index d8116de..705c56a 100644 --- a/examples/hello/mcpp.toml +++ b/examples/hello/mcpp.toml @@ -4,14 +4,14 @@ version = "0.1.0" [build] target = "x86_64-windows-gnu" -# ⚠️ The three that make this an application firmware will load, rather than a +# The three that make this an application firmware will load, rather than a # Windows one. `-nostdlib` removes the C runtime and the default import # libraries; `--subsystem,10` is EFI_APPLICATION; the entry point is the one # UEFI calls, not `mainCRTStartup`. ldflags = ["-nostdlib", "-Wl,--subsystem,10", "-Wl,-e,efi_main"] [dependencies] -# ⚠️ THE FORM MUST MATCH THE ONE THE IMPLEMENTATION USES, not merely the +# THE FORM MUST MATCH THE ONE THE IMPLEMENTATION USES, not merely the # version. mcpp refuses a graph in which one package reaches a dependency by # version and another by git: # diff --git a/examples/hello/src/main.cpp b/examples/hello/src/main.cpp index 79abb12..2f34428 100644 --- a/examples/hello/src/main.cpp +++ b/examples/hello/src/main.cpp @@ -1,6 +1,6 @@ // A UEFI application written against openkal, with no UEFI type in sight. // -// ⚠️ `kal_main` and not `main`: nothing here supplies a C runtime, and the +// `kal_main` and not `main`: nothing here supplies a C runtime, and the // backend's `efi_main` is what firmware calls. That indirection is the point — // the same source compiles against any openkal implementation. import openkal.stream; diff --git a/mcpp.toml b/mcpp.toml index afba8fd..1b8b7c4 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,6 +1,6 @@ # openkal on UEFI Boot Services. # -# ⚠️ THE TARGET IS `x86_64-windows-gnu`, AND THAT IS NOT A WORKAROUND. +# THE TARGET IS `x86_64-windows-gnu`, AND THAT IS NOT A WORKAROUND. # # A UEFI application is PE/COFF with subsystem 10, entered through the Microsoft # x64 calling convention. Both are properties this toolchain already has, so @@ -15,7 +15,7 @@ [package] namespace = "mcpplibs" name = "openkal-uefi" -version = "0.6.0" +version = "0.7.0" description = "An implementation of openkal on UEFI Boot Services, for applications the firmware loads before an operating system exists" license = "Apache-2.0" @@ -32,7 +32,7 @@ authors = ["mcpplibs"] repo = "https://github.com/mcpplibs/openkal-uefi" [dependencies] -openkal = "0.12.0" +openkal = "0.13.0" [build] flags = [ diff --git a/src/kal.cpp b/src/kal.cpp index 3735f9d..1cf0fe1 100644 --- a/src/kal.cpp +++ b/src/kal.cpp @@ -11,13 +11,13 @@ // openkal.memory AllocatePool / FreePool // openkal.abort Exit // -// ⚠️ `process` and `task` are absent because UEFI has no process model — an +// `process` and `task` are absent because UEFI has no process model — an // application is the only thing running, and an interface provided in part // would be worse than one provided not at all. `import openkal.process;` does // not resolve, which is the honest answer rather than a set of calls that // always fail. // -// ⚠️ THE ONE PLACE THIS IS NOT FORWARDING: TEXT. +// THE ONE PLACE THIS IS NOT FORWARDING: TEXT. // // openkal streams carry bytes. UEFI's console takes UCS-2 and treats a line // feed on its own as a bare cursor movement. Every write therefore widens and @@ -54,7 +54,7 @@ kal_intptr write_to(efi_simple_text_output_protocol* out, unsigned w = 0; while (done < n && w < kChunk * 2) { const unsigned char c = p[done]; - // ⚠️ A bare LF moves the cursor down without returning it, so a + // A bare LF moves the cursor down without returning it, so a // second line begins under the end of the first. Firmware differs // in how it renders that; none of them do what the writer meant. if (c == '\n') buf[w++] = u'\r'; @@ -75,7 +75,7 @@ extern "C" { // The entry point firmware calls. It records the two values every other // function here needs and then enters the program. // -// ⚠️ `kal_main` and not `main`: there is no C runtime to call one, and naming +// `kal_main` and not `main`: there is no C runtime to call one, and naming // it `main` would invite a toolchain to attach startup code that does not exist // in this arrangement. [[noreturn]] void kal_main(); @@ -122,7 +122,7 @@ kal_intptr kal_stream_write(kal_stream s, const void* buf, kal_uintptr n) { return write_to(out, static_cast(buf), n); } -// ⚠️ Not provided in a usable form, and reported rather than faked. UEFI's +// Not provided in a usable form, and reported rather than faked. UEFI's // console input is a key-stroke protocol with a wait event, not a byte stream; // presenting it as one would give a reader something that appears to work and // loses every key that is not a plain character. Returning "no bytes, no error" @@ -147,7 +147,7 @@ kal_uintptr kal_stream_props(kal_stream s) { // ── openkal.memory ────────────────────────────────────────────────────────── // -// ⚠️ AllocatePool has no alignment parameter. It guarantees 8-byte alignment, +// AllocatePool has no alignment parameter. It guarantees 8-byte alignment, // which covers every fundamental type on this architecture but not an // over-aligned one. Rather than return memory that does not meet the request, // a stricter alignment is satisfied by over-allocating and storing the original diff --git a/src/uefi.h b/src/uefi.h index 6e65695..f826fe7 100644 --- a/src/uefi.h +++ b/src/uefi.h @@ -1,6 +1,6 @@ /* The part of the UEFI specification this implementation uses. * - * ⚠️ WHY THE MinGW TARGET AND NOT A BARE-METAL ONE. + * WHY THE MinGW TARGET AND NOT A BARE-METAL ONE. * * A UEFI application is PE/COFF with subsystem 10, and it is entered through * the Microsoft x64 calling convention. Both are properties this toolchain @@ -14,7 +14,7 @@ * produces `IMAGE_SUBSYSTEM_EFI_APPLICATION` with no DLL imports, which is * exactly what firmware loads. * - * ⚠️ THE OFFSETS ARE CHECKED, NOT TRUSTED. + * THE OFFSETS ARE CHECKED, NOT TRUSTED. * * These tables are ABI, and a field written one slot out reads a neighbouring * function pointer — which calls something that exists, with the wrong From c5da572584d2c63e20fbecb4c7516487224b91d6 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 17 Sep 2026 17:47:43 +0800 Subject: [PATCH 2/2] README: the install line names this release --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a548b64..c538936 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,8 @@ target = "x86_64-windows-gnu" ldflags = ["-nostdlib", "-Wl,--subsystem,10", "-Wl,-e,efi_main"] [dependencies] -openkal = "0.9.0" -openkal-uefi = "0.3.0" +openkal = "0.13.0" +openkal-uefi = "0.7.0" ``` ## The target is `x86_64-windows-gnu`, and that is not a workaround