Improved MENSCH™ Microcomputer Software

Programming the 6502 microprocessor and its relatives in assembly and other languages.
Martin_H
Posts: 837
Joined: 08 Jan 2014

Re: Improved MENSCH™ Microcomputer Software

Post by Martin_H »

@BDD, thanks for the code example, it was instructive. I remember you discussing synching the direct page pointer with the stack frame, but I didn't have enough context to understand what it achieved. I get it now; programs no longer need a shared direct page area (formerly page zero); every function has its own direct page! This eliminates the tension between dedicating blocks of the direct page versus reusing parts of it. The latter being an attractive nuisance for bugs.

To resolve this on the 6502, I used a Forth like page zero data stack using the X register. But the 65816 builds that into its much larger data and return stack. Making this convention unnecessary.

BTW I laughed at the "cld ;just in case!" line. I love programming the 6502 and I understand why it has support for packed decimal, but it's a potential for hard-to-find bugs.
User avatar
BigDumbDinosaur
Posts: 9427
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: Improved MENSCH™ Microcomputer Software

Post by BigDumbDinosaur »

Martin_H wrote:
@BDD, thanks for the code example, it was instructive.
You’re welcome.

A certain amount of patient experimentation went into that framework before I was satisfied with it and felt it was safe to make it public.  An objective, aside from taking advantage of the 816’s movable direct page, was to automate the process as much as possible to prevent messing up the stack—debugging something like that can be challenging.  :shock:  In that vein, I am relying on some of the Kowalski assembler’s features, especially symbols declared as assembly-time variables (the ones using the .set pseudo-op and the .= operator).  There is, of course, heavy dependency on local symbols and labels—it would have been much harder had those not been available.

Something to note is when DP is pointed to the stack in my framework, the offset starts at $00.  This is in contrast to using stack-relative addressing, in which the offset starts at $01.  The $00 offset is the result of the TSC - INC - TCD sequence, which I came up with to avoid an off-by-one error when addressing the temporary direct page.  I am generally not concerned with this offset business, as I give each register a symbol and only use that symbol to access the register’s stack copy.

Another thing worth noting is the order in which the registers are pushed to the stack.  By pushing .Y before .X, it becomes possible to pass a 32-bit pointer in those registers, with the least-significant word (LSW) in .X, and use indirect-long addressing.  Passing the LSW in .Y is possible if the registers are reversed on the stack, of course.  However, as the low-high combination in .X-.Y is common in much 6502 programming, I felt it was best to stick with it.

I always push DP last, so it is at SP+1 on the stack.  By doing so, I can revert to the entry direct page within the function, if necessary, with LDA $00 - TCD, without changing the register stack frame.  I can go back to the local direct page by again using the TSC - INC - TCD sequence.  Or, if switching between the local and entry direct pages is to happen multiple times, I push the local DP value to SP+1 (with the entry DP at SP+3) so I can easily fetch the local DP with LDA $01,S when DP is pointed elsewhere.

A caution regarding these stack and direct page shenanigans is in order.  It is essential that your operating environment, especially interrupt handlers, not make any assumptions about what is in DB (data bank) and DP, or the sizes of the registers.  In particular, always keep in mind that if the index registers are set to eight bits, whatever is in the MSB of both registers will vanish.  This is unlike the accumulator, which will retain the MSB in .B.B remains accessible through the XBA instruction.

The other register “gotchas” to watch out for are the TDC and TSC instructions.  Those two transfer a 16-bit value into the accumulator without regard to the accumulator’s width, replacing whatever was in .B with the MSB of the source register.

Quote:
BTW I laughed at the "cld ;just in case!" line.  I love programming the 6502 and I understand why it has support for packed decimal, but it's a potential for hard-to-find bugs.
That comes from back when I started writing machine code for my Commodore 64 (over 42 years ago!) that used decimal arithmetic and would experience mysterious crashes.  It wasn’t until after I had looked at the C-64’s IRQ handler using Supermon 64 that I discovered the IRQ handler doesn’t revert to binary arithmetic after saving the registers.  It became yet another bit of “defensive programming.”  :D
Last edited by BigDumbDinosaur on Fri Feb 06, 2026 8:26 pm, edited 1 time in total.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
Martin_H
Posts: 837
Joined: 08 Jan 2014

Re: Improved MENSCH™ Microcomputer Software

Post by Martin_H »

I successfully ported a rewritten Arduino Ping))) sample and the required pbPulsin function. There was only a single bug in the pbPulsin function. Woot woot!

