6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Sep 29, 2024 11:24 am

All times are UTC




Post new topic Reply to topic  [ 27 posts ]  Go to page Previous  1, 2
Author Message
PostPosted: Tue Nov 20, 2018 10:36 pm 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
The string is printed backwards because your index is counting down from the end. There are two reasonable ways to correct this:

1: Use your Y register as the loop counter, counting down, while indexing up from 0 with the X register.

2: Load your X register with the *negative* of your string length, and adjust your base pointer to compensate. This is a bit tricker to pull off as a novice, but something similar is often done by advanced compilers for newer CPUs.

Incidentally, to test for equality with zero, you often don't need a CMP instruction - because the increment and decrement instructions set the N and Z flags for you. They just don't set C and V. This means the extra indexing of solution 1 should come for free:
Code:
    ldy #$0d
    ldx #0
loop:
    lda $081b,x     
    jsr CHROUT       
    inx              ; Increment index
    dey              ; Decrement count
    bne loop       ; WHILE remaining characters, loop.
    rts             


Top
 Profile  
Reply with quote  
PostPosted: Tue Nov 20, 2018 11:15 pm 
Offline

Joined: Fri Nov 16, 2018 8:55 pm
Posts: 71
Quote:
It's worth noting that your comparison with zero isn't needed: it's a common thing for a newcomer to 6502 to do, but once you've got a better handle on the way the status bits are set, you'll be confident that the Z flag will already be set by the DEX.


It's interesting you mention that. I just started reading chapter 3 of Butterfield's Machine Language book and he immediately starts talking about the relationship between bne, the Z flag, and CPU interrupts... along with giving indication as to when you can drop the dex instruction.


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 21, 2018 12:00 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8521
Location: Southern California
What you can drop is the compare-to-zero instruction. It's an automatic, implied part of the following 65c02 instructions: LDA, LDX, LDY, INC, INX, INY, DEC, DEX, DEY, INA, DEA, AND, ORA, EOR, ASL, LSR, ROL, ROR, PLA, PLX, PLY, SBC, ADC, TAX, TXA, TAY, TYA, and TSX. This means that, for example, a CMP #0 after an LDA, or a CPX #0 after a DEX, is redundant, a wasted instruction. The only time a 65c02 (CMOS) needs a compare-to-zero instruction after one of these is if you want to compare a register that was not involved in the previous instruction; for example,

Code:
        DEY
        CPX  #0

(Note the Y and the X are not the same register.) If you can spare a register to which you can transfer the one you want to test, you can save a byte with the transfer instead of a compare instruction. The example above, if the contents of A don't need to be kept, could be changed to:

Code:
        DEY
        TXA

and then you can branch on the N or Z flag which tell if X was negative or zero. The TXA isn't any faster (both TXA and CPX# take two clocks); but TXA takes only one byte, whereas the CPX #0 takes two bytes.

The NMOS 6502 did have a bug in that the flags weren't always correct after a decimal-mode operation like ADC; so then you might have to follow it with the CMP #0 to get the N and Z flags right. It's best to just use the CMOS processor when possible.

(This is from the programming tips page of my 6502 primer.)

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 21, 2018 5:38 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8406
Location: Midwestern USA
load81 wrote:
Wow, you're all super helpful.

Thanks! That and five dollars, pounds, euros, etc., can get us some lunch. :D

Quote:
As for turning all of my raw memory values into constants, noted. Other than using the name of the sys call (in the case of calling a KERNAL routine) is there some sort of naming convention that an informal standard for this kind of thing? I've tried reading ASM sources of some other projects and the constant and macro names are frequently rather cryptic.

