diff --git a/include/apple2.h b/include/apple2.h index 9c813c5..b2f2c86 100644 --- a/include/apple2.h +++ b/include/apple2.h @@ -8,8 +8,19 @@ typedef struct { * The apple 2 hardware used an MOS-6502 processor. */ mos6502 *cpu; + + /* + * This is the literal memory that the CPU above will create. You + * should _not_ attempt to free this memory; allow the CPU's own + * delete function to do that. + */ + vm_segment *memory; } apple2; extern apple2 *apple2_create(); +extern void apple2_free(apple2 *); +extern void apple2_press_key(apple2 *, vm_8bit); +extern void apple2_clear_strobe(apple2 *); +extern void apple2_release_key(apple2 *); #endif diff --git a/src/apple2.c b/src/apple2.c index e0c9cf4..3b880c6 100644 --- a/src/apple2.c +++ b/src/apple2.c @@ -3,7 +3,23 @@ */ #include "apple2.h" +#include "vm_segment.h" +/* + * This is the memory address where an apple program can find the value + * of the key that was last pressed. + */ +#define LAST_KEY 0xC000 + +/* + * This is the address in memory where you can find whether a key is + * currently pressed or not. + */ +#define ANY_KEY_DOWN 0xC010 + +/* + * Create the basic apple2 structure. + */ apple2 * apple2_create() { @@ -15,6 +31,65 @@ apple2_create() } mach->cpu = mos6502_create(); + mach->memory = mach->cpu->memory; return mach; } + +/* + * Free the memory reserved for an apple2 struct. + */ +void +apple2_free(apple2 *mach) +{ + mos6502_free(mach->cpu); + + // NOTE: we do _NOT_ want to clear the memory field of mach, as it's + // co-owned with the cpu struct that we just freed above. + + free(mach); +} + +void +apple2_press_key(apple2 *mach, vm_8bit ch) +{ + // The apple2 can only handle ASCII values of 0 through 127. + // However, the eigth bit is called the "strobe" bit, and is treated + // specially. In particular, the strobe bit is 1 if a key was + // pressed down, and remains 1 until you reset it by reading from + // the clear-strobe location. + ch = ch | 0x80; + + // This is the location in memory where a program will expect to + // find the value of the last key that was pressed. + vm_segment_set(mach->memory, LAST_KEY, ch); + + // This area is a combination of flags; the eighth bit here is the + // "any-key-down" flag, which is a bit of a mouthful. It's 1 if a + // key is pressed, and 0 if not. The effect of reading this bit will + // also _clear_ the strobe bit in the $C000 address (above). + vm_segment_set(mach->memory, ANY_KEY_DOWN, 0x80); +} + +/* + * This function will clear the 8th bit, which is the "strobe" bit, from + * the position in memory where the value of the last key that was + * pressed is held. + */ +void +apple2_clear_strobe(apple2 *mach) +{ + vm_8bit ch; + + ch = vm_segment_get(mach->memory, LAST_KEY); + vm_segment_set(mach->memory, LAST_KEY, ch & 0x7F); +} + +/* + * This function will clear the value of the any-key-down switch/flag. + */ +void +apple2_release_key(apple2 *mach) +{ + vm_segment_set(mach->memory, ANY_KEY_DOWN, 0); +} diff --git a/tests/apple2.c b/tests/apple2.c index f41319b..1257c98 100644 --- a/tests/apple2.c +++ b/tests/apple2.c @@ -2,11 +2,47 @@ #include "apple2.h" +static apple2 *mach; + +void +setup() +{ + mach = apple2_create(); +} + +void +teardown() +{ + apple2_free(mach); +} + +TestSuite(apple2, .init = setup, .fini = teardown); + Test(apple2, create) { - apple2 *mach; - - mach = apple2_create(); cr_assert_neq(mach, NULL); cr_assert_neq(mach->cpu, NULL); } + +Test(apple2, press_key) +{ + apple2_press_key(mach, 123); + cr_assert_eq(vm_segment_get(mach->memory, 0xC000), 123 | 0x80); + cr_assert_eq(vm_segment_get(mach->memory, 0xC010), 0x80); +} + +Test(apple2, clear_strobe) +{ + apple2_press_key(mach, 123); + cr_assert_eq(vm_segment_get(mach->memory, 0xC000), 123 | 0x80); + apple2_clear_strobe(mach); + cr_assert_eq(vm_segment_get(mach->memory, 0xC000), 123); +} + +Test(apple2, release_key) +{ + apple2_press_key(mach, 123); + cr_assert_eq(vm_segment_get(mach->memory, 0xC010), 0x80); + apple2_release_key(mach); + cr_assert_eq(vm_segment_get(mach->memory, 0xC010), 0); +}