[HN Gopher] Dirty tricks 6502 programmers use (2019)
       ___________________________________________________________________
        
       Dirty tricks 6502 programmers use (2019)
        
       Author : amichail
       Score  : 71 points
       Date   : 2025-04-16 13:58 UTC (3 hours ago)
        
 (HTM) web link (nurpax.github.io)
 (TXT) w3m dump (nurpax.github.io)
        
       | pvg wrote:
       | Thread some years ago
       | https://news.ycombinator.com/item?id=20732867
        
         | vidarh wrote:
         | Always fun to see what we wrote years ago...
        
       | dhosek wrote:
       | I remember the annoyance a lot of people had with the non-
       | sequential layout of text/graphics memory on the Apple ][ (thanks
       | to Woz's clever hacks to reduce chip count), but when writing
       | assembly code to access screen locations, it turned out that it
       | was actually easier to deal with the somewhat weird arrangement
       | of bytes than it would have if everything were sequentially
       | arranged in memory. Those little 8-byte gaps every three (non-
       | consecutive) rows made calculating row starts much simpler.
        
         | devmor wrote:
         | I've run into a similar effect when reverse engineering custom
         | http packet protocols - the ones that have a unique pattern to
         | the data structure are often easier to discern the usefulness
         | of at a glance before even extracting the data I'm looking for!
        
         | univacky wrote:
         | When Jordan Mechner wrote Karateka for the Apple ][, he used an
         | array of pointers to rows. A team member realized that by
         | inverting the order of the array, all graphics would appear
         | upside down. Broderbund agreed to ship that "upside down"
         | version on the backside of the single-sided floppy, so that if
         | you booted it upside down it played upside down.
         | 
         | https://www.theverge.com/2021/7/5/22564151/karateka-apple-ii...
         | 
         | https://archive.org/details/wozaday_Karateka_Side_B
        
         | deater wrote:
         | haha as someone who has spent a lot of time recently doing
         | Apple II graphics coding, both for games, sizecoding, and the
         | demoscene, let me tell you that the weird layout in fact is not
         | easier to deal with.
         | 
         | You have to waste a lot of space on lookup tables, or else
         | complex calculations. And don't get me started on the "screen
         | holes" you aren't allowed to write to in the lo-res address
         | space making it exciting if you're trying to use modern
         | decompression routines to unpack graphics in-place
        
         | flohofwoe wrote:
         | The best 8-bitter video memory layout (for pixel data) I have
         | seen is in the little known KC85/4:
         | 
         | The display is 320x256 pixels, organized into 40x256 bytes for
         | pixels (8 pixels per byte) and another 40x256 bytes for Speccy-
         | like color attribute bytes (the color blocks are just 8x1
         | instead of 8x8 pixels), the start address for video memory is
         | 0x8000 with the pixels and colors in different video bank.
         | 
         | Now the twist: the video memory layout is vertical, e.g.
         | writing consecutive bytes in video memory fills vertical pixel
         | columns.
         | 
         | This layout is perfect for the Z80 with its 16-bit register
         | pairs. To 'compute' a video memory location:
         | LD H, 0x80 + column    ; column = 0..39         LD L, row
         | ; row = 0..255
         | 
         | ...and now you have the address of a pixel- or color-byte in
         | HL.
         | 
         | To blit an 8x8 character just load the start of the font pixels
         | into DE and do 8x unrolled LDI.
         | 
         | Unfortunately the KC85/4 had a slow CPU clock (at 1.77 MHz only
         | half as fast as a Speccy), but it's good enough for stuff like
         | this:
         | 
         | https://floooh.github.io/kcide-sample/kc854.html?file=demo.k...
        
       | p0w3n3d wrote:
       | Quite surprising for me as a long time Atari 65XE user is that
       | those PRG were starting with a basic command. On Atari all binary
       | programs were loaded without BASIC in memory. If you forgot to
       | disable basic, there was a chance the program wouldn't run or
       | would hang. I guess this must have been due to different memory
       | layout?
        
         | bluGill wrote:
         | C64 always had basic built in. Because it was there you could
         | assume it, and further it being there affected how the system
         | booted. I'm was never a C64 guy so don't know what the options
         | were for someone who didn't want basic.
         | 
         | The original Atari didn't have built in basic (the 2nd
         | generation XL and your 3rd generation XE series both did). As
         | such Atari programmers could never assume basic, and even when
         | you could assume basic you couldn't assume the version, there
         | were a few bugs in various versions. (I knew someone with a XL
         | who used the earlier version of basic because the bugs in the
         | XL version were serious enough to affect his usage).
        
           | vardump wrote:
           | Unless you use only cartridges, BASIC ROM is required to load
           | anything at all from disk or tape.
           | 
           | Once your program loads you can turn the BASIC ROM off to see
           | the RAM underneath.
           | 
           | Commodore MAX Machine was basically a stripped down C64. It
           | had just 2 kB RAM and no ROMs at all.
           | 
           | https://www.c64-wiki.com/wiki/Commodore_MAX_Machine
        
         | dspillett wrote:
         | IIRC the C64, like the BBC range of 6502-based Micros, had
         | their BASIC in ROM and in fact booted to it in REPL mode by
         | default. As such it was always in memory as the ROM would
         | always be there1. There were certain bits of the address space
         | that were not safe to use if the user would drop back into
         | BASIC as the ROM used those for its stack & heap and other
         | scratch space, but otherwise you could ignore BASIC's existence
         | once your machine code was running.
         | 
         | ----
         | 
         | [1] caveat: under usual circumstances, on the Beebs you could
         | switch a different bank into the memory space used by the BASIC
         | ROM, either another ROM or some "sideways RAM"
        
           | Luc wrote:
           | You can do the same bank switching on a C64. Through
           | different bits of RAM location $0001 you can switch off the
           | BASIC, KERNAL and character ROMs, exposing the RAM in those
           | locations.
        
           | gabrielsroka wrote:
           | The C64 has a BASIC ROM and a kernal ROM. You can swap out
           | the BASIC ROM or both ROMs for RAM using addresses 0 and 1 on
           | the 6510.
        
         | RiverCrochet wrote:
         | The C64 always booted to BASIC, specifically CBM BASIC 2.0.
         | There wasn't a provision for automatic booting from disk until
         | at least the C128.
         | 
         | LOAD "*",8,1 was the command to load the first file off of your
         | attached 1541 (if you were lucky enough to have multiple 1541s,
         | your first one would be device 8 and you'd have had to set the
         | device number on others to 9 or higher). Anyone who had and
         | played a lot of games on the C64 back in the day has this
         | command etched in their permanent memory.
         | 
         | There was the convenient-looking RUN/STOP key (yes it is
         | confusing, it's STOP without SHIFT, and RUN with SHIFT held
         | down) but the RUN key would only auto-load from device 1 which
         | was the cassette. Made sense in 1982 when the machine was
         | released because disk drives were about $500 in 1982 dollars,
         | the same price as the system itself.
         | 
         | BASIC 2.0 had no "BLOAD" or "BRUN" to directly load and/or run
         | a binary executable. The underlying Kernal could do this, but
         | BASIC left a LOT of functionality on the C64 unexposed (such as
         | - all the sprites and graphics modes). So the standard was to
         | release programs that were in a form that would look like a
         | BASIC program.
         | 
         | So C64 BASIC doesn't have a BLOAD command but ... it kinda did
         | in a janky way. The ,1 in LOAD"*",8,1 means to "not relocate
         | the file" - and any PRG file on a 1541 disk will have the
         | intended address of the "program" as its first two bytes. If
         | the ,1 is present, BASIC will tell the Kernal to load the file
         | at the address it specifies. (There was no SAVE"xxx",8,1). Some
         | software would load to an address that would overwrite the
         | BASIC main loop vectors and immediately start without having to
         | type RUN afterward. Without the ,1 BASIC will load it into the
         | BASIC program text space at 2048.
         | 
         | Much other software was a hacked BASIC program that had one
         | command, something like 10 SYS 2057 or similar, and then 6502
         | code right after the necessary codes that told BASIC the end of
         | program text was reached. BASIC program text started at memory
         | location 2048, right after the 1K of screen RAM at 1024. SYS is
         | a command that simply jumps to a machine language program - and
         | in this case, it's jumping to the binary tacked on to the end
         | of the small BASIC program, which would be the game or at least
         | a booting program for the game.
         | 
         | Programs like this had the actual address in the PRG matching
         | what BASIC would want, so LOAD "*",8 or LOAD"*",8,1 typically
         | made no difference unless the game was that auto-RUNing type.
         | 
         | The C64 had 4K of RAM not used for anything else at 49152. It
         | was common for utilities to live there, so you'd load those in
         | with a LOAD"something",8,1 and then SYS 49152 to start them.
        
         | masto wrote:
         | The C64 starts up straight into BASIC from ROM. Unlike some
         | other contemporary computers, it doesn't attempt to boot from
         | any external devices (except the cartridge port). There isn't
         | really a DOS in the usual sense. Apart from simple support for
         | loading and saving programs, and a very basic channel I/O
         | facility, everything else is handled by the firmware in the
         | disk drive, which has its own 6502 and operating system.
         | 
         | For example, there's no command for getting a directory
         | listing. You type `LOAD "$",8` (8 being the disk drive), and
         | the drive pretends there's a BASIC program called `$` that
         | happens to contain a directory listing you can then look at
         | with `LIST`. (https://en.wikipedia.org/wiki/Commodore_DOS#/medi
         | a/File:Comm...)
         | 
         | By default, LOAD loads tokenized BASIC programs, but if you add
         | an extra `,1` to the command, the file can contain arbitrary
         | data starting at any location in memory. You could use this to
         | load a machine language program and then run it with `SYS
         | <location>`. Clever programmers figured out they could skip
         | this step by having their file overwrite a vector that gets
         | called after the load completes and jump right into their code,
         | resulting in every Commodore kid having being able to type
         | `LOAD"*",8,1` on autopilot.
         | 
         | I got distracted by other trivia (I grew up with this computer
         | and it was hugely influential and I will adore it forever) from
         | getting to the actual point: The C64 uses a variant of the
         | 6502, the 6510. It has a special register for swapping out any
         | combination of the three ROMs (BASIC, KERNAL (sic), and the
         | character ROM) plus I/O registers that overlay portions of the
         | 64 address space. If your code doesn't use those, you can
         | access the RAM they are hiding by turning them off.
        
           | p0w3n3d wrote:
           | On my ATARI there was no DOS too. When you start the 65XE you
           | can hold (iirc) START to start loading an application from
           | the cassette recorder, but it was recommended to hold both
           | (again iirc) START and OPTION to bypass BASIC, because BASIC
           | interpreter being held in the memory, somehow interfered with
           | bigger games (I think this was due to memory, but I'd like to
           | learn from someone who know). I myself got into this trouble
           | sometimes. Also you could have a CARTDRIGE with DOS-like
           | Turbo management which allowed to scan cassette for given
           | filename with binary application, but no one used this
           | because it would take crazy long. I never had chance to use
           | floppy disk, but I think it was behaving in a similar way
           | (you had to have a floppy with DOS and hold START when
           | powering the computer to load it), but at that time the FDD
           | drives for atari were horryfyingly expensive (they had the
           | same CPU 6502, and even there were some demoes which used
           | this CPU as a coprocessor), so I stayed with a cassete reader
           | with TURBO.
           | 
           | Of course games were also sold on CARTDRIGEs and this was the
           | fastest way to play, but it wasn't popular in my country.
        
         | pwg wrote:
         | On the Atari's you could also run 6502 binaries from inside
         | Atari BASIC. The Atari ROM OS explicitly reserved page 6 of the
         | memory map for "user use" and Atari Basic followed suit. There
         | were (IIRC) also a tiny number of page 0 bytes reserved for
         | 'user use' as well.
         | 
         | So, as long as your entire binary fit into 256 bytes, you could
         | run it from inside BASIC. In fact, you could even store it as a
         | BASIC program, the BASIC just needed to "POKE" the binary into
         | page 6, and then you could jump to it.
         | 
         | To do anything larger than 256 bytes required you to dig into
         | the inner workings of where BASIC stored code itself and avoid
         | overwriting any of BASIC's data, or having it overwrite any of
         | your code. Not impossible to do, but did require a lot of
         | undocumented (or not so well documented) work.
        
       | delduca wrote:
       | I wrote a 6502 emulator in Lua
       | 
       | https://github.com/willtobyte/MOS6502
        
       ___________________________________________________________________
       (page generated 2025-04-16 17:00 UTC)