[Project Log] Python on the 6502/C64, 8080, 6800, 6809 and AVR

The following is a full assembler/99/4A dev tool set which has a lot of enhancements over the standard 99/4A assembler. The good thing about this that it is open source and written in…python.

If you wanted you could change the expression parser so it enforces the standard operator priority instead of the current left-to-right evaluation priority, thus eliminating the requirements for explicit parentheses. Of course, then the your 9900 assembly code would only be valid with your modified assembler.

https://endlos99.github.io/xdt99/

1 Like

Most development can be done with an emulator (see above post for emulator link) and cross-assembler. For doing testing on actual hardware the silver/black console is desired because it will recognize software cartridges with ROM only (no TI GROMs needed as with the beige console).

BTW, Atariage is the forum for discussion and questions related to all modern 99/4A development (hardware and software) - which is still taking place. There will be someone from that forum who can answer almost any technical detail related to the 99/4A or the 9900. You would like find several users on that forum who would jump at the chance to do some testing of any 9900 port. If you want to move forward with this, PM me and I can recommend some of the power users.

I will be receiving some additional 99/4A hardware this week, which I believe includes some full systems - console + PEB (Peripheral Expansion Box) w/memory expansion and floppy drives. There are also some newer peripherals which integrate the 32K memory expansion, floppy emulated on compact flash, as well as serial port. With this much smaller peripheral you don’t need the PEB, which is good as the PEB is quite large.

There is also a board available that allows a USB (including wireless) keyboard to be used instead of the built-in keyboard. With this, you can then shove the console+peripheral board to the side and use a KVM switch.

https://www.arcadeshopper.com/wp/?page_id=11#!/Hardware/c/12497083/offset=0&sort=nameAsc

The 99/4A outputs composite video at 256x192 resolution, so you’ll need some kind of line doubler box to use it with a VGA monitor. Another option is to install a 3rd party replacement, the F18A. The original version outputs analog VGA, but there is a new version coming that outputs HDMI.

http://codehackcreate.com/archives/category/ti994a

If you can support page RAM, there are several boards with (corresponding psuedo-standards) that support more than 32K of RAM. Two of the most popular are the Horizon ramdisk and SupeRAMs boards. Most of the emulators support these boards, most development can be done without the actual hardware.

One of the best sources of technical information on the TI 99/4A has been compiled by Thierry Nouspikel, and can be found on his TI-99-4A Tech Pages. There is detailed here info on all of the 99/4A chips and peripherals, including several that Thierry designed himself. He is truly a 99/4A savant, who started is 99/4A activities while he was a pre-med student. He is currently doing DNA-related research. You will be amazed at the depth and breadth of his 99/4A information.

http://www.unige.ch/medecine/nouspikel/ti99/titech.htm

If you are interested in doing a 99/4A port of your python project, I will donate the necessary 99/4A hardware needed for testing after you have everything working on the emulator. This would include: console, peripherals, keyboard adapter, line doubler/F18A. With this you could then use a KVM if that is more convenient.

3 Likes

I am seeking a new place for discussion about the Python compiler project.

The options are:

  • Resurrect a Wordpress blog I have not used for 10 years. Advantages are you do not have to register to post comments and I have full control over the formatting. Disadvantage is people will not easily find it.
  • Create a thread on one of the several forums catering to vintage computer enthusiasts. Advantage is more exposure to people who may be interested in it. Disadvantage is you have to register an account to comment.
  • Something else I am not seeing?

Comments?

1 Like

This is one of the things “going on at DMS” that I like to vicariously imagine myself to be a part of. I have no idea what you’re doing, but every once in a while, I page through your log and actually understand something. This is why I really like the Discourse arrangement; this is a project I get to feel connected to, even though it’s well outside my ability. I regret mightily that you are seeking another host for your log, but please post here whenever you choose a venue, as I would love to get to continue my voyeurism.

2 Likes

Would hate to see it move. I’m not a programmer, but I’ve enjoyed following the progression.

If you’re looking for a home for it, try here: http://www.vcfed.org/forum/forum.php

RJ

1 Like

How about VCC’s hackaday or patreon?

Does hackaday offer potentially higher exposure than say, vcfed?

Implementation and testing is done on the heap manager for the 8080. Though the processor can use its register pairs as pointers to access memory, it has no indexing capability, meaning it cannot temporarily add an offset to the address in the register. That limitation made the heap code quite cumbersome.

Hence, I am adding the Z80 as another target since it has two index registers the 8080 lacks. Modifying an 8080 disassembler to generate Z80 syntax was not very difficult; making an 8080 assembler parse Z80 syntax is proving to be a challenge.

Work also continues on the heap manager for the 6800. I was initially drawn to Motorola microprocessors due to the fact that a usable computer from SWTPC can be had somewhat cheaper than an equivalent one based on an 8080 or Z80.

The “68” and “80” communities both thought their processor was superior. Writing very similar programs for both at the same time is a very enlightening way to appreciate both the strengths and weaknesses of each one. So far, the one index register of the 6800 is a joy to use compared with the three nonindexable ones of the 8080.

