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

Even agent 99 can miss those metadata headers too :wink:

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.

1 Like

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?

1 Like

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:

https://docs.python.org/3/library/functions.html#int

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 https://github.com/BillGee1/PY65

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
2 Likes

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ā€¦

2 Likes

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.

1 Like

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ā€¦

1 Like

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.

1 Like

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ā€¦

2 Likes

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.

:exploding_head: :

2 Likes

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)
3 Likes

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? ')
1 Like

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.

1 Like