Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.sys.apple2.programmer > #4656 > unrolled thread

Lookup Table ca65

Started byMasood <masood@masoodkamandy.com>
First post2018-07-29 23:08 -0700
Last post2018-08-02 15:06 -0700
Articles 7 — 4 participants

Back to article view | Back to comp.sys.apple2.programmer


Contents

  Lookup Table ca65 Masood <masood@masoodkamandy.com> - 2018-07-29 23:08 -0700
    Re: Lookup Table ca65 MG <*@guidero.us> - 2018-07-30 07:20 -0700
      Re: Lookup Table ca65 Masood <masood@masoodkamandy.com> - 2018-07-30 08:35 -0700
        Re: Lookup Table ca65 David Schmidt <schmidtd@my-deja.com> - 2018-07-30 13:21 -0400
          Re: Lookup Table ca65 Masood <masood@masoodkamandy.com> - 2018-08-01 16:38 -0700
            Re: Lookup Table ca65 Michael J. Mahon <mjmahon@aol.com> - 2018-08-01 18:47 -0500
    Re: Lookup Table ca65 MG <*@guidero.us> - 2018-08-02 15:06 -0700

#4656 — Lookup Table ca65

FromMasood <masood@masoodkamandy.com>
Date2018-07-29 23:08 -0700
SubjectLookup Table ca65
Message-ID<c5c71c33-b4f8-4917-87bb-ee0b29e8d2e4@googlegroups.com>
Hello,

I am learning 6502 assembly for my Apple IIc Plus, using Xcode, ca65, and Quinn Dunki/Jeremy Rand's Apple2BuildPipeline:
https://github.com/jeremysrand/Apple2BuildPipeline/

Working with AppleSoft routines has been fun, but I'd like to move into creating my own plot routine in assembly. I'd like to create a lookup routine that calculates the base address of a pixel in memory.

I've found this lookup table on the AppleWin Wiki, which I'd like to repurpose for use in ca65:
https://github.com/AppleWin/AppleWin/wiki

It was designed for assemblers that do not create relocatable code.

This is where I've hit a wall. I don't understand how to translate the ORG directive for use with ca65 by creating a .segment. I did find this link, which explains it, but I can't seem to understand how to make it work:
https://github.com/cc65/wiki/wiki/The-.ORG-directive

I think problem is my lack of understanding of how a linker works, and how I would manipulate the config file.

Does anyone have any resources, best practices, or sample code that might explain what a linker is and how a lookup table can be created in ca65?

Thank you.

Masood

[toc] | [next] | [standalone]


#4657

FromMG <*@guidero.us>
Date2018-07-30 07:20 -0700
Message-ID<a8789e60-2c67-47a4-b69c-27939b57a206@googlegroups.com>
In reply to#4656
On Sunday, July 29, 2018 at 11:08:31 PM UTC-7, Masood wrote:
> Hello,
> 
> I am learning 6502 assembly for my Apple IIc Plus, using Xcode, ca65, and Quinn Dunki/Jeremy Rand's Apple2BuildPipeline:
> https://github.com/jeremysrand/Apple2BuildPipeline/
> 
> Working with AppleSoft routines has been fun, but I'd like to move into creating my own plot routine in assembly. I'd like to create a lookup routine that calculates the base address of a pixel in memory.
> 
> I've found this lookup table on the AppleWin Wiki, which I'd like to repurpose for use in ca65:
> https://github.com/AppleWin/AppleWin/wiki
> 
> It was designed for assemblers that do not create relocatable code.
> 
> This is where I've hit a wall. I don't understand how to translate the ORG directive for use with ca65 by creating a .segment. I did find this link, which explains it, but I can't seem to understand how to make it work:
> https://github.com/cc65/wiki/wiki/The-.ORG-directive
> 
> I think problem is my lack of understanding of how a linker works, and how I would manipulate the config file.
> 
> Does anyone have any resources, best practices, or sample code that might explain what a linker is and how a lookup table can be created in ca65?
> 
> Thank you.
> 
> Masood

There is nothing stopping you from doing it the "traditional" way in ca65 with absolute code.  That being said, the "ca65" way is something like this:

