From 570b574b931fe89e043fb602175669f5489e7e19 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 28 Oct 2024 00:41:26 +0100 Subject: [PATCH] added sys.memcmp --- compiler/res/prog8lib/atari/syslib.p8 | 59 ++++++++++++++++++++++++ compiler/res/prog8lib/c128/syslib.p8 | 59 ++++++++++++++++++++++++ compiler/res/prog8lib/c64/syslib.p8 | 59 ++++++++++++++++++++++++ compiler/res/prog8lib/cx16/syslib.p8 | 50 ++++++++++++++++++++ compiler/res/prog8lib/pet32/syslib.p8 | 59 ++++++++++++++++++++++++ compiler/res/prog8lib/virtual/syslib.p8 | 12 +++++ docs/source/libraries.rst | 4 ++ docs/source/todo.rst | 3 ++ examples/test.p8 | 61 +++++++++++++++++++++---- virtualmachine/src/prog8/vm/SysCalls.kt | 38 ++++++++++----- 10 files changed, 384 insertions(+), 20 deletions(-) diff --git a/compiler/res/prog8lib/atari/syslib.p8 b/compiler/res/prog8lib/atari/syslib.p8 index a7134174b..8199f2820 100644 --- a/compiler/res/prog8lib/atari/syslib.p8 +++ b/compiler/res/prog8lib/atari/syslib.p8 @@ -177,6 +177,65 @@ _longcopy }} } + asmsub memcmp(uword address1 @R0, uword address2 @R1, uword size @AY) -> byte @A { + ; Compares two blocks of memory + ; Returns -1 (255), 0 or 1, meaning: block 1 sorts before, equal or after block 2. + %asm {{ + sta P8ZP_SCRATCH_REG ; lsb(size) + sty P8ZP_SCRATCH_B1 ; msb(size) + lda cx16.r0 + ldy cx16.r0+1 + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda cx16.r1 + ldy cx16.r1+1 + sta P8ZP_SCRATCH_W2 + sty P8ZP_SCRATCH_W2+1 + + ldx P8ZP_SCRATCH_B1 + beq _no_msb_size + +_loop_msb_size + ldy #0 +- lda (P8ZP_SCRATCH_W1),y + cmp (P8ZP_SCRATCH_W2),y + bcs + + lda #-1 + rts ++ beq + + lda #1 + rts ++ iny + bne - + inc P8ZP_SCRATCH_W1+1 + inc P8ZP_SCRATCH_W2+1 + dec P8ZP_SCRATCH_B1 ; msb(size) -= 1 + dex + bne _loop_msb_size + +_no_msb_size + lda P8ZP_SCRATCH_REG ; lsb(size) + bne + + rts + ++ ldy #0 +- lda (P8ZP_SCRATCH_W1),y + cmp (P8ZP_SCRATCH_W2),y + bcs + + lda #-1 + rts ++ beq + + lda #1 + rts ++ iny + cpy P8ZP_SCRATCH_REG ; lsb(size) + bne - + + lda #0 + rts + }} + } + inline asmsub read_flags() -> ubyte @A { %asm {{ php diff --git a/compiler/res/prog8lib/c128/syslib.p8 b/compiler/res/prog8lib/c128/syslib.p8 index 0ddf29091..f2bb34866 100644 --- a/compiler/res/prog8lib/c128/syslib.p8 +++ b/compiler/res/prog8lib/c128/syslib.p8 @@ -726,6 +726,65 @@ _longcopy }} } + asmsub memcmp(uword address1 @R0, uword address2 @R1, uword size @AY) -> byte @A { + ; Compares two blocks of memory + ; Returns -1 (255), 0 or 1, meaning: block 1 sorts before, equal or after block 2. + %asm {{ + sta P8ZP_SCRATCH_REG ; lsb(size) + sty P8ZP_SCRATCH_B1 ; msb(size) + lda cx16.r0 + ldy cx16.r0+1 + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda cx16.r1 + ldy cx16.r1+1 + sta P8ZP_SCRATCH_W2 + sty P8ZP_SCRATCH_W2+1 + + ldx P8ZP_SCRATCH_B1 + beq _no_msb_size + +_loop_msb_size + ldy #0 +- lda (P8ZP_SCRATCH_W1),y + cmp (P8ZP_SCRATCH_W2),y + bcs + + lda #-1 + rts ++ beq + + lda #1 + rts ++ iny + bne - + inc P8ZP_SCRATCH_W1+1 + inc P8ZP_SCRATCH_W2+1 + dec P8ZP_SCRATCH_B1 ; msb(size) -= 1 + dex + bne _loop_msb_size + +_no_msb_size + lda P8ZP_SCRATCH_REG ; lsb(size) + bne + + rts + ++ ldy #0 +- lda (P8ZP_SCRATCH_W1),y + cmp (P8ZP_SCRATCH_W2),y + bcs + + lda #-1 + rts ++ beq + + lda #1 + rts ++ iny + cpy P8ZP_SCRATCH_REG ; lsb(size) + bne - + + lda #0 + rts + }} + } + inline asmsub read_flags() -> ubyte @A { %asm {{ php diff --git a/compiler/res/prog8lib/c64/syslib.p8 b/compiler/res/prog8lib/c64/syslib.p8 index 4b0ffb415..38581c222 100644 --- a/compiler/res/prog8lib/c64/syslib.p8 +++ b/compiler/res/prog8lib/c64/syslib.p8 @@ -725,6 +725,65 @@ _longcopy }} } + asmsub memcmp(uword address1 @R0, uword address2 @R1, uword size @AY) -> byte @A { + ; Compares two blocks of memory + ; Returns -1 (255), 0 or 1, meaning: block 1 sorts before, equal or after block 2. + %asm {{ + sta P8ZP_SCRATCH_REG ; lsb(size) + sty P8ZP_SCRATCH_B1 ; msb(size) + lda cx16.r0 + ldy cx16.r0+1 + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda cx16.r1 + ldy cx16.r1+1 + sta P8ZP_SCRATCH_W2 + sty P8ZP_SCRATCH_W2+1 + + ldx P8ZP_SCRATCH_B1 + beq _no_msb_size + +_loop_msb_size + ldy #0 +- lda (P8ZP_SCRATCH_W1),y + cmp (P8ZP_SCRATCH_W2),y + bcs + + lda #-1 + rts ++ beq + + lda #1 + rts ++ iny + bne - + inc P8ZP_SCRATCH_W1+1 + inc P8ZP_SCRATCH_W2+1 + dec P8ZP_SCRATCH_B1 ; msb(size) -= 1 + dex + bne _loop_msb_size + +_no_msb_size + lda P8ZP_SCRATCH_REG ; lsb(size) + bne + + rts + ++ ldy #0 +- lda (P8ZP_SCRATCH_W1),y + cmp (P8ZP_SCRATCH_W2),y + bcs + + lda #-1 + rts ++ beq + + lda #1 + rts ++ iny + cpy P8ZP_SCRATCH_REG ; lsb(size) + bne - + + lda #0 + rts + }} + } + inline asmsub read_flags() -> ubyte @A { %asm {{ php diff --git a/compiler/res/prog8lib/cx16/syslib.p8 b/compiler/res/prog8lib/cx16/syslib.p8 index b47c33d7f..bd3a00059 100644 --- a/compiler/res/prog8lib/cx16/syslib.p8 +++ b/compiler/res/prog8lib/cx16/syslib.p8 @@ -1703,6 +1703,56 @@ _longcopy }} } + asmsub memcmp(uword address1 @R0, uword address2 @R1, uword size @AY) -> byte @A { + ; Compares two blocks of memory + ; Returns -1 (255), 0 or 1, meaning: block 1 sorts before, equal or after block 2. + %asm {{ + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + ldx P8ZP_SCRATCH_W1+1 + beq _no_msb_size + +_loop_msb_size + ldy #0 +- lda (cx16.r0),y + cmp (cx16.r1),y + bcs + + lda #-1 + rts ++ beq + + lda #1 + rts ++ iny + bne - + inc cx16.r0+1 + inc cx16.r1+1 + dec P8ZP_SCRATCH_W1+1 + dex + bne _loop_msb_size + +_no_msb_size + lda P8ZP_SCRATCH_W1 + bne + + rts + ++ ldy #0 +- lda (cx16.r0),y + cmp (cx16.r1),y + bcs + + lda #-1 + rts ++ beq + + lda #1 + rts ++ iny + cpy P8ZP_SCRATCH_W1 + bne - + + lda #0 + rts + }} + } + inline asmsub read_flags() -> ubyte @A { %asm {{ php diff --git a/compiler/res/prog8lib/pet32/syslib.p8 b/compiler/res/prog8lib/pet32/syslib.p8 index 5bf2201c4..75bb6152c 100644 --- a/compiler/res/prog8lib/pet32/syslib.p8 +++ b/compiler/res/prog8lib/pet32/syslib.p8 @@ -279,6 +279,65 @@ _longcopy }} } + asmsub memcmp(uword address1 @R0, uword address2 @R1, uword size @AY) -> byte @A { + ; Compares two blocks of memory + ; Returns -1 (255), 0 or 1, meaning: block 1 sorts before, equal or after block 2. + %asm {{ + sta P8ZP_SCRATCH_REG ; lsb(size) + sty P8ZP_SCRATCH_B1 ; msb(size) + lda cx16.r0 + ldy cx16.r0+1 + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + lda cx16.r1 + ldy cx16.r1+1 + sta P8ZP_SCRATCH_W2 + sty P8ZP_SCRATCH_W2+1 + + ldx P8ZP_SCRATCH_B1 + beq _no_msb_size + +_loop_msb_size + ldy #0 +- lda (P8ZP_SCRATCH_W1),y + cmp (P8ZP_SCRATCH_W2),y + bcs + + lda #-1 + rts ++ beq + + lda #1 + rts ++ iny + bne - + inc P8ZP_SCRATCH_W1+1 + inc P8ZP_SCRATCH_W2+1 + dec P8ZP_SCRATCH_B1 ; msb(size) -= 1 + dex + bne _loop_msb_size + +_no_msb_size + lda P8ZP_SCRATCH_REG ; lsb(size) + bne + + rts + ++ ldy #0 +- lda (P8ZP_SCRATCH_W1),y + cmp (P8ZP_SCRATCH_W2),y + bcs + + lda #-1 + rts ++ beq + + lda #1 + rts ++ iny + cpy P8ZP_SCRATCH_REG ; lsb(size) + bne - + + lda #0 + rts + }} + } + inline asmsub read_flags() -> ubyte @A { %asm {{ php diff --git a/compiler/res/prog8lib/virtual/syslib.p8 b/compiler/res/prog8lib/virtual/syslib.p8 index f5c403fc9..396791dc5 100644 --- a/compiler/res/prog8lib/virtual/syslib.p8 +++ b/compiler/res/prog8lib/virtual/syslib.p8 @@ -73,6 +73,18 @@ sys { }} } + sub memcmp(uword address1, uword address2, uword size) -> byte { + ; Compares two blocks of memory of up to 65535 bytes in size + ; Returns -1 (255), 0 or 1, meaning: block 1 sorts before, equal or after block 2. + %ir {{ + loadm.w r65533,sys.memcmp.address1 + loadm.w r65534,sys.memcmp.address2 + loadm.w r65535,sys.memcmp.size + syscall 47 (r65533.w, r65534.w, r65535.w) : r0.b + returnr.b r0 + }} + } + sub exit(ubyte returnvalue) { ; -- immediately exit the program with a return code in the A register %ir {{ diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index 455eafea5..75cff6445 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -296,6 +296,10 @@ sys (part of syslib) Efficiently set a part of memory to the given (u)word value. But the most efficient will always be to write a specialized fill routine in assembly yourself! +``memcmp (address1, address2, size)`` + Compares two blocks of memory of up to 65535 bytes in size. + Returns -1 (255), 0 or 1, meaning: block 1 sorts before, equal or after block 2. + ``read_flags () -> ubyte`` Returns the current value of the CPU status register. diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 552ea6369..886746770 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,6 +3,9 @@ TODO - check benchmark score vs previous version +- writing a 'txt' block in user program suddenly spews out all textio unused reference warnings because of the %option merge promotion going on. Merging should probably be handled differently + + Improve register load order in subroutine call args assignments: in certain situations, the "wrong" order of evaluation of function call arguments is done which results in overwriting registers that already got their value, which requires a lot of stack juggling (especially on plain 6502 cpu!) diff --git a/examples/test.p8 b/examples/test.p8 index 232a3a543..08c72ca80 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,18 +1,61 @@ %import textio +%import string %option no_sysinit %zeropage basicsafe main { sub start() { - alias prn = txt.print_ub - alias spc = txt.spc - alias nl = txt.nl + str name1 = "alfred" + str name2 = "aldrik" + str name3 = "aldrik" - prn(10) - spc() - prn(20) - spc() - prn(30) - nl() + uword block1 = memory("block1", 1000, 0) + uword block2 = memory("block2", 1000, 0) + uword block3 = memory("block3", 1000, 0) + + sys.memset(block1, 1000, 0) + sys.memset(block2, 1000, 0) + sys.memset(block3, 1000, 0) + void string.copy(name1, block1+900) + void string.copy(name2, block2+900) + void string.copy(name3, block3+900) + + txt.print_b(string.compare(name1, name2)) + txt.spc() + txt.print_b(string.compare(name2, name3)) + txt.spc() + txt.print_b(string.compare(name2, name1)) + txt.nl() + txt.print_b(sys.memcmp(name1, name2, len(name1))) + txt.spc() + txt.print_b(sys.memcmp(name2, name3, len(name1))) + txt.spc() + txt.print_b(sys.memcmp(name2, name1, len(name1))) + txt.nl() + txt.nl() + + name1[1] = 0 + name2[1] = 0 + name3[1] = 0 + txt.print_b(string.compare(name1, name2)) + txt.spc() + txt.print_b(string.compare(name2, name3)) + txt.spc() + txt.print_b(string.compare(name2, name1)) + txt.nl() + + txt.print_b(sys.memcmp(name1, name2, len(name1))) + txt.spc() + txt.print_b(sys.memcmp(name2, name3, len(name1))) + txt.spc() + txt.print_b(sys.memcmp(name2, name1, len(name1))) + txt.nl() + + txt.print_b(sys.memcmp(block1, block2, 1000)) + txt.spc() + txt.print_b(sys.memcmp(block2, block3, 1000)) + txt.spc() + txt.print_b(sys.memcmp(block2, block1, 1000)) + txt.nl() } } diff --git a/virtualmachine/src/prog8/vm/SysCalls.kt b/virtualmachine/src/prog8/vm/SysCalls.kt index 781eff8a8..03711d7d2 100644 --- a/virtualmachine/src/prog8/vm/SysCalls.kt +++ b/virtualmachine/src/prog8/vm/SysCalls.kt @@ -50,16 +50,14 @@ SYSCALLS: 37 = memset 38 = memsetw 39 = stringcopy -40 = ...unused... -41 = ...unused... -42 = memcopy_small -43 = load -44 = load_raw -45 = save -46 = delete -47 = rename -48 = directory -49 = getconsolesize +40 = load +41 = load_raw +42 = save +43 = delete +44 = rename +45 = directory +46 = getconsolesize +47 = memcmp */ enum class Syscall { @@ -109,7 +107,8 @@ enum class Syscall { DELETE, RENAME, DIRECTORY, - GETGONSOLESIZE + GETGONSOLESIZE, + MEMCMP ; companion object { @@ -271,6 +270,23 @@ object SysCalls { else returnValue(callspec.returns.single(), 1, vm) } + Syscall.MEMCMP -> { + val (firstV, secondV, sizeV) = getArgValues(callspec.arguments, vm) + var firstAddr = (firstV as UShort).toInt() + var secondAddr = (secondV as UShort).toInt() + var size = (sizeV as UShort).toInt() + while(size>0) { + val comparison = vm.memory.getUB(firstAddr).compareTo(vm.memory.getUB(secondAddr)) + if(comparison<0) + return returnValue(callspec.returns.single(), -1, vm) + else if(comparison>0) + return returnValue(callspec.returns.single(), 1, vm) + firstAddr++ + secondAddr++ + size-- + } + return returnValue(callspec.returns.single(), 0, vm) + } Syscall.RNDFSEED -> { val seed = getArgValues(callspec.arguments, vm).single() as Double if(seed>0) // always use negative seed, this mimics the behavior on CBM machines