Challenges:
- Debugging real-time signals is difficult. For example, debug output statements can't be used.
- Most of the issues were within the sample program and div16 function; it took longer to debug the test environment than the function itself.
- The Ping))) sensor draws significant current, which caused phantom issues when running on USB power. Switching to battery power resolved this.

Next Steps:
- I am porting a line sensor demo using the pbRCTime function. The pbRCTime function is similar to pbPulsin, so it should be faster.
- The timer continues to run after the pulse trailing edge transition. This doesn't affect subsequent calls, but I would like to find a way to stop it entirely.
Martin_H
Posts: 837
Joined: 08 Jan 2014

Re: Improved MENSCH™ Microcomputer Software

Post by Martin_H »

I know this is a fairly simple question. I'm outputting data as text and want the absolute value. No problem I'll just write a function.

Code: Select all

; abs16 - takes the absolute value of the input.
; Inputs:
;   C - number of take absolute value.
; Outputs
;   C - result
PUBLIC abs16
	and #$ffff
	bpl @return
	dec			; undo two's complement
	eor #$ffff
@return:	rts
ENDPUBLIC
I'm just wondering if there's a spiffier way to do this.
User avatar
Dr Jefyll
Posts: 3526
Joined: 11 Dec 2009
Location: Ontario, Canada
Contact:

Re: Improved MENSCH™ Microcomputer Software

Post by Dr Jefyll »

Not really very spiffy, but here's what I notice offhand. :)

Code: Select all

	and #$ffff
This 3-byte, 3-cycle instruction you used isn't the only way to test the accumulator's sign bit and update the N flag. Instead, ...

Code: Select all

	TAX   ;(or TAY)