Aside from "official" names, such as those in the Commodore kernel (or "kernal," which apparently was a typo that persisted because it wasn't immediately caught and too much documentation ended up with the wrong spelling), choosing label and symbol names is very much a matter of taste. I limit my names to eight characters maximum due to old habits involving the assemblers of decades gone by. There are a couple of patterns that I routinely follow:

  • m_ is a prefix for any constant that is used as a mask. For example, m_lonyb = %00001111 would be the mask for extracting the least significant nybble from a byte.

  • n_ is a prefix for a constant that is a "number of" value. For example, n_nxchan = 4 would be the number of communication channels in an NXP quad UART.

  • s_ is a prefix for a constant that is a "size of" value. A little code will explain:

    Code:
    mmtab    .word recenter        ;creat/edit new record
             .word recdelet        ;delete record
             .word recdsply        ;display record
             .word recsrch         ;search for records
    s_mmtab  =*-mmtab              ;size of above table

    In the above, a dispatch table is constructed for the purpose of routing program execution according to a menu choice (1, 2, 3 or 4, in this example) made by the user. The label s_mmtab will be set to 8 during assembly. This value may be used by any code that processes the table. For example, you would convert the user's numeric input value into binary, subtract 1 from it and if the difference was positive (that is, carry was not cleared during the subtraction), double it for a relative index. If the index is equal to or higher than s_mmtab then the user has entered an out-of-range value.

    By allowing the assembler to compute s_mmtab for you, you can add to or delete from the table and not have to separately chase down any hard-coded reference to the size of the table.

I try to create labels and symbols that are reasonably descriptive. For example, mmtab in the above is "main menu table." A zero page pointer will have ptr somewhere in its name, e.g., gpptr1 ("general purpose pointer number 1").

Speaking of the Commodore kernel, you might find the following handy:

Code:
;===============================================================================
;
;STANDARD KERNEL ROM JUMP TABLE
;
acptr    =$ffa5                ;input byte from serial bus
basin    =$ffcf                ;input a byte, also chrin
bsout    =$ffd2                ;output a byte, aka chrout
chkin    =$ffc6                ;define an input channel
chkout   =$ffc9                ;define an output channel
chrin    =basin                ;input a byte
chrout   =bsout                ;output a byte
cint     =$ff81                ;initialize screen editor & VIC
ciout    =$ffa8                ;output byte to serial bus
clall    =$ffe7                ;close all files
close    =$ffc3                ;close a logical file
clrchn   =$ffcc                ;restore default devices
getin    =$ffe4                ;get a byte
iobase   =$fff3                ;get I/O block base address
ioinit   =$ff84                ;initialize I/O devices
listen   =$ffb1                ;command serial bus device to LISTEN
load     =$ffd5                ;load from device
membot   =$ff9c                ;read/set bottom of memory pointer
memtop   =$ff99                ;read/set top of memory pointer
open     =$ffc0                ;open a logical file
plot     =$fff0                ;read/set position of cursor on screen
ramtas   =$ff87                ;initialize RAM, tape buffer, screen
rdtim    =$ffde                ;read the software clock
readst   =$ffb7                ;read I/O status word
restor   =$ff8a                ;restore default I/O vectors
save     =$ffd8                ;save to a device
scnkey   =$ff9f                ;scan the keyboard
screen   =$ffed                ;read number of screen rows & columns
second   =$ff93                ;send SA after LISTEN
setlfs   =$ffba                ;set logical file parameters
setmsg   =$ff90                ;set kernel message control flag
setnam   =$ffbd                ;set filename parameters
settim   =$ffdb                ;set the software clock
settmo   =$ffa2                ;set IEEE time-out flag
stop     =$ffe1                ;check the STOP key
talk     =$ffb4                ;command serial bus device to TALK
tksa     =$ff96                ;send SA after TALK
udtim    =$ffea                ;update the software clock
unlsn    =$ffae                ;command serial bus device to UNLISTEN
untlk    =$ffab                ;command serial bus device to UNTALK
vector   =$ff8d                ;read/set I/O vector table
;
;================================================================================
;
;C-128 EXTENDED KERNEL JUMP TABLE
;
bootdsk  =$ff53                ;attempt boot from disk device 8
closall  =$ff4a                ;close all files on device in .A
crti     =$ff33                ;common interrupt return
dlchr    =$ff62                ;initialize VDC char RAM
dmacall  =$ff50                ;drive DMA controller
pfkey    =$ff65                ;assign string to F-key
getcfg   =$ff6b                ;translate bank to MMU pattern
go64     =$ff4d                ;switch to C-64 mode
indcmp   =$ff7a                ;compare .A to byte in remote bank
indfet   =$ff74                ;get byte from remote bank
indsta   =$ff77                ;put byte to remote bank
jmpfar   =$ff71                ;jump to routine in remote bank
jsrfar   =$ff6e                ;call subroutine in remote bank
lkupla   =$ff59                ;look up LA
lkupsa   =$ff5c                ;look up SA
phoenix  =$ff56                ;initialize ROMs & boot from disk
primm    =$ff7d                ;print following string
setbnk   =$ff68                ;set bank for load/save
spinout  =$ff47                ;set up fast serial bus
swapper  =$ff5f                ;swap active display
;
;===============================================================================
;
;KERNEL INDIRECT VECTORS
;
ikvec    =$0314                ;kernel vector base
;
cbinv    =$0316                ;process BRK
cinv     =$0314                ;process IRQ
ibasin   =$0324                ;input byte
ibsout   =$0326                ;output byte
ichkin   =$031e                ;define LA as input
ickout   =$0320                ;define LA as output
iclall   =$032c                ;close all files in kernel
iclose   =$031c                ;close file
iclrch   =$0322                ;restore default I/O paths
igetin   =$032a                ;get byte
iload    =$0330                ;load RAM from file
iopen    =$031a                ;open file
isave    =$0332                ;save RAM to file
istop    =$0328                ;poll STOP key
nminv    =$0318                ;process NMI
;
;
;   C-128-specific vector—unassigned in C-64...
;
iexmon   =$032e                ;monitor command executive
;

Garth has said on more than one occasion that my label and symbol names tend to be cryptic. :shock: That's unavoidable when limiting size to eight characters. :roll:

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 21, 2018 7:57 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10940
Location: England
load81 wrote:
Wow, you're all super helpful.

Thanks for the appreciation - much appreciated by all of us, I'm sure.

BTW, load81: if these code sequences still seem a bit magical, you'll find it very worthwhile to single step the code by hand. You need to train yourself to have a mental model of what each instruction is doing, each time around the loop. Only later will you be able to see the whole loop in one glance - and even then, it would be easy to get something wrong, so there's a huge difference between trying to understand working code and trying to debug code which isn't working.

Chromatix' idea of counting up to zero is a clever one, but I struggled to code it up for this case, so it might be a bit too clever for me this time in the morning.


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 21, 2018 11:14 am 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
For the "count up" mode, you have to remember that 6502 indexing treats the index registers as unsigned, so the base pointer correction must move it down:
Code:
void puts(char* str, uint8_t len)
{
    char* base = (str + len - 256);
    uint8_t idx = (~len) + 1;
    do {
        putc(base[idx]);
    } while(++idx);
}

For comparison, here's the C translation of my suggested code from earlier:
Code:
void puts(char* str, uint8_t len)
{
    uint8_t idx = 0;
    do {
        putc(str[idx++]);
    } while(--len);
}


Last edited by Chromatix on Wed Nov 21, 2018 12:28 pm, edited 3 times in total.

Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 21, 2018 12:18 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10940
Location: England
Yes, it seemed like you need a lot of work in the preamble, just to shave two cycles from the loop!

In my view, at an early stage of learning the 6502, it's enough to get a program which works. Saving cycles and bytes and using idiomatic sequences comes a bit later. Likewise using X and Y wisely, and zero page.


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 21, 2018 2:28 pm 
Offline

Joined: Fri Nov 16, 2018 8:55 pm
Posts: 71
BigEd wrote:
load81 wrote:
Wow, you're all super helpful.

Thanks for the appreciation - much appreciated by all of us, I'm sure.

BTW, load81: if these code sequences still seem a bit magical, you'll find it very worthwhile to single step the code by hand.


Assuming I'm working from VICE on a Linux box, how do I do that? I have an SD2IEC, Pi1541, and an EZFlash, so I can do this on real hardware if I must. Obviously, I find it faster to cross-dev from modern hardware.


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 21, 2018 2:35 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10940
Location: England
I mean truly by hand, with pencil and paper, noting the values of the registers and the flags for each instruction. It's what you'll find yourself doing in your head once you're up to speed.


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 21, 2018 8:40 pm 
Offline

Joined: Thu Jan 21, 2016 7:33 pm
Posts: 276
Location: Placerville, CA
Agreed. Using a dedicated simulator (there's a pile of them here; if you're running on Linux Sim65 is a good choice, or just use the Kowalski assembler/simulator under WINE) is the next best thing, but for really drilling information into your head nothing beats pencil and paper.


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 21, 2018 9:34 pm 
Offline

Joined: Fri Nov 16, 2018 8:55 pm
Posts: 71
Thanks for the tip. I'll practice 6502 with some graph paper and a pencil. BTW, I'm about 1/3 of the way through Machine Language for the Commodore 64 at this point. I'm just finishing up the exercises in chapter 3.


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 28, 2018 12:30 am 
Offline

Joined: Tue Nov 27, 2018 7:32 pm
Posts: 4
FWIW there's AppleWin (Windows) and LinApple. We've recently done quite a bit of work on LinApple (there's plenty more to do). Since the Apple IIe had a built in monitor and disassembler, and the enhanced Apple IIe had a built in assembler, it's an excellent way to learn 6502 without a need for frameworks or Wine...

]CALL -151
*!
!6000:LDX #$00
<type in rest of your program>
!<enter>
*6000G

Steve Wozniak put a great deal of effort into making the Apple II make the 6502 as accessible as possible. Take advantage of his work! Then, move on to the C64 or your own projects...

https://github.com/linappleii/linapple


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 27 posts ]  Go to page Previous  1, 2

All times are UTC


Who is online

Users browsing this forum: No registered users and 7 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: