From d79dd55a18cb158778fbdc796a60f27518d1f971 Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Tue, 18 Jun 2024 06:53:54 -0400 Subject: cxrefbas and renumbas: round fractional line numbers like BASIC does. --- bcdfp.c | 32 +++++++++++++++++++++++++------- cxrefbas.rst | 5 +++++ linetab.c | 12 ++++++++++++ renumbas.rst | 5 +++++ 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/bcdfp.c b/bcdfp.c index 76157c1..a80e55d 100644 --- a/bcdfp.c +++ b/bcdfp.c @@ -1,8 +1,18 @@ #include +#include #include "bcdfp.h" +/* very dumb and limited BCD floating point conversions. + they're written this way because they're only required to + support line numbers, and I don't want to have to link with + the math library (-lm). */ + extern void die(const char *msg); +void die_range(void) { + die("Line number out of range (>65535)"); +} + unsigned char bcd2int(unsigned char bcd) { return (bcd >> 4) * 10 + (bcd & 0x0f); } @@ -12,24 +22,32 @@ unsigned char int2bcd(unsigned char i) { } unsigned short fp2int(const unsigned char *fp) { - unsigned short result = 0; + unsigned int result = 0; /* examine the exponent/sign byte */ if(fp[0] == 0) return 0; /* special case */ - if(fp[0] & 0x80) die("negative numbers not supported"); + if(fp[0] & 0x80) die("Negative line numbers not supported"); switch(fp[0]) { case 0x40: - result = bcd2int(fp[1]); break; + result = bcd2int(fp[1]); + if(fp[2] >= 0x50) result++; + break; case 0x41: - result = bcd2int(fp[1]) * 100 + bcd2int(fp[2]); break; + result = bcd2int(fp[1]) * 100 + bcd2int(fp[2]); + if(fp[3] >= 0x50) result++; + break; case 0x42: - result = bcd2int(fp[1]) * 10000 + bcd2int(fp[2]) * 100 + bcd2int(fp[3]); break; + result = bcd2int(fp[1]) * 10000 + bcd2int(fp[2]) * 100 + bcd2int(fp[3]); + if(fp[4] >= 0x50) result++; + break; default: - die("number out of range"); break; + die_range(); break; } - return result; + if(result > 0xffff) die_range(); + + return (unsigned short)result; } void int2fp(unsigned short num, unsigned char *fp) { diff --git a/cxrefbas.rst b/cxrefbas.rst index d80d818..7004217 100644 --- a/cxrefbas.rst +++ b/cxrefbas.rst @@ -58,6 +58,11 @@ warning on standard error, e.g. *GOTO A* or *GOSUB 100\*A*. Even *GOTO Line numbers above 32767, e.g. *TRAP 40000*, are not listed. +Atari BASIC allows fractional line numbers, such as *GOTO 123.4*. +These are rounded to the nearest integer when the program is +executed. **cxrefbas** handles these correctly, although you're +not likely to run into them in real-world programs. + OPTIONS ======= diff --git a/linetab.c b/linetab.c index 0351f59..59a96e3 100644 --- a/linetab.c +++ b/linetab.c @@ -93,6 +93,8 @@ void computed_msg(unsigned short lineno) { CALLBACK(got_var) { switch(last_cmd) { + /* any use of a variable in the arguments to these means + we can't renumber that argument. */ case CMD_GOTO: case CMD_GO_TO: case CMD_GOSUB: @@ -102,6 +104,7 @@ CALLBACK(got_var) { computed_msg(lineno); break; case CMD_ON: + /* vars are OK in ON, before the GOTO or GOSUB */ if(on_op) computed_msg(lineno); break; default: @@ -119,9 +122,14 @@ CALLBACK(got_exp) { } if(tok != OP_NUMCONST) return; + + /* beware: standalone only means nothing *follows* the constant + in the same expression. still have to check last_tok to see + what came before. */ standalone = is_standalone_num(pos); switch(last_cmd) { + /* these take a single argument */ case CMD_GOTO: case CMD_GO_TO: case CMD_GOSUB: @@ -134,11 +142,14 @@ CALLBACK(got_exp) { } break; case CMD_IF: + /* this only applies to bare line numbers, like IF A THEN 1000, + not IF A THEN GOTO 1000 (or anything else after THEN). */ if(last_tok == OP_THEN) { add_lineref(lineno, pos + 1); } break; case CMD_ON: { + /* takes arbitrary number of arguments */ switch(last_tok) { case OP_GOTO: case OP_GOSUB: @@ -154,6 +165,7 @@ CALLBACK(got_exp) { } break; case CMD_LIST: { + /* takes one or two arguments */ switch(last_tok) { case CMD_LIST: case OP_COMMA: diff --git a/renumbas.rst b/renumbas.rst index d4b0990..7cef711 100644 --- a/renumbas.rst +++ b/renumbas.rst @@ -44,6 +44,11 @@ Remember that the maximum line number for Atari BASIC is 32767. Renumbering will fail, if the chosen start and increment values would result in lines with numbers higher than this. +Atari BASIC allows fractional line numbers, such as *GOTO 123.4*. +These are rounded to the nearest integer when the program is +executed. **renumbas** handles these correctly, although you're +not likely to run into them in real-world programs. + OPTIONS ======= -- cgit v1.2.3