is only 1 byte and 2 cycles (but it assumes you don't mind trashing X or Y); and

Code: Select all

	ROR
	ROL
is 2 bytes and 4 cycles.

-- Jeff
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html
User avatar
BigDumbDinosaur
Posts: 9427
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: Improved MENSCH™ Microcomputer Software

Post by BigDumbDinosaur »

Dr Jefyll wrote:
Not really very spiffy, but here's what I notice offhand. :)

Code: Select all

	and #$ffff
This 3-byte, 3-cycle instruction you used isn't the only way to test the accumulator's sign bit and update the N flag. Instead, ...

Code: Select all

	TAX   ;(or TAY)
is only 1 byte and 2 cycles (but it assumes you don't mind trashing X or Y); and

Code: Select all

	ROR
	ROL
is 2 bytes and 4 cycles.
Try ORA !#0 to flip your flags...with the 816, there are usually several ways to skin the (16-bit) cat (in the Kowalski assembler, !# causes a 16-bit operand to be assembled).
x86?  We ain't got no x86.  We don't NEED no stinking x86!
User avatar
Dr Jefyll
Posts: 3526
Joined: 11 Dec 2009
Location: Ontario, Canada
Contact:

Re: Improved MENSCH™ Microcomputer Software

Post by Dr Jefyll »

BigDumbDinosaur wrote:
Try ORA !#0 to flip your flags...
Brings us back to 3 bytes and 3 cycles, doesn't it?

-- Jeff

ps- But I agree that there are usually several different ways to skin the cat. (Poor kitty!)
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html
Martin_H
Posts: 837
Joined: 08 Jan 2014

Re: Improved MENSCH™ Microcomputer Software

Post by Martin_H »

I debugged the pbRCTime function and was able to read a line sensor value. Line following robots are always fun.

I'll leave the abs16 function as I don't want to trash the other registers, so 3 cycles 3 bytes is good enough.

I added umax16 and umin16 functions as I'm sure they'll come in handy for clamping servo pulse widths to max and min values. That's useful in preventing servo burnout if a bug starts sending pulse widths outside of RC specs. I'm mean hypothetically, it's not like I let the magic smoke out of a servo doing that.

I also ported and debugged my unsigned square root usqrt16. Useful when doing odometry and dead reckoning. Technically calling it usqrt16 is overkill since only positive numbers have real square roots, but I decided to follow my own naming convention.

PBasic also has an integer trigonometry, and I wrote a 6502 version of that years ago. So, I'll need to port that if I want to do dead reckoning as well.
Martin_H
Posts: 837
Joined: 08 Jan 2014

Re: Improved MENSCH™ Microcomputer Software

Post by Martin_H »

I have the pbPWM function and pwm_led.asm sample working. The LED grows slowly brighter, slowly dimmer, and then returns to the monitor. My planned use of this function is controlling a motor's speed through an H bridge.

I learned something important. If you change the direct page register and later call the monitor's I/O routines, it behaves unpredictably. Good to know for the future.
User avatar
Dr Jefyll
Posts: 3526
Joined: 11 Dec 2009
Location: Ontario, Canada
Contact:

Re: Improved MENSCH™ Microcomputer Software

Post by Dr Jefyll »

Martin_H wrote:
If you change the direct page register and later call the monitor's I/O routines, it behaves unpredictably.
I assume you mean it hypothetically could behave unpredictably if used in that way. It's not like you experienced unpredictable behavior by doing so.

Right. Got it! :wink:

-- Jeff
Martin_H wrote:
[...] servo burnout if a bug starts sending pulse widths outside of RC specs. I'm mean hypothetically, it's not like I let the magic smoke out of a servo doing that.
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: Improved MENSCH™ Microcomputer Software

Post by barnacle »

Martin_H wrote:
My planned use of this function is controlling a motor's speed through an H bridge.
There are some _excellent_ tiny H-bridge motor controllers available on LCSC's parts list, available for literally pennies: e.g. https://www.lcsc.com/product-detail/C18 ... controller

At that price it's not worth buying and soldering transistors!

Neil
Martin_H
Posts: 837
Joined: 08 Jan 2014

Re: Improved MENSCH™ Microcomputer Software

Post by Martin_H »

Dr Jefyll wrote:
I assume you mean it hypothetically could behave unpredictably if used in that way. It's not like you experienced unpredictable behavior by doing so.

Right. Got it! :wink:

-- Jeff
Of course, I would never do that by accident! Yeah, that's the ticket.
barnacle wrote:
There are some _excellent_ tiny H-bridge motor controllers available on LCSC's parts list, available for literally pennies: e.g. https://www.lcsc.com/product-detail/C18 ... controller

At that price it's not worth buying and soldering transistors!
Thanks for the link. Yes, it's better to use an IC over discrete components. In the past I've used an SN754410 because it has an enable line, but I'm always open to newer IC's. I use two pins to control motor direction and a third as a PWM channel to control velocity. With a single VIA port, you can control up to three motors with a single PWM channel for their velocity. Better programmers than me could probably squeeze two PWM channels out of a single VIA port.
User avatar
drogon
Posts: 1671
Joined: 14 Feb 2018
Location: Scotland
Contact:

Re: Improved MENSCH™ Microcomputer Software

Post by drogon »

Martin_H wrote:
Thanks for the link. Yes, it's better to use an IC over discrete components. In the past I've used an SN754410 because it has an enable line, but I'm always open to newer IC's. I use two pins to control motor direction and a third as a PWM channel to control velocity. With a single VIA port, you can control up to three motors with a single PWM channel for their velocity. Better programmers than me could probably squeeze two PWM channels out of a single VIA port.
FWIW, For small motors, I've used many L293D's in the past. The through-hole ones (16-pin DIP) have the advantage that you can vertically stack them to increase current - maybe strictly not 'allowed' but I've seen it done... (And the D variants have built-in flyback diodes)

A disadvantage is that you need 3 outputs per motor (or 2 plus an inverter) but it means you can use high or low 'braking' to stop a motor quickly if needed. Basically 2 pins to set direction or braked and one pin to provide on/off pulses.

-Gordon
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/
Martin_H
Posts: 837
Joined: 08 Jan 2014

Re: Improved MENSCH™ Microcomputer Software

Post by Martin_H »

I'm reading the WDC monitor source to understand how it sets and clears the tone generators. Here's a link to the code:
https://github.com/WesternDesignCenter/ ... _TONES.ASM
Overall I understand it, but one thing confuses me. The store instructions have an '!' character before the address.

Code: Select all

         STX !T5CL
I read the WDC assembler syntax and it says that is an absolute address. All the 6502 assemblers I've used don't have a character that indicates absolute addressing, it's implied for a two-byte address, and T5CL is $DF6A.

I just want to make sure this isn't some extra fancy 65816 absolute addressing mode that I don't know about.
User avatar
BigDumbDinosaur
Posts: 9427
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: Improved MENSCH™ Microcomputer Software

Post by BigDumbDinosaur »

Martin_H wrote:
The store instructions have an '!' character before the address.
Refer to subsection 6.3.3.6 in the 65C816 data sheet for an explanation.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
Post Reply