diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ef31f1203..d3fb47d54 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -6,15 +6,14 @@ on: pull_request: branches: ["**"] workflow_dispatch: - branches: ["**"] jobs: collect-solutions: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: "Checkout code" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Find Dockerfiles" id: "solutions" @@ -31,10 +30,10 @@ jobs: solutions: ${{ steps.solutions.outputs.solutions }} build: - env: + env: ENABLE_DOCKER_PUSH: ${{ github.repository_owner == 'PlummersSoftwareLLC' && github.ref == 'refs/heads/drag-race' }} - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" needs: ["collect-solutions"] @@ -45,7 +44,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Normalize solution name" id: solution-name @@ -76,21 +75,21 @@ jobs: config_file: ./config/hadolint.yml - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub if: ${{ env.ENABLE_DOCKER_PUSH == 'true' }} - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build amd64 if: steps.arch-amd64.outputs.build - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v6 with: tags: primeimages/primes:${{ steps.solution-name.outputs.normalized }} context: ${{ matrix.solution }} @@ -98,10 +97,10 @@ jobs: push: ${{ env.ENABLE_DOCKER_PUSH }} cache-from: type=registry,ref=primeimages/primes:${{ steps.solution-name.outputs.normalized }} cache-to: type=inline - + - name: Build arm64 if: steps.arch-arm64.outputs.build - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v6 with: tags: primeimages/primes:${{ steps.solution-name.outputs.normalized }} context: ${{ matrix.solution }} diff --git a/PrimeBrainFuck/solution_1/Dockerfile b/PrimeBrainFuck/solution_1/Dockerfile index a95b5d9af..11b276987 100644 --- a/PrimeBrainFuck/solution_1/Dockerfile +++ b/PrimeBrainFuck/solution_1/Dockerfile @@ -1,25 +1,13 @@ # container for building -FROM ubuntu:18.04 AS build +FROM ubuntu:22.04 AS build # install tools RUN apt-get update \ - && apt-get install -y lsb-release wget software-properties-common git - -# install clang-12 for C++ standard 17 -RUN wget https://apt.llvm.org/llvm.sh \ - && chmod +x llvm.sh \ - && ./llvm.sh 12 + && apt-get install -y lsb-release wget software-properties-common git clang cmake # set clang as default compiler for C and C++ -ENV CC=/usr/bin/clang-12 \ - CXX=/usr/bin/clang++-12 - -# install latest version of cmake -SHELL ["/bin/bash", "-o", "pipefail", "-c"] -RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | apt-key add - \ - && apt-add-repository 'deb https://apt.kitware.com/ubuntu/ bionic main' \ - && apt-get update \ - && apt-get install -y cmake +ENV CC=/usr/bin/clang \ + CXX=/usr/bin/clang++ # clone custom language interpreter RUN git clone https://github.com/ThatAquarel/BrainF-ck-Interpreter \ @@ -34,10 +22,10 @@ RUN cmake -DCMAKE_BUILD_TYPE=Release .. \ # build prime sieve caller WORKDIR /opt/app/ COPY *.cpp *.b ./ -RUN clang++-12 -Ofast -std=c++17 PrimeBrainFuck.cpp -oPrimeBrainFuck +RUN clang++ -Ofast -std=c++17 PrimeBrainFuck.cpp -oPrimeBrainFuck # container for running built binaries -FROM ubuntu:18.04 +FROM ubuntu:22.04 # copy binaries from build container to current COPY --from=build /BrainF-ck-Interpreter/release/brainfuck /usr/local/bin diff --git a/PrimeMACRO11/solution_1/README.md b/PrimeMACRO11/solution_1/README.md new file mode 100644 index 000000000..c2955949f --- /dev/null +++ b/PrimeMACRO11/solution_1/README.md @@ -0,0 +1,37 @@ +# MACRO-11 solution by davepl + +![Algorithm](https://img.shields.io/badge/Algorithm-base-green) +![Faithfulness](https://img.shields.io/badge/Faithful-no-yellowgreen) +![Parallelism](https://img.shields.io/badge/Parallel-no-green) +![Bit count](https://img.shields.io/badge/Bits-1-green) +![Bit count](https://img.shields.io/badge/Bits-8-yellowgreen) +![Deviation](https://img.shields.io/badge/Deviation-sievesize-blue) + +## Description + +This solution provides two implementations in [MACRO-11](https://en.wikipedia.org/wiki/MACRO-11), that being the macro assembly language for the [DEC PDP-11](https://en.wikipedia.org/wiki/PDP-11) range of computers. + +## Implementations and sieve sizes + +This solution includes two implementations: + +- One ([SIEVE.ASM](SIEVE.ASM)) uses one byte per prime candidate. Due to applicable memory constraints, the sieve size for this implementation is 1,000. +- The other ([SIEVE2.ASM](SIEVE2.ASM)) uses one bit per prime candidate. This implementation's sieve size is 10,000. + +## Run instructions + +This solution's implementations can be assembled and executed on an actual PDP-11 computer or a sufficiently complete emulator, provided it's running an operating system that has MACRO-11 installed. A list of available emulators can be found in the [Emulators section on the PDP-11 Wikipedia page](https://en.wikipedia.org/wiki/PDP-11#Emulators). + +Use the following commands to edit, assemble and run an implementation on the solution. These specific commands apply to a PDP-11 running RT-11 and build and run SIEVE2.ASM which is located on the D1 device; modify the commands to match your specific situation where appropriate: + +```text +macro d1:sieve2.asm +link sieve2 +run sieve2 +``` + +## Results + +This is an image showing the results of an execution of SIEVE2.ASM on an actual PDP-11/34. The "ticks" that are reported are time units equal to 1/60th of a second on PDP-11s that are connected to a 60Hz power grid. On machines that are powered from a 50Hz grid, a tick _may_ be 1/50th of a second instead, but that is not always the case. + +![SIEVE2.ASM results](sieve2_result.jpg) diff --git a/PrimeMACRO11/solution_1/SIEVE.ASM b/PrimeMACRO11/solution_1/SIEVE.ASM new file mode 100644 index 000000000..ff55fc178 --- /dev/null +++ b/PrimeMACRO11/solution_1/SIEVE.ASM @@ -0,0 +1,165 @@ +;--------------------------------------------------------------------- + .TITLE SIEVE ; Program title +;--------------------------------------------------------------------- + + .MCALL .PRINT,.EXIT,.TTYOUT ; System macros + +;-- String Table ----------------------------------------------------- + +HELLOMSG: .ASCIZ /Sieve of Eratosthenes by Davepl 2024/ +DASHESMSG: .ASCIZ /----------------------------------/ +CBITSMSG: .ASCIZ /Clearing byte array. Sieve Size:/ +MSG2: .ASCIZ /Setup complete/ +RUNMSG: .ASCIZ /Running sieve/ +DONEMSG: .ASCIZ /Sieve complete/ +PRIMMSG: .ASCIZ /Prime numbers found:/ +MARKCMPMSG: .ASCIZ /Marking composite: / +CHECKCMPMSG: .ASCIZ /Checking if composite: / +EMPTYMSG: .ASCIZ // + +;-- Constants --------------------------------------------------------- + +LIMIT = 1000. ; Upper limit for primes +BSIZE = 1000. ; Size of byte array (fixed value) + + .EVEN ; Ensure we're on a word boundary + +;-- Code Entry -------------------------------------------------------- + +START: .PRINT #HELLOMSG + .PRINT #DASHESMSG + .PRINT #CBITSMSG + MOV #LIMIT, R0 + JSR PC, PRNUM + .PRINT #EMPTYMSG + +;-- Clear the byte array ----------------------------------------------- + + MOV #BSIZE, R1 + CLR R2 +INITL: + CLRB BYTEARR(R2) + INC R2 + CMP R2, R1 + BNE INITL + + .PRINT #MSG2 + .PRINT #RUNMSG + +;-- Run the sieve ------------------------------------------------------ + + MOV #3, R1 ; Start with 3 (first odd prime) +SIEVE: + CMP R1, #LIMIT ; Check if we've reached the limit + BGE DONE.SV ; If so, we're done + +; Debug Output +; .PRINT #CHECKCMPMSG ; Print the number we're checking next +; MOV R1, R0 +; JSR PC, PRNUM +; .PRINT #EMPTYMSG + + JSR PC, ISCOMP ; Check if R1 is composite + BNE NXTODD ; If prime, skip to next odd number + + MOV R1, R2 ; R2 = R1 (prime number found) +MARK: + ADD R1, R2 ; R2 += R1 (next multiple) + CMP R2, #LIMIT ; Check if we've exceeded the limit + BGE NXTODD ; If so, move to next odd number + +; Debug Output +; .PRINT #MARKCMPMSG ; Print the multiple we're marking next +; MOV R2, R0 +; JSR PC, PRNUM +; .PRINT #EMPTYMSG + + JSR PC, SETCMP ; Mark R2 as composite + BR MARK ; Continue marking multiples + +NXTODD: + ADD #2, R1 ; Move to next odd number + BR SIEVE ; Continue sieving + +DONE.SV: + .PRINT #DONEMSG + .PRINT #PRIMMSG + +;-- Print prime numbers ------------------------------------------------ + + MOV #1, R3 ; Prime count in R3 + + MOV #2, R0 ; Start with 2 (only even prime) + JSR PC, PRNUM + MOV #',, R0 + .TTYOUT R0 + + MOV #3, R1 ; Start checking odd numbers from 3 +PRNLP: + CMP R1, #LIMIT ; Check if we've reached the limit + BGE DONE ; If so, we're done + + JSR PC, ISCOMP ; Check if R1 is composite + BNE PRNXT ; If prime, skip to next odd number + + MOV R1, R0 + JSR PC, PRNUM ; Print the prime number + INC R3 ; Bump the count + MOV #',, R0 + .TTYOUT R0 +PRNXT: + ADD #2, R1 ; Move to next odd number + BR PRNLP ; Continue printing primes + +DONE: .PRINT EMPTYMSG + MOV R3, R0 ; Print count of primes + JSR PC, PRNUM + + .EXIT ; Exit program + +;-- Subroutines -------------------------------------------------------- + +ISCOMP: + MOV R1, R0 + MOVB BYTEARR(R0), R2 ; Load byte from byte array + TSTB R2 ; Check if byte is non-zero + BEQ ISPRIME ; If byte is zero, number is prime + MOV #1, R0 ; Return 1 if composite + RTS PC +ISPRIME: + CLR R0 ; Return 0 if prime + RTS PC + +SETCMP: + MOV R2, R0 + MOVB #1, BYTEARR(R0) ; Set the byte in the array + RTS PC + +PRNUM: +BTOA: + MOV R0, -(SP) ; Save R0 on stack + MOV R1, -(SP) ; Save R1 on stack + MOV R2, -(SP) ; Save R2 on stack + + MOV R0, R1 ; Move number to R1 (low part of dividend) + CLR R0 ; Clear R0 (high part of dividend) + DIV #10., R0 ; Divide R0:R1 by 10, quotient in R0, remainder in R1 + + TST R0 ; Check if quotient is 0 + BEQ PRINT ; If quotient is 0, print digit + JSR PC, BTOA ; Recursive call with quotient + +PRINT: ADD #'0, R1 ; Convert remainder to ASCII + MOV R1, R0 ; Move ASCII digit to R0 + .TTYOUT R0 ; Print the digit + + MOV (SP)+, R2 ; Restore R2 + MOV (SP)+, R1 ; Restore R1 + MOV (SP)+, R0 ; Restore R0 + RTS PC ; Return + + +BYTEARR: .BLKB BSIZE ; Byte array for sieve + + .END START + diff --git a/PrimeMACRO11/solution_1/SIEVE2.ASM b/PrimeMACRO11/solution_1/SIEVE2.ASM new file mode 100644 index 000000000..9854abfc2 --- /dev/null +++ b/PrimeMACRO11/solution_1/SIEVE2.ASM @@ -0,0 +1,259 @@ +;----------------------------------------------------------------------- +; SIEVE2.ASM - Sieve of Eratosthenes in PDP-11 assembly language +; by Dave Plummer 2024 +;----------------------------------------------------------------------- +; +; This variant uses a single bit per number to mark composites, rather +; than a byte per number. This reduces memory usage by a factor of 8 +; so we can sieve larger ranges, but requires more complex bit work. + + .TITLE SIEVE ; Program title + .MCALL .PRINT,.EXIT,.TTYOUT, .GTIM ; System macros + .GLOBL $DIVTK,$DIV60 ; Global symbols + + +HELLOMSG: .ASCIZ /Sieve of Eratosthenes by Davepl 2024/ +DASHESMSG: .ASCIZ /------------------------------------/ +CBITSMSG: .ASCII /Clearing byte array. Sieve Size: /<200> +RUNMSG: .ASCIZ /Running sieve/ +DONEMSG: .ASCIZ /Sieve complete/ +PRIMMSG: .ASCII /Prime numbers found: /<200> +TIMRMSG: .ASCII /Ticks Elapsed: /<200> +MARKCMPMSG: .ASCIZ /Marking composite: / +CHECKCMPMSG: .ASCIZ /Checking if composite: / +TIMESTMP: .ASCII /Timestamp: /<200> +NEWLINE: .ASCIZ // + +LIMIT = 10000. ; Upper limit for primes +BSIZE = 625. ; Size of byte array (fixed value) + +; Ensure we're on a word boundary in case the strings above are odd length + + .EVEN + +START: .PRINT #HELLOMSG ; Welcome banner + .PRINT #DASHESMSG + .PRINT #CBITSMSG ; Display sieve size + MOV #LIMIT, R0 + JSR PC, PRNUM + .PRINT #NEWLINE + +;-- Clear the byte array ----------------------------------------------- + +; .PRINT #TIMESTMP ; Display timestamp + JSR PC, STRTTMR +; JSR PC, PRNUM +; .PRINT #NEWLINE + + MOV #BSIZE, R1 + CLR R2 +INITL: + CLRB BITARR(R2) ; Clear the byte array + INC R2 + CMP R2, R1 + BNE INITL + + .PRINT #RUNMSG ; Display progress message "Running sieve"; + +;-- Run the sieve ------------------------------------------------------ + + MOV #3, R1 ; Start with 3 (first odd prime) +SIEVE: + CMP R1, #LIMIT ; Check if we've reached the limit + BGE DONESV ; If so, we're done + +; Debug - print the number we're checking +; .PRINT #CHECKCMPMSG +; MOV R1, R0 +; JSR PC, PRNUM +; .PRINT #NEWLINE + + JSR PC, ISCOMP ; Check if R1 is composite + BNE NXTODD ; If prime, skip to next odd number + + MOV R1, R2 ; R2 = R1 (prime number found) +MARK: + ADD R1, R2 ; R2 += R1 (next multiple) + CMP R2, #LIMIT ; Check if we've exceeded the limit + BGE NXTODD ; If so, move to next odd number + +; Debug - print the number we're marking +; .PRINT #MARKCMPMSG +; MOV R2, R0 +; JSR PC, PRNUM +; .PRINT #NEWLINE + + JSR PC, SETCMP ; Mark R2 as composite + BR MARK ; Continue marking multiples + +NXTODD: + ADD #2, R1 ; Move to next odd number + BR SIEVE ; Continue sieving + +DONESV: +; .PRINT #TIMESTMP ; Display timestamp + JSR PC, STOPTMR ; Stop the timer +; JSR PC, PRNUM +; .PRINT #NEWLINE + + .PRINT #DONEMSG + .PRINT #PRIMMSG + +;-- Print prime numbers ------------------------------------------------ + +LISTPRM: + MOV #1, R3 ; Prime count in R3 + +; Debug - Print list of primes +; MOV #2, R0 ; Start with 2 (only even prime) +; JSR PC, PRNUM +; MOV #',, R0 +; .TTYOUT R0 + + MOV #3, R1 ; Start checking odd numbers from 3 +PRNLP: + CMP R1, #LIMIT ; Check if we've reached the limit + BGE DONE ; If so, we're done + + JSR PC, ISCOMP ; Check if R1 is composite + BNE PRNXT ; If bit set, composite, so skip to next odd number + + INC R3 ; Bump the count + +PRNXT: + ADD #2, R1 ; Move to next odd number + BR PRNLP ; Continue printing primes + +DONE: + MOV R3, R0 ; Print count of primes + JSR PC, PRNUM + .PRINT NEWLINE + + .PRINT #TIMRMSG + JSR PC, ELAPSED + JSR PC, PRNUM ; Print elapsed time + + .EXIT ; Exit program + +;-- Subroutines -------------------------------------------------------- + +ISCOMP: + ; Check if the number is even + BIT #1, R1 ; Check the low bit of R1 + BEQ ISNOTPRM ; If even, it's not prime (composite) + + ; Adjust index for odd numbers + MOV R1, R4 ; Copy bit number to R4 + SUB #1, R4 ; Subtract 1 to handle only odd numbers + ASR R4 ; Divide by 2 to get the index for bit array + ASR R4 ; Divide by 8 to get byte offset + ASR R4 + ASR R4 + ADD #BITARR, R4 ; Add base address of BYTEARR + MOV R4, R0 ; Store byte address in R0 + + MOV R1, R5 ; Copy bit number to R5 + SUB #1, R5 ; Subtract 1 to handle only odd numbers + ASR R5 ; Divide by 2 to get bit position + BIC #^B1111111111111000, R5 ; Keep only lower 3 bits (bit position within byte) + MOV #1., R2 ; Prepare a single bit + ASH R5, R2 ; Shift to create bit mask + + BITB R2, (R0) ; Test the bit position in the byte + RTS PC ; Return with condition codes set + +ISNOTPRM: + MOV #1., R0 ; Return 1 (composite) for even numbers + RTS PC ; Return + +SETCMP: + MOV R0, -(SP) ; Preserve R0 + MOV R1, -(SP) ; Preserve R1 + + ; Check if the number is even + BIT #1, R2 ; Check the low bit of R2 + BEQ SETRET ; If even, just return + + ; Adjust index for odd numbers + MOV R2, R4 ; Copy bit number to R4 + SUB #1, R4 ; Subtract 1 to handle only odd numbers + ASR R4 ; Divide by 2 to get the index for bit array + ASR R4 ; Divide by 8 to get byte offset + ASR R4 + ASR R4 + ADD #BITARR, R4 ; Add base address of BYTEARR + MOV R4, R0 ; Store byte address in R0 + + MOV R2, R5 ; Copy bit number to R5 + SUB #1, R5 ; Subtract 1 to handle only odd numbers + ASR R5 ; Divide by 2 to get bit position + BIC #^B1111111111111000, R5 ; Keep only lower 3 bits (bit position within byte) + MOV #1., R1 ; Prepare a single bit + ASH R5, R1 ; Shift to create bit mask + + BISB R1, (R0) ; Set the bit (byte operation) + +SETRET: + MOV (SP)+, R1 ; Restore R1 + MOV (SP)+, R0 ; Restore R0 + RTS PC ; Return + +;----------------------------------------------------------------------- +; PRNUM - Print a number in R1 as ASCII +; All registers preserved +;----------------------------------------------------------------------- + +PRNUM: + MOV R0, -(SP) ; Save R0 on stack + MOV R1, -(SP) ; Save R1 on stack + + MOV R0, R1 ; Move number to R1 (low part of dividend) + CLR R0 ; Clear R0 (high part of dividend) + DIV #10., R0 ; Divide R0:R1 by 10, quotient in R0, remainder in R1 + + TST R0 ; Check if quotient is 0 + BEQ PRINT ; If quotient is 0, print digit + JSR PC, PRNUM ; Recursive like I were back in school + +PRINT: ADD #'0, R1 ; Convert remainder to ASCII + MOV R1, R0 ; Move ASCII digit to R0 + .TTYOUT R0 ; Print the digit + + MOV (SP)+, R1 ; Restore R1 + MOV (SP)+, R0 ; Restore R0 + RTS PC ; Return + + .EVEN + +;----------------------------------------------------------------------- +; Timer routines - Used to measure how long the sieve took to execute +;----------------------------------------------------------------------- + +; Global variables to store start and stop times +AREA1: .BLKW 2 +TICKS: .BLKW 2 +TICKS1: .BLKW 2 +TICKS2: .BLKW 2 + +STRTTMR: MOV #TICKS, R1 + .GTIM #AREA1, R1 + MOV TICKS, TICKS1 ; Store low order time in TICKS1 + MOV TICKS+2, TICKS1+2 ; Store low order time in TICKS1 + MOV TICKS+2, R0 + RTS PC + +STOPTMR: MOV #TICKS, R1 + .GTIM #AREA1, R1 + MOV TICKS, TICKS2 ; Store low order time in TICKS2 + MOV TICKS+2, TICKS2+2 ; Store low order time in TICKS2 + MOV TICKS+2, R0 + RTS PC + +ELAPSED: MOV TICKS2+2, R0 + SUB TICKS1+2, R0 + RTS PC + +; Bit array data for the sieve +BITARR: .BLKB BSIZE ; Byte array for sieve + +.END START diff --git a/PrimeMACRO11/solution_1/sieve2_result.jpg b/PrimeMACRO11/solution_1/sieve2_result.jpg new file mode 100755 index 000000000..0d0c3723c Binary files /dev/null and b/PrimeMACRO11/solution_1/sieve2_result.jpg differ diff --git a/PrimeProlog/solution_1/Dockerfile b/PrimeProlog/solution_1/Dockerfile index bade1e5b4..2216fec95 100644 --- a/PrimeProlog/solution_1/Dockerfile +++ b/PrimeProlog/solution_1/Dockerfile @@ -1,4 +1,4 @@ -FROM library/swipl:8.3.26 +FROM library/swipl:9.2.6 WORKDIR /opt/app COPY primes-*.pl bitvector.c run.sh ./