# This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # # This archive contains: # Makefile Notes Snarf Cli.doc # a2.c a2.h curses.c disk.c # tbl.c # echo x - Makefile cat >Makefile <<'@EOF' # # Use -O if you can. It gave a 20% speedup on one test. # CFLAGS = -O LIBS = -lcurses -ltermcap # If Using 286 Xenix, uncomment the following lines: # CFLAGS = -M2l -O # LARGE = -LARGE # # Make SYSTEM be -DBSD for Berkeley-ish machines, leave blank otherwise # SYSTEM = #SYSTEM = -DBSD OBJECTS = a2.o table.o jump.o curses.o main.o screen.o \ mem.o tbl.o parse.o commands.o dos.o cli.o vidout.o \ disk.o prodos.o a2: $(OBJECTS) cc -o a2 $(CFLAGS) $(OBJECTS) $(LIBS) curses.o: curses.c cc -c $(SYSTEM) $(CFLAGS) curses.c shar: -mv ../a2.shar ../a2.shar- shar -v Makefile Notes Snarf Cli.doc Todo *.[ch] > ../a2.shar tar: -mv ../a2.tar ../a2.tar- tar -cvf ../a2.tar [A-Z]* *.[ch] clean: rm -f *.o core clobber: clean rm -f a2 a2.o: a2.h a2.c cc $(LARGE) $(CFLAGS) -c a2.c cli.o: cli.c a2.h cli.h commands.o: commands.c cli.h a2.h disk.o: disk.c a2.h dos.o: dos.c a2.h jump.o: jump.c a2.h main.o: main.c a2.h mem.o: mem.c a2.h parse.o: parse.c cli.h a2.h pascal.o: pascal.c a2.h screen.o: screen.c a2.h table.o: table.c a2.h tbl.o: tbl.c cli.h a2.h vidout.o: vidout.c a2.h @EOF chmod 644 Makefile echo x - Notes cat >Notes <<'@EOF' The default escape character is ~ Speed is everything in the emulator. I use macros instead of functions; autoincrement wherever I can; suitably sized variables to take advantage of wraparound so I don't have to & 0xFF or & 0xFFFF. These things are done for speed, not safety, although they should work on most machines. Hopefully the safety_check() routine will assert fail if at least some of these assumptions are not true. The main loop of the cpu is handled by a big switch statement. There are no function calls since they incur a large overhead. I count on your compiler generating an efficient jump table for the switch. The Apple's memory is represented by a 64K array of bytes (unsigned char). All memory references, with the exception of special locataions in the C000-CFFF range, come unintercepted from the mem array. References in the C000-CFFF range are taken from the mem_map() function. It implements things such as the keyboard read & strobe locations and the bank switching toggles. If mem_map() doesn't have a special hook for a Cxxx location, it will return the mem[] value associated with the location. Thus you can load device PROM images there and you will be able to reference them. Memory stores may be caught on a per-page basis. For example, stores to the C0xx range are caught by store_c0(). Besides the Cxxx functions, store catching routines are used for the memory mapped video from $400-7FF also for the language card. Certain PC values are intercepted and dealt with by C routines. The 64K array jmp_tbl[] determines whether a location is "caught"; if zero, nothing will happen. If non-zero, the value is passed to jump(). Pc's are only intercepted on JMP and JSR instructions (so that we don't pay a speed penalty for every instruction fetch). This imposes obvious limitations on what code can be caught. Intercepted routines include the ROM routines to handle scrolling, the ROM wait and bell calls, and some Cxxx calls. Scrolling must be caught and done separately or it looks horrible. RWTS and Prodos are also caught at the block read/write level to speed up disk access. Note that none of the jump intercepts are necessary. They speed up scrolling and disk access (for Prodos & DOS 3.3) quite a bit, but you can turn them all off and the emulator will still run. The memory map support is enough. The Pc catches ignore whether the language card is active or not; instead, they look for key values to see if they routines they're supposed to sit on top of really are there. If not, they let the real code execute. If we just caught ROM calls, and didn't catch calls that executed when the RAM card was active, then copying the ROM into the language card and switching to it would cause us to lose all of our interception. Beware when adding intercepts for the ROM or other functions: there's always some piece of code that depends on a side effect of a function. The blocks in the disk image are ordered in DOS 3.3 layout. The prodos code uses a mapping table. Thus, one Prodos block consists of two non- contiguous 256 byte blocks in a disk image. I only have the old Disk II boot prom from $C600. Jump.c patches it to look something like a Prodos device controller, so that Prodos has high- level disk intercept support. I don't know if a prom image from a newer disk controller will work or not. The emulator supports raw access to the disk controller by nibble encoding sector images as they're fed in through the shift data register. A routine decodes the motion of the disk head based on references to the stepper magnets. @EOF chmod 644 Notes echo x - Snarf cat >Snarf <<'@EOF' Snarfing the ROM images ----------------------- In order to run the Apple II emulator you will need several ROM images. Since they are copyrighted they cannot be distributed with this program. File to put image in Location in Apple AUTOSTART.ROM F800-FFFF APPLESOFT.ROM D000-EFFF DISK.PROM C600-C6FF (slot 6 Disk II cont.) MONITOR.ROM F800-FFFF (old Apples) INTEGER.ROM D000-EFFF (old Apples) How I snarfed the ROMs: My Apple has an el-cheapo serial card; neither Kermit nor any of my communications programs would talk to it. If I'd had any sort of working communications software when I set out to snarf my ROMs I probably wouldn't have done it this way; as it turns out, however, I've been very happy with this method. I have my Apple connected to my Unix box through the serial card. I can type IN#2 on my apple and cu into it from the Unix machine (typing PR#2 once I'm connected). Once I'm cu'd in, it's easy to use the ROM monitor to dump stuff. It looks something like this: $ cu -l /dev/tty06 -s 9600 -e | tee log Connected ]CALL -151 *F800.FFFF F800- 00 00 00 ... ... * Then when I escape back to the Unix side I have a log of the session in "log". I use this sed command to strip off the junk and only leave the hex data: sed -n -e 's/^M$//' -e '/^[0-9A-F]...-/p' In other words, put the above in a file, say 'fix', chmod +w fix, and say: fix < log > foo Then I run the hex dump through a simple C program to turn it back into binary (the program is called hex.c, and should be in the directory with the emulator source): hex < foo > AUTOSTART.ROM I snarf disk images in much the same way. Here's a little DOS 3.3 routine to dump an entire disk in hex: 0300- A9 00 LDA #$00 ; set up IOB 0302- 8D F0 B7 STA $B7F0 ; BUFFL 0305- 8D EB B7 STA $B7EB ; VOLUME 0308- 8D EC B7 STA $B7EC ; TRACK 030B- 8D ED B7 STA $B7ED ; SECTOR 030E- A9 10 LDA #$10 0310- 8D F1 B7 STA $B7F1 ; BUFFH 0313- A9 B7 LDA #$B7 ; IOBH 0315- A0 E8 LDY #$E8 ; IOBL 0317- 20 B5 B7 JSR $B7B5 ; call RWTS 031A- EE F1 B7 INC $B7F1 ; next page of memory 031D- EE ED B7 INC $B7ED ; next sector 0320- AD ED B7 LDA $B7ED 0323- C9 10 CMP #$10 ; end of track? 0325- 90 EC BCC $0313 0327- 20 3A 03 JSR $033A ; dump 1000-1FFF in hex 032A- A9 00 LDA #$00 ; Start at sector 0 again 032C- 8D ED B7 STA $B7ED 032F- EE EC B7 INC $B7EC ; next track 0332- AD EC B7 LDA $B7EC 0335- C9 23 CMP #$23 ; last track on disk? 0337- 90 D5 BCC $030E 0339- 60 RTS 033A- A9 00 LDA #$00 033C- 85 3C STA $3C 033E- A9 FF LDA #$FF ; range 1000-1FFF 0340- 85 3E STA $3E 0342- A9 10 LDA #$10 0344- 85 3D STA $3D 0346- A9 1F LDA #$1F 0348- 85 3F STA $3F 034A- 20 B3 FD JSR $FDB3 ; dump hex region 034D- 60 RTS Run fix and hex on the log, and you can put together binary disk images for the emulator. I've converted about 45 of my disks this way. Since the emulator expects disk images to be in DOS 3.3 sector ordering, if you use another tool to snarf disk you may need to convert them. The program mapper.c converts from Prodos sector ordered disks into DOS 3.3 sector ordered images: mapper < foo > bar @EOF chmod 644 Snarf echo x - Cli.doc cat >Cli.doc <<'@EOF' CLI Basics ---------- The ~ key (default value) will break the emulation and put the User into the Command Language Interpreter (CLI). The CLI prompt is >>> . The user may type ? to see a list of available commands in the CLI. The CLI allows partial completion of commands. The partial command name must be unambiguous. For instance, "e" or "exam" may be substituted for "examine". General Commands ---------------- cd [path] - With no paramters, displays current working directory (CWD). Otherwise attempts to set the CWD to path. continue or c - Resume the emulation and refresh the screen. escape [char] - Make the character which breaks the emulator into char or display current escape char. dup l|p pop l|p push l|p|addr - The CLI maintains a value stack for the convenience of the user. The emulator doesnt use the stack at all. Values may be pushed on the stack with push and retrieved later with pop or dup. The l and p parameters are used to retrive/store values into the lpoint (list point) and pc. help - Gives a quick list of commands map - Toggle lower to uppercase mapping during emulation. The default is to perform mapping. quit - Exit the Emulator . - Display the current point ![command] - Unix Shell escape. Without command, the shell is called. Otherwise, command is executed. File Commands ------------- bload file addr - Bload will Allow the loading of binary images into the memory of the emulator. bload binfile F000 will load file "binfile" at address F000. bsave file addr1 size - Bsave will save a section of memory to a file. For instance, bsave mybin 300 FF will save all of the memory from 300 to 3FF to file "mybin". disk - Display the names of the disks which are currently inserted in the virtual disk drives. insert file [drive#] - Insert will make a file the disk in a particular drive. For Instance: insert mydisk will make the file "mydisk" appear to the emulator as the disk in drive 1. Register Manipulation --------------------- cl[cdinvz] - Clears Status register bit. For instance, clc will clear the carry bit. clz will clear the zero bit. se[cdinvz] - Set Status register bit. For instance, sec will set the carry flag. sez will set the zero bit. ld[axys] value - Load a value into a register. lda ff will load ff into the accumulator. lds 02 will load 02 into the Stack Pointer. jmp addr - Sets PC = addr. A continue will resume at that location. reset or ~ - Sets PC = addr in the Apple II reset Vector. Combined with continue, will emulate the pressing of the reset button on the Apple II. Memory Manipulation/Examination ------------------------------- examine addr [addr] - Display current memory location if no paramters are givin. If one parameter is givin, the contents of that location are displayed. If two parameters are givin, all memory between the two locations is displayed. examine 300 400 will display all memory between $300 and $400. e 300 will display the 8 bit contents of $300 deposit addr value [value]+ - Put values into memory. Addr must be specified as well as one or more values. Deposit 300 20 will put 20 into memory location 300. Deposit 300 10 20 30 40 will load 10,20,30,40 into consequtive addresses beginning at 300. list [low] [high] - Dissassembly. If noparameters are supplied, dissassembly begins at the current point and continues for 1 page. If low is specified, dissassembly begins at low and continues for 1 page. If both low and high are specified the region between low and high is dissassembled. Debugging Commands ------------------ breakpoint [addr] - Set breakpoint. If addr is not specified, the current breakpoint is displayed. Otherwise, the breakpoint is set to addr. When PC == addr, the emulator breaks into the command mode. break 3FFF sets the breakpoint to 3FFF. nobreak - Turn of breakpoint. trace [low high] [file] - Enable tracing. With no parameters, the current trace status is displayed. When low and high are givin, tracing for that region is enabled (i.e. when low <= pc <= high ). An output consisting of dissassembly and status registers is written to the trace file. If file is specified, output is sent to that file, otherwise the file "trace" is used. For instance, trace 3F0 400 will trace the region of 3F0-400 and send its output to trace. notrace - Disable tracing. phantom location high low [file] - Enable phantom tracing of range low,high. Phantom tracing allows a trace to be performed AFTER location is reached. For instance, phantom 300 F300 F400 will enable tracing of f300-f400 only AFTER PC == 300. Once the PC has reached 300, tracing between F300-F400 is enabled. If file is specified trace output will go to the specified file. Otherwise, "trace" is assumed. nophantom - disable phantom trace. @EOF chmod 600 Cli.doc echo x - a2.c cat >a2.c <<'@EOF' /* * a2, an Apple II emulator in C * (c) Copyright 1990 by Rich Skrenta * * Command line interface written by Tom Markson * * Distribution agreement: * * You may freely copy or redistribute this software, so long * as there is no profit made from its use, sale, trade or * reproduction. You may not change this copyright notice, * and it must be included prominently in any copy made. * * Send emulator related mail to: skrenta@blekko.commodore.com * skrenta@blekko.uucp */ #include #include "a2.h" #define BRANCH() val = mem[Pc++]; if (val & 0x80) Pc += val - 256; else Pc += val; #define put_status(s) stmp = s; N = stmp & 0x80; V = stmp & 0x40; B = stmp & 0x10; D = stmp & 0x08; I = stmp & 0x04; NZ = !(stmp & 0x02); C = stmp & 0x01; extern long trace_lo, trace_hi; extern FILE *logging_fp; extern long phantom_location; extern long breakpoint; run() { register unsigned short ptr; register unsigned short ptmp; register unsigned int val; register unsigned int tmp; register unsigned int num; unsigned char stmp; int foo; int show_flags = FALSE; do { /* while (tracing) */ if (tracing) { if (breakpoint == Pc) { MoveCursor(term_lines, 0); printf("Break at $%.4X\n", Pc); return; } if (phantom_location == (long) Pc) phantom_location = -1; /* start tracing now */ if (phantom_location == -1 && trace_lo != -1 && (long) Pc >= trace_lo && (long) Pc <= trace_hi) { diss(Pc, logging_fp); show_flags = TRUE; } else show_flags = FALSE; } do { /* while (running) */ switch (mem[Pc++]) { case 0x00: /* BRK */ PCINC; push(high(Pc)); push(low(Pc)); B = 1; /* set I flag? */ push(get_status()); Pc = join(mem[0xFFFE], mem[0xFFFF]); break; case 0x01: /* ORA (indirect, X) */ INDX; A |= REFptr; SIGN(A); ZERO(A); break; case 0x02: break; case 0x03: break; case 0x04: /* TSB zero page 65C02 */ break; case 0x05: /* ORA zero page */ A |= rZEROP; SIGN(A); ZERO(A); break; case 0x06: /* ASL zero page */ ZEROP; val = REFzero << 1; CARRY(val); val &= 0xFF; SETzero(val); SIGN(val); ZERO(val); break; case 0x07: break; case 0x08: /* PHP */ push(get_status()); break; case 0x09: /* ORA immediate */ A |= REFimm; SIGN(A); ZERO(A); break; case 0x0A: /* ASL accumulator */ A = A << 1; CARRY(A); A &= 0xFF; SIGN(A); ZERO(A); break; case 0x0B: break; case 0x0C: /* TSB absolute 65C02 */ break; case 0x0D: /* ORA absolute */ ABSOL; A |= REFptr; SIGN(A); ZERO(A); break; case 0x0E: /* ASL absolute */ ABSOL; val = REFptr << 1; CARRY(val); val &= 0xFF; SETptr(val); SIGN(val); ZERO(val); break; case 0x0F: break; case 0x10: /* BPL */ if (N) { PCINC; } else { BRANCH(); } break; case 0x11: /* ORA (indirect), Y */ INDY; A |= REFptr; SIGN(A); ZERO(A); break; case 0x12: /* ORA (indz) 65C02 */ break; case 0x13: break; case 0x14: /* TRB zero page 65C02 */ break; case 0x15: /* ORA zero page, X */ A |= rZEROX; SIGN(A); ZERO(A); break; case 0x16: /* ASL zero page, X */ ZEROX; val = REFzero << 1; CARRY(val); val &= 0xFF; SETzero(val); SIGN(val); ZERO(val); break; case 0x17: break; case 0x18: /* CLC */ C = 0; break; case 0x19: /* ORA absolute, Y */ ABSY; A |= REFptr; SIGN(A); ZERO(A); break; case 0x1A: /* INA 65C02 */ break; case 0x1C: /* TRB absolute 65C02 */ break; case 0x1D: /* ORA absolute, X */ ABSX; A |= REFptr; SIGN(A); ZERO(A); break; case 0x1E: /* ASL absolute, X */ ABSX; val = REFptr << 1; CARRY(val); val &= 0xFF; SETptr(val); SIGN(val); ZERO(val); break; case 0x1F: break; case 0x20: /* JSR */ ptmp = REFimm; ptmp |= mem[Pc] << 8; push(high(Pc)); push(low(Pc)); Pc = ptmp; if (jmp_tbl[Pc]) jump(jmp_tbl[Pc]); break; case 0x21: /* AND (indirect, X) */ INDX; A &= REFptr; SIGN(A); ZERO(A); break; case 0x22: break; case 0x23: break; case 0x24: /* BIT zero page */ tmp = rZEROP; ZERO(A & tmp); N = tmp & 0x80; V = tmp & 0x40; break; case 0x25: /* AND zero page */ A &= rZEROP; SIGN(A); ZERO(A); break; case 0x26: /* ROL zero page */ ZEROP; val = REFzero; tmp = C; val = val << 1; CARRY(val); val = (val & 0xFF) | tmp; SETzero(val); SIGN(val); ZERO(val); break; case 0x27: break; case 0x28: /* PLP */ put_status(pop()); break; case 0x29: /* AND immediate */ A &= REFimm; SIGN(A); ZERO(A); break; case 0x2A: /* ROL accumulator */ tmp = C; A = A << 1; CARRY(A); A = (A & 0xFF) | tmp; SIGN(A); ZERO(A); break; case 0x2B: break; case 0x2C: /* BIT absolute */ ABSOL; tmp = REFptr; ZERO(A & tmp); N = tmp & 0x80; V = tmp & 0x40; break; case 0x2D: /* AND absolute */ ABSOL; A &= REFptr; SIGN(A); ZERO(A); break; case 0x2E: /* ROL absolute */ ABSOL; val = REFptr; tmp = C; val = val << 1; CARRY(val); val = (val & 0xFF) | tmp; SETptr(val); SIGN(val); ZERO(val); break; case 0x2F: break; case 0x30: /* BMI */ if (N) { BRANCH(); } else{ PCINC; } break; case 0x31: /* AND (indirect), Y */ INDY; A &= REFptr; SIGN(A); ZERO(A); break; case 0x32: /* AND (indz) 65C02 */ break; case 0x33: break; case 0x34: /* BIT zero page, X 65C02 */ break; case 0x35: /* AND zero page, X */ A &= rZEROX; SIGN(A); ZERO(A); break; case 0x36: /* ROL zero page, X */ ZEROX; val = REFzero; tmp = C; val = val << 1; CARRY(val); val = (val & 0xFF) | tmp; SETzero(val); SIGN(val); ZERO(val); break; case 0x37: break; case 0x38: /* SEC */ C = 1; break; case 0x39: /* AND absolute, Y */ ABSY; A &= REFptr; SIGN(A); ZERO(A); break; case 0x3A: /* DEA 65C02 */ break; case 0x3B: break; case 0x3C: /* BIT absolute, X 65C02 */ break; case 0x3D: /* AND absolute, X */ ABSX; A &= REFptr; SIGN(A); ZERO(A); break; case 0x3E: /* ROL absolute, X */ ABSX; val = REFptr; tmp = C; val = val << 1; CARRY(val); val = (val & 0xFF) | tmp; SETptr(val); SIGN(val); ZERO(val); break; case 0x3F: break; case 0x40: /* RTI */ put_status(pop()); Pc = pop(); Pc |= pop() << 8; break; case 0x41: /* EOR (indirect, X) */ INDX; A ^= REFptr; SIGN(A); ZERO(A); break; case 0x42: break; case 0x43: break; case 0x44: break; case 0x45: /* EOR zero page */ A ^= rZEROP; SIGN(A); ZERO(A); break; case 0x46: /* LSR zero page */ ZEROP; tmp = REFzero; C = (tmp & 0x01); tmp = tmp >> 1; SETzero(tmp); N = 0; ZERO(tmp); break; case 0x47: break; case 0x48: /* PHA */ push(A); break; case 0x49: /* EOR immediate */ A ^= REFimm; SIGN(A); ZERO(A); break; case 0x4A: /* LSR accumulator */ C = (A & 0x01); A = A >> 1; N = 0; ZERO(A); break; case 0x4B: break; case 0x4C: /* JMP absolute */ ptmp = REFimm; ptmp |= REFimm << 8; Pc = ptmp; if (jmp_tbl[Pc]) jump(jmp_tbl[Pc]); break; case 0x4D: /* EOR absolute */ ABSOL; A ^= REFptr; SIGN(A); ZERO(A); break; case 0x4E: /* LSR absolute */ ABSOL; tmp = REFptr; C = (tmp & 0x01); tmp = tmp >> 1; SETptr(tmp); N = 0; ZERO(tmp); break; case 0x4F: break; case 0x50: /* BVC */ if (V) { PCINC; } else { BRANCH(); } break; case 0x51: /* EOR (indirect), Y */ INDY; A ^= REFptr; SIGN(A); ZERO(A); break; case 0x52: /* EOR (indz) 65C02 */ break; case 0x53: break; case 0x54: break; case 0x55: /* EOR zero page, X */ A ^= rZEROX; SIGN(A); ZERO(A); break; case 0x56: /* LSR zero page, X */ ZEROX; tmp = REFzero; C = (tmp & 0x01); tmp = tmp >> 1; SETzero(tmp); N = 0; ZERO(tmp); break; case 0x57: break; case 0x58: /* CLI */ I = 0; break; case 0x59: /* EOR absolute, Y */ ABSY; A ^= REFptr; SIGN(A); ZERO(A); break; case 0x5A: /* PHY 65C02 */ break; case 0x5B: break; case 0x5C: break; case 0x5D: /* EOR absolute, X */ ABSX; A ^= REFptr; SIGN(A); ZERO(A); break; case 0x5E: /* LSR absolute, X */ ABSX; tmp = REFptr; C = (tmp & 0x01); tmp = tmp >> 1; SETptr(tmp); N = 0; ZERO(tmp); break; case 0x5F: break; case 0x60: /* RTS */ DO_RTS; break; case 0x61: /* ADC (indirect, X) */ INDX; val = REFptr; tmp = A & 0x80; stmp = val & 0x80; if (D) { num = tobinary(val); val = tobinary(A); val += num + C; dCARRY(val); while (val >= 100) val -= 100; A = tobcd(val); } else { A += val + C; CARRY(A); A &= 0xFF; } ZERO(A); SIGN(A); V = (tmp == stmp) && (tmp != N); break; case 0x62: break; case 0x63: break; case 0x64: /* STZ zero page 65C02 */ break; case 0x65: /* ADC zero page */ val = rZEROP; tmp = A & 0x80; stmp = val & 0x80; if (D) { num = tobinary(val); val = tobinary(A); val += num + C; dCARRY(val); while (val >= 100) val -= 100; A = tobcd(val); } else { A += val + C; CARRY(A); A &= 0xFF; } ZERO(A); SIGN(A); V = (tmp == stmp) && (tmp != N); break; case 0x66: /* ROR zero page */ ZEROP; val = REFzero; tmp = C; C = val & 0x01; val = val >> 1; val |= tmp << 7; SETzero(val); SIGN(val); ZERO(val); break; case 0x67: break; case 0x68: /* PLA */ A = pop(); SIGN(A); ZERO(A); break; case 0x69: /* ADC immediate */ val = REFimm; tmp = A & 0x80; stmp = val & 0x80; if (D) { num = tobinary(val); val = tobinary(A); val += num + C; dCARRY(val); while (val >= 100) val -= 100; A = tobcd(val); } else { A += val + C; CARRY(A); A &= 0xFF; } ZERO(A); SIGN(A); V = (tmp == stmp) && (tmp != N); break; case 0x6A: /* ROR accumulator */ tmp = C; C = A & 0x01; A = A >> 1; A |= tmp << 7; SIGN(A); ZERO(A); break; case 0x6B: break; case 0x6C: /* JMP indirect */ ptmp = REFimm; ptmp |= REFimm << 8; Pc = mem[ptmp++]; Pc |= mem[ptmp] << 8; if (jmp_tbl[Pc]) jump(jmp_tbl[Pc]); break; case 0x6D: /* ADC absolute */ ABSOL; val = REFptr; tmp = A & 0x80; stmp = val & 0x80; if (D) { num = tobinary(val); val = tobinary(A); val += num + C; dCARRY(val); while (val >= 100) val -= 100; A = tobcd(val); } else { A += val + C; CARRY(A); A &= 0xFF; } ZERO(A); SIGN(A); V = (tmp == stmp) && (tmp != N); break; case 0x6E: /* ROR absolute */ ABSOL; val = REFptr; tmp = C; C = val & 0x01; val = val >> 1; val |= tmp << 7; SETptr(val); SIGN(val); ZERO(val); break; case 0x6F: break; case 0x70: /* BVS */ if (V) { BRANCH(); } else { PCINC; } break; case 0x71: /* ADC (indirect),Y */ INDY; val = REFptr; tmp = A & 0x80; stmp = val & 0x80; if (D) { num = tobinary(val); val = tobinary(A); val += num + C; dCARRY(val); while (val >= 100) val -= 100; A = tobcd(val); } else { A += val + C; CARRY(A); A &= 0xFF; } ZERO(A); SIGN(A); V = (tmp == stmp) && (tmp != N); break; case 0x72: /* ADC (indz) 65C02 */ break; case 0x73: break; case 0x74: /* STZ zero page, X 65C02 */ break; case 0x75: /* ADC zero page, X */ val = rZEROX; tmp = A & 0x80; stmp = val & 0x80; if (D) { num = tobinary(val); val = tobinary(A); val += num + C; dCARRY(val); while (val >= 100) val -= 100; A = tobcd(val); } else { A += val + C; CARRY(A); A &= 0xFF; } ZERO(A); SIGN(A); V = (tmp == stmp) && (tmp != N); break; case 0x76: /* ROR zero page, X */ ZEROX; val = REFzero; tmp = C; C = val & 0x01; val = val >> 1; val |= tmp << 7; SETzero(val); SIGN(val); ZERO(val); break; case 0x77: break; case 0x78: /* SEI */ I = 1; break; case 0x79: /* ADC absolute, Y */ ABSY; val = REFptr; tmp = A & 0x80; stmp = val & 0x80; if (D) { num = tobinary(val); val = tobinary(A); val += num + C; dCARRY(val); while (val >= 100) val -= 100; A = tobcd(val); } else { A += val + C; CARRY(A); A &= 0xFF; } ZERO(A); SIGN(A); V = (tmp == stmp) && (tmp != N); break; case 0x7A: /* PLY 65C02 */ break; case 0x7B: break; case 0x7C: /* JMP (abs indirect, X) 65C02 */ break; case 0x7D: /* ADC absolute, X */ ABSX; val = REFptr; tmp = A & 0x80; stmp = val & 0x80; if (D) { num = tobinary(val); val = tobinary(A); val += num + C; dCARRY(val); while (val >= 100) val -= 100; A = tobcd(val); } else { A += val + C; CARRY(A); A &= 0xFF; } ZERO(A); SIGN(A); V = (tmp == stmp) && (tmp != N); break; case 0x7E: /* ROR absolute, X */ ABSX; val = REFptr; tmp = C; C = val & 0x01; val = val >> 1; val |= tmp << 7; SETptr(val); SIGN(val); ZERO(val); break; case 0x7F: break; case 0x80: /* BRA 65C02 */ break; case 0x81: /* STA (indirect, X) */ INDX; SETptr(A); break; case 0x82: break; case 0x83: break; case 0x84: /* STY zero page */ sZEROP(Y); break; case 0x85: /* STA zero page */ sZEROP(A); break; case 0x86: /* STX zero page */ sZEROP(X); break; case 0x87: break; case 0x88: /* DEY */ Y--; SIGN(Y); ZERO(Y); break; case 0x89: break; case 0x8A: /* TXA */ A = X; SIGN(A); ZERO(A); break; case 0x8B: break; case 0x8C: /* STY absolute */ ABSOL; SETptr(Y); break; case 0x8D: /* STA absolute */ ABSOL; SETptr(A); break; case 0x8E: /* STX absolute */ ABSOL; SETptr(X); break; case 0x8F: break; case 0x90: /* BCC */ if (C) { PCINC; } else { BRANCH(); } break; case 0x91: /* STA (indirect), Y */ INDY; SETptr(A); break; case 0x92: break; case 0x93: break; case 0x94: /* STY zero page, X */ sZEROX(Y); break; case 0x95: /* STA zero page, X */ sZEROX(A); break; case 0x96: /* STX zero page, Y */ sZEROY(X); break; case 0x97: break; case 0x98: /* TYA */ A = Y; SIGN(A); ZERO(A); break; case 0x99: /* STA absolute, Y */ ABSY; SETptr(A); break; case 0x9A: /* TXS */ Sp = X; break; case 0x9B: break; case 0x9C: /* STZ absolute 65C02 */ break; case 0x9D: /* STA absolute, X */ ABSX; SETptr(A); break; case 0x9E: /* STZ absolute, X 65C02 */ break; case 0x9F: break; case 0xA0: /* LDY immediate */ Y = REFimm; SIGN(Y); ZERO(Y); break; case 0xA1: /* LDA (indirect, X) */ INDX; A = REFptr; SIGN(A); ZERO(A); break; case 0xA2: /* LDX immediate */ X = REFimm; SIGN(X); ZERO(X); break; case 0xA3: break; case 0xA4: /* LDY zero page */ Y = rZEROP; SIGN(Y); ZERO(Y); break; case 0xA5: /* LDA zero page */ A = rZEROP; SIGN(A); ZERO(A); break; case 0xA6: /* LDX zero page */ X = rZEROP; SIGN(X); ZERO(X); break; case 0xA7: break; case 0xA8: /* TAY */ Y = A; SIGN(Y); ZERO(Y); break; case 0xA9: /* LDA immediate */ A = REFimm; SIGN(A); ZERO(A); break; case 0xAA: /* TAX */ X = A; SIGN(X); ZERO(X); break; case 0xAB: break; case 0xAC: /* LDY absolute */ ABSOL; Y = REFptr; SIGN(Y); ZERO(Y); break; case 0xAD: /* LDA absolute */ ABSOL; A = REFptr; SIGN(A); ZERO(A); break; case 0xAE: /* LDX absolute */ ABSOL; X = REFptr; SIGN(X); ZERO(X); break; case 0xAF: break; case 0xB0: /* BCS */ if (C) { BRANCH(); } else { PCINC; } break; case 0xB1: /* LDA (indirect), Y */ INDY; A = REFptr; SIGN(A); ZERO(A); break; case 0xB2: /* LDA (indz) 65C02 */ break; case 0xB3: break; case 0xB4: /* LDY zero page, X */ Y = rZEROX; SIGN(Y); ZERO(Y); break; case 0xB5: /* LDA zero page, X */ A = rZEROX; SIGN(A); ZERO(A); break; case 0xB6: /* LDX zero page, Y */ X = rZEROY; SIGN(X); ZERO(X); break; case 0xB7: break; case 0xB8: /* CLV */ V = 0; break; case 0xB9: /* LDA absolute, Y */ ABSY; A = REFptr; SIGN(A); ZERO(A); break; case 0xBA: /* TSX */ X = Sp; SIGN(X); ZERO(X); break; case 0xBB: break; case 0xBC: /* LDY absolute, X */ ABSX; Y = REFptr; SIGN(Y); ZERO(Y); break; case 0xBD: /* LDA absolute, X */ ABSX; A = REFptr; SIGN(A); ZERO(A); break; case 0xBE: /* LDX absolute, Y */ ABSY; X = REFptr; SIGN(X); ZERO(X); break; case 0xBF: break; case 0xC0: /* CPY immediate */ tmp = REFimm; C = (Y >= tmp); tmp = (Y - tmp) & 0xFF; SIGN(tmp); ZERO(tmp); break; case 0xC1: /* CMP (indirect, X) */ INDX; tmp = REFptr; C = (A >= tmp); tmp = (A - tmp) & 0xFF; SIGN(tmp); ZERO(tmp); break; case 0xC2: break; case 0xC3: break; case 0xC4: /* CPY zero page */ tmp = rZEROP; C = (Y >= tmp); tmp = (Y - tmp) & 0xFF; SIGN(tmp); ZERO(tmp); break; case 0xC5: /* CMP zero page */ tmp = rZEROP; C = (A >= tmp); tmp = (A - tmp) & 0xFF; SIGN(tmp); ZERO(tmp); break; case 0xC6: /* DEC zero page */ ZEROP; stmp = REFzero - 1; SETzero(stmp); SIGN(stmp); ZERO(stmp); break; case 0xC7: break; case 0xC8: /* INY */ Y++; SIGN(Y); ZERO(Y); break; case 0xC9: /* CMP immediate */ tmp = REFimm; C = (A >= tmp); tmp = (A - tmp) & 0xFF; SIGN(tmp); ZERO(tmp); break; case 0xCA: /* DEX */ X--; SIGN(X); ZERO(X); break; case 0xCB: break; case 0xCC: /* CPY absolute */ ABSOL; tmp = REFptr; C = (Y >= tmp); tmp = (Y - tmp) & 0xFF; SIGN(tmp); ZERO(tmp); break; case 0xCD: /* CMP absolute */ ABSOL; tmp = REFptr; C = (A >= tmp); tmp = (A - tmp) & 0xFF; SIGN(tmp); ZERO(tmp); break; case 0xCE: /* DEC absolute */ ABSOL; stmp = REFptr - 1; SETptr(stmp); SIGN(stmp); ZERO(stmp); break; case 0xCF: break; case 0xD0: /* BNE */ if (NZ) { BRANCH(); } else { PCINC; } break; case 0xD1: /* CMP (indirect), Y */ INDY; tmp = REFptr; C = (A >= tmp); tmp = (A - tmp) & 0xFF; SIGN(tmp); ZERO(tmp); break; case 0xD2: /* CMP (indz) 65C02 */ break; case 0xD3: break; case 0xD4: break; case 0xD5: /* CMP zero page, X */ tmp = rZEROX; C = (A >= tmp); tmp = (A - tmp) & 0xFF; SIGN(tmp); ZERO(tmp); break; case 0xD6: /* DEC zero page, X */ ZEROX; stmp = REFzero - 1; SETzero(stmp); SIGN(stmp); ZERO(stmp); break; case 0xD7: break; case 0xD8: /* CLD */ D = 0; break; case 0xD9: /* CMP absolute, Y */ ABSY; tmp = REFptr; C = (A >= tmp); tmp = (A - tmp) & 0xFF; SIGN(tmp); ZERO(tmp); break; case 0xDA: /* PHX 65C02 */ break; case 0xDB: break; case 0xDC: break; case 0xDD: /* CMP absolute, X */ ABSX; tmp = REFptr; C = (A >= tmp); tmp = (A - tmp) & 0xFF; SIGN(tmp); ZERO(tmp); break; case 0xDE: /* DEC absolute, X */ ABSX; stmp = REFptr - 1; SETptr(stmp); SIGN(stmp); ZERO(stmp); break; case 0xDF: break; case 0xE0: /* CPX immediate */ tmp = REFimm; C = (X >= tmp); tmp = (X - tmp) & 0xFF; SIGN(tmp); ZERO(tmp); break; case 0xE1: /* SBC (indirect, X) */ INDX; val = REFptr; tmp = A & 0x80; stmp = val & 0x80; if (D) { assert(FALSE); } else { foo = A - (val + !C); C = (foo >= 0); A = foo & 0xFF; } ZERO(A); SIGN(A); V = (tmp != stmp) && (tmp != N); break; case 0xE2: break; case 0xE3: break; case 0xE4: /* CPX zero page */ tmp = rZEROP; C = (X >= tmp); tmp = (X - tmp) & 0xFF; SIGN(tmp); ZERO(tmp); break; case 0xE5: /* SBC zero page */ val = rZEROP; tmp = A & 0x80; stmp = val & 0x80; if (D) { assert(FALSE); } else { foo = A - (val + !C); C = (foo >= 0); A = foo & 0xFF; } ZERO(A); SIGN(A); V = (tmp != stmp) && (tmp != N); break; case 0xE6: /* INC zero page */ ZEROP; stmp = REFzero + 1; SIGN(stmp); ZERO(stmp); SETzero(stmp); break; case 0xE7: break; case 0xE8: /* INX */ X++; SIGN(X); ZERO(X); break; case 0xE9: /* SBC immediate */ val = REFimm; tmp = A & 0x80; stmp = val & 0x80; if (D) { assert(FALSE); } else { foo = A - (val + !C); C = (foo >= 0); A = foo & 0xFF; } ZERO(A); SIGN(A); V = (tmp != stmp) && (tmp != N); break; case 0xEA: /* NOP */ break; case 0xEB: break; case 0xEC: /* CPX absolute */ ABSOL; tmp = REFptr; C = (X >= tmp); tmp = (X - tmp) & 0xFF; SIGN(tmp); ZERO(tmp); break; case 0xED: /* SBC absolute */ ABSOL; val = REFptr; tmp = A & 0x80; stmp = val & 0x80; if (D) { assert(FALSE); } else { foo = A - (val + !C); C = (foo >= 0); A = foo & 0xFF; } ZERO(A); SIGN(A); V = (tmp != stmp) && (tmp != N); break; case 0xEE: /* INC absolute */ ABSOL; stmp = REFptr + 1; SETptr(stmp); SIGN(stmp); ZERO(stmp); break; case 0xEF: break; case 0xF0: /* BEQ */ if (NZ) { PCINC; } else { BRANCH(); } break; case 0xF1: /* SBC (indirect), Y */ INDY; val = REFptr; tmp = A & 0x80; stmp = val & 0x80; if (D) { assert(FALSE); } else { foo = A - (val + !C); C = (foo >= 0); A = foo & 0xFF; } ZERO(A); SIGN(A); V = (tmp != stmp) && (tmp != N); break; case 0xF2: /* SBC (indz) 65C02 */ break; case 0xF3: break; case 0xF4: break; case 0xF5: /* SBC zero page, X */ val = rZEROX; tmp = A & 0x80; stmp = val & 0x80; if (D) { assert(FALSE); } else { foo = A - (val + !C); C = (foo >= 0); A = foo & 0xFF; } ZERO(A); SIGN(A); V = (tmp != stmp) && (tmp != N); break; case 0xF6: /* INC zero page, X */ ZEROX; stmp = REFzero + 1; SETzero(stmp); SIGN(stmp); ZERO(stmp); break; case 0xF7: break; case 0xF8: /* SED */ D = 1; break; case 0xF9: /* SBC absolute, Y */ ABSY; val = REFptr; tmp = A & 0x80; stmp = val & 0x80; if (D) { assert(FALSE); } else { foo = A - (val + !C); C = (foo >= 0); A = foo & 0xFF; } ZERO(A); SIGN(A); V = (tmp != stmp) && (tmp != N); break; case 0xFA: /* PLX 65C02 */ break; case 0xFB: break; case 0xFC: break; case 0xFD: /* SBC absolute, X */ ABSX; val = REFptr; tmp = A & 0x80; stmp = val & 0x80; if (D) { assert(FALSE); } else { foo = A - (val + !C); C = (foo >= 0); A = foo & 0xFF; } ZERO(A); SIGN(A); V = (tmp != stmp) && (tmp != N); break; case 0xFE: /* INC absolute, X */ ABSX; stmp = REFptr + 1; SETptr(stmp); SIGN(stmp); ZERO(stmp); break; case 0xFF: break; } } while (running); if (show_flags) flags(logging_fp); } while (tracing); } @EOF chmod 644 a2.c echo x - a2.h cat >a2.h <<'@EOF' /* * a2, an Apple II emulator in C * (c) Copyright 1990 by Rich Skrenta * * Command line interface written by Tom Markson * * Distribution agreement: * * You may freely copy or redistribute this software, so long * as there is no profit made from its use, sale, trade or * reproduction. You may not change this copyright notice, * and it must be included prominently in any copy made. * * Send emulator related mail to: skrenta@blekko.commodore.com * skrenta@blekko.uucp */ #define TRUE 1 #define FALSE 0 extern FILE *sfp; /* * Assertion verifier */ #define assert(p) if(! (p)) asfail(__FILE__, __LINE__, "p"); else #define join(low, high) ((low) | ((high) << 8)) #define low(x) ((x) & 0xFF) #define high(x) ((x) >> 8) #define tobinary(v) ((v >> 4) * 10 + (v & 0xF)) #define tobcd(v) ((v % 10) | ((v / 10) << 4)) #define push(s) mem[0x100 | Sp--] = s; #define pop() mem[0x100 | ++Sp] #define SIGN(x) N = ((x) & 0x80) #define ZERO(x) NZ = (x) #define CARRY(x) C = ((x) > 0xFF) #define dCARRY(x) C = ((x) > 99) #define REF(a) ((a & 0xF000) == 0xC000 ? mem_map(a) : mem[a]) #define REFptr ((ptr & 0xF000) == 0xC000 ? mem_map(ptr) : mem[ptr]) #define REFzero mem[ptr] #define REFimm mem[Pc++] #define SET(a,v) if (mem_set[a >> 8]) (*mem_set[a >> 8])(a,v); else mem[a] = v; #define SETptr(v) if (mem_set[ptr >> 8]) (*mem_set[ptr >> 8])(ptr,v); else mem[ptr] = v; #define SETzero(v) mem[ptr] = v; #define PCINC Pc++; #define ABSOL ptr = mem[Pc++]; ptr |= mem[Pc++] << 8; #define ABSX ptr = mem[Pc++]; ptr |= mem[Pc++] << 8; ptr += X; #define ABSY ptr = mem[Pc++]; ptr |= mem[Pc++] << 8; ptr += Y; #define ZEROP ptr = mem[Pc++]; #define ZEROX ptr = (mem[Pc++] + X) & 0xFF; #define ZEROY ptr = (mem[Pc++] + Y) & 0xFF; #define INDX stmp = mem[Pc++] + X; ptr = mem[stmp++]; ptr |= mem[stmp] << 8; #define INDY stmp = mem[Pc++]; ptr = mem[stmp++]; ptr |= mem[stmp] << 8; ptr += Y; #define rZEROP mem[ mem[Pc++] ] #define rZEROX mem[ (mem[Pc++] + X) & 0xFF ] #define rZEROY mem[ (mem[Pc++] + Y) & 0xFF ] #define sZEROP(v) mem[ mem[Pc++] ] = v; #define sZEROX(v) mem[ (mem[Pc++] + X) & 0xFF ] = v; #define sZEROY(v) mem[ (mem[Pc++] + Y) & 0xFF ] = v; #define ZIND ptr = mem[ mem[Pc++] ]; #define ABINDX stmp = mem[Pc++]; stmp |= mem[Pc++] << 8; ptr = mem[stmp++]; ptr |= mem[stmp] << 8; ptr += X; /* * Since we store flags as 0/nonzero instead of 0/1, we need to turn * nonzero back into 1. Thus, "!!" is not a no-op. */ #define get_status() ((!!N << 7)|(!!V << 6)|(!!B << 4)|(!!D << 3)|(!!I << 2)|(!NZ << 1)|(!!C)) extern unsigned char mem[]; /* 64K memory image */ extern unsigned char jmp_tbl[]; /* jmp & jsr interceptor functions */ extern int (*mem_set[])(); /* memory store interceptors */ extern unsigned char mem_map(); extern unsigned short Pc; extern unsigned char Sp; /* stack pointer */ extern unsigned int A; /* accumulator */ extern unsigned char X; /* X register */ extern unsigned char Y; /* Y register */ extern unsigned int N; /* 7 - sign */ extern unsigned int V; /* 6 - Overflow */ /* 5 - Unused */ extern unsigned int B; /* 4 - Break */ extern unsigned int D; /* 3 - Decimal */ extern unsigned int I; /* 2 - Interrupt */ extern unsigned int NZ; /* 1 - inverse of Zero */ extern unsigned int C; /* 0 - Carry */ /* * Note: * Flags are stored as zero/nonzero instead of 0/1, except for * N which is stored 0/0x80. NZ is "not Z"; use !NZ to get * the zero flag. * * The flags code is tight, especially around overflow set on ADC * and SBC. Integer basic is a good test to see if it still works. */ #define M_NONE 0 #define M_ZP 1 #define M_ZPX 2 #define M_ZPY 3 #define M_IND 4 #define M_INDX 5 #define M_INDY 6 #define M_ABS 7 #define M_ABSX 8 #define M_ABSY 9 #define M_IMM 10 #define M_REL 11 #define M_ZIND 12 /* zero page indirect, 65C02 */ #define M_ABINDX 13 /* for jmp $7C in 65C02 */ extern int ram_read; extern int ram_write; extern int bank2_enable; struct op_info { char *name; int add_mode; }; extern struct op_info opcode[]; extern int running; extern int tracing; extern char *my_malloc(); extern int term_lines, term_cols; extern int text1[]; /* base addresses for text page 1 */ extern int last_line; /* line the cursor is on now */ extern int last_col; /* current column of cursor */ extern int disk[2]; extern int write_prot[2]; extern int drive; #define DO_RTS Pc = pop(); Pc |= pop() << 8; Pc++; @EOF chmod 644 a2.h echo x - curses.c cat >curses.c <<'@EOF' /* * a2, an Apple II emulator in C * (c) Copyright 1990 by Rich Skrenta * * Command line interface written by Tom Markson * * Distribution agreement: * * You may freely copy or redistribute this software, so long * as there is no profit made from its use, sale, trade or * reproduction. You may not change this copyright notice, * and it must be included prominently in any copy made. * * Send emulator related mail to: skrenta@blekko.commodore.com * skrenta@blekko.uucp */ /* ** This a screen management library borrowed with permission from the ** Elm mail system (a great mailer--I highly recommend it!). ** ** I've hacked this library to only provide what I need. ** ** Original copyright follows: */ /******************************************************************************* * The Elm Mail System - $Revision: 2.1 $ $State: Exp $ * * Copyright (c) 1986 Dave Taylor ******************************************************************************/ #ifndef M_XENIX #include #endif #include #include #include #define DEFAULT_term_lines 24 #define DEFAULT_COLUMNS 80 #define TTYIN 0 #define VERY_LONG_STRING 2500 int term_lines = DEFAULT_term_lines - 1; int term_cols = DEFAULT_COLUMNS; extern char escape_char; #ifdef SHORTNAMES # define _cleartoeoln _clrtoeoln # define _cleartoeos _clr2eos #endif #ifndef BSD struct termio raw_tty, orig_tty; #else #define TCGETA TIOCGETP #define TCSETAW TIOCSETP struct sgttyb raw_tty, orig_tty; #endif static int inraw = 0; /* are we IN rawmode? */ static int _memory_locked = 0; /* are we IN memlock?? */ static int _intransmit; /* are we transmitting keys? */ static char *_clearscreen, *_moveto, *_cleartoeoln, *_cleartoeos, *_setinverse, *_clearinverse; static int _lines,_columns; static char _terminal[1024]; /* Storage for terminal entry */ static char _capabilities[1024]; /* String for cursor motion */ static char *ptr = _capabilities; /* for buffering */ int outchar(); /* char output for tputs */ char *tgetstr(), /* Get termcap capability */ *tgoto(); /* and the goto stuff */ InitScreen() { int tgetent(), /* get termcap entry */ err; char termname[40]; char *strcpy(), *getenv(); if (getenv("TERM") == NULL) { fprintf(stderr, "TERM variable not set; Screen capabilities required\n"); return(FALSE); } if (strcpy(termname, getenv("TERM")) == NULL) { fprintf(stderr,"Can't get TERM variable\n"); return(FALSE); } if ((err = tgetent(_terminal, termname)) != 1) { fprintf(stderr,"Can't get entry for TERM\n"); return(FALSE); } /* load in all those pesky values */ _clearscreen = tgetstr("cl", &ptr); _moveto = tgetstr("cm", &ptr); _cleartoeoln = tgetstr("ce", &ptr); _cleartoeos = tgetstr("cd", &ptr); _setinverse = tgetstr("so", &ptr); _clearinverse = tgetstr("se", &ptr); _lines = tgetnum("li"); _columns = tgetnum("co"); if (!_clearscreen) { fprintf(stderr, "Terminal must have clearscreen (cl) capability\n"); return(FALSE); } if (!_moveto) { fprintf(stderr, "Terminal must have cursor motion (cm)\n"); return(FALSE); } if (!_cleartoeoln) { fprintf(stderr, "Terminal must have clear to end-of-line (ce)\n"); return(FALSE); } if (!_cleartoeos) { fprintf(stderr, "Terminal must have clear to end-of-screen (cd)\n"); return(FALSE); } if (_lines == -1) _lines = DEFAULT_term_lines; if (_columns == -1) _columns = DEFAULT_COLUMNS; return(TRUE); } ScreenSize(lines, columns) int *lines, *columns; { /** returns the number of lines and columns on the display. **/ if (_lines == 0) _lines = DEFAULT_term_lines; if (_columns == 0) _columns = DEFAULT_COLUMNS; *lines = _lines - 1; /* assume index from zero*/ *columns = _columns; /* assume index from one */ } ClearScreen() { /* clear the screen: returns -1 if not capable */ tputs(_clearscreen, 1, outchar); fflush(stdout); /* clear the output buffer */ } MoveCursor(row, col) int row, col; { /** move cursor to the specified row column on the screen. 0,0 is the top left! **/ char *stuff, *tgoto(); stuff = tgoto(_moveto, col, row); tputs(stuff, 1, outchar); /* fflush(stdout); */ } CleartoEOLN() { /** clear to end of line **/ tputs(_cleartoeoln, 1, outchar); fflush(stdout); /* clear the output buffer */ } CleartoEOS() { /** clear to end of screen **/ tputs(_cleartoeos, 1, outchar); fflush(stdout); /* clear the output buffer */ } Raw(state) int state; { /** state is either TRUE or FALSE, as indicated by call **/ if (state == FALSE && inraw) { (void) ioctl(TTYIN, TCSETAW, &orig_tty); inraw = 0; } else if (state == TRUE && ! inraw) { (void) ioctl(TTYIN, TCGETA, &orig_tty); /** current setting **/ (void) ioctl(TTYIN, TCGETA, &raw_tty); /** again! **/ #ifdef BSD /* raw_tty.sg_flags &= ~(ECHO | CRMOD); /* echo off */ raw_tty.sg_flags &= ~ECHO; /* echo off */ raw_tty.sg_flags |= CBREAK; /* raw on */ #else raw_tty.c_lflag &= ~(ICANON | ECHO); /* noecho raw mode */ raw_tty.c_cc[VMIN] = '\01'; /* minimum # of chars to queue */ raw_tty.c_cc[VTIME] = '\0'; /* minimum time to wait for input */ #endif raw_tty.c_cc[VINTR]= escape_char; (void) ioctl(TTYIN, TCSETAW, &raw_tty); inraw = 1; } } int ReadCh() { /** read a character with Raw mode set! **/ register int result; char ch; result = read(0, &ch, 1); return((result <= 0 ) ? EOF : ch); } outchar(c) char c; { /** output the given character. From tputs... **/ /** Note: this CANNOT be a macro! **/ putc(c, stdout); } #if 0 static int inverse = FALSE; SetInverse() { if (!inverse) { StartInverse(); inverse = TRUE; } } SetNormal() { if (inverse) { EndInverse(); inverse = FALSE; } } #endif StartInverse() { /** set inverse video mode **/ if (!_setinverse) return(-1); tputs(_setinverse, 1, outchar); /* fflush(stdout); */ return(0); } EndInverse() { /** compliment of startinverse **/ if (!_clearinverse) return(-1); tputs(_clearinverse, 1, outchar); /* fflush(stdout); */ return(0); } @EOF chmod 644 curses.c echo x - disk.c cat >disk.c <<'@EOF' /* * a2, an Apple II emulator in C * (c) Copyright 1990 by Rich Skrenta * * Command line interface written by Tom Markson * * Distribution agreement: * * You may freely copy or redistribute this software, so long * as there is no profit made from its use, sale, trade or * reproduction. You may not change this copyright notice, * and it must be included prominently in any copy made. * * Send emulator related mail to: skrenta@blekko.commodore.com * skrenta@blekko.uucp */ #include #include "a2.h" #define GAP 0x7F; /* data gap byte */ /* * 4 by 4 nibble encoding macros */ #define nib1(a) (((a) >> 1) | 0xAA) #define nib2(a) ((a) | 0xAA) #define denib(a,b) (((((a) & 0x55) << 1) & 0xFF) | ((b) & 0x55)) int cur_track[2] = {0, 0}; int sect_pos[2]; /* current sector within track */ extern unsigned char tab1[]; /* For nibblizing. At end of this file */ extern unsigned char tab2[]; /* Disk byte translation table. */ extern unsigned char phys[]; /* DOS 3.3 to physical sector mapping */ unsigned char sect_buf[2][1024]; unsigned char *sectp = NULL; unsigned char *sect_point[2] = {NULL, NULL}; unsigned char write_reg; /* write data register */ int write_mode = FALSE; unsigned char disk_ref(a, n) unsigned short a; unsigned char n; { switch (a) { case 0xC0E0: /* Phase 0 off */ case 0xC0E1: /* Phase 0 on */ case 0xC0E2: /* Phase 1 off */ case 0xC0E3: /* Phase 1 on */ case 0xC0E4: /* Phase 2 off */ case 0xC0E5: /* Phase 2 on */ case 0xC0E6: /* Phase 3 off */ case 0xC0E7: /* Phase 3 on */ step_motor(a); break; case 0xC0E8: /* Drive off */ break; case 0xC0E9: /* Drive on */ break; case 0xC0EA: /* Select drive 1 */ sect_point[drive] = sectp; drive = 0; sectp = sect_point[0]; break; case 0xC0EB: /* Select drive 2 */ sect_point[drive] = sectp; drive = 1; sectp = sect_point[1]; break; case 0xC0EC: /* Shift data register */ if (disk[drive] < 0) return(0xFF); if (write_mode) { raw_disk_write(); return(0); } if (sectp == NULL || *sectp == '\0') { sect_pos[drive]--; if (sect_pos[drive] < 0) sect_pos[drive] = 15; setup_sector(cur_track[drive], sect_pos[drive]); } return(*sectp++); case 0xC0ED: /* Load data register */ write_reg = n; break; case 0xC0EE: /* Read mode */ write_mode = FALSE; return(write_prot[drive] ? 0xFF : 0); break; case 0xC0EF: /* Write mode */ write_mode = TRUE; break; } return(0); } /* * Determine what track the disk head is over by watching toggles to * the four stepper motor magnets. */ step_motor(a) unsigned short a; { static int mag[2][4] = {{0, 0, 0, 0}, {0, 0, 0, 0}}, pmag[2][4] = {{0, 0, 0, 0}, {0, 0, 0, 0}}, /* previous */ ppmag[2][4] = {{0, 0, 0, 0}, {0, 0, 0, 0}}, /* previous previous */ pnum[2] = {0, 0}, ppnum[2] = {0, 0}, track_pos[2] = {0, 0}; static int prev_track[2] = {0, 0}; int magnet_number; a &= 7; magnet_number = a >> 1; ppmag[drive][ppnum[drive]] = pmag[drive][ppnum[drive]]; ppnum[drive] = pnum[drive]; pmag[drive][pnum[drive]] = mag[drive][pnum[drive]]; pnum[drive] = magnet_number; if ((a & 1) == 0) mag[drive][magnet_number] = FALSE; else { if (ppmag[drive][(magnet_number + 1) & 3]) { track_pos[drive]--; if (track_pos[drive] < 0) { track_pos[drive] = 0; if (drive) info("recal d2"); else info("recal"); } } if (ppmag[drive][(magnet_number - 1) & 3]) { track_pos[drive]++; if (track_pos[drive] > 140) track_pos[drive] = 140; } mag[drive][magnet_number] = TRUE; } cur_track[drive] = (track_pos[drive] + 1) / 2; if (cur_track[drive] != prev_track[drive]) { #if 0 sprintf(s, "step to %d%s", cur_track[drive], drive ? " d2" : ""); info(s); #endif sectp[drive] = NULL; /* recompute sector if head moves */ sect_pos[drive] = 0; prev_track[drive] = cur_track[drive]; } } setup_sector(track, sector) int track; int sector; { int checksum; int physical_sector; char s[50]; int i; physical_sector = phys[sector]; sprintf(s, "raw t=%d s=%d%s", track, sector, drive ? " d2" : ""); info(s); sectp = sect_buf[drive]; for (i = 0; i < 16; i++) *sectp++ = GAP; *sectp++ = 0xD5; /* address header */ *sectp++ = 0xAA; *sectp++ = 0x96; *sectp++ = 0xFF; /* disk volume 254 */ *sectp++ = 0xFE; *sectp++ = nib1(track); *sectp++ = nib2(track); *sectp++ = nib1(physical_sector); *sectp++ = nib2(physical_sector); checksum = 254 ^ track ^ physical_sector; *sectp++ = nib1(checksum); *sectp++ = nib2(checksum); *sectp++ = 0xDE; /* address trailer */ *sectp++ = 0xAA; for (i = 0; i < 8; i++) *sectp++ = GAP; *sectp++ = 0xD5; /* data header */ *sectp++ = 0xAA; *sectp++ = 0xAD; encode_data(track, sector); /* nibblized data */ *sectp++ = 0xDE; /* data trailer */ *sectp++ = 0xAA; *sectp++ = 0xEB; *sectp = '\0'; /* ending mark for our use */ sectp = sect_buf[drive]; /* start reading at beginning */ } /* * Take a normal 256 byte sector and nibblize it into 342 bytes * Checksum it with itself; this yields one more byte. * Translate the resulting 343 bytes into "disk bytes". * Insert them into the sector buffer. * * See a reference such as _Beneath Apple Prodos_ for an explanation * of why & how this is done. */ encode_data(track, sector) int track; int sector; { unsigned char buf[344]; unsigned char *one; unsigned char *bump; unsigned char *two; unsigned char *three; unsigned char *dest; int i; read_disk(track, sector, &buf[86]); buf[342] = 0; buf[343] = 0; dest = buf; one = &buf[86]; two = &buf[86 + 0x56]; bump = two; three = &buf[86 + 0xAC]; do { i = (*one++ & 0x03) | ((*two++ & 0x03) << 2) | ((*three++ & 0x03) << 4); *dest++ = tab1[i]; } while (one != bump); sectp[0] = buf[0]; for (i = 1; i <= 342; i++) sectp[i] = buf[i - 1] ^ buf[i]; for (i = 0; i <= 342; i++) sectp[i] = tab2[ sectp[i] >> 2 ]; sectp = §p[343]; } raw_disk_write() { printf("raw write %.2X\n", write_reg); } read_disk(track, sector, buf) int track; int sector; unsigned char *buf; { long block; block = track * 16 + sector; lseek(disk[drive], block * 256, 0); if (read(disk[drive], buf, 256) != 256) perror("bad read"); } write_disk(track, sector, buf) int track; int sector; unsigned char *buf; { long block; block = track * 16 + sector; lseek(disk[drive], block * 256, 0); if (write(disk[drive], buf, 256) != 256) perror("bad write"); } /* * Helps with the bit fiddling necessary to extract the bottom * two bits during the 256 - 342 byte nibblize. */ unsigned char tab1[] = { 0x00, 0x08, 0x04, 0x0C, 0x20, 0x28, 0x24, 0x2C, 0x10, 0x18, 0x14, 0x1C, 0x30, 0x38, 0x34, 0x3C, 0x80, 0x88, 0x84, 0x8C, 0xA0, 0xA8, 0xA4, 0xAC, 0x90, 0x98, 0x94, 0x9C, 0xB0, 0xB8, 0xB4, 0xBC, 0x40, 0x48, 0x44, 0x4C, 0x60, 0x68, 0x64, 0x6C, 0x50, 0x58, 0x54, 0x5C, 0x70, 0x78, 0x74, 0x7C, 0xC0, 0xC8, 0xC4, 0xCC, 0xE0, 0xE8, 0xE4, 0xEC, 0xD0, 0xD8, 0xD4, 0xDC, 0xF0, 0xF8, 0xF4, 0xFC, }; /* * Translates to "disk bytes" */ unsigned char tab2[] = { 0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6, 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3, 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, }; /* * Dos 3.3 to physical sector conversion */ unsigned char phys[] = { 0x00, 0x0D, 0x0B, 0x09, 0x07, 0x05, 0x03, 0x01, 0x0E, 0x0C, 0x0A, 0x08, 0x06, 0x04, 0x02, 0x0F, }; @EOF chmod 644 disk.c echo x - tbl.c cat >tbl.c <<'@EOF' /* * a2, an Apple II emulator in C * (c) Copyright 1990 by Rich Skrenta * * Command line interface written by Tom Markson * * Distribution agreement: * * You may freely copy or redistribute this software, so long * as there is no profit made from its use, sale, trade or * reproduction. You may not change this copyright notice, * and it must be included prominently in any copy made. * * Send emulator related mail to: skrenta@blekko.commodore.com * skrenta@blekko.uucp */ #include #include "cli.h" int quit_emulator(); int cont(); int refresh(); int clc(), sec(), sev(), clb(), seb(); int clri(), sei(), sez(), clz(), sen(), cln(), sed(), cld(), clv(); int ver(); int shell_escape(); int do_soft_reset(); int do_bload(); int do_bsave(); int show_point(); int do_jump(); int ldx(), ldy(), lds(), lda(); int trace(); int set_break_point(), clear_break_point(); int notrace(); int insert_disk(); int dissassemble(); int hex_dump(); int deposit(); int phantom_trace(); int no_phantom_trace(); int cd(); int map(); int sex(); int help(); int set_escape_char(); int hack(); int init_point(), push_point(), dup_point(), pop_point(); int disk_names(); struct cmdtbl first_tbl[] = { {"!", shell_escape}, {".", show_point}, {"bload", do_bload}, {"breakpoint", set_break_point}, {"bsave", do_bsave}, {"cd", cd}, {"clc", clc}, {"cld", cld}, {"cli", clri}, {"cln", cln}, {"clv", clv}, {"clz", clz}, {"continue", refresh}, {"deposit", deposit}, {"disks", disk_names}, {"dup", dup_point}, {"escape", set_escape_char}, {"examine", hex_dump}, {".e", hex_dump}, {"hack", hack}, {"help", help}, {"insert", insert_disk}, {"jmp", do_jump}, {"lda", lda}, {"lds", lds}, {"ldx", ldx}, {"ldy", ldy}, {"list", dissassemble}, {"map", map}, {"nobreak", clear_break_point}, {"notrace", notrace}, {"pop", pop_point}, {"push", push_point}, {"quit", quit_emulator}, {"reset", do_soft_reset}, {"sec", sec}, {"sed", sed}, {"sei", sei}, {"sen", sen}, {"sev", sev}, {".sex", sex}, {"sez", sez}, {"trace", trace}, {".version", ver}, {".c", refresh}, {".clb", clb}, {".d", deposit}, {".e", hex_dump}, {".l", dissassemble}, {".phantom", phantom_trace}, {".seb", seb}, {".nophantom", no_phantom_trace}, {NULL, NULL}, }; @EOF chmod 644 tbl.c exit 0