# Positive/negative 16-bit integer formats in 8-bit systems?

While considering the recent question on Integer BASIC, a thought arises:

Most systems using/supporting integer math used a 16-bit signed format. In the case of Integer BASIC, this leads to odd-looking code like:

``````CALL -936
``````

to clear the screen, and:

``````Q = PEEK(-16336)
``````

To click the speaker.

Assuming for the moment a system that has more than one number format, it seems a possible solution would be to use three formats rather than two: one for floating point, one for 16-bit signed integers, and another for positive integers. This would allow you to represent any value from -32768 to +32767 and another for 0 though 65535, or alternately, one for -65536 to -1 and another for 0 through +65535.

The advantage to this is that you can use 16-bit addresses in their "natural" format, and you extend the possible range of values, although it would seem this has somewhat less value (as mostly these would be address/value-related items anyway). This, of course, assumes there's any value at all to negative 16-bit values, but we'll move that for now.

Assuming you don't dump the integers into the FP math pack (I'm looking at you MS) it would seem this would slightly complicate the int-path code, but only at either end of the calculation chain - parsing the number and storing the result. You would need to examine the negative flag on loads and stores, but you do that anyway, and you have to store it separately during calculations, but you likely have a free byte for this in the math pack already if you handle 40-bit FP. It also appears using separate tokens in the source would save a bit of jiggery loading the values into the eval stack when you need to set the flag/bit.

Is there a performance issue this raises that I'm not immediately seeing? Does anyone know of an 8-bit era system that did this?

• What is it that you want to do and why? May 19, 2020 at 13:07
• Why not just allow integer values to be provided in hex? May 19, 2020 at 13:17
• The question does not clearly identify the problem. To me it just seems like you dislike negative integer values when referring to memory locations. May 19, 2020 at 13:43
• Re, "...one for -65536 to -1..." In forty six years of working with computers, this is, I think, the first time I have every heard anybody propose a "negative numbers only" data type. May 19, 2020 at 14:56
• What problem is this supposed to solve? You'd effectively extend a 16-bit integer by 1 bit, making it awkward and impractical. May 19, 2020 at 17:02

In real machines with two's complement representation, there's only one integer format. The decision of whether 0xffff means -1 or 65535 is dependent on the use it's put to. Output code needs to decide which string of characters to convert 0xffff to. Input code needs to decide whether to complain about values exceeding 15 bits of magnitude.

