• Please review our updated Terms and Rules here

Expanding TRS-80 Level II BASIC

You can, just poke twice.

You can poke 8, 16, 32 or any length of bits you want. As programmer its your responsibilty to convert number to binary then poke in memory (in order).

Most BASIC programming examples include 16 bit conversion routine, if you cannot find one lmk and I will dig out an example.
Kind of like this.

Z=65534

GOSUB 10000 :rem Convert number.
POKE address,L :rem Poke low byte.
POKE address+1,H :rem Poke high byte.
end

1000 rem Call me when you want to convert Z to L (low byte) H (high byte).
H=int(Z/256)
L=Z-(H*256)
return
 
Last edited:
You can, just poke twice.

You can poke 8, 16, 32 or any length of bits you want. As programmer its your responsibilty to convert number to binary then poke in memory (in order).

Most BASIC programming examples include 16 bit conversion routine, if you cannot find one lmk and I will dig out an example.
I'm interested - please do.
 
You can, just poke twice.

You can poke 8, 16, 32 or any length of bits you want. As programmer its your responsibilty to convert number to binary then poke in memory (in order).

Most BASIC programming examples include 16 bit conversion routine, if you cannot find one lmk and I will dig out an example.

Sure, but, that isn't very convenient. The USR statement will accept any variable and transfer it to the HL register as a 16-bit signed integer with a range of -32767 to 32769, with a call to 0xA7F, the start location of ROM routine which does the conversion and shifts the result into the HL register. Your called routine then just needs a 16-bit load instruction to transfer the value from the HL register to wherever you need it. This is how I am currently doing it and no programming effort on my part is required to convert the variable to binary.

Without writing some kind of patch for BASIC I'm not sure that there is a more efficient way of doing it.
 
This example was for 16 bit poke (2 byte conversion).

8 bit (0-255) would require no conversion, just poke.

32 bit? You would write a 4 byte conversion routine....I dont want to take away all your fun.

----------------------
Kind of like this.

Z=65534

GOSUB 10000 :rem Convert number.
POKE address,L :rem Poke low byte.
POKE address+1,H :rem Poke high byte.
end

1000 rem Call me when you want to convert Z to L (low byte) H (high byte).
H=int(Z/256)
L=Z-(H*256)
return
 
This example was for 16 bit poke (2 byte conversion).

8 bit (0-255) would require no conversion, just poke.

32 bit? You would write a 4 byte conversion routine....I dont want to take away all your fun.

----------------------
Kind of like this.

Z=65534

GOSUB 10000 :rem Convert number.
POKE address,L :rem Poke low byte.
POKE address+1,H :rem Poke high byte.
end

1000 rem Call me when you want to convert Z to L (low byte) H (high byte).
H=int(Z/256)
L=Z-(H*256)
return
oooops sorry......

I used INT......I think I mean ABS.

1000 rem Call me when you want to convert Z to L (low byte) H (high byte).

---->H=ABS(Z/256)
L=Z-(H*256)

return

This stage is called DEBUG :)
 
Sure, but, that isn't very convenient. The USR statement will accept any variable and transfer it to the HL register as a 16-bit signed integer with a range of -32767 to 32769, with a call to 0xA7F, the start location of ROM routine which does the conversion and shifts the result into the HL register. Your called routine then just needs a 16-bit load instruction to transfer the value from the HL register to wherever you need it. This is how I am currently doing it and no programming effort on my part is required to convert the variable to binary.

Without writing some kind of patch for BASIC I'm not sure that there is a more efficient way of doing it.
Sure but your question was:----> I really wish POKE could handle two-byte transfers like the Z80's 16-bit load instructions do; like if POKE accepted a data value range of -32767 to 32769 and stuck the low byte into the specified address and the high byte, if the value is outside of the range 0-255, into the next address location up.

Example code I gave you will convert a 16 bit number into 2 bytes that can be poked (low/high format).

If you do some research you will see how this can handle signed numbers also.

You may also want to think of making a larger structure for passing variables to/from BASIC & machine routines.
You as the programmer will develop your own conventions. Much way BASIC itself has a structure for knowing how one routine will pass information to another.

This may require you to dedicate a small patch of RAM to be your API DATA area (8 or so bytes). You are developing a basic API. This API then supports your programming efforts of adding functions that dont exist.
 
The easiest, and most common way for TRS-80 basic is to intercept the syntax error code. Back in the day, that's what a friend and I did, I added lines, boxes, and circle commands, as well as some other stuff. Basic Decoded was a big help in this. (helps you find things like input buffer, current statement and pointer to it, and many other useful things. Your new commands won't be tokenized, but that is a plus in my book.
 
Sure but your question was:----> I really wish POKE could handle two-byte transfers like the Z80's 16-bit load instructions do; like if POKE accepted a data value range of -32767 to 32769 and stuck the low byte into the specified address and the high byte, if the value is outside of the range 0-255, into the next address location up.

That wasn't a question; it was just me expressing my wish that POKE had data transfer capabilities similar to USR.

I know how to convert floats to individual bytes and nibbles.
 
Last edited:
I can't devote much time to this now, but in that issue of 80 US with the article on the USR patch that jrd mentioned, I stumbled upon this:

1655607744283.png
 
I can't devote much time to this now, but in that issue of 80 US with the article on the USR patch that jrd mentioned, I stumbled upon this:

The manuals for the Grafix Solution III are out there and detailed enough that building a clone of it should be a relative snap. But that aside, yeah, I’m sure it would be worth looking at their BASIC extensions for inspiration.
 