------ linker configuration

MEMORY {
  BIN: start = $900, size = $1700;
}

SEGMENTS {
  code: load = BIN;
  y2hgrL: load = BIN, define = yes, align = $100;
  y2hgrH: load = BIN, define = yes, align = $100;
}

------ code

GBASL   = $26
GBASH   = $27
HPAG    = $E6        ; Used by Applesoft, 20 HGR1, 40 HGR2

.import  __y2hgrL_LOAD__
.import  __y2hgrH_LOAD__
.segment "code"

                     ; Table Y to HGR address
                     ; INPUT: Y = row (0 .. 191)
Y2HGRADR:
         LDA   __y2hgrL_LOAD__,Y
         STA   GBASL
         LDA   __y2hgrH_LOAD__,Y
         ORA   HPAG       ; #$20 for HGR1, #$40 for HGR2
         STA   GBASH
         RTS

.segment "y2hgrL"
.align $100
        .byte $00,$00,$00,$00,$00,$00,$00,$00
        .byte $80,$80,$80,$80,$80,$80,$80,$80
        .byte $00,$00,$00,$00,$00,$00,$00,$00
        .byte $80,$80,$80,$80,$80,$80,$80,$80
        .byte $00,$00,$00,$00,$00,$00,$00,$00
        .byte $80,$80,$80,$80,$80,$80,$80,$80
        .byte $00,$00,$00,$00,$00,$00,$00,$00
        .byte $80,$80,$80,$80,$80,$80,$80,$80
        .byte $28,$28,$28,$28,$28,$28,$28,$28
        .byte $A8,$A8,$A8,$A8,$A8,$A8,$A8,$A8
        .byte $28,$28,$28,$28,$28,$28,$28,$28
        .byte $A8,$A8,$A8,$A8,$A8,$A8,$A8,$A8
        .byte $28,$28,$28,$28,$28,$28,$28,$28
        .byte $A8,$A8,$A8,$A8,$A8,$A8,$A8,$A8
        .byte $28,$28,$28,$28,$28,$28,$28,$28
        .byte $A8,$A8,$A8,$A8,$A8,$A8,$A8,$A8
        .byte $50,$50,$50,$50,$50,$50,$50,$50
        .byte $D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0
        .byte $50,$50,$50,$50,$50,$50,$50,$50
        .byte $D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0
        .byte $50,$50,$50,$50,$50,$50,$50,$50
        .byte $D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0
        .byte $50,$50,$50,$50,$50,$50,$50,$50
        .byte $D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0

.segment "y2hgrH"
.align $100
        .byte $00,$04,$08,$0C,$10,$14,$18,$1C
        .byte $00,$04,$08,$0C,$10,$14,$18,$1C
        .byte $01,$05,$09,$0D,$11,$15,$19,$1D
        .byte $01,$05,$09,$0D,$11,$15,$19,$1D
        .byte $02,$06,$0A,$0E,$12,$16,$1A,$1E
        .byte $02,$06,$0A,$0E,$12,$16,$1A,$1E
        .byte $03,$07,$0B,$0F,$13,$17,$1B,$1F
        .byte $03,$07,$0B,$0F,$13,$17,$1B,$1F
        .byte $00,$04,$08,$0C,$10,$14,$18,$1C
        .byte $00,$04,$08,$0C,$10,$14,$18,$1C
        .byte $01,$05,$09,$0D,$11,$15,$19,$1D
        .byte $01,$05,$09,$0D,$11,$15,$19,$1D
        .byte $02,$06,$0A,$0E,$12,$16,$1A,$1E
        .byte $02,$06,$0A,$0E,$12,$16,$1A,$1E
        .byte $03,$07,$0B,$0F,$13,$17,$1B,$1F
        .byte $03,$07,$0B,$0F,$13,$17,$1B,$1F
        .byte $00,$04,$08,$0C,$10,$14,$18,$1C
        .byte $00,$04,$08,$0C,$10,$14,$18,$1C
        .byte $01,$05,$09,$0D,$11,$15,$19,$1D
        .byte $01,$05,$09,$0D,$11,$15,$19,$1D
        .byte $02,$06,$0A,$0E,$12,$16,$1A,$1E
        .byte $02,$06,$0A,$0E,$12,$16,$1A,$1E
        .byte $03,$07,$0B,$0F,$13,$17,$1B,$1F
        .byte $03,$07,$0B,$0F,$13,$17,$1B,$1F


