added bcd.addtol()/subfroml() for in-place long calculations

fix long overflow messages, IR register type error
This commit is contained in:
Irmen de Jong
2025-10-12 21:37:52 +02:00
parent b7620abc9a
commit e962431139
9 changed files with 132 additions and 31 deletions
@@ -264,6 +264,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
if (target.dt.isByte) inplaceByteAdd(target, value)
else if (target.dt.isWord) inplaceWordAdd(target, value)
else if (target.dt.isFloat) inplaceFloatAddOrMul(target, "FADD", value)
else if (target.dt.isLong) inplaceLongAdd(target, value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
}
}
@@ -275,6 +276,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
if (target.dt.isByte) inplaceByteSub(target, value)
else if (target.dt.isWord) inplaceWordSub(target, value)
else if (target.dt.isFloat) inplaceFloatSubOrDiv(target, "FSUB", value)
else if (target.dt.isLong) inplaceLongSub(target, value)
else throw AssemblyError("weird dt ${target.position}")
}
}
@@ -282,12 +284,14 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
if(target.dt.isByte) TODO("inplaceByteMul(target, value) ${target.position}")
else if(target.dt.isWord) inplaceWordMul(target, value)
else if(target.dt.isFloat) inplaceFloatAddOrMul(target, "FMULT", value)
else if(target.dt.isLong) TODO("inplace long mul ${target.position}")
else throw AssemblyError("weird dt ${target.position}")
}
"/" -> {
if(target.dt.isByte) TODO("inplaceByteDiv(target, value) ${target.position}")
else if(target.dt.isWord) inplaceWordDiv(target, value)
else if(target.dt.isFloat) inplaceFloatSubOrDiv(target, "FDIV", value)
else if(target.dt.isLong) TODO("inplace long div ${target.position}")
else throw AssemblyError("weird dt ${target.position}")
}
"%" -> TODO("inplace ptr %")
@@ -943,6 +947,24 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
}
}
private fun inplaceLongAdd(target: PtrTarget, value: AsmAssignSource) {
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> TODO("inplace long add with ${value.number} ${target.position}")
SourceStorageKind.VARIABLE -> TODO("inplace long add with ${value.asmVarname} ${target.position}")
SourceStorageKind.EXPRESSION -> TODO("inplace long add with ${value.expression} ${target.position}")
else -> TODO("inplace long add with ${value.kind} ${target.position}")
}
}
private fun inplaceLongSub(target: PtrTarget, value: AsmAssignSource) {
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> TODO("inplace long sub with ${value.number} ${target.position}")
SourceStorageKind.VARIABLE -> TODO("inplace long sub with ${value.asmVarname} ${target.position}")
SourceStorageKind.EXPRESSION -> TODO("inplace long sub with ${value.expression} ${target.position}")
else -> TODO("inplace long sub with ${value.kind} ${target.position}")
}
}
private fun inplaceWordAdd(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
@@ -1312,11 +1312,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
} else {
val shiftTr = expressionEval.translateExpression(operand)
addToResult(result, shiftTr, shiftTr.resultReg, -1)
var shiftReg = shiftTr.resultReg
if(vmDt==IRDataType.WORD && shiftTr.dt==IRDataType.BYTE) {
shiftReg = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=shiftReg, reg2=shiftTr.resultReg), null)
}
val shiftReg = shiftTr.resultReg
val opc = if (signed) Opcode.ASRNM else Opcode.LSRNM
val ins = if(constAddress!=null)
IRInstruction(opc, vmDt, reg1 = shiftReg, address = constAddress)
@@ -1374,11 +1370,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
} else {
val shiftTr = expressionEval.translateExpression(operand)
addToResult(result, shiftTr, shiftTr.resultReg, -1)
var shiftReg = shiftTr.resultReg
if(vmDt==IRDataType.WORD && shiftTr.dt==IRDataType.BYTE) {
shiftReg = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=shiftReg, reg2=shiftTr.resultReg), null)
}
val shiftReg = shiftTr.resultReg
addInstr(result, if(constAddress!=null)
IRInstruction(Opcode.LSLNM, vmDt, reg1=shiftReg, address = constAddress)
else
+62
View File
@@ -38,6 +38,36 @@ bcd {
return a
}
sub addtol(^^long a, long b) {
; -- inplace long BCD addition (avoids copying long values by value)
setbcd()
;; NOT YET IMPLEMENTED IN PROG8: a^^ += b, so inline asm
%asm {{
lda p8v_a
ldy p8v_a+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
clc
lda (P8ZP_SCRATCH_W1),y
adc p8v_b
sta (P8ZP_SCRATCH_W1),y
iny
lda (P8ZP_SCRATCH_W1),y
adc p8v_b+1
sta (P8ZP_SCRATCH_W1),y
iny
lda (P8ZP_SCRATCH_W1),y
adc p8v_b+2
sta (P8ZP_SCRATCH_W1),y
iny
lda (P8ZP_SCRATCH_W1),y
adc p8v_b+3
sta (P8ZP_SCRATCH_W1),y
}}
clearbcd()
}
sub subb(byte a, byte b) -> byte {
setbcd()
a -= b
@@ -66,7 +96,39 @@ bcd {
return a
}
sub subfroml(^^long a, long b) {
; -- inplace long BCD subtraction (avoids copying long values by value)
setbcd()
;; NOT YET IMPLEMENTED IN PROG8: a^^ -= b, so inline asm
%asm {{
lda p8v_a
ldy p8v_a+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
sec
lda (P8ZP_SCRATCH_W1),y
sbc p8v_b
sta (P8ZP_SCRATCH_W1),y
iny
lda (P8ZP_SCRATCH_W1),y
sbc p8v_b+1
sta (P8ZP_SCRATCH_W1),y
iny
lda (P8ZP_SCRATCH_W1),y
sbc p8v_b+2
sta (P8ZP_SCRATCH_W1),y
iny
lda (P8ZP_SCRATCH_W1),y
sbc p8v_b+3
sta (P8ZP_SCRATCH_W1),y
}}
clearbcd()
}
inline asmsub setbcd() {
; be safe and 6502 compatible and prohibit interrupts during BCD mode
%asm {{
php
sei
+3
View File
@@ -0,0 +1,3 @@
bcd {
; virtual machine does not support BCD
}
@@ -2092,6 +2092,12 @@ internal class AstChecker(private val program: Program,
super.visit(memread)
}
override fun visit(numLiteral: NumericLiteral) {
super.visit(numLiteral)
if(numLiteral.type== BaseDataType.LONG) // other smaller types have already been validated
checkValueTypeAndRange(DataType.forDt(numLiteral.type), numLiteral)
}
private fun allowedMemoryAccessAddressExpression(addressExpression: Expression, program: Program): Boolean {
val dt = addressExpression.inferType(program)
if(dt.isUnsignedWord || (dt.isPointer && dt.getOrUndef().sub?.isByteOrBool==true))
@@ -2351,7 +2357,7 @@ internal class AstChecker(private val program: Program,
if(value.type==BaseDataType.FLOAT)
err("integer value expected instead of float; possible loss of precision")
val number=value.number
if (number < -2147483647 || number > 2147483647)
if (number < -2147483647.0 || number > 2147483647.0)
return err("value '$number' out of range for long")
}
targetDt.isArray -> {
@@ -2463,7 +2469,7 @@ internal class AstChecker(private val program: Program,
val sourceIsBitwiseOperatorExpression = (sourceValue as? BinaryExpression)?.operator in BitwiseOperators
if(sourceDatatype.isWord && targetDatatype.isByte)
errors.err("cannot assign word to byte, maybe use msb() or lsb()", position)
else if(sourceDatatype.isFloat&& targetDatatype.isInteger)
else if(sourceDatatype.isFloat && targetDatatype.isInteger)
errors.err("cannot assign float to ${targetDatatype}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)
else if(targetDatatype.isUnsignedWord && (sourceDatatype.isPassByRef || sourceDatatype.isPointer)) {
// this is allowed: a pass-by-reference or pointer datatype into an uword (untyped pointer value).
@@ -332,12 +332,12 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
fun makeLiteral(literalTextWithGrouping: String, radix: Int): Pair<Double, BaseDataType> {
val literalText = literalTextWithGrouping.replace("_", "")
val integer: Int
val integer: Long
var datatype = BaseDataType.UBYTE
when (radix) {
10 -> {
integer = try {
literalText.toInt()
literalText.toLong()
} catch(x: NumberFormatException) {
throw SyntaxError("invalid decimal literal ${x.message}", ctx.toPosition())
}
@@ -356,7 +356,7 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
else if(literalText.length>8)
datatype = BaseDataType.UWORD
try {
integer = literalText.toInt(2)
integer = literalText.toLong(2)
} catch(x: NumberFormatException) {
throw SyntaxError("invalid binary literal ${x.message}", ctx.toPosition())
}
@@ -367,7 +367,7 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
else if(literalText.length>2)
datatype = BaseDataType.UWORD
try {
integer = literalText.lowercase().toLong(16).toInt()
integer = literalText.lowercase().toLong(16)
} catch(x: NumberFormatException) {
throw SyntaxError("invalid hexadecimal literal ${x.message}", ctx.toPosition())
}
@@ -650,7 +650,7 @@ class NumericLiteral(val type: BaseDataType, // only numerical types allowed
BaseDataType.BYTE -> require(number in -128.0..127.0)
BaseDataType.UWORD -> require(number in 0.0..65535.0)
BaseDataType.WORD -> require(number in -32768.0..32767.0)
BaseDataType.LONG -> require(number in -2147483647.0..2147483647.0)
BaseDataType.LONG -> { /*no hard requirement, the range will be checked later to give a proper error message instead of a crash */ }
BaseDataType.BOOL -> require(number==0.0 || number==1.0)
BaseDataType.POINTER -> throw FatalAstException("pointer literals should not be created, should have been UWORD $position")
else -> require(type.isNumericOrBool) {
@@ -699,8 +699,7 @@ class NumericLiteral(val type: BaseDataType, // only numerical types allowed
in -128..127 -> NumericLiteral(BaseDataType.BYTE, dvalue, position)
in 0..65535 -> NumericLiteral(BaseDataType.UWORD, dvalue, position)
in -32768..32767 -> NumericLiteral(BaseDataType.WORD, dvalue, position)
in -2147483647..2147483647 -> NumericLiteral(BaseDataType.LONG, dvalue, position)
else -> throw FatalAstException("integer overflow: $dvalue")
else -> NumericLiteral(BaseDataType.LONG, dvalue, position)
}
}
+4 -1
View File
@@ -1,7 +1,7 @@
TODO
====
- add bcd.addto()/subto() routines for in-place addition and subtraction
- fix lv1 |= -1 being lv1 = 65535 (wrong optimization value)
- optimizedBitwiseExpr(): use R14:R15 instead to save copying/stack manipulation?
@@ -12,6 +12,8 @@ TODO
- (not needed anymore if everything is saved on the stack?:) can the compiler give a warning if you use R0/R1 (or whatever the temp storage is) in expressions and/or statements together with long integers? (because R0/R1 are likely to be clobbered as temporary storage)
- implement inplaceLongAdd/Sub in PointerAssignmentGen
STRUCTS and TYPED POINTERS
--------------------------
@@ -30,6 +32,7 @@ STRUCTS and TYPED POINTERS
Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
- make $8000000 a valid long integer (-2147483648) this is more involved than you think
- fix the line, cols in Position, sometimes they count from 0 sometimes from 1, should both always be 1-based (is this the reason some source lines end up missing in the IR file?)
- handle Alias in a general way in LiteralsToAutoVarsAndRecombineIdentifiers instead of replacing it scattered over multiple functions
- After long variable type is completed: make all constants long by default (remove type name altogether), reduce to target type implictly if the actual value fits.
+25 -11
View File
@@ -1,24 +1,38 @@
%import bcd
%import textio
%zeropage basicsafe
;;%option no_sysinit
main {
sub start() {
long @shared lv1 = $1234567
long @shared lv1 = $12345678
uword @shared w = $1234
txt.print_uwhex(msw(lv1<<1), true)
txt.print_uwhex(lsw(lv1<<1), true)
lv1 = bcd.addl(lv1, $11110000)
bcd.addtol(&lv1, $11110000)
w = bcd.adduw(w, $1111)
txt.print_ulhex(lv1, true)
txt.nl()
txt.print_uwhex(w, true)
txt.nl()
; TODO support long+1 / -1 expressions....
;lv1 |= -1 ; TODO fix value
cx16.r4 = msw(lv1<<1)
cx16.r5 = lsw(lv1<<1)
txt.print_uwhex(cx16.r4, true)
txt.print_uwhex(cx16.r5, true)
txt.nl()
txt.print_ubhex((lv1<<1) as ubyte, true)
txt.nl()
; txt.print_uwhex(msw(lv1<<1), true)
; txt.print_uwhex(lsw(lv1<<1), true)
; txt.nl()
;
; ; TODO support long+1 / -1 expressions....
;
; cx16.r4 = msw(lv1<<1)
; cx16.r5 = lsw(lv1<<1)
; txt.print_uwhex(cx16.r4, true)
; txt.print_uwhex(cx16.r5, true)
; txt.nl()
;
; txt.print_ubhex((lv1<<1) as ubyte, true)
; txt.nl()
}
}