mirror of
https://github.com/cc65/cc65.git
synced 2025-08-08 06:25:17 +00:00
@@ -172,7 +172,7 @@ ExprNode* FuncHiByte (void);
|
|||||||
/* Handle the .HIBYTE builtin function */
|
/* Handle the .HIBYTE builtin function */
|
||||||
|
|
||||||
ExprNode* MakeBoundedExpr (ExprNode* Expr, unsigned Size);
|
ExprNode* MakeBoundedExpr (ExprNode* Expr, unsigned Size);
|
||||||
/* Force the given expression into a specific size of ForceRange is true */
|
/* Force the given expression into a specific size if ForceRange is true */
|
||||||
|
|
||||||
ExprNode* BoundedExpr (ExprNode* (*ExprFunc) (void), unsigned Size);
|
ExprNode* BoundedExpr (ExprNode* (*ExprFunc) (void), unsigned Size);
|
||||||
/* Parse an expression and force it within a given size if ForceRange is true */
|
/* Parse an expression and force it within a given size if ForceRange is true */
|
||||||
|
@@ -417,7 +417,7 @@ void SegDone (void)
|
|||||||
if ((F->Len == 1 && ED.AddrSize > ADDR_SIZE_ZP) ||
|
if ((F->Len == 1 && ED.AddrSize > ADDR_SIZE_ZP) ||
|
||||||
(F->Len == 2 && ED.AddrSize > ADDR_SIZE_ABS) ||
|
(F->Len == 2 && ED.AddrSize > ADDR_SIZE_ABS) ||
|
||||||
(F->Len == 3 && ED.AddrSize > ADDR_SIZE_FAR)) {
|
(F->Len == 3 && ED.AddrSize > ADDR_SIZE_FAR)) {
|
||||||
LIError (&F->LI, "Range error");
|
LIError (&F->LI, "Range error (Address size %u does not match fragment size %u)", ED.AddrSize, F->Len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -43,6 +43,7 @@
|
|||||||
|
|
||||||
/* ca65 */
|
/* ca65 */
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
#include "expr.h"
|
||||||
#include "segment.h"
|
#include "segment.h"
|
||||||
#include "studyexpr.h"
|
#include "studyexpr.h"
|
||||||
#include "symtab.h"
|
#include "symtab.h"
|
||||||
@@ -187,6 +188,70 @@ static void ED_MergeAddrSize (ExprDesc* ED, const ExprDesc* Right)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ED_MergeAddrSizeAND (ExprNode* Expr, ExprDesc* ED, const ExprDesc* Right)
|
||||||
|
/* Merge the address sizes of two expressions into ED, special case for AND operator */
|
||||||
|
{
|
||||||
|
int ConstL, ConstR;
|
||||||
|
int Size, ConstSize;
|
||||||
|
long Val, ValR;
|
||||||
|
|
||||||
|
if (ED->AddrSize == ADDR_SIZE_DEFAULT) {
|
||||||
|
/* If ED is valid, ADDR_SIZE_DEFAULT gets always overridden, otherwise
|
||||||
|
** it takes precedence over anything else.
|
||||||
|
*/
|
||||||
|
if (ED_IsValid (ED)) {
|
||||||
|
ED->AddrSize = Right->AddrSize;
|
||||||
|
}
|
||||||
|
} else if (Right->AddrSize == ADDR_SIZE_DEFAULT) {
|
||||||
|
/* If Right is valid, ADDR_SIZE_DEFAULT gets always overridden,
|
||||||
|
** otherwise it takes precedence over anything else.
|
||||||
|
*/
|
||||||
|
if (!ED_IsValid (Right)) {
|
||||||
|
ED->AddrSize = Right->AddrSize;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Neither ED nor Right has a default address size, use the smaller of
|
||||||
|
** the two.
|
||||||
|
*/
|
||||||
|
if (Right->AddrSize < ED->AddrSize) {
|
||||||
|
ED->AddrSize = Right->AddrSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Check if either side of the expression is constant */
|
||||||
|
ConstL = IsConstExpr (Expr->Left, &Val);
|
||||||
|
ConstR = IsConstExpr (Expr->Right, &ValR);
|
||||||
|
if (!ConstL && !ConstR) {
|
||||||
|
/* If neither part of the expression is constant, the above is all we can do */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* We start assuming the left side is constant, left value is in Val, right
|
||||||
|
size in Size */
|
||||||
|
Size = Right->AddrSize;
|
||||||
|
/* Now check if the right side is constant, and if so put the right value
|
||||||
|
into Val and the Left size into Size. */
|
||||||
|
if (ConstR) {
|
||||||
|
Val = ValR;
|
||||||
|
Size = ED->AddrSize;
|
||||||
|
}
|
||||||
|
/* Figure out the size of the constant value */
|
||||||
|
if (IsByteRange (Val)) {
|
||||||
|
ConstSize = ADDR_SIZE_ZP;
|
||||||
|
} else if (IsWordRange (Val)) {
|
||||||
|
ConstSize = ADDR_SIZE_ABS;
|
||||||
|
} else if (IsFarRange (Val)) {
|
||||||
|
ConstSize = ADDR_SIZE_FAR;
|
||||||
|
} else {
|
||||||
|
ConstSize = ADDR_SIZE_LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Size == ADDR_SIZE_DEFAULT) {
|
||||||
|
ED->AddrSize = ConstSize;
|
||||||
|
} else {
|
||||||
|
/* use the smaller of the two sizes */
|
||||||
|
ED->AddrSize = (ConstSize < Size) ? ConstSize : Size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static ED_SymRef* ED_FindSymRef (ExprDesc* ED, SymEntry* Sym)
|
static ED_SymRef* ED_FindSymRef (ExprDesc* ED, SymEntry* Sym)
|
||||||
/* Find a symbol reference and return it. Return NULL if the reference does
|
/* Find a symbol reference and return it. Return NULL if the reference does
|
||||||
@@ -489,7 +554,11 @@ static void StudyBinaryExpr (ExprNode* Expr, ExprDesc* D)
|
|||||||
|
|
||||||
/* Merge references and update address size */
|
/* Merge references and update address size */
|
||||||
ED_MergeRefs (D, &Right);
|
ED_MergeRefs (D, &Right);
|
||||||
ED_MergeAddrSize (D, &Right);
|
if (Expr->Op == EXPR_AND) {
|
||||||
|
ED_MergeAddrSizeAND (Expr, D, &Right);
|
||||||
|
} else {
|
||||||
|
ED_MergeAddrSize (D, &Right);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@ endif
|
|||||||
|
|
||||||
WORKDIR = ../testwrk/asm
|
WORKDIR = ../testwrk/asm
|
||||||
|
|
||||||
SUBDIRS = cpudetect opcodes listing
|
SUBDIRS = cpudetect opcodes listing val err
|
||||||
|
|
||||||
.PHONY: all continue mostlyclean clean
|
.PHONY: all continue mostlyclean clean
|
||||||
|
|
||||||
|
45
test/asm/err/Makefile
Normal file
45
test/asm/err/Makefile
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Makefile for the tests that MUST NOT compile
|
||||||
|
|
||||||
|
ifneq ($(shell echo),)
|
||||||
|
CMD_EXE = 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifdef CMD_EXE
|
||||||
|
S = $(subst /,\,/)
|
||||||
|
NOT = - # Hack
|
||||||
|
NULLDEV = nul:
|
||||||
|
MKDIR = mkdir $(subst /,\,$1)
|
||||||
|
RMDIR = -rmdir /s /q $(subst /,\,$1)
|
||||||
|
else
|
||||||
|
S = /
|
||||||
|
NOT = !
|
||||||
|
NULLDEV = /dev/null
|
||||||
|
MKDIR = mkdir -p $1
|
||||||
|
RMDIR = $(RM) -r $1
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifdef QUIET
|
||||||
|
.SILENT:
|
||||||
|
NULLERR = 2>$(NULLDEV)
|
||||||
|
endif
|
||||||
|
|
||||||
|
CA65 := $(if $(wildcard ../../../bin/ca65*),..$S..$S..$Sbin$Sca65,ca65)
|
||||||
|
|
||||||
|
WORKDIR = ../../../testwrk/asm/err
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
SOURCES := $(wildcard *.s)
|
||||||
|
TESTS = $(patsubst %.s,$(WORKDIR)/%.prg,$(SOURCES))
|
||||||
|
|
||||||
|
all: $(TESTS)
|
||||||
|
|
||||||
|
$(WORKDIR):
|
||||||
|
$(call MKDIR,$(WORKDIR))
|
||||||
|
|
||||||
|
$(WORKDIR)/%.prg: %.s | $(WORKDIR)
|
||||||
|
$(if $(QUIET),echo asm/err/$*.s)
|
||||||
|
$(NOT) $(CA65) -o $@ $< $(NULLERR)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@$(call RMDIR,$(WORKDIR))
|
3
test/asm/err/bug1538-1.s
Normal file
3
test/asm/err/bug1538-1.s
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
; this should produce a range error
|
||||||
|
.byte $1234 & $ffff
|
@@ -17,3 +17,16 @@ Overall tests:
|
|||||||
--------------
|
--------------
|
||||||
|
|
||||||
These go into listing/. Refer to listing/readme.txt
|
These go into listing/. Refer to listing/readme.txt
|
||||||
|
|
||||||
|
val:
|
||||||
|
----
|
||||||
|
|
||||||
|
Works very much like the /val directory used to test the compiler - individual
|
||||||
|
tests are run in the simulator and should exit with an exit code of 0 when they
|
||||||
|
pass, or either -1 or a number indicating what part of the test failed on error.
|
||||||
|
|
||||||
|
err:
|
||||||
|
----
|
||||||
|
|
||||||
|
Works very much like the /err directory used to test the compiler - individual
|
||||||
|
tests are assembled and MUST NOT assemble without error.
|
||||||
|
58
test/asm/val/Makefile
Normal file
58
test/asm/val/Makefile
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# Makefile for the regression tests that return an error code on failure
|
||||||
|
|
||||||
|
ifneq ($(shell echo),)
|
||||||
|
CMD_EXE = 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifdef CMD_EXE
|
||||||
|
S = $(subst /,\,/)
|
||||||
|
NULLDEV = nul:
|
||||||
|
MKDIR = mkdir $(subst /,\,$1)
|
||||||
|
RMDIR = -rmdir /s /q $(subst /,\,$1)
|
||||||
|
else
|
||||||
|
S = /
|
||||||
|
NULLDEV = /dev/null
|
||||||
|
MKDIR = mkdir -p $1
|
||||||
|
RMDIR = $(RM) -r $1
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifdef QUIET
|
||||||
|
.SILENT:
|
||||||
|
NULLOUT = >$(NULLDEV)
|
||||||
|
NULLERR = 2>$(NULLDEV)
|
||||||
|
endif
|
||||||
|
|
||||||
|
SIM65FLAGS = -x 5000000000
|
||||||
|
|
||||||
|
CA65 := $(if $(wildcard ../../../bin/ca65*),..$S..$S..$Sbin$Sca65,ca65)
|
||||||
|
LD65 := $(if $(wildcard ../../../bin/ld65*),..$S..$S..$Sbin$Sld65,ld65)
|
||||||
|
SIM65 := $(if $(wildcard ../../../bin/sim65*),..$S..$S..$Sbin$Ssim65,sim65)
|
||||||
|
|
||||||
|
WORKDIR = ../../../testwrk/asm/val
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
SOURCES := $(wildcard *.s)
|
||||||
|
TESTS = $(SOURCES:%.s=$(WORKDIR)/%.6502.prg)
|
||||||
|
TESTS += $(SOURCES:%.s=$(WORKDIR)/%.65c02.prg)
|
||||||
|
|
||||||
|
all: $(TESTS)
|
||||||
|
|
||||||
|
$(WORKDIR):
|
||||||
|
$(call MKDIR,$(WORKDIR))
|
||||||
|
|
||||||
|
define PRG_template
|
||||||
|
|
||||||
|
$(WORKDIR)/%.$1.prg: %.s | $(WORKDIR)
|
||||||
|
$(if $(QUIET),echo asm/val/$$*.$1.prg)
|
||||||
|
$(CA65) -t sim$1 -o $$(@:.prg=.o) $$< $(NULLERR)
|
||||||
|
$(LD65) -C sim6502-asmtest.cfg -o $$@ $$(@:.prg=.o) sim$1.lib $(NULLERR)
|
||||||
|
$(SIM65) $(SIM65FLAGS) $$@ $(NULLOUT)
|
||||||
|
|
||||||
|
endef # PRG_template
|
||||||
|
|
||||||
|
$(eval $(call PRG_template,6502))
|
||||||
|
$(eval $(call PRG_template,65c02))
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@$(call RMDIR,$(WORKDIR))
|
122
test/asm/val/bug1538.s
Normal file
122
test/asm/val/bug1538.s
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
|
||||||
|
.macpack longbranch
|
||||||
|
|
||||||
|
.import _exit
|
||||||
|
.export _main
|
||||||
|
|
||||||
|
offset1 = ($123456 + ((.bank(_main)) * $2345)) ; should produce $123456 + ($23 * $2345) = 1706c5
|
||||||
|
|
||||||
|
_main:
|
||||||
|
ldx #1 ; test counter
|
||||||
|
|
||||||
|
;---------------------------------------------------------------------
|
||||||
|
; first check if we can still force an 16bit address although we are
|
||||||
|
; ANDing with a 8bit value
|
||||||
|
test1:
|
||||||
|
test1a: bit a:$1234 & $ff ; should produce 00 34
|
||||||
|
lda test1a+1
|
||||||
|
cmp #$34
|
||||||
|
jne exiterror
|
||||||
|
|
||||||
|
inx
|
||||||
|
lda test1a+2
|
||||||
|
cmp #$00
|
||||||
|
jne exiterror
|
||||||
|
|
||||||
|
inx
|
||||||
|
test1b: bit a:$34 & $ff ; should produce 00 34
|
||||||
|
lda test1b+1
|
||||||
|
cmp #$34
|
||||||
|
jne exiterror
|
||||||
|
|
||||||
|
inx
|
||||||
|
lda test1b+2
|
||||||
|
cmp #$00
|
||||||
|
jne exiterror
|
||||||
|
|
||||||
|
;---------------------------------------------------------------------
|
||||||
|
; the original bug report, use an expression that contains a non constant
|
||||||
|
; part (.bank(_main)) and limit it to 8bit size by ANDing with $ff
|
||||||
|
test2:
|
||||||
|
inx
|
||||||
|
lda #(offset1 >> 0) & $ff
|
||||||
|
cmp #$c5
|
||||||
|
jne exiterror
|
||||||
|
|
||||||
|
inx
|
||||||
|
lda #(offset1 >> 8) & $ff
|
||||||
|
cmp #$06
|
||||||
|
jne exiterror
|
||||||
|
|
||||||
|
inx
|
||||||
|
lda #(offset1 >> 16) & $ff
|
||||||
|
cmp #$17
|
||||||
|
jne exiterror
|
||||||
|
|
||||||
|
;---------------------------------------------------------------------
|
||||||
|
; import a zeropage symbol (1 byte size) and then AND with a constant
|
||||||
|
; 16bit value - it should not turn into a 16bit address
|
||||||
|
test3:
|
||||||
|
jmp test3chk
|
||||||
|
.importzp tmp1
|
||||||
|
test3a: .byte tmp1 ; verify its 8bit
|
||||||
|
test3b: .byte tmp1 & $ff ; AND with $ff should work of course
|
||||||
|
test3c: .byte tmp1 & $ffff ; AND with $ffff should not change size
|
||||||
|
test3d: .byte tmp1 & $ffffff ; AND with $ffffff should not change size
|
||||||
|
test3e: .byte tmp1 & $ffffffff ; AND with $ffffffff should not change size
|
||||||
|
test3f: .word tmp1 & $ffffff ; AND with $ffffff should not change size
|
||||||
|
test3g: .word tmp1 & $ffffffff ; AND with $ffffffff should not change size
|
||||||
|
test3chk:
|
||||||
|
inx
|
||||||
|
lda test3a
|
||||||
|
cmp #tmp1
|
||||||
|
jne exiterror
|
||||||
|
|
||||||
|
inx
|
||||||
|
lda test3b
|
||||||
|
cmp #tmp1
|
||||||
|
jne exiterror
|
||||||
|
|
||||||
|
inx
|
||||||
|
lda test3c
|
||||||
|
cmp #tmp1
|
||||||
|
jne exiterror
|
||||||
|
|
||||||
|
inx
|
||||||
|
lda test3d
|
||||||
|
cmp #tmp1
|
||||||
|
jne exiterror
|
||||||
|
|
||||||
|
inx
|
||||||
|
lda test3e
|
||||||
|
cmp #tmp1
|
||||||
|
jne exiterror
|
||||||
|
|
||||||
|
inx
|
||||||
|
lda test3f
|
||||||
|
cmp #tmp1
|
||||||
|
jne exiterror
|
||||||
|
|
||||||
|
inx
|
||||||
|
lda test3f+1
|
||||||
|
cmp #$00
|
||||||
|
jne exiterror
|
||||||
|
|
||||||
|
inx
|
||||||
|
lda test3g
|
||||||
|
cmp #tmp1
|
||||||
|
jne exiterror
|
||||||
|
|
||||||
|
inx
|
||||||
|
lda test3g+1
|
||||||
|
cmp #$00
|
||||||
|
jne exiterror
|
||||||
|
|
||||||
|
;---------------------------------------------------------------------
|
||||||
|
; exit OK
|
||||||
|
ldx #0
|
||||||
|
exiterror:
|
||||||
|
txa
|
||||||
|
ldx #0
|
||||||
|
jmp _exit
|
||||||
|
|
35
test/asm/val/sim6502-asmtest.cfg
Normal file
35
test/asm/val/sim6502-asmtest.cfg
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
SYMBOLS {
|
||||||
|
__EXEHDR__: type = import;
|
||||||
|
__STACKSIZE__: type = weak, value = $0800; # 2k stack
|
||||||
|
}
|
||||||
|
MEMORY {
|
||||||
|
ZP: file = "", start = $0000, size = $0100;
|
||||||
|
HEADER: file = %O, start = $0000, size = $000C;
|
||||||
|
MAIN: file = %O, define = yes, start = $0200, size = $FDF0 - __STACKSIZE__, BANK = $23;
|
||||||
|
}
|
||||||
|
SEGMENTS {
|
||||||
|
ZEROPAGE: load = ZP, type = zp;
|
||||||
|
EXEHDR: load = HEADER, type = ro;
|
||||||
|
STARTUP: load = MAIN, type = ro;
|
||||||
|
LOWCODE: load = MAIN, type = ro, optional = yes;
|
||||||
|
ONCE: load = MAIN, type = ro, optional = yes;
|
||||||
|
CODE: load = MAIN, type = ro;
|
||||||
|
RODATA: load = MAIN, type = ro;
|
||||||
|
DATA: load = MAIN, type = rw;
|
||||||
|
BSS: load = MAIN, type = bss, define = yes;
|
||||||
|
}
|
||||||
|
FEATURES {
|
||||||
|
CONDES: type = constructor,
|
||||||
|
label = __CONSTRUCTOR_TABLE__,
|
||||||
|
count = __CONSTRUCTOR_COUNT__,
|
||||||
|
segment = ONCE;
|
||||||
|
CONDES: type = destructor,
|
||||||
|
label = __DESTRUCTOR_TABLE__,
|
||||||
|
count = __DESTRUCTOR_COUNT__,
|
||||||
|
segment = RODATA;
|
||||||
|
CONDES: type = interruptor,
|
||||||
|
label = __INTERRUPTOR_TABLE__,
|
||||||
|
count = __INTERRUPTOR_COUNT__,
|
||||||
|
segment = RODATA,
|
||||||
|
import = __CALLIRQ__;
|
||||||
|
}
|
Reference in New Issue
Block a user