added sys.memcmp

This commit is contained in:
Irmen de Jong 2024-10-28 00:41:26 +01:00
parent a82f211f9a
commit 570b574b93
10 changed files with 384 additions and 20 deletions

View File

@ -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 { inline asmsub read_flags() -> ubyte @A {
%asm {{ %asm {{
php php

View File

@ -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 { inline asmsub read_flags() -> ubyte @A {
%asm {{ %asm {{
php php

View File

@ -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 { inline asmsub read_flags() -> ubyte @A {
%asm {{ %asm {{
php php

View File

@ -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 { inline asmsub read_flags() -> ubyte @A {
%asm {{ %asm {{
php php

View File

@ -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 { inline asmsub read_flags() -> ubyte @A {
%asm {{ %asm {{
php php

View File

@ -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) { sub exit(ubyte returnvalue) {
; -- immediately exit the program with a return code in the A register ; -- immediately exit the program with a return code in the A register
%ir {{ %ir {{

View File

@ -296,6 +296,10 @@ sys (part of syslib)
Efficiently set a part of memory to the given (u)word value. 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! 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`` ``read_flags () -> ubyte``
Returns the current value of the CPU status register. Returns the current value of the CPU status register.

View File

@ -3,6 +3,9 @@ TODO
- check benchmark score vs previous version - 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: 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 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!) in overwriting registers that already got their value, which requires a lot of stack juggling (especially on plain 6502 cpu!)

View File

@ -1,18 +1,61 @@
%import textio %import textio
%import string
%option no_sysinit %option no_sysinit
%zeropage basicsafe %zeropage basicsafe
main { main {
sub start() { sub start() {
alias prn = txt.print_ub str name1 = "alfred"
alias spc = txt.spc str name2 = "aldrik"
alias nl = txt.nl str name3 = "aldrik"
prn(10) uword block1 = memory("block1", 1000, 0)
spc() uword block2 = memory("block2", 1000, 0)
prn(20) uword block3 = memory("block3", 1000, 0)
spc()
prn(30) sys.memset(block1, 1000, 0)
nl() 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()
} }
} }

View File

@ -50,16 +50,14 @@ SYSCALLS:
37 = memset 37 = memset
38 = memsetw 38 = memsetw
39 = stringcopy 39 = stringcopy
40 = ...unused... 40 = load
41 = ...unused... 41 = load_raw
42 = memcopy_small 42 = save
43 = load 43 = delete
44 = load_raw 44 = rename
45 = save 45 = directory
46 = delete 46 = getconsolesize
47 = rename 47 = memcmp
48 = directory
49 = getconsolesize
*/ */
enum class Syscall { enum class Syscall {
@ -109,7 +107,8 @@ enum class Syscall {
DELETE, DELETE,
RENAME, RENAME,
DIRECTORY, DIRECTORY,
GETGONSOLESIZE GETGONSOLESIZE,
MEMCMP
; ;
companion object { companion object {
@ -271,6 +270,23 @@ object SysCalls {
else else
returnValue(callspec.returns.single(), 1, vm) 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 -> { Syscall.RNDFSEED -> {
val seed = getArgValues(callspec.arguments, vm).single() as Double val seed = getArgValues(callspec.arguments, vm).single() as Double
if(seed>0) // always use negative seed, this mimics the behavior on CBM machines if(seed>0) // always use negative seed, this mimics the behavior on CBM machines