VIC 20 Apollo DSKY Indicator Lights

Basic and Machine Language

Moderator: Moderators

Post Reply
MartinC
Vic 20 Drifter
Posts: 33
Joined: Tue Oct 25, 2022 12:18 pm
Website: https://winterfam.co.uk
Location: Kent,uk
Occupation: Author

VIC 20 Apollo DSKY Indicator Lights

Post by MartinC »

Hi all,

This questions relates to an unexpanded VIC 20.

My first (probably too ambitious) assembly project is to try and recreate a lunar module display and keyboard from the 1960s. I need to be able to light up several areas of the screen (2x4 characters) based on whether a memory location contains a zero or one. I've redefined 72 characters and loaded them up starting at location 6144 (decimal). I don't have enough RAM to include reversed characters, so I am trying to figure out an efficient way of reversing these 2x4 portions of the screen so that they appear to "light up" to tell the astronauts things.

I've not run the code below, and it's probably all wrong in terms of addressing modes etc (so forgive my newbieness), but would it reverse a given character?

Code: Select all

		lda 7726 ;put the char code from screen ram location into the accumulator
reverse_char    ;reverse the char code currently in the accumulator
                clc
                adc #6144 ;add 6144 to the accumulator to get the char address
                sta 7675 ;store a in a safe place for later
                ldy #7 ; we need to loop through the 8 bits of the character
rvc_loop   lda #255 ; put 255 in accumulator
                sec ; set the carry bit
                sbc (7675)        ;subtract the char address content from 255
                sta (7675),y      ;store the result back in the char address
                dey             ; decrement y
                inc 7675      ; add 1 to the value in 7676
                bne rvc_loop    ; if x is non-zero rvc_loop
                rts ; all done
Hope you can see what I am trying to do, maybe there's a better way to light these lamps?

Thanks all

Martin
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: VIC 20 Apollo DSKY Indicator Lights

Post by Mike »

Hi, Martin,
MartinC wrote:I've not run the code below, and it's probably all wrong in terms of addressing modes etc (so forgive my newbieness), but would it reverse a given character?
as is, the code won't even assemble. The intent is clear - (try to) calculate a base address within a character set from screen code, invert 8 consecutive bytes starting at that base address -, but this attempt at an implementation does not honour in any way that the 65xx is an 8-bit-CPU and only can handle data 8 bits at a time.

The code can be made to work, yes, but then you also need to be aware that reversing a character definition will affect all screen positions where that character appears! Unless there's only one visible instance of that screen code, that's probably not what you want.

I don't follow your argument about memory restrictions. Even if you double the set to 144 redefined characters, those will take 1152 bytes and you still have ~2.4 KB available for your code on an unexpanded VIC-20.

Anyway, first here's how a working version of your routine would look in BASIC:

Code: Select all

1 A = PEEK(7726) : REM get screen code. likely a constant.
2 AD = 6144 + 8*A : REM note "8*A"!
3 FOR Y=7 TO 0 STEP -1
4 : A = PEEK(AD+Y)
5 : A = 255 - A
6 : POKE AD+Y,A
7 NEXT
Actually, one would calculate "255 - A" simply with EOR #$FF instead of using SBC.

Now, A and Y were subtly chosen as variable names - we'll use the corresponding registers in machine code. AD however specifically needs to go into zero page, so the (ZP),Y address mode can be put to use! We need a pair of addresses for this, the low byte goes to the lower address, the high byte goes to the higher address. As $FB and $FC are not used by BASIC or KERNAL, we'll put AD there.

Code: Select all

 LDA $1E2E   ; A = PEEK(7726)
 STA $FB
 LDA #$00
 ASL $FB
 ROL
 ASL $FB
 ROL
 ASL $FB
 ROL
;STA $FC     ; form "8*A" in $FB/$FC
;CLC
;LDA $FB
;ADC #$00
;STA $FB
;LDA $FC
 ADC #$18
 STA $FC     ; AD = 6144 + 8*A
 LDY #$07    ; FOR Y=7 ...
.loop
 LDA ($FB),Y ; A=PEEK(AD+Y)
 EOR #$FF    ; "A=255-A"
 STA ($FB),Y ; POKEAD+Y,A
 DEY
 BPL loop    ; TO 0:...:NEXT
Some lines are commented out. The code thus takes a shortcut where only the high byte of AD is adjusted. It is not necessary to store away the high byte first. Furthermore, CLC can be omitted as the C flag is known to be clear when ADC #$18 is executed. These are common optimizations you'll see in lots of 65xx code.

...

In reference to another recent thread of yours: at this stage, a cross-development tool like CBM Prg Studio is likely a source of self-induced issues and questions which however do not serve much in understanding the actual problem at hand. It literally adds another abstraction layer, which you don't want at the moment!

Even if above code solves the original question, I wholeheartedly recommend you take a sharper look at the examples in my VICMON primer, and try them out exactly in the way given, i.e. with VICMON in emulation or on real hardware. The examples there convey quite some idioms typical for 65xx assembly language and you should first get a good grip of those.

Greetings,

Michael
User avatar
bjonte
Vic 20 Hobbyist
Posts: 110
Joined: Sun Jan 22, 2017 5:47 am
Location: Gothenburg

Re: VIC 20 Apollo DSKY Indicator Lights

Post by bjonte »

It might be easier to flash by changing the colors of the characters instead.
MartinC
Vic 20 Drifter
Posts: 33
Joined: Tue Oct 25, 2022 12:18 pm
Website: https://winterfam.co.uk
Location: Kent,uk
Occupation: Author

Re: VIC 20 Apollo DSKY Indicator Lights

Post by MartinC »

but this attempt at an implementation does not honour in any way that the 65xx is an 8-bit-CPU and only can handle data 8 bits at a time.
- Apart from that all good though, yeah? :lol:

I have learned from your VICMON tutorial, this is a case of trying to run before you can crawl, as you can tell. Thanks for taking the time for such a detailed explanation. I've been using the ZP pointers method to draw and colour a screen definition, so I don't know why I ignored them. It was Sunday afternoon and I'd been scratching my head for too long :-)

In fact, the code I've written to do that could likely handle this job too, I just need to use a couple of configurable memory locations to use as 'parameters' for the sub routine, to make the screen or colour location I want to write to and what I want to write their re-usable, so to speak.

When it comes to the memory map for the project, this is what I sketched out:
Screenshot 2023-02-06 154335.png
Could I shuffle things around more optimally Mike? Then I could refactor my 'drawscr' subroutine, reduce some duplicaton and the overall size.

Here's the drawscr routine:

Code: Select all

drawscr         ;draw the DSKY screen
                lda #<SCRN   ;to-do: make this a parameter - by populating two memory locations containing pointers to the screen memory target zone
                sta $fb
                lda #>SCRN
                sta $fc
                lda #<SMAP ;to-do: make this a parameter - but populating two memory locations containing pointers to the data to be drawn 
                sta $fd
                lda #>SMAP
                sta $fe
                ldx #2 ;to-do make this a parameter
main_lp    ldy #$00
loop1        lda ($fd),y
                sta ($fb),y
                iny
                bne loop1
                inc $fc
                inc $fe
                dex
                bne main_lp
                rts
At the moment I have another version of this same routine to do the colour - but that seems wasteful, and then my 'flash lamps' routine is just another variation of the same thing. Could this little bit of utility code be placed in the cassette buffer even?
Post Reply