Also on deck is the AVR. As I have said before, this one is slow going because all of my experience with this platform in the last three years has been in C on an Arduino. I am having to relearn everything.

This effort so far has really validated the value of working on multiple platforms at the same time instead of serially. While heap management algorithms are still fresh, I will be implementing the same for the additional platforms of x86, 68K, 9900 and ARM on the Raspberry Pi. Still on the bubble are the 1802 and MSP430.

2 Likes

How else to spend a rainy day than study the source code of my FORTH interpreter and BASIC compiler for the AVR microcontroller? :slight_smile:

Most of you will not know this since Allen is the only other person I am aware of who will admit to programming an AVR in assembly language.

The AVR has what is known as a Harvard architecture. That is, it has separate code and data memory address spaces. It is a natural since the code is stored in flash memory while the data is in conventional RAM.

The memory maps of an AVR look like this:

0 : data address space
*------------------------------*
| Registers and I/O | free RAM |
*------------------------------*
                    |<-- 2K -->|

0 : code address space
*-------------------------------------------*
| Interrupt vectors | program code | unused |
*-------------------------------------------*
|<------------------ 32K ------------------>|

The problem is that the AVR 328p as used in an Arduino has only 2 KBytes of RAM.

FORTH, BASIC and Python all have a large amount of data which is not changed as the program runs.

The obvious solution is to put it in among the 32 KBytes of flash memory. The problem is that there are two different flavors of pointers, data and code.

The GCC tool set used in the Arduino IDE addresses :slight_smile: the problem with a somewhat hairy set of compiler directives. The gory details if you are bored and care to read about it:

http://www.nongnu.org/avr-libc/user-manual/pgmspace.html

FORTH, BASIC and Python have no provision for dealing with this problem.

Since I have complete control over memory layout, I have come up with a way to give the illusion of a common pointer. My modified memory map looks like this:

0 : data address space
*------------------------------*
| Registers and I/O | free RAM |
*------------------------------*
                    |<-- 2K -->|

0 : code address space
*----------------------------------------------------------------------------*
| Interrupt vectors | program code | optional padding | static data | unused |
*----------------------------------------------------------------------------*
|<----------------------------------- 32K ---------------------------------->|

As long as the static data is at a higher address than the end of the RAM, no additional padding is necessary; this is the case except for very simple programs.

Code to access data must distinguish between RAM and flash memory by comparing an address with the end of RAM, somewhat like this:

 000090 2F6E	      [1] 00107	PStr:	mov	R22,R30
 000091 2B6F	      [1] 00108		or	R22,R31
 000092 F099=0000A6 [1/2] 00109		breq	PStr2
 000093 E161	      [1] 00110		ldi	R22,high(SRAM_START+SRAM_SIZE)
 000094 30E0	      [1] 00111		cpi	R30,low(SRAM_START+SRAM_SIZE)
 000095 07F6	      [1] 00112		cpc	R31,R22
 000096 F480=0000A7 [1/2] 00113		brcc	PStr3
 000097 91C1	      [2] 00114		ld	R28,Z+
 000098 91D1	      [2] 00115		ld	R29,Z+
 000099 9181	      [2] 00116		ld	R24,Z+
 00009A 8190	      [1] 00117		ld	R25,Z
 00009B 2F68	      [1] 00118		mov	R22,R24
 00009C 2B69	      [1] 00119		or	R22,R25
 00009D F041=0000A6 [1/2] 00120		breq	PStr2
 00009E E161	      [1] 00121		ldi	R22,high(SRAM_START+SRAM_SIZE)
 00009F 30C0	      [1] 00122		cpi	R28,low(SRAM_START+SRAM_SIZE)
 0000A0 07D6	      [1] 00123		cpc	R29,R22
 0000A1 F460=0000AE [1/2] 00124		brcc	PStr4
 0000A2 9169	      [2] 00125	PStr1:	ld	R22,Y+
 0000A3 DFE5=000089   [3] 00126		rcall	Echo
 0000A4 9701	      [2] 00127		sbiw	R24,1
 0000A5 F7E1=0000A2 [1/2] 00128		brne	PStr1
 0000A6 9508	      [2] 00129	PStr2:	ret
 0000A7 91C5	      [3] 00130	PStr3:	lpm	R28,Z+
 0000A8 91D5	      [3] 00131		lpm	R29,Z+
 0000A9 9185	      [3] 00132		lpm	R24,Z+
 0000AA 9194	      [3] 00133		lpm	R25,Z
 0000AB 2F68	      [1] 00134		mov	R22,R24
 0000AC 2B69	      [1] 00135		or	R22,R25
 0000AD F3C1=0000A6 [1/2] 00136		breq	PStr2
 0000AE 01FE	      [1] 00137	PStr4:	movw	R30,R28
 0000AF 9165	      [3] 00138	PStr5:	lpm	R22,Z+
 0000B0 DFD8=000089   [3] 00139		rcall	Echo
 0000B1 9701	      [2] 00140		sbiw	R24,1
 0000B2 F7E1=0000AF [1/2] 00141		brne	PStr5
 0000B3 9508	      [2] 00142		ret

ld loads from data space whereas lpm loads from program space.

2 Likes

Heap managers are done for the 6800 and 6809.

Still working on the one for the AVR.

The Z80 assembler is finally working. It still needs work to detect and flag invalid input. The Z80 simulator needs work to disassemble the “new” Z80 instructions.

A serious issue has come up with the AVR which makes it impossible to use the official assembler and requires some ugly hacks in mine.

A string is currently defined to look like this in memory:

+---------------+---------------+---------------+----------------------------
|		|		|		|
|      $81	|        length of string	|        characters of the string
|		|		|		|
+---------------+---------------+---------------+----------------------------

The problem is when I want to put constant strings into flash memory. The official AVR assembler insists on putting things on an even address. That makes sense for machine instructions, but is quite limiting for compact data structures.

1 Like

There turns out to be a solution to the problem - put everything on a single line.

Instead of trying to define the object type and the string contents separately:

 			  00017	.macro	msg
 			  00018
 			  00019	.db	strlen(@0)&$FF	; the length
 			  00020	.db	strlen(@0)>>8
 			  00021	.db	@0
 			  00022
 			  00023	.endm

 :  
 :  
 :
 
 000486			  00295	S_True:
 000486 8100		  00296		.db	TYPE_STRING
 			  00297		msg	"True"
+
+000487 0400			.db	strlen("True")&$FF	; the length
+000488 0000			.db	strlen("True")>>8
+000489 54727565		.db	"True"

Create a macro to lay out a string object:

 			  00011	.macro	string
 			  00012
 			  00013	.db	TYPE_STRING,strlen(@0)&$FF,strlen(@0)>>8,@0
 			  00014
 			  00015	.endm

 :  
 :  
 :

 000482			  00292	S_None:
 			  00293		string	"None"
+
+000482 8104004E6F6E		.db	TYPE_STRING,strlen("None")&$FF,strlen("None")>>8,"None"
+000485 6500
1 Like

Currently giving the 6502 version some love in preparation for the next public demo release for the retrocomputing meeting on November 10.

Rewrote the parsing of if/else to handle if/elif/else.

Quite a bit has been added since the last release in July. The current feature list is:

  • Dynamic typing
  • Types: integer, string, True, False, function (partial)
  • Automatic memory management (using reference counting)
  • Variable precision integers
  • Integer operators +, -, *, //, %, &, ^, |
  • Variable length strings
  • String operators +, *
  • Relational operators ==, !=, <, <=, >, >=
  • if else elif
  • while else break continue
  • print function, including sep and end keyword arguments
  • input function
  • hardware access functions: peek, poke
  • randint function
  • int function
  • hex, oct and bin functions

Now working on right shift. Due to variable precision integers, this is not trivial.

2 Likes

A couple of other things worth a mention:

I have been reading this book:

https://www.brucesmith.info/raspberry-pi-assembly-language-raspbian/

I have remembered previous discussions about WebAssembly and think it deserves another look:

2 Likes

I regret that I have to raise the white flag for making another release by the retrocomputing meeting this weekend.

The work on right shift is taking longer than expected. There is no way I can get left shift working by Saturday. There is even not enough time for me to do the regression testing for another release.

Not much on this project has been easy.

Consider the following code:

""" locals.py
examples of local variables"""

A = 'This is the text message from main'

def test1():
    print('In test1.')
    print(A)
    print('Leaving test1.')

def test2():
    print('In test2.')
    A = "This is test2's text message."
    print(A)
    print('Leaving test2.')

def test3():
    print('In test3.')
    print(A)
    A = "This is test3's text message."
    print('Leaving test3.')

test1()
print('Back in main')
print(A)
test2()
print('Back in main')
print(A)
test3()

Running it yields:

In test1.
This is the text message from main
Leaving test1.
Back in main
This is the text message from main
In test2.
This is test2's text message.
Leaving test2.
Back in main
This is the text message from main
In test3.
Traceback (most recent call last):
  File "C:/Users/Bill/AppData/Local/Programs/Python/Python35-32/locals.py", line 29, in <module>
    test3()
  File "C:/Users/Bill/AppData/Local/Programs/Python/Python35-32/locals.py", line 19, in test3
    print(A)
UnboundLocalError: local variable 'A' referenced before assignment

The error indicates that assigning to a variable in test3 blocks access to a global variable of the same name anywhere in the function, even before the assignment. What this means is that functions cannot be compiled in a single pass.

Notice anything?

Project scope changed from “toy Python compiler” to “Fierce Python compiler for all the cool kid micros?”

Or maybe the simpler: Another one of my projects that is almost 2 years old?

1 Like

Do you mean compile Python code into FORTH or compile FORTH programs?

I have written FORTH interpreters for several processors and had thought about implementing a metacompiler.

Fifth-generation programming language paradigm looks interesting.

It is still a toy because it cannot handle but the simplest programs.

  • No lists, tuples or dictionaries
  • No indexing or slicing
  • No iterators
  • No floating point
  • Cannot define functions
  • No exception handling
  • Cannot import libraries
1 Like