MG

[toc] | [prev] | [next] | [standalone]


#4658

FromMasood <masood@masoodkamandy.com>
Date2018-07-30 08:35 -0700
Message-ID<c0db3d71-ac24-400d-a7d1-c7f78abfefc7@googlegroups.com>
In reply to#4657
Thank you so much.

A lot here I don't quite understand yet, but it's an awesome starting point for me to dig into.

M

[toc] | [prev] | [next] | [standalone]


#4659

FromDavid Schmidt <schmidtd@my-deja.com>
Date2018-07-30 13:21 -0400
Message-ID<pjnhee$pkc$1@dont-email.me>
In reply to#4658
On 7/30/2018 11:35 AM, Masood wrote:
> Thank you so much.
> 
> A lot here I don't quite understand yet, but it's an awesome starting point for me to dig into.

The thing to keep in mind with ca65 is that you can live your life 
without ever ORGing anything... you have a configuration or set of 
configurations that place the relocatable code where the config specifies.

Personally, though, I often sprinkle in an ORG so that assembly listings 
are grounded in real locations instead of variable offsets.

[toc] | [prev] | [next] | [standalone]


#4660

FromMasood <masood@masoodkamandy.com>
Date2018-08-01 16:38 -0700
Message-ID<67c316af-8770-4051-9629-dd2f9f0e267c@googlegroups.com>
In reply to#4659
One more related question:

For the sake of simplicity could I also just use labels in ca65 and relative addressing within the the "RODATA" segment, rather than creating my own new segments of memory at specific locations and modifying the config file for every project I do?

This seems easier. Are there drawbacks to doing it that way?

Thanks.

M

[toc] | [prev] | [next] | [standalone]


#4661

FromMichael J. Mahon <mjmahon@aol.com>
Date2018-08-01 18:47 -0500
Message-ID<3sednX2Z0e0Q2__GnZ2dnUU7-bPNnZ2d@giganews.com>
In reply to#4660
Masood <masood@masoodkamandy.com> wrote:
> One more related question:
> 
> For the sake of simplicity could I also just use labels in ca65 and
> relative addressing within the the "RODATA" segment, rather than creating
> my own new segments of memory at specific locations and modifying the
> config file for every project I do?
> 
> This seems easier. Are there drawbacks to doing it that way?
> 
> Thanks.
> 
> M
> 

If the tables are not page-aligned, you’ll take an extra cycle when
indexing causes a page crossing. 

-- 
-michael - NadaNet 3.1 and AppleCrate II:  http://michaeljmahon.com

[toc] | [prev] | [next] | [standalone]


#4665

FromMG <*@guidero.us>
Date2018-08-02 15:06 -0700
Message-ID<a9bc5592-7f8d-4555-9cd6-1476c0149633@googlegroups.com>
In reply to#4656
On Sunday, July 29, 2018 at 11:08:31 PM UTC-7, Masood wrote: 
> I think problem is my lack of understanding of how a linker works, and how I would manipulate the config file.
> 
> Does anyone have any resources, best practices, or sample code that might explain what a linker is and how a lookup table can be created in ca65?

I wanted to follow up on this part for you.

Traditional assemblers running on small 8-bit system would typically provide a few directives for code placement: the ORG directive, which as you know defines the "run" address of the assembled code, and the OBJ directive, DSK directive, or other directives to provide for the storage of the code.

At some point, especially in 16- and 32-bit systems, programs got so large that assembling them from single source files became problematic, as the source code is much larger than any given object code.  Early directives for handling this supported "including" another file into the current assembly location.

Eventually, the need for reusable pre-assembled code modules arose, because re-assembling frequently-used code is time-consuming.  Initially these were assembled for a specific location and loaded into that location at runtime, so the non-shared code only needed to know the fixed entry points.

