Hires vertical position calculations?

11 posts / 0 new
Last post
Offline
Last seen: 4 years 3 weeks ago
Joined: Aug 4 2022 - 23:18
Posts: 2
Hires vertical position calculations?

Heya,

I have been playing for a while with 6502 assembly and namely on Apple ][. Presently I got curious about how vertical position would be calculated in hires. Yes, some uses tables and got one of my own for both hires and text/lores. However hires table is created in basic, and I am more curious about calculating it without using table. Namely calculating with assembly and that horrible 8 bit register. Google didn't give out pure math (read none at all).

Unless somewhere in memory is table used by basic's hires graphics function, in which case that table will be handy. Didn't find anything resembling such table however, but would be memory saver if such thing exist.

If you are curious what I am doing right now, it is actually trying put up two functions. Hires versions of hlin and vlin. Byte accurate versions, not pixel. I am working with something? Well, won't give out details until all required snippets are gathered... nope...

 

Offline
Last seen: 10 hours 25 min ago
Joined: Jun 18 2010 - 13:54
Posts: 891
You might want to look at the

You might want to look at the GBASCALC routine in the Monitor ROM ($F847).

 

Offline
Last seen: 4 years 3 weeks ago
Joined: Aug 4 2022 - 23:18
Posts: 2
Actually are you sure that

Actually are you sure that one is right address, coz all results seem be pointing to text memory address. Plus only PLOT seem be calling it, not HPLOT.  Kinda useless, as basic already has VLINE and HLINE for LORES, but I am working with HIRES and looking to create similar call functions with assembly. HPLOT is both far too slow and don't need pixel accurate function. Just byte accuracy for right results...

Offline
Last seen: 4 years 3 weeks ago
Joined: Sep 14 2022 - 20:02
Posts: 1
As I recall, you could

As I recall, you could transpose a few bits in the Y coordinate, mulitply by 40, and then you should have it.Except that I think there might be some skip bytes at the end of every so many lines, which complicates things. Can anyone shed some light on this?

Offline
Last seen: 1 year 1 week ago
Joined: Sep 3 2019 - 10:36
Posts: 20
Hand-rolled assemby

Back in 1989-90 I was experimenting with the 160x192x16 mode provided by the A2E AppleColor RGB card (not standard DHR).  I didn't know where the ROM calc routines for HIRES lived back then and I wrote some assembly to compute a screen memory value from an X,Y coordinate pair.  For the Y values, it's still 0-191, so the algorithm doesn't change.  There are (of course) lots of intersting patterns in the memory layout of Hires... Here's the basic gist of how I did it (which probably isn't the *best* way, mathwise, but it worked):

The low byte of the destination screen memory address starts as $00.

First, take the Y coord and divide it by 8.  This gives a value of 0-23.  If that value is odd, add $80 to the low byte of the destination screen memory address.

If Y/8 is >7, add another $28 to the low byte of the destination address.

If Y/8 is >15, add another $28 to the low byte of the destination address.

The high byte of the destination address starts as $20.

Take Y/8 Modulo 8 (0-7).  Divide by two (0-3).  Add this to the high byte of the dest address.

Take Y Modulo 8. (0-7).  Multiply by 4.  Add this to the high byte of the destination address.

You now have the memory address for the start of the row.

 

The picture of the hi-res memory layout at https://www.xtof.info/hires-graphics-apple-ii.html (which I believe was similar to what was shown in one of the books that came with the RGB card) helps visualize what's going on with the math.

 

I can provide my source code if you wish, but working through it to reverse engineer the algorithm I see that there are several things that could be improved that I did the hard way (I was 13/14 years old at the time!)... and to be quite honest, a table-based lookup is going to be much, much faster and simpler than trying to do all the math. 

 

 

 

 

mmphosis's picture
Offline
Last seen: 1 week 2 days ago
Joined: Aug 18 2005 - 16:26
Posts: 470
hires address

REM HOW TO CALCULATE THE HIRES ADDRESS

REM GIVEN THE COORDINATE OF A PIXEL

https://mmphosis.netlify.app/hires/

 

 

mjmahon's picture
Offline
Last seen: 2 years 1 month ago
Joined: Sep 7 2005 - 15:21
Posts: 7
Hires base address calculation

The routine you're looking for is in Applesoft at $F411, called HPOSN. 

FlyingZebra's picture
Offline
Last seen: 18 hours 23 min ago
Joined: May 5 2026 - 16:09
Posts: 28
HPOSN alternative
  • In 1982, I was blessed with a copy of a book called "Apple Graphics & Arcade Game Design".
  • In my view Jeffrey Stanton explained as the best how to find a HPOSN much quicker that Woz' ROM routine.  Basically this routine was used in many Apple II games in that era. 

 

10 HGR

20 HEX$ = "0123456789ABCDEF" : J = 0

30 FOR I = 0 TO 191

40 POKE 29,I : POKE 30,J : CALL 24576 : REM FETCH HGR ADDRESS

50 POKE PEEK(27)*256+PEEK(26),255 : REM WRITE AT HGR ADDRESS

60 CALL 24576+18 : REM PRINT ADDRESS + SPC

REM 70 V = PEEK(26):GOSUB 100: VH$=V$: V = PEEK(27):GOSUB 100:VL$=V$

REM 80 PRINT VL$+VH$+" "

90 NEXT I: END

100 VH = INT(V/16)

110 VL = INT(((V / 16)-VH)*16)

120 V$ = MID$(HEX$,VH+1,1) + MID$(HEX$,VL+1,1)

130 RETURN

 

 

CALL-151

 

6000: A4 1D B9 1F 60 18 65 1E

6008: 85 1A B9 DF 60 85 1B A0

6010: 00 60 A6 1A A4 1B 20 40

6018: F9 A9 A0 20 F0 FD 60 00

6020: 00 00 00 00 00 00 00 80

6028: 80 80 80 80 80 80 80 00

6030: 00 00 00 00 00 00 00 80

6038: 80 80 80 80 80 80 80 00

6040: 00 00 00 00 00 00 00 80

6048: 80 80 80 80 80 80 80 00

6050: 00 00 00 00 00 00 00 80

6058: 80 80 80 80 80 80 80 28

6060: 28 28 28 28 28 28 28 A8

6068: A8 A8 A8 A8 A8 A8 A8 28

6070: 28 28 28 28 28 28 28 A8

6078: A8 A8 A8 A8 A8 A8 A8 28

6080: 28 28 28 28 28 28 28 A8

6088: A8 A8 A8 A8 A8 A8 A8 28

6090: 28 28 28 28 28 28 28 A8

6098: A8 A8 A8 A8 A8 A8 A8 50

60A0: 50 50 50 50 50 50 50 D0

60A8: D0 D0 D0 D0 D0 D0 D0 50

60B0: 50 50 50 50 50 50 50 D0

60B8: D0 D0 D0 D0 D0 D0 D0 50

60C0: 50 50 50 50 50 50 50 D0

60C8: D0 D0 D0 D0 D0 D0 D0 50

60D0: 50 50 50 50 50 50 50 D0

60D8: D0 D0 D0 D0 D0 D0 D0 20

60E0: 24 28 2C 30 34 38 3C 20

60E8: 24 28 2C 30 34 38 3C 21

60F0: 25 29 2D 31 35 39 3D 21

60F8: 25 29 2D 31 35 39 3D 22

6100: 26 2A 2E 32 36 3A 3E 22

6108: 26 2A 2E 32 36 3A 3E 23

6110: 27 2B 2F 33 37 3B 3F 23

6118: 27 2B 2F 33 37 3B 3F 20

6120: 24 28 2C 30 34 38 3C 20

6128: 24 28 2C 30 34 38 3C 21

6130: 25 29 2D 31 35 39 3D 21

6138: 25 29 2D 31 35 39 3D 22

6140: 26 2A 2E 32 36 3A 3E 22

6148: 26 2A 2E 32 36 3A 3E 23

6150: 27 2B 2F 33 37 3B 3F 23

6158: 27 2B 2F 33 37 3B 3F 20

6160: 24 28 2C 30 34 38 3C 20

6168: 24 28 2C 30 34 38 3C 21

6170: 25 29 2D 31 35 39 3D 21

6178: 25 29 2D 31 35 39 3D 22

6180: 26 2A 2E 32 36 3A 3E 22

6188: 26 2A 2E 32 36 3A 3E 23

6190: 27 2B 2F 33 37 3B 3F 23

6198: 27 2B 2F 33 37 3B 3F

 

E003G

RUN

 

 

 

------------

 

; ASSEMBLY CODE FOR HGR POSITION

*=$6000

 

HIRESL = $1A

HIRESH = $1B

TVERT = $1D

HORIZ = $1E

 

START LDY *TVERT ; Y ORDINATE

GETADR LDA YVERTL,Y

CLC

ADC *HORIZ

STA *HIRESL

LDA YVERTH,Y

STA *HIRESH

LDY #$00

RTS

 

PRTADR LDX *HIRESL

LDY *HIRESH

JSR $F940 ; PRINT RESULT ADDRESS

LDA #$A0

JSR $FDF0 ; PRINT SPC

;JSR $FD67 ; PRINT LF

RTS

 

YVERTL HEX 0000000000000000 ;L57

HEX 8080808080808080 ;L58

HEX 0000000000000000 ;L59

HEX 8080808080808080 ;L60

HEX 0000000000000000 ;L61

HEX 8080808080808080 ;L62

HEX 0000000000000000 ;L63

HEX 8080808080808080 ;L64

HEX 2828282828282828 ;L65

HEX A8A8A8A8A8A8A8A8 ;L66

HEX 2828282828282828 ;L67

HEX A8A8A8A8A8A8A8A8 ;L68

HEX 2828282828282828 ;L69

HEX A8A8A8A8A8A8A8A8 ;L70

HEX 2828282828282828 ;L71

HEX A8A8A8A8A8A8A8A8 ;L72

HEX 5050505050505050 ;L73

HEX D0D0D0D0D0D0D0D0 ;L74

HEX 5050505050505050 ;L75

HEX D0D0D0D0D0D0D0D0 ;L76

HEX 5050505050505050 ;L77

HEX D0D0D0D0D0D0D0D0 ;L78

HEX 5050505050505050 ;L79

HEX D0D0D0D0D0D0D0D0 ;L80

 

YVERTH HEX 2024282C3034383C ;L82

HEX 2024282C3034383C ;L83

HEX 2125292D3135393D ;L84

HEX 2125292D3135393D ;L85

HEX 22262A2E32363A3E ;L86

HEX 22262A2E32363A3E ;L87

HEX 23272B2F33373B3F ;L88

HEX 23272B2F33373B3F ;L89

HEX 2024282C3034383C ;L90

HEX 2024282C3034383C ;L91

HEX 2125292D3135393D ;L92

HEX 2125292D3135393D ;L93

HEX 22262A2E32363A3E ;L94

HEX 22262A2E32363A3E ;L95

HEX 23272B2F33373B3F ;L96

HEX 23272B2F33373B3F ;L97

HEX 2024282C3034383C ;L98

HEX 2024282C3034383C ;L99

HEX 2125292D3135393D ;L100

HEX 2125292D3135393D ;L101

HEX 22262A2E32363A3E ;L102

HEX 22262A2E32363A3E ;L103

HEX 23272B2F33373B3F ;L104

HEX 23272B2F33373B3F ;L105

Offline
Last seen: 17 hours 13 min ago
Joined: Apr 1 2020 - 16:46
Posts: 1393
A comment on the previous post

I think the brute-force table lookup of the previous post may be the fastest way to get the HIRES line begin address much quicker than Woz' method, but IMHO the table is waste of memory and the whole scheme is useless for software sprites, because these would be too slow even with the lookup table method, which would need to be run for each line of the sprite while the sprite is being rendered on the screen.

 

A much faster (and even more memory gobbling) way is to code a 'sprite slide' like this:

 

    LDA (SPNT),Y

    ORA LINEAD,X

    STA LINEAD,X

    INY

 

    .... repeat this code for 192 times. Prior to calling into this slide, set SPNT to the begin of the sprite bit map, X to the byte index (0....39), Y to zero,  and calculate the entry and exit address into that slide, depending on the y coordinate. The exit address would be the INY instruction after the last line of the sprite bit map. Use evil self-modifying code to replace this INY with a RTS. Then do a computed JSR to the entry address which is the LDA (SPNT),Y of the first y-coordinate (the scn line number) where to put the first line of the sprite (Y  = 0 for this first line).

 

Of course the actual bit shuffling which is just a "ORA" can be more sophisticated, i.e. using another pointer to a mask, but this costs time and slows the sprite engine down. I think that sprites can be designed and placed such that if the same type of sprite does overlap, it still will have the right colors. So no mask is needed. If you want a mask, the code snippet above must be changed to:

 

  LDA LINEAD,X

  AND (MPNT),Y

  ORA (SPNT),Y

  STA LINEAD,X

  INY

 

where MPNT is the pointer to the sprite mask.

 

I'm currently into this topic because I code the driver software for my Apple-1 Color Graphics card, and sought the fastest possible software sprite rendering code which still is universal enough. The code for the sprite slide is generated automatically and every block has the right LINEAD which is calculated once during auto code generation and so it does not need any speedup tricks like the table lookup method of the previous post.

 

Comments invited !

(Oh, and if you know of some open source code to turn a sprite definition into all the slices for all possible x-coordinates, please show me where it is to be found. I could write that stuff myself but why reinvent the wheel ;-)

 

- Uncle Bernie

Offline
Last seen: 9 hours 6 min ago
Joined: Feb 27 2021 - 18:59
Posts: 927
Up the Duff

Something about the "slide" reminds me of Duff's Device, which of course is just a way to write such a low-level structure in a high-level language.

Online
Last seen: 31 min 5 sec ago
Joined: May 16 2021 - 08:07
Posts: 68
ref, formula, hybrid method

One interesting reference about all this is at https://github.com/AppleWin/AppleWin/wiki.

There they mention a better routine, not in ROM, produced later by Woz himself.

 

Sometimes you would like a simple one line formula whether fast or not, in Integer BASIC it can be written

ADDR = 8192*P + YMOD8*1024 + YMOD64/8*128 + Y/64*40

where P is the page, either 1 or 2.  Another way to write the third term is Y/8MOD8*128

It can be done similarly in Applesoft but there will be some ugliness with INT to handle lack of MOD and truncate FP division.

 

If you are just updating 8-row tiles then a hybrid scheme works well where you tabulate only those lines with YMOD8=0.  Then the in-between lines are filled in with a simple ADC #$400. This is a compromise that is reasonably fast and doesn't require the full table.

I have tried using self-modifying code to squeeze a little more out of the loops.  The idea is that direct beats indirect, so instead of creating a pointer, modify a direct address somewhere during an init phase: maybe you gain something. Of course doing away with the row loop altogether as in UB's post above is even better, if you can spare the memory.

 

 

Log in or register to post comments