Jump to content

Search the Community

Showing results for tags 'atari 8-bit'.



More search options

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Marker Groups

  • Members

Forums

  • Atari Systems
    • Atari 2600
    • Atari 5200
    • Atari 7800
    • Atari Lynx
    • Atari Jaguar
    • Dedicated Systems
    • Atari 8-Bit Computers
    • Atari ST/TT/Falcon Computers
  • Gaming General
  • Marketplace
  • Community
  • Game Programming
  • Site
  • Classic Gaming News
  • The Club of Clubs's Discussion
  • I Hate Sauron's Topics
  • 1088 XEL/XLD Owners and Builders's Topics
  • Atari BBS Gurus's Community Chat
  • Atari BBS Gurus's BBS Callers
  • Atari BBS Gurus's BBS SysOps
  • Atari BBS Gurus's Resources
  • Atari Lynx Programmer Club's CC65
  • Atari Lynx Programmer Club's ASM
  • Atari Lynx Programmer Club's Lynx Programming
  • Atari Lynx Programmer Club's Music/Sound
  • Atari Lynx Programmer Club's Graphics
  • The Official AtariAge Shitpost Club's Shitty meme repository
  • The Official AtariAge Shitpost Club's Read this before you enter too deep
  • Arcade Gaming's Discussion
  • Tesla's Vehicles
  • Tesla's Solar
  • Tesla's PowerWall
  • Tesla's General
  • Harmony/Melody's CDFJ
  • Harmony/Melody's DPC+
  • Harmony/Melody's BUS
  • Harmony/Melody's General
  • ZeroPage Homebrew's Discussion
  • Furry Club's Chat/RP
  • PSPMinis.com's General PSP Minis Discussion and Questions
  • PSPMinis.com's Reviews
  • Atari Lynx 30th Birthday's 30th Birthday Programming Competition Games
  • 3D Printing Club's Chat
  • Drivers' Club's Members' Vehicles
  • Drivers' Club's Drives & Events
  • Drivers' Club's Wrenching
  • Drivers' Club's Found in the Wild
  • Drivers' Club's General Discussion
  • Dirtarians's General Discussion
  • Dirtarians's Members' Rigs
  • Dirtarians's Trail Runs & Reports
  • Dirtarians's Wrenching
  • The Green Herb's Discussions
  • Robin Gravel's new blog's My blog
  • Atari Video Club's Harmony Games
  • Atari Video Club's The Atari Gamer
  • Atari Video Club's Video Game Summit
  • Atari Video Club's Discsuuions
  • Star Wars - The Original Trilogy's Star Wars Talk
  • DMGD Club's Incoming!
  • DASM's General
  • AtariVox's Topics
  • Gran Turismo's Gran Turismo
  • Gran Turismo's Misc.
  • Gran Turismo's Announcements
  • The Food Club's Food
  • The Food Club's Drinks
  • The Food Club's Read me first!
  • The (Not So) Official Arcade Archives Club's Rules (READ FIRST)
  • The (Not So) Official Arcade Archives Club's Feedback
  • The (Not So) Official Arcade Archives Club's Rumor Mill
  • The (Not So) Official Arcade Archives Club's Coming Soon
  • The (Not So) Official Arcade Archives Club's General Talk
  • The (Not So) Official Arcade Archives Club's High Score Arena
  • Adelaide South Australia Atari Chat's General Chat & Welcome
  • Adelaide South Australia Atari Chat's Meets
  • Adelaide South Australia Atari Chat's Trades & Swaps
  • KC-ACE Reboot's KC-ACE Reboot Forum
  • The Official Lost Gaming Club's Lost Gaming
  • The Official Lost Gaming Club's Undumped Games
  • The Official Lost Gaming Club's Tip Of My Tounge
  • The Official Lost Gaming Club's Lost Gaming Vault
  • The Official Lost Gaming Club's Club Info
  • GIMP Users's Discussion

Blogs

There are no results to display.

There are no results to display.

Calendars

  • AtariAge Calendar
  • The Club of Clubs's Events
  • Atari BBS Gurus's Calendar
  • ZeroPage Homebrew's Schedule

Product Groups

  • Subscriptions

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


Website


Facebook


Twitter


Instagram


YouTube


eBay


GitHub


Custom Status


Location


Interests


Currently Playing


Playing Next