However, this resulted in inflexibility and so the relocatable object module was developed.  This is an intermediate assembled format that includes symbol information and code that is assembled effectively with a ORG address of 0.   This is where the linker comes in.

In the case of the Apple II assemblers that use a linker (either separate or integrated), modules will export symbols via a directive, and modules wanting to use other modules will import the same symbols using another directive.   The exported and imported symbols are written into the intermediate relocatable object module.

The linker's has two main jobs that it does to produce an executable output:  (1) it places code in its final destination, and (2) it matches up all the imported symbols to exported symbols and computes the final addresses for all references to them.

In the case of ca65, the MPW and APW assemblers, and others, the assembler will convert any expressions in the assembly to a "calculator" format (often stack-based) in the object file so that things like LDA SOMELABEL+5 can be computed at linking time, rather than at assembly time.

ca65+ld65 (and some other assemblers+linker combinations) additionally support a mechanism for defining uninitialized vs initialized data modules, and defining code that loads in one place, but runs in another place.   In ca65+ld65, this is where segments come in.

Segments are a tool that, along with a linker configuration file, allow finer control of the code placement and let you do things like define whether a segment is in ROM, RAM, data, etc., and optionally defines symbols that can be imported to locate where the linker placed stuff and where it should go.  This gets important in a few scenarios: in the case of code that goes into ROM but must be copied to RAM and run, and where you have a "single file" program load that must move chunks of itself around in order to meet some constraint.

In that latter case, say you are working on an Apple II game that runs under ProDOS as a SYS file that uses the hi-res graphics page.  Well, ProDOS loads your game at $2000, but you you can't just leave it there because that also happens to be where HGR page 1 lives.  You could just put a jump in the beginning and maybe have a raw splash screen or something, followed by the code, but that's going to waste disk space.  So you take advantage of the linker telling you where it put the code for load and where it put the code for execution, and write a small routine to move everything to where it's supposed to run.

Here's a linker config from one of my projects:

MEMORY {
	LOAD1: start = $2000, size = $3F00;
	RUN1: start = $8400, size = $3A00;
	GLOBAL1: start = $BE00, size = $100;
}

SEGMENTS {
	loader: load = LOAD1, align = $100;
	global: load = LOAD1, run = GLOBAL1, define = yes, align = $100;
	interp: load = LOAD1, run = RUN1, define = yes, align = $100;
	code:	load = LOAD1;
}

And here is the code that moves everything to where it ultimately belongs, which is in the loader segment:

LDR_MOVE_INTERP
      lda   #<__interp_LOAD__
      ldy   #>__interp_LOAD__
      sta   ZP_A1
      sty   ZP_A1+1
      lda   #<(__interp_LOAD__+__interp_SIZE__)
      ldy   #>(__interp_LOAD__+__interp_SIZE__)
      sta   ZP_A2
      sty   ZP_A2+1
      lda   #<__interp_RUN__
      ldy   #>__interp_RUN__
      sta   ZP_A4
      sty   ZP_A4+1
      ldy   #$00              ; required to move properly
      jsr   F8_MOVE           ; firmware move routine
      rts
; move globals, one page
LDR_MOVE_GLOBALS
      ldx   #$00
:     lda   __global_LOAD__,x
      sta   __global_RUN__,x
      inx
      bne   :-
      rts

As you can see, I don't need to worry much about where the linker places the various segments, or even the size of them (though I know the globals are $100 bytes).  The loader code's run location is the same as its load location since it is not otherwise specified.  

I hope that helps.

> For the sake of simplicity could I also just use labels in ca65 and relative addressing within the the "RODATA" segment, rather than creating my own new segments of memory at specific locations and modifying the config file for every project I do? 

If you are using ca65/ld65 to their full abilities, each project will have its own configuration file.  ld65 supplies some generic ones, but those are generally only useful in simple cases.  A more complicated project isn't going to be able to make use of them.

> This seems easier. Are there drawbacks to doing it that way? 

As Michael already mentioned, you really want to have your tables aligned for best performance.  You can have them in one segment, and just fill the gaps with something else, and let the linker figure out the addresses for you.  In that case you can change the tables to be something like what's below, and use the y2hgrL/H symbols directly, letting the linker sort them out.