Found a page with download links to the Grafyx BASIC software here (just scroll down a bit to "OPERATING SYSTEMS"):


It was TRSDOS based. Not sure if that helps me with my cassette-only Model 1.

I know nothing ATM about disk BASIC; if it was simply a patch to the ROM resident BASIC or a complete substitution resident in RAM.
 
I know nothing ATM about disk BASIC; if it was simply a patch to the ROM resident BASIC or a complete substitution resident in RAM

Level II Basic was designed for the disk basic extension, even including some keywords that are wired to just return an “L3 Error” if their slot in the extension jump vectors in RAM isn’t populated. Earlier I mentioned Microsoft’s “Level 3 Basic” which added a bunch of commands, many graphics related, to cassette TRS-80s, by using the same method as Disk Basic. (Thus rendering it incompatible with disk based systems, although comically you could get it on disk; loading it would override DOS and leave you with the same commands as the cassette version, thereby transforming your $2000 expansion interface/disk setup into a fast loader just for L3 Basic itself. Obviously this wasn’t well received.)

Anyway, Disk Basic (or L3) only occupies about 5k of RAM so, yeah, it is an extension, not a complete replacement.
 
Okay.....

I assume that most if not all of the DOS hooks in Level II BASIC for the Model III and 4 (some of which must/might have been appropriated by the custom graphics commands) are already all there in Model I Level II BASIC?

I've found a Grafyx/GBASIC manual and a handy utility for unpacking disk image files and examining the contents. The next step is working out what the assorted files are, identifying the actual GBASIC extension *.asm and then the individual routines within.

1655635820662.png
 
Last edited:
I assume that most if not all of the DOS hooks in Level II BASIC for the Model III and 4 (some of which must/might have been appropriated by the custom graphics commands) are already all there in Model I Level II BASIC?
Per the Model III, most definitely. The Model III’s Level II BASIC is almost identical to the Model I’s, so far as I’m aware most/all code techniques that apply to one would apply to the other. (The main compatibility breakage between the machines is the floppy disk controller hardware.) You’ll definitely need to check to see if a particular low memory jump vector might have moved if you’re porting Model III code to a Model I, but it should be mostly identical.

Model 4 BASIC extensions, on the other hand, are likely to be less useful as a guide. The Model 4’s native OS was an all-ram affair and BASIC was entirely loaded from disk. It’s very possible the Model 4 Grafyx extensions directly patched BASIC in memory.
 
Instead, the driver chained into RST 10h
Yep, basically you have to patch into something and check who called you (or who called what you patched), which means every time that thing happens, it adds lots of cycles to compare the caller address. One I had patched into was the call to get a character, then looked for it being called from the pause/break check. The interesting thing I did was that I had it load BASIC/CMD on startup, then patch that a little bit too.

There was also a "Microsoft Level III BASIC" on cassette, which added in some of the things (like LINE INPUT) that were found in Disk Basic, plus a LINE command to draw a line on the screen using the chunky graphics blocks. I still have it along with the manual and box (but not the box's pop-up insert) and now I want to rip it to .wav so I can decode and disassemble it sometime. It shouldn't be too hard with my current assembler/disassembler tech to merge L3+DB together into a single clean binary, and use it in my favorite emulator.

So to sum it up again, it all depends on if you can make your custom command work with the standard Disk Basic jump vectors. Also, the disassembly in that book is not so good, and it makes no attempt to format data areas properly.
 
my custom hardware
Hmm, interesting. Well you can't patch SET/RESET, but you can patch things like GET/PUT LSET/RSET and LINE.

Of course if you're building your own Model I clone, it's probably easier to burn your own patched ROM and use the 3000-37FF area for your patches. (That's literally what the Model III did for things like the faster cassette speed.) You do need to avoid messing with the keyword table of course. If they're not in the same order then programs won't be compatible. You could do things like add a check in SET and RESET for a special character like @, then you could do something like SET@100,100 for your custom graphics. Change the jump table address for SET and RESET, and the first thing they do would be CP '@' / JP NZ,oldaddr , and the old way would still work.
 
I've got plenty of references to chew on now, but not much to add until I've digested a fair bit of it. One thing I've discovered, however, while playing around with the line printer commands in Level II BASIC, is that lowercase characters are supported by BASIC.

For example, if you type LPRINT "Hello" (holding down shift while typing "ello"), "Hello" will go to the printer even so "HELLO" gets printed to the screen, regardless if you have the lowercase hardware modification or not. Similarly, PRINT CHR$() recognises lowercase ASCII codes even so it prints them to screen in uppercase only.

Did anyone ever develop a machine code patch or modify the ROM to make Level II BASIC actually print lowercase to video memory? (and maybe invert the operation of SHIFT for letters of the alphabet). I mean, it seems to already be three quarters of the way there already. I'm sure that back in day plenty of owners who splurged out on the lowercase mod would have been itching for such a firmware upgrade.
 
Last edited:
There is no amount of firmware that could fix this. Yes, you could swap the bits in the addressing of the chargen rom, to make it display lowercase *instead* of uppercase...

But the real issue is that the video memory was missing a bit. The "lowercase mod" was a new static ram added to the video array to be able to store the bit. 0x41 is upper case A, 0x61 is lower case a. Without the ability to store that bit in video ram, there was no way to display it.

At least as far as I can remember.
 
But the real issue is that the video memory was missing a bit.

I don't mean implementing the lowercase hardware modification in firmware - I mean modifying Level II BASIC to make full use of it.
 
Back
Top