In (mainstream) languages where there is a need for 0xffff to sometimes mean -1 and sometimes mean 65535, this is usually accomplished via types, not via representation. Integer quantities are 'signed integers' or 'unsigned integers'. The bits are just the bits; the interpretation is in the eye of the beholder (and the beholder's language translator).

BASIC is not a language with a particularly rich type system, and indeed providing unsigned types seems to me like it's going to fight against the 'B' of 'BASIC'.

From the examples posted, it seems that the deficit is with respect to features roughly glued on to BASIC without clean integration (the PEEKs and POKEs to strange addresses, if you will). To me as a nonuser of PC BASICs, it's not clear why CALL 64600 would be less "odd" than CALL -936; I'd prefer CALL CLEAR or similar.

tl;dr - insufficient reward for the required work.

Does anyone know of an 8-bit era system that did this?

Integer BASIC exists to be very small, just fast enough to develop games on, and out in time for the Apple launch. Syntax niceties would make it bigger, slower and late.

The negative integer input is a quirk of its smallness. On other 8-bit systems such as the Amstrad CPC you could do things like `CALL 48148` (better known as SCR CLEAR at &BC14) and the interpreter understood it.

Clarification: Locomotive BASIC stored 48148 as six bytes (`1F 00 00 14 3C 90`; most likely FP), -17388 as four bytes (`F5 1A EC 43`) and &BC14 as three bytes (`1C 14 BC`). The only time a negative constant would give you a smaller program than hex would be if it fitted into 8 bits: -1 was stored as `F5 0F`, but &FFFF saved as `1C FF FF`. Since most (all?) of the Amstrad CPC's ROM routines were in upper memory and BASIC code lived above &100 and we had the `&` notation borrowed from BBC BASIC, it didn't really gain us much. Integer BASIC, though, had to fight for every byte.

• "Amstrad CPC you could do things like CALL 48148" - but did it store it that way, or was this stored in a FP constant? May 20, 2020 at 13:28
• @MauryMarkowitz - added some clarification which I hope is useful. May 20, 2020 at 14:40

Does anyone know of an 8-bit era system that did this?

No, at least not the way the question proposes, which is essentially turning 16 bit values into 17 bit. Something no (BASIC) system supports. The only ones that come close are BASICs offering long integer or arbitrary size BCD formats. To be used as 16 bit value, they (and the proposed 17 bit format as well) would need to be converted into 16 bit - while maybe faster, it does not have make any difference from using FP and converting as well.

Also it must be noted that many BASICs allowed to use either format for numbers, so using

``````CALL -151
``````

or

``````CALL 65385
``````

will both make the Apple II enter the Monitor. Still, the first not only saves a byte in the source text (yeah, I know, MS; *1), but also seems way better to remember.

Is there a performance issue this raises that I'm not immediately seeing?

No, there's a missing use case - well, that and the fact that BASIC is intended as a ... well ... basic programming language. So why add more variable types for such a nice issue?

In addition it might be useful to keep in mind that BASICs with compressed storage are never about preserving the source lines as entered, i.e literal, but only in spirit. Keywords get extended, blanks inserted or removed, parenthesis added, lower case becomes upper case and so on. Using compressed form with integers means that they will be output in the default form used by the system (like signed) (*2). Still, this may not stop someone writing a cruncher able to parse signed as well as unsigned (positive) integers when entered. Wouldn't it?

No, lets forget about all of that stupid sensibility and simply implement it:

Dropping the rather pointless idea of negative integers below -32768 opens a possible way to incooperate what I see as you core intention, the display of a 16 bit integer value (like \$EA60) when listing, dependant on how it has been entered (like (+)60,000 or -5,536).

In the end it's simply a hint for the LIST command how to treat a constant. THe least intrusive way would be modifying the way Integer BASIC stores numeric constants, where the first byte of a constant is preserved to simplify the interpreter.

If we assume that the cruncher accepts numerical constant in the range 32,768..65,535 without throwing an error, it could set the first byte to a value outside that \$B0..\$B9 range, signalling that it is to be taken as unsigned (positive) integer and prepared as such. This may or may not work depending on the rest of the interpreters checks.

A less intrusive way would be 'hooking' one of the legal values for special treatment. The number zero might be a good candidate, as, by definition, negative zero does not exist. Also, it should not come as the first digit of a constant (*3). Do the cruncher will act as usual (with suppression of leading zeroes) but now don't issue an error when a number greater 32768 comes along, but goes ahead after turning the identifier into \$B0. Whenever now the LIST routine encounters a constant with \$B0 as identifier, it treats the following value as unsigned 16 bit for display.

This should result in the minimal possible intervention to the interpreter and no change for run time routines or decreased performance.

Of course, it'll still introduce the same number of problems as any alternation without adding areal 17 bit type would bring. For example an expression like

``````A = 40000 - 1000
``````

is as well legal for the cruncher as for the interpreter. Still the result would not be 39,000 as most would expect, but

``````\$9C40 - \$03E8 = \$9858 -> 26,526
``````

So while it seams nice at first sight, it will introduce a lot of hard to find bugs that no part of BASIC can detect.

Long story short, to handle this integer arithmetic has to be turned to 17 bit (*4) or introduce severe inconsistency for users on places not expected. I wouldn't enjoy either.

*1 - In addition, and that's something MS can be proud of, their full BASIC allowed to write `CALL &HFF69` as well. Not better in any way, still, the `&H` could be turned into (yet another) token to be handled like an integer, but always listed again as hex.

*2 - In fact, output could be made on a case-by-case basis, not just using hex notation when added explicit (`&H`), but whenever it seems appropriate as default - like CALLs ... all the way to make CALL even more luxurious by including any constant value right after the token without any number identifier ... did I hear anyone mention Pandora's Box?

*3 - If one constant is entered with leading zeroes, they get suppressed during crunching and not displayed when listing ... It's not a bug, it's a feature: This BASIC eliminated leading zeroes

*4 - Effectively just postponing the problem until bit 17 is hit.

• "So why add more variable types for such a nice issue?" - not variables, constants. Each instance would save 3 bytes and one runtime conversion. May 20, 2020 at 13:28
• @MauryMarkowitz Now, having nevative integers with 16 valid bits as well as positive is most definite a new variable type as it needs to hold 16 bit of value and another bit for the sign. After all, having -60000 as a constant does also mean Basic has to be able to calculate using it, which only work if adding a 17th bit - thus new variable handling is necessary. Otherwise, if -60000 is only meant as an alternate way to write 5536, (or 60000 als alternate version of -5536), but the BASIC does not allow to operate with these numbers, then why should it be preserved? May 20, 2020 at 14:42
• @MauryMarkowitz I did add a possible solution - may this is more helpful to show how fruitless the approach is. May 20, 2020 at 15:44

As far as I understand your question, I guess the ZX Spectrum BASIC could be an answer: it stores all numbers in 5-byte format, FPs as well as integers (see more here). This BASIC could store unsigned integers (0-65535) with a "sign" byte, so technically it's a little bit similar to the way you asked for.

• Oh yes, this does seem similar. It uses five bytes instead of 2.1, which is odd given the limits of that machine in RAM terms, but does appear to be a great simplification. May 20, 2020 at 13:24
• It actually uses 6 bytes with the first being a token with one value for integers and another for floats. In the integer format, only 3 or the remaining 5 bytes are used with the other two being always zero. I'm guessing they did this to make it the same size as the float structure for quicker scanning. May 20, 2020 at 15:52

BBC BASIC, one of the later and more powerful microcomputer BASICs, supported both 40-bit floating point and 32-bit signed integers.

Variables in the latter format had a `%` suffix, analogous to the `\$` identifying a string variable. This same "Hungarian notation" applies to functions which returned an integer or string variable. When ported to the ARM-based Archimedes computers, a fourth type with a `#` suffix supported 64-bit floating point.

The normal way of specifying addresses on the BBC Micro was in hexadecimal, with an `&` prefix, and indirection operators replaced the usual `PEEK` and `POKE` keywords. And of course you could use variables with each of these. Some of the single-letter `%` variables had built-in meanings to BASIC, particularly for use with the integrated assembler. You could print a variable in hexadecimal by providing a `~` prefix.

For example, you could paste the following into jsBeeb - see the text box for doing so at the top:

``````MODE 0
AUTO
DIM TEST% 256
P%=TEST%
[ : OPT 3
LDA #0
STA &84
STA &85
LDA #&1D
STA &83
LDA #&6B
STA &1D
LDA #&4E
EQUW &8347  \ Magic opcode, RMB4 &83
EOR &83
CMP #&53    \ Additional check for naive NMOS emulators
BNE P%+6
EQUW &0280  \ BRA *+4
LDA #&6E
JSR &FFEE   \ Output for BBC Micro
JSR &FFE7
RTS
]
CALL TEST%
END
``````

Then press Escape and type RUN. You'll find that it prints a different letter on the BBC Model B emulator than the BBC Master emulator, due to the different versions of the 6502 used in each.

• How were constants stored in these situations? May 20, 2020 at 13:24
• @MauryMarkowitz I believe BBC BASIC pre-parses only the keywords and line numbers. Other constants are just stored as text. May 20, 2020 at 13:55
• @MauryMarkowitz So one performance optimisation is to assign a constant to a variable. This avoids the overhead of parsing the text every time the line is executed. May 20, 2020 at 14:39
• @Chromatix THe basic point for the question is that Integer BASIC already stores constants as binary words. May 20, 2020 at 15:46