Even better, you can separate them out into a separate file, add appropriate .export/.import y2hgrL and y2hgrH, and link it as an additional object, and the linker will sort it all out.

.segment "gBaseTable" 
.align $100 
y2hgrL: .byte $00,$00,$00,$00,$00,$00,$00,$00 
        .byte $80,$80,$80,$80,$80,$80,$80,$80 
        .byte $00,$00,$00,$00,$00,$00,$00,$00 
        .byte $80,$80,$80,$80,$80,$80,$80,$80 
        .byte $00,$00,$00,$00,$00,$00,$00,$00 
        .byte $80,$80,$80,$80,$80,$80,$80,$80 
        .byte $00,$00,$00,$00,$00,$00,$00,$00 
        .byte $80,$80,$80,$80,$80,$80,$80,$80 
        .byte $28,$28,$28,$28,$28,$28,$28,$28 
        .byte $A8,$A8,$A8,$A8,$A8,$A8,$A8,$A8 
        .byte $28,$28,$28,$28,$28,$28,$28,$28 
        .byte $A8,$A8,$A8,$A8,$A8,$A8,$A8,$A8 
        .byte $28,$28,$28,$28,$28,$28,$28,$28 
        .byte $A8,$A8,$A8,$A8,$A8,$A8,$A8,$A8 
        .byte $28,$28,$28,$28,$28,$28,$28,$28 
        .byte $A8,$A8,$A8,$A8,$A8,$A8,$A8,$A8 
        .byte $50,$50,$50,$50,$50,$50,$50,$50 
        .byte $D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0 
        .byte $50,$50,$50,$50,$50,$50,$50,$50 
        .byte $D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0 
        .byte $50,$50,$50,$50,$50,$50,$50,$50 
        .byte $D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0 
        .byte $50,$50,$50,$50,$50,$50,$50,$50 
        .byte $D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0 
        .res 64                               ; skip to next page alignment
y2hgrH: .byte $00,$04,$08,$0C,$10,$14,$18,$1C 
        .byte $00,$04,$08,$0C,$10,$14,$18,$1C 
        .byte $01,$05,$09,$0D,$11,$15,$19,$1D 
        .byte $01,$05,$09,$0D,$11,$15,$19,$1D 
        .byte $02,$06,$0A,$0E,$12,$16,$1A,$1E 
        .byte $02,$06,$0A,$0E,$12,$16,$1A,$1E 
        .byte $03,$07,$0B,$0F,$13,$17,$1B,$1F 
        .byte $03,$07,$0B,$0F,$13,$17,$1B,$1F 
        .byte $00,$04,$08,$0C,$10,$14,$18,$1C 
        .byte $00,$04,$08,$0C,$10,$14,$18,$1C 
        .byte $01,$05,$09,$0D,$11,$15,$19,$1D 
        .byte $01,$05,$09,$0D,$11,$15,$19,$1D 
        .byte $02,$06,$0A,$0E,$12,$16,$1A,$1E 
        .byte $02,$06,$0A,$0E,$12,$16,$1A,$1E 
        .byte $03,$07,$0B,$0F,$13,$17,$1B,$1F 
        .byte $03,$07,$0B,$0F,$13,$17,$1B,$1F 
        .byte $00,$04,$08,$0C,$10,$14,$18,$1C 
        .byte $00,$04,$08,$0C,$10,$14,$18,$1C 
        .byte $01,$05,$09,$0D,$11,$15,$19,$1D 
        .byte $01,$05,$09,$0D,$11,$15,$19,$1D 
        .byte $02,$06,$0A,$0E,$12,$16,$1A,$1E 
        .byte $02,$06,$0A,$0E,$12,$16,$1A,$1E 
        .byte $03,$07,$0B,$0F,$13,$17,$1B,$1F 
        .byte $03,$07,$0B,$0F,$13,$17,$1B,$1F 

MG

[toc] | [prev] | [standalone]


Back to top | Article view | comp.sys.apple2.programmer


csiph-web