ringbuffer and pointer optimization todo

This commit is contained in:
Irmen de Jong 2024-07-16 21:28:49 +02:00
parent 78c7ee247a
commit 9046fe8d3a
6 changed files with 123 additions and 51 deletions

View File

@ -284,12 +284,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
} }
SourceStorageKind.MEMORY -> { SourceStorageKind.MEMORY -> {
fun assignViaExprEval(expression: PtExpression) {
assignExpressionToVariable(expression, "P8ZP_SCRATCH_W2", DataType.UWORD)
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2", false)
assignRegisterByte(assign.target, CpuRegister.A, false, true)
}
val value = assign.source.memory!! val value = assign.source.memory!!
when (value.address) { when (value.address) {
is PtNumber -> { is PtNumber -> {
@ -304,10 +298,10 @@ internal class AssignmentAsmGen(private val program: PtProgram,
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, false)) { if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, false)) {
assignRegisterByte(assign.target, CpuRegister.A, false, true) assignRegisterByte(assign.target, CpuRegister.A, false, true)
} else { } else {
assignViaExprEval(value.address) assignByteFromAddressExpression(value.address, assign.target)
} }
} }
else -> assignViaExprEval(value.address) else -> assignByteFromAddressExpression(value.address, assign.target)
} }
} }
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
@ -319,6 +313,24 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
} }
private fun assignByteFromAddressExpression(address: PtExpression, target: AsmAssignTarget) {
// TODO optimize this into more efficient code, using indexed register ,Y instead of explicitly calculating the full pointer value, or use self=modifying code and just use absolute addressing.
// see: https://discord.com/channels/547559626024157184/629863245934755860/1262873088782110750
assignExpressionToVariable(address, "P8ZP_SCRATCH_W2", DataType.UWORD)
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2", false)
assignRegisterByte(target, CpuRegister.A, false, true)
}
private fun storeByteInAToAddressExpression(addressExpr: PtExpression, saveA: Boolean) {
// TODO optimize this into more efficient code, using indexed register ,Y instead of explicitly calculating the full pointer value, or use self=modifying code and just use absolute addressing.
// see: https://discord.com/channels/547559626024157184/629863245934755860/1262873088782110750
if(saveA) asmgen.out(" pha")
assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD)
if(saveA) asmgen.out(" pla")
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2", false)
}
private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) { private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) {
when(val value = assign.source.expression!!) { when(val value = assign.source.expression!!) {
is PtAddressOf -> { is PtAddressOf -> {
@ -3746,17 +3758,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
fun storeViaExprEval() { fun storeViaExprEval() {
when(addressExpr) { when(addressExpr) {
is PtNumber, is PtIdentifier -> { is PtNumber, is PtIdentifier -> storeByteInAToAddressExpression(addressExpr, false)
assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD) else -> storeByteInAToAddressExpression(addressExpr, true)
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2", false)
}
else -> {
// same as above but we need to save the A register
asmgen.out(" pha")
assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD)
asmgen.out(" pla")
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2", false)
}
} }
} }

View File

@ -1,4 +1,5 @@
; any() and all() checks on arrays/memory buffers ; any() and all() checks on arrays/memory buffers.
; These were builtin functions in older versions of the language.
%option no_symbol_prefixing, ignore_unused %option no_symbol_prefixing, ignore_unused

View File

@ -25,17 +25,19 @@ smallringbuffer {
buffer[head] = value buffer[head] = value
head++ head++
fill++ fill++
return true
} }
sub putw(uword value) -> bool { sub putw(uword value) -> bool {
; -- store a word in the buffer, returns success ; -- store a word in the buffer, returns success
if fill>=254 if fill>=254
return false return false
fill+=2 fill += 2
buffer[head] = lsb(value) buffer[head] = lsb(value)
head++ head++
buffer[head] = msb(value) buffer[head] = msb(value)
head++ head++
return true
} }
sub get() -> ubyte { sub get() -> ubyte {
@ -56,7 +58,7 @@ smallringbuffer {
sys.clear_carry() sys.clear_carry()
return 0 return 0
} }
fill-=2 fill -= 2
tail++ tail++
cx16.r0L = buffer[tail] cx16.r0L = buffer[tail]
tail++ tail++
@ -67,5 +69,84 @@ smallringbuffer {
} }
; TODO ringbuffer (FIFO queue) using more than 1 page of ram (maybe even banked ram on the x16) ringbuffer {
; -- A ringbuffer (FIFO queue) that occupies a single page in memory, containing 8 KB maximum.
; You can store and retrieve words too.
uword fill
uword head
uword tail
uword buffer_ptr = memory("ringbuffer", 8192, 0)
sub init() {
; -- (re)initialize the ringbuffer, you must call this before using the other routines
head = fill = 0
tail = 8191
}
sub put(ubyte value) -> bool {
; -- store a byte in the buffer, returns success
if fill==8192
return false
buffer_ptr[head] = value
inc_head()
fill++
return true
}
sub putw(uword value) -> bool {
; -- store a word in the buffer, returns success
if fill>=8191
return false
fill += 2
buffer_ptr[head] = lsb(value)
inc_head()
buffer_ptr[head] = msb(value)
inc_head()
return true
}
sub get() -> ubyte {
; -- retrieves a byte from the buffer. Also sets Carry flag: set=success, clear=buffer was empty
if fill==0 {
sys.clear_carry()
return 0
}
fill--
inc_tail()
cx16.r0L = buffer_ptr[tail]
sys.set_carry()
return cx16.r0L
}
sub getw() -> uword {
; -- retrieves a word from the buffer. Also sets Carry flag: set=success, clear=buffer was empty
if fill<2 {
sys.clear_carry()
return 0
}
fill -= 2
inc_tail()
cx16.r0L = buffer_ptr[tail]
inc_tail()
cx16.r0H = buffer_ptr[tail]
sys.set_carry()
return cx16.r0
}
sub inc_head() {
head++
if msb(head)==$20
head=0
}
sub inc_tail() {
tail++
if msb(tail)==$20
tail=0
}
}
; TODO ringbuffer (FIFO queue) should use banked ram on the X16, but still work on virtual
; TODO stack (LIFO queue) using more than 1 page of ram (maybe even banked ram on the x16) ; TODO stack (LIFO queue) using more than 1 page of ram (maybe even banked ram on the x16)

View File

@ -866,6 +866,7 @@ io_error:
sub f_seek(uword pos_hiword, uword pos_loword) { sub f_seek(uword pos_hiword, uword pos_loword) {
; -- seek in the reading file opened with f_open, to the given 32-bits position ; -- seek in the reading file opened with f_open, to the given 32-bits position
; Note: this will not work if you have already read the last byte of the file! Then you must close and reopen the file first.
ubyte[6] command = ['p',0,0,0,0,0] ubyte[6] command = ['p',0,0,0,0,0]
command[1] = READ_IO_CHANNEL ; f_open uses this secondary address command[1] = READ_IO_CHANNEL ; f_open uses this secondary address
command[2] = lsb(pos_loword) command[2] = lsb(pos_loword)

View File

@ -3,6 +3,8 @@ TODO
See open issues on github. See open issues on github.
Optimize code in assignByteFromAddressExpression() and storeByteInAToAddressExpression()
Re-generate the skeletons doc files. Re-generate the skeletons doc files.
optimize signed byte/word division by powers of 2 (and shift right?), it's now using divmod routine. (also % ?) optimize signed byte/word division by powers of 2 (and shift right?), it's now using divmod routine. (also % ?)
@ -75,9 +77,8 @@ Compiler:
But all library code written in asm uses .proc already..... (textual search/replace when writing the actual asm?) But all library code written in asm uses .proc already..... (textual search/replace when writing the actual asm?)
Once new codegen is written that is based on the IR, this point is mostly moot anyway as that will have its own dead code removal on the IR level. Once new codegen is written that is based on the IR, this point is mostly moot anyway as that will have its own dead code removal on the IR level.
- Zig-like try-based error handling where the V flag could indicate error condition? and/or BRK to jump into monitor on failure? (has to set BRK vector for that) But the V flag is also set on certain normal instructions - Zig-like try-based error handling where the V flag could indicate error condition? and/or BRK to jump into monitor on failure? (has to set BRK vector for that) But the V flag is also set on certain normal instructions
- Zig-like defer to execute a statement/anonymousscope when subroutine exits? (problem is, we have jump insructions and inline asm , where we lose track of when exactly the subroutine exits...) - Zig-like defer to execute a statement/anonymousscope when subroutine exits? (problem is, we have jump instructions and inline asm , where we lose track of when exactly the subroutine exits...)
- generate WASM to eventually run prog8 on a browser canvas? Use binaryen toolkit and/or my binaryen kotlin library? - generate WASM to eventually run prog8 on a browser canvas? Use binaryen toolkit and/or my binaryen kotlin library?
- implement split words arrays all()
Libraries: Libraries:

View File

@ -1,42 +1,27 @@
%import buffers
%import textio %import textio
%option no_sysinit %option no_sysinit
%zeropage basicsafe %zeropage basicsafe
main { main {
sub start() { sub start() {
ubyte @shared b1 = %10101010 uword @shared ptr = $2000
ubyte @shared b2 = %00001111 uword @shared index = 1000
b1 &= ~b2 cx16.r0L = @(ptr+index)
txt.print_ubbin(b1, true) cx16.r1L = ptr[index]
txt.nl() @(ptr+index) = cx16.r0L
b1 |= b2 ptr[index] = cx16.r1L
txt.print_ubbin(b1, true) ; txt.print_ub(ptr[index])
txt.nl()
b1 = %11001100
b2 = %11110000
b1 &= ~b2
txt.print_ubbin(b1, true)
txt.nl()
b1 |= b2
txt.print_ubbin(b1, true)
txt.nl()
; smallringbuffer.init()
;
; smallringbuffer.put(123)
; txt.print_ub(smallringbuffer.get())
; txt.nl() ; txt.nl()
; ; ptr[index] = 123
; smallringbuffer.putw(12345) ; txt.print_ub(ptr[index])
; txt.print_uw(smallringbuffer.getw())
; txt.nl() ; txt.nl()
} }
} }
; ;
;main { ;main {
; sub start() { ; sub start() {