diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index d8fdae92b..fec378eda 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -284,12 +284,6 @@ internal class AssignmentAsmGen(private val program: PtProgram, } } 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!! when (value.address) { is PtNumber -> { @@ -304,10 +298,10 @@ internal class AssignmentAsmGen(private val program: PtProgram, if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, false)) { assignRegisterByte(assign.target, CpuRegister.A, false, true) } else { - assignViaExprEval(value.address) + assignByteFromAddressExpression(value.address, assign.target) } } - else -> assignViaExprEval(value.address) + else -> assignByteFromAddressExpression(value.address, assign.target) } } 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?) { when(val value = assign.source.expression!!) { is PtAddressOf -> { @@ -3746,17 +3758,8 @@ internal class AssignmentAsmGen(private val program: PtProgram, fun storeViaExprEval() { when(addressExpr) { - is PtNumber, is PtIdentifier -> { - assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD) - 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) - } + is PtNumber, is PtIdentifier -> storeByteInAToAddressExpression(addressExpr, false) + else -> storeByteInAToAddressExpression(addressExpr, true) } } diff --git a/compiler/res/prog8lib/anyall.p8 b/compiler/res/prog8lib/anyall.p8 index 39cbcb40d..bda206275 100644 --- a/compiler/res/prog8lib/anyall.p8 +++ b/compiler/res/prog8lib/anyall.p8 @@ -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 diff --git a/compiler/res/prog8lib/buffers.p8 b/compiler/res/prog8lib/buffers.p8 index fa8783573..b4a7a17f0 100644 --- a/compiler/res/prog8lib/buffers.p8 +++ b/compiler/res/prog8lib/buffers.p8 @@ -25,17 +25,19 @@ smallringbuffer { buffer[head] = value head++ fill++ + return true } sub putw(uword value) -> bool { ; -- store a word in the buffer, returns success if fill>=254 return false - fill+=2 + fill += 2 buffer[head] = lsb(value) head++ buffer[head] = msb(value) head++ + return true } sub get() -> ubyte { @@ -56,7 +58,7 @@ smallringbuffer { sys.clear_carry() return 0 } - fill-=2 + fill -= 2 tail++ cx16.r0L = buffer[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) diff --git a/compiler/res/prog8lib/cx16/diskio.p8 b/compiler/res/prog8lib/cx16/diskio.p8 index 6b37c3b7f..18e37c9aa 100644 --- a/compiler/res/prog8lib/cx16/diskio.p8 +++ b/compiler/res/prog8lib/cx16/diskio.p8 @@ -866,6 +866,7 @@ io_error: sub f_seek(uword pos_hiword, uword pos_loword) { ; -- 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] command[1] = READ_IO_CHANNEL ; f_open uses this secondary address command[2] = lsb(pos_loword) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index ea1e06108..f0aee5278 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,6 +3,8 @@ TODO See open issues on github. +Optimize code in assignByteFromAddressExpression() and storeByteInAToAddressExpression() + 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 % ?) @@ -75,9 +77,8 @@ Compiler: 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. - 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? -- implement split words arrays all() Libraries: diff --git a/examples/test.p8 b/examples/test.p8 index 449a2c83b..de01e86c9 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,42 +1,27 @@ +%import buffers %import textio %option no_sysinit %zeropage basicsafe main { sub start() { - ubyte @shared b1 = %10101010 - ubyte @shared b2 = %00001111 + uword @shared ptr = $2000 + uword @shared index = 1000 - b1 &= ~b2 - txt.print_ubbin(b1, true) - txt.nl() - b1 |= b2 - txt.print_ubbin(b1, true) - 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()) + cx16.r0L = @(ptr+index) + cx16.r1L = ptr[index] + @(ptr+index) = cx16.r0L + ptr[index] = cx16.r1L +; txt.print_ub(ptr[index]) ; txt.nl() -; -; smallringbuffer.putw(12345) -; txt.print_uw(smallringbuffer.getw()) +; ptr[index] = 123 +; txt.print_ub(ptr[index]) ; txt.nl() } } + ; ;main { ; sub start() {