So, I've been writing an ez80 emulator called bz80 entirely in TI-BASIC. A bit of eye candy first: here it is computing the Fibonacci sequence:


I'm emulating a regular ez80 here, not a calculator. As a result, the entire address space is treated like RAM, and I don't attempt to emulate any form of IO. So, you won't be playing any games on it any time soon.

Why?
When TI decided to remove native code from the CE series, I decided that writing an emulator in TI-BASIC would be the pettiest possible response.

Limitations
Currently, the two biggest limitations are memory and speed. Memory is limited to 999 bytes, as it's stored in L₁. I could expand this a few times by storing multiple bytes per list element or using multiple lists, at the cost of speed.
I originally intended to store memory in Str1, but then I found out that sub( doesn't work past 9999 elements, and also that due to multi-byte tokens, accessing a token is O(n).
I then decided to store the entire address space in lists named BZAAA through BZZZZ, until I realized that, while you can get a list element without knowing its name using eval(), you cannot store to a list without hardcoding its name.
So, for now, I'm just using one element of L₁ per byte of memory.

The other big limitation here is speed. As you can see from the screenshot, this is probably running at 1-2 Hz when printing the processor state to the screen constantly. It runs a bit faster than that when not displaying anything (about 4.3 Hz for all no-ops, and 2.1 ld hl,1337's per second for the equivalent of 8.4 Hz). In any case, this is never going to do anything at a useful speed.

Here's the GitHub repo, if you want to check it out for yourself: https://github.com/commandblockguy/bz80

Hold up, are those C preprocessor macros?
Yes.
After carefully evaluating the design principles behind TI-BASIC, the ez80, and the CE as a whole, I decided that my build process must include a similar amount of jankiness. I further concluded that the best way to do so would be to use the C preprocessor, since it was obviously not intended for anything like this. I ran into an issue where the preprocessor would insert extra spaces into macros, which I fixed by running the tokenized program through a binary regex to remove spaces. This broke the expr( command, which I fixed with more regexes. Since I was using regexes anyway, I added a few more to automatically remove unnecessary ")]}, which was slightly faster than writing a plugin for my text editor to not freak out when it encounters unclosed parentheses.

I also added a subroutine framework using the preprocessor. It allows you to type "test_routine(0)", and it will add 0 to an argument list, store the ID of test_routine, then recursively call the program. If the ID is set, it will call test_routine instead of the main program. It's pretty bad, and the definition of a subroutine involves a ton of boilerplate. There's really not a better way to do it in the C preprocessor, though.
The subroutines are probably relatively slow - if anyone has any suggestions on how to improve them, that would be great.

Building
You'll need the tivars_lib_cpp CLI, make, and python3.
Clone the repo from the link above, or download a zip, then run make.
I may put a .8xp release on the repo at some point.

Using
Store your program in L₁, with one element per byte. The program will be run from address 0, so existing 8xp programs can't be run.
For small programs, you can probably just ask RunerBot to assemble them for you - just do "@ez80 (assembly code)" to assemble into hex, then convert each hex byte into decimal - this site handles that well.
Alternatively, on Linux you can "od -An -vtu1 -w1 (binary file)", then manually type the result into the list.
I'm working on a script to automatically convert from binary to 8xl.

To-do
z80 mode - everything runs in ez80 mode right now.
CB and ED-prefixed opcodes
Interrupts (not that there are any interrupt sources)
IO (not that there's any hardware to interface with)
Shadow registers
DAA
The H flag
Probably a bunch of other stuff I'm forgetting

Already implemented
(though not necessarily well-tested)

Code:
NOP
DJNZ
JR d
JR cc',d
LD r,n
ADD HL, rr
LD (BC),A
LD (DE),A
LD (Mmn), HL
LD (Mmn), A
LD A, (BC)
LD A, (DE)
LD HL, (Mmn)
LD A, (Mmn)
INC rr
DEC rr
INC r
DEC r
LD r,n
RLCA
RRCA
RLA
RRA
CPL
SCF
CCF
HALT
LD r,r'
ADD A,r
ADC A,r
SUB A,r
SBC A,r
AND A,r
XOR A,r
OR A,r
CP A,r
RET cc
POP rr
RET
JP HL
LD SP,HL
JP Mmn
EX (SP),HL
EX DE,HL
CALL cc, Mmn
PUSH rr
CALL Mmn
ADD A,n
ADC A,n
SUB A,n
SBC A,n
AND A,n
XOR A,n
OR A,n
CP A,n
RST n
IX and IY prefixes


Here's the repo link again, since I buried it in the middle of the post: https://github.com/commandblockguy/bz80
I'm loving the trend of building technically impressive but useless things in BASIC as of late. Keep it up!
I have a nice and small logic routine in basic if you'd like to see it...

Bitlogic Code:

Code:
;LB is two or one numbers
;If 1=dim(LB:2->dim(LB
;Bits is S
;Logic is theta
" ->Str0
ClrHome:For(A,1,S
.5int({LB(1),LB(2->LB
not(not(fPart(Ans
eval(expr(sub("Ans(2)"+sub(" and  or  xor not(",theta,1)+"Ans(1",1+4(theta=4),8-4(theta=4
Ans+Str0->Str0
End:Disp Str0


Another handy and small routine for converting hex to dec to perform ops in BASIC
Base to Decimal:

Code:
;base->b
;Ans is the string to convert
sum(seq(B^(length(Ans)-A)inString("123456789ABCDEF",sub(Ans,A,1)),A,1,length(Ans

Credit to @LogicalJoe for some small HUGE optimizations and for making me laugh Smile
108 bytes:
NonstickAtom785 wrote:
Bitlogic Code:
Code:
;LB is two or one numbers
;If 1=dim(LB:2->dim(LB
;Bits is S
;Logic is theta
" ->Str0
ClrHome:For(A,1,S
.5int({LB(1),LB(2->LB
not(not(fPart(Ans
eval(expr(sub("Ans(2)"+sub(" and  or  xor not(",theta,1)+"Ans(1",1+4(theta=4),8-4(theta=4
Ans+Str0->Str0
End:Disp Str0

54 bytes:

Code:
// theta=1 -> Ans =   |LZ(1) & |LZ(2)
// theta=2 -> Ans =   |LZ(1) | |LZ(2)
// theta=3 -> Ans =   |LZ(1) ^ |LZ(2)
// theta=4 -> Ans = ~(|LZ(1) | |LZ(2))
2^cumSum(not(binompdf(S-1,0
sum(Ansexpr(sub("1<0!=1=0=",2theta-1,2)+"abs(int(2fPart(.5/Anssum(|LZ{1,[i]


Credit to @Weregoose for the awesome bitwise operations (https://ceme.tech/p154901)
KermMartian wrote:
Bitwise XOR/AND/OR of Two Integers

The following code was created by Weregoose (see this topic) for XORing two integers together. A and B are the arguments, and the output is returns in Ans.
Weregoose wrote:
If you want to retool this into another bitwise operation, go to the "=" and substitute it with "<" for AND, or "≤" for OR.

Code:
:int(log(2)⁻╣log(max({A,B
:2^cumSum(binomcdf(Ans,0
:sum(Ans.5(1=abs(int(2fPart(Ans⁻╣(A+Bi


It seems that this one by Kerm could also do the trick for bitwise functions.
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are GMT - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement