Even agent 99 can miss those metadata headers too
Finally mustered enough motivation to implement the integer mod operator and adjust the division code to round toward negative infinity.
I am really tempted to put off work on constant folding since that is just an optimization and implement left and right shift and the int function instead.
Per request from @dave , I isolated all of the system-dependent portions of the run-time library into a single include file and also changed the serial port emulation from a UART at $D000 to an ACIA at $E000. If you see me cursing or slapping my forehead, it is because I just realized I ran an old program which ātalkedā into empty space.
The intention was to do this yesterday, but I went to the Machine Shop committee meeting and wound up helping them find The Last Bugā¢ in their RFID Interlock project.
Hmm, whatās next?
I surrender. I was trying to get the int function done for the retrocomputing meeting this afternoon, but it is just a bit too much:
I left the stubs in the compiler and the run-time library code so that I can continue working on it. Likewise for the bitwise left and right shift operators.
Anyway, the executables are available at GitHub - BillGee1/COMPY65: Python language compiler targeting the 6502 microprocessor
At long last, int() is starting to work for the simplest cases:
print(int('1011', 2))
print(int('1011', 8))
print(int('1011', 10))
print(int('1011', 16))
print(int('1011', 36))
yields
11
521
1011
4113
46693
Still working on int(), but the end of the tunnel is visible.
Howās this for bootstrapping test scaffolding?
Number = input('Number? ')
while Number != '':
Base = input('Base? ')
Base = int(Base, 10)
print(int(Number, Base), Base)
Number = input('Another? ')
Unfortunately, a serious compiler bug surfaced.
Base = int(input('Base? '), 10)
crashes with a stack overflow errorā¦
I think I have the functionality done for int() now and am testing it. The only question is whether ā1_ā is a valid number. The Python on my machine is version 3.5 and I will need to have to wait until I get back to the Space to see what 3.6 does with it.
The compiler bug was fortunately something dumb rather than a major architectural flaw.
Several developments.
Someone commented on Github that the name PY65 may be confused with another project with the same name, a 6502 emulator written in Python. So the name is now COMPY65. Fortunately Github is very good about repository name changes and the original URL still works.
Remember when I posted this?
It has finally bit me.
I was trying to write some test code in the form
print("int('1_2_3')", int('1_2_3'))
for all of the fringe cases I can find.
The assembler was getting tripped up on those embedded apostrophes.
So I borrowed an old FORTRAN stunt, the Hollerith string in which the programmer has to specify the length of a string. This was from back in the day when it was tough for a compiler to count characters in a string with the limited resources it had.
Now, the string looks like this:
3D6F 69 6E 74 28 10719 .holl 12,int('1_2_3');
3D73 27 31 5F 32
3D77 5F 33 27 29
Donāt let me catch you hand writing code like this. It is only for compilers.
Also, I thought it would be useful to implement the hex function to help with the testing. But alas, nothing is easy when Pythonās variable precision integers are involvedā¦
Still working on int(), but the end is near.
My test file currently looks like this:
#print(int('')) # bad
#print(int(' ')) # bad
#print(int('_0')) # bad
#print(int('0_')) # bad
#print(int('0_ ')) # bad
#print(int('0x')) # bad
#print(int('0x', 0)) # bad
#print(int('0x', 16)) # bad
Base = 10
Number = input('Number? ')
while Number != '':
BaseIn = input('Base? ')
if BaseIn != '':
# Change base
Base = int(BaseIn, 10)
print(int(Number, Base), Base)
Number = input('Another? ')
print("int()", int())
print("int(False)", int(False))
print("int(True)", int(True))
print("int(123)", int(123))
print("int('123')", int('123'))
print("int(' 123 ')", int(' 123 '))
print("int('1011')", int('1011'))
print("int('1011', 0)", int('1011', 0))
print("int('1011', 2)", int('1011', 2))
print("int('1011', 8)", int('1011', 8))
print("int('1011', 10)", int('1011', 10))
print("int('1011', 16)", int('1011', 16))
print("int('1011', 36)", int('1011', 36))
print("int('-1')", int('-1'))
print("int('1_2_3')", int('1_2_3'))
input('-- more --')
print("int('000')", int('000'))
print("int('0_0')", int('0_0'))
print("int('0_0', 0)", int('0_0', 0))
print("int('000', 0)", int('000', 0))
print("int('0_0', False)", int('0_0', False))
print("int('0_0', 2)", int('0_0', 2))
print("int('0_0', 8)", int('0_0', 8))
print("int('0_0', 16)", int('0_0', 16))
print("int('1_0', 0)", int('1_0', 0))
print("int('0', 0)", int('0', 0))
print("int('0x10', 0)", int('0x10', 0))
print("int('0o10', 0)", int('0o10', 0))
print("int('0b10', 0)", int('0b10', 0))
print("int('0X10', 0)", int('0X10', 0))
print("int('0O10', 0)", int('0O10', 0))
print("int('0B10', 0)", int('0B10', 0))
print("int('0x_10', 0)", int('0x_10', 0))
print("int('0o_10', 0)", int('0o_10', 0))
print("int('0b_10', 0)", int('0b_10', 0))
input('-- more --')
print("int('0x10', 16)", int('0x10', 16))
print("int('0o10', 8)", int('0o10', 8))
print("int('0b10', 2)", int('0b10', 2))
Everything works except for
print("int('0x10', 16)", int('0x10', 16))
print("int('0o10', 8)", int('0o10', 8))
print("int('0b10', 2)", int('0b10', 2))
That was not accounted for in my original design and Iāll have to think about it a bit.
I just discovered that
int('0b1', 16)
is valid Python, yielding 177.
Arrrrrrgggghhhh!!!
Re-entering Guru Meditationā¦
Taking a short break from working on int() and hex().
Looking ahead at implementing on the 6800, 6809 and AVR.
Here are some common 6502 code sequences and some cobbled together equivalents in 6800 and AVR code.
Load a 16-bit constant into a variable in the zero page:
6502
3CC7 A2 59 [2] 10347 ldx #N_Int&$FF
3CC9 86 18 [3] 10348 stx Ptr0
3CCB A2 40 [2] 10349 ldx #N_Int>>8
3CCD 86 19 [3] 10350 stx Ptr0+1
Ī£ = 10 cycles
6800
012B CE AC2B [3] 00484 ldx #MEMEND
012E DF 14 [5] 00485 stx DataStackTop
Ī£ = 8 cycles
AVR
0001CF E4E3 [1] 01276 ldi R30,low(InputBuffer)
0001D0 E0F8 [1] 01277 ldi R31,high(InputBuffer)
0001D1 93E0 0841 [2] 01278 sts LBP,R30
0001D3 93F0 0842 [2] 01279 sts LBP+1,R31
Ī£ = 6 cycles
Copy a 16-bit variable, both in the zero page:
6502
3CAC A5 3C [3] 10326 lda Pargs
3CAE 85 18 [3] 10327 sta Ptr0
3CB0 A5 3D [3] 10328 lda Pargs+1
3CB2 85 19 [3] 10329 sta Ptr0+1
Ī£ = 12 cycles
6800
0153 DE 12 [4] 00512 ldx ReturnStackTop
0155 DF 00 [5] 00513 stx RS
Ī£ = 9 cycles
AVR
0000D6 91E0 0833 [2] 00525 lds R30,DictBase
0000D8 91F0 0834 [2] 00526 lds R31,DictBase+1
0000DA 93E0 083B [2] 00527 sts DictPtr,R30
0000DC 93F0 083C [2] 00528 sts DictPtr+1,R31
Ī£ = 8 cycles
Subtract a constant from a 16-bit variable in the zero page:
6502
3DF3 38 [2] 10561 sec
3DF4 A5 0E [3] 10562 lda Int4
3DF6 E9 01 [2] 10563 sbc #1
3DF8 85 0E [3] 10564 sta Int4
3DFA A5 0F [3] 10565 lda Int4+1
3DFC E9 00 [2] 10566 sbc #0
3DFE 85 0F [3] 10567 sta Int4+1
Ī£ = 18 cycles
6800
0133 96 14 [3] 00490 ldaa DataStackTop
0135 D6 15 [3] 00491 ldab DataStackTop+1
0137 C0 00 [2] 00492 subb #RETURN_STACK_SIZE&$FF
0139 82 08 [2] 00493 sbca #RETURN_STACK_SIZE>>8
013B 97 14 [4] 00494 staa DataStackTop
013D D7 15 [4] 00495 stab DataStackTop+1
Ī£ = 18 cycles
AVR
0000D6 91E0 0833 [2] 00525 lds R30,DictBase
0000D8 91F0 0834 [2] 00526 lds R31,DictBase+1
0000CB 58E1 [1] 00513 subi R30,low(RETURN_STACK_SIZE + 1)
0000CC 40F0 [1] 00514 sbci R31,high(RETURN_STACK_SIZE + 1)
0000DA 93E0 0833 [2] 00527 sts DictBase,R30
0000DC 93F0 0834 [2] 00528 sts DictBase+1,R31
Ī£ = 10 cycles
Subroutine call overhead:
6502
0238 20 0F07 [6] 00169 jsr InitRTL
0EBA 60 [6] 01673 rts
Ī£ = 12 cycles
6800
03FE BD 010F [9] 01099 jsr Echo
010E 39 [5] 00437 rts
Ī£ = 14 cycles
AVR
0005A1 D002=0005A4 [3] 04598 rcall ntox
0005A9 9508 [2] 04624 ret
Ī£ = 5 cycles
Branch if a variable in the zero page points to 16-bit zero:
6502
1961 A0 00 [2] 03307 ldy #0
1963 B1 44 [5/6] 03308 lda (Var),Y
1966 C8 [2] 03310 iny
1967 11 44 [5/6] 03311 ora (Var),Y
1969 F0 05 (1970) [2/3] 03312 beq GetObject0
Ī£ = 16 cycles
6800
0153 DE 12 [4] 00512 ldx ReturnStackTop
0177 EE 00 [6] 00545 ldx ,X
036C 27 59 (03C7) [4] 00983 beq NUMBER_Fail
Ī£ = 14 cycles
AVR
000274 91E0 0837 [2] 01660 lds R30,DataStackTop
000276 91F0 0838 [2] 01661 lds R31,DataStackTop+1
00026A 9021 [2] 01632 ld R2,Z+
00026B 8030 [1] 01633 ld R3,Z
0002F7 2823 [1] 02048 or R2,R3
0002F8 F049=000302 [1/2] 02049 breq __PICK_LTOne
Ī£ = 9 cycles
Replace a pointer in the zero page with what it points to:
6502
1A22 A0 00 [2] 03540 ldy #0
1A2B B1 44 [5/6] 03547 lda (Var),Y
1A2D AA [2] 03548 tax
1A2E C8 [2] 03549 iny
1A2F B1 44 [5/6] 03550 lda (Var),Y
1A31 86 44 [3] 03551 stx Var
1A33 85 45 [3] 03552 sta Var+1
Ī£ = 22 cycles
6800
0153 DE 12 [4] 00512 ldx ReturnStackTop
0177 EE 00 [6] 00545 ldx ,X
012E DF 12 [5] 00485 stx ReturnStackTop
Ī£ = 15 cycles
AVR
000274 91E0 0835 [2] 01660 lds R30,ReturnStackTop
000276 91F0 0836 [2] 01661 lds R31,ReturnStackTop+1
00026A 9021 [2] 01632 ld R2,Z+
00026B 8030 [1] 01633 ld R3,Z
0000C7 9220 0835 [2] 00510 sts ReturnStackTop,R2
0000C9 9230 0836 [2] 00511 sts ReturnStackTop+1,R3
Ī£ = 11 cycles
Add two variables in the zero page:
6502
1B83 18 [2] 03877 clc
1B84 A5 38 [3] 03878 lda Scr3
1B86 65 0C [3] 03879 adc Int3
1B88 85 1E [3] 03880 sta Ptr3
1B8A A5 39 [3] 03881 lda Scr3+1
1B8C 65 0D [3] 03882 adc Int3+1
1B8E 85 1F [3] 03883 sta Ptr3+1
Ī£ = 20 cycles
6800
0133 96 14 [3] 00490 ldaa DataStackTop
0135 D6 15 [3] 00491 ldab DataStackTop+1
0738 DB 0B [3] 01907 addb Tmp+1
073A 99 0A [3] 01908 adca Tmp
013B 97 14 [4] 00494 staa DataStackTop
013D D7 15 [4] 00495 stab DataStackTop+1
Ī£ = 20 cycles
AVR
000274 91E0 0835 [2] 01660 lds R30,ReturnStackTop
000276 91F0 0836 [2] 01661 lds R31,ReturnStackTop+1
0001FD 91C0 0841 [2] 01348 lds R28,LBP
0001FF 91D0 0842 [2] 01349 lds R29,LBP+1
00030F 0FEC [1] 02101 add R30,R28
000310 1FFD [1] 02102 adc R31,R29
0000C7 9220 0835 [2] 00510 sts ReturnStackTop,R2
0000C9 9230 0836 [2] 00511 sts ReturnStackTop+1,R3
Ī£ = 14 cycles
Decrement a 16-bit variable in the zero page:
6502
1C4E 38 [2] 04041 sec
1C4F A5 0A [3] 04042 lda Int2
1C51 E9 01 [2] 04043 sbc #1
1C53 85 0A [3] 04044 sta Int2
1C55 A5 0B [3] 04045 lda Int2+1
1C57 E9 00 [2] 04046 sbc #0
1C59 85 0B [3] 04047 sta Int2+1
Ī£ = 18 cycles
6800
0153 DE 12 [4] 00512 ldx ReturnStackTop
0130 09 [4] 00487 dex
012E DF 12 [5] 00485 stx ReturnStackTop
Ī£ = 13 cycles
AVR
000274 91E0 0835 [2] 01660 lds R30,ReturnStackTop
000276 91F0 0836 [2] 01661 lds R31,ReturnStackTop+1
0001E7 9731 [2] 01308 sbiw R30,1
0000C7 9220 0835 [2] 00510 sts ReturnStackTop,R2
0000C9 9230 0836 [2] 00511 sts ReturnStackTop+1,R3
Ī£ = 10 cycles
Add two words via two pointers:
6502
1D22 A0 01 [2] 04209 ldy #1
1D24 18 [2] 04210 clc
1D25 B1 18 [5/6] 04211 lda (Ptr0),Y
1D27 71 1A [5/6] 04212 adc (Ptr1),Y
1D29 85 06 [3] 04213 sta Int0
1D2B C8 [2] 04214 iny
1D2C B1 18 [5/6] 04215 lda (Ptr0),Y
1D2E 71 1A [5/6] 04216 adc (Ptr1),Y
1D30 85 07 [3] 04217 sta Int0+1
Ī£ = 32 cycles
6800
0153 DE 12 [4] 00512 ldx ReturnStackTop
0000 A6 00 [5] 00001 ldaa 0,x
0002 E6 01 [5] 00002 ldab 1,x
0153 DE 14 [4] 00512 ldx DataStackTop
0004 EB 01 [5] 00003 addb 1,x
0006 A9 00 [5] 00004 adca 0,x
013B 97 14 [4] 00494 staa DataStackTop
013D D7 15 [4] 00495 stab DataStackTop+1
Ī£ = 36 cycles
AVR
0000B5 91E0 0837 [2] 00479 lds R30,DataStackTop
0000B7 91F0 0838 [2] 00480 lds R31,DataStackTop+1
0001FD 91C0 0841 [2] 01348 lds R28,LBP
0001FF 91D0 0842 [2] 01349 lds R29,LBP+1
00026A 9021 [2] 01632 ld R2,Z+
00026B 8030 [1] 01633 ld R3,Z
00015B 9009 [2] 00960 ld R0,Y+
00015C 8018 [2] 00961 ld R1,Y
00030F 0C20 [1] 02101 add R2,R0
000310 1C31 [1] 02102 adc R3,R1
0000C7 9220 0835 [2] 00510 sts ReturnStackTop,R2
0000C9 9230 0836 [2] 00511 sts ReturnStackTop+1,R3
Ī£ = 21 cycles
Memory access is relatively slow for the AVR and it does not feature anything like a faster zero page. Fortunately, it has lots of registers so that important variables need not be in external memory.
If you recall previous discussions about block copy, the 6800 will be at a severe disadvantage when executing code to process variable-length strings and integers due its single index register. The 6502 had the flexibility to share register Y when advancing pointers in lockstep through several buffers.
Many instructions take an extra cycle on the 6809, but some of the 6800 code fragments can be significantly improved by rewriting to take advantage of features unique to that processor. Iāll work up the 6809 fragments later.
The 6809 has a rough parallel to the Y register indirect indexing mode of the 6502. Several of the addressing modes adds the contents of an accumulator to an index register to form the effective address. The only problem is that the value in the accumulator is treated as a signed number instead of unsigned as in the 6502. This is going to be funā¦
I just discovered that
int('0b1', 16)
is valid Python, yielding 177.
```
I suspect thatās a bug in python. or at leaseā¦ when I fuzzed it to play around I may have found one?
ipython
Python 2.7.15 (default, Jun 17 2018, 12:46:58)
Type "copyright", "credits" or "license" for more information.
IPython 5.8.0 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.
In [1]: int('0b1', 16)
Out[1]: 177
In [2]: int('0b1', 255)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-2-557e04063192> in <module>()
----> 1 int('0b1', 255)
ValueError: int() base must be >= 2 and <= 36
In [3]: int('0b1', 32)
Out[3]: 353
In [4]: int('0b1', 2)
Out[4]: 1
In [5]: int('0b1', 3)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-5-3e29e482cd59> in <module>()
----> 1 int('0b1', 3)
ValueError: invalid literal for int() with base 3: '0b1'
What part are you saying is a bug?
The number base cannot be bigger than 36 because we run out of symbols to represent the value of an individual digit: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ
ValueError: int() base must be >= 2 and <= 36
ā¦
int(ā0b1ā, 3)
ValueError: invalid literal for int() with base 3: ā0b1ā
Now, by all accounts it could be PEMKAC here but I also doubt that 3 being greater than 2 and less than 36 is not a bug of some sort.
Ignore the C-style base prefix notation for a moment.
ā0b1ā is a valid hexadecimal number.
ā0b1ā is not a valid base 3 number.
ā0b1ā is a valid base 2 number if you honor the prefix notation.
:
Got hex() working.
The loop in my test program now looks like this:
Base = 10
Number = input('Number? ')
while Number != '':
BaseIn = input('Base? ')
if BaseIn != '':
# Change base
Base = int(BaseIn, 10)
Value = int(Number, Base)
print(Value, hex(Value), Base)
Number = input('Another? ')
I just realized I can step back from all of these trees and enjoy the forestā¦
The plan was to do this for the retrocomputing meeting, but I can now compile and play this game:
from random import randint
Name = input('Howdy! What is your name? ')
Number = randint(1, 20)
print('\n' + Name + ', I am thinking of a number between 1 and 20.\n')
GuessesTaken = 0
while GuessesTaken < 6:
Guess = int(input('Take a guess... '))
GuessesTaken = GuessesTaken + 1
if Guess == Number:
print('\nGood job,', Name + '!',
'You guessed my number in', GuessesTaken, 'tries.')
break
else:
if Guess < Number:
print('Your guess is too low.')
else:
print('Your guess is too high.')
if GuessesTaken < 6:
print('Try again.\n')
if Guess != Number:
print('\nSorry, I was thinking of', Number)
Int() may have finally been beaten into submission. The testing continuesā¦
What shall I do next?
- Make another release?
- Implement bin() and oct()?
- Implement left and right shift?
- Work on constant folding optimization?
- begin retargeting?
- ???
Part of the code to make this happen:
4575 11550 Int_WhichPrefix
4575 A0 00 [2] 11551 ldy #0 ; Get next character from the string
4577 B1 22 [5/6] 11552 lda (Ptr5),Y
11553
4579 A2 10 [2] 11554 ldx #16 ; Presume base 16
11555
457B C9 58 [2] 11556 cmp #'X'
457D F0 04 (4583) [2/3] 11557 beq Int_GotX
11558
457F C9 78 [2] 11559 cmp #'x'
4581 D0 0F (4592) [2/3] 11560 bne Int_NotBase16
11561
4583 11562 Int_GotX
4583 A4 04 [3] 11563 ldy Byt2 ; If it was base 0
4585 F0 50 (45D7) [2/3] 11564 beq Int_GotPrefix ; Switch to base 16
11565
4587 C0 10 [2] 11566 cpy #16 ; If already base 16
4589 F0 4C (45D7) [2/3] 11567 beq Int_GotPrefix ; Keep it
11568
458B C0 22 [2] 11569 cpy #34 ; if less than base 34
458D 90 66 (45F5) [2/3] 11570 bcc Int_InvalidLiteral ; Invalid
11571
458F 4C 4606 [3] 11572 jmp Int_MainLoop ; Treat like a regular character
11573
4592 11574 Int_NotBase16
4592 A2 02 [2] 11575 ldx #2 ; Presume base 2
The test program now looks like this:
#print(int('')) # bad
#print(int(' ')) # bad
#print(int('_0')) # bad
#print(int('0_')) # bad
#print(int('0_ ')) # bad
#print(int('0x')) # bad
#print(int('0x', 0)) # bad
#print(int('0x', 16)) # bad
Base = 10
Number = input('Number? ')
while Number != '':
BaseIn = input('Base? ')
if BaseIn != '':
# Change base
Base = int(BaseIn, 10)
Value = int(Number, Base)
print(Value, hex(Value), Base)
Number = input('Another? ')
print("int()", int())
print("int(False)", int(False))
print("int(True)", int(True))
print("int(123)", int(123))
print("int('123')", int('123'))
print("int(' 123 ')", int(' 123 '))
print("int('1011')", int('1011'))
print("int('1011', 0)", int('1011', 0))
print("int('1011', 2)", int('1011', 2))
print("int('1011', 8)", int('1011', 8))
print("int('1011', 10)", int('1011', 10))
print("int('1011', 16)", int('1011', 16))
print("int('1011', 36)", int('1011', 36))
print("int('+1')", int('+1'))
print("int('-1')", int('-1'))
print("int(' +1')", int(' +1'))
print("int(' -1')", int(' -1'))
print("int('1_2_3')", int('1_2_3'))
print("int('0')", int('0'))
print("int('0', 0)", int('0', 0))
print("int('0', 10)", int('0', 10))
print("int('00')", int('00'))
print("int('00', 0)", int('00', 0))
print("int('00', 10)", int('00', 10))
input('-- more --')
print("int('000')", int('000'))
print("int('000', 0)", int('000', 0))
print("int('000', 10)", int('000', 10))
print("int('0_0')", int('0_0'))
print("int('0_0', 0)", int('0_0', 0))
print("int('0_0', 10)", int('0_0', 10))
print("int('0_0', False)", int('0_0', False))
print("int('0_0', 2)", int('0_0', 2))
print("int('0_0', 8)", int('0_0', 8))
print("int('0_0', 16)", int('0_0', 16))
print("int('1_0', 0)", int('1_0', 0))
print("int('0', 0)", int('0', 0))
print("int('0x10', 0)", int('0x10', 0))
print("int('0o10', 0)", int('0o10', 0))
print("int('0b10', 0)", int('0b10', 0))
print("int('0X10', 0)", int('0X10', 0))
print("int('0O10', 0)", int('0O10', 0))
print("int('0B10', 0)", int('0B10', 0))
print("int('0x_10', 0)", int('0x_10', 0))
print("int('0o_10', 0)", int('0o_10', 0))
print("int('0b_10', 0)", int('0b_10', 0))
input('-- more --')
print("int('0b1', 36)", int('0b1', 36))
print("int('0o1', 36)", int('0o1', 36))
print("int('0x1', 36)", int('0x1', 36))
print("int('0x10', 16)", int('0x10', 16))
print("int('0o10', 8)", int('0o10', 8))
print("int('0b10', 2)", int('0b10', 2))
It was easy to morph a copy of hex() to make bin() since it was designed with that in mind. Doing oct() will not be so easy since octits cross byte boundaries.
The loop in the test program now looks like this:
Base = 10
Number = input('Number? ')
while Number != '':
BaseIn = input('Base? ')
if BaseIn != '':
# Change base
Base = int(BaseIn, 10)
Value = int(Number, Base)
print(Value, hex(Value), bin(Value), Base)
Number = input('Another? ')
My vote would be left / right shift. In my mind, this is conceptually easy and may serve as an early glimpse into what you may be getting yourself into with the octal crossing byte boundaries.