Found 42 results

  1. The first episode of a new Atari 8-bit podcast, Player/Missile, is available at http://playermissile.com It should be in iTunes soon. I'm going to look at games and classic magazines that covered the Atari 8-bit systems, and go chronologically. This first episode is an introduction, and next episode I'll start covering 1979. My goal is to review one game per episode as well, and for the first episode I'm going to review Star Raiders. Sort of like Ferg's 2600 game-by-game podcast, I'll be looking for feedback from folks about the games that I'll talk about, so I'll announce the games in advance if you'd like to send in stories about the games. I'm looking forward to playing some of these games again, and maybe finding games I missed. If you have any suggestions, let me know!
  2. Here we have a Quickshot IX "Deluxe Joyball Controller" by Spectravideo (SVI). You don't see too many of them around and it's a very interesting concept. Works best for maze-type games in my opinion. It's not a trackball, and rather you push the ball in the direction you want to go and it will click (think microswitches). It is meant to be played horizontally, not vertically (note the left hand/right hand toggle switch). It also has an autofire switch. All four suction cups on bottom are present. This controller is in good shape cosmetically and has been fully tested and functions as it should. Please see the attached pictures for more details. $25 including shipping to anywhere in the US takes it. Willing to ship to Canada but will need a little extra for shipping across the border (and all bets are off on transit time). Shoot me a PM with questions or to finalize a deal. Thanks!
  3. I have read comments, here and on YouTube, over the past 3 years. I have made some updates to all of these games here! A quick note about the hacks: I have decided to place all of my hacks in one, convenient thread. As I grew up with my Atari 5200 and Atari 800XL, there were certain games that I thought had a little more potential. I had created basic hacks for a few of these games. I always thought that my 5200 Popeye could look better. Brutus didn't have a number "5" for a head in the arcade version. I knew I had drawn more accurate Mario characters on my 800XL. If only I could plug them into the games. I teamed up with Playsoft, who provided the tools and wisdom to make these games possible. Some of these games have other contributors. Tep392 helped us tweak the collision and touchy controls in DK Jr. I believe that Tep392 and Kjmann worked on the original Donkey Kong RMT sound version. I believe there is another member who helped Playsoft in some of the A8 to 5200 conversions. I am not sure who drew the giant Popeye head. If anybody has clarity on this, let me know, so credit can go where it is due. These are truly community projects, and I had a great time watching them transform. Playsoft did an amazing job of hacking display list interrupts, giving me a little more height area (eg. thicker 5200 Mario Bros floors, as well as multiple floor types). I have been holding off on releasing the latest Mario Bros hacks, because I didn't want to release as many revisions. I was also hoping for some upgraded sound effects for Mario, but there's not always enough time in the day. There's a bonus hack for the 7800 Mario Bros. I found the 7800 graphics tedious to hack. The characters were drawn too small and flat, and the game itself plays horribly. That version has the least number of animation frames, so the animation is poor regardless. It is my "Lipstick on a Pig" release. Donkey Kong: Atari 8-bit Computer Arcade version -- DK_A8_Arcade.xex Atari 8-bit Computer RMT version (Updated sound) -- DK_A8_RMT.xex Atari 5200 Supersystem version -- DK5200_Arcade.bin Donkey Kong Jr: (Arcade & Enhanced versions) Atari 8-bit Computer Arcade version -- DKJr_A8_Arcade.xex Atari 8-bit Computer Enhanced version -- DKJr_A8_Enhanced.xex Atari 5200 Supersystem Arcade version -- DKJr_5200_Arcade.bin Atari 5200 Supersystem Enhanced version -- DKJr_5200_Enhanced.bin Popeye Arcade: Atari 8-bit Computer version --PopeyeArcade_A8_Final.xex Atari 5200 Supersystem version -- Popeye5200_ArcadeFinal.bin Mario Bros (1983, 5200 version): Atari 5200 Supersystem bin version -- MarioBros_Arcade_52.bin Atari 5200 Supersystem car format -- MarioBros_Arcade_52.zip Mario Bros XE: Atari XL/XE version - MarioBrosArcade_XE.zip Mario Bros 7800: Atari 7800 version - Mariobros_7800_Arcade.a78
  4. Adventure Ponies - Atari XL/XE (64K Required) Dragon Trouble Applejack Smack Oh No! A Hydra Rainbow Dash-ed Uh Oh! It's Discord You Win! Requires 64K - Hold the "OPTION" key when booting to disable BASIC. In Altirra you can disable BASIC by System -> Firmware -> uncheck "BASIC (boot without Option key)" If running in an emulator, NTSC is recommended, This is a WIP and still being tested and optimized. * Current Version * ----------------- 0.87 ----------------- - Disable BASIC automatically, so the player doesn't have to hold down the [OPTION] key when booting. advponies.0.87.xex advponies.0.87.atr * Older Versions * ----------------- 0.86 ----------------- - Added splash screen by Kaz at atarionline.pl, adjusted music volume advponies.0.86.xex advponies.0.86.atr ----------------- 0.85 ----------------- - Added the ability to turn the music on/off. On the "Select Pony" screen, press the [OPTION] key to toggle music on or off. advponies.0.85.xex advponies.0.85.atr ----------------- 0.84 ----------------- - Added background music to the game. (Stage tune and Enemy tune) advponies.0.84.xex advponies.0.84.atr ----------------- 0.83 ----------------- - Modified the Pony Select Screen to show Locked as well as Unlocked characters. advponies.0.83.xex advponies.0.83.atr ----------------- 0.82 ----------------- - Rewrite of all the animation code from C to assembly. This has improved game performance quite a bit. advponies.0.82.xex advponies.0.82.atr ----------------- 0.81 ----------------- - Optimization and refactoring of code. The game has improved performance especially on the Hydra level. advponies.0.81.xex advponies.0.81.atr ----------------- 0.80 ----------------- Original release advponies.0.80.xex advponies.080.atr
  5. On Tuesday I plan to mail @Allan the following unarchived (or needs-better-archiving-quality) Atari items. The shipping can get quite expensive and the funds come directly from the pockets of Allan and myself. I have a hope that some people can donate funds to me via PayPal to contribute to this on-going Atari archiving project. As of right now, I'm not sure how much shipping will cost. I'll give a sum total after the items have been shipped. I have been exchanging many, many pictures with Allan over the last year or so. He goes through the pictures and chooses what needs to be archived, or what needs to be archived in a better format. Occasionally, I send him large batches of items and he scans them and uploads them to archive.org. This can get quite expensive, as he and I send the items back and forth. If you feel like making a donation to me, then send me a personal message and I will send you my PayPal account. I will have @Allan chime in on this thread so that you know this is legitimate. Here are pictures of everything that I'll be sending to Allan this coming week. Thanks for considering making a donation-- the Atari community rocks! Adam
  6. Hello all! I did a lot of Atari 8-bit coding as a teenager on my Atari 400 and wrote about a few games including about SIX in Atari BASIC, and ONE in 100% assembly language. Some of you may recall that I posted about the assembly language game about 12 years ago. It is a Shoot 'em up called 'Sector Wars" and at the time I was trying to raise money for a charity and when the target was reached I was going to release the game. Unfortunately the target wasn't reached but some very kind people (some of which I believe frequent this forum) did donate at the time and the money went straight to the charity via JustGiving. I always wanted to finish the game but with one thing and another I never got around to it. Original Thread for anyone interested : O Anyway to cut a long story short I am just finishing it off now, and I will release it here next week :). In the meantime for the next few days, I will upload some of my games written in BASIC. A couple were published in the UK Home Computing Weekly magazine in the 1980s, and the rest I never got around to sending off to anyone. Some are okay but others a not so good but I will upload them for completeness. Something for lockdown anyway ;). For Friday (17th April 2020) I will upload the two BASIC games published by Home Computing Weekly magazine. The Chaotic Crossing (chaotic.bas) - Published in the UK Home Computing Weekly magazine Magazine Link: https://archive.org/details/home-computing-weekly-066/page/n27/mode/2up HCWMan (Pacman Clone): This isn't great - and ran quite slow. Magazine Link: https://archive.org/details/home-computing-weekly-054/page/n47/mode/2up Over the next few days I will release some more BASIC games including one which is a Yars Revenge clone. Cheers Vic chaotic.bas hcwman.bas
  7. This is (again :)) Dracon from TAQUART Group. I have the honor to present another interview with a person known (this time) for his excellent games on Atari XL/XE. At the time of the interview he is currently 30 years old and... it's English programmer Ivan Mackintosh, to whom we owe such hits as CRUMBLE`s CRISIS or REBOUND (giving only a few of the collection). Despite the passage of time, however, I decided to publish it, with the consent of the Ivan himself... This interview was based on a series of our emails from 1999 year and was firstly published at Polish website Atari.Area. D = Dracon I = Ivan Mackintosh D - Hello, at first I must say that you were a really great Atari 8-bit programmer. I remember you from such great games as BLACK LAMP or CAVERNIA (was it your latter game on Atari ?). I - Hi. I am glad that you like my games. CAVERNIA was not my last game, because I wrote one more for Atari, which has never been released - "TUBE BADDIES". It's a two-player game where you have to shoot down the monsters and stick holes in the pipes so that no new monsters appear. It was another game made with Richard Munns - so, as usual, there is great music and graphics. My first game written in assembler 6502 was REBOUND. Previous games (Crumble`s Crisis and continuation - Space Lobsters which is only good for graphics - no playability) were written in compiled Basic. This is probably the reason why they are so slow. D - The English magazine NEW ATARI USER once published a review of your game TAGALON. I also read there that there was a big break between writing and publishing this game... Is it true ? I - I think so, as I've never seen it in the sold version or even the cover ! D - So it seems that you didn't make much money writing games for Atari... Am I right ? I - I had a promised fee of 1500 English pounds (9500 PLN ?) for Black Lamp and 2000 English pounds (13000 PLN ?) for Tube Baddies game. Since they were never properly spent, we didn't get that fee. These two games were also the biggest earner. So in fact, when all the money was divided between Richard and me, we didn't have that much for 5-6 years of Atari XL programming. Certainly not enough to do it as a full-time job. D - Where did you get the inspiration your games from ? I - TAGALON was similar to SABRE WULF (from ZX SPECTRUM) for 2 players. TUBE BADDIES was inspired by another spectrum game - COOKIE. We were asked by Atari company to create BLACK LAMP. REBOUND was an attempt to write a sports game. And the other games just "happened" (without direct inspiration). D - Have you programmed games on a "clean" Atari or with the help of e.g. Atari ST (I'm asking in the sense of creating games on an 8-bit computer)? I - The first two games (CRUMBLES CRISIS and SPACE LOBSTERS) were written in Atari Basic and then compiled. It took two 1050 stations to speed up the compilation to about half an hour. I used a software assembler (Synapse Synassembler) to do the REBOUND. The rest of the games were programmed using my cartridge with MAC-65 assembler. All games were encoded on my faithful 800XL. D - And how did it look like to create e.g. music ? I - Richard used a synthesizer to work out the melody. Once he composed it, he manually translated the notes into values in the Basic's DATA command. When it was done, I converted his music player into an assembler and run it on VBI interrupts. D - Can you tell what is happening now with other famous people from Atari XL, like programmer Ian Copeland or musician Adam Gilmore ? I - Unfortunately I can't help it - I never talked to these people. D - Do you know that there are still people in Poland who write amazing programs, especially demos ? I - About three years ago (1996) I was in contact with one of the guys who made JOYRIDEcurrently doing demo. It was the last time my Atari 1050 drive was still working ! D - Well, what is your opinion about the so-called demo programs ? Is there any sense to write such programs instead of good games ? I - I think that some demos coming from Poland are excellent. It's just a shame that all this didn't happen 10 years before the Atari scene died. Anyway, as I see it, in the UK the 8-bit Atari scene is now extinct. D - Can you tell us what Richard Munns and HARLEQUIN (who has released the PLASTRON game, for example) are currently doing? I - Richard is messing up with PC computers, although he is not doing it professionally. I don't know what is happening with HARLEQUIN. Richard had contacts there when he was making music for it. As far as I can remember, he was cheated by HARLEQUIN because they did not pay him for his work. Richard didn't hear about HARLEQUIN later - maybe the company went bankrupt. D - Where do you work now ? I - I am a professional programmer (more precisely: "senior software engineer"), writing C++/COM applications for Reuters. D - Do you know if NEW ATARI USER magazine still exists ? I heard that it is (was?) available only for subscription... I - About 2 years ago (i.e. in 1997) I saw a copy of it on a computer show. It was A5 format. Really not much bigger than some leaflet. I don't think it's in print anymore. D - Do you sometimes turn on your 800XL (or emulate on PC) nowadays? I - None of that. My hobby nowadays is rather arcade machines. However, I still have 3 Atari computers (400, 800XL, 130XE) at home. D - So say something more about it. I - Currently I am more interested in games and arcade emulators. I have one slot machine and a NeoGeo console with 5 cartridges. I've never been to Poland, so I don't know anything about your resorts, but those in England usually have a lot of entertainment gaming machines, full of big "cabinets", containing a monitor, joystick, special table, etc. This is the kind of machine I have in my private room. D - You mentioned that SPACE LOBSTERS is only good for graphics. Yes, that's right, but why is it so difficult ? Why didn't you put any codes in your games (like Frank Cohen or Chris Murray) ? I - You mean, you haven't found any cheats yet ! There are none in CRUMBLES CRISIS nor in LOBSTERS SPACE. However, in CRUMBLES CRISIS you can rename the files so that you can play faster in the later stages of the game. In BLACK LAMP, as far as I remember, you can type "forest" and you will get infinite life. In CAVERNIA, type "tamsin" (my wife's name) to go to level 16 and "stevie nicks" (she's Richard's favorite pop artist) to move forward 1 level. It seems to me that this last code also works in TUBE BADDIES! D - Besides, I'm curious what happens when a player completes SPACE LOBSTERS or CRUMBLE CRISIS - is there any "final sequence" ? I - I don't remember what it is like in CRUMBLES... but there is nothing in SPACE LOBSTERS - we thought that the game is so bad that we don't have to do something like that, as nobody will get that far. So the game starts again, but it's more difficult. I think you have to collect more codes or something like that. Richard used to play it to get this far! D - In general - do you remember coding on Atari and you still have this ability ? I - I have Atari so I still remember those times. I think I could still code on it. It would be quite slow as I only remember what it can do, not how to do it. So I would have to take a good look at the book "Mapping the Atari". I would even like to program the Atari again, but I just don't have time. D - What's your favorite Atari game ? Mine is BLACK LAMP and DRACONUS. Both have a unique fantasy atmosphere and I am still impressed by the craftmaship in them. Appropriate, refined graphics, music and the rest - I think that these games take the maximum out of XL ! I - You've probably noticed from several of our games (Rebound, Tagalon, Tube Baddies) that we really like games for 2 players. One of our favorite games of all time was M.U.L.E. We also played a lot in International Karate, Drop Zone (we liked Archer McLean's games) and the games from Lucasfilm were also outstanding (Ballblazer, Rescue on Fractulas, etc.). D - And which games were favorites on other 8-bit computers? I - Our (my and Richard's) favourite games are arcade adventures. We grew up on ZX Spectrum games such as ATIC ATAC, SABRE WULF, ALIEN 8, etc. D - What is your opinion on your various "rewritten" (patched) games? I mean your games with trainers, intros, etc. ? I - After seeing it, I am generally flattered by the fact that people considered these games worth modifying ! D - In that case, what about such a case - a "transfer" of the CAVERNIA's hero in another game named DEIMOS? Is that OK? I - I really enjoyed playing DEIMOS. It reminded me very much of the kind of game that Richard and I would write together. As for the observations, it was a shame that the whole scenery was in gray tones and the colors of the hero were strange. I was tempted to make a modified version with other colors, but I noticed that the image with the game is compressed. I would need to have it unpacked on disk for modification. Although to tell you the truth, I don't have any free time for this. D - What made you stop writing games on XL/XE ? Haven't you thought about releasing e.g. TUBE BADDIES in Poland (here was a big market for this computer in mid 90's), Germany or somewhere else ? I - I went to the University and because of that I didn't have much free time, besides, companies in the UK were offering less and less money for games. I didn't realize there was a market outside my country and I didn't speak other languages either. D - Have you seen some games in which Richard Munns was involved (Plastron, Zero War)? How did you like them ? I - I saw Plastron during the creation, as Andy Dickinson was a friend of mine who lived in the neighboring city. However, I did not like his games very much. He also created the game COUNTDOWN. Nevertheless, the graphics and music in Plastron were good. D - Why was there no music or any ending in CAVERNIA ? Not enough memory (anyway, the hero's animation was great!) ? I - We didn't get a lot of money for the game - there was no incentive to make the game more beautiful. D - Going back to the emulation. Have you seen any Atari emulator on PC ? I - To be honest, I haven't touched any Atari stuff in a few years. I used an XL-it emulator by Markus Geitzen but about 3 or 4 years ago. This emulator was really good except for some problems with player/battle graphics (PMG) and the inability to overlay 2 players to get a third color on the bits I applied. This unfortunately meant that Richard's sprites didn't look so great. Now, however, there is a significant change - thank you very much for sending the Atari800Win emulator! It is excellent, so much better than the XL-it I had. It is more or less perfect ! I was so impressed that now I can even get rid of my real Atari. I was playing my games really well. D - Why did you choose Atari instead of C-64 (for creating games) ? I - We (me and Richard) did not choose, it just happened. We both had Atari for a few years before I started programming. It's a real shame that the C-64 came on the market (at least in the UK) more than Atari, as Atari was great for programming. I suppose if we were creating for the C-64, we could make more money. D - How long did it take you to create Black Lamp game ? The result was amazing and the game is just wonderful ! I also played Black Lamp on Amiga, but it was much worse version. I - Atari told us that they want the same Black Lamp as the Atari ST version. But when we started asking for game maps and other details, they couldn't provide that and then they let us encode what we wanted, if that was similar. I don't remember exactly, but I think it took 9 months to write (remember that it was all done on weekends and free evenings). Atari wanted to see how far we were every 2 months, so they could change things if they weren't satisfied with something. D - It's a shame there aren't any more games as well done as yours and Ian Copeland's. Maybe the 8-bit period was just one stage in creating games, a kind of training... Nowadays it is easier to use the advantages of the machine (good graphics cards) and make nice games (almost unoptimized algorithms and games require more and more CPU power). What do you think about it? I - Nobody cares anymore. There aren't many games that pull everything out of a machine now. TURISMO GAME on PSX was the last game I was really impressed with. It seems that many developers think that just nice graphics will make the game a hit. I completely disagree with that. I believe that gameplay is the most important thing. I can support it with the fact that I bought an arcade slot machine with a 1982 Mr.Do! game. It's a great game. D - You are right that certainly playability (inherent in the idea) is the most important thing in any game. So what do you say about the super hits from the 8-bit games (Blinky`s Scary School, World Soccer, Black Lamp, etc.), which were also released in 16-bit versions? They were certainly not appreciated as popular and good games, even though the idea was the same and the graphics were much better so what about this sad fact ? I - It seems to me that when people get new machines, they want newgames that use the power of the machine rather than some old games that just improved the graphics. Well, in the world of emulation it doesn't matter much now. Can you get Gameboy Color in Poland (small console) ? As I noticed, the DROPZONE game is soon to be released on it ! D - It's supposed to be available, but I don't know if in large quantities. Thanks for the interview ! I - No problem. At the end of this interview I'd add, that Ivan Mackintosh after stopping creating games on Atari also released some small PC utilities, which were related to XL emulation. These were quite a few programmers that helped to handle Atari floppy disk images: IMAGEMAN and ATARI DISK IMAGE MANAGER. Besides, one more interesting thing - at this address you can find a couple of small Ivan's games for NEOGEO Pocket! Polish version available here.
  8. INTERVIEW WITH... F#Ready ! I am extremely pleased to present another interview with a well-known personality of the Atari community. At the "Silly Venture" 2018 computer party I came across one of the famous (The meritoriously :)) Atari-sceners of the of the 80s/90s era. And it turned out that I didn't need to persuade him so much for a few words about his history, which I proudly published for you here. So it's an interview with the living legend of western 8-bit Atari scene, the man known as F#READY and once Frankenstein from the High Tech Team. Interview time: December 2018. D : Dracon/TAQUART F: F#Ready F: Hi, my name is Freddy, $30 years old. I live in Hilversum with my girlfriend and our 10-years old daughter. I’m currently work as Software Engineer / Scrum Master at an hosting company in Amsterdam. Got my Atari 800XL in 1985, that’s when my passion for programming started. The Atari and retro computing in general is still one of my favourite hobbies. Next to that I’m crazy about bicycles. I have two recumbent bicycles and a rowing bike ( my recumbent Nazca Pioneer). D : So.... why did you choose ATARI computer (but not C64 or Amstrad?) and was it your first 8-bitter? How did you start your computer adventure then? F: Well, I was introduced to the home computer by my uncle. He had a ZX spectrum and that was like a magic box to me. At one time I could borrow it for a few weeks when my uncle went on holiday. I was hooked and remember typing all basic programs I could find. There was no tape recorder or anything to store the programs, so I kept the computer running through the night or typed a new one the next day. Anyhow, of course my uncle got his computer back and I saved money to buy my own computer. I had to do my research into which computer I should buy… In October 1985 I bought my own Atari 800XL with 1010 data-recorder. The other home computers were simply too expensive and I had seen the Atari computers before in a local shop. Again I typed in a lot of program listings from books and magazines. Each new Atari magazine was like a big birthday present and I read those several times. Soon I started programming my own (simple) games in Atari Basic. Even made some cassette inlays (still have those). So that's how it all started. D : And what's next ? You made huge progress through the years.... F: Next to Spectrum and Atari I had some experience with BBC computers during an internship at a school. I had to convert BBC Basic to Gw-Basic on the PC. They also had books about 6502, so that was a really nice working environment In the meantime I became member of a local Atari club in my region; Atari GebruikersGroep Friesland (AGGF). My long time friend André (The Exterminator) bought an 130XE, so we went together to play games, copy new stuff and have fun. There we meet the other HTT guys; Yeb (SolarSystems) and Eric (The DDT-Crew). In the beginning I was still programming machine language in hex codes using BASIC data statements or directly in Supermon. The other guys had access to assemblers, so I soon switched to Atmas II for all my assembler programming. I have to add that I really learned a lot from using Supermon and hacking existing programs (mostly games). I’m still not much into playing games, more discovering how things are done. That was the real excitement, looking inside a game to find new kinds of tricks with display lists or how DLI and VBI were used. Armed with ‘Mapping the Atari’ this was a nice way to learn more about the Atari hardware. As a group we had a lot of fun. We visited each other on a regular basis next to the local club meetings of course. And we exchanged letters and disks through the mail (yes, it was the time without internet!). There was no musician, so I ripped music from games to use in our intros. Personally I was inspired by the demos I saw on the Atari ST. We were not aware of a ‘demoscene’, even the word was never used at that time! High-Tech Team in 1989 year. From left to right: The Exterminator, The DDT-Crew, SolarSystems, Frankenstein D : Were there (in the Netherlands) proper computer literature and magazines that simplify Atari programming? F: There were quite a few in the beginning. Of course I got the Dutch magazines first; Atari magazine and Atari info. Our local Atari club AGGF did a few magazines called 'Reset' for which I wrote a few small articles. Next to that there were too many; Atari User, Page 6, Atari Magazin (German), Happy Computer (German), Analog and Antic. I could only afford to buy some and mostly bought Atari user and the German Atari Magazin. The Happy Computer special edition was the best one. A thick magazine packed full of listings with quality software (e.g. Turbo BASIC). So yes, I think I learned a lot from the magazines. There was not much more than that. D : So you were really lucky! Because in Poland at the time (1985-1990?) there were literally no serious, extensive books/articles on programming Atari 8-bit. Well, at least there were few books and texts only by one authors (Wojciech Zientara). That's why quite many Polish programmers had been trying to explore Atari system on their own (e.g. Henryk Cygert, known better for his amazing games like “Miecze Valdgira”). Can you imagine he has been analysing the whole Atari memory and system routines and wrote down (by hand) a big paper notebook on it?! F: Ah cool, I didn’t know that. I heard that ‘mapping the Atari’ was also made by reverse engineering instead of reading official Atari documents. I can imagine it was fun too. By the way, I have a few of these Polish Atari ‘school’ books and I think Poland was ahead of the rest because of teaching these things at school! A different perspective I guess. D : Didn't you have any difficulties to master Atari hardware ? What was the biggest challenge in it? F: Difficulties with the Atari? Not really. I gradually discovered more things. Whenever I learned something new I experimented until I understood how it worked. E.g. display lists. There were examples and it was easy to modify and later try it in the assembler. The book mapping the Atari helped greatly. It was my Atari bible. D : You said before about HTT-team. But how it what was born and how it evolved? F: The HTT started with four guys who made a few intros and the Big demo. After that a few joined us, but soon it all collapsed because of different reasons (e.g. read the articles in Mega Magazine #1). It was also one of the reasons I started Mega Magazine. So, in my humble opinion there was not much ‘evolving’ going on. It just ended around the time of the first Mega Magazine release. D : During MegaZine’s time one could say it already was there (the so-called small Atari European demoscene). The time was early nineties and there were people from The Netherlands, Germany, Poland, Sweden, Czech & Slovakia, Canary Island (!), Sweden, Hungary and even Turkey (!).... Many of them had created, let me say, MegaZine “family” of editors & readers, I guess. F: Yes, exactly that! It was one big (happy?) family. And just like making demos is was all for fun. For Mega Magazine I was much inspired by the first issues of Maggie on the ST (by TLB), some other disk magazines and Dr.Dobbs Journal. D : OK, you just mentioned about your inspiration from ST machine. Where did you had contact with it ? And weren’t you ”envious” about Commodore 64 computer (due to plenty of nice(r) games, etc.) and its big demoscene? Didn’t you plan to move into other computer during your Atari days’ period? F: Some people from the local Atari club had Atari ST’s and loads of software. A good friend also got an ST next to his 130XE, so I visited him often to watch demos and play games on the ST. I once bought an Atari STE in the early 90s but had more fun the the 8-bit. I sold the 1040STE after one year. And really, I’m not kidding you, I did not know anyone with a C64. I did saw some screenshots in magazines and knew more games were released for the C64, but that was about it. But on to this “controversial” photo with me (shot in early 90s): The one I'm holding was from Pirx/Our 5oft. They used it to rip gfx (for own games, etc.). Of course I seen it before, but I had no friends with C64. I did my research though, so I was aware of the Amiga and that it was designed by Jay Miner. D : So does it mean Atari (8/16-bit) was quite popular in the Netherlands in nineties ? In my country for example, there were full of small (8-bit) Ataris so there was quite a big market for making games in 1990-1995 year. F: I don’t think there was a big market here. Most people switched to ST, Amiga or PC. There still was a reasonably Atari community, but I think it was nothing compared to C64 and Amiga. D : So what about these: Stichting Pokey, Atari Club Eindhoven and other? In any case, not bad for a small country. Well, I'm afraid that in Poland in the eighties and nineties there were no active foundations or formal clubs (like ABBUC) as above…. There were few newspapers, a couple of publishers and certain amount of Atari demoscene people... F: Yes, and before Stichting Pokey there was Stichting Atari Gebruikers (SAG) with their Atari Magazine and regional meetings. Next to that there was an Atari division from the Hobby Computer Club (HCC). The HCC Commodore division survived and let other retro computer die-hards join them. This meeting is still happening every second month in Maarssen (near Utrecht). As you may know, in february the Atari 8-bit fans bring a lot of their Atari stuff to have a real Atari Invasion at the Commodore club See here: http://www.atari-invasion.nl/ and http://atariage.com/forums/topic/273530-atari-invasion-2k18/ and http://atariage.com/forums/topic/286339-atari-invasion-2k19/ D : Back to Mega Magazine… So what’s like to be main editor of such big magazine? Was it easy for you to manage all the contents, writers, etc. ? ;o F: Big? It really was just a hobby and the articles I got came from friends. I also liked to write a few articles myself. Sometimes there was a delay for an article, but than it could be used for the following issue. The hard part was to fit all the text on one disk side. D : From one moment (but not from the start?) MegaZine started to be distributed commercially... Why does it happen? And was it successful? I know ANG (A NEW GENERATION SOFTWARE - worth to show what it was) was responsible for this in the Netherlands and Mirage in Poland, but I’m afraid the magazine hadn’t been spreaded (by official way) as much as it could, isn’t it? F: IMHO it was spread enough through the usual channels, mainly Atari friends. Spreading it through ANG was an easy step (we were friends) and convenient for me because they did all the copying, labeling and they created nice looking covers D : The Mega Magazine (aka MegaZine) was a real gem for me. No other Atari magazine (formal or informal) had so much nice dose of humour, knowledge and overall good impression! It’s a pity it is not continued in this or another form. Why? Or maybe you are going to do so in future? F: Thank you for your kind words. All credits to the contributors, they made it possible. As you can see, MegaZine #8 was the last one mainly done by Jiří Bernášek and myself. The whole story can be found on the magazine itself TL;DR : in the age of internet there are enough other methods to get and spread the information. The last issue was done to release the remaining good articles, done by Jiří in 1995. I wanted to finish the whole series nicely with number #8. D : I remember that at later stage HTT was quite a big band, wasn’t it? And I always wondered about two facts: - What does “High Tech Team” name mean itself? Is it: the guys from High Technical school (aka Polytechnic ) ? - Why some HTT-members took their nicks in “plural” style? ;o F: How did we came up the name ‘HTT’? That’s a good question! No, it had absolutely nothing to do with a technical school. I’m sure it was simply a popular term at that time. High technology, meaning ‘cutting edge’. And the three letter abbreviation was probably inspired by groups on the Atari ST (TEX, TCB, TLB etc.). LOL, I really don’t know why some members decided to be not one, but more than one Solarsystems (there must be a great ego at work there) or why one guy calls himself a crew. It sure confused me at the time D : What is the origin of your nick in HTT ? Is it a homage to favourite fictional character in movie or book by Mary Shelley ? Or was it randomly taken ? F: I think it was inspired by (fragments of) the movie. “It’s alive!”, the feeling when you finally got that assembly program working OK, now is clear. But from a couple of last years you came back with another nickname… Why? ;o Maybe to indicate a fresh start, I don’t know. I felt it was time for a new name. People change and maybe the nick should change too. In detail: From the Atari BASIC screen. After boot, arrow up, insert 2x, press F. That's my name Since we don't have the inverse-space anymore, this was replaced by the modern day hash-tag. D : You forgot (?) to add you were releasing some cracktros (unprotected games with your custom, colorful intros) as HTT. So, that’s what some of demoscene members had started with. What about that? Who was doing what, if that’s not any secret nowadays? Is there any place one can see or download your releases (for brilliant games like BLACK LAMP, PLASTRON, BLINKY’S SCARY SCHOOL, anything else) ? F: Ah, yes, but I don’t have a complete overview of what we did on cracktros (yet!). D : What do you mean by this? I saw only three games with HTT’s intro… F: There should be more. I have a large collection of files which I did not check with the ones on the internet. Would love to preserve those cracks (not only HTT, also others). And as a side note, we didn’t even know we were doing cracktros, never even mentioned the word at that time. Call me naive, but we really didn’t know much about any ‘demoscene’ or even ‘demo parties’. D : So what made you write such intros? Any idea usually comes from somewhere… C64 was around the corner? F: Not from other systems, we didn’t know much about those at that time. We all had Atari. It must have been the intros from others before us, like Steve Zipp (who did this Black Lamp cracktro), CSS and CCA. 😧 Let me guess… CCA = Copy Crew Amsterdam? And CSS = Copy Service Stuttgart OR Chip Special Software? F: Yes, Amsterdam and Copy Service Stuttgart 🙂 Probably also saw the ABBUC hobbytronic demos before we did our things. I remember visiting an event where ABBUC showed one of their demos and they made a nice digitized photo for us which we used in the Little demo and Little Color demo. D : These „copy services” sounds little suspicious. Were them huge factory to copy stuff for free? ;))) F: LOL, you really should interview Steve Zipp or one of the CSS guys. It would be nice to hear those stories, since I really don’t know much what was going on there. D : Steve Zipp is (in a way) a legend. But I don’t know if he is so talkative as you! F: One thing I heard. Steve Zipp got his software from a guy reviewing new software for a magazine. So, it was first send to Steve and later send back to the reviewer. D : Wow, didn’t know it! In Poland in 80/90s were so-called „computer studios” which sold a lot of A8 programs, including even demos. So quite many times I saw „Big Atari 8bit” as one of the hits there. These were real „copy-services” in my country. I bet in Netherlands situation was more normal and demos were for available for free, right? F: Yes, I never heard of selling demos and we were really surprised that Page 6 offered us money because they told us the Big demo would be included in their public domain first. We even got a little assignment (and money!) to make a small intro ‘creator’ for the Page 6 magazine. Still have to find where they used it D : Not bad for non-professionals. What I heard about ABBUC, they ask various ppl to prepare intro to its magazine but without benefits (except fame) Correct. That was a big (!) difference with the UK. I got a thank you letter and they put the Big demo in their public domain, which was ok, we liked it. Still like their concept of intro for the magazine. It gives the disk something special. Ok, back to the intros (that’s what we called it). I remember making a cheat version for ‘tank commander’ and a little tool to generate accounts for ‘ghostbusters’ (I think it was on a Mega Magazine, called ‘account buster’). We all did a few game intros, but not much real game cracking. We got most already cracked file versions directly from Steve Zipp who had access to original software and was known for his cracking skills. D : Since you were not into demoscene (crackscene) when releasing any intro+game, what were you hoping for ? Was it for just pure fun or hope to make new friends in Atari-world or just for…. fame? F: Fun and being creative. That’s all. The goal was not to get new friends, but it obviously resulted in getting to know new people, some of those became good friends. No, don’t care about fame. I have much respect for all who are just creative, no matter what they do. It can be a real struggle to start coding on a new platform, so anything creative gives you a good feeling, it doesn’t even have to be something special for the rest of the world. D : Cool. So you did some demos, cracktros and very fine disk-magazine, but were not plans for any game or bigger utility? I only heard about announced stereo music-editor from one of the HTT members…. I mean here the time of HTT, not ANG afterwards. F: There was a plan to make a game with the code from the Platinum main menu, but since we all know what happened to that demo... Yeb (SolarSystems) worked on a music editor called ‘Atmus’. I still have the source code, but he did not even remember he ever made such a thing. D : Interesting! Is the source code almost ready to go? Would it have been better than other music editors at that time? I heard about Platinum demo in the past and know it as one of "impossible" productions which had too much expectations for.... But as of today, have you got some parts from it to use in future maybe? F: Don’t know the state of Atmus and it’s impossible to compare an unfinished product with other finished ones I do remember it was supposed to work with joystick and mouse. I still have all the source code from the unreleased Platinum, but some parts were already released as ABBUC intros years ago. To be honest, the unreleased stuff was quickly outdated and is not interesting compared to all the great demos that has been released over the years. For example, I used a little code from ‘Megadeth Scroller’ to create ‘The Lost Bytes’ 16K for Silly Venture 2k14. Also code from the ‘Plazma’ part was used by FRS and myself for the ‘Lets Rol’ 16K intro (Silly Venture 2k16). D : Oh, there was just one collaborative demo in which should HTT took part… Could you reveal more about this project? F: The Expo ‘91 demo was made for an Atari Benelux Expo in 1991. John from Pokey’s Magazine (The Missing Link, ANG Software) asked us to take part. I used a chiptune from Benjy ripped from the Hobbytronic demo and digi musix which I got from Detail (TACF) We did the final part for this demo and the small intro. The graphics was done by Prizm. Since Pokey’s Magazine was a Dutch magazine they wanted me to write the text in Dutch. I think I translated the English text back to Dutch including the group names in the greetinx. The result is funny if you can read Dutch language. The end part is difficult to reach since you had to read a large part of the scroller until you could press start. Digi music with screen on was not very common in 1991. D : Still speaking of demos, I must admit “Big Atari 8-bit Demo” was quite impressive at its time. I remember trying to read all texts from “the biggest scroller” there (but rather failed, as far as I remember ). Did "BIGA8Demo" made HTT somehow more popular in Atari world? Did you receive postcards, phone-calls or congratulation-letters from many other Atari freaks? F: Yes, we were all very surprised by the amount of letters we got in our P.O.Box. We decided to fairly distribute the new contacts we got. We got even more mail after the advertisement in Page 6, which was of course a great honour for us. While it might be considered to be a very simple demo nowadays, I’m still very proud of it. D : Well, especially digi-sound and music was surprising in "A8BD". But I bet you there are some fun facts about this production, hidden somewhere… F: Maybe a few, did you know... - the intro text was not decoded, but if you alter it with a sector editor the program will punish you (beware, it will destroy your disk if protection in disabled!) - the big scroller was decoded into 6-bit per character, an easy compression (25%) but fast enough to decompress - musix from the music menu was carefully chosen based on where we ripped it from memory. So, all musix from a menu don't overlap each other in memory - although the music menu is easy if you know about pm gfx,, I got questions on how we managed to get a screen with only 16 characters wide. for some this was already black art - the initial plan was to have a picture show instead of digi samples, but it turned out the guy who promised to do the pictures was not very good at it and gave up. - there were 17 master disks each with a unique serial number. the number is related to the person who got it directly from us. E.g. ABBUC, Page 6, etc. - the DOS menu is of course fake. when pressing return 3 times it shows a message and does a fake format. It all shows how much fun we had in making this demo D : Fine. What do you think about phenomenon of ripping on Atari 8-bit scene in mid 80/90s ? There were plenty of demos with ripped music from commercial games (is this due to lack of good musicians or music editors available at that time?). There were also demos with converted/ripped graphics from Commodore 64 (e.g. WFMH or HARD group) or even with some code/fx (HARD group). Do you condemn it? F: I’m not against it. Sometimes it is the only option. We had no musician in the early days and I liked to rip music from games. We had great respect for their work. On the other hand I would always prefer original music or graphics in a demo. It has a better chance of being a good demo when everything fits together nicely. D : Being longtime Atarian (with some years of break, right?) what keeps you still using Atari and visit different computer parties from time to time ? ;o F: There was a long break indeed. Did a few small things during that break, but really got back to the Atari when I decided to go to Silly Venture 2k14 to meet ‘old’ friends. Actually Grey talked me into coding something small and I rediscovered my hobby. Also, the great IDE (WUDSN) from JAC!, the MADS assembler and Altirra emulator helped a lot. Hereby a big thank you to the creators of these tools! At the moment the Atari as a hobby gives me great pleasure. I’m lucky to have a few good friends with the same hobby, so that helps too. It is always nice to go to parties to meet old and new friends who have the same interest. It’s also a very friendly and creative community. D : By the way, how do you like visiting Poland now (2018 year) and in the past (1991 year) ?Are there significant differences ? F: I’m not an expert, but I think the economy in Poland improved a lot. Prices went up, so I don’t feel like a rich dude anymore when visiting Poland. Transport and hotels are still much cheaper than in the Netherlands. I’m sure people in Poland are more wealthy now, maybe even rich compared to 1991. You should be able to answer this But from a personal point of view, people are still friendly and very hospitable. D : You said to me on last SV2k18 that you handed over “Atari Preservation Project” to somebody else. What is its status nowadays? F: Not exactly that. I try to do a few things and gave advice about best practices for software preservation since 2003 when the APP started. I see the current A8 preservation project is going very well. Farb and others from the AtariAge community are doing a wonderful job! It’s nice to see that the project is on the right track with good people and lots of knowledge about the A8. D : And now something quite different... Please tell me what is your favourite: - Demo (Atari / other) - Game (Atari / other) - Music (do you still listen to extreme metal bands?) F: Ah no! Demo, too difficult. Sorry, not possible to answer that. Game: Rescue on Fractalus and Boulderdash Construction Kit. Music, depends on my mood. Like to listen to industrial / martial music like Frontline Assembly, Laibach and Triarii. And of course chiptunes from different systems Don’t listen much to metal bands at the moment. Still like the old bands like Napalm Death. D : Does this kind of extreme/experimental music make you more quiet or vice versa??? ;o F: Yes it helps me to relax D : Well, we unfortunately have to finish this huuuuge (!) interview. What would you like to say at the end? F: There are probably important things I forgot to mention and things I remembered differently or are simply wrong. Feel free to correct me and restore my memory Thank you for taking the time to interview me. I can go on forever, since I really like to talk about the good old days, happy memories, it shaped my mind. I hope to see you and other Atari friends again sometimes, maybe next Silly Venture? D : We'll see! So, thanx a lot for taking your time as well and let us long live Atari (in our hearts) ! F: My pleasure P.S. Polish version can be found here.
  9. Bit Operations ============================================================== Part 1 - Introduction http://atariage.com/forums/blog/576/entry-13175-part-1-of-11-simple-assembly-for-atari-basic/ Part 2 - Learn 82.7% of Assembly Language in About Three Pages http://atariage.com/forums/blog/576/entry-13176-part-2-of-11-simple-assembly-for-atari-basic/ Part 3 - The World Inside a USR() Routine http://atariage.com/forums/blog/576/entry-13177-part-3-of-11-simple-assembly-for-atari-basic/ Part 4 - Implement DPEEK() http://atariage.com/forums/blog/576/entry-13178-part-4-of-11-simple-assembly-for-atari-basic/ Part 5 - Implement DPOKE http://atariage.com/forums/blog/576/entry-13180-part-5-of-11-simple-assembly-for-atari-basic/ Part 6 - Various Bit Manipulations http://atariage.com/forums/blog/576/entry-13181-part-6-of-11-simple-assembly-for-atari-basic/ Part 7 - Convert Integer to Hex String http://atariage.com/forums/blog/576/entry-13182-part-7-of-11-simple-assembly-for-atari-basic/ Part 8 - Convert Integer to Bit String http://atariage.com/forums/blog/576/entry-13183-part-8-of-11-simple-assembly-for-atari-basic/ Part 9 - Memory Copy http://atariage.com/forums/blog/576/entry-13184-part-9-of-11-simple-assembly-for-atari-basic/ Part 10 - Binary File I/O Part 1 (XIO is Broken) http://atariage.com/forums/blog/576/entry-13185-part-10-of-11-simple-assembly-for-atari-basic/ Part 11 - Binary File I/O Part 2 (XIO is Broken) http://atariage.com/forums/blog/576/entry-13186-part-11-simple-assembly-for-atari-basic-the-end/ ============================================================== The fun and games in programming for the Atari environment often requires manipulating hardware registers. Many features are enabled and disabled by individual bits in a byte. Additionally, the data used for many graphics features in the Atari 8-bit environment makes more sense when dealt with at the bit level. For instance, a character on the Atari's normal text screen (ANTIC Mode 2) can be flipped between inverse video and normal video by switching the high bit in the character byte on and off. The same bit in ANTIC modes 4 and 5 chooses between COLPF2 or COLPF3 color for certain bit patterns in the character data. The text color in ANTIC modes 6 and 7 is controlled by manipulating the two highest bits of the character byte. Pixels in a map mode are specified by single bits, pairs of bits, or groups of four bits. Bit-wise operations, simple in 6502 machine code, are painful and very slow in BASIC. Isolating a byte's individual bits in BASIC code is a horrible sight to behold. This is nothing like the two-byte DPeek and DPoke that BASIC can duplicate (slowly) with a multiply or divide. Bit work in BASIC requires repeated value testing and computation to identify which bits are set and which are clear. A simple example of the terror is presented here: 100 REM DISSECT BITS IN 16-BIT WORD 105 REM SHOWBITS.BAS 110 REM 115 WORD=35235:REM $89A3 120 BVAL=32768:REM $8000 125 ? "WORD = ";WORD 130 ? "BITS ="; 135 FOR BIT=0 TO 15 140 IF INT(BIT/4)=BIT/4 THEN ? " "; 145 IF WORD>=BVAL THEN ? "1";:WORD=WORD-BVAL:GOTO 155 150 ? "0"; 155 BVAL=BVAL/2 160 NEXT BIT 165 ? 170 END The code starts with the value of the highest (that is, 16th) bit of a word. If the test value is greater than or equal to the bit value then the bit is set. If not, then the bit is 0. Then the value of the bit position is divided by 2 to get the value of the next position. This evaluation and test action loops 16 times until all the bits are evaluated. One optional extra is inserted – before every four bits it outputs a space to separate the bits into groups corresponding to nybbles (half bytes). In terms of code this is about as simple as it gets. The routine could be made faster by packaging the bit values in an array and testing from the array. But the same problem remains: BASIC is looping and performing multiple operations in each loop. In this simple example it is scanning one 16-bit integer. Implementing useful bit operations requires dissecting two 16-bit integers and assembling output in another 16-bit integer. We really don't want to go there in BASIC. In defense of Atari BASIC, though it lacks bit operations, so do most other BASIC languages, because the original purpose of BASIC is to protect beginner programmers from the hardware. But, we're working on an Atari here and want to embrace the hardware. OSS BASIC XL provides several operator symbols for actions on bits: & == Bit-wise AND two 16-bit values ! == Bit-wise OR two 16-bit values % == Bit-wise EOR (Exclusive OR) two 16-bit values The 6502 supports these operations and also supports other bit manipulations that BASIC XL (and other more modern BASICs) do not support. Shifting bits and rotating bits in a byte are simple in the 6502. These could be interesting in a utility for BASIC. So, we will plan for machine language routines that do bitwise AND, OR, EOR operations and also support left and right shift, and left and right rotate. Since the USR() environment automatically provides 16-bit argument values to the machine language code a choice needs to be made: implement the routines to work on 16-bit values or just a single byte. In the case of AND, OR, and EOR there's little difference between supporting a byte or 16-bit operation. If the supplied argument values are only 8-bits (with 0 as the high byte) then the resulting computation will also appear to be 8-bits. Rotating and Shifting bits is a different situation. The results of shifting and rotating an 8-bit value is different from the same action on a 16-bit value. In most situations the Atari environment uses 8-bits as data (character sets, map graphics, player/missile data), so these bit operations are more likely to be useful on just a byte. Below is the list of operations to design. Each operation has two values, an operator, and a return value: Result == 16-bit value AND 16-bit value Result == 16-bit value OR 16-bit value Result == 16-bit value EOR 16-bit value Result == 8-bit value RIGHT SHIFT number of bits Result == 8 bit value LEFT SHIFT number of bits Result == 8-bit value RIGHT ROTATE number of bits Result == 8-bit value LEFT ROTATE number of bits Given the earlier Dpeek and Dpoke examples most readers are probably not looking forward to the tedious repetition of the argument setup and validation code for seven similar, separate routines. Nothing like that will happen. Instead, all the operations can be combined into one utility. This is simple (and sensible) to do, because all these operations use the same number of arguments and all return a value to BASIC. Since BASIC converts all arguments to 16-bit values, the operations that work on 8-bit values will use only the low byte of the value and ignore the high byte. The reader will be relieved to learn there will be just one instance of the initialization code for managing the stack and arguments. This is where the perceived overkill factor of the generic initialization code pays off. The next problem is how to choose one of seven different operations in one machine language utility. There are always several ways to solve a problem. One possibility is that each routine will have its own entry point relative to the start. Each bit operation routine would have to call the initialization and then somehow find its way back to the proper bit operation code. This has the problem that it is difficult to share the initialization code between all the operations and keep the routines relocatable. Simple relative branching keeps the code relocatable, but using JSR (Jump To Subroutine) to call the initialization code or the actual routines makes the code non-relocatable without a heap of other work. Another possibility is that the machine language routine uses the value identifying the bit operation as the basis for branching to the specific routine. This has more promise. It means the machine language code will test the argument for each bit operation identifier and then branch accordingly. A table of addresses for JMP or JSR would be faster, but it would not be easily relocatable. Keeping the code relocatable requires the entry for all routines fall within branching range of the decision making code. Generally speaking, the maximum distance of a branch is plus or minus about 127 bytes. The assembler is always more than happy to tell the programmer when target code is out of range of a branch. These bit-wise features are simple concepts for 6502 machine language, so the routines will not be large, but even if they were, there are ways around the problem of the half-page branch distance. BITS in Mac/65 Assembler Code 1000 ; BITS.M65 1005 ; 1010 ; Perform bit level operations 1015 ; for Atari BASIC. 1020 ; 1025 ; USR 3 arguments: 1030 ; Oper == Operation (1,2,...7) 1035 ; Val1 == First value. 1040 ; Val2 == Second Value. 1045 ; 1050 ; Operations: 1055 ; 1 = 16-bit Val1 OR 16-bit Val2 1060 ; 2 = 16-bit Val1 AND 16-bit Val2 1065 ; 3 = 16-bit Val1 EOR 16-bit Val2 1070 ; 4 = 8-bit Val1 LSR 8-bit num bits Val2 1075 ; 5 = 8-bit Val1 LSL 8-bit num bits Val2 1080 ; 6 = 8-bit Val1 ROR 8-bit num bits Val2 1085 ; 7 = 8-bit Val1 ROL 8-bit num bits Val2 1090 ; 1095 ; USR return value is the result. 1100 ; 1105 ; Use the FR0/FR1 FP register. 1110 ; The return value for BASIC 1115 ; goes in FR0. 1120 ; No FP is used so all of FR0 1125 ; (and more FP registers) can 1130 ; be considered available. 1135 ; 1140 ZRET = $D4 ; FR0 $D4/$D5 Return value 1145 ZARGS = $D5 ; $D6-1 for arg Pulldown loop 1150 ZVAL2 = $D6 ; FR0 $D6/$D7 Value 1155 ZVAL1 = $D8 ; FR0 $D8/$D9 Value 1160 ZOPER = $DA ; FR1 $DA/$DB Operation 1165 ; 1170 ; Define operations1175 ; 1180 OPER_OR = $01 1185 OPER_AND = $02 1190 OPER_EOR = $03 1195 OPER_LSR = $04 1200 OPER_LSL = $05 1205 OPER_ROR = $06 1210 OPER_ROL = $07 1215 ; 1220 .OPT OBJ 1225 ; 1230 ; Arbitrary. This is relocatable. 1235 ; 1240 *= $9500 1245 ; 1250 INIT 1255 LDA #$00 ; Make sure return 1260 STA ZRET ; value is cleared 1265 STA ZRET+1 ; by default. 1270 PLA ; Get argument count 1275 TAY 1280 BEQ EXIT ; Shortcut for no args 1285 ASL A ; Now number of bytes 1290 TAY 1295 CMP #$06 ; Value1, Value2, Oper 1300 BEQ PULLDOWN 1305 ; 1310 ; Bad args. Clean up for exit. 1315 ; 1320 DISPOSE ; any number of args 1325 PLA 1330 DEY 1335 BNE DISPOSE 1340 RTS ; Abandon ship 1345 ; 1350 ; This code works the same 1355 ; for 1, 4, 8 ... arguments. 1360 ; 1365 PULLDOWN 1370 PLA 1375 STA ZARGS,Y 1380 DEY 1385 BNE PULLDOWN 1390 ; 1395 ; Like a Switch/Case 1400 ; 1405 LDY ZOPER 1410 BEQ EXIT ; Zero operator 1415 ; 1420 DEY ; #$01 OR 1425 BEQ DO_OR 1430 ; 1435 DEY ; #$02 AND 1440 BEQ DO_AND 1445 ; 1450 DEY ; #$03 EOR 1455 BEQ DO_EOR 1460 ; 1465 DEY ; #$04 LSR 1470 BEQ DO_LSR 1475 ; 1480 DEY ; #$05 LSL 1485 BEQ DO_LSL 1490 ; 1495 DEY ; #$06 ROR 1500 BEQ DO_ROR 1505 ; 1510 DEY ; #$07 ROL 1515 BEQ DO_ROL 1520 ; 1525 EXIT 1530 RTS ; bye. 1535 ; 1540 ; 1 = 16-bit Val1 OR 16-bit Val2 1545 DO_OR 1550 LDA ZVAL1 1555 ORA ZVAL2 1560 STA ZRET 1565 LDA ZVAL1+1 1570 ORA ZVAL2+1 1575 STA ZRET+1 1580 RTS 1585 ; 1590 ; 2 = 16-bit Val1 AND 16-bit Val2 1595 DO_AND 1600 LDA ZVAL1 1605 AND ZVAL2 1610 STA ZRET 1615 LDA ZVAL1+1 1620 AND ZVAL2+1 1625 STA ZRET+1 1630 RTS 1635 ; 1640 ; 3 = 16-bit Val1 EOR 16-bit Val2 1645 DO_EOR 1650 LDA ZVAL1 1655 EOR ZVAL2 1660 STA ZRET 1665 LDA ZVAL1+1 1670 EOR ZVAL2+1 1675 STA ZRET+1 1680 RTS 1685 ; 1690 ; 4 = 8-bit Val1 LSR 8-bit num bits Val2 1695 DO_LSR 1700 LDA ZVAL1 1705 STA ZRET 1710 LDX ZVAL2 1715 BEQ EXIT 1720 LSR_LOOP 1725 LSR A 1730 DEX 1735 BNE LSR_LOOP 1740 STA ZRET 1745 RTS 1750 ; 1755 ; 5 = 8-bit Val1 LSL 8-bit num bits Val2 1760 DO_LSL 1765 LDA ZVAL1 1770 STA ZRET 1775 LDX ZVAL2 1780 BEQ EXIT 1785 LSL_LOOP 1790 ASL A 1795 DEX 1800 BNE LSL_LOOP 1805 STA ZRET 1810 RTS 1815 ; 1820 ; 6 = 8-bit Val1 ROR 8-bit num bits Val2 1825 DO_ROR 1830 LDA ZVAL1 1835 STA ZRET 1840 LDX ZVAL2 1845 BEQ EXIT 1850 ROR_LOOP 1855 CLC 1860 ROR A 1865 BCC OVER_ROR_INC 1870 ORA #$80 1875 OVER_ROR_INC 1880 DEX 1885 BNE ROR_LOOP 1890 STA ZRET 1895 RTS 1900 ; 1905 ; 7 = 8-bit Val1 ROL 8-bit num bits Val2 1910 DO_ROL 1915 LDA ZVAL1 1920 STA ZRET 1925 LDX ZVAL2 1930 BEQ EXIT 1935 ROL_LOOP 1940 CLC 1945 ROL A 1950 BCC OVER_ROL_INC 1955 ORA #$01 1960 OVER_ROL_INC 1965 DEX 1970 BNE ROL_LOOP 1975 STA ZRET 1980 RTS 1985 ; 1990 .END Now for the take-apart. The common initialization has these extra lines of code: 1255 LDA #$00 ; Make sure return 1260 STA ZRET ; value is cleared 1265 STA ZRET+1 ; by default. The program starts by clearing the response data. Since early exit may occur for different reasons in different places of the code, it makes sense to do this once rather than in several different places. When the code finds an error it can just return (RTS) to BASIC. There is nothing new with the DISPOSE and PULLDOWN loops – they are the same as prior examples. The next section is a simple switch/case-like decision blocks to identify which operator to use: 1395 ; Like a Switch/Case 1400 ; 1405 LDY ZOPER 1410 BEQ EXIT ; Zero operator 1415 ; 1420 DEY ; #$01 OR 1425 BEQ DO_OR 1430 ; 1435 DEY ; #$02 AND 1440 BEQ DO_AND . . . 1510 DEY ; #$07 ROL 1515 BEQ DO_ROL Zero is not a valid operation identifier, so that can be discarded quickly. Ordinarily, testing a list of values would look something like this which requires four bytes for each test: CPY #$01 BEQ DO_OR Since operator identifiers are sequential beginning from 1 the code can use a shortcut – it decrements the operator identifier and branches when reaching zero to the appropriate routine. This is still two instructions, but only three bytes (for a total of 21 bytes rather than 28 bytes to test 7 values). The OR, AND, and EOR routines are similar: 1540 ; 1 = 16-bit Val1 OR 16-bit Val2 1545 DO_OR 1550 LDA ZVAL1 1555 ORA ZVAL2 ; <- OR 1560 STA ZRET 1565 LDA ZVAL1+1 1570 ORA ZVAL2+1 ; <- OR 1575 STA ZRET+1 1580 RTS The only difference between each routine is the actual 6502 operation (ORA, AND, EOR) combining Value 1 and Value 2 which it then outputs to the return value for BASIC. The shift routines must loop since the 6502 shift instructions move the data by only one bit: 1690 ; 4 = 8-bit Val1 LSR 8-bit num bits Val2 1695 DO_LSR 1700 LDA ZVAL1 1705 STA ZRET 1710 LDX ZVAL2 1715 BEQ EXIT 1720 LSR_LOOP 1725 LSR A ; <- Can only move bits one position at a time 1730 DEX 1735 BNE LSR_LOOP 1740 STA ZRET 1745 RTS Each routine loops to shift only the low byte of Value 1 by the number of bits in Value 2 with the result output in the low byte of the return value for BASIC. Since the initialization cleared the entire return value, these routines do not need to worry about clearing the return value's high byte. The bit rotation routines: 1820 ; 6 = 8-bit Val1 ROR 8-bit num bits Val2 1825 DO_ROR 1830 LDA ZVAL1 1835 STA ZRET 1840 LDX ZVAL2 1845 BEQ EXIT 1850 ROR_LOOP 1855 CLC 1860 ROR A 1865 BCC OVER_ROR_INC 1870 ORA #$80 1875 OVER_ROR_INC 1880 DEX 1885 BNE ROR_LOOP 1890 STA ZRET 1895 RTS This is a little more complicated. The 6502 bit rotations can be thought of as operations using nine bits, not eight. The Carry bit does double duty. It provides the source bit value to move into the byte and then accepts the value of the bit that was rotated out of the byte. The problem here is that the bit moving out of the byte has to immediately be moved back into the other end of the byte. How the code solves this: it forces the Carry bit to zero (CLC) insuring a zero bit always rotates into the byte. After the rotation the code checks the bit that rotated out of the byte (in Carry). If the Carry bit is set then the code turns on the bit that should have rotated into the other end of the byte (here, ORA #$80). Testing BITS The Atari BASIC program below, TESTBITS.BAS, demonstrates the routines: 100 REM TEST BIT OPERATIONS UTILITY 105 REM 110 GRAPHICS 0:POKE 710,0:POKE 82,0 115 DIM B(3,1),N$(21) 120 GOSUB 10000:REM BITS UTILITY 125 RESTORE 155:REM BIT PATTERNS 130 FOR X=0 TO 1 135 FOR Y=0 TO 3 140 READ D:B(Y,X)=D 145 NEXT Y 150 NEXT X 155 DATA 0,257,0,257,0,0,258,258 160 REM ENUMERATE BIT OPERATIONS 165 READ BOR,BAND,BEOR,BLSR,BLSL,BROR,BROL 170 DATA 1,2,3,4,5,6,7 175 N$="OR ANDEORLSRLSLRORROL" 180 REM 185 REM TEST OR, AND, EOR 190 REM 195 FOR OPER=BOR TO BEOR 200 FOR Y=0 TO 3 205 V=USR(BITS,OPER,B(Y,0),B(Y,1)) 210 ? B(Y,0);" "; 215 ? N$(OPER*3-2,OPER*3);" "; 220 ? B(Y,1);" = ";V 225 NEXT Y 230 ? 235 NEXT OPER 240 REM 245 REM TEST SHIFT AND ROTATE 250 REM 255 V=129:REM $81 260 FOR OPER=BLSR TO BROL 265 FOR Y=0 TO 8 270 W=USR(BITS,OPER,V,Y) 275 ? V;" "; 280 ? N$(OPER*3-2,OPER*3);" "; 285 ? Y;" = ";W 290 NEXT Y 295 ? 300 NEXT OPER 305 END 9997 REM 9998 REM SETUP BITS ML UTILITY 9999 REM 10000 DIM BT$(162) 10001 BITS=ADR(BT$) 10002 RESTORE 27000 10003 FOR I=0 TO 161 10004 READ D:POKE BITS+I,D 10005 NEXT I 10006 RETURN 26996 REM H1:BITS.OBJ 26997 REM Size = 162 26998 REM Start = 38144 26999 REM End = 38305 27000 DATA 169,0,133,212,133,213,104,240 27001 DATA 43,10,168,201,6,240,5,104 27002 DATA 136,208,252,96,104,153,213,0 27003 DATA 136,208,249,164,218,240,21,136 27004 DATA 240,19,136,240,29,136,240,39 27005 DATA 136,240,49,136,240,61,136,240 27006 DATA 73,136,240,90,96,165,216,5 27007 DATA 214,133,212,165,217,5,215,133 27008 DATA 213,96,165,216,37,214,133,212 27009 DATA 165,217,37,215,133,213,96,165 27010 DATA 216,69,214,133,212,165,217,69 27011 DATA 215,133,213,96,165,216,133,212 27012 DATA 166,214,240,208,74,202,208,252 27013 DATA 133,212,96,165,216,133,212,166 27014 DATA 214,240,193,10,202,208,252,133 27015 DATA 212,96,165,216,133,212,166,214 27016 DATA 240,178,24,106,144,2,9,128 27017 DATA 202,208,247,133,212,96,165,216 27018 DATA 133,212,166,214,240,158,24,42 27019 DATA 144,2,9,1,202,208,247,133 27020 DATA 212,96 The program starts by building a table of bit combinations for testing OR, AND, and EOR. The non-zero decimal values correspond to integers with one bit set in the high byte, and a bit set in the low byte. However the low byte values are different bits. This helps demonstrate that the bit operations are working on both the high bytes and low bytes, and that the bits are combined as expected by the operation. The code loops through the OR, AND, and EOR operations, and for each operation loops through the table of bits displaying the results of the operations. The next section loops through the shift and rotate operations and for each operation it performs zero through 8 bit shifts displaying the results. Test program output: 0 OR 0 = 0 257 OR 0 = 257 0 OR 258 = 258 257 OR 258 = 259 0 AND 0 = 0 257 AND 0 = 0 0 AND 258 = 0 257 AND 258 = 256 0 EOR 0 = 0 257 EOR 0 = 257 0 EOR 258 = 258 257 EOR 258 = 3 129 LSR 0 = 129 129 LSR 1 = 64 129 LSR 2 = 32 129 LSR 3 = 16 129 LSR 4 = 8 129 LSR 5 = 4 129 LSR 6 = 2 129 LSR 7 = 1 129 LSR 8 = 0 129 LSL 0 = 129 129 LSL 1 = 2 129 LSL 2 = 4 129 LSL 3 = 8 129 LSL 4 = 16 129 LSL 5 = 32 129 LSL 6 = 64 129 LSL 7 = 128 129 LSL 8 = 0 129 ROR 0 = 129 129 ROR 1 = 192 129 ROR 2 = 96 129 ROR 3 = 48 129 ROR 4 = 24 129 ROR 5 = 12 129 ROR 6 = 6 129 ROR 7 = 3 129 ROR 8 = 129 129 ROL 0 = 129 129 ROL 1 = 3 129 ROL 2 = 6 129 ROL 3 = 12 129 ROL 4 = 24 129 ROL 5 = 48 129 ROL 6 = 96 129 ROL 7 = 192 129 ROL 8 = 129 That shows the operations are working as expected. That is, assuming one is willing to sit and visualize the bit patterns corresponding to each decimal value. For these purposes the output is less than ideal. Atari BASIC prints numbers as decimals. There is no built-in option to display bytes and integer values in ways more meaningful for 8-bit computer programming. There must be a better way… (Stay tuned for the next episode.) Below are the source files and examples of how to load the machine language routine into BASIC included in the disk image and archive: BITS File List: BITS.M65 Saved Mac/65 source BITS.L65 Mac/65 source listing BITS.T65 Mac/65 source listed to H6: (linux) BITS.ASM Mac/65 assembly listing BITS.TSM Mac/65 assembly listing to H6: (linux) BITS.OBJ Mac/65 assembled machine language program (with load segments) BITS.BIN Assembled machine language program without load segments BITS.LIS LISTed DATA statements for BITS.BIN routine. BITS.TLS LISTed DATA statements for BITS.BIN routine to H6: (linux) MAKEBITS.BAS BASIC program to create the BITS.BIN file. This also contains the BITS routine in DATA statements. MAKEBITS.LIS LISTed version of MAKEBITS.BAS MAKEBITS.TLS LISTed version of MAKEBITS.BAS to H6: (linux) SHOWBITS.BAS Example BASIC program dissecting bits in a 16-bit integer. SHOWBITS.LIS LISTed version of SHOWBITS.BAS SHOWBITS.TLS LISTed version of SHOWBITS.BAS to H6: (linux) TESTBITS.BAS BASIC program that tests the BITS USR() routines. TESTBITS.LIS LISTed version of TESTBITS.BAS. TESTBITS.TLS LISTed version of TESTBITS.BAS to H6: (linux) ZIP archive of files: Bits_Disk.zip Tar archive of files (remove the .zip after download) Bits_Disk.tgz.zip For everyone who has been born of God overcomes the world. And this is the victory that has overcome the world—our faith. 1 John 5:4
  10. Binary File I/O (Part 2 of 2) ============================================================== Part 1 - Introduction http://atariage.com/forums/blog/576/entry-13175-part-1-of-11-simple-assembly-for-atari-basic/ Part 2 - Learn 82.7% of Assembly Language in About Three Pages http://atariage.com/forums/blog/576/entry-13176-part-2-of-11-simple-assembly-for-atari-basic/ Part 3 - The World Inside a USR() Routine http://atariage.com/forums/blog/576/entry-13177-part-3-of-11-simple-assembly-for-atari-basic/ Part 4 - Implement DPEEK() http://atariage.com/forums/blog/576/entry-13178-part-4-of-11-simple-assembly-for-atari-basic/ Part 5 - Implement DPOKE http://atariage.com/forums/blog/576/entry-13180-part-5-of-11-simple-assembly-for-atari-basic/ Part 6 - Various Bit Manipulations http://atariage.com/forums/blog/576/entry-13181-part-6-of-11-simple-assembly-for-atari-basic/ Part 7 - Convert Integer to Hex String http://atariage.com/forums/blog/576/entry-13182-part-7-of-11-simple-assembly-for-atari-basic/ Part 8 - Convert Integer to Bit String http://atariage.com/forums/blog/576/entry-13183-part-8-of-11-simple-assembly-for-atari-basic/ Part 9 - Memory Copy http://atariage.com/forums/blog/576/entry-13184-part-9-of-11-simple-assembly-for-atari-basic/ Part 10 - Binary File I/O Part 1 (XIO is Broken) http://atariage.com/forums/blog/576/entry-13185-part-10-of-11-simple-assembly-for-atari-basic/ Part 11 - Binary File I/O Part 2 (XIO is Broken) http://atariage.com/forums/blog/576/entry-13186-part-11-simple-assembly-for-atari-basic-the-end/ ============================================================== New XIO in Mac/65 Assembler Code Many articles on this subject go by a simple route – use BASIC code to set up all the IOCB values for the 7/Get Bytes or 11/Put Bytes commands, and then provide a minimal assembly routine that simply calls the CIO vector. While BASIC mangles the binary read/write functions, XIO itself also is sufficiently broken to justify a complete machine language USR() routine that exercises CIO commands the way they were intended. For example, ICAX values are not always needed or even wanted, but XIO requires the values. In fact, ICAX values are rarely needed outside of the 3/Open command. Similarly, the filespec/buffer is often not necessary. This routine will perform the same purpose of XIO, but allow a variable number of arguments, adding more arguments only as needed: Always required (2 arguments): 1) Channel Number – Only low byte values 0 to 7 accepted. The high byte is ignored. 2) CIO Command – Only the low byte of the argument will be used. Optional (4 arguments): 3) Filespec/Buffer address – 16-bit value 4) Filespec/Buffer length – 16-bit value Additionally optional when the Filespec/Buffer is provided (5 arguments): 5) ICAX1 – the low byte of this argument is used. High byte is ignored. Additionally optional when the Filespec/Buffer and ICAX1 are provided (6 arguments): 6) ICAX2 – the low byte of this argument is used. High byte is ignored. Since the routine accepts 2, 4, 5, or 6 arguments it can support any of the following: 2) USR(NEWXIO,6,18) – Perform command 18/Fill for screen device (assuming channel 6) 4) USR(NEWXIO,1,254,ADR(F$),LEN(F$)) - Use channel 1 to format (254) the disk drive described by F$. This would also be the format/number of arguments needed for performing CIO Commands 7/Get Bytes and 11/Put Bytes. 5) USR(NEWXIO,5,3,ADR(F$),LEN(F$),6) - Use channel 5 to open (3) the disk directory (read/4 + directory/2 = 6) described by F$ 6) USR(NEWXIO,6,3,ADR(F$),LEN(F$),28,7) – Use channel 6 to open (3) as graphics mode 7 with a text window (read/4 + write/8 + window/16 = 28) assuming F$ describes “S:” Variable number of arguments means this program is organized differently from the prior utilities that copy stack arguments to Page Zero. Since this is just an interface for putting values into the IOCB it doesn't make use of Page Zero beyond returning a status value to BASIC. 0100 ; NXIO.M65 0105 ; 0110 ; NEW CIO/XIO INTERFACE 0115 ; 0120 ; SETUP IOCB WITH THE SUPPLIED 0125 ; ARGUMENTS AND CALL CIO 0130 ; 0135 ; USR 2, 4, 5, or 6 ARGUMENTS: 0140 ; CHANNEL == IOCB CHANEL (LOW BYTE) 0145 ; COMMAND == CIO COMMAND (LOW BYTE) 0150 ; BUF ADR == ADDRESS OF BUFFER 0155 ; BUF LEN == LENGTH OF BUFFER 0160 ; ICAX1 == CIO ICAX1 VALUE (LOW BYTE) 0165 ; ICAX2 == CIO ICAX2 VALUE (LOW BYTE) 0170 ; 0175 ; RETURN VALUE IS CIOV RESULT IN Y REG 0180 ; 0185 ZRET = $D4 ; FR0 $D4/$D5 Return Value 0190 ; 0195 CIOV = $E456 ; CIO Vector 0200 ; 0205 IOCB = $0340 ; Base IO Control Block 0210 ICHID = IOCB+$00 ; Handler ID 0215 ICDNO = IOCB+$01 ; Device number 0220 ICCMD = IOCB+$02 ; ** CIO Command 0225 ICSTA = IOCB+$03 ; CIO Status 0230 ICBAL = IOCB+$04 ; ** Buffer address (low) 0235 ICBAH = IOCB+$05 ; ** Buffer address (high) 0240 ICPTL = IOCB+$06 ; Put char routine (low) 0245 ICPTH = IOCB+$07 ; Put char routine (high) 0250 ICBLL = IOCB+$08 ; ** Buffer length (low) 0255 ICBLH = IOCB+$09 ; ** Buffer length (high) 0260 ICAX1 = IOCB+$0A ; ** Aux Byte 1 0265 ICAX2 = IOCB+$0B ; ** Aux Byte 2 0270 ICAX3 = IOCB+$0C ; Aux Byte 3 0275 ICAX4 = IOCB+$0D ; Aux Byte 4 0280 ICAX5 = IOCB+$0E ; Aux Byte 5 0285 ICAX6 = IOCB+$0F ; Aux Byte 6 0290 ; 0295 .OPT OBJ 0300 ; 0305 *= $9000 ; Arbitrary. this is relocatable 0310 ; 0315 INIT 0320 LDY #$FF ; Make the return 0325 STY ZRET ; value -1 ($FFFF) 0330 STY ZRET+1 ; by default. 0335 ; 0340 PLA ; Get argument count 0345 BEQ BYE ; Shortcut for no args. 0350 ; 0355 TAY 0360 ; 0365 CMP #$01 ; One arg is not enough. 0370 BEQ DISPOSE 0375 ; 0380 CMP #$03 ; Three args is not supported. 0385 BEQ DISPOSE 0390 ; 0395 CMP #$07 ; More than six is not valid. 0400 BCC DO_CHANNEL ; All good. Ready to pull args. 0405 ; 0410 ; Bad arg count. Clean up for exit. 0415 ; 0420 DISPOSE ; Any number of arguments 0425 PLA 0430 PLA 0435 DEY 0440 BNE DISPOSE 0445 RTS ; Abandon ship. 0450 ; 0455 ; Pull channel and multiply times 16 0460 ; 0465 DO_CHANNEL ; Arg 1 = Channel 0470 DEY ; subtract one arg 0475 PLA ; discard high byte 0480 PLA ; Channel number 0485 CMP #$08 ; More than 7 channels 0490 BCS DISPOSE ; is invalid. 0495 ASL A ; * 2 0500 ASL A ; * 4 0505 ASL A ; * 8 0510 ASL A ; * 16 0515 TAX 0520 ; 0525 DO_ICCMD ; Arg 2 = Command 0530 PLA ; discard high byte 0535 PLA ; command byte 0540 STA ICCMD,X ; Store Command in IOCB 0545 DEY ; subtract one arg 0550 BEQ DO_CIO0555 ; 0560 DO_ICBA ; Arg 3 = Buffer Address 0565 PLA ; Address high byte 0570 STA ICBAH,X 0575 PLA ; Address low byte 0580 STA ICBAL,X 0585 DEY ; subtract one arg 0590 ; 0595 DO_ICBL ; Arg 4 = Buffer Length 0600 PLA ; Length high byte 0605 STA ICBLH,X 0610 PLA ; Length low byte 0615 STA ICBLL,X 0620 DEY ; subtract one arg 0625 BEQ DO_CIO 0630 ; 0635 DO_ICAX1 ; Arg 5 = Aux Byte 1 0640 PLA ; discard high byte 0645 PLA ; Aux byte 0650 STA ICAX1,X ; Store AUX1 in IOCB 0655 DEY ; subtract one arg 0660 BEQ DO_CIO 0665 ; 0670 DO_ICAX2 ; Arg 6 = Aux Byte 2 0675 PLA ; discard high byte 0680 PLA ; Aux byte 0685 STA ICAX2,X ; Store AUX2 in IOCB 0690 DEY ; This should be zero args now... 0695 ; 0700 DO_CIO ; IOCB is set, now execute... 0705 STY ZRET ; Clear return value low 0710 STY ZRET+1 ; and high byte. 0715 JSR CIOV ; Engage, Mr Crusher. 0720 BPL BYE ; No error 0725 STY ZRET ; Copy Y to return value 0730 ; 0735 BYE 0740 RTS 0745 ; 0750 .END The initialization is similar to prior utilities. It begins by setting the return value to a known value ($FFFF) that cannot be returned by a successful exit. Then it pulls the argument count and does a series of value checks to identify any invalid number of arguments. If the code identifies an issue here it branches to cleaning the stack and then exits. One difference in the stack argument management is that this utility does not double the argument count to derive the number of bytes on the stack, because it will not be looping to copy the stack values into Page Zero. The channel handling is more involved than other arguments: 0455 ; Pull channel and multiply times 16 0460 ; 0465 DO_CHANNEL ; Arg 1 = Channel 0470 DEY ; subtract one arg 0475 PLA ; discard high byte 0480 PLA ; Channel number 0485 CMP #$08 ; More than 7 channels 0490 BCS DISPOSE ; is invalid. 0495 ASL A ; * 2 0500 ASL A ; * 4 0505 ASL A ; * 8 0510 ASL A ; * 16 0515 TAX The channel is pulled from the low byte of the argument. If the value exceeds the range of available channels, then it diverts to the stack cleanup to dispose of the remaining arguments and exits. Recall the earlier discussion about identifying the IOCB for a specific channel -- multiply the channel number times 16 and add to $340. Here the code multiplies the channel number by 16 allowing use of the value as an index to load values into the correct IOCB. The remaining arguments are handled similarly: 0525 DO_ICCMD ; Arg 2 = Command 0530 PLA ; discard high byte 0535 PLA ; command byte 0540 STA ICCMD,X ; Store Command in IOCB 0545 DEY ; subtract one arg 0550 BEQ DO_CIO The values are pulled from the stack and stored in the corresponding IOCB field. Then the argument counter is decremented. At the end of processing the arguments for command, buffer length, and ICAX1 (arguments 2, 4, and 5) the argument count is tested if it has reached zero. If this occurs then the program skips over the work for processing any subsequent arguments. Finally, it gets down to business: 0700 DO_CIO ; IOCB is set, now execute... 0705 STY ZRET ; Clear return value low 0710 STY ZRET+1 ; and high byte. 0715 JSR CIOV ; Engage, Mr Crusher. 0720 BPL BYE ; No error 0725 STY ZRET ; Copy Y to return value Recall that the Y register is used to count arguments and by the time the routine reaches this point the Y register is guaranteed to contain zero. So, this is a convenient source to clear the high byte of the return value for BASIC. Next, the code calls the CIO Vector ($E456). When the CIO routine returns the error code is in the Y register and the utility copies that value to the low byte of the return value. Let's go over a couple implications when this is used in BASIC: 1. The function accepts an absolute address and a length allowing access to any part of memory. While this is much more flexible than XIO it also means that this routine cannot directly accept a BASIC string. This routine can use a string passed by its address via ADR(). It is also up to the BASIC program to pass the correct length. LEN() is correct only when the string has defined content, so a BASIC program must fill or pad out the string to its expected length. 2. Since this is a USR() routine it is not integrated in BASIC's error handling. Therefore TRAP cannot trap any Input/Output errors. The BASIC program must check the return value of the NXIO routine or use the STATUS command to identify problems. Testing New XIO Now that we have some experience using files for binary data we're going to start with something different. The Atari BASIC program below, MAKENXIO.BAS, creates a binary file containing the machine language code for the NXIO routine. 1 REM MAKENXIO.BAS 5 REM CREATE NXIO.BIN FILE 10 OPEN #1,8,0,"H1:NXIO.BIN" 15 FOR I=1 TO 94 20 READ D:PUT #1,D 25 NEXT I 30 FOR I=95 TO 255 35 PUT #1,0 40 NEXT I 45 CLOSE #150 END 21996 REM H1:NXIO.OBJ 21997 REM SIZE = 94 21998 REM START = 36864 21999 REM END = 36957 22000 DATA 160,255,132,212,132,213,104,240 22001 DATA 84,168,201,1,240,8,201,3 22002 DATA 240,4,201,7,144,6,104,104 22003 DATA 136,208,251,96,136,104,104,201 22004 DATA 8,176,243,10,10,10,10,170 22005 DATA 104,104,157,66,3,136,240,34 22006 DATA 104,157,69,3,104,157,68,3 22007 DATA 136,104,157,73,3,104,157,72 22008 DATA 3,136,240,14,104,104,157,74 22009 DATA 3,136,240,6,104,104,157,75 22010 DATA 3,136,132,212,132,213,32,86 22011 DATA 228,16,2,132,212,96 22012 DATA 67,3,133,212,96 This will make managing the utility easier, allowing the test program (and any other BASIC program) to load the utility directly from the file without reading DATA statements. Note that the program purposely pads the output to 255 bytes, so that a BASIC program can use the (broken) XIO command to load the binary data. Next, is the test program that exercises the features of NXIO. It begins by loading the NXIO machine language routine into a string using XIO. This is acceptable for the tightly confined usage here – the program does only one operation to read a file of 255 bytes. The remaining CIO activity in the program is run by the shiny, new NXIO routine: 100 REM TSTNXIO1.BAS10 5 REM TEST THE NEW XIO USR ROUTINE 110 POKE 82,0:GRAPHICS 0 115 DIM NXIO$(255):NXIO$(255)=" " 120 NXIO=ADR(NXIO$) 125 REM READ NXIO FROM FILE 130 OPEN #1,4,0,"H1:NXIO.BIN" 135 XIO 7,#1,4,0,NXIO$ 140 CLOSE #1 145 REM 150 REM TEST THE BAD ARG EXIT 155 REM TEST BAD ARGS 0160 ? "TESTING BAD ARGUMENTS..." 165 BADARG=USR(NXIO) 170 ? "BAD ARGS 0 = ";BADARG 175 REM TEST BAD ARGS 1 180 BADARG=USR(NXIO,3) 185 ? "BAD ARGS 1 = ";BADARG 190 REM TEST BAD ARGS 3 195 BADARG=USR(NXIO,3,3,32000) 200 ? "BAD ARGS 3 = ";BADARG 205 REM TEST BAD ARGS 7 210 BADARG=USR(NXIO,3,3,32000,2,3,3,3) 215 ? "BAD ARGS 7 = ";BADARG 220 GOSUB 595 225 REM 230 REM DO CIO 3/OPEN TO SET UP 235 REM A GRAPHICS MODE AND RUN 240 REM THE 18/FILL COMMAND.2 45 REM FORCE "S:" CLOSED 250 CLOSE #6 255 REM OPEN AS GR MODE 5, NO WINDOW 260 GROPEN=USR(NXIO,6,3,ADR("S:"),2,12,5) 265 REM SAME AS EARLIER XIO FILL DEMO 270 COLOR 3 275 PLOT 70,45:DRAWTO 50,10 280 DRAWTO 30,10:POSITION 10,45 285 POKE 765,3290 XFILL=USR(NXIO,6,18) 295 GOSUB 600:REM WAIT FOR A KEY 300 REM REPORT THE RESULTS 305 GRAPHICS 0 310 ? "GROPEN = ";GROPEN 315 ? "XFILL = ";XFILL 320 GOSUB 595 325 REM 330 REM GAMES WITH BINARY FILES 335 REM LOAD THE 8 BYTE MEMORY FILE 340 DIM D$($="!!!!!!!!" 345 ? "LOADING 8 BYTE MEMORY.BIN..." 350 OPEN #1,4,0,"H1:MEMORYT0.BIN" 355 XREAD8=USR(NXIO,1,7,ADR(D$), 360 CLOSE #1 365 FOR I=1 TO 8 370 ? ASC(D$(I,I)), 375 NEXT I 380 ? "XREAD8 = ";XREAD8 385 GOSUB 595 390 REM 395 REM SAVE THE ROM CHARACTER SET 400 CR=57 344:REM ROM SET $E000 405 ? "SAVING ROM CHARACTER SET..." 410 OPEN #1,8,0,"H1:CSET.BIN" 415 XSAVE=USR(NXIO,1,11,CR,1024) 420 CLOSE #1 425 ? "XSAVE = ";XSAVE 430 GOSUB 595 435 REM 440 REM GAMES WITH THE BINARY LOAD 445 REM SETUP SCREEN FIRST... 450 GRAPHICS 0:POSITION 0,12 455 SC=PEEK(88)+256*PEEK(89) 460 FOR Y=0 TO 7 465 FOR X=0 TO 31 470 POKE SC+Y*40+X,Y*32+X 475 NEXT X 480 NEXT Y 485 ? "NORMAL CSET DISPLAYED" 490 ? "TO LOAD SOFT SET" 495 GOSUB 595 500 REM 505 REM LOAD THE SOFT SET IN 510 REM FLIPPED HALF SETS 515 CH=36864:REM SOFT SET $9000 520 POKE 756,144 525 OPEN #1,4,0,"H1:CSET.BIN" 530 CSLOAD1=USR(NXIO,1,7,CH+512,512) 535 CSLOAD2=USR(NXIO,1,7,CH,512) 540 CLOSE #1 545 ? "SWAPPED, SOFT CSET CSET DISPLAYED" 550 GOSUB 595 555 REM 560 REM THE END 565 GRAPHICS 0 570 ? "CSLOAD1 = ";CSLOAD1 575 ? "CSLOAD2 = ";CSLOAD2 580 END 585 REM 590 REM WAIT FOR A KEY 595 ? "PRESS A KEY" 600 OPEN #1,4,0,"K:" 605 POKE 764,255 610 GET #1,A 615 CLOSE #1620 RETURN The program begins by reading the machine language routine via XIO into a string 255 characters long. Yes, the routine is actually only 94 bytes long, so it wastes a bit of space. Such is life when using XIO. The first round of tests validates the argument management. There is a separate test for each bad argument possibility – 0, 1, 3, and 7 (or greater). Each failure to start results in error code 65535 from NXIO: The next round of tests uses NXIO with all the supported arguments to open a graphics mode 5 display with no text window. Then it draws a shape and uses NXIO to execute the 18/Fill command: After the fill completes press a key to continue and then the program prints the NXIO exit codes for the Graphics Open and the Fill: Press a key again and the program runs the next test which uses 7/Get Characters to read the 8-byte MEMORYT0.BIN file created earlier. After loading the file the program prints the ATASCII codes for the bytes in the string. It should report the values below, and then print the exit code from NXIO for the 8-byte read.: Press a key after this to run the next test. This will use the 11/Put Characters to save the entire 1,024 bytes of the Atari's ROM character set to a file. Then it will print the exit code from the NXIO routine for the operation: Press a key to run the next test. This will prepare the screen to demonstrate loading the character set from the file into memory. The characters are displayed in the internal order. Note the order of the characters. Press a key to continue the test. The program will use 7/Get Characters to load the first 512 bytes from the file into the second 512 bytes of the soft character set in memory, and then it loads the second set of 512 bytes from the file into the first 512 bytes of the soft character set in memory. This effectively swaps the images of the first half of the character set with the second half. Observe the “changed” order of the characters: Finally, press a key to return the display to the normal character set and the program will display the return codes from NXIO for the loading activities and then it ends: The tests all work as expected, especially all the uses of 7/Get Characters and 11/Put Characters. So, there is no problem with CIO. The problem really is that BASIC's XIO command unnecessarily manages the interface to CIO commands. Correct use of the CIO commands is so simple and the behavior XIO implements is so involved and complicated that it is difficult to consider it simply an accident. What could be the reason for XIO's bizarrely over-engineered behavior? Perhaps at the time Atari BASIC was designed there was an expectation that these I/O operations must work in increments no less and no more than 255 bytes. Perhaps a misunderstanding between OS design and BASIC design? Perhaps design requirements were in motion and it was too late to fix the behavior. Truly weird. Below is a list of the source files and test examples from the New XIO discussion available in the disk image and archive. The files are listed in the order presented during the discussion. The BASIC programs come in the tokenized BASIC format ending in .BAS. Two listings in text format are also provided: Atari ATASCII format as .LIS and unix/linux text format ending in .TLS. New XIO File List: MSAVEDAT BASIC program to PRINT eight values to a file. MSAVEDT0 BASIC program to PUT eight bytes to a file. MLOADDT0 BASIC program to GET eight bytes from a file. XIOFILL BASIC program performing the XIO fill command using device “Q:” MSAVEDT2 BASIC program using XIO for 11/Put Bytes to write a file. MSAVEDT3 BASIC program using XIO for 11/Put Bytes to write data to a file with a trailing string to identify the source of excess file data. MLOADDT BASIC program using XIO for 7/Get Bytes. MLOADDTX BASIC program using XIO for 7/Get Bytes with a trailing string to detect excess data read from the file. MSAVE512 BASIC program to generate a file containing 512 bytes. MLOAD512 BASIC program using XIO for 7/Get Bytes attempting to load 512 bytes from a file. MLOAD8 BASIC program using XIO for 7/Get Bytes attempting to load bytes from a file containing 8 bytes. NXIO.M65 Saved Mac/65 source NXIO.L65 Mac/65 source listing NXIO.T65 Mac/65 source listed to H6: (linux) NXIO.ASM Mac/65 assembly listing NXIO.TSM Mac/65 assembly listing to H6: (linux) NXIO.OBJ Mac/65 assembled machine language program (with load segments) NXIO.BIN Assembled machine language program without load segments with additional data padded to the end of the file to make it 255 bytes long. NXIO.DAT LISTed DATA statements for NXIO machine language routine. MAKENXIO BASIC program to create the BIN file with padding to 255 bytes so the file can be loaded using XIO to read the binary data. TSTNXIO1 BASIC program testing the NXIO USR() routines for various CIO commands. ZIP archive of files: NXIO_Disk.zip Tar archive of files (remove the .zip after download) NXIO_Disk.tgz.zip Game Over The movie really is over this time. Thanks for playing. Enjoy the new toys. Finally, welcome to the new world of assembly language. Try to think of new ways to accelerate and improve BASIC programs. - End - Blessed is the man who walks not in the counsel of the wicked, nor stands in the way of sinners, nor sits in the seat of scoffers; Psalm 1:1
  11. Binary File I/O (Part 1 of 2) ============================================================== Part 1 - Introduction http://atariage.com/forums/blog/576/entry-13175-part-1-of-11-simple-assembly-for-atari-basic/ Part 2 - Learn 82.7% of Assembly Language in About Three Pages http://atariage.com/forums/blog/576/entry-13176-part-2-of-11-simple-assembly-for-atari-basic/ Part 3 - The World Inside a USR() Routine http://atariage.com/forums/blog/576/entry-13177-part-3-of-11-simple-assembly-for-atari-basic/ Part 4 - Implement DPEEK() http://atariage.com/forums/blog/576/entry-13178-part-4-of-11-simple-assembly-for-atari-basic/ Part 5 - Implement DPOKE http://atariage.com/forums/blog/576/entry-13180-part-5-of-11-simple-assembly-for-atari-basic/ Part 6 - Various Bit Manipulations http://atariage.com/forums/blog/576/entry-13181-part-6-of-11-simple-assembly-for-atari-basic/ Part 7 - Convert Integer to Hex String http://atariage.com/forums/blog/576/entry-13182-part-7-of-11-simple-assembly-for-atari-basic/ Part 8 - Convert Integer to Bit String http://atariage.com/forums/blog/576/entry-13183-part-8-of-11-simple-assembly-for-atari-basic/ Part 9 - Memory Copy http://atariage.com/forums/blog/576/entry-13184-part-9-of-11-simple-assembly-for-atari-basic/ Part 10 - Binary File I/O Part 1 (XIO is Broken) http://atariage.com/forums/blog/576/entry-13185-part-10-of-11-simple-assembly-for-atari-basic/ Part 11 - Binary File I/O Part 2 (XIO is Broken) http://atariage.com/forums/blog/576/entry-13186-part-11-simple-assembly-for-atari-basic-the-end/ ============================================================== Sidebar: This section turned out to be considerably more difficult and time consuming to write than anticipated. No two sources agree completely on this subject. The only consistency I found is the list of CIO and XIO command numbers. Everything else encountered documents this subject with varying amounts of accuracy. Descriptions of the CIO and XIO commands sometimes differ in the just names, but in ways that imply different expectations for results. Detailed descriptions of the commands vary from the strangely incomplete to being outright wrong. One guide for machine language on this subject described CIO features using the BASIC XIO limitations. Another tutorial declared that only 155 bytes could be read in one operation. In the end it took writing test programs in BASIC and Assembly to understand precisely how XIO commands work vs how the corresponding CIO commands actually do work. If anyone cares, the stuff that worked is derived from reading De Re Atari (50%, which was mostly correct), Compute!'s Mapping the Atari (20% and it has a couple mistakes), Atari's BASIC Reference Manual (15% which was painful and incomplete), and rest from several 6502 programming manuals that were altogether horriffic. Stating that programs work with data would be borderline silly. Everything about programs is about working with data – they calculate data, manipulate data, and copy data. A fundamental concern of programming is how to introduce data to the program and get it back out. Many programs have their data built into them or receive data by computing values, reading from storage, or by other input/output device or communications. A file contains data. A serial port sends and receives data. A joystick provides data. Numbers are data, text is data, graphic information is data. Data, Data, Data. Atari BASIC programs have access to several data acquisition methods. Data may be stored in a program using DATA statements. Data may be read from a file, or from user input. Although all data is ultimately represented as bytes in memory, BASIC languages provide a higher abstraction where the data it accepts is usually expressed as text and numbers, and even in the case of number values the input and output data is expressed as the text equivalent of the number value. This means data presented to BASIC is typically not in its final, usable form for the computer. The BASIC language reads the text data then converts it into a format the computer understands. Although an Atari BASIC program can contain its own data, it cannot have the data built into it in a form that is immediately usable. For instance, variables and array elements must be specifically initialized. The program must assign the values as it runs. There is not even a default behavior to clear numeric array values to zero. Data contained in DATA statements is not immediately usable by the Atari's unique features. The Atari's custom hardware features often use blocks of binary data – Graphics data, character set data, Player/Missile images, etc. The Atari BASIC DATA statement cannot present arbitrary binary data. It can only present decimal numbers and strings with limitations on the characters that can be included in the string. Like most BASIC languages, Atari BASIC has little provision for dealing with data in the computer's terms other than PEEK and POKE. Most Atari BASIC programs creating data in memory for Atari graphics features will read DATA statements – the text equivalent of number values – and then POKE those values as bytes into memory. This consumes a lot of storage space in the BASIC program. The byte value 192 in a DATA statement is presented as the text characters “1”, “9”, and “2” and then if there is another value the comma separator between the values also occupies memory. This one value to POKE into one byte of memory requires four supporting bytes in DATA. And then after the program stores the value in memory the DATA statement continues to occupy memory, wasting space. Wasted space means reduced space for code and reduced features in large programs. In addition to DATA's memory space issue the other problem with reading DATA statements is BASIC's slow execution time. BASIC must loop for every data value; reading the data, storing it into memory, and then reading more data. Any significant amount of data provides a long, boring wait for the user. Many BASIC games share this “feature” of making the user wait for a long program initialization. The second test program for the BITS utilities illustrates this problem. The time to store several machine language utilities totaling only about 300 bytes in memory was long enough that the program's loading section was modified to output helpful progress messages to assure the user the program had not crashed. Now consider that one complete character set is 1,024 bytes, and a complicated program may need to setup several thousand bytes of data. Assembly language and some compiled languages do not have these same issues with data storage space. These languages can declare where data will occupy memory and define the initial values. This is saved with the assembled/compiled machine language program, so the same mechanism that loads the machine language program into memory also establishes the organized, initialized data in memory. So, what to do about BASIC's problems? Eliminating the space-wasting behavior means eliminating DATA statements filled with numbers, or somehow optimizing the contents. Strings could be used to represent a lot of data. One character of a string is conveniently one byte of information. But strings still have a few problems: ATASCII characters are not always the same as binary byte values, so some translation is needed. Representing cursor control characters can be problematic when a program is LIST'ed and then ENTER'ed. There are two characters that cannot be expressed at all in a quoted string – the ATASCII End of Line character and the quote character itself. If the data includes either of these unpresentable characters then the string as DATA must replace them with a different value that can be included, and then after assigning the string it must go back and insert the problem characters into their correct positions in the string. This means adding more data to identify the locations of these special characters. Furthermore, the DATA statements filled with strings still occupy memory after the data is assigned to a string variable, though the waste factor for string characters is closer to 1:1 rather than roughly 4:1 for numeric (byte) data. If a BASIC program completely eliminates the DATA statements then where else can a program get its data? Here is an idea – This program loads values into memory from DATA: 10 RESTORE 50 20 FOR I = 0 TO 7 30 READ D:POKE 1536+I,D 40 NEXT I 50 DATA 2,34,27,155,132,130,129,128 That program above has the same end result as this program loading values into memory from a file: 10 OPEN #3,4,0,"D1:MEMORY.BIN" 20 FOR I = 0 TO 7 30 INPUT #1,D:POKE 1536+I,D 40 NEXT I 50 CLOSE #1 The difference is that the file reading method leaves behind no redundant DATA occupying valuable code and memory space (aside from the code to load the data.) Whether reading eight bytes or 800 bytes the amount of code to read from a file is the same. So, how does the data get into the file? A little one-time work is required to write the data into the file. Here is the same original code, but instead of POKE'ing the data into memory it writes the data out to the file. Then, the original program can be changed to read the data from the file and POKE that into memory, and so eliminate the DATA statements. 1 REM MSAVEDAT.BAS 5 REM SAVE FILE FROM DATA 10 RESTORE 50 15 OPEN #1,8,0,"H1:MEMORY28.BIN" 20 FOR I=0 TO 7 30 READ D:? #1;D 40 NEXT I 45 CLOSE #1 50 DATA 2,34,27,155,132,130,129,128 There is a question that is not obvious at this point, but will be wedged in here now, because the answer determines the way the code should write data to and read data from the file, and ultimately the direction of an assembly language utility. The question is, “What data is in the file?” Most of the time I work with the Atari800 or Atari++ emulators in Linux to extend the lifespan of the real 8-bit hardware, so here I will detour into Linux for the benefit of tools that show exactly what is in the data file. First of all, the file itself containing the data written for the 8 bytes: -rw-rw-rw- 1 kenjen users 28 Feb 24 18:15 MEMORY28.BIN This file is intended to contain data for 8 bytes, so then why does the directory listing report the file contains 28 bytes? A hex dump utility shows the file contains the following information: 0000 32 9b 33 34 9b 32 37 9b 31 35 35 9b 31 33 32 9b 2.34.27. 155.132. 0010 31 33 30 9b 31 32 39 9b 31 32 38 9b 130.129. 128. The right side of the hex dump provides the explanation. The file contains the text representation (ASCII/ATASCII) of the numeric values. The byte $9B is the ATASCII CHR$(155), the Atari's End Of Line character, which appears after each value. Recall that BASIC prefers to read and write data using text representation of values. The example program uses PRINT (or ?) and INPUT which quietly translate between an internal representation and the text representation. This is BASIC's data abstraction versus the programmer's desire to have BASIC do what is meant, not what is written. The program's (poorly expressed) intent is to store and read bytes of memory. However, BASIC treats the numbers as bytes only within the context of PEEK and POKE instructions. So, then how to get data into the file that is just discrete byte values? Single characters are bytes, so using the CHR$() function will output numeric values as single characters (aka bytes), so that's potentially workable for output. But, then how about reading the bytes? A program using INPUT will still read the file contents as a string which means it will try to read bytes until it reaches an End of Line ($9B) character. So, this is also not a workable solution. Atari BASIC provides a method to write and read a file one byte at a time with the commands PUT and GET. The same example program using PUT instead of PRINT (or ?): 1 REM MSAVEDT0.BAS 5 REM PUT BYTES TO A FILE 10 RESTORE 50 15 OPEN #1,8,0,"H1:MEMORYT0.BIN" 20 FOR I=0 TO 7 30 READ D:PUT #1,D 40 NEXT I 45 CLOSE #1 50 DATA 2,34,27,155,132,130,129,128 The program above results in this file only 8 bytes long: -rw-rw-rw- 1 kenjen users 8 Feb 24 18:52 MEMORYT0.BIN and that file contains the eight values as individual bytes (Linux Hex dump): 0000 02 22 1b 9b 84 82 81 80 ."...... This program uses GET instead of INPUT to retrieve the data to store in memory: 1 REM MLOADDT0.BAS 5 REM GET BYTES FROM FILE 10 OPEN #3,4,0,"H1:MEMORYT0.BIN" 20 FOR I = 0 TO 7 30 GET #1,D:POKE 1536+I,D 40 NEXT I 50 CLOSE #1 So, now we know how using data files can save valuable memory space in BASIC, and how to optimize the file content to the actual bytes as they would be stored in memory. However, a fundamental problem with BASIC remains – the program must read the file data byte by byte during a loop, and BASIC's slow execution speed will turn any significant amount of data into a long and inconvenient wait. In fact, doing device I/O byte by byte in BASIC is slower than reading from DATA statements in memory, so this memory saving solution penalizes the program with even slower data loading. If only there was some kind of machine language solution that could read the bytes from a file as fast as possible. What to do, Toto?!? OSS's BASIC XL provides a precedent with the Bput and Bget commands that write and read arbitrary length blocks of memory directly to and from a file as fast as the device can transfer bytes which is far faster than BASIC's ability to loop for individual bytes. How does BASIC XL manage this? Is it simply reading individual characters in a loop at machine language speed? Or is it doing another trick? It turns out that bulk input and output of bytes is a feature provided by the Atari OS's Centralized I/O (CIO) routines, but the problem is that Atari BASIC does not support all the commands that CIO provides. Gaming Atari's CIO (or not) Many systems of the 8-bit era require the programmer use unique calls to read and write information to each kind of device. Each act of reading a disk file, a cassette file, and user input typed from a keyboard may require calling different entry points in the OS. Likewise, writing to a disk file, a cassette, the screen, or a printer may also require calling different OS routines. Even using two disk drives could require different OS calls. Input/Output programming on these systems can be tedious affairs of device-specific coding for every possible input/output option which deters programmers from even providing choices to users. But the Atari system is different. The Atari includes a standard, modular, reusable input/output model. Simply put, the programmer fills out a common data structure describing the input/output operation and makes a call to one address in the OS. This is a high level abstraction for device input/output. There are no sectors to consider, no serial communications to worry about, no fixed buffers in the system. Everything is a stream of data in or out, read or written on demand. In a very general way this is similar to the unix world's “everything-is-a-file” philosophy. Changing disk file output to screen or printer output requires only a different device/file name. The setup and the call to the OS are the same for all. Considering the Atari's Central I/O (CIO) was written in the late 70s this is nearly magical, and very under-appreciated behavior in an 8-bit computer. Atari CIO The Atari CIO defines a basic set of commands that every handler must be prepared to accept. (Listed in the chart below.) This doesn't necessarily mean every device must do every I/O command. A keyboard cannot do output, and a printer cannot do input. However, the device handler is responsible for sanely managing commands and safely replying that an incompatible command request is not implemented. Command Command Number Description Open 3 Same as BASIC's OPEN command Get Text Record 5 Similar to BASIC's INPUT command Get Characters (Bytes) 7 Similar to BASIC's GET command Put Text Record 9 Similar to BASIC's PRINT command Put Characters (Bytes) 11 Similar to BASIC's PUT command Close 12 Same as BASIC's CLOSE command Status 13 Same as BASIC's STATUS command Command numbers above this range are handler-specific operations. For example, commands 17 and 18 are specific to the screen device (“S:”) to perform graphics DRAWTO and Fill, respectively. Commands from 32 to 38 (and higher) perform various DOS file management functions for the “😧” device. Per the list above, everything provided by the base CIO commands appear to correspond to a BASIC language I/O command. Well, almost, but not quite – “Similar” is not the same as “Same”. There is a bit of a disconnect between how BASIC uses these CIO actions, and what the CIO actions can really accomplish. Atari BASIC does not actually use the 5/Get Text Record and 9/Put Text Record. These commands read and write a stream of text characters ending with the Atari End Of Line character which is the Atari OS/CIO's general definition of “Text Record”. Atari BASIC's PRINT and INPUT behaviors are more complicated than the “Text Record” model, because BASIC I/O activity occurs incrementally rather than in a complete record. INPUT can handle multiple variables in one line. PRINT outputs variables and strings as BASIC interprets the values. PRINT also uses semicolons to suppress the end of line, and commas cause tabs/columnar output which are not inherent abilities in the CIO 9/Put Text Record command. So, BASIC is not using the Text Record I/O commands and is managing the I/O by other means. Additionally, notice that the titles for command 7/Get Characters and command 11/Put Characters do not exactly match how BASIC uses those commands. Both commands move bytes – stress the plural: b-y-t-e-S. However, Atari BASIC uses these CIO commands in the most minimal manner to serve as PUT and GET which move only one byte. Since we're looking for a way to read or write an arbitrary number of bytes (plural) these two CIO commands appear to be exactly what we need. The CIO command list above comes from Atari's “BASIC REFERENCE MANUAL” discussion of BASIC's XIO command. This suggests that XIO is capable of exercising these commands. If this is true, then the XIO command could be used to run these CIO operations as CIO intended. That's the theory. The IOCB First, let's learn how CIO operations work. The next section will examine how the XIO command relates to the CIO operations. Earlier it was stated that the programmer fills out a common data structure describing the input/output operation. This data structure is called the Input/Output Control Block (or IOCB). Each of the eight input/output channels has a 16-byte, IOCB dedicated to it at fixed locations in memory starting at $340/832(dec) for channel 0, $350 for channel 1, and so on up to $3B0 for channel 7. So, it is easy to find the IOCB for a channel. Multiply the channel number by 16 and add that to $340. The IOCB tracks the state of I/O operations to the channel. Though the IOCB is 16 bytes long the programmer need only interact with a few of the bytes. Some of the bytes are maintained by the OS, and some are not typically used at all except for special device commands. The values are referred to by their offset from the start of the IOCB: ICCMD: IOCB + $2 This is the CIO command discussed above. ICSTA: IOCB + $3 This is the last error/status of the previously completed CIO command. ICBAL/ICBAH: IOCB + $4 and + $5 This is the 16-bit address (low byte and high byte) of the input or output buffer here. In the case of 3/Open and CIO commands for DOS operations on disk files this buffer is the address of the string for the device/file name. ICBLL/ICBLH: IOCB + $8 and + $9 This is the 16-bit length (low byte, high byte) of the data here. In the case of read and write operations (5, 7, 9, or 11) this is the number of bytes to read in or write out from the buffer. CIO will update this value indicating the number of bytes actually transferred. In the case of 3/Open and commands for DOS operations on disk files this is the length of the device/file name. ICAX1: IOCB + $A Auxiliary byte for the handler. This commonly indicates the Mode for the 3/Open command. CIO will maintain this value here. Meaning of Mode values: For files combine these values for the Mode: 8 - write 4 - read 1 - append For the disk/DOS specifically: 2 - open the directory. For the “S:” device additional values direct how the OS opens graphics mode displays: 16 - Create the text window. 32 - Do not clear screen memory. Finally, For the “E:” handler the value 1 added to the open state (12 + 1 = 13) enables forced read Mode from the screen as if the Return key were held down. ICAX2: IOCB + $B Auxiliary byte for the handler. For the “S:” handler this specifies the Graphics mode to open. (The GRAPHICS numbers that BASIC uses, not ANTIC modes.) For the “😄” device value 0 is normal inter-record gaps, and 128 is the faster, short inter-record gaps. Other serial devices may use specific values here. The programmer need not be concerned with much more in most situations. The other bytes in the IOCB are either the responsibility of CIO, or only used for specific device functions. For the sake of reference: ICHID: IOCB + $0 Set by the OS to the index into the handler table when the channel is currently open. If the channel is closed the default value is 255/$FF. ICDNO: IOCB + $1 Set by the OS to the device number when multiple devices of the same kind are in use - e.g. 1 for “D1:”, 2 for “D2:”. ICPTL/ICPTH: IOCB + $6 and + $7 CIO populates this with the jump address for the handler's single-character PUT routine. Atari BASIC uses this as a shortcut to output characters. ICAX3, ICAX4, ICAX5, ICAX6: IOCB + $C through + $F Optional auxiliary bytes for the handler. NOTE and POINT DOS operations use these locations. Atari BASIC's XIO BASIC's XIO command provides a generic interface to access CIO functions that BASIC does not directly implement with a built-in command. The diagram below shows how the XIO values relate to the CIO's IOCB fields: XIO cmdno, #aexp, aexp1, aexp2, filespec ↓ ↓ ↓ ↓ ↓ CIO ICCMD Channel number ICAX1 ICAX2 ICBAL + ICBAH, ICBLL + ICBLH The XIO arguments conveniently match the list of CIO's IOCB values that a programmer should manage. This should mean the XIO command can substitute for any other CIO I/O command (in theory). An important distinction concerning XIO behavior vs the usual CIO expectations is that the 3/Open command uses ICAX1 and ICAX2 values where most other CIO commands don't need to specify those values. Depending on the device and the CIO command the original values set by the 3/Open could be significant and should not be disturbed. However, XIO accepts ICAX1 and ICAX2 values on every command which potentially could allow a later command to overwrite the values set by 3/Open. If ICAX1 and ICAX2 are important for later commands then for safety's sake they should be remembered and restated on subsequent use of XIO. A variety of documentation describing XIO share a common mistake by describing the last argument as always a filespec (the device identifier, and filename if applicable). In reality, the last argument simply provides the data buffer and length for the CIO command. This buffer has variable purpose. It is expected to contain the filespec for 3/Open and DOS-specific file manipulation commands. For most other commands this argument provides the input or output buffer for data. Atari BASIC can use an explicit string or a DIMensioned string for the buffer argument to XIO. The contents of this string does not even matter for some commands – for example, the 18/Fill and 17/Drawto CIO commands for the “S:” device do not use the buffer, so they do not care what value is passed. Below is an example of XIO performing the 18/Fill command for the non-existent device “Q:”. This works, because the real device identifier, “S:”, is only needed when the screen device is opened and the 18/Fill command does not use the buffer. 1 REM XIOFILL.BAS 5 REM DO FILL ON MYTHICAL Q: DEVICE 10 GRAPHICS 5+16 20 COLOR 330 PLOT 70,45:DRAWTO 50,1040 DRAWTO 30,10:POSITION 10,45 50 POKE 765,3 60 XIO 18,#6,0,0,"Q:" 70 GOTO 70 XIO and CIO 11/Put Bytes Assuming the XIO command works as advertized it should give access to the CIO commands 7/Get Bytes and 11/Put Bytes to manage a stream of bytes of arbitrary length. Below is a program using XIO to write a binary file using the 11/Put Bytes command. For the sake of demonstrating the global nature of XIO the other BASIC commands to OPEN and CLOSE the file are also replaced by XIO performing CIO commands 3/Open and 12/Close: 1 REM MSAVEDT2.BAS 5 REM SAVE FILE FROM DATA BY XIO 10 RESTORE 15 DIM D$(8) 20 FOR I=1 TO 8 25 READ D:D$(I)=CHR$(D) 30 NEXT I 35 ICAX1=8:ICAX2=0 39 REM OPEN 40 XIO 3,#1,ICAX1,ICAX2,"H1:MEMORY2.BIN" 44 REM PUT BYTES 45 XIO 11,#1,ICAX1,ICAX2,D$ 49 REM CLOSE 50 XIO 12,#1,ICAX1,ICAX2,"H:" 60 DATA 2,34,27,155,132,130,129,128 Hey! That seemed to work! So, lets take a look at the MEMORY2.BIN file... -rw-r--r-- 1 kenjen users 255 Feb 27 16:58 MEMORY2.BIN What on earth?!? The string is 8 bytes, but the file contains 255 bytes? How weird. Let's see what hexdump in linux says about the contents... 0000 02 22 1b 9b 84 82 81 80 9b 08 00 00 00 00 40 01 ."...... [email protected] 0010 00 00 00 00 81 14 00 04 00 19 00 04 04 00 00 00 ........ ........ 0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ 0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ . . . 00e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ 00f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ....... The 8 bytes that the program intended to write are there at the beginning of the file where they should be, so that's one good thing. But, the rest of the file appears to be junk out to the length of 255 bytes. It looks like the write started with the designated data string and then kept on going through whatever memory followed the string. Make the following additions to test this theory... 17 DIM E$(26) 18 E$="This should not be output." After running the program again and putting the file through hexdump: 0000 02 22 1b 9b 84 82 81 80 9b 68 69 73 20 73 68 6f ."...... .his sho 0010 75 6c 64 20 6e 6f 74 20 62 65 20 6f 75 74 70 75 uld not be outpu 0020 74 2e 40 08 00 00 00 00 40 01 00 00 00 00 81 14 [email protected] @....... 0030 00 04 00 19 00 04 00 00 00 00 00 00 00 00 00 00 ........ ........ 0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ 0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ . . . 00e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ 00f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ....... So, that confirms it. The eight characters from D$ string were output and then followed by whatever happened to be in memory after that string to a total length of 255 bytes. This would suggest a simple bug with the way Atari BASIC passed the output length to CIO, but there's something else in the output that tells us the problem is worse than that. The byte that follows the first eight bytes is $9b, the Atari End of Line character and which occupies the place in the output where the first character of E$ should appear. (Interesting.) That End of Line character is not part of the real data of either of the strings. This must mean that Atari BASIC intentionally output the End of Line after the D$ content as if it were performing PRINT. The odd part is that Atari BASIC accounted for this End of Line by skipping the next actual byte in memory which is the first character of E$. Then Atari BASIC figured out how many bytes remained until it reached 255 bytes and directed output of those bytes from memory. In the Olympics of buggy behavior this is a Track and Field gold medalist. What could have possessed them to do this? Perhaps this could be salvageable. The good part is the intended bytes were output intact at the beginning of the file. If the behavior is simply that Atari BASIC pads additional content, then this is a usable and (barely) tolerable situation for binary file output. So, let's see what happens with more data – such as half a kilobyte simulating an Antic mode 6 or 7 character set: 1 REM MSAVEDT3.BAS 5 REM SAVE LARGE FILE WITH XIO 15 DIM D$(512) 20 FOR I=1 TO 497 STEP 16 25 D$(I)="0123456789ABCDEF" 30 NEXT I 35 ICAX1=8:ICAX2=0 39 REM OPEN 40 XIO 3,#1,ICAX1,ICAX2,"H1:MEMORY3.BIN" 44 REM PUT BYTES 45 XIO 11,#1,ICAX1,ICAX2,D$ 49 REM CLOSE 50 XIO 12,#1,ICAX1,ICAX2,"H:" And after running the program the actual file length is... disappointment again... -rw-r--r-- 1 kenjen users 255 Feb 27 22:14 MEMORY3.BIN One point of good news is the hexdump utility shows the file does contain only the real bytes of the intended string up to 255 characters and that the last byte in the file is not substituted with the End Of Line: 0000 30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 46 01234567 89ABCDEF 0010 30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 46 01234567 89ABCDEF 0020 30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 46 01234567 89ABCDEF . . . 00e0 30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 46 01234567 89ABCDEF 00f0 30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 01234567 89ABCDE That nails the coffin shut. Atari BASIC's use of the CIO 11/Put Bytes command is almost completely broken. For some reason Atari BASIC XIO believes the 11/Put Bytes must output a 255 byte, fixed-length block and Atari BASIC doctors the I/O activity to meet this expectation. Next we will look at CIO's 7/Get Bytes command and see if Atari BASIC's support is also broken for this command. XIO and CIO 7/Get Bytes First, let's verify that 7/Get Bytes can at least read the first 8 bytes from the 255 byte binary file: 1 REM MLOADDT.BAS 5 REM READ FILE USING XIO 10 DIM D$(8):D$(8)=" " 15 ICAX1=4:ICAX2=0 19 REM OPEN 20 XIO 3,#1,ICAX1,ICAX2,"H1:MEMORY2.BIN" 24 REM GET BYTES 25 XIO 7,#1,ICAX1,ICAX2,D$ 29 REM CLOSE 30 XIO 12,#1,ICAX1,ICAX2,"H:" 35 FOR I=1 TO 8 40 ? ASC(D$(I,I)), 45 NEXT I:? When it runs BASIC reports: 2 34 27 155 132 130 129 128 It looks like it is reading the data into the string correctly. But, is there anything else going on? Remember Atari BASIC thinks the write has to output 255 bytes. Maybe it is also doing something strange during the read. Lets see if it is reading more than directed: 1 REM MLOADDTX.BAS 5 REM READ FILE USING XIO 10 DIM D$(8):D$(8)=" " 11 DIM E$(255):E$="!":E$(255)="!":E$(2)=E$ 15 ICAX1=4:ICAX2=0 19 REM OPEN 20 XIO 3,#1,ICAX1,ICAX2,"H1:MEMORY2.BIN" 24 REM GET BYTES 25 XIO 7,#1,ICAX1,ICAX2,D$ 29 REM CLOSE 30 XIO 12,#1,ICAX1,ICAX2,"H:" 35 FOR I=1 TO 8 40 ? ASC(D$(I,I)), 45 NEXT I:? 50 ? E$ This is basically the same program with the addition of a large E$ declared and populated entirely with exclamation points, "!". If Atari BASIC reads only the 8 bytes it needs to populate D$ then E$ should be undisturbed and the program will end by displaying 255 uninterrupted exclamation points. But, this is the actual result: Unfortunately, this did not do what is expected or needed. BASIC overwrote E$ with the remaining data from the file. (An interesting bit of trivia is that the first character of E$ is preserved. This character corresponds to the End Of Line that BASIC had inserted in the output.) So, now we know that the CIO 7/Get Bytes command works well enough from Atari BASIC to retrieve short sequences of binary data as long as a buffer of 255 bytes is provided. Now to figure out if it can load longer data. Given the 255 character limit when using 11/Put Bytes the next program will test that limit for reading. First, we need a file that has more than 255 bytes. Recall that the earlier attempt to write 512 bytes actually failed, so here is a (much slower) program to make the 512 byte file as intended: 1 REM MSAVE512.BAS 5 REM WRITE 512 BYTES TO FILE 10 DIM D$(16):D$="0123456789ABCDEF" 15 OPEN #1,8,0,"H1:MEM512.BIN" 20 FOR I=1 TO 32 25 FOR J=1 TO 16 30 D=ASC(D$(J)) 35 PUT #1,D 40 NEXT J 45 NEXT I 50 CLOSE #1 Next is the program to test if Atari BASIC XIO has a 255 byte limit for the 7/Get Bytes command similar to the way it limits 11/Put Bytes: 1 REM MLOAD512.BAS 5 REM READ 512 BYTES FROM FILE 10 DIM D$(512) 15 D$="!":D$(512)="!":D$(2)=D$ 20 ICAX1=4:ICAX2=0 24 REM OPEN 25 XIO 3,#1,ICAX1,ICAX2,"H1:MEM512.BIN" 29 REM GET BYTES 30 XIO 7,#1,ICAX1,ICAX2,D$ 34 REM CLOSE 35 XIO 12,#1,ICAX1,ICAX2,"H:" 40 ? D$ D$ is declared 512 bytes long and filled with exclamation points. Then it attempts to read the 512 bytes from the file into D$. If this works properly the entire contents of D$ should show the "0" to "F" pattern read from the file all the way to the end. But this is what happens, instead: No joy for Atari BASIC's XIO. The result shows the first 255 characters of D$ are populated with the values from the file and the remainder of D$ is the exclamation points set during the program initialization. But, the horror is not over. Recall the MEMORYT0.BIN file created earlier that contains only 8 bytes? This program attempts to read just those 8 bytes from the file: 1 REM MLOAD8.BAS 5 REM READ THE 8 BYTES FROM FILE 10 DIM D$(8) 15 D$="!!!!!!!!" 20 ICAX1=4:ICAX2=0 24 REM OPEN 25 XIO 3,#1,ICAX1,ICAX2,"H1:MEMORYT0.BIN" 29 REM GET BYTES 30 XIO 7,#1,ICAX1,ICAX2,D$ 34 REM CLOSE 35 XIO 12,#1,ICAX1,ICAX2,"H:" 40 ? D$ And this is what happens when the program runs: ERROR- 136 AT LINE 30 The program gets an End Of File error during the XIO performing the 7/Get Bytes request. BASIC XIO expects to read 255 bytes from the file no matter the size of the string buffer supplied, even if the buffer is defined shorter. So, the bottom line is that Atari BASIC XIO has mangled use of the CIO 7/Get Bytes command similar to the 11/Put Bytes command. Here's a summary of what we have left of these CIO command as Atari BASIC has abused them: XIO for 7/Get Bytes is usable for reading binary data from 1 to 255 bytes. If the intended data is shorter than actual file size (of 255 bytes) then Atari BASIC will continue retrieving data from the file and storing it into memory up to that 255 byte maximum length. In order to complete this activity the file must have at least 255 bytes, and in the interest of safety the string buffer accepting the data should be 255 bytes. XIO for 11/Put Bytes is usable to write 1 to 255 bytes to a file with the understanding that anything shorter than 255 bytes will be padded to the length of 255 bytes with junk. In addition, Atari BASIC's capability is further restricted, because it only performs input and output to a a string variable. Saving and loading data to any arbitrary memory location is problematic. This could be circumvented with more involved coding to manipulate a string table entry to point to a specific memory addresses before the read or write. But, again, since BASIC insists on moving 255 bytes at a time, even this method isn't a great solution. We're not going to even bother trying that. In the next episode we'll look at a machine language replacement for XIO and its test program. For my thoughts are not your thoughts, neither are your ways my ways, declares the Lord. For as the heavens are higher than the earth, so are my ways higher than your ways and my thoughts than your thoughts. Isaiah 55:8-9
  12. Memory Copy ============================================================== Part 1 - Introduction http://atariage.com/forums/blog/576/entry-13175-part-1-of-11-simple-assembly-for-atari-basic/ Part 2 - Learn 82.7% of Assembly Language in About Three Pages http://atariage.com/forums/blog/576/entry-13176-part-2-of-11-simple-assembly-for-atari-basic/ Part 3 - The World Inside a USR() Routine http://atariage.com/forums/blog/576/entry-13177-part-3-of-11-simple-assembly-for-atari-basic/ Part 4 - Implement DPEEK() http://atariage.com/forums/blog/576/entry-13178-part-4-of-11-simple-assembly-for-atari-basic/ Part 5 - Implement DPOKE http://atariage.com/forums/blog/576/entry-13180-part-5-of-11-simple-assembly-for-atari-basic/ Part 6 - Various Bit Manipulations http://atariage.com/forums/blog/576/entry-13181-part-6-of-11-simple-assembly-for-atari-basic/ Part 7 - Convert Integer to Hex String http://atariage.com/forums/blog/576/entry-13182-part-7-of-11-simple-assembly-for-atari-basic/ Part 8 - Convert Integer to Bit String http://atariage.com/forums/blog/576/entry-13183-part-8-of-11-simple-assembly-for-atari-basic/ Part 9 - Memory Copy http://atariage.com/forums/blog/576/entry-13184-part-9-of-11-simple-assembly-for-atari-basic/ Part 10 - Binary File I/O Part 1 (XIO is Broken) http://atariage.com/forums/blog/576/entry-13185-part-10-of-11-simple-assembly-for-atari-basic/ Part 11 - Binary File I/O Part 2 (XIO is Broken) http://atariage.com/forums/blog/576/entry-13186-part-11-simple-assembly-for-atari-basic-the-end/ ============================================================== A number of features on the Atari benefit from fast memory copies. High speed data copying provides convenience to the user by reducing wait times for actions that would use slow, time-consuming BASIC loops. Copying a character set (1024 bytes) is a good example. High speed memory moves also enable use of Atari features that are not otherwise possible due to BASIC's speed. For example, vertical movement and animation of Player/Missile graphics in BASIC is not realistic, but a memory move function makes P/M graphics animation practical in an Atari BASIC program. Though Atari BASIC lacks a general purpose memory move there is a hack around this problem that exploits the way Atari BASIC manages strings. As simply as possible: A BASIC program may dig through the variable control structures and change the starting address of a string variable, effectively overlaying the string on any memory possible: screen memory, player/missile memory, color registers, character sets, hardware addresses, etc. String assignments then cause memory to be assigned or copied at near machine language speeds. The copy is done in ascending order and there is no consideration for source and destination memory (strings) that overlap. However, this string abuse method is a subject for an entirely different discussion and is not what will be done here. The subject of memory moves on the 6502 includes many considerations and is the inspiration for numerous discussions, arguments, and holy wars between programmers around the subject of speed, efficiency, optimization, and the odors emanating from the grandmothers of those who disagree with one's obvious and indisputable common sense. In short, although copying from A to B seems like a simple subject, there are a many choices and different methods to arrange a 6502 algorithm to copy memory. Topics that make a difference in code: How many bytes are being copied? More or less than 128 bytes? More or less than 256 bytes? More or less than 32K bytes? Does the code need to consider whether or not the source and destination memory overlap? Should the memory be copied in ascending or descending order? Is speed important? Should loops be unrolled into explicit code? Is the size of the code more important than speed? All of the above can start a group of programmers on a never-ending flame war. For the sake of defusing arguments I will admit that the result created here will not be everyone's idea of best, fastest and most efficient. We will follow the rule, “moderation in all things.” Well, more of a guideline than a rule. Let's say that we are copying four bytes from a source to a destination. The fastest way to copy is to do it directly and explicitly: LDA SOURCE STA DEST LDA SOURCE+1 STA DEST+1 LDA SOURCE+2 STA DEST+2 LDA SOURCE+3 STA DEST+3 That's about as fast as it gets on the 6502. Depending on whether or not source or destination is in Page Zero, this code uses about 4 to 6 bytes worth of instructions (total of 16 to 24 bytes just for this example!) and 6 to 8 cycles of CPU time for each byte it moves. That's all the good about it. Now the reality check: The code is not general purpose. When source and destination change then another block of code must be created. This method makes the assembly source code for a memory move of any substantial size very wordy, not to mention the tedious typing for the programmer. An explicit copy like this is done when performance is the ultimate goal. The next level of optimization is a loop to move a range of bytes: LDX #$00 LOOP LDA SOURCE,X STA DEST,X INX CPX #$04 BNE LOOP This code uses an index to copy bytes in ascending order and makes a comparison for the number of bytes. The code occupies 9 to 11 bytes (without the LDX initialization), roughly half the size of the explicit method. The size efficiency becomes greater the more bytes are copied. This code can copy 4 bytes, 10 bytes, or 100 bytes and remains the same size, while the earlier explicit method expands with every byte. However, this code uses 15 to 17 CPU cycles (not including the LDX initialization which occurs only once) for every byte it copies which is more than twice as long as the explicit copy. Additionally, this still uses absolute addresses and is not a general purpose, reusable routine. Here is a little optimization on this method: LDX #$03 LOOP LDA SOURCE,X STA DEST,X DEX BPL LOOP This version uses an index to copy bytes in descending order and eliminates the compare instruction by relying on the CPU negative flag changing when the index decrements from $00 to $FF. This reduces the code size to 7 to 9 bytes without the LDX initialization. Execution time (without the LDX) is reduced to 13 to 15 cycles per byte copied – just about twice as long at the explicit method. Again, like all other examples this uses absolute addresses and so is not a general purpose, reusable routine. A general purpose routine must use indirection instructions allowing the source and destination addresses to be different each time the program uses the routine. This means Page Zero locations must be initialized for source and destination:. LDA # <SOURCE STA ZSOURCE LDA # >SOURCE+1 STA ZSOURCE+1 LDA # <DEST STA ZDEST LDA # >DEST+1 STA ZDEST+1 ; LDY #$03 LOOP LDA (ZSOURCE),Y STA (ZDEST),Y DEY BPL LOOP This version is not so short. The initialization (executed once) is 18 bytes long. While the actual looping section is only 7 bytes it executes in 15 to 17 cycles due to the indirection. The advantage here is that the looping section is reusable. Another consideration of the indexed, looping methods is that they can copy a limited number of bytes. The maximum number of bytes a loop can copy using the X or Y register as an index is 256 bytes ($00 to $FF). The method of comparison or detection of the end of the copy loop can also limit the number of bytes. An earlier example copies in descending order uses BPL to detect the index wrapping around from $00, a positive value, to $FF, a negative value. This means the branching evaluation cannot encounter any other negative value which limits this code 129 bytes. 129? not 128? Why? LDX #$80 ; Start at +128 LOOP LDA SOURCE,X STA DEST,X DEX ; From here X as $7F descending BPL LOOP ; to $00 is valid It is possible to copy a specific number of bytes less than 256 in ascending order without using a comparison instruction, but, this is less obvious. This code example below alters the starting address to compensate for the starting index, so that the branch decision can rely on the CPU flag change when the index reaches zero: ; $00 - $04 = $FC LDX #$FC LOOP LDA SOURCE-$FC,X STA DEST-$FC,X INX ; Continues for $FD, $FE, $FF BNE LOOP ; and quits at index $00 So, then what do do when more there are more than 256 bytes to copy? In the simple loop examples just add another loop that copies more bytes with different source and destination addresses. But, when there is a lot of memory to copy this quickly becomes redundant. The advantage of using the indirection method is that the source and destination addresses in Page Zero can be easily modified and used to repeat the loop again. The example below copies 1024 bytes: LDX #$04 ; Number of 256 byte pages LDY #$00 LOOP LDA (ZSOURCE),Y STA (ZDEST),Y INY BNE LOOP ; Copy a page INC ZSOURCE+1 ; Increment addresses' INC ZDEST+1 ; high bytes DEX BNE LOOP ; Do another page The looping part is 14 bytes long. It can copy any number of 256 byte pages just by changing the X register. This is a practical routine for the Atari which has character sets 512 or 1024 bytes long. Player/missile graphics may use memory maps of 256 bytes. More generally, aligning any data to fit within the range of an index register allows reasonably compact code like this. The next problem is how to copy any number of bytes. Simply change perspective and think of the problem in two parts. First, if the number of bytes is 256 or greater then the previous page copy shown above can take care of all the whole, 256-byte pages. The high byte of the size provides the number of whole 256 byte pages. That leaves the size's low byte which specifies the remaining zero to 255 bytes. This second part just needs a slightly different byte copying loop that will stop early at a specific count. Assuming the source, destination, and size are all set into designated page 0 locations, then the relocatable, re-usable code could look something like this: MEMMOVE in Mac/65 Assembler Code 0100 ; MEMMOVE.M65 0105 ; 0110 ; GENERAL MEMORY MOVE 0115 ; 0120 ; GENERIC MEMORY MOVE FROM 0125 ; SOURCE TO DESTINATION 0130 ; ASCENDING. 0135 ; 0140 ; USR 3 ARGUMENTS: 0145 ; SOURCE == FROM ADDRESS 0150 ; DEST == TO ADDRESS 0155 ; SIZE == NUMBER OF BYTES 0160 ; 0165 ; RETURN VALUE IS BYTES COPIED. 0170 ; 0175 ZRET = $D4 ; FR0 $D4/$D5 Return Value 0180 ZARGS = $D5 ; $D6-1 for arg Pulldown loop 0185 ZSIZE = $D6 ; FR0 $D6/$D7 Size 0190 ZDEST = $D8 ; FR1 $D8/$D9 Destination 0195 ZSOURCE = $DA ; FR1 $DA/$DB Source 0200 ; 0205 .OPT OBJ 0210 ; 0215 *= $9200 ; Arbitrary. this is relocatable 0220 ; 0225 INIT 0230 LDY #$00 ; Make the return 0235 STY ZRET ; value clear to 0 0240 STY ZRET+1 ; by default. 0245 PLA ; Get argument count 0250 BEQ BYE ; Shortcut for no args 0255 ASL A ; Now number of bytes 0260 TAY 0265 CMP #$06 ; Source, Dest, Size 0270 BEQ PULLDOWN 0275 ; 0280 ; Bad arg count. Clean up for exit. 0285 ; 0290 DISPOSE ; Any number of arguments 0295 PLA 0300 DEY 0305 BNE DISPOSE 0310 RTS ; Abandon ship. 0315 ; 0320 ; Pull args into Page Zero. 0325 ; This code works the same 0330 ; for 1, 4, 8... arguments. 0335 ; 0340 PULLDOWN ; arguments in Y 0345 PLA 0350 STA ZARGS,Y 0355 DEY 0360 BNE PULLDOWN 0365 ; 0370 ; Since we're good to start the 0375 ; copy, then set the return 0380 ; value to the size. 0385 ; 0390 LDA ZSIZE+1 0395 STA ZRET+1 0400 LDA ZSIZE 0405 STA ZRET 0410 ; 0415 ; Moving full 256 byte pages or not? 0420 ; 0425 LDY #0 0430 LDX ZSIZE+1 ; Number of pages is 0435 BEQ MOVEPARTIAL ; Zero so, try partial. 0440 ; 0445 ; Copy full index range of 256 bytes. 0450 ; 0455 MOVEPAGE 0460 LDA (ZSOURCE),Y 0465 STA (ZDEST),Y 0470 INY 0475 BNE MOVEPAGE ; Y rolled $FF to $00 0480 INC ZSOURCE+1 ; Next page src 0485 INC ZDEST+1 ; next page dst 0490 DEX ; this page done 0495 BNE MOVEPAGE ; Non-zero means more 0500 ; 0505 ; A partial page remains? 0510 ; 0515 MOVEPARTIAL 0520 LDX ZSIZE ; Low byte remainder. 0525 BEQ BYE ; Zero, exit 0530 ; 0535 MOVEMEM 0540 LDA (ZSOURCE),Y 0545 STA (ZDEST),Y 0550 INY ; Copy ascending. 0555 DEX ; and subtract count. 0560 BNE MOVEMEM 0565 BYE 0570 RTS 0575 ; 0580 .END MEMMOVE copies any number of bytes in ascending order from the source to destination. Earlier the article mentioned an Atari BASIC string hack that assigns strings to specific memory addresses and uses the string assignment action to copy from a source address to a target address. String assignments work in ascending order. Therefore, this routine achieves the same result as the string method (ascending copy) with a bit less hacking of the BASIC variable table. Copying in reverse order or automatically detecting source and target overlap would add more code for options that are not as frequently needed. This is certainly not the highest performing option possible. But, it is fairly compact and uses Page zero allowing the routine to be general-purpose, and reusable, and that is a reasonable goal for a routine to support Atari BASIC. If this discussion concerned writing an entire video game in assembly then the code would focus on execution time and use unrolled loops or other bells and whistles for copying faster at the expense of code size. Testing MEMMOVE The Atari BASIC program below, TESTMOVE.BAS, reasonably verifies operation of the memory move utility works as expected: 100 REM TEST MEMORY MOVE OPERATIONS 105 REM 110 GRAPHICS 0:POKE 710,0:POKE 82,0 115 DIM S$(258),D$(259) 120 GOSUB 503:REM RESET SRC AND DST 125 GOSUB 10000:REM LOAD MEMMOVE 130 REM RUN TESTS FOR 8,255,256,257 135 RESTORE 230 140 READ MSIZE 145 IF MSIZE<1 THEN ? "Done":END 150 ? "Testing move size ";MSIZE;" . . . "; 155 X=USR(MMOV,ADR(S$),ADR(D$)+1,MSIZE) 160 DCOUNT=0 165 FOR I=1 TO 259 170 IF D$(I,I)="*" THEN DCOUNT=DCOUNT+1 175 NEXT I 180 IF DCOUNT=MSIZE THEN ? "OK":GOTO 190 185 ? "FAILED! ";DCOUNT;" <> ";MSIZE 190 GOSUB 504:REM RESET DST 195 GOTO 140 200 REM 205 REM NUMBER OF BYTES TO MOVE FOR 210 REM EACH TEST. TESTS LESS THAN 215 REM ONE PAGE AND THE BORDER 220 REM CONDITIONS AROUND ONE PAGE. 225 REM 230 DATA 8,255,256,257,-1 235 END 500 REM 501 REM RESET MEMORY 502 REM 503 S$="*":S$(258)="*":S$(2)=S$ 504 D$=".":D$(259)=".":D$(2)=D$ 505 RETURN 9997 REM 9998 REM SETUP ML MEMMOVE UTILITY 9999 REM 10000 DIM MM$(68) 10001 MMOV=ADR(MM$) 10002 RESTORE 24000:? "Loading MMOV..." 10003 FOR I=0 TO 67 10004 READ D:POKE MMOV+I,D 10005 NEXT I:? 10006 RETURN 23996 REM H1:MEMMOVE.OBJ 23997 REM SIZE = 68 23998 REM START = 37376 23999 REM END = 37443 24000 DATA 160,0,132,212,132,213,104,240 24001 DATA 58,10,168,201,6,240,5,104 24002 DATA 136,208,252,96,104,153,213,0 24003 DATA 136,208,249,165,215,133,213,165 24004 DATA 214,133,212,160,0,166,215,240 24005 DATA 14,177,218,145,216,200,208,249 24006 DATA 230,219,230,217,202,208,242,166 24007 DATA 214,240,8,177,218,145,216,200 24008 DATA 202,208,248,96 The program tests different sizes to verify the partial and full page copy loops, and also tests the border conditions around a full page copy. After each each copy it tests the contents of the destination memory counting all the locations that were changed by the memory move. Successful output will look like this: Below are the source files and examples of how to load the machine language routine into BASIC included in the disk image and archive: MEMMOVE File List: MEMMOVE.M65 Saved Mac/65 source MEMMOVE.L65 Mac/65 source listing MEMMOVE.T65 Mac/65 source listed to H6: (linux) MEMMOVE.ASM Mac/65 assembly listing MEMMOVE.TSM Mac/65 assembly listing to H6: (linux) MEMMOVE.OBJ Mac/65 assembled machine language program (with load segments) MEMMOVE.BIN Assembled machine language program without load segments MEMMOVE.LIS LISTed DATA statements for MEMMOVE.BIN routine. MEMMOVE.TLS LISTed DATA statements for MEMMOVE.BIN routine to H6: (linux) MAKEMMOV.BAS BASIC program to create the MEMMOVE.BIN file. This also contains the MEMMOVE routine in DATA statements. MAKEMMOV.LIS LISTed version of MAKEMMOV.BAS MAKEMMOV.TLS LISTed version of MAKEMMOV.BAS to H6: (linux) TESTMOVE.BAS BASIC program that tests the MEMMOVE USR() routine. TESTMOVE.LIS LISTed version of TESTMOVE.BAS. TESTMOVE.TLS LISTed version of TESTMOVE.BAS to H6: (linux) ZIP archive of files: Move_Disk.zip Tar archive of files (remove the .zip after download) Move_Disk.tgz.zip Great peace have those who love your law; nothing can make them stumble. Psalm 119:165
  13. Convert Integer To Bit String ============================================================== Part 1 - Introduction http://atariage.com/forums/blog/576/entry-13175-part-1-of-11-simple-assembly-for-atari-basic/ Part 2 - Learn 82.7% of Assembly Language in About Three Pages http://atariage.com/forums/blog/576/entry-13176-part-2-of-11-simple-assembly-for-atari-basic/ Part 3 - The World Inside a USR() Routine http://atariage.com/forums/blog/576/entry-13177-part-3-of-11-simple-assembly-for-atari-basic/ Part 4 - Implement DPEEK() http://atariage.com/forums/blog/576/entry-13178-part-4-of-11-simple-assembly-for-atari-basic/ Part 5 - Implement DPOKE http://atariage.com/forums/blog/576/entry-13180-part-5-of-11-simple-assembly-for-atari-basic/ Part 6 - Various Bit Manipulations http://atariage.com/forums/blog/576/entry-13181-part-6-of-11-simple-assembly-for-atari-basic/ Part 7 - Convert Integer to Hex String http://atariage.com/forums/blog/576/entry-13182-part-7-of-11-simple-assembly-for-atari-basic/ Part 8 - Convert Integer to Bit String http://atariage.com/forums/blog/576/entry-13183-part-8-of-11-simple-assembly-for-atari-basic/ Part 9 - Memory Copy http://atariage.com/forums/blog/576/entry-13184-part-9-of-11-simple-assembly-for-atari-basic/ Part 10 - Binary File I/O Part 1 (XIO is Broken) http://atariage.com/forums/blog/576/entry-13185-part-10-of-11-simple-assembly-for-atari-basic/ Part 11 - Binary File I/O Part 2 (XIO is Broken) http://atariage.com/forums/blog/576/entry-13186-part-11-simple-assembly-for-atari-basic-the-end/ ============================================================== This machine language routine converts a 16-bit integer into a string of values representing 0 and 1 bits. This is useful for visualizing bits in a byte, word, or address for diagnostic purposes. This is practical on the Atari for displaying character set or player/missile graphics data in a human-readable way. This routine has a different number of arguments from the INT2HEX string conversion routine and so is presented on its own. This routine has similar problems to solve as the INT2HEX routine. It must deal with the low byte stored in memory before the high byte, because it must output the bit values in order of highest bits first and lowest bits last. (e.g 258 or hex $0102 should be output as “0000000100000010” though it is stored as $02 $01). Converting a byte to a string of bits is fairly easy in 6502. In general terms: test the high bit and output a “0” or a “1” as needed, then shift the bits of the byte and repeat until 8 bits have been extracted. The routine below includes a touch of customization – the USR() routine may also specify the characters (ASCII/ATASCII) used to represent “0” and “1”. INT2BITS in Mac/65 Assembler Code 0100 ; INT2BITS 0105 ; 0110 ; Convert 16-bit integer into 0115 ; string of bits. 0120 ; 0125 ; INT2BITS USR 2 arguments: 0130 ; Value == Integer to convert. 0135 ; StrAdr == Address of string 0140 ; which must be able 0145 ; to hold 16 characters 0150 ; Zero == Character used for 0155 ; clear bit. 0160 ; One == Character used for 0165 ; set bit. 0170 ; 0175 ; USR return value 0 means no 0180 ; conversion. Non-Zero means 0185 ; STRADR contains data. 0190 ; 0195 ; Use the FR0/FR1 FP register. 0200 ; The return value for BASIC 0205 ; goes in FR0. 0210 ; No FP is used so all of FR0 0215 ; (and more FP registers) can 0220 ; be considered available. 0225 ; 0230 ZRET = $D4 ; FR0 $D4/$D5 Return value 0235 ZARGS = $D5 ; $D6-1 for arg Pulldown loop 0240 ZONE = $D6 ; FR0 $D6/$D7 char for "1" 0245 ZZERO = $D8 ; FR0 $D8/$D9 char for "0" 0250 ZSTR = $DA ; FR0 $DA/$DB StrAdr 0255 ZVAL = $DC ; FR0 $DC/$DD Integer value 0260 ; 0265 .OPT OBJ 0270 ; 0275 ; Arbitrary. This is relocatable. 0280 ; 0285 *= $9300 0290 ; 0295 INIT 0300 LDA #$00 ; Make sure return 0305 STA ZRET ; value is cleared 0310 STA ZRET+1 ; by default. 0315 PLA ; Get argument count 0320 BEQ EXIT ; Shortcut for no args 0325 ASL A ; Now number of bytes 0330 TAY 0335 CMP #$08 ; Integer Value1, StrAdr, Zero, One 0340 BEQ PULLDOW N0345 ; 0350 ; Bad args. Clean up for exit. 0355 ; 0360 DISPOSE ; any number of args 0365 PLA 0370 DEY 0375 BNE DISPOSE 0380 RTS ; Abandon ship 0385 ; 0390 ; This code works the same 0395 ; for 1, 4, 8 ... arguments. 0400 ; 0405 PULLDOWN 0410 PLA 0415 STA ZARGS,Y 0420 DEY 0425 BNE PULLDOWN 0430 ; 0435 ; Arg validation. 0440 ; StrAdr may not be null. 0445 ; Zero and One may not 0450 ; be equal. 0455 ; 0460 LDA ZSTR 0465 ORA ZSTR+1 0470 BEQ EXIT ; zstr is null 0475 LDA ZZERO 0480 CMP ZONE 0485 BEQ EXIT ; zero == one 0490 ; 0495 ; Break Integer Value into 0500 ; bytes and test bits from 0505 ; high to low... 0510 ; 0515 LDX #$01 ; X index bytes. 0520 ; Y is already 0 0525 ; Y index string. 0530 ; 0535 WALK_BITS 0540 CLC 0545 ASL ZVAL,X ; Shift out bit 0550 LDA ZZERO ; Assume Zero 0555 BCC COPY2STR ; Copy if so 0560 LDA ZONE ; Actually One 0565 COPY2STR 0570 STA (ZSTR),Y 0575 INY ; Next string position 0580 CPY #$08 ; End of high byte? 0585 BEQ NEXT_BYTE ; Yes. Dec index. 0590 CPY #$10 ; End of low byte? 0595 BNE WALK_BITS ; No, loop again. 0600 NEXT_BYTE 0605 DEX ; 1, 0 will continue 0610 BPL WALK_BITS ; -1 ends loop 0615 END_LOOP 0620 ; 0625 INC ZRET ; Successful return 0630 EXIT 0635 RTS 0640 ; 0645 .END The variable validation has a trivial addition to make sure the character values for “0” and “1” are not the same: 0475 LDA ZZERO 0480 CMP ZONE 0485 BEQ EXIT ; zero == one Unlike the hex conversion there is just one part to the bit conversion: 0515 LDX #$01 ; X index bytes. 0520 ; Y is already 0 0525 ; Y index string. 0530 ; 0535 WALK_BITS 0540 CLC 0545 ASL ZVAL,X ; Shift out bit 0550 LDA ZZERO ; Assume Zero 0555 BCC COPY2STR ; Copy if so 0560 LDA ZONE ; Actually One 0565 COPY2STR 0570 STA (ZSTR),Y 0575 INY ; Next string position 0580 CPY #$08 ; End of high byte? 0585 BEQ NEXT_BYTE ; Yes. Dec index. 0590 CPY #$10 ; End of low byte? 0595 BNE WALK_BITS ; No, loop again. 0600 NEXT_BYTE 0605 DEX ; 1, 0 will continue 0610 BPL WALK_BITS ; -1 ends loop 0615 END_LOOP There are two loops happening here. One loop, indexed by X, counts in reverse, 1 to 0, from the high byte to the low byte of the integer. Within that loop is the activity to output all the bits in the byte which is done as part of the loop indexed by Y to count from character position 0 to 15. When the Y loop reaches values 8 and 16 it signals that all the bits in the byte have been output, so then it is time to continue the outer loop to get the next byte. The Y index is thus incremented 8 times for each decrement (one byte) of the X loop. As with the integer to hexadecimal conversion this utility receives the address of the output string, not its control information, so it can only fill in the bytes in the string and can't change the size as BASIC understands it. A BASIC program using the results of the conversion must insure that it populates the length of the string prior to calling the conversion utility: 10 DIM A$(16) 20 A$=" ":REM 16 SPACES Now that we have routines to convert values into hex strings and bit representation, the test program for BITS can be revisited to provide improved diagnostic information. Testing BITS Again (with INT2HEX and INT2BITS) Below is the Atari BASIC TESTBITS.BAS program upgraded with the new conversion utilities to produce much more sensible output illustrating the changes in the bits: 100 REM TESTBIT2.BAS 105 REM TEST BIT OPERATIONS UTILITY 2 110 GRAPHICS 0:POKE 710,0:POKE 82,0 115 DIM B(3,1),N$(21),HX$(5),BIT$(17) 120 N$="OR ANDEORLSRLSLRORROL" 125 HX$=" ":BIT$=" " 130 AZERO=48:AONE=49:REM ATASCII 0 1 135 REM ENUMERATE BIT OPERATIONS 140 RESTORE 150 145 READ BOR,BAND,BEOR,BLSR,BLSL,BROR,BROL 150 DATA 1,2,3,4,5,6,7 155 GOSUB 10000:REM BITS UTILITY 160 RESTORE 190:REM BIT PATTERNS 165 FOR X=0 TO 1 170 FOR Y=0 TO 3 175 READ D:B(Y,X)=D 180 NEXT Y 185 NEXT X 190 DATA 0,257,0,257,0,0,258,258 195 REM 200 REM TEST OR, AND, EOR 205 REM 210 FOR OPER=BOR TO BEOR 215 FOR Y=0 TO 3 220 ? " "; 225 VALUE=B(Y,0):GOSUB 405 230 ? N$(OPER*3-2,OPER*3); 235 VALUE=B(Y,1):GOSUB 405 240 ? "=== ===== ===================" 245 VALUE=USR(BITS,OPER,B(Y,0),B(Y,1)) 250 ? " ";:GOSUB 405 255 ? 260 NEXT Y 265 NEXT OPER 270 REM 275 REM TEST SHIFT AND ROTATE 280 REM 285 V=129:REM $81 290 FOR OPER=BLSR TO BROL 295 ? "Testing ";N$(OPER*3-2,OPER*3);" on Value "; 300 VALUE=V:GOSUB 505 305 FOR Y=0 TO 8 310 ? N$(OPER*3-2,OPER*3);" ";Y;" = "; 315 VALUE=USR(BITS,OPER,V,Y) 320 GOSUB 505 325 NEXT Y 330 ? 335 NEXT OPER 340 END 400 REM 401 REM OUTPUT INTEGER CONVERTED TO 402 REM USEFUL FORMAT: 403 REM $HEX = BITS BITS BITS BITS 404 REM 405 RH=USR(INT2HEX,VALUE,ADR(HX$)) 406 RB=USR(BIT2STR,VALUE,ADR(BIT$),AZERO,AONE) 407 ? " $";HX$;" = ";BIT$(1,4);" ";BIT$(5,;" ";BIT$(9,12);" ";BIT$(13,16) 408 RETURN 500 REM 501 REM OUTPUT BYTE VALUE CONVERTED TO 502 REM USEFUL FORMAT: 503 REM $HEX = BITS BITS 504 REM 505 RH=USR(INT2HEX,VALUE,ADR(HX$)) 506 RB=USR(BIT2STR,VALUE,ADR(BIT$),AZERO,AONE) 507 ? "$";HX$(3,4);" = ";BIT$(9,12);" ";BIT$(13,16) 508 RETURN 9997 REM 9998 REM SETUP ML UTILITIES 9999 REM 10000 DIM BT$(162),B2S$(67),I2H$(72) 10001 BITS=ADR(BT$) 10002 RESTORE 27000:? "Loading BITS" 10003 FOR I=0 TO 161 10004 READ D:POKE BITS+I,D 10005 NEXT I:? 10006 BIT2STR=ADR(B2S$) 10007 RESTORE 25000:? "Loading BIT2STR" 10008 FOR I=0 TO 66 10009 READ D:POKE BIT2STR+I,D 10010 NEXT I:? 10011 INT2HEX=ADR(I2H$) 10012 RESTORE 26000:? "Loading INT2HEX" 10013 FOR I=0 TO 71 10014 READ D:POKE INT2HEX+I,D 10015 NEXT I:? 10016 RETURN 24996 REM H1:INT2BITS.OBJ 24997 REM SIZE = 67 24998 REM START = 37632 24999 REM END = 37698 25000 DATA 169,0,133,212,133,213,104,240 25001 DATA 57,10,168,201,8,240,5,104 25002 DATA 136,208,252,96,104,153,213,0 25003 DATA 136,208,249,165,218,5,219,240 25004 DATA 33,165,216,197,214,240,27,162 25005 DATA 1,24,22,220,165,216,144,2 25006 DATA 165,214,145,218,200,192,8,240 25007 DATA 4,192,16,208,236,202,16,233 25008 DATA 230,212,96 25996 REM H1:INT2HEX.OBJ 25997 REM SIZE = 72 25998 REM START = 37888 25999 REM END = 37959 26000 DATA 169,0,133,212,133,213,104,240 26001 DATA 62,10,168,201,4,240,5,104 26002 DATA 136,208,252,96,104,153,213,0 26003 DATA 136,208,249,5,215,240,40,216 26004 DATA 162,1,181,216,74,74,74,74 26005 DATA 145,214,181,216,41,15,200,145 26006 DATA 214,200,202,16,237,136,177,214 26007 DATA 201,10,144,2,105,6,105,48 26008 DATA 145,214,136,16,241,230,212,96 26996 REM H1:BITS.OBJ 26997 REM SIZE = 162 26998 REM START = 38144 26999 REM END = 38305 27000 DATA 169,0,133,212,133,213,104,240 27001 DATA 43,10,168,201,6,240,5,104 27002 DATA 136,208,252,96,104,153,213,0 27003 DATA 136,208,249,164,218,240,21,136 27004 DATA 240,19,136,240,29,136,240,39 27005 DATA 136,240,49,136,240,61,136, 27006 DATA 73,136,240,90,96,165,216,5 27007 DATA 214,133,212,165,217,5,215,133 27008 DATA 213,96,165,216,37,214,133,212 27009 DATA 165,217,37,215,133,213,96,165 27010 DATA 216,69,214,133,212,165,217,69 27011 DATA 215,133,213,96,165,216,133,212 27012 DATA 166,214,240,208,74,202,208,252 27013 DATA 133,212,96,165,216,133,212,166 27014 DATA 214,240,193,10,202,208,252,133 27015 DATA 212,96,165,216,133,212,166,214 27016 DATA 240,178,24,106,144,2,9,128 27017 DATA 202,208,247,133,212,96,165,216 27018 DATA 133,212,166,214,240,158,24,42 27019 DATA 144,2,9,1,202,208,247,133 27020 DATA 212,96 Here is the kinder, gentler TESTBIT2 output with the values and bits illustrated: Loading BITS Loading BIT2STR Loading INT2HEX $0000 = 0000 0000 0000 0000 OR $0000 = 0000 0000 0000 0000 === ===== =================== $0000 = 0000 0000 0000 0000 $0101 = 0000 0001 0000 0001 OR $0000 = 0000 0000 0000 0000 === ===== =================== $0101 = 0000 0001 0000 0001 $0000 = 0000 0000 0000 0000 OR $0102 = 0000 0001 0000 0010 === ===== =================== $0102 = 0000 0001 0000 0010 $0101 = 0000 0001 0000 0001 OR $0102 = 0000 0001 0000 0010 === ===== =================== $0103 = 0000 0001 0000 0011 $0000 = 0000 0000 0000 0000 AND $0000 = 0000 0000 0000 0000 === ===== =================== $0000 = 0000 0000 0000 0000 $0101 = 0000 0001 0000 0001 AND $0000 = 0000 0000 0000 0000 === ===== =================== $0000 = 0000 0000 0000 0000 $0000 = 0000 0000 0000 0000 AND $0102 = 0000 0001 0000 0010 === ===== =================== $0000 = 0000 0000 0000 0000 $0101 = 0000 0001 0000 0001 AND $0102 = 0000 0001 0000 0010 === ===== =================== $0100 = 0000 0001 0000 0000 $0000 = 0000 0000 0000 0000 EOR $0000 = 0000 0000 0000 0000 === ===== =================== $0000 = 0000 0000 0000 0000 $0101 = 0000 0001 0000 0001 EOR $0000 = 0000 0000 0000 0000 === ===== =================== $0101 = 0000 0001 0000 0001 $0000 = 0000 0000 0000 0000 EOR $0102 = 0000 0001 0000 0010 === ===== =================== $0102 = 0000 0001 0000 0010 $0101 = 0000 0001 0000 0001 EOR $0102 = 0000 0001 0000 0010 === ===== =================== $0003 = 0000 0000 0000 0011 Testing LSR on Value $81 = 1000 0001 LSR 0 = $81 = 1000 0001 LSR 1 = $40 = 0100 0000 LSR 2 = $20 = 0010 0000 LSR 3 = $10 = 0001 0000 LSR 4 = $08 = 0000 1000 LSR 5 = $04 = 0000 0100 LSR 6 = $02 = 0000 0010 LSR 7 = $01 = 0000 0001 LSR 8 = $00 = 0000 0000 Testing LSL on Value $81 = 1000 0001 LSL 0 = $81 = 1000 0001 LSL 1 = $02 = 0000 0010 LSL 2 = $04 = 0000 0100 LSL 3 = $08 = 0000 1000 LSL 4 = $10 = 0001 0000 LSL 5 = $20 = 0010 0000 LSL 6 = $40 = 0100 0000 LSL 7 = $80 = 1000 0000 LSL 8 = $00 = 0000 0000 Testing ROR on Value $81 = 1000 0001 ROR 0 = $81 = 1000 0001 ROR 1 = $C0 = 1100 0000 ROR 2 = $60 = 0110 0000 ROR 3 = $30 = 0011 0000 ROR 4 = $18 = 0001 1000 ROR 5 = $0C = 0000 1100 ROR 6 = $06 = 0000 0110 ROR 7 = $03 = 0000 0011 ROR 8 = $81 = 1000 0001 Testing ROL on Value $81 = 1000 0001 ROL 0 = $81 = 1000 0001 ROL 1 = $03 = 0000 0011 ROL 2 = $06 = 0000 0110 ROL 3 = $0C = 0000 1100 ROL 4 = $18 = 0001 1000 ROL 5 = $30 = 0011 0000 ROL 6 = $60 = 0110 0000 ROL 7 = $C0 = 1100 0000 ROL 8 = $81 = 1000 0001 The output is considerably easier to understand. So neat, so organized, so obvious and informative! Below are the source files and examples of how to load the machine language routine into BASIC included in the disk image and archive: INT2BITS File List: INT2BITS.M65 Saved Mac/65 source INT2BITS.L65 Mac/65 source listing INT2BITS.T65 Mac/65 source listed to H6: (linux) INT2BITS.ASM Mac/65 assembly listing INT2BITS.TSM Mac/65 assembly listing to H6: (linux) INT2BITS.OBJ Mac/65 assembled machine language program (with load segments) INT2BITS.BIN Assembled machine language program without load segments INT2BITS.LIS LISTed DATA statements for INT2BITS.BIN routine. INT2BITS.TLS LISTed DATA statements for INT2BITS.BIN routine to H6: (linux) MAKEI2B.BAS BASIC program to create the INT2BITS.BIN file. This also contains the INT2BITS routine in DATA statements. MAKEI2B.LIS LISTed version of MAKEI2B.BAS MAKEI2B.TLS LISTed version of MAKEI2B.BAS to H6: (linux) TESTI2B.BAS BASIC program that tests the INT2BITS USR() routines. TESTI2B.LIS LISTed version of TESTI2B.BAS. TESTI2B.TLS LISTed version of TESTI2B.BAS to H6: (linux) ZIP archive of files: Convert_Disk.zip Tar archive of files (remove the .zip after download) Convert_Disk.tgz.zip The Lord will fulfill his purpose for me; your steadfast love, O Lord, endures forever. Do not forsake the work of your hands. Psalm 138:8
  14. Convert Integer To Hex String ============================================================== Part 1 - Introduction http://atariage.com/forums/blog/576/entry-13175-part-1-of-11-simple-assembly-for-atari-basic/ Part 2 - Learn 82.7% of Assembly Language in About Three Pages http://atariage.com/forums/blog/576/entry-13176-part-2-of-11-simple-assembly-for-atari-basic/ Part 3 - The World Inside a USR() Routine http://atariage.com/forums/blog/576/entry-13177-part-3-of-11-simple-assembly-for-atari-basic/ Part 4 - Implement DPEEK() http://atariage.com/forums/blog/576/entry-13178-part-4-of-11-simple-assembly-for-atari-basic/ Part 5 - Implement DPOKE http://atariage.com/forums/blog/576/entry-13180-part-5-of-11-simple-assembly-for-atari-basic/ Part 6 - Various Bit Manipulations http://atariage.com/forums/blog/576/entry-13181-part-6-of-11-simple-assembly-for-atari-basic/ Part 7 - Convert Integer to Hex String http://atariage.com/forums/blog/576/entry-13182-part-7-of-11-simple-assembly-for-atari-basic/ Part 8 - Convert Integer to Bit String http://atariage.com/forums/blog/576/entry-13183-part-8-of-11-simple-assembly-for-atari-basic/ Part 9 - Memory Copy http://atariage.com/forums/blog/576/entry-13184-part-9-of-11-simple-assembly-for-atari-basic/ Part 10 - Binary File I/O Part 1 (XIO is Broken) http://atariage.com/forums/blog/576/entry-13185-part-10-of-11-simple-assembly-for-atari-basic/ Part 11 - Binary File I/O Part 2 (XIO is Broken) http://atariage.com/forums/blog/576/entry-13186-part-11-simple-assembly-for-atari-basic-the-end/ ============================================================== OSS BASIC XL includes the Hex$() function that converts an integer value to a string of hexadecimal digits. That would be a useful accessory to the bit manipulation test program, since byte values are easier to visualize in hexadecimal. The integer to hexadecimal string conversion sounds more simple than it is. An integer made of low byte value, $12, and high byte value, $34, exists in memory in the order: $12, $34. However, the hexadecimal text representation is in reverse order with high byte first, “3412”. Furthermore, the text representation is four characters (bytes) as “3”, “4”, “1”, “2”. This means the two bytes of the integer must be separated into individual nybble values and reorganized in the string order. Next, the actual binary values of each nybble (0 to 15) must be converted into a text character. The ASCII/ATASCII characters “0” through “9” are not contiguous with the characters “A” through “F”. A look-up table of 16 text characters would be the most simple and direct method, but that would not be relocatable. So, a comparison/computation method is used, and discussed below: INT2HEX in Mac/65 Assembler Code 0100 ; INT2HEX.M65 0105 ; 0110 ; Convert 16-bit integer into 0115 ; hex string. 0120 ; 0125 ; INT2HEX USR 2 arguments: 0130 ; Value == Integer to convert. 0135 ; StrAdr == Address of string 0140 ; which must be able 0145 ; to hold 4 characters 0150 ; 0155 ; USR return value 0 means no 0160 ; conversion. Non-Zero means 0165 ; STRADR contains hex string. 0170 ; 0175 ; Use the FR0/FR1 FP register. 0180 ; The return value for BASIC 0185 ; goes in FR0. 0190 ; No FP is used so all of FR0 0195 ; (and more FP registers) can 0200 ; be considered available. 0205 ; 0210 ZRET = $D4 ; FR0 $D4/$D5 Return value 0215 ZARGS = $D5 ; $D6-1 for arg Pulldown loop 0220 ZSTR = $D6 ; FR0 $D6/$D7 STRADR 0225 ZVAL = $D8 ; FR0 $D8/$D9 Integer value 0230 ; 0235 .OPT OBJ 0240 ; 0245 ; Arbitrary. This is relocatable. 0250 ; 0255 *= $9400 0260 ; 0265 INIT 0270 LDA #$00 ; Make sure return 0275 STA ZRET ; value is cleared 0280 STA ZRET+1 ; by default. 0285 PLA ; Get argument count 0290 BEQ EXIT ; Shortcut for no args 0295 ASL A ; Now number of bytes 0300 TAY 0305 CMP #$04 ; Integer Value1, StrAdr 0310 BEQ PULLDOWN 0315 ; 0320 ; Bad args. Clean up for exit. 0325 ; 0330 DISPOSE ; any number of args 0335 PLA 0340 DEY 0345 BNE DISPOSE 0350 RTS ; Abandon ship 0355 ; 0360 ; This code works the same 0365 ; for 1, 4, 8 ... arguments. 0370 ; 0375 PULLDOWN 0380 PLA 0385 STA ZARGS,Y 0390 DEY 0395 BNE PULLDOWN 0400 ; 0405 ; Arg validation. 0410 ; StrAdr may not be null. 0415 ; 0420 LDA ZSTR 0425 ORA ZSTR+1 0430 BEQ EXIT ; zstr is null 0435 ; 0440 CLD 0445 ; 0450 ; Split Integer Value into 0455 ; nybbles and temporarily store 0460 ; in the String output... 0465 ; 0470 LDX #$01 ; X index bytes. 0475 ; Y is already 0 0480 ; Y index string. 0485 ; 0490 SPLIT_BYTES 0495 LDA ZVAL,X ; Byte 0500 ; 0505 ; Right shift 4 to keep 0510 ; the high nybble. 0515 LSR A 0520 LSR A 0525 LSR A 0530 LSR A 0535 STA (ZSTR),Y ; Save for later 0540 ; 0545 ; Now, do the low nybble 0550 LDA ZVAL,X ; Byte again 0555 AND #$0F ; low nybble 0560 INY ; Next char in string 0565 STA (ZSTR),Y ; Save for later 0570 ; 0575 INY ; Next char in string 0580 DEX ; Next integer byte 0585 BPL SPLIT_BYTES 0590 ; 0595 ; Next, convert the nybbles 0600 ; saved in the string into the 0605 ; final ASCII form. 0610 ; 0615 DEY ; Correct string index. 0620 BYTE2HEX 0625 LDA (ZSTR),Y 0630 CMP #$0A ; 10 or greater? 0635 BCC ADD_48 ; No, 0 to 9. 0640 ; 0645 ADC #$06 ; "A"-"0"-$0A-Carry 0650 ADD_48 0655 ADC #$30 0660 STA (ZSTR),Y 0665 DEY ; 3, 2, 1, 0 will continue 0670 BPL BYTE2HEX ; -1 ends loop 0675 ; 0680 INC ZRET ; Successful return 0685 EXIT 0690 RTS 0695 ; 0700 .END There is a new bit of code in the initialization: 0405 ; Arg validation. 0410 ; StrAdr may not be null. 0415 ; 0420 LDA ZSTR 0425 ORA ZSTR+1 0430 BEQ EXIT ; zstr is null 0435 ; 0440 CLD This shows a short way to compare an address (or integer) to NULL/zero. It simply OR's the low and high bytes together. Then if any bit is set it means the address is not NULL. Any non-zero value is considered legitimate (which is not a purely correct assumption, but reasonably good enough for this exercise.) The long way to check the string address would be to load low byte, compare to zero. If it is not zero, then the value is valid and so branch to the code to continue the routine. If it is zero, then load the high byte, compare to zero, and take the successful branch for non-zero and exit the routine if it is zero. That would take six instructions, handily duplicated by the three here. Full validation (not done here) would mean a lot more code making sure the value is a valid address for a BASIC string, or otherwise not anywhere near low memory and not in the ROM area. Finally, this code turns off BCD mode (CLD) because it will use Add instructions on binary values later. The conversion is separated into two activities: First is separating the bytes into nybbles and placing them in the correct order. The second phase is converting the nybbles into the corresponding ASCII/ATASCII characters. The code could be done in just one loop, but that would mean duplicating the nybble to text conversion code twice in the loop. (And that idea could be implemented more efficiently as a subroutine called by JSR, but then the code would not be relocatable.) The first part of the conversion: 0490 SPLIT_BYTES 0495 LDA ZVAL,X ; Byte 0500 ; 0505 ; Right shift 4 to keep 0510 ; the high nybble. 0515 LSR A 0520 LSR A 0525 LSR A 0530 LSR A 0535 STA (ZSTR),Y ; Save for later 0540 ; 0545 ; Now, do the low nybble 0550 LDA ZVAL,X ; Byte again 0555 AND #$0F ; low nybble 0560 INY ; Next char in string 0565 STA (ZSTR),Y ; Save for later 0570 ; 0575 INY ; Next char in string 0580 DEX ; Next integer byte 0585 BPL SPLIT_BYTES There are two loops happening here. One loop, indexed by X, counts in reverse, 1 to 0, from the high byte to the low byte of the integer. Within that loop is the activity to separate the two nybbles in the byte. Mixed in this loop is another loop, indexed by Y, counting from 0 to 3 to index each character position of the string. After the routine liberates the nybbles from their byte it stores the nybbles in their respective positions in the string. The Y index is thus incremented twice (two characters) for each decrement (one byte) of the X loop. The end result is a “string” containing the four, binary nybble values each in a separate “character”. The binary values are not human readable, so the routine must convert the binary values to corresponding ASCII/ATASCII characters: 0620 BYTE2HEX 0625 LDA (ZSTR),Y 0630 CMP #$0A ; 10 or greater? 0635 BCC ADD_48 ; No, 0 to 9. 0640 ; 0645 ADC #$06 ; "A"-"0"-$0A-Carry 0650 ADD_48 0655 ADC #$30 0660 STA (ZSTR),Y 0665 DEY ; 3, 2, 1, 0 will continue 0670 BPL BYTE2HEX ; -1 ends loop This second part of the conversion code loops though the string in reverse converting each byte value into the corresponding ASCII/ATASCII value. The obvious method to do this would be a lookup table that translates the 16 possible values into the corresponding character. However, a lookup table is not easily relocatable, so this version does the conversion by value comparisons and math. Here is the problem laid out in one table. The math must convert one series of values (“Values”) into another series of values (“Text Values”) that will print as text characters. Values ASCII Text Text Values Difference $00 “0” $30 $30 $01 “1” $31 $30 $02 “2” $32 $30 $03 “3” $33 $30 $04 “4” $34 $30 $05 “5” $35 $30 $06 “6” $36 $30 $07 “7” $37 $30 $08 “8” $38 $30 $09 “9” $39 $30 $0A “A” $41 $37 $0B “B” $42 $37 $0C “C” $43 $37 $0D “D” $44 $37 $0E “E” $45 $37 $0F “F” $46 $37 This tells us a few facts. The original series (“Values”) breaks down to two destination series with different offsets, $30 and $37, and that the value of the offset increases rather than decreases ($30 < $37) with each series. Since the $A to $F text values are after the $0 to $9 values, then only addition is needed to convert the binary “Values” series into the “Text Values” series. So, the code merely needs to determine whether the value is in the first series or the second series and then add the difference accordingly. However, to squeeze code size a small bit of cleverness is added that capitalizes on the relationship between the offsets for each series. At each position the code tests to determine if the value is in the $0 to $9 range or the $A to $F range. Values $0 to $9 result in a branch to code that adds $30 to the value, converting binary values $0 to $9 to ASCII/ATASCII values “0” to “9” (or $30 to $39). That branch bypasses other code below for handling values $A to $F. The conversion for values $A to $F is in two parts – first, the code adds an offset. On first glance at the table above this offset should be the difference of $37 - $30 or $7. The first two conditions below do result in the $07 offset. But, an easily overlooked condition in the code is the carry bit acquired by the comparison. So, the offset value is decremented to $6 to compensate for the carry bit: 1. the difference between the “A” and “0” characters ($41 - $30 = $11) and then 2. it subtracts the integer value of the start of the $A to $F range ($11 - $A == $7) and then 3. it removes the Carry bit that is acquired by the comparison operation ($7 - $1 == $6). After adding the offset the execution path falls into the same section used for $0 to $9 which adds $30 to the value. So, for binary value $A the end result is $A + $6 + Carry + $30 which is $41 or ASCII/ATASCII “A”. The entire working code is 15 bytes long. The actual code using a lookup table to convert binary to ASCII would be much shorter (and faster), but still require an additional 16 bytes of supporting data for the translation table. Since BASIC can pass the address of the string data, not not the control information of the string, the machine language routine has no way of knowing the size of the string – either the DIM'ensioned size or the actual string length. Therefore the machine language code can neither limit itself to the maximum string length nor change the string's current length while populating the text value. A BASIC program using the results of the conversion must insure that it sets the real length of the string prior to calling the conversion function. This is simple – the BASIC program need only assign a value to the string that is four characters long prior to using the machine language routine. Such as: 130 DIM A$(4) 140 A$=" ":REM FOUR SPACES The conversion routine overwrites the contents of the string replacing the blank spaces with the hexadecimal text string. The BASIC program can then safely refer to the string contents, like this: 150 RH=USR(INT2HEX,4660,ADR(A$)) 160 ? 4660;"(dec) = $";A$ 170 ? "Low Byte = $";A$(3,4) 180 ? "High Byte = $";A$(1,2) which results in this output: 4660(dec) = $1234 Low Byte = $34 High Byte = $12 Below are the source files and examples of how to load the machine language routine into BASIC included in the disk image and archive: INT2HEX File List: INT2HEX.M65 Saved Mac/65 source INT2HEX.L65 Mac/65 source listing INT2HEX.T65 Mac/65 source listed to H6: (linux) INT2HEX.ASM Mac/65 assembly listing INT2HEX.TSM Mac/65 assembly listing to H6: (linux) INT2HEX.OBJ Mac/65 assembled machine language program (with load segments) INT2HEX.BIN Assembled machine language program without load segments INT2HEX.LIS LISTed DATA statements for INT2HEX.BIN routine. INT2HEX.TLS LISTed DATA statements for INT2HEX.BIN routine to H6: (linux) MAKEI2H.BAS BASIC program to create the INT2HEX.BIN file. This also contains the INT2HEX routine in DATA statements. MAKEI2H.LIS LISTed version of MAKEI2H.BAS MAKEI2H.TLS LISTed version of MAKEI2H.BAS to H6: (linux) TESTI2H.BAS BASIC program that tests the INT2HEX USR() routines. TESTI2H.LIS LISTed version of TESTI2H.BAS. TESTI2H.TLS LISTed version of TESTI2H.BAS to H6: (linux) But, let's say that you're really so lazy that its still too much effort for you to visualize the bits in each hex value. The solution to that problem is the next routine that converts a 16-bit integer into a string of 16 characters where each character represents a bit value, 0 or 1. ZIP archive of files: Convert_Disk.zip Tar archive of files (remove the .zip after download) Convert_Disk.tgz.zip Do not be anxious about anything, but in everything by prayer and supplication with thanksgiving let your requests be made known to God. And the peace of God, which surpasses all understanding, will guard your hearts and your minds in Christ Jesus. Philippians 4:6-7
  15. DPOKE ============================================================== Part 1 - Introduction http://atariage.com/forums/blog/576/entry-13175-part-1-of-11-simple-assembly-for-atari-basic/ Part 2 - Learn 82.7% of Assembly Language in About Three Pages http://atariage.com/forums/blog/576/entry-13176-part-2-of-11-simple-assembly-for-atari-basic/ Part 3 - The World Inside a USR() Routine http://atariage.com/forums/blog/576/entry-13177-part-3-of-11-simple-assembly-for-atari-basic/ Part 4 - Implement DPEEK() http://atariage.com/forums/blog/576/entry-13178-part-4-of-11-simple-assembly-for-atari-basic/ Part 5 - Implement DPOKE http://atariage.com/forums/blog/576/entry-13180-part-5-of-11-simple-assembly-for-atari-basic/ Part 6 - Various Bit Manipulations http://atariage.com/forums/blog/576/entry-13181-part-6-of-11-simple-assembly-for-atari-basic/ Part 7 - Convert Integer to Hex String http://atariage.com/forums/blog/576/entry-13182-part-7-of-11-simple-assembly-for-atari-basic/ Part 8 - Convert Integer to Bit String http://atariage.com/forums/blog/576/entry-13183-part-8-of-11-simple-assembly-for-atari-basic/ Part 9 - Memory Copy http://atariage.com/forums/blog/576/entry-13184-part-9-of-11-simple-assembly-for-atari-basic/ Part 10 - Binary File I/O Part 1 (XIO is Broken) http://atariage.com/forums/blog/576/entry-13185-part-10-of-11-simple-assembly-for-atari-basic/ Part 11 - Binary File I/O Part 2 (XIO is Broken) http://atariage.com/forums/blog/576/entry-13186-part-11-simple-assembly-for-atari-basic-the-end/ ============================================================== Writing a two-byte, 16-bit value (DPOKE) goes hand-in-hand with reading (DPEEK). An 8-bit computer environment has many opportunities for managing 16-bit address and the Atari environment is no exception. Address of display lists, Address of screen memory. Addresses of buffers. Address pointers abound. OSS BASIC XL provides the DPOKE command to perform a 16-bit, two-byte write to a specified memory location. Like DPEEK, this can be duplicated in regular Atari BASIC, although much slower. So, in BASIC XL the action: Dpoke Address, Value is frequently seen in Atari BASIC programs expressed as: POKE ADDRESS, VALUE - INT( VALUE / 256 ) * 256 POKE ADDRESS + 1, INT( VALUE / 256 ) That’s a bit more complicated than DPEEK. Here the value must be broken down into the high byte (integer result of the value divided by 256) and the low byte (the remainder after division), and then the two values assigned to the two consecutive bytes in memory in low byte, high byte order. This can also be worked into a reusable subroutine in Atari BASIC: 10 DPOKE=28500 20 ADDRESS=560:VALUE=39968:GOSUB DPOKE 30 END. . . 28497 REM IMPLEMENT DPOKE 28498 REM INPUT = ADDRESS 28499 REM INPUT = VALUE 28500 HB=INT(VALUE/256) 28501 POKE ADDRESS,VALUE-HB*256:POKE ADDRESS+1,HB 28502 RETURN This is still fairly simple code. A small difference here is the high byte is calculated and retained to avoid multiple occurrences of the same floating point division. But, again, expressed in Atari BASIC this is fairly slow using one floating point division and one multiplication. As before, applying assembly language rocketry yields: DPOKE in Mac/65 Assembler Code 0100 ; DPOKE 0105 ; 0110 ; Write a 16-bit word into 0115 ; the specified address. 0120 ; 0125 ; USR 2 arguments: 0130 ; Addr == address to write. 0135 ; Val == Value to write. 0140 ; 0145 ; USR return value is 0. 0150 ; 0155 ; Use the FR0 FP register. 0160 ; The return value for BASIC 0165 ; goes in FR0. 0170 ; No FP is used so all of FR0 0175 ; (and more FP registers) can 0180 ; be considered available. 0185 ; 0190 ZRET = $D4 ; FR0 $D4/$D5 Return value 0195 ZARGS = $D5 ; $D6-1 for arg Pulldown loop 0200 ZVAL = $D6 ; FR0 $D6/$D7 Value 0205 ZADDR = $D8 ; FR0 $D8/$D9 Address 0210 ; 0215 .OPT OBJ 0220 ; 0225 ; Arbitrary. This is relocatable. 0230 ; 0235 *= $9600 0240 ; 0245 INIT 0250 PLA ; argument count 0255 TAY 0260 BEQ EXIT ; shortcut for no args 0265 ASL A ; now number of bytes 0270 TAY 0275 CMP #$04 ; Value, Address 0280 BEQ PULLDOWN 0285 ; 0290 ; Bad args. Clean up for exit. 0295 ; 0300 DISPOSE ; any number of args 0305 PLA 0310 DEY 0315 BNE DISPOSE 0320 BEQ EXIT 0325 ; 0330 ; This code works the same 0335 ; for 1, 4, 8 ... arguments. 0340 ; 0345 PULLDOWN 0350 PLA 0355 STA ZARGS,Y 0360 DEY 0365 BNE PULLDOWN 0370 ; 0375 ; Y is already zero here. 0380 ; 0385 LDA ZVAL 0390 STA (ZADDR),Y 0395 INY ; now Y is 1 0400 LDA ZVAL+1 0405 STA (ZADDR),Y 0410 ; 0415 ; On failure Y = 0 0420 ; On Success Y = 1 0425 ; 0430 EXIT ; 0435 STY ZRET 0440 STY ZRET+1 0445 RTS ; bye. 0450 ; 0455 .END The error detection and handling have a few differences from DPEEK. The DPOKE routine does not inherently require a response value where DPEEK does, so the DPOKE code does not have separate, early-exit code and uses only one place to set the response value merely to indicate general success or failure. This code uses an additional Page Zero value to accept the value to write to the address: 0190 ZRET = $D4 ; FR0 $D4/$D5 Return value 0195 ZARGS = $D5 ; $D6-1 for arg Pulldown loop 0200 ZVAL = $D6 ; FR0 $D6/$D7 Value 0205 ZADDR = $D8 ; FR0 $D8/$D9 Address The real work part of the code is obviously different since writing bytes into memory (DPOKE) is not the same as reading bytes from memory (DPEEK): 0375 ; Y is already zero here. 0380 ; 0385 LDA ZVAL 0390 STA (ZADDR),Y 0395 INY ; now Y is 1 0400 LDA ZVAL+1 0405 STA (ZADDR),Y The DPOKE version above conforms to my standard plan (more or less) for stack management and error handling. Most of the remaining utilities will stick with this plan. However, for the sake of (averting) argument there are admittedly more optimal ways to accomplish this depending on the programmer's definition of optimal. For instance, the value to write to the address need not be temporarily stored in Page Zero. After the code pulls the target address from the stack and establishes it in Page Zero it could pull the value and directly store it to the target address, like this: 0375 ; Y must be 1 here. 0380 ; 0385 PLA ; high byte 0390 STA (ZADDR),Y 0395 DEY ; now Y is 0 0400 PLA ; low byte 0405 STA (ZADDR),Y That does look clever as it subtracts two bytes from the code, and removes ZVAL from Page Zero. However, to make this work the argument handling must be different to pull two, not four bytes worth of arguments, and the Y value must be reset to 1 before arriving at this code, and the error return value needs to be reworked. In other words, the code savings in one place may be consumed by code needed in another place. The standardized approach may not be the most optimal, but it is conceptually consistent, flexible, and doesn't need to be re-factored from scratch for each different routine. Also, regardless of the degree of inefficiency in this less-than-optimal method the execution time is negligible compared to slower Atari BASIC. More time is spent by Atari BASIC converting the floating point values into 16-bit integers to pass as arguments to the machine language routine than the amount of time for execution of the the machine language routine. Testing DPEEK and DPOKE The Atari BASIC program below, TESTDPK.BAS, demonstrates how to load the machine language routines into strings and it tests the two routines: 0 REM H1:TESTDPK.BAS 100 REM TEST DPEEK AND DPOKE USR() TOOLS 110 GRAPHICS 0:POKE 710,0:POKE 82,0 120 GOSUB 10000 130 REM 140 REM TEST DPEEK 150 REM 155 ? "*** DPEEK ***" 160 DL=PEEK(560)+256*PEEK(561) 170 ? "BASIC Dpeek Display List = ";DL 180 DL=0 190 DL=USR(DPEEK,560) 200 ? "USR() Dpeek Display List = ";DL 210 REM 220 REM TEST DPOKE 230 REM 240 CL=10+256*68:REM 711=10,712=68 250 X=USR(DPOKE,711,CL) 260 REM 270 REM VERIFY WITH DPEEK 280 REM 290 ? "*** DPOKE ***" 300 ? "10 + 256 * 68 = ";10+256*68 310 CL=PEEK(711)+256*PEEK(712) 320 ? "BASIC After Dpoke = ";CL 330 CL=0 340 CL=USR(DPEEK,711) 350 ? "USR() After Dpoke = ";CL 360 END 970 REM 980 REM DPEEK AND DPOKE 990 REM 10000 DIM DPE$(36),DPO$(38) 10001 DPEEK=ADR(DPE$):DPOKE=ADR(DPO$) 10010 RESTORE 27000 10011 FOR I=0 TO 35 10100 READ D:POKE DPEEK+I,D 10101 NEXT I 10110 FOR I=0 TO 36 10111 READ D:POKE DPOKE+I,D 11000 NEXT I 11001 RETURN 26999 REM dpeek.obj 27000 DATA 104,168,240,10,10,168,201,2 27010 DATA 240,9,104,136,208,252,132,212 27020 DATA 132,213,96,104,153,213,0,136 27030 DATA 208,249,177,214,133,212,200,177 27040 DATA 214,133,213,96 28999 REM dpoke.obj 29000 DATA 104,168,240,28,10,168,201,4 29001 DATA 240,6,104,136,208,252,240,16 29002 DATA 104,153,213,0,136,208,249,165 29003 DATA 214,145,216,200,165,215,145,216 29004 DATA 132,212,132,213,96 First the program performs DPEEK the slow way in BASIC by calculating the 16-bit address of the display list (at locations 560 and 561) with a multiply in BASIC (line 160). Then the program performs the same action using the DPEEK USR() routine (line 190). The value determined by each method is displayed for comparison: Next the program tests the DPOKE USR() routine by assigning a 16-bit value to locations 711 and 712. First it calculates the 16-bit value and reports it. Then it performs DPOKE via the USR() routine (Line 250). Then it retrieves the contents of the two memory locations by the slower BASIC method for DPEEK and by the DPEEK USR() routine. Finally, it reports the value determined by each method to verify the value was written to memory correctly: Below are the source files and examples of how to load the machine language routine into BASIC included in the disk image and archive: DPOKE File List: DPOKE.M65 Saved Mac/65 source DPOKE.L65 Mac/65 source listing DPOKE.T65 Mac/65 source listed to H6: (linux) DPOKE.ASM Mac/65 assembly listing DPOKE.TSM Mac/65 assembly listing to H6: (linux) DPOKE.OBJ Mac/65 assembled machine language program (with load segments) DPOKE.BIN Assembled machine language program without load segments DPOKE.LIS LISTed DATA statements for DPOKE.BIN routine. DPOKE.TLS LISTed DATA statements for DPOKE.BIN routine to H6: (linux) MAKEDPOK.BAS BASIC program to create the DPOKE.BIN file. This also contains the DPOKE routine in DATA statements. MAKEDPOK.LIS LISTed version of MKDPOKE.BAS MAKEDPOK.TLS LISTed version of MKDPOKE.BAS to H6: (linux) TESTDPK.BAS BASIC program that tests the DPEEK and DPOKE USR() routines. TESTDPK.LIS LISTed version of TESTDPK.BAS. TESTDPK.TLS LISTed version of TESTDPK.BAS to H6: (linux) ZIP archive of files: Dpoke_Disk.zip Tar archive of files (remove the .zip after download) Dpoke_Disk.tgz.zip For we are his workmanship, created in Christ Jesus for good works, which God prepared beforehand, that we should walk in them. Ephesians 2:10
  16. DPEEK ============================================================== Part 1 - Introduction http://atariage.com/forums/blog/576/entry-13175-part-1-of-11-simple-assembly-for-atari-basic/ Part 2 - Learn 82.7% of Assembly Language in About Three Pages http://atariage.com/forums/blog/576/entry-13176-part-2-of-11-simple-assembly-for-atari-basic/ Part 3 - The World Inside a USR() Routine http://atariage.com/forums/blog/576/entry-13177-part-3-of-11-simple-assembly-for-atari-basic/ Part 4 - Implement DPEEK() http://atariage.com/forums/blog/576/entry-13178-part-4-of-11-simple-assembly-for-atari-basic/ Part 5 - Implement DPOKE http://atariage.com/forums/blog/576/entry-13180-part-5-of-11-simple-assembly-for-atari-basic/ Part 6 - Various Bit Manipulations http://atariage.com/forums/blog/576/entry-13181-part-6-of-11-simple-assembly-for-atari-basic/ Part 7 - Convert Integer to Hex String http://atariage.com/forums/blog/576/entry-13182-part-7-of-11-simple-assembly-for-atari-basic/ Part 8 - Convert Integer to Bit String http://atariage.com/forums/blog/576/entry-13183-part-8-of-11-simple-assembly-for-atari-basic/ Part 9 - Memory Copy http://atariage.com/forums/blog/576/entry-13184-part-9-of-11-simple-assembly-for-atari-basic/ Part 10 - Binary File I/O Part 1 (XIO is Broken) http://atariage.com/forums/blog/576/entry-13185-part-10-of-11-simple-assembly-for-atari-basic/ Part 11 - Binary File I/O Part 2 (XIO is Broken) http://atariage.com/forums/blog/576/entry-13186-part-11-simple-assembly-for-atari-basic-the-end/ ============================================================== Since one byte can hold the value 0 to 255 a value larger than 255 requires two bytes. The second byte takes the place value 256. Just as Base10 has “ones” values 0 to 9, and then “tens” value for the next position, so the two bytes provide a Base256 value – “ones” value 0 to 255 in the first position and then 256 as the “tens” in the second position. The two-digit value limit for Base10: multiply the maximum base value by 10 for the “tens” value and add the maximum base value of the “ones”, or 9 * 10 + 9 = 99. The same applies for Base256: multiply the maximum base value by 256 for the “tens” and then add the maximum base value as the “ones”, or 255 * 256 + 255 == 65,535. (Or, in hex this is $FF * $100 + $FF == $FFFF.) So, the real value limit of 16-bits starts at 0 and ends at 65,535 or $FFFF. The 64K address space of an 8-bit computer is described by a 16-bit value. So, the Atari environment is liberally sprinkled with 16-bit values as addresses, pointers, and larger integer values. Manipulating 16-bit values is complementary to working in the Atari computing environment, but Atari BASIC does not provide a direct and easy method for this. OSS BASIC XL provides the Dpeek() function to perform a 16-bit, two-byte PEEK of the value at a specified memory location. This can also be duplicated in regular Atari BASIC, although slower. In BASIC XL the action: Value = Dpeek( Address ) is frequently seen in Atari BASIC programs expressed as: VALUE = PEEK( ADDRESS ) + 256 * PEEK( ADDRESS + 1 ) That’s not very complicated. A little programming grease mixed with Atari BASIC’s ability to GOSUB to a variable produces this reusable subroutine: 10 DPEEK=28000 20 ADDRESS=560:GOSUB DPEEK 30 ? "DPEEK(560)= ";VALUE 40 END. . . 27997 REM IMPLEMENT DPEEK 27998 REM INPUT = ADDRESS 27999 REM OUTPUT = VALUE 28000 VALUE=PEEK(ADDRESS)+256*PEEK(ADDRESS+1) 28001 RETURN The routine is simple; just one real line of BASIC code. But, execution in Atari BASIC is fairly slow, since it includes a floating point multiplication. Infrequent slothfulness is forgivable, but repeated use causes obvious performance drag. What this problem needs is a little assembly language propulsion... DPEEK Mac/65 Assembler Code 0100 ; DPEEK 0105 ; 0110 ; Return the 16-bit contents 0115 ; at the specified address. 0120 ; 0125 ; USR 1 arguments: 0130 ; Addr == address of value. 0135 ; 0140 ; USR return value is 16-bit 0145 ; contents of address. 0150 ; 0155 ; Use the FR0 FP register. 0160 ; The return value for BASIC 0165 ; goes in FR0. 0170 ; No FP is used so all of FR0 0175 ; (and more FP registers) can 0180 ; be considered available. 0185 ; 0190 ZRET = $D4 ; FR0 $D4/$D5 Return value 0195 ZARGS = $D5 ; $D6-1 for arg Pulldown loop 0200 ZADDR = $D6 ; FR0 $D6/$D7 Address 0205 ; 0210 .OPT OBJ 0215 ; 0220 ; Arbitrary. This is relocatable. 0225 ; 0230 *= $9700 0235 ; 0240 INIT 0245 PLA ; argument count 0250 TAY 0255 BEQ EXIT_ERR ; shortcut for no args 0260 ASL A ; now number of bytes 0265 TAY 0270 CMP #$02 ; Address 0275 BEQ PULLDOWN 0280 ; 0285 ; Bad args. Clean up for exit. 0290 ; 0295 DISPOSE ; any number of args 0300 PLA 0305 DEY 0310 BNE DISPOSE 0315 ; 0320 EXIT_ERR ; return "error" 0325 STY ZRET ; Y is Zero 0330 STY ZRET+1 ; 0335 RTS ; bye. 0340 ; 0345 ; This code works the same 0350 ; for 1, 4, 8 ... arguments. 0355 ; 0360 PULLDOWN 0365 PLA 0370 STA ZARGS,Y 0375 DEY 0380 BNE PULLDOWN 0385 ; 0390 ; Y is already zero here. 0395 ; 0400 LDA (ZADDR),Y 0405 STA ZRET 0410 INY 0415 LDA (ZADDR),Y 0420 STA ZRET+1 0425 ; 0430 RTS ; bye. 0435 ; 0440 .END In this first example I’ll walk through all the setup and safety checks. This will be similar for most of the other utilities. Some programmers would consider that there is much more code here than required. The safety checks look like overkill, because the working code for this particular utility is so short. Protecting the BASIC programmer from torpedoing the system is important enough to justify protection. From the point of view of execution timing this overhead is inconsequential compared to the speed of BASIC. First of all, the routine is relocatable which means the code makes no absolute references to locations within itself. All branches are relative to the current location, so the code could execute from almost anywhere in memory. However, it does need a couple of fixed locations for working values in the program: 0155 ; Use the FR0 FP register. 0160 ; The return value for BASIC 0165 ; goes in FR0. 0170 ; No FP is used so all of FR0 0175 ; (and more FP registers) can 0180 ; be considered available.0185 ; 0190 ZRET = $D4 ; FR0 $D4/$D5 Return value 0195 ZARGS = $D5 ; $D6-1 for arg Pulldown loop 0200 ZADDR = $D6 ; FR0 $D6/$D7 Address The program needs two values – the address from which to PEEK the 16-bit value, and a place to put the 16-bit value for BASIC to reference. Specifically, the code is using the Page Zero locations. “Page Zero” means all the address locations where the high byte of the address is $00. (The 256 locations from address $0000 to $00FF). Page Zero locations are chosen for several reasons: 6502 instructions referencing Page Zero are one byte shorter and usually execute faster than instructions referencing other pages. The 6502 has useful addressing modes that only work for Page Zero references. BASIC already defines a place in Page Zero for the value the machine language routine returns to BASIC. Since Page Zero locations are so useful they are also highly contested. The Atari OS defines and uses just about every byte in the first half of Page Zero. BASIC and the Floating Point routines use almost all of the second half of Page Zero. As it turns out the locations used by this machine language routine are “claimed” by the Floating Point routines. However, as long as the machine language routine does not need to use the Floating Point routines then these locations are free for use. Now to the working code. The routine begins by pulling the argument count from the stack and checking for zero arguments. If it finds no arguments to process then the routine branches to another location for an early exit. Yes, the TAY instruction is not needed to correctly branch for no arguments. The PLA instruction sets the zero flag when the argument count popped from the stack is zero. However, the exit code will use the Y register to return an error value (which is zero) to BASIC: 0240 INIT 0245 PLA ; argument count 0250 TAY 0255 BEQ EXIT_ERR ; shortcut for no args Next, the routine converts the number of arguments into the number of bytes to pull from the stack. It uses ASL which is the same as multiplying the value in the Accumulator times two. This is stored in the Y register which is used as an index for later loops. The routine verifies the argument count is correct (only 1 argument – the address to PEEK) which is 2 bytes. If this is correct the routine branches to the code that will pull the stack values and place them in Zero page memory for use later: 0260 ASL A ; now number of bytes 0265 TAY 0270 CMP #$02 ; Address 0275 BEQ PULLDOWN If the argument count is incorrect then the routine discards the argument values on the stack, so it can safely exit. Remember the Y register already contains the number of argument bytes on the stack: 0295 DISPOSE ; any number of args 0300 PLA 0305 DEY 0310 BNE DISPOSE Next, the routine falls into the exit section. Earlier, if there were no arguments the routine branched here directly. Note that if there are no arguments the Y register contained 0 when it branched here directly, and at the conclusion of cleaning up the stack in the DISPOSE loop the Y register will also be 0. So, in either case of bad arguments, too many or too few, the return value to BASIC is cleared to 0: 0320 EXIT_ERR ; return "error" 0325 STY ZRET ; Y is Zero 0330 STY ZRET+1 ; 0335 RTS ; bye. Clearing the return value really isn’t necessary and isn’t exceedingly useful beyond insuring random values are not returned to BASIC in the case of an error. Returning a real error would require the USR() pass another argument from BASIC that the routine would use to indicate success or failure. However, the DPEEK action is so simple that this level of error detection begins to enter the arena of silly. The error detection shown here already leans toward overkill and is done for design consistency with the other utilities covered later. At this point the arguments are correct. The routine pulls the values from the stack and places them in Page Zero locations $D6/$D7 referred to as ZADDR. On entry to this point in the code the Y register contains the number of bytes to pull from the stack (always 2 for this routine). The loop pulls them off the stack and places them into memory descending as it goes, because the high byte is pulled first, then the low byte. The base address is not ZADDR, but ZARGS, a value defined one byte less than ZADDR. This is because the Y value will be used as an index from the number of argument bytes (2, 4, 6, etc.) counting down to 1, not to 0. Counting backwards results in the stack values placed in memory in the correct low byte, high byte order used by the 6502. When Y reaches 0 it falls out of the loop: 0360 PULLDOWN 0365 PLA 0370 STA ZARGS,Y 0375 DEY 0380 BNE PULLDOWN This code works for any number of arguments as long as the destination can be sequential bytes in memory. However, this is admittedly overkill for only one argument. More explicit code that directly pulls one 16-bit argument from the stack requires the same number of instructions, but is one byte less (and executes faster). Just for reference: 0360 PULLDOWN 0365 PLA 0370 STA ZADDR+1 ; high byte first. 0375 PLA 0380 STA ZADDR ; low byte next. The actual work to perform the double byte peek is just the five instructions before the final RTS. The routine reads two bytes through the address contained in ZADDR ($D6/$D7) and copies the bytes to the return value, ZRET ($D4/$D5): 0400 LDA (ZADDR),Y 0405 STA ZRET 0410 INY 0415 LDA (ZADDR),Y 0420 STA ZRET+1 After the routine exits Atari BASIC converts the value stored in locations $D4/$D5 (ZRET in the code) into a floating point value. This becomes the return value from USR(). So, in the following BASIC statement the variable X is assigned the value taken from $D4/$D5: 250 X=USR(DPEEK,560): REM DISPLAY LIST ADDRESS Below are source files and examples of how to load the machine language routine into BASIC included in the disk image and archive: DPEEK File List: DPEEK.M65 Saved Mac/65 source DPEEK.L65 Mac/65 source listing DPEEK.T65 Mac/65 source listed to H6: (linux) DPEEK.ASM Mac/65 assembly listing DPEEK.TSM Mac/65 assembly listing to H6: (linux) DPEEK.OBJ Mac/65 assembled machine language program (with load segments) DPEEK.BIN Assembled machine language program without load segments MKDPEEK.BAS BASIC program to create the DPEEK.BIN file. This also contains the DPEEK routine in DATA statements. MKDPEEK.LIS LISTed version of MKDPEEK.BAS MKDPEEK.TLS LISTed version of MKDPEEK.BAS to H6: (linux) DPEEK.BAS BASIC program that loads DATA statements into a string. DPEEK.LIS LISTed version of DPEEK.BAS DPEEK.TLS LISTed version of DPEEK.BAS to H6: (linux) The next task should be a test program to demonstrate using DPEEK in BASIC. But, since DPEEK isn’t much use without DPOKE, we will visit DPOKE first before using the machine language routines together in a BASIC program. ZIP archive of files: Dpeek_Disk.zip Tar archive of files (remove the .zip after download) Dpeek_Disk.tgz.zip Therefore I tell you, do not be anxious about your life, what you will eat or what you will drink, nor about your body, what you will put on. Is not life more than food, and the body more than clothing? Look at the birds of the air: they neither sow nor reap nor gather into barns, and yet your heavenly Father feeds them. Are you not of more value than they? And which of you by being anxious can add a single hour to his span of life? And why are you anxious about clothing? Consider the lilies of the field, how they grow: they neither toil nor spin, yet I tell you, even Solomon in all his glory was not arrayed like one of these. Matthew 6:25-34
  17. I picked up this cart and I didn't recall seeing it before, and I'm betting it is on every cart, but it's neat anyway. Kinda makes me want to put paper over it and scribble out the pattern. ian
  18. Who needs some 8-bit classics, especially some of the harder to find XE titles? All tested and working perfectly; cart-only unless otherwise noted. PM for pics and we can chat about a price that works for both of us. Thanks! Cartridge-based: Crime Buster XE Donkey Kong XE AtariArtist (silver label) AtariWriter Pilot Tape-based: Caverns of Khafka cassette tape + box UNTESTED but I received it brand new - only opened it because I needed the manual for my collection Let me know if you have any q's! Need a manual? Check out my "manual man" post for a semi-complete list (still worth inquiring about others that I may have)!
  19. I'm working on getting Atari 8-Bit set up in RetroPie using the Atari800 emulator and most everything seems to be working just fine except for this: My WASD keys don't deliver any input at all. I've tried multiple keyboards, checked to make sure they aren't currently bound to joystick controls in the emulator and I assume there is somewhere in the emulator which is binding them to "movement" but I can't seem to find it. So right now, any game that requires pressing "S" to start, like the Scott Adams adventure games, I can't do anything with because my "S" key doesn't work. I've scoured Google looking for solution but it seems there aren't many people who have set up Atari800 on the RetroPie because it most certainly isn't the most user friendly of emulators. Anyone else run into this same issue and found a solution? Thank you in advance for your help!
  20. I am digging out some of the rare stuff in my collection (after 20+ years of storage) and wondering what the value on some of them are... Specifically, here's an Atari 1200 prototype unit. I haven't tried to turn it on, but it worked years ago when I last powered it up. I have a couple of 8-bit prototypes, 5200 prototypes, and quite a few 2600 prototypes I'll be asking about soon, too! Thanks for your help...
  21. I'm presently in the market for an Atari XE game system. The bare minimum I want is the console & keyboard with any accessories for TV hookup. If anyone has the whole setup - console, keyboard, joystick, light gun, Flight Simulator and Bug Hunt Cartridges, hookup accessories - that's even better. I reside in the USA. While I am willing to pay for shipping in the USA and Canada, I will not pay shipping costs for any units from elsewhere (UK, Europe, etc). Anyone who has the above and is willing to part with it can either reply here or contact me via DM. If we decide to do business and close the deal, I'm willing to cut a check but can negotiate other payment terms.
  22. SOLD For Sale: Indus GT for Atari 8-bit machines. Tested, fully working, smoked door intact. The whole unit is very nice. Sorry I just realized the pictures are out of focus. If you need better pics, let me know. Has all the disks with their manuals, plus the overall disk drive manual. I rarely see one of these for sale with all of the disks and manuals... in their original Indus sleeves too. Has original working power supply with the "For use with Indus GT only" sticker wrapped around the input side. No disappointments here... goes great with your 800xl, 1200xl or any other Atari computer. $100 plus actual shipping. I'll double box it and pack it carefully. Prefer cash (PayPal), but will consider trades, tell me what you have, I'll tell you if I'm intersted!
  23. Rewindgames will release ANG Software's Greatest Hits on October 24th 2016. Worldwide premiere at the Abbuc JHV Meeting on October 22nd in Herten Germany. This premium collectors edition features the 6 best Dutch puzzle games for the Atari 8-bit: Johnny the Ghost, Mines!, Simple Minds, Shit!, Thinx and Whoops 2 on one disk! For sale at Rewindgames.com from October 24th 2016 for only € 12.99. Exclusive premiere discount at the Abbuc JHV. Buy it in Herten, Germany for only € 9.95! https://youtu.be/OBwwmjXkwQo
  24. Hi, New/old A8 basic coder here (haven't touched it in ~30y). Trying to remember how to use ML strings to redefine character sets... My most immediate problem is that I can't get certain characters on the screen. CHR$(127) does not output the right-facing triangle character. I need to get that in a string to redefine my character set but I'm forgetting how. The screenshot attached shows another person's program that contains said character in a string, but I don't know how it was done. I have access to Atari800MacX, Envision on a windows XP VM, and I'm open to other mac/win/a8 tools if there's an easy-peasy character set generator that outputs what I need. I also don't mind converting the values by hand into ATASCII but I figure there's easier ways My requirements are that the code be self-contained in the basic file and that it be as compact as possible, so I can't use external files to load the charset data, and I figure strings are better than data statements.
  25. pmgraphics

    chiseler 30677

    From the album: Chiseler

    Atari computer classic type-in-code video game. Youtube link to video since album doesn't seem to take videos.
×
×
  • Create New...